.claude/skills/create-plugin/SKILL.md
Guide for creating new plugins from preset template. CRITICAL: Includes MANDATORY dependency management and Model B distribution rules.
npx skillsauth add NextSpark-js/nextspark create-pluginInstall 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.
Complete guide for scaffolding and configuring new plugins from the preset template.
Los paquetes NPM son para DISTRIBUCIÓN. En proyectos de usuario, el código se COPIA a
/contents/plugins/.
| Contexto | Plugin Location |
|----------|-----------------|
| Monorepo (desarrollo) | plugins/<plugin-name>/ |
| Proyecto usuario | contents/plugins/<plugin-name>/ (copiado por CLI) |
Cada plugin DEBE tener un package.json con esta estructura:
{
"name": "@nextsparkjs/plugin-NOMBRE",
"version": "1.0.0",
"private": false,
"main": "./plugin.config.ts",
"requiredPlugins": [],
"dependencies": {
// SOLO dependencias EXCLUSIVAS de este plugin que NO están en core
},
"peerDependencies": {
"@nextsparkjs/core": "workspace:*",
"next": "^15.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"zod": "^4.0.0"
}
}
REGLA CRÍTICA: Si core tiene una dependencia, el plugin DEBE declararla como peerDependency, NUNCA como dependency.
pnpm create:plugin <plugin-name>core/templates/contents/plugins/starter/plugins/<plugin-name>/contents/plugins/<plugin-name>/Before creating ANY plugin, collect:
Required Information:
1. Plugin name (lowercase, hyphenated slug)
2. Display name (human-readable)
3. Description (purpose of the plugin)
4. Author (team or individual)
5. Complexity level (utility | service | full)
6. Primary functionality (what problem does it solve?)
7. Has entities? (yes/no)
| Level | Includes | Use When |
|-------|----------|----------|
| utility | lib/core.ts, types | Simple helper functions, utilities |
| service | API + components + hooks | Most plugins - external API integration |
| full | Entities + migrations + everything | Complex plugins with own database tables |
pnpm create:plugin <plugin-name>
pnpm create:plugin <plugin-name> \
--description "Plugin description" \
--author "Author Name" \
--display-name "Display Name" \
--complexity service
pnpm create:plugin analytics \
--description "User analytics and metrics tracking" \
--author "Development Team" \
--display-name "Analytics" \
--complexity service
plugins/<plugin-name>/ # Monorepo location
├── plugin.config.ts # Plugin configuration
├── README.md # Plugin documentation
├── .env.example # Environment variables template
├── api/
│ └── example/route.ts # Example API endpoint
├── lib/
│ ├── core.ts # Core plugin logic
│ ├── types.ts # TypeScript types
│ └── plugin-env.ts # Env loader wrapper (REQUIRED)
├── components/
│ └── ExampleWidget.tsx # Example UI component
├── hooks/
│ └── usePlugin.ts # Custom React hook
├── entities/ # (if complexity: full)
│ └── [entity]/ # Each entity has 4 required files
│ ├── [entity].config.ts
│ ├── [entity].fields.ts
│ ├── [entity].types.ts
│ ├── [entity].service.ts
│ └── messages/
├── migrations/ # (if complexity: full)
│ └── README.md
├── messages/
│ ├── en.json # English translations
│ └── es.json # Spanish translations
└── tests/
└── plugin.test.ts # Unit tests
import type { PluginConfig } from '@/core/types/plugins'
import { exampleFunction } from './lib/core'
export const myPluginConfig: PluginConfig = {
name: 'my-plugin',
displayName: 'My Plugin',
version: '1.0.0',
description: 'Plugin description',
enabled: true,
// Dependencies on other plugins
dependencies: [], // e.g., ['auth', 'analytics']
// Exported API
api: {
exampleFunction,
},
// Lifecycle hooks
hooks: {
onLoad: async () => {
console.log('[My Plugin] Loading...')
},
onActivate: async () => {
console.log('[My Plugin] Activated')
},
onDeactivate: async () => {
console.log('[My Plugin] Deactivated')
},
onUnload: async () => {
console.log('[My Plugin] Unloaded')
},
},
}
export interface MyPluginConfig {
apiKey?: string
baseUrl?: string
debug?: boolean
}
export interface MyPluginResult {
success: boolean
data?: unknown
message: string
timestamp: number
}
# ============================================
# MY PLUGIN CONFIGURATION
# ============================================
# Copy this file to .env
# Priority: Plugin .env > Root .env > Defaults
# Required for production
MY_PLUGIN_API_KEY=your-api-key-here
# Optional configuration
MY_PLUGIN_BASE_URL=https://api.example.com
MY_PLUGIN_DEBUG=false
MY_PLUGIN_ENABLED=true
Every plugin MUST use the core's centralized env-loader:
// plugins/my-plugin/lib/plugin-env.ts
import { getPluginEnv } from '@nextsparkjs/core/lib/plugins/env-loader'
interface MyPluginEnvConfig {
MY_PLUGIN_API_KEY?: string
MY_PLUGIN_BASE_URL?: string
MY_PLUGIN_DEBUG?: string
MY_PLUGIN_ENABLED?: string
}
class PluginEnvironment {
private static instance: PluginEnvironment
private config: MyPluginEnvConfig = {}
private loaded = false
private constructor() {
this.loadEnvironment()
}
public static getInstance(): PluginEnvironment {
if (!PluginEnvironment.instance) {
PluginEnvironment.instance = new PluginEnvironment()
}
return PluginEnvironment.instance
}
private loadEnvironment(): void {
if (this.loaded) return
const env = getPluginEnv('my-plugin')
this.config = {
MY_PLUGIN_API_KEY: env.MY_PLUGIN_API_KEY,
MY_PLUGIN_BASE_URL: env.MY_PLUGIN_BASE_URL || 'https://api.example.com',
MY_PLUGIN_DEBUG: env.MY_PLUGIN_DEBUG || 'false',
MY_PLUGIN_ENABLED: env.MY_PLUGIN_ENABLED || 'true',
}
this.loaded = true
}
public getApiKey(): string | undefined {
return this.config.MY_PLUGIN_API_KEY
}
public getBaseUrl(): string {
return this.config.MY_PLUGIN_BASE_URL || 'https://api.example.com'
}
public isDebugEnabled(): boolean {
return this.config.MY_PLUGIN_DEBUG === 'true'
}
public isPluginEnabled(): boolean {
return this.config.MY_PLUGIN_ENABLED !== 'false'
}
}
export const pluginEnv = PluginEnvironment.getInstance()
Benefits:
Critical Step: Add the plugin to the plugin-sandbox theme for testing.
// contents/themes/plugin-sandbox/config/theme.config.ts
export const pluginSandboxThemeConfig: ThemeConfig = {
plugins: [
'my-plugin', // <-- Add your new plugin here
],
}
Then rebuild the registry:
node core/scripts/build/registry.mjs
# 1. Verify plugin structure
ls -la plugins/<plugin-name>/
# 2. Verify package.json has correct peerDependencies
cat plugins/<plugin-name>/package.json
# 3. Build registry to include new plugin
node core/scripts/build/registry.mjs
# 4. Verify plugin appears in registry
grep "<plugin-name>" core/lib/registries/plugin-registry.ts
# 5. Verify no duplicate dependencies
pnpm ls zod # Should show ONE version
# 6. Verify no TypeScript errors
pnpm tsc --noEmit
# 7. Test plugin activation (optional)
# Set NEXT_PUBLIC_ACTIVE_THEME=plugin-sandbox in .env.local
# Run: pnpm dev
Each entity requires 4 files:
| File | Purpose |
|------|---------|
| [entity].config.ts | Entity configuration |
| [entity].fields.ts | Field definitions |
| [entity].types.ts | TypeScript types |
| [entity].service.ts | Data access service |
Reference: core/templates/contents/themes/starter/entities/tasks/
peerDependenciesdependencies@nextsparkjs/plugin-<name>peerDependencies correctas (no duplicar core)node core/scripts/build/registry.mjspnpm ls zod shows ONE versionpnpm tsc --noEmit| Pattern | Why It's Wrong | Correct Approach |
|---------|----------------|------------------|
| Manual file creation | Missing files, wrong structure | Use pnpm create:plugin |
| Skipping sandbox registration | Can't test plugin | Add to plugin-sandbox theme |
| Skipping registry rebuild | Plugin won't be recognized | Run node core/scripts/build/registry.mjs |
| Modifying core files | Architecture violation | Only work in contents/plugins/ |
After scaffolding the plugin, continue with implementation:
lib/components/NEXT_PUBLIC_ACTIVE_THEME='plugin-sandbox'
monorepo-architecture skill - CRITICAL: Package hierarchy, dependency rules, Model B distributionplugins skill - Plugin development patterns and dependency managementcore/docs/04-entities/core/docs/03-registry-system/development
Zod validation patterns for this Next.js application. Covers schema definition, API validation, form integration, error formatting, and type inference. Use this skill when implementing validation for APIs, forms, or entity schemas.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
testing
Test coverage metrics and registry system for this Next.js application. Covers FEATURE_REGISTRY, FLOW_REGISTRY, TAGS_REGISTRY, and coverage metrics interpretation. Use this skill when evaluating test coverage, identifying gaps, or planning testing priorities.
development
TanStack Query (React Query) patterns for data fetching in this Next.js application. Covers useQuery, useMutation, optimistic updates, cache invalidation, and anti-patterns. Use this skill when implementing data fetching or state management with server data.