Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

File Operations

These tools cover reading, listing, searching, and locating files. They are read-only — no file is modified. For writing and editing, see Editing.

All paths are relative to the active project root unless you supply an absolute path. Reads are subject to the project’s security deny-list (e.g. SSH keys and credential files are blocked by default).

See also: Output Buffers — how large file reads are stored as @file_id refs rather than dumped into context.


read_file

Purpose: Read the contents of a file, optionally restricted to a line range.

Parameters:

NameTypeRequiredDefaultDescription
pathstringyesFile path relative to project root
start_lineintegernoFirst line to return (1-indexed)
end_lineintegernoLast line to return (1-indexed, inclusive)

Example — read an entire file:

{
  "path": "src/main.rs"
}

Output:

{
  "content": "fn main() {\n    println!(\"Hello\");\n}\n",
  "total_lines": 3
}

Example — read a specific range:

{
  "path": "src/main.rs",
  "start_line": 10,
  "end_line": 25
}

When you supply both start_line and end_line, the tool returns exactly those lines with no overflow cap applied. When neither is supplied and the file exceeds 200 lines, only the first 200 lines are returned and an overflow field tells you how to retrieve the rest:

{
  "content": "... first 200 lines ...",
  "total_lines": 850,
  "overflow": {
    "shown": 200,
    "total": 850,
    "hint": "File has 850 lines. Use start_line/end_line to read specific ranges"
  }
}

Tips:

  • Use symbols or symbols to locate the line range of a function before calling read_file — this lets you fetch exactly what you need without reading the whole file.
  • For large files, prefer reading in chunks with explicit start_line/end_line over reading the whole file.
  • If you want to search for a pattern rather than read, use grep instead.

Source-range gate

When start_line / end_line is supplied on a source file (.rs, .ts, .py, and other languages codescout recognises), the tool checks whether the requested range overlaps a named symbol body. If it does, the request is blocked and the error names the overlapping symbol:

Error: range 45–72 overlaps symbol `MyStruct/validate` — use symbols(name='validate', include_body=true) instead

This steers you toward symbols(include_body=true), which is robust to line-number shifts caused by later edits.

Bypass: Add "force": true to skip the gate and return the raw content regardless:

{ "path": "src/model.rs", "start_line": 45, "end_line": 72, "force": true }

Scope: Only recognised source files are gated. Config files, Markdown, TOML, JSON, and other non-code formats are never affected.


tree

Purpose: List files and directories under a path. Pass recursive=true for a full tree.

Parameters:

NameTypeRequiredDefaultDescription
pathstringyesDirectory path relative to project root
recursivebooleannofalseDescend into subdirectories
detail_levelstringnocompact"full" to show all entries without the exploring-mode cap
offsetintegerno0Skip this many entries (focused-mode pagination)
limitintegerno50Max entries per page in focused mode

Example — shallow listing:

{
  "path": "src"
}

Output:

{
  "entries": [
    "/home/user/project/src/main.rs",
    "/home/user/project/src/lib.rs",
    "/home/user/project/src/tools/"
  ]
}

Directories are suffixed with /. In exploring mode the output is capped at 200 entries; if the directory has more, an overflow field appears with guidance.

Example — full recursive tree:

{
  "path": "src",
  "recursive": true
}

Hidden files and paths matched by .gitignore are excluded automatically.

Tips:

  • Start with a shallow listing to understand the top-level structure, then drill into subdirectories of interest.
  • Use recursive=true only when you need the full tree. On large repositories a recursive walk can produce many entries; narrow it with a more specific path if you hit the overflow cap.
  • To find files by name pattern use tree (with glob) instead — it supports glob patterns and is faster for targeted searches.

grep

Purpose: Search the codebase for a regex pattern. Returns matching lines with file path and line number.

Parameters:

NameTypeRequiredDefaultDescription
patternstringyesRegular expression to search for
pathstringnoproject rootDirectory to restrict the search to
max_resultsintegerno50Maximum number of matching lines to return

