Skip to main content

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 "" python3 invoke-agent.py git-workflow-orchestrator "sync all submodules" python3 invoke-agent.py codi-documentation-writer "update README.md"

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 """) print(" python3 invoke-agent.py --list") print(" python3 invoke-agent.py --info ") print() print("Examples:") print(" python3 invoke-agent.py git-workflow-orchestrator "sync all submodules"") print(" python3 invoke-agent.py codi-documentation-writer "update README.md"") print(" python3 invoke-agent.py orchestrator "coordinate multi-module development"") print() print("Output: Task() call ready for Claude Code execution") sys.exit(0)

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()