self: { config, lib, pkgs, ... }: let cfg = config.programs.skills; lock = lib.importJSON cfg.lockFile; fetch = src: builtins.fetchTree ( { type = "git"; inherit (src) url rev; allRefs = true; } # With a narHash Nix reuses what `skills` already put in the store, so # private sources work even when root evaluates this without SSH keys. // lib.optionalAttrs (src ? narHash) { inherit (src) narHash; } ); skills = lib.concatMapAttrs ( _: src: let tree = fetch src; in lib.mapAttrs (_: path: "${tree}/${path}") src.skills ) lock.sources; linkInto = dir: lib.mapAttrs' (name: path: lib.nameValuePair "${dir}/${name}" { source = path; }) skills; in { options.programs.skills = { enable = lib.mkEnableOption "agent skills from a skills lock file"; package = lib.mkOption { type = lib.types.package; default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; defaultText = lib.literalExpression "skills.packages.\${system}.default"; description = "The skills CLI."; }; lockFile = lib.mkOption { type = lib.types.path; example = lib.literalExpression "./skills.lock"; description = "The skills.lock whose skills to install."; }; manifest = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; example = "/home/me/dotfiles/skills.json"; description = '' Absolute path of the writable skills.json next to {option}`lockFile`, so that `skills add` and `skills update` edit your configuration. ''; }; agents = lib.mkOption { type = lib.types.listOf ( lib.types.enum [ "claude" "codex" "opencode" ] ); default = [ "claude" ]; description = "Agents to install the skills into."; }; }; config = lib.mkIf cfg.enable { assertions = [ { assertion = lock.version or null == 1; message = "programs.skills.lockFile has an unsupported version; upgrade skills."; } ]; home.packages = [ cfg.package ]; home.sessionVariables = { SKILLS_MANAGED_BY = "home-manager"; } // lib.optionalAttrs (cfg.manifest != null) { SKILLS_MANIFEST = cfg.manifest; }; home.file = lib.mkMerge [ (lib.mkIf (lib.elem "claude" cfg.agents) (linkInto ".claude/skills")) (lib.mkIf (lib.elem "codex" cfg.agents) (linkInto ".codex/skills")) ]; xdg.configFile = lib.mkIf (lib.elem "opencode" cfg.agents) (linkInto "opencode/skills"); }; }