# Scaffolder

> Generates boilerplate code following loaded rules. Creates new components, modules, APIs, and features that automatically comply with your coding standards. Extracts patterns from rules and applies them consistently.

- Skill: `diegosouzapw/scaffolder` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/scaffolder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/scaffolder/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/scaffolder

---


<identity>
Rule-Aware Scaffolder - Creates new code that automatically adheres to your project's coding standards.
</identity>

<capabilities>
- Creating new React/Vue/Angular components
- Adding new API endpoints or routes
- Setting up new modules or packages
- Generating test files for existing code
- Creating data models or database schemas
- Bootstrapping feature directories
</capabilities>

<instructions>
<execution_process>

### Step 1: Load Rule Index

Load the rule index to discover relevant rules dynamically:
- @.claude/context/rule-index.json

### Step 2: Identify Target Framework and Query Index

Determine which technologies apply based on what you're scaffolding:

**Component scaffolding**:
- Detect: React, Next.js, TypeScript
- Query: `index.technology_map['react']`, `index.technology_map['nextjs']`, `index.technology_map['typescript']`

**API Route scaffolding**:
- Detect: Next.js App Router or FastAPI
- Query: `index.technology_map['nextjs']` or `index.technology_map['fastapi']`

**Test File scaffolding**:
- Detect: Jest, Cypress, Playwright, Vitest, pytest
- Query: `index.technology_map['jest']`, `index.technology_map['cypress']`, etc.

**Database Model scaffolding**:
- Detect: Prisma, SQL, database patterns
- Query: `index.technology_map['prisma']` or database-related rules

### Step 3: Load Relevant Rules

Load only the relevant rule files from the index (progressive disclosure):
- Master rules first (from `.claude/rules-master/`)
- Archive rules supplement (from `.claude/archive/`)
- Load 3-5 most relevant rules, not all 1,081

### Step 4: Extract Patterns from Rules

Parse the loaded rule files to extract scaffolding patterns:

**From Next.js rules** (TECH_STACK_NEXTJS.md or nextjs.mdc):
- Server Components by default
- 'use client' only when needed
- Place in `app/` for routes, `components/` for shared
- Use lowercase-with-dashes for directories

**From TypeScript rules**:
- Interfaces for object shapes
- Proper return type annotations
- Avoid `any`, use `unknown`
- PascalCase for types/interfaces

**From React rules**:
- Functional components only
- Custom hooks for reusable logic
- Props interface for each component
- Error boundaries for critical sections

### Step 5: Generate Compliant Code

Apply extracted patterns to generate code that passes rule-auditor.
</execution_process>

<best_practices>
1. **Always Audit After**: Run `/audit` after scaffolding to catch any edge cases
2. **Customize Templates**: Add project-specific patterns to rules for consistent generation
3. **Use for Consistency**: Scaffold even simple files to maintain team conventions
4. **Review Generated Code**: Scaffolded code is a starting point, not final implementation
5. **Keep Rules Updated**: As patterns evolve, update rules so scaffolder stays current
</best_practices>

<integration>
### Component Generation Flow

```
1. User: /scaffold component UserDashboard
2. Scaffolder reads: nextjs.mdc, typescript.mdc, react.mdc
3. Extracts patterns: Server Component, Suspense, interfaces
4. Generates compliant code structure
5. Writes files to correct locations
6. Runs rule-auditor to verify compliance
7. Reports any manual adjustments needed
```

### Feature Module Generation

For larger features, scaffold generates a complete module:

```
/scaffold feature user-management

Generates:
app/
└── (dashboard)/
    └── users/
        ├── page.tsx           # List page
        ├── [id]/
        │   └── page.tsx       # Detail page
        ├── new/
        │   └── page.tsx       # Create page
        └── components/
            ├── user-list.tsx
            ├── user-card.tsx
            └── user-form.tsx
components/
└── users/
    └── ... (shared components)
lib/
└── users/
    ├── api.ts                 # API functions
    ├── types.ts               # Type definitions
    └── validations.ts         # Zod schemas
```
</integration>
</instructions>

