diff --git a/nix/packages.nix b/nix/packages.nix index eb50d4a17..8795e35c8 100644 --- a/nix/packages.nix +++ b/nix/packages.nix @@ -1,54 +1,105 @@ # nix/packages.nix — Hermes Agent package built with uv2nix -{ inputs, ... }: { - perSystem = { pkgs, system, ... }: +{ inputs, ... }: +{ + perSystem = + { pkgs, ... }: let hermesVenv = pkgs.callPackage ./python.nix { inherit (inputs) uv2nix pyproject-nix pyproject-build-systems; }; + hermesTui = pkgs.callPackage ./tui.nix { }; + # Import bundled skills, excluding runtime caches bundledSkills = pkgs.lib.cleanSourceWith { src = ../skills; - filter = path: _type: - !(pkgs.lib.hasInfix "/index-cache/" path); + filter = path: _type: !(pkgs.lib.hasInfix "/index-cache/" path); }; runtimeDeps = with pkgs; [ - nodejs_20 ripgrep git openssh ffmpeg tirith + nodejs_22 + ripgrep + git + openssh + ffmpeg + tirith ]; runtimePath = pkgs.lib.makeBinPath runtimeDeps; - in { - packages.default = pkgs.stdenv.mkDerivation { - pname = "hermes-agent"; - version = (builtins.fromTOML (builtins.readFile ../pyproject.toml)).project.version; - dontUnpack = true; - dontBuild = true; - nativeBuildInputs = [ pkgs.makeWrapper ]; + # Lockfile hashes for dev shell stamps + pyprojectHash = builtins.hashString "sha256" (builtins.readFile ../pyproject.toml); + uvLockHash = + if builtins.pathExists ../uv.lock then + builtins.hashString "sha256" (builtins.readFile ../uv.lock) + else + "none"; + in + { + packages = { + default = pkgs.stdenv.mkDerivation { + pname = "hermes-agent"; + version = (fromTOML (builtins.readFile ../pyproject.toml)).project.version; - installPhase = '' - runHook preInstall + dontUnpack = true; + dontBuild = true; + nativeBuildInputs = [ pkgs.makeWrapper ]; - mkdir -p $out/share/hermes-agent $out/bin - cp -r ${bundledSkills} $out/share/hermes-agent/skills + installPhase = '' + runHook preInstall - ${pkgs.lib.concatMapStringsSep "\n" (name: '' - makeWrapper ${hermesVenv}/bin/${name} $out/bin/${name} \ - --suffix PATH : "${runtimePath}" \ - --set HERMES_BUNDLED_SKILLS $out/share/hermes-agent/skills - '') [ "hermes" "hermes-agent" "hermes-acp" ]} + mkdir -p $out/share/hermes-agent $out/bin + cp -r ${bundledSkills} $out/share/hermes-agent/skills - runHook postInstall - ''; + # copy pre-built TUI (same layout as dev: ui-tui/dist/ + node_modules/) + mkdir -p $out/ui-tui + cp -r ${hermesTui}/lib/hermes-tui/* $out/ui-tui/ - meta = with pkgs.lib; { - description = "AI agent with advanced tool-calling capabilities"; - homepage = "https://github.com/NousResearch/hermes-agent"; - mainProgram = "hermes"; - license = licenses.mit; - platforms = platforms.unix; + ${pkgs.lib.concatMapStringsSep "\n" + (name: '' + makeWrapper ${hermesVenv}/bin/${name} $out/bin/${name} \ + --suffix PATH : "${runtimePath}" \ + --set HERMES_BUNDLED_SKILLS $out/share/hermes-agent/skills \ + --set HERMES_TUI_DIR $out/ui-tui \ + --set HERMES_PYTHON ${hermesVenv}/bin/python3 + '') + [ + "hermes" + "hermes-agent" + "hermes-acp" + ] + } + + runHook postInstall + ''; + + passthru.devShellHook = '' + STAMP=".nix-stamps/hermes-agent" + STAMP_VALUE="${pyprojectHash}:${uvLockHash}" + if [ ! -f "$STAMP" ] || [ "$(cat "$STAMP")" != "$STAMP_VALUE" ]; then + echo "hermes-agent: installing Python dependencies..." + uv venv .venv --python ${pkgs.python311}/bin/python3 2>/dev/null || true + source .venv/bin/activate + uv pip install -e ".[all]" + [ -d mini-swe-agent ] && uv pip install -e ./mini-swe-agent 2>/dev/null || true + [ -d tinker-atropos ] && uv pip install -e ./tinker-atropos 2>/dev/null || true + mkdir -p .nix-stamps + echo "$STAMP_VALUE" > "$STAMP" + else + source .venv/bin/activate + fi + ''; + + meta = with pkgs.lib; { + description = "AI agent with advanced tool-calling capabilities"; + homepage = "https://github.com/NousResearch/hermes-agent"; + mainProgram = "hermes"; + license = licenses.mit; + platforms = platforms.unix; + }; }; + + tui = hermesTui; }; }; }