AI Code Editing

AI-powered code editing tools for agents to read, write, and modify code files with precision using SEARCH/REPLACE diffs

python
from praisonaiagents import Agent

agent = Agent(name="code-agent", instructions="Write and review Python code.")
agent.start("Write a function to check if a number is prime.")

PraisonAI Code provides AI agents with powerful tools to read, write, and modify code files with surgical precision. Inspired by Kilo Code’s architecture, it uses a SEARCH/REPLACE diff strategy with fuzzy matching for reliable code modifications.

The user asks the agent to edit project files; read, write, and SEARCH/REPLACE diffs apply changes safely.

Diagram

AI Code Editing

🤖 Agent

📖 Read File

✍️ Write File

🔄 Apply Diff

📄 File Content

💾 Updated File

🔍 SEARCH Block

✅ REPLACE Block

How It Works

Diagram
AI Code EditingAgentUserAI Code EditingAgentUserRequestProcess requestResult Agent–>>User: Response

Quick Start

  1. Set the workspace
    python
    from praisonai.code import set_workspace, code_read_file, code_apply_diff
    
    set_workspace("/path/to/your/project")

    When you launch praisonai code from the terminal, the CLI resolves the workspace from --workspace / PRAISONAI_WORKSPACE / the resumed session’s directory and calls set_workspace for you (see Workspace directory). Call set_workspace yourself only when embedding the code tools in your own Python script.

  2. Read and patch a file
    python
    content = code_read_file("src/main.py")
    print(content)
    
    diff = """<<<<<<< SEARCH
    :start_line:10
    -------
    def old_function():
        pass
    =======
    def new_function():
        return True
    >>>>>>> REPLACE"""
    
    result = code_apply_diff("src/main.py", diff)
    print(result)  # "Successfully applied 1 change(s) to src/main.py"

Available Tools

ToolDescription
code_read_fileRead file contents with optional line ranges
code_write_fileCreate or overwrite files
code_list_filesList directory contents with filtering
code_apply_diffApply SEARCH/REPLACE diffs with fuzzy matching
code_search_replaceSimple search and replace operations
code_execute_commandRun shell commands safely

Using with Agents

The CODE_TOOLS list contains all tools ready for use with PraisonAI agents:

python
from praisonaiagents import Agent
from praisonai.code import CODE_TOOLS, set_workspace

# Set workspace before creating agent
set_workspace("/path/to/project")

# Create an agent with code editing capabilities
agent = Agent(
    name="Code Editor",
    instructions="""You are a code editing assistant. 
    Use the code tools to read, modify, and write files.
    Always read a file before modifying it.""",
    tools=CODE_TOOLS
)

# The agent can now edit code
agent.start("Add error handling to the main.py file")

Tool Reference

code_read_file

Read file contents with optional line ranges and line number annotations.

python
from praisonai.code import code_read_file, set_workspace

set_workspace("/my/project")

# Read entire file
content = code_read_file("src/main.py")

# Read specific lines (1-indexed)
content = code_read_file("src/main.py", start_line=10, end_line=50)

Output format:

File: src/main.py (150 lines)

  1 | def hello():
  2 |     print("Hello, World!")
  3 |
  4 | def main():
  5 |     hello()

code_write_file

Create new files or completely replace existing files.

python
from praisonai.code import code_write_file, set_workspace

set_workspace("/my/project")

# Create a new file
result = code_write_file("src/new_module.py", """
def greet(name):
    return f"Hello, {name}!"
""")
print(result)  # "Created file: src/new_module.py (45 bytes)"

# Overwrite existing file
result = code_write_file("src/config.py", "DEBUG = True")
print(result)  # "Updated file: src/config.py (12 bytes)"

code_list_files

List files and directories with optional filtering.

python
from praisonai.code import code_list_files, set_workspace

set_workspace("/my/project")

# List current directory
files = code_list_files(".")

# List recursively with extension filter
files = code_list_files("src", recursive=True, extensions="py,js")

Output format:

Contents of src:
  📁 utils/
  📁 models/
  📄 main.py (2.5KB)
  📄 config.py (512B)
  📄 helpers.js (1.2KB)

code_apply_diff

Apply precise, surgical modifications using SEARCH/REPLACE diffs.

