toolplane-python-client 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.
Files changed (81) hide show
  1. toolplane/__init__.py +106 -0
  2. toolplane/common/__init__.py +93 -0
  3. toolplane/common/base_config.py +129 -0
  4. toolplane/common/base_connection_manager.py +171 -0
  5. toolplane/common/base_session_manager.py +321 -0
  6. toolplane/common/base_tool_manager.py +347 -0
  7. toolplane/common/constants.py +47 -0
  8. toolplane/common/utils.py +310 -0
  9. toolplane/core/__init__.py +67 -0
  10. toolplane/core/config.py +107 -0
  11. toolplane/core/connection.py +285 -0
  12. toolplane/core/errors.py +298 -0
  13. toolplane/core/machine.py +480 -0
  14. toolplane/core/request.py +775 -0
  15. toolplane/core/session.py +332 -0
  16. toolplane/core/session_context.py +514 -0
  17. toolplane/core/task.py +130 -0
  18. toolplane/core/tool.py +329 -0
  19. toolplane/http_core/__init__.py +37 -0
  20. toolplane/http_core/http_config.py +97 -0
  21. toolplane/http_core/http_connection.py +409 -0
  22. toolplane/http_core/http_machine.py +298 -0
  23. toolplane/http_core/http_request.py +748 -0
  24. toolplane/http_core/http_session.py +348 -0
  25. toolplane/http_core/http_session_context.py +491 -0
  26. toolplane/http_core/http_task.py +101 -0
  27. toolplane/http_core/http_tool.py +400 -0
  28. toolplane/interfaces/__init__.py +27 -0
  29. toolplane/interfaces/client_interface.py +122 -0
  30. toolplane/interfaces/connection_interface.py +193 -0
  31. toolplane/interfaces/event_interface.py +290 -0
  32. toolplane/interfaces/request_interface.py +439 -0
  33. toolplane/interfaces/session_interface.py +288 -0
  34. toolplane/interfaces/tool_interface.py +441 -0
  35. toolplane/proto/__init__.py +0 -0
  36. toolplane/proto/service_pb2.py +315 -0
  37. toolplane/proto/service_pb2_grpc.py +2240 -0
  38. toolplane/provider_cli.py +268 -0
  39. toolplane/provider_registry.py +77 -0
  40. toolplane/provider_runtime.py +302 -0
  41. toolplane/toolkits/__init__.py +0 -0
  42. toolplane/toolkits/standalone_tools/__init__.py +0 -0
  43. toolplane/toolkits/standalone_tools/create_directory.py +94 -0
  44. toolplane/toolkits/standalone_tools/create_file.py +124 -0
  45. toolplane/toolkits/standalone_tools/file_search.py +229 -0
  46. toolplane/toolkits/standalone_tools/grep_search.py +372 -0
  47. toolplane/toolkits/standalone_tools/launcher.py +146 -0
  48. toolplane/toolkits/standalone_tools/list_dir.py +395 -0
  49. toolplane/toolkits/standalone_tools/read_file.py +346 -0
  50. toolplane/toolkits/standalone_tools/replace_string_in_file.py +407 -0
  51. toolplane/toolkits/standalone_tools/run_tests.py +66 -0
  52. toolplane/toolkits/standalone_tools/semantic_search.py +485 -0
  53. toolplane/toolkits/standalone_tools/standalone_toolkit.py +979 -0
  54. toolplane/toolkits/standalone_tools/test_failure_analysis.py +618 -0
  55. toolplane/toolkits/standalone_tools/test_standalone_toolkit.py +517 -0
  56. toolplane/toolkits/swe/__init__.py +35 -0
  57. toolplane/toolkits/swe/create_directory.py +15 -0
  58. toolplane/toolkits/swe/create_file.py +15 -0
  59. toolplane/toolkits/swe/descriptions.py +273 -0
  60. toolplane/toolkits/swe/execute_bash.py +93 -0
  61. toolplane/toolkits/swe/file_editor.py +775 -0
  62. toolplane/toolkits/swe/file_search.py +16 -0
  63. toolplane/toolkits/swe/finish.py +50 -0
  64. toolplane/toolkits/swe/grep_search.py +19 -0
  65. toolplane/toolkits/swe/list_dir.py +407 -0
  66. toolplane/toolkits/swe/read_file.py +18 -0
  67. toolplane/toolkits/swe/replace_string_in_file.py +17 -0
  68. toolplane/toolkits/swe/search.py +260 -0
  69. toolplane/toolkits/swe/semantic_search.py +20 -0
  70. toolplane/toolkits/swe/str_replace_editor.py +647 -0
  71. toolplane/toolkits/swe/submit.py +29 -0
  72. toolplane/toolkits/swe/swe_toolkit.py +1296 -0
  73. toolplane/toolplane_client.py +686 -0
  74. toolplane/toolplane_http_client.py +681 -0
  75. toolplane/utils/__init__.py +3 -0
  76. toolplane/utils/schema.py +146 -0
  77. toolplane_python_client-0.1.0.dist-info/METADATA +543 -0
  78. toolplane_python_client-0.1.0.dist-info/RECORD +81 -0
  79. toolplane_python_client-0.1.0.dist-info/WHEEL +5 -0
  80. toolplane_python_client-0.1.0.dist-info/entry_points.txt +2 -0
  81. toolplane_python_client-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Description: Read file contents with enhanced features and line range support.
