diff --git a/modules/programs/opencode.nix b/modules/programs/opencode.nix index 8a72377a8..02270b7ac 100644 --- a/modules/programs/opencode.nix +++ b/modules/programs/opencode.nix @@ -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/.md`) + - A path to a file (creates `opencode/command/.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/.md`) + - A path to a file (creates `opencode/agent/.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/.json`) + - A path to a file (creates `opencode/themes/.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 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 + ); }; } diff --git a/tests/modules/programs/opencode/agents-bulk-directory.nix b/tests/modules/programs/opencode/agents-bulk-directory.nix new file mode 100644 index 000000000..fd18f1b0f --- /dev/null +++ b/tests/modules/programs/opencode/agents-bulk-directory.nix @@ -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} + ''; +} diff --git a/tests/modules/programs/opencode/agents-bulk/code-reviewer.md b/tests/modules/programs/opencode/agents-bulk/code-reviewer.md new file mode 100644 index 000000000..1c414fd53 --- /dev/null +++ b/tests/modules/programs/opencode/agents-bulk/code-reviewer.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. diff --git a/tests/modules/programs/opencode/agents-bulk/documentation.md b/tests/modules/programs/opencode/agents-bulk/documentation.md new file mode 100644 index 000000000..71d12c6ff --- /dev/null +++ b/tests/modules/programs/opencode/agents-bulk/documentation.md @@ -0,0 +1,3 @@ +# Documentation Agent + +You are a technical writer specializing in creating clear, comprehensive documentation. diff --git a/tests/modules/programs/opencode/agents-inline.nix b/tests/modules/programs/opencode/agents-inline.nix index e077f20f7..1b56fa7e9 100644 --- a/tests/modules/programs/opencode/agents-inline.nix +++ b/tests/modules/programs/opencode/agents-inline.nix @@ -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 = '' diff --git a/tests/modules/programs/opencode/changelog-command.md b/tests/modules/programs/opencode/changelog-command.md index e7565670b..5b04653f3 100644 --- a/tests/modules/programs/opencode/changelog-command.md +++ b/tests/modules/programs/opencode/changelog-command.md @@ -1,4 +1,4 @@ # Update Changelog Command Update CHANGELOG.md with a new entry for the specified version. -Usage: /changelog [version] [change-type] [message] \ No newline at end of file +Usage: /changelog [version] [change-type] [message] diff --git a/tests/modules/programs/opencode/code-reviewer-agent.md b/tests/modules/programs/opencode/code-reviewer-agent.md index f95340356..2eb839fb5 100644 --- a/tests/modules/programs/opencode/code-reviewer-agent.md +++ b/tests/modules/programs/opencode/code-reviewer-agent.md @@ -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 \ No newline at end of file +- Suggest improvements for readability and performance diff --git a/tests/modules/programs/opencode/commands-bulk-directory.nix b/tests/modules/programs/opencode/commands-bulk-directory.nix new file mode 100644 index 000000000..33d0b84aa --- /dev/null +++ b/tests/modules/programs/opencode/commands-bulk-directory.nix @@ -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} + ''; +} diff --git a/tests/modules/programs/opencode/commands-bulk/changelog.md b/tests/modules/programs/opencode/commands-bulk/changelog.md new file mode 100644 index 000000000..5b04653f3 --- /dev/null +++ b/tests/modules/programs/opencode/commands-bulk/changelog.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] diff --git a/tests/modules/programs/opencode/commands-bulk/commit.md b/tests/modules/programs/opencode/commands-bulk/commit.md new file mode 100644 index 000000000..216206b4d --- /dev/null +++ b/tests/modules/programs/opencode/commands-bulk/commit.md @@ -0,0 +1,4 @@ +# Commit Command + +Create a git commit with proper message formatting. +Usage: /commit [message] diff --git a/tests/modules/programs/opencode/commands-inline.nix b/tests/modules/programs/opencode/commands-inline.nix index 294f92bc9..c58e3c00a 100644 --- a/tests/modules/programs/opencode/commands-inline.nix +++ b/tests/modules/programs/opencode/commands-inline.nix @@ -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 = '' diff --git a/tests/modules/programs/opencode/commit-command.md b/tests/modules/programs/opencode/commit-command.md index 04c7f13bb..216206b4d 100644 --- a/tests/modules/programs/opencode/commit-command.md +++ b/tests/modules/programs/opencode/commit-command.md @@ -1,4 +1,4 @@ # Commit Command Create a git commit with proper message formatting. -Usage: /commit [message] \ No newline at end of file +Usage: /commit [message] diff --git a/tests/modules/programs/opencode/default.nix b/tests/modules/programs/opencode/default.nix index 8a865230b..c347ca1d3 100644 --- a/tests/modules/programs/opencode/default.nix +++ b/tests/modules/programs/opencode/default.nix @@ -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; } diff --git a/tests/modules/programs/opencode/documentation-agent.md b/tests/modules/programs/opencode/documentation-agent.md index f76305eb4..63660c80e 100644 --- a/tests/modules/programs/opencode/documentation-agent.md +++ b/tests/modules/programs/opencode/documentation-agent.md @@ -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 \ No newline at end of file +- Consider the target audience diff --git a/tests/modules/programs/opencode/mixed-content.nix b/tests/modules/programs/opencode/mixed-content.nix index ea7fecd9a..8668b531c 100644 --- a/tests/modules/programs/opencode/mixed-content.nix +++ b/tests/modules/programs/opencode/mixed-content.nix @@ -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"' ''; } diff --git a/tests/modules/programs/opencode/themes-bulk-directory.nix b/tests/modules/programs/opencode/themes-bulk-directory.nix new file mode 100644 index 000000000..e76e4a5d9 --- /dev/null +++ b/tests/modules/programs/opencode/themes-bulk-directory.nix @@ -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} + ''; +} diff --git a/tests/modules/programs/opencode/themes-bulk/dark-theme.json b/tests/modules/programs/opencode/themes-bulk/dark-theme.json new file mode 100644 index 000000000..5c2d46075 --- /dev/null +++ b/tests/modules/programs/opencode/themes-bulk/dark-theme.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://opencode.ai/theme.json", + "name": "dark-theme", + "colors": { + "primary": "#1e1e1e", + "secondary": "#252526" + } +} diff --git a/tests/modules/programs/opencode/themes-bulk/light-theme.json b/tests/modules/programs/opencode/themes-bulk/light-theme.json new file mode 100644 index 000000000..6b10dbb28 --- /dev/null +++ b/tests/modules/programs/opencode/themes-bulk/light-theme.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://opencode.ai/theme.json", + "name": "light-theme", + "colors": { + "primary": "#ffffff", + "secondary": "#f3f3f3" + } +}