nixpi
4 min read
nixpi is a declarative Nix configuration framework for the Pi AI coding agent. One programs.pi module schema describes the whole agent — settings, model providers, extensions, skills, prompts, themes and their runtime dependencies — and the same config can be used as a standalone package, in home-manager, on NixOS or on nix-darwin.
The result is a self-contained, reproducible agent: a wrapped pi binary whose configuration and extensions live in the Nix store, pinned by a flake lock. A project can ship its own agent and anyone can start it with nix run. MIT licensed.
sh
nix run github:mateusdcc/nixpi # default agent, no setup
Configuration
nix
programs.pi = {
enable = true;
settings = {
defaultProvider = "anthropic";
defaultThinkingLevel = "high";
theme = "dark";
};
extensions = {
ripgrep-search.enable = true;
plan-mode = { enable = true; mode = "balanced"; };
};
skills.commit-style.enable = true;
providers.local = { # e.g. Ollama
baseUrl = "http://localhost:11434/v1";
models = [ { id = "qwen2.5-coder"; } ];
};
environment = {
variables.PI_OFFLINE = "1"; # non-secret only
required = [ "ANTHROPIC_API_KEY" ]; # checked at launch, never stored
};
};
Options are typed, so a typo or invalid value fails at evaluation time rather than at runtime.
Integrations
Every integration uses the same options. The only difference is where the built package ends up.
| Situation | Entry point | Run it |
|---|---|---|
| One-off trial | packages.<system>.default | nix run github:mateusdcc/nixpi |
| Project-owned agent | lib.nixpi.makePi | nix run .# |
| User environment | homeModules.default | activate home-manager |
| NixOS host | nixosModules.default | rebuild NixOS |
| macOS host | nixDarwinModules.default | rebuild nix-darwin |
There are also flake templates (standalone, home-manager, devshell, learning) and direnv support.
Project-owned agent
Build an agent as a flake package, so the repo carries exactly the agent it expects:
nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-26.05";
nixpi.url = "github:mateusdcc/nixpi";
};
outputs = { nixpkgs, nixpi, ... }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.default = nixpi.lib.nixpi.makePi {
inherit pkgs;
modules = [{
programs.pi = {
enable = true;
settings.defaultProvider = "openai";
extensions.ripgrep-search.enable = true;
environment.required = [ "OPENAI_API_KEY" ];
};
}];
};
};
}
makePi returns a package whose passthru exposes config, options, unwrapped and extend. extend derives a variant agent without rebuilding the whole config:
nix
pi.extend { programs.pi.skills.commit-style.enable = true; }
State
Configuration is immutable and runtime state stays mutable:
• In the Nix store: settings.json, models.json and the packaged extensions, skills, prompts and themes.
• Writable in ~/.pi/agent: auth.json, sessions/, lock files and debug logs.
home-manager links the generated settings.json/models.json into ~/.pi/agent. The standalone wrapper points Pi at the store paths and keeps state in the home directory (sessionDir / PI_CODING_AGENT_SESSION_DIR can move sessions elsewhere).
Secrets
Never put API keys in environment.variables or anywhere else in a Nix expression, because the Nix store is world-readable. Use environment.required to list the variables that must exist, and supply the values from the shell, a secret manager such as sops-nix, or auth.json.
Building blocks
lib.nixpi packages your own resources for use as modules:
| Function | Creates |
|---|---|
evalPi | Full module evaluation (config, options) |
makePi | The configured, wrapped Pi package |
mkPiExtension | Extension from a src dir, inline JS/TS content, or single entrypoint script |
mkPiSkill | Skill (SKILL.md) from name/description/content |
mkPiPrompt | Prompt template (argumentHint → frontmatter) |
mkPiTheme | JSON theme |
mkPiProvider | Model provider (baseUrl, api, models) |
mkPi*Module | Wraps any of the above into a programs.pi.<kind>.<name> option |
Each resource can declare runtimePackages, which go onto the agent's PATH. An extension that shells out to git or rg brings them along instead of relying on the host.
nix
myExtension = nixpi.lib.nixpi.mkPiExtension {
inherit pkgs;
pname = "hello";
content = ''
export default pi => pi.registerCommand("hello", { handler: () => console.log("hello") });
'';
runtimePackages = [ pkgs.git ];
};
Bundled
• Profiles: minimal, research, learning, legal-research. These are presets set with mkDefault, so your own config can still override them.
• Extensions: ripgrep-search, plan-mode, echo, pi-gpt-search, research-tools.
• Skills: commit-style and a set of research/market-analysis skills.
Flake outputs: piModules.{base,default,profiles,extensions,skills,providers}, host modules, and overlays.default, which exposes everything under pkgs.nixpi.
pi-packages
The companion flake pi-packages adds more modules:
• subagents for multi-agent swarms
• lazy-archify for architecture diagrams
• image-tools for clipboard images
• app-screenshot for macOS screenshots
• the generative-ui skill
To use it, import pi-packages.piModules.default (or homeModules.default) next to nixpi, with inputs.nixpi.follows = "nixpi".