"""Write file tool."""
from enum import Enum
from pydantic import BaseModel, Field
from ..bot import BotContext
from .base import ToolResult
from .registry import folder_bot, get_services
[docs]
class WriteMode(str, Enum):
"""Write mode for file operations."""
overwrite = "overwrite"
append = "append"
[docs]
class WriteFileRequest(BaseModel, frozen=True):
"""Request for writing a file."""
path: str = Field(
description="File path relative to the root folder (e.g., 'notes/todo.md')"
)
content: str = Field(description="Content to write to the file")
mode: WriteMode = Field(
default=WriteMode.overwrite,
description="Write mode: 'overwrite' replaces file content, 'append' adds to end.",
)
[docs]
@folder_bot.tool(
name="write_file",
request_type=WriteFileRequest,
response_type=ToolResult,
)
async def write_file(
request: WriteFileRequest, _context: BotContext | None = None
) -> ToolResult:
"""Create or update a file in the folder.
The file path must satisfy the configured include rules.
Use this to help the user manage their notes, todos, and documentation.
"""
services = get_services(_context)
if services is None:
return ToolResult(content="Services not available", is_error=True)
file_path = services.validate_path(request.path)
if file_path is None:
return ToolResult(content="Invalid path: access denied", is_error=True)
rel_path = str(file_path.relative_to(services.root))
if not services.is_file_allowed(rel_path):
return ToolResult(
content=f"Cannot write to '{request.path}': does not match allowed file patterns",
is_error=True,
)
if request.mode == WriteMode.append and not services.is_append_allowed(rel_path):
return ToolResult(
content=f"Cannot append to '{request.path}': not in append_allowed patterns",
is_error=True,
)
try:
file_path.parent.mkdir(parents=True, exist_ok=True)
file_existed = file_path.exists()
content = request.content
if request.mode == WriteMode.append and file_existed:
existing = file_path.read_text(encoding="utf-8", errors="replace")
content = existing + content
file_path.write_text(content, encoding="utf-8")
if request.mode == WriteMode.append:
action = "Appended to" if file_existed else "Created"
else:
action = "Updated" if file_existed else "Created"
return ToolResult(content=f"{action} {rel_path}")
except Exception as e:
return ToolResult(content=f"Error writing file: {e}", is_error=True)