Skip to content

The tools

The twenty-five tools, their inputs, outputs, and error codes. Eleven are available in read-only mode; the other fourteen require the full surface. In read-only mode only the read tools are registered. outline and check_syntax additionally require the optional @vscode/tree-sitter-wasm peer dependency (npm i @vscode/tree-sitter-wasm) — without it they are hidden from both surfaces. This mirrors the canonical SPEC.md in the repo.

ToolMutatingSummary
read_filenoRead a text file (UTF-8/UTF-16), with line numbers, paging, tail.
read_filesnoRead several text files in one call, each under a numbered header.
read_imagenoRead an image (PNG/JPEG/GIF/WebP) as a base64 image part.
list_dirnoList the entries of a directory.
globnoFind files by glob, most-recently-modified first.
grepnoSearch file contents by regular expression (optionally multiline).
diffnoUnified diff between two text files, without git.
file_statnoStructured metadata for a path (type, size, mtime, mode) as JSON.
treenoPrint a directory as an indented, gitignore-aware tree.
outlinenoSymbol skeleton of a source file with line ranges (tree-sitter).
check_syntaxnoParse a source file and report syntax errors (tree-sitter).
write_fileyesCreate or overwrite a file (atomic).
edit_fileyesReplace one exact occurrence of a string in a file.
multi_edityesApply several edit_file-style edits to one file atomically.
apply_patchyesApply a unified diff (modify/create/delete/rename) atomically.
replaceyesProject-wide regex find/replace with a dry-run preview (atomic).
moveyesMove/rename one file (atomic).
copyyesCopy one file, binary-safe (atomic).
mkdiryesCreate a directory and missing parents.
removeyesDelete one file.
bashyesRun a shell command (sh -c) and capture stdout/stderr/exit.
monitor_startyesStart a background command (dev server, watcher); return an id, optionally waiting until ready.
monitor_pollyesRead a monitor's new output since a byte offset; report running state and exit code.
monitor_stopyesStop a monitor (SIGTERM→SIGKILL) and remove its files.
monitor_listyesList running and finished monitors.

Every failure is a JSON error envelope. Success is plain text for most tools; bash and the monitor_* tools return a JSON object, and read_image returns a base64 image part.

read_file

Read a text file. UTF-8 by default; a UTF-16 file with a byte-order mark (LE/BE) is detected and decoded (but the editing tools refuse it — see Text & encoding).

InputTypeRequiredDefaultNotes
pathstringyesFile to read.
offsetintegerno11-based start line. A negative value counts from the end (-N reads the last N lines); 0 is invalid.
limitintegerno2000Maximum lines to return.

Output. Lines prefixed with right-aligned line numbers and a tab. An empty file returns (empty file). Over-long lines are truncated with a marker. When more lines remain, a continuation hint with the next offset is appended.

Errors. not_found, not_a_file, is_binary, too_large, path_escape, invalid_input (offset 0, or a positive offset past EOF).

read_files

Read a batch of text files in one call — use it instead of many read_file calls when you already know the paths. Each file's content is rendered exactly like read_file (numbered lines) under a ==> <path> <== header.

InputTypeRequiredDefaultNotes
pathsstring[]yes1–64 files to read, in order. Each relative or absolute.

Output. One section per path. A readable file appears under ==> <path> <== with numbered lines ((empty file) for an empty one). A path that fails — missing, binary, a directory, oversized, or escaping — becomes a single ==> <path> — <code>: <message> <== line without failing the others. The combined output is capped by maxOutputBytes: a large file is line-truncated, and once the budget is exhausted the remaining files are dropped with a [... N more file(s) not shown ...] marker (call again with fewer paths).

Errors. invalid_input only (empty array, more than 64 paths, wrong types). Per-file problems are reported inline, not as a tool error.

read_image

Read an image file and return it as an image part so a vision-capable model can view it. Produces no text part.

InputTypeRequiredDefaultNotes
pathstringyesImage to read.

Output. A single image part { type: "image", data, mimeType } where data is base64. The format is detected from the file's magic bytes; PNG, JPEG, GIF, and WebP are supported. The source is confined to the workspace and refused if larger than maxImageBytes (default 5 MB).

Errors. not_found, not_a_file (directory), not_an_image (unrecognized format), too_large (larger than maxImageBytes), path_escape.

list_dir

List the entries of a directory (non-recursive).

