(cwd: string)
| 174 | |
| 175 | /** Load Claude Code agents (plugins + standalone .claude/agents) as QodeX roles. */ |
| 176 | export async function loadClaudeCodeAgents(cwd: string): Promise<Record<string, RoleConfig>> { |
| 177 | if (disabled()) return {}; |
| 178 | const dirs: string[] = []; |
| 179 | try { |
| 180 | for (const p of await relevantPluginPaths(cwd)) dirs.push(path.join(p.path, 'agents')); |
| 181 | } catch { /* ignore */ } |
| 182 | dirs.push(path.join(claudeHome(), 'agents')); // user standalone |
| 183 | dirs.push(path.join(cwd, '.claude', 'agents')); // project standalone (wins — listed last) |
| 184 | |
| 185 | const out: Record<string, RoleConfig> = {}; |
| 186 | for (const dir of dirs) { |
| 187 | let entries; |
| 188 | try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { continue; } |
| 189 | for (const ent of entries) { |
| 190 | if (!ent.isFile() || !ent.name.endsWith('.md')) continue; |
| 191 | try { |
| 192 | const parsed = parseAgent(await fs.readFile(path.join(dir, ent.name), 'utf-8'), ent.name.slice(0, -3)); |
| 193 | if (parsed) out[parsed.name] = parsed.role; // later dirs override earlier |
| 194 | } catch (e: any) { |
| 195 | logger.debug('Failed to parse Claude Code agent', { file: ent.name, err: e?.message }); |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | return out; |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * Merge imported Claude Code agents into config.roles (mutates config). User-defined |
no test coverage detected