.claude/skills/plugins/SKILL.md
Plugin development system for this Next.js application. Covers plugin structure, configuration, lifecycle hooks, registry integration, environment variables, and testing patterns. CRITICAL: Includes MANDATORY dependency management rules for NPM distribution. Use this skill when creating, modifying, or validating plugins.
npx skillsauth add NextSpark-js/nextspark pluginsInstall 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.
Patterns for developing plugins in this Next.js application.
| Context | Plugin Location | Reason |
|---------|-----------------|--------|
| Monorepo (development) | plugins/<plugin-name>/ | Workspace package |
| Consumer project | contents/plugins/<plugin-name>/ | Copied by CLI (not node_modules) |
CRITICAL: NPM packages are for DISTRIBUTION. In consumer projects, code is COPIED to
/contents/plugins/, NOT kept innode_modules.
Plugin Structure (Monorepo):
plugins/
└── [plugin-name]/
├── package.json # REQUIRED: peerDependencies
├── plugin.config.ts # Plugin configuration (REQUIRED)
├── README.md # Documentation (REQUIRED)
├── .env.example # Environment template (REQUIRED)
├── types/ # TypeScript definitions
├── lib/ # Core logic
├── hooks/ # React hooks
├── components/ # React components
├── providers/ # Context providers
├── api/ # API endpoints
├── entities/ # Plugin entities (optional)
└── docs/ # Documentation
Plugin Types: utility | service | configuration
→ See references/plugin-types.md for detailed structures
If
@nextsparkjs/corehas a dependency, plugins MUST declare it aspeerDependency, NEVER asdependency.
❌ WRONG - Duplicate dependencies (FORBIDDEN):
┌─────────────────────────────────────────────────┐
│ node_modules/ │
│ ├── @nextsparkjs/core/ │
│ │ └── node_modules/[email protected] ← Instance 1 │
│ ├── @nextsparkjs/plugin-ai/ │
│ │ └── node_modules/[email protected] ← Instance 2 │
│ └── @nextsparkjs/plugin-langchain/ │
│ └── node_modules/[email protected] ← Instance 3! │
└─────────────────────────────────────────────────┘
Result: Type conflicts, instanceof errors, bloated bundle
✅ CORRECT - Single instance (MANDATORY):
┌─────────────────────────────────────────────────┐
│ node_modules/ │
│ ├── [email protected] ← ONE single instance (hoisted) │
│ ├── @nextsparkjs/core/ (provides zod) │
│ ├── @nextsparkjs/plugin-ai/ (uses host's zod) │
│ └── @nextsparkjs/plugin-langchain/ (uses zod) │
└─────────────────────────────────────────────────┘
Result: No conflicts, optimized bundle
| Category | Type | Examples |
|----------|------|----------|
| Singleton libraries | peerDependencies | zod, react, react-dom, next |
| Shared with core | peerDependencies | @tanstack/react-query, lucide-react |
| Plugin-exclusive | dependencies | @ai-sdk/, @langchain/ |
{
"zod": "^4.1.5",
"@tanstack/react-query": "^5.85.0",
"lucide-react": "^0.539.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.1",
"date-fns": "^4.1.0",
"react-hook-form": "^7.62.0",
"sonner": "^2.0.7",
"next-intl": "^4.3.4",
"better-auth": "^1.3.5"
}
{
"name": "@nextsparkjs/plugin-NAME",
"version": "1.0.0",
"private": false,
"main": "./plugin.config.ts",
"dependencies": {
"@ai-sdk/anthropic": "^2.0.17" // ✅ Only THIS plugin uses it
},
"peerDependencies": {
"@nextsparkjs/core": "workspace:*",
"next": "^15.0.0",
"react": "^19.0.0",
"zod": "^4.0.0"
}
}
Every plugin MUST have a plugin.config.ts file:
import { z } from 'zod'
import type { PluginConfig } from '@/core/types/plugin'
const MyPluginConfigSchema = z.object({
apiKey: z.string().min(1),
timeout: z.number().min(1000).default(5000),
debugMode: z.boolean().default(false)
})
export const myPluginConfig: PluginConfig = {
name: 'my-plugin',
version: '1.0.0',
displayName: 'My Custom Plugin',
description: 'Clear description of plugin functionality',
enabled: true,
dependencies: [],
components: { MyComponent: undefined },
services: { useMyService: undefined },
hooks: {
async onLoad() { console.log('[My Plugin] Loading...') },
async onActivate() { console.log('[My Plugin] Activated') },
async onDeactivate() { console.log('[My Plugin] Deactivated') },
async onUnload() { console.log('[My Plugin] Unloaded') }
}
}
export default myPluginConfig
Use the core's centralized env-loader for automatic plugin .env loading:
// contents/plugins/my-plugin/lib/plugin-env.ts
import { getPluginEnv } from '@nextsparkjs/core/lib/plugins/env-loader'
const env = getPluginEnv('my-plugin')
const apiKey = env.MY_PLUGIN_API_KEY
const enabled = env.MY_PLUGIN_ENABLED === 'true'
.env (contents/plugins/my-plugin/.env) - Highest priority.env (project root) - FallbackSHARED Variables (root .env):
DATABASE_URL, BETTER_AUTH_SECRETANTHROPIC_API_KEY, OPENAI_API_KEY (can be shared across plugins)PLUGIN Variables (plugin .env with prefix):
MY_PLUGIN_* namespace# contents/plugins/my-plugin/.env.example
# Plugin-specific configuration
MY_PLUGIN_ENABLED=true
MY_PLUGIN_DEBUG=false
MY_PLUGIN_API_KEY=your-api-key-here
MY_PLUGIN_TIMEOUT=5000
| File | Purpose |
|------|---------|
| .env.example | Template with all variables documented |
| lib/plugin-env.ts | Type-safe wrapper using core's env-loader |
// core/lib/registries/plugin-registry.ts (AUTO-GENERATED)
export const PLUGIN_REGISTRY: Record<string, PluginRegistryEntry> = {
'my-plugin': {
name: 'my-plugin',
config: myPluginConfig,
hasAPI: true,
apiPath: '/api/plugin/my-plugin',
hasComponents: true
}
}
# After creating or modifying plugins
node core/scripts/build/registry.mjs
python .claude/skills/plugins/scripts/scaffold-plugin.py \
--name "my-plugin" \
--type "service" \
--features "components,hooks,api"
// ❌ NEVER: Put dependencies that core already has
{
"dependencies": {
"zod": "^4.0.0", // ❌ Core has it → peerDependency
"react": "^19.0.0" // ❌ Core has it → peerDependency
}
}
// ❌ NEVER: Put global variables in plugin .env
// USE_LOCAL_AI=true // WRONG - This overrides root .env!
// ❌ NEVER: Hardcode configuration
const config = { apiKey: 'hardcoded-key' }
// ❌ NEVER: Skip error handling in handlers
export async function badProcess(data: any) {
return await externalAPI(data) // No try/catch
}
// ❌ NEVER: Use any types
export interface BadInterface { data: any }
peerDependenciesdependenciesplugin.config.ts with all required fieldsREADME.md with usage documentation.env.example with namespaced variables onlylib/plugin-env.ts using core's env-loadertypes/ directoryMY_PLUGIN_* namespacedata-cy selectorsnode core/scripts/build/registry.mjspnpm buildpnpm ls zod # Should show ONE version only
references/plugin-types.md - Detailed plugin type structuresreferences/plugin-templates.md - Component, hook, API templatesreferences/plugin-testing.md - Testing patterns and POMsmonorepo-architecture - Package hierarchy and dependency rulescypress-selectors - Selector patterns for plugin componentstanstack-query - Data fetching in plugin hookszod-validation - Input validation for plugin APIsregistry-system - Plugin registry integrationdevelopment
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.