# Tools | Tool | Approval | Notes | | --- | --- | --- | | `read_file` | automatic | Numbered lines, `offset`/`limit` for paging; a PNG, JPEG, GIF or WebP comes back as a picture the model can look at | | `glob` | automatic | Newest first; `*.go` matches at any depth | | `grep` | automatic | ripgrep when present, pure Go otherwise, identical output either way | | `edit_file` | prompts | Requires a prior read; refuses ambiguous matches; returns a diff and any [diagnostics](#diagnostics) | | `write_file` | prompts | Whole files only; separate from `edit_file` so the metrics stay honest | | `bash` | prompts | Own process group, killed whole on timeout; output capped head and tail. `background: true` returns a handle instead of waiting | | `process_output` | never | New output from a background process since you last asked, and whether it is still running | | `process_kill` | never | Stops a background process and its children; everything is killed when the session ends anyway | | `todo_write` | never | The working plan for a multi-step task; the whole list is sent each time | | `context_status` | never | How full the window is, how many turns and what they cost | | `ask_user` | never | Puts a multiple-choice question to you and waits — see [questions](tui.md) | | `web_search` | prompts | Only with `web_search: custom`; runs `ddgs` on this machine | | `web_fetch` | prompts | With either web search mode; refuses private and loopback addresses | | `mcp____` | prompts | One per tool of each [MCP server](mcp.md) in the config | # Diagnostics The agent writes a file and finds out it broke the build three tool calls later, if at all. The cheapest fix is to put the compiler's own opinion in the result the model is already reading: ``` ⏺ EditFile(path=internal/agent/loop.go) ⎿ 4 additions, 1 removal … diagnostics (gopls): 2 problems loop.go:14:2: undefined: fmt.Printl loop.go:19:6: declared and not used: n ``` After a successful `edit_file` or `write_file`, the changed file goes to whatever this machine already has for its language — `gopls`, `pyright-langserver`, `typescript-language-server` or `rust-analyzer` if one is on `PATH`, and otherwise the compiler or linter: `go build` and `go vet`, `ruff` or `pyflakes`, `eslint` or `tsc`, `shellcheck`. Nothing is installed and nothing leaves the machine. **A clean file adds nothing at all.** Silence is the good news, and a "0 problems" line on every edit would be a paragraph of tokens per edit saying that nothing happened. Ten problems are reported and the rest counted, and a diagnostic about a *neighbouring* file — the helper you have just renamed out from under — is kept and labelled, because that is the one the agent has no other way to learn about. Language servers are started once and kept warm between edits, keyed by the workspace around the file rather than by the working root, so `/worktree` does not strand a warm `gopls` behind it. A check that has not finished in two seconds is abandoned rather than waited for: a tool call that hangs because a language server is confused is far worse than one that reports nothing. Every other way this can go wrong lands in the same place — a server that will not start, that dies mid-session, that answers with garbage, or one on `PATH` that turns out to be a broken shim — and the answer is always no diagnostics and a fall through to the linter, never a hung tool call. A server that fails to start is put on a five-minute cooldown rather than retried on every edit. It is on by default, which is the one setting here that leans that way: it runs a tool you already have, against a file you already told the agent to write, and nothing it reads goes anywhere. `"diagnostics": "off"` in the config, the `/settings` row, or `-no-diagnostics` for a single run turns it off.