|
4 | 4 | across different AI tool handlers. |
5 | 5 | """ |
6 | 6 |
|
7 | | -import concurrent.futures |
8 | 7 | import json |
9 | 8 | import logging |
10 | | -import shutil |
11 | | -import threading |
12 | 9 | from pathlib import Path |
13 | 10 | from typing import Dict, List, Optional, Type |
14 | 11 |
|
15 | 12 | from ..repo_loader import RepoConfigLoader |
| 13 | +from ..fetching.base import BaseEntityFetcher, RepoConfig |
16 | 14 | from .base import BaseAgentHandler |
17 | 15 | from .claude import ClaudeAgentHandler |
18 | 16 | from .codebuddy import CodebuddyAgentHandler |
@@ -116,6 +114,10 @@ def __init__(self, config_dir: Optional[Path] = None): |
116 | 114 | self.repos_file = self.config_dir / "agent_repos.json" |
117 | 115 | self.config_dir.mkdir(parents=True, exist_ok=True) |
118 | 116 |
|
| 117 | + # Initialize fetcher with agent parser |
| 118 | + from ..fetching.parsers import AgentParser |
| 119 | + self.fetcher = BaseEntityFetcher(parser=AgentParser()) |
| 120 | + |
119 | 121 | # Initialize handlers |
120 | 122 | self._handlers: Dict[str, BaseAgentHandler] = {} |
121 | 123 | for app_name, handler_class in AGENT_HANDLERS.items(): |
@@ -311,120 +313,35 @@ def fetch_agents_from_repos(self, max_workers: int = 8) -> List[Agent]: |
311 | 313 | self._init_default_repos_file() |
312 | 314 | repos = self._load_repos() |
313 | 315 |
|
314 | | - # Filter enabled repos |
315 | | - enabled_repos = { |
316 | | - repo_id: repo for repo_id, repo in repos.items() if repo.enabled |
317 | | - } |
318 | | - |
319 | | - if not enabled_repos: |
320 | | - logger.warning("No enabled repositories found") |
321 | | - return [] |
| 316 | + # Convert AgentRepo objects to RepoConfig objects for the fetcher |
| 317 | + repo_configs = [ |
| 318 | + RepoConfig( |
| 319 | + owner=repo.owner, |
| 320 | + name=repo.name, |
| 321 | + branch=repo.branch, |
| 322 | + path=repo.agents_path, |
| 323 | + enabled=repo.enabled |
| 324 | + ) |
| 325 | + for repo in repos.values() |
| 326 | + ] |
322 | 327 |
|
323 | | - logger.info(f"Fetching agents from {len(enabled_repos)} repositories in parallel") |
| 328 | + # Fetch using unified fetcher |
| 329 | + agents = self.fetcher.fetch_from_repos( |
| 330 | + repos=repo_configs, |
| 331 | + max_workers=max_workers |
| 332 | + ) |
324 | 333 |
|
325 | | - all_agents = [] |
| 334 | + # Update installed status from existing agents |
326 | 335 | existing_agents = self._load_agents() |
327 | | - |
328 | | - # Thread-safe storage for results |
329 | | - agents_results = [] |
330 | | - lock = threading.Lock() |
331 | | - |
332 | | - # Use claude handler for fetching (all repos use same format) |
333 | | - handler = self.get_handler("claude") |
334 | | - |
335 | | - def process_repository(repo_id: str, repo: AgentRepo): |
336 | | - """Process a single repository to extract agents.""" |
337 | | - try: |
338 | | - agents = self._fetch_agents_from_repo(repo, handler) |
339 | | - for agent in agents: |
340 | | - if agent.key in existing_agents: |
341 | | - agent.installed = existing_agents[agent.key].installed |
342 | | - |
343 | | - with lock: |
344 | | - agents_results.extend(agents) |
345 | | - |
346 | | - logger.info(f"Found {len(agents)} agents in {repo_id}") |
347 | | - return len(agents) |
348 | | - except Exception as e: |
349 | | - logger.warning(f"Failed to fetch agents from {repo_id}: {e}") |
350 | | - return 0 |
351 | | - |
352 | | - # Use ThreadPoolExecutor for parallel processing |
353 | | - actual_workers = min(max_workers, len(enabled_repos)) |
354 | | - logger.debug(f"Using {actual_workers} concurrent workers") |
355 | | - |
356 | | - with concurrent.futures.ThreadPoolExecutor(max_workers=actual_workers) as executor: |
357 | | - # Submit all tasks |
358 | | - future_to_repo = { |
359 | | - executor.submit(process_repository, repo_id, repo): repo_id |
360 | | - for repo_id, repo in enabled_repos.items() |
361 | | - } |
362 | | - |
363 | | - # Wait for all tasks to complete |
364 | | - for future in concurrent.futures.as_completed(future_to_repo): |
365 | | - repo_id = future_to_repo[future] |
366 | | - try: |
367 | | - agent_count = future.result() |
368 | | - logger.debug(f"Completed processing {repo_id}: {agent_count} agents") |
369 | | - except Exception as e: |
370 | | - logger.error(f"Exception processing {repo_id}: {e}") |
371 | | - |
372 | | - all_agents = agents_results |
373 | | - |
374 | | - # Merge and save |
375 | | - for agent in all_agents: |
| 336 | + for agent in agents: |
| 337 | + if agent.key in existing_agents: |
| 338 | + agent.installed = existing_agents[agent.key].installed |
376 | 339 | existing_agents[agent.key] = agent |
377 | | - self._save_agents(existing_agents) |
378 | | - |
379 | | - logger.info(f"Total agents fetched: {len(all_agents)}") |
380 | | - return all_agents |
381 | | - |
382 | | - def _fetch_agents_from_repo( |
383 | | - self, repo: AgentRepo, handler: BaseAgentHandler |
384 | | - ) -> List[Agent]: |
385 | | - """Fetch agents from a single repository. |
386 | | -
|
387 | | - Args: |
388 | | - repo: The repository to fetch from |
389 | | - handler: The handler to use for parsing |
390 | 340 |
|
391 | | - Returns: |
392 | | - List of agents found |
393 | | - """ |
394 | | - # Use the new Fetcher class similar to awesome-claude-agents |
395 | | - fetcher = Fetcher() |
396 | | - |
397 | | - repo_data = { |
398 | | - "owner": repo.owner, |
399 | | - "name": repo.name, |
400 | | - "branch": repo.branch, |
401 | | - "agentsPath": repo.agents_path or "agents" |
402 | | - } |
403 | | - |
404 | | - agents_data = fetcher.fetch_agents_from_repo(repo_data) |
405 | | - agents = [] |
406 | | - |
407 | | - for agent_data in agents_data: |
408 | | - # Convert to Agent model |
409 | | - filename = agent_data["file_path"].split("/")[-1] if "/" in agent_data["file_path"] else agent_data["file_path"] |
410 | | - |
411 | | - agent = Agent( |
412 | | - key=f"{repo.owner}/{repo.name}:{agent_data['name']}", |
413 | | - name=agent_data["name"], |
414 | | - description=agent_data["description"], |
415 | | - filename=filename, |
416 | | - installed=False, |
417 | | - repo_owner=repo.owner, |
418 | | - repo_name=repo.name, |
419 | | - repo_branch=repo.branch, |
420 | | - agents_path=repo.agents_path, |
421 | | - readme_url=f"https://github.com/{repo.owner}/{repo.name}/blob/{repo.branch}/{agent_data['file_path']}", |
422 | | - tools=agent_data.get("source_data", {}).get("tools", []), |
423 | | - color=agent_data.get("source_data", {}).get("color"), |
424 | | - ) |
425 | | - agents.append(agent) |
426 | | - logger.debug(f"Found agent: {agent.key}") |
| 341 | + # Save updated agents |
| 342 | + self._save_agents(existing_agents) |
427 | 343 |
|
| 344 | + logger.info(f"Total agents fetched: {len(agents)}") |
428 | 345 | return agents |
429 | 346 |
|
430 | 347 | def fetch_agents_from_external_sources(self) -> List[Agent]: |
|
0 commit comments