UNPKG

@al76/tools-and-spec-workflow-mcp

Version:

MCP server for spec-driven development workflow with real-time web dashboard

154 lines (151 loc) 6 kB
import { PathUtils } from '../core/path-utils.js'; import { readFile, access, readdir } from 'fs/promises'; import { join } from 'path'; import { constants } from 'fs'; export const getSpecContextTool = { name: 'get-spec-context', description: `加载现有规范文档以恢复工作。 # 说明 仅在中断后返回处理现有规范或开始处理您未创建的规范时调用。如果您刚刚创建了文档,在活跃规范创建期间永远不要使用。加载 requirements.md、design.md 和 tasks.md 以获取实现上下文。`, inputSchema: { type: 'object', properties: { projectPath: { type: 'string', description: '项目根目录的绝对路径' }, specName: { type: 'string', description: '要加载上下文的规范名称' } }, required: ['projectPath', 'specName'] } }; export async function getSpecContextHandler(args, context) { const { projectPath, specName } = args; try { const specPath = PathUtils.getSpecPath(projectPath, specName); // Check if spec directory exists try { await access(specPath, constants.F_OK); } catch { // Check if there are any specs at all to suggest alternatives const specsRoot = PathUtils.getSpecPath(projectPath, ''); try { await access(specsRoot, constants.F_OK); const availableSpecs = await readdir(specsRoot, { withFileTypes: true }); const specNames = availableSpecs .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); if (specNames.length > 0) { return { success: false, message: `No specification found for: ${specName}`, data: { availableSpecs: specNames, suggestedSpecs: specNames.slice(0, 3) // Show first 3 as suggestions }, nextSteps: [ `Available specs: ${specNames.join(', ')}`, 'Use an existing spec name', 'Or create new with create-spec-doc' ] }; } } catch { // Specs directory doesn't exist } return { success: false, message: `No specification found for: ${specName}`, nextSteps: [ 'Create spec with create-spec-doc', 'Check spec name spelling', 'Verify project setup' ] }; } const specFiles = [ { name: 'requirements.md', title: 'Requirements' }, { name: 'design.md', title: 'Design' }, { name: 'tasks.md', title: 'Tasks' } ]; const sections = []; const documentStatus = { requirements: false, design: false, tasks: false }; let hasContent = false; for (const file of specFiles) { const filePath = join(specPath, file.name); try { await access(filePath, constants.F_OK); const content = await readFile(filePath, 'utf-8'); if (content && content.trim()) { sections.push(`### ${file.title}\n${content.trim()}`); hasContent = true; // Update status const docName = file.name.replace('.md', ''); documentStatus[docName] = true; } } catch { // File doesn't exist, skip } } if (!hasContent) { return { success: true, message: `Specification documents for "${specName}" exist but are empty`, data: { context: `## Specification Context\n\nNo specification documents found for: ${specName}`, specName, documents: documentStatus }, nextSteps: [ `Add content to .spec-workflow/specs/${specName}/`, 'Create missing documents', 'Ensure all three docs have content' ] }; } // Format the complete specification context const formattedContext = `## Specification Context (Pre-loaded): ${specName} ${sections.join('\n\n---\n\n')} **Note**: Specification documents have been pre-loaded. Do not use get-content to fetch them again.`; return { success: true, message: `Specification context loaded successfully for: ${specName}`, data: { context: formattedContext, specName, documents: documentStatus, sections: sections.length, specPath }, nextSteps: [ 'Context loaded - proceed with implementation', 'Reference requirements and design for each task', 'Update task status with manage-tasks' ], projectContext: { projectPath, workflowRoot: PathUtils.getWorkflowRoot(projectPath), dashboardUrl: context.dashboardUrl } }; } catch (error) { return { success: false, message: `Failed to load specification context: ${error.message}`, nextSteps: [ 'Check project path', 'Verify spec name', 'Check file permissions', 'Create spec if missing' ] }; } } //# sourceMappingURL=get-spec-context.js.map