mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
antigravity-cli: rename gemini-cli module
This commit is contained in:
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@@ -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
|
||||
|
||||
498
modules/programs/antigravity-cli.nix
Normal file
498
modules/programs/antigravity-cli.nix
Normal file
@@ -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 `<version>`, `<change_type>`, and `<message>` 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/<name>/SKILL.md`)
|
||||
- A path to a file (creates `~/.gemini/config/skills/<name>/SKILL.md`)
|
||||
- A path to a directory (symlinks `~/.gemini/config/skills/<name>/` 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";
|
||||
}
|
||||
];
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -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 `<version>`, `<change_type>`, and `<message>` 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/<name>/SKILL.md`)
|
||||
- A path to a file (creates `~/.gemini/skills/<name>/SKILL.md`)
|
||||
- A path to a directory (symlinks `~/.gemini/skills/<name>/` 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;
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
61
tests/modules/programs/antigravity-cli-legacy/context.nix
Normal file
61
tests/modules/programs/antigravity-cli-legacy/context.nix
Normal file
@@ -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"
|
||||
'';
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"'
|
||||
@@ -1,5 +1,5 @@
|
||||
[[rule]]
|
||||
commandPrefix = "git "
|
||||
toolName = "run_shell_command"
|
||||
commandPrefix = "nix "
|
||||
decision = "ask_user"
|
||||
priority = 100
|
||||
toolName = "run_shell_command"
|
||||
44
tests/modules/programs/antigravity-cli-legacy/policies.nix
Normal file
44
tests/modules/programs/antigravity-cli-legacy/policies.nix
Normal file
@@ -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 "'
|
||||
'';
|
||||
}
|
||||
17
tests/modules/programs/antigravity-cli-legacy/settings.json
Normal file
17
tests/modules/programs/antigravity-cli-legacy/settings.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"autoAccept": true,
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"command(git)"
|
||||
],
|
||||
"ask": [
|
||||
"command(*)"
|
||||
],
|
||||
"deny": [
|
||||
"command(rm -rf)"
|
||||
]
|
||||
},
|
||||
"preferredEditor": "nvim",
|
||||
"theme": "Default",
|
||||
"vimMode": true
|
||||
}
|
||||
@@ -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 \
|
||||
@@ -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
|
||||
@@ -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"
|
||||
];
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 \
|
||||
@@ -0,0 +1 @@
|
||||
Additional context for specialized tasks.
|
||||
9
tests/modules/programs/antigravity-cli/context-inline.md
Normal file
9
tests/modules/programs/antigravity-cli/context-inline.md
Normal file
@@ -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
|
||||
9
tests/modules/programs/antigravity-cli/context.md
Normal file
9
tests/modules/programs/antigravity-cli/context.md
Normal file
@@ -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
|
||||
@@ -1,6 +1,9 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
programs.gemini-cli = {
|
||||
programs.antigravity-cli = {
|
||||
enable = true;
|
||||
package = pkgs.writeShellScriptBin "antigravity-cli" "";
|
||||
context = {
|
||||
# Test inline content
|
||||
GEMINI = ''
|
||||
12
tests/modules/programs/antigravity-cli/default.nix
Normal file
12
tests/modules/programs/antigravity-cli/default.nix
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
'';
|
||||
}
|
||||
@@ -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
|
||||
'';
|
||||
}
|
||||
66
tests/modules/programs/antigravity-cli/mcp.nix
Normal file
66
tests/modules/programs/antigravity-cli/mcp.nix
Normal file
@@ -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"'
|
||||
'';
|
||||
}
|
||||
28
tests/modules/programs/antigravity-cli/policies-removed.nix
Normal file
28
tests/modules/programs/antigravity-cli/policies-removed.nix
Normal file
@@ -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`.
|
||||
''
|
||||
];
|
||||
}
|
||||
17
tests/modules/programs/antigravity-cli/settings.json
Normal file
17
tests/modules/programs/antigravity-cli/settings.json
Normal file
@@ -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"
|
||||
}
|
||||
51
tests/modules/programs/antigravity-cli/settings.nix
Normal file
51
tests/modules/programs/antigravity-cli/settings.nix
Normal file
@@ -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 `<version>`, `<change_type>`, and `<message>` 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"'
|
||||
'';
|
||||
}
|
||||
26
tests/modules/programs/antigravity-cli/skills-dir.nix
Normal file
26
tests/modules/programs/antigravity-cli/skills-dir.nix
Normal file
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -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"
|
||||
];
|
||||
}
|
||||
@@ -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"
|
||||
'';
|
||||
}
|
||||
44
tests/modules/programs/antigravity-cli/skills.nix
Normal file
44
tests/modules/programs/antigravity-cli/skills.nix
Normal file
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: data-analysis
|
||||
description: Do some data analysis
|
||||
---
|
||||
|
||||
# Data Analysis
|
||||
|
||||
Use good data analytics tools for analysis 👍
|
||||
@@ -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()
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
name: xlsx
|
||||
description: Handling xlsx files correctly
|
||||
---
|
||||
|
||||
# XLSX Handling
|
||||
|
||||
Use the python library called `openpyxl` to handle these files
|
||||
@@ -1,2 +0,0 @@
|
||||
description = "Adds a new entry to the project's CHANGELOG.md file."
|
||||
prompt = "Your task is to parse the `<version>`, `<change_type>`, and `<message>` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.\n"
|
||||
@@ -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}}."
|
||||
@@ -1,4 +0,0 @@
|
||||
[[rule]]
|
||||
toolName = "read_file"
|
||||
decision = "allow"
|
||||
priority = 50
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"autoAccept": true,
|
||||
"preferredEditor": "nvim",
|
||||
"theme": "Default",
|
||||
"vimMode": true
|
||||
}
|
||||
@@ -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"
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user