> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/yocxy2/claurst/llms.txt
> Use this file to discover all available pages before exploring further.

# File tools

> Read, write, edit, and search files in your project. These are the most frequently used tools in Claurst.

Claurst provides six built-in file tools. All file-path parameters must be **absolute paths**. Tools that write files require a permission prompt unless the session is in `AcceptEdits` or `BypassPermissions` mode.

<Note>
  Before Claude can edit or overwrite a file, it must have read that file in the same session. This **read-before-write** enforcement prevents clobbering concurrent changes.
</Note>

***

## Read

Read file contents. Returns text lines prefixed with line numbers, or a typed result for images, PDFs, and notebooks.

**Tool name:** `Read`

### Parameters

<ParamField path="file_path" type="string" required>
  Absolute path to the file to read.
</ParamField>

<ParamField path="offset" type="integer">
  1-based line number to start reading from. Omit to start at line 1.
</ParamField>

<ParamField path="limit" type="integer">
  Maximum number of lines to return. The Rust implementation defaults to 2 000 lines.
</ParamField>

<ParamField path="pages" type="string">
  PDF page range to extract (e.g. `"1-5"`, `"3"`, `"10-20"`). PDF files only. Maximum 20 pages per call.
</ParamField>

### Return value

The return type varies by file format:

<ResponseField name="type" type="string" required>
  Discriminant: `"text"`, `"image"`, `"notebook"`, `"pdf"`, `"parts"`, or `"file_unchanged"`.
</ResponseField>

<ResponseField name="content" type="string">
  Line-numbered file content. Present when `type` is `"text"`.
</ResponseField>

<ResponseField name="numLines" type="integer">
  Lines returned in this slice. Present when `type` is `"text"`.
</ResponseField>

<ResponseField name="startLine" type="integer">
  First line number returned (1-based). Present when `type` is `"text"`.
</ResponseField>

<ResponseField name="totalLines" type="integer">
  Total lines in the file. Present when `type` is `"text"`.
</ResponseField>

<ResponseField name="base64" type="string">
  Base64-encoded binary data. Present when `type` is `"image"` or `"pdf"`.
</ResponseField>

### Notes

* Blocked device paths (`/dev/zero`, `/dev/random`, `/dev/urandom`, etc.) are rejected to prevent infinite reads.
* Binary files that are not images or PDFs return an error.
* Reading a file registers it in the session's read-state cache, which enables `Edit` and `Write` to operate on it.

### Example

```json theme={null}
{
  "file_path": "/home/user/project/src/main.rs",
  "offset": 1,
  "limit": 50
}
```

```
1	fn main() {
2	    println!("Hello, world!");
3	}
```

***

## Write

Create a new file or completely overwrite an existing one.

**Tool name:** `Write`\
**Permission:** `Write` (prompts unless `AcceptEdits` or `BypassPermissions` mode)

### Parameters

<ParamField path="file_path" type="string" required>
  Absolute path to the file to create or overwrite.
</ParamField>

<ParamField path="content" type="string" required>
  Full content to write. The file is replaced atomically.
</ParamField>

### Return value

<ResponseField name="type" type="string" required>
  `"create"` for new files, `"update"` for overwrites.
</ResponseField>

<ResponseField name="filePath" type="string" required>
  Absolute path that was written.
</ResponseField>

<ResponseField name="structuredPatch" type="object">
  Diff of changes applied, useful for display.
</ResponseField>

<ResponseField name="originalFile" type="string">
  Previous file content. `null` for new files.
</ResponseField>

### Notes

* Parent directories are created automatically.
* For existing files, the file must have been read in this session first (read-before-write).
* If the file has been modified on disk since the last read, the write is rejected to avoid clobbering concurrent changes.
* `.ipynb` files are automatically redirected to `NotebookEdit`.
* Maximum file size: 1 GiB.

### Example

```json theme={null}
{
  "file_path": "/home/user/project/src/config.toml",
  "content": "[server]\nport = 8080\n"
}
```

***

## Edit

Make an exact string replacement inside an existing file. Prefer `Edit` over `Write` when you only need to change part of a file — it is faster and produces a cleaner diff.

