Awesome-omni-skill project-scaffold

Generate project boilerplate from scaffold templates. Use when creating a new project after stack selection. Triggers on: scaffold project, generate boilerplate, create from template.

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tools/project-scaffold" ~/.claude/skills/diegosouzapw-awesome-omni-skill-project-scaffold && rm -rf "$T"
manifest: skills/tools/project-scaffold/SKILL.md
source content

Project Scaffold Skill

CRITICAL: Initial scaffold commit respects user preference

Trigger: Before running

git commit
for initial scaffold.

Check: Ask user before committing: "Create initial commit? [Y/n]"

  • If user confirms or default: Proceed with
    git commit -m "Initial scaffold"
  • If user declines: Stop after
    git add .
    — do not commit

Note: New projects don't have

project.json
yet, so we ask the user directly. After scaffolding, if the user sets
git.autoCommit: false
in
project.json
, all future agent commits will require manual confirmation.

Generate project boilerplate based on selected stack archetype. This skill is invoked by

project-bootstrap
after stack selection.


Prerequisites

This skill expects:

  1. StackDecision — The selected stack from stack-advisor (or manual selection)
  2. Project path — Where to generate files
  3. Project name — Used for package.json, go.mod, etc.
  4. RequirementsManifest (optional) — For entity-based schema generation

Scaffold Selection

Match the selected stack to available scaffolds:

Stack ArchetypeScaffold
nextjs-supabase
~/.config/opencode/scaffolds/nextjs-supabase/
nextjs-prisma
~/.config/opencode/scaffolds/nextjs-prisma/
go-chi-postgres
~/.config/opencode/scaffolds/go-chi-postgres/
remix-supabase
~/.config/opencode/scaffolds/nextjs-supabase/
(adapt)
python-fastapi
~/.config/opencode/scaffolds/python-fastapi/

If no exact scaffold match exists, use the closest archetype and adapt.


Step 1: Load Scaffold Configuration

Read

scaffold.yaml
from the selected scaffold directory:

cat ~/.config/opencode/scaffolds/<scaffold-name>/scaffold.yaml

The configuration defines:

  • variables
    — User prompts for customization
  • dependencies
    — npm/go/pip packages to install
  • conditionalDependencies
    — Feature-based additions
  • structure
    — Directory tree to create
  • files
    — Templates to render
  • postScaffold
    — Commands to run after generation

Step 2: Collect Variables

For each variable in

scaffold.yaml
, use defaults from context or prompt user:

variables:
  - name: projectName
    prompt: "Project name"
    transform: kebab-case
    source: context.projectName  # Auto-fill from bootstrap context
  
  - name: description
    prompt: "Project description"
    source: context.description
  
  - name: supabaseProjectId
    prompt: "Supabase project ID (or 'local' for local dev)"
    default: local

Auto-fill priority:

  1. Context from project-bootstrap (projectName, description, features)
  2. Defaults from scaffold.yaml
  3. Prompt user if neither available

Step 3: Create Directory Structure

Create all directories defined in

structure
:

mkdir -p <project-path>/src/app
mkdir -p <project-path>/src/components/ui
mkdir -p <project-path>/src/hooks
mkdir -p <project-path>/src/lib/supabase
mkdir -p <project-path>/supabase/migrations
mkdir -p <project-path>/docs

Step 4: Render Template Files

For each file in the scaffold's

files/
directory:

4.1 Identify Template Type

ExtensionProcessing
.hbs
Render with Handlebars, remove
.hbs
from output
.template
Render with Handlebars, remove
.template
from output
(no special extension)Copy as-is

4.2 Build Template Context

const context = {
  // From bootstrap/stack-advisor
  projectName: 'my-project',
  projectNamePascal: 'MyProject',
  projectNameCamel: 'myProject',
  description: 'A scheduling app',
  
  // From StackDecision
  stack: {
    frontend: { framework: 'nextjs', version: '15' },
    database: { provider: 'supabase', type: 'postgres' },
    styling: { framework: 'tailwind', version: '4' },
    auth: { provider: 'supabase' }
  },
  
  // From RequirementsManifest (if available)
  features: {
    authentication: true,
    multiTenant: true,
    payments: true,
    email: false,
    ai: false
  },
  
  // Entities for schema generation
  entities: [
    { name: 'Organization', description: 'Tenant/workspace' },
    { name: 'Project', description: 'Work container' },
    { name: 'Task', description: 'Individual work item' }
  ],
  
  // Computed helpers
  hasPayments: capabilities.payments,
  hasEmail: capabilities.email,
  hasAI: capabilities.ai,
  hasMultiTenant: capabilities.multiTenant,
  
  // Date/time
  year: '2026',
  date: '2026-02-19'
};

4.3 Render Each File

for (const file of scaffoldConfig.files) {
  const templatePath = `${scaffoldDir}/files/${file.template}`;
  const outputPath = `${projectPath}/${file.output}`;
  
  if (file.template.endsWith('.hbs')) {
    const template = readFile(templatePath);
    const rendered = handlebars.compile(template)(context);
    writeFile(outputPath, rendered);
  } else {
    copyFile(templatePath, outputPath);
  }
}

