superqode 0.1.5__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.
- superqode/__init__.py +33 -0
- superqode/acp/__init__.py +23 -0
- superqode/acp/client.py +913 -0
- superqode/acp/permission_screen.py +457 -0
- superqode/acp/types.py +480 -0
- superqode/acp_discovery.py +856 -0
- superqode/agent/__init__.py +22 -0
- superqode/agent/edit_strategies.py +334 -0
- superqode/agent/loop.py +892 -0
- superqode/agent/qe_report_templates.py +39 -0
- superqode/agent/system_prompts.py +353 -0
- superqode/agent_output.py +721 -0
- superqode/agent_stream.py +953 -0
- superqode/agents/__init__.py +59 -0
- superqode/agents/acp_registry.py +305 -0
- superqode/agents/client.py +249 -0
- superqode/agents/data/augmentcode.com.toml +51 -0
- superqode/agents/data/cagent.dev.toml +51 -0
- superqode/agents/data/claude.com.toml +60 -0
- superqode/agents/data/codeassistant.dev.toml +51 -0
- superqode/agents/data/codex.openai.com.toml +57 -0
- superqode/agents/data/fastagent.ai.toml +66 -0
- superqode/agents/data/geminicli.com.toml +77 -0
- superqode/agents/data/goose.block.xyz.toml +54 -0
- superqode/agents/data/junie.jetbrains.com.toml +56 -0
- superqode/agents/data/kimi.moonshot.cn.toml +57 -0
- superqode/agents/data/llmlingagent.dev.toml +51 -0
- superqode/agents/data/molt.bot.toml +49 -0
- superqode/agents/data/opencode.ai.toml +60 -0
- superqode/agents/data/stakpak.dev.toml +51 -0
- superqode/agents/data/vtcode.dev.toml +51 -0
- superqode/agents/discovery.py +266 -0
- superqode/agents/messaging.py +160 -0
- superqode/agents/persona.py +166 -0
- superqode/agents/registry.py +421 -0
- superqode/agents/schema.py +72 -0
- superqode/agents/unified.py +367 -0
- superqode/app/__init__.py +111 -0
- superqode/app/constants.py +314 -0
- superqode/app/css.py +366 -0
- superqode/app/models.py +118 -0
- superqode/app/suggester.py +125 -0
- superqode/app/widgets.py +1591 -0
- superqode/app_enhanced.py +399 -0
- superqode/app_main.py +17187 -0
- superqode/approval.py +312 -0
- superqode/atomic.py +296 -0
- superqode/commands/__init__.py +1 -0
- superqode/commands/acp.py +965 -0
- superqode/commands/agents.py +180 -0
- superqode/commands/auth.py +278 -0
- superqode/commands/config.py +374 -0
- superqode/commands/init.py +826 -0
- superqode/commands/providers.py +819 -0
- superqode/commands/qe.py +1145 -0
- superqode/commands/roles.py +380 -0
- superqode/commands/serve.py +172 -0
- superqode/commands/suggestions.py +127 -0
- superqode/commands/superqe.py +460 -0
- superqode/config/__init__.py +51 -0
- superqode/config/loader.py +812 -0
- superqode/config/schema.py +498 -0
- superqode/core/__init__.py +111 -0
- superqode/core/roles.py +281 -0
- superqode/danger.py +386 -0
- superqode/data/superqode-template.yaml +1522 -0
- superqode/design_system.py +1080 -0
- superqode/dialogs/__init__.py +6 -0
- superqode/dialogs/base.py +39 -0
- superqode/dialogs/model.py +130 -0
- superqode/dialogs/provider.py +870 -0
- superqode/diff_view.py +919 -0
- superqode/enterprise.py +21 -0
- superqode/evaluation/__init__.py +25 -0
- superqode/evaluation/adapters.py +93 -0
- superqode/evaluation/behaviors.py +89 -0
- superqode/evaluation/engine.py +209 -0
- superqode/evaluation/scenarios.py +96 -0
- superqode/execution/__init__.py +36 -0
- superqode/execution/linter.py +538 -0
- superqode/execution/modes.py +347 -0
- superqode/execution/resolver.py +283 -0
- superqode/execution/runner.py +642 -0
- superqode/file_explorer.py +811 -0
- superqode/file_viewer.py +471 -0
- superqode/flash.py +183 -0
- superqode/guidance/__init__.py +58 -0
- superqode/guidance/config.py +203 -0
- superqode/guidance/prompts.py +71 -0
- superqode/harness/__init__.py +54 -0
- superqode/harness/accelerator.py +291 -0
- superqode/harness/config.py +319 -0
- superqode/harness/validator.py +147 -0
- superqode/history.py +279 -0
- superqode/integrations/superopt_runner.py +124 -0
- superqode/logging/__init__.py +49 -0
- superqode/logging/adapters.py +219 -0
- superqode/logging/formatter.py +923 -0
- superqode/logging/integration.py +341 -0
- superqode/logging/sinks.py +170 -0
- superqode/logging/unified_log.py +417 -0
- superqode/lsp/__init__.py +26 -0
- superqode/lsp/client.py +544 -0
- superqode/main.py +1069 -0
- superqode/mcp/__init__.py +89 -0
- superqode/mcp/auth_storage.py +380 -0
- superqode/mcp/client.py +1236 -0
- superqode/mcp/config.py +319 -0
- superqode/mcp/integration.py +337 -0
- superqode/mcp/oauth.py +436 -0
- superqode/mcp/oauth_callback.py +385 -0
- superqode/mcp/types.py +290 -0
- superqode/memory/__init__.py +31 -0
- superqode/memory/feedback.py +342 -0
- superqode/memory/store.py +522 -0
- superqode/notifications.py +369 -0
- superqode/optimization/__init__.py +5 -0
- superqode/optimization/config.py +33 -0
- superqode/permissions/__init__.py +25 -0
- superqode/permissions/rules.py +488 -0
- superqode/plan.py +323 -0
- superqode/providers/__init__.py +33 -0
- superqode/providers/gateway/__init__.py +165 -0
- superqode/providers/gateway/base.py +228 -0
- superqode/providers/gateway/litellm_gateway.py +1170 -0
- superqode/providers/gateway/openresponses_gateway.py +436 -0
- superqode/providers/health.py +297 -0
- superqode/providers/huggingface/__init__.py +74 -0
- superqode/providers/huggingface/downloader.py +472 -0
- superqode/providers/huggingface/endpoints.py +442 -0
- superqode/providers/huggingface/hub.py +531 -0
- superqode/providers/huggingface/inference.py +394 -0
- superqode/providers/huggingface/transformers_runner.py +516 -0
- superqode/providers/local/__init__.py +100 -0
- superqode/providers/local/base.py +438 -0
- superqode/providers/local/discovery.py +418 -0
- superqode/providers/local/lmstudio.py +256 -0
- superqode/providers/local/mlx.py +457 -0
- superqode/providers/local/ollama.py +486 -0
- superqode/providers/local/sglang.py +268 -0
- superqode/providers/local/tgi.py +260 -0
- superqode/providers/local/tool_support.py +477 -0
- superqode/providers/local/vllm.py +258 -0
- superqode/providers/manager.py +1338 -0
- superqode/providers/models.py +1016 -0
- superqode/providers/models_dev.py +578 -0
- superqode/providers/openresponses/__init__.py +87 -0
- superqode/providers/openresponses/converters/__init__.py +17 -0
- superqode/providers/openresponses/converters/messages.py +343 -0
- superqode/providers/openresponses/converters/tools.py +268 -0
- superqode/providers/openresponses/schema/__init__.py +56 -0
- superqode/providers/openresponses/schema/models.py +585 -0
- superqode/providers/openresponses/streaming/__init__.py +5 -0
- superqode/providers/openresponses/streaming/parser.py +338 -0
- superqode/providers/openresponses/tools/__init__.py +21 -0
- superqode/providers/openresponses/tools/apply_patch.py +352 -0
- superqode/providers/openresponses/tools/code_interpreter.py +290 -0
- superqode/providers/openresponses/tools/file_search.py +333 -0
- superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
- superqode/providers/registry.py +716 -0
- superqode/providers/usage.py +332 -0
- superqode/pure_mode.py +384 -0
- superqode/qr/__init__.py +23 -0
- superqode/qr/dashboard.py +781 -0
- superqode/qr/generator.py +1018 -0
- superqode/qr/templates.py +135 -0
- superqode/safety/__init__.py +41 -0
- superqode/safety/sandbox.py +413 -0
- superqode/safety/warnings.py +256 -0
- superqode/server/__init__.py +33 -0
- superqode/server/lsp_server.py +775 -0
- superqode/server/web.py +250 -0
- superqode/session/__init__.py +25 -0
- superqode/session/persistence.py +580 -0
- superqode/session/sharing.py +477 -0
- superqode/session.py +475 -0
- superqode/sidebar.py +2991 -0
- superqode/stream_view.py +648 -0
- superqode/styles/__init__.py +3 -0
- superqode/superqe/__init__.py +184 -0
- superqode/superqe/acp_runner.py +1064 -0
- superqode/superqe/constitution/__init__.py +62 -0
- superqode/superqe/constitution/evaluator.py +308 -0
- superqode/superqe/constitution/loader.py +432 -0
- superqode/superqe/constitution/schema.py +250 -0
- superqode/superqe/events.py +591 -0
- superqode/superqe/frameworks/__init__.py +65 -0
- superqode/superqe/frameworks/base.py +234 -0
- superqode/superqe/frameworks/e2e.py +263 -0
- superqode/superqe/frameworks/executor.py +237 -0
- superqode/superqe/frameworks/javascript.py +409 -0
- superqode/superqe/frameworks/python.py +373 -0
- superqode/superqe/frameworks/registry.py +92 -0
- superqode/superqe/mcp_tools/__init__.py +47 -0
- superqode/superqe/mcp_tools/core_tools.py +418 -0
- superqode/superqe/mcp_tools/registry.py +230 -0
- superqode/superqe/mcp_tools/testing_tools.py +167 -0
- superqode/superqe/noise.py +89 -0
- superqode/superqe/orchestrator.py +778 -0
- superqode/superqe/roles.py +609 -0
- superqode/superqe/session.py +713 -0
- superqode/superqe/skills/__init__.py +57 -0
- superqode/superqe/skills/base.py +106 -0
- superqode/superqe/skills/core_skills.py +899 -0
- superqode/superqe/skills/registry.py +90 -0
- superqode/superqe/verifier.py +101 -0
- superqode/superqe_cli.py +76 -0
- superqode/tool_call.py +358 -0
- superqode/tools/__init__.py +93 -0
- superqode/tools/agent_tools.py +496 -0
- superqode/tools/base.py +324 -0
- superqode/tools/batch_tool.py +133 -0
- superqode/tools/diagnostics.py +311 -0
- superqode/tools/edit_tools.py +653 -0
- superqode/tools/enhanced_base.py +515 -0
- superqode/tools/file_tools.py +269 -0
- superqode/tools/file_tracking.py +45 -0
- superqode/tools/lsp_tools.py +610 -0
- superqode/tools/network_tools.py +350 -0
- superqode/tools/permissions.py +400 -0
- superqode/tools/question_tool.py +324 -0
- superqode/tools/search_tools.py +598 -0
- superqode/tools/shell_tools.py +259 -0
- superqode/tools/todo_tools.py +121 -0
- superqode/tools/validation.py +80 -0
- superqode/tools/web_tools.py +639 -0
- superqode/tui.py +1152 -0
- superqode/tui_integration.py +875 -0
- superqode/tui_widgets/__init__.py +27 -0
- superqode/tui_widgets/widgets/__init__.py +18 -0
- superqode/tui_widgets/widgets/progress.py +185 -0
- superqode/tui_widgets/widgets/tool_display.py +188 -0
- superqode/undo_manager.py +574 -0
- superqode/utils/__init__.py +5 -0
- superqode/utils/error_handling.py +323 -0
- superqode/utils/fuzzy.py +257 -0
- superqode/widgets/__init__.py +477 -0
- superqode/widgets/agent_collab.py +390 -0
- superqode/widgets/agent_store.py +936 -0
- superqode/widgets/agent_switcher.py +395 -0
- superqode/widgets/animation_manager.py +284 -0
- superqode/widgets/code_context.py +356 -0
- superqode/widgets/command_palette.py +412 -0
- superqode/widgets/connection_status.py +537 -0
- superqode/widgets/conversation_history.py +470 -0
- superqode/widgets/diff_indicator.py +155 -0
- superqode/widgets/enhanced_status_bar.py +385 -0
- superqode/widgets/enhanced_toast.py +476 -0
- superqode/widgets/file_browser.py +809 -0
- superqode/widgets/file_reference.py +585 -0
- superqode/widgets/issue_timeline.py +340 -0
- superqode/widgets/leader_key.py +264 -0
- superqode/widgets/mode_switcher.py +445 -0
- superqode/widgets/model_picker.py +234 -0
- superqode/widgets/permission_preview.py +1205 -0
- superqode/widgets/prompt.py +358 -0
- superqode/widgets/provider_connect.py +725 -0
- superqode/widgets/pty_shell.py +587 -0
- superqode/widgets/qe_dashboard.py +321 -0
- superqode/widgets/resizable_sidebar.py +377 -0
- superqode/widgets/response_changes.py +218 -0
- superqode/widgets/response_display.py +528 -0
- superqode/widgets/rich_tool_display.py +613 -0
- superqode/widgets/sidebar_panels.py +1180 -0
- superqode/widgets/slash_complete.py +356 -0
- superqode/widgets/split_view.py +612 -0
- superqode/widgets/status_bar.py +273 -0
- superqode/widgets/superqode_display.py +786 -0
- superqode/widgets/thinking_display.py +815 -0
- superqode/widgets/throbber.py +87 -0
- superqode/widgets/toast.py +206 -0
- superqode/widgets/unified_output.py +1073 -0
- superqode/workspace/__init__.py +75 -0
- superqode/workspace/artifacts.py +472 -0
- superqode/workspace/coordinator.py +353 -0
- superqode/workspace/diff_tracker.py +429 -0
- superqode/workspace/git_guard.py +373 -0
- superqode/workspace/git_snapshot.py +526 -0
- superqode/workspace/manager.py +750 -0
- superqode/workspace/snapshot.py +357 -0
- superqode/workspace/watcher.py +535 -0
- superqode/workspace/worktree.py +440 -0
- superqode-0.1.5.dist-info/METADATA +204 -0
- superqode-0.1.5.dist-info/RECORD +288 -0
- superqode-0.1.5.dist-info/WHEEL +5 -0
- superqode-0.1.5.dist-info/entry_points.txt +3 -0
- superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
- superqode-0.1.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core MCP Tools - Always loaded tools for essential operations.
|
|
3
|
+
|
|
4
|
+
14 core tools:
|
|
5
|
+
- fleet_init, agent_spawn, fleet_status
|
|
6
|
+
- test_generate, test_execute, test_report
|
|
7
|
+
- memory_store, memory_retrieve, memory_query
|
|
8
|
+
- quality_analyze, task_orchestrate, task_status
|
|
9
|
+
- tools_discover, tools_load_domain
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
from .registry import MCPToolRegistry, MCPTool, ToolDomain
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def fleet_init(
|
|
17
|
+
topology: str = "hierarchical", max_agents: int = 10, **kwargs
|
|
18
|
+
) -> Dict[str, Any]:
|
|
19
|
+
"""Initialize the QE fleet with specified topology."""
|
|
20
|
+
return {
|
|
21
|
+
"status": "initialized",
|
|
22
|
+
"topology": topology,
|
|
23
|
+
"max_agents": max_agents,
|
|
24
|
+
"available_agents": [
|
|
25
|
+
"flaky-test-hunter",
|
|
26
|
+
"visual-tester",
|
|
27
|
+
"accessibility-ally",
|
|
28
|
+
"deployment-readiness",
|
|
29
|
+
"code-complexity",
|
|
30
|
+
"requirements-validator",
|
|
31
|
+
"contract-tester",
|
|
32
|
+
"mutation-tester",
|
|
33
|
+
],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def agent_spawn(
|
|
38
|
+
agent_type: str, config: Optional[Dict[str, Any]] = None, **kwargs
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""Spawn a QE agent of the specified type."""
|
|
41
|
+
from ..agents import get_agent, AgentConfig
|
|
42
|
+
|
|
43
|
+
agent_config = AgentConfig(**(config or {}))
|
|
44
|
+
agent = get_agent(agent_type, agent_config)
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
"agent_id": agent.agent_id,
|
|
48
|
+
"type": agent_type,
|
|
49
|
+
"status": "spawned",
|
|
50
|
+
"info": agent.get_info(),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def fleet_status(**kwargs) -> Dict[str, Any]:
|
|
55
|
+
"""Get the status of the QE fleet."""
|
|
56
|
+
return {
|
|
57
|
+
"status": "active",
|
|
58
|
+
"agents_available": 8,
|
|
59
|
+
"agents_running": 0,
|
|
60
|
+
"domains_loaded": ["core"],
|
|
61
|
+
"memory_usage": "low",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def test_generate(
|
|
66
|
+
target: str, framework: str = "pytest", coverage_goal: float = 80.0, **kwargs
|
|
67
|
+
) -> Dict[str, Any]:
|
|
68
|
+
"""Generate tests for the specified target."""
|
|
69
|
+
return {
|
|
70
|
+
"status": "generated",
|
|
71
|
+
"target": target,
|
|
72
|
+
"framework": framework,
|
|
73
|
+
"tests_generated": 0,
|
|
74
|
+
"coverage_goal": coverage_goal,
|
|
75
|
+
"message": "Use testing domain tools for actual generation",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def test_execute(
|
|
80
|
+
pattern: str = "**/test_*.py", parallel: bool = True, workers: int = 4, **kwargs
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
"""Execute tests matching the pattern."""
|
|
83
|
+
return {
|
|
84
|
+
"status": "executed",
|
|
85
|
+
"pattern": pattern,
|
|
86
|
+
"parallel": parallel,
|
|
87
|
+
"workers": workers,
|
|
88
|
+
"results": {"passed": 0, "failed": 0, "skipped": 0},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def test_report(
|
|
93
|
+
format: str = "html", output_path: Optional[str] = None, **kwargs
|
|
94
|
+
) -> Dict[str, Any]:
|
|
95
|
+
"""Generate a test report."""
|
|
96
|
+
return {
|
|
97
|
+
"status": "generated",
|
|
98
|
+
"format": format,
|
|
99
|
+
"output_path": output_path or f"./reports/test_report.{format}",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async def memory_store(
|
|
104
|
+
key: str,
|
|
105
|
+
value: Any,
|
|
106
|
+
namespace: str = "default",
|
|
107
|
+
ttl: Optional[int] = None,
|
|
108
|
+
persist: bool = False,
|
|
109
|
+
**kwargs,
|
|
110
|
+
) -> Dict[str, Any]:
|
|
111
|
+
"""Store a value in agent memory."""
|
|
112
|
+
return {"status": "stored", "key": key, "namespace": namespace, "persist": persist, "ttl": ttl}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def memory_retrieve(key: str, namespace: str = "default", **kwargs) -> Dict[str, Any]:
|
|
116
|
+
"""Retrieve a value from agent memory."""
|
|
117
|
+
return {
|
|
118
|
+
"status": "retrieved",
|
|
119
|
+
"key": key,
|
|
120
|
+
"namespace": namespace,
|
|
121
|
+
"value": None,
|
|
122
|
+
"found": False,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
async def memory_query(
|
|
127
|
+
pattern: str, namespace: str = "default", limit: int = 100, **kwargs
|
|
128
|
+
) -> Dict[str, Any]:
|
|
129
|
+
"""Query memory with a pattern."""
|
|
130
|
+
return {
|
|
131
|
+
"status": "queried",
|
|
132
|
+
"pattern": pattern,
|
|
133
|
+
"namespace": namespace,
|
|
134
|
+
"results": [],
|
|
135
|
+
"total": 0,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def quality_analyze(
|
|
140
|
+
target: str, checks: Optional[List[str]] = None, **kwargs
|
|
141
|
+
) -> Dict[str, Any]:
|
|
142
|
+
"""Analyze code quality."""
|
|
143
|
+
return {
|
|
144
|
+
"status": "analyzed",
|
|
145
|
+
"target": target,
|
|
146
|
+
"checks": checks or ["complexity", "security", "coverage"],
|
|
147
|
+
"score": 0,
|
|
148
|
+
"findings": [],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async def task_orchestrate(
|
|
153
|
+
task: str, agents: Optional[List[str]] = None, priority: str = "medium", **kwargs
|
|
154
|
+
) -> Dict[str, Any]:
|
|
155
|
+
"""Orchestrate a task across agents."""
|
|
156
|
+
return {
|
|
157
|
+
"status": "orchestrating",
|
|
158
|
+
"task": task,
|
|
159
|
+
"agents": agents or [],
|
|
160
|
+
"priority": priority,
|
|
161
|
+
"task_id": "task-001",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
async def task_status(task_id: str, **kwargs) -> Dict[str, Any]:
|
|
166
|
+
"""Get the status of a task."""
|
|
167
|
+
return {"task_id": task_id, "status": "pending", "progress": 0, "agents": [], "results": None}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def tools_discover(
|
|
171
|
+
domain: Optional[str] = None, keyword: Optional[str] = None, **kwargs
|
|
172
|
+
) -> Dict[str, Any]:
|
|
173
|
+
"""Discover available tools."""
|
|
174
|
+
from .registry import get_registry, ToolDomain
|
|
175
|
+
|
|
176
|
+
registry = get_registry()
|
|
177
|
+
|
|
178
|
+
if domain:
|
|
179
|
+
try:
|
|
180
|
+
d = ToolDomain(domain)
|
|
181
|
+
tools = registry.list_tools(d)
|
|
182
|
+
except ValueError:
|
|
183
|
+
return {"error": f"Unknown domain: {domain}"}
|
|
184
|
+
else:
|
|
185
|
+
tools = registry.list_tools()
|
|
186
|
+
|
|
187
|
+
if keyword:
|
|
188
|
+
tools = [
|
|
189
|
+
t
|
|
190
|
+
for t in tools
|
|
191
|
+
if keyword.lower() in t.name.lower() or keyword.lower() in t.description.lower()
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
"tools": [t.to_mcp_format() for t in tools],
|
|
196
|
+
"count": len(tools),
|
|
197
|
+
"domains": registry.list_domains(),
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
async def tools_load_domain(domain: str, **kwargs) -> Dict[str, Any]:
|
|
202
|
+
"""Load tools for a specific domain."""
|
|
203
|
+
from .registry import get_registry, ToolDomain
|
|
204
|
+
|
|
205
|
+
registry = get_registry()
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
d = ToolDomain(domain)
|
|
209
|
+
except ValueError:
|
|
210
|
+
return {"error": f"Unknown domain: {domain}"}
|
|
211
|
+
|
|
212
|
+
# Register domain tools if not already
|
|
213
|
+
if d == ToolDomain.TESTING:
|
|
214
|
+
from .testing_tools import register_testing_tools
|
|
215
|
+
|
|
216
|
+
register_testing_tools(registry)
|
|
217
|
+
tools = registry.load_domain(d)
|
|
218
|
+
|
|
219
|
+
return {"domain": domain, "tools_loaded": len(tools), "tools": [t.name for t in tools]}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def register_core_tools(registry: MCPToolRegistry) -> None:
|
|
223
|
+
"""Register all core tools."""
|
|
224
|
+
tools = [
|
|
225
|
+
MCPTool(
|
|
226
|
+
name="fleet_init",
|
|
227
|
+
description="Initialize the QE fleet with specified topology",
|
|
228
|
+
handler=fleet_init,
|
|
229
|
+
domain=ToolDomain.CORE,
|
|
230
|
+
schema={
|
|
231
|
+
"type": "object",
|
|
232
|
+
"properties": {
|
|
233
|
+
"topology": {"type": "string", "enum": ["hierarchical", "mesh", "ring"]},
|
|
234
|
+
"max_agents": {"type": "integer", "default": 10},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
keywords=["init", "start", "fleet", "setup"],
|
|
238
|
+
),
|
|
239
|
+
MCPTool(
|
|
240
|
+
name="agent_spawn",
|
|
241
|
+
description="Spawn a QE agent of the specified type",
|
|
242
|
+
handler=agent_spawn,
|
|
243
|
+
domain=ToolDomain.CORE,
|
|
244
|
+
schema={
|
|
245
|
+
"type": "object",
|
|
246
|
+
"properties": {"agent_type": {"type": "string"}, "config": {"type": "object"}},
|
|
247
|
+
"required": ["agent_type"],
|
|
248
|
+
},
|
|
249
|
+
keywords=["spawn", "agent", "create"],
|
|
250
|
+
),
|
|
251
|
+
MCPTool(
|
|
252
|
+
name="fleet_status",
|
|
253
|
+
description="Get the status of the QE fleet",
|
|
254
|
+
handler=fleet_status,
|
|
255
|
+
domain=ToolDomain.CORE,
|
|
256
|
+
keywords=["status", "health", "fleet"],
|
|
257
|
+
),
|
|
258
|
+
MCPTool(
|
|
259
|
+
name="test_generate",
|
|
260
|
+
description="Generate tests for a target",
|
|
261
|
+
handler=test_generate,
|
|
262
|
+
domain=ToolDomain.CORE,
|
|
263
|
+
schema={
|
|
264
|
+
"type": "object",
|
|
265
|
+
"properties": {
|
|
266
|
+
"target": {"type": "string"},
|
|
267
|
+
"framework": {"type": "string"},
|
|
268
|
+
"coverage_goal": {"type": "number"},
|
|
269
|
+
},
|
|
270
|
+
"required": ["target"],
|
|
271
|
+
},
|
|
272
|
+
keywords=["generate", "test", "create"],
|
|
273
|
+
),
|
|
274
|
+
MCPTool(
|
|
275
|
+
name="test_execute",
|
|
276
|
+
description="Execute tests",
|
|
277
|
+
handler=test_execute,
|
|
278
|
+
domain=ToolDomain.CORE,
|
|
279
|
+
schema={
|
|
280
|
+
"type": "object",
|
|
281
|
+
"properties": {
|
|
282
|
+
"pattern": {"type": "string"},
|
|
283
|
+
"parallel": {"type": "boolean"},
|
|
284
|
+
"workers": {"type": "integer"},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
keywords=["run", "execute", "test"],
|
|
288
|
+
),
|
|
289
|
+
MCPTool(
|
|
290
|
+
name="test_report",
|
|
291
|
+
description="Generate test report",
|
|
292
|
+
handler=test_report,
|
|
293
|
+
domain=ToolDomain.CORE,
|
|
294
|
+
schema={
|
|
295
|
+
"type": "object",
|
|
296
|
+
"properties": {
|
|
297
|
+
"format": {"type": "string", "enum": ["html", "json", "junit", "markdown"]},
|
|
298
|
+
"output_path": {"type": "string"},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
keywords=["report", "test", "results"],
|
|
302
|
+
),
|
|
303
|
+
MCPTool(
|
|
304
|
+
name="memory_store",
|
|
305
|
+
description="Store a value in agent memory",
|
|
306
|
+
handler=memory_store,
|
|
307
|
+
domain=ToolDomain.CORE,
|
|
308
|
+
schema={
|
|
309
|
+
"type": "object",
|
|
310
|
+
"properties": {
|
|
311
|
+
"key": {"type": "string"},
|
|
312
|
+
"value": {},
|
|
313
|
+
"namespace": {"type": "string"},
|
|
314
|
+
"ttl": {"type": "integer"},
|
|
315
|
+
"persist": {"type": "boolean"},
|
|
316
|
+
},
|
|
317
|
+
"required": ["key", "value"],
|
|
318
|
+
},
|
|
319
|
+
keywords=["store", "memory", "save"],
|
|
320
|
+
),
|
|
321
|
+
MCPTool(
|
|
322
|
+
name="memory_retrieve",
|
|
323
|
+
description="Retrieve a value from agent memory",
|
|
324
|
+
handler=memory_retrieve,
|
|
325
|
+
domain=ToolDomain.CORE,
|
|
326
|
+
schema={
|
|
327
|
+
"type": "object",
|
|
328
|
+
"properties": {"key": {"type": "string"}, "namespace": {"type": "string"}},
|
|
329
|
+
"required": ["key"],
|
|
330
|
+
},
|
|
331
|
+
keywords=["get", "retrieve", "memory"],
|
|
332
|
+
),
|
|
333
|
+
MCPTool(
|
|
334
|
+
name="memory_query",
|
|
335
|
+
description="Query memory with a pattern",
|
|
336
|
+
handler=memory_query,
|
|
337
|
+
domain=ToolDomain.CORE,
|
|
338
|
+
schema={
|
|
339
|
+
"type": "object",
|
|
340
|
+
"properties": {
|
|
341
|
+
"pattern": {"type": "string"},
|
|
342
|
+
"namespace": {"type": "string"},
|
|
343
|
+
"limit": {"type": "integer"},
|
|
344
|
+
},
|
|
345
|
+
"required": ["pattern"],
|
|
346
|
+
},
|
|
347
|
+
keywords=["query", "search", "memory"],
|
|
348
|
+
),
|
|
349
|
+
MCPTool(
|
|
350
|
+
name="quality_analyze",
|
|
351
|
+
description="Analyze code quality",
|
|
352
|
+
handler=quality_analyze,
|
|
353
|
+
domain=ToolDomain.CORE,
|
|
354
|
+
schema={
|
|
355
|
+
"type": "object",
|
|
356
|
+
"properties": {
|
|
357
|
+
"target": {"type": "string"},
|
|
358
|
+
"checks": {"type": "array", "items": {"type": "string"}},
|
|
359
|
+
},
|
|
360
|
+
"required": ["target"],
|
|
361
|
+
},
|
|
362
|
+
keywords=["analyze", "quality", "check"],
|
|
363
|
+
),
|
|
364
|
+
MCPTool(
|
|
365
|
+
name="task_orchestrate",
|
|
366
|
+
description="Orchestrate a task across agents",
|
|
367
|
+
handler=task_orchestrate,
|
|
368
|
+
domain=ToolDomain.CORE,
|
|
369
|
+
schema={
|
|
370
|
+
"type": "object",
|
|
371
|
+
"properties": {
|
|
372
|
+
"task": {"type": "string"},
|
|
373
|
+
"agents": {"type": "array", "items": {"type": "string"}},
|
|
374
|
+
"priority": {"type": "string"},
|
|
375
|
+
},
|
|
376
|
+
"required": ["task"],
|
|
377
|
+
},
|
|
378
|
+
keywords=["orchestrate", "task", "coordinate"],
|
|
379
|
+
),
|
|
380
|
+
MCPTool(
|
|
381
|
+
name="task_status",
|
|
382
|
+
description="Get task status",
|
|
383
|
+
handler=task_status,
|
|
384
|
+
domain=ToolDomain.CORE,
|
|
385
|
+
schema={
|
|
386
|
+
"type": "object",
|
|
387
|
+
"properties": {"task_id": {"type": "string"}},
|
|
388
|
+
"required": ["task_id"],
|
|
389
|
+
},
|
|
390
|
+
keywords=["status", "task"],
|
|
391
|
+
),
|
|
392
|
+
MCPTool(
|
|
393
|
+
name="tools_discover",
|
|
394
|
+
description="Discover available tools",
|
|
395
|
+
handler=tools_discover,
|
|
396
|
+
domain=ToolDomain.CORE,
|
|
397
|
+
schema={
|
|
398
|
+
"type": "object",
|
|
399
|
+
"properties": {"domain": {"type": "string"}, "keyword": {"type": "string"}},
|
|
400
|
+
},
|
|
401
|
+
keywords=["discover", "tools", "list"],
|
|
402
|
+
),
|
|
403
|
+
MCPTool(
|
|
404
|
+
name="tools_load_domain",
|
|
405
|
+
description="Load tools for a specific domain",
|
|
406
|
+
handler=tools_load_domain,
|
|
407
|
+
domain=ToolDomain.CORE,
|
|
408
|
+
schema={
|
|
409
|
+
"type": "object",
|
|
410
|
+
"properties": {"domain": {"type": "string"}},
|
|
411
|
+
"required": ["domain"],
|
|
412
|
+
},
|
|
413
|
+
keywords=["load", "domain", "tools"],
|
|
414
|
+
),
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
for tool in tools:
|
|
418
|
+
registry.register(tool)
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Tool Registry - Central registry for MCP tools with lazy loading.
|
|
3
|
+
|
|
4
|
+
Implements 87% context reduction by only loading domain tools when needed.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any, Callable, Dict, List, Optional, Set
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ToolDomain(str, Enum):
|
|
16
|
+
"""Tool domains for lazy loading."""
|
|
17
|
+
|
|
18
|
+
CORE = "core" # Always loaded
|
|
19
|
+
TESTING = "testing" # Test generation, execution
|
|
20
|
+
SECURITY = "security" # Vulnerability scanning
|
|
21
|
+
PERFORMANCE = "performance" # Benchmarking
|
|
22
|
+
COVERAGE = "coverage" # Coverage analysis
|
|
23
|
+
QUALITY = "quality" # Quality gates
|
|
24
|
+
FLAKY = "flaky" # Flaky test detection
|
|
25
|
+
ACCESSIBILITY = "accessibility" # A11y testing
|
|
26
|
+
LEARNING = "learning" # Pattern management
|
|
27
|
+
ADVANCED = "advanced" # Mutation, contract testing
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class MCPTool:
|
|
32
|
+
"""Definition of an MCP tool."""
|
|
33
|
+
|
|
34
|
+
name: str
|
|
35
|
+
description: str
|
|
36
|
+
handler: Callable[..., Any]
|
|
37
|
+
domain: ToolDomain
|
|
38
|
+
schema: Optional[Dict[str, Any]] = None
|
|
39
|
+
keywords: List[str] = field(default_factory=list)
|
|
40
|
+
examples: List[str] = field(default_factory=list)
|
|
41
|
+
requires_auth: bool = False
|
|
42
|
+
timeout: int = 30
|
|
43
|
+
|
|
44
|
+
def to_mcp_format(self) -> Dict[str, Any]:
|
|
45
|
+
"""Convert to MCP tool format."""
|
|
46
|
+
result = {
|
|
47
|
+
"name": self.name,
|
|
48
|
+
"description": self.description,
|
|
49
|
+
}
|
|
50
|
+
if self.schema:
|
|
51
|
+
result["inputSchema"] = self.schema
|
|
52
|
+
return result
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MCPToolRegistry:
|
|
56
|
+
"""
|
|
57
|
+
Registry for MCP tools with lazy loading.
|
|
58
|
+
|
|
59
|
+
Only core tools are loaded initially. Domain-specific tools
|
|
60
|
+
are loaded on-demand when requested or when keywords match.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self):
|
|
64
|
+
self._tools: Dict[str, MCPTool] = {}
|
|
65
|
+
self._domains: Dict[ToolDomain, List[str]] = {d: [] for d in ToolDomain}
|
|
66
|
+
self._loaded_domains: Set[ToolDomain] = {ToolDomain.CORE}
|
|
67
|
+
self._keyword_index: Dict[str, Set[str]] = {}
|
|
68
|
+
|
|
69
|
+
def register(self, tool: MCPTool) -> None:
|
|
70
|
+
"""Register a tool."""
|
|
71
|
+
self._tools[tool.name] = tool
|
|
72
|
+
self._domains[tool.domain].append(tool.name)
|
|
73
|
+
|
|
74
|
+
# Index keywords
|
|
75
|
+
for keyword in tool.keywords:
|
|
76
|
+
if keyword not in self._keyword_index:
|
|
77
|
+
self._keyword_index[keyword] = set()
|
|
78
|
+
self._keyword_index[keyword].add(tool.name)
|
|
79
|
+
|
|
80
|
+
logger.debug(f"Registered tool: {tool.name} in domain {tool.domain.value}")
|
|
81
|
+
|
|
82
|
+
def get_tool(self, name: str) -> Optional[MCPTool]:
|
|
83
|
+
"""Get a tool by name."""
|
|
84
|
+
return self._tools.get(name)
|
|
85
|
+
|
|
86
|
+
def list_tools(
|
|
87
|
+
self, domain: Optional[ToolDomain] = None, loaded_only: bool = False
|
|
88
|
+
) -> List[MCPTool]:
|
|
89
|
+
"""List tools, optionally filtered by domain."""
|
|
90
|
+
if domain:
|
|
91
|
+
tool_names = self._domains.get(domain, [])
|
|
92
|
+
else:
|
|
93
|
+
tool_names = list(self._tools.keys())
|
|
94
|
+
|
|
95
|
+
tools = []
|
|
96
|
+
for name in tool_names:
|
|
97
|
+
tool = self._tools.get(name)
|
|
98
|
+
if tool:
|
|
99
|
+
if loaded_only and tool.domain not in self._loaded_domains:
|
|
100
|
+
continue
|
|
101
|
+
tools.append(tool)
|
|
102
|
+
|
|
103
|
+
return tools
|
|
104
|
+
|
|
105
|
+
def list_domains(self) -> List[Dict[str, Any]]:
|
|
106
|
+
"""List available domains with tool counts."""
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
"domain": domain.value,
|
|
110
|
+
"tool_count": len(self._domains[domain]),
|
|
111
|
+
"loaded": domain in self._loaded_domains,
|
|
112
|
+
}
|
|
113
|
+
for domain in ToolDomain
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
def load_domain(self, domain: ToolDomain) -> List[MCPTool]:
|
|
117
|
+
"""Load a domain's tools."""
|
|
118
|
+
if domain in self._loaded_domains:
|
|
119
|
+
return self.list_tools(domain)
|
|
120
|
+
|
|
121
|
+
self._loaded_domains.add(domain)
|
|
122
|
+
logger.info(f"Loaded domain: {domain.value}")
|
|
123
|
+
|
|
124
|
+
return self.list_tools(domain)
|
|
125
|
+
|
|
126
|
+
def get_loaded_domains(self) -> List[ToolDomain]:
|
|
127
|
+
"""Get list of loaded domains."""
|
|
128
|
+
return list(self._loaded_domains)
|
|
129
|
+
|
|
130
|
+
def auto_load_for_keywords(self, text: str) -> List[ToolDomain]:
|
|
131
|
+
"""Auto-load domains based on keyword detection in text."""
|
|
132
|
+
text_lower = text.lower()
|
|
133
|
+
domains_to_load: Set[ToolDomain] = set()
|
|
134
|
+
|
|
135
|
+
# Keyword patterns for auto-loading
|
|
136
|
+
domain_keywords = {
|
|
137
|
+
ToolDomain.TESTING: ["test", "generate test", "run test", "test suite"],
|
|
138
|
+
ToolDomain.SECURITY: ["security", "vulnerability", "scan", "owasp", "cve"],
|
|
139
|
+
ToolDomain.PERFORMANCE: ["performance", "benchmark", "slow", "bottleneck"],
|
|
140
|
+
ToolDomain.COVERAGE: ["coverage", "gap", "uncovered"],
|
|
141
|
+
ToolDomain.QUALITY: ["quality", "gate", "deploy", "readiness"],
|
|
142
|
+
ToolDomain.FLAKY: ["flaky", "unstable", "intermittent"],
|
|
143
|
+
ToolDomain.ACCESSIBILITY: ["accessibility", "a11y", "wcag", "screen reader"],
|
|
144
|
+
ToolDomain.LEARNING: ["pattern", "learn", "train"],
|
|
145
|
+
ToolDomain.ADVANCED: ["mutation", "contract", "api spec"],
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for domain, keywords in domain_keywords.items():
|
|
149
|
+
if domain in self._loaded_domains:
|
|
150
|
+
continue
|
|
151
|
+
for keyword in keywords:
|
|
152
|
+
if keyword in text_lower:
|
|
153
|
+
domains_to_load.add(domain)
|
|
154
|
+
break
|
|
155
|
+
|
|
156
|
+
# Load detected domains
|
|
157
|
+
for domain in domains_to_load:
|
|
158
|
+
self.load_domain(domain)
|
|
159
|
+
|
|
160
|
+
return list(domains_to_load)
|
|
161
|
+
|
|
162
|
+
def get_tools_for_mcp(self) -> List[Dict[str, Any]]:
|
|
163
|
+
"""Get all loaded tools in MCP format."""
|
|
164
|
+
tools = []
|
|
165
|
+
for tool in self.list_tools(loaded_only=True):
|
|
166
|
+
tools.append(tool.to_mcp_format())
|
|
167
|
+
return tools
|
|
168
|
+
|
|
169
|
+
async def invoke(self, tool_name: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
170
|
+
"""Invoke a tool by name."""
|
|
171
|
+
tool = self.get_tool(tool_name)
|
|
172
|
+
if not tool:
|
|
173
|
+
return {"error": f"Tool not found: {tool_name}"}
|
|
174
|
+
|
|
175
|
+
# Auto-load domain if needed
|
|
176
|
+
if tool.domain not in self._loaded_domains:
|
|
177
|
+
self.load_domain(tool.domain)
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
result = await tool.handler(**params)
|
|
181
|
+
return {"result": result}
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.exception(f"Tool invocation failed: {tool_name}")
|
|
184
|
+
return {"error": str(e)}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# Global registry instance
|
|
188
|
+
_registry: Optional[MCPToolRegistry] = None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def get_registry() -> MCPToolRegistry:
|
|
192
|
+
"""Get the global tool registry."""
|
|
193
|
+
global _registry
|
|
194
|
+
if _registry is None:
|
|
195
|
+
_registry = MCPToolRegistry()
|
|
196
|
+
# Register core tools by default
|
|
197
|
+
from .core_tools import register_core_tools
|
|
198
|
+
|
|
199
|
+
register_core_tools(_registry)
|
|
200
|
+
return _registry
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def register_tool(tool: MCPTool) -> None:
|
|
204
|
+
"""Register a tool in the global registry."""
|
|
205
|
+
get_registry().register(tool)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_tool(name: str) -> Optional[MCPTool]:
|
|
209
|
+
"""Get a tool from the global registry."""
|
|
210
|
+
return get_registry().get_tool(name)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def list_tools(domain: Optional[ToolDomain] = None) -> List[MCPTool]:
|
|
214
|
+
"""List tools from the global registry."""
|
|
215
|
+
return get_registry().list_tools(domain)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def list_domains() -> List[Dict[str, Any]]:
|
|
219
|
+
"""List domains from the global registry."""
|
|
220
|
+
return get_registry().list_domains()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def load_domain(domain: ToolDomain) -> List[MCPTool]:
|
|
224
|
+
"""Load a domain in the global registry."""
|
|
225
|
+
return get_registry().load_domain(domain)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_loaded_domains() -> List[ToolDomain]:
|
|
229
|
+
"""Get loaded domains from the global registry."""
|
|
230
|
+
return get_registry().get_loaded_domains()
|