|
7 | 7 | import sys |
8 | 8 | from pathlib import Path |
9 | 9 |
|
| 10 | +from .lsp.config import LSPConfig |
10 | 11 | from .mcp.config import MCPConfig |
11 | 12 |
|
12 | 13 | # ---- context management ------------------------------------------------- |
|
256 | 257 | "context_path": null, |
257 | 258 | "skill_path": null |
258 | 259 | }}, |
| 260 | + "lsp": {{ |
| 261 | + "_comment": "Optional per-extension LSP server overrides. Keys are file extensions (e.g. '.py', '.cpp'); 'command' is the server argv; 'language_id' defaults to the extension without the dot. These override the built-in DEFAULT_SERVERS table. Remove this section to use only the built-ins. The server binary must be on PATH. The '.example' entry below is inert (no such extension) — copy it to a real extension like '.cpp' to activate.", |
| 262 | + "servers": {{ |
| 263 | + ".example": {{ |
| 264 | + "command": ["clangd", "--background-index", "--clang-tidy"], |
| 265 | + "language_id": "cpp" |
| 266 | + }} |
| 267 | + }} |
| 268 | + }}, |
259 | 269 | "mcp": {{ |
260 | 270 | "_comment": "Optional MCP servers (requires: pip install -e '.[mcp]'). Each server's tools become agent tools named mcp__<server>__<tool>. Transports: stdio (spawn command+args, pass through env var names), streamable-http / sse (connect to url, optional headers). 'parallel: true' marks read-only servers whose tools may run concurrently; default is serial. 'timeout' bounds connects, discovery and calls (seconds).", |
261 | 271 | "servers": {{ |
@@ -447,6 +457,31 @@ def load_mcp_config(path: str | os.PathLike | None = None) -> MCPConfig: |
447 | 457 | return MCPConfig.from_dict(section.get("servers")) |
448 | 458 |
|
449 | 459 |
|
| 460 | +def load_lsp_config(path: str | os.PathLike | None = None) -> LSPConfig: |
| 461 | + """Load per-extension LSP server overrides from the config file. |
| 462 | +
|
| 463 | + Reads the optional ``lsp.servers`` object (keyed by file extension, |
| 464 | + e.g. ``".py"``) and returns an ``LSPConfig`` (empty when the file |
| 465 | + has no ``lsp`` section or no servers). These entries layer on top of |
| 466 | + the built-in ``DEFAULT_SERVERS`` table in ``lsp/manager.py``. |
| 467 | +
|
| 468 | + Malformed entries raise ValueError so config errors surface at |
| 469 | + session start rather than the first LSP call. |
| 470 | + """ |
| 471 | + data = _read_config(path) |
| 472 | + section = data.get("lsp") or {} |
| 473 | + if not isinstance(section, dict): |
| 474 | + raise ValueError(f"config file {_config_path(path)}: lsp must be an object") |
| 475 | + servers = section.get("servers") or {} |
| 476 | + if not isinstance(servers, dict): |
| 477 | + raise ValueError(f"config file {_config_path(path)}: lsp.servers must be an object") |
| 478 | + try: |
| 479 | + return LSPConfig.from_dict(servers) |
| 480 | + except ValueError as e: |
| 481 | + # Re-raise with the config-file path for a clearer message. |
| 482 | + raise ValueError(f"config file {_config_path(path)}: {e}") from e |
| 483 | + |
| 484 | + |
450 | 485 | def load_models_config(path: str | os.PathLike | None = None) -> dict[str, dict]: |
451 | 486 | """Load named LLM profiles from the config file's ``models`` object. |
452 | 487 |
|
|
0 commit comments