Step 5: Generate Database Schema (If Entities Provided)

If

RequirementsManifest.entities
exists, generate initial migration:

5.1 For Supabase

Generate

supabase/migrations/00001_initial_schema.sql
:

-- Generated from spec entities
-- {{date}}

-- Organizations (multi-tenant core)
{{#if hasMultiTenant}}
CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE organization_members (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
  role TEXT NOT NULL DEFAULT 'member',
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(organization_id, user_id)
);

ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE organization_members ENABLE ROW LEVEL SECURITY;

CREATE POLICY "org_member_select" ON organizations
  FOR SELECT USING (
    id IN (SELECT organization_id FROM organization_members WHERE user_id = auth.uid())
  );

CREATE POLICY "org_member_select" ON organization_members
  FOR SELECT USING (user_id = auth.uid());
{{/if}}

-- Entity tables
{{#each entities}}
{{#unless (isBuiltIn name)}}
CREATE TABLE {{snakeCase name}}s (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  {{#if ../hasMultiTenant}}
  organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
  {{/if}}
  name TEXT NOT NULL,
  -- TODO: Add fields for {{description}}
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

ALTER TABLE {{snakeCase name}}s ENABLE ROW LEVEL SECURITY;

{{#if ../hasMultiTenant}}
CREATE POLICY "{{snakeCase name}}_org_access" ON {{snakeCase name}}s
  FOR ALL USING (
    organization_id IN (
      SELECT organization_id FROM organization_members WHERE user_id = auth.uid()
    )
  );
{{/if}}

{{/unless}}
{{/each}}

5.2 For Prisma

Generate

prisma/schema.prisma
:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  {{#if hasMultiTenant}}
  memberships OrganizationMember[]
  {{/if}}
}

{{#if hasMultiTenant}}
model Organization {
  id        String   @id @default(cuid())
  name      String
  slug      String   @unique
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  members   OrganizationMember[]
  {{#each entities}}
  {{#unless (isBuiltIn name)}}
  {{camelCase name}}s {{pascalCase name}}[]
  {{/unless}}
  {{/each}}
}

model OrganizationMember {
  id             String       @id @default(cuid())
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  organizationId String
  user           User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId         String
  role           String       @default("member")
  createdAt      DateTime     @default(now())

  @@unique([organizationId, userId])
}
{{/if}}

{{#each entities}}
{{#unless (isBuiltIn name)}}
model {{pascalCase name}} {
  id        String   @id @default(cuid())
  {{#if ../hasMultiTenant}}
  organization   Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  organizationId String
  {{/if}}
  name      String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
{{/unless}}
{{/each}}

Step 6: Resolve Dependencies

6.1 Merge Dependencies

Combine base dependencies with conditional dependencies:

const allDeps = [...scaffoldConfig.dependencies.production];
const allDevDeps = [...scaffoldConfig.dependencies.development];

for (const cond of scaffoldConfig.conditionalDependencies) {
  if (evaluateCondition(cond.if, context)) {
    allDeps.push(...cond.add);
  }
}

6.2 For Node.js Projects

Generate or update

package.json
:

{
  "name": "{{projectName}}",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "typecheck": "tsc --noEmit",
    "test": "jest",
    "test:e2e": "playwright test"
  },
  "dependencies": {
    {{#each dependencies}}
    "{{name}}": "{{version}}"{{#unless @last}},{{/unless}}
    {{/each}}
  },
  "devDependencies": {
    {{#each devDependencies}}
    "{{name}}": "{{version}}"{{#unless @last}},{{/unless}}
    {{/each}}
  }
}

6.3 For Go Projects

Generate

go.mod
:

module {{projectName}}

go 1.23

require (
    github.com/go-chi/chi/v5 v5.0.12
    github.com/jackc/pgx/v5 v5.5.5
    {{#if hasAuth}}
    github.com/golang-jwt/jwt/v5 v5.2.1
    {{/if}}
)

Step 7: Generate Environment Files

7.1 Create
.env.example

# Database
{{#if (eq stack.database.provider 'supabase')}}
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
{{else}}
DATABASE_URL=postgresql://user:password@localhost:5432/{{projectName}}
{{/if}}

{{#if hasPayments}}
# Stripe
STRIPE_SECRET_KEY=sk_test_xxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
{{/if}}

{{#if hasEmail}}
# Email
RESEND_API_KEY=re_xxx
{{/if}}

{{#if hasAI}}
# AI
OPENAI_API_KEY=sk-xxx
{{/if}}

# App
NEXT_PUBLIC_APP_URL=http://localhost:3000

7.2 Create
.env.local
(gitignored)

Copy

.env.example
to
.env.local
for immediate local development.


Step 8: Generate Documentation Stubs

8.1 Create
docs/project.json

This is handled by project-bootstrap, but scaffold ensures the structure:

{
  "$schema": "https://opencode.ai/schemas/project.json",
  "name": "{{projectName}}",
  "description": "{{description}}",
  "stack": { /* from StackDecision */ },
  "features": { /* from context */ }
}

8.2 Create
AGENTS.md

# {{projectNamePascal}}

{{description}}

## Development

\`\`\`bash
npm run dev     # Start dev server (port 3000)
npm run build   # Production build
npm run test    # Run tests
\`\`\`

## Stack

- **Frontend:** Next.js 15 (App Router)
- **Database:** {{stack.database.provider}}
- **Styling:** Tailwind CSS v4
- **Auth:** {{stack.auth.provider}}

## Documentation

- [Architecture](docs/ARCHITECTURE.md)
- [Conventions](docs/CONVENTIONS.md)

Step 9: Run Post-Scaffold Commands

Execute commands defined in

postScaffold
:

postScaffold:
  - command: npm install
    workdir: .
  - command: npx supabase init
    condition: stack.database.provider == 'supabase'
  - command: git init
  - command: git add .
  - command: git commit -m "Initial scaffold from {{scaffoldName}}"
    requiresConfirmation: true
    confirmationPrompt: "Create initial commit? [Y/n]"

Execution:

cd <project-path>

# Install dependencies
npm install

# Initialize Supabase (if applicable)
npx supabase init --workdir .

# Initialize git
git init
git add .

# Ask user before committing (requiresConfirmation: true)
# If user confirms: git commit -m "Initial scaffold from nextjs-supabase"
# If user declines: skip commit, files remain staged

Handling

requiresConfirmation
:

When a postScaffold command has

requiresConfirmation: true
:

  1. Display the
    confirmationPrompt
    to the user
  2. If user confirms (Y/Enter): execute the command
  3. If user declines (n): skip the command, continue with next step
  4. Report which commands were skipped

Step 10: Summary Output

Report what was generated:

═══════════════════════════════════════════════════════════════════════
                      SCAFFOLD COMPLETE
═══════════════════════════════════════════════════════════════════════

✅ Created project: /Users/dev/code/my-project

📁 Directory structure:
   src/
   ├── app/           Next.js App Router
   ├── components/    React components
   ├── hooks/         Custom hooks
   └── lib/           Utilities + Supabase clients
   supabase/
   └── migrations/    Database migrations
   docs/              Documentation

📦 Dependencies installed:
   • next@15, react@19, react-dom@19
   • @supabase/supabase-js, @supabase/ssr
   • tailwindcss@4, clsx, tailwind-merge
   • stripe, @stripe/stripe-js (payments feature)

🗃️ Database schema generated:
   • organizations (multi-tenant)
   • organization_members
   • projects (from spec)
   • tasks (from spec)

📝 Files created:
   • package.json
   • tsconfig.json
   • next.config.ts
   • tailwind.config.ts
   • src/app/layout.tsx
   • src/app/page.tsx
   • src/lib/supabase/client.ts
   • src/lib/supabase/server.ts
   • supabase/migrations/00001_initial_schema.sql
   • .env.example
   • .env.local
   • AGENTS.md

🚀 Next steps:
   1. cd /Users/dev/code/my-project
   2. Update .env.local with your Supabase credentials
   3. Run `npm run dev` to start development
   4. Review docs/drafts/prd-mvp.md for your user stories

═══════════════════════════════════════════════════════════════════════

Handlebars Helpers

The scaffold system provides these custom helpers:

HelperDescriptionExample
kebabCase
Convert to kebab-case
{{kebabCase projectName}}
my-project
camelCase
Convert to camelCase
{{camelCase name}}
myProject
pascalCase
Convert to PascalCase
{{pascalCase name}}
MyProject
snakeCase
Convert to snake_case
{{snakeCase name}}
my_project
upperCase
Convert to UPPER_CASE
{{upperCase name}}
MY_PROJECT
eq
Equality check
{{#if (eq type 'supabase')}}
isBuiltIn
Check if entity is built-in
{{#unless (isBuiltIn name)}}

Built-in entities (skipped in schema generation):

  • User
  • Organization
  • OrganizationMember

Error Handling

Scaffold Not Found

❌ No scaffold found for archetype: django-postgres

Available scaffolds:
  • nextjs-supabase
  • nextjs-prisma
  • go-chi-postgres

Would you like to:
  A. Use closest match (nextjs-prisma) and adapt
  B. Generate minimal structure only
  C. Cancel

> _

Missing Variables

⚠️ Missing required variable: supabaseProjectId

Enter Supabase project ID (or 'local' for local dev):
> _

Post-Scaffold Command Failed

⚠️ Post-scaffold command failed: npm install

Error: ENOENT: npm not found

The scaffold is complete but dependencies were not installed.
Run manually: cd /path/to/project && npm install

Output

Return the scaffold result to the calling agent:

{
  "success": true,
  "projectPath": "/Users/dev/code/my-project",
  "scaffold": "nextjs-supabase",
  "filesCreated": [
    "package.json",
    "tsconfig.json",
    "src/app/layout.tsx",
    "..."
  ],
  "dependenciesInstalled": true,
  "gitInitialized": true,
  "schemaGenerated": true,
  "entities": ["Organization", "Project", "Task"]
}