scaffold

$npx mdskill add SethGammon/Citadel/scaffold

Generate project-aligned files matching existing conventions

  • Creates new components and services using proven code patterns
  • Depends on file system access to read current naming standards
  • Decides structure by analyzing two or three similar exemplar files
  • Delivers ready-to-integrate source code at project registration points
SKILL.md
.github/skills/scaffoldView on GitHub ↗
---
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

## 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:**
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.

### 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 — renders something, has at least one real method, returns a typed value
7. 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:**
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

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
---
```
More from SethGammon/Citadel