Skip to main content
ClaudeWave
Skill696 repo starsupdated today

plugin-builder

# ClaudeWave: Plugin Builder Plugin Builder guides users through building and shipping Vellum plugins end-to-end, from scaffolding the directory structure and wiring imports against the plugin API to packaging manifests and publishing to the marketplace. Use this skill when a user wants to bundle hooks, tools, and skills into an installable plugin package, publish existing capabilities to the marketplace, or deploy updates to a plugin's GitHub repository.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/vellum-ai/vellum-assistant /tmp/plugin-builder && cp -r /tmp/plugin-builder/skills/plugin-builder ~/.claude/skills/plugin-builder
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Plugin Builder

You guide the user through building a Vellum plugin end to end: deciding what surfaces they need, scaffolding the directory, wiring imports against `@vellumai/plugin-api`, packaging the manifest, and shipping the plugin through the marketplace catalog.

Plugins are in beta. The peer-dep range you declare is what gets you load. Treat everything you write in this skill as something that can break between Vellum releases until 1.0 ships, and pin a real range.

## When to use this skill

USE THIS SKILL WHEN:

- The user wants to build a plugin for their assistant, even if they have not said "plugin" by name. Any "extend my assistant so it can do X automatically" request that implies shipping a capability is in scope.
- The user has existing skills, hooks, or tools scattered in their workspace and wants to bundle them.
- The user wants to publish a GitHub repo into the Vellum plugin marketplace.
- The user is exploring the surfaces (hooks, tools, skills) for the first time and needs a guided scaffold.

DO NOT use this skill when:

- The user only wants to install a plugin (`assistant plugins install <name>` is a 30-second CLI call, no skill needed).
- The user only wants a single SKILL.md authored (skill-management is the right skill).
- The user wants to add a webhook or user route (`assistant routes` is the right skill).
- The user wants to write a one-off TypeScript file the assistant should execute inline (no packaging needed).

## Before you write a single file

Ask before building. Five questions, in this order. Stop if the user is unclear on any of them.

1. **What job does the plugin do?** One sentence, plain language. If you cannot write this, the plugin should not be built yet.
2. **Which surfaces does it ship?** Tools (model calls), hooks (lifecycle transforms), and skills (on-demand instructions) are the three. Most plugins ship one or two, not all three.
3. **Does it need credentials?** An API key, OAuth token, or webhook secret is not a value that belongs in a `.ts` file. Anything sensitive gets declared in the manifest and resolved at `init` time.
4. **Where will the source live?** A GitHub repo, ideally under the user's own namespace. The marketplace entry pins to a full commit SHA.
5. **Is the user writing TypeScript or compiling ahead?** In-repo Bun/Node compile on daemon start is the default. If they want a different build, ask now.

You have an alignment problem if the user cannot answer questions 1 and 2. Push back and clarify before scaffolding. The most expensive waste of plugin-authoring time is building a plugin whose job is fuzzy.

✓ Checkpoint: alignment on job and surfaces locked before continuing.

## Mental model

A plugin is a directory with a manifest and zero or more surface subdirectories. The host walks the directory on load and discovers what the plugin contributes. Missing directories are silently skipped, so a plugin contributes only what it ships. A broken surface file fails only itself; sibling plugins keep loading.

| Surface | Lives in          | When it fires                                                                                                 |
| ------- | ----------------- | ------------------------------------------------------------------------------------------------------------- |
| Tools   | `tools/<name>.ts` | When the model decides to call the tool.                                                                      |
| Hooks   | `hooks/<name>.ts` | At fixed lifecycle events (init, user-prompt-submit, pre/post-model-call, post-tool-use, post-compact, stop). |
| Skills  | `skills/<name>/`  | When the conversation matches the skill's `description` and activation hints.                                 |

Everything else inside the plugin directory (`src/`, `utils/`, `schemas/`) is yours and is not walked by the loader. Put shared helpers there.

A broken plugin never blocks the rest of the workspace. Loading is per-plugin, per-surface, and time-boxed to 10 seconds.

## Scaffold the directory

The loader expects exactly this shape:

```
my-plugin/
├── package.json         # Manifest, required
├── README.md            # Optional docs
├── hooks/               # One file per hook
├── tools/               # One file per tool
├── skills/              # One directory per skill
│   └── <skill-name>/
│       └── SKILL.md
└── src/                 # Yours, not walked by the loader
```

Choose a kebab-case directory name. It becomes the install name. `@scope/<name>` is allowed; the loader strips the scope for the runtime plugin name. Duplicate names fail registration.

The manifest is a normal `package.json` with three watched fields:

```json
{
  "name": "@you/my-plugin",
  "version": "0.1.0",
  "peerDependencies": {
    "@vellumai/plugin-api": "^0.8.0"
  },
  "vellum": {}
}
```

- `name`: required. The scope is stripped for the runtime plugin name.
- `version`: informational. Defaults to `0.0.0` if absent.
- `peerDependencies["@vellumai/plugin-api"]`: required while the API is in beta. Pin a real range. Mismatches are logged but do not yet block load; they will harden into a hard reject before 1.0.
- `vellum`: reserved.

To exercise the plugin locally before pushing to the catalog, drop the directory into the path the loader scans. The host walks `<workspaceDir>/plugins/<name>/` on each daemon start:

```
cp -R my-plugin "$(assistant daemon workspace)/plugins/my-plugin"
```

(or copy into the path your runtime resolves for `<workspaceDir>`, then restart the daemon). Install by name is reserved for catalog-published plugins shipped through `marketplace.json`.

✓ Checkpoint: directory tree and manifest written. Plugin directory copied into the workspace's `plugins/<name>/`, daemon restarted, `assistant plugins list` shows your plugin with status `ok`.

## Recipe 1: a tool the model can call

A tool is a default-exported object from `tools/<name>.ts`. The file basename becomes the tool name unless you override `name`. Every fie