skills/paperclip-plugin-dev/SKILL.md
This skill should be used when the user asks to "scaffold a Paperclip plugin", "write a Paperclip plugin manifest", "add a UI slot to a Paperclip plugin", "publish a Paperclip plugin to npm", or "install a Paperclip plugin". Builds, publishes, and installs Paperclip plugins correctly, with critical lessons learned from real publishing failures — plugin capabilities, jobs, webhooks, and agent tools.
npx skillsauth add b-open-io/prompts paperclip-plugin-devInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build Paperclip plugins based on the actual SDK source code, validator source code, and examples. Follows lessons learned from real publishing failures.
@paperclipai/plugin-sdk is published on npm under calver
(YYYY.MDD.patch), with a canary dist-tag for prereleases. Before starting
work, run npm view @paperclipai/plugin-sdk version dist-tags exports --json,
inspect the installed package exports, and pin the selected release in the
project. Do not copy a remembered SDK version or entry-point list.
These mistakes have cost real time. Do not repeat them.
files field in package.json is REQUIREDnpm uses .gitignore to exclude files. Since dist/ is gitignored, built output will be absent unless explicitly declared:
{ "files": ["dist", "package.json"] }
The server rejects manifests where features lack matching capabilities. Every UI slot type, tool, job, and webhook requires a specific capability. See references/manifest-reference.md for the full mapping table.
Example: declaring a dashboardWidget slot without ui.dashboardWidget.register in capabilities causes install failure.
0.0.1The scaffold generates 0.1.0. Change to 0.0.1 before first publish.
paperclipPlugin fields point to ./dist/. Run bun run build before npm publish. Verify with npm pack --dry-run.
When iterating, clear stale cache on the server: npm cache clean --force via SSH.
Publishing a new version with additional capabilities puts the plugin in upgrade_pending state. Plan v1 capabilities carefully.
agents.create in SDKThe plugin SDK can read, pause, resume, invoke, and chat with agents — but cannot create or update arbitrary agents. Two newer, narrower paths exist: ctx.agents.managed (get/reconcile/reset of manifest-declared plugin-managed agents by stable key, requires agents.managed) and ctx.agents.sessions (create/message/close two-way chat sessions, requires agent.sessions.*). For anything else, present templates in the UI and let the operator create agents manually.
error.data, NOT error.messageWhen catching errors from Convex mutations in Next.js API routes, error.message is always the generic "[Request ID: xxx] Server Error". The actual user-facing message is in error.data. Use this pattern:
import { ConvexError } from "convex/values";
function extractConvexErrorMessage(error: unknown): string {
if (error instanceof ConvexError) {
const data = error.data;
if (typeof data === "string") return data;
if (data && typeof data === "object" && "message" in data) {
return String((data as { message: unknown }).message);
}
return JSON.stringify(data);
}
if (error instanceof Error) return error.message;
return String(error);
}
The SDK is now published to npm — depend on @paperclipai/plugin-sdk directly and bump it like any dependency (calver; check the canary dist-tag for bleeding edge). The old local-tgz workflow is only needed when tracking upstream changes that haven't shipped yet:
cd ~/code/paperclip/packages/shared && pnpm run build && pnpm pack
cd ~/code/paperclip/packages/plugins/sdk && pnpm run build && pnpm pack
# Copy both .tgz files to plugin's .paperclip-sdk/ directory
# Delete pnpm-lock.yaml (integrity hashes are cached)
bun install
Local tgz files go stale the moment upstream changes shared types — prefer the published package.
Never call the internal Paperclip API via ctx.http.fetch or raw fetch — use the typed clients: ctx.issues, ctx.agents, ctx.goals, ctx.projects, ctx.companies, ctx.executionWorkspaces, plus the managed-resource clients ctx.routines (requires routines.managed) and ctx.skills (requires skills.managed). The old "there is no ctx.routines client" limitation is gone. ctx.http.fetch (requires http.outbound) is for external services and exists for host-managed tracing/audit; requests need absolute URLs.
node ~/code/paperclip/packages/plugins/create-paperclip-plugin/src/index.ts \
<package-name> \
--output <dir> \
--display-name "<Name>" \
--description "<text>" \
--author "<name>" \
--category connector|workspace|automation|ui \
--sdk-path ~/code/paperclip/packages/plugins/sdk
Immediately after scaffolding:
"files": ["dist", "package.json"] to package.json"0.0.1"import { definePlugin, runWorker } from "@paperclipai/plugin-sdk";
const plugin = definePlugin({
async setup(ctx) {
ctx.events.on("issue.created", async (event) => { ... });
ctx.jobs.register("my-sync", async (job) => { ... });
ctx.data.register("health", async (params) => ({ status: "ok" }));
ctx.actions.register("resync", async (params) => { ... });
ctx.tools.register("my-tool", { displayName: "...", description: "...", parametersSchema: { ... } },
async (params, runCtx) => ({ content: "result" }));
},
async onHealth() { return { status: "ok" }; },
});
export default plugin;
runWorker(plugin, import.meta.url);
For the full PluginContext API: read references/worker-api-reference.md.
import { usePluginData, usePluginAction, useHostContext, usePluginStream } from "@paperclipai/plugin-sdk/ui";
export function DashboardWidget() {
const { companyId } = useHostContext();
const { data, loading, error, refresh } = usePluginData<T>("health", { companyId });
const doAction = usePluginAction("resync");
if (loading) return <div>Loading...</div>;
return <div>Status: {data?.status}</div>;
}
For all hooks, props, patterns, and styling: read references/ui-reference.md.
[ ] bun run build
[ ] "files": ["dist", "package.json"] in package.json
[ ] Version is correct (0.0.1 for first publish)
[ ] Every declared slot type has matching capability
[ ] tools[] → "agent.tools.register", jobs[] → "jobs.schedule", webhooks[] → "webhooks.receive"
[ ] npm pack --dry-run — verify dist/ appears
[ ] bun run test passes
[ ] Use Skill(bopen-tools:npm-publish) for publishing
Enter the npm package name (e.g., @bopen-io/tortuga-plugin) in the "Install Plugin" dialog in Settings → Plugins. The server downloads from npm, validates the manifest, and starts the worker.
"<plugin-slug>-<slot-type>" — e.g., "tortuga-dashboard-widget""DashboardWidget", "FleetPage""clawnet-sync", "fleet-status"constants.ts filePlugin workers run as out-of-process child processes (node:child_process fork) speaking JSON-RPC to the host — no longer a vm.createContext() sandbox. Bundle as ESM via createPluginBundlerPresets from @paperclipai/plugin-sdk/bundlers (the old CJS-only requirement is gone). All Paperclip host interaction still goes through PluginContext methods, gated by declared capabilities.
Declare format: "secret-ref" in instanceConfigSchema. Operator pastes a secret UUID. Resolve at runtime: await ctx.secrets.resolve(config.apiKey). Never cache resolved values.
For detailed API documentation, consult:
references/manifest-reference.md — Capabilities (73 as of SDK 2026.707.0 — verify against packages/shared/src/constants.ts PLUGIN_CAPABILITIES, the reference file predates the expansion from 37), slot types, validation rules, declaration examplesreferences/worker-api-reference.md — Full PluginContext API, lifecycle hooks, runtime constraintsreferences/ui-reference.md — UI hooks (now 7: usePluginData, usePluginAction, useHostContext, useHostNavigation, useHostLocation, usePluginStream, usePluginToast), component props, styling, streaming, navigation patterns~/code/paperclip is a fork of paperclipai/paperclip — verify it is synced with upstream (git fetch upstream && git merge upstream/master) before treating it as source of truth, or consult github.com/paperclipai/paperclip directly.
~/code/paperclip/packages/plugins/sdk/~/code/paperclip/doc/plugins/PLUGIN_SPEC.md~/code/paperclip/packages/plugins/examples/plugin-kitchen-sink-example/~/code/paperclip/packages/plugins/examples/plugin-hello-world-example/~/code/paperclip/packages/plugins/examples/plugin-file-browser-example/~/code/paperclip/packages/plugins/create-paperclip-plugin/~/code/paperclip/server/src/services/plugin-capability-validator.ts~/code/paperclip/server/src/services/plugin-manifest-validator.ts~/code/tortuga-plugin/~/code/tortuga-plugin/ARCHITECTURE.mdtools
This skill should be used when a Claude Code session needs to keep working after Anthropic usage runs out, or when the user asks to run the Claude Code harness on GPT-5.6 Sol. Trigger phrases include "my Anthropic usage ran out", "I'm out of Claude usage", "usage limit reached, what now", "keep working on another model", "run Claude Code on GPT-5.6 Sol", "use GPT-5.6 Sol as the model", "set up claudex", "claudex isn't working", "route the harness through CLIProxyAPI", or "bill against my ChatGPT/Codex subscription". It stands up a local proxy so the Claude Code CLI runs on OpenAI's Codex backend as an escape hatch, and diagnoses that setup when it drifts. macOS + Homebrew.
testing
This skill should be used when the user asks to "open Visual Wayfinder", "answer a Wayfinder ticket visually", "turn this decision into a configurator", "show Wayfinder choices as a dashboard", "prototype the Wayfinder questionnaire", or wants interactive choice cards, tradeoff controls, rankings, ranges, toggles, and consequence previews for one active Wayfinder decision. It wraps the Wayfinder skill and JSON Render; it never replaces the tracker or resolves more than the active decision.
development
This skill should be used when the user asks to "make a visual proposal", "write this up so I can share it", "present these options visually", "diagram the trade-offs", "turn this plan into something reviewable", or requests a shareable design pitch, architecture proposal, RFC, options comparison, or visual roadmap for work that has not been built. It produces one self-contained, theme-aware HTML page led by grounded diagrams. Use visual-review instead for completed code changes; do not use this skill for internal task tracking.
tools
This skill should be used when the user asks to "add plugin settings", "make a plugin configurable", "store per-project plugin configuration", "use settings.local.json", "create a plugin state file", "expose skill settings in Agent Master", or "add a skill interface". Distinguishes official Claude Code settings from project-owned configuration and documents bOpen Agent Master skill interface discovery.