Skip to main content
File editing tools provide secure, workspace-scoped file operations with precise, conflict-safe find-and-replace that’s safe by default. Ambiguous matches fail loudly instead of editing the wrong occurrence. Read-modify-write on the same file is automatically serialised across parallel tools and multi-agent teams, and writes abort with a clear error if the file changed on disk since it was last read — no extra parameters required. A fuzzy matching ladder makes first-try edits succeed even when old_string drifts from the file by whitespace, indentation, or line endings.
The user requests a code change; the agent reads the file, matches uniquely, verifies the hash, then writes and returns a diff.
See also: the Model Harness decides whether edit_file or apply_patch is advertised first, based on the agent’s model family.
The user describes an edit; the agent reads, matches uniquely, and writes with conflict checks.

Quick Start

1

Agent edits a file safely (no boilerplate)

2

String-name resolution — no import needed

edit_file and apply_patch resolve by name. Enable them via tools=["edit_file", "apply_patch"] in Python, tools: [edit_file, apply_patch] in YAML, or --tools edit_file,apply_patch from the CLI — no explicit import required. Or grab the curated bundle with toolsets=["coding"].
Interactive CLI users get these tools automatically. Running praison "prompt" or praisonai tui launch now includes edit_file and apply_patch in the default toolset (the new edit group in Interactive Tools). Under the default approval_mode="auto" they are context-approved so nothing blocks; set PRAISON_TOOLS_DISABLE=edit or ToolConfig(enable_edit=False) to opt out.
3

Direct SDK use (automatic protection)

4

Replace all occurrences


How It Works

Two modules export read_file with different signatures:
  • from praisonaiagents.tools.file_tools import read_fileread_file(filepath, encoding='utf-8') -> str
  • from praisonaiagents.tools.edit_tools import read_fileread_file(filepath) -> Tuple[str, str]
Either flavour records the hash into the shared registry, so reading via file_tools.read_file still arms the automatic staleness guard for a later edit_file or apply_patch on the same path.
Which primitive comes first? For Claude vs. GPT models, the order in which edit_file and apply_patch are advertised is tuned automatically by the Model Harness. Both stay available; unknown models keep the historic ordering.

Safe by Default: Concurrency & Staleness

Every read-modify-write on the same file is serialised by a per-file lock, and every write checks whether the file changed since it was last read — both run automatically across FileTools, EditTools, and apply_patch.

Per-File Locking

Automatic Staleness Guard

Which Override Do I Need?

Precedence Ladder

The precedence order is: expected_hash > force=True > automatic last-read-hash guard.

force=True Examples


How Fuzzy Matching Works

edit_file walks a deterministic ladder of matching strategies and stops at the first strategy that produces a confident match. Exact matches always win; fuzzy strategies only engage when an exact substring is not found. Existing code keeps behaving exactly as before — only previously-failing edits now succeed. Confidence guards (block_anchor only):
  • Similarity threshold: 0.7 (constant _BLOCK_ANCHOR_THRESHOLD in source)
  • Disproportionate-length guard: rejects blocks more than 2× or less than half the old_string line count
  • Tie-breaking: two equally-scored candidates are treated as ambiguous (not silently picked)
Why this matters for coding agents: LLM-generated old_string values routinely drift by whitespace, indentation, or line endings. The fuzzy ladder makes the first attempt succeed when the target is unambiguous, saving retry turns and tokens.

Multi-file Patches with apply_patch

apply_patch lets an agent Add, Update, and Delete multiple files in a single atomic call — all changes succeed together or none are committed.
1

Agent Quick Start

2

Direct SDK use

Patch Format Reference

Optional sentinels *** Begin Patch / *** End Patch are accepted and stripped. Update hunk syntax:
Each @@ hunk runs through the same fuzzy ladder as edit_file, so whitespace/indentation drift in the old block is tolerated.

Atomicity Guarantees

apply_patch Error Messages


Post-edit Diagnostics

After a successful edit, edit_file and apply_patch can run a lightweight checker on the modified file and append concise diagnostics to the tool result — so the agent sees the problem and fixes it in the same loop.

Agent Quick Start

When a Diagnostics block appears, the agent self-corrects in the next turn — no separate “run the linter” task required.

Three modes

Configure the mode