**Tool name:** `Edit`\
**Permission:** `Write` (prompts unless `AcceptEdits` or `BypassPermissions` mode)

### Parameters

<ParamField path="file_path" type="string" required>
  Absolute path to the file to edit. The file must already exist.
</ParamField>

<ParamField path="old_string" type="string" required>
  The exact text to find and replace. Must be present in the file. Unless `replace_all` is `true`, this string must appear **exactly once** — if it appears multiple times the call fails.
</ParamField>

<ParamField path="new_string" type="string" required>
  The replacement text. Must differ from `old_string`.
</ParamField>

<ParamField path="replace_all" type="boolean" default="false">
  When `true`, replace every occurrence of `old_string`. When `false` (default), the call fails if `old_string` appears more than once.
</ParamField>

### Return value

<ResponseField name="filePath" type="string" required>
  Path of the edited file.
</ResponseField>

<ResponseField name="structuredPatch" type="object" required>
  Diff of the applied change.
</ResponseField>

<ResponseField name="replaceAll" type="boolean" required>
  Whether `replace_all` was used.
</ResponseField>

<ResponseField name="userModified" type="boolean" required>
  Whether you modified the proposed diff before it was applied.
</ResponseField>

### Notes

* Read-before-write and mtime staleness checks apply (same as `Write`).
* The tool normalizes straight/curly quotes when matching `old_string`, and preserves the original quote style in the output.
* `.ipynb` files are redirected to `NotebookEdit`.
* A set of protected files (`.gitconfig`, `.bashrc`, `.zshrc`, `.mcp.json`, `.claude.json`) resist automatic editing.

### Example

```json theme={null}
{
  "file_path": "/home/user/project/src/lib.rs",
  "old_string": "fn hello() {\n    println!(\"hello\");\n}",
  "new_string": "fn hello() {\n    println!(\"Hello, Claurst!\");\n}"
}
```

***

## Glob

Find files by name pattern. Results are sorted by modification time (most recent first).

**Tool name:** `Glob`

### Parameters

<ParamField path="pattern" type="string" required>
  Glob pattern to match against file paths (e.g. `"**/*.rs"`, `"src/**/*.ts"`, `"*.toml"`).
</ParamField>

<ParamField path="path" type="string">
  Directory to search in. Defaults to the current working directory.
</ParamField>

### Return value

<ResponseField name="filenames" type="string[]" required>
  Matching file paths, relative to the working directory.
</ResponseField>

<ResponseField name="numFiles" type="integer" required>
  Number of files returned.
</ResponseField>

<ResponseField name="truncated" type="boolean" required>
  `true` if results were capped (the TypeScript implementation caps at 100; the Rust implementation at 250).
</ResponseField>

<ResponseField name="durationMs" type="number" required>
  Time taken to complete the search.
</ResponseField>

### Example

```json theme={null}
{
  "pattern": "**/*.rs",
  "path": "/home/user/project/src"
}
```

```
src/main.rs
src/lib.rs
src/tools/bash.rs
```

***

## Grep

Search file contents using a regular expression. Backed by `ripgrep` in the TypeScript implementation and a Rust `walkdir`+`regex` traversal in the Claurst Rust codebase.

**Tool name:** `Grep`

### Parameters

<ParamField path="pattern" type="string" required>
  Regular expression to search for (e.g. `"fn\\s+\\w+"`, `"TODO|FIXME"`).
</ParamField>

<ParamField path="path" type="string">
  File or directory to search. Defaults to the current working directory.
</ParamField>

<ParamField path="glob" type="string">
  Restrict search to files matching this glob (e.g. `"*.rs"`, `"**/*.tsx"`).
</ParamField>

<ParamField path="output_mode" type="string" default="files_with_matches">
  Controls output format:

  * `"files_with_matches"` — list of matching file paths (default)
  * `"content"` — matching lines with optional context
  * `"count"` — match count per file
</ParamField>

<ParamField path="-i" type="boolean">
  Case-insensitive matching.
</ParamField>

