indent 0.1.5__tar.gz → 0.1.6__tar.gz
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.
Potentially problematic release.
This version of indent might be problematic. Click here for more details.
- {indent-0.1.5 → indent-0.1.6}/PKG-INFO +1 -1
- indent-0.1.6/exponent/__init__.py +1 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/cli_rpc_types.py +16 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/tool_execution.py +81 -0
- {indent-0.1.5 → indent-0.1.6}/pyproject.toml +1 -1
- indent-0.1.5/exponent/__init__.py +0 -1
- {indent-0.1.5 → indent-0.1.6}/.gitignore +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/cli.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/cloud_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/common.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/config_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/github_app_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/listen_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/run_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/settings.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/shell_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/theme.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/types.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/upgrade.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/utils.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/commands/workflow_commands.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/config.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/__init__.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/client.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/cloud_config_queries.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/get_chats_query.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/github_config_queries.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/mutations.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/queries.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/graphql/subscriptions.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/checkpoints.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/client.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/code_execution.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/error_info.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/exceptions.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/file_write.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/files.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/git.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/languages/python_execution.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/languages/shell_streaming.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/languages/types.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/session.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/system_context.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/truncation.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/types.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/remote_execution/utils.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/types/__init__.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/types/command_data.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/types/event_types.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/types/generated/__init__.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/core/types/generated/strategy_info.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/migration-docs/login.md +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/py.typed +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/utils/__init__.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/utils/colors.py +0 -0
- {indent-0.1.5 → indent-0.1.6}/exponent/utils/version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.6" # Keep in sync with pyproject.toml
|
|
@@ -104,6 +104,20 @@ class GrepToolResult(ToolResult, tag=GREP_TOOL_NAME):
|
|
|
104
104
|
truncated: bool = False
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
EDIT_TOOL_NAME = "edit"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class EditToolInput(ToolInput, tag=EDIT_TOOL_NAME):
|
|
111
|
+
file_path: str
|
|
112
|
+
old_string: str
|
|
113
|
+
new_string: str
|
|
114
|
+
replace_all: bool = False
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class EditToolResult(ToolResult, tag=EDIT_TOOL_NAME):
|
|
118
|
+
message: str
|
|
119
|
+
|
|
120
|
+
|
|
107
121
|
BASH_TOOL_NAME = "bash"
|
|
108
122
|
|
|
109
123
|
|
|
@@ -131,6 +145,7 @@ ToolInputType = (
|
|
|
131
145
|
| ListToolInput
|
|
132
146
|
| GlobToolInput
|
|
133
147
|
| GrepToolInput
|
|
148
|
+
| EditToolInput
|
|
134
149
|
| BashToolInput
|
|
135
150
|
)
|
|
136
151
|
PartialToolResultType = PartialBashToolResult
|
|
@@ -141,6 +156,7 @@ ToolResultType = (
|
|
|
141
156
|
| ListToolResult
|
|
142
157
|
| GlobToolResult
|
|
143
158
|
| GrepToolResult
|
|
159
|
+
| EditToolResult
|
|
144
160
|
| BashToolResult
|
|
145
161
|
| ErrorToolResult
|
|
146
162
|
)
|
|
@@ -9,6 +9,8 @@ from exponent.core.remote_execution import files
|
|
|
9
9
|
from exponent.core.remote_execution.cli_rpc_types import (
|
|
10
10
|
BashToolInput,
|
|
11
11
|
BashToolResult,
|
|
12
|
+
EditToolInput,
|
|
13
|
+
EditToolResult,
|
|
12
14
|
ErrorToolResult,
|
|
13
15
|
GlobToolInput,
|
|
14
16
|
GlobToolResult,
|
|
@@ -51,6 +53,8 @@ async def execute_tool(
|
|
|
51
53
|
return await execute_glob_files(tool_input, working_directory)
|
|
52
54
|
elif isinstance(tool_input, GrepToolInput):
|
|
53
55
|
return await execute_grep_files(tool_input, working_directory)
|
|
56
|
+
elif isinstance(tool_input, EditToolInput):
|
|
57
|
+
return await execute_edit_file(tool_input, working_directory)
|
|
54
58
|
elif isinstance(tool_input, BashToolInput):
|
|
55
59
|
raise ValueError("Bash tool input should be handled by execute_bash_tool")
|
|
56
60
|
else:
|
|
@@ -190,6 +194,83 @@ async def execute_write_file(
|
|
|
190
194
|
return WriteToolResult(message=result)
|
|
191
195
|
|
|
192
196
|
|
|
197
|
+
async def execute_edit_file( # noqa: PLR0911
|
|
198
|
+
tool_input: EditToolInput, working_directory: str
|
|
199
|
+
) -> EditToolResult | ErrorToolResult:
|
|
200
|
+
# Validate absolute path requirement
|
|
201
|
+
if not tool_input.file_path.startswith("/"):
|
|
202
|
+
return ErrorToolResult(
|
|
203
|
+
error_message=f"File path must be absolute, got relative path: {tool_input.file_path}"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
file = AsyncPath(working_directory, tool_input.file_path)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
exists = await file.exists()
|
|
210
|
+
except (OSError, PermissionError) as e:
|
|
211
|
+
return ErrorToolResult(error_message=f"Cannot access file: {e!s}")
|
|
212
|
+
|
|
213
|
+
if not exists:
|
|
214
|
+
return ErrorToolResult(error_message="File not found")
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
if await file.is_dir():
|
|
218
|
+
return ErrorToolResult(
|
|
219
|
+
error_message=f"{await file.absolute()} is a directory"
|
|
220
|
+
)
|
|
221
|
+
except (OSError, PermissionError) as e:
|
|
222
|
+
return ErrorToolResult(error_message=f"Cannot check file type: {e!s}")
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
# Read the entire file without truncation limits
|
|
226
|
+
content = await safe_read_file(file)
|
|
227
|
+
except PermissionError:
|
|
228
|
+
return ErrorToolResult(
|
|
229
|
+
error_message=f"Permission denied: cannot read {tool_input.file_path}"
|
|
230
|
+
)
|
|
231
|
+
except UnicodeDecodeError:
|
|
232
|
+
return ErrorToolResult(
|
|
233
|
+
error_message="File appears to be binary or has invalid text encoding"
|
|
234
|
+
)
|
|
235
|
+
except Exception as e: # noqa: BLE001
|
|
236
|
+
return ErrorToolResult(error_message=f"Error reading file: {e!s}")
|
|
237
|
+
|
|
238
|
+
# Check if search text exists
|
|
239
|
+
if tool_input.old_string not in content:
|
|
240
|
+
return ErrorToolResult(
|
|
241
|
+
error_message=f"Search text not found in {tool_input.file_path}"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Check if old_string and new_string are identical
|
|
245
|
+
if tool_input.old_string == tool_input.new_string:
|
|
246
|
+
return ErrorToolResult(error_message="Old string and new string are identical")
|
|
247
|
+
|
|
248
|
+
# Check uniqueness if replace_all is False
|
|
249
|
+
if not tool_input.replace_all:
|
|
250
|
+
occurrences = content.count(tool_input.old_string)
|
|
251
|
+
if occurrences > 1:
|
|
252
|
+
return ErrorToolResult(
|
|
253
|
+
error_message=f"String '{tool_input.old_string}' appears {occurrences} times in file. Use a larger context or replace_all=True"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Perform replacement
|
|
257
|
+
if tool_input.replace_all:
|
|
258
|
+
new_content = content.replace(tool_input.old_string, tool_input.new_string)
|
|
259
|
+
else:
|
|
260
|
+
# Replace only the first occurrence
|
|
261
|
+
new_content = content.replace(tool_input.old_string, tool_input.new_string, 1)
|
|
262
|
+
|
|
263
|
+
# Write back to file
|
|
264
|
+
try:
|
|
265
|
+
path = Path(working_directory, tool_input.file_path)
|
|
266
|
+
await execute_full_file_rewrite(path, new_content, working_directory)
|
|
267
|
+
return EditToolResult(
|
|
268
|
+
message=f"Successfully replaced text in {tool_input.file_path}"
|
|
269
|
+
)
|
|
270
|
+
except Exception as e: # noqa: BLE001
|
|
271
|
+
return ErrorToolResult(error_message=f"Error writing file: {e!s}")
|
|
272
|
+
|
|
273
|
+
|
|
193
274
|
async def execute_list_files(
|
|
194
275
|
tool_input: ListToolInput, working_directory: str
|
|
195
276
|
) -> ListToolResult | ErrorToolResult:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "indent"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.6"
|
|
8
8
|
description = "Indent is an AI Pair Programmer"
|
|
9
9
|
authors = [{ name = "Sashank Thupukari", email = "sashank@exponent.run" }]
|
|
10
10
|
requires-python = ">=3.10,<3.13"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.5" # Keep in sync with pyproject.toml
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|