AGENT SKILL

plugin-conventions

ACTIVATE when creating a Claude Code plugin, writing plugin.json, marketplace.json, hooks.json, a hook script, or an evals/evals.json file. ACTIVATE for 'Claude plugin', 'plugin.json', 'marketplace.json', 'hooks.json', 'CLAUDE_PLUGIN_ROOT', 'permissionDecision', 'evals.json'. Covers: plugin directory structure, plugin.json/marketplace.json schemas, hooks.json format, the hook output contract (exit codes, which channel reaches Claude), CLAUDE_PLUGIN_ROOT portability, distribution best practices, the evals/evals.json format for a skill's routing and behaviour test cases, validation commands. DO NOT use for: SKILL.md writing conventions (see skill-authoring), agent .md format (see agent-authoring).

适用职业律师与法务
License 许可
MIT
最近更新
2026-10-13
Stars
★ 9
Forks
2

01 前言 Front Matter

name
plugin-conventions
metadata
{ "version": "1.0" }
description
ACTIVATE when creating a Claude Code plugin, writing plugin.json, marketplace.json, hooks.json, a hook script, or an evals/evals.json file. ACTIVATE for 'Claude plugin', 'plugin.json', 'marketplace.json', 'hooks.json', 'CLAUDE_PLUGIN_ROOT', 'permissionDecision', 'evals.json'. Covers: plugin directory structure, plugin.json/marketplace.json schemas, hooks.json format, the hook output contract (exit codes, which channel reaches Claude), CLAUDE_PLUGIN_ROOT portability, distribution best practices, the evals/evals.json format for a skill's routing and behaviour test cases, validation commands. DO NOT use for: SKILL.md writing conventions (see skill-authoring), agent .md format (see agent-authoring).

02 资源文件 2

03 SKILL.md 正文

Claude Code Plugin & Marketplace Conventions

Best practices for creating, structuring, and distributing Claude Code plugins and marketplaces, based on official documentation and the anthropics/claude-plugins-official repository.

Plugin Directory Structure

plugin-name/
├── .claude-plugin/
│   └── plugin.json          # Manifest (ONLY this file here)
├── commands/                 # Slash commands (.md)
├── agents/                   # Subagent definitions (.md)
├── skills/                   # Skills (subdirectories with SKILL.md)
│   └── skill-name/
│       ├── SKILL.md
│       ├── scripts/
│       ├── references/
│       ├── examples/
│       └── evals/
│           └── evals.json    # Routing + behaviour test cases (see below)
├── hooks/
│   ├── hooks.json            # Hook configuration
│   └── scripts/              # Hook scripts
├── .mcp.json                 # MCP server definitions
├── .lsp.json                 # LSP configurations
├── settings.json             # Default settings
├── scripts/                  # Shared utilities
├── LICENSE
└── README.md

Critical rules:

  • .claude-plugin/ contains ONLY plugin.json — never nest components inside it
  • All component directories at plugin root level
  • Kebab-case for all directory and file names
  • Only create directories for components actually used

plugin.json Schema

Minimal (auto-discovery handles the rest)

{
  "name": "plugin-name"
}

Recommended

{
  "name": "plugin-name",
  "version": "1.0.0",
  "description": "Brief plugin description",
  "author": {
    "name": "Author Name",
    "email": "author@example.com",
    "url": "https://github.com/author"
  },
  "repository": "https://github.com/author/plugin",
  "license": "MIT",
  "keywords": ["keyword1", "keyword2"]
}

Component Paths (optional, supplements auto-discovery)

{
  "skills": "./skills/",
  "agents": "./agents/agent-name.md",
  "hooks": "./hooks/hooks.json",
  "commands": "./commands/command-name.md",
  "mcpServers": "./.mcp.json",
  "lspServers": "./.lsp.json",
  "outputStyles": "./styles/"
}

Path rules:

  • All paths relative to plugin root
  • Must start with ./
  • Custom paths supplement default directories, never replace them

marketplace.json Schema

{
  "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
  "name": "marketplace-name",
  "description": "Brief marketplace description",
  "owner": {
    "name": "Owner Name",
    "email": "owner@example.com"
  },
  "metadata": {
    "version": "1.0.0",
    "description": "Detailed marketplace description"
  },
  "plugins": [
    {
      "name": "plugin-name",
      "source": "./plugins/plugin-name",
      "description": "Plugin description",
      "version": "1.0.0",
      "author": { "name": "Author" },
      "category": "development",
      "license": "MIT",
      "keywords": ["keyword"],
      "tags": ["community-managed"]
    }
  ]
}

Required fields

  • name — kebab-case, no spaces
  • owner — object with name (required), email (optional)
  • plugins — array of plugin entries, each with name and source

Reserved marketplace names

claude-code-marketplace, claude-code-plugins, claude-plugins-official, anthropic-marketplace, anthropic-plugins, agent-skills, life-sciences. Names impersonating official Anthropic marketplaces are blocked.

