creating-plugins
Build and scaffold custom plugins for the EmDash CMS environment.
Install
mkdir -p .claude/skills/creating-plugins && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10979" && unzip -o skill.zip -d .claude/skills/creating-plugins && rm skill.zipInstalls to .claude/skills/creating-plugins
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Create EmDash CMS plugins with hooks, storage, settings, admin UI, API routes, and Portable Text block types. Use this skill when asked to build, scaffold, or implement an EmDash plugin, or when creating plugin features like custom block types, admin pages, or content hooks.Key capabilities
- →Scaffolds EmDash CMS plugins
- →Implements hooks and routes
- →Manages admin UI and block types
- →Supports standard and native plugin formats
How it works
It provides a standardized structure and tools for creating TypeScript-based plugins for EmDash CMS.
Inputs & outputs
When to use creating-plugins
- →Scaffolding a new plugin
- →Implementing custom content hooks
- →Adding admin pages to CMS
About this skill
Creating EmDash plugins
Build against the API that reaches the intended execution mode. Source types and production-boundary tests take precedence over examples in this skill when they disagree.
Choose a format
| Format | Runtime source | Admin UI | Distribution |
|---|---|---|---|
| Sandboxed | src/plugin.ts default-exports a SandboxedPlugin | Block Kit pages, widgets, saved-entry panels, and actions | Plugin CLI and registry |
| Native | definePlugin() / createPlugin() | React, Block Kit, and Astro components | Trusted site dependency only |
Use a sandboxed plugin unless the feature needs host-process access, React admin code, Astro rendering components, raw page fragments, or custom Portable Text block definitions. Native plugins run with the site's authority and cannot be installed from the registry.
Scaffold a sandboxed plugin
pnpm dlx @emdash-cms/plugin-cli init my-plugin
cd my-plugin
pnpm install
pnpm run test
The manifest is the identity and trust contract. Runtime hooks and routes live in src/plugin.ts; the CLI generates descriptors, manifests, and bundles. Do not create a separate descriptor factory or sandbox-entry.ts.
import type { SandboxedPlugin } from "emdash/plugin";
const plugin: SandboxedPlugin = {
hooks: {
"content:afterSave": async (event, ctx) => {
ctx.log.info("Content saved", { id: event.content.id });
},
},
};
export default plugin;
Import authoring types from emdash/plugin with import type. Value imports are limited to lightweight helpers such as pluginRoute() and pluginResponse(), which the CLI bundles. Sandboxed runtime code can use Web APIs but not Node.js built-ins.
Declare access
Declare every host API in emdash-plugin.jsonc. Adding authority, exposing a route publicly, or adding MCP tools requires renewed administrator approval.
| Capability | Grants |
|---|---|
schema:read | Public collection and field definitions |
admin.editor-draft:read | Selected unsaved field values after an explicit editor interaction |
admin.editor-draft:patch | Host-validated unsaved field changes proposed for editor review |
content:read | Content identity, translations, and published public URLs |
content:revisions:read | Retained revision data; implies content read |
content:write | Create, update, delete, and translation creation; implies read |
content:publish | Revision-fenced publish, unpublish, schedule, and unschedule; implies read |
content:restore | Revision-fenced reads and restoration of trashed content |
hooks.content-policy:register | Pre-publish, pre-schedule, and pre-unpublish policy hooks |
taxonomies:read | Taxonomy definitions, terms, and entry assignments |
taxonomies:write | Term creation and assignment deltas; implies read |
bylines:read | Public byline profiles and single or batched entry credits |
redirects:read | Versioned redirect inspection |
redirects:write | Versioned redirect creation, update, and deletion; implies read |
comments:read | Stored non-trashed comments and their personal data |
comments:moderate | Expected-status moderation; implies read |
media:read | Ready-media metadata and authenticated asset URLs |
media:bytes:read | Bounded media bytes and content hashes |
media:metadata:write | Alt text, caption, and focal-point updates |
media:write | Upload and delete; implies media read |
network:request | ctx.http.fetch() restricted to allowedHosts |
network:request:unrestricted | ctx.http.fetch() without a host list |
users:read | User directory lookup; also required by comment hooks |
email:send | Email delivery when a transport is configured |
hooks.email-transport:register | Exclusive email:deliver hook |
hooks.email-events:register | Email before/after hooks |
hooks.page-fragments:register | Trusted-only page fragments; excluded from sandbox registration |
Settings, KV, declared storage, logging, and cron scheduling are plugin-scoped and need no capability. Use ctx.settings for user configuration, ctx.kv for internal key-value state, and declared ctx.storage.<collection> for queryable records.
Read the focused reference for the API being used:
- Content, schema, translations, and publication
- Taxonomies and redirects
- Comments
- Media
- Storage, KV, and encrypted settings
Routes, HTTP, and MCP
Routes are private by default and require authentication, their declared RBAC permission, token scope or CSRF as appropriate. Public routes are internet-facing and require explicit install consent.
Undeclared routes keep the legacy JSON/query envelope. Declare methods and body modes for host-enforced parsing. Use pluginResponse() only on a route that declares a raw response. Outbound ctx.http.fetch() responses and declared route bodies are buffered and bounded.
MCP tools reference private JSON routes with explicit permissions and Zod input schemas. Mark difficult-to-reverse operations destructive: true. Read API routes and MCP tools.
Hooks
Sandboxed hooks enter the same priority, dependency, timeout, error-policy, enablement, exclusive-provider, and capability pipeline as trusted hooks. Publication policy hooks identify action origin and actor without granting publication authority. Comment moderation invoked through ctx.comments reports plugin origin and runs the normal after-hook once. Read Hooks.
Declarative admin UI
Sandboxed pages and widgets return validated Block Kit. Structured links use host-resolved targets; routeCtx.ui carries host-attested locale, direction, and surface. External images require matching network authority.
Saved-entry panels and actions point to private routes. Ordinary panel load receives only host-reloaded saved identity and version. Add admin.editor-draft:read or admin.editor-draft:patch plus extension-level collection and field selectors when an explicit interaction must receive selected unsaved fields or propose an atomic whole-field patch. Patch does not imply read. The host previews accepted patches, marks the form dirty, and never saves them automatically. Read saved content through capability-gated ctx.content.
Declarative field widgets currently compose supported Block Kit elements into a JSON value. Custom Portable Text blocks and Astro render components remain native-only. Read Admin UI, Block Kit, and Portable Text blocks.
Runner parity
Cloudflare Worker Loader and Node/workerd execute the same bundle behind a plugin-scoped bridge. Both return buffered WHATWG responses from ctx.http.fetch() with binary bytes preserved and decoded request/response bodies limited to 8 MiB. Write against exported types, not extra methods found in one wrapper.
Read Sandbox boundaries before designing around an API not listed here.
Test the production boundary
Use createPluginTestHost() for fast hook, route, manifest, capability, KV, settings, and storage transport tests. Use createPluginRuntimeTestHost() when the test must exercise real content actions, plugin activation, media, comments, redirects, scheduling, restart, authorization, CSRF, caching, Block Kit validation, or saved-entry extensions.
Runtime fixtures establish state without firing hooks. Runtime actions call production boundaries; inspectors read observable state. Queue outbound HTTP responses with host.http.respond() and inspect requests with host.http.requests(). Dispose every host after use. Add Node/workerd parity only for runner-sensitive behavior.
References
- Content, schema, translations, and publication
- Taxonomies and redirects
- Comments
- Media
- Hooks
- Storage, KV, and encrypted settings
- Admin UI and field widgets
- API routes and MCP tools
- Block Kit
- Portable Text blocks
- [Sandbox
Content truncated.
When not to use it
- →When the project is not an EmDash CMS plugin
- →When the user wants a non-TypeScript plugin
Limitations
- →Specific to EmDash CMS
- →Requires TypeScript knowledge
How it compares
It offers a dedicated, standardized framework for EmDash CMS plugin development.
Compared to similar skills
creating-plugins side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| creating-plugins (this skill) | 0 | 3mo | Review | Advanced |
| scaffold-feature | 0 | 5mo | Review | Intermediate |
| supabase-developer | 95 | 9mo | Review | Intermediate |
| payload | 73 | 4mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
scaffold-feature
niklasbrandt
Scaffold a complete full-stack feature: FastAPI endpoint, dashboard Web Component, i18n keys, test stubs, and documentation checks.
supabase-developer
daffy0208
Build full-stack applications with Supabase (PostgreSQL, Auth, Storage, Real-time, Edge Functions). Use when implementing authentication, database design with RLS, file storage, real-time features, or serverless functions.
payload
payloadcms
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
telegram-mini-app
davila7
Expert in building Telegram Mini Apps (TWA) - web apps that run inside Telegram with native-like experience. Covers the TON ecosystem, Telegram Web App API, payments, user authentication, and building viral mini apps that monetize. Use when: telegram mini app, TWA, telegram web app, TON app, mini app.
stripe-integration
wshobson
Implement Stripe payment processing for robust, PCI-compliant payment flows including checkout, subscriptions, and webhooks. Use when integrating Stripe payments, building subscription systems, or implementing secure checkout flows.
deepwiki-rs
sopaco
AI-powered Rust documentation generation engine for comprehensive codebase analysis, C4 architecture diagrams, and automated technical documentation. Use when Claude needs to analyze source code, understand software architecture, generate technical specs, or create professional documentation from any programming language.