InputTypeRequiredDefaultNotes
pathstringnoworkspace rootDirectory to list.

Output. One entry per line; directories are marked. An empty directory returns a sentinel line rather than an empty result.

Errors. not_found, not_a_file (path is a file), path_escape, io_error.

glob

Find files (not directories) by glob pattern.

InputTypeRequiredDefaultNotes
patternstringyesGlob pattern, e.g. **/*.ts.
pathstringnoworkspace rootBase directory to search from.
respect_gitignorebooleannotrueSkip files ignored by the git ignore stack, and the .git/ dir.

Output. Matching file paths, most-recently-modified first. Hidden files are included. No matches returns (no matches) (a success, not an error).

Errors. not_found, path_escape, invalid_input (bad glob).

grep

Search file contents by regular expression, recursively. Uses ripgrep when available, otherwise an equivalent in-process fallback; both apply one consistent policy (hidden files searched, .git/ skipped, the full git ignore stack respected, binary and oversized files skipped).

InputTypeRequiredDefaultNotes
patternstringyesRegex (Rust/ripgrep syntax).
pathstringnoworkspace rootFile or directory to search.
globstringnoRestrict to files matching this glob.
output_modeenumnofiles_with_matchesfiles_with_matches | content | count.
ignore_casebooleannofalseCase-insensitive matching.
multilinebooleannofalseMatch across line boundaries (. spans \n, ^/$ at line bounds).
contextintegerno0Context lines both sides (content mode only).
before_contextintegernoLines before each match (content mode); overrides context for that side.
after_contextintegernoLines after each match (content mode); overrides context for that side.
head_limitintegernoMax results to return (files, or matches in content mode). ≥ 0; 0 or omit for unlimited.
offsetintegerno00-based number of results to skip — a result offset, not read_file's line offset.

Output. files_with_matches prints one file path per line; content prints path:line:text (with context if requested); count prints path:N. No matches returns (no matches). Output is deterministic (sorted by path, then line). head_limit/offset page over the collected results; a footer reports state honestly (showing A..B of N; call again with offset=B for more), or warns that the scan was incomplete when it was cut off by the output cap.

Errors. not_found, path_escape, invalid_input (bad regex).

diff

Compare two text files and return a standard unified diff — no git required. Both files are read and their line endings normalized to LF before comparison, so a pure CRLF-vs-LF difference reports no change.

InputTypeRequiredDefaultNotes
fromstringyesThe original (left) file.
tostringyesThe changed (right) file.

Output. A unified diff (--- from, +++ to, @@ hunks). Identical content returns (no differences). The diff is bounded by maxOutputBytes (a huge diff is truncated with a marker).

Errors. not_found, not_a_file, is_binary, too_large, path_escape (for either operand).

file_stat

Return structured metadata for one path, as a JSON object — inspect a path before reading it.

InputTypeRequiredDefaultNotes
pathstringyesPath to inspect.

Output. A JSON object { path, type, size, mtime, mode, ... } where type is file | directory | symlink | other, mtime is ISO-8601, and mode is an octal string (e.g. "0644"). A symlink is reported without being followed, with a symlink_target. For a regular file it also reports binary (whether the content looks binary) and mime (an image MIME type or null), reading only a small head slice — so it works on files too large to read.

Errors. not_found, path_escape, io_error.

tree

Print a directory as an indented tree — directories end with /, symlinks with @, and files show a byte size.

InputTypeRequiredDefaultNotes
pathstringnoworkspace rootRoot directory of the tree.
depthintegerno4Maximum levels to descend below the root (≥ 0; 0 or omit = default of 4). Max 20.
respect_gitignorebooleannotrueSkip files ignored by the git ignore stack, and the .git/ dir.

Output. An indented ASCII tree rooted at path. Symlinked directories are listed but not traversed (cycle-safe). Output is byte-bounded like grep/read_file. An empty (or fully ignored) directory prints a (no entries) line.

Errors. not_found, not_a_file (path is a file), path_escape, io_error.

outline

Return the symbol skeleton of one source file — classes, functions, methods and other declarations as indented lines with 1-based (start-end) line ranges. Use it to understand an unfamiliar file cheaply, then read only the relevant ranges with read_file. Requires the optional @vscode/tree-sitter-wasm peer dependency.

InputTypeRequiredDefaultNotes
pathstringyesSource file to outline.

Output. A header line <path> — <language>, <N> lines, then one indented line per symbol (two spaces per nesting level): the declaration's first source line (trimmed, truncated) followed by its (start-end) line range. A file with no symbols prints (no symbols found). Output is capped at 2000 symbols (a trailing [... N more symbols omitted ...] line reports the rest) and byte-bounded like read_file. If the file has syntax errors the outline is still produced, with a trailing note: pointing at check_syntax.

The language is picked by file extension: typescript (.ts/.mts/.cts), tsx, javascript (.js/.mjs/.cjs/.jsx), python, go, rust, java, and c-sharp. Other extensions — including ones check_syntax supports — are invalid_input.

Errors. invalid_input (unsupported extension), not_found, not_a_file, is_binary, too_large (input over MAX_FILE_BYTES or the 2 MB parse limit), path_escape, timeout, aborted, io_error.

check_syntax

Parse one source file with tree-sitter and report syntax errors — the parser's ERROR and MISSING nodes — as JSON. A pure parse check: it does not type-check, resolve imports, or lint; ok: true means the file parses, not that it compiles. Requires the optional @vscode/tree-sitter-wasm peer dependency.

InputTypeRequiredDefaultNotes
pathstringyesSource file to check.

Output. A JSON object { path, language, ok, errors, error_count, truncated } where each entry of errors is { kind, line, column, near }kind is error (stray/unparseable input, near is the source line) or missing (a token the parser expected, near is that token), and line/column are 1-based. At most 50 errors are reported (truncated: true beyond that).

The language is picked by file extension; every bundled grammar is supported: the eight outline languages plus ruby, php, bash, css, ini, powershell, and c/cpp (both routed to the cpp grammar).

Errors. invalid_input (unsupported extension), not_found, not_a_file, is_binary, too_large (input over MAX_FILE_BYTES or the 2 MB parse limit), path_escape, timeout, aborted, io_error.

write_file

Create or overwrite a file with the given content (atomic: temp file + rename).

InputTypeRequiredNotes
pathstringyesDestination.
contentstringyesFull content to write, exactly.

Output. A success message with the byte count and whether the file was created or overwritten. Parent directories must already exist.

Syntax warning. When the optional @vscode/tree-sitter-wasm peer dependency is installed and the file's extension has a grammar (see check_syntax), the success message of write_file, edit_file, multi_edit, and apply_patch gains a trailing warning: <language> syntax error in <file> at line N, column C ... line when the written content does not parse. Advisory only: the write always succeeds, the warning never becomes an error, and files without a grammar, oversized content (> 1 MB), or a slow parse are silently skipped (apply_patch checks at most five files per patch).

Structured diff. write_file (on overwrite), edit_file, multi_edit, and replace (on apply) also set meta.diff on the result to a real unified diff of the change (true line numbers, three lines of context). It is a sidecar for a client to render — the model-facing content stays the short prose summary. Absent when there is nothing to compare (a brand-new write_file, or an overwrite whose prior content is binary/unreadable). apply_patch emits no meta.diff (its input already is the diff).

Errors. not_found (missing parent), not_a_file, path_escape, io_error.

edit_file

Replace one occurrence of old_string with new_string.

InputTypeRequiredDefaultNotes
pathstringyesFile to edit.
old_stringstringyesText to find, matched literally (no regex, no line-number prefixes).
new_stringstringyesReplacement; must differ from old_string.
replace_allbooleannofalseReplace every exact occurrence instead of requiring a unique one.

Matching. old_string is matched literally and exactly first, and must be unique unless replace_all is set. When an exact match is not found (single-replacement path only), a whitespace-tolerant cascade runs — strictest→loosest: indentation-flexible, per-line-trimmed, all-whitespace-collapsed, then trimmed-substring. It applies only if it resolves to exactly one region (otherwise ambiguous_match, never a guess), and the success message discloses that a tolerant match was used. Line endings and BOM are preserved. The result may carry a syntax warning.

Errors. no_match, ambiguous_match, invalid_input (identical strings), not_found, is_binary, too_large, path_escape.

multi_edit

Apply several edit_file-style edits to ONE file in a single atomic call.

InputTypeRequiredNotes
pathstringyesFile to edit.
editsarrayyesAt least one { old_string, new_string, replace_all? }; applied in order.

Behavior. Edits run in order, each operating on the result of the previous, and each inherits edit_file's exact-then-tolerant matching. The whole batch is applied atomically; any failing edit aborts the call — nothing is written — and the error names the failing edit index. The result may carry a syntax warning.

Errors. As edit_file, prefixed with the failing edit index.

apply_patch

Apply a unified diff across one or more files atomically.

InputTypeRequiredNotes
patchstringyesA unified diff spanning one or more files.

Behavior. A /dev/null source (---) denotes a create; a /dev/null target (+++) a delete; a block whose old and new paths differ is a rename/move (with hunks it moves and edits in one step; without them it is a pure rename that preserves the exact bytes). Each file block is validated and applied; all changes commit together or roll back together. Creating an existing path, renaming onto an existing destination, or naming the same path twice is rejected. Modified files preserve line endings and BOM; created files use LF. The result may carry syntax warnings for up to five written files.

Errors. patch_failed (a hunk did not apply; names the file), invalid_input, not_found, not_a_file, is_binary, too_large, path_escape, io_error.

replace

Project-wide find/replace, preview-first. pattern is a regular expression (same engine as grep); replacement may reference capture groups ($1..$9), the whole match ($&), or a literal $ ($$). Scope with path (a file or directory) and/or glob; at least one is required. Ignored files (.gitignore), binary files, and oversized files are skipped. It replaces the sed -i-via-bash pattern with a workspace-confined, guardable, atomic operation.

InputTypeRequiredDefaultNotes
patternstringyesRegular expression to match.
replacementstringyesReplacement text; $1..$9/$& capture refs, $$ for $.
pathstringno*File or directory to scope to (a directory is walked).
globstringno*Glob filtering files under the scope (**/*.ts; bare = any dir).
ignore_casebooleannofalseCase-insensitive matching (i flag).
multilinebooleannofalseMatch across lines and let . span newlines (m+s flags).
dry_runbooleannotruePreview only; set false to apply.

