Reads, lists, searches and compares files without changing them. Writing and editing live in filesystem_write; LLM-backed questions and edits live in file_ai.
| Action | Use it for |
|---|---|
read |
one file, line-numbered by default; page large files with offset/limit |
list |
the top level of a directory, or the whole tree with recursive: true |
find |
files whose path matches a glob (*.py, **/*.scad) |
find_up |
walk parent directories looking for one exact filename (pyproject.toml, .git) |
grep |
search file contents; literal by default, regex: true for a pattern |
info |
size, type, timestamps, line count — call before reading an unknown file |
exists |
true or false without an error |
diff |
unified diff between path and other (both files) |
read numbers lines as 12: text. The number column is not part of the file — when you copy text into edit, read with numbered: false or strip the column.
Large files are paged automatically: by default the first 500 lines (or first 50 KB). The header says lines 1–500 of 3200, and a numbered read ends with [more: call read with offset=501]. Continue with offset. A raw (numbered: false) read has no footer, so its text is safe to copy.
{"action": "read", "path": "src/app.py"}
{"action": "read", "path": "src/app.py", "offset": 40, "limit": 30}
{"action": "read", "path": "src/app.py", "numbered": false}
Binary files (a NUL byte in the first 8 KB) are refused; use info for metadata or image_view for images.
grep walks a directory recursively, returns relative/path.py:42: text, and stops at 100 matches by default. It skips .git, node_modules, .venv, __pycache__, dist, build and similar directories; pass include_ignored: true to search them too.
regex: true enables regular expressions. The default is a literal substring.case_sensitive: true is available; the default is case-insensitive.glob: "*.py" limits the filenames searched.context: 2 adds two lines before and after each match (path-42- text marks context).max_results raises or lowers the cap.{"action": "grep", "path": "src", "pattern": "TODO", "glob": "*.py"}
{"action": "grep", "path": "src", "pattern": "def \\w+\\(", "regex": true, "context": 1}
{"action": "find", "path": ".", "pattern": "*.scad"}
{"action": "find_up", "path": "src/deep/file.py", "name": "pyproject.toml"}
{"action": "diff", "path": "src/app.py", "other": "src/app.py.bak"}
If nothing matches, the error suggests a shorter pattern, regex: true, include_ignored: true, or a different path — try one of those before concluding that the text is absent.
In multi-user mode every path resolves inside user_data/<user_id>/ or the current session directory. A path outside both returns Access denied. Relative paths resolve against the user sandbox. Do not work around a refusal by copying files into the sandbox or /tmp; ask the user to upload the file.
12: number column into an edit — read with numbered: false.grep, or file_ai ask.find for content — find matches paths; grep matches text.destination to diff — the second file goes in other.