codetool-explore 0.6.0__tar.gz → 0.7.1__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.
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/PKG-INFO +4 -2
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/README.md +3 -1
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/pyproject.toml +1 -1
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/api.py +2 -1
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/explorer.py +21 -1
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/matcher.py +4 -3
- codetool_explore-0.7.1/src/codetool_explore/regex.py +14 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_api.py +68 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/uv.lock +1 -1
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/.gitignore +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/LICENSE +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/benchmarks/benchmark_output_lengths.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/benchmarks/benchmark_search.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/hatch_build.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/Cargo.lock +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/Cargo.toml +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/app.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/case.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/config.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/constants.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/file_search.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/ignore_rules.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/literal.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/main.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/matcher.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/models.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/output.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/path_utils.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/ranking.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/regex_search.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/search.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/text.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/rust/src/walker.rs +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/scripts/package_rust_binary.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/scripts/update_readme_benchmarks.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/__init__.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/cli.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/compression.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/cursor.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/errors.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/ignore.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/py.typed +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/__init__.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/case.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/config.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/constants.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/file_search.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/ignore_rules.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/literal.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/models.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/output.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/regex_search.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/search.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/text.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/walker.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/ranking.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/roots.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/rust_backend.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/text_output.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test___init__.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_cli.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_cursor.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_hatch_build.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_ignore.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_packaged_binary.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_python_backend.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_ranking.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_rust_backend.py +0 -0
- {codetool_explore-0.6.0 → codetool_explore-0.7.1}/tests/test_rust_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codetool-explore
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: Fast, dependency-free workspace search, read, and list exploration for coding-agent tools with Rust backend
|
|
5
5
|
Project-URL: Homepage, https://github.com/pbi-agent/codetool-explore
|
|
6
6
|
Project-URL: Repository, https://github.com/pbi-agent/codetool-explore
|
|
@@ -114,7 +114,9 @@ than one file is read, each file is prefixed by a compact `path:` header. Use
|
|
|
114
114
|
`start_line` and `limit` to cap the returned line range; if more lines remain,
|
|
115
115
|
text output starts with `-- more: cursor=N`. CSV files are read as ordinary
|
|
116
116
|
text. Binary-looking, missing, unreadable, or directory paths fail with
|
|
117
|
-
controlled `ExploreError` subclasses.
|
|
117
|
+
controlled `ExploreError` subclasses. If `root` is already a file,
|
|
118
|
+
`target="read"` reads that file directly, so accidental search-like `pattern`
|
|
119
|
+
values from tool calls still produce the requested line range.
|
|
118
120
|
|
|
119
121
|
`target="list"` treats `pattern` as one file/directory path and returns one
|
|
120
122
|
directory level under each root. Text output uses the same compact tree display
|
|
@@ -84,7 +84,9 @@ than one file is read, each file is prefixed by a compact `path:` header. Use
|
|
|
84
84
|
`start_line` and `limit` to cap the returned line range; if more lines remain,
|
|
85
85
|
text output starts with `-- more: cursor=N`. CSV files are read as ordinary
|
|
86
86
|
text. Binary-looking, missing, unreadable, or directory paths fail with
|
|
87
|
-
controlled `ExploreError` subclasses.
|
|
87
|
+
controlled `ExploreError` subclasses. If `root` is already a file,
|
|
88
|
+
`target="read"` reads that file directly, so accidental search-like `pattern`
|
|
89
|
+
values from tool calls still produce the requested line range.
|
|
88
90
|
|
|
89
91
|
`target="list"` treats `pattern` as one file/directory path and returns one
|
|
90
92
|
directory level under each root. Text output uses the same compact tree display
|
|
@@ -10,6 +10,7 @@ from .compression import compress_result
|
|
|
10
10
|
from .errors import ExploreArgumentError, ExploreBackendError, ExplorePatternError
|
|
11
11
|
from .explorer import list_path_target, read_file_target
|
|
12
12
|
from .python_backend import resolve_case, search_python
|
|
13
|
+
from .regex import compile_regex
|
|
13
14
|
from .roots import RootInput
|
|
14
15
|
from .rust_backend import RustBackendUnavailable, find_rust_binary, search_rust
|
|
15
16
|
from .text_output import format_text_result
|
|
@@ -68,7 +69,7 @@ def _regex_can_produce_empty_match(pattern: str, *, case: str) -> bool:
|
|
|
68
69
|
_, case_sensitive = resolve_case(requested_case, pattern)
|
|
69
70
|
flags = 0 if case_sensitive else re.IGNORECASE
|
|
70
71
|
try:
|
|
71
|
-
compiled =
|
|
72
|
+
compiled = compile_regex(pattern, flags)
|
|
72
73
|
except re.error as exc:
|
|
73
74
|
raise ExplorePatternError(f"invalid regex: {exc}") from exc
|
|
74
75
|
return any(
|
|
@@ -252,7 +252,12 @@ def _resolve_explore_paths(
|
|
|
252
252
|
raise ExploreArgumentError(f"{target} path must not be empty")
|
|
253
253
|
|
|
254
254
|
return tuple(
|
|
255
|
-
_resolve_path_for_search_root(
|
|
255
|
+
_resolve_path_for_search_root(
|
|
256
|
+
query,
|
|
257
|
+
search_root=search_root,
|
|
258
|
+
root_set=root_set,
|
|
259
|
+
target=target,
|
|
260
|
+
)
|
|
256
261
|
for search_root in root_set.roots
|
|
257
262
|
)
|
|
258
263
|
|
|
@@ -262,10 +267,25 @@ def _resolve_path_for_search_root(
|
|
|
262
267
|
*,
|
|
263
268
|
search_root: SearchRoot,
|
|
264
269
|
root_set: NormalizedRoots,
|
|
270
|
+
target: str,
|
|
265
271
|
) -> ResolvedExplorePath:
|
|
266
272
|
root_abs = search_root.abs_path
|
|
267
273
|
root_is_file = os.path.isfile(root_abs)
|
|
268
274
|
root_base = root_abs if os.path.isdir(root_abs) else os.path.dirname(root_abs)
|
|
275
|
+
if target == "read" and root_is_file:
|
|
276
|
+
display_base = root_set.rel_base if root_set.has_multiple else root_base
|
|
277
|
+
display_path = _display_path(root_abs, display_base)
|
|
278
|
+
return ResolvedExplorePath(
|
|
279
|
+
query=query,
|
|
280
|
+
abs_path=root_abs,
|
|
281
|
+
display_path=display_path,
|
|
282
|
+
root_display=root_set.display,
|
|
283
|
+
display_base=display_base,
|
|
284
|
+
root_base=root_base,
|
|
285
|
+
search_root_abs=root_abs,
|
|
286
|
+
root_is_file=True,
|
|
287
|
+
rel_base_abs=root_set.rel_base,
|
|
288
|
+
)
|
|
269
289
|
if os.path.isabs(query):
|
|
270
290
|
abs_path = os.path.abspath(query)
|
|
271
291
|
base = root_set.rel_base if root_set.has_multiple else root_base
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/matcher.py
RENAMED
|
@@ -6,6 +6,7 @@ import re
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from typing import TypeAlias
|
|
8
8
|
|
|
9
|
+
from ..regex import compile_regex
|
|
9
10
|
from .literal import LiteralMatcher
|
|
10
11
|
from .regex_search import RegexLineMatcher
|
|
11
12
|
|
|
@@ -22,7 +23,7 @@ def build_content_matcher(
|
|
|
22
23
|
|
|
23
24
|
if regex:
|
|
24
25
|
flags = 0 if case_sensitive else re.IGNORECASE
|
|
25
|
-
return RegexLineMatcher(
|
|
26
|
+
return RegexLineMatcher(compile_regex(pattern, flags))
|
|
26
27
|
needle = pattern.encode("utf-8", errors="surrogatepass")
|
|
27
28
|
return LiteralMatcher(needle=needle, case_sensitive=case_sensitive)
|
|
28
29
|
|
|
@@ -49,7 +50,7 @@ class PathMatcher:
|
|
|
49
50
|
compiled = None
|
|
50
51
|
if regex:
|
|
51
52
|
flags = 0 if case_sensitive else re.IGNORECASE
|
|
52
|
-
compiled =
|
|
53
|
+
compiled = compile_regex(pattern, flags)
|
|
53
54
|
return cls(
|
|
54
55
|
pattern=pattern,
|
|
55
56
|
regex=regex,
|
|
@@ -76,4 +77,4 @@ def path_match_subject(rel_path: str, path_scope: str) -> str:
|
|
|
76
77
|
|
|
77
78
|
if path_scope == "basename":
|
|
78
79
|
return rel_path.rsplit("/", 1)[-1]
|
|
79
|
-
return rel_path
|
|
80
|
+
return rel_path
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Shared regex helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def compile_regex(pattern: str, flags: int = 0) -> re.Pattern[str]:
|
|
10
|
+
"""Compile a user regex without leaking Python's advisory future warnings."""
|
|
11
|
+
|
|
12
|
+
with warnings.catch_warnings():
|
|
13
|
+
warnings.simplefilter("ignore", FutureWarning)
|
|
14
|
+
return re.compile(pattern, flags)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import warnings
|
|
4
5
|
|
|
5
6
|
import pytest
|
|
6
7
|
|
|
@@ -265,6 +266,41 @@ def test_read_target_defaults_to_plain_text_line_range(tmp_path):
|
|
|
265
266
|
assert "2:" not in result
|
|
266
267
|
|
|
267
268
|
|
|
269
|
+
def test_read_target_accepts_file_root_with_search_hint_pattern(tmp_path):
|
|
270
|
+
target = tmp_path / "webapp" / "src" / "styles" / "modal.css"
|
|
271
|
+
write(
|
|
272
|
+
target,
|
|
273
|
+
"one\n"
|
|
274
|
+
"two\n"
|
|
275
|
+
".task-form-dialog {\n"
|
|
276
|
+
" color: red;\n"
|
|
277
|
+
"}\n",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
text = api.explore(
|
|
281
|
+
"task-form-dialog",
|
|
282
|
+
root=str(target),
|
|
283
|
+
target="read",
|
|
284
|
+
start_line=3,
|
|
285
|
+
limit=2,
|
|
286
|
+
)
|
|
287
|
+
full = api.explore(
|
|
288
|
+
"task-form-dialog",
|
|
289
|
+
root=str(target),
|
|
290
|
+
target="read",
|
|
291
|
+
start_line=3,
|
|
292
|
+
limit=2,
|
|
293
|
+
result_format="full",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
assert text == "-- more: cursor=5\n.task-form-dialog {\n color: red;"
|
|
297
|
+
assert full["pattern"] == "task-form-dialog"
|
|
298
|
+
assert full["root"] == str(target)
|
|
299
|
+
assert full["path"] == "modal.css"
|
|
300
|
+
assert full["returned"] == 2
|
|
301
|
+
assert full["next_cursor"] == "5"
|
|
302
|
+
|
|
303
|
+
|
|
268
304
|
def test_read_target_reports_more_and_supports_csv_as_text(tmp_path):
|
|
269
305
|
write(tmp_path / "data.csv", "name,value\nalpha,1\nbeta,2\n")
|
|
270
306
|
|
|
@@ -788,6 +824,38 @@ def test_auto_regex_empty_match_uses_python_for_counts(monkeypatch, tmp_path):
|
|
|
788
824
|
assert result["matches"] == [{"path": "a.py", "count": 4, "first_line": 1}]
|
|
789
825
|
|
|
790
826
|
|
|
827
|
+
def test_nested_set_regex_warning_is_suppressed_in_auto(monkeypatch, tmp_path):
|
|
828
|
+
write(tmp_path / "a.py", "[abc]\n")
|
|
829
|
+
monkeypatch.setattr(api, "find_rust_binary", lambda: "/fake/rust")
|
|
830
|
+
|
|
831
|
+
def fake_rust(pattern, **kwargs):
|
|
832
|
+
return {
|
|
833
|
+
"pattern": pattern,
|
|
834
|
+
"backend": "rust",
|
|
835
|
+
"root": kwargs["root"],
|
|
836
|
+
"regex": True,
|
|
837
|
+
"mode": kwargs["mode"],
|
|
838
|
+
"matches": [],
|
|
839
|
+
"returned": 0,
|
|
840
|
+
"total_files": 1,
|
|
841
|
+
"total_matches": 0,
|
|
842
|
+
"count": 0,
|
|
843
|
+
"truncated": False,
|
|
844
|
+
"next_cursor": None,
|
|
845
|
+
"offset": 0,
|
|
846
|
+
"limit": kwargs["limit"],
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
monkeypatch.setattr(api, "search_rust", fake_rust)
|
|
850
|
+
|
|
851
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
852
|
+
warnings.simplefilter("always")
|
|
853
|
+
api.explore("[[abc]]", root=str(tmp_path), result_format="full")
|
|
854
|
+
api.explore("[[abc]]", root=str(tmp_path), backend="python")
|
|
855
|
+
|
|
856
|
+
assert not [item for item in caught if item.category is FutureWarning]
|
|
857
|
+
|
|
858
|
+
|
|
791
859
|
def test_auto_regex_falls_back_to_python_when_rust_rejects_syntax(
|
|
792
860
|
monkeypatch, tmp_path
|
|
793
861
|
):
|
|
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
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/__init__.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/case.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/config.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/constants.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/file_search.py
RENAMED
|
File without changes
|
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/literal.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/models.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/output.py
RENAMED
|
File without changes
|
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/search.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/text.py
RENAMED
|
File without changes
|
{codetool_explore-0.6.0 → codetool_explore-0.7.1}/src/codetool_explore/python_backend/walker.py
RENAMED
|
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
|