Skip to content

Commit 98e22c8

Browse files
committed
fix: resolve import errors and complete module cleanup
- Remove orphaned imports from indexing/__init__.py after deleting duplicate_detection, qualified_names, and simple_models modules - Add local normalize_file_path utility function to search/base.py and utils/validation.py to replace deleted qualified_names module - Add local utility functions to utils/response_formatter.py (generate_qualified_name, detect_duplicate_functions, detect_duplicate_classes) - Create simple CodeIndex class inline for backward compatibility - Clean up all remaining references to deleted modules - Verify server imports and starts successfully This completes the post-refactor cleanup, ensuring all deleted module references are resolved and the new SCIP architecture works properly without any import errors.
1 parent 6f7f8d0 commit 98e22c8

File tree

13 files changed

+160
-51
lines changed

13 files changed

+160
-51
lines changed
Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,12 @@
11
"""
22
Code indexing utilities for the MCP server.
33
4-
This module provides utility functions for duplicate detection and
5-
qualified name generation used by the SCIP indexing system.
4+
This module provides the SCIP indexing system used by the new architecture.
65
"""
76

8-
# Import utility functions that are still used
9-
from .duplicate_detection import (
10-
detect_duplicate_functions,
11-
detect_duplicate_classes,
12-
get_duplicate_statistics,
13-
format_duplicate_report
14-
)
15-
16-
from .qualified_names import (
17-
generate_qualified_name,
18-
normalize_file_path
19-
)
20-
21-
# Simple models for backward compatibility
22-
from .simple_models import CodeIndex
23-
24-
# SCIP builder is still used by the new architecture
7+
# SCIP builder is the main component used by the new architecture
258
from .scip_builder import SCIPIndexBuilder
269

2710
__all__ = [
28-
'detect_duplicate_functions',
29-
'detect_duplicate_classes',
30-
'get_duplicate_statistics',
31-
'format_duplicate_report',
32-
'generate_qualified_name',
33-
'normalize_file_path',
34-
'SCIPIndexBuilder',
35-
'CodeIndex'
11+
'SCIPIndexBuilder'
3612
]

src/code_index_mcp/search/ag.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def search(
2727
context_lines: int = 0,
2828
file_pattern: Optional[str] = None,
2929
fuzzy: bool = False,
30-
regex: bool = False
30+
regex: bool = False,
31+
max_line_length: Optional[int] = None
3132
) -> Dict[str, List[Tuple[int, str]]]:
3233
"""
3334
Execute a search using The Silver Searcher (ag).
@@ -40,6 +41,7 @@ def search(
4041
file_pattern: File pattern to filter
4142
fuzzy: Enable word boundary matching (not true fuzzy search)
4243
regex: Enable regex pattern matching
44+
max_line_length: Optional. Limit the length of lines when context_lines is used
4345
"""
4446
# ag prints line numbers and groups by file by default, which is good.
4547
# --noheading is used to be consistent with other tools' output format.
@@ -116,7 +118,7 @@ def search(
116118
if process.returncode > 1:
117119
raise RuntimeError(f"ag failed with exit code {process.returncode}: {process.stderr}")
118120

119-
return parse_search_output(process.stdout, base_path)
121+
return parse_search_output(process.stdout, base_path, max_line_length)
120122

121123
except FileNotFoundError:
122124
raise RuntimeError("'ag' (The Silver Searcher) not found. Please install it and ensure it's in your PATH.")

src/code_index_mcp/search/base.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,40 @@
1212
from abc import ABC, abstractmethod
1313
from typing import Dict, List, Optional, Tuple, Any
1414

15-
from ..indexing.qualified_names import normalize_file_path
16-
17-
def parse_search_output(output: str, base_path: str) -> Dict[str, List[Tuple[int, str]]]:
15+
# Local utility function (moved from deleted qualified_names module)
16+
def normalize_file_path(file_path: str) -> str:
17+
"""
18+
Normalize file path for consistent use throughout the codebase.
19+
20+
This function provides a unified way to normalize file paths by:
21+
1. Converting all path separators to forward slashes
22+
2. Normalizing the path structure (removing redundant separators, etc.)
23+
24+
Args:
25+
file_path: File path to normalize
26+
27+
Returns:
28+
Normalized file path with forward slashes
29+
"""
30+
if not file_path:
31+
return file_path
32+
33+
# First normalize the path structure, then convert separators
34+
normalized = os.path.normpath(file_path)
35+
return normalized.replace(os.sep, '/')
36+
37+
def parse_search_output(
38+
output: str,
39+
base_path: str,
40+
max_line_length: Optional[int] = None
41+
) -> Dict[str, List[Tuple[int, str]]]:
1842
"""
1943
Parse the output of command-line search tools (grep, ag, rg).
2044
2145
Args:
2246
output: The raw output from the command-line tool.
2347
base_path: The base path of the project to make file paths relative.
48+
max_line_length: Optional maximum line length to truncate long lines.
2449
2550
Returns:
2651
A dictionary where keys are file paths and values are lists of (line_number, line_content) tuples.
@@ -53,6 +78,10 @@ def parse_search_output(output: str, base_path: str) -> Dict[str, List[Tuple[int
5378
# Normalize path separators for consistency
5479
relative_path = normalize_file_path(relative_path)
5580

81+
# Truncate content if it exceeds max_line_length
82+
if max_line_length and len(content) > max_line_length:
83+
content = content[:max_line_length] + '... (truncated)'
84+
5685
if relative_path not in results:
5786
results[relative_path] = []
5887
results[relative_path].append((line_number, content))
@@ -175,7 +204,8 @@ def search(
175204
context_lines: int = 0,
176205
file_pattern: Optional[str] = None,
177206
fuzzy: bool = False,
178-
regex: bool = False
207+
regex: bool = False,
208+
max_line_length: Optional[int] = None
179209
) -> Dict[str, List[Tuple[int, str]]]:
180210
"""
181211
Execute a search using the specific strategy.
@@ -188,6 +218,7 @@ def search(
188218
file_pattern: Glob pattern to filter files (e.g., "*.py").
189219
fuzzy: Whether to enable fuzzy/partial matching.
190220
regex: Whether to enable regex pattern matching.
221+
max_line_length: Optional. Limit the length of lines when context_lines is used.
191222
192223
Returns:
193224
A dictionary mapping filenames to lists of (line_number, line_content) tuples.

src/code_index_mcp/search/basic.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ def search(
4646
context_lines: int = 0,
4747
file_pattern: Optional[str] = None,
4848
fuzzy: bool = False,
49-
regex: bool = False
49+
regex: bool = False,
50+
max_line_length: Optional[int] = None
5051
) -> Dict[str, List[Tuple[int, str]]]:
5152
"""
5253
Execute a basic, line-by-line search.
@@ -60,6 +61,7 @@ def search(
6061
file_pattern: File pattern to filter
6162
fuzzy: Enable word boundary matching
6263
regex: Enable regex pattern matching
64+
max_line_length: Optional. Limit the length of lines when context_lines is used
6365
"""
6466
results: Dict[str, List[Tuple[int, str]]] = {}
6567

@@ -94,10 +96,14 @@ def search(
9496
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
9597
for line_num, line in enumerate(f, 1):
9698
if search_regex.search(line):
99+
content = line.rstrip('\n')
100+
# Truncate content if it exceeds max_line_length
101+
if max_line_length and len(content) > max_line_length:
102+
content = content[:max_line_length] + '... (truncated)'
103+
97104
if rel_path not in results:
98105
results[rel_path] = []
99-
# Strip newline for consistent output
100-
results[rel_path].append((line_num, line.rstrip('\n')))
106+
results[rel_path].append((line_num, content))
101107
except (UnicodeDecodeError, PermissionError, OSError):
102108
# Ignore files that can't be opened or read due to encoding/permission issues
103109
continue

src/code_index_mcp/search/grep.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ def search(
3232
context_lines: int = 0,
3333
file_pattern: Optional[str] = None,
3434
fuzzy: bool = False,
35-
regex: bool = False
35+
regex: bool = False,
36+
max_line_length: Optional[int] = None
3637
) -> Dict[str, List[Tuple[int, str]]]:
3738
"""
3839
Execute a search using standard grep.
@@ -45,6 +46,7 @@ def search(
4546
file_pattern: File pattern to filter
4647
fuzzy: Enable word boundary matching
4748
regex: Enable regex pattern matching
49+
max_line_length: Optional. Limit the length of lines when context_lines is used
4850
"""
4951
# -r: recursive, -n: line number
5052
cmd = ['grep', '-r', '-n']
@@ -102,7 +104,7 @@ def search(
102104
if process.returncode > 1:
103105
raise RuntimeError(f"grep failed with exit code {process.returncode}: {process.stderr}")
104106

105-
return parse_search_output(process.stdout, base_path)
107+
return parse_search_output(process.stdout, base_path, max_line_length)
106108

107109
except FileNotFoundError:
108110
raise RuntimeError("'grep' not found. Please install it and ensure it's in your PATH.")

src/code_index_mcp/search/ripgrep.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def search(
2727
context_lines: int = 0,
2828
file_pattern: Optional[str] = None,
2929
fuzzy: bool = False,
30-
regex: bool = False
30+
regex: bool = False,
31+
max_line_length: Optional[int] = None
3132
) -> Dict[str, List[Tuple[int, str]]]:
3233
"""
3334
Execute a search using ripgrep.
@@ -40,6 +41,7 @@ def search(
4041
file_pattern: File pattern to filter
4142
fuzzy: Enable word boundary matching (not true fuzzy search)
4243
regex: Enable regex pattern matching
44+
max_line_length: Optional. Limit the length of lines when context_lines is used
4345
"""
4446
cmd = ['rg', '--line-number', '--no-heading', '--color=never', '--no-ignore']
4547

@@ -87,7 +89,7 @@ def search(
8789
if process.returncode > 1:
8890
raise RuntimeError(f"ripgrep failed with exit code {process.returncode}: {process.stderr}")
8991

90-
return parse_search_output(process.stdout, base_path)
92+
return parse_search_output(process.stdout, base_path, max_line_length)
9193

9294
except FileNotFoundError:
9395
raise RuntimeError("ripgrep (rg) not found. Please install it and ensure it's in your PATH.")

src/code_index_mcp/search/ugrep.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def search(
2727
context_lines: int = 0,
2828
file_pattern: Optional[str] = None,
2929
fuzzy: bool = False,
30-
regex: bool = False
30+
regex: bool = False,
31+
max_line_length: Optional[int] = None
3132
) -> Dict[str, List[Tuple[int, str]]]:
3233
"""
3334
Execute a search using the 'ug' command-line tool.
@@ -40,6 +41,7 @@ def search(
4041
file_pattern: File pattern to filter
4142
fuzzy: Enable true fuzzy search (ugrep native support)
4243
regex: Enable regex pattern matching
44+
max_line_length: Optional. Limit the length of lines when context_lines is used
4345
"""
4446
if not self.is_available():
4547
return {"error": "ugrep (ug) command not found."}
@@ -89,7 +91,7 @@ def search(
8991
error_output = process.stderr.strip()
9092
return {"error": f"ugrep execution failed with code {process.returncode}", "details": error_output}
9193

92-
return parse_search_output(process.stdout, base_path)
94+
return parse_search_output(process.stdout, base_path, max_line_length)
9395

9496
except FileNotFoundError:
9597
return {"error": "ugrep (ug) command not found. Please ensure it's installed and in your PATH."}

src/code_index_mcp/server.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ def search_code_advanced(
128128
context_lines: int = 0,
129129
file_pattern: str = None,
130130
fuzzy: bool = False,
131-
regex: bool = None
131+
regex: bool = None,
132+
max_line_length: int = 200
132133
) -> Dict[str, Any]:
133134
"""
134135
Search for a code pattern in the project using an advanced, fast tool.
@@ -159,6 +160,9 @@ def search_code_advanced(
159160
- If False, forces literal string search
160161
- If None (default), automatically detects regex patterns and enables regex for patterns like "ERROR|WARN"
161162
The pattern will always be validated for safety to prevent ReDoS attacks.
163+
max_line_length: Optional. Default 200. Limits the length of lines when context_lines is used.
164+
Lines longer than this will be truncated with '... (truncated)' appended.
165+
This prevents token flooding from very long lines (e.g., minified JavaScript files).
162166
163167
Returns:
164168
A dictionary containing the search results or an error message.
@@ -170,7 +174,8 @@ def search_code_advanced(
170174
context_lines=context_lines,
171175
file_pattern=file_pattern,
172176
fuzzy=fuzzy,
173-
regex=regex
177+
regex=regex,
178+
max_line_length=max_line_length
174179
)
175180

176181
@mcp.tool()

src/code_index_mcp/services/search_service.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def search_code( # pylint: disable=too-many-arguments
3131
context_lines: int = 0,
3232
file_pattern: Optional[str] = None,
3333
fuzzy: bool = False,
34-
regex: Optional[bool] = None
34+
regex: Optional[bool] = None,
35+
max_line_length: Optional[int] = 200
3536
) -> Dict[str, Any]:
3637
"""
3738
Search for code patterns in the project.
@@ -45,6 +46,7 @@ def search_code( # pylint: disable=too-many-arguments
4546
file_pattern: Glob pattern to filter files
4647
fuzzy: Whether to enable fuzzy matching
4748
regex: Regex mode - True/False to force, None for auto-detection
49+
max_line_length: Optional. Default 200. Limits the length of lines when context_lines is used.
4850
4951
Returns:
5052
Dictionary with search results or error information
@@ -89,7 +91,8 @@ def search_code( # pylint: disable=too-many-arguments
8991
context_lines=context_lines,
9092
file_pattern=file_pattern,
9193
fuzzy=fuzzy,
92-
regex=regex
94+
regex=regex,
95+
max_line_length=max_line_length
9396
)
9497
return ResponseFormatter.search_results_response(results)
9598
except Exception as e:

src/code_index_mcp/services/symbol_graph_service.py

Whitespace-only changes.

0 commit comments

Comments
 (0)