UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

271 lines (270 loc) 9.82 kB
import { getProjectOperations } from '../core/operations/project-operations.js'; import logger from '../../../logger.js'; import path from 'path'; import fs from 'fs/promises'; export async function extractProjectFromContext(context, projectPath) { const workingPath = projectPath || process.cwd(); try { logger.debug({ workingPath, sessionId: context.sessionId }, 'Starting project context extraction'); if (context.currentProject) { logger.debug({ currentProject: context.currentProject }, 'Found project in session context'); return { projectId: context.currentProject, projectName: context.currentProject, source: 'session', confidence: 0.95 }; } const gitResult = await extractFromGitRemote(workingPath); if (gitResult.confidence > 0.8) { logger.debug({ gitResult }, 'Extracted project from git remote'); return gitResult; } const packageResult = await extractFromPackageJson(workingPath); if (packageResult.confidence > 0.7) { logger.debug({ packageResult }, 'Extracted project from package.json'); return packageResult; } const directoryResult = extractFromDirectoryName(workingPath); logger.debug({ directoryResult }, 'Using directory name as project context'); return directoryResult; } catch (error) { logger.warn({ error, workingPath }, 'Project context extraction failed, using fallback'); return { projectId: 'default-project', projectName: 'Default Project', source: 'fallback', confidence: 0.1 }; } } export async function extractEpicFromContext(context, projectId) { try { logger.debug({ projectId, sessionId: context.sessionId }, 'Starting epic context extraction'); if (context.currentTask) { const epicFromTask = await extractEpicFromTask(context.currentTask); if (epicFromTask.confidence > 0.8) { logger.debug({ epicFromTask }, 'Found epic from current task'); return epicFromTask; } } if (projectId) { const projectEpic = await extractEpicFromProject(projectId); if (projectEpic.confidence > 0.6) { logger.debug({ projectEpic }, 'Found epic from project'); return projectEpic; } } const generatedEpic = generateEpicFromProject(projectId || 'default-project'); logger.debug({ generatedEpic }, 'Generated epic from project'); return generatedEpic; } catch (error) { logger.warn({ error, projectId }, 'Epic context extraction failed, using fallback'); if (projectId && projectId !== 'default-project') { return generateEpicFromProject(projectId); } return { epicId: 'default-epic', epicName: 'Default Epic', source: 'fallback', confidence: 0.1 }; } } async function extractFromGitRemote(projectPath) { try { const { exec } = await import('child_process'); const { promisify } = await import('util'); const execAsync = promisify(exec); const { stdout } = await execAsync('git remote get-url origin', { cwd: projectPath }); const remoteUrl = stdout.trim(); if (remoteUrl) { let projectName = ''; const httpsMatch = remoteUrl.match(/https:\/\/[^/]+\/[^/]+\/([^/]+)(?:\.git)?$/); if (httpsMatch) { projectName = httpsMatch[1]; } const sshMatch = remoteUrl.match(/git@[^:]+:([^/]+\/)?([^/]+)(?:\.git)?$/); if (sshMatch) { projectName = sshMatch[2]; } if (projectName) { const projectId = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-'); return { projectId, projectName, source: 'git', confidence: 0.85 }; } } } catch (error) { logger.debug({ error, projectPath }, 'Git remote extraction failed'); } return { projectId: 'unknown-git-project', projectName: 'Unknown Git Project', source: 'git', confidence: 0.2 }; } async function extractFromPackageJson(projectPath) { try { const packageJsonPath = path.join(projectPath, 'package.json'); const packageContent = await fs.readFile(packageJsonPath, 'utf-8'); const packageJson = JSON.parse(packageContent); if (packageJson.name) { const projectName = packageJson.name; const projectId = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-'); return { projectId, projectName, source: 'package', confidence: 0.75 }; } } catch (error) { logger.debug({ error, projectPath }, 'Package.json extraction failed'); } return { projectId: 'unknown-package-project', projectName: 'Unknown Package Project', source: 'package', confidence: 0.2 }; } function extractFromDirectoryName(projectPath) { const directoryName = path.basename(projectPath); const projectId = directoryName.toLowerCase().replace(/[^a-z0-9-]/g, '-'); return { projectId, projectName: directoryName, source: 'directory', confidence: 0.6 }; } async function extractEpicFromTask(taskId) { try { const { getTaskOperations } = await import('../core/operations/task-operations.js'); const taskOps = getTaskOperations(); const taskResult = await taskOps.getTask(taskId); if (taskResult.success && taskResult.data?.epicId) { return { epicId: taskResult.data.epicId, epicName: taskResult.data.epicId, source: 'session', confidence: 0.9 }; } } catch (error) { logger.debug({ error, taskId }, 'Epic extraction from task failed'); } return { epicId: 'unknown-epic', epicName: 'Unknown Epic', source: 'session', confidence: 0.1 }; } async function extractEpicFromProject(projectId) { try { const projectOps = getProjectOperations(); const projectResult = await projectOps.getProject(projectId); if (projectResult.success && projectResult.data) { const epicIds = projectResult.data.epicIds; if (epicIds && epicIds.length > 0) { const firstEpicId = epicIds[0]; return { epicId: firstEpicId, epicName: firstEpicId, source: 'project', confidence: 0.7 }; } } } catch (error) { logger.debug({ error, projectId }, 'Epic extraction from project failed'); } const epicId = `project-epic-1`; return { epicId, epicName: epicId, source: 'project', confidence: 0.7 }; } export async function extractTaskListContext(projectPath) { const workingPath = projectPath || process.cwd(); try { logger.debug({ workingPath }, 'Starting task list context extraction'); const { TaskListIntegrationService } = await import('../integrations/task-list-integration.js'); const taskListService = TaskListIntegrationService.getInstance(); const existingTaskList = await taskListService.detectExistingTaskList(workingPath); if (existingTaskList && existingTaskList.isAccessible) { logger.debug({ taskListFile: existingTaskList.fileName }, 'Found existing task list file'); const parseResult = await taskListService.parseTaskList(existingTaskList.filePath); if (parseResult.success && parseResult.taskListData) { const projectName = parseResult.taskListData.metadata.projectName; const projectId = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-'); return { projectId, projectName, source: 'directory', confidence: 0.85 }; } } logger.debug({ workingPath }, 'No accessible task list found'); } catch (error) { logger.debug({ error, workingPath }, 'Task list context extraction failed'); } return { projectId: 'no-task-list-project', projectName: 'No Task List Project', source: 'fallback', confidence: 0.1 }; } function generateEpicFromProject(projectId) { const epicId = `${projectId}-main-epic`; const epicName = `${projectId} Main Epic`; return { epicId, epicName, source: 'default', confidence: 0.5 }; } export function sanitizeProjectId(projectId) { if (!projectId) return ''; let sanitized = projectId .toLowerCase() .replace(/[^a-z0-9-]/g, '-') .replace(/--+/g, '-'); if (sanitized.match(/^-+$/) || sanitized === '') { return ''; } sanitized = sanitized.replace(/^-+|-+$/g, ''); return sanitized; } export function sanitizeEpicId(epicId) { if (!epicId) return ''; let sanitized = epicId .toLowerCase() .replace(/[^a-z0-9-]/g, '-') .replace(/--+/g, '-'); if (sanitized.match(/^-+$/) || sanitized === '') { return ''; } sanitized = sanitized.replace(/^-+|-+$/g, ''); return sanitized; }