UNPKG

taskflow-ai

Version:

TaskFlow AI - 智能PRD文档解析与任务管理助手,支持多模型AI协同、MCP编辑器集成,专为开发团队设计的CLI工具

1,459 lines (1,451 loc) 199 kB
'use strict'; var fs = require('fs-extra'); var path = require('path'); var MarkdownIt = require('markdown-it'); var fs$1 = require('fs'); var os = require('os'); var winston = require('winston'); var axios = require('axios'); var crypto = require('crypto'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs); var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path); var fs__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(fs$1); var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os); var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto); /** * 模型相关类型定义 */ /** * 消息角色 */ exports.MessageRole = void 0; (function (MessageRole) { MessageRole["USER"] = "user"; MessageRole["ASSISTANT"] = "assistant"; MessageRole["SYSTEM"] = "system"; })(exports.MessageRole || (exports.MessageRole = {})); /** * 文件类型 */ exports.FileType = void 0; (function (FileType) { FileType["MARKDOWN"] = "markdown"; FileType["PDF"] = "pdf"; FileType["WORD"] = "word"; FileType["TEXT"] = "text"; FileType["JSON"] = "json"; FileType["UNKNOWN"] = "unknown"; })(exports.FileType || (exports.FileType = {})); /** * 文档处理器 - 支持多种格式的文档解析 * 作为PRD解析引擎的核心组件 */ /** * 文档类型枚举 */ var DocumentType; (function (DocumentType) { DocumentType["MARKDOWN"] = "markdown"; DocumentType["TEXT"] = "text"; DocumentType["JSON"] = "json"; DocumentType["HTML"] = "html"; DocumentType["WORD"] = "word"; DocumentType["PDF"] = "pdf"; })(DocumentType || (DocumentType = {})); /** * 章节类型枚举 */ var SectionType; (function (SectionType) { SectionType["OVERVIEW"] = "overview"; SectionType["REQUIREMENTS"] = "requirements"; SectionType["FEATURES"] = "features"; SectionType["TECHNICAL"] = "technical"; SectionType["TIMELINE"] = "timeline"; SectionType["RESOURCES"] = "resources"; SectionType["APPENDIX"] = "appendix"; SectionType["OTHER"] = "other"; })(SectionType || (SectionType = {})); /** * 文档处理器类 */ class DocumentProcessor { constructor(logger) { this.logger = logger; this.markdownParser = new MarkdownIt({ html: true, linkify: true, typographer: true }); } /** * 处理文档文件 * @param filePath 文件路径 * @param options 处理选项 */ async processDocument(filePath, options = {}) { try { this.logger.info(`开始处理文档: ${filePath}`); // 检查文件是否存在 if (!await fs__namespace.pathExists(filePath)) { throw new Error(`文件不存在: ${filePath}`); } // 获取文件信息 const stats = await fs__namespace.stat(filePath); const documentType = this.detectDocumentType(filePath); // 读取文件内容 const content = await this.readFileContent(filePath, documentType); // 生成元数据 const metadata = { fileName: path__namespace.basename(filePath), fileSize: stats.size, createdAt: stats.birthtime, modifiedAt: stats.mtime, documentType, language: options.detectLanguage ? this.detectLanguage(content) : 'zh-CN', wordCount: this.countWords(content), estimatedReadTime: Math.ceil(this.countWords(content) / 200) // 假设每分钟200字 }; // 解析文档结构 const sections = await this.parseDocumentStructure(content, documentType, options); const documentStructure = { title: this.extractTitle(content, documentType) || metadata.fileName, sections, metadata }; this.logger.info(`文档处理完成: ${sections.length} 个章节`); return documentStructure; } catch (error) { this.logger.error(`文档处理失败: ${error.message}`); throw error; } } /** * 检测文档类型 * @param filePath 文件路径 */ detectDocumentType(filePath) { const ext = path__namespace.extname(filePath).toLowerCase(); switch (ext) { case '.md': case '.markdown': return DocumentType.MARKDOWN; case '.txt': return DocumentType.TEXT; case '.json': return DocumentType.JSON; case '.html': case '.htm': return DocumentType.HTML; case '.docx': case '.doc': return DocumentType.WORD; case '.pdf': return DocumentType.PDF; default: return DocumentType.TEXT; } } /** * 读取文件内容 * @param filePath 文件路径 * @param documentType 文档类型 */ async readFileContent(filePath, documentType) { switch (documentType) { case DocumentType.MARKDOWN: case DocumentType.TEXT: case DocumentType.HTML: case DocumentType.JSON: return await fs__namespace.readFile(filePath, 'utf-8'); case DocumentType.WORD: // TODO: 实现Word文档解析 this.logger.warn('Word文档解析暂未实现,将作为文本处理'); return await fs__namespace.readFile(filePath, 'utf-8'); case DocumentType.PDF: // TODO: 实现PDF文档解析 this.logger.warn('PDF文档解析暂未实现,将作为文本处理'); return await fs__namespace.readFile(filePath, 'utf-8'); default: return await fs__namespace.readFile(filePath, 'utf-8'); } } /** * 解析文档结构 * @param content 文档内容 * @param documentType 文档类型 * @param options 处理选项 */ async parseDocumentStructure(content, documentType, options) { switch (documentType) { case DocumentType.MARKDOWN: return this.parseMarkdownStructure(content, options); case DocumentType.TEXT: return this.parseTextStructure(content, options); case DocumentType.JSON: return this.parseJsonStructure(content, options); case DocumentType.HTML: return this.parseHtmlStructure(content, options); default: return this.parseTextStructure(content, options); } } /** * 解析Markdown文档结构 * @param content Markdown内容 * @param options 处理选项 */ parseMarkdownStructure(content, options) { const sections = []; const lines = content.split('\n'); let currentSection = null; let sectionCounter = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // 检测标题 const headerMatch = line.match(/^(#{1,6})\s+(.+)$/); if (headerMatch) { const level = headerMatch[1].length; const title = headerMatch[2].trim(); // 保存上一个章节 if (currentSection) { sections.push(currentSection); } // 创建新章节 currentSection = { id: `section-${++sectionCounter}`, title, content: '', level, subsections: [], type: this.classifySectionType(title), keywords: options.extractKeywords ? this.extractKeywords(title) : [], importance: options.calculateImportance ? this.calculateImportance(title, '') : 0.5 }; } else if (currentSection && line) { // 添加内容到当前章节 currentSection.content += line + '\n'; } } // 添加最后一个章节 if (currentSection) { sections.push(currentSection); } // 后处理:更新重要性评分和关键词 if (options.calculateImportance || options.extractKeywords) { sections.forEach(section => { if (options.extractKeywords) { section.keywords = this.extractKeywords(section.title + ' ' + section.content); } if (options.calculateImportance) { section.importance = this.calculateImportance(section.title, section.content); } }); } return this.buildSectionHierarchy(sections); } /** * 解析纯文本文档结构 * @param content 文本内容 * @param options 处理选项 */ parseTextStructure(content, options) { const sections = []; const paragraphs = content.split(/\n\s*\n/).filter(p => p.trim()); paragraphs.forEach((paragraph, index) => { const lines = paragraph.trim().split('\n'); const title = lines[0].trim(); const content = lines.slice(1).join('\n').trim(); sections.push({ id: `section-${index + 1}`, title: title || `段落 ${index + 1}`, content, level: 1, subsections: [], type: this.classifySectionType(title), keywords: options.extractKeywords ? this.extractKeywords(paragraph) : [], importance: options.calculateImportance ? this.calculateImportance(title, content) : 0.5 }); }); return sections; } /** * 解析JSON文档结构 * @param content JSON内容 * @param options 处理选项 */ parseJsonStructure(content, _options) { try { const data = JSON.parse(content); const sections = []; if (Array.isArray(data)) { data.forEach((item, index) => { sections.push(this.createSectionFromObject(item, `item-${index}`, 1)); }); } else if (typeof data === 'object') { Object.entries(data).forEach(([key, value], index) => { sections.push(this.createSectionFromObject({ [key]: value }, `section-${index + 1}`, 1)); }); } return sections; } catch (error) { this.logger.error(`JSON解析失败: ${error.message}`); return []; } } /** * 解析HTML文档结构 * @param content HTML内容 * @param options 处理选项 */ parseHtmlStructure(content, options) { // 简化的HTML解析,提取标题和内容 const sections = []; const headerRegex = /<h([1-6])[^>]*>(.*?)<\/h[1-6]>/gi; let match; let sectionCounter = 0; while ((match = headerRegex.exec(content)) !== null) { const level = parseInt(match[1]); const title = match[2].replace(/<[^>]*>/g, '').trim(); sections.push({ id: `section-${++sectionCounter}`, title, content: '', // TODO: 提取对应的内容 level, subsections: [], type: this.classifySectionType(title), keywords: options.extractKeywords ? this.extractKeywords(title) : [], importance: options.calculateImportance ? this.calculateImportance(title, '') : 0.5 }); } return this.buildSectionHierarchy(sections); } /** * 从对象创建章节 * @param obj 对象 * @param id 章节ID * @param level 层级 */ createSectionFromObject(obj, id, level) { const title = typeof obj === 'object' && obj !== null ? Object.keys(obj)[0] || id : String(obj); const content = typeof obj === 'object' && obj !== null ? JSON.stringify(obj, null, 2) : String(obj); return { id, title, content, level, subsections: [], type: this.classifySectionType(title), keywords: this.extractKeywords(content), importance: this.calculateImportance(title, content) }; } /** * 构建章节层次结构 * @param sections 扁平的章节列表 */ buildSectionHierarchy(sections) { const result = []; const stack = []; for (const section of sections) { // 找到合适的父级 while (stack.length > 0 && stack[stack.length - 1].level >= section.level) { stack.pop(); } if (stack.length === 0) { // 顶级章节 result.push(section); } else { // 子章节 stack[stack.length - 1].subsections.push(section); } stack.push(section); } return result; } /** * 分类章节类型 * @param title 章节标题 */ classifySectionType(title) { const titleLower = title.toLowerCase(); if (titleLower.includes('概述') || titleLower.includes('overview') || titleLower.includes('简介')) { return SectionType.OVERVIEW; } if (titleLower.includes('需求') || titleLower.includes('requirement')) { return SectionType.REQUIREMENTS; } if (titleLower.includes('功能') || titleLower.includes('feature')) { return SectionType.FEATURES; } if (titleLower.includes('技术') || titleLower.includes('technical') || titleLower.includes('架构')) { return SectionType.TECHNICAL; } if (titleLower.includes('时间') || titleLower.includes('timeline') || titleLower.includes('计划')) { return SectionType.TIMELINE; } if (titleLower.includes('资源') || titleLower.includes('resource')) { return SectionType.RESOURCES; } if (titleLower.includes('附录') || titleLower.includes('appendix')) { return SectionType.APPENDIX; } return SectionType.OTHER; } /** * 提取关键词 * @param text 文本内容 */ extractKeywords(text) { // 简化的关键词提取 const words = text .toLowerCase() .replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 1); // 统计词频 const wordCount = new Map(); words.forEach(word => { wordCount.set(word, (wordCount.get(word) || 0) + 1); }); // 返回出现频率最高的词 return Array.from(wordCount.entries()) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([word]) => word); } /** * 计算章节重要性 * @param title 标题 * @param content 内容 */ calculateImportance(title, content) { let score = 0.5; // 基础分数 // 基于标题的重要性关键词 const importantKeywords = [ '核心', '关键', '重要', '必须', '主要', 'core', 'key', 'important', 'critical', 'main' ]; const titleLower = title.toLowerCase(); importantKeywords.forEach(keyword => { if (titleLower.includes(keyword)) { score += 0.1; } }); // 基于内容长度 const contentLength = content.length; if (contentLength > 1000) score += 0.1; if (contentLength > 2000) score += 0.1; // 基于章节类型 const sectionType = this.classifySectionType(title); switch (sectionType) { case SectionType.REQUIREMENTS: case SectionType.FEATURES: score += 0.2; break; case SectionType.TECHNICAL: score += 0.15; break; case SectionType.OVERVIEW: score += 0.1; break; } return Math.min(1.0, score); } /** * 提取文档标题 * @param content 文档内容 * @param documentType 文档类型 */ extractTitle(content, documentType) { switch (documentType) { case DocumentType.MARKDOWN: { const mdTitleMatch = content.match(/^#\s+(.+)$/m); return mdTitleMatch ? mdTitleMatch[1].trim() : null; } case DocumentType.HTML: { const htmlTitleMatch = content.match(/<h1[^>]*>(.*?)<\/h1>/i); return htmlTitleMatch ? htmlTitleMatch[1].replace(/<[^>]*>/g, '').trim() : null; } default: { const firstLine = content.split('\n')[0].trim(); return firstLine.length > 0 && firstLine.length < 100 ? firstLine : null; } } } /** * 检测文档语言 * @param content 文档内容 */ detectLanguage(content) { // 简化的语言检测 const chineseChars = content.match(/[\u4e00-\u9fa5]/g); const totalChars = content.replace(/\s/g, '').length; if (chineseChars && chineseChars.length / totalChars > 0.3) { return 'zh-CN'; } return 'en-US'; } /** * 统计单词数量 * @param content 文档内容 */ countWords(content) { // 中英文混合的单词统计 const chineseChars = content.match(/[\u4e00-\u9fa5]/g); const englishWords = content.match(/[a-zA-Z]+/g); const chineseCount = chineseChars ? chineseChars.length : 0; const englishCount = englishWords ? englishWords.length : 0; return chineseCount + englishCount; } } /* eslint-disable @typescript-eslint/no-explicit-any */ /** * TaskFlow AI 任务相关类型定义 */ /** * 任务状态枚举 */ exports.TaskStatus = void 0; (function (TaskStatus) { TaskStatus["NOT_STARTED"] = "not_started"; TaskStatus["PENDING"] = "pending"; TaskStatus["IN_PROGRESS"] = "in_progress"; TaskStatus["RUNNING"] = "running"; TaskStatus["COMPLETED"] = "completed"; TaskStatus["DONE"] = "done"; TaskStatus["CANCELLED"] = "cancelled"; TaskStatus["FAILED"] = "failed"; TaskStatus["BLOCKED"] = "blocked"; TaskStatus["ON_HOLD"] = "on_hold"; TaskStatus["REVIEW"] = "review"; TaskStatus["TODO"] = "todo"; })(exports.TaskStatus || (exports.TaskStatus = {})); /** * 任务优先级 */ exports.TaskPriority = void 0; (function (TaskPriority) { TaskPriority["LOW"] = "low"; TaskPriority["MEDIUM"] = "medium"; TaskPriority["HIGH"] = "high"; TaskPriority["CRITICAL"] = "critical"; })(exports.TaskPriority || (exports.TaskPriority = {})); /** * 任务类型 */ exports.TaskType = void 0; (function (TaskType) { TaskType["FEATURE"] = "feature"; TaskType["BUG_FIX"] = "bug_fix"; TaskType["REFACTOR"] = "refactor"; TaskType["TEST"] = "test"; TaskType["DOCUMENT"] = "document"; TaskType["ANALYSIS"] = "analysis"; TaskType["DESIGN"] = "design"; TaskType["DEPLOYMENT"] = "deployment"; TaskType["RESEARCH"] = "research"; })(exports.TaskType || (exports.TaskType = {})); /** * 需求提取器 - 从文档中智能提取和分析需求 * 使用自然语言处理和模式匹配技术 */ /** * 需求类型枚举 */ var RequirementType; (function (RequirementType) { RequirementType["FUNCTIONAL"] = "functional"; RequirementType["NON_FUNCTIONAL"] = "non_functional"; RequirementType["BUSINESS"] = "business"; RequirementType["TECHNICAL"] = "technical"; RequirementType["USER_STORY"] = "user_story"; RequirementType["CONSTRAINT"] = "constraint"; RequirementType["ASSUMPTION"] = "assumption"; // 假设条件 })(RequirementType || (RequirementType = {})); /** * 需求提取器类 */ class RequirementExtractor { constructor(logger) { // 需求识别模式 this.requirementPatterns = { functional: [ /系统(应该|必须|需要|能够)(.+)/gi, /用户(可以|能够|应该)(.+)/gi, /应用程序(应该|必须|需要)(.+)/gi, /(实现|支持|提供)(.+)功能/gi, /the system (should|must|shall|will)(.+)/gi, /users (can|should|must|will be able to)(.+)/gi ], nonFunctional: [ /(性能|响应时间|吞吐量)(.+)/gi, /(安全|权限|认证)(.+)/gi, /(可用性|稳定性|可靠性)(.+)/gi, /(兼容性|扩展性|可维护性)(.+)/gi, /(performance|security|usability|reliability)(.+)/gi ], userStory: [ /作为(.+?),我希望(.+?),以便(.+)/gi, /As a (.+?), I want (.+?) so that (.+)/gi, /Given (.+?) when (.+?) then (.+)/gi ], constraint: [ /(限制|约束|不能|禁止)(.+)/gi, /(constraint|limitation|restriction)(.+)/gi ] }; // 优先级关键词 this.priorityKeywords = { critical: ['关键', '核心', '重要', '必须', 'critical', 'essential', 'must', 'key'], high: ['高', '优先', '重点', 'high', 'priority', 'important'], medium: ['中等', '一般', '普通', 'medium', 'normal', 'standard'], low: ['低', '次要', '可选', 'low', 'optional', 'nice to have'] }; // 复杂度关键词 this.complexityKeywords = { high: ['复杂', '困难', '挑战', '集成', '算法', 'complex', 'difficult', 'challenging', 'integration'], medium: ['中等', '标准', '常规', 'medium', 'standard', 'typical'], low: ['简单', '基础', '直接', 'simple', 'basic', 'straightforward'] }; this.logger = logger; } /** * 从文档结构中提取需求 * @param documentStructure 文档结构 * @param options 提取选项 */ async extractRequirements(documentStructure, options = {}) { try { this.logger.info('开始提取需求'); const requirements = []; const warnings = []; const suggestions = []; // 遍历所有章节提取需求 for (const section of documentStructure.sections) { const sectionRequirements = await this.extractFromSection(section, options); requirements.push(...sectionRequirements); } // 后处理:检测依赖关系 if (options.detectDependencies) { this.detectDependencies(requirements); } // 生成摘要 const summary = this.generateSummary(requirements); // 生成建议 suggestions.push(...this.generateSuggestions(requirements, summary)); // 验证结果 warnings.push(...this.validateRequirements(requirements)); this.logger.info(`需求提取完成: ${requirements.length} 个需求`); return { requirements, summary, warnings, suggestions }; } catch (error) { this.logger.error(`需求提取失败: ${error.message}`); throw error; } } /** * 从单个章节提取需求 * @param section 文档章节 * @param options 提取选项 */ async extractFromSection(section, options) { const requirements = []; const content = section.title + '\n' + section.content; // 根据章节类型选择提取策略 switch (section.type) { case SectionType.REQUIREMENTS: requirements.push(...this.extractFunctionalRequirements(section, content)); requirements.push(...this.extractNonFunctionalRequirements(section, content)); break; case SectionType.FEATURES: requirements.push(...this.extractFeatureRequirements(section, content)); if (options.includeUserStories) { requirements.push(...this.extractUserStories(section, content)); } break; case SectionType.TECHNICAL: requirements.push(...this.extractTechnicalRequirements(section, content)); break; default: // 通用提取 requirements.push(...this.extractGeneralRequirements(section, content)); break; } // 递归处理子章节 for (const subsection of section.subsections) { const subRequirements = await this.extractFromSection(subsection, options); requirements.push(...subRequirements); } return requirements; } /** * 提取功能需求 * @param section 章节 * @param content 内容 */ extractFunctionalRequirements(section, content) { const requirements = []; let reqCounter = 1; this.requirementPatterns.functional.forEach(pattern => { let match; while ((match = pattern.exec(content)) !== null) { const description = match[0].trim(); if (description.length > 10) { // 过滤太短的匹配 requirements.push(this.createRequirement(`${section.id}-func-${reqCounter++}`, this.extractTitle(description), description, RequirementType.FUNCTIONAL, section, content)); } } }); return requirements; } /** * 提取非功能需求 * @param section 章节 * @param content 内容 */ extractNonFunctionalRequirements(section, content) { const requirements = []; let reqCounter = 1; this.requirementPatterns.nonFunctional.forEach(pattern => { let match; while ((match = pattern.exec(content)) !== null) { const description = match[0].trim(); if (description.length > 10) { requirements.push(this.createRequirement(`${section.id}-nonfunc-${reqCounter++}`, this.extractTitle(description), description, RequirementType.NON_FUNCTIONAL, section, content)); } } }); return requirements; } /** * 提取功能特性需求 * @param section 章节 * @param content 内容 */ extractFeatureRequirements(section, content) { const requirements = []; // 按行分析,查找功能描述 const lines = content.split('\n').filter(line => line.trim()); let reqCounter = 1; lines.forEach(line => { const trimmedLine = line.trim(); // 检查是否是功能描述(列表项、编号项等) if (this.isFunctionDescription(trimmedLine)) { requirements.push(this.createRequirement(`${section.id}-feature-${reqCounter++}`, this.extractTitle(trimmedLine), trimmedLine, RequirementType.FUNCTIONAL, section, content)); } }); return requirements; } /** * 提取用户故事 * @param section 章节 * @param content 内容 */ extractUserStories(section, content) { const requirements = []; let storyCounter = 1; this.requirementPatterns.userStory.forEach(pattern => { let match; while ((match = pattern.exec(content)) !== null) { const description = match[0].trim(); requirements.push(this.createRequirement(`${section.id}-story-${storyCounter++}`, `用户故事 ${storyCounter}`, description, RequirementType.USER_STORY, section, content)); } }); return requirements; } /** * 提取技术需求 * @param section 章节 * @param content 内容 */ extractTechnicalRequirements(section, content) { const requirements = []; // 查找技术相关的描述 const techKeywords = ['技术栈', '架构', '数据库', '框架', '接口', 'API', '服务', '组件']; const lines = content.split('\n').filter(line => line.trim()); let reqCounter = 1; lines.forEach(line => { const trimmedLine = line.trim(); if (techKeywords.some(keyword => trimmedLine.includes(keyword)) && trimmedLine.length > 10) { requirements.push(this.createRequirement(`${section.id}-tech-${reqCounter++}`, this.extractTitle(trimmedLine), trimmedLine, RequirementType.TECHNICAL, section, content)); } }); return requirements; } /** * 提取通用需求 * @param section 章节 * @param content 内容 */ extractGeneralRequirements(section, content) { const requirements = []; // 简单的基于关键词的提取 const lines = content.split('\n').filter(line => line.trim()); let reqCounter = 1; lines.forEach(line => { const trimmedLine = line.trim(); if (this.isRequirementLike(trimmedLine)) { requirements.push(this.createRequirement(`${section.id}-req-${reqCounter++}`, this.extractTitle(trimmedLine), trimmedLine, RequirementType.BUSINESS, section, content)); } }); return requirements; } /** * 创建需求对象 * @param id 需求ID * @param title 标题 * @param description 描述 * @param type 类型 * @param section 来源章节 * @param content 章节内容 */ createRequirement(id, title, description, type, section, _content) { return { id, title, description, type, priority: this.analyzePriority(description), category: section.title, source: section.id, dependencies: [], acceptanceCriteria: this.extractAcceptanceCriteria(description), estimatedEffort: this.estimateEffort(description, type), complexity: this.analyzeComplexity(description), tags: this.extractTags(description), stakeholders: this.extractStakeholders(description), businessValue: this.calculateBusinessValue(description, type), technicalRisk: this.calculateTechnicalRisk(description, type), metadata: { extractedAt: new Date(), confidence: this.calculateConfidence(description, type), extractionMethod: 'pattern_matching', keywords: section.keywords, relatedSections: [section.id] } }; } /** * 分析优先级 * @param text 文本 */ analyzePriority(text) { const textLower = text.toLowerCase(); for (const [priority, keywords] of Object.entries(this.priorityKeywords)) { if (keywords.some(keyword => textLower.includes(keyword))) { return priority; } } return exports.TaskPriority.MEDIUM; } /** * 分析复杂度 * @param text 文本 */ analyzeComplexity(text) { const textLower = text.toLowerCase(); for (const [complexity, keywords] of Object.entries(this.complexityKeywords)) { if (keywords.some(keyword => textLower.includes(keyword))) { return complexity; } } // 基于文本长度判断 if (text.length > 200) return 'high'; if (text.length > 100) return 'medium'; return 'low'; } /** * 估算工作量 * @param description 描述 * @param type 类型 */ estimateEffort(description, type) { let baseHours = 8; // 基础工作量 // 根据类型调整 switch (type) { case RequirementType.TECHNICAL: baseHours = 16; break; case RequirementType.NON_FUNCTIONAL: baseHours = 12; break; case RequirementType.USER_STORY: baseHours = 6; break; } // 根据复杂度调整 const complexity = this.analyzeComplexity(description); switch (complexity) { case 'high': baseHours *= 2; break; case 'low': baseHours *= 0.5; break; } return Math.round(baseHours); } /** * 计算业务价值 * @param description 描述 * @param type 类型 */ calculateBusinessValue(description, type) { let value = 5; // 基础值 const valueKeywords = ['收入', '用户', '体验', '效率', '成本', 'revenue', 'user', 'experience', 'efficiency']; const textLower = description.toLowerCase(); valueKeywords.forEach(keyword => { if (textLower.includes(keyword)) { value += 1; } }); // 根据类型调整 if (type === RequirementType.BUSINESS) value += 2; if (type === RequirementType.USER_STORY) value += 1; return Math.min(10, value); } /** * 计算技术风险 * @param description 描述 * @param type 类型 */ calculateTechnicalRisk(description, type) { let risk = 3; // 基础风险 const riskKeywords = ['集成', '新技术', '算法', '性能', '安全', 'integration', 'new', 'algorithm', 'performance', 'security']; const textLower = description.toLowerCase(); riskKeywords.forEach(keyword => { if (textLower.includes(keyword)) { risk += 1; } }); // 根据类型调整 if (type === RequirementType.TECHNICAL) risk += 2; if (type === RequirementType.NON_FUNCTIONAL) risk += 1; return Math.min(10, risk); } /** * 计算提取置信度 * @param description 描述 * @param type 类型 */ calculateConfidence(description, _type) { let confidence = 0.5; // 基于模式匹配的置信度 if (this.requirementPatterns.functional.some(pattern => pattern.test(description))) { confidence += 0.3; } // 基于长度和结构 if (description.length > 20 && description.length < 500) { confidence += 0.2; } return Math.min(1.0, confidence); } /** * 提取验收标准 * @param description 描述 */ extractAcceptanceCriteria(description) { const criteria = []; // 查找验收标准相关的模式 const criteriaPatterns = [ /验收标准[::]\s*(.+)/gi, /acceptance criteria[::]\s*(.+)/gi, /given (.+?) when (.+?) then (.+)/gi ]; criteriaPatterns.forEach(pattern => { let match; while ((match = pattern.exec(description)) !== null) { criteria.push(match[1] || match[0]); } }); return criteria; } /** * 提取标签 * @param description 描述 */ extractTags(description) { const tags = []; // 基于关键词生成标签 const tagKeywords = { 'UI': ['界面', '页面', '按钮', 'UI', 'interface', 'page', 'button'], 'API': ['接口', 'API', 'service', '服务'], 'Database': ['数据库', '存储', 'database', 'storage'], 'Security': ['安全', '权限', 'security', 'permission', 'auth'], 'Performance': ['性能', '速度', 'performance', 'speed'] }; const textLower = description.toLowerCase(); Object.entries(tagKeywords).forEach(([tag, keywords]) => { if (keywords.some(keyword => textLower.includes(keyword))) { tags.push(tag); } }); return tags; } /** * 提取干系人 * @param description 描述 */ extractStakeholders(description) { const stakeholders = []; const stakeholderPatterns = [ /用户/g, /管理员/g, /开发者/g, /测试人员/g, /user/gi, /admin/gi, /developer/gi, /tester/gi ]; stakeholderPatterns.forEach(pattern => { const matches = description.match(pattern); if (matches) { stakeholders.push(...matches); } }); return [...new Set(stakeholders)]; // 去重 } /** * 检测依赖关系 * @param requirements 需求列表 */ detectDependencies(requirements) { // 简化的依赖检测:基于关键词匹配 requirements.forEach(req => { requirements.forEach(otherReq => { if (req.id !== otherReq.id) { // 检查是否有依赖关系的关键词 if (this.hasDependencyRelation(req.description, otherReq.description)) { req.dependencies.push(otherReq.id); } } }); }); } /** * 检查是否有依赖关系 * @param desc1 描述1 * @param desc2 描述2 */ hasDependencyRelation(desc1, desc2) { // 简化的依赖检测逻辑 const dependencyKeywords = ['基于', '依赖', '需要', '使用', 'based on', 'depends on', 'requires', 'uses']; return dependencyKeywords.some(keyword => desc1.toLowerCase().includes(keyword) && desc2.toLowerCase().includes(keyword)); } /** * 生成摘要 * @param requirements 需求列表 */ generateSummary(requirements) { const functionalCount = requirements.filter(r => r.type === RequirementType.FUNCTIONAL).length; const nonFunctionalCount = requirements.filter(r => r.type === RequirementType.NON_FUNCTIONAL).length; const userStoryCount = requirements.filter(r => r.type === RequirementType.USER_STORY).length; const highPriorityCount = requirements.filter(r => r.priority === exports.TaskPriority.HIGH || r.priority === exports.TaskPriority.CRITICAL).length; const totalEffort = requirements.reduce((sum, req) => sum + req.estimatedEffort, 0); const complexityScores = requirements.map(r => { switch (r.complexity) { case 'high': return 3; case 'medium': return 2; case 'low': return 1; default: return 2; } }); const avgComplexityScore = complexityScores.reduce((sum, score) => sum + score, 0) / complexityScores.length; const avgComplexity = avgComplexityScore > 2.5 ? 'high' : avgComplexityScore > 1.5 ? 'medium' : 'low'; // 计算覆盖度评分(基于需求的完整性和质量) const avgConfidence = requirements.reduce((sum, req) => sum + req.metadata.confidence, 0) / requirements.length; const coverageScore = Math.min(1.0, avgConfidence * (requirements.length / 10)); // 假设10个需求为完整覆盖 return { totalRequirements: requirements.length, functionalRequirements: functionalCount, nonFunctionalRequirements: nonFunctionalCount, userStories: userStoryCount, highPriorityRequirements: highPriorityCount, estimatedTotalEffort: totalEffort, averageComplexity: avgComplexity, coverageScore }; } /** * 生成建议 * @param requirements 需求列表 * @param summary 摘要 */ generateSuggestions(requirements, summary) { const suggestions = []; if (summary.totalRequirements < 5) { suggestions.push('需求数量较少,建议检查是否遗漏了重要需求'); } if (summary.nonFunctionalRequirements === 0) { suggestions.push('未发现非功能需求,建议补充性能、安全、可用性等方面的需求'); } if (summary.highPriorityRequirements / summary.totalRequirements > 0.8) { suggestions.push('高优先级需求比例过高,建议重新评估需求优先级'); } if (summary.coverageScore < 0.6) { suggestions.push('需求覆盖度较低,建议补充更详细的需求描述'); } const avgEffort = summary.estimatedTotalEffort / summary.totalRequirements; if (avgEffort > 20) { suggestions.push('平均工作量较高,建议将复杂需求拆分为更小的任务'); } return suggestions; } /** * 验证需求 * @param requirements 需求列表 */ validateRequirements(requirements) { const warnings = []; requirements.forEach(req => { if (req.description.length < 10) { warnings.push(`需求 ${req.id} 描述过于简短`); } if (req.metadata.confidence < 0.3) { warnings.push(`需求 ${req.id} 提取置信度较低`); } if (req.acceptanceCriteria.length === 0) { warnings.push(`需求 ${req.id} 缺少验收标准`); } }); return warnings; } /** * 判断是否是功能描述 * @param line 文本行 */ isFunctionDescription(line) { const patterns = [ /^[-*+]\s+/, // 列表项 /^\d+\.\s+/, // 编号项 /^[a-zA-Z0-9]+[.)]\s+/, // 字母或数字编号 /功能|特性|能力/ ]; return patterns.some(pattern => pattern.test(line)) && line.length > 10; } /** * 判断是否像需求 * @param line 文本行 */ isRequirementLike(line) { const requirementIndicators = [ '需要', '应该', '必须', '能够', '支持', '实现', '提供', 'need', 'should', 'must', 'shall', 'support', 'implement', 'provide' ]; return requirementIndicators.some(indicator => line.toLowerCase().includes(indicator)) && line.length > 15; } /** * 提取标题 * @param text 文本 */ extractTitle(text) { // 提取前50个字符作为标题 const title = text.substring(0, 50).trim(); return title.endsWith('...') ? title : title + (text.length > 50 ? '...' : ''); } } /** * PRD解析器类 * 负责解析产品需求文档,整合文档处理和需求提取功能 */ class PRDParser { /** * 创建PRD解析器实例 * @param modelCoordinator 模型协调器实例 * @param logger 日志记录器实例 */ constructor(modelCoordinator, logger) { this.modelCoordinator = modelCoordinator; this.logger = logger; this.documentProcessor = new DocumentProcessor(logger); this.requirementExtractor = new RequirementExtractor(logger); } /** * 从文件中解析PRD * @param filePath PRD文件路径 * @param options 解析选项 */ async parseFromFile(filePath, options) { var _a, _b; try { this.logger.info(`开始解析PRD文件:${filePath}`); // 检查文件是否存在 if (!await fs__namespace.pathExists(filePath)) { throw new Error(`文件不存在:${filePath}`); } // 使用文档处理器处理文档 const processingOptions = { extractTables: true, extractImages: false, detectLanguage: true, analyzeStructure: true, extractKeywords: true, calculateImportance: true }; const documentStructure = await this.documentProcessor.processDocument(filePath, processingOptions); // 使用需求提取器提取需求 const extractionOptions = { includeUserStories: (_a = options === null || options === void 0 ? void 0 : options.extractFeatures) !== null && _a !== void 0 ? _a : true, detectDependencies: true, estimateEffort: true, analyzePriority: (_b = options === null || options === void 0 ? void 0 : options.prioritize) !== null && _b !== void 0 ? _b : true, extractAcceptanceCriteria: true, detectStakeholders: true }; const extractionResult = await this.requirementExtractor.extractRequirements(documentStructure, extractionOptions); // 转换为ParsedPRD格式 return this.convertToParseResult(documentStructure, extractionResult); } catch (error) { this.logger.error(`解析PRD文件失败:${error.message}`); throw error; } } /** * 解析PRD内容 * @param content PRD文档内容 * @param fileType 文件类型 * @param options 解析选项 */ async parseContent(content, fileType = exports.FileType.MARKDOWN, options) { try { // 处理不同文件类型的内容 const processedContent = this.preprocessContent(content, fileType); // 使用模型解析内容 const response = await this.modelCoordinator.parsePRD(processedContent, options); // 解析并验证模型返回的JSON结果 try { const result = JSON.parse(response.content); this.validateParseResult(result); this.logger.info('PRD解析成功'); return result; } catch (error) { this.logger.error(`解析模型返回结果失败:${error.message}`); throw new Error(`无法解析模型返回的JSON结果:${error.message}`); } } catch (error) { this.logger.error(`解析PRD内容失败:${error.message}`); throw error; } } /** * 预处理不同类型的文档内容 * @param content 文档内容 * @param fileType 文件类型 */ preprocessContent(content, fileType) { // 根据不同文件类型进行预处理 switch (fileType) { case exports.FileType.MARKDOWN: // Markdown格式不需要特殊处理 return content; case exports.FileType.JSON: // 如果是JSON格式,尝试解析并美化输出 try { const parsed = JSON.parse(content); return JSON.stringify(parsed, null, 2); } catch { // 如果解析失败,直接返回原内容 return content; } default: // 其他格式暂时不做特殊处理 return content; } } /** * 根据文件扩展名检测文件类型 * @param filePath 文件路径 */ detectFileType(filePath) { const ext = path__namespace.extname(filePath).toLowerCase(); switch (ext) { case '.md': case '.markdown': return exports.FileType.MARKDOWN; case '.pdf': return exports.FileType.PDF; case '.docx': case '.doc': return exports.FileType.WORD; case '.txt': return exports.FileType.TEXT; case '.json': return exports.FileType.JSON; default: return exports.FileType.UNKNOWN; } } /** * 验证解析结果是否符合预期格式 * @param result 解析结果 */ validateParseResult(result) { // 验证基本结构 if (!result.title) { this.logger.warn('解析结果缺少标题'); } if (!result.description) { this.logger.warn('解析结果缺少描述'); } if (!Array.isArray(result.sections) || result.sections.length === 0) { throw new Error('解析结果必须包含至少一个章节'); } // 验证每个章节的结构 result.sections.forEach((section, index) => { if (!section.title) { this.logger.warn(`第${index + 1}个章节缺少标题`); } if (!Array.isArray(section.features)) { this.logger.warn(`第${index + 1}个章节的features字段不是数组`); section.features = []; } }); } /** * 转换为ParsedPRD格式 * @param documentStructure 文档结构 * @param extractionResult 需求提取结果 */ convertToParseResult(documentStructure, extractionResult) { // 将需求转换为功能特性 const features = extractionResult.requirements.map(req => ({ id: req.id, name: req.title, description: req.description, priority: req.priority, type: req.type, dependencies: req.dependencies, estimatedHours: req.estimatedEffort, tags: req.tags, acceptanceCriteria: req.acceptanceCriteria, complexity: req.complexity, businessValue: req.businessValue, technicalRisk: req.technicalRisk, stakeholders: req.stakeholders, category: req.category, status: exports.TaskStatus.NOT_STARTED, createdAt: req.metadata.extractedAt, updatedAt: req.metadata.extractedAt })); // 构建ParsedPRD对象 const parsedPRD = { id: `prd-${Date.now()}`, title: documentStructure.title, description: this.extractDescription(documentStructure), sections: [], // 临时空数组,后续会填充 metadata: { version: '1.0.0', features, fileName: documentStructure.metadata.fileName, fileSize: documentStructure.metadata.fileSize, parsedAt: new Date(), language: documentStructure.metadata.language, wordCount: documentStructure.metadata.wordCount, estimatedReadTime: documentStructure.metadata.estimatedReadTime, extractionSummary: extractionResult.summary, warnings: extractionResult.warnings, suggestions: extractionResult.suggestions, documentStructure: { sectionsCount: documentStructure.sections.length, maxDepth: this.calculateMaxDepth(this.convertToPRDSections(documentStructure.sections)), hasTableOfContents: this.hasTableOfContents(documentStructure), primaryLanguage: documentStructure.metadata.language }, createdAt: new Date(), updatedAt: new Date() } }; return parsedPRD; } /** * 提取文档描述 * @param documentStructure 文档结构 */ extractDescription(documentStructure) { // 查找概述或简介章节 const overviewSection = documentStructure.sections.find(section => section.type === 'overview' || section.title.toLowerCase().includes('概述') || section.title.toLowerCase().includes('简介') || section.title.toLowerCase().includes('overview')); if (overviewSection) { return overviewSection.content.substring(0, 500).trim(); } // 如果没有找到概述章节,使用第一个章节的内容 if (documentStructure.sections.length > 0) { return documentStructure.sections[0].content.substring(0, 500).trim(); } return '从PRD文档自动提取的产品需求'; } /** * 转换DocumentSection到PRDSection * @param sections 文档章节列表 */ convertToPRDSections(sections) { return sections.map(section => ({ title: section.title, content: section.content, level: section.level, features: [], // 默认为空,实际应该从内容中提取 subsections: section.subsections ? this.convertToPRDSections(section.subsections) : undefined })); } /** * 计算最大深度 * @param sections 章节列表 */ calculateMaxDepth(sections) { let maxDepth = 0; const calculateDepth = (sectionList, currentDepth = 1) => { maxDepth = Math.max(maxDepth, currentDepth); sectionList.forEach(section => { if (section.subsections && section.subsections.length > 0) { calculateDepth(section.subsections, currentDepth + 1); } }); }; calculateDepth(sections); return maxDepth; } /** * 检查是否有目录 * @param documentStructure 文档结构 */ hasTableOfContents(documentStructure) { return documentStructure.sections.so