scripts-adk-coditect-adapter
#!/usr/bin/env python3 """ ADK-CODITECT Adapter
Adapt CODITECT agent definitions to Google ADK format. Enables running CODITECT agents through the ADK framework.
Usage: python3 adk-coditect-adapter.py convert agents/my-agent.md output/ python3 adk-coditect-adapter.py list ~/.coditect/agents/ python3 adk-coditect-adapter.py validate agents/my_agent/ """
import argparse import json import os import re import sys import yaml from pathlib import Path from typing import Any
def parse_frontmatter(content: str) -> tuple[dict, str]: """Parse YAML frontmatter from markdown content.""" if not content.startswith("---"): return {}, content
end = content.find("---", 3)
if end == -1:
return {}, content
try:
frontmatter = yaml.safe_load(content[3:end])
body = content[end + 3:].strip()
return frontmatter or {}, body
except yaml.YAMLError:
return {}, content
def extract_instruction(body: str) -> str: """Extract instruction content from agent markdown body.""" # Remove headers and code blocks, keep content lines = [] in_code_block = False
for line in body.split("\n"):
if line.startswith("```"):
in_code_block = not in_code_block
continue
if in_code_block:
continue
if line.startswith("#"):
continue
if line.strip().startswith("|"): # Table
continue
if line.strip():
lines.append(line)
return "\n".join(lines[:50]) # Limit instruction length
def coditect_to_adk_model(model: str) -> str: """Map CODITECT model names to ADK model names.""" mapping = { "opus": "gemini-2.5-flash", # No direct equivalent, use capable model "sonnet": "gemini-2.5-flash", "haiku": "gemini-2.0-flash-lite", "claude-sonnet-4": "gemini-2.5-flash", "claude-opus-4": "gemini-2.5-flash", } return mapping.get(model.lower(), "gemini-2.5-flash")
def coditect_to_adk_tools(tools_str: str) -> list[str]: """Map CODITECT tools to ADK tool imports.""" tool_mapping = { "read": "# File reading handled by agent", "write": "# File writing handled by agent", "edit": "# File editing handled by agent", "bash": "# Shell execution handled by agent", "glob": "# File search handled by agent", "grep": "# Content search handled by agent", "task": "# Sub-agent delegation", "todowrite": "# Task tracking", "websearch": "google_search", "webfetch": "# Web fetching", }
tools = []
if tools_str:
for tool in tools_str.split(","):
tool = tool.strip().lower()
mapped = tool_mapping.get(tool)
if mapped and not mapped.startswith("#"):
tools.append(mapped)
return tools
def generate_adk_agent( name: str, frontmatter: dict, instruction: str, output_dir: Path ) -> Path: """Generate ADK agent files from CODITECT agent definition.""" # Create agent directory agent_dir = output_dir / name.replace("-", "_") agent_dir.mkdir(parents=True, exist_ok=True)
# Determine model
model = coditect_to_adk_model(frontmatter.get("model", "sonnet"))
# Determine tools
tools = coditect_to_adk_tools(frontmatter.get("tools", ""))
# Generate __init__.py
init_content = "from . import agent\n"
(agent_dir / "__init__.py").write_text(init_content)
# Generate agent.py
tools_import = ""
tools_list = "[]"
if "google_search" in tools:
tools_import = "from google.adk.tools import google_search\n"
tools_list = "[google_search]"
agent_content = f'''"""
{frontmatter.get('title', name)} Agent
Auto-generated from CODITECT agent definition. Original: agents/{name}.md
{frontmatter.get('summary', '')} """
from google.adk.agents import Agent {tools_import}
root_agent = Agent( name="{name.replace('-', '_')}", model="{model}", description="""{frontmatter.get('summary', 'CODITECT agent adapted for ADK.')}""", instruction="""{instruction[:2000]}""", tools={tools_list}, ) '''
(agent_dir / "agent.py").write_text(agent_content)
# Generate README
readme_content = f"""# {frontmatter.get('title', name)}
{frontmatter.get('summary', '')}
Original CODITECT Agent
- Source:
agents/{name}.md - Type: {frontmatter.get('agent_type', 'specialist')}
- Domain: {frontmatter.get('domain', [])}
Running with ADK
# Interactive
adk run {agent_dir.name}
# Web UI
adk web {output_dir}
Auto-Generated
This agent was auto-generated by adk-coditect-adapter.py.
To modify, update the original CODITECT agent and re-run conversion.
"""
(agent_dir / "README.md").write_text(readme_content)
return agent_dir
def convert_agent(input_path: Path, output_dir: Path) -> Path: """Convert a single CODITECT agent to ADK format.""" content = input_path.read_text() frontmatter, body = parse_frontmatter(content)
# Extract agent name
name = input_path.stem
# Extract instruction
instruction = extract_instruction(body)
# Generate ADK agent
return generate_adk_agent(name, frontmatter, instruction, output_dir)
def list_agents(agents_dir: Path) -> list[dict]: """List all CODITECT agents in a directory.""" agents = []
for path in agents_dir.glob("*.md"):
content = path.read_text()
frontmatter, _ = parse_frontmatter(content)
agents.append({
"name": path.stem,
"title": frontmatter.get("title", path.stem),
"type": frontmatter.get("agent_type", "specialist"),
"model": frontmatter.get("model", "unknown"),
"summary": frontmatter.get("summary", "")[:100],
"path": str(path),
})
return agents
def validate_adk_agent(agent_dir: Path) -> list[str]: """Validate an ADK agent directory structure.""" errors = []
if not agent_dir.is_dir():
errors.append(f"Not a directory: {agent_dir}")
return errors
init_path = agent_dir / "__init__.py"
if not init_path.exists():
errors.append("Missing __init__.py")
else:
init_content = init_path.read_text()
if "from . import agent" not in init_content:
errors.append("__init__.py missing 'from . import agent'")
agent_path = agent_dir / "agent.py"
if not agent_path.exists():
errors.append("Missing agent.py")
else:
agent_content = agent_path.read_text()
if "root_agent" not in agent_content and "app" not in agent_content:
errors.append("agent.py missing 'root_agent' or 'app' definition")
return errors
def main(): parser = argparse.ArgumentParser(description="ADK-CODITECT Adapter") subparsers = parser.add_subparsers(dest="command", required=True)
# Convert command
convert_parser = subparsers.add_parser("convert", help="Convert CODITECT agent to ADK")
convert_parser.add_argument("input", help="CODITECT agent file (.md)")
convert_parser.add_argument("output", help="Output directory for ADK agent")
# List command
list_parser = subparsers.add_parser("list", help="List CODITECT agents")
list_parser.add_argument("agents_dir", help="Agents directory")
list_parser.add_argument("--json", action="store_true", help="Output as JSON")
# Validate command
validate_parser = subparsers.add_parser("validate", help="Validate ADK agent")
validate_parser.add_argument("agent_dir", help="ADK agent directory")
# Batch convert command
batch_parser = subparsers.add_parser("batch", help="Batch convert agents")
batch_parser.add_argument("agents_dir", help="CODITECT agents directory")
batch_parser.add_argument("output", help="Output directory")
batch_parser.add_argument("--filter", help="Filter agents by name pattern")
args = parser.parse_args()
if args.command == "convert":
input_path = Path(args.input)
output_dir = Path(args.output)
if not input_path.exists():
print(f"Error: Input file not found: {input_path}")
sys.exit(1)
result_dir = convert_agent(input_path, output_dir)
print(f"Converted: {input_path.name} -> {result_dir}")
elif args.command == "list":
agents_dir = Path(args.agents_dir)
agents = list_agents(agents_dir)
if args.json:
print(json.dumps(agents, indent=2))
else:
print(f"Found {len(agents)} agents:\n")
for agent in agents:
print(f" {agent['name']}")
print(f" Type: {agent['type']}, Model: {agent['model']}")
print(f" {agent['summary']}")
print()
elif args.command == "validate":
agent_dir = Path(args.agent_dir)
errors = validate_adk_agent(agent_dir)
if errors:
print("Validation errors:")
for error in errors:
print(f" - {error}")
sys.exit(1)
else:
print("Valid ADK agent structure")
elif args.command == "batch":
agents_dir = Path(args.agents_dir)
output_dir = Path(args.output)
pattern = args.filter or "*"
converted = 0
for path in agents_dir.glob(f"{pattern}.md"):
try:
result_dir = convert_agent(path, output_dir)
print(f"Converted: {path.name}")
converted += 1
except Exception as e:
print(f"Failed: {path.name} - {e}")
print(f"\nConverted {converted} agents to {output_dir}")
if name == "main": main()