package manager import ( "context" "fmt" "sync" "github.com/yourname/cyrene-ai/plugin-manager/internal/sdk" ) // ToolRegistry aggregates tool definitions from all running plugins and dispatches execution. type ToolRegistry struct { mu sync.RWMutex tools map[string]sdk.Tool // tool ID -> Tool } func NewToolRegistry() *ToolRegistry { return &ToolRegistry{tools: make(map[string]sdk.Tool)} } func (r *ToolRegistry) Register(tool sdk.Tool) error { r.mu.Lock() defer r.mu.Unlock() id := tool.Definition().ID if _, exists := r.tools[id]; exists { return fmt.Errorf("tool %q already registered", id) } r.tools[id] = tool return nil } func (r *ToolRegistry) Unregister(toolID string) { r.mu.Lock() defer r.mu.Unlock() delete(r.tools, toolID) } func (r *ToolRegistry) Get(toolID string) (sdk.Tool, bool) { r.mu.RLock() defer r.mu.RUnlock() t, ok := r.tools[toolID] return t, ok } func (r *ToolRegistry) List() []sdk.Tool { r.mu.RLock() defer r.mu.RUnlock() result := make([]sdk.Tool, 0, len(r.tools)) for _, t := range r.tools { result = append(result, t) } return result } func (r *ToolRegistry) Definitions() []sdk.ToolDefinition { r.mu.RLock() defer r.mu.RUnlock() defs := make([]sdk.ToolDefinition, 0, len(r.tools)) for _, t := range r.tools { defs = append(defs, t.Definition()) } return defs } func (r *ToolRegistry) Execute(ctx context.Context, toolID string, args map[string]interface{}) (*sdk.ToolResult, error) { r.mu.RLock() tool, ok := r.tools[toolID] r.mu.RUnlock() if !ok { return nil, fmt.Errorf("tool %q not found", toolID) } if err := tool.Validate(args); err != nil { return &sdk.ToolResult{Success: false, Error: err.Error()}, nil } return tool.Execute(ctx, args) } // UnregisterAll removes all tools matching a prefix (plugin's tools). func (r *ToolRegistry) UnregisterAll(toolIDs []string) { r.mu.Lock() defer r.mu.Unlock() for _, id := range toolIDs { delete(r.tools, id) } }