From 63ecc7d495f06dcc429d73479b3596c35c5bf916 Mon Sep 17 00:00:00 2001 From: Mirza Arnaut Date: Sun, 25 Jan 2026 01:01:32 +0100 Subject: [PATCH] opencode: handle string store paths in skill sources Add support for store directory paths provided as strings, in addition to the existing path type check for directories. Context: certain project will include a `SKILL.md` file, and instead of managing them via some 3rd party manager or manually, why not include them from the source itself! I implemented this in my setup, since I wanted to include the `SKILL.md` file from the [beads](https://github.com/steveyegge/beads) project. --- modules/programs/opencode.nix | 20 ++++++++++++++-- tests/modules/programs/opencode/default.nix | 1 + .../programs/opencode/skills-store-path.nix | 24 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/modules/programs/opencode/skills-store-path.nix diff --git a/modules/programs/opencode.nix b/modules/programs/opencode.nix index fec747a1..1cbbe2d6 100644 --- a/modules/programs/opencode.nix +++ b/modules/programs/opencode.nix @@ -230,7 +230,13 @@ in }; skills = lib.mkOption { - type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path; + type = lib.types.either (lib.types.attrsOf ( + lib.types.oneOf [ + lib.types.lines + lib.types.path + lib.types.str + ] + )) lib.types.path; default = { }; description = '' Custom agent skills for opencode. @@ -245,6 +251,9 @@ in - A path to a file (creates `opencode/skill//SKILL.md`) - A path to a directory (creates `opencode/skill//` with all files) + This also accepts Nix store paths (e.g., source from a package), allowing you to + reference subdirectories within a package source. + If a path is used, it is expected to contain one folder per skill name, each containing a {file}`SKILL.md`. The directory is symlinked to {file}`$XDG_CONFIG_HOME/opencode/skill/`. @@ -268,6 +277,9 @@ in # A skill can also be a directory containing SKILL.md and other files. data-analysis = ./skills/data-analysis; + + # A skill can also be a subdirectory within a package source (store path) + beads = "''${pkgs.beads.src}/claude-plugin/skills/beads"; } ''; }; @@ -443,7 +455,11 @@ in ) // lib.mapAttrs' ( name: content: - if lib.isPath content && lib.pathIsDirectory content then + if + (lib.isPath content && lib.pathIsDirectory content) + || (builtins.isString content && lib.hasPrefix builtins.storeDir content) + + then lib.nameValuePair "opencode/skill/${name}" { source = content; recursive = true; diff --git a/tests/modules/programs/opencode/default.nix b/tests/modules/programs/opencode/default.nix index 99193913..22be9e6d 100644 --- a/tests/modules/programs/opencode/default.nix +++ b/tests/modules/programs/opencode/default.nix @@ -16,6 +16,7 @@ opencode-mixed-content = ./mixed-content.nix; opencode-skills-inline = ./skills-inline.nix; opencode-skills-path = ./skills-path.nix; + opencode-skills-store-path = ./skills-store-path.nix; opencode-skills-directory = ./skills-directory.nix; opencode-skills-bulk-directory = ./skills-bulk-directory.nix; opencode-themes-inline = ./themes-inline.nix; diff --git a/tests/modules/programs/opencode/skills-store-path.nix b/tests/modules/programs/opencode/skills-store-path.nix new file mode 100644 index 00000000..bb66aeb5 --- /dev/null +++ b/tests/modules/programs/opencode/skills-store-path.nix @@ -0,0 +1,24 @@ +{ pkgs, ... }: + +let + src = pkgs.writeTextDir "skills/external-skill/SKILL.md" '' + # Mock Skill + This content simulates a skill living inside a package source. + ''; +in +{ + programs.opencode = { + enable = true; + skills = { + # We reference the specific subfolder inside the store path + internal-skill = "${src}/skills/external-skill"; + }; + }; + + nmt.script = '' + assertFileExists home-files/.config/opencode/skill/internal-skill/SKILL.md + + assertFileContent home-files/.config/opencode/skill/internal-skill/SKILL.md \ + "${src}/skills/external-skill/SKILL.md" + ''; +}