Design patterns for building autonomous coding agents, inspired by [Cline](https://github.com/cline/cline) and [OpenAI Codex](https://github.com/openai/codex).
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Design patterns for building autonomous coding agents, inspired by [Cline](https://github.com/cline/cline) and [OpenAI Codex](https://github.com/openai/codex).
classAgentLoop:
def__init__(self, llm, tools, max_iterations=50):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.max_iterations = max_iterations
self.history = []
defrun(self, task: str) -> str:
self.history.append({"role": "user", "content": task})
for i in (.max_iterations):
response = .llm.chat(
messages=.history,
tools=._format_tools(),
tool_choice=
)
response.tool_calls:
tool_call response.tool_calls:
result = ._execute_tool(tool_call)
.history.append({
: ,
: tool_call.,
: (result)
})
:
response.content
() -> :
tool = .tools[tool_call.name]
args = json.loads(tool_call.arguments)
tool.execute(**args)
range
self
# Think: Get LLM response with tool options
self
self
self
"auto"
# Decide: Check if agent wants to use a tool
if
for
in
# Act: Execute the tool
self
# Observe: Add result to history
self
"role"
"tool"
"tool_call_id"
id
"content"
str
else
# No more tool calls = task complete
return
return
"Max iterations reached"
def
_execute_tool
self, tool_call
Any
self
return
1.2 Multi-Model Architecture
classMultiModelAgent:
"""
Use different models for different purposes:
- Fast model for planning
- Powerful model for complex reasoning
- Specialized model for code generation
"""def__init__(self):
self.models = {
"fast": "gpt-3.5-turbo", # Quick decisions"smart": "gpt-4-turbo", # Complex reasoning"code": "claude-3-sonnet", # Code generation
}
defselect_model(self, task_type: str) -> str:
if task_type == "planning":
returnself.models["fast"]
elif task_type == "analysis":
returnself.models["smart"]
elif task_type == "code":
returnself.models["code"]
returnself.models["smart"]
2. Tool Design Patterns
2.1 Tool Schema
classTool:
"""Base class for agent tools""" @propertydefschema(self) -> dict:
"""JSON Schema for the tool"""return {
"name": self.name,
"description": self.description,
"parameters": {
"type": "object",
"properties": self._get_parameters(),
"required": self._get_required()
}
}
defexecute(self, **kwargs) -> ToolResult:
"""Execute the tool and return result"""raise NotImplementedError
classReadFileTool(Tool):
name = "read_file"
description = "Read the contents of a file from the filesystem"def_get_parameters(self):
return {
"path": {
"type": "string",
"description": "Absolute path to the file"
},
"start_line": {
"type": "integer",
"description": "Line to start reading from (1-indexed)"
},
"end_line": {
"type": "integer",
"description": "Line to stop reading at (inclusive)"
}
}
def_get_required(self):
return ["path"]
defexecute(self, path: str, start_line: int = None, end_line: int = None) -> ToolResult:
try:
withopen(path, 'r') as f:
lines = f.readlines()
if start_line and end_line:
lines = lines[start_line-1:end_line]
return ToolResult(
success=True,
output="".join(lines)
)
except FileNotFoundError:
return ToolResult(
success=False,
error=f"File not found: {path}"
)
2.2 Essential Agent Tools
CODING_AGENT_TOOLS = {
# File operations"read_file": "Read file contents",
"write_file": "Create or overwrite a file",
"edit_file": "Make targeted edits to a file",
"list_directory": "List files and folders",
"search_files": "Search for files by pattern",
# Code understanding"search_code": "Search for code patterns (grep)",
"get_definition": "Find function/class definition",
"get_references": "Find all references to a symbol",
# Terminal"run_command": "Execute a shell command",
"read_output": "Read command output",
"send_input": "Send input to running command",
# Browser (optional)"open_browser": "Open URL in browser",
"click_element": "Click on page element",
"type_text": "Type text into input",
"screenshot": "Capture screenshot",
# Context"ask_user": "Ask the user a question",
"search_web": "Search the web for information"
}
2.3 Edit Tool Design
classEditFileTool(Tool):
"""
Precise file editing with conflict detection.
Uses search/replace pattern for reliable edits.
"""
name = "edit_file"
description = "Edit a file by replacing specific content"defexecute(
self,
path: str,
search: str,
replace: str,
expected_occurrences: int = 1) -> ToolResult:
"""
Args:
path: File to edit
search: Exact text to find (must match exactly, including whitespace)
replace: Text to replace with
expected_occurrences: How many times search should appear (validation)
"""withopen(path, 'r') as f:
content = f.read()
# Validate
actual_occurrences = content.count(search)
if actual_occurrences != expected_occurrences:
return ToolResult(
success=False,
error=f"Expected {expected_occurrences} occurrences, found {actual_occurrences}"
)
if actual_occurrences == 0:
return ToolResult(
success=False,
error="Search text not found in file"
)
# Apply edit
new_content = content.replace(search, replace)
withopen(path, 'w') as f:
f.write(new_content)
return ToolResult(
success=True,
output=f"Replaced {actual_occurrences} occurrence(s)"
)
3. Permission & Safety Patterns
3.1 Permission Levels
classPermissionLevel(Enum):
# Fully automatic - no user approval needed
AUTO = "auto"# Ask once per session
ASK_ONCE = "ask_once"# Ask every time
ASK_EACH = "ask_each"# Never allow
NEVER = "never"
PERMISSION_CONFIG = {
# Low risk - can auto-approve"read_file": PermissionLevel.AUTO,
"list_directory": PermissionLevel.AUTO,
"search_code": PermissionLevel.AUTO,
# Medium risk - ask once"write_file": PermissionLevel.ASK_ONCE,
"edit_file": PermissionLevel.ASK_ONCE,
# High risk - ask each time"run_command": PermissionLevel.ASK_EACH,
"delete_file": PermissionLevel.ASK_EACH,
# Dangerous - never auto-approve"sudo_command": PermissionLevel.NEVER,
"format_disk": PermissionLevel.NEVER
}
3.2 Approval UI Pattern
classApprovalManager:
def__init__(self, ui, config):
self.ui = ui
self.config = config
self.session_approvals = {}
defrequest_approval(self, tool_name: str, args: dict) -> bool:
level = self.config.get(tool_name, PermissionLevel.ASK_EACH)
if level == PermissionLevel.AUTO:
returnTrueif level == PermissionLevel.NEVER:
self.ui.show_error(f"Tool '{tool_name}' is not allowed")
returnFalseif level == PermissionLevel.ASK_ONCE:
if tool_name inself.session_approvals:
returnself.session_approvals[tool_name]
# Show approval dialog
approved = self.ui.show_approval_dialog(
tool=tool_name,
args=args,
risk_level=self._assess_risk(tool_name, args)
)
if level == PermissionLevel.ASK_ONCE:
self.session_approvals[tool_name] = approved
return approved
def_assess_risk(self, tool_name: str, args: dict) -> str:
"""Analyze specific call for risk level"""if tool_name == "run_command":
cmd = args.get("command", "")
ifany(danger in cmd for danger in ["rm -rf", "sudo", "chmod"]):
return"HIGH"return"MEDIUM"
from mcp import Server, Tool
classMCPAgent:
"""
Agent that can dynamically discover and use MCP tools.
'Add a tool that...' pattern from Cline.
"""def__init__(self, llm):
self.llm = llm
self.mcp_servers = {}
self.available_tools = {}
defconnect_server(self, name: str, config: dict) -> None:
"""Connect to an MCP server"""
server = Server(config)
self.mcp_servers[name] = server
# Discover tools
tools = server.list_tools()
for tool in tools:
self.available_tools[tool.name] = {
"server": name,
"schema": tool.schema
}
asyncdefcreate_tool(self, description: str) -> str:
"""
Create a new MCP server based on user description.
'Add a tool that fetches Jira tickets'
"""# Generate MCP server code
code = self.llm.generate(f"""
Create a Python MCP server with a tool that does:
{description}
Use the FastMCP framework. Include proper error handling.
Return only the Python code.
""")
# Save and install
server_name = self._extract_name(description)
path = f"./mcp_servers/{server_name}/server.py"withopen(path, 'w') as f:
f.write(code)
# Hot-reloadself.connect_server(server_name, {"path": path})
returnf"Created tool: {server_name}"