pi-mono-coding 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.
- pi_coding/__init__.py +215 -0
- pi_coding/config.py +59 -0
- pi_coding/core/__init__.py +5 -0
- pi_coding/core/defaults.py +4 -0
- pi_coding/tools/__init__.py +70 -0
- pi_coding/tools/bash.py +213 -0
- pi_coding/tools/edit.py +176 -0
- pi_coding/tools/find.py +182 -0
- pi_coding/tools/grep.py +280 -0
- pi_coding/tools/ls.py +126 -0
- pi_coding/tools/read.py +210 -0
- pi_coding/tools/write.py +99 -0
- pi_coding/utils/__init__.py +91 -0
- pi_coding/utils/edit_diff.py +374 -0
- pi_coding/utils/git.py +201 -0
- pi_coding/utils/path_utils.py +129 -0
- pi_coding/utils/shell.py +143 -0
- pi_coding/utils/truncate.py +274 -0
- pi_mono_coding-0.1.0.dist-info/METADATA +485 -0
- pi_mono_coding-0.1.0.dist-info/RECORD +21 -0
- pi_mono_coding-0.1.0.dist-info/WHEEL +4 -0
pi_coding/tools/ls.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Ls tool - lists directory contents with type indicators and truncation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from pi_agent.types import AgentToolUpdateCallback
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pi_agent import AgentTool, AgentToolResult
|
|
12
|
+
from pi_ai.types import TextContent
|
|
13
|
+
|
|
14
|
+
from pi_coding.utils import DEFAULT_MAX_BYTES, format_size, resolve_to_cwd, truncate_head
|
|
15
|
+
|
|
16
|
+
DEFAULT_LIMIT = 500
|
|
17
|
+
|
|
18
|
+
_LS_TOOL_PARAMETERS = {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"path": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Directory to list (default: current directory)",
|
|
24
|
+
},
|
|
25
|
+
"limit": {
|
|
26
|
+
"type": "number",
|
|
27
|
+
"description": "Maximum number of entries to return (default: 500)",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
"required": [],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def _execute_ls(
|
|
35
|
+
tool_call_id: str,
|
|
36
|
+
params: dict[str, Any],
|
|
37
|
+
cwd: str,
|
|
38
|
+
cancel_event: asyncio.Event | None,
|
|
39
|
+
on_update: AgentToolUpdateCallback | None,
|
|
40
|
+
) -> AgentToolResult:
|
|
41
|
+
path = params.get("path", ".")
|
|
42
|
+
limit = params.get("limit", DEFAULT_LIMIT)
|
|
43
|
+
|
|
44
|
+
if cancel_event and cancel_event.is_set():
|
|
45
|
+
return AgentToolResult(
|
|
46
|
+
content=[TextContent(type="text", text="Operation aborted")],
|
|
47
|
+
details={"error": "aborted"},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
dir_path = resolve_to_cwd(path, cwd)
|
|
51
|
+
|
|
52
|
+
if not os.path.exists(dir_path):
|
|
53
|
+
return AgentToolResult(
|
|
54
|
+
content=[TextContent(type="text", text=f"Error: Path not found: {dir_path}")],
|
|
55
|
+
details={"error": "path_not_found", "path": dir_path},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if not os.path.isdir(dir_path):
|
|
59
|
+
return AgentToolResult(
|
|
60
|
+
content=[TextContent(type="text", text=f"Error: Not a directory: {dir_path}")],
|
|
61
|
+
details={"error": "not_a_directory", "path": dir_path},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
entries = sorted(os.listdir(dir_path), key=lambda s: s.lower())
|
|
66
|
+
except PermissionError as e:
|
|
67
|
+
return AgentToolResult(
|
|
68
|
+
content=[TextContent(type="text", text=f"Error: Cannot read directory: {e}")],
|
|
69
|
+
details={"error": "permission_denied", "path": dir_path},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
results: list[str] = []
|
|
73
|
+
entry_limit_reached = False
|
|
74
|
+
|
|
75
|
+
for entry in entries:
|
|
76
|
+
if len(results) >= limit:
|
|
77
|
+
entry_limit_reached = True
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
full_path = os.path.join(dir_path, entry)
|
|
81
|
+
suffix = "/" if os.path.isdir(full_path) else ""
|
|
82
|
+
results.append(entry + suffix)
|
|
83
|
+
|
|
84
|
+
if not results:
|
|
85
|
+
return AgentToolResult(
|
|
86
|
+
content=[TextContent(type="text", text="(empty directory)")],
|
|
87
|
+
details={"path": dir_path},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
raw_output = "\n".join(results)
|
|
91
|
+
truncation = truncate_head(raw_output)
|
|
92
|
+
|
|
93
|
+
output = truncation.content
|
|
94
|
+
details: dict[str, Any] = {}
|
|
95
|
+
notices: list[str] = []
|
|
96
|
+
|
|
97
|
+
if entry_limit_reached:
|
|
98
|
+
notices.append(f"{limit} entries limit reached. Use limit={limit * 2} for more")
|
|
99
|
+
details["entry_limit_reached"] = limit
|
|
100
|
+
|
|
101
|
+
if truncation.truncated:
|
|
102
|
+
notices.append(f"{format_size(DEFAULT_MAX_BYTES)} limit reached")
|
|
103
|
+
details["truncation"] = truncation.__dict__
|
|
104
|
+
|
|
105
|
+
if notices:
|
|
106
|
+
output += f"\n\n[{'. '.join(notices)}]"
|
|
107
|
+
|
|
108
|
+
return AgentToolResult(
|
|
109
|
+
content=[TextContent(type="text", text=output)],
|
|
110
|
+
details=details if details else None,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def create_ls_tool(cwd: str, options: dict[str, Any] | None = None) -> AgentTool:
|
|
115
|
+
return AgentTool(
|
|
116
|
+
name="ls",
|
|
117
|
+
label="List",
|
|
118
|
+
description=f"List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Includes dotfiles. Output is truncated to {DEFAULT_LIMIT} entries or {DEFAULT_MAX_BYTES // 1024}KB (whichever is hit first).",
|
|
119
|
+
parameters=_LS_TOOL_PARAMETERS,
|
|
120
|
+
execute=lambda tool_call_id, params, cancel_event, on_update: _execute_ls(
|
|
121
|
+
tool_call_id, params, cwd, cancel_event, on_update
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
ls_tool = create_ls_tool(os.getcwd())
|
pi_coding/tools/read.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Read tool - reads file contents with line numbers and truncation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import base64
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
10
|
+
|
|
11
|
+
from pi_agent import AgentTool, AgentToolResult
|
|
12
|
+
from pi_agent.types import AgentToolUpdateCallback
|
|
13
|
+
from pi_ai.types import ImageContent, TextContent
|
|
14
|
+
|
|
15
|
+
from pi_coding.utils import (
|
|
16
|
+
DEFAULT_MAX_BYTES,
|
|
17
|
+
DEFAULT_MAX_LINES,
|
|
18
|
+
TruncationResult,
|
|
19
|
+
format_size,
|
|
20
|
+
resolve_read_path,
|
|
21
|
+
truncate_head,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from pi_coding.utils import TruncationOptions
|
|
26
|
+
|
|
27
|
+
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
|
|
28
|
+
IMAGE_MIME_TYPES = {
|
|
29
|
+
".jpg": "image/jpeg",
|
|
30
|
+
".jpeg": "image/jpeg",
|
|
31
|
+
".png": "image/png",
|
|
32
|
+
".gif": "image/gif",
|
|
33
|
+
".webp": "image/webp",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _detect_image_mime_type(path: str) -> str | None:
|
|
38
|
+
ext = Path(path).suffix.lower()
|
|
39
|
+
return IMAGE_MIME_TYPES.get(ext)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_READ_TOOL_PARAMETERS = {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"path": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Path to the file to read (relative or absolute)",
|
|
48
|
+
},
|
|
49
|
+
"offset": {
|
|
50
|
+
"type": "number",
|
|
51
|
+
"description": "Line number to start reading from (1-indexed)",
|
|
52
|
+
},
|
|
53
|
+
"limit": {
|
|
54
|
+
"type": "number",
|
|
55
|
+
"description": "Maximum number of lines to read",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"required": ["path"],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def _execute_read(
|
|
63
|
+
tool_call_id: str,
|
|
64
|
+
params: dict[str, Any],
|
|
65
|
+
cwd: str,
|
|
66
|
+
cancel_event: asyncio.Event | None,
|
|
67
|
+
on_update: AgentToolUpdateCallback | None,
|
|
68
|
+
) -> AgentToolResult:
|
|
69
|
+
path = params.get("path")
|
|
70
|
+
offset = params.get("offset")
|
|
71
|
+
limit = params.get("limit")
|
|
72
|
+
|
|
73
|
+
if not path:
|
|
74
|
+
return AgentToolResult(
|
|
75
|
+
content=[TextContent(type="text", text="Error: 'path' parameter is required")],
|
|
76
|
+
details={"error": "missing_parameter"},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
absolute_path = resolve_read_path(path, cwd)
|
|
80
|
+
|
|
81
|
+
if cancel_event and cancel_event.is_set():
|
|
82
|
+
return AgentToolResult(
|
|
83
|
+
content=[TextContent(type="text", text="Operation aborted")],
|
|
84
|
+
details={"error": "aborted"},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if not os.path.exists(absolute_path):
|
|
88
|
+
return AgentToolResult(
|
|
89
|
+
content=[TextContent(type="text", text=f"Error: File not found: {path}")],
|
|
90
|
+
details={"error": "file_not_found", "path": absolute_path},
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if not os.path.isfile(absolute_path):
|
|
94
|
+
return AgentToolResult(
|
|
95
|
+
content=[TextContent(type="text", text=f"Error: Not a file: {path}")],
|
|
96
|
+
details={"error": "not_a_file", "path": absolute_path},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
mime_type = _detect_image_mime_type(absolute_path)
|
|
100
|
+
|
|
101
|
+
if mime_type:
|
|
102
|
+
try:
|
|
103
|
+
with open(absolute_path, "rb") as f:
|
|
104
|
+
data = base64.standard_b64encode(f.read()).decode("utf-8")
|
|
105
|
+
return AgentToolResult(
|
|
106
|
+
content=[
|
|
107
|
+
TextContent(type="text", text=f"Read image file [{mime_type}]"),
|
|
108
|
+
ImageContent(type="image", data=data, mimeType=mime_type),
|
|
109
|
+
],
|
|
110
|
+
details={"path": absolute_path, "mime_type": mime_type},
|
|
111
|
+
)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
return AgentToolResult(
|
|
114
|
+
content=[TextContent(type="text", text=f"Error reading image: {e}")],
|
|
115
|
+
details={"error": str(e), "path": absolute_path},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
with open(absolute_path, encoding="utf-8") as f:
|
|
120
|
+
content = f.read()
|
|
121
|
+
|
|
122
|
+
if cancel_event and cancel_event.is_set():
|
|
123
|
+
return AgentToolResult(
|
|
124
|
+
content=[TextContent(type="text", text="Operation aborted")],
|
|
125
|
+
details={"error": "aborted"},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
all_lines = content.split("\n")
|
|
129
|
+
total_file_lines = len(all_lines)
|
|
130
|
+
|
|
131
|
+
start_line = max(0, (offset or 1) - 1)
|
|
132
|
+
start_line_display = start_line + 1
|
|
133
|
+
|
|
134
|
+
if start_line >= total_file_lines:
|
|
135
|
+
return AgentToolResult(
|
|
136
|
+
content=[
|
|
137
|
+
TextContent(
|
|
138
|
+
type="text",
|
|
139
|
+
text=f"Error: Offset {offset} is beyond end of file ({total_file_lines} lines total)",
|
|
140
|
+
)
|
|
141
|
+
],
|
|
142
|
+
details={"error": "offset_out_of_bounds", "total_lines": total_file_lines},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if limit is not None:
|
|
146
|
+
end_line = min(start_line + limit, total_file_lines)
|
|
147
|
+
selected_content = "\n".join(all_lines[start_line:end_line])
|
|
148
|
+
user_limited_lines = end_line - start_line
|
|
149
|
+
else:
|
|
150
|
+
selected_content = "\n".join(all_lines[start_line:])
|
|
151
|
+
user_limited_lines = None
|
|
152
|
+
|
|
153
|
+
truncation = truncate_head(selected_content)
|
|
154
|
+
|
|
155
|
+
if truncation.first_line_exceeds_limit:
|
|
156
|
+
first_line_size = format_size(len(all_lines[start_line].encode("utf-8")))
|
|
157
|
+
output_text = f"[Line {start_line_display} is {first_line_size}, exceeds {format_size(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '{start_line_display}p' {path} | head -c {DEFAULT_MAX_BYTES}]"
|
|
158
|
+
return AgentToolResult(
|
|
159
|
+
content=[TextContent(type="text", text=output_text)],
|
|
160
|
+
details={"truncation": truncation.__dict__},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if truncation.truncated:
|
|
164
|
+
end_line_display = start_line_display + truncation.output_lines - 1
|
|
165
|
+
next_offset = end_line_display + 1
|
|
166
|
+
output_text = truncation.content
|
|
167
|
+
if truncation.truncated_by == "lines":
|
|
168
|
+
output_text += f"\n\n[Showing lines {start_line_display}-{end_line_display} of {total_file_lines}. Use offset={next_offset} to continue.]"
|
|
169
|
+
else:
|
|
170
|
+
output_text += f"\n\n[Showing lines {start_line_display}-{end_line_display} of {total_file_lines} ({format_size(DEFAULT_MAX_BYTES)} limit). Use offset={next_offset} to continue.]"
|
|
171
|
+
return AgentToolResult(
|
|
172
|
+
content=[TextContent(type="text", text=output_text)],
|
|
173
|
+
details={"truncation": truncation.__dict__},
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if user_limited_lines is not None and start_line + user_limited_lines < total_file_lines:
|
|
177
|
+
remaining = total_file_lines - (start_line + user_limited_lines)
|
|
178
|
+
next_offset = start_line + user_limited_lines + 1
|
|
179
|
+
output_text = truncation.content
|
|
180
|
+
output_text += f"\n\n[{remaining} more lines in file. Use offset={next_offset} to continue.]"
|
|
181
|
+
return AgentToolResult(
|
|
182
|
+
content=[TextContent(type="text", text=output_text)],
|
|
183
|
+
details={"total_lines": total_file_lines},
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return AgentToolResult(
|
|
187
|
+
content=[TextContent(type="text", text=truncation.content)],
|
|
188
|
+
details={"path": absolute_path, "total_lines": total_file_lines},
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
return AgentToolResult(
|
|
193
|
+
content=[TextContent(type="text", text=f"Error reading file: {e}")],
|
|
194
|
+
details={"error": str(e), "path": absolute_path},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def create_read_tool(cwd: str, options: dict[str, Any] | None = None) -> AgentTool:
|
|
199
|
+
return AgentTool(
|
|
200
|
+
name="read",
|
|
201
|
+
label="Read",
|
|
202
|
+
description=f"Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to {DEFAULT_MAX_LINES} lines or {DEFAULT_MAX_BYTES // 1024}KB (whichever is hit first). Use offset/limit for large files.",
|
|
203
|
+
parameters=_READ_TOOL_PARAMETERS,
|
|
204
|
+
execute=lambda tool_call_id, params, cancel_event, on_update: _execute_read(
|
|
205
|
+
tool_call_id, params, cwd, cancel_event, on_update
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
read_tool = create_read_tool(os.getcwd())
|
pi_coding/tools/write.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Write tool - writes content to files with automatic directory creation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from pi_agent.types import AgentToolUpdateCallback
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from pi_agent import AgentTool, AgentToolResult
|
|
12
|
+
from pi_ai.types import TextContent
|
|
13
|
+
|
|
14
|
+
from pi_coding.utils import resolve_read_path
|
|
15
|
+
|
|
16
|
+
_WRITE_TOOL_PARAMETERS = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"path": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Path to the file to write (relative or absolute)",
|
|
22
|
+
},
|
|
23
|
+
"content": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "Content to write to the file",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
"required": ["path", "content"],
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _execute_write(
|
|
33
|
+
tool_call_id: str,
|
|
34
|
+
params: dict[str, Any],
|
|
35
|
+
cwd: str,
|
|
36
|
+
cancel_event: asyncio.Event | None,
|
|
37
|
+
on_update: AgentToolUpdateCallback | None,
|
|
38
|
+
) -> AgentToolResult:
|
|
39
|
+
path = params.get("path")
|
|
40
|
+
content = params.get("content")
|
|
41
|
+
|
|
42
|
+
if not path or content is None:
|
|
43
|
+
return AgentToolResult(
|
|
44
|
+
content=[
|
|
45
|
+
TextContent(type="text", text="Error: 'path' and 'content' parameters are required")
|
|
46
|
+
],
|
|
47
|
+
details={"error": "missing_parameters"},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
absolute_path = resolve_read_path(path, cwd)
|
|
51
|
+
|
|
52
|
+
if cancel_event and cancel_event.is_set():
|
|
53
|
+
return AgentToolResult(
|
|
54
|
+
content=[TextContent(type="text", text="Operation aborted")],
|
|
55
|
+
details={"error": "aborted"},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
Path(absolute_path).parent.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
if cancel_event and cancel_event.is_set():
|
|
62
|
+
return AgentToolResult(
|
|
63
|
+
content=[TextContent(type="text", text="Operation aborted")],
|
|
64
|
+
details={"error": "aborted"},
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
with open(absolute_path, "w", encoding="utf-8") as f:
|
|
68
|
+
f.write(content)
|
|
69
|
+
|
|
70
|
+
lines = content.count("\n") + 1
|
|
71
|
+
chars = len(content)
|
|
72
|
+
|
|
73
|
+
return AgentToolResult(
|
|
74
|
+
content=[
|
|
75
|
+
TextContent(type="text", text=f"Successfully wrote {lines} lines ({chars} chars) to {path}")
|
|
76
|
+
],
|
|
77
|
+
details={"path": absolute_path, "line_count": lines, "char_count": chars},
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
return AgentToolResult(
|
|
82
|
+
content=[TextContent(type="text", text=f"Error writing file: {e}")],
|
|
83
|
+
details={"error": str(e), "path": absolute_path},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_write_tool(cwd: str, options: dict[str, Any] | None = None) -> AgentTool:
|
|
88
|
+
return AgentTool(
|
|
89
|
+
name="write",
|
|
90
|
+
label="Write",
|
|
91
|
+
description="Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
|
|
92
|
+
parameters=_WRITE_TOOL_PARAMETERS,
|
|
93
|
+
execute=lambda tool_call_id, params, cancel_event, on_update: _execute_write(
|
|
94
|
+
tool_call_id, params, cwd, cancel_event, on_update
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
write_tool = create_write_tool(os.getcwd())
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Shared utility functions for pi_coding."""
|
|
2
|
+
|
|
3
|
+
from pi_coding.utils.edit_diff import (
|
|
4
|
+
EditDiffError,
|
|
5
|
+
EditDiffResult,
|
|
6
|
+
FuzzyMatchResult,
|
|
7
|
+
compute_edit_diff,
|
|
8
|
+
detect_line_ending,
|
|
9
|
+
fuzzy_find_text,
|
|
10
|
+
generate_diff_string,
|
|
11
|
+
normalize_for_fuzzy_match,
|
|
12
|
+
normalize_to_lf,
|
|
13
|
+
restore_line_endings,
|
|
14
|
+
strip_bom,
|
|
15
|
+
)
|
|
16
|
+
from pi_coding.utils.git import (
|
|
17
|
+
GitSource,
|
|
18
|
+
get_current_branch,
|
|
19
|
+
get_repo_status,
|
|
20
|
+
parse_git_url,
|
|
21
|
+
)
|
|
22
|
+
from pi_coding.utils.path_utils import (
|
|
23
|
+
UNICODE_SPACES,
|
|
24
|
+
expand_path,
|
|
25
|
+
file_exists,
|
|
26
|
+
normalize_at_prefix,
|
|
27
|
+
normalize_unicode_spaces,
|
|
28
|
+
resolve_read_path,
|
|
29
|
+
resolve_to_cwd,
|
|
30
|
+
try_curly_quote_variant,
|
|
31
|
+
try_macos_screenshot_path,
|
|
32
|
+
try_nfd_variant,
|
|
33
|
+
)
|
|
34
|
+
from pi_coding.utils.shell import (
|
|
35
|
+
get_shell_config,
|
|
36
|
+
get_shell_env,
|
|
37
|
+
kill_process_tree,
|
|
38
|
+
sanitize_binary_output,
|
|
39
|
+
)
|
|
40
|
+
from pi_coding.utils.truncate import (
|
|
41
|
+
DEFAULT_MAX_BYTES,
|
|
42
|
+
DEFAULT_MAX_LINES,
|
|
43
|
+
GREP_MAX_LINE_LENGTH,
|
|
44
|
+
TruncationOptions,
|
|
45
|
+
TruncationResult,
|
|
46
|
+
format_size,
|
|
47
|
+
truncate_head,
|
|
48
|
+
truncate_line,
|
|
49
|
+
truncate_tail,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"EditDiffError",
|
|
54
|
+
"EditDiffResult",
|
|
55
|
+
"FuzzyMatchResult",
|
|
56
|
+
"compute_edit_diff",
|
|
57
|
+
"detect_line_ending",
|
|
58
|
+
"fuzzy_find_text",
|
|
59
|
+
"generate_diff_string",
|
|
60
|
+
"normalize_for_fuzzy_match",
|
|
61
|
+
"normalize_to_lf",
|
|
62
|
+
"restore_line_endings",
|
|
63
|
+
"strip_bom",
|
|
64
|
+
"GitSource",
|
|
65
|
+
"get_current_branch",
|
|
66
|
+
"get_repo_status",
|
|
67
|
+
"parse_git_url",
|
|
68
|
+
"UNICODE_SPACES",
|
|
69
|
+
"expand_path",
|
|
70
|
+
"file_exists",
|
|
71
|
+
"normalize_at_prefix",
|
|
72
|
+
"normalize_unicode_spaces",
|
|
73
|
+
"resolve_read_path",
|
|
74
|
+
"resolve_to_cwd",
|
|
75
|
+
"try_curly_quote_variant",
|
|
76
|
+
"try_macos_screenshot_path",
|
|
77
|
+
"try_nfd_variant",
|
|
78
|
+
"get_shell_config",
|
|
79
|
+
"get_shell_env",
|
|
80
|
+
"kill_process_tree",
|
|
81
|
+
"sanitize_binary_output",
|
|
82
|
+
"DEFAULT_MAX_BYTES",
|
|
83
|
+
"DEFAULT_MAX_LINES",
|
|
84
|
+
"GREP_MAX_LINE_LENGTH",
|
|
85
|
+
"TruncationOptions",
|
|
86
|
+
"TruncationResult",
|
|
87
|
+
"format_size",
|
|
88
|
+
"truncate_head",
|
|
89
|
+
"truncate_line",
|
|
90
|
+
"truncate_tail",
|
|
91
|
+
]
|