feat: package skills as per-skill Nix derivations (task 0001)
Add the content tier: a standalone flake that auto-discovers each skill (a directory containing a SKILL.md, at any depth) and exposes it as an individually addressable derivation built by lib.mkSkill. Directories without a SKILL.md are descended through as cosmetic containers; once a SKILL.md is found, that directory's subfolders are its assets, not further skills. Skill names must be globally unique across the tree — a collision is a hard eval-time error, not a warning. A fixture-driven skill-build check under `nix flake check` exercises the recursive walk, the builder, the SKILL.md-at-$out-root contract, the eval-time name passthru, and the collision error. The repo ships no real skill content yet, so packages.<system> is empty today.
This commit was merged in pull request #1.
This commit is contained in:
1
checks/fixtures/collision/first/dup/SKILL.md
Normal file
1
checks/fixtures/collision/first/dup/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# dup (first)
|
||||
1
checks/fixtures/collision/second/dup/SKILL.md
Normal file
1
checks/fixtures/collision/second/dup/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# dup (second)
|
||||
1
checks/fixtures/valid/alpha/SKILL.md
Normal file
1
checks/fixtures/valid/alpha/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# alpha — a top-level skill
|
||||
1
checks/fixtures/valid/delta/SKILL.md
Normal file
1
checks/fixtures/valid/delta/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# delta — another top-level skill
|
||||
1
checks/fixtures/valid/group/beta/SKILL.md
Normal file
1
checks/fixtures/valid/group/beta/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# beta — nested under a cosmetic container
|
||||
1
checks/fixtures/valid/group/beta/tools/SKILL.md
Normal file
1
checks/fixtures/valid/group/beta/tools/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# not a skill — beta asset that happens to contain SKILL.md
|
||||
1
checks/fixtures/valid/one/two/three/gamma/SKILL.md
Normal file
1
checks/fixtures/valid/one/two/three/gamma/SKILL.md
Normal file
@@ -0,0 +1 @@
|
||||
# gamma — several containers deep
|
||||
56
checks/skill-build.nix
Normal file
56
checks/skill-build.nix
Normal file
@@ -0,0 +1,56 @@
|
||||
# The skill-build check: drives fixture skills through the content tier so
|
||||
# `nix flake check` covers every discovery shape and the collision hard-error.
|
||||
{
|
||||
pkgs,
|
||||
mkSkill,
|
||||
discoverSkills,
|
||||
}:
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
|
||||
discovered = discoverSkills ./fixtures/valid;
|
||||
built = map (skill: mkSkill { inherit pkgs; inherit (skill) name src; }) discovered;
|
||||
|
||||
collision = builtins.tryEval (discoverSkills ./fixtures/collision);
|
||||
|
||||
discoveredNames = lib.sort (a: b: a < b) (map (skill: skill.name) discovered);
|
||||
in
|
||||
pkgs.runCommandLocal "skill-build-check"
|
||||
{
|
||||
# Referencing every built skill forces each one to build before this
|
||||
# derivation — that is the "build every auto-discovered skill" coverage.
|
||||
# Each entry is "name=storepath" so the assertions can read each $out root
|
||||
# without re-deriving anything, and confirm the name passthru round-trips.
|
||||
pairs = lib.concatStringsSep " " (map (skill: "${skill.skillName}=${skill}") built);
|
||||
collisionSucceeded = if collision.success then "yes" else "no";
|
||||
expectedNames = lib.concatStringsSep " " discoveredNames;
|
||||
}
|
||||
''
|
||||
fail() { echo "FAIL: $1" >&2; exit 1; }
|
||||
|
||||
echo "content tier: the name-collision fixture must fail discovery at eval"
|
||||
[ "$collisionSucceeded" = no ] \
|
||||
|| fail "a two-skills-one-name tree was discovered without error"
|
||||
|
||||
echo "content tier: discovery finds every skill regardless of nesting depth,"
|
||||
echo " and stops at a skill rather than descending into its assets"
|
||||
[ "$expectedNames" = "alpha beta delta gamma" ] \
|
||||
|| fail "discovered names were [$expectedNames], expected [alpha beta delta gamma]"
|
||||
|
||||
echo "content tier: each built skill has SKILL.md at its \$out root"
|
||||
beta_path=
|
||||
for pair in $pairs; do
|
||||
name="''${pair%%=*}"
|
||||
path="''${pair#*=}"
|
||||
test -f "$path/SKILL.md" || fail "$name has no SKILL.md at its \$out root"
|
||||
[ "$name" = beta ] && beta_path="$path"
|
||||
done
|
||||
|
||||
echo "content tier: a skill's asset subfolder rides along in \$out and is"
|
||||
echo " not split off into its own skill"
|
||||
[ -n "$beta_path" ] || fail "beta was not discovered"
|
||||
test -f "$beta_path/tools/SKILL.md" \
|
||||
|| fail "beta's asset subfolder did not ride along into \$out"
|
||||
|
||||
touch "$out"
|
||||
''
|
||||
Reference in New Issue
Block a user