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
superqode/tools/base.py
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Tool System - Minimal, Standard Interface.
|
|
3
|
+
|
|
4
|
+
Design:
|
|
5
|
+
- OpenAI-compatible tool format (works with any provider via LiteLLM)
|
|
6
|
+
- No opinionated prompts - just tool name, description, parameters
|
|
7
|
+
- Transparent execution - what you call is what runs
|
|
8
|
+
|
|
9
|
+
Performance features:
|
|
10
|
+
- Streaming output support for long-running tools
|
|
11
|
+
- Progress callbacks for UI updates
|
|
12
|
+
- Async-first design for non-blocking execution
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from abc import ABC, abstractmethod
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Any, Awaitable, Dict, List, Optional, Callable, Union
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
import json
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Type aliases for callbacks
|
|
23
|
+
OutputCallback = Callable[[str], Union[None, Awaitable[None]]]
|
|
24
|
+
ProgressCallback = Callable[[float, str], Union[None, Awaitable[None]]]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ToolContext:
|
|
29
|
+
"""Context passed to tool execution.
|
|
30
|
+
|
|
31
|
+
Minimal context - just what's needed for execution.
|
|
32
|
+
No hidden state, no magic.
|
|
33
|
+
|
|
34
|
+
Streaming support:
|
|
35
|
+
on_output: Called with output chunks as they're produced
|
|
36
|
+
on_progress: Called with (progress_fraction, status_message)
|
|
37
|
+
|
|
38
|
+
tool_registry: Set by the agent loop so tools like BatchTool can execute other tools.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
session_id: str
|
|
42
|
+
working_directory: Path
|
|
43
|
+
# Optional: for permission checks (user can enable/disable)
|
|
44
|
+
require_confirmation: bool = False
|
|
45
|
+
# Callback for streaming output (optional) - can be sync or async
|
|
46
|
+
on_output: Optional[OutputCallback] = None
|
|
47
|
+
# Callback for progress updates (0.0 to 1.0, plus status message)
|
|
48
|
+
on_progress: Optional[ProgressCallback] = None
|
|
49
|
+
# Optional: registry for BatchTool to resolve and run other tools
|
|
50
|
+
tool_registry: Optional["ToolRegistry"] = None
|
|
51
|
+
# Delegation depth for SubAgentTool (0=top, incremented for child sessions; max 3)
|
|
52
|
+
delegation_depth: int = 0
|
|
53
|
+
|
|
54
|
+
async def emit_output(self, text: str) -> None:
|
|
55
|
+
"""Emit output to the callback if set."""
|
|
56
|
+
if self.on_output:
|
|
57
|
+
result = self.on_output(text)
|
|
58
|
+
if hasattr(result, "__await__"):
|
|
59
|
+
await result
|
|
60
|
+
|
|
61
|
+
async def emit_progress(self, fraction: float, status: str = "") -> None:
|
|
62
|
+
"""Emit progress update to the callback if set.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
fraction: Progress from 0.0 to 1.0
|
|
66
|
+
status: Optional status message
|
|
67
|
+
"""
|
|
68
|
+
if self.on_progress:
|
|
69
|
+
result = self.on_progress(fraction, status)
|
|
70
|
+
if hasattr(result, "__await__"):
|
|
71
|
+
await result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class ToolResult:
|
|
76
|
+
"""Result from tool execution.
|
|
77
|
+
|
|
78
|
+
Simple, transparent result format.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
success: bool
|
|
82
|
+
output: str
|
|
83
|
+
error: Optional[str] = None
|
|
84
|
+
# Metadata for debugging/logging (not sent to model)
|
|
85
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
86
|
+
|
|
87
|
+
def to_message(self) -> str:
|
|
88
|
+
"""Convert to message content for the model."""
|
|
89
|
+
if self.success:
|
|
90
|
+
return self.output
|
|
91
|
+
else:
|
|
92
|
+
return f"Error: {self.error}\n{self.output}" if self.output else f"Error: {self.error}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Tool(ABC):
|
|
96
|
+
"""Base class for all tools.
|
|
97
|
+
|
|
98
|
+
Minimal interface:
|
|
99
|
+
- name: Tool identifier
|
|
100
|
+
- description: What it does (sent to model)
|
|
101
|
+
- parameters: JSON Schema (sent to model)
|
|
102
|
+
- execute(): Run the tool
|
|
103
|
+
|
|
104
|
+
NO:
|
|
105
|
+
- Complex initialization
|
|
106
|
+
- Hidden system prompts
|
|
107
|
+
- Opinionated formatting
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def name(self) -> str:
|
|
113
|
+
"""Tool name (e.g., 'read_file')."""
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def description(self) -> str:
|
|
119
|
+
"""Tool description for the model. Keep it simple and factual."""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def parameters(self) -> Dict[str, Any]:
|
|
125
|
+
"""JSON Schema for parameters."""
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
async def execute(self, args: Dict[str, Any], ctx: ToolContext) -> ToolResult:
|
|
130
|
+
"""Execute the tool with given arguments."""
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
def to_openai_format(self) -> Dict[str, Any]:
|
|
134
|
+
"""Convert to OpenAI function calling format.
|
|
135
|
+
|
|
136
|
+
This is the standard format that works with:
|
|
137
|
+
- OpenAI (GPT-4, GPT-5)
|
|
138
|
+
- Anthropic (Claude)
|
|
139
|
+
- Google (Gemini)
|
|
140
|
+
- All LiteLLM-supported providers
|
|
141
|
+
"""
|
|
142
|
+
return {
|
|
143
|
+
"type": "function",
|
|
144
|
+
"function": {
|
|
145
|
+
"name": self.name,
|
|
146
|
+
"description": self.description,
|
|
147
|
+
"parameters": self.parameters,
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ToolRegistry:
|
|
153
|
+
"""Registry of available tools.
|
|
154
|
+
|
|
155
|
+
Simple dict-based registry. No magic, no auto-discovery.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
def __init__(self):
|
|
159
|
+
self._tools: Dict[str, Tool] = {}
|
|
160
|
+
|
|
161
|
+
def register(self, tool: Tool) -> None:
|
|
162
|
+
"""Register a tool."""
|
|
163
|
+
self._tools[tool.name] = tool
|
|
164
|
+
|
|
165
|
+
def get(self, name: str) -> Optional[Tool]:
|
|
166
|
+
"""Get a tool by name."""
|
|
167
|
+
return self._tools.get(name)
|
|
168
|
+
|
|
169
|
+
def list(self) -> List[Tool]:
|
|
170
|
+
"""List all registered tools."""
|
|
171
|
+
return list(self._tools.values())
|
|
172
|
+
|
|
173
|
+
def to_openai_format(self) -> List[Dict[str, Any]]:
|
|
174
|
+
"""Get all tools in OpenAI format."""
|
|
175
|
+
return [tool.to_openai_format() for tool in self._tools.values()]
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def default(cls) -> "ToolRegistry":
|
|
179
|
+
"""Create registry with default minimal tools."""
|
|
180
|
+
from .file_tools import ReadFileTool, WriteFileTool, ListDirectoryTool
|
|
181
|
+
from .edit_tools import EditFileTool, InsertTextTool
|
|
182
|
+
from .shell_tools import BashTool
|
|
183
|
+
from .search_tools import GrepTool, GlobTool
|
|
184
|
+
|
|
185
|
+
registry = cls()
|
|
186
|
+
|
|
187
|
+
# Core file operations
|
|
188
|
+
registry.register(ReadFileTool())
|
|
189
|
+
registry.register(WriteFileTool())
|
|
190
|
+
registry.register(ListDirectoryTool())
|
|
191
|
+
|
|
192
|
+
# Editing
|
|
193
|
+
registry.register(EditFileTool())
|
|
194
|
+
registry.register(InsertTextTool())
|
|
195
|
+
|
|
196
|
+
# Shell
|
|
197
|
+
registry.register(BashTool())
|
|
198
|
+
|
|
199
|
+
# Search
|
|
200
|
+
registry.register(GrepTool())
|
|
201
|
+
registry.register(GlobTool())
|
|
202
|
+
|
|
203
|
+
return registry
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def full(cls) -> "ToolRegistry":
|
|
207
|
+
"""Create registry with all available tools."""
|
|
208
|
+
from .file_tools import ReadFileTool, WriteFileTool, ListDirectoryTool
|
|
209
|
+
from .edit_tools import EditFileTool, InsertTextTool, PatchTool, MultiEditTool
|
|
210
|
+
from .shell_tools import BashTool
|
|
211
|
+
from .search_tools import GrepTool, GlobTool, CodeSearchTool
|
|
212
|
+
from .diagnostics import DiagnosticsTool
|
|
213
|
+
from .network_tools import FetchTool, DownloadTool
|
|
214
|
+
from .agent_tools import SubAgentTool, TaskCoordinatorTool
|
|
215
|
+
from .lsp_tools import LSPTool
|
|
216
|
+
from .web_tools import WebSearchTool, WebFetchTool
|
|
217
|
+
from .question_tool import QuestionTool, ConfirmTool
|
|
218
|
+
from .todo_tools import TodoWriteTool, TodoReadTool
|
|
219
|
+
from .batch_tool import BatchTool
|
|
220
|
+
|
|
221
|
+
registry = cls()
|
|
222
|
+
|
|
223
|
+
# Core file operations
|
|
224
|
+
registry.register(ReadFileTool())
|
|
225
|
+
registry.register(WriteFileTool())
|
|
226
|
+
registry.register(ListDirectoryTool())
|
|
227
|
+
|
|
228
|
+
# Editing (basic + advanced)
|
|
229
|
+
registry.register(EditFileTool())
|
|
230
|
+
registry.register(InsertTextTool())
|
|
231
|
+
registry.register(PatchTool())
|
|
232
|
+
registry.register(MultiEditTool())
|
|
233
|
+
|
|
234
|
+
# TODO management
|
|
235
|
+
registry.register(TodoWriteTool())
|
|
236
|
+
registry.register(TodoReadTool())
|
|
237
|
+
|
|
238
|
+
# Batch (parallel tool execution)
|
|
239
|
+
registry.register(BatchTool())
|
|
240
|
+
|
|
241
|
+
# Shell
|
|
242
|
+
registry.register(BashTool())
|
|
243
|
+
|
|
244
|
+
# Search (basic + semantic)
|
|
245
|
+
registry.register(GrepTool())
|
|
246
|
+
registry.register(GlobTool())
|
|
247
|
+
registry.register(CodeSearchTool())
|
|
248
|
+
|
|
249
|
+
# Diagnostics
|
|
250
|
+
registry.register(DiagnosticsTool())
|
|
251
|
+
|
|
252
|
+
# Network
|
|
253
|
+
registry.register(FetchTool())
|
|
254
|
+
registry.register(DownloadTool())
|
|
255
|
+
|
|
256
|
+
# Web tools (search + enhanced fetch)
|
|
257
|
+
registry.register(WebSearchTool())
|
|
258
|
+
registry.register(WebFetchTool())
|
|
259
|
+
|
|
260
|
+
# Agent tools
|
|
261
|
+
registry.register(SubAgentTool())
|
|
262
|
+
registry.register(TaskCoordinatorTool())
|
|
263
|
+
|
|
264
|
+
# LSP tools
|
|
265
|
+
registry.register(LSPTool())
|
|
266
|
+
|
|
267
|
+
# Interactive tools
|
|
268
|
+
registry.register(QuestionTool())
|
|
269
|
+
registry.register(ConfirmTool())
|
|
270
|
+
|
|
271
|
+
return registry
|
|
272
|
+
|
|
273
|
+
@classmethod
|
|
274
|
+
def standard(cls) -> "ToolRegistry":
|
|
275
|
+
"""Create registry with standard tools (no network/agent)."""
|
|
276
|
+
from .file_tools import ReadFileTool, WriteFileTool, ListDirectoryTool
|
|
277
|
+
from .edit_tools import EditFileTool, InsertTextTool, PatchTool, MultiEditTool
|
|
278
|
+
from .shell_tools import BashTool
|
|
279
|
+
from .search_tools import GrepTool, GlobTool, CodeSearchTool
|
|
280
|
+
from .diagnostics import DiagnosticsTool
|
|
281
|
+
from .lsp_tools import LSPTool
|
|
282
|
+
from .question_tool import QuestionTool, ConfirmTool
|
|
283
|
+
from .todo_tools import TodoWriteTool, TodoReadTool
|
|
284
|
+
from .batch_tool import BatchTool
|
|
285
|
+
|
|
286
|
+
registry = cls()
|
|
287
|
+
|
|
288
|
+
# Core file operations
|
|
289
|
+
registry.register(ReadFileTool())
|
|
290
|
+
registry.register(WriteFileTool())
|
|
291
|
+
registry.register(ListDirectoryTool())
|
|
292
|
+
|
|
293
|
+
# Editing
|
|
294
|
+
registry.register(EditFileTool())
|
|
295
|
+
registry.register(InsertTextTool())
|
|
296
|
+
registry.register(PatchTool())
|
|
297
|
+
registry.register(MultiEditTool())
|
|
298
|
+
|
|
299
|
+
# TODO management
|
|
300
|
+
registry.register(TodoWriteTool())
|
|
301
|
+
registry.register(TodoReadTool())
|
|
302
|
+
|
|
303
|
+
# Batch (parallel tool execution)
|
|
304
|
+
registry.register(BatchTool())
|
|
305
|
+
|
|
306
|
+
# Shell
|
|
307
|
+
registry.register(BashTool())
|
|
308
|
+
|
|
309
|
+
# Search
|
|
310
|
+
registry.register(GrepTool())
|
|
311
|
+
registry.register(GlobTool())
|
|
312
|
+
registry.register(CodeSearchTool())
|
|
313
|
+
|
|
314
|
+
# Diagnostics
|
|
315
|
+
registry.register(DiagnosticsTool())
|
|
316
|
+
|
|
317
|
+
# LSP tools
|
|
318
|
+
registry.register(LSPTool())
|
|
319
|
+
|
|
320
|
+
# Interactive tools
|
|
321
|
+
registry.register(QuestionTool())
|
|
322
|
+
registry.register(ConfirmTool())
|
|
323
|
+
|
|
324
|
+
return registry
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Batch Tool - Execute multiple tools in parallel.
|
|
3
|
+
Enables parallel execution of up to 10 tools. Recursive batch calls are blocked.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from typing import Any, Dict, List
|
|
8
|
+
|
|
9
|
+
from .base import Tool, ToolResult, ToolContext
|
|
10
|
+
|
|
11
|
+
MAX_CONCURRENT = 10
|
|
12
|
+
BATCH_TOOL_NAME = "batch"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BatchTool(Tool):
|
|
16
|
+
"""Execute multiple tool calls in parallel."""
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def name(self) -> str:
|
|
20
|
+
return BATCH_TOOL_NAME
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def description(self) -> str:
|
|
24
|
+
return (
|
|
25
|
+
"Execute multiple tool calls in parallel (up to 10). "
|
|
26
|
+
"Provide a list of {tool, parameters} objects. "
|
|
27
|
+
"Useful for parallel reads, searches, or independent operations. "
|
|
28
|
+
"Cannot include batch itself. Results are returned together."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def parameters(self) -> Dict[str, Any]:
|
|
33
|
+
return {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"tool_calls": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"description": "Array of tool calls to execute in parallel",
|
|
39
|
+
"minItems": 1,
|
|
40
|
+
"maxItems": MAX_CONCURRENT,
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"tool": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Name of the tool to execute",
|
|
47
|
+
},
|
|
48
|
+
"parameters": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "Parameters for the tool",
|
|
51
|
+
"additionalProperties": True,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
"required": ["tool", "parameters"],
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"required": ["tool_calls"],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async def execute(self, args: Dict[str, Any], ctx: ToolContext) -> ToolResult:
|
|
62
|
+
tool_calls = args.get("tool_calls", [])
|
|
63
|
+
if not tool_calls:
|
|
64
|
+
return ToolResult(success=False, output="", error="At least one tool call is required")
|
|
65
|
+
|
|
66
|
+
if len(tool_calls) > MAX_CONCURRENT:
|
|
67
|
+
return ToolResult(
|
|
68
|
+
success=False,
|
|
69
|
+
output="",
|
|
70
|
+
error=f"At most {MAX_CONCURRENT} tool calls allowed, got {len(tool_calls)}",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Block recursive batch
|
|
74
|
+
names = [t.get("tool") or t.get("tool_name") for t in tool_calls]
|
|
75
|
+
if BATCH_TOOL_NAME in names:
|
|
76
|
+
return ToolResult(
|
|
77
|
+
success=False,
|
|
78
|
+
output="",
|
|
79
|
+
error="Recursive batch calls are not allowed",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
registry = getattr(ctx, "tool_registry", None)
|
|
83
|
+
if not registry:
|
|
84
|
+
return ToolResult(
|
|
85
|
+
success=False,
|
|
86
|
+
output="",
|
|
87
|
+
error="Batch tool requires a tool registry in context",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
async def run_one(tc: Dict[str, Any]) -> ToolResult:
|
|
91
|
+
name = tc.get("tool") or tc.get("tool_name", "")
|
|
92
|
+
params = tc.get("parameters") or tc.get("params") or {}
|
|
93
|
+
tool = registry.get(name)
|
|
94
|
+
if not tool:
|
|
95
|
+
return ToolResult(success=False, output="", error=f"Unknown tool: {name}")
|
|
96
|
+
try:
|
|
97
|
+
return await tool.execute(params, ctx)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
return ToolResult(success=False, output="", error=f"Tool error: {str(e)}")
|
|
100
|
+
|
|
101
|
+
tasks = [run_one(t) for t in tool_calls]
|
|
102
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
103
|
+
|
|
104
|
+
# Convert exceptions to ToolResult
|
|
105
|
+
out_results: List[ToolResult] = []
|
|
106
|
+
for i, r in enumerate(results):
|
|
107
|
+
if isinstance(r, Exception):
|
|
108
|
+
name = tool_calls[i].get("tool", "?")
|
|
109
|
+
out_results.append(ToolResult(success=False, output="", error=f"{name}: {str(r)}"))
|
|
110
|
+
else:
|
|
111
|
+
out_results.append(r)
|
|
112
|
+
|
|
113
|
+
# Format output
|
|
114
|
+
lines = []
|
|
115
|
+
for i, (tc, res) in enumerate(zip(tool_calls, out_results)):
|
|
116
|
+
name = tc.get("tool", "?")
|
|
117
|
+
status = "OK" if res.success else "FAILED"
|
|
118
|
+
lines.append(f"[{i + 1}] {name}: {status}")
|
|
119
|
+
lines.append(res.output if res.success else (res.error or ""))
|
|
120
|
+
lines.append("")
|
|
121
|
+
output = "\n".join(lines).strip()
|
|
122
|
+
|
|
123
|
+
return ToolResult(
|
|
124
|
+
success=all(r.success for r in out_results),
|
|
125
|
+
output=output,
|
|
126
|
+
error=None if all(r.success for r in out_results) else "One or more tools failed",
|
|
127
|
+
metadata={
|
|
128
|
+
"results": [
|
|
129
|
+
{"success": r.success, "output": r.output, "error": r.error}
|
|
130
|
+
for r in out_results
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
)
|