indent 0.1.1__tar.gz → 0.1.3__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.1 → indent-0.1.3}/PKG-INFO +2 -3
- indent-0.1.3/exponent/__init__.py +1 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/github_app_commands.py +3 -3
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/cli_rpc_types.py +3 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/files.py +30 -2
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/tool_execution.py +3 -8
- {indent-0.1.1 → indent-0.1.3}/pyproject.toml +3 -5
- indent-0.1.1/exponent/__init__.py +0 -1
- indent-0.1.1/exponent/core/remote_execution/command_execution.py +0 -105
- {indent-0.1.1 → indent-0.1.3}/.gitignore +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/cli.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/cloud_commands.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/common.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/config_commands.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/listen_commands.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/run_commands.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/settings.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/shell_commands.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/theme.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/types.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/upgrade.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/commands/utils.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/config.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/__init__.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/client.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/cloud_config_queries.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/get_chats_query.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/github_config_queries.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/mutations.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/queries.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/graphql/subscriptions.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/checkpoints.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/client.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/code_execution.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/error_info.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/exceptions.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/file_write.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/git.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/languages/python_execution.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/languages/shell_streaming.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/languages/types.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/session.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/system_context.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/truncation.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/types.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/remote_execution/utils.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/types/__init__.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/types/command_data.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/types/event_types.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/types/generated/__init__.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/core/types/generated/strategy_info.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/migration-docs/login.md +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/py.typed +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/utils/__init__.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/utils/colors.py +0 -0
- {indent-0.1.1 → indent-0.1.3}/exponent/utils/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: indent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
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
|
|
@@ -12,7 +12,6 @@ Requires-Dist: click<9,>=8.1.7
|
|
|
12
12
|
Requires-Dist: colour<0.2,>=0.1.5
|
|
13
13
|
Requires-Dist: diff-match-patch<20230431,>=20230430
|
|
14
14
|
Requires-Dist: eval-type-backport<0.3,>=0.2.0
|
|
15
|
-
Requires-Dist: gevent==24.2.1
|
|
16
15
|
Requires-Dist: git-python>=1.0.3
|
|
17
16
|
Requires-Dist: gitignore-parser<0.2,>=0.1.11
|
|
18
17
|
Requires-Dist: gql[httpx,websockets]<4,>=3.5.0
|
|
@@ -26,7 +25,7 @@ Requires-Dist: prompt-toolkit<4,>=3.0.36
|
|
|
26
25
|
Requires-Dist: pydantic-settings<3,>=2.2.1
|
|
27
26
|
Requires-Dist: pydantic[email]<3,>=2.6.4
|
|
28
27
|
Requires-Dist: pygit2<2,>=1.15.0
|
|
29
|
-
Requires-Dist: python-ripgrep==0.0.
|
|
28
|
+
Requires-Dist: python-ripgrep==0.0.9
|
|
30
29
|
Requires-Dist: pyyaml>=6.0.2
|
|
31
30
|
Requires-Dist: questionary<3,>=2.0.1
|
|
32
31
|
Requires-Dist: rapidfuzz<4,>=3.9.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.3" # Keep in sync with pyproject.toml
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
|
-
from pathlib import Path
|
|
4
3
|
import subprocess
|
|
5
4
|
import sys
|
|
6
|
-
from uuid import uuid4
|
|
7
5
|
import webbrowser
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
from git import GitCommandError, Repo
|
|
@@ -45,7 +45,7 @@ def install_github_app(
|
|
|
45
45
|
if not git_info:
|
|
46
46
|
raise RuntimeError("Not running inside of valid git repository")
|
|
47
47
|
|
|
48
|
-
install_url = "https://github.com/apps/indent
|
|
48
|
+
install_url = "https://github.com/apps/indent/installations/new"
|
|
49
49
|
webbrowser.open(install_url)
|
|
50
50
|
|
|
51
51
|
click.confirm(
|
|
@@ -96,14 +96,17 @@ class GrepToolInput(ToolInput, tag=GREP_TOOL_NAME):
|
|
|
96
96
|
pattern: str
|
|
97
97
|
path: str | None = None
|
|
98
98
|
include: str | None = None
|
|
99
|
+
multiline: bool | None = None
|
|
99
100
|
|
|
100
101
|
|
|
101
102
|
class GrepToolResult(ToolResult, tag=GREP_TOOL_NAME):
|
|
102
103
|
matches: list[str]
|
|
103
104
|
truncated: bool = False
|
|
104
105
|
|
|
106
|
+
|
|
105
107
|
WEB_FETCH_TOOL_NAME = "web_fetch"
|
|
106
108
|
|
|
109
|
+
|
|
107
110
|
class WebFetchToolInput(ToolInput, tag=WEB_FETCH_TOOL_NAME):
|
|
108
111
|
query: str
|
|
109
112
|
|
|
@@ -6,6 +6,7 @@ from anyio import Path as AsyncPath
|
|
|
6
6
|
from python_ripgrep import PySortMode, PySortModeKind, files, search
|
|
7
7
|
from rapidfuzz import process
|
|
8
8
|
|
|
9
|
+
from exponent.core.remote_execution.cli_rpc_types import ErrorToolResult, GrepToolResult
|
|
9
10
|
from exponent.core.remote_execution.types import (
|
|
10
11
|
FileAttachment,
|
|
11
12
|
FilePath,
|
|
@@ -28,6 +29,7 @@ FILE_NOT_FOUND: Final[str] = "File {} does not exist"
|
|
|
28
29
|
MAX_FILES_TO_WALK: Final[int] = 10_000
|
|
29
30
|
|
|
30
31
|
GLOB_MAX_COUNT: Final[int] = 1000
|
|
32
|
+
GREP_MAX_RESULTS = 100
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
class FileCache:
|
|
@@ -222,12 +224,32 @@ async def search_files(
|
|
|
222
224
|
file_pattern: str | None,
|
|
223
225
|
regex: str,
|
|
224
226
|
working_directory: str,
|
|
225
|
-
|
|
227
|
+
multiline: bool | None = None,
|
|
228
|
+
) -> GrepToolResult | ErrorToolResult:
|
|
226
229
|
path = AsyncPath(working_directory) / path_str
|
|
230
|
+
|
|
231
|
+
if not await path.exists():
|
|
232
|
+
return ErrorToolResult(
|
|
233
|
+
error_message=f"Path does not exist: {path_str}",
|
|
234
|
+
)
|
|
235
|
+
|
|
227
236
|
path_resolved = await path.resolve()
|
|
228
237
|
globs = [file_pattern] if file_pattern else None
|
|
229
238
|
|
|
230
|
-
|
|
239
|
+
if globs:
|
|
240
|
+
matched_files = await to_thread(
|
|
241
|
+
files,
|
|
242
|
+
patterns=[],
|
|
243
|
+
paths=[str(path_resolved)],
|
|
244
|
+
globs=globs,
|
|
245
|
+
max_count=1,
|
|
246
|
+
)
|
|
247
|
+
if not matched_files:
|
|
248
|
+
return ErrorToolResult(
|
|
249
|
+
error_message=f"No files matched the include glob pattern: {file_pattern} at {path_str}",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
results = await to_thread(
|
|
231
253
|
search,
|
|
232
254
|
patterns=[regex],
|
|
233
255
|
paths=[str(path_resolved)],
|
|
@@ -238,6 +260,12 @@ async def search_files(
|
|
|
238
260
|
separator_field_context="|",
|
|
239
261
|
separator_field_match="|",
|
|
240
262
|
separator_context="\n...\n",
|
|
263
|
+
multiline=multiline,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return GrepToolResult(
|
|
267
|
+
matches=results[:GREP_MAX_RESULTS],
|
|
268
|
+
truncated=bool(len(results) > GREP_MAX_RESULTS),
|
|
241
269
|
)
|
|
242
270
|
|
|
243
271
|
|
|
@@ -37,8 +37,6 @@ from exponent.core.remote_execution.utils import (
|
|
|
37
37
|
safe_read_file,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
-
GREP_MAX_RESULTS = 100
|
|
41
|
-
|
|
42
40
|
|
|
43
41
|
async def execute_tool(
|
|
44
42
|
tool_input: ToolInputType, working_directory: str
|
|
@@ -247,16 +245,13 @@ async def execute_glob_files(
|
|
|
247
245
|
|
|
248
246
|
async def execute_grep_files(
|
|
249
247
|
tool_input: GrepToolInput, working_directory: str
|
|
250
|
-
) -> GrepToolResult:
|
|
251
|
-
|
|
248
|
+
) -> GrepToolResult | ErrorToolResult:
|
|
249
|
+
return await files.search_files(
|
|
252
250
|
path_str=working_directory if tool_input.path is None else tool_input.path,
|
|
253
251
|
file_pattern=tool_input.include,
|
|
254
252
|
regex=tool_input.pattern,
|
|
255
253
|
working_directory=working_directory,
|
|
256
|
-
|
|
257
|
-
return GrepToolResult(
|
|
258
|
-
matches=results[:GREP_MAX_RESULTS],
|
|
259
|
-
truncated=bool(len(results) > GREP_MAX_RESULTS),
|
|
254
|
+
multiline=tool_input.multiline,
|
|
260
255
|
)
|
|
261
256
|
|
|
262
257
|
|
|
@@ -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.3"
|
|
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"
|
|
@@ -29,8 +29,7 @@ dependencies = [
|
|
|
29
29
|
"toml>=0.10.2,<0.11",
|
|
30
30
|
"websockets~=11.0",
|
|
31
31
|
"anyio>=4.6.0,<5",
|
|
32
|
-
"python-ripgrep==0.0.
|
|
33
|
-
"gevent==24.2.1",
|
|
32
|
+
"python-ripgrep==0.0.9",
|
|
34
33
|
"certifi>=2024.8.30,<2025",
|
|
35
34
|
"eval-type-backport>=0.2.0,<0.3",
|
|
36
35
|
"pip>=25.0.1,<26",
|
|
@@ -48,7 +47,6 @@ indent = "exponent.cli:cli"
|
|
|
48
47
|
dev = [
|
|
49
48
|
"coverage>=7.6.1,<8",
|
|
50
49
|
"freezegun>=1.5.1,<2",
|
|
51
|
-
"gevent>=24.2.1,<25",
|
|
52
50
|
"pytest>=8.3.3,<9",
|
|
53
51
|
"pytest-asyncio>=0.24.0,<0.25",
|
|
54
52
|
"pytest-cov>=5.0.0,<6",
|
|
@@ -103,7 +101,7 @@ filterwarnings = [
|
|
|
103
101
|
|
|
104
102
|
[tool.coverage.run]
|
|
105
103
|
# handle forms of concurrency
|
|
106
|
-
concurrency = ["
|
|
104
|
+
concurrency = ["thread", "multiprocessing"]
|
|
107
105
|
parallel = true
|
|
108
106
|
# provides sensible defaults for coverage reporting
|
|
109
107
|
plugins = ["covdefaults"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.1" # Keep in sync with pyproject.toml
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from exponent.core.remote_execution import files
|
|
5
|
-
from exponent.core.remote_execution.types import (
|
|
6
|
-
CommandRequest,
|
|
7
|
-
CommandResponse,
|
|
8
|
-
)
|
|
9
|
-
from exponent.core.types.command_data import (
|
|
10
|
-
FileReadCommandData,
|
|
11
|
-
PrototypeCommandData,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
# Intentionally split the separator into two parts
|
|
15
|
-
# to avoid matching it in the content
|
|
16
|
-
CONTEXT_BATCH_SEPARATOR = "\n<batch_sep" + "arator>\n"
|
|
17
|
-
CONTEXT_FILE_SEPARATOR = "\n<file_sep" + "arator>\n"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
async def execute_command(
|
|
21
|
-
request: CommandRequest,
|
|
22
|
-
working_directory: str,
|
|
23
|
-
) -> CommandResponse:
|
|
24
|
-
try:
|
|
25
|
-
if isinstance(request.data, FileReadCommandData):
|
|
26
|
-
correlation_id = request.correlation_id
|
|
27
|
-
file_path = request.data.file_path
|
|
28
|
-
path = Path(working_directory, file_path)
|
|
29
|
-
content, _ = await files.get_file_content(
|
|
30
|
-
path, request.data.offset, request.data.limit
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
return CommandResponse(
|
|
34
|
-
subcommand=request.data.type.value,
|
|
35
|
-
content=content,
|
|
36
|
-
correlation_id=correlation_id,
|
|
37
|
-
)
|
|
38
|
-
elif isinstance(request.data, PrototypeCommandData):
|
|
39
|
-
correlation_id = request.correlation_id
|
|
40
|
-
command_name = request.data.command_name
|
|
41
|
-
content_json = request.data.content_json
|
|
42
|
-
content_raw = request.data.content_raw
|
|
43
|
-
content_rendered = request.data.content_rendered
|
|
44
|
-
|
|
45
|
-
content = await execute_prototype_command(
|
|
46
|
-
command_name=command_name,
|
|
47
|
-
content_json=content_json,
|
|
48
|
-
content_raw=content_raw,
|
|
49
|
-
content_rendered=content_rendered,
|
|
50
|
-
working_directory=working_directory,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
return CommandResponse(
|
|
54
|
-
subcommand=command_name,
|
|
55
|
-
content=content,
|
|
56
|
-
correlation_id=correlation_id,
|
|
57
|
-
)
|
|
58
|
-
else:
|
|
59
|
-
raise ValueError(f"Unknown command request: {request}")
|
|
60
|
-
except Exception as e: # noqa: BLE001 - TODO (Josh): Specialize errors for execution
|
|
61
|
-
return CommandResponse(
|
|
62
|
-
content="An error occurred during command execution: " + str(e),
|
|
63
|
-
correlation_id=request.correlation_id,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
async def execute_prototype_command(
|
|
68
|
-
command_name: str,
|
|
69
|
-
content_json: dict[str, Any],
|
|
70
|
-
content_raw: str,
|
|
71
|
-
content_rendered: str,
|
|
72
|
-
working_directory: str,
|
|
73
|
-
) -> str:
|
|
74
|
-
if command_name == "file_open":
|
|
75
|
-
return f'Successfully opened file "{content_json["file_path"]}"'
|
|
76
|
-
elif command_name == "search_files":
|
|
77
|
-
results = await files.search_files(
|
|
78
|
-
path_str=content_json["path"],
|
|
79
|
-
file_pattern=content_json["file_pattern"],
|
|
80
|
-
regex=content_json["regex"],
|
|
81
|
-
working_directory=working_directory,
|
|
82
|
-
)
|
|
83
|
-
return "\n".join(results)
|
|
84
|
-
elif command_name == "codebase_context":
|
|
85
|
-
batches = await files.get_all_file_contents(
|
|
86
|
-
working_directory=working_directory,
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
return CONTEXT_BATCH_SEPARATOR.join(
|
|
90
|
-
CONTEXT_FILE_SEPARATOR.join(batch) for batch in batches
|
|
91
|
-
)
|
|
92
|
-
elif command_name == "ls":
|
|
93
|
-
results = await files.file_walk(
|
|
94
|
-
directory=content_json["path"],
|
|
95
|
-
ignore_extra=files.DEFAULT_IGNORES,
|
|
96
|
-
max_files=1000,
|
|
97
|
-
)
|
|
98
|
-
return "\n".join(results)
|
|
99
|
-
elif command_name == "glob":
|
|
100
|
-
results = await files.glob(
|
|
101
|
-
path=content_json["path"],
|
|
102
|
-
glob_pattern=content_json["glob"],
|
|
103
|
-
)
|
|
104
|
-
return "\n".join(results)
|
|
105
|
-
raise ValueError(f"Unhandled prototype command: {command_name}")
|
|
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
|