feat(marketplace): command consolidation + 8 new plugins (v8.1.0 → v9.0.0) [BREAKING]

Phase 1b: Rename all ~94 commands across 12 plugins to /<noun> <action>
sub-command pattern. Git-flow consolidated from 8→5 commands (commit
variants absorbed into --push/--merge/--sync flags). Dispatch files,
name: frontmatter, and cross-reference updates for all plugins.

Phase 2: Design documents for 8 new plugins in docs/designs/.

Phase 3: Scaffold 8 new plugins — saas-api-platform, saas-db-migrate,
saas-react-platform, saas-test-pilot, data-seed, ops-release-manager,
ops-deploy-pipeline, debug-mcp. Each with plugin.json, commands, agents,
skills, README, and claude-md-integration. Marketplace grows from 12→20.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 14:52:11 -05:00
parent 5098422858
commit 2d51df7a42
321 changed files with 13582 additions and 1019 deletions

View File

@@ -0,0 +1,134 @@
---
name: component-patterns
description: Component structure conventions including functional components, prop typing, exports, and co-located tests
---
# Component Patterns
## Purpose
Define standard patterns for React component scaffolding. This skill ensures all generated components follow consistent structure, typing, export conventions, and test co-location.
---
## Component File Structure
Every component file follows this order:
```typescript
// 1. Imports (external first, then internal, then styles)
import { type FC } from 'react';
import { Button } from '@/components/ui/Button';
import styles from './ComponentName.module.css';
// 2. Types (inline for simple, separate file for complex)
interface ComponentNameProps {
title: string;
onAction: () => void;
children?: React.ReactNode;
}
// 3. Component definition
/**
* Brief description of what this component does.
*
* @component
* @example
* <ComponentName title="Hello" onAction={() => console.log('clicked')} />
*/
const ComponentName: FC<ComponentNameProps> = ({ title, onAction, children }) => {
return (
<div>
<h2>{title}</h2>
{children}
<button onClick={onAction}>Action</button>
</div>
);
};
// 4. Display name (for DevTools)
ComponentName.displayName = 'ComponentName';
// 5. Export
export default ComponentName;
```
## Component Type Templates
### UI Component (presentational)
- Props in, JSX out — no side effects, no data fetching
- Pure function: same props always produce same output
- Accept `className` prop for style override flexibility
- Accept `children` if component is a container/wrapper
### Page Component
- Includes data fetching (server component in App Router, `useEffect` in client)
- Loading state with skeleton placeholder
- Error state with retry action
- `'use client'` directive only if client interactivity required (App Router)
### Layout Component
- Accepts `children: React.ReactNode` as required prop
- Optional slot props for sidebar, header, footer
- Handles responsive behavior
- Wraps with error boundary
### Form Component
- Controlled inputs with `useState` or form library (`react-hook-form`)
- Typed form values interface
- Validation schema (Zod recommended)
- Submit handler with loading state
- Error display per field and form-level
## Test Co-location Patterns
Test file sits next to component file:
```
src/components/Button/
Button.tsx
Button.test.tsx
Button.module.css (if CSS Modules)
index.ts (barrel file)
```
### Minimum Test Coverage
```typescript
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ComponentName from './ComponentName';
describe('ComponentName', () => {
it('renders without crashing', () => {
render(<ComponentName title="Test" onAction={() => {}} />);
expect(screen.getByText('Test')).toBeInTheDocument();
});
it('calls onAction when button clicked', async () => {
const onAction = vi.fn();
render(<ComponentName title="Test" onAction={onAction} />);
await userEvent.click(screen.getByRole('button'));
expect(onAction).toHaveBeenCalledOnce();
});
});
```
## Barrel File Convention
Each component directory exports through `index.ts`:
```typescript
export { default as ComponentName } from './ComponentName';
export type { ComponentNameProps } from './ComponentName';
```
## Anti-Patterns to Avoid
| Pattern | Why | Alternative |
|---------|-----|-------------|
| Class components | Legacy API, verbose | Functional components + hooks |
| `React.FC` with children | Children always optional, incorrect type narrowing | Explicit `children` prop in interface |
| Prop spreading `{...props}` | Obscures expected interface | Explicitly destructure needed props |
| `useEffect` for derived state | Unnecessary render cycle | Compute during render or `useMemo` |
| `forwardRef` without `displayName` | Unnamed in DevTools | Always set `displayName` |