python
from praisonai.code import code_apply_diff, set_workspace

set_workspace("/my/project")

diff = """<<<<<<< SEARCH
:start_line:5
-------
def calculate_total(items):
    total = 0
    for item in items:
        total += item
    return total
=======
def calculate_total(items):
    \"\"\"Calculate total with 10% markup.\"\"\"
    return sum(item * 1.1 for item in items)
>>>>>>> REPLACE"""

result = code_apply_diff("src/utils.py", diff)

Diff Format

The diff format uses clear markers:

<<<<<<< SEARCH
:start_line:N
-------
[exact content to find]
=======
[new content to replace with]
>>>>>>> REPLACE
  • :start_line:N – Optional line number hint for faster matching
  • ------- – Separator after line hints
  • ======= – Divider between search and replace content
  • Multiple blocks can be included in a single diff

Multiple Changes

Apply multiple changes in one operation:

python
diff = """<<<<<<< SEARCH
:start_line:1
-------
import os
=======
import os
import sys
>>>>>>> REPLACE

<<<<<<< SEARCH
:start_line:20
-------
def old_function():
    pass
=======
def new_function():
    return True
>>>>>>> REPLACE"""

result = code_apply_diff("src/main.py", diff)
# "Successfully applied 2 change(s) to src/main.py"

code_search_replace

Simple search and replace for straightforward text substitutions.

python
from praisonai.code import code_search_replace, set_workspace

set_workspace("/my/project")

# Simple text replacement
result = code_search_replace(
    "src/main.py",
    search="old_variable_name",
    replace="new_variable_name"
)
print(result)  # "Replaced 5 occurrence(s) in src/main.py"

# Regex replacement
result = code_search_replace(
    "src/main.py",
    search=r"def (\w+)\(",
    replace=r"def renamed_\1(",
    is_regex=True
)

code_execute_command

Execute shell commands safely within the workspace.

python
from praisonai.code import code_execute_command, set_workspace

set_workspace("/my/project")

# Run tests
result = code_execute_command("python -m pytest tests/")
print(result)

# Run linter
result = code_execute_command("ruff check src/")

# Run in subdirectory
result = code_execute_command("npm test", cwd="frontend")

Fuzzy Matching

The diff strategy includes fuzzy matching using Levenshtein distance, which helps when:

  • Code has been slightly modified since you read it
  • There are minor whitespace differences
  • Line numbers have shifted due to other changes
python
from praisonai.code import apply_diff

# The tool uses a 95% similarity threshold by default
# This allows for minor variations while preventing incorrect matches

Indentation Preservation

When applying diffs, indentation is automatically preserved:

python
# Original file:
class MyClass:
    def method(self):
        pass

# Diff with different base indentation:
diff = """<<<<<<< SEARCH
:start_line:2
-------
    def method(self):
        pass
=======
    def method(self):
        return self.value
>>>>>>> REPLACE"""

# Result maintains correct indentation:
class MyClass:
    def method(self):
        return self.value

Security Features

Workspace Isolation

All file operations are restricted to the configured workspace:

python
set_workspace("/my/project")

# This works - within workspace
code_read_file("src/main.py")

# This fails - path traversal blocked
code_read_file("../../../etc/passwd")
# Error: Path '../../../etc/passwd' is outside the workspace

Gitignore Support

The code_list_files tool respects .gitignore patterns by default:

python
# Files in .gitignore are automatically excluded
files = code_list_files(".", recursive=True)
# node_modules/, __pycache__/, .git/ are excluded

Safe Command Execution

Commands are executed with safety checks:

python
from praisonai.code.tools.execute_command import is_safe_command

# Safe commands
is_safe_command("python test.py")  # True
is_safe_command("npm run build")   # True

# Potentially dangerous commands
is_safe_command("rm -rf /")        # False
is_safe_command("sudo apt install") # False

Crash-Safe Writes

code_write_file, code_search_replace, and code_apply_diff write files atomically. Each write goes to a temp file in the target’s directory, is flushed and fsync‘d, then swapped over the target with os.replace. If the agent is cancelled, times out, is SIGTERM‘d, or the disk fills up mid-write, the original file is untouched.

