devsquad 3.6.0__py3-none-any.whl
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.
- devsquad-3.6.0.dist-info/METADATA +944 -0
- devsquad-3.6.0.dist-info/RECORD +95 -0
- devsquad-3.6.0.dist-info/WHEEL +5 -0
- devsquad-3.6.0.dist-info/entry_points.txt +2 -0
- devsquad-3.6.0.dist-info/licenses/LICENSE +21 -0
- devsquad-3.6.0.dist-info/top_level.txt +2 -0
- scripts/__init__.py +0 -0
- scripts/ai_semantic_matcher.py +512 -0
- scripts/alert_manager.py +505 -0
- scripts/api/__init__.py +43 -0
- scripts/api/models.py +386 -0
- scripts/api/routes/__init__.py +20 -0
- scripts/api/routes/dispatch.py +348 -0
- scripts/api/routes/lifecycle.py +330 -0
- scripts/api/routes/metrics_gates.py +347 -0
- scripts/api_server.py +318 -0
- scripts/auth.py +451 -0
- scripts/cli/__init__.py +1 -0
- scripts/cli/cli_visual.py +642 -0
- scripts/cli.py +1094 -0
- scripts/collaboration/__init__.py +212 -0
- scripts/collaboration/_version.py +1 -0
- scripts/collaboration/agent_briefing.py +656 -0
- scripts/collaboration/ai_semantic_matcher.py +260 -0
- scripts/collaboration/anchor_checker.py +281 -0
- scripts/collaboration/anti_rationalization.py +470 -0
- scripts/collaboration/async_integration_example.py +255 -0
- scripts/collaboration/batch_scheduler.py +149 -0
- scripts/collaboration/checkpoint_manager.py +561 -0
- scripts/collaboration/ci_feedback_adapter.py +351 -0
- scripts/collaboration/code_map_generator.py +247 -0
- scripts/collaboration/concern_pack_loader.py +352 -0
- scripts/collaboration/confidence_score.py +496 -0
- scripts/collaboration/config_loader.py +188 -0
- scripts/collaboration/consensus.py +244 -0
- scripts/collaboration/context_compressor.py +533 -0
- scripts/collaboration/coordinator.py +668 -0
- scripts/collaboration/dispatcher.py +1636 -0
- scripts/collaboration/dual_layer_context.py +128 -0
- scripts/collaboration/enhanced_worker.py +539 -0
- scripts/collaboration/feature_usage_tracker.py +206 -0
- scripts/collaboration/five_axis_consensus.py +334 -0
- scripts/collaboration/input_validator.py +401 -0
- scripts/collaboration/integration_example.py +287 -0
- scripts/collaboration/intent_workflow_mapper.py +350 -0
- scripts/collaboration/language_parsers.py +269 -0
- scripts/collaboration/lifecycle_protocol.py +1446 -0
- scripts/collaboration/llm_backend.py +453 -0
- scripts/collaboration/llm_cache.py +448 -0
- scripts/collaboration/llm_cache_async.py +347 -0
- scripts/collaboration/llm_retry.py +387 -0
- scripts/collaboration/llm_retry_async.py +389 -0
- scripts/collaboration/mce_adapter.py +597 -0
- scripts/collaboration/memory_bridge.py +1607 -0
- scripts/collaboration/models.py +537 -0
- scripts/collaboration/null_providers.py +297 -0
- scripts/collaboration/operation_classifier.py +289 -0
- scripts/collaboration/output_slicer.py +225 -0
- scripts/collaboration/performance_monitor.py +462 -0
- scripts/collaboration/permission_guard.py +865 -0
- scripts/collaboration/prompt_assembler.py +756 -0
- scripts/collaboration/prompt_variant_generator.py +483 -0
- scripts/collaboration/protocols.py +267 -0
- scripts/collaboration/report_formatter.py +352 -0
- scripts/collaboration/retrospective.py +279 -0
- scripts/collaboration/role_matcher.py +92 -0
- scripts/collaboration/role_template_market.py +352 -0
- scripts/collaboration/rule_collector.py +678 -0
- scripts/collaboration/scratchpad.py +346 -0
- scripts/collaboration/skill_registry.py +151 -0
- scripts/collaboration/skillifier.py +878 -0
- scripts/collaboration/standardized_role_template.py +317 -0
- scripts/collaboration/task_completion_checker.py +237 -0
- scripts/collaboration/test_quality_guard.py +695 -0
- scripts/collaboration/unified_gate_engine.py +598 -0
- scripts/collaboration/usage_tracker.py +309 -0
- scripts/collaboration/user_friendly_error.py +176 -0
- scripts/collaboration/verification_gate.py +312 -0
- scripts/collaboration/warmup_manager.py +635 -0
- scripts/collaboration/worker.py +513 -0
- scripts/collaboration/workflow_engine.py +684 -0
- scripts/dashboard.py +1088 -0
- scripts/generate_benchmark_report.py +786 -0
- scripts/history_manager.py +604 -0
- scripts/mcp_server.py +289 -0
- skills/__init__.py +32 -0
- skills/dispatch/handler.py +52 -0
- skills/intent/handler.py +59 -0
- skills/registry.py +67 -0
- skills/retrospective/__init__.py +0 -0
- skills/retrospective/handler.py +125 -0
- skills/review/handler.py +356 -0
- skills/security/handler.py +454 -0
- skills/test/__init__.py +0 -0
- skills/test/handler.py +78 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
UsageTracker - 轻量级功能使用追踪系统
|
|
5
|
+
|
|
6
|
+
用于追踪 DevSquad 各组件的使用情况,为优化决策提供数据支持。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import threading
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, Any, Optional, List, Tuple
|
|
14
|
+
from collections import defaultdict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UsageTracker:
|
|
18
|
+
"""轻量级功能使用追踪器
|
|
19
|
+
|
|
20
|
+
特性:
|
|
21
|
+
- 线程安全的使用统计
|
|
22
|
+
- 自动持久化到文件
|
|
23
|
+
- 支持错误追踪
|
|
24
|
+
- 生成使用报告
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, persist_file: Optional[str] = None):
|
|
28
|
+
"""初始化追踪器
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
persist_file: 持久化文件路径,默认为 .usage_stats.json
|
|
32
|
+
"""
|
|
33
|
+
self.stats: Dict[str, Dict[str, Any]] = defaultdict(lambda: {
|
|
34
|
+
"count": 0,
|
|
35
|
+
"first_used": None,
|
|
36
|
+
"last_used": None,
|
|
37
|
+
"errors": 0,
|
|
38
|
+
})
|
|
39
|
+
self.persist_file = persist_file or ".usage_stats.json"
|
|
40
|
+
self._lock = threading.RLock()
|
|
41
|
+
self._load_stats()
|
|
42
|
+
|
|
43
|
+
def track(self, feature_name: str, success: bool = True,
|
|
44
|
+
metadata: Optional[Dict] = None) -> None:
|
|
45
|
+
"""追踪功能使用
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
feature_name: 功能名称,格式如 "dispatcher.dispatch"
|
|
49
|
+
success: 是否成功执行
|
|
50
|
+
metadata: 额外的元数据(可选)
|
|
51
|
+
"""
|
|
52
|
+
with self._lock:
|
|
53
|
+
stat = self.stats[feature_name]
|
|
54
|
+
stat["count"] += 1
|
|
55
|
+
|
|
56
|
+
now = datetime.now().isoformat()
|
|
57
|
+
if stat["first_used"] is None:
|
|
58
|
+
stat["first_used"] = now
|
|
59
|
+
stat["last_used"] = now
|
|
60
|
+
|
|
61
|
+
if not success:
|
|
62
|
+
stat["errors"] += 1
|
|
63
|
+
|
|
64
|
+
if metadata:
|
|
65
|
+
if "metadata" not in stat:
|
|
66
|
+
stat["metadata"] = []
|
|
67
|
+
stat["metadata"].append(metadata)
|
|
68
|
+
# 只保留最近 10 条元数据
|
|
69
|
+
stat["metadata"] = stat["metadata"][-10:]
|
|
70
|
+
|
|
71
|
+
def get_stats(self, feature_name: Optional[str] = None) -> Dict:
|
|
72
|
+
"""获取统计数据
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
feature_name: 功能名称,None 表示获取所有统计
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
统计数据字典
|
|
79
|
+
"""
|
|
80
|
+
with self._lock:
|
|
81
|
+
if feature_name:
|
|
82
|
+
return dict(self.stats.get(feature_name, {}))
|
|
83
|
+
return {k: dict(v) for k, v in self.stats.items()}
|
|
84
|
+
|
|
85
|
+
def get_top_features(self, limit: int = 10) -> List[Tuple[str, int]]:
|
|
86
|
+
"""获取使用最多的功能
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
limit: 返回的功能数量
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
[(功能名, 调用次数), ...] 列表
|
|
93
|
+
"""
|
|
94
|
+
with self._lock:
|
|
95
|
+
sorted_features = sorted(
|
|
96
|
+
self.stats.items(),
|
|
97
|
+
key=lambda x: x[1]["count"],
|
|
98
|
+
reverse=True
|
|
99
|
+
)
|
|
100
|
+
return [(name, stats["count"]) for name, stats in sorted_features[:limit]]
|
|
101
|
+
|
|
102
|
+
def get_unused_features(self, all_features: List[str]) -> List[str]:
|
|
103
|
+
"""获取从未使用的功能
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
all_features: 所有功能名称列表
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
未使用的功能名称列表
|
|
110
|
+
"""
|
|
111
|
+
with self._lock:
|
|
112
|
+
used = set(self.stats.keys())
|
|
113
|
+
return [f for f in all_features if f not in used]
|
|
114
|
+
|
|
115
|
+
def get_error_prone_features(self, min_calls: int = 5,
|
|
116
|
+
error_threshold: float = 0.1) -> List[Tuple[str, float]]:
|
|
117
|
+
"""获取高错误率的功能
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
min_calls: 最小调用次数阈值
|
|
121
|
+
error_threshold: 错误率阈值(0.1 = 10%)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
[(功能名] 列表
|
|
125
|
+
"""
|
|
126
|
+
with self._lock:
|
|
127
|
+
error_prone = []
|
|
128
|
+
for name, stat in self.stats.items():
|
|
129
|
+
if stat["count"] >= min_calls:
|
|
130
|
+
error_rate = stat["errors"] / stat["count"]
|
|
131
|
+
if error_rate >= error_threshold:
|
|
132
|
+
error_prone.append((name, error_rate))
|
|
133
|
+
return sorted(error_prone, key=lambda x: x[1], reverse=True)
|
|
134
|
+
|
|
135
|
+
def generate_report(self) -> str:
|
|
136
|
+
"""生成使用报告
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Markdown 格式的报告文本
|
|
140
|
+
"""
|
|
141
|
+
with self._lock:
|
|
142
|
+
total_calls = sum(s["count"] for s in self.stats.values())
|
|
143
|
+
total_errors = sum(s["errors"] for s in self.stats.values())
|
|
144
|
+
|
|
145
|
+
lines = [
|
|
146
|
+
"# DevSquad 功能使用报告",
|
|
147
|
+
f"\n**生成时间**: {datetime.now().isoformat()}",
|
|
148
|
+
f"**追踪功能数**: {len(self.stats)}",
|
|
149
|
+
f"**总调用次数**: {total_calls:,}",
|
|
150
|
+
f"**总错误次数**: {total_errors}",
|
|
151
|
+
f"**总体错误率**: {(total_errors/max(1,total_calls)*100):.2f}%",
|
|
152
|
+
"\n## Top 10 最常用功能\n",
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
for name, count in self.get_top_features(10):
|
|
156
|
+
stat = self.stats[name]
|
|
157
|
+
error_rate = (stat["errors"] / max(1, stat["count"])) * 100
|
|
158
|
+
lines.append(f"- **{name}**: {count:,} 次调用, 错误率 {error_rate:.1f}%")
|
|
159
|
+
|
|
160
|
+
# 按组件分类
|
|
161
|
+
lines.append("\n## 按组件分类\n")
|
|
162
|
+
by_component = defaultdict(int)
|
|
163
|
+
for name, stat in self.stats.items():
|
|
164
|
+
component = name.split(".")[0] if "." in name else "other"
|
|
165
|
+
by_component[component] += stat["count"]
|
|
166
|
+
|
|
167
|
+
for component, count in sorted(by_component.items(),
|
|
168
|
+
key=lambda x: x[1], reverse=True):
|
|
169
|
+
pct = (count / max(1, total_calls)) * 100
|
|
170
|
+
lines.append(f"- **{component}**: {count:,} 次调用 ({pct:.1f}%)")
|
|
171
|
+
|
|
172
|
+
# 高错误率功能
|
|
173
|
+
error_prone = self.get_error_prone_features()
|
|
174
|
+
if error_prone:
|
|
175
|
+
lines.append("\n## ⚠️ 高错误率功能\n")
|
|
176
|
+
for name, error_rate in error_prone[:5]:
|
|
177
|
+
stat = self.stats[name]
|
|
178
|
+
lines.append(
|
|
179
|
+
f"- **{name}**: 错误率 {error_rate*100:.1f}% "
|
|
180
|
+
f"({stat['errors']}/{stat['count']} 次调用)"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# 使用频率分布
|
|
184
|
+
lines.append("\n## 使用频率分布\n")
|
|
185
|
+
freq_buckets = {
|
|
186
|
+
"高频 (>100次)": 0,
|
|
187
|
+
"中频 (10-100次)": 0,
|
|
188
|
+
"低频 (1-10次)": 0,
|
|
189
|
+
}
|
|
190
|
+
for stat in self.stats.values():
|
|
191
|
+
count = stat["count"]
|
|
192
|
+
if count > 100:
|
|
193
|
+
freq_buckets["高频 (>100次)"] += 1
|
|
194
|
+
elif count >= 10:
|
|
195
|
+
freq_buckets["中频 (10-100次)"] += 1
|
|
196
|
+
else:
|
|
197
|
+
freq_buckets["低频 (1-10次)"] += 1
|
|
198
|
+
|
|
199
|
+
for bucket, num in freq_buckets.items():
|
|
200
|
+
pct = (num / max(1, len(self.stats))) * 100
|
|
201
|
+
lines.append(f"- {bucket}: {num} 个功能 ({pct:.1f}%)")
|
|
202
|
+
|
|
203
|
+
return "\n".join(lines)
|
|
204
|
+
|
|
205
|
+
def save(self) -> bool:
|
|
206
|
+
"""保存统计数据到文件
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
是否保存成功
|
|
210
|
+
"""
|
|
211
|
+
with self._lock:
|
|
212
|
+
try:
|
|
213
|
+
with open(self.persist_file, 'w', encoding='utf-8') as f:
|
|
214
|
+
json.dump(dict(self.stats), f, indent=2, ensure_ascii=False)
|
|
215
|
+
return True
|
|
216
|
+
except Exception as e:
|
|
217
|
+
print(f"Failed to save usage stats: {e}")
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
def _load_stats(self) -> None:
|
|
221
|
+
"""从文件加载统计数据"""
|
|
222
|
+
try:
|
|
223
|
+
if Path(self.persist_file).exists():
|
|
224
|
+
with open(self.persist_file, 'r', encoding='utf-8') as f:
|
|
225
|
+
loaded = json.load(f)
|
|
226
|
+
self.stats.update(loaded)
|
|
227
|
+
except Exception as e:
|
|
228
|
+
print(f"Failed to load usage stats: {e}")
|
|
229
|
+
|
|
230
|
+
def clear(self) -> int:
|
|
231
|
+
"""清空所有统计数据
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
清空的功能数量
|
|
235
|
+
"""
|
|
236
|
+
with self._lock:
|
|
237
|
+
count = len(self.stats)
|
|
238
|
+
self.stats.clear()
|
|
239
|
+
return count
|
|
240
|
+
|
|
241
|
+
def export_json(self) -> str:
|
|
242
|
+
"""导出为 JSON 字符串
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
JSON 格式的统计数据
|
|
246
|
+
"""
|
|
247
|
+
with self._lock:
|
|
248
|
+
return json.dumps(dict(self.stats), indent=2, ensure_ascii=False)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# 全局单例
|
|
252
|
+
_global_tracker: Optional[UsageTracker] = None
|
|
253
|
+
_tracker_lock = threading.Lock()
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def get_tracker() -> UsageTracker:
|
|
257
|
+
"""获取全局追踪器实例(线程安全)
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
全局 UsageTracker 实例
|
|
261
|
+
"""
|
|
262
|
+
global _global_tracker
|
|
263
|
+
if _global_tracker is None:
|
|
264
|
+
with _tracker_lock:
|
|
265
|
+
if _global_tracker is None:
|
|
266
|
+
_global_tracker = UsageTracker()
|
|
267
|
+
return _global_tracker
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def track_usage(feature_name: str, success: bool = True,
|
|
271
|
+
metadata: Optional[Dict] = None) -> None:
|
|
272
|
+
"""便捷函数:追踪功能使用
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
feature_name: 功能名称
|
|
276
|
+
success: 是否成功
|
|
277
|
+
metadata: 元数据
|
|
278
|
+
"""
|
|
279
|
+
get_tracker().track(feature_name, success, metadata)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_usage_stats(feature_name: Optional[str] = None) -> Dict:
|
|
283
|
+
"""便捷函数:获取统计数据
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
feature_name: 功能名称,None 表示获取所有
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
统计数据字典
|
|
290
|
+
"""
|
|
291
|
+
return get_tracker().get_stats(feature_name)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def generate_usage_report() -> str:
|
|
295
|
+
"""便捷函数:生成使用报告
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
Markdown 格式的报告
|
|
299
|
+
"""
|
|
300
|
+
return get_tracker().generate_report()
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def save_usage_stats() -> bool:
|
|
304
|
+
"""便捷函数:保存统计数据
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
是否保存成功
|
|
308
|
+
"""
|
|
309
|
+
return get_tracker().save()
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
User-Friendly Error System — 用户友好的错误提示
|
|
4
|
+
|
|
5
|
+
将技术性错误信息翻译为人类可读的提示,包含:
|
|
6
|
+
- 友好的错误描述
|
|
7
|
+
- 修复建议
|
|
8
|
+
- 使用示例
|
|
9
|
+
|
|
10
|
+
Author: DevSquad Team
|
|
11
|
+
Version: 1.0
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UserFriendlyError(Exception):
|
|
18
|
+
"""
|
|
19
|
+
用户友好的错误提示
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
message: 人类可读的错误描述
|
|
23
|
+
suggestion: 修复建议
|
|
24
|
+
example: 使用示例
|
|
25
|
+
original_error: 原始技术错误(可选)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
message: str,
|
|
31
|
+
suggestion: Optional[str] = None,
|
|
32
|
+
example: Optional[str] = None,
|
|
33
|
+
original_error: Optional[Exception] = None,
|
|
34
|
+
):
|
|
35
|
+
self.message = message
|
|
36
|
+
self.suggestion = suggestion
|
|
37
|
+
self.example = example
|
|
38
|
+
self.original_error = original_error
|
|
39
|
+
super().__init__(self.format())
|
|
40
|
+
|
|
41
|
+
def format(self) -> str:
|
|
42
|
+
parts = [f"❌ {self.message}"]
|
|
43
|
+
if self.suggestion:
|
|
44
|
+
parts.append(f"💡 {self.suggestion}")
|
|
45
|
+
if self.example:
|
|
46
|
+
parts.append(f"📝 示例: {self.example}")
|
|
47
|
+
return "\n".join(parts)
|
|
48
|
+
|
|
49
|
+
def to_dict(self) -> dict:
|
|
50
|
+
return {
|
|
51
|
+
"error": self.message,
|
|
52
|
+
"suggestion": self.suggestion,
|
|
53
|
+
"example": self.example,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
ERROR_TEMPLATES = {
|
|
58
|
+
"task_too_short": {
|
|
59
|
+
"message": "任务描述太短了,请详细说明你想做什么",
|
|
60
|
+
"suggestion": "好的任务描述应该包含:做什么 + 为什么 + 有什么特殊要求",
|
|
61
|
+
"example": "devsquad dispatch -t '设计一个支持手机号和邮箱登录的用户认证系统'",
|
|
62
|
+
},
|
|
63
|
+
"task_too_long": {
|
|
64
|
+
"message": "任务描述太长了,请精简到10000字以内",
|
|
65
|
+
"suggestion": "把核心需求说清楚即可,细节可以在后续对话中补充",
|
|
66
|
+
"example": "devsquad dispatch -t '优化数据库查询性能,重点关注慢查询和索引'",
|
|
67
|
+
},
|
|
68
|
+
"task_empty": {
|
|
69
|
+
"message": "请输入任务描述",
|
|
70
|
+
"suggestion": "告诉 DevSquad 你想做什么,它会自动匹配合适的AI角色来帮你",
|
|
71
|
+
"example": "devsquad dispatch -t '设计用户权限系统'",
|
|
72
|
+
},
|
|
73
|
+
"task_invalid_type": {
|
|
74
|
+
"message": "任务描述需要是文字内容",
|
|
75
|
+
"suggestion": "请用自然语言描述你的任务,中文或英文都可以",
|
|
76
|
+
"example": "devsquad dispatch -t 'Review the authentication module for security issues'",
|
|
77
|
+
},
|
|
78
|
+
"task_forbidden_content": {
|
|
79
|
+
"message": "任务描述包含不允许的内容",
|
|
80
|
+
"suggestion": "请移除HTML标签、脚本代码或SQL语句,使用纯文本描述任务",
|
|
81
|
+
"example": "devsquad dispatch -t '实现用户注册功能'",
|
|
82
|
+
},
|
|
83
|
+
"task_injection_detected": {
|
|
84
|
+
"message": "检测到异常的指令模式,请正常描述你的任务",
|
|
85
|
+
"suggestion": "直接描述你想完成的项目任务即可,不需要特殊的指令格式",
|
|
86
|
+
"example": "devsquad dispatch -t '搭建微服务架构的订单系统'",
|
|
87
|
+
},
|
|
88
|
+
"role_not_found": {
|
|
89
|
+
"message": "找不到指定的角色",
|
|
90
|
+
"suggestion": "使用 'devsquad roles' 查看所有可用角色,或省略 -r 参数让系统自动匹配",
|
|
91
|
+
"example": "devsquad dispatch -t '设计系统架构' -r architect security",
|
|
92
|
+
},
|
|
93
|
+
"too_many_roles": {
|
|
94
|
+
"message": "角色数量太多了(最多10个)",
|
|
95
|
+
"suggestion": "精选最核心的2-4个角色,或省略 -r 让系统自动匹配",
|
|
96
|
+
"example": "devsquad dispatch -t '设计安全系统' -r arch sec",
|
|
97
|
+
},
|
|
98
|
+
"backend_unavailable": {
|
|
99
|
+
"message": "AI后端不可用",
|
|
100
|
+
"suggestion": "检查API Key是否设置正确,或使用Mock模式测试",
|
|
101
|
+
"example": "export OPENAI_API_KEY='your-key' && devsquad dispatch -t '...'",
|
|
102
|
+
},
|
|
103
|
+
"config_invalid": {
|
|
104
|
+
"message": "配置文件格式有误",
|
|
105
|
+
"suggestion": "运行 'devsquad init' 重新生成配置文件",
|
|
106
|
+
"example": "devsquad init",
|
|
107
|
+
},
|
|
108
|
+
"dispatch_failed": {
|
|
109
|
+
"message": "任务调度失败",
|
|
110
|
+
"suggestion": "请检查任务描述是否清晰,或尝试简化任务后重试",
|
|
111
|
+
"example": "devsquad dispatch -t '实现用户登录功能'",
|
|
112
|
+
},
|
|
113
|
+
"path_traversal": {
|
|
114
|
+
"message": "检测到非法路径",
|
|
115
|
+
"suggestion": "请使用正常的标识符,不要包含路径分隔符",
|
|
116
|
+
"example": "devsquad dispatch -t '查看项目状态'",
|
|
117
|
+
},
|
|
118
|
+
"unknown_template": {
|
|
119
|
+
"message": "找不到指定的生命周期模板",
|
|
120
|
+
"suggestion": "使用 'devsquad lifecycle list' 查看可用模板",
|
|
121
|
+
"example": "devsquad spec -t '需求分析'",
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def make_user_friendly_error(error_key: str, **kwargs) -> UserFriendlyError:
|
|
127
|
+
"""
|
|
128
|
+
根据错误键创建用户友好的错误
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
error_key: 错误模板键
|
|
132
|
+
**kwargs: 额外参数(如 original_error)
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
UserFriendlyError 实例
|
|
136
|
+
"""
|
|
137
|
+
template = ERROR_TEMPLATES.get(error_key, {})
|
|
138
|
+
return UserFriendlyError(
|
|
139
|
+
message=template.get("message", f"操作失败 ({error_key})"),
|
|
140
|
+
suggestion=template.get("suggestion"),
|
|
141
|
+
example=template.get("example"),
|
|
142
|
+
original_error=kwargs.get("original_error"),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def translate_validation_result(reason: str, original: Optional[Exception] = None) -> UserFriendlyError:
|
|
147
|
+
"""
|
|
148
|
+
将 InputValidator 的 ValidationResult.reason 翻译为用户友好的错误
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
reason: InputValidator 返回的原始原因
|
|
152
|
+
original: 原始异常
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
UserFriendlyError 实例
|
|
156
|
+
"""
|
|
157
|
+
reason_lower = reason.lower() if reason else ""
|
|
158
|
+
|
|
159
|
+
if "too short" in reason_lower or "min" in reason_lower:
|
|
160
|
+
return make_user_friendly_error("task_too_short", original_error=original)
|
|
161
|
+
elif "too long" in reason_lower or "max" in reason_lower:
|
|
162
|
+
return make_user_friendly_error("task_too_long", original_error=original)
|
|
163
|
+
elif "empty" in reason_lower or "whitespace" in reason_lower:
|
|
164
|
+
return make_user_friendly_error("task_empty", original_error=original)
|
|
165
|
+
elif "string" in reason_lower or "type" in reason_lower:
|
|
166
|
+
return make_user_friendly_error("task_invalid_type", original_error=original)
|
|
167
|
+
elif "forbidden" in reason_lower:
|
|
168
|
+
return make_user_friendly_error("task_forbidden_content", original_error=original)
|
|
169
|
+
elif "injection" in reason_lower or "prompt" in reason_lower:
|
|
170
|
+
return make_user_friendly_error("task_injection_detected", original_error=original)
|
|
171
|
+
else:
|
|
172
|
+
return UserFriendlyError(
|
|
173
|
+
message=f"输入验证失败: {reason}",
|
|
174
|
+
suggestion="请检查输入内容后重试",
|
|
175
|
+
original_error=original,
|
|
176
|
+
)
|