# Tools

Swival gives the model a fixed set of tools at runtime. Most tools are always available.

Command execution tools are included by default (commands default to `"all"`): `run_command` takes an argv array and is available in every command mode except `--commands none`, while `run_shell_command` takes a shell string and is only available with `--commands all` or `--yolo`. Pass `--commands none` to remove both, or `--commands ask` for interactive approval (argv-only, no shell).

`use_skill` appears only when skills are discovered, MCP tools appear when external MCP servers are configured, and A2A tools appear when remote A2A agents are configured.

## `read_file`

`read_file` can read text files and directory listings inside allowed roots. File output is line-numbered, which makes later edits precise. The default window starts at `offset=1` with `limit=2000` lines.

If output is truncated, Swival appends a continuation hint with the next offset. You can also request `tail_lines=N` to start from the end of the file, which is useful for logs. `tail_lines` is mutually exclusive with `offset`.

Large responses are capped at 50 KB per call by default, and individual long lines are truncated at 2,000 characters. Directory reads return sorted entries and mark subdirectories with a trailing `/`. Both the default line count (2000) and the size cap can be tuned with `--max-output-lines` and `--max-output-kb`, or the matching `max_output_lines` and `max_output_kb` config keys.

Each file read ends with a `[checksum=...]` trailer that hashes the file's current contents. The model can pass that value back to `edit_file` as a guard, so an edit fails if the file changed since it was read. See [`edit_file`](#edit_file) for how that check works.

## `read_multiple_files`

`read_multiple_files` reads several files in a single call. Each entry in the `files` array can specify its own `offset`, `limit`, and `tail_lines`, just like `read_file` (`offset` and `tail_lines` remain mutually exclusive per entry). Results are grouped by file with `--- path ---` headers and the same line-numbered format as `read_file`.

Per-file errors (missing files, binary files, path escapes) are reported inline without failing the batch. The total response is capped at 50 KB across all files. If the budget runs out mid-batch, the files already read are returned along with a truncation notice. A single oversized file is always included (with its own line-level truncation) so the tool never returns empty content for a valid request.

The batch is limited to 20 files per call. Directories are rejected with an inline error — use `read_file` for directory listings.

`read_multiple_files` participates in the read-before-write guard the same way `read_file` does: every file successfully read is recorded.

## `write_file`

`write_file` creates or overwrites files and automatically creates missing parent directories. It supports two mutually exclusive modes. In normal write mode, you provide `content`. In move mode, you provide `move_from` and Swival performs an atomic rename when possible.

When `move_from` is used and no `content` is provided, Swival moves the source path to the destination path without copying text content. This supports non-text files and symlinks as well. If the destination already exists, the read-before-write guard still applies to that destination, while the source path is exempt because rename does not modify source content.

A successful write ends with a `[checksum=...]` trailer that hashes the bytes just written, the same format `read_file` reports. The model can hand that value straight to a later `edit_file` on the path, so it never has to re-read a file it only just wrote.

## `edit_file`

`edit_file` is the main incremental editing tool. It replaces `old_string` with `new_string` in an existing file and supports `replace_all` when you intentionally want multiple replacements.

Matching is done in three passes. Swival tries an exact string match first. If that fails, it retries with per-line trimmed matching so leading and trailing whitespace differences do not break the edit. If that still fails, it retries with Unicode normalization so smart quotes, em dashes, and ellipsis variants map to ASCII equivalents.

When multiple matches are found and `replace_all` is false, the call fails with an error that nudges the model toward `line_number`. The optional `line_number` parameter accepts a 1-based line number from `read_file` output. When provided, Swival filters candidate matches to only those whose span includes that line.

This is the preferred way to disambiguate repeated matches: the model copies the line number it just read rather than expanding `old_string` with more context. `replace_all` ignores `line_number`.

A stale `line_number` no longer forces a retry. If the requested line misses every match but `old_string` still resolves to a single location across the three passes, Swival applies the edit there anyway. Matches that merely nest across passes (the exact substring sitting inside its line-trimmed or Unicode-normalized span) count as one location, so this fallback fires whenever the target is genuinely unique. Only when two or more distinct locations remain does the call fail, and then the error lists the actual candidate lines so the model can retry with the right one.

The optional `checksum` parameter guards against editing a file that changed since it was last read. Pass the latest `[checksum=...]` value reported for the path, whether that came from `read_file`, `write_file`, or an earlier `edit_file`. If the file no longer hashes to that value, the edit fails before touching anything, which catches the case where another process (or a parallel subagent) rewrote the file in between. It is optional: empty or whitespace-only values are treated as no checksum supplied, so weaker models that omit it are not penalized.

A successful edit returns a fresh `[checksum=...]` trailer for the file's new contents, computed by hashing the bytes back from disk so it matches exactly what the next checksum check will compute. That lets the model chain several edits to the same file in one turn, feeding each edit's returned checksum into the next, without an intervening `read_file`.

