Skip to main content
Glama
project-generator.ts11 kB
import { GoogleGenerativeAI } from '@google/generative-ai'; interface ProjectArgs { projectName: string; framework?: 'react' | 'vue' | 'svelte'; typescript?: boolean; components?: string[]; template?: 'basic' | 'dashboard' | 'landing' | 'blog' | 'auth'; } // Initialize Gemini AI const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ''); // Project templates const PROJECT_TEMPLATES = { basic: { description: 'Basic setup with essential components', components: ['button', 'card'], pages: ['Home'] }, dashboard: { description: 'Dashboard layout with navigation and data display', components: ['button', 'card', 'table', 'nav-menu', 'sidebar', 'avatar', 'badge', 'chart'], pages: ['Dashboard', 'Analytics', 'Settings'] }, landing: { description: 'Landing page with hero section and features', components: ['button', 'card', 'hero', 'features', 'footer'], pages: ['Home', 'About', 'Contact'] }, blog: { description: 'Blog layout with post listing and detail pages', components: ['button', 'card', 'breadcrumb', 'avatar', 'separator'], pages: ['Home', 'Blog', 'Post', 'About'] }, auth: { description: 'Authentication pages with forms', components: ['button', 'card', 'input', 'label', 'form', 'alert'], pages: ['Login', 'Register', 'ForgotPassword', 'Dashboard'] } }; // Vite configs for different frameworks const VITE_CONFIGS = { react: `import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import path from 'path' export default defineConfig({ plugins: [react(), tailwindcss()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, })`, vue: `import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import tailwindcss from '@tailwindcss/vite' import path from 'path' export default defineConfig({ plugins: [vue(), tailwindcss()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, })`, svelte: `import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' import tailwindcss from '@tailwindcss/vite' import path from 'path' export default defineConfig({ plugins: [svelte(), tailwindcss()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, })` }; const PACKAGE_CONFIGS = { react: { dependencies: [ 'react', 'react-dom', '@radix-ui/react-slot', 'class-variance-authority', 'clsx', 'tailwind-merge', 'lucide-react' ], devDependencies: [ '@types/react', '@types/react-dom', '@vitejs/plugin-react', 'vite', 'typescript', '@tailwindcss/vite', 'tailwindcss', 'autoprefixer' ] }, vue: { dependencies: [ 'vue', 'class-variance-authority', 'clsx', 'tailwind-merge', 'lucide-vue-next' ], devDependencies: [ '@vitejs/plugin-vue', 'vite', 'typescript', 'vue-tsc', '@tailwindcss/vite', 'tailwindcss', 'autoprefixer' ] }, svelte: { dependencies: [ 'svelte', 'tailwind-variants', 'bits-ui', 'clsx', 'tailwind-merge', 'lucide-svelte' ], devDependencies: [ '@sveltejs/vite-plugin-svelte', 'vite', 'typescript', 'svelte-check', '@tailwindcss/vite', 'tailwindcss', 'autoprefixer' ] } }; export async function createProject(args: ProjectArgs) { try { const { projectName, framework = 'react', typescript = true, components = [], template = 'basic' } = args; const templateConfig = PROJECT_TEMPLATES[template]; const allComponents = [...new Set([...templateConfig.components, ...components])]; let content = `# ${projectName} - ${framework} Project Setup\n\n`; content += `Generated project setup for a ${templateConfig.description}.\n\n`; // 1. Package.json const packageConfig = PACKAGE_CONFIGS[framework]; const packageJson = { name: projectName.toLowerCase().replace(/\s+/g, '-'), private: true, version: '0.1.0', type: 'module', scripts: { dev: 'vite', build: typescript ? 'tsc && vite build' : 'vite build', preview: 'vite preview', lint: 'eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0' }, dependencies: Object.fromEntries(packageConfig.dependencies.map(dep => [dep, 'latest'])), devDependencies: Object.fromEntries(packageConfig.devDependencies.map(dep => [dep, 'latest'])) }; content += `## 1. Package Configuration\n\n\`\`\`json\n${JSON.stringify(packageJson, null, 2)}\n\`\`\`\n\n`; // 2. Vite config content += `## 2. Vite Configuration\n\n\`\`\`${typescript ? 'ts' : 'js'}\n${VITE_CONFIGS[framework]}\n\`\`\`\n\n`; // 3. Tailwind config const tailwindConfig = `/** @type {import('tailwindcss').Config} */ export default { content: ['./index.html', './src/**/*.{${framework === 'svelte' ? 'js,ts,svelte' : 'js,ts,jsx,tsx'}}'], theme: { extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, }, }, plugins: [], }`; content += `## 3. Tailwind Configuration\n\n\`\`\`js\n${tailwindConfig}\n\`\`\`\n\n`; // 4. CSS with CSS variables const cssContent = `@import "tailwindcss"; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96%; --secondary-foreground: 222.2 84% 4.9%; --muted: 210 40% 96%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96%; --accent-foreground: 222.2 84% 4.9%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; --primary: 217.2 91.2% 59.8%; --primary-foreground: 222.2 84% 4.9%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 224.3 76.3% 94.1%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } }`; content += `## 4. Global CSS\n\n\`\`\`css\n${cssContent}\n\`\`\`\n\n`; // 5. Utility functions const utilsContent = framework === 'react' ? `import { type ClassValue, clsx } from "clsx" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }` : `import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }`; content += `## 5. Utility Functions\n\n\`\`\`${typescript ? 'ts' : 'js'}\n${utilsContent}\n\`\`\`\n\n`; // 6. Installation steps content += `## 6. Installation Steps\n\n`; content += `\`\`\`bash\n# 1. Create project directory\nmkdir ${projectName.toLowerCase().replace(/\s+/g, '-')}\ncd ${projectName.toLowerCase().replace(/\s+/g, '-')}\n\n`; content += `# 2. Initialize project\nnpm create vite@latest . --template ${framework}${typescript ? '-ts' : ''}\n\n`; content += `# 3. Install dependencies\nnpm install ${packageConfig.dependencies.join(' ')}\n\n`; content += `# 4. Install dev dependencies\nnpm install -D ${packageConfig.devDependencies.join(' ')}\n\n`; content += `# 5. Start development server\nnpm run dev\n\`\`\`\n\n`; // 7. Components to implement content += `## 7. Recommended Components\n\n`; content += `The following shadcn/ui components are recommended for this template:\n\n`; allComponents.forEach(comp => { content += `- ${comp}\n`; }); content += `\n`; // 8. AI-generated project structure if (process.env.GEMINI_API_KEY) { try { const model = genAI.getGenerativeModel({ model: 'gemini-pro' }); const prompt = `Generate a detailed project structure and initial implementation plan for a ${framework} project with the following requirements: Project Name: ${projectName} Template: ${template} (${templateConfig.description}) Framework: ${framework} TypeScript: ${typescript} Components needed: ${allComponents.join(', ')} Please provide: 1. Detailed folder structure 2. Key files to create initially 3. Development workflow recommendations 4. Best practices for this setup 5. Next steps after setup`; const result = await model.generateContent(prompt); const aiRecommendations = result.response.text(); content += `## 8. AI-Generated Project Plan\n\n${aiRecommendations}\n\n`; } catch (error) { console.warn('Failed to get AI recommendations:', error); } } return { content: [ { type: 'text', text: content } ] }; } catch (error) { return { content: [ { type: 'text', text: `Error creating project: ${error instanceof Error ? error.message : 'Unknown error'}` } ] }; } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Tai-DT/mcp-tailwind-gemini'

If you have feedback or need assistance with the MCP directory API, please join our Discord server