skills/flake.nix
dan fb882a9434 feat: add ops-review skill with Phase 1 lenses
Multi-lens review skill for operational infrastructure (Nix, shell,
Docker, CI/CD). Modeled on code-review with linter-first hybrid
architecture.

Phase 1 lenses (core safety):
- secrets: credential exposure, Nix store, Docker layers, CI masking
- shell-safety: shellcheck-backed, temp files, guard snippets
- blast-radius: targeting/scoping, dry-run, rollback
- privilege: least-privilege, containers, systemd sandboxing

Design reviewed via orch consensus (sonar, flash-or, gemini, gpt).
Lenses deploy to ~/.config/lenses/ops/ via home-manager.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 17:36:24 -08:00

148 lines
4.8 KiB
Nix

{
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 <name> - 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);
};
};
}