scaffold · diff
git:20260329.6c378a2 to git:20260503.631b94d
15 added, 60 removed. Audit A to A.
---
name: scaffold
description: >-
Project-aware file generation. Reads existing codebase conventions (naming,
structure, imports, exports, test patterns) then generates new files that
match exactly. Wires generated files into the project's registration points.
user-invocable: true
auto-trigger: false
trigger_keywords:
- scaffold
- generate component
- generate module
- generate service
- new component
- new module
- new route
- new service
- create component
- stub out
---
# /scaffold — Project-Aware File Generator
- ## Identity
-
- You are a scaffolding expert. You generate new files that look like they were
- written by the same developer who wrote the rest of the project. You NEVER
- generate boilerplate from memory or templates — you read the actual codebase
- first, find the closest existing examples, and replicate their exact patterns.
-
- Your output is indistinguishable from hand-written code because it IS
- hand-written code — copied from the project's own conventions.
-
## Orientation
- **Use when:**
- - Creating a new component, module, service, route, hook, domain, or utility
- - The project has existing examples of the same kind of file
- - You want the new file wired in (exports, routes, registrations) on first write
+ **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 in the project (write it from scratch with the user)
- - You are modifying existing files (just edit them directly)
- - The project has no conventions yet (use `/setup` to establish them first)
+ **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.
- **What this skill needs:**
- - A target type: what kind of file (component, service, route, hook, module, etc.)
- - A name: what to call it
- - Optional: a brief description of what it does (helps generate meaningful internals)
+ **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. These are your
- exemplars — the gold standard for how this project writes this kind of file.
+ 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:**
1. File naming convention (PascalCase, kebab-case, camelCase, snake_case)
2. Directory placement (co-located with component? separate `hooks/` dir?)
3. Import style (path aliases? relative? named imports? default exports?)
4. Export style (named exports? default? re-exported from barrel/index file?)
5. Internal patterns (how state is managed, how errors are handled, JSDoc or no)
6. Test co-location (`.test.ts` next to file? `__tests__/` directory? separate `tests/` tree?)
7. Types pattern (inline? separate `.types.ts`? shared types file?)
**Output a brief analysis** (3-5 lines) summarizing the conventions you found.
- Example: "This project uses PascalCase .tsx files co-located with .test.tsx files.
- Components use named exports, re-exported from index.ts barrels. Hooks are in a
- shared hooks/ directory. Path aliases: @/components, @/hooks, @/services."
### 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:**
1. Match the exemplar's structure exactly — same section order, same patterns
2. Replace names and specific logic, keep structural patterns
3. Every generated file must be syntactically valid and importable
4. No placeholder comments (`// TODO: implement`, `// Add logic here`)
5. No empty function bodies unless the exemplar has them
- 6. Minimal but functional — if it is a component, it renders something. If it is
- a service, it has at least one real method signature. If it is a hook, it
- returns a typed value.
- 7. Match the project's TypeScript strictness (explicit return types if exemplars
- have them, inferred if they don't)
-
- **For components specifically:**
- - Match the exemplar's props pattern (interface vs type, inline vs separate)
- - Match the exemplar's state management pattern (useState, Zustand, context, etc.)
- - Include the same utility imports the exemplar uses (cn, clsx, classNames, etc.)
- - If the exemplar uses forwardRef, use forwardRef
- - If the exemplar uses memo, use memo
+ 6. Minimal but functional — renders something, has at least one real method, returns a typed value
+ 7. Match the project's TypeScript strictness
- **For services/modules specifically:**
- - Match initialization pattern (constructor, factory function, singleton)
- - Match error handling pattern (throw, Result type, error callbacks)
- - Match async patterns (Promise, async/await, callbacks)
+ Match the exemplar's props pattern, state management, utility imports, async patterns, and error handling exactly.
### Step 5: WIRE IT IN
- New files that exist but are not connected to anything are useless. Find every
- registration point the exemplars use and add the new file there.
+ 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:**
1. Only wire into registration points that the exemplars actually use
2. Match the exact format — same spacing, same trailing commas, same comments
3. If a registration point uses alphabetical ordering, maintain it
4. Never create new registration points — only add to existing ones
### Step 6: VERIFY
- After generating all files:
-
- 1. **Typecheck**: Run the project's typecheck command if available. Every generated
- file must pass. If it fails, fix the issue — do not leave it for the user.
- 2. **Import check**: Verify the main file is importable from outside its directory
- (via barrel export or direct import, matching the project's convention).
- 3. **Convention check**: Re-read the exemplars one more time. Compare your output.
- Flag any deviations you notice and fix them.
-
- If typecheck is not available or not configured, do a manual read-through of
- each generated file to verify syntax and imports are correct.
+ 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
```
-
- If the user provided a description, also include a one-line summary of what the
- generated file does. If they didn't, suggest they fill in the implementation
- details now that the structure is in place.
```
---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
---
```