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:
- Project Type - Ask which type:
angular-app- Angular SPA (Angular 21, PrimeNG, TypeScript, Vitest) ← defaulttypescript-api- REST API service (Node.js, Express, TypeScript, Prisma)typescript-cli- Command-line tool (Node.js, TypeScript, Commander)rails-api- Rails API-only (Ruby, Rails, PostgreSQL)rails-full- Rails full-stack (Ruby, Rails, Hotwire, PostgreSQL)react-app- React SPA (React, TypeScript, Vite)-
nextjs-app- Next.js full-stack (Next.js, TypeScript, Tailwind) -
Project Name - The directory name (use current directory name if already in a project folder)
-
Optional Features - Multi-select from:
- Docker support
- API documentation (OpenAPI)
- Authentication boilerplate
- Database migrations setup
- Error monitoring (Sentry)
- 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.jsonwith zoneless change detection enabled ("zoneless": truein application builder options)package.jsonwith Angular 21, PrimeNG, PrimeIcons, PrimeFlex, Vitest, @angular-eslinttsconfig.jsonwith strict settings +strictTemplates: trueeslint.config.jswith @angular-eslintvitest.config.tsfor Angular 21 zoneless testing- Feature-oriented directory structure:
src/app/{core,shared,features} - PrimeNG preset file using
definePreset()from@primeuix/themesbased on Aura with VisiTrans design tokens - VisiTrans CSS variables file alongside PrimeNG tokens
app.config.tswithprovidePrimeNG()configuration
TypeScript Projects (typescript-api, typescript-cli, react-app, nextjs-app)¶
package.jsonwith scripts and dependenciestsconfig.jsonwith strict settingseslint.config.jsor.eslintrc.jsprettier.config.jsor.prettierrcvitest.config.tsorjest.config.js
Rails Projects (rails-api, rails-full)¶
Gemfilewith recommended gems.rubocop.ymlfor Ruby style- Database configuration templates
strong_migrationsinitializer
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:
- Locate inbox: Check these paths in order:
00-inbox/,01-docs/01-inbox/,docs/inbox/,docs/00-inbox/ - If inbox contains files (excluding README.md, .gitkeep):
- Read each document (up to 10 files, prioritizing .md then .txt then .pdf)
- Extract key themes, requirements, and stakeholder needs
- 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
- Display: "Found [N] inbox documents. Using as context for project setup."
- If inbox is empty or missing: Proceed normally (no change to current behavior)
- 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)¶
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:
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.jsis retained as a polyfill fallback. The app uses zoneless change detection (seeangular.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¶
Optional Features¶
When selected, add:
Docker Support¶
Dockerfiledocker-compose.yml.dockerignore- Update
.gitignore
API Documentation (OpenAPI)¶
docs/openapi.ymltemplate- 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.tsorconfig/initializers/sentry.rb.env.examplewith SENTRY_DSN placeholder
Feature Flags Setup¶
- TypeScript: Unleash or custom setup
- Rails: Flipper gem
- Basic toggle infrastructure
SpecKit Integration¶
If using SpecKit workflow:
- Create
.specify/directory andspecs/directory - Move or create
constitution.mdin.specify/memory/ - 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
--forceconceptually to overwrite (ask user first)
"Missing PRD"¶
- Bootstrap works without PRD
- Recommend creating PRD.md for better CLAUDE.md generation