> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Code Editing

> AI-powered code editing with SEARCH/REPLACE diff format

# Code Editing

PraisonAI provides a powerful code editing module for AI-powered code manipulation, inspired by Kilo Code's architecture.

## Installation

The code editing module is included with PraisonAI:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
pip install praisonai
```

## Quick Start

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import (
    set_workspace,
    code_read_file,
    code_write_file,
    code_apply_diff,
    code_execute_command,
    CODE_TOOLS
)

# Set the workspace directory
set_workspace("/path/to/project")

# Read a file with line numbers
content = code_read_file("src/main.py")
print(content)

# Write to a file
code_write_file("src/new_file.py", "print('Hello, World!')")

# Execute a command
output = code_execute_command("python --version")
```

## SEARCH/REPLACE Diff Format

The code editing module uses a SEARCH/REPLACE diff format for precise code modifications:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_apply_diff

diff = """
<<<<<<< SEARCH
def old_function():
    return "old"
=======
def new_function():
    return "new"
>>>>>>> REPLACE
"""

result = code_apply_diff("src/main.py", diff)
print(result)  # "Successfully applied diff to src/main.py"
```

### Line Number Hints

For faster matching in large files, use line number hints:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
diff = """
<<<<<<< SEARCH :start_line:42
def target_function():
    pass
=======
def target_function():
    return "updated"
>>>>>>> REPLACE
"""

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

## Available Tools

### code\_read\_file

Read file contents with optional line range:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_read_file

# 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=20)
```

### code\_write\_file

Create or overwrite a file:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_write_file

code_write_file("src/new_file.py", """
def hello():
    print("Hello, World!")
""")
```

### code\_apply\_diff

Apply SEARCH/REPLACE diffs:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_apply_diff

diff = """
<<<<<<< SEARCH
old_code
=======
new_code
>>>>>>> REPLACE
"""

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

### code\_search\_replace

Apply multiple search/replace operations:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_search_replace

operations = [
    {"search": "old_name", "replace": "new_name"},
    {"search": "deprecated_func", "replace": "new_func"}
]

result = code_search_replace("src/file.py", operations)
```

### code\_list\_files

List files in a directory:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_list_files

files = code_list_files("src/", recursive=True)
for f in files:
    print(f)
```

### code\_execute\_command

Execute shell commands:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_execute_command

output = code_execute_command("python -m pytest tests/")
print(output)
```

### code\_run\_python

Execute Python source in a subprocess (not a sandbox — the child inherits the current interpreter and environment). Source is passed as an argv list, so newlines, backslashes and quotes reach the child interpreter exactly as written, identically on POSIX and Windows.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_run_python

result = code_run_python(
    code="print('hello')\nfor i in range(3): print(i)",
    cwd=".",
    timeout=30,
)
# {
#   'success': True, 'exit_code': 0,
#   'stdout': 'hello\n0\n1\n2\n',
#   'stderr': '',
#   'command': '/usr/bin/python -c <code>',
#   'cwd': '.',
# }
```

| Field       | Type | Notes                                                     |
| ----------- | ---- | --------------------------------------------------------- |
| `success`   | bool | `True` iff `exit_code == 0`                               |
| `exit_code` | int  | `-1` on timeout / launch error                            |
| `stdout`    | str  | Child stdout                                              |
| `stderr`    | str  | Child stderr                                              |
| `command`   | str  | Always `"<python> -c <code>"` — source is not echoed back |
| `cwd`       | str  | Working directory used                                    |
| `error`     | str  | Present only on timeout / launch error                    |

<Warning>
  Not a sandbox. Do not pass untrusted code from end users without isolating the host process (subprocess with `setrlimit`, container, or VM). See [`python_tools`](/docs/tools/python_tools) for the safer, already-timeout-boxed alternative for user-supplied code.
</Warning>

## Using with Agents

The code tools can be used directly with PraisonAI agents:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai import Agent
from praisonai.code import CODE_TOOLS

agent = Agent(
    name="Code Editor",
    role="Software Developer",
    goal="Edit and improve code",
    tools=CODE_TOOLS
)

result = agent.start("Refactor the main.py file to use async/await")
```

