view-file-tool 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
view_file_tool/main.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ view_file Tool
3
+ ==============
4
+ Read the contents of a file from the local filesystem.
5
+
6
+ Parameters:
7
+ path - Absolute path to the file to read (required).
8
+ start_line - First line to return, 1-indexed inclusive (optional).
9
+ end_line - Last line to return, 1-indexed inclusive (optional).
10
+ Must be >= start_line. Max 800 lines per call.
11
+ """
12
+
13
+ import logging
14
+ import os
15
+ from typing import Any, Dict, List, Optional
16
+ import aiofiles
17
+ from openchadpy.tool_base import ToolBase
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ MAX_LINES = 800
22
+
23
+
24
+ class Tool(ToolBase):
25
+ name = "view_file"
26
+ description = (
27
+ "Read the contents of a file from the local filesystem. "
28
+ "Supports an optional line range (start_line / end_line). "
29
+ "Returns at most 800 lines per call. "
30
+ "Lines are returned 1-indexed with line numbers prepended."
31
+ )
32
+ input_schema = {
33
+ "type": "object",
34
+ "properties": {
35
+ "path": {
36
+ "type": "string",
37
+ "description": "Absolute path to the file to read.",
38
+ },
39
+ "start_line": {
40
+ "type": "integer",
41
+ "description": (
42
+ "First line to return, 1-indexed inclusive. "
43
+ "Defaults to 1 when omitted."
44
+ ),
45
+ },
46
+ "end_line": {
47
+ "type": "integer",
48
+ "description": (
49
+ "Last line to return, 1-indexed inclusive. "
50
+ "Defaults to start_line + 799 when omitted. "
51
+ "Must be >= start_line."
52
+ ),
53
+ },
54
+ },
55
+ "required": ["path"],
56
+ }
57
+ allowed_callers = ["direct", "code_execution"]
58
+
59
+ async def execute(self, **kwargs) -> Dict[str, Any]:
60
+ path: str = kwargs.get("path", "").strip()
61
+ start_line: Optional[int] = kwargs.get("start_line")
62
+ end_line: Optional[int] = kwargs.get("end_line")
63
+
64
+ if not path:
65
+ return {"error": "path is required and must not be empty."}
66
+
67
+ if not os.path.exists(path):
68
+ return {"error": f"File not found: {path!r}"}
69
+
70
+ if not os.path.isfile(path):
71
+ return {"error": f"Path is not a file: {path!r}"}
72
+
73
+ try:
74
+ async with aiofiles.open(path, "r", encoding="utf-8", errors="replace") as f:
75
+ all_lines: List[str] = await f.readlines()
76
+ except Exception as e:
77
+ logger.error(f"[view_file] failed to read {path!r}: {e}")
78
+ return {"error": f"Failed to read file: {e}"}
79
+
80
+ total_lines = len(all_lines)
81
+
82
+ # Resolve defaults
83
+ sl: int = max(1, int(start_line)) if start_line is not None else 1
84
+ el: int = (
85
+ min(int(end_line), total_lines)
86
+ if end_line is not None
87
+ else min(sl + MAX_LINES - 1, total_lines)
88
+ )
89
+
90
+ # Validate range
91
+ if sl > total_lines:
92
+ return {
93
+ "error": (
94
+ f"start_line {sl} exceeds total lines in file ({total_lines})."
95
+ )
96
+ }
97
+ if el < sl:
98
+ return {"error": f"end_line ({el}) must be >= start_line ({sl})."}
99
+
100
+ # Clamp window to MAX_LINES
101
+ if el - sl + 1 > MAX_LINES:
102
+ el = sl + MAX_LINES - 1
103
+
104
+ selected = all_lines[sl - 1 : el]
105
+ numbered = "".join(f"{sl + i}: {line}" for i, line in enumerate(selected))
106
+
107
+ return {
108
+ "path": path,
109
+ "total_lines": total_lines,
110
+ "start_line": sl,
111
+ "end_line": el,
112
+ "content": numbered,
113
+ }
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: view_file_tool
3
+ Version: 0.1.0
4
+ Summary: Read the contents of a file from the local filesystem.
5
+ Requires-Python: >=3.13
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: aiofiles>=25.1.0
8
+ Requires-Dist: openchadpy>=0.1.23
9
+ Requires-Dist: types-aiofiles>=25.1.0.20260518
@@ -0,0 +1,6 @@
1
+ view_file_tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ view_file_tool/main.py,sha256=eNYV2JDWcJq5TbXKJ7SXDMjnQHOBh2xpjIOHsZoQviY,3616
3
+ view_file_tool-0.1.0.dist-info/METADATA,sha256=V7zLyYiBVZ6QwF4abO8T5k-SGr3494Tic-WBoE6RsEY,299
4
+ view_file_tool-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
5
+ view_file_tool-0.1.0.dist-info/top_level.txt,sha256=7pl6hH7CP6NUe1ENmjoOqG8RcNowhC-7XDeZ_8mLmq4,15
6
+ view_file_tool-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ view_file_tool