# Safely Editing Files with Earendil π File Tools: Atomic Writes and Conflict Detection

> Safely edit files with Earendil π tools. Enjoy atomic writes, backups, and conflict detection to prevent data corruption during AI-assisted edits.

- Repository: [Earendil Works/pi](https://github.com/earendil-works/pi)
- Tags: how-to-guide
- Published: 2026-05-25

---

**Earendil π provides atomic file editing with automatic backups, change detection, and user-confirmed diffs through the `editFile` tool in [`.pi/extensions/tps.ts`](https://github.com/earendil-works/pi/blob/main/.pi/extensions/tps.ts) to prevent data corruption during AI-assisted edits.**

Earendil π (the `pi` CLI) ships with built-in **file tools** designed for safely editing files with earendil pi file tools directly from an interactive session. According to the `earendil-works/pi` repository, these tools implement transactional guarantees that protect against crashes, external modifications, and race conditions. The implementation spans the hidden `.pi/extensions` namespace and integrates with the TUI component layer to ensure robust data integrity.

## Core Safety Mechanisms

The `editFile` tool implements five overlapping safety layers to prevent data loss.

### Atomic Writes via Temporary Files

Before modifying any source file, the tool writes new content to a temporary file using the `*.tmp` naming pattern. It then performs an atomic `fs.rename` operation to swap the temporary file with the original. This approach ensures that a system crash or interruption never leaves a partially written or corrupted file on disk.

### Automatic Backup Generation

Prior to the atomic swap, the tool creates a backup by copying the current version to `<filename>.bak`. If the user aborts the operation or if a conflict is detected, the original file can be restored instantly from this backup. The backup path is returned in the tool's response for programmatic access.

### Change Detection and Conflict Prevention

When a file is opened in the editor, the system records the file's **mtime** (modification timestamp) and **size**. Upon saving, it re-checks these values against the current file state. If either has changed, indicating an external modification, the user is prompted to choose between **overwrite**, **merge**, or **cancel**, preventing silent overwrites.

### User-Confirmed Diff Workflow

Before committing changes, the tool generates a unified diff using `diff-any` and displays it in the TUI. The user must explicitly confirm with `y` (yes) or `n` (no) before the atomic swap proceeds. This verification step ensures intentional modifications and provides visibility into exact changes.

### Concurrency Safety

The TUI's editor component, defined in [`packages/tui/src/components/editor.ts`](https://github.com/earendil-works/pi/blob/main/packages/tui/src/components/editor.ts), disables further edits while a pending file-tool operation is in flight. This prevents race conditions when multiple edit commands are issued rapidly or when the REPL attempts concurrent file modifications.

## Architecture and Implementation

The safety features are distributed across the `.pi/extensions` directory and the TUI package:

- **[`.pi/extensions/tps.ts`](https://github.com/earendil-works/pi/blob/main/.pi/extensions/tps.ts)** – Contains the core **transactional processing system** and the `editFile` implementation, handling atomic writes, backup creation, and diff generation.
- **[`.pi/extensions/redraws.ts`](https://github.com/earendil-works/pi/blob/main/.pi/extensions/redraws.ts)** – Provides the hook that refreshes the TUI view after a successful edit operation completes.
- **[`.pi/extensions/prompt-url-widget.ts`](https://github.com/earendil-works/pi/blob/main/.pi/extensions/prompt-url-widget.ts)** – Parses `file://` URLs from the prompt line and forwards them to the file-tool for direct invocation.
- **[`packages/tui/src/components/editor.ts`](https://github.com/earendil-works/pi/blob/main/packages/tui/src/components/editor.ts)** – The on-screen editor component that manages input handling, undo/redo stacks, syntax highlighting, and save triggers (**Ctrl-S** or **`:w`**).
- **[`packages/ai/src/invokeSkill.ts`](https://github.com/earendil-works/pi/blob/main/packages/ai/src/invokeSkill.ts)** – The runtime helper that exposes the file-tool as the `file.edit` skill for programmatic access.

## Interactive Workflow

Working with files safely follows a standardized workflow:

1. **Open the file** – Run `pi edit src/utils/helpers.ts` to load the file into the TUI editor and cache its metadata.
2. **Edit content** – Modify the file using standard editor commands (arrow keys or Vim-style shortcuts).
3. **Trigger save** – Press **Ctrl-S** or type `:w` to invoke the `editFile` skill, which automatically generates a diff, checks for external changes, performs the atomic write, and creates the backup.
4. **Restore if needed** – If you abort or need to revert, copy the backup file manually: `cp src/utils/helpers.ts.bak src/utils/helpers.ts`.

## Programmatic Usage

You can invoke the file tools directly from TypeScript code using the `invokeSkill` helper:

```typescript
import { invokeSkill } from "@earendil/pi";

// Prepare the new content
const newContent = `export const foo = () => "bar";`;

// Call the file-tool directly
const result = await invokeSkill("file.edit", {
  path: "src/lib/foo.ts",
  content: newContent,
  mode: "replace",   // other modes: "append", "prepend"
});

if (result.status === "ok") {
  console.log("File saved, backup at:", result.backupPath);
} else {
  console.error("Edit failed:", result.error);
}

```

The `file.edit` skill accepts parameters defined in [`tool.schema.json`](https://github.com/earendil-works/pi/blob/main/tool.schema.json), including `path`, `content`, and `mode`, and returns an object containing `status` and `backupPath`.

## Summary

- **Atomic transactions** use temporary files (`*.tmp`) and `fs.rename` to prevent partial writes.
- **Automatic backups** are created at `<filename>.bak` before any modification.
- **Conflict detection** compares stored mtime/size against current values to catch external changes.
- **Visual confirmation** requires user approval of unified diffs before applying changes.
- **Concurrency protection** disables concurrent edits in the TUI while file-tool operations are pending.
- **Programmatic access** is available via `invokeSkill("file.edit", ...)` from [`packages/ai/src/invokeSkill.ts`](https://github.com/earendil-works/pi/blob/main/packages/ai/src/invokeSkill.ts).

## Frequently Asked Questions

### What happens if a file changes while I'm editing it in the pi TUI?

The system detects external modifications by comparing the cached **mtime** and **size** against the current file state at save time. If a discrepancy is found, the tool interrupts the save process and prompts you to **overwrite** (replace with your changes), **merge** (combine versions), or **cancel** (abort the operation), preventing accidental data loss.

### Where are backup files stored when using earendil pi file tools?

Backups are stored in the same directory as the target file with the `.bak` extension appended to the original filename (e.g., [`config.ts`](https://github.com/earendil-works/pi/blob/main/config.ts) becomes `config.ts.bak`). The `editFile` tool returns the exact `backupPath` in its response, and you can manually restore the original using standard shell commands like `cp`.

### Can I use the file editing tools outside of the interactive TUI session?

Yes. The file tools are exposed as the `file.edit` **skill** in the pi runtime, accessible programmatically via `invokeSkill` from [`packages/ai/src/invokeSkill.ts`](https://github.com/earendil-works/pi/blob/main/packages/ai/src/invokeSkill.ts). This allows headless scripts and automated workflows to benefit from the same atomic writes, backups, and safety checks available in the interactive editor.

### How does pi prevent race conditions when multiple edit commands are issued?

The TUI editor component in [`packages/tui/src/components/editor.ts`](https://github.com/earendil-works/pi/blob/main/packages/tui/src/components/editor.ts) implements a concurrency lock that disables further edits while a pending file-tool operation is in flight. This prevents race conditions between multiple rapid save commands or concurrent LLM-driven tool invocations from interfering with each other's file operations.