#!/usr/bin/env python3 """ Show information about available agents. Usage: python3 agent-info.py [--tree] [name] """ import argparse import json import re import sys from pathlib import Path from typing import Dict, List, Optional CLAUDE_DIR = Path.home() / ".claude" AGENTS_DIR = CLAUDE_DIR / "agents" REGISTRY_PATH = CLAUDE_DIR / "state" / "component-registry.json" # Agent hierarchy (from CLAUDE.md) HIERARCHY = { "personal-assistant": { "supervisor": None, "subordinates": ["master-orchestrator"] }, "master-orchestrator": { "supervisor": "personal-assistant", "subordinates": ["linux-sysadmin", "k8s-orchestrator", "programmer-orchestrator"] }, "linux-sysadmin": { "supervisor": "master-orchestrator", "subordinates": [] }, "k8s-orchestrator": { "supervisor": "master-orchestrator", "subordinates": ["k8s-diagnostician", "argocd-operator", "prometheus-analyst", "git-operator"] }, "k8s-diagnostician": { "supervisor": "k8s-orchestrator", "subordinates": [] }, "argocd-operator": { "supervisor": "k8s-orchestrator", "subordinates": [] }, "prometheus-analyst": { "supervisor": "k8s-orchestrator", "subordinates": [] }, "git-operator": { "supervisor": "k8s-orchestrator", "subordinates": [] }, "programmer-orchestrator": { "supervisor": "master-orchestrator", "subordinates": ["code-planner", "code-implementer", "code-reviewer"] }, "code-planner": { "supervisor": "programmer-orchestrator", "subordinates": [] }, "code-implementer": { "supervisor": "programmer-orchestrator", "subordinates": [] }, "code-reviewer": { "supervisor": "programmer-orchestrator", "subordinates": [] } } def load_registry() -> Dict: """Load component registry.""" try: with open(REGISTRY_PATH) as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return {} def find_agent_files() -> List[Path]: """Find all agent markdown files.""" if not AGENTS_DIR.exists(): return [] return [f for f in AGENTS_DIR.glob("*.md") if f.name != "README.md"] def parse_agent_md(path: Path) -> Dict: """Parse an agent markdown file for metadata.""" try: content = path.read_text() result = { "name": path.stem, "path": str(path.relative_to(CLAUDE_DIR)), "description": "", "model": "unknown", "tools": [], } # Parse YAML frontmatter if content.startswith("---"): parts = content.split("---", 2) if len(parts) >= 2: frontmatter = parts[1] for line in frontmatter.strip().split("\n"): if ":" in line: key, value = line.split(":", 1) key = key.strip() value = value.strip() if key == "name": result["name"] = value elif key == "description": result["description"] = value elif key == "model": result["model"] = value elif key == "tools": result["tools"] = [t.strip() for t in value.split(",")] return result except Exception as e: return {"name": path.stem, "error": str(e)} def get_model_emoji(model: str) -> str: """Get emoji for model type.""" return { "opus": "šŸ”·", "sonnet": "šŸ”¶", "haiku": "šŸ”ø" }.get(model.lower(), "ā—‹") def list_agents(): """List all available agents.""" registry = load_registry() reg_agents = registry.get("agents", {}) print(f"\nšŸ¤– Available Agents ({len(reg_agents)})\n") # Group by model by_model = {"opus": [], "sonnet": [], "haiku": [], "unknown": []} for name, info in reg_agents.items(): model = info.get("model", "unknown") by_model.get(model, by_model["unknown"]).append({ "name": name, "description": info.get("description", "No description"), "triggers": info.get("triggers", []) }) for model in ["opus", "sonnet", "haiku"]: agents = by_model[model] if not agents: continue emoji = get_model_emoji(model) print(f"=== {model.title()} {emoji} ===") for agent in sorted(agents, key=lambda a: a["name"]): print(f" {agent['name']}") print(f" {agent['description']}") if agent['triggers']: print(f" Triggers: {', '.join(agent['triggers'][:3])}") print("") def show_tree(): """Show agent hierarchy as a tree.""" print(f"\n🌳 Agent Hierarchy\n") def print_tree(name: str, prefix: str = "", is_last: bool = True): info = HIERARCHY.get(name, {}) registry = load_registry() reg_info = registry.get("agents", {}).get(name, {}) model = reg_info.get("model", "?") emoji = get_model_emoji(model) connector = "└── " if is_last else "ā”œā”€ā”€ " print(f"{prefix}{connector}{name} {emoji} ({model})") new_prefix = prefix + (" " if is_last else "│ ") subordinates = info.get("subordinates", []) for i, sub in enumerate(subordinates): print_tree(sub, new_prefix, i == len(subordinates) - 1) # Start from root print_tree("personal-assistant") print("") print("Legend: šŸ”· opus šŸ”¶ sonnet šŸ”ø haiku") print("") def show_agent(name: str): """Show details for a specific agent.""" registry = load_registry() reg_agents = registry.get("agents", {}) # Find matching agent matches = [n for n in reg_agents.keys() if name.lower() in n.lower()] if not matches: print(f"Agent '{name}' not found.") print("\nAvailable agents:") for n in sorted(reg_agents.keys()): print(f" - {n}") return if len(matches) > 1 and name not in matches: print(f"Multiple matches for '{name}':") for m in matches: print(f" - {m}") return agent_name = name if name in matches else matches[0] reg_info = reg_agents[agent_name] print(f"\nšŸ¤– Agent: {agent_name}\n") model = reg_info.get("model", "unknown") print(f"Model: {model} {get_model_emoji(model)}") print(f"Description: {reg_info.get('description', 'No description')}") # Triggers triggers = reg_info.get("triggers", []) if triggers: print(f"\nTriggers:") for t in triggers: print(f" - {t}") # Hierarchy hier = HIERARCHY.get(agent_name, {}) supervisor = hier.get("supervisor") subordinates = hier.get("subordinates", []) print(f"\nHierarchy:") if supervisor: print(f" Supervisor: {supervisor}") else: print(f" Supervisor: (top-level)") if subordinates: print(f" Subordinates:") for sub in subordinates: sub_info = reg_agents.get(sub, {}) sub_model = sub_info.get("model", "?") print(f" - {sub} ({sub_model})") # Check for agent file agent_file = AGENTS_DIR / f"{agent_name}.md" if agent_file.exists(): print(f"\nFile: agents/{agent_name}.md") file_info = parse_agent_md(agent_file) if file_info.get("tools"): print(f"Tools: {', '.join(file_info['tools'])}") print("") def main(): parser = argparse.ArgumentParser(description="Show agent information") parser.add_argument("name", nargs="?", help="Agent name to show details") parser.add_argument("--tree", "-t", action="store_true", help="Show agent hierarchy tree") parser.add_argument("--list", "-l", action="store_true", help="List all agents") args = parser.parse_args() if args.tree: show_tree() elif args.name and not args.list: show_agent(args.name) else: list_agents() if __name__ == "__main__": main()