mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
534 lines
17 KiB
Nix
534 lines
17 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
options,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.programs.antigravity-cli;
|
|
|
|
renamedGeminiOptions = [
|
|
"commands"
|
|
"context"
|
|
"defaultModel"
|
|
"enable"
|
|
"enableMcpIntegration"
|
|
"mcpServers"
|
|
"package"
|
|
"permissions"
|
|
"policies"
|
|
"settings"
|
|
"skills"
|
|
"useLegacyGeminiConfig"
|
|
];
|
|
|
|
oldGeminiCliDefined = lib.any (
|
|
name: (lib.getAttrFromPath [ "programs" "gemini-cli" name ] options).isDefined
|
|
) renamedGeminiOptions;
|
|
|
|
jsonFormat = pkgs.formats.json { };
|
|
tomlFormat = pkgs.formats.toml { };
|
|
|
|
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 =
|
|
let
|
|
renamedGeminiOption =
|
|
name:
|
|
lib.mkRenamedOptionModule
|
|
[
|
|
"programs"
|
|
"gemini-cli"
|
|
name
|
|
]
|
|
[
|
|
"programs"
|
|
"antigravity-cli"
|
|
name
|
|
];
|
|
in
|
|
map renamedGeminiOption renamedGeminiOptions;
|
|
|
|
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 [
|
|
(lib.mkIf oldGeminiCliDefined {
|
|
programs.antigravity-cli = {
|
|
package = lib.mkDefault pkgs.gemini-cli;
|
|
useLegacyGeminiConfig = lib.mkDefault true;
|
|
};
|
|
})
|
|
{
|
|
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 lib.hm.strings.isPathLike v then v else tomlFormat.generate "gemini-cli-policy-${n}.toml" v;
|
|
}
|
|
) cfg.policies
|
|
// (
|
|
if lib.hm.strings.isPathLike cfg.skills then
|
|
{
|
|
"${geminiSkillsDir}" = {
|
|
source = cfg.skills;
|
|
recursive = true;
|
|
};
|
|
}
|
|
else
|
|
lib.mapAttrs' (
|
|
n: v:
|
|
if lib.hm.strings.isPathLike v && lib.pathIsDirectory v then
|
|
lib.nameValuePair "${geminiSkillsDir}/${n}" {
|
|
source = v;
|
|
recursive = true;
|
|
}
|
|
else
|
|
lib.nameValuePair "${geminiSkillsDir}/${n}/SKILL.md" (
|
|
if lib.hm.strings.isPathLike 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 lib.hm.strings.isPathLike cfg.skills then
|
|
{
|
|
"${antigravitySkillsDir}" = {
|
|
source = cfg.skills;
|
|
recursive = true;
|
|
};
|
|
}
|
|
else
|
|
lib.mapAttrs' (
|
|
n: v:
|
|
if lib.hm.strings.isPathLike v && lib.pathIsDirectory v then
|
|
lib.nameValuePair "${antigravitySkillsDir}/${n}" {
|
|
source = v;
|
|
recursive = true;
|
|
}
|
|
else
|
|
lib.nameValuePair "${antigravitySkillsDir}/${n}/SKILL.md" (
|
|
if lib.hm.strings.isPathLike v then { source = v; } else { text = v; }
|
|
)
|
|
) cfg.skills
|
|
);
|
|
})
|
|
{
|
|
assertions = [
|
|
{
|
|
assertion = !lib.hm.strings.isPathLike cfg.skills || lib.pathIsDirectory cfg.skills;
|
|
message = "`programs.antigravity-cli.skills` must be a directory when set to a path";
|
|
}
|
|
];
|
|
}
|
|
]
|
|
);
|
|
}
|