* At least one of path / glob is required.

Output. With dry_run: true (the default): a totals line (N replacement(s) across M file(s)) followed by a unified-diff preview per changed file — nothing is written. With dry_run: false: the edits are applied atomically (all files succeed or none do, each file's line endings and BOM preserved) and a per-file summary is returned, possibly carrying syntax warnings for up to five files. No matches yields (no matches).

Errors. invalid_input (bad regex, a pattern matching the empty string, or neither path nor glob), not_found (a missing explicit path), path_escape, io_error. Writing through a symlink is refused (invalid_input).

move

Move or rename one file (atomic rename). Files only — a directory source is rejected; use bash for directory moves.

InputTypeRequiredDefaultNotes
sourcestringyesFile to move.
destinationstringyesNew path. Missing parent directories are created.
overwritebooleannofalseWhen true, replace an existing destination file.

Output. A success message naming the source and destination. Refuses (invalid_input) if the destination already exists unless overwrite is true, and if source equals destination. The source's permission mode is preserved. Refuses to move through a symlink at either endpoint.

Errors. not_found (missing source), not_a_file (directory source, or directory destination), invalid_input (destination exists without overwrite, same source/destination, or a symlink), path_escape, io_error.

copy

Copy one file (atomic publish via temp + rename, binary-safe). Files only — a directory source is rejected; use bash for directory copies.

InputTypeRequiredDefaultNotes
sourcestringyesFile to copy.
destinationstringyesCopy target. Missing parent directories are created.
overwritebooleannofalseWhen true, replace an existing destination file.

Output. A success message naming the source and destination. The source's permission mode is preserved and its bytes are copied exactly (binary-safe). Same refusals as move (existing destination, identical paths, symlink endpoints).

Errors. not_found, not_a_file, invalid_input, path_escape, io_error.

mkdir

Create a directory, including any missing parents (like mkdir -p). Idempotent — succeeds if the directory already exists.

InputTypeRequiredNotes
pathstringyesDirectory to create. Missing parent directories are made.

Output. A message stating the directory was created (or already existed). Fails if the path already exists as a file.

Errors. not_a_file (path, or a parent component, is a file), path_escape, io_error.

remove

Delete one file. Files only — a directory is rejected; use bash for recursive directory removal.

InputTypeRequiredNotes
pathstringyesFile to delete.

Output. A message naming the removed file. Fails with not_found if the path does not exist, and refuses to delete through a symlink.

Errors. not_found, not_a_file (path is a directory), invalid_input (symlink), path_escape, io_error.

bash

Run a shell command via sh -c and return stdout, stderr, and exit code.

InputTypeRequiredDefaultNotes
commandstringyesThe shell command.
cwdstringnoworkspace rootWorking directory.
timeout_msintegerno120000Max run time; may be raised up to bashTimeoutMaxMs (600000). A larger value clamps.

Output. On success, a JSON object { exit_code, stdout, stderr, signal, timed_out }. The command runs to completion and blocks until it exits (stdin is closed) — a long-lived process must be backgrounded, e.g. npm start > /tmp/out.log 2>&1 &. A non-zero exit is a normal result, not an error (isError is false). stdout/stderr are budgeted against a shared maxBashOutputBytes (default 16 KB — lower than maxOutputBytes, since command logs are noisier than a file the model chose to read). On overflow the full output is written to a .clarvis/ spill file and the inline result keeps the tail (the end, where errors and summaries live) behind a marker naming the spill path (see Limits & spill).

Errors. timeout (process group killed; partial output returned), output_limit (a hard per-stream in-memory ceiling was hit and the command was killed), not_found / not_a_file (bad cwd), path_escape (cwd outside the workspace), io_error (spawn failure).

monitor_start

Standardized background-process monitors. bash blocks until a command exits; a monitor is its complement for a command that doesn't — a dev server, tail -f, a watcher. No state is held in-process: each call re-derives the truth from the .clarvis/monitor-<id>.{json,log,exit} sidecars and the live OS process, so monitors are as stateless as the file tools.

monitor_start launches a command in the background and returns its id immediately, optionally blocking until the output signals it is ready.

InputTypeRequiredDefaultNotes
commandstringyesShell command, run via sh -c. Do not append a trailing & — the monitor backgrounds it.
cwdstringnoworkspace rootWorking directory.
ready_whenstringnoRegex; when set, the call blocks until the combined output matches it (see below).
ready_timeout_msintegerno30000Max wait for ready_when (monitorReadyTimeoutMs). Ignored unless ready_when is set.

Output. Returns immediately with { id, running, ready, output, next_offset }. The command's combined stdout+stderr is appended to .clarvis/monitor-<id>.log. When ready_when is given the call blocks until the log matches it — or until ready_timeout_ms elapses, or the process exits — and ready reports whether the match was seen (null when no ready_when was given). ready_when is tested against the first maxOutputBytes of output, so a marker printed only after that much startup chatter is not detected. At most maxMonitors (default 32) live monitors may exist at once.

Errors. too_many_monitors (maxMonitors reached), invalid_input (bad ready_when regex), not_found / not_a_file (bad cwd), path_escape (cwd outside the workspace), io_error (spawn failure).

monitor_poll

Read new output from a monitor since a byte offset.

InputTypeRequiredDefaultNotes
idstringyesThe monitor id from monitor_start.
offsetintegerno0Byte offset to read from; pass back the previous poll's next_offset.
matchstringnoRegex; keep only matching lines.

Output. { running, output, next_offset, exit_code } — pass next_offset back to page forward. exit_code is the command's natural exit code once it has exited on its own: null while running, and null if the monitor was stopped or killed. Output is bounded to maxOutputBytes; while the process is still running and match is not set, a partial trailing line is held back so a line is not split across polls (with match, or when the byte cap truncates the slice, a line may span two polls).

Errors. monitor_not_found (unknown id), invalid_input (bad match regex).

monitor_stop

Stop a monitor and remove its files.

InputTypeRequiredNotes
idstringyesThe monitor id from monitor_start.

Output. { stopped, id }. Signals the monitor's whole process group (SIGTERM, then SIGKILL after a short grace) and removes its sidecars. Idempotent for an already-exited monitor — it just cleans up.

Errors. monitor_not_found (unknown id).

monitor_list

List every monitor, running and finished. Takes no arguments.

Output. { monitors: [ { id, command, running, started_at, cwd } ] }, newest first. Use it to find and stop leaked monitors.

Errors. None specific to this tool.

Reaping & leaks. A monitor's process outlives the tool call and survives host exit, so a forgotten monitor leaks (the same exec exposure as bash, not a sandbox). sweepMonitors(workspaceRoot) — the companion to sweepSpillDir — removes the sidecars of monitors whose process has exited and leaves live ones untouched; call it at session start. exit_code is captured for a natural exit only: a command that execs away or is killed leaves exit_code null. See Limits & spill and Core API → sweepMonitors.

See also

Released under the MIT License.