Expose homeModules.default, a home-manager module whose programs.agents.skills option places operator-selected skill derivations into Claude Code's skills directory, one recursive home.file per skill, gated on programs.claude-code.enable and sourcing configDir from the claude-code module. Add a nix flake check that builds the home-files tree under several operator configurations and asserts placement, recursion, the enable gate, configDir sourcing, and coexistence with an operator's own skills.
64 lines
2.4 KiB
Nix
64 lines
2.4 KiB
Nix
# Places operator-selected skills into Claude Code's skills directory, one per
|
|
# skill, for every project.
|
|
{
|
|
config,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.programs.agents;
|
|
claudeCode = config.programs.claude-code;
|
|
in
|
|
{
|
|
options.programs.agents.skills = lib.mkOption {
|
|
type = lib.types.listOf lib.types.package;
|
|
default = [ ];
|
|
example = lib.literalExpression "[ inputs.skills.packages.\${system}.some-skill ]";
|
|
description = ''
|
|
Skills to install globally, into the `skills/` subdirectory of
|
|
{option}`programs.claude-code.configDir`, so they are active in every
|
|
project.
|
|
|
|
Selection is by derivation: pass skill derivations from the skills flake's
|
|
`packages.<system>`, each of which carries its placement name as an
|
|
eval-time attribute. An empty list installs nothing.
|
|
|
|
Every skill is written as its own recursive {option}`home.file`, so this
|
|
option composes with an operator's own {option}`programs.claude-code.skills`
|
|
and with other modules placing skills under the same directory rather than
|
|
claiming the tree. Placement is gated on
|
|
{option}`programs.claude-code.enable`.
|
|
'';
|
|
};
|
|
|
|
# `agents` is an umbrella namespace with no shared enable, so the skills
|
|
# feature self-gates.
|
|
# An empty list writes nothing, and files land only when Claude Code is
|
|
# enabled.
|
|
# Reading each skill's source realises its derivation, so gating on
|
|
# `claude-code.enable` also keeps that realisation off a host that installs
|
|
# nothing.
|
|
config = lib.mkIf claudeCode.enable {
|
|
# `configDir` comes from the Claude Code module, so skills land beside its
|
|
# own wherever the operator points it.
|
|
# Each name is the derivation's eval-time `skillName`, so placement reads no
|
|
# `$out` and needs no import-from-derivation.
|
|
# `recursive = true` materialises `skills/<name>/` as a directory of per-file
|
|
# symlinks, which is what lets this module, the operator's own skills, and
|
|
# self-placing tool modules share one `skills/` tree.
|
|
#
|
|
# This deliberately does not feed the single-valued
|
|
# `programs.claude-code.skills`, which would collide with an operator already
|
|
# setting it.
|
|
home.file = lib.listToAttrs (
|
|
map (skill: {
|
|
name = "${claudeCode.configDir}/skills/${skill.skillName}";
|
|
value = {
|
|
source = skill;
|
|
recursive = true;
|
|
};
|
|
}) cfg.skills
|
|
);
|
|
};
|
|
}
|