mcp-product-manager
Version:
MCP Orchestrator for task and project management with web interface
990 lines (964 loc) • 53.1 kB
JavaScript
/**
* SPAPS Integration Wizard - Step-by-step SDK integration guide
*/
import fs from 'fs/promises';
import path from 'path';
import { exec } from 'child_process';
import { promisify } from 'util';
class SPAPSIntegrationWizard {
state;
STATE_DIR = '.spaps-wizard';
TOTAL_STEPS = 12;
constructor(params) {
this.state = {
projectPath: params.project_path || process.cwd(),
projectType: params.project_type || 'existing',
currentStep: params.current_step || 1,
totalSteps: this.TOTAL_STEPS,
completedSteps: [],
detectedFramework: null,
hasAuth: false,
hasPayments: false,
uiLibrary: null,
refactoringTasks: [],
validationResults: {},
startedAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
sessionId: `wizard_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`
};
}
async initialize() {
// Load existing state if available
await this.loadState();
// Analyze the project
const analysis = await this.analyzeProject();
// Update state with analysis results
this.state.detectedFramework = analysis.framework;
this.state.hasAuth = analysis.hasAuth;
this.state.hasPayments = analysis.hasPayments;
this.state.uiLibrary = analysis.uiLibrary;
// Determine refactoring needs for existing projects
if (this.state.projectType === 'existing') {
this.state.refactoringTasks = await this.identifyRefactoringNeeds(analysis);
}
// Save initial state
await this.saveState();
// Return first step
return this.getStep(1);
}
async nextStep() {
// Validate current step completion
const validation = await this.validateCurrentStep();
if (!validation.passed) {
return {
error: "Current step validation failed",
step: this.state.currentStep,
validation,
action_required: "Complete current step requirements before proceeding"
};
}
// Mark current step as completed
if (!this.state.completedSteps.includes(this.state.currentStep)) {
this.state.completedSteps.push(this.state.currentStep);
}
// Move to next step
this.state.currentStep++;
this.state.lastActivity = new Date().toISOString();
// Check if wizard is complete
if (this.state.currentStep > this.TOTAL_STEPS) {
await this.saveState();
return this.generateCompletionSummary();
}
await this.saveState();
return this.getStep(this.state.currentStep);
}
async getStatus() {
await this.loadState();
return {
session_id: this.state.sessionId,
project_path: this.state.projectPath,
project_type: this.state.projectType,
current_step: this.state.currentStep,
total_steps: this.state.totalSteps,
completed_steps: this.state.completedSteps,
progress_percentage: Math.round((this.state.completedSteps.length / this.TOTAL_STEPS) * 100),
detected_framework: this.state.detectedFramework,
has_auth: this.state.hasAuth,
has_payments: this.state.hasPayments,
ui_library: this.state.uiLibrary,
refactoring_needed: this.state.refactoringTasks.length > 0,
started_at: this.state.startedAt,
last_activity: this.state.lastActivity,
next_action: this.state.currentStep <= this.TOTAL_STEPS
? `Continue with step ${this.state.currentStep}: ${this.getStepTitle(this.state.currentStep)}`
: "Integration complete!"
};
}
async validateStep(stepNumber, validationData) {
const step = stepNumber || this.state.currentStep;
this.state.lastActivity = new Date().toISOString();
const validation = await this.performStepValidation(step, validationData);
this.state.validationResults[step] = validation;
await this.saveState();
return validation;
}
async analyzeProject() {
const projectPath = this.state.projectPath;
try {
// Check for package.json
const packageJsonPath = path.join(projectPath, 'package.json');
let packageJson = {};
try {
const content = await fs.readFile(packageJsonPath, 'utf-8');
packageJson = JSON.parse(content);
}
catch (error) {
// No package.json found - might be a new project
}
// Detect framework
let framework = 'unknown';
if (packageJson.dependencies?.next || packageJson.devDependencies?.next) {
// Check if it's App Router or Pages Router
const appDirExists = await this.fileExists(path.join(projectPath, 'app'));
const pagesDirExists = await this.fileExists(path.join(projectPath, 'pages'));
framework = appDirExists ? 'nextjs-app' : pagesDirExists ? 'nextjs-pages' : 'nextjs';
}
else if (packageJson.dependencies?.react || packageJson.devDependencies?.react) {
framework = 'react';
}
else if (packageJson.dependencies?.vue || packageJson.devDependencies?.vue) {
framework = 'vue';
}
// Detect existing auth
const hasAuth = !!(packageJson.dependencies?.['next-auth'] ||
packageJson.dependencies?.auth0 ||
packageJson.dependencies?.firebase ||
packageJson.dependencies?.supabase ||
await this.findInFiles(projectPath, /auth|login|signin/i));
// Detect existing payments
const hasPayments = !!(packageJson.dependencies?.stripe ||
packageJson.dependencies?.paypal ||
await this.findInFiles(projectPath, /payment|stripe|paypal|checkout/i));
// Detect UI library
let uiLibrary = null;
if (packageJson.dependencies?.['@mui/material'])
uiLibrary = 'mui';
else if (packageJson.dependencies?.['@chakra-ui/react'])
uiLibrary = 'chakra';
else if (packageJson.dependencies?.['@radix-ui/react-dialog'])
uiLibrary = 'shadcn';
else if (packageJson.dependencies?.antd)
uiLibrary = 'antd';
return {
framework,
hasAuth,
hasPayments,
uiLibrary,
packageJson,
isTypeScript: !!(packageJson.dependencies?.typescript || packageJson.devDependencies?.typescript)
};
}
catch (error) {
return {
framework: 'unknown',
hasAuth: false,
hasPayments: false,
uiLibrary: null,
packageJson: {},
isTypeScript: false
};
}
}
async identifyRefactoringNeeds(analysis) {
const tasks = [];
// Auth refactoring
if (analysis.hasAuth) {
tasks.push({
area: 'authentication',
reason: 'Replace existing auth with SPAPS authentication',
priority: 'high',
steps: [
'Backup existing auth implementation',
'Install SPAPS SDK alongside existing auth',
'Create parallel auth system with feature flag',
'Update login components to support both systems',
'Migrate user sessions gradually',
'Remove old auth system after validation'
]
});
}
// UI library compatibility - respect existing choices
if (analysis.uiLibrary) {
// Don't force UI changes on existing projects
console.error(`[Wizard] Detected existing UI library: ${analysis.uiLibrary}. Will adapt to existing styles.`);
}
return tasks;
}
getStep(stepNumber) {
const step = this.getStepDefinition(stepNumber);
return {
// Simple, clear step info
current_step: stepNumber,
total_steps: this.TOTAL_STEPS,
step_title: step.title,
progress: `${this.state.completedSteps.length}/${this.TOTAL_STEPS} completed`,
// Direct TodoWrite input - agent can use this directly
todos: step.todoList,
// Concrete files to create/modify
files_to_create: this.getFilesToCreate(stepNumber),
files_to_modify: this.getFilesToModify(stepNumber),
// Executable validation commands
validation_commands: this.getValidationCommands(stepNumber),
// What to do next
next_action: stepNumber < this.TOTAL_STEPS
? `Call mcp__product-manager__spaps_integration_wizard({action: "continue"}) when validation passes`
: `Integration complete! Call action: "status" for summary`,
// Context (simplified)
project_type: this.state.projectType,
detected_framework: this.state.detectedFramework,
// Warnings for common mistakes
common_mistakes: step.commonMistakes,
// Session tracking
session_id: this.state.sessionId
};
}
getStepDefinition(stepNumber) {
const steps = {
1: {
number: 1,
title: "Project Setup & Dependencies",
description: this.state.projectType === 'existing'
? "Adding SPAPS integration to existing project."
: "Setting up a new Next.js application with SPAPS integration foundation.",
prerequisites: ["Node.js 18+", "npm/yarn/pnpm", "TypeScript knowledge"],
todoList: this.state.projectType === 'existing' ? [
// For existing projects - minimal changes
{ content: "Install spaps-sdk from npm (latest version)", activeForm: "Installing spaps-sdk from npm", status: "pending" },
{ content: "Install spaps CLI in devDependencies", activeForm: "Installing spaps CLI in devDependencies", status: "pending" },
{ content: "Install concurrently in devDependencies if not present", activeForm: "Installing concurrently if needed", status: "pending" },
{ content: "Update package.json dev script to include spaps local", activeForm: "Updating package.json dev script", status: "pending" },
{ content: "Verify all dependencies are correctly installed", activeForm: "Verifying dependencies", status: "pending" },
{ content: "Request step 2 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
] : [
// For new projects - full setup
{ content: "Create Next.js app with TypeScript and App Router", activeForm: "Creating Next.js app with TypeScript and App Router", status: "pending" },
{ content: "Initialize shadcn/ui with Default style and Slate base color", activeForm: "Initializing shadcn/ui with Default style and Slate base color", status: "pending" },
{ content: "Install spaps-sdk from npm (version 1.0.1)", activeForm: "Installing spaps-sdk from npm", status: "pending" },
{ content: "Install spaps CLI in devDependencies", activeForm: "Installing spaps CLI in devDependencies", status: "pending" },
{ content: "Install concurrently in devDependencies", activeForm: "Installing concurrently in devDependencies", status: "pending" },
{ content: "Update package.json dev script to run spaps local", activeForm: "Updating package.json dev script", status: "pending" },
{ content: "Verify all dependencies are correctly installed", activeForm: "Verifying dependencies", status: "pending" },
{ content: "Request step 2 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create Next.js app with SPAPS dependencies and development setup",
validation: ["package.json contains spaps-sdk ^1.0.1", "shadcn/ui configured with Default style", "Dev script runs both spaps local and next dev"],
commonMistakes: ["Wrong package name (using @sweet-potato/sdk)", "Missing concurrently", "Wrong shadcn style (New York instead of Default)", "Missing TypeScript"],
nextSteps: ["Environment configuration", "SPAPS local server setup"]
},
2: {
number: 2,
title: "Environment Configuration",
description: "Configure environment variables and SPAPS local development server.",
prerequisites: ["Step 1 completed", "spaps-sdk installed", "SPAPS local server capability"],
todoList: [
{ content: "Create .env.local with SPAPS configuration", activeForm: "Creating environment file", status: "pending" },
{ content: "Add SPAPS_ENV=local for local development", activeForm: "Adding local development config", status: "pending" },
{ content: "Start SPAPS local server on port 3456", activeForm: "Starting SPAPS local server", status: "pending" },
{ content: "Verify SPAPS server health at localhost:3456/health", activeForm: "Verifying SPAPS server health", status: "pending" },
{ content: "Configure production environment variables", activeForm: "Configuring production environment", status: "pending" },
{ content: "Test environment switching logic", activeForm: "Testing environment switching", status: "pending" },
{ content: "Request step 3 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Set up local development with SPAPS server and environment configuration",
validation: ["SPAPS server runs on port 3456", "Health endpoint returns 200", "Environment variables configured", "Local/production switching works"],
commonMistakes: ["Wrong port (3000 or 3300 instead of 3456)", "Missing SPAPS_ENV variable", "Committing .env.local to git", "Not starting SPAPS server"],
nextSteps: ["SPAPS SDK initialization"]
},
3: {
number: 3,
title: "SDK Initialization",
description: "Initialize SPAPS SDK with proper configuration and create the foundation files.",
prerequisites: ["Step 1-2 completed", "SPAPS server running on 3456", "Environment configured"],
todoList: [
{ content: "Create src/lib/spaps.ts with SweetPotatoSDK", activeForm: "Creating SPAPS SDK file", status: "pending" },
{ content: "Add environment-aware API URL logic", activeForm: "Adding environment-aware URL logic", status: "pending" },
{ content: "Export sdk instance and utilities", activeForm: "Exporting SDK utilities", status: "pending" },
{ content: "Create admin configuration file", activeForm: "Creating admin config", status: "pending" },
{ content: "Add TypeScript type definitions", activeForm: "Adding TypeScript types", status: "pending" },
{ content: "Test SDK initialization", activeForm: "Testing SDK initialization", status: "pending" },
{ content: "Request step 4 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create SDK initialization with local/production configuration",
validation: ["SDK initializes without errors", "Environment switching works", "Types are properly defined", "Admin config exists"],
commonMistakes: ["Wrong import paths", "Missing environment detection", "Incorrect SDK configuration", "Missing type definitions"],
nextSteps: ["Email authentication implementation"]
},
4: {
number: 4,
title: "Email/Password Authentication",
description: "Implement email and password authentication using SPAPS SDK.",
prerequisites: ["Step 1-3 completed", "SDK initialized"],
todoList: [
{ content: "Create components/auth directory structure", activeForm: "Creating auth directory", status: "pending" },
{ content: this.state.projectType === 'existing'
? "Integrate SPAPS auth with your existing form components"
: "Build email/password form with shadcn/ui components",
activeForm: "Building authentication form", status: "pending" },
{ content: "Implement signInWithPassword with OBJECT parameter", activeForm: "Implementing signInWithPassword", status: "pending" },
{ content: "Add TokenManager.storeTokens for token storage", activeForm: "Adding token storage", status: "pending" },
{ content: "Setup TokenManager.autoRefreshToken", activeForm: "Setting up auto-refresh", status: "pending" },
{ content: "Implement SweetPotatoAPIError handling", activeForm: "Implementing error handling", status: "pending" },
{ content: "Test with test@example.com / Test123!", activeForm: "Testing authentication", status: "pending" },
{ content: "Request step 5 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create email authentication form with proper SDK integration",
validation: ["Form uses signInWithPassword with object parameter", "TokenManager stores tokens correctly", "Error handling for invalid credentials", "Test login works"],
commonMistakes: ["Using separate parameters instead of object", "Manual localStorage instead of TokenManager", "Missing error handling", "Not calling autoRefreshToken"],
nextSteps: ["Wallet authentication"]
},
5: {
number: 5,
title: "Wallet Authentication",
description: "Implement blockchain wallet authentication for Ethereum and other chains.",
prerequisites: ["Step 1-4 completed", "Email auth working", "MetaMask available"],
todoList: [
{ content: "Add wallet connection UI with shadcn/ui", activeForm: "Adding wallet connection UI", status: "pending" },
{ content: "Implement authenticateWallet method (NOT signInWithWallet)", activeForm: "Implementing authenticateWallet", status: "pending" },
{ content: "Create complete signature callback function", activeForm: "Creating signature callback", status: "pending" },
{ content: "Use WalletUtils.detectChainType for chain detection", activeForm: "Using WalletUtils for chain detection", status: "pending" },
{ content: "Test with address 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb8", activeForm: "Testing wallet authentication", status: "pending" },
{ content: "Store tokens and setup auto-refresh", activeForm: "Setting up token storage", status: "pending" },
{ content: "Handle wallet-specific errors", activeForm: "Handling wallet errors", status: "pending" },
{ content: "Request step 6 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create wallet authentication with signature callback",
validation: ["Uses authenticateWallet (not signInWithWallet)", "Signature callback properly implemented", "Chain type detection works", "MetaMask integration functional"],
commonMistakes: ["Wrong method name (signInWithWallet)", "Missing signature callback", "Pre-signing instead of callback", "Not handling MetaMask errors"],
nextSteps: ["Magic link authentication"]
},
6: {
number: 6,
title: "Magic Link Authentication",
description: "Implement passwordless magic link authentication for seamless user onboarding.",
prerequisites: ["Step 1-5 completed", "Email and wallet auth working"],
todoList: [
{ content: "Create magic link request form", activeForm: "Creating magic link form", status: "pending" },
{ content: "Implement sendMagicLink method", activeForm: "Implementing sendMagicLink", status: "pending" },
{ content: "Create magic link verification page", activeForm: "Creating verification page", status: "pending" },
{ content: "Implement verifyMagicLink method", activeForm: "Implementing verifyMagicLink", status: "pending" },
{ content: "Add loading states and user feedback", activeForm: "Adding loading states", status: "pending" },
{ content: "Handle magic link expiration", activeForm: "Handling link expiration", status: "pending" },
{ content: "Test complete magic link flow", activeForm: "Testing magic link flow", status: "pending" },
{ content: "Request step 7 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create passwordless authentication flow with magic links",
validation: ["Magic link request works", "Verification page handles tokens", "Token extraction from URL", "Complete auth flow functional"],
commonMistakes: ["Not extracting token from URL", "Missing loading states", "Poor error handling", "Not handling expiration"],
nextSteps: ["Payment integration"]
},
7: {
number: 7,
title: "Payment Integration",
description: "Integrate Stripe payments through SPAPS for subscription and one-time payments.",
prerequisites: ["Step 1-6 completed", "Authentication methods working"],
todoList: [
{ content: "Create payment form components", activeForm: "Creating payment forms", status: "pending" },
{ content: "Implement createCheckoutSession", activeForm: "Implementing checkout session", status: "pending" },
{ content: "Add subscription management", activeForm: "Adding subscription management", status: "pending" },
{ content: "Create customer portal integration", activeForm: "Creating customer portal", status: "pending" },
{ content: "Handle payment webhooks", activeForm: "Handling payment webhooks", status: "pending" },
{ content: "Add usage tracking", activeForm: "Adding usage tracking", status: "pending" },
{ content: "Test payment flows", activeForm: "Testing payment flows", status: "pending" },
{ content: "Request step 8 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Integrate Stripe payments with subscription management",
validation: ["Checkout sessions create successfully", "Customer portal accessible", "Webhooks handled properly", "Usage tracking works"],
commonMistakes: ["Missing webhook handling", "Not using SPAPS payment methods", "Poor error handling", "Missing loading states"],
nextSteps: ["Email whitelist setup"]
},
8: {
number: 8,
title: "Email Whitelist",
description: "Set up email whitelist for providing free premium access to specific users.",
prerequisites: ["Step 1-7 completed", "Payment system integrated"],
todoList: [
{ content: "Create whitelist management UI", activeForm: "Creating whitelist UI", status: "pending" },
{ content: "Implement addToWhitelist method", activeForm: "Implementing addToWhitelist", status: "pending" },
{ content: "Add bulk whitelist operations", activeForm: "Adding bulk operations", status: "pending" },
{ content: "Create whitelist status checking", activeForm: "Creating status checking", status: "pending" },
{ content: "Add tier-based access control", activeForm: "Adding tier-based access", status: "pending" },
{ content: "Implement whitelist removal", activeForm: "Implementing whitelist removal", status: "pending" },
{ content: "Test whitelist functionality", activeForm: "Testing whitelist functionality", status: "pending" },
{ content: "Request step 9 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create email whitelist system for premium access control",
validation: ["Whitelist management works", "Bulk operations functional", "Tier-based access works", "Status checking accurate"],
commonMistakes: ["Not checking whitelist status", "Missing tier validation", "Poor bulk operation handling", "No removal capability"],
nextSteps: ["Admin features"]
},
9: {
number: 9,
title: "Admin Features",
description: "Implement comprehensive admin functionality with role-based access control.",
prerequisites: ["Step 1-8 completed", "Whitelist system working"],
todoList: [
{ content: "Create admin dashboard components", activeForm: "Creating admin dashboard", status: "pending" },
{ content: "Implement role-based access control", activeForm: "Implementing RBAC", status: "pending" },
{ content: "Add user management interface", activeForm: "Adding user management", status: "pending" },
{ content: "Create audit logging system", activeForm: "Creating audit logging", status: "pending" },
{ content: "Implement admin authentication", activeForm: "Implementing admin auth", status: "pending" },
{ content: "Add system monitoring tools", activeForm: "Adding monitoring tools", status: "pending" },
{ content: "Test admin functionality", activeForm: "Testing admin functionality", status: "pending" },
{ content: "Request step 10 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create comprehensive admin system with RBAC and audit logging",
validation: ["Admin dashboard accessible", "RBAC enforced properly", "Audit logging works", "User management functional"],
commonMistakes: ["Missing role validation", "No audit logging", "Poor error handling", "Insecure admin access"],
nextSteps: ["Error handling and validation"]
},
10: {
number: 10,
title: "Error Handling & Validation",
description: "Implement comprehensive error handling and input validation throughout the application.",
prerequisites: ["Step 1-9 completed", "All major features implemented"],
todoList: [
{ content: "Create global error boundary", activeForm: "Creating error boundary", status: "pending" },
{ content: "Implement form validation schemas", activeForm: "Implementing form validation", status: "pending" },
{ content: "Add comprehensive error messages", activeForm: "Adding error messages", status: "pending" },
{ content: "Create error logging system", activeForm: "Creating error logging", status: "pending" },
{ content: "Implement retry mechanisms", activeForm: "Implementing retry mechanisms", status: "pending" },
{ content: "Add error recovery workflows", activeForm: "Adding error recovery", status: "pending" },
{ content: "Test error scenarios", activeForm: "Testing error scenarios", status: "pending" },
{ content: "Request step 11 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Create robust error handling and validation system",
validation: ["Error boundary catches errors", "Form validation works", "Error messages helpful", "Logging functional"],
commonMistakes: ["Generic error messages", "No error boundaries", "Missing validation", "Poor error UX"],
nextSteps: ["UI polish and optimization"]
},
11: {
number: 11,
title: "UI Polish & Optimization",
description: "Polish the user interface and optimize the application for production use.",
prerequisites: ["Step 1-10 completed", "Error handling implemented"],
todoList: [
{ content: "Refine UI components and styling", activeForm: "Refining UI components", status: "pending" },
{ content: "Add loading states and animations", activeForm: "Adding loading states", status: "pending" },
{ content: "Implement responsive design", activeForm: "Implementing responsive design", status: "pending" },
{ content: "Optimize performance and bundle size", activeForm: "Optimizing performance", status: "pending" },
{ content: "Add accessibility improvements", activeForm: "Adding accessibility", status: "pending" },
{ content: "Create user onboarding flow", activeForm: "Creating onboarding flow", status: "pending" },
{ content: "Polish user experience", activeForm: "Polishing user experience", status: "pending" },
{ content: "Request step 12 of SPAPS integration wizard", activeForm: "Requesting next step", status: "pending" }
],
implementation: "Polish UI/UX and optimize for production deployment",
validation: ["UI components polished", "Loading states smooth", "Responsive design works", "Performance optimized"],
commonMistakes: ["Poor loading states", "Non-responsive design", "Large bundle size", "Accessibility issues"],
nextSteps: ["Final testing and deployment"]
},
12: {
number: 12,
title: "Testing & Deployment",
description: "Final testing, documentation, and deployment preparation for the SPAPS integration.",
prerequisites: ["Step 1-11 completed", "All features implemented and polished"],
todoList: [
{ content: "Run comprehensive integration tests", activeForm: "Running integration tests", status: "pending" },
{ content: "Test all authentication methods", activeForm: "Testing authentication methods", status: "pending" },
{ content: "Verify payment flows end-to-end", activeForm: "Verifying payment flows", status: "pending" },
{ content: "Test admin functionality thoroughly", activeForm: "Testing admin functionality", status: "pending" },
{ content: "Prepare production environment", activeForm: "Preparing production environment", status: "pending" },
{ content: "Create deployment documentation", activeForm: "Creating deployment docs", status: "pending" },
{ content: "Deploy to staging/production", activeForm: "Deploying application", status: "pending" },
{ content: "Complete SPAPS integration wizard", activeForm: "Completing integration", status: "pending" }
],
implementation: "Complete final testing, documentation, and deployment",
validation: ["All tests passing", "Authentication flows work", "Payment integration functional", "Admin features operational"],
commonMistakes: ["Incomplete testing", "Missing documentation", "Production config errors", "Performance issues"],
nextSteps: ["Integration complete - monitor and maintain"]
}
};
return steps[stepNumber] || {
number: stepNumber,
title: `Step ${stepNumber}`,
description: "Step definition not found - please check step number",
prerequisites: [],
todoList: [
{ content: `Define step ${stepNumber} implementation`, activeForm: `Defining step ${stepNumber}`, status: "pending" }
],
implementation: "",
validation: [],
commonMistakes: [],
nextSteps: []
};
}
shouldGenerateUIComponents() {
// Only generate UI components for new projects or if no UI library detected
return this.state.projectType === 'new' || !this.state.uiLibrary;
}
getUIAgnosticAuthForm() {
// Generate a basic auth form that doesn't impose UI library choices
if (this.state.projectType === 'existing') {
return `
// SPAPS Authentication Integration
// Adapt this code to your existing UI components
import { sdk, TokenManager } from '@/lib/spaps';
export function SPAPSAuthForm() {
// Use your existing form components and styling
// This is just the SPAPS integration logic
const handleEmailAuth = async (email: string, password: string) => {
try {
const response = await sdk.auth.signInWithPassword({ email, password });
TokenManager.storeTokens(response.access_token, response.refresh_token);
TokenManager.autoRefreshToken();
} catch (error) {
console.error('Auth failed:', error);
}
};
// Implement using your existing UI components
return (
<div>
{/* Use your existing form components here */}
{/* Example: <YourFormComponent onSubmit={handleEmailAuth} /> */}
</div>
);
}`;
}
// For new projects, return full shadcn implementation
return this.getDefaultAuthForm();
}
getDefaultAuthForm() {
// Full shadcn implementation for new projects
return `/* Full shadcn auth form implementation */`;
}
getFilesToCreate(stepNumber) {
const framework = this.state.detectedFramework;
switch (stepNumber) {
case 1:
return this.getStep1Files(framework);
case 2:
return this.getStep2Files(framework);
case 3:
return this.getStep3Files(framework);
default:
return {};
}
}
getFilesToModify(stepNumber) {
switch (stepNumber) {
case 1:
return this.getStep1Modifications();
case 2:
return this.getStep2Modifications();
case 3:
return this.getStep3Modifications();
default:
return {};
}
}
getValidationCommands(stepNumber) {
switch (stepNumber) {
case 1:
return [
'test -f package.json || echo "FAIL: package.json missing"',
'test -f tsconfig.json || echo "FAIL: tsconfig.json missing"',
'npm list typescript > /dev/null 2>&1 || echo "FAIL: TypeScript not installed"',
'npx tsc --noEmit || echo "FAIL: TypeScript compilation errors"'
];
case 2:
return [
'test -f .env.local || echo "FAIL: .env.local missing"',
'grep -q "SPAPS_" .env.local || echo "FAIL: SPAPS environment variables missing"',
'test ! -f .env || echo "WARN: .env file should not be committed"'
];
case 3:
return [
'test -f src/lib/spaps.ts || echo "FAIL: SPAPS SDK file missing"',
'npm list spaps-sdk > /dev/null 2>&1 || echo "FAIL: spaps-sdk not installed"',
'npm list spaps > /dev/null 2>&1 || echo "FAIL: spaps CLI not installed"'
];
default:
return [`echo "No validation defined for step ${stepNumber}"`];
}
}
getStep1Files(framework) {
const files = {};
// Always create tsconfig.json
files['tsconfig.json'] = `{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "ES6"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}`;
// Framework-specific files
if (framework?.includes('nextjs')) {
files['next.config.js'] = `/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
appDir: true,
},
}
module.exports = nextConfig`;
}
return files;
}
getStep1Modifications() {
const framework = this.state.detectedFramework;
const basePackage = {
dependencies: {
"typescript": "^5.0.0",
"@types/node": "^20.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0"
},
scripts: {
"typecheck": "tsc --noEmit"
}
};
if (framework?.includes('nextjs')) {
basePackage.dependencies = {
...basePackage.dependencies,
"next": "15.5.2",
"react": "19.1.0",
"react-dom": "19.1.0"
};
basePackage.scripts = {
...basePackage.scripts,
"dev": "concurrently \"npx spaps local\" \"next dev --turbopack\"",
"dev:next": "next dev --turbopack",
"dev:spaps": "npx spaps local",
"build": "next build --turbopack",
"start": "next start",
"lint": "next lint"
};
}
return {
'package.json': {
operation: 'merge',
content: basePackage
}
};
}
getStep2Files(framework) {
return {
'.env.local': `# SPAPS Configuration
NEXT_PUBLIC_SPAPS_ENV=local
NEXT_PUBLIC_SPAPS_API_URL_LOCAL=http://localhost:3456
NEXT_PUBLIC_SPAPS_API_URL_PRODUCTION=https://api.sweetpotato.dev
NEXT_PUBLIC_SPAPS_APP_ID=your_app_id
NEXT_PUBLIC_SPAPS_API_KEY=your_api_key
ADMIN_PASSWORD=your_secure_admin_password
SPAPS_PUBLIC_KEY=your_public_key_here
SPAPS_SECRET_KEY=your_secret_key_here
# Environment
NODE_ENV=development`,
'.env.example': `# SPAPS Configuration
NEXT_PUBLIC_SPAPS_ENV=local
NEXT_PUBLIC_SPAPS_API_URL_LOCAL=http://localhost:3456
NEXT_PUBLIC_SPAPS_API_URL_PRODUCTION=https://api.sweetpotato.dev
NEXT_PUBLIC_SPAPS_APP_ID=your_app_id
NEXT_PUBLIC_SPAPS_API_KEY=your_api_key
ADMIN_PASSWORD=your_secure_admin_password
SPAPS_PUBLIC_KEY=your_public_key_here
SPAPS_SECRET_KEY=your_secret_key_here
# Environment
NODE_ENV=development`,
'.gitignore': `# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# SPAPS wizard state
.spaps-wizard/
# Dependencies
node_modules/
# Build outputs
.next/
dist/
build/`
};
}
getStep2Modifications() {
return {};
}
getStep3Modifications() {
return {
'package.json': {
operation: 'merge',
content: {
dependencies: {
"spaps-sdk": "^1.1.0"
},
devDependencies: {
"spaps": "^0.5.0",
"concurrently": "^9.2.1"
}
}
}
};
}
getStep3Files(framework) {
const files = {};
// Create src directory structure
files['src/lib/spaps.ts'] = `import { SweetPotatoSDK, TokenManager, WalletUtils, SweetPotatoAPIError } from 'spaps-sdk';
// Determine API URL based on environment configuration
const getApiUrl = (): string => {
const env = process.env.NEXT_PUBLIC_SPAPS_ENV || 'local';
if (env === 'production') {
return process.env.NEXT_PUBLIC_SPAPS_API_URL_PRODUCTION || 'https://api.sweetpotato.dev';
} else if (env === 'local') {
return process.env.NEXT_PUBLIC_SPAPS_API_URL_LOCAL || 'http://localhost:3456';
}
// Fallback to legacy configuration
return process.env.NEXT_PUBLIC_SPAPS_API_URL || 'http://localhost:3456';
};
const API_URL = getApiUrl();
const IS_PRODUCTION = process.env.NEXT_PUBLIC_SPAPS_ENV === 'production';
// Initialize SPAPS SDK with environment-aware configuration
export const sdk = new SweetPotatoSDK({
apiUrl: API_URL,
// In production, add both applicationId and apiKey
...(IS_PRODUCTION ? {
applicationId: process.env.NEXT_PUBLIC_SPAPS_APP_ID,
apiKey: process.env.NEXT_PUBLIC_SPAPS_API_KEY,
headers: {
'X-API-Key': process.env.NEXT_PUBLIC_SPAPS_API_KEY
}
} : {})
});
// Re-export utilities for easy access
export { TokenManager, WalletUtils, SweetPotatoAPIError };
// Helper function to get admin JWT token
export const getAdminToken = (): string | null => {
return TokenManager.getAccessToken();
};
// Helper function to make authenticated API calls
export const makeAuthenticatedRequest = async (
endpoint: string,
options: RequestInit = {}
): Promise<Response> => {
const token = getAdminToken();
return fetch(\`\${API_URL}\${endpoint}\`, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': token ? \`Bearer \${token}\` : '',
// Add API key header if in production mode
...(IS_PRODUCTION && process.env.NEXT_PUBLIC_SPAPS_API_KEY ? {
'X-API-Key': process.env.NEXT_PUBLIC_SPAPS_API_KEY
} : {}),
...options.headers,
},
});
};`;
files['src/lib/admin-config.ts'] = `/**
* Default Admin Configuration
* These accounts are automatically whitelisted/granted admin access
*/
export const DEFAULT_ADMIN_ACCOUNTS = {
email: 'buildooor@gmail.com',
wallets: {
ethereum: '0xa72bb7CeF1e4B2Cc144373d8dE0Add7CCc8DF4Ba',
solana: 'HVEbdiYU3Rr34NHBSgKs7q8cvdTeZLqNL77Z1FB2vjLy',
}
};
/**
* Production admin credentials
*/
export const ADMIN_CREDENTIALS = {
email: 'buildooor@gmail.com',
password: process.env.ADMIN_PASSWORD || 'your_secure_admin_password',
};
/**
* Helper to check if an address/email is an admin
*/
export function isAdminAccount(identifier: string): boolean {
const normalized = identifier.toLowerCase();
return (
normalized === DEFAULT_ADMIN_ACCOUNTS.email.toLowerCase() ||
normalized === DEFAULT_ADMIN_ACCOUNTS.wallets.ethereum.toLowerCase() ||
normalized === DEFAULT_ADMIN_ACCOUNTS.wallets.solana.toLowerCase()
);
}
/**
* Get admin status display
*/
export function getAdminBadge(identifier: string): string | null {
if (isAdminAccount(identifier)) {
return '👑 Admin';
}
return null;
}`;
files['src/types/spaps.ts'] = `// SPAPS type definitions
export interface User {
id: string;
email: string;
name?: string;
createdAt: string;
updatedAt: string;
}
export interface AuthResponse {
access_token: string;
refresh_token: string;
user: User;
}
export interface AuthSession {
user: User;
token: string;
expiresAt: string;
}
export interface PaymentIntent {
id: string;
amount: number;
currency: string;
status: 'pending' | 'succeeded' | 'failed';
}`;
return files;
}
async validateCurrentStep() {
return this.performStepValidation(this.state.currentStep);
}
async performStepValidation(step, data) {
const validation = {
step,
passed: true,
checks: [],
errors: [],
warnings: [],
timestamp: new Date().toISOString(),
commands_run: []
};
const commands = this.getValidationCommands(step);
try {
// Execute validation commands using pre-imported modules
const execAsync = promisify(exec);
for (const command of commands) {
try {
const result = await execAsync(command, { cwd: this.state.projectPath });
validation.commands_run.push({
command,
success: true,
output: result.stdout || 'OK'
});
validation.checks.push(`✓ ${command}`);
}
catch (error) {
const output = error.stdout || error.stderr || error.message;
validation.commands_run.push({
command,
success: false,
output,
error: error.message
});
if (output.includes('FAIL:')) {
validation.passed = false;
validation.errors.push(output.replace('FAIL: ', ''));
}
else if (output.includes('WARN:')) {
validation.warnings.push(output.replace('WARN: ', ''));
}
}
}
}
catch (error) {
validation.passed = false;
validation.errors.push(`Validation execution error: ${error.message}`);
}
return validation;
}
generateCompletionSummary() {
return {
status: "completed",
message: "🎉 SPAPS Integration Wizard completed successfully!",
summary: {
project_path: this.state.projectPath,
project_type: this.state.projectType,
framework: this.state.detectedFramework,
total_steps: this.TOTAL_STEPS,
completed_steps: this.state.completedSteps.length,
duration: this.calculateDuration(),
refactoring_completed: this.state.refactoringTasks.length
},
next_steps: [
"Test all SPAPS features in your application",
"Review security configurations",
"Set up monitoring and error tracking",
"Deploy to staging environment",
"Create user documentation"
],
resources: {
documentation: "https://docs.sweetpotato.dev",
support: "https://support.sweetpotato.dev",
community: "https://discord.gg/sweetpotato"
}
};
}
calculateDuration() {
const start = new Date(this.state.startedAt);
const end = new Date();
const diffMs = end.getTime() - start.getTime();
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffMins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
return diffHours > 0 ? `${diffHours}h ${diffMins}m` : `${diffMins}m`;
}
getStepTitle(stepNumber) {
return this.getStepDefinition(stepNumber).title;
}
async saveState() {
try {
const stateDir = path.join(this.state.projectPath, this.STATE_DIR);
await fs.mkdir(stateDir, { recursive: true });
const statePath = path.join(stateDir, 'wizard-state.json');
await fs.writeFile(statePath, JSON.stringify(this.state, null, 2));
}
catch (error) {
console.error('Failed to save wizard state:', error);
}
}
async loadState() {
try {
const statePath = path.join(this.state.projectPath, this.STATE_DIR, 'wizard-state.json');
const content = await fs.readFile(statePath, 'utf-8');
const savedState = JSON.parse(content);
// Merge saved state with current state, preserving structure
this.state = { ...this.state, ...savedState };
}
catch (error) {
// No existing state found, use defaults
}
}
async fileExists(filePath) {
try {
await fs.access(filePath);
return true;
}
catch {
return false;
}
}
async findInFiles(directory, pattern) {
try {
const files = await fs.readdir(directory, { rec