235 lines
7.4 KiB
Nix
235 lines
7.4 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
with lib; let
|
|
cfg = config.coding.agents.pi;
|
|
mcpCfg = config.programs.mcp or null;
|
|
in {
|
|
options.coding.agents.pi = {
|
|
enable = mkEnableOption "Pi agent management via canonical agent.toml definitions";
|
|
|
|
mcpServers = mkOption {
|
|
type = types.attrsOf types.anything;
|
|
default = if mcpCfg != null then mcpCfg.servers else {};
|
|
defaultText = literalExpression "config.programs.mcp.servers";
|
|
description = ''
|
|
MCP server configurations for Pi (pi-mcp-adapter).
|
|
Written to ~/.pi/agent/mcp.json.
|
|
Automatically inherits from config.programs.mcp.servers.
|
|
'';
|
|
};
|
|
|
|
agentsInput = mkOption {
|
|
type = types.nullOr types.anything;
|
|
default = null;
|
|
description = ''
|
|
The `agents` flake input (your personal AGENTS repo).
|
|
When set, the primary agent's system prompt is rendered as SYSTEM.md,
|
|
all agents are listed in AGENTS.md, and subagent .md files are deployed.
|
|
'';
|
|
};
|
|
|
|
modelOverrides = mkOption {
|
|
type = types.attrsOf types.str;
|
|
default = {};
|
|
description = ''
|
|
Per-agent model overrides for Pi subagents.
|
|
Maps agent slug to model string, e.g.:
|
|
{ chiron = "anthropic/claude-sonnet-4"; chiron-forge = "anthropic/claude-sonnet-4"; }
|
|
'';
|
|
};
|
|
|
|
primaryAgent = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = ''
|
|
Override which canonical agent is used as primary for SYSTEM.md.
|
|
When null, the first agent with mode="primary" is used.
|
|
'';
|
|
};
|
|
|
|
settings = mkOption {
|
|
type = types.submodule {
|
|
freeformType = types.attrsOf types.anything;
|
|
options = {
|
|
packages = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = ''
|
|
Pi packages to install (npm:, git:, or local paths).
|
|
These are written to ~/.pi/agent/settings.json.
|
|
'';
|
|
};
|
|
|
|
defaultProvider = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Default LLM provider (e.g. 'anthropic', 'openai', 'zai').";
|
|
};
|
|
|
|
defaultModel = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Default model ID.";
|
|
};
|
|
|
|
defaultThinkingLevel = mkOption {
|
|
type = types.nullOr (types.enum ["off" "minimal" "low" "medium" "high" "xhigh"]);
|
|
default = null;
|
|
description = "Default extended thinking level.";
|
|
};
|
|
|
|
theme = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Pi theme name.";
|
|
};
|
|
|
|
hideThinkingBlock = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
description = "Hide thinking blocks in output.";
|
|
};
|
|
|
|
quietStartup = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
description = "Hide startup header.";
|
|
};
|
|
|
|
compaction = mkOption {
|
|
type = types.nullOr (types.submodule {
|
|
options = {
|
|
enabled = mkOption {
|
|
type = types.nullOr types.bool;
|
|
default = null;
|
|
};
|
|
reserveTokens = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
};
|
|
keepRecentTokens = mkOption {
|
|
type = types.nullOr types.int;
|
|
default = null;
|
|
};
|
|
};
|
|
});
|
|
default = null;
|
|
description = "Auto-compaction settings.";
|
|
};
|
|
|
|
enabledModels = mkOption {
|
|
type = types.nullOr (types.listOf types.str);
|
|
default = null;
|
|
description = "Model patterns for Ctrl+P cycling.";
|
|
};
|
|
|
|
sessionDir = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Directory where session files are stored.";
|
|
};
|
|
|
|
extensions = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Local extension file paths or directories.";
|
|
};
|
|
|
|
skills = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "Local skill file paths or directories.";
|
|
};
|
|
};
|
|
};
|
|
default = {};
|
|
description = ''
|
|
Pi settings written to ~/.pi/agent/settings.json.
|
|
Only non-null values are included in the generated JSON.
|
|
See pi docs/settings.md for all options.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable (let
|
|
# Build settings.json by filtering out null values recursively
|
|
filterNulls = attrs:
|
|
lib.filterAttrs (_: v: v != null) (
|
|
builtins.mapAttrs (_: v:
|
|
if builtins.isAttrs v
|
|
then let
|
|
filtered = filterNulls v;
|
|
in
|
|
if filtered == {} then null else filtered
|
|
else v) attrs
|
|
);
|
|
|
|
piSettings = filterNulls cfg.settings;
|
|
|
|
# Rendered agents (only computed when agentsInput is set)
|
|
rendered =
|
|
if cfg.agentsInput != null
|
|
then
|
|
(import ../../../../lib {inherit lib;}).agents.renderForPi {
|
|
inherit pkgs;
|
|
canonical = cfg.agentsInput.lib.loadAgents;
|
|
modelOverrides = cfg.modelOverrides;
|
|
primaryAgent = cfg.primaryAgent;
|
|
}
|
|
else null;
|
|
|
|
# Dynamic home.file entries for agent .md files
|
|
agentFiles =
|
|
if cfg.agentsInput != null
|
|
then
|
|
let
|
|
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
|
|
in
|
|
builtins.listToAttrs (
|
|
map (name: {
|
|
name = ".pi/agent/agents/${name}.md";
|
|
value = {source = "${rendered}/agents/${name}.md";};
|
|
})
|
|
agentNames
|
|
)
|
|
else {};
|
|
in {
|
|
home.file = mkMerge [
|
|
# ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ───────
|
|
(mkIf (cfg.mcpServers != {}) {
|
|
".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;};
|
|
})
|
|
|
|
# ── ~/.pi/agent/settings.json ──────────────────────────────────
|
|
{
|
|
".pi/agent/settings.json".text = builtins.toJSON piSettings;
|
|
}
|
|
|
|
# ── AGENTS.md — agent descriptions and specialist listing ──────
|
|
(mkIf (cfg.agentsInput != null) {
|
|
".pi/agent/AGENTS.md".source = "${rendered}/AGENTS.md";
|
|
})
|
|
|
|
# ── SYSTEM.md — primary agent's system prompt ──────────────────
|
|
(mkIf (cfg.agentsInput != null) {
|
|
".pi/agent/SYSTEM.md".source = "${rendered}/SYSTEM.md";
|
|
})
|
|
|
|
# ── Agents — pi-subagents .md files ────────────────────────────
|
|
agentFiles
|
|
|
|
# ── Skills symlinked from AGENTS repo ──────────────────────────
|
|
(mkIf (cfg.agentsInput != null) {
|
|
".pi/agent/skills".source = cfg.agentsInput.lib.mkOpencodeSkills {
|
|
inherit pkgs;
|
|
customSkills = "${cfg.agentsInput}/skills";
|
|
};
|
|
})
|
|
];
|
|
});
|
|
}
|