mcp-code-indexer 4.0.1__py3-none-any.whl → 4.1.0__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.
Files changed (57) hide show
  1. mcp_code_indexer/__init__.py +7 -5
  2. mcp_code_indexer/ask_handler.py +2 -2
  3. mcp_code_indexer/claude_api_handler.py +10 -5
  4. mcp_code_indexer/cleanup_manager.py +20 -12
  5. mcp_code_indexer/commands/makelocal.py +85 -63
  6. mcp_code_indexer/data/stop_words_english.txt +1 -1
  7. mcp_code_indexer/database/connection_health.py +29 -20
  8. mcp_code_indexer/database/database.py +44 -31
  9. mcp_code_indexer/database/database_factory.py +19 -20
  10. mcp_code_indexer/database/exceptions.py +10 -10
  11. mcp_code_indexer/database/models.py +126 -1
  12. mcp_code_indexer/database/path_resolver.py +22 -21
  13. mcp_code_indexer/database/retry_executor.py +37 -19
  14. mcp_code_indexer/deepask_handler.py +3 -3
  15. mcp_code_indexer/error_handler.py +46 -20
  16. mcp_code_indexer/file_scanner.py +15 -12
  17. mcp_code_indexer/git_hook_handler.py +71 -76
  18. mcp_code_indexer/logging_config.py +13 -5
  19. mcp_code_indexer/main.py +85 -22
  20. mcp_code_indexer/middleware/__init__.py +1 -1
  21. mcp_code_indexer/middleware/auth.py +47 -43
  22. mcp_code_indexer/middleware/error_middleware.py +15 -15
  23. mcp_code_indexer/middleware/logging.py +44 -42
  24. mcp_code_indexer/middleware/security.py +84 -76
  25. mcp_code_indexer/migrations/002_performance_indexes.sql +1 -1
  26. mcp_code_indexer/migrations/004_remove_branch_dependency.sql +14 -14
  27. mcp_code_indexer/migrations/006_vector_mode.sql +189 -0
  28. mcp_code_indexer/query_preprocessor.py +2 -2
  29. mcp_code_indexer/server/mcp_server.py +158 -94
  30. mcp_code_indexer/transport/__init__.py +1 -1
  31. mcp_code_indexer/transport/base.py +19 -17
  32. mcp_code_indexer/transport/http_transport.py +89 -76
  33. mcp_code_indexer/transport/stdio_transport.py +12 -8
  34. mcp_code_indexer/vector_mode/__init__.py +36 -0
  35. mcp_code_indexer/vector_mode/chunking/__init__.py +19 -0
  36. mcp_code_indexer/vector_mode/chunking/ast_chunker.py +403 -0
  37. mcp_code_indexer/vector_mode/chunking/chunk_optimizer.py +500 -0
  38. mcp_code_indexer/vector_mode/chunking/language_handlers.py +478 -0
  39. mcp_code_indexer/vector_mode/config.py +155 -0
  40. mcp_code_indexer/vector_mode/daemon.py +335 -0
  41. mcp_code_indexer/vector_mode/monitoring/__init__.py +19 -0
  42. mcp_code_indexer/vector_mode/monitoring/change_detector.py +312 -0
  43. mcp_code_indexer/vector_mode/monitoring/file_watcher.py +445 -0
  44. mcp_code_indexer/vector_mode/monitoring/merkle_tree.py +418 -0
  45. mcp_code_indexer/vector_mode/providers/__init__.py +72 -0
  46. mcp_code_indexer/vector_mode/providers/base_provider.py +230 -0
  47. mcp_code_indexer/vector_mode/providers/turbopuffer_client.py +338 -0
  48. mcp_code_indexer/vector_mode/providers/voyage_client.py +212 -0
  49. mcp_code_indexer/vector_mode/security/__init__.py +11 -0
  50. mcp_code_indexer/vector_mode/security/patterns.py +297 -0
  51. mcp_code_indexer/vector_mode/security/redactor.py +368 -0
  52. {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/METADATA +82 -24
  53. mcp_code_indexer-4.1.0.dist-info/RECORD +66 -0
  54. mcp_code_indexer-4.0.1.dist-info/RECORD +0 -47
  55. {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/LICENSE +0 -0
  56. {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/WHEEL +0 -0
  57. {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/entry_points.txt +0 -0
@@ -7,7 +7,7 @@ Provides common interface and functionality for different transport methods.
7
7
  import asyncio
8
8
  import logging
9
9
  from abc import ABC, abstractmethod
10
- from typing import Any, Optional
10
+ from typing import Any
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
@@ -15,66 +15,68 @@ logger = logging.getLogger(__name__)
15
15
  class Transport(ABC):
16
16
  """
17
17
  Abstract base class for MCP transport implementations.
18
-
18
+
19
19
  Defines the common interface that all transports must implement
20
20
  to work with the MCPCodeIndexServer.
21
21
  """
22
-
22
+
23
23
  def __init__(self, server_instance: Any):
24
24
  """
25
25
  Initialize transport with server instance.
26
-
26
+
27
27
  Args:
28
28
  server_instance: The MCPCodeIndexServer instance
29
29
  """
30
30
  self.server = server_instance
31
31
  self.logger = logger.getChild(self.__class__.__name__)
32
-
32
+
33
33
  @abstractmethod
34
34
  async def run(self) -> None:
35
35
  """
36
36
  Run the transport server.
37
-
37
+
38
38
  This method should handle the main server loop and connection
39
39
  management for the specific transport type.
40
40
  """
41
41
  pass
42
-
42
+
43
43
  async def initialize(self) -> None:
44
44
  """
45
45
  Initialize transport-specific resources.
46
-
46
+
47
47
  Called before run() to set up any transport-specific state.
48
48
  Default implementation does nothing.
49
49
  """
50
50
  pass
51
-
51
+
52
52
  async def cleanup(self) -> None:
53
53
  """
54
54
  Clean up transport-specific resources.
55
-
55
+
56
56
  Called when shutting down to clean up connections and resources.
57
57
  Default implementation does nothing.
58
58
  """
59
59
  pass
60
-
61
- async def _run_with_retry(self, max_retries: int = 3, base_delay: float = 1.0) -> None:
60
+
61
+ async def _run_with_retry(
62
+ self, max_retries: int = 3, base_delay: float = 1.0
63
+ ) -> None:
62
64
  """
63
65
  Run transport with exponential backoff retry logic.
64
-
66
+
65
67
  Args:
66
68
  max_retries: Maximum number of retry attempts
67
69
  base_delay: Base delay between retries in seconds
68
70
  """
69
71
  retry_count = 0
70
-
72
+
71
73
  while retry_count <= max_retries:
72
74
  try:
73
75
  await self.run()
74
76
  break
75
77
  except Exception as e:
76
78
  retry_count += 1
77
-
79
+
78
80
  if retry_count > max_retries:
79
81
  self.logger.error(
80
82
  "Transport failed after %d attempts",
@@ -88,7 +90,7 @@ class Transport(ABC):
88
90
  },
89
91
  )
90
92
  raise
91
-
93
+
92
94
  delay = base_delay * (2 ** (retry_count - 1))
93
95
  self.logger.warning(
94
96
  "Transport failed, retrying in %.1f seconds (attempt %d/%d)",
@@ -104,5 +106,5 @@ class Transport(ABC):
104
106
  }
105
107
  },
106
108
  )
107
-
109
+
108
110
  await asyncio.sleep(delay)
@@ -8,17 +8,16 @@ Server-Sent Events for streaming responses.
8
8
  import asyncio
9
9
  import json
10
10
  import logging
11
- import uuid
12
11
  from contextlib import asynccontextmanager
13
- from typing import Any, Dict, List, Optional
12
+ from typing import Any, AsyncGenerator, Dict, List, Optional
14
13
 
15
14
  try:
16
- from fastapi import FastAPI, HTTPException, Request, Depends
17
- from fastapi.responses import StreamingResponse
15
+ import uvicorn
16
+ from fastapi import Depends, FastAPI, HTTPException
18
17
  from fastapi.middleware.cors import CORSMiddleware
19
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
18
+ from fastapi.responses import StreamingResponse
19
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
20
20
  from pydantic import BaseModel, ValidationError
21
- import uvicorn
22
21
  except ImportError as e:
23
22
  raise ImportError(
24
23
  "HTTP transport dependencies not installed. "
@@ -26,18 +25,19 @@ except ImportError as e:
26
25
  "Please reinstall mcp-code-indexer."
27
26
  ) from e
28
27
 
29
- from .base import Transport
28
+ from ..middleware.auth import HTTPAuthMiddleware
30
29
 
31
30
  # Import middleware
32
31
  from ..middleware.logging import HTTPLoggingMiddleware, HTTPMetricsCollector
33
- from ..middleware.auth import HTTPAuthMiddleware
34
32
  from ..middleware.security import HTTPSecurityMiddleware
33
+ from .base import Transport
35
34
 
36
35
  logger = logging.getLogger(__name__)
37
36
 
38
37
 
39
38
  class MCPRequest(BaseModel):
40
39
  """MCP JSON-RPC request model."""
40
+
41
41
  jsonrpc: str = "2.0"
42
42
  method: str
43
43
  params: Dict[str, Any]
@@ -46,6 +46,7 @@ class MCPRequest(BaseModel):
46
46
 
47
47
  class MCPResponse(BaseModel):
48
48
  """MCP JSON-RPC response model."""
49
+
49
50
  jsonrpc: str = "2.0"
50
51
  result: Optional[Any] = None
51
52
  error: Optional[Dict[str, Any]] = None
@@ -55,11 +56,11 @@ class MCPResponse(BaseModel):
55
56
  class HTTPTransport(Transport):
56
57
  """
57
58
  HTTP transport implementation using FastAPI.
58
-
59
+
59
60
  Provides REST API endpoints for MCP tools with optional authentication
60
61
  and Server-Sent Events for streaming responses.
61
62
  """
62
-
63
+
63
64
  def __init__(
64
65
  self,
65
66
  server_instance: Any,
@@ -70,7 +71,7 @@ class HTTPTransport(Transport):
70
71
  ):
71
72
  """
72
73
  Initialize HTTP transport.
73
-
74
+
74
75
  Args:
75
76
  server_instance: The MCPCodeIndexServer instance
76
77
  host: Host to bind the server to
@@ -83,26 +84,26 @@ class HTTPTransport(Transport):
83
84
  self.port = port
84
85
  self.auth_token = auth_token
85
86
  self.cors_origins = cors_origins or ["*"]
86
-
87
+
87
88
  # Connection management
88
89
  self.active_connections: Dict[str, asyncio.Queue] = {}
89
90
  self.app: Optional[FastAPI] = None
90
-
91
+
91
92
  # Metrics collection
92
93
  self.metrics = HTTPMetricsCollector()
93
-
94
+
94
95
  async def initialize(self) -> None:
95
96
  """Initialize FastAPI application and routes."""
96
97
  self.app = await self._create_app()
97
-
98
+
98
99
  @asynccontextmanager
99
- async def _lifespan(self, app: FastAPI):
100
+ async def _lifespan(self, app: FastAPI) -> AsyncGenerator[None, None]:
100
101
  """FastAPI lifespan context manager."""
101
102
  self.logger.info("HTTP transport starting up")
102
103
  yield
103
104
  self.logger.info("HTTP transport shutting down")
104
105
  await self.cleanup()
105
-
106
+
106
107
  async def _create_app(self) -> FastAPI:
107
108
  """Create and configure FastAPI application."""
108
109
  app = FastAPI(
@@ -111,23 +112,23 @@ class HTTPTransport(Transport):
111
112
  version="1.0.0",
112
113
  lifespan=self._lifespan,
113
114
  )
114
-
115
+
115
116
  # Add middleware stack (in reverse order of execution)
116
-
117
+
117
118
  # Security middleware (outermost)
118
119
  app.add_middleware(HTTPSecurityMiddleware)
119
-
120
+
120
121
  # Logging middleware
121
122
  app.add_middleware(HTTPLoggingMiddleware)
122
-
123
+
123
124
  # Authentication middleware
124
125
  if self.auth_token:
125
126
  app.add_middleware(
126
127
  HTTPAuthMiddleware,
127
128
  auth_token=self.auth_token,
128
- public_paths=["/health", "/docs", "/openapi.json", "/metrics"]
129
+ public_paths=["/health", "/docs", "/openapi.json", "/metrics"],
129
130
  )
130
-
131
+
131
132
  # CORS middleware (innermost, applied first)
132
133
  app.add_middleware(
133
134
  CORSMiddleware,
@@ -136,62 +137,64 @@ class HTTPTransport(Transport):
136
137
  allow_methods=["GET", "POST", "OPTIONS"],
137
138
  allow_headers=["*"],
138
139
  )
139
-
140
+
140
141
  # Authentication dependency
141
142
  security = HTTPBearer(auto_error=False) if self.auth_token else None
142
-
143
+
143
144
  async def verify_token(
144
- credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
145
+ credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
145
146
  ) -> bool:
146
147
  """Verify Bearer token if authentication is enabled."""
147
148
  if not self.auth_token:
148
149
  return True
149
-
150
+
150
151
  if not credentials or credentials.credentials != self.auth_token:
151
152
  raise HTTPException(
152
- status_code=401,
153
- detail="Invalid authentication token"
153
+ status_code=401, detail="Invalid authentication token"
154
154
  )
155
155
  return True
156
-
156
+
157
157
  @app.get("/health")
158
- async def health_check():
158
+ async def health_check() -> Dict[str, str]:
159
159
  """Health check endpoint."""
160
160
  return {"status": "healthy", "transport": "http"}
161
-
161
+
162
162
  @app.get("/metrics")
163
- async def get_metrics(authenticated: bool = Depends(verify_token)):
163
+ async def get_metrics(
164
+ authenticated: bool = Depends(verify_token),
165
+ ) -> Dict[str, Any]:
164
166
  """Get HTTP transport metrics."""
165
167
  metrics = {}
166
-
168
+
167
169
  metrics["http"] = self.metrics.get_metrics()
168
-
170
+
169
171
  # Add connection stats
170
172
  metrics["connections"] = {
171
173
  "active_sse_connections": len(self.active_connections),
172
174
  "connection_ids": list(self.active_connections.keys()),
173
175
  }
174
-
176
+
175
177
  return metrics
176
-
178
+
177
179
  @app.get("/tools")
178
- async def list_tools(authenticated: bool = Depends(verify_token)):
180
+ async def list_tools(
181
+ authenticated: bool = Depends(verify_token),
182
+ ) -> Dict[str, Any]:
179
183
  """List available MCP tools."""
180
184
  tools = await self.server._handle_list_tools()
181
185
  return {"tools": tools}
182
-
186
+
183
187
  @app.post("/mcp", response_model=MCPResponse)
184
188
  async def handle_mcp_request(
185
- request: MCPRequest,
186
- authenticated: bool = Depends(verify_token)
187
- ):
189
+ request: MCPRequest, authenticated: bool = Depends(verify_token)
190
+ ) -> MCPResponse:
188
191
  """Handle MCP JSON-RPC requests."""
189
192
  try:
190
193
  # Route to appropriate tool handler
191
194
  if request.method == "tools/call":
192
195
  tool_name = request.params.get("name")
193
196
  tool_arguments = request.params.get("arguments", {})
194
-
197
+
195
198
  # Map tool names to handler methods
196
199
  tool_handlers = {
197
200
  "get_file_description": self.server._handle_get_file_description,
@@ -206,28 +209,34 @@ class HTTPTransport(Transport):
206
209
  "search_codebase_overview": self.server._handle_search_codebase_overview,
207
210
  "check_database_health": self.server._handle_check_database_health,
208
211
  }
209
-
212
+
210
213
  if tool_name not in tool_handlers:
211
214
  return MCPResponse(
212
215
  id=request.id,
213
- error={"code": -32601, "message": f"Unknown tool: {tool_name}"}
216
+ error={
217
+ "code": -32601,
218
+ "message": f"Unknown tool: {tool_name}",
219
+ },
214
220
  )
215
-
221
+
216
222
  # Execute tool handler
217
223
  result = await tool_handlers[tool_name](tool_arguments)
218
-
224
+
219
225
  return MCPResponse(id=request.id, result=result)
220
-
226
+
221
227
  else:
222
228
  return MCPResponse(
223
229
  id=request.id,
224
- error={"code": -32601, "message": f"Unknown method: {request.method}"}
230
+ error={
231
+ "code": -32601,
232
+ "message": f"Unknown method: {request.method}",
233
+ },
225
234
  )
226
-
235
+
227
236
  except ValidationError as e:
228
237
  return MCPResponse(
229
238
  id=request.id,
230
- error={"code": -32602, "message": f"Invalid params: {str(e)}"}
239
+ error={"code": -32602, "message": f"Invalid params: {str(e)}"},
231
240
  )
232
241
  except Exception as e:
233
242
  self.logger.error(
@@ -242,56 +251,58 @@ class HTTPTransport(Transport):
242
251
  )
243
252
  return MCPResponse(
244
253
  id=request.id,
245
- error={"code": -32603, "message": f"Internal error: {str(e)}"}
254
+ error={"code": -32603, "message": f"Internal error: {str(e)}"},
246
255
  )
247
-
256
+
248
257
  @app.get("/events/{connection_id}")
249
258
  async def server_sent_events(
250
- connection_id: str,
251
- authenticated: bool = Depends(verify_token)
252
- ):
259
+ connection_id: str, authenticated: bool = Depends(verify_token)
260
+ ) -> Any:
253
261
  """Server-Sent Events endpoint for streaming responses."""
254
- async def event_stream():
262
+
263
+ async def event_stream() -> AsyncGenerator[str, None]:
255
264
  # Create connection queue
256
- queue = asyncio.Queue()
265
+ queue: asyncio.Queue[Dict[str, Any]] = asyncio.Queue()
257
266
  self.active_connections[connection_id] = queue
258
-
267
+
259
268
  try:
260
269
  while True:
261
270
  # Wait for events from the queue
262
271
  try:
263
- event_data = await asyncio.wait_for(queue.get(), timeout=30.0)
272
+ event_data = await asyncio.wait_for(
273
+ queue.get(), timeout=30.0
274
+ )
264
275
  yield f"data: {json.dumps(event_data)}\\n\\n"
265
276
  except asyncio.TimeoutError:
266
277
  # Send keepalive
267
278
  yield f"data: {json.dumps({'type': 'keepalive'})}\\n\\n"
268
-
279
+
269
280
  except asyncio.CancelledError:
270
281
  pass
271
282
  finally:
272
283
  # Clean up connection
273
284
  self.active_connections.pop(connection_id, None)
274
-
285
+
275
286
  return StreamingResponse(
276
287
  event_stream(),
277
288
  media_type="text/event-stream",
278
289
  headers={
279
290
  "Cache-Control": "no-cache",
280
291
  "Connection": "keep-alive",
281
- }
292
+ },
282
293
  )
283
-
294
+
284
295
  return app
285
-
296
+
286
297
  async def run(self) -> None:
287
298
  """
288
299
  Run the HTTP transport server.
289
-
300
+
290
301
  Starts the uvicorn ASGI server with the configured FastAPI application.
291
302
  """
292
303
  if not self.app:
293
304
  await self.initialize()
294
-
305
+
295
306
  self.logger.info(
296
307
  "Starting HTTP transport",
297
308
  extra={
@@ -304,8 +315,10 @@ class HTTPTransport(Transport):
304
315
  }
305
316
  },
306
317
  )
307
-
318
+
308
319
  # Configure uvicorn
320
+ if not self.app:
321
+ raise RuntimeError("FastAPI app not initialized")
309
322
  config = uvicorn.Config(
310
323
  app=self.app,
311
324
  host=self.host,
@@ -314,9 +327,9 @@ class HTTPTransport(Transport):
314
327
  access_log=True,
315
328
  loop="asyncio",
316
329
  )
317
-
330
+
318
331
  server = uvicorn.Server(config)
319
-
332
+
320
333
  try:
321
334
  await server.serve()
322
335
  except Exception as e:
@@ -330,7 +343,7 @@ class HTTPTransport(Transport):
330
343
  },
331
344
  )
332
345
  raise
333
-
346
+
334
347
  async def cleanup(self) -> None:
335
348
  """Clean up HTTP transport resources."""
336
349
  # Close all active SSE connections
@@ -339,24 +352,24 @@ class HTTPTransport(Transport):
339
352
  await queue.put({"type": "disconnect"})
340
353
  except Exception as e:
341
354
  self.logger.warning(f"Error closing connection {connection_id}: {e}")
342
-
355
+
343
356
  self.active_connections.clear()
344
357
  self.logger.info("HTTP transport cleanup completed")
345
-
358
+
346
359
  async def send_event(self, connection_id: str, event_data: Dict[str, Any]) -> bool:
347
360
  """
348
361
  Send an event to a specific SSE connection.
349
-
362
+
350
363
  Args:
351
364
  connection_id: Connection identifier
352
365
  event_data: Event data to send
353
-
366
+
354
367
  Returns:
355
368
  True if event was sent successfully, False otherwise
356
369
  """
357
370
  if connection_id not in self.active_connections:
358
371
  return False
359
-
372
+
360
373
  try:
361
374
  await self.active_connections[connection_id].put(event_data)
362
375
  return True
@@ -19,24 +19,24 @@ logger = logging.getLogger(__name__)
19
19
  class StdioTransport(Transport):
20
20
  """
21
21
  Stdio transport implementation using MCP's built-in stdio server.
22
-
22
+
23
23
  This transport maintains compatibility with the existing stdio-based
24
24
  MCP protocol while fitting into the transport abstraction.
25
25
  """
26
-
26
+
27
27
  def __init__(self, server_instance: Any):
28
28
  """
29
29
  Initialize stdio transport.
30
-
30
+
31
31
  Args:
32
32
  server_instance: The MCPCodeIndexServer instance
33
33
  """
34
34
  super().__init__(server_instance)
35
-
35
+
36
36
  async def run(self) -> None:
37
37
  """
38
38
  Run the stdio transport server.
39
-
39
+
40
40
  Uses the mcp library's stdio_server context manager to handle
41
41
  JSON-RPC communication over stdin/stdout with retry logic.
42
42
  """
@@ -49,7 +49,7 @@ class StdioTransport(Transport):
49
49
  }
50
50
  },
51
51
  )
52
-
52
+
53
53
  max_retries = 5
54
54
  base_delay = 2.0 # seconds
55
55
 
@@ -59,8 +59,12 @@ class StdioTransport(Transport):
59
59
  self.logger.info(
60
60
  f"stdio_server context established (attempt {attempt + 1})"
61
61
  )
62
- initialization_options = self.server.server.create_initialization_options()
63
- self.logger.debug(f"Initialization options: {initialization_options}")
62
+ initialization_options = (
63
+ self.server.server.create_initialization_options()
64
+ )
65
+ self.logger.debug(
66
+ f"Initialization options: {initialization_options}"
67
+ )
64
68
 
65
69
  await self.server._run_session_with_retry(
66
70
  read_stream, write_stream, initialization_options
@@ -0,0 +1,36 @@
1
+ """
2
+ Vector Mode for MCP Code Indexer.
3
+
4
+ This package provides semantic search capabilities using embeddings and vector databases.
5
+ Includes automated file monitoring, AST-based code chunking, and secure embedding generation.
6
+ """
7
+
8
+ from typing import Optional
9
+ from pathlib import Path
10
+ import os
11
+
12
+ __version__ = "1.0.0"
13
+
14
+ def is_vector_mode_available() -> bool:
15
+ """Check if vector mode dependencies are available."""
16
+ try:
17
+ import voyage
18
+ import turbopuffer
19
+ import tree_sitter
20
+ import watchdog
21
+ return True
22
+ except ImportError:
23
+ return False
24
+
25
+ def get_vector_config_path() -> Path:
26
+ """Get path to vector mode configuration."""
27
+ config_dir = Path.home() / ".mcp-code-index" / "vector"
28
+ config_dir.mkdir(parents=True, exist_ok=True)
29
+ return config_dir / "config.yaml"
30
+
31
+ def check_api_keys() -> dict[str, bool]:
32
+ """Check availability of required API keys."""
33
+ return {
34
+ "voyage": os.getenv("VOYAGE_API_KEY") is not None,
35
+ "turbopuffer": os.getenv("TURBOPUFFER_API_KEY") is not None,
36
+ }
@@ -0,0 +1,19 @@
1
+ """
2
+ AST-based code chunking for vector mode.
3
+
4
+ Provides semantic code chunking using Tree-sitter parsers to extract
5
+ meaningful code units for embedding generation.
6
+ """
7
+
8
+ from .ast_chunker import ASTChunker, CodeChunk
9
+ from .language_handlers import LanguageHandler, get_language_handler
10
+ from .chunk_optimizer import ChunkOptimizer, OptimizedChunk
11
+
12
+ __all__ = [
13
+ "ASTChunker",
14
+ "CodeChunk",
15
+ "LanguageHandler",
16
+ "get_language_handler",
17
+ "ChunkOptimizer",
18
+ "OptimizedChunk",
19
+ ]