# How are code edits applied?

> Open-source coding agents — a good answer covers: Edit formats (diff, search/replace, whole file, patch); how edits are validated (lint, tests, retries); undo or git integration.

Canonical page: https://llms-technical-reviews.com/coding-agents/q/editing/

## Verdict

[Aider](/p/aider/) gives the most reviewable result, with one git commit per edit. [OpenCode](/p/opencode/) and [Codewhale](/p/codewhale/) forgive the most model mistakes, and [PI-Desktop](/p/pi-desktop/) is the hardest to apply to stale text. The app builders mostly rewrite whole files.

**Edit blocks parsed from text.** [Aider](/p/aider/) uses SEARCH/REPLACE with exact matching, because the fuzzy matcher is disabled in code. It lints after each edit and offers `/undo` for its own commits.

**Exact search/replace tools.**
- [Cline](/p/cline/): `editor` needs a unique match, and GPT/Codex models get `apply_patch` instead. Git checkpoints are stored under private refs.
- [Qwen Code](/p/qwen-code/): refuses an edit unless the file was read this session and is unchanged on disk.
- [DeepSeek-Reasonix](/p/deepseek-reasonix/): `multi_edit` applies a batch atomically. A per-turn rewind store sits outside git and does not cover bash.

**Forgiving matchers.**
- [OpenCode](/p/opencode/): nine fallback replacers, LSP diagnostics after each write, and snapshots in a separate git directory.
- [Codewhale](/p/codewhale/): exact, then indentation-tolerant, then punctuation-tolerant matching, with an `expected_hash` guard. A side git repository backs `/restore N`.

**Patch grammars and anchors.**
- [Codex](/p/codex/): its own `*** Begin Patch` grammar and no lint step. The editor found that the git-snapshot `undo` feature has been removed at this commit and is kept only as a no-op flag, so rollback is your own git.
- [Open Interpreter](/p/openinterpreter/): `apply_patch` in native mode. Emulated harnesses edit through alias tools such as a Claude-style `Edit`.
- [PI-Desktop](/p/pi-desktop/): line-range ops guarded by a 4-hex tag of the file. A stale tag fails the edit.

**Whole files (app builders).**
- [bolt.diy](/p/bolt.diy/): full files in `<boltAction>` tags. `saveFileHistory` has no callers, so there is no file history.
- [Open Lovable](/p/open-lovable/): full `<file>` blocks. Snippet merges happen only with a Morph API key, and `build-validator.ts` is never called.
- [VibeSDK](/p/vibesdk/): Think's exact `edit` on a git-backed workspace. The model chooses `commit` restore points, and rollback re-commits old content.

Pick: Aider for every change as a reviewable commit.
Pick: OpenCode or Codewhale for weaker models that slip on whitespace.
Pick: VibeSDK for an app builder with real restore points.

More projects in this category are being researched.

## Per-project answers

### anomalyco/opencode (answered)

## Code Editing

OpenCode provides **three editing tools**: `edit`, `write`, and `apply_patch`. Which tool is exposed to the model depends on the model ID — `apply_patch` is shown for GPT models while `edit` and `write` are used for others (`tool/registry.ts` lines 297-301).

**Edit tool** (`tool/edit.ts`): Takes `filePath`, `oldString`, `newString`, and optional `replaceAll`. It implements a sophisticated **multi-strategy search/replace** with 9 fallback replacers: `SimpleReplacer` (exact match), `LineTrimmedReplacer` (trimmed lines), `BlockAnchorReplacer` (first/last line anchors with Levenshtein similarity), `WhitespaceNormalizedReplacer`, `IndentationFlexibleReplacer`, `EscapeNormalizedReplacer`, `TrimmedBoundaryReplacer`, `ContextAwareReplacer`, and `MultiOccurrenceReplacer` (lines 694-704). Each replacer is tried in order until a unique match is found. The tool computes a unified diff before applying, shows the diff in permission prompts, normalizes line endings, handles BOM, and formats the file after writing. If the edited file has LSP diagnostics errors, they're reported back to the model (lines 196-201). A semaphore-based file lock prevents concurrent edits (lines 35-45).

**Write tool** (`tool/write.ts`): Takes `filePath` and `content`. Computes a diff against the existing file (or empty if new), shows it in a permission prompt, writes the file with directory creation, formats it, and reports LSP diagnostics (up to 5 files).

**Apply Patch tool** (`tool/apply_patch.ts`): Takes a full `patchText` in a custom patch format. Validates the patch, parses hunks (supports add, update, delete, move), verifies each file path against external directory checks, computes per-file diffs, asks for permission with the total diff, then applies all changes. After applying, runs LSP diagnostics across all touched files (lines 266-293).