<examples>
<code_example>
**Next.js Server Component**

**Command**: `/scaffold component UserProfile`

**Generated**: `components/user-profile/index.tsx`

```tsx
// Server Component (default per nextjs.mdc)
// Location: components/user-profile/ (lowercase-with-dashes per nextjs.mdc)

import { Suspense } from 'react'
import { UserProfileSkeleton } from './skeleton'
import { UserProfileContent } from './content'

// Interface defined (per typescript.mdc)
interface UserProfileProps {
  userId: string
  showDetails?: boolean
}

// Async Server Component for data fetching (per nextjs.mdc > Data Fetching)
export async function UserProfile({ userId, showDetails = false }: UserProfileProps) {
  return (
    // Suspense boundary (per nextjs.mdc > Components)
    <Suspense fallback={<UserProfileSkeleton />}>
      <UserProfileContent userId={userId} showDetails={showDetails} />
    </Suspense>
  )
}

// Default export for dynamic imports (per nextjs.mdc)
export default UserProfile
```

**Also generates**:
- `components/user-profile/content.tsx` - Async content component
- `components/user-profile/skeleton.tsx` - Loading skeleton
- `components/user-profile/types.ts` - Shared types
- `components/user-profile/index.ts` - Barrel export
</code_example>

<code_example>
**Next.js Client Component**

**Command**: `/scaffold client-component SearchBar`

**Generated**: `components/search-bar/index.tsx`

```tsx
'use client'  // Required for useState (per nextjs.mdc > Components)

import { useState, useCallback } from 'react'
import { useDebounce } from '@/hooks/use-debounce'

// Props interface (per typescript.mdc)
interface SearchBarProps {
  onSearch: (query: string) => void
  placeholder?: string
  debounceMs?: number
}

// Functional component (per react.mdc)
export function SearchBar({
  onSearch,
  placeholder = 'Search...',
  debounceMs = 300,
}: SearchBarProps) {
  // Minimal client state (per nextjs.mdc > State Management)
  const [query, setQuery] = useState('')

  // Debounced callback (per nextjs.mdc > Performance)
  const debouncedSearch = useDebounce(onSearch, debounceMs)

  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value
    setQuery(value)
    debouncedSearch(value)
  }, [debouncedSearch])

  return (
    <input
      type="search"
      value={query}
      onChange={handleChange}
      placeholder={placeholder}
      className="w-full px-4 py-2 border rounded-lg"  // Tailwind (per tailwind.mdc)
      aria-label={placeholder}  // Accessibility
    />
  )
}

export default SearchBar
```
</code_example>

<code_example>
**Next.js API Route (App Router)**

**Command**: `/scaffold api users`

**Generated**: `app/api/users/route.ts`

```tsx
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'  // Zod for validation (per nextjs.mdc > Forms and Validation)

// Request schema (per typescript.mdc > Type System)
const CreateUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
})

// Response type (per typescript.mdc)
interface UserResponse {
  id: string
  email: string
  name: string
  createdAt: string
}

// GET handler (per nextjs.mdc > Routing)
export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url)
    const page = parseInt(searchParams.get('page') ?? '1')
    const limit = parseInt(searchParams.get('limit') ?? '10')

    // TODO: Replace with actual database query
    const users: UserResponse[] = []

    return NextResponse.json({
      data: users,
      pagination: { page, limit, total: 0 },
    })
  } catch (error) {
    // Proper error handling (per nextjs.mdc > Data Fetching)
    console.error('Failed to fetch users:', error)
    return NextResponse.json(
      { error: 'Failed to fetch users' },
      { status: 500 }
    )
  }
}

// POST handler with validation
export async function POST(request: NextRequest) {
  try {
    const body = await request.json()

    // Server-side validation (per nextjs.mdc > Forms and Validation)
    const validated = CreateUserSchema.parse(body)

    // TODO: Replace with actual database insert
    const user: UserResponse = {
      id: crypto.randomUUID(),
      ...validated,
      createdAt: new Date().toISOString(),
    }

    return NextResponse.json({ data: user }, { status: 201 })
  } catch (error) {
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        { error: 'Validation failed', details: error.errors },
        { status: 400 }
      )
    }
    console.error('Failed to create user:', error)
    return NextResponse.json(
      { error: 'Failed to create user' },
      { status: 500 }
    )
  }
}
```
</code_example>

