agentpool 2.1.9__py3-none-any.whl → 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. acp/__init__.py +13 -4
  2. acp/acp_requests.py +20 -77
  3. acp/agent/connection.py +8 -0
  4. acp/agent/implementations/debug_server/debug_server.py +6 -2
  5. acp/agent/protocol.py +6 -0
  6. acp/bridge/README.md +15 -2
  7. acp/bridge/__init__.py +3 -2
  8. acp/bridge/__main__.py +60 -19
  9. acp/bridge/ws_server.py +173 -0
  10. acp/bridge/ws_server_cli.py +89 -0
  11. acp/client/connection.py +38 -29
  12. acp/client/implementations/default_client.py +3 -2
  13. acp/client/implementations/headless_client.py +2 -2
  14. acp/connection.py +2 -2
  15. acp/notifications.py +20 -50
  16. acp/schema/__init__.py +2 -0
  17. acp/schema/agent_responses.py +21 -0
  18. acp/schema/client_requests.py +3 -3
  19. acp/schema/session_state.py +63 -29
  20. acp/stdio.py +39 -9
  21. acp/task/supervisor.py +2 -2
  22. acp/transports.py +362 -2
  23. acp/utils.py +17 -4
  24. agentpool/__init__.py +6 -1
  25. agentpool/agents/__init__.py +2 -0
  26. agentpool/agents/acp_agent/acp_agent.py +407 -277
  27. agentpool/agents/acp_agent/acp_converters.py +196 -38
  28. agentpool/agents/acp_agent/client_handler.py +191 -26
  29. agentpool/agents/acp_agent/session_state.py +17 -6
  30. agentpool/agents/agent.py +607 -572
  31. agentpool/agents/agui_agent/__init__.py +0 -2
  32. agentpool/agents/agui_agent/agui_agent.py +176 -110
  33. agentpool/agents/agui_agent/agui_converters.py +0 -131
  34. agentpool/agents/agui_agent/helpers.py +3 -4
  35. agentpool/agents/base_agent.py +632 -17
  36. agentpool/agents/claude_code_agent/FORKING.md +191 -0
  37. agentpool/agents/claude_code_agent/__init__.py +13 -1
  38. agentpool/agents/claude_code_agent/claude_code_agent.py +1058 -291
  39. agentpool/agents/claude_code_agent/converters.py +74 -143
  40. agentpool/agents/claude_code_agent/history.py +474 -0
  41. agentpool/agents/claude_code_agent/models.py +77 -0
  42. agentpool/agents/claude_code_agent/static_info.py +100 -0
  43. agentpool/agents/claude_code_agent/usage.py +242 -0
  44. agentpool/agents/context.py +40 -0
  45. agentpool/agents/events/__init__.py +24 -0
  46. agentpool/agents/events/builtin_handlers.py +67 -1
  47. agentpool/agents/events/event_emitter.py +32 -2
  48. agentpool/agents/events/events.py +104 -3
  49. agentpool/agents/events/infer_info.py +145 -0
  50. agentpool/agents/events/processors.py +254 -0
  51. agentpool/agents/interactions.py +41 -6
  52. agentpool/agents/modes.py +67 -0
  53. agentpool/agents/slashed_agent.py +5 -4
  54. agentpool/agents/tool_call_accumulator.py +213 -0
  55. agentpool/agents/tool_wrapping.py +18 -6
  56. agentpool/common_types.py +56 -21
  57. agentpool/config_resources/__init__.py +38 -1
  58. agentpool/config_resources/acp_assistant.yml +2 -2
  59. agentpool/config_resources/agents.yml +3 -0
  60. agentpool/config_resources/agents_template.yml +1 -0
  61. agentpool/config_resources/claude_code_agent.yml +10 -6
  62. agentpool/config_resources/external_acp_agents.yml +2 -1
  63. agentpool/delegation/base_team.py +4 -30
  64. agentpool/delegation/pool.py +136 -289
  65. agentpool/delegation/team.py +58 -57
  66. agentpool/delegation/teamrun.py +51 -55
  67. agentpool/diagnostics/__init__.py +53 -0
  68. agentpool/diagnostics/lsp_manager.py +1593 -0
  69. agentpool/diagnostics/lsp_proxy.py +41 -0
  70. agentpool/diagnostics/lsp_proxy_script.py +229 -0
  71. agentpool/diagnostics/models.py +398 -0
  72. agentpool/functional/run.py +10 -4
  73. agentpool/mcp_server/__init__.py +0 -2
  74. agentpool/mcp_server/client.py +76 -32
  75. agentpool/mcp_server/conversions.py +54 -13
  76. agentpool/mcp_server/manager.py +34 -54
  77. agentpool/mcp_server/registries/official_registry_client.py +35 -1
  78. agentpool/mcp_server/tool_bridge.py +186 -139
  79. agentpool/messaging/__init__.py +0 -2
  80. agentpool/messaging/compaction.py +72 -197
  81. agentpool/messaging/connection_manager.py +11 -10
  82. agentpool/messaging/event_manager.py +5 -5
  83. agentpool/messaging/message_container.py +6 -30
  84. agentpool/messaging/message_history.py +99 -8
  85. agentpool/messaging/messagenode.py +52 -14
  86. agentpool/messaging/messages.py +54 -35
  87. agentpool/messaging/processing.py +12 -22
  88. agentpool/models/__init__.py +1 -1
  89. agentpool/models/acp_agents/base.py +6 -24
  90. agentpool/models/acp_agents/mcp_capable.py +126 -157
  91. agentpool/models/acp_agents/non_mcp.py +129 -95
  92. agentpool/models/agents.py +98 -76
  93. agentpool/models/agui_agents.py +1 -1
  94. agentpool/models/claude_code_agents.py +144 -19
  95. agentpool/models/file_parsing.py +0 -1
  96. agentpool/models/manifest.py +113 -50
  97. agentpool/prompts/conversion_manager.py +1 -1
  98. agentpool/prompts/prompts.py +5 -2
  99. agentpool/repomap.py +1 -1
  100. agentpool/resource_providers/__init__.py +11 -1
  101. agentpool/resource_providers/aggregating.py +56 -5
  102. agentpool/resource_providers/base.py +70 -4
  103. agentpool/resource_providers/codemode/code_executor.py +72 -5
  104. agentpool/resource_providers/codemode/helpers.py +2 -2
  105. agentpool/resource_providers/codemode/provider.py +64 -12
  106. agentpool/resource_providers/codemode/remote_mcp_execution.py +2 -2
  107. agentpool/resource_providers/codemode/remote_provider.py +9 -12
  108. agentpool/resource_providers/filtering.py +3 -1
  109. agentpool/resource_providers/mcp_provider.py +89 -12
  110. agentpool/resource_providers/plan_provider.py +228 -46
  111. agentpool/resource_providers/pool.py +7 -3
  112. agentpool/resource_providers/resource_info.py +111 -0
  113. agentpool/resource_providers/static.py +4 -2
  114. agentpool/sessions/__init__.py +4 -1
  115. agentpool/sessions/manager.py +33 -5
  116. agentpool/sessions/models.py +59 -6
  117. agentpool/sessions/protocol.py +28 -0
  118. agentpool/sessions/session.py +11 -55
  119. agentpool/skills/registry.py +13 -8
  120. agentpool/storage/manager.py +572 -49
  121. agentpool/talk/registry.py +4 -4
  122. agentpool/talk/talk.py +9 -10
  123. agentpool/testing.py +538 -20
  124. agentpool/tool_impls/__init__.py +6 -0
  125. agentpool/tool_impls/agent_cli/__init__.py +42 -0
  126. agentpool/tool_impls/agent_cli/tool.py +95 -0
  127. agentpool/tool_impls/bash/__init__.py +64 -0
  128. agentpool/tool_impls/bash/helpers.py +35 -0
  129. agentpool/tool_impls/bash/tool.py +171 -0
  130. agentpool/tool_impls/delete_path/__init__.py +70 -0
  131. agentpool/tool_impls/delete_path/tool.py +142 -0
  132. agentpool/tool_impls/download_file/__init__.py +80 -0
  133. agentpool/tool_impls/download_file/tool.py +183 -0
  134. agentpool/tool_impls/execute_code/__init__.py +55 -0
  135. agentpool/tool_impls/execute_code/tool.py +163 -0
  136. agentpool/tool_impls/grep/__init__.py +80 -0
  137. agentpool/tool_impls/grep/tool.py +200 -0
  138. agentpool/tool_impls/list_directory/__init__.py +73 -0
  139. agentpool/tool_impls/list_directory/tool.py +197 -0
  140. agentpool/tool_impls/question/__init__.py +42 -0
  141. agentpool/tool_impls/question/tool.py +127 -0
  142. agentpool/tool_impls/read/__init__.py +104 -0
  143. agentpool/tool_impls/read/tool.py +305 -0
  144. agentpool/tools/__init__.py +2 -1
  145. agentpool/tools/base.py +114 -34
  146. agentpool/tools/manager.py +57 -1
  147. agentpool/ui/base.py +2 -2
  148. agentpool/ui/mock_provider.py +2 -2
  149. agentpool/ui/stdlib_provider.py +2 -2
  150. agentpool/utils/file_watcher.py +269 -0
  151. agentpool/utils/identifiers.py +121 -0
  152. agentpool/utils/pydantic_ai_helpers.py +46 -0
  153. agentpool/utils/streams.py +616 -2
  154. agentpool/utils/subprocess_utils.py +155 -0
  155. agentpool/utils/token_breakdown.py +461 -0
  156. agentpool/vfs_registry.py +7 -2
  157. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/METADATA +41 -27
  158. agentpool-2.5.0.dist-info/RECORD +579 -0
  159. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/WHEEL +1 -1
  160. agentpool_cli/__main__.py +24 -0
  161. agentpool_cli/create.py +1 -1
  162. agentpool_cli/serve_acp.py +100 -21
  163. agentpool_cli/serve_agui.py +87 -0
  164. agentpool_cli/serve_opencode.py +119 -0
  165. agentpool_cli/ui.py +557 -0
  166. agentpool_commands/__init__.py +42 -5
  167. agentpool_commands/agents.py +75 -2
  168. agentpool_commands/history.py +62 -0
  169. agentpool_commands/mcp.py +176 -0
  170. agentpool_commands/models.py +56 -3
  171. agentpool_commands/pool.py +260 -0
  172. agentpool_commands/session.py +1 -1
  173. agentpool_commands/text_sharing/__init__.py +119 -0
  174. agentpool_commands/text_sharing/base.py +123 -0
  175. agentpool_commands/text_sharing/github_gist.py +80 -0
  176. agentpool_commands/text_sharing/opencode.py +462 -0
  177. agentpool_commands/text_sharing/paste_rs.py +59 -0
  178. agentpool_commands/text_sharing/pastebin.py +116 -0
  179. agentpool_commands/text_sharing/shittycodingagent.py +112 -0
  180. agentpool_commands/tools.py +57 -0
  181. agentpool_commands/utils.py +80 -30
  182. agentpool_config/__init__.py +30 -2
  183. agentpool_config/agentpool_tools.py +498 -0
  184. agentpool_config/builtin_tools.py +77 -22
  185. agentpool_config/commands.py +24 -1
  186. agentpool_config/compaction.py +258 -0
  187. agentpool_config/converters.py +1 -1
  188. agentpool_config/event_handlers.py +42 -0
  189. agentpool_config/events.py +1 -1
  190. agentpool_config/forward_targets.py +1 -4
  191. agentpool_config/jinja.py +3 -3
  192. agentpool_config/mcp_server.py +132 -6
  193. agentpool_config/nodes.py +1 -1
  194. agentpool_config/observability.py +44 -0
  195. agentpool_config/session.py +0 -3
  196. agentpool_config/storage.py +82 -38
  197. agentpool_config/task.py +3 -3
  198. agentpool_config/tools.py +11 -22
  199. agentpool_config/toolsets.py +109 -233
  200. agentpool_server/a2a_server/agent_worker.py +307 -0
  201. agentpool_server/a2a_server/server.py +23 -18
  202. agentpool_server/acp_server/acp_agent.py +234 -181
  203. agentpool_server/acp_server/commands/acp_commands.py +151 -156
  204. agentpool_server/acp_server/commands/docs_commands/fetch_repo.py +18 -17
  205. agentpool_server/acp_server/event_converter.py +651 -0
  206. agentpool_server/acp_server/input_provider.py +53 -10
  207. agentpool_server/acp_server/server.py +24 -90
  208. agentpool_server/acp_server/session.py +173 -331
  209. agentpool_server/acp_server/session_manager.py +8 -34
  210. agentpool_server/agui_server/server.py +3 -1
  211. agentpool_server/mcp_server/server.py +5 -2
  212. agentpool_server/opencode_server/.rules +95 -0
  213. agentpool_server/opencode_server/ENDPOINTS.md +401 -0
  214. agentpool_server/opencode_server/OPENCODE_UI_TOOLS_COMPLETE.md +202 -0
  215. agentpool_server/opencode_server/__init__.py +19 -0
  216. agentpool_server/opencode_server/command_validation.py +172 -0
  217. agentpool_server/opencode_server/converters.py +975 -0
  218. agentpool_server/opencode_server/dependencies.py +24 -0
  219. agentpool_server/opencode_server/input_provider.py +421 -0
  220. agentpool_server/opencode_server/models/__init__.py +250 -0
  221. agentpool_server/opencode_server/models/agent.py +53 -0
  222. agentpool_server/opencode_server/models/app.py +72 -0
  223. agentpool_server/opencode_server/models/base.py +26 -0
  224. agentpool_server/opencode_server/models/common.py +23 -0
  225. agentpool_server/opencode_server/models/config.py +37 -0
  226. agentpool_server/opencode_server/models/events.py +821 -0
  227. agentpool_server/opencode_server/models/file.py +88 -0
  228. agentpool_server/opencode_server/models/mcp.py +44 -0
  229. agentpool_server/opencode_server/models/message.py +179 -0
  230. agentpool_server/opencode_server/models/parts.py +323 -0
  231. agentpool_server/opencode_server/models/provider.py +81 -0
  232. agentpool_server/opencode_server/models/pty.py +43 -0
  233. agentpool_server/opencode_server/models/question.py +56 -0
  234. agentpool_server/opencode_server/models/session.py +111 -0
  235. agentpool_server/opencode_server/routes/__init__.py +29 -0
  236. agentpool_server/opencode_server/routes/agent_routes.py +473 -0
  237. agentpool_server/opencode_server/routes/app_routes.py +202 -0
  238. agentpool_server/opencode_server/routes/config_routes.py +302 -0
  239. agentpool_server/opencode_server/routes/file_routes.py +571 -0
  240. agentpool_server/opencode_server/routes/global_routes.py +94 -0
  241. agentpool_server/opencode_server/routes/lsp_routes.py +319 -0
  242. agentpool_server/opencode_server/routes/message_routes.py +761 -0
  243. agentpool_server/opencode_server/routes/permission_routes.py +63 -0
  244. agentpool_server/opencode_server/routes/pty_routes.py +300 -0
  245. agentpool_server/opencode_server/routes/question_routes.py +128 -0
  246. agentpool_server/opencode_server/routes/session_routes.py +1276 -0
  247. agentpool_server/opencode_server/routes/tui_routes.py +139 -0
  248. agentpool_server/opencode_server/server.py +475 -0
  249. agentpool_server/opencode_server/state.py +151 -0
  250. agentpool_server/opencode_server/time_utils.py +8 -0
  251. agentpool_storage/__init__.py +12 -0
  252. agentpool_storage/base.py +184 -2
  253. agentpool_storage/claude_provider/ARCHITECTURE.md +433 -0
  254. agentpool_storage/claude_provider/__init__.py +42 -0
  255. agentpool_storage/claude_provider/provider.py +1089 -0
  256. agentpool_storage/file_provider.py +278 -15
  257. agentpool_storage/memory_provider.py +193 -12
  258. agentpool_storage/models.py +3 -0
  259. agentpool_storage/opencode_provider/ARCHITECTURE.md +386 -0
  260. agentpool_storage/opencode_provider/__init__.py +16 -0
  261. agentpool_storage/opencode_provider/helpers.py +414 -0
  262. agentpool_storage/opencode_provider/provider.py +895 -0
  263. agentpool_storage/project_store.py +325 -0
  264. agentpool_storage/session_store.py +26 -6
  265. agentpool_storage/sql_provider/__init__.py +4 -2
  266. agentpool_storage/sql_provider/models.py +48 -0
  267. agentpool_storage/sql_provider/sql_provider.py +269 -3
  268. agentpool_storage/sql_provider/utils.py +12 -13
  269. agentpool_storage/zed_provider/__init__.py +16 -0
  270. agentpool_storage/zed_provider/helpers.py +281 -0
  271. agentpool_storage/zed_provider/models.py +130 -0
  272. agentpool_storage/zed_provider/provider.py +442 -0
  273. agentpool_storage/zed_provider.py +803 -0
  274. agentpool_toolsets/__init__.py +0 -2
  275. agentpool_toolsets/builtin/__init__.py +2 -12
  276. agentpool_toolsets/builtin/code.py +96 -57
  277. agentpool_toolsets/builtin/debug.py +118 -48
  278. agentpool_toolsets/builtin/execution_environment.py +115 -230
  279. agentpool_toolsets/builtin/file_edit/file_edit.py +115 -7
  280. agentpool_toolsets/builtin/skills.py +9 -4
  281. agentpool_toolsets/builtin/subagent_tools.py +64 -51
  282. agentpool_toolsets/builtin/workers.py +4 -2
  283. agentpool_toolsets/composio_toolset.py +2 -2
  284. agentpool_toolsets/entry_points.py +3 -1
  285. agentpool_toolsets/fsspec_toolset/__init__.py +13 -1
  286. agentpool_toolsets/fsspec_toolset/diagnostics.py +860 -73
  287. agentpool_toolsets/fsspec_toolset/grep.py +99 -7
  288. agentpool_toolsets/fsspec_toolset/helpers.py +3 -2
  289. agentpool_toolsets/fsspec_toolset/image_utils.py +161 -0
  290. agentpool_toolsets/fsspec_toolset/toolset.py +500 -95
  291. agentpool_toolsets/mcp_discovery/__init__.py +5 -0
  292. agentpool_toolsets/mcp_discovery/data/mcp_servers.parquet +0 -0
  293. agentpool_toolsets/mcp_discovery/toolset.py +511 -0
  294. agentpool_toolsets/mcp_run_toolset.py +87 -12
  295. agentpool_toolsets/notifications.py +33 -33
  296. agentpool_toolsets/openapi.py +3 -1
  297. agentpool_toolsets/search_toolset.py +3 -1
  298. agentpool-2.1.9.dist-info/RECORD +0 -474
  299. agentpool_config/resources.py +0 -33
  300. agentpool_server/acp_server/acp_tools.py +0 -43
  301. agentpool_server/acp_server/commands/spawn.py +0 -210
  302. agentpool_storage/text_log_provider.py +0 -275
  303. agentpool_toolsets/builtin/agent_management.py +0 -239
  304. agentpool_toolsets/builtin/chain.py +0 -288
  305. agentpool_toolsets/builtin/history.py +0 -36
  306. agentpool_toolsets/builtin/integration.py +0 -85
  307. agentpool_toolsets/builtin/tool_management.py +0 -90
  308. agentpool_toolsets/builtin/user_interaction.py +0 -52
  309. agentpool_toolsets/semantic_memory_toolset.py +0 -536
  310. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/entry_points.txt +0 -0
  311. {agentpool-2.1.9.dist-info → agentpool-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,319 @@
1
+ """LSP (Language Server Protocol) routes.
2
+
3
+ Provides endpoints for LSP server status and diagnostics,
4
+ compatible with OpenCode's LSP API.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from contextlib import suppress
10
+ import os
11
+ from typing import Literal
12
+
13
+ from fastapi import APIRouter, HTTPException, Query
14
+ from pydantic import BaseModel
15
+
16
+ from agentpool_server.opencode_server.dependencies import StateDep
17
+ from agentpool_server.opencode_server.models.events import LspStatus, LspUpdatedEvent
18
+
19
+
20
+ # =============================================================================
21
+ # Diagnostic Models (matching OpenCode's LSP diagnostic format)
22
+ # =============================================================================
23
+
24
+
25
+ class DiagnosticPosition(BaseModel):
26
+ """Position in a text document."""
27
+
28
+ line: int
29
+ character: int
30
+
31
+
32
+ class DiagnosticRange(BaseModel):
33
+ """Range in a text document."""
34
+
35
+ start: DiagnosticPosition
36
+ end: DiagnosticPosition
37
+
38
+
39
+ class Diagnostic(BaseModel):
40
+ """LSP Diagnostic matching vscode-languageserver-types format."""
41
+
42
+ range: DiagnosticRange
43
+ message: str
44
+ severity: int | None = None # 1=Error, 2=Warning, 3=Info, 4=Hint
45
+ code: str | int | None = None
46
+ source: str | None = None
47
+
48
+
49
+ class FormatterStatus(BaseModel):
50
+ """Formatter status information."""
51
+
52
+ id: str
53
+ """Formatter identifier."""
54
+
55
+ name: str
56
+ """Formatter name."""
57
+
58
+ root: str
59
+ """Workspace root path."""
60
+
61
+ status: Literal["connected", "error"]
62
+ """Connection status."""
63
+
64
+
65
+ router = APIRouter(tags=["lsp"])
66
+
67
+
68
+ @router.get("/lsp")
69
+ async def list_lsp_servers(state: StateDep) -> list[LspStatus]:
70
+ """List all active LSP servers.
71
+
72
+ Returns the status of all running LSP servers, including their
73
+ connection state and workspace root.
74
+
75
+ Returns:
76
+ List of LSP server status objects.
77
+ """
78
+ try:
79
+ lsp_manager = state.get_or_create_lsp_manager()
80
+ except RuntimeError:
81
+ # Agent doesn't have an execution environment - return empty list
82
+ return []
83
+
84
+ servers: list[LspStatus] = []
85
+ for server_id, server_state in lsp_manager._servers.items():
86
+ # Get relative root path
87
+ root_uri = server_state.root_uri or ""
88
+ if root_uri.startswith("file://"):
89
+ root_path = root_uri[7:] # Remove file:// prefix
90
+ # Make path relative to working directory
91
+ with suppress(ValueError):
92
+ root_path = os.path.relpath(root_path, state.working_dir)
93
+ else:
94
+ root_path = root_uri
95
+
96
+ servers.append(
97
+ LspStatus(
98
+ id=server_id,
99
+ name=server_id,
100
+ root=root_path,
101
+ status="connected" if server_state.initialized else "error",
102
+ )
103
+ )
104
+
105
+ return servers
106
+
107
+
108
+ @router.post("/lsp/start")
109
+ async def start_lsp_server(
110
+ state: StateDep,
111
+ server_id: str = Query(..., description="LSP server ID (e.g., 'pyright', 'rust-analyzer')"),
112
+ root_uri: str | None = Query(None, description="Workspace root URI"),
113
+ ) -> LspStatus:
114
+ """Start an LSP server.
115
+
116
+ Starts the specified LSP server for the given workspace root.
117
+ If no root_uri is provided, uses the server's working directory.
118
+
119
+ Args:
120
+ state: Server state dependency (injected).
121
+ server_id: The LSP server identifier (e.g., 'pyright', 'typescript').
122
+ root_uri: Optional workspace root URI (file:// format).
123
+
124
+ Returns:
125
+ The started server's status.
126
+
127
+ Raises:
128
+ HTTPException: If the server fails to start or is not registered.
129
+ """
130
+ try:
131
+ lsp_manager = state.get_or_create_lsp_manager()
132
+ except RuntimeError as e:
133
+ raise HTTPException(status_code=503, detail=str(e)) from e
134
+
135
+ # Default to working directory if no root provided
136
+ if root_uri is None:
137
+ root_uri = f"file://{state.working_dir}"
138
+
139
+ try:
140
+ server_state = await lsp_manager.start_server(server_id, root_uri)
141
+ except ValueError as e:
142
+ raise HTTPException(status_code=404, detail=str(e)) from e
143
+ except RuntimeError as e:
144
+ raise HTTPException(status_code=500, detail=str(e)) from e
145
+
146
+ # Emit lsp.updated event to notify clients of server status change
147
+ await state.broadcast_event(LspUpdatedEvent.create())
148
+
149
+ # Get relative root path for response
150
+ root_path = root_uri
151
+ if root_uri.startswith("file://"):
152
+ root_path = root_uri[7:]
153
+ with suppress(ValueError):
154
+ root_path = os.path.relpath(root_path, state.working_dir)
155
+
156
+ return LspStatus(
157
+ id=server_id,
158
+ name=server_id,
159
+ root=root_path,
160
+ status="connected" if server_state.initialized else "error",
161
+ )
162
+
163
+
164
+ @router.post("/lsp/stop")
165
+ async def stop_lsp_server(
166
+ state: StateDep,
167
+ server_id: str = Query(..., description="LSP server ID to stop"),
168
+ ) -> dict[str, str]:
169
+ """Stop an LSP server.
170
+
171
+ Args:
172
+ state: Server state dependency (injected).
173
+ server_id: The LSP server identifier to stop.
174
+
175
+ Returns:
176
+ Success message.
177
+ """
178
+ try:
179
+ lsp_manager = state.get_or_create_lsp_manager()
180
+ except RuntimeError:
181
+ return {"status": "ok", "message": "No LSP manager active"}
182
+
183
+ await lsp_manager.stop_server(server_id)
184
+
185
+ # Emit lsp.updated event to notify clients of server status change
186
+ await state.broadcast_event(LspUpdatedEvent.create())
187
+
188
+ return {"status": "ok", "message": f"Server {server_id} stopped"}
189
+
190
+
191
+ @router.get("/lsp/diagnostics")
192
+ async def get_diagnostics(
193
+ state: StateDep,
194
+ path: str | None = Query(None, description="File path to get diagnostics for"),
195
+ ) -> dict[str, list[Diagnostic]]:
196
+ """Get diagnostics from all active LSP servers.
197
+
198
+ Returns diagnostics organized by file path. If a specific path is provided,
199
+ returns diagnostics only for that file using CLI diagnostics.
200
+
201
+ This uses CLI-based diagnostic tools (pyright, mypy, etc.) which are more
202
+ reliable for on-demand checks than the LSP push model.
203
+
204
+ Args:
205
+ state: Server state dependency (injected).
206
+ path: Optional file path to get diagnostics for.
207
+
208
+ Returns:
209
+ Dictionary mapping file paths to lists of diagnostic objects.
210
+ """
211
+ try:
212
+ lsp_manager = state.get_or_create_lsp_manager()
213
+ except RuntimeError:
214
+ return {}
215
+
216
+ results: dict[str, list[Diagnostic]] = {}
217
+
218
+ # If a specific path is provided, run CLI diagnostics for it
219
+ if path:
220
+ # Make path absolute if needed
221
+ if not os.path.isabs(path): # noqa: PTH117
222
+ path = os.path.join(state.working_dir, path) # noqa: PTH118
223
+
224
+ # Find the appropriate server for this file
225
+ server_info = lsp_manager.get_server_for_file(path)
226
+ if server_info and server_info.has_cli_diagnostics:
227
+ try:
228
+ result = await lsp_manager.run_cli_diagnostics(server_info.id, [path])
229
+ if result.success and result.diagnostics:
230
+ for diag in result.diagnostics:
231
+ file_path = diag.file or path
232
+ if file_path not in results:
233
+ results[file_path] = []
234
+ # Convert from 1-based (CLI tools) to 0-based (LSP)
235
+ results[file_path].append(
236
+ Diagnostic(
237
+ range=DiagnosticRange(
238
+ start=DiagnosticPosition(
239
+ line=max(0, diag.line - 1),
240
+ character=max(0, diag.column - 1),
241
+ ),
242
+ end=DiagnosticPosition(
243
+ line=max(0, (diag.end_line or diag.line) - 1),
244
+ character=max(0, (diag.end_column or diag.column) - 1),
245
+ ),
246
+ ),
247
+ message=diag.message,
248
+ severity=_severity_to_lsp(diag.severity),
249
+ code=diag.code,
250
+ source=diag.source or server_info.id,
251
+ )
252
+ )
253
+ except Exception: # noqa: BLE001
254
+ # CLI diagnostics failed, return empty
255
+ pass
256
+
257
+ return results
258
+
259
+
260
+ def _severity_to_lsp(severity: str) -> int:
261
+ """Convert severity string to LSP severity number."""
262
+ mapping = {
263
+ "error": 1,
264
+ "warning": 2,
265
+ "info": 3,
266
+ "hint": 4,
267
+ }
268
+ return mapping.get(severity.lower(), 1)
269
+
270
+
271
+ @router.get("/lsp/servers")
272
+ async def list_available_servers(state: StateDep) -> list[dict[str, object]]:
273
+ """List all registered (available) LSP servers.
274
+
275
+ Returns information about all LSP servers that can be started,
276
+ regardless of whether they are currently running.
277
+
278
+ Returns:
279
+ List of server configurations.
280
+ """
281
+ try:
282
+ lsp_manager = state.get_or_create_lsp_manager()
283
+ except RuntimeError:
284
+ return []
285
+
286
+ servers = []
287
+ for server_id, config in lsp_manager._server_configs.items():
288
+ servers.append({
289
+ "id": server_id,
290
+ "extensions": config.extensions,
291
+ "running": server_id in lsp_manager._servers,
292
+ })
293
+
294
+ return servers
295
+
296
+
297
+ # =============================================================================
298
+ # Formatter Routes
299
+ # =============================================================================
300
+
301
+
302
+ @router.get("/formatter")
303
+ async def list_formatters(state: StateDep) -> list[FormatterStatus]:
304
+ """List all active formatters.
305
+
306
+ Returns the status of all running formatters, including their
307
+ connection state and workspace root.
308
+
309
+ Note: This is currently a stub that returns an empty list.
310
+ Formatter support can be added in the future.
311
+
312
+ Returns:
313
+ List of formatter status objects.
314
+ """
315
+ # Stub implementation - formatters not yet implemented
316
+ # OpenCode has formatters like prettier, biome, etc.
317
+ # For now, return empty list
318
+ _ = state # Reserved for future use
319
+ return []