# Plan: Reduce Tool Calls with Higher-Leverage Tools

## Goal

Add a small set of higher-level tools and targeted upgrades to existing tools so the agent can complete common repository tasks with fewer tool calls.

The main interaction pattern to optimize is:

1. discover likely files
2. inspect relevant snippets
3. read supporting files
4. make several edits
5. run verification and interpret failures

Today this often requires many separate `list_files`, `grep`, `read_file`, and `edit_file` calls. The plan below narrows the first implementation pass to the highest-leverage changes with the smallest increase in tool-surface area.

## Success Criteria

- Reduce the number of tool calls needed for common code-navigation and editing tasks.
- Preserve the existing read/write sandbox and read-before-write guard semantics.
- Keep tool outputs predictable, text-first, and compact enough for model context.
- Prefer extending familiar tools over adding near-duplicate tools.
- Ship the work in small, testable milestones.

## Design Principles

### 1. Favor extending existing tools over adding siblings

Start with features that fit the model's current mental model:

- `grep` gains context support
- `read_file` gains a batched companion: `read_files`
- `edit_file` gains a batched companion: `multi_edit`

This reduces tool-call count without forcing the model to choose between overlapping tools.

### 2. Keep outputs compact and structured

Every new tool should return plain text, but with a stable layout that is easy for an LLM to parse:

- per-file headers
- short summaries before large blocks
- explicit truncation notices
- clear continuation hints when more detail is needed

### 3. Preserve existing guardrails

New tools must continue to enforce:

- path containment checks
- symlink-safe resolution
- read-before-write protection for existing files
- output size caps
- clear `error:` strings for recoverable tool failures

### 4. Make common tasks one-call when possible

The best candidates are tasks that currently require repeated back-and-forth:

- search and inspect nearby code
- read multiple files together
- apply several related edits together

## Scope

## Phase 1 (ship this first)

1. extend `grep` with contextual output
2. add `read_files`
3. add `multi_edit`

## Phase 2 (only after Phase 1 proves insufficient)

4. evaluate whether `search_and_read` is still needed after contextual `grep`
5. add a minimal Python-only `find_symbol` tool if symbol navigation remains a pain point

Anything beyond that is intentionally out of scope for this plan.

---

## Phase 1

## Feature 1: Extend `grep` with context

### Purpose

Collapse the common sequence:

- `grep`
- inspect results
- `read_file` on the top matches

into a single search call whenever nearby context is enough.

### Decision

Do not add a separate `grep_with_context` tool. Extend the existing `grep` tool instead. Fewer tools means less model confusion and better tool selection.

### Proposed behavior

Return search matches plus surrounding context lines, grouped by file.

### Suggested parameters

Existing parameters remain:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `pattern` | string | yes | Search pattern |
| `path` | string | no | Search root, defaults to `.` |
| `include` | string | no | File glob filter |
| `case_insensitive` | boolean | no | Case-insensitive search |

Add these parameters:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `before` | integer | no | Lines before each match |
| `after` | integer | no | Lines after each match |
| `max_matches_per_file` | integer | no | Cap contextual blocks per file |
| `max_files` | integer | no | Cap number of files returned |
| `literal` | boolean | no | Treat `pattern` as a literal string instead of regex |

### Default behavior

- Default to literal matching unless regex is explicitly requested.
- Return grouped contextual blocks per file.
- Merge overlapping context windows so adjacent matches do not duplicate large chunks of text.
- Preserve current compact output when no context parameters are supplied.

### Implementation notes

- Reuse the existing filesystem walk and filtering logic from `grep`.
- Add a literal-search path that safely escapes the pattern before matching.
- Keep the output under the same byte budget used by other file/search tools.
- If the result is truncated, say exactly which limits were hit.
- Prefer deterministic ordering so repeated calls remain stable.

### Why this matters

This is the smallest change with the fastest payoff. It should remove many immediate follow-up `read_file` calls.

### Testing

Add tests for:

- basic contextual output
- multiple files with stable ordering
- literal matching vs regex matching
- include filter behavior
- overlapping-context block merging
- truncation behavior
- invalid regex handling
- binary file skipping
- symlink escape prevention

---

## Feature 2: `read_files`

### Purpose

Allow the model to fetch several files in one call instead of issuing repeated `read_file` calls.

### Proposed behavior

Read multiple files and return them as separate sections in one response.

### Suggested parameters

Accept a list of per-file read requests rather than a single shared offset/limit:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `files` | array[object] | yes | List of file read requests |

Each request object:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `file_path` | string | yes | File to read |
| `offset` | integer | no | 1-based line number to start from |
| `limit` | integer | no | Max lines to return |
| `tail` | integer | no | Tail behavior |

### Output behavior

- Return results grouped by file with the same line-numbered format as `read_file`.
- Return per-file errors inline instead of failing the entire batch.
- Enforce a total output cap across the full batch.
- Consider a hard limit on the number of requested files per call.

### Implementation notes

- Internally reuse the same safe reading logic used by `read_file`.
- Keep per-file formatting consistent so the model can copy text into `edit_file` or `multi_edit`.
- Decide whether directory reads are allowed; if not, return per-file errors inline.

### Why this matters

Once the model has identified relevant files, this removes a lot of repetitive I/O calls.

### Testing

Add tests for:

- reading several files in one call
- different offsets/limits across files
- missing file errors inline
- mixed readable/unreadable files
- total-output truncation
- path escape rejection
- read-before-write tracking interaction if applicable

---

## Feature 3: `multi_edit`

### Purpose

Apply several targeted edits in a single tool call, either within one file or across several files.

