87 lines
2.8 KiB
Go
87 lines
2.8 KiB
Go
// Package agent dispatches `sherlock <name>` invocations to a small
|
|
// set of supported CLIs (Copilot, Claude Code, …). Each built-in agent
|
|
// is a concrete type in its own file that implements the Agent
|
|
// interface; they register themselves at init time. Adding a new
|
|
// built-in is a matter of dropping a new file in this package.
|
|
//
|
|
// Operators can also declare custom agents in config.toml's
|
|
// `[agents.<name>]` table — a wrapper command plus a backend type
|
|
// ("copilot" or "claude") that selects the MCP-config and env
|
|
// conventions. Those are added at runtime via RegisterCustom. The
|
|
// per-CLI quirks shared by built-in and custom agents live in
|
|
// backend.go.
|
|
//
|
|
// The reusable bits live in this package too (exec.go for env/exec
|
|
// helpers; the mcp sibling package for MCP-config rendering). Agent
|
|
// implementations are intentionally short — they wire the per-CLI
|
|
// quirks (flag names, env var names, MCP config schema) and delegate
|
|
// everything else.
|
|
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"gitea.alexandru.macocian.me/amacocian/sherlock/internal/mcp"
|
|
)
|
|
|
|
// Context is everything the CLI hands an agent at spawn time. Kept
|
|
// deliberately small — Phase 2+ fields land here as MCPs are wired.
|
|
type Context struct {
|
|
// Servers is the set of MCP servers sherlock wants the agent to
|
|
// know about. The agent's Spawn method renders these into the
|
|
// per-agent MCP config file before exec.
|
|
Servers mcp.Servers
|
|
}
|
|
|
|
// Agent is the contract one concrete type implements per supported CLI.
|
|
type Agent interface {
|
|
// Name is the case-sensitive subcommand: `sherlock <Name>`.
|
|
Name() string
|
|
// Description shows up in `sherlock status`.
|
|
Description() string
|
|
// Spawn replaces the current process with the agent CLI. On
|
|
// success it does not return; any returned error is a setup
|
|
// failure (binary missing, MCP render failed, exec failed).
|
|
Spawn(ctx Context, args []string) error
|
|
}
|
|
|
|
var registry = map[string]Agent{}
|
|
|
|
// Register adds a to the package-level registry. Called from init()
|
|
// in each agent file. Duplicate names panic at package load — that's
|
|
// always a programmer error.
|
|
func Register(a Agent) {
|
|
if _, dup := registry[a.Name()]; dup {
|
|
panic(fmt.Sprintf("agent: %q registered twice", a.Name()))
|
|
}
|
|
registry[a.Name()] = a
|
|
}
|
|
|
|
// Get returns the agent for name and whether it exists.
|
|
func Get(name string) (Agent, bool) {
|
|
a, ok := registry[name]
|
|
return a, ok
|
|
}
|
|
|
|
// Names returns the registered agent names, sorted.
|
|
func Names() []string {
|
|
out := make([]string, 0, len(registry))
|
|
for n := range registry {
|
|
out = append(out, n)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// All returns the registered agents, sorted by name. Useful for
|
|
// rendering descriptions in `sherlock status`.
|
|
func All() []Agent {
|
|
names := Names()
|
|
out := make([]Agent, len(names))
|
|
for i, n := range names {
|
|
out[i] = registry[n]
|
|
}
|
|
return out
|
|
}
|