## `delete_file`

`delete_file` is a soft delete. Instead of removing files permanently, Swival moves them into `.swival/trash/<trash_id>/` and appends metadata to `.swival/trash/index.jsonl`. Directories are not allowed.

Trash retention is enforced automatically. Entries older than seven days are removed, and total trash size is capped at 50 MB with oldest-first eviction when needed.

## `list_files`

`list_files` recursively evaluates glob patterns such as `**/*.py` and returns matches sorted by file modification time, newest first. Results are capped at 100 files and output is still bounded by the same 50 KB response cap.

## `grep`

`grep` searches file contents with Python regular expressions. Matches are grouped by file, include line numbers, and are sorted by file recency so the newest files are surfaced first. You can narrow by directory with `path` and by filename glob with `include` (supports `**/*.ext` patterns). Set `context_lines` to show surrounding lines around each match; when active, matching lines are marked with `<<<` to distinguish them from context.

Set `case_insensitive` to `true` for case-insensitive matching. Results are capped at 100 matches and long lines are truncated to 2,000 characters.

## `outline`

`outline` shows the structural skeleton of one or more files: classes, functions, and top-level declarations with line numbers. No bodies are included. This is useful for surveying a file before reading specific sections.

Pass a directory instead of a file and `outline` returns a shallow survey of the directory's source files rather than a single file's skeleton. The `depth` parameter controls nesting (`1` top-level only, `2` classes plus methods, `3` nested functions and classes); it defaults to `2` for files and `1` for directory surveys. Pass `files` for a batch of up to 20 paths, each optionally carrying its own `depth`.

Pass `file_path` for a single file, or `files` for a batch (up to 20 files). The `depth` parameter controls nesting: 1 for top-level only, 2 for classes and methods (the default), 3 for nested functions and classes. In batch mode, each file can override the depth individually.

## `think`

`think` is structured scratchpad reasoning. It lets the model capture numbered thoughts, revise earlier thoughts, and branch from a prior thought to compare alternative approaches. This is especially helpful for debugging and multi-step refactors.

The only required parameter is `thought`. Everything else is optional.

`thought_number` is a 1-based step number that auto-increments if omitted. `total_thoughts` is the estimated total steps needed, defaulting to 3 on the first call and then carrying forward. `next_thought_needed` is a boolean that defaults to true: set it to false when done thinking.

A `mode` parameter (`"new"`, `"revision"`, `"branch"`) selects the type of thought. Revision mode requires `revises_thought` to reference an earlier thought number. Branch mode requires `branch_from_thought` plus a `branch_id` label.

The tool applies tolerant coercion so models that send extra or contradictory fields don't get stuck in validation loops. Incompatible fields are stripped based on the inferred mode, and corrective error messages include valid thought numbers when a reference is wrong.

## `todo`

`todo` tracks work items during a run. The list lives in memory for the duration of the session, surviving context compaction because the state object exists outside the message history. Actions include `add`, `done`, `remove`, `clear`, and `list`, and each action returns the full current list.

Matching for `done` and `remove` is fuzzy in a controlled way, so exact wording is not required every time. Swival tries exact matching first, then prefix matching, then substring matching. The list allows up to 50 items, and each item can be up to 500 characters.

## History Logging

Every final answer is appended to `.swival/HISTORY.md` with a timestamp and the originating question. This file is capped at 500 KB. When a new entry would exceed the cap, the oldest entries are trimmed to make room rather than dropping the new one. Use `--no-history` if you do not want history writes.

## `view_image`

`view_image` reads an image file from the filesystem and presents it to the model for visual analysis. It supports PNG, JPEG, GIF, WebP, and BMP formats. The tool takes a required `image_path` parameter and an optional `question` to focus the model's description.

This tool is only available when the model supports vision. Swival checks vision support at startup and removes the tool if the model is known not to support image input. For unrecognized models, the tool is included optimistically.

`read_file` does not work on image files — `view_image` is the only way to inspect images.

## `fetch_url`

`fetch_url` downloads HTTP or HTTPS content and returns it as markdown, plain text, or raw HTML. It is designed for documentation lookup and API reference pulls. Binary content types are rejected.

The `format` parameter selects the output format: `"markdown"` (default), `"text"`, or `"html"`. The `timeout` parameter sets the request timeout in seconds (1–120, default 30).

Raw response bodies are capped at 5 MB, and inline output is capped at 50 KB. Larger converted outputs are saved under `.swival/` so the agent can page through them with `read_file`.

All `fetch_url` output is wrapped with an `[UNTRUSTED EXTERNAL CONTENT]` header before the model sees it. This label is also baked into spill files so it survives when the agent reads large results back via `read_file`. Failed fetches (errors) are not wrapped or counted.

