Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Design patterns for building autonomous coding agents. Covers tool integration, permission systems, browser automation, and human-in-the-loop workflows. Use when building AI agents, designing tool APIs, implementing permission systems, or creating autonomous coding assistants.
.claude/skills/davila7-autonomous-agent-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 158% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 139% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 148% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 235% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 169% | 0% |
> Design patterns for building autonomous coding agents, inspired by Cline and OpenAI Codex.
Use this skill when:
┌─────────────────────────────────────────────────────────────┐
│ AGENT LOOP │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Think │───▶│ Decide │───▶│ Act │ │
│ │ (Reason) │ │ (Plan) │ │ (Execute)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ▲ │ │
│ │ ┌──────────┐ │ │
│ └─────────│ Observe │◀─────────┘ │
│ │ (Result) │ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────┘pythonclass AgentLoop: 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 = [] def run(self, task: str) -> str: self.history.append({"role": "user", "content": task}) for i in range(self.max_iterations): # Think: Get LLM response with tool options response = self.llm.chat( messages=self.history, tools=self._format_tools(), tool_choice="auto" ) # Decide: Check if agent wants to use a tool if response.tool_calls: for tool_call in response.tool_calls: # Act: Execute the tool result = self._execute_tool(tool_call) # Observe: Add result to history self.history.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) else: # No more tool calls = task complete return response.content return "Max iterations reached" def _execute_tool(self, tool_call) -> Any: tool = self.tools[tool_call.name] args = json.loads(tool_call.arguments) return tool.execute(**args)
pythonclass MultiModelAgent: """ 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 } def select_model(self, task_type: str) -> str: if task_type == "planning": return self.models["fast"] elif task_type == "analysis": return self.models["smart"] elif task_type == "code": return self.models["code"] return self.models["smart"]
pythonclass Tool: """Base class for agent tools""" @property def schema(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() } } def execute(self, **kwargs) -> ToolResult: """Execute the tool and return result""" raise NotImplementedError class ReadFileTool(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"] def execute(self, path: str, start_line: int = None, end_line: int = None) -> ToolResult: try: with open(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}" )
pythonCODING_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" }
pythonclass EditFileTool(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" def execute( 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) """ with open(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) with open(path, 'w') as f: f.write(new_content) return ToolResult( success=True, output=f"Replaced {actual_occurrences} occurrence(s)" )
pythonclass PermissionLevel(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 }
pythonclass ApprovalManager: def __init__(self, ui, config): self.ui = ui self.config = config self.session_approvals = {} def request_approval(self, tool_name: str, args: dict) -> bool: level = self.config.get(tool_name, PermissionLevel.ASK_EACH) if level == PermissionLevel.AUTO: return True if level == PermissionLevel.NEVER: self.ui.show_error(f"Tool '{tool_name}' is not allowed") return False if level == PermissionLevel.ASK_ONCE: if tool_name in self.session_approvals: return self.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", "") if any(danger in cmd for danger in ["rm -rf", "sudo", "chmod"]): return "HIGH" return "MEDIUM"
pythonclass SandboxedExecution: """ Execute code/commands in isolated environment """ def __init__(self, workspace_dir: str): self.workspace = workspace_dir self.allowed_commands = ["npm", "python", "node", "git", "ls", "cat"] self.blocked_paths = ["/etc", "/usr", "/bin", os.path.expanduser("~")] def validate_path(self, path: str) -> bool: """Ensure path is within workspace""" real_path = os.path.realpath(path) workspace_real = os.path.realpath(self.workspace) return real_path.startswith(workspace_real) def validate_command(self, command: str) -> bool: """Check if command is allowed""" cmd_parts = shlex.split(command) if not cmd_parts: return False base_cmd = cmd_parts[0] return base_cmd in self.allowed_commands def execute_sandboxed(self, command: str) -> ToolResult: if not self.validate_command(command): return ToolResult( success=False, error=f"Command not allowed: {command}" ) # Execute in isolated environment result = subprocess.run( command, shell=True, cwd=self.workspace, capture_output=True, timeout=30, env={ **os.environ, "HOME": self.workspace, # Isolate home directory } ) return ToolResult( success=result.returncode == 0, output=result.stdout.decode(), error=result.stderr.decode() if result.returncode != 0 else None )
pythonclass BrowserTool: """ Browser automation for agents using Playwright/Puppeteer. Enables visual debugging and web testing. """ def __init__(self, headless: bool = True): self.browser = None self.page = None self.headless = headless async def open_url(self, url: str) -> ToolResult: """Navigate to URL and return page info""" if not self.browser: self.browser = await playwright.chromium.launch(headless=self.headless) self.page = await self.browser.new_page() await self.page.goto(url) # Capture state screenshot = await self.page.screenshot(type='png') title = await self.page.title() return ToolResult( success=True, output=f"Loaded: {title}", metadata={ "screenshot": base64.b64encode(screenshot).decode(), "url": self.page.url } ) async def click(self, selector: str) -> ToolResult: """Click on an element""" try: await self.page.click(selector, timeout=5000) await self.page.wait_for_load_state("networkidle") screenshot = await self.page.screenshot() return ToolResult( success=True, output=f"Clicked: {selector}", metadata={"screenshot": base64.b64encode(screenshot).decode()} ) except TimeoutError: return ToolResult( success=False, error=f"Element not found: {selector}" ) async def type_text(self, selector: str, text: str) -> ToolResult: """Type text into an input""" await self.page.fill(selector, text) return ToolResult(success=True, output=f"Typed into {selector}") async def get_page_content(self) -> ToolResult: """Get accessible text content of the page""" content = await self.page.evaluate(""" () => { // Get visible text const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null, false ); let text = ''; while (walker.nextNode()) { const node = walker.currentNode; if (node.textContent.trim()) { text += node.textContent.trim() + '\\n'; } } return text; } """) return ToolResult(success=True, output=content)
pythonclass VisualAgent: """ Agent that uses screenshots to understand web pages. Can identify elements visually without selectors. """ def __init__(self, llm, browser): self.llm = llm self.browser = browser async def describe_page(self) -> str: """Use vision model to describe current page""" screenshot = await self.browser.screenshot() response = self.llm.chat([ { "role": "user", "content": [ {"type": "text", "text": "Describe this webpage. List all interactive elements you see."}, {"type": "image", "data": screenshot} ] } ]) return response.content async def find_and_click(self, description: str) -> ToolResult: """Find element by visual description and click it""" screenshot = await self.browser.screenshot() # Ask vision model to find element response = self.llm.chat([ { "role": "user", "content": [ { "type": "text", "text": f""" Find the element matching: "{description}" Return the approximate coordinates as JSON: {{"x": number, "y": number}} """ }, {"type": "image", "data": screenshot} ] } ]) coords = json.loads(response.content) await self.browser.page.mouse.click(coords["x"], coords["y"]) return ToolResult(success=True, output=f"Clicked at ({coords['x']}, {coords['y']})")
`pythonclass ContextManager: """ Manage context provided to the agent. Inspired by Cline's @-mention patterns. """ def __init__(self, workspace: str): self.workspace = workspace self.context = [] def add_file(self, path: str) -> None: """@file - Add file contents to context""" with open(path, 'r') as f: content = f.read() self.context.append({ "type": "file", "path": path, "content": content }) def add_folder(self, path: str, max_files: int = 20) -> None: """@folder - Add all files in folder""" for root, dirs, files in os.walk(path): for file in files[:max_files]: file_path = os.path.join(root, file) self.add_file(file_path) def add_url(self, url: str) -> None: """@url - Fetch and add URL content""" response = requests.get(url) content = html_to_markdown(response.text) self.context.append({ "type": "url", "url": url, "content": content }) def add_problems(self, diagnostics: list) -> None: """@problems - Add IDE diagnostics""" self.context.append({ "type": "diagnostics", "problems": diagnostics }) def format_for_prompt(self) -> str: """Format all context for LLM prompt""" parts = [] for item in self.context: if item["type"] == "file": parts.append(f"## File: {item['path']}\n```\n{item['content']}\n```") elif item["type"] == "url": parts.append(f"## URL: {item['url']}\n{item['content']}") elif item["type"] == "diagnostics": parts.append(f"## Problems:\n{json.dumps(item['problems'], indent=2)}") return "\n\n".join(parts)
pythonclass CheckpointManager: """ Save and restore agent state for long-running tasks. """ def __init__(self, storage_dir: str): self.storage_dir = storage_dir os.makedirs(storage_dir, exist_ok=True) def save_checkpoint(self, session_id: str, state: dict) -> str: """Save current agent state""" checkpoint = { "timestamp": datetime.now().isoformat(), "session_id": session_id, "history": state["history"], "context": state["context"], "workspace_state": self._capture_workspace(state["workspace"]), "metadata": state.get("metadata", {}) } path = os.path.join(self.storage_dir, f"{session_id}.json") with open(path, 'w') as f: json.dump(checkpoint, f, indent=2) return path def restore_checkpoint(self, checkpoint_path: str) -> dict: """Restore agent state from checkpoint""" with open(checkpoint_path, 'r') as f: checkpoint = json.load(f) return { "history": checkpoint["history"], "context": checkpoint["context"], "workspace": self._restore_workspace(checkpoint["workspace_state"]), "metadata": checkpoint["metadata"] } def _capture_workspace(self, workspace: str) -> dict: """Capture relevant workspace state""" # Git status, file hashes, etc. return { "git_ref": subprocess.getoutput(f"cd {workspace} && git rev-parse HEAD"), "git_dirty": subprocess.getoutput(f"cd {workspace} && git status --porcelain") }
pythonfrom mcp import Server, Tool class MCPAgent: """ 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 = {} def connect_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 } async def create_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" with open(path, 'w') as f: f.write(code) # Hot-reload self.connect_server(server_name, {"path": path}) return f"Created tool: {server_name}"
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 49,113 | 18,533 | -62% | 1 | 1 | 0% | 4,120 | 9,501 | +131% | 0 | 0 | — |
case-02 | fail→fail | 21,344 | 21,483 | +1% | 1 | 1 | 0% | 4,258 | 9,656 | +127% | 0 | 0 | — |
case-03 | pass→pass | 14,924 | 21,280 | +43% | 1 | 1 | 0% | 3,028 | 8,398 | +177% | 0 | 0 | — |
case-04 | fail→fail | 21,767 | 20,007 | -8% | 1 | 1 | 0% | 4,059 | 9,823 | +142% | 0 | 0 | — |
case-05 | pass→pass | 20,097 | 19,989 | -1% | 1 | 1 | 0% | 4,070 | 9,723 | +139% | 0 | 0 | — |
case-11 | fail→fail | 11,853 | 11,069 | -7% | 1 | 1 | 0% | 2,297 | 7,754 | +238% | 0 | 0 | — |
case-06 | fail→fail | 17,431 | 19,778 | +13% | 1 | 1 | 0% | 3,306 | 9,224 | +179% | 0 | 0 | — |
case-07 | pass→pass | 16,785 | 19,219 | +15% | 1 | 1 | 0% | 3,457 | 9,111 | +164% | 0 | 0 | — |
case-08 | pass→pass | 16,364 | 13,843 | -15% | 1 | 1 | 0% | 3,414 | 8,376 | +145% | 0 | 0 | — |
case-09 | fail→pass | 16,742 | 18,465 | +10% | 1 | 1 | 0% | 2,896 | 7,479 | +158% | 0 | 0 | — |
case-10 | pass→pass | 25,156 | 25,439 | +1% | 1 | 1 | 0% | 4,884 | 10,804 | +121% | 0 | 0 | — |
case-17 | pass→pass | 19,871 | 56,299 | +183% | 1 | 1 | 0% | 3,366 | 10,490 | +212% | 0 | 0 | — |
case-12 | fail→pass | 20,271 | 17,682 | -13% | 1 | 1 | 0% | 3,832 | 9,156 | +139% | 0 | 0 | — |
case-13 | pass→pass | 19,397 | 22,498 | +16% | 1 | 1 | 0% | 3,483 | 10,151 | +191% | 0 | 0 | — |
case-14 | pass→pass | 25,678 | 21,172 | -18% | 1 | 1 | 0% | 4,758 | 9,546 | +101% | 0 | 0 | — |
case-15 | fail→pass | 18,165 | 14,073 | -23% | 1 | 1 | 0% | 3,382 | 8,400 | +148% | 0 | 0 | — |
case-16 | fail→pass | 13,098 | 12,489 | -5% | 1 | 1 | 0% | 2,469 | 8,282 | +235% | 0 | 0 | — |
case-18 | fail→pass | 16,206 | 13,525 | -17% | 1 | 1 | 0% | 3,020 | 8,122 | +169% | 0 | 0 | — |
case-19 | pass→pass | 16,672 | 12,871 | -23% | 1 | 1 | 0% | 3,136 | 8,006 | +155% | 0 | 0 | — |
case-20 | pass→pass | 5,390 | 5,855 | +9% | 1 | 1 | 0% | 781 | 6,709 | +759% | 0 | 0 | — |
case-21 | pass→pass | 43,805 | 23,462 | -46% | 1 | 1 | 0% | 3,031 | 9,067 | +199% | 0 | 0 | — |
case-22 | pass→pass | 7,951 | 7,579 | -5% | 1 | 1 | 0% | 1,476 | 7,145 | +384% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +23 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.