diff --git a/.github/labeler.yml b/.github/labeler.yml index bc331859b..7750a323d 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -10,7 +10,7 @@ - modules/programs/claude-code.nix - modules/programs/codex.nix - modules/programs/fabric-ai.nix - - modules/programs/gemini-cli.nix + - modules/programs/antigravity-cli.nix - modules/programs/mcp.nix - modules/programs/mistral-vibe.nix - modules/programs/mods.nix diff --git a/modules/programs/antigravity-cli.nix b/modules/programs/antigravity-cli.nix new file mode 100644 index 000000000..8fd267456 --- /dev/null +++ b/modules/programs/antigravity-cli.nix @@ -0,0 +1,498 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.programs.antigravity-cli; + + jsonFormat = pkgs.formats.json { }; + tomlFormat = pkgs.formats.toml { }; + + isStorePathString = + content: builtins.isString content && lib.hasPrefix "${builtins.storeDir}/" content; + + isPathLikeContent = content: lib.isPath content || isStorePathString content; + + transformMcpServer = + server: + removeAttrs server [ + "httpUrl" + "url" + ] + // lib.optionalAttrs (server ? httpUrl) { + serverUrl = server.httpUrl; + } + // lib.optionalAttrs (server ? url) { + serverUrl = server.url; + }; + + transformMcpServers = lib.mapAttrs (_name: transformMcpServer); + + commandSkillName = lib.replaceStrings [ "/" ] [ ":" ]; + +in +{ + meta.maintainers = [ lib.maintainers.rrvsh ]; + + imports = [ + (lib.mkRenamedOptionModule [ "programs" "gemini-cli" ] [ "programs" "antigravity-cli" ]) + ]; + + options.programs.antigravity-cli = { + enable = lib.mkEnableOption "Antigravity CLI"; + + package = lib.mkPackageOption pkgs "antigravity-cli" { + nullable = true; + }; + + useLegacyGeminiConfig = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to write configuration to the legacy Gemini CLI locations. + + This is enabled automatically when + {option}`programs.antigravity-cli.package` is a `gemini-cli` package. + ''; + }; + + enableMcpIntegration = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Whether to integrate the MCP servers config from + {option}`programs.mcp.servers` into + {option}`programs.antigravity-cli.mcpServers`. + + Note: Any servers already present in + {option}`programs.antigravity-cli.mcpServers` + is not overridden by servers present under the same + name in {option}`programs.mcp.servers` + ''; + }; + + settings = lib.mkOption { + inherit (jsonFormat) type; + default = { }; + example = { + colorScheme = "tokyo night"; + altScreenMode = "always"; + toolPermission = "proceed-in-sandbox"; + artifactReviewPolicy = "agent-decides"; + }; + description = '' + Configuration written to + {file}`~/.gemini/antigravity-cli/settings.json`. + ''; + }; + + mcpServers = lib.mkOption { + inherit (jsonFormat) type; + default = { }; + example = { + github = { + serverUrl = "https://api.githubcopilot.com/mcp/"; + }; + filesystem = { + command = "npx"; + args = [ + "-y" + "@modelcontextprotocol/server-filesystem" + "/tmp" + ]; + }; + }; + description = '' + MCP server configuration written to + {file}`~/.gemini/config/mcp_config.json`. + + Remote servers use `serverUrl`; `url` and `httpUrl` are accepted + for migration and converted automatically. + ''; + }; + + commands = + let + commandType = lib.types.submodule { + freeformType = lib.types.str; + options = { + prompt = lib.mkOption { + type = lib.types.str; + description = '' + The prompt that will be sent to the Gemini model when the command is executed. + This can be a single-line or multi-line string. + The special placeholder {{args}} will be replaced with the text the user typed after the command name. + ''; + }; + description = lib.mkOption { + type = lib.types.str; + description = '' + A brief, one-line description of what the command does. + This text will be displayed next to your command in the /help menu. + If you omit this field, a generic description will be generated from the filename. + ''; + }; + }; + }; + in + lib.mkOption { + type = lib.types.attrsOf commandType; + default = { }; + description = '' + An attribute set of Gemini CLI custom commands to migrate to + Antigravity CLI global skills. + + The name of the attribute set will be the name of each generated + skill directory. + ''; + example = lib.literalExpression '' + changelog = { + prompt = + ''' + Your task is to parse the ``, ``, and `` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file. + '''; + description = "Adds a new entry to the project's CHANGELOG.md file."; + }; + "git/fix" = { # Becomes /git:fix + prompt = "Please analyze the staged git changes and provide a code fix for the issue described here: {{args}}."; + description = "Generates a fix for a given GitHub issue."; + }; + ''; + }; + + permissions = lib.mkOption { + type = lib.types.nullOr ( + lib.types.submodule { + options = { + allow = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Permissions to allow without prompting. + ''; + }; + + deny = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Permissions to deny without prompting. + ''; + }; + + ask = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = '' + Permissions to ask before using. + ''; + }; + }; + } + ); + default = null; + description = '' + Antigravity CLI fine-grained permissions written to + {option}`programs.antigravity-cli.settings.permissions`. + ''; + example = lib.literalExpression '' + { + allow = [ "command(git)" ]; + deny = [ "command(rm -rf)" ]; + ask = [ "command(*)" ]; + } + ''; + }; + + policies = lib.mkOption { + type = lib.types.attrsOf (lib.types.either lib.types.path tomlFormat.type); + default = { }; + description = '' + An attribute set of Gemini CLI policy definitions to create in + {file}`~/.gemini/policies/` when + {option}`programs.antigravity-cli.package` is a `gemini-cli` + package. + + Antigravity CLI configures permissions in + {option}`programs.antigravity-cli.settings.permissions` or + {option}`programs.antigravity-cli.permissions`. + ''; + example = lib.literalExpression '' + { + "my-rules" = { + rule = [ + { + toolName = "run_shell_command"; + commandPrefix = "git "; + decision = "ask_user"; + priority = 100; + } + ]; + }; + "other-rules" = ./path/to/rules.toml; + } + ''; + }; + + defaultModel = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "gemini-2.5-flash"; + description = '' + The default model to use for the CLI. + Will be set as $GEMINI_MODEL when configured. + ''; + }; + + context = lib.mkOption { + type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path); + default = { }; + example = lib.literalExpression '' + { + GEMINI = ''' + # Global Context + + You are a helpful AI assistant for software development. + + ## Coding Standards + + - Follow consistent code style + - Write clear comments + - Test your changes + '''; + + AGENTS = ./path/to/agents.md; + + CONTEXT = ''' + Additional context instructions here. + '''; + } + ''; + description = '' + Global context(s) for Antigravity CLI. + + The attribute name becomes the filename, with a {file}`.md` + extension added automatically. Each value is either: + - Inline content as a string + - A path to a file containing the content + + The configured files are written to {file}`~/.gemini/`. + + Note: You can customize which context file names Antigravity CLI looks for by setting + `settings.context.fileName`. For example: + ```nix + settings = { + context.fileName = ["AGENTS.md", "CONTEXT.md", "GEMINI.md"]; + }; + ``` + ''; + }; + + skills = lib.mkOption { + type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path; + default = { }; + example = lib.literalExpression '' + { + xlsx = ./skills/xlsx/SKILL.md; + data-analysis = ./skills/data-analysis; + pdf-processing = ''' + --- + name: pdf-processing + description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction. + --- + + # PDF Processing + + ## Quick start + + Use pdfplumber to extract text from PDFs: + + ```python + import pdfplumber + + with pdfplumber.open("document.pdf") as pdf: + text = pdf.pages[0].extract_text() + ``` + '''; + } + ''; + description = '' + Custom skills for Antigravity CLI. + + This option can be either: + - An attribute set defining skills + - A path to a directory containing skill folders + + If an attribute set is used, the attribute name becomes the + skill directory name, and the value is either: + - Inline content as a string (creates `~/.gemini/config/skills//SKILL.md`) + - A path to a file (creates `~/.gemini/config/skills//SKILL.md`) + - A path to a directory (symlinks `~/.gemini/config/skills//` to that directory) + + If a path is used, it is expected to contain one folder per + skill name, each containing a {file}`SKILL.md`. The directory is + symlinked to {file}`~/.gemini/config/skills/`. + ''; + }; + }; + + config = + let + useGeminiConfig = + cfg.useLegacyGeminiConfig || (cfg.package != null && lib.getName cfg.package == "gemini-cli"); + antigravitySkillsDir = ".gemini/config/skills"; + geminiSkillsDir = ".gemini/skills"; + antigravitySettings = lib.recursiveUpdate (lib.optionalAttrs (cfg.permissions != null) { + inherit (cfg) permissions; + }) (removeAttrs cfg.settings [ "mcpServers" ]); + legacyMcpServers = lib.optionalAttrs (cfg.settings ? mcpServers) cfg.settings.mcpServers; + mcpServers = lib.recursiveUpdate legacyMcpServers cfg.mcpServers; + geminiSettings = + lib.recursiveUpdate + (lib.optionalAttrs (cfg.permissions != null) { + inherit (cfg) permissions; + }) + (lib.recursiveUpdate cfg.settings (lib.optionalAttrs (mcpServers != { }) { inherit mcpServers; })); + in + lib.mkIf cfg.enable ( + lib.mkMerge [ + { + home = { + packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + sessionVariables = lib.mkIf (cfg.defaultModel != null) { + GEMINI_MODEL = cfg.defaultModel; + }; + }; + } + { + home.file = lib.mapAttrs' ( + n: v: lib.nameValuePair ".gemini/${n}.md" (if lib.isPath v then { source = v; } else { text = v; }) + ) cfg.context; + } + (lib.mkIf useGeminiConfig { + programs.antigravity-cli.settings.mcpServers = lib.mkIf ( + cfg.enableMcpIntegration && config.programs.mcp.enable + ) (lib.mapAttrs (_n: lib.mkDefault) config.programs.mcp.servers); + + home.file = + lib.optionalAttrs (geminiSettings != { }) { + ".gemini/settings.json".source = jsonFormat.generate "gemini-cli-settings.json" geminiSettings; + } + // lib.mapAttrs' ( + n: v: + lib.nameValuePair ".gemini/commands/${n}.toml" { + source = tomlFormat.generate "gemini-cli-command-${n}.toml" { + inherit (v) description prompt; + }; + } + ) cfg.commands + // lib.mapAttrs' ( + n: v: + lib.nameValuePair ".gemini/policies/${n}.toml" { + source = + if isPathLikeContent v then v else tomlFormat.generate "gemini-cli-policy-${n}.toml" v; + } + ) cfg.policies + // ( + if isPathLikeContent cfg.skills then + { + "${geminiSkillsDir}" = { + source = cfg.skills; + recursive = true; + }; + } + else + lib.mapAttrs' ( + n: v: + if isPathLikeContent v && lib.pathIsDirectory v then + lib.nameValuePair "${geminiSkillsDir}/${n}" { + source = v; + recursive = true; + } + else + lib.nameValuePair "${geminiSkillsDir}/${n}/SKILL.md" ( + if isPathLikeContent v then { source = v; } else { text = v; } + ) + ) cfg.skills + ); + }) + (lib.mkIf (!useGeminiConfig) { + programs.antigravity-cli.mcpServers = + lib.mkIf (cfg.enableMcpIntegration && config.programs.mcp.enable) + (lib.mapAttrs (_n: server: lib.mkDefault (transformMcpServer server)) config.programs.mcp.servers); + + assertions = [ + { + assertion = cfg.policies == { }; + message = '' + `programs.antigravity-cli.policies` is only supported when + `programs.antigravity-cli.package` is a `gemini-cli` package. + Antigravity CLI configures permissions in + `programs.antigravity-cli.settings.permissions` or + `programs.antigravity-cli.permissions`. + ''; + } + ]; + + home.file = + lib.optionalAttrs (antigravitySettings != { }) { + ".gemini/antigravity-cli/settings.json".source = + jsonFormat.generate "antigravity-cli-settings.json" antigravitySettings; + } + // lib.optionalAttrs (mcpServers != { }) { + ".gemini/config/mcp_config.json".source = jsonFormat.generate "antigravity-cli-mcp-config.json" { + mcpServers = transformMcpServers mcpServers; + }; + } + // lib.mapAttrs' ( + n: v: + let + skillName = commandSkillName n; + in + lib.nameValuePair "${antigravitySkillsDir}/${skillName}/SKILL.md" { + text = '' + --- + name: ${skillName} + description: ${v.description} + --- + + ${v.prompt} + ''; + } + ) cfg.commands + // ( + if isPathLikeContent cfg.skills then + { + "${antigravitySkillsDir}" = { + source = cfg.skills; + recursive = true; + }; + } + else + lib.mapAttrs' ( + n: v: + if isPathLikeContent v && lib.pathIsDirectory v then + lib.nameValuePair "${antigravitySkillsDir}/${n}" { + source = v; + recursive = true; + } + else + lib.nameValuePair "${antigravitySkillsDir}/${n}/SKILL.md" ( + if isPathLikeContent v then { source = v; } else { text = v; } + ) + ) cfg.skills + ); + }) + { + assertions = [ + { + assertion = !isPathLikeContent cfg.skills || lib.pathIsDirectory cfg.skills; + message = "`programs.antigravity-cli.skills` must be a directory when set to a path"; + } + ]; + } + ] + ); +} diff --git a/modules/programs/gemini-cli.nix b/modules/programs/gemini-cli.nix deleted file mode 100644 index bd0cfd76d..000000000 --- a/modules/programs/gemini-cli.nix +++ /dev/null @@ -1,314 +0,0 @@ -{ - config, - lib, - pkgs, - ... -}: -let - cfg = config.programs.gemini-cli; - - jsonFormat = pkgs.formats.json { }; - tomlFormat = pkgs.formats.toml { }; -in -{ - meta.maintainers = [ lib.maintainers.rrvsh ]; - - options.programs.gemini-cli = { - enable = lib.mkEnableOption "gemini-cli"; - - package = lib.mkPackageOption pkgs "gemini-cli" { nullable = true; }; - - enableMcpIntegration = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Whether to integrate the MCP servers config from - {option}`programs.mcp.servers` into - {option}`programs.gemini-cli.settings.mcpServers`. - - Note: Any servers already present in - {option}`programs.gemini-cli.settings.mcpServers` - is not overridden by servers present under the same - name in {option}`programs.mcp.servers` - ''; - }; - - settings = lib.mkOption { - inherit (jsonFormat) type; - default = { }; - example = { - ui.theme = "Default"; - general = { - vimMode = true; - preferredEditor = "nvim"; - previewFeatures = true; - }; - ide.enabled = true; - privacy.usageStatisticsEnabled = false; - tools.autoAccept = false; - context.loadMemoryFromIncludeDirectories = true; - security.auth.selectedType = "oauth-personal"; - }; - description = "JSON config for gemini-cli"; - }; - - commands = - let - commandType = lib.types.submodule { - freeformType = lib.types.str; - options = { - prompt = lib.mkOption { - type = lib.types.str; - description = '' - The prompt that will be sent to the Gemini model when the command is executed. - This can be a single-line or multi-line string. - The special placeholder {{args}} will be replaced with the text the user typed after the command name. - ''; - }; - description = lib.mkOption { - type = lib.types.str; - description = '' - A brief, one-line description of what the command does. - This text will be displayed next to your command in the /help menu. - If you omit this field, a generic description will be generated from the filename. - ''; - }; - }; - }; - in - lib.mkOption { - type = lib.types.attrsOf commandType; - default = { }; - description = '' - An attribute set of custom commands that will be globally available. - The name of the attribute set will be the name of each command. - You may use subdirectories to create namespaced commands, such as `git/fix` becoming `/git:fix`. - See https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/commands.md#custom-commands for more information. - ''; - example = lib.literalExpression '' - changelog = { - prompt = - ''' - Your task is to parse the ``, ``, and `` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file. - '''; - description = "Adds a new entry to the project's CHANGELOG.md file."; - }; - "git/fix" = { # Becomes /git:fix - prompt = "Please analyze the staged git changes and provide a code fix for the issue described here: {{args}}."; - description = "Generates a fix for a given GitHub issue."; - }; - ''; - }; - - policies = lib.mkOption { - type = lib.types.attrsOf (lib.types.either lib.types.path tomlFormat.type); - default = { }; - description = '' - An attribute set of policy definitions to create in `~/.gemini/policies/`. - The attribute name becomes the filename with `.toml` extension automatically added. - The value can be either an attribute set representing the TOML policy or a path to a TOML file. - ''; - example = lib.literalExpression '' - { - "my-rules" = { - rule = [ - { - toolName = "run_shell_command"; - commandPrefix = "git "; - decision = "ask_user"; - priority = 100; - } - ]; - }; - "other-rules" = ./path/to/rules.toml; - } - ''; - }; - - defaultModel = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "gemini-2.5-flash"; - description = '' - The default model to use for the CLI. - Will be set as $GEMINI_MODEL when configured. - ''; - }; - - context = lib.mkOption { - type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path); - default = { }; - example = lib.literalExpression '' - { - GEMINI = ''' - # Global Context - - You are a helpful AI assistant for software development. - - ## Coding Standards - - - Follow consistent code style - - Write clear comments - - Test your changes - '''; - - AGENTS = ./path/to/agents.md; - - CONTEXT = ''' - Additional context instructions here. - '''; - } - ''; - description = '' - Global context(s) for gemini-cli. - - The attribute name becomes the filename, with a {file}`.md` - extension added automatically. Each value is either: - - Inline content as a string - - A path to a file containing the content - - The configured files are written to {file}`~/.gemini/`. - - Note: You can customize which context file names gemini-cli looks for by setting - `settings.context.fileName`. For example: - ```nix - settings = { - context.fileName = ["AGENTS.md", "CONTEXT.md", "GEMINI.md"]; - }; - ``` - ''; - }; - - skills = lib.mkOption { - type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path; - default = { }; - example = lib.literalExpression '' - { - xlsx = ./skills/xlsx/SKILL.md; - data-analysis = ./skills/data-analysis; - pdf-processing = ''' - --- - name: pdf-processing - description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction. - --- - - # PDF Processing - - ## Quick start - - Use pdfplumber to extract text from PDFs: - - ```python - import pdfplumber - - with pdfplumber.open("document.pdf") as pdf: - text = pdf.pages[0].extract_text() - ``` - '''; - } - ''; - description = '' - Custom skills for gemini-cli. - - This option can be either: - - An attribute set defining skills - - A path to a directory containing skill folders - - If an attribute set is used, the attribute name becomes the - skill directory name, and the value is either: - - Inline content as a string (creates `~/.gemini/skills//SKILL.md`) - - A path to a file (creates `~/.gemini/skills//SKILL.md`) - - A path to a directory (symlinks `~/.gemini/skills//` to that directory) - - If a path is used, it is expected to contain one folder per - skill name, each containing a {file}`SKILL.md`. The directory is - symlinked to {file}`~/.gemini/skills/`. - ''; - }; - }; - - config = - let - isStorePathString = - content: builtins.isString content && lib.hasPrefix "${builtins.storeDir}/" content; - isPathLikeContent = content: lib.isPath content || isStorePathString content; - in - lib.mkIf cfg.enable ( - lib.mkMerge [ - { - programs.gemini-cli.settings.mcpServers = lib.mkIf ( - cfg.enableMcpIntegration && config.programs.mcp.enable - ) (lib.mapAttrs (_n: lib.mkDefault) config.programs.mcp.servers); - } - { - home = { - packages = lib.mkIf (cfg.package != null) [ cfg.package ]; - file.".gemini/settings.json" = lib.mkIf (cfg.settings != { }) { - source = jsonFormat.generate "gemini-cli-settings.json" cfg.settings; - }; - sessionVariables = lib.mkIf (cfg.defaultModel != null) { - GEMINI_MODEL = cfg.defaultModel; - }; - }; - } - { - home.file = lib.mapAttrs' ( - n: v: lib.nameValuePair ".gemini/${n}.md" (if lib.isPath v then { source = v; } else { text = v; }) - ) cfg.context; - } - { - home.file = lib.mapAttrs' ( - n: v: - lib.nameValuePair ".gemini/commands/${n}.toml" { - source = tomlFormat.generate "gemini-cli-command-${n}.toml" { - inherit (v) description prompt; - }; - } - ) cfg.commands; - } - { - home.file = lib.mapAttrs' ( - n: v: - lib.nameValuePair ".gemini/policies/${n}.toml" { - source = - if builtins.isPath v || builtins.isString v || lib.isDerivation v then - v - else - tomlFormat.generate "gemini-cli-policy-${n}.toml" v; - } - ) cfg.policies; - } - { - assertions = [ - { - assertion = !isPathLikeContent cfg.skills || lib.pathIsDirectory cfg.skills; - message = "`programs.gemini-cli.skills` must be a directory when set to a path"; - } - ]; - } - { - home.file = - if isPathLikeContent cfg.skills then - { - ".gemini/skills" = { - source = cfg.skills; - recursive = true; - }; - } - else - lib.mapAttrs' ( - n: v: - if isPathLikeContent v && lib.pathIsDirectory v then - lib.nameValuePair ".gemini/skills/${n}" { - source = v; - recursive = true; - } - else - lib.nameValuePair ".gemini/skills/${n}/SKILL.md" ( - if isPathLikeContent v then { source = v; } else { text = v; } - ) - ) cfg.skills; - } - ] - ); -} diff --git a/tests/modules/programs/gemini-cli/context-additional.md b/tests/modules/programs/antigravity-cli-legacy/context-additional.md similarity index 100% rename from tests/modules/programs/gemini-cli/context-additional.md rename to tests/modules/programs/antigravity-cli-legacy/context-additional.md diff --git a/tests/modules/programs/gemini-cli/context-inline.md b/tests/modules/programs/antigravity-cli-legacy/context-inline.md similarity index 100% rename from tests/modules/programs/gemini-cli/context-inline.md rename to tests/modules/programs/antigravity-cli-legacy/context-inline.md diff --git a/tests/modules/programs/gemini-cli/context.md b/tests/modules/programs/antigravity-cli-legacy/context.md similarity index 100% rename from tests/modules/programs/gemini-cli/context.md rename to tests/modules/programs/antigravity-cli-legacy/context.md diff --git a/tests/modules/programs/antigravity-cli-legacy/context.nix b/tests/modules/programs/antigravity-cli-legacy/context.nix new file mode 100644 index 000000000..c664c4050 --- /dev/null +++ b/tests/modules/programs/antigravity-cli-legacy/context.nix @@ -0,0 +1,61 @@ +{ + pkgs, + lib, + options, + ... +}: + +{ + programs.gemini-cli = { + enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; + context = { + # Test inline content + GEMINI = '' + # Global Context + + You are a helpful AI assistant for software development. + + ## Coding Standards + + - Follow consistent code style + - Write clear comments + - Test your changes + ''; + # Test file path + AGENTS = ./context.md; + # Test another inline content + CONTEXT = '' + Additional context for specialized tasks. + ''; + }; + settings = { + context.fileName = [ + "AGENTS.md" + "CONTEXT.md" + "GEMINI.md" + ]; + }; + }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + + nmt.script = '' + assertFileExists home-files/.gemini/GEMINI.md + assertFileContent home-files/.gemini/GEMINI.md \ + ${./context-inline.md} + + assertFileExists home-files/.gemini/AGENTS.md + assertFileContent home-files/.gemini/AGENTS.md \ + ${./context.md} + + assertFileExists home-files/.gemini/CONTEXT.md + assertFileContent home-files/.gemini/CONTEXT.md \ + ${./context-additional.md} + + assertFileExists home-path/etc/profile.d/hm-session-vars.sh + assertFileNotRegex home-path/etc/profile.d/hm-session-vars.sh \ + "GEMINI_MODEL" + ''; +} diff --git a/tests/modules/programs/gemini-cli/default.nix b/tests/modules/programs/antigravity-cli-legacy/default.nix similarity index 89% rename from tests/modules/programs/gemini-cli/default.nix rename to tests/modules/programs/antigravity-cli-legacy/default.nix index 11d00e67c..3b9e57c1d 100644 --- a/tests/modules/programs/gemini-cli/default.nix +++ b/tests/modules/programs/antigravity-cli-legacy/default.nix @@ -6,4 +6,5 @@ gemini-cli-skills-store-path-dir = ./skills-store-path-dir.nix; gemini-cli-skills-path-not-directory = ./skills-path-not-directory.nix; gemini-cli-mcp-servers = ./mcp.nix; + gemini-cli-policies = ./policies.nix; } diff --git a/tests/modules/programs/gemini-cli/mcp.nix b/tests/modules/programs/antigravity-cli-legacy/mcp.nix similarity index 73% rename from tests/modules/programs/gemini-cli/mcp.nix rename to tests/modules/programs/antigravity-cli-legacy/mcp.nix index fc80d5755..ec625f8f6 100644 --- a/tests/modules/programs/gemini-cli/mcp.nix +++ b/tests/modules/programs/antigravity-cli-legacy/mcp.nix @@ -1,7 +1,15 @@ +{ + pkgs, + lib, + options, + ... +}: + { programs = { gemini-cli = { enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; enableMcpIntegration = true; settings = { theme = "Default"; @@ -9,6 +17,9 @@ mcpServers = { github = { url = "https://api.githubcopilot.com/mcp/"; + env = { + url = "https://token.example/env"; + }; }; filesystem = { command = "npx"; @@ -47,9 +58,15 @@ }; }; }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + nmt.script = '' assertFileExists home-files/.gemini/settings.json assertFileRegex home-files/.gemini/settings.json '"github"' + assertFileRegex home-files/.gemini/settings.json '"url"' + assertFileRegex home-files/.gemini/settings.json 'https://token.example/env' assertFileRegex home-files/.gemini/settings.json '"filesystem"' assertFileRegex home-files/.gemini/settings.json '"database"' assertFileNotRegex home-files/.gemini/settings.json '"other-tmp"' diff --git a/tests/modules/programs/gemini-cli/my-rules.toml b/tests/modules/programs/antigravity-cli-legacy/other-rules.toml similarity index 77% rename from tests/modules/programs/gemini-cli/my-rules.toml rename to tests/modules/programs/antigravity-cli-legacy/other-rules.toml index 0d8b2ad9e..1c4c04916 100644 --- a/tests/modules/programs/gemini-cli/my-rules.toml +++ b/tests/modules/programs/antigravity-cli-legacy/other-rules.toml @@ -1,5 +1,5 @@ [[rule]] -commandPrefix = "git " +toolName = "run_shell_command" +commandPrefix = "nix " decision = "ask_user" priority = 100 -toolName = "run_shell_command" diff --git a/tests/modules/programs/antigravity-cli-legacy/policies.nix b/tests/modules/programs/antigravity-cli-legacy/policies.nix new file mode 100644 index 000000000..1856ac332 --- /dev/null +++ b/tests/modules/programs/antigravity-cli-legacy/policies.nix @@ -0,0 +1,44 @@ +{ + pkgs, + lib, + options, + ... +}: + +{ + programs.gemini-cli = { + enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; + policies = { + "my-rules" = { + rule = [ + { + toolName = "run_shell_command"; + commandPrefix = "git "; + decision = "ask_user"; + priority = 100; + } + ]; + }; + "other-rules" = ./other-rules.toml; + }; + }; + + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + + nmt.script = '' + assertFileExists home-files/.gemini/policies/my-rules.toml + assertFileRegex home-files/.gemini/policies/my-rules.toml \ + 'toolName = "run_shell_command"' + assertFileRegex home-files/.gemini/policies/my-rules.toml \ + 'commandPrefix = "git "' + + assertFileExists home-files/.gemini/policies/other-rules.toml + assertFileRegex home-files/.gemini/policies/other-rules.toml \ + 'toolName = "run_shell_command"' + assertFileRegex home-files/.gemini/policies/other-rules.toml \ + 'commandPrefix = "nix "' + ''; +} diff --git a/tests/modules/programs/antigravity-cli-legacy/settings.json b/tests/modules/programs/antigravity-cli-legacy/settings.json new file mode 100644 index 000000000..57606523d --- /dev/null +++ b/tests/modules/programs/antigravity-cli-legacy/settings.json @@ -0,0 +1,17 @@ +{ + "autoAccept": true, + "permissions": { + "allow": [ + "command(git)" + ], + "ask": [ + "command(*)" + ], + "deny": [ + "command(rm -rf)" + ] + }, + "preferredEditor": "nvim", + "theme": "Default", + "vimMode": true +} diff --git a/tests/modules/programs/gemini-cli/settings.nix b/tests/modules/programs/antigravity-cli-legacy/settings.nix similarity index 54% rename from tests/modules/programs/gemini-cli/settings.nix rename to tests/modules/programs/antigravity-cli-legacy/settings.nix index 92ced6b93..96b84e864 100644 --- a/tests/modules/programs/gemini-cli/settings.nix +++ b/tests/modules/programs/antigravity-cli-legacy/settings.nix @@ -1,6 +1,14 @@ +{ + pkgs, + lib, + options, + ... +}: + { programs.gemini-cli = { enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; defaultModel = "gemini-2.5-flash"; settings = { theme = "Default"; @@ -20,32 +28,30 @@ description = "Generates a fix for a given GitHub issue."; }; }; - policies = { - "my-rules" = { - rule = [ - { - toolName = "run_shell_command"; - commandPrefix = "git "; - decision = "ask_user"; - priority = 100; - } - ]; - }; - "other-rules" = ./other-rules.toml; + permissions = { + allow = [ "command(git)" ]; + deny = [ "command(rm -rf)" ]; + ask = [ "command(*)" ]; }; }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + nmt.script = '' assertFileExists home-files/.gemini/settings.json assertFileContent home-files/.gemini/settings.json \ ${./settings.json} - assertFileContent home-files/.gemini/commands/changelog.toml \ - ${./changelog.toml} - assertFileContent home-files/.gemini/commands/git/fix.toml \ - ${./fix.toml} - assertFileContent home-files/.gemini/policies/my-rules.toml \ - ${./my-rules.toml} - assertFileContent home-files/.gemini/policies/other-rules.toml \ - ${./other-rules.toml} + assertFileExists home-files/.gemini/commands/changelog.toml + assertFileRegex home-files/.gemini/commands/changelog.toml \ + 'prompt =' + assertFileRegex home-files/.gemini/commands/changelog.toml \ + "Adds a new entry to the project's CHANGELOG.md file." + assertFileExists home-files/.gemini/commands/git/fix.toml + assertFileRegex home-files/.gemini/commands/git/fix.toml \ + 'prompt =' + assertFileRegex home-files/.gemini/commands/git/fix.toml \ + 'Generates a fix for a given GitHub issue.' assertFileExists home-path/etc/profile.d/hm-session-vars.sh assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ diff --git a/tests/modules/programs/gemini-cli/skills-dir.nix b/tests/modules/programs/antigravity-cli-legacy/skills-dir.nix similarity index 74% rename from tests/modules/programs/gemini-cli/skills-dir.nix rename to tests/modules/programs/antigravity-cli-legacy/skills-dir.nix index 4a43d9651..c418b0540 100644 --- a/tests/modules/programs/gemini-cli/skills-dir.nix +++ b/tests/modules/programs/antigravity-cli-legacy/skills-dir.nix @@ -1,9 +1,21 @@ +{ + pkgs, + lib, + options, + ... +}: + { programs.gemini-cli = { enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; skills = ./skills; }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + nmt.script = '' assertFileExists home-files/.gemini/skills/xlsx/SKILL.md assertLinkExists home-files/.gemini/skills/xlsx/SKILL.md diff --git a/tests/modules/programs/antigravity-cli-legacy/skills-path-not-directory.nix b/tests/modules/programs/antigravity-cli-legacy/skills-path-not-directory.nix new file mode 100644 index 000000000..fdfefa2ad --- /dev/null +++ b/tests/modules/programs/antigravity-cli-legacy/skills-path-not-directory.nix @@ -0,0 +1,22 @@ +{ + pkgs, + lib, + options, + ... +}: + +{ + programs.gemini-cli = { + enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; + skills = ./skills/xlsx/SKILL.md; + }; + + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + + test.asserts.assertions.expected = [ + "`programs.antigravity-cli.skills` must be a directory when set to a path" + ]; +} diff --git a/tests/modules/programs/gemini-cli/skills-store-path-dir.nix b/tests/modules/programs/antigravity-cli-legacy/skills-store-path-dir.nix similarity index 62% rename from tests/modules/programs/gemini-cli/skills-store-path-dir.nix rename to tests/modules/programs/antigravity-cli-legacy/skills-store-path-dir.nix index 2155d0d68..790482d25 100644 --- a/tests/modules/programs/gemini-cli/skills-store-path-dir.nix +++ b/tests/modules/programs/antigravity-cli-legacy/skills-store-path-dir.nix @@ -1,4 +1,9 @@ -{ pkgs, ... }: +{ + pkgs, + lib, + options, + ... +}: let src = pkgs.writeTextDir "skills/external-skill/SKILL.md" '' @@ -8,9 +13,14 @@ in { programs.gemini-cli = { enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; skills = "${src}/skills"; }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + nmt.script = '' assertFileExists home-files/.gemini/skills/external-skill/SKILL.md assertLinkExists home-files/.gemini/skills/external-skill/SKILL.md diff --git a/tests/modules/programs/gemini-cli/skills.nix b/tests/modules/programs/antigravity-cli-legacy/skills.nix similarity index 81% rename from tests/modules/programs/gemini-cli/skills.nix rename to tests/modules/programs/antigravity-cli-legacy/skills.nix index 14b76b72e..2b17878c0 100644 --- a/tests/modules/programs/gemini-cli/skills.nix +++ b/tests/modules/programs/antigravity-cli-legacy/skills.nix @@ -1,6 +1,14 @@ +{ + pkgs, + lib, + options, + ... +}: + { programs.gemini-cli = { enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; skills = { xlsx = ./skills/xlsx/SKILL.md; data-analysis = ./skills/data-analysis; @@ -25,6 +33,10 @@ ''; }; }; + test.asserts.warnings.expected = [ + "The option `programs.gemini-cli' defined in ${lib.showFiles options.programs.gemini-cli.files} has been renamed to `programs.antigravity-cli'." + ]; + nmt.script = '' assertFileExists home-files/.gemini/skills/xlsx/SKILL.md assertFileContent home-files/.gemini/skills/xlsx/SKILL.md \ diff --git a/tests/modules/programs/gemini-cli/skills/data-analysis/SKILL.md b/tests/modules/programs/antigravity-cli-legacy/skills/data-analysis/SKILL.md similarity index 100% rename from tests/modules/programs/gemini-cli/skills/data-analysis/SKILL.md rename to tests/modules/programs/antigravity-cli-legacy/skills/data-analysis/SKILL.md diff --git a/tests/modules/programs/gemini-cli/skills/pdf-processing/SKILL.md b/tests/modules/programs/antigravity-cli-legacy/skills/pdf-processing/SKILL.md similarity index 100% rename from tests/modules/programs/gemini-cli/skills/pdf-processing/SKILL.md rename to tests/modules/programs/antigravity-cli-legacy/skills/pdf-processing/SKILL.md diff --git a/tests/modules/programs/gemini-cli/skills/xlsx/SKILL.md b/tests/modules/programs/antigravity-cli-legacy/skills/xlsx/SKILL.md similarity index 100% rename from tests/modules/programs/gemini-cli/skills/xlsx/SKILL.md rename to tests/modules/programs/antigravity-cli-legacy/skills/xlsx/SKILL.md diff --git a/tests/modules/programs/antigravity-cli/context-additional.md b/tests/modules/programs/antigravity-cli/context-additional.md new file mode 100644 index 000000000..7e9fe2c85 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/context-additional.md @@ -0,0 +1 @@ +Additional context for specialized tasks. diff --git a/tests/modules/programs/antigravity-cli/context-inline.md b/tests/modules/programs/antigravity-cli/context-inline.md new file mode 100644 index 000000000..e5f3e288d --- /dev/null +++ b/tests/modules/programs/antigravity-cli/context-inline.md @@ -0,0 +1,9 @@ +# Global Context + +You are a helpful AI assistant for software development. + +## Coding Standards + +- Follow consistent code style +- Write clear comments +- Test your changes diff --git a/tests/modules/programs/antigravity-cli/context.md b/tests/modules/programs/antigravity-cli/context.md new file mode 100644 index 000000000..e5f3e288d --- /dev/null +++ b/tests/modules/programs/antigravity-cli/context.md @@ -0,0 +1,9 @@ +# Global Context + +You are a helpful AI assistant for software development. + +## Coding Standards + +- Follow consistent code style +- Write clear comments +- Test your changes diff --git a/tests/modules/programs/gemini-cli/context.nix b/tests/modules/programs/antigravity-cli/context.nix similarity index 91% rename from tests/modules/programs/gemini-cli/context.nix rename to tests/modules/programs/antigravity-cli/context.nix index 6fbac0eac..b7173c878 100644 --- a/tests/modules/programs/gemini-cli/context.nix +++ b/tests/modules/programs/antigravity-cli/context.nix @@ -1,6 +1,9 @@ +{ pkgs, ... }: + { - programs.gemini-cli = { + programs.antigravity-cli = { enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; context = { # Test inline content GEMINI = '' diff --git a/tests/modules/programs/antigravity-cli/default.nix b/tests/modules/programs/antigravity-cli/default.nix new file mode 100644 index 000000000..393f37141 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/default.nix @@ -0,0 +1,12 @@ +{ + antigravity-cli-settings = ./settings.nix; + antigravity-cli-context = ./context.nix; + antigravity-cli-skills = ./skills.nix; + antigravity-cli-skills-dir = ./skills-dir.nix; + antigravity-cli-skills-store-path-dir = ./skills-store-path-dir.nix; + antigravity-cli-skills-path-not-directory = ./skills-path-not-directory.nix; + antigravity-cli-mcp-servers = ./mcp.nix; + antigravity-cli-policies-removed = ./policies-removed.nix; + antigravity-cli-gemini-package-paths = ./gemini-package-paths.nix; + antigravity-cli-legacy-gemini-config = ./legacy-gemini-config.nix; +} diff --git a/tests/modules/programs/antigravity-cli/gemini-package-paths.nix b/tests/modules/programs/antigravity-cli/gemini-package-paths.nix new file mode 100644 index 000000000..2371bd4b2 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/gemini-package-paths.nix @@ -0,0 +1,39 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "gemini-cli" ""; + settings.theme = "Default"; + commands.review = { + prompt = "Review current changes."; + description = "Reviews current changes."; + }; + skills.audit = '' + --- + name: audit + description: Audit code changes. + --- + + Audit code changes. + ''; + policies.commands.rule = [ + { + toolName = "run_shell_command"; + commandPrefix = "git "; + decision = "ask_user"; + priority = 100; + } + ]; + }; + + nmt.script = '' + assertFileExists home-files/.gemini/settings.json + assertPathNotExists home-files/.gemini/antigravity-cli/settings.json + assertFileExists home-files/.gemini/commands/review.toml + assertFileExists home-files/.gemini/skills/audit/SKILL.md + assertFileExists home-files/.gemini/policies/commands.toml + assertPathNotExists home-files/.gemini/config/skills/review/SKILL.md + assertPathNotExists home-files/.gemini/config/mcp_config.json + ''; +} diff --git a/tests/modules/programs/antigravity-cli/legacy-gemini-config.nix b/tests/modules/programs/antigravity-cli/legacy-gemini-config.nix new file mode 100644 index 000000000..da4eb0791 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/legacy-gemini-config.nix @@ -0,0 +1,24 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + useLegacyGeminiConfig = true; + settings.theme = "Default"; + mcpServers.github.serverUrl = "https://api.githubcopilot.com/mcp/"; + commands.review = { + prompt = "Review current changes."; + description = "Reviews current changes."; + }; + }; + + nmt.script = '' + assertFileExists home-files/.gemini/settings.json + assertFileExists home-files/.gemini/commands/review.toml + assertFileRegex home-files/.gemini/settings.json '"github"' + assertPathNotExists home-files/.gemini/antigravity-cli/settings.json + assertPathNotExists home-files/.gemini/config/mcp_config.json + assertPathNotExists home-files/.gemini/config/skills/review/SKILL.md + ''; +} diff --git a/tests/modules/programs/antigravity-cli/mcp.nix b/tests/modules/programs/antigravity-cli/mcp.nix new file mode 100644 index 000000000..3a49f1ffd --- /dev/null +++ b/tests/modules/programs/antigravity-cli/mcp.nix @@ -0,0 +1,66 @@ +{ pkgs, ... }: + +{ + programs = { + antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + enableMcpIntegration = true; + settings = { + colorScheme = "terminal"; + toolPermission = "request-review"; + }; + mcpServers = { + github = { + url = "https://api.githubcopilot.com/mcp/"; + env = { + url = "https://token.example/env"; + }; + }; + filesystem = { + command = "npx"; + args = [ + "-y" + "@modelcontextprotocol/server-filesystem" + "/tmp" + ]; + }; + }; + }; + mcp = { + enable = true; + servers = { + filesystem = { + command = "npx"; + args = [ + "-y" + "@modelcontextprotocol/server-filesystem" + "/other-tmp" + ]; + }; + database = { + command = "npx"; + args = [ + "-y" + "@bytebase/dbhub" + "--dsn" + "postgresql://user:pass@localhost:5432/db" + ]; + env = { + DATABASE_URL = "postgresql://user:pass@localhost:5432/db"; + }; + }; + }; + }; + }; + nmt.script = '' + assertFileExists home-files/.gemini/config/mcp_config.json + assertFileRegex home-files/.gemini/config/mcp_config.json '"github"' + assertFileRegex home-files/.gemini/config/mcp_config.json '"serverUrl"' + assertFileRegex home-files/.gemini/config/mcp_config.json '"url"' + assertFileRegex home-files/.gemini/config/mcp_config.json 'https://token.example/env' + assertFileRegex home-files/.gemini/config/mcp_config.json '"filesystem"' + assertFileRegex home-files/.gemini/config/mcp_config.json '"database"' + assertFileNotRegex home-files/.gemini/config/mcp_config.json '"other-tmp"' + ''; +} diff --git a/tests/modules/programs/antigravity-cli/policies-removed.nix b/tests/modules/programs/antigravity-cli/policies-removed.nix new file mode 100644 index 000000000..f0a524925 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/policies-removed.nix @@ -0,0 +1,28 @@ +_: + +{ + programs.antigravity-cli = { + enable = true; + package = null; + policies."my-rules" = { + rule = [ + { + toolName = "run_shell_command"; + commandPrefix = "git "; + decision = "ask_user"; + priority = 100; + } + ]; + }; + }; + + test.asserts.assertions.expected = [ + '' + `programs.antigravity-cli.policies` is only supported when + `programs.antigravity-cli.package` is a `gemini-cli` package. + Antigravity CLI configures permissions in + `programs.antigravity-cli.settings.permissions` or + `programs.antigravity-cli.permissions`. + '' + ]; +} diff --git a/tests/modules/programs/antigravity-cli/settings.json b/tests/modules/programs/antigravity-cli/settings.json new file mode 100644 index 000000000..deded7712 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/settings.json @@ -0,0 +1,17 @@ +{ + "altScreenMode": "always", + "artifactReviewPolicy": "agent-decides", + "colorScheme": "tokyo night", + "permissions": { + "allow": [ + "command(git)" + ], + "ask": [ + "command(*)" + ], + "deny": [ + "command(rm -rf)" + ] + }, + "toolPermission": "proceed-in-sandbox" +} diff --git a/tests/modules/programs/antigravity-cli/settings.nix b/tests/modules/programs/antigravity-cli/settings.nix new file mode 100644 index 000000000..be191d161 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/settings.nix @@ -0,0 +1,51 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + defaultModel = "gemini-2.5-flash"; + settings = { + colorScheme = "tokyo night"; + altScreenMode = "always"; + toolPermission = "proceed-in-sandbox"; + artifactReviewPolicy = "agent-decides"; + }; + commands = { + changelog = { + prompt = '' + Your task is to parse the ``, ``, and `` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file. + ''; + description = "Adds a new entry to the project's CHANGELOG.md file."; + }; + "git/fix" = { + prompt = "Please analyze the staged git changes and provide a code fix for the issue described here: {{args}}."; + description = "Generates a fix for a given GitHub issue."; + }; + }; + permissions = { + allow = [ "command(git)" ]; + deny = [ "command(rm -rf)" ]; + ask = [ "command(*)" ]; + }; + }; + nmt.script = '' + assertFileExists home-files/.gemini/antigravity-cli/settings.json + assertFileContent home-files/.gemini/antigravity-cli/settings.json \ + ${./settings.json} + assertFileExists home-files/.gemini/config/skills/changelog/SKILL.md + assertFileRegex home-files/.gemini/config/skills/changelog/SKILL.md \ + 'name: changelog' + assertFileRegex home-files/.gemini/config/skills/changelog/SKILL.md \ + "Adds a new entry to the project's CHANGELOG.md file." + assertFileExists home-files/.gemini/config/skills/git:fix/SKILL.md + assertFileRegex home-files/.gemini/config/skills/git:fix/SKILL.md \ + 'name: git:fix' + assertFileRegex home-files/.gemini/config/skills/git:fix/SKILL.md \ + 'Generates a fix for a given GitHub issue.' + + assertFileExists home-path/etc/profile.d/hm-session-vars.sh + assertFileContains home-path/etc/profile.d/hm-session-vars.sh \ + 'export GEMINI_MODEL="gemini-2.5-flash"' + ''; +} diff --git a/tests/modules/programs/antigravity-cli/skills-dir.nix b/tests/modules/programs/antigravity-cli/skills-dir.nix new file mode 100644 index 000000000..2571889bb --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills-dir.nix @@ -0,0 +1,26 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + skills = ./skills; + }; + + nmt.script = '' + assertFileExists home-files/.gemini/config/skills/xlsx/SKILL.md + assertLinkExists home-files/.gemini/config/skills/xlsx/SKILL.md + assertFileContent home-files/.gemini/config/skills/xlsx/SKILL.md \ + ${./skills/xlsx/SKILL.md} + + assertFileExists home-files/.gemini/config/skills/data-analysis/SKILL.md + assertLinkExists home-files/.gemini/config/skills/data-analysis/SKILL.md + assertFileContent home-files/.gemini/config/skills/data-analysis/SKILL.md \ + ${./skills/data-analysis/SKILL.md} + + assertFileExists home-files/.gemini/config/skills/pdf-processing/SKILL.md + assertLinkExists home-files/.gemini/config/skills/pdf-processing/SKILL.md + assertFileContent home-files/.gemini/config/skills/pdf-processing/SKILL.md \ + ${./skills/pdf-processing/SKILL.md} + ''; +} diff --git a/tests/modules/programs/antigravity-cli/skills-path-not-directory.nix b/tests/modules/programs/antigravity-cli/skills-path-not-directory.nix new file mode 100644 index 000000000..a313af85f --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills-path-not-directory.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + skills = ./skills/xlsx/SKILL.md; + }; + + test.asserts.assertions.expected = [ + "`programs.antigravity-cli.skills` must be a directory when set to a path" + ]; +} diff --git a/tests/modules/programs/antigravity-cli/skills-store-path-dir.nix b/tests/modules/programs/antigravity-cli/skills-store-path-dir.nix new file mode 100644 index 000000000..e9c0b80a9 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills-store-path-dir.nix @@ -0,0 +1,21 @@ +{ pkgs, ... }: + +let + src = pkgs.writeTextDir "skills/external-skill/SKILL.md" '' + # External Skill + ''; +in +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + skills = "${src}/skills"; + }; + + nmt.script = '' + assertFileExists home-files/.gemini/config/skills/external-skill/SKILL.md + assertLinkExists home-files/.gemini/config/skills/external-skill/SKILL.md + assertFileContent home-files/.gemini/config/skills/external-skill/SKILL.md \ + "${src}/skills/external-skill/SKILL.md" + ''; +} diff --git a/tests/modules/programs/antigravity-cli/skills.nix b/tests/modules/programs/antigravity-cli/skills.nix new file mode 100644 index 000000000..3f13e5c71 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills.nix @@ -0,0 +1,44 @@ +{ pkgs, ... }: + +{ + programs.antigravity-cli = { + enable = true; + package = pkgs.writeShellScriptBin "antigravity-cli" ""; + skills = { + xlsx = ./skills/xlsx/SKILL.md; + data-analysis = ./skills/data-analysis; + pdf-processing = '' + --- + name: pdf-processing + description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction. + --- + + # PDF Processing + + ## Quick start + + Use pdfplumber to extract text from PDFs: + + ```python + import pdfplumber + + with pdfplumber.open("document.pdf") as pdf: + text = pdf.pages[0].extract_text() + ``` + ''; + }; + }; + nmt.script = '' + assertFileExists home-files/.gemini/config/skills/xlsx/SKILL.md + assertFileContent home-files/.gemini/config/skills/xlsx/SKILL.md \ + ${./skills/xlsx/SKILL.md} + + assertFileExists home-files/.gemini/config/skills/data-analysis/SKILL.md + assertFileContent home-files/.gemini/config/skills/data-analysis/SKILL.md \ + ${./skills/data-analysis/SKILL.md} + + assertFileExists home-files/.gemini/config/skills/pdf-processing/SKILL.md + assertFileContent home-files/.gemini/config/skills/pdf-processing/SKILL.md \ + ${./skills/pdf-processing/SKILL.md} + ''; +} diff --git a/tests/modules/programs/antigravity-cli/skills/data-analysis/SKILL.md b/tests/modules/programs/antigravity-cli/skills/data-analysis/SKILL.md new file mode 100644 index 000000000..ff2ed4527 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills/data-analysis/SKILL.md @@ -0,0 +1,8 @@ +--- +name: data-analysis +description: Do some data analysis +--- + +# Data Analysis + +Use good data analytics tools for analysis 👍 diff --git a/tests/modules/programs/antigravity-cli/skills/pdf-processing/SKILL.md b/tests/modules/programs/antigravity-cli/skills/pdf-processing/SKILL.md new file mode 100644 index 000000000..c64826c41 --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills/pdf-processing/SKILL.md @@ -0,0 +1,17 @@ +--- +name: pdf-processing +description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction. +--- + +# PDF Processing + +## Quick start + +Use pdfplumber to extract text from PDFs: + +```python +import pdfplumber + +with pdfplumber.open("document.pdf") as pdf: + text = pdf.pages[0].extract_text() +``` diff --git a/tests/modules/programs/antigravity-cli/skills/xlsx/SKILL.md b/tests/modules/programs/antigravity-cli/skills/xlsx/SKILL.md new file mode 100644 index 000000000..ea88c85fa --- /dev/null +++ b/tests/modules/programs/antigravity-cli/skills/xlsx/SKILL.md @@ -0,0 +1,8 @@ +--- +name: xlsx +description: Handling xlsx files correctly +--- + +# XLSX Handling + +Use the python library called `openpyxl` to handle these files diff --git a/tests/modules/programs/gemini-cli/changelog.toml b/tests/modules/programs/gemini-cli/changelog.toml deleted file mode 100644 index 858eb68ac..000000000 --- a/tests/modules/programs/gemini-cli/changelog.toml +++ /dev/null @@ -1,2 +0,0 @@ -description = "Adds a new entry to the project's CHANGELOG.md file." -prompt = "Your task is to parse the ``, ``, and `` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.\n" diff --git a/tests/modules/programs/gemini-cli/fix.toml b/tests/modules/programs/gemini-cli/fix.toml deleted file mode 100644 index 807be9f4c..000000000 --- a/tests/modules/programs/gemini-cli/fix.toml +++ /dev/null @@ -1,2 +0,0 @@ -description = "Generates a fix for a given GitHub issue." -prompt = "Please analyze the staged git changes and provide a code fix for the issue described here: {{args}}." diff --git a/tests/modules/programs/gemini-cli/other-rules.toml b/tests/modules/programs/gemini-cli/other-rules.toml deleted file mode 100644 index 30b1911dd..000000000 --- a/tests/modules/programs/gemini-cli/other-rules.toml +++ /dev/null @@ -1,4 +0,0 @@ -[[rule]] -toolName = "read_file" -decision = "allow" -priority = 50 diff --git a/tests/modules/programs/gemini-cli/settings.json b/tests/modules/programs/gemini-cli/settings.json deleted file mode 100644 index 8d0604618..000000000 --- a/tests/modules/programs/gemini-cli/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "autoAccept": true, - "preferredEditor": "nvim", - "theme": "Default", - "vimMode": true -} diff --git a/tests/modules/programs/gemini-cli/skills-path-not-directory.nix b/tests/modules/programs/gemini-cli/skills-path-not-directory.nix deleted file mode 100644 index a1e389cb7..000000000 --- a/tests/modules/programs/gemini-cli/skills-path-not-directory.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ - programs.gemini-cli = { - enable = true; - skills = ./skills/xlsx/SKILL.md; - }; - - test.asserts.assertions.expected = [ - "`programs.gemini-cli.skills` must be a directory when set to a path" - ]; -}