Compare commits

...

9 Commits

Author SHA1 Message Date
Tim Kleinschmidt
1cfa305fba opencode: add agent skills support
Adds support for OpenCode Agent Skills by managing skill definitions
under "/opencode/skill/<name>/SKILL.md" via `programs.opencode.skills`.

Documentation: https://opencode.ai/docs/skills/
2026-01-03 11:47:20 +01:00
Tim Kleinschmidt
3e87b442b5 opencode: update docs
.config is already part of $XDG_CONFIG_HOME
2026-01-03 11:47:20 +01:00
leiserfg
99a037de18 vicinae: fix settings example 2026-01-03 11:31:39 +01:00
Robert Helgesson
3b3164dfe3 zsh: avoid escaping showing up in dotDir docs 2026-01-03 11:29:29 +01:00
Robert Helgesson
bc43546503 docs: turn option references into links 2026-01-03 11:29:29 +01:00
Aguirre Matteo
78a8fae57f yt-dlp: remove redundant extraConfig test 2026-01-03 11:23:26 +01:00
Aguirre Matteo
b558d54215 yt-dlp: allow multiple declarations of the same option 2026-01-03 11:23:26 +01:00
Robert Helgesson
9ef24320f1 emacs: remove unnecessary rec 2026-01-03 11:10:00 +01:00
Vladislav
18f9d668aa anki: fix UI scale configuration
Use `numbers.between 1.0 2.0` for the UI scale type to match Anki's
actual behavior where ProfileManager.uiScale clamps values below 1.0
to 1.0.
2026-01-03 10:49:37 +01:00
23 changed files with 236 additions and 53 deletions

View File

