Build your own plugin

Pick items from several repos into a pack, build it into a Claude Code plugin, and install it.

  1. 1. Find the items
  2. 2. Create a pack and add items
    1. What you can pick
    2. Pack names
  3. 3. Build it
  4. 4. Install it in Claude Code
  5. Install into Gas City
  6. Change a pack
  7. Keep it current
  8. Don’t edit the built files
  9. Packs for every project

This walkthrough builds csharp-sdlc, a C# pack with a skill, an agent and a command from wshobson/agents, plus the whole Stripe plugin from anthropics/claude-plugins-official. To follow it exactly, add those sources first:

ipm marketplace add wshobson/agents anthropics/claude-plugins-official
ipm catalog sync

1. Find the items

ipm search c# --type skill --type agent
ipm search accessibility --type command
ipm search stripe --type plugin

Each hit’s ID is on the line under it. Check one with ipm show:

ipm show 'agent:wshobson/agents:plugins/jvm-languages/agents/csharp-pro.md'

2. Create a pack and add items

Run these in your project. ipm pack new creates ipm.toml in the current folder, unless a folder above already has one, in which case that folder is the project.

ipm pack new csharp-sdlc --description "C# development lifecycle"
ipm pack add csharp-sdlc \
  skill:wshobson/agents:plugins/dotnet-contribution/skills/dotnet-backend-patterns \
  agent:wshobson/agents:plugins/jvm-languages/agents/csharp-pro.md \
  command:wshobson/agents:plugins/accessibility-compliance/commands/accessibility-audit.md \
  'plugin:anthropics/claude-plugins-official#stripe'
ipm pack show csharp-sdlc

ipm pack show lists each item’s type, name, repo, stars and ID. ipm.toml now holds:

[packs.csharp-sdlc]
description = "C# development lifecycle"
items = [
  "skill:wshobson/agents:plugins/dotnet-contribution/skills/dotnet-backend-patterns",
  "agent:wshobson/agents:plugins/jvm-languages/agents/csharp-pro.md",
  "command:wshobson/agents:plugins/accessibility-compliance/commands/accessibility-audit.md",
  "plugin:anthropics/claude-plugins-official#stripe",
]

ipm pack add checks every ID before writing anything: if one is wrong, nothing is added.

What you can pick

Pick Becomes
A skill, an agent, or a command in its own file A file in your pack’s plugin, loading as <pack>:<name>
A Copilot plugin Its skills, placed in your pack’s plugin
A whole plugin (local, inline or external) A dependency of your pack, keeping its own name, hooks and MCP servers
A hook, an MCP server or an inline command Refused: they come only with their plugin, and ipm pack add prints the command that adds it
A repo or a marketplace Refused: pick what it holds (ipm show tree <id> lists it)

Pack names

Pack names are kebab-case: lowercase letters, digits and single hyphens, such as csharp-sdlc, which claude plugin validate --strict wants of a plugin name; ipm pack new suggests the kebab form of a name it refuses. Names Claude Code reserves for Anthropic (claude-…, anthropic-…, and official next to claude or anthropic) are refused.

3. Build it

ipm pack build
✓ csharp-sdlc: 3 item(s), 1 plugin dependency(ies)
validate: ok

Install in Claude Code: ipm install claude
Or wire it yourself:
  claude plugin marketplace add /path/to/project/.ipm

ipm pack build:

  1. resolves every item to a repo, path and commit;
  2. finds each repo at its commit in your home: your synced source when it is at that commit (no download), else the shared pin store $IPM_HOME/pinned, which pnpm fills (with --ignore-scripts) the first time any project needs that commit;
  3. places the picked files in .ipm/plugins/csharp-sdlc/ (skills in skills/, agents in agents/, commands in commands/) with a plugin.json recording where each came from;
  4. writes the marketplace .ipm/.claude-plugin/marketplace.json, named <project folder>-ipm, listing csharp-sdlc and its dependency csharp-sdlc-stripe;
  5. records every pin in .ipm/ipm.lock;
  6. runs claude plugin validate --strict on the result;
  7. writes .ipm/build.stamp last: a hash of the packs, the lock and the ipm version, which lets ipm install skip a build that is already current.