View File

@@ -0,0 +1,96 @@
---
name: framework-detection
description: Detect React framework, TypeScript configuration, routing structure, and project conventions
---
# Framework Detection
## Purpose
Analyze a project's `package.json`, configuration files, and directory structure to determine the React framework, TypeScript usage, routing pattern, and CSS approach. This skill is loaded at the start of setup and routing commands to adapt output to the project's specific toolchain.
---
## Framework Detection Rules
Check `package.json` dependencies and devDependencies in this order (first match wins):
| Framework | Detection Criteria | Routing |
|-----------|-------------------|---------|
| **Next.js (App Router)** | `next` in deps + `app/` directory exists | File-based (`app/page.tsx`) |
| **Next.js (Pages Router)** | `next` in deps + `pages/` directory exists (no `app/`) | File-based (`pages/index.tsx`) |
| **Remix** | `@remix-run/react` in deps | File-based with loaders (`routes/`) |
| **Vite + React** | `vite` in deps + `@vitejs/plugin-react` | Client-side (react-router or tanstack-router) |
| **Create React App** | `react-scripts` in deps | Client-side (react-router) |
| **Gatsby** | `gatsby` in deps | File-based (`src/pages/`) |
## TypeScript Detection
| Signal | Conclusion |
|--------|------------|
| `tsconfig.json` exists | TypeScript project |
| `typescript` in devDependencies | TypeScript project |
| `.tsx` files in `src/` or `app/` | TypeScript project |
| None of the above | JavaScript project — generate `.jsx` files |
## CSS Approach Detection
Check in order:
| Signal | Approach |
|--------|----------|
| `tailwindcss` in deps + `tailwind.config.*` | Tailwind CSS |
| `*.module.css` or `*.module.scss` files exist | CSS Modules |
| `styled-components` in deps | styled-components |
| `@emotion/react` in deps | Emotion |
| `vanilla-extract` in deps | Vanilla Extract |
| None detected | Plain CSS or inline styles |
## Test Runner Detection
| Signal | Runner |
|--------|--------|
| `vitest` in devDependencies | Vitest |
| `jest` in devDependencies or `jest.config.*` exists | Jest |
| `@testing-library/react` in deps | Testing Library (works with both) |
| `cypress` in deps | Cypress (E2E, not unit) |
## State Management Detection
| Signal | Library |
|--------|---------|
| `zustand` in dependencies | Zustand |
| `@reduxjs/toolkit` in dependencies | Redux Toolkit |
| `recoil` in dependencies | Recoil |
| `jotai` in dependencies | Jotai |
| `mobx-react` in dependencies | MobX |
| Files with `createContext` + `useReducer` pattern | React Context (built-in) |
## Directory Structure Patterns
Common patterns to detect and respect:
| Pattern | Typical Path | Detection |
|---------|-------------|-----------|
| Feature-based | `src/features/<feature>/components/` | `features/` directory with subdirectories |
| Component-based | `src/components/<Component>/` | `components/` with PascalCase subdirectories |
| Flat components | `src/components/*.tsx` | `components/` with files only, no subdirectories |
| Atomic design | `src/components/atoms/`, `molecules/`, `organisms/` | Atomic naming directories |
## Output
Store detected configuration as a reference object for other skills:
```json
{
"framework": "nextjs-app",
"typescript": true,
"css_approach": "tailwind",
"test_runner": "vitest",
"state_management": "zustand",
"component_dir": "src/components",
"pages_dir": "app",
"hooks_dir": "src/hooks",
"structure_pattern": "feature-based"
}
```

View File

