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
scripts/mcp_server.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DevSquad MCP (Model Context Protocol) Server — For OpenClaw / Claude Code Tool Integration.
|
|
3
|
+
|
|
4
|
+
This server exposes MultiAgentDispatcher capabilities as MCP tools, enabling
|
|
5
|
+
any MCP-compatible AI agent (OpenClaw, Claude Code, Cursor, etc.) to invoke
|
|
6
|
+
multi-agent collaboration directly.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python scripts/mcp_server.py # Start stdio transport (default)
|
|
10
|
+
python scripts/mcp_server.py --port 8080 # Start SSE transport
|
|
11
|
+
|
|
12
|
+
MCP Tools Exposed:
|
|
13
|
+
1. multiagent_dispatch — Execute a multi-agent collaboration task
|
|
14
|
+
2. multiagent_quick — Quick dispatch with format options
|
|
15
|
+
3. multiagent_roles — List available roles
|
|
16
|
+
4. multiagent_status — System status and capabilities
|
|
17
|
+
5. multiagent_analyze — Analyze task intent (dry-run)
|
|
18
|
+
|
|
19
|
+
Dependencies (optional, graceful fallback):
|
|
20
|
+
pip install mcp # For MCP protocol support
|
|
21
|
+
"""
|
|
22
|
+
import json
|
|
23
|
+
import sys
|
|
24
|
+
import os
|
|
25
|
+
import logging
|
|
26
|
+
from typing import Any, Dict, List, Optional
|
|
27
|
+
|
|
28
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
|
29
|
+
|
|
30
|
+
logging.basicConfig(level=logging.WARNING, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
31
|
+
logger = logging.getLogger("DevSquad-MCP")
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
from scripts.collaboration.input_validator import InputValidator
|
|
35
|
+
_validator = InputValidator()
|
|
36
|
+
except ImportError:
|
|
37
|
+
_validator = None
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from mcp.server.fastmcp import FastMCP
|
|
41
|
+
MCP_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
MCP_AVAILABLE = False
|
|
44
|
+
logger.warning("MCP SDK not installed. Run: pip install mcp")
|
|
45
|
+
|
|
46
|
+
from scripts.collaboration.dispatcher import MultiAgentDispatcher
|
|
47
|
+
from scripts.collaboration.permission_guard import PermissionLevel
|
|
48
|
+
from scripts.collaboration.models import ROLE_REGISTRY
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DevSquadMCPServer:
|
|
52
|
+
"""MCP Server wrapper for DevSquad."""
|
|
53
|
+
|
|
54
|
+
def __init__(self):
|
|
55
|
+
self._dispatcher: Optional[MultiAgentDispatcher] = None
|
|
56
|
+
|
|
57
|
+
def _get_dispatcher(self, **kwargs) -> MultiAgentDispatcher:
|
|
58
|
+
"""Lazy-init dispatcher with caching."""
|
|
59
|
+
if self._dispatcher is None:
|
|
60
|
+
self._dispatcher = MultiAgentDispatcher(**kwargs)
|
|
61
|
+
return self._dispatcher
|
|
62
|
+
|
|
63
|
+
def shutdown(self):
|
|
64
|
+
"""Clean up dispatcher."""
|
|
65
|
+
if self._dispatcher:
|
|
66
|
+
self._dispatcher.shutdown()
|
|
67
|
+
self._dispatcher = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def create_mcp_server() -> "FastMCP":
|
|
71
|
+
"""Create and configure the MCP server with all tools."""
|
|
72
|
+
if not MCP_AVAILABLE:
|
|
73
|
+
raise ImportError("MCP SDK not installed. Run: pip install mcp")
|
|
74
|
+
|
|
75
|
+
mcp = FastMCP("DevSquad")
|
|
76
|
+
server = DevSquadMCPServer()
|
|
77
|
+
|
|
78
|
+
@mcp.tool()
|
|
79
|
+
def multiagent_dispatch(
|
|
80
|
+
task: str,
|
|
81
|
+
roles: Optional[List[str]] = None,
|
|
82
|
+
mode: str = "auto",
|
|
83
|
+
output_format: str = "markdown",
|
|
84
|
+
dry_run: bool = False,
|
|
85
|
+
) -> str:
|
|
86
|
+
"""
|
|
87
|
+
Execute a full multi-agent collaboration task.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
task: The task description to collaborate on.
|
|
91
|
+
roles: Optional list of roles (arch/pm/sec/test/coder/infra/ui).
|
|
92
|
+
If omitted, auto-matches based on task intent (supports CN+EN keywords).
|
|
93
|
+
mode: Execution mode — 'auto'(default), 'parallel', 'sequential', or 'consensus'.
|
|
94
|
+
output_format: Output format — 'markdown'(default), 'json', or 'compact'.
|
|
95
|
+
dry_run: If True, only analyze without execution.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Markdown or JSON formatted collaboration result with findings,
|
|
99
|
+
conflicts resolution, and action items.
|
|
100
|
+
"""
|
|
101
|
+
disp = server._get_dispatcher()
|
|
102
|
+
if _validator:
|
|
103
|
+
vresult = _validator.validate_task(task)
|
|
104
|
+
if not vresult.valid:
|
|
105
|
+
return json.dumps({"error": f"Invalid task: {vresult.reason}", "success": False})
|
|
106
|
+
task = vresult.sanitized_input or task
|
|
107
|
+
try:
|
|
108
|
+
result = disp.dispatch(
|
|
109
|
+
task=task,
|
|
110
|
+
roles=roles,
|
|
111
|
+
mode=mode,
|
|
112
|
+
dry_run=dry_run,
|
|
113
|
+
)
|
|
114
|
+
if output_format == "json":
|
|
115
|
+
return json.dumps({
|
|
116
|
+
"success": result.success,
|
|
117
|
+
"matched_roles": getattr(result, 'matched_roles', None),
|
|
118
|
+
"summary": result.summary,
|
|
119
|
+
"report": result.to_markdown(),
|
|
120
|
+
}, ensure_ascii=False, indent=2)
|
|
121
|
+
elif output_format == "compact":
|
|
122
|
+
return result.summary
|
|
123
|
+
return result.to_markdown()
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.error(f"Dispatch error: {e}", exc_info=True)
|
|
126
|
+
return json.dumps({"error": "Internal error occurred", "success": False})
|
|
127
|
+
|
|
128
|
+
@mcp.tool()
|
|
129
|
+
def multiagent_quick(
|
|
130
|
+
task: str,
|
|
131
|
+
output_format: str = "structured",
|
|
132
|
+
include_action_items: bool = True,
|
|
133
|
+
include_timing: bool = False,
|
|
134
|
+
) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Quick dispatch with simplified interface and 3 output formats.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
task: Task description.
|
|
140
|
+
output_format: 'structured'(default table), 'compact'(one-line), or 'detailed'(full).
|
|
141
|
+
include_action_items: Include H/M/L priority action items.
|
|
142
|
+
include_timing: Include execution timing data.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Formatted collaboration result optimized for quick reading.
|
|
146
|
+
"""
|
|
147
|
+
disp = server._get_dispatcher()
|
|
148
|
+
if _validator:
|
|
149
|
+
vresult = _validator.validate_task(task)
|
|
150
|
+
if not vresult.valid:
|
|
151
|
+
return json.dumps({"error": f"Invalid task: {vresult.reason}", "success": False})
|
|
152
|
+
task = vresult.sanitized_input or task
|
|
153
|
+
try:
|
|
154
|
+
result = disp.quick_dispatch(
|
|
155
|
+
task=task,
|
|
156
|
+
output_format=output_format,
|
|
157
|
+
include_action_items=include_action_items,
|
|
158
|
+
include_timing=include_timing,
|
|
159
|
+
)
|
|
160
|
+
return result.to_markdown() if hasattr(result, 'to_markdown') else str(result)
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.error(f"Quick dispatch error: {e}", exc_info=True)
|
|
163
|
+
return json.dumps({"error": "Internal error occurred", "success": False})
|
|
164
|
+
|
|
165
|
+
@mcp.tool()
|
|
166
|
+
def multiagent_roles(format: str = "text") -> str:
|
|
167
|
+
"""
|
|
168
|
+
List all available agent roles with descriptions.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
format: 'text' or 'json'.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Role list with descriptions showing expertise areas.
|
|
175
|
+
"""
|
|
176
|
+
roles = {}
|
|
177
|
+
for rid, rdef in ROLE_REGISTRY.items():
|
|
178
|
+
display_id = rdef.aliases[0] if rdef.aliases else rid
|
|
179
|
+
status_tag = " [planned]" if rdef.status == "planned" else ""
|
|
180
|
+
roles[display_id] = f"{rdef.description}{status_tag}"
|
|
181
|
+
if format == "json":
|
|
182
|
+
return json.dumps(roles, ensure_ascii=False, indent=2)
|
|
183
|
+
lines = [f"**{role}** — {desc}" for role, desc in roles.items()]
|
|
184
|
+
return "\n".join(lines)
|
|
185
|
+
|
|
186
|
+
@mcp.tool()
|
|
187
|
+
def multiagent_status() -> str:
|
|
188
|
+
"""
|
|
189
|
+
Get system status, version info, and capability summary.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
JSON with version, status, available roles/modes, and module info.
|
|
193
|
+
"""
|
|
194
|
+
disp = server._get_dispatcher()
|
|
195
|
+
try:
|
|
196
|
+
stats = disp.get_status() if hasattr(disp, 'get_status') else {}
|
|
197
|
+
return json.dumps({
|
|
198
|
+
"name": "DevSquad",
|
|
199
|
+
"version": "3.6.0",
|
|
200
|
+
"status": "ready",
|
|
201
|
+
"modules": 48,
|
|
202
|
+
"tests": 1548,
|
|
203
|
+
"roles": 7,
|
|
204
|
+
"modes": ["auto", "parallel", "sequential", "consensus"],
|
|
205
|
+
"backends": ["mock", "openai", "anthropic"],
|
|
206
|
+
"languages": ["zh", "en", "ja"],
|
|
207
|
+
"features": {
|
|
208
|
+
"memory_bridge": True,
|
|
209
|
+
"mce_adapter": True,
|
|
210
|
+
"workbuddy_claw": True,
|
|
211
|
+
"context_compression": True,
|
|
212
|
+
"permission_guard": True,
|
|
213
|
+
"skill_learning": True,
|
|
214
|
+
"streaming": True,
|
|
215
|
+
"checkpoint": True,
|
|
216
|
+
"workflow_engine": True,
|
|
217
|
+
"prompt_injection_detection": True,
|
|
218
|
+
"i18n": True,
|
|
219
|
+
},
|
|
220
|
+
}, ensure_ascii=False, indent=2)
|
|
221
|
+
except Exception as e:
|
|
222
|
+
return json.dumps({"name": "DevSquad", "version": "3.6.0", "status": "ready", "error": "Internal error occurred"})
|
|
223
|
+
|
|
224
|
+
@mcp.tool()
|
|
225
|
+
def multiagent_analyze(task: str) -> str:
|
|
226
|
+
"""
|
|
227
|
+
Analyze a task's intent and suggest optimal role configuration (dry-run).
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
task: Task description to analyze.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Analysis including suggested roles, estimated complexity, and mode recommendation.
|
|
234
|
+
"""
|
|
235
|
+
disp = server._get_dispatcher(enable_warmup=False)
|
|
236
|
+
if _validator:
|
|
237
|
+
vresult = _validator.validate_task(task)
|
|
238
|
+
if not vresult.valid:
|
|
239
|
+
return json.dumps({"error": f"Invalid task: {vresult.reason}"})
|
|
240
|
+
task = vresult.sanitized_input or task
|
|
241
|
+
try:
|
|
242
|
+
result = disp.dispatch(task, dry_run=True)
|
|
243
|
+
return json.dumps({
|
|
244
|
+
"task": task,
|
|
245
|
+
"suggested_roles": getattr(result, 'matched_roles', []),
|
|
246
|
+
"summary": result.summary,
|
|
247
|
+
"complexity": "estimated from task analysis",
|
|
248
|
+
"recommended_mode": "auto",
|
|
249
|
+
}, ensure_ascii=False, indent=2)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
return json.dumps({"error": "Internal error occurred"}, ensure_ascii=False, indent=2)
|
|
252
|
+
|
|
253
|
+
@mcp.tool()
|
|
254
|
+
def multiagent_shutdown() -> str:
|
|
255
|
+
"""
|
|
256
|
+
Shutdown the DevSquad dispatcher and free resources.
|
|
257
|
+
Call this when done to clean up memory and connections.
|
|
258
|
+
"""
|
|
259
|
+
server.shutdown()
|
|
260
|
+
return json.dumps({"status": "shutdown_complete"})
|
|
261
|
+
|
|
262
|
+
return mcp
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def main():
|
|
266
|
+
"""Start the MCP server."""
|
|
267
|
+
import argparse
|
|
268
|
+
|
|
269
|
+
parser = argparse.ArgumentParser(description="DevSquad MCP Server")
|
|
270
|
+
parser.add_argument("--port", "-p", type=int, default=None, help="SSE transport port (default: stdio)")
|
|
271
|
+
parser.add_argument("--host", default="127.0.0.1", help="SSE host (default: 127.0.0.1)")
|
|
272
|
+
args = parser.parse_args()
|
|
273
|
+
|
|
274
|
+
if not MCP_AVAILABLE:
|
|
275
|
+
print("ERROR: MCP SDK required. Install with: pip install mcp", file=sys.stderr)
|
|
276
|
+
sys.exit(1)
|
|
277
|
+
|
|
278
|
+
mcp = create_mcp_server()
|
|
279
|
+
|
|
280
|
+
if args.port:
|
|
281
|
+
logger.info(f"Starting SSE server on {args.host}:{args.port}")
|
|
282
|
+
mcp.run(transport="sse", host=args.host, port=args.port)
|
|
283
|
+
else:
|
|
284
|
+
logger.info("Starting stdio server")
|
|
285
|
+
mcp.run(transport="stdio")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == "__main__":
|
|
289
|
+
main()
|
skills/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DevSquad V3.6.0 — Layered Sub-Skill Architecture
|
|
3
|
+
|
|
4
|
+
This package provides fine-grained, independently usable skills that wrap
|
|
5
|
+
DevSquad's core modules. Each sub-skill is a thin handler (~50 lines) that
|
|
6
|
+
imports from the existing collaboration layer — no duplicated logic.
|
|
7
|
+
|
|
8
|
+
Architecture:
|
|
9
|
+
skills/
|
|
10
|
+
├── dispatch/ → MultiAgentDispatcher (7-role orchestration)
|
|
11
|
+
├── intent/ → IntentWorkflowMapper (6 intents × 3 languages)
|
|
12
|
+
├── review/ → FiveAxisConsensusEngine (5-axis code review)
|
|
13
|
+
├── security/ → Security audit + PermissionGuard
|
|
14
|
+
├── test/ → TestQualityGuard + test strategy
|
|
15
|
+
└── retrospective/ → RetrospectiveEngine + pattern extraction
|
|
16
|
+
|
|
17
|
+
Usage (standalone):
|
|
18
|
+
from skills.dispatch.handler import DispatchSkill
|
|
19
|
+
result = DispatchSkill().run("Fix login bug")
|
|
20
|
+
|
|
21
|
+
Usage (via registry):
|
|
22
|
+
from skills import SkillRegistry
|
|
23
|
+
skill = SkillRegistry.get("dispatch")
|
|
24
|
+
result = skill.run("Fix login bug")
|
|
25
|
+
|
|
26
|
+
All sub-skills work with Mock backend by default (no API key needed).
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from .registry import BaseSkill, get_skill, list_skills, discover_all
|
|
30
|
+
|
|
31
|
+
__all__ = ["BaseSkill", "get_skill", "list_skills", "discover_all"]
|
|
32
|
+
__version__ = "3.6.0"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Dispatch Skill — One-click multi-agent task orchestration."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
6
|
+
|
|
7
|
+
from skills.registry import BaseSkill
|
|
8
|
+
|
|
9
|
+
_dispatcher = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_dispatcher():
|
|
13
|
+
global _dispatcher
|
|
14
|
+
if _dispatcher is None:
|
|
15
|
+
from scripts.collaboration.dispatcher import MultiAgentDispatcher
|
|
16
|
+
_dispatcher = MultiAgentDispatcher()
|
|
17
|
+
return _dispatcher
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DispatchSkill(BaseSkill):
|
|
21
|
+
name = "dispatch"
|
|
22
|
+
description = "Multi-agent task dispatch: submit a task → auto-match roles → parallel execution → structured report"
|
|
23
|
+
version = "3.6.0"
|
|
24
|
+
|
|
25
|
+
def run(self, task: str, roles: list = None, mode: str = "auto",
|
|
26
|
+
dry_run: bool = False) -> dict:
|
|
27
|
+
d = _get_dispatcher()
|
|
28
|
+
result = d.dispatch(
|
|
29
|
+
task_description=task,
|
|
30
|
+
roles=roles,
|
|
31
|
+
mode=mode,
|
|
32
|
+
dry_run=dry_run,
|
|
33
|
+
)
|
|
34
|
+
timing = result.timing if hasattr(result, 'timing') and isinstance(result.timing, dict) else {}
|
|
35
|
+
return {
|
|
36
|
+
"success": result.success,
|
|
37
|
+
"matched_roles": result.matched_roles,
|
|
38
|
+
"worker_results": result.worker_results,
|
|
39
|
+
"report": getattr(result, 'report', None),
|
|
40
|
+
"intent_match": result.intent_match,
|
|
41
|
+
"five_axis_result": result.five_axis_result,
|
|
42
|
+
"timing": {"total_s": round(timing.get("total", 0), 3)},
|
|
43
|
+
"status": "success" if result.success else "failed",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def quick(self, task: str, **kwargs) -> dict:
|
|
47
|
+
return self.run(task, **kwargs)
|
|
48
|
+
|
|
49
|
+
def roles_info(self) -> list:
|
|
50
|
+
from scripts.collaboration.role_matcher import RoleMatcher
|
|
51
|
+
rm = RoleMatcher()
|
|
52
|
+
return [{"id": r.role_id, "name": r.name, "keywords": r.keywords[:5]} for r in rm.roles]
|
skills/intent/handler.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Intent Detection Skill — 用户意图自动识别与工作流映射。"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
6
|
+
|
|
7
|
+
from skills.registry import BaseSkill
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class IntentSkill(BaseSkill):
|
|
11
|
+
name = "intent"
|
|
12
|
+
description = "Detect user intent from natural language and map to workflow chain (6 intents × 3 languages)"
|
|
13
|
+
version = "3.6.0"
|
|
14
|
+
|
|
15
|
+
INTENT_MAP = {
|
|
16
|
+
"bug_fix": "🐛 Bug修复",
|
|
17
|
+
"new_feature": "✨ 新功能开发",
|
|
18
|
+
"code_review": "🔍 代码评审",
|
|
19
|
+
"refactor": "♻️ 重构优化",
|
|
20
|
+
"investigation": "🔬 问题排查",
|
|
21
|
+
"documentation": "📝 文档编写",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
def detect(self, text: str, lang: str = "auto") -> dict:
|
|
25
|
+
from scripts.collaboration.intent_workflow_mapper import IntentWorkflowMapper
|
|
26
|
+
mapper = IntentWorkflowMapper()
|
|
27
|
+
result = mapper.detect_intent(text, lang=lang)
|
|
28
|
+
icon = self.INTENT_MAP.get(result.intent_type, "❓")
|
|
29
|
+
return {
|
|
30
|
+
"intent": result.intent_type,
|
|
31
|
+
"label": f"{icon} {result.intent_type}",
|
|
32
|
+
"confidence": round(result.confidence, 4),
|
|
33
|
+
"lang": lang,
|
|
34
|
+
"required_roles": result.required_roles or [],
|
|
35
|
+
"optional_roles": result.optional_roles or [],
|
|
36
|
+
"workflow_chain": result.workflow_chain or [],
|
|
37
|
+
"gates": [],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
def batch_detect(self, texts: list, **kwargs) -> list:
|
|
41
|
+
return [self.detect(t, **kwargs) for t in texts]
|
|
42
|
+
|
|
43
|
+
def list_intents(self) -> list:
|
|
44
|
+
return [
|
|
45
|
+
{"type": k, "label": v, "description": self._describe(k)}
|
|
46
|
+
for k, v in self.INTENT_MAP.items()
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def _describe(intent_type: str) -> str:
|
|
51
|
+
descs = {
|
|
52
|
+
"bug_fix": "Fix defects, errors, or unexpected behavior",
|
|
53
|
+
"new_feature": "Build new functionality or features",
|
|
54
|
+
"code_review": "Review code quality, security, performance",
|
|
55
|
+
"refactor": "Improve code structure without changing behavior",
|
|
56
|
+
"investigation": "Diagnose root cause of problems",
|
|
57
|
+
"documentation": "Create or update documentation",
|
|
58
|
+
}
|
|
59
|
+
return descs.get(intent_type, "")
|
skills/registry.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Sub-skill registry: discover and instantiate skills by name."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, Optional, Type
|
|
6
|
+
|
|
7
|
+
_SKILL_DIR = Path(__file__).parent
|
|
8
|
+
_AVAILABLE_SKILLS: Dict[str, Type] = {}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseSkill:
|
|
12
|
+
"""Base class for all DevSquad sub-skills."""
|
|
13
|
+
|
|
14
|
+
name: str = ""
|
|
15
|
+
description: str = ""
|
|
16
|
+
version: str = "3.6.0"
|
|
17
|
+
|
|
18
|
+
def run(self, *args, **kwargs):
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
|
|
21
|
+
def info(self) -> dict:
|
|
22
|
+
return {"name": self.name, "description": self.description, "version": self.version}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _load_skill(name: str) -> Optional[Type[BaseSkill]]:
|
|
26
|
+
skill_dir = _SKILL_DIR / name
|
|
27
|
+
if not skill_dir.is_dir():
|
|
28
|
+
return None
|
|
29
|
+
handler_path = skill_dir / "handler.py"
|
|
30
|
+
if not handler_path.exists():
|
|
31
|
+
return None
|
|
32
|
+
try:
|
|
33
|
+
mod = importlib.import_module(f"skills.{name}.handler")
|
|
34
|
+
for attr_name in dir(mod):
|
|
35
|
+
attr = getattr(mod, attr_name)
|
|
36
|
+
if isinstance(attr, type) and issubclass(attr, BaseSkill) and attr is not BaseSkill:
|
|
37
|
+
return attr
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_skill(name: str) -> BaseSkill:
|
|
44
|
+
"""Get a skill instance by name. Lazy-loads on first access."""
|
|
45
|
+
if name not in _AVAILABLE_SKILLS:
|
|
46
|
+
cls = _load_skill(name)
|
|
47
|
+
if cls is None:
|
|
48
|
+
raise ValueError(f"Skill '{name}' not found. Available: {list_skills()}")
|
|
49
|
+
_AVAILABLE_SKILLS[name] = cls
|
|
50
|
+
return _AVAILABLE_SKILLS[name]()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def list_skills() -> list:
|
|
54
|
+
"""List all available sub-skill names."""
|
|
55
|
+
if not _AVAILABLE_SKILLS:
|
|
56
|
+
for d in sorted(_SKILL_DIR.iterdir()):
|
|
57
|
+
if d.is_dir() and (d / "handler.py").exists():
|
|
58
|
+
cls = _load_skill(d.name)
|
|
59
|
+
if cls:
|
|
60
|
+
_AVAILABLE_SKILLS[d.name] = cls
|
|
61
|
+
return list(_AVAILABLE_SKILLS.keys())
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def discover_all() -> Dict[str, BaseSkill]:
|
|
65
|
+
"""Instantiate all available skills."""
|
|
66
|
+
names = list_skills()
|
|
67
|
+
return {name: get_skill(name) for name in names}
|
|
File without changes
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Retrospective Skill - V3.6.0"""
|
|
2
|
+
|
|
3
|
+
from skills.registry import BaseSkill
|
|
4
|
+
from scripts.collaboration.retrospective import RetrospectiveEngine
|
|
5
|
+
from scripts.collaboration.models import (
|
|
6
|
+
StructuredGoal,
|
|
7
|
+
GoalItem,
|
|
8
|
+
GoalItemStatus,
|
|
9
|
+
AnchorResult,
|
|
10
|
+
AnchorTrigger,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RetrospectiveSkill(BaseSkill):
|
|
15
|
+
name = "retrospective"
|
|
16
|
+
description = "项目复盘 - 提取改进模式/生成可执行建议/复盘摘要"
|
|
17
|
+
version = "3.6.0"
|
|
18
|
+
|
|
19
|
+
CAPABILITIES = [
|
|
20
|
+
"post_dispatch_analysis",
|
|
21
|
+
"pattern_extraction",
|
|
22
|
+
"improvement_suggestion",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
OUTPUT_FORMATS = ["findings", "patterns", "improvements", "summary"]
|
|
26
|
+
|
|
27
|
+
def run(self, action="info", **kwargs):
|
|
28
|
+
actions = {
|
|
29
|
+
"run_retrospective": self.run_retrospective,
|
|
30
|
+
"extract_patterns": self.extract_patterns,
|
|
31
|
+
"generate_improvements": self.generate_improvements,
|
|
32
|
+
"summary": self.summary,
|
|
33
|
+
}
|
|
34
|
+
fn = actions.get(action)
|
|
35
|
+
if not fn:
|
|
36
|
+
return {"error": f"Unknown action: {action}. Available: {list(actions.keys())}"}
|
|
37
|
+
return fn(**kwargs)
|
|
38
|
+
|
|
39
|
+
def _mock_goal(self, task: str = "") -> StructuredGoal:
|
|
40
|
+
desc = task or "模拟任务:完成功能开发并修复已知问题"
|
|
41
|
+
return StructuredGoal(
|
|
42
|
+
goal_id="retro_mock_001",
|
|
43
|
+
original_description=desc,
|
|
44
|
+
items=[
|
|
45
|
+
GoalItem(item_id="item_001", description=desc, status=GoalItemStatus.FULLY_COVERED, coverage_score=1.0),
|
|
46
|
+
],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def _mock_anchor_history(self) -> list:
|
|
50
|
+
return [
|
|
51
|
+
AnchorResult(aligned=True, trigger=AnchorTrigger.MILESTONE, coverage=0.8, drift_score=0.05),
|
|
52
|
+
AnchorResult(aligned=True, trigger=AnchorTrigger.PHASE_GATE, coverage=0.9, drift_score=0.02),
|
|
53
|
+
AnchorResult(aligned=False, trigger=AnchorTrigger.DIRECTION_CHANGE, coverage=0.7, drift_score=0.35),
|
|
54
|
+
AnchorResult(aligned=True, trigger=AnchorTrigger.STEP_COMPLETE, coverage=0.95, drift_score=0.01),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
def run_retrospective(self, dispatch_result: dict = None, task: str = "") -> dict:
|
|
58
|
+
engine = RetrospectiveEngine()
|
|
59
|
+
goal = self._mock_goal(task)
|
|
60
|
+
history = self._mock_anchor_history()
|
|
61
|
+
report = engine.run(goal=goal, anchor_history=history)
|
|
62
|
+
result = report.to_dict() if hasattr(report, 'to_dict') else {
|
|
63
|
+
"task_goal": report.task_goal,
|
|
64
|
+
"deviations": [d.__dict__ for d in report.deviations],
|
|
65
|
+
"redundant_steps": report.redundant_steps,
|
|
66
|
+
"improvements": report.improvements,
|
|
67
|
+
"summary": report.summary,
|
|
68
|
+
}
|
|
69
|
+
if dispatch_result:
|
|
70
|
+
result["dispatch_context"] = dispatch_result
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
def extract_patterns(self, history_items: list = None) -> dict:
|
|
74
|
+
items = history_items or [
|
|
75
|
+
{"type": "goal_drift", "count": 3},
|
|
76
|
+
{"type": "redundant_step", "count": 2},
|
|
77
|
+
{"type": "late_error_detection", "count": 4},
|
|
78
|
+
]
|
|
79
|
+
patterns = {}
|
|
80
|
+
for item in items:
|
|
81
|
+
t = item.get("type", "unknown")
|
|
82
|
+
patterns[t] = patterns.get(t, 0) + item.get("count", 1)
|
|
83
|
+
top_patterns = sorted(patterns.items(), key=lambda x: x[1], reverse=True)[:5]
|
|
84
|
+
return {
|
|
85
|
+
"total_items": len(items),
|
|
86
|
+
"patterns": [{"type": t, "occurrences": c} for t, c in top_patterns],
|
|
87
|
+
"insight": f"最频繁模式: {top_patterns[0][0] if top_patterns else 'N/A'} ({top_patterns[0][1] if top_patterns else 0}次)",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def generate_improvements(self, findings: list) -> dict:
|
|
91
|
+
if not findings:
|
|
92
|
+
findings = ["goal_drift", "missing_anchor_checks"]
|
|
93
|
+
improvement_map = {
|
|
94
|
+
"goal_drift": "增加中间锚点检查以尽早发现目标偏移",
|
|
95
|
+
"missing_anchor_checks": "在关键决策点添加 AnchorCheck 验证",
|
|
96
|
+
"redundant_steps": "优化工作流消除重复步骤",
|
|
97
|
+
"late_error_detection": "前置错误检测点,缩短反馈循环",
|
|
98
|
+
"low_coverage": "补充缺失的测试维度和边界用例",
|
|
99
|
+
}
|
|
100
|
+
improvements = [improvement_map.get(f, f"针对 {f} 的改进建议") for f in findings]
|
|
101
|
+
return {
|
|
102
|
+
"findings_count": len(findings),
|
|
103
|
+
"improvements": improvements,
|
|
104
|
+
"priority": "high" if len(findings) > 3 else "medium",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
def summary(self, retro_result: dict = None) -> dict:
|
|
108
|
+
if retro_result:
|
|
109
|
+
deviations = retro_result.get("deviations", [])
|
|
110
|
+
improvements = retro_result.get("improvements", [])
|
|
111
|
+
s = retro_result.get("summary", "")
|
|
112
|
+
else:
|
|
113
|
+
full = self.run_retrospective()
|
|
114
|
+
deviations = full.get("deviations", [])
|
|
115
|
+
improvements = full.get("improvements", [])
|
|
116
|
+
s = full.get("summary", "")
|
|
117
|
+
went_well = [f"目标覆盖率达标" if not deviations else "任务已完成"]
|
|
118
|
+
to_improve = improvements[:5] if improvements else ["保持当前流程"]
|
|
119
|
+
return {
|
|
120
|
+
"what_went_well": went_well,
|
|
121
|
+
"what_to_improve": to_improve,
|
|
122
|
+
"deviation_count": len(deviations),
|
|
123
|
+
"improvement_count": len(improvements),
|
|
124
|
+
"raw_summary": s,
|
|
125
|
+
}
|