<code_example>
**FastAPI Endpoint**

**Command**: `/scaffold fastapi-route users`

**Generated**: `app/routers/users.py`

```python
"""User management endpoints."""
# Type hints required (per python.mdc)
from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, EmailStr, Field

# Router with tags (per fastapi.mdc > API Design)
router = APIRouter(prefix="/users", tags=["users"])


# Pydantic models (per fastapi.mdc > Components and Validation)
class UserCreate(BaseModel):
    """Schema for creating a user."""
    email: EmailStr
    name: str = Field(..., min_length=2, max_length=100)


class UserResponse(BaseModel):
    """Schema for user response."""
    id: UUID
    email: EmailStr
    name: str
    created_at: str

    class Config:
        from_attributes = True


class PaginatedResponse(BaseModel):
    """Paginated response wrapper."""
    data: list[UserResponse]
    total: int
    page: int
    limit: int


# Dependency injection (per fastapi.mdc > Dependency Injection)
async def get_db():
    """Database session dependency."""
    # TODO: Replace with actual database session
    yield None


# GET endpoint with pagination (per fastapi.mdc > Performance)
@router.get("", response_model=PaginatedResponse)
async def list_users(
    db: Annotated[None, Depends(get_db)],
    page: Annotated[int, Query(ge=1)] = 1,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
) -> PaginatedResponse:
    """
    List all users with pagination.

    - **page**: Page number (starting from 1)
    - **limit**: Items per page (max 100)
    """
    # TODO: Replace with actual database query
    users: list[UserResponse] = []
    total = 0

    return PaginatedResponse(data=users, total=total, page=page, limit=limit)


# POST endpoint with validation
@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(
    user_data: UserCreate,
    db: Annotated[None, Depends(get_db)],
) -> UserResponse:
    """
    Create a new user.

    - **email**: Valid email address
    - **name**: User's display name (2-100 chars)
    """
    # TODO: Replace with actual database insert
    # Check for existing user, create, return

    raise HTTPException(
        status_code=status.HTTP_501_NOT_IMPLEMENTED,
        detail="Database integration pending",
    )


# GET by ID endpoint
@router.get("/{user_id}", response_model=UserResponse)
async def get_user(
    user_id: UUID,
    db: Annotated[None, Depends(get_db)],
) -> UserResponse:
    """Get a specific user by ID."""
    # TODO: Replace with actual database query
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail=f"User {user_id} not found",
    )
```
</code_example>

<code_example>
**Test File (Vitest/Jest)**

**Command**: `/scaffold test components/user-profile`

**Generated**: `components/user-profile/__tests__/index.test.tsx`

```tsx
// Test file (per jest-*.mdc / vitest-*.mdc patterns)
import { render, screen, waitFor } from '@testing-library/react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { UserProfile } from '../index'

// Mock external dependencies (per testing best practices)
vi.mock('@/lib/api', () => ({
  fetchUser: vi.fn(),
}))

describe('UserProfile', () => {
  // Clear mocks before each test (per clean-code.mdc)
  beforeEach(() => {
    vi.clearAllMocks()
  })

  // Descriptive test names (per testing guidelines)
  it('renders loading skeleton initially', () => {
    render(<UserProfile userId="123" />)

    expect(screen.getByTestId('user-profile-skeleton')).toBeInTheDocument()
  })

  it('displays user information after loading', async () => {
    const mockUser = {
      id: '123',
      name: 'John Doe',
      email: 'john@example.com',
    }

    const { fetchUser } = await import('@/lib/api')
    vi.mocked(fetchUser).mockResolvedValue(mockUser)

    render(<UserProfile userId="123" />)

    await waitFor(() => {
      expect(screen.getByText('John Doe')).toBeInTheDocument()
      expect(screen.getByText('john@example.com')).toBeInTheDocument()
    })
  })

  it('handles error state gracefully', async () => {
    const { fetchUser } = await import('@/lib/api')
    vi.mocked(fetchUser).mockRejectedValue(new Error('Network error'))

    render(<UserProfile userId="123" />)

    await waitFor(() => {
      expect(screen.getByText(/failed to load/i)).toBeInTheDocument()
    })
  })

  // Edge cases (per qa best practices)
  it('shows details when showDetails prop is true', async () => {
    render(<UserProfile userId="123" showDetails />)

    await waitFor(() => {
      expect(screen.getByTestId('user-details-section')).toBeInTheDocument()
    })
  })
})
```
</code_example>

