| name | modify-generated-code |
| description | Workflow for modifying generated code in Baseplate, including template extraction, generator updates, and project synchronization. |
Modify Generated Code Skill
Use this skill when modifying code that is generated by Baseplate generators, adding new templates, or updating generator behavior.
Overview
Baseplate follows a code-first development approach:
- Make changes directly in working codebases (example projects)
- Extract those changes into reusable templates
- Update generators to use the new templates
- Validate and sync the generated code
Example projects:
examples/blog-with-auth - Blog application with authentication
examples/todo-with-better-auth - Todo application with Better Auth integration
Documentation References
For detailed documentation on generators, consult these guides via the baseplate-docs MCP:
| Guide | Document ID | Description |
|---|
| Generator Development Guide | df804c4d-72f1-4b30-bb4a-cdafac7608d2 | Comprehensive guide covering core providers, template rendering, and generator patterns |
| React Generators Guide | 8687772b-b258-4241-9cbe-e73e9d13a203 | React-specific providers, Vite configuration, component generation |
| Fastify Generators Guide | 862dcef2-7ff8-4de1-9840-fc73d89f32eb | Backend providers, API generation, configuration management |
| Core Providers Reference | 200cfab4-56ea-4f60-880c-fa6b8a18048c | Detailed reference for nodeProvider, typescriptFileProvider, etc. |
To fetch a guide:
mcp__baseplate_docs__get_document_by_id({
documentId: 'df804c4d-72f1-4b30-bb4a-cdafac7608d2',
});
Generator Architecture
Important: Never manually edit files in generated/ directories. These are auto-generated by the generate-templates command. Only modify:
- Template source files in
templates/ directories
extractor.json configuration
- The main
*.generator.ts file
Generator Packages
Generators are organized into three main packages:
| Package | Location | Purpose |
|---|
@baseplate-dev/core-generators | packages/core-generators/src/generators/ | Node.js, TypeScript, ESLint, Prettier, Vitest |
@baseplate-dev/react-generators | packages/react-generators/src/generators/ | React, Vite, Apollo, admin UI |
@baseplate-dev/fastify-generators | packages/fastify-generators/src/generators/ | Fastify, Prisma, Pothos, auth, email |
Generator Structure
Every generator follows this pattern:
import { createGenerator, createGeneratorTask } from '@baseplate-dev/sync';
import { z } from 'zod';
export const myGenerator = createGenerator({
name: 'category/my-generator',
generatorFileUrl: import.meta.url,
descriptorSchema: z.object({
}),
buildTasks: (descriptor) => ({
paths: MY_GENERATOR_GENERATED.paths.task,
imports: MY_GENERATOR_GENERATED.imports.task,
renderers: MY_GENERATOR_GENERATED.renderers.task,
main: createGeneratorTask({
dependencies: {
renderers: MY_GENERATOR_GENERATED.renderers.provider,
},
run({ renderers }) {
return {
build: async (builder) => {
await builder.apply(renderers.myTemplate.render());
},
};
},
}),
}),
});
Key Providers
Core Providers (from @baseplate-dev/core-generators):
| Provider | Purpose |
|---|
nodeProvider | Package.json, dependencies, scripts |
typescriptFileProvider | TypeScript file generation with imports |
packageInfoProvider | Package name, root path, src path |
eslintConfigProvider | ESLint configuration |
nodeGitIgnoreProvider | .gitignore management |
React Providers (from @baseplate-dev/react-generators):
| Provider | Purpose |
|---|
reactBaseConfigProvider | Vite plugins, server options, app configuration |
reactPathsProvider | React-specific paths (components folder, etc.) |
Fastify Providers (from @baseplate-dev/fastify-generators):
| Provider | Purpose |
|---|
fastifyProvider | Node flags, dev output formatter |
fastifyOutputProvider | Runtime config (node commands, flags) |
configServiceProvider | Environment variables with Zod validation |
Shared Providers and Scopes
Providers communicate between tasks using scopes:
import { packageScope } from '@baseplate-dev/core-generators';
exports: {
myProvider: myProviderType.export(packageScope),
}
dependencies: {
someProvider: someProviderType,
}
Template Rendering with Renderers
The modern pattern uses auto-generated renderers:
import { MY_GENERATOR_GENERATED } from './generated/index.js';
buildTasks: () => ({
paths: MY_GENERATOR_GENERATED.paths.task,
imports: MY_GENERATOR_GENERATED.imports.task,
renderers: MY_GENERATOR_GENERATED.renderers.task,
main: createGeneratorTask({
dependencies: {
renderers: MY_GENERATOR_GENERATED.renderers.provider,
},
run({ renderers }) {
return {
build: async (builder) => {
await builder.apply(renderers.myTemplate.render());
await builder.apply(
renderers.service.render({
variables: {
TPL_SERVICE_NAME: 'UserService',
},
}),
);
},
};
},
}),
});
Creating New Generators
Use the MCP action to scaffold new generators:
mcp__baseplate_dev_server__create_generator({
name: 'category/my-generator',
directory: 'packages/fastify-generators/src/generators',
includeTemplates: true,
});
Example directories:
packages/core-generators/src/generators
packages/fastify-generators/src/generators
packages/react-generators/src/generators
plugins/plugin-storage/src/generators/fastify
This creates:
<name>.generator.ts - Main generator with boilerplate
index.ts - Barrel export
generated/index.ts - Placeholder for generated exports
extractor.json - Template extractor configuration
After creating, add templates to the templates/ directory and regenerate the typed template helpers:
mcp__baseplate_dev_server__generate_templates({});
Or via command line:
pnpm generate:templates
Step-by-Step Process
1. Code Development
Make changes in the appropriate example project (e.g., examples/blog-with-auth).
Note: You only need to modify one project at a time.
pnpm run:example blog-with-auth -- pnpm build
pnpm run:example blog-with-auth -- pnpm typecheck
2. Review Changes with User
Before proceeding with template extraction, pause and allow the user to review the code changes. This ensures:
- The fixes/features look correct before extracting to templates
- Any issues can be caught early before they propagate to generators
- The user understands what will be extracted
IMPORTANT: At this stage, you should ONLY have modified files in the example project (e.g., examples/blog-with-auth). Do NOT modify any generator code (*.generator.ts), template files in templates/ directories, or plugin code until AFTER the user has reviewed and approved the example project changes. Generator modifications happen in Step 5 after template extraction.
3. Template Metadata Management (Optional)
For most cases: Skip this step! Template metadata is automatically preserved when modifying existing template files.
Only manage metadata when:
- Creating brand new template files
- Changing fundamental template properties (name, generator, type)
- Removing template files completely
Check existing metadata:
mcp__baseplate_dev_server__show_template_metadata({
filePath: 'src/components/my-component.tsx',
project: 'blog-with-auth',
});
Configure NEW templates only when needed:
mcp__baseplate_dev_server__configure_ts_template({
filePath: 'src/components/brand-new-component.tsx',
generator: '@baseplate-dev/react-generators#core/react',
templateName: 'brand-new-component',
project: 'blog-with-auth',
});
mcp__baseplate_dev_server__configure_text_template({
filePath: 'src/config/new-config.json',
generator: '@baseplate-dev/core-generators',
templateName: 'new-config',
variables: { appName: { value: 'MyApp' } },
project: 'blog-with-auth',
});
mcp__baseplate_dev_server__configure_raw_template({
filePath: 'public/new-icon.ico',
generator: '@baseplate-dev/core-generators',
templateName: 'new-icon',
project: 'blog-with-auth',
});
Delete template completely:
mcp__baseplate_dev_server__delete_template({
filePath: 'src/components/old-component.tsx',
project: 'blog-with-auth',
});
4. Template Extraction
Extract templates from your working codebase:
mcp__baseplate_dev_server__extract_templates({
project: 'blog-with-auth',
app: 'backend',
});
This updates the local generator templates based on your code changes.
5. Generator Updates
Back in the repository root:
pnpm typecheck
pnpm build && pnpm lint
6. Diff Validation
Check for differences between written and generated code:
mcp__baseplate_dev_server__diff_project({
project: 'blog-with-auth',
packages: ['backend'],
});
7. Handle Differences
Analyze the diff results:
- Acceptable differences (import aliases, minor formatting): Can be incrementally fixed using
sync-file or ignored
- Significant differences: Update generators to match written code
- Intentional differences: Use snapshot system
Incremental fixes with sync-file:
For acceptable differences that you want to eliminate before the final sync, use the sync-file command to apply individual files:
mcp__baseplate_dev_server__sync_file({
project: 'blog-with-auth',
app: 'backend',
files: ['src/routes/users.ts', 'src/models/*.ts'],
});
This writes the generated version directly to both the working directory and baseplate/generated/ folder without performing a full sync. Use this to incrementally clear diffs before running the final overwrite sync in step 8.
Intentional differences are changes that should exist in the example project but NOT be extracted into generators. Common examples:
- Unit tests specific to the example project
- Demo data or sample content
- Example-specific configuration
For files with intentional differences:
mcp__baseplate_dev_server__snapshot_add({
project: 'blog-with-auth',
app: 'backend',
files: ['src/specific-file.ts'],
});
mcp__baseplate_dev_server__snapshot_add({
project: 'blog-with-auth',
app: 'backend',
files: ['src/removed-file.ts'],
deleted: true,
});
8. Code Synchronization
Once diffs are acceptable, synchronize the working codebase:
mcp__baseplate_dev_server__sync_project({
project: 'blog-with-auth',
overwrite: true,
});
Warning: This will overwrite all files in the project. Before running with overwrite: true:
- Ensure your diff looks correct (step 6)
- Consider committing your current changes first, so you can revert if needed
9. Final Validation
mcp__baseplate_dev_server__diff_project({
project: 'blog-with-auth',
packages: ['backend'],
});
The diff should show no changes or only expected/snapshotted differences.
Also verify the example project still builds and typechecks after the sync:
pnpm run:example blog-with-auth -- pnpm install
pnpm run:example blog-with-auth -- pnpm typecheck
pnpm run:example blog-with-auth -- pnpm build
10. Sync All Projects
Sync all example projects to apply your generator changes:
mcp__baseplate_dev_server__sync_all_projects({
overwrite: true,
});
11. Create Changeset
Add a changeset to document the changes for the changelog:
Example changeset format:
---
'@baseplate-dev/plugin-auth': patch
---
Brief description of the change
- Bullet point details of what changed
- Another detail if needed
Changeset types:
patch: Bug fixes, minor changes (most common)
minor: New features, non-breaking changes
major: Breaking changes
Package names should match the package that was modified (e.g., @baseplate-dev/plugin-auth, @baseplate-dev/fastify-generators, @baseplate-dev/react-generators).
Advanced: Snapshot Management
For complex scenarios with intentional differences:
mcp__baseplate_dev_server__snapshot_show({
project: 'blog-with-auth',
app: 'backend',
});
mcp__baseplate_dev_server__snapshot_remove({
project: 'blog-with-auth',
app: 'backend',
files: ['src/file.ts'],
});
mcp__baseplate_dev_server__snapshot_save({
project: 'blog-with-auth',
app: 'backend',
force: true,
});
Advanced: Template Management
mcp__baseplate_dev_server__create_generator({
name: 'email/sendgrid',
directory: 'packages/fastify-generators/src/generators',
includeTemplates: true,
});
mcp__baseplate_dev_server__list_templates({
project: 'blog-with-auth',
});
mcp__baseplate_dev_server__generate_templates({
project: 'blog-with-auth',
});
mcp__baseplate_dev_server__delete_template({
filePath: 'src/outdated-file.ts',
project: 'blog-with-auth',
});
Important: When to Stop and Ask for Help
If you encounter issues with core generator logic (e.g., providers not wiring correctly, template rendering issues, or unexpected generation behavior), stop and highlight the issue for the user rather than trying to hack around it. These issues often indicate deeper problems that need proper resolution.
Troubleshooting
Type Errors After Template Extraction
- Check that all template variables are properly defined in generators
- Ensure import providers are correctly configured
- Run
pnpm typecheck to identify specific issues
Diff Conflicts
- Use
snapshot_add to add resolved files to snapshot
- For manual conflicts, edit files and re-add to snapshot
- Consider if differences indicate generator bugs vs. intentional customizations
Import Alias Differences
- Differences in relative paths vs. import aliases are acceptable as long as they resolve to the same file (e.g.,
../components/button.ts vs @src/components/button.ts)
- Use snapshots if these differences are intentional and should be preserved
- Update generators if aliases should be standardized
Best Practices
- Start with working code - Always develop features in a concrete codebase first
- Use snapshots judiciously - Only for intentional differences, not generator bugs
- Validate frequently - Run diff commands often during development
- Test generated code - Ensure example projects build and typecheck after syncing (
pnpm run:example <name> -- pnpm typecheck)
- Keep commits focused - Separate generator changes from template changes when possible
- Document template variables - Use clear, descriptive names for template variables
- Sync all projects when done - Always run
sync_all_projects to ensure all examples are updated