|
19 | 19 | from .gemini import GeminiAgentHandler |
20 | 20 | from .opencode import OpenCodeAgentHandler |
21 | 21 | from .models import Agent, AgentRepo |
| 22 | +from ..fetcher import Fetcher |
22 | 23 |
|
23 | 24 | logger = logging.getLogger(__name__) |
24 | 25 |
|
@@ -314,55 +315,111 @@ def _fetch_agents_from_repo( |
314 | 315 | Returns: |
315 | 316 | List of agents found |
316 | 317 | """ |
317 | | - temp_dir, actual_branch = handler._download_repo( |
318 | | - repo.owner, repo.name, repo.branch |
319 | | - ) |
| 318 | + # Use the new Fetcher class similar to awesome-claude-agents |
| 319 | + fetcher = Fetcher() |
| 320 | + |
| 321 | + repo_data = { |
| 322 | + "owner": repo.owner, |
| 323 | + "name": repo.name, |
| 324 | + "branch": repo.branch, |
| 325 | + "agentsPath": repo.agents_path or "agents" |
| 326 | + } |
| 327 | + |
| 328 | + agents_data = fetcher.fetch_agents_from_repo(repo_data) |
320 | 329 | agents = [] |
321 | 330 |
|
| 331 | + for agent_data in agents_data: |
| 332 | + # Convert to Agent model |
| 333 | + filename = agent_data["file_path"].split("/")[-1] if "/" in agent_data["file_path"] else agent_data["file_path"] |
| 334 | + |
| 335 | + agent = Agent( |
| 336 | + key=f"{repo.owner}/{repo.name}:{agent_data['name']}", |
| 337 | + name=agent_data["name"], |
| 338 | + description=agent_data["description"], |
| 339 | + filename=filename, |
| 340 | + installed=False, |
| 341 | + repo_owner=repo.owner, |
| 342 | + repo_name=repo.name, |
| 343 | + repo_branch=repo.branch, |
| 344 | + agents_path=repo.agents_path, |
| 345 | + readme_url=f"https://github.com/{repo.owner}/{repo.name}/blob/{repo.branch}/{agent_data['file_path']}", |
| 346 | + tools=agent_data.get("source_data", {}).get("tools", []), |
| 347 | + color=agent_data.get("source_data", {}).get("color"), |
| 348 | + ) |
| 349 | + agents.append(agent) |
| 350 | + logger.debug(f"Found agent: {agent.key}") |
| 351 | + |
| 352 | + return agents |
| 353 | + |
| 354 | + def fetch_agents_from_external_sources(self) -> List[Agent]: |
| 355 | + """Fetch agents from external sources defined in agent_repos.json. |
| 356 | +
|
| 357 | + This uses the same method as awesome-claude-agents for fetching from |
| 358 | + external JSON sources. |
| 359 | +
|
| 360 | + Returns: |
| 361 | + List of discovered agents from all sources |
| 362 | + """ |
| 363 | + # Load the agent_repos.json file |
| 364 | + package_dir = Path(__file__).parent.parent |
| 365 | + repos_file = package_dir / "agent_repos.json" |
| 366 | + |
| 367 | + if not repos_file.exists(): |
| 368 | + logger.warning("agent_repos.json file not found") |
| 369 | + return [] |
| 370 | + |
322 | 371 | try: |
323 | | - scan_dir = temp_dir |
324 | | - if repo.agents_path: |
325 | | - scan_dir = temp_dir / repo.agents_path.strip("/") |
| 372 | + with open(repos_file, "r", encoding="utf-8") as f: |
| 373 | + repos_data = json.load(f) |
| 374 | + except Exception as e: |
| 375 | + logger.warning(f"Failed to load agent_repos.json: {e}") |
| 376 | + return [] |
326 | 377 |
|
327 | | - if not scan_dir.exists(): |
328 | | - logger.warning(f"Agents path not found: {scan_dir}") |
329 | | - return agents |
| 378 | + # Use the Fetcher to get agents from each repository |
| 379 | + fetcher = Fetcher() |
| 380 | + all_agents = [] |
| 381 | + existing_agents = self._load_agents() |
330 | 382 |
|
331 | | - for agent_file in scan_dir.glob("*.md"): |
332 | | - if not agent_file.is_file(): |
333 | | - continue |
| 383 | + for repo_id, repo_data in repos_data.items(): |
| 384 | + if not repo_data.get("enabled", True): |
| 385 | + logger.debug(f"Skipping disabled repository: {repo_id}") |
| 386 | + continue |
334 | 387 |
|
335 | | - # Skip README files |
336 | | - if agent_file.name.lower() in ["readme.md", "contributing.md"]: |
337 | | - continue |
| 388 | + try: |
| 389 | + agents_data = fetcher.fetch_agents_from_repo(repo_data) |
| 390 | + for agent_data in agents_data: |
| 391 | + # Convert to Agent model |
| 392 | + filename = agent_data["file_path"].split("/")[-1] if "/" in agent_data["file_path"] else agent_data["file_path"] |
| 393 | + |
| 394 | + agent_key = f"{repo_data['owner']}/{repo_data['name']}:{agent_data['name']}" |
| 395 | + |
| 396 | + agent = Agent( |
| 397 | + key=agent_key, |
| 398 | + name=agent_data["name"], |
| 399 | + description=agent_data["description"], |
| 400 | + filename=filename, |
| 401 | + installed=existing_agents.get(agent_key, Agent("", "", "", "", False)).installed, |
| 402 | + repo_owner=repo_data["owner"], |
| 403 | + repo_name=repo_data["name"], |
| 404 | + repo_branch=repo_data.get("branch", "main"), |
| 405 | + agents_path=repo_data.get("agentsPath", "agents"), |
| 406 | + readme_url=f"https://github.com/{repo_data['owner']}/{repo_data['name']}/blob/{repo_data.get('branch', 'main')}/{agent_data['file_path']}", |
| 407 | + tools=agent_data.get("source_data", {}).get("tools", []), |
| 408 | + color=agent_data.get("source_data", {}).get("color"), |
| 409 | + ) |
| 410 | + all_agents.append(agent) |
338 | 411 |
|
339 | | - meta = handler.parse_agent_metadata(agent_file) |
340 | | - filename = agent_file.name |
| 412 | + logger.info(f"Found {len(agents_data)} agents in {repo_id}") |
341 | 413 |
|
342 | | - path_from_repo_root = agent_file.relative_to(temp_dir) |
343 | | - readme_path = str(path_from_repo_root).replace("\\", "/") |
| 414 | + except Exception as e: |
| 415 | + logger.warning(f"Failed to fetch agents from {repo_id}: {e}") |
344 | 416 |
|
345 | | - agent = Agent( |
346 | | - key=f"{repo.owner}/{repo.name}:{meta.get('name', filename.replace('.md', ''))}", |
347 | | - name=meta.get("name", filename.replace(".md", "")), |
348 | | - description=meta.get("description", ""), |
349 | | - filename=filename, |
350 | | - installed=False, |
351 | | - repo_owner=repo.owner, |
352 | | - repo_name=repo.name, |
353 | | - repo_branch=actual_branch, |
354 | | - agents_path=repo.agents_path, |
355 | | - readme_url=f"https://github.com/{repo.owner}/{repo.name}/blob/{actual_branch}/{readme_path}", |
356 | | - tools=meta.get("tools", []), |
357 | | - color=meta.get("color"), |
358 | | - ) |
359 | | - agents.append(agent) |
360 | | - logger.debug(f"Found agent: {agent.key}") |
361 | | - finally: |
362 | | - if temp_dir.exists(): |
363 | | - shutil.rmtree(temp_dir) |
| 417 | + # Update existing agents database |
| 418 | + for agent in all_agents: |
| 419 | + existing_agents[agent.key] = agent |
| 420 | + self._save_agents(existing_agents) |
364 | 421 |
|
365 | | - return agents |
| 422 | + return all_agents |
366 | 423 |
|
367 | 424 | def sync_installed_status(self, app_type: str = "claude") -> None: |
368 | 425 | """Sync the installed status of all agents. |
|
0 commit comments