@@ -0,0 +1,174 @@
---
name: routing-conventions
description: File-based routing (Next.js), react-router conventions, dynamic routes, layouts, and middleware
---
# Routing Conventions
## Purpose
Define routing patterns for each supported framework. This skill ensures route scaffolding produces the correct file structure, naming conventions, and framework-specific boilerplate.
---
## Next.js App Router (v13.4+)
### File Conventions
| File | Purpose |
|------|---------|
| `page.tsx` | Route UI — required to make segment publicly accessible |
| `layout.tsx` | Shared layout wrapping child pages — persists across navigations |
| `loading.tsx` | Loading UI shown while page is loading (Suspense boundary) |
| `error.tsx` | Error UI shown when page throws (must be client component) |
| `not-found.tsx` | 404 UI for segment |
| `route.ts` | API route handler (GET, POST, etc.) |
### Route Patterns
```
app/
page.tsx # /
about/page.tsx # /about
blog/page.tsx # /blog
blog/[slug]/page.tsx # /blog/:slug (dynamic)
dashboard/
layout.tsx # Shared dashboard layout
page.tsx # /dashboard
settings/page.tsx # /dashboard/settings
(marketing)/ # Route group (no URL segment)
pricing/page.tsx # /pricing
```
### Dynamic Routes
| Pattern | File Path | URL Match |
|---------|-----------|-----------|
| Dynamic segment | `[id]/page.tsx` | `/users/123` |
| Catch-all | `[...slug]/page.tsx` | `/docs/a/b/c` |
| Optional catch-all | `[[...slug]]/page.tsx` | `/docs` or `/docs/a/b` |
### Server vs Client Components
- Pages are Server Components by default
- Add `'use client'` directive only when using: `useState`, `useEffect`, `onClick`, browser APIs
- Pass data from server to client via props, not through context
## Next.js Pages Router (Legacy)
### File Conventions
```
pages/
index.tsx # /
about.tsx # /about
blog/index.tsx # /blog
blog/[slug].tsx # /blog/:slug
_app.tsx # App wrapper (layouts)
_document.tsx # HTML document customization
404.tsx # Custom 404 page
api/users.ts # API route: /api/users
```
### Data Fetching
| Method | When | Use Case |
|--------|------|----------|
| `getServerSideProps` | Every request | Dynamic data, auth-gated pages |
| `getStaticProps` | Build time | Blog posts, marketing pages |
| `getStaticPaths` | Build time | Dynamic routes with static generation |
## React Router (v6+)
### Route Definition
```typescript
// router.tsx
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <ErrorBoundary />,
children: [
{ index: true, element: <Home /> },
{
path: 'dashboard',
element: <Suspense fallback={<Loading />}><Dashboard /></Suspense>,
},
{
path: 'users/:id',
element: <UserProfile />,
loader: userLoader,
},
],
},
]);
```
### Layout Pattern
```typescript
// layouts/RootLayout.tsx
import { Outlet } from 'react-router-dom';
export function RootLayout() {
return (
<div>
<Header />
<main>
<Outlet />
</main>
<Footer />
</div>
);
}
```
## Protected Routes
### Pattern: Auth Guard Component
```typescript
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) return <LoadingSkeleton />;
if (!isAuthenticated) return <Navigate to="/login" replace />;
return <>{children}</>;
}
```
### App Router: Middleware
```typescript
// middleware.ts (project root)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('session');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = { matcher: ['/dashboard/:path*'] };
```
## Error Boundaries
Every page route should have an error boundary:
- App Router: `error.tsx` file in route segment (automatically client component)
- React Router: `errorElement` prop on route definition
- Fallback: Generic `ErrorBoundary` component wrapping page content
Include retry functionality and user-friendly error message. Log error details to console (placeholder for error reporting service).

View File

