kiro-spec-engine 1.2.2 → 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 +91 -0
- package/README.md +172 -0
- package/bin/kiro-spec-engine.js +62 -0
- package/docs/adoption-guide.md +506 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/architecture.md +706 -0
- package/docs/cross-tool-guide.md +554 -0
- package/docs/developer-guide.md +615 -0
- package/docs/manual-workflows-guide.md +417 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/docs/upgrade-guide.md +632 -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 +4 -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,606 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Ultrawork Enhancer V3 - 主控制器(带收敛控制)
|
|
4
|
+
|
|
5
|
+
负责协调文档增强过程,管理改进循环,强制收敛条件
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from typing import Optional, List, Tuple
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from document_evaluator import DocumentEvaluator, QualityAssessment
|
|
14
|
+
from improvement_identifier import ImprovementIdentifier, Improvement
|
|
15
|
+
from modification_applicator import ModificationApplicator, ModificationResult
|
|
16
|
+
from quality_scorer import QualityScorer, ScoringResult
|
|
17
|
+
from backup_manager import BackupManager, BackupInfo
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class EnhancementResult:
|
|
22
|
+
"""增强结果"""
|
|
23
|
+
success: bool
|
|
24
|
+
document_type: str # 'requirements', 'design', 'tasks'
|
|
25
|
+
initial_score: float
|
|
26
|
+
final_score: float
|
|
27
|
+
iterations: int
|
|
28
|
+
improvements_applied: List[Improvement] = field(default_factory=list)
|
|
29
|
+
improvements_failed: List[Tuple[Improvement, Exception]] = field(default_factory=list)
|
|
30
|
+
stopping_reason: str = "" # 'threshold_reached', 'max_iterations', 'plateau', 'no_improvements'
|
|
31
|
+
modification_report: str = ""
|
|
32
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
33
|
+
score_history: List[float] = field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class UltraworkEnhancerV3:
|
|
37
|
+
"""
|
|
38
|
+
Ultrawork 增强器 V3 - 主控制器
|
|
39
|
+
|
|
40
|
+
集成所有核心组件,管理改进循环,强制收敛条件
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self,
|
|
44
|
+
quality_threshold: float = 9.0,
|
|
45
|
+
max_iterations: int = 10,
|
|
46
|
+
plateau_iterations: int = 3,
|
|
47
|
+
min_score_improvement: float = 0.1,
|
|
48
|
+
create_backups: bool = True,
|
|
49
|
+
cleanup_backups_on_success: bool = True):
|
|
50
|
+
"""
|
|
51
|
+
初始化增强器
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
quality_threshold: 质量阈值 (0-10)
|
|
55
|
+
max_iterations: 最大迭代次数
|
|
56
|
+
plateau_iterations: 平台期迭代次数(连续N次无改进则停止)
|
|
57
|
+
min_score_improvement: 最小分数改进(低于此值视为无改进)
|
|
58
|
+
create_backups: 是否创建备份
|
|
59
|
+
cleanup_backups_on_success: 成功后是否清理备份
|
|
60
|
+
"""
|
|
61
|
+
self.quality_threshold = quality_threshold
|
|
62
|
+
self.max_iterations = max_iterations
|
|
63
|
+
self.plateau_iterations = plateau_iterations
|
|
64
|
+
self.min_score_improvement = min_score_improvement
|
|
65
|
+
self.create_backups = create_backups
|
|
66
|
+
self.cleanup_backups_on_success = cleanup_backups_on_success
|
|
67
|
+
|
|
68
|
+
# 初始化核心组件
|
|
69
|
+
self.evaluator = DocumentEvaluator()
|
|
70
|
+
self.identifier = ImprovementIdentifier()
|
|
71
|
+
self.applicator = ModificationApplicator()
|
|
72
|
+
self.scorer = QualityScorer()
|
|
73
|
+
self.backup_manager = BackupManager()
|
|
74
|
+
|
|
75
|
+
def set_quality_threshold(self, threshold: float):
|
|
76
|
+
"""设置质量阈值"""
|
|
77
|
+
if not 0.0 <= threshold <= 10.0:
|
|
78
|
+
raise ValueError(f"Threshold must be between 0.0 and 10.0, got {threshold}")
|
|
79
|
+
self.quality_threshold = threshold
|
|
80
|
+
|
|
81
|
+
def set_max_iterations(self, max_iter: int):
|
|
82
|
+
"""设置最大迭代次数"""
|
|
83
|
+
if not 1 <= max_iter <= 100:
|
|
84
|
+
raise ValueError(f"Max iterations must be between 1 and 100, got {max_iter}")
|
|
85
|
+
self.max_iterations = max_iter
|
|
86
|
+
|
|
87
|
+
def enhance_requirements_quality(self, requirements_path: str) -> EnhancementResult:
|
|
88
|
+
"""
|
|
89
|
+
增强 Requirements 文档质量
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
requirements_path: Requirements 文档路径
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
EnhancementResult: 增强结果
|
|
96
|
+
"""
|
|
97
|
+
print(f"\n{'='*60}")
|
|
98
|
+
print(f"Enhancing Requirements: {requirements_path}")
|
|
99
|
+
print(f"{'='*60}\n")
|
|
100
|
+
|
|
101
|
+
# 读取文档
|
|
102
|
+
try:
|
|
103
|
+
with open(requirements_path, 'r', encoding='utf-8') as f:
|
|
104
|
+
content = f.read()
|
|
105
|
+
except Exception as e:
|
|
106
|
+
return EnhancementResult(
|
|
107
|
+
success=False,
|
|
108
|
+
document_type='requirements',
|
|
109
|
+
initial_score=0.0,
|
|
110
|
+
final_score=0.0,
|
|
111
|
+
iterations=0,
|
|
112
|
+
stopping_reason=f"file_read_error: {e}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# 初始评估
|
|
116
|
+
initial_assessment = self.evaluator.assess_requirements_quality(content)
|
|
117
|
+
initial_score = initial_assessment.score
|
|
118
|
+
|
|
119
|
+
print(f"Initial Score: {initial_score:.2f}/10")
|
|
120
|
+
print(f"Language: {initial_assessment.language}")
|
|
121
|
+
print(f"Missing Sections: {initial_assessment.missing_sections}")
|
|
122
|
+
print(f"Issues: {len(initial_assessment.issues)}\n")
|
|
123
|
+
|
|
124
|
+
# 检查是否已达到阈值
|
|
125
|
+
if initial_score >= self.quality_threshold:
|
|
126
|
+
print(f"✓ Already meets quality threshold ({self.quality_threshold})")
|
|
127
|
+
return EnhancementResult(
|
|
128
|
+
success=True,
|
|
129
|
+
document_type='requirements',
|
|
130
|
+
initial_score=initial_score,
|
|
131
|
+
final_score=initial_score,
|
|
132
|
+
iterations=0,
|
|
133
|
+
stopping_reason='threshold_reached',
|
|
134
|
+
score_history=[initial_score]
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# 创建备份
|
|
138
|
+
backup_id = None
|
|
139
|
+
if self.create_backups:
|
|
140
|
+
try:
|
|
141
|
+
backup_id = self.backup_manager.create_backup(
|
|
142
|
+
requirements_path,
|
|
143
|
+
reason="requirements_enhancement"
|
|
144
|
+
)
|
|
145
|
+
print(f"✓ Created backup: {backup_id}\n")
|
|
146
|
+
except Exception as e:
|
|
147
|
+
print(f"✗ Failed to create backup: {e}")
|
|
148
|
+
print(" Aborting enhancement to preserve document integrity\n")
|
|
149
|
+
return EnhancementResult(
|
|
150
|
+
success=False,
|
|
151
|
+
document_type='requirements',
|
|
152
|
+
initial_score=initial_score,
|
|
153
|
+
final_score=initial_score,
|
|
154
|
+
iterations=0,
|
|
155
|
+
stopping_reason=f"backup_failed: {e}"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# 开始改进循环
|
|
159
|
+
current_content = content
|
|
160
|
+
current_score = initial_score
|
|
161
|
+
score_history = [initial_score]
|
|
162
|
+
all_applied = []
|
|
163
|
+
all_failed = []
|
|
164
|
+
iteration = 0
|
|
165
|
+
plateau_count = 0
|
|
166
|
+
|
|
167
|
+
while iteration < self.max_iterations:
|
|
168
|
+
iteration += 1
|
|
169
|
+
print(f"\n--- Iteration {iteration}/{self.max_iterations} ---")
|
|
170
|
+
|
|
171
|
+
# 评估当前质量
|
|
172
|
+
assessment = self.evaluator.assess_requirements_quality(current_content)
|
|
173
|
+
|
|
174
|
+
# 识别改进
|
|
175
|
+
improvements = self.identifier.identify_requirements_improvements(
|
|
176
|
+
current_content,
|
|
177
|
+
assessment
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if not improvements:
|
|
181
|
+
print("✓ No more improvements identified")
|
|
182
|
+
stopping_reason = 'no_improvements'
|
|
183
|
+
break
|
|
184
|
+
|
|
185
|
+
print(f"Identified {len(improvements)} improvements")
|
|
186
|
+
|
|
187
|
+
# 应用改进
|
|
188
|
+
result = self.applicator.apply_requirements_improvements(
|
|
189
|
+
current_content,
|
|
190
|
+
improvements,
|
|
191
|
+
language=assessment.language
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
print(f"Applied {len(result.applied_improvements)} improvements")
|
|
195
|
+
print(f"Failed {len(result.failed_improvements)} improvements")
|
|
196
|
+
|
|
197
|
+
# 更新内容
|
|
198
|
+
current_content = result.modified_content
|
|
199
|
+
all_applied.extend(result.applied_improvements)
|
|
200
|
+
all_failed.extend(result.failed_improvements)
|
|
201
|
+
|
|
202
|
+
# 重新评估
|
|
203
|
+
new_assessment = self.evaluator.assess_requirements_quality(current_content)
|
|
204
|
+
new_score = new_assessment.score
|
|
205
|
+
score_history.append(new_score)
|
|
206
|
+
|
|
207
|
+
score_delta = new_score - current_score
|
|
208
|
+
print(f"Score: {current_score:.2f} → {new_score:.2f} (Δ{score_delta:+.2f})")
|
|
209
|
+
|
|
210
|
+
# 检查收敛条件
|
|
211
|
+
|
|
212
|
+
# 1. 达到阈值
|
|
213
|
+
if new_score >= self.quality_threshold:
|
|
214
|
+
print(f"\n✓ Quality threshold reached ({self.quality_threshold})")
|
|
215
|
+
stopping_reason = 'threshold_reached'
|
|
216
|
+
current_score = new_score
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
# 2. 分数无改进(平台期)
|
|
220
|
+
if score_delta < self.min_score_improvement:
|
|
221
|
+
plateau_count += 1
|
|
222
|
+
print(f"⚠ Plateau detected ({plateau_count}/{self.plateau_iterations})")
|
|
223
|
+
|
|
224
|
+
if plateau_count >= self.plateau_iterations:
|
|
225
|
+
print(f"\n✓ Plateau reached ({self.plateau_iterations} iterations without improvement)")
|
|
226
|
+
stopping_reason = 'plateau'
|
|
227
|
+
current_score = new_score
|
|
228
|
+
break
|
|
229
|
+
else:
|
|
230
|
+
plateau_count = 0 # 重置平台期计数
|
|
231
|
+
|
|
232
|
+
current_score = new_score
|
|
233
|
+
|
|
234
|
+
# 检查是否达到最大迭代次数
|
|
235
|
+
if iteration >= self.max_iterations:
|
|
236
|
+
print(f"\n✓ Max iterations reached ({self.max_iterations})")
|
|
237
|
+
stopping_reason = 'max_iterations'
|
|
238
|
+
|
|
239
|
+
# 保存增强后的文档
|
|
240
|
+
try:
|
|
241
|
+
with open(requirements_path, 'w', encoding='utf-8') as f:
|
|
242
|
+
f.write(current_content)
|
|
243
|
+
print(f"\n✓ Saved enhanced document to {requirements_path}")
|
|
244
|
+
|
|
245
|
+
# 清理备份(如果成功且配置为清理)
|
|
246
|
+
if backup_id and self.cleanup_backups_on_success:
|
|
247
|
+
if self.backup_manager.cleanup_backup(backup_id):
|
|
248
|
+
print(f"✓ Cleaned up backup: {backup_id}")
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
print(f"\n✗ Failed to save document: {e}")
|
|
252
|
+
|
|
253
|
+
# 尝试恢复备份
|
|
254
|
+
if backup_id:
|
|
255
|
+
try:
|
|
256
|
+
self.backup_manager.restore_backup(backup_id)
|
|
257
|
+
print(f"✓ Restored from backup: {backup_id}")
|
|
258
|
+
except Exception as restore_error:
|
|
259
|
+
print(f"✗ Failed to restore backup: {restore_error}")
|
|
260
|
+
|
|
261
|
+
return EnhancementResult(
|
|
262
|
+
success=False,
|
|
263
|
+
document_type='requirements',
|
|
264
|
+
initial_score=initial_score,
|
|
265
|
+
final_score=current_score,
|
|
266
|
+
iterations=iteration,
|
|
267
|
+
improvements_applied=all_applied,
|
|
268
|
+
improvements_failed=all_failed,
|
|
269
|
+
stopping_reason=f"file_write_error: {e}",
|
|
270
|
+
score_history=score_history
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# 生成报告
|
|
274
|
+
print(f"\n{'='*60}")
|
|
275
|
+
print(f"Enhancement Complete")
|
|
276
|
+
print(f"{'='*60}")
|
|
277
|
+
print(f"Initial Score: {initial_score:.2f}/10")
|
|
278
|
+
print(f"Final Score: {current_score:.2f}/10")
|
|
279
|
+
print(f"Improvement: +{current_score - initial_score:.2f}")
|
|
280
|
+
print(f"Iterations: {iteration}")
|
|
281
|
+
print(f"Stopping Reason: {stopping_reason}")
|
|
282
|
+
print(f"Applied Improvements: {len(all_applied)}")
|
|
283
|
+
print(f"Failed Improvements: {len(all_failed)}")
|
|
284
|
+
print(f"{'='*60}\n")
|
|
285
|
+
|
|
286
|
+
return EnhancementResult(
|
|
287
|
+
success=True,
|
|
288
|
+
document_type='requirements',
|
|
289
|
+
initial_score=initial_score,
|
|
290
|
+
final_score=current_score,
|
|
291
|
+
iterations=iteration,
|
|
292
|
+
improvements_applied=all_applied,
|
|
293
|
+
improvements_failed=all_failed,
|
|
294
|
+
stopping_reason=stopping_reason,
|
|
295
|
+
score_history=score_history
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def enhance_design_completeness(self, design_path: str, requirements_path: str) -> EnhancementResult:
|
|
299
|
+
"""
|
|
300
|
+
增强 Design 文档完整性
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
design_path: Design 文档路径
|
|
304
|
+
requirements_path: Requirements 文档路径
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
EnhancementResult: 增强结果
|
|
308
|
+
"""
|
|
309
|
+
print(f"\n{'='*60}")
|
|
310
|
+
print(f"Enhancing Design: {design_path}")
|
|
311
|
+
print(f"{'='*60}\n")
|
|
312
|
+
|
|
313
|
+
# 读取文档
|
|
314
|
+
try:
|
|
315
|
+
with open(design_path, 'r', encoding='utf-8') as f:
|
|
316
|
+
design_content = f.read()
|
|
317
|
+
with open(requirements_path, 'r', encoding='utf-8') as f:
|
|
318
|
+
requirements_content = f.read()
|
|
319
|
+
except Exception as e:
|
|
320
|
+
return EnhancementResult(
|
|
321
|
+
success=False,
|
|
322
|
+
document_type='design',
|
|
323
|
+
initial_score=0.0,
|
|
324
|
+
final_score=0.0,
|
|
325
|
+
iterations=0,
|
|
326
|
+
stopping_reason=f"file_read_error: {e}"
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# 初始评估
|
|
330
|
+
initial_assessment = self.evaluator.assess_design_quality(
|
|
331
|
+
design_content,
|
|
332
|
+
requirements_content
|
|
333
|
+
)
|
|
334
|
+
initial_score = initial_assessment.score
|
|
335
|
+
|
|
336
|
+
print(f"Initial Score: {initial_score:.2f}/10")
|
|
337
|
+
print(f"Language: {initial_assessment.language}")
|
|
338
|
+
print(f"Missing Sections: {initial_assessment.missing_sections}")
|
|
339
|
+
print(f"Issues: {len(initial_assessment.issues)}\n")
|
|
340
|
+
|
|
341
|
+
# 检查是否已达到阈值
|
|
342
|
+
if initial_score >= self.quality_threshold:
|
|
343
|
+
print(f"✓ Already meets quality threshold ({self.quality_threshold})")
|
|
344
|
+
return EnhancementResult(
|
|
345
|
+
success=True,
|
|
346
|
+
document_type='design',
|
|
347
|
+
initial_score=initial_score,
|
|
348
|
+
final_score=initial_score,
|
|
349
|
+
iterations=0,
|
|
350
|
+
stopping_reason='threshold_reached',
|
|
351
|
+
score_history=[initial_score]
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# 创建备份
|
|
355
|
+
backup_id = None
|
|
356
|
+
if self.create_backups:
|
|
357
|
+
try:
|
|
358
|
+
backup_id = self.backup_manager.create_backup(
|
|
359
|
+
design_path,
|
|
360
|
+
reason="design_enhancement"
|
|
361
|
+
)
|
|
362
|
+
print(f"✓ Created backup: {backup_id}\n")
|
|
363
|
+
except Exception as e:
|
|
364
|
+
print(f"✗ Failed to create backup: {e}")
|
|
365
|
+
print(" Aborting enhancement to preserve document integrity\n")
|
|
366
|
+
return EnhancementResult(
|
|
367
|
+
success=False,
|
|
368
|
+
document_type='design',
|
|
369
|
+
initial_score=initial_score,
|
|
370
|
+
final_score=initial_score,
|
|
371
|
+
iterations=0,
|
|
372
|
+
stopping_reason=f"backup_failed: {e}"
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
# 开始改进循环
|
|
376
|
+
current_content = design_content
|
|
377
|
+
current_score = initial_score
|
|
378
|
+
score_history = [initial_score]
|
|
379
|
+
all_applied = []
|
|
380
|
+
all_failed = []
|
|
381
|
+
iteration = 0
|
|
382
|
+
plateau_count = 0
|
|
383
|
+
|
|
384
|
+
while iteration < self.max_iterations:
|
|
385
|
+
iteration += 1
|
|
386
|
+
print(f"\n--- Iteration {iteration}/{self.max_iterations} ---")
|
|
387
|
+
|
|
388
|
+
# 评估当前质量
|
|
389
|
+
assessment = self.evaluator.assess_design_quality(
|
|
390
|
+
current_content,
|
|
391
|
+
requirements_content
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# 识别改进
|
|
395
|
+
improvements = self.identifier.identify_design_improvements(
|
|
396
|
+
current_content,
|
|
397
|
+
requirements_content,
|
|
398
|
+
assessment
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if not improvements:
|
|
402
|
+
print("✓ No more improvements identified")
|
|
403
|
+
stopping_reason = 'no_improvements'
|
|
404
|
+
break
|
|
405
|
+
|
|
406
|
+
print(f"Identified {len(improvements)} improvements")
|
|
407
|
+
|
|
408
|
+
# 应用改进
|
|
409
|
+
result = self.applicator.apply_design_improvements(
|
|
410
|
+
current_content,
|
|
411
|
+
improvements,
|
|
412
|
+
requirements_content,
|
|
413
|
+
language=assessment.language
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
print(f"Applied {len(result.applied_improvements)} improvements")
|
|
417
|
+
print(f"Failed {len(result.failed_improvements)} improvements")
|
|
418
|
+
|
|
419
|
+
# 更新内容
|
|
420
|
+
current_content = result.modified_content
|
|
421
|
+
all_applied.extend(result.applied_improvements)
|
|
422
|
+
all_failed.extend(result.failed_improvements)
|
|
423
|
+
|
|
424
|
+
# 重新评估
|
|
425
|
+
new_assessment = self.evaluator.assess_design_quality(
|
|
426
|
+
current_content,
|
|
427
|
+
requirements_content
|
|
428
|
+
)
|
|
429
|
+
new_score = new_assessment.score
|
|
430
|
+
score_history.append(new_score)
|
|
431
|
+
|
|
432
|
+
score_delta = new_score - current_score
|
|
433
|
+
print(f"Score: {current_score:.2f} → {new_score:.2f} (Δ{score_delta:+.2f})")
|
|
434
|
+
|
|
435
|
+
# 检查收敛条件
|
|
436
|
+
|
|
437
|
+
# 1. 达到阈值
|
|
438
|
+
if new_score >= self.quality_threshold:
|
|
439
|
+
print(f"\n✓ Quality threshold reached ({self.quality_threshold})")
|
|
440
|
+
stopping_reason = 'threshold_reached'
|
|
441
|
+
current_score = new_score
|
|
442
|
+
break
|
|
443
|
+
|
|
444
|
+
# 2. 分数无改进(平台期)
|
|
445
|
+
if score_delta < self.min_score_improvement:
|
|
446
|
+
plateau_count += 1
|
|
447
|
+
print(f"⚠ Plateau detected ({plateau_count}/{self.plateau_iterations})")
|
|
448
|
+
|
|
449
|
+
if plateau_count >= self.plateau_iterations:
|
|
450
|
+
print(f"\n✓ Plateau reached ({self.plateau_iterations} iterations without improvement)")
|
|
451
|
+
stopping_reason = 'plateau'
|
|
452
|
+
current_score = new_score
|
|
453
|
+
break
|
|
454
|
+
else:
|
|
455
|
+
plateau_count = 0
|
|
456
|
+
|
|
457
|
+
current_score = new_score
|
|
458
|
+
|
|
459
|
+
# 检查是否达到最大迭代次数
|
|
460
|
+
if iteration >= self.max_iterations:
|
|
461
|
+
print(f"\n✓ Max iterations reached ({self.max_iterations})")
|
|
462
|
+
stopping_reason = 'max_iterations'
|
|
463
|
+
|
|
464
|
+
# 保存增强后的文档
|
|
465
|
+
try:
|
|
466
|
+
with open(design_path, 'w', encoding='utf-8') as f:
|
|
467
|
+
f.write(current_content)
|
|
468
|
+
print(f"\n✓ Saved enhanced document to {design_path}")
|
|
469
|
+
|
|
470
|
+
# 清理备份(如果成功且配置为清理)
|
|
471
|
+
if backup_id and self.cleanup_backups_on_success:
|
|
472
|
+
if self.backup_manager.cleanup_backup(backup_id):
|
|
473
|
+
print(f"✓ Cleaned up backup: {backup_id}")
|
|
474
|
+
|
|
475
|
+
except Exception as e:
|
|
476
|
+
print(f"\n✗ Failed to save document: {e}")
|
|
477
|
+
|
|
478
|
+
# 尝试恢复备份
|
|
479
|
+
if backup_id:
|
|
480
|
+
try:
|
|
481
|
+
self.backup_manager.restore_backup(backup_id)
|
|
482
|
+
print(f"✓ Restored from backup: {backup_id}")
|
|
483
|
+
except Exception as restore_error:
|
|
484
|
+
print(f"✗ Failed to restore backup: {restore_error}")
|
|
485
|
+
|
|
486
|
+
return EnhancementResult(
|
|
487
|
+
success=False,
|
|
488
|
+
document_type='design',
|
|
489
|
+
initial_score=initial_score,
|
|
490
|
+
final_score=current_score,
|
|
491
|
+
iterations=iteration,
|
|
492
|
+
improvements_applied=all_applied,
|
|
493
|
+
improvements_failed=all_failed,
|
|
494
|
+
stopping_reason=f"file_write_error: {e}",
|
|
495
|
+
score_history=score_history
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
# 生成报告
|
|
499
|
+
print(f"\n{'='*60}")
|
|
500
|
+
print(f"Enhancement Complete")
|
|
501
|
+
print(f"{'='*60}")
|
|
502
|
+
print(f"Initial Score: {initial_score:.2f}/10")
|
|
503
|
+
print(f"Final Score: {current_score:.2f}/10")
|
|
504
|
+
print(f"Improvement: +{current_score - initial_score:.2f}")
|
|
505
|
+
print(f"Iterations: {iteration}")
|
|
506
|
+
print(f"Stopping Reason: {stopping_reason}")
|
|
507
|
+
print(f"Applied Improvements: {len(all_applied)}")
|
|
508
|
+
print(f"Failed Improvements: {len(all_failed)}")
|
|
509
|
+
print(f"{'='*60}\n")
|
|
510
|
+
|
|
511
|
+
return EnhancementResult(
|
|
512
|
+
success=True,
|
|
513
|
+
document_type='design',
|
|
514
|
+
initial_score=initial_score,
|
|
515
|
+
final_score=current_score,
|
|
516
|
+
iterations=iteration,
|
|
517
|
+
improvements_applied=all_applied,
|
|
518
|
+
improvements_failed=all_failed,
|
|
519
|
+
stopping_reason=stopping_reason,
|
|
520
|
+
score_history=score_history
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
def validate_tasks_completeness(self, tasks_path: str) -> EnhancementResult:
|
|
524
|
+
"""
|
|
525
|
+
验证 Tasks 文档完整性
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
tasks_path: Tasks 文档路径
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
EnhancementResult: 验证结果
|
|
532
|
+
"""
|
|
533
|
+
print(f"\n{'='*60}")
|
|
534
|
+
print(f"Validating Tasks: {tasks_path}")
|
|
535
|
+
print(f"{'='*60}\n")
|
|
536
|
+
|
|
537
|
+
# 读取文档
|
|
538
|
+
try:
|
|
539
|
+
with open(tasks_path, 'r', encoding='utf-8') as f:
|
|
540
|
+
content = f.read()
|
|
541
|
+
except Exception as e:
|
|
542
|
+
return EnhancementResult(
|
|
543
|
+
success=False,
|
|
544
|
+
document_type='tasks',
|
|
545
|
+
initial_score=0.0,
|
|
546
|
+
final_score=0.0,
|
|
547
|
+
iterations=0,
|
|
548
|
+
stopping_reason=f"file_read_error: {e}"
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
# 评估
|
|
552
|
+
assessment = self.evaluator.assess_tasks_quality(content)
|
|
553
|
+
score = assessment.score
|
|
554
|
+
|
|
555
|
+
print(f"Completion Score: {score:.2f}/10")
|
|
556
|
+
print(f"Issues: {assessment.issues}\n")
|
|
557
|
+
|
|
558
|
+
# Tasks 文档不需要增强,只需要验证
|
|
559
|
+
return EnhancementResult(
|
|
560
|
+
success=True,
|
|
561
|
+
document_type='tasks',
|
|
562
|
+
initial_score=score,
|
|
563
|
+
final_score=score,
|
|
564
|
+
iterations=0,
|
|
565
|
+
stopping_reason='validation_only',
|
|
566
|
+
score_history=[score]
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def main():
|
|
571
|
+
"""命令行入口"""
|
|
572
|
+
import sys
|
|
573
|
+
|
|
574
|
+
if len(sys.argv) < 3:
|
|
575
|
+
print("Usage: python ultrawork_enhancer_v3.py <command> <path> [requirements_path]")
|
|
576
|
+
print("Commands:")
|
|
577
|
+
print(" requirements <path> - Enhance requirements document")
|
|
578
|
+
print(" design <path> <requirements> - Enhance design document")
|
|
579
|
+
print(" tasks <path> - Validate tasks document")
|
|
580
|
+
sys.exit(1)
|
|
581
|
+
|
|
582
|
+
command = sys.argv[1]
|
|
583
|
+
path = sys.argv[2]
|
|
584
|
+
|
|
585
|
+
enhancer = UltraworkEnhancerV3()
|
|
586
|
+
|
|
587
|
+
if command == 'requirements':
|
|
588
|
+
result = enhancer.enhance_requirements_quality(path)
|
|
589
|
+
elif command == 'design':
|
|
590
|
+
if len(sys.argv) < 4:
|
|
591
|
+
print("Error: design command requires requirements path")
|
|
592
|
+
sys.exit(1)
|
|
593
|
+
requirements_path = sys.argv[3]
|
|
594
|
+
result = enhancer.enhance_design_completeness(path, requirements_path)
|
|
595
|
+
elif command == 'tasks':
|
|
596
|
+
result = enhancer.validate_tasks_completeness(path)
|
|
597
|
+
else:
|
|
598
|
+
print(f"Error: Unknown command '{command}'")
|
|
599
|
+
sys.exit(1)
|
|
600
|
+
|
|
601
|
+
# 返回退出码
|
|
602
|
+
sys.exit(0 if result.success else 1)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
if __name__ == '__main__':
|
|
606
|
+
main()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Workflow Quality Gate - Spec 创建工作流集成脚本
|
|
4
|
+
|
|
5
|
+
用于在 requirements-first-workflow subagent 中调用质量门控
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from quality_gate_enforcer import QualityGateEnforcer, GateResult
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
"""
|
|
16
|
+
命令行入口 - 供 subagent 调用
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
python workflow_quality_gate.py requirements <path>
|
|
20
|
+
python workflow_quality_gate.py design <design_path> <requirements_path>
|
|
21
|
+
python workflow_quality_gate.py tasks <path>
|
|
22
|
+
|
|
23
|
+
Exit Codes:
|
|
24
|
+
0 - Quality gate passed
|
|
25
|
+
1 - Quality gate failed
|
|
26
|
+
2 - Error occurred
|
|
27
|
+
"""
|
|
28
|
+
if len(sys.argv) < 3:
|
|
29
|
+
print("Error: Insufficient arguments", file=sys.stderr)
|
|
30
|
+
print("Usage: workflow_quality_gate.py <stage> <path> [requirements_path]", file=sys.stderr)
|
|
31
|
+
sys.exit(2)
|
|
32
|
+
|
|
33
|
+
stage = sys.argv[1].lower()
|
|
34
|
+
doc_path = sys.argv[2]
|
|
35
|
+
|
|
36
|
+
# 验证文件存在
|
|
37
|
+
if not os.path.exists(doc_path):
|
|
38
|
+
print(f"Error: File not found: {doc_path}", file=sys.stderr)
|
|
39
|
+
sys.exit(2)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
# 创建质量门控器
|
|
43
|
+
enforcer = QualityGateEnforcer(
|
|
44
|
+
requirements_threshold=9.0,
|
|
45
|
+
design_threshold=9.0,
|
|
46
|
+
tasks_threshold=8.0
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# 执行质量门检查
|
|
50
|
+
if stage == 'requirements':
|
|
51
|
+
result = enforcer.check_requirements_gate(doc_path)
|
|
52
|
+
|
|
53
|
+
elif stage == 'design':
|
|
54
|
+
if len(sys.argv) < 4:
|
|
55
|
+
print("Error: Design gate requires requirements path", file=sys.stderr)
|
|
56
|
+
sys.exit(2)
|
|
57
|
+
requirements_path = sys.argv[3]
|
|
58
|
+
if not os.path.exists(requirements_path):
|
|
59
|
+
print(f"Error: Requirements file not found: {requirements_path}", file=sys.stderr)
|
|
60
|
+
sys.exit(2)
|
|
61
|
+
result = enforcer.check_design_gate(doc_path, requirements_path)
|
|
62
|
+
|
|
63
|
+
elif stage == 'tasks':
|
|
64
|
+
result = enforcer.check_tasks_gate(doc_path)
|
|
65
|
+
|
|
66
|
+
else:
|
|
67
|
+
print(f"Error: Unknown stage '{stage}'", file=sys.stderr)
|
|
68
|
+
print("Valid stages: requirements, design, tasks", file=sys.stderr)
|
|
69
|
+
sys.exit(2)
|
|
70
|
+
|
|
71
|
+
# 输出结果
|
|
72
|
+
print(f"\n{'='*60}")
|
|
73
|
+
print(f"Quality Gate Result: {stage.upper()}")
|
|
74
|
+
print(f"{'='*60}")
|
|
75
|
+
print(f"Status: {'PASSED ✓' if result.passed else 'FAILED ✗'}")
|
|
76
|
+
print(f"Score: {result.score:.2f}/10")
|
|
77
|
+
print(f"Threshold: {result.threshold}/10")
|
|
78
|
+
|
|
79
|
+
if result.enhancement_result:
|
|
80
|
+
print(f"\nEnhancement Details:")
|
|
81
|
+
print(f" Initial Score: {result.enhancement_result.initial_score:.2f}/10")
|
|
82
|
+
print(f" Final Score: {result.enhancement_result.final_score:.2f}/10")
|
|
83
|
+
print(f" Improvement: +{result.enhancement_result.improvement:.2f}")
|
|
84
|
+
print(f" Iterations: {result.enhancement_result.iterations}")
|
|
85
|
+
print(f" Stopping Reason: {result.enhancement_result.stopping_reason}")
|
|
86
|
+
|
|
87
|
+
print(f"{'='*60}\n")
|
|
88
|
+
|
|
89
|
+
# 返回退出码
|
|
90
|
+
sys.exit(0 if result.passed else 1)
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"Error: {str(e)}", file=sys.stderr)
|
|
94
|
+
import traceback
|
|
95
|
+
traceback.print_exc(file=sys.stderr)
|
|
96
|
+
sys.exit(2)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == '__main__':
|
|
100
|
+
main()
|