Claude Seekho

Skills and Plugins: How They Work Inside

Section 15 covered the basic idea of a skill. Here is the full mechanism: when and how Claude loads a skill, the frontmatter fields, arguments, scripts, and what happens from installing a plugin to loading it in a session. (Details follow the Claude Code docs.)

17.1 Skill lifecycle — what happens in one request

How a skill gets triggered
  1. 1.1. Session start: only each skill's name + description are in context
  2. 2.2. User prompt: "write the release notes"
  3. 3.3. Claude matches: compares the prompt ↔ descriptions
  4. 4.4. SKILL.md loads: the full body now enters the context
  5. 5.5. Extra files / scripts: read or run when needed
  6. 6.6. Result: the answer follows the skill's instructions

The description is the trigger — it decides when the skill runs

That is why description is the most important field. Claude does not have the skill's body until it loads it — it decides only by looking at the description. If the description does not clearly say "when to use", the skill will never trigger (or will trigger in the wrong place).

17.2 Where skills live and which has higher priority

LocationPathFor whom
EnterpriseIn the managed settings directory .claude/skills/<name>/SKILL.mdThe whole organization
Personal~/.claude/skills/<name>/SKILL.mdAll your projects
Project.claude/skills/<name>/SKILL.mdEveryone on this repo (committed to git)
Nested<subdir>/.claude/skills/<name>/SKILL.mdWhen working in that folder
Plugin<plugin>/skills/<name>/SKILL.mdWherever the plugin is enabled
claude.ai accountSkills enabled on the accountSessions logged in with that account
  • If names are the same, priority: Enterprise > Personal > Project.
  • A skill overrides a .claude/commands/ file and a bundled skill of the same name.
  • Plugin skills are always namespaced (/plugin-name:skill-name), so they never clash with anything.

17.3 SKILL.md frontmatter — important fields

FieldWhat it does
nameCommand name (/name). If omitted, the folder name is used
descriptionWhat it does + when to use it. Claude decides from this
when_to_useExtra trigger context, appended to the description
disable-model-invocation: trueClaude cannot run it itself — only the user via /name. The description is not even put in context
user-invocable: falseHidden from the / menu — only Claude uses it itself
allowed-toolsNo permission prompt for these tools during the skill's turn (ends at the next message)
disallowed-toolsThese tools are blocked while the skill is active
argument-hint, argumentsAutocomplete hint ([issue-number]), named arguments
model, effortOverride model / effort for that turn
context: fork + agentRun the skill in a separate subagent (e.g. agent: Explore) — main context stays clean
pathsGlob patterns — auto-activates only when working on matching files
hooksRegister hooks when the skill is invoked
shellbash (default) or powershell

17.4 Who can run it — invocation control

SettingUser via /name?Claude itself?When to use
DefaultYesYesMost skills
disable-model-invocation: trueYesNoDeploy, commit, risky or manual work
user-invocable: falseNoYesBackground knowledge (e.g. coding conventions)

17.5 Arguments and variables

---
name: fix-issue
description: Takes a GitHub issue number and fixes it
argument-hint: [issue-number]
disable-model-invocation: true
---

Fix GitHub issue $ARGUMENTS according to our coding standards.
1. Read the details with `gh issue view $ARGUMENTS`
2. Fix it, run the tests, commit
PlaceholderValue
$ARGUMENTSThe full argument text (/fix-issue 123 → 123)
$0, $1 / $ARGUMENTS[N]Positional arguments (0-based, shell-style quoting)
$issueA named argument given in the arguments: frontmatter
${CLAUDE_SKILL_DIR}The skill's folder (for building script paths)
${CLAUDE_PROJECT_DIR}, ${CLAUDE_SESSION_ID}Project folder, session ID
No placeholderClaude Code appends ARGUMENTS: <value> at the end

17.6 Supporting files and scripts

release-notes/
├── SKILL.md              # required — main instructions (keep under 500 lines)
├── reference.md          # read when needed
├── template.md           # read when needed
└── scripts/
    └── collect_commits.py   # RUN, not read — only its output enters the context
  • Mention these files in SKILL.md ("see template.md for the format") so Claude knows when to open them.
  • The script's code does not enter the context, only its output — so keeping big logic in a script saves tokens and is deterministic.
  • To run a script without a permission prompt use allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/collect_commits.py *).

