--- name: automation-scripts-generator description: Generate automation scripts for component creation, bulk operations, code transformation, project scaffolding, and custom CLI tools for UI library development workflows allowed-tools: - Read - Write - Edit - Bash - Glob - Grep - Task --- # Automation Scripts Generator Expert skill for creating automation scripts and CLI tools for UI library development. Specializes in component generators, bulk operations, code transformers, project scaffolding, and custom development workflows. ## Core Capabilities ### 1. Component Generation Scripts - **New Component**: Generate complete component with tests, styles, docs - **Variant Generator**: Create component variants automatically - **Bulk Creation**: Generate multiple components at once - **Template Customization**: Configurable component templates - **File Organization**: Auto-organize files in correct structure - **Index Updates**: Auto-update barrel exports ### 2. Code Transformation - **Refactoring Scripts**: Automated code refactoring - **Migration Tools**: Migrate between patterns/libraries - **Import Organizer**: Sort and clean imports - **Props Transformer**: Convert prop patterns - **Type Generator**: Generate TypeScript types from data - **Style Converter**: Convert CSS to different formats ### 3. Bulk Operations - **Mass Rename**: Rename files/components in bulk - **Batch Update**: Update props across components - **Global Replace**: Smart search and replace - **Delete Unused**: Find and remove unused code - **Add Feature**: Add feature to multiple components - **Update Dependencies**: Batch dependency updates ### 4. Project Scaffolding - **New Project**: Initialize complete UI library - **Documentation Site**: Set up Storybook/docs site - **Testing Setup**: Configure testing infrastructure - **CI/CD Pipeline**: Set up GitHub Actions - **Package Configuration**: Set up build and publish - **Monorepo Setup**: Configure Turborepo/Nx ### 5. CLI Tools - **Interactive Prompts**: User-friendly CLI interface - **Command Framework**: Build custom commands - **Configuration**: Load/save preferences - **Validation**: Input validation and error handling - **Progress Indicators**: Show progress for long operations - **Logging**: Structured logging and debugging ### 6. Quality Automation - **Linting Scripts**: Auto-fix lint issues - **Format All**: Format entire codebase - **Type Check**: Run TypeScript checks - **Test Runner**: Execute test suites - **Coverage Reports**: Generate coverage analysis - **Performance Audit**: Analyze bundle sizes ## Workflow ### Phase 1: Script Planning 1. **Identify Task** - What needs automation? - How often is it done? - What's the manual process? - What can go wrong? 2. **Design Solution** - Command interface? - Input parameters? - Output format? - Error handling? 3. **Choose Tools** - Node.js script? - Shell script? - CLI framework? - Dependencies needed? ### Phase 2: Implementation 1. **Build Core Logic** - File operations - Code generation - Validation - Error handling 2. **Add CLI Interface** - Argument parsing - Interactive prompts - Progress indicators - Output formatting 3. **Test Thoroughly** - Happy path - Edge cases - Error scenarios - Dry run mode ### Phase 3: Integration 1. **Document Script** - Usage instructions - Examples - Options reference - Troubleshooting 2. **Add to Workflow** - npm scripts - package.json - CI/CD pipeline - Developer docs 3. **Optimize** - Performance - Error messages - User experience - Logging ## Script Templates ### Component Generator (Node.js) ```typescript #!/usr/bin/env node // scripts/generate-component.ts import fs from 'fs/promises' import path from 'path' import { prompts } from 'prompts' import chalk from 'chalk' interface ComponentOptions { name: string type: 'basic' | 'compound' | 'polymorphic' withTests: boolean withStories: boolean withDocs: boolean } async function generateComponent(options: ComponentOptions) { const { name, type, withTests, withStories, withDocs } = options console.log(chalk.blue(`\nšŸš€ Generating ${type} component: ${name}\n`)) const componentDir = path.join(process.cwd(), 'src', 'components', name) // Create directory await fs.mkdir(componentDir, { recursive: true }) // Generate component file const componentCode = generateComponentCode(name, type) await fs.writeFile(path.join(componentDir, `${name}.tsx`), componentCode) console.log(chalk.green(`āœ“ Created ${name}.tsx`)) // Generate types file const typesCode = generateTypesCode(name) await fs.writeFile(path.join(componentDir, `${name}.types.ts`), typesCode) console.log(chalk.green(`āœ“ Created ${name}.types.ts`)) // Generate tests if (withTests) { const testCode = generateTestCode(name) await fs.writeFile(path.join(componentDir, `${name}.test.tsx`), testCode) console.log(chalk.green(`āœ“ Created ${name}.test.tsx`)) } // Generate Storybook story if (withStories) { const storyCode = generateStoryCode(name) await fs.writeFile(path.join(componentDir, `${name}.stories.tsx`), storyCode) console.log(chalk.green(`āœ“ Created ${name}.stories.tsx`)) } // Generate README if (withDocs) { const readmeCode = generateReadmeCode(name) await fs.writeFile(path.join(componentDir, 'README.md'), readmeCode) console.log(chalk.green(`āœ“ Created README.md`)) } // Generate index file const indexCode = generateIndexCode(name) await fs.writeFile(path.join(componentDir, 'index.ts'), indexCode) console.log(chalk.green(`āœ“ Created index.ts`)) // Update barrel export await updateBarrelExport(name) console.log(chalk.green(`āœ“ Updated src/components/index.ts`)) console.log(chalk.green.bold(`\n✨ Component ${name} generated successfully!\n`)) } function generateComponentCode(name: string, type: string): string { const templates = { basic: `import React from 'react' import { ${name}Props } from './${name}.types' export function ${name}({ children, ...props }: ${name}Props) { return (
{children}
) }`, compound: `import React, { createContext, useContext } from 'react' import { ${name}Props, ${name}ContextValue } from './${name}.types' const ${name}Context = createContext<${name}ContextValue | undefined>(undefined) export function ${name}({ children, ...props }: ${name}Props) { const value: ${name}ContextValue = { // Add context value here } return ( <${name}Context.Provider value={value}>
{children}
) } export function use${name}() { const context = useContext(${name}Context) if (!context) { throw new Error('use${name} must be used within ${name}') } return context } ${name}.Item = function ${name}Item({ children }: { children: React.ReactNode }) { const context = use${name}() return
{children}
}`, polymorphic: `import React from 'react' import { ${name}Props } from './${name}.types' export function ${name}({ as, children, ...props }: ${name}Props) { const Component = as || 'div' return {children} }`, } return templates[type] || templates.basic } function generateTypesCode(name: string): string { return `import React from 'react' export interface ${name}Props extends React.HTMLAttributes { children?: React.ReactNode // Add your props here } export interface ${name}ContextValue { // Add context value types here }` } function generateTestCode(name: string): string { return `import { render, screen } from '@testing-library/react' import { ${name} } from './${name}' describe('${name}', () => { it('renders children', () => { render(<${name}>Hello World) expect(screen.getByText('Hello World')).toBeInTheDocument() }) })` } function generateStoryCode(name: string): string { return `import type { Meta, StoryObj } from '@storybook/react' import { ${name} } from './${name}' const meta: Meta = { title: 'Components/${name}', component: ${name}, tags: ['autodocs'], } export default meta type Story = StoryObj export const Default: Story = { args: { children: '${name} content', }, }` } function generateReadmeCode(name: string): string { return `# ${name} ## Usage \`\`\`tsx import { ${name} } from '@your-library/components' function App() { return ( <${name}> Content here ) } \`\`\` ## Props | Prop | Type | Default | Description | |------|------|---------|-------------| | children | ReactNode | - | The content | ## Examples ### Basic Usage \`\`\`tsx <${name}>Hello World \`\`\` ` } function generateIndexCode(name: string): string { return `export { ${name} } from './${name}' export type { ${name}Props } from './${name}.types'` } async function updateBarrelExport(name: string) { const indexPath = path.join(process.cwd(), 'src', 'components', 'index.ts') try { let content = await fs.readFile(indexPath, 'utf-8') const exportLine = `export * from './${name}'\n` // Check if export already exists if (!content.includes(exportLine)) { // Add export in alphabetical order const exports = content.split('\n').filter(line => line.startsWith('export')) exports.push(exportLine.trim()) exports.sort() content = exports.join('\n') + '\n' await fs.writeFile(indexPath, content) } } catch (error) { // If index doesn't exist, create it await fs.writeFile(indexPath, `export * from './${name}'\n`) } } // CLI Interface async function main() { console.log(chalk.cyan.bold('\nšŸ“¦ Component Generator\n')) const response = await prompts([ { type: 'text', name: 'name', message: 'Component name (PascalCase):', validate: (value) => /^[A-Z][a-zA-Z0-9]*$/.test(value) || 'Must be PascalCase (e.g., Button)', }, { type: 'select', name: 'type', message: 'Component type:', choices: [ { title: 'Basic', value: 'basic' }, { title: 'Compound', value: 'compound' }, { title: 'Polymorphic', value: 'polymorphic' }, ], }, { type: 'confirm', name: 'withTests', message: 'Generate tests?', initial: true, }, { type: 'confirm', name: 'withStories', message: 'Generate Storybook story?', initial: true, }, { type: 'confirm', name: 'withDocs', message: 'Generate README?', initial: true, }, ]) if (!response.name) { console.log(chalk.red('\nāŒ Cancelled\n')) process.exit(0) } try { await generateComponent(response as ComponentOptions) } catch (error) { console.error(chalk.red('\nāŒ Error generating component:'), error) process.exit(1) } } main() ``` ### Bulk Rename Script ```bash #!/bin/bash # scripts/bulk-rename.sh # Bulk rename components # Usage: ./scripts/bulk-rename.sh old-pattern new-pattern set -e OLD_PATTERN=$1 NEW_PATTERN=$2 if [ -z "$OLD_PATTERN" ] || [ -z "$NEW_PATTERN" ]; then echo "Usage: ./scripts/bulk-rename.sh old-pattern new-pattern" echo "Example: ./scripts/bulk-rename.sh UIButton Button" exit 1 fi echo "šŸ”„ Renaming $OLD_PATTERN → $NEW_PATTERN" echo "" # Find all files containing the old pattern FILES=$(grep -rl "$OLD_PATTERN" src/) if [ -z "$FILES" ]; then echo "āŒ No files found containing '$OLD_PATTERN'" exit 0 fi echo "Files to update:" echo "$FILES" echo "" read -p "Continue? (y/n) " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "āŒ Cancelled" exit 0 fi # Replace in file contents for file in $FILES; do sed -i "" "s/$OLD_PATTERN/$NEW_PATTERN/g" "$file" echo "āœ“ Updated $file" done # Rename files find src/ -name "*$OLD_PATTERN*" | while read file; do new_file=$(echo "$file" | sed "s/$OLD_PATTERN/$NEW_PATTERN/g") mv "$file" "$new_file" echo "āœ“ Renamed $file → $new_file" done echo "" echo "✨ Rename complete!" ``` ### Code Formatter Script ```typescript #!/usr/bin/env node // scripts/format-all.ts import { exec } from 'child_process' import { promisify } from 'util' import ora from 'ora' import chalk from 'chalk' const execAsync = promisify(exec) interface FormatOptions { fix: boolean check: boolean staged: boolean } async function formatCode(options: FormatOptions) { const { fix, check, staged } = options console.log(chalk.cyan.bold('\nšŸŽØ Code Formatter\n')) // Get files to format let files = 'src/**/*.{ts,tsx,js,jsx,css,scss,json,md}' if (staged) { const spinner = ora('Getting staged files...').start() try { const { stdout } = await execAsync('git diff --cached --name-only --diff-filter=ACMR') files = stdout .split('\n') .filter((f) => /\.(ts|tsx|js|jsx|css|scss|json|md)$/.test(f)) .join(' ') if (!files) { spinner.succeed('No staged files to format') return } spinner.succeed(`Found ${files.split(' ').length} staged files`) } catch (error) { spinner.fail('Failed to get staged files') throw error } } // Run Prettier const prettierSpinner = ora('Running Prettier...').start() try { const prettierCmd = check ? `prettier --check ${files}` : `prettier --write ${files}` await execAsync(prettierCmd) prettierSpinner.succeed('Prettier complete') } catch (error) { prettierSpinner.fail('Prettier found issues') if (!fix) { console.log(chalk.yellow('\nRun with --fix to auto-fix issues')) } throw error } // Run ESLint const eslintSpinner = ora('Running ESLint...').start() try { const eslintCmd = fix ? `eslint ${files.replace(/\{.*\}/, '{ts,tsx,js,jsx}')} --fix` : `eslint ${files.replace(/\{.*\}/, '{ts,tsx,js,jsx}')}` await execAsync(eslintCmd) eslintSpinner.succeed('ESLint complete') } catch (error) { eslintSpinner.fail('ESLint found issues') if (!fix) { console.log(chalk.yellow('\nRun with --fix to auto-fix issues')) } throw error } console.log(chalk.green.bold('\n✨ Formatting complete!\n')) } // CLI const args = process.argv.slice(2) const options: FormatOptions = { fix: args.includes('--fix'), check: args.includes('--check'), staged: args.includes('--staged'), } formatCode(options).catch((error) => { console.error(chalk.red('\nāŒ Formatting failed\n')) process.exit(1) }) ``` ### Migration Script ```typescript #!/usr/bin/env node // scripts/migrate-to-tailwind.ts import fs from 'fs/promises' import path from 'path' import { glob } from 'glob' import chalk from 'chalk' // CSS to Tailwind class mappings const cssToTailwind: Record = { 'display: flex': 'flex', 'flex-direction: column': 'flex-col', 'justify-content: center': 'justify-center', 'align-items: center': 'items-center', 'padding: 1rem': 'p-4', 'margin: 1rem': 'm-4', 'background-color: #3b82f6': 'bg-blue-500', 'color: white': 'text-white', 'font-weight: bold': 'font-bold', 'border-radius: 0.5rem': 'rounded-lg', } async function migrateToTailwind() { console.log(chalk.cyan.bold('\nšŸŽØ Migrating to Tailwind CSS\n')) // Find all component files const files = await glob('src/components/**/*.tsx') let totalChanges = 0 for (const file of files) { let content = await fs.readFile(file, 'utf-8') let changes = 0 // Find style objects const styleRegex = /style=\{\{([^}]+)\}\}/g const matches = content.matchAll(styleRegex) for (const match of matches) { const styleContent = match[1] const classes: string[] = [] // Convert each CSS property for (const [css, tailwind] of Object.entries(cssToTailwind)) { if (styleContent.includes(css)) { classes.push(tailwind) changes++ } } if (classes.length > 0) { // Replace style with className const replacement = `className="${classes.join(' ')}"` content = content.replace(match[0], replacement) } } if (changes > 0) { await fs.writeFile(file, content) console.log(chalk.green(`āœ“ ${file} (${changes} changes)`)) totalChanges += changes } } console.log(chalk.green.bold(`\n✨ Migration complete! (${totalChanges} total changes)\n`)) } migrateToTailwind().catch((error) => { console.error(chalk.red('\nāŒ Migration failed:'), error) process.exit(1) }) ``` ## Best Practices ### Script Design 1. **Single Responsibility**: One script, one task 2. **Idempotent**: Safe to run multiple times 3. **Dry Run**: Preview changes before applying 4. **Validation**: Validate inputs and state 5. **Error Handling**: Graceful error messages ### CLI UX 1. **Clear Messages**: What's happening and why 2. **Progress Indicators**: Show progress for long tasks 3. **Confirmation Prompts**: Ask before destructive actions 4. **Colorized Output**: Use colors meaningfully 5. **Help Text**: Always provide --help ### Code Quality 1. **TypeScript**: Type-safe scripts 2. **Error Recovery**: Handle failures gracefully 3. **Logging**: Log important operations 4. **Testing**: Test scripts with various inputs 5. **Documentation**: Document usage and options ### Performance 1. **Parallel Operations**: Run independent tasks concurrently 2. **Caching**: Cache expensive operations 3. **Incremental**: Process only changed files 4. **Streaming**: Stream large file operations 5. **Debouncing**: Avoid duplicate operations ## When to Use This Skill Activate this skill when you need to: - Generate new components with boilerplate - Create CLI tools for developers - Automate repetitive tasks - Migrate code between patterns - Scaffold new projects - Bulk update code across files - Transform code automatically - Set up automation workflows - Build custom dev tools - Optimize development workflow ## Output Format When creating automation scripts, provide: 1. **Complete Script**: Production-ready code 2. **Installation Instructions**: Dependencies and setup 3. **Usage Guide**: How to run the script 4. **Options Reference**: All available flags/options 5. **Examples**: Common use cases 6. **Error Handling**: How errors are reported Always build scripts that save time, prevent errors, and improve developer experience.