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:
- 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}
The state version in this release includes the changes below. These
changes are only active if the `home.stateVersion` option is set to
\"26.05\" or later.
- The `gtk.gtk4.theme` option does not mirror `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-gtk.gtk4.theme) option does not mirror
[](#opt-gtk.theme) by default anymore.
- 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 {
type = with lib.types; nullOr (numbers.between 0.0 1.0);
type = with lib.types; nullOr (numbers.between 1.0 2.0);
default = null;
example = 1.0;
description = "User interface scale.";

View File

@@ -133,7 +133,7 @@ in
The attribute name becomes the command filename, and the value is either:
- Inline content as a string
- 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 ''
{
@@ -162,7 +162,7 @@ in
The attribute name becomes the agent filename, and the value is either:
- Inline content as a string
- 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 ''
{
@@ -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 {
type = lib.types.attrsOf (lib.types.either jsonFormat.type lib.types.path);
default = { };
@@ -199,6 +242,13 @@ in
};
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 ];
xdg.configFile = {
@@ -227,6 +277,11 @@ in
text = cfg.rules;
})
);
"opencode/skill" = mkIf (lib.isPath cfg.skills) {
source = cfg.skills;
recursive = true;
};
}
// lib.mapAttrs' (
name: content:
@@ -240,6 +295,18 @@ in
if lib.isPath content then { source = content; } else { text = content; }
)
) 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' (
name: content:
lib.nameValuePair "opencode/themes/${name}.json" (

View File

@@ -133,27 +133,22 @@ in
settings = lib.mkOption {
inherit (jsonFormat) type;
default = { };
description = "Settings written as JSON to `~/.config/vicinae/settings.json.";
example = lib.literalExpression ''
{
faviconService = "twenty";
font = {
size = 10;
};
popToRootOnClose = false;
rootSearch = {
searchFiles = false;
};
favicon_service = "twenty";
font.normal.size = 10;
pop_to_root_on_close=false;
search_files_in_root= false;
theme = {
name = "vicinae-dark";
};
window = {
csd = true;
opacity = 0.95;
rounding = 10;
dark.name = "vicinae-dark";
light.name = "vicinae-light";
};
}
'';
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;
renderSettings = lib.mapAttrsToList (
configAtom =
with types;
oneOf [
bool
int
str
];
renderSingleOption =
name: value:
if lib.isBool value then
if value then "--${name}" else "--no-${name}"
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
@@ -27,13 +42,7 @@ in
package = lib.mkPackageOption pkgs "yt-dlp" { };
settings = mkOption {
type =
with types;
attrsOf (oneOf [
bool
int
str
]);
type = with types; attrsOf (either configAtom (listOf configAtom));
default = { };
example = lib.literalExpression ''
{
@@ -42,6 +51,10 @@ in
sub-langs = "all";
downloader = "aria2c";
downloader-args = "aria2c:'-c -x8 -s8 -k1M'";
color = [
"stdout:no_color"
"stderr:always"
];
}
'';
description = ''

View File

@@ -112,7 +112,7 @@ in
else
config.home.homeDirectory
'';
example = "`\${config.xdg.configHome}/zsh`";
example = literalExpression ''"''${config.xdg.configHome}/zsh"'';
description = ''
Directory where the zsh configuration and more should be located,
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;
default = false;
example = !default;
example = true;
description = ''
Whether to configure {command}`emacsclient` as the default
editor using the {env}`EDITOR` and {env}`VISUAL`

View File

@@ -9,6 +9,10 @@
opencode-agents-path = ./agents-path.nix;
opencode-commands-path = ./commands-path.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-path = ./themes-path.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.optionalAttrs pkgs.stdenv.hostPlatform.isLinux {
yt-dlp-simple-config = ./yt-dlp-simple-config.nix;
yt-dlp-extraConfig = ./yt-dlp-extraConfig.nix;
yt-dlp-config = ./yt-dlp-config.nix;
}

View File

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

View File

@@ -8,6 +8,10 @@
downloader = "aria2c";
downloader-args = "aria2c:'-c -x8 -s8 -k1M'";
trim-filenames = 30;
color = [
"stdout:no_color"
"stderr:always"
];
};
extraConfig = ''
--config-locations /home/user/.yt-dlp.conf
@@ -16,6 +20,6 @@
nmt.script = ''
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}
'';
}