Safely Editing Files with Earendil π File Tools: Atomic Writes and Conflict Detection
Earendil π provides atomic file editing with automatic backups, change detection, and user-confirmed diffs through the editFile tool in .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, 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– Contains the core transactional processing system and theeditFileimplementation, handling atomic writes, backup creation, and diff generation..pi/extensions/redraws.ts– Provides the hook that refreshes the TUI view after a successful edit operation completes..pi/extensions/prompt-url-widget.ts– Parsesfile://URLs from the prompt line and forwards them to the file-tool for direct invocation.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– The runtime helper that exposes the file-tool as thefile.editskill for programmatic access.
Interactive Workflow
Working with files safely follows a standardized workflow:
- Open the file – Run
pi edit src/utils/helpers.tsto load the file into the TUI editor and cache its metadata. - Edit content – Modify the file using standard editor commands (arrow keys or Vim-style shortcuts).
- Trigger save – Press Ctrl-S or type
:wto invoke theeditFileskill, which automatically generates a diff, checks for external changes, performs the atomic write, and creates the backup. - 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:
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, including path, content, and mode, and returns an object containing status and backupPath.
Summary
- Atomic transactions use temporary files (
*.tmp) andfs.renameto prevent partial writes. - Automatic backups are created at
<filename>.bakbefore 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", ...)frompackages/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 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. 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →