From 588d8b8d6ababedd2c944eaa6dff6e6a81a028a0 Mon Sep 17 00:00:00 2001 From: Hari <73809867+harivansh-afk@users.noreply.github.com> Date: Thu, 2 Apr 2026 21:44:43 -0400 Subject: [PATCH] pi-agent stack (#46) * pi-agent stack * fix: hardcode pi binary path, set XDG env for npm npm prefix discovery fails in systemd and activation contexts because NPM_CONFIG_USERCONFIG and XDG_DATA_HOME are not set. Hardcode the known path ~/.local/share/npm/bin/pi instead. --- home/common.nix | 1 + home/pi.nix | 52 ++++++++++++++++++++++++ hosts/netty/configuration.nix | 1 + hosts/netty/pi-agent.nix | 74 +++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 home/pi.nix create mode 100644 hosts/netty/pi-agent.nix diff --git a/home/common.nix b/home/common.nix index 1edd08e..1f93c52 100644 --- a/home/common.nix +++ b/home/common.nix @@ -21,6 +21,7 @@ ./mise.nix ./migration.nix ./nvim.nix + ./pi.nix ./prompt.nix ./skills.nix ./scripts.nix diff --git a/home/pi.nix b/home/pi.nix new file mode 100644 index 0000000..8082ae5 --- /dev/null +++ b/home/pi.nix @@ -0,0 +1,52 @@ +{ + config, + lib, + pkgs, + hostConfig, + ... +}: +let + npmDir = "${config.xdg.dataHome}/npm"; + piBin = "${npmDir}/bin/pi"; +in +lib.mkIf hostConfig.isLinux { + # Install pi-coding-agent globally via npm at activation time. + home.activation.installPiAgent = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + export PATH="${ + lib.makeBinPath [ + pkgs.nodejs_22 + pkgs.coreutils + ] + }:$PATH" + export NPM_CONFIG_USERCONFIG="${config.xdg.configHome}/npm/npmrc" + export XDG_DATA_HOME="${config.xdg.dataHome}" + export XDG_CACHE_HOME="${config.xdg.cacheHome}" + + if [ ! -d "${npmDir}/lib/node_modules/@mariozechner/pi-coding-agent" ]; then + npm install -g @mariozechner/pi-coding-agent 2>/dev/null || true + fi + ''; + + # Install Pi extensions at activation time: + # - @e9n/pi-channels: Telegram/Slack bridge with RPC-based persistent sessions + # - pi-schedule-prompt: cron/interval scheduled prompts + # - pi-subagents: background task delegation with async execution + home.activation.installPiExtensions = lib.hm.dag.entryAfter [ "installPiAgent" ] '' + export PATH="${ + lib.makeBinPath [ + pkgs.nodejs_22 + pkgs.coreutils + pkgs.git + ] + }:$PATH" + export NPM_CONFIG_USERCONFIG="${config.xdg.configHome}/npm/npmrc" + export XDG_DATA_HOME="${config.xdg.dataHome}" + export XDG_CACHE_HOME="${config.xdg.cacheHome}" + + if [ -x "${piBin}" ]; then + for pkg in "@e9n/pi-channels" "pi-schedule-prompt" "pi-subagents"; do + "${piBin}" install "npm:$pkg" 2>/dev/null || true + done + fi + ''; +} diff --git a/hosts/netty/configuration.nix b/hosts/netty/configuration.nix index 86803d0..f3a3cf5 100644 --- a/hosts/netty/configuration.nix +++ b/hosts/netty/configuration.nix @@ -18,6 +18,7 @@ in ./vaultwarden.nix ./forgejo.nix ./betternas.nix + ./pi-agent.nix ../../modules/base.nix (modulesPath + "/profiles/minimal.nix") (modulesPath + "/profiles/headless.nix") diff --git a/hosts/netty/pi-agent.nix b/hosts/netty/pi-agent.nix new file mode 100644 index 0000000..2175d94 --- /dev/null +++ b/hosts/netty/pi-agent.nix @@ -0,0 +1,74 @@ +{ + pkgs, + username, + ... +}: +let + piAgentEnvFile = "/var/lib/pi-agent/pi-agent.env"; + piAgentEnvCheck = pkgs.writeShellScript "pi-agent-env-check" '' + [ -f "${piAgentEnvFile}" ] + ''; + + piBin = "/home/${username}/.local/share/npm/bin/pi"; + + # Wrapper that exec's pi inside tmux's foreground process so systemd + # tracks the actual PID. When pi dies, tmux exits, systemd sees it + # and triggers Restart=on-failure. + piAgentStart = pkgs.writeShellScript "start-pi-agent" '' + if [ ! -x "${piBin}" ]; then + echo "pi binary not found at ${piBin}" >&2 + exit 1 + fi + + # tmux runs in the foreground (-D) so systemd tracks this process. + # The inner shell exec's pi so the tmux pane PID *is* the pi PID. + exec ${pkgs.tmux}/bin/tmux new-session -D -s pi-agent \ + "exec ${piBin} --chat-bridge" + ''; +in +{ + # Ensure state directory and env file have correct permissions. + systemd.tmpfiles.rules = [ + "d /var/lib/pi-agent 0750 ${username} users -" + "z ${piAgentEnvFile} 0600 ${username} users -" + ]; + + # Pi agent running 24/7 in a foreground tmux session. + # Extensions (pi-channels, pi-schedule-prompt, pi-subagents) load + # inside Pi's process and handle Telegram bridging, scheduled tasks, + # and background subagent delegation. + # + # The --chat-bridge flag auto-enables the Telegram bridge on startup. + # Telegram bot token lives in ~/.pi/agent/settings.json (see pi-channels docs). + # ANTHROPIC_API_KEY comes from the env file. + # + # tmux session name: pi-agent + # Attach for debugging: tmux attach -t pi-agent + systemd.services.pi-agent = { + description = "Pi Coding Agent (24/7)"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ + pkgs.nodejs_22 + pkgs.git + pkgs.tmux + pkgs.coreutils + ]; + environment = { + HOME = "/home/${username}"; + NODE_NO_WARNINGS = "1"; + }; + serviceConfig = { + Type = "simple"; + User = username; + Group = "users"; + WorkingDirectory = "/home/${username}"; + ExecCondition = piAgentEnvCheck; + EnvironmentFile = piAgentEnvFile; + ExecStart = piAgentStart; + Restart = "on-failure"; + RestartSec = 10; + }; + }; +}