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:
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;
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user