17.7 A complete example skill

---
name: release-notes
description: Creates user-friendly release notes from Git commits. Use when the user
  asks for release notes, a changelog, or "what's new in this release".
argument-hint: [from-tag]
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/collect_commits.py *)
---

# Release Notes

1. Run `${CLAUDE_SKILL_DIR}/scripts/collect_commits.py $ARGUMENTS` — it returns the commits as JSON.
2. Split the commits into Features / Fixes / Breaking Changes.
3. Follow `template.md` for the format.
4. Remove technical jargon; write for end users.

17.8 Limits and behaviours to remember

PointDetail
Description limitdescription + when_to_use are cut at 1,536 characters in the listing — put the main use case first
Live reloadAdding/editing/deleting a skill applies to the current session immediately; if it is a new top-level skills folder, run /reload-skills
After compactionThe first 5,000 tokens of each invoked skill are kept (total budget 25,000)
allowed-toolsPre-approval for that turn only
Commands vs skillsBoth .claude/commands/deploy.md and .claude/skills/deploy/SKILL.md create /deploy; skills add: supporting files, invocation control, auto-loading
Subfolder commands.claude/commands/frontend/component.md → /frontend:component

17.9 How to write a good description

BadGood
"SQL helper""Reviews SQL Server queries for performance and security. Use when the user shares a SQL query, stored procedure or execution plan."
"Does stuff with PDFs""Extracts text/tables from PDFs, merges/splits PDFs. Use when the user mentions a .pdf file or wants to create a PDF."

17.10 What a plugin is and when you need one

A plugin is a folder containing skills, agents (subagents), hooks, MCP servers (and a few other components), plus a manifest .claude-plugin/plugin.json. Claude Code installs and loads it as one unit.

SituationWhat to use
Just one skill / hook for yourselfStandalone: ~/.claude/skills/, hooks in settings
For the team of one repoThe project .claude/ folder (in git)
Sharing several skills + hooks + MCP togetherPlugin
Versioned releases across many projects / the whole orgPlugin + Marketplace

17.11 From plugin install to session — 3 stages

Plugin loading pipeline
  1. 1.Marketplace: marketplace.json catalog (git repo)
  2. 2.1. Declared: enabledPlugins, extraKnownMarketplaces in settings
  3. 3.2. Fetched: on disk: ~/.claude/plugins/cache/<mkt>/<plugin>/<version>/
  4. 4.3. Loaded: at session start or on /reload-plugins
  5. 5.Components live: skills, agents, hooks, MCP servers

Changing settings or disk does not change a running session — you need /reload-plugins or a new session

17.12 What happens to each component when a plugin loads

