UNPKG

@jmkim85/dev-flow-mcp

Version:

MCP-based Dev Flow - AI-powered development workflow management with 13 essential tools for TDD and context management

192 lines 7.76 kB
/** * Template Manager for Dev Flow MCP v2.1 * Handles project template initialization and management */ import { promises as fs } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; export class TemplateManager { projectRoot; templatesDir; constructor(projectRoot) { this.projectRoot = projectRoot; // Get the directory where this script is located const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); this.templatesDir = join(__dirname, '..', 'templates'); } async initializeTemplate(templateType) { try { const templateInfo = this.getTemplateInfo(templateType); const targetPath = join(this.projectRoot, templateInfo.targetFile); // Check if file already exists try { await fs.access(targetPath); return { success: true, message: `${templateInfo.displayName} already exists at ${templateInfo.targetFile}`, file_path: targetPath, skipped: true }; } catch { // File doesn't exist, proceed with creation } // Read template content const templatePath = join(this.templatesDir, templateInfo.templateFile); let templateContent; try { templateContent = await fs.readFile(templatePath, 'utf-8'); } catch (error) { return { success: false, message: `Template file not found: ${templateInfo.templateFile}. Error: ${error.message}` }; } // Process template content (replace placeholders) const processedContent = await this.processTemplate(templateContent, templateType); // Ensure target directory exists const targetDir = dirname(targetPath); await fs.mkdir(targetDir, { recursive: true }); // Write the processed template await fs.writeFile(targetPath, processedContent, 'utf-8'); return { success: true, message: `${templateInfo.displayName} created successfully at ${templateInfo.targetFile}`, file_path: targetPath, skipped: false }; } catch (error) { return { success: false, message: `Error initializing ${templateType} template: ${error.message}` }; } } getTemplateInfo(templateType) { const templates = { project_overview: { templateFile: 'project_overview.md', targetFile: 'project_overview.md', displayName: 'Project Overview' }, tasklist: { templateFile: 'tasklist_template.yaml', targetFile: '.devflow/tasks.yaml', displayName: 'Task List' }, readme: { templateFile: 'readme_template.md', targetFile: 'README.md', displayName: 'README' }, requirements: { templateFile: 'requirements.md', targetFile: '.devflow/requirements.md', displayName: 'Requirements Document' }, design_spec: { templateFile: 'design_spec.md', targetFile: '.devflow/design_spec.md', displayName: 'Design Specification' } }; return templates[templateType]; } async processTemplate(content, templateType) { // Get project name from directory const projectName = this.projectRoot.split(/[/\\]/).pop() || 'My Project'; const currentDate = new Date().toISOString().split('T')[0]; // Basic placeholder replacements let processed = content .replace(/\[Project Name\]/g, projectName) .replace(/\[Date\]/g, currentDate) .replace(/\[project-directory\]/g, projectName.toLowerCase().replace(/\s+/g, '-')) .replace(/\[repository-url\]/g, `https://github.com/username/${projectName.toLowerCase().replace(/\s+/g, '-')}`); // Template-specific processing if (templateType === 'tasklist') { // Update timestamps in YAML const timestamp = new Date().toISOString(); processed = processed .replace(/2024-01-01T00:00:00Z/g, timestamp); } // Detect project type and customize accordingly const projectType = await this.detectProjectType(); processed = this.customizeForProjectType(processed, projectType); return processed; } async detectProjectType() { const indicators = [ { file: 'package.json', type: 'Node.js' }, { file: 'requirements.txt', type: 'Python' }, { file: 'Cargo.toml', type: 'Rust' }, { file: 'go.mod', type: 'Go' }, { file: 'pom.xml', type: 'Java (Maven)' }, { file: 'build.gradle', type: 'Java (Gradle)' } ]; for (const indicator of indicators) { try { await fs.access(join(this.projectRoot, indicator.file)); return indicator.type; } catch { // File doesn't exist, continue checking } } return 'General'; } customizeForProjectType(content, projectType) { const testCommands = { 'Node.js': 'npm test', 'Python': 'pytest', 'Rust': 'cargo test', 'Go': 'go test ./...', 'Java (Maven)': 'mvn test', 'Java (Gradle)': 'gradle test', 'General': 'npm test' }; const installCommands = { 'Node.js': 'npm install', 'Python': 'pip install -r requirements.txt', 'Rust': 'cargo build', 'Go': 'go mod download', 'Java (Maven)': 'mvn install', 'Java (Gradle)': 'gradle build', 'General': 'npm install' }; const languages = { 'Node.js': 'javascript', 'Python': 'python', 'Rust': 'rust', 'Go': 'go', 'Java (Maven)': 'java', 'Java (Gradle)': 'java', 'General': 'javascript' }; return content .replace(/\[test command\]/g, testCommands[projectType] || 'npm test') .replace(/\[installation command\]/g, installCommands[projectType] || 'npm install') .replace(/\[development command\]/g, installCommands[projectType] || 'npm start') .replace(/\[language\]/g, languages[projectType] || 'javascript') .replace(/npm test/g, testCommands[projectType] || 'npm test') .replace(/\[Primary programming language\]/g, projectType); } async listAvailableTemplates() { return ['project_overview', 'tasklist', 'readme', 'requirements', 'design_spec']; } async getTemplatePreview(templateType) { const templateInfo = this.getTemplateInfo(templateType); const templatePath = join(this.templatesDir, templateInfo.templateFile); try { const content = await fs.readFile(templatePath, 'utf-8'); // Return first 10 lines as preview return content.split('\n').slice(0, 10).join('\n') + '\n...'; } catch (error) { return `Preview not available: ${error.message}`; } } } //# sourceMappingURL=template-manager.js.map