@@ -0,0 +1,203 @@
---
name: state-patterns
description: State management patterns — React Context for simple, Zustand for medium, Redux Toolkit for complex
---
# State Management Patterns
## Purpose
Guide state management decisions and provide scaffolding templates for React Context, Zustand, and Redux Toolkit. This skill helps select the right pattern based on complexity and generates consistent store implementations.
---
## Decision Framework
| Criteria | Context | Zustand | Redux Toolkit |
|----------|---------|---------|---------------|
| **Scope** | Single feature, few consumers | Multiple features, medium consumers | App-wide, many consumers |
| **Complexity** | Simple values (theme, locale, auth) | Medium (cart, form wizard, filters) | Complex (normalized entities, async workflows) |
| **Async logic** | Manual with `useEffect` | Built-in with async actions | `createAsyncThunk` with lifecycle |
| **DevTools** | None built-in | Optional middleware | Full Redux DevTools integration |
| **Dependencies** | None (built-in React) | ~2KB, zero config | ~12KB, more boilerplate |
| **Learning curve** | Low | Low | Medium-High |
### Quick Decision
- Need to share a simple value across a few components? **Context**
- Need a store with some async logic and moderate complexity? **Zustand**
- Need normalized state, middleware, complex async flows, or strict patterns? **Redux Toolkit**
## React Context Template
```typescript
// stores/auth-context.tsx
import { createContext, useContext, useReducer, type ReactNode } from 'react';
// State type
interface AuthState {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
}
// Action types
type AuthAction =
| { type: 'LOGIN'; payload: User }
| { type: 'LOGOUT' }
| { type: 'SET_LOADING'; payload: boolean };
// Initial state
const initialState: AuthState = {
user: null,
isAuthenticated: false,
isLoading: false,
};
// Reducer
function authReducer(state: AuthState, action: AuthAction): AuthState {
switch (action.type) {
case 'LOGIN':
return { ...state, user: action.payload, isAuthenticated: true, isLoading: false };
case 'LOGOUT':
return { ...state, user: null, isAuthenticated: false };
case 'SET_LOADING':
return { ...state, isLoading: action.payload };
default:
return state;
}
}
// Context
const AuthContext = createContext<{
state: AuthState;
dispatch: React.Dispatch<AuthAction>;
} | null>(null);
// Provider
export function AuthProvider({ children }: { children: ReactNode }) {
const [state, dispatch] = useReducer(authReducer, initialState);
return (
<AuthContext.Provider value={{ state, dispatch }}>
{children}
</AuthContext.Provider>
);
}
// Hook with validation
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
```
## Zustand Template
```typescript
// stores/cart-store.ts
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
interface CartState {
items: CartItem[];
addItem: (item: Omit<CartItem, 'quantity'>) => void;
removeItem: (id: string) => void;
clearCart: () => void;
totalPrice: () => number;
}
export const useCartStore = create<CartState>()(
devtools(
persist(
(set, get) => ({
items: [],
addItem: (item) => set((state) => {
const existing = state.items.find((i) => i.id === item.id);
if (existing) {
return { items: state.items.map((i) =>
i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
)};
}
return { items: [...state.items, { ...item, quantity: 1 }] };
}),
removeItem: (id) => set((state) => ({
items: state.items.filter((i) => i.id !== id),
})),
clearCart: () => set({ items: [] }),
totalPrice: () => get().items.reduce(
(sum, item) => sum + item.price * item.quantity, 0
),
}),
{ name: 'cart-storage' }
)
)
);
```
## Redux Toolkit Template
```typescript
// store/slices/productsSlice.ts
import { createSlice, createAsyncThunk, type PayloadAction } from '@reduxjs/toolkit';
// Async thunk
export const fetchProducts = createAsyncThunk(
'products/fetchAll',
async (_, { rejectWithValue }) => {
try {
const response = await fetch('/api/products');
return await response.json();
} catch (error) {
return rejectWithValue('Failed to fetch products');
}
}
);
// Slice
const productsSlice = createSlice({
name: 'products',
initialState: {
items: [] as Product[],
status: 'idle' as 'idle' | 'loading' | 'succeeded' | 'failed',
error: null as string | null,
},
reducers: {
updateProduct: (state, action: PayloadAction<Product>) => {
const index = state.items.findIndex((p) => p.id === action.payload.id);
if (index !== -1) state.items[index] = action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(fetchProducts.pending, (state) => { state.status = 'loading'; })
.addCase(fetchProducts.fulfilled, (state, action) => {
state.status = 'succeeded';
state.items = action.payload;
})
.addCase(fetchProducts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.payload as string;
});
},
});
export const { updateProduct } = productsSlice.actions;
export default productsSlice.reducer;
```
## When NOT to Use Global State
- Form input values (use local `useState` or `react-hook-form`)
- UI toggle state (modal open/close) unless shared across routes
- Computed values derivable from existing state (compute inline or `useMemo`)
- Server cache data (use TanStack Query or SWR instead of Redux)

View File

