UNPKG

shipdeck

Version:

Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.

619 lines (526 loc) • 20.4 kB
/** * Prep Runner for Shipdeck Ultimate * Executes 7-step project initialization sequence * Now uses embedded templates - no external files needed! */ const fs = require('fs'); const path = require('path'); const { getAllTemplates } = require('./embedded-templates'); class PrepRunner { constructor() { this.prepOutputPath = path.join(process.cwd(), 'prep'); // Load embedded templates and map to prep steps const templates = getAllTemplates(); this.prepSteps = templates.map(template => ({ order: template.order, name: template.name, description: template.description, output: this.getOutputFilename(template.name), generator: template.generator })); this.currentStep = 0; this.prepResults = {}; } /** * Get all prep steps */ getSteps() { return this.prepSteps; } /** * Get output filename for a step */ getOutputFilename(name) { const fileMap = { 'generate_master_idea': 'master_idea.md', 'generate_app_name': 'app_name.md', 'generate_logo': 'logo_generation.md', 'generate_pages_and_functionality': 'app_pages_and_functionality.md', 'generate_wireframe': 'wireframe.md', 'generate_initial_data_models': 'initial_data_schema.md', 'generate_system_design': 'system_architecture.md' }; return fileMap[name] || `${name}.md`; } /** * Run the complete prep sequence * @param {Object} context - Initial project context * @returns {Object} All prep outputs */ async runFullPrep(context) { console.log('šŸš€ Starting 7-Step Project Initialization\n'); // Ensure prep directory exists if (!fs.existsSync(this.prepOutputPath)) { fs.mkdirSync(this.prepOutputPath, { recursive: true }); } // Save initial context this.saveContext(context); // Execute each prep step for (const step of this.prepSteps) { await this.executeStep(step, context); } // Generate summary const summary = this.generateSummary(); this.saveSummary(summary); console.log('\nāœ… Project initialization complete!'); console.log(`šŸ“ All outputs saved to: ${this.prepOutputPath}`); return this.prepResults; } /** * Execute a single prep step * @param {Object} step - Step configuration * @param {Object} context - Current context */ async executeStep(step, context) { this.currentStep = step.order; console.log(`\nšŸ“ Step ${step.order}: ${step.description}`); console.log('─'.repeat(50)); try { // Enrich context with previous results const enrichedContext = { ...context, ...this.prepResults, currentStep: step.order, stepName: step.name }; // Use embedded generator function directly let output; if (step.generator && typeof step.generator === 'function') { output = step.generator(enrichedContext); } else { // Fallback to generateStepOutput for backward compatibility output = await this.generateStepOutput(step, {}, enrichedContext); } // Save output const outputPath = path.join(this.prepOutputPath, step.output); fs.writeFileSync(outputPath, output, 'utf8'); // Store result for next steps this.prepResults[step.name] = { content: output, path: outputPath, timestamp: new Date().toISOString() }; console.log(`āœ… Saved to: ${step.output}`); } catch (error) { console.error(`āŒ Failed step ${step.order}:`, error.message); throw error; } } /** * Generate output for a specific step * @param {Object} step - Step configuration * @param {Object} template - Filled template * @param {Object} context - Enriched context * @returns {string} Generated content */ async generateStepOutput(step, template, context) { const generators = { generate_master_idea: () => this.generateMasterIdea(context), generate_app_name: () => this.generateAppName(context), generate_logo: () => this.generateLogoPrompt(context), generate_app_pages_and_functionality: () => this.generatePagesAndFunctionality(context), generate_wireframe: () => this.generateWireframe(context), generate_initial_data_models: () => this.generateDataModels(context), generate_system_design: () => this.generateSystemDesign(context) }; const generator = generators[step.name]; if (generator) { return await generator(); } // Default output if no specific generator return this.formatTemplateOutput(template); } /** * Generate master idea document */ async generateMasterIdea(context) { const output = []; output.push('# Master Idea\n'); output.push(`## Project: ${context.projectName || 'Unnamed Project'}\n`); output.push(`Generated: ${new Date().toISOString()}\n`); output.push('\n## Core Concept\n'); output.push(context.description || 'AI-powered application for rapid development\n'); output.push('\n## Problem Statement\n'); output.push(context.problem || 'Development takes too long and costs too much\n'); output.push('\n## Solution\n'); output.push(context.solution || 'Automated development with AI agents\n'); output.push('\n## Target Audience\n'); output.push(context.audience || 'Developers, startups, and agencies\n'); output.push('\n## Unique Value Proposition\n'); output.push('- Ship MVPs in 48 hours\n'); output.push('- 10x faster development\n'); output.push('- Production-ready code\n'); output.push('- Zero hallucinations\n'); output.push('\n## Success Metrics\n'); output.push('- Time to MVP: < 48 hours\n'); output.push('- Code quality: 100% linted and tested\n'); output.push('- User satisfaction: > 95%\n'); output.push('- Development cost: 80% reduction\n'); return output.join(''); } /** * Generate app name suggestions */ async generateAppName(context) { const masterIdea = this.prepResults.generate_master_idea?.content || ''; const output = []; output.push('# App Name Generation\n\n'); output.push('## Selected Name\n'); output.push(`**${context.projectName || 'ShipDeck'}**\n\n`); output.push('## Alternative Names\n'); const alternatives = [ 'RapidShip', 'CodeVelocity', 'ShipFast', 'DevRocket', 'SprintShip' ]; alternatives.forEach(name => output.push(`- ${name}\n`)); output.push('\n## Name Analysis\n'); output.push('- **Memorable**: Easy to remember and spell\n'); output.push('- **Descriptive**: Conveys speed and shipping\n'); output.push('- **Available**: Domain and social handles available\n'); output.push('- **Scalable**: Works for future expansion\n'); output.push('\n## Brand Personality\n'); output.push('- Fast and efficient\n'); output.push('- Developer-friendly\n'); output.push('- Reliable and professional\n'); output.push('- Innovation-focused\n'); return output.join(''); } /** * Generate logo design prompt */ async generateLogoPrompt(context) { const appName = this.prepResults.generate_app_name?.content || ''; const output = []; output.push('# Logo Generation Guide\n\n'); output.push('## ChatGPT/DALL-E Prompt\n'); output.push('```\n'); output.push('Create a modern, minimalist logo for "ShipDeck" - a developer tool for rapid MVP development.\n'); output.push('Style: Clean, professional, tech-focused\n'); output.push('Colors: Blue gradient (#0066CC to #00AAFF) with white accents\n'); output.push('Elements: Abstract ship or rocket, speed lines, code brackets\n'); output.push('Format: Square with rounded corners, works at all sizes\n'); output.push('Feel: Fast, reliable, innovative\n'); output.push('```\n\n'); output.push('## Logo Variations Needed\n'); output.push('1. **Primary Logo**: Full color with text\n'); output.push('2. **Icon Only**: For app icons and favicons\n'); output.push('3. **Monochrome**: Black and white version\n'); output.push('4. **Dark Mode**: Inverted colors for dark backgrounds\n'); output.push('5. **Social Media**: Optimized for profile pictures\n\n'); output.push('## Usage Guidelines\n'); output.push('- Minimum size: 32x32px\n'); output.push('- Clear space: 0.5x logo height on all sides\n'); output.push('- Never stretch or distort\n'); output.push('- Maintain aspect ratio\n'); return output.join(''); } /** * Generate pages and functionality mapping */ async generatePagesAndFunctionality(context) { const output = []; output.push('# App Pages and Functionality\n\n'); output.push('## Core Pages\n\n'); const pages = [ { name: 'Landing Page', route: '/', purpose: 'Convert visitors to users', features: ['Hero section', 'Feature list', 'Pricing', 'Testimonials', 'CTA'] }, { name: 'Dashboard', route: '/dashboard', purpose: 'Central command center', features: ['Project overview', 'Recent activity', 'Quick actions', 'Stats'] }, { name: 'Projects', route: '/projects', purpose: 'Manage all projects', features: ['Project list', 'Create new', 'Search/filter', 'Templates'] }, { name: 'Agents', route: '/agents', purpose: 'AI agent management', features: ['Agent catalog', 'Usage stats', 'Custom agents', 'Configurations'] }, { name: 'Settings', route: '/settings', purpose: 'User preferences', features: ['Profile', 'Billing', 'API keys', 'Notifications'] } ]; for (const page of pages) { output.push(`### ${page.name}\n`); output.push(`- **Route**: ${page.route}\n`); output.push(`- **Purpose**: ${page.purpose}\n`); output.push('- **Features**:\n'); page.features.forEach(f => output.push(` - ${f}\n`)); output.push('\n'); } output.push('## User Flows\n\n'); output.push('### Onboarding Flow\n'); output.push('1. Sign up → Email verification\n'); output.push('2. Profile setup → Choose plan\n'); output.push('3. First project → Tutorial\n'); output.push('4. Activate agents → Ship first feature\n\n'); output.push('### Development Flow\n'); output.push('1. Create project → Select template\n'); output.push('2. Configure agents → Set parameters\n'); output.push('3. Execute workflow → Monitor progress\n'); output.push('4. Review output → Deploy\n\n'); return output.join(''); } /** * Generate wireframe specifications */ async generateWireframe(context) { const output = []; output.push('# Wireframe Specifications\n\n'); output.push('## Layout Structure\n\n'); output.push('```\n'); output.push('ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”\n'); output.push('│ Header (Logo | Nav | User Menu) │\n'); output.push('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n'); output.push('│ │ │\n'); output.push('│ Sidebar │ Main Content Area │\n'); output.push('│ (Nav) │ │\n'); output.push('│ │ │\n'); output.push('│ │ │\n'); output.push('ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤\n'); output.push('│ Footer (Links | Status) │\n'); output.push('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n'); output.push('```\n\n'); output.push('## Component Library\n\n'); output.push('### Navigation\n'); output.push('- Top navigation bar with logo\n'); output.push('- Collapsible sidebar for main sections\n'); output.push('- Breadcrumb navigation\n'); output.push('- Mobile hamburger menu\n\n'); output.push('### Cards\n'); output.push('- Project cards with status indicators\n'); output.push('- Agent cards with capabilities\n'); output.push('- Stat cards for metrics\n'); output.push('- Feature cards with icons\n\n'); output.push('### Forms\n'); output.push('- Multi-step project creation\n'); output.push('- Agent configuration panels\n'); output.push('- Settings forms with validation\n'); output.push('- Search and filter inputs\n\n'); output.push('### Feedback\n'); output.push('- Progress bars for operations\n'); output.push('- Toast notifications\n'); output.push('- Loading skeletons\n'); output.push('- Empty states with CTAs\n\n'); return output.join(''); } /** * Generate initial data models */ async generateDataModels(context) { const output = []; output.push('# Initial Data Schema\n\n'); output.push('## Core Models\n\n'); output.push('### User\n'); output.push('```typescript\n'); output.push('interface User {\n'); output.push(' id: string;\n'); output.push(' email: string;\n'); output.push(' name: string;\n'); output.push(' tier: "free" | "pro" | "team";\n'); output.push(' createdAt: Date;\n'); output.push(' updatedAt: Date;\n'); output.push(' settings: UserSettings;\n'); output.push('}\n'); output.push('```\n\n'); output.push('### Project\n'); output.push('```typescript\n'); output.push('interface Project {\n'); output.push(' id: string;\n'); output.push(' userId: string;\n'); output.push(' name: string;\n'); output.push(' description: string;\n'); output.push(' status: "planning" | "active" | "shipped";\n'); output.push(' template: string;\n'); output.push(' createdAt: Date;\n'); output.push(' updatedAt: Date;\n'); output.push('}\n'); output.push('```\n\n'); output.push('### Agent\n'); output.push('```typescript\n'); output.push('interface Agent {\n'); output.push(' id: string;\n'); output.push(' name: string;\n'); output.push(' category: string;\n'); output.push(' description: string;\n'); output.push(' tier: "free" | "pro" | "team";\n'); output.push(' capabilities: string[];\n'); output.push(' config: AgentConfig;\n'); output.push('}\n'); output.push('```\n\n'); output.push('### Workflow\n'); output.push('```typescript\n'); output.push('interface Workflow {\n'); output.push(' id: string;\n'); output.push(' projectId: string;\n'); output.push(' name: string;\n'); output.push(' status: "pending" | "running" | "completed" | "failed";\n'); output.push(' steps: WorkflowStep[];\n'); output.push(' results: any;\n'); output.push(' startedAt: Date;\n'); output.push(' completedAt: Date;\n'); output.push('}\n'); output.push('```\n\n'); output.push('## Relationships\n\n'); output.push('- User → has many → Projects\n'); output.push('- Project → has many → Workflows\n'); output.push('- Workflow → uses many → Agents\n'); output.push('- Agent → belongs to → Category\n\n'); return output.join(''); } /** * Generate system architecture design */ async generateSystemDesign(context) { const output = []; output.push('# System Architecture\n\n'); output.push('## Technology Stack\n\n'); output.push('### Frontend\n'); output.push('- **Framework**: Next.js 15 (App Router)\n'); output.push('- **Language**: TypeScript\n'); output.push('- **Styling**: Tailwind CSS + shadcn/ui\n'); output.push('- **State**: Zustand + React Query\n'); output.push('- **Forms**: React Hook Form + Zod\n\n'); output.push('### Backend\n'); output.push('- **API**: Next.js API Routes\n'); output.push('- **Database**: PostgreSQL + Drizzle ORM\n'); output.push('- **Auth**: NextAuth.js\n'); output.push('- **Queue**: BullMQ + Redis\n'); output.push('- **Storage**: S3-compatible\n\n'); output.push('### Infrastructure\n'); output.push('- **Hosting**: Vercel + Railway\n'); output.push('- **CDN**: Cloudflare\n'); output.push('- **Monitoring**: Sentry + Posthog\n'); output.push('- **CI/CD**: GitHub Actions\n\n'); output.push('## Architecture Patterns\n\n'); output.push('### API Design\n'); output.push('- RESTful endpoints for CRUD\n'); output.push('- GraphQL for complex queries\n'); output.push('- WebSocket for real-time updates\n'); output.push('- Rate limiting and caching\n\n'); output.push('### Security\n'); output.push('- JWT authentication\n'); output.push('- Role-based access control\n'); output.push('- Input validation and sanitization\n'); output.push('- HTTPS everywhere\n'); output.push('- Environment variables for secrets\n\n'); output.push('### Scalability\n'); output.push('- Horizontal scaling with load balancer\n'); output.push('- Database connection pooling\n'); output.push('- Redis caching layer\n'); output.push('- CDN for static assets\n'); output.push('- Background job processing\n\n'); return output.join(''); } /** * Create default template for a step */ createDefaultTemplate(step) { return { name: step.name, description: step.description, variables: { projectName: null, description: null }, sections: {}, steps: [] }; } /** * Format template output to markdown */ formatTemplateOutput(template) { const output = []; if (template.name) { output.push(`# ${template.name}\n\n`); } if (template.description) { output.push(`${template.description}\n\n`); } for (const [section, content] of Object.entries(template.sections || {})) { output.push(`## ${section.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}\n\n`); output.push(`${content}\n\n`); } if (template.steps && template.steps.length > 0) { output.push('## Steps\n\n'); template.steps.forEach((step, i) => { output.push(`${i + 1}. ${step.action || step}\n`); }); } return output.join(''); } /** * Save initial context */ saveContext(context) { const contextPath = path.join(this.prepOutputPath, 'context.json'); fs.writeFileSync(contextPath, JSON.stringify(context, null, 2)); } /** * Generate summary of all prep outputs */ generateSummary() { const summary = { timestamp: new Date().toISOString(), steps: this.prepSteps.length, outputs: Object.keys(this.prepResults), files: [] }; for (const [key, result] of Object.entries(this.prepResults)) { summary.files.push({ step: key, path: result.path, size: fs.statSync(result.path).size, created: result.timestamp }); } return summary; } /** * Save summary to file */ saveSummary(summary) { const summaryPath = path.join(this.prepOutputPath, 'summary.json'); fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2)); } /** * Resume from a specific step */ async resumeFromStep(stepNumber, context) { const stepsToRun = this.prepSteps.filter(s => s.order >= stepNumber); console.log(`šŸ“ Resuming from step ${stepNumber}...`); for (const step of stepsToRun) { await this.executeStep(step, context); } return this.prepResults; } /** * Get current status */ getStatus() { return { currentStep: this.currentStep, totalSteps: this.prepSteps.length, completed: Object.keys(this.prepResults).length, outputPath: this.prepOutputPath }; } } module.exports = PrepRunner;