taskflow-ai
Version:
TaskFlow AI - 智能PRD文档解析与任务管理助手,支持多模型AI协同、MCP编辑器集成,专为开发团队设计的CLI工具
1,473 lines (1,467 loc) • 196 kB
JavaScript
import * as fs from 'fs-extra';
import fs__default from 'fs-extra';
import * as path from 'path';
import path__default from 'path';
import MarkdownIt from 'markdown-it';
import * as fs$1 from 'fs';
import * as os from 'os';
import winston from 'winston';
import axios from 'axios';
import * as crypto from 'crypto';
/**
* 模型相关类型定义
*/
/**
* 消息角色
*/
var MessageRole;
(function (MessageRole) {
MessageRole["USER"] = "user";
MessageRole["ASSISTANT"] = "assistant";
MessageRole["SYSTEM"] = "system";
})(MessageRole || (MessageRole = {}));
/**
* 文件类型
*/
var FileType;
(function (FileType) {
FileType["MARKDOWN"] = "markdown";
FileType["PDF"] = "pdf";
FileType["WORD"] = "word";
FileType["TEXT"] = "text";
FileType["JSON"] = "json";
FileType["UNKNOWN"] = "unknown";
})(FileType || (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.pathExists(filePath)) {
throw new Error(`文件不存在: ${filePath}`);
}
// 获取文件信息
const stats = await fs.stat(filePath);
const documentType = this.detectDocumentType(filePath);
// 读取文件内容
const content = await this.readFileContent(filePath, documentType);
// 生成元数据
const metadata = {
fileName: path.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.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.readFile(filePath, 'utf-8');
case DocumentType.WORD:
// TODO: 实现Word文档解析
this.logger.warn('Word文档解析暂未实现,将作为文本处理');
return await fs.readFile(filePath, 'utf-8');
case DocumentType.PDF:
// TODO: 实现PDF文档解析
this.logger.warn('PDF文档解析暂未实现,将作为文本处理');
return await fs.readFile(filePath, 'utf-8');
default:
return await fs.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 任务相关类型定义
*/
/**
* 任务状态枚举
*/
var TaskStatus;
(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";
})(TaskStatus || (TaskStatus = {}));
/**
* 任务优先级
*/
var TaskPriority;
(function (TaskPriority) {
TaskPriority["LOW"] = "low";
TaskPriority["MEDIUM"] = "medium";
TaskPriority["HIGH"] = "high";
TaskPriority["CRITICAL"] = "critical";
})(TaskPriority || (TaskPriority = {}));
/**
* 任务类型
*/
var TaskType;
(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";
})(TaskType || (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 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 === TaskPriority.HIGH || r.priority === 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.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 = 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 FileType.MARKDOWN:
// Markdown格式不需要特殊处理
return content;
case 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.extname(filePath).toLowerCase();
switch (ext) {
case '.md':
case '.markdown':
return FileType.MARKDOWN;
case '.pdf':
return FileType.PDF;
case '.docx':
case '.doc':
return FileType.WORD;
case '.txt':
return FileType.TEXT;
case '.json':
return FileType.JSON;
default:
return 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: 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.some(section => section.title.toLowerCase().includes('目录') ||
section.title.toLowerCase().includes('contents') ||
section.title.toLowerCase().includes('toc'));
}
}
/* eslint-disable @typescript-eslint/no-unused-vars */
/**
* 任务规划器类
* 负责根据PRD生成任务计划
*/
class TaskPlanner {
/**
* 创建任务规划器实例
* @param modelCoordinator 模型协调器实例
* @param logger 日志记录器
*/
constructor(modelCoordinator, logger) {
this.modelCoordinator = modelCoordinator;
this.logger = logger;
}
/**
* 根据PRD解析结果生成任务计划
* @param prdResult PRD解析结果
* @param options 规划选项
*/
async generateTaskPlan(prdResult, options) {
try {
this.logger.info('开始生成任务计划');
// 使用模型生成任务计划
const response = await this.modelCoordinator.planTasks(prdResult, options);
try {
// 解析模型返回的任务计划
const taskPlan = JSON.parse(response.content);
this.validateTaskPlan(taskPlan);
// 后处理任务计划
this.postProcessTaskPlan(taskPlan);
this.logger.in