with one click
automation-scripts-generator
Generate automation scripts for component creation, bulk operations, code transformation, project scaffolding, and custom CLI tools for UI library development workflows
Menu
Generate automation scripts for component creation, bulk operations, code transformation, project scaffolding, and custom CLI tools for UI library development workflows
| 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"] |
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.
Identify Task
Design Solution
Choose Tools
Build Core Logic
Add CLI Interface
Test Thoroughly
Document Script
Add to Workflow
Optimize
#!/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 (
<div {...props}>
{children}
</div>
)
}`,
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}>
<div {...props}>{children}</div>
</${name}Context.Provider>
)
}
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 <div>{children}</div>
}`,
polymorphic: `import React from 'react'
import { ${name}Props } from './${name}.types'
export function ${name}<C extends React.ElementType = 'div'>({
as,
children,
...props
}: ${name}Props<C>) {
const Component = as || 'div'
return <Component {...props}>{children}</Component>
}`,
}
return templates[type] || templates.basic
}
function generateTypesCode(name: string): string {
return `import React from 'react'
export interface ${name}Props extends React.HTMLAttributes<HTMLDivElement> {
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</${name}>)
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<typeof ${name}> = {
title: 'Components/${name}',
component: ${name},
tags: ['autodocs'],
}
export default meta
type Story = StoryObj<typeof ${name}>
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
</${name}>
)
}
\`\`\`
## Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | The content |
## Examples
### Basic Usage
\`\`\`tsx
<${name}>Hello World</${name}>
\`\`\`
`
}
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()
#!/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!"
#!/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)
})
#!/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<string, string> = {
'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)
})
Activate this skill when you need to:
When creating automation scripts, provide:
Always build scripts that save time, prevent errors, and improve developer experience.
Writes, edits, and creates dbt models following best practices. Use when user needs to create new dbt SQL models, update existing models, or convert raw SQL to dbt format. Handles staging, intermediate, and mart models with proper config blocks, CTEs, and documentation.
Apply Domain-Driven Design, Clean Architecture, CQRS, and command/query patterns to code reviews and feature design. Use when analyzing or designing code in Application, Service, Infrastructure, DataAccess, Validation, Domain, or Functions projects, or when addressing architectural concerns, layering, mapping, entities, value objects, repositories, or validators in the Rome Repair Order Service.
Analyzes and refactors code using Domain-Driven Design principles. Use when refactoring domain models, identifying DDD anti-patterns, improving domain clarity, or applying tactical/strategic DDD patterns.
Guide for DDD strategic design - analyzing domains through structured questioning, conducting stakeholder interviews (PM/domain experts/users), and producing Bounded Context analysis, Context Maps, and Ubiquitous Language. Use when user needs help understanding domain boundaries, planning domain interviews, or structuring DDD strategic artifacts.
Win competitive rounds: run a clean process, deliver value previews before asking, coordinate partners, and manage timelines. Use when you're trying to close a 'must win' deal against other funds.
End-to-end associate workflow with time-boxed gates: thesis -> sourcing -> meetings -> diligence -> memo, ending with either IC-ready memo or explicit kill decision. Use when you need to run the full pipeline for a sector or a specific deal.