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.
Files changed (49) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +172 -0
  3. package/bin/kiro-spec-engine.js +62 -0
  4. package/docs/adoption-guide.md +506 -0
  5. package/docs/agent-hooks-analysis.md +815 -0
  6. package/docs/architecture.md +706 -0
  7. package/docs/cross-tool-guide.md +554 -0
  8. package/docs/developer-guide.md +615 -0
  9. package/docs/manual-workflows-guide.md +417 -0
  10. package/docs/steering-strategy-guide.md +196 -0
  11. package/docs/upgrade-guide.md +632 -0
  12. package/lib/adoption/detection-engine.js +14 -4
  13. package/lib/commands/adopt.js +117 -3
  14. package/lib/commands/context.js +99 -0
  15. package/lib/commands/prompt.js +105 -0
  16. package/lib/commands/status.js +225 -0
  17. package/lib/commands/task.js +199 -0
  18. package/lib/commands/watch.js +569 -0
  19. package/lib/commands/workflows.js +240 -0
  20. package/lib/commands/workspace.js +189 -0
  21. package/lib/context/context-exporter.js +378 -0
  22. package/lib/context/prompt-generator.js +482 -0
  23. package/lib/steering/adoption-config.js +164 -0
  24. package/lib/steering/steering-manager.js +289 -0
  25. package/lib/task/task-claimer.js +430 -0
  26. package/lib/utils/tool-detector.js +383 -0
  27. package/lib/watch/action-executor.js +458 -0
  28. package/lib/watch/event-debouncer.js +323 -0
  29. package/lib/watch/execution-logger.js +550 -0
  30. package/lib/watch/file-watcher.js +499 -0
  31. package/lib/watch/presets.js +266 -0
  32. package/lib/watch/watch-manager.js +533 -0
  33. package/lib/workspace/workspace-manager.js +370 -0
  34. package/lib/workspace/workspace-sync.js +356 -0
  35. package/package.json +4 -1
  36. package/template/.kiro/tools/backup_manager.py +295 -0
  37. package/template/.kiro/tools/configuration_manager.py +218 -0
  38. package/template/.kiro/tools/document_evaluator.py +550 -0
  39. package/template/.kiro/tools/enhancement_logger.py +168 -0
  40. package/template/.kiro/tools/error_handler.py +335 -0
  41. package/template/.kiro/tools/improvement_identifier.py +444 -0
  42. package/template/.kiro/tools/modification_applicator.py +737 -0
  43. package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
  44. package/template/.kiro/tools/quality_scorer.py +305 -0
  45. package/template/.kiro/tools/report_generator.py +154 -0
  46. package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
  47. package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
  48. package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
  49. package/template/.kiro/tools/workflow_quality_gate.py +100 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiro-spec-engine",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Kiro Spec Engine - A spec-driven development engine with steering rules and quality enhancement powered by Ultrawork spirit",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  "lib/",
13
13
  "template/",
14
14
  "locales/",
15
+ "docs/",
15
16
  "README.md",
16
17
  "README.zh.md",
17
18
  "LICENSE",
@@ -66,9 +67,11 @@
66
67
  },