Diagram
graph LR
    subgraph "Crash-safe write"
        Call["📝 code_write_file /<br/>search_replace /<br/>apply_diff"] --> Tmp["📄 temp file<br/>.tmp_&lt;hex&gt;"]
        Tmp --> Fsync["💾 fsync"]
        Fsync --> Replace["♻️ os.replace<br/>(atomic)"]
        Replace --> Done["✅ Target updated"]
        Call -. interrupt .-> Keep["🛡️ Original intact"]
        Tmp -. interrupt .-> Keep
        Fsync -. interrupt .-> Keep
    end

    classDef call fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef io fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef keep fill:#F59E0B,stroke:#7C90A0,color:#fff

    class Call call
    class Tmp,Fsync io
    class Replace,Done ok
    class Keep keep
GuaranteeWhat it prevents
Atomic replaceA cancellation / SIGTERM / disk-full mid-write leaving the file zero-byte or half-written
fsync before renamePower loss leaving an empty file after apparent success
Permission bits preservedA 0o755 script turning into 0o600; a 0o600 .env becoming world-readable
Owner preserved (POSIX, best-effort)A group-/shared-owned file silently changing owner
Symlinks written through, not replacedA symlink being turned into a plain file
Read-only targets refused (PermissionError)An inode swap bypassing a target’s read-only permissions

Nothing changes for the caller when a write succeeds — same return value, same file. On failure, the temp file (.tmp_<hex> next to the target) is removed before the error propagates.

Low-Level API

For advanced use cases, you can use the low-level functions directly:

python
from praisonai.code import (
    read_file,
    write_file,
    apply_diff,
    DiffResult,
    apply_search_replace_diff,
)

# Low-level read (returns dict)
result = read_file("src/main.py", workspace="/my/project")
if result['success']:
    print(result['content'])
    print(f"Total lines: {result['total_lines']}")

# Low-level diff application
from praisonai.code.diff import apply_search_replace_diff

result = apply_search_replace_diff(
    original_content="def old(): pass",
    diff_content=diff,
    fuzzy_threshold=0.95,  # 95% similarity required
    buffer_lines=40,       # Search range around line hints
)

if result.success:
    print(result.content)
else:
    print(f"Error: {result.error}")

Helper Functions

Creating Diff Blocks Programmatically

python
from praisonai.code import create_diff_block, create_multi_diff

# Create a single diff block
diff = create_diff_block(
    search_content="def old():\n    pass",
    replace_content="def new():\n    return True",
    start_line=10
)

# Create multiple diff blocks
diff = create_multi_diff([
    {'search': 'old1', 'replace': 'new1', 'start_line': 5},
    {'search': 'old2', 'replace': 'new2', 'start_line': 20},
])

Best Practices

Always read before modifying

Read the file first to get the exact content for your SEARCH block:

python
content = code_read_file("src/main.py")
# Use the exact content from the file in your diff
Use line number hints

Include :start_line:N for faster and more accurate matching:

<<<<<<< SEARCH
:start_line:42
-------
Make atomic changes

Group related changes in a single diff, but keep unrelated changes separate for easier debugging.

Trust the tools on cancellation

code_write_file, code_search_replace, and code_apply_diff write atomically — if the agent is cancelled, times out, or is killed mid-write, the original file is untouched. You don’t need a manual backup for crash safety (you may still want one for content-level rollback).

Verify changes

After applying a diff, read the file again or run tests to verify:

python
result = code_apply_diff("src/main.py", diff)
if "Successfully" in result:
    test_result = code_execute_command("python -m pytest tests/")

Complete Example

Here’s a complete workflow showing an agent that adds error handling to a function:

python
from praisonaiagents import Agent
from praisonai.code import CODE_TOOLS, set_workspace

# Configure workspace
set_workspace("/path/to/my/project")

# Create coding agent
agent = Agent(
    name="Error Handler",
    instructions="""You are a Python expert. Your task is to:
    1. Read the specified file
    2. Identify functions without error handling
    3. Add appropriate try/except blocks
    4. Verify the changes compile correctly

    Always use code_read_file before making changes.
    Use code_apply_diff for precise modifications.
    Run tests after making changes.""",
    tools=CODE_TOOLS
)

# Run the agent
result = agent.start(
    "Add error handling to all database functions in src/database.py"
)
print(result)

Related