Example:

{
  "pattern": "fn\\s+validate_\\w+",
  "path": "src",
  "max_results": 20
}

Output:

{
  "matches": [
    {
      "file": "/home/user/project/src/util/path_security.rs",
      "line": 14,
      "content": "pub fn validate_read_path("
    },
    {
      "file": "/home/user/project/src/util/path_security.rs",
      "line": 38,
      "content": "pub fn validate_write_path("
    }
  ],
  "total": 2
}

The search walks the directory tree using the same .gitignore-aware walker as tree. Binary files that cannot be decoded as UTF-8 are silently skipped. The regex engine enforces size limits to prevent pathological patterns from hanging.

Tips:

  • Use path to narrow the search when you already know which part of the codebase is relevant — this is significantly faster on large repos.
  • Increase max_results if you expect many matches and need to see them all.
  • When you know a symbol name, symbols is more precise than a regex search because it uses the LSP index. Use grep when you are looking for text patterns, string literals, comments, or constructs that the LSP does not model as symbols.
  • To find files by name (not content), use tree (with glob).

tree (with glob)

Purpose: Find files matching a glob pattern. Respects .gitignore.

Parameters:

NameTypeRequiredDefaultDescription
patternstringyesGlob pattern (e.g. **/*.rs, src/**/mod.rs)
pathstringnoproject rootDirectory to search within
max_resultsintegerno100Maximum number of file paths to return

Example:

{
  "pattern": "**/*.toml",
  "path": "."
}

Output:

{
  "files": [
    "/home/user/project/Cargo.toml",
    "/home/user/project/.codescout/project.toml"
  ],
  "total": 2
}

Example — find all test files in a subdirectory:

{
  "pattern": "**/test_*.py",
  "path": "tests"
}

The glob is matched against the path relative to the search directory, so **/*.rs will match files at any depth. The walker respects .gitignore, so build artifacts, vendored dependencies, and other ignored paths are excluded.

Tips:

  • Prefer tree (with glob) over tree when you are looking for files by name — the glob match is more expressive than scanning a directory tree manually.
  • Use grep when you need to find files by their contents rather than their names.
  • The ** wildcard matches across directory boundaries. Use it for language-wide searches like **/*.rs or to locate files with a specific name anywhere in the tree: **/Makefile.

create_file

Purpose: Create a new file or overwrite an existing file with given content.

Parameters:

NameTypeRequiredDefaultDescription
pathstringyesFile path relative to project root
contentstringyesFull file content to write

Example:

{
  "tool": "create_file",
  "arguments": {
    "path": "src/utils/helpers.rs",
    "content": "pub fn clamp(v: f64, min: f64, max: f64) -> f64 {\n    v.max(min).min(max)\n}\n"
  }
}

Output: "ok"

Tips:

  • Creates parent directories if they don’t exist.
  • Overwrites without warning — check that the path is correct before writing.
  • For editing existing files, use edit_file instead.

See Editing for more usage guidance.


edit_file

Purpose: Find-and-replace editing within an existing file. Matches an exact string and replaces it — whitespace-sensitive.

Parameters:

NameTypeRequiredDefaultDescription
pathstringyesFile path relative to project root
old_stringstringyesExact text to find (must match including whitespace)
new_stringstringyesReplacement text
replace_allbooleannofalseReplace every occurrence instead of just the first
insertstringno"prepend" or "append" — add text at the start/end of the file

Example — change an import:

{
  "tool": "edit_file",
  "arguments": {
    "path": "src/main.rs",
    "old_string": "use crate::utils::old_helper;",
    "new_string": "use crate::utils::new_helper;"
  }
}

Output: "ok"

Tips:

  • old_string must match exactly — including indentation and line endings.
  • Use for imports, constants, config values, and small literal changes.
  • For changes to a function or struct body, prefer edit_code(action="replace") — it’s robust to line number shifts.

See Editing for full parameter details and more examples.