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.
49 lines
4.6 KiB
Markdown
49 lines
4.6 KiB
Markdown
---
|
|
spec: nix-skill-packaging
|
|
blocked-by: 0001-content-tier-skill-packaging
|
|
---
|
|
|
|
## What to build
|
|
|
|
The global (every-project) placement output: a home-manager module that installs an operator's selected skills into `~/.claude/skills/`, plus the check that proves its composition.
|
|
|
|
Exposed as `homeModules.default`. The operator-facing option is `programs.agents.skills`, a `listOf package` with default `[]` (an empty list is a no-op). `agents` is deliberately an umbrella namespace with no shared `programs.agents.enable` — each sub-feature self-gates so the namespace stays a clean, mergeable surface another repo could extend. Selection is by derivation: consumers pass skill derivations pulled from `packages.<system>`, not name strings.
|
|
|
|
For each selected skill the module writes an individual `home.file` at `${claude-code.configDir}/skills/<name>` with `source = <skill derivation>` and `recursive = true`, gated on `programs.claude-code.enable`, reading `configDir` from the claude-code module. `recursive = true` is a hard requirement: it materializes `.../skills/<name>/` as a real directory of per-file symlinks so this module, the operator's own skills declarations, and self-placing tool modules coexist under one `skills/` tree. The module deliberately does not feed the single-valued `programs.claude-code.skills` option, which would collide with an operator already setting it.
|
|
|
|
Verified by the home-manager-module composition check under `nix flake check`: it instantiates `homeModules.default` under a sample home-manager configuration selecting a couple of skills with `programs.claude-code` enabled, builds the resulting home-files derivation, and asserts the per-skill `home.file`, `recursive = true`, the `claude-code.enable` gate, and `configDir` sourcing all compose as intended (direct analogue of gitea-axi's home-manager-module check).
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] `homeModules.default` is exposed.
|
|
- [x] It defines `programs.agents.skills` as `listOf package` with default `[]`, and an empty list installs nothing.
|
|
- [x] There is no shared `programs.agents.enable`; the skills feature self-gates.
|
|
- [x] Selection is by derivation (skills pulled from `packages.<system>`), not by name string.
|
|
- [x] Each selected skill is written as an individual `home.file` at `${claude-code.configDir}/skills/<name>` with `recursive = true`, sourcing `configDir` from the claude-code module.
|
|
- [x] Placement is gated on `programs.claude-code.enable`; with it off, no skill files are written.
|
|
- [x] The module does not set `programs.claude-code.skills`.
|
|
- [x] The module composes with an operator's own skills declarations and self-placing tool modules under one `skills/` tree without collision.
|
|
- [x] `nix flake check` includes a home-manager-module composition check that builds the home-files derivation for a sample selection and asserts the above.
|
|
|
|
## Implementation Notes
|
|
|
|
Files: `home-manager-module.nix` (the module, at the repo root, mirroring gitea-axi's placement), `checks/home-manager-module.nix` (the composition check), and `flake.nix` wiring (a `homeModules` output plus the check registration and the new `home-manager` input).
|
|
|
|
- **Placement name comes from `skillName`.**
|
|
The module reads each derivation's `passthru.skillName` — the eval-time name attribute task 0001 established for exactly this purpose — to form `.../skills/<name>` without import-from-derivation.
|
|
|
|
- **`homeModules` exposes an `agents-skills` alias beside `default`.**
|
|
The criteria only require `homeModules.default`; the named alias is an additive convenience mirroring gitea-axi's `homeModules` shape, and `default` points at it.
|
|
|
|
- **`home-manager` flake input follows this flake's nixpkgs.**
|
|
It exists solely so `nix flake check` can evaluate the module against real home-manager; a consumer importing the module supplies their own home-manager and pkgs, so the input has no bearing on what they get.
|
|
|
|
- **Recursive placement is proven by the entry's type, not by file existence.**
|
|
A `test -f skills/<name>/SKILL.md` follows symlinks and cannot distinguish a recursive per-file tree from one opaque symlink over the whole skill, so the check asserts the entry is a real directory (`test -d` and `! -L`).
|
|
A mutation to `recursive = false` fails the check.
|
|
|
|
- **A non-default `configDir` scenario was added during review.**
|
|
The other scenarios all run at the default `configDir`, so a module hardcoding `.claude/skills/` would have passed them identically.
|
|
One configuration now sets a custom `configDir` and asserts placement follows it, closing the "configDir sourcing" leg of the check.
|
|
A mutation hardcoding `.claude` fails the check.
|