Tools Module¶
Tools module for atloop agent.
- class atloop.tools.ToolResult(ok: bool, stdout: str, stderr: str, meta: Dict[str, Any])[source]¶
Bases:
objectResult of tool execution.
- class atloop.tools.BaseTool[source]¶
Bases:
ABCBase class for all tools.
Tools can declare their output semantic types to enable automatic application of appropriate output size limits.
- get_detailed_description() str[source]¶
Generate detailed tool description for LLM prompts.
Extracts comprehensive information from the tool’s docstring including: - Main description - Usage guidelines - Important warnings - Parameters - Examples - When to use vs other tools
- Returns:
Formatted detailed description string suitable for LLM prompts
- property output_semantic_type: OutputSemanticType¶
Return the semantic type of tool output.
Default: STATUS_MESSAGE (suitable for most tools that return simple success/failure messages).
Subclasses can override this property to declare their output semantic type.
- Returns:
OutputSemanticType enum value
- property stderr_semantic_type: OutputSemanticType¶
Return the semantic type of stderr output.
Default: ERROR_MESSAGE (stderr is typically error information).
Subclasses can override this if stderr has a different semantic type.
- Returns:
OutputSemanticType enum value
- property stdout_semantic_type: OutputSemanticType¶
Return the semantic type of stdout output.
Default: Uses output_semantic_type.
Subclasses can override this if stdout has a different semantic type than the general output.
- Returns:
OutputSemanticType enum value
BaseTool¶
- class atloop.tools.base.BaseTool[source]¶
Bases:
ABCBase class for all tools.
Tools can declare their output semantic types to enable automatic application of appropriate output size limits.
- property output_semantic_type: OutputSemanticType¶
Return the semantic type of tool output.
Default: STATUS_MESSAGE (suitable for most tools that return simple success/failure messages).
Subclasses can override this property to declare their output semantic type.
- Returns:
OutputSemanticType enum value
- property stdout_semantic_type: OutputSemanticType¶
Return the semantic type of stdout output.
Default: Uses output_semantic_type.
Subclasses can override this if stdout has a different semantic type than the general output.
- Returns:
OutputSemanticType enum value
- property stderr_semantic_type: OutputSemanticType¶
Return the semantic type of stderr output.
Default: ERROR_MESSAGE (stderr is typically error information).
Subclasses can override this if stderr has a different semantic type.
- Returns:
OutputSemanticType enum value
- validate_args(args: Dict[str, Any]) tuple[bool, str | None][source]¶
Validate tool arguments. Returns (is_valid, error_message).
- get_detailed_description() str[source]¶
Generate detailed tool description for LLM prompts.
Extracts comprehensive information from the tool’s docstring including: - Main description - Usage guidelines - Important warnings - Parameters - Examples - When to use vs other tools
- Returns:
Formatted detailed description string suitable for LLM prompts
ToolRegistry¶
- class atloop.tools.registry.ToolRegistry(sandbox: SandboxAdapter, skill_loader=None)[source]¶
Bases:
objectRegistry for managing tools.
- __init__(sandbox: SandboxAdapter, skill_loader=None)[source]¶
Initialize tool registry.
- Parameters:
sandbox – Sandbox adapter instance (required by run, read_file, write_file, etc.)
skill_loader – Optional skill loader for read_skill_file, load_skill, and load_skill_resource tools. Passed automatically to tools that declare skill_loader in __init__.
- get_tool(name: str) BaseTool | None[source]¶
Get a tool by name (alias for get method).
This method provides a consistent interface name used in the output limit strategy system.
- Parameters:
name – Tool name
- Returns:
Tool instance or None if not found
- get_registration_stats() Dict[str, Any][source]¶
Get tool registration statistics.
- Returns:
Dictionary with registration stats (discovered, registered, failed, tools)
- execute(tool_name: str, args: Dict[str, Any]) ToolResult[source]¶
Execute a tool.
- Parameters:
tool_name – Name of the tool
args – Tool arguments
- Returns:
ToolResult instance
- Raises:
ValueError – If tool not found
ToolExecutor¶
- class atloop.orchestrator.executor.tool_executor.ToolExecutor(coordinator: WorkflowCoordinator)[source]¶
Bases:
objectTool executor for executing tool calls.
- __init__(coordinator: WorkflowCoordinator)[source]¶
Initialize tool executor.
- Parameters:
coordinator – Workflow coordinator instance
Filesystem Tools¶
Filesystem tools.
- class atloop.tools.filesystem.ReadFileTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for reading files from the sandbox workspace with type detection and chunked reading.
Features: - Automatic file type detection (text/binary) - Support for large files via line range reading - Binary file detection and handling - File size limits (10MB max for full read)
Use cases: - Reading source code files - Reading configuration files - Reading documentation files - Reading log files (with line ranges)
Note: This tool reads from the sandbox workspace (/workspace), not from the local machine. For reading skill files (stored locally), use read_skill_file instead.
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize read file tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute read file tool.
- Args:
- args: Tool arguments dictionary
path (str, required): File path. Relative paths are relative to /workspace. Absolute paths are used as-is.
offset (int, optional): Start line number (1-indexed). Default: 1 (start of file). Use this for reading large files in chunks.
limit (int, optional): Number of lines to read. If not specified, reads from offset to end of file. Use this for reading large files in chunks.
- Returns:
ToolResult with: - ok (bool): True if file was read successfully (no errors in stderr) - stdout (str): File content (or metadata for binary/large files) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains path, file_type, file_size, start_line, end_line
- Examples:
# Read entire file read_file(path=”src/main.py”)
# Read first 100 lines read_file(path=”src/main.py”, offset=1, limit=100)
# Read lines 50-100 read_file(path=”src/main.py”, offset=50, limit=51)
# Read from line 200 to end read_file(path=”src/main.py”, offset=200)
Behavior: - Text files: Returns file content in stdout - Binary files: Returns metadata message (file type, size) instead of content - Large files (>10MB): Returns metadata message, suggests using line ranges - File not found: Returns ok=False with error message in stderr - Line ranges: If offset/limit specified, only reads those lines
Path Handling: - All commands run with workdir=”/workspace” - Relative paths are resolved relative to /workspace - Absolute paths are used as-is
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False
- property output_semantic_type: OutputSemanticType¶
FILE_CONTENT.
- Type:
Return semantic type
- class atloop.tools.filesystem.ReadSkillFileTool(skill_loader=None)[source]¶
Bases:
BaseToolTool for reading files from skill directories (on local machine, not from sandbox workspace).
🚨🚨🚨 CRITICAL: This tool reads from skill directories on the LOCAL machine, NOT from the sandbox!
Key Points: - Skill files are stored LOCALLY (on the host machine, in ~/.atloop/skills/ or project .atloop/skills/) - Workspace is a REMOTE SANDBOX (/workspace) - it does NOT contain skill files or templates - When a skill mentions other files (e.g., “see docx-js.md”, “reference guide.md”), those files are LOCAL - You MUST use `read_skill_file` to read skill-related files - they are NOT in the sandbox! - DO NOT try to use `read_file` or `run(“cat …”)` to find skill files in the sandbox - they don’t exist there!
Use cases: - Reading skill files (when skill mentions other files to reference) - Reading files referenced in skill documentation - Accessing skill-specific resources (templates, guides, examples)
Path resolution: - With skill_name: Path is relative to skill directory (most common use case) - Without skill_name: Absolute path or relative to ~/.atloop/ - Supports ~ expansion for home directory
Important distinction: - ✓ Skill files → Use read_skill_file (stored locally) - ✓ Workspace files → Use read_file (stored in remote sandbox /workspace) - ❌ Never use `read_file` or `run` to find skill files - they are not in the sandbox!
- __init__(skill_loader=None)[source]¶
Initialize read skill file tool.
- Parameters:
skill_loader – Optional skill loader instance for resolving skill paths
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute read skill file tool.
🚨🚨🚨 CRITICAL: This tool reads from skill directories on the LOCAL machine, NOT from the sandbox workspace!
Important understanding: - Skill files are stored LOCALLY (on the host machine) - Workspace is a REMOTE SANDBOX (/workspace) - it does NOT contain skill files - When a skill mentions other files, those files are LOCAL, not in the sandbox - You MUST use `read_skill_file` to read skill-related files - DO NOT try to use `read_file` or `run(“cat …”)` to find skill files in the sandbox - they don’t exist there!
Use read_file to read files from the sandbox workspace (/workspace).
- Args:
- args: Tool arguments dictionary
path (str, required): File path. Resolution depends on skill_name: - If skill_name provided: Path is relative to skill directory - If path starts with ~: Expanded to home directory - If absolute path: Used as-is - Otherwise: Relative to ~/.atloop/
skill_name (str, optional): Skill name. If provided, path is resolved relative to the skill’s directory. Use this when a skill mentions other files to reference.
offset (int, optional): Start line number (1-indexed). Default: 1. Use for reading large files in chunks.
limit (int, optional): Number of lines to read. If not specified, reads from offset to end of file.
- Returns:
ToolResult with: - ok (bool): True if file was read successfully (no errors in stderr) - stdout (str): File content (or metadata for binary/large files) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains path, file_size, start_line, end_line
- Examples:
# Read skill file (when skill mentions other files) read_skill_file(path=”references/guide.md”, skill_name=”long_document_writer”)
# Read skill documentation read_skill_file(path=”docx-js.md”, skill_name=”docx”)
# Read with line range read_skill_file(path=”skill-doc.md”, skill_name=”docx”, offset=1, limit=50)
When to use this vs read_file: - ✓ Reading skill files → use read_skill_file (stored locally, NOT in sandbox) - ✓ Reading files referenced in skills → use read_skill_file (skill mentions are LOCAL files) - ✓ Reading skill templates/examples → use read_skill_file (stored locally) - ❌ Reading workspace files → use read_file (sandbox files in /workspace) - ❌ DO NOT use `read_file` or `run` to find skill files - they are NOT in the sandbox!
File Size Limits: - Files larger than 10MB return metadata only - Use offset/limit to read specific sections of large files
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False
- property output_semantic_type: OutputSemanticType¶
FILE_CONTENT.
- Type:
Return semantic type
- class atloop.tools.filesystem.WriteFileTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for writing files (creates new files or completely overwrites existing files).
⚠️ Important Usage Guidelines: - Use for: Creating new files or completely rewriting existing files - Do NOT use for: Modifying parts of existing files (use edit_file instead) - Character limit: Maximum 6,000 characters per turn - Directory creation: Automatically creates parent directories if they don’t exist - ⚠️ CRITICAL: Do NOT generate code with {variable} patterns: Patterns like {error_output},
{variable}, etc. will be written literally to the file and will NOT be populated by shell variable expansion. Use proper templating in the target language instead (e.g., Python f-strings, format(), etc.)
When to use write_file vs edit_file: - ✓ Creating a new file → use write_file - ✓ Completely rewriting a file → use write_file - ❌ Modifying a function/class → use edit_file (more precise and safer) - ❌ Adding/removing a few lines → use edit_file (more precise and safer)
File content handling: - Uses placeholder mechanism: content should be FILE_CONTENT_#1, FILE_CONTENT_#2, etc. - Actual content follows the JSON output, delimited by —(FILE_CONTENT_#1)— - This prevents JSON parsing issues with large content
Trailing newline behavior: - Files always end with exactly one newline character - Input content’s trailing newline is normalized automatically
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize write file tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute write file tool.
⚠️ WARNING: This tool completely overwrites the entire file. If the file exists, all existing content will be replaced with the new content.
When to use this tool: - ✓ Creating a new file - ✓ Completely rewriting an existing file - ❌ Modifying part of a file → use edit_file instead - ❌ Appending content → use append_file instead
- Args:
- args: Tool arguments dictionary
path (str, required): File path. Relative paths are relative to /workspace. Parent directories are automatically created if they don’t exist.
content (str, required): File content to write. This will completely replace any existing file content. Maximum 6,000 characters per turn. Use placeholder FILE_CONTENT_#1 in JSON, actual content follows after.
- Returns:
ToolResult with: - ok (bool): True if file was written successfully (no errors in stderr) - stdout (str): Command output (usually empty) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains path, cmd, duration_ms
- Examples:
# Create a new Python file write_file(path=”src/main.py”, content=”FILE_CONTENT_#1”) # Then provide actual content after JSON: # —(FILE_CONTENT_#1)— # def main(): # print(“Hello, World!”) # —(FILE_CONTENT_#1)—
Note on Trailing Newlines: Files always end with exactly one newline character: - If content ends with ‘n’, it’s preserved - If content doesn’t end with ‘n’, one is added - Result: File always ends with exactly one newline character - Exception: Empty string content results in a file with one newline
⚠️ Important: Content with {variable} patterns: Do NOT generate code or text with patterns like {error_output}, {variable}, etc. expecting them to be populated by shell variable expansion. These are written literally to the file and will NOT be expanded. If you need variable substitution, use proper templating mechanisms in the target language (e.g., Python f-strings, format(), etc.).
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False
- class atloop.tools.filesystem.AppendFileTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for appending content to the end of existing files.
⚠️ This tool is fully available and can be used normally!
Use cases: - Continuing to write files that exceed 6,000 characters (after initial write_file) - Adding log entries - Appending comments or notes - Building files incrementally
Key differences from write_file and edit_file: - write_file: Completely overwrites file (creates or replaces entire file) - edit_file: Replaces specific text within file (precise modifications) - append_file: Adds content to end of file (doesn’t modify existing content)
Content handling: - Content is appended exactly as provided - Preserves trailing newlines in input content - Unlike write_file/edit_file, doesn’t normalize trailing newlines
⚠️ CRITICAL: Do NOT generate code with {variable} patterns: Patterns like {error_output}, {variable}, etc. will be written literally to the file and will NOT be populated by shell variable expansion. Use proper templating in the target language instead (e.g., Python f-strings, format(), etc.)
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize append file tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute append file tool.
⚠️ IMPORTANT: This tool is fully available and can be used normally! Don’t assume it’s unavailable - it works perfectly fine.
- Args:
- args: Tool arguments dictionary
path (str, required): File path. Relative paths are relative to /workspace. File must exist (this tool appends to existing files, doesn’t create new ones).
content (str, required): Content to append to the end of the file.
- Returns:
ToolResult with: - ok (bool): True if content was appended successfully (no errors in stderr) - stdout (str): Command output (usually empty) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains path, cmd, duration_ms
- Examples:
# Continue writing a long document (after initial write_file with 6k chars) append_file(
path=”long_document.md”, content=”nn## Chapter 2nThis is the continuation…”
)
# Add a log entry append_file(
path=”app.log”, content=”[2024-01-01] User logged inn”
)
Content Appending Behavior: - Content is appended exactly as provided, without modification - If content ends with ‘n’, it’s appended with the newline - If content doesn’t end with ‘n’, it’s appended without a newline - This differs from write_file and edit_file, which normalize trailing newlines - Use this when you need precise control over trailing newlines
⚠️ Important: Content with {variable} patterns: Do NOT generate code or text with patterns like {error_output}, {variable}, etc. expecting them to be populated by shell variable expansion. These are written literally to the file and will NOT be expanded. If you need variable substitution, use proper templating mechanisms in the target language (e.g., Python f-strings, format(), etc.).
Common Workflow: 1. First turn: Use write_file to create file with first 6k characters 2. Subsequent turns: Use append_file to continue adding content 3. Check file: Use read_file to verify complete content
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False
- class atloop.tools.filesystem.EditFileTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for editing files using Git-style diff (old_string -> new_string).
⚠️ This is the preferred tool for modifying existing files!
Why use edit_file instead of write_file: - ✓ More precise: Only modifies the specified part - ✓ Safer: Doesn’t risk overwriting unrelated code - ✓ More efficient: No need to read and rewrite entire file - ✓ Better for local modifications: Functions, classes, paragraphs, etc.
⚠️ CRITICAL: Do NOT generate code with {variable} patterns: Patterns like {error_output}, {variable}, etc. will be written literally to the file and will NOT be populated by shell variable expansion. Use proper templating in the target language instead (e.g., Python f-strings, format(), etc.)
Safety features: - Match count validation: Only replaces if old_string appears exactly once - Prevents accidental multiple replacements - Clear error messages when matches are not found or ambiguous
Use cases: - Modifying a function or method - Updating a class definition - Changing a specific section of code - Fixing a bug in a specific location - Updating configuration values
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize edit file tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute edit file tool.
⚠️ IMPORTANT: This is the preferred tool for modifying existing files! Use this instead of write_file for any file modifications.
- Args:
- args: Tool arguments dictionary
path (str, required): File path. Relative paths are relative to /workspace.
content (str, required): Content in format <old>old_string</old><new>new_string</new>. The old_string is the exact text to replace. Must match exactly including whitespace, indentation, and newlines. Should include at least 3 lines of context before and after to ensure uniqueness.
replace_all (bool, optional): If True, replace all occurrences of old_string. Default: False. When False, only replaces if old_string appears exactly once.
- Returns:
ToolResult with: - ok (bool): True if edit was successful (no errors in stderr) - stdout (str): Diff summary (e.g., “Updated file: +2 lines, -1 lines”) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains path, operation, replace_all
- Examples:
# Modify a function edit_file(
path=”src/utils.py”, content=”<old>def calculate(x, y):
return x + y</old><new>def calculate(x, y): return x * y</new>”
)
# Add context for uniqueness edit_file(
path=”src/main.py”, content=”<old># Configuration
DEBUG = True # End config</old><new># Configuration DEBUG = False # End config</new>”
)
Safety Checks: - Match count validation: When replace_all=False (default):
If old_string appears 0 times → Error: “old_string not found”
If old_string appears 1 time → Success: Replaces it
If old_string appears 2+ times → Error: “found X times, make old_string more specific”
When replace_all=True: Replaces all occurrences without validation
Best Practices: 1. Include context: Add at least 3 lines before and after the code you’re changing 2. Be precise: Match exact whitespace, indentation, and newlines 3. Make it unique: Ensure old_string appears only once in the file 4. Use read_file first: Read the file to see exact formatting before editing
Error Messages: - “old_string not found” → Check that old_string exactly matches file content - “found X times” → Add more context to make old_string unique, or use replace_all=True - “File not found” → File doesn’t exist
Note on Trailing Newlines: Files always end with exactly one newline character after editing, regardless of whether new_string ends with a newline or not.
⚠️ Important: Content with {variable} patterns: Do NOT generate code or text with patterns like {error_output}, {variable}, etc. expecting them to be populated by shell variable expansion. These are written literally to the file and will NOT be expanded. If you need variable substitution, use proper templating mechanisms in the target language (e.g., Python f-strings, format(), etc.).
- class atloop.tools.filesystem.MultiEditFileTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for editing multiple files in a single atomic transaction.
Key Features: - Transactional: All edits succeed or all fail (atomicity) - Batch operations: Edit multiple files in one operation - Rollback on error: If any edit fails, all changes are rolled back
Use cases: - Updating imports across multiple files - Refactoring code that spans multiple files - Applying the same change to multiple files - Coordinated updates that must happen together
When to use: - ✓ Need to edit multiple files atomically - ✓ Changes must all succeed or all fail - ❌ Single file edit → use edit_file (simpler) - ❌ Independent edits → use multiple edit_file calls
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize multi-edit file tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute multi-edit file tool.
⚠️ Transactional Behavior: All edits are atomic - either all succeed or all fail. If any edit fails, all changes are rolled back and no files are modified.
- Args:
- args: Tool arguments dictionary
- edits (list, required): List of edit operations. Each edit is a dictionary with:
path (str, required): File path. Relative paths are relative to /workspace.
old_string (str, required): Exact text to replace. Must match exactly. If empty, creates a new file.
new_string (str, required): Replacement text.
replace_all (bool, optional): Replace all occurrences. Default: False.
- Returns:
ToolResult with: - ok (bool): True if all edits succeeded (no errors in stderr) - stdout (str): Summary of edited files (e.g., “Successfully edited 3 file(s)”) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains successful_edits, failed_edits, total_edits, edited_files
- Examples:
# Update imports in multiple files atomically multi_edit_file(edits=[
- {
“path”: “src/file1.py”, “old_string”: “from utils import helper”, “new_string”: “from utils.helpers import helper”
}, {
“path”: “src/file2.py”, “old_string”: “from utils import helper”, “new_string”: “from utils.helpers import helper”
}
])
Transaction Phases: 1. Validation phase: Read all files, check they exist 2. Transaction phase: Apply all edits in memory 3. Commit phase: Write all files (only if all edits succeeded)
Error Handling: - If any file read fails → All edits rolled back, error in stderr - If any edit fails (old_string not found) → All edits rolled back, error in stderr - If any file write fails → Partial success reported, failed files in stderr - Success is determined by stderr content, not exit code
Best Practices: 1. Use when edits must be atomic (all or nothing) 2. Ensure all old_string values are unique in their respective files 3. Include sufficient context in old_string for uniqueness 4. Test with single-file edits first, then combine into multi-edit
Note on Trailing Newlines: All files will always end with exactly one newline character after editing.
- class atloop.tools.filesystem.GlobFilesTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for finding files using gitignore-style glob patterns.
Features: - Supports common glob patterns (, *) - Recursive directory searching - File filtering by extension or pattern
Use cases: - Finding all Python files: *.py - Finding all test files: test_*.py - Finding files recursively: **/*.js - Listing files in a directory: dir/*.txt
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize glob files tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute glob tool to find files matching a pattern.
- Args:
- args: Tool arguments dictionary
pattern (str, required): Glob pattern to match files. Supports: - *.py: Matches all .py files in current directory - **/*.py: Matches all .py files recursively - test_*.py: Matches files starting with “test_” - dir/*.txt: Matches .txt files in “dir” directory
max_results (int, optional): Maximum number of results to return. Default: 100. Use this to limit output for very large matches.
- Returns:
ToolResult with: - ok (bool): True if pattern was executed successfully (no errors in stderr) - stdout (str): Formatted list of matched files, one per line - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains pattern, max_results, matched_count, matched_files
- Examples:
# Find all Python files glob(pattern=”*.py”)
# Find all Python files recursively glob(pattern=”**/*.py”)
# Find test files glob(pattern=”test_*.py”)
# Find files in specific directory glob(pattern=”src/*.py”)
# Limit results glob(pattern=”*.py”, max_results=10)
Pattern Support: - *: Matches any characters (except path separator) - **: Matches any characters including path separators (recursive) - Directory patterns: dir/*.ext matches files in “dir” directory - Leading ./ is optional and ignored
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Empty results (no matches) is considered success (ok=True) - Check stderr for specific error messages if ok=False
System Tools¶
System tools.
- class atloop.tools.system.RunCommandTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for executing shell commands in the sandbox workspace.
⚠️ This is the primary tool for executing system commands!
Use cases: - Running build commands (make, npm, pip, etc.) - Running tests (pytest, unittest, etc.) - Executing scripts (python3, node, etc.) - File operations (ls, cat, grep, find, etc.) - System checks (which, type, etc.)
Important notes: - Commands run in /workspace directory - Success is determined by stderr content, NOT exit code - Many commands return exit_code=0 even with errors - Always check stdout and stderr content to judge success
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize run command tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute shell command in the sandbox workspace.
⚠️ IMPORTANT: This is the primary tool for executing system commands! Use this instead of trying to execute commands through other means.
- Args:
- args: Tool arguments dictionary
cmd (str, required): Shell command to execute. Can be any valid shell command. Examples: “ls -la”, “python3 script.py”, “grep -rn ‘pattern’ .”, etc.
timeout_sec (int, optional): Timeout in seconds. Default: 600 (10 minutes). Use shorter timeouts for quick commands, longer for builds/tests.
- Returns:
ToolResult with: - ok (bool): True if command succeeded (no errors in stderr) - stdout (str): Standard output from the command - stderr (str): Standard error from the command (empty string means success) - meta (dict): Contains cmd, duration_ms
- Examples:
# List files run(cmd=”ls -la”)
# Run Python script run(cmd=”python3 script.py”)
# Run tests run(cmd=”pytest tests/”)
# Search for pattern run(cmd=”grep -rn ‘def function’ .”)
# View file content run(cmd=”cat file.py”) run(cmd=”head -n 50 file.py”) run(cmd=”tail -n 20 file.py”)
⚠️ Critical: Success Determination - DO NOT rely on exit_code - it’s unreliable! - Success = empty stderr (no error messages) - Failure = non-empty stderr (contains error messages) - Many commands return exit_code=0 even with errors (e.g., pytest collection errors) - Always read stdout and stderr content to judge success
⚠️ Hard requirement: avoid “triple-layer quoting hell” If use python3 -c “….”, you must use single quotes inside the string following “-c”. Example: {
“tool”: “run”, “args”: {
“cmd”: “python3 -c "import sys; sys.path.insert(0, ‘.’); from jira_client import JiraClient; client = JiraClient(); projects = client.get_projects(); print(‘Projects:’); for p in projects: print(f" {p[‘key’]}: {p[‘name’]}")"”
}
}
Common Commands: - File viewing: cat, head, tail, less, more - File search: grep, find, locate - Text processing: sed, awk, cut, sort, uniq - File operations: ls, pwd, cd, mkdir, rm, cp, mv - Python: python3 (not python), pip3 (not pip) - Other: echo, wc, diff, which, type
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the command succeeded - If stderr contains “error”, “failed”, “exception”, etc., command likely failed - Always check stderr content, not just ok field
⚠️ IMPORTANT: For Complex Python/Shell Scripts, Use Specialized Tools
DO NOT use run with python3 -c “…” or bash -c “…” for complex scripts! This causes shell escaping issues (quotes, f-strings, etc.).
Instead, use: - run_python_script_string for Python code execution (use PYTHON_SCRIPT_#N placeholder) - run_shell_script_string for complex shell scripts (use SHELL_SCRIPT_#N placeholder)
Use run only for simple commands: ls, cat, grep, find, pwd, python3 script.py, etc.
- property output_semantic_type: OutputSemanticType¶
EXECUTION_RESULT.
Note: File view commands are handled specially in OutputLimitStrategy based on command content, not semantic type.
- Type:
Return semantic type
- class atloop.tools.system.RunPythonScriptStringTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for executing Python code strings without shell escaping issues.
Use this tool instead of `run` with `python3 -c “…”` for complex Python code.
Benefits: - No shell escaping issues (quotes, f-strings, etc.) - Better error messages (can show script content) - Cleaner code (no need to escape quotes)
Usage: Use PYTHON_SCRIPT_#N placeholder in JSON, then provide script content: {
“tool”: “run_python_script_string”, “args”: {
“script”: “PYTHON_SCRIPT_#1”
}
} —(PYTHON_SCRIPT_#1)— import sys from jira_client import JiraClient
client = JiraClient() projects = client.get_projects() for p in projects:
print(f”{p[‘key’]}: {p[‘name’]}”)
—
Important notes: - Script runs in /workspace directory - Success is determined by stderr content, NOT exit code - Script is written to a temporary file, executed, then cleaned up - Python path includes /workspace automatically
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize run Python script string tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute Python script string.
- Args:
- args: Tool arguments dictionary
script (str, required): Python code to execute. Can be multi-line. Should use PYTHON_SCRIPT_#N placeholder in JSON, actual content provided separately.
timeout_sec (int, optional): Timeout in seconds. Default: 600 (10 minutes).
- Returns:
ToolResult with: - ok (bool): True if script succeeded (no errors in stderr) - stdout (str): Standard output from the script - stderr (str): Standard error from the script (empty string means success) - meta (dict): Contains script_preview, duration_ms
- Examples:
# Simple script run_python_script_string(script=”PYTHON_SCRIPT_#1”) —(PYTHON_SCRIPT_#1)— print(“Hello, World!”) —
# Complex script with imports run_python_script_string(script=”PYTHON_SCRIPT_#1”) —(PYTHON_SCRIPT_#1)— import sys sys.path.insert(0, ‘.’) from my_module import MyClass obj = MyClass() result = obj.process() print(f”Result: {result}”) —
⚠️ Critical: Success Determination - DO NOT rely on exit_code - it’s unreliable! - Success = empty stderr (no error messages) - Failure = non-empty stderr (contains error messages) - Always read stdout and stderr content to judge success
- class atloop.tools.system.RunShellScriptStringTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for executing shell script strings without shell escaping issues.
Use this tool instead of `run` with `bash -c “…”` for complex shell scripts.
Benefits: - No shell escaping issues (quotes, variables, etc.) - Better error messages (can show script content) - Cleaner code (no need to escape quotes) - Supports multi-line scripts easily
Usage: Use SHELL_SCRIPT_#N placeholder in JSON, then provide script content: {
“tool”: “run_shell_script_string”, “args”: {
“script”: “SHELL_SCRIPT_#1”
}
} —(SHELL_SCRIPT_#1)— #!/bin/bash set -e
echo “Processing files…” for file in *.txt; do
- if [ -f “$file” ]; then
echo “Found: $file” wc -l “$file”
fi
done —
Important notes: - Script runs in /workspace directory - Success is determined by stderr content, NOT exit code - Script is written to a temporary file, executed, then cleaned up - Uses bash to execute the script
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize run shell script string tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute shell script string.
- Args:
- args: Tool arguments dictionary
script (str, required): Shell script to execute. Can be multi-line. Should use SHELL_SCRIPT_#N placeholder in JSON, actual content provided separately.
timeout_sec (int, optional): Timeout in seconds. Default: 600 (10 minutes).
- Returns:
ToolResult with: - ok (bool): True if script succeeded (no errors in stderr) - stdout (str): Standard output from the script - stderr (str): Standard error from the script (empty string means success) - meta (dict): Contains script_preview, duration_ms
- Examples:
# Simple script run_shell_script_string(script=”SHELL_SCRIPT_#1”) —(SHELL_SCRIPT_#1)— echo “Hello, World!” ls -la —
# Complex script with loops and conditionals run_shell_script_string(script=”SHELL_SCRIPT_#1”) —(SHELL_SCRIPT_#1)— #!/bin/bash set -e
- for file in *.py; do
- if [ -f “$file” ]; then
echo “Processing $file” python3 -m py_compile “$file”
fi
done —
⚠️ Critical: Success Determination - DO NOT rely on exit_code - it’s unreliable! - Success = empty stderr (no error messages) - Failure = non-empty stderr (contains error messages) - Always read stdout and stderr content to judge success
Search Tools¶
Search tools.
- class atloop.tools.search.SearchTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolEnhanced tool for searching file contents using grep with regex, context lines, and file filtering.
Features: - Full regex pattern support - Context lines (before/after matches) - File filtering by glob pattern - Multiple output modes (content, files, count) - Case-insensitive search option
Use cases: - Finding function definitions: search(‘def function_name’) - Finding imports: search(‘^import |^from ‘) - Finding specific patterns: search(‘TODO|FIXME’) - Searching in specific file types: search(‘pattern’, glob=’*.py’)
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize search tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute enhanced search tool to find patterns in files.
- Args:
- args: Tool arguments dictionary
query (str, required): Search query as regex pattern. Supports full regex syntax. Examples: “def function”, “^import “, “TODO|FIXME”, “class w+”
glob (str, optional): Glob pattern to filter files. Examples: “.py”, “*/*.js”
output_mode (str, optional): Output mode. Default: “content” - “content”: Show matching lines with context (default) - “files_with_matches”: Show only file paths that contain matches - “count”: Show match count per file
-A (int, optional): Number of lines to show after each match
-B (int, optional): Number of lines to show before each match
-C (int, optional): Number of lines to show before and after each match
-i (bool, optional): Case-insensitive search. Default: False
-n (bool, optional): Show line numbers. Default: True
max_results (int, optional): Maximum number of results to return. Default: 50. Limits output for very large result sets.
- Returns:
ToolResult with: - ok (bool): True if search executed successfully (no errors in stderr) - stdout (str): Search results (matching lines, file paths, or counts) - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains query, glob, output_mode, max_results, cmd
- Examples:
# Find function definitions search(query=”def w+”, glob=”*.py”)
# Find imports with context search(query=”^import |^from “, glob=”*.py”, -C=2)
# Find TODO comments (case-insensitive) search(query=”TODO|FIXME”, -i=True)
# Find files containing pattern (without showing content) search(query=”def main”, output_mode=”files_with_matches”)
# Count matches per file search(query=”class w+”, output_mode=”count”)
Regex Pattern Tips: - Use ^ for start of line: ^def matches “def” at line start - Use $ for end of line: import$ matches “import” at line end - Use | for alternation: TODO|FIXME matches either - Use w+ for word characters: def w+ matches “def function_name” - Escape special chars: . matches literal dot
Context Lines: - Use -C=3 to show 3 lines before and after each match - Use -B=5 to show 5 lines before each match - Use -A=2 to show 2 lines after each match - Context helps understand code structure around matches
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the search succeeded - No matches found is considered success (ok=True, empty stdout) - Check stderr for specific error messages if ok=False
Interaction Tools¶
Interaction tools module.
- class atloop.tools.interaction.TodoReadTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for reading and displaying the current task list from TODO.md.
Use cases: - Checking current task status - Reviewing progress on multi-step tasks - Understanding what work is in progress - Planning next steps based on current status
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize todo read tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute todo read tool to read and display TODO.md.
- Args:
args: Tool arguments dictionary (no arguments required)
- Returns:
ToolResult with: - ok (bool): True if TODO.md was read successfully (no errors in stderr) - stdout (str): Formatted TODO list with tasks grouped by status - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains todo_file, todo_count, pending, in_progress, completed
- Examples:
# Read current TODO list todo_read()
Output Format: Tasks are grouped by status and displayed as: ``` # TODO List (3 task(s))
## In Progress 1. Task 1 (Running task 1)
## Pending 1. Task 2 (Will run task 2)
## Completed (1) 1. Task 3
Summary: 1 pending, 1 in progress, 1 completed ```
Behavior: - If TODO.md doesn’t exist: Returns ok=True with message to use todo_write - If TODO.md exists but is empty: Returns ok=True with message - If TODO.md has tasks: Returns formatted list grouped by status - Only shows first 5 completed tasks (to avoid clutter)
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False
- class atloop.tools.interaction.TodoWriteTool(sandbox: SandboxAdapter)[source]¶
Bases:
BaseToolTool for creating and managing task lists in TODO.md format.
Use cases: - Tracking progress on complex, multi-step tasks - Managing task status (pending, in_progress, completed) - Organizing work into manageable chunks - Providing visibility into current work status
Task statuses: - pending: Task not yet started - in_progress: Task currently being worked on - completed: Task finished
Best practices: - At least one task should be in_progress at any time - Use activeForm to describe what’s happening (e.g., “Running tests”) - Update status as work progresses
- __init__(sandbox: SandboxAdapter)[source]¶
Initialize todo write tool.
- Parameters:
sandbox – Sandbox adapter instance
- execute(args: Dict[str, Any]) ToolResult[source]¶
Execute todo write tool to create or update TODO.md.
- Args:
- args: Tool arguments dictionary
- todos (list, required): List of todo items. Each item is a dictionary with:
content (str, required): Task content in imperative form. Example: “Run tests”, “Fix bug”, “Write documentation”
activeForm (str, required): Present continuous form describing the action. Example: “Running tests”, “Fixing bug”, “Writing documentation”
status (str, required): Task status. Must be one of: - “pending”: Task not yet started - “in_progress”: Task currently being worked on - “completed”: Task finished
- Returns:
ToolResult with: - ok (bool): True if TODO.md was written successfully (no errors in stderr) - stdout (str): Summary of updated TODO list with counts by status - stderr (str): Error messages if any (empty string means success) - meta (dict): Contains todo_file, todo_count, pending, in_progress, completed
- Examples:
# Create initial TODO list todo_write(todos=[
- {
“content”: “Set up project structure”, “activeForm”: “Setting up project structure”, “status”: “in_progress”
}, {
“content”: “Write unit tests”, “activeForm”: “Writing unit tests”, “status”: “pending”
}
])
# Update TODO list (replaces entire list) todo_write(todos=[
- {
“content”: “Set up project structure”, “activeForm”: “Setting up project structure”, “status”: “completed”
}, {
“content”: “Write unit tests”, “activeForm”: “Writing unit tests”, “status”: “in_progress”
}
])
Important Notes: - This tool replaces the entire TODO.md file with the new todos list - To update a single task, read current todos, modify, then write back - At least one task should be in_progress (warning shown if none) - TODO.md is created in the workspace root directory
File Format: The tool generates markdown format: ```markdown # TODO
## In Progress - [ ] Task 1 (Running task 1)
## Pending - [ ] Task 2 (Will run task 2)
Error Handling: - Success is determined by stderr content, not exit code - If stderr is empty, the operation succeeded - Check stderr for specific error messages if ok=False