tree-sitter-analyzer 1.9.17.1__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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Standalone CLI for find_and_grep (fd → ripgrep composition)
|
|
4
|
+
|
|
5
|
+
Maps CLI flags to the MCP FindAndGrepTool and prints JSON/text.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from ...mcp.tools.find_and_grep_tool import FindAndGrepTool
|
|
16
|
+
from ...output_manager import output_data, output_error, set_output_mode
|
|
17
|
+
from ...project_detector import detect_project_root
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
21
|
+
parser = argparse.ArgumentParser(
|
|
22
|
+
description="Two-stage search: fd for files, then ripgrep for content.",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Required
|
|
26
|
+
parser.add_argument("--roots", nargs="+", required=True, help="Search roots")
|
|
27
|
+
parser.add_argument("--query", required=True, help="Content query")
|
|
28
|
+
|
|
29
|
+
# Output
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--output-format",
|
|
32
|
+
choices=["json", "text"],
|
|
33
|
+
default="json",
|
|
34
|
+
help="Output format (default: json)",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--quiet",
|
|
38
|
+
action="store_true",
|
|
39
|
+
help="Suppress non-essential output",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# fd options (subset mirrors ListFiles)
|
|
43
|
+
parser.add_argument("--pattern")
|
|
44
|
+
parser.add_argument("--glob", action="store_true")
|
|
45
|
+
parser.add_argument("--types", nargs="+")
|
|
46
|
+
parser.add_argument("--extensions", nargs="+")
|
|
47
|
+
parser.add_argument("--exclude", nargs="+")
|
|
48
|
+
parser.add_argument("--depth", type=int)
|
|
49
|
+
parser.add_argument("--follow-symlinks", action="store_true")
|
|
50
|
+
parser.add_argument("--hidden", action="store_true")
|
|
51
|
+
parser.add_argument("--no-ignore", action="store_true")
|
|
52
|
+
parser.add_argument("--size", nargs="+")
|
|
53
|
+
parser.add_argument("--changed-within")
|
|
54
|
+
parser.add_argument("--changed-before")
|
|
55
|
+
parser.add_argument("--full-path-match", action="store_true")
|
|
56
|
+
parser.add_argument("--file-limit", type=int)
|
|
57
|
+
parser.add_argument("--sort", choices=["path", "mtime", "size"])
|
|
58
|
+
|
|
59
|
+
# rg options (subset mirrors SearchContent)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--case", choices=["smart", "insensitive", "sensitive"], default="smart"
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument("--fixed-strings", action="store_true")
|
|
64
|
+
parser.add_argument("--word", action="store_true")
|
|
65
|
+
parser.add_argument("--multiline", action="store_true")
|
|
66
|
+
parser.add_argument("--include-globs", nargs="+")
|
|
67
|
+
parser.add_argument("--exclude-globs", nargs="+")
|
|
68
|
+
parser.add_argument("--max-filesize")
|
|
69
|
+
parser.add_argument("--context-before", type=int)
|
|
70
|
+
parser.add_argument("--context-after", type=int)
|
|
71
|
+
parser.add_argument("--encoding")
|
|
72
|
+
parser.add_argument("--max-count", type=int)
|
|
73
|
+
parser.add_argument("--timeout-ms", type=int)
|
|
74
|
+
parser.add_argument("--count-only-matches", action="store_true")
|
|
75
|
+
parser.add_argument("--summary-only", action="store_true")
|
|
76
|
+
parser.add_argument("--optimize-paths", action="store_true")
|
|
77
|
+
parser.add_argument("--group-by-file", action="store_true")
|
|
78
|
+
parser.add_argument("--total-only", action="store_true")
|
|
79
|
+
|
|
80
|
+
# project root
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--project-root",
|
|
83
|
+
help="Project root directory for security boundary (auto-detected if omitted)",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return parser
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def _run(args: argparse.Namespace) -> int:
|
|
90
|
+
set_output_mode(quiet=bool(args.quiet), json_output=(args.output_format == "json"))
|
|
91
|
+
|
|
92
|
+
project_root = detect_project_root(None, args.project_root)
|
|
93
|
+
tool = FindAndGrepTool(project_root)
|
|
94
|
+
|
|
95
|
+
payload: dict[str, Any] = {
|
|
96
|
+
"roots": list(args.roots),
|
|
97
|
+
"query": args.query,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# fd stage mappings
|
|
101
|
+
if args.pattern:
|
|
102
|
+
payload["pattern"] = args.pattern
|
|
103
|
+
if args.glob:
|
|
104
|
+
payload["glob"] = True
|
|
105
|
+
if args.types:
|
|
106
|
+
payload["types"] = args.types
|
|
107
|
+
if args.extensions:
|
|
108
|
+
payload["extensions"] = args.extensions
|
|
109
|
+
if args.exclude:
|
|
110
|
+
payload["exclude"] = args.exclude
|
|
111
|
+
if args.depth is not None:
|
|
112
|
+
payload["depth"] = int(args.depth)
|
|
113
|
+
if args.follow_symlinks:
|
|
114
|
+
payload["follow_symlinks"] = True
|
|
115
|
+
if args.hidden:
|
|
116
|
+
payload["hidden"] = True
|
|
117
|
+
if args.no_ignore:
|
|
118
|
+
payload["no_ignore"] = True
|
|
119
|
+
if args.size:
|
|
120
|
+
payload["size"] = args.size
|
|
121
|
+
if args.changed_within:
|
|
122
|
+
payload["changed_within"] = args.changed_within
|
|
123
|
+
if args.changed_before:
|
|
124
|
+
payload["changed_before"] = args.changed_before
|
|
125
|
+
if args.full_path_match:
|
|
126
|
+
payload["full_path_match"] = True
|
|
127
|
+
if args.file_limit is not None:
|
|
128
|
+
payload["file_limit"] = int(args.file_limit)
|
|
129
|
+
if args.sort:
|
|
130
|
+
payload["sort"] = args.sort
|
|
131
|
+
|
|
132
|
+
# rg stage mappings
|
|
133
|
+
if args.case:
|
|
134
|
+
payload["case"] = args.case
|
|
135
|
+
if args.fixed_strings:
|
|
136
|
+
payload["fixed_strings"] = True
|
|
137
|
+
if args.word:
|
|
138
|
+
payload["word"] = True
|
|
139
|
+
if args.multiline:
|
|
140
|
+
payload["multiline"] = True
|
|
141
|
+
if args.include_globs:
|
|
142
|
+
payload["include_globs"] = args.include_globs
|
|
143
|
+
if args.exclude_globs:
|
|
144
|
+
payload["exclude_globs"] = args.exclude_globs
|
|
145
|
+
if args.max_filesize:
|
|
146
|
+
payload["max_filesize"] = args.max_filesize
|
|
147
|
+
if args.context_before is not None:
|
|
148
|
+
payload["context_before"] = int(args.context_before)
|
|
149
|
+
if args.context_after is not None:
|
|
150
|
+
payload["context_after"] = int(args.context_after)
|
|
151
|
+
if args.encoding:
|
|
152
|
+
payload["encoding"] = args.encoding
|
|
153
|
+
if args.max_count is not None:
|
|
154
|
+
payload["max_count"] = int(args.max_count)
|
|
155
|
+
if args.timeout_ms is not None:
|
|
156
|
+
payload["timeout_ms"] = int(args.timeout_ms)
|
|
157
|
+
if args.count_only_matches:
|
|
158
|
+
payload["count_only_matches"] = True
|
|
159
|
+
if args.summary_only:
|
|
160
|
+
payload["summary_only"] = True
|
|
161
|
+
if args.optimize_paths:
|
|
162
|
+
payload["optimize_paths"] = True
|
|
163
|
+
if args.group_by_file:
|
|
164
|
+
payload["group_by_file"] = True
|
|
165
|
+
if args.total_only:
|
|
166
|
+
payload["total_only"] = True
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
result = await tool.execute(payload)
|
|
170
|
+
output_data(result, args.output_format)
|
|
171
|
+
return 0 if (isinstance(result, dict) or isinstance(result, int)) else 0
|
|
172
|
+
except Exception as e:
|
|
173
|
+
output_error(str(e))
|
|
174
|
+
return 1
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def main() -> None:
|
|
178
|
+
parser = _build_parser()
|
|
179
|
+
args = parser.parse_args()
|
|
180
|
+
try:
|
|
181
|
+
rc = asyncio.run(_run(args))
|
|
182
|
+
except KeyboardInterrupt:
|
|
183
|
+
rc = 1
|
|
184
|
+
sys.exit(rc)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
main()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Standalone CLI for list_files (fd wrapper)
|
|
4
|
+
|
|
5
|
+
Maps CLI flags to the MCP ListFilesTool and prints JSON/text via OutputManager.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from ...mcp.tools.list_files_tool import ListFilesTool
|
|
16
|
+
from ...output_manager import output_data, output_error, set_output_mode
|
|
17
|
+
from ...project_detector import detect_project_root
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
21
|
+
parser = argparse.ArgumentParser(
|
|
22
|
+
description="List files and directories using fd via MCP wrapper.",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Roots
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"roots",
|
|
28
|
+
nargs="+",
|
|
29
|
+
help="One or more root directories to search in",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Output
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--output-format",
|
|
35
|
+
choices=["json", "text"],
|
|
36
|
+
default="json",
|
|
37
|
+
help="Output format (default: json)",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--quiet",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="Suppress non-essential output",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# fd options
|
|
46
|
+
parser.add_argument("--pattern")
|
|
47
|
+
parser.add_argument("--glob", action="store_true")
|
|
48
|
+
parser.add_argument("--types", nargs="+")
|
|
49
|
+
parser.add_argument("--extensions", nargs="+")
|
|
50
|
+
parser.add_argument("--exclude", nargs="+")
|
|
51
|
+
parser.add_argument("--depth", type=int)
|
|
52
|
+
parser.add_argument("--follow-symlinks", action="store_true")
|
|
53
|
+
parser.add_argument("--hidden", action="store_true")
|
|
54
|
+
parser.add_argument("--no-ignore", action="store_true")
|
|
55
|
+
parser.add_argument("--size", nargs="+")
|
|
56
|
+
parser.add_argument("--changed-within")
|
|
57
|
+
parser.add_argument("--changed-before")
|
|
58
|
+
parser.add_argument("--full-path-match", action="store_true")
|
|
59
|
+
parser.add_argument("--limit", type=int)
|
|
60
|
+
parser.add_argument("--count-only", action="store_true")
|
|
61
|
+
|
|
62
|
+
# project root
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--project-root",
|
|
65
|
+
help="Project root directory for security boundary (auto-detected if omitted)",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return parser
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def _run(args: argparse.Namespace) -> int:
|
|
72
|
+
set_output_mode(quiet=bool(args.quiet), json_output=(args.output_format == "json"))
|
|
73
|
+
|
|
74
|
+
project_root = detect_project_root(None, args.project_root)
|
|
75
|
+
tool = ListFilesTool(project_root)
|
|
76
|
+
|
|
77
|
+
payload: dict[str, Any] = {
|
|
78
|
+
"roots": list(args.roots),
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Optional mappings
|
|
82
|
+
if args.pattern:
|
|
83
|
+
payload["pattern"] = args.pattern
|
|
84
|
+
if args.glob:
|
|
85
|
+
payload["glob"] = True
|
|
86
|
+
if args.types:
|
|
87
|
+
payload["types"] = args.types
|
|
88
|
+
if args.extensions:
|
|
89
|
+
payload["extensions"] = args.extensions
|
|
90
|
+
if args.exclude:
|
|
91
|
+
payload["exclude"] = args.exclude
|
|
92
|
+
if args.depth is not None:
|
|
93
|
+
payload["depth"] = int(args.depth)
|
|
94
|
+
if args.follow_symlinks:
|
|
95
|
+
payload["follow_symlinks"] = True
|
|
96
|
+
if args.hidden:
|
|
97
|
+
payload["hidden"] = True
|
|
98
|
+
if args.no_ignore:
|
|
99
|
+
payload["no_ignore"] = True
|
|
100
|
+
if args.size:
|
|
101
|
+
payload["size"] = args.size
|
|
102
|
+
if args.changed_within:
|
|
103
|
+
payload["changed_within"] = args.changed_within
|
|
104
|
+
if args.changed_before:
|
|
105
|
+
payload["changed_before"] = args.changed_before
|
|
106
|
+
if args.full_path_match:
|
|
107
|
+
payload["full_path_match"] = True
|
|
108
|
+
if args.limit is not None:
|
|
109
|
+
payload["limit"] = int(args.limit)
|
|
110
|
+
if args.count_only:
|
|
111
|
+
payload["count_only"] = True
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
result = await tool.execute(payload)
|
|
115
|
+
output_data(result, args.output_format)
|
|
116
|
+
return 0 if (isinstance(result, dict) and result.get("success", True)) else 0
|
|
117
|
+
except Exception as e:
|
|
118
|
+
output_error(str(e))
|
|
119
|
+
return 1
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main() -> None:
|
|
123
|
+
parser = _build_parser()
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
try:
|
|
126
|
+
rc = asyncio.run(_run(args))
|
|
127
|
+
except KeyboardInterrupt:
|
|
128
|
+
rc = 1
|
|
129
|
+
sys.exit(rc)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
main()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Partial Read Command
|
|
4
|
+
|
|
5
|
+
Handles partial file reading functionality, extracting specified line ranges.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from ...file_handler import read_file_partial
|
|
11
|
+
from ...output_manager import output_data, output_json, output_section
|
|
12
|
+
from .base_command import BaseCommand
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PartialReadCommand(BaseCommand):
|
|
19
|
+
"""Command for reading partial file content by line range."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, args: Any) -> None:
|
|
22
|
+
"""Initialize with arguments but skip base class analysis engine setup."""
|
|
23
|
+
self.args = args
|
|
24
|
+
# Don't call super().__init__() to avoid unnecessary analysis engine setup
|
|
25
|
+
|
|
26
|
+
def validate_file(self) -> bool:
|
|
27
|
+
"""Validate input file exists and is accessible."""
|
|
28
|
+
if not hasattr(self.args, "file_path") or not self.args.file_path:
|
|
29
|
+
from ...output_manager import output_error
|
|
30
|
+
|
|
31
|
+
output_error("File path not specified.")
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
if not Path(self.args.file_path).exists():
|
|
37
|
+
from ...output_manager import output_error
|
|
38
|
+
|
|
39
|
+
output_error(f"File not found: {self.args.file_path}")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def execute(self) -> int:
|
|
45
|
+
"""
|
|
46
|
+
Execute partial read command.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
int: Exit code (0 for success, 1 for failure)
|
|
50
|
+
"""
|
|
51
|
+
# Validate inputs
|
|
52
|
+
if not self.validate_file():
|
|
53
|
+
return 1
|
|
54
|
+
|
|
55
|
+
# Validate partial read arguments
|
|
56
|
+
if not self.args.start_line:
|
|
57
|
+
from ...output_manager import output_error
|
|
58
|
+
|
|
59
|
+
output_error("--start-line is required")
|
|
60
|
+
return 1
|
|
61
|
+
|
|
62
|
+
if self.args.start_line < 1:
|
|
63
|
+
from ...output_manager import output_error
|
|
64
|
+
|
|
65
|
+
output_error("--start-line must be 1 or greater")
|
|
66
|
+
return 1
|
|
67
|
+
|
|
68
|
+
if self.args.end_line and self.args.end_line < self.args.start_line:
|
|
69
|
+
from ...output_manager import output_error
|
|
70
|
+
|
|
71
|
+
output_error("--end-line must be greater than or equal to --start-line")
|
|
72
|
+
return 1
|
|
73
|
+
|
|
74
|
+
# Read partial content
|
|
75
|
+
try:
|
|
76
|
+
partial_content = read_file_partial(
|
|
77
|
+
self.args.file_path,
|
|
78
|
+
start_line=self.args.start_line,
|
|
79
|
+
end_line=getattr(self.args, "end_line", None),
|
|
80
|
+
start_column=getattr(self.args, "start_column", None),
|
|
81
|
+
end_column=getattr(self.args, "end_column", None),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if partial_content is None:
|
|
85
|
+
from ...output_manager import output_error
|
|
86
|
+
|
|
87
|
+
output_error("Failed to read file partially")
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
# Output the result
|
|
91
|
+
self._output_partial_content(partial_content)
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
from ...output_manager import output_error
|
|
96
|
+
|
|
97
|
+
output_error(f"Failed to read file partially: {e}")
|
|
98
|
+
return 1
|
|
99
|
+
|
|
100
|
+
def _output_partial_content(self, content: str) -> None:
|
|
101
|
+
"""Output the partial content in the specified format."""
|
|
102
|
+
# Build result data
|
|
103
|
+
result_data = {
|
|
104
|
+
"file_path": self.args.file_path,
|
|
105
|
+
"range": {
|
|
106
|
+
"start_line": self.args.start_line,
|
|
107
|
+
"end_line": getattr(self.args, "end_line", None),
|
|
108
|
+
"start_column": getattr(self.args, "start_column", None),
|
|
109
|
+
"end_column": getattr(self.args, "end_column", None),
|
|
110
|
+
},
|
|
111
|
+
"content": content,
|
|
112
|
+
"content_length": len(content),
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Build range info for header
|
|
116
|
+
range_info = f"Line {self.args.start_line}"
|
|
117
|
+
if hasattr(self.args, "end_line") and self.args.end_line:
|
|
118
|
+
range_info += f"-{self.args.end_line}"
|
|
119
|
+
|
|
120
|
+
# Output format selection
|
|
121
|
+
output_format = getattr(self.args, "output_format", "text")
|
|
122
|
+
|
|
123
|
+
if output_format == "json":
|
|
124
|
+
# Pure JSON output
|
|
125
|
+
output_json(result_data)
|
|
126
|
+
else:
|
|
127
|
+
# Human-readable format with header
|
|
128
|
+
output_section("Partial Read Result")
|
|
129
|
+
output_data(f"File: {self.args.file_path}")
|
|
130
|
+
output_data(f"Range: {range_info}")
|
|
131
|
+
output_data(f"Characters read: {len(content)}")
|
|
132
|
+
output_data("") # Empty line for separation
|
|
133
|
+
|
|
134
|
+
# Output the actual content
|
|
135
|
+
print(content, end="") # Use print to avoid extra formatting
|
|
136
|
+
|
|
137
|
+
async def execute_async(self, language: str) -> int:
|
|
138
|
+
"""Not used for partial read command."""
|
|
139
|
+
return self.execute()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Query Command
|
|
4
|
+
|
|
5
|
+
Handles query execution functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from ...core.query_service import QueryService
|
|
11
|
+
from ...output_manager import output_data, output_error, output_info, output_json
|
|
12
|
+
from .base_command import BaseCommand
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class QueryCommand(BaseCommand):
|
|
16
|
+
"""Command for executing queries."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, args: Any) -> None:
|
|
19
|
+
"""Initialize the query command with QueryService."""
|
|
20
|
+
super().__init__(args)
|
|
21
|
+
self.query_service = QueryService()
|
|
22
|
+
|
|
23
|
+
async def execute_query(
|
|
24
|
+
self, language: str, query: str, query_name: str = "custom"
|
|
25
|
+
) -> list[dict] | None:
|
|
26
|
+
"""Execute a specific tree-sitter query using QueryService."""
|
|
27
|
+
try:
|
|
28
|
+
# Get filter expression if provided
|
|
29
|
+
filter_expression = getattr(self.args, "filter", None)
|
|
30
|
+
|
|
31
|
+
if query_name != "custom":
|
|
32
|
+
# Use predefined query key
|
|
33
|
+
results = await self.query_service.execute_query(
|
|
34
|
+
self.args.file_path,
|
|
35
|
+
language,
|
|
36
|
+
query_key=query_name,
|
|
37
|
+
filter_expression=filter_expression,
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
# Use custom query string
|
|
41
|
+
results = await self.query_service.execute_query(
|
|
42
|
+
self.args.file_path,
|
|
43
|
+
language,
|
|
44
|
+
query_string=query,
|
|
45
|
+
filter_expression=filter_expression,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return results
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
output_error(f"Query execution failed: {e}")
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
async def execute_async(self, language: str) -> int:
|
|
55
|
+
# Get the query to execute
|
|
56
|
+
query_to_execute = None
|
|
57
|
+
|
|
58
|
+
if hasattr(self.args, "query_key") and self.args.query_key:
|
|
59
|
+
# Sanitize query key input
|
|
60
|
+
sanitized_query_key = self.security_validator.sanitize_input(
|
|
61
|
+
self.args.query_key, max_length=100
|
|
62
|
+
)
|
|
63
|
+
# Check if query exists
|
|
64
|
+
available_queries = self.query_service.get_available_queries(language)
|
|
65
|
+
if sanitized_query_key not in available_queries:
|
|
66
|
+
output_error(
|
|
67
|
+
f"Query '{sanitized_query_key}' not found for language '{language}'"
|
|
68
|
+
)
|
|
69
|
+
return 1
|
|
70
|
+
# Store query name - QueryService will resolve the query string
|
|
71
|
+
query_to_execute = sanitized_query_key # This is actually the query key now
|
|
72
|
+
query_name = sanitized_query_key
|
|
73
|
+
elif hasattr(self.args, "query_string") and self.args.query_string:
|
|
74
|
+
# Security check for query string (potential regex patterns)
|
|
75
|
+
is_safe, error_msg = self.security_validator.regex_checker.validate_pattern(
|
|
76
|
+
self.args.query_string
|
|
77
|
+
)
|
|
78
|
+
if not is_safe:
|
|
79
|
+
output_error(f"Unsafe query pattern: {error_msg}")
|
|
80
|
+
return 1
|
|
81
|
+
query_to_execute = self.args.query_string
|
|
82
|
+
query_name = "custom"
|
|
83
|
+
|
|
84
|
+
if not query_to_execute:
|
|
85
|
+
output_error("No query specified.")
|
|
86
|
+
return 1
|
|
87
|
+
|
|
88
|
+
# Execute specific query
|
|
89
|
+
results = await self.execute_query(language, query_to_execute, query_name)
|
|
90
|
+
if results is None:
|
|
91
|
+
return 1
|
|
92
|
+
|
|
93
|
+
# Output results
|
|
94
|
+
if results:
|
|
95
|
+
if self.args.output_format == "json":
|
|
96
|
+
output_json(results)
|
|
97
|
+
else:
|
|
98
|
+
for i, query_result in enumerate(results, 1):
|
|
99
|
+
output_data(
|
|
100
|
+
f"\n{i}. {query_result['capture_name']} ({query_result['node_type']})"
|
|
101
|
+
)
|
|
102
|
+
output_data(
|
|
103
|
+
f" Position: Line {query_result['start_line']}-{query_result['end_line']}"
|
|
104
|
+
)
|
|
105
|
+
output_data(f" Content:\n{query_result['content']}")
|
|
106
|
+
else:
|
|
107
|
+
output_info("\nINFO: No results found matching the query.")
|
|
108
|
+
|
|
109
|
+
return 0
|