Plugin source types

Type Format
Local path "./plugins/name"
GitHub { "source": "github", "repo": "org/repo", "ref": "main", "sha": "abc" }
Git URL { "source": "url", "url": "https://github.com/org/repo.git" }
Git subdir { "source": "git-subdir", "url": "org/repo", "path": "plugins/name" }
npm { "source": "npm", "package": "name", "version": "^1.0" }

Plugin categories

development, productivity, security, testing, database, deployment, monitoring, design, learning

hooks.json Format

The official format uses nested matcher + hooks[] with type:

{
  "description": "Human-readable description of hooks purpose",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate.sh\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Hook types

Type Field Description
command command Shell command, receives JSON on stdin
http url HTTP POST to endpoint
prompt prompt Single-turn LLM evaluation
agent prompt Agentic verifier with Read/Grep/Glob tools

Hook events

Full reference: https://docs.claude.com/en/docs/claude-code/hooks

Event Matcher Can block?
SessionStart startup, resume, clear, compact, fork No
SessionEnd clear, resume, logout, prompt_input_exit, other No
UserPromptSubmit None Yes (exit 2)
PreToolUse Tool name Yes (exit 2)
PostToolUse Tool name No
Stop None Yes (exit 2)
SubagentStop Agent type Yes (exit 2)
Notification Notification type No
PreCompact manual, auto Yes (exit 2)

An empty, * or absent matcher fires on every occurrence. A matcher written with letters, digits, _, - and spaces alone, its names separated by | or ,, compares exact names: Edit|Write fires on those two tools and never on NotebookEdit. Any other character makes it a JavaScript regular expression that may match anywhere in the value, so Edit.* also fires on NotebookEdit: anchor it as ^Edit$. A matcher on an event marked None is ignored silently.

Hook output contract

Output on the wrong channel fails silently: the action proceeds and the message reaches nobody. Five rules decide it:

  • On most events, exit 2 is the only code that blocks by itself (WorktreeCreate and WorktreeRemove fail on any non-zero exit). Without a schema-valid JSON object on stdout, any other non-zero exit is a non-blocking error and the action proceeds, so exit 1 enforces nothing. Such an object is read on every exit code: at exit 1 a JSON deny still decides, and no JSON overrides the block of exit 2.
  • At exit 0, stderr goes to the debug log only: neither Claude nor the user sees it.
  • Plain stdout reaches Claude's context only on SessionStart, UserPromptSubmit, UserPromptExpansion and PostModelSwitch; on other events, speak through exit 2 or JSON.
  • A PreToolUse decision is hookSpecificOutput.permissionDecision (allow, deny, ask or defer) with permissionDecisionReason. The top-level decision/reason pair is deprecated for that event.
  • JSON stdout is exactly one object built with an encoder (jq -n --arg …, Python's json.dumps): an unparsable one is dropped as a hook error, and a field at the wrong level is ignored.

The stdin fields per event, the effect of exit 2 per event and the JSON fields are tabled in references/hook-output-contract.md. How to test a hook (a sourced harness, payload files, one assertion per channel) lives in shell-test:shell-test-conventions.

Portability — ${CLAUDE_PLUGIN_ROOT}

Always use ${CLAUDE_PLUGIN_ROOT} for intra-plugin path references. Never hardcode paths.

In hooks.json: "command": "\"${CLAUDE_PLUGIN_ROOT}/scripts/tool.sh\"". Quote the whole path: a shell-form command runs through sh -c, and an unquoted root containing a space splits into several words, exits 127 and silently disables the hook. claude plugin validate only warns about an unquoted placeholder; --strict turns the warning into exit 1. In MCP config: "args": ["${CLAUDE_PLUGIN_ROOT}/servers/server.js"] In shell scripts: source "${CLAUDE_PLUGIN_ROOT}/lib/common.sh"

evals/evals.json Format

A skill's evals/evals.json pins the test cases that prove it triggers on the right prompts and produces the right behaviour once loaded. This marketplace's convention (see plugins/career/evals/evals.json for a worked example):

{
  "$comment": "One object per case; `skills` names what should load, `expected_behavior` lists what a passing run must do.",
  "routing": [
    {
      "query": "A prompt a real user would type",
      "skills": ["plugin-name:skill-name"],
      "expected_behavior": [
        "loads plugin-name:skill-name, not a sibling skill",
        "the specific guardrail that makes this skill different from its neighbours"
      ]
    }
  ]
}
  • routing cases test discovery from the description alone: does the right skill load, and not a sibling that could plausibly match the same query?
  • skills is an array of plugin:skill-name refs, same shape as an agent's skills: field.
  • expected_behavior lists assertions in plain language, not a machine-checked schema — a human or an agent replays the case and checks each line.
  • Keep at least one case per skill that has caused a real false-positive or false-negative activation; that is the evidence a routing rule was worth writing.

Distribution Best Practices

  1. Version bumps required — Claude Code caches by version; no bump = no update for users
  2. Version in one place — avoid setting version in both plugin.json and marketplace.json (plugin.json wins silently)
  3. Pin with sha for reproducible builds in production marketplaces
  4. Test locally with claude --plugin-dir ./my-plugin
  5. Reload without restart with /reload-plugins
  6. Debug loading with claude --debug or /debug

Validation

# Validate marketplace (--strict turns every warning into exit 1: the CI form)
claude plugin validate --strict .

# Validate individual plugin
claude plugin validate --strict plugins/my-plugin

# Validate from within Claude Code
/plugin validate .

Naming Conventions

Component Convention Example
Plugin kebab-case code-review-assistant
Skills kebab-case directories api-testing/
Commands kebab-case .md run-tests.md
Agents kebab-case .md code-reviewer.md
Hooks kebab-case with ext validate-input.sh
Config Standard names hooks.json, .mcp.json

Plugin skills are namespaced as /plugin-name:skill-name to prevent conflicts.

See also: skill-authoring for the SKILL.md format itself, agent-authoring for the agent .md format referenced by agents/.

04 同仓库更多 Skills

FabienSalles
FabienSalles/claude-marketplace

vitest-test-conventions

ACTIVATE when writing Vitest tests, creating test files, using vi.fn()/vi.mock()/vi.spyOn(), or test factories. ACTIVATE for 'Vitest', 'it.each', 'NestJS testing module', 'supertest', 'vi.fn', 'vi.mock', 'spyOn'. Provides Vitest/NestJS-specific testing patterns; cross-language testing principles (DAMP, AAA, spy over mock, what NOT to test) live in craft:testing-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), TDD workflow/iterations (see vitest:vitest-tdd-workflow), PHP/PHPUnit tests (see phpunit:php-test-conventions).

测试与质量
★ 9
FabienSalles
FabienSalles/claude-marketplace

vitest-tdd-workflow

ACTIVATE when building new features, endpoints, or user stories using TDD with Vitest in a TypeScript/NestJS context. ACTIVATE for '/feature-tdd-dev', 'TDD', 'red-green-refactor' in TS context. Provides Vitest/NestJS-specific TDD examples and commands; cross-language TDD workflow (cross-layer iterations, RED-GREEN-REFACTOR, working app, bug-fix-first-test, mocks-hiding-bugs) lives in craft:tdd-workflow-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), test writing conventions (see vitest:vitest-test-conventions), PHP TDD (see phpunit:php-tdd-workflow).