Validation is skipped, with a note, when claude isn’t on your PATH, or with --no-validate. If anything fails, your previous build and lock are left as they were.

If an external plugin’s upstream entry isn’t pinned to a commit, ipm pack build warns you: Claude Code will install whatever that entry points at on the day it installs.

4. Install it in Claude Code

ipm install claude csharp-sdlc

ipm install claude builds the pack first when .ipm/ is out of date (it prints build is current when it isn’t), then installs it into Claude Code for this project; --scope user installs it for every project, --scope local for just you in this folder, and --path DIR picks another folder. --dry-run prints the claude commands without running them, and --update re-pins every item before building. The install is recorded in ipm.toml ([[packs.csharp-sdlc.installs]]), so a bare ipm install later reinstalls every recorded install. To wire it yourself instead:

claude plugin marketplace add ./.ipm

Then, in Claude Code:

/plugin install csharp-sdlc@<project folder>-ipm

Claude Code installs csharp-sdlc-stripe with it. Your picks load under the pack’s name: the skill and the command (commands show as skills) as csharp-sdlc:<name>, alongside the csharp-pro agent. The Stripe plugin keeps its own namespace.

Adding a marketplace changes your Claude Code configuration. To try a build first, use a throwaway config: export CLAUDE_CONFIG_DIR=$(mktemp -d) before running claude.

Install into Gas City

The same pack can become a Gas City pack folder:

ipm install gascity csharp-sdlc
Import it into a city's pack.toml or city.toml:
  [imports.csharp-sdlc]
  source = "/Users/you/work/.ipm/.gascity/csharp-sdlc"

It builds first unless the build is current, then writes .ipm/.gascity/csharp-sdlc/ in the project with your skills, agents and MCP servers in Gas City’s layout. To write it somewhere else, name an existing folder with -C: ipm install gascity csharp-sdlc -C ~/cities/dev writes ~/cities/dev/csharp-sdlc/. Leave out the pack to write every pack of the project, each in its own folder; -g writes your user-scope packs into the project’s .ipm/.gascity the same way. Paste the [imports] table into the city’s pack.toml or city.toml: ipm never edits a city. Gas City has no slash commands or hooks, so commands, hooks and plugins whose files live in another repo are skipped with a skipped … warning; agent and MCP settings Gas City has no equivalent for are dropped with a warning. --dry-run lists the files without writing. Rerun ipm install gascity (or a bare ipm install) after changing the pack: it replaces its own folder completely. See the commands reference for every message.

Change a pack

ipm pack add csharp-sdlc '<another id>'
ipm pack remove csharp-sdlc 'plugin:anthropics/claude-plugins-official#stripe'
ipm pack build

Rerun ipm pack build after every change. Items you remove leave no files behind, and building twice gives identical output. In Claude Code, run /plugin marketplace update <project folder>-ipm to pick up the new build.

ipm pack list lists every pack in the scope with its item count and description.

Keep it current

ipm pack build keeps every item at the commit in .ipm/ipm.lock, so your plugin doesn’t change when your sources do. To move forward:

ipm catalog sync --upgrade   # move your sources to their latest commits
ipm pack build --update   # re-pin every item to your current catalog

Don’t edit the built files

Files in .ipm/plugins/ are hard links to the repos in your home ($IPM_HOME/sources or $IPM_HOME/pinned): editing one changes that copy too, and every other project built from it, and the next ipm pack build rebuilds the folder anyway. To change an item, change it upstream, or fork it into a repo of your own, add that as a source and pick from it.

Packs for every project

Add -g to any ipm pack command or to ipm pack build to use your user scope, $IPM_HOME/global, instead of a project:

ipm pack new everyday -g
ipm pack add everyday '<id>' -g
ipm pack build -g
ipm install claude everyday -g      # user scope; recorded in $IPM_HOME/installs.json

Its marketplace is called ipm-global. To wire it yourself instead: claude plugin marketplace add ~/Library/ipm/global/.ipm (macOS; ~/.local/share/ipm/global/.ipm on Linux).