mirror of
https://github.com/nix-community/home-manager.git
synced 2026-01-11 01:19:32 +08:00
opencode: support directory-based configuration for commands, agents, and themes
The `commands`, `agents`, and `themes` options now accept either an attribute set (existing behavior) or a path to a directory containing multiple files. When a directory path is provided, it is symlinked to the appropriate `$XDG_CONFIG_HOME/opencode/` subdirectory. This change aligns with the existing `skills` option implementation and provides a more convenient way to manage multiple configuration files without needing to define each one individually in Nix.
This commit is contained in:
committed by
Austin Horstman
parent
2be878259a
commit
081234b704
@@ -126,14 +126,22 @@ in
|
||||
};
|
||||
|
||||
commands = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path);
|
||||
type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path;
|
||||
default = { };
|
||||
description = ''
|
||||
Custom commands for opencode.
|
||||
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/opencode/command/` directory.
|
||||
|
||||
This option can either be:
|
||||
- An attribute set defining commands
|
||||
- A path to a directory containing multiple command files
|
||||
|
||||
If an attribute set is used, the attribute name becomes the command filename,
|
||||
and the value is either:
|
||||
- Inline content as a string (creates `opencode/command/<name>.md`)
|
||||
- A path to a file (creates `opencode/command/<name>.md`)
|
||||
|
||||
If a path is used, it is expected to contain command files.
|
||||
The directory is symlinked to {file}`$XDG_CONFIG_HOME/opencode/command/`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
@@ -155,14 +163,22 @@ in
|
||||
};
|
||||
|
||||
agents = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path);
|
||||
type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path;
|
||||
default = { };
|
||||
description = ''
|
||||
Custom agents for opencode.
|
||||
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/opencode/agent/` directory.
|
||||
|
||||
This option can either be:
|
||||
- An attribute set defining agents
|
||||
- A path to a directory containing multiple agent files
|
||||
|
||||
If an attribute set is used, the attribute name becomes the agent filename,
|
||||
and the value is either:
|
||||
- Inline content as a string (creates `opencode/agent/<name>.md`)
|
||||
- A path to a file (creates `opencode/agent/<name>.md`)
|
||||
|
||||
If a path is used, it is expected to contain agent files.
|
||||
The directory is symlinked to {file}`$XDG_CONFIG_HOME/opencode/agent/`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
@@ -227,14 +243,23 @@ in
|
||||
};
|
||||
|
||||
themes = mkOption {
|
||||
type = lib.types.attrsOf (lib.types.either jsonFormat.type lib.types.path);
|
||||
type = lib.types.either (lib.types.attrsOf (lib.types.either jsonFormat.type lib.types.path)) lib.types.path;
|
||||
default = { };
|
||||
description = ''
|
||||
Custom themes for opencode. The attribute name becomes the theme
|
||||
filename, and the value is either:
|
||||
- An attribute set, that is converted to a json
|
||||
- A path to a file containing the content
|
||||
Themes are stored in {file}`$XDG_CONFIG_HOME/opencode/themes/` directory.
|
||||
Custom themes for opencode.
|
||||
|
||||
This option can either be:
|
||||
- An attribute set defining themes
|
||||
- A path to a directory containing multiple theme files
|
||||
|
||||
If an attribute set is used, the attribute name becomes the theme filename,
|
||||
and the value is either:
|
||||
- An attribute set that is converted to a JSON file (creates `opencode/themes/<name>.json`)
|
||||
- A path to a file (creates `opencode/themes/<name>.json`)
|
||||
|
||||
If a path is used, it is expected to contain theme files.
|
||||
The directory is symlinked to {file}`$XDG_CONFIG_HOME/opencode/themes/`.
|
||||
|
||||
Set `programs.opencode.settings.theme` to enable the custom theme.
|
||||
See <https://opencode.ai/docs/themes/> for the documentation.
|
||||
'';
|
||||
@@ -243,10 +268,22 @@ in
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = !lib.isPath cfg.commands || lib.pathIsDirectory cfg.commands;
|
||||
message = "`programs.opencode.commands` must be a directory when set to a path";
|
||||
}
|
||||
{
|
||||
assertion = !lib.isPath cfg.agents || lib.pathIsDirectory cfg.agents;
|
||||
message = "`programs.opencode.agents` must be a directory when set to a path";
|
||||
}
|
||||
{
|
||||
assertion = !lib.isPath cfg.skills || lib.pathIsDirectory cfg.skills;
|
||||
message = "`programs.opencode.skills` must be a directory when set to a path";
|
||||
}
|
||||
{
|
||||
assertion = !lib.isPath cfg.themes || lib.pathIsDirectory cfg.themes;
|
||||
message = "`programs.opencode.themes` must be a directory when set to a path";
|
||||
}
|
||||
];
|
||||
|
||||
home.packages = mkIf (cfg.package != null) [ cfg.package ];
|
||||
@@ -278,23 +315,42 @@ in
|
||||
})
|
||||
);
|
||||
|
||||
"opencode/command" = mkIf (lib.isPath cfg.commands) {
|
||||
source = cfg.commands;
|
||||
recursive = true;
|
||||
};
|
||||
|
||||
"opencode/agent" = mkIf (lib.isPath cfg.agents) {
|
||||
source = cfg.agents;
|
||||
recursive = true;
|
||||
};
|
||||
|
||||
"opencode/skill" = mkIf (lib.isPath cfg.skills) {
|
||||
source = cfg.skills;
|
||||
recursive = true;
|
||||
};
|
||||
|
||||
"opencode/themes" = mkIf (lib.isPath cfg.themes) {
|
||||
source = cfg.themes;
|
||||
recursive = true;
|
||||
};
|
||||
}
|
||||
// lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair "opencode/command/${name}.md" (
|
||||
if lib.isPath content then { source = content; } else { text = content; }
|
||||
)
|
||||
) cfg.commands
|
||||
// lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair "opencode/agent/${name}.md" (
|
||||
if lib.isPath content then { source = content; } else { text = content; }
|
||||
)
|
||||
) cfg.agents
|
||||
// lib.optionalAttrs (builtins.isAttrs cfg.commands) (
|
||||
lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair "opencode/command/${name}.md" (
|
||||
if lib.isPath content then { source = content; } else { text = content; }
|
||||
)
|
||||
) cfg.commands
|
||||
)
|
||||
// lib.optionalAttrs (builtins.isAttrs cfg.agents) (
|
||||
lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair "opencode/agent/${name}.md" (
|
||||
if lib.isPath content then { source = content; } else { text = content; }
|
||||
)
|
||||
) cfg.agents
|
||||
)
|
||||
// lib.mapAttrs' (
|
||||
name: content:
|
||||
if lib.isPath content && lib.pathIsDirectory content then
|
||||
@@ -307,23 +363,25 @@ in
|
||||
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" (
|
||||
if lib.isPath content then
|
||||
{
|
||||
source = content;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = jsonFormat.generate "opencode-${name}.json" (
|
||||
{
|
||||
"$schema" = "https://opencode.ai/theme.json";
|
||||
}
|
||||
// content
|
||||
);
|
||||
}
|
||||
)
|
||||
) cfg.themes;
|
||||
// lib.optionalAttrs (builtins.isAttrs cfg.themes) (
|
||||
lib.mapAttrs' (
|
||||
name: content:
|
||||
lib.nameValuePair "opencode/themes/${name}.json" (
|
||||
if lib.isPath content then
|
||||
{
|
||||
source = content;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = jsonFormat.generate "opencode-${name}.json" (
|
||||
{
|
||||
"$schema" = "https://opencode.ai/theme.json";
|
||||
}
|
||||
// content
|
||||
);
|
||||
}
|
||||
)
|
||||
) cfg.themes
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
15
tests/modules/programs/opencode/agents-bulk-directory.nix
Normal file
15
tests/modules/programs/opencode/agents-bulk-directory.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
programs.opencode = {
|
||||
enable = true;
|
||||
agents = ./agents-bulk;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/opencode/agent/code-reviewer.md
|
||||
assertFileExists home-files/.config/opencode/agent/documentation.md
|
||||
assertFileContent home-files/.config/opencode/agent/code-reviewer.md \
|
||||
${./agents-bulk/code-reviewer.md}
|
||||
assertFileContent home-files/.config/opencode/agent/documentation.md \
|
||||
${./agents-bulk/documentation.md}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Code Reviewer Agent
|
||||
|
||||
You are a senior software engineer specializing in code reviews.
|
||||
Focus on code quality, security, and maintainability.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Documentation Agent
|
||||
|
||||
You are a technical writer specializing in creating clear, comprehensive documentation.
|
||||
@@ -12,7 +12,8 @@
|
||||
- Review for potential bugs and edge cases
|
||||
- Check for security vulnerabilities
|
||||
- Ensure code follows best practices
|
||||
- Suggest improvements for readability and performance'';
|
||||
- Suggest improvements for readability and performance
|
||||
'';
|
||||
documentation = ''
|
||||
# Documentation Agent
|
||||
|
||||
@@ -23,7 +24,8 @@
|
||||
- Write clear, concise documentation
|
||||
- Include practical examples
|
||||
- Use proper formatting and structure
|
||||
- Consider the target audience'';
|
||||
- Consider the target audience
|
||||
'';
|
||||
};
|
||||
};
|
||||
nmt.script = ''
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Update Changelog Command
|
||||
|
||||
Update CHANGELOG.md with a new entry for the specified version.
|
||||
Usage: /changelog [version] [change-type] [message]
|
||||
Usage: /changelog [version] [change-type] [message]
|
||||
|
||||
@@ -7,4 +7,4 @@ Focus on code quality, security, and maintainability.
|
||||
- Review for potential bugs and edge cases
|
||||
- Check for security vulnerabilities
|
||||
- Ensure code follows best practices
|
||||
- Suggest improvements for readability and performance
|
||||
- Suggest improvements for readability and performance
|
||||
|
||||
15
tests/modules/programs/opencode/commands-bulk-directory.nix
Normal file
15
tests/modules/programs/opencode/commands-bulk-directory.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
programs.opencode = {
|
||||
enable = true;
|
||||
commands = ./commands-bulk;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/opencode/command/changelog.md
|
||||
assertFileExists home-files/.config/opencode/command/commit.md
|
||||
assertFileContent home-files/.config/opencode/command/changelog.md \
|
||||
${./commands-bulk/changelog.md}
|
||||
assertFileContent home-files/.config/opencode/command/commit.md \
|
||||
${./commands-bulk/commit.md}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Update Changelog Command
|
||||
|
||||
Update CHANGELOG.md with a new entry for the specified version.
|
||||
Usage: /changelog [version] [change-type] [message]
|
||||
4
tests/modules/programs/opencode/commands-bulk/commit.md
Normal file
4
tests/modules/programs/opencode/commands-bulk/commit.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Commit Command
|
||||
|
||||
Create a git commit with proper message formatting.
|
||||
Usage: /commit [message]
|
||||
@@ -6,12 +6,14 @@
|
||||
# Update Changelog Command
|
||||
|
||||
Update CHANGELOG.md with a new entry for the specified version.
|
||||
Usage: /changelog [version] [change-type] [message]'';
|
||||
Usage: /changelog [version] [change-type] [message]
|
||||
'';
|
||||
commit = ''
|
||||
# Commit Command
|
||||
|
||||
Create a git commit with proper message formatting.
|
||||
Usage: /commit [message]'';
|
||||
Usage: /commit [message]
|
||||
'';
|
||||
};
|
||||
};
|
||||
nmt.script = ''
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Commit Command
|
||||
|
||||
Create a git commit with proper message formatting.
|
||||
Usage: /commit [message]
|
||||
Usage: /commit [message]
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
opencode-commands-inline = ./commands-inline.nix;
|
||||
opencode-agents-path = ./agents-path.nix;
|
||||
opencode-commands-path = ./commands-path.nix;
|
||||
opencode-agents-bulk-directory = ./agents-bulk-directory.nix;
|
||||
opencode-commands-bulk-directory = ./commands-bulk-directory.nix;
|
||||
opencode-mixed-content = ./mixed-content.nix;
|
||||
opencode-skills-inline = ./skills-inline.nix;
|
||||
opencode-skills-path = ./skills-path.nix;
|
||||
@@ -15,6 +17,7 @@
|
||||
opencode-skills-bulk-directory = ./skills-bulk-directory.nix;
|
||||
opencode-themes-inline = ./themes-inline.nix;
|
||||
opencode-themes-path = ./themes-path.nix;
|
||||
opencode-themes-bulk-directory = ./themes-bulk-directory.nix;
|
||||
opencode-mcp-integration = ./mcp-integration.nix;
|
||||
opencode-mcp-integration-with-override = ./mcp-integration-with-override.nix;
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ Focus on user-friendly explanations and examples.
|
||||
- Write clear, concise documentation
|
||||
- Include practical examples
|
||||
- Use proper formatting and structure
|
||||
- Consider the target audience
|
||||
- Consider the target audience
|
||||
|
||||
@@ -15,16 +15,65 @@
|
||||
'';
|
||||
path-agent = ./test-agent.md;
|
||||
};
|
||||
skills = {
|
||||
inline-skill = ''
|
||||
---
|
||||
name: inline-skill
|
||||
description: An inline skill definition
|
||||
---
|
||||
|
||||
## What I do
|
||||
This skill is defined inline.
|
||||
'';
|
||||
path-skill = ./git-release-SKILL.md;
|
||||
dir-skill = ./skill-dir/data-analysis;
|
||||
};
|
||||
themes = {
|
||||
inline-theme = {
|
||||
name = "inline-theme";
|
||||
colors = {
|
||||
primary = "#000000";
|
||||
secondary = "#ffffff";
|
||||
};
|
||||
};
|
||||
path-theme = ./my-theme.json;
|
||||
};
|
||||
};
|
||||
nmt.script = ''
|
||||
# Commands
|
||||
assertFileExists home-files/.config/opencode/command/inline-command.md
|
||||
assertFileExists home-files/.config/opencode/command/path-command.md
|
||||
assertFileExists home-files/.config/opencode/agent/inline-agent.md
|
||||
assertFileExists home-files/.config/opencode/agent/path-agent.md
|
||||
|
||||
assertFileContent home-files/.config/opencode/command/path-command.md \
|
||||
${./test-command.md}
|
||||
|
||||
# Agents
|
||||
assertFileExists home-files/.config/opencode/agent/inline-agent.md
|
||||
assertFileExists home-files/.config/opencode/agent/path-agent.md
|
||||
|
||||
assertFileContent home-files/.config/opencode/agent/path-agent.md \
|
||||
${./test-agent.md}
|
||||
|
||||
# Skills
|
||||
assertFileExists home-files/.config/opencode/skill/inline-skill/SKILL.md
|
||||
assertFileExists home-files/.config/opencode/skill/path-skill/SKILL.md
|
||||
assertFileExists home-files/.config/opencode/skill/dir-skill/SKILL.md
|
||||
assertFileExists home-files/.config/opencode/skill/dir-skill/notes.txt
|
||||
|
||||
assertFileContent home-files/.config/opencode/skill/path-skill/SKILL.md \
|
||||
${./git-release-SKILL.md}
|
||||
assertFileContent home-files/.config/opencode/skill/dir-skill/SKILL.md \
|
||||
${./skill-dir/data-analysis/SKILL.md}
|
||||
|
||||
# Themes
|
||||
assertFileExists home-files/.config/opencode/themes/inline-theme.json
|
||||
assertFileExists home-files/.config/opencode/themes/path-theme.json
|
||||
|
||||
assertFileContent home-files/.config/opencode/themes/path-theme.json \
|
||||
${./my-theme.json}
|
||||
|
||||
# Verify inline-theme has the schema
|
||||
assertFileContains home-files/.config/opencode/themes/inline-theme.json \
|
||||
'"$schema": "https://opencode.ai/theme.json"'
|
||||
'';
|
||||
}
|
||||
|
||||
15
tests/modules/programs/opencode/themes-bulk-directory.nix
Normal file
15
tests/modules/programs/opencode/themes-bulk-directory.nix
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
programs.opencode = {
|
||||
enable = true;
|
||||
themes = ./themes-bulk;
|
||||
};
|
||||
|
||||
nmt.script = ''
|
||||
assertFileExists home-files/.config/opencode/themes/dark-theme.json
|
||||
assertFileExists home-files/.config/opencode/themes/light-theme.json
|
||||
assertFileContent home-files/.config/opencode/themes/dark-theme.json \
|
||||
${./themes-bulk/dark-theme.json}
|
||||
assertFileContent home-files/.config/opencode/themes/light-theme.json \
|
||||
${./themes-bulk/light-theme.json}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/theme.json",
|
||||
"name": "dark-theme",
|
||||
"colors": {
|
||||
"primary": "#1e1e1e",
|
||||
"secondary": "#252526"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/theme.json",
|
||||
"name": "light-theme",
|
||||
"colors": {
|
||||
"primary": "#ffffff",
|
||||
"secondary": "#f3f3f3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user