kiro-spec-engine 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/README.md +172 -0
- package/bin/kiro-spec-engine.js +62 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/cross-tool-guide.md +554 -0
- package/docs/manual-workflows-guide.md +417 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/lib/adoption/detection-engine.js +14 -4
- package/lib/commands/adopt.js +117 -3
- package/lib/commands/context.js +99 -0
- package/lib/commands/prompt.js +105 -0
- package/lib/commands/status.js +225 -0
- package/lib/commands/task.js +199 -0
- package/lib/commands/watch.js +569 -0
- package/lib/commands/workflows.js +240 -0
- package/lib/commands/workspace.js +189 -0
- package/lib/context/context-exporter.js +378 -0
- package/lib/context/prompt-generator.js +482 -0
- package/lib/steering/adoption-config.js +164 -0
- package/lib/steering/steering-manager.js +289 -0
- package/lib/task/task-claimer.js +430 -0
- package/lib/utils/tool-detector.js +383 -0
- package/lib/watch/action-executor.js +458 -0
- package/lib/watch/event-debouncer.js +323 -0
- package/lib/watch/execution-logger.js +550 -0
- package/lib/watch/file-watcher.js +499 -0
- package/lib/watch/presets.js +266 -0
- package/lib/watch/watch-manager.js +533 -0
- package/lib/workspace/workspace-manager.js +370 -0
- package/lib/workspace/workspace-sync.js +356 -0
- package/package.json +3 -1
- package/template/.kiro/tools/backup_manager.py +295 -0
- package/template/.kiro/tools/configuration_manager.py +218 -0
- package/template/.kiro/tools/document_evaluator.py +550 -0
- package/template/.kiro/tools/enhancement_logger.py +168 -0
- package/template/.kiro/tools/error_handler.py +335 -0
- package/template/.kiro/tools/improvement_identifier.py +444 -0
- package/template/.kiro/tools/modification_applicator.py +737 -0
- package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
- package/template/.kiro/tools/quality_scorer.py +305 -0
- package/template/.kiro/tools/report_generator.py +154 -0
- package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
- package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
- package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
- package/template/.kiro/tools/workflow_quality_gate.py +100 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Document Evaluator - 文档质量评估组件
|
|
4
|
+
|
|
5
|
+
负责分析文档结构和内容,评估质量并识别改进领域
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import Dict, List, Optional
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class QualityAssessment:
|
|
15
|
+
"""质量评估结果"""
|
|
16
|
+
score: float # 0-10
|
|
17
|
+
criteria_scores: Dict[str, float] = field(default_factory=dict)
|
|
18
|
+
missing_sections: List[str] = field(default_factory=list)
|
|
19
|
+
incomplete_sections: List[str] = field(default_factory=list)
|
|
20
|
+
issues: List[str] = field(default_factory=list)
|
|
21
|
+
language: str = 'en'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DocumentEvaluator:
|
|
25
|
+
"""
|
|
26
|
+
文档评估器 - 分析文档质量
|
|
27
|
+
|
|
28
|
+
支持中英文文档评估
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self.language = None
|
|
33
|
+
|
|
34
|
+
def _detect_language(self, content: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
检测文档语言
|
|
37
|
+
返回: 'zh' (中文) 或 'en' (英文)
|
|
38
|
+
"""
|
|
39
|
+
# 统计中文字符数量
|
|
40
|
+
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', content))
|
|
41
|
+
# 统计英文单词数量
|
|
42
|
+
english_words = len(re.findall(r'\b[a-zA-Z]+\b', content))
|
|
43
|
+
|
|
44
|
+
# 如果中文字符超过100个,判定为中文
|
|
45
|
+
if chinese_chars > 100:
|
|
46
|
+
return 'zh'
|
|
47
|
+
# 如果英文单词超过中文字符的3倍,判定为英文
|
|
48
|
+
elif english_words > chinese_chars * 3:
|
|
49
|
+
return 'en'
|
|
50
|
+
# 默认中文
|
|
51
|
+
return 'zh'
|
|
52
|
+
|
|
53
|
+
def assess_requirements_quality(self, content: str, language: Optional[str] = None) -> QualityAssessment:
|
|
54
|
+
"""评估 Requirements 文档质量 (0-10) - 支持中英文,增强评分算法"""
|
|
55
|
+
lang = language or self._detect_language(content)
|
|
56
|
+
self.language = lang
|
|
57
|
+
|
|
58
|
+
score = 0.0
|
|
59
|
+
criteria_scores = {}
|
|
60
|
+
missing_sections = []
|
|
61
|
+
incomplete_sections = []
|
|
62
|
+
issues = []
|
|
63
|
+
|
|
64
|
+
if lang == 'zh':
|
|
65
|
+
# 中文评分标准 - 增强版
|
|
66
|
+
# 基础结构检查 (2分) - 更严格的检查
|
|
67
|
+
structure_score = 0.0
|
|
68
|
+
|
|
69
|
+
# 检查概述/Introduction
|
|
70
|
+
if "## 1. 概述" in content or "## Introduction" in content or "## 概述" in content:
|
|
71
|
+
structure_score += 0.5
|
|
72
|
+
# 检查概述内容是否充实
|
|
73
|
+
intro_match = re.search(r'##.*?概述.*?\n(.*?)(?=\n##|\Z)', content, re.DOTALL | re.IGNORECASE)
|
|
74
|
+
if intro_match and len(intro_match.group(1).strip()) < 100:
|
|
75
|
+
incomplete_sections.append("概述内容过于简短")
|
|
76
|
+
else:
|
|
77
|
+
missing_sections.append("概述/Introduction")
|
|
78
|
+
|
|
79
|
+
# 检查用户故事
|
|
80
|
+
if "## 2. 用户故事" in content or "用户故事" in content or "User Story" in content:
|
|
81
|
+
structure_score += 0.5
|
|
82
|
+
else:
|
|
83
|
+
missing_sections.append("用户故事")
|
|
84
|
+
|
|
85
|
+
# 检查功能需求
|
|
86
|
+
if "## 3. 功能需求" in content or "功能需求" in content or "Functional Requirements" in content:
|
|
87
|
+
structure_score += 0.5
|
|
88
|
+
else:
|
|
89
|
+
missing_sections.append("功能需求")
|
|
90
|
+
|
|
91
|
+
# 检查非功能需求
|
|
92
|
+
if "## 4. 非功能需求" in content or "非功能需求" in content or "Non-functional" in content:
|
|
93
|
+
structure_score += 0.5
|
|
94
|
+
else:
|
|
95
|
+
missing_sections.append("非功能需求")
|
|
96
|
+
|
|
97
|
+
criteria_scores['structure'] = structure_score
|
|
98
|
+
score += structure_score
|
|
99
|
+
|
|
100
|
+
# EARS 格式检查 (2分) - 更精确的模式匹配
|
|
101
|
+
ears_patterns = len(re.findall(r'(?:WHEN|当|如果).*?(?:THEN|那么|则).*?(?:SHALL|应该|必须)', content, re.IGNORECASE | re.DOTALL))
|
|
102
|
+
ears_score = min(ears_patterns * 0.15, 2.0)
|
|
103
|
+
criteria_scores['ears_format'] = ears_score
|
|
104
|
+
score += ears_score
|
|
105
|
+
|
|
106
|
+
if ears_patterns < 5:
|
|
107
|
+
issues.append(f"EARS 格式验收标准较少 (当前 {ears_patterns},建议 5+)")
|
|
108
|
+
elif ears_patterns < 10:
|
|
109
|
+
issues.append(f"EARS 格式验收标准可以更多 (当前 {ears_patterns},建议 10+)")
|
|
110
|
+
|
|
111
|
+
# 用户故事质量 (2分) - 检查格式完整性
|
|
112
|
+
user_story_patterns = len(re.findall(r'(?:作为|As a).*?(?:我希望|I want).*?(?:以便|So that)', content, re.IGNORECASE | re.DOTALL))
|
|
113
|
+
user_story_score = min(user_story_patterns * 0.25, 2.0)
|
|
114
|
+
criteria_scores['user_stories'] = user_story_score
|
|
115
|
+
score += user_story_score
|
|
116
|
+
|
|
117
|
+
if user_story_patterns < 3:
|
|
118
|
+
issues.append(f"用户故事较少 (当前 {user_story_patterns},建议 3+)")
|
|
119
|
+
|
|
120
|
+
# 验收标准完整性 (2分) - 检查每个需求是否有验收标准
|
|
121
|
+
acceptance_criteria = len(re.findall(r'(?:\*\*验收标准\*\*:|Acceptance Criteria)', content, re.IGNORECASE))
|
|
122
|
+
requirements_count = len(re.findall(r'### \d+\.\d+', content))
|
|
123
|
+
|
|
124
|
+
if requirements_count > 0:
|
|
125
|
+
acceptance_ratio = acceptance_criteria / requirements_count
|
|
126
|
+
acceptance_score = min(acceptance_ratio * 2.0, 2.0)
|
|
127
|
+
else:
|
|
128
|
+
acceptance_score = min(acceptance_criteria * 0.3, 2.0)
|
|
129
|
+
|
|
130
|
+
criteria_scores['acceptance_criteria'] = acceptance_score
|
|
131
|
+
score += acceptance_score
|
|
132
|
+
|
|
133
|
+
if acceptance_criteria < requirements_count:
|
|
134
|
+
issues.append(f"部分需求缺少验收标准 ({acceptance_criteria}/{requirements_count})")
|
|
135
|
+
|
|
136
|
+
# 非功能需求覆盖 (1分) - 更全面的检查
|
|
137
|
+
nfr_keywords = ['性能', '安全', '可用性', '可维护性', '兼容性', '可扩展性']
|
|
138
|
+
nfr_coverage = sum(1 for keyword in nfr_keywords if keyword in content)
|
|
139
|
+
nfr_score = min(nfr_coverage * 0.17, 1.0)
|
|
140
|
+
criteria_scores['nfr_coverage'] = nfr_score
|
|
141
|
+
score += nfr_score
|
|
142
|
+
|
|
143
|
+
missing_nfr = [kw for kw in nfr_keywords if kw not in content]
|
|
144
|
+
if missing_nfr:
|
|
145
|
+
issues.append(f"缺少非功能需求: {', '.join(missing_nfr)}")
|
|
146
|
+
|
|
147
|
+
# 约束条件 (1分)
|
|
148
|
+
if "约束条件" in content or "限制" in content or "Constraints" in content:
|
|
149
|
+
criteria_scores['constraints'] = 1.0
|
|
150
|
+
score += 1.0
|
|
151
|
+
else:
|
|
152
|
+
criteria_scores['constraints'] = 0.0
|
|
153
|
+
missing_sections.append("约束条件")
|
|
154
|
+
else:
|
|
155
|
+
# 英文评分标准 - 增强版
|
|
156
|
+
# 基础结构检查 (2分) - 更严格的检查
|
|
157
|
+
structure_score = 0.0
|
|
158
|
+
|
|
159
|
+
# 检查 Introduction/Overview
|
|
160
|
+
if "## Introduction" in content or "## Overview" in content:
|
|
161
|
+
structure_score += 0.5
|
|
162
|
+
# 检查内容是否充实
|
|
163
|
+
intro_match = re.search(r'##.*?(?:Introduction|Overview).*?\n(.*?)(?=\n##|\Z)', content, re.DOTALL | re.IGNORECASE)
|
|
164
|
+
if intro_match and len(intro_match.group(1).strip()) < 100:
|
|
165
|
+
incomplete_sections.append("Introduction/Overview content is too brief")
|
|
166
|
+
else:
|
|
167
|
+
missing_sections.append("Introduction/Overview")
|
|
168
|
+
|
|
169
|
+
# 检查 Glossary
|
|
170
|
+
if "## Glossary" in content or "## Terminology" in content:
|
|
171
|
+
structure_score += 0.5
|
|
172
|
+
# 检查是否有定义
|
|
173
|
+
glossary_match = re.search(r'##.*?(?:Glossary|Terminology).*?\n(.*?)(?=\n##|\Z)', content, re.DOTALL | re.IGNORECASE)
|
|
174
|
+
if glossary_match:
|
|
175
|
+
definitions = len(re.findall(r'^\s*-\s*\*\*.*?\*\*:', glossary_match.group(1), re.MULTILINE))
|
|
176
|
+
if definitions < 3:
|
|
177
|
+
incomplete_sections.append(f"Glossary has few definitions ({definitions}, suggest 3+)")
|
|
178
|
+
else:
|
|
179
|
+
missing_sections.append("Glossary")
|
|
180
|
+
|
|
181
|
+
# 检查 Requirements
|
|
182
|
+
if "## Requirements" in content or "## Functional Requirements" in content:
|
|
183
|
+
structure_score += 0.5
|
|
184
|
+
else:
|
|
185
|
+
missing_sections.append("Requirements")
|
|
186
|
+
|
|
187
|
+
# 检查 Non-functional Requirements
|
|
188
|
+
if "Non-functional" in content or "Non-Functional" in content:
|
|
189
|
+
structure_score += 0.5
|
|
190
|
+
else:
|
|
191
|
+
missing_sections.append("Non-functional Requirements")
|
|
192
|
+
|
|
193
|
+
criteria_scores['structure'] = structure_score
|
|
194
|
+
score += structure_score
|
|
195
|
+
|
|
196
|
+
# EARS 格式检查 (2分) - 更精确的模式匹配
|
|
197
|
+
ears_patterns = len(re.findall(r'(?:WHEN|IF|WHILE|WHERE).*?(?:THEN|THE\s+system\s+SHALL)', content, re.IGNORECASE | re.DOTALL))
|
|
198
|
+
ears_score = min(ears_patterns * 0.12, 2.0)
|
|
199
|
+
criteria_scores['ears_format'] = ears_score
|
|
200
|
+
score += ears_score
|
|
201
|
+
|
|
202
|
+
if ears_patterns < 5:
|
|
203
|
+
issues.append(f"Few EARS-format acceptance criteria (current {ears_patterns}, target 5+)")
|
|
204
|
+
elif ears_patterns < 10:
|
|
205
|
+
issues.append(f"EARS-format criteria could be more (current {ears_patterns}, target 10+)")
|
|
206
|
+
|
|
207
|
+
# 用户故事质量 (2分) - 检查格式完整性
|
|
208
|
+
user_story_patterns = len(re.findall(r'(?:As a|As an).*?I want.*?(?:So that|so that)', content, re.IGNORECASE | re.DOTALL))
|
|
209
|
+
user_story_score = min(user_story_patterns * 0.2, 2.0)
|
|
210
|
+
criteria_scores['user_stories'] = user_story_score
|
|
211
|
+
score += user_story_score
|
|
212
|
+
|
|
213
|
+
if user_story_patterns < 3:
|
|
214
|
+
issues.append(f"Few user stories (current {user_story_patterns}, target 3+)")
|
|
215
|
+
|
|
216
|
+
# 验收标准完整性 (2分) - 检查每个需求是否有验收标准
|
|
217
|
+
acceptance_criteria = len(re.findall(r'(?:Acceptance Criteria|#### Acceptance Criteria)', content, re.IGNORECASE))
|
|
218
|
+
requirements_count = len(re.findall(r'### Requirement \d+', content, re.IGNORECASE))
|
|
219
|
+
|
|
220
|
+
if requirements_count > 0:
|
|
221
|
+
acceptance_ratio = acceptance_criteria / requirements_count
|
|
222
|
+
acceptance_score = min(acceptance_ratio * 2.0, 2.0)
|
|
223
|
+
else:
|
|
224
|
+
acceptance_score = min(acceptance_criteria * 0.25, 2.0)
|
|
225
|
+
|
|
226
|
+
criteria_scores['acceptance_criteria'] = acceptance_score
|
|
227
|
+
score += acceptance_score
|
|
228
|
+
|
|
229
|
+
if acceptance_criteria < requirements_count:
|
|
230
|
+
issues.append(f"Some requirements lack acceptance criteria ({acceptance_criteria}/{requirements_count})")
|
|
231
|
+
|
|
232
|
+
# 非功能需求覆盖 (1分) - 更全面的检查
|
|
233
|
+
nfr_keywords = ['performance', 'security', 'usability', 'maintainability', 'compatibility', 'scalability', 'reliability']
|
|
234
|
+
nfr_coverage = sum(1 for keyword in nfr_keywords if keyword.lower() in content.lower())
|
|
235
|
+
nfr_score = min(nfr_coverage * 0.14, 1.0)
|
|
236
|
+
criteria_scores['nfr_coverage'] = nfr_score
|
|
237
|
+
score += nfr_score
|
|
238
|
+
|
|
239
|
+
missing_nfr = [kw for kw in nfr_keywords if kw.lower() not in content.lower()]
|
|
240
|
+
if len(missing_nfr) > 3:
|
|
241
|
+
issues.append(f"Missing non-functional requirements: {', '.join(missing_nfr[:3])}")
|
|
242
|
+
|
|
243
|
+
# 约束条件 (1分)
|
|
244
|
+
if "constraint" in content.lower() or "limitation" in content.lower():
|
|
245
|
+
criteria_scores['constraints'] = 1.0
|
|
246
|
+
score += 1.0
|
|
247
|
+
else:
|
|
248
|
+
criteria_scores['constraints'] = 0.0
|
|
249
|
+
missing_sections.append("Constraints")
|
|
250
|
+
|
|
251
|
+
return QualityAssessment(
|
|
252
|
+
score=min(score, 10.0),
|
|
253
|
+
criteria_scores=criteria_scores,
|
|
254
|
+
missing_sections=missing_sections,
|
|
255
|
+
incomplete_sections=incomplete_sections,
|
|
256
|
+
issues=issues,
|
|
257
|
+
language=lang
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def assess_design_quality(self, design_content: str, requirements_content: str, language: Optional[str] = None) -> QualityAssessment:
|
|
261
|
+
"""评估 Design 文档质量 (0-10) - 支持中英文,增强评分算法"""
|
|
262
|
+
lang = language or self._detect_language(design_content)
|
|
263
|
+
self.language = lang
|
|
264
|
+
|
|
265
|
+
score = 0.0
|
|
266
|
+
criteria_scores = {}
|
|
267
|
+
missing_sections = []
|
|
268
|
+
incomplete_sections = []
|
|
269
|
+
issues = []
|
|
270
|
+
|
|
271
|
+
if lang == 'zh':
|
|
272
|
+
# 中文评分标准 - 增强版
|
|
273
|
+
# 基础结构检查 (2.5分) - 更严格的检查
|
|
274
|
+
structure_score = 0.0
|
|
275
|
+
|
|
276
|
+
# 检查系统概述
|
|
277
|
+
if "## 1. 系统概述" in design_content or "## 1. 概述" in design_content or "## Overview" in design_content:
|
|
278
|
+
structure_score += 0.5
|
|
279
|
+
# 检查内容充实度
|
|
280
|
+
overview_match = re.search(r'##.*?概述.*?\n(.*?)(?=\n##|\Z)', design_content, re.DOTALL | re.IGNORECASE)
|
|
281
|
+
if overview_match and len(overview_match.group(1).strip()) < 150:
|
|
282
|
+
incomplete_sections.append("系统概述内容过于简短")
|
|
283
|
+
else:
|
|
284
|
+
missing_sections.append("系统概述")
|
|
285
|
+
|
|
286
|
+
# 检查架构设计
|
|
287
|
+
if "## 2. 架构设计" in design_content or "## Architecture" in design_content:
|
|
288
|
+
structure_score += 0.5
|
|
289
|
+
else:
|
|
290
|
+
missing_sections.append("架构设计")
|
|
291
|
+
|
|
292
|
+
# 检查组件设计
|
|
293
|
+
if "## 3. 组件设计" in design_content or "## Components" in design_content:
|
|
294
|
+
structure_score += 0.5
|
|
295
|
+
else:
|
|
296
|
+
missing_sections.append("组件设计")
|
|
297
|
+
|
|
298
|
+
# 检查数据流/接口设计
|
|
299
|
+
if "## 4. 数据流设计" in design_content or "## 4. 接口设计" in design_content or "## Interface" in design_content:
|
|
300
|
+
structure_score += 0.5
|
|
301
|
+
else:
|
|
302
|
+
missing_sections.append("数据流/接口设计")
|
|
303
|
+
|
|
304
|
+
# 检查错误处理
|
|
305
|
+
if "错误处理" in design_content or "异常处理" in design_content or "Error Handling" in design_content:
|
|
306
|
+
structure_score += 0.5
|
|
307
|
+
else:
|
|
308
|
+
missing_sections.append("错误处理策略")
|
|
309
|
+
|
|
310
|
+
criteria_scores['structure'] = structure_score
|
|
311
|
+
score += structure_score
|
|
312
|
+
|
|
313
|
+
# 需求追溯性检查 (2.5分) - 更精确的匹配
|
|
314
|
+
req_references = len(re.findall(r'(?:需求|Requirements?|Validates:)\s*\d+\.\d+', design_content, re.IGNORECASE))
|
|
315
|
+
# 检查双向追溯
|
|
316
|
+
bidirectional_refs = len(re.findall(r'Validates:\s*Requirements?\s*\d+\.\d+', design_content, re.IGNORECASE))
|
|
317
|
+
|
|
318
|
+
traceability_score = min(req_references * 0.15, 2.0)
|
|
319
|
+
if bidirectional_refs > 0:
|
|
320
|
+
traceability_score += min(bidirectional_refs * 0.1, 0.5)
|
|
321
|
+
|
|
322
|
+
criteria_scores['traceability'] = traceability_score
|
|
323
|
+
score += traceability_score
|
|
324
|
+
|
|
325
|
+
if req_references < 3:
|
|
326
|
+
issues.append(f"需求追溯较少 (当前 {req_references},建议 5+)")
|
|
327
|
+
if bidirectional_refs == 0:
|
|
328
|
+
issues.append("缺少双向追溯 (Validates: Requirements X.Y)")
|
|
329
|
+
|
|
330
|
+
# 架构图和设计图 (1.5分) - 检查图表质量
|
|
331
|
+
mermaid_diagrams = len(re.findall(r'```mermaid', design_content))
|
|
332
|
+
plantuml_diagrams = len(re.findall(r'```plantuml', design_content))
|
|
333
|
+
diagram_keywords = len(re.findall(r'架构图|设计图|流程图|时序图', design_content))
|
|
334
|
+
|
|
335
|
+
total_diagrams = mermaid_diagrams + plantuml_diagrams + diagram_keywords
|
|
336
|
+
diagram_score = min(total_diagrams * 0.4, 1.5)
|
|
337
|
+
criteria_scores['diagrams'] = diagram_score
|
|
338
|
+
score += diagram_score
|
|
339
|
+
|
|
340
|
+
if total_diagrams == 0:
|
|
341
|
+
missing_sections.append("架构图/设计图")
|
|
342
|
+
elif total_diagrams < 2:
|
|
343
|
+
issues.append(f"设计图较少 (当前 {total_diagrams},建议 2+)")
|
|
344
|
+
|
|
345
|
+
# 组件详细度 (1.5分) - 检查组件描述完整性
|
|
346
|
+
component_sections = len(re.findall(r'### \d+\.\d+', design_content))
|
|
347
|
+
interface_definitions = len(re.findall(r'(?:接口定义|Interface|API|方法|Method)', design_content, re.IGNORECASE))
|
|
348
|
+
responsibility_mentions = len(re.findall(r'(?:职责|Responsibility|负责)', design_content, re.IGNORECASE))
|
|
349
|
+
|
|
350
|
+
component_score = 0.0
|
|
351
|
+
if component_sections > 0:
|
|
352
|
+
component_score += min(component_sections * 0.15, 0.5)
|
|
353
|
+
if interface_definitions > 0:
|
|
354
|
+
component_score += min(interface_definitions * 0.1, 0.5)
|
|
355
|
+
if responsibility_mentions > 0:
|
|
356
|
+
component_score += min(responsibility_mentions * 0.1, 0.5)
|
|
357
|
+
|
|
358
|
+
criteria_scores['component_detail'] = component_score
|
|
359
|
+
score += component_score
|
|
360
|
+
|
|
361
|
+
if component_sections < 3:
|
|
362
|
+
issues.append(f"组件数量较少 (当前 {component_sections},建议 3+)")
|
|
363
|
+
|
|
364
|
+
# 技术选型说明 (1分)
|
|
365
|
+
tech_keywords = ['技术选型', '技术栈', '框架选择', '数据库', 'API', '协议']
|
|
366
|
+
tech_coverage = sum(1 for keyword in tech_keywords if keyword in design_content)
|
|
367
|
+
tech_score = min(tech_coverage * 0.2, 1.0)
|
|
368
|
+
criteria_scores['technology'] = tech_score
|
|
369
|
+
score += tech_score
|
|
370
|
+
|
|
371
|
+
if tech_coverage < 2:
|
|
372
|
+
issues.append("技术选型说明不足")
|
|
373
|
+
|
|
374
|
+
# 非功能需求设计 (1分)
|
|
375
|
+
nfr_design = ['性能设计', '安全设计', '可扩展性', '容错机制', '监控', '日志']
|
|
376
|
+
nfr_coverage = sum(1 for keyword in nfr_design if keyword in design_content)
|
|
377
|
+
nfr_score = min(nfr_coverage * 0.2, 1.0)
|
|
378
|
+
criteria_scores['nfr_design'] = nfr_score
|
|
379
|
+
score += nfr_score
|
|
380
|
+
|
|
381
|
+
missing_nfr = [kw for kw in nfr_design if kw not in design_content]
|
|
382
|
+
if len(missing_nfr) > 3:
|
|
383
|
+
issues.append(f"缺少非功能需求设计: {', '.join(missing_nfr[:3])}")
|
|
384
|
+
|
|
385
|
+
# 接口定义完整性 (1分)
|
|
386
|
+
interface_indicators = len(re.findall(r'(?:接口定义|API\s*设计|数据结构|参数说明|返回值)', design_content))
|
|
387
|
+
interface_score = min(interface_indicators * 0.25, 1.0)
|
|
388
|
+
criteria_scores['interfaces'] = interface_score
|
|
389
|
+
score += interface_score
|
|
390
|
+
else:
|
|
391
|
+
# 英文评分标准 - 增强版
|
|
392
|
+
# 基础结构检查 (2.5分) - 更严格的检查
|
|
393
|
+
structure_score = 0.0
|
|
394
|
+
|
|
395
|
+
# 检查 Overview/Introduction
|
|
396
|
+
if "## Overview" in design_content or "## Introduction" in design_content:
|
|
397
|
+
structure_score += 0.5
|
|
398
|
+
# 检查内容充实度
|
|
399
|
+
overview_match = re.search(r'##.*?(?:Overview|Introduction).*?\n(.*?)(?=\n##|\Z)', design_content, re.DOTALL | re.IGNORECASE)
|
|
400
|
+
if overview_match and len(overview_match.group(1).strip()) < 150:
|
|
401
|
+
incomplete_sections.append("Overview/Introduction content is too brief")
|
|
402
|
+
else:
|
|
403
|
+
missing_sections.append("Overview/Introduction")
|
|
404
|
+
|
|
405
|
+
# 检查 Architecture
|
|
406
|
+
if "## Architecture" in design_content or "## System Architecture" in design_content:
|
|
407
|
+
structure_score += 0.5
|
|
408
|
+
else:
|
|
409
|
+
missing_sections.append("Architecture")
|
|
410
|
+
|
|
411
|
+
# 检查 Components
|
|
412
|
+
if "## Components" in design_content or "## Component" in design_content:
|
|
413
|
+
structure_score += 0.5
|
|
414
|
+
else:
|
|
415
|
+
missing_sections.append("Components")
|
|
416
|
+
|
|
417
|
+
# 检查 Interfaces/Data Flow
|
|
418
|
+
if "## Interface" in design_content or "## Data Flow" in design_content or "## API" in design_content:
|
|
419
|
+
structure_score += 0.5
|
|
420
|
+
else:
|
|
421
|
+
missing_sections.append("Interfaces/Data Flow")
|
|
422
|
+
|
|
423
|
+
# 检查 Error Handling
|
|
424
|
+
if "Error Handling" in design_content or "Exception Handling" in design_content:
|
|
425
|
+
structure_score += 0.5
|
|
426
|
+
else:
|
|
427
|
+
missing_sections.append("Error Handling")
|
|
428
|
+
|
|
429
|
+
criteria_scores['structure'] = structure_score
|
|
430
|
+
score += structure_score
|
|
431
|
+
|
|
432
|
+
# 需求追溯性检查 (2.5分) - 更精确的匹配
|
|
433
|
+
req_references = len(re.findall(r'Requirement[s]?\s+\d+\.\d+|_Requirements:\s+\d+\.\d+|Validates:\s+Requirements?\s+\d+\.\d+', design_content, re.IGNORECASE))
|
|
434
|
+
# 检查双向追溯
|
|
435
|
+
bidirectional_refs = len(re.findall(r'Validates:\s*Requirements?\s+\d+\.\d+', design_content, re.IGNORECASE))
|
|
436
|
+
|
|
437
|
+
traceability_score = min(req_references * 0.12, 2.0)
|
|
438
|
+
if bidirectional_refs > 0:
|
|
439
|
+
traceability_score += min(bidirectional_refs * 0.08, 0.5)
|
|
440
|
+
|
|
441
|
+
criteria_scores['traceability'] = traceability_score
|
|
442
|
+
score += traceability_score
|
|
443
|
+
|
|
444
|
+
if req_references < 3:
|
|
445
|
+
issues.append(f"Few requirements references (current {req_references}, target 5+)")
|
|
446
|
+
if bidirectional_refs == 0:
|
|
447
|
+
issues.append("Missing bidirectional traceability (Validates: Requirements X.Y)")
|
|
448
|
+
|
|
449
|
+
# 架构图和设计图 (1.5分) - 检查图表质量
|
|
450
|
+
mermaid_diagrams = len(re.findall(r'```mermaid', design_content))
|
|
451
|
+
plantuml_diagrams = len(re.findall(r'```plantuml', design_content))
|
|
452
|
+
diagram_keywords = len(re.findall(r'Architecture Diagram|Component Diagram|Sequence Diagram|Flow Diagram', design_content, re.IGNORECASE))
|
|
453
|
+
|
|
454
|
+
total_diagrams = mermaid_diagrams + plantuml_diagrams + diagram_keywords
|
|
455
|
+
diagram_score = min(total_diagrams * 0.35, 1.5)
|
|
456
|
+
criteria_scores['diagrams'] = diagram_score
|
|
457
|
+
score += diagram_score
|
|
458
|
+
|
|
459
|
+
if total_diagrams == 0:
|
|
460
|
+
missing_sections.append("Architecture/Design Diagrams")
|
|
461
|
+
elif total_diagrams < 2:
|
|
462
|
+
issues.append(f"Few diagrams (current {total_diagrams}, target 2+)")
|
|
463
|
+
|
|
464
|
+
# 组件详细度 (1.5分) - 检查组件描述完整性
|
|
465
|
+
component_sections = len(re.findall(r'### \d+\.', design_content))
|
|
466
|
+
interface_definitions = len(re.findall(r'(?:Interface|API|Method|Function)\s*(?:Definition|Signature)', design_content, re.IGNORECASE))
|
|
467
|
+
responsibility_mentions = len(re.findall(r'Responsibility|Responsibilities|Purpose', design_content, re.IGNORECASE))
|
|
468
|
+
dependency_mentions = len(re.findall(r'Depend(?:s|encies)|Requires?', design_content, re.IGNORECASE))
|
|
469
|
+
|
|
470
|
+
component_score = 0.0
|
|
471
|
+
if component_sections > 0:
|
|
472
|
+
component_score += min(component_sections * 0.12, 0.5)
|
|
473
|
+
if interface_definitions > 0:
|
|
474
|
+
component_score += min(interface_definitions * 0.08, 0.4)
|
|
475
|
+
if responsibility_mentions > 0:
|
|
476
|
+
component_score += min(responsibility_mentions * 0.08, 0.3)
|
|
477
|
+
if dependency_mentions > 0:
|
|
478
|
+
component_score += min(dependency_mentions * 0.06, 0.3)
|
|
479
|
+
|
|
480
|
+
criteria_scores['component_detail'] = component_score
|
|
481
|
+
score += component_score
|
|
482
|
+
|
|
483
|
+
if component_sections < 3:
|
|
484
|
+
issues.append(f"Few components (current {component_sections}, target 3+)")
|
|
485
|
+
|
|
486
|
+
# 技术选型说明 (1分)
|
|
487
|
+
tech_keywords = ['technology', 'framework', 'database', 'api', 'protocol', 'stack', 'library']
|
|
488
|
+
tech_coverage = sum(1 for keyword in tech_keywords if keyword.lower() in design_content.lower())
|
|
489
|
+
tech_score = min(tech_coverage * 0.15, 1.0)
|
|
490
|
+
criteria_scores['technology'] = tech_score
|
|
491
|
+
score += tech_score
|
|
492
|
+
|
|
493
|
+
if tech_coverage < 2:
|
|
494
|
+
issues.append("Insufficient technology stack explanation")
|
|
495
|
+
|
|
496
|
+
# 非功能需求设计 (1分)
|
|
497
|
+
nfr_design = ['performance', 'security', 'scalability', 'fault tolerance', 'monitoring', 'logging', 'error handling']
|
|
498
|
+
nfr_coverage = sum(1 for keyword in nfr_design if keyword.lower() in design_content.lower())
|
|
499
|
+
nfr_score = min(nfr_coverage * 0.15, 1.0)
|
|
500
|
+
criteria_scores['nfr_design'] = nfr_score
|
|
501
|
+
score += nfr_score
|
|
502
|
+
|
|
503
|
+
missing_nfr = [kw for kw in nfr_design if kw.lower() not in design_content.lower()]
|
|
504
|
+
if len(missing_nfr) > 4:
|
|
505
|
+
issues.append(f"Missing non-functional design: {', '.join(missing_nfr[:3])}")
|
|
506
|
+
|
|
507
|
+
# 接口定义完整性 (1分)
|
|
508
|
+
interface_indicators = len(re.findall(r'(?:Interface|API\s+Design|Data\s+Model|Data\s+Structure|Parameter|Return\s+Value)', design_content, re.IGNORECASE))
|
|
509
|
+
interface_score = min(interface_indicators * 0.2, 1.0)
|
|
510
|
+
criteria_scores['interfaces'] = interface_score
|
|
511
|
+
score += interface_score
|
|
512
|
+
|
|
513
|
+
return QualityAssessment(
|
|
514
|
+
score=min(score, 10.0),
|
|
515
|
+
criteria_scores=criteria_scores,
|
|
516
|
+
missing_sections=missing_sections,
|
|
517
|
+
incomplete_sections=incomplete_sections,
|
|
518
|
+
issues=issues,
|
|
519
|
+
language=lang
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
def assess_tasks_quality(self, content: str) -> QualityAssessment:
|
|
523
|
+
"""评估 Tasks 文档完整性"""
|
|
524
|
+
# 匹配不同状态的任务
|
|
525
|
+
completed_tasks = re.findall(r'- \[x\] (.+)', content)
|
|
526
|
+
in_progress_tasks = re.findall(r'- \[-\] (.+)', content)
|
|
527
|
+
not_started_tasks = re.findall(r'- \[ \] (.+)', content)
|
|
528
|
+
queued_tasks = re.findall(r'- \[~\] (.+)', content)
|
|
529
|
+
|
|
530
|
+
total_count = len(completed_tasks) + len(in_progress_tasks) + len(not_started_tasks) + len(queued_tasks)
|
|
531
|
+
completed_count = len(completed_tasks)
|
|
532
|
+
|
|
533
|
+
# 基于完成率计算分数
|
|
534
|
+
completion_rate = (completed_count / total_count * 100) if total_count > 0 else 0
|
|
535
|
+
score = completion_rate / 10.0 # 转换为 0-10 分
|
|
536
|
+
|
|
537
|
+
issues = []
|
|
538
|
+
if total_count == 0:
|
|
539
|
+
issues.append("No tasks found in document")
|
|
540
|
+
elif completion_rate < 50:
|
|
541
|
+
issues.append(f"Low completion rate: {completion_rate:.1f}%")
|
|
542
|
+
|
|
543
|
+
return QualityAssessment(
|
|
544
|
+
score=min(score, 10.0),
|
|
545
|
+
criteria_scores={'completion_rate': score},
|
|
546
|
+
missing_sections=[],
|
|
547
|
+
incomplete_sections=[],
|
|
548
|
+
issues=issues,
|
|
549
|
+
language='en'
|
|
550
|
+
)
|