### Proposed behavior

Accept a list of edit operations. Each operation includes a file path, an `old_string`, and a `new_string`.

### Suggested parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `edits` | array[object] | yes | List of edit operations |
| `atomic` | boolean | no | Apply all edits or fail with no writes |

Each edit object:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `file_path` | string | yes | Existing file to edit |
| `old_string` | string | yes | Text to replace |
| `new_string` | string | yes | Replacement text |

### Key decisions

- Default to atomic behavior.
- Do not support `replace_all` in `multi_edit`.
- Do not allow `multi_edit` to create new files.

If the model needs multiple replacements, it should provide multiple explicit edit operations. That keeps each change auditable and limits the blast radius of a bad match.

### Implementation notes

- Reuse `edit.py` replacement logic instead of inventing a second matcher.
- Validate all edits before writing any file when `atomic=true`.
- Return a concise summary: files changed, operations applied, per-file counts.
- Preserve read-before-write enforcement for every touched existing file.
- Reject duplicate or conflicting edits in the same batch unless ordering semantics are explicitly defined.

### Why this matters

Small refactors currently take many `edit_file` calls. This should materially reduce edit churn without making each edit too opaque.

### Testing

Add tests for:

- multiple edits in one file
- edits across multiple files
- atomic rollback on one failing edit
- duplicate/conflicting edits
- read-before-write guard enforcement
- attempts to create a missing file

---

## Phase 2

## Feature 4: Evaluate `search_and_read`

### Purpose

Only add this if contextual `grep` still leaves a gap.

### Rationale

Once `grep` supports context, file caps, and literal matching, `search_and_read` may be too overlapping to justify a new tool. It should only be added if there is a clear, repeated need for:

- ranking files by match density or recency
- showing a small number of best files automatically
- bundling search plus snippet selection into one opinionated result

### Gate for implementation

Do not implement this unless Phase 1 usage shows that the model still frequently does:

1. contextual `grep`
2. `read_file` or `read_files` on the same top matches

If it is implemented later, the schema description must clearly distinguish it from `grep`.

---

## Feature 5: Minimal Python-only `find_symbol`

### Purpose

Reduce repeated text search when the model wants a Python definition body.

### Scope

Keep this narrow:

- Python files only
- definitions only
- no `find_references`
- clear fallback errors when parsing fails or the language is unsupported

### Proposed behavior

Given a symbol name, return:

- file path
- line number
- extracted body or declaration block

### Implementation notes

- Use Python `ast` for `.py` files.
- Support top-level functions, classes, and methods where practical.
- Return a clear `error:` message for unsupported languages or parse failures.
- Document in the schema that the tool is Python-only.

### Why this matters

This may help in Python-heavy repos, but it is intentionally a second-phase optimization rather than a core first-pass feature.

### Testing

Add tests for:

- top-level function/class lookup
- method lookup where supported
- malformed Python source
- unsupported-language behavior

---

## Cross-Cutting Changes

## 1. Tool schema updates in `swival/tools.py`

For each new tool or parameter expansion:

- add JSON schema entries to `TOOLS`
- keep descriptions crisp and example-driven
- call out defaults clearly, especially for literal vs regex search

## 2. Dispatch and helper reuse

Add implementations in `swival/tools.py`, but factor shared logic into helper functions where it reduces duplication:

- file resolution and containment checks
- output budget accounting
- batched file reading
- grouped snippet formatting
- atomic multi-file write staging

Avoid introducing abstraction layers that are larger than the feature itself.

## 3. System prompt updates

Update `swival/system_prompt.txt` so the model knows when to prefer the higher-leverage tools, for example:

- use contextual `grep` when locating code by text
- use `read_files` when you already know several relevant files
- use `multi_edit` for related changes across one or more files

This is important; new tools only reduce calls if the model is prompted to use them.

## 4. Documentation

Update:

- `docs.md/tools.md`
- `README.md` if tool lists are surfaced there
- any examples that currently encourage repeated low-level calls

## 5. Tests

Primary test files likely affected:

- `tests/test_tools.py`
- `tests/test_list_grep.py`
- potentially a new focused file for `multi_edit`

Add both unit-style tool tests and dispatch-level coverage.

---

## Rollout Order

### Milestone 1

Extend `grep` with `before`, `after`, `max_matches_per_file`, `max_files`, and `literal` parameters.

Reason: smallest implementation, immediate call-count reduction, minimal new surface area.

### Milestone 2

Add `read_files` with per-file range requests and inline per-file errors.

Reason: straightforward implementation, common need after search.

### Milestone 3

Add atomic `multi_edit` without `replace_all`.

Reason: high editing leverage while keeping each replacement explicit and auditable.

### Milestone 4

Reassess whether a dedicated `search_and_read` or Python-only `find_symbol` tool is still justified.

Reason: these are only worth the extra tool surface if Phase 1 still leaves obvious inefficiencies.

---

## Resolved Decisions

- Extend `grep` directly; do not add a separate `grep_with_context` tool.
- Default search to literal matching, with opt-in regex behavior.
- `read_files` should accept per-file read requests, not shared offsets/limits.
- `multi_edit` should not create new files.
- `multi_edit` should not support `replace_all`.
- Batch reads should return per-file errors inline.
- Any symbol tool should be clearly labeled Python-only at first.

---

## Recommended First Implementation Slice

If only one short development cycle is available, implement:

1. contextual `grep`
2. `read_files`
3. `multi_edit`

That trio gives the biggest reduction in exploratory and editing tool chatter while keeping the tool surface tight and predictable.
