Skip to content

vt-c-bootstrap

Scaffold a new project with best-practice structure, configurations, and documentation. Use when starting a new project or adding toolkit structure to an existing folder.

Plugin: core-standards
Category: Project Setup
Command: /vt-c-bootstrap


Bootstrap Skill

Create a new project with industry best-practice structure, tooling, and documentation.

Invocation

# Interactive mode - prompts for all options
/vt-c-bootstrap

# With project type and name (angular-app is default)
/vt-c-bootstrap angular-app my-frontend
/vt-c-bootstrap typescript-api my-service
/vt-c-bootstrap rails-api my-app

Execution Instructions

When this skill is invoked, follow these steps:

Step 1: Gather Requirements

If arguments were not provided, use AskUserQuestion to gather:

  1. Project Type - Ask which type:
  2. angular-app - Angular SPA (Angular 21, PrimeNG, TypeScript, Vitest) ← default
  3. typescript-api - REST API service (Node.js, Express, TypeScript, Prisma)
  4. typescript-cli - Command-line tool (Node.js, TypeScript, Commander)
  5. rails-api - Rails API-only (Ruby, Rails, PostgreSQL)
  6. rails-full - Rails full-stack (Ruby, Rails, Hotwire, PostgreSQL)
  7. react-app - React SPA (React, TypeScript, Vite)
  8. nextjs-app - Next.js full-stack (Next.js, TypeScript, Tailwind)

  9. Project Name - The directory name (use current directory name if already in a project folder)

  10. Optional Features - Multi-select from:

  11. Docker support
  12. API documentation (OpenAPI)
  13. Authentication boilerplate
  14. Database migrations setup
  15. Error monitoring (Sentry)
  16. Feature flags setup

Step 2: Check Current Directory

Before creating structure: - If current directory is empty OR only contains PRD.md/Constitution files → use current directory - If project-name argument provided AND current directory is different → create new subdirectory - If .specify/ exists → preserve existing spec files

Step 3: Create Directory Structure

Create the following structure (adapt paths based on Step 2):

project-root/
├── 00-inbox/                # Drop zone for raw input (created if not present)
│   └── README.md
├── .github/
│   ├── workflows/
│   │   ├── ci.yml
│   │   └── deploy.yml
│   └── PULL_REQUEST_TEMPLATE.md
├── 01-docs/
│   ├── solutions/
│   │   └── patterns/
│   │       └── critical-patterns.md
│   ├── journal/
│   │   └── .gitkeep
│   ├── adr/
│   │   └── .gitkeep
│   └── runbooks/
│       └── .gitkeep
├── src/                     # Or app/ for Rails
├── tests/                   # Or spec/ for Rails
├── scripts/
├── .env.example
├── .gitignore
├── CLAUDE.md
└── README.md

Step 4: Create Configuration Files

Based on project type, create appropriate configs:

All Projects

.gitignore - Comprehensive ignore file for the stack (includes .env, .env.local, .env.*.local — SPEC-114 / FR-12) .env.example - Environment variables template with comments .claudeignore - Claude Code context exclusions (from templates/scaffold/base/.claudeignore). Enforced by the claudeignore-guard.sh PreToolUse hook — SPEC-114 / FR-17. Hides credentials, secret directories, and context-polluting artifacts from Claude's tool operations. CLAUDE.md - Project-specific instructions (see template below) README.md - Getting started documentation

Angular Projects (angular-app)

  • angular.json with zoneless change detection enabled ("zoneless": true in application builder options)
  • package.json with Angular 21, PrimeNG, PrimeIcons, PrimeFlex, Vitest, @angular-eslint
  • tsconfig.json with strict settings + strictTemplates: true
  • eslint.config.js with @angular-eslint
  • vitest.config.ts for Angular 21 zoneless testing
  • Feature-oriented directory structure: src/app/{core,shared,features}
  • PrimeNG preset file using definePreset() from @primeuix/themes based on Aura with VisiTrans design tokens
  • VisiTrans CSS variables file alongside PrimeNG tokens
  • app.config.ts with providePrimeNG() configuration

