diff --git a/modules/programs/opencode.nix b/modules/programs/opencode.nix index 914ff442c2..71eaceac14 100644 --- a/modules/programs/opencode.nix +++ b/modules/programs/opencode.nix @@ -73,6 +73,34 @@ let else cfg.package; + normalizeDirectory = + name: source: + if lib.isPath source then + source + else + pkgs.runCommandLocal name { } '' + if [[ ! -d ${lib.escapeShellArg (toString source)} ]]; then + echo ${lib.escapeShellArg "programs.opencode.skills must be a directory"} >&2 + exit 1 + fi + ln -s ${lib.escapeShellArg (toString source)} "$out" + ''; + + normalizeSkill = + source: + pkgs.runCommandLocal "opencode-skill" { } '' + source=${lib.escapeShellArg (toString source)} + if [[ -d "$source" ]]; then + ln -s "$source" "$out" + elif [[ -f "$source" ]]; then + mkdir "$out" + ln -s "$source" "$out/SKILL.md" + else + echo "OpenCode skill source must be a file or directory: $source" >&2 + exit 1 + fi + ''; + in { meta.maintainers = with lib.maintainers; [ delafthi ]; @@ -452,7 +480,7 @@ in message = "`programs.opencode.tools` must be a directory when set to a path"; } { - assertion = !lib.hm.strings.isPathLike cfg.skills || lib.pathIsDirectory cfg.skills; + assertion = !lib.isPath cfg.skills || lib.pathIsDirectory cfg.skills; message = "`programs.opencode.skills` must be a directory when set to a path"; } { @@ -540,7 +568,7 @@ in }; "opencode/skills" = mkIf (lib.hm.strings.isPathLike cfg.skills) { - source = cfg.skills; + source = normalizeDirectory "opencode-skills" cfg.skills; recursive = true; }; @@ -575,11 +603,16 @@ in ) // lib.mapAttrs' ( name: content: - if lib.hm.strings.isPathLike content && lib.pathIsDirectory content then + if lib.isPath content && lib.pathIsDirectory content then lib.nameValuePair "opencode/skills/${name}" { source = content; recursive = true; } + else if lib.hm.strings.isPathLike content && !lib.isPath content then + lib.nameValuePair "opencode/skills/${name}" { + source = normalizeSkill content; + recursive = true; + } else lib.nameValuePair "opencode/skills/${name}/SKILL.md" ( if lib.hm.strings.isPathLike content then { source = content; } else { text = content; } diff --git a/tests/modules/programs/opencode/skills-store-path-dir.nix b/tests/modules/programs/opencode/skills-store-path-dir.nix index 073820a2ac..4263653c42 100644 --- a/tests/modules/programs/opencode/skills-store-path-dir.nix +++ b/tests/modules/programs/opencode/skills-store-path-dir.nix @@ -1,8 +1,9 @@ -{ pkgs, ... }: +{ realPkgs, ... }: let - src = pkgs.writeTextDir "skills/external-skill/SKILL.md" '' - # External Skill + src = realPkgs.runCommand "opencode-ifd-skills-directory" { } '' + mkdir -p "$out/skills/external-skill" + echo '# External Skill' > "$out/skills/external-skill/SKILL.md" ''; in { diff --git a/tests/modules/programs/opencode/skills-store-path.nix b/tests/modules/programs/opencode/skills-store-path.nix index 774ba02273..277b20d833 100644 --- a/tests/modules/programs/opencode/skills-store-path.nix +++ b/tests/modules/programs/opencode/skills-store-path.nix @@ -1,24 +1,24 @@ -{ pkgs, ... }: +{ realPkgs, ... }: let - src = pkgs.writeTextDir "skills/external-skill/SKILL.md" '' - # Mock Skill - This content simulates a skill living inside a package source. + src = realPkgs.runCommand "opencode-ifd-skill-source" { } '' + mkdir -p "$out/skills/external-skill" + echo '# External Skill' > "$out/skills/external-skill/SKILL.md" ''; in { programs.opencode = { enable = true; skills = { - # We reference the specific subfolder inside the store path - internal-skill = "${src}/skills/external-skill"; + directory = "${src}/skills/external-skill"; + file = "${src}/skills/external-skill/SKILL.md"; }; }; nmt.script = '' - assertFileExists home-files/.config/opencode/skills/internal-skill/SKILL.md - - assertFileContent home-files/.config/opencode/skills/internal-skill/SKILL.md \ + assertFileContent home-files/.config/opencode/skills/directory/SKILL.md \ + "${src}/skills/external-skill/SKILL.md" + assertFileContent home-files/.config/opencode/skills/file/SKILL.md \ "${src}/skills/external-skill/SKILL.md" ''; }