{ lib, defaultPackages }: let safeName = name: builtins.match "[A-Za-z0-9][A-Za-z0-9._-]*" name != null; # Package a skill directory (containing SKILL.md). # runtimeInputs: packages put on PATH of every launcher that enables the skill # skills: other mkSkill packages this skill depends on mkSkill = { pkgs, name, src, runtimeInputs ? [ ], skills ? [ ], }: assert lib.assertMsg (safeName name) "mkSkill: unsafe skill name '${name}'"; pkgs.runCommand "skill-${name}" { passthru = { skillName = name; inherit runtimeInputs; skillDeps = skills; }; } '' if [ ! -f ${src}/SKILL.md ]; then echo "mkSkill: ${name}: SKILL.md is missing in ${src}" >&2 exit 1 fi cp -r ${src} $out chmod -R u+w $out ''; in { inherit safeName mkSkill; # Evaluate an environment from a list of modules. mkEnvironment = { pkgs, modules ? [ ], }: let eval = lib.evalModules { modules = [ ../modules/core.nix ../modules/claude.nix ../modules/codex.nix ../modules/launcher.nix ../modules/companions.nix { _module.args = { inherit pkgs; agentEnvLib = { inherit safeName mkSkill; }; }; claude.package = lib.mkDefault (defaultPackages pkgs.stdenv.hostPlatform.system).claude; codex.package = lib.mkDefault (defaultPackages pkgs.stdenv.hostPlatform.system).codex; } ] ++ modules; }; failed = map (a: a.message) (lib.filter (a: !a.assertion) eval.config.assertions); checked = if failed == [ ] then eval.config else throw "agent-environments:\n${lib.concatMapStringsSep "\n" (m: "- ${m}") failed}"; in { config = checked; inherit (checked.build) package launchers; }; # A module declaring `modules.skills..enable` for a packaged skill. mkSkillModule = skill: { config, lib, ... }: { options.modules.skills.${skill.skillName}.enable = lib.mkEnableOption "the ${skill.skillName} skill"; config = lib.mkIf config.modules.skills.${skill.skillName}.enable { skills.packages = [ skill ]; }; }; }