Skill与智能体开发
★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-refactoring

ACTIVATE when refactoring TypeScript code. ACTIVATE for 'refactor', 'extract', 'redesign', 'simplify', or 'clean up' in a TypeScript / Node / NestJS context. This skill provides TS-specific examples for the cross-language refactoring rules defined in craft:refactoring-principles. DO NOT use for: writing new features from scratch, general OOP patterns (see ts-oop).

★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-ports-adapters

ACTIVATE when a TypeScript codebase with no dependency-injection container has to declare a port and wire its adapter — a repository, a clock, an id generator, an event dispatcher the domain depends on. ACTIVATE for 'port', 'adapter', 'SPI', 'dependency inversion' or 'composition root' in a codebase that wires its dependencies by hand. Covers: the port as a type of functions in the domain layer, the adapter as an object literal annotated with it, the handler that takes its dependencies first and its data second, the composition root in the infrastructure layer, and the in-memory stub that substitutes for the adapter in a unit test. DO NOT use for: NestJS, or any container-based DI with decorators and tokens (see nest-ddd-conventions), aggregate operations and smart constructors (see ddd-ts-fp), the Result type itself (see ts-functional).

测试与质量
★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-oop

ACTIVATE when designing TypeScript classes, and the value objects or collections modelled as classes, or when the user asks about object design, encapsulation, Tell Don't Ask, or Symbol.iterator. Provides TS-specific examples for the cross-language OOP rules defined in craft:oop-principles, plus TS-specific patterns (Symbol.iterator, branded types for primitive identifiers). DO NOT use for: a value object or an aggregate modelled as an immutable record rather than a class (see ddd-ts-fp), refactoring methodology (see ts-refactoring).

★ 9
FabienSalles
FabienSalles/claude-marketplace

ts-layer-boundaries

ACTIVATE when wiring a Worker entry point, a Router, a controller, or a domain event consumer in a TypeScript CQRS codebase, or when deciding where a port belongs. ACTIVATE for 'composition root', 'layer boundary', 'ESLint zones', 'domain must not import infrastructure'. Covers: the *Worker.ts composition root, the curried consumer, Router wiring of command endpoints, the five-gesture controller, the domain-to-infrastructure import boundary enforced by ESLint, infrastructure-only ports, in-memory doubles for every port, the read-can-reach-write asymmetry, and read endpoints going through a domain handler. DO NOT use for: the port's own shape and adapter binding (see ts-ports-adapters), the Result type (see ts-functional), domain event structure (see ts-ddd-events).

★ 9