mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-23 14:14:57 +08:00
core/tools/file: read, write, listdir, edit_replace
Module-level @tool handlers under workspace root; first-occurrence string replace.
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
from .edit_replace import edit_replace as edit_replace
|
||||||
|
from .listdir import listdir as listdir
|
||||||
|
from .read import read_file as read_file
|
||||||
|
from .write import write_file as write_file
|
||||||
|
|
||||||
|
FILE_TOOLS = [read_file, write_file, listdir, edit_replace]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from plyngent.agent import tool
|
||||||
|
from plyngent.tools.workspace import resolve_path
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def edit_replace(path: str, old_string: str, new_string: str) -> str:
|
||||||
|
"""Replace the first occurrence of ``old_string`` with ``new_string`` in a file."""
|
||||||
|
if not old_string:
|
||||||
|
return "error: old_string must not be empty"
|
||||||
|
target = resolve_path(path)
|
||||||
|
if not target.is_file():
|
||||||
|
return f"error: not a file: {path}"
|
||||||
|
text = target.read_text(encoding="utf-8", errors="replace")
|
||||||
|
if old_string not in text:
|
||||||
|
return "error: old_string not found in file"
|
||||||
|
updated = text.replace(old_string, new_string, 1)
|
||||||
|
_ = target.write_text(updated, encoding="utf-8")
|
||||||
|
return f"replaced first occurrence in {path}"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from plyngent.agent import tool
|
||||||
|
from plyngent.tools.workspace import resolve_path
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def listdir(path: str = ".") -> str:
|
||||||
|
"""List entries in a directory under the workspace (name and type)."""
|
||||||
|
target = resolve_path(path)
|
||||||
|
if not target.is_dir():
|
||||||
|
return f"error: not a directory: {path}"
|
||||||
|
lines: list[str] = []
|
||||||
|
for entry in sorted(target.iterdir(), key=lambda p: p.name):
|
||||||
|
kind = "dir" if entry.is_dir() else "file"
|
||||||
|
lines.append(f"{kind}\t{entry.name}")
|
||||||
|
return "\n".join(lines) if lines else "(empty)"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from plyngent.agent import tool
|
||||||
|
from plyngent.tools.workspace import resolve_path
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def read_file(path: str, *, offset: int = 0, limit: int | None = None) -> str:
|
||||||
|
"""Read a text file under the workspace.
|
||||||
|
|
||||||
|
``offset`` is 0-based line start; ``limit`` is max lines (None = rest of file).
|
||||||
|
"""
|
||||||
|
target = resolve_path(path)
|
||||||
|
if not target.is_file():
|
||||||
|
return f"error: not a file: {path}"
|
||||||
|
text = target.read_text(encoding="utf-8", errors="replace")
|
||||||
|
lines = text.splitlines(keepends=True)
|
||||||
|
if offset < 0:
|
||||||
|
return "error: offset must be >= 0"
|
||||||
|
start = offset
|
||||||
|
end = len(lines) if limit is None else min(len(lines), start + limit)
|
||||||
|
if start >= len(lines):
|
||||||
|
return ""
|
||||||
|
return "".join(lines[start:end])
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from plyngent.agent import tool
|
||||||
|
from plyngent.tools.workspace import resolve_path
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def write_file(path: str, content: str) -> str:
|
||||||
|
"""Write text content to a file under the workspace (creates parents)."""
|
||||||
|
target = resolve_path(path)
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
_ = target.write_text(content, encoding="utf-8")
|
||||||
|
return f"wrote {len(content)} characters to {path}"
|
||||||
Reference in New Issue
Block a user