Skip to content

Day 34 of 52 · about 25 min · Skills · Bonus

Package your skills and hooks as a plugin

Copying .claude folders between projects works until one copy drifts. A plugin is one folder with a manifest: your skill and hook travel together, carry a version number, and load in any project with one flag.

You’ll finish with

  • A plugin, plugins/acme-kit/, holding a manifest, the client-update skill and the file guard hook
  • A clean claude plugin validate and a session that runs /acme-kit:client-update

You’ll need

  • Claude Code installed and signed in, with Node.js on your PATH (the hook runs with node)
  • The client-update skill (Day 12) and protect-files.mjs hook (Day 14), or this day’s practice kit

Before you start

Tell us about your setup and the steps change to fit it. Your answers stay in this browser, and you can change them at any time.

Which system are you on?

Commands differ between systems. Pick the one you’ll run Claude Code on.

The steps

One step at a time. When a step’s check matches, tick it and the next one opens.

0 of 5 steps done0%
  1. Step 1 of 5: Lay out the plugin folder

    A manifest in .claude-plugin/, and each component in its own folder beside it.

    plugins/acme-kit/

    • folder .claude-plugin
      • plugin.jsonNewthe manifest
    • folder skills
      • folder client-updatecopied from .claude/skills/
        • SKILL.md
        • example.md
    • folder hooks
      • hooks.jsonNew
    • folder scripts
      • protect-files.mjscopied from .claude/hooks/
    Only plugin.json goes in .claude-plugin/. Components saved there don’t load.
    The kit has these files. In your own project, ask Claude:
    Copy .claude/skills/client-update into plugins/acme-kit/skills/, and .claude/hooks/protect-files.mjs into plugins/acme-kit/scripts/. Copy only: don’t change or delete the originals.

    Check it worked

    plugins/acme-kit/ holds skills/client-update/SKILL.md and scripts/protect-files.mjs.

  2. Step 2 of 5: Write the manifest

    plugin.json names the plugin, and the name becomes the prefix on its skills.
    plugins/acme-kit/.claude-plugin/plugin.jsonJSON
    {
      "name": "acme-kit",
      "description": "The client-update skill and a hook that blocks edits to .env files and test/, packaged to install in any project",
      "version": "1.0.0",
      "author": {
        "name": "HorizonLux Claude Code Challenge"
      }
    }
    
    .claude-plugin/plugin.json
    name: acme-kit
    Required, no spaces. Skills become /acme-kit:<skill>
    description: "The client-update skill…"
    What people see in /plugin
    version: 1.0.0
    Installs stay on this version until you change it
    author.name: "HorizonLux…"
    Who to credit
    Only name is required. The validator warns when the other three are missing.

    Check it worked

    .claude-plugin/ holds plugin.json and nothing else.

  3. Step 3 of 5: Move the guard hook

    Copy the hook entry into hooks/hooks.json and point it at the plugin’s own script.
    plugins/acme-kit/hooks/hooks.jsonJSON
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "node",
                "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/protect-files.mjs"]
              }
            ]
          }
        ]
      }
    }
    

    Changes to hooks/hooks.json

    Unchanged: "matcher": "Edit|Write",
    Unchanged: "command": "node",
    Removed: "args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-files.mjs"]
    Added: "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/protect-files.mjs"]
    Same shape as the hooks object in settings.json. Only the script path changes.

    ${CLAUDE_PLUGIN_ROOT} is wherever the plugin is installed, so the path works in any project. The Stop hook stays in the project: it runs npm test, which other projects may not have.

    Check it worked

    hooks.json has one PreToolUse entry, matching Edit|Write.

  4. Step 4 of 5: Validate and load it

    Check the plugin, then start a session with it loaded for that session only.
    Terminal

    Run: claude plugin validate ./plugins/acme-kit

    What happens: It names the manifest it checked, then prints:

    Output: ✔ Validation passed

    Run: claude --plugin-dir ./plugins/acme-kit

    What happens: Loads the plugin for this session. Nothing is installed.

    Run both from the project root, in any shell.

    Check it worked

    The validator’s last line is ✔ Validation passed, and Claude Code starts.

    If it didn’t work

    It prints ✘ Validation failed

    Each line above the result names the field to fix. Fix it and run the command again.

  5. Step 5 of 5: Prove both parts work

    Run the prefixed skill, and find the plugin’s hook in /hooks.
    In Claude Code

    You type: /acme-kit:client-update

    What happens: Claude writes the client update, as /client-update did.

    You type: /hooks

    What happens: One PreToolUse hook comes from project settings, one from the plugin.

    Plugin skills carry the plugin name. Hooks don’t, so this one runs twice here.

    In your own project, delete the originals from .claude/ once the plugin works. In the practice project, keep them.

    Check it worked

    The skill runs, and /hooks lists a PreToolUse hook labelled with the plugin.

    If it didn’t work

    The skill is unknown

    Run /reload-plugins after editing plugin files. The Errors tab in /plugin says what failed to load.

Finish · 5 of 5 steps to go

Your setup is one portable folder

Day 35 lists it in a team marketplace, so teammates install it by name.

What you’ll have checked

  • claude plugin validate passes
  • /acme-kit:client-update runs with --plugin-dir

Lesson updates by email

Get lesson updates by email

Leave your email and we’ll tell you when we add or update a lesson.

We’ll email you when we add or update a lesson. We use your email only for this. Privacy policy

Claude Code for your team

Want Claude Code set up across your team?

HorizonLux sets up and repairs Claude Code for teams: CLAUDE.md rules, hooks that enforce them and reviewed MCP servers. Book a free call to talk it through.

Questions

Questions about Day 34

Why not copy .claude/ into every repo?

That is fine for one project. A plugin keeps one versioned copy, so every project gets the same fix.

Sources

Checked against the official Claude Code docs on .

Independent resource, not affiliated with Anthropic. HorizonLux wrote this lesson; Claude Code is made by Anthropic.