<ParamField path="-n" type="boolean">
  Show line numbers in `content` mode.
</ParamField>

<ParamField path="-A" type="integer">
  Lines of context after each match (`output_mode: "content"` only).
</ParamField>

<ParamField path="-B" type="integer">
  Lines of context before each match (`output_mode: "content"` only).
</ParamField>

<ParamField path="-C" type="integer">
  Lines of context before and after each match. Equivalent to setting both `-A` and `-B`.
</ParamField>

<ParamField path="context" type="integer">
  Alias for `-C`.
</ParamField>

<ParamField path="type" type="string">
  File type shortcut (e.g. `"rs"`, `"ts"`, `"py"`, `"js"`, `"go"`). Expands to the relevant file extensions.
</ParamField>

<ParamField path="head_limit" type="integer" default="250">
  Limit output to the first N lines or entries.
</ParamField>

<ParamField path="offset" type="integer" default="0">
  Skip the first N entries (for pagination).
</ParamField>

<ParamField path="multiline" type="boolean" default="false">
  Enable multiline matching (`.` matches newlines).
</ParamField>

### Return value

<ResponseField name="mode" type="string" required>
  The `output_mode` that was used.
</ResponseField>

<ResponseField name="numFiles" type="integer" required>
  Number of files that matched.
</ResponseField>

<ResponseField name="filenames" type="string[]" required>
  Matching file paths.
</ResponseField>

<ResponseField name="content" type="string">
  Matching lines. Present when `mode` is `"content"`.
</ResponseField>

<ResponseField name="numMatches" type="integer">
  Total match count. Present when `mode` is `"count"`.
</ResponseField>

### Notes

* VCS directories (`.git`, `.svn`, `.hg`, `.jj`) and build directories (`node_modules/`, `target/`, `__pycache__/`) are excluded automatically.
* The TypeScript implementation caps individual lines at 500 characters.

### Example

```json theme={null}
{
  "pattern": "async fn",
  "path": "/home/user/project/src",
  "glob": "*.rs",
  "output_mode": "content",
  "-n": true,
  "-C": 2
}
```

***

## NotebookEdit

Edit a cell in a Jupyter `.ipynb` notebook. Supports inserting, replacing, and deleting cells.

**Tool name:** `NotebookEdit`\
**Permission:** `Write`

### Parameters

<ParamField path="notebook_path" type="string" required>
  Absolute path to the `.ipynb` file.
</ParamField>

<ParamField path="new_source" type="string" required>
  New source content for the cell (for `replace` and `insert` modes).
</ParamField>

<ParamField path="edit_mode" type="string" default="replace">
  Operation to perform: `"replace"`, `"insert"`, or `"delete"`.
</ParamField>

<ParamField path="cell_id" type="string">
  Target cell ID. Required for `replace` and `delete`. Omit when inserting a new cell.
</ParamField>

<ParamField path="cell_type" type="string">
  Cell type for new cells: `"code"` or `"markdown"`. Used with `insert` mode.
</ParamField>

### Return value

<ResponseField name="notebook_path" type="string" required>
  Path of the modified notebook.
</ResponseField>

<ResponseField name="cell_id" type="string">
  Cell ID that was affected.
</ResponseField>

<ResponseField name="edit_mode" type="string" required>
  The operation that was applied.
</ResponseField>

<ResponseField name="original_file" type="string" required>
  Full notebook JSON before the edit.
</ResponseField>

<ResponseField name="updated_file" type="string" required>
  Full notebook JSON after the edit.
</ResponseField>

### Notes

* Read-before-write and mtime staleness checks apply.
* On `replace`, `execution_count` and `outputs` are cleared to avoid stale output display.
* In the Rust implementation, cells can also be targeted by a `cell-N` index pattern (e.g. `"cell-0"` for the first cell).

### Example

```json theme={null}
{
  "notebook_path": "/home/user/project/analysis.ipynb",
  "cell_id": "abc123",
  "new_source": "import pandas as pd\ndf = pd.read_csv('data.csv')\ndf.head()",
  "edit_mode": "replace"
}
```
