UNPKG

aipm-mcp

Version:

Complete AIPM integration for Cursor IDE - Get tasks, manage features, track time, and build features with AI. Supports both MCP stdio mode and HTTP server mode.

1,147 lines (1,066 loc) โ€ข 83.2 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { config } from 'dotenv'; import { homedir } from 'os'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'; import { spawn } from 'child_process'; import { AIPMClient } from './aipm-client.js'; import { AIPM_TOOLS } from './tools.js'; import { GetTasksArgsSchema, StartTaskArgsSchema, CompleteTaskArgsSchema, CreateTaskArgsSchema, GetFeaturesArgsSchema, BreakdownFeatureArgsSchema, LogTimeArgsSchema, UpdateTaskImplementationArgsSchema, } from './types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load environment variables from .env file if present config(); // Try to load config from user's home directory const CONFIG_DIR = join(homedir(), '.aipm-mcp'); const CONFIG_FILE = join(CONFIG_DIR, 'config.json'); function loadUserConfig() { if (existsSync(CONFIG_FILE)) { try { const configData = readFileSync(CONFIG_FILE, 'utf8'); const userConfig = JSON.parse(configData); // Set environment variables from user config if not already set if (!process.env.AIPM_API_URL && userConfig.aipmUrl) { process.env.AIPM_API_URL = userConfig.aipmUrl; } if (!process.env.AIPM_API_TOKEN && userConfig.apiToken) { process.env.AIPM_API_TOKEN = userConfig.apiToken; } if (!process.env.AIPM_ORGANIZATION_ID && userConfig.organizationId) { process.env.AIPM_ORGANIZATION_ID = userConfig.organizationId; } if (!process.env.MCP_SERVER_NAME && userConfig.serverName) { process.env.MCP_SERVER_NAME = userConfig.serverName; } } catch (error) { // Ignore config loading errors silently } } } // Load user configuration loadUserConfig(); export class AIPMServer { server; aipmClient; constructor() { // Validate required environment variables const apiUrl = process.env.AIPM_API_URL; const apiToken = process.env.AIPM_API_TOKEN; if (!apiUrl || !apiToken) { throw new Error('Missing required configuration. Please run "npx aipm-mcp configure" to set up your AIPM connection.'); } this.aipmClient = new AIPMClient(apiUrl, apiToken); this.server = new Server({ name: process.env.MCP_SERVER_NAME || 'aipm-mcp', version: process.env.MCP_SERVER_VERSION || '1.0.0', }, { capabilities: { tools: {}, }, }); this.setupHandlers(); } setupHandlers() { // Handle tool listing this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: AIPM_TOOLS, }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_tasks': return await this.handleGetTasks(args); case 'get_task_details': return await this.handleGetTaskDetails(args); case 'start_task': return await this.handleStartTask(args); case 'complete_task': return await this.handleCompleteTask(args); case 'create_task': return await this.handleCreateTask(args); case 'get_features': return await this.handleGetFeatures(args); case 'breakdown_feature': return await this.handleBreakdownFeature(args); case 'get_products': return await this.handleGetProducts(args); case 'log_time': return await this.handleLogTime(args); case 'get_time_logs': return await this.handleGetTimeLogs(args); case 'search_tasks': return await this.handleSearchTasks(args); case 'get_current_user': return await this.handleGetCurrentUser(args); case 'update_task_implementation': return await this.handleUpdateTaskImplementation(args); case 'get_development_guidance': return await this.handleGetDevelopmentGuidance(args); case 'build_feature': return await this.handleBuildFeature(args); case 'implement_feature': return await this.handleImplementFeature(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { // Enhanced error handling with detailed information let errorMessage = 'Unknown error occurred'; let errorDetails = ''; if (error instanceof Error) { errorMessage = error.message; // Check if it's our custom AIPMError if (error.name === 'AIPMError' && error.statusCode) { const aipmError = error; errorDetails = `\n\n**Error Details:**\n` + `- Status Code: ${aipmError.statusCode}\n` + `- Error Code: ${aipmError.errorCode}\n` + `- Context: ${aipmError.requestContext}\n`; if (aipmError.details) { errorDetails += `- Details: ${JSON.stringify(aipmError.details, null, 2)}\n`; } // Add specific guidance based on error type switch (aipmError.errorCode) { case 'VALIDATION_ERROR': errorDetails += `\n**๐Ÿ’ก How to fix:** Check the field names and values in your request. Ensure all required fields are provided with valid values.`; break; case 'UNAUTHORIZED': errorDetails += `\n**๐Ÿ’ก How to fix:** Check your authentication token and ensure it's valid.`; break; case 'PENDING_APPROVAL': errorDetails += `\n**๐Ÿ’ก How to fix:** Contact your administrator to approve your account.`; break; case 'NOT_FOUND': errorDetails += `\n**๐Ÿ’ก How to fix:** Verify the resource ID exists and you have access to it.`; break; case 'FORBIDDEN': errorDetails += `\n**๐Ÿ’ก How to fix:** Check your permissions for this resource.`; break; case 'SERVER_ERROR': errorDetails += `\n**๐Ÿ’ก How to fix:** This is a server issue. Please try again later or contact support.`; break; } } } return { content: [ { type: 'text', text: `โŒ **Error executing ${name}**\n\n${errorMessage}${errorDetails}`, }, ], isError: true, }; } }); } async handleGetTasks(args) { const validatedArgs = GetTasksArgsSchema.parse(args || {}); const tasks = await this.aipmClient.getTasks(validatedArgs); const taskSummary = tasks.map(task => ({ id: task.id, key: task.key, title: task.title, status: task.status, priority: task.priority, type: task.type, estimatedHours: task.estimatedHours, assignedTo: task.assignedTo?.name || 'Unassigned', product: task.product.name, feature: task.feature?.name || 'No feature' })); return { content: [ { type: 'text', text: `Found ${tasks.length} tasks:\n\n${taskSummary .map((task) => `**${task.key}**: ${task.title}\n` + ` Status: ${task.status} | Priority: ${task.priority} | Type: ${task.type}\n` + ` Product: ${task.product} | Feature: ${task.feature}\n` + ` Assigned to: ${task.assignedTo} | Estimated: ${task.estimatedHours || 'N/A'}h\n`) .join('\n')}`, }, { type: 'text', text: `\nDetailed task data:\n${JSON.stringify(tasks, null, 2)}`, }, ], }; } async handleGetTaskDetails(args) { if (!args?.taskId) { throw new Error('taskId is required'); } const task = await this.aipmClient.getTaskById(args.taskId); return { content: [ { type: 'text', text: `**Task Details: ${task.key}**\n\n` + `**Title:** ${task.title}\n` + `**Status:** ${task.status}\n` + `**Priority:** ${task.priority}\n` + `**Type:** ${task.type}\n` + `**Product:** ${task.product.name}\n` + `**Feature:** ${task.feature?.name || 'No feature'}\n` + `**Assigned to:** ${task.assignedTo?.name || 'Unassigned'}\n` + `**Estimated Hours:** ${task.estimatedHours || 'N/A'}\n` + `**Actual Hours:** ${task.actualHours || 'N/A'}\n` + `**Created:** ${new Date(task.createdAt).toLocaleDateString()}\n` + `**Started:** ${task.startedAt ? new Date(task.startedAt).toLocaleDateString() : 'Not started'}\n` + `**Completed:** ${task.completedAt ? new Date(task.completedAt).toLocaleDateString() : 'Not completed'}\n\n` + `**Description:**\n${task.description || 'No description'}\n\n` + `**AI Prompt:**\n${task.aiPrompt || 'No AI prompt available'}\n\n` + `**Generated Code:**\n${task.generatedCode || 'No generated code available'}`, }, ], }; } async handleStartTask(args) { const validatedArgs = StartTaskArgsSchema.parse(args); const result = await this.aipmClient.startTask(validatedArgs.taskId, validatedArgs.notes); return { content: [ { type: 'text', text: `โœ… Task ${validatedArgs.taskId} started successfully!\n\n` + `Status updated to IN_PROGRESS and time tracking began.\n` + `Time log ID: ${result.timeLogId || 'N/A'}`, }, ], }; } async handleCompleteTask(args) { const validatedArgs = CompleteTaskArgsSchema.parse(args); await this.aipmClient.completeTask(validatedArgs.taskId, { completionNotes: validatedArgs.completionNotes, timeSpent: validatedArgs.timeSpent, codeChanges: validatedArgs.codeChanges, }); return { content: [ { type: 'text', text: `โœ… Task ${validatedArgs.taskId} completed successfully!\n\n` + `Status updated to DONE and time tracking stopped.\n` + `${validatedArgs.timeSpent ? `Time logged: ${validatedArgs.timeSpent}h\n` : ''}` + `${validatedArgs.completionNotes ? `Notes: ${validatedArgs.completionNotes}` : ''}`, }, ], }; } async handleCreateTask(args) { const validatedArgs = CreateTaskArgsSchema.parse(args); const task = await this.aipmClient.createTask(validatedArgs); return { content: [ { type: 'text', text: `โœ… Task created successfully!\n\n` + `**${task.key}**: ${task.title}\n` + `Status: ${task.status} | Priority: ${task.priority} | Type: ${task.type}\n` + `Product: ${task.product.name}\n` + `Estimated Hours: ${task.estimatedHours || 'N/A'}h\n` + `Assigned to: ${task.assignedTo?.name || 'Unassigned'}`, }, ], }; } async handleGetFeatures(args) { const validatedArgs = GetFeaturesArgsSchema.parse(args || {}); const features = await this.aipmClient.getFeatures(validatedArgs); return { content: [ { type: 'text', text: `Found ${features.length} features:\n\n${features .map((feature) => `**${feature.key}**: ${feature.name}\n` + ` Status: ${feature.status} | Priority: ${feature.priority}\n` + ` Product: ${feature.product.name} | Progress: ${feature.progress}%\n` + ` Description: ${feature.description || 'No description'}\n` + ` Acceptance Criteria: ${feature.acceptanceCriteria || 'No criteria'}\n`) .join('\n')}`, }, ], }; } async handleBreakdownFeature(args) { const validatedArgs = BreakdownFeatureArgsSchema.parse(args); const result = await this.aipmClient.breakdownFeature(validatedArgs.featureId, validatedArgs.additionalContext); return { content: [ { type: 'text', text: `๐Ÿค– Feature breakdown completed!\n\n` + `**Breakdown Summary:**\n${result.breakdown}\n\n` + `**Generated Tasks (${result.tasks.length}):**\n${result.tasks .map((task) => `- **${task.key}**: ${task.title}\n` + ` Priority: ${task.priority} | Estimated: ${task.estimatedHours || 'N/A'}h\n` + ` Description: ${task.description || 'No description'}\n`) .join('\n')}`, }, ], }; } async handleGetProducts(args) { const products = await this.aipmClient.getProducts(); return { content: [ { type: 'text', text: `Found ${products.length} products:\n\n${products .map((product) => `**${product.key}**: ${product.name}\n` + ` Status: ${product.status} | Progress: ${product.progress}%\n` + ` Description: ${product.description || 'No description'}\n` + ` Target Date: ${product.targetDate ? new Date(product.targetDate).toLocaleDateString() : 'No target date'}\n`) .join('\n')}`, }, ], }; } async handleLogTime(args) { const validatedArgs = LogTimeArgsSchema.parse(args); const timeLog = await this.aipmClient.logTime(validatedArgs); return { content: [ { type: 'text', text: `โœ… Time logged successfully!\n\n` + `Hours: ${timeLog.hours}h\n` + `Date: ${timeLog.date}\n` + `${timeLog.description ? `Description: ${timeLog.description}\n` : ''}` + `${timeLog.ticket ? `Task: ${timeLog.ticket.key} - ${timeLog.ticket.title}\n` : ''}` + `${timeLog.feature ? `Feature: ${timeLog.feature.key} - ${timeLog.feature.name}\n` : ''}`, }, ], }; } async handleGetTimeLogs(args) { const timeLogs = await this.aipmClient.getTimeLogs(args || {}); const totalHours = timeLogs.reduce((sum, log) => sum + log.hours, 0); return { content: [ { type: 'text', text: `Found ${timeLogs.length} time logs (Total: ${totalHours}h):\n\n${timeLogs .map((log) => `**${log.date}**: ${log.hours}h\n` + ` ${log.description || 'No description'}\n` + ` ${log.ticket ? `Task: ${log.ticket.key} - ${log.ticket.title}` : ''}\n` + ` ${log.feature ? `Feature: ${log.feature.key} - ${log.feature.name}` : ''}\n`) .join('\n')}`, }, ], }; } async handleSearchTasks(args) { if (!args?.query) { throw new Error('query is required for search'); } const tasks = await this.aipmClient.searchTasks(args.query, args.limit); return { content: [ { type: 'text', text: `Search results for "${args.query}" (${tasks.length} found):\n\n${tasks .map((task) => `**${task.key}**: ${task.title}\n` + ` Status: ${task.status} | Priority: ${task.priority}\n` + ` Product: ${task.product.name} | Feature: ${task.feature?.name || 'No feature'}\n` + ` Description: ${task.description || 'No description'}\n`) .join('\n')}`, }, ], }; } async handleGetCurrentUser(args) { const user = await this.aipmClient.getCurrentUser(); return { content: [ { type: 'text', text: `**Current User:**\n` + `Name: ${user.name}\n` + `Email: ${user.email}\n` + `Role: ${user.role}\n` + `ID: ${user.id}`, }, ], }; } async handleUpdateTaskImplementation(args) { const validatedArgs = UpdateTaskImplementationArgsSchema.parse(args); await this.aipmClient.updateTaskImplementation(validatedArgs.taskId, { implementationDetails: validatedArgs.implementationDetails, testResults: validatedArgs.testResults, codeChanges: validatedArgs.codeChanges, }); return { content: [ { type: 'text', text: `โœ… Task ${validatedArgs.taskId} implementation details updated successfully!\n\n` + `Implementation details recorded.\n` + `${validatedArgs.testResults ? `Test results recorded.\n` : ''}` + `${validatedArgs.codeChanges ? `Code changes recorded.\n` : ''}\n` + `**๐Ÿ“ Progress Tracking Active**\n\n` + `**Continue Development:**\n` + `- Keep implementing and testing\n` + `- Update progress again in 30-60 minutes\n` + `- Document any new challenges or solutions\n` + `- When complete, use 'complete_task' with final summary\n\n` + `**๐Ÿ’ก Remember to include:**\n` + `- Specific technical approaches used\n` + `- Any challenges faced and solutions found\n` + `- Test results and coverage metrics\n` + `- Code quality considerations`, }, ], }; } async handleGetDevelopmentGuidance(args) { const context = args?.context || 'general'; const taskId = args?.taskId; const additionalContext = args?.additionalContext; let guidance = ''; switch (context) { case 'task_implementation': guidance = ` **๐Ÿš€ Task Implementation Workflow** **Step-by-Step Process:** 1. **Understand Requirements** โ†’ Review task details and acceptance criteria 2. **Plan Approach** โ†’ Break down into smaller implementation steps 3. **Implement** โ†’ Write code following best practices 4. **Test** โ†’ Write and run tests for your implementation 5. **Document** โ†’ Update implementation details regularly 6. **Complete** โ†’ Finalize with comprehensive summary **Best Practices:** - Update progress every 30-60 minutes using 'update_task_implementation' - Include specific technical challenges and solutions - Document test results and coverage metrics - Consider performance and security implications - Follow established code conventions`; break; case 'testing': guidance = ` **๐Ÿงช Testing Best Practices** **Test Strategy:** - Write unit tests for core functionality - Include integration tests for component interactions - Run tests frequently during development - Document test results in implementation updates - Aim for good test coverage **Test Documentation:** - Record test execution results - Note any test failures and resolutions - Include coverage percentages - Document testing challenges and solutions`; break; case 'code_review': guidance = ` **๐Ÿ” Code Review Guidelines** **Review Checklist:** - Code follows established conventions - Proper error handling implemented - Performance considerations addressed - Security best practices followed - Documentation is clear and complete - Tests are comprehensive and passing`; break; case 'debugging': guidance = ` **๐Ÿ› Debugging Best Practices** **Debugging Process:** 1. Reproduce the issue consistently 2. Identify the root cause 3. Implement a targeted fix 4. Test the fix thoroughly 5. Document the solution and lessons learned **Tools and Techniques:** - Use logging and debugging tools - Write additional tests to catch similar issues - Consider edge cases and error conditions - Document debugging steps for future reference`; break; default: guidance = ` **๐Ÿ’ก General Development Guidance** **Workflow Best Practices:** - Always start by understanding task requirements - Break large tasks into manageable chunks - Update progress regularly during development - Document decisions and trade-offs - Test thoroughly before completing tasks - Include comprehensive implementation details **Available MCP Tools:** - \`get_tasks\` - Find tasks to work on - \`get_task_details\` - Get full task information - \`start_task\` - Begin work and time tracking - \`update_task_implementation\` - Track progress during development - \`complete_task\` - Finalize with detailed summary - \`log_time\` - Manual time logging if needed`; } if (taskId) { guidance += `\n\n**Task-Specific Context:**\nTask ID: ${taskId}`; } if (additionalContext) { guidance += `\n\n**Additional Context:**\n${additionalContext}`; } return { content: [ { type: 'text', text: guidance, }, ], }; } async handleBuildFeature(args) { const { featureId, requirements, acceptanceCriteria, product, estimatedHours, userId } = args; if (!featureId || !requirements) { throw new Error('featureId and requirements are required'); } console.log('๐Ÿš€ HTTP: Building feature:', featureId, 'for user:', userId); // Check if workspace already exists (idempotency check) const { cwd } = await import('process'); const { existsSync } = await import('fs'); const currentWorkspace = cwd(); const featureDir = `${currentWorkspace}/aipm-features/${featureId}`; if (existsSync(featureDir)) { console.log(`โš ๏ธ Workspace already exists for feature ${featureId}, skipping file creation`); return { success: true, featureId: featureId, buildTime: '0s', filesCreated: ['Workspace already existed'], testsWritten: 0, status: 'WORKSPACE_EXISTS', message: `Feature workspace already exists at ${featureDir}`, workspacePath: featureDir, currentWorkspace: currentWorkspace }; } try { // Step 1: Report workspace preparation started await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'WORKSPACE_PREPARING', 'Creating feature workspace and files...'); // Get current working directory (where Cursor is currently open) const { cwd } = await import('process'); const currentWorkspace = cwd(); // Create feature directory within current workspace const featureDir = `${currentWorkspace}/aipm-features/${featureId}`; const { mkdirSync, writeFileSync } = await import('fs'); // Create feature directory mkdirSync(featureDir, { recursive: true }); // Step 2: Report workspace created await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'WORKSPACE_CREATED', 'Feature workspace created, generating files...'); // Create feature specification const featureSpec = `# Feature: ${featureId} ## Requirements ${requirements} ${acceptanceCriteria ? `## Acceptance Criteria ${acceptanceCriteria}` : ''} ## Product Context ${product || 'General application'} ## Estimated Hours ${estimatedHours || 8} hours ## Implementation Notes - This feature should be implemented using modern React/TypeScript patterns - Include proper error handling and loading states - Write comprehensive tests - Follow the existing codebase patterns and conventions - Ensure accessibility and responsive design - Integrate with the existing AIPM codebase structure `; writeFileSync(`${featureDir}/FEATURE_SPEC.md`, featureSpec); // Create Cursor configuration const cursorConfig = { name: `AIPM Feature: ${featureId}`, description: `Implementation of feature: ${requirements}`, featureId: featureId, requirements: requirements, acceptanceCriteria: acceptanceCriteria, product: product, estimatedHours: estimatedHours, workspace: currentWorkspace, createdAt: new Date().toISOString() }; writeFileSync(`${featureDir}/.cursor-feature.json`, JSON.stringify(cursorConfig, null, 2)); // Step 3: Report files created await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'FILES_CREATED', 'Feature files created, preparing AI instructions...'); // Create AI instructions const aiInstructions = `# AI Implementation Instructions for Cursor You are implementing the feature described in FEATURE_SPEC.md within the existing AIPM codebase. ## ๐Ÿš€ IMMEDIATE ACTION REQUIRED **Start implementing this feature right now in the current workspace!** ## ๐Ÿ“‹ How to Use These Documents ### **1. FEATURE_SPEC.md - Start Here!** - **Read this first** to understand what needs to be built - **Analyze the requirements** and acceptance criteria - **Identify the scope** of the feature ### **2. IMPLEMENTATION_PLAN.md - Your Checklist** - **Review the analysis** of what the feature needs - **Check off items** as you complete them - **Use as a progress tracker** - **Follow the status update guidelines** ### **3. This File (AI_INSTRUCTIONS.md) - Implementation Guide** - **Follow the step-by-step instructions** - **Use the suggested AI commands** - **Reference this when stuck** ## ๐Ÿ“Š CRITICAL: Status Update Requirements **You MUST update the build status throughout implementation using HTTP requests:** ### **Status Update Command:** \`\`\`bash curl -X POST "http://localhost:3002/update-status" \\ -H "Content-Type: application/json" \\ -d '{ "featureId": "${featureId}", "status": "IN_PROGRESS", "detailedStatus": "YOUR_CURRENT_STATUS", "message": "Descriptive message about current progress" }' \`\`\` ### **When to Update Status:** **๐Ÿ”น Starting Implementation:** \`\`\`bash curl -X POST "http://localhost:3002/update-status" -H "Content-Type: application/json" -d '{"featureId": "${featureId}", "status": "IN_PROGRESS", "detailedStatus": "USER_CONFIRMED", "message": "User confirmed implementation, starting development"}' \`\`\` **๐Ÿ”น During Development:** \`\`\`bash # When you start coding curl -X POST "http://localhost:3002/update-status" -H "Content-Type: application/json" -d '{"featureId": "${featureId}", "status": "IN_PROGRESS", "detailedStatus": "IMPLEMENTATION_STARTED", "message": "Started implementing components"}' # When making progress curl -X POST "http://localhost:3002/update-status" -H "Content-Type: application/json" -d '{"featureId": "${featureId}", "status": "IN_PROGRESS", "detailedStatus": "IMPLEMENTATION_IN_PROGRESS", "message": "Created main component, working on API integration"}' \`\`\` **๐Ÿ”น When Complete:** \`\`\`bash curl -X POST "http://localhost:3002/update-status" -H "Content-Type: application/json" -d '{"featureId": "${featureId}", "status": "COMPLETED", "detailedStatus": "USER_APPROVED", "message": "Feature implementation completed and approved. All requirements met."}' \`\`\` ### **Status Types Reference:** - **Main Status:** \`IN_PROGRESS\`, \`COMPLETED\`, \`FAILED\`, \`CANCELLED\` - **Detailed Status:** \`USER_CONFIRMED\` โ†’ \`IMPLEMENTATION_STARTED\` โ†’ \`IMPLEMENTATION_IN_PROGRESS\` โ†’ \`USER_REVIEWING\` โ†’ \`USER_APPROVED\` ## Your Task 1. **READ** the feature specification in FEATURE_SPEC.md 2. **ANALYZE** the requirements and acceptance criteria 3. **PLAN** the implementation approach within the existing AIPM structure 4. **UPDATE STATUS** to USER_CONFIRMED before starting 5. **CREATE** the necessary files in the appropriate directories 6. **UPDATE STATUS** during implementation milestones 7. **WRITE** tests for the feature 8. **UPDATE STATUS** to COMPLETED when finished 9. **ENSURE** the implementation meets all requirements ## Implementation Approach - **Start by analyzing the feature requirements** - **Identify what UI components, API endpoints, and data models are needed** - **Create a plan for the implementation within the existing codebase** - **Update status before major implementation phases** - **Implement the feature step by step in the current workspace** - **Write comprehensive tests** - **Document your implementation** - **Update final status when complete** ## Code Quality Requirements - Use TypeScript for type safety - Follow React best practices - Include proper error handling - Write clean, maintainable code - Add appropriate comments and documentation - Ensure accessibility compliance - Make the UI responsive - Follow existing AIPM codebase patterns ## ๐ŸŽฏ FIRST STEPS 1. **Read FEATURE_SPEC.md** to understand the requirements 2. **Review IMPLEMENTATION_PLAN.md** for the structured checklist 3. **Update status to USER_CONFIRMED** using the curl command above 4. **Start coding the feature** immediately in the current workspace 5. **Update status regularly** during implementation 6. **Write tests** as you go ## ๐Ÿ’ก Essential AI Commands to Use ### **Analysis & Planning** \`\`\` "Read the FEATURE_SPEC.md file and break down the requirements into implementable components" "Analyze the feature requirements and create a detailed implementation plan" "What are the key components needed for this feature?" \`\`\` ### **Component Creation** \`\`\` "Create a React component for this feature following AIPM design patterns" "Add proper TypeScript types for this component" "Implement responsive design and accessibility features" \`\`\` ### **API Integration** \`\`\` "Create API endpoints for this feature using the existing AIPM backend structure" "Add proper error handling and validation" \`\`\` ### **Testing** \`\`\` "Write unit tests for this component using React Testing Library" "Create integration tests for the API endpoints" \`\`\` ### **Code Quality** \`\`\` "Review this code and suggest improvements for performance and maintainability" "Ensure this follows AIPM coding standards" \`\`\` ## ๐Ÿ”ง When You Get Stuck ### **If AI seems confused about the feature:** \`\`\` "Let me clarify the requirements. Read the FEATURE_SPEC.md again and ask me any questions you have" \`\`\` ### **If integration is challenging:** \`\`\` "Show me how similar features are implemented in the existing AIPM codebase" \`\`\` ### **If you're stuck on a specific part:** \`\`\` "I'm having trouble with [specific issue]. Can you help me debug this step by step?" \`\`\` **BEGIN IMPLEMENTATION NOW!** `; writeFileSync(`${featureDir}/AI_INSTRUCTIONS.md`, aiInstructions); // Create README const readme = `# AIPM Feature Implementation: ${featureId} This directory contains the implementation for feature: **${featureId}** ## Quick Start 1. Read the \`FEATURE_SPEC.md\` file to understand the requirements 2. Follow the \`AI_INSTRUCTIONS.md\` for implementation guidance 3. Use Cursor's AI capabilities to implement the feature 4. Integrate the implementation with the existing AIPM codebase ## Files in this directory: - \`FEATURE_SPEC.md\` - Complete feature specification - \`AI_INSTRUCTIONS.md\` - Instructions for Cursor AI - \`.cursor-feature.json\` - Feature metadata ## Implementation Notes - This feature should be integrated with the existing AIPM codebase - Follow the existing project structure and patterns - Use the existing components, services, and utilities where possible - Ensure the implementation works with the current AIPM architecture Happy coding! ๐Ÿš€ `; writeFileSync(`${featureDir}/README.md`, readme); // Step 4: Report workspace ready await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'WORKSPACE_READY', 'Workspace ready, opening Cursor...'); // Create implementation plan const implementationPlan = `# Implementation Plan: ${featureId} ## Feature Overview ${requirements} ## ๐Ÿ“Š Status Update Checklist **CRITICAL: You MUST update status at each milestone using:** \`\`\`bash curl -X POST "http://localhost:3002/update-status" -H "Content-Type: application/json" -d '{"featureId": "${featureId}", "status": "STATUS", "detailedStatus": "DETAILED_STATUS", "message": "Progress message"}' \`\`\` ### Status Update Milestones: - [ ] **USER_CONFIRMED** - Before starting any implementation - [ ] **IMPLEMENTATION_STARTED** - When you begin coding - [ ] **IMPLEMENTATION_IN_PROGRESS** - During major development phases - [ ] **USER_REVIEWING** - When implementation is complete, awaiting review - [ ] **USER_APPROVED** - Final status when feature is complete and approved ## Analysis Based on the requirements, this feature likely needs: ### UI Components - [ ] Identify required React components - [ ] Create or modify existing components - [ ] Ensure responsive design - [ ] Add proper accessibility features - [ ] **Update status: IMPLEMENTATION_IN_PROGRESS** (UI phase) ### API Integration - [ ] Identify required API endpoints - [ ] Create or modify backend routes - [ ] Add proper error handling - [ ] Implement data validation - [ ] **Update status: IMPLEMENTATION_IN_PROGRESS** (API phase) ### Data Models - [ ] Identify required data structures - [ ] Create or modify database models - [ ] Add proper relationships - [ ] Implement data validation - [ ] **Update status: IMPLEMENTATION_IN_PROGRESS** (Data phase) ### Testing - [ ] Write unit tests for components - [ ] Write integration tests for API - [ ] Write end-to-end tests - [ ] Ensure test coverage - [ ] **Update status: USER_REVIEWING** (Testing complete) ### Final Steps - [ ] Review all requirements are met - [ ] Test feature thoroughly - [ ] Document implementation - [ ] **Update status: USER_APPROVED** (Feature complete) ## Next Steps 1. **Update status to USER_CONFIRMED** before starting 2. Review the existing AIPM codebase structure 3. Identify where this feature fits in the architecture 4. **Update status to IMPLEMENTATION_STARTED** when beginning 5. Start implementing the most critical components first 6. **Update status regularly** during development phases 7. Test each component as you build it 8. Integrate with existing functionality 9. **Update final status to USER_APPROVED** when complete ## Integration Points - Frontend: \`frontend/app/\` directory - Backend: \`backend/src/\` directory - Database: \`backend/prisma/\` directory - Components: \`frontend/app/components/\` directory - Pages: \`frontend/app/\` directory (Next.js pages) `; writeFileSync(`${featureDir}/IMPLEMENTATION_PLAN.md`, implementationPlan); // Open files in current Cursor instance (don't open new window) const filesToOpen = [ `${featureDir}/FEATURE_SPEC.md`, `${featureDir}/AI_INSTRUCTIONS.md`, `${featureDir}/IMPLEMENTATION_PLAN.md` ]; // Try to open files in current Cursor instance using different methods const { exec } = await import('child_process'); const { promisify } = await import('util'); const execAsync = promisify(exec); try { // Method 1: Try to open files in current Cursor instance await execAsync(`cursor ${filesToOpen.join(' ')}`); } catch (error) { try { // Method 2: Try macOS specific approach await execAsync(`open -a Cursor ${filesToOpen.join(' ')}`); } catch (error2) { try { // Method 3: Try with full path await execAsync(`"/Applications/Cursor.app/Contents/MacOS/Cursor" ${filesToOpen.join(' ')}`); } catch (error3) { console.log('Could not automatically open files in Cursor. Please open them manually:'); filesToOpen.forEach(file => console.log(` - ${file}`)); } } } // Step 5: Report Cursor opening await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'CURSOR_OPENING', 'Opening Cursor with feature workspace...'); // Try to automatically trigger Cursor's AI (macOS only) try { const implementationCommand = `Feature ${featureId}. Read FEATURE_SPEC.md, AI_INSTRUCTIONS.md (including status update requirements), and IMPLEMENTATION_PLAN.md. Follow the status update checklist and ask for confirmation before implementing.`; const appleScript = ` tell application "Cursor" activate delay 1 tell application "System Events" -- Open the AI chat panel (Cmd+L - new Cursor chat command) key code 37 using {command down} delay 1 -- Type the implementation command keystroke "${implementationCommand.replace(/"/g, '\\"').replace(/\n/g, ' ')}" delay 1 -- Press Enter to send the command key code 36 end tell end tell `; console.log('Executing AppleScript:', appleScript); await execAsync(`osascript -e '${appleScript}'`); console.log('AppleScript executed successfully'); // Step 6: Report AI chat triggered await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'AI_CHAT_TRIGGERED', 'AI chat opened, waiting for user confirmation'); } catch (scriptError) { console.log('AppleScript automation failed:', scriptError); console.log('Files are ready for manual AI interaction'); // Step 6b: Report manual interaction required await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'MANUAL_INTERACTION_REQUIRED', 'Files ready, manual AI interaction required'); } const filesCreated = [ `${featureDir}/FEATURE_SPEC.md`, `${featureDir}/AI_INSTRUCTIONS.md`, `${featureDir}/README.md`, `${featureDir}/IMPLEMENTATION_PLAN.md`, `${featureDir}/.cursor-feature.json` ]; // Step 7: Report final workspace prepared status await this.aipmClient.updateBuildStatus(featureId, 'IN_PROGRESS', 'WORKSPACE_PREPARED', `Feature workspace prepared for "${featureId}". Files created and Cursor opened. Cursor's AI will ask for confirmation before implementing.`); const result = { success: true, featureId: featureId, buildTime: '0s', filesCreated: filesCreated, testsWritten: 0, status: 'WORKSPACE_PREPARED', message: `Feature workspace prepared for "${featureId}". Files created and Cursor opened. Cursor's AI will ask for confirmation before implementing.`, implementationGuide: `# Cursor AI Implementation Guide ## Feature: ${featureId} ### Requirements ${requirements} ${acceptanceCriteria ? `### Acceptance Criteria ${acceptanceCriteria}` : ''} ## Current Status: โณ **Workspace Prepared** The feature workspace has been created and Cursor has been opened with the implementation files. Cursor's AI will now: 1. **Read the FEATURE_SPEC.md** file to understand the requirements 2. **Review the AI_INSTRUCTIONS.md** for implementation guidance 3. **Ask for your confirmation** before proceeding with implementation 4. **Wait for your approval** before making any code changes ## Next Steps 1. **Review the requirements** in Cursor's chat 2. **Confirm implementation** when you're ready 3. **Monitor the implementation** as Cursor creates the code 4. **Test the feature** once implementation is complete ## Workspace Location \`${featureDir}\` ## What Cursor Will Do Once you confirm, Cursor will: - Analyze the feature requirements - Create necessary components and services - Integrate with the existing AIPM codebase - Write tests for the implementation - Ensure the feature meets all requirements **The feature is NOT implemented yet - it's ready for your review and confirmation!**`, filesToOpen: filesToOpen, workspacePath: featureDir, currentWorkspace: currentWorkspace }; // Report the build results back to AIPM await this.reportBuildResultsToAIPM(featureId, result); return result; } catch (error) { console.error('โŒ HTTP: Build feature failed:', error); // Report failure status await this.aipmClient.updateBuildStatus(featureId, 'FAILED', 'BUILD_FAILED', `Build failed: ${error instanceof Error ? error.message : 'Unknown error'}`); const errorResult = { success: false, featureId: featureId, buildTime: '0s', filesCreated: [], testsWritten: 0, status: 'BUILD_FAILED', errors: [error instanceof Error ? error.message : 'Unknown error'], message: 'Failed to create feature files in current workspace' }; // Report the error back to AIPM await this.reportBuildResultsToAIPM(featureId, errorResult); return errorResult; } } async handleImplementFeature(args) { const { featureId, requirements, acceptanceCriteria, analysis, instructions, filesToOpen } = args; if (!featureId || !requirements || !instructions) { throw new Error('featureId, requirements, and instructions are required'); } try { console.log('๐Ÿš€ Starting feature implementation for:', featureId); console.log('๐Ÿ“‹ Requirements:', requirements); console.log('๐Ÿ“ Files to open:', filesToOpen); // Open Cursor with the specified files if (filesToOpen && filesToOpen.length > 0) { await this.openCursorWithFiles(filesToOpen); } // Create implementation guide const implementationGuide = this.createImplementationGuide(featureId, requirements, acceptanceCriteria, analysis, instructions); return { success: true, featureId, message: 'Feature implementation started! Cursor should be open with the relevant files.', implementationGuide, filesToOpen, instructions }; } catch (error) { console.error('โŒ Feature implementation failed:', error); return { success: false, featureId, errors: [error instanceof Error ? error.message : 'Unknown error'] }; } } async openCursorWithFiles(files) { try { // Open Cursor with the specified files const cursorProcess = spawn('cursor', [ '--new-window', ...files ], { stdio: 'inherit', detached: true }); // Wait a moment for Cursor to open await new Promise(resolve => setTimeout(resolve, 2000)); } catch (error) { console.error('Failed to open Cursor:', error); throw error; } } createImplementationGuide(featureId, requirements, acceptanceCriteria, analysis, instructions) { return `# Feature Implementation Guide: ${featureId} ## Requirements ${requirements} ${acceptanceCriteria ? `## Acceptance Criteria ${acceptanceCriteria}` : ''} ${analysis ? `## Analysis Results - Has UI: ${analysis.hasUI ? 'Yes' : 'No'} - Has API: ${analysis.hasAPI ? 'Yes' : 'No'} - Has Database: ${analysis.hasDatabase ? 'Yes' : 'No'} - Is CRUD: ${analysis.isCRUD ? 'Yes' : 'No'} - Data Types: ${analysis.dataTypes?.join(', ') || 'None'}` : ''} ## Implementation Instructions ${instructions || 'No specific instructions provided.'} ## Next Steps 1. Review the requirements and analysis above 2. Open the relevant files in Cursor 3. Implement the feature according to the instructions 4. Test your implementation 5. Update the feature status in AIPM when complete ## Tips - Follow existing code patterns in the project - Use TypeScript for type safety - Add proper error handling - Write tests for new functionality - Update documentation as needed Generated on: ${new Date().toISOString()} `; } async analyzeFeatureRequirements(requirements, acceptanceCriteria) { const text = `${requirements} ${acceptanceCriteria || ''}`.toLowerCase(); // Analyze requirements for different aspects const analysis = { hasUI: this.containsUIKeywords(text), hasAPI: this.containsAPIKeywords(text), hasDatabase: this.containsDatabaseKeywords(text), isCRUD: this.containsCRUDKeywords(text), dataTypes: this.extractDataTypes(text), complexity: this.assessComplexity(text), estimatedHours: this.estimateHours(text), priority: this.assessPriority(text) }; console.log('๐Ÿ“Š Feature Analysis:', analysis); return analysis; } containsUIKeywords(text) { const uiKeywords = [ 'ui', 'interface', 'component', 'form', 'button', 'modal', 'page', 'screen', 'view', 'display', 'user interface', 'dashboard', 'chart', 'analytics', 'widget', 'card', 'table', 'list', 'grid', 'layout', 'design', 'frontend', 'react', 'component', 'visual', 'graph', 'plot', 'statistics', 'metrics', 'report', 'summary' ]; return uiKeywords.some(keyword => text.includes(keyword)); } containsAPIKeywords(text) { const apiKeywords = [ 'api', 'endpoint', 'route', 'controller', 'service', 'fetch', 'request', 'response', 'http', 'backend', 'server', 'data', 'save', 'load', 'retrieve', 'update', 'create', 'delete', 'post', 'get', 'put', 'patch', 'rest', 'graphql', 'websocket' ]; return apiKeywords.some(keyword => text.includes(keyword)); } containsDatabaseKeywords(text) { const dbKeywords = [ 'database', 'model', 'schema', 'table', 'query', 'data', 'store', 'save', 'persist', 'entity', 'record', 'field', 'column', 'migration', 'prisma', 'sql', 'nosql' ]; return dbKeywords.some(keyword =>