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.
Files changed (288) hide show
  1. superqode/__init__.py +33 -0
  2. superqode/acp/__init__.py +23 -0
  3. superqode/acp/client.py +913 -0
  4. superqode/acp/permission_screen.py +457 -0
  5. superqode/acp/types.py +480 -0
  6. superqode/acp_discovery.py +856 -0
  7. superqode/agent/__init__.py +22 -0
  8. superqode/agent/edit_strategies.py +334 -0
  9. superqode/agent/loop.py +892 -0
  10. superqode/agent/qe_report_templates.py +39 -0
  11. superqode/agent/system_prompts.py +353 -0
  12. superqode/agent_output.py +721 -0
  13. superqode/agent_stream.py +953 -0
  14. superqode/agents/__init__.py +59 -0
  15. superqode/agents/acp_registry.py +305 -0
  16. superqode/agents/client.py +249 -0
  17. superqode/agents/data/augmentcode.com.toml +51 -0
  18. superqode/agents/data/cagent.dev.toml +51 -0
  19. superqode/agents/data/claude.com.toml +60 -0
  20. superqode/agents/data/codeassistant.dev.toml +51 -0
  21. superqode/agents/data/codex.openai.com.toml +57 -0
  22. superqode/agents/data/fastagent.ai.toml +66 -0
  23. superqode/agents/data/geminicli.com.toml +77 -0
  24. superqode/agents/data/goose.block.xyz.toml +54 -0
  25. superqode/agents/data/junie.jetbrains.com.toml +56 -0
  26. superqode/agents/data/kimi.moonshot.cn.toml +57 -0
  27. superqode/agents/data/llmlingagent.dev.toml +51 -0
  28. superqode/agents/data/molt.bot.toml +49 -0
  29. superqode/agents/data/opencode.ai.toml +60 -0
  30. superqode/agents/data/stakpak.dev.toml +51 -0
  31. superqode/agents/data/vtcode.dev.toml +51 -0
  32. superqode/agents/discovery.py +266 -0
  33. superqode/agents/messaging.py +160 -0
  34. superqode/agents/persona.py +166 -0
  35. superqode/agents/registry.py +421 -0
  36. superqode/agents/schema.py +72 -0
  37. superqode/agents/unified.py +367 -0
  38. superqode/app/__init__.py +111 -0
  39. superqode/app/constants.py +314 -0
  40. superqode/app/css.py +366 -0
  41. superqode/app/models.py +118 -0
  42. superqode/app/suggester.py +125 -0
  43. superqode/app/widgets.py +1591 -0
  44. superqode/app_enhanced.py +399 -0
  45. superqode/app_main.py +17187 -0
  46. superqode/approval.py +312 -0
  47. superqode/atomic.py +296 -0
  48. superqode/commands/__init__.py +1 -0
  49. superqode/commands/acp.py +965 -0
  50. superqode/commands/agents.py +180 -0
  51. superqode/commands/auth.py +278 -0
  52. superqode/commands/config.py +374 -0
  53. superqode/commands/init.py +826 -0
  54. superqode/commands/providers.py +819 -0
  55. superqode/commands/qe.py +1145 -0
  56. superqode/commands/roles.py +380 -0
  57. superqode/commands/serve.py +172 -0
  58. superqode/commands/suggestions.py +127 -0
  59. superqode/commands/superqe.py +460 -0
  60. superqode/config/__init__.py +51 -0
  61. superqode/config/loader.py +812 -0
  62. superqode/config/schema.py +498 -0
  63. superqode/core/__init__.py +111 -0
  64. superqode/core/roles.py +281 -0
  65. superqode/danger.py +386 -0
  66. superqode/data/superqode-template.yaml +1522 -0
  67. superqode/design_system.py +1080 -0
  68. superqode/dialogs/__init__.py +6 -0
  69. superqode/dialogs/base.py +39 -0
  70. superqode/dialogs/model.py +130 -0
  71. superqode/dialogs/provider.py +870 -0
  72. superqode/diff_view.py +919 -0
  73. superqode/enterprise.py +21 -0
  74. superqode/evaluation/__init__.py +25 -0
  75. superqode/evaluation/adapters.py +93 -0
  76. superqode/evaluation/behaviors.py +89 -0
  77. superqode/evaluation/engine.py +209 -0
  78. superqode/evaluation/scenarios.py +96 -0
  79. superqode/execution/__init__.py +36 -0
  80. superqode/execution/linter.py +538 -0
  81. superqode/execution/modes.py +347 -0
  82. superqode/execution/resolver.py +283 -0
  83. superqode/execution/runner.py +642 -0
  84. superqode/file_explorer.py +811 -0
  85. superqode/file_viewer.py +471 -0
  86. superqode/flash.py +183 -0
  87. superqode/guidance/__init__.py +58 -0
  88. superqode/guidance/config.py +203 -0
  89. superqode/guidance/prompts.py +71 -0
  90. superqode/harness/__init__.py +54 -0
  91. superqode/harness/accelerator.py +291 -0
  92. superqode/harness/config.py +319 -0
  93. superqode/harness/validator.py +147 -0
  94. superqode/history.py +279 -0
  95. superqode/integrations/superopt_runner.py +124 -0
  96. superqode/logging/__init__.py +49 -0
  97. superqode/logging/adapters.py +219 -0
  98. superqode/logging/formatter.py +923 -0
  99. superqode/logging/integration.py +341 -0
  100. superqode/logging/sinks.py +170 -0
  101. superqode/logging/unified_log.py +417 -0
  102. superqode/lsp/__init__.py +26 -0
  103. superqode/lsp/client.py +544 -0
  104. superqode/main.py +1069 -0
  105. superqode/mcp/__init__.py +89 -0
  106. superqode/mcp/auth_storage.py +380 -0
  107. superqode/mcp/client.py +1236 -0
  108. superqode/mcp/config.py +319 -0
  109. superqode/mcp/integration.py +337 -0
  110. superqode/mcp/oauth.py +436 -0
  111. superqode/mcp/oauth_callback.py +385 -0
  112. superqode/mcp/types.py +290 -0
  113. superqode/memory/__init__.py +31 -0
  114. superqode/memory/feedback.py +342 -0
  115. superqode/memory/store.py +522 -0
  116. superqode/notifications.py +369 -0
  117. superqode/optimization/__init__.py +5 -0
  118. superqode/optimization/config.py +33 -0
  119. superqode/permissions/__init__.py +25 -0
  120. superqode/permissions/rules.py +488 -0
  121. superqode/plan.py +323 -0
  122. superqode/providers/__init__.py +33 -0
  123. superqode/providers/gateway/__init__.py +165 -0
  124. superqode/providers/gateway/base.py +228 -0
  125. superqode/providers/gateway/litellm_gateway.py +1170 -0
  126. superqode/providers/gateway/openresponses_gateway.py +436 -0
  127. superqode/providers/health.py +297 -0
  128. superqode/providers/huggingface/__init__.py +74 -0
  129. superqode/providers/huggingface/downloader.py +472 -0
  130. superqode/providers/huggingface/endpoints.py +442 -0
  131. superqode/providers/huggingface/hub.py +531 -0
  132. superqode/providers/huggingface/inference.py +394 -0
  133. superqode/providers/huggingface/transformers_runner.py +516 -0
  134. superqode/providers/local/__init__.py +100 -0
  135. superqode/providers/local/base.py +438 -0
  136. superqode/providers/local/discovery.py +418 -0
  137. superqode/providers/local/lmstudio.py +256 -0
  138. superqode/providers/local/mlx.py +457 -0
  139. superqode/providers/local/ollama.py +486 -0
  140. superqode/providers/local/sglang.py +268 -0
  141. superqode/providers/local/tgi.py +260 -0
  142. superqode/providers/local/tool_support.py +477 -0
  143. superqode/providers/local/vllm.py +258 -0
  144. superqode/providers/manager.py +1338 -0
  145. superqode/providers/models.py +1016 -0
  146. superqode/providers/models_dev.py +578 -0
  147. superqode/providers/openresponses/__init__.py +87 -0
  148. superqode/providers/openresponses/converters/__init__.py +17 -0
  149. superqode/providers/openresponses/converters/messages.py +343 -0
  150. superqode/providers/openresponses/converters/tools.py +268 -0
  151. superqode/providers/openresponses/schema/__init__.py +56 -0
  152. superqode/providers/openresponses/schema/models.py +585 -0
  153. superqode/providers/openresponses/streaming/__init__.py +5 -0
  154. superqode/providers/openresponses/streaming/parser.py +338 -0
  155. superqode/providers/openresponses/tools/__init__.py +21 -0
  156. superqode/providers/openresponses/tools/apply_patch.py +352 -0
  157. superqode/providers/openresponses/tools/code_interpreter.py +290 -0
  158. superqode/providers/openresponses/tools/file_search.py +333 -0
  159. superqode/providers/openresponses/tools/mcp_adapter.py +252 -0
  160. superqode/providers/registry.py +716 -0
  161. superqode/providers/usage.py +332 -0
  162. superqode/pure_mode.py +384 -0
  163. superqode/qr/__init__.py +23 -0
  164. superqode/qr/dashboard.py +781 -0
  165. superqode/qr/generator.py +1018 -0
  166. superqode/qr/templates.py +135 -0
  167. superqode/safety/__init__.py +41 -0
  168. superqode/safety/sandbox.py +413 -0
  169. superqode/safety/warnings.py +256 -0
  170. superqode/server/__init__.py +33 -0
  171. superqode/server/lsp_server.py +775 -0
  172. superqode/server/web.py +250 -0
  173. superqode/session/__init__.py +25 -0
  174. superqode/session/persistence.py +580 -0
  175. superqode/session/sharing.py +477 -0
  176. superqode/session.py +475 -0
  177. superqode/sidebar.py +2991 -0
  178. superqode/stream_view.py +648 -0
  179. superqode/styles/__init__.py +3 -0
  180. superqode/superqe/__init__.py +184 -0
  181. superqode/superqe/acp_runner.py +1064 -0
  182. superqode/superqe/constitution/__init__.py +62 -0
  183. superqode/superqe/constitution/evaluator.py +308 -0
  184. superqode/superqe/constitution/loader.py +432 -0
  185. superqode/superqe/constitution/schema.py +250 -0
  186. superqode/superqe/events.py +591 -0
  187. superqode/superqe/frameworks/__init__.py +65 -0
  188. superqode/superqe/frameworks/base.py +234 -0
  189. superqode/superqe/frameworks/e2e.py +263 -0
  190. superqode/superqe/frameworks/executor.py +237 -0
  191. superqode/superqe/frameworks/javascript.py +409 -0
  192. superqode/superqe/frameworks/python.py +373 -0
  193. superqode/superqe/frameworks/registry.py +92 -0
  194. superqode/superqe/mcp_tools/__init__.py +47 -0
  195. superqode/superqe/mcp_tools/core_tools.py +418 -0
  196. superqode/superqe/mcp_tools/registry.py +230 -0
  197. superqode/superqe/mcp_tools/testing_tools.py +167 -0
  198. superqode/superqe/noise.py +89 -0
  199. superqode/superqe/orchestrator.py +778 -0
  200. superqode/superqe/roles.py +609 -0
  201. superqode/superqe/session.py +713 -0
  202. superqode/superqe/skills/__init__.py +57 -0
  203. superqode/superqe/skills/base.py +106 -0
  204. superqode/superqe/skills/core_skills.py +899 -0
  205. superqode/superqe/skills/registry.py +90 -0
  206. superqode/superqe/verifier.py +101 -0
  207. superqode/superqe_cli.py +76 -0
  208. superqode/tool_call.py +358 -0
  209. superqode/tools/__init__.py +93 -0
  210. superqode/tools/agent_tools.py +496 -0
  211. superqode/tools/base.py +324 -0
  212. superqode/tools/batch_tool.py +133 -0
  213. superqode/tools/diagnostics.py +311 -0
  214. superqode/tools/edit_tools.py +653 -0
  215. superqode/tools/enhanced_base.py +515 -0
  216. superqode/tools/file_tools.py +269 -0
  217. superqode/tools/file_tracking.py +45 -0
  218. superqode/tools/lsp_tools.py +610 -0
  219. superqode/tools/network_tools.py +350 -0
  220. superqode/tools/permissions.py +400 -0
  221. superqode/tools/question_tool.py +324 -0
  222. superqode/tools/search_tools.py +598 -0
  223. superqode/tools/shell_tools.py +259 -0
  224. superqode/tools/todo_tools.py +121 -0
  225. superqode/tools/validation.py +80 -0
  226. superqode/tools/web_tools.py +639 -0
  227. superqode/tui.py +1152 -0
  228. superqode/tui_integration.py +875 -0
  229. superqode/tui_widgets/__init__.py +27 -0
  230. superqode/tui_widgets/widgets/__init__.py +18 -0
  231. superqode/tui_widgets/widgets/progress.py +185 -0
  232. superqode/tui_widgets/widgets/tool_display.py +188 -0
  233. superqode/undo_manager.py +574 -0
  234. superqode/utils/__init__.py +5 -0
  235. superqode/utils/error_handling.py +323 -0
  236. superqode/utils/fuzzy.py +257 -0
  237. superqode/widgets/__init__.py +477 -0
  238. superqode/widgets/agent_collab.py +390 -0
  239. superqode/widgets/agent_store.py +936 -0
  240. superqode/widgets/agent_switcher.py +395 -0
  241. superqode/widgets/animation_manager.py +284 -0
  242. superqode/widgets/code_context.py +356 -0
  243. superqode/widgets/command_palette.py +412 -0
  244. superqode/widgets/connection_status.py +537 -0
  245. superqode/widgets/conversation_history.py +470 -0
  246. superqode/widgets/diff_indicator.py +155 -0
  247. superqode/widgets/enhanced_status_bar.py +385 -0
  248. superqode/widgets/enhanced_toast.py +476 -0
  249. superqode/widgets/file_browser.py +809 -0
  250. superqode/widgets/file_reference.py +585 -0
  251. superqode/widgets/issue_timeline.py +340 -0
  252. superqode/widgets/leader_key.py +264 -0
  253. superqode/widgets/mode_switcher.py +445 -0
  254. superqode/widgets/model_picker.py +234 -0
  255. superqode/widgets/permission_preview.py +1205 -0
  256. superqode/widgets/prompt.py +358 -0
  257. superqode/widgets/provider_connect.py +725 -0
  258. superqode/widgets/pty_shell.py +587 -0
  259. superqode/widgets/qe_dashboard.py +321 -0
  260. superqode/widgets/resizable_sidebar.py +377 -0
  261. superqode/widgets/response_changes.py +218 -0
  262. superqode/widgets/response_display.py +528 -0
  263. superqode/widgets/rich_tool_display.py +613 -0
  264. superqode/widgets/sidebar_panels.py +1180 -0
  265. superqode/widgets/slash_complete.py +356 -0
  266. superqode/widgets/split_view.py +612 -0
  267. superqode/widgets/status_bar.py +273 -0
  268. superqode/widgets/superqode_display.py +786 -0
  269. superqode/widgets/thinking_display.py +815 -0
  270. superqode/widgets/throbber.py +87 -0
  271. superqode/widgets/toast.py +206 -0
  272. superqode/widgets/unified_output.py +1073 -0
  273. superqode/workspace/__init__.py +75 -0
  274. superqode/workspace/artifacts.py +472 -0
  275. superqode/workspace/coordinator.py +353 -0
  276. superqode/workspace/diff_tracker.py +429 -0
  277. superqode/workspace/git_guard.py +373 -0
  278. superqode/workspace/git_snapshot.py +526 -0
  279. superqode/workspace/manager.py +750 -0
  280. superqode/workspace/snapshot.py +357 -0
  281. superqode/workspace/watcher.py +535 -0
  282. superqode/workspace/worktree.py +440 -0
  283. superqode-0.1.5.dist-info/METADATA +204 -0
  284. superqode-0.1.5.dist-info/RECORD +288 -0
  285. superqode-0.1.5.dist-info/WHEEL +5 -0
  286. superqode-0.1.5.dist-info/entry_points.txt +3 -0
  287. superqode-0.1.5.dist-info/licenses/LICENSE +648 -0
  288. superqode-0.1.5.dist-info/top_level.txt +1 -0
@@ -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
+ )