SSRF protections are built in. Swival resolves every URL in the redirect chain and blocks private, loopback, link-local, and reserved addresses.

## `run_command`

`run_command` executes a command given as an array of strings and returns its output. It is available in all command modes except `--commands none`.

```sh
swival --commands ls,git,python3 "Run the tests"
swival --commands ask "Run the tests"
```

`--commands` accepts `"all"` (unrestricted, the default), `"none"` (disabled), `"ask"` (interactive approval per command bucket), or a comma-separated whitelist like `ls,git,python3`. In whitelist mode, Swival resolves each whitelisted command to an absolute path at startup and rejects commands that resolve inside the base directory, so the model cannot edit and execute workspace scripts in one loop.

In ask mode, Swival prompts before each new command bucket. Interpreter inline-code flags (`bash -c`, `python3 -c`, `node -e`, `bun -e`) are classified as separate high-risk buckets distinct from the plain interpreter name, so approving `bash` to run scripts does not also approve `bash -c` for arbitrary code. See [Safety and Sandboxing](safety-and-sandboxing.md#ask-mode) for details.

Timeout defaults to 30 seconds and is clamped to a maximum of 240 seconds. While a command runs, Swival shows a transient progress indicator on stderr that fills toward the timeout, then wipes itself before the captured output prints. It is suppressed under `--quiet`, and for subagent and background commands.

Output is sanitized as it streams from the process. Programs that draw progress bars, spinners, or any live-updating display repaint the same region over and over, so Swival emulates a small terminal and collapses those repaints down to the final frame, the way a human sees it after the command finishes. This keeps intermediate frames out of the model's context and out of your terminal. The 1 MB hard cap applies to the retained sanitized output, not the raw byte volume, so a command that repaints one screen forever is not counted as truncated. When the cap is exceeded, `[output truncated at 1MB]` is appended.

Inline command output is capped at 10 KB. Larger output is written to `.swival/cmd_output_*.txt`. Those files are cleaned up automatically after roughly ten minutes.

Setting `background=true` launches the command detached and returns immediately with its PID and a log file path instead of waiting for it to exit. Standard output and standard error are appended to that log, which the model can read back later with `read_file`. This is how the agent starts long-running servers, file watchers, or any task meant to outlive a single tool call. The timeout does not apply to a backgrounded process, and the progress indicator is suppressed for it.

## `run_shell_command`

`run_shell_command` executes a shell command string and returns its output. It supports pipes, redirects, `&&` chains, and other shell syntax. Commands run through `/bin/sh -c` on Unix or `cmd.exe /c` on Windows. It accepts the same `timeout` and `background` parameters as `run_command`.

`run_shell_command` is only available with `--commands all` or `--yolo`. It does not appear in ask mode or whitelist mode, since shell strings bypass command-level policy controls.

## `use_skill`

When skills are discovered, Swival exposes `use_skill` so the model can load full instructions on demand. The system prompt only includes a compact skill catalog at startup, and full skill instructions are injected only when the tool is called. This keeps the default prompt smaller while still allowing rich task-specific guidance.

## `run_metaskill`

When executable [Agent MetaSKILLs](metaskills.md) are discovered, Swival exposes `run_metaskill` so the model can execute dynamic skill workflows. Parameters:

- `name` (required) — The metaskill name to execute. Constrained to an enum of discovered metaskill names.
- `input` — A JSON object passed to the metaskill program as `input`.
- `max_ask_calls` — Override the nested model call budget (default 5).
- `max_command_calls` — Override the command call budget (default 10, 0 disables commands).

The tool returns a string. On success: a `[Metaskill: name completed]` header followed by a JSON result envelope. On failure: a string starting with `error:`.

MetaSKILLs run with a private transcript so nested model calls don't flood the parent conversation context. They inherit the session's model, sandbox, command policy, and reporting.

## `snapshot`

`snapshot` is a context management tool for collapsing exploration into compact summaries. When the model spends many turns reading files, grepping, and reasoning before arriving at a conclusion, `snapshot` lets it collapse all of that into a single short message so the context window stays clean for the actual work.

For the full picture of how this fits into Swival's context management architecture, see [Context Management](context-management.md).

The tool supports four actions: `save`, `restore`, `cancel`, and `status`.

`save` sets an explicit checkpoint to mark the start of a focused investigation. It takes a required `label` parameter (max 100 characters). Only one explicit checkpoint can exist at a time.

`restore` collapses all turns since the checkpoint into a single summary message. It takes a required `summary` parameter (max 4,000 characters) and an optional `force` parameter that defaults to false. If no explicit checkpoint exists, it collapses from the last implicit checkpoint instead.

`cancel` clears the explicit checkpoint without collapsing anything. `status` reports the current checkpoint state, dirty status, and history.

### Implicit Checkpoints

Calling `save` before `restore` is not required. The system automatically creates implicit checkpoints at every user message, after each successful restore, and on conversation reset. When `restore` is called without a prior `save`, it collapses everything since the last implicit checkpoint, which is typically the most recent user message.

### Dirty Scopes

Tools are classified as read-only or mutating. Read-only tools (`read_file`, `read_multiple_files`, `list_files`, `grep`, `outline`, `fetch_url`, `view_image`, `think`, `todo`, `snapshot`) are safe to collapse because they don't change anything on disk. Mutating tools (`write_file`, `edit_file`, `delete_file`, `run_command`, unknown MCP tools, and A2A tools) dirty the scope.

If the scope contains mutating tool calls, `restore` fails with a list of the dirty tools. Pass `force=true` to override when you are confident the summary captures the mutations.

### Snapshot History

Completed snapshots are preserved across context compaction. Up to 10 past summaries are retained and injected into the system prompt so knowledge survives aggressive compaction.

The collapsed message that replaces all intermediate turns looks like this:

```text
[snapshot: <label>]
<your summary>
(collapsed N turns, saved ~K tokens)
```

### Input Commands

You can also trigger snapshots manually with `/save`, `/restore`, and `/unsave`. These work like the tool actions but are initiated by the user instead of the model, and are available in interactive mode and in one-shot mode when `--oneshot-commands` is set. `/restore` auto-generates the summary by calling the LLM, so you don't need to write one yourself. See [Usage](usage.md) for details.

### Example Workflow

1. User asks to debug a performance issue.
2. Agent reads six files, greps for bottlenecks, thinks through options.
3. Agent calls `snapshot` with `action=restore` and `summary="Bottleneck is in db/queries.py:89. The get_users() query does N+1 selects. Fix: add .select_related('profile') to the queryset."`.
4. All exploration collapses to roughly 100 tokens.
5. Agent proceeds to implement the fix with a clean context.

## `spawn_subagent`

`spawn_subagent` launches an independent subagent in a background thread to work on a task in parallel. The subagent runs its own `run_agent_loop()` with isolated state (messages, thinking, todo, snapshot) but shares the parent's LLM config, MCP/A2A connections, secret encryption, and LLM filter.

Subagents inherit the parent's full system prompt (instructions, memory, AGENTS.md) but have no access to the parent's conversation history. The `task` parameter must include all necessary context.

Subagents cannot spawn their own subagents — the tool is removed from their tool list to prevent recursion. Up to 4 subagents can run concurrently.

Subagents are auto-enabled for the `google`, `geap`, `chatgpt`, and `bedrock` providers, or when the detected context window is at least 100,000 tokens. Pass `--subagents` or `--no-subagents` on the CLI (or `subagents=True`/`False` in Session or config) to override the default.

## `check_subagents`

`check_subagents` monitors and manages spawned subagents. It supports three actions:

`poll` returns the status of all subagents: running, done, failed, or cancelled. Done subagents include a preview of their result (first 500 characters). Failed subagents include the error message.

`collect` blocks until a specific subagent finishes and returns its full result. Requires `subagent_id`. The default timeout is 300 seconds.

`cancel` sends a cancellation signal to a specific subagent. Requires `subagent_id`. The subagent will stop at its next cancellation check point (start of turn or between tool calls).

This tool is only available when subagent support is enabled.

## MCP Tools

Swival can connect to external tool servers via the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP). MCP tools are discovered at startup and exposed alongside built-in tools.

MCP tool output is size-guarded: results up to 20 KB are returned inline, larger results are saved to `.swival/` for paginated reads via `read_file`, and output is hard-capped at 10 MB. All MCP output is wrapped with an `[UNTRUSTED EXTERNAL CONTENT]` header, including spill files.

See [MCP](mcp.md) for configuration and details.

## A2A Tools

Swival can connect to remote agents via the [Agent-to-Agent (A2A) protocol](https://google.github.io/A2A/). A2A tools are discovered at startup and exposed alongside built-in tools.

Unlike MCP tools, A2A tools always accept a natural-language `message` plus optional `context_id` and `task_id` for multi-turn conversations. A2A tool output is size-guarded the same way as MCP output, with continuation metadata preserved across size limits and context compaction. All A2A output is wrapped with an `[UNTRUSTED EXTERNAL CONTENT]` header, including spill files.

See [A2A](a2a.md) for configuration and details.

## Goal Tool (`complete_goal`)

When a goal is active, Swival exposes a single `complete_goal` tool that the model uses to declare the objective achieved after an evidence-based audit. The tool takes no arguments and is the only way out of the goal loop other than a blocker note or hitting `--max-turns`. See [Goals](goal.md) for the full lifecycle, slash command reference, and budget behavior.