Which checker runs?

The first installed candidate for the file extension is used. Nothing installed → diagnostics are silently skipped and the tool returns the plain success string.

Output format

Guarantees

  • Backward compatibleauto mode keeps the success string unchanged when the edit is clean.
  • Never breaks an edit — a missing, slow, or crashing checker is silently skipped (logged at DEBUG).
  • Bounded — 10-second timeout per file; output capped at 2000 characters and truncated cleanly beyond that.
  • Sandbox-safeeslint runs with --no-config-lookup so workspace-controlled configs/plugins cannot execute code; tsc runs per-file without a tsconfig.json.
  • Zero overhead in off mode — all imports are deferred.

Post-edit Formatting

After a successful edit, EditTools can run a configured language-appropriate formatter on the touched file and persist the formatted result. Default is "off" — byte-for-byte identical to prior behaviour. A missing or failing formatter never turns a successful edit into a failure.

Agent Quick Start

Three modes

Invalid values silently fall back to "off".

Built-in formatters

Relative commands (e.g. ./node_modules/.bin/prettier) resolve relative to the edited file’s directory, not CWD. Formatter runs under a 10-second timeout; timeouts are swallowed. The on-disk hash baseline is only refreshed when the formatter exits 0.

Custom formatter overrides

formatters maps lowercase extensions to (tool_name, argv_template). Use {path} in argv entries; if omitted, the path is appended.

Configuration Options

File Editing Functions

Edit Parameters

Constructor Parameters (EditTools / create_edit_tools)

Confining the no-workspace fallback

Pin the fallback base directory even when no Workspace object is wired up.
When workspace= is set, root is ignored. When neither is set, the base defaults to the current working directory captured at construction — later os.chdir() calls do not move the boundary.

Error Messages

An “Ambiguous match” error can also fire when fuzzy strategies produce more than one candidate location (e.g. whitespace-normalised matches at two places). Fix by adding more surrounding context to old_string.
If a file contains mixed line endings, any CRLF present causes the file to be normalised to CRLF on save.

Common Patterns

Code Refactoring

Configuration Updates

Surviving Concurrent Edits

If you already have a trusted hash:

Atomic Multi-file Rename


Best Practices

Any read_file call (via edit_tools or file_tools) arms the automatic staleness guard for the next write or edit on that path. The guard fires without any extra parameters; just read first and the protection is in place.
force=True bypasses the automatic staleness guard on edit_file, apply_patch, and write_file — but the per-file lock still applies. It’s not a “skip all safety” flag; it only allows the write to proceed when the file changed since the last read. Use it for deliberate rollbacks or idempotent snapshots, not as a routine escape from proper read-before-write discipline.
The per-file lock is keyed on the canonical path, so two callers that resolve to the same file always serialise — regardless of which module or agent they use. Different files proceed in parallel, so unrelated edits in a multi-agent run are never unnecessarily blocked.
Use search_files to locate patterns before editing so you know scope and can craft a unique old_string.
edit_file returns a bounded unified diff (10 lines max, 200 chars per line) so you rarely need a second read.
Include surrounding context so the match is unambiguous; use replace_all=True only when every occurrence should change.
CRLF files stay CRLF, LF files stay LF, UTF-8 BOM is preserved. UTF-16 files are rejected with a clear error.
Use apply_patch when changes span multiple files and must succeed/fail together (rename, refactor, dependency bump). Use edit_file when changing one file in one place — it returns a focused diff and avoids patch syntax overhead.
The patch hunk format uses @@ to separate hunks and === to separate old from new — this is not unified diff format. Add/Delete sections must not contain @@/=== markers.
File operations respect workspace boundaries. Paths outside the workspace are rejected to prevent directory traversal.
Leave post_edit_diagnostics at its default ("auto"). When the checker reports problems, the tool result includes a Diagnostics (<tool>): block — instruct the agent to fix the reported issues in its next edit. Switch to "on" if you want positive no problems found confirmations in traces, or "off" if you’re in a sandbox with no checkers available.

Workspace

How workspace containment secures file operations — the per-file lock complements workspace boundaries

Bot Default Tools

File tools included in default bot toolsets

Toolsets

Attach the curated coding toolset to get edit_file + apply_patch + code search + shell + todos in one line