From edf3f59954f4ea31bfb08d09f5c2a392286ce2f6 Mon Sep 17 00:00:00 2001 From: Austin Horstman Date: Fri, 1 May 2026 12:17:18 -0500 Subject: [PATCH] github-copilot-cli: add lsp server support --- .../misc/news/2026/05/2026-05-01_17-00-00.nix | 12 ++++ modules/programs/github-copilot-cli.nix | 58 ++++++++++++++++++- .../programs/github-copilot-cli/default.nix | 1 + .../expected-lsp-config.json | 32 ++++++++++ .../programs/github-copilot-cli/lsp.nix | 38 ++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 modules/misc/news/2026/05/2026-05-01_17-00-00.nix create mode 100644 tests/modules/programs/github-copilot-cli/expected-lsp-config.json create mode 100644 tests/modules/programs/github-copilot-cli/lsp.nix diff --git a/modules/misc/news/2026/05/2026-05-01_17-00-00.nix b/modules/misc/news/2026/05/2026-05-01_17-00-00.nix new file mode 100644 index 0000000000..c450b1e0fb --- /dev/null +++ b/modules/misc/news/2026/05/2026-05-01_17-00-00.nix @@ -0,0 +1,12 @@ +{ config, ... }: +{ + time = "2026-05-01T17:00:00+00:00"; + condition = config.programs.github-copilot-cli.enable; + message = '' + The `programs.github-copilot-cli.lspServers` option has been added. + + This option manages the `lsp-config.json` file under `COPILOT_HOME`, + allowing GitHub Copilot CLI to start configured language servers for code + intelligence features. + ''; +} diff --git a/modules/programs/github-copilot-cli.nix b/modules/programs/github-copilot-cli.nix index 296eac59fd..9f5247e305 100644 --- a/modules/programs/github-copilot-cli.nix +++ b/modules/programs/github-copilot-cli.nix @@ -74,7 +74,8 @@ in example = literalExpression ''"''${config.xdg.configHome}/copilot"''; description = '' Directory holding Copilot CLI configuration files such as - {file}`config.json`, {file}`mcp-config.json`, and + {file}`config.json`, {file}`mcp-config.json`, + {file}`lsp-config.json`, and {file}`copilot-instructions.md`. Defaults to `''${config.xdg.configHome}/copilot` when @@ -201,6 +202,55 @@ in ''; }; + lspServers = mkOption { + type = lib.types.attrsOf jsonFormat.type; + default = { }; + example = literalExpression '' + { + typescript = { + command = "typescript-language-server"; + args = [ "--stdio" ]; + fileExtensions = { + ".ts" = "typescript"; + ".tsx" = "typescriptreact"; + ".js" = "javascript"; + ".jsx" = "javascriptreact"; + }; + }; + python = { + command = lib.getExe' pkgs.pyright "pyright-langserver"; + args = [ "--stdio" ]; + fileExtensions = { + ".py" = "python"; + ".pyw" = "python"; + ".pyi" = "python"; + }; + }; + } + ''; + description = '' + LSP (Language Server Protocol) server configurations written to + {file}`lsp-config.json` inside + {option}`programs.github-copilot-cli.configDir`. + + Each attribute defines a server entry under `lspServers` in the config + file. Supported fields include: + - `command` — command used to start the LSP server + - `args` — arguments passed to the command + - `fileExtensions` — map of file extensions to language IDs + - `env` — environment variables for the server process + - `rootUri` — server root directory relative to the Git root + - `initializationOptions` — custom startup options + - `requestTimeoutMs` — request timeout in milliseconds + + Language server packages are not installed automatically. Add them to + {option}`home.packages` when needed. + + See + for the documentation. + ''; + }; + agents = mkOption { type = lib.types.either (lib.types.attrsOf ( lib.types.oneOf [ @@ -324,6 +374,12 @@ in }; }; + "${cfg.configDir}/lsp-config.json" = mkIf (cfg.lspServers != { }) { + source = jsonFormat.generate "github-copilot-cli-lsp-config.json" { + inherit (cfg) lspServers; + }; + }; + "${cfg.configDir}/copilot-instructions.md" = if isPathLikeContent cfg.context then { source = cfg.context; } diff --git a/tests/modules/programs/github-copilot-cli/default.nix b/tests/modules/programs/github-copilot-cli/default.nix index ec9338f3d1..569b4701f6 100644 --- a/tests/modules/programs/github-copilot-cli/default.nix +++ b/tests/modules/programs/github-copilot-cli/default.nix @@ -1,6 +1,7 @@ { github-copilot-cli-config = ./config.nix; github-copilot-cli-directories = ./directories.nix; + github-copilot-cli-lsp = ./lsp.nix; github-copilot-cli-mcp = ./mcp.nix; github-copilot-cli-mcp-integration = ./mcp-integration.nix; github-copilot-cli-path-not-directory = ./path-not-directory.nix; diff --git a/tests/modules/programs/github-copilot-cli/expected-lsp-config.json b/tests/modules/programs/github-copilot-cli/expected-lsp-config.json new file mode 100644 index 0000000000..949782184e --- /dev/null +++ b/tests/modules/programs/github-copilot-cli/expected-lsp-config.json @@ -0,0 +1,32 @@ +{ + "lspServers": { + "python": { + "args": [ + "--stdio" + ], + "command": "pyright-langserver", + "env": { + "PYTHONPATH": "${PYTHONPATH:-}" + }, + "fileExtensions": { + ".py": "python", + ".pyi": "python", + ".pyw": "python" + }, + "requestTimeoutMs": 120000, + "rootUri": "backend" + }, + "typescript": { + "args": [ + "--stdio" + ], + "command": "typescript-language-server", + "fileExtensions": { + ".js": "javascript", + ".jsx": "javascriptreact", + ".ts": "typescript", + ".tsx": "typescriptreact" + } + } + } +} diff --git a/tests/modules/programs/github-copilot-cli/lsp.nix b/tests/modules/programs/github-copilot-cli/lsp.nix new file mode 100644 index 0000000000..f55afc5b84 --- /dev/null +++ b/tests/modules/programs/github-copilot-cli/lsp.nix @@ -0,0 +1,38 @@ +{ + programs.github-copilot-cli = { + enable = true; + lspServers = { + typescript = { + command = "typescript-language-server"; + args = [ "--stdio" ]; + fileExtensions = { + ".ts" = "typescript"; + ".tsx" = "typescriptreact"; + ".js" = "javascript"; + ".jsx" = "javascriptreact"; + }; + }; + python = { + command = "pyright-langserver"; + args = [ "--stdio" ]; + fileExtensions = { + ".py" = "python"; + ".pyw" = "python"; + ".pyi" = "python"; + }; + env = { + PYTHONPATH = "\${PYTHONPATH:-}"; + }; + rootUri = "backend"; + requestTimeoutMs = 120000; + }; + }; + }; + + nmt.script = '' + assertFileExists home-files/.copilot/lsp-config.json + assertFileContent home-files/.copilot/lsp-config.json ${./expected-lsp-config.json} + assertPathNotExists home-files/.copilot/config.json + assertPathNotExists home-files/.copilot/mcp-config.json + ''; +}