mcp-code-indexer 3.1.4__py3-none-any.whl → 3.1.5__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.
- mcp_code_indexer/__init__.py +8 -6
- mcp_code_indexer/ask_handler.py +105 -75
- mcp_code_indexer/claude_api_handler.py +125 -82
- mcp_code_indexer/cleanup_manager.py +107 -81
- mcp_code_indexer/database/connection_health.py +212 -161
- mcp_code_indexer/database/database.py +529 -415
- mcp_code_indexer/database/exceptions.py +167 -118
- mcp_code_indexer/database/models.py +54 -19
- mcp_code_indexer/database/retry_executor.py +139 -103
- mcp_code_indexer/deepask_handler.py +178 -140
- mcp_code_indexer/error_handler.py +88 -76
- mcp_code_indexer/file_scanner.py +163 -141
- mcp_code_indexer/git_hook_handler.py +352 -261
- mcp_code_indexer/logging_config.py +76 -94
- mcp_code_indexer/main.py +406 -320
- mcp_code_indexer/middleware/error_middleware.py +106 -71
- mcp_code_indexer/query_preprocessor.py +40 -40
- mcp_code_indexer/server/mcp_server.py +785 -470
- mcp_code_indexer/token_counter.py +54 -47
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/METADATA +3 -3
- mcp_code_indexer-3.1.5.dist-info/RECORD +37 -0
- mcp_code_indexer-3.1.4.dist-info/RECORD +0 -37
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/top_level.txt +0 -0
@@ -12,15 +12,19 @@ from typing import Any, Dict, Optional
|
|
12
12
|
|
13
13
|
class DatabaseError(Exception):
|
14
14
|
"""Base exception for all database-related errors."""
|
15
|
-
|
16
|
-
def __init__(
|
17
|
-
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
message: str,
|
19
|
+
operation_name: str = "",
|
20
|
+
error_context: Optional[Dict[str, Any]] = None,
|
21
|
+
):
|
18
22
|
self.message = message
|
19
23
|
self.operation_name = operation_name
|
20
24
|
self.error_context = error_context or {}
|
21
25
|
self.timestamp = datetime.now(timezone.utc)
|
22
26
|
super().__init__(f"{operation_name}: {message}" if operation_name else message)
|
23
|
-
|
27
|
+
|
24
28
|
def to_dict(self) -> Dict[str, Any]:
|
25
29
|
"""Convert exception to dictionary for structured logging."""
|
26
30
|
return {
|
@@ -28,240 +32,283 @@ class DatabaseError(Exception):
|
|
28
32
|
"message": self.message,
|
29
33
|
"operation_name": self.operation_name,
|
30
34
|
"timestamp": self.timestamp.isoformat(),
|
31
|
-
"error_context": self.error_context
|
35
|
+
"error_context": self.error_context,
|
32
36
|
}
|
33
37
|
|
34
38
|
|
35
39
|
class DatabaseLockError(DatabaseError):
|
36
40
|
"""Exception for SQLite database locking issues that are retryable."""
|
37
|
-
|
38
|
-
def __init__(
|
39
|
-
|
41
|
+
|
42
|
+
def __init__(
|
43
|
+
self,
|
44
|
+
message: str,
|
45
|
+
retry_count: int = 0,
|
46
|
+
operation_name: str = "",
|
47
|
+
last_attempt: Optional[datetime] = None,
|
48
|
+
lock_type: str = "unknown",
|
49
|
+
):
|
40
50
|
self.retry_count = retry_count
|
41
51
|
self.last_attempt = last_attempt or datetime.now(timezone.utc)
|
42
52
|
self.lock_type = lock_type # 'read', 'write', 'exclusive'
|
43
|
-
|
53
|
+
|
44
54
|
error_context = {
|
45
55
|
"retry_count": retry_count,
|
46
56
|
"last_attempt": self.last_attempt.isoformat(),
|
47
57
|
"lock_type": lock_type,
|
48
|
-
"retryable": True
|
58
|
+
"retryable": True,
|
49
59
|
}
|
50
|
-
|
60
|
+
|
51
61
|
super().__init__(message, operation_name, error_context)
|
52
62
|
|
53
63
|
|
54
64
|
class DatabaseBusyError(DatabaseError):
|
55
65
|
"""Exception for SQLite database busy errors that are retryable."""
|
56
|
-
|
57
|
-
def __init__(
|
58
|
-
|
66
|
+
|
67
|
+
def __init__(
|
68
|
+
self,
|
69
|
+
message: str,
|
70
|
+
operation_name: str = "",
|
71
|
+
busy_timeout: float = 0.0,
|
72
|
+
resource_type: str = "connection",
|
73
|
+
):
|
59
74
|
self.busy_timeout = busy_timeout
|
60
75
|
self.resource_type = resource_type # 'connection', 'transaction', 'table'
|
61
|
-
|
76
|
+
|
62
77
|
error_context = {
|
63
78
|
"busy_timeout": busy_timeout,
|
64
79
|
"resource_type": resource_type,
|
65
|
-
"retryable": True
|
80
|
+
"retryable": True,
|
66
81
|
}
|
67
|
-
|
82
|
+
|
68
83
|
super().__init__(message, operation_name, error_context)
|
69
84
|
|
70
85
|
|
71
86
|
class DatabaseConnectionError(DatabaseError):
|
72
87
|
"""Exception for database connection issues."""
|
73
|
-
|
74
|
-
def __init__(
|
75
|
-
|
88
|
+
|
89
|
+
def __init__(
|
90
|
+
self,
|
91
|
+
message: str,
|
92
|
+
operation_name: str = "",
|
93
|
+
connection_info: Optional[Dict[str, Any]] = None,
|
94
|
+
):
|
76
95
|
self.connection_info = connection_info or {}
|
77
|
-
|
96
|
+
|
78
97
|
error_context = {
|
79
98
|
"connection_info": self.connection_info,
|
80
|
-
"retryable": False # Connection errors usually indicate config issues
|
99
|
+
"retryable": False, # Connection errors usually indicate config issues
|
81
100
|
}
|
82
|
-
|
101
|
+
|
83
102
|
super().__init__(message, operation_name, error_context)
|
84
103
|
|
85
104
|
|
86
105
|
class DatabaseSchemaError(DatabaseError):
|
87
106
|
"""Exception for database schema-related errors."""
|
88
|
-
|
89
|
-
def __init__(
|
90
|
-
|
107
|
+
|
108
|
+
def __init__(
|
109
|
+
self,
|
110
|
+
message: str,
|
111
|
+
operation_name: str = "",
|
112
|
+
schema_version: Optional[str] = None,
|
113
|
+
migration_info: Optional[Dict] = None,
|
114
|
+
):
|
91
115
|
self.schema_version = schema_version
|
92
116
|
self.migration_info = migration_info or {}
|
93
|
-
|
117
|
+
|
94
118
|
error_context = {
|
95
119
|
"schema_version": schema_version,
|
96
120
|
"migration_info": self.migration_info,
|
97
|
-
"retryable": False # Schema errors require manual intervention
|
121
|
+
"retryable": False, # Schema errors require manual intervention
|
98
122
|
}
|
99
|
-
|
123
|
+
|
100
124
|
super().__init__(message, operation_name, error_context)
|
101
125
|
|
102
126
|
|
103
127
|
class DatabaseIntegrityError(DatabaseError):
|
104
128
|
"""Exception for database integrity constraint violations."""
|
105
|
-
|
106
|
-
def __init__(
|
107
|
-
|
108
|
-
|
129
|
+
|
130
|
+
def __init__(
|
131
|
+
self,
|
132
|
+
message: str,
|
133
|
+
operation_name: str = "",
|
134
|
+
constraint_type: str = "unknown",
|
135
|
+
affected_table: str = "",
|
136
|
+
):
|
137
|
+
self.constraint_type = (
|
138
|
+
constraint_type # 'primary_key', 'foreign_key', 'unique', 'check'
|
139
|
+
)
|
109
140
|
self.affected_table = affected_table
|
110
|
-
|
141
|
+
|
111
142
|
error_context = {
|
112
143
|
"constraint_type": constraint_type,
|
113
144
|
"affected_table": affected_table,
|
114
|
-
"retryable": False # Integrity errors indicate data issues
|
145
|
+
"retryable": False, # Integrity errors indicate data issues
|
115
146
|
}
|
116
|
-
|
147
|
+
|
117
148
|
super().__init__(message, operation_name, error_context)
|
118
149
|
|
119
150
|
|
120
151
|
class DatabaseTimeoutError(DatabaseError):
|
121
152
|
"""Exception for database operation timeouts."""
|
122
|
-
|
123
|
-
def __init__(
|
124
|
-
|
153
|
+
|
154
|
+
def __init__(
|
155
|
+
self,
|
156
|
+
message: str,
|
157
|
+
operation_name: str = "",
|
158
|
+
timeout_seconds: float = 0.0,
|
159
|
+
operation_type: str = "unknown",
|
160
|
+
):
|
125
161
|
self.timeout_seconds = timeout_seconds
|
126
162
|
self.operation_type = operation_type # 'read', 'write', 'transaction'
|
127
|
-
|
163
|
+
|
128
164
|
error_context = {
|
129
165
|
"timeout_seconds": timeout_seconds,
|
130
166
|
"operation_type": operation_type,
|
131
|
-
"retryable": True # Timeouts might be transient
|
167
|
+
"retryable": True, # Timeouts might be transient
|
132
168
|
}
|
133
|
-
|
169
|
+
|
134
170
|
super().__init__(message, operation_name, error_context)
|
135
171
|
|
136
172
|
|
137
173
|
def classify_sqlite_error(error: Exception, operation_name: str = "") -> DatabaseError:
|
138
174
|
"""
|
139
175
|
Classify a raw SQLite error into our structured exception hierarchy.
|
140
|
-
|
176
|
+
|
141
177
|
Args:
|
142
178
|
error: Original exception from SQLite
|
143
179
|
operation_name: Name of the operation that failed
|
144
|
-
|
180
|
+
|
145
181
|
Returns:
|
146
182
|
Appropriate DatabaseError subclass with context
|
147
183
|
"""
|
148
184
|
error_message = str(error).lower()
|
149
185
|
original_message = str(error)
|
150
|
-
|
186
|
+
|
151
187
|
# Database locking errors
|
152
|
-
if any(
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
188
|
+
if any(
|
189
|
+
msg in error_message
|
190
|
+
for msg in [
|
191
|
+
"database is locked",
|
192
|
+
"sqlite_locked",
|
193
|
+
"attempt to write a readonly database",
|
194
|
+
]
|
195
|
+
):
|
196
|
+
lock_type = (
|
197
|
+
"write"
|
198
|
+
if "write" in error_message or "readonly" in error_message
|
199
|
+
else "read"
|
200
|
+
)
|
158
201
|
return DatabaseLockError(
|
159
|
-
original_message,
|
160
|
-
operation_name=operation_name,
|
161
|
-
lock_type=lock_type
|
202
|
+
original_message, operation_name=operation_name, lock_type=lock_type
|
162
203
|
)
|
163
|
-
|
204
|
+
|
164
205
|
# Database busy errors
|
165
|
-
if any(
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
206
|
+
if any(
|
207
|
+
msg in error_message
|
208
|
+
for msg in [
|
209
|
+
"database is busy",
|
210
|
+
"sqlite_busy",
|
211
|
+
"cannot start a transaction within a transaction",
|
212
|
+
]
|
213
|
+
):
|
214
|
+
resource_type = (
|
215
|
+
"transaction" if "transaction" in error_message else "connection"
|
216
|
+
)
|
171
217
|
return DatabaseBusyError(
|
172
|
-
original_message,
|
173
|
-
operation_name=operation_name,
|
174
|
-
resource_type=resource_type
|
218
|
+
original_message, operation_name=operation_name, resource_type=resource_type
|
175
219
|
)
|
176
|
-
|
220
|
+
|
177
221
|
# Connection errors
|
178
|
-
if any(
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
)
|
188
|
-
|
222
|
+
if any(
|
223
|
+
msg in error_message
|
224
|
+
for msg in [
|
225
|
+
"unable to open database",
|
226
|
+
"disk i/o error",
|
227
|
+
"database disk image is malformed",
|
228
|
+
"no such database",
|
229
|
+
]
|
230
|
+
):
|
231
|
+
return DatabaseConnectionError(original_message, operation_name=operation_name)
|
232
|
+
|
189
233
|
# Schema errors
|
190
|
-
if any(
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
)
|
200
|
-
|
234
|
+
if any(
|
235
|
+
msg in error_message
|
236
|
+
for msg in [
|
237
|
+
"no such table",
|
238
|
+
"no such column",
|
239
|
+
"table already exists",
|
240
|
+
"syntax error",
|
241
|
+
]
|
242
|
+
):
|
243
|
+
return DatabaseSchemaError(original_message, operation_name=operation_name)
|
244
|
+
|
201
245
|
# Integrity constraint errors
|
202
|
-
if any(
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
246
|
+
if any(
|
247
|
+
msg in error_message
|
248
|
+
for msg in [
|
249
|
+
"unique constraint failed",
|
250
|
+
"foreign key constraint failed",
|
251
|
+
"primary key constraint failed",
|
252
|
+
"check constraint failed",
|
253
|
+
]
|
254
|
+
):
|
208
255
|
constraint_type = "unknown"
|
209
256
|
if "unique" in error_message:
|
210
257
|
constraint_type = "unique"
|
211
258
|
elif "foreign key" in error_message:
|
212
|
-
constraint_type = "foreign_key"
|
259
|
+
constraint_type = "foreign_key"
|
213
260
|
elif "primary key" in error_message:
|
214
261
|
constraint_type = "primary_key"
|
215
262
|
elif "check" in error_message:
|
216
263
|
constraint_type = "check"
|
217
|
-
|
264
|
+
|
218
265
|
return DatabaseIntegrityError(
|
219
266
|
original_message,
|
220
267
|
operation_name=operation_name,
|
221
|
-
constraint_type=constraint_type
|
268
|
+
constraint_type=constraint_type,
|
222
269
|
)
|
223
|
-
|
270
|
+
|
224
271
|
# Default to generic database error
|
225
272
|
return DatabaseError(
|
226
273
|
original_message,
|
227
274
|
operation_name=operation_name,
|
228
|
-
error_context={"original_error_type": type(error).__name__}
|
275
|
+
error_context={"original_error_type": type(error).__name__},
|
229
276
|
)
|
230
277
|
|
231
278
|
|
232
279
|
def is_retryable_error(error: Exception) -> bool:
|
233
280
|
"""
|
234
281
|
Determine if an error is retryable based on our classification.
|
235
|
-
|
282
|
+
|
236
283
|
Args:
|
237
284
|
error: Exception to check
|
238
|
-
|
285
|
+
|
239
286
|
Returns:
|
240
287
|
True if the error should trigger a retry
|
241
288
|
"""
|
242
289
|
if isinstance(error, DatabaseError):
|
243
290
|
return error.error_context.get("retryable", False)
|
244
|
-
|
291
|
+
|
245
292
|
# For raw exceptions, use simple classification
|
246
293
|
error_message = str(error).lower()
|
247
294
|
retryable_patterns = [
|
248
295
|
"database is locked",
|
249
296
|
"database is busy",
|
250
|
-
"sqlite_busy",
|
297
|
+
"sqlite_busy",
|
251
298
|
"sqlite_locked",
|
252
|
-
"cannot start a transaction within a transaction"
|
299
|
+
"cannot start a transaction within a transaction",
|
253
300
|
]
|
254
|
-
|
301
|
+
|
255
302
|
return any(pattern in error_message for pattern in retryable_patterns)
|
256
303
|
|
257
304
|
|
258
305
|
def get_error_classification_stats(errors: list) -> Dict[str, Any]:
|
259
306
|
"""
|
260
307
|
Analyze a list of errors and provide classification statistics.
|
261
|
-
|
308
|
+
|
262
309
|
Args:
|
263
310
|
errors: List of Exception objects to analyze
|
264
|
-
|
311
|
+
|
265
312
|
Returns:
|
266
313
|
Dictionary with error classification statistics
|
267
314
|
"""
|
@@ -270,34 +317,36 @@ def get_error_classification_stats(errors: list) -> Dict[str, Any]:
|
|
270
317
|
"error_types": {},
|
271
318
|
"retryable_count": 0,
|
272
319
|
"non_retryable_count": 0,
|
273
|
-
"most_common_errors": {}
|
320
|
+
"most_common_errors": {},
|
274
321
|
}
|
275
|
-
|
322
|
+
|
276
323
|
error_messages = {}
|
277
|
-
|
324
|
+
|
278
325
|
for error in errors:
|
279
326
|
# Classify error
|
280
|
-
classified =
|
327
|
+
classified = (
|
328
|
+
classify_sqlite_error(error)
|
329
|
+
if not isinstance(error, DatabaseError)
|
330
|
+
else error
|
331
|
+
)
|
281
332
|
error_type = type(classified).__name__
|
282
|
-
|
333
|
+
|
283
334
|
# Count by type
|
284
335
|
stats["error_types"][error_type] = stats["error_types"].get(error_type, 0) + 1
|
285
|
-
|
336
|
+
|
286
337
|
# Count retryable vs non-retryable
|
287
338
|
if is_retryable_error(classified):
|
288
339
|
stats["retryable_count"] += 1
|
289
340
|
else:
|
290
341
|
stats["non_retryable_count"] += 1
|
291
|
-
|
342
|
+
|
292
343
|
# Track common error messages
|
293
344
|
message = str(error)
|
294
345
|
error_messages[message] = error_messages.get(message, 0) + 1
|
295
|
-
|
346
|
+
|
296
347
|
# Find most common error messages
|
297
348
|
stats["most_common_errors"] = sorted(
|
298
|
-
error_messages.items(),
|
299
|
-
key=lambda x: x[1],
|
300
|
-
reverse=True
|
349
|
+
error_messages.items(), key=lambda x: x[1], reverse=True
|
301
350
|
)[:5]
|
302
|
-
|
351
|
+
|
303
352
|
return stats
|
@@ -14,42 +14,57 @@ from pydantic import BaseModel, Field
|
|
14
14
|
class Project(BaseModel):
|
15
15
|
"""
|
16
16
|
Represents a tracked project/repository.
|
17
|
-
|
17
|
+
|
18
18
|
Projects are identified by project name and folder paths,
|
19
19
|
allowing tracking across different local copies without git coupling.
|
20
20
|
"""
|
21
|
+
|
21
22
|
id: str = Field(..., description="Generated unique identifier")
|
22
23
|
name: str = Field(..., description="User-provided project name")
|
23
|
-
aliases: List[str] = Field(
|
24
|
-
|
25
|
-
|
24
|
+
aliases: List[str] = Field(
|
25
|
+
default_factory=list, description="Alternative identifiers"
|
26
|
+
)
|
27
|
+
created: datetime = Field(
|
28
|
+
default_factory=datetime.utcnow, description="Creation timestamp"
|
29
|
+
)
|
30
|
+
last_accessed: datetime = Field(
|
31
|
+
default_factory=datetime.utcnow, description="Last access timestamp"
|
32
|
+
)
|
26
33
|
|
27
34
|
|
28
35
|
class FileDescription(BaseModel):
|
29
36
|
"""
|
30
37
|
Represents a file description within a project.
|
31
|
-
|
38
|
+
|
32
39
|
Stores detailed summaries of file contents including purpose, components,
|
33
40
|
and relationships to enable efficient codebase navigation.
|
34
41
|
"""
|
42
|
+
|
35
43
|
id: Optional[int] = Field(None, description="Database ID")
|
36
44
|
project_id: str = Field(..., description="Reference to project")
|
37
45
|
file_path: str = Field(..., description="Relative path from project root")
|
38
46
|
description: str = Field(..., description="Detailed content description")
|
39
47
|
file_hash: Optional[str] = Field(None, description="SHA-256 of file contents")
|
40
|
-
last_modified: datetime = Field(
|
48
|
+
last_modified: datetime = Field(
|
49
|
+
default_factory=datetime.utcnow, description="Last update timestamp"
|
50
|
+
)
|
41
51
|
version: int = Field(default=1, description="For optimistic concurrency control")
|
42
|
-
source_project_id: Optional[str] = Field(
|
43
|
-
|
52
|
+
source_project_id: Optional[str] = Field(
|
53
|
+
None, description="Source project if copied from upstream"
|
54
|
+
)
|
55
|
+
to_be_cleaned: Optional[int] = Field(
|
56
|
+
None, description="UNIX timestamp for cleanup, NULL = active"
|
57
|
+
)
|
44
58
|
|
45
59
|
|
46
60
|
class MergeConflict(BaseModel):
|
47
61
|
"""
|
48
62
|
Represents a merge conflict between file descriptions.
|
49
|
-
|
63
|
+
|
50
64
|
Used during branch merging when the same file has different descriptions
|
51
65
|
in source and target branches.
|
52
66
|
"""
|
67
|
+
|
53
68
|
id: Optional[int] = Field(None, description="Database ID")
|
54
69
|
project_id: str = Field(..., description="Project identifier")
|
55
70
|
file_path: str = Field(..., description="Path to conflicted file")
|
@@ -58,53 +73,65 @@ class MergeConflict(BaseModel):
|
|
58
73
|
source_description: str = Field(..., description="Description from source branch")
|
59
74
|
target_description: str = Field(..., description="Description from target branch")
|
60
75
|
resolution: Optional[str] = Field(None, description="AI-provided resolution")
|
61
|
-
created: datetime = Field(
|
76
|
+
created: datetime = Field(
|
77
|
+
default_factory=datetime.utcnow, description="Creation timestamp"
|
78
|
+
)
|
62
79
|
|
63
80
|
|
64
81
|
class ProjectOverview(BaseModel):
|
65
82
|
"""
|
66
83
|
Represents a condensed, interpretive overview of an entire codebase.
|
67
|
-
|
84
|
+
|
68
85
|
Stores a comprehensive narrative that captures architecture, components,
|
69
86
|
relationships, and design patterns in a single document rather than
|
70
87
|
individual file descriptions.
|
71
88
|
"""
|
89
|
+
|
72
90
|
project_id: str = Field(..., description="Reference to project")
|
73
91
|
overview: str = Field(..., description="Comprehensive codebase narrative")
|
74
|
-
last_modified: datetime = Field(
|
92
|
+
last_modified: datetime = Field(
|
93
|
+
default_factory=datetime.utcnow, description="Last update timestamp"
|
94
|
+
)
|
75
95
|
total_files: int = Field(..., description="Number of files in codebase")
|
76
|
-
total_tokens: int = Field(
|
96
|
+
total_tokens: int = Field(
|
97
|
+
..., description="Total tokens in individual descriptions"
|
98
|
+
)
|
77
99
|
|
78
100
|
|
79
101
|
class CodebaseOverview(BaseModel):
|
80
102
|
"""
|
81
103
|
Represents a complete codebase structure with file descriptions.
|
82
|
-
|
104
|
+
|
83
105
|
Provides hierarchical view of project files with token count information
|
84
106
|
to help determine whether to use full overview or search-based approach.
|
85
107
|
"""
|
108
|
+
|
86
109
|
project_name: str = Field(..., description="Project name")
|
87
110
|
total_files: int = Field(..., description="Total number of tracked files")
|
88
111
|
total_tokens: int = Field(..., description="Total token count for all descriptions")
|
89
112
|
is_large: bool = Field(..., description="True if exceeds configured token limit")
|
90
113
|
token_limit: int = Field(..., description="Current token limit setting")
|
91
|
-
structure:
|
114
|
+
structure: "FolderNode" = Field(..., description="Hierarchical folder structure")
|
92
115
|
|
93
116
|
|
94
117
|
class FolderNode(BaseModel):
|
95
118
|
"""
|
96
119
|
Represents a folder in the codebase hierarchy.
|
97
120
|
"""
|
121
|
+
|
98
122
|
name: str = Field(..., description="Folder name")
|
99
123
|
path: str = Field(..., description="Full path from project root")
|
100
|
-
files: List[
|
101
|
-
|
124
|
+
files: List["FileNode"] = Field(
|
125
|
+
default_factory=list, description="Files in this folder"
|
126
|
+
)
|
127
|
+
folders: List["FolderNode"] = Field(default_factory=list, description="Subfolders")
|
102
128
|
|
103
129
|
|
104
130
|
class FileNode(BaseModel):
|
105
131
|
"""
|
106
132
|
Represents a file in the codebase hierarchy.
|
107
133
|
"""
|
134
|
+
|
108
135
|
name: str = Field(..., description="File name")
|
109
136
|
path: str = Field(..., description="Full path from project root")
|
110
137
|
description: str = Field(..., description="File description")
|
@@ -114,6 +141,7 @@ class SearchResult(BaseModel):
|
|
114
141
|
"""
|
115
142
|
Represents a search result with relevance scoring.
|
116
143
|
"""
|
144
|
+
|
117
145
|
file_path: str = Field(..., description="Path to the matching file")
|
118
146
|
description: str = Field(..., description="File description")
|
119
147
|
relevance_score: float = Field(..., description="Search relevance score")
|
@@ -124,11 +152,16 @@ class CodebaseSizeInfo(BaseModel):
|
|
124
152
|
"""
|
125
153
|
Information about codebase size and token usage.
|
126
154
|
"""
|
155
|
+
|
127
156
|
total_tokens: int = Field(..., description="Total token count")
|
128
157
|
is_large: bool = Field(..., description="Whether codebase exceeds token limit")
|
129
|
-
recommendation: str = Field(
|
158
|
+
recommendation: str = Field(
|
159
|
+
..., description="Recommended approach (use_search or use_overview)"
|
160
|
+
)
|
130
161
|
token_limit: int = Field(..., description="Configured token limit")
|
131
|
-
cleaned_up_files: List[str] = Field(
|
162
|
+
cleaned_up_files: List[str] = Field(
|
163
|
+
default_factory=list, description="Files removed during cleanup"
|
164
|
+
)
|
132
165
|
cleaned_up_count: int = Field(default=0, description="Number of files cleaned up")
|
133
166
|
|
134
167
|
|
@@ -136,6 +169,7 @@ class WordFrequencyTerm(BaseModel):
|
|
136
169
|
"""
|
137
170
|
Represents a term and its frequency from word analysis.
|
138
171
|
"""
|
172
|
+
|
139
173
|
term: str = Field(..., description="The word/term")
|
140
174
|
frequency: int = Field(..., description="Number of occurrences")
|
141
175
|
|
@@ -144,6 +178,7 @@ class WordFrequencyResult(BaseModel):
|
|
144
178
|
"""
|
145
179
|
Results from word frequency analysis of file descriptions.
|
146
180
|
"""
|
181
|
+
|
147
182
|
top_terms: List[WordFrequencyTerm] = Field(..., description="Top frequent terms")
|
148
183
|
total_terms_analyzed: int = Field(..., description="Total terms processed")
|
149
184
|
total_unique_terms: int = Field(..., description="Number of unique terms found")
|