|
1 | 1 | import fs from 'fs' |
| 2 | +import os from 'os' |
2 | 3 | import path from 'path' |
3 | 4 |
|
4 | 5 | import { pluralize } from '@codebuff/common/util/string' |
@@ -35,28 +36,45 @@ let userAgentFilePaths: Map<string, string> = new Map() |
35 | 36 | /** |
36 | 37 | * Initialize the agent registry by loading user agents via the SDK. |
37 | 38 | * This must be called at CLI startup before any sync agent loading functions. |
| 39 | + * |
| 40 | + * Agents are loaded from: |
| 41 | + * - {cwd}/.agents (project) |
| 42 | + * - {cwd}/../.agents (parent, e.g. monorepo root) |
| 43 | + * - ~/.agents (global, user's home directory) |
| 44 | + * |
| 45 | + * Later directories take precedence, so project agents override global ones. |
38 | 46 | */ |
39 | 47 | export async function initializeAgentRegistry(): Promise<void> { |
40 | | - const agentsDir = findAgentsDirectory() |
41 | | - if (agentsDir) { |
42 | | - try { |
43 | | - userAgentsCache = await sdkLoadLocalAgents({ agentsPath: agentsDir }) |
44 | | - // Build ID-to-filepath map by scanning agent files |
45 | | - userAgentFilePaths = buildAgentFilePathMap(agentsDir) |
46 | | - } catch (error) { |
47 | | - // Fall back to empty cache if SDK loading fails, but log a warning |
48 | | - logger.warn({ error, agentsDir }, 'Failed to load user agents from .agents directory') |
49 | | - userAgentsCache = {} |
50 | | - userAgentFilePaths = new Map() |
51 | | - } |
| 48 | + try { |
| 49 | + // Let SDK load from all default directories (cwd, parent, home) |
| 50 | + userAgentsCache = await sdkLoadLocalAgents({ verbose: false }) |
| 51 | + // Build ID-to-filepath map by scanning all agent directories |
| 52 | + userAgentFilePaths = buildAgentFilePathMap(getDefaultAgentDirs()) |
| 53 | + } catch (error) { |
| 54 | + // Fall back to empty cache if SDK loading fails, but log a warning |
| 55 | + logger.warn({ error }, 'Failed to load user agents from .agents directories') |
| 56 | + userAgentsCache = {} |
| 57 | + userAgentFilePaths = new Map() |
52 | 58 | } |
53 | 59 | } |
54 | 60 |
|
55 | 61 | /** |
56 | | - * Scan agent directory and build a map from agent ID to source file path. |
| 62 | + * Get default agent directories to scan. |
| 63 | + * Matches the SDK's getDefaultAgentDirs() to ensure consistency. |
| 64 | + */ |
| 65 | +const getDefaultAgentDirs = (): string[] => { |
| 66 | + const cwdAgents = path.join(process.cwd(), AGENTS_DIR_NAME) |
| 67 | + const parentAgents = path.join(process.cwd(), '..', AGENTS_DIR_NAME) |
| 68 | + const homeAgents = path.join(os.homedir(), AGENTS_DIR_NAME) |
| 69 | + return [cwdAgents, parentAgents, homeAgents] |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * Scan agent directories and build a map from agent ID to source file path. |
57 | 74 | * Uses regex to extract IDs from files without requiring module loading. |
| 75 | + * Later directories in the list take precedence (can override earlier ones). |
58 | 76 | */ |
59 | | -const buildAgentFilePathMap = (agentsDir: string): Map<string, string> => { |
| 77 | +const buildAgentFilePathMap = (agentsDirs: string[]): Map<string, string> => { |
60 | 78 | const idToPath = new Map<string, string>() |
61 | 79 | const idRegex = /id\s*:\s*['"`]([^'"`]+)['"`]/i |
62 | 80 |
|
@@ -87,7 +105,10 @@ const buildAgentFilePathMap = (agentsDir: string): Map<string, string> => { |
87 | 105 | } |
88 | 106 | } |
89 | 107 |
|
90 | | - scanDirectory(agentsDir) |
| 108 | + // Scan all directories - later directories override earlier ones |
| 109 | + for (const agentsDir of agentsDirs) { |
| 110 | + scanDirectory(agentsDir) |
| 111 | + } |
91 | 112 | return idToPath |
92 | 113 | } |
93 | 114 |
|
|
0 commit comments