@sprouted/create-vibes
Version:
Create a new Vibes monorepo
112 lines ⢠4.58 kB
JavaScript
import { TemplateEngine } from '../template-engine/index.js';
import path from 'path';
import fs from 'fs-extra';
import chalk from 'chalk';
import inquirer from 'inquirer';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export class CreateFeatureCommand {
constructor() {
this.engine = new TemplateEngine();
}
async execute(options) {
const projectRoot = options.projectRoot || process.cwd();
const featuresDir = path.join(projectRoot, 'features');
// Convert feature name to kebab-case
const featureName = options.name.toLowerCase().replace(/\s+/g, '-');
// Check for spec first (spec-driven development)
const specPath = path.join(projectRoot, 'specs/features', `${featureName}.md`);
const hasSpec = await fs.pathExists(specPath);
if (!hasSpec) {
console.log(chalk.yellow('\nš No specification found for this feature!'));
console.log(chalk.cyan('š” Create a spec first with:'));
console.log(chalk.cyan(` vibe spec ${featureName}\n`));
console.log(chalk.yellow('Spec-driven development ensures clarity before implementation.\n'));
process.exit(1);
}
// Ensure features directory exists
await fs.ensureDir(featuresDir);
const featurePath = path.join(featuresDir, featureName);
// Check if feature already exists
if (await fs.pathExists(featurePath)) {
throw new Error(`Feature '${featureName}' already exists!`);
}
console.log(chalk.blue(`š Creating feature: ${featureName}`));
// Get template path
const templatePath = path.join(__dirname, '../../templates/features/todo');
// Prepare variables
const variables = {
featureName,
includeApi: options.includeApi ?? true,
includeUi: options.includeUi ?? true,
uiFramework: options.uiFramework || 'react',
features: options.features || []
};
try {
// Generate from template
await this.engine.generateFromTemplate(templatePath, variables, {
outputPath: featurePath
});
console.log(chalk.green(`ā
Feature '${featureName}' created successfully!`));
console.log('\nNext steps:');
console.log(chalk.cyan(` 1. cd features/${featureName}`));
console.log(chalk.cyan(` 2. pnpm install`));
console.log(chalk.cyan(` 3. Start coding! š`));
}
catch (error) {
console.error(chalk.red('Failed to create feature:'), error);
throw error;
}
}
async interactivePrompt() {
const answers = await inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'š What should we call this feature?',
validate: (input) => {
if (!input.trim())
return 'Feature name is required';
if (!/^[a-zA-Z0-9-\s]+$/.test(input)) {
return 'Feature name can only contain letters, numbers, hyphens, and spaces';
}
return true;
}
},
{
type: 'confirm',
name: 'includeApi',
message: 'Include API layer?',
default: true
},
{
type: 'confirm',
name: 'includeUi',
message: 'Include UI layer?',
default: true
},
{
type: 'list',
name: 'uiFramework',
message: 'Which UI framework?',
choices: ['react', 'react-native'],
default: 'react',
when: (answers) => answers.includeUi
},
{
type: 'checkbox',
name: 'features',
message: 'Additional features to include:',
choices: [
{ name: 'React Router', value: 'react-router' },
{ name: 'Expo Router', value: 'expo-router' },
{ name: 'Tailwind CSS', value: 'tailwindcss' },
{ name: 'NativeWind', value: 'nativewind' }
],
when: (answers) => answers.includeUi
}
]);
return answers;
}
}
//# sourceMappingURL=create-feature.js.map