diff --git a/flake.nix b/flake.nix index 5d15287..828c89a 100644 --- a/flake.nix +++ b/flake.nix @@ -23,10 +23,16 @@ default = self.packages.${system}.hermes-agent; }; - checks = import ./checks.nix { - inherit pkgs; - inherit (self.packages.${system}) hermes-agent; - }; + checks = + (import ./checks.nix { + inherit pkgs; + inherit (self.packages.${system}) hermes-agent; + }) + // { + skills-coexistence = import ./tests/skills-coexistence.nix { + inherit self nixpkgs system; + }; + }; devShells.default = pkgs.mkShell { packages = [ self.packages.${system}.hermes-agent ]; diff --git a/module.nix b/module.nix index c332a9c..df9e682 100644 --- a/module.nix +++ b/module.nix @@ -505,6 +505,13 @@ in system.activationScripts."hermes-agent-setup" = lib.stringAfter [ "users" ] '' set -euo pipefail + # Ensure required directories exist during activation (do not rely on tmpfiles ordering) + install -d -o ${cfg.user} -g ${cfg.group} -m 0750 ${cfg.stateDir} + install -d -o ${cfg.user} -g ${cfg.group} -m 0750 ${cfg.stateDir}/.hermes + install -d -o ${cfg.user} -g ${cfg.group} -m 0750 ${cfg.stateDir}/.hermes/skills + install -d -o ${cfg.user} -g ${cfg.group} -m 0750 ${cfg.workingDirectory} + install -d -o ${cfg.user} -g ${cfg.group} -m 0750 ${builtins.dirOf cfg.logPath} + # Link config file install -o ${cfg.user} -g ${cfg.group} -m 0640 -D ${configFile} ${cfg.stateDir}/.hermes/cli-config.yaml diff --git a/tests/fixtures/custom-skill/SKILL.md b/tests/fixtures/custom-skill/SKILL.md new file mode 100644 index 0000000..c42e462 --- /dev/null +++ b/tests/fixtures/custom-skill/SKILL.md @@ -0,0 +1,8 @@ +--- +name: repo-watch +description: Test custom skill for nix-hermes-agent module validation. +--- + +# repo-watch + +Test skill body. diff --git a/tests/skills-coexistence.nix b/tests/skills-coexistence.nix new file mode 100644 index 0000000..e7c9e1c --- /dev/null +++ b/tests/skills-coexistence.nix @@ -0,0 +1,58 @@ +{ self, nixpkgs, system }: + +let + pkgs = import nixpkgs { inherit system; }; + testSkill = builtins.path { + path = ./fixtures/custom-skill; + name = "hermes-test-custom-skill"; + }; +in +pkgs.testers.runNixOSTest { + name = "hermes-skills-coexistence"; + + nodes.machine = { ... }: { + imports = [ self.nixosModules.hermes-agent ]; + + services.hermes-agent = { + enable = true; + package = self.packages.${system}.hermes-agent; + skills = { + bundled.enable = false; + custom.repo-watch = { + category = "research"; + source = testSkill; + }; + }; + documents = { + "SOUL.md" = "# SOUL.md\nTest soul\n"; + "AGENTS.md" = "# AGENTS.md\nTest agents\n"; + "USER.md" = "# USER.md\nTest user\n"; + }; + config = { + toolsets = [ "all" ]; + model = { + default = "moonshotai/kimi-k2.5"; + provider = "openrouter"; + }; + }; + }; + + system.stateVersion = "25.05"; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -f /var/lib/hermes/.hermes/skills/research/repo-watch/SKILL.md") + machine.succeed("grep -F 'research/repo-watch' /var/lib/hermes/.hermes/skills/.nix-managed-skills.json") + + machine.succeed("mkdir -p /var/lib/hermes/.hermes/skills/manual-test") + machine.succeed("cat > /var/lib/hermes/.hermes/skills/manual-test/SKILL.md <<'EOF'\n---\nname: manual-test\ndescription: unmanaged test skill\n---\n\n# manual-test\nEOF") + machine.succeed("chown -R hermes:hermes /var/lib/hermes/.hermes/skills/manual-test") + + machine.succeed("/run/current-system/activate") + + machine.succeed("test -f /var/lib/hermes/.hermes/skills/research/repo-watch/SKILL.md") + machine.succeed("test -f /var/lib/hermes/.hermes/skills/manual-test/SKILL.md") + ''; +}