mirror of
https://github.com/nix-community/home-manager.git
synced 2026-01-11 09:29:41 +08:00
opencode: add custom tools support
Adds support for custom tools - user-defined functions that the LLM can call during conversations. Custom tools work alongside opencode's built-in tools and are configured through the new `tools` option. The configuration follows the same pattern as other opencode settings like `agents` and `commands`, supporting: - Inline TypeScript content - Individual file paths - Bulk directory imports
This commit is contained in:
committed by
Austin Horstman
parent
081234b704
commit
b1b1c68033
@@ -264,6 +264,49 @@ in
|
|||||||
See <https://opencode.ai/docs/themes/> for the documentation.
|
See <https://opencode.ai/docs/themes/> for the documentation.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tools = lib.mkOption {
|
||||||
|
type = lib.types.either (lib.types.attrsOf (lib.types.either lib.types.lines lib.types.path)) lib.types.path;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Custom tools for opencode.
|
||||||
|
|
||||||
|
This option can either be:
|
||||||
|
- An attribute set defining tools
|
||||||
|
- A path to a directory containing multiple tool files
|
||||||
|
|
||||||
|
If an attribute set is used, the attribute name becomes the tool filename,
|
||||||
|
and the value is either:
|
||||||
|
- Inline content as a string (creates `opencode/tool/<name>.ts`)
|
||||||
|
- A path to a file (creates `opencode/tool/<name>.ts` or `opencode/tool/<name>.js`)
|
||||||
|
|
||||||
|
If a path is used, it is expected to contain tool files.
|
||||||
|
The directory is symlinked to {file}`$XDG_CONFIG_HOME/opencode/tool/`.
|
||||||
|
|
||||||
|
See <https://opencode.ai/docs/tools/> for the documentation.
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
database-query = '''
|
||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Query the project database",
|
||||||
|
args: {
|
||||||
|
query: tool.schema.string().describe("SQL query to execute"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
// Your database logic here
|
||||||
|
return `Executed query: ''${args.query}`
|
||||||
|
},
|
||||||
|
})
|
||||||
|
''';
|
||||||
|
|
||||||
|
# Or reference an existing file
|
||||||
|
api-client = ./tools/api-client.ts;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
@@ -276,6 +319,10 @@ in
|
|||||||
assertion = !lib.isPath cfg.agents || lib.pathIsDirectory cfg.agents;
|
assertion = !lib.isPath cfg.agents || lib.pathIsDirectory cfg.agents;
|
||||||
message = "`programs.opencode.agents` must be a directory when set to a path";
|
message = "`programs.opencode.agents` must be a directory when set to a path";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
assertion = !lib.isPath cfg.tools || lib.pathIsDirectory cfg.tools;
|
||||||
|
message = "`programs.opencode.tools` must be a directory when set to a path";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
assertion = !lib.isPath cfg.skills || lib.pathIsDirectory cfg.skills;
|
assertion = !lib.isPath cfg.skills || lib.pathIsDirectory cfg.skills;
|
||||||
message = "`programs.opencode.skills` must be a directory when set to a path";
|
message = "`programs.opencode.skills` must be a directory when set to a path";
|
||||||
@@ -325,6 +372,11 @@ in
|
|||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"opencode/tool" = mkIf (lib.isPath cfg.tools) {
|
||||||
|
source = cfg.tools;
|
||||||
|
recursive = true;
|
||||||
|
};
|
||||||
|
|
||||||
"opencode/skill" = mkIf (lib.isPath cfg.skills) {
|
"opencode/skill" = mkIf (lib.isPath cfg.skills) {
|
||||||
source = cfg.skills;
|
source = cfg.skills;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
@@ -351,6 +403,14 @@ in
|
|||||||
)
|
)
|
||||||
) cfg.agents
|
) cfg.agents
|
||||||
)
|
)
|
||||||
|
// lib.optionalAttrs (builtins.isAttrs cfg.tools) (
|
||||||
|
lib.mapAttrs' (
|
||||||
|
name: content:
|
||||||
|
lib.nameValuePair "opencode/tool/${name}.ts" (
|
||||||
|
if lib.isPath content then { source = content; } else { text = content; }
|
||||||
|
)
|
||||||
|
) cfg.tools
|
||||||
|
)
|
||||||
// lib.mapAttrs' (
|
// lib.mapAttrs' (
|
||||||
name: content:
|
name: content:
|
||||||
if lib.isPath content && lib.pathIsDirectory content then
|
if lib.isPath content && lib.pathIsDirectory content then
|
||||||
|
|||||||
12
tests/modules/programs/opencode/api-client-tool.ts
Normal file
12
tests/modules/programs/opencode/api-client-tool.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Make API requests to external services",
|
||||||
|
args: {
|
||||||
|
endpoint: tool.schema.string().describe("API endpoint to call"),
|
||||||
|
method: tool.schema.string().describe("HTTP method"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Called ${args.method} ${args.endpoint}`
|
||||||
|
},
|
||||||
|
})
|
||||||
11
tests/modules/programs/opencode/database-query-tool.ts
Normal file
11
tests/modules/programs/opencode/database-query-tool.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Query the project database",
|
||||||
|
args: {
|
||||||
|
query: tool.schema.string().describe("SQL query to execute"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Executed query: ${args.query}`
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -10,6 +10,9 @@
|
|||||||
opencode-commands-path = ./commands-path.nix;
|
opencode-commands-path = ./commands-path.nix;
|
||||||
opencode-agents-bulk-directory = ./agents-bulk-directory.nix;
|
opencode-agents-bulk-directory = ./agents-bulk-directory.nix;
|
||||||
opencode-commands-bulk-directory = ./commands-bulk-directory.nix;
|
opencode-commands-bulk-directory = ./commands-bulk-directory.nix;
|
||||||
|
opencode-tools-inline = ./tools-inline.nix;
|
||||||
|
opencode-tools-path = ./tools-path.nix;
|
||||||
|
opencode-tools-bulk-directory = ./tools-bulk-directory.nix;
|
||||||
opencode-mixed-content = ./mixed-content.nix;
|
opencode-mixed-content = ./mixed-content.nix;
|
||||||
opencode-skills-inline = ./skills-inline.nix;
|
opencode-skills-inline = ./skills-inline.nix;
|
||||||
opencode-skills-path = ./skills-path.nix;
|
opencode-skills-path = ./skills-path.nix;
|
||||||
|
|||||||
@@ -15,6 +15,22 @@
|
|||||||
'';
|
'';
|
||||||
path-agent = ./test-agent.md;
|
path-agent = ./test-agent.md;
|
||||||
};
|
};
|
||||||
|
tools = {
|
||||||
|
inline-tool = ''
|
||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Inline tool definition",
|
||||||
|
args: {
|
||||||
|
input: tool.schema.string().describe("Test input"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Processed: ''${args.input}`
|
||||||
|
},
|
||||||
|
})
|
||||||
|
'';
|
||||||
|
path-tool = ./test-tool.ts;
|
||||||
|
};
|
||||||
skills = {
|
skills = {
|
||||||
inline-skill = ''
|
inline-skill = ''
|
||||||
---
|
---
|
||||||
@@ -54,6 +70,13 @@
|
|||||||
assertFileContent home-files/.config/opencode/agent/path-agent.md \
|
assertFileContent home-files/.config/opencode/agent/path-agent.md \
|
||||||
${./test-agent.md}
|
${./test-agent.md}
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
assertFileExists home-files/.config/opencode/tool/inline-tool.ts
|
||||||
|
assertFileExists home-files/.config/opencode/tool/path-tool.ts
|
||||||
|
|
||||||
|
assertFileContent home-files/.config/opencode/tool/path-tool.ts \
|
||||||
|
${./test-tool.ts}
|
||||||
|
|
||||||
# Skills
|
# Skills
|
||||||
assertFileExists home-files/.config/opencode/skill/inline-skill/SKILL.md
|
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/path-skill/SKILL.md
|
||||||
|
|||||||
11
tests/modules/programs/opencode/test-tool.ts
Normal file
11
tests/modules/programs/opencode/test-tool.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Test tool for unit testing",
|
||||||
|
args: {
|
||||||
|
input: tool.schema.string().describe("Test input parameter"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Processed: ${args.input}`
|
||||||
|
},
|
||||||
|
})
|
||||||
15
tests/modules/programs/opencode/tools-bulk-directory.nix
Normal file
15
tests/modules/programs/opencode/tools-bulk-directory.nix
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
tools = ./tools-bulk;
|
||||||
|
};
|
||||||
|
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/opencode/tool/database-query.ts
|
||||||
|
assertFileExists home-files/.config/opencode/tool/api-client.ts
|
||||||
|
assertFileContent home-files/.config/opencode/tool/database-query.ts \
|
||||||
|
${./tools-bulk/database-query.ts}
|
||||||
|
assertFileContent home-files/.config/opencode/tool/api-client.ts \
|
||||||
|
${./tools-bulk/api-client.ts}
|
||||||
|
'';
|
||||||
|
}
|
||||||
12
tests/modules/programs/opencode/tools-bulk/api-client.ts
Normal file
12
tests/modules/programs/opencode/tools-bulk/api-client.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Make API requests to external services",
|
||||||
|
args: {
|
||||||
|
endpoint: tool.schema.string().describe("API endpoint to call"),
|
||||||
|
method: tool.schema.string().describe("HTTP method"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Called ${args.method} ${args.endpoint}`
|
||||||
|
},
|
||||||
|
})
|
||||||
11
tests/modules/programs/opencode/tools-bulk/database-query.ts
Normal file
11
tests/modules/programs/opencode/tools-bulk/database-query.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Query the project database",
|
||||||
|
args: {
|
||||||
|
query: tool.schema.string().describe("SQL query to execute"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Executed query: ${args.query}`
|
||||||
|
},
|
||||||
|
})
|
||||||
42
tests/modules/programs/opencode/tools-inline.nix
Normal file
42
tests/modules/programs/opencode/tools-inline.nix
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
tools = {
|
||||||
|
database-query = ''
|
||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Query the project database",
|
||||||
|
args: {
|
||||||
|
query: tool.schema.string().describe("SQL query to execute"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Executed query: ''${args.query}`
|
||||||
|
},
|
||||||
|
})
|
||||||
|
'';
|
||||||
|
api-client = ''
|
||||||
|
import { tool } from "@opencode-ai/plugin"
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "Make API requests to external services",
|
||||||
|
args: {
|
||||||
|
endpoint: tool.schema.string().describe("API endpoint to call"),
|
||||||
|
method: tool.schema.string().describe("HTTP method"),
|
||||||
|
},
|
||||||
|
async execute(args) {
|
||||||
|
return `Called ''${args.method} ''${args.endpoint}`
|
||||||
|
},
|
||||||
|
})
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/opencode/tool/database-query.ts
|
||||||
|
assertFileExists home-files/.config/opencode/tool/api-client.ts
|
||||||
|
assertFileContent home-files/.config/opencode/tool/database-query.ts \
|
||||||
|
${./database-query-tool.ts}
|
||||||
|
assertFileContent home-files/.config/opencode/tool/api-client.ts \
|
||||||
|
${./api-client-tool.ts}
|
||||||
|
'';
|
||||||
|
}
|
||||||
13
tests/modules/programs/opencode/tools-path.nix
Normal file
13
tests/modules/programs/opencode/tools-path.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
tools = {
|
||||||
|
test-tool = ./test-tool.ts;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
nmt.script = ''
|
||||||
|
assertFileExists home-files/.config/opencode/tool/test-tool.ts
|
||||||
|
assertFileContent home-files/.config/opencode/tool/test-tool.ts \
|
||||||
|
${./test-tool.ts}
|
||||||
|
'';
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user