mirror of
https://github.com/nix-community/home-manager.git
synced 2026-11-07 14:59:33 +00:00
Keep settings writable while applying declared values. Match packages by native npm, Git and lexical local-path identities so declarations replace equivalent saved entries without reordering unrelated packages.
288 lines
8.7 KiB
Nix
288 lines
8.7 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
literalExpression
|
|
mkEnableOption
|
|
mkIf
|
|
mkOption
|
|
mkPackageOption
|
|
;
|
|
|
|
cfg = config.programs.pi-coding-agent;
|
|
|
|
jsonFormat = pkgs.formats.json { };
|
|
|
|
upstreamConfigDir = "${config.home.homeDirectory}/.pi/agent";
|
|
|
|
packageWithExtraPackages =
|
|
if cfg.package != null && cfg.extraPackages != [ ] then
|
|
pkgs.symlinkJoin {
|
|
inherit (cfg.package) meta;
|
|
name = "${lib.getName cfg.package}-wrapped-${lib.getVersion cfg.package}";
|
|
paths = [ cfg.package ];
|
|
preferLocalBuild = true;
|
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
|
postBuild = ''
|
|
wrapProgram $out/bin/pi \
|
|
--suffix PATH : ${lib.makeBinPath cfg.extraPackages}
|
|
'';
|
|
}
|
|
else
|
|
cfg.package;
|
|
in
|
|
{
|
|
meta.maintainers = with lib.maintainers; [ semi710 ];
|
|
|
|
options.programs.pi-coding-agent = {
|
|
enable = mkEnableOption "pi-coding-agent";
|
|
|
|
package = mkPackageOption pkgs "pi-coding-agent" { nullable = true; };
|
|
|
|
extraPackages = mkOption {
|
|
type = with lib.types; listOf package;
|
|
default = [ ];
|
|
example = literalExpression "[ pkgs.nodejs pkgs.bun ]";
|
|
description = ''
|
|
Extra packages available to Pi Coding Agent.
|
|
These are added to the PATH of the wrapped pi binary.
|
|
|
|
Needed for packages installed by pi (e.g.
|
|
{command}`npm:@termdraw/pi` requires {command}`npm` and
|
|
{command}`bun`).
|
|
'';
|
|
};
|
|
|
|
configDir = mkOption {
|
|
type = lib.types.str;
|
|
default = upstreamConfigDir;
|
|
defaultText = literalExpression ''"''${config.home.homeDirectory}/.pi/agent"'';
|
|
example = literalExpression ''"''${config.xdg.configHome}/pi/agent"'';
|
|
description = ''
|
|
Directory holding Pi Coding Agent's configuration files.
|
|
|
|
Defaults to {file}`~/.pi/agent`, matching the upstream
|
|
{command}`pi` CLI default. The {env}`PI_CODING_AGENT_DIR`
|
|
environment variable is exported automatically whenever the
|
|
directory differs from this default so the CLI reads
|
|
configuration from the same location.
|
|
'';
|
|
};
|
|
|
|
mutableSettings = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Keep settings.json writable. Packages are matched by npm name or Git
|
|
repository, ignoring versions and refs. Declared entries replace matching
|
|
entries in place; other entries keep their relative order. Local paths
|
|
are resolved lexically against the configuration directory, without following
|
|
symlinks. Home-relative paths and local file URLs are also supported.
|
|
Unsupported Git source forms cause activation to fail rather than leave
|
|
a stale pin. The enabledModels list is replaced as a whole when declared.
|
|
Other configuration files remain immutable.
|
|
|
|
Declared values take precedence on activation. Removing a declaration does
|
|
not remove it from the existing file. Stop the application before activation
|
|
to avoid concurrent writes. Comments and formatting are not preserved.
|
|
Switching back to immutable configuration uses Home Manager's normal
|
|
file collision handling.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
inherit (jsonFormat) type;
|
|
default = { };
|
|
example = {
|
|
defaultProvider = "anthropic";
|
|
defaultModel = "claude-sonnet-4-20250514";
|
|
defaultThinkingLevel = "medium";
|
|
theme = "dark";
|
|
packages = [
|
|
"npm:@termdraw/pi"
|
|
"npm:pi-mcp-adapter"
|
|
];
|
|
compaction = {
|
|
enabled = true;
|
|
reserveTokens = 16384;
|
|
keepRecentTokens = 20000;
|
|
};
|
|
retry = {
|
|
enabled = true;
|
|
maxRetries = 3;
|
|
};
|
|
enabledModels = [
|
|
"claude-*"
|
|
"gpt-4o"
|
|
];
|
|
};
|
|
description = ''
|
|
Configuration written to
|
|
{file}`~/.pi/agent/settings.json`.
|
|
See <https://pi.dev/docs/latest/settings> for the
|
|
documentation.
|
|
'';
|
|
};
|
|
|
|
keybindings = mkOption {
|
|
inherit (jsonFormat) type;
|
|
default = { };
|
|
example = {
|
|
"tui.editor.cursorUp" = [
|
|
"up"
|
|
"ctrl+p"
|
|
];
|
|
"tui.editor.cursorDown" = [
|
|
"down"
|
|
"ctrl+n"
|
|
];
|
|
"tui.editor.deleteWordBackward" = [
|
|
"ctrl+w"
|
|
"alt+backspace"
|
|
];
|
|
};
|
|
description = ''
|
|
Keybindings configuration written to
|
|
{file}`~/.pi/agent/keybindings.json`.
|
|
See <https://pi.dev/docs/latest/keybindings> for the
|
|
documentation.
|
|
'';
|
|
};
|
|
|
|
models = mkOption {
|
|
inherit (jsonFormat) type;
|
|
default = { };
|
|
example = {
|
|
providers = {
|
|
ollama = {
|
|
baseUrl = "http://localhost:11434/v1";
|
|
api = "openai-completions";
|
|
apiKey = "ollama";
|
|
models = [ { id = "llama3.1:8b"; } ];
|
|
};
|
|
};
|
|
};
|
|
description = ''
|
|
Custom model providers written to
|
|
{file}`~/.pi/agent/models.json`.
|
|
|
|
Each provider entry may contain `baseUrl`,
|
|
`api`, `apiKey`, `compat`, and a `models`
|
|
list with `id`, `name`, `reasoning`, etc.
|
|
|
|
See <https://pi.dev/docs/latest/models> for the
|
|
documentation.
|
|
'';
|
|
};
|
|
|
|
context = mkOption {
|
|
type = lib.types.either lib.types.lines lib.types.path;
|
|
default = "";
|
|
description = ''
|
|
Global context for Pi Coding Agent.
|
|
|
|
The value is either:
|
|
- Inline content as a string
|
|
- A path to a file containing the content
|
|
|
|
The configured content is written to
|
|
{file}`AGENTS.md` inside
|
|
{option}`programs.pi-coding-agent.configDir`
|
|
(default {file}`~/.pi/agent/AGENTS.md`).
|
|
'';
|
|
example = literalExpression "./pi-context.md";
|
|
};
|
|
|
|
appendSystem = mkOption {
|
|
type = lib.types.either lib.types.lines lib.types.path;
|
|
default = "";
|
|
description = ''
|
|
Additional instructions appended to Pi Coding Agent's system prompt.
|
|
|
|
The value is either:
|
|
- Inline content as a string
|
|
- A path to a file containing the content
|
|
|
|
The configured content is written to
|
|
{file}`APPEND_SYSTEM.md` inside
|
|
{option}`programs.pi-coding-agent.configDir`
|
|
(default {file}`~/.pi/agent/APPEND_SYSTEM.md`).
|
|
|
|
A project-local {file}`.pi/APPEND_SYSTEM.md` takes precedence
|
|
over this file.
|
|
'';
|
|
example = literalExpression "./pi-append-system.md";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
home = {
|
|
activation.piCodingAgentMutableSettings = lib.mkIf (cfg.mutableSettings && cfg.settings != { }) (
|
|
lib.hm.dag.entryAfter [ "linkGeneration" ] (
|
|
lib.hm.generators.mkImpureConfigMerger {
|
|
inherit pkgs;
|
|
format = "json";
|
|
empty = "{}";
|
|
mode = "600";
|
|
path = "${cfg.configDir}/settings.json";
|
|
staticSettings = jsonFormat.generate "pi-coding-agent-settings.json" cfg.settings;
|
|
|
|
jqOperation = ''
|
|
${builtins.toJSON config.home.homeDirectory} as $homeDirectory
|
|
| ${builtins.toJSON cfg.configDir} as $configDir
|
|
| ${builtins.readFile ./pi-coding-agent-settings-filter.jq}
|
|
'';
|
|
}
|
|
)
|
|
);
|
|
|
|
packages = mkIf (packageWithExtraPackages != null) [
|
|
packageWithExtraPackages
|
|
];
|
|
|
|
sessionVariables = lib.mkIf (cfg.configDir != upstreamConfigDir) {
|
|
PI_CODING_AGENT_DIR = cfg.configDir;
|
|
};
|
|
|
|
file = lib.mkMerge [
|
|
(mkIf (!cfg.mutableSettings && cfg.settings != { }) {
|
|
"${cfg.configDir}/settings.json".source =
|
|
jsonFormat.generate "pi-coding-agent-settings.json" cfg.settings;
|
|
})
|
|
|
|
(mkIf (cfg.keybindings != { }) {
|
|
"${cfg.configDir}/keybindings.json".source =
|
|
jsonFormat.generate "pi-coding-agent-keybindings.json" cfg.keybindings;
|
|
})
|
|
|
|
(mkIf (cfg.models != { }) {
|
|
"${cfg.configDir}/models.json".source =
|
|
jsonFormat.generate "pi-coding-agent-models.json" cfg.models;
|
|
})
|
|
|
|
(
|
|
if lib.hm.strings.isPathLike cfg.context then
|
|
{ "${cfg.configDir}/AGENTS.md".source = cfg.context; }
|
|
else
|
|
(mkIf (cfg.context != "") {
|
|
"${cfg.configDir}/AGENTS.md".text = cfg.context;
|
|
})
|
|
)
|
|
|
|
(
|
|
if lib.hm.strings.isPathLike cfg.appendSystem then
|
|
{ "${cfg.configDir}/APPEND_SYSTEM.md".source = cfg.appendSystem; }
|
|
else
|
|
(mkIf (cfg.appendSystem != "") {
|
|
"${cfg.configDir}/APPEND_SYSTEM.md".text = cfg.appendSystem;
|
|
})
|
|
)
|
|
];
|
|
};
|
|
};
|
|
}
|