67
68
  "dependencies": {
68
69
  "chalk": "^4.1.2",
70
+ "chokidar": "^3.5.3",
69
71
  "commander": "^9.0.0",
70
72
  "fs-extra": "^10.0.0",
71
73
  "inquirer": "^8.2.0",
74
+ "minimatch": "^10.1.1",
72
75
  "path": "^0.12.7",
73
76
  "semver": "^7.5.4"
74
77
  },
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Backup Manager - 备份管理组件
4
+
5
+ 负责创建和管理文档备份,支持回滚操作
6
+ """
7
+
8
+ import os
9
+ import shutil
10
+ from datetime import datetime
11
+ from typing import List, Optional
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+
15
+
16
+ @dataclass
17
+ class BackupInfo:
18
+ """备份信息"""
19
+ backup_id: str
20
+ original_path: str
21
+ backup_path: str
22
+ timestamp: datetime
23
+ reason: str
24
+ size_bytes: int
25
+
26
+
27
+ class BackupManager:
28
+ """
29
+ 备份管理器 - 创建和管理文档备份
30
+
31
+ 支持备份创建、恢复和清理
32
+ """
33
+
34
+ def __init__(self, backup_dir: Optional[str] = None):
35
+ """
36
+ 初始化备份管理器
37
+
38
+ Args:
39
+ backup_dir: 备份目录路径,默认为 None(自动确定)
40
+ """
41
+ self.backup_dir = backup_dir
42
+ self.backups = {} # backup_id -> BackupInfo
43
+
44
+ def _get_backup_dir(self, file_path: str) -> str:
45
+ """
46
+ 获取备份目录路径
47
+
48
+ Args:
49
+ file_path: 原始文件路径
50
+
51
+ Returns:
52
+ str: 备份目录路径
53
+ """
54
+ if self.backup_dir:
55
+ return self.backup_dir
56
+
57
+ # 自动确定备份目录:.kiro/specs/{spec-name}/backups/
58
+ file_path_obj = Path(file_path)
59
+
60
+ # 查找 .kiro/specs/ 目录
61
+ current = file_path_obj.parent
62
+ while current != current.parent:
63
+ if current.name == 'specs' and current.parent.name == '.kiro':
64
+ # 找到 specs 目录,使用当前文件所在的 spec 目录
65
+ spec_dir = file_path_obj.parent
66
+ backup_dir = spec_dir / 'backups'
67
+ return str(backup_dir)
68
+ current = current.parent
69
+
70
+ # 如果找不到 specs 目录,使用文件所在目录的 backups 子目录
71
+ return str(file_path_obj.parent / 'backups')
72
+
73
+ def create_backup(self, file_path: str, reason: str = "enhancement") -> str:
74
+ """
75
+ 创建文件备份
76
+
77
+ Args:
78
+ file_path: 要备份的文件路径
79
+ reason: 备份原因
80
+
81
+ Returns:
82
+ str: 备份 ID
83
+
84
+ Raises:
85
+ FileNotFoundError: 文件不存在
86
+ PermissionError: 没有权限
87
+ IOError: 备份创建失败
88
+ """
89
+ # 检查文件是否存在
90
+ if not os.path.exists(file_path):
91
+ raise FileNotFoundError(f"File not found: {file_path}")
92
+
93
+ # 检查文件是否可读
94
+ if not os.access(file_path, os.R_OK):
95
+ raise PermissionError(f"Permission denied: {file_path}")
96
+
97
+ # 获取备份目录
98
+ backup_dir = self._get_backup_dir(file_path)
99
+
100
+ # 创建备份目录(如果不存在)
101
+ os.makedirs(backup_dir, exist_ok=True)
102
+
103
+ # 生成备份 ID 和备份文件名
104
+ timestamp = datetime.now()
105
+ timestamp_str = timestamp.strftime("%Y%m%d_%H%M%S_%f")
106
+ file_name = os.path.basename(file_path)
107
+ backup_id = f"{file_name}.backup-{timestamp_str}"
108
+ backup_path = os.path.join(backup_dir, backup_id)
109
+
110
+ # 复制文件
111
+ try:
112
+ shutil.copy2(file_path, backup_path)
113
+ except Exception as e:
114
+ raise IOError(f"Failed to create backup: {e}")
115
+
116
+ # 获取文件大小
117
+ size_bytes = os.path.getsize(backup_path)
118
+
119
+ # 记录备份信息
120
+ backup_info = BackupInfo(
121
+ backup_id=backup_id,
122
+ original_path=file_path,
123
+ backup_path=backup_path,
124
+ timestamp=timestamp,
125
+ reason=reason,
126
+ size_bytes=size_bytes
127
+ )
128
+
129
+ self.backups[backup_id] = backup_info
130
+
131
+ return backup_id
132
+
133
+ def restore_backup(self, backup_id: str) -> bool:
134
+ """
135
+ 从备份恢复文件
136
+
137
+ Args:
138
+ backup_id: 备份 ID
139
+
140
+ Returns:
141
+ bool: 恢复是否成功
142
+
143
+ Raises:
144
+ ValueError: 备份 ID 不存在
145
+ IOError: 恢复失败
146
+ """
147
+ # 检查备份是否存在
148
+ if backup_id not in self.backups:
149
+ raise ValueError(f"Backup not found: {backup_id}")
150
+
151
+ backup_info = self.backups[backup_id]
152
+
153
+ # 检查备份文件是否存在
154
+ if not os.path.exists(backup_info.backup_path):
155
+ raise IOError(f"Backup file not found: {backup_info.backup_path}")
156
+
157
+ # 恢复文件
158
+ try:
159
+ shutil.copy2(backup_info.backup_path, backup_info.original_path)
160
+ return True
161
+ except Exception as e:
162
+ raise IOError(f"Failed to restore backup: {e}")
163
+
164
+ def cleanup_backup(self, backup_id: str) -> bool:
165
+ """
166
+ 清理备份文件
167
+
168
+ Args:
169
+ backup_id: 备份 ID
170
+
171
+ Returns:
172
+ bool: 清理是否成功
173
+ """
174
+ # 检查备份是否存在
175
+ if backup_id not in self.backups:
176
+ return False
177
+
178
+ backup_info = self.backups[backup_id]
179
+
180
+ # 删除备份文件
181
+ try:
182
+ if os.path.exists(backup_info.backup_path):
183
+ os.remove(backup_info.backup_path)
184
+
185
+ # 从记录中移除
186
+ del self.backups[backup_id]
187
+
188
+ return True
189
+ except Exception:
190
+ return False
191
+
192
+ def list_backups(self, file_path: Optional[str] = None) -> List[BackupInfo]:
193
+ """
194
+ 列出备份
195
+
196
+ Args:
197
+ file_path: 原始文件路径(可选),如果提供则只列出该文件的备份
198
+
199
+ Returns:
200
+ List[BackupInfo]: 备份信息列表
201
+ """
202
+ if file_path:
203
+ # 只返回指定文件的备份
204
+ return [
205
+ info for info in self.backups.values()
206
+ if info.original_path == file_path
207
+ ]
208
+ else:
209
+ # 返回所有备份
210
+ return list(self.backups.values())
211
+
212
+ def cleanup_all_backups(self) -> int:
213
+ """
214
+ 清理所有备份
215
+
216
+ Returns:
217
+ int: 清理的备份数量
218
+ """
219
+ count = 0
220
+ backup_ids = list(self.backups.keys())
221
+
222
+ for backup_id in backup_ids:
223
+ if self.cleanup_backup(backup_id):
224
+ count += 1
225
+
226
+ return count
227
+
228
+ def get_backup_info(self, backup_id: str) -> Optional[BackupInfo]:
229
+ """
230
+ 获取备份信息
231
+
232
+ Args:
233
+ backup_id: 备份 ID
234
+
235
+ Returns:
236
+ Optional[BackupInfo]: 备份信息,如果不存在则返回 None
237
+ """
238
+ return self.backups.get(backup_id)
239
+
240
+
241
+ def main():
242
+ """测试备份管理器"""
243
+ import tempfile
244
+
245
+ # 创建临时文件
246
+ with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.md') as f:
247
+ f.write("# Test Document\n\nThis is a test.")
248
+ temp_file = f.name
249
+
250
+ print(f"Created test file: {temp_file}")
251
+
252
+ # 创建备份管理器
253
+ manager = BackupManager()
254
+
255
+ # 创建备份
256
+ print("\n1. Creating backup...")
257
+ backup_id = manager.create_backup(temp_file, reason="test")
258
+ print(f" Backup ID: {backup_id}")
259
+
260
+ # 列出备份
261
+ print("\n2. Listing backups...")
262
+ backups = manager.list_backups(temp_file)
263
+ for backup in backups:
264
+ print(f" - {backup.backup_id}")
265
+ print(f" Path: {backup.backup_path}")
266
+ print(f" Size: {backup.size_bytes} bytes")
267
+ print(f" Reason: {backup.reason}")
268
+
269
+ # 修改原文件
270
+ print("\n3. Modifying original file...")
271
+ with open(temp_file, 'w') as f:
272
+ f.write("# Modified Document\n\nThis has been modified.")
273
+
274
+ with open(temp_file, 'r') as f:
275
+ print(f" Content: {f.read()}")
276
+
277
+ # 恢复备份
278
+ print("\n4. Restoring backup...")
279
+ manager.restore_backup(backup_id)
280
+
281
+ with open(temp_file, 'r') as f:
282
+ print(f" Restored content: {f.read()}")
283
+
284
+ # 清理备份
285
+ print("\n5. Cleaning up backup...")
286
+ manager.cleanup_backup(backup_id)
287
+ print(f" Remaining backups: {len(manager.list_backups(temp_file))}")
288
+
289
+ # 清理临时文件
290
+ os.remove(temp_file)
291
+ print(f"\n6. Cleaned up test file")
292
+
293
+
294
+ if __name__ == '__main__':
295
+ main()
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Configuration Manager - 配置管理器
4
+
5
+ 管理 Ultrawork 增强器的配置
6
+ 注意:这是一个轻量级实现,基本配置功能已集成在 UltraworkEnhancerV3 中
7
+ """
8
+
9
+ import json
10
+ from dataclasses import dataclass, asdict
11
+ from pathlib import Path
12
+ from typing import Optional, Dict, Any
13
+
14
+
15
+ @dataclass
16
+ class UltraworkConfig:
17
+ """Ultrawork 配置数据结构"""
18
+ # 质量阈值
19
+ requirements_threshold: float = 9.0
20
+ design_threshold: float = 9.0
21
+ tasks_threshold: float = 8.0
22
+
23
+ # 收敛控制
24
+ max_iterations: int = 10
25
+ plateau_iterations: int = 3
26
+ min_improvement: float = 0.1
27
+
28
+ # 备份设置
29
+ create_backups: bool = True
30
+ cleanup_backups_on_success: bool = True
31
+ backup_retention_days: int = 7
32
+
33
+ # 日志设置
34
+ enable_logging: bool = True
35
+ log_file: Optional[str] = None
36
+ verbose: bool = False
37
+
38
+ # 评分权重 - Requirements
39
+ req_structure_weight: float = 0.20
40
+ req_ears_weight: float = 0.20
41
+ req_stories_weight: float = 0.20
42
+ req_criteria_weight: float = 0.20
43
+ req_nfr_weight: float = 0.10
44
+ req_constraints_weight: float = 0.10
45
+
46
+ # 评分权重 - Design
47
+ design_structure_weight: float = 0.25
48
+ design_traceability_weight: float = 0.20
49
+ design_components_weight: float = 0.20
50
+ design_diagrams_weight: float = 0.15
51
+ design_technology_weight: float = 0.10
52
+ design_nfr_weight: float = 0.05
53
+ design_interfaces_weight: float = 0.05
54
+
55
+
56
+ class ConfigurationManager:
57
+ """
58
+ 配置管理器
59
+
60
+ 支持项目级和 Spec 级配置,Spec 级配置优先
61
+ """
62
+
63
+ DEFAULT_PROJECT_CONFIG_PATH = ".kiro/ultrawork-config.json"
64
+ SPEC_CONFIG_FILENAME = "ultrawork-config.json"
65
+
66
+ def __init__(self):
67
+ """初始化配置管理器"""
68
+ self.config = UltraworkConfig()
69
+
70
+ def load_config(self, spec_path: Optional[str] = None) -> UltraworkConfig:
71
+ """
72
+ 加载配置
73
+
74
+ 优先级:Spec 级 > 项目级 > 默认值
75
+
76
+ Args:
77
+ spec_path: Spec 目录路径(可选)
78
+
79
+ Returns:
80
+ 配置对象
81
+ """
82
+ # 从默认值开始
83
+ config_dict = asdict(self.config)
84
+
85
+ # 加载项目级配置
86
+ project_config = self._load_project_config()
87
+ if project_config:
88
+ config_dict.update(project_config)
89
+
90
+ # 加载 Spec 级配置(如果提供)
91
+ if spec_path:
92
+ spec_config = self._load_spec_config(spec_path)
93
+ if spec_config:
94
+ config_dict.update(spec_config)
95
+
96
+ # 验证配置
97
+ config_dict = self._validate_config(config_dict)
98
+
99
+ # 创建配置对象
100
+ self.config = UltraworkConfig(**config_dict)
101
+ return self.config
102
+
103
+ def save_config(self, config: UltraworkConfig,
104
+ spec_path: Optional[str] = None) -> bool:
105
+ """
106
+ 保存配置
107
+
108
+ Args:
109
+ config: 配置对象
110
+ spec_path: Spec 目录路径(可选,如果提供则保存为 Spec 级配置)
111
+
112
+ Returns:
113
+ 是否成功
114
+ """
115
+ try:
116
+ config_dict = asdict(config)
117
+
118
+ if spec_path:
119
+ # 保存为 Spec 级配置
120
+ config_path = Path(spec_path) / self.SPEC_CONFIG_FILENAME
121
+ else:
122
+ # 保存为项目级配置
123
+ config_path = Path(self.DEFAULT_PROJECT_CONFIG_PATH)
124
+
125
+ config_path.parent.mkdir(parents=True, exist_ok=True)
126
+
127
+ with open(config_path, 'w', encoding='utf-8') as f:
128
+ json.dump(config_dict, f, indent=2, ensure_ascii=False)
129
+
130
+ return True
131
+
132
+ except Exception as e:
133
+ print(f"Error saving config: {e}")
134
+ return False
135
+
136
+ def _load_project_config(self) -> Optional[Dict[str, Any]]:
137
+ """加载项目级配置"""
138
+ config_path = Path(self.DEFAULT_PROJECT_CONFIG_PATH)
139
+
140
+ if not config_path.exists():
141
+ return None
142
+
143
+ try:
144
+ with open(config_path, 'r', encoding='utf-8') as f:
145
+ return json.load(f)
146
+ except Exception as e:
147
+ print(f"Warning: Failed to load project config: {e}")
148
+ return None
149
+
150
+ def _load_spec_config(self, spec_path: str) -> Optional[Dict[str, Any]]:
151
+ """加载 Spec 级配置"""
152
+ config_path = Path(spec_path) / self.SPEC_CONFIG_FILENAME
153
+
154
+ if not config_path.exists():
155
+ return None
156
+
157
+ try:
158
+ with open(config_path, 'r', encoding='utf-8') as f:
159
+ return json.load(f)
160
+ except Exception as e:
161
+ print(f"Warning: Failed to load spec config: {e}")
162
+ return None
163
+
164
+ def _validate_config(self, config_dict: Dict[str, Any]) -> Dict[str, Any]:
165
+ """
166
+ 验证配置值
167
+
168
+ Args:
169
+ config_dict: 配置字典
170
+
171
+ Returns:
172
+ 验证后的配置字典
173
+ """
174
+ # 验证阈值范围 (0-10)
175
+ for key in ['requirements_threshold', 'design_threshold', 'tasks_threshold']:
176
+ if key in config_dict:
177
+ config_dict[key] = max(0.0, min(10.0, config_dict[key]))
178
+
179
+ # 验证迭代次数 (1-100)
180
+ if 'max_iterations' in config_dict:
181
+ config_dict['max_iterations'] = max(1, min(100, config_dict['max_iterations']))
182
+
183
+ if 'plateau_iterations' in config_dict:
184
+ config_dict['plateau_iterations'] = max(1, min(10, config_dict['plateau_iterations']))
185
+
186
+ # 验证权重总和 = 1.0
187
+ req_weights = [
188
+ 'req_structure_weight', 'req_ears_weight', 'req_stories_weight',
189
+ 'req_criteria_weight', 'req_nfr_weight', 'req_constraints_weight'
190
+ ]
191
+ if all(k in config_dict for k in req_weights):
192
+ total = sum(config_dict[k] for k in req_weights)
193
+ if abs(total - 1.0) > 0.01:
194
+ print(f"Warning: Requirements weights sum to {total:.2f}, normalizing to 1.0")
195
+ for k in req_weights:
196
+ config_dict[k] /= total
197
+
198
+ design_weights = [
199
+ 'design_structure_weight', 'design_traceability_weight',
200
+ 'design_components_weight', 'design_diagrams_weight',
201
+ 'design_technology_weight', 'design_nfr_weight', 'design_interfaces_weight'
202
+ ]
203
+ if all(k in config_dict for k in design_weights):
204
+ total = sum(config_dict[k] for k in design_weights)
205
+ if abs(total - 1.0) > 0.01:
206
+ print(f"Warning: Design weights sum to {total:.2f}, normalizing to 1.0")
207
+ for k in design_weights:
208
+ config_dict[k] /= total
209
+
210
+ return config_dict
211
+
212
+ def get_default_config(self) -> UltraworkConfig:
213
+ """获取默认配置"""
214
+ return UltraworkConfig()
215
+
216
+
217
+ # 注意:基本配置功能已集成在 ultrawork_enhancer_v3.py 的 UltraworkEnhancerV3 类中
218
+ # 本类提供更完整的配置文件管理,可选使用