---
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:

1. Make changes directly in working codebases (example projects)
2. Extract those changes into reusable templates
3. Update generators to use the new templates
4. 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:

```javascript
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:

```typescript
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({
    // Configuration options
  }),
  buildTasks: (descriptor) => ({
    // Add auto-generated tasks for templates
    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:

```typescript
import { packageScope } from '@baseplate-dev/core-generators';

// Export a provider to package scope (available to entire package)
exports: {
  myProvider: myProviderType.export(packageScope),
}

// Consume a provider from dependencies
dependencies: {
  someProvider: someProviderType,
}
```

### Template Rendering with Renderers

The modern pattern uses auto-generated renderers:

```typescript
// Renderers are generated from templates in ./templates/ directory
import { MY_GENERATOR_GENERATED } from './generated/index.js';

// Use in tasks
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) => {
          // Simple render
          await builder.apply(renderers.myTemplate.render());

          // Render with variables
          await builder.apply(
            renderers.service.render({
              variables: {
                TPL_SERVICE_NAME: 'UserService',
              },
            }),
          );
        },
      };
    },
  }),
});
```

### Creating New Generators

Use the MCP action to scaffold new generators:

```javascript
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:

```javascript
mcp__baseplate_dev_server__generate_templates({});
```

Or via command line:

```bash
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.

```bash
# Validate changes work in the example project
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:**

```javascript
mcp__baseplate_dev_server__show_template_metadata({
  filePath: 'src/components/my-component.tsx',
  project: 'blog-with-auth',
});
```

**Configure NEW templates only when needed:**

```javascript
// For NEW TypeScript templates
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',
});

// For NEW text templates with variables
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',
});

// For NEW raw/binary templates
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:**

```javascript
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:

```javascript
mcp__baseplate_dev_server__extract_templates({
  project: 'blog-with-auth',
  app: 'backend', // or 'web' for frontend
});
```

This updates the local generator templates based on your code changes.

### 5. Generator Updates

Back in the repository root:

```bash
# Fix any type errors from added/removed variables and templates
pnpm typecheck

# Update generator configurations, schemas, and UI as needed:
# - Wire up new variables in compilers
# - Update schemas in project-builder-web or plugins
# - Add/remove generator logic as needed

# Validate changes
pnpm build && pnpm lint
```

### 6. Diff Validation

Check for differences between written and generated code:

```javascript
mcp__baseplate_dev_server__diff_project({
  project: 'blog-with-auth',
  packages: ['backend'], // or ['web'] for frontend
});
```

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

```javascript
// Apply specific files from generated output
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:

```javascript
// Add files to snapshot
mcp__baseplate_dev_server__snapshot_add({
  project: 'blog-with-auth',
  app: 'backend',
  files: ['src/specific-file.ts'],
});

// For removed files that should stay removed
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:

```javascript
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

```javascript
// Run final diff to ensure no unexpected changes
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:

```bash
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:

```javascript
mcp__baseplate_dev_server__sync_all_projects({
  overwrite: true,
});
```

### 11. Create Changeset

Add a changeset to document the changes for the changelog:

```bash
# Create a new changeset file in .changeset/ directory
```

Example changeset format:

```markdown
---
'@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:

```javascript
// View current snapshot status
mcp__baseplate_dev_server__snapshot_show({
  project: 'blog-with-auth',
  app: 'backend',
});

// Remove files from snapshot (let them be generated normally)
mcp__baseplate_dev_server__snapshot_remove({
  project: 'blog-with-auth',
  app: 'backend',
  files: ['src/file.ts'],
});

// Save complete snapshot (overwrites existing)
mcp__baseplate_dev_server__snapshot_save({
  project: 'blog-with-auth',
  app: 'backend',
  force: true,
});
```

## Advanced: Template Management

```javascript
// Create a new generator
mcp__baseplate_dev_server__create_generator({
  name: 'email/sendgrid',
  directory: 'packages/fastify-generators/src/generators',
  includeTemplates: true,
});

// List current templates
mcp__baseplate_dev_server__list_templates({
  project: 'blog-with-auth',
});

// Generate template files after manual extractor.json changes
mcp__baseplate_dev_server__generate_templates({
  project: 'blog-with-auth',
});

// Remove outdated templates
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

1. **Start with working code** - Always develop features in a concrete codebase first
2. **Use snapshots judiciously** - Only for intentional differences, not generator bugs
3. **Validate frequently** - Run diff commands often during development
4. **Test generated code** - Ensure example projects build and typecheck after syncing (`pnpm run:example <name> -- pnpm typecheck`)
5. **Keep commits focused** - Separate generator changes from template changes when possible
6. **Document template variables** - Use clear, descriptive names for template variables
7. **Sync all projects when done** - Always run `sync_all_projects` to ensure all examples are updated
