// Package codeagent provides the code agent registry and common utilities. package codeagent import ( "context" "fmt" "sync" "github.com/orchard9/rdev/internal/domain" "github.com/orchard9/rdev/internal/port" ) // Registry implements port.CodeAgentRegistry with thread-safe agent management. type Registry struct { mu sync.RWMutex agents map[domain.AgentProvider]port.CodeAgent defProv domain.AgentProvider hasAgent bool } // NewRegistry creates a new empty agent registry. func NewRegistry() *Registry { return &Registry{ agents: make(map[domain.AgentProvider]port.CodeAgent), } } // Ensure Registry implements port.CodeAgentRegistry at compile time. var _ port.CodeAgentRegistry = (*Registry)(nil) // Register adds an agent implementation for a provider. func (r *Registry) Register(agent port.CodeAgent) { r.mu.Lock() defer r.mu.Unlock() provider := agent.Provider() r.agents[provider] = agent // If this is the first agent, make it the default if !r.hasAgent { r.defProv = provider r.hasAgent = true } } // Get returns the agent for a specific provider. func (r *Registry) Get(provider domain.AgentProvider) port.CodeAgent { r.mu.RLock() defer r.mu.RUnlock() return r.agents[provider] } // Default returns the default agent implementation. func (r *Registry) Default() port.CodeAgent { r.mu.RLock() defer r.mu.RUnlock() if !r.hasAgent { return nil } return r.agents[r.defProv] } // SetDefault sets which provider should be used as the default. func (r *Registry) SetDefault(provider domain.AgentProvider) error { r.mu.Lock() defer r.mu.Unlock() if _, exists := r.agents[provider]; !exists { return fmt.Errorf("agent provider %q is not registered", provider) } r.defProv = provider return nil } // Available returns all registered providers. func (r *Registry) Available() []domain.AgentProvider { r.mu.RLock() defer r.mu.RUnlock() providers := make([]domain.AgentProvider, 0, len(r.agents)) for p := range r.agents { providers = append(providers, p) } return providers } // AvailableAgents returns all registered agents that are currently available. func (r *Registry) AvailableAgents(ctx context.Context) []port.CodeAgent { r.mu.RLock() defer r.mu.RUnlock() available := make([]port.CodeAgent, 0, len(r.agents)) for _, agent := range r.agents { if agent.Available(ctx) { available = append(available, agent) } } return available } // DefaultProvider returns the current default provider. // Returns empty string if no agents are registered. func (r *Registry) DefaultProvider() domain.AgentProvider { r.mu.RLock() defer r.mu.RUnlock() if !r.hasAgent { return "" } return r.defProv } // Count returns the number of registered agents. func (r *Registry) Count() int { r.mu.RLock() defer r.mu.RUnlock() return len(r.agents) }