<code_example>
**Cypress E2E Test**

**Command**: `/scaffold e2e-test user-flow`

**Generated**: `cypress/e2e/user-flow.cy.ts`

```typescript
// E2E test (per cypress-e2e-testing-*.mdc)
describe('User Flow', () => {
  // Setup before tests (per cypress best practices)
  beforeEach(() => {
    // Reset state and seed data
    cy.task('db:seed')
    cy.visit('/')
  })

  // Critical user flow (per cypress-e2e-testing guidelines)
  it('allows user to sign up, login, and view profile', () => {
    // Use data-testid selectors (per cypress best practices)
    cy.get('[data-testid="signup-link"]').click()

    // Fill signup form
    cy.get('[data-testid="email-input"]').type('test@example.com')
    cy.get('[data-testid="password-input"]').type('SecurePass123!')
    cy.get('[data-testid="name-input"]').type('Test User')
    cy.get('[data-testid="signup-submit"]').click()

    // Verify redirect to dashboard
    cy.url().should('include', '/dashboard')

    // Navigate to profile
    cy.get('[data-testid="profile-link"]').click()

    // Verify profile data
    cy.get('[data-testid="profile-name"]').should('contain', 'Test User')
    cy.get('[data-testid="profile-email"]').should('contain', 'test@example.com')
  })

  // API mocking example (per cypress-api-testing guidelines)
  it('handles API errors gracefully', () => {
    // Mock API failure
    cy.intercept('GET', '/api/users/*', {
      statusCode: 500,
      body: { error: 'Internal server error' },
    }).as('getUserError')

    cy.visit('/profile')

    // Wait for mocked request
    cy.wait('@getUserError')

    // Verify error handling
    cy.get('[data-testid="error-message"]')
      .should('be.visible')
      .and('contain', 'Failed to load profile')

    // Verify retry option
    cy.get('[data-testid="retry-button"]').should('be.visible')
  })
})
```
</code_example>

<usage_example>
**Quick Commands**:

```
# Generate a Server Component
/scaffold component MyComponent

# Generate a Client Component
/scaffold client-component MyInteractiveWidget

# Generate an API route
/scaffold api resource-name

# Generate a FastAPI router
/scaffold fastapi-route resource-name

# Generate test for existing file
/scaffold test path/to/component

# Generate E2E test
/scaffold e2e-test flow-name

# Generate with specific rules
/scaffold component MyComponent --rules nextjs,typescript

# Generate in specific location
/scaffold component MyComponent --path src/features/auth

# List available scaffold templates
/scaffold --list
```

**Available Templates**:

| Template | Framework | Files Generated |
|----------|-----------|-----------------|
| `component` | Next.js/React | index.tsx, types.ts, skeleton.tsx |
| `client-component` | Next.js | index.tsx with 'use client' |
| `page` | Next.js App Router | page.tsx, loading.tsx, error.tsx |
| `api` | Next.js App Router | route.ts with handlers |
| `fastapi-route` | FastAPI | router file with endpoints |
| `hook` | React | Custom hook with types |
| `context` | React | Context provider + hook |
| `test` | Jest/Vitest | Test file for component |
| `e2e-test` | Cypress/Playwright | E2E test spec |
| `model` | Prisma | Schema model |
| `migration` | Database | Migration file |
</usage_example>
</examples>

