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
JavaScript
/**
* 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;