4
+
5
+ This tool reads the contents of a file with support for line ranges,
6
+ encoding detection, and various output formats.
7
+
8
+ Parameters:
9
+ file_path (string, required): The absolute path of the file to read.
10
+ start_line (integer, required): The line number to start reading from (1-based).
11
+ end_line (integer, required): The inclusive line number to end reading at (1-based).
12
+ encoding (string, optional): The encoding to use for reading the file (default: auto-detect).
13
+ show_line_numbers (boolean, optional): Show line numbers in output (default: True).
14
+ highlight_syntax (boolean, optional): Attempt to highlight syntax if possible (default: False).
15
+ max_line_length (integer, optional): Maximum line length before truncation (default: 1000).
16
+ """
17
+
18
+ import argparse
19
+ import os
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Any, Dict, List, Optional
23
+
24
+
25
+ def read_file(
26
+ file_path: str,
27
+ start_line: int,
28
+ end_line: int,
29
+ encoding: str = None,
30
+ show_line_numbers: bool = True,
31
+ highlight_syntax: bool = False,
32
+ max_line_length: int = 1000,
33
+ ) -> Dict[str, Any]:
34
+ """
35
+ Read file contents with enhanced features and line range support.
36
+
37
+ Args:
38
+ file_path: The absolute path of the file to read
39
+ start_line: Line number to start reading from (1-based)
40
+ end_line: Line number to end reading at (1-based)
41
+ encoding: Encoding to use for reading the file
42
+ show_line_numbers: Show line numbers in output
43
+ highlight_syntax: Attempt to highlight syntax
44
+ max_line_length: Maximum line length before truncation
45
+
46
+ Returns:
47
+ Dictionary containing file content and metadata
48
+ """
49
+ try:
50
+ path = Path(file_path)
51
+
52
+ if not path.exists():
53
+ return {
54
+ "success": False,
55
+ "error": f"File does not exist: {file_path}",
56
+ "content": None,
57
+ }
58
+
59
+ if not path.is_file():
60
+ return {
61
+ "success": False,
62
+ "error": f"Path is not a file: {file_path}",
63
+ "content": None,
64
+ }
65
+
66
+ # Detect encoding if not specified
67
+ if encoding is None:
68
+ encoding = detect_encoding(path)
69
+
70
+ # Read the file
71
+ try:
72
+ with open(path, "r", encoding=encoding, errors="replace") as f:
73
+ all_lines = f.readlines()
74
+ except UnicodeDecodeError:
75
+ # Fallback to UTF-8 with error handling
76
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
77
+ all_lines = f.readlines()
78
+
79
+ total_lines = len(all_lines)
80
+
81
+ # Validate line numbers
82
+ if start_line < 1:
83
+ start_line = 1
84
+ if end_line < 1:
85
+ end_line = total_lines
86
+ if end_line == -1:
87
+ end_line = total_lines
88
+
89
+ start_line = min(start_line, total_lines)
90
+ end_line = min(end_line, total_lines)
91
+
92
+ if start_line > end_line:
93
+ return {
94
+ "success": False,
95
+ "error": f"Start line ({start_line}) is greater than end line ({end_line})",
96
+ "content": None,
97
+ }
98
+
99
+ # Extract the requested lines
100
+ selected_lines = all_lines[start_line - 1 : end_line]
101
+
102
+ # Process lines
103
+ processed_lines = []
104
+ for i, line in enumerate(selected_lines, start=start_line):
105
+ # Remove trailing newline
106
+ line = line.rstrip("\n\r")
107
+
108
+ # Truncate long lines
109
+ if len(line) > max_line_length:
110
+ line = line[:max_line_length] + "... [line truncated]"
111
+
112
+ processed_lines.append(
113
+ {
114
+ "line_number": i,
115
+ "content": line,
116
+ "original_length": len(all_lines[i - 1].rstrip("\n\r")),
117
+ }
118
+ )
119
+
120
+ # Get file metadata
121
+ stat_info = path.stat()
122
+
123
+ result = {
124
+ "success": True,
125
+ "file_path": str(path.resolve()),
126
+ "encoding": encoding,
127
+ "total_lines": total_lines,
128
+ "start_line": start_line,
129
+ "end_line": end_line,
130
+ "lines_returned": len(processed_lines),
131
+ "file_size": stat_info.st_size,
132
+ "content": processed_lines,
133
+ "show_line_numbers": show_line_numbers,
134
+ "highlight_syntax": highlight_syntax
135
+ and get_file_language(path) is not None,
136
+ "file_language": get_file_language(path),
137
+ }
138
+
139
+ return result
140
+
141
+ except PermissionError:
142
+ return {
143
+ "success": False,
144
+ "error": f"Permission denied: {file_path}",
145
+ "content": None,
146
+ }
147
+ except Exception as e:
148
+ return {"success": False, "error": f"Error reading file: {e}", "content": None}
149
+
150
+
151
+ def detect_encoding(file_path: Path) -> str:
152
+ """Detect file encoding using simple heuristics."""
153
+ try:
154
+ # Try common encodings in order
155
+ encodings = ["utf-8", "utf-16", "latin-1", "cp1252"]
156
+
157
+ for encoding in encodings:
158
+ try:
159
+ with open(file_path, "r", encoding=encoding) as f:
160
+ f.read(1024) # Try to read first 1KB
161
+ return encoding
162
+ except UnicodeDecodeError:
163
+ continue
164
+
165
+ # If all fail, return utf-8 as default
166
+ return "utf-8"
167
+
168
+ except Exception:
169
+ return "utf-8"
170
+
171
+
172
+ def get_file_language(file_path: Path) -> Optional[str]:
173
+ """Determine the programming language based on file extension."""
174
+ extension_map = {
175
+ ".py": "python",
176
+ ".js": "javascript",
177
+ ".ts": "typescript",
178
+ ".jsx": "javascript",
179
+ ".tsx": "typescript",
180
+ ".java": "java",
181
+ ".cpp": "cpp",
182
+ ".c": "c",
183
+ ".h": "c",
184
+ ".hpp": "cpp",
185
+ ".cs": "csharp",
186
+ ".php": "php",
187
+ ".rb": "ruby",
188
+ ".go": "go",
189
+ ".rs": "rust",
190
+ ".swift": "swift",
191
+ ".kt": "kotlin",
192
+ ".scala": "scala",
193
+ ".r": "r",
194
+ ".sql": "sql",
195
+ ".html": "html",
196
+ ".htm": "html",
197
+ ".css": "css",
198
+ ".scss": "scss",
199
+ ".sass": "sass",
200
+ ".less": "less",
201
+ ".xml": "xml",
202
+ ".json": "json",
203
+ ".yaml": "yaml",
204
+ ".yml": "yaml",
205
+ ".toml": "toml",
206
+ ".ini": "ini",
207
+ ".cfg": "ini",
208
+ ".conf": "ini",
209
+ ".md": "markdown",
210
+ ".rst": "rst",
211
+ ".tex": "latex",
212
+ ".sh": "bash",
213
+ ".bash": "bash",
214
+ ".zsh": "zsh",
215
+ ".fish": "fish",
216
+ ".ps1": "powershell",
217
+ ".bat": "batch",
218
+ ".dockerfile": "dockerfile",
219
+ ".makefile": "makefile",
220
+ ".cmake": "cmake",
221
+ ".gradle": "gradle",
222
+ }
223
+
224
+ return extension_map.get(file_path.suffix.lower())
225
+
226
+
227
+ def format_output(result: Dict[str, Any], output_format: str) -> str:
228
+ """Format the output according to the specified format."""
229
+ if not result["success"]:
230
+ return f"Error: {result['error']}"
231
+
232
+ if output_format == "json":
233
+ import json
234
+
235
+ return json.dumps(result, indent=2)
236
+
237
+ elif output_format == "raw":
238
+ return "\n".join(line["content"] for line in result["content"])
239
+
240
+ else: # default format
241
+ lines = []
242
+
243
+ # Header
244
+ lines.append(f"File: {result['file_path']}")
245
+ lines.append(
246
+ f"Lines: {result['start_line']}-{result['end_line']} (of {result['total_lines']})"
247
+ )
248
+ lines.append(f"Encoding: {result['encoding']}")
249
+ if result["file_language"]:
250
+ lines.append(f"Language: {result['file_language']}")
251
+ lines.append("-" * 60)
252
+
253
+ # Content
254
+ for line_info in result["content"]:
255
+ if result["show_line_numbers"]:
256
+ line_num = f"{line_info['line_number']:4d}"
257
+ content = line_info["content"]
258
+
259
+ # Show truncation indicator
260
+ if line_info["original_length"] > len(content):
261
+ truncated = " [truncated]"
262
+ else:
263
+ truncated = ""
264
+
265
+ lines.append(f"{line_num}: {content}{truncated}")
266
+ else:
267
+ lines.append(line_info["content"])
268
+
269
+ return "\n".join(lines)
270
+
271
+
272
+ def main():
273
+ parser = argparse.ArgumentParser(
274
+ description="Read file contents with enhanced features and line range support."
275
+ )
276
+ parser.add_argument(
277
+ "file_path", type=str, help="The absolute path of the file to read"
278
+ )
279
+ parser.add_argument(
280
+ "start_line", type=int, help="The line number to start reading from (1-based)"
281
+ )
282
+ parser.add_argument(
283
+ "end_line",
284
+ type=int,
285
+ help="The inclusive line number to end reading at (1-based, use -1 for end of file)",
286
+ )
287
+ parser.add_argument(
288
+ "--encoding",
289
+ type=str,
290
+ help="The encoding to use for reading the file (default: auto-detect)",
291
+ )
292
+ parser.add_argument(
293
+ "--show_line_numbers",
294
+ action="store_true",
295
+ default=True,
296
+ help="Show line numbers in output (default: True)",
297
+ )
298
+ parser.add_argument(
299
+ "--no_line_numbers",
300
+ action="store_true",
301
+ default=False,
302
+ help="Hide line numbers in output",
303
+ )
304
+ parser.add_argument(
305
+ "--highlight_syntax",
306
+ action="store_true",
307
+ default=False,
308
+ help="Attempt to highlight syntax if possible (default: False)",
309
+ )
310
+ parser.add_argument(
311
+ "--max_line_length",
312
+ type=int,
313
+ default=1000,
314
+ help="Maximum line length before truncation (default: 1000)",
315
+ )
316
+ parser.add_argument(
317
+ "--output_format",
318
+ choices=["default", "json", "raw"],
319
+ default="default",
320
+ help="Output format (default: default)",
321
+ )
322
+
323
+ args = parser.parse_args()
324
+
325
+ # Handle line numbers flag
326
+ show_line_numbers = args.show_line_numbers and not args.no_line_numbers
327
+
328
+ result = read_file(
329
+ args.file_path,
330
+ args.start_line,
331
+ args.end_line,
332
+ encoding=args.encoding,
333
+ show_line_numbers=show_line_numbers,
334
+ highlight_syntax=args.highlight_syntax,
335
+ max_line_length=args.max_line_length,
336
+ )
337
+
338
+ output = format_output(result, args.output_format)
339
+ print(output)
340
+
341
+ # Exit with error code if reading failed
342
+ sys.exit(0 if result["success"] else 1)
343
+
344
+
345
+ if __name__ == "__main__":
346
+ main()