{ config, lib, pkgs, ... }: let cfg = config.claude; settingsFile = (pkgs.formats.json { }).generate "claude-settings.json" cfg.settings; mcpConfigFile = (pkgs.formats.json { }).generate "claude-mcp.json" { mcpServers = cfg.mcpServers; }; hasMcp = cfg.mcpServers != { }; in { options.claude = { enable = lib.mkEnableOption "Claude Code"; package = lib.mkOption { type = lib.types.package; description = "The Claude Code package; must provide bin/claude."; }; settings = lib.mkOption { type = (pkgs.formats.json { }).type; default = { }; description = "Settings layered over the account's settings.json via --settings."; }; plugins = lib.mkOption { type = lib.types.listOf lib.types.path; default = [ ]; description = "Plugin directories, loaded with --plugin-dir. Nothing is installed into the account."; }; mcpServers = lib.mkOption { type = (pkgs.formats.json { }).type; default = { }; description = "MCP servers (the mcpServers object of an MCP JSON file), loaded with --mcp-config. Credentials stay in the account."; }; }; config.build.agents.claude = lib.mkIf cfg.enable { exe = lib.getExe' cfg.package "claude"; stateEnvVar = "CLAUDE_CONFIG_DIR"; instructionsFile = "CLAUDE.md"; # Managed links: path relative to the state dir -> store path. files = { }; # --mcp-config is variadic and would swallow the user's arguments if it came # last, so it goes first and --settings always follows it. flags = lib.optionals hasMcp [ "--mcp-config" "${mcpConfigFile}" ] ++ lib.optionals (cfg.settings != { } || hasMcp) [ "--settings" "${settingsFile}" ] ++ lib.concatMap (p: [ "--plugin-dir" "${p}" ]) cfg.plugins; }; }