agentpool 2.1.9__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.

Potentially problematic release.


This version of agentpool might be problematic. Click here for more details.

Files changed (474) hide show
  1. acp/README.md +64 -0
  2. acp/__init__.py +172 -0
  3. acp/__main__.py +10 -0
  4. acp/acp_requests.py +285 -0
  5. acp/agent/__init__.py +6 -0
  6. acp/agent/connection.py +256 -0
  7. acp/agent/implementations/__init__.py +6 -0
  8. acp/agent/implementations/debug_server/__init__.py +1 -0
  9. acp/agent/implementations/debug_server/cli.py +79 -0
  10. acp/agent/implementations/debug_server/debug.html +234 -0
  11. acp/agent/implementations/debug_server/debug_server.py +496 -0
  12. acp/agent/implementations/testing.py +91 -0
  13. acp/agent/protocol.py +65 -0
  14. acp/bridge/README.md +162 -0
  15. acp/bridge/__init__.py +6 -0
  16. acp/bridge/__main__.py +91 -0
  17. acp/bridge/bridge.py +246 -0
  18. acp/bridge/py.typed +0 -0
  19. acp/bridge/settings.py +15 -0
  20. acp/client/__init__.py +7 -0
  21. acp/client/connection.py +251 -0
  22. acp/client/implementations/__init__.py +7 -0
  23. acp/client/implementations/default_client.py +185 -0
  24. acp/client/implementations/headless_client.py +266 -0
  25. acp/client/implementations/noop_client.py +110 -0
  26. acp/client/protocol.py +61 -0
  27. acp/connection.py +280 -0
  28. acp/exceptions.py +46 -0
  29. acp/filesystem.py +524 -0
  30. acp/notifications.py +832 -0
  31. acp/py.typed +0 -0
  32. acp/schema/__init__.py +265 -0
  33. acp/schema/agent_plan.py +30 -0
  34. acp/schema/agent_requests.py +126 -0
  35. acp/schema/agent_responses.py +256 -0
  36. acp/schema/base.py +39 -0
  37. acp/schema/capabilities.py +230 -0
  38. acp/schema/client_requests.py +247 -0
  39. acp/schema/client_responses.py +96 -0
  40. acp/schema/common.py +81 -0
  41. acp/schema/content_blocks.py +188 -0
  42. acp/schema/mcp.py +82 -0
  43. acp/schema/messages.py +171 -0
  44. acp/schema/notifications.py +82 -0
  45. acp/schema/protocol_stuff.md +3 -0
  46. acp/schema/session_state.py +160 -0
  47. acp/schema/session_updates.py +419 -0
  48. acp/schema/slash_commands.py +51 -0
  49. acp/schema/terminal.py +15 -0
  50. acp/schema/tool_call.py +347 -0
  51. acp/stdio.py +250 -0
  52. acp/task/__init__.py +53 -0
  53. acp/task/debug.py +197 -0
  54. acp/task/dispatcher.py +93 -0
  55. acp/task/queue.py +69 -0
  56. acp/task/sender.py +82 -0
  57. acp/task/state.py +87 -0
  58. acp/task/supervisor.py +93 -0
  59. acp/terminal_handle.py +30 -0
  60. acp/tool_call_reporter.py +199 -0
  61. acp/tool_call_state.py +178 -0
  62. acp/transports.py +104 -0
  63. acp/utils.py +240 -0
  64. agentpool/__init__.py +63 -0
  65. agentpool/__main__.py +7 -0
  66. agentpool/agents/__init__.py +30 -0
  67. agentpool/agents/acp_agent/__init__.py +5 -0
  68. agentpool/agents/acp_agent/acp_agent.py +837 -0
  69. agentpool/agents/acp_agent/acp_converters.py +294 -0
  70. agentpool/agents/acp_agent/client_handler.py +317 -0
  71. agentpool/agents/acp_agent/session_state.py +44 -0
  72. agentpool/agents/agent.py +1264 -0
  73. agentpool/agents/agui_agent/__init__.py +19 -0
  74. agentpool/agents/agui_agent/agui_agent.py +677 -0
  75. agentpool/agents/agui_agent/agui_converters.py +423 -0
  76. agentpool/agents/agui_agent/chunk_transformer.py +204 -0
  77. agentpool/agents/agui_agent/event_types.py +83 -0
  78. agentpool/agents/agui_agent/helpers.py +192 -0
  79. agentpool/agents/architect.py +71 -0
  80. agentpool/agents/base_agent.py +177 -0
  81. agentpool/agents/claude_code_agent/__init__.py +11 -0
  82. agentpool/agents/claude_code_agent/claude_code_agent.py +1021 -0
  83. agentpool/agents/claude_code_agent/converters.py +243 -0
  84. agentpool/agents/context.py +105 -0
  85. agentpool/agents/events/__init__.py +61 -0
  86. agentpool/agents/events/builtin_handlers.py +129 -0
  87. agentpool/agents/events/event_emitter.py +320 -0
  88. agentpool/agents/events/events.py +561 -0
  89. agentpool/agents/events/tts_handlers.py +186 -0
  90. agentpool/agents/interactions.py +419 -0
  91. agentpool/agents/slashed_agent.py +244 -0
  92. agentpool/agents/sys_prompts.py +178 -0
  93. agentpool/agents/tool_wrapping.py +184 -0
  94. agentpool/base_provider.py +28 -0
  95. agentpool/common_types.py +226 -0
  96. agentpool/config_resources/__init__.py +16 -0
  97. agentpool/config_resources/acp_assistant.yml +24 -0
  98. agentpool/config_resources/agents.yml +109 -0
  99. agentpool/config_resources/agents_template.yml +18 -0
  100. agentpool/config_resources/agui_test.yml +18 -0
  101. agentpool/config_resources/claude_code_agent.yml +16 -0
  102. agentpool/config_resources/claude_style_subagent.md +30 -0
  103. agentpool/config_resources/external_acp_agents.yml +77 -0
  104. agentpool/config_resources/opencode_style_subagent.md +19 -0
  105. agentpool/config_resources/tts_test_agents.yml +78 -0
  106. agentpool/delegation/__init__.py +8 -0
  107. agentpool/delegation/base_team.py +504 -0
  108. agentpool/delegation/message_flow_tracker.py +39 -0
  109. agentpool/delegation/pool.py +1129 -0
  110. agentpool/delegation/team.py +325 -0
  111. agentpool/delegation/teamrun.py +343 -0
  112. agentpool/docs/__init__.py +5 -0
  113. agentpool/docs/gen_examples.py +42 -0
  114. agentpool/docs/utils.py +370 -0
  115. agentpool/functional/__init__.py +20 -0
  116. agentpool/functional/py.typed +0 -0
  117. agentpool/functional/run.py +80 -0
  118. agentpool/functional/structure.py +136 -0
  119. agentpool/hooks/__init__.py +20 -0
  120. agentpool/hooks/agent_hooks.py +247 -0
  121. agentpool/hooks/base.py +119 -0
  122. agentpool/hooks/callable.py +140 -0
  123. agentpool/hooks/command.py +180 -0
  124. agentpool/hooks/prompt.py +122 -0
  125. agentpool/jinja_filters.py +132 -0
  126. agentpool/log.py +224 -0
  127. agentpool/mcp_server/__init__.py +17 -0
  128. agentpool/mcp_server/client.py +429 -0
  129. agentpool/mcp_server/constants.py +32 -0
  130. agentpool/mcp_server/conversions.py +172 -0
  131. agentpool/mcp_server/helpers.py +47 -0
  132. agentpool/mcp_server/manager.py +232 -0
  133. agentpool/mcp_server/message_handler.py +164 -0
  134. agentpool/mcp_server/registries/__init__.py +1 -0
  135. agentpool/mcp_server/registries/official_registry_client.py +345 -0
  136. agentpool/mcp_server/registries/pulsemcp_client.py +88 -0
  137. agentpool/mcp_server/tool_bridge.py +548 -0
  138. agentpool/messaging/__init__.py +58 -0
  139. agentpool/messaging/compaction.py +928 -0
  140. agentpool/messaging/connection_manager.py +319 -0
  141. agentpool/messaging/context.py +66 -0
  142. agentpool/messaging/event_manager.py +426 -0
  143. agentpool/messaging/events.py +39 -0
  144. agentpool/messaging/message_container.py +209 -0
  145. agentpool/messaging/message_history.py +491 -0
  146. agentpool/messaging/messagenode.py +377 -0
  147. agentpool/messaging/messages.py +655 -0
  148. agentpool/messaging/processing.py +76 -0
  149. agentpool/mime_utils.py +95 -0
  150. agentpool/models/__init__.py +21 -0
  151. agentpool/models/acp_agents/__init__.py +22 -0
  152. agentpool/models/acp_agents/base.py +308 -0
  153. agentpool/models/acp_agents/mcp_capable.py +790 -0
  154. agentpool/models/acp_agents/non_mcp.py +842 -0
  155. agentpool/models/agents.py +450 -0
  156. agentpool/models/agui_agents.py +89 -0
  157. agentpool/models/claude_code_agents.py +238 -0
  158. agentpool/models/file_agents.py +116 -0
  159. agentpool/models/file_parsing.py +367 -0
  160. agentpool/models/manifest.py +658 -0
  161. agentpool/observability/__init__.py +9 -0
  162. agentpool/observability/observability_registry.py +97 -0
  163. agentpool/prompts/__init__.py +1 -0
  164. agentpool/prompts/base.py +27 -0
  165. agentpool/prompts/builtin_provider.py +75 -0
  166. agentpool/prompts/conversion_manager.py +95 -0
  167. agentpool/prompts/convert.py +96 -0
  168. agentpool/prompts/manager.py +204 -0
  169. agentpool/prompts/parts/zed.md +33 -0
  170. agentpool/prompts/prompts.py +581 -0
  171. agentpool/py.typed +0 -0
  172. agentpool/queries/tree-sitter-language-pack/README.md +7 -0
  173. agentpool/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  174. agentpool/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  175. agentpool/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  176. agentpool/queries/tree-sitter-language-pack/clojure-tags.scm +7 -0
  177. agentpool/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  178. agentpool/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  179. agentpool/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  180. agentpool/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  181. agentpool/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  182. agentpool/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  183. agentpool/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  184. agentpool/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  185. agentpool/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  186. agentpool/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  187. agentpool/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  188. agentpool/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  189. agentpool/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  190. agentpool/queries/tree-sitter-language-pack/matlab-tags.scm +10 -0
  191. agentpool/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  192. agentpool/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  193. agentpool/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  194. agentpool/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  195. agentpool/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  196. agentpool/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  197. agentpool/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  198. agentpool/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  199. agentpool/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  200. agentpool/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  201. agentpool/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  202. agentpool/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  203. agentpool/queries/tree-sitter-languages/README.md +24 -0
  204. agentpool/queries/tree-sitter-languages/c-tags.scm +9 -0
  205. agentpool/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  206. agentpool/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  207. agentpool/queries/tree-sitter-languages/dart-tags.scm +91 -0
  208. agentpool/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  209. agentpool/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  210. agentpool/queries/tree-sitter-languages/elm-tags.scm +19 -0
  211. agentpool/queries/tree-sitter-languages/fortran-tags.scm +15 -0
  212. agentpool/queries/tree-sitter-languages/go-tags.scm +30 -0
  213. agentpool/queries/tree-sitter-languages/haskell-tags.scm +3 -0
  214. agentpool/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  215. agentpool/queries/tree-sitter-languages/java-tags.scm +20 -0
  216. agentpool/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  217. agentpool/queries/tree-sitter-languages/julia-tags.scm +60 -0
  218. agentpool/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  219. agentpool/queries/tree-sitter-languages/matlab-tags.scm +10 -0
  220. agentpool/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  221. agentpool/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  222. agentpool/queries/tree-sitter-languages/php-tags.scm +26 -0
  223. agentpool/queries/tree-sitter-languages/python-tags.scm +12 -0
  224. agentpool/queries/tree-sitter-languages/ql-tags.scm +26 -0
  225. agentpool/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  226. agentpool/queries/tree-sitter-languages/rust-tags.scm +60 -0
  227. agentpool/queries/tree-sitter-languages/scala-tags.scm +65 -0
  228. agentpool/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  229. agentpool/queries/tree-sitter-languages/zig-tags.scm +3 -0
  230. agentpool/repomap.py +1231 -0
  231. agentpool/resource_providers/__init__.py +17 -0
  232. agentpool/resource_providers/aggregating.py +54 -0
  233. agentpool/resource_providers/base.py +172 -0
  234. agentpool/resource_providers/codemode/__init__.py +9 -0
  235. agentpool/resource_providers/codemode/code_executor.py +215 -0
  236. agentpool/resource_providers/codemode/default_prompt.py +19 -0
  237. agentpool/resource_providers/codemode/helpers.py +83 -0
  238. agentpool/resource_providers/codemode/progress_executor.py +212 -0
  239. agentpool/resource_providers/codemode/provider.py +150 -0
  240. agentpool/resource_providers/codemode/remote_mcp_execution.py +143 -0
  241. agentpool/resource_providers/codemode/remote_provider.py +171 -0
  242. agentpool/resource_providers/filtering.py +42 -0
  243. agentpool/resource_providers/mcp_provider.py +246 -0
  244. agentpool/resource_providers/plan_provider.py +196 -0
  245. agentpool/resource_providers/pool.py +69 -0
  246. agentpool/resource_providers/static.py +289 -0
  247. agentpool/running/__init__.py +20 -0
  248. agentpool/running/decorators.py +56 -0
  249. agentpool/running/discovery.py +101 -0
  250. agentpool/running/executor.py +284 -0
  251. agentpool/running/injection.py +111 -0
  252. agentpool/running/py.typed +0 -0
  253. agentpool/running/run_nodes.py +87 -0
  254. agentpool/server.py +122 -0
  255. agentpool/sessions/__init__.py +13 -0
  256. agentpool/sessions/manager.py +302 -0
  257. agentpool/sessions/models.py +71 -0
  258. agentpool/sessions/session.py +239 -0
  259. agentpool/sessions/store.py +163 -0
  260. agentpool/skills/__init__.py +5 -0
  261. agentpool/skills/manager.py +120 -0
  262. agentpool/skills/registry.py +210 -0
  263. agentpool/skills/skill.py +36 -0
  264. agentpool/storage/__init__.py +17 -0
  265. agentpool/storage/manager.py +419 -0
  266. agentpool/storage/serialization.py +136 -0
  267. agentpool/talk/__init__.py +13 -0
  268. agentpool/talk/registry.py +128 -0
  269. agentpool/talk/stats.py +159 -0
  270. agentpool/talk/talk.py +604 -0
  271. agentpool/tasks/__init__.py +20 -0
  272. agentpool/tasks/exceptions.py +25 -0
  273. agentpool/tasks/registry.py +33 -0
  274. agentpool/testing.py +129 -0
  275. agentpool/text_templates/__init__.py +39 -0
  276. agentpool/text_templates/system_prompt.jinja +30 -0
  277. agentpool/text_templates/tool_call_default.jinja +13 -0
  278. agentpool/text_templates/tool_call_markdown.jinja +25 -0
  279. agentpool/text_templates/tool_call_simple.jinja +5 -0
  280. agentpool/tools/__init__.py +16 -0
  281. agentpool/tools/base.py +269 -0
  282. agentpool/tools/exceptions.py +9 -0
  283. agentpool/tools/manager.py +255 -0
  284. agentpool/tools/tool_call_info.py +87 -0
  285. agentpool/ui/__init__.py +2 -0
  286. agentpool/ui/base.py +89 -0
  287. agentpool/ui/mock_provider.py +81 -0
  288. agentpool/ui/stdlib_provider.py +150 -0
  289. agentpool/utils/__init__.py +44 -0
  290. agentpool/utils/baseregistry.py +185 -0
  291. agentpool/utils/count_tokens.py +62 -0
  292. agentpool/utils/dag.py +184 -0
  293. agentpool/utils/importing.py +206 -0
  294. agentpool/utils/inspection.py +334 -0
  295. agentpool/utils/model_capabilities.py +25 -0
  296. agentpool/utils/network.py +28 -0
  297. agentpool/utils/now.py +22 -0
  298. agentpool/utils/parse_time.py +87 -0
  299. agentpool/utils/result_utils.py +35 -0
  300. agentpool/utils/signatures.py +305 -0
  301. agentpool/utils/streams.py +112 -0
  302. agentpool/utils/tasks.py +186 -0
  303. agentpool/vfs_registry.py +250 -0
  304. agentpool-2.1.9.dist-info/METADATA +336 -0
  305. agentpool-2.1.9.dist-info/RECORD +474 -0
  306. agentpool-2.1.9.dist-info/WHEEL +4 -0
  307. agentpool-2.1.9.dist-info/entry_points.txt +14 -0
  308. agentpool-2.1.9.dist-info/licenses/LICENSE +22 -0
  309. agentpool_cli/__init__.py +34 -0
  310. agentpool_cli/__main__.py +66 -0
  311. agentpool_cli/agent.py +175 -0
  312. agentpool_cli/cli_types.py +23 -0
  313. agentpool_cli/common.py +163 -0
  314. agentpool_cli/create.py +175 -0
  315. agentpool_cli/history.py +217 -0
  316. agentpool_cli/log.py +78 -0
  317. agentpool_cli/py.typed +0 -0
  318. agentpool_cli/run.py +84 -0
  319. agentpool_cli/serve_acp.py +177 -0
  320. agentpool_cli/serve_api.py +69 -0
  321. agentpool_cli/serve_mcp.py +74 -0
  322. agentpool_cli/serve_vercel.py +233 -0
  323. agentpool_cli/store.py +171 -0
  324. agentpool_cli/task.py +84 -0
  325. agentpool_cli/utils.py +104 -0
  326. agentpool_cli/watch.py +54 -0
  327. agentpool_commands/__init__.py +180 -0
  328. agentpool_commands/agents.py +199 -0
  329. agentpool_commands/base.py +45 -0
  330. agentpool_commands/commands.py +58 -0
  331. agentpool_commands/completers.py +110 -0
  332. agentpool_commands/connections.py +175 -0
  333. agentpool_commands/markdown_utils.py +31 -0
  334. agentpool_commands/models.py +62 -0
  335. agentpool_commands/prompts.py +78 -0
  336. agentpool_commands/py.typed +0 -0
  337. agentpool_commands/read.py +77 -0
  338. agentpool_commands/resources.py +210 -0
  339. agentpool_commands/session.py +48 -0
  340. agentpool_commands/tools.py +269 -0
  341. agentpool_commands/utils.py +189 -0
  342. agentpool_commands/workers.py +163 -0
  343. agentpool_config/__init__.py +53 -0
  344. agentpool_config/builtin_tools.py +265 -0
  345. agentpool_config/commands.py +237 -0
  346. agentpool_config/conditions.py +301 -0
  347. agentpool_config/converters.py +30 -0
  348. agentpool_config/durable.py +331 -0
  349. agentpool_config/event_handlers.py +600 -0
  350. agentpool_config/events.py +153 -0
  351. agentpool_config/forward_targets.py +251 -0
  352. agentpool_config/hook_conditions.py +331 -0
  353. agentpool_config/hooks.py +241 -0
  354. agentpool_config/jinja.py +206 -0
  355. agentpool_config/knowledge.py +41 -0
  356. agentpool_config/loaders.py +350 -0
  357. agentpool_config/mcp_server.py +243 -0
  358. agentpool_config/nodes.py +202 -0
  359. agentpool_config/observability.py +191 -0
  360. agentpool_config/output_types.py +55 -0
  361. agentpool_config/pool_server.py +267 -0
  362. agentpool_config/prompt_hubs.py +105 -0
  363. agentpool_config/prompts.py +185 -0
  364. agentpool_config/py.typed +0 -0
  365. agentpool_config/resources.py +33 -0
  366. agentpool_config/session.py +119 -0
  367. agentpool_config/skills.py +17 -0
  368. agentpool_config/storage.py +288 -0
  369. agentpool_config/system_prompts.py +190 -0
  370. agentpool_config/task.py +162 -0
  371. agentpool_config/teams.py +52 -0
  372. agentpool_config/tools.py +112 -0
  373. agentpool_config/toolsets.py +1033 -0
  374. agentpool_config/workers.py +86 -0
  375. agentpool_prompts/__init__.py +1 -0
  376. agentpool_prompts/braintrust_hub.py +235 -0
  377. agentpool_prompts/fabric.py +75 -0
  378. agentpool_prompts/langfuse_hub.py +79 -0
  379. agentpool_prompts/promptlayer_provider.py +59 -0
  380. agentpool_prompts/py.typed +0 -0
  381. agentpool_server/__init__.py +9 -0
  382. agentpool_server/a2a_server/__init__.py +5 -0
  383. agentpool_server/a2a_server/a2a_types.py +41 -0
  384. agentpool_server/a2a_server/server.py +190 -0
  385. agentpool_server/a2a_server/storage.py +81 -0
  386. agentpool_server/acp_server/__init__.py +22 -0
  387. agentpool_server/acp_server/acp_agent.py +786 -0
  388. agentpool_server/acp_server/acp_tools.py +43 -0
  389. agentpool_server/acp_server/commands/__init__.py +18 -0
  390. agentpool_server/acp_server/commands/acp_commands.py +594 -0
  391. agentpool_server/acp_server/commands/debug_commands.py +376 -0
  392. agentpool_server/acp_server/commands/docs_commands/__init__.py +39 -0
  393. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +169 -0
  394. agentpool_server/acp_server/commands/docs_commands/get_schema.py +176 -0
  395. agentpool_server/acp_server/commands/docs_commands/get_source.py +110 -0
  396. agentpool_server/acp_server/commands/docs_commands/git_diff.py +111 -0
  397. agentpool_server/acp_server/commands/docs_commands/helpers.py +33 -0
  398. agentpool_server/acp_server/commands/docs_commands/url_to_markdown.py +90 -0
  399. agentpool_server/acp_server/commands/spawn.py +210 -0
  400. agentpool_server/acp_server/converters.py +235 -0
  401. agentpool_server/acp_server/input_provider.py +338 -0
  402. agentpool_server/acp_server/server.py +288 -0
  403. agentpool_server/acp_server/session.py +969 -0
  404. agentpool_server/acp_server/session_manager.py +313 -0
  405. agentpool_server/acp_server/syntax_detection.py +250 -0
  406. agentpool_server/acp_server/zed_tools.md +90 -0
  407. agentpool_server/aggregating_server.py +309 -0
  408. agentpool_server/agui_server/__init__.py +11 -0
  409. agentpool_server/agui_server/server.py +128 -0
  410. agentpool_server/base.py +189 -0
  411. agentpool_server/http_server.py +164 -0
  412. agentpool_server/mcp_server/__init__.py +6 -0
  413. agentpool_server/mcp_server/server.py +314 -0
  414. agentpool_server/mcp_server/zed_wrapper.py +110 -0
  415. agentpool_server/openai_api_server/__init__.py +5 -0
  416. agentpool_server/openai_api_server/completions/__init__.py +1 -0
  417. agentpool_server/openai_api_server/completions/helpers.py +81 -0
  418. agentpool_server/openai_api_server/completions/models.py +98 -0
  419. agentpool_server/openai_api_server/responses/__init__.py +1 -0
  420. agentpool_server/openai_api_server/responses/helpers.py +74 -0
  421. agentpool_server/openai_api_server/responses/models.py +96 -0
  422. agentpool_server/openai_api_server/server.py +242 -0
  423. agentpool_server/py.typed +0 -0
  424. agentpool_storage/__init__.py +9 -0
  425. agentpool_storage/base.py +310 -0
  426. agentpool_storage/file_provider.py +378 -0
  427. agentpool_storage/formatters.py +129 -0
  428. agentpool_storage/memory_provider.py +396 -0
  429. agentpool_storage/models.py +108 -0
  430. agentpool_storage/py.typed +0 -0
  431. agentpool_storage/session_store.py +262 -0
  432. agentpool_storage/sql_provider/__init__.py +21 -0
  433. agentpool_storage/sql_provider/cli.py +146 -0
  434. agentpool_storage/sql_provider/models.py +249 -0
  435. agentpool_storage/sql_provider/queries.py +15 -0
  436. agentpool_storage/sql_provider/sql_provider.py +444 -0
  437. agentpool_storage/sql_provider/utils.py +234 -0
  438. agentpool_storage/text_log_provider.py +275 -0
  439. agentpool_toolsets/__init__.py +15 -0
  440. agentpool_toolsets/builtin/__init__.py +33 -0
  441. agentpool_toolsets/builtin/agent_management.py +239 -0
  442. agentpool_toolsets/builtin/chain.py +288 -0
  443. agentpool_toolsets/builtin/code.py +398 -0
  444. agentpool_toolsets/builtin/debug.py +291 -0
  445. agentpool_toolsets/builtin/execution_environment.py +381 -0
  446. agentpool_toolsets/builtin/file_edit/__init__.py +11 -0
  447. agentpool_toolsets/builtin/file_edit/file_edit.py +747 -0
  448. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/__init__.py +5 -0
  449. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/example_usage.py +311 -0
  450. agentpool_toolsets/builtin/file_edit/fuzzy_matcher/streaming_fuzzy_matcher.py +443 -0
  451. agentpool_toolsets/builtin/history.py +36 -0
  452. agentpool_toolsets/builtin/integration.py +85 -0
  453. agentpool_toolsets/builtin/skills.py +77 -0
  454. agentpool_toolsets/builtin/subagent_tools.py +324 -0
  455. agentpool_toolsets/builtin/tool_management.py +90 -0
  456. agentpool_toolsets/builtin/user_interaction.py +52 -0
  457. agentpool_toolsets/builtin/workers.py +128 -0
  458. agentpool_toolsets/composio_toolset.py +96 -0
  459. agentpool_toolsets/config_creation.py +192 -0
  460. agentpool_toolsets/entry_points.py +47 -0
  461. agentpool_toolsets/fsspec_toolset/__init__.py +7 -0
  462. agentpool_toolsets/fsspec_toolset/diagnostics.py +115 -0
  463. agentpool_toolsets/fsspec_toolset/grep.py +450 -0
  464. agentpool_toolsets/fsspec_toolset/helpers.py +631 -0
  465. agentpool_toolsets/fsspec_toolset/streaming_diff_parser.py +249 -0
  466. agentpool_toolsets/fsspec_toolset/toolset.py +1384 -0
  467. agentpool_toolsets/mcp_run_toolset.py +61 -0
  468. agentpool_toolsets/notifications.py +146 -0
  469. agentpool_toolsets/openapi.py +118 -0
  470. agentpool_toolsets/py.typed +0 -0
  471. agentpool_toolsets/search_toolset.py +202 -0
  472. agentpool_toolsets/semantic_memory_toolset.py +536 -0
  473. agentpool_toolsets/streaming_tools.py +265 -0
  474. agentpool_toolsets/vfs_toolset.py +124 -0