## Features

### Fuzzy Matching

The diff application uses fuzzy matching with Levenshtein distance:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import code_apply_diff

# Even with slight differences, the diff will match
diff = """
<<<<<<< SEARCH
def function():  # slight whitespace difference
    pass
=======
def function():
    return True
>>>>>>> REPLACE
"""

result = code_apply_diff("src/file.py", diff, similarity_threshold=0.8)
```

### Indentation Preservation

The module automatically preserves indentation when applying diffs:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
diff = """
<<<<<<< SEARCH
    def nested_function():
        pass
=======
    def nested_function():
        return "preserved indentation"
>>>>>>> REPLACE
"""
```

### Workspace Security

Every file-modifying tool (`code_apply_diff`, `code_search_replace`, `code_write_file`, `code_read_file`, `code_list_files`, and the low-level `append_to_file`) is confined to the workspace root. Paths that resolve outside the workspace are rejected with `"Path '<path>' is outside the workspace"`.

* **Default workspace is the current working directory.** If you never call `set_workspace(...)`, the tools confine writes and reads to `cwd`. Absolute paths outside `cwd` are rejected.
* **Explicit `set_workspace(...)` is still recommended** — it makes the confinement boundary explicit and portable across working directories.
* **Protected-path check runs first.** System paths (e.g. `/etc/hosts`, `~/.ssh/`) are rejected even if they sit inside the workspace. See [Protected Paths](/docs/features/protected-paths).
* **Gitignore Support**: Respects `.gitignore` patterns for listing.
* **Access Control**: Configurable file access rules.

<Warning>
  **Behaviour change (PraisonAI [#3087](https://github.com/MervinPraison/PraisonAI/issues/3087), commit [`d1d0272`](https://github.com/MervinPraison/PraisonAI/commit/d1d0272ee0d930ceb2619315d1636c50fb4ceef4)).** Before this fix, workspace confinement was silently skipped in `apply_diff`, `search_replace`, and `append_to_file` whenever no workspace was set. Agents could escape `cwd` via absolute paths like `apply_diff("/etc/hosts", ...)`. After the fix, `cwd` is used as the default workspace and confinement is always enforced. If you relied on writing to absolute paths outside `cwd` without calling `set_workspace()`, call `set_workspace(...)` at the intended root instead.
</Warning>

### append\_to\_file

<Note>
  `append_to_file` is available as a low-level import: `from praisonai.code.tools.write_file import append_to_file`. An agent-tool wrapper (`code_append_to_file`) is not yet exported from `praisonai.code`.
</Note>

Append content to a file (creates it if missing):

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code.tools.write_file import append_to_file

result = append_to_file(
    "logs/run.log",
    "new line\n",
    workspace="/path/to/project",   # optional — defaults to cwd
    create_if_missing=True,          # default True
    encoding="utf-8",                # default utf-8
)
```

Returns `{"success": bool, "path": str, "created": bool, "bytes_appended": int}` on success, or `{"success": False, "error": str}` on failure.

## Configuration

### Set Workspace

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.code import set_workspace, get_workspace

# Set workspace
set_workspace("/path/to/project")

# Get current workspace
workspace = get_workspace()
print(workspace)  # "/path/to/project"
```

### Environment Variables

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Set default workspace
export PRAISONAI_CODE_WORKSPACE=/path/to/project
```

## Module Structure

```
praisonai/code/
├── __init__.py          # Main exports
├── agent_tools.py       # Agent-compatible tool wrappers
├── diff/
│   └── diff_strategy.py # SEARCH/REPLACE diff with fuzzy matching
├── tools/
│   ├── read_file.py     # Read files with line ranges
│   ├── write_file.py    # Create/overwrite files
│   ├── list_files.py    # List directory contents
│   ├── apply_diff.py    # Apply SEARCH/REPLACE diffs
│   ├── search_replace.py # Multiple search/replace ops
│   └── execute_command.py # Run shell commands
└── utils/
    ├── file_utils.py    # Line numbers, file ops
    ├── text_utils.py    # Similarity, fuzzy search
    └── ignore_utils.py  # Gitignore, access control
```
