indent 0.1.4__py3-none-any.whl → 0.1.6__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.

Potentially problematic release.


This version of indent might be problematic. Click here for more details.

exponent/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.4" # Keep in sync with pyproject.toml
1
+ __version__ = "0.1.6" # Keep in sync with pyproject.toml
@@ -104,18 +104,18 @@ class GrepToolResult(ToolResult, tag=GREP_TOOL_NAME):
104
104
  truncated: bool = False
105
105
 
106
106
 
107
- WEB_FETCH_TOOL_NAME = "web_fetch"
107
+ EDIT_TOOL_NAME = "edit"
108
108
 
109
109
 
110
- class WebFetchToolInput(ToolInput, tag=WEB_FETCH_TOOL_NAME):
111
- query: str
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
112
115
 
113
116
 
114
- class WebFetchToolResult(ToolResult, tag=WEB_FETCH_TOOL_NAME):
115
- url: str
116
- title: bool = False
117
- encrypted_content: str | None = None
118
- page_age: str | None = None
117
+ class EditToolResult(ToolResult, tag=EDIT_TOOL_NAME):
118
+ message: str
119
119
 
120
120
 
121
121
  BASH_TOOL_NAME = "bash"
@@ -145,6 +145,7 @@ ToolInputType = (
145
145
  | ListToolInput
146
146
  | GlobToolInput
147
147
  | GrepToolInput
148
+ | EditToolInput
148
149
  | BashToolInput
149
150
  )
150
151
  PartialToolResultType = PartialBashToolResult
@@ -155,6 +156,7 @@ ToolResultType = (
155
156
  | ListToolResult
156
157
  | GlobToolResult
157
158
  | GrepToolResult
159
+ | EditToolResult
158
160
  | BashToolResult
159
161
  | ErrorToolResult
160
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: indent
3
- Version: 0.1.4
3
+ Version: 0.1.6
4
4
  Summary: Indent is an AI Pair Programmer
5
5
  Author-email: Sashank Thupukari <sashank@exponent.run>
6
6
  Requires-Python: <3.13,>=3.10
@@ -1,4 +1,4 @@
1
- exponent/__init__.py,sha256=EbzjWm450G-BHYY2B1fRN9gjMU0txzZ-JYSHtaTQGMs,58
1
+ exponent/__init__.py,sha256=oVf6rpSEmuBL8TgePOpVGMK15v_0KI_2MgQ0HuIaP14,58
2
2
  exponent/cli.py,sha256=GA-Bw1nys9JNrlob0t3LcS9OZ4U5JcNYjR_r3kDD_qs,3596
3
3
  exponent/py.typed,sha256=9XZl5avs8yHp89XP_1Fjtbeg_2rjYorCC9I0k_j-h2c,334
4
4
  exponent/commands/cloud_commands.py,sha256=4DgS7PjCtCFB5uNN-szzAzOj16UU1D9b9_qS7DskoLE,2026
@@ -24,7 +24,7 @@ exponent/core/graphql/mutations.py,sha256=WRwgJzMTETvry1yc9-EBlIRWkePjHIskBAm_6t
24
24
  exponent/core/graphql/queries.py,sha256=TXXHLGb7QpeICaofowVYsjyHDfqjCoQ3omLbesuw06s,2612
25
25
  exponent/core/graphql/subscriptions.py,sha256=gg42wG5HqEuNMJU7OUHruNCAGtM6FKPLRD7KfjcKjC4,9995
26
26
  exponent/core/remote_execution/checkpoints.py,sha256=3QGYMLa8vT7XmxMYTRcGrW8kNGHwRC0AkUfULribJWg,6354
27
- exponent/core/remote_execution/cli_rpc_types.py,sha256=3Yj6kIkqPAf7FNNni5V4XNe8SLPoTgbcL6swesW7cw4,4593
27
+ exponent/core/remote_execution/cli_rpc_types.py,sha256=GPRK_9bKJiNHJx3Y-3elO0rOwz70kyik9Wo3xdGoLVA,4587
28
28
  exponent/core/remote_execution/client.py,sha256=u5JH1yhbHcWpdee340x9JnYCsFJ-rJuu0-YPgSbojRw,21603
29
29
  exponent/core/remote_execution/code_execution.py,sha256=jYPB_7dJzS9BTPLX9fKQpsFPatwjbXuaFFSxT9tDTfI,2388
30
30
  exponent/core/remote_execution/error_info.py,sha256=Rd7OA3ps06qYejPVcOaMBB9AtftP3wqQoOfiILFASnc,1378
@@ -34,7 +34,7 @@ exponent/core/remote_execution/files.py,sha256=0EmOP2OdreGHjkKEIhtB_nNcjvLLx_UJW
34
34
  exponent/core/remote_execution/git.py,sha256=Yo4mhkl6LYzGhVco91j_E8WOUey5KL9437rk43VCCA8,7826
35
35
  exponent/core/remote_execution/session.py,sha256=cSJcCG1o74mBE6lZS_9VFmhyZdW6BeIOsbq4IVWH0t4,3863
36
36
  exponent/core/remote_execution/system_context.py,sha256=0FkbsSxEVjdjTF0tQpOkYK_VaVM126C3_K8QP0YXxOs,1510
37
- exponent/core/remote_execution/tool_execution.py,sha256=G7wB_DA0jANqywhm_a6lbWtwjCcpQsuQHlExGohsgeY,9233
37
+ exponent/core/remote_execution/tool_execution.py,sha256=_UQPyOTY49RQ70kfgnf03eev7c7lTWhFBC68cuifj2M,12354
38
38
  exponent/core/remote_execution/truncation.py,sha256=rFQDfT7qf_u6lvhEADWSpoRe_GTPegXXqknk7OZp0uI,10093
39
39
  exponent/core/remote_execution/types.py,sha256=3x73SJ3jQtaIu01musXgS-VF_MYwYucG3hyA2nWHbII,17567
40
40
  exponent/core/remote_execution/utils.py,sha256=Hw2zGMq0NKVwkVzNOVekAvpM33rfIz2P36mEcJr43Ag,20688
@@ -50,7 +50,7 @@ exponent/migration-docs/login.md,sha256=KIeXy3m2nzSUgw-4PW1XzXfHael1D4Zu93CplLMb
50
50
  exponent/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  exponent/utils/colors.py,sha256=HBkqe_ZmhJ9YiL2Fpulqek4KvLS5mwBTY4LQSM5N8SM,2762
52
52
  exponent/utils/version.py,sha256=Q4txP7Rg_KO0u0tUpx8O0DoOt32wrX7ctNeDXVKaOfA,8835
53
- indent-0.1.4.dist-info/METADATA,sha256=OTkZrr6729d5hBErMwl_RqvSr1ZrLRfCsx-MiuWA2b8,1283
54
- indent-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
- indent-0.1.4.dist-info/entry_points.txt,sha256=q8q1t1sbl4NULGOR0OV5RmSG4KEjkpEQRU_RUXEGzcs,44
56
- indent-0.1.4.dist-info/RECORD,,
53
+ indent-0.1.6.dist-info/METADATA,sha256=09jZnI_VohQwM0FiOXUtvE6Q-Bf_8AA_fgkXTYu77hc,1283
54
+ indent-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ indent-0.1.6.dist-info/entry_points.txt,sha256=q8q1t1sbl4NULGOR0OV5RmSG4KEjkpEQRU_RUXEGzcs,44
56
+ indent-0.1.6.dist-info/RECORD,,
File without changes