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.
Files changed (45) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +172 -0
  3. package/bin/kiro-spec-engine.js +62 -0
  4. package/docs/agent-hooks-analysis.md +815 -0
  5. package/docs/cross-tool-guide.md +554 -0
  6. package/docs/manual-workflows-guide.md +417 -0
  7. package/docs/steering-strategy-guide.md +196 -0
  8. package/lib/adoption/detection-engine.js +14 -4
  9. package/lib/commands/adopt.js +117 -3
  10. package/lib/commands/context.js +99 -0
  11. package/lib/commands/prompt.js +105 -0
  12. package/lib/commands/status.js +225 -0
  13. package/lib/commands/task.js +199 -0
  14. package/lib/commands/watch.js +569 -0
  15. package/lib/commands/workflows.js +240 -0
  16. package/lib/commands/workspace.js +189 -0
  17. package/lib/context/context-exporter.js +378 -0
  18. package/lib/context/prompt-generator.js +482 -0
  19. package/lib/steering/adoption-config.js +164 -0
  20. package/lib/steering/steering-manager.js +289 -0
  21. package/lib/task/task-claimer.js +430 -0
  22. package/lib/utils/tool-detector.js +383 -0
  23. package/lib/watch/action-executor.js +458 -0
  24. package/lib/watch/event-debouncer.js +323 -0
  25. package/lib/watch/execution-logger.js +550 -0
  26. package/lib/watch/file-watcher.js +499 -0
  27. package/lib/watch/presets.js +266 -0
  28. package/lib/watch/watch-manager.js +533 -0
  29. package/lib/workspace/workspace-manager.js +370 -0
  30. package/lib/workspace/workspace-sync.js +356 -0
  31. package/package.json +3 -1
  32. package/template/.kiro/tools/backup_manager.py +295 -0
  33. package/template/.kiro/tools/configuration_manager.py +218 -0
  34. package/template/.kiro/tools/document_evaluator.py +550 -0
  35. package/template/.kiro/tools/enhancement_logger.py +168 -0
  36. package/template/.kiro/tools/error_handler.py +335 -0
  37. package/template/.kiro/tools/improvement_identifier.py +444 -0
  38. package/template/.kiro/tools/modification_applicator.py +737 -0
  39. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  40. package/template/.kiro/tools/quality_scorer.py +305 -0
  41. package/template/.kiro/tools/report_generator.py +154 -0
  42. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  43. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  44. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  45. 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()