summaryrefslogtreecommitdiff
path: root/personal_agent/tools/filesystem.py
diff options
context:
space:
mode:
Diffstat (limited to 'personal_agent/tools/filesystem.py')
-rw-r--r--personal_agent/tools/filesystem.py112
1 files changed, 112 insertions, 0 deletions
diff --git a/personal_agent/tools/filesystem.py b/personal_agent/tools/filesystem.py
new file mode 100644
index 0000000..72ad404
--- /dev/null
+++ b/personal_agent/tools/filesystem.py
@@ -0,0 +1,112 @@
+from pathlib import Path
+
+from personal_agent.tools.base import Tool, ToolError, ToolResult
+
+
+class FileSystemListTool(Tool):
+ @property
+ def name(self) -> str:
+ return "filesystem_list"
+
+ @property
+ def description(self) -> str:
+ return "List files and directories inside the agent's sandbox."
+
+ @property
+ def schema(self) -> dict[str, object]:
+ return {
+ "type": "object",
+ "properties": {
+ "path": {
+ "type": "string",
+ "description": (
+ "Relative path inside the filesystem sandbox. "
+ "Use '.' for the sandbox root."
+ ),
+ },
+ },
+ "required": ["path"],
+ }
+
+ def execute(self, arguments: dict[str, object]) -> ToolResult:
+ path = arguments.get("path")
+
+ if not isinstance(path, str):
+ raise ToolError("path must be a string")
+
+ sandbox = Path.home() / "Projects" / "personal-agent" / "data"
+ target = (sandbox / path).resolve()
+
+ if not target.is_relative_to(sandbox.resolve()):
+ raise ToolError("path is outside the filesystem sandbox")
+
+ if not target.exists():
+ raise ToolError(f"path does not exist: {path}")
+
+ if not target.is_dir():
+ raise ToolError(f"path is not a directory: {path}")
+
+ entries = []
+
+ for entry in sorted(target.iterdir()):
+ kind = "directory" if entry.is_dir() else "file"
+ entries.append(f"{kind}: {entry.name}")
+
+ if not entries:
+ return ToolResult(content="Directory is empty.")
+
+ return ToolResult(content="\n".join(entries))
+
+
+class FileSystemReadTool(Tool):
+ MAX_FILE_SIZE = 1024 * 1024 # 1 MiB
+
+ @property
+ def name(self) -> str:
+ return "filesystem_read"
+
+ @property
+ def description(self) -> str:
+ return "Read the contents of a file inside the agent's sandbox."
+
+ @property
+ def schema(self) -> dict[str, object]:
+ return {
+ "type": "object",
+ "properties": {
+ "path": {
+ "type": "string",
+ "description": ("Relative path inside the filesystem sandbox."),
+ },
+ },
+ "required": ["path"],
+ }
+
+ def execute(self, arguments: dict[str, object]) -> ToolResult:
+ path = arguments.get("path")
+
+ if not isinstance(path, str):
+ raise ToolError("path must be a string")
+
+ sandbox = Path.home() / "Projects" / "personal-agent" / "data"
+ sandbox = sandbox.resolve()
+ target = (sandbox / path).resolve()
+
+ if not target.is_relative_to(sandbox):
+ raise ToolError("path is outside the filesystem sandbox")
+
+ if not target.exists():
+ raise ToolError(f"path does not exist: {path}")
+
+ if not target.is_file():
+ raise ToolError(f"path is not a file: {path}")
+
+ if target.stat().st_size > self.MAX_FILE_SIZE:
+ raise ToolError("file is too large to read")
+
+ try:
+ content = target.read_text(encoding="utf-8")
+ except ToolError as error:
+ raise ToolError("file is not valid UTF-8 text") from error
+
+ return ToolResult(content=content)