@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
JavaScript
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