@@ -7,15 +7,19 @@ section is therefore not final.
This release has the following notable changes: This release has the following notable changes:
- The [](#opt-programs.anki.uiScale) option now expects a value in the
range 1.02.0, previously it erroneously expected values in the
range `0.01.0`.
## State Version Changes {#sec-release-26.05-state-version-changes} ## State Version Changes {#sec-release-26.05-state-version-changes}
The state version in this release includes the changes below. These The state version in this release includes the changes below. These
changes are only active if the `home.stateVersion` option is set to changes are only active if the `home.stateVersion` option is set to
\"26.05\" or later. \"26.05\" or later.
- The `gtk.gtk4.theme` option does not mirror `gtk.theme` by default - The [](#opt-gtk.gtk4.theme) option does not mirror
anymore. [](#opt-gtk.theme) by default anymore.
- The `programs.zsh.dotDir` option now defaults to the XDG configuration
directory (usually `~/.config/zsh`) when `xdg.enable` is true.
- The [](#opt-programs.zsh.dotDir) option now defaults to the XDG
configuration directory (usually `~/.config/zsh`) when
[](#opt-xdg.enable) is true.

View File

@@ -82,7 +82,7 @@ in
}; };
uiScale = lib.mkOption { uiScale = lib.mkOption {
type = with lib.types; nullOr (numbers.between 0.0 1.0); type = with lib.types; nullOr (numbers.between 1.0 2.0);
default = null; default = null;
example = 1.0; example = 1.0;
description = "User interface scale."; description = "User interface scale.";

View File

@@ -133,7 +133,7 @@ in
The attribute name becomes the command filename, and the value is either: The attribute name becomes the command filename, and the value is either:
- Inline content as a string - Inline content as a string
- A path to a file containing the command content - A path to a file containing the command content
Commands are stored in {file}`$XDG_CONFIG_HOME/.config/opencode/command/` directory. Commands are stored in {file}`$XDG_CONFIG_HOME/opencode/command/` directory.
''; '';
example = lib.literalExpression '' example = lib.literalExpression ''
{ {
@@ -162,7 +162,7 @@ in
The attribute name becomes the agent filename, and the value is either: The attribute name becomes the agent filename, and the value is either:
- Inline content as a string - Inline content as a string
- A path to a file containing the agent content - A path to a file containing the agent content
Agents are stored in {file}`$XDG_CONFIG_HOME/.config/opencode/agent/` directory. Agents are stored in {file}`$XDG_CONFIG_HOME/opencode/agent/` directory.
''; '';
example = lib.literalExpression '' example = lib.literalExpression ''
{ {
@@ -183,6 +183,49 @@ in
''; '';
}; };
skills = lib.mkOption {
type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path;
default = { };
description = ''
Custom agent skills for opencode.
This option can either be:
- An attribute set defining skills
- A path to a directory containing multiple skill folders
If an attribute set is used, the attribute name becomes the skill directory name,
and the value is either:
- Inline content as a string (creates `opencode/skill/<name>/SKILL.md`)
- A path to a file (creates `opencode/skill/<name>/SKILL.md`)
- A path to a directory (creates `opencode/skill/<name>/` with all files)
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/`.
See <https://opencode.ai/docs/skills/> for the documentation.
'';
example = lib.literalExpression ''
{
git-release = '''
---
name: git-release
description: Create consistent releases and changelogs
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command
''';
# A skill can also be a directory containing SKILL.md and other files.
data-analysis = ./skills/data-analysis;
}
'';
};
themes = mkOption { themes = mkOption {
type = lib.types.attrsOf (lib.types.either jsonFormat.type lib.types.path); type = lib.types.attrsOf (lib.types.either jsonFormat.type lib.types.path);
default = { }; default = { };
@@ -199,6 +242,13 @@ in
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [
{
assertion = !lib.isPath cfg.skills || lib.pathIsDirectory cfg.skills;
message = "`programs.opencode.skills` must be a directory when set to a path";
}
];
home.packages = mkIf (cfg.package != null) [ cfg.package ]; home.packages = mkIf (cfg.package != null) [ cfg.package ];
xdg.configFile = { xdg.configFile = {
@@ -227,6 +277,11 @@ in
text = cfg.rules; text = cfg.rules;
}) })
); );
"opencode/skill" = mkIf (lib.isPath cfg.skills) {
source = cfg.skills;
recursive = true;
};
} }
// lib.mapAttrs' ( // lib.mapAttrs' (
name: content: name: content:
@@ -240,6 +295,18 @@ in
if lib.isPath content then { source = content; } else { text = content; } if lib.isPath content then { source = content; } else { text = content; }
) )
) cfg.agents ) cfg.agents
// lib.mapAttrs' (
name: content:
if lib.isPath content && lib.pathIsDirectory content then
lib.nameValuePair "opencode/skill/${name}" {
source = content;
recursive = true;
}
else
lib.nameValuePair "opencode/skill/${name}/SKILL.md" (
if lib.isPath content then { source = content; } else { text = content; }
)
) (if builtins.isAttrs cfg.skills then cfg.skills else { })
// lib.mapAttrs' ( // lib.mapAttrs' (
name: content: name: content:
lib.nameValuePair "opencode/themes/${name}.json" ( lib.nameValuePair "opencode/themes/${name}.json" (

View File

@@ -133,27 +133,22 @@ in
settings = lib.mkOption { settings = lib.mkOption {
inherit (jsonFormat) type; inherit (jsonFormat) type;
default = { }; default = { };
description = "Settings written as JSON to `~/.config/vicinae/settings.json.";
example = lib.literalExpression '' example = lib.literalExpression ''
{ {
faviconService = "twenty"; favicon_service = "twenty";
font = { font.normal.size = 10;
size = 10; pop_to_root_on_close=false;
}; search_files_in_root= false;
popToRootOnClose = false;
rootSearch = {
searchFiles = false;
};
theme = { theme = {
name = "vicinae-dark"; dark.name = "vicinae-dark";
}; light.name = "vicinae-light";
window = {
csd = true;
opacity = 0.95;
rounding = 10;
}; };
} }
''; '';
description = ''
Settings written as JSON to {file}`~/.config/vicinae/settings.json`.
See {command}`vicinae config default`.
'';
}; };
}; };

View File

@@ -9,12 +9,27 @@ let
cfg = config.programs.yt-dlp; cfg = config.programs.yt-dlp;
renderSettings = lib.mapAttrsToList ( configAtom =
with types;
oneOf [
bool
int
str
];
renderSingleOption =
name: value: name: value:
if lib.isBool value then if lib.isBool value then
if value then "--${name}" else "--no-${name}" if value then "--${name}" else "--no-${name}"
else else
"--${name} ${toString value}" "--${name} ${toString value}";
renderSettings = lib.mapAttrsToList (
name: value:
if lib.isList value then
lib.concatStringsSep "\n" (map (renderSingleOption name) value)
else
renderSingleOption name value
); );
in in
@@ -27,13 +42,7 @@ in
package = lib.mkPackageOption pkgs "yt-dlp" { }; package = lib.mkPackageOption pkgs "yt-dlp" { };
settings = mkOption { settings = mkOption {
type = type = with types; attrsOf (either configAtom (listOf configAtom));
with types;
attrsOf (oneOf [
bool
int
str
]);
default = { }; default = { };
example = lib.literalExpression '' example = lib.literalExpression ''
{ {
@@ -42,6 +51,10 @@ in
sub-langs = "all"; sub-langs = "all";
downloader = "aria2c"; downloader = "aria2c";
downloader-args = "aria2c:'-c -x8 -s8 -k1M'"; downloader-args = "aria2c:'-c -x8 -s8 -k1M'";
color = [
"stdout:no_color"
"stderr:always"
];
} }
''; '';
description = '' description = ''

View File

@@ -112,7 +112,7 @@ in
else else
config.home.homeDirectory config.home.homeDirectory
''; '';
example = "`\${config.xdg.configHome}/zsh`"; example = literalExpression ''"''${config.xdg.configHome}/zsh"'';
description = '' description = ''
Directory where the zsh configuration and more should be located, Directory where the zsh configuration and more should be located,
relative to the users home directory. The default is the home relative to the users home directory. The default is the home

View File

@@ -107,10 +107,10 @@ in
''; '';
}; };
defaultEditor = mkOption rec { defaultEditor = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
example = !default; example = true;
description = '' description = ''
Whether to configure {command}`emacsclient` as the default Whether to configure {command}`emacsclient` as the default
editor using the {env}`EDITOR` and {env}`VISUAL` editor using the {env}`EDITOR` and {env}`VISUAL`

View File

@@ -9,6 +9,10 @@
opencode-agents-path = ./agents-path.nix; opencode-agents-path = ./agents-path.nix;
opencode-commands-path = ./commands-path.nix; opencode-commands-path = ./commands-path.nix;
opencode-mixed-content = ./mixed-content.nix; opencode-mixed-content = ./mixed-content.nix;
opencode-skills-inline = ./skills-inline.nix;
opencode-skills-path = ./skills-path.nix;
opencode-skills-directory = ./skills-directory.nix;
opencode-skills-bulk-directory = ./skills-bulk-directory.nix;
opencode-themes-inline = ./themes-inline.nix; opencode-themes-inline = ./themes-inline.nix;
opencode-themes-path = ./themes-path.nix; opencode-themes-path = ./themes-path.nix;
opencode-mcp-integration = ./mcp-integration.nix; opencode-mcp-integration = ./mcp-integration.nix;

View File

@@ -0,0 +1,10 @@
---
name: git-release
description: Create consistent releases and changelogs
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command

View File

@@ -0,0 +1,9 @@
---
name: pdf-processing
description: Extract text and tables from PDF files
---
## What I do
- Extract text from PDFs
- Identify tables and structured data

View File

@@ -0,0 +1,9 @@
---
name: data-analysis
description: Help analyze datasets and results
---
## What I do
- Summarize datasets
- Suggest charts and metrics

View File

@@ -0,0 +1 @@
extra fixture file

View File

@@ -0,0 +1,15 @@
{
programs.opencode = {
enable = true;
skills = ./skills-bulk;
};
nmt.script = ''
assertFileExists home-files/.config/opencode/skill/git-release/SKILL.md
assertFileExists home-files/.config/opencode/skill/pdf-processing/SKILL.md
assertFileContent home-files/.config/opencode/skill/git-release/SKILL.md \
${./skills-bulk/git-release/SKILL.md}
assertFileContent home-files/.config/opencode/skill/pdf-processing/SKILL.md \
${./skills-bulk/pdf-processing/SKILL.md}
'';
}

View File

@@ -0,0 +1,6 @@
---
name: git-release
description: Create consistent releases and changelogs
---
This is the bulk skillsDir fixture for git-release.

View File

@@ -0,0 +1,6 @@
---
name: pdf-processing
description: Extract text and tables from PDF files
---
This is the bulk skillsDir fixture for pdf-processing.

View File

@@ -0,0 +1,15 @@
{
programs.opencode = {
enable = true;
skills = {
data-analysis = ./skill-dir/data-analysis;
};
};
nmt.script = ''
assertFileExists home-files/.config/opencode/skill/data-analysis/SKILL.md
assertFileExists home-files/.config/opencode/skill/data-analysis/notes.txt
assertFileContent home-files/.config/opencode/skill/data-analysis/SKILL.md \
${./skill-dir/data-analysis/SKILL.md}
'';
}

View File

@@ -0,0 +1,25 @@
{
programs.opencode = {
enable = true;
skills = {
git-release = ''
---
name: git-release
description: Create consistent releases and changelogs
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command
'';
};
};
nmt.script = ''
assertFileExists home-files/.config/opencode/skill/git-release/SKILL.md
assertFileContent home-files/.config/opencode/skill/git-release/SKILL.md \
${./git-release-SKILL.md}
'';
}

View File

@@ -0,0 +1,14 @@
{
programs.opencode = {
enable = true;
skills = {
pdf-processing = ./pdf-processing-SKILL.md;
};
};
nmt.script = ''
assertFileExists home-files/.config/opencode/skill/pdf-processing/SKILL.md
assertFileContent home-files/.config/opencode/skill/pdf-processing/SKILL.md \
${./pdf-processing-SKILL.md}
'';
}

View File

@@ -1,5 +1,4 @@
{ lib, pkgs, ... }: { lib, pkgs, ... }:
lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux { lib.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
yt-dlp-simple-config = ./yt-dlp-simple-config.nix; yt-dlp-config = ./yt-dlp-config.nix;
yt-dlp-extraConfig = ./yt-dlp-extraConfig.nix;
} }

View File

@@ -1,3 +1,5 @@
--color stdout:no_color
--color stderr:always
--downloader aria2c --downloader aria2c
--downloader-args aria2c:'-c -x8 -s8 -k1M' --downloader-args aria2c:'-c -x8 -s8 -k1M'
--no-embed-subs --no-embed-subs

View File

@@ -8,6 +8,10 @@
downloader = "aria2c"; downloader = "aria2c";
downloader-args = "aria2c:'-c -x8 -s8 -k1M'"; downloader-args = "aria2c:'-c -x8 -s8 -k1M'";
trim-filenames = 30; trim-filenames = 30;
color = [
"stdout:no_color"
"stderr:always"
];
}; };
extraConfig = '' extraConfig = ''
--config-locations /home/user/.yt-dlp.conf --config-locations /home/user/.yt-dlp.conf
@@ -16,6 +20,6 @@
nmt.script = '' nmt.script = ''
assertFileExists home-files/.config/yt-dlp/config assertFileExists home-files/.config/yt-dlp/config
assertFileContent home-files/.config/yt-dlp/config ${./yt-dlp-simple-config-expected} assertFileContent home-files/.config/yt-dlp/config ${./yt-dlp-config-expected}
''; '';
} }

View File

@@ -1,2 +0,0 @@
--config-locations /home/user/.yt-dlp.conf

View File

@@ -1,13 +0,0 @@
{
programs.yt-dlp = {
enable = true;
extraConfig = ''
--config-locations /home/user/.yt-dlp.conf
'';
};
nmt.script = ''
assertFileExists home-files/.config/yt-dlp/config
assertFileContent home-files/.config/yt-dlp/config ${./yt-dlp-extraConfig-expected}
'';
}