/** * Shared utilities for displaying agent information. * Used by both the CLI `claude agents` handler and the interactive `/agents` command. */ import { getDefaultSubagentModel } from '../../utils/model/agent.js' import { getSourceDisplayName, type SettingSource, } from '../../utils/settings/constants.js' import type { AgentDefinition } from './loadAgentsDir.js' type AgentSource = SettingSource | 'built-in' | 'plugin' export type AgentSourceGroup = { label: string source: AgentSource } /** * Ordered list of agent source groups for display. * Both the CLI and interactive UI should use this to ensure consistent ordering. */ export const AGENT_SOURCE_GROUPS: AgentSourceGroup[] = [ { label: 'User agents', source: 'userSettings' }, { label: 'Project agents', source: 'projectSettings' }, { label: 'Local agents', source: 'localSettings' }, { label: 'Managed agents', source: 'policySettings' }, { label: 'Plugin agents', source: 'plugin' }, { label: 'CLI arg agents', source: 'flagSettings' }, { label: 'Built-in agents', source: 'built-in' }, ] export type ResolvedAgent = AgentDefinition & { overriddenBy?: AgentSource } /** * Annotate agents with override information by comparing against the active * (winning) agent list. An agent is "overridden" when another agent with the * same type from a higher-priority source takes precedence. * * Also deduplicates by (agentType, source) to handle git worktree duplicates * where the same agent file is loaded from both the worktree and main repo. */ export function resolveAgentOverrides( allAgents: AgentDefinition[], activeAgents: AgentDefinition[], ): ResolvedAgent[] { const activeMap = new Map() for (const agent of activeAgents) { activeMap.set(agent.agentType, agent) } const seen = new Set() const resolved: ResolvedAgent[] = [] // Iterate allAgents, annotating each with override info from activeAgents. // Deduplicate by (agentType, source) to handle git worktree duplicates. for (const agent of allAgents) { const key = `${agent.agentType}:${agent.source}` if (seen.has(key)) continue seen.add(key) const active = activeMap.get(agent.agentType) const overriddenBy = active && active.source !== agent.source ? active.source : undefined resolved.push({ ...agent, overriddenBy }) } return resolved } /** * Resolve the display model string for an agent. * Returns the model alias or 'inherit' for display purposes. */ export function resolveAgentModelDisplay( agent: AgentDefinition, ): string | undefined { const model = agent.model || getDefaultSubagentModel() if (!model) return undefined return model === 'inherit' ? 'inherit' : model } /** * Get a human-readable label for the source that overrides an agent. * Returns lowercase, e.g. "user", "project", "managed". */ export function getOverrideSourceLabel(source: AgentSource): string { return getSourceDisplayName(source).toLowerCase() } /** * Compare agents alphabetically by name (case-insensitive). */ export function compareAgentsByName( a: AgentDefinition, b: AgentDefinition, ): number { return a.agentType.localeCompare(b.agentType, undefined, { sensitivity: 'base', }) }