**Validation** comes from LSP diagnostics after each write/edit operation — if LSP errors are found, the output tells the model to fix them. There is no automated test-run or lint-run after editing in the editing tools themselves (that's left to the model's discretion or a subsequent step).

**Undo/git integration**: The `Snapshot` service (`snapshot/index.ts`) tracks file patches via `structuredPatch` (diff library) keyed by a hash. Before starting LLM stream execution, a snapshot is captured (`track()`, line 102 in processor.ts). On step finish, the snapshot is compared and a `patch` part is persisted (processor.ts lines 471-483). The `Session.revert` field stores snapshot/diff info for undo operations (`session.ts` lines 209-214, `session/revert.ts`).


Citations: [packages/opencode/src/tool/edit.ts:682-730](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/tool/edit.ts#L682-L730) · [packages/opencode/src/tool/edit.ts:244-426](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/tool/edit.ts#L244-L426) · [packages/opencode/src/tool/write.ts:38-104](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/tool/write.ts#L38-L104) · [packages/opencode/src/tool/apply_patch.ts:30-310](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/tool/apply_patch.ts#L30-L310) · [packages/opencode/src/tool/registry.ts:291-301](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/tool/registry.ts#L291-L301) · [packages/opencode/src/snapshot/index.ts:1-60](https://github.com/anomalyco/opencode/blob/4ac0d9c3d169bbe81d9570013effdda3fe24d36e/packages/opencode/src/snapshot/index.ts#L1-L60)

### openai/codex (answered)

Codex CLI applies code edits through a dedicated **apply-patch** crate at `codex-rs/apply-patch/src/lib.rs:1`. The model sends edits as structured function calls containing patch chunks, which are parsed by the `parser` module into `UpdateFileChunk` objects (exported from line 27). The core logic in `file_update.rs:25` reads the target file, computes replacements against the original content using `compute_replacements()` (line 60), and produces the new file contents. The patch format supports change-context matching via `seek_sequence::seek_sequence()` (line 72) which locates context lines before applying edits, allowing robust search/replace operations. The `ApplyPatchFileUpdate` struct (exported from line 33) tracks what changed. Edits go through the approval pipeline defined in `core/src/tools/approvals.rs:67` where tool calls like `ExecCommand` and `ApplyPatchFileUpdate` require user approval based on the configured `AskForApproval` policy. Validation is handled through the **Guardian** review system: the tool orchestrator in `core/src/tools/orchestrator.rs:1` runs approval → sandbox-selection → execution with retry escalation. There is no built-in linter integration; however, edits are recorded as file diffs and tracked via `TurnDiffTracker` for display. Undo is supported through Git — Codex discovers the project's Git repository and can diff against HEAD or the remote (the `GitDiffToRemoteParams` schema suggests git-aware diffing). The approval system also caches decisions per session via `ApprovalStore` (line 42 in sandboxing.rs) so repeated identical edits don't reprompt.


Citations: [codex-rs/apply-patch/src/lib.rs:1-35](https://github.com/openai/codex/blob/ccde2fc8b7fd08216757d3d3f67362847ab8341b/codex-rs/apply-patch/src/lib.rs#L1-L35) · [codex-rs/apply-patch/src/file_update.rs:25-55](https://github.com/openai/codex/blob/ccde2fc8b7fd08216757d3d3f67362847ab8341b/codex-rs/apply-patch/src/file_update.rs#L25-L55) · [codex-rs/core/src/tools/approvals.rs:67-80](https://github.com/openai/codex/blob/ccde2fc8b7fd08216757d3d3f67362847ab8341b/codex-rs/core/src/tools/approvals.rs#L67-L80) · [codex-rs/core/src/tools/orchestrator.rs:1-15](https://github.com/openai/codex/blob/ccde2fc8b7fd08216757d3d3f67362847ab8341b/codex-rs/core/src/tools/orchestrator.rs#L1-L15) · [codex-rs/core/src/tools/sandboxing.rs:40-80](https://github.com/openai/codex/blob/ccde2fc8b7fd08216757d3d3f67362847ab8341b/codex-rs/core/src/tools/sandboxing.rs#L40-L80)

### cline/cline (answered)

Code edits are applied through **three built-in tools**: (1) `editor` — a search/replace tool that finds exact string matches in a file and replaces them (`editor.ts`), (2) `apply_patch` — a unified-diff parser that accepts standard patch format and tolerates legacy shell wrappers (`apply-patch.ts`), and (3) `run_commands` — shell execution that can invoke arbitrary editors, compilers, or scripts (`bash.ts`). The **editor tool** validates that the old string appears exactly once in the target file (unique match requirement), counts occurrences to prevent ambiguous replacements, and computes a line-based diff for display (`editor.ts:67-98`). The **apply-patch tool** parses hunk headers, line positions, and context lines; it supports adding, modifying, renaming, and deleting files, with guardrails like `restrictToCwd` to prevent path-traversal (`apply-patch.ts:59-78`). **Validation** is implicit: the tool returns success/failure and any diff output; the agent sees the result and can retry. There is no automatic lint or test-run after editing. **Undo/checkpoint** is backed by git: the checkpoint hooks use `git stash push --include-untracked` with a private `refs/cline/restore-transactions/` ref, and `checkpoint-restore.ts` implements full worktree rollback with commit/rollback semantics (`checkpoint-restore.ts:50-80`). `checkpoint-diff.ts` compares checkpoints against the current workspace.

> **Editor's note.** Correction: checkpoint hooks snapshot with `git stash create` plus an untracked-files parent under `refs/cline/checkpoints/<session>/<run>`. `stash push --include-untracked` with `refs/cline/restore-transactions/` is the restore rollback. `restrictToCwd` only restricts relative paths.

Citations: [sdk/packages/core/src/extensions/tools/executors/editor.ts:1-100](https://github.com/cline/cline/blob/cd80a20e96481f5f5d413789f6847accf846487b/sdk/packages/core/src/extensions/tools/executors/editor.ts#L1-L100) · [sdk/packages/core/src/extensions/tools/executors/apply-patch.ts:1-100](https://github.com/cline/cline/blob/cd80a20e96481f5f5d413789f6847accf846487b/sdk/packages/core/src/extensions/tools/executors/apply-patch.ts#L1-L100) · [sdk/packages/core/src/extensions/tools/executors/apply-patch-parser.ts:1-30](https://github.com/cline/cline/blob/cd80a20e96481f5f5d413789f6847accf846487b/sdk/packages/core/src/extensions/tools/executors/apply-patch-parser.ts#L1-L30) · [sdk/packages/core/src/session/checkpoint-restore.ts:42-80](https://github.com/cline/cline/blob/cd80a20e96481f5f5d413789f6847accf846487b/sdk/packages/core/src/session/checkpoint-restore.ts#L42-L80) · [sdk/packages/core/src/hooks/checkpoint-hooks.ts:1-60](https://github.com/cline/cline/blob/cd80a20e96481f5f5d413789f6847accf846487b/sdk/packages/core/src/hooks/checkpoint-hooks.ts#L1-L60)

### openinterpreter/openinterpreter (answered)

Code edits use the apply_patch system. The model calls apply_patch (freeform format with inline patches in shell commands). ApplyPatchHandler (core/src/tools/handlers/apply_patch.rs:79-81) routes patch commands. The core library (apply-patch/src/lib.rs) implements parse_patch() and StreamingPatchParser for unified diffs, plus maybe_parse_apply_patch() for extracting patches from shell commands using tree-sitter Bash grammar (apply-patch/src/invocation.rs:1-100). ApplyPatchFileChange supports Add, Delete, and Update (unified-diff hunks with optional move_path). These map to protocol FileChange::Add/Delete/Update (core/src/apply_patch.rs:67-84). Patch streaming sends PatchApplyUpdatedEvent deltas as hunks parse (handler lines 121-158). Validation: assess_patch_safety() checks permission profiles and sandbox routes. The ToolOrchestrator (core/src/tools/orchestrator.rs:1-60) sequences approval to sandbox selection to attempt to retry. Edits run inside the environment filesystem with FileSystemSandboxContext enforcement. There is no lint/test validation step or git integration; safety relies on sandboxing and approvals.

> **Editor's note.** Correction: apply_patch is the native harness's edit path only; emulated harnesses (claude-code, zcode, deepseek-tui, …) use alias tools such as a Claude-style `Edit` that does exact-string replacement in-process (core/src/tools/handlers/harness_aliases.rs `handle_edit`).

Citations: [codex-rs/core/src/tools/handlers/apply_patch.rs:79-158](https://github.com/openinterpreter/openinterpreter/blob/2767e5f20d6927500b8f1938c773c61afb823245/codex-rs/core/src/tools/handlers/apply_patch.rs#L79-L158) · [codex-rs/apply-patch/src/lib.rs:48-60](https://github.com/openinterpreter/openinterpreter/blob/2767e5f20d6927500b8f1938c773c61afb823245/codex-rs/apply-patch/src/lib.rs#L48-L60) · [codex-rs/apply-patch/src/invocation.rs:1-100](https://github.com/openinterpreter/openinterpreter/blob/2767e5f20d6927500b8f1938c773c61afb823245/codex-rs/apply-patch/src/invocation.rs#L1-L100) · [codex-rs/core/src/tools/orchestrator.rs:1-60](https://github.com/openinterpreter/openinterpreter/blob/2767e5f20d6927500b8f1938c773c61afb823245/codex-rs/core/src/tools/orchestrator.rs#L1-L60)

### Aider-AI/aider (answered)

**Four core edit formats, selected per-model.** The edit format is determined by the model settings (`aider/resources/model-settings.yml`) and can be overridden via `--edit-format`. Available formats include:

- **diff (SEARCH/REPLACE blocks)** — the default for most models. Implemented by `EditBlockCoder` (`aider/coders/editblock_coder.py`). The LLM returns blocks with `<<<<<<< SEARCH` / `=======` / `>>>>>>> REPLACE` fences. `get_edits()` parses them, `apply_edits()` performs a literal string replacement via `do_replace()`. If exact match fails, it tries other chat files, then produces a `SearchReplaceNoExactMatch` error with `find_similar_lines()` suggestions (`editblock_coder.py:38-100`).

- **whole (full file replacement)** — `WholeFileCoder` (`aider/coders/wholefile_coder.py`). The LLM returns the entire file content inside backtick fences. `get_edits()` parses the path before the opening fence and the content between fences.

- **udiff (unified diff format)** — `UnifiedDiffCoder` (`aider/coders/udiff_coder.py`). The LLM returns standard unified diff hunks. Uses `search_and_replace()` and `flexible_search_and_replace()` from `aider/coders/search_replace.py`.

- **diff-fenced** — `EditBlockFencedCoder`, like diff but with language-specific fence markers.

**Function/tool-calling variants.** `WholeFileFunctionCoder` and `EditBlockFunctionCoder` (sibling modules in `aider/coders/`) define JSON tool schemas (e.g. `write_file` with path/content) and the LLM calls them as function calls via litellm's tools parameter (`models.py:1006-1009`). These function-based coders are not in `__all__` by default but can be enabled.

**Validation and retries.** After edits are applied, if `auto_lint` is enabled, `lint_edited()` (`base_coder.py:1681-1696`) runs a linter on every changed file. If lint errors are found, the user is prompted: "Attempt to fix lint errors?" If yes, the errors become a `reflected_message` and the LLM gets another turn to fix them (`base_coder.py:1599-1607`). Similarly for test failures (`base_coder.py:1616-1623`).

**Git integration.** Before each message, `dirty_commit()` (`base_coder.py:2411-2423`) auto-commits any unstaged changes to relevant files so edits can be undone. After successful edits, `auto_commit()` (`base_coder.py:2375-2395`) creates an aider commit. `/undo` (`commands.py:553`) reverts the last aider commit.


Citations: [aider/coders/editblock_coder.py:15-100](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L15-L100) · [aider/coders/wholefile_coder.py:1-80](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/wholefile_coder.py#L1-L80) · [aider/coders/base_coder.py:1599-1623](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1599-L1623) · [aider/coders/base_coder.py:2296-2336](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2296-L2336) · [aider/coders/base_coder.py:2375-2405](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2375-L2405) · [aider/models.py:1006-1015](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L1006-L1015)

### codewhale-hq/Codewhale (answered)

Codewhale provides three file-editing mechanisms with gradual complexity, plus git-based undo:

**1. `write` (`WriteFileTool`, `file.rs:1755`):** Full file replacement. Accepts `path`, `content`, and optional `expected_hash`. Creates parent directories if needed, preserves line-ending style (CRLF/LF) on overwrite, and writes atomically via `run_blocking_write_atomic`. The optional content-hash guard (`verify_expected_hash`, `file.rs:89-108`) refuses the write if the file changed since it was last read, preventing stale-state overwrites.

**2. `edit` (`EditFileTool`, `file.rs:2581`):** Search-and-replace with fuzzy matching. Accepts `path`, `search`, `replace`, and optional `expected_hash`. Matching is multi-layered: exact match first, then indentation-tolerant fuzzy match, then typographic-punctuation (smart quotes, em-dashes) normalization. Returns detailed error messages when search/replace are identical (a common model mistake). After the edit a Rust code formatter (`normalize_edit`, `rust_format.rs`) is applied, and LSP diagnostics are injected into the result via `lsp_diagnostics_for_paths`.

**3. `apply_patch` (`ApplyPatchTool`, `apply_patch.rs:1`):** Unified diff patching supporting multi-hunk patches with fuzzy matching (`MAX_FUZZ=50`, default `fuzz=3`). Hunks can be relocated when their stated line numbers are stale (requires ≥4 anchor lines for safety via `MIN_ANCHOR_LINES`). Preserves CRLF line endings and trailing-newline state from the base file.

**Cross-harness parameter compatibility:** All file tools translate known spellings from other coding harnesses (`old_string`/`new_string`, `file_path`, `filePath`, `offset`/`limit` for read windows) onto the canonical names via `apply_param_aliases` (`file.rs:228-255`). This prevents rejection loops where the model's training data names parameters differently. Unknown parameters are rejected with explicit errors rather than silently dropped.

**Validation pipeline:** All mutations go through `guard_edit` (`syntax_check.rs`) for syntax validation, `normalize_edit` for code formatting, and the content-hash guard. Auto-Review gates can flag edits for user approval based on file path patterns.

**Git/undo:** Workspace snapshots are taken into a side git repository (`turn.rs:8-16`) at pre-turn, pre-tool, and post-tool boundaries. The `/restore N` command and `revert_turn` tool consume these snapshots. The `git_tool` provides direct git operations (commit, push, branch creation).


Citations: [crates/tui/src/tools/file.rs:1755-1835](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/tools/file.rs#L1755-L1835) · [crates/tui/src/tools/file.rs:2581-2670](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/tools/file.rs#L2581-L2670) · [crates/tui/src/tools/file.rs:89-108](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/tools/file.rs#L89-L108) · [crates/tui/src/tools/apply_patch.rs:1-80](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/tools/apply_patch.rs#L1-L80) · [crates/tui/src/tools/file.rs:228-255](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/tools/file.rs#L228-L255) · [crates/tui/src/core/turn.rs:1-20](https://github.com/codewhale-hq/Codewhale/blob/ecbf2869ab9110486a9e1d6d75481d1d7adf8acf/crates/tui/src/core/turn.rs#L1-L20)

### esengine/DeepSeek-Reasonix (answered)

Edits are applied through three compile-time-builtin tools. `write_file` (`internal/tools/builtin/writefile.go:43`) replaces an entire file; it detects no-op writes (same content) and reports them, and creates parent directories. `edit_file` (`internal/tools/builtin/editfile.go:30`) does a search-and-replace: `old_string` must match exactly once in the file, `new_string` replaces it. A fuzzy-match fallback is available (whitespace-tolerant). `multi_edit` (`internal/tools/builtin/multiedit.go:43`) applies a batch of edits atomically to one file — each step runs against the result of the previous one in memory, and the file is only rewritten if every edit succeeds. Each edit tool has a `Preview` method (`internal/tools/builtin/preview.go:25,50`) that computes the diff without executing; preview_test.go asserts `Preview` matches `Execute` exactly. After every write, a bounded "post-write receipt" (`post_write_receipt.go:20`) is appended to the conversation — it shows the matched and replacement spans (capped at ~2KB total) so the model can confirm what landed. The `FileViews` mechanism (`internal/tools/builtin/fileviews.go:23`) tracks SHA-256 hashes of files the agent read or wrote; `write_file` checks that the file hasn't changed since the agent last saw it, refusing the overwrite (`ErrFileChangedSinceSeen`) to prevent clobbering simultaneous edits. There is no lint or test validation between edits and the model's reasoning — that is left to the model's own judgement and the subsequent tool round. Git integration is available via platform/gitcmd for commits and status queries, but edits themselves do not auto-commit; the bash tool can be used to run lint/test commands. All write tools pass through `confineWrite` which checks write roots, session-data guards, and managed config path protections.


Citations: [internal/tools/builtin/editfile.go:18-88](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/editfile.go#L18-L88) · [internal/tools/builtin/multiedit.go:15-80](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/multiedit.go#L15-L80) · [internal/tools/builtin/writefile.go:22-100](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/writefile.go#L22-L100) · [internal/tools/builtin/preview.go:25-60](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/preview.go#L25-L60) · [internal/tools/builtin/fileviews.go:17-60](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/fileviews.go#L17-L60) · [internal/tools/builtin/post_write_receipt.go:9-60](https://github.com/esengine/DeepSeek-Reasonix/blob/7f30fcbdb371585cd684947723097ec67ba4cf4f/internal/tools/builtin/post_write_receipt.go#L9-L60)

### firecrawl/open-lovable (answered)

Code edits are applied by parsing the LLM's text output for XML tags. The primary format is `<file path="...">full file content</file>` (`app/api/apply-ai-code-stream/route.ts:70-74`) — the LLM is instructed to output COMPLETE files, never diffs. Two additional tag types exist: `<package>name</package>` signals packages to install, and `<command>...</command>` commands to run. The parser also handles markdown code blocks (`route.ts:130-131`). A secondary edit format is the Morph Fast Apply block: `<edit target_file="..."><instructions>...</instructions><update>snippet</update></edit>` (`lib/morph-fast-apply.ts:60-76`). When `MORPH_API_KEY` is set, these blocks are sent to the morph-v3-large model via an OpenAI-compatible API to merge the update snippet into the existing file read from the sandbox (`morph-fast-apply.ts:180-217`). Validation is handled by `build-validator.ts:12-80` which fetches the sandbox URL after writing, checks for a default template page, and looks for Vite error overlays. Truncation detection (unmatched braces, unclosed XML tags) can trigger automatic recovery API calls (`generate-ai-code-stream/route.ts:1658-1810`), though `enableTruncationRecovery` defaults to false. File-writing uses the sandbox provider's `writeFile()` method with a fallback to shell `echo` or Python `open()` (`vercel-provider.ts:124-189`, `e2b-provider.ts:108-137`). There is no git integration, no undo mechanism, and no diff-based edit format.

> **Editor's note.** Correction: lib/build-validator.ts is never called, so nothing validates the sandbox after files are written; build errors reach the model only if the user reports them.

Citations: [app/api/apply-ai-code-stream/route.ts:24-262](https://github.com/firecrawl/open-lovable/blob/69bd93bae7a9c97ef989eb70aabe6797fb3dac89/app/api/apply-ai-code-stream/route.ts#L24-L262) · [lib/morph-fast-apply.ts:60-76](https://github.com/firecrawl/open-lovable/blob/69bd93bae7a9c97ef989eb70aabe6797fb3dac89/lib/morph-fast-apply.ts#L60-L76) · [lib/morph-fast-apply.ts:180-217](https://github.com/firecrawl/open-lovable/blob/69bd93bae7a9c97ef989eb70aabe6797fb3dac89/lib/morph-fast-apply.ts#L180-L217) · [lib/build-validator.ts:12-80](https://github.com/firecrawl/open-lovable/blob/69bd93bae7a9c97ef989eb70aabe6797fb3dac89/lib/build-validator.ts#L12-L80) · [lib/sandbox/providers/vercel-provider.ts:124-189](https://github.com/firecrawl/open-lovable/blob/69bd93bae7a9c97ef989eb70aabe6797fb3dac89/lib/sandbox/providers/vercel-provider.ts#L124-L189)

### QwenLM/qwen-code (answered)

Code edits are applied through two tools. **Edit** (`packages/core/src/tools/edit.ts`) uses a search-and-replace format: the model provides `file_path`, `old_string`, and `new_string`. The core function `applyReplacement()` (lines 65–85) uses `safeLiteralReplace()` to perform the text substitution, with a `replace_all` option for multiple occurrences and support for new‑file creation (empty `old_string` + isNewFile flag). **WriteFile** (`packages/core/src/tools/write-file.ts`) writes an entire file from scratch with `writeRuntimeFile()`. Both tools require the file to have been read first in the same turn — this is enforced by `checkPriorRead` from `packages/core/src/tools/priorReadEnforcement.ts`.

Before writing, `captureRuntimeFileVersion()` takes a snapshot of the current file content for diff/undo tracking. After writing, `createPatchSmart()` in `packages/core/src/tools/diffOptions.ts` computes the diff, `getDiffStat()` summarizes it, and the `CommitAttributionService` tracks session edits. There is no automatic lint, test, or retry loop after edits — the model is expected to verify its own changes by running tools like shell or code review. The edit format is always search-and-replace, never unified diff or whole-file patch. Undo is handled through git integration (session commit tracking and `CommitAttributionService`), not an explicit undo command.


Citations: [packages/core/src/tools/edit.ts:65-120](https://github.com/QwenLM/qwen-code/blob/ac81c07dc8cc13a7a6d22d3af7b9fcc3f4858607/packages/core/src/tools/edit.ts#L65-L120) · [packages/core/src/tools/edit.ts:1-45](https://github.com/QwenLM/qwen-code/blob/ac81c07dc8cc13a7a6d22d3af7b9fcc3f4858607/packages/core/src/tools/edit.ts#L1-L45) · [packages/core/src/tools/write-file.ts:1-100](https://github.com/QwenLM/qwen-code/blob/ac81c07dc8cc13a7a6d22d3af7b9fcc3f4858607/packages/core/src/tools/write-file.ts#L1-L100) · [packages/core/src/tools/diffOptions.ts:1-1](https://github.com/QwenLM/qwen-code/blob/ac81c07dc8cc13a7a6d22d3af7b9fcc3f4858607/packages/core/src/tools/diffOptions.ts#L1-L1)

### stackblitz-labs/bolt.diy (answered)

Code edits use a structured XML artifact format parsed in real-time during streaming. The LLM emits `<boltArtifact>` containing `<boltAction type="file">` tags with `filePath` attributes and full file content — Bolt does NOT use diffs or search/replace due to WebContainer limitations (the system prompt explicitly states this at prompts.ts line 36). The `StreamingMessageParser` (`app/lib/runtime/message-parser.ts`) incrementally parses the stream: when it sees `<boltArtifact>` it emits an `onArtifactOpen` callback (line 289), then for each `<boltAction type="file">` it extracts the `filePath` and accumulates content until `</boltAction>` closes. On close, it calls `onActionClose` (line 165) which triggers the `ActionRunner`. The `ActionRunner` (`app/lib/runtime/action-runner.ts`) executes each action sequentially via `#executeAction` (line 150). For file actions, `#runFileAction` (line 310) uses WebContainer's filesystem: it resolves the relative path, creates parent folders via `mkdir -p`, and writes the full file content with `webcontainer.fs.writeFile`. The `EnhancedStreamingMessageParser` (`app/lib/runtime/enhanced-message-parser.ts`) adds a fallback: if the LLM output contains no boltArtifact tags at all, it scans markdown code blocks and wraps them into artifacts automatically using file-path heuristics. There is no git integration for undo — file history is saved separately via `saveFileHistory` (action-runner.ts line 359) which stores previous versions as JSON under `.history/`. Shell commands (`type="shell"`) are executed via the terminal emulator, and `type="start"` launches dev servers. Validation includes pre-flight shell command checks (`#validateShellCommand` at line 577) that verify paths exist before rm/cd/cp/mv operations.

> **Editor's note.** Correction: saveFileHistory/getFileHistory have no callers, so no .history/ snapshots are written; there is no undo beyond what the chat UI keeps.

Citations: [app/lib/runtime/action-runner.ts:310-338](https://github.com/stackblitz-labs/bolt.diy/blob/db479e84ac7ad905bbaafc6aed097f7c0a87c849/app/lib/runtime/action-runner.ts#L310-L338) · [app/lib/runtime/message-parser.ts:76-384](https://github.com/stackblitz-labs/bolt.diy/blob/db479e84ac7ad905bbaafc6aed097f7c0a87c849/app/lib/runtime/message-parser.ts#L76-L384) · [app/lib/runtime/enhanced-message-parser.ts:35-210](https://github.com/stackblitz-labs/bolt.diy/blob/db479e84ac7ad905bbaafc6aed097f7c0a87c849/app/lib/runtime/enhanced-message-parser.ts#L35-L210) · [app/lib/common/prompts/prompts.ts:340-392](https://github.com/stackblitz-labs/bolt.diy/blob/db479e84ac7ad905bbaafc6aed097f7c0a87c849/app/lib/common/prompts/prompts.ts#L340-L392)

### vastsa/PI-Desktop (answered)

**Edits use a line-anchored, tag-verified contract** (ADR 0087). The `Edit` tool (`runtime.ts:3215-3241`) takes `path`, `tag` (4-hex fingerprint of whole file from the latest Read/Write/Edit/Grep), and `ops` — operation strings with `+`-prefixed body rows. Operations are: `PUT N.=M:` (replace lines N–M), `PUT <N:` / `PUT >N:` / `PUT >$:` (insert before/after/append), `CUT N.=M` (delete), `REM` (delete file), `MV DEST` (rename). Each successful write returns a new tag for the next edit. The legacy mode (`old_string`/`new_string`) is also still available (`runtime.ts:3230-3241`) but the line-anchored format is the primary contract.

**Validation and recovery** — Edits are validated at the host-core level (`crates/host-core/src/tools/mod.rs`). Error codes include `EDIT_TAG_MISMATCH`, `EDIT_TAG_UNKNOWN`, `EDIT_LINES_UNSEEN`, `EDIT_PARSE_FAILED`, `EDIT_RANGE_INVALID`, `EDIT_NO_CHANGE` (`packages/shared/src/errors.ts:152-163`). The `RECOVERABLE_MUTATION_ERROR_CODES` set (`runtime.ts:403-407`) defines which errors get one free retry: tag mismatch, unknown tag, and unseen lines. Each error code has specific advice sent in the tool result (`mutationTerminationAdvice()`, `runtime.ts:409-430`). After a failed Edit, the guidance says to re-read the live file and regenerate with fresh tag and narrower anchors.

**Concurrency and safety** — Host-core enforces tool budgets: max 2 in-flight mutations globally, max 1 per session, max 16 total in-flight tools (`crates/host-core/src/tool_budget.rs:7-13`). `PATH_MUTATING_TOOLS` in the runtime also uses a `PathMutex` to prevent concurrent Write/Edit on the same path (`runtime.ts:747`).

**Write tool** — `Write` is simpler: `path` + `content`, create or overwrite. No diffing, no line anchoring. Both Write and Edit go through the same host-core mutation path and the same permission framework.


Citations: [packages/agent-runtime/src/runtime.ts:3215-3241](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L3215-L3241) · [packages/agent-runtime/src/runtime.ts:403-407](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L403-L407) · [packages/agent-runtime/src/runtime.ts:409-430](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L409-L430) · [packages/shared/src/errors.ts:152-163](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/shared/src/errors.ts#L152-L163) · [packages/agent-runtime/src/runtime.ts:745-747](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/packages/agent-runtime/src/runtime.ts#L745-L747) · [docs/adr/0087-line-anchored-edit-contract.md:48-59](https://github.com/vastsa/PI-Desktop/blob/d403c96030381009c266140b7fba97edb6c1ca5d/docs/adr/0087-line-anchored-edit-contract.md#L48-L59)

### cloudflare/vibesdk (answered)

Code edits are applied through two parallel systems depending on the agent behavior:

**ThinkAgent (primary):** Edits happen via SpaceDO-backed workspace tools registered in `ThinkAgent.getTools()`. These use `@cloudflare/think/tools/workspace` tool creators (`createReadTool`, `createWriteTool`, `createEditTool`, `createDeleteTool`) backed by `SpaceWorkspaceOps` which route through Durable Object RPC to SpaceDO. The model can `write` (whole file), `edit` (search/replace on existing files), or `delete`. Edits land directly in SpaceDO's git-backed workspace. Think has no separate diff format validation — the `edit` tool uses Think's built-in search/replace with exact matching. The model is instructed to keep changes minimal and to deploy after editing.

**Agentic/Phasic behaviors:** These use a more elaborate system. The agent emits structured outputs matching Zod schemas in `worker/agents/schemas.ts` — `FileOutputSchema` for whole-file writes, with a `format` field supporting `full_content` or `unified_diff`. Two diff parsers exist in `worker/agents/output-formats/diff-formats/`: `search-replace.ts` (a `<<<<<<< SEARCH` / `=======` / `>>>>>>> REPLACE` format with multiple matching strategies: exact, whitespace-insensitive, indentation-preserving, fuzzy) and `udiff.ts` (unified diff format with indentation normalization). Both parse the model's text output into structured edits.

**Validation:** The Agentic/Phasic flow uses the `preDeploySafetyGate` utility (`worker/agents/utils/preDeploySafetyGate.ts`) which runs before deployment to check for hardcoded secrets/API keys in generated code. Static analysis is performed during build. After deploy, browser console logs are inspected. The Think flow deploys via `deploy_space` and checks browser logs via `get_browser_console_logs`, forming an automatic repair loop.

**Undo/git:** All edits go through SpaceDO, which is backed by Cloudflare Artifacts for durable git history. Each `commit` tool call creates a restore point. `rollbackToCommit()` in `ThinkCodingBehavior` restores a prior commit and redeploys without rewriting history. There is no per-edit undo — users roll back to a named commit.

> **Editor's note.** Correction: preDeploySafetyGate does not scan for secrets. It is a Babel AST check for React render-loop hazards (module-level JSX, unstable store selectors, setState during render, effects without deps) and only runs on the legacy Phasic/Agentic path.

Citations: [worker/agents/think/ThinkAgent.ts:322-349](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/think/ThinkAgent.ts#L322-L349) · [worker/agents/think/space-workspace-ops.ts:1-38](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/think/space-workspace-ops.ts#L1-L38) · [worker/agents/output-formats/diff-formats/search-replace.ts:1-61](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/output-formats/diff-formats/search-replace.ts#L1-L61) · [worker/agents/output-formats/diff-formats/udiff.ts:1-60](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/output-formats/diff-formats/udiff.ts#L1-L60) · [worker/agents/schemas.ts:20-58](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/schemas.ts#L20-L58) · [worker/agents/core/behaviors/think.ts:783-811](https://github.com/cloudflare/vibesdk/blob/9da158d82c597a0e8f4bf033cdccd1053fb6fb15/worker/agents/core/behaviors/think.ts#L783-L811)
