scripts-invoke-agent
#!/usr/bin/env python3 """ Universal CODITECT Agent Invoker
ONE script to invoke ANY of the 130+ CODITECT agents without needing individual wrappers, skills, or complex dispatchers.
Usage:
python3 invoke-agent.py
Output: Generates proper Task() call that can be executed by Claude Code.
Architecture: CODITECT agents (130+) are defined in agents/*.md files Claude Code Task tool has ~52 built-in subagent_types This script bridges the gap by using general-purpose as proxy:
/agent <name> <task>
↓
invoke-agent.py reads agents/<name>.md
↓
Task(subagent_type="general-purpose",
prompt="You are <agent>. <system_prompt>. Task: <task>")
Author: CODITECT Team Version: 1.0.0 Created: 2025-12-22 """
import sys import re import json from pathlib import Path from typing import Optional, Dict, Tuple
def find_framework_root() -> Path: """Find the CODITECT framework root (coditect-core directory)""" # Start from script location: scripts/core/invoke-agent.py current = Path(file).resolve()
# Go up to coditect-core root (2 levels: scripts/core -> scripts -> coditect-core)
framework_root = current.parent.parent.parent
# Verify by checking for agents directory
if (framework_root / "agents").exists():
return framework_root
# Fallback: search upward for agents directory
for parent in current.parents:
if (parent / "agents").exists():
return parent
raise FileNotFoundError("Could not find CODITECT framework root (agents/ directory)")
def parse_agent_markdown(agent_path: Path) -> Tuple[Dict, str]: """ Parse agent markdown file to extract frontmatter and system prompt.
Returns:
Tuple of (frontmatter_dict, system_prompt_text)
"""
content = agent_path.read_text(encoding='utf-8')
# Check for YAML frontmatter (between --- markers)
frontmatter_match = re.match(r'^---\n(.*?)\n---\n(.*)$', content, re.DOTALL)
if frontmatter_match:
frontmatter_text = frontmatter_match.group(1)
system_prompt = frontmatter_match.group(2).strip()
# Simple YAML parsing (avoid external dependency)
frontmatter = {}
for line in frontmatter_text.split('\n'):
if ':' in line:
key, value = line.split(':', 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
frontmatter[key] = value
return frontmatter, system_prompt
else:
# No frontmatter, entire content is system prompt
return {}, content.strip()
def get_agent_info(agent_name: str, framework_root: Path) -> Dict: """ Get agent information from markdown file.
Args:
agent_name: Name of the agent (e.g., 'git-workflow-orchestrator')
framework_root: Path to coditect-core root
Returns:
Dict with agent info including system_prompt, title, model, etc.
"""
agents_dir = framework_root / "agents"
agent_path = agents_dir / f"{agent_name}.md"
if not agent_path.exists():
# Try with common variations
variations = [
f"{agent_name}.md",
f"{agent_name}-agent.md",
f"{agent_name.replace('-', '_')}.md",
]
for var in variations:
test_path = agents_dir / var
if test_path.exists():
agent_path = test_path
break
else:
raise FileNotFoundError(f"Agent not found: {agent_name}\nSearched in: {agents_dir}")
frontmatter, system_prompt = parse_agent_markdown(agent_path)
return {
"name": agent_name,
"title": frontmatter.get("title", agent_name.replace("-", " ").title()),
"model": frontmatter.get("model", "sonnet"),
"tools": frontmatter.get("tools", "Read, Write, Edit, Bash, Grep, Glob, TodoWrite"),
"system_prompt": system_prompt,
"path": str(agent_path),
}
def generate_task_call(agent_info: Dict, task: str, output_format: str = "text") -> str: """ Generate a Task() call that can be executed by Claude Code.
Args:
agent_info: Dict with agent name, system_prompt, model, etc.
task: The task description to execute
output_format: "text" for readable output, "json" for structured output
Returns:
Formatted Task() call string
"""
agent_name = agent_info["name"]
title = agent_info["title"]
model = agent_info["model"]
# Truncate system prompt for the prompt (keep it readable)
system_prompt = agent_info["system_prompt"]
if len(system_prompt) > 2000:
system_prompt = system_prompt[:2000] + "\n\n[... system prompt truncated for brevity ...]"
# Escape triple quotes in system prompt
system_prompt_escaped = system_prompt.replace('"""', '\\"\\"\\"')
if output_format == "json":
return json.dumps({
"subagent_type": "general-purpose",
"model": model,
"description": f"Invoke {title}",
"prompt": f"Use {agent_name} subagent to {task}",
"agent_context": system_prompt[:500] + "..." if len(system_prompt) > 500 else system_prompt
}, indent=2)
# Generate human-readable Task() call
task_call = f'''Task(
subagent_type="general-purpose",
model="{model}",
description="Invoke {title}",
prompt="""
You are the {title}. Your role and capabilities are defined below.
Agent System Prompt
{system_prompt_escaped}
Current Task
{task}
Execute this task according to your defined responsibilities and capabilities. Report progress and results clearly. """ )'''
return task_call
def list_available_agents(framework_root: Path) -> list: """List all available agents""" agents_dir = framework_root / "agents" agents = []
for agent_file in sorted(agents_dir.glob("*.md")):
if agent_file.name != "README.md":
agent_name = agent_file.stem
agents.append(agent_name)
return agents
def main():
"""Main entry point"""
# Parse arguments
if len(sys.argv) < 2:
print("CODITECT Universal Agent Invoker v1.0.0")
print()
print("Usage:")
print(" python3 invoke-agent.py
framework_root = find_framework_root()
# Handle --list flag
if sys.argv[1] == "--list":
agents = list_available_agents(framework_root)
print(f"Available CODITECT agents ({len(agents)}):\n")
for i, agent in enumerate(agents, 1):
print(f" {i:3}. {agent}")
sys.exit(0)
# Handle --info flag
if sys.argv[1] == "--info":
if len(sys.argv) < 3:
print("Usage: python3 invoke-agent.py --info <agent-name>")
sys.exit(1)
agent_name = sys.argv[2]
try:
info = get_agent_info(agent_name, framework_root)
print(f"Agent: {info['name']}")
print(f"Title: {info['title']}")
print(f"Model: {info['model']}")
print(f"Tools: {info['tools']}")
print(f"Path: {info['path']}")
print(f"\nSystem Prompt Preview (first 500 chars):")
print("-" * 50)
print(info['system_prompt'][:500])
if len(info['system_prompt']) > 500:
print(f"\n... ({len(info['system_prompt'])} total characters)")
except FileNotFoundError as e:
print(f"Error: {e}")
sys.exit(1)
sys.exit(0)
# Handle --json flag
output_format = "text"
if "--json" in sys.argv:
output_format = "json"
sys.argv.remove("--json")
# Normal invocation: agent-name "task"
agent_name = sys.argv[1]
task = sys.argv[2] if len(sys.argv) > 2 else "Execute your default workflow"
try:
agent_info = get_agent_info(agent_name, framework_root)
task_call = generate_task_call(agent_info, task, output_format)
if output_format == "text":
print(f"# Invoking: {agent_info['title']}")
print(f"# Agent: {agent_name}")
print(f"# Task: {task}")
print("#" + "-" * 60)
print()
print(task_call)
except FileNotFoundError as e:
print(f"Error: {e}")
print("\nRun 'python3 invoke-agent.py --list' to see available agents")
sys.exit(1)
if name == "main": main()