ComponentFileWhat you get in the session
Manifest.claude-plugin/plugin.jsonThe plugin's name (prefix), version, description
Skillsskills/<name>/SKILL.mdThe /plugin-name:skill-name command + auto-trigger
Agentsagents/<name>.mdA subagent plugin-name:agent-name that Claude can delegate to
Hookshooks/hooks.jsonRun on lifecycle events (same format as settings)
MCP servers.mcp.jsonThe server runs while the plugin is enabled, tools are available
Commands (old form)commands/*.mdSlash commands — use skills/ in new plugins

17.13 Cost of an enabled plugin

  • Context: every skill/agent's name + description stays in context on every turn (even if unused) — it costs tokens. The full body only when used.
  • Processes: the plugin's MCP servers run with every session; hooks fire on their events.
  • Permissions: whatever the plugin runs, it runs with your user privileges — review before installing.
  • Not using it? Disable instead of uninstalling: /plugin or claude plugin disable <id>.

17.14 Your first plugin — end to end

mkdir -p my-first-plugin/.claude-plugin
mkdir -p my-first-plugin/skills/hello
// my-first-plugin/.claude-plugin/plugin.json
{
  "name": "my-first-plugin",          // required; becomes the prefix of every skill/agent, no spaces
  "description": "A greeting plugin to learn the basics",
  "version": "1.0.0",                 // optional; if set, users stay on this exact version
  "author": { "name": "Your Name" }
}
<!-- my-first-plugin/skills/hello/SKILL.md -->
---
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.
claude plugin validate ./my-first-plugin      # "✔ Validation passed"
claude --plugin-dir ./my-first-plugin         # load for this session only (not installed)
# in the session:
/my-first-plugin:hello
/reload-plugins                               # edited the files? reload

Shortcut: claude plugin init my-tool → creates a starter plugin in ~/.claude/skills/my-tool/ that auto-loads in every session (my-tool@skills-dir). To turn an existing .claude/ setup into a plugin, copy commands/, agents/, skills/ and put the hooks object from settings into hooks/hooks.json.

17.15 Creating and sharing a marketplace

// my-marketplace/.claude-plugin/marketplace.json
{
  "name": "my-marketplace",
  "description": "Plugins for my team",
  "owner": { "name": "Your Name" },
  "plugins": [
    {
      "name": "my-first-plugin",                  // keep it the same as the name in plugin.json
      "source": "./plugins/my-first-plugin",      // path relative to the marketplace root
      "description": "A greeting plugin to learn the basics"
    }
  ]
}
claude plugin validate ./my-marketplace
claude plugin marketplace add ./my-marketplace          # or GitHub: owner/repo
claude plugin install my-first-plugin@my-marketplace     # id = <entry-name>@<marketplace>
claude plugin list                                       # Status: ✔ enabled
claude plugin details my-first-plugin                    # component inventory
Plugin sourceWhenExample
Relative pathPlugin is inside the marketplace repo"./plugins/my-first-plugin"
githubThe plugin has its own GitHub repo{"source": "github", "repo": "org/plugin"}
git-subdirA subfolder of a monorepo{"source": "git-subdir", "url": "org/mono", "path": "tools/x"}
url, archive, npm, commandFrom any git URL, zip, npm package, or command output—

17.16 Install scopes and settings

ScopeWhere it is writtenFor whom
User~/.claude/settings.jsonYou, every project
Project.claude/settings.json (in git)Everyone on the repo (each person installs on their own machine)
Local.claude/settings.local.jsonOnly you, only in this repo
ManagedOrg policyForce-enable or block — nobody can override
// .claude/settings.json — enable the plugin for the team
{
  "enabledPlugins": {
    "my-first-plugin@my-marketplace": true
  }
}

Precedence (low → high): --add-dir → user → project → local → --settings flag → managed. The project enabled it but you do not want it? Set it to false in .claude/settings.local.json.

17.17 The plugin ID tells you where it came from

End of the IDMeaning
@<marketplace>Installed from a marketplace
@inlineFrom --plugin-dir / --plugin-url / the Agent SDK — that session only
@skills-dirA plugin folder in ~/.claude/skills/ or .claude/skills/
@syncedA plugin enabled on the claude.ai account, synced into Claude Code

17.18 Versions, updates and paths

  • If you pin a version in plugin.json, users stay on it until you change the version string. If you do not, the git commit SHA becomes the version (every commit = an update).
  • Auto-update: on by default for official marketplaces, off for others. After an update, /reload-plugins or restart.
  • ${CLAUDE_PLUGIN_ROOT} = the plugin's installed version folder (changes with every version) — use it for script paths in hooks/MCP config.
  • ${CLAUDE_PLUGIN_DATA} = a persistent data folder that survives updates — keep cache and config here.
  • A plugin cannot load components from a path outside its own folder (../shared).

17.19 Debug checklist

  1. claude plugin validate <path> — checks the manifest and every skill/agent frontmatter.
  2. In the session /reload-plugins — applies edits; then run /plugin-name:skill and see.
  3. /plugin → Installed tab (components list) and Errors tab (what did not load and why).
  4. MCP server: status in /mcp. Hook: trigger the event and read the debug log.
  5. Plugin loaded but Claude's behaviour did not change? Compare with/without the plugin using claude plugin eval.