mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
github-copilot-cli: add module
This commit is contained in:
committed by
Austin Horstman
parent
f92e976f40
commit
503480e513
15
modules/misc/news/2026/04/2026-04-17_17-06-14.nix
Normal file
15
modules/misc/news/2026/04/2026-04-17_17-06-14.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
_: {
|
||||
time = "2026-04-17T15:06:14+00:00";
|
||||
condition = true;
|
||||
message = ''
|
||||
A new module is available: 'programs.github-copilot-cli'.
|
||||
|
||||
GitHub Copilot CLI brings the agentic Copilot coding experience to the
|
||||
terminal. The module manages the `~/.copilot/config.json` settings file
|
||||
(model, theme, trusted folders, hooks, feature flags, etc.) and the
|
||||
`~/.copilot/mcp-config.json` MCP server registry. Setting
|
||||
`enableMcpIntegration = true` reuses servers defined under
|
||||
`programs.mcp.servers`, with `programs.github-copilot-cli.mcpServers`
|
||||
taking precedence.
|
||||
'';
|
||||
}
|
||||
200
modules/programs/github-copilot-cli.nix
Normal file
200
modules/programs/github-copilot-cli.nix
Normal file
@@ -0,0 +1,200 @@
|
||||
{
|
||||
lib,
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
literalExpression
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
;
|
||||
|
||||
cfg = config.programs.github-copilot-cli;
|
||||
|
||||
jsonFormat = pkgs.formats.json { };
|
||||
|
||||
upstreamConfigDir = "${config.home.homeDirectory}/.copilot";
|
||||
|
||||
transformSingleServer =
|
||||
_name: server:
|
||||
let
|
||||
base = removeAttrs server [ "disabled" ];
|
||||
withType =
|
||||
if base ? type then
|
||||
base
|
||||
else if base ? url then
|
||||
base // { type = "http"; }
|
||||
else
|
||||
base // { type = "local"; };
|
||||
withTools = if withType ? tools then withType else withType // { tools = [ "*" ]; };
|
||||
in
|
||||
withTools;
|
||||
|
||||
transformedMcpServers =
|
||||
if cfg.enableMcpIntegration && config.programs.mcp.enable && config.programs.mcp.servers != { } then
|
||||
lib.mapAttrs transformSingleServer (
|
||||
lib.filterAttrs (
|
||||
_: server: !(server.disabled or false) && (server ? url || server ? command)
|
||||
) config.programs.mcp.servers
|
||||
)
|
||||
else
|
||||
{ };
|
||||
|
||||
mergedMcpServers = transformedMcpServers // cfg.mcpServers;
|
||||
in
|
||||
{
|
||||
meta.maintainers = [ lib.maintainers.ojsef39 ];
|
||||
|
||||
options.programs.github-copilot-cli = {
|
||||
enable = mkEnableOption "GitHub Copilot CLI";
|
||||
|
||||
package = mkPackageOption pkgs "github-copilot-cli" { nullable = true; };
|
||||
|
||||
configDir = mkOption {
|
||||
type = lib.types.str;
|
||||
default =
|
||||
if config.home.preferXdgDirectories then "${config.xdg.configHome}/copilot" else upstreamConfigDir;
|
||||
defaultText = literalExpression ''
|
||||
if config.home.preferXdgDirectories then
|
||||
"''${config.xdg.configHome}/copilot"
|
||||
else
|
||||
"''${config.home.homeDirectory}/.copilot"
|
||||
'';
|
||||
example = literalExpression ''"''${config.xdg.configHome}/copilot"'';
|
||||
description = ''
|
||||
Directory holding Copilot CLI configuration files such as
|
||||
{file}`config.json` and {file}`mcp-config.json`.
|
||||
|
||||
Defaults to `''${config.xdg.configHome}/copilot` when
|
||||
{option}`home.preferXdgDirectories` is enabled and to `~/.copilot`
|
||||
otherwise. The {env}`COPILOT_HOME` environment variable is exported
|
||||
automatically whenever the directory differs from the upstream
|
||||
default of `~/.copilot`.
|
||||
|
||||
See <https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference#changing-the-location-of-the-configuration-directory>.
|
||||
'';
|
||||
};
|
||||
|
||||
enableMcpIntegration = mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to integrate the MCP servers config from
|
||||
{option}`programs.mcp.servers` into
|
||||
{option}`programs.github-copilot-cli.mcpServers`.
|
||||
|
||||
Servers defined in {option}`programs.mcp.servers` are merged with
|
||||
{option}`programs.github-copilot-cli.mcpServers`, with the latter
|
||||
taking precedence. Disabled servers (where `disabled = true`) are
|
||||
excluded from the generated configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = lib.types.attrsOf jsonFormat.type;
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
model = "claude-sonnet-4-5";
|
||||
theme = "default";
|
||||
trusted_folders = [ "/home/user/projects" ];
|
||||
renderMarkdown = true;
|
||||
autoUpdate = false;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Configuration written to {file}`config.json` inside
|
||||
{option}`programs.github-copilot-cli.configDir`.
|
||||
|
||||
Known configuration keys include:
|
||||
- `model` — AI model selection
|
||||
- `effortLevel` — reasoning effort for capable models
|
||||
- `theme` — `"default"`, `"dim"`, `"high-contrast"`, or `"colorblind"`
|
||||
- `mouse` — enable mouse support (default: `true`)
|
||||
- `banner` — frequency of animated banner display
|
||||
- `renderMarkdown` — markdown rendering toggle (default: `true`)
|
||||
- `screenReader` — accessibility optimizations (default: `false`)
|
||||
- `autoUpdate` — automatic CLI updates (default: `true`)
|
||||
- `stream` — token-by-token response streaming (default: `true`)
|
||||
- `includeCoAuthoredBy` — agent commit attribution (default: `true`)
|
||||
- `respectGitignore` — exclude gitignored files from file picker
|
||||
- `trusted_folders` — list of pre-approved directory paths
|
||||
- `allowed_urls`, `denied_urls` — URL allowlists/blocklists
|
||||
- `logLevel` — log verbosity
|
||||
- `disableAllHooks` — global hook disable toggle
|
||||
- `hooks` — inline hook definitions
|
||||
- `enabledFeatureFlags` — enable or disable specific feature flags
|
||||
|
||||
See <https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference>
|
||||
for the documentation.
|
||||
'';
|
||||
};
|
||||
|
||||
mcpServers = mkOption {
|
||||
type = lib.types.attrsOf jsonFormat.type;
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
playwright = {
|
||||
type = "local";
|
||||
command = "npx";
|
||||
args = [ "@playwright/mcp@latest" ];
|
||||
tools = [ "*" ];
|
||||
};
|
||||
context7 = {
|
||||
type = "http";
|
||||
url = "https://mcp.context7.com/mcp";
|
||||
headers = { CONTEXT7_API_KEY = "YOUR-API-KEY"; };
|
||||
tools = [ "*" ];
|
||||
};
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
MCP server configurations written to {file}`mcp-config.json` inside
|
||||
{option}`programs.github-copilot-cli.configDir`.
|
||||
|
||||
Each attribute defines a server entry under `mcpServers` in the config
|
||||
file. Supported server types:
|
||||
- `local` — starts a local process via stdio (`command`, optional `args`, `env`)
|
||||
- `http` — connects to a remote HTTP server (`url`, optional `headers`)
|
||||
- `sse` — legacy HTTP with Server-Sent Events (same structure as `http`)
|
||||
|
||||
The `tools` field accepts `["*"]` to enable all tools or a list of
|
||||
specific tool names.
|
||||
|
||||
See <https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers>
|
||||
for the documentation.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
home.packages = mkIf (cfg.package != null) [ cfg.package ];
|
||||
|
||||
home.file = {
|
||||
# NOTE: Copilot will try to add a firstLaunchAt date and crash if the
|
||||
# file exists but does not have this key set. Only generate the file when
|
||||
# the user has explicitly configured settings, and always inject the
|
||||
# default so the managed file stays valid.
|
||||
"${cfg.configDir}/config.json" = mkIf (cfg.settings != { }) {
|
||||
source = jsonFormat.generate "github-copilot-cli-config.json" (
|
||||
{ firstLaunchAt = "1970-01-01T00:00:00.000Z"; } // cfg.settings
|
||||
);
|
||||
};
|
||||
|
||||
"${cfg.configDir}/mcp-config.json" = mkIf (mergedMcpServers != { }) {
|
||||
source = jsonFormat.generate "github-copilot-cli-mcp-config.json" {
|
||||
mcpServers = mergedMcpServers;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
home.sessionVariables = mkIf (cfg.configDir != upstreamConfigDir) {
|
||||
COPILOT_HOME = cfg.configDir;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -68,6 +68,7 @@ let
|
||||
"git-credential-oauth"
|
||||
"git-lfs"
|
||||
"git-worktree-switcher"
|
||||
"github-copilot-cli"
|
||||
"gitMinimal"
|
||||
"gnupg"
|
||||
"go"
|
||||
|
||||
17
tests/modules/programs/github-copilot-cli/config.nix
Normal file
17
tests/modules/programs/github-copilot-cli/config.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
programs.github-copilot-cli = {
|
||||
enable = true;
|
||||
settings = {
|
||||
model = "claude-sonnet-4-5";
|
||||
theme = "dark";
|
||||
trusted_folders = [ "/home/user/projects" ];
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.copilot/config.json
|
||||
assertFileContent home-files/.copilot/config.json ${./expected-config.json}
|
||||
assertPathNotExists home-files/.copilot/mcp-config.json
|
||||
assertFileNotRegex home-path/etc/profile.d/hm-session-vars.sh 'COPILOT_HOME'
|
||||
'';
|
||||
}
|
||||
6
tests/modules/programs/github-copilot-cli/default.nix
Normal file
6
tests/modules/programs/github-copilot-cli/default.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
github-copilot-cli-config = ./config.nix;
|
||||
github-copilot-cli-mcp = ./mcp.nix;
|
||||
github-copilot-cli-mcp-integration = ./mcp-integration.nix;
|
||||
github-copilot-cli-xdg-config-dir = ./xdg-config-dir.nix;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"firstLaunchAt": "1970-01-01T00:00:00.000Z",
|
||||
"model": "claude-sonnet-4-5",
|
||||
"theme": "dark",
|
||||
"trusted_folders": [
|
||||
"/home/user/projects"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"context7": {
|
||||
"tools": [
|
||||
"*"
|
||||
],
|
||||
"type": "http",
|
||||
"url": "https://mcp.context7.com/mcp"
|
||||
},
|
||||
"playwright": {
|
||||
"args": [
|
||||
"@playwright/mcp@latest"
|
||||
],
|
||||
"command": "npx",
|
||||
"tools": [
|
||||
"*"
|
||||
],
|
||||
"type": "local"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"database": {
|
||||
"args": [
|
||||
"-y",
|
||||
"@bytebase/dbhub"
|
||||
],
|
||||
"command": "npx",
|
||||
"env": {
|
||||
"DATABASE_URL": "postgresql://user:pass@localhost:5432/db"
|
||||
},
|
||||
"tools": [
|
||||
"*"
|
||||
],
|
||||
"type": "local"
|
||||
},
|
||||
"filesystem": {
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"/tmp"
|
||||
],
|
||||
"command": "npx",
|
||||
"tools": [
|
||||
"*"
|
||||
],
|
||||
"type": "local"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
programs = {
|
||||
github-copilot-cli = {
|
||||
enable = true;
|
||||
enableMcpIntegration = true;
|
||||
# user-defined server takes precedence over the integrated one
|
||||
mcpServers.filesystem = {
|
||||
type = "local";
|
||||
command = "npx";
|
||||
args = [
|
||||
"-y"
|
||||
"@modelcontextprotocol/server-filesystem"
|
||||
"/tmp"
|
||||
];
|
||||
tools = [ "*" ];
|
||||
};
|
||||
};
|
||||
mcp = {
|
||||
enable = true;
|
||||
servers = {
|
||||
filesystem = {
|
||||
command = "npx";
|
||||
args = [
|
||||
"-y"
|
||||
"@modelcontextprotocol/server-filesystem"
|
||||
"/other-tmp"
|
||||
];
|
||||
};
|
||||
database = {
|
||||
command = "npx";
|
||||
args = [
|
||||
"-y"
|
||||
"@bytebase/dbhub"
|
||||
];
|
||||
env = {
|
||||
DATABASE_URL = "postgresql://user:pass@localhost:5432/db";
|
||||
};
|
||||
};
|
||||
disabled-server = {
|
||||
command = "disabled-cmd";
|
||||
disabled = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.copilot/mcp-config.json
|
||||
assertFileContent home-files/.copilot/mcp-config.json ${./expected-mcp-integration-config.json}
|
||||
'';
|
||||
}
|
||||
24
tests/modules/programs/github-copilot-cli/mcp.nix
Normal file
24
tests/modules/programs/github-copilot-cli/mcp.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
programs.github-copilot-cli = {
|
||||
enable = true;
|
||||
mcpServers = {
|
||||
playwright = {
|
||||
type = "local";
|
||||
command = "npx";
|
||||
args = [ "@playwright/mcp@latest" ];
|
||||
tools = [ "*" ];
|
||||
};
|
||||
context7 = {
|
||||
type = "http";
|
||||
url = "https://mcp.context7.com/mcp";
|
||||
tools = [ "*" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.copilot/mcp-config.json
|
||||
assertFileContent home-files/.copilot/mcp-config.json ${./expected-mcp-config.json}
|
||||
assertPathNotExists home-files/.copilot/config.json
|
||||
'';
|
||||
}
|
||||
19
tests/modules/programs/github-copilot-cli/xdg-config-dir.nix
Normal file
19
tests/modules/programs/github-copilot-cli/xdg-config-dir.nix
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
home.preferXdgDirectories = true;
|
||||
programs.github-copilot-cli = {
|
||||
enable = true;
|
||||
settings = {
|
||||
model = "claude-sonnet-4-5";
|
||||
theme = "dark";
|
||||
trusted_folders = [ "/home/user/projects" ];
|
||||
};
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/copilot/config.json
|
||||
assertFileContent home-files/.config/copilot/config.json ${./expected-config.json}
|
||||
assertPathNotExists home-files/.copilot/config.json
|
||||
assertFileContains home-path/etc/profile.d/hm-session-vars.sh \
|
||||
'export COPILOT_HOME="/home/hm-user/.config/copilot"'
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user