TypeScript Projects (typescript-api, typescript-cli, react-app, nextjs-app)

  • package.json with scripts and dependencies
  • tsconfig.json with strict settings
  • eslint.config.js or .eslintrc.js
  • prettier.config.js or .prettierrc
  • vitest.config.ts or jest.config.js

Rails Projects (rails-api, rails-full)

  • Gemfile with recommended gems
  • .rubocop.yml for Ruby style
  • Database configuration templates
  • strong_migrations initializer

Step 5: Create GitHub Workflows

.github/workflows/ci.yml:

name: vt-c-bootstrap

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup [Language]
        uses: [setup-action]
      - name: Install dependencies
        run: [install-command]
      - name: Lint
        run: [lint-command]
      - name: Test
        run: [test-command]

Step 5.5: Scan Inbox for Context

Before generating CLAUDE.md and README.md, check for existing inbox contents that can inform the generated files:

  1. Locate inbox: Check these paths in order: 00-inbox/, 01-docs/01-inbox/, docs/inbox/, docs/00-inbox/
  2. If inbox contains files (excluding README.md, .gitkeep):
  3. Read each document (up to 10 files, prioritizing .md then .txt then .pdf)
  4. Extract key themes, requirements, and stakeholder needs
  5. Use these as additional context when generating:
    • PRD.md: Incorporate project goals and requirements from inbox docs
    • CLAUDE.md (Step 6): Add project-specific context from inbox materials
    • README.md: Reflect the project's purpose as described in inbox documents
  6. Display: "Found [N] inbox documents. Using as context for project setup."
  7. If inbox is empty or missing: Proceed normally (no change to current behavior)
  8. NEVER move, delete, or modify inbox documents during bootstrap

Step 6: Create CLAUDE.md

Use this template, filled with project-specific values:

# CLAUDE.md

## Project Overview
[Brief description based on PRD if available]

## Tech Stack
- [Framework from project type]
- [Database if applicable]
- [Key libraries]

