llms.txt@08-Plugins-Skills · git:20261024.48b8a60 · 2026-10-24 · sha256 3266084e73c2e9be

llms.txt@08-Plugins-Skills git:20261024.48b8a60F

Immutable. This exact content never changes and is served at /api/v1/blob/3266084e73c2e9be.

# Plugins & Skills

> Every page below, concatenated. Each section gives the page title, its upstream source and its path in this repository.

---

## Commit Push Pr

Source: https://github.com/anthropics/claude-code/blob/main/.claude/commands/commit-push-pr.md
Path: 08-Plugins-Skills/claude-code-claude-commands-commit-push-pr.md

## Context

- Current git status: !`git status`
- Current git diff (staged and unstaged changes): !`git diff HEAD`
- Current branch: !`git branch --show-current`

## Your task

Based on the above changes:

1. Create a new branch if on main
2. Create a single commit with an appropriate message
3. Push the branch to origin
4. Create a pull request using `gh pr create`
5. You have the capability to call multiple tools in a single response. You MUST do all of the above in a single message. Do not use any other tools or do anything else. Do not send any other text or messages besides these tool calls.

---

## New Sdk App

Source: https://github.com/anthropics/claude-code/blob/main/plugins/agent-sdk-dev/commands/new-sdk-app.md
Path: 08-Plugins-Skills/claude-code-plugins-agent-sdk-dev-commands-new-sdk-app.md

You are tasked with helping the user create a new Claude Agent SDK application. Follow these steps carefully:

## Reference Documentation

Before starting, review the official documentation to ensure you provide accurate and up-to-date guidance. Use WebFetch to read these pages:

1. **Start with the overview**: https://docs.claude.com/en/api/agent-sdk/overview
2. **Based on the user's language choice, read the appropriate SDK reference**:
   - TypeScript: https://docs.claude.com/en/api/agent-sdk/typescript
   - Python: https://docs.claude.com/en/api/agent-sdk/python
3. **Read relevant guides mentioned in the overview** such as:
   - Streaming vs Single Mode
   - Permissions
   - Custom Tools
   - MCP integration
   - Subagents
   - Sessions
   - Any other relevant guides based on the user's needs

**IMPORTANT**: Always check for and use the latest versions of packages. Use WebSearch or WebFetch to verify current versions before installation.

## Gather Requirements

IMPORTANT: Ask these questions one at a time. Wait for the user's response before asking the next question. This makes it easier for the user to respond.

Ask the questions in this order (skip any that the user has already provided via arguments):

1. **Language** (ask first): "Would you like to use TypeScript or Python?"

   - Wait for response before continuing

2. **Project name** (ask second): "What would you like to name your project?"

   - If $ARGUMENTS is provided, use that as the project name and skip this question
   - Wait for response before continuing

3. **Agent type** (ask third, but skip if #2 was sufficiently detailed): "What kind of agent are you building? Some examples:

   - Coding agent (SRE, security review, code review)
   - Business agent (customer support, content creation)
   - Custom agent (describe your use case)"
   - Wait for response before continuing

4. **Starting point** (ask fourth): "Would you like:

   - A minimal 'Hello World' example to start
   - A basic agent with common features
   - A specific example based on your use case"
   - Wait for response before continuing

5. **Tooling choice** (ask fifth): Let the user know what tools you'll use, and confirm with them that these are the tools they want to use (for example, they may prefer pnpm or bun over npm). Respect the user's preferences when executing on the requirements.

After all questions are answered, proceed to create the setup plan.

## Setup Plan

Based on the user's answers, create a plan that includes:

1. **Project initialization**:

   - Create project directory (if it doesn't exist)
   - Initialize package manager:
     - TypeScript: `npm init -y` and setup `package.json` with type: "module" and scripts (include a "typecheck" script)
     - Python: Create `requirements.txt` or use `poetry init`
   - Add necessary configuration files:
     - TypeScript: Create `tsconfig.json` with proper settings for the SDK
     - Python: Optionally create config files if needed

2. **Check for Latest Versions**:

   - BEFORE installing, use WebSearch or check npm/PyPI to find the latest version
   - For TypeScript: Check https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk
   - For Python: Check https://pypi.org/project/claude-agent-sdk/
   - Inform the user which version you're installing

3. **SDK Installation**:

   - TypeScript: `npm install @anthropic-ai/claude-agent-sdk@latest` (or specify latest version)
   - Python: `pip install claude-agent-sdk` (pip installs latest by default)
   - After installation, verify the installed version:
     - TypeScript: Check package.json or run `npm list @anthropic-ai/claude-agent-sdk`
     - Python: Run `pip show claude-agent-sdk`

4. **Create starter files**:

   - TypeScript: Create an `index.ts` or `src/index.ts` with a basic query example
   - Python: Create a `main.py` with a basic query example
   - Include proper imports and basic error handling
   - Use modern, up-to-date syntax and patterns from the latest SDK version

5. **Environment setup**:

   - Create a `.env.example` file with `ANTHROPIC_API_KEY=your_api_key_here`
   - Add `.env` to `.gitignore`
   - Explain how to get an API key from https://console.anthropic.com/

6. **Optional: Create .claude directory structure**:
   - Offer to create `.claude/` directory for agents, commands, and settings
   - Ask if they want any example subagents or slash commands

## Implementation

After gathering requirements and getting user confirmation on the plan:

1. Check for latest package versions using WebSearch or WebFetch
2. Execute the setup steps
3. Create all necessary files
4. Install dependencies (always use latest stable versions)
5. Verify installed versions and inform the user
6. Create a working example based on their agent type
7. Add helpful comments in the code explaining what each part does
8. **VERIFY THE CODE WORKS BEFORE FINISHING**:
   - For TypeScript:
     - Run `npx tsc --noEmit` to check for type errors
     - Fix ALL type errors until types pass completely
     - Ensure imports and types are correct
     - Only proceed when type checking passes with no errors
   - For Python:
     - Verify imports are correct
     - Check for basic syntax errors
   - **DO NOT consider the setup complete until the code verifies successfully**

## Verification

After all files are created and dependencies are installed, use the appropriate verifier agent to validate that the Agent SDK application is properly configured and ready for use:

1. **For TypeScript projects**: Launch the **agent-sdk-verifier-ts** agent to validate the setup
2. **For Python projects**: Launch the **agent-sdk-verifier-py** agent to validate the setup
3. The agent will check SDK usage, configuration, functionality, and adherence to official documentation
4. Review the verification report and address any issues

## Getting Started Guide

Once setup is complete and verified, provide the user with:

1. **Next steps**:

   - How to set their API key
   - How to run their agent:
     - TypeScript: `npm start` or `node --loader ts-node/esm index.ts`
     - Python: `python main.py`

2. **Useful resources**:

   - Link to TypeScript SDK reference: https://docs.claude.com/en/api/agent-sdk/typescript
   - Link to Python SDK reference: https://docs.claude.com/en/api/agent-sdk/python
   - Explain key concepts: system prompts, permissions, tools, MCP servers

3. **Common next steps**:
   - How to customize the system prompt
   - How to add custom tools via MCP
   - How to configure permissions
   - How to create subagents

## Important Notes

- **ALWAYS USE LATEST VERSIONS**: Before installing any packages, check for the latest versions using WebSearch or by checking npm/PyPI directly
- **VERIFY CODE RUNS CORRECTLY**:
  - For TypeScript: Run `npx tsc --noEmit` and fix ALL type errors before finishing
  - For Python: Verify syntax and imports are correct
  - Do NOT consider the task complete until the code passes verification
- Verify the installed version after installation and inform the user
- Check the official documentation for any version-specific requirements (Node.js version, Python version, etc.)
- Always check if directories/files already exist before creating them
- Use the user's preferred package manager (npm, yarn, pnpm for TypeScript; pip, poetry for Python)
- Ensure all code examples are functional and include proper error handling
- Use modern syntax and patterns that are compatible with the latest SDK version
- Make the experience interactive and educational
- **ASK QUESTIONS ONE AT A TIME** - Do not ask multiple questions in a single response

Begin by asking the FIRST requirement question only. Wait for the user's answer before proceeding to the next question.

---

## Code Review

Source: https://github.com/anthropics/claude-code/blob/main/plugins/code-review/commands/code-review.md
Path: 08-Plugins-Skills/claude-code-plugins-code-review-commands-code-review.md

Provide a code review for the given pull request.

**Agent assumptions (applies to all agents and subagents):**
- All tools are functional and will work without error. Do not test tools or make exploratory calls. Make sure this is clear to every subagent that is launched.
- Only call a tool if it is required to complete the task. Every tool call should have a clear purpose.

To do this, follow these steps precisely:

1. Launch a haiku agent to check if any of the following are true:
   - The pull request is closed
   - The pull request is a draft
   - The pull request does not need code review (e.g. automated PR, trivial change that is obviously correct)
   - Claude has already commented on this PR (check `gh pr view <PR> --comments` for comments left by claude)

   If any condition is true, stop and do not proceed.

Note: Still review Claude generated PR's.

2. Launch a haiku agent to return a list of file paths (not their contents) for all relevant CLAUDE.md files including:
   - The root CLAUDE.md file, if it exists
   - Any CLAUDE.md files in directories containing files modified by the pull request

3. Launch a sonnet agent to view the pull request and return a summary of the changes

4. Launch 4 agents in parallel to independently review the changes. Each agent should return the list of issues, where each issue includes a description and the reason it was flagged (e.g. "CLAUDE.md adherence", "bug"). The agents should do the following:

   Agents 1 + 2: CLAUDE.md compliance sonnet agents
   Audit changes for CLAUDE.md compliance in parallel. Note: When evaluating CLAUDE.md compliance for a file, you should only consider CLAUDE.md files that share a file path with the file or parents.

   Agent 3: Opus bug agent (parallel subagent with agent 4)
   Scan for obvious bugs. Focus only on the diff itself without reading extra context. Flag only significant bugs; ignore nitpicks and likely false positives. Do not flag issues that you cannot validate without looking at context outside of the git diff.

   Agent 4: Opus bug agent (parallel subagent with agent 3)
   Look for problems that exist in the introduced code. This could be security issues, incorrect logic, etc. Only look for issues that fall within the changed code.

   **CRITICAL: We only want HIGH SIGNAL issues.** Flag issues where:
   - The code will fail to compile or parse (syntax errors, type errors, missing imports, unresolved references)
   - The code will definitely produce wrong results regardless of inputs (clear logic errors)
   - Clear, unambiguous CLAUDE.md violations where you can quote the exact rule being broken

   Do NOT flag:
   - Code style or quality concerns
   - Potential issues that depend on specific inputs or state
   - Subjective suggestions or improvements

   If you are not certain an issue is real, do not flag it. False positives erode trust and waste reviewer time.

   In addition to the above, each subagent should be told the PR title and description. This will help provide context regarding the author's intent.

5. For each issue found in the previous step by agents 3 and 4, launch parallel subagents to validate the issue. These subagents should get the PR title and description along with a description of the issue. The agent's job is to review the issue to validate that the stated issue is truly an issue with high confidence. For example, if an issue such as "variable is not defined" was flagged, the subagent's job would be to validate that is actually true in the code. Another example would be CLAUDE.md issues. The agent should validate that the CLAUDE.md rule that was violated is scoped for this file and is actually violated. Use Opus subagents for bugs and logic issues, and sonnet agents for CLAUDE.md violations.

6. Filter out any issues that were not validated in step 5. This step will give us our list of high signal issues for our review.

7. If issues were found, skip to step 8 to post inline comments directly.

   If NO issues were found, post a summary comment using `gh pr comment` (if `--comment` argument is provided):
   "No issues found. Checked for bugs and CLAUDE.md compliance."

8. Create a list of all comments that you plan on leaving. This is only for you to make sure you are comfortable with the comments. Do not post this list anywhere.

9. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`. For each comment:
   - Provide a brief description of the issue
   - For small, self-contained fixes, include a committable suggestion block
   - For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), describe the issue and suggested fix without a suggestion block
   - Never post a committable suggestion UNLESS committing the suggestion fixes the issue entirely. If follow up steps are required, do not leave a committable suggestion.

   **IMPORTANT: Only post ONE comment per unique issue. Do not post duplicate comments.**

Use this list when evaluating issues in Steps 4 and 5 (these are false positives, do NOT flag):

- Pre-existing issues
- Something that appears to be a bug but is actually correct
- Pedantic nitpicks that a senior engineer would not flag
- Issues that a linter will catch (do not run the linter to verify)
- General code quality concerns (e.g., lack of test coverage, general security issues) unless explicitly required in CLAUDE.md
- Issues mentioned in CLAUDE.md but explicitly silenced in the code (e.g., via a lint ignore comment)

Notes:

- Use gh CLI to interact with GitHub (e.g., fetch pull requests, create comments). Do not use web fetch.
- Create a todo list before starting.
- You must cite and link each issue in inline comments (e.g., if referring to a CLAUDE.md, include a link to it).
- If no issues are found, post a comment with the following format:

---

## Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

---

- When linking to code in inline comments, follow the following format precisely, otherwise the Markdown preview won't render correctly: https://github.com/anthropics/claude-code/blob/c21d3c10bc8e898b7ac1a2d745bdc9bc4e423afe/package.json#L10-L15
  - Requires full git sha
  - You must provide the full sha. Commands like `https://github.com/owner/repo/blob/$(git rev-parse HEAD)/foo/bar` will not work, since your comment will be directly rendered in Markdown.
  - Repo name must match the repo you're code reviewing
  - # sign after the file name
  - Line range format is L[start]-L[end]
  - Provide at least 1 line of context before and after, centered on the line you are commenting about (eg. if you are commenting about lines 5-6, you should link to `L4-7`)

---

## Code Review Plugin

Source: https://github.com/anthropics/claude-code/blob/main/plugins/code-review/README.md
Path: 08-Plugins-Skills/claude-code-plugins-code-review-readme.md

# Code Review Plugin

Automated code review for pull requests using multiple specialized agents with confidence-based scoring to filter false positives.

## Overview

The Code Review Plugin automates pull request review by launching multiple agents in parallel to independently audit changes from different perspectives. It uses confidence scoring to filter out false positives, ensuring only high-quality, actionable feedback is posted.

## Commands

### `/code-review`

Performs automated code review on a pull request using multiple specialized agents.

**What it does:**
1. Checks if review is needed (skips closed, draft, trivial, or already-reviewed PRs)
2. Gathers relevant CLAUDE.md guideline files from the repository
3. Summarizes the pull request changes
4. Launches 4 parallel agents to independently review:
   - **Agents #1 & #2**: Audit for CLAUDE.md compliance
   - **Agent #3**: Scan for obvious bugs in changes
   - **Agent #4**: Analyze git blame/history for context-based issues
5. Scores each issue 0-100 for confidence level
6. Filters out issues below 80 confidence threshold
7. Outputs review (to terminal by default, or as PR comment with `--comment` flag)

**Usage:**
```bash
/code-review [--comment]
```

**Options:**
- `--comment`: Post the review as a comment on the pull request (default: outputs to terminal only)

**Example workflow:**
```bash
# On a PR branch, run locally (outputs to terminal):
/code-review

# Post review as PR comment:
/code-review --comment

# Claude will:
# - Launch 4 review agents in parallel
# - Score each issue for confidence
# - Output issues ≥80 confidence (to terminal or PR depending on flag)
# - Skip if no high-confidence issues found
```

**Features:**
- Multiple independent agents for comprehensive review
- Confidence-based scoring reduces false positives (threshold: 80)
- CLAUDE.md compliance checking with explicit guideline verification
- Bug detection focused on changes (not pre-existing issues)
- Historical context analysis via git blame
- Automatic skipping of closed, draft, or already-reviewed PRs
- Links directly to code with full SHA and line ranges

**Review comment format:**
```markdown
## Code review

Found 3 issues:

1. Missing error handling for OAuth callback (CLAUDE.md says "Always handle OAuth errors")

https://github.com/owner/repo/blob/abc123.../src/auth.ts#L67-L72

2. Memory leak: OAuth state not cleaned up (bug due to missing cleanup in finally block)

https://github.com/owner/repo/blob/abc123.../src/auth.ts#L88-L95

3. Inconsistent naming pattern (src/conventions/CLAUDE.md says "Use camelCase for functions")

https://github.com/owner/repo/blob/abc123.../src/utils.ts#L23-L28
```

**Confidence scoring:**
- **0**: Not confident, false positive
- **25**: Somewhat confident, might be real
- **50**: Moderately confident, real but minor
- **75**: Highly confident, real and important
- **100**: Absolutely certain, definitely real

**False positives filtered:**
- Pre-existing issues not introduced in PR
- Code that looks like a bug but isn't
- Pedantic nitpicks
- Issues linters will catch
- General quality issues (unless in CLAUDE.md)
- Issues with lint ignore comments

## Installation

This plugin is included in the Claude Code repository. The command is automatically available when using Claude Code.

## Best Practices

### Using `/code-review`
- Maintain clear CLAUDE.md files for better compliance checking
- Trust the 80+ confidence threshold - false positives are filtered
- Run on all non-trivial pull requests
- Review agent findings as a starting point for human review
- Update CLAUDE.md based on recurring review patterns

### When to use
- All pull requests with meaningful changes
- PRs touching critical code paths
- PRs from multiple contributors
- PRs where guideline compliance matters

### When not to use
- Closed or draft PRs (automatically skipped anyway)
- Trivial automated PRs (automatically skipped)
- Urgent hotfixes requiring immediate merge
- PRs already reviewed (automatically skipped)

## Workflow Integration

### Standard PR review workflow:
```bash
# Create PR with changes
# Run local review (outputs to terminal)
/code-review

# Review the automated feedback
# Make any necessary fixes

# Optionally post as PR comment
/code-review --comment

# Merge when ready
```

### As part of CI/CD:
```bash
# Trigger on PR creation or update
# Use --comment flag to post review comments
/code-review --comment
# Skip if review already exists
```

## Requirements

- Git repository with GitHub integration
- GitHub CLI (`gh`) installed and authenticated
- CLAUDE.md files (optional but recommended for guideline checking)

## Troubleshooting

### Review takes too long

**Issue**: Agents are slow on large PRs

**Solution**:
- Normal for large changes - agents run in parallel
- 4 independent agents ensure thoroughness
- Consider splitting large PRs into smaller ones

### Too many false positives

**Issue**: Review flags issues that aren't real

**Solution**:
- Default threshold is 80 (already filters most false positives)
- Make CLAUDE.md more specific about what matters
- Consider if the flagged issue is actually valid

### No review comment posted

**Issue**: `/code-review` runs but no comment appears

**Solution**:
Check if:
- PR is closed (reviews skipped)
- PR is draft (reviews skipped)
- PR is trivial/automated (reviews skipped)
- PR already has review (reviews skipped)
- No issues scored ≥80 (no comment needed)

### Link formatting broken

**Issue**: Code links don't render correctly in GitHub

**Solution**:
Links must follow this exact format:
```
https://github.com/owner/repo/blob/[full-sha]/path/file.ext#L[start]-L[end]
```
- Must use full SHA (not abbreviated)
- Must use `#L` notation
- Must include line range with at least 1 line of context

### GitHub CLI not working

**Issue**: `gh` commands fail

**Solution**:
- Install GitHub CLI: `brew install gh` (macOS) or see [GitHub CLI installation](https://cli.github.com/)
- Authenticate: `gh auth login`
- Verify repository has GitHub remote

## Tips

- **Write specific CLAUDE.md files**: Clear guidelines = better reviews
- **Include context in PRs**: Helps agents understand intent
- **Use confidence scores**: Issues ≥80 are usually correct
- **Iterate on guidelines**: Update CLAUDE.md based on patterns
- **Review automatically**: Set up as part of PR workflow
- **Trust the filtering**: Threshold prevents noise

## Configuration

### Adjusting confidence threshold

The default threshold is 80. To adjust, modify the command file at `commands/code-review.md`:
```markdown
Filter out any issues with a score less than 80.
```

Change `80` to your preferred threshold (0-100).

### Customizing review focus

Edit `commands/code-review.md` to add or modify agent tasks:
- Add security-focused agents
- Add performance analysis agents
- Add accessibility checking agents
- Add documentation quality checks

## Technical Details

### Agent architecture
- **2x CLAUDE.md compliance agents**: Redundancy for guideline checks
- **1x bug detector**: Focused on obvious bugs in changes only
- **1x history analyzer**: Context from git blame and history
- **Nx confidence scorers**: One per issue for independent scoring

### Scoring system
- Each issue independently scored 0-100
- Scoring considers evidence strength and verification
- Threshold (default 80) filters low-confidence issues
- For CLAUDE.md issues: verifies guideline explicitly mentions it

### GitHub integration
Uses `gh` CLI for:
- Viewing PR details and diffs
- Fetching repository data
- Reading git blame and history
- Posting review comments

## Author

Boris Cherny (boris@anthropic.com)

## Version

1.0.0

---

## Commit

Source: https://github.com/anthropics/claude-code/blob/main/plugins/commit-commands/commands/commit.md
Path: 08-Plugins-Skills/claude-code-plugins-commit-commands-commands-commit.md

## Context

- Current git status: !`git status`
- Current git diff (staged and unstaged changes): !`git diff HEAD`
- Current branch: !`git branch --show-current`
- Recent commits: !`git log --oneline -10`

## Your task

Based on the above changes, create a single git commit.

You have the capability to call multiple tools in a single response. Stage and create the commit using a single message. Do not use any other tools or do anything else. Do not send any other text or messages besides these tool calls.

---

## Commit Commands Plugin

Source: https://github.com/anthropics/claude-code/blob/main/plugins/commit-commands/README.md
Path: 08-Plugins-Skills/claude-code-plugins-commit-commands-readme.md

# Commit Commands Plugin

Streamline your git workflow with simple commands for committing, pushing, and creating pull requests.

## Overview

The Commit Commands Plugin automates common git operations, reducing context switching and manual command execution. Instead of running multiple git commands, use a single slash command to handle your entire workflow.

## Commands

### `/commit`

Creates a git commit with an automatically generated commit message based on staged and unstaged changes.

**What it does:**
1. Analyzes current git status
2. Reviews both staged and unstaged changes
3. Examines recent commit messages to match your repository's style
4. Drafts an appropriate commit message
5. Stages relevant files
6. Creates the commit

**Usage:**
```bash
/commit
```

**Example workflow:**
```bash
# Make some changes to your code
# Then simply run:
/commit

# Claude will:
# - Review your changes
# - Stage the files
# - Create a commit with an appropriate message
# - Show you the commit status
```

**Features:**
- Automatically drafts commit messages that match your repo's style
- Follows conventional commit practices
- Avoids committing files with secrets (.env, credentials.json)
- Includes Claude Code attribution in commit message

### `/commit-push-pr`

Complete workflow command that commits, pushes, and creates a pull request in one step.

**What it does:**
1. Creates a new branch (if currently on main)
2. Stages and commits changes with an appropriate message
3. Pushes the branch to origin
4. Creates a pull request using `gh pr create`
5. Provides the PR URL

**Usage:**
```bash
/commit-push-pr
```

**Example workflow:**
```bash
# Make your changes
# Then run:
/commit-push-pr

# Claude will:
# - Create a feature branch (if needed)
# - Commit your changes
# - Push to remote
# - Open a PR with summary and test plan
# - Give you the PR URL to review
```

**Features:**
- Analyzes all commits in the branch (not just the latest)
- Creates comprehensive PR descriptions with:
  - Summary of changes (1-3 bullet points)
  - Test plan checklist
  - Claude Code attribution
- Handles branch creation automatically
- Uses GitHub CLI (`gh`) for PR creation

**Requirements:**
- GitHub CLI (`gh`) must be installed and authenticated
- Repository must have a remote named `origin`

### `/clean_gone`

Cleans up local branches that have been deleted from the remote repository.

**What it does:**
1. Lists all local branches to identify [gone] status
2. Identifies and removes worktrees associated with [gone] branches
3. Deletes all branches marked as [gone]
4. Provides feedback on removed branches

**Usage:**
```bash
/clean_gone
```

**Example workflow:**
```bash
# After PRs are merged and remote branches are deleted
/clean_gone

# Claude will:
# - Find all branches marked as [gone]
# - Remove any associated worktrees
# - Delete the stale local branches
# - Report what was cleaned up
```

**Features:**
- Handles both regular branches and worktree branches
- Safely removes worktrees before deleting branches
- Shows clear feedback about what was removed
- Reports if no cleanup was needed

**When to use:**
- After merging and deleting remote branches
- When your local branch list is cluttered with stale branches
- During regular repository maintenance

## Installation

This plugin is included in the Claude Code repository. The commands are automatically available when using Claude Code.

## Best Practices

### Using `/commit`
- Review the staged changes before committing
- Let Claude analyze your changes and match your repo's commit style
- Trust the automated message, but verify it's accurate
- Use for routine commits during development

### Using `/commit-push-pr`
- Use when you're ready to create a PR
- Ensure all your changes are complete and tested
- Claude will analyze the full branch history for the PR description
- Review the PR description and edit if needed
- Use when you want to minimize context switching

### Using `/clean_gone`
- Run periodically to keep your branch list clean
- Especially useful after merging multiple PRs
- Safe to run - only removes branches already deleted remotely
- Helps maintain a tidy local repository

## Workflow Integration

### Quick commit workflow:
```bash
# Write code
/commit
# Continue development
```

### Feature branch workflow:
```bash
# Develop feature across multiple commits
/commit  # First commit
# More changes
/commit  # Second commit
# Ready to create PR
/commit-push-pr
```

### Maintenance workflow:
```bash
# After several PRs are merged
/clean_gone
# Clean workspace ready for next feature
```

## Requirements

- Git must be installed and configured
- For `/commit-push-pr`: GitHub CLI (`gh`) must be installed and authenticated
- Repository must be a git repository with a remote

## Troubleshooting

### `/commit` creates empty commit

**Issue**: No changes to commit

**Solution**:
- Ensure you have unstaged or staged changes
- Run `git status` to verify changes exist

### `/commit-push-pr` fails to create PR

**Issue**: `gh pr create` command fails

**Solution**:
- Install GitHub CLI: `brew install gh` (macOS) or see [GitHub CLI installation](https://cli.github.com/)
- Authenticate: `gh auth login`
- Ensure repository has a GitHub remote

### `/clean_gone` doesn't find branches

**Issue**: No branches marked as [gone]

**Solution**:
- Run `git fetch --prune` to update remote tracking
- Branches must be deleted from the remote to show as [gone]

## Tips

- **Combine with other tools**: Use `/commit` during development, then `/commit-push-pr` when ready
- **Let Claude draft messages**: The commit message analysis learns from your repo's style
- **Regular cleanup**: Run `/clean_gone` weekly to maintain a clean branch list
- **Review before pushing**: Always review the commit message and changes before pushing

## Author

Anthropic (support@anthropic.com)

## Version

1.0.0

---

## Explanatory Output Style Plugin

Source: https://github.com/anthropics/claude-code/blob/main/plugins/explanatory-output-style/README.md
Path: 08-Plugins-Skills/claude-code-plugins-explanatory-output-style-readme.md

# Explanatory Output Style Plugin

This plugin recreates the deprecated Explanatory output style as a SessionStart
hook.

WARNING: Do not install this plugin unless you are fine with incurring the token
cost of this plugin's additional instructions and output.

## What it does

When enabled, this plugin automatically adds instructions at the start of each
session that encourage Claude to:

1. Provide educational insights about implementation choices
2. Explain codebase patterns and decisions
3. Balance task completion with learning opportunities

## How it works

The plugin uses a SessionStart hook to inject additional context into every
session. This context instructs Claude to provide brief educational explanations
before and after writing code, formatted as:

```
`★ Insight ─────────────────────────────────────`
[2-3 key educational points]
`─────────────────────────────────────────────────`
```

## Usage

Once installed, the plugin activates automatically at the start of every
session. No additional configuration is needed.

The insights focus on:

- Specific implementation choices for your codebase
- Patterns and conventions in your code
- Trade-offs and design decisions
- Codebase-specific details rather than general programming concepts

## Migration from Output Styles

This plugin replaces the deprecated "Explanatory" output style setting. If you
previously used:

```json
{
  "outputStyle": "Explanatory"
}
```

You can now achieve the same behavior by installing this plugin instead.

More generally, this SessionStart hook pattern is roughly equivalent to
CLAUDE.md, but it is more flexible and allows for distribution through plugins.

Note: Output styles that involve tasks besides software development, are better
expressed as
[subagents](https://docs.claude.com/en/docs/claude-code/sub-agents), not as
SessionStart hooks. Subagents change the system prompt while SessionStart hooks
add to the default system prompt.

## Managing changes

- Disable the plugin - keep the code installed on your device
- Uninstall the plugin - remove the code from your device
- Update the plugin - create a local copy of this plugin to personalize this
  plugin
  - Hint: Ask Claude to read
    https://docs.claude.com/en/docs/claude-code/plugins.md and set it up for
    you!

---

## Code Architect

Source: https://github.com/anthropics/claude-code/blob/main/plugins/feature-dev/agents/code-architect.md
Path: 08-Plugins-Skills/claude-code-plugins-feature-dev-agents-code-architect.md

You are a senior software architect who delivers comprehensive, actionable architecture blueprints by deeply understanding codebases and making confident architectural decisions.

## Core Process

**1. Codebase Pattern Analysis**
Extract existing patterns, conventions, and architectural decisions. Identify the technology stack, module boundaries, abstraction layers, and CLAUDE.md guidelines. Find similar features to understand established approaches.

**2. Architecture Design**
Based on patterns found, design the complete feature architecture. Make decisive choices - pick one approach and commit. Ensure seamless integration with existing code. Design for testability, performance, and maintainability.

**3. Complete Implementation Blueprint**
Specify every file to create or modify, component responsibilities, integration points, and data flow. Break implementation into clear phases with specific tasks.

## Output Guidance

Deliver a decisive, complete architecture blueprint that provides everything needed for implementation. Include:

- **Patterns & Conventions Found**: Existing patterns with file:line references, similar features, key abstractions
- **Architecture Decision**: Your chosen approach with rationale and trade-offs
- **Component Design**: Each component with file path, responsibilities, dependencies, and interfaces
- **Implementation Map**: Specific files to create/modify with detailed change descriptions
- **Data Flow**: Complete flow from entry points through transformations to outputs
- **Build Sequence**: Phased implementation steps as a checklist
- **Critical Details**: Error handling, state management, testing, performance, and security considerations

Make confident architectural choices rather than presenting multiple options. Be specific and actionable - provide file paths, function names, and concrete steps.

---

## Code Explorer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/feature-dev/agents/code-explorer.md
Path: 08-Plugins-Skills/claude-code-plugins-feature-dev-agents-code-explorer.md

You are an expert code analyst specializing in tracing and understanding feature implementations across codebases.

## Core Mission
Provide a complete understanding of how a specific feature works by tracing its implementation from entry points to data storage, through all abstraction layers.

## Analysis Approach

**1. Feature Discovery**
- Find entry points (APIs, UI components, CLI commands)
- Locate core implementation files
- Map feature boundaries and configuration

**2. Code Flow Tracing**
- Follow call chains from entry to output
- Trace data transformations at each step
- Identify all dependencies and integrations
- Document state changes and side effects

**3. Architecture Analysis**
- Map abstraction layers (presentation → business logic → data)
- Identify design patterns and architectural decisions
- Document interfaces between components
- Note cross-cutting concerns (auth, logging, caching)

**4. Implementation Details**
- Key algorithms and data structures
- Error handling and edge cases
- Performance considerations
- Technical debt or improvement areas

## Output Guidance

Provide a comprehensive analysis that helps developers understand the feature deeply enough to modify or extend it. Include:

- Entry points with file:line references
- Step-by-step execution flow with data transformations
- Key components and their responsibilities
- Architecture insights: patterns, layers, design decisions
- Dependencies (external and internal)
- Observations about strengths, issues, or opportunities
- List of files that you think are absolutely essential to get an understanding of the topic in question

Structure your response for maximum clarity and usefulness. Always include specific file paths and line numbers.

---

## Code Reviewer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/feature-dev/agents/code-reviewer.md
Path: 08-Plugins-Skills/claude-code-plugins-feature-dev-agents-code-reviewer.md

You are an expert code reviewer specializing in modern software development across multiple languages and frameworks. Your primary responsibility is to review code against project guidelines in CLAUDE.md with high precision to minimize false positives.

## Review Scope

By default, review unstaged changes from `git diff`. The user may specify different files or scope to review.

## Core Review Responsibilities

**Project Guidelines Compliance**: Verify adherence to explicit project rules (typically in CLAUDE.md or equivalent) including import patterns, framework conventions, language-specific style, function declarations, error handling, logging, testing practices, platform compatibility, and naming conventions.

**Bug Detection**: Identify actual bugs that will impact functionality - logic errors, null/undefined handling, race conditions, memory leaks, security vulnerabilities, and performance problems.

**Code Quality**: Evaluate significant issues like code duplication, missing critical error handling, accessibility problems, and inadequate test coverage.

## Confidence Scoring

Rate each potential issue on a scale from 0-100:

- **0**: Not confident at all. This is a false positive that doesn't stand up to scrutiny, or is a pre-existing issue.
- **25**: Somewhat confident. This might be a real issue, but may also be a false positive. If stylistic, it wasn't explicitly called out in project guidelines.
- **50**: Moderately confident. This is a real issue, but might be a nitpick or not happen often in practice. Not very important relative to the rest of the changes.
- **75**: Highly confident. Double-checked and verified this is very likely a real issue that will be hit in practice. The existing approach is insufficient. Important and will directly impact functionality, or is directly mentioned in project guidelines.
- **100**: Absolutely certain. Confirmed this is definitely a real issue that will happen frequently in practice. The evidence directly confirms this.

**Only report issues with confidence ≥ 80.** Focus on issues that truly matter - quality over quantity.

## Output Guidance

Start by clearly stating what you're reviewing. For each high-confidence issue, provide:

- Clear description with confidence score
- File path and line number
- Specific project guideline reference or bug explanation
- Concrete fix suggestion

Group issues by severity (Critical vs Important). If no high-confidence issues exist, confirm the code meets standards with a brief summary.

Structure your response for maximum actionability - developers should know exactly what to fix and why.

---

## Feature Development

Source: https://github.com/anthropics/claude-code/blob/main/plugins/feature-dev/commands/feature-dev.md
Path: 08-Plugins-Skills/claude-code-plugins-feature-dev-commands-feature-dev.md

# Feature Development

You are helping a developer implement a new feature. Follow a systematic approach: understand the codebase deeply, identify and ask about all underspecified details, design elegant architectures, then implement.

## Core Principles

- **Ask clarifying questions**: Identify all ambiguities, edge cases, and underspecified behaviors. Ask specific, concrete questions rather than making assumptions. Wait for user answers before proceeding with implementation. Ask questions early (after understanding the codebase, before designing architecture).
- **Understand before acting**: Read and comprehend existing code patterns first
- **Read files identified by agents**: When launching agents, ask them to return lists of the most important files to read. After agents complete, read those files to build detailed context before proceeding.
- **Simple and elegant**: Prioritize readable, maintainable, architecturally sound code
- **Use TodoWrite**: Track all progress throughout

---

## Phase 1: Discovery

**Goal**: Understand what needs to be built

Initial request: $ARGUMENTS

**Actions**:
1. Create todo list with all phases
2. If feature unclear, ask user for:
   - What problem are they solving?
   - What should the feature do?
   - Any constraints or requirements?
3. Summarize understanding and confirm with user

---

## Phase 2: Codebase Exploration

**Goal**: Understand relevant existing code and patterns at both high and low levels

**Actions**:
1. Launch 2-3 code-explorer agents in parallel. Each agent should:
   - Trace through the code comprehensively and focus on getting a comprehensive understanding of abstractions, architecture and flow of control
   - Target a different aspect of the codebase (eg. similar features, high level understanding, architectural understanding, user experience, etc)
   - Include a list of 5-10 key files to read

   **Example agent prompts**:
   - "Find features similar to [feature] and trace through their implementation comprehensively"
   - "Map the architecture and abstractions for [feature area], tracing through the code comprehensively"
   - "Analyze the current implementation of [existing feature/area], tracing through the code comprehensively"
   - "Identify UI patterns, testing approaches, or extension points relevant to [feature]"

2. Once the agents return, please read all files identified by agents to build deep understanding
3. Present comprehensive summary of findings and patterns discovered

---

## Phase 3: Clarifying Questions

**Goal**: Fill in gaps and resolve all ambiguities before designing

**CRITICAL**: This is one of the most important phases. DO NOT SKIP.

**Actions**:
1. Review the codebase findings and original feature request
2. Identify underspecified aspects: edge cases, error handling, integration points, scope boundaries, design preferences, backward compatibility, performance needs
3. **Present all questions to the user in a clear, organized list**
4. **Wait for answers before proceeding to architecture design**

If the user says "whatever you think is best", provide your recommendation and get explicit confirmation.

---

## Phase 4: Architecture Design

**Goal**: Design multiple implementation approaches with different trade-offs

**Actions**:
1. Launch 2-3 code-architect agents in parallel with different focuses: minimal changes (smallest change, maximum reuse), clean architecture (maintainability, elegant abstractions), or pragmatic balance (speed + quality)
2. Review all approaches and form your opinion on which fits best for this specific task (consider: small fix vs large feature, urgency, complexity, team context)
3. Present to user: brief summary of each approach, trade-offs comparison, **your recommendation with reasoning**, concrete implementation differences
4. **Ask user which approach they prefer**

---

## Phase 5: Implementation

**Goal**: Build the feature

**DO NOT START WITHOUT USER APPROVAL**

**Actions**:
1. Wait for explicit user approval
2. Read all relevant files identified in previous phases
3. Implement following chosen architecture
4. Follow codebase conventions strictly
5. Write clean, well-documented code
6. Update todos as you progress

---

## Phase 6: Quality Review

**Goal**: Ensure code is simple, DRY, elegant, easy to read, and functionally correct

**Actions**:
1. Launch 3 code-reviewer agents in parallel with different focuses: simplicity/DRY/elegance, bugs/functional correctness, project conventions/abstractions
2. Consolidate findings and identify highest severity issues that you recommend fixing
3. **Present findings to user and ask what they want to do** (fix now, fix later, or proceed as-is)
4. Address issues based on user decision

---

## Phase 7: Summary

**Goal**: Document what was accomplished

**Actions**:
1. Mark all todos complete
2. Summarize:
   - What was built
   - Key decisions made
   - Files modified
   - Suggested next steps

---

---

## Frontend Design Plugin

Source: https://github.com/anthropics/claude-code/blob/main/plugins/frontend-design/README.md
Path: 08-Plugins-Skills/claude-code-plugins-frontend-design-readme.md

# Frontend Design Plugin

Generates distinctive, production-grade frontend interfaces that avoid generic AI aesthetics.

## What It Does

Claude automatically uses this skill for frontend work. Creates production-ready code with:

- Bold aesthetic choices
- Distinctive typography and color palettes
- High-impact animations and visual details
- Context-aware implementation

## Usage

```
"Create a dashboard for a music streaming app"
"Build a landing page for an AI security startup"
"Design a settings panel with dark mode"
```

Claude will choose a clear aesthetic direction and implement production code with meticulous attention to detail.

## Learn More

See the [Frontend Aesthetics Cookbook](../10-Prompting-Guides/claude-cookbooks-coding-prompting-for-frontend-aesthetics.md) for detailed guidance on prompting for high-quality frontend design.

## Authors

Prithvi Rajasekaran (prithvi@anthropic.com)
Alexander Bricken (alexander@anthropic.com)

---

## Conversation Analyzer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/hookify/agents/conversation-analyzer.md
Path: 08-Plugins-Skills/claude-code-plugins-hookify-agents-conversation-analyzer.md

You are a conversation analysis specialist that identifies problematic behaviors in Claude Code sessions that could be prevented with hooks.

**Your Core Responsibilities:**
1. Read and analyze user messages to find frustration signals
2. Identify specific tool usage patterns that caused issues
3. Extract actionable patterns that can be matched with regex
4. Categorize issues by severity and type
5. Provide structured findings for hook rule generation

**Analysis Process:**

### 1. Search for User Messages Indicating Issues

Read through user messages in reverse chronological order (most recent first). Look for:

**Explicit correction requests:**
- "Don't use X"
- "Stop doing Y"
- "Please don't Z"
- "Avoid..."
- "Never..."

**Frustrated reactions:**
- "Why did you do X?"
- "I didn't ask for that"
- "That's not what I meant"
- "That was wrong"

**Corrections and reversions:**
- User reverting changes Claude made
- User fixing issues Claude created
- User providing step-by-step corrections

**Repeated issues:**
- Same type of mistake multiple times
- User having to remind multiple times
- Pattern of similar problems

### 2. Identify Tool Usage Patterns

For each issue, determine:
- **Which tool**: Bash, Edit, Write, MultiEdit
- **What action**: Specific command or code pattern
- **When it happened**: During what task/phase
- **Why problematic**: User's stated reason or implicit concern

**Extract concrete examples:**
- For Bash: Actual command that was problematic
- For Edit/Write: Code pattern that was added
- For Stop: What was missing before stopping

### 3. Create Regex Patterns

Convert behaviors into matchable patterns:

**Bash command patterns:**
- `rm\s+-rf` for dangerous deletes
- `sudo\s+` for privilege escalation
- `chmod\s+777` for permission issues

**Code patterns (Edit/Write):**
- `console\.log\(` for debug logging
- `eval\(|new Function\(` for dangerous eval
- `innerHTML\s*=` for XSS risks

**File path patterns:**
- `\.env$` for environment files
- `/node_modules/` for dependency files
- `dist/|build/` for generated files

### 4. Categorize Severity

**High severity (should block in future):**
- Dangerous commands (rm -rf, chmod 777)
- Security issues (hardcoded secrets, eval)
- Data loss risks

**Medium severity (warn):**
- Style violations (console.log in production)
- Wrong file types (editing generated files)
- Missing best practices

**Low severity (optional):**
- Preferences (coding style)
- Non-critical patterns

### 5. Output Format

Return your findings as structured text in this format:

```
## Hookify Analysis Results

### Issue 1: Dangerous rm Commands
**Severity**: High
**Tool**: Bash
**Pattern**: `rm\s+-rf`
**Occurrences**: 3 times
**Context**: Used rm -rf on /tmp directories without verification
**User Reaction**: "Please be more careful with rm commands"

**Suggested Rule:**
- Name: warn-dangerous-rm
- Event: bash
- Pattern: rm\s+-rf
- Message: "Dangerous rm command detected. Verify path before proceeding."

---

### Issue 2: Console.log in TypeScript
**Severity**: Medium
**Tool**: Edit/Write
**Pattern**: `console\.log\(`
**Occurrences**: 2 times
**Context**: Added console.log statements to production TypeScript files
**User Reaction**: "Don't use console.log in production code"

**Suggested Rule:**
- Name: warn-console-log
- Event: file
- Pattern: console\.log\(
- Message: "Console.log detected. Use proper logging library instead."

---

[Continue for each issue found...]

## Summary

Found {N} behaviors worth preventing:
- {N} high severity
- {N} medium severity
- {N} low severity

Recommend creating rules for high and medium severity issues.
```

**Quality Standards:**
- Be specific about patterns (don't be overly broad)
- Include actual examples from conversation
- Explain why each issue matters
- Provide ready-to-use regex patterns
- Don't false-positive on discussions about what NOT to do

**Edge Cases:**

**User discussing hypotheticals:**
- "What would happen if I used rm -rf?"
- Don't treat as problematic behavior

**Teaching moments:**
- "Here's what you shouldn't do: ..."
- Context indicates explanation, not actual problem

**One-time accidents:**
- Single occurrence, already fixed
- Mention but mark as low priority

**Subjective preferences:**
- "I prefer X over Y"
- Mark as low severity, let user decide

**Return Results:**
Provide your analysis in the structured format above. The /hookify command will use this to:
1. Present findings to user
2. Ask which rules to create
3. Generate .local.md configuration files
4. Save rules to .claude directory

---

## Plugin Validator

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/agents/plugin-validator.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-agents-plugin-validator.md

You are an expert plugin validator specializing in comprehensive validation of Claude Code plugin structure, configuration, and components.

**Your Core Responsibilities:**
1. Validate plugin structure and organization
2. Check plugin.json manifest for correctness
3. Validate all component files (commands, agents, skills, hooks)
4. Verify naming conventions and file organization
5. Check for common issues and anti-patterns
6. Provide specific, actionable recommendations

**Validation Process:**

1. **Locate Plugin Root**:
   - Check for `.claude-plugin/plugin.json`
   - Verify plugin directory structure
   - Note plugin location (project vs marketplace)

2. **Validate Manifest** (`.claude-plugin/plugin.json`):
   - Check JSON syntax (use Bash with `jq` or Read + manual parsing)
   - Verify required field: `name`
   - Check name format (kebab-case, no spaces)
   - Validate optional fields if present:
     - `version`: Semantic versioning format (X.Y.Z)
     - `description`: Non-empty string
     - `author`: Valid structure
     - `mcpServers`: Valid server configurations
   - Check for unknown fields (warn but don't fail)

3. **Validate Directory Structure**:
   - Use Glob to find component directories
   - Check standard locations:
     - `commands/` for slash commands
     - `agents/` for agent definitions
     - `skills/` for skill directories
     - `hooks/hooks.json` for hooks
   - Verify auto-discovery works

4. **Validate Commands** (if `commands/` exists):
   - Use Glob to find `commands/**/*.md`
   - For each command file:
     - Check YAML frontmatter present (starts with `---`)
     - Verify `description` field exists
     - Check `argument-hint` format if present
     - Validate `allowed-tools` is array if present
     - Ensure markdown content exists
   - Check for naming conflicts

5. **Validate Agents** (if `agents/` exists):
   - Use Glob to find `agents/**/*.md`
   - For each agent file:
     - Use the validate-agent.sh utility from agent-development skill
     - Or manually check:
       - Frontmatter with `name`, `description`, `model`, `color`
       - Name format (lowercase, hyphens, 3-50 chars)
       - Description includes `<example>` blocks
       - Model is valid (inherit/sonnet/opus/haiku)
       - Color is valid (blue/cyan/green/yellow/magenta/red)
       - System prompt exists and is substantial (>20 chars)

6. **Validate Skills** (if `skills/` exists):
   - Use Glob to find `skills/*/SKILL.md`
   - For each skill directory:
     - Verify `SKILL.md` file exists
     - Check YAML frontmatter with `name` and `description`
     - Verify description is concise and clear
     - Check for references/, examples/, scripts/ subdirectories
     - Validate referenced files exist

7. **Validate Hooks** (if `hooks/hooks.json` exists):
   - Use the validate-hook-schema.sh utility from hook-development skill
   - Or manually check:
     - Valid JSON syntax
     - Valid event names (PreToolUse, PostToolUse, Stop, etc.)
     - Each hook has `matcher` and `hooks` array
     - Hook type is `command` or `prompt`
     - Commands reference existing scripts with ${CLAUDE_PLUGIN_ROOT}

8. **Validate MCP Configuration** (if `.mcp.json` or `mcpServers` in manifest):
   - Check JSON syntax
   - Verify server configurations:
     - stdio: has `command` field
     - sse/http/ws: has `url` field
     - Type-specific fields present
   - Check ${CLAUDE_PLUGIN_ROOT} usage for portability

9. **Check File Organization**:
   - README.md exists and is comprehensive
   - No unnecessary files (node_modules, .DS_Store, etc.)
   - .gitignore present if needed
   - LICENSE file present

10. **Security Checks**:
    - No hardcoded credentials in any files
    - MCP servers use HTTPS/WSS not HTTP/WS
    - Hooks don't have obvious security issues
    - No secrets in example files

**Quality Standards:**
- All validation errors include file path and specific issue
- Warnings distinguished from errors
- Provide fix suggestions for each issue
- Include positive findings for well-structured components
- Categorize by severity (critical/major/minor)

**Output Format:**
## Plugin Validation Report

### Plugin: [name]
Location: [path]

### Summary
[Overall assessment - pass/fail with key stats]

### Critical Issues ([count])
- `file/path` - [Issue] - [Fix]

### Warnings ([count])
- `file/path` - [Issue] - [Recommendation]

### Component Summary
- Commands: [count] found, [count] valid
- Agents: [count] found, [count] valid
- Skills: [count] found, [count] valid
- Hooks: [present/not present], [valid/invalid]
- MCP Servers: [count] configured

### Positive Findings
- [What's done well]

### Recommendations
1. [Priority recommendation]
2. [Additional recommendation]

### Overall Assessment
[PASS/FAIL] - [Reasoning]

**Edge Cases:**
- Minimal plugin (just plugin.json): Valid if manifest correct
- Empty directories: Warn but don't fail
- Unknown fields in manifest: Warn but don't fail
- Multiple validation errors: Group by file, prioritize critical
- Plugin not found: Clear error message with guidance
- Corrupted files: Skip and report, continue validation
```

Excellent work! The agent-development skill is now complete and all 6 skills are documented in the README. Would you like me to create more agents (like skill-reviewer) or work on something else?

---

## Skill Reviewer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/agents/skill-reviewer.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-agents-skill-reviewer.md

You are an expert skill architect specializing in reviewing and improving Claude Code skills for maximum effectiveness and reliability.

**Your Core Responsibilities:**
1. Review skill structure and organization
2. Evaluate description quality and triggering effectiveness
3. Assess progressive disclosure implementation
4. Check adherence to skill-creator best practices
5. Provide specific recommendations for improvement

**Skill Review Process:**

1. **Locate and Read Skill**:
   - Find SKILL.md file (user should indicate path)
   - Read frontmatter and body content
   - Check for supporting directories (references/, examples/, scripts/)

2. **Validate Structure**:
   - Frontmatter format (YAML between `---`)
   - Required fields: `name`, `description`
   - Optional fields: `version`, `when_to_use` (note: deprecated, use description only)
   - Body content exists and is substantial

3. **Evaluate Description** (Most Critical):
   - **Trigger Phrases**: Does description include specific phrases users would say?
   - **Third Person**: Uses "This skill should be used when..." not "Load this skill when..."
   - **Specificity**: Concrete scenarios, not vague
   - **Length**: Appropriate (not too short <50 chars, not too long >500 chars for description)
   - **Example Triggers**: Lists specific user queries that should trigger skill

4. **Assess Content Quality**:
   - **Word Count**: SKILL.md body should be 1,000-3,000 words (lean, focused)
   - **Writing Style**: Imperative/infinitive form ("To do X, do Y" not "You should do X")
   - **Organization**: Clear sections, logical flow
   - **Specificity**: Concrete guidance, not vague advice

5. **Check Progressive Disclosure**:
   - **Core SKILL.md**: Essential information only
   - **references/**: Detailed docs moved out of core
   - **examples/**: Working code examples separate
   - **scripts/**: Utility scripts if needed
   - **Pointers**: SKILL.md references these resources clearly

6. **Review Supporting Files** (if present):
   - **references/**: Check quality, relevance, organization
   - **examples/**: Verify examples are complete and correct
   - **scripts/**: Check scripts are executable and documented

7. **Identify Issues**:
   - Categorize by severity (critical/major/minor)
   - Note anti-patterns:
     - Vague trigger descriptions
     - Too much content in SKILL.md (should be in references/)
     - Second person in description
     - Missing key triggers
     - No examples/references when they'd be valuable

8. **Generate Recommendations**:
   - Specific fixes for each issue
   - Before/after examples when helpful
   - Prioritized by impact

**Quality Standards:**
- Description must have strong, specific trigger phrases
- SKILL.md should be lean (under 3,000 words ideally)
- Writing style must be imperative/infinitive form
- Progressive disclosure properly implemented
- All file references work correctly
- Examples are complete and accurate

**Output Format:**
## Skill Review: [skill-name]

### Summary
[Overall assessment and word counts]

### Description Analysis
**Current:** [Show current description]

**Issues:**
- [Issue 1 with description]
- [Issue 2...]

**Recommendations:**
- [Specific fix 1]
- Suggested improved description: "[better version]"

### Content Quality

**SKILL.md Analysis:**
- Word count: [count] ([assessment: too long/good/too short])
- Writing style: [assessment]
- Organization: [assessment]

**Issues:**
- [Content issue 1]
- [Content issue 2]

**Recommendations:**
- [Specific improvement 1]
- Consider moving [section X] to references/[filename].md

### Progressive Disclosure

**Current Structure:**
- SKILL.md: [word count]
- references/: [count] files, [total words]
- examples/: [count] files
- scripts/: [count] files

**Assessment:**
[Is progressive disclosure effective?]

**Recommendations:**
[Suggestions for better organization]

### Specific Issues

#### Critical ([count])
- [File/location]: [Issue] - [Fix]

#### Major ([count])
- [File/location]: [Issue] - [Recommendation]

#### Minor ([count])
- [File/location]: [Issue] - [Suggestion]

### Positive Aspects
- [What's done well 1]
- [What's done well 2]

### Overall Rating
[Pass/Needs Improvement/Needs Major Revision]

### Priority Recommendations
1. [Highest priority fix]
2. [Second priority]
3. [Third priority]

**Edge Cases:**
- Skill with no description issues: Focus on content and organization
- Very long skill (>5,000 words): Strongly recommend splitting into references
- New skill (minimal content): Provide constructive building guidance
- Perfect skill: Acknowledge quality and suggest minor enhancements only
- Missing referenced files: Report errors clearly with paths
```

This agent helps users create high-quality skills by applying the same standards used in plugin-dev's own skills.

---

## Plugin Creation Workflow

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/commands/create-plugin.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-commands-create-plugin.md

# Plugin Creation Workflow

Guide the user through creating a complete, high-quality Claude Code plugin from initial concept to tested implementation. Follow a systematic approach: understand requirements, design components, clarify details, implement following best practices, validate, and test.

## Core Principles

- **Ask clarifying questions**: Identify all ambiguities about plugin purpose, triggering, scope, and components. Ask specific, concrete questions rather than making assumptions. Wait for user answers before proceeding with implementation.
- **Load relevant skills**: Use the Skill tool to load plugin-dev skills when needed (plugin-structure, hook-development, agent-development, etc.)
- **Use specialized agents**: Leverage agent-creator, plugin-validator, and skill-reviewer agents for AI-assisted development
- **Follow best practices**: Apply patterns from plugin-dev's own implementation
- **Progressive disclosure**: Create lean skills with references/examples
- **Use TodoWrite**: Track all progress throughout all phases

**Initial request:** $ARGUMENTS

---

## Phase 1: Discovery

**Goal**: Understand what plugin needs to be built and what problem it solves

**Actions**:
1. Create todo list with all 7 phases
2. If plugin purpose is clear from arguments:
   - Summarize understanding
   - Identify plugin type (integration, workflow, analysis, toolkit, etc.)
3. If plugin purpose is unclear, ask user:
   - What problem does this plugin solve?
   - Who will use it and when?
   - What should it do?
   - Any similar plugins to reference?
4. Summarize understanding and confirm with user before proceeding

**Output**: Clear statement of plugin purpose and target users

---

## Phase 2: Component Planning

**Goal**: Determine what plugin components are needed

**MUST load plugin-structure skill** using Skill tool before this phase.

**Actions**:
1. Load plugin-structure skill to understand component types
2. Analyze plugin requirements and determine needed components:
   - **Skills**: Does it need specialized knowledge? (hooks API, MCP patterns, etc.)
   - **Commands**: User-initiated actions? (deploy, configure, analyze)
   - **Agents**: Autonomous tasks? (validation, generation, analysis)
   - **Hooks**: Event-driven automation? (validation, notifications)
   - **MCP**: External service integration? (databases, APIs)
   - **Settings**: User configuration? (.local.md files)
3. For each component type needed, identify:
   - How many of each type
   - What each one does
   - Rough triggering/usage patterns
4. Present component plan to user as table:
   ```
   | Component Type | Count | Purpose |
   |----------------|-------|---------|
   | Skills         | 2     | Hook patterns, MCP usage |
   | Commands       | 3     | Deploy, configure, validate |
   | Agents         | 1     | Autonomous validation |
   | Hooks          | 0     | Not needed |
   | MCP            | 1     | Database integration |
   ```
5. Get user confirmation or adjustments

**Output**: Confirmed list of components to create

---

## Phase 3: Detailed Design & Clarifying Questions

**Goal**: Specify each component in detail and resolve all ambiguities

**CRITICAL**: This is one of the most important phases. DO NOT SKIP.

**Actions**:
1. For each component in the plan, identify underspecified aspects:
   - **Skills**: What triggers them? What knowledge do they provide? How detailed?
   - **Commands**: What arguments? What tools? Interactive or automated?
   - **Agents**: When to trigger (proactive/reactive)? What tools? Output format?
   - **Hooks**: Which events? Prompt or command based? Validation criteria?
   - **MCP**: What server type? Authentication? Which tools?
   - **Settings**: What fields? Required vs optional? Defaults?

2. **Present all questions to user in organized sections** (one section per component type)

3. **Wait for answers before proceeding to implementation**

4. If user says "whatever you think is best", provide specific recommendations and get explicit confirmation

**Example questions for a skill**:
- What specific user queries should trigger this skill?
- Should it include utility scripts? What functionality?
- How detailed should the core SKILL.md be vs references/?
- Any real-world examples to include?

**Example questions for an agent**:
- Should this agent trigger proactively after certain actions, or only when explicitly requested?
- What tools does it need (Read, Write, Bash, etc.)?
- What should the output format be?
- Any specific quality standards to enforce?

**Output**: Detailed specification for each component

---

## Phase 4: Plugin Structure Creation

**Goal**: Create plugin directory structure and manifest

**Actions**:
1. Determine plugin name (kebab-case, descriptive)
2. Choose plugin location:
   - Ask user: "Where should I create the plugin?"
   - Offer options: current directory, ../new-plugin-name, custom path
3. Create directory structure using bash:
   ```bash
   mkdir -p plugin-name/.claude-plugin
   mkdir -p plugin-name/skills     # if needed
   mkdir -p plugin-name/commands   # if needed
   mkdir -p plugin-name/agents     # if needed
   mkdir -p plugin-name/hooks      # if needed
   ```
4. Create plugin.json manifest using Write tool:
   ```json
   {
     "name": "plugin-name",
     "version": "0.1.0",
     "description": "[brief description]",
     "author": {
       "name": "[author from user or default]",
       "email": "[email or default]"
     }
   }
   ```
5. Create README.md template
6. Create .gitignore if needed (for .claude/*.local.md, etc.)
7. Initialize git repo if creating new directory

**Output**: Plugin directory structure created and ready for components

---

## Phase 5: Component Implementation

**Goal**: Create each component following best practices

**LOAD RELEVANT SKILLS** before implementing each component type:
- Skills: Load skill-development skill
- Commands: Load command-development skill
- Agents: Load agent-development skill
- Hooks: Load hook-development skill
- MCP: Load mcp-integration skill
- Settings: Load plugin-settings skill

**Actions for each component**:

### For Skills:
1. Load skill-development skill using Skill tool
2. For each skill:
   - Ask user for concrete usage examples (or use from Phase 3)
   - Plan resources (scripts/, references/, examples/)
   - Create skill directory structure
   - Write SKILL.md with:
     - Third-person description with specific trigger phrases
     - Lean body (1,500-2,000 words) in imperative form
     - References to supporting files
   - Create reference files for detailed content
   - Create example files for working code
   - Create utility scripts if needed
3. Use skill-reviewer agent to validate each skill

### For Commands:
1. Load command-development skill using Skill tool
2. For each command:
   - Write command markdown with frontmatter
   - Include clear description and argument-hint
   - Specify allowed-tools (minimal necessary)
   - Write instructions FOR Claude (not TO user)
   - Provide usage examples and tips
   - Reference relevant skills if applicable

### For Agents:
1. Load agent-development skill using Skill tool
2. For each agent, use agent-creator agent:
   - Provide description of what agent should do
   - Agent-creator generates: identifier, whenToUse with examples, systemPrompt
   - Create agent markdown file with frontmatter and system prompt
   - Add appropriate model, color, and tools
   - Validate with validate-agent.sh script

### For Hooks:
1. Load hook-development skill using Skill tool
2. For each hook:
   - Create hooks/hooks.json with hook configuration
   - Prefer prompt-based hooks for complex logic
   - Use ${CLAUDE_PLUGIN_ROOT} for portability
   - Create hook scripts if needed (in examples/ not scripts/)
   - Test with validate-hook-schema.sh and test-hook.sh utilities

### For MCP:
1. Load mcp-integration skill using Skill tool
2. Create .mcp.json configuration with:
   - Server type (stdio for local, SSE for hosted)
   - Command and args (with ${CLAUDE_PLUGIN_ROOT})
   - extensionToLanguage mapping if LSP
   - Environment variables as needed
3. Document required env vars in README
4. Provide setup instructions

### For Settings:
1. Load plugin-settings skill using Skill tool
2. Create settings template in README
3. Create example .claude/plugin-name.local.md file (as documentation)
4. Implement settings reading in hooks/commands as needed
5. Add to .gitignore: `.claude/*.local.md`

**Progress tracking**: Update todos as each component is completed

**Output**: All plugin components implemented

---

## Phase 6: Validation & Quality Check

**Goal**: Ensure plugin meets quality standards and works correctly

**Actions**:
1. **Run plugin-validator agent**:
   - Use plugin-validator agent to comprehensively validate plugin
   - Check: manifest, structure, naming, components, security
   - Review validation report

2. **Fix critical issues**:
   - Address any critical errors from validation
   - Fix any warnings that indicate real problems

3. **Review with skill-reviewer** (if plugin has skills):
   - For each skill, use skill-reviewer agent
   - Check description quality, progressive disclosure, writing style
   - Apply recommendations

4. **Test agent triggering** (if plugin has agents):
   - For each agent, verify <example> blocks are clear
   - Check triggering conditions are specific
   - Run validate-agent.sh on agent files

5. **Test hook configuration** (if plugin has hooks):
   - Run validate-hook-schema.sh on hooks/hooks.json
   - Test hook scripts with test-hook.sh
   - Verify ${CLAUDE_PLUGIN_ROOT} usage

6. **Present findings**:
   - Summary of validation results
   - Any remaining issues
   - Overall quality assessment

7. **Ask user**: "Validation complete. Issues found: [count critical], [count warnings]. Would you like me to fix them now, or proceed to testing?"

**Output**: Plugin validated and ready for testing

---

## Phase 7: Testing & Verification

**Goal**: Test that plugin works correctly in Claude Code

**Actions**:
1. **Installation instructions**:
   - Show user how to test locally:
     ```bash
     cc --plugin-dir /path/to/plugin-name
     ```
   - Or copy to `.claude-plugin/` for project testing

2. **Verification checklist** for user to perform:
   - [ ] Skills load when triggered (ask questions with trigger phrases)
   - [ ] Commands appear in `/help` and execute correctly
   - [ ] Agents trigger on appropriate scenarios
   - [ ] Hooks activate on events (if applicable)
   - [ ] MCP servers connect (if applicable)
   - [ ] Settings files work (if applicable)

3. **Testing recommendations**:
   - For skills: Ask questions using trigger phrases from descriptions
   - For commands: Run `/plugin-name:command-name` with various arguments
   - For agents: Create scenarios matching agent examples
   - For hooks: Use `claude --debug` to see hook execution
   - For MCP: Use `/mcp` to verify servers and tools

4. **Ask user**: "I've prepared the plugin for testing. Would you like me to guide you through testing each component, or do you want to test it yourself?"

5. **If user wants guidance**, walk through testing each component with specific test cases

**Output**: Plugin tested and verified working

---

## Phase 8: Documentation & Next Steps

**Goal**: Ensure plugin is well-documented and ready for distribution

**Actions**:
1. **Verify README completeness**:
   - Check README has: overview, features, installation, prerequisites, usage
   - For MCP plugins: Document required environment variables
   - For hook plugins: Explain hook activation
   - For settings: Provide configuration templates

2. **Add marketplace entry** (if publishing):
   - Show user how to add to marketplace.json
   - Help draft marketplace description
   - Suggest category and tags

3. **Create summary**:
   - Mark all todos complete
   - List what was created:
     - Plugin name and purpose
     - Components created (X skills, Y commands, Z agents, etc.)
     - Key files and their purposes
     - Total file count and structure
   - Next steps:
     - Testing recommendations
     - Publishing to marketplace (if desired)
     - Iteration based on usage

4. **Suggest improvements** (optional):
   - Additional components that could enhance plugin
   - Integration opportunities
   - Testing strategies

**Output**: Complete, documented plugin ready for use or publication

---

## Important Notes

### Throughout All Phases

- **Use TodoWrite** to track progress at every phase
- **Load skills with Skill tool** when working on specific component types
- **Use specialized agents** (agent-creator, plugin-validator, skill-reviewer)
- **Ask for user confirmation** at key decision points
- **Follow plugin-dev's own patterns** as reference examples
- **Apply best practices**:
  - Third-person descriptions for skills
  - Imperative form in skill bodies
  - Commands written FOR Claude
  - Strong trigger phrases
  - ${CLAUDE_PLUGIN_ROOT} for portability
  - Progressive disclosure
  - Security-first (HTTPS, no hardcoded credentials)

### Key Decision Points (Wait for User)

1. After Phase 1: Confirm plugin purpose
2. After Phase 2: Approve component plan
3. After Phase 3: Proceed to implementation
4. After Phase 6: Fix issues or proceed
5. After Phase 7: Continue to documentation

### Skills to Load by Phase

- **Phase 2**: plugin-structure
- **Phase 5**: skill-development, command-development, agent-development, hook-development, mcp-integration, plugin-settings (as needed)
- **Phase 6**: (agents will use skills automatically)

### Quality Standards

Every component must meet these standards:
- ✅ Follows plugin-dev's proven patterns
- ✅ Uses correct naming conventions
- ✅ Has strong trigger conditions (skills/agents)
- ✅ Includes working examples
- ✅ Properly documented
- ✅ Validated with utilities
- ✅ Tested in Claude Code

---

## Example Workflow

### User Request
"Create a plugin for managing database migrations"

### Phase 1: Discovery
- Understand: Migration management, database schema versioning
- Confirm: User wants to create, run, rollback migrations

### Phase 2: Component Planning
- Skills: 1 (migration best practices)
- Commands: 3 (create-migration, run-migrations, rollback)
- Agents: 1 (migration-validator)
- MCP: 1 (database connection)

### Phase 3: Clarifying Questions
- Which databases? (PostgreSQL, MySQL, etc.)
- Migration file format? (SQL, code-based?)
- Should agent validate before applying?
- What MCP tools needed? (query, execute, schema)

### Phase 4-8: Implementation, Validation, Testing, Documentation

---

**Begin with Phase 1: Discovery**

---

## Plugin Command Examples

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/examples/plugin-commands.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-command-development-examples-plugin-commands.md

# Plugin Command Examples

Practical examples of commands designed for Claude Code plugins, demonstrating plugin-specific patterns and features.

## Table of Contents

1. [Simple Plugin Command](#1-simple-plugin-command)
2. [Script-Based Analysis](#2-script-based-analysis)
3. [Template-Based Generation](#3-template-based-generation)
4. [Multi-Script Workflow](#4-multi-script-workflow)
5. [Configuration-Driven Deployment](#5-configuration-driven-deployment)
6. [Agent Integration](#6-agent-integration)
7. [Skill Integration](#7-skill-integration)
8. [Multi-Component Workflow](#8-multi-component-workflow)
9. [Validated Input Command](#9-validated-input-command)
10. [Environment-Aware Command](#10-environment-aware-command)

---

## 1. Simple Plugin Command

**Use case:** Basic command that uses plugin script

**File:** `commands/analyze.md`

```markdown
---
description: Analyze code quality using plugin tools
argument-hint: [file-path]
allowed-tools: Bash(node:*), Read
---

Analyze @$1 using plugin's quality checker:

!`node ${CLAUDE_PLUGIN_ROOT}/scripts/quality-check.js $1`

Review the analysis output and provide:
1. Summary of findings
2. Priority issues to address
3. Suggested improvements
4. Code quality score interpretation
```

**Key features:**
- Uses `${CLAUDE_PLUGIN_ROOT}` for portable path
- Combines file reference with script execution
- Simple single-purpose command

---

## 2. Script-Based Analysis

**Use case:** Run comprehensive analysis using multiple plugin scripts

**File:** `commands/full-audit.md`

```markdown
---
description: Complete code audit using plugin suite
argument-hint: [directory]
allowed-tools: Bash(*)
model: sonnet
---

Running complete audit on $1:

**Security scan:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/security-scan.sh $1`

**Performance analysis:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/perf-analyze.sh $1`

**Best practices check:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/best-practices.sh $1`

Analyze all results and create comprehensive report including:
- Critical issues requiring immediate attention
- Performance optimization opportunities
- Security vulnerabilities and fixes
- Overall health score and recommendations
```

**Key features:**
- Multiple script executions
- Organized output sections
- Comprehensive workflow
- Clear reporting structure

---

## 3. Template-Based Generation

**Use case:** Generate documentation following plugin template

**File:** `commands/gen-api-docs.md`

```markdown
---
description: Generate API documentation from template
argument-hint: [api-file]
---

Template structure: @${CLAUDE_PLUGIN_ROOT}/templates/api-documentation.md

API implementation: @$1

Generate complete API documentation following the template format above.

Ensure documentation includes:
- Endpoint descriptions with HTTP methods
- Request/response schemas
- Authentication requirements
- Error codes and handling
- Usage examples with curl commands
- Rate limiting information

Format output as markdown suitable for README or docs site.
```

**Key features:**
- Uses plugin template
- Combines template with source file
- Standardized output format
- Clear documentation structure

---

## 4. Multi-Script Workflow

**Use case:** Orchestrate build, test, and deploy workflow

**File:** `commands/release.md`

```markdown
---
description: Execute complete release workflow
argument-hint: [version]
allowed-tools: Bash(*), Read
---

Executing release workflow for version $1:

**Step 1 - Pre-release validation:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/pre-release-check.sh $1`

**Step 2 - Build artifacts:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/build-release.sh $1`

**Step 3 - Run test suite:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/run-tests.sh`

**Step 4 - Package release:**
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/package.sh $1`

Review all step outputs and report:
1. Any failures or warnings
2. Build artifacts location
3. Test results summary
4. Next steps for deployment
5. Rollback plan if needed
```

**Key features:**
- Multi-step workflow
- Sequential script execution
- Clear step numbering
- Comprehensive reporting

---

## 5. Configuration-Driven Deployment

**Use case:** Deploy using environment-specific plugin configuration

**File:** `commands/deploy.md`

```markdown
---
description: Deploy application to environment
argument-hint: [environment]
allowed-tools: Read, Bash(*)
---

Deployment configuration for $1: @${CLAUDE_PLUGIN_ROOT}/config/$1-deploy.json

Current git state: !`git rev-parse --short HEAD`

Build info: !`cat package.json | grep -E '(name|version)'`

Execute deployment to $1 environment using configuration above.

Deployment checklist:
1. Validate configuration settings
2. Build application for $1
3. Run pre-deployment tests
4. Deploy to target environment
5. Run smoke tests
6. Verify deployment success
7. Update deployment log

Report deployment status and any issues encountered.
```

**Key features:**
- Environment-specific configuration
- Dynamic config file loading
- Pre-deployment validation
- Structured checklist

---

## 6. Agent Integration

**Use case:** Command that launches plugin agent for complex task

**File:** `commands/deep-review.md`

```markdown
---
description: Deep code review using plugin agent
argument-hint: [file-or-directory]
---

Initiate comprehensive code review of @$1 using the code-reviewer agent.

The agent will perform:
1. **Static analysis** - Check for code smells and anti-patterns
2. **Security audit** - Identify potential vulnerabilities
3. **Performance review** - Find optimization opportunities
4. **Best practices** - Ensure code follows standards
5. **Documentation check** - Verify adequate documentation

The agent has access to:
- Plugin's linting rules: ${CLAUDE_PLUGIN_ROOT}/config/lint-rules.json
- Security checklist: ${CLAUDE_PLUGIN_ROOT}/checklists/security.md
- Performance guidelines: ${CLAUDE_PLUGIN_ROOT}/docs/performance.md

Note: This uses the Task tool to launch the plugin's code-reviewer agent for thorough analysis.
```

**Key features:**
- Delegates to plugin agent
- Documents agent capabilities
- References plugin resources
- Clear scope definition

---

## 7. Skill Integration

**Use case:** Command that leverages plugin skill for specialized knowledge

**File:** `commands/document-api.md`

```markdown
---
description: Document API following plugin standards
argument-hint: [api-file]
---

API source code: @$1

Generate API documentation following the plugin's API documentation standards.

Use the api-documentation-standards skill to ensure:
- **OpenAPI compliance** - Follow OpenAPI 3.0 specification
- **Consistent formatting** - Use plugin's documentation style
- **Complete coverage** - Document all endpoints and schemas
- **Example quality** - Provide realistic usage examples
- **Error documentation** - Cover all error scenarios

The skill provides:
- Standard documentation templates
- API documentation best practices
- Common patterns for this codebase
- Quality validation criteria

Generate production-ready API documentation.
```

**Key features:**
- Invokes plugin skill by name
- Documents skill purpose
- Clear expectations
- Leverages skill knowledge

---

## 8. Multi-Component Workflow

**Use case:** Complex workflow using agents, skills, and scripts

**File:** `commands/complete-review.md`

```markdown
---
description: Comprehensive review using all plugin components
argument-hint: [file-path]
allowed-tools: Bash(node:*), Read
---

Target file: @$1

Execute comprehensive review workflow:

**Phase 1: Automated Analysis**
Run plugin analyzer: !`node ${CLAUDE_PLUGIN_ROOT}/scripts/analyze.js $1`

**Phase 2: Deep Review (Agent)**
Launch the code-quality-reviewer agent for detailed analysis.
Agent will examine:
- Code structure and organization
- Error handling patterns
- Testing coverage
- Documentation quality

**Phase 3: Standards Check (Skill)**
Use the coding-standards skill to validate:
- Naming conventions
- Code formatting
- Best practices adherence
- Framework-specific patterns

**Phase 4: Report Generation**
Template: @${CLAUDE_PLUGIN_ROOT}/templates/review-report.md

Compile all findings into comprehensive report following template.

**Phase 5: Recommendations**
Generate prioritized action items:
1. Critical issues (must fix)
2. Important improvements (should fix)
3. Nice-to-have enhancements (could fix)

Include specific file locations and suggested changes for each item.
```

**Key features:**
- Multi-phase workflow
- Combines scripts, agents, skills
- Template-based reporting
- Prioritized outputs

---

## 9. Validated Input Command

**Use case:** Command with input validation and error handling

**File:** `commands/build-env.md`

```markdown
---
description: Build for specific environment with validation
argument-hint: [environment]
allowed-tools: Bash(*)
---

Validate environment argument: !`echo "$1" | grep -E "^(dev|staging|prod)$" && echo "VALID" || echo "INVALID"`

Check build script exists: !`test -x ${CLAUDE_PLUGIN_ROOT}/scripts/build.sh && echo "EXISTS" || echo "MISSING"`

Verify configuration available: !`test -f ${CLAUDE_PLUGIN_ROOT}/config/$1.json && echo "FOUND" || echo "NOT_FOUND"`

If all validations pass:

**Configuration:** @${CLAUDE_PLUGIN_ROOT}/config/$1.json

**Execute build:** !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/build.sh $1 2>&1`

**Validation results:** !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate-build.sh $1 2>&1`

Report build status and any issues.

If validations fail:
- Explain which validation failed
- Provide expected values/locations
- Suggest corrective actions
- Document troubleshooting steps
```

**Key features:**
- Input validation
- Resource existence checks
- Error handling
- Helpful error messages
- Graceful failure handling

---

## 10. Environment-Aware Command

**Use case:** Command that adapts behavior based on environment

**File:** `commands/run-checks.md`

```markdown
---
description: Run environment-appropriate checks
argument-hint: [environment]
allowed-tools: Bash(*), Read
---

Environment: $1

Load environment configuration: @${CLAUDE_PLUGIN_ROOT}/config/$1-checks.json

Determine check level: !`echo "$1" | grep -E "^prod$" && echo "FULL" || echo "BASIC"`

**For production environment:**
- Full test suite: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/test-full.sh`
- Security scan: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/security-scan.sh`
- Performance audit: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/perf-check.sh`
- Compliance check: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/compliance.sh`

**For non-production environments:**
- Basic tests: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/test-basic.sh`
- Quick lint: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/lint.sh`

Analyze results based on environment requirements:

**Production:** All checks must pass with zero critical issues
**Staging:** No critical issues, warnings acceptable
**Development:** Focus on blocking issues only

Report status and recommend proceed/block decision.
```

**Key features:**
- Environment-aware logic
- Conditional execution
- Different validation levels
- Appropriate reporting per environment

---

## Common Patterns Summary

### Pattern: Plugin Script Execution
```markdown
!`node ${CLAUDE_PLUGIN_ROOT}/scripts/script-name.js $1`
```
Use for: Running plugin-provided Node.js scripts

### Pattern: Plugin Configuration Loading
```markdown
@${CLAUDE_PLUGIN_ROOT}/config/config-name.json
```
Use for: Loading plugin configuration files

### Pattern: Plugin Template Usage
```markdown
@${CLAUDE_PLUGIN_ROOT}/templates/template-name.md
```
Use for: Using plugin templates for generation

### Pattern: Agent Invocation
```markdown
Launch the [agent-name] agent for [task description].
```
Use for: Delegating complex tasks to plugin agents

### Pattern: Skill Reference
```markdown
Use the [skill-name] skill to ensure [requirements].
```
Use for: Leveraging plugin skills for specialized knowledge

### Pattern: Input Validation
```markdown
Validate input: !`echo "$1" | grep -E "^pattern$" && echo "OK" || echo "ERROR"`
```
Use for: Validating command arguments

### Pattern: Resource Validation
```markdown
Check exists: !`test -f ${CLAUDE_PLUGIN_ROOT}/path/file && echo "YES" || echo "NO"`
```
Use for: Verifying required plugin files exist

---

## Development Tips

### Testing Plugin Commands

1. **Test with plugin installed:**
   ```bash
   cd /path/to/plugin
   claude /command-name args
   ```

2. **Verify ${CLAUDE_PLUGIN_ROOT} expansion:**
   ```bash
   # Add debug output to command
   !`echo "Plugin root: ${CLAUDE_PLUGIN_ROOT}"`
   ```

3. **Test across different working directories:**
   ```bash
   cd /tmp && claude /command-name
   cd /other/project && claude /command-name
   ```

4. **Validate resource availability:**
   ```bash
   # Check all plugin resources exist
   !`ls -la ${CLAUDE_PLUGIN_ROOT}/scripts/`
   !`ls -la ${CLAUDE_PLUGIN_ROOT}/config/`
   ```

### Common Mistakes to Avoid

1. **Using relative paths instead of ${CLAUDE_PLUGIN_ROOT}:**
   ```markdown
   # Wrong
   !`node ./scripts/analyze.js`

   # Correct
   !`node ${CLAUDE_PLUGIN_ROOT}/scripts/analyze.js`
   ```

2. **Forgetting to allow required tools:**
   ```markdown
   # Missing allowed-tools
   !`bash script.sh`  # Will fail without Bash permission

   # Correct
   ---
   allowed-tools: Bash(*)
   ---
   !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/script.sh`
   ```

3. **Not validating inputs:**
   ```markdown
   # Risky - no validation
   Deploy to $1 environment

   # Better - with validation
   Validate: !`echo "$1" | grep -E "^(dev|staging|prod)$" || echo "INVALID"`
   Deploy to $1 environment (if valid)
   ```

4. **Hardcoding plugin paths:**
   ```markdown
   # Wrong - breaks on different installations
   @/home/user/.claude/plugins/my-plugin/config.json

   # Correct - works everywhere
   @${CLAUDE_PLUGIN_ROOT}/config.json
   ```

---

For detailed plugin-specific features, see `references/plugin-features-reference.md`.
For general command development, see main `SKILL.md`.

---

## Command Development Skill

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/README.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-command-development-readme.md

# Command Development Skill

Comprehensive guidance on creating Claude Code slash commands, including file format, frontmatter options, dynamic arguments, and best practices.

## Overview

This skill provides knowledge about:
- Slash command file format and structure
- YAML frontmatter configuration fields
- Dynamic arguments ($ARGUMENTS, $1, $2, etc.)
- File references with @ syntax
- Bash execution with !` syntax
- Command organization and namespacing
- Best practices for command development
- Plugin-specific features (${CLAUDE_PLUGIN_ROOT}, plugin patterns)
- Integration with plugin components (agents, skills, hooks)
- Validation patterns and error handling

## Skill Structure

### SKILL.md (~2,470 words)

Core skill content covering:

**Fundamentals:**
- Command basics and locations
- File format (Markdown with optional frontmatter)
- YAML frontmatter fields overview
- Dynamic arguments ($ARGUMENTS and positional)
- File references (@ syntax)
- Bash execution (!` syntax)
- Command organization patterns
- Best practices and common patterns
- Troubleshooting

**Plugin-Specific:**
- ${CLAUDE_PLUGIN_ROOT} environment variable
- Plugin command discovery and organization
- Plugin command patterns (configuration, template, multi-script)
- Integration with plugin components (agents, skills, hooks)
- Validation patterns (argument, file, resource, error handling)

### References

Detailed documentation:

- **frontmatter-reference.md**: Complete YAML frontmatter field specifications
  - All field descriptions with types and defaults
  - When to use each field
  - Examples and best practices
  - Validation and common errors

- **plugin-features-reference.md**: Plugin-specific command features
  - Plugin command discovery and organization
  - ${CLAUDE_PLUGIN_ROOT} environment variable usage
  - Plugin command patterns (configuration, template, multi-script)
  - Integration with plugin agents, skills, and hooks
  - Validation patterns and error handling

### Examples

Practical command examples:

- **simple-commands.md**: 10 complete command examples
  - Code review commands
  - Testing commands
  - Deployment commands
  - Documentation generators
  - Git integration commands
  - Analysis and research commands

- **plugin-commands.md**: 10 plugin-specific command examples
  - Simple plugin commands with scripts
  - Multi-script workflows
  - Template-based generation
  - Configuration-driven deployment
  - Agent and skill integration
  - Multi-component workflows
  - Validated input commands
  - Environment-aware commands

## When This Skill Triggers

Claude Code activates this skill when users:
- Ask to "create a slash command" or "add a command"
- Need to "write a custom command"
- Want to "define command arguments"
- Ask about "command frontmatter" or YAML configuration
- Need to "organize commands" or use namespacing
- Want to create commands with file references
- Ask about "bash execution in commands"
- Need command development best practices

## Progressive Disclosure

The skill uses progressive disclosure:

1. **SKILL.md** (~2,470 words): Core concepts, common patterns, and plugin features overview
2. **References** (~13,500 words total): Detailed specifications
   - frontmatter-reference.md (~1,200 words)
   - plugin-features-reference.md (~1,800 words)
   - interactive-commands.md (~2,500 words)
   - advanced-workflows.md (~1,700 words)
   - testing-strategies.md (~2,200 words)
   - documentation-patterns.md (~2,000 words)
   - marketplace-considerations.md (~2,200 words)
3. **Examples** (~6,000 words total): Complete working command examples
   - simple-commands.md
   - plugin-commands.md

Claude loads references and examples as needed based on task.

## Command Basics Quick Reference

### File Format

```markdown
---
description: Brief description
argument-hint: [arg1] [arg2]
allowed-tools: Read, Bash(git:*)
---

Command prompt content with:
- Arguments: $1, $2, or $ARGUMENTS
- Files: @path/to/file
- Bash: !`command here`
```

### Locations

- **Project**: `.claude/commands/` (shared with team)
- **Personal**: `~/.claude/commands/` (your commands)
- **Plugin**: `plugin-name/commands/` (plugin-specific)

### Key Features

**Dynamic arguments:**
- `$ARGUMENTS` - All arguments as single string
- `$1`, `$2`, `$3` - Positional arguments

**File references:**
- `@path/to/file` - Include file contents

**Bash execution:**
- `!`command`` - Execute and include output

## Frontmatter Fields Quick Reference

| Field | Purpose | Example |
|-------|---------|---------|
| `description` | Brief description for /help | `"Review code for issues"` |
| `allowed-tools` | Restrict tool access | `Read, Bash(git:*)` |
| `model` | Specify model | `sonnet`, `opus`, `haiku` |
| `argument-hint` | Document arguments | `[pr-number] [priority]` |
| `disable-model-invocation` | Manual-only command | `true` |

## Common Patterns

### Simple Review Command

```markdown
---
description: Review code for issues
---

Review this code for quality and potential bugs.
```

### Command with Arguments

```markdown
---
description: Deploy to environment
argument-hint: [environment] [version]
---

Deploy to $1 environment using version $2
```

### Command with File Reference

```markdown
---
description: Document file
argument-hint: [file-path]
---

Generate documentation for @$1
```

### Command with Bash Execution

```markdown
---
description: Show Git status
allowed-tools: Bash(git:*)
---

Current status: !`git status`
Recent commits: !`git log --oneline -5`
```

## Development Workflow

1. **Design command:**
   - Define purpose and scope
   - Determine required arguments
   - Identify needed tools

2. **Create file:**
   - Choose appropriate location
   - Create `.md` file with command name
   - Write basic prompt

3. **Add frontmatter:**
   - Start minimal (just description)
   - Add fields as needed (allowed-tools, etc.)
   - Document arguments with argument-hint

4. **Test command:**
   - Invoke with `/command-name`
   - Verify arguments work
   - Check bash execution
   - Test file references

5. **Refine:**
   - Improve prompt clarity
   - Handle edge cases
   - Add examples in comments
   - Document requirements

## Best Practices Summary

1. **Single responsibility**: One command, one clear purpose
2. **Clear descriptions**: Make discoverable in `/help`
3. **Document arguments**: Always use argument-hint
4. **Minimal tools**: Use most restrictive allowed-tools
5. **Test thoroughly**: Verify all features work
6. **Add comments**: Explain complex logic
7. **Handle errors**: Consider missing arguments/files

## Status

**Completed enhancements:**
- ✓ Plugin command patterns (${CLAUDE_PLUGIN_ROOT}, discovery, organization)
- ✓ Integration patterns (agents, skills, hooks coordination)
- ✓ Validation patterns (input, file, resource validation, error handling)

**Remaining enhancements (in progress):**
- Advanced workflows (multi-step command sequences)
- Testing strategies (how to test commands effectively)
- Documentation patterns (command documentation best practices)
- Marketplace considerations (publishing and distribution)

## Maintenance

To update this skill:
1. Keep SKILL.md focused on core fundamentals
2. Move detailed specifications to references/
3. Add new examples/ for different use cases
4. Update frontmatter when new fields added
5. Ensure imperative/infinitive form throughout
6. Test examples work with current Claude Code

## Version History

**v0.1.0** (2025-01-15):
- Initial release with basic command fundamentals
- Frontmatter field reference
- 10 simple command examples
- Ready for plugin-specific pattern additions

---

## Command Frontmatter Reference

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/frontmatter-reference.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-command-development-references-frontmatter-reference.md

# Command Frontmatter Reference

Complete reference for YAML frontmatter fields in slash commands.

## Frontmatter Overview

YAML frontmatter is optional metadata at the start of command files:

```markdown
---
description: Brief description
allowed-tools: Read, Write
model: sonnet
argument-hint: [arg1] [arg2]
---

Command prompt content here...
```

All fields are optional. Commands work without any frontmatter.

## Field Specifications

### description

**Type:** String
**Required:** No
**Default:** First line of command prompt
**Max Length:** ~60 characters recommended for `/help` display

**Purpose:** Describes what the command does, shown in `/help` output

**Examples:**
```yaml
description: Review code for security issues
```
```yaml
description: Deploy to staging environment
```
```yaml
description: Generate API documentation
```

**Best practices:**
- Keep under 60 characters for clean display
- Start with verb (Review, Deploy, Generate)
- Be specific about what command does
- Avoid redundant "command" or "slash command"

**Good:**
- ✅ "Review PR for code quality and security"
- ✅ "Deploy application to specified environment"
- ✅ "Generate comprehensive API documentation"

**Bad:**
- ❌ "This command reviews PRs" (unnecessary "This command")
- ❌ "Review" (too vague)
- ❌ "A command that reviews pull requests for code quality, security issues, and best practices" (too long)

### allowed-tools

**Type:** String or Array of strings
**Required:** No
**Default:** Inherits from conversation permissions

**Purpose:** Restrict or specify which tools command can use

**Formats:**

**Single tool:**
```yaml
allowed-tools: Read
```

**Multiple tools (comma-separated):**
```yaml
allowed-tools: Read, Write, Edit
```

**Multiple tools (array):**
```yaml
allowed-tools:
  - Read
  - Write
  - Bash(git:*)
```

**Tool Patterns:**

**Specific tools:**
```yaml
allowed-tools: Read, Grep, Edit
```

**Bash with command filter:**
```yaml
allowed-tools: Bash(git:*)           # Only git commands
allowed-tools: Bash(npm:*)           # Only npm commands
allowed-tools: Bash(docker:*)        # Only docker commands
```

**All tools (not recommended):**
```yaml
allowed-tools: "*"
```

**When to use:**

1. **Security:** Restrict command to safe operations
   ```yaml
   allowed-tools: Read, Grep  # Read-only command
   ```

2. **Clarity:** Document required tools
   ```yaml
   allowed-tools: Bash(git:*), Read
   ```

3. **Bash execution:** Enable bash command output
   ```yaml
   allowed-tools: Bash(git status:*), Bash(git diff:*)
   ```

**Best practices:**
- Be as restrictive as possible
- Use command filters for Bash (e.g., `git:*` not `*`)
- Only specify when different from conversation permissions
- Document why specific tools are needed

### model

**Type:** String
**Required:** No
**Default:** Inherits from conversation
**Values:** `sonnet`, `opus`, `haiku`

**Purpose:** Specify which Claude model executes the command

**Examples:**
```yaml
model: haiku    # Fast, efficient for simple tasks
```
```yaml
model: sonnet   # Balanced performance (default)
```
```yaml
model: opus     # Maximum capability for complex tasks
```

**When to use:**

**Use `haiku` for:**
- Simple, formulaic commands
- Fast execution needed
- Low complexity tasks
- Frequent invocations

```yaml
---
description: Format code file
model: haiku
---
```

**Use `sonnet` for:**
- Standard commands (default)
- Balanced speed/quality
- Most common use cases

```yaml
---
description: Review code changes
model: sonnet
---
```

**Use `opus` for:**
- Complex analysis
- Architectural decisions
- Deep code understanding
- Critical tasks

```yaml
---
description: Analyze system architecture
model: opus
---
```

**Best practices:**
- Omit unless specific need
- Use `haiku` for speed when possible
- Reserve `opus` for genuinely complex tasks
- Test with different models to find right balance

### argument-hint

**Type:** String
**Required:** No
**Default:** None

**Purpose:** Document expected arguments for users and autocomplete

**Format:**
```yaml
argument-hint: [arg1] [arg2] [optional-arg]
```

**Examples:**

**Single argument:**
```yaml
argument-hint: [pr-number]
```

**Multiple required arguments:**
```yaml
argument-hint: [environment] [version]
```

**Optional arguments:**
```yaml
argument-hint: [file-path] [options]
```

**Descriptive names:**
```yaml
argument-hint: [source-branch] [target-branch] [commit-message]
```

**Best practices:**
- Use square brackets `[]` for each argument
- Use descriptive names (not `arg1`, `arg2`)
- Indicate optional vs required in description
- Match order to positional arguments in command
- Keep concise but clear

**Examples by pattern:**

**Simple command:**
```yaml
---
description: Fix issue by number
argument-hint: [issue-number]
---

Fix issue #$1...
```

**Multi-argument:**
```yaml
---
description: Deploy to environment
argument-hint: [app-name] [environment] [version]
---

Deploy $1 to $2 using version $3...
```

**With options:**
```yaml
---
description: Run tests with options
argument-hint: [test-pattern] [options]
---

Run tests matching $1 with options: $2
```

### disable-model-invocation

**Type:** Boolean
**Required:** No
**Default:** false

**Purpose:** Prevent SlashCommand tool from programmatically invoking command

**Examples:**
```yaml
disable-model-invocation: true
```

**When to use:**

1. **Manual-only commands:** Commands requiring user judgment
   ```yaml
   ---
   description: Approve deployment to production
   disable-model-invocation: true
   ---
   ```

2. **Destructive operations:** Commands with irreversible effects
   ```yaml
   ---
   description: Delete all test data
   disable-model-invocation: true
   ---
   ```

3. **Interactive workflows:** Commands needing user input
   ```yaml
   ---
   description: Walk through setup wizard
   disable-model-invocation: true
   ---
   ```

**Default behavior (false):**
- Command available to SlashCommand tool
- Claude can invoke programmatically
- Still available for manual invocation

**When true:**
- Command only invokable by user typing `/command`
- Not available to SlashCommand tool
- Safer for sensitive operations

**Best practices:**
- Use sparingly (limits Claude's autonomy)
- Document why in command comments
- Consider if command should exist if always manual

## Complete Examples

### Minimal Command

No frontmatter needed:

```markdown
Review this code for common issues and suggest improvements.
```

### Simple Command

Just description:

```markdown
---
description: Review code for issues
---

Review this code for common issues and suggest improvements.
```

### Standard Command

Description and tools:

```markdown
---
description: Review Git changes
allowed-tools: Bash(git:*), Read
---

Current changes: !`git diff --name-only`

Review each changed file for:
- Code quality
- Potential bugs
- Best practices
```

### Complex Command

All common fields:

```markdown
---
description: Deploy application to environment
argument-hint: [app-name] [environment] [version]
allowed-tools: Bash(kubectl:*), Bash(helm:*), Read
model: sonnet
---

Deploy $1 to $2 environment using version $3

Pre-deployment checks:
- Verify $2 configuration
- Check cluster status: !`kubectl cluster-info`
- Validate version $3 exists

Proceed with deployment following deployment runbook.
```

### Manual-Only Command

Restricted invocation:

```markdown
---
description: Approve production deployment
argument-hint: [deployment-id]
disable-model-invocation: true
allowed-tools: Bash(gh:*)
---

<!--
MANUAL APPROVAL REQUIRED
This command requires human judgment and cannot be automated.
-->

Review deployment $1 for production approval:

Deployment details: !`gh api /deployments/$1`

Verify:
- All tests passed
- Security scan clean
- Stakeholder approval
- Rollback plan ready

Type "APPROVED" to confirm deployment.
```

## Validation

### Common Errors

**Invalid YAML syntax:**
```yaml
---
description: Missing quote
allowed-tools: Read, Write
model: sonnet
---  # ❌ Missing closing quote above
```

**Fix:** Validate YAML syntax

**Incorrect tool specification:**
```yaml
allowed-tools: Bash  # ❌ Missing command filter
```

**Fix:** Use `Bash(git:*)` format

**Invalid model name:**
```yaml
model: gpt4  # ❌ Not a valid Claude model
```

**Fix:** Use `sonnet`, `opus`, or `haiku`

### Validation Checklist

Before committing command:
- [ ] YAML syntax valid (no errors)
- [ ] Description under 60 characters
- [ ] allowed-tools uses proper format
- [ ] model is valid value if specified
- [ ] argument-hint matches positional arguments
- [ ] disable-model-invocation used appropriately

## Best Practices Summary

1. **Start minimal:** Add frontmatter only when needed
2. **Document arguments:** Always use argument-hint with arguments
3. **Restrict tools:** Use most restrictive allowed-tools that works
4. **Choose right model:** Use haiku for speed, opus for complexity
5. **Manual-only sparingly:** Only use disable-model-invocation when necessary
6. **Clear descriptions:** Make commands discoverable in `/help`
7. **Test thoroughly:** Verify frontmatter works as expected

---

## Marketplace Considerations for Commands

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/marketplace-considerations.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-command-development-references-marketplace.md

# Marketplace Considerations for Commands

Guidelines for creating commands designed for distribution and marketplace success.

## Overview

Commands distributed through marketplaces need additional consideration beyond personal use commands. They must work across environments, handle diverse use cases, and provide excellent user experience for unknown users.

## Design for Distribution

### Universal Compatibility

**Cross-platform considerations:**

```markdown
---
description: Cross-platform command
allowed-tools: Bash(*)
---

# Platform-Aware Command

Detecting platform...

case "$(uname)" in
  Darwin*)  PLATFORM="macOS" ;;
  Linux*)   PLATFORM="Linux" ;;
  MINGW*|MSYS*|CYGWIN*) PLATFORM="Windows" ;;
  *)        PLATFORM="Unknown" ;;
esac

Platform: $PLATFORM

<!-- Adjust behavior based on platform -->
if [ "$PLATFORM" = "Windows" ]; then
  # Windows-specific handling
  PATH_SEP="\\"
  NULL_DEVICE="NUL"
else
  # Unix-like handling
  PATH_SEP="/"
  NULL_DEVICE="/dev/null"
fi

[Platform-appropriate implementation...]
```

**Avoid platform-specific commands:**

```markdown
<!-- BAD: macOS-specific -->
!`pbcopy < file.txt`

<!-- GOOD: Platform detection -->
if command -v pbcopy > /dev/null; then
  pbcopy < file.txt
elif command -v xclip > /dev/null; then
  xclip -selection clipboard < file.txt
elif command -v clip.exe > /dev/null; then
  cat file.txt | clip.exe
else
  echo "Clipboard not available on this platform"
fi
```

### Minimal Dependencies

**Check for required tools:**

```markdown
---
description: Dependency-aware command
allowed-tools: Bash(*)
---

# Check Dependencies

Required tools:
- git
- jq
- node

Checking availability...

MISSING_DEPS=""

for tool in git jq node; do
  if ! command -v $tool > /dev/null; then
    MISSING_DEPS="$MISSING_DEPS $tool"
  fi
done

if [ -n "$MISSING_DEPS" ]; then
  ❌ ERROR: Missing required dependencies:$MISSING_DEPS

  INSTALLATION:
  - git: https://git-scm.com/downloads
  - jq: https://stedolan.github.io/jq/download/
  - node: https://nodejs.org/

  Install missing tools and try again.

  Exit.
fi

✓ All dependencies available

[Continue with command...]
```

**Document optional dependencies:**

```markdown
<!--
DEPENDENCIES:
  Required:
  - git 2.0+: Version control
  - jq 1.6+: JSON processing

  Optional:
  - gh: GitHub CLI (for PR operations)
  - docker: Container operations (for containerized tests)

  Feature availability depends on installed tools.
-->
```

### Graceful Degradation

**Handle missing features:**

```markdown
---
description: Feature-aware command
---

# Feature Detection

Detecting available features...

FEATURES=""

if command -v gh > /dev/null; then
  FEATURES="$FEATURES github"
fi

if command -v docker > /dev/null; then
  FEATURES="$FEATURES docker"
fi

Available features: $FEATURES

if echo "$FEATURES" | grep -q "github"; then
  # Full functionality with GitHub integration
  echo "✓ GitHub integration available"
else
  # Reduced functionality without GitHub
  echo "⚠ Limited functionality: GitHub CLI not installed"
  echo "  Install 'gh' for full features"
fi

[Adapt behavior based on available features...]
```

## User Experience for Unknown Users

### Clear Onboarding

**First-run experience:**

```markdown
---
description: Command with onboarding
allowed-tools: Read, Write
---

# First Run Check

if [ ! -f ".claude/command-initialized" ]; then
  **Welcome to Command Name!**

  This appears to be your first time using this command.

  WHAT THIS COMMAND DOES:
  [Brief explanation of purpose and benefits]

  QUICK START:
  1. Basic usage: /command [arg]
  2. For help: /command help
  3. Examples: /command examples

  SETUP:
  No additional setup required. You're ready to go!

  ✓ Initialization complete

  [Create initialization marker]

  Ready to proceed with your request...
fi

[Normal command execution...]
```

**Progressive feature discovery:**

```markdown
---
description: Command with tips
---

# Command Execution

[Main functionality...]

---

💡 TIP: Did you know?

You can speed up this command with the --fast flag:
  /command --fast [args]

For more tips: /command tips
```

### Comprehensive Error Handling

**Anticipate user mistakes:**

```markdown
---
description: Forgiving command
---

# User Input Handling

Argument: "$1"

<!-- Check for common typos -->
if [ "$1" = "hlep" ] || [ "$1" = "hepl" ]; then
  Did you mean: help?

  Showing help instead...
  [Display help]

  Exit.
fi

<!-- Suggest similar commands if not found -->
if [ "$1" != "valid-option1" ] && [ "$1" != "valid-option2" ]; then
  ❌ Unknown option: $1

  Did you mean:
  - valid-option1 (most similar)
  - valid-option2

  For all options: /command help

  Exit.
fi

[Command continues...]
```

**Helpful diagnostics:**

```markdown
---
description: Diagnostic command
---

# Operation Failed

The operation could not complete.

**Diagnostic Information:**

Environment:
- Platform: $(uname)
- Shell: $SHELL
- Working directory: $(pwd)
- Command: /command $@

Checking common issues:
- Git repository: $(git rev-parse --git-dir 2>&1)
- Write permissions: $(test -w . && echo "OK" || echo "DENIED")
- Required files: $(test -f config.yml && echo "Found" || echo "Missing")

This information helps debug the issue.

For support, include the above diagnostics.
```

## Distribution Best Practices

### Namespace Awareness

**Avoid name collisions:**

```markdown
---
description: Namespaced command
---

<!--
COMMAND NAME: plugin-name-command

This command is namespaced with the plugin name to avoid
conflicts with commands from other plugins.

Alternative naming approaches:
- Use plugin prefix: /plugin-command
- Use category: /category-command
- Use verb-noun: /verb-noun

Chosen approach: plugin-name prefix
Reasoning: Clearest ownership, least likely to conflict
-->

# Plugin Name Command

[Implementation...]
```

**Document naming rationale:**

```markdown
<!--
NAMING DECISION:

Command name: /deploy-app

Alternatives considered:
- /deploy: Too generic, likely conflicts
- /app-deploy: Less intuitive ordering
- /my-plugin-deploy: Too verbose

Final choice balances:
- Discoverability (clear purpose)
- Brevity (easy to type)
- Uniqueness (unlikely conflicts)
-->
```

### Configurability

**User preferences:**

```markdown
---
description: Configurable command
allowed-tools: Read
---

# Load User Configuration

Default configuration:
- verbose: false
- color: true
- max_results: 10

Checking for user config: .claude/plugin-name.local.md

if [ -f ".claude/plugin-name.local.md" ]; then
  # Parse YAML frontmatter for settings
  VERBOSE=$(grep "^verbose:" .claude/plugin-name.local.md | cut -d: -f2 | tr -d ' ')
  COLOR=$(grep "^color:" .claude/plugin-name.local.md | cut -d: -f2 | tr -d ' ')
  MAX_RESULTS=$(grep "^max_results:" .claude/plugin-name.local.md | cut -d: -f2 | tr -d ' ')

  echo "✓ Using user configuration"
else
  echo "Using default configuration"
  echo "Create .claude/plugin-name.local.md to customize"
fi

[Use configuration in command...]
```

**Sensible defaults:**

```markdown
---
description: Command with smart defaults
---

# Smart Defaults

Configuration:
- Format: ${FORMAT:-json}  # Defaults to json
- Output: ${OUTPUT:-stdout}  # Defaults to stdout
- Verbose: ${VERBOSE:-false}  # Defaults to false

These defaults work for 80% of use cases.

Override with arguments:
  /command --format yaml --output file.txt --verbose

Or set in .claude/plugin-name.local.md:
\`\`\`yaml
---
format: yaml
output: custom.txt
verbose: true
---
\`\`\`
```

### Version Compatibility

**Version checking:**

```markdown
---
description: Version-aware command
---

<!--
COMMAND VERSION: 2.1.0

COMPATIBILITY:
- Requires plugin version: >= 2.0.0
- Breaking changes from v1.x documented in MIGRATION.md

VERSION HISTORY:
- v2.1.0: Added --new-feature flag
- v2.0.0: BREAKING: Changed argument order
- v1.0.0: Initial release
-->

# Version Check

Command version: 2.1.0
Plugin version: [detect from plugin.json]

if [  "$PLUGIN_VERSION" < "2.0.0" ]; then
  ❌ ERROR: Incompatible plugin version

  This command requires plugin version >= 2.0.0
  Current version: $PLUGIN_VERSION

  Update plugin:
    /plugin update plugin-name

  Exit.
fi

✓ Version compatible

[Command continues...]
```

**Deprecation warnings:**

```markdown
---
description: Command with deprecation warnings
---

# Deprecation Check

if [ "$1" = "--old-flag" ]; then
  ⚠️  DEPRECATION WARNING

  The --old-flag option is deprecated as of v2.0.0
  It will be removed in v3.0.0 (est. June 2025)

  Use instead: --new-flag

  Example:
    Old: /command --old-flag value
    New: /command --new-flag value

  See migration guide: /command migrate

  Continuing with deprecated behavior for now...
fi

[Handle both old and new flags during deprecation period...]
```

## Marketplace Presentation

### Command Discovery

**Descriptive naming:**

```markdown
---
description: Review pull request with security and quality checks
---

<!-- GOOD: Descriptive name and description -->
```

```markdown
---
description: Do the thing
---

<!-- BAD: Vague description -->
```

**Searchable keywords:**

```markdown
<!--
KEYWORDS: security, code-review, quality, validation, audit

These keywords help users discover this command when searching
for related functionality in the marketplace.
-->
```

### Showcase Examples

**Compelling demonstrations:**

```markdown
---
description: Advanced code analysis command
---

# Code Analysis Command

This command performs deep code analysis with actionable insights.

## Demo: Quick Security Audit

Try it now:
\`\`\`
/analyze-code src/ --security
\`\`\`

**What you'll get:**
- Security vulnerability detection
- Code quality metrics
- Performance bottleneck identification
- Actionable recommendations

**Sample output:**
\`\`\`
Security Analysis Results
=========================

🔴 Critical (2):
  - SQL injection risk in users.js:45
  - XSS vulnerability in display.js:23

🟡 Warnings (5):
  - Unvalidated input in api.js:67
  ...

Recommendations:
1. Fix critical issues immediately
2. Review warnings before next release
3. Run /analyze-code --fix for auto-fixes
\`\`\`

---

Ready to analyze your code...

[Command implementation...]
```

### User Reviews and Feedback

**Feedback mechanism:**

```markdown
---
description: Command with feedback
---

# Command Complete

[Command results...]

---

**How was your experience?**

This helps improve the command for everyone.

Rate this command:
- 👍 Helpful
- 👎 Not helpful
- 🐛 Found a bug
- 💡 Have a suggestion

Reply with an emoji or:
- /command feedback

Your feedback matters!
```

**Usage analytics preparation:**

```markdown
<!--
ANALYTICS NOTES:

Track for improvement:
- Most common arguments
- Failure rates
- Average execution time
- User satisfaction scores

Privacy-preserving:
- No personally identifiable information
- Aggregate statistics only
- User opt-out respected
-->
```

## Quality Standards

### Professional Polish

**Consistent branding:**

```markdown
---
description: Branded command
---

# ✨ Command Name

Part of the [Plugin Name] suite

[Command functionality...]

---

**Need Help?**
- Documentation: https://docs.example.com
- Support: support@example.com
- Community: https://community.example.com

Powered by Plugin Name v2.1.0
```

**Attention to detail:**

```markdown
<!-- Details that matter -->

✓ Use proper emoji/symbols consistently
✓ Align output columns neatly
✓ Format numbers with thousands separators
✓ Use color/formatting appropriately
✓ Provide progress indicators
✓ Show estimated time remaining
✓ Confirm successful operations
```

### Reliability

**Idempotency:**

```markdown
---
description: Idempotent command
---

# Safe Repeated Execution

Checking if operation already completed...

if [ -f ".claude/operation-completed.flag" ]; then
  ℹ️  Operation already completed

  Completed at: $(cat .claude/operation-completed.flag)

  To re-run:
  1. Remove flag: rm .claude/operation-completed.flag
  2. Run command again

  Otherwise, no action needed.

  Exit.
fi

Performing operation...

[Safe, repeatable operation...]

Marking complete...
echo "$(date)" > .claude/operation-completed.flag
```

**Atomic operations:**

```markdown
---
description: Atomic command
---

# Atomic Operation

This operation is atomic - either fully succeeds or fully fails.

Creating temporary workspace...
TEMP_DIR=$(mktemp -d)

Performing changes in isolated environment...
[Make changes in $TEMP_DIR]

if [ $? -eq 0 ]; then
  ✓ Changes validated

  Applying changes atomically...
  mv $TEMP_DIR/* ./target/

  ✓ Operation complete
else
  ❌ Changes failed validation

  Rolling back...
  rm -rf $TEMP_DIR

  No changes applied. Safe to retry.
fi
```

## Testing for Distribution

### Pre-Release Checklist

```markdown
<!--
PRE-RELEASE CHECKLIST:

Functionality:
- [ ] Works on macOS
- [ ] Works on Linux
- [ ] Works on Windows (WSL)
- [ ] All arguments tested
- [ ] Error cases handled
- [ ] Edge cases covered

User Experience:
- [ ] Clear description
- [ ] Helpful error messages
- [ ] Examples provided
- [ ] First-run experience good
- [ ] Documentation complete

Distribution:
- [ ] No hardcoded paths
- [ ] Dependencies documented
- [ ] Configuration options clear
- [ ] Version number set
- [ ] Changelog updated

Quality:
- [ ] No TODO comments
- [ ] No debug code
- [ ] Performance acceptable
- [ ] Security reviewed
- [ ] Privacy considered

Support:
- [ ] README complete
- [ ] Troubleshooting guide
- [ ] Support contact provided
- [ ] Feedback mechanism
- [ ] License specified
-->
```

### Beta Testing

**Beta release approach:**

```markdown
---
description: Beta command (v0.9.0)
---

# 🧪 Beta Command

**This is a beta release**

Features may change based on feedback.

BETA STATUS:
- Version: 0.9.0
- Stability: Experimental
- Support: Limited
- Feedback: Encouraged

Known limitations:
- Performance not optimized
- Some edge cases not handled
- Documentation incomplete

Help improve this command:
- Report issues: /command report-issue
- Suggest features: /command suggest
- Join beta testers: /command join-beta

---

[Command implementation...]

---

**Thank you for beta testing!**

Your feedback helps make this command better.
```

## Maintenance and Updates

### Update Strategy

**Versioned commands:**

```markdown
<!--
VERSION STRATEGY:

Major (X.0.0): Breaking changes
- Document all breaking changes
- Provide migration guide
- Support old version briefly

Minor (x.Y.0): New features
- Backward compatible
- Announce new features
- Update examples

Patch (x.y.Z): Bug fixes
- No user-facing changes
- Update changelog
- Security fixes prioritized

Release schedule:
- Patches: As needed
- Minors: Monthly
- Majors: Annually or as needed
-->
```

**Update notifications:**

```markdown
---
description: Update-aware command
---

# Check for Updates

Current version: 2.1.0
Latest version: [check if available]

if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then
  📢 UPDATE AVAILABLE

  New version: $LATEST_VERSION
  Current: $CURRENT_VERSION

  What's new:
  - Feature improvements
  - Bug fixes
  - Performance enhancements

  Update with:
    /plugin update plugin-name

  Release notes: https://releases.example.com/v$LATEST_VERSION
fi

[Command continues...]
```

## Best Practices Summary

### Distribution Design

1. **Universal**: Works across platforms and environments
2. **Self-contained**: Minimal dependencies, clear requirements
3. **Graceful**: Degrades gracefully when features unavailable
4. **Forgiving**: Anticipates and handles user mistakes
5. **Helpful**: Clear errors, good defaults, excellent docs

### Marketplace Success

1. **Discoverable**: Clear name, good description, searchable keywords
2. **Professional**: Polished presentation, consistent branding
3. **Reliable**: Tested thoroughly, handles edge cases
4. **Maintainable**: Versioned, updated regularly, supported
5. **User-focused**: Great UX, responsive to feedback

### Quality Standards

1. **Complete**: Fully documented, all features working
2. **Tested**: Works in real environments, edge cases handled
3. **Secure**: No vulnerabilities, safe operations
4. **Performant**: Reasonable speed, resource-efficient
5. **Ethical**: Privacy-respecting, user consent

With these considerations, commands become marketplace-ready and delight users across diverse environments and use cases.

---

## Plugin-Specific Command Features Reference

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/command-development/references/plugin-features-reference.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-command-development-references-plugin-features.md

# Plugin-Specific Command Features Reference

This reference covers features and patterns specific to commands bundled in Claude Code plugins.

## Table of Contents

- [Plugin Command Discovery](#plugin-command-discovery)
- [CLAUDE_PLUGIN_ROOT Environment Variable](#claude_plugin_root-environment-variable)
- [Plugin Command Patterns](#plugin-command-patterns)
- [Integration with Plugin Components](#integration-with-plugin-components)
- [Validation Patterns](#validation-patterns)

## Plugin Command Discovery

### Auto-Discovery

Claude Code automatically discovers commands in plugins using the following locations:

```
plugin-name/
├── commands/              # Auto-discovered commands
│   ├── foo.md            # /foo (plugin:plugin-name)
│   └── bar.md            # /bar (plugin:plugin-name)
└── plugin.json           # Plugin manifest
```

**Key points:**
- Commands are discovered at plugin load time
- No manual registration required
- Commands appear in `/help` with "(plugin:plugin-name)" label
- Subdirectories create namespaces

### Namespaced Plugin Commands

Organize commands in subdirectories for logical grouping:

```
plugin-name/
└── commands/
    ├── review/
    │   ├── security.md    # /security (plugin:plugin-name:review)
    │   └── style.md       # /style (plugin:plugin-name:review)
    └── deploy/
        ├── staging.md     # /staging (plugin:plugin-name:deploy)
        └── prod.md        # /prod (plugin:plugin-name:deploy)
```

**Namespace behavior:**
- Subdirectory name becomes namespace
- Shown as "(plugin:plugin-name:namespace)" in `/help`
- Helps organize related commands
- Use when plugin has 5+ commands

### Command Naming Conventions

**Plugin command names should:**
1. Be descriptive and action-oriented
2. Avoid conflicts with common command names
3. Use hyphens for multi-word names
4. Consider prefixing with plugin name for uniqueness

**Examples:**
```
Good:
- /mylyn-sync          (plugin-specific prefix)
- /analyze-performance (descriptive action)
- /docker-compose-up   (clear purpose)

Avoid:
- /test               (conflicts with common name)
- /run                (too generic)
- /do-stuff           (not descriptive)
```

## CLAUDE_PLUGIN_ROOT Environment Variable

### Purpose

`${CLAUDE_PLUGIN_ROOT}` is a special environment variable available in plugin commands that resolves to the absolute path of the plugin directory.

**Why it matters:**
- Enables portable paths within plugin
- Allows referencing plugin files and scripts
- Works across different installations
- Essential for multi-file plugin operations

### Basic Usage

Reference files within your plugin:

```markdown
---
description: Analyze using plugin script
allowed-tools: Bash(node:*), Read
---

Run analysis: !`node ${CLAUDE_PLUGIN_ROOT}/scripts/analyze.js`

Read template: @${CLAUDE_PLUGIN_ROOT}/templates/report.md
```

**Expands to:**
```
Run analysis: !`node /path/to/plugins/plugin-name/scripts/analyze.js`

Read template: @/path/to/plugins/plugin-name/templates/report.md
```

### Common Patterns

#### 1. Executing Plugin Scripts

```markdown
---
description: Run custom linter from plugin
allowed-tools: Bash(node:*)
---

Lint results: !`node ${CLAUDE_PLUGIN_ROOT}/bin/lint.js $1`

Review the linting output and suggest fixes.
```

#### 2. Loading Configuration Files

```markdown
---
description: Deploy using plugin configuration
allowed-tools: Read, Bash(*)
---

Configuration: @${CLAUDE_PLUGIN_ROOT}/config/deploy-config.json

Deploy application using the configuration above for $1 environment.
```

#### 3. Accessing Plugin Resources

```markdown
---
description: Generate report from template
---

Use this template: @${CLAUDE_PLUGIN_ROOT}/templates/api-report.md

Generate a report for @$1 following the template format.
```

#### 4. Multi-Step Plugin Workflows

```markdown
---
description: Complete plugin workflow
allowed-tools: Bash(*), Read
---

Step 1 - Prepare: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/prepare.sh $1`
Step 2 - Config: @${CLAUDE_PLUGIN_ROOT}/config/$1.json
Step 3 - Execute: !`${CLAUDE_PLUGIN_ROOT}/bin/execute $1`

Review results and report status.
```

### Best Practices

1. **Always use for plugin-internal paths:**
   ```markdown
   # Good
   @${CLAUDE_PLUGIN_ROOT}/templates/foo.md

   # Bad
   @./templates/foo.md  # Relative to current directory, not plugin
   ```

2. **Validate file existence:**
   ```markdown
   ---
   description: Use plugin config if exists
   allowed-tools: Bash(test:*), Read
   ---

   !`test -f ${CLAUDE_PLUGIN_ROOT}/config.json && echo "exists" || echo "missing"`

   If config exists, load it: @${CLAUDE_PLUGIN_ROOT}/config.json
   Otherwise, use defaults...
   ```

3. **Document plugin file structure:**
   ```markdown
   <!--
   Plugin structure:
   ${CLAUDE_PLUGIN_ROOT}/
   ├── scripts/analyze.js  (analysis script)
   ├── templates/          (report templates)
   └── config/             (configuration files)
   -->
   ```

4. **Combine with arguments:**
   ```markdown
   Run: !`${CLAUDE_PLUGIN_ROOT}/bin/process.sh $1 $2`
   ```

### Troubleshooting

**Variable not expanding:**
- Ensure command is loaded from plugin
- Check bash execution is allowed
- Verify syntax is exact: `${CLAUDE_PLUGIN_ROOT}`

**File not found errors:**
- Verify file exists in plugin directory
- Check file path is correct relative to plugin root
- Ensure file permissions allow reading/execution

**Path with spaces:**
- Bash commands automatically handle spaces
- File references work with spaces in paths
- No special quoting needed

## Plugin Command Patterns

### Pattern 1: Configuration-Based Commands

Commands that load plugin-specific configuration:

```markdown
---
description: Deploy using plugin settings
allowed-tools: Read, Bash(*)
---

Load configuration: @${CLAUDE_PLUGIN_ROOT}/deploy-config.json

Deploy to $1 environment using:
1. Configuration settings above
2. Current git branch: !`git branch --show-current`
3. Application version: !`cat package.json | grep version`

Execute deployment and monitor progress.
```

**When to use:** Commands that need consistent settings across invocations

### Pattern 2: Template-Based Generation

Commands that use plugin templates:

```markdown
---
description: Generate documentation from template
argument-hint: [component-name]
---

Template: @${CLAUDE_PLUGIN_ROOT}/templates/component-docs.md

Generate documentation for $1 component following the template structure.
Include:
- Component purpose and usage
- API reference
- Examples
- Testing guidelines
```

**When to use:** Standardized output generation

### Pattern 3: Multi-Script Workflow

Commands that orchestrate multiple plugin scripts:

```markdown
---
description: Complete build and test workflow
allowed-tools: Bash(*)
---

Build: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/build.sh`
Validate: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh`
Test: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/test.sh`

Review all outputs and report:
1. Build status
2. Validation results
3. Test results
4. Recommended next steps
```

**When to use:** Complex plugin workflows with multiple steps

### Pattern 4: Environment-Aware Commands

Commands that adapt to environment:

```markdown
---
description: Deploy based on environment
argument-hint: [dev|staging|prod]
---

Environment config: @${CLAUDE_PLUGIN_ROOT}/config/$1.json

Environment check: !`echo "Deploying to: $1"`

Deploy application using $1 environment configuration.
Verify deployment and run smoke tests.
```

**When to use:** Commands that behave differently per environment

### Pattern 5: Plugin Data Management

Commands that manage plugin-specific data:

```markdown
---
description: Save analysis results to plugin cache
allowed-tools: Bash(*), Read, Write
---

Cache directory: ${CLAUDE_PLUGIN_ROOT}/cache/

Analyze @$1 and save results to cache:
!`mkdir -p ${CLAUDE_PLUGIN_ROOT}/cache && date > ${CLAUDE_PLUGIN_ROOT}/cache/last-run.txt`

Store analysis for future reference and comparison.
```

**When to use:** Commands that need persistent data storage

## Integration with Plugin Components

### Invoking Plugin Agents

Commands can trigger plugin agents using the Task tool:

```markdown
---
description: Deep analysis using plugin agent
argument-hint: [file-path]
---

Initiate deep code analysis of @$1 using the code-analyzer agent.

The agent will:
1. Analyze code structure
2. Identify patterns
3. Suggest improvements
4. Generate detailed report

Note: This uses the Task tool to launch the plugin's code-analyzer agent.
```

**Key points:**
- Agent must be defined in plugin's `agents/` directory
- Claude will automatically use Task tool to launch agent
- Agent has access to same plugin resources

### Invoking Plugin Skills

Commands can reference plugin skills for specialized knowledge:

```markdown
---
description: API documentation with best practices
argument-hint: [api-file]
---

Document the API in @$1 following our API documentation standards.

Use the api-docs-standards skill to ensure documentation includes:
- Endpoint descriptions
- Parameter specifications
- Response formats
- Error codes
- Usage examples

Note: This leverages the plugin's api-docs-standards skill for consistency.
```

**Key points:**
- Skill must be defined in plugin's `skills/` directory
- Mention skill by name to hint Claude should invoke it
- Skills provide specialized domain knowledge

### Coordinating with Plugin Hooks

Commands can be designed to work with plugin hooks:

```markdown
---
description: Commit with pre-commit validation
allowed-tools: Bash(git:*)
---

Stage changes: !\`git add $1\`

Commit changes: !\`git commit -m "$2"\`

Note: This commit will trigger the plugin's pre-commit hook for validation.
Review hook output for any issues.
```

**Key points:**
- Hooks execute automatically on events
- Commands can prepare state for hooks
- Document hook interaction in command

### Multi-Component Plugin Commands

Commands that coordinate multiple plugin components:

```markdown
---
description: Comprehensive code review workflow
argument-hint: [file-path]
---

File to review: @$1

Execute comprehensive review:

1. **Static Analysis** (via plugin scripts)
   !`node ${CLAUDE_PLUGIN_ROOT}/scripts/lint.js $1`

2. **Deep Review** (via plugin agent)
   Launch the code-reviewer agent for detailed analysis.

3. **Best Practices** (via plugin skill)
   Use the code-standards skill to ensure compliance.

4. **Documentation** (via plugin template)
   Template: @${CLAUDE_PLUGIN_ROOT}/templates/review-report.md

Generate final report combining all outputs.
```

**When to use:** Complex workflows leveraging multiple plugin capabilities

## Validation Patterns

### Input Validation

Commands should validate inputs before processing:

```markdown
---
description: Deploy to environment with validation
argument-hint: [environment]
---

Validate environment: !`echo "$1" | grep -E "^(dev|staging|prod)$" || echo "INVALID"`

$IF($1 in [dev, staging, prod],
  Deploy to $1 environment using validated configuration,
  ERROR: Invalid environment '$1'. Must be one of: dev, staging, prod
)
```

**Validation approaches:**
1. Bash validation using grep/test
2. Inline validation in prompt
3. Script-based validation

### File Existence Checks

Verify required files exist:

```markdown
---
description: Process configuration file
argument-hint: [config-file]
---

Check file: !`test -f $1 && echo "EXISTS" || echo "MISSING"`

Process configuration if file exists: @$1

If file doesn't exist, explain:
- Expected location
- Required format
- How to create it
```

### Required Arguments

Validate required arguments provided:

```markdown
---
description: Create deployment with version
argument-hint: [environment] [version]
---

Validate inputs: !`test -n "$1" -a -n "$2" && echo "OK" || echo "MISSING"`

$IF($1 AND $2,
  Deploy version $2 to $1 environment,
  ERROR: Both environment and version required. Usage: /deploy [env] [version]
)
```

### Plugin Resource Validation

Verify plugin resources available:

```markdown
---
description: Run analysis with plugin tools
allowed-tools: Bash(test:*)
---

Validate plugin setup:
- Config exists: !`test -f ${CLAUDE_PLUGIN_ROOT}/config.json && echo "✓" || echo "✗"`
- Scripts exist: !`test -d ${CLAUDE_PLUGIN_ROOT}/scripts && echo "✓" || echo "✗"`
- Tools available: !`test -x ${CLAUDE_PLUGIN_ROOT}/bin/analyze && echo "✓" || echo "✗"`

If all checks pass, proceed with analysis.
Otherwise, report missing components and installation steps.
```

### Output Validation

Validate command execution results:

```markdown
---
description: Build and validate output
allowed-tools: Bash(*)
---

Build: !`bash ${CLAUDE_PLUGIN_ROOT}/scripts/build.sh`

Validate output:
- Exit code: !`echo $?`
- Output exists: !`test -d dist && echo "✓" || echo "✗"`
- File count: !`find dist -type f | wc -l`

Report build status and any validation failures.
```

### Graceful Error Handling

Handle errors gracefully with helpful messages:

```markdown
---
description: Process file with error handling
argument-hint: [file-path]
---

Try processing: !`node ${CLAUDE_PLUGIN_ROOT}/scripts/process.js $1 2>&1 || echo "ERROR: $?"`

If processing succeeded:
- Report results
- Suggest next steps

If processing failed:
- Explain likely causes
- Provide troubleshooting steps
- Suggest alternative approaches
```

## Best Practices Summary

### Plugin Commands Should:

1. **Use ${CLAUDE_PLUGIN_ROOT} for all plugin-internal paths**
   - Scripts, templates, configuration, resources

2. **Validate inputs early**
   - Check required arguments
   - Verify file existence
   - Validate argument formats

3. **Document plugin structure**
   - Explain required files
   - Document script purposes
   - Clarify dependencies

4. **Integrate with plugin components**
   - Reference agents for complex tasks
   - Use skills for specialized knowledge
   - Coordinate with hooks when relevant

5. **Provide helpful error messages**
   - Explain what went wrong
   - Suggest how to fix
   - Offer alternatives

6. **Handle edge cases**
   - Missing files
   - Invalid arguments
   - Failed script execution
   - Missing dependencies

7. **Keep commands focused**
   - One clear purpose per command
   - Delegate complex logic to scripts
   - Use agents for multi-step workflows

8. **Test across installations**
   - Verify paths work everywhere
   - Test with different arguments
   - Validate error cases

---

For general command development, see main SKILL.md.
For command examples, see examples/ directory.

---

## Create Plugin Settings

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-settings/examples/create-settings-command.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-settings-examples-create-settings-command.md

# Create Plugin Settings

This command helps users create a `.claude/my-plugin.local.md` settings file.

## Steps

### Step 1: Ask User for Preferences

Use AskUserQuestion to gather configuration:

```json
{
  "questions": [
    {
      "question": "Enable plugin for this project?",
      "header": "Enable Plugin",
      "multiSelect": false,
      "options": [
        {
          "label": "Yes",
          "description": "Plugin will be active"
        },
        {
          "label": "No",
          "description": "Plugin will be disabled"
        }
      ]
    },
    {
      "question": "Validation mode?",
      "header": "Mode",
      "multiSelect": false,
      "options": [
        {
          "label": "Strict",
          "description": "Maximum validation and security checks"
        },
        {
          "label": "Standard",
          "description": "Balanced validation (recommended)"
        },
        {
          "label": "Lenient",
          "description": "Minimal validation only"
        }
      ]
    }
  ]
}
```

### Step 2: Parse Answers

Extract answers from AskUserQuestion result:

- answers["0"]: enabled (Yes/No)
- answers["1"]: mode (Strict/Standard/Lenient)

### Step 3: Create Settings File

Use Write tool to create `.claude/my-plugin.local.md`:

```markdown
---
enabled: <true if Yes, false if No>
validation_mode: <strict, standard, or lenient>
max_file_size: 1000000
notify_on_errors: true
---

# Plugin Configuration

Your plugin is configured with <mode> validation mode.

To modify settings, edit this file and restart Claude Code.
```

### Step 4: Inform User

Tell the user:
- Settings file created at `.claude/my-plugin.local.md`
- Current configuration summary
- How to edit manually if needed
- Reminder: Restart Claude Code for changes to take effect
- Settings file is gitignored (won't be committed)

## Implementation Notes

Always validate user input before writing:
- Check mode is valid
- Validate numeric fields are numbers
- Ensure paths don't have traversal attempts
- Sanitize any free-text fields

---

## Real-World Plugin Settings Examples

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-settings/references/real-world-examples.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-settings-references-real-world-examples.md

# Real-World Plugin Settings Examples

Detailed analysis of how production plugins use the `.claude/plugin-name.local.md` pattern.

## multi-agent-swarm Plugin

### Settings File Structure

**.claude/multi-agent-swarm.local.md:**

```markdown
---
agent_name: auth-implementation
task_number: 3.5
pr_number: 1234
coordinator_session: team-leader
enabled: true
dependencies: ["Task 3.4"]
additional_instructions: "Use JWT tokens, not sessions"
---

# Task: Implement Authentication

Build JWT-based authentication for the REST API.

## Requirements
- JWT token generation and validation
- Refresh token flow
- Secure password hashing

## Success Criteria
- Auth endpoints implemented
- Tests passing (100% coverage)
- PR created and CI green
- Documentation updated

## Coordination
Depends on Task 3.4 (user model).
Report status to 'team-leader' session.
```

### How It's Used

**File:** `hooks/agent-stop-notification.sh`

**Purpose:** Send notifications to coordinator when agent becomes idle

**Implementation:**

```bash
#!/bin/bash
set -euo pipefail

SWARM_STATE_FILE=".claude/multi-agent-swarm.local.md"

# Quick exit if no swarm active
if [[ ! -f "$SWARM_STATE_FILE" ]]; then
  exit 0
fi

# Parse frontmatter
FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$SWARM_STATE_FILE")

# Extract configuration
COORDINATOR_SESSION=$(echo "$FRONTMATTER" | grep '^coordinator_session:' | sed 's/coordinator_session: *//' | sed 's/^"\(.*\)"$/\1/')
AGENT_NAME=$(echo "$FRONTMATTER" | grep '^agent_name:' | sed 's/agent_name: *//' | sed 's/^"\(.*\)"$/\1/')
TASK_NUMBER=$(echo "$FRONTMATTER" | grep '^task_number:' | sed 's/task_number: *//' | sed 's/^"\(.*\)"$/\1/')
PR_NUMBER=$(echo "$FRONTMATTER" | grep '^pr_number:' | sed 's/pr_number: *//' | sed 's/^"\(.*\)"$/\1/')
ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//')

# Check if enabled
if [[ "$ENABLED" != "true" ]]; then
  exit 0
fi

# Send notification to coordinator
NOTIFICATION="🤖 Agent ${AGENT_NAME} (Task ${TASK_NUMBER}, PR #${PR_NUMBER}) is idle."

if tmux has-session -t "$COORDINATOR_SESSION" 2>/dev/null; then
  tmux send-keys -t "$COORDINATOR_SESSION" "$NOTIFICATION" Enter
  sleep 0.5
  tmux send-keys -t "$COORDINATOR_SESSION" Enter
fi

exit 0
```

**Key patterns:**
1. **Quick exit** (line 7-9): Returns immediately if file doesn't exist
2. **Field extraction** (lines 11-17): Parses each frontmatter field
3. **Enabled check** (lines 19-21): Respects enabled flag
4. **Action based on settings** (lines 23-29): Uses coordinator_session to send notification

### Creation

**File:** `commands/launch-swarm.md`

Settings files are created during swarm launch with:

```bash
cat > "$WORKTREE_PATH/.claude/multi-agent-swarm.local.md" <<EOF
---
agent_name: $AGENT_NAME
task_number: $TASK_ID
pr_number: TBD
coordinator_session: $COORDINATOR_SESSION
enabled: true
dependencies: [$DEPENDENCIES]
additional_instructions: "$EXTRA_INSTRUCTIONS"
---

# Task: $TASK_DESCRIPTION

$TASK_DETAILS
EOF
```

### Updates

PR number updated after PR creation:

```bash
# Update pr_number field
sed "s/^pr_number: .*/pr_number: $PR_NUM/" \
  ".claude/multi-agent-swarm.local.md" > temp.md
mv temp.md ".claude/multi-agent-swarm.local.md"
```

## ralph-wiggum Plugin

### Settings File Structure

**.claude/ralph-loop.local.md:**

```markdown
---
iteration: 1
max_iterations: 10
completion_promise: "All tests passing and build successful"
started_at: "2025-01-15T14:30:00Z"
---

Fix all the linting errors in the project.
Make sure tests pass after each fix.
Document any changes needed in CLAUDE.md.
```

### How It's Used

**File:** `hooks/stop-hook.sh`

**Purpose:** Prevent session exit and loop Claude's output back as input

**Implementation:**

```bash
#!/bin/bash
set -euo pipefail

RALPH_STATE_FILE=".claude/ralph-loop.local.md"

# Quick exit if no active loop
if [[ ! -f "$RALPH_STATE_FILE" ]]; then
  exit 0
fi

# Parse frontmatter
FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE")

# Extract configuration
ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//')
COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/')

# Check max iterations
if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then
  echo "🛑 Ralph loop: Max iterations ($MAX_ITERATIONS) reached."
  rm "$RALPH_STATE_FILE"
  exit 0
fi

# Get transcript and check for completion promise
TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path')
LAST_OUTPUT=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1 | jq -r '.message.content | map(select(.type == "text")) | map(.text) | join("\n")')

# Check for completion
if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then
  PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g')

  if [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then
    echo "✅ Ralph loop: Detected completion"
    rm "$RALPH_STATE_FILE"
    exit 0
  fi
fi

# Continue loop - increment iteration
NEXT_ITERATION=$((ITERATION + 1))

# Extract prompt from markdown body
PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE")

# Update iteration counter
TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$"
sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE"
mv "$TEMP_FILE" "$RALPH_STATE_FILE"

# Block exit and feed prompt back
jq -n \
  --arg prompt "$PROMPT_TEXT" \
  --arg msg "🔄 Ralph iteration $NEXT_ITERATION" \
  '{
    "decision": "block",
    "reason": $prompt,
    "systemMessage": $msg
  }'

exit 0
```

**Key patterns:**
1. **Quick exit** (line 7-9): Skip if not active
2. **Iteration tracking** (lines 11-20): Count and enforce max iterations
3. **Promise detection** (lines 25-33): Check for completion signal in output
4. **Prompt extraction** (line 38): Read markdown body as next prompt
5. **State update** (lines 40-43): Increment iteration atomically
6. **Loop continuation** (lines 45-53): Block exit and feed prompt back

### Creation

**File:** `scripts/setup-ralph-loop.sh`

```bash
#!/bin/bash
PROMPT="$1"
MAX_ITERATIONS="${2:-0}"
COMPLETION_PROMISE="${3:-}"

# Create state file
cat > ".claude/ralph-loop.local.md" <<EOF
---
iteration: 1
max_iterations: $MAX_ITERATIONS
completion_promise: "$COMPLETION_PROMISE"
started_at: "$(date -Iseconds)"
---

$PROMPT
EOF

echo "Ralph loop initialized: .claude/ralph-loop.local.md"
```

## Pattern Comparison

| Feature | multi-agent-swarm | ralph-wiggum |
|---------|-------------------|--------------|
| **File** | `.claude/multi-agent-swarm.local.md` | `.claude/ralph-loop.local.md` |
| **Purpose** | Agent coordination state | Loop iteration state |
| **Frontmatter** | Agent metadata | Loop configuration |
| **Body** | Task assignment | Prompt to loop |
| **Updates** | PR number, status | Iteration counter |
| **Deletion** | Manual or on completion | On loop exit |
| **Hook** | Stop (notifications) | Stop (loop control) |

## Best Practices from Real Plugins

### 1. Quick Exit Pattern

Both plugins check file existence first:

```bash
if [[ ! -f "$STATE_FILE" ]]; then
  exit 0  # Not active
fi
```

**Why:** Avoids errors when plugin isn't configured and performs fast.

### 2. Enabled Flag

Both use an `enabled` field for explicit control:

```yaml
enabled: true
```

**Why:** Allows temporary deactivation without deleting file.

### 3. Atomic Updates

Both use temp file + atomic move:

```bash
TEMP_FILE="${FILE}.tmp.$$"
sed "s/^field: .*/field: $NEW_VALUE/" "$FILE" > "$TEMP_FILE"
mv "$TEMP_FILE" "$FILE"
```

**Why:** Prevents corruption if process is interrupted.

### 4. Quote Handling

Both strip surrounding quotes from YAML values:

```bash
sed 's/^"\(.*\)"$/\1/'
```

**Why:** YAML allows both `field: value` and `field: "value"`.

### 5. Error Handling

Both handle missing/corrupt files gracefully:

```bash
if [[ ! -f "$FILE" ]]; then
  exit 0  # No error, just not configured
fi

if [[ -z "$CRITICAL_FIELD" ]]; then
  echo "Settings file corrupt" >&2
  rm "$FILE"  # Clean up
  exit 0
fi
```

**Why:** Fails gracefully instead of crashing.

## Anti-Patterns to Avoid

### ❌ Hardcoded Paths

```bash
# BAD
FILE="/Users/alice/.claude/my-plugin.local.md"

# GOOD
FILE=".claude/my-plugin.local.md"
```

### ❌ Unquoted Variables

```bash
# BAD
echo $VALUE

# GOOD
echo "$VALUE"
```

### ❌ Non-Atomic Updates

```bash
# BAD: Can corrupt file if interrupted
sed -i "s/field: .*/field: $VALUE/" "$FILE"

# GOOD: Atomic
TEMP_FILE="${FILE}.tmp.$$"
sed "s/field: .*/field: $VALUE/" "$FILE" > "$TEMP_FILE"
mv "$TEMP_FILE" "$FILE"
```

### ❌ No Default Values

```bash
# BAD: Fails if field missing
if [[ $MAX -gt 100 ]]; then
  # MAX might be empty!
fi

# GOOD: Provide default
MAX=${MAX:-10}
```

### ❌ Ignoring Edge Cases

```bash
# BAD: Assumes exactly 2 --- markers
sed -n '/^---$/,/^---$/{ /^---$/d; p; }'

# GOOD: Handles --- in body
awk '/^---$/{i++; next} i>=2'  # For body
```

## Conclusion

The `.claude/plugin-name.local.md` pattern provides:
- Simple, human-readable configuration
- Version-control friendly (gitignored)
- Per-project settings
- Easy parsing with standard bash tools
- Supports both structured config (YAML) and freeform content (markdown)

Use this pattern for any plugin that needs user-configurable behavior or state persistence.

---

## Advanced Plugin Example

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-structure/examples/advanced-plugin.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-structure-examples-advanced-plugin.md

# Advanced Plugin Example

A complex, enterprise-grade plugin with MCP integration and advanced organization.

## Directory Structure

```
enterprise-devops/
├── .claude-plugin/
│   └── plugin.json
├── commands/
│   ├── ci/
│   │   ├── build.md
│   │   ├── test.md
│   │   └── deploy.md
│   ├── monitoring/
│   │   ├── status.md
│   │   └── logs.md
│   └── admin/
│       ├── configure.md
│       └── manage.md
├── agents/
│   ├── orchestration/
│   │   ├── deployment-orchestrator.md
│   │   └── rollback-manager.md
│   └── specialized/
│       ├── kubernetes-expert.md
│       ├── terraform-expert.md
│       └── security-auditor.md
├── skills/
│   ├── kubernetes-ops/
│   │   ├── SKILL.md
│   │   ├── references/
│   │   │   ├── deployment-patterns.md
│   │   │   ├── troubleshooting.md
│   │   │   └── security.md
│   │   ├── examples/
│   │   │   ├── basic-deployment.yaml
│   │   │   ├── stateful-set.yaml
│   │   │   └── ingress-config.yaml
│   │   └── scripts/
│   │       ├── validate-manifest.sh
│   │       └── health-check.sh
│   ├── terraform-iac/
│   │   ├── SKILL.md
│   │   ├── references/
│   │   │   └── best-practices.md
│   │   └── examples/
│   │       └── module-template/
│   └── ci-cd-pipelines/
│       ├── SKILL.md
│       └── references/
│           └── pipeline-patterns.md
├── hooks/
│   ├── hooks.json
│   └── scripts/
│       ├── security/
│       │   ├── scan-secrets.sh
│       │   ├── validate-permissions.sh
│       │   └── audit-changes.sh
│       ├── quality/
│       │   ├── check-config.sh
│       │   └── verify-tests.sh
│       └── workflow/
│           ├── notify-team.sh
│           └── update-status.sh
├── .mcp.json
├── servers/
│   ├── kubernetes-mcp/
│   │   ├── index.js
│   │   ├── package.json
│   │   └── lib/
│   ├── terraform-mcp/
│   │   ├── main.py
│   │   └── requirements.txt
│   └── github-actions-mcp/
│       ├── server.js
│       └── package.json
├── lib/
│   ├── core/
│   │   ├── logger.js
│   │   ├── config.js
│   │   └── auth.js
│   ├── integrations/
│   │   ├── slack.js
│   │   ├── pagerduty.js
│   │   └── datadog.js
│   └── utils/
│       ├── retry.js
│       └── validation.js
└── config/
    ├── environments/
    │   ├── production.json
    │   ├── staging.json
    │   └── development.json
    └── templates/
        ├── deployment.yaml
        └── service.yaml
```

## File Contents

### .claude-plugin/plugin.json

```json
{
  "name": "enterprise-devops",
  "version": "2.3.1",
  "description": "Comprehensive DevOps automation for enterprise CI/CD pipelines, infrastructure management, and monitoring",
  "author": {
    "name": "DevOps Platform Team",
    "email": "devops-platform@company.com",
    "url": "https://company.com/teams/devops"
  },
  "homepage": "https://docs.company.com/plugins/devops",
  "repository": {
    "type": "git",
    "url": "https://github.com/company/devops-plugin.git"
  },
  "license": "Apache-2.0",
  "keywords": [
    "devops",
    "ci-cd",
    "kubernetes",
    "terraform",
    "automation",
    "infrastructure",
    "deployment",
    "monitoring"
  ],
  "commands": [
    "./commands/ci",
    "./commands/monitoring",
    "./commands/admin"
  ],
  "agents": [
    "./agents/orchestration",
    "./agents/specialized"
  ],
  "hooks": "./hooks/hooks.json",
  "mcpServers": "./.mcp.json"
}
```

### .mcp.json

```json
{
  "mcpServers": {
    "kubernetes": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/servers/kubernetes-mcp/index.js"],
      "env": {
        "KUBECONFIG": "${KUBECONFIG}",
        "K8S_NAMESPACE": "${K8S_NAMESPACE:-default}"
      }
    },
    "terraform": {
      "command": "python",
      "args": ["${CLAUDE_PLUGIN_ROOT}/servers/terraform-mcp/main.py"],
      "env": {
        "TF_STATE_BUCKET": "${TF_STATE_BUCKET}",
        "AWS_REGION": "${AWS_REGION}"
      }
    },
    "github-actions": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/servers/github-actions-mcp/server.js"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}",
        "GITHUB_ORG": "${GITHUB_ORG}"
      }
    }
  }
}
```

### commands/ci/build.md

```markdown
---
name: build
description: Trigger and monitor CI build pipeline
---

# Build Command

Trigger CI/CD build pipeline and monitor progress in real-time.

## Process

1. **Validation**: Check prerequisites
   - Verify branch status
   - Check for uncommitted changes
   - Validate configuration files

2. **Trigger**: Start build via MCP server
   \`\`\`javascript
   // Uses github-actions MCP server
   const build = await tools.github_actions_trigger_workflow({
     workflow: 'build.yml',
     ref: currentBranch
   })
   \`\`\`

3. **Monitor**: Track build progress
   - Display real-time logs
   - Show test results as they complete
   - Alert on failures

4. **Report**: Summarize results
   - Build status
   - Test coverage
   - Performance metrics
   - Deploy readiness

## Integration

After successful build:
- Offer to deploy to staging
- Suggest performance optimizations
- Generate deployment checklist
```

### agents/orchestration/deployment-orchestrator.md

```markdown
---
description: Orchestrates complex multi-environment deployments with rollback capabilities and health monitoring
capabilities:
  - Plan and execute multi-stage deployments
  - Coordinate service dependencies
  - Monitor deployment health
  - Execute automated rollbacks
  - Manage deployment approvals
---

# Deployment Orchestrator Agent

Specialized agent for orchestrating complex deployments across multiple environments.

## Expertise

- **Deployment strategies**: Blue-green, canary, rolling updates
- **Dependency management**: Service startup ordering, dependency injection
- **Health monitoring**: Service health checks, metric validation
- **Rollback automation**: Automatic rollback on failure detection
- **Approval workflows**: Multi-stage approval processes

## Orchestration Process

1. **Planning Phase**
   - Analyze deployment requirements
   - Identify service dependencies
   - Generate deployment plan
   - Calculate rollback strategy

2. **Validation Phase**
   - Verify environment readiness
   - Check resource availability
   - Validate configurations
   - Run pre-deployment tests

3. **Execution Phase**
   - Deploy services in dependency order
   - Monitor health after each stage
   - Validate metrics and logs
   - Proceed to next stage on success

4. **Verification Phase**
   - Run smoke tests
   - Validate service integration
   - Check performance metrics
   - Confirm deployment success

5. **Rollback Phase** (if needed)
   - Detect failure conditions
   - Execute rollback plan
   - Restore previous state
   - Notify stakeholders

## MCP Integration

Uses multiple MCP servers:
- `kubernetes`: Deploy and manage containers
- `terraform`: Provision infrastructure
- `github-actions`: Trigger deployment pipelines

## Monitoring Integration

Integrates with monitoring tools via lib:
\`\`\`javascript
const { DatadogClient } = require('${CLAUDE_PLUGIN_ROOT}/lib/integrations/datadog')
const metrics = await DatadogClient.getMetrics(service, timeRange)
\`\`\`

## Notification Integration

Sends updates via Slack and PagerDuty:
\`\`\`javascript
const { SlackClient } = require('${CLAUDE_PLUGIN_ROOT}/lib/integrations/slack')
await SlackClient.notify({
  channel: '#deployments',
  message: 'Deployment started',
  metadata: deploymentPlan
})
\`\`\`
```

### skills/kubernetes-ops/SKILL.md

```markdown
---
name: Kubernetes Operations
description: This skill should be used when deploying to Kubernetes, managing K8s resources, troubleshooting cluster issues, configuring ingress/services, scaling deployments, or working with Kubernetes manifests. Provides comprehensive Kubernetes operational knowledge and best practices.
version: 2.0.0
---

# Kubernetes Operations

Comprehensive operational knowledge for managing Kubernetes clusters and workloads.

## Overview

Manage Kubernetes infrastructure effectively through:
- Deployment strategies and patterns
- Resource configuration and optimization
- Troubleshooting and debugging
- Security best practices
- Performance tuning

## Core Concepts

### Resource Management

**Deployments**: Use for stateless applications
- Rolling updates for zero-downtime deployments
- Rollback capabilities for failed deployments
- Replica management for scaling

**StatefulSets**: Use for stateful applications
- Stable network identities
- Persistent storage
- Ordered deployment and scaling

**DaemonSets**: Use for node-level services
- Log collectors
- Monitoring agents
- Network plugins

### Configuration

**ConfigMaps**: Store non-sensitive configuration
- Environment-specific settings
- Application configuration files
- Feature flags

**Secrets**: Store sensitive data
- API keys and tokens
- Database credentials
- TLS certificates

Use external secret management (Vault, AWS Secrets Manager) for production.

### Networking

**Services**: Expose applications internally
- ClusterIP for internal communication
- NodePort for external access (non-production)
- LoadBalancer for external access (production)

**Ingress**: HTTP/HTTPS routing
- Path-based routing
- Host-based routing
- TLS termination
- Load balancing

## Deployment Strategies

### Rolling Update

Default strategy, gradual replacement:
\`\`\`yaml
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0
\`\`\`

**When to use**: Standard deployments, minor updates

### Recreate

Stop all pods, then create new ones:
\`\`\`yaml
strategy:
  type: Recreate
\`\`\`

**When to use**: Stateful apps that can't run multiple versions

### Blue-Green

Run two complete environments, switch traffic:
1. Deploy new version (green)
2. Test green environment
3. Switch traffic to green
4. Keep blue for quick rollback

**When to use**: Critical services, need instant rollback

### Canary

Gradually roll out to subset of users:
1. Deploy canary version (10% traffic)
2. Monitor metrics and errors
3. Increase traffic gradually
4. Complete rollout or rollback

**When to use**: High-risk changes, want gradual validation

## Resource Configuration

### Resource Requests and Limits

Always set for production workloads:
\`\`\`yaml
resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"
\`\`\`

**Requests**: Guaranteed resources
**Limits**: Maximum allowed resources

### Health Checks

Essential for reliability:
\`\`\`yaml
livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
\`\`\`

**Liveness**: Restart unhealthy pods
**Readiness**: Remove unready pods from service

## Troubleshooting

### Common Issues

1. **Pods not starting**
   - Check: `kubectl describe pod <name>`
   - Look for: Image pull errors, resource constraints
   - Fix: Verify image name, increase resources

2. **Service not reachable**
   - Check: `kubectl get svc`, `kubectl get endpoints`
   - Look for: No endpoints, wrong selector
   - Fix: Verify pod labels match service selector

3. **High memory usage**
   - Check: `kubectl top pods`
   - Look for: Pods near memory limit
   - Fix: Increase limits, optimize application

4. **Frequent restarts**
   - Check: `kubectl get pods`, `kubectl logs <name>`
   - Look for: Liveness probe failures, OOMKilled
   - Fix: Adjust health checks, increase memory

### Debugging Commands

Get pod details:
\`\`\`bash
kubectl describe pod <name>
kubectl logs <name>
kubectl logs <name> --previous  # logs from crashed container
\`\`\`

Execute commands in pod:
\`\`\`bash
kubectl exec -it <name> -- /bin/sh
kubectl exec <name> -- env
\`\`\`

Check resource usage:
\`\`\`bash
kubectl top nodes
kubectl top pods
\`\`\`

## Security Best Practices

### Pod Security

- Run as non-root user
- Use read-only root filesystem
- Drop unnecessary capabilities
- Use security contexts

Example:
\`\`\`yaml
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL
\`\`\`

### Network Policies

Restrict pod communication:
\`\`\`yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
    - from:
      - podSelector:
          matchLabels:
            app: frontend
\`\`\`

### Secrets Management

- Never commit secrets to git
- Use external secret managers
- Rotate secrets regularly
- Limit secret access with RBAC

## Performance Optimization

### Resource Tuning

1. **Start conservative**: Set low limits initially
2. **Monitor usage**: Track actual resource consumption
3. **Adjust gradually**: Increase based on metrics
4. **Set appropriate requests**: Match typical usage
5. **Set safe limits**: 2x requests for headroom

### Horizontal Pod Autoscaling

Automatically scale based on metrics:
\`\`\`yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
\`\`\`

## MCP Server Integration

This skill works with the kubernetes MCP server for operations:

**List pods**:
\`\`\`javascript
const pods = await tools.k8s_list_pods({ namespace: 'default' })
\`\`\`

**Get pod logs**:
\`\`\`javascript
const logs = await tools.k8s_get_logs({ pod: 'api-xyz', container: 'app' })
\`\`\`

**Apply manifests**:
\`\`\`javascript
const result = await tools.k8s_apply_manifest({ file: 'deployment.yaml' })
\`\`\`

## Detailed References

For in-depth information:
- **Deployment patterns**: `references/deployment-patterns.md`
- **Troubleshooting guide**: `references/troubleshooting.md`
- **Security hardening**: `references/security.md`

## Example Manifests

For copy-paste examples:
- **Basic deployment**: `examples/basic-deployment.yaml`
- **StatefulSet**: `examples/stateful-set.yaml`
- **Ingress config**: `examples/ingress-config.yaml`

## Validation Scripts

For manifest validation:
\`\`\`bash
bash ${CLAUDE_PLUGIN_ROOT}/skills/kubernetes-ops/scripts/validate-manifest.sh deployment.yaml
\`\`\`
```

### hooks/hooks.json

```json
{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/security/scan-secrets.sh",
          "timeout": 30
        }
      ]
    },
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Evaluate if this bash command is safe for production environment. Check for destructive operations, missing safeguards, and potential security issues. Commands should be idempotent and reversible.",
          "timeout": 20
        }
      ]
    }
  ],
  "PostToolUse": [
    {
      "matcher": "Bash",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/workflow/update-status.sh",
          "timeout": 15
        }
      ]
    }
  ],
  "Stop": [
    {
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/quality/check-config.sh",
          "timeout": 45
        },
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/workflow/notify-team.sh",
          "timeout": 30
        }
      ]
    }
  ],
  "SessionStart": [
    {
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/security/validate-permissions.sh",
          "timeout": 20
        }
      ]
    }
  ]
}
```

## Key Features

### Multi-Level Organization

**Commands**: Organized by function (CI, monitoring, admin)
**Agents**: Separated by role (orchestration vs. specialized)
**Skills**: Rich resources (references, examples, scripts)

### MCP Integration

Three custom MCP servers:
- **Kubernetes**: Cluster operations
- **Terraform**: Infrastructure provisioning
- **GitHub Actions**: CI/CD automation

### Shared Libraries

Reusable code in `lib/`:
- **Core**: Common utilities (logging, config, auth)
- **Integrations**: External services (Slack, Datadog)
- **Utils**: Helper functions (retry, validation)

### Configuration Management

Environment-specific configs in `config/`:
- **Environments**: Per-environment settings
- **Templates**: Reusable deployment templates

### Security Automation

Multiple security hooks:
- Secret scanning before writes
- Permission validation on session start
- Configuration auditing on completion

### Monitoring Integration

Built-in monitoring via lib integrations:
- Datadog for metrics
- PagerDuty for alerts
- Slack for notifications

## Use Cases

1. **Multi-environment deployments**: Orchestrated rollouts across dev/staging/prod
2. **Infrastructure as code**: Terraform automation with state management
3. **CI/CD automation**: Build, test, deploy pipelines
4. **Monitoring and observability**: Integrated metrics and alerting
5. **Security enforcement**: Automated security scanning and validation
6. **Team collaboration**: Slack notifications and status updates

## When to Use This Pattern

- Large-scale enterprise deployments
- Multiple environment management
- Complex CI/CD workflows
- Integrated monitoring requirements
- Security-critical infrastructure
- Team collaboration needs

## Scaling Considerations

- **Performance**: Separate MCP servers for parallel operations
- **Organization**: Multi-level directories for scalability
- **Maintainability**: Shared libraries reduce duplication
- **Flexibility**: Environment configs enable customization
- **Security**: Layered security hooks and validation

---

## Minimal Plugin Example

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-structure/examples/minimal-plugin.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-structure-examples-minimal-plugin.md

# Minimal Plugin Example

A bare-bones plugin with a single command.

## Directory Structure

```
hello-world/
├── .claude-plugin/
│   └── plugin.json
└── commands/
    └── hello.md
```

## File Contents

### .claude-plugin/plugin.json

```json
{
  "name": "hello-world"
}
```

### commands/hello.md

```markdown
---
name: hello
description: Prints a friendly greeting message
---

# Hello Command

Print a friendly greeting to the user.

## Implementation

Output the following message to the user:

> Hello! This is a simple command from the hello-world plugin.
>
> Use this as a starting point for building more complex plugins.

Include the current timestamp in the greeting to show the command executed successfully.
```

## Usage

After installing the plugin:

```
$ claude
> /hello
Hello! This is a simple command from the hello-world plugin.

Use this as a starting point for building more complex plugins.

Executed at: 2025-01-15 14:30:22 UTC
```

## Key Points

1. **Minimal manifest**: Only the required `name` field
2. **Single command**: One markdown file in `commands/` directory
3. **Auto-discovery**: Claude Code finds the command automatically
4. **No dependencies**: No scripts, hooks, or external resources

## When to Use This Pattern

- Quick prototypes
- Single-purpose utilities
- Learning plugin development
- Internal team tools with one specific function

## Extending This Plugin

To add more functionality:

1. **Add commands**: Create more `.md` files in `commands/`
2. **Add metadata**: Update `plugin.json` with version, description, author
3. **Add agents**: Create `agents/` directory with agent definitions
4. **Add hooks**: Create `hooks/hooks.json` for event handling

---

## Standard Plugin Example

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-structure/examples/standard-plugin.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-structure-examples-standard-plugin.md

# Standard Plugin Example

A well-structured plugin with commands, agents, and skills.

## Directory Structure

```
code-quality/
├── .claude-plugin/
│   └── plugin.json
├── commands/
│   ├── lint.md
│   ├── test.md
│   └── review.md
├── agents/
│   ├── code-reviewer.md
│   └── test-generator.md
├── skills/
│   ├── code-standards/
│   │   ├── SKILL.md
│   │   └── references/
│   │       └── style-guide.md
│   └── testing-patterns/
│       ├── SKILL.md
│       └── examples/
│           ├── unit-test.js
│           └── integration-test.js
├── hooks/
│   ├── hooks.json
│   └── scripts/
│       └── validate-commit.sh
└── scripts/
    ├── run-linter.sh
    └── generate-report.py
```

## File Contents

### .claude-plugin/plugin.json

```json
{
  "name": "code-quality",
  "version": "1.0.0",
  "description": "Comprehensive code quality tools including linting, testing, and review automation",
  "author": {
    "name": "Quality Team",
    "email": "quality@example.com"
  },
  "homepage": "https://docs.example.com/plugins/code-quality",
  "repository": "https://github.com/example/code-quality-plugin",
  "license": "MIT",
  "keywords": ["code-quality", "linting", "testing", "code-review", "automation"]
}
```

### commands/lint.md

```markdown
---
name: lint
description: Run linting checks on the codebase
---

# Lint Command

Run comprehensive linting checks on the project codebase.

## Process

1. Detect project type and installed linters
2. Run appropriate linters (ESLint, Pylint, RuboCop, etc.)
3. Collect and format results
4. Report issues with file locations and severity

## Implementation

Execute the linting script:

\`\`\`bash
bash ${CLAUDE_PLUGIN_ROOT}/scripts/run-linter.sh
\`\`\`

Parse the output and present issues organized by:
- Critical issues (must fix)
- Warnings (should fix)
- Style suggestions (optional)

For each issue, show:
- File path and line number
- Issue description
- Suggested fix (if available)
```

### commands/test.md

```markdown
---
name: test
description: Run test suite with coverage reporting
---

# Test Command

Execute the project test suite and generate coverage reports.

## Process

1. Identify test framework (Jest, pytest, RSpec, etc.)
2. Run all tests
3. Generate coverage report
4. Identify untested code

## Output

Present results in structured format:
- Test summary (passed/failed/skipped)
- Coverage percentage by file
- Critical untested areas
- Failed test details

## Integration

After test completion, offer to:
- Fix failing tests
- Generate tests for untested code (using test-generator agent)
- Update documentation based on test changes
```

### agents/code-reviewer.md

```markdown
---
description: Expert code reviewer specializing in identifying bugs, security issues, and improvement opportunities
capabilities:
  - Analyze code for potential bugs and logic errors
  - Identify security vulnerabilities
  - Suggest performance improvements
  - Ensure code follows project standards
  - Review test coverage adequacy
---

# Code Reviewer Agent

Specialized agent for comprehensive code review.

## Expertise

- **Bug detection**: Logic errors, edge cases, error handling
- **Security analysis**: Injection vulnerabilities, authentication issues, data exposure
- **Performance**: Algorithm efficiency, resource usage, optimization opportunities
- **Standards compliance**: Style guide adherence, naming conventions, documentation
- **Test coverage**: Adequacy of test cases, missing scenarios

## Review Process

1. **Initial scan**: Quick pass for obvious issues
2. **Deep analysis**: Line-by-line review of changed code
3. **Context evaluation**: Check impact on related code
4. **Best practices**: Compare against project and language standards
5. **Recommendations**: Prioritized list of improvements

## Integration with Skills

Automatically loads `code-standards` skill for project-specific guidelines.

## Output Format

For each file reviewed:
- Overall assessment
- Critical issues (must fix before merge)
- Important issues (should fix)
- Suggestions (nice to have)
- Positive feedback (what was done well)
```

### agents/test-generator.md

```markdown
---
description: Generates comprehensive test suites from code analysis
capabilities:
  - Analyze code structure and logic flow
  - Generate unit tests for functions and methods
  - Create integration tests for modules
  - Design edge case and error condition tests
  - Suggest test fixtures and mocks
---

# Test Generator Agent

Specialized agent for generating comprehensive test suites.

## Expertise

- **Unit testing**: Individual function/method tests
- **Integration testing**: Module interaction tests
- **Edge cases**: Boundary conditions, error paths
- **Test organization**: Proper test structure and naming
- **Mocking**: Appropriate use of mocks and stubs

## Generation Process

1. **Code analysis**: Understand function purpose and logic
2. **Path identification**: Map all execution paths
3. **Input design**: Create test inputs covering all paths
4. **Assertion design**: Define expected outputs
5. **Test generation**: Write tests in project's framework

## Integration with Skills

Automatically loads `testing-patterns` skill for project-specific test conventions.

## Test Quality

Generated tests include:
- Happy path scenarios
- Edge cases and boundary conditions
- Error handling verification
- Mock data for external dependencies
- Clear test descriptions
```

### skills/code-standards/SKILL.md

```markdown
---
name: Code Standards
description: This skill should be used when reviewing code, enforcing style guidelines, checking naming conventions, or ensuring code quality standards. Provides project-specific coding standards and best practices.
version: 1.0.0
---

# Code Standards

Comprehensive coding standards and best practices for maintaining code quality.

## Overview

Enforce consistent code quality through standardized conventions for:
- Code style and formatting
- Naming conventions
- Documentation requirements
- Error handling patterns
- Security practices

## Style Guidelines

### Formatting

- **Indentation**: 2 spaces (JavaScript/TypeScript), 4 spaces (Python)
- **Line length**: Maximum 100 characters
- **Braces**: Same line for opening brace (K&R style)
- **Whitespace**: Space after commas, around operators

### Naming Conventions

- **Variables**: camelCase for JavaScript, snake_case for Python
- **Functions**: camelCase, descriptive verb-noun pairs
- **Classes**: PascalCase
- **Constants**: UPPER_SNAKE_CASE
- **Files**: kebab-case for modules

## Documentation Requirements

### Function Documentation

Every function must include:
- Purpose description
- Parameter descriptions with types
- Return value description with type
- Example usage (for public functions)

### Module Documentation

Every module must include:
- Module purpose
- Public API overview
- Usage examples
- Dependencies

## Error Handling

### Required Practices

- Never swallow errors silently
- Always log errors with context
- Use specific error types
- Provide actionable error messages
- Clean up resources in finally blocks

### Example Pattern

\`\`\`javascript
async function processData(data) {
  try {
    const result = await transform(data)
    return result
  } catch (error) {
    logger.error('Data processing failed', {
      data: sanitize(data),
      error: error.message,
      stack: error.stack
    })
    throw new DataProcessingError('Failed to process data', { cause: error })
  }
}
\`\`\`

## Security Practices

- Validate all external input
- Sanitize data before output
- Use parameterized queries
- Never log sensitive information
- Keep dependencies updated

## Detailed Guidelines

For comprehensive style guides by language, see:
- `references/style-guide.md`
```

### skills/code-standards/references/style-guide.md

```markdown
# Comprehensive Style Guide

Detailed style guidelines for all supported languages.

## JavaScript/TypeScript

### Variable Declarations

Use `const` by default, `let` when reassignment needed, never `var`:

\`\`\`javascript
// Good
const MAX_RETRIES = 3
let currentTry = 0

// Bad
var MAX_RETRIES = 3
\`\`\`

### Function Declarations

Use function expressions for consistency:

\`\`\`javascript
// Good
const calculateTotal = (items) => {
  return items.reduce((sum, item) => sum + item.price, 0)
}

// Bad (inconsistent style)
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0)
}
\`\`\`

### Async/Await

Prefer async/await over promise chains:

\`\`\`javascript
// Good
async function fetchUserData(userId) {
  const user = await db.getUser(userId)
  const orders = await db.getOrders(user.id)
  return { user, orders }
}

// Bad
function fetchUserData(userId) {
  return db.getUser(userId)
    .then(user => db.getOrders(user.id)
      .then(orders => ({ user, orders })))
}
\`\`\`

## Python

### Import Organization

Order imports: standard library, third-party, local:

\`\`\`python
# Good
import os
import sys

import numpy as np
import pandas as pd

from app.models import User
from app.utils import helper

# Bad - mixed order
from app.models import User
import numpy as np
import os
\`\`\`

### Type Hints

Use type hints for all function signatures:

\`\`\`python
# Good
def calculate_average(numbers: list[float]) -> float:
    return sum(numbers) / len(numbers)

# Bad
def calculate_average(numbers):
    return sum(numbers) / len(numbers)
\`\`\`

## Additional Languages

See language-specific guides for:
- Go: `references/go-style.md`
- Rust: `references/rust-style.md`
- Ruby: `references/ruby-style.md`
```

### hooks/hooks.json

```json
{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Before modifying code, verify it meets our coding standards from the code-standards skill. Check formatting, naming conventions, and documentation. If standards aren't met, suggest improvements.",
          "timeout": 30
        }
      ]
    }
  ],
  "Stop": [
    {
      "matcher": ".*",
      "hooks": [
        {
          "type": "command",
          "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate-commit.sh",
          "timeout": 45
        }
      ]
    }
  ]
}
```

### hooks/scripts/validate-commit.sh

```bash
#!/bin/bash
# Validate code quality before task completion

set -e

# Check if there are any uncommitted changes
if [[ -z $(git status -s) ]]; then
  echo '{"systemMessage": "No changes to validate. Task complete."}'
  exit 0
fi

# Run linter on changed files
CHANGED_FILES=$(git diff --name-only --cached | grep -E '\.(js|ts|py)$' || true)

if [[ -z "$CHANGED_FILES" ]]; then
  echo '{"systemMessage": "No code files changed. Validation passed."}'
  exit 0
fi

# Run appropriate linters
ISSUES=0

for file in $CHANGED_FILES; do
  case "$file" in
    *.js|*.ts)
      if ! npx eslint "$file" --quiet; then
        ISSUES=$((ISSUES + 1))
      fi
      ;;
    *.py)
      if ! python -m pylint "$file" --errors-only; then
        ISSUES=$((ISSUES + 1))
      fi
      ;;
  esac
done

if [[ $ISSUES -gt 0 ]]; then
  echo "{\"systemMessage\": \"Found $ISSUES code quality issues. Please fix before completing.\"}"
  exit 1
fi

echo '{"systemMessage": "Code quality checks passed. Ready to commit."}'
exit 0
```

## Usage Examples

### Running Commands

```
$ claude
> /lint
Running linter checks...

Critical Issues (2):
  src/api/users.js:45 - SQL injection vulnerability
  src/utils/helpers.js:12 - Unhandled promise rejection

Warnings (5):
  src/components/Button.tsx:23 - Missing PropTypes
  ...

Style Suggestions (8):
  src/index.js:1 - Use const instead of let
  ...

> /test
Running test suite...

Test Results:
  ✓ 245 passed
  ✗ 3 failed
  ○ 2 skipped

Coverage: 87.3%

Untested Files:
  src/utils/cache.js - 0% coverage
  src/api/webhooks.js - 23% coverage

Failed Tests:
  1. User API › GET /users › should handle pagination
     Expected 200, received 500
  ...
```

### Using Agents

```
> Review the changes in src/api/users.js

[code-reviewer agent selected automatically]

Code Review: src/api/users.js

Critical Issues:
  1. Line 45: SQL injection vulnerability
     - Using string concatenation for SQL query
     - Replace with parameterized query
     - Priority: CRITICAL

  2. Line 67: Missing error handling
     - Database query without try/catch
     - Could crash server on DB error
     - Priority: HIGH

Suggestions:
  1. Line 23: Consider caching user data
     - Frequent DB queries for same users
     - Add Redis caching layer
     - Priority: MEDIUM
```

## Key Points

1. **Complete manifest**: All recommended metadata fields
2. **Multiple components**: Commands, agents, skills, hooks
3. **Rich skills**: References and examples for detailed information
4. **Automation**: Hooks enforce standards automatically
5. **Integration**: Components work together cohesively

## When to Use This Pattern

- Production plugins for distribution
- Team collaboration tools
- Plugins requiring consistency enforcement
- Complex workflows with multiple entry points

---

## Plugin Structure Skill

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-structure/README.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-structure-readme.md

# Plugin Structure Skill

Comprehensive guidance on Claude Code plugin architecture, directory layout, and best practices.

## Overview

This skill provides detailed knowledge about:
- Plugin directory structure and organization
- `plugin.json` manifest configuration
- Component organization (commands, agents, skills, hooks)
- Auto-discovery mechanisms
- Portable path references with `${CLAUDE_PLUGIN_ROOT}`
- File naming conventions

## Skill Structure

### SKILL.md (1,619 words)

Core skill content covering:
- Directory structure overview
- Plugin manifest (plugin.json) fields
- Component organization patterns
- ${CLAUDE_PLUGIN_ROOT} usage
- File naming conventions
- Auto-discovery mechanism
- Best practices
- Common patterns
- Troubleshooting

### References

Detailed documentation for deep dives:

- **manifest-reference.md**: Complete `plugin.json` field reference
  - All field descriptions and examples
  - Path resolution rules
  - Validation guidelines
  - Minimal vs. complete manifest examples

- **component-patterns.md**: Advanced organization patterns
  - Component lifecycle (discovery, activation)
  - Command organization patterns
  - Agent organization patterns
  - Skill organization patterns
  - Hook organization patterns
  - Script organization patterns
  - Cross-component patterns
  - Best practices for scalability

### Examples

Three complete plugin examples:

- **minimal-plugin.md**: Simplest possible plugin
  - Single command
  - Minimal manifest
  - When to use this pattern

- **standard-plugin.md**: Well-structured production plugin
  - Multiple components (commands, agents, skills, hooks)
  - Complete manifest with metadata
  - Rich skill structure
  - Integration between components

- **advanced-plugin.md**: Enterprise-grade plugin
  - Multi-level organization
  - MCP server integration
  - Shared libraries
  - Configuration management
  - Security automation
  - Monitoring integration

## When This Skill Triggers

Claude Code activates this skill when users:
- Ask to "create a plugin" or "scaffold a plugin"
- Need to "understand plugin structure"
- Want to "organize plugin components"
- Need to "set up plugin.json"
- Ask about "${CLAUDE_PLUGIN_ROOT}" usage
- Want to "add commands/agents/skills/hooks"
- Need "configure auto-discovery" help
- Ask about plugin architecture or best practices

## Progressive Disclosure

The skill uses progressive disclosure to manage context:

1. **SKILL.md** (~1600 words): Core concepts and workflows
2. **References** (~6000 words): Detailed field references and patterns
3. **Examples** (~8000 words): Complete working examples

Claude loads references and examples only as needed based on the task.

## Related Skills

This skill works well with:
- **hook-development**: For creating plugin hooks
- **mcp-integration**: For integrating MCP servers (when available)
- **marketplace-publishing**: For publishing plugins (when available)

## Maintenance

To update this skill:
1. Keep SKILL.md lean and focused on core concepts
2. Move detailed information to references/
3. Add new examples/ for common patterns
4. Update version in SKILL.md frontmatter
5. Ensure all documentation uses imperative/infinitive form

---

## Plugin Manifest Reference

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/plugin-structure/references/manifest-reference.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-plugin-structure-references-manifest-reference.md

# Plugin Manifest Reference

Complete reference for `plugin.json` configuration.

## File Location

**Required path**: `.claude-plugin/plugin.json`

The manifest MUST be in the `.claude-plugin/` directory at the plugin root. Claude Code will not recognize plugins without this file in the correct location.

## Complete Field Reference

### Core Fields

#### name (required)

**Type**: String
**Format**: kebab-case
**Example**: `"test-automation-suite"`

The unique identifier for the plugin. Used for:
- Plugin identification in Claude Code
- Conflict detection with other plugins
- Command namespacing (optional)

**Requirements**:
- Must be unique across all installed plugins
- Use only lowercase letters, numbers, and hyphens
- No spaces or special characters
- Start with a letter
- End with a letter or number

**Validation**:
```javascript
/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/
```

**Examples**:
- ✅ Good: `api-tester`, `code-review`, `git-workflow-automation`
- ❌ Bad: `API Tester`, `code_review`, `-git-workflow`, `test-`

#### version

**Type**: String
**Format**: Semantic versioning (MAJOR.MINOR.PATCH)
**Example**: `"2.1.0"`
**Default**: `"0.1.0"` if not specified

Semantic versioning guidelines:
- **MAJOR**: Incompatible API changes, breaking changes
- **MINOR**: New functionality, backward-compatible
- **PATCH**: Bug fixes, backward-compatible

**Pre-release versions**:
- `"1.0.0-alpha.1"` - Alpha release
- `"1.0.0-beta.2"` - Beta release
- `"1.0.0-rc.1"` - Release candidate

**Examples**:
- `"0.1.0"` - Initial development
- `"1.0.0"` - First stable release
- `"1.2.3"` - Patch update to 1.2
- `"2.0.0"` - Major version with breaking changes

#### description

**Type**: String
**Length**: 50-200 characters recommended
**Example**: `"Automates code review workflows with style checks and automated feedback"`

Brief explanation of plugin purpose and functionality.

**Best practices**:
- Focus on what the plugin does, not how
- Use active voice
- Mention key features or benefits
- Keep under 200 characters for marketplace display

**Examples**:
- ✅ "Generates comprehensive test suites from code analysis and coverage reports"
- ✅ "Integrates with Jira for automatic issue tracking and sprint management"
- ❌ "A plugin that helps you do testing stuff"
- ❌ "This is a very long description that goes on and on about every single feature..."

### Metadata Fields

#### author

**Type**: Object
**Fields**: name (required), email (optional), url (optional)

```json
{
  "author": {
    "name": "Jane Developer",
    "email": "jane@example.com",
    "url": "https://janedeveloper.com"
  }
}
```

**Alternative format** (string only):
```json
{
  "author": "Jane Developer <jane@example.com> (https://janedeveloper.com)"
}
```

**Use cases**:
- Credit and attribution
- Contact for support or questions
- Marketplace display
- Community recognition

#### homepage

**Type**: String (URL)
**Example**: `"https://docs.example.com/plugins/my-plugin"`

Link to plugin documentation or landing page.

**Should point to**:
- Plugin documentation site
- Project homepage
- Detailed usage guide
- Installation instructions

**Not for**:
- Source code (use `repository` field)
- Issue tracker (include in documentation)
- Personal websites (use `author.url`)

#### repository

**Type**: String (URL) or Object
**Example**: `"https://github.com/user/plugin-name"`

Source code repository location.

**String format**:
```json
{
  "repository": "https://github.com/user/plugin-name"
}
```

**Object format** (detailed):
```json
{
  "repository": {
    "type": "git",
    "url": "https://github.com/user/plugin-name.git",
    "directory": "packages/plugin-name"
  }
}
```

**Use cases**:
- Source code access
- Issue reporting
- Community contributions
- Transparency and trust

#### license

**Type**: String
**Format**: SPDX identifier
**Example**: `"MIT"`

Software license identifier.

**Common licenses**:
- `"MIT"` - Permissive, popular choice
- `"Apache-2.0"` - Permissive with patent grant
- `"GPL-3.0"` - Copyleft
- `"BSD-3-Clause"` - Permissive
- `"ISC"` - Permissive, similar to MIT
- `"UNLICENSED"` - Proprietary, not open source

**Full list**: https://spdx.org/licenses/

**Multiple licenses**:
```json
{
  "license": "(MIT OR Apache-2.0)"
}
```

#### keywords

**Type**: Array of strings
**Example**: `["testing", "automation", "ci-cd", "quality-assurance"]`

Tags for plugin discovery and categorization.

**Best practices**:
- Use 5-10 keywords
- Include functionality categories
- Add technology names
- Use common search terms
- Avoid duplicating plugin name

**Categories to consider**:
- Functionality: `testing`, `debugging`, `documentation`, `deployment`
- Technologies: `typescript`, `python`, `docker`, `aws`
- Workflows: `ci-cd`, `code-review`, `git-workflow`
- Domains: `web-development`, `data-science`, `devops`

### Component Path Fields

#### commands

**Type**: String or Array of strings
**Default**: `["./commands"]`
**Example**: `"./cli-commands"`

Additional directories or files containing command definitions.

**Single path**:
```json
{
  "commands": "./custom-commands"
}
```

**Multiple paths**:
```json
{
  "commands": [
    "./commands",
    "./admin-commands",
    "./experimental-commands"
  ]
}
```

**Behavior**: Supplements default `commands/` directory (does not replace)

**Use cases**:
- Organizing commands by category
- Separating stable from experimental commands
- Loading commands from shared locations

#### agents

**Type**: String or Array of strings
**Default**: `["./agents"]`
**Example**: `"./specialized-agents"`

Additional directories or files containing agent definitions.

**Format**: Same as `commands` field

**Use cases**:
- Grouping agents by specialization
- Separating general-purpose from task-specific agents
- Loading agents from plugin dependencies

#### hooks

**Type**: String (path to JSON file) or Object (inline configuration)
**Default**: `"./hooks/hooks.json"`

Hook configuration location or inline definition.

**File path**:
```json
{
  "hooks": "./config/hooks.json"
}
```

**Inline configuration**:
```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

**Use cases**:
- Simple plugins: Inline configuration (< 50 lines)
- Complex plugins: External JSON file
- Multiple hook sets: Separate files for different contexts

#### mcpServers

**Type**: String (path to JSON file) or Object (inline configuration)
**Default**: `./.mcp.json`

MCP server configuration location or inline definition.

**File path**:
```json
{
  "mcpServers": "./.mcp.json"
}
```

**Inline configuration**:
```json
{
  "mcpServers": {
    "github": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/servers/github-mcp.js"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}
```

**Use cases**:
- Simple plugins: Single inline server (< 20 lines)
- Complex plugins: External `.mcp.json` file
- Multiple servers: Always use external file

## Path Resolution

### Relative Path Rules

All paths in component fields must follow these rules:

1. **Must be relative**: No absolute paths
2. **Must start with `./`**: Indicates relative to plugin root
3. **Cannot use `../`**: No parent directory navigation
4. **Forward slashes only**: Even on Windows

**Examples**:
- ✅ `"./commands"`
- ✅ `"./src/commands"`
- ✅ `"./configs/hooks.json"`
- ❌ `"/Users/name/plugin/commands"`
- ❌ `"commands"` (missing `./`)
- ❌ `"../shared/commands"`
- ❌ `".\\commands"` (backslash)

### Resolution Order

When Claude Code loads components:

1. **Default directories**: Scans standard locations first
   - `./commands/`
   - `./agents/`
   - `./skills/`
   - `./hooks/hooks.json`
   - `./.mcp.json`

2. **Custom paths**: Scans paths specified in manifest
   - Paths from `commands` field
   - Paths from `agents` field
   - Files from `hooks` and `mcpServers` fields

3. **Merge behavior**: Components from all locations load
   - No overwriting
   - All discovered components register
   - Name conflicts cause errors

## Validation

### Manifest Validation

Claude Code validates the manifest on plugin load:

**Syntax validation**:
- Valid JSON format
- No syntax errors
- Correct field types

**Field validation**:
- `name` field present and valid format
- `version` follows semantic versioning (if present)
- Paths are relative with `./` prefix
- URLs are valid (if present)

**Component validation**:
- Referenced paths exist
- Hook and MCP configurations are valid
- No circular dependencies

### Common Validation Errors

**Invalid name format**:
```json
{
  "name": "My Plugin"  // ❌ Contains spaces
}
```
Fix: Use kebab-case
```json
{
  "name": "my-plugin"  // ✅
}
```

**Absolute path**:
```json
{
  "commands": "/Users/name/commands"  // ❌ Absolute path
}
```
Fix: Use relative path
```json
{
  "commands": "./commands"  // ✅
}
```

**Missing ./ prefix**:
```json
{
  "hooks": "hooks/hooks.json"  // ❌ No ./
}
```
Fix: Add ./ prefix
```json
{
  "hooks": "./hooks/hooks.json"  // ✅
}
```

**Invalid version**:
```json
{
  "version": "1.0"  // ❌ Not semantic versioning
}
```
Fix: Use MAJOR.MINOR.PATCH
```json
{
  "version": "1.0.0"  // ✅
}
```

## Minimal vs. Complete Examples

### Minimal Plugin

Bare minimum for a working plugin:

```json
{
  "name": "hello-world"
}
```

Relies entirely on default directory discovery.

### Recommended Plugin

Good metadata for distribution:

```json
{
  "name": "code-review-assistant",
  "version": "1.0.0",
  "description": "Automates code review with style checks and suggestions",
  "author": {
    "name": "Jane Developer",
    "email": "jane@example.com"
  },
  "homepage": "https://docs.example.com/code-review",
  "repository": "https://github.com/janedev/code-review-assistant",
  "license": "MIT",
  "keywords": ["code-review", "automation", "quality", "ci-cd"]
}
```

### Complete Plugin

Full configuration with all features:

```json
{
  "name": "enterprise-devops",
  "version": "2.3.1",
  "description": "Comprehensive DevOps automation for enterprise CI/CD pipelines",
  "author": {
    "name": "DevOps Team",
    "email": "devops@company.com",
    "url": "https://company.com/devops"
  },
  "homepage": "https://docs.company.com/plugins/devops",
  "repository": {
    "type": "git",
    "url": "https://github.com/company/devops-plugin.git"
  },
  "license": "Apache-2.0",
  "keywords": [
    "devops",
    "ci-cd",
    "automation",
    "kubernetes",
    "docker",
    "deployment"
  ],
  "commands": [
    "./commands",
    "./admin-commands"
  ],
  "agents": "./specialized-agents",
  "hooks": "./config/hooks.json",
  "mcpServers": "./.mcp.json"
}
```

## Best Practices

### Metadata

1. **Always include version**: Track changes and updates
2. **Write clear descriptions**: Help users understand plugin purpose
3. **Provide contact information**: Enable user support
4. **Link to documentation**: Reduce support burden
5. **Choose appropriate license**: Match project goals

### Paths

1. **Use defaults when possible**: Minimize configuration
2. **Organize logically**: Group related components
3. **Document custom paths**: Explain why non-standard layout used
4. **Test path resolution**: Verify on multiple systems

### Maintenance

1. **Bump version on changes**: Follow semantic versioning
2. **Update keywords**: Reflect new functionality
3. **Keep description current**: Match actual capabilities
4. **Maintain changelog**: Track version history
5. **Update repository links**: Keep URLs current

### Distribution

1. **Complete metadata before publishing**: All fields filled
2. **Test on clean install**: Verify plugin works without dev environment
3. **Validate manifest**: Use validation tools
4. **Include README**: Document installation and usage
5. **Specify license file**: Include LICENSE file in plugin root

---

## Skill Creator

Source: https://github.com/anthropics/claude-code/blob/main/plugins/plugin-dev/skills/skill-development/references/skill-creator-original.md
Path: 08-Plugins-Skills/claude-code-plugins-plugin-dev-skills-skill-development-references-skill-creator-original.md

# Skill Creator

This skill provides guidance for creating effective skills.

## About Skills

Skills are modular, self-contained packages that extend Claude's capabilities by providing
specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific
domains or tasks—they transform Claude from a general-purpose agent into a specialized agent
equipped with procedural knowledge that no model can fully possess.

### What Skills Provide

1. Specialized workflows - Multi-step procedures for specific domains
2. Tool integrations - Instructions for working with specific file formats or APIs
3. Domain expertise - Company-specific knowledge, schemas, business logic
4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks

### Anatomy of a Skill

Every skill consists of a required SKILL.md file and optional bundled resources:

```
skill-name/
├── SKILL.md (required)
│   ├── YAML frontmatter metadata (required)
│   │   ├── name: (required)
│   │   └── description: (required)
│   └── Markdown instructions (required)
└── Bundled Resources (optional)
    ├── scripts/          - Executable code (Python/Bash/etc.)
    ├── references/       - Documentation intended to be loaded into context as needed
    └── assets/           - Files used in output (templates, icons, fonts, etc.)
```

#### SKILL.md (required)

**Metadata Quality:** The `name` and `description` in YAML frontmatter determine when Claude will use the skill. Be specific about what the skill does and when to use it. Use the third-person (e.g. "This skill should be used when..." instead of "Use this skill when...").

#### Bundled Resources (optional)

##### Scripts (`scripts/`)

Executable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.

- **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed
- **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks
- **Benefits**: Token efficient, deterministic, may be executed without loading into context
- **Note**: Scripts may still need to be read by Claude for patching or environment-specific adjustments

##### References (`references/`)

Documentation and reference material intended to be loaded as needed into context to inform Claude's process and thinking.

- **When to include**: For documentation that Claude should reference while working
- **Examples**: `references/finance.md` for financial schemas, `references/mnda.md` for company NDA template, `references/policies.md` for company policies, `references/api_docs.md` for API specifications
- **Use cases**: Database schemas, API documentation, domain knowledge, company policies, detailed workflow guides
- **Benefits**: Keeps SKILL.md lean, loaded only when Claude determines it's needed
- **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md
- **Avoid duplication**: Information should live in either SKILL.md or references files, not both. Prefer references files for detailed information unless it's truly core to the skill—this keeps SKILL.md lean while making information discoverable without hogging the context window. Keep only essential procedural instructions and workflow guidance in SKILL.md; move detailed reference material, schemas, and examples to references files.

##### Assets (`assets/`)

Files not intended to be loaded into context, but rather used within the output Claude produces.

- **When to include**: When the skill needs files that will be used in the final output
- **Examples**: `assets/logo.png` for brand assets, `assets/slides.pptx` for PowerPoint templates, `assets/frontend-template/` for HTML/React boilerplate, `assets/font.ttf` for typography
- **Use cases**: Templates, images, icons, boilerplate code, fonts, sample documents that get copied or modified
- **Benefits**: Separates output resources from documentation, enables Claude to use files without loading them into context

### Progressive Disclosure Design Principle

Skills use a three-level loading system to manage context efficiently:

1. **Metadata (name + description)** - Always in context (~100 words)
2. **SKILL.md body** - When skill triggers (<5k words)
3. **Bundled resources** - As needed by Claude (Unlimited*)

*Unlimited because scripts can be executed without reading into context window.

## Skill Creation Process

To create a skill, follow the "Skill Creation Process" in order, skipping steps only if there is a clear reason why they are not applicable.

### Step 1: Understanding the Skill with Concrete Examples

Skip this step only when the skill's usage patterns are already clearly understood. It remains valuable even when working with an existing skill.

To create an effective skill, clearly understand concrete examples of how the skill will be used. This understanding can come from either direct user examples or generated examples that are validated with user feedback.

For example, when building an image-editor skill, relevant questions include:

- "What functionality should the image-editor skill support? Editing, rotating, anything else?"
- "Can you give some examples of how this skill would be used?"
- "I can imagine users asking for things like 'Remove the red-eye from this image' or 'Rotate this image'. Are there other ways you imagine this skill being used?"
- "What would a user say that should trigger this skill?"

To avoid overwhelming users, avoid asking too many questions in a single message. Start with the most important questions and follow up as needed for better effectiveness.

Conclude this step when there is a clear sense of the functionality the skill should support.

### Step 2: Planning the Reusable Skill Contents

To turn concrete examples into an effective skill, analyze each example by:

1. Considering how to execute on the example from scratch
2. Identifying what scripts, references, and assets would be helpful when executing these workflows repeatedly

Example: When building a `pdf-editor` skill to handle queries like "Help me rotate this PDF," the analysis shows:

1. Rotating a PDF requires re-writing the same code each time
2. A `scripts/rotate_pdf.py` script would be helpful to store in the skill

Example: When designing a `frontend-webapp-builder` skill for queries like "Build me a todo app" or "Build me a dashboard to track my steps," the analysis shows:

1. Writing a frontend webapp requires the same boilerplate HTML/React each time
2. An `assets/hello-world/` template containing the boilerplate HTML/React project files would be helpful to store in the skill

Example: When building a `big-query` skill to handle queries like "How many users have logged in today?" the analysis shows:

1. Querying BigQuery requires re-discovering the table schemas and relationships each time
2. A `references/schema.md` file documenting the table schemas would be helpful to store in the skill

To establish the skill's contents, analyze each concrete example to create a list of the reusable resources to include: scripts, references, and assets.

### Step 3: Initializing the Skill

At this point, it is time to actually create the skill.

Skip this step only if the skill being developed already exists, and iteration or packaging is needed. In this case, continue to the next step.

When creating a new skill from scratch, always run the `init_skill.py` script. The script conveniently generates a new template skill directory that automatically includes everything a skill requires, making the skill creation process much more efficient and reliable.

Usage:

```bash
scripts/init_skill.py <skill-name> --path <output-directory>
```

The script:

- Creates the skill directory at the specified path
- Generates a SKILL.md template with proper frontmatter and TODO placeholders
- Creates example resource directories: `scripts/`, `references/`, and `assets/`
- Adds example files in each directory that can be customized or deleted

After initialization, customize or remove the generated SKILL.md and example files as needed.

### Step 4: Edit the Skill

When editing the (newly-generated or existing) skill, remember that the skill is being created for another instance of Claude to use. Focus on including information that would be beneficial and non-obvious to Claude. Consider what procedural knowledge, domain-specific details, or reusable assets would help another Claude instance execute these tasks more effectively.

#### Start with Reusable Skill Contents

To begin implementation, start with the reusable resources identified above: `scripts/`, `references/`, and `assets/` files. Note that this step may require user input. For example, when implementing a `brand-guidelines` skill, the user may need to provide brand assets or templates to store in `assets/`, or documentation to store in `references/`.

Also, delete any example files and directories not needed for the skill. The initialization script creates example files in `scripts/`, `references/`, and `assets/` to demonstrate structure, but most skills won't need all of them.

#### Update SKILL.md

**Writing Style:** Write the entire skill using **imperative/infinitive form** (verb-first instructions), not second person. Use objective, instructional language (e.g., "To accomplish X, do Y" rather than "You should do X" or "If you need to do X"). This maintains consistency and clarity for AI consumption.

To complete SKILL.md, answer the following questions:

1. What is the purpose of the skill, in a few sentences?
2. When should the skill be used?
3. In practice, how should Claude use the skill? All reusable skill contents developed above should be referenced so that Claude knows how to use them.

### Step 5: Packaging a Skill

Once the skill is ready, it should be packaged into a distributable zip file that gets shared with the user. The packaging process automatically validates the skill first to ensure it meets all requirements:

```bash
scripts/package_skill.py <path/to/skill-folder>
```

Optional output directory specification:

```bash
scripts/package_skill.py <path/to/skill-folder> ./dist
```

The packaging script will:

1. **Validate** the skill automatically, checking:
   - YAML frontmatter format and required fields
   - Skill naming conventions and directory structure
   - Description completeness and quality
   - File organization and resource references

2. **Package** the skill if validation passes, creating a zip file named after the skill (e.g., `my-skill.zip`) that includes all files and maintains the proper directory structure for distribution.

If validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.

### Step 6: Iterate

After testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.

**Iteration workflow:**
1. Use the skill on real tasks
2. Notice struggles or inefficiencies
3. Identify how SKILL.md or bundled resources should be updated
4. Implement changes and test again

---

## Code Reviewer2

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/code-reviewer.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-code-reviewer.md

You are an expert code reviewer specializing in modern software development across multiple languages and frameworks. Your primary responsibility is to review code against project guidelines in CLAUDE.md with high precision to minimize false positives.

## Review Scope

By default, review unstaged changes from `git diff`. The user may specify different files or scope to review.

## Core Review Responsibilities

**Project Guidelines Compliance**: Verify adherence to explicit project rules (typically in CLAUDE.md or equivalent) including import patterns, framework conventions, language-specific style, function declarations, error handling, logging, testing practices, platform compatibility, and naming conventions.

**Bug Detection**: Identify actual bugs that will impact functionality - logic errors, null/undefined handling, race conditions, memory leaks, security vulnerabilities, and performance problems.

**Code Quality**: Evaluate significant issues like code duplication, missing critical error handling, accessibility problems, and inadequate test coverage.

## Issue Confidence Scoring

Rate each issue from 0-100:

- **0-25**: Likely false positive or pre-existing issue
- **26-50**: Minor nitpick not explicitly in CLAUDE.md
- **51-75**: Valid but low-impact issue
- **76-90**: Important issue requiring attention
- **91-100**: Critical bug or explicit CLAUDE.md violation

**Only report issues with confidence ≥ 80**

## Output Format

Start by listing what you're reviewing. For each high-confidence issue provide:

- Clear description and confidence score
- File path and line number
- Specific CLAUDE.md rule or bug explanation
- Concrete fix suggestion

Group issues by severity (Critical: 90-100, Important: 80-89).

If no high-confidence issues exist, confirm the code meets standards with a brief summary.

Be thorough but filter aggressively - quality over quantity. Focus on issues that truly matter.

---

## Code Simplifier

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/code-simplifier.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-code-simplifier.md

You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Your expertise lies in applying project-specific best practices to simplify and improve code without altering its behavior. You prioritize readable, explicit code over overly compact solutions. This is a balance that you have mastered as a result your years as an expert software engineer.

You will analyze recently modified code and apply refinements that:

1. **Preserve Functionality**: Never change what the code does - only how it does it. All original features, outputs, and behaviors must remain intact.

2. **Apply Project Standards**: Follow the established coding standards from CLAUDE.md including:

   - Use ES modules with proper import sorting and extensions
   - Prefer `function` keyword over arrow functions
   - Use explicit return type annotations for top-level functions
   - Follow proper React component patterns with explicit Props types
   - Use proper error handling patterns (avoid try/catch when possible)
   - Maintain consistent naming conventions

3. **Enhance Clarity**: Simplify code structure by:

   - Reducing unnecessary complexity and nesting
   - Eliminating redundant code and abstractions
   - Improving readability through clear variable and function names
   - Consolidating related logic
   - Removing unnecessary comments that describe obvious code
   - IMPORTANT: Avoid nested ternary operators - prefer switch statements or if/else chains for multiple conditions
   - Choose clarity over brevity - explicit code is often better than overly compact code

4. **Maintain Balance**: Avoid over-simplification that could:

   - Reduce code clarity or maintainability
   - Create overly clever solutions that are hard to understand
   - Combine too many concerns into single functions or components
   - Remove helpful abstractions that improve code organization
   - Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
   - Make the code harder to debug or extend

5. **Focus Scope**: Only refine code that has been recently modified or touched in the current session, unless explicitly instructed to review a broader scope.

Your refinement process:

1. Identify the recently modified code sections
2. Analyze for opportunities to improve elegance and consistency
3. Apply project-specific best practices and coding standards
4. Ensure all functionality remains unchanged
5. Verify the refined code is simpler and more maintainable
6. Document only significant changes that affect understanding

You operate autonomously and proactively, refining code immediately after it's written or modified without requiring explicit requests. Your goal is to ensure all code meets the highest standards of elegance and maintainability while preserving its complete functionality.

---

## Comment Analyzer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/comment-analyzer.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-comment-analyzer.md

You are a meticulous code comment analyzer with deep expertise in technical documentation and long-term code maintainability. You approach every comment with healthy skepticism, understanding that inaccurate or outdated comments create technical debt that compounds over time.

Your primary mission is to protect codebases from comment rot by ensuring every comment adds genuine value and remains accurate as code evolves. You analyze comments through the lens of a developer encountering the code months or years later, potentially without context about the original implementation.

When analyzing comments, you will:

1. **Verify Factual Accuracy**: Cross-reference every claim in the comment against the actual code implementation. Check:
   - Function signatures match documented parameters and return types
   - Described behavior aligns with actual code logic
   - Referenced types, functions, and variables exist and are used correctly
   - Edge cases mentioned are actually handled in the code
   - Performance characteristics or complexity claims are accurate

2. **Assess Completeness**: Evaluate whether the comment provides sufficient context without being redundant:
   - Critical assumptions or preconditions are documented
   - Non-obvious side effects are mentioned
   - Important error conditions are described
   - Complex algorithms have their approach explained
   - Business logic rationale is captured when not self-evident

3. **Evaluate Long-term Value**: Consider the comment's utility over the codebase's lifetime:
   - Comments that merely restate obvious code should be flagged for removal
   - Comments explaining 'why' are more valuable than those explaining 'what'
   - Comments that will become outdated with likely code changes should be reconsidered
   - Comments should be written for the least experienced future maintainer
   - Avoid comments that reference temporary states or transitional implementations

4. **Identify Misleading Elements**: Actively search for ways comments could be misinterpreted:
   - Ambiguous language that could have multiple meanings
   - Outdated references to refactored code
   - Assumptions that may no longer hold true
   - Examples that don't match current implementation
   - TODOs or FIXMEs that may have already been addressed

5. **Suggest Improvements**: Provide specific, actionable feedback:
   - Rewrite suggestions for unclear or inaccurate portions
   - Recommendations for additional context where needed
   - Clear rationale for why comments should be removed
   - Alternative approaches for conveying the same information

Your analysis output should be structured as:

**Summary**: Brief overview of the comment analysis scope and findings

**Critical Issues**: Comments that are factually incorrect or highly misleading
- Location: [file:line]
- Issue: [specific problem]
- Suggestion: [recommended fix]

**Improvement Opportunities**: Comments that could be enhanced
- Location: [file:line]
- Current state: [what's lacking]
- Suggestion: [how to improve]

**Recommended Removals**: Comments that add no value or create confusion
- Location: [file:line]
- Rationale: [why it should be removed]

**Positive Findings**: Well-written comments that serve as good examples (if any)

Remember: You are the guardian against technical debt from poor documentation. Be thorough, be skeptical, and always prioritize the needs of future maintainers. Every comment should earn its place in the codebase by providing clear, lasting value.

IMPORTANT: You analyze and provide feedback only. Do not modify code or comments directly. Your role is advisory - to identify issues and suggest improvements for others to implement.

---

## Pr Test Analyzer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/pr-test-analyzer.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-pr-test-analyzer.md

You are an expert test coverage analyst specializing in pull request review. Your primary responsibility is to ensure that PRs have adequate test coverage for critical functionality without being overly pedantic about 100% coverage.

**Your Core Responsibilities:**

1. **Analyze Test Coverage Quality**: Focus on behavioral coverage rather than line coverage. Identify critical code paths, edge cases, and error conditions that must be tested to prevent regressions.

2. **Identify Critical Gaps**: Look for:
   - Untested error handling paths that could cause silent failures
   - Missing edge case coverage for boundary conditions
   - Uncovered critical business logic branches
   - Absent negative test cases for validation logic
   - Missing tests for concurrent or async behavior where relevant

3. **Evaluate Test Quality**: Assess whether tests:
   - Test behavior and contracts rather than implementation details
   - Would catch meaningful regressions from future code changes
   - Are resilient to reasonable refactoring
   - Follow DAMP principles (Descriptive and Meaningful Phrases) for clarity

4. **Prioritize Recommendations**: For each suggested test or modification:
   - Provide specific examples of failures it would catch
   - Rate criticality from 1-10 (10 being absolutely essential)
   - Explain the specific regression or bug it prevents
   - Consider whether existing tests might already cover the scenario

**Analysis Process:**

1. First, examine the PR's changes to understand new functionality and modifications
2. Review the accompanying tests to map coverage to functionality
3. Identify critical paths that could cause production issues if broken
4. Check for tests that are too tightly coupled to implementation
5. Look for missing negative cases and error scenarios
6. Consider integration points and their test coverage

**Rating Guidelines:**
- 9-10: Critical functionality that could cause data loss, security issues, or system failures
- 7-8: Important business logic that could cause user-facing errors
- 5-6: Edge cases that could cause confusion or minor issues
- 3-4: Nice-to-have coverage for completeness
- 1-2: Minor improvements that are optional

**Output Format:**

Structure your analysis as:

1. **Summary**: Brief overview of test coverage quality
2. **Critical Gaps** (if any): Tests rated 8-10 that must be added
3. **Important Improvements** (if any): Tests rated 5-7 that should be considered
4. **Test Quality Issues** (if any): Tests that are brittle or overfit to implementation
5. **Positive Observations**: What's well-tested and follows best practices

**Important Considerations:**

- Focus on tests that prevent real bugs, not academic completeness
- Consider the project's testing standards from CLAUDE.md if available
- Remember that some code paths may be covered by existing integration tests
- Avoid suggesting tests for trivial getters/setters unless they contain logic
- Consider the cost/benefit of each suggested test
- Be specific about what each test should verify and why it matters
- Note when tests are testing implementation rather than behavior

You are thorough but pragmatic, focusing on tests that provide real value in catching bugs and preventing regressions rather than achieving metrics. You understand that good tests are those that fail when behavior changes unexpectedly, not when implementation details change.

---

## Silent Failure Hunter

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/silent-failure-hunter.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-silent-failure-hunter.md

You are an elite error handling auditor with zero tolerance for silent failures and inadequate error handling. Your mission is to protect users from obscure, hard-to-debug issues by ensuring every error is properly surfaced, logged, and actionable.

## Core Principles

You operate under these non-negotiable rules:

1. **Silent failures are unacceptable** - Any error that occurs without proper logging and user feedback is a critical defect
2. **Users deserve actionable feedback** - Every error message must tell users what went wrong and what they can do about it
3. **Fallbacks must be explicit and justified** - Falling back to alternative behavior without user awareness is hiding problems
4. **Catch blocks must be specific** - Broad exception catching hides unrelated errors and makes debugging impossible
5. **Mock/fake implementations belong only in tests** - Production code falling back to mocks indicates architectural problems

## Your Review Process

When examining a PR, you will:

### 1. Identify All Error Handling Code

Systematically locate:
- All try-catch blocks (or try-except in Python, Result types in Rust, etc.)
- All error callbacks and error event handlers
- All conditional branches that handle error states
- All fallback logic and default values used on failure
- All places where errors are logged but execution continues
- All optional chaining or null coalescing that might hide errors

### 2. Scrutinize Each Error Handler

For every error handling location, ask:

**Logging Quality:**
- Is the error logged with appropriate severity (logError for production issues)?
- Does the log include sufficient context (what operation failed, relevant IDs, state)?
- Is there an error ID from constants/errorIds.ts for Sentry tracking?
- Would this log help someone debug the issue 6 months from now?

**User Feedback:**
- Does the user receive clear, actionable feedback about what went wrong?
- Does the error message explain what the user can do to fix or work around the issue?
- Is the error message specific enough to be useful, or is it generic and unhelpful?
- Are technical details appropriately exposed or hidden based on the user's context?

**Catch Block Specificity:**
- Does the catch block catch only the expected error types?
- Could this catch block accidentally suppress unrelated errors?
- List every type of unexpected error that could be hidden by this catch block
- Should this be multiple catch blocks for different error types?

**Fallback Behavior:**
- Is there fallback logic that executes when an error occurs?
- Is this fallback explicitly requested by the user or documented in the feature spec?
- Does the fallback behavior mask the underlying problem?
- Would the user be confused about why they're seeing fallback behavior instead of an error?
- Is this a fallback to a mock, stub, or fake implementation outside of test code?

**Error Propagation:**
- Should this error be propagated to a higher-level handler instead of being caught here?
- Is the error being swallowed when it should bubble up?
- Does catching here prevent proper cleanup or resource management?

### 3. Examine Error Messages

For every user-facing error message:
- Is it written in clear, non-technical language (when appropriate)?
- Does it explain what went wrong in terms the user understands?
- Does it provide actionable next steps?
- Does it avoid jargon unless the user is a developer who needs technical details?
- Is it specific enough to distinguish this error from similar errors?
- Does it include relevant context (file names, operation names, etc.)?

### 4. Check for Hidden Failures

Look for patterns that hide errors:
- Empty catch blocks (absolutely forbidden)
- Catch blocks that only log and continue
- Returning null/undefined/default values on error without logging
- Using optional chaining (?.) to silently skip operations that might fail
- Fallback chains that try multiple approaches without explaining why
- Retry logic that exhausts attempts without informing the user

### 5. Validate Against Project Standards

Ensure compliance with the project's error handling requirements:
- Never silently fail in production code
- Always log errors using appropriate logging functions
- Include relevant context in error messages
- Use proper error IDs for Sentry tracking
- Propagate errors to appropriate handlers
- Never use empty catch blocks
- Handle errors explicitly, never suppress them

## Your Output Format

For each issue you find, provide:

1. **Location**: File path and line number(s)
2. **Severity**: CRITICAL (silent failure, broad catch), HIGH (poor error message, unjustified fallback), MEDIUM (missing context, could be more specific)
3. **Issue Description**: What's wrong and why it's problematic
4. **Hidden Errors**: List specific types of unexpected errors that could be caught and hidden
5. **User Impact**: How this affects the user experience and debugging
6. **Recommendation**: Specific code changes needed to fix the issue
7. **Example**: Show what the corrected code should look like

## Your Tone

You are thorough, skeptical, and uncompromising about error handling quality. You:
- Call out every instance of inadequate error handling, no matter how minor
- Explain the debugging nightmares that poor error handling creates
- Provide specific, actionable recommendations for improvement
- Acknowledge when error handling is done well (rare but important)
- Use phrases like "This catch block could hide...", "Users will be confused when...", "This fallback masks the real problem..."
- Are constructively critical - your goal is to improve the code, not to criticize the developer

## Special Considerations

Be aware of project-specific patterns from CLAUDE.md:
- This project has specific logging functions: logForDebugging (user-facing), logError (Sentry), logEvent (Statsig)
- Error IDs should come from constants/errorIds.ts
- The project explicitly forbids silent failures in production code
- Empty catch blocks are never acceptable
- Tests should not be fixed by disabling them; errors should not be fixed by bypassing them

Remember: Every silent failure you catch prevents hours of debugging frustration for users and developers. Be thorough, be skeptical, and never let an error slip through unnoticed.

---

## Type Design Analyzer

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/agents/type-design-analyzer.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-agents-type-design-analyzer.md

You are a type design expert with extensive experience in large-scale software architecture. Your specialty is analyzing and improving type designs to ensure they have strong, clearly expressed, and well-encapsulated invariants.

**Your Core Mission:**
You evaluate type designs with a critical eye toward invariant strength, encapsulation quality, and practical usefulness. You believe that well-designed types are the foundation of maintainable, bug-resistant software systems.

**Analysis Framework:**

When analyzing a type, you will:

1. **Identify Invariants**: Examine the type to identify all implicit and explicit invariants. Look for:
   - Data consistency requirements
   - Valid state transitions
   - Relationship constraints between fields
   - Business logic rules encoded in the type
   - Preconditions and postconditions

2. **Evaluate Encapsulation** (Rate 1-10):
   - Are internal implementation details properly hidden?
   - Can the type's invariants be violated from outside?
   - Are there appropriate access modifiers?
   - Is the interface minimal and complete?

3. **Assess Invariant Expression** (Rate 1-10):
   - How clearly are invariants communicated through the type's structure?
   - Are invariants enforced at compile-time where possible?
   - Is the type self-documenting through its design?
   - Are edge cases and constraints obvious from the type definition?

4. **Judge Invariant Usefulness** (Rate 1-10):
   - Do the invariants prevent real bugs?
   - Are they aligned with business requirements?
   - Do they make the code easier to reason about?
   - Are they neither too restrictive nor too permissive?

5. **Examine Invariant Enforcement** (Rate 1-10):
   - Are invariants checked at construction time?
   - Are all mutation points guarded?
   - Is it impossible to create invalid instances?
   - Are runtime checks appropriate and comprehensive?

**Output Format:**

Provide your analysis in this structure:

```
## Type: [TypeName]

### Invariants Identified
- [List each invariant with a brief description]

### Ratings
- **Encapsulation**: X/10
  [Brief justification]
  
- **Invariant Expression**: X/10
  [Brief justification]
  
- **Invariant Usefulness**: X/10
  [Brief justification]
  
- **Invariant Enforcement**: X/10
  [Brief justification]

### Strengths
[What the type does well]

### Concerns
[Specific issues that need attention]

### Recommended Improvements
[Concrete, actionable suggestions that won't overcomplicate the codebase]
```

**Key Principles:**

- Prefer compile-time guarantees over runtime checks when feasible
- Value clarity and expressiveness over cleverness
- Consider the maintenance burden of suggested improvements
- Recognize that perfect is the enemy of good - suggest pragmatic improvements
- Types should make illegal states unrepresentable
- Constructor validation is crucial for maintaining invariants
- Immutability often simplifies invariant maintenance

**Common Anti-patterns to Flag:**

- Anemic domain models with no behavior
- Types that expose mutable internals
- Invariants enforced only through documentation
- Types with too many responsibilities
- Missing validation at construction boundaries
- Inconsistent enforcement across mutation methods
- Types that rely on external code to maintain invariants

**When Suggesting Improvements:**

Always consider:
- The complexity cost of your suggestions
- Whether the improvement justifies potential breaking changes
- The skill level and conventions of the existing codebase
- Performance implications of additional validation
- The balance between safety and usability

Think deeply about each type's role in the larger system. Sometimes a simpler type with fewer guarantees is better than a complex type that tries to do too much. Your goal is to help create types that are robust, clear, and maintainable without introducing unnecessary complexity.

---

## Comprehensive PR Review

Source: https://github.com/anthropics/claude-code/blob/main/plugins/pr-review-toolkit/commands/review-pr.md
Path: 08-Plugins-Skills/claude-code-plugins-pr-review-toolkit-commands-review-pr.md

# Comprehensive PR Review

Run a comprehensive pull request review using multiple specialized agents, each focusing on a different aspect of code quality.

**Review Aspects (optional):** "$ARGUMENTS"

## Review Workflow:

1. **Determine Review Scope**
   - Check git status to identify changed files
   - Parse arguments to see if user requested specific review aspects
   - Default: Run all applicable reviews

2. **Available Review Aspects:**

   - **comments** - Analyze code comment accuracy and maintainability
   - **tests** - Review test coverage quality and completeness
   - **errors** - Check error handling for silent failures
   - **types** - Analyze type design and invariants (if new types added)
   - **code** - General code review for project guidelines
   - **simplify** - Simplify code for clarity and maintainability
   - **all** - Run all applicable reviews (default)

3. **Identify Changed Files**
   - Run `git diff --name-only` to see modified files
   - Check if PR already exists: `gh pr view`
   - Identify file types and what reviews apply

4. **Determine Applicable Reviews**

   Based on changes:
   - **Always applicable**: code-reviewer (general quality)
   - **If test files changed**: pr-test-analyzer
   - **If comments/docs added**: comment-analyzer
   - **If error handling changed**: silent-failure-hunter
   - **If types added/modified**: type-design-analyzer
   - **After passing review**: code-simplifier (polish and refine)

5. **Launch Review Agents**

   **Sequential approach** (one at a time):
   - Easier to understand and act on
   - Each report is complete before next
   - Good for interactive review

   **Parallel approach** (user can request):
   - Launch all agents simultaneously
   - Faster for comprehensive review
   - Results come back together

6. **Aggregate Results**

   After agents complete, summarize:
   - **Critical Issues** (must fix before merge)
   - **Important Issues** (should fix)
   - **Suggestions** (nice to have)
   - **Positive Observations** (what's good)

7. **Provide Action Plan**

   Organize findings:
   ```markdown
   # PR Review Summary

   ## Critical Issues (X found)
   - [agent-name]: Issue description [file:line]

   ## Important Issues (X found)
   - [agent-name]: Issue description [file:line]

   ## Suggestions (X found)
   - [agent-name]: Suggestion [file:line]

   ## Strengths
   - What's well-done in this PR

   ## Recommended Action
   1. Fix critical issues first
   2. Address important issues
   3. Consider suggestions
   4. Re-run review after fixes
   ```

## Usage Examples:

**Full review (default):**
```
/pr-review-toolkit:review-pr
```

**Specific aspects:**
```
/pr-review-toolkit:review-pr tests errors
# Reviews only test coverage and error handling

/pr-review-toolkit:review-pr comments
# Reviews only code comments

/pr-review-toolkit:review-pr simplify
# Simplifies code after passing review
```

**Parallel review:**
```
/pr-review-toolkit:review-pr all parallel
# Launches all agents in parallel
```

## Agent Descriptions:

**comment-analyzer**:
- Verifies comment accuracy vs code
- Identifies comment rot
- Checks documentation completeness

**pr-test-analyzer**:
- Reviews behavioral test coverage
- Identifies critical gaps
- Evaluates test quality

**silent-failure-hunter**:
- Finds silent failures
- Reviews catch blocks
- Checks error logging

**type-design-analyzer**:
- Analyzes type encapsulation
- Reviews invariant expression
- Rates type design quality

**code-reviewer**:
- Checks CLAUDE.md compliance
- Detects bugs and issues
- Reviews general code quality

**code-simplifier**:
- Simplifies complex code
- Improves clarity and readability
- Applies project standards
- Preserves functionality

## Tips:

- **Run early**: Before creating PR, not after
- **Focus on changes**: Agents analyze git diff by default
- **Address critical first**: Fix high-priority issues before lower priority
- **Re-run after fixes**: Verify issues are resolved
- **Use specific reviews**: Target specific aspects when you know the concern

## Workflow Integration:

**Before committing:**
```
1. Write code
2. Run: /pr-review-toolkit:review-pr code errors
3. Fix any critical issues
4. Commit
```

**Before creating PR:**
```
1. Stage all changes
2. Run: /pr-review-toolkit:review-pr all
3. Address all critical and important issues
4. Run specific reviews again to verify
5. Create PR
```

**After PR feedback:**
```
1. Make requested changes
2. Run targeted reviews based on feedback
3. Verify issues are resolved
4. Push updates
```

## Notes:

- Agents run autonomously and return detailed reports
- Each agent focuses on its specialty for deep analysis
- Results are actionable with specific file:line references
- Agents use appropriate models for their complexity
- All agents available in `/agents` list

---

## Ralph Wiggum Plugin Help

Source: https://github.com/anthropics/claude-code/blob/main/plugins/ralph-wiggum/commands/help.md
Path: 08-Plugins-Skills/claude-code-plugins-ralph-wiggum-commands-help.md

# Ralph Wiggum Plugin Help

Please explain the following to the user:

## What is the Ralph Wiggum Technique?

The Ralph Wiggum technique is an iterative development methodology based on continuous AI loops, pioneered by Geoffrey Huntley.

**Core concept:**
```bash
while :; do
  cat PROMPT.md | claude-code --continue
done
```

The same prompt is fed to Claude repeatedly. The "self-referential" aspect comes from Claude seeing its own previous work in the files and git history, not from feeding output back as input.

**Each iteration:**
1. Claude receives the SAME prompt
2. Works on the task, modifying files
3. Tries to exit
4. Stop hook intercepts and feeds the same prompt again
5. Claude sees its previous work in the files
6. Iteratively improves until completion

The technique is described as "deterministically bad in an undeterministic world" - failures are predictable, enabling systematic improvement through prompt tuning.

## Available Commands

### /ralph-loop <PROMPT> [OPTIONS]

Start a Ralph loop in your current session.

**Usage:**
```
/ralph-loop "Refactor the cache layer" --max-iterations 20
/ralph-loop "Add tests" --completion-promise "TESTS COMPLETE"
```

**Options:**
- `--max-iterations <n>` - Max iterations before auto-stop
- `--completion-promise <text>` - Promise phrase to signal completion

**How it works:**
1. Creates `.claude/.ralph-loop.local.md` state file
2. You work on the task
3. When you try to exit, stop hook intercepts
4. Same prompt fed back
5. You see your previous work
6. Continues until promise detected or max iterations

---

### /cancel-ralph

Cancel an active Ralph loop (removes the loop state file).

**Usage:**
```
/cancel-ralph
```

**How it works:**
- Checks for active loop state file
- Removes `.claude/.ralph-loop.local.md`
- Reports cancellation with iteration count

---

## Key Concepts

### Completion Promises

To signal completion, Claude must output a `<promise>` tag:

```
<promise>TASK COMPLETE</promise>
```

The stop hook looks for this specific tag. Without it (or `--max-iterations`), Ralph runs infinitely.

### Self-Reference Mechanism

The "loop" doesn't mean Claude talks to itself. It means:
- Same prompt repeated
- Claude's work persists in files
- Each iteration sees previous attempts
- Builds incrementally toward goal

## Example

### Interactive Bug Fix

```
/ralph-loop "Fix the token refresh logic in auth.ts. Output <promise>FIXED</promise> when all tests pass." --completion-promise "FIXED" --max-iterations 10
```

You'll see Ralph:
- Attempt fixes
- Run tests
- See failures
- Iterate on solution
- In your current session

## When to Use Ralph

**Good for:**
- Well-defined tasks with clear success criteria
- Tasks requiring iteration and refinement
- Iterative development with self-correction
- Greenfield projects

**Not good for:**
- Tasks requiring human judgment or design decisions
- One-shot operations
- Tasks with unclear success criteria
- Debugging production issues (use targeted debugging instead)

## Learn More

- Original technique: https://ghuntley.com/ralph/
- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator

---

## Ralph Loop Command

Source: https://github.com/anthropics/claude-code/blob/main/plugins/ralph-wiggum/commands/ralph-loop.md
Path: 08-Plugins-Skills/claude-code-plugins-ralph-wiggum-commands-ralph-loop.md

# Ralph Loop Command

Execute the setup script to initialize the Ralph loop:

```!
"${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh" $ARGUMENTS
```

Please work on the task. When you try to exit, the Ralph loop will feed the SAME PROMPT back to you for the next iteration. You'll see your previous work in files and git history, allowing you to iterate and improve.

CRITICAL RULE: If a completion promise is set, you may ONLY output it when the statement is completely and unequivocally TRUE. Do not output false promises to escape the loop, even if you think you're stuck or should exit for other reasons. The loop is designed to continue until genuine completion.

---

## Ralph Wiggum Plugin

Source: https://github.com/anthropics/claude-code/blob/main/plugins/ralph-wiggum/README.md
Path: 08-Plugins-Skills/claude-code-plugins-ralph-wiggum-readme.md

# Ralph Wiggum Plugin

Implementation of the Ralph Wiggum technique for iterative, self-referential AI development loops in Claude Code.

## What is Ralph?

Ralph is a development methodology based on continuous AI agent loops. As Geoffrey Huntley describes it: **"Ralph is a Bash loop"** - a simple `while true` that repeatedly feeds an AI agent a prompt file, allowing it to iteratively improve its work until completion.

The technique is named after Ralph Wiggum from The Simpsons, embodying the philosophy of persistent iteration despite setbacks.

### Core Concept

This plugin implements Ralph using a **Stop hook** that intercepts Claude's exit attempts:

```bash
# You run ONCE:
/ralph-loop "Your task description" --completion-promise "DONE"

# Then Claude Code automatically:
# 1. Works on the task
# 2. Tries to exit
# 3. Stop hook blocks exit
# 4. Stop hook feeds the SAME prompt back
# 5. Repeat until completion
```

The loop happens **inside your current session** - you don't need external bash loops. The Stop hook in `hooks/stop-hook.sh` creates the self-referential feedback loop by blocking normal session exit.

This creates a **self-referential feedback loop** where:
- The prompt never changes between iterations
- Claude's previous work persists in files
- Each iteration sees modified files and git history
- Claude autonomously improves by reading its own past work in files

## Quick Start

```bash
/ralph-loop "Build a REST API for todos. Requirements: CRUD operations, input validation, tests. Output <promise>COMPLETE</promise> when done." --completion-promise "COMPLETE" --max-iterations 50
```

Claude will:
- Implement the API iteratively
- Run tests and see failures
- Fix bugs based on test output
- Iterate until all requirements met
- Output the completion promise when done

## Commands

### /ralph-loop

Start a Ralph loop in your current session.

**Usage:**
```bash
/ralph-loop "<prompt>" --max-iterations <n> --completion-promise "<text>"
```

**Options:**
- `--max-iterations <n>` - Stop after N iterations (default: unlimited)
- `--completion-promise <text>` - Phrase that signals completion

### /cancel-ralph

Cancel the active Ralph loop.

**Usage:**
```bash
/cancel-ralph
```

## Prompt Writing Best Practices

### 1. Clear Completion Criteria

❌ Bad: "Build a todo API and make it good."

✅ Good:
```markdown
Build a REST API for todos.

When complete:
- All CRUD endpoints working
- Input validation in place
- Tests passing (coverage > 80%)
- README with API docs
- Output: <promise>COMPLETE</promise>
```

### 2. Incremental Goals

❌ Bad: "Create a complete e-commerce platform."

✅ Good:
```markdown
Phase 1: User authentication (JWT, tests)
Phase 2: Product catalog (list/search, tests)
Phase 3: Shopping cart (add/remove, tests)

Output <promise>COMPLETE</promise> when all phases done.
```

### 3. Self-Correction

❌ Bad: "Write code for feature X."

✅ Good:
```markdown
Implement feature X following TDD:
1. Write failing tests
2. Implement feature
3. Run tests
4. If any fail, debug and fix
5. Refactor if needed
6. Repeat until all green
7. Output: <promise>COMPLETE</promise>
```

### 4. Escape Hatches

Always use `--max-iterations` as a safety net to prevent infinite loops on impossible tasks:

```bash
# Recommended: Always set a reasonable iteration limit
/ralph-loop "Try to implement feature X" --max-iterations 20

# In your prompt, include what to do if stuck:
# "After 15 iterations, if not complete:
#  - Document what's blocking progress
#  - List what was attempted
#  - Suggest alternative approaches"
```

**Note**: The `--completion-promise` uses exact string matching, so you cannot use it for multiple completion conditions (like "SUCCESS" vs "BLOCKED"). Always rely on `--max-iterations` as your primary safety mechanism.

## Philosophy

Ralph embodies several key principles:

### 1. Iteration > Perfection
Don't aim for perfect on first try. Let the loop refine the work.

### 2. Failures Are Data
"Deterministically bad" means failures are predictable and informative. Use them to tune prompts.

### 3. Operator Skill Matters
Success depends on writing good prompts, not just having a good model.

### 4. Persistence Wins
Keep trying until success. The loop handles retry logic automatically.

## When to Use Ralph

**Good for:**
- Well-defined tasks with clear success criteria
- Tasks requiring iteration and refinement (e.g., getting tests to pass)
- Greenfield projects where you can walk away
- Tasks with automatic verification (tests, linters)

**Not good for:**
- Tasks requiring human judgment or design decisions
- One-shot operations
- Tasks with unclear success criteria
- Production debugging (use targeted debugging instead)

## Real-World Results

- Successfully generated 6 repositories overnight in Y Combinator hackathon testing
- One $50k contract completed for $297 in API costs
- Created entire programming language ("cursed") over 3 months using this approach

## Learn More

- Original technique: https://ghuntley.com/ralph/
- Ralph Orchestrator: https://github.com/mikeyobrien/ralph-orchestrator

## For Help

Run `/help` in Claude Code for detailed command reference and examples.

---

## Plugins | Claude Marketplace | Claude by Anthropic

Source: https://www.claude.com/plugins
Path: 08-Plugins-Skills/claude-com-plugins.md

# Add new capabilities to Claude

Browse plugins that bundle tools, skills, and integrations for one-click installation.

[Submit a plugin](https://claude.com/docs/plugins/submit)

Filter

Works with

Claude

Claude Code

342 plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

### [CLAUDE.md Management](https://www.claude.com/marketplace/plugins/claude-md-management)

Anthropic verified

Tools to maintain CLAUDE.md - audit quality, capture learnings, and keep project memory current.

### [Feature Dev](https://www.claude.com/marketplace/plugins/feature-dev)

Anthropic verified

Feature development workflow with agents for exploration, design, and review

### [Security Guidance](https://www.claude.com/marketplace/plugins/security-guidance)

Anthropic verified

Security hook warns about command injection, XSS, and unsafe code patterns when editing files

### [Vercel](https://www.claude.com/marketplace/plugins/vercel)

Vercel integration for Claude Code. Manage deployments, builds, logs, domains, and frontend infrastructure directly.

### [TypeScript LSP](https://www.claude.com/marketplace/plugins/typescript-lsp)

Anthropic verified

TypeScript/JavaScript language server for enhanced code intelligence

### [Ralph Loop](https://www.claude.com/marketplace/plugins/ralph-loop)

Anthropic verified

Interactive AI loops for iterative development using the Ralph Wiggum technique: Claude works on tasks repeatedly, seeing prior work until completion.

### [Claude Code Setup](https://www.claude.com/marketplace/plugins/claude-code-setup)

Anthropic verified

Analyze codebases and recommend tailored Claude Code automations such as hooks, skills, MCP servers, and subagents.

### [Commit Commands](https://www.claude.com/marketplace/plugins/commit-commands)

Anthropic verified

Commands for git commit workflows including commit, push, and PR creation

### [Figma](https://www.claude.com/marketplace/plugins/figma)

Figma integration: access design files, extract components, read tokens, translate to code. Bridge design-development gap.

### [Supabase](https://www.claude.com/marketplace/plugins/supabase)

Supabase MCP: Database ops, auth, storage, real-time. Manage projects, run SQL, interact with your backend.

### [PR Review Toolkit](https://www.claude.com/marketplace/plugins/pr-review-toolkit)

Anthropic verified

PR review agents for comments, tests, errors, types, quality, and simplification

### [Pyright LSP](https://www.claude.com/marketplace/plugins/pyright-lsp)

Anthropic verified

Python language server (Pyright) for type checking and code intelligence

### [Chrome DevTools](https://www.claude.com/marketplace/plugins/chrome-devtools-mcp)

Control and inspect a live Chrome browser from your coding agent. Record performance traces, analyze network requests...

### [telegram](https://www.claude.com/marketplace/plugins/telegram)

Telegram messaging bridge with built-in access control. Manage pairing, allowlists, and policy via /telegram:access.

### [Atlassian](https://www.claude.com/marketplace/plugins/atlassian)

Connect to Jira & Confluence. Search/create issues, access docs, manage sprints, integrate development workflows.

### [Slack](https://www.claude.com/marketplace/plugins/slack)

Anthropic verified

Official Slack MCP server for interactive and collaborative workflows. Surface insights, draft messages, and engage teams directly within Slack from Claude Cowork.

### [Serena](https://www.claude.com/marketplace/plugins/serena)

Semantic code analysis MCP server for intelligent code understanding, refactoring, and navigation via language server protocol.

View more

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Skills Cookbook - Claude Code Guide

Source: https://github.com/anthropics/claude-cookbooks/blob/main/skills/CLAUDE.md
Path: 08-Plugins-Skills/claude-cookbooks-skills-claude.md

# Skills Cookbook - Claude Code Guide

## Project Overview

This is a comprehensive Jupyter notebook cookbook demonstrating Claude's Skills feature for document generation (Excel, PowerPoint, PDF). It's designed for developers learning to integrate Skills into their applications.

## Quick Start Commands

### Environment Setup
```bash
# Create and activate virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies (MUST use local whl for Skills support)
pip install -r requirements.txt

# Configure API key
cp .env.example .env
# Edit .env and add your ANTHROPIC_API_KEY
```

### Running Notebooks
```bash
# Launch Jupyter
jupyter notebook

# Or use VSCode with Jupyter extension
# Make sure to select the venv kernel in VSCode: Cmd+Shift+P → "Python: Select Interpreter"
```

### Testing & Verification
```bash
# Verify environment and SDK version
python -c "import anthropic; print(f'SDK Version: {anthropic.__version__}')"

# Check outputs directory for generated files
ls -lh outputs/
```

## Architecture Overview

### Directory Structure
```
skills/
├── notebooks/              # 3 progressive Jupyter notebooks
│   ├── 01_skills_introduction.ipynb
│   ├── 02_skills_financial_applications.ipynb  # WIP
│   └── 03_skills_custom_development.ipynb      # WIP
├── sample_data/           # Financial datasets for examples
├── custom_skills/         # Custom skill development area
├── outputs/               # Generated files (xlsx, pptx, pdf)
├── file_utils.py          # Files API helper functions
└── docs/                  # Implementation tracking
```

### Key Technical Details

**Beta API Requirements:**
- All Skills functionality uses `client.beta.*` namespace
- Required beta headers: `code-execution-2025-08-25`, `files-api-2025-04-14`, `skills-2025-10-02`
- Must use `client.beta.messages.create()` with `container` parameter
- Code execution tool (`code_execution_20250825`) is REQUIRED
- Use pre-built Agent skills by referencing their `skill_id` or create and upload your own via the Skills API

**Files API Integration:**
- Skills generate files and return `file_id` attributes
- Must use `client.beta.files.download()` to download files
- Must use `client.beta.files.retrieve_metadata()` to get file info
- Helper functions in `file_utils.py` handle extraction and download

**Built-in Skills:**
- `xlsx` - Excel workbooks with formulas and charts
- `pptx` - PowerPoint presentations
- `pdf` - PDF documents
- `docx` - Word documents

## Development Gotchas

### 1. SDK Version
**Important**: Ensure you have the Anthropic SDK version 0.71.0 or later with Skills support
```bash
pip install anthropic>=0.71.0
# Restart Jupyter kernel after installation if upgrading!
```

### 2. Beta Namespace Required
**Problem**: `container` parameter not recognized, files API fails
**Solution**: Use `client.beta.messages.create()` and `client.beta.files.*`
```python
# ❌ Wrong
response = client.messages.create(container={...})
content = client.files.content(file_id)

# ✅ Correct
response = client.beta.messages.create(container={...})
content = client.beta.files.content(file_id)
```

### 3. Beta Headers Placement
**Problem**: Setting Skills beta in default_headers requires code_execution on ALL requests
**Solution**: Use `betas` parameter per-request instead
```python
# ❌ Wrong (affects all requests)
client = Anthropic(default_headers={"anthropic-beta": "skills-2025-10-02"})

# ✅ Correct (per-request)
response = client.beta.messages.create(
    betas=["code-execution-2025-08-25", "files-api-2025-04-14", "skills-2025-10-02"],
    ...
)
```

### 4. File ID Extraction
**Problem**: Response structure differs from standard Messages API
**Solution**: File IDs in `bash_code_execution_tool_result.content.content[0].file_id`
```python
# Use file_utils.extract_file_ids() - handles beta response structure
from file_utils import extract_file_ids, download_all_files
file_ids = extract_file_ids(response)
```

### 5. Files API Response Objects
**Problem**: `'BinaryAPIResponse' object has no attribute 'content'`, `'FileMetadata' object has no attribute 'size'`
**Solution**: Use `.read()` for file content and `.size_bytes` for file size
```python
# ❌ Wrong
file_content = client.beta.files.download(file_id)
with open(path, 'wb') as f:
    f.write(file_content.content)  # No .content attribute!

# ✅ Correct
file_content = client.beta.files.download(file_id)
with open(path, 'wb') as f:
    f.write(file_content.read())  # Use .read()

# FileMetadata fields: id, filename, size_bytes (not size), mime_type, created_at, type, downloadable
metadata = client.beta.files.retrieve_metadata(file_id)
print(f"Size: {metadata.size_bytes} bytes")  # Use size_bytes, not size
```

### 6. Jupyter Kernel Selection
**Problem**: Wrong Python interpreter = wrong dependencies
**Solution**: Always select venv kernel in VSCode/Jupyter
- VSCode: Cmd+Shift+P → "Python: Select Interpreter" → select venv
- Jupyter: Kernel → Change Kernel → select venv

### 7. Module Reload Required
**Problem**: Changes to `file_utils.py` not reflected in running notebooks
**Solution**: Restart kernel or reload module
```python
import importlib
importlib.reload(file_utils)
```

### 8. Document Generation Times
**Problem**: File creation takes longer than typical API calls, users may think cell is frozen
**Actual Observed Times:**
- Excel: ~2 minutes
- PowerPoint: ~1-2 minutes (simple 2-3 slide presentations)
- PDF: ~1-2 minutes

**Solution**: Add clear timing expectations before file creation cells
```markdown
**⏱️ Note**: Excel generation typically takes 1-2 minutes.
Be patient - the cell will show [*] while running!
```
**Important**: Keep examples simple and focused. Generation times are consistent at 1-2 minutes for well-scoped examples.

## Common Tasks

### Adding a New Notebook Section
1. Follow existing structure in `01_skills_introduction.ipynb`
2. Include setup cell with imports and beta headers
3. Show API call, response handling, file download
4. Add error handling examples
5. Update `docs/skills_cookbook_plan.md` checklist

### Creating Sample Data
1. Add realistic financial data to `sample_data/`
2. Use CSV for tabular, JSON for structured
3. Include headers and proper formatting
4. Reference in notebook with pandas
5. Keep file sizes reasonable (<100KB)

### Testing File Download
1. Run notebook cell to generate file
2. Check response for file_id
3. Use `download_all_files()` helper
4. Verify file in `outputs/` directory
5. Open file in native app to validate

**Note**: Files are overwritten by default. You'll see `[overwritten]` in the download summary when a file already existed. Set `overwrite=False` to prevent this.

### Debugging API Errors
1. Check SDK version: `anthropic.__version__` should be `0.69.0`
2. Verify beta headers are passed per-request
3. Ensure code_execution tool is included
4. Check response structure with `print(response)`
5. Look for error details in `response.stop_reason`

## Testing Checklist

Before committing notebook changes:
- [ ] Restart kernel and run all cells
- [ ] Verify all file downloads work
- [ ] Check outputs/ for generated files
- [ ] Validate files open correctly in native apps
- [ ] Update skills_cookbook_plan.md checklist
- [ ] Test in fresh virtual environment

## Resources

- **API Reference**: https://docs.claude.com/en/api/messages
- **Files API**: https://docs.claude.com/en/api/files-content
- **Skills Documentation**: https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview

## Project-Specific Notes

- **Focus Domain**: Finance & Analytics with practical business applications
- **Target Audience**: Intermediate developers and business analysts
- **Notebook 1**: Complete and tested (file downloads working)
- **Notebook 2**: Financial Applications - next priority
- **Notebook 3**: Custom Skills Development - after Notebook 2

## Environment Variables

Required in `.env`:
```bash
ANTHROPIC_API_KEY=your-api-key-here
```

Optional (for advanced examples):
```bash
ANTHROPIC_BASE_URL=https://api.anthropic.com  # If using proxy
```

---

## Financial Modeling Suite

Source: https://github.com/anthropics/claude-cookbooks/blob/main/skills/custom_skills/creating-financial-models/SKILL.md
Path: 08-Plugins-Skills/claude-cookbooks-skills-custom-skills-creating-financial-models-skill.md

# Financial Modeling Suite

A comprehensive financial modeling toolkit for investment analysis, valuation, and risk assessment using industry-standard methodologies.

## Core Capabilities

### 1. Discounted Cash Flow (DCF) Analysis
- Build complete DCF models with multiple growth scenarios
- Calculate terminal values using perpetuity growth and exit multiple methods
- Determine weighted average cost of capital (WACC)
- Generate enterprise and equity valuations

### 2. Sensitivity Analysis
- Test key assumptions impact on valuation
- Create data tables for multiple variables
- Generate tornado charts for sensitivity ranking
- Identify critical value drivers

### 3. Monte Carlo Simulation
- Run thousands of scenarios with probability distributions
- Model uncertainty in key inputs
- Generate confidence intervals for valuations
- Calculate probability of achieving targets

### 4. Scenario Planning
- Build best/base/worst case scenarios
- Model different economic environments
- Test strategic alternatives
- Compare outcome probabilities

## Input Requirements

### For DCF Analysis
- Historical financial statements (3-5 years)
- Revenue growth assumptions
- Operating margin projections
- Capital expenditure forecasts
- Working capital requirements
- Terminal growth rate or exit multiple
- Discount rate components (risk-free rate, beta, market premium)

### For Sensitivity Analysis
- Base case model
- Variable ranges to test
- Key metrics to track

### For Monte Carlo Simulation
- Probability distributions for uncertain variables
- Correlation assumptions between variables
- Number of iterations (typically 1,000-10,000)

### For Scenario Planning
- Scenario definitions and assumptions
- Probability weights for scenarios
- Key performance indicators to track

## Output Formats

### DCF Model Output
- Complete financial projections
- Free cash flow calculations
- Terminal value computation
- Enterprise and equity value summary
- Valuation multiples implied
- Excel workbook with full model

### Sensitivity Analysis Output
- Sensitivity tables showing value ranges
- Tornado chart of key drivers
- Break-even analysis
- Charts showing relationships

### Monte Carlo Output
- Probability distribution of valuations
- Confidence intervals (e.g., 90%, 95%)
- Statistical summary (mean, median, std dev)
- Risk metrics (VaR, probability of loss)

### Scenario Planning Output
- Scenario comparison table
- Probability-weighted expected values
- Decision tree visualization
- Risk-return profiles

## Model Types Supported

1. **Corporate Valuation**
   - Mature companies with stable cash flows
   - Growth companies with J-curve projections
   - Turnaround situations

2. **Project Finance**
   - Infrastructure projects
   - Real estate developments
   - Energy projects

3. **M&A Analysis**
   - Acquisition valuations
   - Synergy modeling
   - Accretion/dilution analysis

4. **LBO Models**
   - Leveraged buyout analysis
   - Returns analysis (IRR, MOIC)
   - Debt capacity assessment

## Best Practices Applied

### Modeling Standards
- Consistent formatting and structure
- Clear assumption documentation
- Separation of inputs, calculations, outputs
- Error checking and validation
- Version control and change tracking

### Valuation Principles
- Use multiple valuation methods for triangulation
- Apply appropriate risk adjustments
- Consider market comparables
- Validate against trading multiples
- Document key assumptions clearly

### Risk Management
- Identify and quantify key risks
- Use probability-weighted scenarios
- Stress test extreme cases
- Consider correlation effects
- Provide confidence intervals

## Example Usage

"Build a DCF model for this technology company using the attached financials"

"Run a Monte Carlo simulation on this acquisition model with 5,000 iterations"

"Create sensitivity analysis showing impact of growth rate and WACC on valuation"

"Develop three scenarios for this expansion project with probability weights"

## Scripts Included

- `dcf_model.py`: Complete DCF valuation engine
- `sensitivity_analysis.py`: Sensitivity testing framework

## Limitations and Disclaimers

- Models are only as good as their assumptions
- Past performance doesn't guarantee future results
- Market conditions can change rapidly
- Regulatory and tax changes may impact results
- Professional judgment required for interpretation
- Not a substitute for professional financial advice

## Quality Checks

The model automatically performs:
1. Balance sheet balancing checks
2. Cash flow reconciliation
3. Circular reference resolution
4. Sensitivity bound checking
5. Statistical validation of Monte Carlo results

## Updates and Maintenance

- Models use latest financial theory and practices
- Regular updates for market parameter defaults
- Incorporation of regulatory changes
- Continuous improvement based on usage patterns

---

## Claude Skills for Financial Applications

Source: https://github.com/anthropics/claude-cookbooks/blob/main/skills/notebooks/02_skills_financial_applications.ipynb
Path: 08-Plugins-Skills/claude-cookbooks-skills-notebooks-02-skills-financial-applications.md

# Claude Skills for Financial Applications

Build real-world financial dashboards, portfolio analytics, and
automated reporting workflows using Claude\'s Excel, PowerPoint, and PDF
skills.

> **💡 Real-world Impact:** These are the same Skills that power
> **[Claude Creates
> Files](https://github.com/anthropics/claude-cookbooks/blob/main/skills/19-Reference/claude-can-now-create-and-edit-files-claude.md)**, enabling Claude
> to create professional financial documents directly in the interface.

**What you\'ll learn:**

- Create comprehensive financial models in Excel with formulas and
  charts
- Generate executive presentations from financial data
- Build portfolio analysis tools with risk metrics
- Automate multi-format reporting pipelines
:::

::: {.cell .markdown}
## Table of Contents

1.  [Setup & Data Loading](#setup)
2.  [Use Case 1: Financial Dashboard Creation](#financial-dashboard)
    - [Excel Financial Model](#excel-model)
    - [Executive PowerPoint](#executive-ppt)
    - [PDF Financial Report](#pdf-report)
3.  [Use Case 2: Portfolio Analysis Workflow](#portfolio-analysis)
    - [Portfolio Analytics Excel](#portfolio-excel)
    - [Investment Committee Deck](#investment-deck)
4.  [Use Case 3: Automated Reporting Pipeline](#reporting-pipeline)
:::

::: {.cell .markdown}
## Prerequisites

This notebook assumes you\'ve completed **Notebook 1: Introduction to
Skills**.

If you haven\'t:

1.  Complete the setup in Notebook 1 first
2.  Verify your environment with the test cells
3.  Ensure you can create and download files

**Required:**

- Anthropic API key configured
- SDK version 0.69.0 installed from whl
- Virtual environment activated
:::

::: {.cell .markdown}
## 1. Setup & Data Loading {#setup} {#1-setup--data-loading-setup}

Let\'s start by importing our dependencies and loading the financial
data we\'ll work with throughout this notebook.
:::

::: {.cell .code}
``` python
# Standard imports
import json
import os
import sys
from pathlib import Path

import pandas as pd

# Add parent directory for imports
sys.path.insert(0, str(Path.cwd().parent))

# Anthropic SDK
from anthropic import Anthropic
from dotenv import load_dotenv

# Our utilities
from file_utils import (
    download_all_files,
    print_download_summary,
)

# Load environment
load_dotenv(Path.cwd().parent / ".env")

# Configuration
API_KEY = os.getenv("ANTHROPIC_API_KEY")
MODEL = "claude-sonnet-4-5"

if not API_KEY:
    raise ValueError("ANTHROPIC_API_KEY not found. Please configure your .env file.")

# Initialize client
client = Anthropic(api_key=API_KEY)

# Setup directories
OUTPUT_DIR = Path.cwd().parent / "outputs" / "financial"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

DATA_DIR = Path.cwd().parent / "sample_data"

print("✓ Environment configured")
print(f"✓ Output directory: {OUTPUT_DIR}")
print(f"✓ Data directory: {DATA_DIR}")
```
:::

::: {.cell .markdown}
### Load Financial Data

We have four datasets representing different aspects of a company\'s
financial position:
:::

::: {.cell .code}
``` python
# Load financial statements
financial_statements = pd.read_csv(DATA_DIR / "financial_statements.csv")
print("📊 Financial Statements Overview:")
print(f"   Shape: {financial_statements.shape}")
print(f"   Categories: {len(financial_statements['Category'].unique())} financial metrics")
print(f"   Quarters: {list(financial_statements.columns[1:5])}")
print()

# Show sample data
print("Sample data (first 5 rows):")
financial_statements.head()
```
:::

::: {.cell .code}
``` python
# Load portfolio holdings
with open(DATA_DIR / "portfolio_holdings.json") as f:
    portfolio_data = json.load(f)

print("💼 Portfolio Overview:")
print(f"   Portfolio: {portfolio_data['portfolio_name']}")
print(f"   Total Value: ${portfolio_data['total_value']:,.2f}")
print(f"   Holdings: {len(portfolio_data['holdings'])} stocks")
print(f"   Cash Position: ${portfolio_data['cash_position']['amount']:,.2f}")
print(f"   Total Return: {portfolio_data['performance_metrics']['total_return_percent']:.1f}%")
print()

# Convert holdings to DataFrame for easier manipulation
portfolio_df = pd.DataFrame(portfolio_data["holdings"])
print("Top 5 holdings by value:")
portfolio_df.nlargest(5, "market_value")[["ticker", "name", "market_value", "unrealized_gain"]]
```
:::

::: {.cell .code}
``` python
# Load quarterly metrics
with open(DATA_DIR / "quarterly_metrics.json") as f:
    quarterly_metrics = json.load(f)

print("📈 Quarterly Metrics Overview:")
print(f"   Quarters available: {len(quarterly_metrics['quarters'])}")
print(f"   Metrics per quarter: {len(quarterly_metrics['quarters'][0])} KPIs")
print()

# Show latest quarter metrics
latest_quarter = quarterly_metrics["quarters"][-1]
print(f"Latest Quarter ({latest_quarter['quarter']}):")
for key, value in latest_quarter.items():
    if key != "quarter" and isinstance(value, int | float):
        if "revenue" in key.lower() or "cost" in key.lower():
            print(f"   {key.replace('_', ' ').title()}: ${value:,.0f}")
        elif "percent" in key.lower() or "margin" in key.lower() or "rate" in key.lower():
            print(f"   {key.replace('_', ' ').title()}: {value:.1f}%")
        else:
            print(f"   {key.replace('_', ' ').title()}: {value:,.0f}")
```
:::

::: {.cell .markdown}
### Helper Functions

Let\'s define some helper functions for this notebook:
:::

::: {.cell .code}
``` python
def create_skills_message(client, prompt, skills, prefix="", show_token_usage=True):
    """
    Helper function to create messages with Skills.

    Args:
        client: Anthropic client
        prompt: User prompt
        skills: List of skill dicts [{"type": "anthropic", "skill_id": "xlsx", "version": "latest"}]
        prefix: Prefix for downloaded files
        show_token_usage: Whether to print token usage

    Returns:
        Tuple of (response, download_results)
    """
    response = client.beta.messages.create(
        model=MODEL,
        max_tokens=4096,
        container={"skills": skills},
        tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
        messages=[{"role": "user", "content": prompt}],
        betas=[
            "code-execution-2025-08-25",
            "files-api-2025-04-14",
            "skills-2025-10-02",
        ],
    )

    if show_token_usage:
        print(
            f"\n📊 Token Usage: {response.usage.input_tokens} in, {response.usage.output_tokens} out"
        )

    # Download files
    results = download_all_files(client, response, output_dir=str(OUTPUT_DIR), prefix=prefix)

    return response, results


def format_financial_value(value, is_currency=True, decimals=0):
    """Format financial values for display."""
    if is_currency:
        return f"${value:,.{decimals}f}"
    else:
        return f"{value:,.{decimals}f}"


print("✓ Helper functions defined")
```
:::

::: {.cell .markdown}
## 2. Use Case 1: Financial Dashboard Creation {#financial-dashboard} {#2-use-case-1-financial-dashboard-creation-financial-dashboard}

Now that we have our data loaded and helper functions defined, let\'s
dive into our first practical use case: creating comprehensive financial
dashboards. We\'ll start by generating multi-sheet Excel workbooks that
automatically include formulas, formatting, and charts.

### 2.1 Excel Financial Model {#excel-model} {#21-excel-financial-model-excel-model}

We\'ll create a financial dashboard that includes:

- Profit & Loss statements with year-over-year comparisons
- Balance sheet analysis
- Cash flow tracking
- KPI dashboards with visualizations

This demonstrates how Claude\'s Skills can handle complex Excel
generation tasks that would typically require hours of manual work.
:::

::: {.cell .code}
``` python
# Create Financial Dashboard Excel
print("Creating financial dashboard Excel file...")
print("This creates a 2-sheet dashboard optimized for the Skills API.")
print("\n⏱️ Generation time: 1-2 minutes\n")

# Prepare the financial data
fs_data = financial_statements.to_dict("records")
quarters_2024 = ["Q1_2024", "Q2_2024", "Q3_2024", "Q4_2024"]

# Extract key financial metrics
revenue_by_quarter = {
    "Q1 2024": financial_statements[financial_statements["Category"] == "Revenue"][
        "Q1_2024"
    ].values[0],
    "Q2 2024": financial_statements[financial_statements["Category"] == "Revenue"][
        "Q2_2024"
    ].values[0],
    "Q3 2024": financial_statements[financial_statements["Category"] == "Revenue"][
        "Q3_2024"
    ].values[0],
    "Q4 2024": financial_statements[financial_statements["Category"] == "Revenue"][
        "Q4_2024"
    ].values[0],
}

financial_dashboard_prompt = f"""
Create a financial dashboard Excel workbook with 2 sheets:

Sheet 1 - "P&L Summary":
Create a Profit & Loss summary table for 2024 quarters with these rows:
- Revenue: {", ".join([f"Q{i + 1}: ${v / 1000000:.1f}M" for i, v in enumerate(revenue_by_quarter.values())])}
- Gross Profit: Use values from the data
- Operating Income: Use values from the data
- Net Income: Use values from the data
- Add a Total column with SUM formulas
- Add a row showing profit margins (Net Income / Revenue)
- Apply currency formatting and bold headers
- Add a simple bar chart showing quarterly revenue

Sheet 2 - "Key Metrics":
Create a metrics dashboard with:
- Total Revenue 2024: SUM of all quarters
- Average Quarterly Revenue: AVERAGE formula
- Q4 vs Q1 Growth: Percentage increase
- Best Quarter: MAX formula to identify
- Operating Margin Q4: Calculate from data
- Year-over-year growth vs 2023

Apply professional formatting with borders, bold headers, and currency formats.
"""

# Create the Excel financial dashboard
excel_response, excel_results = create_skills_message(
    client,
    financial_dashboard_prompt,
    [{"type": "anthropic", "skill_id": "xlsx", "version": "latest"}],
    prefix="financial_dashboard_",
)

print("\n" + "=" * 60)
print_download_summary(excel_results)

if len(excel_results) > 0 and excel_results[0]["success"]:
    print("\n✅ Financial dashboard Excel created successfully!")
```
:::

::: {.cell .markdown}
### 💡 Best Practices for Excel Generation {#bulb-best-practices-for-excel-generation}

Based on our testing, here are the optimal approaches for creating Excel
files with Skills:

**Recommended Approach:**

- **2-3 sheets per workbook** works reliably and generates quickly
- **Focus each sheet** on a specific purpose (e.g., P&L, metrics,
  charts)
- **Add complexity incrementally** - start simple, then enhance

**For Complex Dashboards:**

1.  **Create multiple focused files** instead of one complex file
    - Example: `financial_pnl.xlsx`, `balance_sheet.xlsx`,
      `kpi_dashboard.xlsx`
2.  **Use the pipeline pattern** to create and enhance files
    sequentially
3.  **Combine files programmatically** using pandas or openpyxl if
    needed

**Performance Tips:**

- Simple 2-sheet dashboards: \~1-2 minutes
- PowerPoint and PDF generation: Very reliable for complex content
- Token usage: Structured data (JSON/CSV) is more efficient than prose
:::

::: {.cell .markdown}
### 2.2 Executive PowerPoint {#executive-ppt} {#22-executive-powerpoint-executive-ppt}

With our financial data now organized in Excel, let\'s create an
executive presentation that summarizes the key insights. This
demonstrates how Skills can generate professional PowerPoint
presentations with charts, formatted text, and multiple slides - perfect
for board meetings or investor updates.

The presentation will include:

- Q4 2024 performance highlights
- Financial metrics with year-over-year comparisons
- Profitability trends with visualizations
- Key takeaways and outlook
:::

::: {.cell .code}
``` python
print("Creating executive presentation from financial metrics...")
print("\n⏱️ Generation time: 1-2 minutes\n")

# Calculate some key metrics for the presentation
q4_2024_revenue = 14500000
q4_2023_revenue = 12300000
yoy_growth = (q4_2024_revenue - q4_2023_revenue) / q4_2023_revenue * 100

q4_2024_net_income = 1878750
q4_2023_net_income = 1209000
net_income_growth = (q4_2024_net_income - q4_2023_net_income) / q4_2023_net_income * 100

executive_ppt_prompt = f"""
Create a 4-slide executive presentation for Q4 2024 financial results:

Slide 1 - Title:
- Title: "Q4 2024 Financial Results"
- Subtitle: "Executive Summary - Acme Corporation"
- Date: January 2025

Slide 2 - Financial Highlights:
- Title: "Q4 2024 Performance Highlights"
- Create a two-column layout:
  Left side - Key Metrics:
  • Revenue: $14.5M (+{yoy_growth:.1f}% YoY)
  • Net Income: $1.88M (+{net_income_growth:.1f}% YoY)
  • Operating Margin: 17.9% (up 2.9pp)
  • Operating Cash Flow: $2.85M

  Right side - Column chart showing quarterly revenue:
  Q1 2024: $12.5M
  Q2 2024: $13.2M
  Q3 2024: $13.8M
  Q4 2024: $14.5M

Slide 3 - Profitability Trends:
- Title: "Margin Expansion & Profitability"
- Add a line chart showing net margin % by quarter:
  Q1 2024: 11.4%
  Q2 2024: 11.8%
  Q3 2024: 12.4%
  Q4 2024: 13.0%
- Add bullet points below:
  • Consistent margin expansion throughout 2024
  • Operating leverage driving profitability
  • Cost optimization initiatives delivering results

Slide 4 - Key Takeaways:
- Title: "Key Takeaways & Outlook"
- Bullet points:
  ✓ Record Q4 revenue of $14.5M
  ✓ 17.9% YoY revenue growth
  ✓ 55% increase in net income YoY
  ✓ Strong cash generation: $2.85M operating cash flow
  ✓ Well-positioned for continued growth in 2025

Use professional corporate design:
- Dark blue (#003366) for headers
- Clean, modern layout
- Data-driven visualizations
"""

# Create the executive presentation
ppt_response, ppt_results = create_skills_message(
    client,
    executive_ppt_prompt,
    [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}],
    prefix="executive_summary_",
)

print("\n" + "=" * 60)
print_download_summary(ppt_results)

if len(ppt_results) > 0 and ppt_results[0]["success"]:
    print("\n✅ Executive presentation created successfully!")
```
:::

::: {.cell .markdown}
## 3. Use Case 2: Portfolio Analysis Workflow {#portfolio-analysis} {#3-use-case-2-portfolio-analysis-workflow-portfolio-analysis}

Now let\'s shift our focus from company financials to investment
portfolio analysis. In this section, we\'ll demonstrate how to create
comprehensive portfolio analytics and investment committee presentations
using the portfolio data we loaded earlier.

This workflow showcases:

- Detailed portfolio performance analysis in Excel
- Risk metrics and sector allocation visualization
- Professional investment committee presentations
- Data-driven rebalancing recommendations

We\'ll start by creating an Excel workbook with portfolio analytics,
then generate an investment committee presentation that summarizes our
findings.
:::

::: {.cell .markdown}
### First, let\'s create a comprehensive portfolio analysis Excel workbook

Before we create the investment committee presentation, we need to
analyze our portfolio data in detail. This Excel workbook will serve as
the foundation for our investment recommendations.
:::

::: {.cell .code}
``` python
print("Creating portfolio analysis Excel workbook...")
print("This creates a focused 2-sheet portfolio analysis optimized for the Skills API.")
print("\n⏱️ Generation time: 1-2 minutes\n")

# Prepare portfolio data for the prompt
top_holdings = portfolio_df.nlargest(5, "market_value")
sector_allocation = portfolio_data["sector_allocation"]

portfolio_excel_prompt = f"""
Create a portfolio analysis Excel workbook with 2 sheets:

Sheet 1 - "Portfolio Overview":
Create a comprehensive holdings and performance table:

Section 1 - Holdings (top of sheet):
{portfolio_df[["ticker", "name", "shares", "current_price", "market_value", "unrealized_gain", "allocation_percent"]].head(10).to_string()}

Section 2 - Portfolio Summary:
- Total portfolio value: ${portfolio_data["total_value"]:,.2f}
- Total unrealized gain: ${portfolio_df["unrealized_gain"].sum():,.2f}
- Total Return: {portfolio_data["performance_metrics"]["total_return_percent"]:.1f}%
- YTD Return: {portfolio_data["performance_metrics"]["year_to_date_return"]:.1f}%
- Sharpe Ratio: {portfolio_data["performance_metrics"]["sharpe_ratio"]:.2f}
- Portfolio Beta: {portfolio_data["performance_metrics"]["beta"]:.2f}

Apply conditional formatting: green for gains, red for losses.
Add a bar chart showing top 5 holdings by value.

Sheet 2 - "Sector Analysis & Risk":
Create sector allocation and risk metrics:

Section 1 - Sector Allocation:
{json.dumps(sector_allocation, indent=2)}
Include a pie chart of sector allocation.

Section 2 - Key Risk Metrics:
- Portfolio Beta: {portfolio_data["performance_metrics"]["beta"]:.2f}
- Standard Deviation: {portfolio_data["performance_metrics"]["standard_deviation"]:.1f}%
- Value at Risk (95%): $62,500
- Maximum Drawdown: -12.3%
- Sharpe Ratio: {portfolio_data["performance_metrics"]["sharpe_ratio"]:.2f}

Section 3 - Rebalancing Recommendations:
- Reduce Technology from 20% to 18%
- Increase Healthcare from 8.7% to 10%
- Maintain current diversification

Apply professional formatting with clear sections and headers.
"""

# Create portfolio analysis Excel
portfolio_response, portfolio_results = create_skills_message(
    client,
    portfolio_excel_prompt,
    [{"type": "anthropic", "skill_id": "xlsx", "version": "latest"}],
    prefix="portfolio_analysis_",
)

print("\n" + "=" * 60)
print_download_summary(portfolio_results)

if len(portfolio_results) > 0 and portfolio_results[0]["success"]:
    print("\n✅ Portfolio analysis Excel created successfully!")
```
:::

::: {.cell .markdown}
### 3.2 Investment Committee Presentation {#investment-deck} {#32-investment-committee-presentation-investment-deck}

With our detailed portfolio analysis complete, let\'s now create a
professional presentation for the investment committee. This
presentation will distill the key insights from our Excel analysis into
a concise, visual format suitable for decision-makers.

The presentation will cover:

- Portfolio performance summary with key metrics
- Asset allocation and diversification analysis
- Risk metrics and risk-adjusted returns
- Strategic recommendations for rebalancing
:::

::: {.cell .code}
``` python
print("Creating investment committee presentation...")
print("\n⏱️ Generation time: 1-2 minutes\n")

investment_deck_prompt = f"""
Create a 5-slide investment committee presentation:

Slide 1 - Title:
- Title: "Portfolio Review - Q4 2024"
- Subtitle: "{portfolio_data["portfolio_name"]}"
- Date: January 2025
- Portfolio Value: ${portfolio_data["total_value"]:,.0f}

Slide 2 - Portfolio Overview:
- Title: "Portfolio Performance Summary"
- Two-column layout:

  Left - Key Metrics:
  • Total Value: ${portfolio_data["total_value"]:,.0f}
  • YTD Return: +{portfolio_data["performance_metrics"]["year_to_date_return"]:.1f}%
  • Total Return: ${portfolio_data["performance_metrics"]["total_return"]:,.0f}
  • Sharpe Ratio: {portfolio_data["performance_metrics"]["sharpe_ratio"]:.2f}

  Right - Bar chart of top 5 holdings by value:
  {", ".join([f"{h['ticker']}: ${h['market_value']:,.0f}" for h in top_holdings.to_dict("records")])}

Slide 3 - Sector Allocation:
- Title: "Asset Allocation & Diversification"
- Pie chart showing:
  Technology: {sector_allocation["Technology"]:.1f}%
  Financials: {sector_allocation["Financials"]:.1f}%
  Healthcare: {sector_allocation["Healthcare"]:.1f}%
  Consumer: {sector_allocation["Consumer Discretionary"] + sector_allocation["Consumer Staples"]:.1f}%
  Fixed Income: {sector_allocation["Bonds"]:.1f}%
  Cash: {sector_allocation["Cash"]:.1f}%

Slide 4 - Risk Analysis:
- Title: "Risk Metrics & Analysis"
- Content:
  Risk Indicators:
  • Portfolio Beta: {portfolio_data["performance_metrics"]["beta"]:.2f} (lower market risk)
  • Standard Deviation: {portfolio_data["performance_metrics"]["standard_deviation"]:.1f}%
  • Maximum Drawdown: -12.3%
  • Value at Risk (95%): $62,500

  Risk-Adjusted Performance:
  • Sharpe Ratio: {portfolio_data["performance_metrics"]["sharpe_ratio"]:.2f} (excellent)
  • Alpha Generation: +2.3% vs benchmark

Slide 5 - Recommendations:
- Title: "Strategic Recommendations"
- Bullet points:
  ✓ Maintain current allocation - well diversified
  ✓ Consider profit-taking in Technology (20% → 18%)
  ✓ Increase Healthcare allocation (8.7% → 10%)
  ✓ Monitor bond duration given rate environment
  ✓ Rebalance quarterly to maintain targets

Use professional investment presentation design.
"""

# Create investment committee deck
investment_response, investment_results = create_skills_message(
    client,
    investment_deck_prompt,
    [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}],
    prefix="investment_committee_",
)

print("\n" + "=" * 60)
print_download_summary(investment_results)
print("\n✅ Investment committee presentation created successfully!")
```
:::

::: {.cell .markdown}
## 4. Use Case 3: Automated Reporting Pipeline {#reporting-pipeline} {#4-use-case-3-automated-reporting-pipeline-reporting-pipeline}

So far, we\'ve created individual documents for specific purposes. Now
let\'s demonstrate the power of chaining multiple Skills together in an
automated workflow. This pipeline pattern is essential for production
systems where you need to generate multiple related documents from the
same data source.

In this example, we\'ll create a complete reporting suite that:

1.  **Analyzes data** in Excel with calculations and charts
2.  **Summarizes insights** in a PowerPoint presentation
3.  **Documents the process** in a formal PDF report

This showcases how Skills can work together to create a comprehensive
reporting solution that would traditionally require multiple tools and
manual coordination.

**Key benefits of the pipeline approach:**

- Consistent data across all documents
- Reduced total generation time
- Token usage optimization
- Scalable to multiple report types

**⏱️ Total expected time:** 2-3 minutes for the complete pipeline
:::

::: {.cell .code}
``` python
print("🔄 Starting Automated Reporting Pipeline")
print("=" * 60)
print("This will create a complete reporting suite:")
print("1. Excel analysis → 2. PowerPoint summary → 3. PDF documentation")
print("\n⏱️ Total pipeline time: 2-3 minutes\n")

# Track token usage across the pipeline
pipeline_tokens = {"input": 0, "output": 0}

# Step 1: Create Excel Analysis
print("Step 1/3: Creating Excel analysis with quarterly metrics...")

excel_pipeline_prompt = f"""
Create a quarterly business metrics Excel file:

Sheet 1 - "Quarterly KPIs":
Create a table with these quarterly metrics for 2024:
{
    json.dumps(
        [
            {
                k: v
                for k, v in q.items()
                if k in ["quarter", "revenue", "gross_margin", "customer_count", "churn_rate"]
            }
            for q in quarterly_metrics["quarters"]
        ],
        indent=2,
    )
}

Add:
- Quarter-over-quarter growth calculations
- Average and total rows
- Conditional formatting for trends
- Line chart showing revenue trend
- Column chart showing customer count

Sheet 2 - "YoY Comparison":
Compare Q4 2024 vs Q4 2023 for all metrics.
Calculate percentage changes and highlight improvements.

Professional formatting with headers and borders.
"""

excel_response, excel_results = create_skills_message(
    client,
    excel_pipeline_prompt,
    [{"type": "anthropic", "skill_id": "xlsx", "version": "latest"}],
    prefix="pipeline_1_metrics_",
    show_token_usage=False,
)

pipeline_tokens["input"] += excel_response.usage.input_tokens
pipeline_tokens["output"] += excel_response.usage.output_tokens
print(
    f"✓ Excel created - Tokens: {excel_response.usage.input_tokens} in, {excel_response.usage.output_tokens} out"
)

# Step 2: Create PowerPoint Summary
print("\nStep 2/3: Creating PowerPoint summary from metrics...")

ppt_pipeline_prompt = """
Create a 3-slide quarterly metrics summary presentation:

Slide 1:
- Title: "Q4 2024 Metrics Summary"
- Subtitle: "Automated Reporting Pipeline Demo"

Slide 2:
- Title: "Key Performance Indicators"
- Show Q4 2024 metrics:
  • Revenue: $3.2M (+15% QoQ)
  • Customers: 850 (+8.9% QoQ)
  • Gross Margin: 72%
  • Churn Rate: 2.8% (improved from 3.5%)
- Add a simple bar chart comparing Q3 vs Q4 revenue

Slide 3:
- Title: "Quarterly Trend Analysis"
- Line chart showing revenue growth Q1-Q4
- Key insight bullets:
  • Consistent QoQ growth
  • Customer acquisition accelerating
  • Churn reduction successful

Clean, data-focused design.
"""

ppt_response, ppt_results = create_skills_message(
    client,
    ppt_pipeline_prompt,
    [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}],
    prefix="pipeline_2_summary_",
    show_token_usage=False,
)

pipeline_tokens["input"] += ppt_response.usage.input_tokens
pipeline_tokens["output"] += ppt_response.usage.output_tokens
print(
    f"✓ PowerPoint created - Tokens: {ppt_response.usage.input_tokens} in, {ppt_response.usage.output_tokens} out"
)

# Step 3: Create PDF Documentation
print("\nStep 3/3: Creating PDF documentation...")

pdf_pipeline_prompt = """
Create a PDF document summarizing the quarterly reporting pipeline:

AUTOMATED REPORTING PIPELINE
Q4 2024 Results Documentation

EXECUTIVE SUMMARY
This document summarizes the Q4 2024 business metrics generated through
our automated reporting pipeline.

KEY METRICS
- Revenue: $3.2M (15% QoQ growth)
- Customer Base: 850 active customers
- Gross Margin: 72%
- Churn Rate: 2.8% (improved from 3.5%)

PIPELINE COMPONENTS
1. Data Processing: Quarterly metrics analyzed in Excel
2. Visualization: Key insights presented in PowerPoint
3. Documentation: Formal report generated in PDF

AUTOMATION BENEFITS
• Reduced reporting time by 90%
• Consistent format and quality
• Eliminated manual errors
• Scalable to multiple reports

NEXT STEPS
- Expand pipeline to include predictive analytics
- Add automated email distribution
- Implement real-time data feeds

Generated: January 2025
Pipeline Version: 1.0

Format as a professional technical document.
"""

pdf_response, pdf_results = create_skills_message(
    client,
    pdf_pipeline_prompt,
    [{"type": "anthropic", "skill_id": "pdf", "version": "latest"}],
    prefix="pipeline_3_documentation_",
    show_token_usage=False,
)

pipeline_tokens["input"] += pdf_response.usage.input_tokens
pipeline_tokens["output"] += pdf_response.usage.output_tokens
print(
    f"✓ PDF created - Tokens: {pdf_response.usage.input_tokens} in, {pdf_response.usage.output_tokens} out"
)

# Pipeline Summary
print("\n" + "=" * 60)
print("🎯 PIPELINE COMPLETE!")
print("=" * 60)

print("\n📊 Pipeline Token Usage Summary:")
print(f"   Total Input Tokens: {pipeline_tokens['input']:,}")
print(f"   Total Output Tokens: {pipeline_tokens['output']:,}")
print(f"   Total Tokens: {pipeline_tokens['input'] + pipeline_tokens['output']:,}")
print(f"   Average per document: {(pipeline_tokens['input'] + pipeline_tokens['output']) // 3:,}")

print("\n📁 Generated Files:")
all_results = excel_results + ppt_results + pdf_results
for i, result in enumerate(all_results, 1):
    if result["success"]:
        print(f"   {i}. {os.path.basename(result['output_path'])} ({result['size'] / 1024:.1f} KB)")

print("\n✅ Automated reporting pipeline executed successfully!")
print("   All three documents created and linked in workflow.")
```
:::

::: {.cell .markdown}
## Summary & Next Steps {#summary--next-steps}

### What We\'ve Accomplished

In this notebook, you\'ve learned how to:

✅ **Financial Dashboard Creation**

- Built multi-sheet Excel models with formulas and charts
- Generated executive PowerPoint presentations
- Created professional PDF reports

✅ **Portfolio Analysis**

- Developed portfolio analytics workbooks
- Created investment committee presentations
- Implemented risk metrics and rebalancing tools

✅ **Automated Pipelines**

- Chained multiple document formats
- Optimized token usage
- Built production-ready patterns

### Key Takeaways

1.  **Skills dramatically simplify financial document creation** - What
    would take hours manually takes minutes
2.  **Token efficiency is excellent** - Skills use \~90% fewer tokens
    than manual instructions
3.  **Quality is professional-grade** - Documents are immediately usable
    in business contexts
4.  **Automation is straightforward** - Pipeline patterns enable complex
    workflows

### Continue Your Learning

📚 **Next: [Notebook 3 - Custom Skills
Development](https://github.com/anthropics/claude-cookbooks/blob/main/skills/notebooks/03_skills_custom_development.ipynb)**

- Build your own specialized financial skills
- Create company-specific templates
- Implement advanced automation

### Try These Experiments

1.  **Modify the financial dashboard** to include your own metrics
2.  **Create a custom portfolio** with different asset classes
3.  **Build a pipeline** for your specific reporting needs
4.  **Experiment with complexity** to understand generation times
5.  **Track token usage** across different document types

### Resources

- [Claude API Documentation](https://docs.anthropic.com/en/api/messages)
- [Skills
  Documentation](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview)
- [Best
  Practices](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
- [Files API Reference](https://docs.claude.com/en/api/files-content)
:::

---

## Building Custom Skills for Claude

Source: https://github.com/anthropics/claude-cookbooks/blob/main/skills/notebooks/03_skills_custom_development.ipynb
Path: 08-Plugins-Skills/claude-cookbooks-skills-notebooks-03-skills-custom-development.md

# Building Custom Skills for Claude

Learn how to create, deploy, and manage custom skills to extend
Claude\'s capabilities with your organization\'s specialized knowledge
and workflows.
:::

::: {.cell .markdown}
## Table of Contents

1.  [Introduction & Setup](#introduction)
2.  [Understanding Custom Skills Architecture](#architecture)
3.  [Example 1: Financial Ratio Calculator](#financial-ratio)
4.  [Example 2: Company Brand Guidelines](#brand-guidelines)
5.  [Example 3: Financial Modeling Suite](#financial-modeling)
6.  [Skill Management & Versioning](#management)
7.  [Best Practices & Production Tips](#best-practices)
8.  [Troubleshooting](#troubleshooting)
:::

::: {.cell .markdown}
## 1. Introduction & Setup {#introduction} {#1-introduction--setup-introduction}

### What are Custom Skills?

**Custom skills** are specialized expertise packages you create to teach
Claude your organization\'s unique workflows, domain knowledge, and best
practices. Unlike Anthropic\'s pre-built skills (Excel, PowerPoint,
PDF), custom skills allow you to:

- **Codify organizational knowledge** - Capture your team\'s specific
  methodologies
- **Ensure consistency** - Apply the same standards across all
  interactions
- **Automate complex workflows** - Chain together multi-step processes
- **Maintain intellectual property** - Keep proprietary methods secure

### Key Benefits

  -----------------------------------------------------------------------
  Benefit                       Description
  ----------------------------- -----------------------------------------
  **Expertise at Scale**        Deploy specialized knowledge to every
                                Claude interaction

  **Version Control**           Track changes and roll back if needed

  **Composability**             Combine multiple skills for complex tasks

  **Privacy**                   Your skills remain private to your
                                organization
  -----------------------------------------------------------------------

### Prerequisites

Before starting, ensure you have:

- Completed [Notebook 1: Introduction to
  Skills](https://github.com/anthropics/claude-cookbooks/blob/main/skills/notebooks/01_skills_introduction.ipynb)
- An Anthropic API key with Skills beta access
- Python environment with the local SDK installed
:::

::: {.cell .markdown}
### Environment Setup

Let\'s set up our environment and import necessary libraries:
:::

::: {.cell .code}
``` python
import os
import sys
from pathlib import Path
from typing import Any

# Add parent directory for imports
sys.path.insert(0, str(Path.cwd().parent))

from anthropic import Anthropic
from anthropic.lib import files_from_dir
from dotenv import load_dotenv

# Import our utilities
from file_utils import (
    download_all_files,
    extract_file_ids,
    print_download_summary,
)

# We'll create skill_utils later in this notebook
# from skill_utils import (
#     create_skill,
#     list_skills,
#     delete_skill,
#     test_skill
# )

# Load environment variables
load_dotenv(Path.cwd().parent / ".env")

API_KEY = os.getenv("ANTHROPIC_API_KEY")
MODEL = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-5")

if not API_KEY:
    raise ValueError(
        "ANTHROPIC_API_KEY not found. Copy ../.env.example to ../.env and add your API key."
    )

# Initialize client with Skills beta
client = Anthropic(api_key=API_KEY, default_headers={"anthropic-beta": "skills-2025-10-02"})

# Setup directories
SKILLS_DIR = Path.cwd().parent / "custom_skills"
OUTPUT_DIR = Path.cwd().parent / "outputs"
OUTPUT_DIR.mkdir(exist_ok=True)

print("✓ API key loaded")
print(f"✓ Using model: {MODEL}")
print(f"✓ Custom skills directory: {SKILLS_DIR}")
print(f"✓ Output directory: {OUTPUT_DIR}")
print("\n📝 Skills beta header configured for skill management")
```
:::

::: {.cell .markdown}
## 2. Understanding Custom Skills Architecture {#architecture} {#2-understanding-custom-skills-architecture-architecture}

### Skill Structure

Every custom skill follows this directory structure:

    skill_name/
    ├── SKILL.md           # REQUIRED: Instructions with YAML frontmatter
    ├── *.md               # Optional: Any additional .md files (documentation, guides)
    ├── scripts/           # Optional: Executable code
    │   ├── process.py
    │   └── utils.js
    └── resources/         # Optional: Templates, data files
        └── template.xlsx

**Important:**

- **SKILL.md is the ONLY required file** - everything else is optional
- **Multiple .md files allowed** - You can have any number of markdown
  files in the top-level folder
- **All .md files are loaded** - Not just SKILL.md and REFERENCE.md, but
  any .md file you include
- **Organize as needed** - Use multiple .md files to structure complex
  documentation

📖 Read our engineering blog post on [Equipping agents for the real
world with
Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills)

### Skills Are Not Just Markdown

![Skills Can Include Scripts and Files](https://github.com/anthropics/claude-cookbooks/blob/main/skills/assets/not-just-markdown.png)

Skills can bundle various file types:

- **Markdown files**: Instructions and documentation (SKILL.md,
  REFERENCE.md, etc.)
- **Scripts**: Python, JavaScript, or other executable code for complex
  operations
- **Templates**: Pre-built files that can be customized (Excel
  templates, document templates)
- **Resources**: Supporting data files, configuration, or assets

### SKILL.md Requirements {#skillmd-requirements}

The `SKILL.md` file must include:

1.  **YAML Frontmatter** (name: 64 chars, description: 1024 chars)
    - `name`: Lowercase alphanumeric with hyphens (required)
    - `description`: Brief description of what the skill does (required)
2.  **Instructions** (markdown format)
    - Clear guidance for Claude
    - Examples of usage
    - Any constraints or rules
    - Recommended: Keep under 5,000 tokens

### Additional Documentation Files

You can include multiple markdown files for better organization:

    skill_name/
    ├── SKILL.md           # Main instructions (required)
    ├── REFERENCE.md       # API reference (optional)
    ├── EXAMPLES.md        # Usage examples (optional)
    ├── TROUBLESHOOTING.md # Common issues (optional)
    └── CHANGELOG.md       # Version history (optional)

All `.md` files in the root directory will be available to Claude when
the skill is loaded.

### Bundled Files Example

![Bundled Files in Skills](https://github.com/anthropics/claude-cookbooks/blob/main/skills/assets/skills-bundled-files.png)

This example shows how Skills can bundle multiple files:

- **SKILL.md**: Contains the main instructions with colors, typography,
  and sections
- **slide-decks.md**: Additional documentation for specific use cases
- **Scripts and resources**: Can be referenced and used during skill
  execution

### Progressive Disclosure

Skills load in three stages to optimize token usage:

  ----------------------------------------------------------------------------
  Stage            Content         Token Cost           When Loaded
  ---------------- --------------- -------------------- ----------------------
  **1. Metadata**  Name &          name: 64 chars,      Always visible
                   description     description: 1024    
                                   chars                

  **2.             All .md files   \<5,000 tokens       When relevant
  Instructions**                   recommended          

  **3. Resources** Scripts & files As needed            During execution
  ----------------------------------------------------------------------------

### API Workflow

``` python
# 1. Create skill
skill = client.beta.skills.create(
    display_title="My Skill",
    files=files_from_dir("path/to/skill")
)

# 2. Use in messages
response = client.beta.messages.create(
    container={
        "skills": [{
            "type": "custom",
            "skill_id": skill.id,
            "version": "latest"
        }]
    },
    # ... rest of message parameters
)
```

### Best Practices

For detailed guidance on skill creation and best practices, see:

- [Claude Skills Best
  Practices](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
- [Skills
  Documentation](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview)
:::

::: {.cell .markdown}
### Create Skill Utility Functions

Let\'s create helper functions for skill management:
:::

::: {.cell .code}
``` python
def create_skill(client: Anthropic, skill_path: str, display_title: str) -> dict[str, Any]:
    """
    Create a new custom skill from a directory.

    Args:
        client: Anthropic client instance
        skill_path: Path to skill directory
        display_title: Human-readable skill name

    Returns:
        Dictionary with skill_id, version, and metadata
    """
    try:
        # Create skill using files_from_dir
        skill = client.beta.skills.create(
            display_title=display_title, files=files_from_dir(skill_path)
        )

        return {
            "success": True,
            "skill_id": skill.id,
            "display_title": skill.display_title,
            "latest_version": skill.latest_version,
            "created_at": skill.created_at,
            "source": skill.source,
        }
    except Exception as e:
        return {"success": False, "error": str(e)}


def list_custom_skills(client: Anthropic) -> list[dict[str, Any]]:
    """
    List all custom skills in the workspace.

    Returns:
        List of skill dictionaries
    """
    try:
        skills_response = client.beta.skills.list(source="custom")

        skills = []
        for skill in skills_response.data:
            skills.append(
                {
                    "skill_id": skill.id,
                    "display_title": skill.display_title,
                    "latest_version": skill.latest_version,
                    "created_at": skill.created_at,
                    "updated_at": skill.updated_at,
                }
            )

        return skills
    except Exception as e:
        print(f"Error listing skills: {e}")
        return []


def delete_skill(client: Anthropic, skill_id: str) -> bool:
    """
    Delete a custom skill and all its versions.

    Args:
        client: Anthropic client
        skill_id: ID of skill to delete

    Returns:
        True if successful, False otherwise
    """
    try:
        # First delete all versions
        versions = client.beta.skills.versions.list(skill_id=skill_id)

        for version in versions.data:
            client.beta.skills.versions.delete(skill_id=skill_id, version=version.version)

        # Then delete the skill itself
        client.beta.skills.delete(skill_id)
        return True

    except Exception as e:
        print(f"Error deleting skill: {e}")
        return False


def test_skill(
    client: Anthropic,
    skill_id: str,
    test_prompt: str,
    model: str = "claude-sonnet-4-5",
) -> Any:
    """
    Test a custom skill with a prompt.

    Args:
        client: Anthropic client
        skill_id: ID of skill to test
        test_prompt: Prompt to test the skill
        model: Model to use for testing

    Returns:
        Response from Claude
    """
    response = client.beta.messages.create(
        model=model,
        max_tokens=4096,
        container={"skills": [{"type": "custom", "skill_id": skill_id, "version": "latest"}]},
        tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
        messages=[{"role": "user", "content": test_prompt}],
        betas=[
            "code-execution-2025-08-25",
            "files-api-2025-04-14",
            "skills-2025-10-02",
        ],
    )

    return response


print("✓ Skill utility functions defined")
print("  - create_skill()")
print("  - list_custom_skills()")
print("  - delete_skill()")
print("  - test_skill()")
```
:::

::: {.cell .markdown}
### Check Existing Custom Skills

Let\'s see if any custom skills already exist in your workspace:
:::

::: {.cell .markdown}
### ⚠️ Important: Clean Up Existing Skills Before Starting {#warning-important-clean-up-existing-skills-before-starting}

If you\'re re-running this notebook, you may have skills from a previous
session. Skills cannot have duplicate display titles, so you have three
options:

1.  **Delete existing skills** (recommended for testing) - Clean slate
    approach
2.  **Use different display titles** - Add timestamps or version numbers
    to names
3.  **Update existing skills with new versions** - See [Skill Management
    & Versioning](#management) section

Let\'s check for and optionally clean up existing skills:
:::

::: {.cell .code}
``` python
# Check for existing skills that might conflict
existing_skills = list_custom_skills(client)
skill_titles_to_create = [
    "Financial Ratio Analyzer",
    "Corporate Brand Guidelines",
    "Financial Modeling Suite",
]
conflicting_skills = []

if existing_skills:
    print(f"Found {len(existing_skills)} existing custom skill(s):")
    for skill in existing_skills:
        print(f"  - {skill['display_title']} (ID: {skill['skill_id']})")
        if skill["display_title"] in skill_titles_to_create:
            conflicting_skills.append(skill)

    if conflicting_skills:
        print(
            f"\n⚠️ Found {len(conflicting_skills)} skill(s) that will conflict with this notebook:"
        )
        for skill in conflicting_skills:
            print(f"  - {skill['display_title']} (ID: {skill['skill_id']})")

        print("\n" + "=" * 70)
        print("To clean up these skills and start fresh, uncomment and run:")
        print("=" * 70)
        print("\n# UNCOMMENT THE LINES BELOW TO DELETE CONFLICTING SKILLS:")
        print("# for skill in conflicting_skills:")
        print("#     if delete_skill(client, skill['skill_id']):")
        print("#         print(f\"✅ Deleted: {skill['display_title']}\")")
        print("#     else:")
        print("#         print(f\"❌ Failed to delete: {skill['display_title']}\")")

        # for skill in conflicting_skills:
        #     if delete_skill(client, skill['skill_id']):
        #         print(f"✅ Deleted: {skill['display_title']}")
        #     else:
        #         print(f"❌ Failed to delete: {skill['display_title']}")
    else:
        print("\n✅ No conflicting skills found. Ready to proceed!")
else:
    print("✅ No existing custom skills found. Ready to create new ones!")
```
:::

::: {.cell .markdown}
## 3. Example 1: Financial Ratio Calculator {#financial-ratio} {#3-example-1-financial-ratio-calculator-financial-ratio}

Let\'s create our first custom skill - a financial ratio calculator that
can analyze company financial health.

### Skill Overview

The **Financial Ratio Calculator** skill will:

- Calculate key financial ratios (ROE, P/E, Current Ratio, etc.)
- Interpret ratios with industry context
- Generate formatted reports
- Work with various data formats (CSV, JSON, text)
:::

::: {.cell .markdown}
### Upload the Financial Analyzer Skill

Now let\'s upload our financial analyzer skill to Claude:
:::

::: {.cell .code}
``` python
# Upload the Financial Analyzer skill
financial_skill_path = SKILLS_DIR / "analyzing-financial-statements"

if financial_skill_path.exists():
    print("Uploading Financial Analyzer skill...")
    result = create_skill(client, str(financial_skill_path), "Financial Ratio Analyzer")

    if result["success"]:
        financial_skill_id = result["skill_id"]
        print("✅ Skill uploaded successfully!")
        print(f"   Skill ID: {financial_skill_id}")
        print(f"   Version: {result['latest_version']}")
        print(f"   Created: {result['created_at']}")
    else:
        print(f"❌ Upload failed: {result['error']}")
        if "cannot reuse an existing display_title" in str(result["error"]):
            print("\n💡 Solution: A skill with this name already exists.")
            print("   Run the 'Clean Up Existing Skills' cell above to delete it first,")
            print("   or change the display_title to something unique.")
else:
    print(f"⚠️ Skill directory not found: {financial_skill_path}")
    print(
        "Please ensure the custom_skills directory contains the analyzing-financial-statements folder."
    )
```
:::

::: {.cell .markdown}
### Test the Financial Analyzer Skill

Let\'s test the skill with sample financial data:
:::

::: {.cell .code}
``` python
# Test the Financial Analyzer skill
if "financial_skill_id" in locals():
    test_prompt = """
    Calculate financial ratios for this company:

    Income Statement:
    - Revenue: $1,000M
    - EBITDA: $200M
    - Net Income: $120M

    Balance Sheet:
    - Total Assets: $2,000M
    - Current Assets: $500M
    - Current Liabilities: $300M
    - Total Debt: $400M
    - Shareholders Equity: $1,200M

    Market Data:
    - Share Price: $50
    - Shares Outstanding: 100M

    Please calculate key ratios and provide analysis.
    """

    print("Testing Financial Analyzer skill...")
    response = test_skill(client, financial_skill_id, test_prompt)

    # Print response
    for content in response.content:
        if content.type == "text":
            print(content.text)
else:
    print("⚠️ Please upload the Financial Analyzer skill first (run the previous cell)")
```
:::

::: {.cell .markdown}
## 4. Example 2: Company Brand Guidelines {#brand-guidelines} {#4-example-2-company-brand-guidelines-brand-guidelines}

Now let\'s create a skill that ensures all documents follow corporate
brand standards.

### Skill Overview {#skill-overview}

The **Brand Guidelines** skill will:

- Apply consistent colors, fonts, and layouts
- Ensure logo placement and usage
- Maintain professional tone and messaging
- Work across all document types (Excel, PowerPoint, PDF)
:::

::: {.cell .code}
``` python
# Upload the Brand Guidelines skill
brand_skill_path = SKILLS_DIR / "applying-brand-guidelines"

if brand_skill_path.exists():
    print("Uploading Brand Guidelines skill...")
    result = create_skill(client, str(brand_skill_path), "Corporate Brand Guidelines")

    if result["success"]:
        brand_skill_id = result["skill_id"]
        print("✅ Skill uploaded successfully!")
        print(f"   Skill ID: {brand_skill_id}")
        print(f"   Version: {result['latest_version']}")
    else:
        print(f"❌ Upload failed: {result['error']}")
        if "cannot reuse an existing display_title" in str(result["error"]):
            print("\n💡 Solution: A skill with this name already exists.")
            print("   Run the 'Clean Up Existing Skills' cell above to delete it first,")
            print("   or change the display_title to something unique.")
else:
    print(f"⚠️ Skill directory not found: {brand_skill_path}")
```
:::

::: {.cell .markdown}
### Test Brand Guidelines with Document Creation

Let\'s test the brand skill by creating a branded PowerPoint
presentation:
:::

::: {.cell .code}
``` python
# Test Brand Guidelines skill with PowerPoint creation
if "brand_skill_id" in locals():
    # Combine brand skill with Anthropic's pptx skill
    response = client.beta.messages.create(
        model=MODEL,
        max_tokens=4096,
        container={
            "skills": [
                {"type": "custom", "skill_id": brand_skill_id, "version": "latest"},
                {"type": "anthropic", "skill_id": "pptx", "version": "latest"},
            ]
        },
        tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
        messages=[
            {
                "role": "user",
                "content": """Create a 3-slide PowerPoint presentation following Acme Corporation brand guidelines:

            Slide 1: Title slide for "Q4 2025 Results"
            Slide 2: Revenue Overview with a chart showing Q1-Q4 growth
            Slide 3: Key Achievements (3 bullet points)

            Apply all brand colors, fonts, and formatting standards.
            """,
            }
        ],
        betas=[
            "code-execution-2025-08-25",
            "files-api-2025-04-14",
            "skills-2025-10-02",
        ],
    )

    print("Response from Claude:")
    for content in response.content:
        if content.type == "text":
            print(content.text[:500] + "..." if len(content.text) > 500 else content.text)

    # Download generated file
    file_ids = extract_file_ids(response)
    if file_ids:
        results = download_all_files(
            client, response, output_dir=str(OUTPUT_DIR), prefix="branded_"
        )
        print_download_summary(results)
else:
    print("⚠️ Please upload the Brand Guidelines skill first")
```
:::

::: {.cell .markdown}
## 5. Example 3: Financial Modeling Suite {#financial-modeling} {#5-example-3-financial-modeling-suite-financial-modeling}

Let\'s create our most advanced skill - a comprehensive financial
modeling suite for valuation and risk analysis.

### Skill Overview {#skill-overview}

The **Financial Modeling Suite** skill provides:

- **DCF Valuation**: Complete discounted cash flow models
- **Sensitivity Analysis**: Test impact of variables on valuation
- **Monte Carlo Simulation**: Risk modeling with probability
  distributions
- **Scenario Planning**: Best/base/worst case analysis

This demonstrates a multi-file skill with complex calculations and
professional-grade financial modeling.
:::

::: {.cell .markdown}
### Upload the Financial Modeling Suite

First, upload the financial modeling skill:
:::

::: {.cell .code}
``` python
# Upload the Financial Modeling Suite skill
modeling_skill_path = SKILLS_DIR / "creating-financial-models"

if modeling_skill_path.exists():
    print("Uploading Financial Modeling Suite skill...")
    result = create_skill(client, str(modeling_skill_path), "Financial Modeling Suite")

    if result["success"]:
        modeling_skill_id = result["skill_id"]
        print("✅ Skill uploaded successfully!")
        print(f"   Skill ID: {modeling_skill_id}")
        print(f"   Version: {result['latest_version']}")
        print("\nThis skill includes:")
        print("   - DCF valuation model (dcf_model.py)")
        print("   - Sensitivity analysis framework (sensitivity_analysis.py)")
        print("   - Monte Carlo simulation capabilities")
        print("   - Scenario planning tools")
    else:
        print(f"❌ Upload failed: {result['error']}")
else:
    print(f"⚠️ Skill directory not found: {modeling_skill_path}")
    print(
        "Please ensure the custom_skills directory contains the creating-financial-models folder."
    )
```
:::

::: {.cell .markdown}
### Test the Financial Modeling Suite

Let\'s test the advanced modeling capabilities with a DCF valuation
request:
:::

::: {.cell .code}
``` python
# Test the Financial Modeling Suite with a DCF valuation
if "modeling_skill_id" in locals():
    dcf_test_prompt = """
    Perform a DCF valuation for TechCorp with the following data:

    Historical Financials (Last 3 Years):
    - Revenue: $500M, $600M, $750M
    - EBITDA Margin: 25%, 27%, 30%
    - CapEx: $50M, $55M, $60M
    - Working Capital: 15% of revenue

    Projections:
    - Revenue growth: 20% for years 1-3, then declining to 5% by year 5
    - EBITDA margin expanding to 35% by year 5
    - Terminal growth rate: 3%

    Market Assumptions:
    - WACC: 10%
    - Tax rate: 25%
    - Current net debt: $200M
    - Shares outstanding: 100M

    Please create a complete DCF model with sensitivity analysis on WACC and terminal growth.
    Generate an Excel file with the full model including:
    1. Revenue projections
    2. Free cash flow calculations
    3. Terminal value
    4. Enterprise value to equity value bridge
    5. Sensitivity table
    """

    print("Testing Financial Modeling Suite with DCF valuation...")
    print("=" * 70)
    print("\n⏱️ Note: Complex financial model generation may take 1-2 minutes.\n")

    response = client.beta.messages.create(
        model=MODEL,
        max_tokens=4096,
        container={
            "skills": [
                {"type": "custom", "skill_id": modeling_skill_id, "version": "latest"},
                {"type": "anthropic", "skill_id": "xlsx", "version": "latest"},
            ]
        },
        tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
        messages=[{"role": "user", "content": dcf_test_prompt}],
        betas=[
            "code-execution-2025-08-25",
            "files-api-2025-04-14",
            "skills-2025-10-02",
        ],
    )

    # Print Claude's response
    for content in response.content:
        if content.type == "text":
            # Print first 800 characters to keep output manageable
            text = content.text
            if len(text) > 800:
                print(text[:800] + "\n\n[... Output truncated for brevity ...]")
            else:
                print(text)

    # Download the DCF model if generated
    file_ids = extract_file_ids(response)
    if file_ids:
        print("\n" + "=" * 70)
        print("Downloading generated DCF model...")
        results = download_all_files(
            client, response, output_dir=str(OUTPUT_DIR), prefix="dcf_model_"
        )
        print_download_summary(results)
        print("\n💡 Open the Excel file to explore the complete DCF valuation model!")
else:
    print("⚠️ Please upload the Financial Modeling Suite skill first (run the previous cell)")
```
:::

::: {.cell .markdown}
## 6. Skill Management & Versioning {#management} {#6-skill-management--versioning-management}

Managing skills over time requires understanding versioning, updates,
and lifecycle management.

### Listing Your Skills

Get an overview of all custom skills in your workspace:
:::

::: {.cell .code}
``` python
# List all your custom skills
my_skills = list_custom_skills(client)

if my_skills:
    print(f"You have {len(my_skills)} custom skill(s):\n")
    print("=" * 70)
    for i, skill in enumerate(my_skills, 1):
        print(f"\n{i}. {skill['display_title']}")
        print(f"   Skill ID: {skill['skill_id']}")
        print(f"   Current Version: {skill['latest_version']}")
        print(f"   Created: {skill['created_at']}")
        if skill.get("updated_at"):
            print(f"   Last Updated: {skill['updated_at']}")
    print("\n" + "=" * 70)
else:
    print("No custom skills found in your workspace.")
```
:::

::: {.cell .markdown}
### Creating New Versions

Skills support versioning to maintain history and enable rollback.
Let\'s make an enhancement to our Financial Analyzer skill and create a
new version.

#### Step 1: Enhance the Financial Analyzer

We\'ll add **healthcare industry** benchmarks to make our skill more
versatile. This is a real-world scenario where you\'d expand a skill\'s
capabilities based on user needs.
:::

::: {.cell .code}
``` python
# Add healthcare industry benchmarks to the Financial Analyzer
# This demonstrates a realistic skill enhancement scenario

if "financial_skill_id" in locals():
    # Read the current interpret_ratios.py file
    interpret_file_path = SKILLS_DIR / "analyzing-financial-statements" / "interpret_ratios.py"

    with open(interpret_file_path) as f:
        content = f.read()

    # Add healthcare benchmarks after the 'manufacturing' section
    healthcare_benchmarks = """        },
        'healthcare': {
            'current_ratio': {'excellent': 2.3, 'good': 1.8, 'acceptable': 1.4, 'poor': 1.0},
            'debt_to_equity': {'excellent': 0.3, 'good': 0.6, 'acceptable': 1.0, 'poor': 1.8},
            'roe': {'excellent': 0.22, 'good': 0.16, 'acceptable': 0.11, 'poor': 0.07},
            'gross_margin': {'excellent': 0.65, 'good': 0.45, 'acceptable': 0.30, 'poor': 0.20},
            'pe_ratio': {'undervalued': 18, 'fair': 28, 'growth': 40, 'expensive': 55}
        """

    # Find the position after manufacturing section and before the closing brace
    insert_pos = content.find("        }\n    }")  # Find the end of the BENCHMARKS dict

    if insert_pos != -1:
        # Insert the healthcare benchmarks
        new_content = content[:insert_pos] + healthcare_benchmarks + content[insert_pos:]

        # Save the enhanced file
        with open(interpret_file_path, "w") as f:
            f.write(new_content)

        print("✅ Enhanced Financial Analyzer with healthcare industry benchmarks")
        print("\nChanges made:")
        print("  - Added healthcare industry to BENCHMARKS")
        print("  - Includes specific thresholds for:")
        print("    • Current ratio (liquidity)")
        print("    • Debt-to-equity (leverage)")
        print("    • ROE (profitability)")
        print("    • Gross margin")
        print("    • P/E ratio (valuation)")
        print("\n📝 Now we can create a new version of the skill with this enhancement!")
    else:
        print("⚠️ Could not find the correct position to insert healthcare benchmarks")
        print("The file structure may have changed.")
else:
    print("⚠️ Please upload the Financial Analyzer skill first (run cells in Section 3)")
```
:::

::: {.cell .markdown}
#### Step 2: Create a New Version

Now that we\'ve enhanced our skill, let\'s create a new version to track
this change:
:::

::: {.cell .code}
``` python
# Create a new version of the enhanced Financial Analyzer skill
def create_skill_version(client: Anthropic, skill_id: str, skill_path: str):
    """Create a new version of an existing skill."""
    try:
        version = client.beta.skills.versions.create(
            skill_id=skill_id, files=files_from_dir(skill_path)
        )
        return {
            "success": True,
            "version": version.version,
            "created_at": version.created_at,
        }
    except Exception as e:
        return {"success": False, "error": str(e)}


# Create the new version with our healthcare enhancement
if "financial_skill_id" in locals():
    print("Creating new version of Financial Analyzer with healthcare benchmarks...")

    result = create_skill_version(
        client, financial_skill_id, str(SKILLS_DIR / "analyzing-financial-statements")
    )

    if result["success"]:
        print("✅ New version created successfully!")
        print(f"   Version: {result['version']}")
        print(f"   Created: {result['created_at']}")
        print("\n📊 Version History:")
        print("   v1: Original skill with tech, retail, financial, manufacturing")
        print(f"   v{result['version']}: Enhanced with healthcare industry benchmarks")
    else:
        print(f"❌ Version creation failed: {result['error']}")
else:
    print("⚠️ Please run the previous cells to upload the skill and make enhancements first")
```
:::

::: {.cell .markdown}
#### Step 3: Test the New Version

Let\'s verify our enhancement works by analyzing a healthcare company:
:::

::: {.cell .code}
``` python
# Test the enhanced skill with healthcare industry data
if "financial_skill_id" in locals():
    healthcare_test_prompt = """
    Analyze this healthcare company using the healthcare industry benchmarks:

    Company: MedTech Solutions (Healthcare Industry)

    Income Statement:
    - Revenue: $800M
    - EBITDA: $320M
    - Net Income: $160M

    Balance Sheet:
    - Total Assets: $1,200M
    - Current Assets: $400M
    - Current Liabilities: $200M
    - Total Debt: $300M
    - Shareholders Equity: $700M

    Market Data:
    - Share Price: $75
    - Shares Outstanding: 50M

    Please calculate key ratios and provide healthcare-specific analysis.
    """

    print("Testing enhanced Financial Analyzer with healthcare company...")
    print("=" * 70)

    response = test_skill(client, financial_skill_id, healthcare_test_prompt, MODEL)

    # Print Claude's analysis
    for content in response.content:
        if content.type == "text":
            # Print first 1000 characters to keep output manageable
            text = content.text
            if len(text) > 1000:
                print(text[:1000] + "\n\n[... Output truncated for brevity ...]")
            else:
                print(text)

    print(
        "\n✅ The skill now recognizes 'healthcare' as an industry and applies specific benchmarks!"
    )
else:
    print("⚠️ Please run the previous cells to create the enhanced version first")
```
:::

::: {.cell .markdown}
### Cleanup: Managing Your Skills

When you\'re done testing or need to clean up your workspace, you can
selectively remove skills. Let\'s review what we\'ve created and provide
options for cleanup:
:::

::: {.cell .code}
``` python
# Comprehensive skill cleanup with detailed reporting
def review_and_cleanup_skills(client, dry_run=True):
    """
    Review all skills and optionally clean up the ones created in this notebook.

    Args:
        client: Anthropic client
        dry_run: If True, only show what would be deleted without actually deleting
    """
    # Get all current skills
    all_skills = list_custom_skills(client)

    # Skills we created in this notebook
    notebook_skill_names = [
        "Financial Ratio Analyzer",
        "Corporate Brand Guidelines",
        "Financial Modeling Suite",
    ]

    # Track skills created by this notebook
    notebook_skills = []
    other_skills = []

    for skill in all_skills:
        if skill["display_title"] in notebook_skill_names:
            notebook_skills.append(skill)
        else:
            other_skills.append(skill)

    print("=" * 70)
    print("SKILL INVENTORY REPORT")
    print("=" * 70)

    print(f"\nTotal custom skills in workspace: {len(all_skills)}")

    if notebook_skills:
        print(f"\n📚 Skills created by this notebook ({len(notebook_skills)}):")
        for skill in notebook_skills:
            print(f"   • {skill['display_title']}")
            print(f"     ID: {skill['skill_id']}")
            print(f"     Version: {skill['latest_version']}")
            print(f"     Created: {skill['created_at']}")
    else:
        print("\n✅ No skills from this notebook found")

    if other_skills:
        print(f"\n🔧 Other skills in workspace ({len(other_skills)}):")
        for skill in other_skills:
            print(f"   • {skill['display_title']} (v{skill['latest_version']})")

    # Cleanup options
    if notebook_skills:
        print("\n" + "=" * 70)
        print("CLEANUP OPTIONS")
        print("=" * 70)

        if dry_run:
            print("\n🔍 DRY RUN MODE - No skills will be deleted")
            print("\nTo delete the notebook skills, uncomment and run:")
            print("-" * 40)
            print("# review_and_cleanup_skills(client, dry_run=False)")
            print("-" * 40)

            print("\nThis would delete:")
            for skill in notebook_skills:
                print(f"   • {skill['display_title']}")
        else:
            print("\n⚠️ DELETION MODE - Skills will be permanently removed")
            print("\nDeleting notebook skills...")

            success_count = 0
            for skill in notebook_skills:
                if delete_skill(client, skill["skill_id"]):
                    print(f"   ✅ Deleted: {skill['display_title']}")
                    success_count += 1
                else:
                    print(f"   ❌ Failed to delete: {skill['display_title']}")

            print(f"\n📊 Cleanup complete: {success_count}/{len(notebook_skills)} skills deleted")

    return {
        "total_skills": len(all_skills),
        "notebook_skills": len(notebook_skills),
        "other_skills": len(other_skills),
        "notebook_skill_ids": [s["skill_id"] for s in notebook_skills],
    }


# Run the review (in dry-run mode by default)
print("Reviewing your custom skills workspace...")
cleanup_summary = review_and_cleanup_skills(client, dry_run=True)

# Store skill IDs for potential cleanup
if cleanup_summary["notebook_skill_ids"]:
    skills_to_cleanup = cleanup_summary["notebook_skill_ids"]
    print(f"\n💡 Tip: {len(skills_to_cleanup)} skill(s) can be cleaned up when you're done testing")

# UNCOMMENT THE LINE BELOW TO ACTUALLY DELETE THE NOTEBOOK SKILLS:
# review_and_cleanup_skills(client, dry_run=False)
```
:::

::: {.cell .markdown}
## 7. Best Practices & Production Tips {#best-practices} {#7-best-practices--production-tips-best-practices}

### Skill Design Principles

1.  **Single Responsibility**: Each skill should focus on one area of
    expertise
2.  **Clear Documentation**: SKILL.md should be comprehensive yet
    concise
3.  **Error Handling**: Scripts should handle edge cases gracefully
4.  **Version Control**: Use Git to track skill changes
5.  **Testing**: Always test skills before production deployment

### Directory Structure Best Practices

    custom_skills/
    ├── financial_analyzer/       # Single purpose, clear naming
    │   ├── SKILL.md             # Under 5,000 tokens
    │   ├── scripts/             # Modular Python/JS files
    │   └── tests/               # Unit tests for scripts
    ├── brand_guidelines/         # Organizational standards
    │   ├── SKILL.md
    │   ├── REFERENCE.md         # Additional documentation
    │   └── assets/              # Logos, templates

### Performance Optimization

  -----------------------------------------------------------------------
  Strategy             Impact           Implementation
  -------------------- ---------------- ---------------------------------
  **Minimal            Faster skill     name: 64 chars, description: 1024
  Frontmatter**        discovery        chars

  **Lazy Loading**     Reduced token    Reference files only when needed
                       usage            

  **Skill              Avoid            Combine skills vs. mega-skill
  Composition**        duplication      

  **Caching**          Faster responses Reuse skill containers
  -----------------------------------------------------------------------

### Security Considerations

- **API Keys**: Never hardcode credentials in skills
- **Data Privacy**: Don\'t include sensitive data in skill files
- **Access Control**: Skills are workspace-specific
- **Validation**: Sanitize inputs in scripts
- **Audit Trail**: Log skill usage for compliance
:::

::: {.cell .markdown}
## Next Steps

🎉 **Congratulations!** You\'ve learned how to create, deploy, and
manage custom skills for Claude.

### What You\'ve Learned

- ✅ Custom skill architecture and requirements
- ✅ Creating skills with SKILL.md and Python scripts
- ✅ Uploading skills via the API
- ✅ Combining custom and Anthropic skills
- ✅ Best practices for production deployment
- ✅ Troubleshooting common issues

### Continue Your Journey

1.  **Experiment**: Modify the example skills for your use cases
2.  **Build**: Create skills for your organization\'s workflows
3.  **Optimize**: Monitor token usage and performance
4.  **Share**: Document your skills for team collaboration

### Resources

- [Claude API Documentation](https://docs.anthropic.com/en/api/messages)
- [Skills
  Documentation](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview)
- [Best
  Practices](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/best-practices)
- [Files API
  Documentation](https://docs.claude.com/en/api/files-content)
- Example Skills Repository (coming soon)

### Skill Ideas to Try

- 📊 **Data Pipeline**: ETL workflows with validation
- 📝 **Document Templates**: Contracts, proposals, reports
- 🔍 **Code Review**: Style guides and best practices
- 📈 **Analytics Dashboard**: KPI tracking and visualization
- 🤖 **Automation Suite**: Repetitive task workflows

Happy skill building! 🚀
:::

---

## How to add skills support to your agent - Agent Skills

Source: https://agentskills.io/client-implementation/adding-skills-support
Path: 08-Plugins-Skills/client-implementation-adding-skills-support.md

## On this page

- [The core principle: progressive disclosure](#the-core-principle-progressive-disclosure)
- [Step 1: Discover skills](#step-1-discover-skills)
  - [Where to scan](#where-to-scan)
  - [What to scan for](#what-to-scan-for)
  - [Handling name collisions](#handling-name-collisions)
  - [Trust considerations](#trust-considerations)
  - [Cloud-hosted and sandboxed agents](#cloud-hosted-and-sandboxed-agents)
- [Step 2: Parse SKILL.md files](#step-2-parse-skill-md-files)
  - [Frontmatter extraction](#frontmatter-extraction)
  - [Handling malformed YAML](#handling-malformed-yaml)
  - [Lenient validation](#lenient-validation)
  - [What to store](#what-to-store)
- [Step 3: Disclose available skills to the model](#step-3-disclose-available-skills-to-the-model)
  - [Building the skill catalog](#building-the-skill-catalog)
  - [Where to place the catalog](#where-to-place-the-catalog)
  - [Behavioral instructions](#behavioral-instructions)
  - [Filtering](#filtering)
  - [When no skills are available](#when-no-skills-are-available)
- [Step 4: Activate skills](#step-4-activate-skills)
  - [Model-driven activation](#model-driven-activation)
  - [User-explicit activation](#user-explicit-activation)
  - [What the model receives](#what-the-model-receives)
  - [Structured wrapping](#structured-wrapping)
  - [Listing bundled resources](#listing-bundled-resources)
  - [Permission allowlisting](#permission-allowlisting)
- [Step 5: Manage skill context over time](#step-5-manage-skill-context-over-time)
  - [Protect skill content from context compaction](#protect-skill-content-from-context-compaction)
  - [Deduplicate activations](#deduplicate-activations)
  - [Subagent delegation (optional)](#subagent-delegation-optional)

For client implementors

# How to add skills support to your agent

Copy pageCopy page

A guide for adding Agent Skills support to an AI agent or development tool.

Copy pageCopy page

This guide walks through how to add Agent Skills support to an AI agent or development tool. It covers the full lifecycle: discovering skills, telling the model about them, loading their content into context, and keeping that content effective over time. The core integration is the same regardless of your agent’s architecture. The implementation details vary based on two factors:

- **Where do skills live?** A locally-running agent can scan the user’s filesystem for skill directories. A cloud-hosted or sandboxed agent will need an alternative discovery mechanism — an API, a remote registry, or bundled assets.
- **How does the model access skill content?** If the model has file-reading capabilities, it can read `SKILL.md` files directly. Otherwise, you’ll provide a dedicated tool or inject skill content into the prompt programmatically.

The guide notes where these differences matter. You don’t need to support every scenario — follow the path that fits your agent. **Prerequisites**: Familiarity with the [Agent Skills specification](specification.md), which defines the `SKILL.md` file format, frontmatter fields, and directory conventions.


[​](#the-core-principle-progressive-disclosure)

The core principle: progressive disclosure

Every skills-compatible agent follows the same three-tier loading strategy:

| Tier             | What’s loaded               | When                                 | Token cost                  |
|------------------|-----------------------------|--------------------------------------|-----------------------------|
| 1\. Catalog      | Name + description          | Session start                        | ~50-100 tokens per skill    |
| 2\. Instructions | Full `SKILL.md` body        | When the skill is activated          | \<5000 tokens (recommended) |
| 3\. Resources    | Scripts, references, assets | When the instructions reference them | Varies                      |

The model sees the catalog from the start, so it knows what skills are available. When it decides a skill is relevant, it loads the full instructions. If those instructions reference supporting files, the model loads them individually as needed. This keeps the base context small while giving the model access to specialized knowledge on demand. An agent with 20 installed skills doesn’t pay the token cost of 20 full instruction sets upfront — only the ones actually used in a given conversation.


[​](#step-1-discover-skills)

Step 1: Discover skills

At session startup, find all available skills and load their metadata.


[​](#where-to-scan)

Where to scan

Which directories you scan depends on your agent’s environment. Most locally-running agents scan at least two scopes:

- **Project-level** (relative to the working directory): Skills specific to a project or repository.
- **User-level** (relative to the home directory): Skills available across all projects for a given user.

Other scopes are possible too — for example, organization-wide skills deployed by an admin, or skills bundled with the agent itself. The right set of scopes depends on your agent’s deployment model. Within each scope, consider scanning both a **client-specific directory** and the **`.agents/skills/` convention**:

| Scope   | Path                               | Purpose                       |
|---------|------------------------------------|-------------------------------|
| Project | `<project>/.<your-client>/skills/` | Your client’s native location |
| Project | `<project>/.agents/skills/`        | Cross-client interoperability |
| User    | `~/.<your-client>/skills/`         | Your client’s native location |
| User    | `~/.agents/skills/`                | Cross-client interoperability |

The `.agents/skills/` paths have emerged as a widely-adopted convention for cross-client skill sharing. While the Agent Skills specification does not mandate where skill directories live (it only defines what goes inside them), scanning `.agents/skills/` means skills installed by other compliant clients are automatically visible to yours, and vice versa.

Some implementations also scan `.claude/skills/` (both project-level and user-level) for pragmatic compatibility, since many existing skills are installed there. Other additional locations include ancestor directories up to the git root (useful for monorepos), [XDG](https://specifications.freedesktop.org/basedir-spec/latest/) config directories, and user-configured paths.


[​](#what-to-scan-for)

What to scan for

Within each skills directory, look for **subdirectories containing a file named exactly `SKILL.md`**:

```python
~/.agents/skills/
├── pdf-processing/
│   ├── SKILL.md          ← discovered
│   └── scripts/
│       └── extract.py
├── data-analysis/
│   └── SKILL.md          ← discovered
└── README.md             ← ignored (not a skill directory)
```

Practical scanning rules:

- Skip directories that won’t contain skills, such as `.git/` and `node_modules/`
- Optionally respect `.gitignore` to avoid scanning build artifacts
- Set reasonable bounds (e.g., max depth of 4-6 levels, max 2000 directories) to prevent runaway scanning in large directory trees


[​](#handling-name-collisions)

Handling name collisions

When two skills share the same `name`, apply a deterministic precedence rule. The universal convention across existing implementations: **project-level skills override user-level skills.** Within the same scope (e.g., two skills named `code-review` found under both `<project>/.agents/skills/` and `<project>/.<your-client>/skills/`), either first-found or last-found is acceptable — pick one and be consistent. Log a warning when a collision occurs so the user knows a skill was shadowed.


[​](#trust-considerations)

Trust considerations

Project-level skills come from the repository being worked on, which may be untrusted (e.g., a freshly cloned open-source project). Consider gating project-level skill loading on a trust check — only load them if the user has marked the project folder as trusted. This prevents untrusted repositories from silently injecting instructions into the agent’s context.


[​](#cloud-hosted-and-sandboxed-agents)

Cloud-hosted and sandboxed agents

If your agent runs in a container or on a remote server, it won’t have access to the user’s local filesystem. Discovery needs to work differently depending on the skill scope:

- **Project-level skills** are often the easiest case. If the agent operates on a cloned repository (even inside a sandbox), project-level skills travel with the code and can be scanned from the repo’s directory tree.
- **User-level and organization-level skills** don’t exist in the sandbox. You’ll need to provision them from an external source — for example, cloning a configuration repository, accepting skill URLs or packages through your agent’s settings, or letting users upload skill directories through a web UI.
- **Built-in skills** can be packaged as static assets within the agent’s deployment artifact, making them available in every session without external fetching.

Once skills are available to the agent, the rest of the lifecycle — parsing, disclosure, activation — works the same.


[​](#step-2-parse-skill-md-files)

Step 2: Parse `SKILL.md` files

For each discovered `SKILL.md`, extract the metadata and body content.


[​](#frontmatter-extraction)

Frontmatter extraction

A `SKILL.md` file has two parts: YAML frontmatter between `---` delimiters, and a markdown body after the closing delimiter. To parse:

1.  Find the opening `---` at the start of the file and the closing `---` after it.
2.  Parse the YAML block between them. Extract `name` and `description` (required), plus any optional fields.
3.  Everything after the closing `---`, trimmed, is the skill’s body content.

See the [specification](specification.md) for the full set of frontmatter fields and their constraints.


[​](#handling-malformed-yaml)

Handling malformed YAML

Skill files authored for other clients may contain technically invalid YAML that their parsers happen to accept. The most common issue is unquoted values containing colons:

```python
# Technically invalid YAML — the colon breaks parsing
description: Use this skill when: the user asks about PDFs
```

Consider a fallback that wraps such values in quotes or converts them to YAML block scalars before retrying. This improves cross-client compatibility at minimal cost.


[​](#lenient-validation)

Lenient validation

Warn on issues but still load the skill when possible:

- Name doesn’t match the parent directory name → warn, load anyway
- Name exceeds 64 characters → warn, load anyway
- Description is missing or empty → skip the skill (a description is essential for disclosure), log the error
- YAML is completely unparseable → skip the skill, log the error

Record diagnostics so they can be surfaced to the user (in a debug command, log file, or UI), but don’t block skill loading on cosmetic issues.

The [specification](specification.md) defines strict constraints on the `name` field (matching the parent directory, character set, max length). The lenient approach above deliberately relaxes these to improve compatibility with skills authored for other clients.


[​](#what-to-store)

What to store

At minimum, each skill record needs three fields:

| Field         | Description                          |
|---------------|--------------------------------------|
| `name`        | From frontmatter                     |
| `description` | From frontmatter                     |
| `location`    | Absolute path to the `SKILL.md` file |

Store these in an in-memory map keyed by `name` for fast lookup during activation. You can also store the **body** (the markdown content after the frontmatter) at discovery time, or read it from `location` at activation time. Storing it makes activation faster; reading it at activation time uses less memory in aggregate and picks up changes to skill files between activations. The skill’s **base directory** (the parent directory of `location`) is needed later to resolve relative paths and enumerate bundled resources — derive it from `location` when needed.


[​](#step-3-disclose-available-skills-to-the-model)

Step 3: Disclose available skills to the model

Tell the model what skills exist without loading their full content. This is [tier 1 of progressive disclosure](#the-core-principle-progressive-disclosure).


[​](#building-the-skill-catalog)

Building the skill catalog

For each discovered skill, include `name`, `description`, and optionally `location` (the path to the `SKILL.md` file) in whatever structured format suits your stack — XML, JSON, or a bulleted list all work:

```python
<available_skills>
  <skill>
    <name>pdf-processing</name>
    <description>Extract PDF text, fill forms, merge files. Use when handling PDFs.</description>
    <location>/home/user/.agents/skills/pdf-processing/SKILL.md</location>
  </skill>
  <skill>
    <name>data-analysis</name>
    <description>Analyze datasets, generate charts, and create summary reports.</description>
    <location>/home/user/project/.agents/skills/data-analysis/SKILL.md</location>
  </skill>
</available_skills>
```

The `location` field serves two purposes: it enables file-read activation (see [Step 4](#step-4-activate-skills)), and it gives the model a base path for resolving relative references in the skill body (like `scripts/evaluate.py`). If your dedicated activation tool provides the skill directory path in its result (see [Structured wrapping](#structured-wrapping) in Step 4), you can omit `location` from the catalog. Otherwise, include it. Each skill adds roughly 50-100 tokens to the catalog. Even with dozens of skills installed, the catalog remains compact.


[​](#where-to-place-the-catalog)

Where to place the catalog

Two approaches are common: **System prompt section**: Add the catalog as a labeled section in the system prompt, preceded by brief instructions on how to use skills. This is the simplest approach and works with any model that has access to a file-reading tool. **Tool description**: Embed the catalog in the description of a dedicated skill-activation tool (see [Step 4](#step-4-activate-skills)). This keeps the system prompt clean and naturally couples discovery with activation. Both work. System prompt placement is simpler and more broadly compatible; tool description embedding is cleaner when you have a dedicated activation tool.


[​](#behavioral-instructions)

Behavioral instructions

Include a short instruction block alongside the catalog telling the model how and when to use skills. The wording depends on which activation mechanism you support (see [Step 4](#step-4-activate-skills)): **If the model activates skills by reading files:**

```python
The following skills provide specialized instructions for specific tasks.
When a task matches a skill's description, use your file-read tool to load
the SKILL.md at the listed location before proceeding.
When a skill references relative paths, resolve them against the skill's
directory (the parent of SKILL.md) and use absolute paths in tool calls.
```

**If the model activates skills via a dedicated tool:**

```python
The following skills provide specialized instructions for specific tasks.
When a task matches a skill's description, call the activate_skill tool
with the skill's name to load its full instructions.
```

Keep these instructions concise. The goal is to tell the model that skills exist and how to load them — the skill content itself provides the detailed instructions once loaded.


[​](#filtering)

Filtering

Some skills should be excluded from the catalog. Common reasons:

- The user has disabled the skill in settings
- A permission system denies access to the skill
- The skill has opted out of model-driven activation (e.g., via a `disable-model-invocation` flag)

**Hide filtered skills entirely** from the catalog rather than listing them and blocking at activation time. This prevents the model from wasting turns attempting to load skills it can’t use.


[​](#when-no-skills-are-available)

When no skills are available

If no skills are discovered, omit the catalog and behavioral instructions entirely. Don’t show an empty `<available_skills/>` block or register a skill tool with no valid options — this would confuse the model.


[​](#step-4-activate-skills)

Step 4: Activate skills

When the model or user selects a skill, deliver the full instructions into the conversation context. This is [tier 2 of progressive disclosure](#the-core-principle-progressive-disclosure).


[​](#model-driven-activation)

Model-driven activation

Most implementations rely on the model’s own judgment as the activation mechanism, rather than implementing harness-side trigger matching or keyword detection. The model reads the catalog (from [Step 3](#step-3-disclose-available-skills-to-the-model)), decides a skill is relevant to the current task, and loads it. Two implementation patterns: **File-read activation**: The model calls its standard file-read tool with the `SKILL.md` path from the catalog. No special infrastructure needed — the agent’s existing file-reading capability is sufficient. The model receives the file content as a tool result. This is the simplest approach when the model has file access. **Dedicated tool activation**: Register a tool (e.g., `activate_skill`) that takes a skill name and returns the content. This is required when the model can’t read files directly, and optional (but useful) even when it can. Advantages over raw file reads:

- Control what content is returned — e.g., strip YAML frontmatter or preserve it (see [What the model receives](#what-the-model-receives) below)
- Wrap content in structured tags for identification during context management
- List bundled resources (e.g., `references/*`) alongside the instructions
- Enforce permissions or prompt for user consent
- Track activation for analytics

If you use a dedicated activation tool, constrain the `name` parameter to the set of valid skill names (e.g., as an enum in the tool schema). This prevents the model from hallucinating nonexistent skill names. If no skills are available, don’t register the tool at all.


[​](#user-explicit-activation)

User-explicit activation

Users should also be able to activate skills directly, without waiting for the model to decide. The most common pattern is a **slash command or mention syntax** (`/skill-name` or `$skill-name`) that the harness intercepts. The specific syntax is up to you — the key idea is that the harness handles the lookup and injection, so the model receives skill content without needing to take an activation action itself. An autocomplete widget (listing available skills as the user types) can also make this discoverable.


[​](#what-the-model-receives)

What the model receives

When a skill is activated, the model receives the skill’s instructions. Two options for what exactly that content looks like: **Full file**: The model sees the entire `SKILL.md` including YAML frontmatter. This is the natural outcome with file-read activation, where the model reads the raw file. It’s also a valid choice for dedicated tools. The frontmatter may contain fields useful at activation time — for example, [`compatibility`](specification.md#compatibility-field) notes environment requirements that could inform how the model executes the skill’s instructions. **Body only (frontmatter stripped)**: The harness parses and removes the YAML frontmatter, returning only the markdown instructions. Among existing implementations with dedicated activation tools, most take this approach — stripping the frontmatter after extracting `name` and `description` during discovery. Both approaches work in practice.


[​](#structured-wrapping)

Structured wrapping

If you use a dedicated activation tool, consider wrapping skill content in identifying tags. For example:

```python
<skill_content name="pdf-processing">
# PDF Processing

## When to use this skill
Use this skill when the user needs to work with PDF files...

[rest of SKILL.md body]

Skill directory: /home/user/.agents/skills/pdf-processing
Relative paths in this skill are relative to the skill directory.

<skill_resources>
  <file>scripts/extract.py</file>
  <file>scripts/merge.py</file>
  <file>references/pdf-spec-summary.md</file>
</skill_resources>
</skill_content>
```

This has practical benefits:

- The model can clearly distinguish skill instructions from other conversation content
- The harness can identify skill content during context compaction ([Step 5](#step-5-manage-skill-context-over-time))
- Bundled resources are surfaced to the model without being eagerly loaded


[​](#listing-bundled-resources)

Listing bundled resources

When a dedicated activation tool returns skill content, it can also enumerate supporting files (scripts, references, assets) in the skill directory — but it should **not eagerly read them**. The model loads specific files on demand using its file-read tools when the skill’s instructions reference them. For large skill directories, consider capping the listing and noting that it may be incomplete.


[​](#permission-allowlisting)

Permission allowlisting

If your agent has a permission system that gates file access, **allowlist skill directories** so the model can read bundled resources without triggering user confirmation prompts. Without this, every reference to a bundled script or reference file results in a permission dialog, breaking the flow for skills that include resources beyond the `SKILL.md` itself.


[​](#step-5-manage-skill-context-over-time)

Step 5: Manage skill context over time

Once skill instructions are in the conversation context, keep them effective for the duration of the session.


[​](#protect-skill-content-from-context-compaction)

Protect skill content from context compaction

If your agent truncates or summarizes older messages when the context window fills up, **exempt skill content from pruning**. Skill instructions are durable behavioral guidance — losing them mid-conversation silently degrades the agent’s performance without any visible error. The model continues operating but without the specialized instructions the skill provided. Common approaches:

- Flag skill tool outputs as protected so the pruning algorithm skips them
- Use the [structured tags](#structured-wrapping) from Step 4 to identify skill content and preserve it during compaction


[​](#deduplicate-activations)

Deduplicate activations

Consider tracking which skills have been activated in the current session. If the model (or user) attempts to load a skill that’s already in context, you can skip the re-injection to avoid the same instructions appearing multiple times in the conversation.


[​](#subagent-delegation-optional)

Subagent delegation (optional)

This is an advanced pattern only supported by some clients. Instead of injecting skill instructions into the main conversation, the skill is run in a **separate subagent session**. The subagent receives the skill instructions, performs the task, and returns a summary of its work to the main conversation. This pattern is useful when a skill’s workflow is complex enough to benefit from a dedicated, focused session.

[Using scripts](skill-creation-using-scripts.md)

---

## Client Showcase - Agent Skills

Source: https://agentskills.io/clients
Path: 08-Plugins-Skills/clients.md

# Client Showcase

Copy pageCopy page

Agent products that support the Agent Skills format.

Copy pageCopy page


Specification

---

## Install and manage plugins - Claude Code Docs

Source: https://code.claude.com/docs/en/discover-plugins
Path: 08-Plugins-Skills/discover-plugins.md

## On this page

- [Install a plugin](#install-a-plugin)
  - [Choose an install scope](#choose-an-install-scope)
  - [JetBrains, non-interactive runs, and the Agent SDK](#other-places-you-run-claude-code)
  - [Plugins from your claude.ai account](#plugins-from-your-claude-ai-account)
  - [Install from your shell](#install-from-your-shell)
- [Add a marketplace](#add-a-marketplace)
  - [Add a marketplace and install in one command](#add-a-marketplace-and-install-in-one-command)
  - [Add and install in a session](#add-and-install-in-a-session)
  - [Add and install from your shell](#add-and-install-from-your-shell)
  - [Add a private marketplace](#add-a-private-marketplace)
  - [Add a marketplace from claude.ai](#add-from-claude-ai)
- [Manage installed plugins](#manage-installed-plugins)
  - [Manage plugins synced from claude.ai](#manage-plugins-synced-from-claude-ai)
  - [Uninstall a plugin the project enables](#uninstall-a-plugin-the-project-enables)
  - [See what an installed plugin adds to your sessions](#see-what-an-installed-plugin-adds-to-your-sessions)
  - [Find plugins you no longer use](#find-plugins-you-no-longer-use)
  - [Plugins with dependencies](#plugins-with-dependencies)
  - [Manage plugins from your shell](#manage-plugins-from-your-shell)
- [Keep plugins updated](#keep-plugins-updated)
  - [Turn auto-update on or off for a marketplace](#turn-auto-update-on-or-off-for-a-marketplace)
  - [Update plugins now](#update-plugins-now)
  - [Auto-update from a private marketplace](#auto-update-from-a-private-marketplace)
- [Manage marketplaces](#manage-marketplaces)
- [Next steps](#next-steps)

Use plugins

# Install and manage plugins

Copy pageCopy page

Install Claude Code plugins from a marketplace on any surface you use, choose an install scope, and update or remove them later.

Copy pageCopy page

Installing a plugin adds its skills, agents, hooks, and MCP servers to Claude Code on your machine. This page is for anyone using plugins on their own machine or account, whether in the terminal, the desktop app, an IDE, or a cloud session: it covers installing, choosing a scope, adding marketplaces, and keeping plugins updated.

These cases are covered on other pages:

- **You use claude.ai chat or Cowork, not Claude Code**: see [Plugins on claude.ai and in Cowork](https://claude.com/docs/plugins/overview)
- **Claude Code printed an error**: find it in [Troubleshoot plugins](plugins-troubleshooting.md)

Start with [Install a plugin](#install-a-plugin). If someone sent you an install command whose `@` name isn’t `claude-plugins-official`, [add that marketplace](#add-a-marketplace) first.


[​](#install-a-plugin)

Install a plugin

As an example, this section installs [`commit-commands`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/commit-commands) from [Anthropic’s official marketplace](plugins-anthropic-marketplaces.md), which adds commands for committing, pushing, and opening pull requests. The same steps install any other plugin: substitute its name and its marketplace’s name wherever `commit-commands` and `claude-plugins-official` appear. If that plugin comes from a different marketplace, [add the marketplace](#add-a-marketplace) first. Pick the tab for where you run Claude Code.

- Terminal

- Desktop app

- VS Code

- Cloud session

Start Claude Code with `claude` in your project, then:

1

Open the plugin's details with the install command

Run `/plugin install` with the plugin’s name and marketplace. In a session, this command doesn’t install right away: it opens the `/plugin` panel on that plugin’s details so you can review it and choose a scope first.

```python
/plugin install commit-commands@claude-plugins-official
```

To browse instead, run `/plugin` with no plugin name: the panel opens on the **Discover** tab, which lists the plugins from your marketplaces, and you can type to search, then press **Enter** on a plugin to open its details.

2

Review what the plugin adds

The details pane shows the plugin’s description. It can also show:

- **Will install**: the commands, agents, skills, hooks, and MCP and LSP servers the plugin adds.
- **Last updated**: shown for a plugin in Anthropic’s official marketplace.
- **Context cost**: for a plugin in Anthropic’s official marketplace, two token estimates. **Every turn** is what the plugin adds to each message you send, and **When invoked** is what its skills and agents add once Claude loads them. The estimates appear when you open the plugin by naming its marketplace, as the step 1 command does, or from the **Marketplaces** tab. The details pane you reach from the **Discover** list doesn’t show them.

Plugins from a local or custom marketplace can show `Components will be discovered at installation` instead.A plugin can run hooks and MCP servers, so read the pane before you install. See [Plugin security and trust](plugins-security.md).

3

Choose a scope

Select one of the three install options:

- **Install for you (user scope)**: you get the plugin in every project on this machine
- **Install for all collaborators on this repository (project scope)**: it’s enabled for everyone who works in this repository
- **Install for you, in this repo only (local scope)**: you get it in this repository only

[Choose an install scope](#choose-an-install-scope) says which settings file each one writes to and which applies when the same plugin is set at more than one.After you select a scope, Claude Code installs the plugin along with any dependencies it declares, then prints an install summary.

4

Read the install summary

The last sentence of the summary tells you whether the plugin is usable in this session yet:

- **Active now**: `Plugin is now active.` No reload is needed.
- **Active, but a server needs setup**: `Plugin is now active.` is followed by `Its bundled MCP server needs configuration before it can start`. The plugin’s [bundled MCP server](plugins-components.md#include-a-packaged-mcpb-server) can’t start until you set its options. Select the plugin on the **Installed** tab in `/plugin` and choose **Configure** to set the server’s options.
- **Reload needed**: `Run /reload-plugins to apply.` The panel closes and Claude Code runs that reload for you. If the reload would [invalidate the prompt cache](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin), it warns and leaves the plugin pending instead. Run `/reload-plugins --force` to activate it anyway, which costs one uncached request.
- **Load failed**: `The plugin couldn't be loaded`. Open the **Errors** tab in `/plugin` for the reason, then see [After install: plugin not working](plugins-troubleshooting.md#plugin-installed-but-not-working).

5

Confirm the plugin works

Type `/` and look for the plugin’s skills under its name, in the form `/<plugin>:<skill>`. For `commit-commands`, `/commit-commands:commit` appears. Two other places list the plugin too:

- Open the **Installed** tab in `/plugin`, which lists the plugin with its scope.
- In your shell, run `claude plugin list`, which prints the same list with `Version`, `Scope`, and `Status` lines.

If `/commit-commands:commit` doesn’t appear, see [After install: plugin not working](plugins-troubleshooting.md#plugin-installed-but-not-working).

Installing from any other marketplace requires one extra step first: [add the marketplace](#add-a-marketplace). Claude Code adds Anthropic’s official marketplace for you the first time you start an interactive terminal session, which is why the example skips that step. If you found a plugin on [claude.com/marketplace](https://claude.com/marketplace), its **Claude Code** button copies the install command in its [shell form](#install-from-your-shell), `claude plugin install <name>@claude-plugins-official`.

In a local or SSH session in the desktop app’s **Code** tab:

1

Open the plugin browser

Click the **+** button next to the prompt box and select **Add plugins**. If the **+** menu shows **Plugins** instead of **Add plugins**, select **Plugins**, then **Browse plugins**. The plugin browser opens with the plugins from your marketplaces.

2

Select the plugin

Find `commit-commands` and select it.

3

Choose a scope

Choose a [scope](#choose-an-install-scope): your user account, this project, or local-only.

To enable, disable, or uninstall a plugin later, see [Install plugins in the desktop app](../16-Mobile-Desktop/desktop.md#install-plugins).

In the Claude Code panel in VS Code:

1

Open Manage plugins

Type `/plugins` in the prompt box to open **Manage plugins**.

2

Install the plugin

On the **Plugins** tab, search for `commit-commands` and click **Install**. If the tab lists no plugins, add `anthropics/claude-plugins-official` on the **Marketplaces** tab first.

3

Choose a scope

Choose a [scope](#choose-an-install-scope): **Install for you**, **Install for this project**, or **Install locally**.

Your changes apply to open sessions without a restart. See [Manage plugins in VS Code](../03-IDE-Integrations/vs-code.md#manage-plugins).

A [cloud session](../02-Claude-Code-CLI/cloud-environments.md), including [the browser at claude.ai/code](../02-Claude-Code-CLI/claude-code-on-the-web.md), has no plugin browser and doesn’t load the plugins you installed on your own machine or the ones your repository’s `.claude/settings.json` turns on. For plugins your organization distributes through managed settings, see [Manage plugins for your organization](plugins-org.md).See [which parts of your setup are also available in a cloud session](../02-Claude-Code-CLI/cloud-environments.md#what-carries-over-from-your-setup) for the rest of your setup.


[​](#choose-an-install-scope)

Choose an install scope

A plugin’s install scope decides who gets the plugin and which settings file records it as enabled:

- **User scope**: the plugin is enabled for you in every project on this machine. The entry goes in `enabledPlugins` in `~/.claude/settings.json`.
- **Project scope**: the plugin is enabled for everyone who works in this repository. The entry goes in `.claude/settings.json`, which you commit. Committing that entry turns the plugin on for your collaborators but doesn’t download it to their machines, so each collaborator also runs `claude plugin install <name>@<marketplace> --scope project` once; see [Enabled in project settings but not installed](plugins-loading.md#enabled-in-project-settings-but-not-installed).
- **Local scope**: the plugin is enabled for you in this repository only. The entry goes in `.claude/settings.local.json`.

Some plugins are set by their author to start turned off, through the [`defaultEnabled`](plugins-reference.md#defaultenabled) field. Such a plugin is installed but stays off until you turn it on with `claude plugin enable <name>` in your shell, or from the **Installed** tab of `/plugin` in a session. When the same plugin is set at several scopes, the local setting overrides the project setting, and the project setting overrides the user setting. See [Find where a plugin is enabled](plugins-loading.md#find-where-a-plugin-is-enabled) for the full rule. The terminal, the desktop app’s local sessions, and the VS Code extension on one computer read the same settings files, so a plugin you install at user scope in any of them is available in the other two.


[​](#other-places-you-run-claude-code)

JetBrains, non-interactive runs, and the Agent SDK

Some places you run Claude Code have no plugin browser of their own:

- **JetBrains IDEs**: the JetBrains plugin runs Claude Code in the IDE’s terminal, so use the **Terminal** tab’s steps there.
- **`claude -p` and other non-interactive runs**: `/plugin` doesn’t run, and Claude replies `/plugin isn't available in this environment.` Plugins you already installed do load. Install and manage them from your shell with [`claude plugin` commands](#install-from-your-shell).
- **Agent SDK**: load plugins through the SDK’s plugin option. See [Load plugins in the Agent SDK](../05-Agent-SDK/agent-sdk-plugins.md).

If Claude Code reports that a plugin enabled in the repository’s `.claude/settings.json` isn’t installed, see [Enabled in project settings but not installed](plugins-loading.md#enabled-in-project-settings-but-not-installed).

If you’re a plugin author testing a copy of your plugin on disk, start Claude Code from your shell with `--plugin-dir` to load it for one session instead of installing it. See [Flags that load a plugin for one session](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session).


[​](#plugins-from-your-claude-ai-account)

Plugins from your claude.ai account

Your claude.ai account is a separate source of plugins, alongside the marketplaces you install from:

- **What arrives**: every plugin you turn on for your claude.ai account, and every plugin your organization turns on for its members. In a terminal session they sync in the background each time you start Claude Code while signed in with that account; in Cowork sessions they download when the session starts.
- **Where you see them**: in `/plugin` and `claude plugin list` under the ID `<name>@synced`. You can turn one off at your own scope unless your organization requires it.
- **What doesn’t go the other way**: plugins you install with `/plugin` or `claude plugin install` stay on this machine and aren’t added to your claude.ai account.

For sync timing, sign-in requirements, and turning sync off, see [Plugins synced from claude.ai](plugins-loading.md#synced-plugins).


[​](#install-from-your-shell)

Install from your shell

Run `claude plugin install` in your shell to install a plugin without starting a Claude Code session, for example from a setup script.

- **Scope**: user scope by default. Pass `--scope project` or `--scope local` to change it.
- **When the plugins load**: plugins it installs load the next time you start Claude Code, or when you run `/reload-plugins` in a session that’s already open.
- **The marketplace on a new machine**: on a machine where no one has opened an interactive Claude Code session yet, the official marketplace isn’t registered, so a script that installs from it runs `claude plugin marketplace add anthropics/claude-plugins-official` before the install. See [Add and install from your shell](#add-and-install-from-your-shell).

```python
claude plugin install formatter@your-org --scope project
```

The command prints `Successfully installed plugin: formatter@your-org (scope: project)` when it finishes. Some plugins install by running a command that their marketplace names, called a [`command` source](plugins-marketplace-reference.md#command-plugin-source). Claude Code shows you that command and asks you to accept it before it runs. A script has no one to answer that prompt, so pass `--yes` there to accept it. For the other `claude plugin install` flags, see [plugin install](plugins-cli-reference.md#plugin-install).


[​](#add-a-marketplace)

Add a marketplace

You only need this section when the plugin you want isn’t in Anthropic’s official marketplace, for example one a coworker published or one from Anthropic’s community marketplace. A marketplace is a catalog of plugins, and Claude Code has to know about a marketplace before you can install from it. You add a marketplace once. After that, its plugins appear on the **Discover** tab and install with `/plugin install <plugin>@<marketplace>` in a session or `claude plugin install <plugin>@<marketplace>` in your shell, where `<marketplace>` is the name the marketplace registered under. To do both in one step, see [Add a marketplace and install in one command](#add-a-marketplace-and-install-in-one-command). In a Claude Code session, run `/plugin marketplace add` followed by the marketplace’s source: a GitHub repository, a git repository on any host, a local directory or file, or a hosted `marketplace.json`.

| Source                     | What you type                                                                                                                                                                                                                       | Example                                                                                                                        |
|:---------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|
| GitHub repository          | `owner/repo`. Add `#ref` to pin a branch or tag.                                                                                                                                                                                    | `/plugin marketplace add anthropics/claude-code`, or `/plugin marketplace add your-org/plugins#v1.2.0` to pin the `v1.2.0` tag |
| Git repository on any host | The full clone URL. Add `#ref` to pin a branch or tag.                                                                                                                                                                              | `/plugin marketplace add https://gitlab.example.com/your-group/your-marketplace.git#v1.0.0`                                    |
| Local directory or file    | A relative or absolute path to a directory that holds `.claude-plugin/marketplace.json`, or to the JSON file itself. Start a relative path with `./` or `../`, because Claude Code reads a bare `name/name` as a GitHub repository. | `/plugin marketplace add ./my-marketplace`                                                                                     |
| Hosted `marketplace.json`  | Its `https://` URL                                                                                                                                                                                                                  | `/plugin marketplace add https://example.com/marketplace.json`                                                                 |

From your shell, `claude plugin marketplace add` takes the same sources.

`/plugin market` also works as a shorter form of `/plugin marketplace`.

Include the `https://` prefix on every URL, or use the `git@host:path` form for SSH. If you type a bare `gitlab.example.com/your-group/your-marketplace.git`, Claude Code reads it as GitHub `owner/repo` shorthand and rejects it. When the command succeeds, it prints `Successfully added marketplace: <name>`, and the marketplace’s plugins appear on the **Discover** tab the next time you open `/plugin`, with no reload needed. If it fails, match the error message in [Troubleshoot plugins](plugins-troubleshooting.md#add-a-marketplace).


[​](#add-a-marketplace-and-install-in-one-command)

Add a marketplace and install in one command

To install a plugin from a marketplace you haven’t added yet, name the marketplace source with `--marketplace` on the install command, in a session or from your shell. The source takes [the same forms as `/plugin marketplace add`](#add-a-marketplace), such as GitHub `owner/repo`, a git URL, or a local path. Give the plugin name by itself, without an `@marketplace` suffix.


[​](#add-and-install-in-a-session)

Add and install in a session

Run `/plugin install` in a Claude Code session with the plugin and the source. Requires Claude Code v2.1.275 or later. In a session, the source can’t contain spaces.

```python
/plugin install deploy-helper --marketplace your-org/plugins
```

If you haven’t added that marketplace yet, Claude Code shows the source it resolved and asks you to confirm before adding it. Once the marketplace is added, the plugin’s details open and you choose an [installation scope](#install-a-plugin). If the source matches a marketplace you’ve already added, Claude Code skips the confirmation and opens the plugin’s details in that marketplace.


[​](#add-and-install-from-your-shell)

Add and install from your shell

In your shell, without starting a session, run `claude plugin install` with the plugin and the source. Requires Claude Code v2.1.292 or later.

```python
claude plugin install deploy-helper --marketplace your-org/plugins
```

The shell command adds the marketplace without a confirmation step. A marketplace you’ve already added from that source is reused. A new one is added under the same [organization policy checks](plugins-org.md#restrict-what-users-can-install) as `claude plugin marketplace add`, and is declared in your user settings even when you pass `--scope project`. If you haven’t added that marketplace yet, the command prints `Successfully added marketplace: <name> (declared in user settings)` and then [installs the plugin](#install-from-your-shell).


[​](#add-a-private-marketplace)

Add a private marketplace

A private marketplace is one in a repository you need credentials to clone, on GitHub or any other git host. You add it with the same `/plugin marketplace add` or `claude plugin marketplace add` command as a public one. Claude Code clones it with the git credentials already on your machine and never prompts, so each way of connecting has a requirement:

- **HTTPS**: your git credential helpers apply, so access you set up with `gh auth login`, the macOS Keychain, or `git-credential-store` works. Interactive prompts are suppressed, so a host you have never authenticated to fails instead of asking for a password.
- **SSH**: the host must already be in your `known_hosts` file and the key must work without a passphrase prompt. If your git setup names an SSH program in `GIT_SSH_COMMAND`, `GIT_SSH`, or your git config’s `core.sshCommand`, Claude Code runs that program.
- **GitHub `owner/repo` shorthand**: Claude Code checks whether your SSH key authenticates to `github.com`, then clones over SSH if it does and over HTTPS if it doesn’t. Set [`CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`](../02-Claude-Code-CLI/env-vars.md#variables) to skip that check and always clone over HTTPS.

The same credentials apply when you run `/plugin install`, `/plugin marketplace update`, and `claude plugin update`. On a GitHub Enterprise Server host, see [Plugin marketplaces on GHES](../02-Claude-Code-CLI/github-enterprise-server.md#plugin-marketplaces-on-ghes) for the credentials each operation needs. If your organization registers the marketplace for you through managed settings, you don’t add it yourself. See [Pre-install and require plugins](plugins-org.md#pre-install-and-require-plugins).


[​](#add-from-claude-ai)

Add a marketplace from claude.ai

In terminal sessions where [plugins sync from your claude.ai account](plugins-loading.md#synced-plugins), claude.ai can also list plugin marketplaces for you, such as your organization’s plugin library and your own claude.ai uploads. You add one of these by its name rather than by a source. Adding a marketplace from claude.ai requires Claude Code v2.1.273 or later. Add a claude.ai marketplace from the `/plugin` panel or from your shell:

- **Inside a session**: run `/plugin` and go to the **Marketplaces** tab, which lists the marketplaces from claude.ai. Select one there to add it.
- **From your shell**: run `claude plugin marketplace list`, which prints them in a `From claude.ai:` section. Then run `claude plugin marketplace add` with the `--claudeai` flag and the name shown in the list.

For example, this command adds a marketplace named `claudeai-organization-library`:

```python
claude plugin marketplace add --claudeai claudeai-organization-library
```

Claude Code registers the marketplace under a local name that starts with `claudeai-`, derived from the name that claude.ai lists it under. For example, a marketplace listed as “Organization library” becomes `claudeai-organization-library`. Install its plugins by that name, for example with `claude plugin install <plugin>@claudeai-organization-library`. If you sign out, or sign in to a different claude.ai organization, the marketplace stays configured but shows no plugins, and the plugins you already installed from it keep loading. The `From claude.ai:` section can also list git-based marketplaces shared through claude.ai, and it prints a source for each of those. Add them by that source as in [Add a marketplace](#add-a-marketplace), not with `--claudeai`.


[​](#manage-installed-plugins)

Manage installed plugins

The **Installed** tab in `/plugin` lists your plugins with actions to enable, disable, update, or uninstall each one. In a Claude Code session, run `/plugin` and press **Tab** to reach it, or run `/plugin enable`, `/plugin disable`, or `/plugin uninstall` to open the panel and make that change there. Disabled plugins are grouped under a collapsed header at the bottom of the list. Use these keys on the list:

- Type to filter by name or description.
- Press **Space** to enable or disable the selected plugin, and **f** to favorite it.
- Press **Enter** to open a plugin’s details.

A plugin’s details menu offers **Disable plugin** or **Enable plugin**, **Update now**, and **Uninstall**. Two more items appear for plugins that take settings, and a plugin can show both:

- **Configure options**: shown when the plugin’s manifest declares [`userConfig` options](plugins-reference.md#user-configuration). Opens the dialog for those options
- **Configure**: shown when the plugin includes a [bundled MCP server](plugins-components.md#include-a-packaged-mcpb-server). Sets that server’s own `user_config` settings

The tab can also show plugins at **Managed** scope. Your organization installed those through [managed settings](../02-Claude-Code-CLI/settings.md#settings-files), and you can’t enable, disable, or uninstall them here. For a synced plugin that your organization requires on claude.ai, see [Manage plugins synced from claude.ai](#manage-plugins-synced-from-claude-ai). When you close the `/plugin` panel with pending changes you made in it, Claude Code runs `/reload-plugins` for you to apply them. If the reload would [invalidate the prompt cache](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin), it warns and leaves the changes pending instead. Run `/reload-plugins --force` to apply them anyway.


[​](#manage-plugins-synced-from-claude-ai)

Manage plugins synced from claude.ai

The **Installed** tab in `/plugin` also lists the [plugins synced from your claude.ai account](plugins-loading.md#synced-plugins), with `synced` as their source. Synced plugins appear in terminal sessions on Claude Code v2.1.273 or later.

- **Enable or disable**: use the **Installed** tab, unless your organization marked the plugin as required.
- **Remove**: turn the plugin off on claude.ai.

When Claude Code syncs an added, updated, or removed plugin into an interactive session, you see `Plugins changed. Run /reload-plugins to activate.` Run `/reload-plugins` to load the change in that session, or leave it for the next time you start Claude Code.


[​](#uninstall-a-plugin-the-project-enables)

Uninstall a plugin the project enables

When you choose **Uninstall** for a plugin that this repository’s `.claude/settings.json` enables, whether from the **Installed** tab or with `/plugin uninstall`, Claude Code asks whether to disable it for you or uninstall it for everyone:

- **Disable for me**: press **y**. Claude Code writes `false` for the plugin in your `.claude/settings.local.json` and leaves it installed for the project.
- **Uninstall for everyone**: press **u**. Claude Code removes the plugin from the shared `.claude/settings.json`.


[​](#see-what-an-installed-plugin-adds-to-your-sessions)

See what an installed plugin adds to your sessions

In your shell, run `claude plugin details <name>` for an installed plugin. The `Always-on` line is the number of tokens the plugin adds to every session where it’s enabled, and the per-component rows show which skill or agent contributes most. For the full output and what each figure means, see [Measure what a plugin costs](plugins-measure.md#measure-what-a-plugin-costs).


[​](#find-plugins-you-no-longer-use)

Find plugins you no longer use

On the **Installed** tab in `/plugin`, plugins you installed yourself and haven’t used recently appear under a **Not used recently** header, and each plugin’s details show a **Last used** line. Use that header and that line to find plugins that still add startup and context cost, then disable or uninstall them.


[​](#plugins-with-dependencies)

Plugins with dependencies

A plugin can declare other plugins that it depends on. When you install, disable, or uninstall such a plugin from a marketplace, Claude Code acts on those dependencies too:

- **Install**: Claude Code also installs and enables the plugin’s declared dependencies at the same scope. The success message lists them.
- **Enable**: Claude Code also enables the plugin’s dependencies that are installed but disabled. If a declared dependency isn’t installed, the enable fails and the message tells you to install it first.
- **Disable**: when another enabled plugin still needs the one you named, Claude Code refuses and prints a chained command that disables both in the right order.
- **Uninstall**: auto-installed dependencies stay until you run `claude plugin prune` in your shell; see [plugin prune](plugins-cli-reference.md#plugin-prune).

If you loaded the plugin with `--plugin-dir` instead, see [Test a plugin and its dependency locally](plugins-dependencies.md#test-a-plugin-and-its-dependency-locally).


[​](#manage-plugins-from-your-shell)

Manage plugins from your shell

You can also manage plugins without starting a Claude Code session. In your shell, run `claude plugin install`, `enable`, `disable`, or `uninstall` as ordinary terminal commands; they change the same settings the `/plugin` panel does. Each takes `--scope` to target one scope, and uses a default scope when you omit it:

- `enable` and `disable` act on the most specific scope whose settings already list the plugin.
- `install` and `uninstall` act on user scope.

For example, these commands disable and re-enable a plugin, then uninstall it at project scope:

```python
claude plugin disable formatter@your-org
claude plugin enable formatter@your-org
claude plugin uninstall formatter@your-org --scope project
```


[​](#keep-plugins-updated)

Keep plugins updated

Plugins update automatically when the marketplace they came from has auto-update turned on. After a session starts, Claude Code refreshes those marketplaces and updates the on-disk copies of the plugins you installed from them. The running session keeps the versions it already loaded. After an update, you see `Plugin updated: <name> · Run /reload-plugins to apply`, and the next session loads the new versions automatically. These are the auto-update defaults for each kind of marketplace:

- **On by default**: `claude-plugins-official` and the other [official marketplace names](plugins-security.md#official-marketplace-names) except `knowledge-work-plugins` and `first-party-plugins`, plus [marketplaces added from claude.ai](#add-from-claude-ai).
- **Off by default**: every other marketplace, including the community marketplace, third-party marketplaces, and local development marketplaces.

For when auto-update runs, which plugins it skips, and the environment variables that turn it off, see [When auto-update runs](plugins-loading.md#when-auto-update-runs).


[​](#turn-auto-update-on-or-off-for-a-marketplace)

Turn auto-update on or off for a marketplace

In a Claude Code session, run `/plugin` and go to the **Marketplaces** tab. Select the marketplace, then select **Enable auto-update** or **Disable auto-update**.


[​](#update-plugins-now)

Update plugins now

To update one plugin, open it on the **Installed** tab in `/plugin` during a session and select **Update now**, or run `claude plugin update <plugin>@<marketplace>` in your shell. There’s no command that updates every plugin at once. To update the plugins you installed from one marketplace at once, go to the **Marketplaces** tab in `/plugin`, select the marketplace, and choose **Update marketplace**. That refreshes the marketplace’s listing, updates the plugins you installed from it, and reports any plugin it left for you to update yourself. A plugin with a [`command` source](plugins-marketplace-reference.md#command-plugin-source) or with a marketplace entry that sets a `headersHelper` command isn’t updated this way, so you update it from its view on the **Installed** tab or with `claude plugin update <plugin>@<marketplace>`. If you run `claude plugin marketplace update` in your shell with no name, it refreshes every marketplace’s listing but leaves your installed plugins at their current versions.


[​](#auto-update-from-a-private-marketplace)

Auto-update from a private marketplace

For a private marketplace, see [What background auto-update does with credentials](plugins-host-marketplace.md#what-background-auto-update-does-with-credentials) for how background auto-updates authenticate over SSH and HTTPS, and [Troubleshoot plugins](plugins-troubleshooting.md#add-a-marketplace) for the messages you see when they fail.


[​](#manage-marketplaces)

Manage marketplaces

The **Marketplaces** tab in `/plugin` lists every marketplace you registered, along with its source. Select one to browse its plugins, update its listing, turn auto-update on or off, or remove it. You can also list, update, and remove marketplaces with commands, from your shell or inside a session:

| Action                         | In your shell                             | Inside a session                    |
|:-------------------------------|:------------------------------------------|:------------------------------------|
| List marketplaces              | `claude plugin marketplace list`          | `/plugin marketplace list`          |
| Update a marketplace’s listing | `claude plugin marketplace update <name>` | `/plugin marketplace update <name>` |
| Remove a marketplace           | `claude plugin marketplace remove <name>` | `/plugin marketplace remove <name>` |

When you remove a marketplace, Claude Code uninstalls every plugin you installed from it and removes their `enabledPlugins` entries from your settings files. The **Marketplaces** tab names those plugins before it asks you to confirm.


[​](#next-steps)

Next steps

- [Anthropic’s marketplaces](plugins-anthropic-marketplaces.md): how the official, community, and demo marketplaces differ and where to browse each one
- [Plugin loading reference](plugins-loading.md): why a plugin loaded, didn’t load, or didn’t change after an update
- [Plugin security and trust](plugins-security.md): what to review before you install a plugin from a marketplace you don’t know
- [Troubleshoot plugins](plugins-troubleshooting.md): install and marketplace error messages with their fixes
- [Create a plugin](plugins-create.md): build your own

---

## Improving frontend design through Skills | Claude by Anthropic

Source: https://www.claude.com/blog/improving-frontend-design-through-skills
Path: 08-Plugins-Skills/improving-frontend-design-through-skills.md

# Improving frontend design through Skills

Best practices for building richer, more customized frontend design with Claude and Skills.

- 

  Category[Best practices](https://www.claude.com/resources/best-practices)
- 

  ProductClaude apps
- 

  DateNovember 12, 2025
- 

  Reading time13 min
- 


You might notice that when you ask an LLM to build a landing page without guidance, it will almost always conform to Inter fonts, purple gradients on white backgrounds, and minimal animations.

The issue? [Distributional convergence.](https://en.wikipedia.org/wiki/Convergence_of_random_variables) During sampling, models predict tokens based on statistical patterns in training data. Safe design choices–those that work universally and offend no one–dominate web training data. Without direction, Claude samples from this high-probability center.

For developers building customer-facing products, this generic aesthetic undermines brand identity and makes AI-generated interfaces immediately recognizable—and dismissible.

### The steerability challenge

The good news is that Claude is highly steerable with the right prompting. Tell Claude to "avoid Inter and Roboto" or "use atmospheric backgrounds instead of solid colors," and results improve immediately. This sensitivity to guidance is a feature; it means Claude can adapt to different design contexts, constraints, and aesthetic preferences.

But this creates a practical challenge: the more specialized the task, the more context you need to provide. For frontend design, effective guidance spans typography principles, color theory, animation patterns, and background treatment. You need to specify which defaults to avoid and which alternatives to prefer across multiple dimensions.

You could pack all this into a system prompt, but then every request–debugging Python, analyzing data, writing emails–carries frontend design context. The question becomes: how do you provide Claude with domain-specific guidance exactly when needed, without permanent context overhead for unrelated tasks?

## **Skills: dynamic context loading**

This is precisely what [Skills](../19-Reference/skills.md) were designed for: delivering specialized context on demand without permanent overhead. A skill is a document (often markdown) containing instructions, constraints, and domain knowledge, stored in a designated directory that Claude can access through simple file-reading tools. Claude can leverage these skills to dynamically load in information it needs at runtime, progressively enhancing its context instead of loading everything upfront.

When equipped with these skills and the necessary tools to read them, Claude can autonomously identify and load relevant skills based on the task at hand. For instance, when asked to build a landing page or create a React component, Claude can load a frontend design skill and apply its instructions just-in-time. This is the essential mental model: skills are prompts and contextual resources that activate on demand, providing specialized guidance for specific task types without incurring permanent context overhead.

This allows developers to reap the benefits of Claude’s steerability without overloading the context window by stuffing disparate instructions across many tasks into the system prompt. As we’ve [previously explained,](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) too many tokens in the context window can result in degradation of performance, so keeping the contents of the context window lean and focused is extremely important for eliciting the best performance from the model. Skills solve for this by making effective prompts reusable and contextual.

## **Prompting for better frontend output**

We can unlock significantly better UI generations from Claude, without permanent context overhead, by creating a frontend design skill. The core insight is to think about frontend design the way a frontend engineer would. The more you can map aesthetic improvements to implementable frontend code, the better Claude can execute.

Leveraging this insight, we identified several areas where targeted prompting works well: typography, animations, background effects, and themes. These all translate cleanly to code that Claude can write. Implementing this in your prompts does not require detailed technical instructions, just using targeted language that engages the model to think more critically about these design axes is enough to elicit stronger outputs. This maps closely with the guidance we provided in our [context engineering](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) blog article, about prompting the model at the right altitude, avoiding the two extremes of low-altitude hardcoded logic like specifying exact hex codes and vague high-altitude guidance that assumes shared context.

### **Typography**

To see this in action, let's start by viewing typography as one dimension we can influence via prompting. The prompt below specifically steers Claude to use more interesting fonts:

Copy

``` CodeBlock-module-scss-module__aqLerq__pre
<use_interesting_fonts>
Typography instantly signals quality. Avoid using boring, generic fonts.

Never use: Inter, Roboto, Open Sans, Lato, default system fonts

Here are some examples of good, impactful choices:
- Code aesthetic: JetBrains Mono, Fira Code, Space Grotesk
- Editorial: Playfair Display, Crimson Pro
- Technical: IBM Plex family, Source Sans 3
- Distinctive: Bricolage Grotesque, Newsreader

Pairing principle: High contrast = interesting. Display + monospace, serif + geometric sans, variable font across weights.

Use extremes: 100/200 weight vs 800/900, not 400 vs 600. Size jumps of 3x+, not 1.5x.

Pick one distinctive font, use it decisively. Load from Google Fonts.
</use_interesting_fonts>
```

**Output generated with base prompt:**

**Output generated with base prompt and typography section**


Interestingly, the mandate to use more interesting fonts seems to encourage the model to improve other aspects of the design as well.

Typography alone leads to significant improvement, but fonts are just one dimension. What about cohesive aesthetics across the entire interface?

### **Themes**

Another dimension we can prompt for is designs inspired by well-known themes and aesthetics. Claude has a rich understanding of popular themes; we can use this to communicate the specific aesthetics we want our frontend to embody. Here’s an example:

Copy

``` CodeBlock-module-scss-module__aqLerq__pre
<always_use_rpg_theme>
Always design with RPG aesthetic:
- Fantasy-inspired color palettes with rich, dramatic tones
- Ornate borders and decorative frame elements
- Parchment textures, leather-bound styling, and weathered materials
- Epic, adventurous atmosphere with dramatic lighting
- Medieval-inspired serif typography with embellished headers
</always_use_rpg_theme>
```

This produces the following RPG-themed UI:

Typography and themes show targeted prompting works. But manually specifying each dimension is tedious. What if we could combine all these improvements into one reusable asset?

### **A general-purpose prompt**

The same principle extends to other design dimensions: prompting for motion (animations and micro-interactions) adds polish that static designs lack, while guiding the model toward more interesting background choices creates depth and visual interest. This is where a comprehensive skill shines.

Bringing this all together, we developed a ~400 token prompt – compact enough to load without bloating context (even when loaded as a skill) – that dramatically improves frontend output across typography, color, motion, and backgrounds:

Copy

``` CodeBlock-module-scss-module__aqLerq__pre
<frontend_aesthetics>
You tend to converge toward generic, "on distribution" outputs. In frontend design,this creates what users call the "AI slop" aesthetic. Avoid this: make creative,distinctive frontends that surprise and delight. 

Focus on:
- Typography: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics.
- Color & Theme: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Draw from IDE themes and cultural aesthetics for inspiration.
- Motion: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions.
- Backgrounds: Create atmosphere and depth rather than defaulting to solid colors. Layer CSS gradients, use geometric patterns, or add contextual effects that match the overall aesthetic.

Avoid generic AI-generated aesthetics:
- Overused font families (Inter, Roboto, Arial, system fonts)
- Clichéd color schemes (particularly purple gradients on white backgrounds)
- Predictable layouts and component patterns
- Cookie-cutter design that lacks context-specific character

Interpret creatively and make unexpected choices that feel genuinely designed for the context. Vary between light and dark themes, different fonts, different aesthetics. You still tend to converge on common choices (Space Grotesk, for example) across generations. Avoid this: it is critical that you think outside the box!
</frontend_aesthetics>
```

In the example above, we start by giving Claude general context on the problem and what we're trying to solve for. We've found that giving the model this type of high-level context is a helpful prompting tactic to calibrate outputs. We then identify the vectors of improved design we discussed before and give targeted advice to encourage the model to think more creatively across all of these dimensions.

We also include additional guidance at the end to prevent Claude from converging to a different local maximum. Even with explicit instructions to avoid certain patterns, the model can default to other common choices (like Space Grotesk for typography). The final reminder to "think outside the box" reinforces creative variation.

### **Impact on frontend design**

With this skill active, Claude's output improves across several types of frontend designs, including:

**Example 1: SaaS landing page**

**Example 2: Blog layout**

**Example 3: Admin dashboard**


## **Improving artifact quality in [claude.ai](http://claude.ai) with Skills**

Design taste isn't the only limitation. Claude also faces architectural constraints when building artifacts. [Artifacts](https://support.claude.com/en/articles/9487310-what-are-artifacts-and-how-do-i-use-them) are interactive, editable content (like code or documents) that Claude creates and displays alongside your chat.

In addition to the issue with design taste explored above, Claude has another default behavior that limits its ability to generate fantastic frontend artifacts in [claude.ai](http://claude.ai). Currently, when asked to create a frontend, Claude just builds a single HTML file with CSS and JS. This is because Claude understands that frontends must be single HTML files to be properly rendered as artifacts.

In the same way you’d expect a human developer to only be able to create very basic frontends if they could only write HTML/CSS/JS in a single file, we hypothesized that Claude would be able to generate more impressive frontend artifacts if we gave it instructions to use richer tooling.

This led us to create a [web-artifacts-builder skill](https://github.com/anthropics/skills/blob/main/web-artifacts-builder/SKILL.md) which leverages Claude’s ability to [use a computer](https://claude.com/resources/articles/create-files) and guides Claude to build artifacts using multiple files and modern web technologies like [React](https://react.dev/), [Tailwind CSS](https://tailwindcss.com/) and [shadcn/ui](https://ui.shadcn.com/). Under the hood, the skill exposes scripts that (1) help Claude efficiently set up a basic React repo and (2) bundle everything into a single file using [Parcel](https://parceljs.org/) to meet the single-HTML-file requirement after it is done editing. This is one of the core benefits of skills - by giving Claude access to scripts to execute boilerplate actions, Claude is able to minimize token usage while increasing reliability and performance.

With the web-artifacts-builder skill, Claude could leverage shadcn/ui's form components and Tailwind's responsive grid system to create a more comprehensive artifact.

**Example 1: Whiteboard app**

For example, when prompted to create a whiteboard app without the web-artifacts-builder skill, Claude outputted a very basic interface:

On the other hand, when using the new web-artifacts-builder skill, Claude generated a much cleaner and more featureful application out-of-the-box that included drawing different shapes and text:


**Example 2: Task Manager App**

Similarly, when asked to create a task management app, without the skill, Claude generated a functional but very minimal application:

With the skill, Claude generated an app that was more featureful out of the box. For example, Claude included a “Create New Task” form component that allows users to set an associated Category and Due Date on tasks:


To try out this new skill in [Claude.ai](http://claude.ai), simply enable the skill and then ask Claude to “use the web-artifacts-builder skill” when building artifacts.

## **Optimizing Claude’s frontend design capabilities with Skills**

This frontend design skill demonstrates a broader principle about language model capabilities: models often have the ability to do more than they express by default. Claude has strong design understanding, but distributional convergence obscures it without guidance. While you could add these instructions to your system prompt, this entails that every request carries frontend design context, even when this knowledge isn’t relevant to the task at hand. Instead, using Skills transforms Claude from a tool that needs constant guidance into one that brings domain expertise to every task.

Skills are also highly customizable – you can create your own tailored to your specific needs. This allows you to define the exact primitives you want to bake into the skill, whether that's your company's design system, specific component patterns, or industry-specific UI conventions. By encoding these decisions into a Skill, you transform component parts of an agent’s thinking into a reusable asset that your entire development team can leverage. The skill becomes organizational knowledge that persists and scales, ensuring consistent quality across projects.

This pattern extends beyond frontend work. Any domain where Claude produces generic outputs despite having more expansive understanding is a candidate for Skill development. The method is consistent: identify convergent defaults, provide concrete alternatives, structure guidance at the right altitude, and make it reusable through Skills.

For frontend development, this means Claude can generate distinctive interfaces without per-request prompt engineering. To get started, explore our [frontend design cookbook](../10-Prompting-Guides/claude-cookbooks-coding-prompting-for-frontend-aesthetics.md) or try out our [new frontend design plugin in Claude Code](https://github.com/anthropics/claude-code/tree/main/plugins/frontend-design).

**Feeling inspired? To create your own frontend skills, check out our [skill-creator](https://github.com/anthropics/skills/tree/main/skill-creator).**


**Acknowledgements**  
Written by Anthropic's Applied AI team: Prithvi Rajasekaran, Justin Wei, and Alexander Bricken, alongside our marketing partners Molly Vorwerck and Ryan Whitehead.


### Agent Skills

Start using Skills with Claude to build more powerful applications today.

[Get started](https://claude.com/resources/articles/skills)

## Related articles

Explore more product news and best practices for teams building with Claude.


ArticleOct 22, 2026

### Building agents: When to fold subagents into your main agent loop

When building agents in Claude Platform, using fewer subagents in favor of progressively disclosed context and tools can improve quality while reducing cost and latency. Here’s when and how to migrate.

Claude Platform


ArticleOct 19, 2026

### Redesigning source control at Anthropic

As merges to Anthropic's monorepo grew from about 2,500 to more than 30,000 a day, source control became the biggest development bottleneck. Here is how the team built Scion, an in-house version control system designed for always-online agents, and migrated off git in five months.

Claude Code

(opens in new tab)[](https://www.claude.com/resources/articles/how-an-anthropic-japan-business-development-representative-runs-his-day)

ArticleOct 16, 2026

### How an Anthropic BDR in Japan runs his day with Claude Managed Agents

How one business development representative clears his inbound queue and drafts bilingual customer emails with Claude Managed Agents and Claude Tag.

Claude Managed Agents


ArticleOct 16, 2026

### How the Claude Code team does code review

How do you review code at the volume agents produce it? The Claude Code team shares its review process, from Claude verifying its own work to risk-tiered human approval, plus a checklist for adopting it.

Claude Code

(opens in new tab)

---

## Constrain plugin dependency versions - Claude Code Docs

Source: https://code.claude.com/docs/en/plugin-dependencies
Path: 08-Plugins-Skills/plugin-dependencies.md

## On this page

- [Why constrain dependency versions](#why-constrain-dependency-versions)
- [Declare a dependency with a version constraint](#declare-a-dependency-with-a-version-constraint)
- [Bundle plugins for a team](#bundle-plugins-for-a-team)
- [Depend on a plugin from another marketplace](#depend-on-a-plugin-from-another-marketplace)
- [Test a plugin and its dependency locally](#test-a-plugin-and-its-dependency-locally)
- [Tag plugin releases for version resolution](#tag-plugin-releases-for-version-resolution)
- [How constraints interact](#how-constraints-interact)
- [Enable or disable a plugin with dependencies](#enable-or-disable-a-plugin-with-dependencies)
- [Remove orphaned auto-installed dependencies](#remove-orphaned-auto-installed-dependencies)
- [Resolve dependency errors](#resolve-dependency-errors)
- [See also](#see-also)

Plugin distribution

# Constrain plugin dependency versions

Copy pageCopy page

Declare version constraints on plugin dependencies, and bundle a curated plugin set behind one install.

Copy pageCopy page

A plugin can depend on other plugins by listing them in `plugin.json` or in its marketplace entry. By default, a dependency tracks the latest available version, so an upstream release can change the dependency under your plugin without warning. Version constraints let you hold a dependency at a tested version range until you choose to move. When you install a plugin that declares dependencies, Claude Code resolves and installs them automatically, apart from a dependency whose marketplace entry has a [`command` source](plugin-marketplaces.md#how-users-accept-the-command) or a [`headersHelper`](plugin-marketplaces.md#how-users-accept-a-headershelper-command), which you install yourself first. Later, `/reload-plugins`, auto-update of the dependent plugin’s marketplace, re-running `claude plugin install` on the dependent plugin, and `claude plugin marketplace add` each install any declared dependency that isn’t installed yet, under the same rules; if one stays unresolved, see [Resolve dependency errors](#resolve-dependency-errors). This guide is for plugin authors who declare dependencies in `plugin.json` and for marketplace maintainers who tag releases. Dependencies here are other plugins; for the npm and Bun packages a plugin itself uses, see [Node.js package dependencies](plugins-reference.md#node-js-package-dependencies). To install plugins that have dependencies, see [Discover and install plugins](discover-plugins.md). For the full manifest schema, see the [Plugins reference](plugins-reference.md).


[​](#why-constrain-dependency-versions)

Why constrain dependency versions

Consider an internal marketplace where two teams publish plugins. The platform team maintains `secrets-vault`, an MCP server that wraps a secrets backend. The deploy team maintains `deploy-kit`, which calls `secrets-vault` to fetch credentials during deploys. `deploy-kit` is tested against `secrets-vault` v2.1.0. Without a version constraint, the next time the platform team tags a release that renames an MCP tool, auto-update moves every engineer’s `secrets-vault` to the new version and `deploy-kit` breaks. With a version constraint, `deploy-kit` declares that it needs `secrets-vault` in the `~2.1.0` range. Engineers with `deploy-kit` installed stay on the highest matching `2.1.x` patch. The deploy team upgrades on their own schedule by publishing a new `deploy-kit` version with a wider constraint.


[​](#declare-a-dependency-with-a-version-constraint)

Declare a dependency with a version constraint

List dependencies in the `dependencies` array of your plugin’s `.claude-plugin/plugin.json`. The following manifest declares one unversioned dependency and one constrained dependency:

.claude-plugin/plugin.json

```python
{
  "name": "deploy-kit",
  "version": "3.1.0",
  "dependencies": [
    "audit-logger",
    { "name": "secrets-vault", "version": "~2.1.0" }
  ]
}
```

An entry can be a bare string with only the plugin name, like `"audit-logger"` in the `deploy-kit` manifest, which depends on whatever version that plugin’s marketplace provides. For more control, use an object with these fields:

| Field         | Type   | Description                                                                                                                                                                                                                                                             |
|:--------------|:-------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`        | string | Plugin name. Resolves within the same marketplace as the declaring plugin. Required.                                                                                                                                                                                    |
| `version`     | string | A [semver range](https://github.com/npm/node-semver#ranges) such as `~2.1.0`, `^2.0`, `>=1.4`, or `=2.1.0`. The dependency is fetched at the highest tagged version that satisfies this range.                                                                          |
| `marketplace` | string | A different marketplace to resolve `name` in. Cross-marketplace dependencies are blocked unless the target marketplace is listed in [`allowCrossMarketplaceDependenciesOn`](#depend-on-a-plugin-from-another-marketplace) in the root marketplace’s `marketplace.json`. |

Pre-release versions such as `2.0.0-beta.1` are excluded unless your range opts in with a pre-release suffix like `^2.0.0-0`.


[​](#bundle-plugins-for-a-team)

Bundle plugins for a team

Besides the required `name`, a plugin manifest can consist of only a `dependencies` array. Installing it pulls in every dependency, which makes it a way to package a curated plugin set behind one install. For example, a platform team can publish role-specific bundles in an internal marketplace so engineers run one `claude plugin install` instead of installing each tool separately:

.claude-plugin/plugin.json

```python
{
  "name": "backend-standard",
  "version": "1.0.0",
  "description": "Standard plugin set for backend engineers",
  "dependencies": [
    "secrets-vault",
    "deploy-kit",
    { "name": "db-migrate", "version": "^3.0" },
    "oncall-runbook"
  ]
}
```

Installing `backend-standard` resolves and installs all four dependencies. To add a tool to the standard set later, publish a new `backend-standard` version with the extra dependency. Unless the marketplace [auto-updates](discover-plugins.md#configure-auto-updates), engineers pick up the new version in one of two ways:

- Enable auto-update for the marketplace in `/plugin`. The next auto-update moves the bundle to the new version and installs any dependencies it adds.
- Run `claude plugin update backend-standard`, then `/reload-plugins` to install the newly added dependencies.

To roll bundles out across an organization, add the bundle plugin to `enabledPlugins` in [managed settings](../02-Claude-Code-CLI/settings-reference.md#enabledplugins).


[​](#depend-on-a-plugin-from-another-marketplace)

Depend on a plugin from another marketplace

By default, Claude Code refuses to auto-install a dependency that lives in a different marketplace than the plugin declaring it. This prevents one marketplace from silently pulling in plugins from a source you have not reviewed. To allow it, the maintainer of the root marketplace adds the target marketplace name to `allowCrossMarketplaceDependenciesOn` in `marketplace.json`. The root marketplace is the one that hosts the plugin the user is installing; only its allowlist is consulted, so trust does not chain through intermediate marketplaces. The following `marketplace.json` allows `deploy-kit` to depend on a plugin from `acme-shared`:

.claude-plugin/marketplace.json

```python
{
  "name": "acme-tools",
  "owner": { "name": "Acme" },
  "allowCrossMarketplaceDependenciesOn": ["acme-shared"],
  "plugins": [
    {
      "name": "deploy-kit",
      "source": "./deploy-kit",
      "dependencies": [
        { "name": "audit-logger", "marketplace": "acme-shared" }
      ]
    }
  ]
}
```

If the field is missing or does not include the target marketplace, install fails with a `cross-marketplace` error naming the field to set. Users can still install the dependency manually first, which satisfies the constraint without changing the allowlist.


[​](#test-a-plugin-and-its-dependency-locally)

Test a plugin and its dependency locally

If you’re developing a plugin and the plugin it depends on at the same time, load both with `--plugin-dir`:

```python
claude --plugin-dir ./my-dependency --plugin-dir ./my-plugin
```

The local copy of the dependency satisfies your plugin’s dependency entry, even when the entry names a marketplace, so you don’t need to install the dependency from its marketplace. Claude Code doesn’t check a [version constraint](#declare-a-dependency-with-a-version-constraint) against a local copy, so the local `plugin.json` doesn’t need a `version`. Before v2.1.242, a dependency entry that named a marketplace never matched the local copy, and Claude Code disabled your plugin at load. When both plugins sit in one parent folder, you can pass that folder to `--plugin-dir` once. If the folder isn’t itself a plugin, Claude Code loads each child folder that has a `.claude-plugin/plugin.json`. Requires Claude Code v2.1.265 or later. If you haven’t installed the dependency from its marketplace, your plugin stops loading when the local copy goes away:

- **You disabled the local copy**: Claude Code disables your plugin at the next plugin load. For a dependency entry that names a marketplace, Claude Code reports `Dependency "<name>@inline" is disabled — enable it or remove the dependency`; for a bare-name entry, it reports the dependency by its bare name. `<name>@inline` is how Claude Code identifies every `--plugin-dir` and `--plugin-url` plugin.
- **You started a session without the dependency’s `--plugin-dir` flag**: Claude Code reports the dependency as not installed. Pass the flag again, or install the dependency from its marketplace.


[​](#tag-plugin-releases-for-version-resolution)

Tag plugin releases for version resolution

Claude Code resolves version constraints against git tags on the repository that hosts the dependency: the plugin’s own repository for `github`, `url`, and `git-subdir` [plugin sources](plugin-marketplaces.md#plugin-sources), or the marketplace repository for a plugin the marketplace references by a relative path. For Claude Code to find a dependency’s available versions, the upstream plugin’s releases must be tagged using a specific naming convention. Tag each release as `{plugin-name}--v{version}`, where `{version}` matches the `version` field in that commit’s `plugin.json`. From the plugin directory, run:

```python
claude plugin tag --push
```

The `claude plugin tag` command derives the tag name from the plugin’s manifest and the enclosing marketplace entry. Before creating the tag, it validates the plugin contents, checks that `plugin.json` and the marketplace entry agree on the version, requires a clean working tree under the plugin directory, and refuses if the tag already exists.

- `--push` pushes the tag to the `origin` remote, so the repository needs a configured `origin` remote. Pass `--remote` to push to a different one.
- If the push fails, the tag is still created locally and the command exits with an error.
- With `--push`, a successful run ends with `Created tag secrets-vault--v2.1.0` and `Pushed to origin`, where the last line names the remote it pushed to. Without `--push`, the command prints the `git push` command to run instead.
- `--dry-run` prints what would be tagged without creating it.

Running `git tag secrets-vault--v2.1.0` directly is equivalent if you keep `plugin.json` and the marketplace entry in sync yourself. The plugin name prefix lets one marketplace repository host multiple plugins with independent version lines. The `--v` separator is parsed as a prefix match on the full plugin name, so plugin names that contain hyphens are handled correctly. When you install a plugin that declares `{ "name": "secrets-vault", "version": "~2.1.0" }`, Claude Code lists the tags on the repository that hosts `secrets-vault`, filters to those starting with `secrets-vault--v`, and fetches the highest version satisfying `~2.1.0`. If no tag on the plugin’s own repository satisfies the range, the install fails with `Dependency "secrets-vault@acme-tools" has no git tag satisfying ~2.1.0`, which names the dependency together with its marketplace. For a relative-path plugin with no matching tag, Claude Code installs the marketplace’s current copy instead and checks the constraint when the plugin loads. For a plugin the marketplace references by a relative path, a marketplace added as a local folder path resolves tags the same way when the folder is a git repository. This requires Claude Code v2.1.196 or later. In two cases Claude Code installs the dependency from the folder’s current contents instead:

- Earlier versions don’t read tags from a local-folder marketplace, so a constrained dependency loads only if that copy satisfies the range.
- A local folder that isn’t a git repository has no tags, regardless of version.

The resolved tag’s semver is recorded separately from `plugin.json`’s `version`, so constraint checks use the tag that was actually fetched even if `plugin.json` at that commit has a stale value. The cache directory name for a tag-resolved install includes a 12-character commit-SHA suffix, so if a maintainer force-moves a tag to a different commit, the next install gets a fresh cache directory instead of reusing stale content.

For dependencies with an `npm`, `archive`, or `command` [plugin source](plugin-marketplaces.md#plugin-sources), the constraint does not control which version is fetched, since tag-based resolution applies only to git-backed sources. The constraint is still checked at load time, and the dependent plugin is disabled with `dependency-version-unsatisfied` if the installed version does not satisfy it. For a `command` source, Claude Code checks the version in the dependency’s `plugin.json` and ignores the content-hash suffix; a dependency whose `plugin.json` sets no version satisfies no constraint, so set one before you constrain it.Claude Code never installs a dependency with a `command` source itself, so users [install it first](plugin-marketplaces.md#how-users-accept-the-command). Claude Code never runs the `headersHelper` on a dependency’s marketplace entry either, so users [install that plugin first](plugin-marketplaces.md#how-users-accept-a-headershelper-command).


[​](#how-constraints-interact)

How constraints interact

When several installed plugins constrain the same dependency, Claude Code intersects their ranges and resolves the dependency to the highest version that satisfies all of them. The table below shows how common combinations resolve.

| Plugin A requires | Plugin B requires | Result                                                                                          |
|:------------------|:------------------|:------------------------------------------------------------------------------------------------|
| `^2.0`            | `>=2.1`           | One install at the highest `2.x` tag at or above `2.1.0`. Both plugins load.                    |
| `~2.1`            | `~3.0`            | Install of plugin B fails with `range-conflict`. Plugin A and the dependency stay as they were. |
| `=2.1.0`          | none              | The dependency stays at `2.1.0`. Auto-update skips newer versions while plugin A is installed.  |

Auto-update fetches a constrained dependency at the highest git tag that satisfies every installed plugin’s range, rather than at the marketplace’s latest version, so the dependency continues to receive updates within its allowed range. If no tag satisfies all ranges, auto-update skips that dependency and lists the skip in the `/plugin` Errors tab, naming the constraining plugin. When you uninstall the last plugin that constrains a dependency, the dependency is no longer held and resumes tracking its marketplace entry on the next update.


[​](#enable-or-disable-a-plugin-with-dependencies)

Enable or disable a plugin with dependencies

This section covers plugins installed from a marketplace. For a copy you loaded with `--plugin-dir`, see [Test a plugin and its dependency locally](#test-a-plugin-and-its-dependency-locally). Enabling a plugin also enables the plugins it depends on, and disabling a plugin is blocked if another enabled plugin still needs it. When you enable a plugin, Claude Code also enables its dependencies at the same scope. If a dependency has its own dependencies, Claude Code enables those too. The success message lists what else was enabled along with the plugin you named. If a dependency can’t be enabled, the command refuses and tells you what’s blocking and how to fix it:

| Condition                                                                              | Result                                                                                                                 |
|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------|
| A dependency is not installed                                                          | Enable fails and prints the `claude plugin install` command for each missing dependency.                               |
| A dependency is blocked by your organization’s plugin policy                           | Enable fails and names the blocked dependency.                                                                         |
| A dependency is set to `false` at a scope with higher precedence than the target scope | Enable fails. Enable the dependency at that scope, or pass `--scope` to write there.                                   |
| All dependencies are installed and allowed                                             | Enable succeeds and writes `true` for the plugin and each dependency that was not already enabled at the target scope. |

This holds even when a dependency sets [`defaultEnabled: false`](plugins-reference.md#default-enablement) in its manifest, because Claude Code writes an explicit `true` for it. The same applies at install: a dependency pulled in to satisfy an active plugin installs with `true` regardless of its own default. When you disable a plugin, Claude Code refuses if another enabled plugin still depends on it. The error names the plugins that depend on it and gives you a chained command that disables them in the right order, ending with the one you asked for. For example, if `deploy-kit` depends on `secrets-vault`, disabling `secrets-vault` alone fails with output similar to the following:

```python
secrets-vault is still required by deploy-kit. Disable that plugin first, or
disable everything together: claude plugin disable deploy-kit@acme-tools && claude plugin disable secrets-vault@acme-tools
```

Copy the chained command from the error to disable the full set in one step.


[​](#remove-orphaned-auto-installed-dependencies)

Remove orphaned auto-installed dependencies

Auto-installed dependencies stay on disk after the plugins that installed them are uninstalled, in case you reinstall a dependent plugin or want to keep using the dependency directly. To clean them up, run `claude plugin prune` to list the auto-installed dependencies that no longer have any installed plugin requiring them and remove them after a confirmation prompt.

```python
claude plugin prune
```

If nothing qualifies for removal, the command prints `Nothing to prune` with the reason and exits. This is the expected output on a fresh install, not an error. By default, prune operates at user scope and asks for confirmation before removing anything:

- `--scope project` or `--scope local` targets a different scope.
- `--dry-run` lists what would be removed without changing anything.
- `-y` skips the confirmation prompt. When stdin or stdout isn’t a terminal, prune lists the orphans and exits without removing them unless you pass `-y`.

To prune as part of an uninstall, pass `--prune` to `claude plugin uninstall`. After removing the named plugin, Claude Code scans for and removes any auto-installed dependencies that are now orphaned. Plugins you installed yourself are never pruned, only those installed automatically through another plugin’s `dependencies` array. The same confirmation behavior applies. When stdin or stdout isn’t a terminal, the uninstall still completes, but the prune step lists the orphans and removes nothing unless you pass `-y`. For example, to uninstall `deploy-kit` and clean up the dependencies it leaves behind:

```python
claude plugin uninstall deploy-kit --prune
```


[​](#resolve-dependency-errors)

Resolve dependency errors

Dependency problems appear in `claude plugin list` and in the `/plugin` interface, as descriptive error messages rather than the literal codes in this table. Claude Code disables the affected plugin until you resolve the error. The table below lists the most common errors and how to resolve them.

| Error                            | Meaning                                                                                                                                                                                                                           | How to resolve                                                                                                                                                                                                                                                          |
|:---------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `dependency-unsatisfied`         | A declared dependency is not installed, or it is installed but disabled.                                                                                                                                                          | Run the `claude plugin install` command shown in the error message. If the dependency’s marketplace is not yet configured, add it with `claude plugin marketplace add` and Claude Code resolves the dependency automatically. If the dependency is disabled, enable it. |
| `range-conflict`                 | The version requirements for a dependency cannot be combined. The error message names the cause: no version satisfies all of the ranges, a range is not valid semver syntax, or the combined ranges are too complex to intersect. | Uninstall or update one of the conflicting plugins, fix any invalid `version` string, simplify long `||` chains, or ask the upstream author to widen its constraint.                                                                                                    |
| `dependency-version-unsatisfied` | The installed dependency’s version is outside this plugin’s declared range.                                                                                                                                                       | Run `claude plugin install <dependency>@<marketplace>` to re-resolve the dependency against all current constraints.                                                                                                                                                    |
| `no-matching-tag`                | The dependency’s repository has no `{name}--v*` tag satisfying the range.                                                                                                                                                         | Check that the upstream has tagged releases using the convention above, or relax your range.                                                                                                                                                                            |

To check for these errors programmatically, run `claude plugin list --json`. Plugins with problems include an `errors` field listing them. Plugins that loaded cleanly omit the field.


[​](#see-also)

See also

- [Create plugins](plugins.md): build plugins with skills, agents, and hooks
- [Create and distribute a plugin marketplace](plugin-marketplaces.md): host plugins for your team
- [Plugins reference](plugins-reference.md#plugin-manifest-schema): the full `plugin.json` schema
- [Version management](plugins-reference.md#version-management): how a plugin’s own version is resolved and used as the cache key

---

## Test plugins with evals - Claude Code Docs

Source: https://code.claude.com/docs/en/plugin-evals
Path: 08-Plugins-Skills/plugin-evals.md

## On this page

- [Requirements](#requirements)
- [How an eval run works](#how-an-eval-run-works)
  - [What happens in a run](#what-happens-in-a-run)
  - [How a case is scored](#how-a-case-is-scored)
  - [The no-plugin baseline](#the-no-plugin-baseline)
- [Create your first eval suite](#create-your-first-eval-suite)
- [Write and refine cases](#write-and-refine-cases)
  - [Write a case manually](#write-a-case-manually)
  - [Set run limits and tools in prompt.md](#set-run-limits-and-tools-in-prompt-md)
  - [Choose and weight graders](#grade-the-result)
  - [Choose graders that give a stable signal](#choose-graders-that-give-a-stable-signal)
  - [Score against the no-plugin baseline](#compare-against-a-no-plugin-baseline)
  - [Use a different eval directory](#use-a-different-eval-directory)
- [Set up fixtures and mocks](#set-up-fixtures-and-mocks)
  - [Seed the workspace or conversation](#add-setup-or-history-with-case-yaml)
  - [Mock MCP servers](#mock-mcp-servers)
  - [Replay agent mock answers](#replay-agent-mock-answers)
- [Run evals](#run-evals)
  - [Choose what to evaluate](#choose-what-to-evaluate)
  - [Grant tools](#grant-tools)
  - [Command options](#command-options)
  - [Run evals in CI](#run-evals-in-ci)
- [Read the results](#read-the-results)
  - [HTML report](#html-report)
  - [JSON result](#json-result)
- [What a run can access](#security)
  - [Trust the plugin directory](#trust-the-plugin-directory)
  - [How runs are isolated](#how-runs-are-isolated)
- [Eval suite reference](#eval-suite-reference)
  - [prompt.md frontmatter](#prompt-md-fields)
  - [case.yaml fields](#case-yaml-fields)
  - [Grader frontmatter](#grader-frontmatter)
  - [What a grader can look at](#what-a-grader-can-look-at)
  - [Grader types](#grader-types)
  - [Mock files](#mock-files)
  - [Regex patterns in expect](#expect-patterns)
- [Troubleshooting](#troubleshooting)
  - [”plugin eval is currently in early access”](#%E2%80%9Dplugin-eval-is-currently-in-early-access%E2%80%9D)
  - [”plugin eval is currently unavailable”](#%E2%80%9Dplugin-eval-is-currently-unavailable%E2%80%9D)
  - [”is not a trusted plugin directory, and this run cannot stop to ask you about it”](#%E2%80%9Dis-not-a-trusted-plugin-directory-and-this-run-cannot-stop-to-ask-you-about-it%E2%80%9D)
  - [“is too old for claude plugin eval”](#git-is-too-old-for-claude-plugin-eval)
  - [“cannot be evaluated where sessions load it”](#cannot-be-evaluated-where-sessions-load-it)
  - [”No eval cases found”](#%E2%80%9Dno-eval-cases-found%E2%80%9D)
  - [The baseline arm shows no plugin, or delta is zero](#the-baseline-arm-shows-no-plugin-or-delta-is-zero)
  - [”Agent type ’…’ not found” for one of your plugin’s agents](#%E2%80%9Dagent-type-%E2%80%99%E2%80%A6%E2%80%99-not-found%E2%80%9D-for-one-of-your-plugin%E2%80%99s-agents)
  - [Everything scores zero although the right files were produced](#everything-scores-zero-although-the-right-files-were-produced)
  - [A regex over the trace doesn’t match text I can see](#a-regex-over-the-trace-doesn%E2%80%99t-match-text-i-can-see)
  - [Tools are denied, MCP tools are missing, or Bash won’t run](#tools-are-denied-mcp-tools-are-missing-or-bash-won%E2%80%99t-run)
  - [The run exits 1 but the results look fine](#the-run-exits-1-but-the-results-look-fine)
  - [--json output path must end in .json](#json-output-path-must-end-in-json)
  - [A grader shows passed: false under a run that scored 1.0](#a-grader-shows-passed-false-under-a-run-that-scored-1-0)
  - [“ended on an API error and scored 0 without grading”](#runs-ended-on-an-api-error)
  - [Runs fail with a usage-limit or rate-limit error partway through](#runs-fail-with-a-usage-limit-or-rate-limit-error-partway-through)
  - [“mock call budget exceeded”](#mock-call-budget-exceeded)
  - [Runs time out or hit the turn cap](#runs-time-out-or-hit-the-turn-cap)
- [See also](#see-also)

Create plugins

# Test plugins with evals

Copy pageCopy page

Write eval cases for your Claude Code plugin, run them with claude plugin eval, grade the results, compare against a no-plugin baseline, and gate CI on the score.

Copy pageCopy page

The `claude plugin eval` shell command runs your [plugin](plugins.md) against a suite of test cases and scores the results. Each case is a realistic prompt plus one or more graders. A grader is a pass/fail check on what Claude produced, such as a regex over the reply, whether a particular tool was called, or a rubric that a second model judges the reply against. You don’t have to write the suite manually. `claude plugin eval init` asks you about your plugin, proposes the cases and graders, tries them, and writes the files. You can also ask Claude to do the same from a session you already have open. Use evals to:

- Measure how reliably your plugin leads Claude to produce the right outcome
- Catch regressions when you change the plugin or a new model is released
- See what the plugin contributes compared with no plugin

This page is for plugin and skill authors who have a working plugin and want to test its behavior, and for teams that gate plugin changes in CI. For iterating on one skill inside a Claude Code conversation, the [skill-creator plugin](skills.md#run-evals-with-skill-creator) runs a similar comparison with its own `evals/evals.json` format, and neither tool reads the other’s case files. To create a plugin, see [Create a plugin](plugins-create.md); to check a plugin’s files for syntax and schema errors rather than its behavior, use [`claude plugin validate`](plugins-cli-reference.md#plugin-validate).

Every eval run and every judge grader is a real model call on your account, counted against your plan’s usage or your API bill, so check the [requirements](#requirements) first. Then [create your first eval suite](#create-your-first-eval-suite), or go to [Run evals in CI](#run-evals-in-ci) if you already have one.


[​](#requirements)

Requirements

To run plugin evals you need:

- Claude Code v2.1.269 or later. Run `claude --version` to check and `claude update` to upgrade.
- Git 2.31 or later, if git is installed. Run `git --version` to check. With an older git, `claude plugin eval` [stops before running any case](#git-is-too-old-for-claude-plugin-eval). Without git, it runs normally.
- A plugin directory with a `plugin.json` or `.claude-plugin/plugin.json` manifest, or a [skills-directory plugin](plugins-loading.md#plugins-shared-through-a-repository).
- The same authentication and model provider your normal Claude Code sessions use. Eval runs, judge-scored graders, and `claude plugin eval init` call the model with your credentials, so they count against your plan’s usage limits or your API bill. When the command reports a cost, the figure is a [list-price estimate](../17-Billing-Plans/costs.md) of those calls. If you run Claude Code on Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry, run the suite from a shell that exports the same provider variables as your normal sessions, because each run inherits them from that shell as the [`env` field](#prompt-md-fields) describes.


[​](#how-an-eval-run-works)

How an eval run works

An eval suite lives in a directory called `evals/` inside your plugin, laid out as [Write and refine cases](#write-and-refine-cases) shows. Each case is its own subdirectory with a [prompt](#set-run-limits-and-tools-in-prompt-md) and one or more [graders](#grade-the-result). The prompt is something a person using your plugin might type, such as a request one of its skills should handle.


[​](#what-happens-in-a-run)

What happens in a run

For each run of a case, Claude Code starts a fresh, [isolated](#how-runs-are-isolated) [non-interactive session](../02-Claude-Code-CLI/headless.md) with only your plugin loaded, sends the prompt, and lets Claude work until it finishes or hits the case’s turn or time limit. Each grader then checks the final reply, the transcript, or a file Claude created, and passes or fails.


[​](#how-a-case-is-scored)

How a case is scored

One run of a non-deterministic agent tells you little, so each case runs three times by default. A run’s score is the fraction of its graders that passed, weighted if you set weights, and the case’s score is the mean across its runs. A case passes when its score meets the [`--threshold`](#command-options), `1.0` by default. In model calls, a suite makes roughly cases × runs agent runs with the plugin and the same number again for the [no-plugin baseline](#the-no-plugin-baseline), plus three short judge calls per `llm` or `baseline` grader per run.


[​](#the-no-plugin-baseline)

The no-plugin baseline

A high score on its own doesn’t tell you the plugin helped, because Claude might do as well without it. To separate the two, a case’s runs are repeated with no plugin loaded, and you get two scores, `WITH` and `W/OUT`. Their difference, `Δ`, is what the plugin contributed. If a case scores 1.0 both with and without the plugin, the plugin isn’t what made it pass. The two sets of runs are called the with-arm and the without-arm; [Score against the no-plugin baseline](#compare-against-a-no-plugin-baseline) covers which cases run the with-arm only and how graders are scored across the two arms.


[​](#create-your-first-eval-suite)

Create your first eval suite

This walkthrough writes one case for your own plugin, runs it, and reads the result. Before you start, make sure you have:

- Claude Code v2.1.269 or later and the other [requirements](#requirements)
- A terminal open at your plugin’s root directory, the one containing `plugin.json` or `.claude-plugin/plugin.json`
- One skill in the plugin you want to test, and a request a user would type that should trigger it

1

Create the cases

From the plugin root, run:

```python
claude plugin eval init
```

If Claude Code doesn’t already trust this directory it first asks `Trust this plugin directory?`; answer `y`.An interactive Claude Code session then opens. Claude reads your plugin and asks you what a good result looks like, proposes prompts that should and shouldn’t trigger the plugin, designs graders for each, runs them once as a trial to check they behave, and writes one case directory per prompt under `evals/`, each named after its prompt.When Claude tells you the suite is ready, exit that session with `/exit` or Ctrl+D to return to your shell.If you already have a Claude Code session open at the plugin root, you can instead ask Claude there to run `claude plugin eval init`. Claude runs the command and then asks you the same questions in that conversation.If you’d rather write a case yourself to see exactly what the files contain, follow [Write a case manually](#write-a-case-manually) and come back here to run it.

2

Run the suite

Back at your shell in the plugin root, run every case under `evals/`:

```python
claude plugin eval .
```

You already trusted this directory during step 1, so the run starts immediately. If you wrote the case manually instead, the run first asks `Trust this plugin directory? [y/N]`; answer `y`. [What a run can access](#security) explains what you’re agreeing to.Each case runs three times with your plugin and three times without it, so one case is six runs. A progress line prints as each run finishes, with that run’s score and each grader’s verdict.

3

Read the summary

When the suite finishes you see a summary table, followed by where the report went:

```python
CASE        WITH  W/OUT Δ      RUNS COST    NOTES
first-case  1.00  0.33  +0.67  6    $0.41

1 case(s) · mean Δ +0.67 · 74s · $0.41
Report: /Users/you/my-plugin/evals/results/2026-09-10T17-02-11-482Z/report.html
Published: https://claude.ai/... · keep local next time with --no-publish
```

`WITH` is the case’s score with your plugin loaded, `W/OUT` is the score without it, and a positive `Δ` means the plugin raised the score. `COST` is a list-price estimate of the model calls, and `NOTES` shows the highest-weight failing grader’s explanation, or the run’s error, from the with-arm.

4

Open the report and iterate

Open the `Published:` URL, or the `Report:` path when no `Published:` line appears, to see each grader’s verdict and explanation for every run, and for `llm` graders the judge’s votes and the excerpt it judged. The `Published:` line appears only when your account can [publish reports](#html-report).The most common first finding is a `Δ` near zero with the case’s `tool_used: Skill` grader failing, which means Claude isn’t choosing your skill on natural phrasing. Adjust the skill’s [`description`](skills.md#frontmatter-reference), run `claude plugin eval .` again, and compare.To iterate on one case with fewer runs, run a single arm once. A single run is noisy, so confirm any change at the default three runs before you trust it. With one arm the table shows `SCORE` and `PASS%` columns instead of `WITH`, `W/OUT`, and `Δ`:

```python
claude plugin eval . --case <case-name> --runs 1 --ablation none
```

Replace `<case-name>` with one of the directory names under `evals/`.


[​](#write-and-refine-cases)

Write and refine cases

The cases `claude plugin eval init` writes are plain files you can open, change, and add to. A case is a directory under the plugin’s eval directory that contains a `prompt.md`, a `case.yaml`, or both. Give each case at least one grader, as a `graders/<name>.md` file or a `graders:` entry in `case.yaml`, because a case without one fails to load. To group cases, nest them under a directory that isn’t itself a case; anything inside a case directory, such as `graders/` and fixture files, belongs to that case. This is the layout `claude plugin eval init` writes and the one to use for new suites. The [eval suite reference](#eval-suite-reference) has the complete tree, including mocks and results:

```python
my-plugin/
├── .claude-plugin/plugin.json
├── skills/...
└── evals/
    ├── first-case/
    │   ├── prompt.md          # frontmatter: case fields; body: the prompt
    │   ├── graders/
    │   │   ├── criteria.md    # frontmatter: type + options; body: rubric or pattern
    │   │   └── skill-fired.md
    │   └── case.yaml          # optional: only for context.* fields
    ├── ignores-unrelated-request/
    │   └── ...
    └── results/               # written by each run; add to .gitignore
```


[​](#write-a-case-manually)

Write a case manually

Having Claude write the cases with `claude plugin eval init` is the recommended path. To write one yourself instead, start from a blank template. The following command writes a case named `first-case` with a placeholder `prompt.md` and one placeholder grader, and runs nothing:

```python
claude plugin eval init --bare first-case
```

```python
evals/first-case/
├── prompt.md            # the prompt sent to Claude, plus run limits
└── graders/
    └── criteria.md      # one grader: how to score the result
```

In `prompt.md` you write the message Claude receives in each run, and set the run’s limits and the tools the case may use in its frontmatter. Open `evals/first-case/prompt.md` and replace the placeholder body with a request one of your skills should handle, phrased the way a user would type it rather than naming the skill. This example is for a skill that drafts commit messages; use your own request:

```python
---
max_turns: 10
allowed_tools: [Read, Glob, Grep, Skill]
---

Write me a commit message for this change: I renamed getUser to fetchUser and updated the three call sites.
```

Each run starts in an empty working directory, so put whatever the task needs in the prompt itself, or [set up the workspace](#add-setup-or-history-with-case-yaml) first. The [full list of frontmatter fields](#prompt-md-fields) covers the model, timeout, tags, and environment variables. Each file under `graders/` is one check applied after the run. Open `evals/first-case/graders/criteria.md` and replace the placeholder with a rubric for the judge model, written as concrete PASS and FAIL conditions:

```python
---
type: llm
---

PASS if <what a correct response contains>.
FAIL if <what a wrong or missing response looks like>.
```

Then add a second grader that checks whether your skill is what produced the answer. Create `evals/first-case/graders/skill-fired.md`, replacing `your-skill-name` with the skill’s directory name under `skills/`, which is the name Claude invokes it by:

```python
---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"(?:[\w-]+:)?your-skill-name"'
---
```

This passes when Claude invoked that skill at least once during the run, including by its namespaced `plugin-name:skill-name` form. [Grader types](#grader-types) lists the other checks available, such as matching a regex or confirming a file was created. With both files saved, run the case the way the [quickstart](#create-your-first-eval-suite) does, with `claude plugin eval .` from the plugin root.


[​](#set-run-limits-and-tools-in-prompt-md)

Set run limits and tools in prompt.md

Set a case’s `max_turns`, `timeout_seconds`, `model`, `tags`, and the `allowed_tools` it may use in `prompt.md` frontmatter; the [prompt.md frontmatter](#prompt-md-fields) reference lists every field and its default. Claude receives the body exactly as you wrote it. `@path` mentions in it aren’t expanded into file attachments, so if Claude needs to read a file, grant a tool for it in `allowed_tools`.


[​](#grade-the-result)

Choose and weight graders

A grader’s frontmatter sets its `type`, and optionally a `weight` that makes it count for more of the run’s score and an [`arm`](#compare-against-a-no-plugin-baseline) that controls how it’s scored against the baseline. Of the six types, `regex`, `tool_used`, `tool_order`, and `file_exists` are computed from the transcript and files and cost nothing, while `llm` and `baseline` call a judge model and add to the run’s cost. There are no custom-code graders. [Grader types](#grader-types) lists each type’s options and pass condition, and [what a grader can look at](#what-a-grader-can-look-at) lists the values `target` and `focus` accept. By default, the judge for `llm` and `baseline` graders is the model Claude Code uses for background tasks. Pass `--judge-model sonnet` or a full model ID to choose the judge yourself.


[​](#choose-graders-that-give-a-stable-signal)

Choose graders that give a stable signal

An `llm` grader asks a model for a verdict, so its answer can differ between runs, and it differs more the longer the text it has to read. These habits keep a suite’s scores steady enough to trust:

- For long output such as a generated file, grade it with a `regex` grader over the file’s contents, which checks the whole file the same way every time. Keep `llm` graders for short outputs, with rubrics written as concrete PASS and FAIL conditions.
- Give each case one grader on the result, such as the final message or a produced file, and one on the steps Claude took to produce it, such as `tool_used` or `tool_order`. Together they tell you both whether the answer was right and whether your plugin produced it.
- If a case’s `tool_used: Skill` grader passes but `Δ` is negative, suspect the judge before the plugin. A small judge model can mark a correct answer wrong because it’s formatted differently from what the rubric describes. Re-run with `--judge-model sonnet`, and tighten the rubric so formatting doesn’t decide the verdict.
- To check that a build or test passed inside the run, have the prompt ask Claude to run it and write the outcome to a file, grade that file, and assert the command ran with a `tool_used` grader whose `input_match` names the command.


[​](#compare-against-a-no-plugin-baseline)

Score against the no-plugin baseline

When a plugin is under test, a case normally runs in two arms. The with-arm is its runs with the plugin loaded, and the without-arm is the same number of runs with no plugin at all. The summary and report show both scores and `Δ`, the with-arm score minus the without-arm score. In these situations a case runs the with-arm only, so it gets no `W/OUT` score or `Δ`:

- **You pass `--ablation none`**: every case runs one arm, which halves the cost when you don’t need the comparison, such as while iterating on graders.
- **The case resumes a transcript and the target is a path**: with a [target](#choose-what-to-evaluate) such as `.` rather than an installed plugin’s name, a [`context.history_file`](#add-setup-or-history-with-case-yaml) case runs one arm by default, on the assumption that the recorded conversation already reflects the plugin. The run prints a `single-arm (no Δ)` notice on stderr naming these cases. To compare the resumed turn with and without the plugin, pass `--ablation with-without`.
- **No plugin was found for the case**: when the target is a path, a case whose plugin Claude Code couldn’t locate also runs one arm by default. See [the baseline arm shows no plugin](#the-baseline-arm-shows-no-plugin-or-delta-is-zero) to fix it.

In a two-arm run, some graders are reported with `scored: false`. A check like “the skill was invoked” can never pass without the plugin, so counting it would push the without-arm toward zero and inflate `Δ`. To keep the two arms comparable, Claude Code excludes such graders from the score in both arms and reports them in the with-arm as pass/fail indicators only. That includes:

- Every `tool_used` grader whose `tool` is `Skill`
- Every `regex` grader with `target: mock_calls` and every `llm` grader with `focus: mock_calls`, when each [mocked server](#mock-mcp-servers) in the case is one your plugin declares
- Any grader you mark `arm: with-only`

Three settings change that exclusion:

- **Every grader excluded**: if every grader in a case is in the excluded set, they’re scored normally instead, since there would be nothing left to score.
- **`arm: both`**: set `arm: both` on a grader to score it in both arms regardless, which is what you want for a “must not invoke the skill” check with `min: 0` and `max: 0`.
- **`--ablation none`**: under `--ablation none` nothing is excluded, so the same suite can produce a different absolute score in the two modes.


[​](#use-a-different-eval-directory)

Use a different eval directory

If `evals/` is already taken by another tool, keep the suite in a different directory. You can record that directory in the plugin’s `plugin.json` so every run and every collaborator uses it, or pass it on the command line for a single run:

- **In `plugin.json`**: add `"experimental": { "evals": "quality/evals" }`.
- **On the command line**: pass `--eval-dir quality/evals` to both `claude plugin eval` and `claude plugin eval init`.

If you set both, the flag’s directory is used. Give a relative path of plain directory names such as `qa` or `quality/evals`. An absolute path or one containing `..` isn’t accepted: as a flag value it’s an error, while an unusable manifest value prints a `Warning:` line and the run uses `evals/` instead. Cases, results, and `init` output all move to that directory.


[​](#set-up-fixtures-and-mocks)

Set up fixtures and mocks

A case can need more than a prompt: files or a git repository in the workspace, an earlier conversation to continue, or answers from the MCP servers your plugin connects to. Each of those is set up beside the case so runs stay repeatable.


[​](#add-setup-or-history-with-case-yaml)

Seed the workspace or conversation

Each run starts in an empty workspace. When a case needs more than the prompt, add a `case.yaml` beside `prompt.md` with a `context` block:

- **Fixture files or a git repository**: write a Bash script in the case directory and name it in `context.scaffold_script`. The script runs as you, outside the agent’s sandbox, and only when you pass `--scaffold`, so pass that flag only for suites you or your organization wrote.
- **An earlier conversation to continue**: save the transcript as a `.jsonl` file and name it in `context.history_file`, and the case’s prompt becomes the next user turn. When the target is a path, such a case runs [without a baseline arm](#compare-against-a-no-plugin-baseline) by default.
- **Fixture directories Claude can read during the run**: list them in `context.add_dirs`.

A `case.yaml` also needs `schema_version: "1.1"` and `name`; the [case.yaml fields](#case-yaml-fields) reference has the full list. This `case.yaml` seeds a workspace from a script and lets Claude read fixtures from a `resources/` directory:

```python
schema_version: "1.1"
name: changelog-from-diff
tags: [smoke]
context:
  scaffold_script: fixture.sh
  add_dirs: [resources]
```

A scaffold script starts in the empty workspace with a small fixed environment: your shell’s `PATH`, `HOME` set to the run’s temporary home directory, `TMPDIR`, and a few constants such as `TERM=dumb`. Nothing else from your shell reaches it, and neither do the case’s `EVAL_*` variables. If the script exits non-zero or runs longer than 120 seconds, that run scores 0 with a `scaffold failed` error. Use the script for files and git state only, since project configuration it writes [isn’t loaded](#how-runs-are-isolated).


[​](#mock-mcp-servers)

Mock MCP servers

You can evaluate a plugin whose skills call MCP tools without the real service behind them. Put one Markdown file per tool under `evals/mocks/<server>/<tool>.md` for the whole suite, or under a case’s own `mocks/` directory for one case, where `<server>` is the server’s name in your plugin’s [MCP configuration](plugins-components.md#mcp-servers). A run never starts your plugin’s real MCP servers unless you ask. Claude Code registers a substitute server under each server’s own name. Tools with a mock file answer from it and are allowed without an `--allow-tools` grant, and a tool with no mock file isn’t available to Claude. A server with no mocks at all appears in the case’s `mocked:` progress line as `plugin_<plugin>_<server>[not started: no mock]`. The file’s body is what the tool returns to Claude. This mock substitutes for a `create_issue` tool on a server named `tracker`, checks the input Claude sends, and echoes the title back. Save it as `evals/mocks/tracker/create_issue.md`:

```python
---
expect:
  title: string
  priority: [low, medium, high]
---

Created issue #4821: {{input.title}}
```

A mock file’s body and frontmatter accept these options:

- **Substitutions**: insert fields from the call’s input with `{{input.<field>}}`, and the contents of a fixture file beside the mock with `{{file:fixtures/{input.<field>}.json}}`.
- **`expect:`**: the `expect:` block guards the input. If a call violates it, the run aborts with score 0 and records why, so a case can assert what your plugin asked the server to do.
- **`error: true`**: set `error: true` to return the body as a tool error instead.
- **`type: agent`**: set `type: agent` to have the judge model answer as the server from instructions in the body. Calls to agent mocks share one [budget per run](#mock-call-budget-exceeded) of four times the case’s `max_turns`, and a call past it aborts the run with score 0.

The [mock file reference](#mock-files) lists every key and the `_server.md` and `_tools.json` files. To grade the calls themselves, point a grader at `target: mock_calls`. To run against the plugin’s real MCP servers instead, pass one of these flags. Either way those processes run as you, outside the run’s sandbox, and their tools need an [`--allow-tools` grant](#grant-tools):

- **`--allow-real-servers`**: start the real process for each server you haven’t mocked, and keep answering mocked tools from their files
- **`--mocks off`**: ignore `mocks/` entirely and start every server the plugin declares


[​](#replay-agent-mock-answers)

Replay agent mock answers

A `type: agent` mock answers with a call to the [`--judge-model`](#command-options), so its output varies between runs and changes if you change the judge. When a run completes without an error or abort, Claude Code saves each answer an agent mock gave under the results directory in `mock-recordings/`. Open `ADOPT.txt` there to see each recording and the `.replay/<server>/` directory to copy it into, beside the mock that produced it. After you copy a recording there, later runs answer the identical call from it with no model call. Commit `mocks/.replay/` with the rest of `mocks/` so CI runs are repeatable.


[​](#run-evals)

Run evals

Once a suite exists, `claude plugin eval` runs it. You choose which plugin and cases run with the target argument, grant any tools the cases need beyond the read-only set with `--allow-tools`, and control run count, models, cost, and output with the other options.


[​](#choose-what-to-evaluate)

Choose what to evaluate

Most of the time you run `claude plugin eval .` from the plugin root, which runs every case in the suite with the plugin you’re standing in loaded. To run a single case file, or to evaluate a plugin you installed rather than one you’re developing, pass a different target:

| Target                                                    | What runs                                                                                                                                                                                                                                                         |
|:----------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| A plugin’s root directory, such as `.`                    | Every case under its eval directory, with that plugin loaded                                                                                                                                                                                                      |
| A single `prompt.md` or `case.yaml` file                  | That case, with its enclosing plugin loaded                                                                                                                                                                                                                       |
| An installed plugin by name, `name` or `name@marketplace` | The plugin and the cases in its eval directory, read [in place or from the installed copy](plugins-loading.md#in-place-and-copied-plugins). Results are written under `./evals/results/` in your current directory, or `./<dir>/results/` with `--eval-dir` |
| `name@skills-dir`                                         | The same, for a [skills-directory plugin](plugins-loading.md#plugins-shared-through-a-repository)                                                                                                                                                           |
| Omitted                                                   | The current directory as a path                                                                                                                                                                                                                                   |

Add `--case <glob>` to filter by case name and `--tag <tag>` to keep cases with any of the given tags. Put the target before `--tag`, `--allow-tools`, and `--json`. The first two take a list and `--json` takes an optional path, so each of them reads a target that follows as its own value.


[​](#grant-tools)

Grant tools

Runs never stop to ask for permission. Built-in tools that need a grant you didn’t give, such as `Bash`, `Write`, `Edit`, `WebFetch`, and `WebSearch`, are removed from the session, so Claude can’t call them at all. A run allows only the read-only tools the case lists in `allowed_tools`, from `Read`, `Glob`, `Grep`, `NotebookRead`, `Skill`, `AskUserQuestion`, `Agent`, `TodoWrite`, and the task tools `TaskCreate`, `TaskGet`, `TaskList`, `TaskUpdate`, and `TaskStop`, plus whatever you grant with `--allow-tools`. That grant applies to every case in the run. To let cases use `Bash`, `Write`, `Edit`, `WebFetch`, or `WebSearch`, grant them yourself:

```python
claude plugin eval . --allow-tools Write Edit "Bash(npm test *)"
```

When a case asked for a tool you didn’t grant, the progress output lists it as `not granted`. Tools on a [mocked](#mock-mcp-servers) MCP server need no grant. Tools on a real plugin MCP server need both the server started, with `--allow-real-servers` or `--mocks off`, and a grant by name, such as `--allow-tools "mcp__plugin_my-plugin_github__*"`; a plugin’s MCP tools are named `mcp__plugin_<plugin>_<server>__<tool>`. When you grant `Bash` in any form, every command runs under Claude Code’s [OS-level sandbox](../02-Claude-Code-CLI/sandboxing.md). Writes are confined to the run’s workspace, your home directory and Claude Code configuration are unreadable, and network access is limited to domains you grant with `--allow-tools "WebFetch(domain:example.com)"`. If you grant Bash or PowerShell on a machine with no sandbox backend, Claude Code refuses each run rather than running it unconfined, and the case shows a run error and usually scores 0. Native Windows has no backend, so run shell-granting suites under WSL2; on Linux, install `bubblewrap` and `socat` first. See the [sandboxing prerequisites](../02-Claude-Code-CLI/sandboxing.md).


[​](#command-options)

Command options

This table covers the options for run count, models, scoring, cost, tool grants, mocks, and output. Run `claude plugin eval --help` for the complete list, which also includes `--case`, `--tag`, `--eval-dir`, `--no-scaffold`, `--report`, and `--verbose`.

| Option                     | Default                                                                                             | Effect                                                                                                                                                                                                                                                                                        |
|:---------------------------|:----------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--runs <n>`               | Each case’s `runs`, else 3                                                                          | Runs per case per arm                                                                                                                                                                                                                                                                         |
| `-j`, `--concurrency <n>`  | `1`                                                                                                 | Run up to this many agent runs at once, from 1 to 8. They share your account’s rate limit, so this shortens wall-clock time rather than raising throughput past that limit. Results keep case order                                                                                           |
| `--model <model>`          | Each case’s `model`, else `ANTHROPIC_MODEL` if set, else Claude Code’s default                      | Model for the agent under test. Pin it in CI so a model rollout isn’t mistaken for a plugin regression                                                                                                                                                                                        |
| `--judge-model <model>`    | The model for [background tasks](#grade-the-result)                                                 | Model for `llm` and `baseline` graders                                                                                                                                                                                                                                                        |
| `--ablation <mode>`        | Decided per case; see [Score against the no-plugin baseline](#compare-against-a-no-plugin-baseline) | Whether to also run each case without the plugin to measure what it adds. `none` runs one arm; `with-without` adds the no-plugin baseline                                                                                                                                                     |
| `--threshold <0..1>`       | `1.0`                                                                                               | A case passes when its with-arm score is at least this. Any case below it makes the command exit 1                                                                                                                                                                                            |
| `--max-cost-usd <usd>`     | No ceiling                                                                                          | A ceiling on the run’s list-price cost estimate, not on plan usage. Checked before each run starts. Once spent, nothing further starts; runs that already started finish, so spend can pass the ceiling by those runs. If any run is left unstarted, the command exits 2 with partial results |
| `--allow-tools <tools...>` | None                                                                                                | Grant tools beyond the read-only set. See [Grant tools](#grant-tools)                                                                                                                                                                                                                         |
| `--scaffold`               | Off                                                                                                 | Run each case’s [`scaffold_script`](#add-setup-or-history-with-case-yaml)                                                                                                                                                                                                                     |
| `--trust-plugin`           | Off                                                                                                 | Skip the first-run trust prompt for a plugin whose code and suite you’d run yourself. Pass it in CI so the job is never refused by or left waiting at the prompt. See [What a run can access](#security)                                                                                      |
| `--mocks <mode>`           | `record`                                                                                            | `record` answers MCP tool calls from [mocks](#mock-mcp-servers), doesn’t start the plugin’s real servers, and saves agent-mock answers for replay. `off` ignores mocks and starts the plugin’s real MCP servers                                                                               |
| `--allow-real-servers`     | Off                                                                                                 | With `--mocks record`, also start the plugin’s real MCP servers for servers that have no mock                                                                                                                                                                                                 |
| `--json [path]`            | Off                                                                                                 | Print the [result document](#json-result) to stdout, or write it to a path ending in `.json`. The run is quiet: no progress lines or summary table                                                                                                                                            |
| `--output-dir <dir>`       | `<eval dir>/results/<timestamp>/`                                                                   | Where `aggregate-result.json` and `report.html` go                                                                                                                                                                                                                                            |
| `--no-publish`             |                                                                                                     | Keep the HTML report local. See [HTML report](#html-report)                                                                                                                                                                                                                                   |
| `--publish-report`         |                                                                                                     | Publish the report even where it would stay local by default, such as a run a Claude Code session started                                                                                                                                                                                     |
| `--keep-temp`              | Off                                                                                                 | Keep every run’s sandbox directory and print its path, for debugging what Claude produced                                                                                                                                                                                                     |


[​](#run-evals-in-ci)

Run evals in CI

In your CI job, run the suite with `--json` to write the result for archiving, and fail the build on the exit code. Pass `--trust-plugin` so the job never waits at the [first-run trust prompt](#security), pin both models so scores are comparable over time, keep the report local, and set a cost ceiling as an upper limit:

```python
claude plugin eval . \
  --trust-plugin \
  --json results.json \
  --threshold 0.8 \
  --model claude-sonnet-5 \
  --judge-model claude-haiku-4-5 \
  --no-publish \
  --max-cost-usd 20
```

The job’s exit code tells you what happened:

| Exit code | Meaning                                                                                                                                                                                                                                                                                                                                   |
|:----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0         | The suite passed: every case scored at or above `--threshold` and nothing else failed the run                                                                                                                                                                                                                                             |
| 1         | A case scored below the threshold, a case file failed to load, no cases were found, a run couldn’t be started, or a run [ended on an API error](#runs-ended-on-an-api-error). Also returned when the plugin directory isn’t trusted and `--trust-plugin` wasn’t passed, an option was invalid, or the `--json` result couldn’t be written |
| 2         | Partial run: the `--max-cost-usd` ceiling was hit, or your credential was rejected before or at the first run. `results.json` is still written with `partial: true` and the reason                                                                                                                                                        |
| 130       | Interrupted. Partial results are written                                                                                                                                                                                                                                                                                                  |
| 143       | Terminated, such as by a CI timeout                                                                                                                                                                                                                                                                                                       |

The with-minus-without delta is reported but never changes the exit code, and neither do problems writing or publishing the HTML report. To see why a case scored low, run it locally without `--json` so the per-run progress and grader lines print. A CI runner also needs these in place:

- **Install and credentials**: a CI runner needs a Claude Code install and [credentials in the environment](../02-Claude-Code-CLI/authentication.md) such as `ANTHROPIC_API_KEY` or your cloud provider’s variables.
- **Trust**: without `--trust-plugin`, a job whose checkout directory Claude Code doesn’t already trust needs the [first-run trust prompt](#trust-the-plugin-directory), and a run that can’t ask is refused with exit 1.
- **`init` in CI**: `claude plugin eval init` needs a terminal to ask you its questions; in CI, run `claude plugin eval init --bare <name>` to get the blank template.

To keep costs predictable, give quick every-change suites only graders that don’t call a judge, use `--ablation none` where you don’t need `Δ`, and leave `partial: true` documents and runs with `skippedPaidGraders` out of any trend you chart.


[​](#read-the-results)

Read the results

Every run with at least one case writes a `results/<timestamp>/` directory inside the eval directory, containing `aggregate-result.json` and `report.html`. For a path target that’s under the plugin; for a plugin you named, it’s under your current directory, as the [target table](#choose-what-to-evaluate) shows. The summary table, the JSON, and the report all render the same result data.


[​](#html-report)

HTML report

`report.html` is a single self-contained file that makes no external requests, so you can attach it to a CI job or open it from disk. This example is the top of a report for a three-case suite run with `--threshold 0.8`; the cost shown is a list-price estimate and varies with the model and the number of cases:

Read it from the top down:

- **The verdict line and tiles** answer whether the plugin helped across the whole suite. Suite score is the mean of the per-case with-plugin scores, Ablation Δ is how far that is above or below the baseline score, and Cases counts how many met the threshold. Perfect runs is the share of with-plugin runs where every grader passed.
- **Each case card** shows the case’s own `Δ` and with-plugin score, with a tick on the bar at the threshold. A case whose `Δ` is negative gets a red left edge, so regressions stand out when you scroll.
- **Inside a case**, the with-plugin runs come first and the baseline runs after. Each run lists its graders with a pass or fail chip. A failed grader is already expanded with its explanation, and an `llm` grader also shows the judge’s votes and the evidence it was shown, which is where you find out why a run scored low. Graders that don’t count toward the score, such as `tool_used: Skill`, carry a `plugin-fired indicator` badge.
- **Prompt and Graders**, below the runs, show the case’s prompt and each grader’s rubric or pattern, so someone reading the report without the suite can see what was asked and what counted as good.

If you’re signed in with a claude.ai subscription and [artifacts](../02-Claude-Code-CLI/artifacts.md) are available for your account, Claude Code also publishes the report as a private artifact and prints `Published: <url>`. Pass `--no-publish` to keep it local. If no `Published:` line appears, such as with API-key authentication, the local file is the report. A run that a Claude Code session started, such as when you ask Claude to run the suite for you, also stays local, and its `Report:` line says `kept local`. Add `--publish-report` to that command to publish it.


[​](#json-result)

JSON result

`aggregate-result.json`, and `--json` output, is a versioned document with `schemaVersion: 1` for CI scripts to parse. Field names are camelCase and new fields are added without renaming existing ones, so write your script to ignore fields it doesn’t recognize. These are the fields a gating script usually reads. The document also carries the suite configuration, every grader definition, and per-run grader results with explanations and evidence:

| Field                                             | Meaning                                                                                                                                                                                                                                                               |
|:--------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `partial`, `partialReason`                        | `true` with `cost_ceiling`, `interrupted`, or `auth_failed` when the suite didn’t finish. Leave partial results out of trend charts                                                                                                                                   |
| `aggregates.overallScore`                         | Mean case score across the suite                                                                                                                                                                                                                                      |
| `aggregates.casesPassed`, `aggregates.casesTotal` | Cases at or above `--threshold`, and the total                                                                                                                                                                                                                        |
| `aggregates.meanDelta`                            | Mean `Δ` across cases, under the two-arm mode                                                                                                                                                                                                                         |
| `cases[].name`                                    | Case name                                                                                                                                                                                                                                                             |
| `cases[].aggregates.score`                        | Mean with-arm run score for the case                                                                                                                                                                                                                                  |
| `cases[].aggregates.delta`                        | With-arm score minus without-arm score. Omitted when the case ran one arm or the arms aren’t comparable                                                                                                                                                               |
| `cases[].arms.with[].error`                       | `null`, or why a run ended abnormally, such as `timed out after 300s`. Some of these runs are still graded on what they produced, so a non-null error doesn’t imply score 0. A run that [ended on an API error](#runs-ended-on-an-api-error) scores 0 without grading |
| `cases[].arms.with[].aborted`                     | Present when a [mock](#mock-mcp-servers) stopped the run through `expect:`, `abort_when`, or the [agent-mock call budget](#mock-call-budget-exceeded), with `server`, `tool`, and `reason`. The run scores 0 and `error` stays `null`                                 |
| `cases[].arms.with[].skippedPaidGraders`          | `true` when the cost ceiling skipped this run’s judge graders, so its score isn’t comparable                                                                                                                                                                          |
| `costUsd`, `durationSeconds`, `claudeVersion`     | Estimated cost at list price including judge calls, wall-clock seconds, and the Claude Code version that ran the suite                                                                                                                                                |


[​](#security)

What a run can access

`claude plugin eval` loads the target plugin’s skills, hooks, and agents and runs its eval suite on your machine, as you. Pointing it at a plugin is the same trust decision as `claude --plugin-dir`, so only evaluate plugins you trust. The isolation described in this section limits what the agent under test can reach; it isn’t a boundary against the plugin’s own code, and a suite that passes says nothing about whether the plugin is safe.


[​](#trust-the-plugin-directory)

Trust the plugin directory

The first time you run `claude plugin eval` against a directory, Claude Code asks `Trust this plugin directory?` before it loads anything from it, unless you already accepted the trust prompt there in an interactive `claude` session. Inside a git repository, answering yes trusts the whole repository, for interactive sessions too. When stdin or stdout isn’t a terminal, or under `--json`, the run can’t ask and is refused with exit 1; pass `--trust-plugin` to assert the trust yourself, only for a plugin you’d run on your own machine. A target you name rather than give as a path, meaning an installed plugin or a skills-directory plugin, skips the prompt. Some parts of the plugin and suite run only when you pass their flag for that run:

- A case’s [`scaffold_script`](#add-setup-or-history-with-case-yaml) with `--scaffold`
- [Tools beyond the read-only set](#grant-tools) with `--allow-tools`
- The plugin’s [real MCP servers](#mock-mcp-servers) with `--allow-real-servers` or `--mocks off`

A case’s `allowed_tools` and a skill’s own `allowed-tools` frontmatter can’t widen any of them. When the plugin includes hooks you didn’t write, or you start its real MCP servers, treat its scores as advisory unless you ran it in an isolated environment such as a container or CI runner, since hooks and servers run outside the agent’s sandbox and could modify the files the graders read.


[​](#how-runs-are-isolated)

How runs are isolated

Each run gets a temporary home directory, working directory, and Claude Code configuration, and the agent under test runs there as a `claude -p` child process with only your plugin loaded. Keep these consequences in mind when you write cases:

- **Nothing personal or project-level loads.** Your user settings, hooks, `CLAUDE.md` files, MCP servers, other installed plugins, memory, and skills are absent. Project-scoped configuration isn’t read anywhere either: no `.claude/` directory, `CLAUDE.md`, or `.mcp.json` loads from above the workspace or inside it, even one a `scaffold_script` wrote, and `add_dirs` directories grant read access only. Most of your shell environment is withheld too; only an [allowlist](#prompt-md-fields) and `EVAL_*` variables reach the run. Ship any skills, agents, hooks, or MCP servers a case depends on in the plugin under test, since a [`scaffold_script`](#add-setup-or-history-with-case-yaml) can supply only files and git state.
- **Managed policy can still restrict a run.** Restrictions in [managed settings](../13-Enterprise-Admin/managed-settings.md) an administrator deployed to the machine apply inside a run, so results on a managed machine can differ from an unmanaged one by that policy.
- **The Artifact tool is off.** A skill that publishes an [artifact](../02-Claude-Code-CLI/artifacts.md) can be graded only on what it produces before that step.
- **The case definitions are hidden from the agent.** A run can’t read the eval directory, so Claude can’t see the case’s prompt, its graders, or sibling cases.
- **No network sandbox outside shell commands.** Shell commands you grant run under the sandbox’s network rules. A `WebFetch(domain:…)` grant reaches that domain directly, and the plugin’s own hooks and any real MCP servers you start can reach any host.


[​](#eval-suite-reference)

Eval suite reference

Everything an eval suite can contain lives under the plugin’s eval directory, `evals/` unless you [configured another](#use-a-different-eval-directory). A directory counts as a case when it holds a `prompt.md` or a `case.yaml`, and a case without at least one grader fails to load with an `invalid case.yaml` error that names `graders`. This tree shows every file `claude plugin eval` reads or writes in the eval directory:

```python
evals/
├── <case>/                        # one directory per case; nest under a non-case directory to group
│   ├── prompt.md                  # frontmatter: case and run fields; body: the prompt
│   ├── case.yaml                  # optional: context.* fields, or the whole case in one file
│   ├── graders/
│   │   └── <name>.md              # one grader per file; frontmatter: type and options; body: rubric
│   ├── mocks/                     # optional: mocks for this case only, same layout as below
│   └── <fixtures, scripts, transcripts referenced by case.yaml>
├── mocks/                         # optional: suite-wide MCP mocks
│   ├── <server>/
│   │   ├── <tool>.md              # one mocked tool; body: the tool result
│   │   ├── _server.md             # optional: one agent that answers several tools
│   │   ├── _tools.json            # optional: saved tools/list response for real descriptions and schemas
│   │   └── fixtures/              # files inserted with {{file:fixtures/...}}
│   └── .replay/<server>/          # adopted agent-mock recordings, answered without a model call
└── results/<timestamp>/           # written by each run; add results/ to .gitignore
    ├── aggregate-result.json
    ├── report.html
    └── mock-recordings/           # agent-mock answers from clean runs, with ADOPT.txt
```


[​](#prompt-md-fields)

prompt.md frontmatter

`prompt.md` frontmatter accepts these fields. An unknown key is an error:

| Field                  | Default                      | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|:-----------------------|:-----------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `schema_version`       | `"1.1"`, set for you         | Case format version. Cases written as `prompt.md` get it automatically, so you rarely set it                                                                                                                                                                                                                                                                                                                                                                                  |
| `name`                 | The directory name           | Case name. `--case` globs match it and the report keys on it                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `description`          |                              | For humans. Not used at run time                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `tags`                 | `[]`                         | Labels for `--tag` filtering. A case runs if any of its tags matches                                                                                                                                                                                                                                                                                                                                                                                                          |
| `plugins`              | The nearest enclosing plugin | Plugin directories under test, relative to the case directory. Set `plugins: ["../.."]` when auto-detection doesn’t find your plugin; see [the plugin didn’t load](#the-baseline-arm-shows-no-plugin-or-delta-is-zero)                                                                                                                                                                                                                                                        |
| `runs`                 | `3`                          | Runs per arm, 1 to 50. `--runs` overrides it                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `expected_outcome`     |                              | For humans. Not used at run time                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `model`                | The child session’s default  | Model for the agent under test. `--model` overrides it                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `max_turns`            | `10`                         | Turn cap, up to 200. Hitting it is recorded as a run error and usually lowers the score, so set it generously                                                                                                                                                                                                                                                                                                                                                                 |
| `timeout_seconds`      | `300`                        | Wall-clock cap per run, up to 3600                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `allowed_tools`        | `[]`                         | Tools the case wants, such as `[Read, Glob, Grep, Skill]`. Read-only tools are granted when listed here; for anything else, see [Grant tools](#grant-tools)                                                                                                                                                                                                                                                                                                                   |
| `append_system_prompt` |                              | Text appended to the child session’s system prompt                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `env`                  | `{}`                         | Extra environment variables for the child session. Keys must match `EVAL_[A-Z0-9_]*`; any other key fails the run. The run inherits only an allowlist from your shell: basics such as `PATH` and locale, proxy and certificate settings, the variables that select and authenticate your model provider, most `ANTHROPIC_*` and `CLAUDE_CODE_*` configuration, and `EVAL_*`. To pass the plugin anything else, such as a toolchain setting, export it as an `EVAL_*` variable |


[​](#case-yaml-fields)

case.yaml fields

`case.yaml` is an alternative or companion to `prompt.md`: it describes a case in YAML and adds the fields that point at other files. It requires `schema_version: "1.1"` and `name`. The `prompt.md` fields `description`, `tags`, `plugins`, `runs`, and `expected_outcome` go at the top level; `model`, `max_turns`, `timeout_seconds`, `allowed_tools`, `append_system_prompt`, and `env` go under `execution:`. When both files exist, `prompt.md` frontmatter overrides the matching `case.yaml` fields, the `prompt.md` body is the prompt, and `graders/*.md` are added after any graders listed in `case.yaml`. These fields exist only in `case.yaml`:

| Field                     | Purpose                                                                                                                                                                                                                                                                                                       |
|:--------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `context.scaffold_script` | A Bash script in the case directory that runs in the empty workspace before Claude starts, to create fixture files or a git repository. It runs only when you pass [`--scaffold`](#add-setup-or-history-with-case-yaml), with a minimal environment and a 120-second limit, and a non-zero exit fails the run |
| `context.history_file`    | A `.jsonl` transcript in the case directory to resume. The case’s prompt becomes the next user turn                                                                                                                                                                                                           |
| `context.add_dirs`        | Directories inside the case directory that Claude may read during the run, granted read-only                                                                                                                                                                                                                  |
| `execution.prompt`        | The prompt, when you keep the whole case in `case.yaml` and omit `prompt.md`                                                                                                                                                                                                                                  |
| `graders`                 | A list of graders, each with a `name` plus the same keys a `graders/*.md` file takes in frontmatter. For `llm` graders, put the rubric in `criteria`                                                                                                                                                          |


[​](#grader-frontmatter)

Grader frontmatter

Every grader file under `graders/` takes these keys in frontmatter, plus the options for its type. The grader’s name is the filename without `.md`:

| Key      | Default  | Purpose                                                                                                                                                                                      |
|:---------|:---------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type`   | required | One of the [grader types](#grader-types)                                                                                                                                                     |
| `weight` | `1`      | Relative weight in the run’s score. Any positive number                                                                                                                                      |
| `arm`    | unset    | `with-only` excludes the grader from scoring in a [two-arm run](#compare-against-a-no-plugin-baseline); `both` forces a grader Claude Code would otherwise exclude to be scored in both arms |


[​](#what-a-grader-can-look-at)

What a grader can look at

`regex` graders take a `target` and `llm` graders take a `focus`. Both accept the same values:

| Value                            | What the grader sees                                                                                                                                                                                                                                                                                           |
|:---------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `last_message`                   | Claude’s final response text. This is the default                                                                                                                                                                                                                                                              |
| `trace`                          | The session as JSON, one message per line. A `regex` grader sees every message; an `llm` judge sees the first 12 and the last 12. Quotes and newlines inside it are JSON-escaped, so a regex matches `\"` rather than `"`                                                                                      |
| `files`                          | The list of paths Claude created during the run, one per line. Not their contents, and not files that a scaffold created or that Claude only modified                                                                                                                                                          |
| `{ source: file, path: <path> }` | The contents of one file in the workspace after the run. Use this to grade what the plugin produced. A PNG, JPEG, GIF, or WebP file is shown to an `llm` judge as an image. An `llm` judge refuses other binary files such as `.pptx` or PDF; render them to an image or write them out as text and grade that |
| `mock_calls`                     | Each call Claude made to a [mocked MCP tool](#mock-mcp-servers), with its input and the mock’s answer                                                                                                                                                                                                          |


[​](#grader-types)

Grader types

Each grader type below lists its options and when it passes:

| Type          | Options                               | Passes when                                                                                                                                                                                                                  |
|:--------------|:--------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `regex`       | `pattern`, `flags`, `match`, `target` | The JavaScript regex `pattern` is found in the target. Set `match: not_contains` to require absence or `match: "count:N"` to require exactly N matches. Put case-insensitivity in `flags: i`; inline `(?i)` isn’t supported  |
| `tool_used`   | `tool`, `input_match`, `min`, `max`   | The number of calls to `tool` whose JSON-encoded input matches the optional `input_match` regex is between `min`, default 1, and `max`, default unlimited. To assert a tool was never called, set both `min: 0` and `max: 0` |
| `tool_order`  | `before`, `after`                     | Both tools were called and the first matching `before` call precedes the first matching `after` call. Each is a tool name or `{ tool, input_match }`                                                                         |
| `file_exists` | `path`, `exists`                      | A file Claude created matches the `path` glob, or none does with `exists: false`. Only files created during the run count                                                                                                    |
| `llm`         | `criteria`, `focus`                   | A judge model votes PASS on the rubric in at least two of three votes. In the `.md` layout the file body is the criteria                                                                                                     |
| `baseline`    | `baseline_file`, `criteria`           | A judge finds the run satisfies the criteria at least as well as the reference transcript at `baseline_file`, a `.jsonl` in the case directory                                                                               |


[​](#mock-files)

Mock files

A `<tool>.md` file under `mocks/<server>/` answers one tool. Its body is the tool result, with `{{input.<field>}}` and `{{file:fixtures/<name>}}` substitutions. Its frontmatter accepts these keys:

| Key          | Default | Purpose                                                                                                                                                                                                                                                                                                 |
|:-------------|:--------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type`       | `fixed` | `fixed` returns the body as written. `agent` treats the body as instructions for the [judge model](#command-options), which acts as the server for the run and sees earlier calls as history                                                                                                            |
| `expect`     | unset   | A map from dotted input paths to a type name such as `string`, `number`, `boolean`, `array`, or `object`, a [`/regex/`](#expect-patterns), a literal, or a list of allowed literals. A call that violates it aborts the run with score 0 and is reported as `aborted` with the server, tool, and reason |
| `error`      | `false` | `fixed` only. Return the body as a tool error                                                                                                                                                                                                                                                           |
| `abort_when` | unset   | `agent` only. Prose listing the only conditions under which the agent may abort the run                                                                                                                                                                                                                 |

Two optional files sit beside the tool files in a server’s directory:

- **`_server.md`**: a single `type: agent` mock that answers several tools, listed in its `tools:` frontmatter key. A `<tool>.md` for the same tool takes precedence. An `expect:` guard here is a load error unless `tools:` lists a single tool, so put the guard on the individual `<tool>.md` instead
- **`_tools.json`**: a saved `tools/list` response from the real server, so mocked tools carry their real descriptions and input schemas instead of a permissive placeholder

A case’s own `mocks/` directory uses the same layout and overrides the suite’s mocks file by file.


[​](#expect-patterns)

Regex patterns in expect

A `/regex/` value in `expect:` uses a small dialect that Claude Code checks when it loads the suite:

- Literal characters, `.`, escapes such as `\d`, and character classes such as `[a-z]`
- The quantifiers `*`, `+`, `?`, and the `{m,n}` forms, each on a single character, escape, or class
- An optional `^` at the start and `$` at the end
- The flags `i` and `s` only

A pattern outside the dialect, such as one with a group, alternation, a backreference, lookaround, or another flag, stops the case from loading: the case scores 0 and its error names the pattern. To allow several exact values, write a list of literals instead of an alternation. Each pattern checks values only up to a maximum length, and a longer value counts as a violation. Quantifiers can lower that length, and a leading `^` raises it, so anchor patterns with `^` and keep quantifiers few.


[​](#troubleshooting)

Troubleshooting

These are the problems authors encounter most often, keyed on what you see.


[​](#”plugin-eval-is-currently-in-early-access”)

”plugin eval is currently in early access”

Your build predates general availability of the command. Run `claude update`, then run the command again in a fresh session.


[​](#”plugin-eval-is-currently-unavailable”)

”plugin eval is currently unavailable”

Anthropic has switched the command off server-side. Nothing on your machine turns it back on; run `claude update` and try again in a fresh session later.


[​](#”is-not-a-trusted-plugin-directory-and-this-run-cannot-stop-to-ask-you-about-it”)

”is not a trusted plugin directory, and this run cannot stop to ask you about it”

This is the first run against a directory Claude Code doesn’t trust yet, and it can’t ask you because stdin or stdout isn’t a terminal or you passed `--json`. Run `claude plugin eval <dir>` once in a terminal and answer the prompt, or pass `--trust-plugin` if you trust the plugin’s code and suite. See [What a run can access](#security).


[​](#git-is-too-old-for-claude-plugin-eval)

“is too old for claude plugin eval”

The `git` on your `PATH` is older than 2.31, so `claude plugin eval` stopped before running any case and exited 1 with a message naming your version:

```python
git 2.30 is too old for claude plugin eval: it ignores the environment configuration (GIT_CONFIG_COUNT, added in git 2.31) that switches off the repository's git hooks and helper programs for the run. Install git 2.31 or newer.
```

For each run, Claude Code switches off git hooks, credential helpers, and other programs a repository’s git configuration can start. It does so through environment configuration that git reads only from version 2.31. An older git ignores that configuration, so the suite stops rather than scoring runs where those programs could execute. Install git 2.31 or later and run the suite again. Before v2.1.283, `claude plugin eval` didn’t check the git version, and on an older git the suite ran with those programs left on.


[​](#cannot-be-evaluated-where-sessions-load-it)

“cannot be evaluated where sessions load it”

You ran `claude plugin eval` in your shell with the name of a plugin installed from a marketplace you [added from a local path](plugins-loading.md#in-place-and-copied-plugins). No session could load the plugin from that marketplace now, so the command refuses with this message and exits with code 1.

```python
Error: formatter@my-marketplace cannot be evaluated where sessions load it: no session could load it now. Its marketplace is a folder whose catalog could not be read, or lists it no more. Check the marketplace with `claude plugin marketplace list`, or pass ./<dir> to evaluate a directory.
```

The sentence after `no session could load it now.` names what failed. Run `claude plugin marketplace list` in your shell and fix what the message names. For example, restore the marketplace directory or put the plugin’s entry back in its catalog. To evaluate the plugin’s source without the marketplace, pass its directory as the target, such as `claude plugin eval ./plugins/formatter`.


[​](#”no-eval-cases-found”)

”No eval cases found”

No `<case>/prompt.md` or `<case>/case.yaml` exists beneath the eval directory in effect, or your `--case` and `--tag` filters matched no case. Run from the plugin root, or run `claude plugin eval init` to create a suite.


[​](#the-baseline-arm-shows-no-plugin-or-delta-is-zero)

The baseline arm shows no plugin, or delta is zero

If the summary has no `W/OUT` column, or a case fails with “ablation requested but no plugin resolved”, the usual cause is that no plugin was found for the case. If every case resumes a transcript through `context.history_file`, the missing column is expected instead, because those cases run [one arm by default](#compare-against-a-no-plugin-baseline). Otherwise, add `plugins: ["../.."]` to the case, giving the path from the case directory to the plugin directory. If the plugin did load and `Δ` is still near zero with your `tool_used: Skill` grader failing, that’s usually a real finding, meaning the skill’s `description` doesn’t trigger on the prompt’s phrasing. Adjust the description and re-run the same suite.


[​](#”agent-type-’…’-not-found”-for-one-of-your-plugin’s-agents)

”Agent type ’…’ not found” for one of your plugin’s agents

By default each case runs both with your plugin and without it, and the runs without it are the [no-plugin baseline](#the-no-plugin-baseline). When Claude dispatches one of your plugin’s agents in a baseline run, the Agent tool call fails with `Agent type '<plugin>:<agent-name>' not found. Available agents: ...`. The list names only agents that exist without the plugin, such as the [built-in subagents](../09-Agents-Patterns/sub-agents.md#built-in-subagents). The error is expected, since `Δ` compares your plugin’s runs against the baseline. In the JSON result, the baseline runs are under `cases[].arms.without`. In runs with your plugin loaded, a case that lists `Agent` in `allowed_tools` can dispatch one of your plugin’s agents by its namespaced name, such as `my-plugin:code-reviewer` for the `code-reviewer` agent in a plugin named `my-plugin`. To skip the baseline runs, pass `--ablation none`.


[​](#everything-scores-zero-although-the-right-files-were-produced)

Everything scores zero although the right files were produced

Your graders target `files`, the list of created paths, when you meant the file’s contents. Use `{ source: file, path: <path> }` as the `target` or `focus`. Separately, `file_exists` counts only files created during the run, so a file the scaffold created or that Claude only edited is invisible to it; grade its contents, or use `tool_used` on `Edit`.


[​](#a-regex-over-the-trace-doesn’t-match-text-i-can-see)

A regex over the trace doesn’t match text I can see

- **Wrong target**: the default `target` is `last_message`, not the trace.
- **JSON escaping**: when you do target `trace`, it’s JSON per line, so quotes appear as `\"`.
- **Regex syntax**: regexes use JavaScript syntax, so put `i` in `flags` rather than writing `(?i)`.


[​](#tools-are-denied-mcp-tools-are-missing-or-bash-won’t-run)

Tools are denied, MCP tools are missing, or Bash won’t run

Anything beyond the read-only set needs your grant, such as `--allow-tools Bash Write`. Your personal MCP servers never load in a run. The plugin’s own servers don’t start unless you [opt in](#mock-mcp-servers), and their tools then also need an `--allow-tools "mcp__plugin_<plugin>_<server>__*"` grant; a mocked tool needs neither.


[​](#the-run-exits-1-but-the-results-look-fine)

The run exits 1 but the results look fine

When every case scored at or above `--threshold` and the command still exits 1, a line on stderr names the cause, even under `--json`. For example, a case file failed to load, or runs [ended on an API error](#runs-ended-on-an-api-error). For the other causes, see the [exit codes](#run-evals-in-ci). The default `--threshold` is 1.0, so the command exits 1 when any case scores below perfect. Set a threshold that matches the score you require.


[​](#json-output-path-must-end-in-json)

`--json output path must end in .json`

You put the target after `--json`, so it was read as the output path. Put the target first, as in `claude plugin eval . --json`, or give `--json` an explicit `.json` path.


[​](#a-grader-shows-passed-false-under-a-run-that-scored-1-0)

A grader shows passed: false under a run that scored 1.0

That grader is excluded from the score by design in a two-arm run, and its `scored` field is `false`. See [Score against the no-plugin baseline](#compare-against-a-no-plugin-baseline).


[​](#runs-ended-on-an-api-error)

“ended on an API error and scored 0 without grading”

This stderr warning means the API answered at least one run’s last request with an HTTP error, such as a rate limit or a plan usage limit. Such a run scores 0 with no grader results, and the command exits non-zero whatever the scores and `--threshold` are. To fix it, find each affected run’s error and resolve the cause it reports, then run the suite again:

- **Without `--json`**: the error is at the end of the run’s progress line, after `error:`
- **Under `--json`**: the error is in the [JSON result](#json-result), at `cases[].arms.with[].error` or `cases[].arms.without[].error`

The count includes [baseline](#the-no-plugin-baseline) runs too. Their scores never decide whether a case passes, so every case can pass while the command still fails. In a two-arm run, the affected case’s `W/OUT` and `Δ` print as `—`. For a usage or rate limit, see [Runs fail with a usage-limit or rate-limit error](#runs-fail-with-a-usage-limit-or-rate-limit-error-partway-through). Before v2.1.299, such a run was graded on what it had produced.


[​](#runs-fail-with-a-usage-limit-or-rate-limit-error-partway-through)

Runs fail with a usage-limit or rate-limit error partway through

Runs that end with a usage-limit or rate-limit error [score 0 without grading](#runs-ended-on-an-api-error), and the command exits non-zero. The suite still finishes and isn’t marked `partial`. Re-run after the limit resets, with `--runs 1` or a `--case` filter if you need to stay under it.


[​](#mock-call-budget-exceeded)

“mock call budget exceeded”

Every `type: agent` [mock](#mock-mcp-servers) in a run draws on one call budget of four times the case’s `max_turns`, which is 40 calls at the default of 10. Calls answered from `.replay/` recordings count too, and the case’s `mock budget` progress line prints the budget. A call past it aborts the run with score 0 and this reason, so raise `max_turns` in the case for a skill that makes many calls to agent mocks.


[​](#runs-time-out-or-hit-the-turn-cap)

Runs time out or hit the turn cap

The defaults are 10 turns and 300 seconds. Raise `max_turns` and `timeout_seconds` in the case for tasks that need more, and use `--max-cost-usd` as the cost ceiling rather than tight per-run limits.


[​](#see-also)

See also

- [Create a plugin](plugins-create.md): build the plugin you’re testing, and load it with `--plugin-dir` during development
- [Plugin commands reference](plugins-cli-reference.md#plugin-eval): the `plugin eval` and `plugin eval init` command entries. The manifest’s [`experimental.evals`](plugins-reference.md#fields) key is on the manifest reference
- [Skills](skills.md): how a skill’s description decides when Claude invokes it, which is what a case that checks whether the skill triggers is measuring
- [Sandboxing](../02-Claude-Code-CLI/sandboxing.md): the OS-level sandbox that applies when you grant Bash to a run
- [Publish a plugin](plugins-publish.md): publish the plugin once its suite passes
- [Measure plugin cost and usage](plugins-measure.md): what the plugin adds to each session’s context and whether people still use it

---

## Recommend your plugin from your CLI - Claude Code Docs

Source: https://code.claude.com/docs/en/plugin-hints
Path: 08-Plugins-Skills/plugin-hints.md

## On this page

- [How it works](#how-it-works)
- [Emit the hint](#emit-the-hint)
- [Choose where to emit](#choose-where-to-emit)
- [What the user sees](#what-the-user-sees)
- [Hint format](#hint-format)
- [Requirements](#requirements)
- [Get your plugin into the official marketplace](#get-your-plugin-into-the-official-marketplace)
- [See also](#see-also)

Plugin distribution

# Recommend your plugin from your CLI

Copy pageCopy page

Emit a one-line marker from your CLI so Claude Code prompts users to install your official plugin.

Copy pageCopy page

If you maintain a CLI or SDK and have a plugin in the official Anthropic marketplace, your tool can prompt Claude Code users to install that plugin. Your CLI writes a one-line marker to stderr when it detects it is running inside Claude Code. Claude Code reads the marker, strips it from the output, and shows the user a one-time install prompt. The protocol requires no extra commands and does not change what your CLI prints for users outside Claude Code. This page is for CLI and SDK maintainers. If you are looking to install plugins, see [Discover and install plugins](discover-plugins.md).


[​](#how-it-works)

How it works

Claude Code sets the [`CLAUDECODE`](../02-Claude-Code-CLI/env-vars.md) environment variable to `1` for every command it runs through the Bash and PowerShell tools, and for [hook](../07-Hooks/hooks.md) commands. From v2.1.172 it also sets [`CLAUDE_CODE_CHILD_SESSION`](../02-Claude-Code-CLI/env-vars.md) to `1` in those same subprocesses. When your CLI sees one of these variables, it writes a self-closing `<claude-code-hint />` tag to stderr. In hook commands the hint tag is stripped and ignored. Only Bash and PowerShell tool output triggers the install prompt. When Claude Code receives the command output, it:

1.  Scans for hint lines and removes them before the output reaches the model
2.  Checks that the hint targets a plugin in an official Anthropic marketplace
3.  Checks that the plugin is not already installed and has not been prompted before
4.  Shows the user an install prompt that names the command that emitted the hint

Claude Code never installs a plugin automatically. The user always confirms.


[​](#emit-the-hint)

Emit the hint

Hint prompts only fire for plugins listed in the official Anthropic marketplace. See [Get your plugin into the official marketplace](#get-your-plugin-into-the-official-marketplace) before you ship the integration. Gate emission on an environment variable so the marker is unlikely to appear when a human runs your CLI directly, then write the tag to stderr on its own line. Choose which variable to check:

- `CLAUDECODE`: set on every Claude Code version, so it reaches the most sessions. It is also set in tmux sessions and stdio MCP server subprocesses that Claude Code starts. IDE extensions also set it in their integrated terminals, where a human may be running your CLI directly.
- `CLAUDE_CODE_CHILD_SESSION`: set only in subprocesses Claude Code itself spawns, such as tool calls, hook commands, and [status line](../02-Claude-Code-CLI/statusline.md) commands, so the tag does not normally reach a human terminal. A long-lived process that was started inside a session, such as a tmux server, captures the variable, so shells later launched from that process still show the raw tag.

The following examples gate on `CLAUDECODE` for maximum reach and emit a hint for a plugin named `example-cli` in the official marketplace:

Node.js

Python

Go

Shell

```python
if (process.env.CLAUDECODE) {
  process.stderr.write(
    '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />\n',
  )
}
```

```python
import os, sys

if os.environ.get("CLAUDECODE"):
    print(
        '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />',
        file=sys.stderr,
    )
```

```python
if os.Getenv("CLAUDECODE") != "" {
    fmt.Fprintln(os.Stderr,
        `<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />`)
}
```

```python
if [ -n "$CLAUDECODE" ]; then
  printf '%s\n' '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />' >&2
fi
```

Replace `example-cli` with your plugin’s name in the official marketplace.


[​](#choose-where-to-emit)

Choose where to emit

You control which code paths emit the hint. Claude Code deduplicates by plugin, so emitting on every invocation has no downside. Touchpoints that work well include:

| Placement                 | Why it works                                               |
|:--------------------------|:-----------------------------------------------------------|
| `--help` output           | Claude often runs help when exploring an unfamiliar CLI    |
| Unknown-subcommand errors | Reaches the moment Claude is confused about your interface |
| Login or auth success     | The user is already in a setup mindset                     |
| First-run welcome message | A natural onboarding moment                                |


[​](#what-the-user-sees)

What the user sees

When the hint passes all checks, Claude Code shows a prompt like the following:

```python
─────────────────────────────────────────────────────────────
  Plugin recommendation

    The example-cli command suggests installing a plugin.

    Plugin: example-cli
    Marketplace: claude-plugins-official
    Official integration for example-cli deployments

    Would you like to install it?
    ❯ 1. Yes, install example-cli
      2. No
      3. No, and don't show plugin installation hints again

─────────────────────────────────────────────────────────────
```

The prompt names the command that produced the hint so users can spot a mismatch between the tool and the plugin it recommends. If the user doesn’t respond within 30 seconds, Claude Code dismisses the prompt as **No**. Prompt frequency is bounded, and some sessions never prompt:

- **Once per plugin**: after the prompt is shown, Claude Code records the plugin and never prompts for it again, regardless of the user’s answer.
- **Once per session**: across all CLIs on the machine, at most one hint prompt appears per Claude Code session.
- **Main interactive session only**: Claude Code shows the prompt only in the terminal session the user is typing into. Claude Code never prompts for a command that a [subagent](../09-Agents-Patterns/sub-agents.md) runs, and never prompts when the user runs Claude Code in [non-interactive mode](../02-Claude-Code-CLI/headless.md) with the `-p` flag or through the [Agent SDK](../05-Agent-SDK/agent-sdk-overview.md). Claude Code still strips the hint line from the command output in all of these cases.
- **Telemetry opt-outs**: sessions where analytics are disabled never show hint prompts. This includes sessions with `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` set, and sessions on third-party providers such as Amazon Bedrock or Google Cloud’s Agent Platform where the [automatic telemetry opt-out](../13-Enterprise-Admin/data-usage.md#default-behaviors-by-api-provider) applies.

Selecting **Yes** installs the plugin to user scope. Selecting **No, and don’t show plugin installation hints again** disables all future hint prompts for the user.


[​](#hint-format)

Hint format

The hint is a self-closing tag with three required attributes.

```python
<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />
```

| Attribute | Required | Description                                       |
|:----------|:---------|:--------------------------------------------------|
| `v`       | Yes      | Protocol version. `1` is the only supported value |
| `type`    | Yes      | Hint kind. `plugin` is the only supported value   |
| `value`   | Yes      | Plugin identifier in `name@marketplace` form      |

Attribute values may be quoted with double quotes or left unquoted. Unquoted values cannot contain whitespace. Escape sequences are not supported.


[​](#requirements)

Requirements

Claude Code enforces two conditions before acting on a hint. Hints that fail either check are dropped:

- **Own line**: the tag must occupy its own line. A tag embedded mid-line, for example inside a log statement, is ignored. Leading and trailing whitespace on the line is allowed.
- **Official marketplace**: the `value` must reference a plugin in an Anthropic-controlled marketplace such as `claude-plugins-official`. Hints that point to other marketplaces are silently dropped.

The hint line is always removed from the output before it reaches the model, even when the version or type is unrecognized, so the marker is never counted toward token usage. The remaining guidance is recommended but not enforced. Claude Code cannot observe whether your CLI follows it:

- **Write to stderr**: stderr keeps the tag out of shell pipelines such as `example-cli deploy | jq`. Claude Code scans both streams, so stdout also works.
- **Gate on an environment variable**: only emit when `CLAUDECODE` or `CLAUDE_CODE_CHILD_SESSION` is set. See [Emit the hint](#emit-the-hint) for how the two variables differ.


[​](#get-your-plugin-into-the-official-marketplace)

Get your plugin into the official marketplace

The hint protocol only takes effect for plugins listed in the official Anthropic marketplace, `claude-plugins-official`. Anthropic curates that marketplace at its discretion, and the in-app submission forms add plugins to the [community marketplace](plugins.md#submit-your-plugin-to-the-community-marketplace) instead, which the hint protocol does not check. If you are working with an Anthropic partner contact, reach out to them to coordinate an official-marketplace listing.


[​](#see-also)

See also

- [Create plugins](plugins.md): build the plugin your CLI recommends
- [Create and distribute a plugin marketplace](plugin-marketplaces.md): host plugins outside the official marketplace
- [Environment variables](../02-Claude-Code-CLI/env-vars.md): full reference for `CLAUDECODE` and related variables

---

## Create a marketplace - Claude Code Docs

Source: https://code.claude.com/docs/en/plugin-marketplaces
Path: 08-Plugins-Skills/plugin-marketplaces.md

## On this page

- [Create a marketplace](#create-a-marketplace)
- [Add plugin entries](#add-plugin-entries)
- [Rules for plugin entries](#rules-for-plugin-entries)
  - [Write relative paths from the marketplace root](#write-relative-paths-from-the-marketplace-root)
  - [Keep the entry name and the manifest name the same](#keep-the-entry-name-and-the-manifest-name-the-same)
- [Choose a plugin source](#choose-a-plugin-source)
- [Validate and test](#validate-and-test)
  - [Problems that validation reports](#problems-that-validation-reports)
  - [Problems that surface when you add or install](#problems-that-surface-when-you-add-or-install)
  - [Test an edit to a plugin](#test-an-edit-to-a-plugin)
  - [Remove the marketplace to start over](#remove-the-marketplace-to-start-over)
- [Host your marketplace](#host-your-marketplace)
- [Next steps](#next-steps)

Run a marketplace

# Create a marketplace

Copy pageCopy page

Build a plugin marketplace from a marketplace.json file and test it locally before you host it.

Copy pageCopy page

A plugin marketplace is a directory or repository with a `.claude-plugin/marketplace.json` file that lists your plugins and where to fetch each one. You push the directory to a git host, and anyone with access registers it in Claude Code with one command and installs your plugins from it. Create your own marketplace when you want a group you choose, such as your team or your organization, to install your plugins and keep receiving your updates from a catalog you control. The repository can be private, it can list as many plugins as you like, and an administrator can [require it on every machine](plugins-org.md).

These cases are covered on other pages:

- **Sharing one plugin with a few people**: send them the plugin’s directory or a `.zip` of it. See [Share a plugin without a marketplace](plugins-publish.md#share-a-plugin-without-a-marketplace).
- **Offering a plugin to everyone**: submit it to Anthropic’s directory. See [Submit to Anthropic’s directory](plugins-publish.md#submit-to-anthropics-directory).
- **Using a plugin yourself**: load it with `--plugin-dir` or save it in your skills directory. See [Develop without a marketplace](plugins-create.md#develop-without-a-marketplace).

Start with [Create a marketplace](#create-a-marketplace) to build one on your own machine and install a plugin from it, then [add more plugin entries](#add-plugin-entries).


[​](#create-a-marketplace)

Create a marketplace

The following steps create a marketplace on your machine, add a plugin to it, register it in Claude Code, and install the plugin from it. That is the whole loop, and it’s the same loop your users go through once you host the marketplace somewhere they can reach. Run every command in your shell, from the directory where you want `my-marketplace/` created. You need a plugin to list. The example uses `my-first-plugin` from [Create your first plugin](plugins-create.md#create-your-first-plugin), a plugin with one skill that you run as `/my-first-plugin:hello`; build it first if you don’t have a plugin yet. To use a plugin of your own instead, substitute its directory and its `name` wherever the steps say `my-first-plugin`. For what a plugin directory can contain, see the [plugin directory explorer](plugins-components.md#explore-the-plugin-directory).

1

Set up the marketplace directory

A marketplace is a directory with a `.claude-plugin/marketplace.json` file, plus the plugins it lists. Create the marketplace directory and its `.claude-plugin/` folder, then copy your plugin in under `plugins/`:

```python
mkdir -p my-marketplace/.claude-plugin my-marketplace/plugins
cp -r my-first-plugin my-marketplace/plugins/
```

Check that the plugin is valid where it now sits, so that any later error is about the marketplace and not the plugin:

```python
claude plugin validate ./my-marketplace/plugins/my-first-plugin
```

The last line of the output reads `✔ Validation passed`.

2

Create the marketplace file

Save `marketplace.json` at `my-marketplace/.claude-plugin/marketplace.json`. The file requires a `name`, an `owner`, and a `plugins` array.Each object in `plugins` is a plugin entry and needs a `name` and a `source`. Write the entry’s `source` as a path from the marketplace root. The root is `my-marketplace/`, the directory that contains `.claude-plugin/`.

my-marketplace/.claude-plugin/marketplace.json

```python
{
  "name": "my-marketplace",
  "description": "Plugins for my team",
  "owner": {
    "name": "Your Name"
  },
  "plugins": [
    {
      "name": "my-first-plugin",
      "source": "./plugins/my-first-plugin",
      "description": "A greeting plugin to learn the basics"
    }
  ]
}
```

3

Validate the marketplace

Run `claude plugin validate` on the marketplace directory to check the JSON syntax, the required fields, and each plugin entry in its `.claude-plugin/marketplace.json`.

```python
claude plugin validate ./my-marketplace
```

For the file as written in step 2, the last line of the output reads `✔ Validation passed`.

4

Add the marketplace and install the plugin

Register the directory as a marketplace.

```python
claude plugin marketplace add ./my-marketplace
```

The command prints `✔ Successfully added marketplace: my-marketplace (declared in user settings)`, which means the marketplace is recorded in your user settings file.Install the plugin. The install id is the entry’s `name`, an `@`, and the marketplace `name`.

```python
claude plugin install my-first-plugin@my-marketplace
```

The command prints `✔ Successfully installed plugin: my-first-plugin@my-marketplace (scope: user)`.Inside a session, `/plugin marketplace add ./my-marketplace` registers the marketplace the same way. `/plugin install my-first-plugin@my-marketplace` opens the plugin’s details in the `/plugin` panel, where you install it. For that flow, see [Install and manage plugins](discover-plugins.md).

5

Confirm the plugin loaded

List installed plugins.

```python
claude plugin list
```

The output lists `my-first-plugin@my-marketplace` with `Status: ✔ enabled`.To see what the plugin loaded, show its details.

```python
claude plugin details my-first-plugin
```

The `Component inventory` section reads `Skills (1) hello`.To run the skill, start a session and enter `/my-first-plugin:hello`. Claude greets you. The command has the plugin’s name as a prefix, as every plugin skill’s name does.


[​](#add-plugin-entries)

Add plugin entries

Every plugin you distribute is one object in the `plugins` array of `marketplace.json`. To add a second plugin, add a second object. These fields cover most entries:

- `name`: the identifier people type before `@` when they install. [Plugin entries](plugins-marketplace-reference.md#plugin-entries) gives the characters a name can use.
- `source`: where Claude Code fetches the plugin from. Write a relative path string for a plugin inside the marketplace directory, as in [the walkthrough](#create-a-marketplace), or a source object for a plugin outside it. See [Choose a plugin source](#choose-a-plugin-source).
- `description`: the line people see next to the plugin when they browse your marketplace in `/plugin`.

For the full field list, see [Plugin entries](plugins-marketplace-reference.md#plugin-entries), which also covers the [`plugin.json`](plugins-reference.md) fields an entry can set and when they apply.


[​](#rules-for-plugin-entries)

Rules for plugin entries

Most failed installs from a new marketplace come from a relative path written from the wrong directory, or from an entry name that differs from the `name` in the plugin’s `plugin.json`.


[​](#write-relative-paths-from-the-marketplace-root)

Write relative paths from the marketplace root

The marketplace root is the directory that contains `.claude-plugin/`. In [the walkthrough](#create-a-marketplace), that’s `my-marketplace/`, so the entry’s `source` is `"./plugins/my-first-plugin"`. The path doesn’t start inside `.claude-plugin/`, so don’t use `..` to leave it. A path with `..` and a path to a missing directory fail at different commands:

- **A path with `..`**: `claude plugin validate` reports the entry as invalid. The message begins `Path contains "..": ./../plugins/my-first-plugin`.
- **A path to a directory that doesn’t exist**: `claude plugin validate` passes. `claude plugin install` fails with `Source path does not exist: <path>`, and `<path>` is the absolute location Claude Code checked.


[​](#keep-the-entry-name-and-the-manifest-name-the-same)

Keep the entry name and the manifest name the same

A marketplace plugin has an entry `name` in `marketplace.json` and a `name` in its own `plugin.json`, called the manifest name. Each name appears in different places:

- **Entry name**: the install id, `<entry-name>@<marketplace>`. It’s what people type to install, what `claude plugin list` shows, and the key Claude Code writes under [`enabledPlugins`](../02-Claude-Code-CLI/settings-reference.md#enabledplugins) in their settings file.
- **Manifest name**: the prefix on the plugin’s skills, and the name `claude plugin details` takes.

When the two names differ and someone installs by the manifest name, Claude Code reports `Plugin "<manifest-name>" not found in marketplace "<marketplace>"`. Keep the two names the same. For more on how Claude Code uses the two names, see [Plugin loading reference](plugins-loading.md#find-where-a-plugin-came-from).


[​](#choose-a-plugin-source)

Choose a plugin source

Each plugin entry in `marketplace.json` has a `source` that tells Claude Code where to fetch that one plugin. Pick the source by where the plugin’s files are stored. The table lists the sources most marketplace owners use.

| Source        | Use it when                                                               | Minimal `source` value                                                                    |
|:--------------|:--------------------------------------------------------------------------|:------------------------------------------------------------------------------------------|
| Relative path | The plugin’s files are inside the marketplace directory itself            | `"./plugins/my-first-plugin"`                                                             |
| `github`      | The plugin is a GitHub repository of its own                              | `{ "source": "github", "repo": "your-org/my-first-plugin" }`                              |
| `git-subdir`  | The plugin is a subdirectory of some other repository, such as a monorepo | `{ "source": "git-subdir", "url": "your-org/monorepo", "path": "tools/my-first-plugin" }` |

In a `git-subdir` source, `url` takes a git URL or an `owner/repo` GitHub shorthand. A plugin can also come from one of these source types:

- `url`: a git repository by URL, on any host
- `archive`: a zip file downloaded over HTTPS
- `npm`: an npm package
- `command`: a directory produced by running a command on the machine where the plugin is installed

For the fields of every source type, and for pinning a git-based source to a `ref` or `sha`, see [Plugin sources](plugins-marketplace-reference.md#plugin-sources).


[​](#validate-and-test)

Validate and test

As you add plugins, run `claude plugin validate ./my-marketplace` in your shell after every edit, and install from the marketplace on your own machine before you share it. Validation and installation catch different problems.


[​](#problems-that-validation-reports)

Problems that validation reports

`claude plugin validate` reads only files inside the marketplace directory. It reports:

- JSON syntax errors, as `json: Invalid JSON syntax: <reason>`
- Missing required fields, such as `owner: Invalid input`
- A marketplace or plugin name that breaks the naming rules in the [marketplace reference](plugins-marketplace-reference.md#top-level-fields)
- A relative `source` that contains `..`
- Unknown fields at the top level or in a plugin entry, as warnings
- Problems in the `plugin.json` of each relative-path plugin, as `plugins[N] plugin.json → <field>: <message>`

For every message `validate` can print, see [Validation messages](plugins-marketplace-reference.md#validation-messages). For its flags and exit codes, see [`plugin validate`](plugins-cli-reference.md#plugin-validate).


[​](#problems-that-surface-when-you-add-or-install)

Problems that surface when you add or install

Problems that `claude plugin validate` doesn’t report appear when you add the marketplace or install from it:

- **When you add the marketplace**: the exact [official marketplace names](plugins-marketplace-reference.md#reserved-names), such as `claude-plugins-official`, pass validation. When you add a marketplace with one of those names, Claude Code refuses it with a message that starts `The name '<name>' is reserved for official Anthropic marketplaces`.
- **When you install a plugin**:
  - Claude Code first fetches a `github`, `git-subdir`, or other remote source when you install the plugin, so a wrong `repo` or `path` appears then.
  - A relative `source` whose directory doesn’t exist also fails at install, with `Source path does not exist: <path>`.


[​](#test-an-edit-to-a-plugin)

Test an edit to a plugin

In [the walkthrough](#create-a-marketplace), you added `my-marketplace` from a local directory with a relative-path `source`. With that setup, Claude Code reads the plugin’s files directly from `my-marketplace/plugins/`. Your edits take effect at the next session start or when you run `/reload-plugins` in a session, with no change to the plugin’s `version`. People who install from your hosted marketplace get a copy in the plugin cache instead. For how they receive a new version, see [Keep users up to date](plugins-host-marketplace.md#keep-users-up-to-date).


[​](#remove-the-marketplace-to-start-over)

Remove the marketplace to start over

To remove everything and start over, run `claude plugin marketplace remove my-marketplace` in your shell. The command removes the marketplace and uninstalls its plugins.


[​](#host-your-marketplace)

Host your marketplace

Once you can install a plugin from the marketplace on your own machine, as in [Create a marketplace](#create-a-marketplace), push the marketplace directory to a git host. Your teammates then run `claude plugin marketplace add <owner>/<repo>` in their shell for a GitHub repository, or the same command with the repository URL. They then install a plugin by name as in [the walkthrough](#create-a-marketplace). For private-repository access, updates, versioning, and renaming or removing entries, see [Host and maintain a marketplace](plugins-host-marketplace.md).


[​](#next-steps)

Next steps

- [Host and maintain a marketplace](plugins-host-marketplace.md): pick a host, keep users up to date, and rename or remove plugins safely
- [Marketplace reference](plugins-marketplace-reference.md): `marketplace.json` fields and source types
- [Manage plugins for your organization](plugins-org.md): require your marketplace and its plugins on every machine
- [Suggest plugins by relevance](plugins-relevance.md): have Claude Code suggest a plugin from your marketplace when a session matches

---

## Recommend plugins for your org - Claude Code Docs

Source: https://code.claude.com/docs/en/plugin-relevance
Path: 08-Plugins-Skills/plugin-relevance.md

## On this page

- [How it works](#how-it-works)
- [Add relevance to a plugin entry](#add-relevance-to-a-plugin-entry)
- [Field reference](#field-reference)
  - [relevance](#relevance)
  - [relevance.signals](#relevance-signals)
- [Enable suggestions in managed settings](#enable-suggestions-in-managed-settings)
- [What the user sees](#what-the-user-sees)
- [Validate your marketplace](#validate-your-marketplace)
- [See also](#see-also)

Plugin distribution

# Recommend plugins for your org

Copy pageCopy page

Add a relevance block to marketplace plugin entries so Claude Code suggests them when a user’s work matches.

Copy pageCopy page

If you operate a plugin marketplace for your organization, you can have Claude Code suggest specific plugins to users based on what they are working on. Add a `relevance` block to a plugin’s entry in `marketplace.json`, then allowlist the marketplace in managed settings. When a user’s session matches one of the declared signals, Claude Code surfaces an install suggestion for that plugin. Marketplace-declared suggestions are opt-in per marketplace through [managed settings](../13-Enterprise-Admin/managed-settings.md). No marketplace’s `relevance` declarations produce suggestions until an administrator adds it to the allowlist, including the official Anthropic marketplace. Claude Code also includes one built-in suggestion that is independent of this allowlist; that tip and all marketplace-declared tips are disabled when [`spinnerTipsEnabled`](../02-Claude-Code-CLI/settings-reference.md#spinnertipsenabled) is set to `false`. This page is for marketplace operators and enterprise administrators. If you are looking to install plugins, see [Discover and install plugins](discover-plugins.md).


[​](#how-it-works)

How it works

Each plugin entry in `marketplace.json` can carry a `relevance` object. The object names a topic and one or more signals. A signal is a pattern that Claude Code tests against the current session, such as the working directory or files Claude has read. Signal matching happens locally on the user’s machine. The matching adds no network traffic and does not report which signals matched, or their values, to Anthropic or to the marketplace operator. When a signal matches and the plugin is not already installed, Claude Code shows the plugin in three places:

- **Spinner tip**: a “Working with *topic*? Install the *plugin* plugin” message with the `/plugin install` command appears below the spinner while Claude is responding.
- **Session-start suggestion**: if the `cwd` signal matches the working directory, a one-line `plugin suggestion: <name>@<marketplace> · /plugin` notification appears before the first turn.
- **`/plugin` Discover tab**: the plugin is pinned to the top of the Discover list with an annotation such as “suggested for this directory” or “suggested for stripe commands”.

The spinner tip and the session-start notification are part of the spinner-tips system. Claude Code disables both when `spinnerTipsEnabled` resolves to `false` across your settings files, or when `excludeDefault` resolves to `true` across the [`spinnerTipsOverride`](../02-Claude-Code-CLI/settings-reference.md#spinnertipsoverride) keys in user, `--settings`, and managed settings and those keys configure at least one tip or a `tipsFile`. The Discover-tab pin is independent of tip settings. Claude Code never installs a plugin automatically. The user always confirms.


[​](#add-relevance-to-a-plugin-entry)

Add relevance to a plugin entry

Add a `relevance` object to the plugin’s entry in your `marketplace.json`. The following example declares that the `terraform-helpers` plugin is relevant when Claude reads a `.tf` file or when Claude runs `terraform`:

```python
{
  "name": "acme-corp-plugins",
  "owner": { "name": "Acme Platform Team" },
  "plugins": [
    {
      "name": "terraform-helpers",
      "source": "./plugins/terraform-helpers",
      "description": "Acme conventions and helpers for Terraform",
      "relevance": {
        "topic": "Terraform",
        "signals": {
          "cli": ["terraform"],
          "filesRead": ["**/*.tf"]
        }
      }
    }
  ]
}
```

A plugin with a `relevance` block but no matching signal behaves like any other marketplace entry. It appears in the Discover list in its normal position and never surfaces as a spinner tip.


[​](#field-reference)

Field reference


[​](#relevance)

`relevance`

| Field     | Type   | Description                                                                                                                                                                                                                                                                                                                                                       |
|:----------|:-------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `topic`   | string | Optional. The phrase that fills “Working with *topic*?” in the spinner tip. Often the product name, for example `Stripe`. Use a domain such as `design` when the plugin name does not read naturally as a topic. Defaults to the plugin name with each hyphen segment capitalized. The session-start notification does not use this value. Maximum 64 characters. |
| `signals` | object | Matchers that determine when the plugin is relevant. At least one signal is required for the plugin to be suggestible. See the table below.                                                                                                                                                                                                                       |


[​](#relevance-signals)

`relevance.signals`

| Field          | Type             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
|:---------------|:-----------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `cwd`          | array of strings | Glob patterns matched against the session’s working directory. Matched as an absolute path and, when inside a git repository, as a path relative to the repository root. Forward-slash normalized and case-insensitive. Every pattern matches the directory itself and everything under it, so `infra`, `infra/`, and `infra/**` behave identically. This is the only signal that can match at session start, before the first turn. Maximum 10 patterns of 256 characters each.                                                                                                                                                                                                                                                                                                                               |
| `cli`          | array of strings | Command names from shell commands Claude has run this session, for example `["stripe"]`. Applies on every platform: commands run on Windows through PowerShell or Git Bash are recorded the same way. Claude Code records one command name per shell tool invocation: the first token after any leading environment variable assignments and `sudo`. Compound commands contribute only their leading command, so `cd infra && terraform plan` records `cd`, not `terraform`. Exact match. Maximum 10 entries of 64 characters each.                                                                                                                                                                                                                                                                            |
| `hosts`        | array of strings | Hostnames seen in `http://` or `https://` URLs in Bash commands this session, for example `["api.stripe.com"]`. Bare lowercase hostname only: no scheme, port, or path. Exact case-insensitive match. Maximum 20 entries of 128 characters each.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `filesRead`    | array of strings | Glob patterns matched against the paths of files Claude has read this session, for example `["**/*.tf"]`. Forward-slash normalized and case-insensitive. Maximum 10 patterns of 256 characters each.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `manifestDeps` | array of objects | Dependencies declared in package manifests Claude has read this session. Each entry is `{ "file": "...", "pattern": "..." }`, where `file` is a regular expression matched against the manifest file’s path as recorded in session state, typically an absolute path, and `pattern` is a regular expression matched against that file’s contents. Anchor `file` at the end, for example `[/\\\\]package\\.json$` in JSON-escaped form, because a start-anchored pattern never matches an absolute path. Paths are not separator-normalized for this signal, so Windows paths use backslashes. Manifest files larger than 512 KB are skipped. Both values are JavaScript `RegExp` source strings of at most 256 characters. `file` matches case-insensitively. `pattern` is case-sensitive. Maximum 10 entries. |

The `cli`, `hosts`, `filesRead`, and `manifestDeps` signals need session history, so they can only match on the spinner tip and the Discover tab. The `filesRead` and `manifestDeps` signals test the session’s recorded file state, which also includes files Claude has written or edited and auto-loaded `CLAUDE.md` memory files. For these two signals, Claude Code skips paths under its own [configuration directory](../02-Claude-Code-CLI/claude-directory.md) and its temporary directories. The following example uses `manifestDeps` to suggest a Stripe plugin once Claude has read a `package.json` that depends on `stripe`. The `file` pattern uses `[/\\\\]` so it matches both forward-slash and backslash path separators, and `\\.` so the dot is literal. In JSON, each backslash in the regular expression is written twice.

```python
{
  "name": "stripe-helpers",
  "source": "./plugins/stripe-helpers",
  "relevance": {
    "topic": "Stripe",
    "signals": {
      "manifestDeps": [
        {
          "file": "[/\\\\]package\\.json$",
          "pattern": "\"stripe\"\\s*:"
        }
      ]
    }
  }
}
```

Claude Code ignores unknown fields under `relevance` and `relevance.signals` at load time, so older clients continue to load your marketplace.


[​](#enable-suggestions-in-managed-settings)

Enable suggestions in managed settings

Declaring `relevance` in `marketplace.json` is not enough on its own. An administrator must allowlist the marketplace in [managed settings](../13-Enterprise-Admin/managed-settings.md) before its suggestions appear to users. Add the marketplace name to `pluginSuggestionMarketplaces`. For any marketplace other than the official Anthropic marketplace, also declare the marketplace source in the same managed settings, either as that name’s entry in `extraKnownMarketplaces` or as an entry in `strictKnownMarketplaces`. The allowlisted name is ignored if the marketplace registered on the machine came from a different source. This prevents an unrelated source from registering under an allowlisted name to have its plugins suggested across your org. The following `managed-settings.json` registers an org marketplace from a GitHub repository and enables its suggestions:

```python
{
  "extraKnownMarketplaces": {
    "acme-corp-plugins": {
      "source": {
        "source": "github",
        "repo": "acme-corp/claude-plugins"
      }
    }
  },
  "pluginSuggestionMarketplaces": ["acme-corp-plugins"]
}
```

The official marketplace is exempt from the source-declaration requirement because its name can only register from the official Anthropic source. Allowlisting the name alone is sufficient:

```python
{
  "pluginSuggestionMarketplaces": ["claude-plugins-official"]
}
```


[​](#what-the-user-sees)

What the user sees

When a signal matches during a session, the spinner tip reads:

```python
Working with Terraform? Install the terraform-helpers plugin:
/plugin install terraform-helpers@acme-corp-plugins
```

At session start, a matching `cwd` signal surfaces the one-line notification:

```python
plugin suggestion: terraform-helpers@acme-corp-plugins · /plugin
```

A given plugin’s suggestion appears at most once every three sessions across the spinner tip and the session-start notification combined, and neither repeats once the plugin is installed. The session-start notification additionally stops appearing after the suggestion has been shown twice. In the `/plugin` Discover tab, the plugin is pinned above the other results with an annotation that names the matching signal, such as `suggested for this directory` or `suggested for terraform commands`. The Discover tab pins a given plugin once; later visits list it in normal order.


[​](#validate-your-marketplace)

Validate your marketplace

Run `claude plugin validate` against your marketplace directory to check the `relevance` block before publishing:

```python
claude plugin validate ./my-marketplace
```

The validator reports unknown keys under `relevance` and `relevance.signals` as warnings, flags a `relevance` value that is not an object, and rejects a `signals.hosts` entry that includes a scheme, port, or path.


[​](#see-also)

See also

- [Create and distribute a plugin marketplace](plugin-marketplaces.md): build the marketplace that hosts your plugins
- [Recommend your plugin from your CLI](plugin-hints.md): prompt users from your own CLI instead of from Claude Code’s session signals
- [All settings](../02-Claude-Code-CLI/settings-reference.md#pluginsuggestionmarketplaces): `pluginSuggestionMarketplaces` and `extraKnownMarketplaces`

---

## Anthropic's marketplaces - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/anthropic-marketplaces
Path: 08-Plugins-Skills/plugins-anthropic-marketplaces.md

## On this page

- [Anthropic’s marketplaces](#anthropic%E2%80%99s-marketplaces)
  - [The demo marketplace in anthropics/claude-code](#the-demo-marketplace-in-anthropics%2Fclaude-code)
- [Find plugins in the official marketplace](#find-plugins-in-the-official-marketplace)
- [Browse and install from Anthropic’s marketplaces](#browse-and-install-from-anthropic%E2%80%99s-marketplaces)
  - [Add the community or demo marketplace](#add-the-community-or-demo-marketplace)
- [Third-party marketplaces](#third-party-marketplaces)
- [Next steps](#next-steps)

Use plugins

# Anthropic's marketplaces

Copy pageCopy page

Anthropic’s official, community, and demo plugin marketplaces for Claude Code: their names, repositories, how you add each, and where to browse their plugins.

Copy pageCopy page

Anthropic publishes three general-purpose plugin marketplaces for Claude Code: [official](https://github.com/anthropics/claude-plugins-official), [community](https://github.com/anthropics/claude-plugins-community), and [demo](https://github.com/anthropics/claude-code). Each is a catalog of plugins in its own GitHub repository. When you install a plugin from one of them in a Claude Code session, you type the marketplace’s name after `@`, as in `/plugin install commit-commands@claude-plugins-official`. Use this page to distinguish the three marketplaces and to find where to check whether the official one holds a given plugin.

These cases are covered on other pages:

- **How to install a plugin**: see [Install plugins](discover-plugins.md)
- **A failed install**: see [Troubleshoot plugins](plugins-troubleshooting.md)

Go to the part of the page you need:

- To distinguish the three marketplaces by repository, marketplace name, and how you get each one, see [Anthropic’s marketplaces](#anthropic%E2%80%99s-marketplaces).
- To find a plugin in the official marketplace, see [Find plugins in the official marketplace](#find-plugins-in-the-official-marketplace).


[​](#anthropic’s-marketplaces)

Anthropic’s marketplaces

A marketplace is a catalog of plugins that a repository defines in its `.claude-plugin/marketplace.json` file. The official, community, and demo marketplaces each come from their own GitHub repository. Anthropic also publishes topic-specific marketplaces, such as `anthropics/skills` and `anthropics/knowledge-work-plugins`, which you add in a Claude Code session with `/plugin marketplace add <owner>/<repo>`. This table gives each marketplace’s repository and marketplace name, which is what you type after `@` when you install a plugin from that marketplace. The community marketplace’s name is `claude-community`, not its repository name.

|                  | Official                                                                                                                                                                                                                                                                                                                                                                                             | Community                                                                                              | Demo                                                                                      |
|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------|
| Repository       | [`anthropics/claude-plugins-official`](https://github.com/anthropics/claude-plugins-official)                                                                                                                                                                                                                                                                                                        | [`anthropics/claude-plugins-community`](https://github.com/anthropics/claude-plugins-community)        | [`anthropics/claude-code`](https://github.com/anthropics/claude-code/tree/main/plugins)   |
| Marketplace name | `claude-plugins-official`                                                                                                                                                                                                                                                                                                                                                                            | `claude-community`                                                                                     | `claude-code-plugins`                                                                     |
| What’s in it     | Plugins Anthropic maintains, plus plugins from partners and other authors                                                                                                                                                                                                                                                                                                                            | Third-party plugins that their authors submitted to Anthropic                                          | A small set of example plugins that show what a plugin can contain                        |
| How you get it   | Claude Code adds it the first time you start an interactive terminal session, unless a [managed policy](plugins-org.md#allow-the-official-marketplace-and-your-own) or `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL` blocks it. See [Marketplace `claude-plugins-official` not found](plugins-troubleshooting.md#marketplace-claude-plugins-official-not-found) if it’s missing | You add it in a Claude Code session with `/plugin marketplace add anthropics/claude-plugins-community` | You add it in a Claude Code session with `/plugin marketplace add anthropics/claude-code` |

If you wrote a plugin and want other people to install it, see [Publish a plugin](plugins-publish.md), which covers your own marketplace and submitting to Anthropic’s directory.


[​](#the-demo-marketplace-in-anthropics/claude-code)

The demo marketplace in `anthropics/claude-code`

If a tutorial or an older set of instructions tells you to run `/plugin marketplace add anthropics/claude-code`, that adds the demo marketplace, named `claude-code-plugins`. It isn’t the official marketplace. Most of the demo marketplace’s plugins are also in the official marketplace under the same names. For example, `code-review`, `feature-dev`, `commit-commands`, and `security-guidance` are in both. Install those from `claude-plugins-official` so you don’t have two copies installed.


[​](#find-plugins-in-the-official-marketplace)

Find plugins in the official marketplace

By default, Claude Code adds the official marketplace, `claude-plugins-official`, for you. Most of what it lists comes from partners and other authors rather than from Anthropic: tool vendors publish plugins that connect Claude Code to their services, and Anthropic maintains a smaller set of its own, such as `commit-commands`, `code-review`, `feature-dev`, and the [language server plugins](plugins-code-intelligence.md). The catalog changes often, so this page doesn’t list it. To see what’s in it, use the **Discover** tab of `/plugin` in a Claude Code session, which you can search, or browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web.


[​](#browse-and-install-from-anthropic’s-marketplaces)

Browse and install from Anthropic’s marketplaces

You can search Anthropic’s marketplaces for a plugin in Claude Code, on the web, or on GitHub:

- **In Claude Code, by browsing**: run `/plugin` in an interactive session. Its **Discover** tab lists the plugins from your marketplaces.
- **In Claude Code, by name**: run `/plugin install <name>` in a session, which looks the name up in your marketplaces. If the plugin is in one of them, its details open in the `/plugin` panel, and nothing installs until you choose an [installation scope](discover-plugins.md#install-a-plugin) and confirm there. If it isn’t, you see `Plugin "<name>" not found in any marketplace`.
- **On the web**: search the full catalog on [Claude Marketplace](https://claude.com/marketplace/plugins), which shows install counts and marks some plugins **Anthropic verified**.
- **On GitHub**: open `.claude-plugin/marketplace.json` in the marketplace’s repository, such as [`anthropics/claude-plugins-official`](https://github.com/anthropics/claude-plugins-official). That file is the catalog itself.

Anthropic’s directory is separate from these marketplaces. The directory is the catalog on claude.ai. A plugin you add from the directory on claude.ai reaches Claude Code through [account sync](plugins-loading.md#synced-plugins). To list your own plugin there, see [Submit to Anthropic’s directory](plugins-publish.md#submit-to-anthropics-directory). To install from the desktop app or from a script, or to see what a cloud session loads, see [Install plugins](discover-plugins.md).


[​](#add-the-community-or-demo-marketplace)

Add the community or demo marketplace

The community and demo marketplaces aren’t registered until you add them in a Claude Code session:

- **Community**: run `/plugin marketplace add anthropics/claude-plugins-community`, then install with the `@claude-community` suffix.
- **Demo**: run `/plugin marketplace add anthropics/claude-code`, then install with the `@claude-code-plugins` suffix.

If `claude-plugins-official` isn’t on the **Marketplaces** tab of `/plugin`, add it the same way with `/plugin marketplace add anthropics/claude-plugins-official`. For `not found` errors and marketplaces that won’t add, see [Troubleshoot plugins](plugins-troubleshooting.md#install-a-plugin).


[​](#third-party-marketplaces)

Third-party marketplaces

Many popular plugins aren’t in any Anthropic marketplace. They’re in their authors’ own marketplaces, usually a GitHub repository with a `.claude-plugin/marketplace.json` at its root. Anthropic doesn’t review third-party marketplaces, so read [Plugin security and trust](plugins-security.md) before you add one. To use a third-party marketplace, add its repository in a Claude Code session with `/plugin marketplace add <owner>/<repo>`, then install with `/plugin install <plugin>@<marketplace-name>`. The marketplace name is the `name` field of that `marketplace.json`, and Claude Code prints it once it has added the marketplace. For other ways to add a marketplace, see [Add a marketplace](discover-plugins.md#add-a-marketplace).


[​](#next-steps)

Next steps

- [Install and manage plugins](discover-plugins.md): install a plugin from one of these marketplaces and choose a scope
- [Plugin security and trust](plugins-security.md): what a plugin can do on your machine and how to review one before you install it
- [Code intelligence plugins](plugins-code-intelligence.md): install one of the official marketplace’s language-server plugins
- [Create a marketplace](plugin-marketplaces.md): run your own marketplace alongside Anthropic’s

---

## Recommend your plugin from your CLI - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/cli-hints
Path: 08-Plugins-Skills/plugins-cli-hints.md

## On this page

- [Emit the hint](#emit-the-hint)
- [Hint format](#hint-format)
- [Check when the prompt appears](#check-when-the-prompt-appears)
- [Preview what the user sees](#preview-what-the-user-sees)
- [Next steps](#next-steps)

Create plugins

# Recommend your plugin from your CLI

Copy pageCopy page

Prompt Claude Code users to install your official-marketplace plugin by emitting a claude-code-hint tag from your CLI or SDK.

Copy pageCopy page

If you maintain a CLI or SDK, your tool can prompt Claude Code users to install your plugin. When your CLI detects that it’s running inside Claude Code, have it write a one-line `<claude-code-hint />` tag to stderr. Claude Code removes the line from Bash and PowerShell tool output before the model sees the output, then shows the user a one-time install prompt. This page applies only if your plugin is listed in `claude-plugins-official` or another marketplace with one of Anthropic’s [official marketplace names](plugins-security.md#official-marketplace-names). The community marketplace, `claude-community`, isn’t one of them.

To publish a plugin, see [Publish and distribute a plugin](plugins-publish.md).


[​](#emit-the-hint)

Emit the hint

Emit the tag only when `CLAUDECODE` or `CLAUDE_CODE_CHILD_SESSION` is set, so it doesn’t appear when a person runs your CLI directly. Claude Code sets `CLAUDECODE=1` in the commands it runs through the Bash and PowerShell tools and in hook commands. On v2.1.172 and later it also sets `CLAUDE_CODE_CHILD_SESSION=1` there. The variables differ in which processes carry them:

- **`CLAUDECODE`**: set by every Claude Code version. IDE extensions also set it in their integrated terminals, so a gate on `CLAUDECODE` alone also emits the tag when a person runs your CLI themselves in one of those terminals
- **`CLAUDE_CODE_CHILD_SESSION`**: set only in subprocesses Claude Code itself starts. Use it when you can require v2.1.172 or later

The [environment variables reference](../02-Claude-Code-CLI/env-vars.md) has the details. The following examples gate on `CLAUDECODE` for the widest reach and emit a hint for a plugin named `example-cli` in the official marketplace:

Node.js

Python

Go

Shell

```python
if (process.env.CLAUDECODE) {
  process.stderr.write(
    '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />\n',
  )
}
```

```python
import os, sys

if os.environ.get("CLAUDECODE"):
    print(
        '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />',
        file=sys.stderr,
    )
```

```python
if os.Getenv("CLAUDECODE") != "" {
    fmt.Fprintln(os.Stderr,
        `<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />`)
}
```

```python
if [ -n "$CLAUDECODE" ]; then
  printf '%s\n' '<claude-code-hint v="1" type="plugin" value="example-cli@claude-plugins-official" />' >&2
fi
```

Replace `example-cli` with your plugin’s name in the official marketplace. You can emit the hint on every invocation, because Claude Code prompts for each plugin once. To check the emitter, run `CLAUDECODE=1 example-cli` in a terminal and confirm the tag line appears on stderr, then run `example-cli` without the variable and confirm nothing extra prints.


[​](#hint-format)

Hint format

The tag must occupy its own line; Claude Code ignores a tag embedded mid-line. The tag takes three attributes, all required:

| Attribute | Description                                       |
|:----------|:--------------------------------------------------|
| `v`       | Protocol version. `1` is the only supported value |
| `type`    | Hint kind. `plugin` is the only supported value   |
| `value`   | Plugin identifier in `name@marketplace` form      |

Values may be double-quoted or unquoted; an unquoted value can’t contain whitespace. Claude Code removes the line from the output even when `v` or `type` is unrecognized.


[​](#check-when-the-prompt-appears)

Check when the prompt appears

The prompt appears only in interactive terminal sessions. In `claude -p` runs, in subagent runs, and in hook command output, the tag is stripped and no prompt is shown. All of these checks must also pass:

- **Official and installable**: `value` names a plugin that Claude Code finds in its local copy of an official marketplace, that isn’t already installed, and that no policy blocks
- **Analytics on**: a session where Claude Code’s analytics are off never prompts, for example one with `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` set, or one on a third-party provider such as Amazon Bedrock, where the [automatic telemetry opt-out](../13-Enterprise-Admin/data-usage.md#default-behaviors-by-api-provider) applies
- **Frequency limits**: one prompt per session, one prompt ever per plugin regardless of the user’s answer, and none once 100 plugins have been prompted for on that machine
- **Not turned off**: the user hasn’t chosen **No, and don’t show plugin installation hints again**
- **Local, attended session**: the session’s workspace is local rather than on a cloud or remote machine, and the session isn’t running unattended. For example, a session started with `--cloud`, one serving Remote Control, or an agent-team teammate never prompts


[​](#preview-what-the-user-sees)

Preview what the user sees

When the checks in [Check when the prompt appears](#check-when-the-prompt-appears) pass, Claude Code shows a **Plugin recommendation** dialog like the following:

```python
─────────────────────────────────────────────────────────────
  Plugin recommendation

    The example-cli command suggests installing a plugin.

    Plugin: example-cli
    Marketplace: claude-plugins-official
    Description: Official integration for example-cli deployments

    Would you like to install it?
    ❯ 1. Yes, install
      2. No
      3. No, and don't show plugin installation hints again

─────────────────────────────────────────────────────────────
```

The dialog names the first word of the shell command Claude ran, so users can spot a mismatch. Each answer has one effect:

- **Yes, install**: installs the plugin at [user scope](discover-plugins.md)
- **No, and don’t show plugin installation hints again**: turns off future hint prompts for that user
- **No answer for 30 seconds**: counts as **No**


[​](#next-steps)

Next steps

- [Publish and distribute a plugin](plugins-publish.md): the routes for distributing a plugin, including the official marketplace, which the hint requires
- [Plugin commands reference](plugins-cli-reference.md#plugin-install): the shell command that installs the same plugin outside a session

---

## Plugin commands reference - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/cli-reference
Path: 08-Plugins-Skills/plugins-cli-reference.md

## On this page

- [claude plugin commands](#claude-plugin-commands)
  - [plugin init](#plugin-init)
  - [plugin install](#plugin-install)
  - [JSON result format](#plugin-json-result)
  - [JSON result for marketplace commands](#json-result-for-marketplace-commands)
  - [Accept a displayed install command](#accept-a-displayed-install-command)
  - [plugin uninstall](#plugin-uninstall)
  - [What an uninstall deletes and keeps](#what-an-uninstall-deletes-and-keeps)
  - [plugin enable](#plugin-enable)
  - [plugin disable](#plugin-disable)
  - [plugin update](#plugin-update)
  - [Which scope the command updates](#which-scope-the-command-updates)
  - [Update by bare name](#update-by-bare-name)
  - [Retry an unfinished dependency install](#retry-an-unfinished-dependency-install)
  - [plugin list](#plugin-list)
  - [JSON output](#json-output)
  - [plugin details](#plugin-details)
  - [plugin configure](#plugin-configure)
  - [plugin prune](#plugin-prune)
  - [plugin eval](#plugin-eval)
  - [plugin eval init](#plugin-eval-init)
  - [plugin tag](#plugin-tag)
  - [plugin test](#plugin-test)
  - [plugin validate](#plugin-validate)
  - [Validate a directory](#validate-a-directory)
  - [Output and exit codes](#output-and-exit-codes)
- [claude plugin marketplace commands](#claude-plugin-marketplace-commands)
  - [plugin marketplace add](#plugin-marketplace-add)
  - [plugin marketplace list](#plugin-marketplace-list)
  - [plugin marketplace remove](#plugin-marketplace-remove)
  - [plugin marketplace update](#plugin-marketplace-update)
- [/plugin in a session](#plugin-in-a-session)
- [/reload-plugins](#reload-plugins)
  - [Reload summary](#reload-summary)
  - [Reloads that change MCP tools](#reloads-that-change-mcp-tools)
  - [Sessions without an interactive terminal](#sessions-without-an-interactive-terminal)
- [Flags that load a plugin for one session](#flags-that-load-a-plugin-for-one-session)
- [Next steps](#next-steps)

Reference

# Plugin commands reference

Copy pageCopy page

Reference for the claude plugin shell commands, /plugin and /reload-plugins in a session, and the flags that load a plugin for one session.

Copy pageCopy page

You run plugin commands either as `claude plugin` from your shell or a script, or as `/plugin` and `/reload-plugins` inside a Claude Code session. This reference gives each command’s flags, defaults, output, and exit codes, along with the two flags that load a plugin for one session. The tables below list each subcommand’s commonly used options, not every option. Run `claude plugin --help` in your shell to see which subcommands your version has, and `claude plugin <subcommand> --help` for a subcommand’s full option list.

These cases are covered on other pages:

- **Install and manage steps, and where `/plugin` runs**: see [Install and manage plugins](discover-plugins.md)
- **What a command changes on disk and which scope takes precedence**: see [Plugin loading reference](plugins-loading.md)
- **What an error message means**: see [Troubleshoot plugins](plugins-troubleshooting.md)


[​](#claude-plugin-commands)

claude plugin commands

Run `claude plugin <subcommand>` from your shell or a script, outside a Claude Code session. These subcommands install and manage plugins without opening the [`/plugin`](#plugin-in-a-session) panel. `claude plugins` is an alias for `claude plugin`. Every subcommand shares these exit codes, plugin arguments, and scope values:

- **Exit codes**: `0` on success and `1` on failure. `validate` adds exit `2` for an unexpected error, and `eval` adds the codes listed in [its section](#plugin-eval).
- **Plugin arguments**: a `<plugin>` argument is a plugin `name` or `name@marketplace`. When two marketplaces offer the same name, use the qualified form. `configure` takes only the qualified form.
- **Scopes**: `--scope` takes `user`, `project`, or `local`, and names the settings file the command writes to. `update` also takes `managed`.


[​](#plugin-init)

plugin init

Scaffold a new plugin at `~/.claude/skills/<name>/`. Unless your administrator has [blocked skills-directory plugins](plugins-loading.md#skills-directory-plugins-blocked-by-managed-settings), it loads in your next session as `<name>@skills-dir` with no install step. `new` is an alias for `init`. For the create, test, and edit workflow that begins with this command, see [Create a plugin](plugins-create.md).

```python
claude plugin init <name> [options]
```

`<name>` becomes the directory name under `~/.claude/skills/` and the plugin’s `name` in its manifest. The command has no flag for another location. To scaffold inside a project instead, see [Create a plugin](plugins-create.md).

| Flag                     | Description                                                                                             |
|:-------------------------|:--------------------------------------------------------------------------------------------------------|
| `--description <text>`   | Manifest description                                                                                    |
| `--author <name>`        | Author name. Defaults to `git config user.name`                                                         |
| `--author-email <email>` | Author email. Defaults to `git config user.email`                                                       |
| `--with <components...>` | Also scaffold starter files for `skills`, `agents`, `hooks`, `mcp`, `lsp`, `output-style`, or `channel` |
| `-f, --force`            | Overwrite an existing `.claude-plugin/` at the target                                                   |

Scaffold a plugin with starter skill and hook files:

```python
claude plugin init my-helper --with skills hooks
```

Claude Code validates what it wrote and prints `Created plugin "my-helper" at ~/.claude/skills/my-helper`, followed by the id it loads as and the `claude plugin disable` command that turns it off. Claude Code exits `1` without writing when it can’t scaffold safely, and the message names the reason. These are common reasons:

- An unknown `--with` value
- An existing scaffold at the target without `--force`
- A managed marketplace allowlist without the `skills-dir` source, or a blocklist that names it. The message is [`Plugins from ~/.claude/skills/ are blocked by your organization's managed settings`](plugins-troubleshooting.md#plugins-from-claude-skills-are-blocked-by-your-organizations-managed-s).


[​](#plugin-install)

plugin install

Install a plugin from one of your marketplaces. `i` is an alias for `install`.

```python
claude plugin install <plugin> [options]
```

Most plugins install without a prompt. For a plugin whose marketplace entry [runs a command to install it](plugins-host-marketplace.md) or [sets a `headersHelper` for its download](plugins-host-marketplace.md#how-users-accept-a-headershelper-command), Claude Code first prints the command and asks `Run this command now? [y/N]`.

| Flag                        | Description                                                                                                                                                                                                                                                                                                                                                                                                                      |
|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-s, --scope <scope>`       | Installation scope: `user`, `project`, or `local`. Defaults to `user`                                                                                                                                                                                                                                                                                                                                                            |
| `--config <key=value>`      | Set a [`userConfig`](plugins-reference.md) option the plugin’s manifest declares. Repeat the flag for each option. A key written `<server>.<key>` sets a setting that a [bundled MCP server](plugins-components.md#include-a-packaged-mcpb-server) declares in its own `user_config` instead, for a bundle file shipped inside the plugin. The `<server>.<key>` form requires Claude Code v2.1.285 or later |
| `-y, --yes`                 | Accept the displayed install command without the `Run this command now?` prompt. Ignored when the command runs inside a Claude Code session, such as from the Bash tool or a hook. Requires Claude Code v2.1.229 or later                                                                                                                                                                                                        |
| `--accept-command <sha256>` | Accept the displayed install command whose `sha256` a previous [`--json` run](#plugin-json-result) reported in `shownCommand`, in place of `-y`. Can’t be combined with `-y`. See [Accept a displayed install command](#accept-a-displayed-install-command). Requires Claude Code v2.1.271 or later                                                                                                                              |
| `--json`                    | Print the result as one JSON object on the last line of stdout instead of the human-readable message, for use in scripts. See [JSON result format](#plugin-json-result). Requires Claude Code v2.1.268 or later                                                                                                                                                                                                                  |
| `--marketplace <source>`    | Install `<plugin>`, given by its bare name, from the marketplace at `<source>`, adding the marketplace first if you haven’t added it yet. See [Add a marketplace and install in one command](discover-plugins.md#add-a-marketplace-and-install-in-one-command). Requires Claude Code v2.1.292 or later                                                                                                                      |

Run `claude plugin install --help` in your shell to see every option your version supports. Pass `-y` from your own terminal to accept the displayed command without the prompt. Here’s what happens without a TTY and when Claude runs the command:

- **stdin or stdout isn’t a TTY, and you pass neither `-y` nor `--accept-command`**: the install is refused. The output says the command was only displayed, and the exit code is `1`
- **Claude runs the command through its Bash tool**: `-y` is ignored. Run the command from your own terminal instead

Install a plugin for everyone who clones the project:

```python
claude plugin install formatter@my-marketplace --scope project
```

Claude Code prints `Successfully installed plugin: formatter@my-marketplace (scope: project)`. When nothing new is installed, the output says why:

- **Already installed at that scope**: the output is `Plugin "formatter@my-marketplace" is already installed (scope: project)` and the exit code is `0`
- **You decline a command-source prompt**: the output is `Aborted.` and the exit code is `1`
- **You decline a `headersHelper` prompt, or it can’t be confirmed without a TTY**: the output is `Aborted — the command was not run.` and the exit code is `1`


[​](#plugin-json-result)

JSON result format

When you pass `--json` to `plugin install`, the last line of stdout is one JSON object. Parse only that line, because Claude Code prints any command the marketplace declares ahead of it. Three fields are always present:

- `command`: the subcommand that ran, such as `install`
- `outcome`: `ok` or `failed`
- `message`: a human-readable description of the result

Other fields, such as `pluginId`, `scope`, and `failureCode`, appear only when they apply. The `--json` option on `plugin uninstall`, `plugin update`, `plugin enable`, and `plugin disable` prints the same object with that subcommand’s own fields. A usage error, such as an invalid `--scope`, prints no result line and exits `1` with the reason on stderr.


[​](#json-result-for-marketplace-commands)

JSON result for marketplace commands

On `plugin marketplace add`, `plugin marketplace remove`, and `plugin marketplace update`, `--json` prints one JSON object on the last line of stdout with `command`, `outcome`, and `message` fields. The following is the result of `claude plugin marketplace remove your-marketplace --json`:

```python
{"command":"marketplace-remove","outcome":"ok","marketplace":"your-marketplace","message":"Successfully removed marketplace: your-marketplace"}
```

The `command` value is `marketplace-add`, `marketplace-remove`, or `marketplace-update`. The fields below appear only when they apply:

- `marketplace`: the name of the marketplace the command acted on
- `failureCode`: a code for why the command failed, such as `invalid_source`

`plugin marketplace add` and `plugin marketplace remove` can print no result line when the argument is the [reserved name](plugins-marketplace-reference.md#reserved-names) `anthropic-plugin-directory`, so check the exit code for that name.


[​](#accept-a-displayed-install-command)

Accept a displayed install command

When a `--json` run displays a marketplace-declared command and doesn’t run it, the `failed` result also carries a `shownCommand` object. Its fields include the command as displayed, the plugin it belongs to, and the command’s `sha256`. To accept exactly that command, re-run with that `sha256` as `--accept-command` from your own terminal, because the flag has no effect inside a Claude Code session. Requires Claude Code v2.1.271 or later. The `sha256` counts as acceptance for exactly that command, plugin, and marketplace catalog. If any of them changed since the command was displayed, Claude Code doesn’t accept the `sha256` and shows the command again. A change that the run’s own marketplace refresh fetches also counts as such a change. If `shownCommand.acceptCommandMatched` is `false`, the `sha256` you passed doesn’t match the command now displayed. Review that command before re-running with its `sha256`.


[​](#plugin-uninstall)

plugin uninstall

Remove an installed plugin from one scope. `remove` and `rm` are aliases for `uninstall`.

```python
claude plugin uninstall <plugin> [options]
```

| Flag                  | Description                                                                                                                                                                                                    |
|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-s, --scope <scope>` | Uninstall from scope: `user`, `project`, or `local`. Defaults to `user`                                                                                                                                        |
| `--keep-data`         | Preserve the plugin’s persistent data directory, `~/.claude/plugins/data/<id>/`                                                                                                                                |
| `--prune`             | Also remove auto-installed [dependencies](plugins-dependencies.md) that no remaining plugin needs                                                                                                        |
| `-y, --yes`           | Skip the `--prune` confirmation prompt. Required with `--prune` when stdin or stdout isn’t a TTY                                                                                                               |
| `--json`              | Print the result as one JSON object on the last line of stdout, in the [same format as `plugin install --json`](#plugin-json-result). Can’t be combined with `--prune`. Requires Claude Code v2.1.268 or later |

Uninstall a plugin from project scope:

```python
claude plugin uninstall formatter@my-marketplace --scope project
```

Claude Code prints `Successfully uninstalled plugin: formatter (scope: project)`. When the plugin isn’t installed at that scope, the command prints a line that starts `Failed to uninstall plugin "formatter@my-marketplace":` and exits `1`. If the failure line continues with `"formatter" was not uninstalled:` and names a settings file, Claude Code couldn’t confirm that the scope’s settings no longer switch the plugin on, so the plugin stays installed with everything it saved. With `--json`, the result carries `failureCode: "settings_still_on"`. This settings check requires Claude Code v2.1.282 or later.


[​](#what-an-uninstall-deletes-and-keeps)

What an uninstall deletes and keeps

When you uninstall a plugin from the last scope it’s installed at, Claude Code also deletes the plugin’s stored [options and secrets](plugins-reference.md#user-configuration) and its data directory, `~/.claude/plugins/data/<id>/`. There are three exceptions:

- With `--keep-data`, the data directory stays
- When another installed plugin uses the same folder, such as one whose ID differs from this one only in letter case, the data directory stays
- When Claude Code can’t read the list of installed plugins back after it removes the plugin from that scope, the options, secrets, and data directory all stay, because the plugin may still be installed at another scope. The uninstall still succeeds. The message lists what stayed and how to delete it, and with `--json` the result carries `savedKept: "install_records_unreadable"`

With `--json`, `keptData` reports whether the directory stayed, and `/plugin` shows `· data preserved` when it did. For a directory that stays without `--keep-data`, this reporting requires Claude Code v2.1.281 or later. The `savedKept` field requires Claude Code v2.1.282 or later.


[​](#plugin-enable)

plugin enable

Enable a disabled plugin. For a [plugin synced from claude.ai](plugins-loading.md#synced-plugins), pass `<name>@synced` as the plugin.

```python
claude plugin enable <plugin> [options]
```

| Flag                  | Description                                                                                                                                                                  |
|:----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-s, --scope <scope>` | Scope to enable at: `user`, `project`, or `local`. Auto-detected when omitted                                                                                                |
| `--json`              | Print the result as one JSON object on the last line of stdout, in the [same format as `plugin install --json`](#plugin-json-result). Requires Claude Code v2.1.268 or later |

Without `--scope`, the command checks your settings files in the order local, project, user, and uses the first scope that mentions the plugin. If you pass a `--scope` where the plugin isn’t declared, the command either writes an override or fails:

- **A scope that [takes precedence](plugins-loading.md) over the declaring one**: Claude Code writes an override at the scope you passed. For example, `claude plugin disable formatter --scope local` turns off a project-enabled plugin for you alone
- **Any other scope**: the command fails with `Plugin "formatter" is installed at project scope, not user. Use --scope project or omit --scope to auto-detect.`

If the plugin is already enabled at the resolved scope, the command prints `Plugin "formatter" is already enabled` and exits `1`. With `--json`, the result has `"failureCode": "already_in_goal_state"` and `"alreadyInGoalState": true`, so a script can treat that case as success. When the plugin declares [dependencies](plugins-dependencies.md), Claude Code enables them too. The command fails in these cases:

- **A dependency is not installed**: enable fails and prints the `claude plugin install` command for each missing dependency
- **A dependency is blocked by your organization’s plugin policy**: enable fails and names the blocked dependency
- **A dependency is set to `false` at a scope with higher precedence than the target scope**: enable fails. Enable the dependency at that scope, or pass `--scope` to write there

Re-enable a plugin wherever it’s declared:

```python
claude plugin enable formatter
```

Claude Code prints `Successfully enabled plugin: formatter (scope: project)`, naming the scope it detected.


[​](#plugin-disable)

plugin disable

Disable a plugin without uninstalling it. For a [plugin synced from claude.ai](plugins-loading.md#synced-plugins), pass `<name>@synced` as the plugin.

```python
claude plugin disable [plugin] [options]
```

| Flag                  | Description                                                                                                                                                                  |
|:----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-a, --all`           | Disable every enabled plugin. Can’t be combined with a plugin name or `--scope`                                                                                              |
| `-s, --scope <scope>` | Scope to disable at: `user`, `project`, or `local`. Auto-detected when omitted                                                                                               |
| `--json`              | Print the result as one JSON object on the last line of stdout, in the [same format as `plugin install --json`](#plugin-json-result). Requires Claude Code v2.1.268 or later |

Without `--scope`, the scope is auto-detected in the same local, project, user order as [`plugin enable`](#plugin-enable). If you pass neither a plugin name nor `--all`, Claude Code prints `Please specify a plugin name or use --all to disable all plugins` and exits `1`. Disabling a plugin that is already disabled prints `Plugin "formatter" is already disabled` and exits `1`, as [`plugin enable`](#plugin-enable) does for an already-enabled plugin. The command fails for a plugin that is still required:

- **Another enabled plugin [depends on](plugins-dependencies.md) it**: the command fails and names the dependents to disable first
- **Your organization requires it as a synced plugin**: the command fails and saves nothing

Disable one plugin:

```python
claude plugin disable formatter
```

Claude Code prints `Successfully disabled plugin: formatter (scope: project)`.


[​](#plugin-update)

plugin update

Update a plugin to the latest version its marketplace offers. The new version loads in your next session, or after you run `/reload-plugins` in a running one.

```python
claude plugin update <plugin> [options]
```

| Flag                        | Description                                                                                                                                                                                                                                   |
|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-s, --scope <scope>`       | Scope to update: `user`, `project`, `local`, or `managed`. Auto-detected when omitted                                                                                                                                                         |
| `-y, --yes`                 | Accept a changed install command from a [command-source](plugins-host-marketplace.md) plugin, without the prompt. Required when stdin or stdout isn’t a TTY, unless you pass `--accept-command`. Requires Claude Code v2.1.229 or later |
| `--accept-command <sha256>` | Accept the marketplace-declared command whose `sha256` a previous [`--json` run](#plugin-json-result) reported in `shownCommand`, in place of `-y`. Can’t be combined with `-y`. Requires Claude Code v2.1.271 or later                       |
| `--json`                    | Print the result as one JSON object on the last line of stdout, in the [same format as `plugin install --json`](#plugin-json-result). Requires Claude Code v2.1.268 or later                                                                  |

Update a plugin:

```python
claude plugin update formatter@my-marketplace
```

Claude Code prints `Checking for updates for plugin "formatter@my-marketplace"…`, then the result. When nothing is newer, it prints `formatter is already at the latest version (1.0.0).` and exits `0`, unless it [retries the plugin’s dependency install](#retry-an-unfinished-dependency-install) and that install fails.


[​](#which-scope-the-command-updates)

Which scope the command updates

If you omit `--scope`, the command updates the plugin at the most specific scope it’s installed at for your current project, checking local, project, user, then managed. Before v2.1.281, the command used `user` when you omitted `--scope`, so updating a plugin installed only at project or local scope failed with `Plugin "<name>" is not installed at scope user`. On those versions, pass `--scope`. `managed` is the one scope you can update but not install to. For admin-installed plugins, see [Manage plugins for your organization](plugins-org.md).


[​](#update-by-bare-name)

Update by bare name

You can pass a bare plugin name, which the command matches against your installed plugins. When installed plugins from different marketplaces share the name, the command refuses the update and lists the qualified `plugin-name@marketplace-name` commands to run instead. Updating by bare name requires Claude Code v2.1.246 or later.


[​](#retry-an-unfinished-dependency-install)

Retry an unfinished dependency install

When the plugin is already at its latest version, the command can also retry an unfinished dependency install in its cached copy. For the cases where that retry runs or is skipped, see [The packages it lists are not installed](plugins-troubleshooting.md#the-packages-it-lists-are-not-installed). If the retry fails, the output is `Failed to update plugin "formatter@my-marketplace"` with the reason and the exit code is `1`. Before v2.1.287, the command reported the plugin at its latest version without retrying the install.


[​](#plugin-list)

plugin list

List installed plugins with their version, scope, and status.

```python
claude plugin list [options]
```

| Flag                   | Description                                                                                                                                                                                                                                                                                                                                                                        |
|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--json`               | Print the list as JSON                                                                                                                                                                                                                                                                                                                                                             |
| `--available`          | Also list plugins your marketplaces offer that you haven’t installed. Has no effect without `--json`                                                                                                                                                                                                                                                                               |
| `--data-size [plugin]` | Measure each installed plugin’s [saved data directory](#what-an-uninstall-deletes-and-keeps), or only the named plugin’s, given as `name@marketplace`. Has no effect without `--json`. If the name has no install record, the command prints `--data-size names a plugin that is not installed` and exits `1` instead of printing the list. Requires Claude Code v2.1.285 or later |

Claude Code groups the human-readable output by how each plugin loads:

- **`Installed plugins:`**: plugins you installed from a marketplace
- **`Session-only plugins (--plugin-dir / --plugin-url):`**: plugins loaded by those flags in the same command, as in `claude --plugin-dir ./my-plugin plugin list`
- **`Skills-directory plugins (.claude/skills/*):`**: plugins Claude Code found in a skills directory
- **`Synced from claude.ai`**: [plugins synced from your claude.ai account](plugins-loading.md#synced-plugins)

With nothing in any group, Claude Code prints `` No plugins installed. Use `claude plugin install` to install a plugin. ``


[​](#json-output)

JSON output

With `--json`, Claude Code prints an array with one object per installation. Each object carries the fields below. `id`, `version`, `scope`, `enabled`, and `installPath` are always present, and the others appear only when they apply.

| Field               | Type             | Description                                                                                                                                                                                                                                                                                          |
|:--------------------|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id`                | string           | `name@marketplace` for installs, `name@inline` for session-only plugins, `name@skills-dir` for skills-directory plugins, `name@synced` for plugins synced from claude.ai                                                                                                                             |
| `version`           | string           | For a marketplace install, the [version Claude Code computed](plugins-loading.md#versions-and-updates) at install. For a session-only, skills-directory, or synced plugin, the manifest’s `version`, or `unknown` when it declares none                                                        |
| `scope`             | string           | `user`, `project`, `local`, or `managed` for installs; `user` or `project` for skills-directory plugins; `session` for session-only plugins; `synced` for plugins synced from claude.ai                                                                                                              |
| `enabled`           | boolean          | Whether the plugin is enabled in your merged settings                                                                                                                                                                                                                                                |
| `installPath`       | string           | Directory the plugin loads from, except for a plugin that sessions [load in place](plugins-loading.md#in-place-and-copied-plugins) from its marketplace’s folder                                                                                                                               |
| `readFromFolder`    | string           | For a plugin that sessions [load in place](plugins-loading.md#in-place-and-copied-plugins) from its marketplace’s folder, the plugin’s source directory inside that folder. Requires Claude Code v2.1.289 or later                                                                             |
| `folderVersion`     | string           | With `readFromFolder`, the plugin’s `version` as Claude Code loaded it from that folder, which can differ from the `version` field above. Absent when the plugin didn’t load or declares no version. Requires Claude Code v2.1.289 or later                                                          |
| `installedAt`       | string           | ISO timestamp of the install. Marketplace installs only                                                                                                                                                                                                                                              |
| `lastUpdated`       | string           | ISO timestamp of the last update. Marketplace installs only                                                                                                                                                                                                                                          |
| `projectPath`       | string           | Project the install belongs to. `project` and `local` scope only                                                                                                                                                                                                                                     |
| `mcpServers`        | object           | The plugin’s MCP server definitions, when a marketplace-installed plugin has any                                                                                                                                                                                                                     |
| `errors`            | array of strings | Load errors, when the plugin failed to load                                                                                                                                                                                                                                                          |
| `notes`             | array of strings | Warnings that aren’t load errors, such as authoring issues or [packages that aren’t installed](plugins-loading.md#when-the-dependency-install-fails-or-is-skipped)                                                                                                                             |
| `errorDetails`      | array of objects | One object per `errors` entry, giving its diagnostic `type` and the names it refers to, such as the plugin, marketplace, server, or file. Requires Claude Code v2.1.268 or later                                                                                                                     |
| `noteDetails`       | array of objects | The same detail objects for each `notes` entry. Requires Claude Code v2.1.268 or later                                                                                                                                                                                                               |
| `hasUserConfig`     | boolean          | Present and `true` when the plugin loaded and its manifest declares [`userConfig` options](plugins-reference.md#user-configuration). Absent for a plugin that failed to load, whatever its manifest declares. Saved values are never included. Requires Claude Code v2.1.285 or later |
| `projectEnabled`    | boolean          | Whether the project’s shared `.claude/settings.json` turns the plugin on. Marketplace installs only. Requires Claude Code v2.1.285 or later                                                                                                                                                          |
| `dataDirSize`       | object           | With `--data-size`, the size of the plugin’s [saved data directory](#what-an-uninstall-deletes-and-keeps) as `bytes` and `human`; absent when the directory is missing or empty. Marketplace installs only. Requires Claude Code v2.1.285 or later                                                   |
| `dataDirUnreadable` | boolean          | With `--data-size`, `true` when the saved data directory exists but couldn’t be measured. Marketplace installs only. Requires Claude Code v2.1.285 or later                                                                                                                                          |

With `--json --available`, Claude Code prints one object instead of an array. Its `installed` field holds the array of installed-plugin objects, and its `available` field holds one object per uninstalled marketplace plugin with the fields below.

| Field             | Type             | Description                                                                                                                 |
|:------------------|:-----------------|:----------------------------------------------------------------------------------------------------------------------------|
| `pluginId`        | string           | `name@marketplace`                                                                                                          |
| `name`            | string           | The plugin’s name in the marketplace                                                                                        |
| `marketplaceName` | string           | The marketplace that offers it                                                                                              |
| `source`          | string or object | The marketplace entry’s [source](plugins-marketplace-reference.md): a string for a relative path, an object otherwise |
| `description`     | string           | The entry’s description, when it has one                                                                                    |
| `version`         | string           | The entry’s version, when it declares one                                                                                   |
| `installCount`    | number           | Install count, when Claude Code has one for the plugin                                                                      |


[​](#plugin-details)

plugin details

Show a plugin’s component inventory and its projected token cost. The plugin must be loaded: installed, found in a skills directory, or passed with `--plugin-dir` or `--plugin-url` in the same command. The `<name>` is a plugin `name` or `name@marketplace`.

```python
claude plugin details <name>
```

The command takes no flags beyond `--help`. Show what an installed plugin contributes:

```python
claude plugin details formatter
```

Claude Code prints the plugin’s name, version, description, and source, then these sections:

- **`Component inventory`**: the plugin’s skills, agents, hooks, MCP servers, and LSP servers
- **`Projected token cost`**: the always-on tokens the plugin adds to every session
- **`Per-component (rounded)`**: always-on and on-invoke estimates for each skill, agent, and command. Omitted when the plugin has none

For what the two cost figures mean, see [Measure plugin cost and usage](plugins-measure.md). For a plugin that isn’t loaded, Claude Code prints this message and exits `1`:

```python
Plugin "formatter" not found. Run `claude plugin list` to see installed plugins, or pass --plugin-dir <path> to load one from disk.
```

An ID that ends in `@builtin` names one of the [plugins built into Claude Code](plugins-mods-overview.md#mods-built-into-claude-code). If you pass such an ID and Claude Code doesn’t find that plugin, the message reads instead:

```python
Plugin "<name>@builtin" not found. Built-in plugins don't appear in `claude plugin list`. To see the ones available in a session, run /plugin in Claude Code and open the Installed tab.
```


[​](#plugin-configure)

plugin configure

Show an installed plugin’s [`userConfig`](plugins-reference.md#user-configuration) options and which are set, or save values piped in on stdin. Requires Claude Code v2.1.285 or later.

```python
claude plugin configure <plugin>
```

| Flag             | Description                                                                                                                                                                                                                                                                                                                           |
|:-----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--values-stdin` | Read option values from stdin as a JSON object of single-line strings and save them. Options you leave out keep their saved values                                                                                                                                                                                                    |
| `--json`         | Print the result as one JSON object on stdout. Without `--values-stdin`, the object carries the options’ `schema` and `choices`, their starting `inputs`, and the `configured` and `unconfigured` option names. With `--values-stdin`, it carries the `saved` option names and, when they could be read back, the `unconfigured` ones |

Without flags, the command lists each option with up to three labels: `required` or `optional`, then `sensitive` for an option the manifest declares sensitive, then `set` or `not set`. It prints no saved values. With `--json`, the output includes the saved values of options that aren’t sensitive, and never the text of a sensitive one. To save values, write them to a file as a JSON object that maps option keys to string values, then pass the file on stdin. Replace `formatter@my-marketplace` with your own plugin’s id as `claude plugin list` shows it. This example sets one option named `api_url` from a file `values.json` that contains `{"api_url": "https://example.com"}`:

```python
claude plugin configure formatter@my-marketplace --values-stdin < values.json
```

Claude Code validates each value against the option’s declared type and prints `Configuration saved. Restart Claude Code to apply it.` If you pass a key the manifest doesn’t declare, or a value that fails validation, the command saves nothing, prints `Failed to save configuration:` with the reason, and exits `1`. With `--json`, a refused value also prints an object on stdout whose `refused` field carries the `message` and, when one option is at fault, its `option` key. Pass the plugin’s full `name@marketplace` id, as `claude plugin list` shows it. `configure` doesn’t accept a bare `name`. When no loaded plugin has that id, the command prints `No installed plugin has the id "<plugin>".` and exits `1`. For the settings of a bundled MCP server, see [`plugin install --config`](#plugin-install) or the **Configure** item in `/plugin`.


[​](#plugin-prune)

plugin prune

Remove auto-installed [dependencies](plugins-dependencies.md) that no installed plugin needs anymore. The command never removes a plugin you installed yourself. `autoremove` is an alias for `prune`.

```python
claude plugin prune [options]
```

| Flag                  | Description                                                             |
|:----------------------|:------------------------------------------------------------------------|
| `-s, --scope <scope>` | Prune at scope: `user`, `project`, or `local`. Defaults to `user`       |
| `--dry-run`           | List what would be removed without removing it                          |
| `-y, --yes`           | Skip the confirmation prompt. Required when stdin or stdout isn’t a TTY |

Preview what a prune would remove:

```python
claude plugin prune --dry-run
```

Claude Code lists the orphaned dependencies and ends with `(dry run — nothing removed)`. With none to remove, it prints a line that starts `Nothing to prune`. Without `--dry-run`, the command removes the orphaned dependencies only after you confirm at the prompt or pass `-y`. The exit code is `0` whatever you answer at the prompt. What `prune` does depends on whether a terminal is attached and whether you pass `-y`:

| Terminal and flags               | What happens                                                                                    |
|:---------------------------------|:------------------------------------------------------------------------------------------------|
| Interactive terminal, no `-y`    | Lists the orphaned dependencies and asks `Remove? [y/N]`                                        |
| Any terminal, `-y`               | Removes them and prints `Removed N auto-installed plugins: <names>`                             |
| Non-TTY stdin or stdout, no `-y` | Prints the list and `` Not a TTY — run `claude plugin prune -y` to remove. ``, removing nothing |


[​](#plugin-eval)

plugin eval

Run a plugin’s [eval cases](plugin-evals.md) and report scored results. Requires Claude Code v2.1.269 or later. Each case is a prompt plus graders. Claude Code runs it several times in an isolated session with only the target plugin loaded, and by default also without the plugin so the report shows the difference. See [Test plugins with evals](plugin-evals.md) for the case format, graders, results, and CI usage.

```python
claude plugin eval [target] [options]
```

The optional `target` defaults to the current directory and takes any of these forms:

- A plugin directory
- A single `prompt.md` or `case.yaml` file
- An installed plugin as `name` or `name@marketplace`
- `name@skills-dir`

Put the target before `--tag`, `--allow-tools`, and `--json`. Each of these options takes the words that follow it as its value, so a target written after one of them is read as a tag, a tool name, or the JSON output path instead of as the target. This table lists the options most runs use. Run `claude plugin eval --help` for the complete set, including `--case`, `--tag`, `--output-dir`, `--report`, `--allow-real-servers`, `--keep-temp`, and `--verbose`.

| Option                     | Description                                                                                                                                                          | Default                                                                        |
|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|
| `--runs <n>`               | Runs per case in each [arm](plugin-evals.md#compare-against-a-no-plugin-baseline)                                                                              | Each case’s `runs`, else 3                                                     |
| `-j, --concurrency <n>`    | Agent sessions to run at once, 1 to 8. They share your rate limit                                                                                                    | `1`                                                                            |
| `--model <model>`          | Model for the agent under test                                                                                                                                       | Each case’s `model`, else `ANTHROPIC_MODEL` if set, else Claude Code’s default |
| `--judge-model <model>`    | Model for `llm` and `baseline` graders                                                                                                                               | The model for [background tasks](plugin-evals.md#grade-the-result)       |
| `--ablation <mode>`        | `none` or `with-without`. See [Score against the no-plugin baseline](plugin-evals.md#compare-against-a-no-plugin-baseline)                                     | Decided per case, as that section describes                                    |
| `--threshold <0..1>`       | Exit 1 if any case scores below this                                                                                                                                 | `1.0`                                                                          |
| `--max-cost-usd <usd>`     | Stop before the next run once spend reaches this, exit 2, and report partial results                                                                                 | No limit                                                                       |
| `--allow-tools <tools...>` | Grant tools beyond the read-only set, such as `Bash`, `Write`, `Edit`, or `"mcp__plugin_<plugin>_<server>__*"`. See [Grant tools](plugin-evals.md#grant-tools) |                                                                                |
| `--scaffold`               | Run each case’s [`scaffold_script`](plugin-evals.md#add-setup-or-history-with-case-yaml)                                                                       | Off                                                                            |
| `--trust-plugin`           | Skip the first-run trust prompt, for CI. See [What a run can access](plugin-evals.md#security)                                                                 | Off                                                                            |
| `--mocks <mode>`           | `record` or `off`. See [Mock MCP servers](plugin-evals.md#mock-mcp-servers)                                                                                    | `record`                                                                       |
| `--eval-dir <dir>`         | Directory below the plugin that holds the cases                                                                                                                      | The manifest’s `experimental.evals`, else `evals`                              |
| `--json [path]`            | Print the [result document](plugin-evals.md#json-result) to stdout, or write it to a `.json` path                                                              |                                                                                |
| `--no-publish`             | Keep the HTML report local                                                                                                                                           |                                                                                |

The exit code reports how the run ended. To act on it in a pipeline, see [Run evals in CI](plugin-evals.md#run-evals-in-ci).

| Exit code | Meaning                                                                                                                                                                                                                                   |
|:----------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `0`       | The suite passed: every case meets the threshold and nothing else failed the run                                                                                                                                                          |
| `1`       | A failing case, a load error, an untrusted plugin directory, a run that [ended on an API error](plugin-evals.md#runs-ended-on-an-api-error), or another cause listed under [Run evals in CI](plugin-evals.md#run-evals-in-ci) |
| `2`       | A partial run                                                                                                                                                                                                                             |
| `130`     | Interrupted                                                                                                                                                                                                                               |
| `143`     | Terminated                                                                                                                                                                                                                                |


[​](#plugin-eval-init)

plugin eval init

Create an eval suite for the plugin in the current directory. Requires Claude Code v2.1.269 or later. See [Create your first eval suite](plugin-evals.md#create-your-first-eval-suite).

```python
claude plugin eval init [name] [options]
```

Run the command from the plugin’s root folder, the directory that holds `.claude-plugin/plugin.json` or the skill’s `SKILL.md`. To scaffold the suite in another directory on purpose, pass `--eval-dir`. In a terminal, the command opens an interactive Claude Code session for an authoring interview. In the interview, Claude does the following:

1.  Reads the plugin
2.  Asks you what it should do well
3.  Proposes cases and graders
4.  Writes the case files
5.  Runs the cases and reviews the grades with you to check that the graders score the way you would

With `--bare`, or without a terminal, the command writes a blank single-case template instead. When Claude runs the command from inside a Claude Code session, the command prints the interview instructions for that session to follow rather than writing a template. The optional `name` is a case name. It’s required with `--bare` or without a terminal, because the command writes the blank template for that case. A case name starts with a letter or digit and contains only letters, digits, `.`, `_`, and `-`. On every platform, the command also refuses names that Windows can’t store, such as `con` or a name ending in `.`. The command accepts these options:

| Option              | Description                                                                                       | Default                                           |
|:--------------------|:--------------------------------------------------------------------------------------------------|:--------------------------------------------------|
| `--bare`            | Write a blank `prompt.md` and `graders/criteria.md` for `<name>` instead of running the interview |                                                   |
| `-i, --interactive` | Require the interview. Fails without a terminal instead of writing a template                     |                                                   |
| `--eval-dir <dir>`  | Directory below the current directory to write cases into                                         | The manifest’s `experimental.evals`, else `evals` |


[​](#plugin-tag)

plugin tag

Create an annotated git tag named `<name>--v<version>` for a plugin release. Before tagging, the command checks that the plugin’s `plugin.json` and any marketplace entry that lists it agree on the version. For when to tag a release, see [Publish a plugin](plugins-publish.md).

```python
claude plugin tag [path] [options]
```

The `[path]` is the plugin directory, defaulting to the current directory. The command finds the marketplace entry by walking up from that directory to a `.claude-plugin/marketplace.json` that lists the plugin.

| Flag                  | Description                                                                         |
|:----------------------|:------------------------------------------------------------------------------------|
| `--push`              | Push the tag to `--remote` after creating it                                        |
| `--dry-run`           | Print what would be tagged without creating the tag                                 |
| `-f, --force`         | Skip the dirty-working-tree and tag-already-exists checks                           |
| `-m, --message <msg>` | Tag annotation message. `%s` stands for the version. Defaults to `<name> <version>` |
| `--remote <name>`     | Remote to push to with `--push`. Defaults to `origin`                               |

Preview the tag for a plugin in a marketplace checkout:

```python
claude plugin tag plugins/formatter --dry-run
```

Claude Code prints the plan:

- The plugin name
- The version and which file it came from
- The matching marketplace entry, when there is one
- The tag name
- The `git tag` and `git push` commands it would run

Without `--dry-run`, Claude Code prints `Created tag formatter--v1.0.0` and either `Pushed to origin` or the push command to run yourself. If the push fails, the tag is still created locally and the command exits with an error. The command exits `1` and prints the reason when it can’t tag safely. Common reasons are:

- No `version` in `plugin.json` or the marketplace entry
- The tag already exists
- The working tree is dirty


[​](#plugin-test)

plugin test

Run the tests for a [mod](plugins-mods-overview.md), a plugin whose code registers event handlers. The command needs no session, sign-in, or network. For how to write a test, see [Test a mod](plugins-mods-test.md).

```python
claude plugin test [directory]
```

The `[directory]` is the mod’s directory, defaulting to the current directory. The command runs every file under it whose name ends in `.test.ts` or `.test.tsx`, and exits with status 1 when a test fails. Run the tests for a mod in `./first-mod`:

```python
claude plugin test ./first-mod
```


[​](#plugin-validate)

plugin validate

Validate a plugin manifest, a marketplace manifest, or the skills, agents, and commands in a directory, and exit with a code a CI job can act on. For the create, test, and edit workflow, see [Create a plugin](plugins-create.md). For what the validator checks in each manifest, see the [plugin manifest reference](plugins-reference.md) and the [marketplace reference](plugins-marketplace-reference.md).

```python
claude plugin validate <path> [options]
```

| Flag       | Description                                                                                                      |
|:-----------|:-----------------------------------------------------------------------------------------------------------------|
| `--strict` | Treat warnings as errors, so unrecognized fields and missing metadata that the runtime tolerates fail the run    |
| `--json`   | Output the validation report as one JSON object with the same exit codes. Requires Claude Code v2.1.259 or later |

Validate a plugin before committing it:

```python
claude plugin validate ./my-plugin --strict
```


[​](#validate-a-directory)

Validate a directory

The `<path>` is a manifest file or a directory. Given a directory, Claude Code picks what to validate by what it finds there:

- `.claude-plugin/marketplace.json`, when it exists
- Otherwise `.claude-plugin/plugin.json`
- Otherwise the component files, chosen by the directory’s name. Validating component files without a manifest requires Claude Code v2.1.233 or later:
  - A directory named `skills`, `agents`, or `commands`: the files inside it
  - A directory named `.claude`: the `skills`, `agents`, and `commands` directories inside it
  - Any other directory: those three directories under its `.claude`

When the directory holds both a `.claude-plugin/marketplace.json` and a `.claude-plugin/plugin.json`, Claude Code validates the marketplace and also the plugin’s manifest and component files. This requires Claude Code v2.1.289 or later. Claude Code doesn’t follow symlinks inside the directory you name. What it does depends on where the link is:

- **A linked `skills`, `agents`, or `commands` directory under the plugin or `.claude` root**: Claude Code warns that nothing in it was read.
- **A linked entry inside a `skills`, `agents`, or `commands` directory**: Claude Code skips it and warns, per directory, how many entries it skipped that a session would load.
- **The `skills`, `agents`, or `commands` directory you name is itself a symlink, or its parent `.claude` directory is**: Claude Code reports an error and checks nothing in it. Name the real directory instead.

A few files are not read by a validation run:

- **A `SKILL.md` at the plugin root**: when you run `claude plugin validate` against a plugin directory, Claude Code doesn’t check a `SKILL.md` at the plugin root
- **A `CLAUDE.md` at the plugin root**: in a plugin run, Claude Code also warns about a `CLAUDE.md` at the plugin root
- **Plugin files in a marketplace run**: from a marketplace directory, Claude Code doesn’t open the skill, agent, command, or hook files of plugins the marketplace lists in other directories, or the MCP server files they bundle. To find errors in those files, validate each plugin directory


[​](#output-and-exit-codes)

Output and exit codes

Claude Code prints the file it validated, any errors and warnings with their paths, and a verdict line. The exit code follows the verdict:

| Exit code | Verdict line                                                                    | Meaning                                                    |
|:----------|:--------------------------------------------------------------------------------|:-----------------------------------------------------------|
| `0`       | `Validation passed` or `Validation passed with warnings`                        | The manifest loads. With `--strict`, no warnings either    |
| `1`       | `Validation failed` or `Validation failed (--strict treats warnings as errors)` | An error, or a warning under `--strict`                    |
| `2`       | `Unexpected error during validation: <reason>`                                  | The validator itself failed, such as on an unreadable path |

With `--json`, Claude Code writes the report to stdout as one JSON object with these top-level fields:

- `success`: the same verdict the exit code gives
- `strict`: whether the run treated warnings as errors
- `target`: the resolved path Claude Code validated
- `manifest`: the manifest’s own result, or `null` for a run without a manifest
- `contents`: per-file results, each naming its `file` and carrying `errors`, `warnings`, and `notes` arrays
  - `gatingHooks`: whether each [mod](plugins-mods-overview.md) hook that can refuse an action, such as a `tool.call` hook, has a [`.catch` handler](plugins-mods-events.md#handle-a-hook-that-fails). Each item gives `module`, `pattern`, `hook`, and `hasCatch`. Requires Claude Code v2.1.290 or later

On exit `2`, the command writes nothing to stdout. The error message goes to stderr.


[​](#claude-plugin-marketplace-commands)

claude plugin marketplace commands

Run `claude plugin marketplace <subcommand>` from your shell to add, list, refresh, and remove the marketplaces you install plugins from.

- **Exit codes**: these subcommands follow the [exit-code convention](#claude-plugin-commands) of the plugin commands
- **Scopes**: their `--scope` flag has no `-s` short form

For what a marketplace is and how Claude Code caches it, see [Plugin loading reference](plugins-loading.md).


[​](#plugin-marketplace-add)

plugin marketplace add

Add a marketplace from a GitHub repository, a git URL, a hosted `marketplace.json`, or a local path, and declare it in a settings file. After you add it, Claude Code installs any [dependencies](plugins-dependencies.md) that your installed plugins were missing.

```python
claude plugin marketplace add <source> [options]
```

| Flag                  | Description                                                                                                                                                                                                                    |
|:----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--scope <scope>`     | Settings file to declare the marketplace in: `user`, `project`, or `local`. Defaults to `user`                                                                                                                                 |
| `--sparse <paths...>` | Limit the git checkout to these directories, for monorepos. `github` and `git` sources only                                                                                                                                    |
| `--claudeai`          | Read the argument as the name of a [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai) instead of a source. Requires Claude Code v2.1.273 or later                                                  |
| `--json`              | Print whether the command succeeded, and its message, as one JSON object on the last line of stdout, in the [JSON result format](#plugin-json-result). Has no effect with `--claudeai`. Requires Claude Code v2.1.287 or later |

`<source>` takes any of the forms in the table below, and its form decides the source type and how Claude Code fetches the marketplace. For the resulting source object, see the [marketplace reference](plugins-marketplace-reference.md).

| You type                                                                                                              | Source type | How Claude Code fetches it                                                                               |
|:----------------------------------------------------------------------------------------------------------------------|:------------|:---------------------------------------------------------------------------------------------------------|
| `owner/repo`, `owner/repo#ref`, or `owner/repo@ref`                                                                   | `github`    | Clones the GitHub repository, pinned to `ref` when given. Owner and repo must follow GitHub naming rules |
| `user@host:path[.git][#ref]`                                                                                          | `git`       | Clones over SSH                                                                                          |
| An `http://` or `https://` URL that ends in `.git[#ref]` or contains `/_git/`, such as `https://example.com/repo.git` | `git`       | Clones the URL, including Azure DevOps URLs                                                              |
| `https://github.com/owner/repo` or `https://gitlab.com/namespace/project`, or the same over `http://`                 | `git`       | Clones the URL after appending `.git`                                                                    |
| Any other `http://` or `https://` URL, including a self-hosted git host without `.git`                                | `url`       | Fetches the URL as a `marketplace.json`. To clone a repository there instead, append `.git`              |
| `./path`, `../path`, `/path`, or `~/path` to a directory                                                              | `directory` | Reads the directory in place. On Windows, `.\`, `..\`, and `C:\` forms also work                         |
| The same path forms, to a `.json` file                                                                                | `file`      | Reads the file in place                                                                                  |

For a host whose clone URLs don’t carry the `.git` suffix, such as AWS CodeCommit, add the marketplace as a git entry in [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) instead. Claude Code clones a git entry whether or not its URL ends in `.git`. Claude Code also clones a `gitlab.com` URL with nested subgroups, such as `https://gitlab.com/group/subgroup/project`. Add a marketplace and share it with the project:

```python
claude plugin marketplace add your-org/your-marketplace --scope project
```

Claude Code prints `Successfully added marketplace: your-marketplace (declared in project settings)`, using the `name` from the marketplace’s own manifest. A repeat add or an invalid source prints one of these results instead:

- **Marketplace already on disk**: the output is `Marketplace 'your-marketplace' already on disk — declared in project settings` and the exit code is `0`
- **Unrecognized source**: the output is `Invalid marketplace source format. Try: owner/repo, https://..., or ./path` and the exit code is `1`
- **Bare host such as `gitlab.example.com/team/plugins`**: the add fails as an invalid `owner/repo` shorthand, and the message tells you to add `https://` or use a local path

Add a [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai) by the name printed in the `From claude.ai:` section of `claude plugin marketplace list`:

```python
claude plugin marketplace add --claudeai claudeai-organization-library
```

With `--claudeai`, the command refuses `--scope` and `--sparse`. The marketplace is hosted for your account, not declared in a settings file, so you can’t share it through a project’s `.claude/settings.json`.


[​](#plugin-marketplace-list)

plugin marketplace list

List every marketplace you’ve added, with its source.

```python
claude plugin marketplace list [options]
```

| Flag     | Description            |
|:---------|:-----------------------|
| `--json` | Print the list as JSON |

Claude Code prints `Configured marketplaces:` and one `Source:` line per marketplace, or `No marketplaces configured`. With `--json`, Claude Code prints an array with one object per marketplace, carrying the fields below. Every field is a string.

| Field             | Description                                                            |
|:------------------|:-----------------------------------------------------------------------|
| `name`            | The marketplace’s name                                                 |
| `source`          | `github`, `git`, `url`, `directory`, `file`, or `claudeai`             |
| `repo`            | `owner/repo`. `github` sources only                                    |
| `url`             | The clone or fetch URL. `git` and `url` sources only                   |
| `path`            | The local path. `directory` and `file` sources only                    |
| `ref`             | The pinned branch or tag. `github` and `git` sources, only when pinned |
| `installLocation` | Where Claude Code cached the marketplace                               |

An added [claude.ai marketplace](discover-plugins.md#add-from-claude-ai) has no local clone, so its entry carries its claude.ai identifiers, `marketplaceId` and `organizationUuid`, in place of `installLocation`. It also carries `scope` when one is recorded, and `status`. If your terminal sessions [sync plugins from your claude.ai account](plugins-loading.md#synced-plugins), the text listing ends with a `From claude.ai:` section. That section names the marketplaces claude.ai lists for your account that you haven’t added, both git-based and hosted. It requires Claude Code v2.1.273 or later. To add a marketplace from that section, see [Add a marketplace from claude.ai](discover-plugins.md#add-from-claude-ai). The `--json` output covers configured marketplaces only and leaves the section out.


[​](#plugin-marketplace-remove)

plugin marketplace remove

Remove a marketplace’s declaration from your settings. `rm` is an alias for `remove`.

When you remove a marketplace from the last scope that declares it, Claude Code also deletes its cache and uninstalls every plugin you installed from it. It also deletes their saved [options and secrets](plugins-reference.md#user-configuration) and [data](plugins-components.md#path-variables-and-persistent-data) where it can.To refresh a marketplace without losing its plugins, run `plugin marketplace update` instead.

```python
claude plugin marketplace remove <name> [options]
```

The `<name>` is the marketplace name that `plugin marketplace list` shows, not the source you passed to `add`.

| Flag              | Description                                                                                                                                                                                   |
|:------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--scope <scope>` | Remove the declaration from one settings scope: `user`, `project`, or `local`. Without it, Claude Code removes the declaration from every scope                                               |
| `--json`          | Print whether the command succeeded, and its message, as one JSON object on the last line of stdout, in the [JSON result format](#plugin-json-result). Requires Claude Code v2.1.287 or later |

Remove a marketplace from every scope:

```python
claude plugin marketplace remove your-marketplace
```

Claude Code prints `Successfully removed marketplace: your-marketplace`. When the command uninstalls plugins, the output lists them under a line such as `Also uninstalled 2 plugins from this marketplace:`. To use one of them again, add the marketplace back and reinstall the plugin. If you scope to a settings file that doesn’t declare the marketplace, the command fails with `Marketplace 'your-marketplace' is not declared in project settings. Omit --scope to remove it from all scopes.`


[​](#plugin-marketplace-update)

plugin marketplace update

Refresh one marketplace, or every marketplace, from its source to fetch new plugins and versions. A marketplace added with a branch or tag `ref` updates to the latest commit of that ref, not the repository’s default branch.

```python
claude plugin marketplace update [name] [options]
```

| Flag     | Description                                                                                                                                                                                                                                               |
|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--json` | Print whether the command succeeded, and its message, as one JSON object on the last line of stdout, in the [JSON result format](#plugin-json-result). Without a name, the command refuses `--json` and exits `1`. Requires Claude Code v2.1.287 or later |

Refresh one marketplace:

```python
claude plugin marketplace update your-marketplace
```

Claude Code prints `Successfully updated marketplace: your-marketplace`. When you omit the name, it prints a count such as `Successfully updated 2 marketplaces`.


[​](#plugin-in-a-session)

/plugin in a session

Inside an interactive session, `/plugin` opens the plugin panel. Each subcommand opens the panel on a tab, runs an action there, or prints a result inline. `/plugins` and `/marketplace` are aliases for `/plugin`. You can run these commands only in an interactive terminal session. In a non-interactive run such as `claude -p`, Claude Code replies that `/plugin` isn’t available in this environment. For which surfaces have `/plugin`, how to install without it, and what each panel tab shows, see [Install and manage plugins](discover-plugins.md). A `<plugin>` is a plugin `name` or `name@marketplace`. The table below lists every session form. The shell subcommands `init`, `update`, `details`, `prune`, `eval`, `eval init`, and `test` have no session form.

| Command                                             | Aliases                                        | What it does                                                                                                                                                                                                                                                                                                                                                  |
|:----------------------------------------------------|:-----------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `/plugin`                                           |                                                | Opens the panel on the **Discover** tab. Any unrecognized first word after `/plugin` does the same                                                                                                                                                                                                                                                            |
| `/plugin help`                                      | `/plugin --help`, `/plugin -h`                 | Shows the usage list of `/plugin` subcommands                                                                                                                                                                                                                                                                                                                 |
| `/plugin list [--enabled|--disabled]`               | `ls`                                           | Prints your marketplace-installed plugins inline, with version, scope, and status. A filter flag shows only that state. A plugin whose enable state hasn’t been applied yet is marked `— run /reload-plugins to apply`                                                                                                                                        |
| `/plugin install`                                   | `i`                                            | Opens the **Discover** tab                                                                                                                                                                                                                                                                                                                                    |
| `/plugin install <plugin>`                          | `i`                                            | Opens the plugin’s details in the **Discover** tab. With `name@marketplace`, opens them in that marketplace’s list                                                                                                                                                                                                                                            |
| `/plugin install <source>`                          | `i`                                            | Reports a [marketplace not found](plugins-troubleshooting.md#marketplace-not-found) error and installs nothing when the target is a path, URL, or `owner/repo`, even a source you’ve already added. To install from a source, see [Add a marketplace and install in one command](discover-plugins.md#add-a-marketplace-and-install-in-one-command) |
| `/plugin install <plugin> --marketplace <source>`   | `i`                                            | Adds the marketplace at `<source>` when you haven’t added it yet, asking you to confirm first, then opens the plugin’s details. See [Add a marketplace and install in one command](discover-plugins.md#add-a-marketplace-and-install-in-one-command). Requires Claude Code v2.1.275 or later                                                             |
| `/plugin manage`                                    |                                                | Opens the **Installed** tab                                                                                                                                                                                                                                                                                                                                   |
| `/plugin stats`                                     |                                                | Opens the **Stats** tab, in sessions where [`/skill-doctor`](skills.md#find-unused-skills) is available. Anywhere else it opens the panel on the **Discover** tab                                                                                                                                                                                       |
| `/plugin enable <plugin>`                           |                                                | Opens the **Installed** tab at the plugin and enables it                                                                                                                                                                                                                                                                                                      |
| `/plugin disable <plugin>`                          |                                                | Opens the **Installed** tab at the plugin and disables it                                                                                                                                                                                                                                                                                                     |
| `/plugin uninstall <plugin>`                        |                                                | Opens the **Installed** tab at the plugin and uninstalls it                                                                                                                                                                                                                                                                                                   |
| `/plugin configure <plugin>`                        | `config`                                       | Opens the plugin’s [`userConfig`](plugins-reference.md) dialog, or reports that the plugin declares none                                                                                                                                                                                                                                       |
| `/plugin validate <path>`                           |                                                | Prints the same report as `claude plugin validate`, inline                                                                                                                                                                                                                                                                                                    |
| `/plugin tag [path] [--push] [--dry-run] [--force]` |                                                | Creates the release tag as `claude plugin tag` does. Accepts `--push`, `--dry-run`, and `--force` or `-f`; with any other flag or an extra argument, Claude Code prints usage instead                                                                                                                                                                         |
| `/plugin marketplace`                               | `market`                                       | Does nothing visible. Pass `add`, `list`, `update`, or `remove`                                                                                                                                                                                                                                                                                               |
| `/plugin marketplace add [source]`                  | `market add`                                   | With a source, adds it and reports the result. Without one, opens the **Add marketplace** input                                                                                                                                                                                                                                                               |
| `/plugin marketplace list`                          | `market list`                                  | Prints your marketplace names inline                                                                                                                                                                                                                                                                                                                          |
| `/plugin marketplace update [name]`                 | `market update`                                | Opens the **Marketplaces** tab. With a name, refreshes that marketplace there                                                                                                                                                                                                                                                                                 |
| `/plugin marketplace remove [name]`                 | `market remove`, `market rm`, `marketplace rm` | Opens the **Marketplaces** tab. With a name, removes that marketplace there                                                                                                                                                                                                                                                                                   |

If you name a plugin that isn’t installed in the current project in `/plugin enable`, `disable`, `uninstall`, or `configure`, Claude Code prints `Plugin "<plugin>" is not installed in this project` instead of acting.


[​](#reload-plugins)

/reload-plugins

Apply pending plugin changes to the running session without restarting it. Pending changes are plugins you installed, updated, enabled, disabled, or edited on disk since the session started. When you close the `/plugin` panel with pending changes you made in it, Claude Code runs `/reload-plugins` for you. Run it yourself after plugin changes that happen outside the panel, such as a `claude plugin` command you ran in another terminal.

```python
/reload-plugins [--force]
```

| Flag      | Description                                                                                       |
|:----------|:--------------------------------------------------------------------------------------------------|
| `--force` | Apply the reload even when it would invalidate the prompt cache. `force` without dashes works too |


[​](#reload-summary)

Reload summary

Claude Code reloads every active plugin and prints one summary line, `Reloaded: N plugins · N skills · N agents · N hooks · N plugin MCP servers · N plugin LSP servers`, omitting the plugin MCP server count in a session without an interactive terminal. When any plugin failed, the summary adds `N errors during load. Run /plugin for details.` The skills count covers every skill a plugin provides, both its `commands/` entries and its `SKILL.md` skills. The agents count is the number of agents loaded in the session, including ones that don’t come from plugins. When a reloaded plugin’s [dependencies](plugins-dependencies.md) are missing, Claude Code installs them, reloads again, and appends `(+ N dependencies: <names>) resolved` to the summary.


[​](#reloads-that-change-mcp-tools)

Reloads that change MCP tools

When the reload would add or remove a plugin MCP server or the `LSP` tool, and that change would invalidate the [prompt cache](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin), Claude Code doesn’t apply the reload. It prints a line such as `This reload changes MCP tools (<server>) — your next message will re-read the whole conversation instead of using the cache. Run /reload-plugins --force to apply.` Pass `--force` to apply it anyway.


[​](#sessions-without-an-interactive-terminal)

Sessions without an interactive terminal

`/reload-plugins` also runs in sessions without an interactive terminal, such as the desktop app, the Agent SDK, and [non-interactive mode](../02-Claude-Code-CLI/headless.md) with `-p`. Requires Claude Code v2.1.260 or later. In those sessions, the command runs only when you type it into the session yourself, such as in the `-p` prompt or the desktop app’s prompt box. When it arrives another way, such as through [Remote Control](../02-Claude-Code-CLI/remote-control.md) or a message relayed from Slack, the command replies `/reload-plugins isn't available over a remote connection in this session.` and reloads nothing. The reload in those sessions doesn’t connect or disconnect plugin MCP servers. Those changes take effect in your next session.


[​](#flags-that-load-a-plugin-for-one-session)

Flags that load a plugin for one session

Two `claude` flags load a plugin for one session only, without installing it. Both are repeatable. Plugin authors use them to test a plugin before publishing. For the load-edit-reload workflow, see [Develop without a marketplace](plugins-create.md#develop-without-a-marketplace).

| Flag                  | Description                                                                                                                                                                | Example                                                                     |
|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------|
| `--plugin-dir <path>` | Load a plugin from a directory or a `.zip` archive of one. A folder of plugins loads each child folder that holds a `.claude-plugin/plugin.json`. Each flag takes one path | `claude --plugin-dir ./my-plugin --plugin-dir ./other.zip`                  |
| `--plugin-url <url>`  | Fetch a plugin `.zip` archive from a URL. Repeat the flag, or pass several URLs space-separated in one quoted value                                                        | `claude --plugin-url "https://example.com/a.zip https://example.com/b.zip"` |

A plugin that either flag loads is a session-only plugin. [`claude plugin list`](#plugin-list) shows it only when the same flag precedes the subcommand, as in `claude --plugin-dir ./my-plugin plugin list`. The plugin appears as `<name>@inline` under a heading that begins `Session-only plugins`, and `--json` reports its `scope` as `session`. When a session-only plugin shares a name with an installed plugin, Claude Code loads the session-only copy for that session and skips the installed one. The installed copy loads instead if you disabled the session-only copy with `claude plugin disable <name>@inline`, or if managed settings lock that plugin name. For the precedence, see [Plugin loading reference](plugins-loading.md). An administrator can reject both flags, and folders named in the [`CLAUDE_CODE_PLUGIN_DIRS`](../02-Claude-Code-CLI/env-vars.md#variables) variable, with the managed [`disableSideloadFlags`](../02-Claude-Code-CLI/settings-reference.md#disablesideloadflags) setting. Claude Code then prints that the flag is disabled by your organization’s managed settings and exits `1` without starting. From the Agent SDK, the [`plugins`](../05-Agent-SDK/agent-sdk-plugins.md) option is the equivalent of `--plugin-dir`.


[​](#next-steps)

Next steps

- [Install and manage plugins](discover-plugins.md): the same operations as steps, with what you see at each one
- [Plugin loading reference](plugins-loading.md): what each command changes on disk and which scope takes effect
- [Troubleshoot plugins](plugins-troubleshooting.md): install, marketplace, load, and validation error messages with their fixes
- [Plugin manifest reference](plugins-reference.md): the fields `claude plugin validate` checks

---

## Code intelligence plugins - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/code-intelligence
Path: 08-Plugins-Skills/plugins-code-intelligence.md

## On this page

- [Install a code intelligence plugin](#install-a-code-intelligence-plugin)
- [See what Claude gains](#see-what-claude-gains)
  - [Read the diagnostics yourself](#read-the-diagnostics-yourself)
- [Accept or dismiss the recommendation dialog](#accept-or-dismiss-the-recommendation-dialog)
  - [When the recommendation dialog appears](#when-the-recommendation-dialog-appears)
  - [Respond to the recommendation dialog](#respond-to-the-recommendation-dialog)
  - [Turn recommendations back on](#turn-recommendations-back-on)
- [Troubleshoot code intelligence](#troubleshoot-code-intelligence)
- [Add a language without an official plugin](#add-a-language-without-an-official-plugin)
- [Next steps](#next-steps)

Use plugins

# Code intelligence plugins

Copy pageCopy page

Install a language server plugin so Claude sees type errors after edits and navigates code by symbol, and answer the LSP plugin recommendation dialog.

Copy pageCopy page

A code intelligence plugin gives Claude the live diagnostics and go-to-definition that your editor has, so Claude catches type errors and missing imports that its own edits introduce before you run your build, and finds definitions and references by symbol instead of by text search. Each plugin connects Claude Code to a language server for one language through the Language Server Protocol (LSP). You install the plugin from Anthropic’s official marketplace and the language server binary on your machine.

Code intelligence plugins work in terminal sessions. In [cloud sessions](../02-Claude-Code-CLI/claude-code-on-the-web.md), Claude Code doesn’t start plugin language servers, so Claude gets no diagnostics or code navigation there. To write your own language server plugin, or to connect a language server that has no plugin, see [LSP servers in plugin components](plugins-components.md#lsp-servers).

To get started, find your language in the table under [Install a code intelligence plugin](#install-a-code-intelligence-plugin). The plugins in that table come from Anthropic’s [official plugin marketplace](plugins-anthropic-marketplaces.md). If you already saw an **LSP plugin recommendation** dialog, see [Accept or dismiss the recommendation dialog](#accept-or-dismiss-the-recommendation-dialog) for what each choice does.


[​](#install-a-code-intelligence-plugin)

Install a code intelligence plugin

A code intelligence plugin tells Claude Code which command starts the language server and which file extensions it handles. It doesn’t include the language server. Install the language server binary first, then the plugin, then confirm the server starts.

1

Install the language server binary

Find your language in the table below and install the binary in its row. If your language isn’t listed, see [Add a language without an official plugin](#add-a-language-without-an-official-plugin).

| Language                  | Plugin                                                                                                           | Binary                          |
|:--------------------------|:-----------------------------------------------------------------------------------------------------------------|:--------------------------------|
| C/C++                     | [`clangd-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/clangd-lsp)               | `clangd`                        |
| C#                        | [`csharp-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/csharp-lsp)               | `csharp-ls`                     |
| Go                        | [`gopls-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/gopls-lsp)                 | `gopls`                         |
| Java                      | [`jdtls-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/jdtls-lsp)                 | `jdtls`                         |
| Kotlin                    | [`kotlin-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/kotlin-lsp)               | `kotlin-lsp`                    |
| Liquid                    | [`liquid-lsp`](https://github.com/Shopify/liquid-skills/tree/main/plugins/liquid-lsp)                            | `shopify`, from the Shopify CLI |
| Lua                       | [`lua-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/lua-lsp)                     | `lua-language-server`           |
| PHP                       | [`php-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/php-lsp)                     | `intelephense`                  |
| Python                    | [`pyright-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/pyright-lsp)             | `pyright-langserver`            |
| Ruby                      | [`ruby-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/ruby-lsp)                   | `ruby-lsp`                      |
| Rust                      | [`rust-analyzer-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/rust-analyzer-lsp) | `rust-analyzer`                 |
| Swift                     | [`swift-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/swift-lsp)                 | `sourcekit-lsp`                 |
| TypeScript and JavaScript | [`typescript-lsp`](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/typescript-lsp)       | `typescript-language-server`    |

Anthropic maintains every plugin in the table except `liquid-lsp`, which Shopify maintains and the official marketplace lists.To find the command that installs the binary, follow the plugin’s link in the table to its README. For TypeScript, that command is `npm install -g typescript-language-server typescript`.After you install the binary, confirm it’s on the `PATH` of the shell you start `claude` from, for example with `which typescript-language-server`, or `Get-Command typescript-language-server` in PowerShell.

2

Install the plugin

In the VS Code extension or the desktop app, follow [Install a plugin](discover-plugins.md#install-a-plugin) instead of this step. In a terminal, start Claude Code by running `claude`, then enter this at its prompt, replacing `typescript-lsp` with the plugin the step 1 table lists for your language:

```python
/plugin install typescript-lsp@claude-plugins-official
```

A confirmation message says whether the plugin is active now or needs `/reload-plugins`. If the install fails with `Marketplace "claude-plugins-official" not found`, see the [troubleshooting entry for that error](plugins-troubleshooting.md#marketplace-claude-plugins-official-not-found). To control where the plugin is installed, or to run the install from your shell instead of inside Claude Code, see [Install plugins](discover-plugins.md).

3

Confirm the server starts

The language server starts the first time Claude edits a file with one of the plugin’s extensions. To see it work, ask Claude to introduce a type error in a file of that language and then fix it. Then check the conversation for a diagnostics line:

- **A diagnostics line appears**: `Found N new diagnostic issues in M files (ctrl+o to expand)` under the edit that introduced the error means the server started.
- **No diagnostics line appears**: run `/plugin` and open the **Errors** tab. A row reading `Executable not found in $PATH: "<binary>"` names the binary to install. If the tab has no such row, see [Troubleshoot code intelligence](#troubleshoot-code-intelligence).

After you install a missing binary, Claude Code tries again the next time Claude edits a matching file. If you installed the binary into a directory that isn’t on the `PATH` of the shell you started `claude` from, start a new session from a shell where it is.


[​](#see-what-claude-gains)

See what Claude gains

With a language server running, Claude gains diagnostics and code navigation:

- **Diagnostics after edits**: each time Claude edits or writes a file the server handles, Claude gets the errors and warnings the server reports. It sees a type error, missing import, or syntax error it introduced without running a compiler.
- **Code navigation**: Claude gets an `LSP` tool that looks up symbols through the server instead of searching text for them. The tool is read-only. For what Claude can look up with the tool and how permissions apply to it, see [LSP tool behavior](../02-Claude-Code-CLI/tools-reference.md#lsp-tool-behavior).


[​](#read-the-diagnostics-yourself)

Read the diagnostics yourself

After Claude edits a file the server handles, the conversation shows only the `Found N new diagnostic issues` summary. To read the issues themselves, press **Ctrl+O**.


[​](#accept-or-dismiss-the-recommendation-dialog)

Accept or dismiss the recommendation dialog

If a language server binary is already on your `PATH` and the plugin that uses it isn’t installed, Claude Code offers to install the plugin for you in a dialog titled **LSP plugin recommendation**.


[​](#when-the-recommendation-dialog-appears)

When the recommendation dialog appears

The **LSP plugin recommendation** dialog can appear after Claude edits a file. These conditions decide whether it appears and which plugin it offers:

- **A plugin matches the file**: one of the marketplaces you’ve added, or the official marketplace Claude Code registered for you, lists a code intelligence plugin for that file’s extension, and the plugin’s binary is installed.
- **Official first**: when more than one marketplace offers a plugin for the extension, the dialog offers the official marketplace’s plugin.
- **Once per session**: the dialog appears at most once in a session, for the first matching file Claude edits.
- **Not for cloud sessions**: the dialog never appears when your terminal is attached to a cloud session, such as one you started with [`claude --cloud`](../02-Claude-Code-CLI/claude-code-on-the-web.md#from-terminal-to-cloud).


[​](#respond-to-the-recommendation-dialog)

Respond to the recommendation dialog

The **LSP plugin recommendation** dialog names the plugin and offers these choices:

- **Yes, install**: Claude Code installs the plugin for your user account and prints `<plugin> installed · restart to apply`. Start a new session to load the server.
- **No, not now**: the dialog closes, and a later session can offer the plugin again. Pressing **Esc** does the same.
- **Never for this plugin**: the dialog stops appearing for that plugin and still appears for others.
- **Disable all LSP recommendations**: the dialog stops appearing for every language.

If you don’t choose an option, Claude Code closes it after 30 seconds and counts that as ignored. The count is kept across sessions. After five ignored dialogs, Claude Code stops recommending plugins, the same as if you’d chosen **Disable all LSP recommendations**.


[​](#turn-recommendations-back-on)

Turn recommendations back on

The **LSP plugin recommendation** dialog stops appearing after you choose **Disable all LSP recommendations** or ignore it five times.

- **Disabled or ignored five times**: to turn it back on in either case, remove the `lspRecommendationDisabled` and `lspRecommendationIgnoredCount` keys from `~/.claude.json`, Claude Code’s own configuration file.
- **Never for this plugin**: if you chose **Never for this plugin** and want that plugin offered again, remove its `name@marketplace` id from the `lspRecommendationNeverPlugins` list in the same file.


[​](#troubleshoot-code-intelligence)

Troubleshoot code intelligence

The plugins troubleshooting page covers the symptoms specific to code intelligence plugins under [Language server doesn’t start, uses too much memory, or reports wrong diagnostics](plugins-troubleshooting.md#language-server-doesnt-start):

- **The language server doesn’t start**: you see `Executable not found in $PATH` in the **Errors** tab of `/plugin`, or Claude never reports diagnostics for the language.
- **High memory use**: memory use increases while the server indexes the project.
- **False positive diagnostics in a monorepo**: diagnostics report imports as unresolved when they aren’t.


[​](#add-a-language-without-an-official-plugin)

Add a language without an official plugin

If your language isn’t in the [table of official plugins](#install-a-code-intelligence-plugin), you can still connect a language server.

1.  Write a plugin with an `.lsp.json` file that names the server command and the file extensions it handles.
2.  Then load the plugin with [`--plugin-dir`](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session) or publish it to a marketplace.

For the file’s fields and a worked example, see [LSP servers in plugin components](plugins-components.md#lsp-servers).


[​](#next-steps)

Next steps

- [LSP servers in plugin components](plugins-components.md#lsp-servers): write the `.lsp.json` for a language server that has no official plugin
- [Install and manage plugins](discover-plugins.md): scopes, updates, and uninstalling
- [Troubleshoot plugins](plugins-troubleshooting.md): load errors beyond the language-server ones on this page
- [Find plugins in the official marketplace](plugins-anthropic-marketplaces.md#find-plugins-in-the-official-marketplace): where to browse the rest of the official marketplace

---

## Code Review | Claude by Anthropic

Source: https://www.claude.com/plugins/code-review
Path: 08-Plugins-Skills/plugins-code-review.md

# Code Review

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

- 

  Install in
  Claude Code
- 

  Made by[Anthropic](https://anthropic.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-review)
- 

  Installs438,525

Automates pull request code review using multiple specialized agents working in parallel. Five independent reviewers analyze your changes from different angles: CLAUDE.md compliance checking, bug detection, git history context analysis, previous PR comment review, and code comment verification. Each finding is scored on a 0-100 confidence scale, and only high-confidence issues (default threshold: 80) are posted as comments, dramatically reducing false positives and review noise.

The plugin intelligently filters out PRs that don't need review—closed, draft, automated, or already-reviewed pull requests are automatically skipped. Comments include direct GitHub links with full SHA hashes and line number ranges for easy navigation to the exact code in question.

**How to use:** Run `/code-review` on any PR branch to launch the full automated review. The system will analyze your changes, score potential issues, and post actionable feedback directly to GitHub. You can customize the confidence threshold or focus areas (security, performance, accessibility) by editing the command configuration.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Code Simplifier | Claude by Anthropic

Source: https://www.claude.com/plugins/code-simplifier
Path: 08-Plugins-Skills/plugins-code-simplifier.md

# Code Simplifier

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

- 

  Install in
  Claude Code
- 

  Made by[Anthropic](https://anthropic.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-simplifier)
- 

  Installs346,763

Code Simplifier is an autonomous agent that refines and simplifies your code for improved clarity, consistency, and maintainability. It focuses on recently modified code sections, making targeted improvements while preserving all original functionality and behavior.

The agent follows established coding standards including ES modules, function declarations with explicit returns, and React best practices. It reduces nesting depth, eliminates redundant code, improves variable and function naming, and replaces nested ternaries with clearer conditional structures like switch statements.

**How to use:** The agent works proactively on your recently modified code. You can also ask it directly to review specific files or code sections with prompts like "simplify this component" or "refine the recently changed functions for clarity."

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Add components to a plugin - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/components
Path: 08-Plugins-Skills/plugins-components.md

## On this page

- [Explore the plugin directory](#explore-the-plugin-directory)
- [Add each kind of component](#add-each-kind-of-component)
  - [Skills](#skills)
  - [Commands](#commands)
  - [Define commands in the manifest](#define-commands-in-the-manifest)
  - [Agents](#agents)
  - [Organize agents in subfolders](#organize-agents-in-subfolders)
  - [Frontmatter fields in plugin agents](#frontmatter-fields-in-plugin-agents)
  - [Hooks](#hooks)
  - [When plugin hooks fire](#when-plugin-hooks-fire)
  - [Environment, quoting, and matching MCP tools](#environment-quoting-and-matching-mcp-tools)
  - [MCP servers](#mcp-servers)
  - [Reach users on claude.ai and Cowork](#reach-users-on-claude-ai-and-cowork)
  - [Server names, tool names, and reloads](#server-names-tool-names-and-reloads)
  - [Include a packaged MCPB server](#include-a-packaged-mcpb-server)
  - [LSP servers](#lsp-servers)
  - [Executables](#executables)
  - [Default settings](#default-settings)
  - [Themes and output styles](#themes-and-output-styles)
  - [Channels](#channels)
  - [Monitors](#monitors)
- [Ask the user for configuration values](#user-configuration)
  - [When the configuration dialog appears](#when-the-configuration-dialog-appears)
- [Reference plugin paths and store data](#path-variables-and-persistent-data)
  - [Install dependencies into the data directory](#install-dependencies-into-the-data-directory)
- [Next steps](#next-steps)

Create plugins

# Add components to a plugin

Copy pageCopy page

Add skills, hooks, MCP servers, and every other component type to a Claude Code plugin, with an example that validates for each.

Copy pageCopy page

A Claude Code plugin is built from components, such as skills, agents, hooks, and MCP servers. Each component has a default folder in the plugin, an optional manifest key in `.claude-plugin/plugin.json` that replaces or adds to that folder, and a name the user sees. For each key’s full field table, see the [manifest reference](plugins-reference.md#fields). Use this page to add a component to a plugin that already loads. After you add a component, run `/reload-plugins` in a running session or start a new one so Claude Code loads it. To check the component’s file before loading it, run [`claude plugin validate .`](plugins-cli-reference.md#plugin-validate) in your shell from the plugin directory.

These cases are covered on other pages:

- **Building your first plugin**: start with [Create a plugin](plugins-create.md)
- **Installing someone else’s plugin**: see [Install plugins](discover-plugins.md)
- **Your plugin’s users are on claude.ai or in Cowork**: a different set of components loads there. See [Plugin structure and testing](https://claude.com/docs/plugins/build) and the [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app)


[​](#explore-the-plugin-directory)

Explore the plugin directory

The explorer shows an example plugin, `my-plugin`, that has one of every kind of component in its default location:

- A review skill and an `about` command
- A security-review subagent
- A hook that formats files after Claude edits them, and the `scripts/` folder it calls
- A log monitor
- An output style and a color theme
- A route-audit workflow
- A `hello-plugin` executable
- Default settings
- A local MCP server and a Go language server

Each file is the smallest valid example of its format, there to show the shape rather than to be useful: a real skill or agent carries full instructions and often supporting files, and a real hook or monitor does real work. The sections after the explorer use the same files as their examples and link to fuller ones. Select a file or folder to read what it’s for, see what goes in it, and find the section that covers it.


[​](#add-each-kind-of-component)

Add each kind of component

Each section below covers one kind of component: where its files go in the plugin, an example that validates, what the user sees once the plugin loads, and the manifest key that changes the default location. Add the ones your plugin needs; none is required.


[​](#skills)

Skills

A [skill](skills.md) is a `SKILL.md` file that Claude can load when its description matches the task. The user can also run it as a command. Save each skill in its own directory under `skills/`:

```python
my-plugin/
├── .claude-plugin/
│   └── plugin.json
└── skills/
    └── review/
        └── SKILL.md
```

Give the `SKILL.md` a `description` so Claude knows when to use it:

skills/review/SKILL.md

```python
---
description: Reviews a pull request for style and test coverage. Use when asked to review code.
---

Review the changed files. Report style problems first, then missing tests.
```

After you load the plugin, `/my-plugin:review` runs the skill. The command name and who can invoke it follow these rules:

- **Command name**: `/<plugin>:<directory>`, so `skills/review/SKILL.md` in `my-plugin` is `/my-plugin:review`. If you set `name` in the frontmatter, it replaces the last segment and the plugin prefix stays. See [how a skill gets its command name](skills.md#how-a-skill-gets-its-command-name)
- **Who invokes it**: Claude, the user, or both, controlled by frontmatter. See [Control who invokes a skill](skills.md#control-who-invokes-a-skill)

You can also place skills outside the default `skills/` directory:

- **Additional directories**: list them in the `skills` manifest key. They add to the default `skills/` scan rather than replacing it, unlike `commands` and `agents`
- **A single skill at the plugin root**: with no `skills/` directory and no `skills` manifest key, a `SKILL.md` at the plugin root loads as one skill. Set `name` in its frontmatter, because otherwise a marketplace install names the skill after its [cache directory](plugins-loading.md#find-plugins-on-disk) rather than your plugin

To include instructions in a plugin, write them as a skill. Claude Code doesn’t load a `CLAUDE.md` at the plugin root, and `claude plugin validate` warns `CLAUDE.md at the plugin root is not loaded as project context`. If a rule must hold every time, such as [blocking edits to protected files](../07-Hooks/hooks-guide.md#block-edits-to-protected-files), add it to the plugin as a [hook](#hooks) rather than a skill. To choose between the two, see the Hook vs Skill tab under [Compare similar features](../02-Claude-Code-CLI/features-overview.md#compare-similar-features). For frontmatter fields and supporting files, see [Skills](skills.md).


[​](#commands)

Commands

A command is a single Markdown file the user runs by name, such as `/my-plugin:about`.

Commands are the older format, and [skills](#skills) supersede them for new work. A skill runs by name the same way, and it can also carry supporting files in its directory. Keep `commands/` for files you’re moving over from `.claude/commands/`.

Save a command at `commands/<file>.md` and it becomes `/<plugin>:<file>`. A subdirectory adds a segment, so `commands/db/migrate.md` is `/my-plugin:db:migrate`. Command files take the same frontmatter as skills.


[​](#define-commands-in-the-manifest)

Define commands in the manifest

You only need this if you want to keep command files somewhere other than `commands/`, or to define a short command inside `plugin.json` without a separate Markdown file. Set the `commands` manifest key, and Claude Code reads it instead of scanning `commands/`. The key takes a path, an array of paths, or an object that maps each command name to either a `source` file or inline `content`. This manifest defines `/my-plugin:about` inline, with no Markdown file:

.claude-plugin/plugin.json

```python
{
  "name": "my-plugin",
  "commands": {
    "about": {
      "content": "Summarize what this repository does in three sentences.",
      "description": "Summarize the repository"
    }
  }
}
```

Load the plugin and run `/my-plugin:about` in the session to confirm it loaded. For the full key syntax, see [`commands`](plugins-reference.md#commands).


[​](#agents)

Agents

A [subagent](../09-Agents-Patterns/sub-agents.md) is a separate assistant, with its own instructions and context window, that Claude can delegate a task to. Each Markdown file under `agents/` defines one:

agents/security-reviewer.md

```python
---
name: security-reviewer
description: Reviews code changes for security issues. Use after edits to authentication or input handling.
model: sonnet
---

You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
```

This agent is named `my-plugin:security-reviewer`, and the user can [invoke it explicitly](../09-Agents-Patterns/sub-agents.md#invoke-subagents-explicitly) with `@agent-my-plugin:security-reviewer`. The name form is `<plugin>:<name>`, where `<name>` comes from the frontmatter `name` field, or from the file name when that field is missing. The `agents` manifest key replaces the `agents/` scan.


[​](#organize-agents-in-subfolders)

Organize agents in subfolders

You can put plugin agent files in subfolders of `agents/`. Claude Code [loads them recursively](../09-Agents-Patterns/sub-agents.md#choose-the-subagent-scope) and joins the plugin name, each subfolder name, and the file name with colons to form the agent’s scoped name. For example, `agents/review/security.md` in a plugin named `my-plugin` loads as `my-plugin:review:security`. Two settings change that name:

- Frontmatter `name`: it replaces only the file name, so `name: audit` in `agents/review/security.md` loads as `my-plugin:review:audit`
- Manifest [`agents`](plugins-reference.md#fields) field: a file you list there loads without subfolder names, so `"agents": "./custom/review/security.md"` loads as `my-plugin:security`


[​](#frontmatter-fields-in-plugin-agents)

Frontmatter fields in plugin agents

A plugin agent’s frontmatter follows these rules:

- **Supported fields**: `name`, `description`, `model`, `effort`, `maxTurns`, `autoCompactWindow`, `tools`, `disallowedTools`, `skills`, `memory`, `background`, `omitClaudeMd`, `isolation`, `color`, and the `cacheTtl` key of `experimental`. The only valid `isolation` value is `"worktree"`. See [supported frontmatter fields](../09-Agents-Patterns/sub-agents.md#supported-frontmatter-fields) for what each one does
- **Ignored fields**: `permissionMode`, `hooks`, `mcpServers`, and `initialPrompt`. An agent file can’t add hooks or MCP servers on its own, so add those as plugin [hooks](#hooks) and [MCP servers](#mcp-servers) instead
- **A `name` longer than 256 characters**: Claude Code ignores it and names the agent after its file, as it does when the field is missing
- **Frontmatter that doesn’t parse**: the agent still loads with every field ignored. It’s named after the file, and its description reads `Agent from my-plugin plugin`. Run [`claude plugin validate`](plugins-cli-reference.md#plugin-validate) in your shell to find these files

For what each field does and the precedence rules, see [Subagents](../09-Agents-Patterns/sub-agents.md#supported-frontmatter-fields).


[​](#hooks)

Hooks

A [hook](../07-Hooks/hooks-guide.md) runs something automatically at a point in Claude Code’s lifecycle, such as after every file edit: a shell command, an HTTP request, an MCP tool call, a prompt to a model, or a subagent. Save the plugin’s hooks in `hooks/hooks.json` at the plugin root, under a top-level `"hooks"` key, in the same shape as the `hooks` object in `settings.json`. That lets you copy an existing settings hook in unchanged. This hook runs a bundled script after every `Write` or `Edit`:

hooks/hooks.json

```python
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/format.sh\""
          }
        ]
      }
    ]
  }
}
```

Save the script at `scripts/format.sh` and make it executable. Load the plugin and ask Claude to edit a file. A `PostToolUse` hook that exits 0 shows nothing in the transcript, so confirm it ran with [debug logging](../07-Hooks/hooks.md#debug-hooks) or by what the script itself changes. Hooks in `hooks/hooks.json` and in the `hooks` manifest key both load. For every event and its payload, see [Hook events](../07-Hooks/hooks.md#hook-events). When another enabled plugin has the same name, one of the two registers its `hooks/hooks.json` hooks and the other’s are left out. See [Hooks when two enabled plugins share a name](plugins-loading.md#hooks-when-two-enabled-plugins-share-a-name) for which one, and for the note in `/plugin` that tells you. To write hooks as JavaScript functions that run inside Claude Code and can draw in its interface, list a module file under a `modules` key in the same `hooks/hooks.json`. A plugin with one is a mod. See [Create a mod](plugins-mods-create.md).


[​](#when-plugin-hooks-fire)

When plugin hooks fire

A plugin’s hooks don’t wait for one of the plugin’s skills or commands to be used. Claude Code registers them when a session loads the plugin, and they fire on their events from then on. To limit when a hook runs, narrow its `matcher`. If a hook never fires, see [hooks that don’t fire](plugins-troubleshooting.md#failed-to-load-hooks-from-and-hooks-that-dont-fire).


[​](#environment-quoting-and-matching-mcp-tools)

Environment, quoting, and matching MCP tools

The hook’s environment, the quoting of `${CLAUDE_PLUGIN_ROOT}`, and matchers for the plugin’s own MCP tools work as follows:

- **Environment**: every hook process receives `CLAUDE_PLUGIN_ROOT` and `CLAUDE_PLUGIN_DATA` in its environment, plus `CLAUDE_PLUGIN_OPTION_<KEY>` for each [user configuration](#user-configuration) value, so your script can read them from there
- **Quoting**: when `command` has no `args`, it runs through a shell, so wrap the `${CLAUDE_PLUGIN_ROOT}` path in double quotes, as the `hooks/hooks.json` example under [Hooks](#hooks) does, to keep the expanded path one shell word. When you pass `args` instead, each element is passed as one argument with no shell and needs no quoting. See [exec form and shell form](../07-Hooks/hooks.md#exec-form-and-shell-form)
- **Matching the plugin’s own MCP tools**: a tool from an [MCP server this plugin declares](#mcp-servers) is named `mcp__plugin_<plugin>_<server>__<tool>`, so write that full name in the matcher. A matcher on the server name alone never fires. See [Match MCP tools](../07-Hooks/hooks.md#match-mcp-tools)


[​](#mcp-servers)

MCP servers

An MCP server gives Claude tools from an external system. Declare it in `.mcp.json` at the plugin root, in the same shape as a [project `.mcp.json`](../06-MCP-Tools/General/mcp.md#project-scope). This `.mcp.json` declares one server named `db`:

.mcp.json

```python
{
  "mcpServers": {
    "db": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
    }
  }
}
```

You can also omit the `mcpServers` wrapper and put `db` at the top level of the file. Load the plugin and run `/mcp` to confirm the server appears as `plugin:my-plugin:db`. `claude plugin validate` checks `.mcp.json` and reports a server entry that Claude Code would drop at load time as an error. Requires Claude Code v2.1.281 or later. For where a bad entry shows up at load time, see [MCP servers that don’t start](plugins-troubleshooting.md#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start). The `mcpServers` manifest key takes an inline server map, a path to a JSON file, or an array of those. When a manifest server has the same name as one in `.mcp.json`, the manifest server replaces it.


[​](#reach-users-on-claude-ai-and-cowork)

Reach users on claude.ai and Cowork

A local stdio server, such as the `db` server under [MCP servers](#mcp-servers), runs in Claude Code and in a Cowork session that runs on your machine in the Claude Desktop app, but not on claude.ai. To reach users there too, reference a remote server by its `https://` URL, which claude.ai and Cowork offer to the user as a connector, as [Bundle an MCP connector with its skill](https://claude.com/docs/plugins/build#bundle-an-mcp-connector-with-its-skill) shows.


[​](#server-names-tool-names-and-reloads)

Server names, tool names, and reloads

The server’s names, variable substitution, and reload behavior follow these rules:

- **Server name**: `plugin:<plugin>:<server>`, so the `db` server in `my-plugin` is `plugin:my-plugin:db` in `/mcp`. Use the same form to name the server in an [`mcp_tool` hook](../07-Hooks/hooks.md#mcp-tool-hook-fields)
- **Tool names**: `mcp__plugin_<plugin>_<server>__<tool>`, so a `query` tool on that `db` server is `mcp__plugin_my-plugin_db__query`. That is the name to use in [permission rules](../02-Claude-Code-CLI/permissions.md) and [hook matchers](#hooks)
- **Substitution**: `${CLAUDE_PLUGIN_ROOT}` and the other [path variables](#path-variables-and-persistent-data) are substituted in `command`, `args`, and `env`. No quoting is needed in `args`, because each element is passed as one argument
- **Reload**: when the user runs `/reload-plugins` and [the reload applies](plugins-cli-reference.md#reloads-that-change-mcp-tools), a server whose configuration is unchanged keeps its connection. A server whose configuration changed reconnects, and one you removed disconnects


[​](#include-a-packaged-mcpb-server)

Include a packaged MCPB server

The `mcpServers` key also accepts a packaged server as an [MCPB file](https://github.com/modelcontextprotocol/mcpb), whose extension is `.mcpb` or the older `.dxt`. Point the key at the file, as a path inside the plugin or an `https://` URL:

.claude-plugin/plugin.json

```python
{
  "name": "my-plugin",
  "mcpServers": "./servers/db.mcpb"
}
```

The server takes its name from the `name` in the bundle’s manifest. A bundle’s own manifest can declare settings the server needs from the user in a `user_config` block. A bundled server with a required setting that has no saved value doesn’t start. The `/plugin` **Errors** tab shows `Bundled MCP server "<name>" was not started: it needs configuration`. Users supply the values in one of two ways:

- **In `/plugin`**: select the plugin on the **Installed** tab and choose **Configure**
- **At install, from the shell**: pass [`--config <server>.<key>=<value>`](plugins-cli-reference.md#plugin-install) to `claude plugin install`. Requires Claude Code v2.1.285 or later, and works only for a bundle packaged inside the plugin.

For transports and authentication, see [MCP](../06-MCP-Tools/General/mcp.md#plugin-provided-mcp-servers).


[​](#lsp-servers)

LSP servers

An LSP server gives Claude diagnostics and code navigation for a language. If an [official code intelligence plugin](plugins-code-intelligence.md) already covers your language, install that instead of writing one. Otherwise declare the server in `.lsp.json` at the plugin root:

.lsp.json

```python
{
  "gopls": {
    "command": "gopls",
    "args": ["serve"],
    "extensionToLanguage": {
      ".go": "go"
    }
  }
}
```

The file maps each server name directly to its configuration, with no wrapper object around the map. `command` is the binary’s name, with its arguments in `args`. `extensionToLanguage` needs at least one extension, each starting with `.`. `claude plugin validate` doesn’t read this file. When any entry is invalid, the whole file is skipped at load and `Invalid LSP server config for ".lsp.json"` appears in the `/plugin` **Errors** tab. Your plugin configures the connection but doesn’t install the server binary, and each file extension gets one server:

- **Missing binary**: Claude Code starts `command` by name from the user’s `PATH`. When the binary isn’t there, the server fails to start and `claude --debug` logs `LSP server <name> failed to start`
- **Extension conflicts**: when two enabled servers claim the same extension, the first registered handles those files and the other isn’t used for them, whether the servers come from one plugin or two. The `/plugin` **Errors** tab shows the warning `LSP server "<name>" is not used for <ext> files`

The `lspServers` manifest key takes the same map inline, a path to a JSON file, or an array of those, and its servers add to the ones in `.lsp.json`. When a manifest server has the same name as one in `.lsp.json`, the manifest server replaces it. For `transport`, timeouts, restarts, and the other fields, see [`lspServers`](plugins-reference.md#lspservers). Send log output to stderr, not stdout. Claude Code reads a server’s stdout as protocol messages only, and accepts message headers up to 64 KiB and a message body up to 32 MiB. Claude Code disconnects a server that exceeds either limit or writes non-protocol output to stdout, and counts the disconnect as a crash for `restartOnCrash` and `maxRestarts`. When you run with `--debug`, Claude Code writes an error naming the cause to the debug log.


[​](#executables)

Executables

Files in `bin/` at the plugin root are on the `PATH` of the Bash tool’s shell while the plugin is enabled, so Claude can run them as bare commands. A command that Claude runs with the [PowerShell tool](../02-Claude-Code-CLI/tools-reference.md#powershell-tool) outside the [sandbox](../02-Claude-Code-CLI/sandboxing.md) finds them on its `PATH` too, and a sandboxed PowerShell command doesn’t. Add an executable script:

bin/hello-plugin

```python
#!/bin/bash
echo "hello from my-plugin"
```

Make it executable with `chmod +x bin/hello-plugin` and load the plugin. When you ask Claude to run `hello-plugin`, the Bash tool result shows the script’s output. Plugin `bin/` directories come after the user’s own `PATH` entries, so a plugin can’t shadow `git`, `ls`, or another system command. claude.ai and Cowork don’t install a plugin that has a top-level `bin/` directory, including one you [distribute through claude.ai organization settings](https://claude.com/docs/plugins/org-sync#keep-executables-out-of-the-top-level-bin-directory).


[​](#default-settings)

Default settings

To set defaults that apply while the plugin is enabled, add a `settings.json` at the plugin root, or put the same object inline in the `settings` manifest key. Two keys take effect, `agent` and `subagentStatusLine`, and every other key is dropped. Set `agent` to run one of the plugin’s own agents as the main thread:

settings.json

```python
{
  "agent": "security-reviewer"
}
```

Load the plugin and start a session. Claude then answers in the main conversation with the `security-reviewer` agent’s system prompt and model. For everything the key controls, see the [`agent` setting](../02-Claude-Code-CLI/settings-reference.md#agent). When the same key is set in more than one place, these rules decide which value applies:

- **File over manifest**: when both exist and `settings.json` sets at least one supported key, `settings.json` applies and the manifest’s `settings` is ignored
- **User settings over plugin defaults**: across settings sources, plugin defaults are the lowest layer, so a user’s own `agent` in `~/.claude/settings.json` overrides yours
- **Two plugins set the same key**: the value from the plugin loaded last applies, and `claude --debug` logs `overrides setting`

For the `subagentStatusLine` shape, see [subagent status lines](../02-Claude-Code-CLI/statusline.md#subagent-status-lines).


[​](#themes-and-output-styles)

Themes and output styles

A plugin can include color themes and output styles. Both appear in the same pickers as the user’s own. For either one, setting the manifest key replaces the folder scan.

| Component    | Save as                   | Format                                                                                                                           | Appears in                            | Manifest key          |
|:-------------|:--------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------|:----------------------|
| Theme        | `themes/<slug>.json`      | The [custom theme file](../02-Claude-Code-CLI/terminal-config.md#create-a-custom-theme) format users write in `~/.claude/themes/`                | `/theme`, under the file’s `name`     | `experimental.themes` |
| Output style | `output-styles/<name>.md` | The [custom output style](../02-Claude-Code-CLI/output-styles.md#create-a-custom-output-style) format, with `name` and `description` frontmatter | `/output-style`, as `<plugin>:<name>` | `outputStyles`        |

Plugin themes are read-only, so when a user edits one in `/theme`, the edit is saved as a copy in their own themes directory. This theme recolors the prompt accent and error text on the dark preset:

themes/dracula.json

```python
{
  "name": "Dracula",
  "base": "dark",
  "overrides": {
    "claude": "#bd93f9",
    "error": "#ff5555"
  }
}
```


[​](#channels)

Channels

A [channel](../02-Claude-Code-CLI/channels.md) lets an outside system such as a chat app send messages into a session. In a plugin, a channel is one of the MCP servers plus a `channels` entry that binds to it and can prompt for its own configuration. This manifest binds a channel to a `telegram` server and asks for a bot token:

.claude-plugin/plugin.json

```python
{
  "name": "my-plugin",
  "mcpServers": {
    "telegram": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"],
      "env": { "BOT_TOKEN": "${user_config.bot_token}" }
    }
  },
  "channels": [
    {
      "server": "telegram",
      "userConfig": {
        "bot_token": {
          "type": "string",
          "title": "Bot token",
          "description": "Telegram bot token",
          "sensitive": true
        }
      }
    }
  ]
}
```

`server` must match a key in `mcpServers`. The per-channel `userConfig` takes the same shape as the [top-level `userConfig` key](#user-configuration). For what the server must implement and how users enable a channel plugin, see [Package as a plugin](../02-Claude-Code-CLI/channels-reference.md#package-as-a-plugin) in the channels reference. For the field table, see [`channels`](plugins-reference.md#channels).


[​](#monitors)

Monitors

A monitor is a shell command that runs in the background for the whole session. What it prints reaches Claude as notifications, so Claude can react to a log or a status change without being asked to watch it. Save the entries in `monitors/monitors.json`:

monitors/monitors.json

```python
[
  {
    "name": "error-log",
    "command": "tail -F ./logs/error.log",
    "description": "Application error log"
  }
]
```

The command runs in a shell, in the session’s current working directory. It runs with your full user permissions and outside the [sandbox](../02-Claude-Code-CLI/sandboxing.md). A monitor’s command is limited in where it starts and what it can reference:

- **Interactive sessions only**: plugin monitors start in an interactive session and never in non-interactive mode with the `-p` flag. They also don’t start in sessions where the API provider or telemetry settings make the [Monitor tool](../02-Claude-Code-CLI/tools-reference.md#monitor-tool) unavailable
- **No user configuration**: `command` gets the [path variables](#path-variables-and-persistent-data) and `${ENV_VAR}` from the environment, but never `${user_config.*}`. A monitor that references one doesn’t start, and monitor processes don’t receive `CLAUDE_PLUGIN_OPTION_<KEY>` either
- **Disabling mid-session**: if you disable a plugin mid-session, Claude Code doesn’t stop monitors that are already running. They stop when the session ends

The `experimental.monitors` manifest key takes the same array inline or a path to a JSON file, and is read instead of `monitors/monitors.json`. For the `when` trigger and the other fields, see [`monitors`](plugins-reference.md#monitors).


[​](#user-configuration)

Ask the user for configuration values

Declare the values your plugin needs from the user in the `userConfig` manifest key, so users don’t edit `settings.json` themselves. Each option appears in a dialog with its `title` as the label and its `description` beneath it. Set `"sensitive": true` for a token or password. The dialog then masks the input, and the value is stored in secure storage rather than `settings.json`. This manifest asks for an endpoint and a token:

.claude-plugin/plugin.json

```python
{
  "name": "my-plugin",
  "userConfig": {
    "api_url": {
      "type": "string",
      "title": "API URL",
      "description": "Base URL of your team's API"
    },
    "api_token": {
      "type": "string",
      "title": "API token",
      "description": "Token for your team's API",
      "sensitive": true
    }
  }
}
```


[​](#when-the-configuration-dialog-appears)

When the configuration dialog appears

The dialog is part of the interactive `/plugin` interface. When the user does any of the following, it opens for any option that isn’t set yet:

- Installs the plugin in `/plugin`
- Runs `/plugin install <plugin>@<marketplace>` inside a session
- Enables the plugin from the **Installed** tab in `/plugin`

To open the same dialog at any time, the user runs `/plugin configure <plugin>@<marketplace>`. The VS Code extension’s [Manage plugins dialog](../03-IDE-Integrations/vs-code.md#install-plugins) asks for unset options as a form after an install, and a gear icon on the plugin’s row opens the form again with every option. The `claude plugin install` shell command never prompts for `userConfig` values. To set values from the shell, pass each one as `--config KEY=VALUE` when you install, or pipe a JSON object to [`claude plugin configure --values-stdin`](plugins-cli-reference.md#plugin-configure) afterward. When options remain unset, `claude plugin install` prints a `userConfig options not yet set` line. For the line’s exact text, see [The `userConfig` dialog never appears](plugins-troubleshooting.md#the-userconfig-dialog-never-appears). For the option fields, where each value is stored, how a component references a saved value, and which fields reject `${user_config.*}`, see [User configuration](plugins-reference.md#user-configuration).


[​](#path-variables-and-persistent-data)

Reference plugin paths and store data

You don’t know where your plugin will be installed, so refer to its files and data through these variables rather than fixed paths. They’re substituted in skill, command, and agent content, in hook and monitor commands, and in MCP and LSP server configurations. They’re also exported to hook, MCP, and LSP processes:

- **`${CLAUDE_PLUGIN_ROOT}`**: the plugin’s install directory. Each version has its own [cache directory](plugins-loading.md#find-plugins-on-disk), so the path changes when the plugin updates. Don’t write state there
- **`${CLAUDE_PLUGIN_DATA}`**: a directory that survives updates, for `node_modules`, virtual environments, and caches. It resolves to `~/.claude/plugins/data/<id>/` and is created when first referenced
- **`${CLAUDE_PROJECT_DIR}`**: the project root, the same value hooks receive

In the data directory path, `<id>` is the plugin identifier with every character other than letters, digits, `_`, and `-` replaced by `-`, so `my-plugin@my-marketplace` becomes `my-plugin-my-marketplace`. On Windows, the substituted paths use forward slashes so a shell doesn’t read backslashes as escapes.


[​](#install-dependencies-into-the-data-directory)

Install dependencies into the data directory

For a marketplace-installed plugin, Claude Code installs eligible [Node.js package dependencies](plugins-loading.md#node-js-package-dependencies) automatically when it caches the plugin, so you may not need to install them yourself. When you do, this `SessionStart` hook installs `node_modules` into `${CLAUDE_PLUGIN_DATA}` on first run and again after an update changes `package.json`:

hooks/hooks.json

```python
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\""
          }
        ]
      }
    ]
  }
}
```

After the first session, `~/.claude/plugins/data/<id>/node_modules` exists. An MCP server can then set `NODE_PATH` to `${CLAUDE_PLUGIN_DATA}/node_modules` in its `env`. For which fields substitute which variable, see [Environment variables](plugins-reference.md#environment-variables).


[​](#next-steps)

Next steps

- [Plugin manifest reference](plugins-reference.md): `plugin.json` fields, path rules, and the standard layout
- [Test plugins with evals](plugin-evals.md): check that the components you added change Claude’s behavior the way you intend
- [Publish and distribute a plugin](plugins-publish.md): version the plugin and put it in a marketplace
- [Troubleshoot plugins](plugins-troubleshooting.md): what to do when a component doesn’t load or a hook doesn’t fire

---

## Context7 | Claude by Anthropic

Source: https://www.claude.com/plugins/context7
Path: 08-Plugins-Skills/plugins-context7.md

# Context7

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

- 

  Install in
  Claude Code
- 

  Made by[Upstash](https://upstash.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/context7)
- 

  Installs417,801

Context7 is an MCP server that delivers up-to-date, version-specific documentation and code examples directly into your prompts. It solves a common problem with LLMs: outdated training data leading to hallucinated APIs and deprecated code patterns. Instead of relying on stale information, Context7 pulls current documentation straight from source repositories.

The plugin provides two main tools: **resolve-library-id** for matching library names to Context7-compatible identifiers, and **query-docs** for retrieving documentation for specific libraries. You can even target specific versions by mentioning them in your prompt.

**How to use:** Simply add "use context7" to any prompt where you need current documentation. For example: "Create a Next.js middleware that checks for a valid JWT in cookies. use context7" or "Configure a Cloudflare Worker script to cache JSON API responses. use context7". You can also specify exact libraries with "use library /supabase/supabase for API and docs".

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Create a Claude Code plugin - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/create
Path: 08-Plugins-Skills/plugins-create.md

## On this page

- [Decide when to use a plugin](#decide-when-to-use-a-plugin)
- [Create your first plugin](#create-your-first-plugin)
  - [Share your plugin](#share-the-plugin)
  - [Plugin layout](#plugin-layout)
- [Develop without a marketplace](#develop-without-a-marketplace)
  - [Load a plugin for one session](#load-a-directory-or-archive-for-one-session)
  - [Load a plugin from a directory or .zip](#load-a-plugin-from-a-directory-or-zip)
  - [Load a folder of plugins](#load-a-folder-of-plugins)
  - [Load a plugin from a URL](#load-a-plugin-from-a-url)
  - [Load plugins from an environment variable](#load-plugins-from-an-environment-variable)
  - [Make a plugin load in every session](#scaffold-a-plugin-that-loads-every-session)
  - [Scaffold the plugin with claude plugin init](#scaffold-the-plugin-with-claude-plugin-init)
  - [Name the plugin’s skills](#skill-names-in-a-scaffolded-plugin)
  - [Stop loading the plugin](#stop-loading-the-plugin)
  - [Share the plugin through a repository](#load-a-plugin-for-everyone-in-one-repository)
- [Test and debug](#test-and-debug)
  - [A component path isn’t found](#a-component-path-isn%E2%80%99t-found)
  - [--plugin-dir at a marketplace root doesn’t load the plugins under plugins/](#plugin-dir-at-a-marketplace-root-doesn%E2%80%99t-load-the-plugins-under-plugins%2F)
  - [The plugin loads but its skills are missing](#the-plugin-loads-but-its-skills-are-missing)
  - [The userConfig dialog never appears](#the-userconfig-dialog-never-appears)
  - [Check that the plugin changes Claude’s behavior](#check-that-the-plugin-changes-claude%E2%80%99s-behavior)
- [Convert an existing .claude/ setup](#convert-an-existing-claude-setup)
- [Next steps](#next-steps)

Create plugins

# Create a Claude Code plugin

Copy pageCopy page

Build your first Claude Code plugin from an empty directory, test it without a marketplace, and convert an existing .claude/ setup.

Copy pageCopy page

A plugin is a directory of skills, agents, hooks, and MCP servers, plus a `plugin.json` file, called the manifest, that names the plugin. Claude Code loads the directory as one unit, so you can share it with teammates, install it in several projects, or publish it to a marketplace. This page is for people writing their own plugins.

These cases are covered on other pages:

- **Installing someone else’s plugin**: see [Install plugins](discover-plugins.md)
- **Not sure you need a plugin**: see [Decide whether you need a plugin](plugins.md#decide-whether-you-need-a-plugin) on the overview
- **Your plugin’s users are on claude.ai or in Cowork**: the same folder installs there with a different subset of components. See [Plugin structure and testing](https://claude.com/docs/plugins/build) and the [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app)

Start from the section that matches what you already have:

- **Nothing yet**: follow [Create your first plugin](#create-your-first-plugin), then [Develop without a marketplace](#develop-without-a-marketplace) and [Test and debug](#test-and-debug).
- **Files under `.claude/` already**: do the first-plugin walkthrough once to learn the layout, then follow [Convert an existing `.claude/` setup](#convert-an-existing-claude-setup).


[​](#decide-when-to-use-a-plugin)

Decide when to use a plugin

Skills, agents, hooks, and MCP servers all work standalone in your project or home directory. Keep that standalone setup while it serves one project or only you. Make a plugin when you want to share the setup with teammates, install it in several projects, or publish versioned releases. When you move standalone skills, agents, hooks, and MCP config into a plugin, their location and names change:

- **Where the files go**: under the plugin’s own directory, called the plugin root, as `skills/`, `agents/`, `hooks/hooks.json`, and `.mcp.json`.
- **How they’re named**: plugin skills and agents get the plugin name as a prefix, such as `/my-plugin:hello`, so two plugins can each provide a `hello` skill without colliding.

To move an existing setup into a plugin, see [Convert an existing `.claude/` setup](#convert-an-existing-claude-setup).


[​](#create-your-first-plugin)

Create your first plugin

In this walkthrough, you create a plugin whose only component is one skill, a greeting, and run it with `--plugin-dir`, which loads a plugin for one session without installing it. A plugin can hold any mix of [components](plugins-components.md), such as skills, agents, hooks, and MCP servers, and none is required; one skill is the smallest example that shows the layout. You need Claude Code [installed and signed in](../01-Getting-Started/quickstart.md#step-1-install-claude-code). Open a terminal in the directory where you want to keep the plugin, such as `~/projects`, and run the commands in these steps from it. You can keep a plugin anywhere, because you pass its path to Claude Code when you start a session.

1

Create the plugin directory

Create the plugin directory, with a `.claude-plugin/` folder inside it to hold the manifest:

```python
mkdir -p my-first-plugin/.claude-plugin
```

2

Write the manifest

The [manifest](plugins-reference.md) is a JSON file named `plugin.json` that tells Claude Code the plugin’s name and describes it. Save this one as `my-first-plugin/.claude-plugin/plugin.json`:

my-first-plugin/.claude-plugin/plugin.json

```python
{
  "name": "my-first-plugin",
  "description": "A greeting plugin to learn the basics",
  "version": "1.0.0",
  "author": {
    "name": "Your Name"
  }
}
```

The four fields do this:

- **`name`**: required. It identifies the plugin and becomes the prefix on every skill and agent the plugin provides. Don’t put spaces in it.
- **`description`**: the text users see for the plugin in `/plugin`.
- **`version`**: optional. Setting it keeps users on that version until you change it; [Release a new version](plugins-host-marketplace.md#release-a-new-version) says when to set or omit it.
- **`author`**: who to credit. `name` is required inside it; `email` and `url` are optional.

Every other field is on the [manifest reference](plugins-reference.md#fields).Only `plugin.json` goes inside `.claude-plugin/`. The skill you add next goes directly under `my-first-plugin/`, next to that folder.

3

Add a skill

This plugin’s one component is a skill. Each skill is a directory under `skills/` that contains a `SKILL.md` file. Create the skill’s directory:

```python
mkdir -p my-first-plugin/skills/hello
```

Then create `my-first-plugin/skills/hello/SKILL.md` with this content:

my-first-plugin/skills/hello/SKILL.md

```python
---
name: hello
description: Greet the user with a friendly message
disable-model-invocation: true
---

Greet the user warmly and ask how you can help them today.
```

The `disable-model-invocation: true` line means Claude doesn’t run the skill on its own. Remove that line from a skill you want Claude to run on its own. The skill’s command combines the plugin name and the skill’s name, so you run this one as `/my-first-plugin:hello`. For the other frontmatter fields, see the [skill frontmatter reference](skills.md#frontmatter-reference).

4

Validate the plugin

Check the manifest and the skill’s frontmatter before you run anything:

```python
claude plugin validate ./my-first-plugin
```

The command prints the manifest path it checked and `✔ Validation passed`. If it prints `✘ Validation failed` instead, each line above that result line names the field to fix. Look up each message under [`claude plugin validate` reports errors](plugins-troubleshooting.md#claude-plugin-validate-reports-errors).

5

Run Claude Code with the plugin

Start a session with the plugin loaded:

```python
claude --plugin-dir ./my-first-plugin
```

Once Claude Code starts, run the skill:

```python
/my-first-plugin:hello
```

Claude replies with a greeting.

The plugin loads only in sessions you start with `--plugin-dir`. To keep working on it without the flag, or to test a `.zip` build, see [Develop without a marketplace](#develop-without-a-marketplace). To have Claude scaffold and check a larger plugin with you, [install](discover-plugins.md#install-a-plugin) Anthropic’s `plugin-dev` plugin from the `claude-plugins-official` marketplace, which adds skills and agents for writing components such as skills, hooks, and MCP servers and for validating the finished plugin. Once it’s installed, run `/plugin-dev:create-plugin` followed by a description of the plugin you want, and Claude walks you through designing, creating, and validating it.


[​](#share-the-plugin)

Share your plugin

A plugin you built with [Create your first plugin](#create-your-first-plugin) exists only on your machine. When it’s ready for other people, there are three ways to get it to them:

- **Send it to a few people directly**: give them the plugin’s directory or a `.zip` of it, and nothing needs to be published. See [Share a plugin without a marketplace](plugins-publish.md#share-a-plugin-without-a-marketplace).
- **List it in your own marketplace**: teammates add your marketplace once and install the plugin by name, and they receive your updates. See [Publish through your own marketplace](plugins-publish.md#publish-through-your-own-marketplace).
- **Submit it to Anthropic’s directory**: after it passes review, people can add it on claude.ai and in Cowork, and it reaches Claude Code through their account. See [Submit to Anthropic’s directory](plugins-publish.md#submit-to-anthropics-directory).


[​](#plugin-layout)

Plugin layout

Each kind of [component](plugins-components.md), such as skills, agents, hooks, and MCP servers, goes in a fixed directory under the plugin root, which is the directory you pass to `--plugin-dir`. Add only the directories you use. To click through a complete plugin directory and read what each file does, open the [plugin explorer](plugins-components.md#explore-the-plugin-directory). The table lists the directories most plugins start with, and the [full layout](plugins-reference.md#standard-layout) lists the rest.

| Location                     | Contents                                                                                                                          |
|:-----------------------------|:----------------------------------------------------------------------------------------------------------------------------------|
| `.claude-plugin/plugin.json` | The manifest. When you load a plugin with `--plugin-dir` and it has no manifest, Claude Code names the plugin after its directory |
| `skills/`                    | One `<name>/SKILL.md` directory per skill                                                                                         |
| `commands/`                  | Flat Markdown files, the older form of skills. Use `skills/` for new plugins                                                      |
| `agents/`                    | One Markdown file per subagent                                                                                                    |
| `hooks/hooks.json`           | Hook configuration: a top-level `"hooks"` key whose value has the same shape as `hooks` in a settings file                        |
| `.mcp.json`                  | MCP server definitions                                                                                                            |

Only `plugin.json` goes inside `.claude-plugin/`. Components saved there don’t load.The plugin root is the plugin’s own directory, not `~/.claude/` itself. A `.mcp.json` saved at `~/.claude/.mcp.json` doesn’t load.


[​](#develop-without-a-marketplace)

Develop without a marketplace

You don’t need a [marketplace](plugins.md#get-plugins-from-a-marketplace) to run a plugin you’re writing. Load it directly from disk or a URL instead:

- [`--plugin-dir`](#load-a-directory-or-archive-for-one-session): loads a directory or `.zip` archive for one session.
- [`--plugin-url`](#fetch-an-archive-from-a-url-for-one-session): fetches a `.zip` archive from a URL for one session.
- [`claude plugin init`](#scaffold-a-plugin-that-loads-every-session): scaffolds a plugin under `~/.claude/skills/` that loads across sessions.

If two plugins loaded in different ways share a name, see [Name conflicts](plugins-loading.md#name-conflicts) for which one Claude Code keeps.


[​](#load-a-directory-or-archive-for-one-session)

Load a plugin for one session

You can load a plugin for a single session in three ways: from a directory or `.zip` archive on disk with `--plugin-dir`, from a URL with `--plugin-url`, or from an environment variable when you can’t add a flag. Each plugin loads for that session only, and nothing is written to your settings for it. When you edit the plugin’s files during the session, run `/reload-plugins` to load the changes.


[​](#load-a-plugin-from-a-directory-or-zip)

Load a plugin from a directory or `.zip`

When you start `claude` from your shell, pass `--plugin-dir` with the plugin’s root directory or a `.zip` archive of it. Repeat the flag to load several plugins:

```python
claude --plugin-dir ./my-first-plugin --plugin-dir ./other-plugin.zip
```


[​](#load-a-folder-of-plugins)

Load a folder of plugins

To load several plugins from one place, pass a folder that holds them, such as `--plugin-dir ./plugins`. Loading a folder of plugins requires Claude Code v2.1.265 or later. If the folder has no `.claude-plugin/` directory and no plugin components at its top level, Claude Code treats it as a folder of plugins. Each immediate subfolder that has a `.claude-plugin/plugin.json` manifest then loads as a separate plugin. Everything else in the folder is skipped without an error, including a subfolder that has no manifest. If a plugin in the folder doesn’t load, check that its subfolder has a `.claude-plugin/plugin.json`. You can also pass a folder that keeps a `.claude-plugin/marketplace.json` beside its plugin folders. As long as that `.claude-plugin/` directory holds no `plugin.json`, the plugin folders still load. Nothing is installed or enabled from the marketplace file, because Claude Code doesn’t read it. Loading plugins from such a folder requires Claude Code v2.1.281 or later. In an interactive session, you can also add and remove plugins in the folder after startup:

- A subfolder you add loads as a new plugin once its manifest exists.
- When you remove a subfolder, its plugin unloads.

A message appears in the session for each of these changes. If loading or unloading a plugin mid-conversation would [invalidate the prompt cache](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin), the change is held instead, and the message tells you to run `/reload-plugins` to apply it.


[​](#load-a-plugin-from-a-url)

Load a plugin from a URL

When you start `claude` from your shell, pass `--plugin-url` with the address of a `.zip` archive, such as a build artifact your CI publishes:

```python
claude --plugin-url https://example.com/my-first-plugin.zip
```

Claude Code downloads the archive at startup. To load several, repeat the flag or pass the URLs space-separated in one quoted argument. Point the flag only at archives you control or trust. If Claude Code can’t fetch the archive, or the archive is invalid, it starts without the plugin and records a plugin load error that you can review in the `/plugin` manager’s **Errors** tab.


[​](#load-plugins-from-an-environment-variable)

Load plugins from an environment variable

To load plugins in a session where you can’t add the `--plugin-dir` flag, list their absolute paths in the [`CLAUDE_CODE_PLUGIN_DIRS`](../02-Claude-Code-CLI/env-vars.md#variables) environment variable instead. Claude Code loads each path as it loads a `--plugin-dir` path. These plugins load in addition to any you pass with `--plugin-dir`. [Project and local settings can’t set this variable](../02-Claude-Code-CLI/settings-reference.md#variables-claude-code-ignores-in-env). `CLAUDE_CODE_PLUGIN_DIRS` requires Claude Code v2.1.280 or later. Managed settings can turn off `--plugin-dir` and `CLAUDE_CODE_PLUGIN_DIRS`. See [Flags that load a plugin for one session](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session). To test a plugin together with a plugin it depends on, see [Test a plugin and its dependency locally](plugins-dependencies.md#test-a-plugin-and-its-dependency-locally).


[​](#scaffold-a-plugin-that-loads-every-session)

Make a plugin load in every session

Your personal skills directory is `~/.claude/skills/`, and a folder there that contains a `.claude-plugin/plugin.json` is a skills-directory plugin. Unless your administrator has [blocked skills-directory plugins](plugins-loading.md#skills-directory-plugins-blocked-by-managed-settings), Claude Code loads each one when a session starts, with no flag and no install step. `claude plugin init` scaffolds one for you.


[​](#scaffold-the-plugin-with-claude-plugin-init)

Scaffold the plugin with `claude plugin init`

`claude plugin init` writes a starter plugin under `~/.claude/skills/`. Scaffold one from your shell:

```python
claude plugin init my-tool
```

The command creates `~/.claude/skills/my-tool/` with a `.claude-plugin/plugin.json` and a root `SKILL.md`. It prints `✔ Created plugin "my-tool" at ~/.claude/skills/my-tool` followed by `It will auto-load next session as my-tool@skills-dir. Run /reload-plugins to load it now.` Pass `--with skills` to have `claude plugin init` scaffold a skill under `skills/` for you. The other `--with` values are on the [plugin commands reference](plugins-cli-reference.md#plugin-init).


[​](#skill-names-in-a-scaffolded-plugin)

Name the plugin’s skills

The root skill at `~/.claude/skills/my-tool/SKILL.md` is also a personal skill, so you invoke it as `/my-tool`, not `/my-tool:my-tool`. Skills you add under `skills/` inside the plugin get the plugin-name prefix, such as `/my-tool:example`.


[​](#stop-loading-the-plugin)

Stop loading the plugin

To stop loading a scaffolded plugin, delete its directory, or run `claude plugin disable my-tool@skills-dir` in your shell with the `my-tool@skills-dir` name that `claude plugin init` printed. In the ID `my-tool@skills-dir`, `skills-dir` stands where a marketplace name would, because the plugin loads from your skills directory rather than from a marketplace.


[​](#load-a-plugin-for-everyone-in-one-repository)

Share the plugin through a repository

`claude plugin init` writes the plugin to your personal skills directory at `~/.claude/skills/`, which all your projects on this machine share. To make a plugin load for everyone in one repository, create the same layout yourself at `<project>/.claude/skills/<name>/`, including its `.claude-plugin/plugin.json`. See [Plugins shared through a repository](plugins-loading.md#plugins-shared-through-a-repository) for the conditions under which Claude Code loads it.


[​](#test-and-debug)

Test and debug

When a change to your plugin doesn’t show up, work through these checks in order. Each one tells you what Claude Code did with the plugin:

1.  In your shell, run `claude plugin validate <path>`. It checks the manifest and the frontmatter of every skill, agent, and command file, and exits `0` on `Validation passed`. Add `--strict` to fail on warnings too. Exit codes and directory handling are on the [plugin commands reference](plugins-cli-reference.md#plugin-validate).
2.  In the running session, run `/reload-plugins` to apply edits you made on disk. It prints one `Reloaded:` line with counts. Then confirm a skill loaded by typing its `/plugin-name:skill` command, or by finding the plugin in the `/plugin` **Installed** tab.
3.  In the same session, run `/plugin`. The **Installed** tab lists your plugin and, in the plugin’s details, the components Claude Code found. The **Errors** tab lists what failed to load and why, such as a path in your manifest that doesn’t exist.
4.  Back in your shell, run `claude plugin list`. It prints session-only and skills-directory plugins in their own sections with `Status: ✔ loaded` or the load error. To include the plugin you’re developing, pass `--plugin-dir` with its path before `plugin list`.
5.  If your plugin is in a skills directory and `claude plugin list` doesn’t print it, with no error there or in `/plugin`, see [Skills-directory plugins blocked by managed settings](plugins-loading.md#skills-directory-plugins-blocked-by-managed-settings).

To check an MCP server, run `/mcp` in the session to see the server’s status. When the server is healthy, `/mcp` lists it as connected. If it isn’t, see [MCP servers that don’t start](plugins-troubleshooting.md#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start). To check a hook, trigger the event it matches. For example, ask Claude to edit a file to trigger a `PostToolUse` hook. Then read the [debug log](../07-Hooks/hooks.md#debug-hooks), which shows which hooks matched, their exit codes, and their output. The next sections cover the failures you’re most likely to hit while developing, and the [troubleshooting page](plugins-troubleshooting.md#build-a-plugin) has the full entry for each.


[​](#a-component-path-isn’t-found)

A component path isn’t found

The **Errors** tab of `/plugin` shows `<component> path not found: <path>`, for example `commands path not found`. A component path in your manifest, such as `commands`, `skills`, `agents`, or `hooks`, points at nothing. Fix the path or create the directory, then run `/reload-plugins` in the session. See [`commands path not found`](plugins-troubleshooting.md#commands-path-not-found).


[​](#plugin-dir-at-a-marketplace-root-doesn’t-load-the-plugins-under-plugins/)

`--plugin-dir` at a marketplace root doesn’t load the plugins under `plugins/`

`--plugin-dir` takes the plugin’s root directory, the one that contains `.claude-plugin/plugin.json` and the component directories such as `skills/`. If you point it at a marketplace root instead, Claude Code doesn’t read `marketplace.json`, so a plugin under `plugins/` doesn’t load, and you see no error. Point the flag at one plugin’s folder, or add the marketplace. See [the troubleshooting entry](plugins-troubleshooting.md#plugin-dir-loads-a-plugin-with-no-components).


[​](#the-plugin-loads-but-its-skills-are-missing)

The plugin loads but its skills are missing

The `skills/` directory is inside `.claude-plugin/`, or a `skills` entry in the manifest points at a file. Move `skills/` to the plugin root, point each `skills` entry at a directory that contains `SKILL.md`, and run `/reload-plugins` in the session. See [Plugin loads but its skills are missing](plugins-troubleshooting.md#plugin-loads-but-its-skills-are-missing).


[​](#the-userconfig-dialog-never-appears)

The `userConfig` dialog never appears

The dialog for your plugin’s [`userConfig`](plugins-components.md#user-configuration) options is part of installing through `/plugin` in a session. Loading with `--plugin-dir` doesn’t show it, and neither does `claude plugin install` in the shell. With the plugin loaded, run `/plugin configure <plugin-name>` in the session to open it. See [The `userConfig` dialog never appears](plugins-troubleshooting.md#the-userconfig-dialog-never-appears).


[​](#check-that-the-plugin-changes-claude’s-behavior)

Check that the plugin changes Claude’s behavior

A plugin that loads without errors can still fail to steer Claude the way you intend. `claude plugin eval`, which you run in your shell, runs your test cases with and without the plugin and scores the difference. See [Test plugins with evals](plugin-evals.md), starting with [Create your first eval suite](plugin-evals.md#create-your-first-eval-suite).


[​](#convert-an-existing-claude-setup)

Convert an existing `.claude/` setup

If you already have skills, agents, or hooks under a project’s `.claude/` directory, you can move them into a plugin without rewriting them. Run the commands in these steps from the project root, which is the directory that contains `.claude/`, because the `cp` paths are relative to it.

1

Create the plugin structure

Create the plugin directory and its `.claude-plugin/` folder alongside `.claude/`. You can move the plugin anywhere afterwards.

```python
mkdir -p my-plugin/.claude-plugin
```

Create `my-plugin/.claude-plugin/plugin.json`:

my-plugin/.claude-plugin/plugin.json

```python
{
  "name": "my-plugin",
  "description": "Migrated from standalone configuration",
  "version": "1.0.0"
}
```

2

Copy your existing files

Copy each configuration directory you have to the plugin root, and skip the command for any directory you don’t have.

```python
cp -r .claude/commands my-plugin/
```

```python
cp -r .claude/agents my-plugin/
```

```python
cp -r .claude/skills my-plugin/
```

Run `ls -a my-plugin` to confirm that each directory you copied appears next to `.claude-plugin`.

3

Move your hooks

If you have hooks in `.claude/settings.json` or `.claude/settings.local.json`, create a hooks directory:

```python
mkdir -p my-plugin/hooks
```

Create `my-plugin/hooks/hooks.json` and copy the `hooks` object from your settings file into it. The format is the same.This example shows the shape with one hook that runs a linter on each file Claude writes or edits. Replace the example with your own `hooks` object.

my-plugin/hooks/hooks.json

```python
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npm run lint:fix" }]
      }
    ]
  }
}
```

4

Test the migrated plugin

Load the plugin for a session:

```python
claude --plugin-dir ./my-plugin
```

Check each component under its new name:

- **Skills**: run `/my-plugin:deploy` for a skill that was `/deploy`.
- **Subagents**: ask Claude to use the `my-plugin:reviewer` agent for an agent that was `reviewer`.
- **Hooks**: trigger the event each hook matches.

If something is missing, work through [Test and debug](#test-and-debug).

While the originals are still under `.claude/`, they stay loaded alongside the plugin’s copies:

- **Skills and agents**: the two sets don’t collide, because the plugin’s skills and agents carry the `my-plugin:` prefix. `/deploy` and `/my-plugin:deploy` both work, and Claude sees `reviewer` and `my-plugin:reviewer` as two subagents.
- **Hooks**: hooks have no prefix, so a hook that is in both your settings file and `hooks/hooks.json` runs twice each time its event fires.

After you’ve confirmed the plugin works, delete the originals from `.claude/` and remove the `hooks` object from your settings file.


[​](#next-steps)

Next steps

- [Plugin components](plugins-components.md): add agents, hooks, MCP servers, LSP servers, and user configuration to your plugin
- [Test plugins with evals](plugin-evals.md): write eval cases and run them with `claude plugin eval` to check how reliably the plugin guides Claude’s behavior
- [Publish a plugin](plugins-publish.md): version it, put it in a marketplace, and submit it for review
- [Plugin structure and testing](https://claude.com/docs/plugins/build): the same plugin folder installs on claude.ai and in Cowork. Some components are Claude Code-only, and the [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app) lists which load on each surface
- [Plugin manifest reference](plugins-reference.md): every `plugin.json` field, path rule, and directory
- [Skills](skills.md): write the skills your plugin provides
- [Anthropic’s plugins in the claude-code repository](https://github.com/anthropics/claude-code/tree/main/plugins): complete worked examples of the layout on this page, such as `feature-dev` and `code-review`

---

## Plugin dependencies - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/dependencies
Path: 08-Plugins-Skills/plugins-dependencies.md

## On this page

- [Declare dependencies](#declare-dependencies)
  - [Declare a dependency with a version constraint](#declare-a-dependency-with-a-version-constraint)
  - [Bundle plugins for a team](#bundle-plugins-for-a-team)
  - [Depend on a plugin from another marketplace](#depend-on-a-plugin-from-another-marketplace)
  - [Test a plugin and its dependency locally](#test-a-plugin-and-its-dependency-locally)
- [Release a plugin that others depend on](#tag-plugin-releases-for-version-resolution)
  - [Create a release tag](#create-a-release-tag)
  - [Constrain a dependency that has a non-git source](#constrain-a-dependency-that-has-a-non-git-source)
- [How dependencies behave for your users](#how-dependencies-behave-for-your-users)
  - [How a constraint resolves against tags](#how-a-constraint-resolves-against-tags)
  - [Confirm the resolved version](#confirm-the-resolved-version)
  - [Combine constraints from several plugins](#combine-constraints-from-several-plugins)
  - [Dependencies when your plugin loads from claude.ai](#dependencies-when-your-plugin-loads-from-claude-ai)
- [See also](#see-also)

Create plugins

# Plugin dependencies

Copy pageCopy page

Declare the plugins your plugin depends on, with version ranges such as ^1.2, and see how Claude Code installs, resolves, and prunes them.

Copy pageCopy page

A plugin dependency is another plugin that your plugin relies on, such as one whose MCP server or skill it calls. Each dependency tracks the latest version its marketplace provides unless you declare a version constraint, a semantic-version range such as `^2.0` or `~2.1.0` that you’ve tested against. This page is for plugin authors who declare dependencies in `plugin.json` and for marketplace maintainers who tag releases.

These cases are covered on other pages:

- **Installing a plugin that has dependencies**: see [Manage installed plugins](discover-plugins.md#manage-installed-plugins)
- **Reading a dependency error**: see [Dependency errors](plugins-troubleshooting.md#dependency-errors)
- **Declaring the npm and Bun packages that your plugin’s own code needs**: see [Node.js package dependencies](plugins-loading.md#node-js-package-dependencies)

To add a constraint, start at [Declare a dependency with a version constraint](#declare-a-dependency-with-a-version-constraint). If you maintain a plugin that others depend on, [tag your releases](#tag-plugin-releases-for-version-resolution) so their constraints can resolve.


[​](#declare-dependencies)

Declare dependencies

Without a version constraint, a dependency moves to each new release its marketplace publishes the next time users update. If that release renames an MCP tool your plugin calls, your plugin breaks for everyone who updates. With a constraint such as `~2.1.0` on a dependency from a git-backed source, users who have your plugin installed keep receiving `2.1.x` patches of the dependency and never move to `2.2`. To upgrade on your own schedule, test against a newer release and then publish a new version of your plugin with a wider constraint.


[​](#declare-a-dependency-with-a-version-constraint)

Declare a dependency with a version constraint

List dependencies in the `dependencies` array of your plugin’s `.claude-plugin/plugin.json`. The following manifest declares one unversioned dependency and one constrained dependency:

.claude-plugin/plugin.json

```python
{
  "name": "deploy-kit",
  "version": "3.1.0",
  "dependencies": [
    "audit-logger",
    { "name": "secrets-vault", "version": "~2.1.0" }
  ]
}
```

An entry can be a string: the plugin name alone, such as `"audit-logger"` in this manifest, or `"name@marketplace"` to resolve it in another marketplace. With a bare string, your plugin depends on whatever version that plugin’s marketplace provides. To set a version constraint, use an object with these fields, each a string:

| Field         | Description                                                                                                                                                                                                                                                                                      |
|:--------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`        | The dependency’s plugin name, as it appears in its marketplace entry. Claude Code looks it up in the same marketplace as the declaring plugin unless you set `marketplace`. Required.                                                                                                            |
| `version`     | A [semantic-version range](https://github.com/npm/node-semver#ranges) such as `~2.1.0`, `^2.0`, `>=1.4`, or `=2.1.0`. The dependency installs at the highest git tag that satisfies this range, so the dependency’s maintainer must [tag releases](#tag-plugin-releases-for-version-resolution). |
| `marketplace` | A different marketplace to resolve `name` in. An allowlist controls cross-marketplace dependencies, described in [Depend on a plugin from another marketplace](#depend-on-a-plugin-from-another-marketplace).                                                                                    |

A range doesn’t match pre-release versions such as `2.0.0-beta.1` unless you opt in with a pre-release suffix such as `^2.0.0-0`.


[​](#bundle-plugins-for-a-team)

Bundle plugins for a team

To let engineers install a curated set of plugins with one command, publish a plugin whose manifest contains a `name` and a `dependencies` array. A plugin manifest needs only `name`, so this is a valid plugin, and installing it installs every dependency. For example, a platform team can publish role-specific bundles in an internal marketplace so engineers run one `claude plugin install` instead of installing each plugin separately:

.claude-plugin/plugin.json

```python
{
  "name": "backend-standard",
  "version": "1.0.0",
  "description": "Standard plugin set for backend engineers",
  "dependencies": [
    "secrets-vault",
    "deploy-kit",
    { "name": "db-migrate", "version": "^3.0" },
    "oncall-runbook"
  ]
}
```

To add a plugin to the standard set later, publish a new `backend-standard` version with the extra dependency. When the marketplace doesn’t [auto-update by default](plugins-loading.md#which-marketplaces-and-plugins-auto-update), engineers either turn on auto-update for the marketplace or update manually:

- **Turn on auto-update for the marketplace**: the next auto-update moves the bundle to the new version and installs any dependencies it adds.
- **Update manually**: run `claude plugin update backend-standard` in a shell, then `/reload-plugins` in an open session to install the newly added dependencies.

For the engineer-side steps, see [Keep plugins updated](discover-plugins.md#keep-plugins-updated). To deploy a bundle to everyone in an organization, an administrator adds it to `enabledPlugins` in managed settings. See [Pre-install and require plugins](plugins-org.md#pre-install-and-require-plugins).


[​](#depend-on-a-plugin-from-another-marketplace)

Depend on a plugin from another marketplace

By default, Claude Code doesn’t install a dependency from a different marketplace than the declaring plugin’s own, unless the user already has that dependency installed and enabled at the same scope. This default prevents one marketplace from silently installing plugins from a source the user hasn’t reviewed. To allow the install, add the target marketplace’s name to `allowCrossMarketplaceDependenciesOn` in the root marketplace’s `marketplace.json`. The root marketplace is the one that hosts the plugin the user is installing. Only the root marketplace’s allowlist applies. The following `marketplace.json` allows `deploy-kit` to depend on a plugin from `your-shared-marketplace`:

.claude-plugin/marketplace.json

```python
{
  "name": "your-marketplace",
  "owner": { "name": "Your Org" },
  "allowCrossMarketplaceDependenciesOn": ["your-shared-marketplace"],
  "plugins": [
    {
      "name": "deploy-kit",
      "source": "./deploy-kit",
      "dependencies": [
        { "name": "audit-logger", "marketplace": "your-shared-marketplace" }
      ]
    }
  ]
}
```

If `allowCrossMarketplaceDependenciesOn` is missing or doesn’t include the target marketplace, Claude Code doesn’t install the dependency. When the dependency is declared in the marketplace entry, the install itself is refused with a message that starts `Dependency "audit-logger@your-shared-marketplace" (required by deploy-kit@your-marketplace) is in marketplace "your-shared-marketplace", which is not in the allowlist` and names the field to set. When it’s declared in `plugin.json`, the install completes without the dependency and your plugin then fails to load. The allowlist check doesn’t apply to a dependency that is already enabled. If a user installs `audit-logger` from `your-shared-marketplace` themselves first, at the same scope, `deploy-kit` then installs without any change to the allowlist.


[​](#test-a-plugin-and-its-dependency-locally)

Test a plugin and its dependency locally

If you’re developing a plugin and the plugin it depends on at the same time, start Claude Code from your shell and load both with [`--plugin-dir`](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session):

```python
claude --plugin-dir ./my-dependency --plugin-dir ./my-plugin
```

The local copy of the dependency satisfies your plugin’s dependency entry, so you don’t need to install the dependency from its marketplace.

- **No `version` needed**: the local `plugin.json` doesn’t need a `version` either, because a [version constraint](#declare-a-dependency-with-a-version-constraint) isn’t checked against a local copy.
- **Entries that name a marketplace**: an entry that names a marketplace also matches the local copy on Claude Code v2.1.242 or later.

Until you install the dependency from its marketplace, your plugin stops loading whenever the local copy is disabled or absent:

- **You disabled the local copy**: your plugin is disabled at the next plugin load, with an error that ends `is disabled — enable it or remove the dependency`. When the error names the dependency as `<name>@inline`, that identifier refers to the `--plugin-dir` copy.
- **You started a session without the dependency’s `--plugin-dir` flag**: the error reports the dependency as not installed. Pass the flag again, or install the dependency from its marketplace.

When both plugins are in one parent folder, you can pass that folder to `--plugin-dir` once. If the folder isn’t itself a plugin, Claude Code loads each child folder that has a `.claude-plugin/plugin.json`. Requires Claude Code v2.1.265 or later.


[​](#tag-plugin-releases-for-version-resolution)

Release a plugin that others depend on

If you maintain a plugin that other plugins depend on with a version constraint, tag its releases so those constraints can resolve. A constraint resolves against git tags on the repository that hosts the plugin. Tag the repository that the plugin’s [plugin source](plugins-marketplace-reference.md#plugin-sources) in `marketplace.json` points at:

- **`github`, `url`, or `git-subdir` source**: the plugin’s own repository, so the plugin’s author creates the tags
- **Relative path such as `./plugins/secrets-vault`**: the marketplace repository, so the marketplace maintainer creates the tags


[​](#create-a-release-tag)

Create a release tag

Tag each release as `<plugin-name>--v<version>`, where `<version>` matches the `version` field in that commit’s `plugin.json`. The plugin-name prefix lets one marketplace repository host several plugins with independent version histories. Create the tag from the plugin directory, with an `origin` remote configured to receive the pushed tag, using [`claude plugin tag`](plugins-cli-reference.md#plugin-tag):

```python
claude plugin tag --push
```

The command builds the tag name from the plugin’s manifest. Before creating the tag, it runs these checks:

- Validates the plugin
- Checks that `plugin.json` and the marketplace entry agree on the version, when the plugin directory is inside a marketplace checkout
- Requires a clean working tree under the plugin directory
- Refuses if the tag already exists

A successful run prints `Created tag secrets-vault--v2.1.0`. With `--push`, it also prints `Pushed to origin`. Without `--push`, it prints the `git push` command to run yourself. Pass `--dry-run` to see the plan without creating anything. The [`claude plugin tag` reference](plugins-cli-reference.md#plugin-tag) lists the remaining flags. You can also run `git tag secrets-vault--v2.1.0` directly, as long as you keep the `version` in `plugin.json` and in the marketplace entry in sync yourself.


[​](#constrain-a-dependency-that-has-a-non-git-source)

Constrain a dependency that has a non-git source

Tag-based resolution applies only to git-backed sources. For a dependency with an `npm`, `archive`, or `command` [plugin source](plugins-marketplace-reference.md#plugin-sources), the constraint doesn’t control which version is fetched. It’s still checked when the plugin loads, and the dependent plugin is disabled if the installed version doesn’t satisfy it. For `npm`, `archive`, and `command` sources, the version checked is the `version` in the dependency’s `plugin.json`. Set one there before you constrain that dependency, because a `plugin.json` that sets no version satisfies no constraint. Claude Code never installs a dependency with a `command` source itself, so users [install it first](plugins-marketplace-reference.md#command-plugin-source). It also never runs a dependency’s [`headersHelper`](plugins-host-marketplace.md#authenticate-archive-downloads), so users also install a dependency whose marketplace entry sets one before they install your plugin. Besides `claude plugin install`, these operations also install any missing declared dependency, and the `command` and `headersHelper` limits apply to them too:

- `/reload-plugins`
- Auto-update of the dependent plugin’s marketplace
- Re-running `claude plugin install` on the dependent plugin
- `claude plugin marketplace add`


[​](#how-dependencies-behave-for-your-users)

How dependencies behave for your users

These sections describe how Claude Code resolves, checks, and combines the constraints you declare once your plugin is installed alongside others.


[​](#how-a-constraint-resolves-against-tags)

How a constraint resolves against tags

When a user installs a plugin that declares `{ "name": "secrets-vault", "version": "~2.1.0" }`, the dependency installs from the highest `secrets-vault--v` tag that satisfies `~2.1.0` on the repository that hosts `secrets-vault`. When no tag satisfies the range, the install either fails or uses the marketplace’s current copy:

- **Plugin with its own repository**: the install fails with a message containing `Dependency "secrets-vault@your-marketplace" has no git tag satisfying`.
- **Plugin referenced by a relative path**: the install uses the marketplace’s current copy instead, and the constraint is checked when the plugin loads. If that copy is outside the range, the dependent plugin stays disabled and `claude plugin list` shows `Requires "secrets-vault@your-marketplace" ~2.1.0, installed 3.0.0`.

For a plugin the marketplace references by a relative path, a marketplace you added as a local folder path also resolves constraints against that folder’s git tags, when the folder is a git repository. A local folder that isn’t a git repository has no tags, so Claude Code installs the dependency from the folder’s current contents instead.


[​](#confirm-the-resolved-version)

Confirm the resolved version

To confirm which version a constraint resolved to, run `claude plugin list` in your shell. A tag-resolved dependency shows its version with a 12-character commit suffix, such as `2.1.0-8713c5b11005`. Constraint checks use the tag’s version rather than the `version` in `plugin.json`, even if `plugin.json` at that commit lags behind. If you force-move a tag to a different commit, the next install fetches that commit’s content instead of reusing a stale cached copy. See [Versions and updates](plugins-loading.md#versions-and-updates) for how a plugin’s version becomes its cache key.


[​](#combine-constraints-from-several-plugins)

Combine constraints from several plugins

When several installed plugins constrain the same dependency, the dependency resolves to the highest version that satisfies all of their ranges. Common combinations resolve like this:

| Plugin A requires | Plugin B requires | Result                                                                                                                          |
|:------------------|:------------------|:--------------------------------------------------------------------------------------------------------------------------------|
| `^2.0`            | `>=2.1`           | One install at the highest `2.x` tag at or above `2.1.0`. Both plugins load.                                                    |
| `~2.1`            | `~3.0`            | Installing plugin B fails with a `has conflicting version requirements` message. Plugin A and the dependency stay as they were. |
| `=2.1.0`          | none              | The dependency stays at `2.1.0`. Auto-update skips newer versions while plugin A is installed.                                  |

Auto-update fetches a constrained dependency at the highest git tag that satisfies every installed plugin’s range, rather than at the marketplace’s latest version. If the installed plugins’ ranges don’t overlap, auto-update leaves that dependency at its current version, and the `/plugin` **Errors** tab shows an entry naming the constraining plugin. If they overlap but no tag falls in the range, auto-update fetches the marketplace’s current copy and skips the update when that copy’s `version` falls outside any installed plugin’s range. When a user uninstalls the last plugin that constrains a dependency, the dependency is no longer constrained to a version range and resumes tracking its marketplace entry on the next update.


[​](#dependencies-when-your-plugin-loads-from-claude-ai)

Dependencies when your plugin loads from claude.ai

When a user turns your plugin on for their claude.ai account, Claude Code loads it as `<name>@synced`, [with no marketplace](plugins-loading.md#synced-plugins). For that copy of your plugin, Claude Code counts a dependency entry that names a marketplace as met when the user has either of these:

- **The dependency from its marketplace**: installed from the marketplace your entry names, and enabled. Claude Code checks a `version` range in your entry against the installed copy’s version.
- **A claude.ai plugin of the same name**: enabled from their claude.ai account. Claude Code checks a `version` range in your entry against that plugin’s `version`, and a plugin that states no `version` passes the check.

If you tell users to turn your plugin on from claude.ai, tell them to turn the dependency on there too, or to install it from its marketplace. A claude.ai plugin meets the entry only when your plugin also loads from claude.ai.


[​](#see-also)

See also

- [`claude plugin prune`](plugins-cli-reference.md#plugin-prune): remove auto-installed dependencies no plugin needs anymore
- [Host a marketplace](plugins-host-marketplace.md): release channels and recommending other plugins

---

## Feature Dev | Claude by Anthropic

Source: https://www.claude.com/plugins/feature-dev
Path: 08-Plugins-Skills/plugins-feature-dev.md

# Feature Dev

Anthropic verified

Feature development workflow with agents for exploration, design, and review

- 

  Install in
  Claude Code
- 

  Made by[Anthropic](https://anthropic.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/feature-dev)
- 

  Installs256,017

A structured 7-phase workflow for building features systematically. Rather than jumping into code, this plugin guides you through understanding the existing codebase, clarifying requirements, designing architecture thoughtfully, and conducting quality reviews before and after implementation.

The plugin deploys three specialized agents: **code-explorer** traces execution paths and maps architecture layers to understand existing patterns; **code-architect** proposes multiple implementation approaches with clear trade-offs; and **code-reviewer** catches bugs, security issues, and convention violations with confidence-scored findings.

**How to use:** Run `/feature-dev` followed by a description of what you want to build. For example: `/feature-dev Add user authentication with OAuth` or simply `/feature-dev` to start the guided workflow. The plugin will walk you through discovery, codebase exploration, clarifying questions, architecture design, implementation, quality review, and a final summary.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Frontend Design | Claude by Anthropic

Source: https://www.claude.com/plugins/frontend-design
Path: 08-Plugins-Skills/plugins-frontend-design.md

# Frontend Design

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

- 

  Install in
  Claude Code
- 

  Made by[Anthropic](https://anthropic.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/frontend-design)
- 

  Installs1,134,112

Generate distinctive, production-grade frontend interfaces that stand out from generic AI-generated designs. This plugin enables Claude to create polished code with bold aesthetic choices, distinctive typography and color palettes, high-impact animations, and context-aware visual details.

The plugin establishes a design framework before coding—identifying purpose, audience, and a specific aesthetic direction (brutalist, maximalist, retro-futuristic, luxury, playful, etc.). It intentionally avoids common patterns like generic system fonts, predictable purple gradients, and cookie-cutter components.

Key design areas include thoughtful typography with unexpected font pairings, orchestrated motion and scroll-triggered interactions, spatial composition with asymmetry and grid-breaking elements, and visual depth through gradients, textures, and layered effects.

**How to use:** Simply ask Claude to build frontend interfaces and this skill activates automatically. Try prompts like "Create a dashboard for a music streaming app", "Build a landing page for an AI security startup", or "Design a settings panel with dark mode support".

## Other plugins

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## GitHub | Claude by Anthropic

Source: https://www.claude.com/plugins/github
Path: 08-Plugins-Skills/plugins-github.md

# GitHub

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

- 

  Install in
  Claude Code
- 

  Made by[GitHub](https://github.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/github)
- 

  Installs319,381

The GitHub plugin connects Claude Code directly to GitHub's platform through the official GitHub MCP server. Manage repositories, issues, pull requests, and workflows without leaving your development environment. Browse code, analyze commits, and understand project structure across all your accessible repositories.

Key capabilities include creating and updating issues and pull requests, reviewing code with AI assistance, monitoring GitHub Actions workflows, analyzing build failures, and managing releases. The plugin also provides security features like examining code scanning findings and reviewing Dependabot alerts.

**How to use:** After connecting your GitHub account, interact naturally with your repositories. Try prompts like "Create an issue for the login bug in my-app repo", "Review the latest PR on my project", "Show me failed GitHub Actions runs", "Search for repositories about machine learning", or "List open issues assigned to me". The plugin handles authentication and API calls automatically.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Host and maintain a marketplace - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/host-marketplace
Path: 08-Plugins-Skills/plugins-host-marketplace.md

## On this page

- [Host your marketplace](#host-your-marketplace)
  - [Register the marketplace for everyone in a repository](#register-the-marketplace-for-everyone-in-a-repository)
  - [Avoid relative-path entries in a URL-hosted marketplace](#avoid-relative-path-entries-in-a-url-hosted-marketplace)
  - [Stay within the download limits for hosted files](#stay-within-the-download-limits-for-hosted-files)
  - [Edit plugins in place on a shared directory](#edit-plugins-in-place-on-a-shared-directory)
  - [Keep plugin files out of Git LFS](#keep-plugin-files-out-of-git-lfs)
  - [Share files within a marketplace with symlinks](#share-files-within-a-marketplace-with-symlinks)
- [Distribute through organization settings](#distribute-through-organization-settings)
- [Grant access to a private marketplace](#grant-access-to-a-private-marketplace)
  - [Serve users who have no git-host account](#serve-users-who-have-no-git-host-account)
  - [What background auto-update does with credentials](#what-background-auto-update-does-with-credentials)
- [Roll out to a whole company](#roll-out-to-a-whole-company)
- [Keep users up to date](#keep-users-up-to-date)
  - [Turn on auto-update](#turn-on-auto-update)
  - [Release a new version](#release-a-new-version)
  - [Hold users on one version](#hold-users-on-one-version)
  - [Change the command of a command source](#change-the-command-of-a-command-source)
- [Run release channels](#run-release-channels)
- [Rename or remove a plugin](#rename-or-remove-a-plugin)
  - [Migrate users with a renames map](#migrate-users-with-a-renames-map)
  - [Uninstall removed plugins from users’ machines](#uninstall-removed-plugins-from-users%E2%80%99-machines)
- [Authenticate archive downloads](#authenticate-archive-downloads)
  - [Add a headersHelper to a plugin entry](#add-a-headershelper-to-a-plugin-entry)
  - [Write the headersHelper command](#write-the-headershelper-command)
  - [When Claude Code skips a headersHelper command or drops its output](#when-claude-code-skips-a-headershelper-command-or-drops-its-output)
  - [How users accept a headersHelper command](#how-users-accept-a-headershelper-command)
  - [Installs and updates that refuse a command instead of asking](#installs-and-updates-that-refuse-the-command-instead-of-asking)
  - [When a marketplace url source’s command runs](#when-a-marketplace-url-sources-command-runs)
- [Depend on and recommend other plugins](#depend-on-and-recommend-other-plugins)
- [Work around what a marketplace can’t do](#work-around-what-a-marketplace-can%E2%80%99t-do)
- [Next steps](#next-steps)

Run a marketplace

# Host and maintain a marketplace

Copy pageCopy page

Publish a plugin marketplace where users can reach it, grant access to a private one, and release updates and renames without breaking installs.

Copy pageCopy page

Hosting a marketplace means putting your `marketplace.json` catalog where other people can add it with `/plugin marketplace add`, install its plugins, and keep receiving your changes after you push. This page is for the person who operates a marketplace.

These cases are covered on other pages:

- **You haven’t written the catalog file yet**: start with [Create a marketplace](plugin-marketplaces.md)
- **You’re an admin requiring, restricting, or pre-installing marketplaces across your organization’s machines**: read [Manage plugins for your organization](plugins-org.md)

Start with [Host your marketplace](#host-your-marketplace) to pick a host and the command your users run. Read [Keep users up to date](#keep-users-up-to-date) before your first release. Read [Rename or remove a plugin](#rename-or-remove-a-plugin) before you change a plugin’s `name`.


[​](#host-your-marketplace)

Host your marketplace

You can host the marketplace on GitHub, on another git host, as a hosted `marketplace.json` URL, or in a directory on a shared filesystem. Send your users the add command for your host and tell them what they need on their machine:

| Host                                                             | Users run, in a Claude Code session                                    | What users need                                                                                                                                |
|:-----------------------------------------------------------------|:-----------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------|
| GitHub                                                           | `/plugin marketplace add your-org/your-marketplace`                    | `git`, and for a private repository the access described under [Grant access to a private marketplace](#grant-access-to-a-private-marketplace) |
| GitLab, Bitbucket, GitHub Enterprise Server, or another git host | `/plugin marketplace add https://gitlab.example.com/team/plugins.git`  | `git`, and access to the host from their machine. Send the full URL, because `owner/repo` shorthand always means github.com                    |
| A hosted `marketplace.json` URL                                  | `/plugin marketplace add https://plugins.example.com/marketplace.json` | HTTPS access to the URL. Users don’t need `git` for the catalog itself                                                                         |
| A directory on a shared filesystem                               | `/plugin marketplace add /Volumes/shared/claude-plugins`               | Read access to the path                                                                                                                        |

To pin a branch or tag of a GitHub or git-URL marketplace, tell users to append `#<ref>`, as in `your-org/your-marketplace#stable`. The [plugin commands reference](plugins-cli-reference.md#plugin-marketplace-add) lists every form the command accepts. A successful add prints `Successfully added marketplace: your-marketplace`. Claude Code takes that name from the `name` field in your `marketplace.json`, not from the repository name. Users then install a plugin by its entry’s `name` and the marketplace’s `name`, as in `/plugin install code-formatter@your-marketplace`.


[​](#register-the-marketplace-for-everyone-in-a-repository)

Register the marketplace for everyone in a repository

To share the marketplace with everyone who works in one repository, run `claude plugin marketplace add your-org/your-marketplace --scope project` there once from your shell and commit the `.claude/settings.json` it writes. Claude Code then registers the marketplace for each teammate who [trusts the folder](plugins-org.md#require-plugins-per-repository).


[​](#avoid-relative-path-entries-in-a-url-hosted-marketplace)

Avoid relative-path entries in a URL-hosted marketplace

When users add your marketplace as a bare `marketplace.json` URL, Claude Code downloads only that file. An entry in your `plugins` array whose `source` is a relative path such as `./plugins/formatter` then fails at install with [`its marketplace entry path does not stay inside the marketplace directory`](plugins-troubleshooting.md#plugins-with-relative-paths-fail-in-url-based-marketplaces). Give every entry a source that can be fetched on its own, such as a `github` repository or an `archive` URL, or host the marketplace in a git repository so Claude Code clones the whole tree.


[​](#stay-within-the-download-limits-for-hosted-files)

Stay within the download limits for hosted files

When users add your marketplace as a `marketplace.json` URL, or install an entry with an [`archive`](plugins-marketplace-reference.md#archive-plugin-source) source, Claude Code downloads the file from your server. The download fails past the limits in this table, so size your files and configure your server to stay inside them.

| File                                               | Largest download | Time for your server to respond | Redirects                                                                                                                                                                |
|:---------------------------------------------------|:-----------------|:--------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `marketplace.json` from a `url` marketplace source | 5 MiB            | 10 seconds                      | A redirect to a different origin must use `https://` and can’t point at a loopback, link-local, or cloud-metadata host, so a redirect from `https://` to `http://` fails |
| Zip from an `archive` plugin source                | 256 MiB          | 120 seconds                     | At most five. Every redirect target must use `https://` and can’t point at a loopback, link-local, or cloud-metadata host                                                |

A request that a redirect sends to a different origin carries none of the headers you configured on the marketplace source or the plugin entry. After an archive downloads, the install fails when the zip exceeds any of these extraction limits:

- **Entries**: 100,000 files and directories
- **File size**: 512 MiB for any one file, uncompressed
- **Total size**: 1 GiB uncompressed
- **Compression ratio**: uncompressed content 50 times the size of the zip


[​](#edit-plugins-in-place-on-a-shared-directory)

Edit plugins in place on a shared directory

When users add your marketplace from a shared directory, Claude Code reads plugins with relative-path sources directly from that directory instead of copying them. Users see your edits when they next start a session or run `/reload-plugins`, without an update step or a version bump.


[​](#keep-plugin-files-out-of-git-lfs)

Keep plugin files out of Git LFS

Keep the files your plugins need out of [Git LFS](https://git-lfs.com). When users add a marketplace hosted in a git repository, or install a git-based plugin it lists, Claude Code clones that marketplace or plugin repository onto their machine. The clone never downloads LFS content, so LFS-tracked files arrive as pointer files.


[​](#share-files-within-a-marketplace-with-symlinks)

Share files within a marketplace with symlinks

To share files between your plugin and other parts of the same marketplace, create symbolic links inside your plugin directory. When Claude Code copies the plugin into its cache, it handles each symlink by where the target resolves:

- **Within the plugin’s own directory**: the symlink is preserved as a relative symlink in the cache, so it keeps resolving to the copied target at runtime.
- **Elsewhere within the same marketplace**: the symlink is dereferenced. The target’s content is copied into the cache in its place. This lets a meta-plugin’s `skills/` directory link to skills defined by other plugins in the marketplace.
- **Outside the marketplace**: the symlink is skipped for security.

For plugins installed from a local path, or from a [`command` source](plugins-marketplace-reference.md#command-plugin-source) whose `mode` is the default `copy`, Claude Code preserves only symlinks that resolve within the plugin’s own directory and skips all others. The following command creates a link from inside a marketplace plugin to a shared skill defined by a sibling plugin. On Windows, use `mklink /D` from an elevated Command Prompt or enable Developer Mode:

```python
ln -s ../../shared-plugin/skills/foo ./skills/foo
```


[​](#distribute-through-organization-settings)

Distribute through organization settings

On a Team or Enterprise plan, you can also distribute the marketplace through [**Organization settings \> Plugins & skills**](https://claude.ai/admin-settings/skills?tab=inventory) on claude.ai instead of hosting it somewhere users add it themselves. Organization sync reads the repository through your organization’s GitHub or GitLab connection on claude.ai, so your users’ git credentials aren’t involved. Organization sync is stricter about the repository than `/plugin marketplace add` is:

- **Marketplace repository**: on github.com and gitlab.com, it must be private or internal
- **Plugin sources**: organization sync accepts only some [source types](plugins-marketplace-reference.md#plugin-sources)
- **Top-level `bin/` directory**: claude.ai rejects a plugin that has one and syncs the rest of the marketplace. The error message starts with `Plugin contains a top-level bin/ directory`. Keep executables in another directory, such as `scripts/`, and reference them as `${CLAUDE_PLUGIN_ROOT}/scripts/<name>` from your hooks or MCP server configs

[Sync your organization’s plugins from a repository](https://claude.com/docs/plugins/org-sync) on claude.com lists the accepted sources, the GitLab setup, and the `bin/` error, and [Manage plugins for your organization](https://claude.com/docs/plugins/admin) covers the admin workflow.


[​](#grant-access-to-a-private-marketplace)

Grant access to a private marketplace

When a user adds, installs from, or updates your marketplace, Claude Code runs `git` on their machine with interactive prompts turned off and relies on whatever credentials that machine already holds. Claude Code has no git token of its own, and `marketplace.json` has no field for one. You choose whether the clone runs over SSH or HTTPS by the form of the add command you send users:

- **GitHub `owner/repo`**: Claude Code probes `ssh -T git@github.com` and clones over SSH when the probe succeeds. If the probe fails, or the SSH clone itself fails, it clones over HTTPS. Users on machines without a GitHub SSH key can set `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1` to skip the probe and clone over HTTPS.
- **`git@host:path.git`**: SSH.
- **`https://example.com/repo.git`**: HTTPS.

Tell users what each protocol needs on their machine:

- **SSH**: the key must work without a passphrase prompt, for example because it’s loaded in `ssh-agent`. The host must already be in `known_hosts`.
- **HTTPS**: Claude Code leaves the user’s git credential helper enabled but forbids it from prompting. A credential the helper already stores works; one it would have to ask for fails. On GitHub, `gh auth login` followed by `gh auth setup-git` stores one.

For a GitHub Enterprise Server host, users need git access to that host from their machine. See [Plugin marketplaces on GHES](../02-Claude-Code-CLI/github-enterprise-server.md#plugin-marketplaces-on-ghes) for what each Claude Code surface needs to reach a GHES-hosted marketplace. If you distribute through **Organization settings \> Plugins & skills** on claude.ai instead, your users’ git credentials aren’t involved. See [Distribute through organization settings](#distribute-through-organization-settings).


[​](#serve-users-who-have-no-git-host-account)

Serve users who have no git-host account

Users without a git-host account can add a marketplace you serve as a `marketplace.json` URL or from a shared directory, but they can install only the plugins whose entry sources they can also reach. An entry that points at a private `github` repository still fails at install for them, because Claude Code fetches it with the same non-interactive `git` it uses for a git-hosted marketplace. These entry sources need no git account:

- **`archive`**: a zip downloaded over HTTPS. Users need neither `git` nor an account, only network access to the URL. Requires Claude Code v2.1.224 or later. Pin each archive with `sha256` so Claude Code refuses a changed download. To send credentials with the download, see [Authenticate archive downloads](#authenticate-archive-downloads).
- **A public git repository**: Claude Code clones a public `url` or `git-subdir` source over HTTPS without credentials when the entry gives an `https://` URL. For a `github` source, or a `git-subdir` source written as `owner/repo`, tell users without a GitHub SSH key to set `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`.

Keep `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1` in your instructions even if `claude plugin install` succeeds without it from the shell on a machine with no SSH key. For a `github` source, that command can fall back to HTTPS on its own, printing `SSH not configured, cloning via HTTPS`. Installs from `/plugin` inside a session and plugin updates don’t fall back, so without the variable they fail for users who have no GitHub SSH key. For a team on one network, a `directory` marketplace on a shared filesystem also works without git accounts. Users need only read access to the path.


[​](#what-background-auto-update-does-with-credentials)

What background auto-update does with credentials

Background auto-update is Claude Code’s unattended refresh of marketplaces and installed plugins after a session starts. It’s off for your marketplace until a user or admin turns it on, as covered under [Keep users up to date](#keep-users-up-to-date). When it’s on for a private marketplace, the background check for new commits uses the user’s configured git credential helpers and never prompts. Each kind of remote and helper gives a different result:

- **SSH remotes**: a key loaded in `ssh-agent` authenticates the check.
- **HTTPS remotes with a stored credential**: a helper that can supply a stored credential without prompting authenticates the check. Git Credential Manager, the macOS Keychain helper, and `git-credential-store` work this way once they hold a credential for the host.
- **HTTPS remotes with a helper that needs to prompt**: the helper can’t answer in the background. The update fails quietly and the existing checkout stays in place, so the user’s plugins keep working from the last synced state.

After the check, Claude Code does one of the following:

- **The checkout is up to date**: Claude Code leaves it as it is.
- **The check finds new commits, or fails because it can’t reach or authenticate to the remote**: Claude Code clones the marketplace again and replaces the existing checkout with the new clone. If that clone fails, the existing checkout stays in place. The re-clone can [time out on large repositories](plugins-troubleshooting.md#git-clone-timed-out-after-120s).

To keep a private marketplace current, a user can do either of the following:

- **Store a credential**: sign in to the credential helper first so it holds a credential for the host. For GitHub, run `gh auth login`, then `gh auth setup-git`.
- **Keep the checkout on failure**: if the user sets `CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1`, Claude Code keeps the existing checkout without attempting the re-clone when the background check can’t reach or authenticate to the remote. Plugins keep working from the last synced state.

If a user sets `GITHUB_TOKEN` or another provider token in the environment, that alone doesn’t authenticate the background check. A token takes effect through a credential helper, such as the `gh` CLI’s helper, which reads `GH_TOKEN` and `GITHUB_TOKEN`.


[​](#roll-out-to-a-whole-company)

Roll out to a whole company

Rolling a plugin out to a company involves you as the marketplace owner, an administrator who controls managed settings, and each person who uses Claude Code. You can run the rollout without the administrator, in which case each person adds the marketplace and installs the plugin themselves.

| Who                        | What they do                                                                                                                                                          | Where it’s covered                                                                                                                                                  |
|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| You, the marketplace owner | Keep the catalog in a repository only the company can read, send the add command for your host, and say what each person needs on their machine                       | [Host your marketplace](#host-your-marketplace) and [Grant access to a private marketplace](#grant-access-to-a-private-marketplace)                                 |
| An administrator           | Registers the marketplace and turns its plugins on for everyone with `extraKnownMarketplaces` and `enabledPlugins` in managed settings, and sets `autoUpdate` there   | [Require a marketplace and its plugins](plugins-org.md#require-a-marketplace-and-its-plugins) and [Set update policy](plugins-org.md#set-update-policy) |
| Each person                | Needs read access to a private git repository, with credentials already stored on their machine. Without an administrator, they also run the add and install commands | [Add a private marketplace](discover-plugins.md#add-a-private-marketplace)                                                                                     |

For people who have no git-host account, these sections each cover one way to reach them:

- **Entry sources that need no git account**: [Serve users who have no git-host account](#serve-users-who-have-no-git-host-account)
- **A pre-populated plugins directory**: [Seed containers and CI](plugins-org.md#seed-containers-and-ci), which also serves users who have no git-host account
- **claude.ai organization settings**: [Distribute through organization settings](#distribute-through-organization-settings), where your users’ git credentials aren’t involved


[​](#keep-users-up-to-date)

Keep users up to date

Your changes reach users through background auto-update, once it’s turned on for your marketplace, or when users update the plugin themselves. In both cases a user gets a new copy of a plugin only when its computed version changes, as described under [Release a new version](#release-a-new-version).


[​](#turn-on-auto-update)

Turn on auto-update

Background auto-update is off for your marketplace by default, and `marketplace.json` has no field to turn it on. A user or an admin turns it on:

- **Tell users to turn it on**: each user goes to **Marketplaces** in `/plugin`, selects your marketplace, and selects **Enable auto-update**.
- **Ask an admin to set it**: if an admin sets `"autoUpdate": true` on your marketplace’s `extraKnownMarketplaces` entry in managed settings, it’s on for everyone who receives those settings. See [Set update policy](plugins-org.md#set-update-policy).

Without auto-update, users receive your changes when they run `/plugin marketplace update <name>` in a session or `claude plugin update <plugin>@<name>` in the shell. For what users see when an update reaches them, see [When auto-update runs](plugins-loading.md#when-auto-update-runs).


[​](#release-a-new-version)

Release a new version

To release a new version to users, change the plugin’s `version`. Users get a new copy only when the plugin’s computed version differs from the one they have. That version comes from `plugin.json` first, then from the marketplace entry, per [Versions and updates](plugins-loading.md#versions-and-updates). A plugin that users [load in place](plugins-loading.md#find-plugins-on-disk) from a marketplace they added from a local path isn’t controlled by `version`. It loads your current files at every session start, whatever its version string says. For every install other than an in-place load or one from a `command` source, either increase `version` on each release or omit it:

- **Bump `version` on each release**: users stay on their cached copy until the string changes. If you set `"version": "1.0.0"` and push new commits without changing it, users don’t receive them.
- **Omit `version`**: users track your commits instead. Leave `version` out of both `plugin.json` and the marketplace entry.

Don’t set `version` in both `plugin.json` and the marketplace entry. If you do, Claude Code uses the `plugin.json` value without warning, and `claude plugin validate` reports the mismatch as `Entry declares version "<a>" but <path>/plugin.json says "<b>"`.


[​](#hold-users-on-one-version)

Hold users on one version

One marketplace serves one version of each plugin at a time, so you hold users on a version by choosing what each entry points at:

- **`ref` and `sha` on the plugin entry**: `ref` names a branch or tag and `sha` names a commit for a `github`, `url`, or `git-subdir` source. See [Plugin sources](plugins-marketplace-reference.md#plugin-sources).
- **`#<ref>` on the add command**: users who add `your-org/your-marketplace#stable` get that branch or tag of the catalog. For two release lines at once, see [Run release channels](#run-release-channels).
- **`<plugin>--v<version>` tags**: a dependency’s version range resolves against these tags. See [Release a plugin that others depend on](plugins-dependencies.md#tag-plugin-releases-for-version-resolution).

[Release a new version](#release-a-new-version) says when a changed entry reaches users.


[​](#change-the-command-of-a-command-source)

Change the command of a command source

If you change the `command` of a [`command` source](plugins-marketplace-reference.md#command-plugin-source), or switch its `mode`, each user has to accept the new command before Claude Code runs it. Claude Code runs only the exact command a user accepted when they installed or last updated the plugin. After a user’s copy of your marketplace picks up the change, that user sees the following:

- **No more background runs**: the [once-per-session run](plugins-loading.md#when-a-command-source-re-runs) of the command stops for that user, so the tool’s new output doesn’t reach them.
- **An entry in the `/plugin` Errors tab**: the entry shows the new command and the `claude plugin update` command to run.

Tell users to run the `claude plugin update` command that entry shows, in a terminal. Claude Code shows them the new command and asks them to accept it.


[​](#run-release-channels)

Run release channels

To offer stable and early-access tracks, host two marketplaces whose entries point at different refs of the same plugin, and let each user add the one they want. Claude Code has no release-channel concept, and one marketplace serves one version of each plugin at a time. Give the two `marketplace.json` files different `name` values. Claude Code identifies a marketplace by its `name`, so a user can’t have two marketplaces with the same name registered at once. With these two catalogs, users who add `stable-tools` install `code-formatter` from the `stable` branch, and users who add `latest-tools` install it from `latest`:

```python
{
  "name": "stable-tools",
  "owner": { "name": "Your Org" },
  "plugins": [
    { "name": "code-formatter", "source": { "source": "github", "repo": "your-org/code-formatter", "ref": "stable" } }
  ]
}
```

```python
{
  "name": "latest-tools",
  "owner": { "name": "Your Org" },
  "plugins": [
    { "name": "code-formatter", "source": { "source": "github", "repo": "your-org/code-formatter", "ref": "latest" } }
  ]
}
```

Give the two refs different `plugin.json` versions, or omit `version` so the commit SHA distinguishes them. Updates are detected by comparing versions, so a ref that moves without a version change leaves users on the cached copy. To assign the channels to user groups instead of letting users choose, an admin gives each group the matching `extraKnownMarketplaces` entry, as described under [Set update policy](plugins-org.md#set-update-policy).


[​](#rename-or-remove-a-plugin)

Rename or remove a plugin

A plugin’s `name` is its identifier. Users reference it in the `enabledPlugins` and `pluginConfigs` settings keys and in `/plugin install`, so changing it breaks every existing install. To change the label users see in `/plugin` without breaking anything, set `displayName` in `plugin.json` and keep `name` unchanged.


[​](#migrate-users-with-a-renames-map)

Migrate users with a renames map

When you must change a `name`, add a top-level `renames` map to `marketplace.json` so Claude Code migrates existing users instead of reporting [`Plugin "<name>" not found in marketplace`](plugins-troubleshooting.md#plugin-not-found-in-marketplace). Do the same when you remove an entry from `plugins`. Map each former name to its current name, or to `null` when the plugin is gone. This marketplace renames `formatter` to `code-formatter` and records that `legacy-linter` was removed:

```python
{
  "name": "your-marketplace",
  "owner": { "name": "Your Org" },
  "plugins": [
    { "name": "code-formatter", "source": "./plugins/code-formatter" }
  ],
  "renames": {
    "formatter": "code-formatter",
    "legacy-linter": null
  }
}
```

After you push, a user who still has the old name enabled sees one of these results:

- **Renamed entry**: the plugin loads under its new name. `claude plugin list` and the plugin’s details under `/plugin` show `Renamed to "code-formatter" in the "your-marketplace" marketplace` once, and Claude Code rewrites the old key to the new one in `enabledPlugins` and `pluginConfigs` in the user, project, and local settings scopes.
- **`null` entry**: the old key is dropped from those scopes and the user sees `Removed from the "your-marketplace" marketplace`.
- **Enabled in managed settings**: the plugin still loads under its new name, but Claude Code can’t rewrite managed settings, so the notice recurs until an admin updates `enabledPlugins` there.

For a marketplace users added from a git repository or URL, a renamed plugin reports [`Plugin "<name>" not cached at <path>`](plugins-troubleshooting.md#plugin-not-cached-at) until the user runs `/plugin install code-formatter@your-marketplace` once in a session. Treat `renames` as append-only history. Keep old entries after everyone has migrated. When you rename again, add a second entry rather than editing the first, because Claude Code follows the chain from the oldest name. In your shell, run `claude plugin validate .` after editing the map. It rejects a chain that cycles or that ends anywhere other than `null` or a name in `plugins`, with `renames.<name>: chain does not resolve`.


[​](#uninstall-removed-plugins-from-users’-machines)

Uninstall removed plugins from users’ machines

To uninstall a removed plugin from users’ machines rather than leave a copy behind, set `"forceRemoveDeletedPlugins": true` at the top level of `marketplace.json`. Without the field, a removed plugin stays installed and reports `Plugin "<name>" not found in marketplace` when a session loads it. With it, Claude Code does the following at each session start:

1.  Compares what users installed from your marketplace against the entries and the `renames` map, and treats any plugin that is neither listed nor renamed as removed.
2.  Uninstalls each removed plugin from the user, project, and local scopes. Plugins that only managed settings installed stay in place.
3.  Lists each removed plugin under a **Flagged** heading in `/plugin` with the status `Removed from marketplace`.


[​](#authenticate-archive-downloads)

Authenticate archive downloads

To authenticate an [`archive`](plugins-marketplace-reference.md#archive-plugin-source) download, such as a download from a private registry, set the HTTP headers Claude Code sends with it. You can set `headers` in either of these places:

- **The marketplace’s `url` source**: the `url` source you registered the marketplace from, such as an [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) entry.
- **The plugin’s entry**: on Claude Code v2.1.238 or later, you can set it on the plugin’s `marketplace.json` entry instead, beside `source`.

In either place, set a `headersHelper` command instead of `headers` when the value is short-lived, such as a token your registry generates on request. Claude Code runs the command and sends the JSON object it prints as that place’s headers. Requires Claude Code v2.1.238 or later. The [marketplace reference](plugins-marketplace-reference.md#plugin-entries) lists the `headers` and `headersHelper` entry fields. The place you choose decides which downloads get the headers and when Claude Code runs the command:

| Place                    | Downloads that get the headers                                                             | When Claude Code runs a `headersHelper` set there                                                                                                                   |
|:-------------------------|:-------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Marketplace `url` source | Archive downloads on the marketplace URL’s origin, meaning the same scheme, host, and port | Before each fetch of the marketplace’s `marketplace.json` and before each archive download on that origin. Claude Code reuses one run’s output for up to 60 seconds |
| Plugin entry             | That entry’s download only                                                                 | Only when a user installs or updates that one plugin by itself and [accepts the command](#how-users-accept-a-headershelper-command)                                 |

Where both places set a header of the same name, Claude Code sends the entry’s value. Within one place, a header the command prints overrides a header of the same name listed in `headers`.


[​](#add-a-headershelper-to-a-plugin-entry)

Add a headersHelper to a plugin entry

This entry sets `headersHelper` beside `source`. It also sets [`"strict": false`](plugins-marketplace-reference.md#strict-mode), which Claude Code requires of a `marketplace.json` entry that sets `headersHelper`:

```python
{
  "name": "my-plugin",
  "description": "Formatting commands for internal services",
  "strict": false,
  "source": {
    "source": "archive",
    "url": "https://registry.example.com/plugins/my-plugin-2.1.0.zip"
  },
  "headersHelper": "/opt/bin/mint-registry-token.sh"
}
```

To check the entry, run `claude plugin install my-plugin@your-marketplace` in your shell. Claude Code shows you the command and the archive URL, and downloads the zip after you accept.


[​](#write-the-headershelper-command)

Write the headersHelper command

Whether you set `headersHelper` on a marketplace’s `url` source or on a plugin entry, write the command to meet these requirements:

- **Command text**: at most 500 characters of printable ASCII, with no run of four or more spaces.
- **Output**: print one JSON object of header names and string values on stdout, then exit 0 within 10 seconds.
- **Shell and working directory**: Claude Code runs the command through `sh`, or through `cmd.exe` on Windows. The working directory is the configuration directory, which is `~/.claude` or [`CLAUDE_CONFIG_DIR`](../02-Claude-Code-CLI/env-vars.md#variables). Give an absolute path or a command on `PATH`, because a relative path resolves against that directory, not the user’s project.
- **Variables Claude Code removes**: when the command is set in a `marketplace.json` entry, or in a project’s `.claude/settings.json` or `.claude/settings.local.json`, Claude Code removes from the environment every variable whose name looks like a credential, by the [same rule it applies to an MCP `headersHelper`](../06-MCP-Tools/General/mcp.md#which-variables-a-helper-can-read). `ANTHROPIC_API_KEY` and `MY_REGISTRY_TOKEN` are both removed, so have the command read its credential from a file or a credential store. This removal doesn’t apply to a command set in user settings, a `--settings` file, or managed settings.
- **Variables Claude Code sets**: `CLAUDE_CODE_MARKETPLACE_URL` and `CLAUDE_CODE_MARKETPLACE_NAME` for a `url` source’s command, and `CLAUDE_CODE_PLUGIN_NAME` and `CLAUDE_CODE_PLUGIN_ARCHIVE_URL` for an entry’s command. `CLAUDE_CODE_MARKETPLACE_NAME` is unset on the first fetch after a user adds a marketplace by URL, because that fetch is what supplies the name.

A command that mints a bearer token prints an object like this one:

```python
{"Authorization": "Bearer eyJhbGciOiJSUzI1NiJ9"}
```


[​](#when-claude-code-skips-a-headershelper-command-or-drops-its-output)

When Claude Code skips a headersHelper command or drops its output

A `headersHelper` command doesn’t run, or headers from `headers` or from the command’s output are dropped, when one of the following applies:

- **Command fails**: if the command exits non-zero, runs past 10 seconds, or prints anything other than a JSON object of string values, the fetch or download the command was run for doesn’t happen.
- **Marketplace URL doesn’t start with `https://`**: that `url` source’s command doesn’t run, and requests carry only the headers listed in its `headers` field.
- **Redirect leaves the origin**: when a download is redirected off the archive URL’s origin, the redirected request carries no `headers` values or command output from either the marketplace `url` source or the plugin entry.
- **Entry sets a routing or identity header**: Claude Code drops request-routing and client-identity names such as `Host`, `Cookie`, and `X-Forwarded-*` from an entry’s `headers` and command output, and keeps authentication names such as `Authorization`. Every `marketplace.json` entry is filtered this way. For an inline plugin entry in settings, see [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces).
- **Command set in an `--add-dir` directory’s settings**: the command is ignored, on a `url` source and on an [inline plugin entry](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) alike, and only that file’s `headers` are sent.
- **Managed settings block the command**: setting [`disableCommandPluginSources`](../02-Claude-Code-CLI/settings-reference.md#disablecommandpluginsources) to `true` blocks `headersHelper` commands, and [`allowManagedHooksOnly`](../02-Claude-Code-CLI/settings-reference.md#allowmanagedhooksonly) blocks them too unless `disableCommandPluginSources` is explicitly `false`. Under either block, Claude Code still runs the command for a marketplace that managed settings themselves declare.


[​](#how-users-accept-a-headershelper-command)

How users accept a headersHelper command

A user accepts a plugin entry’s command each time they install or update that one plugin by itself. Claude Code shows the command and the archive URL, and runs the command only after the user accepts. Users can install or update the plugin inside a Claude Code session in a terminal, in their shell with no session running, or in the VS Code extension:

- **Terminal session**: from the plugin’s own view in `/plugin`.
- **Shell**: with `claude plugin install` or `claude plugin update`.
- **VS Code extension**: from the [**Manage plugins** dialog](../03-IDE-Integrations/vs-code.md#manage-plugins), with version 2.1.290 or later of the extension.

In a non-interactive shell, pass [`--yes`](plugins-cli-reference.md#plugin-install) to accept the command. To accept only the command that a previous `--json` run displayed, pass [`--accept-command`](plugins-cli-reference.md#plugin-install) with the `sha256` the run reported. Claude Code runs only the command it showed, for the archive URL it showed. If the entry’s command or archive URL changed in between, Claude Code refuses the install or update. A change in the query string alone doesn’t count, except in the VS Code extension or with `--accept-command`.


[​](#installs-and-updates-that-refuse-the-command-instead-of-asking)

Installs and updates that refuse a command instead of asking

On any operation other than a single-plugin install or update, Claude Code neither runs an entry’s command nor downloads its archive. The plugin stays at its installed version or stays uninstalled, and the user sees one of these results:

- **Installing several plugins at once, from a plugin suggestion, or as another plugin’s dependency**: Claude Code refuses the plugin that has the command and directs the user to that plugin’s own view in `/plugin`. The other plugins in a bulk install still install. A plugin that depends on the refused plugin fails to install until the user installs the refused plugin by itself.
- **Background auto-update, or session start for a plugin whose archive was never downloaded**: Claude Code lists the plugin in the `/plugin` Errors tab so the user knows to install or update it themselves.


[​](#when-a-marketplace-url-sources-command-runs)

When a marketplace `url` source’s command runs

You declare a marketplace `url` source’s `headersHelper` in a settings file, such as an [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) entry, rather than in the catalog the marketplace publishes. Claude Code therefore doesn’t ask the user to accept it on each install or update. Instead, the settings file that declares it decides when Claude Code runs it:

| Settings file                                                                 | When Claude Code runs the command                                                                                                                                                                                                                 |
|:------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| User settings, a `--settings` file, or a managed settings file on the machine | Without asking, including during a background marketplace refresh                                                                                                                                                                                 |
| A project’s `.claude/settings.json` or `.claude/settings.local.json`          | Only after the user accepts the [workspace trust dialog](../02-Claude-Code-CLI/permissions.md#what-runs-before-you-trust-a-folder) for that folder itself. A `-p` or SDK session doesn’t count as accepting it, and neither does trust granted to a parent folder |
| Server-managed settings                                                       | In an interactive session, only after the user approves the delivered settings in the [security approval dialog](../13-Enterprise-Admin/server-managed-settings.md#security-approval-dialogs)                                                                      |

For an [inline plugin entry](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) in one of these files, Claude Code requires the same folder trust or settings approval as for a marketplace-level command in that file, and the user also accepts the entry’s command on each install or update.


[​](#depend-on-and-recommend-other-plugins)

Depend on and recommend other plugins

An entry can declare dependencies on other plugins.

- **Version ranges**: a dependency can carry a semver range.
- **Cross-marketplace dependencies**: a dependency from another marketplace installs only when your marketplace lists that marketplace in `allowCrossMarketplaceDependenciesOn`.

For version ranges, the `<plugin>--v<version>` git-tag convention they resolve against, and cross-marketplace trust, see [Plugin dependencies](plugins-dependencies.md). To have Claude Code suggest a plugin when a project matches it, add a `relevance` block to the entry with the signals that identify the project. Users see suggestions from your marketplace only when an admin lists it in `pluginSuggestionMarketplaces`. For the signals and the enablement step, see [Plugin relevance](plugins-relevance.md).


[​](#work-around-what-a-marketplace-can’t-do)

Work around what a marketplace can’t do

Some things owners ask for have no field in `marketplace.json`. Here is the nearest option for each:

- **Restrict what else users install**: the marketplace allowlist is a managed setting, `strictKnownMarketplaces`. See [Restrict what users can install](plugins-org.md#restrict-what-users-can-install).
- **Install or enable a plugin without the user asking**: no entry field installs a plugin. Managed `enabledPlugins` does that for a fleet; see [Pre-install and require plugins](plugins-org.md#pre-install-and-require-plugins).
- **Show different entries to different users**: entries carry no audience field, and every user who adds the marketplace sees the whole catalog. Host separate marketplaces for separate audiences.
- **Mark a plugin deprecated**: there is no deprecation state. The option is to remove the entry, map its name to `null` in `renames`, and optionally set `forceRemoveDeletedPlugins`.
- **Turn on auto-update for your users**: each user turns it on under **Marketplaces** in `/plugin`, or an admin sets `autoUpdate` in managed settings. See [Turn on auto-update](#turn-on-auto-update).
- **Carry git credentials**: no marketplace field holds a git token. Access to a git-hosted marketplace or plugin follows the user’s git setup, per [Grant access to a private marketplace](#grant-access-to-a-private-marketplace). For `archive` sources, an entry can set [`headers` or `headersHelper`](#authenticate-archive-downloads) instead.


[​](#next-steps)

Next steps

- [Marketplace reference](plugins-marketplace-reference.md): `marketplace.json` fields, source types, and validation messages
- [Manage plugins for your organization](plugins-org.md): require, restrict, or seed your marketplace across your organization’s machines
- [Plugin dependencies](plugins-dependencies.md): tag releases so plugins that depend on yours can resolve versions
- [Troubleshoot plugins](plugins-troubleshooting.md): the errors your users see when adding or updating from your marketplace

---

## Plugin loading reference - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/loading
Path: 08-Plugins-Skills/plugins-loading.md

## On this page

- [Check which stage a plugin reached](#check-which-stage-a-plugin-reached)
  - [Plugins and marketplaces that aren’t on disk at session start](#plugins-and-marketplaces-that-aren%E2%80%99t-on-disk-at-session-start)
- [Find where a plugin came from](#find-where-a-plugin-came-from)
  - [Entry name and manifest name](#entry-name-and-manifest-name)
  - [Plugins shared through a repository](#plugins-shared-through-a-repository)
  - [Trust check and component limits for repository plugins](#trust-check-and-component-limits-for-repository-plugins)
  - [Check why a repository plugin didn’t load](#check-why-a-repository-plugin-didnt-load)
  - [Skills-directory plugins blocked by managed settings](#skills-directory-plugins-blocked-by-managed-settings)
  - [Plugins synced from claude.ai](#synced-plugins)
  - [Sync timing in cloud Cowork sessions](#sync-timing-in-cloud-cowork-sessions)
  - [Sync timing in terminal sessions](#sync-timing-in-terminal-sessions)
  - [Sign-in requirements for terminal sync](#sign-in-requirements-for-terminal-sync)
  - [Control which synced plugins load](#control-which-synced-plugins-load)
- [Find where a plugin is enabled](#find-where-a-plugin-is-enabled)
  - [Disabled in user settings but still loads](#disabled-in-user-settings-but-still-loads)
  - [Enabled in project settings but not installed](#enabled-in-project-settings-but-not-installed)
- [Find plugins on disk](#find-plugins-on-disk)
  - [In-place and copied plugins](#in-place-and-copied-plugins)
  - [Paths that escape the plugin directory](#paths-that-escape-the-plugin-directory)
  - [Cleanup of previous versions](#cleanup-of-previous-versions)
  - [Node.js package dependencies](#node-js-package-dependencies)
  - [When the dependency install runs](#when-the-dependency-install-runs)
  - [Limits on the dependency install](#limits-on-the-dependency-install)
  - [When the dependency install fails or is skipped](#when-the-dependency-install-fails-or-is-skipped)
- [Versions and updates](#versions-and-updates)
  - [How Claude Code computes the version](#how-claude-code-computes-the-version)
  - [When Claude Code refreshes a marketplace before an install](#when-claude-code-refreshes-a-marketplace-before-an-install)
  - [When auto-update runs](#when-auto-update-runs)
  - [Which marketplaces and plugins auto-update](#which-marketplaces-and-plugins-auto-update)
  - [When a command source re-runs](#when-a-command-source-re-runs)
- [Name conflicts](#name-conflicts)
  - [Hooks when two enabled plugins share a name](#hooks-when-two-enabled-plugins-share-a-name)
  - [Keep a session-only plugin from loading](#keep-a-session-only-plugin-from-loading)
- [Next steps](#next-steps)

Troubleshooting

# Plugin loading reference

Copy pageCopy page

Trace where Claude Code loads each plugin from, which settings file decides whether it loads, and why an update changed nothing.

Copy pageCopy page

Use this page when a plugin didn’t load, loaded a different copy than you expected, or didn’t pick up an update, and you want to see which source, settings scope, or file on disk decided that. It gives the rules Claude Code applies when a session starts and each time you run `/reload-plugins`. You can also ask Claude to read this page and diagnose your setup.

These cases are covered on other pages:

- **Install, enable, disable, and update steps**: see [Install and manage plugins](discover-plugins.md)
- **You have a specific error message**: see [Troubleshoot plugins](plugins-troubleshooting.md)

Start with [Check which stage a plugin reached](#check-which-stage-a-plugin-reached) for the three stages an installed plugin passes through, or go to the section that matches what you’re seeing:

- A plugin you turned off still loads: [Find where a plugin is enabled](#find-where-a-plugin-is-enabled)
- An update changed nothing: [Versions and updates](#versions-and-updates)
- You’re looking at the files under `~/.claude/plugins/`: [Find plugins on disk](#find-plugins-on-disk)
- A `--plugin-dir` plugin didn’t load, or a same-named plugin loaded instead: [Name conflicts](#name-conflicts)
- A plugin under `~/.claude/skills/` or `.claude/skills/` didn’t load, and no error says why: [Skills-directory plugins blocked by managed settings](#skills-directory-plugins-blocked-by-managed-settings)


[​](#check-which-stage-a-plugin-reached)

Check which stage a plugin reached

An `enabledPlugins` entry becomes a plugin you can use in stages: your settings declare it, Claude Code fetches it to disk, and the running session loads it. When a plugin doesn’t behave as a settings file suggests, check which stage it reached:

- **Declared, in settings**: `enabledPlugins` says which plugins should be on, and `extraKnownMarketplaces` says which marketplaces should exist. When you run `claude plugin marketplace add`, Claude Code writes the marketplace to `extraKnownMarketplaces` in your user settings as well as to disk
- **Fetched, on disk under `~/.claude/plugins/`**: the records of what Claude Code has fetched, and the fetched files themselves:
  - `known_marketplaces.json` records each marketplace Claude Code has fetched, with its `source`, `installLocation`, `lastUpdated`, and `autoUpdate`. There is one `known_marketplaces.json` per user, so a marketplace you add in one project is available in every project
  - `installed_plugins.json` records each install with its `scope`, `installPath`, and `version`
  - `cache/` holds the plugin files
- **Loaded, in the running session**: the plugin set Claude Code loaded at startup or at the last `/reload-plugins`. Changes to settings or to disk don’t reach this layer until you run `/reload-plugins` or start a new session. That is why `claude plugin update` ends with `Restart to apply changes.` and background updates prompt you with `Run /reload-plugins to apply`


[​](#plugins-and-marketplaces-that-aren’t-on-disk-at-session-start)

Plugins and marketplaces that aren’t on disk at session start

Plugins load at session start from `installed_plugins.json` and the cache without using the network. After the session starts, Claude Code checks the declared marketplaces in the background:

- **A marketplace that settings declare but `known_marketplaces.json` lacks**: Claude Code clones it, then reloads plugins and downloads enabled plugins that aren’t cached yet
- **A declared marketplace whose source changed in settings**: Claude Code re-fetches it from the new source and shows `Plugins changed. Run /reload-plugins to activate.`

An enabled plugin that neither path fetched and that has no usable cache directory shows `Plugin "<name>" not cached at <path>` in the `/plugin` **Errors** tab, and `claude plugin list` adds `— run /plugin to refresh` to the same line. For the fix, see [`Plugin "<name>" not cached at <path>`](plugins-troubleshooting.md#plugin-not-cached-at).


[​](#find-where-a-plugin-came-from)

Find where a plugin came from

Every plugin has an id of the form `<name>@<origin>`, which is what you see in settings files and in `claude plugin list --json`. The part after `@` tells you where Claude Code found the plugin:

| ID ends in       | How the plugin got there                                                                                                                                                                                     | How you turn it on or off                                                                                                                                           |
|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `@<marketplace>` | You installed it from a marketplace you added                                                                                                                                                                | `"<name>@<marketplace>": true` or `false` under `enabledPlugins` in a settings file                                                                                 |
| `@inline`        | You started Claude Code with `--plugin-dir` or `--plugin-url`, set [`CLAUDE_CODE_PLUGIN_DIRS`](../02-Claude-Code-CLI/env-vars.md#variables), or an Agent SDK app passed the `plugins` option. It loads for that session only | On for the session unless the manifest sets `defaultEnabled: false` or a settings file sets `"<name>@inline": false`                                                |
| `@skills-dir`    | You saved a plugin directory that has a `.claude-plugin/plugin.json` under `~/.claude/skills/` or the project’s `.claude/skills/`                                                                            | The manifest’s `defaultEnabled`, unless a settings file sets `"<name>@skills-dir"` to `true` or `false`                                                             |
| `@synced`        | You or your organization turned it on for your claude.ai account, and Claude Code [downloaded it](#synced-plugins)                                                                                           | On unless the manifest sets `defaultEnabled: false` or a settings file sets `"<name>@synced": false`. A plugin your organization marks as required loads regardless |

For a marketplace plugin, `<name>` is the entry name in `marketplace.json`; for `@inline` and `@skills-dir` it’s the `name` in the plugin’s manifest. The origin names in this table are reserved, so no marketplace can be named `inline`, `skills-dir`, or `synced`.


[​](#entry-name-and-manifest-name)

Entry name and manifest name

A marketplace plugin has two names, and they can differ:

- **The entry name in `marketplace.json`**: the install and enable key. It’s what you write in `enabledPlugins`, what the cache directory is named after, and what `claude plugin list` shows
- **The `name` in the manifest**: what the plugin’s components are namespaced under, and what [name conflicts](#name-conflicts) compare


[​](#plugins-shared-through-a-repository)

Plugins shared through a repository

To share a plugin through a repository, list it under `enabledPlugins` in `.claude/settings.json` or place it under `.claude/skills/`. Claude Code doesn’t scan a project’s `.claude/plugins/` directory. A cloud session doesn’t add the marketplaces a repository lists under [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces), because that requires the workspace trust dialog, which a cloud session never shows.


[​](#trust-check-and-component-limits-for-repository-plugins)

Trust check and component limits for repository plugins

A project-scope plugin is checked into the repository and reaches every collaborator who clones it. Because that content comes from the repository rather than from you, it loads only after the same trust check that applies to project allow rules in `.claude/settings.json`. Trusting a parent folder or running with `-p` isn’t enough. Components that run code are restricted further:

- MCP servers it declares go through the [same per-server approval](../06-MCP-Tools/General/mcp.md) as a project `.mcp.json`
- MCP servers it declares as an [MCP bundle](plugins-reference.md#mcpservers), a `.mcpb` or `.dxt` file, or from a file outside the plugin directory are skipped. Declare them inline or in a `.mcp.json` inside the plugin directory
- [Background monitors](plugins-components.md#monitors) do not load

Neither the trust check nor these component limits apply to a plugin under your own `~/.claude/skills/`. For how to write `--plugin-dir` and skills-directory plugins, see [Create plugins](plugins-create.md).


[​](#check-why-a-repository-plugin-didnt-load)

Check why a repository plugin didn’t load

If a plugin in your repository’s `.claude/skills/` doesn’t load, check for these causes:

- **In a subdirectory**: a plugin at the repository root doesn’t load. Claude Code reads the `.claude/skills/` of the session’s [primary working directory](../02-Claude-Code-CLI/permissions.md#working-directories) and, unlike plain skills and commands, doesn’t [search parent directories](skills.md#discovery-from-parent-and-nested-directories) for plugins. Launch from the repository root instead, or [move the session there with `/cd`](../02-Claude-Code-CLI/permissions.md#move-the-session-to-another-directory) on v2.1.246 or later
- **From the desktop app, in a worktree**: the plugin loads from the main checkout’s `.claude/skills/` rather than the worktree’s. See [What worktrees share with the main checkout](../02-Claude-Code-CLI/worktrees.md#what-worktrees-share-with-the-main-checkout)
- **In a folder you haven’t trusted**: the plugin loads only after you accept the [workspace trust dialog](../02-Claude-Code-CLI/permissions.md#what-runs-before-you-trust-a-folder) for that folder
- **In an organization that manages Claude Code**: see [Skills-directory plugins blocked by managed settings](#skills-directory-plugins-blocked-by-managed-settings)


[​](#skills-directory-plugins-blocked-by-managed-settings)

Skills-directory plugins blocked by managed settings

If a plugin folder in `~/.claude/skills/` or a project’s `.claude/skills/` didn’t load and you see no error, your administrator may have blocked it through [managed settings](../13-Enterprise-Admin/managed-settings.md). These folders come from you or your repository rather than from a marketplace, so Claude Code skips them when your administrator sets one of these restrictions:

- **Customization locked to plugins**: [`strictPluginOnlyCustomization`](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization) set to `true` or to an array naming one or more kinds of customization to lock. The whole plugin is skipped when any one kind is locked, such as `mcp`, whichever components it has.
- **Marketplace sources restricted**: a [`strictKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#strictknownmarketplaces) allowlist without a `{ "source": "skills-dir" }` entry, or a [`blockedMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#blockedmarketplaces) list with one.

`/plugin` and `claude plugin list` show no plugin and no error for a skipped folder. Its `/<name>:<skill>` commands and agents are missing, as are its hooks and MCP servers. Unless skills are among the locked kinds, a `SKILL.md` at the folder’s root still loads as a plain skill. In that case `/<name>` keeps working, and the `/plugin` **Installed** tab lists that skill under **Skills**.

- **Confirm the cause**: run `/status` in a Claude Code session. If the `Setting sources` line lists `Enterprise managed settings`, ask your administrator whether one of these settings applies to you. If it doesn’t, see [Check why a repository plugin didn’t load](#check-why-a-repository-plugin-didnt-load) for other causes.
- **Load the whole plugin**: add it to a [marketplace](plugin-marketplaces.md#create-a-marketplace) your organization allows and install it from there. To load it for one session instead, start `claude` from your shell with [`--plugin-dir`](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session) and the folder’s path, which works unless your administrator has also turned off that flag.


[​](#synced-plugins)

Plugins synced from claude.ai

A plugin you turn on for your claude.ai account also loads in Claude Code, alongside the plugins you install from marketplaces. That includes plugins your organization turns on for its members. Each of these plugins loads as `<name>@synced`, with no marketplace and no [install record](#check-which-stage-a-plugin-reached). In terminal sessions, a synced plugin’s skills, agents, hooks, MCP servers, and LSP servers all load, with the same trust as a marketplace plugin you installed. For the components Cowork loads, see the [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app) on claude.com. Synced plugins load in Cowork sessions and in terminal sessions where you sign in with your claude.ai account:

- **[Cowork](../15-Claude-AI-Features/product-cowork.md)**: Claude Code downloads them into the session’s own environment when the session starts
- **Terminal sessions**: each time you start Claude Code, it syncs once in the background, downloading new and updated plugins and removing the ones that you or your organization turned off. Syncing in terminal sessions requires Claude Code v2.1.273 or later


[​](#sync-timing-in-cloud-cowork-sessions)

Sync timing in cloud Cowork sessions

If you turn a plugin on or off for your claude.ai account during a Cowork session that runs in the cloud, keep working in that session. If nothing has changed after a few messages, start a new session.


[​](#sync-timing-in-terminal-sessions)

Sync timing in terminal sessions

Because the terminal sync runs in the background, it can finish after your session has started. When it adds, updates, or removes a synced plugin in an interactive session, you see `Plugins changed. Run /reload-plugins to activate.` Run `/reload-plugins` to load the change in that session, or leave it for the next time you start Claude Code. If you turn a plugin on or off for your claude.ai account during a terminal session, Claude Code downloads or removes it the next time you start Claude Code.


[​](#sign-in-requirements-for-terminal-sync)

Sign-in requirements for terminal sync

In your terminal, plugins sync only in sessions where you sign in with your claude.ai account. Claude Code neither downloads nor loads synced plugins in these terminal sessions, even after you sign in with `/login`:

- A session where `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, or an `apiKeyHelper` script supplies the credential in place of that sign-in
- A session that doesn’t [fetch feature flags from Anthropic](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching), such as one where you set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
- A session in [bare mode](../02-Claude-Code-CLI/headless.md#start-faster-with-bare-mode) or one you start with `--safe-mode`
- A session you start with a [`--setting-sources`](../02-Claude-Code-CLI/cli-reference.md#cli-flags) list that leaves out `user`

If you signed in on an earlier version of Claude Code, that sign-in doesn’t cover plugins until Claude Code renews it in the background. To get access sooner, run `/login` again. Plugin sync then starts the next time you start Claude Code.


[​](#control-which-synced-plugins-load)

Control which synced plugins load

You can turn synced plugins off one at a time, except a plugin your organization requires, or turn off every synced plugin on the machine:

- **One plugin**: `claude plugin disable <name>@synced` in your shell and the `/plugin` **Installed** tab in a session both save `"<name>@synced": false` in your user-level [`enabledPlugins`](../02-Claude-Code-CLI/settings-reference.md#enabledplugins). To keep the plugin out of a project in every environment, set the same key in the project’s committed `.claude/settings.json`
- **Every synced plugin on a machine**: set [`syncClaudeAiPlugins`](../02-Claude-Code-CLI/settings-reference.md#syncclaudeaiplugins) to `false` in your user settings, or your organization sets it in [managed settings](../13-Enterprise-Admin/managed-settings.md). Claude Code stops downloading, and the next time you start it, it moves the plugins it already synced to `~/.claude/plugins/.trash/` and no longer loads them. If your organization turns off Skills on claude.ai, plugins stop syncing too
- **A plugin your organization requires**: a plugin that your organization marks as required on claude.ai loads even if you disabled it earlier. `claude plugin disable` refuses it with `Plugin "<name>@synced" is required by your organization and can't be disabled here. Contact your admin to change it.`, and `claude plugin list` marks it `required by your org`

For removing a plugin on claude.ai, see [Manage installed plugins](discover-plugins.md#manage-installed-plugins).


[​](#find-where-a-plugin-is-enabled)

Find where a plugin is enabled

You can set an `enabledPlugins` entry in any of six sources. The table lists them from lowest precedence to highest, and who each one applies to. For the settings files themselves, see [Settings files and who they affect](../02-Claude-Code-CLI/settings.md#where-settings-live).

| Source      | Where you set it                                                                                  | Reaches                                                                                                   |
|:------------|:--------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------|
| `--add-dir` | `.claude/settings.json` or `.claude/settings.local.json` in a directory you pass with `--add-dir` | This session only. Only a `true` value has an effect, and every other source overrides it                 |
| `user`      | `~/.claude/settings.json`                                                                         | You, in every project                                                                                     |
| `project`   | `.claude/settings.json`                                                                           | Everyone who clones the repository                                                                        |
| `local`     | `.claude/settings.local.json`                                                                     | You, in this repository only                                                                              |
| `flag`      | The `--settings` value you pass at launch                                                         | This session only                                                                                         |
| `managed`   | [Managed settings](../13-Enterprise-Admin/managed-settings.md)                                                     | Every user the policy covers. `true` force-enables and `false` blocks, and no other source overrides them |

These sources merge key by key. For each plugin id, the value that applies is the one from the highest-precedence source that mentions the id. A source that doesn’t mention the id leaves the value from the lower-precedence source in effect.


[​](#disabled-in-user-settings-but-still-loads)

Disabled in user settings but still loads

If you set a plugin to `false` in `~/.claude/settings.json` and it still loads, a `true` in a higher-precedence source is overriding it. The plugin’s row in `claude plugin list` and in `/plugin` shows `Disabled in ~/.claude/settings.json but still loads — project settings enable it, which overrides your user setting`. The message names the source that overrode you: `project`, `project, gitignored` for `.claude/settings.local.json`, `cli flag`, or `managed`. To opt out of a project-enabled plugin on your machine, set the id to `false` in `.claude/settings.local.json`, which has higher precedence than the project file.


[​](#enabled-in-project-settings-but-not-installed)

Enabled in project settings but not installed

When a plugin’s only `true` is in the project’s `.claude/settings.json`, Claude Code doesn’t fetch it onto a machine where it isn’t installed, unless its marketplace entry has a [relative-path source](plugins-marketplace-reference.md#plugin-sources) or a [seed directory](plugins-org.md#seed-containers-and-ci) already holds it. Instead, the `/plugin` **Errors** tab shows `Plugin "<name>" is enabled in project settings but isn't installed here`. A relative-path plugin needs no install record because it loads from the marketplace itself. Claude Code fetches a plugin with an external source only when one of these sources sets it to `true`:

- Your user settings
- A `.claude/settings.local.json` that git doesn’t track
- The `--settings` flag
- Managed settings


[​](#find-plugins-on-disk)

Find plugins on disk

Claude Code keeps plugin files and state records under one plugins root, which is `~/.claude/plugins` unless you set [`CLAUDE_CODE_PLUGIN_CACHE_DIR`](../02-Claude-Code-CLI/env-vars.md). Every path in the table is relative to that root.

| Path                                                                                                   | What it holds                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
|:-------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `cache/<marketplace>/<plugin>/<version>/`                                                              | One directory per installed version of a marketplace plugin. `<plugin>` is the marketplace entry name and `<version>` is the [resolved version](#versions-and-updates). `${CLAUDE_PLUGIN_ROOT}` points at this directory                                                                                                                                                                                                                                                                                                        |
| `data/<plugin-id>/`                                                                                    | The plugin’s persistent directory, exposed as `${CLAUDE_PLUGIN_DATA}`. For how `<plugin-id>` is formed, see [Path variables and persistent data](plugins-components.md#path-variables-and-persistent-data). Claude Code creates it when a plugin component first uses it and keeps it across updates. By default, Claude Code deletes it when you uninstall the plugin from its last scope. For `--keep-data` and the other cases where it stays, see [plugin uninstall](plugins-cli-reference.md#plugin-uninstall) |
| `marketplaces/<name>/`                                                                                 | The clone or download of a marketplace added from GitHub, another Git host, or a URL. A marketplace added from a local `file` or `directory` source has no copy here, and its `installLocation` in `known_marketplaces.json` is the path you gave                                                                                                                                                                                                                                                                               |
| `synced/`                                                                                              | The plugins Claude Code [synced from your claude.ai account](#synced-plugins)                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `.trash/`                                                                                              | Plugins that the claude.ai sync removed, such as after you turn one off on claude.ai or stop syncing                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `installed_plugins.json` and `known_marketplaces.json`                                                 | The records of what Claude Code has installed and which marketplaces it has fetched, described under [Check which stage a plugin reached](#check-which-stage-a-plugin-reached). A [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai) is recorded in `known_marketplaces_claudeai.json` instead                                                                                                                                                                                                      |
| `flagged-plugins.json`                                                                                 | Plugins Claude Code uninstalled because their marketplace delisted them. They appear in the **Flagged** section of `/plugin`; see [Host a marketplace](plugins-host-marketplace.md)                                                                                                                                                                                                                                                                                                                                       |
| `installed_plugins.set-aside.<date>.<hash>.json` and `installed_plugins.unreadable.<date>.<hash>.kept` | Dated copies Claude Code keeps before it drops install records that no version of Claude Code can use or rebuilds an unreadable `installed_plugins.json`. See [the recovery notes](plugins-troubleshooting.md#installed-plugins-json-could-not-be-read-and-was-rebuilt). They age out on the [`cleanupPeriodDays`](../02-Claude-Code-CLI/settings-reference.md#cleanupperioddays) schedule                                                                                                                                                |

Because `${CLAUDE_PLUGIN_ROOT}` points at a version directory, a plugin’s root path changes with every version. Keep a plugin’s durable files in `${CLAUDE_PLUGIN_DATA}` instead.


[​](#in-place-and-copied-plugins)

In-place and copied plugins

Claude Code loads some plugins in place from where you keep them and copies the rest into the cache, according to their origin:

- **`--plugin-dir` and skills-directory plugins**: the directory loads in place and is never copied. A `--plugin-url` archive or a `--plugin-dir` `.zip` is extracted into a session temp directory first
- **Relative-path plugins in a marketplace you added from a local path**: the plugin loads in place from its path inside the marketplace folder. Your edits to the source directory take effect at the next session start or `/reload-plugins`, and you don’t need to increase the version. The plugin’s hook processes and MCP and LSP servers receive a `CLAUDE_PLUGIN_ROOT` that points at the source directory. For its Node.js package dependencies, see [When the dependency install runs](#when-the-dependency-install-runs)
- **`command`-source plugins in [link mode](plugins-marketplace-reference.md#command-plugin-source)**: the directory the command printed loads in place, through links in the cache entry
- **Every other marketplace plugin**: Claude Code copies the plugin into `cache/<marketplace>/<plugin>/<version>/` at install and loads that copy. Files outside the plugin directory aren’t copied, so when a script inside a copied plugin reads a path above the plugin root, such as `../shared`, it doesn’t find them


[​](#paths-that-escape-the-plugin-directory)

Paths that escape the plugin directory

Whether a plugin loads in place or from a cached copy, Claude Code doesn’t let it declare components outside its own directory. It rejects a component path that resolves outside the plugin root, whether the path is declared in `plugin.json` or in a marketplace entry:

- **A path that points outside the plugin as written**, such as `../shared-utils`
- **A symlink that leads outside the plugin**, other than [links between plugins within one marketplace](plugins-host-marketplace.md#share-files-within-a-marketplace-with-symlinks)
- **On macOS and Linux, a path that contains a backslash anywhere in it**, even when the path stays inside the plugin. Components declared with backslash paths therefore load on Windows only, so write component paths with forward slashes, such as `./commands/deploy.md`

A rejected path appears as a [`path escapes plugin directory`](../02-Claude-Code-CLI/errors.md#path-escapes-plugin-directory) error, and the plugin loads without that component.


[​](#cleanup-of-previous-versions)

Cleanup of previous versions

When you update or uninstall a plugin, Claude Code writes an `.orphaned_at` marker into the previous version directory. It removes that directory in a background cleanup 14 days later, so a session that already loaded the old version keeps running. The sweep runs only while `installed_plugins.json` records at least one install. After you uninstall your last plugin, orphaned directories stay until you install another.


[​](#node-js-package-dependencies)

Node.js package dependencies

When Claude Code copies a plugin into the cache, it also installs the plugin’s Node.js package dependencies there, so the plugin’s hooks and MCP servers can load them. This section covers the npm and Bun packages a plugin declares in its own `package.json`. For plugins that depend on other plugins, see [plugin dependency versions](plugins-dependencies.md).


[​](#when-the-dependency-install-runs)

When the dependency install runs

Claude Code installs the dependencies into the copied version directory each time it creates one:

- When you install a plugin
- When Claude Code updates a plugin to a new version
- At session start when an enabled plugin isn’t cached yet, such as on a new machine

For a relative-path plugin [loaded in place](#in-place-and-copied-plugins) from a marketplace you added from a local path, Claude Code doesn’t install the dependencies into the source directory. Install them there yourself, or from a hook into [`${CLAUDE_PLUGIN_DATA}`](plugins-components.md#path-variables-and-persistent-data). The install runs only when the plugin’s root directory contains both a `package.json` and a supported lockfile. The lockfile decides which package manager Claude Code runs:

| Lockfile                                     | Package manager |
|:---------------------------------------------|:----------------|
| `bun.lock`                                   | Bun             |
| `npm-shrinkwrap.json` or `package-lock.json` | npm             |

If a plugin contains more than one of these lockfiles, Claude Code uses the first match, checking in order: `bun.lock`, `npm-shrinkwrap.json`, `package-lock.json`. Claude Code skips the install in these lockfile cases:

- **`bun.lockb`**: Bun’s binary lockfile can’t be checked. Ship a text `bun.lock` or an npm lockfile instead
- **`yarn.lock` or `pnpm-lock.yaml`**: replace it with an npm lockfile
- **A lockfile in a format Claude Code doesn’t read**: an npm lockfile needs a `lockfileVersion` of `2` or `3`, which npm 7 or later writes, and a `bun.lock` needs a `lockfileVersion` no higher than `2`

Include an npm lockfile to reach the most users. Claude Code runs the matched lockfile’s package manager from the user’s PATH and doesn’t try the other lockfile instead if that package manager is missing. For a plugin distributed through an npm source, use `npm-shrinkwrap.json`, because npm excludes `package-lock.json` from published packages.


[​](#limits-on-the-dependency-install)

Limits on the dependency install

Claude Code constrains this dependency install so that no code from the plugin or its packages executes during it, and bounds how long it can run:

- **Registry packages only**: every dependency must be a registry package pinned in the lockfile to an exact version. A plugin with a git, GitHub, folder, workspace, or linked dependency gets no install.
- **`https` downloads**: a download link in the lockfile must use `https`, unless it points at the installing user’s own default npm registry.
- **A separate install folder**: the package manager runs in a folder of its own that holds only a copy of the checked dependency list, so npm and Bun don’t read the plugin’s `.npmrc`, `.env`, or `bunfig.toml`. When the install succeeds, Claude Code moves the resulting `node_modules` into the plugin.
- **Frozen resolution**: the install uses exactly the versions the lockfile pins, and Claude Code skips it when `package.json` and the lockfile don’t list the same dependencies
- **No lifecycle scripts**: `--ignore-scripts` keeps `preinstall`, `install`, and `postinstall` scripts from running, so dependencies that build native modules in those scripts download but don’t compile during this install
- **No overrides or patches**: a plugin whose `package.json` sets npm `overrides` gets no install from an npm lockfile, and a plugin that sets Bun `patchedDependencies` gets no install from `bun.lock`
- **60-second timeout**: Claude Code stops an install that runs longer and treats it as failed

Claude Code fetches an npm-source plugin before this dependency install, and none of the package’s own install scripts run during the fetch. See [npm plugin source](plugins-marketplace-reference.md#npm-plugin-source). You can’t turn the automatic install off. No setting or environment variable disables it. In restricted networks, see the [network access requirements](../13-Enterprise-Admin/network-config.md#network-access-requirements) for the hosts to allow.


[​](#when-the-dependency-install-fails-or-is-skipped)

When the dependency install fails or is skipped

If the install fails or is skipped, the plugin still loads, but the parts of it that need the missing packages may not work. `/plugin` and `claude plugin list` show a note on an enabled plugin whose cached copy has a lockfile and a `package.json` that lists runtime dependencies, but no `node_modules` directory. The note says whether the install didn’t finish or can’t run for this plugin. See [the troubleshooting entry](plugins-troubleshooting.md#the-packages-it-lists-are-not-installed) for what to do about each. When the automatic install can’t provide a dependency, install it from a hook into the [persistent data directory](plugins-components.md#path-variables-and-persistent-data). That includes packages that need their lifecycle scripts to build, Python dependencies, plugins locked with Yarn or pnpm, and dependencies that aren’t registry packages, such as git dependencies.


[​](#versions-and-updates)

Versions and updates

If a plugin’s author pushed new commits and `claude plugin update` prints `<name> is already at the latest version (<version>).`, the version Claude Code computes for the plugin is unchanged, so the plugin’s files on disk don’t change. Claude Code computes a version for every plugin it installs, and that version is how it detects an update. `claude plugin update` and background auto-update compute the version again and don’t replace the cached copy when it matches what `installed_plugins.json` records. An update that you start can still [retry an unfinished dependency install](plugins-troubleshooting.md#the-packages-it-lists-are-not-installed) in that copy. The version also names the plugin’s cache directory. A manifest that pins `"version"` is one way the computed version stays the same across commits. See [How Claude Code computes the version](#how-claude-code-computes-the-version) for the resolution order. A plugin [loaded in place](#in-place-and-copied-plugins) from a marketplace you added from a local path loads its current source files at every session start, whatever its version string says. For a plugin from a [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai), the version claude.ai records for the plugin is its version, and the manifest’s `version` isn’t read.


[​](#how-claude-code-computes-the-version)

How Claude Code computes the version

For a marketplace you added by source, Claude Code picks the rule by the `source` type of the plugin’s marketplace entry. The [marketplace reference](plugins-marketplace-reference.md#plugin-sources) lists the source types. For every source type in that list except `command`:

1.  The `version` field in the plugin’s manifest comes first
2.  Then the `version` field in the plugin’s marketplace entry
3.  When neither is set, the version comes from the source type:

| Source type                                                                                | Version when no `version` field is set                                                                                                               |
|:-------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------|
| `github`, `url`, or `git-subdir`                                                           | The commit SHA of the source, shortened to 12 characters. A `git-subdir` version also carries a hash of the subdirectory path                        |
| `archive`                                                                                  | The SHA-256 digest, shortened to 12 characters: the `sha256` pin in the marketplace entry, or the digest of the downloaded file when there is no pin |
| Relative path inside a Git-hosted marketplace                                              | The commit SHA of the installed directory                                                                                                            |
| Local directory, when neither the plugin directory nor its marketplace is a git repository | `unknown`                                                                                                                                            |
| `npm`                                                                                      | `unknown`                                                                                                                                            |

Claude Code doesn’t take the version from a repository that encloses the install path, such as a git-managed `~/.claude`. For a `command` source, Claude Code always derives the version from what the command produced: a 12-character hash on its own, or `<manifest version>-<hash>` when the manifest sets one. The marketplace entry’s `version` is ignored for command sources. For what the hash covers, see [Copy mode and link mode](plugins-marketplace-reference.md#copy-mode-and-link-mode). Because the manifest comes first, a manifest that pins `"version": "1.0.0"` keeps every user on the cached copy until its author changes the string, however many commits they push. To let users track commits instead, leave `version` out of both the manifest and the entry. [Host a marketplace](plugins-host-marketplace.md) covers which choice fits which release setup.


[​](#when-claude-code-refreshes-a-marketplace-before-an-install)

When Claude Code refreshes a marketplace before an install

When you install a plugin, Claude Code looks it up in its local copy of the marketplace catalog. You can run `/plugin install` in a session or `claude plugin install` in your shell, and name the plugin with or without its marketplace. The table shows which of those combinations refresh the local copy.

| Plugin name        | Command                                      | What Claude Code refreshes                                                   |
|:-------------------|:---------------------------------------------|:-----------------------------------------------------------------------------|
| `name@marketplace` | `/plugin install` or `claude plugin install` | The named marketplace, before the lookup                                     |
| `name` alone       | `/plugin install`                            | Only marketplaces that have auto-update on, and only after the lookup misses |
| `name` alone       | `claude plugin install`                      | Nothing. It reads the cached catalogs without refreshing                     |

The refresh before a `name@marketplace` install doesn’t depend on the marketplace’s auto-update setting or on `DISABLE_AUTOUPDATER`. When the refresh fails, the install proceeds from the cached catalog and `claude plugin install` reports `marketplace not refreshed`. Claude Code skips the refresh before a `name@marketplace` install when:

- The marketplace was added from a local `file` or `directory` source, or is defined inline in settings with a [`settings` source](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces)
- A [seed directory](../02-Claude-Code-CLI/env-vars.md) supplies the marketplace
- Claude Code refreshed the marketplace within the last 30 seconds
- You set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
- [Managed settings](plugins-org.md#restrict-what-users-can-install) block the marketplace, in which case Claude Code also refuses the install


[​](#when-auto-update-runs)

When auto-update runs

In an interactive session, after you send your first message, Claude Code waits a random delay of up to ten minutes. It then refreshes every marketplace with auto-update on and updates the plugins installed from them on disk. The running session keeps the versions it loaded, and you see `Plugin updated: <name> · Run /reload-plugins to apply`. Whether or not you reload, the new versions load on your next launch.


[​](#which-marketplaces-and-plugins-auto-update)

Which marketplaces and plugins auto-update

Whether a marketplace auto-updates follows the first of these that is set:

1.  **`autoUpdate` on its `extraKnownMarketplaces` entry** in a settings file
2.  **`autoUpdate` on its `known_marketplaces.json` entry**, which the **Enable auto-update** toggle under `/plugin` **Marketplaces** writes. When a settings file also declares the marketplace under `extraKnownMarketplaces`, the toggle writes `autoUpdate` to that settings entry as well
3.  **The default**: on for Anthropic’s official marketplaces such as `claude-plugins-official`, off for `knowledge-work-plugins` and `first-party-plugins`, on for [marketplaces added from claude.ai](discover-plugins.md#add-from-claude-ai), and off for every other marketplace

If you set `DISABLE_UPDATES=1`, `DISABLE_AUTOUPDATER=1`, or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`, the whole pass is off and the **Enable auto-update** toggle is hidden, unless you also set `FORCE_AUTOUPDATE_PLUGINS=1`. The [environment variables reference](../02-Claude-Code-CLI/env-vars.md) covers each variable’s wider effect. Auto-update also skips a plugin whose marketplace entry declares a `headersHelper`. [Installs and updates that refuse a command instead of asking](plugins-host-marketplace.md#installs-and-updates-that-refuse-the-command-instead-of-asking) explains when such a plugin appears in the `/plugin` **Errors** tab and how you update it from there. When a copied plugin updates mid-session, hook commands, monitors, MCP servers, and LSP servers keep using the previous version’s path. Run `/reload-plugins` to switch hooks, MCP servers, and LSP servers to the new path. Monitors require a session restart.


[​](#when-a-command-source-re-runs)

When a command source re-runs

Plugins with a `command` source don’t wait for the [auto-update pass](#when-auto-update-runs). The printed directory reflects the tool’s state at the time the command ran, so Claude Code runs the [command you accepted](plugins-host-marketplace.md#change-the-command-of-a-command-source) again at these times:

- Every time you install or update the plugin
- Once per session for each enabled command-sourced plugin, in the background, shortly after the session starts. This run doesn’t depend on the marketplace’s auto-update setting or on `DISABLE_AUTOUPDATER`
- At startup or on `/reload-plugins`, when an enabled plugin’s installed version is missing from the plugin cache

Claude Code skips the two background runs when you set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](../02-Claude-Code-CLI/env-vars.md). Explicit installs and updates still run the command with that variable set. When the command’s hashed output has changed, Claude Code installs the result as a new version and reloads it in the running interactive session, switching [the same components that `/reload-plugins` switches](plugins-cli-reference.md#reload-plugins). You see a notification that the plugin was reloaded. If reloading in place would invalidate the session’s prompt cache, Claude Code instead prompts you to run `/reload-plugins`, which [warns about the cache cost and applies when rerun with `--force`](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin).


[​](#name-conflicts)

Name conflicts

When enabled plugins from different origins share a manifest name, this order decides which one loads, from highest precedence to lowest:

1.  A plugin whose id appears in managed settings `enabledPlugins`, as `true` or `false`. A `--plugin-dir` copy whose manifest name matches the id’s name part isn’t loaded, and you see `--plugin-dir copy of "<name>" ignored: plugin is locked by managed settings`
2.  An enabled `--plugin-dir`, `--plugin-url`, or `CLAUDE_CODE_PLUGIN_DIRS` plugin. It replaces a same-named installed marketplace plugin or skills-directory plugin:
    - **An installed marketplace plugin**: replaced silently. `claude plugin list` still shows the marketplace row as enabled, because that row reflects your settings. Only the log Claude Code writes under `~/.claude/debug/` when you start with `--debug` records `Plugin "<name>" from --plugin-dir overrides installed version`
    - **A skills-directory plugin**: replaced with a `/plugin` **Errors** tab row that reads `Not loaded — the name "<name>" is already taken by a session-only plugin (--plugin-dir / --plugin-url), which takes precedence`
3.  An installed marketplace plugin. A skills-directory plugin of the same name gets the same `Not loaded` row, naming the installed plugin
4.  A skills-directory plugin. Between two of these, the copy under `~/.claude/skills/` loads and the project’s `.claude/skills/` copy is dropped, with a row that says which path shadowed it
5.  A plugin [synced from claude.ai](#synced-plugins). When an enabled plugin from any other origin matches its name, Claude Code loads that plugin and reports the synced copy as not loaded. To use the claude.ai copy instead, disable your own copy

Because the order compares manifest names, a `--plugin-dir` plugin named `hello-plugin` replaces `hello@example-marketplace` when that plugin’s manifest also says `"name": "hello-plugin"`.


[​](#hooks-when-two-enabled-plugins-share-a-name)

Hooks when two enabled plugins share a name

When you install and enable two plugins with the same manifest name from different marketplaces, both appear as enabled in `/plugin`, but the hooks of one of them are left out. One plugin per name registers the hooks in its `hooks/hooks.json`, and one plugin per name loads a [hooks module](plugins-mods-overview.md). When your organization’s managed settings turn on one of the copies, that copy holds the name. Otherwise the copy Claude Code loads first holds it. To see which copy holds the name, run `/plugin` in your session and open the **Errors** tab. A note there for the copy whose hooks were left out names the copy that holds the name, and the left-out copy’s details show the same note. For `hooks/hooks.json` hooks the note begins `Its hooks.json hooks do not run`, and for a hooks module it begins `Its hooks module does not load`. The note requires Claude Code v2.1.296 or later. To run the left-out copy’s hooks instead, disable or uninstall the copy that holds the name, then run `/reload-plugins` in your session. The reload registers the remaining copy’s hooks and clears the note. When the copy that holds the name is one your managed settings turn on, you can’t disable it, and the other copy’s hooks stay off while both are installed.


[​](#keep-a-session-only-plugin-from-loading)

Keep a session-only plugin from loading

To keep a `--plugin-dir` plugin from shadowing anything, or to turn one off when a parent process passes the flag for you, set its id to `false` in any settings file. For a plugin whose manifest name is `hello-plugin`, the entry is `"enabledPlugins": {"hello-plugin@inline": false}`. A disabled session-only plugin doesn’t shadow, so the marketplace or skills-directory copy loads instead.


[​](#next-steps)

Next steps

- [Install and manage plugins](discover-plugins.md): the install, enable, disable, and update steps themselves
- [Troubleshoot plugins](plugins-troubleshooting.md): error messages by the stage that produces them
- [Plugin commands reference](plugins-cli-reference.md): the flags and commands named on this page
- [Manage plugins for your organization](plugins-org.md): the managed settings that force-enable or block plugins

---

## Marketplace reference - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/marketplace-reference
Path: 08-Plugins-Skills/plugins-marketplace-reference.md

## On this page

- [Marketplace file](#marketplace-file)
  - [Reserved names](#reserved-names)
- [Top-level fields](#top-level-fields)
- [Plugin entries](#plugin-entries)
  - [How an entry combines with plugin.json](#entry-and-plugin-json)
  - [Hooks in an entry](#hooks-in-an-entry)
  - [Display fields](#display-fields)
  - [Strict mode](#strict-mode)
- [Plugin sources](#plugin-sources)
  - [Relative path plugin source](#relative-path-plugin-source)
  - [Bare names under pluginRoot](#bare-names-under-pluginroot)
  - [github plugin source](#github-plugin-source)
  - [url plugin source](#url-plugin-source)
  - [git-subdir plugin source](#git-subdir-plugin-source)
  - [npm plugin source](#npm-plugin-source)
  - [archive plugin source](#archive-plugin-source)
  - [command plugin source](#command-plugin-source)
  - [What the command must do](#what-the-command-must-do)
  - [Output that fails the install or update](#output-that-fails-the-install-or-update)
  - [Copy mode and link mode](#copy-mode-and-link-mode)
- [Marketplace sources](#marketplace-sources)
  - [Fields by type](#fields-by-type)
  - [Source values valid only in policy lists](#source-values-valid-only-in-policy-lists)
  - [Source objects in settings](#source-objects-in-settings)
- [Validation messages](#validation-messages)
  - [Invalid input on a source](#invalid-input-on-a-source)
  - [Failures that validation doesn’t catch](#failures-that-validation-doesn%E2%80%99t-catch)
- [Next steps](#next-steps)

Reference

# Marketplace reference

Copy pageCopy page

Complete reference for marketplace.json fields, plugin entries, and the plugin and marketplace source objects, with where each is valid.

Copy pageCopy page

`marketplace.json` is the file that defines a plugin marketplace. It contains the marketplace’s name, its owner, and one entry per plugin. Each entry’s plugin source says where Claude Code fetches that plugin from. A marketplace source is a separate object that says where Claude Code fetches the marketplace file itself. You write one in settings, or Claude Code builds one when you run `claude plugin marketplace add`. This reference is for marketplace maintainers who need an exact field name or value, and for administrators who need to know which `source` values are valid in [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces), [`strictKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#strictknownmarketplaces), and [`blockedMarketplaces`](plugins-org.md#restrict-what-users-can-install).

These cases are covered on other pages:

- **Building or hosting a marketplace**: see [Create a marketplace](plugin-marketplaces.md) and [Host and maintain a marketplace](plugins-host-marketplace.md)
- **Allowlist and blocklist recipes**: see [Manage plugins for your organization](plugins-org.md)

Find the section for what you’re writing or reading:

- **The marketplace file**: [Top-level fields](#top-level-fields) and [Plugin entries](#plugin-entries)
- **An entry’s `source`**: [Plugin sources](#plugin-sources)
- **A `source` object in settings**: [Marketplace sources](#marketplace-sources)
- **Output from [`claude plugin validate <path>`](plugins-cli-reference.md)**: [Validation messages](#validation-messages), which maps each message to the field it names


[​](#marketplace-file)

Marketplace file

Save the marketplace file at `.claude-plugin/marketplace.json` in your marketplace’s directory. If you keep the file somewhere else in the repository, users have to declare the marketplace in [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) with `path` set on its source, because `claude plugin marketplace add` has no option for it. The directory that contains `.claude-plugin/` is called the marketplace root, and every relative plugin source resolves from it, not from `.claude-plugin/`. Each user registers one marketplace per `name`, so a user can’t have two marketplaces with the same name registered at once. Claude Code ignores an unknown top-level key or plugin-entry key rather than rejecting it, so a typo loads silently. `claude plugin validate` reports each unknown key as a warning.


[​](#reserved-names)

Reserved names

You can’t give your marketplace any of the following names:

- **Official marketplace names**: `claude-code-marketplace`, `claude-code-plugins`, `claude-plugins-official`, `anthropic-marketplace`, `anthropic-plugins`, `agent-skills`, `anthropic-agent-skills`, `life-sciences`, `knowledge-work-plugins`, `claude-for-legal`, `claude-for-financial-services`, `financial-services-plugins`, `first-party-plugins`, and `claude-tag-plugins`. Reserved unless the marketplace comes from a `github` or `git` [marketplace source](#marketplace-sources) under `github.com/anthropics/`.
- **Community marketplace names**: `claude-community`, `claude-plugins-community`, and `healthcare`. Reserved under the same rule as the official names.
- **Plugin directory names**: `anthropic-plugin-directory` and `claude-plugin-directory`. Reserved under the same rule as the official names.
- **Names that impersonate an official marketplace**: names such as `official-claude-plugins` or `claude-plugins-v2`, and any name containing a non-ASCII character. The error is `Marketplace name impersonates an official Anthropic/Claude marketplace`. A control or bidirectional-formatting character in a name also reports `Marketplace name cannot contain control or bidirectional-formatting characters`. A marketplace already registered under such a name stops loading, along with its plugins.
- **Another spelling of a reserved name**: a name that differs from a reserved name only by a trailing dot, or by a symbol other than an underscore in place of a hyphen, so `claude.code.plugins` counts as `claude-code-plugins`. Adding the marketplace fails with [`is another spelling of "<reserved>", a reserved marketplace name`](../02-Claude-Code-CLI/errors.md#marketplace-name-is-another-spelling-of-a-reserved-name), and a marketplace already registered under one stops loading. This check requires Claude Code v2.1.280 or later.
- **Names Claude Code uses for plugins that don’t come from a marketplace**: `inline` for plugins loaded with [`--plugin-dir`](../02-Claude-Code-CLI/cli-reference.md), `builtin` for built-in plugins, `skills-dir` for plugins auto-loaded from [`.claude/skills/`](skills.md), and `synced` for plugins synced from your claude.ai account. `claude-plugin-test` is also reserved. `skills-dir` also appears as `{"source": "skills-dir"}` in `strictKnownMarketplaces` and `blockedMarketplaces`, described under [Source values valid only in policy lists](#source-values-valid-only-in-policy-lists).
- **`npm`, `pip`, `uv`, `cargo`, `github`, and `gh`**: reserved in any casing. This check requires Claude Code v2.1.275 or later.
- **Member names that every JavaScript object has**: `constructor`, `hasOwnProperty`, `isPrototypeOf`, `propertyIsEnumerable`, `toLocaleString`, `toString`, and `valueOf`. `claude plugin marketplace add` refuses a marketplace that uses one with [`Claude Code reserves this name and cannot register a marketplace under it`](plugins-troubleshooting.md#claude-code-reserves-this-name). This check requires Claude Code v2.1.296 or later.
- **Names starting with `claudeai-`**: reserved for marketplaces hosted on claude.ai. `claude plugin marketplace add` refuses any other marketplace that uses one with `Cannot add marketplace "<name>": names starting with "claudeai-" are reserved for marketplaces hosted on claude.ai`.
- **The download folder of a registered GitHub marketplace, `<owner>-<repo>`**: Claude Code downloads a marketplace added from a `github` source such as `acme/x-tools` through a folder named `acme-x-tools`, whatever that marketplace’s own `name` is. While that marketplace is registered under a name other than `acme-x-tools`, `claude plugin marketplace add` refuses a different marketplace named `acme-x-tools` after downloading it, and reports `Can't use the marketplace name "acme-x-tools"`. This check requires Claude Code v2.1.290 or later.

When a registered marketplace stops loading because its name imitates an official one, `claude plugin list` and `/plugin` report `Claude Code refuses the marketplace name "<name>"`. The message tells you to remove the marketplace. Removing it also uninstalls its plugins and deletes their data, options, and secrets. This named refusal message requires Claude Code v2.1.282 or later. If a settings file declares the marketplace inline, the message tells you to rename its entry before you remove the old name. For the steps, see [Claude Code refuses the marketplace name](../02-Claude-Code-CLI/errors.md#claude-code-refuses-the-marketplace-name). The message lists them in Claude Code v2.1.292 or later.


[​](#top-level-fields)

Top-level fields

The table lists every key Claude Code reads from `marketplace.json`. `name`, `owner`, and `plugins` are required.

| Field                                      | Type             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
|:-------------------------------------------|:-----------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`                                     | string           | Marketplace identifier: letters, digits, `.`, `_`, and `-`, starting with a letter or digit, and no `..`. `claude plugin validate` fails any other name, because Claude Code [can’t install plugins from a marketplace that uses one](plugins-troubleshooting.md#cannot-install-plugins-from-a-marketplace-with-this-name). Users type the name after `@` in a [plugin id](plugins-loading.md#find-where-a-plugin-came-from) such as `my-plugin@my-marketplace` when they install a plugin. See [Reserved names](#reserved-names) |
| `owner`                                    | object           | Maintainer information. `name` is required; `email` and `url` are optional                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `plugins`                                  | array            | [Plugin entries](#plugin-entries). Each entry is validated on its own, so one invalid entry doesn’t fail the marketplace                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `$schema`                                  | string           | JSON Schema URL for editor autocomplete. Ignored at load time                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `description`                              | string           | Marketplace description shown to users. `claude plugin validate` warns when it’s missing                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `version`                                  | string           | Marketplace manifest version                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `metadata.description`, `metadata.version` | string           | Alternate location for `description` and `version`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `metadata.pluginRoot`                      | string           | Directory that bare plugin source names resolve under. See [Relative path plugin source](#relative-path-plugin-source). Requires Claude Code v2.1.239 or later                                                                                                                                                                                                                                                                                                                                                                                |
| `forceRemoveDeletedPlugins`                | boolean          | When `true`, a plugin you remove from `plugins` is uninstalled on users’ machines. See [Host and maintain a marketplace](plugins-host-marketplace.md)                                                                                                                                                                                                                                                                                                                                                                                   |
| `allowCrossMarketplaceDependenciesOn`      | array of strings | Marketplace names whose plugins may be installed as dependencies of this marketplace’s plugins. When you install a plugin, only the list in that plugin’s own marketplace applies, for its whole dependency chain. See [Plugin dependencies](plugins-dependencies.md)                                                                                                                                                                                                                                                                   |
| `renames`                                  | object           | Map from a former plugin `name` to its current name, or to `null` for a plugin you removed. See [Host and maintain a marketplace](plugins-host-marketplace.md)                                                                                                                                                                                                                                                                                                                                                                          |


[​](#plugin-entries)

Plugin entries

Each object in the top-level `plugins` array of `marketplace.json` names a plugin and says where to fetch it. `name` and `source` are required. Apart from the [directory listing fields](plugins-reference.md#directory-listing-fields), an entry also accepts every [`plugin.json` field](plugins-reference.md), such as `description`, `version`, `author`, `commands`, and `hooks`. For when those fields apply, see [How an entry combines with plugin.json](#entry-and-plugin-json). The table lists the entry’s own fields and the manifest fields whose meaning changes in an entry.

| Field            | Type             | Description                                                                                                                                                                                                                                                                                                            |
|:-----------------|:-----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`           | string           | Plugin identifier: letters, digits, `.`, `_`, and `-`, starting with a letter or digit. `claude plugin validate` fails other names, which Claude Code can’t install. Users type it before `@` when they install, even when the plugin’s own `plugin.json` sets a different `name`                                      |
| `source`         | string or object | Where to fetch the plugin. See [Plugin sources](#plugin-sources)                                                                                                                                                                                                                                                       |
| `description`    | string           | Shown in [`/plugin`](discover-plugins.md) listings and details                                                                                                                                                                                                                                                    |
| `version`        | string           | Version string for the plugin. When `plugin.json` also sets `version`, `plugin.json` takes precedence and `claude plugin validate` warns. See [Plugin loading reference](plugins-loading.md)                                                                                                                     |
| `category`       | string           | Free-form category for organizing the catalog                                                                                                                                                                                                                                                                          |
| `tags`           | array of strings | Free-form tags for search                                                                                                                                                                                                                                                                                              |
| `strict`         | boolean          | Default `true`. Whether `plugin.json` is the definitive source for the plugin’s components. See [Strict mode](#strict-mode)                                                                                                                                                                                            |
| `relevance`      | object           | Signals that tell Claude Code when to suggest the plugin. See [Recommend plugins for your org](plugins-relevance.md)                                                                                                                                                                                             |
| `dependencies`   | array            | Plugins that must be enabled for this one to work. Each item is `"name"`, `"name@marketplace"`, or an object. See [Plugin dependencies](plugins-dependencies.md)                                                                                                                                                 |
| `defaultEnabled` | boolean          | Default `true`. Whether the plugin starts enabled when the user hasn’t set it in [`enabledPlugins`](../02-Claude-Code-CLI/settings-reference.md#enabledplugins). The entry value takes precedence over `plugin.json`                                                                                                                   |
| `displayName`    | string           | Human-readable name shown in the UI. When neither the entry nor the plugin’s `plugin.json` sets one, users see the plugin’s `name`                                                                                                                                                                                     |
| `metadata`       | object           | Free-form object for your own fields. Claude Code doesn’t read it. Requires Claude Code v2.1.222 or later                                                                                                                                                                                                              |
| `headers`        | object           | HTTP headers Claude Code sends when it downloads this entry’s [archive](#archive-plugin-source). A header set here replaces a header of the same name from the marketplace source’s [`headers`](#fields-by-type). Requires Claude Code v2.1.238 or later                                                               |
| `headersHelper`  | string           | Command that prints this entry’s archive-download headers as one JSON object, for a credential that expires. The entry must also set [`"strict": false`](#strict-mode). Requires Claude Code v2.1.238 or later. See [Authenticate archive downloads](plugins-host-marketplace.md#authenticate-archive-downloads) |


[​](#entry-and-plugin-json)

How an entry combines with plugin.json

The entry’s fields apply differently to a fetched plugin that has its own `.claude-plugin/plugin.json` and to one that doesn’t:

- **No `plugin.json`**: the entry is the manifest regardless of `strict`. Every manifest field that the entry accepts applies, including [`mcpServers`, `lspServers`, `userConfig`, and `channels`](plugins-reference.md).
- **`plugin.json` present**: `plugin.json` is the manifest. [Strict mode](#strict-mode) decides whether the entry’s six component fields, `commands`, `agents`, `skills`, `hooks`, `outputStyles`, and `themes`, are combined with it or rejected as a conflict. Entry `mcpServers`, `lspServers`, `userConfig`, and `channels` don’t apply. Declare them in `plugin.json`.


[​](#hooks-in-an-entry)

Hooks in an entry

Write entry `hooks` as an inline object that maps hook event names to matcher arrays. If you write a file path or an array instead, `claude plugin validate` passes it. Those hooks never run, and Claude Code reports a `not yet supported in a marketplace entry` error for the plugin. Put file-based hooks in the plugin’s own [`hooks/hooks.json`](plugins-components.md) or `plugin.json`.


[​](#display-fields)

Display fields

Both the entry and the plugin’s own `plugin.json` can set the display fields `displayName`, `description`, `author`, `homepage`, `repository`, `license`, and `keywords`. Users see these values in plugin listings and details, before and after install:

- For a field you set on the entry, users see the entry’s value, even when `plugin.json` sets a different one.
- For a field the entry leaves unset, users see the `plugin.json` value.

Before install, Claude Code can read `plugin.json` only for entries with a [relative-path source](#relative-path-plugin-source), whose plugin files are inside the marketplace itself. For an entry with any other source type, users see only the entry’s own fields until they install the plugin.


[​](#strict-mode)

Strict mode

`strict` decides what happens when the fetched plugin has its own `plugin.json` and the entry also declares any of the [component fields](#entry-and-plugin-json): `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes`. With `strict: true`, the default, Claude Code appends the entry’s component fields to `plugin.json`, except `hooks`, whose matchers replace the manifest’s per event. With `strict: false`, an entry that declares any component field is a conflict, and the plugin fails to load. The table shows each combination of `strict`, `plugin.json`, and the entry’s component fields.

| `strict`            | `plugin.json` | Entry component fields | Result                                                                                                                                                                                                                                   |
|:--------------------|:--------------|:-----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| any                 | absent        | any                    | The entry is the manifest                                                                                                                                                                                                                |
| `true`, the default | present       | any                    | `plugin.json` is the authority. Claude Code appends the entry’s component fields to it, except `hooks`, whose matchers [replace the manifest’s per event](plugins-reference.md#how-entry-fields-combine-with-plugin-json) |
| `false`             | present       | none                   | `plugin.json` is the manifest, as with `true`                                                                                                                                                                                            |
| `false`             | present       | one or more            | Conflict. The plugin fails to load with `Plugin <name> has conflicting manifests: both plugin.json and marketplace entry specify components`                                                                                             |


[​](#plugin-sources)

Plugin sources

A plugin entry’s `source` says where Claude Code fetches that one plugin from. It’s either a relative path string or an object whose own `source` key names the type, so an entry looks like `"source": { "source": "github", "repo": "your-org/formatter" }`. The table lists each plugin source type and its fields.

| Type          | Fields                           | Notes                                                                                                                                                                                                                          |
|:--------------|:---------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Relative path | the string itself                | A directory inside the marketplace, resolved from the marketplace root. Must start with `./`, unless you write a [bare name under `metadata.pluginRoot`](#relative-path-plugin-source). `"."` on its own means the root itself |
| `github`      | `repo`, `ref`, `sha`             | GitHub repository in `owner/repo` form                                                                                                                                                                                         |
| `url`         | `url`, `ref`, `sha`              | Any git repository by URL                                                                                                                                                                                                      |
| `git-subdir`  | `url`, `path`, `ref`, `sha`      | One subdirectory of a git repository, fetched with a sparse checkout                                                                                                                                                           |
| `npm`         | `package`, `version`, `registry` | npm registry package or tarball link, fetched with your npm client and unpacked without running install scripts                                                                                                                |
| `archive`     | `url`, `sha256`                  | Zip archive over HTTPS. Requires Claude Code v2.1.224 or later                                                                                                                                                                 |
| `command`     | `command`, `timeout`, `mode`     | Directory printed by a command Claude Code runs on the user’s machine. Requires Claude Code v2.1.229 or later                                                                                                                  |

The names `url` and `github` are also [marketplace source](#marketplace-sources) types, where `url` means a direct link to a `marketplace.json` file rather than a git repository. `git` exists only as a marketplace source, and `npm` exists as both. `git-subdir`, `archive`, and `command` exist only as plugin sources. Use a relative path for a plugin in a subdirectory of the marketplace repository itself. Use `git-subdir` for a subdirectory of some other repository. `github`, `url`, and `git-subdir` sources share the `ref` and `sha` fields:

- **`ref`**: a branch or tag. Defaults to the repository’s default branch.
- **`sha`**: a full 40-character lowercase commit SHA. When you set both `ref` and `sha`, Claude Code checks out `sha`. On most git hosts, including GitHub, GitLab, and Bitbucket, this means installation succeeds even if the branch or tag named by `ref` has since been deleted upstream, as long as the commit is still reachable from the repository. Some servers, such as AWS CodeCommit, don’t support fetching commits by SHA. On those servers the `ref` must still exist and the pinned commit must be reachable from it.

For how each type is fetched, cached, and versioned, see [Plugin loading reference](plugins-loading.md).


[​](#relative-path-plugin-source)

Relative path plugin source

The path resolves from the marketplace root. `./plugins/formatter` is `<root>/plugins/formatter` even though the marketplace file is in `<root>/.claude-plugin/`. A path containing `..` fails validation. On macOS and Linux, Claude Code refuses an entry path that contains a backslash anywhere after the leading `./`, so write the path with forward slashes.

```python
{ "name": "formatter", "source": "./plugins/formatter" }
```

A relative path resolves only when Claude Code has the marketplace’s files, so check the [marketplace source](#marketplace-sources) type:

- **`github`, `git`, `file`, and `directory`**: Claude Code has the marketplace’s files.
- **`url`**: Claude Code fetches only `marketplace.json`, so relative paths can’t resolve. Give each plugin an object source instead, such as `github` or `git-subdir`.
- **`settings`**: relative paths are rejected outright.


[​](#bare-names-under-pluginroot)

Bare names under pluginRoot

A bare name is a single directory name with no `/`, such as `"formatter"`. To write bare names instead of `./` paths, set [`metadata.pluginRoot`](#top-level-fields) to the directory they resolve under. With `"pluginRoot": "./plugins"`, `"source": "formatter"` resolves to `./plugins/formatter`. Requires Claude Code v2.1.239 or later. `metadata.pluginRoot` has these limits:

- It must itself be a relative path inside the marketplace.
- It has no effect on a source that already starts with `./`.
- A source that contains a `/`, such as `team-a/formatter`, isn’t a bare name and still needs the `./` prefix, even when `metadata.pluginRoot` is set.


[​](#github-plugin-source)

github plugin source

`repo` takes `owner/repo`. `ref` and `sha` are optional.

```python
{
  "name": "formatter",
  "source": {
    "source": "github",
    "repo": "your-org/formatter",
    "ref": "v2.0.0",
    "sha": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
  }
}
```


[​](#url-plugin-source)

url plugin source

`url` is a full git URL: `https://`, `http://`, `file://`, or `git@`. A `.git` suffix isn’t required, so Azure DevOps and AWS CodeCommit URLs work as written. This type doesn’t take `owner/repo` shorthand.

```python
{
  "name": "formatter",
  "source": {
    "source": "url",
    "url": "https://gitlab.example.com/your-group/formatter.git",
    "ref": "main"
  }
}
```


[​](#git-subdir-plugin-source)

git-subdir plugin source

`url` accepts a full git URL or GitHub `owner/repo` shorthand. `path` is the subdirectory that holds the plugin, and Claude Code checks out only that subdirectory. Over an `https` or SSH URL, Claude Code asks the server for a partial clone, so from a host that supports partial clones, a plugin in a large monorepo installs without downloading the rest of the repository.

```python
{
  "name": "formatter",
  "source": {
    "source": "git-subdir",
    "url": "https://github.com/your-org/monorepo.git",
    "path": "tools/formatter"
  }
}
```


[​](#npm-plugin-source)

npm plugin source

An `npm` source takes these fields:

- `package`: a registry package name such as `@your-org/formatter`, a name with a version appended such as `@your-org/formatter@2.0.0`, or an `https` link to the package’s tarball file
- `version`: a version, a semver range, or a dist-tag, used when `package` is a package name with no version appended. Omit it to fetch `latest`
- `registry`: a registry URL for a package that isn’t on the default registry

Claude Code fetches the package with your npm client. The package’s install scripts, such as `preinstall` or `postinstall`, never run, and its dependencies aren’t installed during the fetch. If the package has a supported lockfile beside its `package.json`, Claude Code installs those [Node.js package dependencies](plugins-loading.md#node-js-package-dependencies) in a separate step, also with scripts disabled. Claude Code checks the `package` value before fetching anything. A refused value fails the install with a message that names the value and the reason. The refused values include:

- **A git address, a folder or `file:` path, or an `npm:` alias**: use a [`github`, `url`, or `git-subdir` source](#plugin-sources) for a git repository, a relative path for a folder in the marketplace, and the package’s own name for an alias
- **A tarball link on github.com, gist.github.com, gitlab.com, bitbucket.org, or git.sr.ht**: refused even when the link is a GitHub release download, unless it’s a GitLab npm registry link under `gitlab.com/api/v4/`
- **A tarball link over `http`**: refused unless it points at the installing user’s own default npm registry

The `registry` URL must use `https` unless it is the installing user’s own default npm registry. With any other `http` registry, the install fails before npm contacts it.

```python
{
  "name": "formatter",
  "source": {
    "source": "npm",
    "package": "@your-org/formatter",
    "version": "^2.0.0",
    "registry": "https://npm.example.com"
  }
}
```


[​](#archive-plugin-source)

archive plugin source

`url` must use `https://` and can’t point at a loopback, link-local, or cloud-metadata host. For the size, timeout, redirect, and extraction limits on the download, see [Stay within the download limits for hosted files](plugins-host-marketplace.md#stay-within-the-download-limits-for-hosted-files). The plugin root may be at the top of the zip or one directory down. `sha256` is the archive’s digest as 64 hex characters, uppercase or lowercase. When you set it, Claude Code refuses a download that doesn’t match.

```python
{
  "name": "formatter",
  "source": {
    "source": "archive",
    "url": "https://artifacts.example.com/formatter-2.0.0.zip",
    "sha256": "6bfa50e3d2e00c052b46abe51fff89346ac803e45771f76dcf6df1ab74cca5e1"
  }
}
```


[​](#command-plugin-source)

command plugin source

Use a `command` source when a tool installed on the user’s machine produces the plugin directory, such as an IDE that renders its plugin for the toolchain the user has selected. Claude Code runs the command when the user installs or updates the plugin, and [again once per session](plugins-loading.md#when-a-command-source-re-runs), so users get the tool’s changed output without reinstalling. A `command` source takes these fields:

- `command`: a shell command that prints the plugin directory’s absolute path as one line and exits 0. Claude Code shows users the whole string for review before it runs. Write it as printable ASCII, at most 500 characters, with no run of four or more spaces.
- `timeout`: a whole number of seconds from 1 to 600. Defaults to 60.
- `mode`: `copy`, the default, or `link`. See [Copy mode and link mode](#copy-mode-and-link-mode).

```python
{
  "name": "formatter",
  "source": {
    "source": "command",
    "command": "my-tool claude-plugin-path",
    "timeout": 120
  }
}
```

For how users accept the command, see [Install from your shell](discover-plugins.md#install-from-your-shell). For what users see after you change it, see [Change the command of a command source](plugins-host-marketplace.md#change-the-command-of-a-command-source). Administrators turn command sources off with [`disableCommandPluginSources`](../02-Claude-Code-CLI/settings-reference.md#disablecommandpluginsources).


[​](#what-the-command-must-do)

What the command must do

Write the command to meet these requirements:

- **Shell and working directory**: Claude Code runs the command through `sh`, or through `cmd.exe` on Windows, from the user’s home directory. Give an absolute path or a command on `PATH`.
- **Output**: print exactly one line on stdout, the absolute path of the plugin directory, and exit 0 within `timeout` seconds.
- **Directory contents**: the directory holds the complete plugin by the time the command exits. The path can differ from one run to the next.


[​](#output-that-fails-the-install-or-update)

Output that fails the install or update

The install or update fails when the command exits non-zero, runs longer than `timeout`, or prints anything other than one absolute path. It also fails when the printed directory is one of these:

- **No plugin content**: the printed directory has no plugin content at its top level, such as a `.claude-plugin/` directory or a `skills/`, `commands/`, `agents/`, or `hooks/` directory.
- **The session’s own directory**: the printed directory is the one Claude Code was started in, or one of its parents.
- **A network path**: on Windows, the printed path is a UNC path.
- **Too large to copy**: in copy mode, the directory is larger than 256 MiB or has more than 20,000 entries.


[​](#copy-mode-and-link-mode)

Copy mode and link mode

`mode` decides whether Claude Code copies the printed directory or uses it in place:

- **`copy`**: Claude Code copies the directory into the plugin cache and derives the [plugin version](plugins-loading.md#how-claude-code-computes-the-version) from a hash of the copied files. Your tool can delete or rewrite the directory after the command exits. A re-run that produces identical files counts as up to date.
- **`link`**: Claude Code fills the plugin’s cache entry with a link to each top-level entry of the printed directory and loads the files in place. Nothing is copied, file contents aren’t hashed, and the size limits don’t apply. Use it for a directory too large to copy, such as a rendered SDK export.

A link-mode plugin has these requirements:

- **Keep the directory in place**: Claude Code loads the plugin through the links at every startup, so the printed directory must stay where it is for as long as the plugin stays installed.
- **Print a different path to signal new content**: the version comes from the printed directory’s real path and its top-level entries, not from the files inside them.
- **Keep top-level symlinks inside the directory**: the install fails if a top-level entry is a symlink that points outside the printed directory.
- **Include `node_modules`**: Claude Code skips the [Node.js package dependency install](plugins-loading.md#node-js-package-dependencies) for a link-mode plugin, so print a directory that already contains the packages the plugin needs.
- **Sessions started inside the directory**: a session started in the printed directory or anywhere below it doesn’t load the plugin.
- **Not on Windows**: Claude Code refuses to install a link-mode plugin on Windows. Declare `"mode": "copy"` there.


[​](#marketplace-sources)

Marketplace sources

A marketplace source says where Claude Code fetches a `marketplace.json` from. The CLI builds one for you when you add a marketplace, and you write one yourself in settings:

- **[`claude plugin marketplace add`](plugins-cli-reference.md)**: Claude Code builds the source from the string you pass.
- **[`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces)**: you write the source yourself as the `source` object.
- **[`strictKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#strictknownmarketplaces) and [`blockedMarketplaces`](plugins-org.md#restrict-what-users-can-install)**: administrators write sources in these two policy lists. `strictKnownMarketplaces` is the allowlist and `blockedMarketplaces` is the blocklist.

The type names `url`, `git`, and `github` mean something different in a marketplace source than in a [plugin source](#plugin-sources):

| Type name | As a marketplace source                                                                       | As a plugin source                                                       |
|:----------|:----------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------|
| `url`     | A direct link to a `marketplace.json` file, with fields `url`, `headers`, and `headersHelper` | A git repository to clone, with fields `url`, `ref`, and `sha`           |
| `git`     | A git repository to clone, with fields `url`, `ref`, `path`, and `sparsePaths`                | Doesn’t exist                                                            |
| `github`  | A GitHub repository, with fields `repo`, `ref`, `path`, and `sparsePaths`                     | A GitHub repository, with fields `repo`, `ref`, and `sha`, and no `path` |

The table lists every marketplace source type with its fields, the `claude plugin marketplace add` input that produces it, and what it does in each of the three settings keys.

| Type          | Fields                               | `marketplace add` input                                                                                                                                             | `extraKnownMarketplaces`                                     | `strictKnownMarketplaces`                                                                                                                                                                | `blockedMarketplaces`                                                  |
|:--------------|:-------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------|
| `url`         | `url`, `headers`, `headersHelper`    | An `http://` or `https://` URL that doesn’t match a git form                                                                                                        | Loads                                                        | Allows the same URL                                                                                                                                                                      | Blocks the same URL                                                    |
| `github`      | `repo`, `ref`, `path`, `sparsePaths` | `owner/repo`, `owner/repo@ref`, or `owner/repo#ref`                                                                                                                 | Loads                                                        | Allows the same `repo`, `ref`, and `path`. `repo` may be `owner/*`                                                                                                                       | Blocks the same, and a `git` URL to the same repository                |
| `git`         | `url`, `ref`, `path`, `sparsePaths`  | A `user@host:path` URL, or an `http://` or `https://` URL that ends in `.git`, contains `/_git/`, or names a github.com or gitlab.com repository. `#ref` pins a ref | Loads                                                        | Allows the same URL, `ref`, and `path`                                                                                                                                                   | Blocks the same, and other spellings of the same github.com repository |
| `npm`         | `package`                            | Not produced                                                                                                                                                        | Fails to load: `NPM marketplace sources not yet implemented` | Parses but matches nothing, because nothing registers an `npm` marketplace                                                                                                               | Parses but matches nothing                                             |
| `file`        | `path`                               | A path to a `.json` file                                                                                                                                            | Loads                                                        | Allows the same path                                                                                                                                                                     | Blocks the same path                                                   |
| `directory`   | `path`                               | A path to a directory                                                                                                                                               | Loads                                                        | Allows the same path                                                                                                                                                                     | Blocks the same path                                                   |
| `settings`    | `name`, `plugins`, `owner`           | Not produced                                                                                                                                                        | Loads                                                        | Allows an entry with the same `name` and identical `plugins`                                                                                                                             | Blocks the same `name`                                                 |
| `skills-dir`  | none                                 | Not produced                                                                                                                                                        | Fails to load: `Unsupported marketplace source type`         | Allows [skills-directory plugins](plugins-org.md#keep-skills-directory-plugins-loading). See [Source values valid only in policy lists](#source-values-valid-only-in-policy-lists) | Stops skills-directory plugins from loading                            |
| `hostPattern` | `hostPattern`                        | Not produced                                                                                                                                                        | Fails to load: `Unsupported marketplace source type`         | Allows `github`, `git`, and `url` sources whose host matches                                                                                                                             | Blocks those sources                                                   |
| `pathPattern` | `pathPattern`                        | Not produced                                                                                                                                                        | Fails to load: `Unsupported marketplace source type`         | Allows `file` and `directory` sources whose `path` matches                                                                                                                               | Blocks those sources                                                   |


[​](#fields-by-type)

Fields by type

The table lists each marketplace source field that has a default, a constraint, or a meaning specific to its type.

| Field           | Types           | Description                                                                                                                                                                                                                                                                                                                                                    |
|:----------------|:----------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `url`           | `url`           | Link to the `marketplace.json` file. Claude Code downloads only that file, so the marketplace’s plugins can’t use [relative-path sources](#relative-path-plugin-source). See [Stay within the download limits for hosted files](plugins-host-marketplace.md#stay-within-the-download-limits-for-hosted-files) for the size, timeout, and redirect limits |
| `url`           | `git`           | The git repository to clone                                                                                                                                                                                                                                                                                                                                    |
| `headers`       | `url`           | Map of HTTP headers Claude Code sends with the fetch, for authenticated hosts                                                                                                                                                                                                                                                                                  |
| `headersHelper` | `url`           | Command that prints headers whose values are too short-lived to list in `headers`. Requires Claude Code v2.1.238 or later. See [Authenticate archive downloads](plugins-host-marketplace.md#authenticate-archive-downloads)                                                                                                                              |
| `repo`          | `github`        | In `marketplace add` and `extraKnownMarketplaces`, `repo` must name one repository. `marketplace add` rejects `owner/*` as not a valid `owner/repo` shorthand; in `extraKnownMarketplaces` Claude Code takes it literally and the clone fails                                                                                                                  |
| `ref`           | `github`, `git` | Branch or tag. Defaults to the repository’s default branch                                                                                                                                                                                                                                                                                                     |
| `path`          | `github`, `git` | The marketplace file’s path inside the repository. Defaults to `.claude-plugin/marketplace.json`                                                                                                                                                                                                                                                               |
| `path`          | `file`          | The marketplace file itself. Claude Code reads it in place and takes the directory two levels up as the marketplace root, so keep the file at `<root>/.claude-plugin/marketplace.json`                                                                                                                                                                         |
| `path`          | `directory`     | The marketplace root, the directory that contains `.claude-plugin/marketplace.json`                                                                                                                                                                                                                                                                            |
| `sparsePaths`   | `github`, `git` | Array of directories for a sparse checkout, such as `[".claude-plugin", "plugins"]`. `claude plugin marketplace add --sparse` sets it                                                                                                                                                                                                                          |
| `skipLfs`       | `github`, `git` | Accepted and has no effect. See [Keep plugin files out of Git LFS](plugins-host-marketplace.md#keep-plugin-files-out-of-git-lfs)                                                                                                                                                                                                                         |
| `name`          | `settings`      | Must equal the `extraKnownMarketplaces` key and can’t be a [reserved name](#reserved-names)                                                                                                                                                                                                                                                                    |
| `plugins`       | `settings`      | The inline catalog, with no hosted file. Each item takes `name`, `source`, `description`, `version`, `strict`, `headers`, and `headersHelper`. Write each item’s `source` as an object type, because a relative path has no repository to resolve against                                                                                                      |


[​](#source-values-valid-only-in-policy-lists)

Source values valid only in policy lists

`hostPattern`, `pathPattern`, `skills-dir`, and the `owner/*` form of `repo` are valid only in the two policy lists, `strictKnownMarketplaces` and `blockedMarketplaces`:

- **`hostPattern` and `pathPattern`**: regular expressions Claude Code tests against a source before it fetches from it.
- **`skills-dir`**: not a source. If you set `strictKnownMarketplaces` without a `{"source": "skills-dir"}` entry, [skills-directory plugins](plugins-org.md#keep-skills-directory-plugins-loading) stop loading. If you lock any customization type with [`strictPluginOnlyCustomization`](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization), they stop loading whether or not the entry is present.
- **`owner/*`**: as a `github` `repo` value, matches every repository under exactly that GitHub owner. Requires Claude Code v2.1.223 or later.

For match order, exact-`ref` semantics, and recipes, see [Manage plugins for your organization](plugins-org.md).


[​](#source-objects-in-settings)

Source objects in settings

An `extraKnownMarketplaces` value is a map from marketplace name to an object with `source`. This entry registers a marketplace from a git repository at its `main` branch:

```python
{
  "extraKnownMarketplaces": {
    "your-marketplace": {
      "source": {
        "source": "git",
        "url": "https://git.example.com/your-org/your-marketplace.git",
        "ref": "main"
      }
    }
  }
}
```

`strictKnownMarketplaces` and `blockedMarketplaces` are arrays of source objects. This allowlist admits one GitHub owner and one internal host:

```python
{
  "strictKnownMarketplaces": [
    { "source": "github", "repo": "your-org/*" },
    { "source": "hostPattern", "hostPattern": "^git\\.example\\.com$" }
  ]
}
```


[​](#validation-messages)

Validation messages

`claude plugin validate <path>` takes the marketplace root or the marketplace file itself. It prints errors and warnings. For exit codes and `--strict`, see [plugin validate](plugins-cli-reference.md#plugin-validate). A message names a plugin entry by its index, written as `plugins.1.source` or `plugins[1].source`. A message prefixed with an entry index and `plugin.json →`, such as `plugins[2] plugin.json →`, is about that plugin’s own files. [`claude plugin validate` reports errors](plugins-troubleshooting.md#claude-plugin-validate-reports-errors) lists those messages with their fixes. Warnings that mention Claude Desktop flag names that Claude Desktop rejects. The table maps marketplace-level messages to the field each is about.

| Message                                                                                                                                                                                                                                          | Level   | Field                                                                                                                |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------------------------------------------------------------------------------------------------------------------|
| `Marketplace must have a name`                                                                                                                                                                                                                   | Error   | `name` is empty                                                                                                      |
| `Marketplace name cannot contain spaces. Use kebab-case (e.g., "my-marketplace")`                                                                                                                                                                | Error   | `name`                                                                                                               |
| `Marketplace name cannot contain path separators (/ or \), ".." sequences, or be "."`                                                                                                                                                            | Error   | `name`                                                                                                               |
| `Marketplace name impersonates an official Anthropic/Claude marketplace`                                                                                                                                                                         | Error   | `name`. See [Reserved names](#reserved-names)                                                                        |
| `Marketplace name cannot contain control or bidirectional-formatting characters`                                                                                                                                                                 | Error   | `name` contains a control character, such as an escape or a newline, or a Unicode bidirectional-formatting character |
| `Marketplace name "inline" is reserved for --plugin-dir session plugins`, and the `builtin`, `skills-dir`, `synced`, `claude-plugin-test`, `npm`, `pip`, `uv`, `cargo`, `github`, and `gh` variants                                              | Error   | `name`                                                                                                               |
| `Author name cannot be empty`                                                                                                                                                                                                                    | Error   | `owner.name`                                                                                                         |
| `Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")`                                                                                                                                                                          | Error   | `plugins[i].name`                                                                                                    |
| `Plugin name cannot contain control or bidirectional-formatting characters`                                                                                                                                                                      | Error   | `plugins[i].name`                                                                                                    |
| `Plugin name "x" is reserved: it passes as one of Anthropic's own`                                                                                                                                                                               | Error   | `plugins[i].name`. See the manifest’s [`name`](plugins-reference.md#name) for the reserved names      |
| `Plugin name "x" reads as one of Anthropic's own`                                                                                                                                                                                                | Warning | `plugins[i].name`                                                                                                    |
| `Claude Code cannot install plugins from marketplace "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change the marketplace's "name".` | Error   | `name`                                                                                                               |
| `Claude Code cannot install plugin "x". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change this entry's "name".`                        | Error   | `plugins[i].name`                                                                                                    |
| `Duplicate plugin name "x" found in marketplace`                                                                                                                                                                                                 | Error   | Two entries share a `name`                                                                                           |
| `plugins.i.source: Invalid input`                                                                                                                                                                                                                | Error   | The entry’s `source` matches no type. See [Invalid input on a source](#invalid-input-on-a-source)                    |
| `plugins.i.source: Invalid string: must start with "./"`                                                                                                                                                                                         | Error   | A relative-path `source` without the leading `./`. Before v2.1.285, this mistake printed `Invalid input` instead     |
| `plugins[i].source: Path contains "..": <path>`                                                                                                                                                                                                  | Error   | A relative `source` that escapes the marketplace root                                                                |
| `source.source: 'unsupported' is a parse-time placeholder and cannot be authored`                                                                                                                                                                | Error   | `plugins[i].source`                                                                                                  |
| `Plugin "x" sets headersHelper but is not "strict": false`                                                                                                                                                                                       | Error   | `plugins[i].headersHelper`, on an `archive` entry                                                                    |
| `chain does not resolve (<reason>) — target must be a name in plugins[], a key in renames, or null`                                                                                                                                              | Error   | `renames.<old>`                                                                                                      |
| `target "x" is not a valid plugin name (PluginIdSchema)`                                                                                                                                                                                         | Error   | `renames.<old>`                                                                                                      |
| `Unknown field 'x'. Claude Code ignores it at load time.`                                                                                                                                                                                        | Warning | The named key at the top level, under `metadata`, in an entry, or under an entry’s `relevance`                       |
| `Marketplace has no plugins defined`                                                                                                                                                                                                             | Warning | `plugins` is empty                                                                                                   |
| `Plugin "x" sets headers/headersHelper, which only apply to "archive" sources; they have no effect on this entry.`                                                                                                                               | Warning | `plugins[i].headers` or `plugins[i].headersHelper`, on an entry whose `source` isn’t `archive`                       |
| `Plugin "x" fetches its archive with a headersHelper but sets no sha256 pin`                                                                                                                                                                     | Warning | `plugins[i].source.sha256`                                                                                           |
| `Header "x" is a request-routing/identity header that catalog entries may not set; Claude Code drops it at download time.`                                                                                                                       | Warning | `plugins[i].headers.<name>`                                                                                          |
| `Local source "x" is or traverses a symlink, so <path> was not read`                                                                                                                                                                             | Warning | `plugins[i].source`                                                                                                  |
| `No marketplace description provided. Adding a description helps users understand what this marketplace offers`                                                                                                                                  | Warning | `description`                                                                                                        |
| `Entry declares version "x" but <path>/plugin.json says "y". At install time, plugin.json wins`                                                                                                                                                  | Warning | `plugins[i].version`, on a relative-path entry                                                                       |
| `'relevance' must be an object containing topic and signals; got <type>. It will be ignored at load time.`                                                                                                                                       | Warning | `plugins[i].relevance`                                                                                               |
| `'metadata' must be a free-form object; got <type>. It will be ignored at load time.`                                                                                                                                                            | Warning | `plugins[i].metadata`                                                                                                |
| `'experimental' must be an object containing component declarations; got <type>. It will be ignored at load time.`                                                                                                                               | Warning | `plugins[i].experimental`                                                                                            |
| `Marketplace name "x" is reserved in Claude Desktop`                                                                                                                                                                                             | Warning | `name` is `org`, `org-provisioned`, or `unknown`. Claude Desktop rejects the marketplace                             |
| `Marketplace name "x" is not accepted by Claude Desktop (letters, digits, ".", "_", "-"; must start alphanumeric; max 128 chars)`                                                                                                                | Warning | `name`. Claude Desktop rejects the marketplace                                                                       |
| `Plugin name "x" is not accepted by Claude Desktop (letters, digits, ".", "_", "-"; must start alphanumeric; max 128 chars)`                                                                                                                     | Warning | `plugins[i].name`. Claude Desktop drops the entry                                                                    |


[​](#invalid-input-on-a-source)

Invalid input on a source

`Invalid input` on a `source` means the object matched no source type. Check for these causes:

- An `npm` `package` containing `..`
- A `source` type that isn’t one of the [plugin sources](#plugin-sources)
- A known type with a required field missing or of the wrong type, such as `github` without `repo`

A relative path that doesn’t start with `./`, other than `"."` or a [bare name under `metadata.pluginRoot`](#relative-path-plugin-source), fails with `Invalid string: must start with "./"`. Before v2.1.285, it printed `Invalid input` like the causes above.


[​](#failures-that-validation-doesn’t-catch)

Failures that validation doesn’t catch

`claude plugin validate` doesn’t report every failure. An entry `hooks` written as a file path or array passes validation, and the error appears only when the plugin loads, as [Hooks in an entry](#hooks-in-an-entry) describes. Errors fetching a `source` also appear only after install, not in validation. [`claude plugin list`](plugins-cli-reference.md) shows a plugin that failed to load with its error, and [Troubleshoot plugins](plugins-troubleshooting.md) covers the load-time strings.


[​](#next-steps)

Next steps

- [Create a marketplace](plugin-marketplaces.md): build a marketplace from these fields and install from it locally
- [Host and maintain a marketplace](plugins-host-marketplace.md): where to put the file and how users receive changes
- [Plugin manifest reference](plugins-reference.md): the `plugin.json` fields an entry can override
- [Manage plugins for your organization](plugins-org.md): allowlist and blocklist recipes that use these source values

---

## Measure plugin cost and usage - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/measure
Path: 08-Plugins-Skills/plugins-measure.md

## On this page

- [Measure what a plugin costs](#measure-what-a-plugin-costs)
  - [Lower the always-on figure](#lower-the-always-on-figure)
  - [Cost shown to users before install](#cost-shown-to-users-before-install)
- [Check whether a plugin is used](#check-whether-a-plugin-is-used)
  - [Not used recently in /plugin](#not-used-recently-in-%2Fplugin)
  - [Find skills that never run](#find-skills-that-never-run)
  - [Unused plugins in /doctor](#unused-plugins-in-%2Fdoctor)
  - [Usage share in /usage](#usage-share-in-%2Fusage)
- [Measure across a fleet](#measure-across-a-fleet)
  - [OpenTelemetry events for plugin installs and use](#pick-the-opentelemetry-event-for-each-question)
  - [Redacted plugin names in your backend](#redacted-plugin-names-in-your-backend)
  - [Query the Analytics API](#query-the-analytics-api)
- [Next steps](#next-steps)

Create plugins

# Measure plugin cost and usage

Copy pageCopy page

Measure a Claude Code plugin’s token cost, find out whether people still use it, and pick the telemetry events for organization-wide plugin questions.

Copy pageCopy page

Every session where a plugin is enabled includes the names and descriptions of its skills, agents, and commands in Claude’s context, and those tokens count against the user’s usage whether or not the plugin gets used. This page shows how to see that number for a plugin, how to reduce it if you maintain the plugin, and where usage shows up so you can tell whether a plugin is still being used. This page is for plugin authors and maintainers. If you administer Claude Code for an organization, [Measure across a fleet](#measure-across-a-fleet) covers the same questions across every machine.

These cases are covered on other pages:

- **Testing how reliably the plugin changes Claude’s behavior**: see [Test plugins with evals](plugin-evals.md)
- **Trimming your own session’s context**: see [Manage installed plugins](discover-plugins.md#manage-installed-plugins) and the [context window](../02-Claude-Code-CLI/context-window.md) page

Start with [Measure what a plugin costs](#measure-what-a-plugin-costs).


[​](#measure-what-a-plugin-costs)

Measure what a plugin costs

To see what a plugin adds to Claude’s context, run [`claude plugin details`](plugins-cli-reference.md#plugin-details) with the plugin’s name. You run it in your shell, not at the prompt of a running Claude Code session. The plugin has to be loaded: installed, in a skills directory, or passed with `--plugin-dir` in the same command, as in `claude --plugin-dir ./formatter plugin details formatter`. This example reads an installed plugin named `formatter` that has two skills, a command, an agent, a hook, and an MCP server:

```python
claude plugin details formatter
```

```python
formatter 1.0.0
  Description: Formats and lints code on save
  Source: formatter@my-marketplace

Component inventory
  Skills (3)  format-all, format-code, lint-fix
  Agents (1)  style-reviewer
  Hooks (1)  PostToolUse  (harness-only — no model context cost)
  MCP servers (1)  formatter-tools  (tool schemas resolved at runtime; not counted)
  LSP servers (0)

Projected token cost
  Always-on:   ~146 tok   added to every session

Per-component (rounded)
  component       always-on  on-invoke
  format-code           ~40        ~30
  lint-fix              ~50        ~30
  style-reviewer        ~40        ~40
  format-all           < 20        ~30

  On-invoke cost is paid each time a skill or agent fires.
  Token counts are estimates and may differ from actual usage.
```

Each part of the output answers a different question:

- **Component inventory**: what Claude Code found in the plugin. Commands are counted with skills, so `format-all` appears under `Skills`. Hooks and MCP servers get no cost estimate and no per-component row; to see what a plugin’s MCP tools add, run `/context` in a session with the plugin enabled and read the `MCP tools` category.
- **Always-on**: the tokens that the names and descriptions of the plugin’s skills, agents, and commands add to every session where the plugin is enabled, whether or not anything runs. This is the number every user carries, and the one to reduce.
- **Per-component**: each row splits one skill, agent, or command into its always-on share and its on-invoke cost, which is the body that loads only when that component runs. Use the always-on column to find which component contributes most.


[​](#lower-the-always-on-figure)

Lower the always-on figure

If you maintain the plugin, these changes reduce what it adds to every session. If you only use it, your options are to disable or uninstall it; see [Manage installed plugins](discover-plugins.md#manage-installed-plugins). The always-on figure counts each component’s name plus its `description` and `when_to_use` frontmatter. To lower it:

- Shorten skill and agent descriptions.
- Split a large plugin so users install only the components they need.

A skill’s description is also what Claude matches a request against, so a shorter one can stop the skill triggering. After you trim descriptions, check triggering with a [`tool_used: Skill` grader](plugin-evals.md#create-your-first-eval-suite) in your eval suite. For what each component type contributes, see [plugin components](plugins-components.md).


[​](#cost-shown-to-users-before-install)

Cost shown to users before install

Plugins in the official marketplace show their cost to users before install. In `/plugin`, when a user browses a marketplace’s plugin list and selects a plugin, the details pane shows a **Context cost** section with an `Every turn:` line and a `When invoked:` line. When the always-on figure is 2,000 tokens or more, the `Every turn:` line appears highlighted. A plugin in your own marketplace has no **Context cost** section.


[​](#check-whether-a-plugin-is-used)

Check whether a plugin is used

Claude Code doesn’t report a plugin’s usage back to its author. Usage is recorded on the machine of each person who installed the plugin, so what you can learn depends on your relationship to those people:

- **You administer Claude Code for their organization**: the OpenTelemetry events and the Analytics API count installs and skill activations across every machine. See [Measure across a fleet](#measure-across-a-fleet).
- **They’re teammates you can ask**: each user’s own Claude Code shows them whether they still use the plugin, in four places: the [`/plugin` panel](#not-used-recently-in-/plugin), [`/skill-doctor`](#find-skills-that-never-run), [`/doctor`](#unused-plugins-in-/doctor), and [`/usage`](#usage-share-in-/usage). All four are commands the user runs at the Claude Code prompt in a session on their own machine.
- **Neither**: you have no usage signal from Claude Code for that plugin.

For usage of a plugin listed in Anthropic’s directory, see [Track published plugin usage](https://claude.com/docs/connectors/building/after-publishing#track-published-plugin-usage) on claude.com.


[​](#not-used-recently-in-/plugin)

Not used recently in `/plugin`

On the **Installed** tab of `/plugin`, a plugin the user installed from a marketplace moves under a **Not used recently** header once it has gone unused for at least 14 days and 10 sessions. The plugin’s details also show a `Last used:` line. For what users do with that header and line, see [Find plugins you no longer use](discover-plugins.md#find-plugins-you-no-longer-use). The **Not used recently** header never appears for:

- Plugins loaded with `--plugin-dir` or from a skills directory
- Plugins enabled through managed settings, or mounted from a [seed directory](plugins-org.md#seed-containers-and-ci)
- Plugins that include a theme, output style, monitor, or workflow, because those are in use without a tracked invocation

A plugin’s [language server](plugins-components.md#lsp-servers) counts as used when it delivers diagnostics or answers a code navigation request, so an LSP plugin whose server is active in your sessions isn’t listed as unused. When the user’s organization sets [`strictKnownMarketplaces`](plugins-org.md#restrict-what-users-can-install), neither the header nor the `Last used:` line appears.


[​](#find-skills-that-never-run)

Find skills that never run

Run `/skill-doctor` to see what each of your skills costs and how often it gets used. It flags skills that are in Claude’s skill listing but have never been invoked, including skills from plugins. In an interactive session, the report opens in the `/plugin` manager’s **Stats** tab. See [Find unused skills](skills.md#find-unused-skills) for what the report covers and where it’s available.


[​](#unused-plugins-in-/doctor)

Unused plugins in `/doctor`

The `/doctor` checkup lists each user-installed skill, MCP server, and plugin, and recommends disabling the ones that were not used. See [`/doctor` in the commands reference](../02-Claude-Code-CLI/commands.md#all-commands).


[​](#usage-share-in-/usage)

Usage share in `/usage`

On a Pro, Max, Team, or Enterprise plan, the `/usage` breakdown attributes recent usage to skills, subagents, plugins, and MCP servers as a share of the total. See [Using the `/usage` command](../17-Billing-Plans/costs.md#using-the-/usage-command).


[​](#measure-across-a-fleet)

Measure across a fleet

If you administer Claude Code for an organization, you can measure plugin cost and usage across every machine from either of these sources:

- **OpenTelemetry events**: Claude Code exports these to your own backend once you [configure an exporter](../13-Enterprise-Admin/monitoring-usage.md). See [OpenTelemetry events for plugin installs and use](#pick-the-opentelemetry-event-for-each-question).
- **Analytics API**: served from Anthropic’s records, with no exporter needed. See [Query the Analytics API](#query-the-analytics-api).


[​](#pick-the-opentelemetry-event-for-each-question)

OpenTelemetry events for plugin installs and use

These OpenTelemetry events and attributes answer each plugin question from your backend:

| Question                                          | OpenTelemetry event or attribute                                                                                                                              |
|:--------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Which plugins get installed, and from where       | [`claude_code.plugin_installed`](../13-Enterprise-Admin/monitoring-usage.md#plugin-installed-event), one per install                                                           |
| Which plugins are active in how many sessions     | [`claude_code.plugin_loaded`](../13-Enterprise-Admin/monitoring-usage.md#plugin-loaded-event), one per enabled plugin at session start                                         |
| Which skills activate, and which plugin owns them | [`claude_code.skill_activated`](../13-Enterprise-Admin/monitoring-usage.md#skill-activated-event), with `plugin.name` and `marketplace.name` for plugin skills                 |
| What a plugin’s hooks report                      | [`claude_code.hook_plugin_metrics`](../13-Enterprise-Admin/monitoring-usage.md#hook-plugin-metrics-event), emitted only for hooks in official-marketplace plugins              |
| What a plugin costs in API spend                  | `plugin.name` and `marketplace.name` on the [cost counter](../13-Enterprise-Admin/monitoring-usage.md#cost-counter), set when the active skill or subagent belongs to a plugin |


[​](#redacted-plugin-names-in-your-backend)

Redacted plugin names in your backend

Plugins from the official marketplace report their plugin name and marketplace name to your backend verbatim. Every other plugin’s name is redacted or omitted by default, including a plugin from your organization’s own marketplace. The plugin’s [trust tier](plugins-security.md#find-plugins-in-telemetry) decides which. To get real names on some events, set the [`OTEL_LOG_TOOL_DETAILS`](../13-Enterprise-Admin/monitoring-usage.md#common-configuration-variables) environment variable to `1` on the machines that export telemetry, for example in the `env` block of the same [managed settings](../13-Enterprise-Admin/monitoring-usage.md#administrator-configuration) that configure the exporter:

| Event                                 | Default                                                                                            | With `OTEL_LOG_TOOL_DETAILS=1`                      |
|:--------------------------------------|:---------------------------------------------------------------------------------------------------|:----------------------------------------------------|
| `plugin_loaded`                       | `plugin.name` and `marketplace.name` are the literal string `third-party`                          | Real names                                          |
| `plugin_installed`, `skill_activated` | `plugin.name` and `marketplace.name` omitted; on `skill_activated`, `skill.name` is `custom_skill` | Real names                                          |
| Cost counter                          | `plugin.name` is `third-party`; `marketplace.name` absent                                          | Real `plugin.name`; `marketplace.name` still absent |

On `plugin_loaded`, `plugin_id_hash` still identifies each plugin by default, so you can count distinct third-party plugins.


[​](#query-the-analytics-api)

Query the Analytics API

On the Enterprise plan, the Analytics API answers “which plugins does my organization install and invoke” from Anthropic’s records, with no exporter needed. [`GET /v1/organizations/analytics/plugins`](../04-API-Reference/Admin/platform-claude-com-http-beta-organization-analytics-plugins-list.md) returns per-plugin, per-day install and invocation counts across Claude Code and Cowork, which you can group by user, RBAC group, or product. Plugin activity that reaches Anthropic without a plugin name appears in one aggregate `third-party` row. [Find plugins in telemetry](plugins-security.md#find-plugins-in-telemetry) says which plugins Claude Code reports by name. Authenticate the request with an API key that has the `read:analytics` scope, which a Primary Owner creates as described under [Access data programmatically](../13-Enterprise-Admin/analytics.md#access-data-programmatically). See the [endpoint reference](../04-API-Reference/Admin/platform-claude-com-http-beta-organization-analytics-plugins-list.md) for the parameters and response fields.


[​](#next-steps)

Next steps

- [Test plugins with evals](plugin-evals.md): measure how reliably the plugin steers Claude, not only what it costs
- [Lower the always-on figure](#lower-the-always-on-figure): what to change in the plugin to reduce its per-turn cost
- [Plugin security and trust](plugins-security.md#find-plugins-in-telemetry): which telemetry fields carry plugin names and when they’re redacted
- [Monitoring usage](../13-Enterprise-Admin/monitoring-usage.md): the full OpenTelemetry event reference

---

## Manage mods for your organization - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/admin
Path: 08-Plugins-Skills/plugins-mods-admin.md

## On this page

- [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading)
- [Know what happens by default](#know-what-happens-by-default)
  - [Know which controls still apply](#know-which-controls-still-apply)
- [Decide whether to leave mods on](#decide-whether-to-leave-mods-on)
  - [Review what a mod can do](#review-what-a-mod-can-do)
- [Choose how much to allow](#choose-how-much-to-allow)
  - [Allow only your organization’s mods](#allow-only-your-organization%E2%80%99s-mods)
  - [Apply your plugin controls to mods](#apply-your-plugin-controls-to-mods)
  - [Set options on the built-in guard](#set-options-on-the-built-in-guard)
- [Run your organization’s own mods](#run-your-organization%E2%80%99s-own-mods)
  - [Install your organization’s mods and set the order](#install-your-organizations-mods)
  - [Enforce a policy with a mod of your own](#enforce-a-policy-with-a-mod-of-your-own)
  - [Refuse mods when your check fails](#refuse-mods-when-your-check-fails)
- [Next steps](#next-steps)

Manage plugins for your organization

# Manage mods for your organization

Copy pageCopy page

Control Claude Code mods with managed settings: stop user-installed mods, allow only your own, review what a mod can do, and enforce policy with your own mod.

Copy pageCopy page

A [mod](plugins-mods-overview.md) is a plugin that runs code inside Claude Code with the permissions of the user who installed it. Mods aren’t sandboxed. Through [managed settings](../13-Enterprise-Admin/managed-settings.md), you decide whether mods run on your users’ machines, which ones, and in what order. You can also install a mod of your own that watches or refuses what other mods do. This page is for the person who deploys managed settings for Claude Code, whether as a file, through MDM, or from the claude.ai admin console. Mods are on by default in Claude Code v2.1.286 and later. Start with the section that matches what you came to do:

- **Keep users’ own mods out, with or without mods of your own**: [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading)
- **See what your users get when you change nothing**: [Know what happens by default](#know-what-happens-by-default)
- **Leave mods on with other limits**: [Choose how much to allow](#choose-how-much-to-allow)

These cases are covered on other pages:

- **You haven’t deployed managed settings before**: start with [Deploy managed settings](../13-Enterprise-Admin/managed-settings.md)
- **You want to control which plugins users can install**: see [Manage plugins for your organization](plugins-org.md)


[​](#stop-user-installed-mods-from-loading)

Stop user-installed mods from loading

To keep every mod your users bring from running its hooks, set the `allowManagedModsOnly` option on the [built-in guard](#know-what-happens-by-default), a policy mod that Claude Code loads ahead of every mod a user installs. The option goes in managed settings under `pluginConfigs`, keyed by `cc-plugin-sec-default@builtin`:

managed-settings.json

```python
{
  "pluginConfigs": {
    "cc-plugin-sec-default@builtin": {
      "options": {
        "allowManagedModsOnly": true
      }
    }
  }
}
```

With the option set in managed settings:

- **No mod a user brings runs its hooks**: that covers a mod in a plugin the user installed, a mod loaded with `--plugin-dir`, and a mod [Claude wrote during a session](plugins-mods-create.md#ask-claude-for-a-mod)
- **Your organization’s mods still run**: a mod that [counts as your organization’s](#install-your-organizations-mods) isn’t checked. Every other mod counts as a user’s and is refused. That includes a mod in a plugin you enable from a GitHub or other remote marketplace, and one your organization turns on for its members on claude.ai. If none counts as yours, no installed mod runs its hooks.
- **Users can’t undo it**: the guard reads the option from managed settings only, so the same entry in a user, project, or local settings file, or in a file passed with `--settings`, changes nothing
- **A file or MDM policy covers every provider**: when you deliver the option as a file or through MDM, it works the same way on Amazon Bedrock, Google Cloud’s Agent Platform, and Microsoft Foundry. For delivery from the claude.ai admin console, see [Platform availability](../13-Enterprise-Admin/server-managed-settings.md#platform-availability)
- **Users’ other customizations keep working**: their [hooks in settings files](../07-Hooks/hooks.md) and in plugins’ `hooks/hooks.json`, status lines, and `/goal` aren’t affected
- **Built-in mods keep running**: mods built into Claude Code, such as `AGENTS.md` support, each have [their own switch](plugins-mods-overview.md#mods-built-into-claude-code)

To confirm the option on a user’s machine, start Claude Code there with `--plugin-dir` and the path of a directory that holds a mod, such as `claude --plugin-dir ./first-mod`. The mod’s hooks don’t run, and the transcript and the debug log have the [guard’s message](plugins-mods-troubleshoot.md#messages-from-the-built-in-guard), which names the mod and `allowManagedModsOnly`. If the message isn’t there, see [Check that a policy is in force](../13-Enterprise-Admin/managed-settings.md#check-that-a-policy-is-in-force) and the [rules that decide whether an option takes effect](#set-options-on-the-built-in-guard). If you set `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` to `0` during early access, replace it with this option. Claude Code v2.1.287 and later ignores the variable at any value, so a `0` there leaves mods on.


[​](#know-what-happens-by-default)

Know what happens by default

With no mod settings of your own, this is what your users get:

- **Mods are on.** A user can install a plugin that contains a mod from any marketplace your plugin settings allow, or load one from a directory with `--plugin-dir`.
- **A built-in guard runs first.** Claude Code loads a built-in mod named `sec-default@builtin` ahead of every mod a user installs. Users can’t turn it off. `/plugin` and the debug log list it as `cc-plugin-sec-default`. The guard loads when either of these is true:
  - The machine has managed settings
  - The session runs on [`/login` credentials](../02-Claude-Code-CLI/authentication.md#authentication-precedence) for a Team or Enterprise plan

  A user who authenticates with an API key, or through Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry, gets the guard only on a machine that has managed settings. A session that runs on a long-lived [`CLAUDE_CODE_OAUTH_TOKEN`](../02-Claude-Code-CLI/authentication.md#generate-a-long-lived-token) doesn’t count as that login either, even when a Team or Enterprise login is stored on the machine.
- **The guard protects what you manage.** A user’s mod can’t change what your managed hooks receive or decide, the system prompt, your managed `CLAUDE.md` and other managed instructions, what any mod reads as settings, or the tools and descriptions of your managed MCP servers.
- **Outside what you manage, a user’s mod acts as that user.** The mod keeps that user’s permissions: it can still read and write files, start processes, and make network requests. It can also rewrite tool calls and prompts, deny a tool call, and draw in the interface.
- **Permission rules and `PreToolUse` hooks take precedence over a user’s mod.** With the guard loaded, by default a user’s mod can’t approve a call that a `deny` rule refuses or an `ask` rule prompts for, whichever settings file holds the rule. The mod can’t approve a call that any `PreToolUse` hook blocked, either. Permission rules and `PreToolUse` hooks apply to Claude’s tool calls, not to a mod’s [`$.fs` and `$.process` calls](plugins-mods-api.md#reach-files-processes-and-the-network). With `Read(.env)` denied, a mod can still read that file with `$.fs.read` or start a program that does. To limit a mod’s own `$.fs` and `$.process` calls, [keep the mod from loading](#stop-user-installed-mods-from-loading) or handle them in a [policy mod](#enforce-a-policy-with-a-mod-of-your-own).
- **A user’s mod can still skip some prompts.** In [auto mode](../02-Claude-Code-CLI/permission-modes.md#eliminate-prompts-with-auto-mode), a call the mod approves runs without a classifier check, unless the guard is loaded and the session runs with [`--permission-prompt-tool`](../02-Claude-Code-CLI/cli-reference.md#cli-flags) or an Agent SDK [`canUseTool` callback](../05-Agent-SDK/agent-sdk-user-input.md#handle-tool-approval-requests). There the classifier checks the call first. For the prompts a mod’s approval doesn’t skip, see [Extend permissions with hooks](../02-Claude-Code-CLI/permissions.md#precedence-among-rules-hooks-and-mods).

The guard’s source is public in the [`mods/sec-default` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods/sec-default).


[​](#know-which-controls-still-apply)

Know which controls still apply

Mods don’t replace the controls you already have:

- **Settings hooks keep working.** Command, HTTP, prompt, and agent hooks in settings files and in plugins’ `hooks/hooks.json` run as before, alongside mods.
- **Deny rules take precedence where the guard loads.** A user’s mod can’t approve a call that a `deny` rule refuses, unless you set [`allowModsToOverrideDenyRules`](#set-options-on-the-built-in-guard).
- **Managed hooks run first.** A `PreToolUse` hook in managed settings runs before any mod’s [`tool.call`](plugins-mods-reference.md#tools) hook, and when that managed hook blocks a tool call, no mod’s `tool.call` hook runs for that call. If the call goes ahead and a mod rewrites it, your managed hooks run again on the rewritten call, so they can still block it. `PreToolUse` hooks from other settings files and from plugins run after the last mod. If a mod answers the tool call itself, the tool never runs and neither do those hooks. See [The order mods run in](plugins-mods-events.md#the-order-mods-run-in).
- **Network policy covers `$.http.fetch`.**
  - **Your organization’s policy doesn’t allow WebFetch**: Claude Code also refuses every mod’s `$.http.fetch` request. See [WebFetch availability](../02-Claude-Code-CLI/tools-reference.md#webfetch-availability).
  - **You turn off the WebFetch tool with [`CLAUDE_CODE_DISABLE_WEB_FETCH`](../02-Claude-Code-CLI/env-vars.md#variables) or a `WebFetch` [deny rule](../02-Claude-Code-CLI/permissions.md#tool-specific-permission-rules), in managed settings or any other settings file**: mods can still make `$.http.fetch` requests. To stop them for every mod your users install, [keep user-installed mods from loading](#stop-user-installed-mods-from-loading), or hook `http.fetch` in a [policy mod](#enforce-a-policy-with-a-mod-of-your-own) and refuse requests from users’ mods. Give that hook a [`.catch` handler](plugins-mods-events.md#handle-a-hook-that-fails) that refuses too, because Claude Code skips a hook that fails and the request then goes ahead. If you only [restrict which plugins users can install](plugins-org.md#restrict-what-users-can-install), fewer mods load, but those you allow still make these requests.
  - **You set [`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](../02-Claude-Code-CLI/env-vars.md)**: a mod that you or your users install can still make these requests. The variable stops only the [mods built into Claude Code](plugins-mods-overview.md#mods-built-into-claude-code), plus any `$.http.fetch` request that carries the session’s Anthropic credentials. Before v2.1.288, the variable stopped every mod’s `$.http.fetch` request.

  None of these covers a program the mod starts with `$.process.run`, which reaches the network with the user’s own access.
- **Plugin controls cover mods.** A mod is a plugin, so the [settings that restrict what users can install](plugins-org.md#restrict-what-users-can-install), such as `strictKnownMarketplaces`, decide whether it can be installed at all.
- **Mods can’t change the permission prompt.** A mod can restyle much of Claude Code’s interface, but not the permission prompt, so it can’t change what a prompt shows. A mod can still approve or deny a tool call before the prompt appears, as [Know what happens by default](#know-what-happens-by-default) describes.
- **Trust prompts come first.** In an interactive session in a directory the user hasn’t trusted yet, no mod loads until they answer the trust prompt.
- **`--safe-mode` turns installed mods off, yours included.** Start a session with `claude --safe-mode` to check whether a mod caused a problem.

None of these controls sandboxes a mod. A mod you allow runs as the user, with the user’s access to files, processes, and the network.


[​](#decide-whether-to-leave-mods-on)

Decide whether to leave mods on

A mod can do more than the other parts of a plugin because it runs inside Claude Code. It sees every prompt and tool call, can change them, and can allow or deny a tool call before a permission prompt appears. What a user can load as a mod depends on the plugin controls you already have:

| Your plugin controls today                         | What a user can load as a mod                                                                                                                                                                                                             |
|:---------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| None                                               | A mod from any marketplace, from any directory with `--plugin-dir`, or that Claude writes during a session                                                                                                                                |
| A marketplace allowlist                            | A mod from the marketplaces you allow, or from any directory with `--plugin-dir`. A mod Claude writes during a session loads only when the allowlist [includes `skills-dir`](plugins-org.md#keep-skills-directory-plugins-loading). |
| A marketplace allowlist and `disableSideloadFlags` | A mod from the marketplaces you allow                                                                                                                                                                                                     |

[Manage plugins for your organization](plugins-org.md) lists the ways a plugin loads and the setting that controls each. To check the mods in a marketplace before your users install them, see [Review what a mod can do](#review-what-a-mod-can-do). To keep users’ mods out until you’ve done that, see [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading).


[​](#review-what-a-mod-can-do)

Review what a mod can do

You can see what a mod is able to do without running it. In your shell, run `claude plugin validate` on the plugin’s directory:

```python
claude plugin validate ./some-mod
```

The `hooks:` and `calls:` lines in the output describe the mod’s code:

```python
  ❯ ./register.js hooks: session.start, tool.call, ui.render{component=Pane}
  ❯ ./register.js calls: $.fs.read, $.http.fetch, $.store.set, $.ui.open
```

The `hooks:` line lists the events the mod receives. The `calls:` line lists the mods API methods its code calls. The [mods API](plugins-mods-api.md), written `$` in a mod’s code, is how a mod reaches files, processes, and the network. Claude Code refuses to load a mod that uses the mods API in a way this command can’t read. Look at the `calls:` line for these:

| Call                               | What it means                                                                                                                                                                               |
|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.fs.read`, `$.fs.write`          | Reads or writes files anywhere the user can                                                                                                                                                 |
| `$.process.run`, `$.process.spawn` | Starts programs as the user                                                                                                                                                                 |
| `$.http.fetch`                     | Makes network requests                                                                                                                                                                      |
| `$.env.get`, `$.settings.read`     | Reads environment variables and settings, which can hold API keys. An `env reads:` line in the output names each variable.                                                                  |
| `$.env.set`                        | Sets an environment variable for Claude Code and for every command and MCP server it starts afterward, which can change what those programs run. An `env writes:` line names each variable. |
| `$.mcp.call`                       | Calls a tool on a connected MCP server, under the session’s permission rules                                                                                                                |
| `$.model.complete`                 | Uses the user’s plan or API key for model calls                                                                                                                                             |
| `$.prompt.submit`                  | Submits a prompt, and can send it as the user’s own words                                                                                                                                   |
| `$.session.send`                   | Sends a message that another session’s, subagent’s, or [teammate’s](../09-Agents-Patterns/agent-teams.md) Claude reads                                                                                      |

In the `hooks:` line, [`tool.call`](plugins-mods-reference.md#tools) and [`prompt.submit`](plugins-mods-reference.md#prompts-and-what-claude-reads) mean the mod sees every tool call and every prompt, and can change them. [`session.append`](plugins-mods-reference.md#session) means the mod can rewrite each row of the conversation before it’s stored. [`ui.render{component=AskUserQuestion}`](plugins-mods-interface.md#change-what-claude-code-already-draws) means the mod can redraw the dialog Claude uses to ask the user a question. `tool.check` means the mod can approve or deny a tool call before a permission prompt appears. [Know what happens by default](#know-what-happens-by-default) lists which of your rules and hooks take precedence over its answer.


[​](#choose-how-much-to-allow)

Choose how much to allow

Mod policies range from no installed mods at all to any mod a user chooses, with your own mod checking the others, and each one is a few managed settings. Find the policy you want in the first column and set what the second column names. [Deploy managed settings](../13-Enterprise-Admin/managed-settings.md) covers where managed settings live.

| What you want                                                      | Settings                                                                                                                                                                       |
|:-------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| No installed mod runs, with settings hooks untouched               | Set [`allowManagedModsOnly`](#set-options-on-the-built-in-guard) and deploy no mods of your own                                                                                |
| No installed mods and no hooks at all, your managed hooks included | Set `disableAllHooks` to `true`                                                                                                                                                |
| Only your organization’s mods                                      | Set the guard’s [`allowManagedModsOnly` option](#stop-user-installed-mods-from-loading), and [install your mods](#install-your-organizations-mods) so that they count as yours |
| Any mod from marketplaces you approve                              | Keep your [marketplace restrictions](plugins-org.md#restrict-what-users-can-install), and set `disableSideloadFlags` to `true`                                           |
| Any mod, with your own mod checking the others                     | [Install your mod](#install-your-organizations-mods), and list it with `sec-default@builtin` in `prependPlugins`                                                               |

What each setting does:

- **`allowManagedModsOnly`**: an option on the built-in guard. Claude Code refuses users’ own mods, so none of their hooks run. Users’ settings hooks, status lines, and `/goal` keep working. [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading) lists what it covers.
- **`allowManagedHooksOnly`**: a wider setting. Only [your organization’s mods](#install-your-organizations-mods) and the mods built into Claude Code load. A mod a user installed themselves doesn’t. The setting also blocks hooks in users’ own settings files. Read [What runs under `allowManagedHooksOnly`](../02-Claude-Code-CLI/settings-reference.md#what-runs-under-allowmanagedhooksonly) before you set it.
- **`disableAllHooks`**: the widest setting. In managed settings, it stops the mods in every installed plugin, yours included, and turns off every hook in settings files, so a `PreToolUse` hook in your managed settings no longer blocks anything. Custom status lines and `/goal` stop working too. Read [`disableAllHooks`](../02-Claude-Code-CLI/settings-reference.md#disableallhooks) before you set it.
- **`disableSideloadFlags`**: rejects `--plugin-dir` and `--plugin-url` at startup, and keeps mods Claude writes during a session from loading. The setting also rejects `--agents` and `--mcp-config`. Read [`disableSideloadFlags`](../02-Claude-Code-CLI/settings-reference.md#disablesideloadflags) before you set it.

Mods built into Claude Code, such as `AGENTS.md` support, aren’t affected by these settings. Each has [its own switch](plugins-mods-overview.md#mods-built-into-claude-code). A user whose mod was refused or didn’t load finds the reason in their debug log. [Refusal messages](plugins-mods-troubleshoot.md#refusal-messages) lists the lines for `allowManagedHooksOnly` and `disableAllHooks`, and [Messages from the built-in guard](plugins-mods-troubleshoot.md#messages-from-the-built-in-guard) has the line for `allowManagedModsOnly`.


[​](#allow-only-your-organization’s-mods)

Allow only your organization’s mods

To run your organization’s mods and block the ones users bring, deploy the settings from the **Only your organization’s mods** row of the [policy table](#choose-how-much-to-allow), plus `disableSideloadFlags`. With this complete `managed-settings.json`, Claude Code refuses users’ own mods, so none of their hooks run, and your policy mod runs ahead of other mods:

managed-settings.json

```python
{
  "extraKnownMarketplaces": {
    "acme-tools": {
      "source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
    }
  },
  "enabledPlugins": { "acme-guard@acme-tools": true },
  "prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"],
  "pluginConfigs": {
    "cc-plugin-sec-default@builtin": {
      "options": { "allowManagedModsOnly": true }
    }
  },
  "disableSideloadFlags": true
}
```

Each group of keys does one job:

- **`extraKnownMarketplaces`, `enabledPlugins`, and `prependPlugins`**: install your mod so that it counts as yours, and run it first with the guard after it. [Install your organization’s mods and set the order](#install-your-organizations-mods) covers the directory these keys point at.
- **`pluginConfigs`**: sets the guard’s `allowManagedModsOnly` option, so Claude Code refuses users’ own mods. Their settings hooks, status lines, and `/goal` keep working.
- **`disableSideloadFlags`**: see [`disableSideloadFlags`](../02-Claude-Code-CLI/settings-reference.md#disablesideloadflags) for the flags it rejects at startup

To confirm the policy on a test machine, in your shell start a session with `claude --debug` and read the debug log:

- **Your mod**: its `hooks module` line has `tier prepend`
- **A mod the user installed**: a line reads `refused by cc-plugin-sec-default: mods are limited to your organization's by policy (allowManagedModsOnly)`. An earlier line says that mod’s hooks module `loaded`, so look for the refusal.
- **A plugin directory**: `claude --plugin-dir ./any-mod` exits with a message that starts `--plugin-dir is disabled by your organization's managed settings (disableSideloadFlags)`

To also limit which marketplaces users can add, combine this file with your [marketplace restrictions](plugins-org.md#restrict-what-users-can-install).


[​](#apply-your-plugin-controls-to-mods)

Apply your plugin controls to mods

A mod is a plugin, so the ways you [manage plugins for your organization](plugins-org.md) also apply to a plugin that holds a mod:

- **See which plugins load across your fleet**: [Audit and review](plugins-org.md#audit-and-review)
- **Decide when a plugin you reviewed can update**: [Set update policy](plugins-org.md#set-update-policy)
- **Give one group a different policy, such as a pilot**: [Plan for what managed settings can’t enforce](plugins-org.md#plan-for-what-managed-settings-can%E2%80%99t-enforce)
- **Check which apps and session kinds apply the plugin keys**: [When each surface applies the plugin keys](plugins-org.md#when-each-surface-applies-the-plugin-keys)
- **Set up CI and containers**: [Seed containers and CI](plugins-org.md#seed-containers-and-ci)
- **Offer mods your users may install**: [Host a marketplace](plugins-host-marketplace.md). A mod that Claude Code copies from a GitHub, git, URL, or npm source counts as a user’s, not as [your organization’s](#install-your-organizations-mods).


[​](#set-options-on-the-built-in-guard)

Set options on the built-in guard

The built-in guard takes options. Set them in managed settings under `pluginConfigs`, keyed by `cc-plugin-sec-default@builtin`, as the example in [Stop user-installed mods from loading](#stop-user-installed-mods-from-loading) does. The table gives what your users get with each option unset and with it set to `true`:

| Option                         | Unset                                       | `true`                                                                                                                                                                                                                 |
|:-------------------------------|:--------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allowManagedModsOnly`         | Users’ own mods run                         | Only [your organization’s mods](#install-your-organizations-mods), and mods built into Claude Code, run their hooks. Claude Code refuses every other mod, including one a user installed or named with `--plugin-dir`. |
| `allowModsToOverrideDenyRules` | Deny rules take precedence over users’ mods | A user’s mod that approves tool calls can approve a call that a `deny` rule refuses                                                                                                                                    |

These rules decide whether an option takes effect:

- **The id has one form here**: Claude Code reads the options only under `cc-plugin-sec-default@builtin`. `prependPlugins` accepts `sec-default@builtin` as well, and `pluginConfigs` doesn’t.
- **Only managed settings count**: the same entry in a user, project, or local settings file, or in a file passed with `--settings`, neither sets an option nor loosens one
- **The guard has to load**: if you set `prependPlugins`, [name the guard in the list](#install-your-organizations-mods). Where the guard doesn’t load, neither option applies.
- **The guard fails closed**: if the guard can’t read managed settings, it refuses every user’s mod at load. If it can’t check the deny rules for a call that a user’s mod approved, it refuses the call.

The [messages from the built-in guard](plugins-mods-troubleshoot.md#messages-from-the-built-in-guard) are what your users see when either option applies.


[​](#run-your-organization’s-own-mods)

Run your organization’s own mods

You can deploy mods of your own to every user, choose where they run relative to users’ mods, and use one to enforce a policy.


[​](#install-your-organizations-mods)

Install your organization’s mods and set the order

Your organization’s mods load where users’ mods don’t and can run ahead of them, so Claude Code has to be able to tell that a mod came from you. It treats a mod as your organization’s only when all of these are true:

- Managed `enabledPlugins` sets the mod’s plugin to `true`
- Managed settings name the plugin’s [marketplace](plugin-marketplaces.md) as a directory on the user’s machine, by absolute path. An `extraKnownMarketplaces` entry does that and registers the marketplace for the user too.
- The marketplace lists the plugin by a relative path, so Claude Code [loads it in place](plugins-loading.md#in-place-and-copied-plugins) from that directory

To meet them, have your device management copy the marketplace directory to the same path on every machine. Make the directory and every directory above it writable only by an administrator, as the managed settings file is. Anyone who can write there can rewrite your mod. Managed settings you deliver from the claude.ai admin console can carry the keys, but they can’t put the directory on a machine. The directory holds the marketplace’s manifest and the plugin:

```python
/opt/acme/claude-plugins/
├── .claude-plugin/
│   └── marketplace.json
└── plugins/
    └── acme-guard/
        ├── .claude-plugin/
        │   └── plugin.json
        └── hooks/
            ├── hooks.json
            └── register.js
```

The manifest lists the plugin by its path relative to that directory:

/opt/acme/claude-plugins/.claude-plugin/marketplace.json

```python
{
  "name": "acme-tools",
  "owner": { "name": "Acme" },
  "plugins": [
    { "name": "acme-guard", "source": "./plugins/acme-guard", "description": "Acme policy mod" }
  ]
}
```

A plugin that Claude Code copies into its cache counts as a user’s, even when managed `enabledPlugins` enables it. That covers every plugin from a GitHub, git, URL, or npm source. Its mod runs among users’ mods, `prependPlugins` and `appendPlugins` skip it, `allowManagedModsOnly` refuses it, and `allowManagedHooksOnly` keeps it from loading. The user’s debug log has a line that starts with the plugin’s id and `is enabled by managed settings, but`. Claude Code fires an event each time it’s about to act, such as run a tool, and passes it to each mod in turn. A mod that counts as yours [runs before users’ mods](plugins-mods-events.md#the-order-mods-run-in) even when you list it nowhere. To set its place, list its id in one of two settings. The id is the plugin’s name, `@`, and the marketplace’s name, such as `acme-guard@acme-tools`.

- **`prependPlugins`**: your mod sees every event before any user’s mod and every result after. It can change the event, refuse it, or skip the users’ mods.
- **`appendPlugins`**: your mod runs after every user’s mod, so it sees only the events those mods pass on, in the form they pass them

This example declares the `acme-tools` marketplace at `/opt/acme/claude-plugins`, enables `acme-guard` from it, and runs that mod first, with the built-in guard after it:

managed-settings.json

```python
{
  "extraKnownMarketplaces": {
    "acme-tools": {
      "source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
    }
  },
  "enabledPlugins": { "acme-guard@acme-tools": true },
  "prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"]
}
```

Each key does one job:

- **`extraKnownMarketplaces`**: names the directory that holds the `acme-tools` marketplace. `path` is the absolute path of the directory that contains `.claude-plugin/marketplace.json`.
- **`enabledPlugins`**: turns `acme-guard` on for every user who receives these managed settings
- **`prependPlugins`**: puts `acme-guard` first and the built-in guard second, both ahead of any mod a user installs. Claude Code follows the order you list.

To confirm that a user’s machine received the settings, see [Check that a policy is in force](../13-Enterprise-Admin/managed-settings.md#check-that-a-policy-is-in-force). To confirm where the mod runs, start a session on that machine with `claude --debug` and search the [debug log](plugins-mods-troubleshoot.md#read-the-debug-log) for the mod’s id:

- **`hooks module acme-guard@acme-tools loaded`, with `tier prepend`**: the mod counts as your organization’s and runs first
- **The same line with `tier user`**: Claude Code treats it as a user’s mod. A second line, `prependPlugins names acme-guard@acme-tools, which is not an enabled managed plugin with a hooks module; skipped`, says the list skipped it.

These rules decide which ids in the two lists take effect:

- **The list replaces the default**: when you set `prependPlugins` in managed settings, name `sec-default@builtin` in it to keep the built-in guard. The guard is built in and needs no `enabledPlugins` entry.
- **Your own ids must count as yours**: in managed settings, Claude Code skips an id whose plugin doesn’t meet the conditions for an organization’s mod
- **Repositories can’t set them**: Claude Code reads both settings from managed settings, never from a repository’s settings file. A user can set them in `~/.claude/settings.json` to order their own mods only on a machine with no managed settings, and only when the session’s credential isn’t a Team or Enterprise `/login`. Anywhere else, Claude Code ignores both keys in user settings. A list there neither adds nor removes the built-in guard.


[​](#enforce-a-policy-with-a-mod-of-your-own)

Enforce a policy with a mod of your own

To keep every user’s mod out, you don’t need a mod of your own. Set [`allowManagedModsOnly`](#stop-user-installed-mods-from-loading). Write a policy mod when you want to allow some users’ mods and refuse others, or to record what mods do. Each time another mod is about to load, your mod receives the list that `claude plugin validate` prints, in an event named [`plugin.register`](plugins-mods-reference.md#other-mods). A mod in `prependPlugins` can read that list and refuse the mod. It can also [handle any mods API call by name](plugins-mods-api.md#reach-files-processes-and-the-network) to record or refuse that call for every other mod. The name is the method without the `$.`, so a hook on `fs.write` sees every `$.fs.write` call. This policy mod refuses any user’s mod whose own code calls `$.process.run` or `$.process.spawn`. It also keeps an audit log, writing each tool call and each file a mod writes to the debug log. Because it runs first, the log records what was requested, before any user’s mod changes it. Save it as `acme-guard/hooks/register.js`:

acme-guard/hooks/register.js

```python
// The methods no user's mod may call, each spelled namespace.method
const BLOCKED_CALLS = ['process.run', 'process.spawn']

export function register(on) {
  // Runs each time another mod is about to load
  on('plugin.register', async ($, e, next) => {
    // Keep the calls in that mod's code that are on the blocked list
    const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))
    if (e.tier === 'user' && blocked.length > 0) {
      // Returning refuse keeps the mod from loading, and the text is the reason
      return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }
    }
    // Let every other mod load
    return next(e)
  })

  // Record each tool call, then let it go ahead unchanged
  on('tool.call', async ($, e, next) => {
    $.ui.log('audit tool.call ' + e.tool, { to: 'debug' })
    return next(e)
  })

  // Record which mod wrote a file, then the path, quoted because the mod chose it
  on('fs.write', async ($, e, next) => {
    $.ui.log('audit fs.write by ' + next.origin.plugin + ' ' + JSON.stringify(e.path), { to: 'debug' })
    return next(e)
  })
}
```

The file registers three hooks:

- **`plugin.register`**: decides whether another mod loads. It refuses a user’s mod that calls a blocked method and passes every other mod on.
- **`tool.call`**: writes a line such as `audit tool.call Bash` to the debug log for each tool call, and changes nothing
- **`fs.write`**: writes a line such as `audit fs.write by reader "/tmp/notes.md"` for each `$.fs.write` call another mod makes, and changes nothing. The mod’s name comes first and the path is quoted, so a path that a mod picks can’t pass for another field of the line.

The `plugin.register` hook reads two fields of the event:

- **`e.tier`**: where the mod would run, one of `prepend`, `user`, `append`, or `builtin`. Every mod a person installs is `user`.
- **`e.uses.calls`**: the mods API methods the mod calls, each written `namespace.method` such as `process.run`, without the `$.` that `claude plugin validate` prints

When a user installs a mod that calls `$.process.run`, the mod doesn’t load, and their debug log has a line that ends with `refused by acme-guard:` and your reason. The refusal also reaches the transcript in a [session that hot-reloads a plugin directory](plugins-mods-troubleshoot.md#find-out-why-a-mod-does-nothing). To block a call without refusing the whole mod, return `{ deny: 'your reason' }` from a hook on that call’s name. To send the audit lines somewhere other than the debug log, call `$.http.fetch` from the same hooks. A session can run without your mod. If the worker thread that runs installed mods [crashes three times](plugins-mods-troubleshoot.md#mods-that-run-in-the-hooks-worker-are-off-for-this-session), Claude Code unloads every mod that isn’t built in, including yours, until the user runs `/reload-plugins` or starts a new session. And a user who starts Claude Code with `--safe-mode` runs without installed mods, yours included. [Create a mod](plugins-mods-create.md) covers the files a mod needs. [Test a policy mod](plugins-mods-test.md#test-a-mod-that-judges-other-mods) has a test file for this policy mod.


[​](#refuse-mods-when-your-check-fails)

Refuse mods when your check fails

If your `plugin.register` hook throws or exceeds its time limit, Claude Code skips the hook, so the check fails open and the mod it was checking loads. To fail closed and refuse users’ mods, move the check into a named function and add a `.catch` handler that returns the refusal. This version of the file shows the `plugin.register` hook only, so keep the two audit hooks from the first version in `register`:

acme-guard/hooks/register.js

```python
const BLOCKED_CALLS = ['process.run', 'process.spawn']

// The same check as before, moved into a function of its own
async function checkMod($, e, next) {
  const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))
  if (e.tier === 'user' && blocked.length > 0) {
    return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }
  }
  return next(e)
}

export function register(on) {
  // The handler runs only when checkMod throws or exceeds its time limit
  on('plugin.register', checkMod).catch(async ($, e, next) => {
    // Let your organization's mods and built-in mods load
    if (e.tier !== 'user') return next(e)
    // Refuse the user's mod that couldn't be checked
    return { refuse: 'Acme policy check failed, so this mod was not loaded' }
  })
}
```

With the handler in place, a mod that was being checked when the check threw or timed out doesn’t load, and the refusal line carries the second reason, as in `refused by acme-guard: Acme policy check failed, so this mod was not loaded`. The handler passes every mod outside the `user` tier to `next(e)`, so a failed check doesn’t stop the mods your organization lists. [Handle a hook that fails](plugins-mods-events.md#handle-a-hook-that-fails) covers `.catch` for other events.


[​](#next-steps)

Next steps

- [Plugin security](plugins-security.md): what any plugin can do on a user’s machine, and how to review one before it’s installed
- [Mods overview](plugins-mods-overview.md): what a mod is and how it compares to hooks, skills, and MCP servers
- [The order mods run in](plugins-mods-events.md#the-order-mods-run-in): how `prependPlugins` and `appendPlugins` fit with users’ mods
- [Settings and environment variables](plugins-mods-reference.md#settings-and-environment-variables): every setting named on this page in one table

---

## Use the mods API - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/api
Path: 08-Plugins-Skills/plugins-mods-api.md

## On this page

- [Add a command or a tool](#add-a-command-or-a-tool)
  - [Add a command](#add-a-command)
  - [Add a tool](#add-a-tool)
- [Call a model](#call-a-model)
  - [Send one prompt](#send-one-prompt)
  - [Use prompt caching](#use-prompt-caching)
  - [Choose between prompt and system](#choose-between-prompt-and-system)
  - [Check for cache hits](#check-for-cache-hits)
  - [What a model.complete hook receives](#what-a-model-complete-hook-receives)
- [Run work in the background](#run-work-in-the-background)
  - [Show something without starting a turn](#show-something-without-starting-a-turn)
  - [Start a turn from a background job](#start-a-turn-from-a-background-job)
  - [Stop background work](#stop-background-work)
  - [Calls and background work after the user interrupts a turn](#calls-and-background-work-after-the-user-interrupts-a-turn)
  - [Retry a refused or dropped call](#retry-a-refused-or-dropped-call)
  - [What refused calls return](#what-refused-calls-return)
  - [Work your mod started before the interruption](#work-your-mod-started-before-the-interruption)
- [Send and receive messages between sessions](#send-and-receive-messages-between-sessions)
- [Reach files, processes, and the network](#reach-files-processes-and-the-network)
- [Next steps](#next-steps)

Build

# Use the mods API

Copy pageCopy page

Call the mods API from a Claude Code mod to add commands and tools, call a model, run work on a timer, message other sessions, and reach files and the network.

Copy pageCopy page

The mods API is the set of methods a mod calls to act: add commands and tools, call a model, run work between events, and reach the file system, processes, and the network. Every hook receives it as its first argument, `$`, with the methods grouped in namespaces such as `$.ui` and `$.fs`. [Events](plugins-mods-events.md) decide when a hook runs, and the mods API is what the hook calls once it does. Build your [first mod](plugins-mods-create.md) before you start here. For every method, see [mods API methods](plugins-mods-reference.md#mods-api-methods) or read [the types for your build](plugins-mods-create.md#get-the-types-for-your-build).


[​](#add-a-command-or-a-tool)

Add a command or a tool

A mod can add a command for the user to run and a tool for Claude to call. Register both in a [`session.start`](plugins-mods-reference.md#session) hook. Claude Code waits for that hook before the first prompt, so what you register is available from the first turn.


[​](#add-a-command)

Add a command

A command is for the user. Register it, then handle [`command.run`](plugins-mods-reference.md#commands-and-configuration) for its name. This example adds a `/standup` command that takes an optional number of days:

```python
on('session.start', async ($, e, next) => {
  // Add /standup to the command list, with the description the user sees there
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// The matcher limits the hook to /standup, so other commands don't reach it
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args is the text typed after the command name, or an empty string
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
```

After the session starts, `/standup` appears with its description in the list you see when you type `/`. The `argumentHint` shows in the prompt after you type the command and a space, as in `/standup [days]`. When you run `/standup 3`, the second hook returns `Summary for the last 3 day(s): ...`, and the transcript shows that text after the plugin’s name. The hook never calls `next`, because the command has no behavior other than yours. The `text` you return prints in the transcript and Claude reads it. To print nothing, as a command that only opens a [pane](plugins-mods-interface.md#pick-where-to-draw) does, return `{}`. To let the command run while Claude is working, add `immediate: true` to the registration. Pick a name that no built-in command uses. Type `/` in a session to see them. `$.command.register` throws for a taken name, with a message such as `"/focus" refused: it is the built-in /focus`. A hook that throws is skipped, so the rest of your `session.start` hook doesn’t run either. Register commands last in that hook, or wrap the call in `try` and `catch`.


[​](#add-a-tool)

Add a tool

A tool is for Claude. Register it with a name, a description Claude reads, and a JSON Schema for its input. Claude sees it under a longer name made of `mcp__`, your plugin’s name, two underscores, and the name you registered. You handle its calls in a [`tool.call`](plugins-mods-events.md#guard-or-change-a-tool-call) hook filtered to that full name. This example, from a plugin named `my-mod`, registers `ticket`, so the full name is `mcp__my-mod__ticket`. It gives Claude a tool that looks up a ticket in an issue tracker:

```python
on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude decides when to call the tool from this description
    description: 'Look up a ticket by its id and return its title and status',
    // The arguments Claude has to send: one required string named id
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// The full tool name is mcp__, the plugin's name, and the registered name
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // The tool's arguments are fields of e, so the id is e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // Return a result either way, so Claude learns when the lookup failed
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
```

When you ask about a ticket, Claude can call `mcp__my-mod__ticket` with its id. The second hook fetches the ticket and returns the response body, which Claude reads as the tool’s result. When the server answers with an error status, Claude reads `Lookup failed with status` and the number.

When [MCP tool search](../06-MCP-Tools/General/mcp.md#scale-with-mcp-tool-search) defers a registered tool, Claude sees its name but not its description until it searches for it. If Claude should consider the tool on every turn, add [`isDeferred: false`](plugins-mods-reference.md#tools) to the registration to [load the full tool upfront](../06-MCP-Tools/General/mcp.md#exempt-a-server-from-deferral). The field requires Claude Code v2.1.293 or later, and earlier versions ignore it.


[​](#call-a-model)

Call a model

A mod can send its own requests to a model for a small job such as classifying or summarizing text. `$.model.complete` sends your prompt on its own, and `$.model.fork({ prompt })` sends the current conversation with your prompt at the end. This table compares what each request contains:

| In the request                      | `$.model.complete`                                                                                                             | `$.model.fork`                                                |
|:------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------|
| Model                               | The `model` you pass                                                                                                           | The session’s model                                           |
| System prompt                       | A short [attribution block](../13-Enterprise-Admin/llm-gateway-protocol.md#system-prompt-attribution-block), then your `system` if you pass one | The session’s system prompt                                   |
| Messages                            | One user message, your `prompt`                                                                                                | The conversation so far, then your `prompt` as a user message |
| CLAUDE.md and other project context | Not included                                                                                                                   | Included, as in the conversation’s last request               |
| Tools                               | None                                                                                                                           | Claude’s tools, which the model can’t call                    |

A fork repeats the conversation’s last request, so the Claude API serves most of it from the [prompt cache](../02-Claude-Code-CLI/prompt-caching.md) while the conversation is still cached. Both calls use the session’s credentials, so they bill to the user’s plan, API key, or cloud provider. [The types for your build](plugins-mods-create.md#get-the-types-for-your-build) document every `$.model` method.


[​](#send-one-prompt)

Send one prompt

Pass `model` and `prompt` to `$.model.complete`. `prompt` becomes the user message. To give the model instructions, such as a role or an output format, also pass `system`, which becomes the system prompt. This hook answers a `/triage` command, [registered as a command](#add-a-command), by asking a small model to label the text typed after it:

```python
on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // The system prompt sets the job, and the prompt carries the text to label
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // One word needs few tokens, and the call gives up after 15 seconds
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text exists only when the model answered, so check r.isAnswered first
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})
```

When you run `/triage the export button does nothing`, the mod sends that text to the model and prints its answer, such as `Label: bug`. When the model doesn’t answer, the label is `unknown`. A Claude API failure doesn’t reject the call, so check `r.isAnswered`, and read `r.reason` when it’s `false`. The call rejects for a request Claude Code won’t send, such as a model your organization blocks. [The types for your build](plugins-mods-create.md#get-the-types-for-your-build) list the other options, such as `effort`, and the [limits](plugins-mods-reference.md#limits) give the `maxTokens` default.


[​](#use-prompt-caching)

Use prompt caching

`$.model.complete` supports the Claude API’s [prompt caching](../04-API-Reference/Guides/build-with-claude-prompt-caching.md). The API caches the start of a request, called the prefix, up to a [cache breakpoint](../04-API-Reference/Guides/build-with-claude-prompt-caching.md#explicit-cache-breakpoints) that you set. When every call starts with the same long static content, such as instructions or reference material, set a breakpoint at the end of that content. Later calls then read it from the cache instead of paying the full input price for it. To set a breakpoint, pass `prompt` as an array of `{ text }` blocks instead of a string, and add `cache: true` to the last block of the static content. Claude Code sends that block with the API’s `cache_control` field. `system` takes the same array form. To decide between them, see [Choose between `prompt` and `system`](#choose-between-prompt-and-system).

Arrays of blocks require Claude Code v2.1.292 or later. Earlier versions reject an array in `prompt` with an error that ends with `takes { model, prompt } (host check)`, and they leave an array in `system` out of the request.

This version of the [`/triage` hook](#send-one-prompt) sends a long set of labeling rules before the text to label, with a breakpoint after the rules. `RULES` is a string of your own:

```python
on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    prompt: [
      // Identical on every call, so it forms the cached prefix
      { text: RULES, cache: true },
      // Changes on every call, so it goes after the breakpoint
      { text: e.args },
    ],
  })
  return { text: 'Label: ' + (r.isAnswered ? r.text.trim() : 'unknown') }
})
```

The TTL and the number of breakpoints have these limits:

- **TTL**: a cache entry lasts five minutes after its last use. The TTL comes from the user’s Claude Code settings, not from the call. For one hour, set [`subagentPromptCacheTtl`](../02-Claude-Code-CLI/prompt-caching.md#choose-the-ttl-yourself) to `1h`.
- **Breakpoints per request**: the API accepts [up to four](../04-API-Reference/Guides/build-with-claude-prompt-caching.md#when-to-use-multiple-breakpoints), and one more comes back as an `api-error` in `r.reason`


[​](#choose-between-prompt-and-system)

Choose between `prompt` and `system`

Put the static content that your calls share at the start of `prompt` unless you know that your requests go directly to the Claude API:

- **Directly to the Claude API, with an API key or a Claude subscription**: either field works
- **Through [Amazon Bedrock](../02-Claude-Code-CLI/amazon-bedrock.md), [Claude Platform on AWS](../02-Claude-Code-CLI/claude-platform-on-aws.md), [Google Cloud’s Agent Platform](../02-Claude-Code-CLI/google-vertex-ai.md), [Microsoft Foundry](../02-Claude-Code-CLI/microsoft-foundry.md), or an [LLM gateway](../13-Enterprise-Admin/llm-gateway.md)**: use `prompt`. Claude Code begins the system prompt with an [attribution block](../13-Enterprise-Admin/llm-gateway-protocol.md#system-prompt-attribution-block) whose fingerprint comes from the start of the user message. The `api.anthropic.com` endpoint strips that block before caching. Other endpoints receive it as part of the prompt, so a breakpoint in `system` can miss when `prompt` starts differently.
- **In a mod that other people run**: use `prompt`, because you don’t choose their provider

`system` comes before `prompt` in the prefix, so a breakpoint in `prompt` covers `system` too, and a call with a different `system` misses the cache.


[​](#check-for-cache-hits)

Check for cache hits

The result of `$.model.complete` has a `usage` object with the API’s [cache fields](../04-API-Reference/Guides/build-with-claude-prompt-caching.md#tracking-cache-performance). `usage.cache_creation_input_tokens` counts the tokens the call wrote to the cache, and `usage.cache_read_input_tokens` counts the tokens it read from the cache. Expect a write on the first call and reads on later calls within the TTL. If every call writes and none reads, the prefix differs between calls or the calls are further apart than the TTL. For a prefix that differs, see [Choose between `prompt` and `system`](#choose-between-prompt-and-system). If both fields stay at zero on calls the model answered, nothing was cached. Check for each of these causes:

- **The prefix is too short**: the API doesn’t cache a prefix under the model’s [minimum length](../04-API-Reference/Guides/build-with-claude-prompt-caching.md#cache-limitations), and it returns no error
- **Prompt caching is disabled**: when a [`DISABLE_PROMPT_CACHING` variable](../02-Claude-Code-CLI/prompt-caching.md#disable-prompt-caching) applies to the model, Claude Code removes the breakpoints and sends the text uncached
- **Your gateway strips `cache_control`**: a gateway can [remove the field and still return success](../02-Claude-Code-CLI/prompt-caching.md#where-the-cache-lives)
- **Another mod rewrites the start of the text**: Claude Code then [sends it without breakpoints](#what-a-model-complete-hook-receives)


[​](#what-a-model-complete-hook-receives)

What a `model.complete` hook receives

If you hook the [`model.complete`](plugins-mods-reference.md#mods-api-calls) event to inspect or change other mods’ requests, read the text from these fields:

- **`e.prompt`**: always a string. When the caller passed an array, it’s the blocks’ text concatenated in order.
- **`e.system`**: a string built the same way, or absent when the caller passed no `system`
- **`e.promptBlocks` and `e.systemBlocks`**: the caller’s arrays, each present when the caller passed an array for that field

Claude Code sends the strings your hook passes to `next` and uses the arrays you pass with them to place [cache breakpoints](#use-prompt-caching). It keeps the leading blocks that still match the start of the string, with their breakpoints, and sends the rest of the string with no breakpoint. For example, `next({ ...e, prompt: e.prompt + NOTE })` keeps the caller’s breakpoints, and a hook that changes the start of `prompt` removes them.


[​](#run-work-in-the-background)

Run work in the background

Work that outlives one event, such as checking on something once a minute, runs on a timer you start from `session.start`. A hook itself runs for one event and has a [time limit](plugins-mods-reference.md#limits) on its own execution time. Time spent waiting on `next` or on a mods API call doesn’t count, except a `$.clock.sleep`. `$.clock.every` and `$.clock.after` take the place of `setInterval` and `setTimeout`, with the delay in milliseconds first: `$.clock.after(5000, fn)` calls `fn` once, five seconds from now. Each returns a timer with a `cancel()` method, and `await $.clock.now()` gives the time in milliseconds. This hook looks up a pull request’s checks once a minute and shows the result under the prompt. `summarize` is a function of your own that turns the command’s JSON output into a few words:

```python
on('session.start', async ($, e, next) => {
  // Call the function every 60,000 milliseconds, starting one minute from now
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // Replace the line under the prompt with the latest summary
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // Return without waiting for the timer, so the session starts right away
  return next(e)
})
```

The session starts as usual. A minute later, a line appears under the prompt with a `⚠`, the mod’s name, and then `checks:` and your summary. It’s replaced once a minute after that. The timer’s callback runs outside any event, so it keeps running between turns and doesn’t start one. If the callback throws, the error goes to the [debug log](plugins-mods-troubleshoot.md#read-the-debug-log) and the timer runs again at the next interval.


[​](#show-something-without-starting-a-turn)

Show something without starting a turn

A background job can show the user something without starting a turn. Each of these calls puts text in a different place:

| Call                | What the user sees                                                                                                                                                                                                                                                         |
|:--------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.ui.status(text)` | One line under the prompt that stays until you change it. It starts with `⚠` and the mod’s name, as in `⚠ my-mod: checks: 3 passing`.                                                                                                                                      |
| `$.ui.toast(text)`  | A toast notification with the mod’s name that disappears after a few seconds. It’s a box at the top right in [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md), and one line at the right under the prompt in the classic renderer.                                              |
| `$.ui.log(text)`    | A dim line in the transcript that Claude doesn’t read. It starts with `●` and the mod’s name, as in `● my-mod: build finished`.                                                                                                                                            |
| `$.ui.notify(text)` | A notification from their terminal, outside Claude Code, sent the way their [`preferredNotifChannel`](../02-Claude-Code-CLI/settings-reference.md#preferrednotifchannel) setting says. The mod’s name is its title unless you pass a non-empty `{ title }`, which is shown as you give it. |

`$.ui.notify` is the only one of these that answers. It resolves to `{ isSent: true, channel }` when the user’s channel wrote the notification to their terminal. `channel` is the setting’s value that sent it, such as `kitty` or `terminal_bell`. The `terminal_bell` channel rings the bell and shows neither your text nor the title. Otherwise the call resolves to `{ isSent: false, reason }`:

| `reason`     | Meaning                                                                                                                                                                                                                                                        |
|:-------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `disabled`   | The user set `preferredNotifChannel` to `notifications_disabled`                                                                                                                                                                                               |
| `no-channel` | The setting is `auto` and their terminal has no notification Claude Code can send                                                                                                                                                                              |
| `no-surface` | The session has no terminal, as in a `-p` run, the Agent SDK, the desktop app, the VS Code extension, or a cloud session. Also the answer to a call made before the session starts, while your mod is loading, and to a call made while Claude Code is exiting |
| `refused`    | Another mod’s hook on `ui.notify` answered in Claude Code’s place without sending                                                                                                                                                                              |

The call rejects instead of resolving when `text` isn’t a string, when another mod’s hook on `ui.notify` returns `{ deny }`, or when writing to the terminal fails. Your mod can’t choose the channel, and Claude Code doesn’t limit how often a mod notifies. The user’s setting is the only switch. When the call reaches a running session, the user’s [`Notification` hooks](../07-Hooks/hooks.md#notification) run with the type `plugin_notification`. They run even when the answer is `disabled`, and in a session with no terminal, where the answer is `no-surface`. No hook runs for a call made before the session starts or while Claude Code is exiting. The hook your mod called `$.ui.notify` from is left out of that run, so a `classic.Notification` hook that notifies isn’t called again for its own notification. A terminal doesn’t report whether it showed the notification, so `isSent: true` means it was sent, not that the user saw it. This example notifies when the main conversation’s turn ends without an interruption, and falls back to a toast when no notification with text went out. It doesn’t wait for the notification before passing the event on, because the user’s `Notification` hooks run first and can take a while:

```python
on('turn.complete', ($, e, next) => {
  const isMainTurn = e.agentId === undefined && !e.isAborted
  const fallBack = () => $.ui.toast('Turn finished')
  if (isMainTurn) {
    $.ui.notify('Turn finished').then(sent => {
      const hasText = sent.isSent && sent.channel !== 'terminal_bell'
      if (!hasText) fallBack()
    }, fallBack)
  }
  return next(e)
})
```

In the [VS Code extension](plugins-mods-interface.md#draw-in-the-vs-code-extension), status lines and toasts show above the prompt box instead, and log lines in the conversation.


[​](#start-a-turn-from-a-background-job)

Start a turn from a background job

When a background job finds something that needs Claude’s attention, it can start a turn by submitting a prompt with `$.prompt.submit({ text })`. Claude reads the text after a sentence that names your mod as the sender. To send it as the user’s own words, without that sentence, add `asUser: true`. The call waits until the session is idle and then starts a new turn. It resolves when that turn starts, so don’t `await` it in a handler that runs while Claude is working.


[​](#stop-background-work)

Stop background work

Timers stop when the module reloads. For long-running work inside a hook, [`next.signal`](plugins-mods-reference.md#the-hook-function) is an `AbortSignal` that aborts when the event your hook is handling is abandoned, for example when the user interrupts, so pass it to anything long-running.


[​](#calls-and-background-work-after-the-user-interrupts-a-turn)

Calls and background work after the user interrupts a turn

The reason `the session was stopped and nobody has sent input since` in a result or error means the user stopped a running turn, such as with Esc or **Stop**. After that interruption, calls that start new work in the session are refused until the user’s next input, such as a message at the prompt. Apart from `$.prompt.submit` and `$.command.run`, this handling of calls and background work after an interruption requires Claude Code v2.1.304 or later.


[​](#retry-a-refused-or-dropped-call)

Retry a refused or dropped call

Make a refused or dropped call again from a [`turn.complete`](plugins-mods-events.md#follow-a-turn) hook. Calls stay refused until the user’s next input, and nothing queues or repeats them for you. When a call’s result or error contains one of these strings, keep its arguments for a later retry:

- **`the session was stopped and nobody has sent input since`**: the call was refused
- **`was removed from the queue before it ran`**: the prompt or command left the queue without running and was dropped, for example when the user stopped the turn

In this example, call `submitPrompt` from your background job in place of [`$.prompt.submit`](#start-a-turn-from-a-background-job). Put the `pendingText` variable and the `submitPrompt` function above [`register`](plugins-mods-reference.md#files), and the `turn.complete` hook inside it, because [validation](plugins-mods-create.md#check-what-claude-code-reads-from-your-mod) refuses `$` passed to a function declared inside `register`. When the result or the error carries one of those strings, `submitPrompt` keeps the text in `pendingText` for the `turn.complete` hook to submit again:

```python
// The text of the last refused or dropped prompt, or null
let pendingText = null

async function submitPrompt($, text) {
  let reason
  try {
    const result = await $.prompt.submit({ text })
    reason = result?.drop ?? ''
  } catch (error) {
    reason = String(error?.message ?? error)
  }
  if (reason.includes('the session was stopped and nobody has sent input since') ||
      reason.includes('was removed from the queue before it ran')) {
    pendingText = text
  }
  // Any other result or error isn't retried
}

on('turn.complete', async ($, e, next) => {
  if (pendingText !== null) {
    const text = pendingText
    // Empty pendingText before retrying, so that a second run of this hook
    // doesn't submit the same text.
    pendingText = null
    // Not awaited: the hook would stay open until the new turn starts
    submitPrompt($, text)
  }
  return next(e)
})
```

Before the user’s next input, `turn.complete` can fire for the interrupted turn itself and for a subagent’s turn, and calls made then are refused again. In that case `submitPrompt` keeps the text for a later `turn.complete`. For a call that rejects, such as `$.command.run`, catch the error and test its message the way `submitPrompt` tests `reason`.


[​](#what-refused-calls-return)

What refused calls return

In the results, `<reason>` stands for `the session was stopped and nobody has sent input since`:

| Some refused calls                                                                                                               | Result until the user’s next input                                                                                                                                                  |
|:---------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.prompt.submit`                                                                                                                | Resolves with `{ drop: '<reason>' }` and queues no prompt                                                                                                                           |
| `$.command.run`                                                                                                                  | Rejects with an error whose message ends with `<reason>`                                                                                                                            |
| [`$.agent.spawn`](plugins-mods-reference.md#mods-api-methods)                                                              | Resolves with `{ deny: '<reason>' }` and starts no subagent                                                                                                                         |
| `$.session.send` to a subagent that finished                                                                                     | Resolves with `{ isDelivered: false, reason: 'Agent "<id>" was not resumed: <reason>.' }`, where `<id>` is the subagent’s id. A subagent that’s still running receives the message. |
| `$.session.send` to an [agent team](../09-Agents-Patterns/agent-teams.md) teammate                                                               | Resolves with `{ isDelivered: false, reason: 'Teammate "<name>" was not messaged: <reason>.' }`, where `<name>` is the teammate’s name                                              |
| [`$.tool.call`](plugins-mods-reference.md#mods-api-methods) of Bash with `run_in_background: true`, Monitor, or CronCreate | Resolves with `{ deny: '<tool> was not run: it starts background work, a schedule or a goal, and <reason>' }`, where `<tool>` is the tool’s name                                    |

Along with these `$.session.send` and `$.tool.call` results, the user can see the [`Stopped:` toast](plugins-mods-troubleshoot.md#stopped-type-a-message-to-continue).


[​](#work-your-mod-started-before-the-interruption)

Work your mod started before the interruption

After the user stops a running turn, for example with Esc, some work your mod had already started is dropped and some waits for the user’s next input.

| Work                                                                                                           | What happens until the user’s next input                                                                                                                                  | What to do                                                                                   |
|:---------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------|
| A prompt from `$.prompt.submit` still in the queue                                                             | Dropped. The call resolves with `{ drop: 'the prompt was removed from the queue before it ran' }`.                                                                        | Submit it again as [Retry a refused or dropped call](#retry-a-refused-or-dropped-call) shows |
| A command from `$.command.run` still in the queue                                                              | Dropped. The call rejects with an error whose message includes `the command was removed from the queue before it ran`.                                                    | Run it again as [Retry a refused or dropped call](#retry-a-refused-or-dropped-call) shows    |
| A one-time job scheduled with a tool such as CronCreate that comes due before the user’s next input            | Starts no turn then, and runs after that input                                                                                                                            | Nothing needed                                                                               |
| A recurring job scheduled with a tool such as CronCreate that comes due before the user’s next input           | Starts no turn then. The job loses that run and keeps its schedule.                                                                                                       | Nothing needed                                                                               |
| An event from a monitor your mod started                                                                       | The monitor keeps running. An event the monitor raises before the user’s next input is dropped, except the notice that the monitor ended, which arrives after that input. | Nothing can be done                                                                          |
| A subagent your mod spawned and later resumed with `$.session.send` that finishes before the user’s next input | Its finish notice waits, and reaches the conversation after that input                                                                                                    | Nothing needed                                                                               |

When a scheduled job comes due or a monitor raises an event before the user’s next input, the user can see the [`Stopped:` toast](plugins-mods-troubleshoot.md#stopped-type-a-message-to-continue).


[​](#send-and-receive-messages-between-sessions)

Send and receive messages between sessions

A mod can send a plain-text message to another of your sessions, to one of this session’s subagents, or to a teammate in its [agent team](../09-Agents-Patterns/agent-teams.md). It can also observe the messages that arrive and leave. To send one, call `$.session.send({ to, text })`, which makes the same delivery the SendMessage tool makes. Set `to` by who receives the message:

- **Another of your sessions**: `{ sessionId }`
- **A subagent or teammate**: `{ agentId }`, with an id from `$.agent.list()`
- **The sender of a message you received**: the string address that message came from

The call resolves once the message is queued, with `{ isDelivered: true }`. When nothing was delivered, it resolves with `{ isDelivered: false, reason }`, and `reason` says why. This hook answers a `/ping` command, [registered as a command](#add-a-command), by asking the session whose id you type after it for a status:

```python
on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args is the session id typed after /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // The call resolves either way, so check isDelivered to learn what happened
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // An empty result prints nothing in this session's transcript
  return {}
})
```

When the message is queued, nothing appears in your session, and the other session’s Claude reads `Status? One line.` When nothing was delivered, a toast notification gives the reason. `session.receive` and `session.send` let a mod observe the messages. Return `next(e)` from both to pass each message through unchanged:

| Event             | Fires when                                                      | Useful fields                                                                                                                                                                                         |
|:------------------|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `session.receive` | A message arrives for this session, before Claude reads it      | `e.text`, and `e.origin.kind`, such as `peer` or `peer-send-message` for another session or agent, `task-notification`, or `scheduled-trigger`. Return `{ consumed: reason }` to keep it from Claude. |
| `session.send`    | A message is about to leave, from the SendMessage tool or a mod | `e.to`, `e.text`, and `e.origin.kind`, which is `model` or `plugin`                                                                                                                                   |

A session set to [refuse inbound messages](../02-Claude-Code-CLI/cross-session-messaging.md#control-inbound-messages) refuses a message before `session.receive` fires, so a hook never sees it. A message that’s held for your approval reaches the hook first, so a mod can read a message you haven’t approved yet. The hook’s `next(e)` rejects when the message isn’t delivered. The sender’s name on a received message is whatever the sender wrote, so don’t base a decision on it.


[​](#reach-files-processes-and-the-network)

Reach files, processes, and the network

A mod reaches the file system, processes, and the network through the mods API, with the same permissions as the user running Claude Code. The hooks module itself has no Node.js APIs, no timer globals such as `setTimeout`, and no network or file access of its own. Standard JavaScript and web APIs such as `URL`, `TextEncoder`, `AbortController`, and `crypto.subtle` are available. Each namespace below covers one kind of access:

| Namespace    | What it does                                                                                                                                                                                                                        |
|:-------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.fs`       | `read(path)`, `write(path, text)`, `exists(path)`, `stat(path)`, and `list(path)` work on files and directories                                                                                                                     |
| `$.process`  | `run(['git', 'status'])` starts a command and resolves when it exits. `spawn` streams a long-running command’s output.                                                                                                              |
| `$.http`     | `fetch(url, init)` over `http` or `https`. It resolves to `{ status, ok, headers, text }` once the body is read.                                                                                                                    |
| `$.store`    | A JSON key-value store of your plugin’s own, kept between sessions                                                                                                                                                                  |
| `$.env`      | `get` and `set` environment variables. Write the name as a string literal.                                                                                                                                                          |
| `$.settings` | `read` what the settings files and managed policy hold                                                                                                                                                                              |
| `$.session`  | `messages()` returns the transcript as a list of `{ role, text, toolUses }`. Also the working directory, model, and more. [`usage()`](plugins-mods-reference.md#mods-api-methods) returns context window use and plan limits. |
| `$.mcp`      | `call` a tool on a connected MCP server                                                                                                                                                                                             |

Files, processes, and requests have a few rules of their own:

- **Paths**: a relative path resolves against the working directory of the session, or of the subagent whose event the hook is handling
- **`$.fs.list`**: returns one directory’s entries as `{ name, kind, size, isLink }` and isn’t recursive
- **`$.process.run`**: takes an argument list and uses no shell. It resolves to `{ exitCode, stdout, stderr }` whatever the exit code. It rejects if the program can’t start or is still running at the timeout, which is 30 seconds by default, so wrap it in `try` and `catch`.
- **`$.http.fetch`**: follows up to five redirects. On a redirect to a different origin, it keeps only the `accept`, `accept-language`, `content-type`, and `user-agent` request headers you set and drops the rest, so a request that depends on another header, such as `Authorization`, can fail after that redirect. The [limits](plugins-mods-reference.md#limits) give its timeout and body sizes.

Every one of these calls is itself an event, named for its namespace and method without the `$.`, such as `fs.read` for `$.fs.read`. A mod [earlier in the chain](plugins-mods-events.md#the-order-mods-run-in) can observe, rewrite, or refuse your call, which is how an organization restricts what mods reach. A mod can refuse your `$.process.spawn` call after the command has produced output or exited, and nothing the command did is undone. The call then rejects with a message that ends with one of these strings and the refusing mod’s reason:

- **`$.process.spawn started, and a plugin withheld its result:`**: the refusing mod hadn’t read the command’s output to the end. Claude Code stops the command if it’s still running.
- **`$.process.spawn ran, and a plugin withheld its result:`**: the refusing mod had read the command’s output to the end, so the command had exited


[​](#next-steps)

Next steps

- [React to events](plugins-mods-events.md): hook tool calls, prompts, and turns
- [Draw in the interface](plugins-mods-interface.md): show what your mod collects in a pane or above the prompt
- [Test a mod](plugins-mods-test.md): stub any of these calls in a test
- [Mods reference](plugins-mods-reference.md): events, mods API methods, and limits

---

## Create a mod - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/create
Path: 08-Plugins-Skills/plugins-mods-create.md

## On this page

- [Ask Claude for a mod](#ask-claude-for-a-mod)
  - [Use the mod in other sessions](#use-the-mod-in-other-sessions)
  - [Sessions where a mod Claude writes can’t load](#sessions-that-skip-the-approval)
- [Write a mod yourself](#write-a-mod-yourself)
  - [How the example mod works](#how-the-example-mod-works)
- [Keep working on a mod](#keep-working-on-a-mod)
  - [Change a mod with Claude](#change-a-mod-with-claude)
  - [Get type definitions for your version](#get-the-types-for-your-build)
  - [Check what Claude Code reads from your mod](#check-what-claude-code-reads-from-your-mod)
  - [Test the mod](#test-the-mod)
- [Share your mod](#share-your-mod)
- [Next steps](#next-steps)

Mods

# Create a mod

Copy pageCopy page

Have Claude write a Claude Code mod from a description, or write one yourself that counts tool calls and adds a command. Learn the reload and validate loop.

Copy pageCopy page

A mod is a Claude Code [plugin](plugins.md) with an entry file, called the hooks module: a JavaScript or TypeScript file whose functions Claude Code calls when events happen. To make one:

- **Ask Claude to write it**: [describe what you want](#ask-claude-for-a-mod) in a Claude Code session
- **Write it yourself**: [follow the tutorial](#write-a-mod-yourself) to learn how a mod’s code works. You don’t need Node.js, a bundler, or a build step, because Claude Code loads `.js` and `.ts` files directly.

If you haven’t decided whether a mod is the right tool, read the [comparison on the overview](plugins-mods-overview.md#compare-mods-settings-hooks-skills-and-mcp-servers) first.

Use Claude Code v2.1.287 or later. In your shell, run `claude --version` to check. To see whether mods can load for you, see [Check whether mods can load](plugins-mods-troubleshoot.md#check-whether-mods-can-load).


[​](#ask-claude-for-a-mod)

Ask Claude for a mod

Describe the mod you want in an interactive Claude Code session, and Claude writes it. Claude works from a built-in [skill](skills.md) named `plugin-authoring`, which tells it where to write the mod, which events and methods your version has, and how the mod gets loaded. Claude can load the skill when you ask for a mod, or you can load it yourself by running `/plugin-authoring` at the Claude Code prompt. The mod runs once you approve it, except in [sessions where a mod Claude writes can’t load](#sessions-that-skip-the-approval).

1

Describe the mod

Ask for the mod in your own words, for example `make a mod that shows the current git branch above the prompt`. Claude writes the mod in a directory of its own in the session’s mods folder, which is `~/.claude/dev-mods/` followed by the session’s ID. A mod’s full path looks like `~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/`.

In the `default` and `acceptEdits` [permission modes](../02-Claude-Code-CLI/permission-modes.md), Claude Code asks you to approve each of the mod’s files before Claude creates it, because the mods folder is a [protected path](../02-Claude-Code-CLI/permission-modes.md#protected-paths). Approve each file as it comes up.

2

Approve the mod

When Claude saves the first file, Claude Code asks whether to enable hot reloading for the session. Hot reloading runs the mods Claude writes in this session and picks up each later change.Choose one of these answers:

- **Enable for this session**: the mods in the session’s mods folder load when the turn ends, and reload at the end of each turn that changes them. Your answer lasts for the session, including after you resume it.
- **Not now**: nothing loads for now. The files stay where Claude wrote them, and the mods load the next time that session starts. To keep a mod from ever loading, delete its directory.

3

Check that the mod loaded

Run `/plugin` at the Claude Code prompt and press Tab until the **Installed** tab is selected. It lists the mod, and you can turn it off there.

4

Try the mod

Use what you asked for. For the example prompt, the current branch name appears above the prompt box. If the mod doesn’t do what you wanted, tell Claude what to change. The mod reloads at the end of each turn that changes its files, so you can try the change as soon as Claude finishes.


[​](#use-the-mod-in-other-sessions)

Use the mod in other sessions

A mod Claude wrote loads only in the session that made it, and Claude Code deletes that session’s mods folder once it’s older than [`cleanupPeriodDays`](../02-Claude-Code-CLI/settings-reference.md#cleanupperioddays). To keep the mod, copy its directory out of the mods folder to a place of your own, such as `~/mods/git-branch`. Then choose how to load it:

- **In a session you start**: in your shell, run `claude --plugin-dir ~/mods/git-branch`
- **For other people**: [add it to a marketplace](#share-your-mod) so they can install it


[​](#sessions-that-skip-the-approval)

Sessions where a mod Claude writes can’t load

A mod Claude writes loads only after you approve it, in a trusted workspace where mods are allowed to run. In these sessions it doesn’t load:

- **Nobody is there to approve**: the session can’t show you a prompt, as in a `claude -p` run or [`dontAsk` mode](../02-Claude-Code-CLI/permission-modes.md)
- **The workspace isn’t trusted**: you haven’t accepted the trust prompt for the directory
- **Mods are disabled**: you started with `--safe-mode` or `--bare`, you set `disableAllHooks`, or your organization’s [managed settings block it](plugins-mods-admin.md#choose-how-much-to-allow)


[​](#write-a-mod-yourself)

Write a mod yourself

In this tutorial you build a mod named `first-mod` that counts the tool calls Claude makes, shows the count beside the spinner while Claude works, and adds a `/tally` command that prints it. You then read the type declarations Claude Code writes beside your mod and run `claude plugin validate`. Together they show you the events and methods your version offers and what Claude Code reads from your code. This recording shows the finished mod. The spinner counts tool calls, `/tally` prints the count, and an edit to the code takes effect while the session runs:

You write three files:

```python
first-mod/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.js
```

- **`plugin.json`**: the plugin’s [manifest](plugins-reference.md)
- **`hooks.json`**: [points to your code file](plugins-mods-reference.md#files)
- **`register.js`**: your code, called the hooks module

1

Create the plugin directory

Create the two directories that hold the files:

- Bash or Zsh

- PowerShell

```python
mkdir -p first-mod/.claude-plugin first-mod/hooks
```

```python
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
```

2

Write the manifest

A mod is a plugin, and a mod needs a [manifest](plugins-reference.md). This mod’s manifest has no special fields. Save this as `first-mod/.claude-plugin/plugin.json`:

first-mod/.claude-plugin/plugin.json

```python
{
  "name": "first-mod",
  "version": "0.1.0",
  "description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
  "author": { "name": "Your Name" }
}
```

3

Tell Claude Code where your code is

When Claude Code loads a plugin, it reads the plugin’s `hooks/hooks.json`. The `modules` key in that file gives the path to your code, and having it is what makes the plugin a mod. List one path, relative to `hooks.json`. Here it points to `register.js`, which you write in the next step.Save this as `first-mod/hooks/hooks.json`:

first-mod/hooks/hooks.json

```python
{
  "description": "The first-mod hooks module",
  "modules": ["./register.js"]
}
```

4

Write the code

This file is the mod’s code, called the hooks module. When the mod loads, Claude Code calls the `register` function the file exports and passes it a function named [`on`](plugins-mods-reference.md#the-hook-function). Each call to `on` registers an event handler, called a hook, for the event it names.Save this as `first-mod/hooks/register.js`:

first-mod/hooks/register.js

```python
// The count, shared by the hooks below
let calls = 0

// Claude Code calls this once when the mod loads
export function register(on) {
  // Runs when the session starts, before your first prompt
  on('session.start', async ($, e, next) => {
    // Add the /tally command
    await $.command.register({
      name: 'tally',
      description: 'Show how many tool calls Claude has made',
    })
    // Let the session start as usual
    return next(e)
  })

  // Runs each time Claude is about to use a tool
  on('tool.call', async ($, e, next) => {
    calls += 1
    // Ask Claude Code to draw the interface again, so the new count shows
    $.ui.invalidate('ui.render')
    // Let the tool run as usual
    return next(e)
  })

  // Runs when you type /tally, and only then, because of the matcher
  on('command.run', { command: 'tally' }, async () => {
    // The text to print in the transcript
    return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
  })

  // Runs each time Claude Code draws the spinner
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    // Keep Claude Code's spinner, with the count added after its word
    return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
  })
}
```

The file keeps a count in `calls` and registers four hooks:

- **[`session.start`](plugins-mods-reference.md#session)** runs when the session starts, before your first prompt, and again each time the mod reloads. It adds the `/tally` command to Claude Code.
- **[`tool.call`](plugins-mods-reference.md#tools)** runs each time Claude is about to use a tool. It adds one to `calls` and asks Claude Code to draw the interface again.
- **[`command.run`](plugins-mods-reference.md#commands-and-configuration)** runs when you type `/tally`. It returns the text to print.
- **[`ui.render`](plugins-mods-reference.md#interface)** runs each time Claude Code draws the spinner. It adds the count after the spinner’s word.

[How the example mod works](#how-the-example-mod-works) explains the three arguments each hook takes and what each one returns.

5

Load the mod

Start Claude Code with the `--plugin-dir` flag, which loads a plugin directory for one session without installing it:

```python
claude --plugin-dir ./first-mod
```

6

Try the mod

Ask Claude to do something that takes a few tool calls, such as `list the files here and read the README`. While Claude works, the spinner’s word is followed by a count that rises, as in `Thinking · tool calls: 2…`. When Claude finishes, type `/tally` and press Enter. The transcript shows `first-mod: Claude has made 2 tool calls since this mod loaded`, with your own count. Claude Code puts the plugin’s name in front of the command’s text.To check the command without an interactive session, run it in non-interactive mode:

```python
claude -p "/tally" --plugin-dir ./first-mod
```

```python
first-mod: Claude has made 0 tool calls since this mod loaded
```

If `/tally` isn’t in the command list, the module didn’t load. See [Find out why a mod does nothing](plugins-mods-troubleshoot.md#find-out-why-a-mod-does-nothing).

7

Change the code while the session runs

Leave the session open. In `register.js`, change `' · tool calls: '` to `' · tools used: '` in the `ui.render` hook and save. The highlighted line is the one that changes:

first-mod/hooks/register.js

```python
  // Runs each time Claude Code draws the spinner
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    // Keep Claude Code's spinner, with the count added after its word
    return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
  })
```

A line in the transcript says `first-mod` reloaded and lists its hooks, and the next spinner uses the new text, as in `Thinking · tools used: 1…`.


[​](#how-the-example-mod-works)

How the example mod works

Each function you pass to `on` is a hook, which is an event handler. Claude Code passes every hook the same three arguments:

- **The mods API**, named `$`: every method a mod can call to reach outside itself, in [namespaces](plugins-mods-reference.md#mods-api-methods) such as `$.ui` and `$.command`
- **The event**, named `e`: the [event’s input](plugins-mods-reference.md#events) as plain data, such as a tool call’s name and arguments
- **The next handler**, named [`next`](plugins-mods-events.md#how-a-hook-handles-an-event): a function that passes the event on to the other mods and then to Claude Code’s own behavior, and returns the result

The hooks in `first-mod` handle their events in these ways:

- **Observe**: the `session.start` hook registers the command, and the `tool.call` hook counts the call and asks for a redraw. Both return `next(e)`, so the session starts and the tool runs as usual.
- **Answer**: the `command.run` hook returns its own result and never calls `next`. The second argument to `on`, `{ command: 'tally' }`, is a filter, called a [matcher](plugins-mods-events.md#filter-which-events-a-hook-handles), so the hook runs only for `/tally`.
- **Rewrite**: the `ui.render` hook calls `next` with a copy of `e` whose `suffix` holds the count, so Claude Code draws its usual spinner with your text after the word

Claude Code watches a directory loaded with `--plugin-dir` and hot-reloads the hooks module when a file in it changes. Each reload runs `register` again, so `calls` resets to `0` and `/tally` starts counting again. To keep a value across reloads, see [Keep state](plugins-mods-interface.md#keep-state).


[​](#keep-working-on-a-mod)

Keep working on a mod

Once a mod loads, you can have Claude change it, check your code against the type definitions for your version, list the events and calls Claude Code finds in it, and test it.


[​](#change-a-mod-with-claude)

Change a mod with Claude

To change a mod you already have, start the session with `--plugin-dir` pointed at the mod’s directory, so that what Claude writes loads in the same session:

```python
claude --plugin-dir ./first-mod
```

Then ask for the change, for example `add a /tally-reset command to this mod that sets the tally back to zero`. Claude edits the hooks module, runs `claude plugin validate`, and fixes what it reports. A directory you load with `--plugin-dir` is a [protected path](../02-Claude-Code-CLI/permission-modes.md#protected-paths), so in `default` and `acceptEdits` modes you’re asked to approve each of Claude’s edits to the mod. The protected paths table gives the result for the other permission modes. Files Claude saves during its turn reload when the turn ends, so you can try `/tally-reset` as soon as Claude finishes.


[​](#get-the-types-for-your-build)

Get type definitions for your version

When Claude Code loads a mod from `--plugin-dir` in an interactive session, or a mod [Claude wrote for you](#ask-claude-for-a-mod), it writes TypeScript declaration files into the mod’s `.claude-plugin/types/` directory. They describe the exact events, mods API methods, and elements in the Claude Code version you’re running, so your editor can autocomplete and type-check your hooks. The directory holds these files:

| Path                                           | What it declares                                                                                                            |
|:-----------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------|
| `claude-code/index.d.ts`                       | Every event and its input and result, every mods API namespace and method, and the elements each surface can draw           |
| `claude-code-tools/index.d.ts`                 | The built-in tools’ inputs and results, so that checking `e.tool === 'Bash'` narrows `e`                                    |
| `claude-code-mcp/index.d.ts`                   | The inputs of the MCP tools that were connected the last time you saved a file in the mod                                   |
| `index.d.ts` in a directory named for a plugin | What that plugin adds to the mods API. There’s one directory for each plugin your `plugin.json` lists under `dependencies`. |
| `tsconfig.json`                                | Compiler options that fit a hooks module                                                                                    |

If your mod has no `tsconfig.json` of its own, Claude Code adds one at the mod’s root that extends the generated one, so your editor and `tsc -p ./first-mod` type-check the mod without more setup. The events and methods can change between releases, so trust these files over any page, this one included, when they disagree. `claude-code/index.d.ts` is the fullest reference for your build, with a comment and an example for every mods API method. To look something up, search the file for its name, such as `'tool.call'`.


[​](#check-what-claude-code-reads-from-your-mod)

Check what Claude Code reads from your mod

To see your mod the way Claude Code sees it, without running your code or starting a session, use `claude plugin validate`. It checks the manifest and runs the same static analysis on the hooks module’s source that Claude Code runs when it loads a mod. In your shell, run it on the mod’s directory:

```python
claude plugin validate ./first-mod
```

For `first-mod`, the output includes these lines.

```python
  ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
  ❯ ./register.js calls: $.command.register, $.ui.invalidate

✔ Validation passed
```

Check the `hooks:` line for the events your module hooks, each with its filter in braces, and `calls:` for every mods API method it calls. If your module reads or sets environment variables, look for `env reads:` and `env writes:` lines too, and `state reads:` and `state writes:` if it uses [`$.state`](plugins-mods-interface.md#keep-state). You also see one line for each hook that can refuse an action, such as `gating hook without .catch: tool.call`, which says whether that hook has a [`.catch` handler](plugins-mods-events.md#handle-a-hook-that-fails). If an event you meant to handle is missing from the first line, Claude Code won’t call that hook either. The usual cause is a misspelled event name, which the command reports as an error such as `"tool.calls" is not an event`. Follow these rules so that static analysis can find every hook and call:

- Write each mods API call in full: `$`, the namespace, then the method, as in `$.store.get('notes')`. You can pass `$` to a function declared at the top level of the same file, and for a function of yours named `loadNotes`, the `calls:` line then reads `$.store.get (via loadNotes)`. Passing `$` to a method, a function defined inside the hook, or a function you import from another of your files fails validation. The `read` and `update` functions that [`$.state`](plugins-mods-interface.md#keep-state) uses are the imports that can take it. Don’t assign `$` or one of its namespaces to a variable, destructure it, or index it with a computed name. `const ui = $.ui` fails with `$.ui is used as a value`.
- Write the event name in each `on` call as a string literal, such as `'tool.call'`. A variable, or a loop over a list of names, fails with `the event name passed to on() is not a string literal`.
- Inside `register`, don’t declare a second variable or parameter named `on`. Validation fails with `"on" is declared again (shadowed)`.
- Import only from files inside the plugin directory, by relative path. The one bare import allowed is `claude-code`, for types and a few helpers.
- Use `import` declarations at the top of the file, as in `import { name } from './file.js'`. A dynamic `import()` fails with `a dynamic import(); a hooks module imports its own files with an import declaration`.
- Write every file as an ES module, with `import` and not `require`. The [reference](plugins-mods-reference.md#files) lists the file extensions Claude Code loads.


[​](#test-the-mod)

Test the mod

You can write automated tests for a mod and run them from your shell with `claude plugin test`, with no session, sign-in, or network. A test fires the events your hooks handle and checks what the hooks did. This test fires two tool calls, runs `/tally`, and checks that the reply counts both. Save it as `first-mod/tests/first-mod.test.ts`:

first-mod/tests/first-mod.test.ts

```python
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  // Answer each tool call in Claude Code's place, so no tool runs
  on('tool.call', () => ({ result: 'ok' }))

  // Fire two tool calls, which the mod's tool.call hook counts
  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })

  // Run /tally and check the text its hook returns
  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

In your shell, run the tests from the `first-mod` directory:

```python
claude plugin test
```

The output names each test and whether it passed, with timings that vary from run to run:

```python
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]

 1 pass
 0 fail
Ran 1 test across 1 file. [0.19s]
```

[Test a mod](plugins-mods-test.md) covers stubbing a model call or the store, and testing timers and drawings.


[​](#share-your-mod)

Share your mod

A mod is a plugin, so you version it in the manifest and people install and update it with the `/plugin` commands. How you share it depends on who it’s for:

- **A few people**: send them the plugin’s directory or a `.zip` of it. See [Share a plugin without a marketplace](plugins-publish.md#share-a-plugin-without-a-marketplace)
- **Your team**: list it in [your own marketplace](plugins-publish.md#publish-through-your-own-marketplace), such as a private repository with one directory for each plugin. To add that marketplace for everyone who works in a repository, [register it in the repository’s settings](plugins-host-marketplace.md#register-the-marketplace-for-everyone-in-a-repository)
- **Your whole organization**: an administrator can [install your organization’s mods](plugins-mods-admin.md#install-your-organizations-mods) through managed settings
- **Anyone**: make your marketplace’s repository public, or [submit the plugin to Anthropic’s directory](plugins-publish.md#submit-to-anthropics-directory)

Before you do, check the plugin’s `name`: `claude plugin validate` fails a name that [looks like one of Anthropic’s own](plugins-reference.md#name), such as one that starts with `claude-`. The events and methods can change between releases, so your README is the place to say which Claude Code version you tested with. Keep developing against the directory with `--plugin-dir`, not against an installed copy. Claude Code caches an installed plugin by version, so your edits don’t reach the installed copy until you increment the version and install again.


[​](#next-steps)

Next steps

- [Draw in the interface](plugins-mods-interface.md): open a pane, draw above the prompt, and add buttons and text fields
- [React to events](plugins-mods-events.md): hook tool calls, prompts, and turns
- [Use the mods API](plugins-mods-api.md): add commands and tools, call a model, and run work on a timer
- [Test a mod](plugins-mods-test.md): stub what Claude Code would answer, and test timers and drawings
- [Troubleshoot a mod](plugins-mods-troubleshoot.md): the reasons a mod does nothing, and the debug log
- [Read the source of built-in mods](plugins-mods-overview.md#read-the-source-of-built-in-mods): complete plugins, each with its hooks module and tests

---

## React to events with a mod - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/events
Path: 08-Plugins-Skills/plugins-mods-events.md

## On this page

- [How a hook handles an event](#how-a-hook-handles-an-event)
  - [Observe an event](#observe-an-event)
  - [Rewrite an event](#rewrite-an-event)
  - [Answer an event](#answer-an-event)
  - [Filter which events a hook handles](#filter-which-events-a-hook-handles)
- [Hook what Claude is doing](#hook-what-claude-is-doing)
  - [Guard or change a tool call](#guard-or-change-a-tool-call)
  - [Hold a tool call until the user decides](#hold-a-tool-call-until-the-user-decides)
  - [Approve or refuse a tool call before the user is asked](#approve-or-refuse-a-tool-call-before-the-user-is-asked)
  - [Check whether a tool call is read-only](#check-whether-a-tool-call-is-read-only)
  - [Filter a tool.check hook by whether the call is read-only](#filter-a-tool-check-hook-by-whether-the-call-is-read-only)
  - [Rewrite or add to a prompt](#rewrite-or-add-to-a-prompt)
  - [Follow a turn](#follow-a-turn)
  - [Handle the settings hook events](#hook-the-settings-hook-events)
- [Run alongside other mods](#run-alongside-other-mods)
  - [The order mods run in](#the-order-mods-run-in)
  - [Where settings hooks run in the order](#where-settings-hooks-run-in-the-order)
  - [Handle a hook that fails](#handle-a-hook-that-fails)
- [Next steps](#next-steps)

Build

# React to events with a mod

Copy pageCopy page

Handle Claude Code events from a mod: observe, rewrite, or answer tool calls, prompts, and turns, filter which events a hook handles, and plan for other mods.

Copy pageCopy page

A hook is an event handler: a function that Claude Code runs when a named event happens. Claude Code fires an event at each point where it’s about to act, such as when it runs a tool, submits a prompt, sends a request to the model, or starts or ends a session. Your hook runs before Claude Code acts, so it can observe the event, rewrite it, or answer it in Claude Code’s place. You register a hook with [`on(eventName, handler)`](plugins-mods-reference.md#the-hook-function). Build your [first mod](plugins-mods-create.md) before you start here. For every event and its exact fields, see the [reference](plugins-mods-reference.md#events) or read [the types for your build](plugins-mods-create.md#get-the-types-for-your-build).


[​](#how-a-hook-handles-an-event)

How a hook handles an event

A hook sits between an event and what Claude Code would do about it, so it can observe the event, rewrite it, or answer it itself. It receives three arguments: the [mods API](plugins-mods-api.md) as `$`, the event as `e`, and the next handler as `next`. The handlers for an event form a middleware chain. `next(e)` calls the next handler, which is another mod’s hook or, at the end of the chain, Claude Code’s own behavior, and it resolves to the result. What your hook does with `next` decides which of the three it does.


[​](#observe-an-event)

Observe an event

To observe an event without changing it, do your work and return `next(e)`. This hook logs each tool Claude is about to use:

```python
on('tool.call', async ($, e, next) => {
  // Runs before the tool does
  $.ui.log('Claude is about to use ' + e.tool)
  // Pass the event on unchanged
  return next(e)
})
```

Before each tool runs, a dim line such as `● my-mod: Claude is about to use Bash` appears in the transcript, where `my-mod` is your plugin’s name. The tool runs as it would without the mod. To act after the event, `await next(e)`, do your work, and return the result. This hook logs each tool after it has run:

```python
on('tool.call', async ($, e, next) => {
  // Let the tool run, and wait for its result
  const result = await next(e)
  // Runs after the tool does
  $.ui.log(e.tool + ' finished')
  // Give the result back unchanged
  return result
})
```

The line now appears after each tool finishes. Claude reads the same result either way, because the hook returns what `next(e)` resolved to.


[​](#rewrite-an-event)

Rewrite an event

To change what Claude Code acts on, such as the text of a prompt, call `next` with a modified copy of the event. The event itself is immutable: it’s deeply frozen, and assigning to a field throws. This hook trims each prompt before it’s sent:

```python
on('prompt.submit', async ($, e, next) => {
  // Pass on a copy of the event with its text changed
  return next({ ...e, text: e.text.trim() })
})
```

Later handlers and Claude Code receive the trimmed prompt and never see the original. You can also change the result: `await next(e)`, then return a copy of the result with a field replaced.


[​](#answer-an-event)

Answer an event

To handle an event yourself, return a result without calling `next`. That short-circuits the chain, so later mods and Claude Code’s own behavior don’t run. This hook refuses every Bash command:

```python
on('tool.call', { tool: 'Bash' }, async () => {
  // No call to next, so the command never runs
  return { deny: 'Bash is turned off in this project. Use the file tools.' }
})
```

When Claude tries a Bash command, the command doesn’t run, and Claude reads the `deny` text as the tool’s result. Each event has its own result shape, which the [events reference](plugins-mods-reference.md#events) lists.


[​](#filter-which-events-a-hook-handles)

Filter which events a hook handles

To run a hook for some events only, pass a filter as the second argument to `on`. Claude Code calls the filter a matcher. It’s an object whose fields are compared with the event’s, and the hook runs only when every field matches. A field can be a value, an array of allowed values, or a regular expression. Each line in this example registers the same function, `hook`, for a narrower set of tool calls:

```python
// A string matches one value: Bash calls only
on('tool.call', { tool: 'Bash' }, hook)
// An array matches any value in it: Edit calls and Write calls
on('tool.call', { tool: ['Edit', 'Write'] }, hook)
// A regular expression matches by pattern: every tool of one MCP server
on('tool.call', { tool: /^mcp__github__/ }, hook)
```

`hook` runs once for a Bash, Edit, or Write call, and once for a call to a tool whose name starts with `mcp__github__`. A call to any other tool, such as Read, matches none of the three, so `hook` doesn’t run for it. The event name can be a wildcard. `'classic.*'` matches every [settings hook event](#hook-the-settings-hook-events). `'*'` matches every event except the [telemetry events](plugins-mods-reference.md#telemetry), which take their own name and a `{ to: 'collector' }` filter. Register each event once per matcher. If you call `on` twice for `session.start` with no matcher, the module fails to load with `on("session.start") is registered twice without a matcher`. Put everything your mod does at session start in one hook.


[​](#hook-what-claude-is-doing)

Hook what Claude is doing

Handle these events to see or change a tool call, a prompt, or a turn as it happens. For every event and what a hook can return, see the [events reference](plugins-mods-reference.md#events).


[​](#guard-or-change-a-tool-call)

Guard or change a tool call

A `tool.call` hook sees each tool Claude is about to use, so it can refuse the call, change its arguments, or let it through. `tool.call` fires when Claude Code is about to run a tool, including calls a subagent makes and calls to MCP tools. `e.tool` is the tool’s name and the tool’s arguments are fields of `e`, such as `e.command` for Bash. When you call `next(e)`, Claude Code runs the permission check and then the tool. This hook refuses a Bash command that force-pushes, and tells Claude why:

```python
// The matcher limits the hook to Bash calls, so e.command is the shell command
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
  if (/git push .*--force/.test(e.command)) {
    // Returning without calling next answers the event, so the command never runs
    return { deny: 'Force pushes are not allowed in this repository. Push to a new branch instead.' }
  }
  // Every other command goes on to the permission check and then to Bash
  return next(e)
})
```

When Claude tries `git push --force`, the command doesn’t run and no permission prompt appears, because the hook never calls `next`. Claude reads the `deny` text as the tool’s result, so write it as an instruction Claude can act on. Every other Bash command runs as it would without the mod. To act after a tool has run, `await next(e)`, do your work, and return what `next` gave you. This hook logs each `.mdx` file Claude changes, with [`$.ui.log`](plugins-mods-api.md#show-something-without-starting-a-turn), which adds a dim line to the transcript that Claude doesn’t read:

```python
on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {
  // Wait for the permission check and the tool, and keep what they produced
  const result = await next(e)
  // A refused call comes back as { deny }, and a failed one has isError set
  const changed = !result.deny && !result.isError
  if (changed && e.file_path.endsWith('.mdx')) $.ui.log('Claude changed ' + e.file_path)
  // Return the result as it came, so Claude reads what the tool returned
  return result
})
```

After Claude edits or writes an `.mdx` file, a dim line in the transcript names the file. Nothing is logged for another kind of file, or for a call that was refused or failed. Claude’s view of the call doesn’t change, because the hook returns the result it received. Your hook can also change a call, retry it, answer it itself, or withhold its result:

- **Change the call**: pass changed arguments to `next`.
- **Retry the call**: call `next(e)` again. A hook that sees `isError` on the first result can run the tool a second time and return that result.
- **Answer the call yourself**: return an object with a `result` field, without calling `next`, and for a built-in tool, give `result` the shape that tool’s own result has in [the types for your build](plugins-mods-create.md#get-the-types-for-your-build). No permission prompt appears and the tool doesn’t run, so the result you return is all Claude learns about what happened.
- **Withhold the result from Claude**: return `{ deny: reason }` after `await next(e)`. Claude reads your reason in place of what `next` returned. When the tool ran, the deny keeps its result from Claude and undoes nothing the tool did. When the tool ran and succeeded, the reason follows a note such as `Bash ran, and a plugin withheld its result:`.

Hooks in your organization’s [managed settings](../13-Enterprise-Admin/server-managed-settings.md) run before any mod’s `tool.call` hook. When a managed hook blocks the call, no mod’s `tool.call` hook runs for that call.


[​](#hold-a-tool-call-until-the-user-decides)

Hold a tool call until the user decides

A hook can pause a tool call and ask the user what to do before it goes ahead. A `tool.call` hook can `await` before it calls `next` or returns, and the tool call stays pending until then. To put the question to the user, call `$.ui.ask`. It shows your question above a numbered list of your options, in the dialog Claude uses to ask you something, and resolves to the label the user picks. After your options, the dialog adds a row for typing a different answer and a **Chat about this** row. The `RISKY` pattern in this example matches `rm -r`, `rm -rf`, `git reset --hard`, and `git push` with `--force`, and it misses other spellings such as `git push -f`. This module asks before it runs a Bash command that matches the pattern:

```python
const RISKY = /\brm\s+-rf?\b|\bgit\s+reset\s+--hard\b|\bgit\s+push\b.*--force/

export function register(on) {
  on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
    // Let every other command through without a question
    if (!RISKY.test(e.command)) return next(e)
    // Start from the safe answer, so a question nobody answers refuses the command
    let answer = 'Refuse'
    try {
      // The tool call waits here until the user picks one of the two labels
      answer = await $.ui.ask('Run this command? ' + e.command, ['Run it', 'Refuse'])
    } catch {
      // The user dismissed the question, or this is a claude -p run with nobody to ask
    }
    if (answer !== 'Run it') {
      // Answer without calling next, so the command doesn't run
      return { deny: 'The user declined this command. Ask before trying a different approach.' }
    }
    return next(e)
  })
}
```

When Claude tries a command such as `rm -rf build`, the question appears with the command in it, and the command waits for the answer:

- **The user picks Run it**: the hook calls `next(e)`, and the usual permission check still runs after it
- **The user picks Refuse**: the command doesn’t run, and Claude reads the `deny` text
- **The user types an answer**: `$.ui.ask` resolves to the typed text. The hook compares it with `Run it`, so any other text refuses the command.
- **Nobody answers**: `$.ui.ask` rejects when the user dismisses the question or picks **Chat about this**, and in a `claude -p` run, so the `catch` block leaves the answer at `Refuse`

Keep the wait inside a mods API call such as `$.ui.ask`, because that time doesn’t count against the hook’s [time limit](plugins-mods-reference.md#limits). Time spent awaiting a promise of your own does count. Claude Code skips a hook that times out, so the held command would run.


[​](#approve-or-refuse-a-tool-call-before-the-user-is-asked)

Approve or refuse a tool call before the user is asked

To decide whether a tool call may run, handle [`tool.check`](plugins-mods-reference.md#tools), the event where Claude Code makes that decision. For a fixed command or path, use a [permission rule](../02-Claude-Code-CLI/permissions.md#permission-rule-syntax) such as `Bash(npm test)`, which takes no code. Handle `tool.check` when the decision depends on what’s true at that moment, such as the current Git branch or a value another hook recorded.

- **What to return**: to decide the call yourself, return an object of your own, such as `{ decision: 'allow' }` or `{ decision: 'deny', reason }`. To leave the decision alone, return what `await next(e)` gives you.
- **What `await next(e)` gives you**: Claude Code’s own decision, as an object whose `decision` is `allow`, `ask`, or `deny`. No permission prompt appears while `next(e)` runs.
- **Arguments**: `e.input` holds the tool’s arguments, such as `command` for Bash
- **When `tool.check` fires**: after the [`PreToolUse` hooks](../07-Hooks/hooks.md#pretooluse) run, and before the permission prompt, [auto mode](../02-Claude-Code-CLI/permission-modes.md#eliminate-prompts-with-auto-mode)’s classifier, or a [`PermissionRequest`](../07-Hooks/hooks.md#permissionrequest) hook

This hook refuses `git push` while the current branch is `main`:

```python
on('tool.check', { tool: 'Bash' }, async ($, e, next) => {
  // Claude Code's own decision
  const decided = await next(e)
  if (!e.input.command.includes('git push')) return decided
  const branch = await $.process.run(['git', 'branch', '--show-current'])
  if (branch.stdout.trim() !== 'main') return decided
  return { decision: 'deny', reason: 'Push from a branch other than main' }
})
```

The hook denies `git push` on `main` and passes every other call through:

- **On `main`**: the hook returns `deny`, even when a rule allows `git push`
- **On another branch, and for other commands**: the call gets the decision it would get without the mod

The hook matches the text of the command, so treat it as a reminder for Claude. To block pushes to `main` for everyone, protect the branch on your Git host.

A `tool.check` hook can also return `allow` or `ask`. In [plan mode](../02-Claude-Code-CLI/permissions.md#hook-approvals-in-plan-mode), for a call that isn’t read-only, such as a file edit, your hook can tighten the decision `next(e)` returns but can’t loosen it. For the permission rules and `PreToolUse` hook decisions your hook’s `allow` can’t replace, see [Extend permissions with hooks](../02-Claude-Code-CLI/permissions.md#precedence-among-rules-hooks-and-mods).


[​](#check-whether-a-tool-call-is-read-only)

Check whether a tool call is read-only

In a [`tool.check`](#approve-or-refuse-a-tool-call-before-the-user-is-asked) hook, `e.isReadOnly` is `true` when the tool reports the call as read-only. Otherwise the field is absent, not `false`. `e.isReadOnly` requires Claude Code v2.1.304 or later. Some tools, such as Read, Grep, Glob, and WebSearch, report every call as read-only. Bash and MCP tools report a call as read-only in these cases:

- **Bash**: the command passes Claude Code’s built-in [read-only command check](../02-Claude-Code-CLI/permissions.md#read-only-commands)
- **An MCP tool**: the server sets the tool’s `readOnlyHint` annotation to `true`

The value is the tool’s own report, so don’t approve a call on this field alone. For example, WebFetch and Agent calls report as read-only, though [WebFetch](../02-Claude-Code-CLI/tools-reference.md#webfetch-tool-behavior) requests a URL and [Agent](../02-Claude-Code-CLI/tools-reference.md#agent-tool-behavior) starts a subagent that runs tools of its own. This hook returns an `ask` for every `mcp__github__` call that isn’t read-only and wasn’t already denied:

```python
on('tool.check', { tool: /^mcp__github__/ }, async ($, e, next) => {
  const decided = await next(e)
  // Read-only calls and denied calls pass through unchanged
  if (e.isReadOnly || decided.decision === 'deny') return decided
  return { decision: 'ask', reason: 'This tool can change the GitHub repository' }
})
```


[​](#filter-a-tool-check-hook-by-whether-the-call-is-read-only)

Filter a `tool.check` hook by whether the call is read-only

To limit which calls your hook handles by [`e.isReadOnly`](#check-whether-a-tool-call-is-read-only), use the [matcher](#filter-which-events-a-hook-handles) for read-only calls and the hook body for the rest:

- **Read-only calls only**: add `isReadOnly: true` to the matcher, as in `on('tool.check', { tool: 'Bash', isReadOnly: true }, hook)`
- **Calls that aren’t read-only**: test `e.isReadOnly` in the hook body, as the [`mcp__github__` example](#check-whether-a-tool-call-is-read-only) does. If you put `isReadOnly: false` in a matcher, your hook never runs.


[​](#rewrite-or-add-to-a-prompt)

Rewrite or add to a prompt

A `prompt.submit` hook sees each prompt before the turn starts, so it can rewrite the text or add to it. `e.text` is what was typed.

| To do this                                                                                                                 | Return this                                                  |
|:---------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------|
| Rewrite the prompt. The transcript and your [prompt history](../02-Claude-Code-CLI/interactive-mode.md#command-history) show the new text. | `next({ ...e, text: newText })`                              |
| Add text only Claude reads, after the prompt                                                                               | `next({ ...e, context: [...(e.context ?? []), extraText] })` |
| Stop the prompt from being sent                                                                                            | `{ drop: 'the reason' }`                                     |

This hook adds the current branch name for Claude whenever a prompt mentions a pull request:

```python
on('prompt.submit', async ($, e, next) => {
  // Pass on a prompt that doesn't mention a pull request as it is
  if (!/\bPR\b|pull request/i.test(e.text)) return next(e)
  const git = await $.process.run(['git', 'branch', '--show-current'])
  // Outside a git repository the command fails, so there's no branch to add
  if (git.exitCode !== 0) return next(e)
  // Keep any context an earlier hook added, and add one more line for Claude
  return next({ ...e, context: [...(e.context ?? []), 'Current branch: ' + git.stdout.trim()] })
})
```

When you send a prompt such as `open a PR for this change`, your message looks the same in the transcript, and Claude also reads a line such as `Current branch: feature/auth` after it. A prompt that doesn’t mention a pull request goes through unchanged, and `git` doesn’t run. To stop a prompt, return `{ drop: 'the reason' }` without calling `next`. The text goes back into the user’s prompt input, and they see `Prompt dropped by a hook:` followed by your reason, so address the reason to them. If your hook returns a `drop` after its `next(e)` call let the prompt through, the turn still runs, and the hook [fails](#handle-a-hook-that-fails) with a message that includes `a drop after its next() was answered`. [Other events](plugins-mods-reference.md#prompts-and-what-claude-reads) cover the rest of what Claude reads: `prompt.section` for each section of the system prompt, `prompt.context` for the context sent with the first message, and `skill.prompt` for a skill’s text. Text from these hooks that changes between requests [invalidates the prompt cache](../02-Claude-Code-CLI/prompt-caching.md). In a `skill.prompt` hook, you can also read [which plugin or file the skill came from](plugins-mods-reference.md#skill-prompt-fields).


[​](#follow-a-turn)

Follow a turn

A turn is everything Claude does in answer to one prompt. Handle `turn.start`, `turn.step`, and `turn.complete` to follow one:

| Event           | When it fires                                                                                                                                                                                                                                     | What a hook can do                                                                                                              |
|:----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------|
| `turn.start`    | A turn begins                                                                                                                                                                                                                                     | Observe. `e.turnId` identifies the turn in the other two events.                                                                |
| `turn.step`     | Claude Code is about to send one request to the model. A turn with tool calls has several. `e.agentId` is set for a subagent’s request.                                                                                                           | Read each request’s token usage, send it to a different model with `next({ ...e, model })`, or answer without calling the model |
| `turn.complete` | The turn ended, including a turn the user interrupted, where `e.isAborted` is `true`. `e.answer` is Claude’s final text, `e.durationMs` how long it took, and `e.usage` the turn’s token totals. A subagent’s turn fires it with `e.agentId` set. | Observe, or return an object with a `text` field, such as `{ text: 'Done in 12 seconds' }`, to show a line under the answer     |

Write a `turn.step` hook as an async generator, because the event streams. `yield* next(e)` forwards the response as it streams and evaluates to the finished result. This hook logs how much of each request the Claude API served from the [prompt cache](../02-Claude-Code-CLI/prompt-caching.md):

```python
// function* makes the hook a generator, which can pass the response on piece by piece
on('turn.step', async function* ($, e, next) {
  // Send the request, forward each piece as it arrives, and keep the finished result
  const result = yield* next(e)
  // Skip a result that reports no token counts
  if (result.usage) {
    $.ui.log('cache read ' + result.usage.cache_read_input_tokens + ' · wrote ' + result.usage.cache_creation_input_tokens)
  }
  // Return the result unchanged, so the turn continues as usual
  return result
})
```

Claude’s response streams to the screen as it does without the mod. After each request finishes, a dim line in the transcript gives the number of tokens read from the cache and the number written to it. A turn with tool calls has several requests, so it adds several lines. `result.usage` holds the token counts the Claude API reports for a request, plus the `model` that answered: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, and `cache_creation_input_tokens`. The hook runs for subagents’ requests too, so check `e.agentId` when you want only the main conversation. To see the tool calls that the API ran itself during the request, such as calls to the [advisor tool](../02-Claude-Code-CLI/advisor.md), read `result.serverToolUses`. Claude Code doesn’t run these calls, so no `tool.call` or `tool.check` hook fires for them. The field is absent when the response has no such calls, and it requires Claude Code v2.1.290 or later.


[​](#hook-the-settings-hook-events)

Handle the settings hook events

Settings hooks are the command, HTTP, prompt, and agent hooks you configure in settings files. Each [settings hook event](../07-Hooks/hooks.md#hook-events), such as `Stop`, `SessionEnd`, or `PostToolUse`, is also an event named `classic.` followed by the settings hook event’s name, such as `classic.Stop`. `e` is the JSON a settings hook receives on stdin, including `transcript_path`. This hook uses `Stop`, which fires when Claude finishes responding, to log where the session’s transcript is saved:

```python
on('classic.Stop', async ($, e, next) => {
  // e has the same fields a Stop hook in a settings file reads from stdin
  $.ui.log('Transcript saved at ' + e.transcript_path)
  // Pass the event on, so Stop hooks in your settings files still run
  return next(e)
})
```

Each time Claude finishes responding, a dim line in the transcript gives the path of the transcript file. The hook returns `next(e)`, so it observes the event and changes nothing about how the turn ends.


[​](#run-alongside-other-mods)

Run alongside other mods

Several mods can handle the same event, and any one of them can fail. If your mod blocks tool calls, check its position in the chain and what happens when its hook fails.


[​](#the-order-mods-run-in)

The order mods run in

Hooks on the same event form one middleware chain. Each mod’s `next` calls the following mod’s hook, and the last `next` reaches Claude Code’s own behavior. The first mod is outermost: it sees the event before the others and the result after them, and it decides whether the others run at all. A later mod can’t stop an earlier one from seeing an event. Claude Code orders the chain by where each mod comes from:

1.  The built-in guard `sec-default@builtin`, a mod built into Claude Code that `/plugin` lists as `cc-plugin-sec-default`, where [it loads](plugins-mods-admin.md#know-what-happens-by-default), mods your organization lists in [`prependPlugins`](plugins-mods-admin.md#install-your-organizations-mods), and then any other mod that counts as your organization’s and isn’t in `appendPlugins`
2.  Mods you install
3.  Mods your organization lists in `appendPlugins`
4.  Other mods built into Claude Code

Among the mods you install, a mod runs before the mods it lists under `dependencies` in its manifest. Within one module, hooks run in the order `register` called `on`.


[​](#where-settings-hooks-run-in-the-order)

Where settings hooks run in the order

The `PreToolUse` hooks configured in settings files also run during a tool call, at fixed points in the chain of mods:

- **`PreToolUse` hooks from managed settings**: run before the first mod’s `tool.call` hook. When a managed hook blocks the call, no mod’s `tool.call` hook runs for that call.
- **`PreToolUse` hooks from every other settings file and from plugins’ `hooks/hooks.json`**: run after the last mod calls `next`, as part of Claude Code’s own behavior. A mod that answers `tool.call` without calling `next` keeps them from running, and a mod that calls `next` sees their decision in the result it returns.

[`tool.check`](#approve-or-refuse-a-tool-call-before-the-user-is-asked) fires after the `PreToolUse` hooks have run. For the permission rules and `PreToolUse` hook decisions a `tool.check` hook’s `allow` can’t replace, see [Extend permissions with hooks](../02-Claude-Code-CLI/permissions.md#precedence-among-rules-hooks-and-mods).


[​](#handle-a-hook-that-fails)

Handle a hook that fails

A hook that fails doesn’t break the session, and you can decide what happens instead. When a hook with no `.catch` handler throws, times out, or returns a result of the wrong shape, what happens next depends on whether it had called `next`:

- **It failed before calling `next`**: Claude Code skips it, and the next handler runs in its place
- **It failed after `next` resolved**: that result stands, and nothing runs a second time

One line names the mod, the event, and the reason, such as `my-mod: tool.call hook skipped: threw Error: boom`. Where you read it depends on the session, as [Find out why a mod does nothing](plugins-mods-troubleshoot.md#find-out-why-a-mod-does-nothing) lists. A `ui.render` hook whose drawing doesn’t validate is reported differently, as [Build a tree from elements](plugins-mods-interface.md#build-a-tree-from-elements) describes. To make a hook that blocks calls fail closed, add a `.catch` error handler that answers in its place. Here, `guard` is your hook function, and the handler tests [`next.called`](plugins-mods-reference.md#the-hook-function) to tell whether `guard` had already called `next` when it failed:

```python
// on returns a registration, and .catch attaches a handler to that one hook
on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {
  // guard had already called next, so return what came back
  if (next.called) return next(e)
  // next.error.kind says why the handler was asked, such as 'throw' or 'timeout'
  return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind }
})
```

When `guard` throws or times out on a Bash call, Claude Code calls the handler with the same event:

- **`guard` failed before it called `next`**: the command doesn’t run, and Claude reads the `deny` text with the kind at the end
- **`guard` failed after it called `next`**: the handler’s `next(e)` resolves to the result that `guard`’s call produced without running the command again, and Claude reads that result

The handler has a shorter [time limit](plugins-mods-reference.md#limits) of its own. If the handler itself throws or times out, Claude Code skips the hook as if it had no handler. When `guard` hadn’t called `next`, the command then goes on as it would without the mod. The same handler shape fits a guard on `prompt.submit` or `config.set`. When `next.called` is false, return the refusal that the [events reference](plugins-mods-reference.md#events) lists for that event: `{ drop: 'the reason' }` for `prompt.submit`, `{ deny: 'the reason' }` for `config.set`. At `tool.check` and `plugin.register`, a refusal returned after `next` resolved still holds, so return it without testing `next.called`:

- **`tool.check`**: return `{ decision: 'deny', reason: 'the reason' }`
- **`plugin.register`**: return `{ refuse: 'the reason' }`, as [Refuse mods when your check fails](plugins-mods-admin.md#refuse-mods-when-your-check-fails) shows

At `tool.call`, a `deny` returned after `next` resolved [withholds the result from Claude](#guard-or-change-a-tool-call).


[​](#next-steps)

Next steps

- [Use the mods API](plugins-mods-api.md): add commands and tools, call a model, and run work on a timer
- [Draw in the interface](plugins-mods-interface.md): show what your hooks collect in a pane or above the prompt
- [Test a mod](plugins-mods-test.md): fire any of these events from a test
- [Mods reference](plugins-mods-reference.md): every event, every mods API method, and the limits

---

## Interface gallery for mods - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/gallery
Path: 08-Plugins-Skills/plugins-mods-gallery.md

## On this page

- [Try a sample](#try-a-sample)
- [Pick an element](#pick-an-element)
- [Show text](#show-text)
  - [Text](#text)
  - [Markdown](#markdown)
  - [Link](#link)
- [Show code and changes](#show-code-and-changes)
  - [Code](#code)
  - [Code as a diff](#code-as-a-diff)
- [Arrange elements](#arrange-elements)
  - [Box](#box)
- [Take input](#take-input)
  - [Button](#button)
  - [Input](#input)
  - [Select](#select)
- [Draw pictures](#draw-pictures)
  - [Raster](#raster)
  - [Svg](#svg)
  - [Image and Client](#image-and-client)
- [See where a mod can draw](#see-where-a-mod-can-draw)
- [Next steps](#next-steps)

Build

# Interface gallery for mods

Copy pageCopy page

See the interface elements a Claude Code mod can draw, such as text, buttons, fields, Markdown, code, and diffs, with sample code and terminal screenshots.

Copy pageCopy page

A mod draws its interface from elements: text, boxes, buttons, fields, and a few that format content for you. The samples here show the code that draws an element, and most come with a screenshot of the result in a terminal pane, so you can pick an element by how it looks. To learn how drawing works, start with [Draw in the interface](plugins-mods-interface.md). For the main props and which apps draw each element, see the [elements reference](plugins-mods-reference.md#elements). The [type declarations](plugins-mods-create.md#get-the-types-for-your-build) list every prop.


[​](#try-a-sample)

Try a sample

The samples on this page are snippets, not whole mods. Each one is the code for one element and anything nested inside it. To see a sample in your own terminal, create the small mod in these steps and paste the sample into it. The mod adds a `/gallery` command that opens a pane and draws the sample there. A [pane](plugins-mods-interface.md#pick-where-to-draw) is a sidebar beside the transcript in a wide fullscreen terminal, or a framed region above the prompt otherwise.

1

Create the mod

Create a directory named `gallery` with `.claude-plugin` and `hooks` directories inside it. [Create a mod](plugins-mods-create.md#write-a-mod-yourself) explains the files.Save the manifest as `gallery/.claude-plugin/plugin.json`:

gallery/.claude-plugin/plugin.json

```python
{
  "name": "gallery",
  "version": "0.1.0",
  "description": "Opens a pane that draws one sample",
  "author": { "name": "Your Name" }
}
```

Name your entry point in `gallery/hooks/hooks.json`:

gallery/hooks/hooks.json

```python
{
  "modules": ["./register.js"]
}
```

Save the code as `gallery/hooks/register.js`. It adds a `/gallery` command that opens a pane, and draws `Plain text` in that pane:

gallery/hooks/register.js

```python
// Stands in for your own callback in the samples that take one
const noop = () => {}
// The Select sample keeps its choice here
let picked = 'md'

// The Raster sample packs its cells with this function
const DEFAULT_COLOR = 0x01000000
function cellsOf(rows) {
  const numbers = rows.flat().flatMap(([char, color]) => [char.codePointAt(0), color, DEFAULT_COLOR])
  return new Uint8Array(Uint32Array.from(numbers).buffer).toBase64()
}

export function register(on) {
  on('session.start', async ($, e, next) => {
    await $.command.register({ name: 'gallery', description: 'Open the sample pane' })
    return next(e)
  })

  on('command.run', { command: 'gallery' }, async ($) => {
    await $.ui.open({ id: 'gallery', focus: true, closeOnEscape: true })
    return {}
  })

  on('ui.render', { component: 'Pane' }, async ($, e, next) => {
    if (e.requestId !== 'gallery') return next(e)
    const { Box, Text, Button, Input, Select, Link, Markdown, Code, Raster, Svg } = $.ui.resolve(e)
    // Replace the element after return with a sample
    return Text({ children: ['Plain text'] })
  })
}
```

2

Run the mod

In your shell, start Claude Code from the directory that holds `gallery`:

```python
claude --plugin-dir ./gallery
```

At the Claude Code prompt, run `/gallery`. A pane opens with `Plain text` in it.

3

Swap in a sample

Copy a sample from this page. In `register.js`, paste it over `Text({ children: ['Plain text'] })`, so that it follows `return`, and save the file. Claude Code reloads the module each time you save, so run `/gallery` again to see the new sample.


[​](#pick-an-element)

Pick an element

The samples are grouped by what you want to put on screen:

- **[Show text](#show-text)**: `Text`, `Markdown`, and `Link`
- **[Show code and changes](#show-code-and-changes)**: `Code`
- **[Arrange elements](#arrange-elements)**: `Box`
- **[Take input](#take-input)**: `Button`, `Input`, and `Select`
- **[Draw pictures](#draw-pictures)**: `Raster`, `Svg`, `Image`, and `Client`


[​](#show-text)

Show text

Three elements put words on screen: `Text` for your own styling, `Markdown` for content that’s already formatted, and `Link` for a URL.


[​](#text)

`Text`

`Text` draws a string with the styles you give it. This sample shows plain text and eight styles, one on each line:

```python
Box({
  flexDirection: 'column',
  children: [
    Text({ children: ['Plain text'] }),
    Text({ bold: true, children: ['bold'] }),
    Text({ italic: true, children: ['italic'] }),
    Text({ underline: true, children: ['underline'] }),
    Text({ strikethrough: true, children: ['strikethrough'] }),
    Text({ dimColor: true, children: ['dimColor'] }),
    Text({ inverse: true, children: ['inverse'] }),
    Text({ color: 'red', children: ["color: 'red'"] }),
    Text({ backgroundColor: 'blue', children: ["backgroundColor: 'blue'"] }),
  ],
})
```

`dimColor` draws the text in gray. `backgroundColor` fills only as wide as the text.


[​](#markdown)

`Markdown`

`Markdown` formats text the way Claude’s replies are formatted. Pass the content in `text`, not in `children`:

```python
Markdown({
  text: '## Release notes\n\nThis build has **two** fixes and one `flag`:\n\n- Faster start\n- Fewer prompts\n\n> Quoted text',
})
```

A heading draws in bold without its `#` marks. Inline code draws in color without its backticks. A quote draws in italics with a bar on its left.


[​](#link)

`Link`

`Link` draws a label followed by its URL:

```python
Link({ href: 'https://code.claude.com/docs', label: 'Claude Code docs' })
```

The terminal draws the URL as text after the label. Whether a click opens it depends on the user’s terminal.


[​](#show-code-and-changes)

Show code and changes

`Code` draws source text with Claude Code’s own syntax colors, or a diff. In the VS Code extension, `Code` shows its `source` as plain text, with none of the colors, line numbers, or diff shading in these screenshots.


[​](#code)

`Code`

Name the `language`, or pass a `path` for Claude Code to infer it from. With `startLine`, the lines are numbered from that number:

```python
Code({
  language: 'javascript',
  startLine: 1,
  source: "const name = 'mods'\nconsole.log('hello ' + name)",
})
```

The colors come from the user’s theme.


[​](#code-as-a-diff)

`Code` as a diff

With `format: 'diff'`, `source` is one or more unified diff hunks:

```python
Code({
  format: 'diff',
  source: '@@ -1,3 +1,3 @@\n # Mods\n-A mod is a plugin.\n+A mod is a plugin that runs code.\n Read on.',
})
```

Claude Code draws line numbers in place of the `@@` line. Where a removed line and an added line are alike, the words that changed get a stronger shade.


[​](#arrange-elements)

Arrange elements


[​](#box)

`Box`

`Box` lays out what’s inside it in a row or a column, and can draw a border. This sample puts a row of words above a bordered box:

```python
Box({
  flexDirection: 'column',
  gap: 1,
  children: [
    Box({
      flexDirection: 'row',
      columnGap: 4,
      children: [Text({ children: ['a row'] }), Text({ children: ['of three'] }), Text({ children: ['items'] })],
    }),
    Box({
      borderStyle: 'round',
      paddingX: 1,
      children: [Text({ children: ["borderStyle: 'round'"] })],
    }),
  ],
})
```

The border stretches to the width of the pane.


[​](#take-input)

Take input

`Button`, `Input`, and `Select` are controls: the user moves between them with Tab and uses the one that has the focus. [Keyboard focus and hotkeys](plugins-mods-interface.md#know-which-keys-your-mod-can-receive) covers which keys reach them. Open a pane with `focus: true` to request keyboard focus for it. Typed letters reach an `Input` once it has the focus. To choose which field has the focus when the pane opens, see [Set a hotkey and the first focus](plugins-mods-interface.md#set-a-hotkey-and-the-first-focus).


[​](#button)

`Button`

A button runs `onPress`. This sample shows the default form, a `plain` button with a hotkey, and a dim one:

```python
Box({
  flexDirection: 'column',
  children: [
    Button({ key: 'save', label: 'Save', onPress: noop }),
    Button({ key: 'next', label: 'Next', hotkey: 'n', plain: true, onPress: noop }),
    Button({ key: 'skip', label: 'Skip', dimColor: true, onPress: noop }),
  ],
})
```

A button that has the focus draws in inverse video. Here the user has pressed Tab twice:


[​](#input)

`Input`

An `Input` is a one-line text field that runs `onSubmit` when the user presses Enter:

```python
Input({
  key: 'title',
  label: 'Title',
  placeholder: 'Type a title and press Enter',
  value: '',
  submitLabel: 'save',
  onSubmit: noop,
})
```

Without the focus, the field shows its label and its placeholder:

With the focus, the label turns bold, a cursor appears, and the `submitLabel` shows after `⏎`:

Typing replaces the placeholder:


[​](#select)

`Select`

A `Select` lets the user pick one of several options, and runs `onSelect` with the option’s `value`:

```python
Select({
  key: 'format',
  label: 'Format',
  value: picked,
  options: [
    { value: 'md', label: 'Markdown' },
    { value: 'html', label: 'HTML' },
    { value: 'txt', label: 'Plain text' },
  ],
  onSelect: (value) => {
    picked = value
  },
})
```

Closed, it shows its label and the current option:

Open, it lists its options and marks one:

After the user picks an option, the list closes:


[​](#draw-pictures)

Draw pictures


[​](#raster)

`Raster`

A `Raster` is a grid of colored character cells, for a heat map, a sparkline, or a game board. The terminal draws it. This sample uses the `cellsOf` function in the starter module, which packs the cells into the string a `Raster` takes. [Draw a grid of colored cells](plugins-mods-interface.md#draw-a-grid-of-colored-cells) explains it:

```python
Raster({
  key: 'grid',
  columns: 3,
  rows: 2,
  cells: cellsOf([
    [['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
    [['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
  ]),
})
```

A `Raster` rounds each color to a smaller palette, so `0x2e7d32` draws as `#337733`.


[​](#svg)

`Svg`

An `Svg` draws an SVG document in the Desktop app and the VS Code extension:

```python
Svg({
  alt: 'Three bars of rising height',
  width: 120,
  height: 60,
  source:
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60"><rect x="10" y="40" width="20" height="20" fill="#2e7d32"/><rect x="50" y="25" width="20" height="35" fill="#f9a825"/><rect x="90" y="5" width="20" height="55" fill="#c62828"/></svg>',
})
```

In the terminal, a pane that returns only an `Svg` opens empty. To draw something else there, check [`e.surface`](plugins-mods-interface.md#pick-where-to-draw) and return a different tree.


[​](#image-and-client)

`Image` and `Client`

Two more elements have no sample here. `Image` draws a PNG or raw pixels in the terminal. `Client` is a region that a second file of yours draws, for animation and pointer input. The [elements reference](plugins-mods-reference.md#elements) lists their props. An `Image` draws as a picture in these terminals:

- kitty 0.28 or later
- Ghostty
- iTerm2 3.5 or later. Drawing in iTerm2 requires Claude Code v2.1.305 or later.

In any other terminal, the user sees the `Image`’s `alt` text, dimmed, in place of the picture. Write `alt` text that stands on its own, because it also shows in these cases, among others:

- **tmux and screen**: a session running inside tmux or screen, in any terminal.
- **Background sessions**: every [background session](../09-Agents-Patterns/agent-view.md), whatever terminal it’s attached from.
- **iTerm2 outside fullscreen rendering**: in iTerm2 the picture draws only under [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md), and not while [`CLAUDE_CODE_ALT_SCREEN_FULL_REPAINT`](../02-Claude-Code-CLI/env-vars.md) is set to `1`. To see the picture, run `/tui fullscreen` at the Claude Code prompt and leave that variable unset.
- **A file path source in iTerm2**: iTerm2 draws an `Image` whose `source` prop holds `png` or `rgba` bytes, and shows the `alt` text for one whose `source` names a `file`. If your mod has iTerm2 users, pass the picture as base64 in `source.png` or `source.rgba` rather than as a path in `source.file`.

If your mod’s users see the dimmed text in a terminal that draws kitty graphics protocol images with Unicode placeholders, they can set [`CLAUDE_CODE_FORCE_TERMINAL_IMAGES`](../02-Claude-Code-CLI/env-vars.md) to `1`. Inside tmux or screen that doesn’t help: the `alt` text goes away, and Claude Code sends the picture without wrapping it for tmux or screen passthrough.


[​](#see-where-a-mod-can-draw)

See where a mod can draw

The samples all draw in a pane. A mod can also draw in other places, and call Claude Code to show something for it:

- **Pane and band**: [Pick where to draw](plugins-mods-interface.md#pick-where-to-draw)
- **Claude Code’s own rows, such as the spinner**: [Change what Claude Code already draws](plugins-mods-interface.md#change-what-claude-code-already-draws)
- **Toast, status line, and log line**: [Show something without starting a turn](plugins-mods-api.md#show-something-without-starting-a-turn)
- **Question dialog**: [Hold a tool call until the user decides](plugins-mods-events.md#hold-a-tool-call-until-the-user-decides)


[​](#next-steps)

Next steps

- [Draw in the interface](plugins-mods-interface.md): build a pane with tabs, step by step
- [Test a drawing](plugins-mods-test.md#test-a-drawing): press your buttons from a test
- [Elements reference](plugins-mods-reference.md#elements): each element’s main props and the apps that draw it

---

## Draw in the interface with a mod - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/interface
Path: 08-Plugins-Skills/plugins-mods-interface.md

## On this page

- [Build a pane with tabs](#build-a-pane-with-tabs)
- [Pick where to draw](#pick-where-to-draw)
  - [Change what Claude Code already draws](#change-what-claude-code-already-draws)
  - [Open a pane at the right time](#open-a-pane-at-the-right-time)
  - [Hold toasts behind a dialog](#hold-toasts-behind-a-dialog)
  - [When a pane waits for a wider terminal](#when-a-pane-waits-for-a-wider-terminal)
  - [Draw in the VS Code extension](#draw-in-the-vs-code-extension)
- [Build a tree from elements](#build-a-tree-from-elements)
  - [Link in the Desktop app and VS Code](#link-in-the-desktop-app)
  - [When a Client fails](#when-a-client-fails)
  - [Draw a grid of colored cells](#draw-a-grid-of-colored-cells)
- [Respond to presses and typing](#respond-to-presses-and-typing)
  - [Keyboard focus and hotkeys](#know-which-keys-your-mod-can-receive)
  - [How a pane gets keyboard focus](#how-a-pane-gets-keyboard-focus)
  - [What each key does](#what-each-key-does)
  - [Set a hotkey and the first focus](#set-a-hotkey-and-the-first-focus)
  - [When the user collapses the band](#when-the-user-collapses-the-band)
  - [Take typed input and draw a row for each item](#take-typed-input-and-draw-a-row-for-each-item)
- [Redraw a site](#redraw-when-something-changes)
  - [When Claude Code redraws without being asked](#when-claude-code-redraws-without-being-asked)
  - [Redraw when your data changes](#redraw-when-your-data-changes)
  - [Redraw on a timer](#redraw-on-a-timer)
  - [How often a site can redraw](#how-often-a-site-can-redraw)
- [Keep state](#keep-state)
  - [Keep a value in \$.state](#keep-a-value-in-%24-state)
  - [Declare the values](#declare-the-values)
  - [Point the manifest at the declaration](#point-the-manifest-at-the-declaration)
  - [Define, read, and write a value](#define-read-and-write-a-value)
  - [Change hello-tabs to use \$.state](#change-hello-tabs-to-use-%24-state)
  - [Load a saved value again after /clear](#load-a-saved-value-again-after-clear)
  - [Save from more than one session](#save-from-more-than-one-session)
- [Next steps](#next-steps)

Build

# Draw in the interface with a mod

Copy pageCopy page

Draw panes, a band above the prompt, buttons, and text fields from a Claude Code mod, handle presses and input, and keep state between redraws and sessions.

Copy pageCopy page

A mod can draw its own interface in Claude Code and change parts of the interface Claude Code already draws. Each place a mod can draw is called a [render site](plugins-mods-reference.md#render-sites), such as a pane, the band above the prompt, or the spinner. Claude Code fires the [`ui.render`](plugins-mods-reference.md#interface) event each time it’s about to draw a render site, and your hook for that event returns what to draw there. This map shows where a mod can draw in a terminal session:

In a narrower terminal, the pane sits above the prompt instead of beside the transcript. Build your [first mod](plugins-mods-create.md) before you start here. Begin with the worked example, which builds a pane with two tabs and a counter, then read the section for each piece you want to change.

To look up one prop or limit, see the [reference](plugins-mods-reference.md#render-sites).


[​](#build-a-pane-with-tabs)

Build a pane with tabs

In this section you build a mod that adds a `/hello-tabs` command, and the command opens a pane. A pane is a sidebar beside the transcript in a wide fullscreen terminal, or a framed region above the prompt otherwise. This pane shows two tabs, and the second tab has a button that adds one to a counter. The count is still there after you restart Claude Code. The finished mod looks like this. The recording opens the pane, switches to the second tab, presses the button a few times, and returns to the first tab:

The tabs are two buttons in a row. The mod keeps track of which one is active and draws that tab’s content under the row.

1

Create the plugin

A mod is a plugin with a manifest, a `hooks.json` that points to your code, and the code file. [Create a mod](plugins-mods-create.md#write-a-mod-yourself) explains each one. Create a directory named `hello-tabs` with `.claude-plugin` and `hooks` directories inside it, then save the first two files.Save the manifest as `hello-tabs/.claude-plugin/plugin.json`:

hello-tabs/.claude-plugin/plugin.json

```python
{
  "name": "hello-tabs",
  "version": "0.1.0",
  "description": "Opens a pane with two tabs and a counter",
  "author": { "name": "Your Name" }
}
```

Name your entry point in `hello-tabs/hooks/hooks.json`:

hello-tabs/hooks/hooks.json

```python
{
  "modules": ["./register.js"]
}
```

2

Write the code

This list says what each hook does, in the order they appear in the code:

- Adds the `/hello-tabs` command, and loads the count an earlier session saved
- Opens the pane when you run that command
- Draws the pane’s content: the row of tabs and the open tab’s body

Two module-level variables, `tab` and `count`, hold the pane’s state.Save this as `hello-tabs/hooks/register.js`:

hello-tabs/hooks/register.js

```python
// The pane's id, used to open the pane and to recognize it when drawing
const PANE = 'hello-tabs'

// What the pane shows: which tab is open, and the counter's value
let tab = 'one'
let count = 0

export function register(on) {
  // Runs before your first prompt, and again after a reload
  on('session.start', async ($, e, next) => {
    await $.command.register({ name: 'hello-tabs', description: 'Open the hello-tabs pane' })
    // Load the count an earlier session saved, if there is one
    const saved = await $.store.get('count')
    if (typeof saved === 'number') count = saved
    return next(e)
  })

  // Runs when you type /hello-tabs
  on('command.run', { command: 'hello-tabs' }, async ($) => {
    // Open the pane, give it the keyboard, and let Esc close it
    await $.ui.open({ id: PANE, title: 'Hello tabs', focus: true, closeOnEscape: true })
    // Print nothing in the transcript
    return {}
  })

  // Runs each time Claude Code draws a pane
  on('ui.render', { component: 'Pane' }, async ($, e, next) => {
    // Leave other mods' panes alone
    if (e.requestId !== PANE) return next(e)
    // Get the elements this app can draw
    const { Box, Text, Button } = $.ui.resolve(e)
    // Ask Claude Code to run this hook again
    const redraw = () => $.ui.invalidate('ui.render')

    // One tab: a button that switches to its tab when pressed
    const tabButton = (name, label, hotkey) =>
      Button({
        key: 'tab-' + name,
        label,
        hotkey,
        plain: true,
        // Dim the tab that isn't open
        dimColor: tab !== name,
        onPress: () => {
          tab = name
          redraw()
        },
      })

    // What goes under the tabs, depending on which one is open
    const body =
      tab === 'one'
        ? [Text({ children: ['This is the first tab.'] })]
        : [
            Box({
              flexDirection: 'row',
              columnGap: 2,
              children: [
                Button({
                  key: 'more',
                  label: 'Add one',
                  hotkey: 'a',
                  onPress: async () => {
                    count += 1
                    redraw()
                    // Save the count so it's there after a restart
                    await $.store.set('count', count)
                  },
                }),
                Text({ children: ['Count: ' + count] }),
              ],
            }),
          ]

    // The whole pane: the row of tabs, a blank line, then the body
    return Box({
      flexDirection: 'column',
      children: [
        Box({
          flexDirection: 'row',
          columnGap: 3,
          children: [tabButton('one', 'One', '1'), tabButton('two', 'Two', '2')],
        }),
        Text({ children: [' '] }),
        ...body,
      ],
    })
  })
}
```

Each hook also does something the code doesn’t make plain:

- **[`session.start`](plugins-mods-reference.md#session)** also reads the saved count from [`$.store`](#keep-state), a key-value store that persists between sessions.
- **[`command.run`](plugins-mods-api.md#add-a-command)** only tells Claude Code the pane exists. Opening a pane draws nothing by itself: Claude Code then fires `ui.render` to ask what goes in it.
- **`ui.render`** returns the element tree, a `Box` that holds other boxes, text, and buttons, and builds it again from `tab` and `count` each time it runs.

Pressing a button runs its `onPress` callback, which changes a variable and calls `redraw`. Claude Code then runs the `ui.render` hook again, and the hook builds a new tree from the new values. Every interactive drawing uses that render cycle: a callback changes state, and the hook renders again from the new state.

3

Open the pane

In your shell, start Claude Code with `claude --plugin-dir ./hello-tabs`. At the Claude Code prompt, run `/hello-tabs`. A pane opens with `1: One` and `2: Two` across the top. Press `2`, then press `a`, the hotkey for **Add one**, a few times. The count rises.

4

Check that the count was saved

Press Esc to close the pane, then exit the session. In your shell, start Claude Code again with the same `claude --plugin-dir ./hello-tabs` command, and at the Claude Code prompt run `/hello-tabs`. The count is where you left it.To clear the count, have the mod call `$.store.delete('count')`. [Keep state](#keep-state) covers how long each kind of value lasts.


[​](#pick-where-to-draw)

Pick where to draw

A `ui.render` hook runs for every render site unless you narrow it to the one you want to draw in. To choose the render site, pass a filter, called a [matcher](plugins-mods-events.md#filter-which-events-a-hook-handles), as the second argument to `on`. `{ component: 'Pane' }` runs the hook only for panes. In the hook, `e.component` names the site, `e.surface` says which app is drawing, and `e.props` holds the site’s own data. For a pane, `e.requestId` is the `id` you opened it with. The pane and the band are empty until a mod fills them. Select a tab to see what each one is and how to draw in it:

- Pane

- Band above the prompt

A pane is a sidebar beside the transcript in a wide fullscreen terminal, or a framed region above the prompt otherwise. With several panes open, each gets a tab that shows its title.A pane appears when your mod calls `$.ui.open` with an `id` you choose, as in `$.ui.open({ id: 'hello-tabs' })`. [Open a pane at the right time](#open-a-pane-at-the-right-time) covers the other fields and when a pane waits for a wider terminal.To draw in your pane, filter on `{ component: 'Pane' }` and check that `e.requestId` is your `id`.

The band is a strip directly above the prompt input. It’s always there, and every mod shares it.Your hook returns a tree to show something in the band, or `next(e)` to show nothing. A tree replaces what the mods [after yours](plugins-mods-events.md#the-order-mods-run-in) draw there. To keep theirs, put the result of `await next(e)` among the children of a [`Box`](#build-a-tree-from-elements) in your tree.To draw in the band, filter on `{ component: 'AbovePrompt' }`.


[​](#change-what-claude-code-already-draws)

Change what Claude Code already draws

Claude Code draws most of its interface itself: messages, tool call rows, the spinner, and more. Each of those parts is a render site too, so a mod can restyle or replace it. To change one, filter your `ui.render` hook on its name from this table:

| Site                                      | What it is                                                                                                                               |
|:------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|
| `UserMessage`, `AssistantMessage`         | A message in the transcript                                                                                                              |
| `ToolUse`, `ToolResult`, `ToolGroup`      | A tool call’s row, its result, and a collapsed group of calls                                                                            |
| `CommandOutput`                           | The row a command printed                                                                                                                |
| `AskUserQuestion`                         | The dialog Claude opens to ask you a question                                                                                            |
| `Spinner`, `ToolProgress`, `TurnDuration` | Status lines for a turn: the line that animates while Claude works, a running tool’s live progress line, and the line that closes a turn |
| `InfoNotice`, `SessionMode`, `PromptHint` | Status lines under the logo, the mode labels in the footer, and the hint line under the prompt                                           |

`Pane`, `AskUserQuestion`, `Spinner`, `UserMessage`, `AssistantMessage`, and the tool call sites work in the terminal, the Desktop app, and the VS Code extension. The [render sites table](plugins-mods-reference.md#render-sites) lists where each of the others is raised. At a site Claude Code already draws, your hook can change a detail, replace the drawing, or leave it alone. Select a tab to see each one applied to the spinner. The examples read a `calls` variable that another hook counts, as in the [tutorial mod](plugins-mods-create.md#write-a-mod-yourself).

- Change a detail

- Replace the drawing

- Leave it alone

To keep Claude Code’s drawing and change one part of it, pass `next` a copy of the event with changed `props`. This hook changes the text after the spinner’s word:

```python
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
  // Keep Claude Code's spinner, and change the text after its word
  return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
```

The spinner keeps its animation and its word, and your text follows the word:

```python
Thinking · tool calls: 2…
```

To draw something of your own in the site’s place, return a tree and don’t call `next`. This hook draws one line of text where the spinner would be:

```python
on('ui.render', { component: 'Spinner' }, async ($, e) => {
  const { Text } = $.ui.resolve(e)
  // No call to next, so this line is drawn in the spinner's place
  return Text({ children: ['Claude has made ' + calls + ' tool calls'] })
})
```

While Claude works, your line shows and Claude Code’s spinner doesn’t:

```python
Claude has made 2 tool calls
```

To leave the site as Claude Code draws it, return `next(e)`. A hook often does that for some events and not others. This hook leaves the spinner alone until there’s a call to count:

```python
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
  // Nothing to show yet, so pass the event on unchanged
  if (calls === 0) return next(e)
  return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
```

Before the first tool call, the spinner looks the way it does without the mod:

```python
Thinking…
```

At these sites, `next(e)` returns a reference to Claude Code’s drawing, `{ type: 'engine', ref }`, unless a mod that runs after yours returned a tree of its own. To change what’s in that drawing, pass `next` a copy of the event with different props, as the **Change a detail** tab does. You can return the reference as it is, or place it in a `Box` beside elements of your own:

```python
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
  const { Box, Text } = $.ui.resolve(e)
  const theirs = await next(e)
  return Box({ flexDirection: 'column', children: [theirs, Text({ children: ['under the spinner'] })] })
})
```

While Claude works, the spinner animates as before, and `under the spinner` appears below it. The permission prompt isn’t a render site, so a mod can’t change what it shows. The question dialog, `AskUserQuestion`, is one, so a mod can change that. A tree for the dialog has to hold the reference exactly once, with your elements above it. Otherwise, Claude Code draws its own dialog.


[​](#open-a-pane-at-the-right-time)

Open a pane at the right time

A pane appears only when your mod opens it. How and when you open it decides whether it takes keyboard focus, how much room it asks for, and whether it shows at all in a narrow terminal. To open a pane, call [`$.ui.open`](plugins-mods-reference.md#mods-api-methods) with an `id` you choose. The `id` is the pane’s name: your `ui.render` hook checks for it, and you pass it again to close the pane.

```python
await $.ui.open({ id: 'hello-tabs', title: 'Hello tabs', focus: true })
```

To close the pane, call `$.ui.close` with the `id` you opened it with:

```python
await $.ui.close({ id: 'hello-tabs' })
```

Besides `id`, `$.ui.open` takes these optional fields:

| Field           | What it does                                                                                                                                                                                                                                                                             |
|:----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `title`         | The pane’s tab label when more than one pane is open                                                                                                                                                                                                                                     |
| `focus`         | Requests [keyboard focus](#know-which-keys-your-mod-can-receive)                                                                                                                                                                                                                         |
| `closeOnEscape` | Makes Esc close the pane                                                                                                                                                                                                                                                                 |
| `holdToasts`    | In the terminal, holds toasts while this pane is the one showing. See [Hold toasts behind a dialog](#hold-toasts-behind-a-dialog).                                                                                                                                                       |
| `rows`          | The height to ask for when the pane sits above the prompt. The default is a third of the space.                                                                                                                                                                                          |
| `columns`       | The width to ask for when the pane sits beside the transcript, at most 45% of the terminal’s width. Once the user has resized a pane beside the transcript, in this session or an earlier one, panes open there at the width they set instead. The same 45% limit applies to that width. |

The [VS Code extension](#draw-in-the-vs-code-extension) ignores `rows` and `columns` and sizes the pane itself. `focus`, `closeOnEscape`, and `holdToasts` are optional and accept only `true`. To leave one off, omit it. Passing `false` throws an error such as `ui.open: focus is true or left out`. To set one of them conditionally, add the field only when the condition holds. This call asks for keyboard focus only when `items` isn’t empty:

```python
const pane = { id: 'hello-tabs', title: 'Hello tabs' }
await $.ui.open(items.length > 0 ? { ...pane, focus: true } : pane)
```

To let a command open the pane while Claude is working, add `immediate: true` when you [register the command](plugins-mods-api.md#add-a-command). Without it, a command typed during a turn waits for the turn to end.


[​](#hold-toasts-behind-a-dialog)

Hold toasts behind a dialog

Pass `holdToasts: true` to `$.ui.open` when the pane is a dialog the user answers and leaves, so toasts don’t appear while they decide. In the terminal, the hold lasts while that pane is the one showing, and a toast raised in that time waits until the hold ends. Claude Code holds other mods’ toasts and its own short-lived notifications as well as the ones your mod raises with [`$.ui.toast`](plugins-mods-api.md#show-something-without-starting-a-turn). Leave the field off a pane that stays open, so the user keeps seeing them.


[​](#when-a-pane-waits-for-a-wider-terminal)

When a pane waits for a wider terminal

A pane your mod opens without being asked doesn’t appear in a narrow terminal, so it can’t take over a small screen. Whether it appears depends on what opened it:

- **Opened by something the user did**, such as a command they ran or a button they pressed, the pane appears at any width
- **Opened by your mod acting by itself**, such as from a timer or a [`turn.start`](plugins-mods-events.md#follow-a-turn) hook, the pane appears only in a terminal at least 144 columns wide. After the user has opened that pane once themselves, 110 columns is enough.

When the pane appears, `$.ui.open` resolves to `{ isPlaced: true }`. When the pane is waiting, `isPlaced` is `false` and `reason` is a string that says why. A waiting pane appears when the user opens it or widens the terminal. To say something is available without opening a pane, call `$.ui.toast('Your message')`, which shows a toast notification.


[​](#draw-in-the-vs-code-extension)

Draw in the VS Code extension

In the VS Code extension, what a mod draws appears in the chat panel with extension version 2.1.299 or later. A session that [doesn’t fetch feature flags](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching), such as one on Amazon Bedrock, needs 2.1.304 or later. In a `ui.render` hook for the panel, `e.surface` is `vscode`. To check which sites and elements the panel draws, and which props it ignores, see the [render sites](plugins-mods-reference.md#render-sites) and [elements](plugins-mods-reference.md#elements) tables. To make a mod work in the panel as well as the terminal, account for these differences:

- **Panes**: in a panel 800 pixels or wider, panes dock beside the conversation, `e.props.placement` is `'dock'`, and `e.viewport.isFullscreen` is `true`. In a narrower panel, panes draw above the prompt box, `placement` is `'inline'`, and a pane shows 12 rows before it scrolls.
- **Toasts and status lines**: these sit in one stack directly above the prompt box, together with the panes when the panel is narrow, with one status line per mod. The stack is hidden while a permission request or a dialog shows.
- **Labels**: the panel frames what a mod draws or rewrites and labels it with the plugin’s name, as in **From the token-chart plugin** or **Edited by the token-chart plugin**.
- **Keyboard**: buttons have no hotkeys, so the user clicks a button or presses Enter on it once it has focus.
- **Keyboard focus**: the panel grants a pane’s `focus: true` request only when the panel has focus, Claude is idle with no question waiting, and the prompt box is empty.
- **The question dialog**: at `AskUserQuestion`, the panel keeps its own question card and draws your elements above it. Props you pass to `next` don’t change the card.
- **Prompt suggestions**: `$.prompt.suggest` resolves with `isShown: false` and shows nothing, because the panel keeps the prompt box’s dim suggestion text for Claude’s own suggestions.
- **Text limits**: the panel cuts a status line at 200 characters or two rows, and a toast at 500 characters.


[​](#build-a-tree-from-elements)

Build a tree from elements

What a `ui.render` hook returns is an element tree: a description of what to draw, made of boxes, text, and controls nested inside each other. You describe the drawing, and Claude Code renders it in whichever app the user has open: the terminal, the Desktop app, or the VS Code extension. To get the elements, call `$.ui.resolve(e)` in your hook, as in `const { Box, Text, Button } = $.ui.resolve(e)`. Each element is a function. You pass it props, and you put the elements and strings that go inside it in `children`. Select a tab to see each of the most common elements and how the terminal draws it:

- Text

- Box

- Button

- Input

`Text` draws a string, with optional styling such as `bold` and `color`:

```python
Text({ children: ['This is the first tab.'] })
```

```python
This is the first tab.
```

`Box` arranges what’s inside it, in a row or a column. This one puts a button and a line of text side by side, two columns apart:

```python
Box({
  flexDirection: 'row',
  columnGap: 2,
  children: [
    Button({ key: 'more', label: 'Add one', onPress: addOne }),
    Text({ children: ['Count: 0'] }),
  ],
})
```

```python
[ Add one ]  Count: 0
```

`Button` is a control the user can press. It runs your `onPress` callback. With `plain: true` it has no brackets and shows its hotkey:

```python
Button({ key: 'more', label: 'Add one', onPress: addOne })
Button({ key: 'tab-one', label: 'One', hotkey: '1', plain: true, onPress: showTabOne })
```

```python
[ Add one ]
1: One
```

`Input` is a text field. It runs your `onSubmit` callback with the text when the user presses Enter:

```python
Input({
  key: 'new-note',
  label: 'Note',
  placeholder: 'Type a note and press Enter',
  value: '',
  submitLabel: 'add',
  onSubmit: addNote,
})
```

```python
Note: Type a note and press Enter
```

The [interface gallery](plugins-mods-gallery.md) has samples and screenshots of most elements. This table lists every element:

| Element                    | What it draws                                                                                                                                                                                                                                                                                               | Where                      |
|:---------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------|
| `Box`                      | A flex container that takes layout props such as `flexDirection`, [`borderStyle`](plugins-mods-reference.md#box-border-styles), and `width`. Spacing props such as `columnGap` and `padding` take a number or a [step name](plugins-mods-reference.md#box-spacing-steps) from `'xs'` to `'xl'`. | Everywhere                 |
| `Text`                     | Styled text. Takes `color`, `bold`, `dimColor`, `italic`, `wrap`, and others such as [`weight`](plugins-mods-reference.md#text-typography). A `color` is a theme key or a color such as `'red'`. A `wrap` is `'wrap'`, `'truncate'`, `'truncate-start'`, `'truncate-middle'`, or `'truncate-end'`.    | Everywhere                 |
| `Button`                   | A control that calls `onPress`                                                                                                                                                                                                                                                                              | Everywhere                 |
| `Link`, `Code`, `Markdown` | A link with `href` and an optional `label`, a code block, and text formatted the way Claude’s replies are. `Markdown` takes its content in a `text` prop, not in `children`, and needs a `key` when you pass `onLinkPress`.                                                                                 | Everywhere                 |
| `Input`, `Select`          | A text field and a dropdown                                                                                                                                                                                                                                                                                 | Terminal, Desktop, VS Code |
| `Svg`                      | An SVG document                                                                                                                                                                                                                                                                                             | Desktop, VS Code           |
| `Client`                   | A region drawn by a second file of yours, for animation and pointer input. That file gets no mods API. It reaches your hooks by posting data, which arrives as a `ui.message` event. If it fails to load, draw, or run, your hooks receive a [`ui.fault`](plugins-mods-reference.md#interface) event. | Terminal, Desktop          |
| `Raster`, `Image`          | A [grid of colored cells](#draw-a-grid-of-colored-cells), and a picture                                                                                                                                                                                                                                     | Terminal                   |

If your module is a `.tsx` or `.jsx` file, you can write the tree as JSX. Destructure the elements from `$.ui.resolve(e)` first. If a tree uses an element the app doesn’t have, a prop an element doesn’t take, or a child where none goes, Claude Code draws its own version of the site. In a session started with `--plugin-dir`, a transcript line says so, such as `ui.render (Pane) refused: Text prop "bogusProp" is not allowed; the engine drew its own`. The [debug log](plugins-mods-troubleshoot.md#read-the-debug-log) records it as `ui.render (Pane): a hook returned a tree that does not validate` with the same reason. Nothing else appears in the session, so when a drawing doesn’t show up, check that line or the log.


[​](#link-in-the-desktop-app)

`Link` in the Desktop app and VS Code

In the Desktop app and the VS Code extension, a `Link` draws as plain text unless its `href` meets these requirements:

- **Scheme and host**: an `https:` URL, or an `http://localhost` URL such as `http://localhost:3000`
- **No `@`**: write an `@` in the path or query as `%40`
- **Spelling**: what `new URL(href).href` returns, apart from a missing `/` after the host. That excludes an uppercase host, a space, and `:443` on an `https:` URL.

In the terminal, these requirements don’t apply.


[​](#when-a-client-fails)

When a `Client` fails

In the terminal, when the file a `Client` runs fails, a dimmed line such as `my-mod: Client client/spinner.js: boom` takes the `Client`’s place, and the rest of your drawing still shows. If your mod handles [`ui.fault`](plugins-mods-reference.md#interface), Claude Code then [draws the site again](#when-claude-code-redraws-without-being-asked).


[​](#draw-a-grid-of-colored-cells)

Draw a grid of colored cells

For a heat map, a sparkline, or a game board in the terminal, draw one `Raster` and not a `Box` for each cell. A `Raster` takes a `key`, its size in `columns` and `rows`, and `cells`, a base64 string that packs every cell. Each cell is three numbers: the character’s code point, its color, and its background color. A color is a 24-bit RGB value in hexadecimal, such as `0xc62828` for a red. The value `0x01000000`, one above that range, means the terminal’s default. Apps other than the terminal have no `Raster`, so check `e.surface` and draw text there. This pane body draws a three by two heat map:

```python
// The value that means "use the terminal's default color"
const DEFAULT_COLOR = 0x01000000

// Pack rows of [character, color] pairs into the one string a Raster takes
// One cell is three numbers: the character's code point, its color, and its background
function cellsOf(rows) {
  const numbers = rows.flat().flatMap(([char, color]) => [char.codePointAt(0), color, DEFAULT_COLOR])
  return new Uint8Array(Uint32Array.from(numbers).buffer).toBase64()
}

on('ui.render', { component: 'Pane' }, async ($, e, next) => {
  // Draw only in the pane opened with the id 'heat'
  if (e.requestId !== 'heat') return next(e)
  const { Box, Text, Raster } = $.ui.resolve(e)
  // Two rows of three cells, each a block character and its color
  const rows = [
    [['█', 0x2e7d32], ['█', 0xf9a825], ['█', 0xc62828]],
    [['█', 0x2e7d32], ['█', 0x2e7d32], ['█', 0xf9a825]],
  ]
  if (e.surface !== 'terminal') {
    return Text({ children: ['The heat map needs the terminal.'] })
  }
  return Box({
    flexDirection: 'column',
    children: [Raster({ key: 'grid', columns: 3, rows: 2, cells: cellsOf(rows) })],
  })
})
```

In the terminal, the pane shows the grid:

The `rows` array is the part you’d change, and `cellsOf` turns it into the packed string. The hook draws only in a pane whose `id` is `heat`, so open one with `$.ui.open({ id: 'heat' })` from a command, as the [`hello-tabs` example](#build-a-pane-with-tabs) opens its pane. Each character has to be one cell wide. To animate a `Raster` that’s already on screen, call `$.ui.blit` with the pane’s `id` as `requestId`, the `Raster`’s `key`, the same size, and new cells. For this example, that’s `$.ui.blit({ requestId: 'heat', key: 'grid', columns: 3, rows: 2, cells: cellsOf(newRows) })`. It repaints that one element without running your `ui.render` hook again.


[​](#respond-to-presses-and-typing)

Respond to presses and typing

When the user presses a button, types into a field, or picks from a list your mod drew, Claude Code calls that control’s callback, which runs in your module. Each control takes its own callbacks:

- **`Button`**: takes `onPress(e)`, where `e.surface` is the app the press came from
- **`Input`**: takes `onSubmit(value)` and `onInput(value)`
- **`Select`**: takes `onSelect(value)` with its choices in `options`, a list of at least one choice with unique values, such as `[{ value: 'sm', label: 'Small' }, { value: 'lg', label: 'Large' }]`

A test presses or types into a control by its `key`, so give each control one. Each use of a control also fires [`ui.press`, `ui.input`, or `ui.select`](plugins-mods-reference.md#interface) with the `key` in `e.element`, and another mod can handle those events. Its hook runs before your callback, so it sees what the user types into your `Input` and can change it or answer in place of your callback. The mods API has no method that presses another mod’s button.


[​](#know-which-keys-your-mod-can-receive)

Keyboard focus and hotkeys

Your mod never reads the keyboard itself. The user presses a key, Claude Code decides which of your controls it’s for, and that control’s callback runs. Apart from a [digit hotkey on the band or an `action` binding](plugins-mods-reference.md#elements), that happens only while your pane or band has keyboard focus. The rest of the time, keys go to the prompt. In the [VS Code extension](#draw-in-the-vs-code-extension), buttons have no hotkeys: the user clicks a button or presses Enter on it, and types into fields.


[​](#how-a-pane-gets-keyboard-focus)

How a pane gets keyboard focus

A pane gets keyboard focus when:

- Your mod opens it with `focus: true` from a command or a press
- The user presses Ctrl+X then Tab
- The user clicks it, which in the terminal needs [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md)

Claude Code grants `focus: true` only while the prompt is empty and nothing else has keyboard focus. A pane that opens while the user is typing doesn’t take their keystrokes.


[​](#what-each-key-does)

What each key does

This table lists what a key does while your pane or band has keyboard focus:

| Key                               | What it does                                                                                                                                                          |
|:----------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Tab                               | Moves to the next control                                                                                                                                             |
| Up and Down                       | Move between controls while your drawing fits. When the pane or band has more rows than it can show, they scroll it.                                                  |
| Enter                             | Presses the focused `Button`, submits the focused `Input`, or picks in a `Select`                                                                                     |
| A button’s hotkey                 | Presses that button. While an `Input` has the focus, every printable key goes to the field.                                                                           |
| Page Up, Page Down, Home, and End | Scroll your pane or band when it has more rows than it can show                                                                                                       |
| Ctrl+X then an arrow key          | Resizes your pane. Left or Up gives it more room, and Right or Down gives the room back. A pane beside the transcript stops widening at 45% of the terminal’s width.  |
| Ctrl+X then X                     | Closes your pane, even while one of its fields has the focus                                                                                                          |
| Ctrl+X then Ctrl+A                | [Collapses the band](#when-the-user-collapses-the-band) to one row while a mod draws there, or expands it again. These keys also work while the prompt has the focus. |
| Esc                               | Returns keyboard focus to the prompt. With `closeOnEscape: true`, it also closes the pane.                                                                            |

A mod can’t bind Tab or the arrow keys to anything else, so a game steers with `w`, `a`, `s`, and `d`.


[​](#set-a-hotkey-and-the-first-focus)

Set a hotkey and the first focus

- **`hotkey`**: to let the user press a `Button` with one key, give it a `hotkey` of one digit or one lowercase letter, as in `hotkey: 'a'`
- **`autoFocus`**: to choose which control has the focus when the pane opens, add `autoFocus: true` to it. In the [VS Code extension](#draw-in-the-vs-code-extension), `autoFocus` does nothing: the control draws as if you had left it out, and you don’t need to change anything. The prop accepts only `true`, so omit it on the other controls.

How a hotkey shows depends on the button and the app:

| Button                     | In the terminal                     | In the Desktop app                   | In the VS Code extension               |
|:---------------------------|:------------------------------------|:-------------------------------------|:---------------------------------------|
| With brackets, the default | `[ Add one ]`, with no hotkey shown | The label with a small key beside it | The label, and the hotkey does nothing |
| With `plain: true`         | `1: One`                            | The label with a small key beside it | The label, and the hotkey does nothing |

In the terminal, name the key in a bracketed button’s label, or use `plain: true`, so the user can see what to press. The [elements reference](plugins-mods-reference.md#elements) has the other `Button` rules: `action`, digit hotkeys on the band, and two buttons on one hotkey.


[​](#when-the-user-collapses-the-band)

When the user collapses the band

In the terminal, while a mod draws in the band, the user can collapse the band to one row to get it out of the way:

- **Click `[-]`**: while the band is expanded, Claude Code draws this mark at the right end of its first row. In [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md), the user clicks it to collapse the band.
- **Press Ctrl+X then Ctrl+A**: this works whether the prompt, the band, or a pane has keyboard focus. The user can [bind other keys](../02-Claude-Code-CLI/keybindings.md#above-prompt-actions) to it through the `abovePrompt:toggle` action.

A collapsed band hides everything the mods draw there and shows one dim row instead, which names the keys that expand it as the user has them bound:

```python
▸ plugin panel hidden · ctrl+x ctrl+a or click to show
```

The same keys, or in fullscreen rendering a click on the row, expand the band again. Until then, nothing presses a button in it: no hotkey, no digit typed into an empty prompt, and no keybinding for a button’s [`action`](plugins-mods-reference.md#elements). If the band has keyboard focus when the user collapses it, the focus returns to the prompt, and Ctrl+X then Tab skips the band while it’s collapsed. The band stays collapsed until the user expands it, even after `/clear`, and is expanded again the next time Claude Code starts. Your mod can’t expand it or tell that it’s collapsed: the `ui.render` hook keeps running, and nothing in `e.props` says so. Don’t make the band the only place your mod shows something the user has to answer.


[​](#take-typed-input-and-draw-a-row-for-each-item)

Take typed input and draw a row for each item

Many panes are a text field with a list under it. The example in this section is a notes pane: you type a note and press Enter to add it, and each note has an `x` button that deletes it. With two notes added, the terminal draws the pane this way:

```python
╭────────────────────────────────────────────────────────✕─╮
│ Note: Type a note and press Enter ⏎ add                  │
│ x buy milk                                               │
│ x call bob                                               │
╰──────────────────────────────────────────────────────────╯
```

The `✕` on the top border is Claude Code’s own mark for closing the pane. The example uses these techniques:

- **Take typed input**: an `Input` calls `onSubmit(value)` with the field’s text when the user presses Enter, and `onInput(value)` on every change
- **Draw a list**: map your data to one row each, and give every row’s button its own `key`

This hook draws the pane’s content:

```python
// The list the pane draws
let notes = []

on('ui.render', { component: 'Pane' }, async ($, e, next) => {
  // Draw only in the pane opened with the id 'notes'
  if (e.requestId !== 'notes') return next(e)
  const { Box, Text, Button, Input } = $.ui.resolve(e)
  const redraw = () => $.ui.invalidate('ui.render')

  return Box({
    flexDirection: 'column',
    children: [
      Input({
        key: 'new-note',
        label: 'Note',
        placeholder: 'Type a note and press Enter',
        // The text the field starts with
        value: '',
        submitLabel: 'add',
        autoFocus: true,
        // Runs when you press Enter in the field
        onSubmit: async (value) => {
          // Ignore an empty line
          if (!value.trim()) return
          notes = [...notes, value.trim()]
          redraw()
          await $.store.set('notes', notes)
        },
      }),
      // One row for each note: a delete button, then the note's text
      ...notes.map((note, i) =>
        Box({
          flexDirection: 'row',
          columnGap: 1,
          children: [
            Button({
              // A key of its own, so each row's button can be told apart
              key: 'delete-' + i,
              label: 'x',
              plain: true,
              onPress: async () => {
                notes = notes.filter((_, j) => j !== i)
                redraw()
                await $.store.set('notes', notes)
              },
            }),
            Text({ children: [note] }),
          ],
        }),
      ),
    ],
  })
})
```

To try the pane:

- **Add a note**: type a line and press Enter. The line appears as a new row, and the field empties.
- **Delete a note**: press Tab until the note’s `x` button has the focus, then press Enter. The `x` is the button’s label and not a hotkey, so typing the letter doesn’t press it.

Each change follows the same render cycle as `hello-tabs`: the callback changes `notes`, calls `redraw`, and saves the list to `$.store`. If your hook draws the pane again while the user is typing a note, the field keeps their text as long as the hook passes the same `value` each time. To replace their text with your own, pass a different `value`. The example saves the notes and doesn’t load them. To bring them back in the next session, read them in a `session.start` hook, the way `hello-tabs` reads `count`. These props make up the field’s line, `Note: Type a note and press Enter ⏎ add`:

| Prop          | In the example                | What it is                                                                 |
|:--------------|:------------------------------|:---------------------------------------------------------------------------|
| `label`       | `Note`                        | The text before the field. The terminal draws `: ` after it.               |
| `placeholder` | `Type a note and press Enter` | Dim text that shows while the field is empty                               |
| `value`       | `''`                          | The text the field starts with. It’s empty here, so the placeholder shows. |
| `submitLabel` | `add`                         | The word after `⏎` that says what Enter does                               |

Submitting an `Input` doesn’t start a turn unless your callback calls [`$.prompt.submit`](plugins-mods-api.md#start-a-turn-from-a-background-job).


[​](#redraw-when-something-changes)

Redraw a site

A drawing is a snapshot: it shows what your `ui.render` hook returned the last time the hook ran. To show something new, the hook has to run again. Claude Code runs it again for some changes, and your mod asks for the rest.


[​](#when-claude-code-redraws-without-being-asked)

When Claude Code redraws without being asked

Claude Code runs your `ui.render` hook again when the site’s props change or the terminal’s width changes. When a `Client` in the site fails and your mod handles [`ui.fault`](plugins-mods-reference.md#interface), Claude Code runs the hook once more after your `ui.fault` hooks return, so your `ui.render` hook can leave the `Client` out. It doesn’t run the hook on a timer, and it can’t tell when a variable in your module changes.


[​](#redraw-when-your-data-changes)

Redraw when your data changes

To have your sites drawn again after your own data changes, call `$.ui.invalidate('ui.render')`. This pane counts presses. The button’s callback changes `count`, then asks for a redraw:

```python
let count = 0

on('ui.render', { component: 'Pane' }, async ($, e, next) => {
  if (e.requestId !== 'counter') return next(e)
  const { Box, Text, Button } = $.ui.resolve(e)
  return Box({
    flexDirection: 'row',
    columnGap: 2,
    children: [
      Button({
        key: 'more',
        label: 'Add one',
        onPress: () => {
          count += 1
          // The data changed, so ask Claude Code to draw the pane again
          $.ui.invalidate('ui.render')
        },
      }),
      Text({ children: ['Count: ' + count] }),
    ],
  })
})
```

Each press raises the number in the pane. The [`hello-tabs` example](#build-a-pane-with-tabs) wraps the same call in its `redraw` function. A value you keep in [`$.state`](#keep-a-value-in-$-state) doesn’t need the call, because writing the value redraws the sites that read it.


[​](#redraw-on-a-timer)

Redraw on a timer

To keep a clock, a countdown, or a value from outside the session current, redraw on a schedule. Start a timer in the module’s `session.start` hook. If the module already has one, as `hello-tabs` does, add the [`$.clock.every`](plugins-mods-api.md#run-work-in-the-background) line to it:

```python
on('session.start', async ($, e, next) => {
  // Every 1000 milliseconds, ask Claude Code to draw your sites again
  $.clock.every(1000, () => $.ui.invalidate('ui.render'))
  return next(e)
})
```

Claude Code now runs your `ui.render` hook once a second. The timer stops when the module reloads, and the new instance of the module starts its own.


[​](#how-often-a-site-can-redraw)

How often a site can redraw

Claude Code throttles redraws of a site, so your mod can call `$.ui.invalidate` as often as its data changes. For how often each site can redraw, see the [limits table](plugins-mods-reference.md#limits). Calls that come faster than the limit are coalesced into one redraw. That redraw runs your hook once, and the hook reads your data as it is at that moment, so the latest value shows and the values in between don’t. An animation can’t run faster than the limit.


[​](#keep-state)

Keep state

Where a mod keeps a value decides how long the value lasts: until the module reloads, until the session ends, or from one session to the next. Choose by how long the value has to last:

| Keep it in              | It lasts until                                                                                                                                                                                                                                     | Use it for                                                     |
|:------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------|
| A module-level variable | The module reloads, which happens every time you save a file during development                                                                                                                                                                    | Values you can lose, as `tab` is in `hello-tabs`               |
| `$.state`               | The session ends, or the user runs `/clear`, `/resume`, or `/branch`                                                                                                                                                                               | Values a drawing depends on that should survive a reload       |
| `$.store`               | Your mod deletes it, or no session reads or writes the store for [`cleanupPeriodDays`](../02-Claude-Code-CLI/settings-reference.md#cleanupperioddays). The store is a key-value store, saved as a JSON file of your plugin’s own under `~/.claude/plugins/store/`. | Settings, history, anything the user expects to find next time |

`$.store.get(key)` resolves to the value or `undefined`, and `$.store.set(key, value)` takes any JSON value.


[​](#keep-a-value-in-$-state)

Keep a value in `$.state`

`$.state` holds values for the length of a session, and it redraws for you. It’s reactive state: a `ui.render` hook that reads a value subscribes to it, so Claude Code redraws that site each time you write the value, and you don’t call `$.ui.invalidate`. A value in `$.state` also survives a reload of the module, which a variable doesn’t. To set it up, declare your values, point your manifest at the declaration, then define and use each value. The examples move the `count` from `hello-tabs` into `$.state`.


[​](#declare-the-values)

Declare the values

Declare the values in a type declaration file. The outer key is your plugin’s name, and each entry under it is a value and its type. Save this as `hello-tabs/types/index.d.ts`:

hello-tabs/types/index.d.ts

```python
declare module 'claude-code' {
  interface PluginState {
    'hello-tabs': {
      tab: 'one' | 'two'
      count: number
    }
  }
}
```


[​](#point-the-manifest-at-the-declaration)

Point the manifest at the declaration

To let `claude plugin validate` check your code against that file, add a `types` field to the manifest with its path:

hello-tabs/.claude-plugin/plugin.json

```python
{
  "name": "hello-tabs",
  "version": "0.1.0",
  "description": "Opens a pane with two tabs and a counter",
  "author": { "name": "Your Name" },
  "types": "./types/index.d.ts"
}
```


[​](#define-read-and-write-a-value)

Define, read, and write a value

In your module, define each value with a default, read it while drawing, and write it from a callback. `atom` names a value and its default, `read` returns it, and `update` writes it. The three helpers call `$.state.get` and `$.state.set` for you:

```python
import { atom, read, update } from 'claude-code'

// At the top of the module: name the value and give its default
const count = atom({ plugin: 'hello-tabs', key: 'count' }, 0)

// In the ui.render hook: read the value to draw it
const n = await read($, count)

// In a Button: write a new value from the old one
onPress: () => update($, count, (value) => value + 1)
```

Because the `ui.render` hook read `count`, Claude Code runs the hook again each time the button writes it. These rules apply to the code:

- **Write `plugin` and `key` as string literals**: `claude plugin validate` reads them from your source
- **Hold the result of each `atom` call in a `const`**: if you declare `count` with `let`, validation fails with `takes a source the scan can read`
- **Declare every value in the type declaration file**: otherwise validation fails with `hello-tabs.count is not declared`
- **Write from a callback or another event’s hook**: a `ui.render` hook can read state and can’t write it, so write from `onPress`, `onSubmit`, or a hook for another event


[​](#change-hello-tabs-to-use-$-state)

Change `hello-tabs` to use `$.state`

To move `count` in `hello-tabs` into `$.state`, change every line that uses it:

- **At the top of the module**: add the `import` line, and replace `let count = 0` with the `atom` line
- **In the `ui.render` hook**: add the `read` line before `tabButton`, and draw `'Count: ' + n` in the `Text`
- **In the Add one button**: replace `onPress` with the one in [Save from more than one session](#save-from-more-than-one-session), which saves the count as well as writing it
- **In the `session.start` hook**: replace the two lines that read `saved` with the `loadCount` call from [Load a saved value again after `/clear`](#load-a-saved-value-again-after-clear)

Keep `redraw` for the tab buttons, because `tab` is still a variable.


[​](#load-a-saved-value-again-after-clear)

Load a saved value again after `/clear`

If your mod copies a saved value from `$.store` into `$.state` at `session.start`, it has to copy it again after `/clear`, `/resume`, or `/branch`. Those commands reset every `$.state` value to its default, and `session.start` doesn’t fire again. [`classic.SessionStart`](plugins-mods-events.md#hook-the-settings-hook-events) does fire after each of them, with `e.source` set to `clear`, `resume`, or `fork`, so copy the value again in a hook on it. Otherwise your drawing shows the default, and a callback that saves the `$.state` value writes the default over what you stored. This code loads `count` from both hooks. It builds on the `$.state` version of `hello-tabs`, where `count` is an atom and `update` is imported. Put `loadCount` above `register`, and add the `loadCount` call to the `session.start` hook you already have. `classic.SessionStart` also fires at startup and after compaction, which doesn’t reset `$.state`, so the filter on `source` keeps the hook to the three resets:

```python
// Copy the saved count from $.store into $.state, or 0 if nothing is saved
async function loadCount($) {
  const saved = Number((await $.store.get('count')) ?? 0)
  await update($, count, () => saved)
}

// Runs before your first prompt, and again after a reload
on('session.start', async ($, e, next) => {
  await loadCount($)
  return next(e)
})

// Runs again after /clear, /resume, and /branch, which reports fork
on('classic.SessionStart', { source: ['clear', 'resume', 'fork'] }, async ($, e, next) => {
  await loadCount($)
  return next(e)
})
```

With both hooks in place, the pane shows the saved count after `/clear` and not `0`, and the next press of **Add one** adds to the saved count. `loadCount` writes the stored value over the one in `$.state`, and `session.start` fires again each time the module reloads. To keep the store from falling behind, save on every change, as the **Add one** button does. To check the reload without a session, [test the drawing after `/clear`](plugins-mods-test.md#test-a-drawing-after-clear).


[​](#save-from-more-than-one-session)

Save from more than one session

Every session on your machine that runs your mod shares one `$.store`. A `get` followed by a `set` isn’t atomic. When two sessions each read a value, change it, and write it back, they race, and the second write replaces the first. To make that less likely:

- **Give each item its own key**: a `set` changes only its own key, so sessions that write different keys don’t overwrite each other
- **Read again right before you write**: for a value that several sessions change, `get` the key in the callback and build the new value from that, not from a copy you loaded at `session.start`. Another session’s write is still lost if it lands between your `get` and your `set`.

This button adds one to whatever the store holds now, then updates the drawing:

```python
onPress: async () => {
  // Read what the store holds now, which another session may have changed
  const saved = Number((await $.store.get('count')) ?? 0)
  // Save the new count, then show it
  await $.store.set('count', saved + 1)
  await update($, count, () => saved + 1)
}
```

If a second session has pressed its own button three times since this session started, this press shows and saves a count that includes those three.


[​](#next-steps)

Next steps

- [React to events](plugins-mods-events.md): feed your drawing from tool calls and turns
- [Use the mods API](plugins-mods-api.md): feed your drawing from timers and model calls
- [Test a drawing](plugins-mods-test.md#test-a-drawing): press your buttons from a test, on more than one surface
- [Render sites](plugins-mods-reference.md#render-sites) and [elements](plugins-mods-reference.md#elements): each site’s props and each element’s props

---

## Mods overview - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/overview
Path: 08-Plugins-Skills/plugins-mods-overview.md

## On this page

- [What a mod can do](#what-a-mod-can-do)
- [Get a mod](#get-a-mod)
  - [Install or update a mod](#install-or-update-a-mod)
  - [Try a sample mod](#try-a-sample-mod)
- [Decide whether to trust a mod](#decide-whether-to-trust-a-mod)
  - [What a mod can reach](#what-a-mod-can-reach)
  - [List what a mod does before you install one](#list-what-a-mod-does-before-you-install-one)
- [Turn mods on or off](#turn-mods-on-or-off)
  - [See which mods a session loaded](#see-which-mods-a-session-loaded)
- [How a mod works](#how-a-mod-works)
  - [What a hook can do with an event](#what-a-hook-can-do-with-an-event)
  - [Where mods run](#where-mods-run)
- [Control mods for your organization](#control-mods-for-your-organization)
- [Compare mods, settings hooks, skills, and MCP servers](#compare-mods-settings-hooks-skills-and-mcp-servers)
- [Mods built into Claude Code](#mods-built-into-claude-code)
  - [Read the source of built-in mods](#read-the-source-of-built-in-mods)
- [Next steps](#next-steps)

Mods

# Mods overview

Copy pageCopy page

Add panes, commands, and tool call rules to Claude Code with a mod. See what a mod can do, how to make or install one, and where mods run.

Copy pageCopy page

A mod is a [plugin](plugins.md) that changes how Claude Code looks and behaves. It’s made of JavaScript or TypeScript event handlers: Claude Code calls one when an event happens, such as a tool call, a submitted prompt, or a part of the interface being drawn, and the handler can watch the event, change it, or take it over. Use a mod to add a feature of your own to Claude Code, such as a pane that charts how full your context is after each request. For the files in a mod and a complete example, see [How a mod works](#how-a-mod-works).

Claude Code’s existing [hooks](../07-Hooks/hooks.md) also run on events, as a shell command, HTTP request, or prompt you configure in a settings file. A mod’s handlers are functions that run inside Claude Code instead. Claude Code calls both kinds hooks: on these pages, “hook” means a mod’s handler, and the settings-file kind is a “settings hook”.


[​](#what-a-mod-can-do)

What a mod can do

Settings hooks, skills, status lines, and MCP servers work from outside Claude Code: each one runs a script, or gives Claude text or tools. A mod runs inside Claude Code, so it can do things they can’t:

- **Draw an interface you can use**: a pane beside the transcript or a band above the prompt, with tabs, buttons, and text fields. See [Draw in the interface](plugins-mods-interface.md).
- **Redraw Claude Code’s own interface**: replace or restyle parts Claude Code draws itself, such as a tool call’s row, the spinner, or the dialog Claude asks questions in. See [Change what Claude Code already draws](plugins-mods-interface.md#change-what-claude-code-already-draws).
- **Step into a tool call or a request**: for example, hold a tool call while you ask the user a question, answer it without running the tool, or send one request to a different model. See [Guard or change a tool call](plugins-mods-events.md#guard-or-change-a-tool-call) and [Follow a turn](plugins-mods-events.md#follow-a-turn).
- **Run your own code on a command**: a `/command` that runs your function at once, with no Claude turn, even while Claude is working. See [Add a command or a tool](plugins-mods-api.md#add-a-command-or-a-tool).
- **Share data between hooks**: a mod’s hooks share the variables in its file, so what one hook records, another can show. For example, one hook can count tool calls while another shows the count beside the spinner, or one can read each request’s token usage while another charts it in a pane. See [React to events](plugins-mods-events.md).

Mods work in the Claude Code CLI, the Code tab of the Claude Desktop app, and the VS Code extension. See [Where mods run](#where-mods-run) for how they behave elsewhere, such as in `claude -p` and cloud sessions. If a settings hook, a skill, or an MCP server already does what you need, [compare them](#compare-mods-settings-hooks-skills-and-mcp-servers) before you write a mod. To manage mods for an organization, see [Manage mods for your organization](plugins-mods-admin.md).


[​](#get-a-mod)

Get a mod

To start with a mod:

- **Use one you already have**: some of Claude Code’s own features are mods, such as `/diff`. See [Mods built into Claude Code](#mods-built-into-claude-code).
- **Make one**: describe what you want in a Claude Code session, and Claude writes the mod. See [Ask Claude for a mod](plugins-mods-create.md#ask-claude-for-a-mod). To learn how a mod’s code works, [write one yourself](plugins-mods-create.md#write-a-mod-yourself).
- **Install one**: see [Install or update a mod](#install-or-update-a-mod), or [try a sample mod](#try-a-sample-mod)


[​](#install-or-update-a-mod)

Install or update a mod

A mod is code that runs with your permissions. It can read and write your files, start processes, and make network requests. Install mods only from authors and marketplaces you trust. See [Decide whether to trust a mod](#decide-whether-to-trust-a-mod).

A mod installs as a plugin, from a marketplace. Give the plugin’s name, an `@`, and the marketplace’s name. These examples install a plugin named `token-chart` from a marketplace named `your-org`:

- In a Claude Code session, run `/plugin install token-chart@your-org`.
- In your shell, run `claude plugin install token-chart@your-org`.

[Install plugins](discover-plugins.md) covers marketplaces, scopes, the VS Code extension and the Desktop app, and [keeping plugins updated](discover-plugins.md#keep-plugins-updated), all of which apply to a plugin that contains a mod without changes. If you install or update a mod from your shell while a session is open, run `/reload-plugins` in that session to load it. Otherwise it loads the next time you start Claude Code.


[​](#try-a-sample-mod)

Try a sample mod

Anthropic shares sample mods in the [`claude-code/mods` directory of the `claude-code-playground` repository](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods). Each one is a complete plugin, and its README says how it was built. The repository shares them as they are, without support.

- [`token-weather`](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods/token-weather): draws a forecast of your context window above the prompt
- [`blast-radius`](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods/blast-radius): holds a risky shell command, such as `rm -rf` or a force push, and shows what it would change, with buttons to proceed or cancel
- [`replay-theater`](https://github.com/anthropics/claude-code-playground/tree/main/claude-code/mods/replay-theater): adds a `/replay` command that steps through the file edits Claude made in the last turn

A sample mod runs with your permissions. To see what one does before you load it, [list its hooks and calls](#list-what-a-mod-does-before-you-install-one). To try one, clone the repository and [load the mod’s directory for one session](plugins-create.md#load-a-directory-or-archive-for-one-session) with `--plugin-dir`. To confirm the mod loaded, [check which mods the session loaded](#see-which-mods-a-session-loaded). To keep one, [add the clone’s `claude-code/mods` directory as a marketplace](discover-plugins.md#add-a-marketplace), then install the mod from `claude-code-playground-mods`. The marketplace points at your clone, so the mod stops loading if you move or delete it.


[​](#decide-whether-to-trust-a-mod)

Decide whether to trust a mod

A mod is code that runs with your permissions, inside Claude Code. Install mods only from authors and [marketplaces you trust](plugins-security.md).


[​](#what-a-mod-can-reach)

What a mod can reach

A mod runs with your permissions, so before you install one, know what it has access to. Once it loads, a mod can:

- **Act on your machine as you**: read and write files anywhere your user account can, start programs, and make network requests
- **Read your secrets**: environment variables and settings files, including an API key you keep in either
- **See your session**: every prompt you send and every tool call Claude makes
- **Change your session**: rewrite a prompt or a tool call, submit a prompt as if you had typed it, or send a message to another of your sessions
- **Act without asking you**: approve a tool call before you’re asked
- **Spend your usage**: call a model on your plan or API key

Mods aren’t sandboxed. If you turn on [sandboxing](../02-Claude-Code-CLI/sandboxing.md), the sandbox isolates the Bash commands Claude runs, and a process that a mod starts runs outside it. With [managed settings](../02-Claude-Code-CLI/settings.md#check-what-your-organization-enforces) or a Team or Enterprise [`/login`](../02-Claude-Code-CLI/authentication.md#authentication-precedence) as the session’s credential, by default a mod’s approval doesn’t get a call past your `ask` rules or `PreToolUse` hooks. Without either, a mod can approve a call that an `ask` rule would prompt for, or that your `PreToolUse` hook blocked. To check when a mod can also approve a call that a `deny` rule refuses, see [Extend permissions with hooks](../02-Claude-Code-CLI/permissions.md#precedence-among-rules-hooks-and-mods). A mod can restyle much of Claude Code’s interface, but not the permission prompt. It can’t change what a prompt shows you.


[​](#list-what-a-mod-does-before-you-install-one)

List what a mod does before you install one

Before you install a mod, you can list which events it handles and what it asks Claude Code to do, such as read a file or make a network request, without running it. Get the plugin’s files first, for example by cloning its repository. Then, in your shell, run `claude plugin validate` on the plugin’s directory:

```python
claude plugin validate ./some-mod
```

The `hooks:` and `calls:` lines in the output list the events the mod handles and what it asks Claude Code to do. [Review what a mod can do](plugins-mods-admin.md#review-what-a-mod-can-do) shows the output and which calls to look for.


[​](#turn-mods-on-or-off)

Turn mods on or off

Mods are on by default. In the terminal, use Claude Code v2.1.287 or later. The Desktop app includes its own copy of Claude Code, and mods work there from v2.1.286. Check the version in the place you use mods:

- **Terminal**: in your shell, run `claude --version`. If yours is older, [update Claude Code](../01-Getting-Started/setup.md#update-claude-code).
- **Desktop app**: in a local session in the Code tab, enter `/status` and read the **Claude Code** row, which shows a version such as `2.1.286`. If yours is older, [update the Desktop app](../16-Mobile-Desktop/desktop.md#claude-code-version-in-the-code-tab).

To turn mods off, choose how many to stop, and for how long. To turn them back on, undo the same change:

- **One mod**: disable or uninstall its plugin from the [**Installed** tab in `/plugin`](discover-plugins.md#manage-installed-plugins)
- **Every installed mod, for one session**: start Claude Code with [`--safe-mode`](../02-Claude-Code-CLI/cli-reference.md#cli-flags), which also disables your other customizations
- **Every mod you installed, in every session**: set [`"disableAllHooks": true`](../02-Claude-Code-CLI/settings-reference.md#disableallhooks) in `~/.claude/settings.json`. Your settings hooks and custom status line stop too. What your organization manages keeps running.

If you use Claude Code through an organization, an administrator can also limit which mods load. Administrators start at [Stop user-installed mods from loading](plugins-mods-admin.md#stop-user-installed-mods-from-loading). `disableAllHooks` and your organization’s `allowManagedModsOnly` stop a mod and leave the rest of its plugin in place: the plugin stays installed, and its skills, commands, agents, and MCP servers load. Other settings and flags reach further. [`disableAllHooks`](../02-Claude-Code-CLI/settings-reference.md#disableallhooks) and [What runs under `allowManagedHooksOnly`](../02-Claude-Code-CLI/settings-reference.md#what-runs-under-allowmanagedhooksonly) list what each one does to a plugin and its settings hooks. To find out whether mods can load for you, see [Check whether mods can load](plugins-mods-troubleshoot.md#check-whether-mods-can-load).

If you set `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` during early access, remove it. Claude Code v2.1.287 and later ignores it, so setting it to `0` doesn’t keep mods off.


[​](#see-which-mods-a-session-loaded)

See which mods a session loaded

To see which mods a terminal session loaded, run `/plugin` at the Claude Code prompt. A dim line under the tabs gives the count and the names, such as `1 mod active · first-mod`. If a mod you installed isn’t named there, see [Find out why a mod does nothing](plugins-mods-troubleshoot.md#find-out-why-a-mod-does-nothing).


[​](#how-a-mod-works)

How a mod works

A mod is a [plugin](plugins.md) whose code registers event handlers, called hooks. Claude Code runs a hook when its event happens, such as when Claude calls a tool or when the spinner is drawn. A small mod has three files:

```python
first-mod/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.js
```

- **`plugin.json`**: the plugin’s [manifest](plugins-reference.md)
- **`hooks.json`**: [points to your code file](plugins-mods-reference.md#files)
- **`register.js`**: [your code](plugins-mods-create.md#write-a-mod-yourself), called the hooks module. It tells Claude Code which events to run your functions on.

This is a complete `register.js`. It counts the tool calls Claude makes and shows the count beside the spinner while Claude works, as in `Thinking · tool calls: 3…`.

hooks/register.js

```python
// The count, shared by the two hooks below
let calls = 0

// Claude Code calls this once when the mod loads
export function register(on) {
  // Runs each time Claude is about to use a tool
  on('tool.call', async ($, e, next) => {
    calls += 1
    // Ask Claude Code to draw the interface again, so the new count shows
    $.ui.invalidate('ui.render')
    // Let the tool run as usual
    return next(e)
  })

  // Runs each time Claude Code draws the spinner
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    // Keep Claude Code's spinner, with the count added after its word
    return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
  })
}
```

The file registers two hooks, and both use the `calls` variable at the top:

- **The [`tool.call`](plugins-mods-reference.md#tools) hook** runs each time Claude is about to use a tool. It adds one to `calls`, asks Claude Code to draw the interface again, and lets the tool run as usual.
- **The [`ui.render`](plugins-mods-reference.md#interface) hook** runs each time Claude Code draws the spinner. It keeps Claude Code’s own spinner and adds the count after the word.

This recording shows the mod at work. Watch the spinner line above the prompt box: while Claude lists a directory and reads two files, it reads `Thinking · tool calls: 1…`, then `2…`, then `3…`.


[​](#what-a-hook-can-do-with-an-event)

What a hook can do with an event

Claude Code runs your hook before it acts on the event, so the hook decides what happens next. It can:

- **Observe**: note what’s happening and let it continue unchanged, as the `tool.call` hook in the example does
- **Rewrite**: change the event before it continues, as the `ui.render` hook does when it adds the count to the spinner
- **Answer**: handle the event itself, so the usual behavior doesn’t run, such as refusing a command

To do anything outside its own code, such as draw, add a command, call a model, read a file, start a process, or make a network request, a hook calls the mods API. A hook has no other way to do those things, which is why Claude Code can [list what a mod does](#list-what-a-mod-does-before-you-install-one) before you install it. For the code behind each choice, see [React to events](plugins-mods-events.md#how-a-hook-handles-an-event). For what a hook can call, see [Use the mods API](plugins-mods-api.md).


[​](#where-mods-run)

Where mods run

A mod’s hooks run in every kind of session that loads the plugin, whichever provider serves the model, including Amazon Bedrock, Google Cloud’s Agent Platform, and Microsoft Foundry. Drawing is narrower: what a mod draws appears in the terminal, the Desktop app, and the VS Code extension. This table lists each place you might run Claude Code:

| Where you run Claude Code                                                                  | Hooks run                                                                                                         | What the mod draws appears                                                                                                                                                                                                                                                                                                                                                 |
|:-------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `claude` in a terminal, including an editor’s integrated terminal and the JetBrains plugin | Yes                                                                                                               | Yes                                                                                                                                                                                                                                                                                                                                                                        |
| The Code tab of the Desktop app, except in a WSL session                                   | Yes                                                                                                               | Yes, except elements the [elements table](plugins-mods-reference.md#elements) marks terminal-only                                                                                                                                                                                                                                                                    |
| A [WSL session](../16-Mobile-Desktop/desktop-wsl.md) in the Desktop app                                   | No, because plugins aren’t available in WSL sessions                                                              | No                                                                                                                                                                                                                                                                                                                                                                         |
| The VS Code extension’s chat panel                                                         | Yes                                                                                                               | Yes, with extension version 2.1.299 or later. A session that [doesn’t fetch feature flags](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching), such as one on Amazon Bedrock, needs 2.1.304 or later. The [render sites](plugins-mods-reference.md#render-sites) and [elements](plugins-mods-reference.md#elements) tables mark what the panel draws. |
| `claude -p` and the [Agent SDK](../05-Agent-SDK/agent-sdk-overview.md)                               | Yes                                                                                                               | No                                                                                                                                                                                                                                                                                                                                                                         |
| [Remote Control](../02-Claude-Code-CLI/remote-control.md) from claude.ai or the mobile app                 | Yes, in the session on your machine                                                                               | In the terminal on your machine                                                                                                                                                                                                                                                                                                                                            |
| A [cloud session](../02-Claude-Code-CLI/claude-code-on-the-web.md)                                         | Yes, for a plugin that [reaches the cloud session](../02-Claude-Code-CLI/cloud-environments.md#what-carries-over-from-your-setup) | No                                                                                                                                                                                                                                                                                                                                                                         |

A mod that draws can check which app it’s running in, and fall back to a line in the transcript or a command’s text reply where nothing draws.


[​](#control-mods-for-your-organization)

Control mods for your organization

Administrators decide whether mods run and which ones, through [managed settings](../13-Enterprise-Admin/managed-settings.md). [Manage mods for your organization](plugins-mods-admin.md) covers what happens by default, how to review a mod, and how to enforce a policy with a mod of your own.


[​](#compare-mods-settings-hooks-skills-and-mcp-servers)

Compare mods, settings hooks, skills, and MCP servers

Mods, settings hooks, skills, and MCP servers overlap. This table shows what each one is and when to pick it.

|                              | Mod                                                                                | Settings hook                                                                                              | Skill                                            | MCP server                                             |
|:-----------------------------|:-----------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|:-------------------------------------------------|:-------------------------------------------------------|
| What it is                   | Functions in a plugin that Claude Code calls in its own process                    | A shell command, HTTP request, or prompt that Claude Code runs on a lifecycle event                        | A `SKILL.md` file of instructions Claude reads   | An external process or service that gives Claude tools |
| What it can change           | Tool calls, prompts, commands, turns, and what the interface draws                 | Whether a tool call or prompt goes ahead, a tool call’s arguments and result, and context added for Claude | What Claude knows and does                       | Which tools Claude has                                 |
| Can it draw in the interface | Yes                                                                                | No                                                                                                         | No                                               | No                                                     |
| What you write               | JavaScript or TypeScript                                                           | A script and a `settings.json` entry                                                                       | Markdown                                         | A server in any language                               |
| Pick it when                 | You want a pane, a band above the prompt, a custom command, or to rewrite an event | You want to block, allow, or log an event with a script you already have                                   | You keep pasting the same instructions into chat | Claude needs to reach an external system               |

Each of the others has its own page: [Hooks](../07-Hooks/hooks.md), [Skills](skills.md), and [MCP](../06-MCP-Tools/General/mcp.md). A plugin can hold all of them, so a mod can ship in the same plugin as a skill and an MCP server.


[​](#mods-built-into-claude-code)

Mods built into Claude Code

Some of Claude Code’s own features are mods. To see the ones your session has, run `/plugin` at the Claude Code prompt and go to the **Installed** tab, which lists them under **Built-in**. You can’t update or uninstall a built-in mod, and the table’s last column says how to turn each one off. The [`mods active` line](#see-which-mods-a-session-loaded) omits built-in mods. This table lists each entry by the name `/plugin` shows:

| Name in `/plugin`            | What it does                                                                                                                                                       | Where it’s on                                                                                                           | How to turn it off                                                                                                            |
|:-----------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------|
| `cc-plugin-agents-md`        | Loads `AGENTS.md` as project instructions                                                                                                                          | Every session, apart from [the ones that can’t read `AGENTS.md`](../02-Claude-Code-CLI/memory.md#when-agents-md-support-is-unavailable) | Disable it in `/plugin`, or [choose which instruction files load](../02-Claude-Code-CLI/memory.md#choose-which-instruction-files-load)        |
| `cc-plugin-diff`             | Takes over [`/diff`](../02-Claude-Code-CLI/interactive-mode.md#review-changes-with-%2Fdiff) and draws its pane                                                                     | Interactive terminal sessions                                                                                           | Disable it in `/plugin`. `/diff` stays, and Claude Code’s built-in version of the command answers it.                         |
| `cc-plugin-plugin-authoring` | Gives Claude the [`plugin-authoring` skill](plugins-mods-create.md#ask-claude-for-a-mod) for writing mods. It holds a skill and no mod code.                 | Unless Anthropic has turned installed mods off remotely                                                                 | Disable it in `/plugin`                                                                                                       |
| `cc-plugin-sec-default`      | Guards what your organization manages from the mods a user installs                                                                                                | [Where the guard loads](plugins-mods-admin.md#know-what-happens-by-default)                                       | You can’t. An administrator [sets the order](plugins-mods-admin.md#install-your-organizations-mods) in managed settings |
| `cc-plugin-telemetry`        | Sends the analytics records that Claude Code and its built-in mods log                                                                                             | Wherever Claude Code’s own analytics are on                                                                             | Disable it in `/plugin`, or turn analytics off, for example with [`DISABLE_TELEMETRY`](../02-Claude-Code-CLI/env-vars.md)                     |
| `cc-plugin-you-should-know`  | [You should know](../02-Claude-Code-CLI/interactive-mode.md#you-should-know-suggestions): a side agent shows a note above the prompt when it finds something you might have missed | Off until you [turn it on](../02-Claude-Code-CLI/interactive-mode.md#you-should-know-suggestions)                                       | Disable it in `/plugin`                                                                                                       |

The settings and flags that stop installed mods, such as `disableAllHooks`, `--bare`, and `--safe-mode`, don’t stop built-in mods.


[​](#read-the-source-of-built-in-mods)

Read the source of built-in mods

The source of some of these mods is public in the [`mods` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods). Each one is a complete plugin with its hooks module and tests:

- [`diff`](https://github.com/anthropics/claude-code/tree/main/mods/diff): the `/diff` pane, with buttons bound to keyboard actions and scrolling the mod handles itself
- [`agents-md`](https://github.com/anthropics/claude-code/tree/main/mods/agents-md): loads `AGENTS.md` as project instructions, with a [`userConfig`](plugins-components.md#user-configuration) option
- [`sec-default`](https://github.com/anthropics/claude-code/tree/main/mods/sec-default): the guard described in [Know what happens by default](plugins-mods-admin.md#know-what-happens-by-default), a model for a mod that enforces policy
- [`telemetry`](https://github.com/anthropics/claude-code/tree/main/mods/telemetry): adds methods that other mods can call, and ships their types


[​](#next-steps)

Next steps

- [Create a mod](plugins-mods-create.md): build one that counts tool calls, shows the count beside the spinner, and adds a command, and learn the edit and reload loop
- [Draw in the interface](plugins-mods-interface.md): panes, the band above the prompt, buttons, text fields, and state
- [React to events](plugins-mods-events.md): tool calls, prompts, turns, and the order mods run in
- [Use the mods API](plugins-mods-api.md): commands, tools, model calls, timers, and files
- [Test a mod](plugins-mods-test.md): automated tests that run without a session
- [Troubleshoot a mod](plugins-mods-troubleshoot.md): the reasons a mod does nothing, and the debug log
- [Manage mods for your organization](plugins-mods-admin.md): defaults, managed settings, reviewing a mod, and policy mods
- [Mods reference](plugins-mods-reference.md): events, methods, elements, and limits

---

## Mods reference - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/reference
Path: 08-Plugins-Skills/plugins-mods-reference.md

## On this page

- [Files](#files)
- [The hook function](#the-hook-function)
- [Events](#events)
  - [Tools](#tools)
  - [Agent and organization fields on tool.check](#agent-and-organization-fields-on-tool-check)
  - [The e.isReadOnly field on tool.check](#tool-check-read-only)
  - [Prompts and what Claude reads](#prompts-and-what-claude-reads)
  - [Source and frontmatter fields on skill.prompt](#skill-prompt-fields)
  - [Commands and configuration](#commands-and-configuration)
  - [Turns](#turns)
  - [Session](#session)
  - [Subagents](#subagents)
  - [Interface](#interface)
  - [Other mods](#other-mods)
  - [Telemetry](#telemetry)
  - [Settings hook events](#settings-hook-events)
  - [Mods API calls](#mods-api-calls)
- [Mods API methods](#mods-api-methods)
- [Render sites](#render-sites)
- [Elements](#elements)
  - [Box border styles](#box-border-styles)
  - [Box spacing steps](#box-spacing-steps)
  - [Text typography props](#text-typography)
  - [Replace bold with weight](#text-bold)
- [Limits](#limits)
- [Settings and environment variables](#settings-and-environment-variables)
- [Commands](#commands)

Mods

# Mods reference

Copy pageCopy page

Complete reference for Claude Code mods: hooks module layout, events, mods API methods, render sites, elements by surface, limits, and settings.

Copy pageCopy page

Look up any event a [mod](plugins-mods-overview.md) can handle, mods API method it can call, or render site it can draw in. Entries cover the Claude Code CLI and the Desktop app as of v2.1.290, and the VS Code extension as of v2.1.299. Each entry gives the name and a one-line description, and links to the guide section that explains it where there is one.

The complete reference is Claude Code’s [TypeScript declarations for mods](https://github.com/anthropics/claude-code/blob/main/mods/types/claude-code.d.ts), which describe every event, method, and element, with examples. The copy on GitHub can be older than the Claude Code version you have installed. When the two disagree, trust [the copy Claude Code writes for your version](plugins-mods-create.md#get-the-types-for-your-build).


[​](#files)

Files

A mod is a plugin directory with these files:

| File                                                                                                       | Required                                                        | Contents                                                                                                                                                                           |
|:-----------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.claude-plugin/plugin.json`                                                                               | Yes                                                             | The plugin [manifest](plugins-reference.md). Mods add no required fields.                                                                                           |
| `hooks/hooks.json`                                                                                         | Yes                                                             | `modules`: an array with one path, relative to this file, to the hooks module, as in `"modules": ["./register.js"]`. Can also hold [settings hooks](../07-Hooks/hooks.md) under `hooks`. |
| The hooks module, such as [`hooks/register.js`](plugins-mods-create.md#write-a-mod-yourself)         | Yes                                                             | The mod’s entry point. Exports `register(on, options)`. Named `.js`, `.mjs`, `.cjs`, `.jsx`, `.ts`, `.mts`, `.cts`, or `.tsx`. An ES module.                                       |
| [`types/index.d.ts`](plugins-mods-interface.md#declare-the-values), named by `types` in the manifest | When the mod uses `$.state` or adds a namespace to the mods API | Declares `PluginState` values and any namespace the mod adds                                                                                                                       |
| Files whose names end in `.test.ts` or `.test.tsx`                                                         | No                                                              | Tests that [`claude plugin test`](plugins-mods-test.md#write-a-test) runs                                                                                                    |

`register` receives `on` and `options`. `options` holds the values of the [`userConfig`](plugins-components.md#user-configuration) fields the manifest declares, with defaults filled in.


[​](#the-hook-function)

The hook function

A mod registers each of its hooks, which are event handlers, by calling `on` inside `register`. `on` takes the event’s name, an optional [matcher](plugins-mods-events.md#filter-which-events-a-hook-handles), which is a filter on the event’s fields, and the hook, as in `on('tool.call', { tool: 'Bash' }, async ($, e, next) => next(e))`. `on` returns a registration with one method, `.catch(handler)`, which sets the hook’s [error handler](plugins-mods-events.md#handle-a-hook-that-fails).

| Argument                                                              | What it is                                                                                                                                                                                                                                                                                                                                                                                                                  |
|:----------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`$`](plugins-mods-events.md#how-a-hook-handles-an-event)       | The mods API: every method in [mods API methods](#mods-api-methods). Write each call in full, namespace then method, as in `$.fs.read('notes.md')`.                                                                                                                                                                                                                                                                         |
| [`e`](plugins-mods-events.md#how-a-hook-handles-an-event)       | The event’s input, as deeply frozen plain data. To change it, pass a copy to `next`.                                                                                                                                                                                                                                                                                                                                        |
| [`next(e)`](plugins-mods-events.md#how-a-hook-handles-an-event) | The next handler, as in middleware. Runs the hooks after this one, then Claude Code’s behavior. Resolves to the event’s result.                                                                                                                                                                                                                                                                                             |
| [`next.signal`](plugins-mods-api.md#stop-background-work)       | An `AbortSignal` that aborts when the event is abandoned                                                                                                                                                                                                                                                                                                                                                                    |
| `next.origin`                                                         | `{ plugin, tier }` of whoever fired the event. Claude Code itself is `{ plugin: 'engine', tier: 'core' }`. A mod’s `tier` is its priority group in the [order mods run in](plugins-mods-events.md#the-order-mods-run-in): `prepend`, `user`, `append`, or `builtin`.                                                                                                                                                  |
| `next.budget`                                                         | The hook’s time limit in milliseconds: `next.budget.ms` is the whole limit, and `next.budget.remainingMs` is what’s left now                                                                                                                                                                                                                                                                                                |
| `next.to(e, tier)`                                                    | Skips to a later tier, which is `append`, `builtin`, or `core`. `next.to(e, 'append')` skips the mods a user installed. Only a mod in `prependPlugins` or `appendPlugins` can call it.                                                                                                                                                                                                                                      |
| `next.error`                                                          | In a `.catch` handler only. `kind` is `throw` or `timeout` when the hook failed, and `message` is the error’s text. `kind` is `re-entry` when the hook was skipped because the event came from inside a mods API call by that hook or by your mod’s code outside its hooks. `re-entry` requires Claude Code v2.1.292 or later.                                                                                              |
| `next.error.cause`                                                    | Set only when `kind` is `re-entry`. `rewritten` if a `tool.call` or [`PreToolUse`](../07-Hooks/hooks.md#pretooluse) hook changed the mods API call the event came from, or an `agent.spawn` hook changed an agent started by that call. `lent` when a method another mod adds to the mods API fired that event, unless `rewritten` also applies. `lent` requires Claude Code v2.1.292 or later and `rewritten` v2.1.300 or later. |
| `next.called`                                                         | In a `.catch` handler only. `true` when the hook had called `next`.                                                                                                                                                                                                                                                                                                                                                         |


[​](#events)

Events

Events are grouped by what they concern, each with when it fires and what a hook on it can return. Hooks on `turn.step` and `process.spawn` are async generators, and other hooks are async functions. The last column of each table uses shorthand. `next(e)` passes the event on unchanged. `next({ ...e, text })` passes on a copy with the named field changed, as in `next({ ...e, text: e.text.trim() })`. An object answers the event without calling `next`, and a word such as `reason` stands for a string you write, as in `{ deny: 'Use the file tools.' }`.


[​](#tools)

Tools

Tool events fire around each tool call Claude makes, from the description Claude reads to the decision on whether the call runs:

| Event                                                                              | Fires when                                                                                                                                                                                                                                                  | A hook can return                                                                                                              |
|:-----------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|
| [`tool.call`](plugins-mods-events.md#guard-or-change-a-tool-call)            | A tool is about to run                                                                                                                                                                                                                                      | `next(e)`, `{ deny: reason }`, or `{ result }`                                                                                 |
| [`tool.check`](plugins-mods-events.md#where-settings-hooks-run-in-the-order) | Claude Code decides whether a tool call may run, after the `tool.call` and `PreToolUse` hooks. `next(e)` resolves to the decision the rules, the permission mode, and those hooks reached.                                                                  | `next(e)` or `{ decision }`, with `decision` set to `allow`, `ask`, or `deny` and an optional `reason`                         |
| `tool.describe`                                                                    | Once for each tool, when its description is first sent to Claude. A second time for an MCP tool when Claude loads it through [tool search](../06-MCP-Tools/General/mcp.md#scale-with-mcp-tool-search), with `e.description` set to the text Claude reads for the loaded tool. | `{ description }`, optionally with `isDeferred` set to `true` to put the tool behind tool search or `false` to load it upfront |


[​](#agent-and-organization-fields-on-tool-check)

Agent and organization fields on `tool.check`

In a `tool.check` hook, read these fields to tell a subagent’s call from the main conversation’s, and to see whether your organization requires approval for a connector tool:

- **`e.agentId`**: set when a [subagent](../09-Agents-Patterns/sub-agents.md) or an [in-process teammate](../09-Agents-Patterns/agent-teams.md#choose-a-display-mode) makes the call, and absent when the main conversation does
- **`e.ceiling`**: `ask` for a connector tool your organization set to `ask`, in [sessions where that setting reaches Claude Code](../06-MCP-Tools/General/mcp.md#organization-controls-on-connector-tools)

On `tool.check`, `e.agentId` and `e.ceiling` require Claude Code v2.1.290 or later.


[​](#tool-check-read-only)

The `e.isReadOnly` field on `tool.check`

In a `tool.check` hook, `e.isReadOnly` is `true` when the tool reports this call as read-only. Otherwise the field is absent, not `false`. The value is the tool’s own report, so read [Check whether a tool call is read-only](plugins-mods-events.md#check-whether-a-tool-call-is-read-only) before you approve a call on this field alone. On `tool.check`, `e.isReadOnly` requires Claude Code v2.1.304 or later.


[​](#prompts-and-what-claude-reads)

Prompts and what Claude reads

Prompt events cover the text the user types and the text Claude Code sends to Claude on its own, such as the system prompt and reminders:

| Event                                                                       | Fires when                                                                                                                                                                                | A hook can return                                                                                                                               |
|:----------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------|
| [`prompt.submit`](plugins-mods-events.md#rewrite-or-add-to-a-prompt)  | A prompt is submitted                                                                                                                                                                     | `next({ ...e, text })`, `next({ ...e, context })`, or `{ drop: reason }`                                                                        |
| `prompt.fill`, `prompt.suggest`                                             | Text is about to go into the prompt box as a draft, or as a dim suggestion                                                                                                                | `next(e)` with changed text                                                                                                                     |
| `prompt.edit`                                                               | The user edits the prompt box                                                                                                                                                             | `next(e)`                                                                                                                                       |
| `prompt.autocomplete`                                                       | The user types in the prompt box and a word ends at the cursor. `e.token` is that word. Claude Code doesn’t wait for the hook: its own autocomplete rows show first.                      | `{ suggestions }`, a list of `{ text, label, description }` rows shown below Claude Code’s own. Choosing a row writes its `text` over the word. |
| `prompt.compose`                                                            | Claude Code renders a system prompt                                                                                                                                                       | `{ sections }`, a list of `{ id, text, scope }` in the order they’re sent                                                                       |
| [`prompt.section`](plugins-mods-events.md#rewrite-or-add-to-a-prompt) | Once for each named section of the system prompt. `e.name` is the section’s `id` in `prompt.compose`.                                                                                     | `{ text }`, or `{ text: null }` to omit the section                                                                                             |
| [`prompt.context`](plugins-mods-events.md#rewrite-or-add-to-a-prompt) | Once for each conversation, for the context sent with the first message                                                                                                                   | `{ blocks }`                                                                                                                                    |
| `prompt.attachment`                                                         | Claude Code adds a message of its own for Claude, such as a reminder. `e.type` names the kind, and for the kinds the types declare, `e.detail` holds the facts the text was written from. | `{ text }`, or `{ text: null }` to omit it                                                                                                      |
| `prompt.mention`                                                            | Claude Code is about to read a file that a prompt @-mentions. Requires Claude Code v2.1.290 or later.                                                                                     | `next({ ...e, path })` to read a different file, or `{ deny: reason }`                                                                          |
| [`skill.prompt`](plugins-mods-events.md#rewrite-or-add-to-a-prompt)   | A skill’s text is expanded for Claude                                                                                                                                                     | `{ text }`                                                                                                                                      |
| `attribution.text`                                                          | Claude Code composes commit or pull request attribution text                                                                                                                              | `{ text }`                                                                                                                                      |


[​](#skill-prompt-fields)

Source and frontmatter fields on `skill.prompt`

In a `skill.prompt` hook, you can read these fields along with the skill’s name in `e.skill` and the text Claude reads in `e.text`:

| Field                  | Type   | What it holds                                                                                                                                                                                                                                                                     |
|:-----------------------|:-------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `e.source`             | string | Who provides the skill: `policySettings`, `userSettings`, or `projectSettings` for a skill in the enterprise, personal, or project [location](skills.md#where-skills-live), respectively. Other sources give a word such as `plugin`, `mcp`, `syncedSkills`, or `built-in`. |
| `e.pluginName`         | string | The plugin’s name, for a skill whose `e.source` is `plugin`                                                                                                                                                                                                                       |
| `e.path`               | string | The absolute path of the skill’s `SKILL.md` file, or of its `.md` file for a skill written as a [command file](skills.md#where-skills-live). Absent when the skill has no file on this machine, as with a built-in skill.                                                   |
| `e.frontmatter`        | object | The frontmatter of the skill’s file, as plain data                                                                                                                                                                                                                                |
| `e.frontmatterProblem` | string | The reason `e.frontmatter` is missing the file’s frontmatter or one of its keys, as in `is a list or a single value, not a table`. Absent when `e.frontmatter` holds the whole frontmatter or the skill has none.                                                                 |

A hook can pass a changed `text` or `frontmatter` to `next`: the hooks after it read both, and Claude reads the final `text`. A hook that changes any other field [fails](plugins-mods-events.md#handle-a-hook-that-fails). On `skill.prompt`, `e.source`, `e.pluginName`, `e.path`, `e.frontmatter`, and `e.frontmatterProblem` require Claude Code v2.1.299 or later.


[​](#commands-and-configuration)

Commands and configuration

Command and configuration events fire when a command runs or is listed, and when a `/config` row is shown or changed:

| Event                                                    | Fires when                                  | A hook can return                             |
|:---------------------------------------------------------|:--------------------------------------------|:----------------------------------------------|
| [`command.run`](plugins-mods-api.md#add-a-command) | A command is about to run                   | `{ text }`, `{}`, or `next(e)`                |
| `command.describe`                                       | Once for each command, for the command list | `{ description, argumentHint, isHidden }`     |
| `config.set`                                             | A `/config` row is about to change          | `next({ ...e, value })` or `{ deny: reason }` |
| `config.describe`                                        | Once for each `/config` row                 | `{ label, description, isHidden }`            |


[​](#turns)

Turns

Turn events follow one answer from start to finish, including each request to the model within it:

| Event                                                         | Fires when                              | A hook can return                                                      |
|:--------------------------------------------------------------|:----------------------------------------|:-----------------------------------------------------------------------|
| [`turn.start`](plugins-mods-events.md#follow-a-turn)    | A turn begins                           | `next(e)`                                                              |
| [`turn.step`](plugins-mods-events.md#follow-a-turn)     | One request is about to go to the model | `yield* next(e)`, or `next({ ...e, model })`, `next({ ...e, effort })` |
| [`turn.complete`](plugins-mods-events.md#follow-a-turn) | A turn ended                            | `next(e)`, or `{ text }` to show a line under the answer               |


[​](#session)

Session

Session events mark the session starting, ending, compacting, and exchanging messages with other sessions:

| Event                                                                                                                                                                             | Fires when                                                                                                                                                                                      | A hook can return                                                                                                                            |
|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------|
| [`session.start`](plugins-mods-api.md#add-a-command-or-a-tool)                                                                                                              | Once for each loaded mod, before the first prompt, and again after a reload of that mod. Not after `/clear`, `/resume`, or `/branch`.                                                           | `next(e)`                                                                                                                                    |
| `session.end`                                                                                                                                                                     | The session ends, or `/clear`, `/resume`, or `/branch` runs. `e.reason` is `clear`, `resume`, `logout`, `prompt_input_exit`, or `other`. `/branch` reports `resume`.                            | `next(e)`                                                                                                                                    |
| `session.compact`                                                                                                                                                                 | The conversation is about to be compacted                                                                                                                                                       | `{ skip: reason }`                                                                                                                           |
| [`session.receive`](plugins-mods-api.md#send-and-receive-messages-between-sessions), [`session.send`](plugins-mods-api.md#send-and-receive-messages-between-sessions) | A message arrives from, or is about to go to, another agent or session. See [Send and receive messages between sessions](plugins-mods-api.md#send-and-receive-messages-between-sessions). | `{ consumed: reason }` for `receive`, `{ isDelivered: false, reason }` for `send`                                                            |
| `session.append`                                                                                                                                                                  | Once for each row the conversation keeps, such as a prompt, a response block, a tool result, or a notice, before it’s stored                                                                    | `next({ ...e, message })` with a changed `message.content`, to rewrite the row’s text blocks or the `content` of a `tool_result` block in it |
| `session.attach`, `session.detach`                                                                                                                                                | Another app connects to or disconnects from the session                                                                                                                                         | `next(e)`                                                                                                                                    |
| `session.measure`                                                                                                                                                                 | After each turn, and when a plan limit’s percent used changes                                                                                                                                   | `next(e)`                                                                                                                                    |


[​](#subagents)

Subagents

Subagent events fire when a subagent type is offered to Claude and when a subagent or an agent-team teammate is about to start:

| Event         | Fires when                                                                                                                | A hook can return                                                  |
|:--------------|:--------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------|
| `agent.offer` | A subagent type is offered to Claude                                                                                      | `{ isOffered: false }` to withhold it                              |
| `agent.spawn` | A subagent or an [agent team](../09-Agents-Patterns/agent-teams.md) teammate is about to start. For a teammate, `e.isTeammate` is `true`. | `next({ ...e, model })` to choose its model, or `{ deny: reason }` |

When Claude resumes a subagent with the [`SendMessage`](../09-Agents-Patterns/sub-agents.md#resume-subagents) tool, your `agent.spawn` hook doesn’t run again. To refuse the `SendMessage` calls that resume a subagent, match that tool in a [`tool.call`](plugins-mods-events.md#guard-or-change-a-tool-call) hook.


[​](#interface)

Interface

Interface events fire when Claude Code draws a render site and when the user uses a control a mod drew. [Draw in the interface](plugins-mods-interface.md) shows what a `ui.render` hook returns:

| Event                                                                                                                                                                                                                                  | Fires when                                                                                                                                                                           |
|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`ui.render`](plugins-mods-interface.md#pick-where-to-draw)                                                                                                                                                                      | A [render site](#render-sites) is about to be drawn                                                                                                                                  |
| `ui.resolve`                                                                                                                                                                                                                           | Mods load, once for each app, render site, and mod. The result is the element table that `$.ui.resolve(e)` reads.                                                                    |
| [`ui.press`](plugins-mods-interface.md#respond-to-presses-and-typing), [`ui.input`](plugins-mods-interface.md#respond-to-presses-and-typing), [`ui.select`](plugins-mods-interface.md#respond-to-presses-and-typing) | A `Button`, `Input`, or `Select` a mod drew is used                                                                                                                                  |
| `ui.focus`, `ui.scroll`                                                                                                                                                                                                                | The focused control or the scroll position of a pane or the band is about to change                                                                                                  |
| `ui.close`                                                                                                                                                                                                                             | A pane is about to close. `e.id` is the pane and `e.origin.kind` is `plugin`, `person`, or `unload`.                                                                                 |
| [`ui.message`](plugins-mods-interface.md#build-a-tree-from-elements)                                                                                                                                                             | A `Client` element posts data to its mod                                                                                                                                             |
| [`ui.fault`](plugins-mods-interface.md#redraw-when-something-changes)                                                                                                                                                            | A `Client` element your mod drew failed to load, draw, or run. `e.phase` is `load`, `render`, or `run`, and `e.reason` is the error message. Requires Claude Code v2.1.289 or later. |


[​](#other-mods)

Other mods

These events let a mod act on other mods as they load, to refuse one or change the mods API it receives:

| Event                                                                                    | Fires when                                                                                                                                                                                                              | A hook can return                                                                                                  |
|:-----------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|
| [`plugin.register`](plugins-mods-admin.md#enforce-a-policy-with-a-mod-of-your-own) | A hooks module is about to load. `e.uses` lists its events, mods API calls, environment variables, and state, as `claude plugin validate` prints them. Each call is written without the `$.` prefix, such as `fs.read`. | `{ refuse: reason }`                                                                                               |
| `engine.create`                                                                          | The mods API is being built for this mod                                                                                                                                                                                | A changed mods API, to add a namespace. A mod outside the `user` [tier](#the-hook-function) can also withhold one. |

When another mod’s hook calls a method on a namespace you added in `engine.create`, your method’s `$` calls run in that hook’s context until every hook on that event returns. For example, a relative path resolves against that hook’s working directory, and `$.prompt.submit` rejects while the turn is waiting on that hook. Calls your method makes after that run in your mod’s own context.


[​](#telemetry)

Telemetry

Telemetry events fire for the usage records Claude Code logs:

| Event                             | Fires when                                                                                                                                                                                                                                                                                                     | A hook can return                |
|:----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------|
| `telemetry.log`, `telemetry.mark` | A telemetry record is about to be logged, or one use of a feature is marked. In a mod you install, give a telemetry hook the filter `{ to: 'collector' }`, as in `on('telemetry.log', { to: 'collector' }, hook)`. Without the filter, the mod fails `claude plugin validate`. `*` doesn’t match these events. | `next(e)`, or `{ deny: reason }` |


[​](#settings-hook-events)

Settings hook events

Each [settings hook event](../07-Hooks/hooks.md#hook-events) is an event named `classic.<Event>`, such as `classic.Stop` or `classic.PostToolUse`. `e` is the hook’s stdin JSON.


[​](#mods-api-calls)

Mods API calls

Every [mods API method](#mods-api-methods) is also an event, named for its namespace and method, such as `fs.read`, `model.complete`, or `ui.open`. A hook on one intercepts calls from the mods that run after it, and can return `next(e)`, `{ deny: reason }`, or `{ value }`.


[​](#mods-api-methods)

Mods API methods

The mods API is the `$` argument every hook receives. Its methods are grouped in namespaces, such as `$.ui`. This table lists each namespace’s methods by name, so `open` in the `$.ui` row is the call `$.ui.open(...)`. The guides show the common ones in use, and [the types for your build](plugins-mods-create.md#get-the-types-for-your-build) document every method with an example.

| Namespace                                                                           | Methods                                                                                                                                                                                                                                                                                                              |
|:------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.plugin`                                                                          | `name`, `root`: this plugin’s name and directory                                                                                                                                                                                                                                                                     |
| [`$.ui`](plugins-mods-interface.md#pick-where-to-draw)                        | `resolve`, `invalidate`, `open`, `close`, `panes`, `focus`, `scroll`, `toast`, `notify`, `status`, `log`, `notice`, `ask`, `copy`, `selection`, `blit`                                                                                                                                                               |
| [`$.command`](plugins-mods-api.md#add-a-command)                              | `register`, `run`, `list`                                                                                                                                                                                                                                                                                            |
| [`$.tool`](plugins-mods-api.md#add-a-tool)                                    | `register`, `call`, `check`, `list`                                                                                                                                                                                                                                                                                  |
| `$.agent`                                                                           | `register`, `spawn`, `list`. `list()` returns this session’s subagents and teammates, each with a `status` of `pending`, `running`, `waiting`, `idle`, `completed`, `failed`, or `killed`, where `idle` and `waiting` require Claude Code v2.1.289 or later.                                                         |
| [`$.model`](plugins-mods-api.md#call-a-model)                                 | `complete`, `fork`, `classify`                                                                                                                                                                                                                                                                                       |
| [`$.prompt`](plugins-mods-api.md#start-a-turn-from-a-background-job)          | `submit`, `read`, `fill`, `suggest`, `compose`. Claude reads text from `submit({ text })` after a sentence that names your mod as the sender. `submit({ text, asUser: true })` sends the text as the user’s own words, without that sentence.                                                                        |
| `$.turn`                                                                            | `abort`                                                                                                                                                                                                                                                                                                              |
| [`$.session`](plugins-mods-api.md#send-and-receive-messages-between-sessions) | `messages`, `cwd`, `root`, `model`, `turns`, `id`, `repo`, `surfaces`, `usage`, `version`, `compact`, `send`, `append`, `authorize`. `usage()` returns `{ startedAt, context, rateLimits, cost }`: `context` has `tokens`, `window`, and `percent`, and `rateLimits` is a list of `{ kind, percentUsed, resetsAt }`. |
| `$.config`                                                                          | `list`, `set`                                                                                                                                                                                                                                                                                                        |
| [`$.settings`](plugins-mods-api.md#reach-files-processes-and-the-network)     | `read`                                                                                                                                                                                                                                                                                                               |
| [`$.env`](plugins-mods-api.md#reach-files-processes-and-the-network)          | `get`, `set`                                                                                                                                                                                                                                                                                                         |
| [`$.fs`](plugins-mods-api.md#reach-files-processes-and-the-network)           | `read`, `write`, `list`, `exists`, `stat`, `ancestors`. `write` isn’t atomic: it replaces the file’s content in place, so another process can read a partly written file. Keep data that several sessions change in `$.store`.                                                                                       |
| [`$.store`](plugins-mods-interface.md#keep-state)                             | `get`, `set`, `delete`, `keys`. A key-value store that every session on the machine shares. See [Save from more than one session](plugins-mods-interface.md#save-from-more-than-one-session).                                                                                                                  |
| [`$.state`](plugins-mods-interface.md#keep-a-value-in-$-state)                | Reactive state: `get`, `set`, with the helpers `atom`, `read`, `update`, `derive`, and `memberOf` imported from `claude-code`                                                                                                                                                                                        |
| [`$.clock`](plugins-mods-api.md#run-work-in-the-background)                   | `now`, `sleep`, `after`, `every`                                                                                                                                                                                                                                                                                     |
| [`$.http`](plugins-mods-api.md#reach-files-processes-and-the-network)         | `fetch`                                                                                                                                                                                                                                                                                                              |
| [`$.process`](plugins-mods-api.md#reach-files-processes-and-the-network)      | `run`, `spawn`                                                                                                                                                                                                                                                                                                       |
| [`$.mcp`](plugins-mods-api.md#reach-files-processes-and-the-network)          | `call`, `connect`. `connect(server)` connects an MCP server that your own plugin’s manifest lists.                                                                                                                                                                                                                   |
| `$.audio`                                                                           | `play`, `speak`                                                                                                                                                                                                                                                                                                      |
| `$.telemetry`                                                                       | `log`, `mark`. A record is sent only when Claude Code or a built-in mod makes the call.                                                                                                                                                                                                                              |


[​](#render-sites)

Render sites

A render site is an extension point in Claude Code’s interface. Each row is a value of `e.component` in a `ui.render` hook, with the fields of `e.props` and the apps that render it. `e.surface` names the app that’s drawing, such as `terminal`, `desktop`, or `vscode`. [Change what Claude Code already draws](plugins-mods-interface.md#change-what-claude-code-already-draws) shows what a hook can do at a site, with an example of each choice. Rendering in the VS Code extension requires extension version 2.1.299 or later. A session that [doesn’t fetch feature flags](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching) needs 2.1.304 or later. [Draw in the VS Code extension](plugins-mods-interface.md#draw-in-the-vs-code-extension) lists what the extension draws differently.

| Site                                                                                       | `e.props`                                                            | `e.requestId`    | Rendered on                |
|:-------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:-----------------|:---------------------------|
| [`Pane`](plugins-mods-interface.md#pick-where-to-draw)                               | `title`, `isFocused`, `bodyColumns`, `placement`, `scroll`, `view`   | The pane’s `id`  | Terminal, Desktop, VS Code |
| [`AbovePrompt`](plugins-mods-interface.md#pick-where-to-draw)                        | `hasSurvey`, `isWorking`, `maxRows`, `bodyColumns`, `scroll`, `view` | One instance     | Terminal, Desktop          |
| `UserMessage`                                                                              | `text`, `origin`, `isExpanded`, and `task` or `from` by origin       | The message id   | Terminal, Desktop, VS Code |
| `AssistantMessage`                                                                         | The reply’s text                                                     | The message id   | Terminal, Desktop, VS Code |
| `ToolUse`, `ToolResult`, `ToolGroup`                                                       | The tool’s name, input, and result                                   | The tool call id | Terminal, Desktop, VS Code |
| `CommandOutput`                                                                            | `command`, `text`                                                    | The message id   | Terminal, Desktop          |
| [`AskUserQuestion`](plugins-mods-interface.md#change-what-claude-code-already-draws) | The question and options                                             | The tool call id | Terminal, Desktop, VS Code |
| `ToolProgress`                                                                             | `kind`                                                               | The tool call id | Terminal                   |
| [`Spinner`](plugins-mods-interface.md#change-what-claude-code-already-draws)         | `word`, `message`, `suffix`, `mode`                                  | The agent id     | Terminal, Desktop, VS Code |
| `TurnDuration`                                                                             | `word`, `durationMs`                                                 | The message id   | Terminal                   |
| `InfoNotice`                                                                               | `text`, `command`                                                    | The message id   | Terminal                   |
| `SessionMode`                                                                              | `modes`                                                              | One instance     | Terminal, Desktop          |
| `PromptHint`                                                                               | `isDraft`, `isWorking`, `hint`                                       | One instance     | Terminal, Desktop          |

`e.viewport` holds `columns`, `rows`, and `isFullscreen`. In the terminal and the Desktop app, it’s absent until the app has measured its window. There, its `rows` is the height of the whole window, not of your pane. In the VS Code extension, it’s always present. To fit a tree to its site, read these props in the hook:

- **Width of a `Pane` or the band**: draw to `e.props.bodyColumns`
- **Height of a `Pane` beside the transcript**: where `e.props.placement` is `'dock'`, `e.props.scroll.bodyRows` is the number of rows the pane has for your tree
- **Height of a `Pane` above the prompt**: where `e.props.placement` is `'inline'`, the pane grows with your tree up to a limit, and `bodyRows` is that limit. To ask for a different limit, see the [`rows` field of `$.ui.open`](plugins-mods-interface.md#open-a-pane-at-the-right-time).

A tree taller than the pane scrolls as a whole.


[​](#elements)

Elements

Elements are the building blocks of a tree a `ui.render` hook returns, and you get them from `$.ui.resolve(e)`. [Build a tree from elements](plugins-mods-interface.md#build-a-tree-from-elements) shows the common ones with how the terminal draws them, and the [interface gallery](plugins-mods-gallery.md) has screenshots of most. A check mark means the app can draw the element.

| Element                                                                                  | Main props                                                                                                                                                                                                                                                                              | Terminal | Desktop | VS Code |
|:-----------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|:-------:|:-------:|
| [`Box`](plugins-mods-interface.md#build-a-tree-from-elements)                      | `key`, flex layout, [`gap`, `padding`, `margin`](#box-spacing-steps), `width`, `height`, `minWidth`, `minHeight`, [`borderStyle`](#box-border-styles), `borderColor`, `borderDimColor`, `backgroundColor`, `position`, `top`, `left`, `right`, `bottom`, `overflow`, `display`, `hover` |    ✓     |    ✓    |    ✓    |
| [`Text`](plugins-mods-interface.md#build-a-tree-from-elements)                     | `color`, `backgroundColor`, [`bold`](#text-bold), `italic`, `underline`, `strikethrough`, `dimColor`, `inverse`, `wrap`, [`size`, `weight`, `mono`, `tabular`](#text-typography), `hover`                                                                                               |    ✓     |    ✓    |    ✓    |
| [`Button`](plugins-mods-interface.md#respond-to-presses-and-typing)                | `key`, `label`, `onPress`, `hotkey`, `plain`, `dimColor`, `autoFocus`, `action`, `hover`                                                                                                                                                                                                |    ✓     |    ✓    |    ✓    |
| [`Link`](plugins-mods-interface.md#link-in-the-desktop-app)                        | `href`, `label`. See [Limits](#limits).                                                                                                                                                                                                                                                 |    ✓     |    ✓    |    ✓    |
| [`Code`](plugins-mods-gallery.md#show-code-and-changes)                            | `source`, `language`, `path`, `startLine`, `format`, `wrap`                                                                                                                                                                                                                             |    ✓     |    ✓    |    ✓    |
| `Markdown`                                                                               | `text`, `key`, `dimColor`, `onLinkPress`, `pressableLinks`                                                                                                                                                                                                                              |    ✓     |    ✓    |    ✓    |
| [`Input`](plugins-mods-interface.md#take-typed-input-and-draw-a-row-for-each-item) | `key`, `label`, `placeholder`, `value`, `submitLabel`, `onSubmit`, `onInput`, `autoFocus`                                                                                                                                                                                               |    ✓     |    ✓    |    ✓    |
| `Select`                                                                                 | `key`, `label`, `options`, `value`, `onSelect`, `autoFocus`                                                                                                                                                                                                                             |    ✓     |    ✓    |    ✓    |
| `Svg`                                                                                    | a `source` of SVG markup up to 131,072 characters, `alt` text, `width`, `height`, and `isInteractive`                                                                                                                                                                                   |          |    ✓    |    ✓    |
| [`Client`](plugins-mods-interface.md#build-a-tree-from-elements)                   | `module`, `key`, `props`, `width`, `height`, `flexGrow`                                                                                                                                                                                                                                 |    ✓     |    ✓    |         |
| [`Raster`](plugins-mods-interface.md#draw-a-grid-of-colored-cells)                 | `key`, `columns` up to 512, `rows` up to 256, `cells`. See [Draw a grid of colored cells](plugins-mods-interface.md#draw-a-grid-of-colored-cells).                                                                                                                                |    ✓     |         |         |
| `Image`                                                                                  | `key`, a `source` such as PNG or RGBA bytes up to 2 MiB, or a file path, `columns` and `rows` up to 255, and `alt` text. See [which terminals draw it](plugins-mods-gallery.md#image-and-client).                                                                                 |    ✓     |         |         |

More `Button` rules:

- **`action`**: names one of Claude Code’s own [keybinding actions](../02-Claude-Code-CLI/keybindings.md), and the user’s binding for it presses the button when that binding is a chord or a modified key
- **Digit hotkeys on the band**: a digit `hotkey` on a button in the band also fires when the user types that digit alone into an empty prompt and pauses
- **A collapsed band**: while the band is [collapsed](plugins-mods-interface.md#when-the-user-collapses-the-band), neither an `action` binding nor a hotkey presses a button in it
- **Two buttons on one hotkey**: when two buttons in one drawing name the same `hotkey`, the later one gets it

`autoFocus` accepts only `true` on any control, so omit the prop to leave it off. The VS Code extension ignores some element props and draws the element as if you had left them out:

- **`Box`**: `position` with its `top`, `left`, `right`, and `bottom`, `alignSelf`, `borderDimColor`, and `hover`
- **`Text`**: `size`, `mono`, `tabular`, and `hover`
- **`Button`**: `hotkey`, `plain`, `autoFocus`, `action`, and `hover`, so the user presses a button there with a click, or with Enter once it has focus
- **`Code`**: `language`, `path`, `startLine`, `format`, and `wrap`, so code draws without colors or line numbers, and a `format: 'diff'` block shows the diff’s raw text
- **`Input` and `Select`**: `autoFocus`
- **`Svg`**: `isInteractive`


[​](#box-border-styles)

`Box` border styles

To draw a border around a `Box`, set its `borderStyle` to one of these names, as in `borderStyle: 'round'`. Each row says what the terminal draws for that name and shows the top edge of the border.

| `borderStyle`    | What the terminal draws                                                 | Top edge |
|:-----------------|:------------------------------------------------------------------------|:---------|
| `'single'`       | Thin lines with square corners                                          | `┌──┐`   |
| `'double'`       | Double lines                                                            | `╔══╗`   |
| `'round'`        | Thin lines with rounded corners                                         | `╭──╮`   |
| `'bold'`         | Thick lines                                                             | `┏━━┓`   |
| `'singleDouble'` | Thin lines on the top and bottom, double lines on the sides             | `╓──╖`   |
| `'doubleSingle'` | Double lines on the top and bottom, thin lines on the sides             | `╒══╕`   |
| `'classic'`      | The ASCII characters `+`, `-`, and `|`                                  | `+--+`   |
| `'arrow'`        | Arrows that point into the `Box`                                        | `↘↓↓↙`   |
| `'dashed'`       | Dashed lines with blank corners                                         | `╌╌`     |
| `'quote'`        | A bar, `▎`, down the left side and blank cells on the other three sides | Blank    |

A `Box` whose `borderStyle` names anything else, such as `'rounded'`, draws with no border.


[​](#box-spacing-steps)

`Box` spacing steps

The `Box` spacing props are `gap`, `columnGap`, `rowGap`, `padding`, `margin`, and the one-side and one-axis forms of `padding` and `margin`, such as `paddingX` and `marginTop`. Each takes a number of terminal cells such as `gap: 1`, or a step name such as `gap: 'md'`. In the terminal and the VS Code extension, each step is this many cells:

| Step   | Cells across | Cells down |
|:-------|:-------------|:-----------|
| `'xs'` | 1            | 0          |
| `'sm'` | 1            | 0          |
| `'md'` | 2            | 1          |
| `'lg'` | 3            | 1          |
| `'xl'` | 4            | 2          |

If you set `gap`, `padding`, or `margin` to a step, the step applies across and down at once. For example, in the terminal and the VS Code extension, `padding: 'md'` is two cells at each side and one row above and below. Step names require Claude Code v2.1.299 or later. On an earlier release, Claude Code [draws its own version of the site](plugins-mods-interface.md#build-a-tree-from-elements) in place of a tree that uses a step name.


[​](#text-typography)

`Text` typography props

Set `size`, `weight`, `mono`, or `tabular` on a `Text` to ask the app drawing your tree for a text size, a weight, a monospaced typeface, or equal-width digits. Before you rely on a prop, check what the terminal and the VS Code extension do with it. If an app ignores a prop you set, your tree still draws there, with the text as if you’d left that prop out.

| Prop      | Values                                                                                                               | In the terminal                                          | In the VS Code extension                            |
|:----------|:---------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------|:----------------------------------------------------|
| `size`    | `'caption'`, `'footnote'`, `'body'`, `'heading'`, or `'title'`                                                       | Ignored: the terminal has one text size                  | Ignored                                             |
| `weight`  | `'regular'`, `'medium'`, `'semibold'`, or `'bold'`                                                                   | `semibold` and `bold` draw as bold text                  | `semibold` and `bold` draw as bold text             |
| `mono`    | `true` to ask for the app’s monospaced typeface                                                                      | Ignored: the terminal is always monospaced               | Ignored, so text keeps the extension’s own typeface |
| `tabular` | `true` to ask for digits of equal width, so that a number that updates in place or a column of numbers stays aligned | Ignored: the terminal’s digits are already one cell wide | Ignored                                             |

These four props require Claude Code v2.1.299 or later. On an earlier release, Claude Code [draws its own version of the site](plugins-mods-interface.md#build-a-tree-from-elements) in place of a tree that sets `size`, `weight`, `mono`, or `tabular`.


[​](#text-bold)

Replace `bold` with `weight`

`bold` on a `Text` is deprecated in favor of `weight` and still works. To replace it on a `Text` with no `weight`, change `bold: true` to `weight: 'semibold'` for the same result on Claude Code v2.1.299 or later. On a release earlier than v2.1.299, a tree that sets `weight` doesn’t draw, and Claude Code [draws its own version of the site](plugins-mods-interface.md#build-a-tree-from-elements) instead. If you set both `bold` and `weight` on a `Text`, `bold` has no effect.


[​](#limits)

Limits

Hooks and mods API calls run under time and size limits. Claude Code skips a hook that exceeds a time limit.

| Limit                                                                                                                    | Value                                                                                                                                                                                                                                                                                                                                  |
|:-------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| A hook’s own execution time for one event, not counting time inside `next` or a mods API call other than `$.clock.sleep` | 10 seconds, or 50 milliseconds for a `prompt.edit` hook                                                                                                                                                                                                                                                                                |
| A `.catch` handler’s execution time                                                                                      | 1 second                                                                                                                                                                                                                                                                                                                               |
| All `session.end` hooks together                                                                                         | As long as the [SessionEnd hook budget](../07-Hooks/hooks.md#sessionend-input), 1.5 seconds unless you change it, counted from when your settings `SessionEnd` hooks finish                                                                                                                                                                  |
| `$.process.run` timeout                                                                                                  | 30 seconds by default, 10 minutes at most                                                                                                                                                                                                                                                                                              |
| `$.model.complete` `maxTokens`                                                                                           | 1024 by default, up to 64,000 or the model’s output limit                                                                                                                                                                                                                                                                              |
| `$.fs.read` and `$.fs.write`                                                                                             | 4 MiB for one file                                                                                                                                                                                                                                                                                                                     |
| A `$.http.fetch` request body                                                                                            | 4 MiB, counted in characters. A call with a larger body rejects.                                                                                                                                                                                                                                                                       |
| A `$.http.fetch` response body                                                                                           | 4 MiB. `text` holds the first 4 MiB and the rest isn’t read. When the `Content-Length` header declares more, the call rejects instead, with a reason that ends `is over the 4194304-byte limit`, except when the last request after any redirects uses the `HEAD` method. The `HEAD` exemption requires Claude Code v2.1.296 or later. |
| One `$.http.fetch` call, redirects and body included                                                                     | 30 seconds                                                                                                                                                                                                                                                                                                                             |
| Redirects one `$.http.fetch` call follows                                                                                | 5                                                                                                                                                                                                                                                                                                                                      |
| A hook’s `drop` reason or `config.set` `deny` reason                                                                     | 4,096 characters. The end of a longer reason is cut, and the drop or deny still applies. The cut requires Claude Code v2.1.292 or later, and on earlier versions the hook [fails](plugins-mods-events.md#handle-a-hook-that-fails) instead.                                                                                      |
| Text in one tree                                                                                                         | The first 100,000 characters are drawn                                                                                                                                                                                                                                                                                                 |
| A `Code`’s `language` or `path`, a `Select` option’s `value`, or a `Client`’s `module`                                   | 10,000 characters. If one is longer, Claude Code [draws its own version of the site](plugins-mods-interface.md#build-a-tree-from-elements).                                                                                                                                                                                      |
| A `Link`’s `href`                                                                                                        | 2,048 characters. A longer `href` keeps the whole tree from drawing.                                                                                                                                                                                                                                                                   |
| `$.store`                                                                                                                | 4 MiB of JSON in total                                                                                                                                                                                                                                                                                                                 |
| `$.session.messages()`                                                                                                   | The newest 4,096 entries                                                                                                                                                                                                                                                                                                               |
| `$.ui.invalidate('ui.render')` redraws                                                                                   | Throttled to 10 a second, or 30 in the terminal for the visible pane, the expanded band, and the hint line under the prompt. Calls that come sooner are coalesced.                                                                                                                                                                     |
| `$.ui.toast`                                                                                                             | Shown for 4 seconds unless you pass `{ timeoutMs }`                                                                                                                                                                                                                                                                                    |
| A pane opened without the user asking                                                                                    | Placed from 144 terminal columns, 110 after they’ve opened it once                                                                                                                                                                                                                                                                     |
| Scopes, such as functions, blocks, and loops, nested inside one another in one file of a hooks module                    | 2,000                                                                                                                                                                                                                                                                                                                                  |
| Command, tool, subagent type, and pane names                                                                             | Letters, digits, `_`, and `-`, up to 64 characters                                                                                                                                                                                                                                                                                     |
| One `claude plugin test` test                                                                                            | 5 seconds unless the test sets `timeoutMs`                                                                                                                                                                                                                                                                                             |


[​](#settings-and-environment-variables)

Settings and environment variables

These are the settings and environment variables that affect mods. The Where column says which settings file or environment each one is read from:

| Name                              | Where                                                                                                                                          | What it does                                                                                                                                                                                                                                                      |
|:----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `CLAUDE_CODE_PLUGIN_DIRS`         | Environment, or `env` in `~/.claude/settings.json`                                                                                             | Plugin directories to load as `--plugin-dir` does, for apps you can’t pass a flag to. Absolute paths separated by `:`, or `;` on Windows.                                                                                                                         |
| `CLAUDE_CODE_PLUGIN_DIR_WATCH`    | Environment                                                                                                                                    | `1` makes a long-running non-interactive session reload `--plugin-dir` mods on save                                                                                                                                                                               |
| `prependPlugins`, `appendPlugins` | Managed settings. User settings only on a machine with no managed settings, in a session whose credential isn’t a Team or Enterprise `/login`. | Lists of plugin ids, such as `acme-guard@acme-tools`. Mods in `prependPlugins` run before every mod a user installs, and mods in `appendPlugins` run after, in the listed order. See [The order mods run in](plugins-mods-events.md#the-order-mods-run-in). |
| `allowManagedModsOnly`            | Managed settings, as an [option on the built-in guard](plugins-mods-admin.md#set-options-on-the-built-in-guard)                          | Only mods that [count as your organization’s](plugins-mods-admin.md#install-your-organizations-mods), and mods built into Claude Code, run their hooks. Users’ settings hooks keep running.                                                                 |
| `allowModsToOverrideDenyRules`    | Managed settings, as an [option on the built-in guard](plugins-mods-admin.md#set-options-on-the-built-in-guard)                          | Lets a mod a user installed approve a tool call that a `deny` rule refuses                                                                                                                                                                                        |
| `allowManagedHooksOnly`           | Managed settings                                                                                                                               | Blocks hooks and installed mods that aren’t your organization’s. See [what keeps running](../02-Claude-Code-CLI/settings-reference.md#what-runs-under-allowmanagedhooksonly).                                                                                                     |
| `disableAllHooks`                 | Any settings file                                                                                                                              | In managed settings, no mod or hook from an installed plugin runs. In your own settings, what your organization manages keeps running. See [`disableAllHooks`](../02-Claude-Code-CLI/settings-reference.md#disableallhooks).                                                      |
| `disableSideloadFlags`            | Managed settings                                                                                                                               | Rejects `--plugin-dir` and `--plugin-url` at startup                                                                                                                                                                                                              |
| `pluginConfigs`                   | User or managed settings                                                                                                                       | Holds `userConfig` values for a mod, keyed by the plugin’s id, such as `acme-guard@acme-tools`, or its name and `@inline`, such as `first-mod@inline`, for one loaded with `--plugin-dir`                                                                         |

`sec-default@builtin` is a guard built into Claude Code, listed as `cc-plugin-sec-default` in `/plugin` and the debug log. It loads ahead of every mod a person installs on a machine with managed settings, or in a session whose credential is a Team or Enterprise [`/login`](../02-Claude-Code-CLI/authentication.md#authentication-precedence). If managed `prependPlugins` is set, the guard loads only when that list names it, at the position listed. Its source is in the [`mods/sec-default` directory of the Claude Code repository](https://github.com/anthropics/claude-code/tree/main/mods/sec-default).


[​](#commands)

Commands

These commands and flags load, inspect, and test a mod. The `claude` commands run in your shell and the `/` commands at the Claude Code prompt. In the table, `<directory>` stands for a path you type, as in `claude plugin validate ./first-mod`. Square brackets mark an optional argument.

| Command                                                                                                         | What it does                                                                                                                                                                                               |
|:----------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`/plugin`](plugins-mods-overview.md#see-which-mods-a-session-loaded)                                     | Shows a line such as `1 mod active · first-mod` under its tabs when a mod that isn’t built in has loaded                                                                                                   |
| [`claude plugin validate <directory>`](plugins-mods-create.md#check-what-claude-code-reads-from-your-mod) | Reads a plugin’s manifest and hooks module and reports errors, the events it handles, and the mods API calls it makes. `--strict` treats warnings as errors and `--json` prints a machine-readable report. |
| [`claude plugin test [directory]`](plugins-mods-test.md#write-a-test)                                     | Runs every file under the directory, or the current directory when you give none, whose name ends in `.test.ts` or `.test.tsx`. Exits with status 1 when a test fails.                                     |
| [`claude --plugin-dir <directory>`](plugins-mods-create.md#write-a-mod-yourself)                          | Loads a plugin directory for one session and reloads its hooks module when you save. Repeat the flag to load several.                                                                                      |
| `/reload-plugins`                                                                                               | Reloads plugins when you run it                                                                                                                                                                            |

---

## Test a mod - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/test
Path: 08-Plugins-Skills/plugins-mods-test.md

## On this page

- [Write a test](#write-a-test)
  - [Stub what Claude Code would answer](#stub-what-claude-code-would-answer)
  - [Follow the test kit’s rules](#follow-the-test-kit%E2%80%99s-rules)
  - [Look up what a stub returns](#look-up-what-a-stub-returns)
- [Test a timer](#test-a-timer)
- [Test a drawing](#test-a-drawing)
  - [Test a drawing after /clear](#test-a-drawing-after-clear)
- [Test a policy mod](#test-a-mod-that-judges-other-mods)
- [Next steps](#next-steps)

Test and troubleshoot

# Test a mod

Copy pageCopy page

Write automated tests for a Claude Code mod that fire events, stub Claude Code’s answers, and press buttons, with no session, sign-in, or network.

Copy pageCopy page

You can write automated tests for a mod and run them from your shell with [`claude plugin test`](plugins-mods-reference.md#commands). A test fires the events your hooks handle and checks what the hooks did, so you catch a problem before it reaches a session. The first example tests the mod from [Create a mod](plugins-mods-create.md).


[​](#write-a-test)

Write a test

A test loads your mod, sends events through its hooks the way Claude Code would, and checks what the hooks did, without a session, a sign-in, or a network. You run tests from your shell with `claude plugin test`, and each test file imports the test kit, a test library in the `claude-code/testing` module. Give each test file a name that ends in `.test.ts`, such as `first-mod.test.ts`, and save it anywhere in the plugin directory. Every test file needs at least one `test()`, or the run fails with `declares no test(): nothing ran`. A test file can import your mod’s own files and sibling `.ts` helpers, so you can unit test plain functions, such as a game’s rules, without the kit. This test fires two tool calls, runs the `/tally` command from [Create a mod](plugins-mods-create.md), and checks that the reply counts both. Its first line is a [stub](#stub-what-claude-code-would-answer), which answers the tool calls in Claude Code’s place. Save it as `first-mod/tests/first-mod.test.ts`:

first-mod/tests/first-mod.test.ts

```python
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  // Answer each tool call in Claude Code's place, so no tool runs
  on('tool.call', () => ({ result: 'ok' }))

  // Fire two tool calls, which the mod's tool.call hook counts
  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })

  // Run /tally and check the text its hook returns
  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

In your shell, run the tests from the `first-mod` directory:

```python
claude plugin test
```

The output names each test and whether it passed, with timings that vary from run to run:

```python
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]

 1 pass
 0 fail
Ran 1 test across 1 file. [0.19s]
```

Each `$.tool.call` went through the mod’s [`tool.call`](plugins-mods-reference.md#tools) hook, which added one to its count and passed the call on to the stub. No `ls` ran and no file was read. `$.command.run` then went to the mod’s [`command.run`](plugins-mods-reference.md#commands-and-configuration) hook, and `answer` is the object that hook returned. The command exits with status 1 when a test fails, so it works in CI. If your own mods can’t load in the shell that runs it, it prints a line starting `claude plugin test: hooks modules are turned off` with the reason, and exits with status 1.


[​](#stub-what-claude-code-would-answer)

Stub what Claude Code would answer

No model, store, or tool runs in a test, so wherever your mod expects Claude Code to answer, the test supplies the answer with a stub. A test function receives two arguments for that:

- **`$`**: the test’s own `$`, which acts as Claude Code. It isn’t the [mods API](plugins-mods-reference.md#mods-api-methods) that a hook receives. Each of its methods fires the event of the same name, sends it through your mod’s hooks, and resolves to the result: `$.tool.call({ tool: 'Bash', command: 'ls' })` fires `tool.call`. `$.command.run`, `$.prompt.submit`, `$.session.start`, and `$.turn.complete` work the same way, and `$.classic.Stop` and the other `$.classic` methods fire a [settings hook event](plugins-mods-events.md#hook-the-settings-hook-events). A test can’t fire a mods API call such as `ui.close` directly. Trigger it through your mod, for example by pressing the button that closes the pane.
- **`on`**: call it to register stubs, which are hooks that answer in Claude Code’s place. Name a stub for a mods API call without the `$.`, so a stub registered as `store.get` answers your mod’s `$.store.get`. When your mod calls [`$.model.complete`](plugins-mods-api.md#call-a-model) or [`$.store.get`](plugins-mods-interface.md#keep-state), a stub supplies the answer.

This example stubs a model call. The hook belongs to a mod named `grader`, and handles a `/grade` command that sends a sentence to a model and reports whether the reply starts with `PASS`. The file holds only the hook under test, so the mod also needs a `plugin.json` and a `hooks.json`, as in [Create a mod](plugins-mods-create.md#write-a-mod-yourself). To type `/grade` in a session, the mod also has to [register the command](plugins-mods-api.md#add-a-command):

grader/hooks/register.js

```python
export function register(on) {
  on('command.run', { command: 'grade' }, async ($, e) => {
    // e.args is the text typed after /grade
    const reply = await $.model.complete({
      model: 'haiku',
      system: 'Grade the sentence. Start your reply with PASS or FAIL.',
      prompt: e.args,
    })
    const passed = reply.isAnswered && reply.text.startsWith('PASS')
    return { text: passed ? 'Passed' : 'Try again' }
  })
}
```

This test stubs the model call to check what the hook does with a passing reply:

grader/tests/grader.test.ts

```python
import { expect, test } from 'claude-code/testing'

test('a passing grade is reported', async ($, on) => {
  // Answer the mod's $.model.complete call with a fixed reply, so no model runs
  on('model.complete', () => ({
    value: {
      isAnswered: true,
      text: 'PASS\nNice sentence.',
      usage: { input_tokens: 10, output_tokens: 5, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 },
    },
  }))

  // Run /grade, which makes the mod call the model
  const answer = await $.command.run({ command: 'grade', args: 'The cat sat on the mat.' })
  expect(answer.text).toBe('Passed')
})
```

The test passes because the hook’s `reply` is the object under `value`, whose `text` starts with `PASS`. To check the other branch, add a second test whose stub returns a `text` that starts with `FAIL`, and expect `Try again`. A stub for a mods API call returns an object with a `value` field, which holds what the call resolves to in your mod: `{ value: 7 }` makes `$.store.get` resolve to `7`. A stub for one of Claude Code’s events, such as [`turn.step`](plugins-mods-reference.md#turns) or `tool.call`, returns that event’s own result, such as `{ result: 'ok' }`. `$.session.send` and `$.prompt.fill` take their event’s result too, as the table shows. [Look up what a stub returns](#look-up-what-a-stub-returns) shows which form each common name takes. These errors mean a stub is wrong or missing. A failed test’s output includes a block headed `the engine reported:`, and each error appears there:

- `returned neither { value } nor { deny }`: a stub for a mods API call returned a bare value, which fails the test
- `no implementation for` followed by a name: your mod made that call and no stub answers it

The kit also exports ready-made mocks for the clock, the store, environment variables, and rows appended to the conversation:

- **`mock.clock(on)`**: answers [`$.clock`](plugins-mods-api.md#run-work-in-the-background) and returns a mock clock that your test advances, so a test of a timer doesn’t wait.
- **`mock.store(on, { count: 7 })`**: answers `$.store` from a store that starts with those entries. It returns nothing, so to check what your mod saved, write the two `store` stubs yourself as the [drawing test](#test-a-drawing) does.
- **`mock.env(on, { CI: 'true' })`**: answers `$.env.get` from those variables.
- **`mock.session(on)`**: returns a mock session whose `appended()` method lists the rows your mod added with [`$.session.append`](plugins-mods-reference.md#session), oldest first; it requires Claude Code v2.1.293 or later.


[​](#follow-the-test-kit’s-rules)

Follow the test kit’s rules

The test kit has a few rules of its own, and breaking one produces the errors new test authors hit first:

- **Register every stub before the test’s first call on `$`.** Calling `on` after that throws an error such as `on("ui.render") after the test first called $`.
- **[`session.start`](plugins-mods-reference.md#session) doesn’t run by itself.** Each test starts with your module freshly loaded and none of its hooks called, so module-level variables hold their initial values. If a hook depends on what `session.start` sets up, fire it first:

  ``` shiki
  // Answer the event after your hook passes it on with next(e)
  on('session.start', () => ({ cwd: '/work' }))
  // Answer the $.command.register call your hook makes
  on('command.register', () => ({ value: undefined }))
  // Fire the event, which runs your session.start hook
  await $.session.start({ surface: 'terminal', isInteractive: true, cwd: '/work' })
  ```

  The second stub answers the `$.command.register` call that a `session.start` hook such as the [tutorial’s](plugins-mods-create.md#write-a-mod-yourself) makes. Without it, that call rejects with `no implementation for command.register` and the kit skips your hook, so nothing after the call in the hook runs. The test doesn’t fail at that point. The skipped hook is listed under `the engine reported:` only if a later check fails.
- **A hook that returns `next(e)` needs a stub to answer.** When your [`ui.render`](plugins-mods-reference.md#interface) hook returns `next(e)`, for example to draw nothing while Claude is idle, [mounting it](#test-a-drawing) fails with `no implementation for ui.render`. Register a stub that returns an element as plain data:

  ``` shiki
  // Stands for what Claude Code would draw at the site
  on('ui.render', () => ({ type: 'Text', props: {}, children: ['drawn by Claude Code'] }))
  ```

  With the stub registered, the mount succeeds, and `ui.find({ type: 'Text' })` returns that element whenever your hook returned `next(e)`.
- **A stub for `turn.step` is an async generator**, and the test reads the stream to its end to get the result:

  ``` shiki
  on('turn.step', async function* ($, e) {
    // Each yield is one piece of the model's streamed reply
    yield { kind: 'text', index: 0, text: 'ok' }
    // The return value is the result of the whole request
    return { turnId: e.turnId, index: e.index, answer: 'ok', toolUses: [], stopReason: 'end_turn', usage: null }
  })

  // Fire one request to the model, which runs your turn.step hook
  const stream = $.turn.step({ turnId: 't', index: 0, model: 'claude-test', messageCount: 1 })
  // Read every piece until the stream says it's done
  let step = await stream.next()
  while (step.done !== true) step = await stream.next()
  const result = step.value
  ```

  When the loop ends, `result` is the object the stub returned, after your `turn.step` hook has had the chance to change it. Here `result.answer` is `'ok'`.
- **Fire a tool call with the tool’s name and arguments as fields**, such as `await $.tool.call({ tool: 'Bash', command: 'ls' })`, and register a `tool.call` stub that returns `{ result }`.


[​](#look-up-what-a-stub-returns)

Look up what a stub returns

Every mods API call your mod makes in a test needs a stub that answers in Claude Code’s place, except the few the kit answers itself: [`$.ui.invalidate`](plugins-mods-interface.md#redraw-when-something-changes), [`$.state`](plugins-mods-interface.md#keep-state), and `$.session.append` calls. For `$.clock` calls, use `mock.clock(on)`, or your mod’s `$.clock.now()` fails with `no implementation for clock.now`. This table lists the ones mods use most. The first column is the call your mod makes or the event it passes on with `next(e)`. The second is the function to pass to `on` under that name, so the `$.store.get` row becomes `on('store.get', ($, e) => ({ value: saved.get(e.key) }))`. A `'...'` in a stub marks text for you to fill in:

| Your mod calls or passes on                                                                                   | Stub                                                                                                                                                                                                                                      |
|:--------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$.command.register`, `$.tool.register`, `$.ui.toast`, `$.ui.log`, `$.ui.status`, `$.ui.close`, `$.store.set` | `() => ({ value: undefined })`. For `ui.toast` and `ui.log`, the text is `e.text`.                                                                                                                                                        |
| `$.store.get`                                                                                                 | `($, e) => ({ value: saved.get(e.key) })`                                                                                                                                                                                                 |
| `$.fs.read`                                                                                                   | `($, e) => ({ value: e.path.endsWith('notes.md') ? '# Notes' : '' })`. `e.path` arrives as an absolute path, so compare with `endsWith`.                                                                                                  |
| `$.ui.open`                                                                                                   | `() => ({ value: { isPlaced: true } })`                                                                                                                                                                                                   |
| `$.ui.ask`                                                                                                    | A `tool.call` stub, because the question reaches it as a call to the `AskUserQuestion` tool: `($, e) => ({ result: { answers: { [e.questions[0].question]: 'Run it' } } })`. Check `e.tool` first if your mod passes on other tool calls. |
| `$.model.complete`                                                                                            | `() => ({ value: { isAnswered: true, text: '...', usage } })`                                                                                                                                                                             |
| `$.process.run`                                                                                               | `($, e) => ({ value: { exitCode: 0, stdout: '...', stderr: '' } })`. `e.argv` is the argument list and `e.init` holds `cwd` and `timeoutMs`.                                                                                              |
| Any mods API call that should fail                                                                            | `() => ({ deny: 'the reason' })`, which makes the call reject in your mod. A stub that throws is skipped instead.                                                                                                                         |
| `session.start`                                                                                               | `() => ({ cwd: '/work' })`                                                                                                                                                                                                                |
| `turn.start`                                                                                                  | `($, e) => ({ turnId: e.turnId })`                                                                                                                                                                                                        |
| `tool.call`                                                                                                   | `() => ({ result: '...' })`                                                                                                                                                                                                               |
| `turn.complete`                                                                                               | `() => ({ text: '' })`. Fire it with `$.turn.complete({ turnId, answer, durationMs, isAborted: false, usage: null })`.                                                                                                                    |
| `prompt.submit`                                                                                               | `($, e) => ({ text: e.text })`                                                                                                                                                                                                            |
| `prompt.fill`                                                                                                 | `() => ({ isFilled: true })`                                                                                                                                                                                                              |
| `$.prompt.read`                                                                                               | `() => ({ value: { text: '...', cursor: 0 } })`                                                                                                                                                                                           |
| `$.ui.copy`                                                                                                   | `() => ({ value: { isCopied: true } })`                                                                                                                                                                                                   |
| `$.ui.notify`                                                                                                 | `() => ({ value: { isSent: true, channel: 'terminal_bell' } })`. The text is `e.text` and the title is `e.title`.                                                                                                                         |
| `$.session.messages`                                                                                          | `() => ({ value: [{ role: 'assistant', text: '...', toolUses: [] }] })`                                                                                                                                                                   |
| `$.session.id`, `$.agent.list`                                                                                | `() => ({ value: 'abc123' })`, `() => ({ value: [] })`                                                                                                                                                                                    |
| `session.send`                                                                                                | `() => ({ isDelivered: true })`. `e.to` arrives as a string even when your mod passed `{ sessionId }`.                                                                                                                                    |
| `session.receive`                                                                                             | `($, e) => ({ text: e.text })`. Fire it with `$.session.receive({ origin: { kind: 'peer-send-message' }, text })`.                                                                                                                        |
| `ui.render`                                                                                                   | `() => ({ type: 'Text', props: {}, children: ['...'] })`                                                                                                                                                                                  |

`expect` has the assertions `toBe`, `toEqual`, `toMatch`, `toMatchObject`, `toContain`, `toBeDefined`, `toBeUndefined`, and `toThrow`, and `.not` before any of them. When an `expect` fails inside a stub or hook that you passed to `on` as a plain function rather than an async generator, the test fails. The engine skips that hook, and the failure output names it, such as `in the test's store.set hook`.


[​](#test-a-timer)

Test a timer

A mod that runs work on a timer needs a clock the test controls, so the test can move time forward instead of waiting. `const clock = mock.clock(on)` returns a mock clock that starts at `0` and moves only when your test moves it. To start at another time, pass it in milliseconds, as in `mock.clock(on, { now: 5000 })`. The clock has these methods:

| Method                      | What it does                                                                                                                      |
|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------|
| `await clock.advance(1000)` | Moves the time forward by that many milliseconds and runs each timer that comes due                                               |
| `await clock.set(5000)`     | Moves the time forward to that value, as `advance` would                                                                          |
| `clock.now()`               | Returns the time, which is what your mod’s `$.clock.now()` resolves to                                                            |
| `await clock.settle()`      | Runs timers that are already due, such as a chain of zero-delay `$.clock.after` calls, without moving the time                    |
| `await clock.sleep(2000)`   | Inside a stub, makes that stub answer only once the test has advanced that far, which is how you simulate a slow model or process |

This hook belongs to a mod named `countdown`, and handles a `/countdown` command that takes a number of seconds, starts a one-second `$.clock.every` timer, and shows a toast at zero. As with `grader`, the file holds only the hook under test and doesn’t register the command:

countdown/hooks/register.js

```python
export function register(on) {
  on('command.run', { command: 'countdown' }, async ($, e) => {
    // e.args is the text typed after /countdown
    let left = Number(e.args)
    const timer = $.clock.every(1000, () => {
      left -= 1
      if (left === 0) {
        timer.cancel()
        $.ui.toast('Time is up')
      }
    })
    // Print nothing in the transcript
    return {}
  })
}
```

This test runs `/countdown 3` and moves the mock clock, so it checks three seconds of behavior without waiting three seconds:

countdown/tests/countdown.test.ts

```python
import { expect, mock, test } from 'claude-code/testing'

test('the countdown ends with a toast', async ($, on) => {
  // Answer every $.clock call from a clock the test controls
  const clock = mock.clock(on)
  // Collect the text of each toast the mod shows
  const toasts: string[] = []
  on('ui.toast', ($, e) => {
    toasts.push(e.text)
    return { value: undefined }
  })

  await $.command.run({ command: 'countdown', args: '3' })
  // After two seconds the timer has fired twice, and no toast is due
  await clock.advance(2000)
  expect(toasts).toEqual([])
  // The third second brings the count to zero
  await clock.advance(1000)
  expect(toasts).toEqual(['Time is up'])
})
```

The first `expect` shows that the toast doesn’t come early, and the second shows that it comes once. Each `advance` resolves after the timers that came due have run, so the check on the next line sees their effect.


[​](#test-a-drawing)

Test a drawing

A test can draw one of your mod’s [render sites](plugins-mods-reference.md#render-sites), then press, type into, and find the elements it drew. `$.ui.mount` draws the site through your mod’s `ui.render` hook and returns a handle with a method for each of those. To cover several apps in one test, set `surface` to the app to draw for. This test opens the pane from [Build a pane with tabs](plugins-mods-interface.md#build-a-pane-with-tabs), switches tabs, presses the button, and checks the count in the terminal and the Desktop app:

hello-tabs/tests/hello-tabs.test.ts

```python
import { expect, test } from 'claude-code/testing'

// What Claude Code passes to a ui.render hook for this pane, apart from the app
const PANE = {
  plugin: 'hello-tabs',
  component: 'Pane',
  requestId: 'hello-tabs',
  viewport: { columns: 100, rows: 30 },
  props: {
    title: 'Hello tabs',
    isFocused: true,
    bodyColumns: 60,
    placement: 'inline',
    scroll: { offset: 0, bodyRows: 10 },
    view: {},
  },
} as const

test('the second tab counts presses and saves the count', async ($, on) => {
  // Stub $.store with a Map, so the test can read what the mod saved
  const saved = new Map<string, unknown>()
  on('store.get', ($, e) => ({ value: saved.get(e.key) }))
  on('store.set', ($, e) => {
    saved.set(e.key, e.value)
    return { value: undefined }
  })

  // Draw the pane once for each app
  for (const surface of ['terminal', 'desktop'] as const) {
    const ui = await $.ui.mount({ ...PANE, surface })
    // Press the buttons by the key the mod gave them
    await ui.press({ key: 'tab-two' })
    await ui.press({ key: 'more' })
    // The second tab's count line is in the drawing
    expect(await ui.find({ type: 'Text', text: /^Count: \d+$/ })).toBeDefined()
    await ui.unmount()
  }

  // One press in each app makes two
  expect(saved.get('count')).toBe(2)
})
```

In your shell, run `claude plugin test` from the `hello-tabs` directory. The test passes when both apps draw the count line and the mod has saved `2`. The count carries over from the first app to the second because both mounts use the same loaded module. The handle that `$.ui.mount` returns has these methods, which address elements by the `key` you gave them:

| Method                                                                | What it does                                                                                                                       |
|:----------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------|
| `press({ key: 'more' })`                                              | Presses the `Button` with that key                                                                                                 |
| `input({ key: 'new-note', text: 'buy milk' })`                        | Types the text into the `Input` with that key and presses Enter. Add `kind: 'change'` to type without submitting.                  |
| `select({ key: 'size', value: 'large' })`                             | Picks the option with that value in the `Select` with that key                                                                     |
| `find({ key: 'more' })` or `find({ type: 'Text', text: 'Count: 2' })` | Returns the first matching element as `{ type, props, children }`, or `undefined`. `text` can be a string or a regular expression. |
| `unmount()`                                                           | Removes the drawing                                                                                                                |

Each method resolves after your handler has finished, so you can check the result on the next line. Set `props` to what Claude Code would pass for that site. The [render sites table](plugins-mods-reference.md#render-sites) lists each site’s props, and [the types for your build](plugins-mods-create.md#get-the-types-for-your-build) have their types. A drawing test checks the tree your hook returns and whether it’s valid for that app. It doesn’t check how the app paints it, so look at a new layout in a real session as well.


[​](#test-a-drawing-after-clear)

Test a drawing after `/clear`

Each test starts with every `$.state` value at its default, which is how `/clear` leaves them. To test what your mod does next, skip `session.start`, fire `classic.SessionStart` with `source: 'clear'`, and check what your mod draws. This test checks the module from [Load a saved value again after `/clear`](plugins-mods-interface.md#load-a-saved-value-again-after-clear). Add it to the file from [Test a drawing](#test-a-drawing), where `PANE` is defined. That file’s first test expects the button to save the count, as the button in [Save from more than one session](plugins-mods-interface.md#save-from-more-than-one-session) does:

hello-tabs/tests/hello-tabs.test.ts

```python
test('the saved count comes back after /clear', async ($, on) => {
  // The store already holds a count of 7
  on('store.get', () => ({ value: 7 }))
  // Answer the event after your hook passes it on with next(e)
  on('classic.SessionStart', () => ({}))

  // Fire the event that follows /clear, which runs your hook
  await $.classic.SessionStart({ source: 'clear' })

  const ui = await $.ui.mount({ ...PANE, surface: 'terminal' })
  await ui.press({ key: 'tab-two' })
  // The pane shows the stored count, not the default of 0
  expect(await ui.find({ type: 'Text', text: 'Count: 7' })).toBeDefined()
})
```

The test passes when your `classic.SessionStart` hook has copied the stored `7` into `$.state` before the pane draws. Without that hook in your module, the pane draws `Count: 0`, `find` returns `undefined`, and the test fails at `toBeDefined`.


[​](#test-a-mod-that-judges-other-mods)

Test a policy mod

A mod your organization lists in [`prependPlugins`](plugins-mods-admin.md) can refuse another mod before it loads. To test one, set your mod’s tier and give the test a second mod for yours to allow or refuse:

- **`tier`**: call it once at the top of the test file, as in `tier('prepend')`, to load your mod as `prepend`, `append`, or `builtin`, its place in the [order mods run in](plugins-mods-events.md#the-order-mods-run-in). Without it, your mod loads as `user`.
- **`plugins`**: pass `test` an options object ahead of the test body. Its `plugins` array holds mods you write inline, each with a `name` and a `register` function. To load one somewhere other than `user`, add `tier` to it.

This test file loads the [policy mod from the admin page](plugins-mods-admin.md#enforce-a-policy-with-a-mod-of-your-own) first. It checks that the policy mod refuses a mod that starts a process and allows one that doesn’t:

acme-guard/tests/guard.test.ts

```python
import { expect, test, tier } from 'claude-code/testing'

// Load the mod under test ahead of every other mod
tier('prepend')

// A second mod whose code calls $.process.run, which the policy blocks
const runner = {
  name: 'runner',
  register(on) {
    on('tool.call', async ($, e, next) => {
      await $.process.run(['ls'])
      return { result: 'runner answered' }
    })
  },
}

// A second mod that calls nothing the policy blocks
const reader = {
  name: 'reader',
  register(on) {
    on('tool.call', async ($, e, next) => {
      return { result: 'reader answered' }
    })
  },
}

test('refuses a mod that starts a process', { plugins: [runner] }, async ($, on) => {
  on('tool.call', () => ({ result: 'claude code answered' }))
  let message = ''
  try {
    // The first call on $ loads the mods, so the refusal is thrown here
    await $.tool.call({ tool: 'Bash', command: 'ls' })
  } catch (error) {
    message = error.message
  }
  expect(message).toBe('runner: refused by acme-guard: Acme policy: mods may not call process.run')
})

test('admits a mod that starts no process', { plugins: [reader] }, async ($, on) => {
  on('tool.call', () => ({ result: 'claude code answered' }))
  const out = await $.tool.call({ tool: 'Bash', command: 'ls' })
  // The answer comes from reader, which shows that it loaded
  expect(out).toEqual({ result: 'reader answered' })
})
```

In your shell, run `claude plugin test` from the `acme-guard` directory. Both tests pass with the policy mod as the admin page shows it. The kit loads every mod at the test’s first call on `$`. When your mod refuses one, that call throws, and the message names the refused mod, the mod that refused it, and your reason. In the second test nothing is refused, so `reader` answers the tool call before it reaches the stub.


[​](#next-steps)

Next steps

- [Troubleshoot a mod](plugins-mods-troubleshoot.md): find out why a mod does nothing in a session
- [Mods reference](plugins-mods-reference.md): every event’s input and result, for writing stubs

---

## Troubleshoot a mod - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/mods/troubleshoot
Path: 08-Plugins-Skills/plugins-mods-troubleshoot.md

## On this page

- [Find out why a mod does nothing](#find-out-why-a-mod-does-nothing)
- [Check whether mods can load](#check-whether-mods-can-load)
- [The mod doesn’t load](#the-mod-doesn%E2%80%99t-load)
  - [Your version is too old](#your-version-is-too-old)
  - [The mods active line doesn’t name the mod](#the-mods-active-line-doesn%E2%80%99t-name-the-mod)
  - [A claude -p run prints hooks module not loaded](#a-claude-p-run-prints-hooks-module-not-loaded)
  - [Refusal messages](#refusal-messages)
  - [Messages from the built-in guard](#messages-from-the-built-in-guard)
  - [validate passes and lists no hooks line](#validate-passes-and-lists-no-hooks-line)
  - [hooks module did not load](#hooks-module-did-not-load)
  - [options do not fit plugin.json userConfig](#options-do-not-fit-plugin-json-userconfig)
  - [code nested too deep to scan: more than 2000 scopes](#code-nested-too-deep-to-scan-more-than-2000-scopes)
  - [the engine ran out of stack linking the file and its imports](#the-engine-ran-out-of-stack-linking-the-file-and-its-imports)
  - [the parser stopped or the engine ran out of stack loading the file](#the-parser-stopped-or-the-engine-ran-out-of-stack-loading-the-file)
  - [No mod loads in a directory you opened for the first time](#no-mod-loads-in-a-directory-you-opened-for-the-first-time)
  - [No installed plugin loads at all](#no-installed-plugin-loads-at-all)
  - [Claude Code stops asking to enable hot reloading](#claude-code-stops-asking-to-enable-hot-reloading)
- [A hook is skipped or a mod is unloaded](#a-hook-is-skipped-or-a-mod-is-unloaded)
  - [hook skipped](#hook-skipped)
  - [no command.run hook answered it](#no-command-run-hook-answered-it)
  - [it crashed the hooks worker](#it-crashed-the-hooks-worker)
  - [its session.start ran again in a fresh copy](#its-session-start-ran-again-in-a-fresh-copy)
  - [\$.agent.register refused: the hooks module that made the call is no longer loaded](#%24-agent-register-refused-the-hooks-module-that-made-the-call-is-no-longer-loaded)
  - [mods that run in the hooks worker are off for this session](#mods-that-run-in-the-hooks-worker-are-off-for-this-session)
- [A tool call is denied](#a-tool-call-is-denied)
  - [a hook changed this call's input after the model wrote it](#a-hook-changed-this-calls-input-after-the-model-wrote-it)
  - [A message about the deny rules in your settings](#a-message-about-the-deny-rules-in-your-settings)
- [A toast appears after a turn is stopped](#a-toast-appears-after-a-turn-is-stopped)
  - [Stopped: type a message to continue; a mod cannot resume work after Stop](#stopped-type-a-message-to-continue)
- [A drawing doesn’t appear or respond](#a-drawing-doesn%E2%80%99t-appear-or-respond)
  - [Another mod’s band shows and yours doesn’t](#another-mods-band-shows-and-yours-doesnt)
  - [A pane or band is empty or shows Claude Code’s usual content](#a-pane-or-band-is-empty-or-shows-claude-code%E2%80%99s-usual-content)
  - [A ui.render line says threw while drawn](#a-ui-render-line-says-threw-while-drawn)
  - [the module failed without a message](#the-module-failed-without-a-message)
  - [\$.ui.open runs and no pane appears](#%24-ui-open-runs-and-no-pane-appears)
  - [A toast doesn’t appear](#a-toast-doesn%E2%80%99t-appear)
  - [The band shows plugin panel hidden](#plugin-panel-hidden)
  - [Hotkeys do nothing](#hotkeys-do-nothing)
  - [A drawing works in the terminal and not in the Desktop app or VS Code](#a-drawing-works-in-the-terminal-and-not-in-the-desktop-app-or-vs-code)
- [An edit or a value is lost](#an-edit-or-a-value-is-lost)
  - [Your edits don’t take effect](#your-edits-don%E2%80%99t-take-effect)
  - [A value resets when the module reloads](#a-value-resets-when-the-module-reloads)
  - [A value resets after /clear, /resume, or /branch](#a-value-resets-after-%2Fclear-%2Fresume-or-%2Fbranch)
- [Read the debug log](#read-the-debug-log)
- [Next steps](#next-steps)

Test and troubleshoot

# Troubleshoot a mod

Copy pageCopy page

Find out why a Claude Code mod does nothing: match the symptom or message to its cause, look up refusal messages, and read the debug log.

Copy pageCopy page

When a mod’s module or one of its hooks fails, Claude Code skips it and the session continues, so a broken mod can look like one that does nothing. Start by checking what Claude Code read from your mod and where it reports a problem, then find the symptom or message you have.


[​](#find-out-why-a-mod-does-nothing)

Find out why a mod does nothing

When a mod does nothing, check what Claude Code reads from the mod’s files, and the line it writes when it skips something. For the first, in your shell run [`claude plugin validate`](plugins-mods-create.md#check-what-claude-code-reads-from-your-mod) with the mod’s directory, as in `claude plugin validate ./first-mod`. It catches a misspelled event, a bad manifest, and a module Claude Code can’t read, without starting a session. When a module doesn’t load, a hook is skipped, or another mod refuses yours, Claude Code writes one line that names your mod. Where you read that line depends on the session:

- **A session that hot-reloads a plugin directory**: a dim line in the transcript. That’s an interactive session you started with `--plugin-dir`, or one where you [enabled hot reloading](plugins-mods-create.md#ask-claude-for-a-mod) for mods Claude wrote.
- **Any other interactive session, such as one that runs a mod you installed from a marketplace**: the [debug log](#read-the-debug-log) only. To get one, start the session with `claude --debug`.
- **A `claude -p` run with `--plugin-dir`**: stderr, in the default text output format. A refusal by another mod goes to the debug log only.


[​](#check-whether-mods-can-load)

Check whether mods can load

To check whether your setup lets mods load at all, without installing one, run `claude plugin test` in your shell, from a directory that doesn’t hold a mod. You don’t need a session. The message it prints tells you the state:

| Message includes                                                                                       | What it means                                                                                                                               |
|:-------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------|
| `no hooks module to load`                                                                              | Mods can load. The command found no mod to test in this directory.                                                                          |
| `hooks modules are turned off here`                                                                    | A setting is blocking your mods: `disableAllHooks` in your own settings, or your organization’s policy                                      |
| `hooks modules are turned off in this process: the rollout switch served off`                          | Anthropic has turned installed mods off remotely.                                                                                           |
| `hooks modules are turned off in this process: the rollout switch was saved off by an earlier session` | The command used a value an earlier session saved, which may be out of date. Start `claude` once to refresh it, then run the command again. |

An organization can also set `allowManagedModsOnly` to allow only its own mods, which this command doesn’t report. In that case Claude Code refuses a mod you install, and [a message says why](plugins-mods-troubleshoot.md#messages-from-the-built-in-guard).


[​](#the-mod-doesn’t-load)

The mod doesn’t load

Nothing the mod adds appears: no command, no drawing, and no change in behavior.


[​](#your-version-is-too-old)

Your version is too old

See [which version to use and how to check yours](plugins-mods-overview.md#turn-mods-on-or-off).


[​](#the-mods-active-line-doesn’t-name-the-mod)

The `mods active` line doesn’t name the mod

Nothing the mod adds appears, and the [`mods active` line](plugins-mods-overview.md#see-which-mods-a-session-loaded) in `/plugin` doesn’t name it. The hooks module didn’t load. When Claude Code refused it, the debug log has a line that starts with `hooks module`, the mod’s name, and `not loaded:`, as in `hooks module first-mod@inline not loaded: disableAllHooks in managed settings` for a mod loaded with `--plugin-dir`. Read the reason after the colon. The [refusal messages](#refusal-messages) section lists each one. If the log has no such line, work through the other entries in this group. Some settings stop a mod and leave the rest of its plugin working. [Turn mods on or off](plugins-mods-overview.md#turn-mods-on-or-off) names them.


[​](#a-claude-p-run-prints-hooks-module-not-loaded)

A `claude -p` run prints `hooks module not loaded`

The line starts with the mod’s name and goes to stderr. The hooks module was refused. A non-interactive run has no transcript, so the message goes to stderr. Read the reason after the colon. The [refusal messages](#refusal-messages) section lists each one.


[​](#refusal-messages)

Refusal messages

Each of these follows `hooks module`, the mod’s name, and `not loaded:` in the debug log.

| Message starts with                                                                                                          | What it means                                                                                                                                                                      |
|:-----------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `hooks modules are turned off for installed plugins in this process: the rollout switch served off`                          | Anthropic has turned installed mods off remotely.                                                                                                                                  |
| `hooks modules are turned off for installed plugins in this process: the rollout switch was saved off by an earlier session` | The session used a value an earlier session saved, which may be out of date. Start Claude Code again to refresh it.                                                                |
| `disableAllHooks in managed settings`                                                                                        | Your organization turned off hooks from installed plugins                                                                                                                          |
| `only managed plugins and built-in plugins run`                                                                              | `allowManagedHooksOnly` is set, or `disableAllHooks` is set in a settings file other than managed settings                                                                         |
| `installed plugins that are not managed load no hooks module in this mode (--bare)`                                          | You started Claude Code with `--bare`                                                                                                                                              |
| `another plugin of that name loads first`                                                                                    | Another enabled plugin has the same name as your mod and [holds the name](plugins-loading.md#hooks-when-two-enabled-plugins-share-a-name), so your hooks module doesn’t load |


[​](#messages-from-the-built-in-guard)

Messages from the built-in guard

On a machine with managed settings, or in a session whose credential is a Team or Enterprise `/login`, the [built-in guard](plugins-mods-admin.md#know-what-happens-by-default) can refuse a mod or one of its answers. Each message names the option your organization’s administrator sets to change the rule.

| Message contains                                                                       | What it means                                                                                                                            | Where it appears                                                                                                       |
|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------|
| `mods are limited to your organization's by policy (allowManagedModsOnly)`             | Your organization allows only [its own mods](plugins-mods-admin.md#install-your-organizations-mods), so yours was refused          | The debug log, and the transcript in a [session that hot-reloads a plugin directory](#find-out-why-a-mod-does-nothing) |
| `tried to lift a deny rule in your settings`                                           | Your mod’s [`tool.check`](plugins-mods-reference.md#tools) hook approved a call that a `deny` rule refuses. The call stays denied. | The transcript and the debug log, once for each mod in a session. In a `claude -p` run, the debug log only.            |
| `the deny rules in your settings could not be checked for this call, so it is refused` | The guard failed while checking a call that a mod approved, so it refused the call                                                       | The reason Claude reads for the denied call                                                                            |


[​](#validate-passes-and-lists-no-hooks-line)

`validate` passes and lists no `hooks` line

`hooks/hooks.json` has no `modules` key, or the key is misspelled. Add `"modules": ["./register.js"]`.


[​](#hooks-module-did-not-load)

`hooks module did not load`

The line starts with the mod’s name, then `hooks module did not load:` and a reason, which gives the file and line when the problem is in your code. Claude Code couldn’t load the module, for example because its top-level code threw. Fix the error the reason names. If the reason ends with `; the version loaded before is unloaded`, no version of the mod is running now. Once you fix the error, run [`/reload-plugins`](plugins-cli-reference.md#reload-plugins) in the session to load the mod again. If you installed the mod and didn’t write it, ask its author to fix the error.


[​](#options-do-not-fit-plugin-json-userconfig)

`options do not fit plugin.json userConfig`

The line starts with the mod’s name, then `hooks module did not load: options do not fit plugin.json userConfig:` and a reason. An option fails validation against its [`userConfig`](plugins-components.md#user-configuration) field, such as a number above the field’s `max`, or a required field has no value. Set or change the value. The end of the line names its `pluginConfigs` entry in `settings.json`.


[​](#code-nested-too-deep-to-scan-more-than-2000-scopes)

`code nested too deep to scan: more than 2000 scopes`

The line starts with the mod’s name, then `hooks module did not load:`, the file, and `code nested too deep to scan: more than 2000 scopes`. A file in a hooks module can’t nest scopes, such as functions, blocks, and loops, more than [2,000 deep](plugins-mods-reference.md#limits). [`claude plugin validate`](plugins-mods-create.md#check-what-claude-code-reads-from-your-mod) reports the same reason. Rewrite the code so its scopes nest less deeply.


[​](#the-engine-ran-out-of-stack-linking-the-file-and-its-imports)

`the engine ran out of stack linking the file and its imports`

Code in the hooks module is nested too deeply, or its chain of imports is too long, for Claude Code to load it. The line starts with the mod’s name and `hooks module did not load:`, then gives a file’s name and this reason. Look through that file and the files reached through its imports for the most deeply nested part and rewrite it to nest less, or shorten the chain of imports. If you installed the mod and didn’t write it, ask its author to.


[​](#the-parser-stopped-or-the-engine-ran-out-of-stack-loading-the-file)

`the parser stopped` or `the engine ran out of stack loading the file`

Code in one file of the hooks module is nested so deeply that Claude Code ran out of stack reading it. The line starts with the mod’s name and `hooks module did not load:`, then gives that file’s name and one of these reasons:

- `code nested too deep to scan (the parser stopped: <message>)`
- `the engine ran out of stack loading the file (code nested too deep): <message>`

The reason gives no line number and doesn’t say what kind of code is nested, so look through the file yourself for its most deeply nested part. Rewrite that part to nest less. If you installed the mod and didn’t write it, ask its author to.


[​](#no-mod-loads-in-a-directory-you-opened-for-the-first-time)

No mod loads in a directory you opened for the first time

You haven’t answered the trust prompt for the directory. Start an interactive session in that directory with `claude`, and accept the trust prompt it opens with.


[​](#no-installed-plugin-loads-at-all)

No installed plugin loads at all

You started Claude Code with `--safe-mode`. Start without the flag.


[​](#claude-code-stops-asking-to-enable-hot-reloading)

Claude Code stops asking to enable hot reloading

Claude writes a mod in an interactive session, nothing loads, and Claude Code doesn’t ask again [whether to enable hot reloading](plugins-mods-create.md#ask-claude-for-a-mod). If the question ends three times without an answer picked, hot reloading stays off. For example, the question ends that way when you set [`askUserQuestionTimeout`](../02-Claude-Code-CLI/settings-reference.md#askuserquestiontimeout) and the time passes before you answer. That setting applies here because Claude Code asks in the same [question dialog that `AskUserQuestion` uses](../02-Claude-Code-CLI/tools-reference.md#question-auto-continue-timeout). A question you dismiss yourself doesn’t count toward the three. To run the mod, [copy its directory out of the mods folder](plugins-mods-create.md#use-the-mod-in-other-sessions), then in your shell start a new session with `--plugin-dir`, as in `claude --plugin-dir ~/mods/git-branch`.


[​](#a-hook-is-skipped-or-a-mod-is-unloaded)

A hook is skipped or a mod is unloaded

The mod loaded, and then Claude Code skipped one of its hooks or unloaded it.


[​](#hook-skipped)

`hook skipped`

The line names the mod and the event, then says `hook skipped:` and a reason, as in `first-mod: tool.call hook skipped: threw Error: boom`. A hook threw, exceeded its [time limit](plugins-mods-reference.md#limits), or returned a result of the wrong shape. The line appears once for each event and kind of failure until the mod reloads. Fix the error. The debug log has a line for every occurrence.


[​](#no-command-run-hook-answered-it)

`no command.run hook answered it`

You run a command your mod added, and the reply names the mod and the command, as in `first-mod registered /tally but no command.run hook answered it`, then tells you to add a hook. Claude Code prints that reply when the command reaches the end of the chain with no answer, which happens in two cases:

- **No hook answered the command**: the module has no `command.run` hook, the hook’s [filter](plugins-mods-events.md#filter-which-events-a-hook-handles) names a different command, or the hook returned `next(e)`
- **Claude Code skipped the hook**: [`hook skipped`](#hook-skipped) lists the reasons. Passing `focus: false` to [`$.ui.open`](plugins-mods-interface.md#open-a-pane-at-the-right-time) is one way to get there.

If the module already has the hook the reply describes, look for a `hook skipped` line that names `command.run`, which gives the reason. A [test](plugins-mods-test.md) that runs the command fails with the same reason.


[​](#it-crashed-the-hooks-worker)

`it crashed the hooks worker`

The line starts with the mod’s name, as in `first-mod was unloaded: it crashed the hooks worker`. Installed mods share one worker thread. The worker stopped responding or crashed, and Claude Code traced that to this mod and unloaded it. A hook that blocks the thread, such as a loop that never awaits, is one cause. Fix the hook.


[​](#its-session-start-ran-again-in-a-fresh-copy)

`its session.start ran again in a fresh copy`

The line starts with the mod’s name and names a `$.prompt.submit`, `$.command.run`, or `$.agent.spawn` call, as in `first-mod: its session.start ran again in a fresh copy; the $.prompt.submit call it had already made was not made again`. Claude Code loaded the mod’s module again, for example after the hooks worker crashed and was replaced, and the fresh copy’s [`session.start`](plugins-mods-reference.md#session) hook ran. The call the line names resolved with the result of its first run instead of running again, so your mod doesn’t submit the prompt, run the command, or start the subagent twice. The rest of the hook ran as usual. There’s nothing to fix. Before v2.1.292, the call ran a second time, so the prompt was submitted, the command run, or the subagent started twice.


[​](#$-agent-register-refused-the-hooks-module-that-made-the-call-is-no-longer-loaded)

`$.agent.register refused: the hooks module that made the call is no longer loaded`

The line starts with your mod’s name, as in `first-mod: $.agent.register refused: the hooks module that made the call is no longer loaded (it was reloaded or removed)`, and the agent isn’t registered. Your mod was reloaded or unloaded before the call. A reload loads a fresh copy of the hooks module, and this call came from code still running in the old copy, such as a hook that hadn’t returned yet. If that hook doesn’t catch the rejection, it fails and Claude Code [skips it](#hook-skipped). To register the agent from the copy that stays loaded, make the call in your [`session.start`](plugins-mods-reference.md#session) hook, which runs again in each fresh copy after a reload.


[​](#mods-that-run-in-the-hooks-worker-are-off-for-this-session)

`mods that run in the hooks worker are off for this session`

The line reads `hooks: mods that run in the hooks worker are off for this session: it crashed 3 times`. The worker stopped three times and Claude Code couldn’t trace the stops to one mod, so it unloaded every mod that isn’t built in, including mods your organization installs. This line reaches the transcript in every interactive session. Run `/reload-plugins` to load them again.


[​](#a-tool-call-is-denied)

A tool call is denied

The mod loaded and its hooks run, and a tool call it touched is refused.


[​](#a-hook-changed-this-calls-input-after-the-model-wrote-it)

`a hook changed this call's input after the model wrote it`

In auto mode, a denied tool call gives this reason. A hook changed the tool call’s input after the [server-side classifier](../02-Claude-Code-CLI/permission-modes.md#server-side-classifier-review) reviewed it, so that review doesn’t cover what would run. The hook can be a mod’s [`tool.call`](plugins-mods-reference.md#tools) or [`turn.step`](plugins-mods-reference.md#turns) hook, or a [`PreToolUse`](../07-Hooks/hooks.md#pretooluse) settings hook. The message doesn’t say which. The message tells Claude to issue the call once more as recorded. If that’s denied too, the hook changes the input every time, so turn off the mod or hook, or leave auto mode and approve the call yourself.


[​](#a-message-about-the-deny-rules-in-your-settings)

A message about the deny rules in your settings

`tried to lift a deny rule in your settings` and `the deny rules in your settings could not be checked for this call, so it is refused` both come from the built-in guard. Look them up in [Messages from the built-in guard](#messages-from-the-built-in-guard).


[​](#a-toast-appears-after-a-turn-is-stopped)

A toast appears after a turn is stopped


[​](#stopped-type-a-message-to-continue)

`Stopped: type a message to continue; a mod cannot resume work after Stop`

This toast means that after you stopped the turn, for example with Esc, a mod tried to start work on its own and Claude Code didn’t let it. Nothing needs fixing. After your next message, mods can start work again and [some of that work comes back](plugins-mods-api.md#work-your-mod-started-before-the-interruption) on its own. If what you expected from the mod doesn’t come back, ask for it again or run the mod’s command again. If you wrote the mod, see [Calls and background work after the user interrupts a turn](plugins-mods-api.md#calls-and-background-work-after-the-user-interrupts-a-turn).


[​](#a-drawing-doesn’t-appear-or-respond)

A drawing doesn’t appear or respond

The mod loaded, and its pane, band, toast, or controls don’t behave as you expect.


[​](#another-mods-band-shows-and-yours-doesnt)

Another mod’s band shows and yours doesn’t

There is one band above the prompt, and every mod shares it. A mod that runs [before yours](plugins-mods-events.md#the-order-mods-run-in) replaces your band when it returns a tree without the result of `next(e)` in it. To show both bands, change that mod’s hook, or ask its author to:

- **Keep the bands after it**: put the result of `await next(e)` among the children of a `Box` in its tree, as in the Band above the prompt tab of [Pick where to draw](plugins-mods-interface.md#pick-where-to-draw)
- **Keep the tree valid**: don’t give that `Box`, or any `Box` around it, a `width`, `height`, `minWidth`, `minHeight`, `position`, `top`, `left`, `right`, `bottom`, `display`, or `overflow` prop

To find that mod, [write a debug log](#read-the-debug-log) and search it for `answered ui.render without next()`. A line such as `other-mod (user) answered ui.render without next()` names a mod that returned its tree without calling `next(e)`. It doesn’t name the [render site](plugins-mods-reference.md#render-sites), so a pane-only mod can appear too. A mod that calls `next(e)` and drops the result gets no line, so check the hooks before yours too. After the change, you see nothing in the band if any of the mods returns a tree that doesn’t validate. To read the reason, look for a `ui.render (AbovePrompt) refused:` line in the transcript of a `--plugin-dir` session, or search the debug log for `does not validate`. With several mods in the band, neither line names a mod, so check each mod’s tree.


[​](#a-pane-or-band-is-empty-or-shows-claude-code’s-usual-content)

A pane or band is empty or shows Claude Code’s usual content

The [tree](plugins-mods-interface.md#build-a-tree-from-elements) your hook returned didn’t validate. With `--plugin-dir`, the transcript says `ui.render (Pane) refused:` with the reason, as in `first-mod: ui.render (Pane) refused: Box prop "flexDirection" must be one of row, column, row-reverse, column-reverse; the engine drew its own`. The debug log has `a hook returned a tree that does not validate` with the same reason. Read the reason on that line. Common causes are a prop the element doesn’t take and an element the app doesn’t have.


[​](#a-ui-render-line-says-threw-while-drawn)

A `ui.render` line says `threw while drawn`

The line names the [render site](plugins-mods-reference.md#render-sites), then says `threw while drawn:` and the error, as in `first-mod: ui.render (ToolUse) threw while drawn: <error>; the engine drew its own`. Claude Code hit that error while drawing the tree your [`ui.render`](plugins-mods-reference.md#interface) hook returned, or while drawing the site from the [`props` your hook passed to `next`](plugins-mods-interface.md#change-what-claude-code-already-draws). The ending `the engine drew its own` means the site shows Claude Code’s usual content. Read the error and fix the value in your hook that caused it. Before v2.1.289, this error in a transcript row ended the session with [`Claude Code exited after an unrecoverable interface error`](../02-Claude-Code-CLI/errors.md#exited-after-an-unrecoverable-interface-error).


[​](#the-module-failed-without-a-message)

`the module failed without a message`

A [`Client`](plugins-mods-interface.md#when-a-client-fails) failed with an error that has no message, such as `throw new Error()`. The line in its place reads like `my-mod: Client client/spinner.js: the module failed without a message`. Find the throw in your `Client`’s code and give the error a message. The line then shows that message. Before v2.1.289, the line showed `Error` as the reason instead.


[​](#$-ui-open-runs-and-no-pane-appears)

`$.ui.open` runs and no pane appears

The call didn’t come from something the user did, and the terminal is narrower than [the width that pane needs](plugins-mods-interface.md#when-a-pane-waits-for-a-wider-terminal). Open the pane from a command or a button, or check the call’s `isPlaced` result. See [Open a pane at the right time](plugins-mods-interface.md#open-a-pane-at-the-right-time).


[​](#a-toast-doesn’t-appear)

A toast doesn’t appear

Your mod calls [`$.ui.toast`](plugins-mods-api.md#show-something-without-starting-a-turn) in an interactive terminal session and you don’t see the toast. To confirm that the call ran, look in the [debug log](#read-the-debug-log) for a line with your mod’s name and the toast’s text, as in `$.ui.toast (first-mod): build finished`. Then check for causes such as these:

- **The line for the call is missing**: look for one that says why Claude Code refused the call, as in `first-mod: $.ui.toast dropped: timeoutMs is a whole number of ms, 1 to 60000`.
- **A pane is holding toasts**: your mod or another one passed [`holdToasts`](plugins-mods-interface.md#hold-toasts-behind-a-dialog) when it opened the pane that’s showing. Close the pane to end the hold. If the pane is yours and is meant to stay open, remove `holdToasts` from its `$.ui.open` call and open the pane again.
- **The toast is under the prompt**: in the [classic renderer](../02-Claude-Code-CLI/fullscreen.md#enable-fullscreen-rendering), look at the right under the prompt. A toast there is one line that starts with the mod’s name, rather than a box at the top right.
- **Your mod raised a newer toast**: in the classic renderer, a newer toast from your mod can take the place of one that’s showing or waiting to show. The debug log has another line for the older toast, which ends with `gave way, cut short` when it was showing, or `gave way, unseen` when it never appeared. To show both messages, put them in one toast.
- **The toast ran out of time undrawn**: in fullscreen rendering, Claude Code draws at most three toasts at a time, so a toast can run out of time before it’s drawn. The debug log has another line for that toast, which ends with `left the stack, never drawn`. When your mod raises several at once, put the messages in one toast.

Before v2.1.290, Claude Code dropped a toast raised within two seconds of the last one it showed for your mod, and the debug log line for the dropped toast said `within 2000ms of the last; dropped`.


[​](#plugin-panel-hidden)

The band shows `plugin panel hidden`

The band is [collapsed](plugins-mods-interface.md#when-the-user-collapses-the-band). It collapses when you press Ctrl+X then Ctrl+A, or the keys you bound in their place, and in [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md) when you click the `[-]` on its first row. It stays collapsed until you expand it or start Claude Code again, and meanwhile no hotkey or `action` binding presses a button in it. To expand the band, press the keys the row names, Ctrl+X then Ctrl+A by default, or in fullscreen rendering click the row.


[​](#hotkeys-do-nothing)

Hotkeys do nothing

Your pane or band doesn’t have keyboard focus, your button is in a collapsed band, or the drawing is in the VS Code extension, where buttons have no hotkeys. Do the one that matches:

- **No keyboard focus**: press Ctrl+X then Tab or, in [fullscreen rendering](../02-Claude-Code-CLI/fullscreen.md), click the pane or band. To have the pane take focus when it opens, open it with `focus: true` from a command.
- **The button is in a collapsed band**: the band shows one dim row that starts with `▸ plugin panel hidden`. Press the keys the row names, Ctrl+X then Ctrl+A by default, to [expand it](#plugin-panel-hidden).
- **In the VS Code extension**: click the button, or press Enter on it once it has focus


[​](#a-drawing-works-in-the-terminal-and-not-in-the-desktop-app-or-vs-code)

A drawing works in the terminal and not in the Desktop app or VS Code

Either that app doesn’t draw the site or element, or the session is one where the VS Code extension draws nothing for mods. The mod’s other hooks still run. Check for these causes:

- **The site or element isn’t drawn in that app**: look up your site in the [render sites](plugins-mods-reference.md#render-sites) table and your elements in the [elements](plugins-mods-reference.md#elements) table. For an element the app lacks, check `e.surface` in the hook and return a tree the app can draw.
- **The VS Code extension is older than 2.1.299**: the chat panel draws for mods from [extension version](../03-IDE-Integrations/vs-code.md#install-the-extension) 2.1.299. Check the version in VS Code’s Extensions view and update the extension there.
- **The extension is older than 2.1.304 and the session doesn’t fetch feature flags**: update the extension, or run `claude` in VS Code’s integrated terminal instead. Sessions on Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry skip the fetch. For the others that skip it, such as a [Claude apps gateway](../13-Enterprise-Admin/claude-apps-gateway.md) session, see [Features that need feature-flag fetching](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching).


[​](#an-edit-or-a-value-is-lost)

An edit or a value is lost

The mod runs, and a change you made or a value it kept isn’t there.


[​](#your-edits-don’t-take-effect)

Your edits don’t take effect

You’re editing a plugin you installed. Claude Code runs the cached copy for the installed version. Develop with `--plugin-dir` pointed at your working copy, as in `claude --plugin-dir ./first-mod`, which reloads when you save.


[​](#a-value-resets-when-the-module-reloads)

A value resets when the module reloads

Module-level variables are re-initialized on each reload. [Keep the value in `$.state` or `$.store`](plugins-mods-interface.md#keep-state).


[​](#a-value-resets-after-/clear-/resume-or-/branch)

A value resets after `/clear`, `/resume`, or `/branch`

A value resets, or a saved value is replaced by its default. Each of those commands resets `$.state` to its defaults, and `session.start` doesn’t fire again. [Load the saved value again](plugins-mods-interface.md#load-a-saved-value-again-after-clear) in a `classic.SessionStart` hook.


[​](#read-the-debug-log)

Read the debug log

The debug log has a line for every module Claude Code loads or refuses, every hook that fails, and every result it refuses, so it’s where to look when the transcript shows nothing. To write one, in your shell start Claude Code with `--debug`, or with `--debug-file <path>` to choose where it goes:

```python
claude --debug-file ./mod-debug.log --plugin-dir ./first-mod
```

In another terminal, follow the file and filter for your mod’s name:

```python
tail -f ./mod-debug.log | grep first-mod
```

A mod that loaded has a line that names it and lists the events it handles. A mod loaded with `--plugin-dir` appears under its name followed by `@inline`:

```python
hooks module first-mod@inline loaded (worker, environment 2, tier user); events: session.start,tool.call,command.run,ui.render
```

A drawing that didn’t validate counts as a refused result and gets a line too. To write your own lines in the log, call [`$.ui.log`](plugins-mods-api.md#show-something-without-starting-a-turn) with a second argument, as in `$.ui.log('message', { to: 'debug' })`. Without the second argument, `$.ui.log` adds a dim line to the transcript. While you edit a mod loaded with `--plugin-dir`, the transcript shows a line for each reload that names the mod and lists its hooks. If a save breaks the module, the line says `reload failed, the previous version stays loaded:` with the reason, and the last working version keeps running until Claude Code next reloads plugins, such as when you run `/reload-plugins`.


[​](#next-steps)

Next steps

- [Test a mod](plugins-mods-test.md): catch problems before they reach a session
- [Troubleshoot plugins](plugins-troubleshooting.md): problems with installing and loading a plugin that aren’t specific to mods

---

## Manage Claude Code plugins for your organization - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/org
Path: 08-Plugins-Skills/plugins-org.md

## On this page

- [Pre-install and require plugins](#pre-install-and-require-plugins)
  - [Choose a delivery mechanism](#choose-a-delivery-mechanism)
  - [Which managed source applies on a machine](#which-managed-source-applies-on-a-machine)
  - [Require a marketplace and its plugins](#require-a-marketplace-and-its-plugins)
  - [Require plugins per repository](#require-plugins-per-repository)
  - [When each surface applies the plugin keys](#when-each-surface-applies-the-plugin-keys)
  - [Confirm the rollout](#confirm-the-rollout)
- [Seed containers and CI](#seed-containers-and-ci)
- [Restrict what users can install](#restrict-what-users-can-install)
  - [Control matrix](#control-matrix)
  - [Aliases for the marketplace keys](#aliases-for-the-marketplace-keys)
  - [Allowlist with strictKnownMarketplaces](#allowlist-with-strictknownmarketplaces)
  - [How entries match](#how-entries-match)
  - [Keep skills-directory plugins loading](#keep-skills-directory-plugins-loading)
  - [Marketplaces hosted on claude.ai](#marketplaces-hosted-on-claude-ai)
  - [Lock every source out](#lock-every-source-out)
  - [Blocklist with blockedMarketplaces](#blocklist-with-blockedmarketplaces)
  - [Allow the official marketplace and your own](#allow-the-official-marketplace-and-your-own)
- [Set update policy](#set-update-policy)
  - [Turn auto-update on or off per marketplace](#turn-auto-update-on-or-off-per-marketplace)
  - [Turn updates off for the whole fleet](#turn-updates-off-for-the-whole-fleet)
  - [Assign release channels to user groups](#assign-release-channels-to-user-groups)
- [Recommend plugins](#recommend-plugins)
- [Audit and review](#audit-and-review)
  - [OpenTelemetry events](#opentelemetry-events)
  - [Analytics API](#analytics-api)
- [Plan for what managed settings can’t enforce](#plan-for-what-managed-settings-can%E2%80%99t-enforce)
- [Troubleshoot policy](#troubleshoot-policy)
- [Next steps](#next-steps)

Manage plugins for your organization

# Manage Claude Code plugins for your organization

Copy pageCopy page

Control which plugins Claude Code installs and allows across your organization through managed settings.

Copy pageCopy page

Managed settings let you decide which plugins Claude Code installs and allows on every machine in your organization. Users can’t override them. You deliver them either as [server-managed settings](../13-Enterprise-Admin/server-managed-settings.md) from the claude.ai admin console or as endpoint-managed settings through MDM or a `managed-settings.json` file. Most controls on this page take effect only from managed settings. This page is for administrators, and the settings here govern Claude Code.

These cases are covered on other pages:

- **Installing plugins for yourself**: start at [Install plugins](discover-plugins.md)
- **Controlling which plugins members can use in claude.ai and Cowork**: see [Manage plugins for your organization](https://claude.com/docs/plugins/admin) on claude.com
- **Rolling one plugin out to claude.ai, Cowork, and Claude Code together**: see [Choose a rollout route](https://claude.com/docs/plugins/org-rollout#choose-a-rollout-route) on claude.com
- **The plugins page in claude.ai’s admin settings**: [**Organization settings \> Plugins & skills**](https://claude.ai/admin-settings/skills?tab=inventory) turns plugins on for members’ claude.ai accounts, and those reach Claude Code as [synced plugins](plugins-loading.md#synced-plugins). It doesn’t set any of the keys on this page

The sections follow the order most rollouts take: [require plugins](#pre-install-and-require-plugins) for everyone or per repository, [seed containers and CI](#seed-containers-and-ci), [restrict](#restrict-what-users-can-install) what users can add on their own, [set update policy](#set-update-policy), then [audit](#audit-and-review) what’s installed. To review every policy key in one place, see the [control matrix](#control-matrix).


[​](#pre-install-and-require-plugins)

Pre-install and require plugins

A marketplace is a catalog of plugins that Claude Code fetches from a git repository, a URL, or a local path. Once you register a marketplace on a machine, Claude Code can install plugins from it. To install plugins for a fleet, set two keys together in [managed settings](../13-Enterprise-Admin/managed-settings.md), the policy file or server-delivered policy that every machine in your organization reads: `extraKnownMarketplaces` registers a marketplace on each machine, and `enabledPlugins` names the plugins to install and enable from it. [Choose a delivery mechanism](#choose-a-delivery-mechanism) covers how managed settings reach each machine.


[​](#choose-a-delivery-mechanism)

Choose a delivery mechanism

Managed settings reach a machine through one of three delivery mechanisms:

- **Server-managed settings**: set the plugin keys as JSON at [**Organization settings \> Claude Code \> Managed settings**](https://claude.ai/admin-settings/claude-code). Requires an [Owner role](../13-Enterprise-Admin/server-managed-settings.md#access-control) in your Claude organization. A cloud session fetches these settings before it installs plugins.
- **MDM policies**: on macOS, deliver a plist whose top-level keys are the settings keys. On Windows, store the whole JSON document as a string in a registry value. The plist domain and the registry key are in [Where each mechanism stores the policy](../13-Enterprise-Admin/managed-settings.md#where-each-mechanism-stores-the-policy).
- **Managed settings file**: place a `managed-settings.json` at the platform’s system path. You can also add files to the `managed-settings.d/` drop-in directory beside it. The file paths per platform are in [Where each mechanism stores the policy](../13-Enterprise-Admin/managed-settings.md#where-each-mechanism-stores-the-policy), and the drop-in merge rules are in [Split a file-based policy across teams](../13-Enterprise-Admin/managed-settings.md#split-a-file-based-policy-across-teams).

Use server-managed settings if you have a Claude for Teams or Enterprise organization on claude.ai and your devices aren’t all under MDM. Otherwise use an MDM policy or the managed settings file. For the trade-off, see [Choose between server-managed and endpoint-managed settings](../13-Enterprise-Admin/server-managed-settings.md#choose-between-server-managed-and-endpoint-managed-settings).


[​](#which-managed-source-applies-on-a-machine)

Which managed source applies on a machine

By default, only one of these three sources applies on a machine. Claude Code uses the first that delivers a policy key, checking server-managed settings first, then MDM policies, then the managed settings file. If server-managed settings deliver even one unrelated policy key, Claude Code ignores the plugin keys in an MDM policy or managed settings file on that machine, apart from the [keys it reads from every source](../13-Enterprise-Admin/managed-settings.md#keys-read-from-every-admin-source). To apply every source instead, set [`managedSourcesBehavior`](../13-Enterprise-Admin/managed-settings.md#compose-every-managed-source) to `"merge"`. [How Claude Code combines managed sources](../13-Enterprise-Admin/managed-settings.md#how-claude-code-combines-managed-sources) also lists the keys Claude Code reads from every source in both modes.


[​](#require-a-marketplace-and-its-plugins)

Require a marketplace and its plugins

Add the marketplace under `extraKnownMarketplaces`, keyed by the marketplace’s own `name` from its `marketplace.json`. Then add each plugin under `enabledPlugins` as `plugin-name@marketplace-name`. Each marketplace entry carries a `source` object with a `source` field naming the type, such as `github`. This managed settings example registers an organization marketplace and force-enables two plugins from it:

```python
{
  "extraKnownMarketplaces": {
    "your-marketplace": {
      "source": { "source": "github", "repo": "your-org/your-marketplace" },
      "autoUpdate": true
    }
  },
  "enabledPlugins": {
    "code-formatter@your-marketplace": true,
    "deploy-helper@your-marketplace": true
  }
}
```

After the settings reach a machine, Claude Code registers the marketplace and installs the two plugins at the start of the user’s next session. Users see them in `/plugin`, and disabling one at their own scope doesn’t stop it from loading, because managed settings take precedence over every other scope. To block a plugin at every scope and hide it from the marketplace listing, set it to `false` in the managed `enabledPlugins` instead. Adjust the `autoUpdate` and `source` fields for your marketplace:

- **`autoUpdate`**: `true` keeps the marketplace and its plugins refreshing in the background, and `false` turns that off. See [Set update policy](#set-update-policy).
- **`source`**: `github` is one of several source types. A `git` source takes a `url` for GitLab or an internal host, and a `url` source takes the address of a hosted `marketplace.json`. Every source shape is in the [marketplace reference](plugins-marketplace-reference.md).

If the marketplace is a private git repository, each user needs read access to it. The clone of a git-based marketplace runs with git on the user’s machine, using stored credentials and no prompts. For users without git-host accounts, use a [seed](#seed-containers-and-ci) instead. A managed entry also overrides a same-name marketplace entry or `--plugin-dir` copy from another source:

- **Marketplaces**: a managed marketplace entry replaces a lower-precedence entry with the same name, and the two entries’ fields don’t merge.
- **`--plugin-dir` copies**: `--plugin-dir` loads a plugin from a local directory for one session. For what happens when that copy’s name matches a plugin your managed `enabledPlugins` names, see [Name conflicts](plugins-loading.md#name-conflicts).

Anthropic’s official marketplace `claude-plugins-official` needs no `extraKnownMarketplaces` entry when `enabledPlugins` sets one of its plugins to `true`. That `name@claude-plugins-official` entry declares the marketplace by itself, wherever these keys apply. If you enable none of its plugins and still want it registered on every machine, give it an explicit entry, as [Allow the official marketplace and your own](#allow-the-official-marketplace-and-your-own) does.


[​](#require-plugins-per-repository)

Require plugins per repository

To cover one repository’s contributors instead of your whole fleet, set `extraKnownMarketplaces` and `enabledPlugins` in that repository’s `.claude/settings.json`. The `extraKnownMarketplaces` entries apply only in a folder the contributor has trusted, and in an untrusted folder Claude Code ignores them without a message:

- **Interactive sessions**: Claude Code registers the marketplace only after the contributor accepts the [workspace trust dialog](../02-Claude-Code-CLI/permissions.md#what-runs-before-you-trust-a-folder) for that folder.
- **[Non-interactive `-p` runs](../02-Claude-Code-CLI/headless.md)**: the entries apply only in a folder whose trust the user already accepted interactively, or whose `hasTrustDialogAccepted` flag you set in `~/.claude.json`.

A plugin that the marketplace lists by a relative path loads from the marketplace copy once the repository’s `extraKnownMarketplaces` entries apply. A plugin whose marketplace entry points at an external source instead, such as the plugin’s own GitHub repository, doesn’t install from the repository’s settings alone. Each contributor sees `Plugin "<name>" is enabled in project settings but isn't installed` until they run `claude plugin install <name>@<marketplace> --scope project`, as [Install plugins](discover-plugins.md) describes. If you use a local `directory` or `file` source with a relative path, the path resolves against your repository’s main checkout. When you run Claude Code from a git worktree, the path still points at the main checkout, so all worktrees share the same marketplace location. To roll out a bundle of plugins with dependencies, put the bundle plugin in `enabledPlugins`, as [Plugin dependencies](plugins-dependencies.md) describes.


[​](#when-each-surface-applies-the-plugin-keys)

When each surface applies the plugin keys

The table shows when each kind of Claude Code session applies `extraKnownMarketplaces` and `enabledPlugins`, from managed settings and from a repository’s `.claude/settings.json`. For the Desktop app and the IDE extensions, see [Install a plugin](discover-plugins.md#install-a-plugin).

| Surface               | Managed `extraKnownMarketplaces` and `enabledPlugins`                                                                                                                                                                                                                                                                                    | Repository `.claude/settings.json`                                                                |
|:----------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------|
| Terminal, interactive | Applied at session start on every machine that receives the settings                                                                                                                                                                                                                                                                     | `extraKnownMarketplaces` applied after trust; `enabledPlugins` applied at session start           |
| `-p` and CI           | Applied at session start, with installs running in the background                                                                                                                                                                                                                                                                        | `extraKnownMarketplaces` in trusted folders only; `enabledPlugins` applied                        |
| Cloud sessions        | In an Anthropic-hosted environment, only server-managed settings reach the session, which waits for them before it installs plugins. MDM policies and managed settings files stay on the user’s machine. For a self-hosted environment, see [Where and when a policy applies](../13-Enterprise-Admin/managed-settings.md#where-and-when-a-policy-applies) | See the **Cloud session** tab under [Install a plugin](discover-plugins.md#install-a-plugin) |

In a `-p` or CI run, marketplaces and plugins install in the background, so a plugin can be missing from the first turn. Set `CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1` to make the run wait for the install before its first query.


[​](#confirm-the-rollout)

Confirm the rollout

Check that the marketplace and plugins arrived on a machine or in a CI run:

- **On one machine**: start Claude Code and run `/plugin`. The marketplace and the plugins are listed.
- **In CI**: run `claude -p` with `--output-format stream-json --verbose`. The `init` event lists the loaded plugins under `plugins`.


[​](#seed-containers-and-ci)

Seed containers and CI

For container images and CI runners that can’t clone at runtime, pre-populate a plugins directory at build time and point `CLAUDE_CODE_PLUGIN_SEED_DIR` at it. Claude Code registers the seed’s marketplaces at startup and loads plugin caches from the seed in place, without cloning. A seed also serves users who have no git-host account.

In CI/CD environments, configure a git credential helper before installing plugins from private repositories. On GitHub Actions, export a token with read access to the marketplace repository as `GH_TOKEN`, then run `gh auth setup-git`. The default workflow token can only access the workflow’s own repository, so a private marketplace in another repository needs a personal access token or app token.

1

Install into the seed at build time

Set `CLAUDE_CODE_PLUGIN_CACHE_DIR` to the seed path so the marketplace and plugins install there instead of `~/.claude/plugins`:

```python
CLAUDE_CODE_PLUGIN_CACHE_DIR=/opt/claude-seed claude plugin marketplace add your-org/your-marketplace
CLAUDE_CODE_PLUGIN_CACHE_DIR=/opt/claude-seed claude plugin install code-formatter@your-marketplace
```

The seed has the same layout as `~/.claude/plugins`: `known_marketplaces.json`, `marketplaces/<name>/`, and `cache/<marketplace>/<plugin>/<version>/`. You can mount the seed at a different path than you built it at.

2

Point the runtime at the seed

Set `CLAUDE_CODE_PLUGIN_SEED_DIR=/opt/claude-seed` in the container’s environment. To use several seeds, separate their paths with `:` on Unix or `;` on Windows. Claude Code uses the first seed that contains a given marketplace or plugin cache.

3

Enable the plugins

The plugins in a seed aren’t enabled on their own. Set `enabledPlugins` for each seed plugin you want loaded, in managed settings or in the repository’s `.claude/settings.json`.

To verify a seed, run `claude -p` with `--output-format stream-json --verbose` in the image. In the `init` event’s `plugins` list, each loaded plugin’s `path` is under the seed, such as `/opt/claude-seed/cache/your-marketplace/code-formatter/1.0.0`. Seed marketplaces follow these rules:

- **Read-only**: Claude Code never writes to the seed and forces `autoUpdate` off for seed marketplaces.
- **Seed entries take precedence**: on each startup, a marketplace declared in the seed overwrites the user’s entry of the same name. Users opt out of a seed plugin with `claude plugin disable`, not by removing the marketplace.
- **Update and remove fail**: `claude plugin marketplace update <name>` and `remove` without `--scope` on a seed marketplace fail with a message that names the seed directory.
- **Policy still applies**: the [allowlist and blocklist](#restrict-what-users-can-install) check a seed marketplace’s recorded source too. Allow the source you built the seed from.

For fleets with no outbound git access, combine a seed with `directory` or `file` marketplace sources on a shared mount. Set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` as well, which also turns off [plugin auto-update](plugins-loading.md#when-auto-update-runs). If a proxy is available, see [Proxy configuration](../13-Enterprise-Admin/network-config.md#proxy-configuration) for the variables to set.


[​](#restrict-what-users-can-install)

Restrict what users can install

The managed `strictKnownMarketplaces` allowlist and `blockedMarketplaces` blocklist decide which marketplace sources plugins may come from. A marketplace’s source is the git repository, URL, or local path that Claude Code fetches it from. Both lists match the source of the marketplace a plugin comes from, not the plugin’s own entry inside that marketplace. For the common lockdown, which allows the official marketplace and your own, see [Allow the official marketplace and your own](#allow-the-official-marketplace-and-your-own). Pair it with [`disableSideloadFlags`](#control-matrix) so users can’t load plugins from a local directory or URL either. Both lists apply before anything downloads and again at session start:

- **Before a download**: the lists apply when a user adds a marketplace and on every install, update, refresh, and auto-update.
- **At session start**: the lists apply again to plugins that are already installed, so an installed plugin whose marketplace source no longer matches doesn’t load. `/plugin` lists it with `Marketplace "<name>" is not in the allowed marketplace list` or `Marketplace "<name>" is blocked by enterprise policy`.

Where the two lists are enforced depends on where you set them:

- **The claude.ai admin console**: Claude Code enforces both lists in the sessions that [read server-managed settings](../13-Enterprise-Admin/managed-settings.md#where-and-when-a-policy-applies). claude.ai also checks them when anyone in your organization adds a new marketplace from a git repository on claude.ai, or from **Customize** in the Claude Desktop app outside its Code tab. That covers a marketplace a member adds for their own account and one added for the whole organization under [**Organization settings \> Plugins**](https://claude.ai/admin-settings/plugins). claude.ai refuses a repository that the allowlist doesn’t admit or that the blocklist names. It doesn’t re-check a marketplace that was added in either place before you set the lists, and it doesn’t check uploaded plugins.
- **A managed settings file, OS-level policy, or other managed source**: Claude Code enforces both lists where it reads that source. claude.ai doesn’t read it.

While any allowlist is set, or a blocklist names any source other than [`skills-dir`](#blocklist-with-blockedmarketplaces), a plugin whose marketplace Claude Code can’t find doesn’t load. `/plugin` shows the policy error for it rather than a not-found error. The common case is a stale `enabledPlugins` entry for a marketplace nobody registered.


[​](#control-matrix)

Control matrix

The table lists each plugin policy key, what it enforces, and what it can’t do.

| Key                                                                                         | What it enforces                                                                                                                                                                                                                                                                                                                                                  | What it can’t do                                                                                                                                                                                                     |
|:--------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `strictKnownMarketplaces`                                                                   | Allowlist of marketplace sources. `[]` blocks every source, including the official marketplace. Alias: `allowedMarketplaces`                                                                                                                                                                                                                                      | Doesn’t register a marketplace, restrict entries inside an allowed marketplace, or block `--plugin-dir`                                                                                                              |
| `blockedMarketplaces`                                                                       | Blocklist of marketplace sources, checked before the allowlist                                                                                                                                                                                                                                                                                                    | Doesn’t block a marketplace already registered from a source it doesn’t match                                                                                                                                        |
| `syncClaudeAiPlugins`                                                                       | Set `false` to stop Claude Code downloading and loading the plugins [synced from claude.ai](plugins-loading.md#synced-plugins) for each user’s account. Requires Claude Code v2.1.273 or later                                                                                                                                                              | Doesn’t turn off one synced plugin. For that, set `"<name>@synced": false` in [`enabledPlugins`](../02-Claude-Code-CLI/settings-reference.md#enabledplugins)                                                                         |
| `enabledPlugins`                                                                            | `true` force-enables, `false` blocks at every scope and hides the plugin                                                                                                                                                                                                                                                                                          | Doesn’t install a plugin whose marketplace isn’t registered or allowed                                                                                                                                               |
| `disableSideloadFlags`                                                                      | Rejects `--plugin-dir`, `--plugin-url`, `--agents`, the Agent SDK `plugins` option, and non-SDK `--mcp-config` at startup, and rejects folders named in the [`CLAUDE_CODE_PLUGIN_DIRS`](../02-Claude-Code-CLI/env-vars.md#variables) variable the same way                                                                                                                        | Doesn’t restrict `.mcp.json`, `claude mcp add`, or SDK-provided servers. Pair it with [`allowedMcpServers`](../06-MCP-Tools/General/managed-mcp.md)                                                                                    |
| `disableCommandPluginSources`                                                               | Blocks plugins with a `command` source from installing, updating, or loading. A `command` source is one whose plugin directory is produced by running a command on the machine. When unset, it takes the value of `allowManagedHooksOnly`                                                                                                                         | Doesn’t affect other source types                                                                                                                                                                                    |
| `allowManagedHooksOnly`                                                                     | Restricts which hooks run. See [`allowManagedHooksOnly`](../02-Claude-Code-CLI/settings-reference.md#allowmanagedhooksonly)                                                                                                                                                                                                                                                       | Doesn’t trust hooks from plugins users enable themselves                                                                                                                                                             |
| `strictPluginOnlyCustomization`                                                             | Blocks skills, agents, hooks, and MCP servers that don’t come from a plugin, managed settings, or Claude Code’s built-ins. It also stops [skills-directory plugins](#keep-skills-directory-plugins-loading) loading. Set `true` to cover all four types, or an array of `skills`, `agents`, `hooks`, and `mcp` values such as `["skills", "hooks"]` to cover some | Doesn’t restrict which plugins users install. Pair it with `strictKnownMarketplaces`                                                                                                                                 |
| `pluginSuggestionMarketplaces`                                                              | Marketplaces whose plugins may appear as install suggestions. See [Recommend plugins](#recommend-plugins)                                                                                                                                                                                                                                                         | Doesn’t affect the built-in tips                                                                                                                                                                                     |
| `pluginTrustMessage`                                                                        | Appends your text to the trust warning that `/plugin` shows before a plugin installs                                                                                                                                                                                                                                                                              | Doesn’t change the warning’s own text                                                                                                                                                                                |
| `allowedChannelPlugins`                                                                     | Replaces the default list of plugins allowed to push channel messages. Requires `channelsEnabled: true`                                                                                                                                                                                                                                                           | See [Restrict which channel plugins can run](../02-Claude-Code-CLI/channels.md#restrict-which-channel-plugins-can-run)                                                                                                               |
| [`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL=1`](../02-Claude-Code-CLI/env-vars.md)               | Stops interactive terminal sessions from auto-registering the official marketplace                                                                                                                                                                                                                                                                                | Doesn’t remove a marketplace already registered. The allowlist and blocklist gate the same auto-registration without it. A machine that started once with it set doesn’t resume auto-registration after you unset it |
| [`allowManagedModsOnly`](plugins-mods-admin.md#stop-user-installed-mods-from-loading) | Stops every installed [mod](plugins-mods-overview.md) that doesn’t [count as your organization’s](plugins-mods-admin.md#install-your-organizations-mods) from running its hooks                                                                                                                                                                       | Doesn’t stop a plugin that contains a mod from installing. For that, use the marketplace keys in this table                                                                                                          |

Every key in the table is a managed setting, apart from `enabledPlugins`, `syncClaudeAiPlugins`, `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`, and `allowManagedModsOnly`:

- **`enabledPlugins`**: you can set it in any scope, and managed settings lock it.
- **`syncClaudeAiPlugins`**: each user can also set it in their own user or local settings. See its [scope in the settings reference](../02-Claude-Code-CLI/settings-reference.md#syncclaudeaiplugins).
- **`CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL`**: this is an environment variable that you deliver through the managed `env` block shown under [Turn updates off for the whole fleet](#turn-updates-off-for-the-whole-fleet).
- **`allowManagedModsOnly`**: this is an option on a built-in plugin, which you set under `pluginConfigs` in managed settings. See [Stop user-installed mods from loading](plugins-mods-admin.md#stop-user-installed-mods-from-loading).

Each settings key here has an entry in the [settings reference](../02-Claude-Code-CLI/settings-reference.md).


[​](#aliases-for-the-marketplace-keys)

Aliases for the marketplace keys

`strictKnownMarketplaces` can also be spelled `allowedMarketplaces`, and `extraKnownMarketplaces` can also be spelled `additionalMarketplaces`.

- **Version**: the aliases require Claude Code v2.1.232 or later, and older clients ignore them. In a file that a mixed fleet reads, keep the canonical names.
- **Both spellings set**: when a file sets both spellings, the canonical key’s value applies.


[​](#allowlist-with-strictknownmarketplaces)

Allowlist with `strictKnownMarketplaces`

Set the allowlist to a list of these source objects. Most entries match exactly, `hostPattern` and `pathPattern` entries match as regular expressions, and `github` owner wildcards match by owner:

- **`github`**: `{ "source": "github", "repo": "your-org/approved-plugins" }`, with optional `ref` and `path`.
- **`github` owner wildcard**: `{ "source": "github", "repo": "your-org/*" }` matches every repository under that owner. The `*` must stand for the whole repository name. Claude Code ignores entries such as `*/plugins` and `your-org/tools-*` as invalid, so they match nothing. Requires Claude Code v2.1.223 or later.
- **`git`**: `{ "source": "git", "url": "https://gitlab.example.com/tools/plugins.git" }`, with optional `ref` and `path`.
- **`url`**: `{ "source": "url", "url": "https://plugins.example.com/marketplace.json" }`, with optional `headers`.
- **`file` and `directory`**: `{ "source": "file", "path": "/opt/marketplace/marketplace.json" }` or `{ "source": "directory", "path": "/opt/marketplace/plugins" }`, with absolute paths.
- **`hostPattern`**: `{ "source": "hostPattern", "hostPattern": "^github\\.example\\.com$" }`, matched against the host of `github`, `git`, and `url` sources. The pattern matches anywhere in the hostname, so anchor it with `^` and `$` as shown to match the whole host. A `github` source always counts as `github.com`. Use a `hostPattern` entry for a GitHub Enterprise Server or GitLab host where developers create their own marketplaces. The [GHES page](../02-Claude-Code-CLI/github-enterprise-server.md#allowlist-ghes-marketplaces-in-managed-settings) has the worked example.
- **`pathPattern`**: `{ "source": "pathPattern", "pathPattern": "^/opt/approved/" }`, matched against the `path` of `file` and `directory` sources. The pattern matches anywhere in the path, so start it with `^` to pin a directory prefix. `".*"` allows every local path.
- **`skills-dir`**: `{ "source": "skills-dir" }` matches no marketplace. For what it does, see [Keep skills-directory plugins loading](#keep-skills-directory-plugins-loading).


[​](#how-entries-match)

How entries match

A `url` entry matches on its `url` value; `headers` aren’t compared. For `github` and `git` entries, the `repo` or `url`, the `ref`, and the `path` must all match, or be absent on both sides:

- An entry without `ref` doesn’t cover a source with `ref: "main"`.
- An entry for `your-org/your-marketplace` doesn’t cover a `git` URL that clones the same repository.
- A trailing slash, a `.git` suffix, or `ssh://` in place of `https://` is a different value. When a marketplace can be cloned by more than one URL, prefer a `hostPattern` entry.

Owner-wildcard entries follow the exact rules for `ref` and match any `path` inside the repository unless the entry pins one. Wildcard matching is case-sensitive on the allowlist.


[​](#keep-skills-directory-plugins-loading)

Keep skills-directory plugins loading

Skills-directory plugins are the plugins users keep under `~/.claude/skills/` or a project’s `.claude/skills/` in folders that carry a `.claude-plugin/plugin.json`. If you set any allowlist without a `{ "source": "skills-dir" }` entry, they stop loading. If you lock any customization type with [`strictPluginOnlyCustomization`](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization), they stop loading whether or not the entry is present. The allowlist doesn’t apply to plain [skills](skills.md), meaning a `SKILL.md` without that manifest.


[​](#marketplaces-hosted-on-claude-ai)

Marketplaces hosted on claude.ai

The allowlist and blocklist match a [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai) by its host. To allow or block one, add a `hostPattern` entry that matches `claude.ai` to `strictKnownMarketplaces` or `blockedMarketplaces`. On the allowlist, such an entry admits your organization’s claude.ai marketplaces and the claude.ai default marketplaces, but not a marketplace made of a member’s own claude.ai uploads or one whose scope claude.ai didn’t state. Requires Claude Code v2.1.273 or later.


[​](#lock-every-source-out)

Lock every source out

An empty allowlist, `[]`, locks every marketplace source out, including the official marketplace. This lockdown doesn’t cover the plugins [synced from claude.ai](plugins-loading.md#synced-plugins), which Claude Code downloads from each user’s account rather than from a marketplace. To stop those as well, set [`syncClaudeAiPlugins`](../02-Claude-Code-CLI/settings-reference.md#syncclaudeaiplugins) to `false` in managed settings, or turn off Skills for your organization on claude.ai.


[​](#blocklist-with-blockedmarketplaces)

Blocklist with `blockedMarketplaces`

`blockedMarketplaces` takes the same source objects as [`strictKnownMarketplaces`](#allowlist-with-strictknownmarketplaces) and is checked first, so a source on both lists is blocked. Blocklist matching is wider than allowlist matching:

- Git URLs are canonicalized, so the `git@` and `https://` forms, `.git` suffixes, and trailing slashes of one `github.com` repository all match the same entry.
- A `github` entry also blocks the equivalent `git` URL, and the other way around.
- For an `owner/*` entry, the owner comparison is case-insensitive.
- An entry without `ref` or `path` blocks every ref and path of the repositories it matches.

This entry blocks every repository under one GitHub owner:

```python
{
  "blockedMarketplaces": [
    { "source": "github", "repo": "untrusted-org/*" }
  ]
}
```

The `url` entries in `blockedMarketplaces` also apply when a user adds an `https://` repository URL that Claude Code [clones rather than fetches](plugins-cli-reference.md#plugin-marketplace-add), such as a bare `github.com` or `gitlab.com` repository URL. The user can’t add that URL if an entry names it. The match ignores the `.git` suffix and any ref the user appends after `#`. Requires Claude Code v2.1.232 or later. A `{ "source": "skills-dir" }` entry here stops [skills-directory plugins](#keep-skills-directory-plugins-loading) from loading, from both `~/.claude/skills/` and a project’s `.claude/skills/`. A blocklist that names only that entry doesn’t count as an active restriction, so it doesn’t [stop plugins whose marketplace Claude Code can’t find](#restrict-what-users-can-install) from loading.


[​](#allow-the-official-marketplace-and-your-own)

Allow the official marketplace and your own

Most organizations allow the official marketplace and their own, and register both so every machine has them. This managed settings policy allows both marketplaces, registers both, force-enables two plugins, and rejects `--plugin-dir`:

```python
{
  "strictKnownMarketplaces": [
    { "source": "github", "repo": "anthropics/claude-plugins-official" },
    { "source": "github", "repo": "your-org/*" },
    { "source": "skills-dir" }
  ],
  "extraKnownMarketplaces": {
    "claude-plugins-official": {
      "source": { "source": "github", "repo": "anthropics/claude-plugins-official" }
    },
    "your-marketplace": {
      "source": { "source": "github", "repo": "your-org/your-marketplace" }
    }
  },
  "enabledPlugins": {
    "code-formatter@your-marketplace": true,
    "deploy-helper@your-marketplace": true
  },
  "disableSideloadFlags": true
}
```

On a machine with this policy, adding any source outside the list, for example `/plugin marketplace add https://example.com/other-marketplace.git`, fails with a message containing `is blocked by enterprise policy` followed by the allowed sources. `claude --plugin-dir ./x` exits with a message naming `disableSideloadFlags`. If you remove the `{ "source": "skills-dir" }` entry from this allowlist, [skills-directory plugins](#keep-skills-directory-plugins-loading) stop loading. Register both marketplaces with explicit `extraKnownMarketplaces` entries, as this policy does, rather than relying on the allowlist or on the official marketplace registering itself:

- **The allowlist doesn’t register anything**: an `extraKnownMarketplaces` entry does, and it must itself pass the allowlist. Claude Code refuses to register a managed marketplace whose source the allowlist doesn’t match.
- **The official marketplace registers itself only in an interactive terminal session**: even there, it registers only when the allowlist permits it. A `-p` run or a terminal attached to a cloud session never registers it.
- **A blocked attempt is remembered**: if a machine ever ran under a policy that blocked the official marketplace, Claude Code records the blocked attempt and doesn’t retry after the policy changes. An `[]` lockdown is one such policy. That machine registers it again only through an `extraKnownMarketplaces` entry such as the one in this policy, an `enabledPlugins` entry for one of its plugins, or a manual `/plugin marketplace add`.


[​](#set-update-policy)

Set update policy

You can set update policy per marketplace, for the whole fleet, or per user group through release channels.


[​](#turn-auto-update-on-or-off-per-marketplace)

Turn auto-update on or off per marketplace

Plugin auto-update runs in the background after startup for marketplaces that have it turned on. For which marketplaces have it on by default, see [When auto-update runs](plugins-loading.md#when-auto-update-runs). To decide for the fleet, set `"autoUpdate": true` or `false` on a managed `extraKnownMarketplaces` entry:

- If the managed entry sets the field, Claude Code refuses the user’s `/plugin` toggle with an error that starts `Auto-update for '<name>' is set by`.
- If the managed entry leaves the field unset, the user’s toggle persists.


[​](#turn-updates-off-for-the-whole-fleet)

Turn updates off for the whole fleet

To turn plugin auto-update off for every marketplace, set `DISABLE_AUTOUPDATER` in the managed `env` block, as this example does. The same variable also stops Claude Code’s own updates:

```python
{
  "env": {
    "DISABLE_AUTOUPDATER": "1"
  }
}
```

To stop Claude Code’s own updates but keep plugin auto-update, add `"FORCE_AUTOUPDATE_PLUGINS": "1"` to the same block. The other [environment variables that stop plugin auto-update](plugins-loading.md#when-auto-update-runs) work the same way. `DISABLE_AUTOUPDATER` doesn’t cover plugins with a [`command` source](plugins-marketplace-reference.md#command-plugin-source). Claude Code re-runs each enabled one’s command every session and installs the output when it changed. For what stops those runs, see [When a command source re-runs](plugins-loading.md#when-a-command-source-re-runs).


[​](#assign-release-channels-to-user-groups)

Assign release channels to user groups

To run stable and early-access channels, host two marketplaces that point at different refs of the same plugins. Then give each user group its own marketplace through either separate endpoint-managed settings or a gateway policy. Server-managed settings from the admin console [apply to every user in your organization](../13-Enterprise-Admin/server-managed-settings.md#current-limitations), so they can’t assign different settings to different groups.

- Deploy separate [endpoint-managed settings](../13-Enterprise-Admin/managed-settings.md#delivery-mechanisms), such as a managed settings file or an MDM profile, to each group’s devices. To check whether the per-group file or profile applies on a device that also has an organization-wide source, see [How Claude Code combines managed sources](../13-Enterprise-Admin/managed-settings.md#precedence-within-the-managed-tier).
- Define one [Claude apps gateway policy](../13-Enterprise-Admin/claude-apps-gateway-config.md#managed) per group. The gateway applies the first policy whose match rule fits a user, so order the policies so that each user reaches their group’s policy. That policy’s `extraKnownMarketplaces` map doesn’t merge with any other policy’s, so list every marketplace the group needs in it, not only its channel marketplace.

With either mechanism, the stable group receives this configuration:

```python
{
  "extraKnownMarketplaces": {
    "stable-tools": {
      "source": { "source": "github", "repo": "your-org/stable-tools" }
    }
  }
}
```

The early-access group receives `latest-tools` instead. To set up the two marketplaces, see [Run release channels](plugins-host-marketplace.md#run-release-channels).


[​](#recommend-plugins)

Recommend plugins

Marketplace owners can attach `relevance` signals to entries so Claude Code suggests the plugin when a project matches. Suggestions from a marketplace appear only when it’s registered on the user’s machine, you list its name in `pluginSuggestionMarketplaces` in managed settings, and you declare its source in the same policy. Declare the source either as the marketplace’s `extraKnownMarketplaces` entry or as an allowlist entry. The official marketplace needs only the name. See [Enable suggestions in managed settings](plugins-relevance.md#enable-suggestions-in-managed-settings).


[​](#audit-and-review)

Audit and review

OpenTelemetry events and the Analytics API tell you what your fleet installs and runs. For what a plugin can run on a machine and what each trust tier permits, read [Plugin security](plugins-security.md) before you approve a marketplace.


[​](#opentelemetry-events)

OpenTelemetry events

`claude_code.plugin_installed` records each install, and `claude_code.plugin_loaded` records each enabled plugin at session start. Both events redact or omit third-party plugin and marketplace names unless you set `OTEL_LOG_TOOL_DETAILS=1`, as [Redacted plugin names in your backend](plugins-measure.md#redacted-plugin-names-in-your-backend) shows. Field lists are under [Plugin installed event](../13-Enterprise-Admin/monitoring-usage.md#plugin-installed-event) and [Plugin loaded event](../13-Enterprise-Admin/monitoring-usage.md#plugin-loaded-event).


[​](#analytics-api)

Analytics API

On the Enterprise plan, `GET /v1/organizations/analytics/plugins` returns per-plugin, per-day install and invocation counts across Claude Code and Cowork. You can group the counts by user or RBAC group. Plugin activity that reaches Anthropic without a plugin name appears in one aggregate `third-party` row. See the [endpoint reference](../04-API-Reference/Admin/platform-claude-com-http-beta-organization-analytics-plugins-list.md) and [Access data programmatically](../13-Enterprise-Admin/analytics.md#access-data-programmatically) for the key it needs.


[​](#plan-for-what-managed-settings-can’t-enforce)

Plan for what managed settings can’t enforce

These requests from security reviews have no dedicated key in the current settings schema. The nearest existing controls are:

- **Per-user or per-group targeting**: every plugin key applies to every user who receives the settings. Server-managed settings deliver one configuration per organization. For per-group policy, use separate endpoint-managed settings or gateway policies, as under [Assign release channels to user groups](#assign-release-channels-to-user-groups).
- **Restricting entries inside an allowed marketplace**: the allowlist matches marketplace sources. To block one plugin from an allowed marketplace, set it to `false` in managed `enabledPlugins`.
- **Hiding `/plugin`**: no key disables the command. The nearest equivalent combines an allowlist naming only your marketplace, managed `enabledPlugins` entries for the plugins you supply, and `disableSideloadFlags`.
- **Gating `--plugin-dir` through the allowlist**: the allowlist doesn’t cover `--plugin-dir`. `disableSideloadFlags` does.
- **Enforcing the claude.ai plugin toggles through these keys**: [**Organization settings \> Plugins & skills**](https://claude.ai/admin-settings/skills?tab=inventory) doesn’t set the keys on this page. What members and your organization turn on there reaches the CLI as [synced plugins](plugins-loading.md#synced-plugins), which have their own controls.


[​](#troubleshoot-policy)

Troubleshoot policy

If plugin policy doesn’t behave as expected on a machine, check for these symptoms first:

- **The managed file didn’t parse**: when a `managed-settings.json` isn’t valid JSON, Claude Code refuses to start and prints [an error naming the file](../02-Claude-Code-CLI/errors.md#managed-settings-document-could-not-be-parsed). A file that parses but has one invalid entry keeps the rest of its policy. See [Invalid entries in managed settings](../13-Enterprise-Admin/managed-settings.md#invalid-entries-in-managed-settings).
- **The managed source didn’t load**: run `/status` and look for `Enterprise managed settings` in the `Setting sources` line. If it’s missing, the source didn’t load.
- **A user reports `blocked by enterprise policy`**: the message names the marketplace or its source. For an allowlist, it also lists the allowed sources. The user-facing entries are on [Troubleshoot plugins](plugins-troubleshooting.md).
- **A plugin the user disabled in `~/.claude/settings.json` still loads**: another settings source re-enabled it, such as a managed `enabledPlugins` entry that force-enables it. `/plugin` and `claude plugin list` show `Disabled in ~/.claude/settings.json but still loads` with that settings source.


[​](#next-steps)

Next steps

- [Marketplace reference](plugins-marketplace-reference.md#marketplace-sources): the `source` values `extraKnownMarketplaces`, `strictKnownMarketplaces`, and `blockedMarketplaces` accept
- [Host and maintain a marketplace](plugins-host-marketplace.md): run the marketplace your policy points at
- [Plugin security and trust](plugins-security.md): what a plugin can do on a machine and how to review one before installing
- [Manage mods for your organization](plugins-mods-admin.md): turn off or limit mods, the plugins that run JavaScript inside Claude Code
- [Server-managed settings](../13-Enterprise-Admin/server-managed-settings.md): deliver these keys from the claude.ai admin console
- [Troubleshoot plugins](plugins-troubleshooting.md#blocked-by-your-organization): the messages users see when policy blocks them

---

## Playwright | Claude by Anthropic

Source: https://www.claude.com/plugins/playwright
Path: 08-Plugins-Skills/plugins-playwright.md

# Playwright

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

- 

  Install in
  Claude Code
- 

  Made by[Microsoft](https://microsoft.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/external_plugins/playwright)
- 

  Installs319,887

Playwright MCP enables Claude to automate browser interactions through structured accessibility data rather than screenshots. This provides fast, lightweight browser automation without requiring vision models—Claude works directly with the page's accessibility tree for deterministic, reliable interactions.

Key capabilities include navigating to URLs, clicking elements, filling forms, handling file uploads, managing browser dialogs, taking screenshots, generating PDFs, and running custom Playwright scripts. The server also provides tab management, network request inspection, console message retrieval, and comprehensive test assertion tools for end-to-end testing workflows.

**How to use:** Ask Claude to perform browser tasks naturally. Try prompts like "Navigate to example.com and take a screenshot", "Fill out the login form with username test@email.com", "Click the Submit button and wait for the confirmation message", or "Run an end-to-end test that verifies the checkout flow works correctly". Claude will use the appropriate browser automation tools to complete these tasks.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Publish and distribute a plugin - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/publish
Path: 08-Plugins-Skills/plugins-publish.md

## On this page

- [Choose how to distribute](#choose-how-to-distribute)
- [Prepare your plugin for release](#prepare-your-plugin-for-release)
- [Share a plugin without a marketplace](#share-a-plugin-without-a-marketplace)
  - [Ship a plugin with your own tool](#ship-a-plugin-with-your-own-tool)
- [Publish through your own marketplace](#publish-through-your-own-marketplace)
  - [Add the marketplace file to your repository](#add-the-marketplace-file-to-your-repository)
  - [Control who can install](#control-who-can-install)
  - [Tell users how to install](#tell-users-how-to-install)
  - [Ship updates to users](#ship-updates-to-users)
- [Submit to Anthropic’s directory](#submit-to-anthropics-directory)
  - [How a listed plugin reaches Claude Code users](#how-a-listed-plugin-reaches-claude-code-users)
- [Ship updates, renames, and removals](#ship-updates-renames-and-removals)
  - [Release a new version](#release-a-new-version)
  - [Tag a release](#tag-a-release)
  - [Rename or remove a plugin](#rename-or-remove-a-plugin)
- [Declare dependencies](#declare-dependencies)
- [Next steps](#next-steps)

Create plugins

# Publish and distribute a plugin

Copy pageCopy page

Publish a Claude Code plugin through your own marketplace or Anthropic’s directory, with a pre-release checklist and how users get updates.

Copy pageCopy page

Publishing a Claude Code plugin means listing it in a marketplace, a JSON catalog that lists plugins and where to fetch each one, so that other people can install it by name and receive your updates. You can run your own marketplace or submit your plugin to Anthropic’s directory. To share a plugin without publishing it, send people the plugin’s directory or a `.zip` of it to load themselves. This page is for the author of a working plugin who is ready to share it.

These cases are covered on other pages:

- **Your plugin isn’t finished yet**: start with [Create a plugin](plugins-create.md)
- **You maintain a CLI or SDK with a plugin in an official marketplace**: see [Recommend your plugin from your CLI](plugins-cli-hints.md)

Start with [Choose how to distribute](#choose-how-to-distribute) to compare the distribution options. If you already know your route, go to [Prepare your plugin for release](#prepare-your-plugin-for-release), then follow your route’s section for what to tell your users and how they receive your updates.


[​](#choose-how-to-distribute)

Choose how to distribute

Choose a distribution option based on who needs to install the plugin:

| Route                                                         | Who can install                                                                                                                                          | What you need                                                                                  | Do users get your updates automatically?     |
|:--------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------|:---------------------------------------------|
| [No marketplace](#share-a-plugin-without-a-marketplace)       | The people you send the plugin folder or a `.zip` of it                                                                                                  | The plugin’s folder                                                                            | None. They load the copy you sent            |
| [Your own marketplace](#publish-through-your-own-marketplace) | Anyone who can reach the repository, which can be a private one your team can clone                                                                      | A git repository or other host with a `.claude-plugin/marketplace.json` that lists your plugin | Off                                          |
| [Anthropic’s directory](#submit-to-anthropics-directory)      | People who add it on claude.ai or in Cowork. It also loads in their Claude Code sessions through [account sync](plugins-loading.md#synced-plugins) | A GitHub repository holding the plugin and a paid claude.ai plan to submit from                | Yes, after the version you push is published |

Auto-update is a per-marketplace setting on the user’s side that fetches new versions in the background.


[​](#prepare-your-plugin-for-release)

Prepare your plugin for release

The name, the version, validation, and an install from a marketplace decide whether a release works for the people who install it. Check them before the first release and again before each later one.

1

Choose a permanent name

Users install, enable, and configure your plugin by `name@marketplace`, so a renamed plugin is a different plugin to every existing install. Choose a kebab-case name such as `deploy-helper`, because `claude plugin validate` warns on other forms, and treat it as permanent. Set `displayName` in `plugin.json` for the label users see.

2

Decide how you'll version

If you set `version` in `plugin.json` and later push commits without changing it, `claude plugin update` prints `<name> is already at the latest version (1.0.0).` and users keep the old copy. Either increment `version` on every release, or omit it in a git-hosted marketplace so Claude Code uses the commit SHA instead. See [Versions and updates](plugins-loading.md#versions-and-updates).

3

Validate

In your shell, run `claude plugin validate --strict ./your-plugin`. A clean run prints `✔ Validation passed`.

- **In CI**: keep `--strict`, which also fails the run with exit code 1 on warnings such as an unknown manifest field or a missing `version`. Drop `--strict` if you chose to omit `version` in the previous step.
- **Paths**: validation reports component paths that don’t start with `./`. Inside hook commands and MCP server configs, refer to files as `${CLAUDE_PLUGIN_ROOT}/...`. See [path rules](plugins-reference.md#path-rules).

4

Install it from a local marketplace

In your shell, add a local marketplace that lists the plugin with `claude plugin marketplace add ./path-to-marketplace`, install the plugin from it, and start a session to confirm it loads.

- For the smallest marketplace that works, see [Create a marketplace](plugin-marketplaces.md).
- To know whether an install loads your source directory or a cached copy, see [In-place and copied plugins](plugins-loading.md#in-place-and-copied-plugins).

5

Fill in the metadata users see

Set `description`, `author`, `homepage`, and `repository` in `plugin.json`, and add a `README.md` at the plugin root. `homepage` must parse as a URL. The [manifest reference](plugins-reference.md#fields) lists every field.

6

Run your eval suite

If you have an eval suite, run `claude plugin eval` in your shell. It runs the plugin’s test cases and scores the results, which catches regressions when you change the plugin. See [Test plugins with evals](plugin-evals.md).


[​](#share-a-plugin-without-a-marketplace)

Share a plugin without a marketplace

If the plugin is in a git repository, people can clone it and load the checkout, or start Claude Code from their shell with `--plugin-url` pointed at a `.zip` you attach to a release. To get your next version they pull or download again. If it isn’t in a repository, send them the directory or a `.zip` of it. They load it in one of two ways:

- **For one session**: they start Claude Code from their shell with `claude --plugin-dir ./deploy-helper`, where the path is the clone, the unzipped folder, or the `.zip` itself. See [Flags that load a plugin for one session](plugins-cli-reference.md#flags-that-load-a-plugin-for-one-session).
- **For every session**: they move the plugin directory, with its `.claude-plugin/plugin.json`, under `~/.claude/skills/`. From then on Claude Code loads it at startup, unless their administrator has [blocked skills-directory plugins](plugins-loading.md#skills-directory-plugins-blocked-by-managed-settings).

Adding a `.claude-plugin/marketplace.json` to that same repository is what lets people install by name and update with a command; see [Publish through your own marketplace](#publish-through-your-own-marketplace).


[​](#ship-a-plugin-with-your-own-tool)

Ship a plugin with your own tool

If you maintain a CLI or SDK, publish the plugin in a marketplace and have your installer or post-install message run or print the two commands a user needs: `claude plugin marketplace add <source>`, then `claude plugin install <name>@<marketplace>`. For in-session discovery when someone uses your tool, see [Recommend your plugin from your CLI](plugins-cli-hints.md).


[​](#publish-through-your-own-marketplace)

Publish through your own marketplace

Your own marketplace is a `.claude-plugin/marketplace.json` file that lists your plugin, added to a git repository. Once the file is in the repository, the plugin is published, with no submission form. You can keep the file in the plugin’s own repository or in a separate one.


[​](#add-the-marketplace-file-to-your-repository)

Add the marketplace file to your repository

To publish from the plugin’s own repository, save the marketplace file beside `plugin.json` in `.claude-plugin/`, with one entry whose `source` is `"./"`, the repository root. Give the entry the same `name` as `plugin.json`, per [Keep the entry name and the manifest name the same](plugin-marketplaces.md#keep-the-entry-name-and-the-manifest-name-the-same):

.claude-plugin/marketplace.json

```python
{
  "name": "your-marketplace",
  "owner": { "name": "Your Name" },
  "plugins": [
    { "name": "deploy-helper", "source": "./" }
  ]
}
```

In your shell, run `claude plugin validate .` in the repository before you push. For what the run checks, see [Validate a directory](plugins-cli-reference.md#validate-a-directory). [Create a marketplace](plugin-marketplaces.md) covers the layout with several plugins in one repository.


[​](#control-who-can-install)

Control who can install

Anyone who can clone the repository can install from it, so if the repository is private, the marketplace is private too. For hosts other than a git repository, see [Host a marketplace](plugins-host-marketplace.md). To reach everyone at a company, including people who don’t use git, see [Roll out to a whole company](plugins-host-marketplace.md#roll-out-to-a-whole-company).


[​](#tell-users-how-to-install)

Tell users how to install

Tell your users to add the marketplace and then install the plugin from their shell, replacing the source and names with yours:

- Add the marketplace once: `claude plugin marketplace add your-org/your-marketplace`, where the argument is a GitHub `owner/repo` shorthand, a URL, or a path
- Install the plugin: `claude plugin install deploy-helper@your-marketplace`
- Or do both from inside a session: `/plugin install deploy-helper --marketplace your-org/your-marketplace`. Requires Claude Code v2.1.275 or later. See [Add a marketplace and install in one command](discover-plugins.md#add-a-marketplace-and-install-in-one-command)
- Or do both from the shell in one command: `claude plugin install deploy-helper --marketplace your-org/your-marketplace`. Requires Claude Code v2.1.292 or later


[​](#ship-updates-to-users)

Ship updates to users

Users receive a release when they ask for it or when auto-update is on for your marketplace:

- **On request**: `claude plugin update deploy-helper@your-marketplace` in the user’s shell refreshes the marketplace and installs the new copy when your plugin’s version has changed
- **Auto-update**: off by default for your marketplace. See [Turn on auto-update](plugins-host-marketplace.md#turn-on-auto-update). Once on, it does the same as `claude plugin update` on a delay after the session starts

[Install plugins](discover-plugins.md) covers the user-side commands, and [when auto-update runs](plugins-loading.md#when-auto-update-runs) covers the timing.


[​](#submit-to-anthropics-directory)

Submit to Anthropic’s directory

[Anthropic’s directory](https://claude.ai/directory) is the catalog people browse on claude.ai and in Cowork to add plugins and connectors. One listing there reaches people on claude.ai, in Cowork, and in Claude Code. You submit from the developer portal at [claude.ai/directory/manage](https://claude.ai/directory/manage), and [Submit a plugin](https://claude.com/docs/plugins/submit#submit-a-plugin) on claude.com walks through the portal. Anthropic’s official marketplace, `claude-plugins-official`, doesn’t take submissions through the directory portal. If you work with an Anthropic partner contact, ask them about an official-marketplace listing. To submit a plugin:

1

Confirm you can submit

Submitting requires a paid claude.ai plan. On Pro and Max you submit from your own account. On Team and Enterprise, an Owner can submit, and on Enterprise an Owner can also grant the **Directory** permission to other members through a custom role under **Organization settings \> Roles**. See [Confirm you can submit to the directory](https://claude.com/docs/directory/publish#confirm-you-can-submit-to-the-directory).

2

Validate the plugin locally

Run `claude plugin validate ./your-plugin --strict` in your shell. Replace `./your-plugin` with the path to your plugin directory. The command catches manifest errors locally; [plugin validate](plugins-cli-reference.md#plugin-validate) lists which files each run reads. The portal applies additional directory rules that the CLI doesn’t check, so a clean local run doesn’t guarantee a clean portal validation.The [plugin pre-submission checklist](https://claude.com/docs/plugins/pre-submission-checklist#run-the-checks-before-you-submit) on claude.com has the checks to run and fix before you submit.

3

Check what loads where

Some plugin components are Claude Code-only and don’t load on claude.ai or in Cowork. The [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app) lists each component by app, so you know what users outside Claude Code will get.

4

Submit in the developer portal

Open the developer portal at [claude.ai/directory/manage](https://claude.ai/directory/manage) and follow [Submit a plugin](https://claude.com/docs/plugins/submit#submit-a-plugin) on claude.com.

The rest of the process is documented on claude.com:

- [Prepare for review](https://claude.com/docs/directory/publish#prepare-for-review): what happens to each version before it’s published
- [Update a published plugin](https://claude.com/docs/plugins/submit#update-a-published-plugin): how a new version reaches the people who have your plugin
- [Submit your plugin, and your MCP server as a connector](https://claude.com/docs/directory/publish#submit-your-plugin-and-your-mcp-server-as-a-connector): what you can submit
- [Move an earlier submission to the developer portal](https://claude.com/docs/directory/publish#move-an-earlier-submission-to-the-developer-portal): what to do if you submitted a plugin through one of the earlier submission forms, before the portal existed


[​](#how-a-listed-plugin-reaches-claude-code-users)

How a listed plugin reaches Claude Code users

A person who installs your plugin from the directory on claude.ai has it on their account, and Claude Code loads it as `<name>@synced`. [Plugins synced from claude.ai](plugins-loading.md#synced-plugins) covers what they see and how they turn it off.


[​](#ship-updates-renames-and-removals)

Ship updates, renames, and removals


[​](#release-a-new-version)

Release a new version

If you publish through your own marketplace and your `plugin.json` sets `version`, increment it and push. Users who run `claude plugin update` or have auto-update on then receive the new version, as described under [Ship updates to users](#ship-updates-to-users). For a directory listing, see [Update a published plugin](https://claude.com/docs/plugins/submit#update-a-published-plugin).


[​](#tag-a-release)

Tag a release

Tag the release in git when other plugins declare a version range on yours, because those ranges resolve against tags. Otherwise you don’t need a tag. To tag, run `claude plugin tag` in your shell from the plugin directory. It creates a `{name}--v{version}` tag. Add `--push` to send the tag to `origin`. The [`plugin tag` reference](plugins-cli-reference.md#plugin-tag) lists its flags.


[​](#rename-or-remove-a-plugin)

Rename or remove a plugin

Never change a published plugin’s `name`. After a rename, users who already installed it lose the plugin, because their install is recorded under the old name. A `renames` entry in your marketplace file migrates them instead. Change `displayName` when you want a different label. If a rename is unavoidable, use the marketplace file’s `renames` map so that existing installs migrate instead of failing with [`Plugin "<name>" not found in marketplace`](plugins-troubleshooting.md#plugin-not-found-in-marketplace). To remove a plugin from the marketplace, or for the full `renames` details, see [Rename or remove a plugin](plugins-host-marketplace.md#rename-or-remove-a-plugin) on the hosting page. The [marketplace reference](plugins-marketplace-reference.md#top-level-fields) has the field.


[​](#declare-dependencies)

Declare dependencies

If your plugin needs another plugin from the same marketplace to be enabled, list it in the `dependencies` array of `plugin.json`. Each entry is a bare name or an object with a semver `version` range. When a user installs your plugin, Claude Code installs and enables the dependency too. [Plugin dependencies](plugins-dependencies.md) covers the range syntax, cross-marketplace dependencies, and how users prune dependencies they no longer need.


[​](#next-steps)

Next steps

- [Host and maintain a marketplace](plugins-host-marketplace.md): release new versions and keep users up to date
- [Plugin dependencies](plugins-dependencies.md): declare and version the plugins yours relies on
- [Recommend your plugin from your CLI](plugins-cli-hints.md): prompt Claude Code users of your CLI to install the plugin
- [Measure plugin cost and usage](plugins-measure.md): see what your plugin costs in context and whether people use it

---

## Ralph Loop | Claude by Anthropic

Source: https://www.claude.com/plugins/ralph-loop
Path: 08-Plugins-Skills/plugins-ralph-loop.md

# Ralph Loop

Anthropic verified

Interactive AI loops for iterative development using the Ralph Wiggum technique: Claude works on tasks repeatedly, seeing prior work until completion.

- 

  Install in
  Claude Code
- 

  Made by[Anthropic](https://anthropic.com)
- 

  Plugin source[View source](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/ralph-loop)
- 

  Installs196,527

Ralph Loop enables iterative, self-referential AI development loops where Claude works on the same task repeatedly until completion. The plugin intercepts session exits via a stop hook and automatically re-feeds your prompt while preserving all file modifications and git history between iterations. This creates autonomous improvement cycles where Claude can refine its work based on test failures and previous attempts.

The technique is ideal for well-defined projects with clear success criteria, greenfield development with automated verification, and any task amenable to iterative refinement. Claude reads its own past work to inform improvements, creating a powerful feedback loop for autonomous problem-solving.

**How to use:** Start an iterative session with `/ralph-loop "your prompt here" --max-iterations 10 --completion-promise "DONE"`. The loop continues until Claude outputs the completion promise or reaches the iteration limit. Cancel anytime with `/cancel-ralph`. Write prompts with explicit completion criteria and success metrics for best results.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Plugin manifest reference - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/manifest-reference
Path: 08-Plugins-Skills/plugins-reference.md

## On this page

- [Manifest file](#manifest-file)
  - [Unrecognized fields](#unrecognized-fields)
  - [Validate the manifest](#validate-the-manifest)
- [Fields](#fields)
  - [name](#name)
  - [displayName](#displayname)
  - [version](#version)
  - [metadata](#metadata)
  - [Directory listing fields](#directory-listing-fields)
  - [defaultEnabled](#defaultenabled)
  - [dependencies](#dependencies)
  - [settings](#settings)
- [Component path forms](#component-path-forms)
  - [Path-only fields](#path-only-fields)
  - [commands](#commands)
  - [hooks](#hooks)
  - [mcpServers](#mcpservers)
  - [lspServers](#lspservers)
  - [monitors](#monitors)
- [Path rules](#path-rules)
  - [Containment and existence](#containment-and-existence)
  - [How each key combines with its default location](#how-each-key-combines-with-its-default-location)
- [User configuration](#user-configuration)
  - [Limit a field to fixed options](#limit-a-field-to-fixed-options)
  - [Where values are stored](#where-values-are-stored)
  - [Reference a saved value](#reference-a-saved-value)
  - [Fields that run through a shell](#fields-that-run-through-a-shell)
- [Channels](#channels)
- [Environment variables](#environment-variables)
  - [Where each variable resolves](#where-each-variable-resolves)
  - [Quoting and path separators](#quoting-and-path-separators)
- [Standard layout](#standard-layout)
- [Marketplace entries and the manifest](#marketplace-entries-and-the-manifest)
  - [How entry fields combine with plugin.json](#how-entry-fields-combine-with-plugin-json)
  - [Metadata precedence](#metadata-precedence)
- [Next steps](#next-steps)

Reference

# Plugin manifest reference

Copy pageCopy page

Complete reference for plugin.json: every field with its type and default, accepted path forms, and the userConfig and environment variable schemas.

Copy pageCopy page

A plugin manifest is the `plugin.json` file in a plugin’s `.claude-plugin/` directory. It carries the plugin’s metadata and the [`userConfig`](#user-configuration) values that Claude Code prompts the user for. It also declares any component that you define inline or keep outside its [default location](#standard-layout). This reference is for plugin creators, and for marketplace owners who put component fields in a marketplace entry.

These cases are covered on other pages:

- **Learning to build a plugin**: start with [Create a plugin](plugins-create.md)
- **What each component does at runtime**: see [Plugin components](plugins-components.md)

Start at the section that matches what you’re looking up:

- A field: the [Fields table](#fields) gives each field’s type, whether it’s required, its default, and what it accepts. [Path rules](#path-rules) covers the `./` prefix and containment for every component path
- A `userConfig` option or a `channels` entry: the [User configuration](#user-configuration) and [Channels](#channels) schemas
- `${CLAUDE_PLUGIN_ROOT}` or another variable a plugin can reference: [Environment variables](#environment-variables)
- Where each component’s files go: [Standard layout](#standard-layout)
- A message from `claude plugin validate`: the [troubleshooting page](plugins-troubleshooting.md) lists each message with its fix and links to the relevant sections on this page


[​](#manifest-file)

Manifest file

The manifest is optional. Without it, Claude Code loads the components it finds in the [standard layout](#standard-layout). The plugin name then comes from the marketplace entry, or from the directory name when you load the plugin with `--plugin-dir`. Write a manifest when you want metadata, a component outside its default directory, `userConfig`, or an inline component definition. Save the manifest at `.claude-plugin/plugin.json` under the plugin root. Put every other plugin file at the plugin root, not inside `.claude-plugin/`. That includes `skills/`, `commands/`, and `hooks/`. The following example sets most of the keys in the [Fields table](#fields). It passes validation in a plugin directory that contains each referenced path.

```python
{
  "name": "deploy-tools",
  "displayName": "Deploy Tools",
  "version": "1.2.0",
  "description": "Deployment commands, a review agent, and a status monitor",
  "author": {
    "name": "Example Team",
    "email": "dev@example.com",
    "url": "https://example.com"
  },
  "homepage": "https://example.com/docs/deploy-tools",
  "repository": "https://github.com/example/deploy-tools",
  "license": "MIT",
  "keywords": ["deployment", "ci"],
  "defaultEnabled": true,
  "dependencies": ["secrets-vault"],
  "metadata": { "catalogId": "cat-123" },
  "skills": ["./extra-skills/"],
  "commands": {
    "status": {
      "source": "./commands/status.md",
      "description": "Show the current deployment status"
    },
    "about": {
      "content": "Explain what the deploy-tools plugin provides.",
      "description": "Describe this plugin"
    }
  },
  "agents": ["./agents/reviewer.md"],
  "hooks": "./config/extra-hooks.json",
  "mcpServers": {
    "deploy-api": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
    }
  },
  "lspServers": "./.lsp.json",
  "outputStyles": "./styles/",
  "experimental": {
    "themes": "./themes/",
    "monitors": "./config/monitors.json"
  },
  "userConfig": {
    "api_token": {
      "type": "string",
      "title": "API token",
      "description": "Token for the deployment API",
      "sensitive": true
    }
  }
}
```


[​](#unrecognized-fields)

Unrecognized fields

An unrecognized top-level key is stripped, and an unrecognized key inside a `userConfig` option, `channels` entry, `lspServers` config, or `monitors` entry is rejected:

- **Top-level fields**: the field is stripped and the plugin loads. `claude plugin validate` reports each unrecognized top-level field as a warning
- **Strict objects**: `userConfig` options, `channels` entries, `lspServers` configs, and `monitors` entries are strict. An unknown key inside one is an error, and the plugin doesn’t load


[​](#validate-the-manifest)

Validate the manifest

`claude plugin validate` is the authoritative check for a manifest. Run it from your shell against the plugin directory:

```python
claude plugin validate ./my-plugin
```

The command reports one of these results:

- **`Validation passed`**: the manifest loads
- **`Validation passed with warnings`**: the manifest loads, but the validator found something to fix, such as an unknown top-level field that Claude Code strips, a `name` that isn’t kebab-case, or a missing `version`, `description`, or `author`. Pass `--strict` to turn warnings into failures in CI
- **`Validation failed`**: the manifest has a type mismatch, a path that is missing or escapes the plugin root, or an unknown key inside a `userConfig` option, `channels` entry, `lspServers` config, or `monitors` entry. Claude Code reports the same problem when it loads the plugin

The command also checks each MCP server entry the plugin declares in `.mcp.json`, in a `.json` file that [`mcpServers`](#mcpservers) names, or inline in `plugin.json`. These MCP checks require Claude Code v2.1.281 or later and include:

- **Errors**: an entry Claude Code would drop when it loads the plugin, a `${user_config.KEY}` reference to an option the manifest doesn’t declare, and a remote `url` that isn’t a valid absolute URL
- **Warnings**: an `http://` or `ws://` URL to a non-loopback host, and a header value that looks like a literal credential


[​](#fields)

Fields

The table lists the top-level keys in `plugin.json`. `name` is the only required key. Where a field name is a link, the linked section has its full rules. For component keys such as `commands` and `hooks`, [Component path forms](#component-path-forms) shows each accepted shape with an example, and every path follows the [path rules](#path-rules) for the `./` prefix, extensions, and containment.

| Field                                            | Type                             | Description                                                                                                                                                                                                                                                                                                      |
|:-------------------------------------------------|:---------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$schema`                                        | String                           | JSON Schema URL for editor autocomplete. Claude Code ignores it at load time                                                                                                                                                                                                                                     |
| [`name`](#name)                                  | String                           | Plugin identifier, required. Use kebab-case. Every component is namespaced under it                                                                                                                                                                                                                              |
| [`displayName`](#displayname)                    | String                           | Name shown in UI in place of `name`                                                                                                                                                                                                                                                                              |
| [`version`](#version)                            | String                           | Version string. Setting it keeps users on that version until you change it                                                                                                                                                                                                                                       |
| `description`                                    | String                           | Short explanation of what the plugin provides                                                                                                                                                                                                                                                                    |
| `author`                                         | Object                           | `name`, which is required, plus optional `email` and `url`                                                                                                                                                                                                                                                       |
| `homepage`                                       | String                           | Documentation URL. Must parse as a URL, or the plugin fails to load                                                                                                                                                                                                                                              |
| `repository`                                     | String                           | Source repository URL. Not validated                                                                                                                                                                                                                                                                             |
| `license`                                        | String                           | SPDX identifier such as `MIT` or `Apache-2.0`                                                                                                                                                                                                                                                                    |
| `keywords`                                       | Array of strings                 | Discovery tags                                                                                                                                                                                                                                                                                                   |
| [`metadata`](#metadata)                          | Object                           | Free-form object for your own data. Claude Code doesn’t read it                                                                                                                                                                                                                                                  |
| [`icon`](#directory-listing-fields)              | String                           | Icon for the plugin’s listing in Anthropic’s directory. Claude Code doesn’t read it                                                                                                                                                                                                                              |
| [`documentationUrl`](#directory-listing-fields)  | String                           | Documentation link for the plugin’s listing in Anthropic’s directory. Claude Code doesn’t read it                                                                                                                                                                                                                |
| [`supportUrl`](#directory-listing-fields)        | String                           | Support link for the plugin’s listing in Anthropic’s directory. Claude Code doesn’t read it                                                                                                                                                                                                                      |
| [`privacyPolicyUrl`](#directory-listing-fields)  | String                           | Privacy policy link for the plugin’s listing in Anthropic’s directory. Claude Code doesn’t read it                                                                                                                                                                                                               |
| [`termsOfServiceUrl`](#directory-listing-fields) | String                           | Terms of service link for the plugin’s listing in Anthropic’s directory. Claude Code doesn’t read it                                                                                                                                                                                                             |
| [`defaultEnabled`](#defaultenabled)              | Boolean                          | Whether the plugin starts enabled when the user hasn’t set it. Defaults to `true`                                                                                                                                                                                                                                |
| [`dependencies`](#dependencies)                  | Array of strings or objects      | Plugins that must be enabled for this one to work                                                                                                                                                                                                                                                                |
| [`settings`](#settings)                          | Object                           | Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect                                                                                                                                                                                                      |
| [`userConfig`](#user-configuration)              | Object                           | Values Claude Code prompts the user for when the plugin is enabled                                                                                                                                                                                                                                               |
| `types`                                          | Path                             | A `.d.ts` file that declares the `$.state` values and `$` nouns of a [mod](plugins-mods-reference.md#files)                                                                                                                                                                                                |
| [`channels`](#channels)                          | Array of objects                 | Message channels the plugin provides, each bound to one of its MCP servers                                                                                                                                                                                                                                       |
| `skills`                                         | Path, or array of paths          | Directories to scan for skills, each a directory of `<name>/SKILL.md` folders or one folder holding `SKILL.md` directly. `"."` names the plugin root. Adds to the default `skills/` scan                                                                                                                         |
| [`commands`](#commands)                          | Path, array of paths, or object  | Flat `.md` command files, directories of them, or an object map of command name to `source` or `content`. Replaces the default `commands/` scan                                                                                                                                                                  |
| `agents`                                         | Path, or array of paths          | Agent `.md` files. Directories aren’t accepted. Replaces the default `agents/` scan                                                                                                                                                                                                                              |
| [`hooks`](#hooks)                                | Path, object, or array of either | `.json` hook files or inline hook config. Loaded together with `hooks/hooks.json`                                                                                                                                                                                                                                |
| [`mcpServers`](#mcpservers)                      | Path, object, or array of either | `.json` MCP config files, `.mcpb` or `.dxt` bundles, or inline server configs keyed by name. Loaded together with `.mcp.json`; a server name declared later replaces an earlier one                                                                                                                              |
| [`lspServers`](#lspservers)                      | Path, object, or array of either | `.json` LSP config files or inline server configs keyed by name. Loaded together with `.lsp.json`                                                                                                                                                                                                                |
| `outputStyles`                                   | Path, or array of paths          | Output style files or directories. Replaces the default `output-styles/` scan                                                                                                                                                                                                                                    |
| `workflows`                                      | Path, or array of paths          | [Workflow](../02-Claude-Code-CLI/workflows.md#distribute-a-workflow-in-a-plugin) `.js` files or directories. Replaces the default `workflows/` scan                                                                                                                                                                              |
| `experimental`                                   | Object                           | Container for `themes`, `monitors`, and `evals`, whose manifest shape may still change                                                                                                                                                                                                                           |
| `experimental.themes`                            | Path, or array of paths          | Theme files or directories. Replaces the default `themes/` scan. A top-level `themes` key still loads, with a `claude plugin validate` warning                                                                                                                                                                   |
| [`experimental.monitors`](#monitors)             | Path, or inline array            | A `.json` file holding the monitors array, or the array itself. Defaults to `monitors/monitors.json`. A top-level `monitors` key still loads, with a `claude plugin validate` warning. Monitors run only in interactive sessions, and not on Amazon Bedrock, Google Cloud’s Agent Platform, or Microsoft Foundry |
| `experimental.evals`                             | Path, or array of paths          | Directory that holds the plugin’s [eval cases](plugin-evals.md#use-a-different-eval-directory) when it isn’t the default `evals/`. `claude plugin eval --eval-dir` overrides it                                                                                                                            |

In the Type column, a path is a string relative to the plugin root, such as `"./custom/commands"`.


[​](#name)

`name`

The plugin identifier. It must be non-empty, with no spaces, `@`, `:`, path separators, control characters, or bidirectional-formatting characters; use kebab-case. Claude Code namespaces every component under it, so an agent `reviewer` in plugin `deploy-tools` appears as `deploy-tools:reviewer`. `claude plugin validate` also checks that the name doesn’t pass as one of Anthropic’s own plugins. The check ignores case and treats any run of separators as one:

| Name                                                                                               | Result  |
|:---------------------------------------------------------------------------------------------------|:--------|
| Starts with `claude-`, `anthropic-`, `anthropics-`, or `cc-plugin-`                                | Error   |
| Is `claude`, `anthropic`, `anthropics`, `claude-code`, or `claude-mods`                            | Error   |
| Puts `official` beside `claude` or `anthropic`, such as `official-claude-tools`                    | Error   |
| Has `claude`, `anthropic`, or `anthropics` as a whole word anywhere else, such as `mcp-for-claude` | Warning |

The error reads `Plugin name "<name>" is reserved: it passes as one of Anthropic's own`, and the warning reads `Plugin name "<name>" reads as one of Anthropic's own`. `claude plugin init` and `claude plugin tag` refuse a name that draws the error. Claude Code still installs and loads a plugin whose name they refuse.


[​](#displayname)

`displayName`

The name shown in UI in place of `name`. It may contain spaces and any casing, and it isn’t used for namespacing or lookup. For a marketplace-installed plugin, a `displayName` on the [marketplace entry](plugins-marketplace-reference.md#plugin-entries) takes precedence over this value.


[​](#version)

`version`

A version string, not checked against semver. Setting it pins the plugin to that version until you change it; see [Versions and updates](plugins-loading.md#versions-and-updates). A plugin with a [`command` source](plugins-marketplace-reference.md), a plugin from a [marketplace hosted on claude.ai](discover-plugins.md#add-from-claude-ai), and a plugin [loaded in place](plugins-loading.md#find-plugins-on-disk) from a marketplace added from a local path aren’t pinned by this field.


[​](#metadata)

`metadata`

A free-form object for your own data, such as catalog or entitlement fields. Claude Code doesn’t read it. Requires Claude Code v2.1.222 or later.


[​](#directory-listing-fields)

Directory listing fields

Anthropic’s directory reads the `icon`, `documentationUrl`, `supportUrl`, `privacyPolicyUrl`, and `termsOfServiceUrl` fields from `plugin.json` for your plugin’s listing when you [submit the plugin](plugins-publish.md#submit-to-anthropics-directory). Claude Code ignores them at load time. Set them only in `plugin.json`. In a [marketplace entry](#marketplace-entries-and-the-manifest), `claude plugin validate` reports each one as an unknown field. Set `icon` to the path of an image file inside the plugin, such as `./logo.png`, and each of the four URL fields to an `https://` URL.

`claude plugin validate` doesn’t report these fields in `plugin.json` as unknown on Claude Code v2.1.281 or later. Earlier versions print an `Unknown field` warning for each one, so a `--strict` run fails on those versions.


[​](#defaultenabled)

`defaultEnabled`

Whether the plugin starts enabled when the user hasn’t set it in [`enabledPlugins`](../02-Claude-Code-CLI/settings-reference.md#enabledplugins). Defaults to `true`. A plugin that an enabled plugin depends on starts enabled regardless. The same field in the marketplace entry overrides this one. Once a user’s `enabledPlugins` entry is written, it persists across plugin updates, so changing `defaultEnabled` in a later release doesn’t change the setting for an existing user.


[​](#dependencies)

`dependencies`

Plugins that must be enabled for this one to work. Each entry is `"name"`, `"name@marketplace"`, or `{ "name": "...", "marketplace": "...", "version": "..." }`. Bare names resolve against this plugin’s own marketplace. See [dependency constraints](plugins-dependencies.md).


[​](#settings)

`settings`

Settings Claude Code applies while the plugin is enabled. Only `agent` and `subagentStatusLine` take effect; other keys are dropped at load. A `settings.json` at the plugin root takes precedence over this key. See [Default settings](plugins-components.md#default-settings).


[​](#component-path-forms)

Component path forms

Every component key accepts a path relative to the plugin root. `hooks`, `mcpServers`, `lspServers`, and `experimental.monitors` also accept inline configuration, `commands` also accepts an object map, and `mcpServers` also accepts MCP bundle paths and URLs. The examples that follow show each accepted shape once. For what each component does at runtime, see [Plugin components](plugins-components.md).


[​](#path-only-fields)

Path-only fields

`agents`, `skills`, `outputStyles`, `workflows`, and `experimental.themes` take one path or an array of paths. `agents` entries must be `.md` files, and `skills` entries must be directories. The other three accept a directory or a file.

```python
{
  "agents": ["./custom-agents/reviewer.md", "./custom-agents/tester.md"],
  "skills": ["./extra-skills/", "."],
  "outputStyles": "./styles/"
}
```


[​](#commands)

`commands`

`commands` takes a path, an array of paths, or an object map. A path names a flat `.md` command file or a directory. In the object map, each key becomes the command name after the plugin prefix. For example, `"about"` in plugin `deploy-tools` runs as `/deploy-tools:about`. Each value sets exactly one of `source` or `content`, and an entry that sets both or neither fails validation. The other fields in this table are optional:

| Field          | Type             | Description                                                      |
|:---------------|:-----------------|:-----------------------------------------------------------------|
| `source`       | string           | Path to the command’s Markdown file, relative to the plugin root |
| `content`      | string           | Inline Markdown for the command body, instead of `source`        |
| `description`  | string           | Description shown for the command                                |
| `argumentHint` | string           | Argument hint shown after the command name, such as `[file]`     |
| `model`        | string           | Default model for the command                                    |
| `allowedTools` | array of strings | Tools the command may use without prompting                      |

This map declares one command from a file and one from inline content:

```python
{
  "commands": {
    "status": { "source": "./commands/status.md", "argumentHint": "[env]" },
    "about": { "content": "Explain what this plugin provides." }
  }
}
```


[​](#hooks)

`hooks`

`hooks` takes a `.json` file path, an inline hooks object in the same shape as [`hooks` in `settings.json`](../07-Hooks/hooks.md#configuration), or an array mixing both. For hook events and handler fields, see the [hooks reference](../07-Hooks/hooks.md#hook-events). A hooks file wraps the event map in a top-level `"hooks"` key, the shape [`hooks/hooks.json`](plugins-components.md#hooks) uses. A file that contains only the event map, without that wrapper, fails to load. An inline object is the event map itself, with no wrapper. Claude Code merges whatever you declare with `hooks/hooks.json` when that file exists. This array loads one hooks file and declares one inline `PostToolUse` hook:

```python
{
  "hooks": [
    "./config/extra-hooks.json",
    {
      "PostToolUse": [
        {
          "matcher": "Write|Edit",
          "hooks": [
            { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.sh" }
          ]
        }
      ]
    }
  ]
}
```

The file that array names carries the `"hooks"` wrapper around its own event map:

config/extra-hooks.json

```python
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/check-command.sh" }
        ]
      }
    ]
  }
}
```


[​](#mcpservers)

`mcpServers`

`mcpServers` takes a `.json` file path, an MCP bundle path or URL, an inline map, or an array mixing them. For server config fields, see [plugin-provided MCP servers](../06-MCP-Tools/General/mcp.md#plugin-provided-mcp-servers). Claude Code loads `.mcp.json` at the plugin root first, then each declared shape in order. A server name declared later replaces an earlier one. An `mcpServers` value takes one of these shapes:

| Shape             | Example value                                                                          | What Claude Code does                                                                                       |
|:------------------|:---------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------|
| `.json` file path | `"./mcp/servers.json"`                                                                 | Reads the file as an `mcpServers` map                                                                       |
| MCP bundle path   | `"./bundle.mcpb"`                                                                      | Extracts the `.mcpb` or `.dxt` bundle into `.mcpb-cache/` under the plugin root and reads its server config |
| MCP bundle URL    | `"https://example.com/server.mcpb"`                                                    | Downloads the bundle into `.mcpb-cache/`, then reads it                                                     |
| Inline map        | `{ "deploy-api": { "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"] } }` | Uses the map as server configs keyed by name                                                                |

A bundle path or URL must end in `.mcpb` or `.dxt`. Any other extension fails validation.


[​](#lspservers)

`lspServers`

`lspServers` takes a `.json` file path, an inline map of server name to config, or an array of either. Claude Code loads `.lsp.json` at the plugin root first, then each declared config in order. A server name declared later replaces an earlier one. Each server config is a strict object with these fields. An unknown key fails validation.

| Field                   | Required | Description                                                                                                                                                                                |
|:------------------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `command`               | Yes      | Language server binary. No spaces unless the value starts with `/`; put arguments in `args`                                                                                                |
| `extensionToLanguage`   | Yes      | Map of file extension to LSP language ID, at least one entry. Keys start with a dot, such as `".go"`                                                                                       |
| `args`                  | No       | Arguments passed to the server                                                                                                                                                             |
| `transport`             | No       | Communication transport: `stdio` (default) or `socket`. Claude Code accepts `socket` but runs every server over stdio, so the stdout protocol rules apply to all servers                   |
| `env`                   | No       | Environment variables for the server process                                                                                                                                               |
| `initializationOptions` | No       | Options sent in the initialize request                                                                                                                                                     |
| `settings`              | No       | Settings sent by `workspace/didChangeConfiguration`                                                                                                                                        |
| `workspaceFolder`       | No       | Workspace folder path for the server                                                                                                                                                       |
| `startupTimeout`        | No       | Milliseconds to wait for startup, a positive integer                                                                                                                                       |
| `shutdownTimeout`       | No       | Milliseconds to wait for a graceful shutdown, a positive integer. When the timeout elapses, Claude Code terminates the server process. When unset, no timeout applies                      |
| `requestTimeout`        | No       | Milliseconds to wait for the server to answer a request, a positive integer. Defaults to `60000`, so a request the server never answers fails after 60 seconds. Requires v2.1.288 or later |
| `restartOnCrash`        | No       | Whether to restart the server after it crashes. Defaults to `true`. Set to `false` to leave a crashed server stopped instead of restarting it                                              |
| `maxRestarts`           | No       | Restart attempts before giving up, zero or more                                                                                                                                            |
| `diagnostics`           | No       | Whether to push diagnostics into context after edits. Defaults to `true`                                                                                                                   |

This inline config runs `gopls` for `.go` files:

```python
{
  "lspServers": {
    "go": {
      "command": "gopls",
      "args": ["serve"],
      "extensionToLanguage": { ".go": "go" }
    }
  }
}
```

For the language servers Anthropic publishes as plugins and how the servers behave at runtime, see [Code intelligence](plugins-code-intelligence.md).


[​](#monitors)

`monitors`

`experimental.monitors` takes a `.json` file path or the inline array. When you omit the key, Claude Code loads `monitors/monitors.json` if it exists. Each entry is a strict object with these fields.

| Field         | Required | Description                                                                                                                                                        |
|:--------------|:---------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`        | Yes      | Identifier unique within the plugin                                                                                                                                |
| `command`     | Yes      | Shell command Claude Code runs as a persistent background process in the session working directory                                                                 |
| `description` | Yes      | Short summary shown in the task panel and notification summaries                                                                                                   |
| `when`        | No       | With `"always"`, the default, the monitor starts at session start and on plugin reload. With `"on-skill-invoke:<skill>"`, it starts the first time that skill runs |

This inline array declares one monitor that starts the first time the `deploy` skill runs:

```python
{
  "experimental": {
    "monitors": [
      {
        "name": "deploy-status",
        "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/poll-deploy.sh",
        "description": "Deployment status changes",
        "when": "on-skill-invoke:deploy"
      }
    ]
  }
}
```

A monitor `command` can’t reference `${user_config.*}`. See [Fields that run through a shell](#fields-that-run-through-a-shell).


[​](#path-rules)

Path rules

Every component path in a manifest is relative to the plugin root and must start with `./`. A path such as `commands/foo.md` fails validation. `skills` and `mcpServers` each accept one form outside that rule:

- **`skills`**: also accepts `"."`. Both `"."` and `"./"` denote the plugin root. Before v2.1.221, `"."` failed manifest validation, so use `"./"` when the plugin must load on earlier versions
- **`mcpServers`**: also accepts an `https://` bundle URL

`experimental.evals` isn’t a component path, so the rules in this section don’t cover it, and `claude plugin eval` checks the value when it runs instead. It names a directory below the plugin root, such as `"quality/evals"`, with or without the `./` prefix. With an array, only the first entry is used. For what the value accepts and what happens with an unusable one, see [Use a different eval directory](plugin-evals.md#use-a-different-eval-directory).


[​](#containment-and-existence)

Containment and existence

Every component path must resolve inside the plugin root and must exist. `claude plugin validate` checks the paths under every component key:

- **Containment**: a path that resolves outside the plugin root doesn’t load, and the `/plugin` **Errors** tab shows `<component> path escapes plugin directory: <path>`. A path containing `..` is the usual case, and `claude plugin validate` reports the error `Path contains ".." which could be a path traversal attempt`
- **Existence**: a path that doesn’t exist doesn’t load, and the `/plugin` **Errors** tab shows `<component> path not found: <path>`. `claude plugin validate` reports the error `Path not found`

For `outputStyles`, `lspServers`, `monitors`, and `themes` paths, the `claude plugin validate` check requires Claude Code v2.1.283 or later.


[​](#how-each-key-combines-with-its-default-location)

How each key combines with its default location

Each component key either replaces its default location, adds to it, or merges with it:

- **Replaces the default**: `commands`, `agents`, `outputStyles`, `workflows`, `experimental.themes`, `experimental.monitors`. When you set `commands`, the default `commands/` directory isn’t scanned. To keep the default and add more, list it explicitly: `"commands": ["./commands/", "./extras/"]`
- **Adds to the default**: `skills`. The `skills/` directory is still scanned, and the listed directories load alongside it
- **Merges**: `hooks`, `mcpServers`, `lspServers`. The default file loads first, and what the manifest declares merges into it, as described under [Component path forms](#component-path-forms)

If a plugin has a default folder such as `commands/` and also sets the manifest key that replaces it, Claude Code loads the manifest paths and not the folder. `claude plugin list` and the `/plugin` interface then show the warning `Default <folder>/ folder is ignored because the manifest sets "<key>"`. To avoid the warning, set the key to a path inside that folder: `"commands": ["./commands/deploy.md"]` names a file in the default folder and produces no warning.


[​](#user-configuration)

User configuration

`userConfig` declares values Claude Code prompts the user for when the plugin is enabled, so users don’t edit `settings.json` themselves. Keys are identifiers made of letters, digits, and underscores, and can’t start with a digit. Each value is a strict object with these fields. An unknown key fails validation.

| Field         | Required | Description                                                                                                                                                                               |
|:--------------|:---------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type`        | Yes      | One of `string`, `number`, `boolean`, `directory`, or `file`                                                                                                                              |
| `title`       | Yes      | Label shown in the configuration dialog                                                                                                                                                   |
| `description` | Yes      | Help text shown beneath the field                                                                                                                                                         |
| `required`    | No       | If `true`, the configuration dialog doesn’t accept an empty value                                                                                                                         |
| `default`     | No       | Value used when the user provides nothing: a string, number, boolean, or array of strings                                                                                                 |
| `options`     | No       | For `string`, the values the field accepts, shown as a picker in `/config`. See [Limit a field to fixed options](#limit-a-field-to-fixed-options). Requires Claude Code v2.1.271 or later |
| `multiple`    | No       | For `string`, allows an array of strings                                                                                                                                                  |
| `sensitive`   | No       | If `true`, masks input and stores the value in secure storage instead of `settings.json`                                                                                                  |
| `min` / `max` | No       | Bounds for `number`                                                                                                                                                                       |

Each option of each enabled plugin also appears as a row in the `/config` panel, except `sensitive` options and `multiple` lists. The `/config` rows require Claude Code v2.1.269 or later. This `userConfig` declares an endpoint and a masked token:

```python
{
  "userConfig": {
    "api_endpoint": {
      "type": "string",
      "title": "API endpoint",
      "description": "Your team's API endpoint"
    },
    "api_token": {
      "type": "string",
      "title": "API token",
      "description": "API authentication token",
      "sensitive": true
    }
  }
}
```


[​](#limit-a-field-to-fixed-options)

Limit a field to fixed options

Set `options` on a `userConfig` field to make users pick its value from a fixed list. To limit a `tone` field to three options, list them in `options` and set `default` to one of them:

```python
{
  "userConfig": {
    "tone": {
      "type": "string",
      "title": "Tone",
      "description": "Voice for generated replies",
      "options": ["neutral", "warm", "formal"],
      "default": "neutral"
    }
  }
}
```

If you declare `options` on any field, users on Claude Code versions before v2.1.271 can’t load the plugin. `options` applies to a `string` field that isn’t `multiple` or `sensitive`. Set `default` to one of the listed values, or set `required: true` so the user must pick one. Each option is a plain label of 1 to 64 characters, and `claude plugin validate`, which you run in your shell, reports anything else it rejects. A plugin whose `options` break these rules fails to load.


[​](#where-values-are-stored)

Where values are stored

Non-sensitive values are saved under [`pluginConfigs`](../02-Claude-Code-CLI/settings-reference.md#pluginconfigs) in the user’s `settings.json`. Sensitive values go to the platform’s secure credential store instead. The [settings page](../02-Claude-Code-CLI/settings-reference.md#pluginconfigs) lists which settings files `pluginConfigs` is read from.


[​](#reference-a-saved-value)

Reference a saved value

Reference a saved value where the plugin needs it, in one of two forms:

- **`${user_config.KEY}`**: substituted in MCP server config, LSP server config, [exec-form](../07-Hooks/hooks.md#exec-form-and-shell-form) hook `args`, and skill and agent content. In skill and agent content, only non-sensitive values are substituted, and a sensitive value there becomes a placeholder
- **`CLAUDE_PLUGIN_OPTION_<KEY>`**: exported to hook processes for every option, with `<KEY>` uppercased. A shell-form hook reads `$CLAUDE_PLUGIN_OPTION_API_TOKEN` for `api_token`


[​](#fields-that-run-through-a-shell)

Fields that run through a shell

Shell-form hook commands, monitor commands, and MCP [`headersHelper`](../06-MCP-Tools/General/mcp.md#use-dynamic-headers-for-custom-authentication) reject `${user_config.*}`. A component that references it in one of these fields fails with an [error](../02-Claude-Code-CLI/errors.md#plugin-command-references-user-config) instead of running, because the field’s value is passed to a shell that would re-parse the substituted value. The table shows how the value can reach each of these fields instead.

| Field                    | How the value can reach it                                                                                                                                                                                                    |
|:-------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Shell-form hook commands | Use [exec form](../07-Hooks/hooks.md#exec-form-and-shell-form) with `args`, or read `CLAUDE_PLUGIN_OPTION_<KEY>` from the hook’s environment                                                                                        |
| Monitor commands         | Not through Claude Code. Monitor processes don’t receive `CLAUDE_PLUGIN_OPTION_<KEY>`, so the monitor script has to obtain the value on its own                                                                               |
| MCP `headersHelper`      | Not through Claude Code. The helper’s environment carries `CLAUDE_PLUGIN_ROOT`, `CLAUDE_CODE_MCP_SERVER_NAME`, and `CLAUDE_CODE_MCP_SERVER_URL` but no option values, so the helper script has to obtain the value on its own |


[​](#channels)

Channels

`channels` declares the message channels a plugin provides, such as a bridge to a chat app. When you declare one, Claude Code can prompt for the channel’s configuration when the plugin is enabled. For how the server injects messages, see the [channels reference](../02-Claude-Code-CLI/channels-reference.md#package-as-a-plugin). Each entry is a strict object bound to one of the plugin’s MCP servers, with these fields:

| Field         | Required | Description                                                                                                                                                                   |
|:--------------|:---------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `server`      | Yes      | Key of the MCP server in this plugin’s `mcpServers` that the channel binds to                                                                                                 |
| `displayName` | No       | Name shown in the configuration dialog title. Defaults to the server name                                                                                                     |
| `userConfig`  | No       | Options to prompt for, in the same shape as [top-level `userConfig`](#user-configuration). Saved values substitute into `${user_config.KEY}` references in the server’s `env` |

This manifest binds a channel to the plugin’s `telegram` MCP server and prompts for a bot token that substitutes into the server’s `env`:

```python
{
  "mcpServers": {
    "telegram": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"],
      "env": { "BOT_TOKEN": "${user_config.bot_token}" }
    }
  },
  "channels": [
    {
      "server": "telegram",
      "displayName": "Telegram",
      "userConfig": {
        "bot_token": {
          "type": "string",
          "title": "Bot token",
          "description": "Telegram bot token",
          "sensitive": true
        }
      }
    }
  ]
}
```


[​](#environment-variables)

Environment variables

Claude Code provides three path variables to plugin components. Reference them as `${NAME}` in the fields listed under [Where each variable resolves](#where-each-variable-resolves), and read them as environment variables in the processes that receive them.

| Variable                | Resolves to                                                                                                                                                                                             | Use it for                                                                |
|:------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
| `${CLAUDE_PLUGIN_ROOT}` | Absolute path of the plugin’s installed version                                                                                                                                                         | Scripts, binaries, and config files bundled with the plugin               |
| `${CLAUDE_PLUGIN_DATA}` | `~/.claude/plugins/data/<id>/`, created on first reference and kept across plugin updates. `<id>` is the plugin identifier with every character other than a letter, digit, `_`, or `-` replaced by `-` | Installed dependencies such as `node_modules`, generated code, and caches |
| `${CLAUDE_PROJECT_DIR}` | The project root                                                                                                                                                                                        | Project-local scripts and config files                                    |

`${CLAUDE_PLUGIN_ROOT}` changes when the plugin updates, so don’t write state there. For where the root moves and when the old directory is cleaned up, see the [loading page](plugins-loading.md). By default, Claude Code deletes the `${CLAUDE_PLUGIN_DATA}` directory when you uninstall the plugin from the last place it’s installed. For `--keep-data` and the other cases where it stays, see [plugin uninstall](plugins-cli-reference.md#plugin-uninstall).


[​](#where-each-variable-resolves)

Where each variable resolves

In each plugin component, `${...}` references resolve inline in specific fields, and some components also receive the variables in their process environment:

| Plugin component                  | Fields where `${...}` resolves              | Exported to the process                                                                            |
|:----------------------------------|:--------------------------------------------|:---------------------------------------------------------------------------------------------------|
| Hook commands                     | Anywhere in `command` and `args`            | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_PROJECT_DIR`, and `CLAUDE_PLUGIN_OPTION_<KEY>` |
| Monitor commands                  | Anywhere in `command`                       | Not exported                                                                                       |
| MCP `stdio` servers               | `command`, `args`, `env`                    | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`                                                         |
| MCP `http`, `sse`, `ws` servers   | `url`, `headers`, `headersHelper`           | Not applicable                                                                                     |
| LSP servers                       | `command`, `args`, `env`, `workspaceFolder` | `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_PROJECT_DIR`                                   |
| Skill, command, and agent content | Anywhere in the Markdown body               | Not applicable                                                                                     |

The variables aren’t present in the environment of commands Claude runs through the Bash tool, in the main session or in a subagent. In skill, command, and agent content, write the `${...}` reference in the Markdown body instead, and Claude Code substitutes the path inline when it loads the content.


[​](#quoting-and-path-separators)

Quoting and path separators

Keep each substituted path a single argument:

- **Hook commands**: use [exec form](../07-Hooks/hooks.md#exec-form-and-shell-form) with `args` so each path is one argument with no quoting
- **Shell-form hooks and monitor commands**: wrap the variable in double quotes so a path with spaces stays one word

If you leave one of these variables outside quotes in a shell-form command in a hooks file, `claude plugin validate` warns about it unless the hook sets [`shell`](../07-Hooks/hooks.md#command-hook-fields) to `"powershell"`. This shell-form hook runs a script bundled with the plugin:

```python
{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}\"/scripts/process.sh"
          }
        ]
      }
    ]
  }
}
```

On Windows, the substituted paths use forward slashes so a shell doesn’t read backslashes as escapes.


[​](#standard-layout)

Standard layout

Each component type has a default location under the plugin root, used when the manifest doesn’t point elsewhere.

| Component     | Default location             | Contents                                                                                                                                                                                                                                                                                                                                              |
|:--------------|:-----------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Manifest      | `.claude-plugin/plugin.json` | Plugin metadata and configuration. Optional                                                                                                                                                                                                                                                                                                           |
| Skills        | `skills/`                    | One `<name>/SKILL.md` per skill. A plugin with `SKILL.md` at its root, no `skills/`, and no `skills` key loads as a single skill                                                                                                                                                                                                                      |
| Commands      | `commands/`                  | Flat Markdown command files. Prefer `skills/` for new plugins                                                                                                                                                                                                                                                                                         |
| Agents        | `agents/`                    | Agent Markdown files. Subfolders are part of the [agent name](plugins-components.md#agents)                                                                                                                                                                                                                                                     |
| Hooks         | `hooks/hooks.json`           | Hook configuration                                                                                                                                                                                                                                                                                                                                    |
| MCP servers   | `.mcp.json`                  | MCP server definitions                                                                                                                                                                                                                                                                                                                                |
| LSP servers   | `.lsp.json`                  | LSP server configurations                                                                                                                                                                                                                                                                                                                             |
| Output styles | `output-styles/`             | Output style Markdown files                                                                                                                                                                                                                                                                                                                           |
| Workflows     | `workflows/`                 | Workflow `.js` files                                                                                                                                                                                                                                                                                                                                  |
| Themes        | `themes/`                    | Theme JSON files                                                                                                                                                                                                                                                                                                                                      |
| Monitors      | `monitors/monitors.json`     | The monitors array                                                                                                                                                                                                                                                                                                                                    |
| Executables   | `bin/`                       | Files here are on the Bash tool’s `PATH` while the plugin is enabled, so Claude runs them as bare commands. claude.ai and Cowork don’t install a plugin that has this directory, including one you [distribute through claude.ai organization settings](https://claude.com/docs/plugins/org-sync#keep-executables-out-of-the-top-level-bin-directory) |
| Settings      | `settings.json`              | `agent` and `subagentStatusLine` defaults applied while the plugin is enabled                                                                                                                                                                                                                                                                         |

A plugin that uses every default location, plus a `scripts/` folder that its hooks call, is laid out like this:

```python
deploy-tools/
├── .claude-plugin/
│   └── plugin.json
├── skills/
│   └── deploy/
│       └── SKILL.md
├── commands/
│   └── status.md
├── agents/
│   └── reviewer.md
├── hooks/
│   └── hooks.json
├── monitors/
│   └── monitors.json
├── output-styles/
│   └── terse.md
├── themes/
│   └── dracula.json
├── workflows/
│   └── release-audit.js
├── bin/
│   └── deploy-tool
├── scripts/
│   └── format.sh
├── settings.json
├── .mcp.json
└── .lsp.json
```

To click through this layout and read what each file does, open the [plugin explorer](plugins-components.md#explore-the-plugin-directory). A `CLAUDE.md` at the plugin root isn’t loaded as context, and `claude plugin validate` warns when it finds one. To include instructions that load into Claude’s context, put them in a skill.


[​](#marketplace-entries-and-the-manifest)

Marketplace entries and the manifest

A [marketplace entry](plugins-marketplace-reference.md) accepts [its own fields](plugins-marketplace-reference.md#plugin-entries), including `strict`, and every field on this page except the [directory listing fields](#directory-listing-fields). The `strict` field decides whether the entry may add components to a plugin that has its own `plugin.json`. It defaults to `true`.


[​](#how-entry-fields-combine-with-plugin-json)

How entry fields combine with `plugin.json`

The entry either serves as the manifest, adds components to it, or conflicts with it:

- **No `plugin.json`**: the entry is the manifest, regardless of `strict`. Entry `hooks` loads only in the inline object form. For a file path or array there, the `/plugin` **Errors** tab shows a `not yet supported in a marketplace entry` error
- **`plugin.json` present, `strict` unset or `true`**: Claude Code loads the manifest and appends the entry’s `commands`, `agents`, `skills`, `outputStyles`, and `themes` to it. For `hooks`, the entry’s matchers for an event replace the manifest’s matchers for that same event, and events only the manifest declares keep theirs
- **`plugin.json` present, `strict: false`**: an entry that declares any of `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes` is a conflict, and the plugin fails to load with `Plugin <name> has conflicting manifests`

When a [marketplace entry whose `source` is the marketplace root](plugins-marketplace-reference.md) lists specific `skills` subdirectories, only those subdirectories load, and the plugin’s default `skills/` directory isn’t scanned. A `skills` key in the manifest instead [adds to the default](#how-each-key-combines-with-its-default-location).


[​](#metadata-precedence)

Metadata precedence

Some metadata fields have a fixed precedence regardless of `strict`:

- **`defaultEnabled` and display fields**: the entry’s `defaultEnabled` and its [display fields](plugins-marketplace-reference.md#entry-and-plugin-json) such as `displayName` override the manifest’s
- **`version`**: the manifest’s `version` overrides the entry’s
- **`name`**: when the entry lists the plugin under a different `name` than the manifest, `enabledPlugins` uses the entry name, and components are namespaced under the manifest name

For the full precedence table, see [Strict mode](plugins-marketplace-reference.md).


[​](#next-steps)

Next steps

- [Add components to a plugin](plugins-components.md): what each component does at runtime, with an example that validates
- [Marketplace reference](plugins-marketplace-reference.md): the entry fields a marketplace can set for your plugin
- [Plugin commands reference](plugins-cli-reference.md#plugin-validate): `claude plugin validate` flags and output
- [Troubleshoot plugins](plugins-troubleshooting.md#claude-plugin-validate-reports-errors): each validation message with its fix

---

## Recommend plugins for your org - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/relevance
Path: 08-Plugins-Skills/plugins-relevance.md

## On this page

- [Understand how plugin relevance works](#understand-how-plugin-relevance-works)
- [Add relevance to a plugin entry](#add-relevance-to-a-plugin-entry)
- [Field reference](#field-reference)
  - [relevance](#relevance)
  - [relevance.signals](#relevance-signals)
  - [Working directory matching](#working-directory-matching)
  - [Command name matching](#command-name-matching)
  - [Manifest dependency matching](#manifest-dependency-matching)
- [Validate your marketplace](#validate-your-marketplace)
- [Enable suggestions in managed settings](#enable-suggestions-in-managed-settings)
- [Preview what the user sees](#preview-what-the-user-sees)
- [See also](#see-also)

Run a marketplace

# Recommend plugins for your org

Copy pageCopy page

Add a relevance block to marketplace plugin entries so Claude Code suggests them when a user’s work matches, and allowlist the marketplace in managed settings.

Copy pageCopy page

Claude Code can suggest installing a plugin from your organization’s marketplace when a user’s session matches signals you define for that plugin. Signals include the working directory, files Claude has read, and commands Claude has run. You define them by adding a `relevance` block to the plugin’s entry in `marketplace.json`. A marketplace operator writes the `relevance` entries. An administrator then allowlists the marketplace in managed settings. Users see no suggestions from a marketplace until it’s allowlisted.

These cases are covered on other pages:

- **You want to install plugins**: see [Install and manage plugins](discover-plugins.md)
- **You want to turn suggestions off**: see [Understand how plugin relevance works](#understand-how-plugin-relevance-works)

Start with the sections for your role:

- **Marketplace operators**: read [how suggestions work](#understand-how-plugin-relevance-works), then [add relevance to a plugin entry](#add-relevance-to-a-plugin-entry) and [validate your marketplace](#validate-your-marketplace)
- **Administrators**: [enable suggestions in managed settings](#enable-suggestions-in-managed-settings)


[​](#understand-how-plugin-relevance-works)

Understand how plugin relevance works

Each plugin entry in `marketplace.json` can include a `relevance` object. The object names a topic and one or more signals. A signal is a pattern that Claude Code tests against the current session, such as the working directory or files Claude has read. Signal matching happens locally on the user’s machine and adds no network traffic. Claude Code doesn’t report which signals matched or their values to Anthropic or to the marketplace operator. When a signal matches and the plugin isn’t already installed, Claude Code suggests the plugin in these places:

- **Spinner tip**: a message with the `/plugin install` command appears below the spinner while Claude is responding.
- **Session-start notification**: if a `cwd` signal matches the working directory, a one-line notification appears before the user sends a first message.
- **`/plugin` Discover tab**: the plugin is pinned to the top of the Discover list.

[Preview what the user sees](#preview-what-the-user-sees) shows the exact text of each and how often they repeat. Claude Code never installs the plugin automatically. The user always confirms. The spinner tip and the session-start notification both stop appearing when the user or project sets [`spinnerTipsEnabled`](../02-Claude-Code-CLI/settings-reference.md#spinnertipsenabled) to `false`, or when a [`spinnerTipsOverride`](../02-Claude-Code-CLI/settings-reference.md#spinnertipsoverride) with `excludeDefault` replaces the built-in tips. The Discover-tab pin isn’t affected by either setting.


[​](#add-relevance-to-a-plugin-entry)

Add relevance to a plugin entry

Add a `relevance` object to the plugin’s entry in your `marketplace.json`. The following example declares that the `terraform-helpers` plugin is relevant when Claude reads a `.tf` file or runs `terraform`:

```python
{
  "name": "your-marketplace",
  "owner": { "name": "Your Org" },
  "plugins": [
    {
      "name": "terraform-helpers",
      "source": "./plugins/terraform-helpers",
      "description": "Your organization's Terraform conventions and helpers",
      "relevance": {
        "topic": "Terraform",
        "signals": {
          "cli": ["terraform"],
          "filesRead": ["**/*.tf"]
        }
      }
    }
  ]
}
```

While none of its signals match, the plugin keeps its normal position in the Discover list and doesn’t appear as a spinner tip. To check the block before publishing, [validate your marketplace](#validate-your-marketplace).


[​](#field-reference)

Field reference

The `relevance` object and its nested `signals` object accept the fields in the following tables. Older clients still load a marketplace that uses `relevance` fields they don’t recognize, because unknown fields under `relevance` and `relevance.signals` are ignored at load time. A recognized field whose value exceeds its limit in the [field reference](#field-reference) invalidates the whole plugin entry, and users can’t install that plugin from the marketplace until you fix it; `claude plugin validate` reports the same limits.


[​](#relevance)

`relevance`

| Field     | Type   | Description                                                                                                                                                             |
|:----------|:-------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `topic`   | string | Optional. The phrase that fills “Working with *topic*?” in the spinner tip. Defaults to the plugin name with each hyphen segment capitalized. Maximum 64 characters.    |
| `signals` | object | Matchers that determine when the plugin is relevant. Claude Code suggests the plugin only if at least one signal is set. See [`relevance.signals`](#relevance-signals). |

The `topic` is often the product name, for example `Terraform`. Use a domain such as `design` when the plugin name doesn’t sound natural as a topic.


[​](#relevance-signals)

`relevance.signals`

The `signals` object accepts the following fields.

| Field          | Type             | Description                                                                                                                                                                                                                                 | Limit                                                                                        |
|:---------------|:-----------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------|
| `cwd`          | array of strings | Glob patterns matched against the session’s working directory. See [working directory matching](#working-directory-matching).                                                                                                               | 10 patterns of 256 characters each                                                           |
| `cli`          | array of strings | Command names from shell commands Claude has run this session, for example `["terraform"]`. Exact match. See [command name matching](#command-name-matching).                                                                               | 10 entries of 64 characters each                                                             |
| `hosts`        | array of strings | Hostnames seen in `http://` or `https://` URLs in Bash commands this session, for example `["registry.terraform.io"]`. Bare lowercase hostname only: no scheme, port, or path. Exact case-insensitive match.                                | 20 entries of 128 characters each                                                            |
| `filesRead`    | array of strings | Glob patterns matched against the paths of files Claude has read this session, for example `["**/*.tf"]`. Forward-slash normalized and case-insensitive.                                                                                    | 10 patterns of 256 characters each                                                           |
| `manifestDeps` | array of objects | Dependencies declared in package manifests Claude has read this session. Each entry is `{ "file": "...", "pattern": "..." }`, where both values are regular expressions. See [manifest dependency matching](#manifest-dependency-matching). | 10 entries, each value at most 256 characters. Manifest files larger than 512 KB are skipped |

The `filesRead` and `manifestDeps` signals also match against files Claude has written or edited this session and against the project’s auto-loaded `CLAUDE.md` memory files.


[​](#working-directory-matching)

Working directory matching

`cwd` is the only signal that can match at session start, before the user sends a first message. Claude Code matches each `cwd` pattern as follows:

- The pattern is matched against the working directory as an absolute path. When the session is inside a git repository, it’s also matched against the working directory’s path relative to the repository root.
- Matching is forward-slash normalized and case-insensitive.
- Every pattern matches the directory itself and everything under it, so `infra`, `infra/`, and `infra/**` behave identically.


[​](#command-name-matching)

Command name matching

Claude Code records one command name for each shell command Claude runs: the first token after any leading environment variable assignments and `sudo`. Compound commands contribute only their leading command, so `cd infra && terraform plan` records `cd`, not `terraform`.


[​](#manifest-dependency-matching)

Manifest dependency matching

Each `manifestDeps` entry pairs two JavaScript `RegExp` source strings:

- `file`: matched case-insensitively against the manifest file’s path. The path is typically absolute, so anchor the pattern at the end rather than the start. Paths aren’t separator-normalized for this signal, so Windows paths use backslashes.
- `pattern`: matched case-sensitively against that file’s contents.

The following example uses `manifestDeps` to suggest your plugin once Claude has read a `package.json` that depends on your SDK’s npm package, named `your-sdk` here.

```python
{
  "name": "your-plugin",
  "source": "./plugins/your-plugin",
  "relevance": {
    "signals": {
      "manifestDeps": [
        {
          "file": "[/\\\\]package\\.json$",
          "pattern": "\"your-sdk\"\\s*:"
        }
      ]
    }
  }
}
```

In this example, the `file` pattern uses `[/\\\\]` so it matches both forward-slash and backslash path separators, and `\\.` so the dot is literal. In JSON, each backslash in the regular expression is written twice.


[​](#validate-your-marketplace)

Validate your marketplace

In your shell, run `claude plugin validate` against your marketplace directory to check the `relevance` block before publishing:

```python
claude plugin validate ./my-marketplace
```

The validator reports errors and warnings on the `relevance` block, including these:

- Reports unknown keys under `relevance` and `relevance.signals` as warnings
- Flags a `relevance` value that isn’t an object
- Rejects a `signals.hosts` entry that includes a scheme, port, or path

Each finding prints with the path of the field it concerns, and the output ends with `Validation passed`, `Validation passed with warnings`, or `Validation failed`.


[​](#enable-suggestions-in-managed-settings)

Enable suggestions in managed settings

Users see no suggestions from a marketplace until an administrator allowlists it in [managed settings](plugins-org.md), even when its `marketplace.json` declares `relevance`. To allowlist a marketplace, edit your managed settings as follows:

- Add the marketplace name to `pluginSuggestionMarketplaces`.
- For any marketplace other than the official Anthropic marketplace, also declare the marketplace source, either as that name’s entry in [`extraKnownMarketplaces`](plugins-org.md#require-a-marketplace-and-its-plugins) or as an entry in [`strictKnownMarketplaces`](plugins-org.md#allowlist-with-strictknownmarketplaces).

On a machine where the marketplace isn’t registered, or is registered under the allowlisted name from a different source, no suggestions from it appear. The source check stops an unrelated source from registering under an allowlisted name to get its plugins suggested across your org. The following `managed-settings.json` registers an org marketplace from a GitHub repository and enables its suggestions:

```python
{
  "extraKnownMarketplaces": {
    "your-marketplace": {
      "source": {
        "source": "github",
        "repo": "your-org/your-marketplace"
      }
    }
  },
  "pluginSuggestionMarketplaces": ["your-marketplace"]
}
```

The official marketplace’s name can only register from the official Anthropic source, so it needs no source declaration. For the official marketplace, allowlist the name alone:

```python
{
  "pluginSuggestionMarketplaces": ["claude-plugins-official"]
}
```


[​](#preview-what-the-user-sees)

Preview what the user sees

When a plugin’s `relevance` signal matches during a session, the tip below the spinner reads:

```python
Working with Terraform? Install the terraform-helpers plugin:
/plugin install terraform-helpers@your-marketplace
```

When a `cwd` signal matches at session start, the one-line notification reads:

```python
plugin suggestion: terraform-helpers@your-marketplace · /plugin
```

In the `/plugin` Discover tab, the plugin is pinned above the other results with an annotation that names the matching signal, such as `suggested for this directory` or `suggested for terraform commands`. Claude Code limits how often it suggests a given plugin:

- The suggestion appears at most once every three sessions across the spinner tip and the session-start notification combined.
- The session-start notification stops appearing once the spinner tip and the notification have shown the plugin a combined total of two times.
- Neither the spinner tip nor the session-start notification repeats once the plugin is installed.
- The Discover tab pins the plugin the first time the user opens the tab while the plugin’s signals match. Claude Code records that in `~/.claude.json`, so every later time the user opens `/plugin` on that machine, the plugin appears in normal order.


[​](#see-also)

See also

- [Host a marketplace](plugins-host-marketplace.md): run the marketplace that hosts your plugins
- [Marketplace reference](plugins-marketplace-reference.md#plugin-entries): every field a plugin entry accepts
- [Recommend your plugin from your CLI](plugins-cli-hints.md): prompt users from your own CLI instead of from Claude Code’s session signals
- [Manage plugins for your organization](plugins-org.md): `extraKnownMarketplaces`, `strictKnownMarketplaces`, and the rest of the plugin policy keys

---

## Plugin security and trust - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/security
Path: 08-Plugins-Skills/plugins-security.md

## On this page

- [Understand what a plugin can do](#understand-what-a-plugin-can-do)
- [Identify Anthropic’s marketplaces by name](#marketplace-tiers)
  - [Official marketplace names](#official-marketplace-names)
- [Review a plugin before you install](#review-a-plugin-before-you-install)
  - [Remove a plugin you no longer trust](#remove-a-plugin-you-no-longer-trust)
- [Recognize when Claude Code refuses or warns](#recognize-when-claude-code-refuses-or-warns)
  - [Trust warning before you install](#trust-warning-before-you-install)
  - [Untrusted marketplace sources and failed integrity checks](#untrusted-marketplace-sources-and-failed-integrity-checks)
- [Enforce plugin controls for your organization](#enforce-plugin-controls-for-your-organization)
- [Find plugins in telemetry](#find-plugins-in-telemetry)
- [Next steps](#next-steps)

Use plugins

# Plugin security and trust

Copy pageCopy page

Decide whether to trust a plugin before you install it, from what a plugin can do on your machine to how to review one and remove it.

Copy pageCopy page

A Claude Code plugin you install can execute arbitrary code on your machine with your user privileges. You install a plugin from a marketplace, which is the catalog Claude Code fetches it from. Some marketplace names are [reserved for Anthropic’s own marketplaces](#marketplace-tiers), and every other marketplace is third-party. A marketplace’s name tells you who publishes the catalog, not what each plugin in it does, so [review a plugin before you install it](#review-a-plugin-before-you-install) whichever marketplace it comes from. Read this page if you’re deciding whether to install a plugin, or if you review tools before your team can use them.

These cases are covered on other pages:

- **Claude Code’s own security model**: see [Security](../13-Enterprise-Admin/security.md)
- **Restricting or requiring plugins for an organization**: see [Manage plugins for your organization](plugins-org.md)
- **The `security-guidance` or `claude-security` plugins**: this page isn’t about them. See [`security-guidance`](../02-Claude-Code-CLI/security-guidance.md) and [`claude-security`](../02-Claude-Code-CLI/claude-security.md)

Start with [what a plugin can do](#understand-what-a-plugin-can-do) and [which marketplaces are Anthropic’s](#marketplace-tiers), then [review the plugin before you install it](#review-a-plugin-before-you-install).


[​](#understand-what-a-plugin-can-do)

Understand what a plugin can do

A plugin can carry content that runs code on your machine with your user privileges and content that enters Claude’s context as instructions, so [review a plugin before you install it](#review-a-plugin-before-you-install). Here’s what an installed plugin can do:

- **Hooks**: a plugin’s [hooks](../07-Hooks/hooks.md) run as shell commands at points in Claude Code’s lifecycle, such as before or after a tool call.
- **Monitors**: a plugin’s [monitors](plugins-components.md#monitors) run as background shell commands that Claude Code starts on its own when the session starts, when you reload plugins, or the first time a named skill runs.
- **Mods**: a plugin’s [mod](plugins-mods-overview.md) runs JavaScript inside Claude Code with your permissions. To list what a mod does before you install it, see [Decide whether to trust a mod](plugins-mods-overview.md#decide-whether-to-trust-a-mod).
- **MCP and LSP servers**: Claude Code connects to the [MCP servers](../06-MCP-Tools/General/mcp.md) an enabled plugin declares and gives Claude their tools. A stdio MCP server runs as a process that Claude Code starts on your machine. Claude Code also starts the language servers the plugin declares.
- **`bin/` directory**: Claude Code adds each enabled plugin’s `bin/` directory to the `PATH` of the Bash tool’s shell, so Claude’s Bash commands can run any executable there. A command that Claude runs with the [PowerShell tool](../02-Claude-Code-CLI/tools-reference.md#powershell-tool) gets those directories on its `PATH` when it runs outside the [sandbox](../02-Claude-Code-CLI/sandboxing.md), and not when it runs inside it.
- **Skills, commands, and agents**: these enter Claude’s context as instructions, so they influence what Claude does with the tools it already has.
- **Updates**: when auto-update is on for the marketplace you installed a plugin from, Claude Code updates that plugin in the background, so the files you reviewed can change on disk. [When auto-update runs](plugins-loading.md#when-auto-update-runs) has the timing. To turn auto-update on or off per marketplace, see [Keep plugins updated](discover-plugins.md#keep-plugins-updated).

Claude Code’s [permission rules](../02-Claude-Code-CLI/permissions.md) and [sandbox](../02-Claude-Code-CLI/sandboxing.md) cover the tool calls Claude makes, not the code a plugin runs by itself:

- **Hooks, monitors, and server processes**: command hooks and monitors are shell commands that run with your full user permissions. Claude Code runs hooks, monitors, MCP servers, LSP servers, and the processes a [mod](plugins-mods-overview.md#what-a-mod-can-reach) starts outside the sandbox.
- **Claude’s tool calls**: a call to one of the plugin’s MCP tools, and a Bash command that runs an executable from the plugin’s `bin/`, are tool calls, so your permission rules apply to them. For what a mod can do to a tool call, see [Decide whether to trust a mod](plugins-mods-overview.md#decide-whether-to-trust-a-mod).

Installing a plugin also enables it, unless its manifest or marketplace entry sets [`defaultEnabled: false`](discover-plugins.md#choose-an-install-scope) and you haven’t enabled it yourself. To remove a plugin you no longer trust, see [Remove a plugin you no longer trust](#remove-a-plugin-you-no-longer-trust).


[​](#marketplace-tiers)

Identify Anthropic’s marketplaces by name

A marketplace’s name places it in one of three tiers: official, community, or third-party. Claude Code accepts the official and community names only for marketplaces sourced from `github.com/anthropics/` repositories, so a third-party marketplace can’t present itself as an Anthropic one. A marketplace that a coworker or your organization publishes is third-party. The table lists which names fall in each tier:

| Tier        | Which marketplaces                                                                               |
|:------------|:-------------------------------------------------------------------------------------------------|
| Official    | The [official marketplace names](#official-marketplace-names), such as `claude-plugins-official` |
| Community   | `claude-community`, `claude-plugins-community`, and `healthcare`                                 |
| Third-party | Every other marketplace                                                                          |

Where the `claude-community` catalog pins a plugin to a commit SHA, which it does for nearly every entry, Claude Code refuses to install a different commit.


[​](#official-marketplace-names)

Official marketplace names

These marketplace names make up the official tier:

- `claude-plugins-official`
- `claude-code-marketplace`
- `claude-code-plugins`
- `anthropic-marketplace`
- `anthropic-plugins`
- `agent-skills`
- `anthropic-agent-skills`
- `life-sciences`
- `knowledge-work-plugins`
- `claude-for-legal`
- `claude-for-financial-services`
- `financial-services-plugins`
- `first-party-plugins`
- `claude-tag-plugins`

For how the official, community, and demo marketplaces differ and where to browse what each one lists, see [Anthropic’s marketplaces](plugins-anthropic-marketplaces.md).


[​](#review-a-plugin-before-you-install)

Review a plugin before you install

Before you install a plugin, look at what it adds and where it comes from.

1

Check the marketplace's source

In your shell, run `claude plugin marketplace list` to print the source each marketplace was added from, such as a GitHub repository or a directory.

2

Read the details pane

In a Claude Code session, run `/plugin` and select the plugin. The details pane shows a **Will install** section listing the plugin’s commands, agents, skills, hooks, and MCP and LSP servers. For a plugin Anthropic has no published component data for, the section shows what the marketplace entry declares, or a note: `Components will be discovered at installation` for a plugin stored inside the marketplace, or `Component summary not available for remote plugin` for one fetched from elsewhere.

3

Read the plugin's source

In the details pane, select **Open homepage** or **View on GitHub** below the install options. If the pane offers neither, open the marketplace repository you found in the first step. Find the plugin’s directory there. The **Will install** section shows that a hook exists but not what it runs, so read these files in the plugin’s directory:

- **`hooks/hooks.json`**: the command each hook runs
- **`.mcp.json`**: each server’s command or URL
- **`bin/`**: every file in the directory

4

List what the plugin contains

Clone the repository that holds the plugin’s directory, then run `claude --plugin-dir <plugin directory> plugin details <plugin name>` in your shell to see what Claude Code finds in it. The command reads the plugin’s files without starting a session and prints a `Component inventory` listing the plugin’s skills and commands, agents, hooks with each hook’s event, and MCP and LSP servers.

After you install a plugin, run `claude plugin details <plugin name>` in your shell to print the same `Component inventory` for the installed copy under `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`.


[​](#remove-a-plugin-you-no-longer-trust)

Remove a plugin you no longer trust

In your shell, run [`claude plugin uninstall <plugin>`](plugins-cli-reference.md#plugin-uninstall) with the `--scope` you installed it at. Then check what the uninstall removed and what it left:

- **Persistent data**: by default, when that was the last scope the plugin was installed at, uninstalling also deletes the plugin’s persistent data directory. For `--keep-data` and the other cases where it stays, see [plugin uninstall](plugins-cli-reference.md#plugin-uninstall).
- **Cached files**: the plugin’s files stay on disk under `~/.claude/plugins/cache/` for 14 days before a [background sweep removes them](plugins-loading.md#cleanup-of-previous-versions). After you uninstall your last plugin, orphaned directories stay until you install another. To delete the files now, remove the plugin’s directory under `~/.claude/plugins/cache/<marketplace>/<plugin>/` yourself.
- **The marketplace**: if you don’t trust the marketplace’s owner either, [remove the marketplace](discover-plugins.md#manage-marketplaces) too, which uninstalls every plugin you installed from it.


[​](#recognize-when-claude-code-refuses-or-warns)

Recognize when Claude Code refuses or warns

The details pane you open from the **Discover** or **Marketplaces** tab in `/plugin` shows the same trust warning for each plugin. Claude Code refuses instead of warning in cases such as those under [Untrusted marketplace sources and failed integrity checks](#untrusted-marketplace-sources-and-failed-integrity-checks).


[​](#trust-warning-before-you-install)

Trust warning before you install

The warning reads the same whatever marketplace the plugin comes from:

```python
Make sure you trust a plugin before installing, updating, or using it. Anthropic does not control what MCP servers, files, or other software are included in plugins and cannot verify that they will work as intended or that they won't change. See each plugin's homepage for more information.
```

If your organization sets `pluginTrustMessage` in [managed settings](plugins-org.md), Claude Code appends that text to the warning.


[​](#untrusted-marketplace-sources-and-failed-integrity-checks)

Untrusted marketplace sources and failed integrity checks

Claude Code refuses to load a marketplace or to install a plugin in these cases, each with its own error message:

- **Untrusted marketplace source**: when a marketplace uses an official or community name but its source is outside `github.com/anthropics/`, Claude Code stops loading the marketplace and the plugins you installed from it. The error is [Marketplace is registered from an untrusted source](../02-Claude-Code-CLI/errors.md#marketplace-is-registered-from-an-untrusted-source).
- **Archive integrity**: when a marketplace entry pins an [`archive` source](plugins-marketplace-reference.md#archive-plugin-source) to a `sha256` digest and the downloaded file’s digest doesn’t match it, Claude Code refuses the install. The error is [Plugin archive integrity check failed](../02-Claude-Code-CLI/errors.md#plugin-archive-integrity-check-failed).

The `sha256` pin is separate from the community catalog’s commit SHA pin, which selects the git commit to check out.


[​](#enforce-plugin-controls-for-your-organization)

Enforce plugin controls for your organization

With [managed settings](plugins-org.md), an administrator can enforce these plugin controls:

- Allowlist or blocklist marketplace sources
- Force-enable plugins
- Turn off the `--plugin-dir` and `--plugin-url` flags and the `CLAUDE_CODE_PLUGIN_DIRS` variable
- Limit hooks to those from managed settings and force-enabled plugins
- Stop plugins from members’ claude.ai accounts from loading in Claude Code, with [`syncClaudeAiPlugins`](plugins-org.md#control-matrix)

The [control matrix](plugins-org.md#control-matrix) says what each key does and doesn’t cover.


[​](#find-plugins-in-telemetry)

Find plugins in telemetry

If your organization exports Claude Code’s [OpenTelemetry events](../13-Enterprise-Admin/monitoring-usage.md) to its own backend, the [marketplace tiers](#marketplace-tiers) decide which plugin names appear there:

- **[Plugin loaded event](../13-Enterprise-Admin/monitoring-usage.md#plugin-loaded-event)**: the event reports official-tier plugin and marketplace names as they are. For the community and third-party tiers, `plugin.name` and `marketplace.name` are the literal string `third-party` unless you set `OTEL_LOG_TOOL_DETAILS=1`.
- **Plugin scope**: the loaded event’s `plugin.scope` still reports where the plugin came from, such as `org` for a plugin your managed settings enable or `user-local` for any other third-party plugin. The [plugin loaded event](../13-Enterprise-Admin/monitoring-usage.md#plugin-loaded-event) lists every value.
- **[Plugin installed event](../13-Enterprise-Admin/monitoring-usage.md#plugin-installed-event)**: unless you set `OTEL_LOG_TOOL_DETAILS=1`, the event omits the name fields for non-official plugins instead of reporting `third-party`.
- **[Claude Code Analytics API](../04-API-Reference/Admin/platform-claude-com-http-beta-organization-analytics-plugins-list.md)**: Claude Code reports plugins from the official and community tiers by name and reports every other plugin as `third-party`.


[​](#next-steps)

Next steps

- [Manage plugins for your organization](plugins-org.md): restrict which marketplaces users can install from and require the ones you trust
- [Install and manage plugins](discover-plugins.md): review a plugin’s details pane before you choose a scope
- [Anthropic’s marketplaces](plugins-anthropic-marketplaces.md): which marketplace names are Anthropic’s
- [Security](../13-Enterprise-Admin/security.md): Claude Code’s own security model

---

## Superpowers | Claude by Anthropic

Source: https://www.claude.com/plugins/superpowers
Path: 08-Plugins-Skills/plugins-superpowers.md

# Superpowers

Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.

- 

  Install in
  Claude Code
- 

  Made by[Jesse Vincent](https://github.com/obra/superpowers)
- 

  Plugin source[View source](https://github.com/obra/superpowers.git)
- 

  Installs1,009,371

Superpowers is a comprehensive skills framework that teaches Claude structured software development methodologies. It provides composable skills for test-driven development (TDD), systematic debugging, brainstorming, subagent-driven development with built-in code review, and the ability to author new skills.

The plugin enforces disciplined practices like "red-green-refactor" TDD cycles where tests must fail before implementation, a four-phase debugging methodology that requires root cause investigation before any fixes, and Socratic brainstorming sessions that refine requirements before coding begins.

**How to use:** Invoke skills with slash commands like `/brainstorming` to explore requirements and design before implementation, or `/execute-plan` to run batched implementation plans with review checkpoints. The framework includes a code-reviewer agent that evaluates implementations against plans, coding standards, and architectural principles.

For debugging, the systematic methodology guides you through root cause investigation, pattern analysis, hypothesis testing, and implementation—with safeguards that trigger architectural review after three failed fix attempts. The writing-skills module teaches you to author and test new skills using TDD principles applied to documentation.

## Other plugins

### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)

Anthropic verified

Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.

### [Code Review](https://www.claude.com/marketplace/plugins/code-review)

Anthropic verified

AI code review with specialized agents and confidence-based filtering for pull requests

### [Context7](https://www.claude.com/marketplace/plugins/context7)

Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.

### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)

Anthropic verified

Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.

### [Playwright](https://www.claude.com/marketplace/plugins/playwright)

Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.

### [GitHub](https://www.claude.com/marketplace/plugins/github)

Official GitHub MCP server for repo management. Create issues, manage PRs, review code, search repos, and access GitHub's API from Claude Code.

[](https://www.claude.com/)

© 2026 Anthropic PBC

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)

## Company

- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../19-Reference/anthropic-com-research.md)
- [Anthropic news](../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)

## Terms and policies

- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)

## Products

- [Claude](../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../15-Claude-AI-Features/product-cowork.md)
- [Claude Duet](../15-Claude-AI-Features/product-claude-duet.md)
- [Claude Tag](../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)

## Capabilities

- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)

## Extensions

- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Google Workspace](https://www.claude.com/claude-for-google-workspace)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)

## Models

- [Mythos (including Fable)](../15-Claude-AI-Features/claude-mythos.md)
- [Opus](../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../15-Claude-AI-Features/claude-haiku.md)

## Enterprise

- [Overview](../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../15-Claude-AI-Features/product-claude-code-enterprise.md)

## Departments

- [Customer support](../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../18-Industry-UseCases/cybersecurity.md)
- [Legal](../18-Industry-UseCases/legal.md)
- [Sales](../18-Industry-UseCases/sales.md)

## Industries

- [Financial advisors](../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../18-Industry-UseCases/finance.md)
- [Government](../18-Industry-UseCases/government.md)
- [Healthcare](../18-Industry-UseCases/healthcare.md)
- [Higher education](../18-Industry-UseCases/education.md)
- [K-12 teachers](../18-Industry-UseCases/teachers.md)
- [Life sciences](../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../18-Industry-UseCases/nonprofits.md)
- [Small business](../18-Industry-UseCases/small-business.md)

## Programs

- [Campus](../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../15-Claude-AI-Features/programs-team-plan-for-scientists.md)
- [Startups](../15-Claude-AI-Features/programs-startups.md)

## Developers

- [Developer docs](../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](../04-API-Reference/Other/home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)

## Platform

- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](../04-API-Reference/Other/partners-claude-on-aws.md)
- [Google Cloud](../04-API-Reference/Other/partners-google-cloud.md)
- [Microsoft Foundry](../04-API-Reference/Other/partners-microsoft-foundry.md)
- [Managed Agents](https://www.claude.com/platform/claude-managed-agents)

## Resources

- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](../04-API-Reference/Other/partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](../04-API-Reference/Other/partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)

## Help and security

- [Availability](https://www.anthropic.com/legal/supported-regions)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)

---

## Troubleshoot plugins - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins/troubleshooting
Path: 08-Plugins-Skills/plugins-troubleshooting.md

## On this page

- [Find where /plugin runs](#find-where-%2Fplugin-runs)
  - [/plugin isn't available in this environment](#plugin-isnt-available-in-this-environment)
  - [zsh: no such file or directory: /plugin](#zsh-no-such-file-or-directory-plugin)
  - [The term '/plugin' is not recognized as the name of a cmdlet](#the-term-plugin-is-not-recognized-as-the-name-of-a-cmdlet)
  - [claude: command not found after claude plugin ...](#claude-command-not-found-after-claude-plugin)
  - [Unknown command and command spellings that don’t exist](#unknown-command-and-command-spellings-that-dont-exist)
- [Add a marketplace](#add-a-marketplace)
  - [Marketplace "claude-plugins-official" not found](#marketplace-claude-plugins-official-not-found)
  - [Marketplace "\<name\>" not found](#marketplace-not-found)
  - [Invalid marketplace source format](#invalid-marketplace-source-format)
  - ['\<source\>' is not a valid GitHub owner/repo shorthand](#is-not-a-valid-github-owner-repo-shorthand)
  - [Invalid git URL](#invalid-git-url)
  - [Path does not exist: \<path\>](#path-does-not-exist)
  - [Marketplace file not found at \<path\>/.claude-plugin/marketplace.json](#marketplace-file-not-found-at-claude-plugin-marketplace-json)
  - [Cannot add marketplace "\<name\>": Claude Code cannot install plugins from a marketplace with this name](#cannot-install-plugins-from-a-marketplace-with-this-name)
  - [Cannot add marketplace "\<name\>": Claude Code reserves this name and cannot register a marketplace under it](#claude-code-reserves-this-name)
  - [SSH authentication failed or HTTPS authentication failed](#ssh-authentication-failed-or-https-authentication-failed)
  - [SSH host key is not in your known_hosts file](#ssh-host-key-is-not-in-your-known-hosts-file)
  - [Command 'git' not found or is in an unsafe location](#command-git-not-found-or-is-in-an-unsafe-location)
  - [Git clone timed out after 120s](#git-clone-timed-out-after-120s)
  - [Marketplace updates keep failing offline](#marketplace-updates-keep-failing-offline)
  - [Marketplace add fails on a GitHub Enterprise Server host](#marketplace-add-fails-on-a-github-enterprise-server-host)
- [Install a plugin](#install-a-plugin)
  - [Plugin "\<name\>" not found in marketplace "\<marketplace\>"](#plugin-not-found-in-marketplace)
  - [not found in marketplace with a refresh hint](#the-message-ends-with-a-refresh-hint)
  - [not found in marketplace with no hint](#the-message-has-no-hint)
  - [Plugin "\<name\>" not found in any marketplace](#plugin-not-found-in-any-marketplace)
  - [Plugin '\<name\>@\<marketplace\>' is already installed globally](#plugin-is-already-installed-globally)
  - ["\<plugin\>" was not installed: it would share its folder with "\<other\>"](#plugin-would-share-its-folder)
  - [This plugin uses a source type your Claude Code version does not support](#this-plugin-uses-a-source-type-your-claude-code-version-does-not-suppo)
  - [Plugin archive integrity check failed](#plugin-archive-integrity-check-failed)
  - [An npm plugin source must name a registry package](#an-npm-plugin-source-must-name-a-registry-package)
  - [Marketplace "\<name\>" is registered from an untrusted source](#marketplace-is-registered-from-an-untrusted-source)
  - [Marketplace "\<name\>" is added but ignored](#marketplace-is-added-but-ignored)
  - [Plugin \<name\> has a corrupt manifest file or has an invalid manifest file](#plugin-has-a-corrupt-manifest-file-or-has-an-invalid-manifest-file)
  - [Plugin directory not found at path: \<path\>](#plugin-directory-not-found-at-path)
  - [No plugins available or No marketplaces configured](#no-plugins-available-or-no-marketplaces-configured)
  - [Marketplace "\<name\>" is already added from a different source](#marketplace-is-already-added-from-a-different-source)
  - [Cannot add marketplace "\<name\>": its source doesn't match its extraKnownMarketplaces entry in user or managed settings](#cannot-add-marketplace-source-doesnt-match)
  - [Failed to install: \<plugin\> (\<reason\>)](#failed-to-install-from-the-plugin-menu)
  - [Could not move the new copy of this plugin version into \<path\>](#could-not-move-the-new-copy-of-this-plugin-version)
  - [Dependency errors](#dependency-errors)
- [Plugin installed but not working](#plugin-installed-but-not-working)
  - [Plugin doesn’t appear or its skills don’t show up](#plugin-doesnt-appear-or-its-skills-dont-show-up)
  - [Run /reload-plugins to apply.](#run-reload-plugins-to-activate)
  - [The packages it lists are not installed or were not installed, because ...](#the-packages-it-lists-are-not-installed)
  - [Plugin "\<name\>" not cached at \<path\>](#plugin-not-cached-at)
  - [installed_plugins.json holds a record under "\<id\>" that this version of Claude Code cannot read](#installed-plugins-json-holds-a-record-this-version-cannot-read)
  - [installed_plugins.json could not be read and was rebuilt](#installed-plugins-json-could-not-be-read-and-was-rebuilt)
  - [install records under names that no version of Claude Code can use were removed from installed_plugins.json](#install-records-under-names-that-no-version-can-use)
  - [does not load (...), so Claude Code ignores the whole file](#does-not-load-so-claude-code-ignores-the-whole-file)
  - [Disabled in ~/.claude/settings.json but still loads](#a-plugin-you-disabled-still-loads)
  - [Plugin "\<name\>" is enabled in project settings but isn't installed here](#plugin-is-enabled-in-project-settings-but-isnt-installed-here)
  - [A plugin stays installed after plugin uninstall on Windows](#a-plugin-stays-installed-after-plugin-uninstall-on-windows)
  - [A plugin installed from a git repository shows version unknown](#a-plugin-installed-from-a-git-repository-shows-version-unknown)
  - [Failed to load hooks from \<path\> and hooks that don’t fire](#failed-to-load-hooks-from-and-hooks-that-dont-fire)
  - [Hooks fail to load](#hooks-fail-to-load)
  - [hook error notices in the transcript](#hook-error-notices-in-the-transcript)
  - [A plugin hook blocks a tool call or prompt](#a-plugin-hook-blocks-a-tool-call-or-prompt)
  - [Hook loads but never fires](#hook-loads-but-never-fires)
  - [Plugin directory does not exist: \<path\>](#plugin-directory-does-not-exist)
  - [Invalid MCP server config for "\<server\>" and MCP servers that don’t start](#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start)
  - [Invalid MCP server config for "\<server\>": \<error\>](#invalid-mcp-server-config-for-%3Cserver%3E-%3Cerror%3E)
  - [Bundled MCP server "\<name\>" was not started: it needs configuration](#bundled-mcp-server-%3Cname%3E-was-not-started-it-needs-configuration)
  - [Server is configured but never connects](#server-is-configured-but-never-connects)
  - [Server works with --plugin-dir but fails after install](#server-works-with-plugin-dir-but-fails-after-install)
  - [Language server doesn’t start, uses too much memory, or reports wrong diagnostics](#language-server-doesnt-start)
  - [Language server doesn’t start](#language-server-doesn%E2%80%99t-start)
  - [Language server uses too much memory](#language-server-uses-too-much-memory)
  - [False positive diagnostics in a monorepo](#false-positive-diagnostics-in-a-monorepo)
- [Build a plugin](#build-a-plugin)
  - [commands path not found: \<path\>](#commands-path-not-found)
  - [--plugin-dir at a marketplace root doesn’t load the plugins under plugins/](#plugin-dir-loads-a-plugin-with-no-components)
  - [Files the plugin references outside its directory aren’t found](#files-the-plugin-references-outside-its-directory-arent-found)
  - [\${CLAUDE_PLUGIN_ROOT} shows forward slashes on Windows](#claude-plugin-root-shows-forward-slashes-on-windows)
  - [Plugin loads but its skills are missing](#plugin-loads-but-its-skills-are-missing)
  - [Skill loads but Claude never invokes the skill](#skill-loads-but-claude-never-invokes-the-skill)
  - [\<directory\> is not a plugin or skill folder from claude plugin eval init](#is-not-a-plugin-or-skill-folder)
  - [The userConfig dialog never appears](#the-userconfig-dialog-never-appears)
  - [claude plugin validate reports errors](#claude-plugin-validate-reports-errors)
  - [Plugin \<name\> has conflicting manifests](#plugin-has-conflicting-manifests)
  - [Warning: No commands found in plugin \<name\> custom directory](#warning-no-commands-found-in-plugin-custom-directory)
- [Host a marketplace](#host-a-marketplace)
  - [Plugins with relative paths fail in URL-based marketplaces](#plugins-with-relative-paths-fail-in-url-based-marketplaces)
  - [Marketplace validation errors](#marketplace-validation-errors)
- [Blocked by your organization](#blocked-by-your-organization)
  - [Marketplace source '\<source\>' is blocked by enterprise policy](#marketplace-source-is-blocked-by-enterprise-policy)
  - [Marketplace "\<name\>" is not in the allowed marketplace list](#marketplace-is-not-in-the-allowed-marketplace-list)
  - [Plugin "\<name\>" is blocked by your organization's policy and cannot be installed](#plugin-is-blocked-by-your-organizations-policy-and-cannot-be-installed)
  - [--plugin-dir is disabled by your organization's managed settings (disableSideloadFlags)](#plugin-dir-is-disabled-by-your-organizations-managed-settings-disables)
  - [Plugins from ~/.claude/skills/ are blocked by your organization's managed settings](#plugins-from-claude-skills-are-blocked-by-your-organizations-managed-s)
  - [Command-sourced plugins are disabled by your organization's managed settings](#command-sourced-plugins-are-disabled-by-your-organizations-managed-set)
  - [Marketplace '\<name\>' is seed-managed](#marketplace-is-seed-managed)
- [Next steps](#next-steps)

Troubleshooting

# Troubleshoot plugins

Copy pageCopy page

Fix plugin errors in Claude Code. Find the exact message you saw, grouped by stage from where /plugin runs through install and org policy.

Copy pageCopy page

This page lists error messages and symptoms for Claude Code plugins and for marketplaces, the catalogs Claude Code installs plugins from. Each entry gives the cause, one fix, and what you see once the fix works. Where a message names a plugin or marketplace, the entry shows a placeholder such as `<name>` instead. Use this page whether you install plugins, build them, host a marketplace, or administer plugins for an organization.

These cases are covered on other pages:

- **Why scopes, the cache, and precedence behave the way they do**: read [Plugin loading reference](plugins-loading.md)
- **Looking up a flag, field, or command**: use the [plugin commands reference](plugins-cli-reference.md), the [manifest reference](plugins-reference.md), or the [marketplace reference](plugins-marketplace-reference.md)
- **A `hooks module not loaded` or `hooks module did not load` message**: the plugin is a [mod](plugins-mods-overview.md), so read [The mod doesn’t load](plugins-mods-troubleshoot.md#the-mod-doesn%E2%80%99t-load)

Search for the exact message you saw. Each message is listed under the stage that produces it, which isn’t always the command you ran. For example, an install can fail because a marketplace is missing, so that message is under [Add a marketplace](#add-a-marketplace).


[​](#find-where-/plugin-runs)

Find where `/plugin` runs

`/plugin` is a command you type inside a running Claude Code terminal session, and it opens an interactive panel. The entries in this section cover the places where you can type it but it can’t run, and the command spellings that don’t exist.


[​](#plugin-isnt-available-in-this-environment)

`/plugin isn't available in this environment`

You typed `/plugin` somewhere other than a Claude Code terminal session, and Claude replied with this line instead of opening anything. You get this reply in a session that has no terminal to draw the `/plugin` panel in: [non-interactive mode](../02-Claude-Code-CLI/headless.md) with `claude -p`, the Agent SDK, the Claude desktop app’s Code tab, the VS Code extension panel, and the browser at claude.ai/code. In the VS Code extension panel, only a `/plugin` line with something after it, such as `/plugin install <plugin>@<marketplace>`, gets this reply. `/plugin` or `/plugins` typed alone opens the **Manage plugins** dialog. Install the plugin from the surface you’re on instead:

- **Claude desktop app, local or SSH session**: use the **Desktop app** tab under [Install a plugin](discover-plugins.md#install-a-plugin)
- **VS Code extension**: use the **VS Code** tab under [Install a plugin](discover-plugins.md#install-a-plugin)
- **Claude Code on the web, or a desktop cloud session**: a cloud session has no plugin browser. See the **Cloud session** tab under [Install a plugin](discover-plugins.md#install-a-plugin) for what a cloud session loads
- **A terminal you have access to**: run `claude` and type `/plugin` there, or run `claude plugin install <plugin>@<marketplace>` in your shell without starting a session

When a terminal install works, `/plugin` prints an install summary that starts with `✓ Installed <plugin>.` and `claude plugin install` prints `Successfully installed plugin: <plugin>@<marketplace>`.


[​](#zsh-no-such-file-or-directory-plugin)

`zsh: no such file or directory: /plugin`

You typed `/plugin ...` at a shell prompt, and the shell reported that no file named `/plugin` exists. Bash reports `bash: /plugin: No such file or directory`. `/plugin` is a command you type inside a Claude Code session, not at the shell prompt. Start a session and type the same command there:

```python
claude
```

Then, at the Claude Code prompt:

```python
/plugin install <plugin>@<marketplace>
```

A successful install prints a summary that starts with `✓ Installed <plugin>.` If the install itself then fails, its message is under [Add a marketplace](#add-a-marketplace) or [Install a plugin](#install-a-plugin). To install from the shell without starting a session, run `claude plugin install <plugin>@<marketplace>` instead.


[​](#the-term-plugin-is-not-recognized-as-the-name-of-a-cmdlet)

`The term '/plugin' is not recognized as the name of a cmdlet`

You typed `/plugin ...` at a PowerShell prompt, and `/plugin` is a Claude Code command, not a program. Bash and Zsh report [their own form of this error](#zsh-no-such-file-or-directory-plugin). Use either of these instead:

- Run `claude`, then type `/plugin` at the Claude Code prompt
- Run `claude plugin install <plugin>@<marketplace>` in PowerShell without starting a session


[​](#claude-command-not-found-after-claude-plugin)

`claude: command not found` after `claude plugin ...`

You ran `claude plugin install ...` in your shell, and the shell couldn’t find `claude` at all. On Windows the message is `'claude' is not recognized as the name of a cmdlet` or `'claude' is not recognized as an internal or external command`. The cause isn’t the plugin command. Follow [Verify your PATH](../02-Claude-Code-CLI/troubleshoot-install.md#verify-your-path), then retry the plugin command.


[​](#unknown-command-and-command-spellings-that-dont-exist)

`Unknown command` and command spellings that don’t exist

You typed a plugin command you saw somewhere and got `Unknown command: /<name>` in a session, or `error: unknown command '<name>'` or `error: unknown option '<flag>'` from the `claude` binary in your shell. Several command spellings are in use that Claude Code doesn’t have. The table below maps each one to the real command. The [plugin commands reference](plugins-cli-reference.md) lists the subcommands and their flags.

| You typed                                  | What Claude Code says                                                        | Use instead                                                                                                                          |
|:-------------------------------------------|:-----------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
| `claude plugin add <source>`               | `error: unknown command 'add'`                                               | `claude plugin marketplace add <source>` to add a marketplace, or `claude plugin install <plugin>@<marketplace>` to install a plugin |
| `claude plugin install <plugin> --project` | `error: unknown option '--project'`                                          | `claude plugin install <plugin>@<marketplace> --scope project`                                                                       |
| `/install <plugin>`                        | `Unknown command: /install`                                                  | `/plugin install <plugin>@<marketplace>`                                                                                             |
| `/plugin add <source>`                     | The `/plugin` panel opens on the **Discover** tab                            | `/plugin marketplace add <source>`                                                                                                   |
| `marketplace.anthropic.com` as a source    | `Invalid marketplace source format. Try: owner/repo, https://..., or ./path` | `anthropics/claude-plugins-official` for the official marketplace                                                                    |

These spellings look wrong but work:

- `claude plugins` is an alias of `claude plugin`
- `claude plugin remove` is an alias of `claude plugin uninstall`
- `/plugins` and `/marketplace` in a session open the same panel as `/plugin`


[​](#add-a-marketplace)

Add a marketplace

A marketplace is a catalog you add to Claude Code from a git repository, a URL, or a local path. These entries cover the messages you get when adding one fails or a later refresh fails.


[​](#marketplace-claude-plugins-official-not-found)

`Marketplace "claude-plugins-official" not found`

You ran `/plugin install <plugin>@claude-plugins-official` in a session, and Claude Code reported that it has no marketplace by that name. The official marketplace isn’t registered on this machine yet. Claude Code normally registers it on its own the first time you start an interactive terminal session. It hasn’t run yet if you’ve only used Claude Code through the VS Code extension, and it skips or defers that step:

- When a policy blocks the source
- When `CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL` is set
- After a failed attempt that’s waiting to retry

The `claude plugin` shell commands never register it for you. Add it, then retry the install:

```python
/plugin marketplace add anthropics/claude-plugins-official
```

Claude Code prints `Successfully added marketplace: claude-plugins-official`, and `/plugin marketplace list` shows the marketplace with its source. For any other marketplace name in this message, see [`Marketplace "<name>" not found`](#marketplace-not-found). The same string also appears in the `/plugin` **Errors** tab, the panel’s list of load failures, when a plugin listed in your settings names a marketplace you haven’t added.


[​](#marketplace-not-found)

`Marketplace "<name>" not found`

You ran `/plugin install` in a session and Claude Code reported that it has no marketplace by that name. Two forms of the command reach this message:

- **`/plugin install <plugin>@<name>`**: the install line, often one someone sent you, names a marketplace you haven’t added. The rest of this entry covers finding and adding it.
- **`/plugin install <source>` with a path, URL, or `owner/repo`**: this form reports the message instead of installing, even for a source you’ve already added. To install from a source in one command, see [Add a marketplace and install in one command](discover-plugins.md#add-a-marketplace-and-install-in-one-command).

If the name starts with `claudeai-`, the marketplace is hosted on claude.ai, and you add it by name from your shell with `claude plugin marketplace add --claudeai <name>`. See [Add a marketplace from claude.ai](discover-plugins.md#add-from-claude-ai). For any other name, an install line names a marketplace but doesn’t say where the marketplace is hosted, and Claude Code has no index to look a marketplace name up in. Ask whoever sent the line for the marketplace’s source, which is a GitHub `owner/repo`, a git URL, or a path. Then [add the marketplace](discover-plugins.md#add-a-marketplace) and run the install line again. A marketplace someone sends you is third-party, so [review the plugin before you install it](plugins-security.md#review-a-plugin-before-you-install). If you already added the marketplace, check the spelling against `/plugin marketplace list`.


[​](#invalid-marketplace-source-format)

`Invalid marketplace source format`

You ran `/plugin marketplace add <source>`, `claude plugin marketplace add <source>`, or `claude plugin install <plugin> --marketplace <source>`, and Claude Code replied `Invalid marketplace source format. Try: owner/repo, https://..., or ./path`. Claude Code accepts a source in one of these forms:

- A GitHub `owner/repo` shorthand
- An `https://` or `http://` URL
- A `user@host:path` SSH URL
- A local path starting with `./`, `../`, `/`, or `~`

A bare name such as `claude-plugins-official` matches none of them. Neither does a bare hostname such as `marketplace.anthropic.com`. Retype the source in one of the accepted forms:

```python
/plugin marketplace add anthropics/claude-plugins-official
```

Claude Code prints `Successfully added marketplace: <name>` when the add works.


[​](#is-not-a-valid-github-owner-repo-shorthand)

`'<source>' is not a valid GitHub owner/repo shorthand`

You passed a source with a slash that isn’t `owner/repo`, such as `github.com/owner/repo` or a `gitlab.example.com/group/project` path. Claude Code refused it with this message and a list of accepted forms. The `owner/repo` shorthand is GitHub-only and has to follow GitHub’s naming rules, so a hostname or an extra path segment fails. Pass the source in the form that matches where the marketplace is hosted:

- **A repository on any host**: the full clone URL
- **A hosted `marketplace.json`**: its `https://` URL
- **A local checkout**: `./path` or an absolute path

For example, to add the official marketplace by its clone URL, in a session:

```python
/plugin marketplace add https://github.com/anthropics/claude-plugins-official.git
```

A successful add prints `Successfully added marketplace: <name>`.


[​](#invalid-git-url)

`Invalid git URL`

You added a marketplace, installed a plugin, or ran an update from a git address, and the command failed with `Invalid git URL` in its message. Claude Code checks every git address before running git. It refuses an address whose protocol it doesn’t support. It also refuses an address that git could read as naming a different server or folder than the one the address shows. The text after the address names what to change. Rewrite the address as the message says and run the command again. A refusal that instead says `is blocked by enterprise policy` comes from your organization’s settings. See [Marketplace source is blocked by enterprise policy](#marketplace-source-is-blocked-by-enterprise-policy).


[​](#path-does-not-exist)

`Path does not exist: <path>`

You passed a local path to `marketplace add`, and nothing exists at that path. A relative path resolves against your current directory. Check the resolved path in the message. Then run the command from the directory the relative path starts from, or pass an absolute path to the marketplace directory. A successful add prints `Successfully added marketplace: <name>`. Claude Code accepts a directory that contains `.claude-plugin/marketplace.json`, or a path to a `.json` file. A path to any other file fails with `File path must point to a .json file (marketplace.json)`.


[​](#marketplace-file-not-found-at-claude-plugin-marketplace-json)

`Marketplace file not found at <path>/.claude-plugin/marketplace.json`

Claude Code cloned or downloaded the marketplace but found no `marketplace.json` at the expected path inside it. The add command reports it as `Failed to add marketplace: Marketplace file not found at ...`. The default location is `.claude-plugin/marketplace.json` at the repository root, and the [marketplace reference](plugins-marketplace-reference.md) lists the accepted locations. The fix differs for the owner and for everyone else:

- **You own the marketplace**: put the file at that location and re-add the marketplace
- **Someone else hosts it**: ask the owner for the exact source they publish


[​](#cannot-install-plugins-from-a-marketplace-with-this-name)

`Cannot add marketplace "<name>": Claude Code cannot install plugins from a marketplace with this name`

You added a marketplace, and the [`name`](plugins-marketplace-reference.md#top-level-fields) in its `marketplace.json` isn’t valid as the part after `@` in a [plugin id](plugins-loading.md#find-where-a-plugin-came-from) such as `my-plugin@my-marketplace`. Claude Code refuses the add and registers nothing. The rest of the message states the rule for the name. In this example, `_internal` breaks the rule by starting with `_`:

```python
Cannot add marketplace "_internal": Claude Code cannot install plugins from a marketplace with this name. Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. The name is set by "name" in the marketplace's marketplace.json; ask its maintainer to change it.
```

Give the marketplace a name that fits that rule, then add it again:

- **You own the marketplace**: change `name` in `marketplace.json`, for example to `internal-tools`
- **Someone else hosts it**: ask the owner to change the name

Before v2.1.295, Claude Code reported the add in this example as successful.


[​](#claude-code-reserves-this-name)

`Cannot add marketplace "<name>": Claude Code reserves this name and cannot register a marketplace under it`

You added a marketplace, and the [`name`](plugins-marketplace-reference.md#top-level-fields) in its `marketplace.json` is one of the member names that every JavaScript object has, such as `constructor`, `toString`, or `valueOf`. Claude Code reserves those names, so it refuses the add and registers nothing. [Reserved names](plugins-marketplace-reference.md#reserved-names) lists them. In this example, the marketplace is named `constructor`:

```python
Cannot add marketplace "constructor": Claude Code reserves this name and cannot register a marketplace under it. The name is set by "name" in the marketplace's marketplace.json; ask its maintainer to change it.
```

If a settings file declares the marketplace under [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces), Claude Code’s attempt to add it at startup fails the same way, and the message appears on the **Errors** tab in `/plugin`. Give the marketplace another name, then add it again:

- **You own the marketplace**: change `name` in `marketplace.json`
- **Someone else hosts it**: ask the owner to change the name

Before v2.1.296, adding such a marketplace failed with an internal error in place of this message.


[​](#ssh-authentication-failed-or-https-authentication-failed)

`SSH authentication failed` or `HTTPS authentication failed`

You added or updated a marketplace from a git repository, and the clone failed with `Failed to clone marketplace repository:` followed by one of these lines. First check the repository itself: a misspelled `owner/repo`, a repository that doesn’t exist, or a private repository you can’t see also ends in this message. Open the repository URL in your browser, or run `git ls-remote <url>` in your terminal, to confirm it exists and you have access. If the repository is right, the cause is credentials. Claude Code runs git with interactive prompts disabled, so it can’t ask you for a password, a key passphrase, or a credential the way your terminal would. If git needs to prompt, you see `fatal: Cannot prompt because user interactivity has been disabled` or `terminal prompts disabled` in the original error. Only credentials that already work non-interactively succeed:

- **SSH**: `ssh -T git@<host>` must succeed without prompting for a passphrase, and the host must already be in `known_hosts`
- **HTTPS**: your credential helper must hold a token for the host. For GitHub, run `gh auth login` and `gh auth setup-git`. For another host, store a personal access token in your git credential helper. Test with `git ls-remote <url>`

Once `git ls-remote` succeeds in your terminal without a prompt, run the add or update again. A successful add prints `Successfully added marketplace: <name>`. A successful update prints `Successfully updated marketplace: <name>` from your shell, or `✔ Updated 1 marketplace` in a session. To make Claude Code skip SSH for GitHub `owner/repo` sources, set `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`. Without it, Claude Code clones those sources over SSH when an SSH key for `github.com` looks configured, and falls back to HTTPS when the SSH clone fails. For what background auto-updates can and can’t do with your credentials, see [What background auto-update does with credentials](plugins-host-marketplace.md#what-background-auto-update-does-with-credentials).


[​](#ssh-host-key-is-not-in-your-known-hosts-file)

`SSH host key is not in your known_hosts file`

You added a marketplace over SSH from a host you’ve never connected to, and the clone failed with this line and a `ssh -T git@<host>` hint. For a host whose key changed, the message is `SSH host key has changed` with a `ssh-keygen -R <host>` hint instead. Claude Code clones with `StrictHostKeyChecking=yes`, so it refuses a host whose key you haven’t accepted yet rather than accepting the key automatically. Connect once from your terminal to accept the fingerprint, then retry:

```python
ssh -T git@github.com
```

For a public repository, add the marketplace by its `https://` URL instead to avoid SSH entirely.


[​](#command-git-not-found-or-is-in-an-unsafe-location)

`Command 'git' not found or is in an unsafe location`

On Windows, you added a marketplace and Claude Code reported `Failed to clone marketplace repository: Command 'git' not found or is in an unsafe location (current directory)`. Claude Code looks for `git` on your `PATH` and refuses to run one found only in the current directory. To fix it, install Git and retry:

1

Install Git for Windows

Install Git for Windows so that `git` is on your `PATH`.

2

Open a new terminal

Open a new terminal so the updated `PATH` applies.

3

Confirm git runs

Confirm `git --version` prints a version.

4

Retry the add

Run the `marketplace add` command again.


[​](#git-clone-timed-out-after-120s)

`Git clone timed out after 120s`

You added or updated a marketplace, and it failed with `Git clone timed out after 120s`, followed by a hint to set `CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS`. Cloning a marketplace, and re-cloning one to update it, gets 120 seconds by default. For a large repository or a slow connection, raise the limit. The value is in milliseconds:

- Bash or Zsh

- PowerShell

```python
export CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS=300000
```

```python
$env:CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS = "300000"
```

Then retry in the same shell. If the repository is a monorepo, limit the checkout to the directories you name with `claude plugin marketplace add <source> --sparse <paths>`.


[​](#marketplace-updates-keep-failing-offline)

Marketplace updates keep failing offline

You work in an environment where the marketplace’s git host is unreachable, and every session repeats a failed refresh in the background. Your existing checkout of the marketplace stays in place and startup isn’t delayed. Each session, for a marketplace with [auto-update on](plugins-loading.md#which-marketplaces-and-plugins-auto-update), Claude Code checks the marketplace’s git host for new commits in the background. When that check can’t reach the host, it tries to clone the marketplace again, and offline that clone fails too. Set this variable to skip the re-clone attempt and keep using the existing checkout when the check can’t reach the host:

- Bash or Zsh

- PowerShell

```python
export CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE=1
```

```python
$env:CLAUDE_CODE_PLUGIN_KEEP_MARKETPLACE_ON_FAILURE = "1"
```

With the variable set, Claude Code skips the re-clone only for a checkout that already contains `.claude-plugin/marketplace.json`. A marketplace that was never cloned or whose clone stopped partway still gets the clone attempt, so add it once while online. For a fully offline deployment, pre-populate the plugins directory at image build time with `CLAUDE_CODE_PLUGIN_SEED_DIR` instead, following [Seed containers and CI](plugins-org.md#seed-containers-and-ci).


[​](#marketplace-add-fails-on-a-github-enterprise-server-host)

Marketplace add fails on a GitHub Enterprise Server host

You added a marketplace from a GitHub Enterprise Server (GHES) URL and got a policy error, or you added it from claude.ai and got a GitHub access error. Both cases are on the GHES page:

- [A policy error](../02-Claude-Code-CLI/github-enterprise-server.md#marketplace-add-fails-with-a-policy-error) means your organization restricted marketplace sources and an admin needs to add a `hostPattern` for the host
- [A GitHub access error on claude.ai](../02-Claude-Code-CLI/github-enterprise-server.md#marketplace-add-on-claude-ai-fails-with-a-github-access-error) means your own GitHub Enterprise account isn’t connected yet


[​](#install-a-plugin)

Install a plugin

You added a marketplace and ran an install, and the install stopped with a message instead of installing anything. These entries cover those messages. They also cover the related messages that appear later in the `/plugin` **Errors** tab, or as an empty **Discover** tab, when a plugin or its marketplace can’t be found, read, or trusted.


[​](#plugin-not-found-in-marketplace)

`Plugin "<name>" not found in marketplace "<marketplace>"`

You ran `/plugin install <name>@<marketplace>` or `claude plugin install <name>@<marketplace>`, and the plugin name isn’t in the copy of that marketplace’s catalog on your machine. `claude plugin install` in your shell prints the same message when you haven’t added the marketplace at all. If `claude plugin marketplace update <marketplace>` then answers `Marketplace '<marketplace>' not found`, [add the marketplace](#add-a-marketplace) first.


[​](#the-message-ends-with-a-refresh-hint)

`not found in marketplace` with a refresh hint

The hint reads `Your local copy may be out of date — try claude plugin marketplace update <marketplace>` or `The marketplace couldn't be refreshed (...)`. Claude Code didn’t refresh the marketplace before the lookup, such as when you’re offline, so your copy of the catalog may be stale. Refresh with the marketplace’s name, then install again:

```python
/plugin marketplace update <marketplace>
```

`claude plugin marketplace update` prints `Successfully updated marketplace: <name>`, and `/plugin marketplace update` shows `✔ Updated 1 marketplace`. If the retried install prints the same message, check the name as [`not found in marketplace` with no hint](#the-message-has-no-hint) describes. [When Claude Code refreshes a marketplace before an install](plugins-loading.md#when-claude-code-refreshes-a-marketplace-before-an-install) lists the other cases where the refresh doesn’t run.


[​](#the-message-has-no-hint)

`not found in marketplace` with no hint

The name is the likeliest problem. Open `/plugin`, go to **Discover**, and copy the name from the list. Before v2.1.232, Claude Code refreshed the named marketplace only after the lookup missed, and only when auto-update was on for it.


[​](#plugin-not-found-in-any-marketplace)

`Plugin "<name>" not found in any marketplace`

You ran `/plugin install <name>` with no `@marketplace`, and no registered marketplace has that plugin. `claude plugin install <name>` reports `Plugin "<name>" not found in any configured marketplace`. Without a marketplace name, `claude plugin install` searches the catalogs it already has and doesn’t refresh them first, and `/plugin install` refreshes only marketplaces that have auto-update on. Name the marketplace, and Claude Code refreshes it before looking the plugin up:

```python
/plugin install <name>@<marketplace>
```

When the install works, you see `✓ Installed <plugin>.` in a session, or `Successfully installed plugin: <plugin>@<marketplace>` from `claude plugin install`. If you don’t know which marketplace lists the plugin, run `/plugin marketplace list` for the marketplaces you have, and browse **Discover** in `/plugin` for the plugin name.


[​](#plugin-is-already-installed-globally)

`Plugin '<name>@<marketplace>' is already installed globally`

You ran `/plugin install` for a plugin that’s already installed at user scope or by managed settings, and Claude Code refused with `Use '/plugin' to manage existing plugins.` If you typed the plugin name without `@<marketplace>`, the message omits `globally`. The plugin is already available in every project, so there’s nothing to add. To change its [scope](discover-plugins.md), enable or disable it, or configure it, open `/plugin` and go to **Installed**. A plugin installed only at project or local scope doesn’t trigger this message. Claude Code lets you install it at user scope as well, so it’s available in other projects. `claude plugin install` in your shell prints a different message. For a plugin already installed at the target scope, it prints `Plugin "<name>@<marketplace>" is already installed (scope: user)` and exits 0. If its cache directory is missing, the same command re-downloads it.


[​](#plugin-would-share-its-folder)

`"<plugin>" was not installed: it would share its folder with "<other>"`

You installed a plugin through `claude plugin install`, `/plugin`, or an install suggestion in a session, and Claude Code refused it with this line, or with `would share its saved data with`. The refused plugin’s id and an installed plugin’s id map to the same folder on disk: they are the same once `.` and `@` are written as `-`. On macOS and Windows, ids that differ only in capitals map to the same folder too. Installing both would put one plugin’s files in the other’s folder, so Claude Code refuses and the installed plugin keeps its files. The message names the way out:

- **The other plugin is installed**: the message says `Only one of the two can be installed.` and names the `claude plugin uninstall` command, or the uninstall step in `/plugin`, that removes the other plugin. Run it, then install again. For what the uninstall removes, see [What an uninstall deletes and keeps](plugins-cli-reference.md#what-an-uninstall-deletes-and-keeps).
- **Both ids arrive in one install**, such as a plugin and a dependency it needs: no install order helps. Only a maintainer of the marketplace that lists the two plugins can fix it, by renaming one of them. When the two come from different marketplaces, a maintainer of either one can.


[​](#this-plugin-uses-a-source-type-your-claude-code-version-does-not-suppo)

`This plugin uses a source type your Claude Code version does not support`

You installed a plugin whose marketplace entry uses a source type this version of Claude Code can’t fetch, and Claude Code stopped with this message and `Update Claude Code and try again.` Update Claude Code, then retry the install. Source types are on the [marketplace reference](plugins-marketplace-reference.md).


[​](#plugin-archive-integrity-check-failed)

`Plugin archive integrity check failed`

You installed a plugin that’s distributed as a zip archive, and Claude Code refused it with this line and `The archive was not installed.` The plugin’s marketplace entry uses an [`archive` source](plugins-marketplace-reference.md) with a `sha256` pin, and the downloaded file’s digest doesn’t match the pin. The full message looks like this:

```python
Plugin archive integrity check failed for https://artifacts.example.com/claude-plugins/my-plugin.zip: expected sha256 6bfa50e3d2e00c052b46abe51fff89346ac803e45771f76dcf6df1ab74cca5e1, got ac52220c0914ef8ca6a602e4a7362f88d30fb021110f72a6d15b68c3fe7df2b7. The archive was not installed. Verify the sha256 in the marketplace entry, or that the URL serves the intended file.
```

The fix differs for the publisher and the installer:

- **You publish the plugin**: recompute the digest of the exact file the URL serves and update the `sha256` in the marketplace entry. Use `shasum -a 256 my-plugin.zip`, or `Get-FileHash -Algorithm SHA256 my-plugin.zip` in PowerShell
- **You install the plugin**: run `/plugin marketplace update <name>` in a session to refresh the catalog in case the entry was corrected, then retry the install. If the digests still disagree after the refresh, ask the marketplace owner which file they pinned before installing


[​](#an-npm-plugin-source-must-name-a-registry-package)

`An npm plugin source must name a registry package`

A plugin whose marketplace entry uses an [`npm` source](plugins-marketplace-reference.md#npm-plugin-source) failed to install, update, or load, and the message includes this sentence. Claude Code checked the entry’s `package` value before fetching anything and refused it. The message names the value and the reason:

```python
"github:acme/formatter" was not installed: it is not an http or https link. An npm plugin source must name a registry package (name or name@version) or link to a tarball file. For a plugin in a git repository, use a "github", "url" or "git-subdir" source.
```

The marketplace’s owner has to change the entry:

- **If that’s you**: change `package` to a value the [npm plugin source reference](plugins-marketplace-reference.md#npm-plugin-source) accepts, or switch the entry to a `github`, `url`, or `git-subdir` source
- **If it isn’t you**: report the message to the marketplace owner


[​](#marketplace-is-registered-from-an-untrusted-source)

`Marketplace "<name>" is registered from an untrusted source`

A marketplace you added earlier stopped loading, and so did its plugins. This line appears in the `/plugin` **Errors** tab or on the next refresh. The marketplace is registered under a name that is [reserved for official Anthropic marketplaces](plugins-marketplace-reference.md), but its registered source isn’t an `anthropics` GitHub repository. Reserved names are re-checked every time a marketplace loads or refreshes, so the marketplace and the plugins installed from it stop loading. The full message names the reserved name and the fix:

```python
Marketplace "claude-community" is registered from an untrusted source: The name 'claude-community' is reserved for official Anthropic marketplaces. Only repositories from 'github.com/anthropics/' can use this name. To fix it, remove the marketplace and re-add it from the official source.
```

The fix differs for users and publishers:

- **You use the marketplace**: in your shell, run `claude plugin marketplace remove <name>`, then add the marketplace again from the official `github.com/anthropics` repository
- **You publish a third-party marketplace that used the name before it became reserved**: rename it and ask users to re-add it from your source

Before v2.1.205, Claude Code checked the name only when you added the marketplace, so an entry registered before its name became reserved kept loading.


[​](#marketplace-is-added-but-ignored)

`Marketplace "<name>" is added but ignored`

The marketplace has an entry in `~/.claude/plugins/known_marketplaces.json`, but the entry failed a check Claude Code runs every time it reads that file, so the marketplace and the plugins installed from it stop loading. In your shell, `claude plugin list` reports each affected plugin with a line that names the reason and the fix:

```python
Marketplace team-tools is added but ignored. Its location is on a network drive, has "." or ".." in its path, or couldn't be checked. Re-add the marketplace (one added from a folder or file must be re-added from a copy on this computer), or, to trust a folder on a network drive, declare it under extraKnownMarketplaces in user or managed settings.
```

In a session, the `/plugin` **Errors** tab puts the marketplace name in quotation marks, ends the line after the reason, and shows the fix on the line under it. The sentence after `is added but ignored` names the check the entry failed:

- `Its location is on a network drive, has "." or ".." in its path, or couldn't be checked`, or the same sentence about `The folder or file it was added from`: the marketplace’s directory, or the local path it was added from, is on a network location, has a `.` or `..` segment in its path, or couldn’t be checked
- `Its git URL can't be used: <reason>` or `Its URL can't be read as an https:// or http:// address`: the entry’s recorded source URL is one Claude Code refuses to clone or fetch from
- `Its source doesn't match its extraKnownMarketplaces entry in user or managed settings`: the entry doesn’t match the [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) declaration of the same name

When `(see the debug log)` follows `is added but ignored` in place of a reason, Claude Code refuses the marketplace’s name, such as [another spelling of a reserved name](../02-Claude-Code-CLI/errors.md#marketplace-name-is-another-spelling-of-a-reserved-name). The [debug log](../02-Claude-Code-CLI/debug-your-config.md) names the entry. **What to do:**

- Follow the fix in the message. In your shell, run `claude plugin marketplace remove <name>`, then add the marketplace again from a supported source or a local path and reinstall its plugins, which the remove command uninstalls. The remove command works on an ignored entry
- To keep a marketplace on a network location, declare it under [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) in your user or managed settings; a declaration in a repository’s `.claude/settings.json` or `.claude/settings.local.json` doesn’t count
- For a source that differs from its settings declaration, re-add the marketplace from the declared source or change the declaration. `claude plugin marketplace add` refuses the same mismatch; see [the matching `Cannot add marketplace` entry](#cannot-add-marketplace-source-doesnt-match)
- For a refused name, remove the marketplace, using the command after `Remove it:` when the line gives one; adding it again under the same name is refused again

Before v2.1.286, whatever the reason, `claude plugin list` reported such a marketplace as `Marketplace <name> not found`, and the `/plugin` **Errors** tab reported it as `Marketplace "<name>" is registered but was refused (see the debug log)`. The reason appeared only in the debug log. In v2.1.286, the reason and fix sentences used different wording, such as `Its recorded location is network-shaped or unclassifiable (never probed)`.


[​](#plugin-has-a-corrupt-manifest-file-or-has-an-invalid-manifest-file)

`Plugin <name> has a corrupt manifest file` or `has an invalid manifest file`

Claude Code fetched the plugin, then failed to read its `.claude-plugin/plugin.json`. In the shell, the `<name>` in this line can be a temporary directory name; the `Failed to install plugin "<name>@<marketplace>"` prefix carries the plugin’s real name. The wording says which check failed:

- **`corrupt manifest file`, followed by `JSON parse error:`**: the file isn’t valid JSON
- **`invalid manifest file`, followed by `Validation errors:`**: the file parses but fails the schema, such as `name: Invalid input` for a missing required field

`claude plugin install` reports either as `Failed to install plugin "<name>@<marketplace>":` and exits with code 1. The plugin’s author has to fix the file, and the plugin can’t be installed until then:

- **If that’s you**: run `claude plugin validate <plugin-directory>` in your shell to see the same error with the offending path, then fix the file
- **If it isn’t you**: report the message to the marketplace owner


[​](#plugin-directory-not-found-at-path)

`Plugin directory not found at path: <path>`

The **Errors** tab in `/plugin` shows this for an enabled plugin that its marketplace lists by a relative path, such as `./plugins/my-plugin`, when no directory exists at that path inside the marketplace. If you maintain the marketplace, correct the entry’s `source` path or restore the folder. Otherwise, report the message to the marketplace owner. `Marketplace directory not found at path: <path>` means the marketplace’s own directory is missing instead. For a marketplace you added from a local path, that directory moved or was deleted. Restore it, or remove the marketplace and add it again from its new location.


[​](#no-plugins-available-or-no-marketplaces-configured)

`No plugins available` or `No marketplaces configured`

You opened `/plugin` and the **Discover** tab is empty, or `claude plugin marketplace list` printed `No marketplaces configured`. No marketplace is registered, so there’s no catalog to show. In a session, add the official marketplace, `anthropics/claude-plugins-official`:

```python
/plugin marketplace add anthropics/claude-plugins-official
```

Claude Code prints `Successfully added marketplace: claude-plugins-official`, and **Discover** lists its plugins. The [Anthropic marketplaces](plugins-anthropic-marketplaces.md) page lists the other marketplaces you can add.


[​](#marketplace-is-already-added-from-a-different-source)

`Marketplace "<name>" is already added from a different source`

You named a new marketplace source with [`--marketplace <source>` on the install command](discover-plugins.md#add-a-marketplace-and-install-in-one-command), in a session or from your shell. The catalog Claude Code fetched from that source has the same name as a marketplace you already added from a different source. Claude Code keeps the existing marketplace instead of replacing it, and the plugin isn’t installed. The full message looks like this:

```python
Marketplace "acme-tools" is already added from a different source (github:acme/plugins). To use this source instead, remove that marketplace first with /plugin marketplace remove acme-tools.
```

Choose which source you want:

- **The marketplace you already added**: install from it by name with `/plugin install <plugin>@<name>`
- **The new source**: run `/plugin marketplace remove <name>`, then retry the install


[​](#cannot-add-marketplace-source-doesnt-match)

`Cannot add marketplace "<name>": its source doesn't match its extraKnownMarketplaces entry in user or managed settings`

You added a marketplace, and the `name` in its `marketplace.json` already has an [`extraKnownMarketplaces`](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) entry in your user settings or managed settings. The source you gave differs from the one that entry lists, so Claude Code refuses the add and registers nothing. Two sources match when they have the same type and the same value in every field. An entry that sets a `ref` you didn’t pass counts as different. A `github` entry also counts as different when you gave the repository as an `https://github.com/` URL, because Claude Code records that URL as a [`git` source](plugins-marketplace-reference.md#marketplace-sources). Do one of these:

- **Use the declared source**: Claude Code [registers marketplaces declared in settings](../02-Claude-Code-CLI/settings-reference.md#extraknownmarketplaces) on its own, so first run `/plugin marketplace list` in a session. If the list shows the name, the marketplace is already registered and there’s nothing to add. If the list doesn’t show it, add it with the source typed the way the entry writes it. For an entry whose `source` object is `{ "source": "github", "repo": "acme-corp/claude-plugins", "ref": "v1.2.0" }`, run this:

  ``` shiki
  /plugin marketplace add acme-corp/claude-plugins#v1.2.0
  ```
- **Use the new source**: edit or remove the `extraKnownMarketplaces` entry, then add the marketplace again. If managed settings declare it, ask your administrator

Before v2.1.287, the message read `Cannot add marketplace "<name>": its network source differs from the one declared for it in settings (kind, target, or a fetch-shaping field such as headers / ref / path / sparsePaths)`.


[​](#failed-to-install-from-the-plugin-menu)

`Failed to install: <plugin> (<reason>)`

You selected plugins to install in the `/plugin` menu, none of them installed, and the menu closed with this summary of what failed. Some reasons, such as git’s output after a failed clone, show only their first line. When such a reason was shortened, the summary ends with `Installing a plugin from its details (Enter) in /plugin shows its full error.` What to do depends on whether the summary shortened the reason:

- Fix what the reason in parentheses names
- When the reason was shortened, run `/plugin`, select the plugin on the **Discover** tab, and press **Enter** to install it from its details. If the install fails there, the details view shows the whole error


[​](#could-not-move-the-new-copy-of-this-plugin-version)

`Could not move the new copy of this plugin version into <path>`

When you install a plugin, Claude Code downloads a fresh copy of its files and moves it into that version’s folder in the [plugin cache](plugins-loading.md#find-plugins-on-disk). This message means the move failed, usually because another program was using the folder while the install ran. The file-system code appears in parentheses:

```python
Could not move the new copy of this plugin version into /home/user/.claude/plugins/cache/acme-tools/formatter/1.2.0: the new copy or the version folder stayed busy while the install ran (ENOTEMPTY) — usually a scanner still reading the freshly downloaded files, another program using that folder, or another process re-creating it. The previously installed copy was moved back. Run the install again once other Claude Code sessions or programs using that folder have finished.
```

The message says what happened to the copy that was installed before, which tells you whether the plugin still works:

- `The previously installed copy was moved back`: the version you had is still installed
- `had to be removed first`, `was not moved back`, or `could not be moved back`: that plugin version isn’t installed until an install succeeds
- No such sentence: there was no earlier copy, so the version isn’t installed yet

On Windows, when another program holds the installed copy itself, the message instead says that copy `could not be replaced` and that `It was not replaced and the new copy was discarded`, so the version you had is still installed. A `Left on disk` list names set-aside folders inside the cache. A later install of that version or a plugin cache cleanup removes them, so you don’t need to delete them. To fix the install:

- Close other Claude Code sessions, editors, and terminals that are using the plugin’s folder under `~/.claude/plugins/cache`, then run the install again
- When the message says to check the plugin cache folder’s permissions, restore your write permission on the folder it names and free disk space, then run the install again


[​](#dependency-errors)

Dependency errors

A plugin that declares dependencies can fail to install, or install and stay disabled, when a dependency can’t be satisfied. The message reaches you at install time or at load time:

- **During install**: the refusal comes back as the install’s error message
- **When the plugin loads**: the problem appears in `claude plugin list` and the `/plugin` **Errors** tab, and Claude Code keeps the affected plugin disabled until you resolve it

The table lists each message and its fix. To declare dependencies as an author, see [Plugin dependencies](plugins-dependencies.md).

| Message                                                                                                                                                            | Meaning                                                                                                                                                                                                            | How to resolve                                                                                                                                                                                                                                                      |
|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Dependency "<dep>" is not installed`                                                                                                                              | A declared dependency isn’t installed.                                                                                                                                                                             | Install it in your shell with `claude plugin install <dep>@<marketplace>`, or uninstall the plugin. If the dependency’s marketplace isn’t registered yet, add it and run `/reload-plugins` in your session, which installs the missing dependencies it can resolve. |
| `"<name>@synced" is loaded from your claude.ai account but meets a marketplace dependency only for other claude.ai plugins`, inside the `is not installed` message | The dependency names a marketplace, and a plugin of that name is loaded from your claude.ai account. The plugin that declares the dependency doesn’t load from claude.ai, so the claude.ai plugin doesn’t meet it. | In your shell, run `claude plugin install <dep>@<marketplace>` to install the dependency from its marketplace. The marketplace copy then loads instead of the claude.ai one.                                                                                        |
| `Dependency "<dep>" is disabled`                                                                                                                                   | The dependency is installed but turned off.                                                                                                                                                                        | Enable the dependency, or uninstall the plugin that needs it.                                                                                                                                                                                                       |
| `Requires "<dep>" <range>, installed <version>`                                                                                                                    | The installed dependency’s version is outside the plugin’s declared range.                                                                                                                                         | Update the dependency to a version in the range, or uninstall the plugin.                                                                                                                                                                                           |
| `<Plugin or Dependency> "<name>" has conflicting version requirements`                                                                                             | No version satisfies every range that pins it. The message lists the ranges.                                                                                                                                       | Uninstall or update one of the conflicting plugins, or ask the upstream author to widen its constraint.                                                                                                                                                             |
| `... has version requirements too complex to intersect` or `has an invalid version requirement`                                                                    | A range isn’t valid semver, or the combined ranges can’t be intersected.                                                                                                                                           | Fix the invalid range or simplify long `||` chains.                                                                                                                                                                                                                 |
| `... has no git tag satisfying <range>`                                                                                                                            | The dependency’s repository has no `<name>--v*` tag in the range.                                                                                                                                                  | Check that the upstream tags releases with that convention, or relax the range.                                                                                                                                                                                     |
| `Dependency "<dep>" (required by <plugin>) is in <marketplace>, which is not in the allowlist`                                                                     | The dependency is in a different marketplace, and cross-marketplace resolution is off by default.                                                                                                                  | Install the dependency yourself at the same scope, in your shell with `claude plugin install <dep>@<marketplace>` plus the `--scope` you’re installing the plugin at, then retry.                                                                                   |

To see these programmatically, run `claude plugin list --json` in your shell. Plugins with problems carry an `errors` field with the messages and an `errorDetails` field with a `type` for each: the first three rows are `dependency-unsatisfied` and the fourth is `dependency-version-unsatisfied`.


[​](#plugin-installed-but-not-working)

Plugin installed but not working

The install succeeded, but the plugin’s skills, hooks, or servers aren’t doing anything. Start with [Plugin doesn’t appear or its skills don’t show up](#plugin-doesnt-appear-or-its-skills-dont-show-up), which tells you where Claude Code reports what it loaded, then match the message.


[​](#plugin-doesnt-appear-or-its-skills-dont-show-up)

Plugin doesn’t appear or its skills don’t show up

You installed a plugin and typed `/` expecting its skills, or asked Claude to use it, and nothing happened. Check the plugin’s state before changing anything:

1

Confirm the plugin is installed and enabled

Run `/plugin` and open **Installed**. Confirm the plugin is listed and enabled. `claude plugin list` in your shell prints the same list with each plugin’s version, scope, and `Status: ✔ enabled`.

2

Read the Errors tab

Open the **Errors** tab in the same panel. Each entry pairs a message with a guidance line. Most messages in the rest of this section come from that tab.

3

Reload if you installed during this session

If the plugin is installed and error-free but you installed it during this session, run `/reload-plugins`. It prints `Reloaded:` with counts of plugins, skills, agents, hooks, and servers. When something failed it adds `N errors during load. Run /plugin for details.`

If the plugin loads with no error and its skills still don’t appear, the next step differs for your own plugin and for someone else’s:

- **A plugin you’re building**: see [Plugin loads but its skills are missing](#plugin-loads-but-its-skills-are-missing)
- **A plugin someone else published**: open **Installed** in `/plugin` and open the plugin’s details pane, which lists what the plugin contains. A plugin that lists no skills there has none to offer when you type `/`


[​](#run-reload-plugins-to-activate)

`Run /reload-plugins to apply.`

The install summary in `/plugin` ended with `Run /reload-plugins to apply.` instead of `Plugin is now active.` A `Plugins changed. Run /reload-plugins to activate.` notice can appear above the prompt at the same time. Claude Code didn’t activate the plugin during the install, either because activating it would [invalidate the prompt cache](../02-Claude-Code-CLI/prompt-caching.md#enabling-or-disabling-a-plugin) or because the activation attempt failed. You don’t need to type the command. The panel closes and Claude Code runs `/reload-plugins` for you, or queues it until the response that’s streaming finishes. Read what that reload prints:

- **`Reloaded:` with counts of plugins, skills, agents, hooks, and servers**: the plugin is now active. When something failed to load, the line adds `N errors during load. Run /plugin for details.`
- **`This reload changes MCP tools (...) — your next message will re-read the whole conversation instead of using the cache. Run /reload-plugins --force to apply.`**: the reload would add or remove a plugin MCP server, or the `LSP` tool, and invalidate your prompt cache. For the LSP case the line starts `This reload adds the LSP tool` or `This reload removes the LSP tool`. Run it with `--force` to activate the plugin anyway, or start a new session

Before v2.1.268, an install that didn’t activate during the install stayed pending until you ran `/reload-plugins` yourself. Before v2.1.246, the skills count in that summary included only a plugin’s `commands/` entries, so a reload could load a plugin’s `SKILL.md` skills and still report `0 skills`.


[​](#the-packages-it-lists-are-not-installed)

`The packages it lists are not installed` or `were not installed, because ...`

`/plugin` and `claude plugin list` show one of these notes on a plugin whose dependency install left no `node_modules` directory. The plugin loads, but the parts that need the missing packages may not work.

- **`are not installed`**: the install can run for this plugin but didn’t finish, for example because it failed or timed out. To retry the install, run the `claude plugin update` command that the note gives, in your shell, or update the plugin from `/plugin`

  ``` shiki
  claude plugin update formatter@my-marketplace
  ```

  If the retry fails, the output gives the cause. While `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set, `claude plugin update` and `/plugin` skip the retry and report that the plugin is at its latest version.
- **`were not installed, because ...`**: the install can’t run for this plugin, and the note names the reason, such as a Yarn, pnpm, or `bun.lockb` lockfile, or a lockfile whose package manager isn’t installed on this computer. Updating the plugin doesn’t install the packages while that reason stands. If the reason is the lockfile, the plugin’s author has to replace it. If it’s a missing package manager, install it, then update the plugin


[​](#plugin-not-cached-at)

`Plugin "<name>" not cached at <path>`

The **Errors** tab shows this line with the guidance `Run /plugin to refresh the plugin cache`. Claude Code has an install record for the plugin, but the directory the record points at is missing, for example after you cleared the cache. Reinstall the plugin from your shell. `claude plugin install <name>@<marketplace>` re-downloads a plugin whose install directory is missing even though its record exists:

```python
claude plugin install <name>@<marketplace>
```

Then run `/reload-plugins` in your session. The **Errors** tab entry disappears and the plugin is back under **Installed**.


[​](#installed-plugins-json-holds-a-record-this-version-cannot-read)

`installed_plugins.json holds a record under "<id>" that this version of Claude Code cannot read`

The message surfaces in these forms:

- **`claude plugin list`**: prints it as a `Note:`
- **`claude plugin install`, `uninstall`, and `update`**: refuse with `Plugin "<name>" was not installed:`, `Plugin "<name>" was not uninstalled:`, or `Plugin "<name>" was not updated:`, followed by the same text
- **`--json` on any of those three commands**: the result line carries the same `message` and `failureCode: "install_records_unreadable"`
- **Several such records**: the message reads `holds records under`
- **The whole file declares a format this version doesn’t know**: the message reads `installed_plugins.json is in a format (version <N>) that this version of Claude Code does not know` instead

The named record in `installed_plugins.json` is valid JSON under a valid plugin id, but its fields don’t parse for this version. Most likely another version of Claude Code wrote it, perhaps a newer one. While the record is there, this version doesn’t rewrite the file, so the record isn’t lost. Take the message’s options in order:

1.  Update Claude Code with `claude update`.
2.  If you can’t update, uninstall the named plugin with the version of Claude Code that wrote the record.
3.  If neither helps, delete the record from `installed_plugins.json` by hand, then restart Claude Code or run `/reload-plugins`.


[​](#installed-plugins-json-could-not-be-read-and-was-rebuilt)

`installed_plugins.json could not be read and was rebuilt`

`claude plugin list` prints this note, with the path of a kept file named `installed_plugins.unreadable.<date>.<hash>.kept`, for as long as that file sits beside `installed_plugins.json`. An `installed_plugins.json` that isn’t valid JSON, or isn’t a list of plugins, can’t say what you installed. Open the `.kept` file to see what the old file recorded, and reinstall the plugins you’re missing. Claude Code never reads the file back, and the file ages out on the [`cleanupPeriodDays`](../02-Claude-Code-CLI/settings-reference.md#cleanupperioddays) schedule.


[​](#install-records-under-names-that-no-version-can-use)

`install records under names that no version of Claude Code can use were removed from installed_plugins.json`

`claude plugin list` prints this note, with the path of a copy named `installed_plugins.set-aside.<date>.<hash>.json`, for as long as that copy sits beside `installed_plugins.json`. The note ends `Nothing needs doing about these copies.` A record in `installed_plugins.json` sat under a key that isn’t a valid plugin id, so no version of Claude Code can use it. The rest of the file loads normally. Claude Code copies the unusable records into the `.set-aside` file and drops them from the list. Claude Code never reads the copies back, and the copies age out on the [`cleanupPeriodDays`](../02-Claude-Code-CLI/settings-reference.md#cleanupperioddays) schedule.


[​](#does-not-load-so-claude-code-ignores-the-whole-file)

`does not load (...), so Claude Code ignores the whole file`

The command worked. The settings file the warning names has an error, so Claude Code ignores the whole file, including anything the command wrote there, until you fix it. Fix the error the warning names. For a value Claude Code doesn’t accept, [Fix a broken settings file](../02-Claude-Code-CLI/settings.md#fix-a-broken-settings-file) says how. Then run the command again if its change is no longer in the file. The warning follows the success line of `claude plugin install`, `enable`, `disable`, or `claude plugin marketplace add` in your shell:

```python
⚠ /home/user/.claude/settings.json does not load (its "permissions" is not valid), so Claude Code ignores the whole file, including anything this command wrote there. Fix the file, then run this command again if its change is missing. If a newer Claude Code wrote the file, update Claude Code instead.
```

The text in parentheses names the error:

- **`its "<key>" is not valid`**: the quoted setting holds a value Claude Code doesn’t accept. Look up the setting in the [settings reference](../02-Claude-Code-CLI/settings-reference.md) for the values it takes. When more than one value fails, the text names the first setting and counts the others, as in `its "permissions" and 1 other value are not valid`.
- **`it is not a JSON object`**: the file’s top level isn’t a JSON object, such as a file whose top level is an array.


[​](#a-plugin-you-disabled-still-loads)

`Disabled in ~/.claude/settings.json but still loads`

You set a plugin to `false` in `~/.claude/settings.json`, and its row in `claude plugin list` or `/plugin` shows this message followed by the source that enables it, such as `— project settings enable it, which overrides your user setting`. A `true` in that higher-precedence source is overriding your user setting. To opt out of a project-enabled plugin on your machine, set the id to `false` in `.claude/settings.local.json`, which has higher precedence than the project file. For the other sources the message can name, see [Disabled in user settings but still loads](plugins-loading.md#disabled-in-user-settings-but-still-loads). If `claude plugin list` instead marks the plugin `required by your org`, no settings file is involved: your organization marks that synced plugin as required on claude.ai, and it loads even if you disabled it earlier. See [Plugins synced from claude.ai](plugins-loading.md#synced-plugins).


[​](#plugin-is-enabled-in-project-settings-but-isnt-installed-here)

`Plugin "<name>" is enabled in project settings but isn't installed here`

The **Errors** tab shows this line for a plugin your project’s `.claude/settings.json` enables, with the guidance `Run claude plugin install <name>@<marketplace> --scope project to install it for this project`. A repository’s settings can enable a plugin for everyone who opens it, but they don’t install it. When the plugin comes from an external source such as a GitHub repository or an npm package, Claude Code doesn’t download it until you install it yourself. Run the command from the guidance line in your shell, then reload:

```python
claude plugin install <name>@<marketplace> --scope project
```

After you run `/reload-plugins` in your session, the **Errors** tab entry is gone and the plugin is listed under **Installed**. If your organization pre-installs plugins for you, it does so through managed settings instead. See [Pre-install and require plugins](plugins-org.md#pre-install-and-require-plugins).


[​](#a-plugin-stays-installed-after-plugin-uninstall-on-windows)

A plugin stays installed after `plugin uninstall` on Windows

On Windows, you run `claude plugin uninstall` at project or local scope and it reports success, but `claude plugin list` or `/plugin` still lists the plugin. `installed_plugins.json` held two install records of the plugin for the project folder, each spelling the folder’s path differently, and one uninstall removes one of them. To check, run `claude plugin list --json` in your shell. The plugin’s remaining row has a `projectPath` that spells the folder differently from where you ran the uninstall, such as `c:\work\app` for `C:\work\app`. Run the same uninstall command again, with the same `--scope`, from the same folder. The second run finds no record under its own spelling of the path, so it removes the one under the other spelling. For a project-scope install:

```python
claude plugin uninstall <name>@<marketplace> --scope project
```

Then run `claude plugin list --json` again to confirm that the row is gone. Before v2.1.295, the second run fails with `Plugin "<name>" is not installed in project scope`. Run `claude update`, then run the uninstall again.


[​](#a-plugin-installed-from-a-git-repository-shows-version-unknown)

A plugin installed from a git repository shows version `unknown`

`Version: unknown` in `claude plugin list` can mean Claude Code couldn’t read the commit it uses as a git-sourced plugin’s version. The plugin may have its own repository or be inside a git-hosted marketplace. Run these commands in your shell, replacing `formatter@my-marketplace` with the plugin’s `name@marketplace` line from `claude plugin list`:

1

Update Claude Code

[Update Claude Code](../01-Getting-Started/setup.md#update-claude-code) to v2.1.302 or later, which clones in a format it can read the commit from:

```python
claude update
```

2

Update the marketplace

Update Claude Code’s copy of the marketplace, which holds the commit for plugins inside the marketplace repository:

```python
claude plugin marketplace update my-marketplace
```

3

Update the plugin

Update the plugin to rewrite its recorded version:

```python
claude plugin update formatter@my-marketplace
```

4

Check the version

Check the `Version:` line for a value such as a [12-character commit ID](plugins-loading.md#how-claude-code-computes-the-version):

```python
claude plugin list
```

`claude plugin update` [changes one scope’s block per run](plugins-cli-reference.md#which-scope-the-command-updates). For a block still showing `unknown`, rerun it with that block’s `Scope:` value:

```python
claude plugin update formatter@my-marketplace --scope project
```

If the block still shows `unknown` after the rerun, see [Get more help](../02-Claude-Code-CLI/troubleshooting.md#get-more-help).


[​](#failed-to-load-hooks-from-and-hooks-that-dont-fire)

`Failed to load hooks from <path>` and hooks that don’t fire

A plugin’s hooks don’t run, or one blocks an action. Either the **Errors** tab shows a load failure for them, the hooks load and you see `<Event> hook error` notices or a blocking error in the transcript, or a hook loads without error and never fires.


[​](#hooks-fail-to-load)

Hooks fail to load

The **Errors** tab shows one of these messages:

- **`Failed to load hooks from <path>: <reason>`**: `hooks/hooks.json` isn’t valid JSON or fails the hooks schema. The reason names the parse or validation error. Fix the file. To catch a JSON syntax problem in `hooks/hooks.json` before you publish the plugin, run `claude plugin validate <plugin-directory>` in your shell
- **`hooks path not found: <path>`**: the manifest’s `hooks` field names a file that doesn’t exist at that path relative to the plugin root. Fix the path or add the file
- **`hooks path escapes plugin directory: <path>`**: see [Path escapes plugin directory](../02-Claude-Code-CLI/errors.md#path-escapes-plugin-directory)


[​](#hook-error-notices-in-the-transcript)

`hook error` notices in the transcript

A notice of the form `... hook error: Failed with non-blocking status code: <stderr>` means the hook ran and its command failed. For example, `Stop hook error: Failed with non-blocking status code: /bin/sh: node: command not found` means the shell Claude Code spawned couldn’t find `node`. Install it, or make sure it’s on the `PATH` of the terminal you start `claude` from. If the stderr shows the plugin’s path cut off at a space, the hook’s shell-form command uses `${CLAUDE_PLUGIN_ROOT}` outside quotes and the install path contains a space. Wrap the variable in double quotes or use [exec form](../07-Hooks/hooks.md#exec-form-and-shell-form). To find the unquoted variable, run `claude plugin validate` on the plugin’s directory and look for its [quoting warning](plugins-reference.md#quoting-and-path-separators). If the notice reads `Failed to run: Plugin directory does not exist: <path>`, see [`Plugin directory does not exist`](#plugin-directory-does-not-exist). For any other error, run the hook’s command yourself from the plugin directory to see the full output, or capture the full stderr with [debug logging](../07-Hooks/hooks.md#debug-hooks).


[​](#a-plugin-hook-blocks-a-tool-call-or-prompt)

A plugin hook blocks a tool call or prompt

A hook that exits with code 2 [blocks the action it ran for](../07-Hooks/hooks.md#exit-code-2). When a plugin’s hook blocks this way and its stderr is the blocking message, the error ends with `This hook comes from the <plugin> plugin.` so that you know which plugin to disable or fix. Before v2.1.281, the error didn’t name the plugin. If that message shows the plugin’s path cut off at a space, apply the [unquoted `${CLAUDE_PLUGIN_ROOT}` fix](#hook-error-notices-in-the-transcript).


[​](#hook-loads-but-never-fires)

Hook loads but never fires

If a hook loads without error but never fires, first run `/plugin` in your session and open the plugin’s details. A note there that begins `Its hooks.json hooks do not run` means another enabled plugin with the same name registered its hooks instead, and [Hooks when two enabled plugins share a name](plugins-loading.md#hooks-when-two-enabled-plugins-share-a-name) says which copy that is and how to switch. Otherwise, check the hook’s definition and then watch it run:

1

Check the event name

Event names are case-sensitive, so confirm yours matches exactly, for example `PostToolUse`.

2

Check the matcher

Confirm the hook’s `matcher` matches the tool name.

3

Trigger the event on purpose

For a `PostToolUse` hook, ask Claude to edit a file.

4

Read the debug log

Open the [debug log](../07-Hooks/hooks.md#debug-hooks), which records which hooks matched. A hook that ran shows up there with its exit code.


[​](#plugin-directory-does-not-exist)

`Plugin directory does not exist: <path>`

Run `/reload-plugins` at the Claude Code prompt first, even though the message says to reinstall. A plugin’s hook fails with `Failed to run: Plugin directory does not exist: <path> (<plugin> — run /plugin to reinstall)`, and the hook doesn’t run, when the directory your session loaded the plugin’s hooks from is gone from disk. [`Plugin directory not found at path: <path>`](#plugin-directory-not-found-at-path) is a different message, about a marketplace entry. The reload loads the plugin’s hooks from its current directory. The failure is shown once per session for each hook event and command, so the hook going quiet doesn’t confirm the fix. Read the reload’s output instead:

- **`Reloaded:` with no errors line**: the plugin’s hooks no longer point at the missing directory
- **`N errors during load. Run /plugin for details.`**: open the **Errors** tab in `/plugin` and follow this page’s entry for the message it shows
- **A line that ends `Run /reload-plugins --force to apply.`**: nothing reloaded, and the hooks keep failing. Run `/reload-plugins --force` at the Claude Code prompt


[​](#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start)

`Invalid MCP server config for "<server>"` and MCP servers that don’t start

A plugin bundles an MCP server, and the **Errors** tab shows `Invalid MCP server config for "<server>": <error>`, or the server is listed but `/mcp` never shows it connected.


[​](#invalid-mcp-server-config-for-%3Cserver%3E-%3Cerror%3E)

`Invalid MCP server config for "<server>": <error>`

The server’s configuration passes the schema check, but Claude Code can’t resolve it for this session. The text after the colon names the cause and decides the fix:

- **`Missing environment variables: <names>`**: set those variables in the shell you start Claude Code from, then start a new session
- **`URL is unset or invalid`**: a `${user_config.*}` option that the URL uses isn’t set. Run `/plugin configure <plugin>` to set it
- **`has an invalid MCP url`** or **`headersHelper for MCP server '<server>' references ${user_config.*}`**: the plugin’s own configuration is at fault. Fix the `url` or `headersHelper` in your plugin’s MCP configuration, or report it to the plugin’s author if the plugin isn’t yours. The `headersHelper` case has its own entry under [plugin command references user_config](../02-Claude-Code-CLI/errors.md#plugin-command-references-user-config)


[​](#bundled-mcp-server-%3Cname%3E-was-not-started-it-needs-configuration)

`Bundled MCP server "<name>" was not started: it needs configuration`

The plugin includes the server as an [MCPB bundle](plugins-components.md#include-a-packaged-mcpb-server) that declares `user_config`, and a required setting has no saved value yet or a saved value fails the bundle’s own validation, so Claude Code skips starting the server. The rest of the plugin works. Select the plugin on `/plugin`’s **Installed** tab and choose **Configure** to supply the values. After you save, `/plugin` shows `Configuration saved.` and closes, and Claude Code reloads plugins as described under [Manage installed plugins](discover-plugins.md#manage-installed-plugins). The server starts once that reload applies. Before v2.1.285, Claude Code skipped the server without showing this line.


[​](#server-is-configured-but-never-connects)

Server is configured but never connects

Run `/mcp` to see the server’s status. When the server is healthy, `/mcp` lists it as connected. To read the error the server printed while starting, run `claude --debug` and open the log at `~/.claude/debug/<session-id>.txt`. The `--debug` flag doesn’t print to the terminal.

When a connection to a [plugin’s stdio server](../06-MCP-Tools/General/mcp.md#plugin-provided-mcp-servers) failed less than 15 minutes before you ran `claude --debug`, Claude Code [doesn’t start the server](../02-Claude-Code-CLI/errors.md#skipping-connection-recent-failure-cached). In the new session, select **Reconnect** from the server’s menu in `/mcp` to start it, then open the log.

A server entry in `.mcp.json` that fails the schema doesn’t appear in the **Errors** tab. Claude Code drops that server and records `Invalid MCP server config for <server> in <path>` only in that debug log. To find the entry without loading the plugin, run `claude plugin validate` in your shell on the plugin directory, which reports it as an error. Before v2.1.281, `claude plugin validate` didn’t check `.mcp.json`.


[​](#server-works-with-plugin-dir-but-fails-after-install)

Server works with `--plugin-dir` but fails after install

You’re the plugin’s author, and the server starts when you load the plugin from its source directory with `--plugin-dir` but fails once the plugin is installed. Claude Code copies an installed plugin into its cache, so a path that only works from the source directory breaks. Write paths inside the plugin with `${CLAUDE_PLUGIN_ROOT}`. For paths that reach outside the plugin directory, see [Files the plugin references outside its directory aren’t found](#files-the-plugin-references-outside-its-directory-arent-found).


[​](#language-server-doesnt-start)

Language server doesn’t start, uses too much memory, or reports wrong diagnostics

You installed a [code intelligence plugin](plugins-code-intelligence.md) and Claude isn’t seeing diagnostics, or the language server is using too much memory or reporting errors that aren’t real.


[​](#language-server-doesn’t-start)

Language server doesn’t start

The plugin connects to a language server binary you install separately, and Claude Code spawns it by command name from your `PATH`. The `/plugin` **Errors** tab shows the failure with its reason, such as `Executable not found in $PATH: "<binary>"`, and `claude --debug` logs it as `LSP server <name> failed to start: <reason>`. Install the binary and confirm it’s on the `PATH` of the terminal you start `claude` from, for example with `which typescript-language-server`. Then start a new session.


[​](#language-server-uses-too-much-memory)

Language server uses too much memory

Language servers such as `rust-analyzer` and `pyright` index the whole project. Disable the plugin with `/plugin disable <plugin>` in a session and rely on Claude’s built-in search tools instead.


[​](#false-positive-diagnostics-in-a-monorepo)

False positive diagnostics in a monorepo

A language server that isn’t configured for the workspace can report unresolved imports for internal packages. There’s nothing to fix on the Claude Code side, and the diagnostics don’t stop Claude from editing code.


[​](#build-a-plugin)

Build a plugin

You’re developing a plugin and loading it with `--plugin-dir` or installing it from a local marketplace. These entries cover the failures you hit while developing a plugin. For the checks to run after each change, see [Test and debug](plugins-create.md#test-and-debug). Two failures that also reach a plugin’s users have their entries under [Plugin installed but not working](#plugin-installed-but-not-working):

- **A hook that doesn’t fire**: see [hooks that don’t fire](#failed-to-load-hooks-from-and-hooks-that-dont-fire)
- **An MCP server that doesn’t start**: see [MCP servers that don’t start](#invalid-mcp-server-config-for-and-mcp-servers-that-dont-start)


[​](#commands-path-not-found)

`commands path not found: <path>`

The **Errors** tab shows `commands path not found: <absolute path>` with the guidance `Check that the path in your manifest or marketplace config is correct`. The same message appears for `skills`, `agents`, and `hooks`. Claude Code resolved a path from your `plugin.json` or marketplace entry against the plugin root and found nothing there. The path in the message is the absolute path it checked, so compare it with what’s on disk. Fix the path or create the directory, then run `/reload-plugins`.

Before v2.1.299, this message could appear at startup on a row labeled with a tag such as `synced[0]`. That row is for a plugin [synced from claude.ai](plugins-loading.md#synced-plugins). It appeared when another Claude Code session on your machine had updated the plugin moments before and moved its files. To load the plugin, restart Claude Code. To keep the message from appearing for this reason, update to v2.1.299 or later.

Paths in the manifest are relative to the plugin root and start with `./`. A path that resolves outside the plugin root is reported as `<component> path escapes plugin directory` instead and is dropped.


[​](#plugin-dir-loads-a-plugin-with-no-components)

`--plugin-dir` at a marketplace root doesn’t load the plugins under `plugins/`

You started `claude --plugin-dir <path>` and see no error, but the plugin’s skills, agents, and hooks aren’t there. `--plugin-dir` takes the plugin’s root directory, the one that contains `.claude-plugin/plugin.json` and the component directories such as `skills/`. If you point it at a marketplace root instead, Claude Code doesn’t read `marketplace.json`, so a plugin under `plugins/` doesn’t load, and you see no error. Before v2.1.281, Claude Code loaded a marketplace root as one empty plugin named after that directory. Point the flag at the plugin directory itself:

```python
claude --plugin-dir ./my-marketplace/plugins/my-plugin
```

Then open **Installed** in `/plugin`, where the plugin’s details pane lists its components.


[​](#files-the-plugin-references-outside-its-directory-arent-found)

Files the plugin references outside its directory aren’t found

A plugin works from its source directory with `--plugin-dir` but fails after install, with errors about a path such as `../shared-utils`. Claude Code copies an installed plugin into its cache and loads it from there, so a path that reaches outside the plugin’s own directory points at nothing in the cache. Move the shared files inside the plugin directory, or reference them through a symlink inside it. For where the cache is and how paths resolve, see [Find plugins on disk](plugins-loading.md#find-plugins-on-disk).


[​](#claude-plugin-root-shows-forward-slashes-on-windows)

`${CLAUDE_PLUGIN_ROOT}` shows forward slashes on Windows

On Windows, a plugin hook receives `${CLAUDE_PLUGIN_ROOT}` as `C:/Users/you/...` rather than `C:\Users\you\...`, and a script that expected backslashes breaks. Claude Code runs shell-form hooks through Git Bash on Windows and substitutes the plugin root in the forward-slash Win32 form on purpose. Bash builtins, MSYS tools, and native Windows binaries all accept that form. If your script needs backslashes, switch the hook to one of the forms that keep native paths, described under [exec form and shell form](../07-Hooks/hooks.md#exec-form-and-shell-form):

- An exec-form hook, which spawns the process directly with an `args` array
- A hook with `"shell": "powershell"`


[​](#plugin-loads-but-its-skills-are-missing)

Plugin loads but its skills are missing

Your plugin is listed under **Installed** with no errors, but its skills aren’t offered when you type `/`. Skills load from `skills/` at the plugin root and commands from `commands/` at the plugin root. Only `plugin.json` belongs inside `.claude-plugin/`, and a `skills/` directory inside `.claude-plugin/` isn’t scanned. Move the directories to the plugin root and run `/reload-plugins`. Afterward, the plugin’s details pane in `/plugin` lists the skills, and typing `/` offers them. Each skill is a directory containing `SKILL.md`. A `skills` entry in the manifest that points at a `SKILL.md` file rather than its directory is reported as `path is a file; skills entries must be directories containing SKILL.md`.


[​](#skill-loads-but-claude-never-invokes-the-skill)

Skill loads but Claude never invokes the skill

Your plugin’s skill runs when you type its `/<plugin>:<skill>` command, but Claude never invokes it in response to a plain request. Check these causes in order:

- **The skill sets `disable-model-invocation: true`**: the template skill in [Create your first plugin](plugins-create.md#create-your-first-plugin) sets it. Remove the line from a skill you want Claude to invoke on its own. [Control who invokes a skill](skills.md#control-who-invokes-a-skill) covers the field
- **The description doesn’t match how people ask**: work through the checks in [Skill not triggering](skills.md#skill-not-triggering)
- **The description is truncated**: when many skills are installed, Claude Code shortens descriptions to fit the listing’s character budget, which can strip the keywords Claude needs to match a request. See [Skill descriptions are cut short](skills.md#skill-descriptions-are-cut-short)

To measure how often the skill triggers across realistic prompts rather than checking one at a time, write an eval case with a [`tool_used: Skill` grader](plugin-evals.md#create-your-first-eval-suite) and run it with `claude plugin eval` after each description change.


[​](#is-not-a-plugin-or-skill-folder)

`<directory> is not a plugin or skill folder` from `claude plugin eval init`

You ran `claude plugin eval init` from a directory that isn’t a plugin’s root, such as your home directory or the root of a repository that keeps the plugin in a subdirectory. `init` writes the suite under the working directory, so it stops instead of creating an `evals/` directory the plugin would never see. Change to the plugin’s root, the directory that holds `.claude-plugin/plugin.json` or the skill’s `SKILL.md`, and run the command again. To scaffold the suite somewhere else on purpose, pass `--eval-dir`. See [Test plugins with evals](plugin-evals.md).


[​](#the-userconfig-dialog-never-appears)

The `userConfig` dialog never appears

Your plugin declares `userConfig` options, but no configuration dialog appears when you install it. Whether the install asks for the values depends on where you run it:

- **`/plugin install` in a session, or the Discover tab in `/plugin`**: the dialog is part of this interactive install
- **The VS Code extension’s Manage plugins dialog**: asks for unset options as a form after the install. Before v2.1.285, installing there showed no options form, so set the values from a terminal session with `/plugin configure <plugin>@<marketplace>`
- **`claude plugin install` in your shell**: never prompts for `userConfig` values. It saves any `--config KEY=VALUE` values you pass, and when options remain unset it prints `N userConfig options not yet set — run /plugin configure <plugin>@<marketplace> in Claude Code, or pass --config KEY=VALUE.` When any of the unset options is required, `(M required)` follows `not yet set`.

If you installed from the shell, pass the values with `--config`, one flag per option:

```python
claude plugin install my-plugin@my-marketplace --config api_url=https://example.com
```

When every option is set, the install output carries no `not yet set` line. To open the dialog afterwards instead, run `/plugin configure my-plugin@my-marketplace` in a session. From the shell, [`claude plugin configure`](plugins-cli-reference.md#plugin-configure) shows which options are still unset and saves values piped in on stdin. It requires Claude Code v2.1.285 or later. If you pass a `--config` key the manifest doesn’t declare, the plugin still installs, and the command prints `⚠ Installed, but --config not applied: --config key "<key>" isn't declared in this plugin's userConfig.` followed by the keys the plugin does declare. For a plugin that ships an [MCPB bundle file](plugins-components.md#include-a-packaged-mcpb-server) declaring `user_config` of its own, the message reads `isn't declared in this plugin's userConfig or by its bundled MCP servers.` instead, and the known keys include that server’s keys, written `<server>.<key>`. A bundle the manifest references by URL isn’t read at install time, so its keys aren’t listed and the message says to configure it in `/plugin`. Setting `<server>.<key>` keys requires Claude Code v2.1.285 or later.


[​](#claude-plugin-validate-reports-errors)

`claude plugin validate` reports errors

You ran `claude plugin validate <path>`, or `/plugin validate <path>` in a session, and it printed `Found N errors` and `Validation failed`, then exited with code 1. The validator reads the manifest at the path you give it: `.claude-plugin/plugin.json` for a plugin directory, `.claude-plugin/marketplace.json` for a marketplace directory, or both for a directory that holds both. For a marketplace, it prefixes problems in an entry’s own manifest with the entry index, as `plugins[1] plugin.json → json: ...`. Before v2.1.289, Claude Code validated a directory that holds both as a marketplace alone. The table covers the messages that stop validation and two warnings, `No frontmatter block found` and `Unknown field '<key>'`, which stop it only when you pass `--strict`. Other warnings, such as a missing description, aren’t listed.

| Message                                                                                                  | Cause                                                                                         | Fix                                                                                                                                                                                                                                                                             |
|:---------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `File not found: <path>`                                                                                 | The path has no manifest, or doesn’t exist.                                                   | Run the command against the plugin or marketplace root, the directory that contains `.claude-plugin/`.                                                                                                                                                                          |
| `No manifest found in directory. Expected .claude-plugin/marketplace.json or .claude-plugin/plugin.json` | The directory has no `.claude-plugin/` manifest.                                              | Create the manifest, or point at the right directory.                                                                                                                                                                                                                           |
| `Invalid JSON syntax: <parse error>`                                                                     | The manifest, or `hooks/hooks.json`, isn’t valid JSON.                                        | Fix the JSON. Until you fix `hooks/hooks.json`, a session loads the plugin without the hooks in that file.                                                                                                                                                                      |
| `Path not found: <path>. The runtime loader will report this as a load failure.`                         | A component path in the manifest doesn’t exist.                                               | Fix the path or create the directory.                                                                                                                                                                                                                                           |
| `Path contains ".." which could be a path traversal attempt: <path>`                                     | A component path escapes the plugin directory.                                                | Use paths inside the plugin root.                                                                                                                                                                                                                                               |
| `Path is a file; skills entries must be directories containing SKILL.md`                                 | A `skills` entry points at `SKILL.md` instead of its directory.                               | Point at the parent directory, or `.` for a root-level `SKILL.md`.                                                                                                                                                                                                              |
| `No frontmatter block found` or `YAML frontmatter failed to parse: <error>`                              | A skill, agent, or command file has missing or invalid YAML frontmatter.                      | Add or fix the frontmatter between `---` delimiters. Reported when validating a plugin directory.                                                                                                                                                                               |
| `Plugin name "<name>" is reserved: it passes as one of Anthropic's own`                                  | The plugin’s `name` is one of the [reserved names](plugins-reference.md#name). | Rename the plugin for what it does.                                                                                                                                                                                                                                             |
| `Unknown field '<key>'`                                                                                  | The manifest has a field the schema doesn’t define.                                           | Remove it, or use the name the message suggests. Claude Code ignores unknown fields at load time. For `privacyPolicyUrl` and the other directory listing fields in `plugin.json`, see [Directory listing fields](plugins-reference.md#directory-listing-fields). |

Run the command again after each fix until it prints no errors. `plugin.json` fields are on the [manifest reference](plugins-reference.md), and marketplace-level messages are under [Marketplace validation errors](#marketplace-validation-errors).


[​](#plugin-has-conflicting-manifests)

`Plugin <name> has conflicting manifests`

The plugin fails to load with `Plugin <name> has conflicting manifests: both plugin.json and marketplace entry specify components.` The plugin has its own `plugin.json`, and its marketplace entry sets `strict: false` while also declaring any of `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes`. Remove those fields from the entry, or set `strict: true` in the entry so Claude Code appends them to `plugin.json`. See [Strict mode](plugins-marketplace-reference.md#strict-mode).


[​](#warning-no-commands-found-in-plugin-custom-directory)

`Warning: No commands found in plugin <name> custom directory`

When the plugin loads, the `claude --debug` log at `~/.claude/debug/<session-id>.txt` records `Warning: No commands found in plugin <name> custom directory: <path>. Expected .md files or SKILL.md in subdirectories.` Nothing appears in the session or the **Errors** tab. The `commands` path in the manifest exists but holds no `.md` files and no `SKILL.md` in a subdirectory. Add the command files, or remove the path from the manifest.


[​](#host-a-marketplace)

Host a marketplace

You publish a marketplace and a user reports an error, or your own validation fails. These entries are for the marketplace owner.


[​](#plugins-with-relative-paths-fail-in-url-based-marketplaces)

Plugins with relative paths fail in URL-based marketplaces

Users added your marketplace with an `https://example.com/marketplace.json` URL. Installs of plugins whose `source` is a relative path, such as `./plugins/my-plugin`, fail with `its marketplace entry path does not stay inside the marketplace directory`. Already-installed plugins fail to load with `Plugin source path refused`. Both messages have an [error reference entry](../02-Claude-Code-CLI/errors.md#marketplace-entry-path-does-not-stay-inside-the-marketplace-directory). When a user adds a URL-based marketplace, Claude Code downloads only the `marketplace.json` file itself. It doesn’t fetch plugin files by relative path from that server, so a relative path in an entry points at a directory that was never fetched. Give each entry a source Claude Code can fetch on its own, such as a GitHub repository:

```python
{ "name": "my-plugin", "source": { "source": "github", "repo": "owner/repo" } }
```

Alternatively, host the marketplace in a git repository and tell users to add it with the repository URL. For a git source, Claude Code clones the whole repository, so relative paths resolve. Source types are on the [marketplace reference](plugins-marketplace-reference.md).


[​](#marketplace-validation-errors)

Marketplace validation errors

You ran `claude plugin validate .` from your marketplace directory and it reported errors or warnings on the marketplace file itself. `claude plugin validate` also validates each entry whose `source` is a local path and warns when the entry’s `version` disagrees with the plugin’s own manifest. The table lists the marketplace-level messages. Entry-level messages are the plugin messages under [`claude plugin validate` reports errors](#claude-plugin-validate-reports-errors), prefixed with `plugins[N] plugin.json →`.

| Message                                                                                                                                                                                                                                               | Kind    | Fix                                                                                                                              |
|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------------------------------------------------------------------------------------------------------------------------------|
| `Duplicate plugin name "<name>" found in marketplace`                                                                                                                                                                                                 | Error   | Give each plugin a unique `name`.                                                                                                |
| `Path contains "..": <path>` under `plugins[N].source`                                                                                                                                                                                                | Error   | Use paths relative to the marketplace root without `..` segments.                                                                |
| `Marketplace name cannot contain control or bidirectional-formatting characters`                                                                                                                                                                      | Error   | Remove the character from the name, such as an escape or a newline.                                                              |
| `Plugin name cannot contain control or bidirectional-formatting characters`                                                                                                                                                                           | Error   | Remove the character from the plugin `name`.                                                                                     |
| `Claude Code cannot install plugins from marketplace "<name>". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change the marketplace's "name".` | Error   | Rename the marketplace to fit the rule the message states.                                                                       |
| `Claude Code cannot install plugin "<name>". Each part of a plugin id (plugin@marketplace) may use only the letters a-z and A-Z, digits, ".", "_" and "-", and must start with a letter or digit. Change this entry's "name".`                        | Error   | Rename the entry to fit the rule the message states.                                                                             |
| `Marketplace has no plugins defined`                                                                                                                                                                                                                  | Warning | Add at least one entry to `plugins`.                                                                                             |
| `No marketplace description provided`                                                                                                                                                                                                                 | Warning | Add a top-level `description`.                                                                                                   |
| `Plugin name "<name>" is not kebab-case` under `plugins[N] plugin.json → name`                                                                                                                                                                        | Warning | Rename to lowercase letters, digits, and hyphens; the claude.ai marketplace sync requires that form.                             |
| `Entry declares version "<a>" but <path>/plugin.json says "<b>"`                                                                                                                                                                                      | Warning | Update the entry to match `plugin.json`, which is authoritative at install time.                                                 |
| `Marketplace name "<name>" is reserved in Claude Desktop`                                                                                                                                                                                             | Warning | Rename the marketplace. Claude Desktop’s managed marketplace sync rejects `org`, `org-provisioned`, and `unknown` in any casing. |
| `Marketplace name "<name>" is not accepted by Claude Desktop` or `Plugin name "<name>" is not accepted by Claude Desktop`                                                                                                                             | Warning | Rename to at most 128 characters of letters, digits, `.`, `_`, and `-`, starting with a letter or digit.                         |

Before v2.1.247, a marketplace name containing control or bidirectional-formatting characters was reported only as `Marketplace name impersonates an official Anthropic/Claude marketplace`.


[​](#blocked-by-your-organization)

Blocked by your organization

Your organization deploys managed settings that restrict plugins, and a command was refused with a policy message. These entries name the setting behind each refusal so you know what to ask your administrator for. For the admin side, see [Manage plugins for your organization](plugins-org.md).


[​](#marketplace-source-is-blocked-by-enterprise-policy)

`Marketplace source '<source>' is blocked by enterprise policy`

You ran `/plugin marketplace add`, `update`, or an install, and Claude Code refused with this line. For a GitHub or git source, the host follows the source in parentheses, as in `'github:owner/repo' (github.com)`. Your administrator set `blockedMarketplaces` or `strictKnownMarketplaces` in managed settings, and this source isn’t permitted. Ask your administrator to allow the source, or add one of the allowed sources the message lists. Match the rest of the message to see what kind of policy blocked the source:

- **`Allowed sources: <list>`**: the block comes from the `strictKnownMarketplaces` allowlist rather than the `blockedMarketplaces` blocklist
- **`No external marketplaces are allowed.`**: the `strictKnownMarketplaces` allowlist is empty
- **A `Tip:` that the shorthand assumes github.com**: the allowlist permits a git host by hostname, and the `owner/repo` shorthand you passed points at github.com. If the repository lives on your internal host, add it again with its full URL, such as `git@your-git-host.com:owner/repo.git`

A marketplace you added before the policy became more restrictive stops refreshing too, because the policy applies on every refresh.


[​](#marketplace-is-not-in-the-allowed-marketplace-list)

`Marketplace "<name>" is not in the allowed marketplace list`

The **Errors** tab shows this line, or `Marketplace "<name>" is blocked by enterprise policy`, for a marketplace you already have registered. The same managed settings that block a [marketplace source](#marketplace-source-is-blocked-by-enterprise-policy) apply at load time. `strictKnownMarketplaces` doesn’t include this marketplace, or `blockedMarketplaces` names it, so Claude Code stops loading it and its plugins. For the allowlist variant, the guidance line shows the allowed sources, or `Contact your administrator to configure allowed marketplace sources`. For the blocklist variant it reads `This marketplace source is explicitly blocked by your administrator`.


[​](#plugin-is-blocked-by-your-organizations-policy-and-cannot-be-installed)

`Plugin "<name>" is blocked by your organization's policy and cannot be installed`

An install was refused with this line, an enable with the same line ending `cannot be enabled`, or an install or update with one naming the reason: `Plugin "<name>" is from marketplace "<marketplace>", which is blocked by your organization's policy`, or `Plugin "<name>" depends on "<dep>", which is blocked by your organization's policy`. Managed settings block this plugin, its marketplace, or a dependency it needs. Ask your administrator which entry applies. A blocked dependency means the plugin can’t install until the dependency’s marketplace is allowed.


[​](#plugin-dir-is-disabled-by-your-organizations-managed-settings-disables)

`--plugin-dir is disabled by your organization's managed settings (disableSideloadFlags)`

You started `claude` with `--plugin-dir`, `--plugin-url`, `--agents`, or `--mcp-config`. Claude Code exited with this message and `Plugins, custom agents, and MCP servers can only be loaded from sources your administrator has approved.` Your administrator set `disableSideloadFlags` in managed settings, which turns off the flags that load plugins, agents, and servers from arbitrary paths. Load the plugin from an approved marketplace instead, or ask your administrator to remove the setting. A related message in the `/plugin` **Errors** tab is `--plugin-dir copy of "<name>" ignored: plugin is locked by managed settings`. Managed settings enable or disable that plugin by name, and Claude Code ignores your `--plugin-dir` copy of it so the flag can’t override the policy.


[​](#plugins-from-claude-skills-are-blocked-by-your-organizations-managed-s)

`Plugins from ~/.claude/skills/ are blocked by your organization's managed settings`

You ran `claude plugin init` or `claude plugin enable`, and it stopped with this line. The message names `strictKnownMarketplaces or blockedMarketplaces` and asks your administrator to add `{"source":"skills-dir"}` to `strictKnownMarketplaces` or remove it from `blockedMarketplaces`. The `skills-dir` source stands for plugins Claude Code loads from your `~/.claude/skills/` directory. Ask your administrator to make the change the message names.


[​](#command-sourced-plugins-are-disabled-by-your-organizations-managed-set)

`Command-sourced plugins are disabled by your organization's managed settings`

You installed or updated a plugin with a `command` source, and it stopped with this line and `The plugin was not installed or updated and its command was not run.` Your administrator set `disableCommandPluginSources`, so Claude Code refuses to run the marketplace-declared command that produces the plugin. Setting `allowManagedHooksOnly` alone has the same effect when `disableCommandPluginSources` is unset. Ask your administrator whether the plugin can be published from a source type the policy allows.


[​](#marketplace-is-seed-managed)

`Marketplace '<name>' is seed-managed`

You ran `claude plugin marketplace update <name>`, and it failed with `Marketplace '<name>' is seed-managed (<dir>)` and a hint to ask your admin. An operator pre-populated this marketplace through `CLAUDE_CODE_PLUGIN_SEED_DIR`, and Claude Code treats a seed-managed marketplace as read-only. A bulk `marketplace update` skips it and updates the others. To change the marketplace’s content, ask the person who maintains the seed image to update it. For the procedure, see [Seed containers and CI](plugins-org.md#seed-containers-and-ci).


[​](#next-steps)

Next steps

- [Plugin loading reference](plugins-loading.md): why scopes, the cache, and precedence behave the way they do
- [Plugin commands reference](plugins-cli-reference.md): flags, defaults, output, and exit codes for the `claude plugin` commands
- [Install and manage plugins](discover-plugins.md): the install steps from the start
- [Manage plugins for your organization](plugins-org.md#troubleshoot-policy): policy-side troubleshooting for administrators

---

## Plugins overview - Claude Code Docs

Source: https://code.claude.com/docs/en/plugins
Path: 08-Plugins-Skills/plugins.md

## On this page

- [Understand what a plugin is](#understand-what-a-plugin-is)
  - [Decide whether you need a plugin](#decide-whether-you-need-a-plugin)
  - [What an enabled plugin adds to your sessions](#what-an-enabled-plugin-adds-to-your-sessions)
- [Get plugins from a marketplace](#get-plugins-from-a-marketplace)
  - [Make an installed plugin available in your session](#make-an-installed-plugin-available-in-your-session)
- [Tell Anthropic’s marketplaces from third-party ones](#tell-anthropic%E2%80%99s-marketplaces-from-third-party-ones)
- [Understand install scopes](#understand-install-scopes)
- [Next steps](#next-steps)

Plugins

# Plugins overview

Copy pageCopy page

Understand what a Claude Code plugin is, when you need one instead of a standalone skill or MCP server, and which page to read to install or create one.

Copy pageCopy page

A Claude Code plugin is a directory of skills, agents, hooks, MCP servers, or other components that Claude Code installs and loads as one unit. Most plugins come from a marketplace, which is a catalog that lists plugins and where to fetch each one. You can also load a plugin from a folder someone gives you, or [build your own](plugins-create.md).

These cases are covered on other pages:

- **You use claude.ai chat or Cowork and not Claude Code**: see [Plugins on claude.ai and in Cowork](https://claude.com/docs/plugins/overview)
- **You built an MCP server and want it in Anthropic’s directory**: see [Publish to the directory](https://claude.com/docs/directory/publish)
- **You want Claude Code inside VS Code or a JetBrains IDE**: that’s the VS Code extension or the JetBrains plugin, not a Claude Code plugin. See [Use Claude Code in VS Code](../03-IDE-Integrations/vs-code.md) or [JetBrains IDEs](../03-IDE-Integrations/jetbrains.md)

To try a plugin now, run `/plugin` in a Claude Code terminal session and install one from the **Discover** tab, which lists the plugins from your marketplaces. From there:

- [Install and manage plugins](discover-plugins.md): the full install steps, scopes, and other surfaces
- [Create a plugin](plugins-create.md): build your own
- [Decide whether you need a plugin](#decide-whether-you-need-a-plugin): whether a plugin is the right tool for what you want


[​](#understand-what-a-plugin-is)

Understand what a plugin is

A plugin is a directory of components, usually with a manifest. The manifest, a JSON file at `.claude-plugin/plugin.json`, gives the plugin its name and can add a version, a description, and other [metadata](plugins-reference.md). The components are what the plugin adds to Claude Code, such as:

- [**Skills**](plugins-components.md#skills): `SKILL.md` instructions Claude loads when relevant, and that you can also run as a command
- [**Agents**](plugins-components.md#agents): subagent definitions Claude can delegate to
- [**Hooks**](plugins-components.md#hooks): commands Claude Code runs at points in its lifecycle, such as after every edit
- [**A hooks module**](plugins-mods-overview.md): hooks written as JavaScript functions, which can also draw panes and add commands. A plugin that has one is called a mod. Some mods are [built into Claude Code](plugins-mods-overview.md#mods-built-into-claude-code).
- [**MCP servers**](plugins-components.md#mcp-servers): tool servers Claude Code connects to while the plugin is enabled

This diagram shows a plugin named `my-plugin` that holds a skill, an agent, hooks, and an MCP server, and what you get from each file once the plugin loads.

For every component type a plugin can hold, with an example of each, see [Plugin components](plugins-components.md). To see where each piece is located in a plugin’s directory, use the [plugin explorer](plugins-components.md#explore-the-plugin-directory) on that page.


[​](#decide-whether-you-need-a-plugin)

Decide whether you need a plugin

Skills, subagents, hooks, and MCP servers all work on their own, without a plugin. A skill you save in `~/.claude/skills/`, for example, is available in every project on your machine. To set one up on its own, see [Skills](skills.md), [Subagents](../09-Agents-Patterns/sub-agents.md), [Hooks](../07-Hooks/hooks-guide.md), or [MCP](../06-MCP-Tools/General/mcp.md). Use a plugin when you want several skills, subagents, hooks, or MCP servers packaged as one unit. Install one to get a setup someone else built, with one command and updates from its marketplace. Make one to give your own setup to teammates, install it in many projects, or publish versioned releases.


[​](#what-an-enabled-plugin-adds-to-your-sessions)

What an enabled plugin adds to your sessions

An enabled plugin is part of every session, not only the sessions where you use it. That has a few consequences worth knowing before you install one:

- **Context and usage**: for each skill, agent, and command that [Claude can invoke on its own](skills.md#control-who-invokes-a-skill), the name and description are in Claude’s context on every turn so that Claude knows it exists. Those tokens count toward your usage and leave less room in the [context window](../02-Claude-Code-CLI/context-window.md) even in sessions where nothing from the plugin runs. The full text of a skill or agent loads only when it’s used. What the plugin’s MCP servers add per turn follows [MCP tool search](../06-MCP-Tools/General/mcp.md#scale-with-mcp-tool-search).
- **Processes**: MCP servers the plugin defines run alongside each session where it’s enabled, and its hooks fire at their events.
- **Permissions**: what the plugin runs, it runs as you. See [Plugin security and trust](plugins-security.md) for what to review first.

You can check a plugin’s footprint at each stage:

- **Before you install**: open the plugin from the **Marketplaces** tab in `/plugin`. Plugins in Anthropic’s official marketplace show a **Context cost** estimate there.
- **After you install**: [Measure what a plugin costs](plugins-measure.md#measure-what-a-plugin-costs) shows how to read a plugin’s footprint, and the **Installed** tab’s **Not used recently** group lists plugins you could turn off.
- **To stop it without uninstalling**: disable the plugin with `/plugin` or, in your shell, `claude plugin disable`. See [Manage installed plugins](discover-plugins.md#manage-installed-plugins).


[​](#get-plugins-from-a-marketplace)

Get plugins from a marketplace

A marketplace is a repository or directory with a `.claude-plugin/marketplace.json` file that lists plugins and where to fetch each one. It’s a catalog, not a hosted store. You add a marketplace once, then install plugins from it by name, such as `commit-commands@claude-plugins-official`.

A plugin marketplace isn’t [Claude Marketplace](https://claude.com/marketplace). Claude Marketplace is the website at claude.com/marketplace where you browse plugins, connectors, partner products, and service partners. It isn’t a marketplace you add with `/plugin marketplace add`.

Claude Code adds Anthropic’s official marketplace the first time you start an interactive terminal session, unless a [managed policy](plugins-org.md#allow-the-official-marketplace-and-your-own) blocks it. Claude Code doesn’t add Anthropic’s community and demo marketplaces on its own. To distinguish the three Anthropic marketplaces, read [Anthropic’s marketplaces](plugins-anthropic-marketplaces.md). To see what the official one lists, open the **Discover** tab of `/plugin` in a session or browse [Claude Marketplace](https://claude.com/marketplace/plugins). This diagram shows the path from a marketplace to your session. A marketplace lists a plugin, you install that plugin, and Claude Code loads its components.

[Install and manage plugins](discover-plugins.md#install-a-plugin) has the install steps for each place you run Claude Code. While you’re developing a plugin, you don’t need a marketplace: load it straight from its folder with `--plugin-dir`, as [Develop without a marketplace](plugins-create.md#develop-without-a-marketplace) shows.


[​](#make-an-installed-plugin-available-in-your-session)

Make an installed plugin available in your session

Before a plugin you installed gives you a skill you can run, it has to be present at each of these layers:

- **Settings**: your settings list the marketplaces you’ve added and the plugins that are enabled.
- **Disk**: `~/.claude/plugins/` holds what Claude Code has fetched and installed.
- **Session**: plugins load at startup, or when you [reload plugins](plugins-loading.md#check-which-stage-a-plugin-reached).

Read [Plugin loading reference](plugins-loading.md) for the rules at each layer, including which settings file takes precedence and where the files are on disk.


[​](#tell-anthropic’s-marketplaces-from-third-party-ones)

Tell Anthropic’s marketplaces from third-party ones

A marketplace’s name places it in one of three tiers. Claude Code accepts the official and community names only for marketplaces sourced from `github.com/anthropics/` repositories:

- **Official**: marketplaces with one of Anthropic’s [official marketplace names](plugins-security.md#official-marketplace-names), including `claude-plugins-official` and the demo marketplace `claude-code-plugins`.
- **Community**: marketplaces with one of Anthropic’s community names, such as `claude-community`. [Identify Anthropic’s marketplaces by name](plugins-security.md#marketplace-tiers) lists them.
- **Third-party**: every other marketplace. A marketplace your coworker or your organization publishes is third-party.

Whatever the tier, a plugin you install can run code with your user privileges. Read [Plugin security and trust](plugins-security.md) for how to review a plugin before you install it. Through [managed settings](../02-Claude-Code-CLI/settings.md#settings-files), an organization can allowlist or block marketplaces, force-install plugins, and turn off session-only loading. Read [Manage plugins for your organization](plugins-org.md) for those controls.


[​](#understand-install-scopes)

Understand install scopes

When you install a plugin, you pick a scope, and the scope decides who the plugin is enabled for:

- **User scope**: enabled for you in every project on this computer
- **Project scope**: enabled for everyone who works in this repository, through the committed `.claude/settings.json`. Each collaborator still [installs it on their own machine](plugins-loading.md#enabled-in-project-settings-but-not-installed)
- **Local scope**: enabled for you in this repository only

A plugin you install at user scope in the terminal, the desktop app’s local sessions, or the VS Code extension is available in the other two on that computer, because all three read the same settings files. See [Choose an install scope](discover-plugins.md#choose-an-install-scope) for how to pick one. A cloud session, including one in the browser at claude.ai/code, doesn’t load the plugins in your local settings. For install steps in the terminal, VS Code, and the desktop app, and for what a cloud session loads, see [Install a plugin](discover-plugins.md#install-a-plugin).

The same plugin format also installs on claude.ai and in Cowork, where a different set of components loads. For those surfaces, see [Plugins on claude.ai and in Cowork](https://claude.com/docs/plugins/overview) on claude.com and its [component support table](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app).


[​](#next-steps)

Next steps

Most people start by installing a plugin from Anthropic’s official marketplace, which Claude Code adds the first time you start an interactive terminal session. Run `/plugin` in a terminal session to browse it, or follow [Install and manage plugins](discover-plugins.md), which also covers the desktop app and VS Code. To see what’s in that marketplace before you open Claude Code, browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web. To build your own, [Create a plugin](plugins-create.md) starts with an empty directory and ends with a working plugin. Once you’ve installed or built a plugin, these pages cover what comes next:

- **Share what you built**: [Publish and distribute a plugin](plugins-publish.md), through your own marketplace or [Anthropic’s directory](plugins-publish.md#submit-to-anthropics-directory)
- **Check whether it works and is used**: [Test plugins with evals](plugin-evals.md) and [Measure plugin cost and usage](plugins-measure.md)
- **Run a marketplace for your team**: [Create a marketplace](plugin-marketplaces.md), then [Host and maintain a marketplace](plugins-host-marketplace.md)
- **Set plugin policy for an organization**: [Manage plugins for your organization](plugins-org.md)
- **Fix a problem**: [Troubleshoot plugins](plugins-troubleshooting.md)

---

## Best practices for skill creators - Agent Skills

Source: https://agentskills.io/skill-creation/best-practices
Path: 08-Plugins-Skills/skill-creation-best-practices.md

## On this page

- [Start from real expertise](#start-from-real-expertise)
  - [Extract from a hands-on task](#extract-from-a-hands-on-task)
  - [Synthesize from existing project artifacts](#synthesize-from-existing-project-artifacts)
- [Refine with real execution](#refine-with-real-execution)
- [Spending context wisely](#spending-context-wisely)
  - [Add what the agent lacks, omit what it knows](#add-what-the-agent-lacks-omit-what-it-knows)
  - [Design coherent units](#design-coherent-units)
  - [Aim for moderate detail](#aim-for-moderate-detail)
  - [Structure large skills with progressive disclosure](#structure-large-skills-with-progressive-disclosure)
- [Calibrating control](#calibrating-control)
  - [Match specificity to fragility](#match-specificity-to-fragility)
  - [Provide defaults, not menus](#provide-defaults-not-menus)
  - [Favor procedures over declarations](#favor-procedures-over-declarations)
- [Patterns for effective instructions](#patterns-for-effective-instructions)
  - [Gotchas sections](#gotchas-sections)
  - [Templates for output format](#templates-for-output-format)
  - [Checklists for multi-step workflows](#checklists-for-multi-step-workflows)
  - [Validation loops](#validation-loops)
  - [Plan-validate-execute](#plan-validate-execute)
  - [Bundling reusable scripts](#bundling-reusable-scripts)
- [Next steps](#next-steps)

For skill creators

# Best practices for skill creators

Copy pageCopy page

How to write skills that are well-scoped and calibrated to the task.

Copy pageCopy page


[​](#start-from-real-expertise)

Start from real expertise

A common pitfall in skill creation is asking an LLM to generate a skill without providing domain-specific context — relying solely on the LLM’s general training knowledge. The result is vague, generic procedures (“handle errors appropriately,” “follow best practices for authentication”) rather than the specific API patterns, edge cases, and project conventions that make a skill valuable. Effective skills are grounded in real expertise. The key is feeding domain-specific context into the creation process.


[​](#extract-from-a-hands-on-task)

Extract from a hands-on task

Complete a real task in conversation with an agent, providing context, corrections, and preferences along the way. Then extract the reusable pattern into a skill. Pay attention to:

- **Steps that worked** — the sequence of actions that led to success
- **Corrections you made** — places where you steered the agent’s approach (e.g., “use library X instead of Y,” “check for edge case Z”)
- **Input/output formats** — what the data looked like going in and coming out
- **Context you provided** — project-specific facts, conventions, or constraints the agent didn’t already know


[​](#synthesize-from-existing-project-artifacts)

Synthesize from existing project artifacts

When you have a body of existing knowledge, you can feed it into an LLM and ask it to synthesize a skill. A data-pipeline skill synthesized from your team’s actual incident reports and runbooks will outperform one synthesized from a generic “data engineering best practices” article, because it captures *your* schemas, failure modes, and recovery procedures. The key is project-specific material, not generic references. Good source material includes:

- Internal documentation, runbooks, and style guides
- API specifications, schemas, and configuration files
- Code review comments and issue trackers (captures recurring concerns and reviewer expectations)
- Version control history, especially patches and fixes (reveals patterns through what actually changed)
- Real-world failure cases and their resolutions


[​](#refine-with-real-execution)

Refine with real execution

The first draft of a skill usually needs refinement. Run the skill against real tasks, then feed the results — all of them, not just failures — back into the creation process. Ask: what triggered false positives? What was missed? What could be cut? Even a single pass of execute-then-revise noticeably improves quality, and complex domains often benefit from several.

Read agent execution traces, not just final outputs. If the agent wastes time on unproductive steps, common causes include instructions that are too vague (the agent tries several approaches before finding one that works), instructions that don’t apply to the current task (the agent follows them anyway), or too many options presented without a clear default.

For a more structured approach to iteration, including test cases, assertions, and grading, see [Evaluating skill output quality](skill-creation-evaluating-skills.md).


[​](#spending-context-wisely)

Spending context wisely

Once a skill activates, its full `SKILL.md` body loads into the agent’s context window alongside conversation history, system context, and other active skills. Every token in your skill competes for the agent’s attention with everything else in that window.


[​](#add-what-the-agent-lacks-omit-what-it-knows)

Add what the agent lacks, omit what it knows

Focus on what the agent *wouldn’t* know without your skill: project-specific conventions, domain-specific procedures, non-obvious edge cases, and the particular tools or APIs to use. You don’t need to explain what a PDF is, how HTTP works, or what a database migration does.

```` shiki
<!-- Too verbose — the agent already knows what PDFs are -->
## Extract PDF text

PDF (Portable Document Format) files are a common file format that contains
text, images, and other content. To extract text from a PDF, you'll need to
use a library. pdfplumber is recommended because it handles most cases well.

<!-- Better — jumps straight to what the agent wouldn't know on its own -->
## Extract PDF text

Use pdfplumber for text extraction. For scanned documents, fall back to
pdf2image with pytesseract.

```python
import pdfplumber

with pdfplumber.open("file.pdf") as pdf:
    text = pdf.pages[0].extract_text()
```
````

Ask yourself about each piece of content: “Would the agent get this wrong without this instruction?” If the answer is no, cut it. If you’re unsure, test it. And if the agent already handles the entire task well without the skill, the skill may not be adding value. See [Evaluating skill output quality](skill-creation-evaluating-skills.md) for how to test this systematically.


[​](#design-coherent-units)

Design coherent units

Deciding what a skill should cover is like deciding what a function should do: you want it to encapsulate a coherent unit of work that composes well with other skills. Skills scoped too narrowly force multiple skills to load for a single task, risking overhead and conflicting instructions. Skills scoped too broadly become hard to activate precisely. A skill for querying a database and formatting the results may be one coherent unit, while a skill that also covers database administration is probably trying to do too much.


[​](#aim-for-moderate-detail)

Aim for moderate detail

Overly comprehensive skills can hurt more than they help — the agent struggles to extract what’s relevant and may pursue unproductive paths triggered by instructions that don’t apply to the current task. Concise, stepwise guidance with a working example tends to outperform exhaustive documentation. When you find yourself covering every edge case, consider whether most are better handled by the agent’s own judgment.


[​](#structure-large-skills-with-progressive-disclosure)

Structure large skills with progressive disclosure

The [specification](specification.md#progressive-disclosure) recommends keeping `SKILL.md` under 500 lines and 5,000 tokens — just the core instructions the agent needs on every run. When a skill legitimately needs more content, move detailed reference material to separate files in `references/` or similar directories. The key is telling the agent *when* to load each file. “Read `references/api-errors.md` if the API returns a non-200 status code” is more useful than a generic “see references/ for details.” This lets the agent load context on demand rather than up front, which is how [progressive disclosure](specification.md#progressive-disclosure) is designed to work.


[​](#calibrating-control)

Calibrating control

Not every part of a skill needs the same level of prescriptiveness. Match the specificity of your instructions to the fragility of the task.


[​](#match-specificity-to-fragility)

Match specificity to fragility

**Give the agent freedom** when multiple approaches are valid and the task tolerates variation. For flexible instructions, explaining *why* can be more effective than rigid directives — an agent that understands the purpose behind an instruction makes better context-dependent decisions. A code review skill can describe what to look for without prescribing exact steps:

```python
## Code review process

1. Check all database queries for SQL injection (use parameterized queries)
2. Verify authentication checks on every endpoint
3. Look for race conditions in concurrent code paths
4. Confirm error messages don't leak internal details
```

**Be prescriptive** when operations are fragile, consistency matters, or a specific sequence must be followed:

```` shiki
## Database migration

Run exactly this sequence:

```bash
python scripts/migrate.py --verify --backup
```

Do not modify the command or add additional flags.
````

Most skills have a mix. Calibrate each part independently.


[​](#provide-defaults-not-menus)

Provide defaults, not menus

When multiple tools or approaches could work, pick a default and mention alternatives briefly rather than presenting them as equal options.

```` shiki
<!-- Too many options -->
You can use pypdf, pdfplumber, PyMuPDF, or pdf2image...

<!-- Clear default with escape hatch -->
Use pdfplumber for text extraction:

```python
import pdfplumber
```

For scanned PDFs requiring OCR, use pdf2image with pytesseract instead.
````


[​](#favor-procedures-over-declarations)

Favor procedures over declarations

A skill should teach the agent *how to approach* a class of problems, not *what to produce* for a specific instance. Compare:

```python
<!-- Specific answer — only useful for this exact task -->
Join the `orders` table to `customers` on `customer_id`, filter where
`region = 'EMEA'`, and sum the `amount` column.

<!-- Reusable method — works for any analytical query -->
1. Read the schema from `references/schema.yaml` to find relevant tables
2. Join tables using the `_id` foreign key convention
3. Apply any filters from the user's request as WHERE clauses
4. Aggregate numeric columns as needed and format as a markdown table
```

This doesn’t mean skills can’t include specific details — output format templates (see [Templates for output format](#templates-for-output-format)), constraints like “never output PII,” and tool-specific instructions are all valuable. The point is that the *approach* should generalize even when individual details are specific.


[​](#patterns-for-effective-instructions)

Patterns for effective instructions

These are reusable techniques for structuring skill content. Not every skill needs all of them — use the ones that fit your task.


[​](#gotchas-sections)

Gotchas sections

The highest-value content in many skills is a list of gotchas — environment-specific facts that defy reasonable assumptions. These aren’t general advice (“handle errors appropriately”) but concrete corrections to mistakes the agent will make without being told otherwise:

```python
## Gotchas

- The `users` table uses soft deletes. Queries must include
  `WHERE deleted_at IS NULL` or results will include deactivated accounts.
- The user ID is `user_id` in the database, `uid` in the auth service,
  and `accountId` in the billing API. All three refer to the same value.
- The `/health` endpoint returns 200 as long as the web server is running,
  even if the database connection is down. Use `/ready` to check full
  service health.
```

Keep gotchas in `SKILL.md` where the agent reads them before encountering the situation. A separate reference file works if you tell the agent when to load it, but for non-obvious issues, the agent may not recognize the trigger.

When an agent makes a mistake you have to correct, add the correction to the gotchas section. This is one of the most direct ways to improve a skill iteratively (see [Refine with real execution](#refine-with-real-execution)).


[​](#templates-for-output-format)

Templates for output format

When you need the agent to produce output in a specific format, provide a template. This is more reliable than describing the format in prose, because agents pattern-match well against concrete structures. Short templates can live inline in `SKILL.md`; for longer templates, or templates only needed in certain cases, store them in `assets/` and reference them from `SKILL.md` so they only load when needed.

```` shiki
## Report structure

Use this template, adapting sections as needed for the specific analysis:

```markdown
# [Analysis Title]

## Executive summary
[One-paragraph overview of key findings]

## Key findings
- Finding 1 with supporting data
- Finding 2 with supporting data

## Recommendations
1. Specific actionable recommendation
2. Specific actionable recommendation
```
````


[​](#checklists-for-multi-step-workflows)

Checklists for multi-step workflows

An explicit checklist helps the agent track progress and avoid skipping steps, especially when steps have dependencies or validation gates.

```python
## Form processing workflow

Progress:
- [ ] Step 1: Analyze the form (run `scripts/analyze_form.py`)
- [ ] Step 2: Create field mapping (edit `fields.json`)
- [ ] Step 3: Validate mapping (run `scripts/validate_fields.py`)
- [ ] Step 4: Fill the form (run `scripts/fill_form.py`)
- [ ] Step 5: Verify output (run `scripts/verify_output.py`)
```


[​](#validation-loops)

Validation loops

Instruct the agent to validate its own work before moving on. The pattern is: do the work, run a validator (a script, a reference checklist, or a self-check), fix any issues, and repeat until validation passes.

```python
## Editing workflow

1. Make your edits
2. Run validation: `python scripts/validate.py output/`
3. If validation fails:
   - Review the error message
   - Fix the issues
   - Run validation again
4. Only proceed when validation passes
```

A reference document can also serve as the “validator” — instruct the agent to check its work against the reference before finalizing.


[​](#plan-validate-execute)

Plan-validate-execute

For batch or destructive operations, have the agent create an intermediate plan in a structured format, validate it against a source of truth, and only then execute.

```python
## PDF form filling

1. Extract form fields: `python scripts/analyze_form.py input.pdf` → `form_fields.json`
   (lists every field name, type, and whether it's required)
2. Create `field_values.json` mapping each field name to its intended value
3. Validate: `python scripts/validate_fields.py form_fields.json field_values.json`
   (checks that every field name exists in the form, types are compatible, and
   required fields aren't missing)
4. If validation fails, revise `field_values.json` and re-validate
5. Fill the form: `python scripts/fill_form.py input.pdf field_values.json output.pdf`
```

The key ingredient is step 3: a validation script that checks the plan (`field_values.json`) against the source of truth (`form_fields.json`). Errors like “Field ‘signature_date’ not found — available fields: customer_name, order_total, signature_date_signed” give the agent enough information to self-correct.


[​](#bundling-reusable-scripts)

Bundling reusable scripts

When [iterating on a skill](skill-creation-evaluating-skills.md), compare the agent’s execution traces across test cases. If you notice the agent independently reinventing the same logic each run — building charts, parsing a specific format, validating output — that’s a signal to write a tested script once and bundle it in `scripts/`. For more on designing and bundling scripts, see [Using scripts in skills](skill-creation-using-scripts.md).


[​](#next-steps)

Next steps

Once you have a working skill, two guides can help you refine it further:

- **[Evaluating skill output quality](skill-creation-evaluating-skills.md)** — Set up test cases, grade results, and iterate systematically.
- **[Optimizing skill descriptions](skill-creation-optimizing-descriptions.md)** — Test and improve your skill’s `description` field so it triggers on the right prompts.

[Quickstart](skill-creation-quickstart.md)[Optimizing descriptions](skill-creation-optimizing-descriptions.md)

---

## Evaluating skill output quality - Agent Skills

Source: https://agentskills.io/skill-creation/evaluating-skills
Path: 08-Plugins-Skills/skill-creation-evaluating-skills.md

## On this page

- [Designing test cases](#designing-test-cases)
- [Running evals](#running-evals)
  - [Workspace structure](#workspace-structure)
  - [Spawning runs](#spawning-runs)
  - [Capturing timing data](#capturing-timing-data)
- [Writing assertions](#writing-assertions)
- [Grading outputs](#grading-outputs)
  - [Grading principles](#grading-principles)
- [Aggregating results](#aggregating-results)
- [Analyzing patterns](#analyzing-patterns)
- [Reviewing results with a human](#reviewing-results-with-a-human)
- [Iterating on the skill](#iterating-on-the-skill)
  - [The loop](#the-loop)

For skill creators

# Evaluating skill output quality

Copy pageCopy page

How to test whether your skill produces good outputs using eval-driven iteration.

Copy pageCopy page

You wrote a skill, tried it on a prompt, and it seemed to work. But does it work reliably — across varied prompts, in edge cases, better than no skill at all? Running structured evaluations (evals) answers these questions and gives you a feedback loop for improving the skill systematically.


[​](#designing-test-cases)

Designing test cases

A test case has three parts:

- **Prompt**: a realistic user message — the kind of thing someone would actually type.
- **Expected output**: a human-readable description of what success looks like.
- **Input files** (optional): files the skill needs to work with.

Store test cases in `evals/evals.json` inside your skill directory:

evals/evals.json

```python
{
  "skill_name": "csv-analyzer",
  "evals": [
    {
      "id": 1,
      "prompt": "I have a CSV of monthly sales data in data/sales_2025.csv. Can you find the top 3 months by revenue and make a bar chart?",
      "expected_output": "A bar chart image showing the top 3 months by revenue, with labeled axes and values.",
      "files": ["evals/files/sales_2025.csv"]
    },
    {
      "id": 2,
      "prompt": "there's a csv in my downloads called customers.csv, some rows have missing emails — can you clean it up and tell me how many were missing?",
      "expected_output": "A cleaned CSV with missing emails handled, plus a count of how many were missing.",
      "files": ["evals/files/customers.csv"]
    }
  ]
}
```

**Tips for writing good test prompts:**

- **Start with 2-3 test cases.** Don’t over-invest before you’ve seen your first round of results. You can expand the set later.
- **Vary the prompts.** Use different phrasings, levels of detail, and formality. Some prompts should be casual (“hey can you clean up this csv”), others precise (“Parse the CSV at data/input.csv, drop rows where column B is null, and write the result to data/output.csv”).
- **Cover edge cases.** Include at least one prompt that tests a boundary condition — a malformed input, an unusual request, or a case where the skill’s instructions might be ambiguous.
- **Use realistic context.** Real users mention file paths, column names, and personal context. Prompts like “process this data” are too vague to test anything useful.

Don’t worry about defining specific pass/fail checks yet — just the prompts and expected outputs. You’ll add detailed checks (called assertions) after you see what the first run produces.


[​](#running-evals)

Running evals

The core pattern is to run each test case twice: once **with the skill** and once **without it** (or with a previous version). This gives you a baseline to compare against.


[​](#workspace-structure)

Workspace structure

Organize eval results in a workspace directory alongside your skill directory. Each pass through the full eval loop gets its own `iteration-N/` directory. Within that, each test case gets an eval directory with `with_skill/` and `without_skill/` subdirectories:

```python
csv-analyzer/
├── SKILL.md
└── evals/
    └── evals.json
csv-analyzer-workspace/
└── iteration-1/
    ├── eval-top-months-chart/
    │   ├── with_skill/
    │   │   ├── outputs/       # Files produced by the run
    │   │   ├── timing.json    # Tokens and duration
    │   │   └── grading.json   # Assertion results
    │   └── without_skill/
    │       ├── outputs/
    │       ├── timing.json
    │       └── grading.json
    ├── eval-clean-missing-emails/
    │   ├── with_skill/
    │   │   ├── outputs/
    │   │   ├── timing.json
    │   │   └── grading.json
    │   └── without_skill/
    │       ├── outputs/
    │       ├── timing.json
    │       └── grading.json
    └── benchmark.json         # Aggregated statistics
```

The main file you author by hand is `evals/evals.json`. The other JSON files (`grading.json`, `timing.json`, `benchmark.json`) are produced during the eval process — by the agent, by scripts, or by you.


[​](#spawning-runs)

Spawning runs

Each eval run should start with a clean context — no leftover state from previous runs or from the skill development process. This ensures the agent follows only what the `SKILL.md` tells it. In environments that support subagents (Claude Code, for example), this isolation comes naturally: each child task starts fresh. Without subagents, use a separate session for each run. For each run, provide:

- The skill path (or no skill for the baseline)
- The test prompt
- Any input files
- The output directory

Here’s an example of the instructions you’d give the agent for a single with-skill run:

```python
Execute this task:
- Skill path: /path/to/csv-analyzer
- Task: I have a CSV of monthly sales data in data/sales_2025.csv.
  Can you find the top 3 months by revenue and make a bar chart?
- Input files: evals/files/sales_2025.csv
- Save outputs to: csv-analyzer-workspace/iteration-1/eval-top-months-chart/with_skill/outputs/
```

For the baseline, use the same prompt but without the skill path, saving to `without_skill/outputs/`. When improving an existing skill, use the previous version as your baseline. Snapshot it before editing (`cp -r <skill-path> <workspace>/skill-snapshot/`), point the baseline run at the snapshot, and save to `old_skill/outputs/` instead of `without_skill/`.


[​](#capturing-timing-data)

Capturing timing data

Timing data lets you compare how much time and tokens the skill costs relative to the baseline — a skill that dramatically improves output quality but triples token usage is a different trade-off than one that’s both better and cheaper. When each run completes, record the token count and duration:

timing.json

```python
{
  "total_tokens": 84852,
  "duration_ms": 23332
}
```

In Claude Code, when a subagent task finishes, the [task completion notification](https://agentskills.io/05-Agent-SDK/agent-sdk-typescript.md#sdk-task-notification-message) includes `total_tokens` and `duration_ms`. Save these values immediately — they aren’t persisted anywhere else.


[​](#writing-assertions)

Writing assertions

Assertions are verifiable statements about what the output should contain or achieve. Add them after you see your first round of outputs — you often don’t know what “good” looks like until the skill has run. Good assertions:

- `"The output file is valid JSON"` — programmatically verifiable.
- `"The bar chart has labeled axes"` — specific and observable.
- `"The report includes at least 3 recommendations"` — countable.

Weak assertions:

- `"The output is good"` — too vague to grade.
- `"The output uses exactly the phrase 'Total Revenue: $X'"` — too brittle; correct output with different wording would fail.

Not everything needs an assertion. Some qualities — writing style, visual design, whether the output “feels right” — are hard to decompose into pass/fail checks. These are better caught during [human review](#reviewing-results-with-a-human). Reserve assertions for things that can be checked objectively. Add assertions to each test case in `evals/evals.json`:

evals/evals.json

```python
{
  "skill_name": "csv-analyzer",
  "evals": [
    {
      "id": 1,
      "prompt": "I have a CSV of monthly sales data in data/sales_2025.csv. Can you find the top 3 months by revenue and make a bar chart?",
      "expected_output": "A bar chart image showing the top 3 months by revenue, with labeled axes and values.",
      "files": ["evals/files/sales_2025.csv"],
      "assertions": [
        "The output includes a bar chart image file",
        "The chart shows exactly 3 months",
        "Both axes are labeled",
        "The chart title or caption mentions revenue"
      ]
    }
  ]
}
```


[​](#grading-outputs)

Grading outputs

Grading means evaluating each assertion against the actual outputs and recording **PASS** or **FAIL** with specific evidence. The evidence should quote or reference the output, not just state an opinion. The simplest approach is to give the outputs and assertions to an LLM and ask it to evaluate each one. For assertions that can be checked by code (valid JSON, correct row count, file exists with expected dimensions), use a verification script — scripts are more reliable than LLM judgment for mechanical checks and reusable across iterations.

grading.json

```python
{
  "assertion_results": [
    {
      "text": "The output includes a bar chart image file",
      "passed": true,
      "evidence": "Found chart.png (45KB) in outputs directory"
    },
    {
      "text": "The chart shows exactly 3 months",
      "passed": true,
      "evidence": "Chart displays bars for March, July, and November"
    },
    {
      "text": "Both axes are labeled",
      "passed": false,
      "evidence": "Y-axis is labeled 'Revenue ($)' but X-axis has no label"
    },
    {
      "text": "The chart title or caption mentions revenue",
      "passed": true,
      "evidence": "Chart title reads 'Top 3 Months by Revenue'"
    }
  ],
  "summary": {
    "passed": 3,
    "failed": 1,
    "total": 4,
    "pass_rate": 0.75
  }
}
```


[​](#grading-principles)

Grading principles

- **Require concrete evidence for a PASS.** Don’t give the benefit of the doubt. If an assertion says “includes a summary” and the output has a section titled “Summary” with one vague sentence, that’s a FAIL — the label is there but the substance isn’t.
- **Review the assertions themselves, not just the results.** While grading, notice when assertions are too easy (always pass regardless of skill quality), too hard (always fail even when the output is good), or unverifiable (can’t be checked from the output alone). Fix these for the next iteration.

For comparing two skill versions, try **blind comparison**: present both outputs to an LLM judge without revealing which came from which version. The judge scores holistic qualities — organization, formatting, usability, polish — on its own rubric, free from bias about which version “should” be better. This complements assertion grading: two outputs might both pass all assertions but differ significantly in overall quality.


[​](#aggregating-results)

Aggregating results

Once every run in the iteration is graded, compute summary statistics per configuration and save them to `benchmark.json` alongside the eval directories (e.g., `csv-analyzer-workspace/iteration-1/benchmark.json`):

benchmark.json

```python
{
  "run_summary": {
    "with_skill": {
      "pass_rate": { "mean": 0.83, "stddev": 0.06 },
      "time_seconds": { "mean": 45.0, "stddev": 12.0 },
      "tokens": { "mean": 3800, "stddev": 400 }
    },
    "without_skill": {
      "pass_rate": { "mean": 0.33, "stddev": 0.10 },
      "time_seconds": { "mean": 32.0, "stddev": 8.0 },
      "tokens": { "mean": 2100, "stddev": 300 }
    },
    "delta": {
      "pass_rate": 0.50,
      "time_seconds": 13.0,
      "tokens": 1700
    }
  }
}
```

The `delta` tells you what the skill costs (more time, more tokens) and what it buys (higher pass rate). A skill that adds 13 seconds but improves pass rate by 50 percentage points is probably worth it. A skill that doubles token usage for a 2-point improvement might not be.

Standard deviation (`stddev`) is only meaningful with multiple runs per eval. In early iterations with just 2-3 test cases and single runs, focus on the raw pass counts and the delta — the statistical measures become useful as you expand the test set and run each eval multiple times.


[​](#analyzing-patterns)

Analyzing patterns

Aggregate statistics can hide important patterns. After computing the benchmarks:

- **Remove or replace assertions that always pass in both configurations.** These don’t tell you anything useful — the model handles them fine without the skill. They inflate the with-skill pass rate without reflecting actual skill value.
- **Investigate assertions that always fail in both configurations.** Either the assertion is broken (asking for something the model can’t do), the test case is too hard, or the assertion is checking for the wrong thing. Fix these before the next iteration.
- **Study assertions that pass with the skill but fail without.** This is where the skill is clearly adding value. Understand *why* — which instructions or scripts made the difference?
- **Tighten instructions when results are inconsistent across runs.** If the same eval passes sometimes and fails others (reflected as high `stddev` in the benchmark), the eval may be flaky (sensitive to model randomness), or the skill’s instructions may be ambiguous enough that the model interprets them differently each time. Add examples or more specific guidance to reduce ambiguity.
- **Check time and token outliers.** If one eval takes 3x longer than the others, read its execution transcript (the full log of what the model did during the run) to find the bottleneck.


[​](#reviewing-results-with-a-human)

Reviewing results with a human

Assertion grading and pattern analysis catch a lot, but they only check what you thought to write assertions for. A human reviewer brings a fresh perspective — catching issues you didn’t anticipate, noticing when the output is technically correct but misses the point, or spotting problems that are hard to express as pass/fail checks. For each test case, review the actual outputs alongside the grades. Record specific feedback for each test case and save it in the workspace (e.g., as a `feedback.json` alongside the eval directories):

feedback.json

```python
{
  "eval-top-months-chart": "The chart is missing axis labels and the months are in alphabetical order instead of chronological.",
  "eval-clean-missing-emails": ""
}
```

“The chart is missing axis labels” is actionable; “looks bad” is not. Empty feedback means the output looked fine — that test case passed your review. During the [iteration step](#iterating-on-the-skill), focus your improvements on the test cases where you had specific complaints.


[​](#iterating-on-the-skill)

Iterating on the skill

After grading and reviewing, you have three sources of signal:

- **Failed assertions** point to specific gaps — a missing step, an unclear instruction, or a case the skill doesn’t handle.
- **Human feedback** points to broader quality issues — the approach was wrong, the output was poorly structured, or the skill produced a technically correct but unhelpful result.
- **Execution transcripts** reveal *why* things went wrong. If the agent ignored an instruction, the instruction may be ambiguous. If the agent spent time on unproductive steps, those instructions may need to be simplified or removed.

The most effective way to turn these signals into skill improvements is to give all three — along with the current `SKILL.md` — to an LLM and ask it to propose changes. The LLM can synthesize patterns across failed assertions, reviewer complaints, and transcript behavior that would be tedious to connect manually. When prompting the LLM, include these guidelines:

- **Generalize from feedback.** The skill will be used across many different prompts, not just the test cases. Fixes should address underlying issues broadly rather than adding narrow patches for specific examples.
- **Keep the skill lean.** Fewer, better instructions often outperform exhaustive rules. If transcripts show wasted work (unnecessary validation, unneeded intermediate outputs), remove those instructions. If pass rates plateau despite adding more rules, the skill may be over-constrained — try removing instructions and see if results hold or improve.
- **Explain the why.** Reasoning-based instructions (“Do X because Y tends to cause Z”) work better than rigid directives (“ALWAYS do X, NEVER do Y”). Models follow instructions more reliably when they understand the purpose.
- **Bundle repeated work.** If every test run independently wrote a similar helper script (a chart builder, a data parser), that’s a signal to bundle the script into the skill’s `scripts/` directory. See [Using scripts](skill-creation-using-scripts.md) for how to do this.


[​](#the-loop)

The loop

1.  Give the eval signals and current `SKILL.md` to an LLM and ask it to propose improvements.
2.  Review and apply the changes.
3.  Rerun all test cases in a new `iteration-<N+1>/` directory.
4.  Grade and aggregate the new results.
5.  Review with a human. Repeat.

Stop when you’re satisfied with the results, feedback is consistently empty, or you’re no longer seeing meaningful improvement between iterations.

The [`skill-creator`](https://github.com/anthropics/skills/tree/main/skills/skill-creator) Skill automates much of this workflow — running evals, grading assertions, aggregating benchmarks, and presenting results for human review.

[Optimizing descriptions](skill-creation-optimizing-descriptions.md)[Using scripts](skill-creation-using-scripts.md)

---

## Optimizing skill descriptions - Agent Skills

Source: https://agentskills.io/skill-creation/optimizing-descriptions
Path: 08-Plugins-Skills/skill-creation-optimizing-descriptions.md

## On this page

- [How skill triggering works](#how-skill-triggering-works)
- [Writing effective descriptions](#writing-effective-descriptions)
- [Designing trigger eval queries](#designing-trigger-eval-queries)
  - [Should-trigger queries](#should-trigger-queries)
  - [Should-not-trigger queries](#should-not-trigger-queries)
  - [Tips for realism](#tips-for-realism)
- [Testing whether a description triggers](#testing-whether-a-description-triggers)
  - [Running multiple times](#running-multiple-times)
- [Avoiding overfitting with train/validation splits](#avoiding-overfitting-with-train%2Fvalidation-splits)
- [The optimization loop](#the-optimization-loop)
- [Applying the result](#applying-the-result)
- [Next steps](#next-steps)

For skill creators

# Optimizing skill descriptions

Copy pageCopy page

How to improve your skill’s description so it triggers reliably on relevant prompts.

Copy pageCopy page

A skill only helps if it gets activated. The `description` field in your `SKILL.md` frontmatter is the primary mechanism agents use to decide whether to load a skill for a given task. An under-specified description means the skill won’t trigger when it should; an over-broad description means it triggers when it shouldn’t. This guide covers how to systematically test and improve your skill’s description for triggering accuracy.


[​](#how-skill-triggering-works)

How skill triggering works

Agents use [progressive disclosure](specification.md#progressive-disclosure) to manage context. At startup, they load only the `name` and `description` of each available skill — just enough to decide when a skill might be relevant. When a user’s task matches a description, the agent reads the full `SKILL.md` into context and follows its instructions. This means the description carries the entire burden of triggering. If the description doesn’t convey when the skill is useful, the agent won’t know to reach for it. One important nuance: agents typically only consult skills for tasks that require knowledge or capabilities beyond what they can handle alone. A simple, one-step request like “read this PDF” may not trigger a PDF skill even if the description matches perfectly, because the agent can handle it with basic tools. Tasks that involve specialized knowledge — an unfamiliar API, a domain-specific workflow, or an uncommon format — are where a well-written description can make the difference.


[​](#writing-effective-descriptions)

Writing effective descriptions

Before testing, it helps to know what a good description looks like. A few principles:

- **Use imperative phrasing.** Frame the description as an instruction to the agent: “Use this skill when…” rather than “This skill does…” The agent is deciding whether to act, so tell it when to act.
- **Focus on user intent, not implementation.** Describe what the user is trying to achieve, not the skill’s internal mechanics. The agent matches against what the user asked for.
- **Err on the side of being pushy.** Explicitly list contexts where the skill applies, including cases where the user doesn’t name the domain directly: “even if they don’t explicitly mention ‘CSV’ or ‘analysis.’”
- **Keep it concise.** A few sentences to a short paragraph is usually right — long enough to cover the skill’s scope, short enough that it doesn’t bloat the agent’s context across many skills. The [specification](specification.md#description-field) enforces a hard limit of 1024 characters.


[​](#designing-trigger-eval-queries)

Designing trigger eval queries

To test triggering, you need a set of eval queries — realistic user prompts labeled with whether they should or shouldn’t trigger your skill.

eval_queries.json

```python
[
  { "query": "I've got a spreadsheet in ~/data/q4_results.xlsx with revenue in col C and expenses in col D — can you add a profit margin column and highlight anything under 10%?", "should_trigger": true },
  { "query": "whats the quickest way to convert this json file to yaml", "should_trigger": false }
]
```

Aim for about 20 queries: 8-10 that should trigger and 8-10 that shouldn’t.


[​](#should-trigger-queries)

Should-trigger queries

These test whether the description captures the skill’s scope. Vary them along several axes:

- **Phrasing**: some formal, some casual, some with typos or abbreviations.
- **Explicitness**: some name the skill’s domain directly (“analyze this CSV”), others describe the need without naming it (“my boss wants a chart from this data file”).
- **Detail**: mix terse prompts with context-heavy ones — a short “analyze my sales CSV and make a chart” alongside a longer message with file paths, column names, and backstory.
- **Complexity**: vary the number of steps and decision points. Include single-step tasks alongside multi-step workflows to test whether the agent can discern the skill is relevant when the task it addresses is buried in a larger chain.

The most useful should-trigger queries are ones where the skill would help but the connection isn’t obvious from the query alone. These are the cases where description wording makes the difference — if the query already asks for exactly what the skill does, any reasonable description would trigger.


[​](#should-not-trigger-queries)

Should-not-trigger queries

The most valuable negative test cases are **near-misses** — queries that share keywords or concepts with your skill but actually need something different. These test whether the description is precise, not just broad. For a CSV analysis skill, weak negative examples would be:

- `"Write a fibonacci function"` — obviously irrelevant, tests nothing.
- `"What's the weather today?"` — no keyword overlap, too easy.

Strong negative examples:

- `"I need to update the formulas in my Excel budget spreadsheet"` — shares “spreadsheet” and “data” concepts, but needs Excel editing, not CSV analysis.
- `"can you write a python script that reads a csv and uploads each row to our postgres database"` — involves CSV, but the task is database ETL, not analysis.


[​](#tips-for-realism)

Tips for realism

Real user prompts contain context that generic test queries lack. Include:

- File paths (`~/Downloads/report_final_v2.xlsx`)
- Personal context (`"my manager asked me to..."`)
- Specific details (column names, company names, data values)
- Casual language, abbreviations, and occasional typos


[​](#testing-whether-a-description-triggers)

Testing whether a description triggers

The basic approach: run each query through your agent with the skill installed and observe whether the agent invokes it. Make sure the skill is registered and discoverable by your agent — how this works varies by client (e.g., a skills directory, a configuration file, or a CLI flag). Most agent clients provide some form of observability — execution logs, tool call histories, or verbose output — that lets you see which skills were consulted during a run. Check your client’s documentation for details. The skill triggered if the agent loaded your skill’s `SKILL.md`; it didn’t trigger if the agent proceeded without consulting it. A query “passes” if:

- `should_trigger` is `true` and the skill was invoked, or
- `should_trigger` is `false` and the skill was not invoked.


[​](#running-multiple-times)

Running multiple times

Model behavior is nondeterministic — the same query might trigger the skill on one run but not the next. Run each query multiple times (3 is a reasonable starting point) and compute a **trigger rate**: the fraction of runs where the skill was invoked. A should-trigger query passes if its trigger rate is above a threshold (0.5 is a reasonable default). A should-not-trigger query passes if its trigger rate is below that threshold. With 20 queries at 3 runs each, that’s 60 invocations. You’ll want to script this. Here’s the general structure — replace the `claude` invocation and detection logic in `check_triggered` with whatever your agent client provides:

```python
#!/bin/bash
QUERIES_FILE="${1:?Usage: $0 <queries.json>}"
SKILL_NAME="my-skill"
RUNS=3

# This example uses Claude Code's JSON output to check for Skill tool calls.
# Replace this function with detection logic for your agent client.
# Should return 0 (success) if the skill was invoked, 1 otherwise.
check_triggered() {
  local query="$1"
  claude -p "$query" --output-format json 2>/dev/null \
    | jq -e --arg skill "$SKILL_NAME" \
      'any(.messages[].content[]; .type == "tool_use" and .name == "Skill" and .input.skill == $skill)' \
      > /dev/null 2>&1
}

count=$(jq length "$QUERIES_FILE")
for i in $(seq 0 $((count - 1))); do
  query=$(jq -r ".[$i].query" "$QUERIES_FILE")
  should_trigger=$(jq -r ".[$i].should_trigger" "$QUERIES_FILE")
  triggers=0

  for run in $(seq 1 $RUNS); do
    check_triggered "$query" && triggers=$((triggers + 1))
  done

  jq -n \
    --arg query "$query" \
    --argjson should_trigger "$should_trigger" \
    --argjson triggers "$triggers" \
    --argjson runs "$RUNS" \
    '{query: $query, should_trigger: $should_trigger, triggers: $triggers, runs: $runs, trigger_rate: ($triggers / $runs)}'
done | jq -s '.'
```

If your agent client supports it, you can stop a run early once the outcome is clear — the agent either consulted the skill or started working without it. This can significantly reduce the time and cost of running the full eval set.


[​](#avoiding-overfitting-with-train/validation-splits)

Avoiding overfitting with train/validation splits

If you optimize the description against all your queries, you risk overfitting — crafting a description that works for these specific phrasings but fails on new ones. The solution is to split your query set:

- **Train set (~60%)**: the queries you use to identify failures and guide improvements.
- **Validation set (~40%)**: queries you set aside and only use to check whether improvements generalize.

Make sure both sets contain a proportional mix of should-trigger and should-not-trigger queries — don’t accidentally put all the positives in one set. Shuffle randomly and keep the split fixed across iterations so you’re comparing apples to apples. If you’re using a script like the one [above](#running-multiple-times), you can split your queries into two files — `train_queries.json` and `validation_queries.json` — and run the script against each one separately.


[​](#the-optimization-loop)

The optimization loop

1.  **Evaluate** the current description on both *train and validation sets*. The train results guide your changes; the validation results tell you whether those changes are generalizing.
2.  **Identify failures** in the *train set*: which should-trigger queries didn’t trigger? Which should-not-trigger queries did?
    - Only use train set failures to guide your changes — whether you’re revising the description yourself or prompting an LLM, keep validation set results out of the process.
3.  **Revise the description.** Focus on generalizing:
    - If should-trigger queries are failing, the description may be too narrow. Broaden the scope or add context about when the skill is useful.
    - If should-not-trigger queries are false-triggering, the description may be too broad. Add specificity about what the skill does *not* do, or clarify the boundary between this skill and adjacent capabilities.
    - Avoid adding specific keywords from failed queries — that’s overfitting. Instead, find the general category or concept those queries represent and address that.
    - If you’re stuck after several iterations, try a structurally different approach to the description rather than incremental tweaks. A different framing or sentence structure may break through where refinement can’t.
    - Check that the description stays under the 1024-character limit — descriptions tend to grow during optimization.
4.  **Repeat** steps 1-3 until all *train set* queries pass or you stop seeing meaningful improvement.
5.  **Select the best iteration** by its validation pass rate — the fraction of queries in the *validation set* that passed. Note that the best description may not be the last one you produced; an earlier iteration might have a higher validation pass rate than later ones that overfit to the train set.

Five iterations is usually enough. If performance isn’t improving, the issue may be with the queries (too easy, too hard, or poorly labeled) rather than the description.

The [`skill-creator`](https://github.com/anthropics/skills/tree/main/skills/skill-creator) Skill automates this loop end-to-end: it splits the eval set, evaluates trigger rates in parallel, proposes description improvements using Claude, and generates a live HTML report you can watch as it runs.


[​](#applying-the-result)

Applying the result

Once you’ve selected the best description:

1.  Update the `description` field in your `SKILL.md` frontmatter.
2.  Verify the description is under the [1024-character limit](specification.md#description-field).
3.  Verify the description triggers as expected. Try a few prompts manually as a quick sanity check. For a more rigorous test, write 5-10 fresh queries (a mix of should-trigger and should-not-trigger) and run them through the eval script — since these queries were never part of the optimization process, they give you an honest check on whether the description generalizes.

Before and after:

```python
# Before
description: Process CSV files.

# After
description: >
  Analyze CSV and tabular data files — compute summary statistics,
  add derived columns, generate charts, and clean messy data. Use this
  skill when the user has a CSV, TSV, or Excel file and wants to
  explore, transform, or visualize the data, even if they don't
  explicitly mention "CSV" or "analysis."
```

The improved description is more specific about what the skill does (summary stats, derived columns, charts, cleaning) and broader about when it applies (CSV, TSV, Excel; even without explicit keywords).


[​](#next-steps)

Next steps

Once your skill triggers reliably, you’ll want to evaluate whether it produces good outputs. See [Evaluating skill output quality](skill-creation-evaluating-skills.md) for how to set up test cases, grade results, and iterate.

[Best practices](skill-creation-best-practices.md)[Evaluating skills](skill-creation-evaluating-skills.md)

---

## Quickstart - Agent Skills

Source: https://agentskills.io/skill-creation/quickstart
Path: 08-Plugins-Skills/skill-creation-quickstart.md

## On this page

- [Prerequisites](#prerequisites)
- [Create the skill](#create-the-skill)
- [Try it out](#try-it-out)
- [How it works](#how-it-works)
- [Next steps](#next-steps)

For skill creators

# Quickstart

Copy pageCopy page

Create your first Agent Skill and see it work in VS Code.

Copy pageCopy page

In this tutorial, you’ll create a skill that gives an agent the capability to roll dice using a random number generator.


[​](#prerequisites)

Prerequisites

- [VS Code](https://code.visualstudio.com/) with [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot)

This tutorial uses VS Code, but Agent Skills are an open format. The same skill works in any compatible agent, including Claude Code and OpenAI Codex.


[​](#create-the-skill)

Create the skill

A skill is a folder containing a `SKILL.md` file. VS Code looks for skills in `.agents/skills/` by default. Create `.agents/skills/roll-dice/SKILL.md` in your project:

.agents/skills/roll-dice/SKILL.md

```` shiki
---
name: roll-dice
description: Roll dice using a random number generator. Use when asked to roll a die (d6, d20, etc.), roll dice, or generate a random dice roll.
---

To roll a die, use the following command that generates a random number from 1
to the given number of sides:

```bash
echo $((RANDOM % <sides> + 1))
```

```powershell
Get-Random -Minimum 1 -Maximum (<sides> + 1)
```

Replace `<sides>` with the number of sides on the die (e.g., 6 for a standard
die, 20 for a d20).
````

That’s it — one file, under 20 lines. Here’s what each part does:

- **`name`** — A short identifier for the skill. Must match the folder name.
- **`description`** — Tells the agent when to use this skill. This is how the agent decides whether to activate it.
- **The body** — Instructions the agent follows when the skill activates. Here, the agent is instructed to generate a random number using a terminal command, substituting the number of sides from the user’s request.


[​](#try-it-out)

Try it out

1.  Open your project in VS Code.
2.  Open the Copilot Chat panel.
3.  Select **Agent** mode from the mode dropdown at the bottom of the chat panel.
4.  Type `/skills` to confirm that `roll-dice` appears in the list. If it doesn’t, check that the file is at `.agents/skills/roll-dice/SKILL.md` relative to your project root.
5.  Ask: **“Roll a d20”**

The agent should activate the `roll-dice` skill. It may ask for permission to run a terminal command — allow it. It will run the command and return a random number between 1 and 20.

Tool-use reliability varies across models — some follow skill instructions and run commands consistently, while others may attempt to answer on their own. If the agent responds without running a terminal command, try selecting a different model from the model dropdown.


[​](#how-it-works)

How it works

Here’s what happened behind the scenes:

1.  **Discovery** — When the chat session started, the agent scanned default skill directories and found your skill. It read only the `name` and `description`, just enough to know when the skill might be relevant.
2.  **Activation** — When you asked about rolling dice, the agent matched your question to the skill’s description and loaded the full `SKILL.md` body into context.
3.  **Execution** — The agent followed the instructions in the body, adapting the terminal command to the number of sides in your request.

This process uses **progressive disclosure** to let the agent access many skills without loading all their instructions up front.


[​](#next-steps)

Next steps

You’ve created a working Agent Skill. From here:

- **[Best practices](skill-creation-best-practices.md)** — How to write skills that are well-scoped and effective.
- **[Optimizing skill descriptions](skill-creation-optimizing-descriptions.md)** — Test and improve your skill’s description so it activates on the right prompts.
- **[Specification](specification.md)** — The complete format reference for `SKILL.md` files.
- **[Example skills](https://github.com/anthropics/skills)** — Browse real-world skills on GitHub.

[Client Showcase](clients.md)[Best practices](skill-creation-best-practices.md)

---

## Using scripts in skills - Agent Skills

Source: https://agentskills.io/skill-creation/using-scripts
Path: 08-Plugins-Skills/skill-creation-using-scripts.md

## On this page

- [One-off commands](#one-off-commands)
- [Referencing scripts from SKILL.md](#referencing-scripts-from-skill-md)
- [Self-contained scripts](#self-contained-scripts)
- [Designing scripts for agentic use](#designing-scripts-for-agentic-use)
  - [Avoid interactive prompts](#avoid-interactive-prompts)
  - [Document usage with --help](#document-usage-with-help)
  - [Write helpful error messages](#write-helpful-error-messages)
  - [Use structured output](#use-structured-output)
  - [Further considerations](#further-considerations)

For skill creators

# Using scripts in skills

Copy pageCopy page

How to run commands and bundle executable scripts in your skills.

Copy pageCopy page

Skills can instruct agents to run shell commands and bundle reusable scripts in a `scripts/` directory. This guide covers one-off commands, self-contained scripts with their own dependencies, and how to design script interfaces for agentic use.


[​](#one-off-commands)

One-off commands

When an existing package already does what you need, you can reference it directly in your `SKILL.md` instructions without a `scripts/` directory. Many ecosystems provide tools that auto-resolve dependencies at runtime.

- uvx

- pipx

- npx

- bunx

- deno run

- go run

[uvx](https://docs.astral.sh/uv/guides/tools/) runs Python packages in isolated environments with aggressive caching. It ships with [uv](https://docs.astral.sh/uv/).

```python
uvx ruff@0.8.0 check .
uvx black@24.10.0 .
```

- Not bundled with Python — requires a separate install.
- Fast. Caches aggressively so repeat runs are near-instant.

[pipx](https://pipx.pypa.io/) runs Python packages in isolated environments. Available via OS package managers (`apt install pipx`, `brew install pipx`).

```python
pipx run 'black==24.10.0' .
pipx run 'ruff==0.8.0' check .
```

- Not bundled with Python — requires a separate install.
- A mature alternative to `uvx`. While `uvx` has become the standard recommendation, `pipx` remains a reliable option with broader OS package manager availability.

[npx](https://docs.npmjs.com/cli/commands/npx) runs npm packages, downloading them on demand. It ships with npm (which ships with Node.js).

```python
npx eslint@9 --fix .
npx create-vite@6 my-app
```

- Bundled with Node.js — no extra install needed.
- Downloads the package, runs it, and caches it for future use.
- Pin versions with `npx package@version` for reproducibility.

[bunx](https://bun.sh/docs/cli/bunx) is Bun’s equivalent of `npx`. It ships with [Bun](https://bun.sh/).

```python
bunx eslint@9 --fix .
bunx create-vite@6 my-app
```

- Drop-in replacement for `npx` in Bun-based environments.
- Only appropriate when the user’s environment has Bun rather than Node.js.

[deno run](https://docs.deno.com/runtime/reference/cli/run/) runs scripts directly from URLs or specifiers. It ships with [Deno](https://deno.com/).

```python
deno run npm:create-vite@6 my-app
deno run --allow-read npm:eslint@9 -- --fix .
```

- Permission flags (`--allow-read`, etc.) are required for filesystem/network access.
- Use `--` to separate Deno flags from the tool’s own flags.

[go run](https://pkg.go.dev/cmd/go#hdr-Compile_and_run_Go_program) compiles and runs Go packages directly. It is built into the `go` command.

```python
go run golang.org/x/tools/cmd/goimports@v0.28.0 .
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run
```

- Built into Go — no extra tooling needed.
- Pin versions or use `@latest` to make the command explicit.

**Tips for one-off commands in skills:**

- **Pin versions** (e.g., `npx eslint@9.0.0`) so the command behaves the same over time.
- **State prerequisites** in your `SKILL.md` (e.g., “Requires Node.js 18+”) rather than assuming the agent’s environment has them. For runtime-level requirements, use the [`compatibility` frontmatter field](specification.md#compatibility-field).
- **Move complex commands into scripts.** A one-off command works well when you’re invoking a tool with a few flags. When a command grows complex enough that it’s hard to get right on the first try, a tested script in `scripts/` is more reliable.


[​](#referencing-scripts-from-skill-md)

Referencing scripts from `SKILL.md`

Use **relative paths from the skill directory root** to reference bundled files. The agent resolves these paths automatically — no absolute paths needed. List available scripts in your `SKILL.md` so the agent knows they exist:

SKILL.md

```python
## Available scripts

- **`scripts/validate.sh`** — Validates configuration files
- **`scripts/process.py`** — Processes input data
```

Then instruct the agent to run them:

SKILL.md

```` shiki
## Workflow

1. Run the validation script:
   ```bash
   bash scripts/validate.sh "$INPUT_FILE"
   ```

2. Process the results:
   ```bash
   python3 scripts/process.py --input results.json
   ```
````

The same relative-path convention works in support files like `references/*.md` — script execution paths (in code blocks) are relative to the **skill directory root**, because the agent runs commands from there.


[​](#self-contained-scripts)

Self-contained scripts

When you need reusable logic, bundle a script in `scripts/` that declares its own dependencies inline. The agent can run the script with a single command — no separate manifest file or install step required. Several languages support inline dependency declarations:

- Python

- Deno

- Bun

- Ruby

[PEP 723](https://peps.python.org/pep-0723/) defines a standard format for inline script metadata. Declare dependencies in a TOML block inside `# ///` markers:

scripts/extract.py

```python
# /// script
# dependencies = [
#   "beautifulsoup4",
# ]
# ///

from bs4 import BeautifulSoup

html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
print(BeautifulSoup(html, "html.parser").select_one("p.info").get_text())
```

Run with [uv](https://docs.astral.sh/uv/) (recommended):

```python
uv run scripts/extract.py
```

`uv run` creates an isolated environment, installs the declared dependencies, and runs the script. [pipx](https://pipx.pypa.io/) (`pipx run scripts/extract.py`) also supports PEP 723.

- Pin versions with [PEP 508](https://peps.python.org/pep-0508/) specifiers: `"beautifulsoup4>=4.12,<5"`.
- Use `requires-python` to constrain the Python version.
- Use `uv lock --script` to create a lockfile for full reproducibility.

Deno’s `npm:` and `jsr:` import specifiers make every script self-contained by default:

scripts/extract.ts

```python
#!/usr/bin/env -S deno run

import * as cheerio from "npm:cheerio@1.0.0";

const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
```

```python
deno run scripts/extract.ts
```

- Use `npm:` for npm packages, `jsr:` for Deno-native packages.
- Version specifiers follow semver: `@1.0.0` (exact), `@^1.0.0` (compatible).
- Dependencies are cached globally. Use `--reload` to force re-fetch.
- Packages with native addons (node-gyp) may not work — packages that ship pre-built binaries work best.

Bun auto-installs missing packages at runtime when no `node_modules` directory is found. Pin versions directly in the import path:

scripts/extract.ts

```python
#!/usr/bin/env bun

import * as cheerio from "cheerio@1.0.0";

const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
```

```python
bun run scripts/extract.ts
```

- No `package.json` or `node_modules` needed. TypeScript works natively.
- Packages are cached globally. First run downloads; subsequent runs are near-instant.
- If a `node_modules` directory exists anywhere up the directory tree, auto-install is disabled and Bun falls back to standard Node.js resolution.

Bundler ships with Ruby since 2.6. Use `bundler/inline` to declare gems directly in the script:

scripts/extract.rb

```python
require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'nokogiri'
end

html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
doc = Nokogiri::HTML(html)
puts doc.at_css('p.info').text
```

```python
ruby scripts/extract.rb
```

- Pin versions explicitly (`gem 'nokogiri', '~> 1.16'`) — there is no lockfile.
- An existing `Gemfile` or `BUNDLE_GEMFILE` env var in the working directory can interfere.


[​](#designing-scripts-for-agentic-use)

Designing scripts for agentic use

When an agent runs your script, it reads stdout and stderr to decide what to do next. A few design choices make scripts dramatically easier for agents to use.


[​](#avoid-interactive-prompts)

Avoid interactive prompts

This is a hard requirement of the agent execution environment. Agents operate in non-interactive shells — they cannot respond to TTY prompts, password dialogs, or confirmation menus. A script that blocks on interactive input will hang indefinitely. Accept all input via command-line flags, environment variables, or stdin:

```python
# Bad: hangs waiting for input
$ python scripts/deploy.py
Target environment: _

# Good: clear error with guidance
$ python scripts/deploy.py
Error: --env is required. Options: development, staging, production.
Usage: python scripts/deploy.py --env staging --tag v1.2.3
```


[​](#document-usage-with-help)

Document usage with `--help`

`--help` output is the primary way an agent learns your script’s interface. Include a brief description, available flags, and usage examples:

```python
Usage: scripts/process.py [OPTIONS] INPUT_FILE

Process input data and produce a summary report.

Options:
  --format FORMAT    Output format: json, csv, table (default: json)
  --output FILE      Write output to FILE instead of stdout
  --verbose          Print progress to stderr

Examples:
  scripts/process.py data.csv
  scripts/process.py --format csv --output report.csv data.csv
```

Keep it concise — the output enters the agent’s context window alongside everything else it’s working with.


[​](#write-helpful-error-messages)

Write helpful error messages

When an agent gets an error, the message directly shapes its next attempt. An opaque “Error: invalid input” wastes a turn. Instead, say what went wrong, what was expected, and what to try:

```python
Error: --format must be one of: json, csv, table.
       Received: "xml"
```


[​](#use-structured-output)

Use structured output

Prefer structured formats — JSON, CSV, TSV — over free-form text. Structured formats can be consumed by both the agent and standard tools (`jq`, `cut`, `awk`), making your script composable in pipelines.

```python
# Whitespace-aligned — hard to parse programmatically
NAME          STATUS    CREATED
my-service    running   2025-01-15

# Delimited — unambiguous field boundaries
{"name": "my-service", "status": "running", "created": "2025-01-15"}
```

**Separate data from diagnostics:** send structured data to stdout and progress messages, warnings, and other diagnostics to stderr. This lets the agent capture clean, parseable output while still having access to diagnostic information when needed.


[​](#further-considerations)

Further considerations

- **Idempotency.** Agents may retry commands. “Create if not exists” is safer than “create and fail on duplicate.”
- **Input constraints.** Reject ambiguous input with a clear error rather than guessing. Use enums and closed sets where possible.
- **Dry-run support.** For destructive or stateful operations, a `--dry-run` flag lets the agent preview what will happen.
- **Meaningful exit codes.** Use distinct exit codes for different failure types (not found, invalid arguments, auth failure) and document them in your `--help` output so the agent knows what each code means.
- **Safe defaults.** Consider whether destructive operations should require explicit confirmation flags (`--confirm`, `--force`) or other safeguards appropriate to the risk level.
- **Predictable output size.** Many agent harnesses automatically truncate tool output beyond a threshold (e.g., 10-30K characters), potentially losing critical information. If your script might produce large output, default to a summary or a reasonable limit, and support flags like `--offset` so the agent can request more information when needed. Alternatively, if output is large and not amenable to pagination, require agents to pass an `--output` flag that specifies either an output file or `-` to explicitly opt in to stdout.

[Evaluating skills](skill-creation-evaluating-skills.md)[Adding skills support](client-implementation-adding-skills-support.md)

---

## Extend Claude with skills - Claude Code Docs

Source: https://code.claude.com/docs/en/skills
Path: 08-Plugins-Skills/skills.md

## On this page

- [Bundled skills](#bundled-skills)
  - [Check your setup with /doctor](#check-your-setup-with-%2Fdoctor)
  - [Run and verify your app](#run-and-verify-your-app)
  - [Run your checks before each commit](#run-your-checks-before-each-commit)
  - [Work on Claude API projects](#work-on-claude-api-projects)
- [Getting started](#getting-started)
  - [Create your first skill](#create-your-first-skill)
- [Choose where skills load](#where-skills-live)
  - [Load skills in monorepos and subdirectories](#discovery-from-parent-and-nested-directories)
  - [Load skills from a directory outside the project](#skills-from-additional-directories)
  - [Run a same-named skill from an added directory](#run-a-same-named-skill-from-an-added-directory)
  - [Resolve skills that share a name](#resolve-skills-that-share-a-name)
  - [Use skills in Cowork and cloud sessions](#skills-in-cowork-and-cloud-sessions)
  - [Skills synced from claude.ai](#how-synced-skills-behave)
  - [Where synced skills load](#where-synced-skills-load)
  - [When a synced skill name matches another command](#when-a-synced-skill-name-matches-another-command)
  - [Names reserved for synced skills](#names-reserved-for-synced-skills)
  - [How Claude Code handles the frontmatter of a synced skill](#how-claude-code-handles-the-frontmatter-of-a-synced-skill)
  - [How Claude Code handles the body of a synced skill](#how-claude-code-handles-the-body-of-a-synced-skill)
  - [Edit a skill during a session](#live-change-detection)
  - [Remove a skill](#remove-a-skill)
- [Configure skills](#configure-skills)
  - [Types of skill content](#types-of-skill-content)
  - [Frontmatter reference](#frontmatter-reference)
  - [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code)
  - [How a skill gets its command name](#how-a-skill-gets-its-command-name)
  - [Characters a skill name can’t hold](#characters-a-skill-name-cant-hold)
  - [Available string substitutions](#available-string-substitutions)
  - [Add supporting files](#add-supporting-files)
  - [Control who invokes a skill](#control-who-invokes-a-skill)
  - [Where you write the skill’s name](#where-you-write-the-skill%E2%80%99s-name)
  - [Skill content lifecycle](#skill-content-lifecycle)
  - [Pre-approve tools for a skill](#pre-approve-tools-for-a-skill)
  - [When managed settings lock hooks to plugins](#when-managed-settings-lock-hooks-to-plugins)
  - [When only managed permission rules apply](#when-only-managed-permission-rules-apply)
  - [Pass arguments to skills](#pass-arguments-to-skills)
  - [Stack several skills in one message](#stack-several-skills-in-one-message)
- [Advanced patterns](#advanced-patterns)
  - [Inject dynamic context](#inject-dynamic-context)
  - [How injected commands run](#how-injected-commands-run)
  - [When an injected command fails](#when-an-injected-command-fails)
  - [Control characters in injected commands](#control-characters-in-injected-commands)
  - [Permission checks on injected commands](#permission-checks-on-injected-commands)
  - [Run skills in a subagent](#run-skills-in-a-subagent)
  - [Example: Research skill using Explore agent](#example-research-skill-using-explore-agent)
  - [Restrict Claude’s skill access](#restrict-claude%E2%80%99s-skill-access)
  - [Override skill visibility from settings](#override-skill-visibility-from-settings)
  - [Find unused skills](#find-unused-skills)
- [Evaluate and iterate on a skill](#evaluate-and-iterate-on-a-skill)
  - [Run evals with skill-creator](#run-evals-with-skill-creator)
- [Share skills](#share-skills)
  - [Generate visual output](#generate-visual-output)
- [Troubleshooting](#troubleshooting)
  - [Skill not triggering](#skill-not-triggering)
  - [Skill triggers too often](#skill-triggers-too-often)
  - [Claude stops following a skill](#claude-stops-following-a-skill)
  - [Skill descriptions are cut short](#skill-descriptions-are-cut-short)
  - [Personal skills disappeared](#personal-skills-disappeared)
- [Related resources](#related-resources)

Skills

# Extend Claude with skills

Copy pageCopy page

Create, manage, and share skills to extend Claude’s capabilities in Claude Code. Includes custom commands and bundled skills.

Copy pageCopy page

Skills extend what Claude can do. Create a `SKILL.md` file with instructions, and Claude adds it to its toolkit. Claude uses skills when relevant, or you can invoke one directly with `/skill-name`. Create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact. Unlike CLAUDE.md content, a skill’s body loads only when it’s used, so long reference material costs almost nothing until you need it.

For built-in commands like `/help` and `/compact`, and bundled skills like `/debug` and `/code-review`, see the [commands reference](../02-Claude-Code-CLI/commands.md).**Custom commands have been merged into skills.** A file at `.claude/commands/deploy.md` and a skill at `.claude/skills/deploy/SKILL.md` both create `/deploy` and work the same way. Your existing `.claude/commands/` files keep working. Skills add optional features: a directory for supporting files, frontmatter to [control whether you or Claude invokes them](#control-who-invokes-a-skill), and the ability for Claude to load them automatically when relevant.

Claude Code skills follow the [Agent Skills](https://agentskills.io) open standard, which works across multiple AI tools. Claude Code extends the standard with additional features like [invocation control](#control-who-invokes-a-skill), [subagent execution](#run-skills-in-a-subagent), and [dynamic context injection](#inject-dynamic-context). See [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code) for which frontmatter fields are part of the standard and which are Claude Code extensions.


[​](#bundled-skills)

Bundled skills

Claude Code includes a set of bundled skills, such as `/doctor`, `/code-review`, `/batch`, `/debug`, `/loop`, and `/claude-api`. Bundled skills are prompt-based: they give Claude detailed instructions and let it orchestrate the work using its tools. Most built-in commands instead execute fixed logic directly. You invoke a bundled skill the same way as any other skill, by typing `/` followed by the skill name. Claude invokes some bundled skills automatically when relevant; others, including `/verify`, run only when you invoke them, which keeps you in control of when these longer-running checks spend time and tokens. Most bundled skills are available in every session. A few depend on a specific feature: `/workflow-authoring`, for example, is available only when [dynamic workflows](../02-Claude-Code-CLI/workflows.md) are enabled. To turn bundled skills off, use the [`disableBundledSkills`](../02-Claude-Code-CLI/settings-reference.md#disablebundledskills) setting.

The [`/doctor`](../02-Claude-Code-CLI/commands.md#all-commands) setup checkup stays typable when `disableBundledSkills` is on. To hide it, set the `DISABLE_DOCTOR_COMMAND` environment variable or a [`skillOverrides`](#override-skill-visibility-from-settings) entry of `"doctor": "off"`.

Bundled skills are listed alongside built-in commands in the [commands reference](../02-Claude-Code-CLI/commands.md), marked **Skill** in the Purpose column.


[​](#check-your-setup-with-/doctor)

Check your setup with `/doctor`

Run `/doctor` at the Claude Code prompt for a setup checkup that diagnoses issues and can fix them. Claude reports its findings first and asks for confirmation before changing anything. The checkup covers these areas:

- **Installation health**: duplicate or leftover installs, `PATH` problems, unparseable settings files, and whether a newer version is available on your [release channel](../01-Getting-Started/setup.md#configure-release-channel)
- **Extensions**: unused skills, MCP servers, and plugins compared with their context cost, and slow [hooks](../07-Hooks/hooks.md)
- **`CLAUDE.md` files**: local `CLAUDE.md` files that duplicate checked-in ones, checked-in [`CLAUDE.md` content Claude could derive from the codebase](../02-Claude-Code-CLI/memory.md#my-claude-md-is-too-large), and the always-loaded guidance that remains, which Claude offers to migrate into skills and nested `CLAUDE.md` files that load on demand
- **Permissions**: an offer to make [auto mode](../02-Claude-Code-CLI/permissions.md#permission-modes) your default permission mode and to [pre-approve](../02-Claude-Code-CLI/permissions.md) read-only commands that you frequently deny
- **Usage**: on a Pro, Max, Team, or Enterprise plan, the [changes `/usage` recommends](../17-Billing-Plans/costs.md#plan-usage-breakdown) to lower your plan usage, measured over the last 7 days. Claude proposes the changes it can make itself and reports the rest as advice, such as a command for you to run. The usage check requires Claude Code v2.1.305 or later, and `/doctor` leaves it out while [Remote Control](../02-Claude-Code-CLI/remote-control.md) is connected.

For read-only installation diagnostics without starting a session, run `claude doctor` in your terminal instead. To audit your instructions rather than your setup, run `/doctor prompt-audit` at the Claude Code prompt. Claude [checks your `CLAUDE.md` files, skills, and other configuration](../02-Claude-Code-CLI/memory.md#audit-your-instruction-files) for outdated or conflicting instructions instead of running the checkup. The `prompt-audit` subcommand requires Claude Code v2.1.283 or later.


[​](#run-and-verify-your-app)

Run and verify your app

Three bundled skills work together to launch your app and confirm changes against the running app instead of tests alone:

| Skill                  | Purpose                                                                                                           |
|:-----------------------|:------------------------------------------------------------------------------------------------------------------|
| `/run`                 | Launch and drive your app to see a change working                                                                 |
| `/verify`              | Build and run your app to confirm a code change does what it should, without falling back to tests or type checks |
| `/run-skill-generator` | Teach `/run` and `/verify` how to build and launch your project                                                   |

`/run` and `/verify` work without setup. They infer the launch from your project type (CLI, server, TUI, browser-driven) and from what’s in your README, `package.json`, or `Makefile`. That inference gets unreliable for projects that need anything beyond a standard launch: a database, an env file, a graphical session, a multi-step build. `/run-skill-generator` records the recipe instead. It gets your app running from a clean environment, captures what worked (the install commands, the env vars, the launch script), and commits it as a per-project skill at `.claude/skills/run-<name>/`. After that, `/run`, `/verify`, and any other agent in the repo follow the recorded recipe instead of rediscovering it. Run `/run-skill-generator` once per project, and again if the build or launch process changes. `/verify` can also record its own recipe. When it has to build and drive your app without a recorded recipe, it writes what worked to `.claude/skills/verify/SKILL.md` at the repo root, or in the touched package directory in a monorepo, so later runs and other agents follow the same steps. At the repo root, the recorded skill replaces the bundled `/verify`. Claude edits the recorded file only when it steered a run wrong, such as a command that failed or a missing step, so you can commit the file without per-session diffs.


[​](#run-your-checks-before-each-commit)

Run your checks before each commit

When a session starts with a skill named `verify` or `simplify` in place, Claude Code’s commit instructions tell Claude to run it right before each commit, except for changes to docs or tests. This requires Claude Code v2.1.286 or later. Claude gets that instruction when these conditions hold at the start of the session:

- **Location**: the skill loads from the enterprise, personal, project, or additional-directory [location](#where-skills-live), or from a `.claude/commands/` file with that name. The recipe that `/verify` records at your repo root is a project skill, so it counts. The bundled `/verify` and `/simplify`, plugin skills, and skills from your claude.ai account don’t count.
- **Invocation**: Claude can invoke the skill. If you’ve [stopped Claude from invoking it](#control-who-invokes-a-skill), for example with `disable-model-invocation: true`, Claude doesn’t get the instruction.
- **Git instructions**: you haven’t turned off [`includeGitInstructions`](../02-Claude-Code-CLI/settings-reference.md#includegitinstructions). Turning it off removes this instruction together with the rest of the built-in commit and PR instructions.


[​](#work-on-claude-api-projects)

Work on Claude API projects

The bundled `/claude-api` skill loads [Claude API](../04-API-Reference/Endpoints/overview.md) and [Managed Agents](../04-API-Reference/Other/managed-agents-overview.md) reference material for your project’s language. Claude also activates it automatically when your code imports `anthropic` or `@anthropic-ai/sdk`. To start one of the skill’s workflows, type a subcommand after the skill name at the Claude Code prompt, for example `/claude-api migrate`. The table lists what each subcommand does and the earliest Claude Code version that includes it. `migrate` and `managed-agents-onboard` predate v2.1.221, the oldest version the table tracks.

| Subcommand                                 | What it does                                                                                                                                                                                                                                                                                                                  | Minimum version       |
|:-------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------|
| `migrate`                                  | Update your existing Claude API code to a newer model                                                                                                                                                                                                                                                                         | Earlier than v2.1.221 |
| `upgrade`                                  | Move your project’s Anthropic SDK dependency across a major version, currently the Python `anthropic` package from 0.x to 1.x                                                                                                                                                                                                 | v2.1.236 or later     |
| `api-version`                              | Update your Claude API code for a newer [API version](../04-API-Reference/Endpoints/versioning.md), then switch your requests to that version                                                                                                                                                                          | v2.1.307 or later     |
| `managed-agents-onboard`                   | Walk through creating a new Managed Agent                                                                                                                                                                                                                                                                                     | Earlier than v2.1.221 |
| `managed-agents-onboard <url>`             | Build the Managed Agent that the page at the URL describes, such as a page in the [Managed Agents docs](../04-API-Reference/Other/managed-agents-overview.md)                                                                                                                                                          | v2.1.290 or later     |
| `managed-agents-onboard <quickstart-name>` | Build one of the Console’s quickstart templates, such as `deep-researcher`. If you give one word that isn’t a template name, Claude lists the valid names                                                                                                                                                                     | v2.1.290 or later     |
| `managed-agents-secure [<agent-id>]`       | Read the settings of one Managed Agent and report where they match the [Secure your Managed Agents deployment](../04-API-Reference/Other/managed-agents-secure-deployment.md) guide or differ from it. Requires the [`ant` CLI](../04-API-Reference/Other/cli-sdks-libraries-cli-quickstart.md)                 | v2.1.304 or later     |
| `prompt-audit`                             | Flag instructions written for older models in your prompts, skills, and tool descriptions and propose fixes as a diff                                                                                                                                                                                                         | v2.1.221 or later     |
| `cost-optimize`                            | Profile where your project’s Claude API spend goes and propose savings from options such as prompt caching, trimming unneeded input and output tokens, batch processing, effort, and model choice, one change at a time                                                                                                       | v2.1.247 or later     |
| `build-eval`                               | Build an eval set for your Claude-powered app                                                                                                                                                                                                                                                                                 | v2.1.259 or later     |
| `hillclimb`                                | Iteratively improve your app against an existing eval                                                                                                                                                                                                                                                                         | v2.1.259 or later     |
| `preserved-thinking-migration`             | Find the edits your integration makes to earlier turns, its system prompt, or its tool list that invalidate [preserved thinking](../04-API-Reference/Guides/build-with-claude-preserved-thinking.md) blocks, measure how much reasoning each one drops, and propose fixes one at a time, re-measuring after each change | v2.1.282 or later     |


[​](#getting-started)

Getting started


[​](#create-your-first-skill)

Create your first skill

This example creates a skill that summarizes the uncommitted changes in your git repository and flags anything risky. It pulls the live diff into the prompt before Claude reads it, so the response is grounded in your actual working tree rather than what Claude can guess from open files. Claude loads the skill automatically when you ask about your changes, or you can invoke it directly with `/summarize-changes`.

1

Create the skill directory

Create a directory for the skill in your personal skills folder. Personal skills are available across all your projects.

```python
mkdir -p ~/.claude/skills/summarize-changes
```

2

Write SKILL.md

Every skill needs a `SKILL.md` file with two parts: YAML frontmatter between `---` markers that tells Claude when to use the skill, and markdown content with the instructions Claude follows when the skill runs. The directory name, or the frontmatter `name` when you set one, becomes the command you type, and the `description` helps Claude decide when to load the skill automatically.Save this to `~/.claude/skills/summarize-changes/SKILL.md`:

```python
---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---

## Current changes

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes.
```

The `` !`git diff HEAD` `` line uses [dynamic context injection](#inject-dynamic-context): Claude Code runs the command and replaces the line with its output before Claude sees the skill content, so the instructions arrive with the current diff already inlined.

3

Test the skill

Open a git project, make a small edit to any file, and start Claude Code by running `claude`. You can test the skill two ways.**Let Claude invoke it automatically** by asking something that matches the description:

```python
What did I change?
```

**Or invoke it directly** with the skill name:

```python
/summarize-changes
```

Either way, Claude should respond with a short summary of your edit and a list of risks.


[​](#where-skills-live)

Choose where skills load

Where you save a skill decides which sessions load it. Save it under your home directory to get it in every project, commit it to a repository to share it with everyone who works there, or distribute it through a plugin or managed settings to reach a whole team.

| Location             | Path                                                                                                                      | Loads in                                                                                                                                                                                                                                                           |
|:---------------------|:--------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Enterprise           | `.claude/skills/<skill-name>/SKILL.md` in the [managed settings directory](../13-Enterprise-Admin/managed-settings.md#delivery-mechanisms) | All users on machines where your organization deploys it                                                                                                                                                                                                           |
| Personal             | `~/.claude/skills/<skill-name>/SKILL.md`                                                                                  | All your projects on this machine, but not [Cowork or cloud sessions](#skills-in-cowork-and-cloud-sessions)                                                                                                                                                        |
| Project              | `.claude/skills/<skill-name>/SKILL.md`                                                                                    | Sessions in this repository, but not [cloud threads in a project](../02-Claude-Code-CLI/claude-projects.md#what-threads-pick-up-from-your-repositories). Commit it so your team gets it too                                                                                        |
| Nested               | `<subdir>/.claude/skills/<skill-name>/SKILL.md`                                                                           | Sessions started in or below `<subdir>`, but not cloud threads in a project. Outside those threads, a session started above it loads the skill once Claude works on files there. See [monorepos and subdirectories](#discovery-from-parent-and-nested-directories) |
| Additional directory | `.claude/skills/<skill-name>/SKILL.md` in a directory you pass with `--add-dir`                                           | That session. See [directories outside the project](#skills-from-additional-directories)                                                                                                                                                                           |
| Plugin               | `<plugin>/skills/<skill-name>/SKILL.md`                                                                                   | Wherever the [plugin](plugins.md) is enabled, as `/plugin-name:skill-name`                                                                                                                                                                          |
| claude.ai account    | Skills enabled for your claude.ai account                                                                                 | Cowork sessions, cloud sessions, and terminal sessions where you sign in with that account. See [Skills synced from claude.ai](#how-synced-skills-behave)                                                                                                          |

Skill folders also follow these rules:

- **Symlinked folders**: a `<skill-name>` entry in the enterprise, personal, or project location can be a symlink to a directory elsewhere on disk. Claude Code reads `SKILL.md` from the target and loads the skill once even if several locations point at the same target. Plugin skills [handle symlinks differently](plugins-host-marketplace.md#share-files-within-a-marketplace-with-symlinks).
- **Reserved name `synced`**: don’t name a skill folder `synced`, in any capitalization. Claude Code uses `~/.claude/skills/synced/` for [skills downloaded from claude.ai](#where-synced-skills-load) and skips a skill you author at that name in the enterprise, personal, and project locations.
- **Reserved name `anthropic-skills`**: outside a plugin, a skill folder or command file whose name is `anthropic-skills` or starts with `anthropic-skills:` doesn’t load. See [Names reserved for synced skills](#names-reserved-for-synced-skills).
- **Characters in the name**: outside a plugin, a skill folder doesn’t load when its name has a character such as a zero-width space or `<`. See [Characters a skill name can’t hold](#characters-a-skill-name-cant-hold).
- **Command files**: a Markdown file in `.claude/commands/` is the older format and still works. It supports the same [frontmatter](#frontmatter-reference) except `name` and `paths`. To find the name you type to invoke it, see [How a skill gets its command name](#how-a-skill-gets-its-command-name). Prefer a skill for new work, since skills also support [supporting files](#add-supporting-files).
- **Skill folder as a plugin**: add a `.claude-plugin/plugin.json` to a personal or project skill folder and Claude Code can load it as a [plugin](plugins-loading.md#plugins-shared-through-a-repository) named `<name>@skills-dir` with its own agents, hooks, and MCP servers. In a project’s `.claude/skills/`, this requires accepting the workspace trust dialog first, and in either location it requires that your administrator hasn’t [blocked skills-directory plugins](plugins-loading.md#skills-directory-plugins-blocked-by-managed-settings).


[​](#discovery-from-parent-and-nested-directories)

Load skills in monorepos and subdirectories

Claude Code loads project skills from `.claude/skills/` in the directory where you start it and in every parent directory up to the repository root, so starting in `packages/frontend/` still picks up skills defined at the root. When you [move the session with `/cd`](../02-Claude-Code-CLI/permissions.md#move-the-session-to-another-directory) on v2.1.246 or later, Claude Code adds the new directory’s project skills. In a session running in a linked [git worktree](../02-Claude-Code-CLI/worktrees.md) that you created with `--worktree` or `git worktree add`, Claude Code searches parent directories only up to the worktree root. On Claude Code v2.1.277 or later, when the worktree checkout has no `.claude/skills` directory at its root, Claude Code loads the main checkout’s project skills instead. See [What worktrees share with the main checkout](../02-Claude-Code-CLI/worktrees.md#what-worktrees-share-with-the-main-checkout). Skills in a `.claude/skills/` directory below where you started don’t load at startup. They load the first time Claude reads or edits a file in that subdirectory and stay available for the rest of the session. Until then they don’t appear in the `/` menu and you can’t invoke them by name. To load them sooner, run `/add-dir` with the subdirectory’s path, which requires Claude Code v2.1.257 or later. For a worktree session that you start from the desktop app, see [What worktrees share with the main checkout](../02-Claude-Code-CLI/worktrees.md#what-worktrees-share-with-the-main-checkout). When a nested skill’s directory name matches another skill’s name, both stay available. With a `deploy` skill at the repository root and another in `apps/web/.claude/skills/`:

- `/deploy` runs the root skill. Claude Code also lists the directory-qualified variants for Claude, with an instruction to invoke the one whose directory holds the files it’s working on, so the nested skill still applies to work in `apps/web/`.
- `/apps/web:deploy` runs the nested skill on its own. Its description names the directory it applies to.


[​](#skills-from-additional-directories)

Load skills from a directory outside the project

When you add a directory with `--add-dir` or `/add-dir`, Claude Code loads the skills in that directory’s `.claude/skills/`, along with its `.claude/commands/` and `.claude/agents/`. Directories the Agent SDK adds through [`additionalDirectories`](../05-Agent-SDK/agent-sdk-typescript.md#options) in TypeScript or [`add_dirs`](../05-Agent-SDK/agent-sdk-python.md#claudeagentoptions) in Python load the same way, because the SDK passes them as `--add-dir`. The `permissions.additionalDirectories` setting in `settings.json` grants file access only and loads none of these. Claude Code watches `.claude/skills/` in a directory you pass with `--add-dir` at launch, as [Edit a skill during a session](#live-change-detection) describes. It doesn’t watch the added directory’s `.claude/commands/` or `.claude/agents/`, so restart the session after changing a file there. These loads depend on the `project` [setting source](../05-Agent-SDK/agent-sdk-claude-code-features.md#control-filesystem-settings-with-settingsources), which is on by default. A [`strictPluginOnlyCustomization`](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization) policy, [bare mode](../02-Claude-Code-CLI/headless.md#start-faster-with-bare-mode), and [`--safe-mode`](../02-Claude-Code-CLI/cli-reference.md#cli-flags) each restrict them further, as those pages describe. See [Additional directories grant file access, not configuration](../02-Claude-Code-CLI/permissions.md#additional-directories-grant-file-access-not-configuration) for the full table of what an added directory loads, including `CLAUDE.md` and plugin settings.


[​](#run-a-same-named-skill-from-an-added-directory)

Run a same-named skill from an added directory

An added directory’s skill gets a prefix when its folder name clashes with an enterprise, personal, or project skill, or one in a directory added earlier. To run it, type `/<prefix>:<skill-name>` at the Claude Code prompt. The `/` menu lists the skill under its prefixed name, and [How a skill gets its command name](#how-a-skill-gets-its-command-name) describes where the prefix comes from. That name requires Claude Code v2.1.309 or later. For example, with `--add-dir ../cli` and a `verify` skill in both your project and `../cli`:

- `/verify` runs your project’s skill
- `/cli:verify` runs the skill from `../cli`


[​](#resolve-skills-that-share-a-name)

Resolve skills that share a name

When two skills share a directory or file name, where each one came from decides which one `/name` runs. For a name set by the frontmatter `name` field, see [How a skill gets its command name](#how-a-skill-gets-its-command-name).

| Same name in                                                                                                                           | Which one runs                                                                                                                                                                                                  |
|:---------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Two of enterprise, personal, and project                                                                                               | Enterprise over personal, and personal over project. With `deploy` in both `~/.claude/skills/` and the project’s `.claude/skills/`, `/deploy` runs the personal one                                             |
| Any of those locations and a [bundled skill](#bundled-skills)                                                                          | Your skill replaces the bundled command, but not its aliases. A project `code-review` skill replaces `/code-review`, and the bundled alias `/review` never runs your skill                                      |
| Any of those locations and a [built-in command](../02-Claude-Code-CLI/commands.md)                                                                     | In a local terminal session, your skill replaces the built-in command, but not its aliases. A project `usage` skill replaces `/usage`, and the built-in alias `/cost` still runs the built-in command           |
| A skill and a file in `.claude/commands/`                                                                                              | The skill                                                                                                                                                                                                       |
| A project-root skill and a nested skill                                                                                                | Both load. See [monorepos and subdirectories](#discovery-from-parent-and-nested-directories)                                                                                                                    |
| An enterprise, personal, or project skill and a skill in an [added directory outside the project](#skills-from-additional-directories) | The enterprise, personal, or project skill, while the added directory’s skill runs under a prefixed name. See [run a same-named skill from an added directory](#run-a-same-named-skill-from-an-added-directory) |
| Two [added directories](#skills-from-additional-directories) outside the project                                                       | The skill from the directory added first, in `--add-dir` or `/add-dir` order. The other runs under a [prefixed name](#run-a-same-named-skill-from-an-added-directory)                                           |
| A plugin skill and a skill at any of the locations above                                                                               | Both load, because plugin skills are namespaced as `/plugin-name:skill-name`                                                                                                                                    |
| Any of the above and the short name of a skill [synced from your claude.ai account](#how-synced-skills-behave)                         | The other skill or command. The synced skill is then listed and runs only under its full name. See [When a synced skill name matches another command](#when-a-synced-skill-name-matches-another-command)        |


[​](#skills-in-cowork-and-cloud-sessions)

Use skills in Cowork and cloud sessions

[Cowork](../15-Claude-AI-Features/product-cowork.md) sessions and [cloud sessions](../02-Claude-Code-CLI/cloud-environments.md#what-carries-over-from-your-setup), including [cloud scheduled tasks](../02-Claude-Code-CLI/web-scheduled-tasks.md), don’t read `~/.claude/skills/` on your machine. Both interactive and scheduled Cowork sessions load the skills enabled for your claude.ai account, synced at session start. Manage them from **Customize** in the Desktop app sidebar or from the skills settings on claude.ai. If a skill exists only in `~/.claude/skills/` on your machine, Claude Code reports that the skill was not found when a [cloud scheduled task](../02-Claude-Code-CLI/web-scheduled-tasks.md) invokes it. This happens because each run starts as a fresh cloud session. To make a personal skill available in these sessions:

- For Cowork sessions, cloud sessions, and cloud threads in a [project](../02-Claude-Code-CLI/claude-projects.md#what-threads-pick-up-from-your-repositories), enable the skill for your claude.ai account. In a project’s cloud thread, skills committed to a repository’s `.claude/skills/` aren’t available. [Some sessions in a self-hosted environment](../13-Enterprise-Admin/self-hosted-environments-configuration.md#how-each-session%E2%80%99s-config-is-assembled) don’t load your account’s skills.
- For cloud sessions other than project threads, you can instead commit the skill to the repository’s `.claude/skills/`. Plugins declared in the repository’s `.claude/settings.json` and plugins enabled only in your user settings [don’t load in cloud sessions](../02-Claude-Code-CLI/cloud-environments.md#what-carries-over-from-your-setup).

[Desktop scheduled tasks](../16-Mobile-Desktop/desktop-scheduled-tasks.md) run locally on your machine, so they do load `~/.claude/skills/`.


[​](#how-synced-skills-behave)

Skills synced from claude.ai

This section applies to you if you use Cowork or cloud sessions, or sign in to Claude Code in your terminal with a claude.ai account. In those sessions, Claude Code loads the skills enabled for your claude.ai account, with no setup on your part, as [Where synced skills load](#where-synced-skills-load) describes. Those skills include the ones you create or turn on in your claude.ai settings, skills your organization provides there, and Anthropic’s built-in skills such as `pdf` and `xlsx`. Claude Code downloads a synced skill from your account rather than reading a file you wrote on the machine where the session runs, so it applies rules to synced skills that don’t apply to the skills you store in the [skills locations](#where-skills-live).


[​](#where-synced-skills-load)

Where synced skills load

In a Cowork or cloud session, Claude Code loads the skills enabled for your claude.ai account, and [Skills in Cowork and cloud sessions](#skills-in-cowork-and-cloud-sessions) says how to choose which skills those sessions get. In your terminal, Claude Code syncs those skills in sessions where you sign in with your claude.ai account. When the session starts, Claude Code downloads your account’s skills into `~/.claude/skills/synced/` in the background, then checks claude.ai for changes while the session runs. When a check finds that a skill was added, edited, or turned off on claude.ai, Claude Code adds, updates, or removes it in the running session without a restart. Syncing in terminal sessions requires Claude Code v2.1.273 or later. The checks run less often while the session is idle:

- **While you or Claude work in the session**: a check runs about every 10 minutes.
- **While the session is idle**: a check runs about every 40 minutes. When you type in the session again, Claude Code checks within a few minutes if the last check was more than 10 minutes ago.

The sync never delays startup, because Claude waits for a skill’s download only when it invokes that skill. A short [non-interactive](../02-Claude-Code-CLI/headless.md) run can therefore finish before a newly added skill downloads, in which case a later session downloads it. To make a non-interactive run download your skills and wait for the list before it answers the prompt, set [`CLAUDE_CODE_SYNC_SKILLS`](../02-Claude-Code-CLI/env-vars.md#variables) to `1`. Before v2.1.273, terminal sessions downloaded them only in a `-p` run with this variable set. Claude Code syncs only in a session that signs in with your claude.ai account and [fetches feature flags from Anthropic](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching). It doesn’t sync in these sessions:

- A session that doesn’t use a sign-in stored by `/login`, such as one that authenticates with an API key, or one where `ANTHROPIC_AUTH_TOKEN`, `CLAUDE_CODE_OAUTH_TOKEN`, or an `apiKeyHelper` script supplies the credential
- A session that doesn’t fetch feature flags, such as one on Amazon Bedrock or one where you set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`
- A session in [bare mode](../02-Claude-Code-CLI/headless.md#start-faster-with-bare-mode) or one you start with `--safe-mode`
- A session where your organization’s managed settings [lock skills to plugin sources](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization-skills), or one you start with a [`--setting-sources`](../02-Claude-Code-CLI/cli-reference.md#cli-flags) list that leaves out `user`

If you sign in with `/login` during a session, restart Claude Code to start syncing. Skills that an earlier session synced stay on disk. Claude Code loads them in later sessions signed in to the same account, even when it can’t reach claude.ai. Claude Code downloads synced skills and never uploads them. If you or Claude edit a file under `~/.claude/skills/synced/`, the change isn’t saved to your claude.ai account, and a later sync can overwrite or remove it. To change a synced skill, update it on claude.ai; the next sync downloads the new version. To see which skills synced, run `/skills`. The menu lists them under `claude.ai sync`. Some of Anthropic’s skills, such as `pdf` and `xlsx`, always sync. For the rest, turn a skill on or off in your skills settings on claude.ai to change whether it syncs. To stop syncing on a machine, set [`syncClaudeAiSkills`](../02-Claude-Code-CLI/settings-reference.md#syncclaudeaiskills) to `false` in your user settings. Claude Code stops downloading, and the next time it starts it moves the skills it already synced to `~/.claude/skills/.trash/` and no longer loads them. Your organization can turn syncing off for everyone by turning off Skills on claude.ai. To stop syncing while leaving Skills on, it can set the same key in [managed settings](../13-Enterprise-Admin/managed-settings.md). If your organization turns Skills off on claude.ai, Claude Code removes the downloaded skills and they stop loading. The removed skills move to `~/.claude/skills/.trash/`, where you can recover the files until the [retention sweep](../02-Claude-Code-CLI/claude-directory.md#cleaned-up-automatically) deletes them. Once your organization turns Skills back on, Claude Code downloads the skills you enabled at the next sync.


[​](#when-a-synced-skill-name-matches-another-command)

When a synced skill name matches another command

You can invoke a synced skill by its short name, `/<name>`, or by its full name, `/anthropic-skills:<name>`. When another command uses the short name, `/<name>` runs the other command, and the synced skill runs only as `/anthropic-skills:<name>`. With a local `deploy` skill and a synced `deploy`, `/deploy` runs the local skill and `/anthropic-skills:deploy` runs the synced one. Before v2.1.269, a synced skill had only its short name. In the `/` menu, `/skills`, and `/context`, a synced skill appears under its short name, or under its full name while another command uses the short name. Run `/skills` in your session. A note under the list explains each synced skill that lost its short name. If one of your personal skills or command files in `~/.claude/` uses the name, the note also says what to rename or delete to free it. From v2.1.269 through v2.1.280, these lists showed every synced skill under its full name, and `/skills` had no such note; both changed in v2.1.281. The command that uses the short name can be any of these:

- A built-in command or a [bundled skill](#bundled-skills), including one that’s unavailable in your session, for example after you turn bundled skills off
- A skill at any [local level](#where-skills-live) or a file in `.claude/commands/`
- A plugin skill
- An [MCP prompt](../06-MCP-Tools/General/mcp.md#use-mcp-prompts-as-commands)

Claude Code labels synced skills so you can tell where they came from. The `/skills` menu and `/context` group synced skills under `claude.ai sync`, and the `/` command menu marks them as coming from claude.ai. When it compares names, Claude Code ignores case, spacing, and invisible characters, and treats compatibility forms such as fullwidth letters and dash variants as their plain equivalents. For example, a synced skill named `Commit` and a local skill named `commit` count as the same name, so `/commit` keeps running your local skill. A name that differs only by a look-alike letter from another alphabet counts as a different name, and the `claude.ai sync` label is how you tell the two apart. These checks and labels require Claude Code v2.1.228 or later.


[​](#names-reserved-for-synced-skills)

Names reserved for synced skills

Claude Code reserves the name `anthropic-skills`, and every name inside that namespace such as `anthropic-skills:pdf`, for skills synced from claude.ai, so a synced skill’s full name never runs anything else. The name is reserved in every session, whether or not you sign in with a claude.ai account.

- **A skill folder, a frontmatter `name`, a file or subfolder in `.claude/commands/`, or a [saved workflow](../02-Claude-Code-CLI/workflows.md#save-the-workflow-for-reuse)**: it doesn’t load. A [startup notice](../02-Claude-Code-CLI/errors.md#a-skill-command-or-workflow-wasnt-loaded-because-its-name-is-reserved) names the first item to rename or edit.
- **A plugin named `anthropic-skills`**: it loads. When one of its skills and a synced skill are both named `<name>`, `/anthropic-skills:<name>` runs the synced skill.
- **An MCP server named `anthropic-skills`**: it connects and its tools work, but [its prompts don’t appear as commands](../06-MCP-Tools/General/mcp.md#use-mcp-prompts-as-commands). Rename the server in your MCP configuration to list them.


[​](#how-claude-code-handles-the-frontmatter-of-a-synced-skill)

How Claude Code handles the frontmatter of a synced skill

Claude Code applies two rules to a synced skill’s frontmatter:

- The frontmatter applies in every kind of session, so an `allowed-tools` grant goes through the normal [permission flow](../02-Claude-Code-CLI/permissions.md). If your organization sets `allowManagedPermissionRulesOnly`, the grant [doesn’t apply](#when-only-managed-permission-rules-apply).
- Claude Code sanitizes the display text the skill supplies, such as its description. It removes control characters, and in text that reaches Claude, such as the description, it also escapes angle brackets so the text can’t imitate Claude Code’s internal formatting. This sanitization requires Claude Code v2.1.228 or later.


[​](#how-claude-code-handles-the-body-of-a-synced-skill)

How Claude Code handles the body of a synced skill

What Claude Code does with a synced skill’s body depends on where the session runs:

- In a cloud session, the body keeps the behavior a local skill has, because the session runs in an isolated container.
- In a Cowork session on your desktop, the body keeps the behavior a local skill has, except that Claude Code replaces every `!` command line with the [`disableSkillShellExecution` placeholder](#inject-dynamic-context), as it does for every skill you supply there.
- In any other session on your machine, Claude Code doesn’t run [`!` commands](#inject-dynamic-context), doesn’t attach the files that `@` references name the way it does for a local skill, and doesn’t substitute the `${CLAUDE_PROJECT_DIR}` and `${CLAUDE_SESSION_ID}` placeholders, so the `@` references and both placeholders reach Claude as literal text. A `!` command line reaches Claude as literal text too, or as that placeholder when `disableSkillShellExecution` is on. This handling requires Claude Code v2.1.228 or later.


[​](#live-change-detection)

Edit a skill during a session

Claude Code watches skill directories for file changes, except in [bare mode](../02-Claude-Code-CLI/headless.md#start-faster-with-bare-mode). When you add, edit, or remove a skill under `~/.claude/skills/`, the project `.claude/skills/`, or a `.claude/skills/` inside an `--add-dir` directory, Claude Code picks up the change within the current session, without a restart. If you create a top-level skills directory that didn’t exist when the session started, run [`/reload-skills`](../02-Claude-Code-CLI/commands.md#all-commands) to pick up the skills you put there. Claude Code isn’t watching that directory yet, so run `/reload-skills` again after each later change there. Live change detection covers `SKILL.md` text only. For a skill folder that is also a [plugin](plugins-loading.md#plugins-shared-through-a-repository), changes to `hooks/`, `.mcp.json`, `agents/`, and `output-styles/` need `/reload-plugins` to take effect.


[​](#remove-a-skill)

Remove a skill

How you remove a skill depends on where it came from:

- **Personal or project skill**: delete the skill’s directory, `~/.claude/skills/<skill-name>/` or `.claude/skills/<skill-name>/`. Claude Code [drops it from `/skills` in the current session](#live-change-detection); content Claude Code already loaded from it follows the [skill content lifecycle](#skill-content-lifecycle).
- **Enterprise skill**: an administrator deletes the skill’s directory from `.claude/skills/` inside the [managed settings directory](../13-Enterprise-Admin/managed-settings.md#delivery-mechanisms), for example `/etc/claude-code/.claude/skills/<skill-name>/` on Linux.
- **Plugin skill**: disable or uninstall the plugin that provides it, from the `/plugin` menu or with `/plugin uninstall <plugin-name>@<marketplace-name>`. Claude Code unloads the plugin’s skills when [the change applies](plugins-cli-reference.md#reload-plugins) or when you restart.
- **Skill synced from claude.ai**: turn the skill off for your claude.ai account, in the same place you [enabled it](#skills-in-cowork-and-cloud-sessions). Claude Code removes it from `~/.claude/skills/synced/` the next time it [syncs your skills](#where-synced-skills-load). If you delete the directory by hand instead, the next sync downloads it again while the skill stays enabled on claude.ai.
- **Bundled skill**: set [`disableBundledSkills`](#bundled-skills) to `true` to turn off bundled skills, or set one skill to `"off"` in [`skillOverrides`](#override-skill-visibility-from-settings) to hide it.

To keep a personal or project skill but stop Claude from invoking it on its own, set [`disable-model-invocation: true`](#control-who-invokes-a-skill) in its frontmatter, or `"user-invocable-only"` in [`skillOverrides`](#override-skill-visibility-from-settings) when you don’t want to edit the file.


[​](#configure-skills)

Configure skills

Skills are configured through YAML frontmatter at the top of `SKILL.md` and the markdown content that follows.


[​](#types-of-skill-content)

Types of skill content

Skill files can contain any instructions, but thinking about how you want to invoke them helps guide what to include: **Reference content** adds knowledge Claude applies to your current work. Conventions, patterns, style guides, domain knowledge. This content runs inline so Claude can use it alongside your conversation context.

```python
---
name: api-conventions
description: API design patterns for this codebase
---

When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats
- Include request validation
```

**Task content** gives Claude step-by-step instructions for a specific action, like deployments, commits, or code generation. These are often actions you want to invoke directly with `/skill-name` rather than letting Claude decide when to run them. Add `disable-model-invocation: true` to prevent Claude from triggering it automatically. The example below adds `context: fork`, which runs the skill in its own subagent context; see [Run skills in a subagent](#run-skills-in-a-subagent).

```python
---
name: deploy
description: Deploy the application to production
context: fork
disable-model-invocation: true
---

Deploy the application:
1. Run the test suite
2. Build the application
3. Push to the deployment target
```

Keep the body itself concise. Once a skill loads, its content [stays in context across turns](#skill-content-lifecycle), so every line is a recurring token cost. State what to do rather than narrating how or why, and apply the same conciseness test you would for [CLAUDE.md content](../02-Claude-Code-CLI/best-practices.md#write-an-effective-claude-md).


[​](#frontmatter-reference)

Frontmatter reference

Configure a skill with YAML [frontmatter](../02-Claude-Code-CLI/glossary.md#frontmatter) between `---` markers at the top of `SKILL.md`, and write the skill’s instructions as Markdown after the closing `---`. Field names use lowercase words separated by hyphens, except `when_to_use`. A [command file](#where-skills-live) in `.claude/commands/` accepts the same fields except `name` and `paths`. This example sets four fields:

```python
---
name: my-skill
description: What this skill does
disable-model-invocation: true
allowed-tools: Read Grep
---

Your skill instructions here...
```

All fields are optional. Only `description` is recommended so Claude knows when to use the skill. A field name must match the table exactly, hyphens included: Claude Code ignores a field it doesn’t recognize without reporting an error. Claude Code reads the frontmatter only when the opening `---` is the file’s first line. Otherwise it treats the whole file, `---` markers included, as skill content. If the YAML between the markers doesn’t parse, the skill still loads with no fields set; see [Skill not triggering](#skill-not-triggering) to find and fix the error. Boolean fields accept `yes`, `no`, `on`, `off`, `1`, and `0` in any letter case, in addition to `true` and `false`. Before v2.1.218, Claude Code recognized only `true` and `false`.

| Field                      | Required    | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|:---------------------------|:------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name`                     | No          | Command name shown in the `/` menu. Defaults to the directory name. See [How a skill gets its command name](#how-a-skill-gets-its-command-name) for how the field interacts with the name you type to invoke the skill.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `description`              | Recommended | What the skill does and when to use it. Claude uses this to decide when to apply the skill. If omitted, uses the first non-empty line of the markdown content. Put the key use case first: the combined `description` and `when_to_use` text is truncated at 1,536 characters in the skill listing to reduce context usage.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `when_to_use`              | No          | Additional context for when Claude should invoke the skill, such as trigger phrases or example requests. Appended to `description` in the skill listing and counts toward the 1,536-character cap.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `argument-hint`            | No          | Hint shown during autocomplete to indicate expected arguments. Example: `[issue-number]` or `[filename] [format]`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `arguments`                | No          | Named positional arguments for [`$name` substitution](#available-string-substitutions) in the skill content. Accepts a space-separated string or a YAML list. Names map to argument positions in order.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `disable-model-invocation` | No          | Set to `true` to prevent Claude from automatically loading this skill. Use for workflows you want to trigger manually with `/name`. Also prevents the skill from being [preloaded into subagents](../09-Agents-Patterns/sub-agents.md#preload-skills-into-subagents) and from running when a [session-scoped scheduled task](../02-Claude-Code-CLI/scheduled-tasks.md) fires with the skill as its prompt. Default: `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `user-invocable`           | No          | Set to `false` when only Claude should invoke the skill: Claude Code hides it from the `/` menu and doesn’t run it when you type `/name`. Use for background knowledge users shouldn’t invoke directly. Default: `true`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `allowed-tools`            | No          | Tools Claude can use without asking permission during the turn that invokes this skill. The grant clears when you send your next message. Accepts a space- or comma-separated string, or a YAML list. See [Pre-approve tools for a skill](#pre-approve-tools-for-a-skill).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `disallowed-tools`         | No          | Tools removed from Claude’s available pool while this skill is active. Use for autonomous skills that should never call certain tools, such as `AskUserQuestion` for a background loop. Accepts a space- or comma-separated string, or a YAML list. The restriction clears when you send your next message. Like deny rules, the field can’t remove [`EndConversation`](../02-Claude-Code-CLI/tools-reference.md#endconversation-tool-behavior) while any other tool remains.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `model`                    | No          | Model to use when this skill is active. The override applies for the rest of the current turn and isn’t saved to settings. The session model resumes when you send your next prompt. Accepts the same values as [`/model`](../02-Claude-Code-CLI/model-config.md), or `inherit` to keep the active model. A value excluded by your organization’s [`availableModels`](../02-Claude-Code-CLI/model-config.md#restrict-model-selection) allowlist isn’t used, and the session keeps its current model. In [auto mode](../02-Claude-Code-CLI/permission-modes.md#eliminate-prompts-with-auto-mode), and in [plan mode while the classifier reviews commands](../02-Claude-Code-CLI/permission-modes.md#analyze-before-you-edit-with-plan-mode), a model that auto mode doesn’t support also isn’t used, and the session keeps its current model. With `context: fork`, the value sets the [forked subagent’s model](#run-skills-in-a-subagent) instead, and an excluded value follows the [same rules as a subagent model override](../02-Claude-Code-CLI/model-config.md#restrict-model-selection). |
| `effort`                   | No          | [Effort level](../02-Claude-Code-CLI/model-config.md#adjust-effort-level) when this skill is active. Overrides the session effort level. When you omit it, the level comes from the [effort resolution order](../02-Claude-Code-CLI/model-config.md#adjust-effort-level). Options: `low`, `medium`, `high`, `xhigh`, `max`; available levels depend on the model.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `context`                  | No          | Set to `fork` to run in a forked subagent context. See [Run skills in a subagent](#run-skills-in-a-subagent).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `agent`                    | No          | Which subagent type to use when `context: fork` is set.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `background`               | No          | Only applies with `context: fork`. Set to `false` to wait for the forked subagent’s result in the turn that invoked the skill, instead of [running it in the background](#run-skills-in-a-subagent). Default: `true`. Requires Claude Code v2.1.218 or later.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `hooks`                    | No          | Hooks that Claude Code registers when the skill is invoked and keeps running for the rest of the session. See [Hooks in skills and agents](../07-Hooks/hooks.md#hooks-in-skills-and-agents) for the configuration format, the `once` option, and the [limits on skill hooks](../07-Hooks/hooks.md#limits-on-skill-hooks).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `paths`                    | No          | Glob patterns that limit when this skill is activated. Accepts a comma-separated string or a YAML list. When set, Claude loads the skill automatically only when working with files matching the patterns. Patterns are relative to Claude’s current working directory, and files outside that directory don’t match. Uses the same glob format as [path-specific rules](../02-Claude-Code-CLI/memory.md#path-specific-rules).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `shell`                    | No          | Shell to use for `` !`command` `` and ```` ```! ```` blocks in this skill. Accepts `bash` (default) or `powershell`. Setting `powershell` runs inline shell commands via PowerShell when the [PowerShell tool](../02-Claude-Code-CLI/tools-reference.md#powershell-tool) is enabled: it’s on by default on Windows without Git Bash, on by default with Git Bash for claude.ai and Console accounts, and needs `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` in Amazon Bedrock, Google Cloud’s Agent Platform, and Microsoft Foundry sessions and on macOS, Linux, and WSL. Set it to `0` to turn the tool off.                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `metadata`                 | No          | Free-form YAML map for your own key-value data, such as entitlement or catalog fields, read by your own tooling from `SKILL.md`. Claude Code doesn’t act on its contents, and drops a value that isn’t a map. Don’t reuse frontmatter field names such as `paths` as keys.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `license`                  | No          | License covering the skill. Part of the [Agent Skills](https://agentskills.io) spec; see [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code). Claude Code accepts the field but doesn’t act on it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `compatibility`            | No          | Environment requirements for the skill, such as intended products or system prerequisites, as defined by the [Agent Skills](https://agentskills.io) spec; see [Using skill frontmatter outside Claude Code](#using-skill-frontmatter-outside-claude-code). Accepts a string of up to 500 characters. Claude Code accepts the field but doesn’t act on it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |


[​](#using-skill-frontmatter-outside-claude-code)

Using skill frontmatter outside Claude Code

Claude Code accepts every field in the table above. Outside Claude Code, you can use only the fields in the [Agent Skills](https://agentskills.io) spec:

| Distribution path                                                                                                                             | Frontmatter fields you can use                                                 |
|:----------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|
| Claude Code skills at [any level](#where-skills-live), including [plugin](plugins.md) skills                                   | Every field in the table above                                                 |
| claude.ai skill uploads, the Skills API, and packaging with `package_skill.py` from [anthropics/skills](https://github.com/anthropics/skills) | `name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools` |

When you enable a personal skill for your claude.ai account, for example to use it in [Cowork and cloud sessions](#skills-in-cowork-and-cloud-sessions) and scheduled tasks, you upload it to claude.ai. The same rules apply to that upload. If you include any field the spec doesn’t allow, packaging or upload fails with a hard error instead of ignoring the field:

```python
Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name
```

Restricting frontmatter to the spec’s six fields avoids the unexpected-key error above. The [Agent Skills spec](https://agentskills.io) and the [Skills API requirements](https://docs.claude.com/en/api/skills-guide) define everything else those paths validate. Claude Code-only body features, such as [dynamic context injection](#inject-dynamic-context), don’t function in claude.ai chat or through the API. Claude Code accepts all six fields, so frontmatter that follows the spec loads in Claude Code without changes.


[​](#how-a-skill-gets-its-command-name)

How a skill gets its command name

The command you type to invoke a skill comes from where the skill file lives and, for skill directories and plugin skills, from the frontmatter `name` field. In a personal or project skill directory, `name` sets the command that the `/` menu shows and that you type, unless another command already uses that name. The directory name also invokes the skill. In a plugin skill, `name` sets the last segment of the command and the plugin prefix stays in place. The table below shows where the command name comes from for each layout:

| Skill location                                                                                                                                                                                                                | Command name source                                                                                                                                                                           | Example                                                                                                                                                                                                                                        |
|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Skill directory under `~/.claude/skills/` or `.claude/skills/`                                                                                                                                                                | Frontmatter `name` or the directory name                                                                                                                                                      | `.claude/skills/deploy-staging/SKILL.md` → `/deploy-staging`, or `/deploy` with `name: deploy`                                                                                                                                                 |
| [Nested](#where-skills-live) `.claude/skills/` directory, when the directory name clashes with another skill                                                                                                                  | Subdirectory path relative to the working directory, then the skill directory name                                                                                                            | `apps/web/.claude/skills/deploy/SKILL.md` → `/apps/web:deploy`                                                                                                                                                                                 |
| `.claude/skills/` in an [added directory outside the project](#skills-from-additional-directories), when the skill directory name clashes with an enterprise, personal, or project skill, or one in a directory added earlier | The added directory’s folder name, then the skill directory name. When two added directories share a folder name, the prefix starts with the parent folder’s name. Requires v2.1.309 or later | `../cli/.claude/skills/verify/SKILL.md` with `--add-dir ../cli` → `/cli:verify`. With `--add-dir ../a/cli --add-dir ../b/cli`, a `verify` skill in each, and none elsewhere, the one in `../a/cli` → `/verify` and the other → `/b/cli:verify` |
| File under `.claude/commands/`                                                                                                                                                                                                | File name without extension                                                                                                                                                                   | `.claude/commands/deploy.md` → `/deploy`                                                                                                                                                                                                       |
| File in a subdirectory of `.claude/commands/`                                                                                                                                                                                 | Subdirectory path relative to `commands/` with each `/` replaced by `:`, then the file name without extension                                                                                 | `.claude/commands/frontend/component.md` → `/frontend:component`                                                                                                                                                                               |
| Plugin `skills/` subdirectory                                                                                                                                                                                                 | Frontmatter `name` or the directory name, namespaced by plugin                                                                                                                                | `my-plugin/skills/review/SKILL.md` → `/my-plugin:review`, or `/my-plugin:fancy` with `name: fancy`                                                                                                                                             |
| Plugin root `SKILL.md`                                                                                                                                                                                                        | Frontmatter `name`, with the plugin directory name as a fallback                                                                                                                              | `my-plugin/SKILL.md` with `name: review` → `/my-plugin:review`. See [a single skill at the plugin root](plugins-components.md#skills)                                                                                                    |
| Skill [synced from claude.ai](#how-synced-skills-behave)                                                                                                                                                                      | The skill’s name on your claude.ai account, prefixed with `anthropic-skills:`                                                                                                                 | Account skill `deploy` → `/anthropic-skills:deploy`, or `/deploy` while no other command uses that name                                                                                                                                        |

In a plugin skill, the frontmatter `name` replaces the directory name in the last segment of the command, so `my-plugin/skills/review/SKILL.md` with `name: fancy` becomes `/my-plugin:fancy`. The bare `/fancy` also invokes the skill unless another command already uses that name. If the `name` you write already starts with the plugin’s own prefix, Claude Code doesn’t add the prefix again on v2.1.246 or later. For example, `name: my-plugin:fancy` still becomes `/my-plugin:fancy`. From v2.1.216 through v2.1.245, Claude Code doubled the prefix when the `name` already carried it. In [non-interactive sessions](../02-Claude-Code-CLI/headless.md), the names `help` and `feedback` aren’t reserved for their terminal-only built-in commands, so a plugin skill with one of those names keeps its bare command there. Every other terminal-only built-in’s name, such as `/login`, stays reserved even though the command can’t run in those sessions. For a plugin-root `SKILL.md`, there is no skill directory to take the name from, so `name` supplies the whole final segment. Without a `name` field, Claude Code falls back to the plugin’s directory name.


[​](#characters-a-skill-name-cant-hold)

Characters a skill name can’t hold

If a skill or custom command is missing from the `/` menu, or the menu doesn’t show a skill’s frontmatter `name`, check the name. Claude Code refuses a command name or a frontmatter `name` that contains characters such as these:

- A zero-width space or a control character
- A private-use or unassigned character, such as a glyph from an icon font
- A character that changes the direction of the text around it
- A blank other than one plain space between words, such as a no-break space, two spaces in a row, or a space at the start or end
- `<` or `>`

A custom command or skill doesn’t load when Claude Code refuses its directory or file name. Rename that folder or file to remove the characters. A skill still loads when these characters are only in a plugin skill folder’s name or in its frontmatter `name`:

- **Folder in a plugin’s `skills/` directory or its manifest’s [`skills` paths](plugins-components.md#skills)**: you don’t need to rename the folder. These characters become hyphens in the skill’s command name, so type the command with the hyphens.
- **Frontmatter `name`**: the `/` menu doesn’t show the `name` value and typing it doesn’t invoke the skill. Edit the `name:` line to remove the characters.

To find out which name Claude Code refused, start it with [`claude --debug`](../02-Claude-Code-CLI/cli-reference.md#cli-flags) from your shell and search the [debug log](../02-Claude-Code-CLI/debug-your-config.md) for `[names]`. In an interactive session, a [startup notice](../02-Claude-Code-CLI/errors.md#skill-or-command-name-refused) also tells you that names were refused. After a rename or a `name:` edit, restart the session, unless the file is in a directory Claude Code [watches for changes](#live-change-detection).


[​](#available-string-substitutions)

Available string substitutions

Skills support string substitution for dynamic values in the skill content:

| Variable                | Description                                                                                                                                                                                                                                                                                                      |
|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `$ARGUMENTS`            | All arguments passed when invoking the skill. When no placeholder receives an argument, Claude Code appends them as `ARGUMENTS: <value>`. See [Pass arguments to skills](#pass-arguments-to-skills).                                                                                                             |
| `$ARGUMENTS[N]`         | Access a specific argument by 0-based index, such as `$ARGUMENTS[0]` for the first argument.                                                                                                                                                                                                                     |
| `$N`                    | Shorthand for `$ARGUMENTS[N]`, such as `$0` for the first argument or `$1` for the second.                                                                                                                                                                                                                       |
| `$name`                 | Named argument declared in the [`arguments`](#frontmatter-reference) frontmatter list. Names map to positions in order, so with `arguments: [issue, branch]` the placeholder `$issue` expands to the first argument and `$branch` to the second.                                                                 |
| `${CLAUDE_SESSION_ID}`  | The current session ID. Useful for logging, creating session-specific files, or correlating skill output with sessions.                                                                                                                                                                                          |
| `${CLAUDE_EFFORT}`      | The current effort level: `low`, `medium`, `high`, `xhigh`, or `max`. Use this to adapt skill instructions to the active effort setting.                                                                                                                                                                         |
| `${CLAUDE_SKILL_DIR}`   | The directory containing the skill’s `SKILL.md` file. For plugin skills, this is the skill’s subdirectory within the plugin, not the plugin root. Use this in bash injection commands to reference scripts or files bundled with the skill, regardless of the current working directory.                         |
| `${CLAUDE_PROJECT_DIR}` | The project root directory. This is the same path [hooks](../07-Hooks/hooks.md#reference-scripts-by-path) and MCP servers receive as `CLAUDE_PROJECT_DIR`. Use this to reference project-local scripts or files, such as `${CLAUDE_PROJECT_DIR}/.claude/hooks/helper.sh`, independent of where the skill is installed. |
| `${CLAUDE_PLUGIN_ROOT}` | The plugin’s installation directory. Substituted only in plugin skills. Use this to reference scripts or files bundled anywhere in the plugin, including resources shared between the plugin’s skills. See [plugin environment variables](plugins-reference.md#environment-variables).            |
| `${CLAUDE_PLUGIN_DATA}` | The plugin’s [persistent data directory](plugins-components.md#path-variables-and-persistent-data), which survives plugin updates. Substituted only in plugin skills. Use this to reference installed dependencies, generated files, or caches that must outlive an update.                                |

Claude Code substitutes `${CLAUDE_SKILL_DIR}` and `${CLAUDE_PROJECT_DIR}` in two places: the skill’s markdown content, and Bash rules in the [`allowed-tools`](#frontmatter-reference) frontmatter. In a plugin skill, Claude Code substitutes `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_PLUGIN_DATA}` in the same two places. Using the same variable in both places lets a skill run a bundled script without a permission prompt. The following skill shows the pattern:

```python
---
name: render-chart
description: Render a chart from a CSV file
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.sh *)
---

Run `${CLAUDE_SKILL_DIR}/scripts/render.sh <csv-file>` to render the chart.
```

If this skill is installed at `~/.claude/skills/render-chart/`, both occurrences of `${CLAUDE_SKILL_DIR}` expand to that directory. The `allowed-tools` rule then matches the exact command the skill body tells Claude to run, so the script runs without prompting. Indexed arguments use shell-style quoting, so wrap multi-word values in quotes to pass them as a single argument. For example, `/my-skill "hello world" second` makes `$0` expand to `hello world` and `$1` to `second`. The `$ARGUMENTS` placeholder always expands to the full argument string as typed. An indexed placeholder with no corresponding argument, such as `$2` when only one argument was passed, stays in the content unchanged. A named placeholder from the [`arguments`](#frontmatter-reference) frontmatter with no matching argument expands to an empty string. If you pass an argument value that itself contains text such as `$1` or `$ARGUMENTS`, Claude Code inserts it as literal text and doesn’t expand it. For example, if a skill’s body contains `Summarize $0` and you run `/summarize "$ARGUMENTS from yesterday"`, Claude receives `Summarize $ARGUMENTS from yesterday`. Claude Code still replaces `${CLAUDE_*}` variables such as `${CLAUDE_SKILL_DIR}` after it inserts the arguments. To include a literal `$` before a digit, `ARGUMENTS`, or a declared argument name, such as `$1.00` in prose, escape it with a backslash: `\$1.00`. A backslash before any other `$` is left unchanged. Only a single backslash directly before the token escapes it. A doubled backslash such as `\\$1` leaves both backslashes in place, and `$1` still expands to the argument value. The backslash escape covers only these argument placeholders. A backslash doesn’t prevent substitution of a `${CLAUDE_*}` variable where the variable applies. **Example using substitutions:**

```python
---
name: session-logger
description: Log activity for this session
---

Log the following to logs/${CLAUDE_SESSION_ID}.log:

$ARGUMENTS
```


[​](#add-supporting-files)

Add supporting files

Skills can include multiple files in their directory. This keeps `SKILL.md` focused on the essentials while letting Claude access detailed reference material only when needed. Large reference docs, API specifications, or example collections don’t need to load into context every time the skill runs.

```python
my-skill/
├── SKILL.md (required - overview and navigation)
├── reference.md (detailed API docs - loaded when needed)
├── examples.md (usage examples - loaded when needed)
└── scripts/
    └── helper.py (utility script - executed, not loaded)
```

Reference supporting files from `SKILL.md` so Claude knows what each file contains and when to load it:

```python
## Additional resources

- For complete API details, see [reference.md](reference.md)
- For usage examples, see [examples.md](examples.md)
```

Keep `SKILL.md` under 500 lines. Move detailed reference material to separate files.


[​](#control-who-invokes-a-skill)

Control who invokes a skill

By default, both you and Claude can invoke any skill. You can type `/skill-name` to invoke it directly, and Claude can load it automatically when relevant to your conversation. Two frontmatter fields let you restrict this:

- **`disable-model-invocation: true`**: Claude can’t invoke the skill on its own. Use this for workflows with side effects or that you want to control timing, like `/commit`, `/deploy`, or `/send-slack-message`. You don’t want Claude deciding to deploy because your code looks ready.
- **`user-invocable: false`**: Only Claude can invoke the skill. Use this for background knowledge that isn’t actionable as a command. A `legacy-system-context` skill explains how an old system works. Claude should know this when relevant, but `/legacy-system-context` isn’t a meaningful action for users to take.

This example creates a deploy skill. If you set `disable-model-invocation: true`, Claude can’t run the skill automatically:

```python
---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
---

Deploy $ARGUMENTS to production:

1. Run the test suite
2. Build the application
3. Push to the deployment target
4. Verify the deployment succeeded
```

If Claude tries anyway, Claude Code blocks the call and instructs it not to reproduce the deploy steps another way, so expect Claude to suggest running `/deploy` yourself. Here’s how the two fields affect invocation and context loading:

| Frontmatter                      | You can invoke | Claude can invoke | When loaded into context                                     |
|:---------------------------------|:---------------|:------------------|:-------------------------------------------------------------|
| (default)                        | Yes            | Yes               | Description always in context, full skill loads when invoked |
| `disable-model-invocation: true` | Yes            | Not on its own    | Description not in context, full skill loads when invoked    |
| `user-invocable: false`          | No             | Yes               | Description always in context, full skill loads when invoked |

In a regular session, skill descriptions are loaded into context so Claude knows what’s available, but full skill content only loads when invoked. [Subagents with preloaded skills](../09-Agents-Patterns/sub-agents.md#preload-skills-into-subagents) work differently: the full skill content is injected at startup.


[​](#where-you-write-the-skill’s-name)

Where you write the skill’s name

To run a skill directly, put its name at the start of your message. After plain text, the name gives Claude permission to run the skill but doesn’t run it:

| Where                                                             | Example                           | What happens                                                                                                                                                                 |
|:------------------------------------------------------------------|:----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| At the start of your message                                      | `/deploy staging`                 | Claude Code runs the skill directly                                                                                                                                          |
| After plain text, as a separate word with no punctuation attached | `go ahead and /deploy to staging` | Nothing runs directly. The name counts as your permission for that message: Claude can run the skill while it responds, and judges from your wording whether you asked it to |

To write about the skill without permitting a run, leave off the slash.


[​](#skill-content-lifecycle)

Skill content lifecycle

When you or Claude invoke a skill, the rendered `SKILL.md` content enters the conversation as a single message and stays there across later turns. This persistence applies to the skill’s instructions, not its permissions: an [`allowed-tools`](#pre-approve-tools-for-a-skill) grant clears when you send your next message. Claude Code does not re-read the skill file on later turns, so write guidance that should apply throughout a task as standing instructions rather than one-time steps. When Claude re-invokes a skill whose rendered content is identical to the copy already in context, Claude Code adds a short note that the skill is already loaded rather than a second copy of the content. When the rendered content differs, because the arguments changed or a [dynamic context](#inject-dynamic-context) command produced new output, Claude Code appends the full content again. [Auto-compaction](../02-Claude-Code-CLI/how-claude-code-works.md#when-context-fills-up) carries invoked skills forward within a token budget. When the conversation is summarized to free context, Claude Code re-attaches the most recent invocation of each skill after the summary, keeping the first 5,000 tokens of each. Re-attached skills share a combined budget of 25,000 tokens. Claude Code fills this budget starting from the most recently invoked skill, so older skills can be dropped entirely after compaction if you have invoked many in one session. If Claude stops following a skill partway through a session, see [Claude stops following a skill](#claude-stops-following-a-skill).


[​](#pre-approve-tools-for-a-skill)

Pre-approve tools for a skill

The `allowed-tools` field grants permission for the listed tools during the turn that invokes the skill, so Claude can use them without prompting you for approval. The grant clears when you send your next message, even though the skill content [stays in context](#skill-content-lifecycle); invoking the skill again re-applies it for that turn. It does not restrict which tools are available: every tool remains callable, and your [permission settings](../02-Claude-Code-CLI/permissions.md) still govern tools that are not listed. To pre-approve tools for the whole session rather than a single turn, add allow rules to those permission settings instead. Workspace trust doesn’t gate this field. Claude Code applies a project skill’s `allowed-tools` even in a `-p` run in a folder you’ve never trusted. A skill can grant itself broad tool access, so review the `allowed-tools` of skills checked into a repository before you run Claude Code there. To withhold the field from repository skills across your organization, see [When only managed permission rules apply](#when-only-managed-permission-rules-apply). This skill lets Claude run git commands without per-use approval whenever you invoke it:

```python
---
name: commit
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---
```

To remove tools from Claude’s available pool while a skill is active, list them in `disallowed-tools` in the skill’s frontmatter. The restriction clears when you send your next message. Like deny rules, the field can’t remove [`EndConversation`](../02-Claude-Code-CLI/tools-reference.md#endconversation-tool-behavior) while any other tool remains. To block tools across all skills and prompts, add deny rules in your [permission settings](../02-Claude-Code-CLI/permissions.md).


[​](#when-managed-settings-lock-hooks-to-plugins)

When managed settings lock hooks to plugins

When your organization’s managed settings list `hooks` in `strictPluginOnlyCustomization`, a personal or project skill whose frontmatter declares `hooks` loses its `allowed-tools` pre-approval, apart from the [skills that keep it](../02-Claude-Code-CLI/settings-reference.md#strictpluginonlycustomization-hooks).

- **Check**: type `/status` in a Claude Code session. If `/status` shows [`Enterprise managed settings`](../02-Claude-Code-CLI/settings.md#check-what-your-organization-enforces), ask your administrator whether `strictPluginOnlyCustomization` lists `hooks`.
- **Fix**: remove `hooks` from the skill’s frontmatter, because its hooks don’t run under that setting. To keep them, [move the skill into a plugin](../07-Hooks/hooks.md#limits-on-skill-hooks) that you install from a marketplace.


[​](#when-only-managed-permission-rules-apply)

When only managed permission rules apply

When your organization sets `allowManagedPermissionRulesOnly` in managed settings, Claude Code ignores `allowed-tools` in project and personal skills and in the [other sources the setting’s entry lists](../02-Claude-Code-CLI/settings-reference.md#allowmanagedpermissionrulesonly). This requires Claude Code v2.1.282 or later. The tools an affected skill lists go through your organization’s managed rules and the normal permission prompt instead. Run `/status` to list each skill whose `allowed-tools` Claude Code has ignored so far in the session. An injected command in the skill that no managed rule allows follows [Permission checks on injected commands](#permission-checks-on-injected-commands).


[​](#pass-arguments-to-skills)

Pass arguments to skills

Both you and Claude can pass arguments when invoking a skill. Arguments are available via the `$ARGUMENTS` placeholder. This skill fixes a GitHub issue by number. The `$ARGUMENTS` placeholder gets replaced with whatever follows the skill name:

```python
---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---

Fix GitHub issue $ARGUMENTS following our coding standards.

1. Read the issue description
2. Understand the requirements
3. Implement the fix
4. Write tests
5. Create a commit
```

When you run `/fix-issue 123`, Claude receives “Fix GitHub issue 123 following our coding standards…” If you invoke a skill with arguments but no placeholder in the skill’s content receives one, Claude Code appends `ARGUMENTS: <your input>` to the end of the skill content so Claude still sees what you typed. A placeholder is `$ARGUMENTS`, an indexed form such as `$1`, or a named argument. An indexed placeholder with no argument at its position stays as literal text and doesn’t count as receiving one. A named placeholder counts even when its position has no argument, because it expands to an empty string. To access individual arguments by position, use `$ARGUMENTS[N]` or the shorter `$N`:

```python
---
name: migrate-component
description: Migrate a component from one language to another
---

Migrate the $ARGUMENTS[0] component from $ARGUMENTS[1] to $ARGUMENTS[2].
Preserve all existing behavior and tests.
```

Running `/migrate-component SearchBar JavaScript TypeScript` replaces `$ARGUMENTS[0]` with `SearchBar`, `$ARGUMENTS[1]` with `JavaScript`, and `$ARGUMENTS[2]` with `TypeScript`. The same skill using the `$N` shorthand:

```python
---
name: migrate-component
description: Migrate a component from one language to another
---

Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.
```


[​](#stack-several-skills-in-one-message)

Stack several skills in one message

When you type at the Claude Code prompt in your terminal, you can stack several skills at the start of one message. Through the Agent SDK or `claude -p`, Claude Code loads only the first skill, with the rest of the line as its arguments. At the terminal prompt, typing `/write-tests /fix-issue 123` loads both skills and passes the trailing text `123` as `$ARGUMENTS` to each of them. Claude Code expands the first skill plus up to five more stacked after it. Expansion stops at the first token that isn’t an inline user-invocable skill. If you stack a skill that runs as a [forked subagent](#run-skills-in-a-subagent), such as [`/code-review`](../02-Claude-Code-CLI/code-review-local.md), or one whose arguments may start with another command, such as `/loop`, expansion ends there. That token and everything after it become the argument text for every expanded skill.


[​](#advanced-patterns)

Advanced patterns


[​](#inject-dynamic-context)

Inject dynamic context

The `` !`<command>` `` syntax runs shell commands before the skill content is sent to Claude. The command output replaces the placeholder, so Claude receives actual data, not the command itself. Claude Code doesn’t run these commands on your machine when the skill is [synced from your claude.ai account](#how-claude-code-handles-the-body-of-a-synced-skill). This restriction requires Claude Code v2.1.228 or later. This skill summarizes a pull request by fetching live PR data with the GitHub CLI. The `` !`gh pr diff` `` and other commands run first, and their output gets inserted into the prompt:

```python
---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

## Your task
Summarize this pull request...
```

Substitution runs once over the original file. Command output is inserted as plain text and is not re-scanned for further `` !`<command>` `` placeholders, so a command cannot emit a placeholder for a later pass to expand. The inline form is only recognized when `!` appears at the start of a line or immediately after whitespace. If `!` follows another character, as in `` KEY=!`cmd` ``, the placeholder is left as literal text and the command does not run. For multi-line commands, use a fenced code block opened with ```` ```! ```` instead of the inline form:

```` shiki
## Environment
```!
node --version
git status --short
```
````

To disable this behavior for skills and custom commands from user, project, plugin, or [additional-directory](#skills-from-additional-directories) sources, set `"disableSkillShellExecution": true` in [settings](../02-Claude-Code-CLI/settings.md). Each command is replaced with `[shell command execution disabled by policy]` instead of being run. Bundled and managed skills are not affected. This setting is most useful in [managed settings](../13-Enterprise-Admin/managed-settings.md), where users cannot override it. Claude Code never runs these commands on your machine when they appear in skills [synced from your claude.ai account](#how-synced-skills-behave), regardless of this setting. This restriction requires Claude Code v2.1.228 or later. [How Claude Code handles the body of a synced skill](#how-claude-code-handles-the-body-of-a-synced-skill) says what Claude receives in place of the command in each kind of session.

To request deeper reasoning when a skill runs, include `ultrathink` anywhere in the skill content. See [Use ultrathink for one-off deep reasoning](../02-Claude-Code-CLI/model-config.md#use-ultrathink-for-one-off-deep-reasoning).


[​](#how-injected-commands-run)

How injected commands run

Claude Code picks the tool that runs a skill’s injected commands from the `shell` key in the skill’s frontmatter and your environment. Every combination runs the commands through the Bash tool or the PowerShell tool, except one that fails the invocation outright:

- `shell: powershell`, with the [PowerShell tool](../02-Claude-Code-CLI/tools-reference.md#powershell-tool) enabled: the commands run through the PowerShell tool.
- `shell: bash` when bash isn’t available: the invocation fails before any command runs. This happens on Windows without Git Bash. Claude Code shows `` Skill <name> requires bash (`shell: bash` in frontmatter) but Git Bash was not found ``.
- Any other combination: the commands run through the Bash tool when bash is available. When it isn’t, they run through the PowerShell tool.

Either tool runs the commands the same way it runs Claude’s own shell commands. They share the working directory, timeout, and output handling:

- **Working directory**: Claude Code runs each command in the session shell’s current working directory. That directory moves when Claude runs `cd`. Use [`${CLAUDE_SKILL_DIR}` or `${CLAUDE_PROJECT_DIR}`](#available-string-substitutions) in paths that must resolve the same way every time.
- **stderr**: with the default `bash` shell, Claude Code merges stderr into stdout. Anything the command writes to stderr appears in the injected text.
- **Timeout**: each command runs under the Bash tool’s default 2-minute [timeout](../02-Claude-Code-CLI/tools-reference.md#timeout-and-output-limits). When the Bash tool [moves a timed-out command to the background](../02-Claude-Code-CLI/tools-reference.md#foreground-commands-that-move-to-the-background), the skill still renders. The injected text reports the move and names the background task and the file collecting the command’s output. When the command is one the Bash tool never auto-backgrounds, Claude Code kills it at the timeout. That failure [aborts the invocation](#when-an-injected-command-fails).
- **Output size**: output past the Bash tool’s inline ceiling arrives as a file path plus a short preview, not truncated text. [Output limits](../02-Claude-Code-CLI/tools-reference.md#output-limits) covers the ceiling and how to adjust each boundary.

The PowerShell tool applies the same timeout, backgrounding, and output-ceiling behavior to the commands it runs. See the [PowerShell tool](../02-Claude-Code-CLI/tools-reference.md#powershell-tool) section for its specifics.


[​](#when-an-injected-command-fails)

When an injected command fails

A failed command aborts the entire skill invocation, not just its own placeholder. Claude never sees the skill content for that invocation. The abort shows `Shell command failed for pattern "..."`. The error message includes the command’s output. With the default `bash` shell, any non-zero exit code counts as a failure. One carveout applies: Claude Code treats exit code 1 from [search and comparison commands](../02-Claude-Code-CLI/tools-reference.md#output-limits) as a normal result and injects their output. Exit codes of 2 or higher fail even for those commands. Which commands get the carveout depends on the shell:

- Default `bash` shell: the commands listed under [Output limits](../02-Claude-Code-CLI/tools-reference.md#output-limits)
- `shell: powershell`, when the PowerShell tool is enabled: a [different set](../02-Claude-Code-CLI/tools-reference.md#exit-code-1-from-searches-and-git-diff-in-powershell) that includes `grep` and `git diff` but not `find` or `diff`

With the default `bash` shell, append `|| true` to any other command you expect to exit non-zero. A check script that exits 1 when it finds problems is one example.


[​](#control-characters-in-injected-commands)

Control characters in injected commands

`No shell command in <name> was run` means one of the file’s `!` commands contains a raw control character other than tab or newline, so none of them runs. See [how to find and remove the character](../02-Claude-Code-CLI/errors.md#no-shell-command-was-run).


[​](#permission-checks-on-injected-commands)

Permission checks on injected commands

Injected commands never prompt for permission while the skill renders. Claude Code checks each one against your [permission rules](../02-Claude-Code-CLI/permissions.md) first. A command a deny rule matches aborts the invocation with `Shell command permission check failed for pattern "..."`. Outside [auto mode](../02-Claude-Code-CLI/permission-modes.md#eliminate-prompts-with-auto-mode), when a command’s permission check returns anything other than allow, Claude Code aborts the invocation with the same error. This includes a rule that would normally ask you. To keep an unmatched command from aborting here, pre-approve it with [`allowed-tools`](#pre-approve-tools-for-a-skill). If your organization restricts permission rules to managed settings, see [When only managed permission rules apply](#when-only-managed-permission-rules-apply). Deny and ask rules still override `allowed-tools`. See [Manage permissions](../02-Claude-Code-CLI/permissions.md#manage-permissions). In auto mode, a command that would otherwise need your approval doesn’t abort the invocation. The skill loads with an instruction telling Claude to run the command first, and Claude’s own call then goes through [auto mode’s usual checks](../02-Claude-Code-CLI/permission-modes.md#how-the-classifier-evaluates-actions). The invocation still aborts in a [forked skill](#run-skills-in-a-subagent) that sets `agent`, and in a session where Claude doesn’t have the [shell tool that runs injected commands](#how-injected-commands-run).


[​](#run-skills-in-a-subagent)

Run skills in a subagent

Add `context: fork` to your frontmatter when you want a skill to run in isolation. Claude Code starts a new subagent of the type set in the `agent` field and gives it the skill content as its prompt. The subagent doesn’t see your conversation history, so the skill’s instructions have to stand on their own.

Despite the name, a skill with `context: fork` doesn’t run in a [fork of the current conversation](../09-Agents-Patterns/sub-agents.md#fork-the-current-conversation), which would hand the subagent everything you’ve discussed so far. When the task depends on that history, fork the conversation instead of using `context: fork`.

The forked subagent runs in the [background](../09-Agents-Patterns/sub-agents.md#run-subagents-in-foreground-or-background): you keep working while it runs, and its result arrives in your conversation when it completes. Set `background: false` in the frontmatter to instead wait for the result in the turn that invoked the skill. Before v2.1.218, forked skills always blocked the turn until they finished. Claude Code also waits for the result, even when the skill doesn’t set `background: false`, in cases like these:

- In non-interactive mode, with the `-p` flag or the Agent SDK
- When you set [`CLAUDE_CODE_DISABLE_BACKGROUND_TASKS`](../02-Claude-Code-CLI/env-vars.md) to `1`, which also turns off all other background task features
- When you invoke a forked skill while an earlier invocation of the same skill is still running
- When a [session-scoped scheduled task](../02-Claude-Code-CLI/scheduled-tasks.md) fires with the skill as its prompt

When an agent in a [dynamic workflow](../02-Claude-Code-CLI/workflows.md) invokes a forked skill, that agent waits for and receives the result, even when the skill doesn’t set `background: false`. Before v2.1.295, when the skill ran in the background, that agent didn’t wait for it. The skill’s result then arrived in your main conversation instead of reaching the agent. A backgrounded fork also runs with the [narrower tool set that applies to background subagents](../09-Agents-Patterns/sub-agents.md#run-subagents-in-foreground-or-background): the skill’s subagent is a regular agent type, so the exemption for subagents that fork the conversation doesn’t cover it. If your skill’s steps depend on a tool outside that set, set `background: false` to keep the full tool set. A forked skill that runs in the background applies its edits outside your session’s [checkpoints](../02-Claude-Code-CLI/checkpointing.md#subagent-edits-not-restored). After one, `/rewind` restores only the files Claude had already edited in your conversation. The skill’s edits to other files stay, so use git to revert them.

`context: fork` only makes sense for skills with explicit instructions. If your skill contains guidelines like “use these API conventions” without a task, the subagent receives the guidelines but no actionable prompt, and returns without meaningful output.

Skills and [subagents](../09-Agents-Patterns/sub-agents.md) work together in two directions:

| Approach                     | System prompt            | Task                        | Also loads                                                                                                    |
|:-----------------------------|:-------------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------|
| Skill with `context: fork`   | From agent type          | SKILL.md content            | CLAUDE.md, per the agent’s [startup context](../09-Agents-Patterns/sub-agents.md#what-loads-at-startup)                       |
| Subagent with `skills` field | Subagent’s markdown body | Claude’s delegation message | Preloaded skills + CLAUDE.md, per the subagent’s [startup context](../09-Agents-Patterns/sub-agents.md#what-loads-at-startup) |

With `context: fork`, you write the task in your skill and pick an agent type to execute it. The built-in Explore and Plan agents [start without CLAUDE.md and git status](../09-Agents-Patterns/sub-agents.md#what-loads-at-startup) to keep their context small. Put everything the task needs in your SKILL.md. A forked skill using `agent: Explore` starts with that content and the agent’s own system prompt, and none of your CLAUDE.md files. For the inverse, where you define a custom subagent that uses skills as reference material, see [Subagents](../09-Agents-Patterns/sub-agents.md#preload-skills-into-subagents).


[​](#example-research-skill-using-explore-agent)

Example: Research skill using Explore agent

This skill runs research in a forked Explore agent. The skill content becomes the task, and the agent provides read-only tools optimized for codebase exploration:

```python
---
name: deep-research
description: Research a topic thoroughly
context: fork
agent: Explore
---

Research $ARGUMENTS thoroughly:

1. Find relevant files using Glob and Grep
2. Read and analyze the code
3. Summarize findings with specific file references
```

When this skill runs:

1.  A new isolated context is created
2.  The subagent receives the skill content as its prompt (the “Research \$ARGUMENTS thoroughly” instructions)
3.  The `agent` field determines the execution environment (model, tools, and permissions)
4.  The subagent summarizes its results and returns them to your main conversation when it finishes

The `agent` field specifies which subagent configuration to use. Options include built-in agents (`Explore`, `Plan`, `general-purpose`) or any custom subagent from `.claude/agents/`. If omitted, uses `general-purpose`.


[​](#restrict-claude’s-skill-access)

Restrict Claude’s skill access

By default, Claude can invoke any skill that doesn’t have `disable-model-invocation: true` set. Skills that define [`allowed-tools`](#pre-approve-tools-for-a-skill) grant Claude access to those tools without per-use approval during the turn that invokes the skill; the grant clears when you send your next message. Your [permission settings](../02-Claude-Code-CLI/permissions.md) still govern baseline approval behavior for all other tools. A few built-in commands are also available through the Skill tool, including `/init` and `/security-review`. Other built-in commands such as `/compact` are not. Three ways to control which skills Claude can invoke: **Disable all skills** by denying the Skill tool in `/permissions`:

```python
# Add to deny rules:
Skill
```

**Allow or deny specific skills** using [permission rules](../02-Claude-Code-CLI/permissions.md):

```python
# Allow only specific skills
Skill(commit)
Skill(review-pr *)

# Deny specific skills
Skill(deploy *)
```

Permission syntax: `Skill(name)` for exact match, `Skill(name *)` for prefix match with any arguments. The table shows what a `deny` rule blocks beyond the name you write, by the kind of name in the rule.

| Your `deny` rule names                                                         | Example rule                     | Claude Code also blocks                                                                   |
|:-------------------------------------------------------------------------------|:---------------------------------|:------------------------------------------------------------------------------------------|
| An alias                                                                       | `Skill(review)`                  | The bundled `/code-review`, through its `/review` alias                                   |
| An unqualified name                                                            | `Skill(deploy)`                  | A [nested skill](#where-skills-live) listed as `apps/web:deploy`                          |
| A [skill synced from claude.ai](#how-synced-skills-behave)                     | `Skill(anthropic-skills:deploy)` | That skill when Claude Desktop delivers it to a session as a plugin                       |
| The plugin form of a synced skill                                              | `Skill(deploy:deploy)`           | The synced skill                                                                          |
| A skill in the [parameter form](../02-Claude-Code-CLI/permissions.md#match-by-input-parameter) | `Skill(skill:deploy)`            | The skill whichever of its names Claude calls it by, including its alias and display name |

Before v2.1.260, Claude Code didn’t block a nested skill listed under its qualified name when the deny rule named only the unqualified name. Claude Code matches an `allow` rule only against the skill’s own name and the name in Claude’s invocation. To approve a [synced skill](#how-synced-skills-behave) without a prompt, name it inside its [reserved namespace](#names-reserved-for-synced-skills):

- `Skill(anthropic-skills:pdf)` approves the synced `pdf` skill
- `Skill(anthropic-skills *)` approves every synced skill
- `Skill(anthropic *)` doesn’t cover `anthropic-skills:pdf`, because a prefix outside the namespace doesn’t match the names inside it

**Hide individual skills** by adding `disable-model-invocation: true` to their frontmatter. This removes the skill from Claude’s context entirely.

With `user-invocable: false`, you can’t invoke the skill, but Claude still can. To keep Claude from invoking it through the Skill tool, set `disable-model-invocation: true`.


[​](#override-skill-visibility-from-settings)

Override skill visibility from settings

The `skillOverrides` setting controls skill visibility from your [settings](../02-Claude-Code-CLI/settings.md) instead of the skill’s own frontmatter. Use it for skills whose SKILL.md you don’t want to edit, such as ones checked into a shared project repo. The `/skills` menu writes it for you: highlight a skill and press `Space` to cycle states, then `Esc` to save to `.claude/settings.local.json`. Each key is a skill name and each value is one of four states:

| Value                   | Listed to Claude     | In `/` menu |
|:------------------------|:---------------------|:------------|
| `"on"`                  | Name and description | Yes         |
| `"name-only"`           | Name only            | Yes         |
| `"user-invocable-only"` | Hidden               | Yes         |
| `"off"`                 | Hidden               | Hidden      |

The `/skills` menu labels the `"user-invocable-only"` state `user-only`. `"off"` also hides the skill from the command lists advertised to [Remote Control](../02-Claude-Code-CLI/remote-control.md) clients and to [Agent SDK](../05-Agent-SDK/agent-sdk-skills.md#discover-available-commands) callers, in addition to the terminal `/` menu. Invoking a hidden skill by its full name returns the `skillOverrides` error instead of running it. A skill that is absent from `skillOverrides` is treated as `"on"`. The example below collapses one skill to its name and turns another off entirely:

```python
{
  "skillOverrides": {
    "legacy-context": "name-only",
    "deploy": "off"
  }
}
```

Some bundled skills have aliases, such as `checkup` for `/doctor`. If you set a `skillOverrides` entry under an alias in [managed settings](../13-Enterprise-Admin/managed-settings.md) or in a file you pass with the `--settings` flag, Claude Code applies it to the skill behind the alias. You can only restrict a skill further through an alias, never make it more visible, and if you also set an entry under the skill’s own name in managed settings, that entry takes precedence. Before v2.1.260, Claude Code didn’t apply an entry under an alias to the skill in any settings source. In user, project, and local settings, Claude Code matches entries against skill names only. If you set an entry for `review` there, it applies to a skill named `review`, not to the bundled `/code-review` through its `/review` alias. Plugin skills are not affected by `skillOverrides`. Manage those through `/plugin` instead.


[​](#find-unused-skills)

Find unused skills

Every skill in the [skill listing](#skill-descriptions-are-cut-short) adds to your context on every turn, whether or not Claude ever uses it. Run `/skill-doctor` to see what each of your skills costs and how often it gets used, so you can decide which ones to turn off. In an interactive session, the report opens in the `/plugin` manager’s **Stats** tab. In [non-interactive mode](../02-Claude-Code-CLI/headless.md) with `-p`, Claude Code prints it as text. The report covers the skills in your session other than bundled skills and enterprise skills. It flags skills in the listing that have never been invoked and says where to turn them off. Of the skills it tells you where to turn off, start with the ones that have the highest context cost. The report also lists plugins you haven’t used recently. `/skill-doctor` requires Claude Code v2.1.252 or later and isn’t available in sessions that skip [feature-flag fetching](../02-Claude-Code-CLI/env-vars.md#features-that-need-feature-flag-fetching). If you run `/skill-doctor` over [Remote Control](../02-Claude-Code-CLI/remote-control.md) from your phone or browser, Claude Code replies [`Skill usage reports are not available on this connection.`](../02-Claude-Code-CLI/errors.md#skill-usage-reports-are-not-available-on-this-connection) instead. Run `/skill-doctor` in the terminal on the machine where the session is running.


[​](#evaluate-and-iterate-on-a-skill)

Evaluate and iterate on a skill

Seeing a skill trigger tells you Claude found it, not that it did what you intended. To know a skill is working, measure separately whether Claude invokes it on the prompts it should, and whether the output matches what you expect when it does. The check for both is a baseline comparison. Collect a few realistic prompts, run each one in a fresh session with the skill available and again with it turned off, and compare the results. A fresh session matters because leftover context from authoring the skill will mask gaps in the written instructions. How you turn the skill off for the second run depends on where it comes from:

- **Personal or project skill**: set it to `"off"` in [`skillOverrides`](#override-skill-visibility-from-settings).
- **Skill that a plugin provides**: `skillOverrides` doesn’t apply to plugin skills. Use [`claude plugin eval`](plugin-evals.md#the-no-plugin-baseline) instead, which repeats each run with no plugin loaded.

Two tools automate the baseline comparison. For a skill that ships in a [plugin](plugins.md), [`claude plugin eval`](plugin-evals.md) runs each prompt in an isolated session with and without the plugin, scores it with graders you define or that it writes for you, and exits non-zero below a threshold so you can gate CI on it. For iterating on a single skill inside a Claude Code conversation, the skill-creator plugin below runs a similar loop with its own `evals/evals.json` format. The two formats aren’t interchangeable.


[​](#run-evals-with-skill-creator)

Run evals with skill-creator

The [`skill-creator` plugin](https://github.com/anthropics/claude-plugins-official/tree/main/plugins/skill-creator) automates the comparison loop inside Claude Code. In the VS Code extension or the desktop app, install it from the official marketplace by following [Install a plugin](discover-plugins.md#install-a-plugin). In a terminal, start Claude Code by running `claude`, then enter this at its prompt:

```python
/plugin install skill-creator@claude-plugins-official
```

If the install fails, match the message Claude Code reports:

- `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
- `Plugin "<name>" not found in marketplace "claude-plugins-official"`: your local copy of the marketplace catalog may be out of date. Refresh it with `/plugin marketplace update claude-plugins-official`, then retry the install. If the retry prints the same message, see [Plugin not found in marketplace](plugins-troubleshooting.md#plugin-not-found-in-marketplace).

If the install summary reports `Run /reload-plugins to apply.`, Claude Code then runs that reload for you. If the reload warns that your next message would re-read the conversation, run `/reload-plugins --force` to make the plugin’s skills available in the current session. Then ask Claude to evaluate an existing skill, for example `evaluate my summarize-changes skill with skill-creator`. The plugin walks you through writing test cases and runs the loop:

- **Test cases**: stores prompts, input files, and expected behavior in `evals/evals.json` inside the skill directory
- **Isolated runs**: spawns a [subagent](../09-Agents-Patterns/sub-agents.md) per test case so each run starts with a clean context, and records token count and duration
- **Grading**: checks each assertion against the output and writes pass or fail with evidence to `grading.json`
- **Benchmark**: aggregates pass rate, time, and tokens for with-skill versus without-skill into `benchmark.json` so you can compare the pass-rate improvement against the token and time overhead
- **Version comparison**: runs a blind A/B between two versions of the skill so you can confirm an edit is an improvement before committing it
- **Description tuning**: generates should-trigger and should-not-trigger prompts, measures the hit rate, and proposes description edits when the skill activates on the wrong requests
- **Review viewer**: opens an HTML report where you inspect each output and record qualitative feedback that the next iteration reads

For the eval file format and the full iteration workflow, see [Evaluating skill output quality](skill-creation-evaluating-skills.md) on agentskills.io. For background on the benchmark and comparison modes, see the [skill-creator announcement](https://claude.com/blog/improving-skill-creator-test-measure-and-refine-agent-skills).


[​](#share-skills)

Share skills

Skills can be distributed at different scopes depending on your audience:

- **Project skills**: Commit `.claude/skills/` to version control
- **Plugins**: Create a `skills/` directory in your [plugin](plugins.md)
- **Managed**: Deploy organization-wide through [managed settings](../13-Enterprise-Admin/managed-settings.md)


[​](#generate-visual-output)

Generate visual output

Skills can bundle and run scripts in any language, giving Claude capabilities beyond what’s possible in a single prompt. One pattern is generating visual output: interactive HTML files that open in your browser for exploring data, debugging, or creating reports. This example creates a codebase explorer: an interactive tree view where you can expand and collapse directories, see file sizes at a glance, and identify file types by color. Create the Skill directory:

```python
mkdir -p ~/.claude/skills/codebase-visualizer/scripts
```

Save this to `~/.claude/skills/codebase-visualizer/SKILL.md`. The description tells Claude when to activate this Skill, and the instructions tell Claude to run the bundled script. The script path uses [`${CLAUDE_SKILL_DIR}`](#available-string-substitutions) so it resolves correctly whether the skill is installed at the personal, project, or plugin level:

```` shiki
---
name: codebase-visualizer
description: Generate an interactive collapsible tree visualization of your codebase. Use when exploring a new repo, understanding project structure, or identifying large files.
allowed-tools: Bash(python3 *)
---

# Codebase Visualizer

Generate an interactive HTML tree view that shows your project's file structure with collapsible directories.

## Usage

Run the visualization script from your project root:

```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
```

This creates `codebase-map.html` in the current directory and opens it in your default browser.

## What the visualization shows

- **Collapsible directories**: Click folders to expand/collapse
- **File sizes**: Displayed next to each file
- **Colors**: Different colors for different file types
- **Directory totals**: Shows aggregate size of each folder
````

Save this to `~/.claude/skills/codebase-visualizer/scripts/visualize.py`. This script scans a directory tree and generates a self-contained HTML file with:

- A **summary sidebar** showing file count, directory count, total size, and number of file types
- A **bar chart** breaking down the codebase by file type (top 8 by size)
- A **collapsible tree** where you can expand and collapse directories, with color-coded file type indicators

The script requires Python 3 but uses only built-in libraries, so there are no packages to install:

```python
#!/usr/bin/env python3
"""Generate an interactive collapsible tree visualization of a codebase."""

import json
import sys
import webbrowser
from html import escape
from pathlib import Path
from collections import Counter

IGNORE = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'dist', 'build'}

def scan(path: Path, stats: dict) -> dict:
    result = {"name": path.name, "children": [], "size": 0}
    try:
        for item in sorted(path.iterdir()):
            if item.name in IGNORE or item.name.startswith('.'):
                continue
            if item.is_file():
                size = item.stat().st_size
                ext = item.suffix.lower() or '(no ext)'
                result["children"].append({"name": item.name, "size": size, "ext": ext})
                result["size"] += size
                stats["files"] += 1
                stats["extensions"][ext] += 1
                stats["ext_sizes"][ext] += size
            elif item.is_dir():
                stats["dirs"] += 1
                child = scan(item, stats)
                if child["children"]:
                    result["children"].append(child)
                    result["size"] += child["size"]
    except PermissionError:
        pass
    return result

def generate_html(data: dict, stats: dict, output: Path) -> None:
    ext_sizes = stats["ext_sizes"]
    total_size = sum(ext_sizes.values()) or 1
    sorted_exts = sorted(ext_sizes.items(), key=lambda x: -x[1])[:8]
    colors = {
        '.js': '#f7df1e', '.ts': '#3178c6', '.py': '#3776ab', '.go': '#00add8',
        '.rs': '#dea584', '.rb': '#cc342d', '.css': '#264de4', '.html': '#e34c26',
        '.json': '#6b7280', '.md': '#083fa1', '.yaml': '#cb171e', '.yml': '#cb171e',
        '.mdx': '#083fa1', '.tsx': '#3178c6', '.jsx': '#61dafb', '.sh': '#4eaa25',
    }
    lang_bars = "".join(
        f'<div class="bar-row"><span class="bar-label">{ext}</span>'
        f'<div class="bar" style="width:{(size/total_size)*100}%;background:{colors.get(ext,"#6b7280")}"></div>'
        f'<span class="bar-pct">{(size/total_size)*100:.1f}%</span></div>'
        for ext, size in sorted_exts
    )
    def fmt(b):
        if b < 1024: return f"{b} B"
        if b < 1048576: return f"{b/1024:.1f} KB"
        return f"{b/1048576:.1f} MB"

    html = f'''<!DOCTYPE html>
<html><head>
  <meta charset="utf-8"><title>Codebase Explorer</title>
  <style>
    body {{ font: 14px/1.5 system-ui, sans-serif; margin: 0; background: #1a1a2e; color: #eee; }}
    .container {{ display: flex; height: 100vh; }}
    .sidebar {{ width: 280px; background: #252542; padding: 20px; border-right: 1px solid #3d3d5c; overflow-y: auto; flex-shrink: 0; }}
    .main {{ flex: 1; padding: 20px; overflow-y: auto; }}
    h1 {{ margin: 0 0 10px 0; font-size: 18px; }}
    h2 {{ margin: 20px 0 10px 0; font-size: 14px; color: #888; text-transform: uppercase; }}
    .stat {{ display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #3d3d5c; }}
    .stat-value {{ font-weight: bold; }}
    .bar-row {{ display: flex; align-items: center; margin: 6px 0; }}
    .bar-label {{ width: 55px; font-size: 12px; color: #aaa; }}
    .bar {{ height: 18px; border-radius: 3px; }}
    .bar-pct {{ margin-left: 8px; font-size: 12px; color: #666; }}
    .tree {{ list-style: none; padding-left: 20px; }}
    details {{ cursor: pointer; }}
    summary {{ padding: 4px 8px; border-radius: 4px; }}
    summary:hover {{ background: #2d2d44; }}
    .folder {{ color: #ffd700; }}
    .file {{ display: flex; align-items: center; padding: 4px 8px; border-radius: 4px; }}
    .file:hover {{ background: #2d2d44; }}
    .size {{ color: #888; margin-left: auto; font-size: 12px; }}
    .dot {{ width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; }}
  </style>
</head><body>
  <div class="container">
    <div class="sidebar">
      <h1>📊 Summary</h1>
      <div class="stat"><span>Files</span><span class="stat-value">{stats["files"]:,}</span></div>
      <div class="stat"><span>Directories</span><span class="stat-value">{stats["dirs"]:,}</span></div>
      <div class="stat"><span>Total size</span><span class="stat-value">{fmt(data["size"])}</span></div>
      <div class="stat"><span>File types</span><span class="stat-value">{len(stats["extensions"])}</span></div>
      <h2>By file type</h2>
      {lang_bars}
    </div>
    <div class="main">
      <h1>📁 {escape(data["name"])}</h1>
      <ul class="tree" id="root"></ul>
    </div>
  </div>
  <script>
    const data = {json.dumps(data)};
    const colors = {json.dumps(colors)};
    function fmt(b) {{ if (b < 1024) return b + ' B'; if (b < 1048576) return (b/1024).toFixed(1) + ' KB'; return (b/1048576).toFixed(1) + ' MB'; }}
    function esc(s) {{ return s.replace(/[&<>"']/g, c => ({{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}}[c])); }}
    function render(node, parent) {{
      if (node.children) {{
        const det = document.createElement('details');
        det.open = parent === document.getElementById('root');
        det.innerHTML = `<summary><span class="folder">📁 ${{esc(node.name)}}</span><span class="size">${{fmt(node.size)}}</span></summary>`;
        const ul = document.createElement('ul'); ul.className = 'tree';
        node.children.sort((a,b) => (b.children?1:0)-(a.children?1:0) || a.name.localeCompare(b.name));
        node.children.forEach(c => render(c, ul));
        det.appendChild(ul);
        const li = document.createElement('li'); li.appendChild(det); parent.appendChild(li);
      }} else {{
        const li = document.createElement('li'); li.className = 'file';
        li.innerHTML = `<span class="dot" style="background:${{colors[node.ext]||'#6b7280'}}"></span>${{esc(node.name)}}<span class="size">${{fmt(node.size)}}</span>`;
        parent.appendChild(li);
      }}
    }}
    data.children.forEach(c => render(c, document.getElementById('root')));
  </script>
</body></html>'''
    output.write_text(html)

if __name__ == '__main__':
    target = Path(sys.argv[1] if len(sys.argv) > 1 else '.').resolve()
    stats = {"files": 0, "dirs": 0, "extensions": Counter(), "ext_sizes": Counter()}
    data = scan(target, stats)
    out = Path('codebase-map.html')
    generate_html(data, stats, out)
    print(f'Generated {out.absolute()}')
    webbrowser.open(f'file://{out.absolute()}')
```

See all 133 lines

To test, open Claude Code in any project and ask “Visualize this codebase.” Claude runs the script, which prints the generated file’s path, such as `Generated /path/to/codebase-map.html`, and opens it in your browser. If you work in a headless environment where no browser opens, the printed path confirms the script succeeded. This pattern works for any visual output: dependency graphs, test coverage reports, API documentation, or database schema visualizations. The bundled script does the work while Claude handles orchestration.


[​](#troubleshooting)

Troubleshooting


[​](#skill-not-triggering)

Skill not triggering

If Claude doesn’t use your skill when expected:

1.  Check the description includes keywords users would naturally say
2.  Verify the skill appears in `What skills are available?`
3.  Try rephrasing your request to match the description more closely
4.  Invoke it directly with `/skill-name` if the skill is user-invocable
5.  If `/skill-name` isn’t recognized either, check whether Claude Code [refused the skill’s name](#characters-a-skill-name-cant-hold)
6.  If the skill sets `paths`, check that the files Claude reads, edits, or writes are inside Claude’s current working directory. The skill doesn’t activate for files outside it, such as those in a sibling git worktree, even one you added with `/add-dir`. To have the skill activate for those files, start Claude Code in that worktree or [move the session there with `/cd`](../02-Claude-Code-CLI/permissions.md#move-the-session-to-another-directory).

If the frontmatter YAML is malformed, Claude Code loads the skill body with empty metadata, so `/skill-name` still works but Claude can’t match against your `description`. Run with `--debug` to see the parse error. If the skill ships in a plugin, you can measure how often it triggers across realistic prompts rather than checking one at a time: write an eval case with a [`tool_used: Skill` grader](plugin-evals.md#create-your-first-eval-suite) and run it with `claude plugin eval` after each description change. To find `SKILL.md` files whose frontmatter doesn’t parse, run [`claude plugin validate`](plugins-cli-reference.md#validate-a-directory) on the skills directory, for example `claude plugin validate .claude/skills` for project skills or `claude plugin validate ~/.claude/skills` for personal skills. Requires Claude Code v2.1.233 or later.


[​](#skill-triggers-too-often)

Skill triggers too often

If Claude uses your skill when you don’t want it:

1.  Make the description more specific
2.  Add `disable-model-invocation: true` if you only want manual invocation


[​](#claude-stops-following-a-skill)

Claude stops following a skill

If Claude follows a skill in its first response and stops following it later, start with whichever of these cases matches:

- **Claude skipped a rule that must hold every time**: move the rule into a [hook](../07-Hooks/hooks-guide.md). Claude Code runs a hook every time its event occurs, such as before each file edit, whether or not Claude is following the skill. To keep the rule with the skill, define the hook in the skill’s [`hooks` frontmatter](../07-Hooks/hooks.md#hooks-in-skills-and-agents). That hook applies from the time the skill is invoked until the session ends.
- **Claude skipped guidance it should apply with judgment**: word the guidance so it applies to the whole task, for example “Run the tests after every edit” rather than “Run the tests”. Claude Code adds the skill’s content to the conversation when the skill is invoked and [doesn’t re-read the file](#skill-content-lifecycle) on later turns.
- **The conversation was compacted**: invoke the skill again to restore its full content. After [compaction](../02-Claude-Code-CLI/how-claude-code-works.md#when-context-fills-up), Claude Code [can keep only the start of an invoked skill](#skill-content-lifecycle), so put the most important instructions near the top of `SKILL.md`.


[​](#skill-descriptions-are-cut-short)

Skill descriptions are cut short

Claude Code loads a listing of skill names and descriptions into context so Claude knows what’s available. The listing always contains every skill name, but if you have many skills, Claude Code drops some descriptions to fit the listing’s character budget, which removes the keywords Claude needs to match your request. The budget scales at 1% of the model’s context window. When the listing overflows, Claude Code drops descriptions starting with the skills you invoke least, so the skills you use most keep their full text. Run `/doctor` for an estimate of the listing’s context cost and its biggest contributors. To find skills worth turning off, run [`/skill-doctor`](#find-unused-skills). When the listing exceeds its budget, Claude Code also writes a warning to the debug log, visible with [`--debug`](../02-Claude-Code-CLI/cli-reference.md#cli-flags). The Skills row in `/context` reports the size of the listing after the budget is applied, so it matches what the model receives. Before v2.1.196, the row counted the full text of every description and could show a value several times larger than the configured budget. To raise the budget, set the [`skillListingBudgetFraction`](../02-Claude-Code-CLI/settings-reference.md#skilllistingbudgetfraction) setting (for example, `0.02` = 2%) or the `SLASH_COMMAND_TOOL_CHAR_BUDGET` environment variable to a fixed character count. To free budget for other skills, set low-priority entries to `"name-only"` in [`skillOverrides`](#override-skill-visibility-from-settings) so they list without a description. You can also trim the `description` and `when_to_use` text at the source: put the key use case first, since each entry’s combined text is capped at 1,536 characters regardless of budget. The cap is configurable with [`skillListingMaxDescChars`](../02-Claude-Code-CLI/settings-reference.md#skilllistingmaxdescchars).


[​](#personal-skills-disappeared)

Personal skills disappeared

If skill folders you created in `~/.claude/skills/` are gone, look in `~/.claude/skills/.trash/`. When Claude Code [syncs skills from claude.ai](#how-synced-skills-behave), it downloads them into the separate `synced` subfolder and doesn’t move or delete the folders you create. Before v2.1.280, a file named `manifest.json` in `~/.claude/skills/` caused Claude Code to move the skill folders that file listed into a timestamped folder under `~/.claude/skills/.trash/`, and those skills stopped loading. To restore a skill, move its folder from the timestamped folder back into `~/.claude/skills/`. Do this before the [retention sweep](../02-Claude-Code-CLI/claude-directory.md#cleaned-up-automatically) deletes trash entries, by default 30 days after they were moved to the trash.


[​](#related-resources)

Related resources

- **[Debug your configuration](../02-Claude-Code-CLI/debug-your-config.md)**: diagnose why a skill isn’t appearing or triggering
- **[Evaluating skill output quality](skill-creation-evaluating-skills.md)**: the eval file format and iteration workflow on agentskills.io
- **[Skill authoring best practices](../04-API-Reference/Agents-Tools/agents-and-tools-agent-skills-best-practices.md)**: writing guidance that applies across Claude products
- **[Subagents](../09-Agents-Patterns/sub-agents.md)**: delegate tasks to specialized agents
- **[Plugins](plugins.md)**: package and distribute skills with other extensions
- **[Hooks](../07-Hooks/hooks.md)**: automate workflows around tool events
- **[Memory](../02-Claude-Code-CLI/memory.md)**: manage CLAUDE.md files for persistent context
- **[Commands](../02-Claude-Code-CLI/commands.md)**: reference for built-in commands and bundled skills
- **[Permissions](../02-Claude-Code-CLI/permissions.md)**: control tool and skill access
- **[Claude Tag skills](https://claude.com/docs/claude-tag/admins/skills-repo)**: project skills committed to a repo also load when that repo is used in a Claude Tag channel

---

## Specification - Agent Skills

Source: https://agentskills.io/specification
Path: 08-Plugins-Skills/specification.md

## On this page

- [Directory structure](#directory-structure)
- [SKILL.md format](#skill-md-format)
  - [Frontmatter](#frontmatter)
  - [name field](#name-field)
  - [description field](#description-field)
  - [license field](#license-field)
  - [compatibility field](#compatibility-field)
  - [metadata field](#metadata-field)
  - [allowed-tools field](#allowed-tools-field)
  - [Body content](#body-content)
- [Optional directories](#optional-directories)
  - [scripts/](#scripts%2F)
  - [references/](#references%2F)
  - [assets/](#assets%2F)
- [Progressive disclosure](#progressive-disclosure)
- [File references](#file-references)
- [Validation](#validation)

# Specification

Copy pageCopy page

The complete format specification for Agent Skills.

Copy pageCopy page


[​](#directory-structure)

Directory structure

A skill is a directory containing, at minimum, a `SKILL.md` file:

```python
skill-name/
├── SKILL.md          # Required: metadata + instructions
├── scripts/          # Optional: executable code
├── references/       # Optional: documentation
├── assets/           # Optional: templates, resources
└── ...               # Any additional files or directories
```


[​](#skill-md-format)

`SKILL.md` format

The `SKILL.md` file must contain YAML frontmatter followed by Markdown content.


[​](#frontmatter)

Frontmatter

| Field           | Required | Constraints                                                                                                       |
|-----------------|----------|-------------------------------------------------------------------------------------------------------------------|
| `name`          | Yes      | Max 64 characters. Lowercase letters, numbers, and hyphens only. Must not start or end with a hyphen.             |
| `description`   | Yes      | Max 1024 characters. Non-empty. Describes what the skill does and when to use it.                                 |
| `license`       | No       | License name or reference to a bundled license file.                                                              |
| `compatibility` | No       | Max 500 characters. Indicates environment requirements (intended product, system packages, network access, etc.). |
| `metadata`      | No       | Arbitrary key-value mapping for additional metadata (a map from string keys to string values).                    |
| `allowed-tools` | No       | Space-separated string of pre-approved tools the skill may use. (Experimental)                                    |

**Minimal example:**

SKILL.md

```python
---
name: skill-name
description: A description of what this skill does and when to use it.
---
```

**Example with optional fields:**

SKILL.md

```python
---
name: pdf-processing
description: Extract PDF text, fill forms, merge files. Use when handling PDFs.
license: Apache-2.0
metadata:
  author: example-org
  version: "1.0"
---
```


[​](#name-field)

`name` field

The required `name` field:

- Must be 1-64 characters
- May only contain unicode lowercase alphanumeric characters (`a-z`, `0-9`) and hyphens (`-`)
- Must not start or end with a hyphen (`-`)
- Must not contain consecutive hyphens (`--`)
- Must match the parent directory name

**Valid examples:**

```python
name: pdf-processing
```

```python
name: data-analysis
```

```python
name: code-review
```

**Invalid examples:**

```python
name: PDF-Processing  # uppercase not allowed
```

```python
name: -pdf  # cannot start with hyphen
```

```python
name: pdf--processing  # consecutive hyphens not allowed
```


[​](#description-field)

`description` field

The required `description` field:

- Must be 1-1024 characters
- Should describe both what the skill does and when to use it
- Should include specific keywords that help agents identify relevant tasks

**Good example:**

```python
description: Extracts text and tables from PDF files, fills PDF forms, and merges multiple PDFs. Use when working with PDF documents or when the user mentions PDFs, forms, or document extraction.
```

**Poor example:**

```python
description: Helps with PDFs.
```


[​](#license-field)

`license` field

The optional `license` field:

- Specifies the license applied to the skill
- We recommend keeping it short (either the name of a license or the name of a bundled license file)

**Example:**

```python
license: Proprietary. LICENSE.txt has complete terms
```


[​](#compatibility-field)

`compatibility` field

The optional `compatibility` field:

- Must be 1-500 characters if provided
- Should only be included if your skill has specific environment requirements
- Can indicate intended product, required system packages, network access needs, etc.

**Examples:**

```python
compatibility: Designed for Claude Code (or similar products)
```

```python
compatibility: Requires git, docker, jq, and access to the internet
```

```python
compatibility: Requires Python 3.14+ and uv
```

Most skills do not need the `compatibility` field.


[​](#metadata-field)

`metadata` field

The optional `metadata` field:

- A map from string keys to string values
- Clients can use this to store additional properties not defined by the Agent Skills spec
- We recommend making your key names reasonably unique to avoid accidental conflicts

**Example:**

```python
metadata:
  author: example-org
  version: "1.0"
```


[​](#allowed-tools-field)

`allowed-tools` field

The optional `allowed-tools` field:

- A space-separated string of tools that are pre-approved to run
- Experimental. Support for this field may vary between agent implementations

**Example:**

```python
allowed-tools: Bash(git:*) Bash(jq:*) Read
```


[​](#body-content)

Body content

The Markdown body after the frontmatter contains the skill instructions. There are no format restrictions. Write whatever helps agents perform the task effectively. Recommended sections:

- Step-by-step instructions
- Examples of inputs and outputs
- Common edge cases

Note that the agent will load this entire file once it’s decided to activate a skill. Consider splitting longer `SKILL.md` content into referenced files.


[​](#optional-directories)

Optional directories

A skill directory may contain any files and directories beyond the required `SKILL.md`. The conventions below are recommendations for organizing common types of content.


[​](#scripts/)

`scripts/`

Contains executable code that agents can run. Scripts should:

- Be self-contained or clearly document dependencies
- Include helpful error messages
- Handle edge cases gracefully

Supported languages depend on the agent implementation. Common options include Python, Bash, and JavaScript.


[​](#references/)

`references/`

Contains additional documentation that agents can read when needed:

- `REFERENCE.md` - Detailed technical reference
- `FORMS.md` - Form templates or structured data formats
- Domain-specific files (`finance.md`, `legal.md`, etc.)

Keep individual [reference files](#file-references) focused. Agents load these on demand, so smaller files mean less use of context.


[​](#assets/)

`assets/`

Contains static resources:

- Templates (document templates, configuration templates)
- Images (diagrams, examples)
- Data files (lookup tables, schemas)


[​](#progressive-disclosure)

Progressive disclosure

Agents load skills *progressively*, pulling in more detail only as a task calls for it. Skills should be structured to take advantage of this:

1.  **Metadata** (~100 tokens): The `name` and `description` fields are loaded at startup for all skills
2.  **Instructions** (\< 5000 tokens recommended): The full `SKILL.md` body is loaded when the skill is activated
3.  **Resources** (as needed): Files (e.g. those in `scripts/`, `references/`, or `assets/`) are loaded only when required

Keep your main `SKILL.md` under 500 lines. Move detailed reference material to separate files.


[​](#file-references)

File references

When referencing other files in your skill, use relative paths from the skill root:

SKILL.md

```python
See [the reference guide](references/REFERENCE.md) for details.

Run the extraction script:
scripts/extract.py
```

Keep file references one level deep from `SKILL.md`. Avoid deeply nested reference chains.


[​](#validation)

Validation

Use the [skills-ref](https://github.com/agentskills/agentskills/tree/main/skills-ref) reference library to validate your skills:

```python
skills-ref validate ./my-skill
```

This checks that your `SKILL.md` frontmatter is valid and follows all naming conventions.