@@ -0,0 +1,137 @@
---
name: typescript-patterns
description: Utility types, generics for components, discriminated unions for props, and strict null checks
---
# TypeScript Patterns for React
## Purpose
Define TypeScript patterns specific to React component development. This skill ensures generated code uses idiomatic TypeScript with proper generic constraints, discriminated unions, and utility types.
---
## Props Interface Conventions
### Basic Props
```typescript
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary' | 'ghost';
disabled?: boolean;
className?: string;
}
```
### Props with Children
```typescript
interface CardProps {
title: string;
children: React.ReactNode; // Explicit, not via FC
}
```
### Discriminated Union Props
Use when a component has mutually exclusive modes:
```typescript
type AlertProps =
| { variant: 'success'; message: string }
| { variant: 'error'; message: string; retry: () => void }
| { variant: 'loading'; progress?: number };
```
### Extending HTML Element Props
```typescript
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
label: string;
error?: string;
size?: 'sm' | 'md' | 'lg'; // Custom size, not HTML size
}
```
## Generic Component Patterns
### Generic List Component
```typescript
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string;
emptyMessage?: string;
}
function List<T>({ items, renderItem, keyExtractor, emptyMessage }: ListProps<T>) {
if (items.length === 0) return <p>{emptyMessage ?? 'No items'}</p>;
return <ul>{items.map((item, i) => <li key={keyExtractor(item)}>{renderItem(item, i)}</li>)}</ul>;
}
```
### Generic Hook
```typescript
function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {
// Implementation
}
```
## Utility Types for React
| Type | Use Case | Example |
|------|----------|---------|
| `React.ReactNode` | Any renderable content | `children: React.ReactNode` |
| `React.ReactElement` | JSX element only (not string/number) | `icon: React.ReactElement` |
| `React.ComponentPropsWithRef<'div'>` | All div props including ref | Extending native elements |
| `React.MouseEventHandler<HTMLButtonElement>` | Typed event handler | `onClick: React.MouseEventHandler<HTMLButtonElement>` |
| `React.ChangeEvent<HTMLInputElement>` | Input change event | `(e: React.ChangeEvent<HTMLInputElement>) => void` |
| `React.FormEvent<HTMLFormElement>` | Form submit event | `onSubmit: React.FormEventHandler<HTMLFormElement>` |
| `React.CSSProperties` | Inline style object | `style?: React.CSSProperties` |
## Common Utility Patterns
### Required Pick
Make specific properties required from an otherwise optional interface:
```typescript
type RequiredName = Required<Pick<UserProps, 'firstName' | 'lastName'>> & Omit<UserProps, 'firstName' | 'lastName'>;
```
### Extract Prop Types from Component
```typescript
type ButtonProps = React.ComponentProps<typeof Button>;
```
### Async State Pattern
```typescript
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
```
## Strict Null Checking Patterns
### Guard Hooks
```typescript
function useRequiredContext<T>(context: React.Context<T | null>, name: string): T {
const value = useContext(context);
if (value === null) throw new Error(`${name} must be used within its Provider`);
return value;
}
```
### Narrowing with Type Guards
```typescript
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'id' in value && 'email' in value;
}
```
## Things to Avoid
| Anti-Pattern | Why | Alternative |
|-------------|-----|-------------|
| `React.FC` | Implicit children, no generics | Explicit typed function |
| `any` for event handlers | Loses type safety | `React.MouseEvent<HTMLButtonElement>` |
| `as` for DOM queries | Runtime type mismatch risk | Type guards or `instanceof` |
| `!` non-null assertion | Hides potential null bugs | Conditional rendering or optional chaining |
| `enum` for prop variants | Not tree-shakeable, numeric by default | String union types |

View File

@@ -0,0 +1,28 @@
# Visual Header Skill
Standard visual header for saas-react-platform commands.
## Header Template
```
+----------------------------------------------------------------------+
| REACT-PLATFORM - [Context] |
+----------------------------------------------------------------------+
```
## Context Values by Command
| Command | Context |
|---------|---------|
| `/react setup` | Setup Wizard |
| `/react component` | Component |
| `/react route` | Route |
| `/react state` | State Management |
| `/react hook` | Custom Hook |
| `/react lint` | Lint |
| Agent mode (react-architect) | Architecture |
| Agent mode (react-auditor) | Audit |
## Usage
Display header at the start of every command response before proceeding with the operation.