{ description = "AI agent skills for Claude Code and OpenCode"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, flake-utils }: let # Home Manager module for deploying skills skillsModule = import ./modules/ai-skills.nix; # List of available skills availableSkills = [ "bd-issue-tracking" "code-review" "doc-review" "niri-window-capture" "ops-review" "orch" "screenshot-latest" "spec-review" "tufte-press" "worklog" "update-spec-kit" "update-opencode" "web-search" "web-research" ]; in flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; in { # Development shell for working on skills devShells.default = pkgs.mkShell { name = "ai-skills"; packages = with pkgs; [ bash shellcheck jq ]; shellHook = '' echo "🤖 AI Skills development environment" echo "Available skills: ${builtins.concatStringsSep ", " availableSkills}" echo "" echo "Commands:" echo " ./bin/deploy-skill.sh - Copy skill to dotfiles" echo " bash -n skills/*/scripts/*.sh - Validate all scripts" ''; }; # Package individual skills for deployment packages = let # Filter to only skills that exist existingSkills = builtins.filter (name: builtins.pathExists (./skills + "/${name}")) availableSkills; individualSkills = builtins.listToAttrs ( map (skillName: { name = skillName; value = pkgs.stdenv.mkDerivation { name = "ai-skill-${skillName}"; src = ./skills + "/${skillName}"; installPhase = '' mkdir -p $out cp -r . $out/ # Make scripts executable if [ -d $out/scripts ]; then chmod +x $out/scripts/*.sh 2>/dev/null || true fi ''; }; }) existingSkills ); in individualSkills // { # All skills as a combined package all-skills = pkgs.symlinkJoin { name = "all-ai-skills"; paths = builtins.attrValues individualSkills; }; }; }) // { # Export the Home Manager module homeManagerModules.ai-skills = skillsModule; # Also export as nixosModules for compatibility nixosModules.ai-skills = skillsModule; # Export skills paths for direct use lib = { inherit availableSkills; # Helper to get skill path getSkillPath = skillName: ./skills/${skillName}; # Helper to get all skill paths getAllSkillPaths = map (name: ./skills/${name}) availableSkills; # API Keys - Single Source of Truth # Used by both direnv stdlib and sops-nix configuration apiKeys = [ "openai" "google" "anthropic" "openrouter" ]; # Generate direnv stdlib use_api_keys function mkDirenvStdlib = keys: let toEnvVar = key: { "openai" = "OPENAI_API_KEY"; "google" = "GEMINI_API_KEY"; "anthropic" = "ANTHROPIC_API_KEY"; "openrouter" = "OPENROUTER_API_KEY"; }.${key} or (builtins.throw "Unknown API key: ${key}"); # Google key exports both GEMINI_API_KEY and GOOGLE_API_KEY mkExport = key: if key == "google" then '' [ -f /run/secrets/api_keys/${key} ] && export ${toEnvVar key}="$(cat /run/secrets/api_keys/${key})" && export GOOGLE_API_KEY="$GEMINI_API_KEY"'' else '' [ -f /run/secrets/api_keys/${key} ] && export ${toEnvVar key}="$(cat /run/secrets/api_keys/${key})"''; in '' use_api_keys() { ${builtins.concatStringsSep "\n " (map mkExport keys)} } ''; # Generate sops-nix secrets attribute set mkSopsSecrets = { keys, owner, group ? "users" }: builtins.listToAttrs (map (key: { name = "api_keys/${key}"; value = { mode = "0600"; inherit owner group; }; }) keys); }; }; }