* Build a map of skill-name -> [agentId] by scanning all workspace skill dirs
()
| 175 | * Build a map of skill-name -> [agentId] by scanning all workspace skill dirs |
| 176 | */ |
| 177 | function buildAgentSkillMap(): Map<string, string[]> { |
| 178 | const map = new Map<string, string[]>(); |
| 179 | const openclawDir = process.env.OPENCLAW_DIR || path.join(homedir(), '.openclaw'); |
| 180 | // Read from openclaw.json if possible |
| 181 | let agentList: Array<{ id: string; workspace: string }> = []; |
| 182 | try { |
| 183 | const openclawConfig = JSON.parse(fs.readFileSync(path.join(openclawDir, 'openclaw.json'), 'utf-8')); |
| 184 | agentList = (openclawConfig?.agents?.list || []).map((a: any) => ({ |
| 185 | id: a.id, |
| 186 | workspace: a.workspace || path.join(openclawDir, 'workspace'), |
| 187 | })); |
| 188 | } catch { |
| 189 | // Fallback: scan directories |
| 190 | try { |
| 191 | const dirs = fs.readdirSync(openclawDir, { withFileTypes: true }); |
| 192 | for (const d of dirs) { |
| 193 | if (d.isDirectory() && d.name.startsWith('workspace')) { |
| 194 | const agentId = d.name === 'workspace' ? 'main' : d.name.replace('workspace-', ''); |
| 195 | agentList.push({ id: agentId, workspace: path.join(openclawDir, d.name) }); |
| 196 | } |
| 197 | } |
| 198 | } catch {} |
| 199 | } |
| 200 | |
| 201 | for (const { id, workspace } of agentList) { |
| 202 | const skillsDir = path.join(workspace, 'skills'); |
| 203 | try { |
| 204 | if (!fs.existsSync(skillsDir)) continue; |
| 205 | const skillDirs = fs.readdirSync(skillsDir, { withFileTypes: true }); |
| 206 | for (const d of skillDirs) { |
| 207 | if (d.isDirectory()) { |
| 208 | const existing = map.get(d.name) || []; |
| 209 | existing.push(id); |
| 210 | map.set(d.name, existing); |
| 211 | } |
| 212 | } |
| 213 | } catch {} |
| 214 | } |
| 215 | |
| 216 | return map; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Load configured skills from config file |