skills/modules/ai-skills.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

114 lines
3.2 KiB
Nix

{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.ai-skills;
# Derive repo root from skillsPath (skills/ is a subdirectory)
repoRoot = dirOf cfg.skillsPath;
skillsList = ''
Available skills:
- code-review: Multi-lens code review with issue filing
- niri-window-capture: Invisibly capture window screenshots
- ops-review: Multi-lens ops/infrastructure review
- screenshot-latest: Find latest screenshots
- tufte-press: Generate study card JSON
- worklog: Create org-mode worklogs
- update-spec-kit: Update spec-kit ecosystem
- update-opencode: Update OpenCode via Nix
- web-search: Search the web via Claude
- web-research: Deep web research with multiple backends
'';
in {
options.services.ai-skills = {
enable = mkEnableOption "AI agent skills for Claude Code and OpenCode";
skillsPath = mkOption {
type = types.path;
default = null;
description = "Path to skills repository (e.g., ~/proj/skills/skills)";
};
# Per-target skill lists
claudeCodeSkills = mkOption {
type = types.listOf types.str;
default = [];
description = "Skills to deploy to Claude Code (~/.claude/skills/). ${skillsList}";
example = [ "worklog" "niri-window-capture" ];
};
openCodeSkills = mkOption {
type = types.listOf types.str;
default = [];
description = "Skills to deploy to OpenCode (~/.config/opencode/skills/). ${skillsList}";
example = [ "worklog" "web-search" ];
};
# Lenses for orch multi-model review
enableLenses = mkOption {
type = types.bool;
default = true;
description = "Deploy review lenses to ~/.config/lenses/";
};
# Workflows (beads protos)
enableWorkflows = mkOption {
type = types.bool;
default = true;
description = "Deploy workflow protos to ~/.beads/molecules.jsonl";
};
};
config = mkIf cfg.enable {
home.file = mkMerge [
# Claude Code skills
(mkIf (cfg.claudeCodeSkills != []) (
builtins.listToAttrs (
map (skillName: {
name = ".claude/skills/${skillName}";
value = {
source = "${cfg.skillsPath}/${skillName}";
recursive = true;
};
}) cfg.claudeCodeSkills
)
))
# OpenCode skills
(mkIf (cfg.openCodeSkills != []) (
builtins.listToAttrs (
map (skillName: {
name = ".config/opencode/skills/${skillName}";
value = {
source = "${cfg.skillsPath}/${skillName}";
recursive = true;
};
}) cfg.openCodeSkills
)
))
# Lenses for orch (sourced from code-review skill)
(mkIf cfg.enableLenses {
".config/lenses" = {
source = "${cfg.skillsPath}/code-review/lenses";
recursive = true;
};
# Ops lenses in separate subdirectory
".config/lenses/ops" = {
source = "${cfg.skillsPath}/ops-review/lenses";
recursive = true;
};
})
# Workflows (beads protos)
(mkIf cfg.enableWorkflows {
".beads/molecules.jsonl" = {
source = "${repoRoot}/workflows/molecules.jsonl";
};
})
];
};
}