The framework provides a set of high-quality, built-in tools for file manipulation, shell execution, code search, directory exploration, web fetching, and code execution, packaged as the BuiltinToolsSkill.
The BuiltinToolsSkill includes ten primary tools:
| Tool | Description |
|---|---|
read_file |
Read content from the workspace with optional line range. |
write_file |
Create or overwrite files. |
edit_file |
Perform precise, hash-anchored line edits. |
bash |
Execute shell commands in a sandboxed-ready environment. |
glob |
Find files matching a glob pattern in the workspace. |
interactive_bash |
Execute persistent tmux commands for interactive sessions. |
grep |
Search a file for lines matching a regex pattern. |
explore |
Display the workspace directory tree up to a given depth. |
webfetch |
Fetch content from a URL and return the response body. |
code_execution |
Execute a code snippet and return its output. |
Tool results from BuiltinToolsSkill are automatically collected into the One-Shot Context Pool if enabled, providing immediate context for the next reasoning turn without polluting the permanent conversation history.
BuiltinToolsSkill sets is_tool_bundle = True. This means:
- Its tools are registered on
ToolRegistryComponentand available for the LLM to call. - The skill is not listed in
SkillComponentand does not appear in the${_installed_skills}system-prompt placeholder. load_skill_detailscannot be called for it (it has no Tier-2 skill details).- No
system_prompt()fragment is injected into the agent's system prompt.
This keeps the agent's skill list clean: BuiltinToolsSkill is infrastructure,
not a user-facing capability the LLM should reason about.
from ecs_agent import SkillManager
from ecs_agent.tools.builtins import BuiltinToolsSkill
manager = SkillManager()
manager.install(world, agent_entity, BuiltinToolsSkill())To bind a workspace root (recommended — hides workspace_root from the LLM schema and injects it automatically):
skill = BuiltinToolsSkill().bind_workspace("/path/to/workspace")
manager.install(world, agent_entity, skill)Reads content from a file relative to the workspace root.
| Parameter | Type | Default | Description |
|---|---|---|---|
file_path |
str |
— | Workspace-relative path. |
offset |
int |
1 |
1-based starting line number. |
limit |
int |
0 |
Maximum lines to return. 0 reads all lines. |
- Security: Prevents path traversal outside the workspace.
- Output: Lines with
LINE#HASH|contentformat, enabling safe use withedit_file. - Best practice: Always specify
offsetandlimitfor large files to avoid reading oversized content into the context window.
Example:
# Read lines 10–30 of a large file
result = await read_file("src/module.py", workspace_root=workspace, offset=10, limit=20)Writes full content to a file. Creates the file (and any parent directories) if it does not exist.
Applies a single precise, hash-anchored edit to a file. Preferred for LLM code edits — avoids rewriting entire files and detects stale anchors.
| Parameter | Type | Required | Description |
|---|---|---|---|
file_path |
str |
Yes | Workspace-relative path to the target file. |
op |
"replace" | "append" | "prepend" |
Yes | Edit operation. |
pos |
str |
Yes | Start position in LINE#HASH format. Obtain from read_file output. |
end |
str | None |
No | End position for range replace. Omit for single-line operations. |
content |
str |
No | New content. Use \n to separate multiple lines. |
read_file returns lines in LINE#HASH|content format:
1#a1b2|def my_function():
2#c3d4| return 42
edit_file verifies the hash before applying the edit. If the hash does not match (stale anchor), the edit is rejected — preventing edits based on outdated content.
replace: Replace a single line (noend) or a range of lines (endprovided).append: Insertcontentafter the line atpos.prepend: Insertcontentbefore the line atpos.
Executes a shell command and returns stdout (or combined output on error).
- Security: Runs within the workspace root.
- Timeout: Required. Prevents hanging the agent.
Finds files matching a glob pattern in the workspace.
- Output: Sorted, newline-delimited workspace-relative paths. Empty string if no matches.
- Security: Restricted to the workspace root.
Executes a tmux subcommand for persistent interactive sessions. Pass the subcommand without the leading tmux prefix:
await interactive_bash("new-session -d -s train")
await interactive_bash("send-keys -t train 'python train.py' Enter")
await interactive_bash("capture-pane -t train -p")Searches a file for lines matching a regular expression and returns matching lines with line numbers.
| Parameter | Type | Description |
|---|---|---|
pattern |
str |
Regular expression pattern (Python re syntax). |
file_path |
str |
Workspace-relative path to the file. |
- Case-sensitive by default. Use
(?i)prefix for case-insensitive matching. - Output: Newline-delimited
LINE: contententries. Empty string if no matches. - Security: Restricted to workspace root; no path traversal.
Example output for pattern="^def ", file_path="module.py":
1: def hello():
4: def add(a, b):
Displays the directory tree of a workspace path as ASCII art.
| Parameter | Type | Description |
|---|---|---|
path |
str |
Workspace-relative path ('.' for root). |
max_depth |
int |
Maximum tree depth to display (1–5). |
- Security: Restricted to workspace root.
- Output: ASCII tree format with
├──and└──connectors.
Example output for path=".", max_depth=2:
.
├── src/
│ ├── __init__.py
│ └── module.py
└── tests/
└── test_module.py
Fetches the content of a URL and returns the response body as text.
| Parameter | Type | Default | Description |
|---|---|---|---|
url |
str |
— | The URL to fetch. |
timeout |
float |
30.0 |
Request timeout in seconds. |
- Follows redirects automatically.
- Raises an error on HTTP 4xx/5xx responses.
- Returns raw text (HTML, JSON, plain text, etc.).
Executes a code snippet and returns its stdout output.
| Parameter | Type | Default | Description |
|---|---|---|---|
code |
str |
— | Source code to execute. |
language |
str |
— | Programming language ('python'). |
timeout |
float |
30.0 |
Maximum execution time in seconds. |
- Supported languages:
python - On non-zero exit, returns
Exit code N\nSTDOUT:\n...\nSTERR:\n.... - On timeout, raises
ValueError.
Example:
result = await code_execution(
code="print(sum(range(1, 101)))",
language="python",
)
# result: "5050\n"All tool parameters expose a human-readable description field in their JSON schema via the Annotated metadata mechanism in discovery.py, so the LLM understands each parameter's purpose.
All file and directory tools require a workspace_root to be configured (injected automatically via bind_workspace). The framework validates that all paths resolve within this root and rejects any traversal attempts (e.g., .., absolute paths, symlinks pointing outside).
Tools that do not operate on the workspace (webfetch, code_execution) do not receive or require workspace_root.
- Read Before Edit: Use
read_file(with appropriateoffset/limit) beforeedit_fileto get freshLINE#HASHanchors. - One Edit Per Call: Re-read after each
edit_fileif line numbers may have shifted. - Discover Before Reading: Use
globorexploreto discover files in unfamiliar workspaces. - Search Before Reading Large Files: Use
grepto locate relevant lines, then useread_filewith a narrow range. - Use Bash for Verification: Run tests or linting via
bashafter editing files. - Persistent Sessions: Use
interactive_bashfor long-running tasks; retrieve output withcapture-pane.
See examples/script_skill_agent.py for a demonstration of these tools in a reasoning loop.