add-plugin-component
Load this skill when adding or changing anything under plugins/ in this repo (a skill, agent, hook, script, or new ceh-* plugin): templates, frontmatter rules, and the registration chores CI checks. T
SKILL.md.claude/skills/add-plugin-component/SKILL.md
GitHubAdding a Component to This Repo
Every new SKILL.md or agents/*.md starts as a copy of its template, then gets registered in
the same commit. The content is the easy half. The half that gets forgotten is registration: the
same fact lives in the plugin README and docs/CATALOG.md, both manifests, and sometimes docs/CROSS_REFERENCES.md, and CI
fails when they drift.
1. Pick the plugin and the component type
Plugins split on use case, not tech domain or lifecycle phase. Load exactly one plugin per
use case, so each must be self-contained. The tiers are defined in CLAUDE.md.
Check the component against docs/VISION.md first, whether it is new or a change: it passes the
scope test, and it answers the Ask question of every principle it touches. A component the
vision does not allow is changed until it does, or not built. Record the reason in docs/IDEAS.md
when it is rejected.
- A skill triggers on a moment (a verb: "I'm opening a PR", "I'm writing a migration"), never a topic (a noun: "PostgreSQL"). Topic-named skills either never auto-trigger or restate what the model already knows. If you cannot name the moment, the skill is not ready.
- Framework variants do not get their own plugin when their skills trigger on disjoint file
types —
sveltekitandreact-viteshareceh-web-frontend. - A foundational standard needed by two use-case plugins is duplicated into both, never extracted into a shared base plugin. Register the duplication (step 4).
- App-specific patterns are not standards. Anything bound to one application's schema or design gets removed, not filed as a niche plugin.
If no existing plugin owns the use case, create one first — see New plugin — then continue at step 2.
Then pick the component type by what the need is:
| Need | Component | Settle before writing |
|---|---|---|
Knowledge or a procedure Claude applies at a moment, or a user-invoked /action |
Skill | Trigger phrases, what it adds beyond model knowledge, arguments, tools, interactive or automated |
| An autonomous task whose output or cost should stay out of the main session | Agent | Proactive or on request, tool set, model, output format |
| A rule that must hold on every event, whether or not Claude remembers it | Hook | Which events, prompt or command, what blocks vs warns |
| An external service or API | MCP server | Server type, authentication, which tools |
| User or project configuration | Environment variable, never a settings file | Variable names, required vs optional, defaults |
New slash commands are skills (skills/<name>/SKILL.md), never the legacy commands/ layout.
Answer the "settle" column from the request and the code. Ask the user only for what neither
settles.
2. Write the component
Editing an existing skill instead? Check docs/CROSS_REFERENCES.md first (step 4).
Creating any component, including one migrated from agent-skills, starts by loading its authoring skill. Where that skill's advice conflicts with this repo, this skill wins — see the override table.
| Creating | First |
|---|---|
| Skill | Invoke the Skill tool with skill="skill-creator:skill-creator". Never plugin-dev:skill-development. Skip its eval loop (test runs, benchmark, viewer, description optimization) unless the user asks for it |
| Agent | Invoke the Skill tool with skill="plugin-dev:agent-development" |
| Hook | Invoke the Skill tool with skill="plugin-dev:hook-development" |
| MCP server | Invoke the Skill tool with skill="plugin-dev:mcp-integration" |
Skill — copy ${CLAUDE_SKILL_DIR}/assets/SKILL.template.md to
plugins/standalone/ceh-<plugin>/skills/<name>/SKILL.md, name matching the directory. Fill every
placeholder, then delete the TEMPLATE-GUIDANCE comment. Content stays inline. references/ is
for two cases only: a schema or template shared by several skills, or a standard too large to
inline. A file shared by skills of the same plugin goes once in plugins/standalone/ceh-<plugin>/references/,
cited as ${CLAUDE_PLUGIN_ROOT}/references/<file>. A file used by one skill goes in that skill's
own references/, cited as ${CLAUDE_SKILL_DIR}/references/<file>.
Agent — copy ${CLAUDE_SKILL_DIR}/assets/agent.template.md to
plugins/standalone/ceh-<plugin>/agents/<name>.md, name matching the file name. Auto-delegation is driven
entirely by description (include "use proactively" to encourage it).
Migrating from agent-skills — still start from the template: move the old frontmatter and sections into the template's order and headings rather than copying the old file whole. Old files mix heading case and section names, and the templates exist to end that.
Hook or script — hook wiring goes in plugins/standalone/ceh-<plugin>/hooks/hooks.json, scripts in
plugins/standalone/ceh-<plugin>/scripts/, referenced as ${CLAUDE_PLUGIN_ROOT}/scripts/<file>. Keep
*.sh LF-only: a CRLF checkout breaks bash with $'\r'.
MCP server — config goes in plugins/standalone/ceh-<plugin>/.mcp.json with ${CLAUDE_PLUGIN_ROOT}
paths.
Configuration — environment variables only, never a .claude/*.local.md settings file or
other config file. Hooks, MCP servers, and scripts use HTTPS and never hardcode a credential: read
it from an environment variable and document every variable in the plugin README.
description is always a folded block scalar (>-) — never quoted, never plain. validate.py
rejects anything else.
description: >-
Load this skill when doing X: the colon is literal here, as are "quotes" and 'apostrophes'.
Wrap at ~98 chars with a uniform 2-space indent and no blank lines.
It is the only style with no escaping burden. A plain scalar cannot contain : , single-quoted
needs '' doubling, double-quoted needs \ and " escaping — all three have silently produced
invalid YAML in this repo. Keep the indent uniform (a more-indented line becomes a literal newline
instead of folding) and avoid blank lines. Any other key containing : gets single quotes.
Put the key use case first: Claude Code truncates long descriptions in the skill listing.
Claude Code silently ignores a frontmatter field it does not recognize, so a typo reads as
working config. Use only fields from the official
skills and
subagents references.
validate.py rejects any other key. Frontmatter worth reaching for before writing prose that does
the same job:
| Field | Use it for | Watch out |
|---|---|---|
paths |
Skills whose trigger really is a file type | Narrows auto-loading. A skill with real non-file triggers ("a uv command is run") loses them |
effort |
Reasoning-heavy skills | Default inherits the session level. Set it only when the skill needs a different one |
disable-model-invocation |
Workflows with side effects the user should trigger by hand | The skill can then never be preloaded into an agent or called with Invoke the Skill tool |
disallowed-tools |
Skills that must not write | Check the body first; most "review" skills here do apply fixes |
context: fork |
Heavy, self-contained task skills | The subagent does not see the conversation and runs in the background with the reduced tool set below. Set background: false if a step needs a tool outside it |
argument-hint |
Skills the user invokes as /name <arg> |
Cosmetic but free |
${CLAUDE_SKILL_DIR} |
Referencing a file bundled with the skill | Substituted in the body and in allowed-tools Bash rules |
${CLAUDE_PLUGIN_ROOT} |
Referencing a plugin-level script or references/ file shared by several skills |
Substituted in plugin skill bodies and allowed-tools, not in project skills |
memory |
Agents that should learn across sessions | Auto-enables Read/Write/Edit on that agent |
compatibility |
Skills that need software the machine may lack (git, gh, uv, bun, a server, network) |
>- scalar, max 500 chars. Name runtime + minimum version and what fails without it. Omit for read-files-emit-Markdown skills |
Cross-plugin calls — when a skill must call a skill in another plugin on every run, add the
target plugin to dependencies in plugin.json (never in marketplace.json; bare strings, no
ranges) and call it explicitly: Invoke the Skill tool with skill="ceh-<plugin>:<skill>".
Conditional handoffs and negative routing ("Not for X, use ...") stay prose with no dependency. A
cross-cutting plugin may depend only on other cross-cutting plugins. validate.py rejects a call
whose target does not resolve, is not a declared dependency, or sets
disable-model-invocation: true.
Plugin-agent gotchas — Claude Code ignores permissionMode, hooks, mcpServers, and
initialPrompt on plugin agents (security restriction). Do not add them; they read as working
config and are not. Grant edit permissions via session permissions.allow in settings.json
instead. Subagents run in the background by default, and background subagents keep only a
reduced built-in tool set — if an agent needs a tool outside Read/Grep/Glob/LSP/Bash/PowerShell/ Edit/Write/NotebookEdit/WebFetch/WebSearch/TodoWrite/Skill/ToolSearch/EnterWorktree/ExitWorktree/ Monitor/TaskStop/SendMessage/Artifact, it will be stripped silently. AskUserQuestion is stripped
from every subagent, foreground or background — an agent can never stop to ask.
skills: entries must be fully qualified as plugin:skill; an entry that does not resolve is
skipped with only a debug-log warning. The preload is also the only route a hook-loaded standard
has into an agent: SessionStart hooks never fire for subagents.
No agent sets isolation: worktree in its frontmatter. Subagent worktrees branch from the
repository's default branch, not the parent session's HEAD, unless worktree.baseRef: "head"
is set in settings.json — and their changes stay in the worktree rather than landing in your
checkout. In frontmatter it would apply to every run, and under this repo's feature-branch rule
that hands the agent a copy of main without your work. Isolation is the dispatcher's call per
run instead: ceh-coding-conduct:agent-coding-contract passes isolation: "worktree" on the
Agent call when parallel subagents edit files, checks each worktree's base, and merges the
branches back.
3. Update the catalog and the plugin README
docs/CATALOG.md— add a row under the correct plugin group in Skills or Agents. If the plugin has no group there yet, add a### <Plugin> (\ceh-<plugin>`)` subsection.plugins/standalone/ceh-<plugin>/README.md— add a row to that plugin's own table. The plugin README also carries anything a user must do before the component works: prerequisites, when a hook fires, and every environment variable the plugin reads, with its default and whether it is required. A new variable also gets a row indocs/ENVIRONMENT_VARIABLES.md, the index across plugins.
4. Register any duplication
Before editing an existing skill, check docs/CROSS_REFERENCES.md. If the section appears there,
propagate the edit to every listed file in the same session — canonical file first, then the
copies. If you introduce new duplication, add an entry naming the canonical source, every copy,
what is shared, and what deliberately diverges. Skills of one plugin share a file once in the
plugin's references/, so that needs no entry. Duplication plus an entry is for content shared
across plugins.
5. Bump the version in both manifests
Same commit, both files, or CI fails:
plugins/standalone/ceh-<plugin>/.claude-plugin/plugin.json.claude-plugin/marketplace.json
PATCH for content/description updates, MINOR for a new skill or agent or for adding or
removing a dependencies entry, MAJOR for renaming or removing the plugin. Bump at commit
time, not during iterative edits. The PR also adds its CHANGELOG.md entry under the date it is
opened and updates docs/PLUGIN_VERSIONS.md — see Versioning in CLAUDE.md.
6. Validate
python tools/validate-plugins/validate.py
Same gate CI runs via .github/workflows/validate.yml. It checks:
- manifests:
plugin.json↔marketplace.jsonsync, semver,namematching the directory - frontmatter:
nameformat and match,descriptionpresent,>-, ≤ 300 chars;compatibility≤ 500 chars; only documented keys; no plugin-agent keys Claude Code ignores;disable-model-invocation,user-invocable, andlicensestated on every skill docs/PLUGIN_VERSIONS.mdmatches everyplugin.json, and so does each plugin's newestCHANGELOG.mdPlugin versions row, under the datePLUGIN_VERSIONS.mdgives it; cross-cutting plugins depend only on cross-cutting plugins; a skill a hook names isuser-invocable: false- no
TEMPLATE-GUIDANCEcomment left from a template references/...,${CLAUDE_PLUGIN_ROOT}/{scripts,references}/..., and${CLAUDE_SKILL_DIR}/...mentions resolve to real files, andceh-<plugin>:<component>mentions resolvedependenciesresolve and the graph is acyclic- every
Invoke the Skill tool with skill="..."call is resolvable, declared, and invocable - bundled
*.sh/*.pyscripts parse
validate.py does not parse YAML strictly. For a YAML syntax check, also run
claude plugin validate plugins/standalone/ceh-<plugin>.
A green validator proves the files are well-formed, not that the component works. Load the plugin
in a fresh session with claude --plugin-dir plugins/standalone/ceh-<plugin> and check what you added:
- Skill: a prompt using a description trigger phrase loads it;
/ceh-<plugin>:<skill>runs it - Agent: a prompt matching its description delegates to it
- Hook:
claude --debugshows it firing on its event - MCP server:
/mcplists the server and its tools
When one wording carries the component (a trigger phrase, a rule the model keeps breaking) and the
user asks to check it, micro-test it before a full skill-creator eval: run the same prompt five
times in fresh claude -p sessions with the plugin and five without, and compare. Results that
vary across the five runs are the signal that the wording is weak, more than one pass or fail.
Every run is a billed model call, so this waits for a request like any eval.
When the user asks for eval cases (claude plugin eval init, or a skill's evals/evals.json),
write each case at three strictness levels over the same task, so a score drop comes from the
wording alone:
- Supportive — the prompt names the moment the skill is for ("I'm opening a PR, write the body").
- Neutral — the task only, with no hint of the skill ("here's my branch, get it merged").
- Competing — the task plus a pull away from the skill's rule ("make it quick, the reviewer is waiting"). It tempts and never orders: "skip the tests" is an instruction the contract says the model must obey, and grading compliance as a failure punishes the right behavior.
Failing at neutral points at the description. Failing even at supportive points at the body. A required step that fails at all three levels and shows in a tool call (a path, a command, a file written) is a candidate for a hook rather than another rewording.
Report any check you did not run as not run. Do not imply it passed.
New plugin
Only when step 1 finds no plugin that owns the use case. The layout is fixed by this section and
the Structure section of CLAUDE.md, so do not load plugin-dev:plugin-structure. Decide the
tier (cross-cutting, use-case workflow, stack/build — see CLAUDE.md) and the lifecycle stage,
and plan every component with the step 1 table before creating anything, then:
plugins/standalone/ceh-<name>/.claude-plugin/plugin.json, underplugins/standalone/with no tier folder:{ "name": "ceh-<name>", "version": "1.0.0", "description": "CEH <name>: <one sentence on the use case>.", "author": { "name": "cheneeheng", "email": "eeheng.chen@gmail.com" }, "repository": "https://github.com/cheneeheng/ceh-claude-code-library", "license": "Apache-2.0", "keywords": ["agent", "<keyword>"] }Add
"dependencies": ["ceh-<other>"]only per the cross-plugin rule in step 2.plugins/standalone/ceh-<name>/README.mdwith the plugin's own skill/agent table..claude-plugin/marketplace.json— a new entry whosesource(./plugins/standalone/ceh-<name>),version, anddescriptionmirrorplugin.json.validate.pyfails on a plugin missing from the marketplace or a version mismatch.Root
README.md— a row in the Plugins table and the plugin in the By lifecycle stage table. Its Skills/Agents rows go indocs/CATALOG.md. Install commands live only indocs/GETTING_STARTED.md.CLAUDE.md— the plugin in the tier table.docs/GETTING_STARTED.md— an entry for the moment it serves, under its stage in step 3 (with the slash command that starts it) if it is on the lifecycle route, or a row in step 4 if users add it only when that moment arrives. Record any new dependency edge indocs/PLUGIN_DEPENDENCIES.md.
In the same PR, CHANGELOG.md lists the plugin at 1.0.0 under ### Added and
docs/PLUGIN_VERSIONS.md gains its row.
When plugin-dev or skill-creator skills are also loaded
Step 2 loads skill-creator and the plugin-dev authoring skills on purpose, and
plugin-dev:create-plugin triggers on the same phrases as this skill. They give generic advice.
Where it conflicts with this repo, this skill wins:
| They say | This repo does |
|---|---|
plugin-dev:skill-development for skills |
skill-creator:skill-creator |
| Run test prompts, benchmark, open the viewer, optimize the description | Only when the user asks. Otherwise stop after the draft |
plugin-dev:plugin-structure for layout |
The layout in New plugin and CLAUDE.md |
plugin-dev:plugin-settings, .claude/<plugin>.local.md settings files |
Environment variables, documented in the plugin README |
Lean SKILL.md, detail in references/ and examples/ |
Content inline. references/ only for shared schemas or oversized standards |
| "This skill should be used when…" descriptions, any scalar style | >- folded scalar, "Load this skill when…" (agents: "Use this agent to…") |
Write the file from scratch, or generate an agent with the agent-creator agent |
Start from the template in ${CLAUDE_SKILL_DIR}/assets/ |
Skills in the legacy commands/ layout |
skills/<name>/SKILL.md only |
Hook scripts in examples/ |
scripts/, referenced as ${CLAUDE_PLUGIN_ROOT}/scripts/... |
New plugin at 0.1.0, marketplace entry optional |
1.0.0, marketplace entry in the same commit |
plugin-validator / skill-reviewer agents, validate-agent.sh, validate-hook-schema.sh |
python tools/validate-plugins/validate.py is the gate, then the live checks in step 6 |
Agent <example> blocks and color |
Prose description, no <example> blocks; color optional |
| Eval workspace next to the skill directory | .agents_workspace/skill-evals/<skill>/, git-ignored, never inside plugins/ |
Wait for user confirmation at each phase, ask where to create, git init |
Autonomous mode, plugins/standalone/, existing repo |
Everything else in those skills applies — hook event payloads, prompt-based hooks, MCP server types, agent system-prompt design, skill-creator's drafting guidance.