## Development Commands
\`\`\`bash
# Start development server
[npm run dev / rails s / etc.]

# Run tests
[npm test / rspec / etc.]

# Run linter
[npm run lint / rubocop / etc.]
\`\`\`

## Architecture
[Based on project type - API routes, components, etc.]

## Key Files
- `src/index.ts` - Application entry point
- `src/routes/` - API endpoints
- `src/models/` - Data models

## Standards
Global standards from ~/.claude/CLAUDE.md apply automatically.
Project-specific conventions are documented below.

## Conventions
- Follow [TypeScript/Ruby] naming conventions
- Tests required for new features
- PR reviews required before merge

## What NOT to Change
<!--
This section is read by /simplify to prevent false positives.
List code patterns that look like simplification candidates but must be preserved.
Be specific: /simplify cannot distinguish intentional complexity from accidental complexity.

Categories to consider:
  - Defensive code that looks redundant (null checks for external API responses)
  - Code separated for future divergence (intentionally duplicated logic)
  - Performance-critical code written imperatively for speed
  - Debugging artifacts (named variables that exist for debugger breakpoints)
  - Domain-specific patterns that conflict with generic best practices
-->
- Intentional defensive null checks (e.g., API response validation, external webhook handling)
- Error boundary components and fallback UI
- JSDoc/TSDoc comments on public APIs
- Security-related code patterns (input sanitization, auth guards)
- Regulatory compliance checks and data validation patterns
- Named function expressions kept for stack trace readability
- [Add project-specific items here]

Step 7: Create Toolkit Integration Files

01-docs/solutions/patterns/critical-patterns.md:

# Critical Patterns

This file contains patterns that must be checked during code review and bug investigation.

## Patterns

*No patterns recorded yet. Patterns will be added as bugs are fixed and solutions documented.*

## How This File Grows

1. Fix a bug using `/vt-c-investigate-bug`
2. Document the solution with `/vt-c-compound`
3. If the bug represents a recurring pattern, it gets added here
4. All orchestrators check this file before starting work

Step 8: Handle Existing Files

If the directory already contains: - PRD.md → Keep it, reference in CLAUDE.md - Constitution → Move to .specify/memory/constitution.md if not already there - spec.md → Move to active spec's specs/[N]-feature/spec.md if not already there - 00-inbox/ → Keep it and all contents, do not overwrite README.md if present - Legacy inbox (01-docs/01-inbox/, docs/inbox/, docs/00-inbox/) → Keep, do not migrate. Note: "Standard inbox location is 00-inbox/ at project root."

Step 9: Initialize Git (if not already)

# Only if .git doesn't exist
git init

Step 10: Initialize Git LFS (if governed)

If .repo-manifest.yaml exists and defines binary_rules:

# Check if git lfs is available
command -v git-lfs &>/dev/null

# If available and not already initialized:
git lfs install

# Ensure .gitattributes has LFS rules for declared extensions
# (Only add rules not already present)

This catches the case where governance was set up during /vt-c-scaffold but /vt-c-1-bootstrap adds a tech stack later that may produce binary artifacts.

Step 11: Install Git Hooks

Install the pre-commit hook to enforce branch-based workflow:

~/.claude/hooks/vt-c-install-hooks.sh

This installs the pre-commit hook that: - Blocks direct commits to main and master - Enforces feature branch workflow - Provides helpful error messages with branch naming suggestions

Step 12: Provide Next Steps

Output to user:

Project scaffolded successfully!

Next steps:
1. cd [project-name] (if new directory created)
2. [npm ci --ignore-scripts / bundle install / etc.]
3. npm audit --audit-level=high  (for npm projects)
4. cp .env.example .env
5. [npm run dev / rails s / etc.]

Toolkit integration ready:
- /vt-c-wf-plan - Create implementation plan
- /vt-c-wf-review - Run code review
- /vt-c-journal - Capture learnings
- /vt-c-finalize-check - Pre-merge verification

Your project is ready!

File Templates

TypeScript API - package.json

{
  "name": "{{PROJECT_NAME}}",
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js",
    "test": "vitest",
    "lint": "eslint src/",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "express": "^4.18.2",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^20.10.0",
    "eslint": "^8.55.0",
    "prettier": "^3.1.0",
    "tsx": "^4.6.0",
    "typescript": "^5.3.0",
    "vitest": "^1.0.0"
  }
}

TypeScript API - tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Rails API - Gemfile additions

# Add to Gemfile
gem 'strong_migrations'
gem 'rack-cors'
gem 'oj' # Fast JSON

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'rubocop', require: false
  gem 'rubocop-rails', require: false
  gem 'rubocop-rspec', require: false
end

Angular App - package.json

{
  "name": "{{PROJECT_NAME}}",
  "version": "0.1.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "vitest",
    "test:watch": "vitest --watch",
    "lint": "ng lint",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@angular/animations": "^21.0.0",
    "@angular/common": "^21.0.0",
    "@angular/compiler": "^21.0.0",
    "@angular/core": "^21.0.0",
    "@angular/forms": "^21.0.0",
    "@angular/platform-browser": "^21.0.0",
    "@angular/router": "^21.0.0",
    "primeng": "^20.0.0",
    "primeicons": "^7.0.0",
    "primeflex": "^4.0.0",
    "@primeuix/themes": "^2.0.0",
    "rxjs": "^7.8.0",
    "zone.js": "^0.15.0"
  },
  "devDependencies": {
    "@angular/build": "^21.0.0",
    "@angular/cli": "^21.0.0",
    "@angular/compiler-cli": "^21.0.0",
    "@angular-eslint/builder": "^19.0.0",
    "@angular-eslint/eslint-plugin": "^19.0.0",
    "@angular-eslint/eslint-plugin-template": "^19.0.0",
    "@angular-eslint/template-parser": "^19.0.0",
    "eslint": "^9.0.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",
    "@analogjs/vitest-angular": "^1.0.0"
  }
}

Note: zone.js is retained as a polyfill fallback. The app uses zoneless change detection (see angular.json "zoneless": true).

Angular App - tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true,
    "sourceMap": true,
    "declaration": false,
    "experimentalDecorators": true,
    "moduleResolution": "bundler",
    "target": "ES2022",
    "module": "ES2022",
    "lib": ["ES2022", "dom"],
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

Angular App - angular.json (key sections)

{
  "projects": {
    "{{PROJECT_NAME}}": {
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "options": {
            "zoneless": true,
            "outputPath": "dist/{{PROJECT_NAME}}",
            "index": "src/index.html",
            "browser": "src/main.ts",
            "tsConfig": "tsconfig.app.json",
            "styles": ["src/styles.css"]
          }
        }
      }
    }
  }
}

Angular App - app.config.ts

// src/app/app.config.ts
import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import { vtPreset } from './core/theme/vt-preset';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZonelessChangeDetection(),
    provideRouter(routes),
    provideHttpClient(withInterceptorsFromDi()),
    provideAnimationsAsync(),
    providePrimeNG({
      theme: {
        preset: vtPreset,
        options: {
          darkModeSelector: false,
        },
      },
    }),
  ],
};

Angular App - Design System Setup (Generated)

Generate VisiTrans design system files from the canonical token source instead of copy-pasting templates:

# PrimeNG preset
python3 TOOLKIT_ROOT/scripts/design_system/generate_primeng_preset.py --product visitrans --output src/app/core/theme/vt-preset.ts

# CSS variables
python3 TOOLKIT_ROOT/scripts/design_system/generate_css_variables.py --product visitrans --output src/styles/vt-variables.css

The generators read from TOOLKIT_ROOT/configs/visitrans-cd-tokens.yaml (single source of truth). Use --product visimatch|visifair|visiarea for product-specific themes.

Angular App - VisiTrans PrimeNG Preset (Reference Template)

// src/app/core/theme/vt-preset.ts
import { definePreset } from '@primeuix/themes';
import Aura from '@primeuix/themes/aura';

export const vtPreset = definePreset(Aura, {
  primitive: {
    vtOrange: {
      50: '#FFF8E6',
      100: '#FFECB3',
      200: '#FFE080',
      300: '#FFD44D',
      400: '#FFC826',
      500: '#FC9E00',
      600: '#E08C00',
      700: '#CC5200',
      800: '#993D00',
      900: '#662900',
      950: '#331400',
    },
    vtGray: {
      50: '#F5F5F5',
      100: '#E6E6E6',
      200: '#D4D4D4',
      300: '#B8B8B8',
      400: '#A1A1A1',
      500: '#8A8A8A',
      600: '#737373',
      700: '#5C5C5C',
      800: '#454545',
      900: '#2D2D2D',
      950: '#1F1F1F',
    },
    vtMagenta: {
      50: '#FFF0F7',
      100: '#FFD6EA',
      200: '#FFADD5',
      300: '#FF85C0',
      400: '#FF5CAB',
      500: '#E6007E',
      600: '#CC0070',
      700: '#B30063',
      800: '#990055',
      900: '#660039',
      950: '#33001D',
    },
  },
  semantic: {
    primary: {
      50: '{vtOrange.50}',
      100: '{vtOrange.100}',
      200: '{vtOrange.200}',
      300: '{vtOrange.300}',
      400: '{vtOrange.400}',
      500: '{vtOrange.500}',
      600: '{vtOrange.600}',
      700: '{vtOrange.700}',
      800: '{vtOrange.800}',
      900: '{vtOrange.900}',
      950: '{vtOrange.950}',
    },
    colorScheme: {
      light: {
        surface: {
          0: '#FFFFFF',
          50: '{vtGray.50}',
          100: '{vtGray.100}',
          200: '{vtGray.200}',
          300: '{vtGray.300}',
          400: '{vtGray.400}',
          500: '{vtGray.500}',
          600: '{vtGray.600}',
          700: '{vtGray.700}',
          800: '{vtGray.800}',
          900: '{vtGray.900}',
          950: '{vtGray.950}',
        },
        formField: {
          background: '#FFFFFF',
          disabledBackground: '{vtGray.100}',
          filledBackground: '{vtGray.50}',
          borderColor: '{vtGray.300}',
          hoverBorderColor: '{vtOrange.500}',
          focusBorderColor: '{vtOrange.500}',
          color: '{vtGray.950}',
          disabledColor: '{vtGray.500}',
          placeholderColor: '{vtGray.500}',
        },
      },
    },
  },
  components: {
    button: {
      borderRadius: '8px',
    },
    card: {
      borderRadius: '12px',
    },
    inputtext: {
      borderRadius: '8px',
    },
  },
});

Angular App - CSS Variables

/* src/styles.css */
/* Google Fonts: convenient for prototyping. For production, self-host Inter from
   public/fonts/Inter/ to avoid third-party tracking (GDPR/privacy). */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');

:root {
  /* VisiTrans Primary Colors (for non-PrimeNG contexts) */
  --vt-black: #1F1F1F;
  --vt-orange: #FC9E00;
  --vt-white: #FFFFFF;

  /* VisiTrans Gray Scale */
  --vt-gray-90: #2D2D2D;
  --vt-gray-70: #5C5C5C;
  --vt-gray-50: #8A8A8A;
  --vt-gray-30: #B8B8B8;
  --vt-gray-10: #E6E6E6;

  /* VisiTrans Orange Scale */
  --vt-orange-dark: #CC5200;
  --vt-orange-light: #FF944D;

  /* VisiTrans Action Color */
  --vt-magenta: #E6007E;

  /* Typography */
  --vt-font-family: 'Inter', sans-serif;

  /* Spacing */
  --vt-space-xs: 4px;
  --vt-space-sm: 8px;
  --vt-space-md: 16px;
  --vt-space-lg: 24px;
  --vt-space-xl: 32px;
  --vt-space-2xl: 48px;
}

body {
  font-family: var(--vt-font-family);
  color: var(--vt-black);
  background-color: var(--vt-white);
}

Angular App - PrimeNG Import Best Practice

Import individual PrimeNG components rather than full modules to enable tree-shaking in PrimeNG v20+:

// Preferred: individual component imports (smaller bundles)
import { Button } from 'primeng/button';
import { Card } from 'primeng/card';

// Avoid in production: full module imports increase bundle size
// import { ButtonModule } from 'primeng/button';

Angular App - Directory Structure

src/
├── app/
│   ├── core/                    # Singleton services, guards, interceptors
│   │   ├── theme/
│   │   │   └── vt-preset.ts     # PrimeNG VisiTrans preset
│   │   ├── interceptors/
│   │   └── guards/
│   ├── shared/                  # Reusable components, pipes, directives
│   │   ├── components/
│   │   ├── pipes/
│   │   └── directives/
│   ├── features/                # Feature modules (lazy-loaded routes)
│   │   └── dashboard/
│   │       ├── dashboard.component.ts
│   │       ├── dashboard.component.html
│   │       └── dashboard.routes.ts
│   ├── app.component.ts
│   ├── app.config.ts
│   └── app.routes.ts
├── styles.css
├── main.ts
└── index.html

Angular App - vitest.config.ts

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['src/test-setup.ts'],
    include: ['src/**/*.spec.ts'],
  },
});

Angular App - test-setup.ts

// src/test-setup.ts
import '@analogjs/vitest-angular/setup-zone';

Optional Features

When selected, add:

Docker Support

  • Dockerfile
  • docker-compose.yml
  • .dockerignore
  • Update .gitignore

API Documentation (OpenAPI)

  • docs/openapi.yml template
  • Add swagger-ui or redoc dependency

Authentication Boilerplate

  • TypeScript: passport.js or jose JWT setup
  • Rails: devise or has_secure_password setup

Database Migrations Setup

  • TypeScript: Prisma schema and initial migration
  • Rails: Database config and example migration

Error Monitoring (Sentry)

  • Sentry SDK dependency
  • src/lib/sentry.ts or config/initializers/sentry.rb
  • .env.example with SENTRY_DSN placeholder

Feature Flags Setup

  • TypeScript: Unleash or custom setup
  • Rails: Flipper gem
  • Basic toggle infrastructure

SpecKit Integration

If using SpecKit workflow:

  1. Create .specify/ directory and specs/ directory
  2. Move or create constitution.md in .specify/memory/
  3. Scaffold will be validated against spec during /vt-c-wf-review

Troubleshooting

"Directory not empty"

  • Bootstrap will preserve existing files
  • Only creates files that don't exist
  • Use --force conceptually to overwrite (ask user first)

"Missing PRD"

  • Bootstrap works without PRD
  • Recommend creating PRD.md for better CLAUDE.md generation