scaffold
/scaffold — Project-Aware File Generator
Orientation
Use when: Creating a new component, module, service, route, hook, domain, or utility with existing examples in the project.
Do NOT use when: The file has no precedent (use /marshal for unconstrained generation), you're modifying existing files (use /refactor), or the project has no conventions yet.
Needs: target type, name, and optional description.
Protocol
Step 1: IDENTIFY THE TARGET
Parse the user's request into:
- type: component | module | service | route | hook | domain | utility | custom
- name: the name the user gave (e.g., "UserProfile", "auth-service", "settings route")
- description: what it does, if provided (otherwise leave blank for now)
If the type is ambiguous, ask ONE clarifying question. Do not ask more than one.
Step 2: FIND EXEMPLARS
Search the codebase for 2-3 existing files of the same type.
Search strategy by type:
| Type | Search Pattern | What to Look For |
|---|---|---|
| component | **/*.tsx in the same directory or sibling directories |
Functional components with similar complexity |
| module | Same directory as where new module will live | Registration pattern, exports, config shape |
| service | **/services/**, **/lib/** |
Class vs function, singleton vs factory, error handling |
| route | Router config files, **/routes.*, **/pages/** |
Route definition format, lazy loading, guards |
| hook | **/hooks/**, **/use*.ts |
Naming, parameter patterns, return types, cleanup |
| domain | Top-level domain/feature directories | Manifest structure, entry point, internal layout |
| utility | **/utils/**, **/helpers/** |
Pure function style, type signatures, JSDoc |
For each exemplar, extract:
- File naming convention (PascalCase, kebab-case, camelCase, snake_case)
- Directory placement (co-located with component? separate
hooks/dir?) - Import style (path aliases? relative? named imports? default exports?)
- Export style (named exports? default? re-exported from barrel/index file?)
- Internal patterns (how state is managed, how errors are handled, JSDoc or no)
- Test co-location (
.test.tsnext to file?__tests__/directory? separatetests/tree?) - Types pattern (inline? separate
.types.ts? shared types file?)
Output a brief analysis (3-5 lines) summarizing the conventions you found.
Step 3: DETERMINE THE FILE SET
Based on the exemplars, determine which files to generate. Not every project needs every file. Only generate what the project's conventions call for.
Decision matrix:
| File | Generate IF... |
|---|---|
| Main file | Always |
Types file (.types.ts) |
Project separates types into their own files (check exemplars) |
Test file (.test.ts) |
Project has co-located tests for this type of file |
| Barrel/index file | Project uses barrel exports AND this file's directory doesn't already have one |
| Barrel update | Project uses barrel exports AND the directory already has an index file |
Style file (.module.css, .styled.ts) |
Project uses co-located styles for this type |
Storybook file (.stories.tsx) |
Project has stories for this type of file |
Do NOT generate:
- Empty placeholder files with only a TODO comment
- Test files that only contain
describe('...', () => { it.todo('...') }) - Types files that only re-export from elsewhere
- Any file type the project doesn't already use
Step 4: GENERATE THE FILES
For each file in the set, generate content by adapting the closest exemplar.
Rules:
- Match the exemplar's structure exactly — same section order, same patterns
- Replace names and specific logic, keep structural patterns
- Every generated file must be syntactically valid and importable
- No placeholder comments (
// TODO: implement,// Add logic here) - No empty function bodies unless the exemplar has them
- Minimal but functional — renders something, has at least one real method, returns a typed value
- Match the project's TypeScript strictness
Match the exemplar's props pattern, state management, utility imports, async patterns, and error handling exactly.
Step 5: WIRE IT IN
Find every registration point the exemplars use and add the new file there.
Common wiring points (check which ones the project uses):
| Wiring Point | How to Find It | What to Add |
|---|---|---|
| Barrel exports | index.ts in the same or parent directory |
export { NewThing } from './NewThing' |
| Route registration | Router config file (search for exemplar's route) | New route entry matching the pattern |
| Module registry | Bootstrap/registration file | New registration call |
| Navigation/sidebar | Nav config array | New nav entry if appropriate |
| Lazy loading map | Dynamic import map | New lazy import entry |
| Type unions | Discriminated unions that list all variants | New variant if this is a new "type" of thing |
Rules:
- Only wire into registration points that the exemplars actually use
- Match the exact format — same spacing, same trailing commas, same comments
- If a registration point uses alphabetical ordering, maintain it
- Never create new registration points — only add to existing ones
Step 6: VERIFY
Run typecheck — every generated file must pass. Fix failures before exiting. If typecheck is unavailable, do a manual read-through for syntax and import correctness.
Fringe Cases
- Target directory or file already exists: Do not silently overwrite. Confirm with the user before proceeding. Output: "A file at
{path}already exists. Overwrite it?" and wait for confirmation. - Template or exemplar not found: List the available file types in the codebase and ask which one to use as the exemplar. Never scaffold from memory if no exemplar exists.
- Language or framework not detected: Ask the user directly rather than guessing. One question: "What type of file should this be? (e.g., React component, Express route, utility function)"
- Typecheck fails after generation: Fix the issue before exiting — do not leave the user with broken generated files.
- No wiring point found: Note the missing registration explicitly in the exit summary rather than silently leaving the file unwired.
Contextual Gates
Reversibility: Amber — creates new files and modifies registration points; git checkout to undo.
Cost: No cost actions — file generation only; no agents spawned, no confirmation needed.
Trust: No gates — safe at all trust levels; overwrite confirmation is in Fringe Cases.
Quality Gates
All of these must be true before the skill exits:
- Found 2+ exemplar files of the same type in the project
- Generated files match the project's naming convention exactly
- Generated files match the project's import/export style exactly
- No placeholder comments, TODO stubs, or empty function bodies
- Every generated file is syntactically valid TypeScript/JavaScript
- Main file is wired into the project (barrel export, route, registry, etc.)
- Test file exists IF AND ONLY IF the project co-locates tests for this type
- Types file exists IF AND ONLY IF the project separates types for this type
- Typecheck passes (or manual verification if typecheck unavailable)
Exit Protocol
Output a summary in this format:
SCAFFOLD COMPLETE
Created:
- path/to/MainFile.tsx (component)
- path/to/MainFile.test.tsx (test)
- path/to/MainFile.types.ts (types)
Wired into:
- path/to/index.ts (barrel export)
- path/to/routes.ts (route registration)
Conventions matched from:
- path/to/ExemplarA.tsx
- path/to/ExemplarB.tsx
Typecheck: PASS
---HANDOFF---
- Scaffolded: {name} ({type})
- Created: {N} files, wired into {N} registration points
- Conventions matched from: {exemplar names}
- Reversibility: green -- new files only, delete to undo
---