Files
sherlock/internal/agent/custom.go
T
Alexandru Macocian 84958c7ef3
Release / release (push) Successful in 17s
Support for local agents
2026-06-15 19:03:56 +02:00

57 lines
1.7 KiB
Go

package agent
import "fmt"
// custom is an operator-defined agent declared in config.toml's
// `[agents.<name>]` table. It wraps an arbitrary command — typically a
// shell wrapper such as `copilot-local` that points a CLI at a
// self-hosted model — and reuses a built-in backend's MCP-config and
// env conventions so MCPs are formatted exactly as the underlying CLI
// expects.
type custom struct {
name string
desc string
command string
backend backend
}
func (c custom) Name() string { return c.name }
func (c custom) Description() string { return c.desc }
func (c custom) Spawn(ctx Context, args []string) error {
return c.backend.spawn(c.name, c.command, ctx, args)
}
// RegisterCustom adds an operator-defined agent to the registry.
//
// - name is the subcommand: `sherlock <name>`.
// - command is the binary to exec; it defaults to name when empty.
// - backendName selects the MCP/-env conventions ("copilot" or
// "claude").
//
// Unlike Register, it returns an error (rather than panicking) on a bad
// backend or a name that collides with an already-registered agent,
// since the input comes from operator config rather than program code.
func RegisterCustom(name, command, backendName string) error {
if name == "" {
return fmt.Errorf("agent: custom agent needs a name")
}
b, err := resolveBackend(backendName)
if err != nil {
return fmt.Errorf("agent: custom agent %q: %w", name, err)
}
if command == "" {
command = name
}
if _, dup := registry[name]; dup {
return fmt.Errorf("agent: custom agent %q conflicts with an existing agent", name)
}
registry[name] = custom{
name: name,
desc: fmt.Sprintf("custom %s agent (%s)", backendName, command),
command: command,
backend: b,
}
return nil
}