File Edit Plugin

Safe file operations with diff preview, automatic backups, and undo capability. Shows unified diffs in permission prompts for clear visibility of changes.

Name file_edit
Type Tool Plugin (Registry-managed)
Tools Provided readFile, updateFile, writeNewFile, removeFile, moveFile, renameFile, undoFileChange
User Commands None
Auto-approved readFile, undoFileChange

Features

  • Diff Preview: Shows unified diff in permission prompts
  • Automatic Backups: Creates backups before any modification
  • Undo Support: Restore any file from its most recent backup
  • Safe Defaults: Only read and undo are auto-approved
  • Gitignore Integration: Automatically adds .jaato to .gitignore
Basic usage
from jaato import PluginRegistry

registry = PluginRegistry()
registry.discover()

# Expose file_edit plugin
registry.expose_tool('file_edit')

# Or with custom backup directory
registry.expose_tool('file_edit', config={
    'backup_dir': '/custom/backup/path'
})
Model usage example
# Read a file (auto-approved)
readFile(path="src/main.py")

# Update existing file (shows diff)
updateFile(
    path="src/main.py",
    new_content="import sys\n\ndef main():\n..."
)

# Create new file (shows content)
writeNewFile(
    path="src/utils.py",
    content="def helper():\n    return 42"
)

# Move/rename a file (shows confirmation)
moveFile(
    source_path="src/old.py",
    destination_path="src/new.py"
)

# Undo last change (auto-approved)
undoFileChange(path="src/main.py")

Configuration Parameters

backup_dir

Directory where backups are stored. Backups are organized by original file path within this directory.

Typestr
Default.jaato/backups

Gitignore Integration

On initialization, the plugin automatically adds .jaato to your .gitignore if it exists and the entry is missing. This prevents backup files from being committed.

Backup Structure
Backups are stored with timestamps:

.jaato/backups/src/main.py/2024-01-15_14-30-00.bak

This allows multiple backups per file and easy manual recovery.
Backup directory structure
.jaato/
  backups/
    src/
      main.py/
        2024-01-15_14-30-00.bak
        2024-01-15_15-45-22.bak
      utils.py/
        2024-01-15_14-35-10.bak
    config/
      settings.json/
        2024-01-14_09-00-00.bak
Custom backup location
# Use a shared backup location
registry.expose_tool('file_edit', config={
    'backup_dir': '/var/backups/jaato'
})

# Or per-project location
registry.expose_tool('file_edit', config={
    'backup_dir': '.backups'
})

Tool Reference

readFile

Read the contents of a file. This is a safe, read-only operation and is auto-approved.

ParameterTypeDescription
pathstringPath to file

updateFile

Update an existing file with new content. Shows a unified diff for approval and creates a backup before modifying.

ParameterTypeDescription
pathstringPath to existing file
new_contentstringComplete new file content

writeNewFile

Create a new file. Fails if the file already exists (use updateFile instead).

ParameterTypeDescription
pathstringPath for new file
contentstringFile content to write

removeFile

Delete a file. Creates a backup before deletion so it can be restored.

ParameterTypeDescription
pathstringPath to file to delete

moveFile

Move or rename a file. Creates destination directories if needed. Creates a backup before moving. Fails if destination exists unless overwrite=True.

ParameterTypeDescription
source_pathstringPath to source file
destination_pathstringPath to move file to
overwritebooleanIf true, overwrite existing destination (default: false)

renameFile

Alias for moveFile. Use for renaming files (same parameters and behavior).

undoFileChange

Restore a file from its most recent backup. Auto-approved since it's a recovery operation.

ParameterTypeDescription
pathstringPath to file to restore
readFile response
{
  "path": "src/main.py",
  "content": "import sys\n\ndef main():\n...",
  "size": 1234,
  "lines": 45
}
updateFile response
{
  "success": true,
  "path": "src/main.py",
  "size": 1456,
  "lines": 52,
  "backup": ".jaato/backups/src/main.py/2024-01-15_14-30-00.bak"
}
moveFile response
{
  "success": true,
  "source": "src/old.py",
  "destination": "src/new.py",
  "source_backup": ".jaato/backups/src/old.py/2024-01-15_14-30-00.bak"
}
undoFileChange response
{
  "success": true,
  "path": "src/main.py",
  "restored_from": ".jaato/backups/src/main.py/2024-01-15_14-30-00.bak",
  "message": "File restored from backup"
}

Permission Display

The file_edit plugin provides custom formatting for permission prompts, showing unified diffs for file modifications.

Update File Display

Shows a unified diff highlighting additions (green +) and deletions (red -).

New File Display

Shows all lines as additions since the file doesn't exist yet.

Delete File Display

Shows all lines as deletions, making it clear what will be removed.

Truncation

Large diffs are automatically truncated to prevent overwhelming the permission display. The summary indicates if truncation occurred.

Permission prompt example
Tool: updateFile
Summary: Update file: src/main.py (+5/-2 lines)

--- src/main.py (original)
+++ src/main.py (modified)
@@ -1,5 +1,8 @@
 import sys
+import os
+from pathlib import Path

 def main():
-    print("hello")
+    print("Hello, World!")
+    return 0

 if __name__ == "__main__":
     main()

[y]es / [n]o / [a]lways / [never]
New file prompt
Tool: writeNewFile
Summary: Create new file: src/utils.py (15 lines)

--- /dev/null
+++ src/utils.py
@@ -0,0 +1,15 @@
+"""Utility functions."""
+
+def helper():
+    """Return a helpful value."""
+    return 42
+
+def format_name(first, last):
+    """Format a full name."""
+    return f"{first} {last}"
...

[y]es / [n]o / [a]lways / [never]

Best Practices

Content Formatting

When using updateFile or writeNewFile, provide raw file content directly. Do not wrap in quotes or triple-quotes.

Use the Right Tool

  • updateFile: For existing files only
  • writeNewFile: For new files only
  • Using the wrong one returns a helpful error

Recovery Workflow

If a modification goes wrong:

  1. Use undoFileChange(path) to restore
  2. Review the original content with readFile(path)
  3. Make the correct modification
Correct content format
# CORRECT: Raw content
writeNewFile(
    path="example.py",
    content="import sys\n\ndef main():\n    pass"
)

# WRONG: Quoted content
writeNewFile(
    path="example.py",
    content='''"""
import sys

def main():
    pass
"""'''  # This writes the quotes too!
)
Error messages
// Using updateFile on non-existent file
{
  "error": "File not found: new.py. Use writeNewFile for new files."
}

// Using writeNewFile on existing file
{
  "error": "File already exists: main.py. Use updateFile to modify existing files."
}

// No backup available
{
  "error": "No backup found for: src/main.py"
}