@@ -0,0 +1,288 @@
1
+ """Tool to chain multiple function calls."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from dataclasses import dataclass
7
+ from enum import StrEnum
8
+ from typing import Any, Literal, assert_never
9
+
10
+ import anyio
11
+ from pydantic import Field
12
+ from pydantic_ai import ModelRetry
13
+ from schemez import Schema
14
+
15
+ from agentpool.agents.context import AgentContext # noqa: TC001
16
+
17
+
18
+ class ErrorStrategy(StrEnum):
19
+ """Strategy for handling errors in the pipeline."""
20
+
21
+ STOP = "stop" # Stop pipeline on error
22
+ SKIP = "skip" # Skip failed step, continue with previous result
23
+ DEFAULT = "default" # Use provided default value
24
+ RETRY = "retry" # Retry the step
25
+
26
+
27
+ class StepCondition(Schema):
28
+ """Condition for conditional execution."""
29
+
30
+ field: str # Field to check in result
31
+ operator: Literal["eq", "gt", "lt", "contains", "exists"]
32
+ value: Any = None
33
+
34
+ def evaluate_with_value(self, value: Any) -> bool:
35
+ """Evaluate this condition against a value.
36
+
37
+ Args:
38
+ value: The value to evaluate against the condition.
39
+
40
+ Returns:
41
+ bool: True if the condition is met, False otherwise.
42
+ """
43
+ field_value = value.get(self.field) if isinstance(value, dict) else value
44
+
45
+ match self.operator:
46
+ case "eq":
47
+ return bool(field_value == self.value)
48
+ case "gt":
49
+ return bool(field_value > self.value)
50
+ case "lt":
51
+ return bool(field_value < self.value)
52
+ case "contains":
53
+ try:
54
+ return self.value in field_value # type: ignore[operator]
55
+ except TypeError:
56
+ return False
57
+ case "exists":
58
+ return field_value is not None
59
+ case _ as unreachable:
60
+ assert_never(unreachable)
61
+
62
+
63
+ @dataclass
64
+ class StepResult:
65
+ """Result of a pipeline step execution."""
66
+
67
+ success: bool
68
+ result: Any
69
+ error: Exception | None = None
70
+ retries: int = 0
71
+ duration: float = 0.0
72
+
73
+
74
+ # Type alias for step results during execution
75
+ type StepResults = dict[str, StepResult]
76
+
77
+
78
+ class PipelineStep(Schema):
79
+ """Single step in a tool pipeline."""
80
+
81
+ tool: str
82
+ input_kwarg: str = "text"
83
+ keyword_args: dict[str, Any] = Field(default_factory=dict)
84
+ name: str | None = None # Optional step name for referencing
85
+ condition: StepCondition | None = None # Conditional execution
86
+ error_strategy: ErrorStrategy = ErrorStrategy.STOP
87
+ default_value: Any = None # Used with ErrorStrategy.DEFAULT
88
+ max_retries: int = 0
89
+ retry_delay: float = 1.0
90
+ timeout: float | None = None
91
+ depends_on: list[str] = Field(default_factory=list) # Step dependencies
92
+
93
+
94
+ class Pipeline(Schema):
95
+ """A pipeline of tool operations."""
96
+
97
+ input: str | dict[str, Any]
98
+ steps: list[PipelineStep]
99
+ mode: Literal["sequential", "parallel"] = "sequential"
100
+ max_parallel: int = 5 # Max concurrent steps
101
+ collect_metrics: bool = False # Collect execution metrics
102
+
103
+
104
+ async def _execute_step(
105
+ ctx: AgentContext,
106
+ step: PipelineStep,
107
+ input_value: Any,
108
+ results: StepResults,
109
+ ) -> StepResult:
110
+ """Execute a single pipeline step."""
111
+ start_time = asyncio.get_event_loop().time()
112
+ retries = 0
113
+
114
+ while True:
115
+ try:
116
+ # Check condition if any
117
+ if step.condition and not step.condition.evaluate_with_value(input_value):
118
+ return StepResult(success=True, result=input_value, duration=0)
119
+
120
+ tool_info = await ctx.agent.tools.get_tool(step.tool) # Get the tool
121
+ if isinstance(input_value, dict): # Prepare kwargs
122
+ kwargs = {**input_value, **step.keyword_args}
123
+ else:
124
+ kwargs = {step.input_kwarg: input_value, **step.keyword_args}
125
+
126
+ # Execute with timeout if specified
127
+ if step.timeout:
128
+ fut = tool_info.execute(ctx, **kwargs)
129
+ result = await asyncio.wait_for(fut, timeout=step.timeout)
130
+ else:
131
+ result = await tool_info.execute(ctx, **kwargs)
132
+
133
+ duration = asyncio.get_event_loop().time() - start_time
134
+ return StepResult(success=True, result=result, duration=duration)
135
+
136
+ except Exception as exc:
137
+ match step.error_strategy:
138
+ case ErrorStrategy.STOP:
139
+ raise
140
+
141
+ case ErrorStrategy.SKIP:
142
+ duration = asyncio.get_event_loop().time() - start_time
143
+ return StepResult(
144
+ success=False,
145
+ result=input_value,
146
+ error=exc,
147
+ duration=duration,
148
+ )
149
+
150
+ case ErrorStrategy.DEFAULT:
151
+ duration = asyncio.get_event_loop().time() - start_time
152
+ return StepResult(
153
+ success=False,
154
+ result=step.default_value,
155
+ error=exc,
156
+ duration=duration,
157
+ )
158
+
159
+ case ErrorStrategy.RETRY:
160
+ retries += 1
161
+ if retries <= step.max_retries:
162
+ await anyio.sleep(step.retry_delay)
163
+ continue
164
+ raise # Max retries exceeded
165
+
166
+
167
+ async def _execute_sequential(ctx: AgentContext, pipeline: Pipeline, results: StepResults) -> Any:
168
+ """Execute steps sequentially."""
169
+ current = pipeline.input
170
+ for step in pipeline.steps:
171
+ result = await _execute_step(ctx, step, current, results)
172
+ if step.name:
173
+ results[step.name] = result
174
+ current = result.result
175
+ return current
176
+
177
+
178
+ async def _execute_parallel(ctx: AgentContext, pipeline: Pipeline, results: StepResults) -> Any:
179
+ """Execute independent steps in parallel."""
180
+ semaphore = asyncio.Semaphore(pipeline.max_parallel)
181
+
182
+ async def run_step(step: PipelineStep) -> None:
183
+ async with semaphore:
184
+ # Wait for dependencies
185
+ for dep in step.depends_on:
186
+ while dep not in results:
187
+ await anyio.sleep(0.1)
188
+
189
+ # Get input from dependency or pipeline input
190
+ input_value = results[step.depends_on[-1]].result if step.depends_on else pipeline.input
191
+ result = await _execute_step(ctx, step, input_value, results)
192
+ if step.name:
193
+ results[step.name] = result
194
+
195
+ # Create tasks for all steps
196
+ tasks = [run_step(step) for step in pipeline.steps]
197
+ await asyncio.gather(*tasks)
198
+ # Return last result
199
+ return results[name].result if (name := pipeline.steps[-1].name) else None
200
+
201
+
202
+ async def chain_tools(
203
+ ctx: AgentContext,
204
+ input_data: str | dict[str, Any],
205
+ steps: list[dict[str, Any]],
206
+ mode: Literal["sequential", "parallel"] = "sequential",
207
+ max_parallel: int = 5,
208
+ collect_metrics: bool = False,
209
+ ) -> Any:
210
+ """Execute multiple tool operations in sequence or parallel.
211
+
212
+ WHEN TO USE THIS TOOL:
213
+ - Use this when you can plan multiple operations confidently in advance
214
+ - Use this for common sequences you've successfully used before
215
+ - Use this to reduce interaction rounds for known operation patterns
216
+ - Use this when all steps are independent of intermediate results
217
+
218
+ DO NOT USE THIS TOOL:
219
+ - When you need to inspect intermediate results
220
+ - When next steps depend on analyzing previous results
221
+ - When you're unsure about the complete sequence
222
+ - When you need to handle errors at each step individually
223
+
224
+ Args:
225
+ ctx: Agent context for tool execution
226
+ input_data: Initial input for the pipeline
227
+ steps: List of step configurations, each containing:
228
+ - tool: Name of the tool to execute
229
+ - input_kwarg: Keyword argument name for input (default: "text")
230
+ - keyword_args: Additional keyword arguments
231
+ - name: Optional step name for referencing
232
+ - condition: Optional execution condition
233
+ - error_strategy: How to handle errors ("stop", "skip", "default", "retry")
234
+ - default_value: Value to use with "default" error strategy
235
+ - max_retries: Maximum retry attempts
236
+ - retry_delay: Delay between retries in seconds
237
+ - timeout: Step timeout in seconds
238
+ - depends_on: List of step names this depends on
239
+ mode: Execution mode - "sequential" or "parallel"
240
+ max_parallel: Maximum concurrent steps for parallel mode
241
+ collect_metrics: Whether to collect execution metrics
242
+
243
+ Examples:
244
+ # Sequential processing
245
+ await chain_tools(
246
+ ctx,
247
+ input_data="main.py",
248
+ steps=[
249
+ {"tool": "load_resource", "input_kwarg": "name"},
250
+ {"tool": "analyze_code", "input_kwarg": "code"},
251
+ {"tool": "format_output", "input_kwarg": "text"}
252
+ ]
253
+ )
254
+
255
+ # Parallel processing with dependencies
256
+ await chain_tools(
257
+ ctx,
258
+ input_data="test.py",
259
+ mode="parallel",
260
+ steps=[
261
+ {"tool": "load_resource", "input_kwarg": "name", "name": "load"},
262
+ {"tool": "analyze_code", "input_kwarg": "code", "depends_on": ["load"]},
263
+ {"tool": "count_tokens", "input_kwarg": "text", "depends_on": ["load"]}
264
+ ]
265
+ )
266
+ """
267
+ try:
268
+ pipeline = Pipeline(
269
+ input=input_data,
270
+ steps=[PipelineStep.model_validate(step) for step in steps],
271
+ mode=mode,
272
+ max_parallel=max_parallel,
273
+ collect_metrics=collect_metrics,
274
+ )
275
+ except Exception as e:
276
+ msg = f"Invalid pipeline configuration: {e}"
277
+ raise ModelRetry(msg) from e
278
+ results: StepResults = {}
279
+
280
+ try:
281
+ match pipeline.mode:
282
+ case "sequential":
283
+ return await _execute_sequential(ctx, pipeline, results)
284
+ case "parallel":
285
+ return await _execute_parallel(ctx, pipeline, results)
286
+ except Exception as e:
287
+ msg = f"Failed to execute pipeline: {e}"
288
+ raise ModelRetry(msg) from e
@@ -0,0 +1,398 @@
1
+ """Provider for code formatting and linting tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ from pathlib import Path
7
+ import re
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from fsspec import AbstractFileSystem
11
+ from upathtools import is_directory
12
+
13
+ from agentpool.agents.context import AgentContext # noqa: TC001
14
+ from agentpool.log import get_logger
15
+ from agentpool.resource_providers import ResourceProvider
16
+ from agentpool_toolsets.fsspec_toolset.diagnostics import DiagnosticsManager
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from anyenv.lsp_servers import Diagnostic
21
+ from exxec.base import ExecutionEnvironment
22
+
23
+ from agentpool.tools.base import Tool
24
+
25
+
26
+ logger = get_logger(__name__)
27
+
28
+ # Map file extensions to ast-grep language identifiers
29
+ EXTENSION_TO_LANGUAGE: dict[str, str] = {
30
+ ".py": "python",
31
+ ".js": "javascript",
32
+ ".jsx": "javascript",
33
+ ".ts": "typescript",
34
+ ".tsx": "tsx",
35
+ ".rs": "rust",
36
+ ".go": "go",
37
+ ".c": "c",
38
+ ".h": "c",
39
+ ".cpp": "cpp",
40
+ ".cc": "cpp",
41
+ ".cxx": "cpp",
42
+ ".hpp": "cpp",
43
+ ".java": "java",
44
+ ".kt": "kotlin",
45
+ ".rb": "ruby",
46
+ ".swift": "swift",
47
+ ".lua": "lua",
48
+ ".cs": "csharp",
49
+ }
50
+
51
+
52
+ def _substitute_metavars(match: Any, fix_pattern: str, source_code: str) -> str:
53
+ """Substitute $METAVARS and $$$METAVARS in fix pattern with captured values."""
54
+ result = fix_pattern
55
+
56
+ # Handle $$$ multi-match metavars first (greedy match)
57
+ for metavar in re.findall(r"\$\$\$([A-Z_][A-Z0-9_]*)", fix_pattern):
58
+ captured_list = match.get_multiple_matches(metavar)
59
+ if captured_list:
60
+ first = captured_list[0]
61
+ last = captured_list[-1]
62
+ start_idx = first.range().start.index
63
+ end_idx = last.range().end.index
64
+ original_span = source_code[start_idx:end_idx]
65
+ result = result.replace(f"$$${metavar}", original_span)
66
+
67
+ # Handle single $ metavars
68
+ for metavar in re.findall(r"(?<!\$)\$([A-Z_][A-Z0-9_]*)", fix_pattern):
69
+ captured = match.get_match(metavar)
70
+ if captured:
71
+ result = result.replace(f"${metavar}", captured.text())
72
+
73
+ return result
74
+
75
+
76
+ def _detect_language(path: str) -> str | None:
77
+ """Detect ast-grep language from file extension."""
78
+ suffix = Path(path).suffix.lower()
79
+ return EXTENSION_TO_LANGUAGE.get(suffix)
80
+
81
+
82
+ class CodeTools(ResourceProvider):
83
+ """Provider for code analysis and transformation tools."""
84
+
85
+ def __init__(
86
+ self,
87
+ env: ExecutionEnvironment | None = None,
88
+ name: str = "code",
89
+ cwd: str | None = None,
90
+ ) -> None:
91
+ """Initialize with an optional execution environment.
92
+
93
+ Args:
94
+ env: Execution environment to operate on. If None, falls back to agent.env
95
+ name: Name for this toolset provider
96
+ cwd: Optional cwd to resolve relative paths against
97
+ """
98
+ from fsspec.asyn import AsyncFileSystem
99
+ from fsspec.implementations.asyn_wrapper import AsyncFileSystemWrapper
100
+ from morefs.asyn_local import AsyncLocalFileSystem
101
+
102
+ super().__init__(name=name)
103
+
104
+ self.execution_env = env
105
+ self.cwd = cwd or (env.cwd if env else None)
106
+ fs = env.get_fs() if env else None
107
+ match fs:
108
+ case AsyncFileSystem():
109
+ self.fs = fs
110
+ case AbstractFileSystem():
111
+ self.fs = AsyncFileSystemWrapper(fs)
112
+ case None:
113
+ self.fs = AsyncLocalFileSystem()
114
+ self._tools: list[Tool] | None = None
115
+ self._diagnostics: DiagnosticsManager | None = None
116
+
117
+ def _get_env(self, agent_ctx: AgentContext) -> ExecutionEnvironment | None:
118
+ """Get execution environment (explicit or from agent)."""
119
+ return self.execution_env or agent_ctx.agent.env
120
+
121
+ def _resolve_path(self, path: str, agent_ctx: AgentContext) -> str:
122
+ """Resolve a potentially relative path to an absolute path.
123
+
124
+ Gets cwd from self.cwd, execution_env.cwd, or agent.env.cwd.
125
+ If cwd is set and path is relative, resolves relative to cwd.
126
+ Otherwise returns the path as-is.
127
+ """
128
+ env = self._get_env(agent_ctx)
129
+ cwd = self.cwd or (env.cwd if env else None)
130
+ if cwd and not (path.startswith("/") or (len(path) > 1 and path[1] == ":")):
131
+ return str(Path(cwd) / path)
132
+ return path
133
+
134
+ async def get_tools(self) -> list[Tool]:
135
+ """Get code analysis tools."""
136
+ if self._tools is not None:
137
+ return self._tools
138
+
139
+ self._tools = [self.create_tool(self.format_code, category="execute")]
140
+ if importlib.util.find_spec("ast_grep_py"):
141
+ self._tools.append(self.create_tool(self.ast_grep, category="search", idempotent=True))
142
+ # Always register - checks for env at runtime (self.execution_env or agent.env)
143
+ self._tools.append(
144
+ self.create_tool(self.run_diagnostics, category="search", idempotent=True)
145
+ )
146
+ return self._tools
147
+
148
+ async def format_code( # noqa: D417
149
+ self, agent_ctx: AgentContext, path: str, language: str | None = None
150
+ ) -> str:
151
+ """Format and lint a code file, returning a concise summary.
152
+
153
+ Args:
154
+ path: Path to the file to format
155
+ language: Programming language (auto-detected from extension if not provided)
156
+
157
+ Returns:
158
+ Short status message about formatting/linting results
159
+ """
160
+ from anyenv.language_formatters import FormatterRegistry
161
+
162
+ resolved = self._resolve_path(path, agent_ctx)
163
+ try:
164
+ content = await self.fs._cat_file(resolved)
165
+ code = content.decode("utf-8") if isinstance(content, bytes) else content
166
+ except FileNotFoundError:
167
+ return f"❌ File not found: {path}"
168
+
169
+ registry = FormatterRegistry("local")
170
+ registry.register_default_formatters()
171
+ # Get formatter by language or detect from extension/content
172
+ formatter = None
173
+ if language:
174
+ formatter = registry.get_formatter_by_language(language)
175
+ if not formatter:
176
+ detected = _detect_language(path)
177
+ if detected:
178
+ formatter = registry.get_formatter_by_language(detected)
179
+ if not formatter:
180
+ detected = registry.detect_language_from_content(code)
181
+ if detected:
182
+ formatter = registry.get_formatter_by_language(detected)
183
+ if not formatter:
184
+ return f"❌ Unsupported language: {language or 'unknown'}"
185
+
186
+ try:
187
+ result = await formatter.format_and_lint_string(code, fix=True)
188
+
189
+ if result.success:
190
+ # Write back if formatted
191
+ if result.format_result.formatted and result.format_result.output:
192
+ await self.fs._pipe_file(resolved, result.format_result.output.encode("utf-8"))
193
+ changes = "formatted and saved"
194
+ else:
195
+ changes = "no changes needed"
196
+ lint_status = "clean" if result.lint_result.success else "has issues"
197
+ duration = f"{result.total_duration:.2f}s"
198
+ return f"✅ {path}: {changes}, {lint_status} ({duration})"
199
+
200
+ errors = []
201
+ if not result.format_result.success:
202
+ errors.append(f"format: {result.format_result.error_type}")
203
+ if not result.lint_result.success:
204
+ errors.append(f"lint: {result.lint_result.error_type}")
205
+ return f"❌ Failed: {', '.join(errors)}"
206
+
207
+ except Exception as e: # noqa: BLE001
208
+ return f"❌ Error: {type(e).__name__}: {e}"
209
+
210
+ async def ast_grep( # noqa: D417
211
+ self,
212
+ agent_ctx: AgentContext,
213
+ path: str,
214
+ rule: dict[str, Any],
215
+ fix: str | None = None,
216
+ dry_run: bool = True,
217
+ ) -> dict[str, Any]:
218
+ """Search or transform code in a file using AST patterns.
219
+
220
+ Uses ast-grep for structural code search and rewriting based on abstract
221
+ syntax trees. More precise than regex - understands code structure.
222
+
223
+ Args:
224
+ path: Path to the file to analyze
225
+ rule: AST matching rule dict (see examples below)
226
+ fix: Optional replacement pattern using $METAVARS from the rule
227
+ dry_run: If True, show changes without applying. If False, write changes.
228
+
229
+ Returns:
230
+ Dict with matches and optionally transformed code
231
+
232
+ ## Pattern Syntax
233
+
234
+ - `$NAME` - captures single node (identifier, expression, etc.)
235
+ - `$$$ITEMS` - captures multiple nodes (arguments, statements, etc.)
236
+ - Patterns match structurally, not textually
237
+
238
+ ## Rule Keys
239
+
240
+ | Key | Description | Example |
241
+ |-----|-------------|---------|
242
+ | pattern | Code pattern with metavars | `"print($MSG)"` |
243
+ | kind | AST node type | `"function_definition"` |
244
+ | regex | Regex on node text | `"^test_"` |
245
+ | inside | Must be inside matching node | `{"kind": "class_definition"}` |
246
+ | has | Must contain matching node | `{"pattern": "return"}` |
247
+ | all | All rules must match | `[{"kind": "call"}, {"has": ...}]` |
248
+ | any | Any rule must match | `[{"pattern": "print"}, {"pattern": "log"}]` |
249
+
250
+ ## Examples
251
+
252
+ **Find all print calls:**
253
+ ```
254
+ rule={"pattern": "print($MSG)"}
255
+ ```
256
+
257
+ **Find and replace console.log:**
258
+ ```
259
+ rule={"pattern": "console.log($MSG)"}
260
+ fix="logger.info($MSG)"
261
+ ```
262
+
263
+ **Find functions containing await:**
264
+ ```
265
+ rule={
266
+ "kind": "function_definition",
267
+ "has": {"pattern": "await $EXPR"}
268
+ }
269
+ ```
270
+ """
271
+ from ast_grep_py import SgRoot
272
+
273
+ resolved = self._resolve_path(path, agent_ctx)
274
+
275
+ # Detect language from extension
276
+ language = _detect_language(path)
277
+ if not language:
278
+ return {"error": f"Cannot detect language for: {path}"}
279
+
280
+ # Read file
281
+ try:
282
+ content = await self.fs._cat_file(resolved)
283
+ code = content.decode("utf-8") if isinstance(content, bytes) else content
284
+ except FileNotFoundError:
285
+ return {"error": f"File not found: {path}"}
286
+
287
+ root = SgRoot(code, language)
288
+ node = root.root()
289
+ matches = node.find_all(**rule)
290
+
291
+ result: dict[str, Any] = {
292
+ "path": path,
293
+ "language": language,
294
+ "match_count": len(matches),
295
+ "matches": [
296
+ {
297
+ "text": m.text(),
298
+ "range": {
299
+ "start": {
300
+ "line": m.range().start.line,
301
+ "column": m.range().start.column,
302
+ },
303
+ "end": {
304
+ "line": m.range().end.line,
305
+ "column": m.range().end.column,
306
+ },
307
+ },
308
+ "kind": m.kind(),
309
+ }
310
+ for m in matches
311
+ ],
312
+ }
313
+
314
+ if fix and matches:
315
+ edits = [m.replace(_substitute_metavars(m, fix, code)) for m in matches]
316
+ fixed_code = node.commit_edits(edits)
317
+ result["fixed_code"] = fixed_code
318
+ result["dry_run"] = dry_run
319
+
320
+ if not dry_run:
321
+ await self.fs._pipe_file(resolved, fixed_code.encode("utf-8"))
322
+ result["written"] = True
323
+
324
+ return result
325
+
326
+ async def run_diagnostics(self, agent_ctx: AgentContext, path: str) -> str: # noqa: PLR0911, D417
327
+ """Run LSP diagnostics (type checking, linting) on files.
328
+
329
+ Uses available CLI diagnostic tools (pyright, mypy, ty, oxlint, biome, etc.)
330
+ to check code for errors, warnings, and style issues.
331
+
332
+ Args:
333
+ path: Path to file or directory to check. For directories, checks all
334
+ supported files recursively.
335
+
336
+ Returns:
337
+ Formatted diagnostic output showing errors, warnings, and info messages.
338
+ Returns a message if no diagnostics tools are available.
339
+
340
+ Note:
341
+ Only available when CodeTools is initialized with an ExecutionEnvironment.
342
+ Automatically detects available diagnostic tools and runs appropriate
343
+ ones based on file extensions.
344
+ """
345
+ resolved = self._resolve_path(path, agent_ctx)
346
+
347
+ # Get or create diagnostics manager
348
+ if self._diagnostics is None:
349
+ env = self._get_env(agent_ctx)
350
+ if not env:
351
+ return "Diagnostics unavailable: no execution environment configured"
352
+ self._diagnostics = DiagnosticsManager(env)
353
+
354
+ # Check if path is directory or file
355
+ try:
356
+ is_dir = await self.fs._isdir(resolved)
357
+ except Exception: # noqa: BLE001
358
+ is_dir = False
359
+
360
+ if is_dir:
361
+ # Collect all files in directory
362
+ try:
363
+ files = await self.fs._find(resolved, detail=True)
364
+ # Filter to only include files (not directories)
365
+ file_paths = [
366
+ path
367
+ for path, info in files.items() # pyright: ignore[reportAttributeAccessIssue]
368
+ if not await is_directory(self.fs, path, entry_type=info["type"])
369
+ ]
370
+ except Exception as e: # noqa: BLE001
371
+ return f"Error scanning directory: {e}"
372
+
373
+ if not file_paths:
374
+ return f"No files found in: {path}"
375
+
376
+ all_diagnostics: list[Diagnostic] = []
377
+ for file_path in file_paths:
378
+ diagnostics = await self._diagnostics.run_for_file(file_path)
379
+ all_diagnostics.extend(diagnostics)
380
+
381
+ if not all_diagnostics:
382
+ return f"No issues found in {len(file_paths)} files"
383
+
384
+ formatted = self._diagnostics.format_diagnostics(all_diagnostics)
385
+ return f"Found {len(all_diagnostics)} issues:\n{formatted}"
386
+ # Single file
387
+ try:
388
+ diagnostics = await self._diagnostics.run_for_file(resolved)
389
+ except FileNotFoundError:
390
+ return f"File not found: {path}"
391
+ except Exception as e: # noqa: BLE001
392
+ return f"Error running diagnostics: {e}"
393
+
394
+ if not diagnostics:
395
+ return f"No issues found in: {path}"
396
+
397
+ formatted = self._diagnostics.format_diagnostics(diagnostics)
398
+ return f"Found {len(diagnostics)} issues:\n{formatted}"