chunkhound 0.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.
- chunkhound/__init__.py +25 -0
- chunkhound/api/__init__.py +5 -0
- chunkhound/api/cli/__init__.py +13 -0
- chunkhound/api/cli/commands/__init__.py +11 -0
- chunkhound/api/cli/commands/config.py +488 -0
- chunkhound/api/cli/commands/mcp.py +57 -0
- chunkhound/api/cli/commands/run.py +366 -0
- chunkhound/api/cli/main.py +172 -0
- chunkhound/api/cli/parsers/__init__.py +14 -0
- chunkhound/api/cli/parsers/config_parser.py +393 -0
- chunkhound/api/cli/parsers/main_parser.py +149 -0
- chunkhound/api/cli/parsers/mcp_parser.py +64 -0
- chunkhound/api/cli/parsers/run_parser.py +91 -0
- chunkhound/api/cli/utils/__init__.py +13 -0
- chunkhound/api/cli/utils/output.py +246 -0
- chunkhound/api/cli/utils/validation.py +274 -0
- chunkhound/chunker.py +377 -0
- chunkhound/config.py +777 -0
- chunkhound/database.py +314 -0
- chunkhound/embeddings.py +1255 -0
- chunkhound/file_discovery_cache.py +260 -0
- chunkhound/file_watcher.py +480 -0
- chunkhound/mcp_entry.py +57 -0
- chunkhound/mcp_server.py +463 -0
- chunkhound/parser.py +4013 -0
- chunkhound/process_detection.py +276 -0
- chunkhound/py.typed +0 -0
- chunkhound/signal_coordinator.py +464 -0
- chunkhound/tree_cache.py +339 -0
- chunkhound-0.1.0.dist-info/METADATA +168 -0
- chunkhound-0.1.0.dist-info/RECORD +65 -0
- chunkhound-0.1.0.dist-info/WHEEL +4 -0
- chunkhound-0.1.0.dist-info/entry_points.txt +3 -0
- chunkhound-0.1.0.dist-info/licenses/LICENSE +21 -0
- core/__init__.py +44 -0
- core/exceptions/__init__.py +37 -0
- core/exceptions/core.py +301 -0
- core/models/__init__.py +24 -0
- core/models/chunk.py +400 -0
- core/models/embedding.py +422 -0
- core/models/file.py +281 -0
- core/types/__init__.py +51 -0
- core/types/common.py +156 -0
- interfaces/__init__.py +11 -0
- interfaces/database_provider.py +197 -0
- interfaces/embedding_provider.py +271 -0
- interfaces/language_parser.py +305 -0
- providers/__init__.py +16 -0
- providers/database/__init__.py +7 -0
- providers/database/duckdb_provider.py +2119 -0
- providers/embeddings/__init__.py +7 -0
- providers/embeddings/openai_provider.py +469 -0
- providers/parsing/__init__.py +7 -0
- providers/parsing/csharp_parser.py +790 -0
- providers/parsing/java_parser.py +771 -0
- providers/parsing/javascript_parser.py +507 -0
- providers/parsing/markdown_parser.py +402 -0
- providers/parsing/python_parser.py +397 -0
- providers/parsing/typescript_parser.py +736 -0
- registry/__init__.py +403 -0
- services/__init__.py +13 -0
- services/base_service.py +24 -0
- services/embedding_service.py +478 -0
- services/indexing_coordinator.py +504 -0
- services/search_service.py +374 -0
chunkhound/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""ChunkHound - Local-first semantic code search with vector and regex capabilities."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
__author__ = "ChunkHound Team"
|
|
5
|
+
__description__ = "Local-first semantic code search with vector and regex capabilities"
|
|
6
|
+
|
|
7
|
+
# Import modules only when needed to avoid dependency issues during setup
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Database",
|
|
10
|
+
"CodeParser",
|
|
11
|
+
"Chunker",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
def __getattr__(name: str):
|
|
15
|
+
"""Lazy import to avoid dependency issues during setup."""
|
|
16
|
+
if name == "Database":
|
|
17
|
+
from .database import Database
|
|
18
|
+
return Database
|
|
19
|
+
elif name == "CodeParser":
|
|
20
|
+
from .parser import CodeParser
|
|
21
|
+
return CodeParser
|
|
22
|
+
elif name == "Chunker":
|
|
23
|
+
from .chunker import Chunker
|
|
24
|
+
return Chunker
|
|
25
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"""Config command module - handles embedding server configuration operations."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import asyncio
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, Any, List, Optional
|
|
8
|
+
|
|
9
|
+
from loguru import logger
|
|
10
|
+
|
|
11
|
+
from chunkhound.config import get_config_manager, reset_config_manager, ServerConfig
|
|
12
|
+
from ..utils.output import OutputFormatter, format_health_status, format_server_info, print_section
|
|
13
|
+
from ..utils.validation import validate_config_args, validate_server_name
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def config_command(args: argparse.Namespace) -> None:
|
|
17
|
+
"""Execute the config command with appropriate subcommand.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
args: Parsed command-line arguments
|
|
21
|
+
"""
|
|
22
|
+
# Route to appropriate subcommand
|
|
23
|
+
subcommand_handlers = {
|
|
24
|
+
"list": config_list_command,
|
|
25
|
+
"add": config_add_command,
|
|
26
|
+
"remove": config_remove_command,
|
|
27
|
+
"test": config_test_command,
|
|
28
|
+
"health": config_health_command,
|
|
29
|
+
"enable": config_enable_command,
|
|
30
|
+
"disable": config_disable_command,
|
|
31
|
+
"set-default": config_set_default_command,
|
|
32
|
+
"validate": config_validate_command,
|
|
33
|
+
"benchmark": config_benchmark_command,
|
|
34
|
+
"switch": config_switch_command,
|
|
35
|
+
"discover": config_discover_command,
|
|
36
|
+
"export": config_export_command,
|
|
37
|
+
"import": config_import_command,
|
|
38
|
+
"template": config_template_command,
|
|
39
|
+
"batch-test": config_batch_test_command,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
handler = subcommand_handlers.get(args.config_command)
|
|
43
|
+
if handler:
|
|
44
|
+
await handler(args)
|
|
45
|
+
else:
|
|
46
|
+
logger.error(f"Unknown config command: {args.config_command}")
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def config_list_command(args: argparse.Namespace) -> None:
|
|
51
|
+
"""Handle config list command."""
|
|
52
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
56
|
+
servers = config_manager.registry.list_servers()
|
|
57
|
+
|
|
58
|
+
if not servers:
|
|
59
|
+
formatter.info("No servers configured.")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
print(f"Configured servers ({len(servers)}):")
|
|
63
|
+
print()
|
|
64
|
+
|
|
65
|
+
for name in servers:
|
|
66
|
+
server = config_manager.registry.get_server(name)
|
|
67
|
+
default_marker = " (default)" if config_manager.registry._default_server == name else ""
|
|
68
|
+
enabled_marker = "" if server.enabled else " (disabled)"
|
|
69
|
+
|
|
70
|
+
print(f" {name}{default_marker}{enabled_marker}")
|
|
71
|
+
print(f" Type: {server.type}")
|
|
72
|
+
print(f" URL: {server.base_url}")
|
|
73
|
+
print(f" Model: {server.model or 'auto-detected'}")
|
|
74
|
+
|
|
75
|
+
if getattr(args, 'show_health', False):
|
|
76
|
+
try:
|
|
77
|
+
health = await config_manager.registry.check_server_health(name)
|
|
78
|
+
status = format_health_status({
|
|
79
|
+
'healthy': health.is_healthy,
|
|
80
|
+
'response_time_ms': health.response_time_ms,
|
|
81
|
+
'error': health.error_message
|
|
82
|
+
})
|
|
83
|
+
print(f" Health: {status}")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
print(f" Health: ❓ unknown ({e})")
|
|
86
|
+
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
formatter.error(f"Failed to list servers: {e}")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
async def config_add_command(args: argparse.Namespace) -> None:
|
|
95
|
+
"""Handle config add command."""
|
|
96
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
100
|
+
|
|
101
|
+
# Validate arguments
|
|
102
|
+
if not validate_config_args(args.type, args.base_url, args.model, getattr(args, 'api_key', None)):
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
|
|
105
|
+
# Check if server name already exists
|
|
106
|
+
existing_servers = config_manager.registry.list_servers()
|
|
107
|
+
if not validate_server_name(args.name, existing_servers):
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
|
|
110
|
+
# Prepare metadata for BGE-IN-ICL specific options
|
|
111
|
+
metadata = {}
|
|
112
|
+
if args.type == 'bge-in-icl':
|
|
113
|
+
if hasattr(args, 'batch_size') and args.batch_size:
|
|
114
|
+
metadata['batch_size'] = args.batch_size
|
|
115
|
+
|
|
116
|
+
# Create server configuration
|
|
117
|
+
server_config = ServerConfig(
|
|
118
|
+
name=args.name,
|
|
119
|
+
type=args.type,
|
|
120
|
+
base_url=args.base_url,
|
|
121
|
+
model=args.model or ('bge-in-icl' if args.type == 'bge-in-icl' else None),
|
|
122
|
+
api_key=getattr(args, 'api_key', None),
|
|
123
|
+
enabled=True,
|
|
124
|
+
metadata=metadata
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Add server to configuration
|
|
128
|
+
config_manager.add_server(server_config)
|
|
129
|
+
|
|
130
|
+
# Set as default if requested
|
|
131
|
+
if getattr(args, 'default', False):
|
|
132
|
+
config_manager.registry.set_default_server(args.name)
|
|
133
|
+
|
|
134
|
+
# Save configuration
|
|
135
|
+
config_manager.save_config()
|
|
136
|
+
|
|
137
|
+
formatter.success(f"Added server '{args.name}' successfully")
|
|
138
|
+
|
|
139
|
+
if getattr(args, 'default', False):
|
|
140
|
+
formatter.info(f"Set '{args.name}' as default server")
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
formatter.error(f"Failed to add server: {e}")
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
async def config_remove_command(args: argparse.Namespace) -> None:
|
|
148
|
+
"""Handle config remove command."""
|
|
149
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
153
|
+
|
|
154
|
+
if args.name not in config_manager.registry.list_servers():
|
|
155
|
+
formatter.error(f"Server '{args.name}' not found")
|
|
156
|
+
sys.exit(1)
|
|
157
|
+
|
|
158
|
+
config_manager.remove_server(args.name)
|
|
159
|
+
config_manager.save_config()
|
|
160
|
+
|
|
161
|
+
formatter.success(f"Removed server '{args.name}' successfully")
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
formatter.error(f"Failed to remove server: {e}")
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
async def config_test_command(args: argparse.Namespace) -> None:
|
|
169
|
+
"""Handle config test command."""
|
|
170
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
174
|
+
|
|
175
|
+
server_name = getattr(args, 'name', None)
|
|
176
|
+
if not server_name:
|
|
177
|
+
server_name = config_manager.registry._default_server
|
|
178
|
+
if not server_name:
|
|
179
|
+
formatter.error("No default server configured and no server specified")
|
|
180
|
+
sys.exit(1)
|
|
181
|
+
|
|
182
|
+
if server_name not in config_manager.registry.list_servers():
|
|
183
|
+
formatter.error(f"Server '{server_name}' not found")
|
|
184
|
+
sys.exit(1)
|
|
185
|
+
|
|
186
|
+
formatter.info(f"Testing server '{server_name}'...")
|
|
187
|
+
|
|
188
|
+
# Perform health check
|
|
189
|
+
health = await config_manager.registry.check_server_health(server_name)
|
|
190
|
+
|
|
191
|
+
if health.is_healthy:
|
|
192
|
+
formatter.success(f"Server '{server_name}' is healthy ({health.response_time_ms:.1f}ms)")
|
|
193
|
+
else:
|
|
194
|
+
formatter.error(f"Server '{server_name}' is unhealthy: {health.error_message}")
|
|
195
|
+
sys.exit(1)
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
formatter.error(f"Failed to test server: {e}")
|
|
199
|
+
sys.exit(1)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def config_health_command(args: argparse.Namespace) -> None:
|
|
203
|
+
"""Handle config health command."""
|
|
204
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
208
|
+
|
|
209
|
+
if getattr(args, 'monitor', False):
|
|
210
|
+
formatter.info("Starting health monitoring... (Press Ctrl+C to stop)")
|
|
211
|
+
await config_manager.start_monitoring()
|
|
212
|
+
try:
|
|
213
|
+
while True:
|
|
214
|
+
await asyncio.sleep(10)
|
|
215
|
+
except KeyboardInterrupt:
|
|
216
|
+
formatter.info("Health monitoring stopped")
|
|
217
|
+
await config_manager.stop_monitoring()
|
|
218
|
+
else:
|
|
219
|
+
# Single health check for all servers
|
|
220
|
+
servers = config_manager.registry.list_servers()
|
|
221
|
+
if not servers:
|
|
222
|
+
formatter.info("No servers configured")
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
formatter.info("Checking server health...")
|
|
226
|
+
|
|
227
|
+
for name in servers:
|
|
228
|
+
try:
|
|
229
|
+
health = await config_manager.registry.check_server_health(name)
|
|
230
|
+
status = format_health_status({
|
|
231
|
+
'healthy': health.is_healthy,
|
|
232
|
+
'response_time_ms': health.response_time_ms,
|
|
233
|
+
'error': health.error_message
|
|
234
|
+
})
|
|
235
|
+
print(f" {name}: {status}")
|
|
236
|
+
except Exception as e:
|
|
237
|
+
print(f" {name}: ❓ Error checking health: {e}")
|
|
238
|
+
|
|
239
|
+
except Exception as e:
|
|
240
|
+
formatter.error(f"Failed to check health: {e}")
|
|
241
|
+
sys.exit(1)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def config_enable_command(args: argparse.Namespace) -> None:
|
|
245
|
+
"""Handle config enable command."""
|
|
246
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
250
|
+
|
|
251
|
+
if args.name not in config_manager.registry.list_servers():
|
|
252
|
+
formatter.error(f"Server '{args.name}' not found")
|
|
253
|
+
sys.exit(1)
|
|
254
|
+
|
|
255
|
+
server = config_manager.registry.get_server(args.name)
|
|
256
|
+
if server.enabled:
|
|
257
|
+
formatter.info(f"Server '{args.name}' is already enabled")
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
config_manager.registry.enable_server(args.name)
|
|
261
|
+
config_manager.save_config()
|
|
262
|
+
|
|
263
|
+
formatter.success(f"Enabled server '{args.name}'")
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
formatter.error(f"Failed to enable server: {e}")
|
|
267
|
+
sys.exit(1)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
async def config_disable_command(args: argparse.Namespace) -> None:
|
|
271
|
+
"""Handle config disable command."""
|
|
272
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
276
|
+
|
|
277
|
+
if args.name not in config_manager.registry.list_servers():
|
|
278
|
+
formatter.error(f"Server '{args.name}' not found")
|
|
279
|
+
sys.exit(1)
|
|
280
|
+
|
|
281
|
+
server = config_manager.registry.get_server(args.name)
|
|
282
|
+
if not server.enabled:
|
|
283
|
+
formatter.info(f"Server '{args.name}' is already disabled")
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
# Check if this is the default server
|
|
287
|
+
if config_manager.registry._default_server == args.name:
|
|
288
|
+
formatter.warning(f"Disabling default server '{args.name}' - you may want to set a new default")
|
|
289
|
+
|
|
290
|
+
config_manager.registry.disable_server(args.name)
|
|
291
|
+
config_manager.save_config()
|
|
292
|
+
|
|
293
|
+
formatter.success(f"Disabled server '{args.name}'")
|
|
294
|
+
|
|
295
|
+
except Exception as e:
|
|
296
|
+
formatter.error(f"Failed to disable server: {e}")
|
|
297
|
+
sys.exit(1)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
async def config_set_default_command(args: argparse.Namespace) -> None:
|
|
301
|
+
"""Handle config set-default command."""
|
|
302
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
306
|
+
|
|
307
|
+
if args.name not in config_manager.registry.list_servers():
|
|
308
|
+
formatter.error(f"Server '{args.name}' not found")
|
|
309
|
+
sys.exit(1)
|
|
310
|
+
|
|
311
|
+
server = config_manager.registry.get_server(args.name)
|
|
312
|
+
if not server.enabled:
|
|
313
|
+
formatter.error(f"Cannot set disabled server '{args.name}' as default")
|
|
314
|
+
sys.exit(1)
|
|
315
|
+
|
|
316
|
+
config_manager.registry.set_default_server(args.name)
|
|
317
|
+
config_manager.save_config()
|
|
318
|
+
|
|
319
|
+
formatter.success(f"Set '{args.name}' as default server")
|
|
320
|
+
|
|
321
|
+
except Exception as e:
|
|
322
|
+
formatter.error(f"Failed to set default server: {e}")
|
|
323
|
+
sys.exit(1)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
async def config_validate_command(args: argparse.Namespace) -> None:
|
|
327
|
+
"""Handle config validate command."""
|
|
328
|
+
formatter = OutputFormatter(verbose=getattr(args, 'verbose', False))
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
config_manager = get_config_manager(str(args.config) if args.config else None)
|
|
332
|
+
|
|
333
|
+
# Use enhanced validation
|
|
334
|
+
validation_result = config_manager.validate_config_file(args.config)
|
|
335
|
+
|
|
336
|
+
formatter.info(f"Validating configuration: {validation_result.get('config_path', 'default')}")
|
|
337
|
+
print()
|
|
338
|
+
|
|
339
|
+
if validation_result.get('valid', False):
|
|
340
|
+
formatter.success("Configuration is valid")
|
|
341
|
+
|
|
342
|
+
# Show summary
|
|
343
|
+
servers = validation_result.get('servers', [])
|
|
344
|
+
formatter.info(f"Found {len(servers)} server(s)")
|
|
345
|
+
|
|
346
|
+
for server_info in servers:
|
|
347
|
+
status = "✅" if server_info.get('valid', False) else "❌"
|
|
348
|
+
print(f" {status} {server_info.get('name', 'unknown')}")
|
|
349
|
+
|
|
350
|
+
if not server_info.get('valid', False):
|
|
351
|
+
errors = server_info.get('errors', [])
|
|
352
|
+
for error in errors:
|
|
353
|
+
print(f" Error: {error}")
|
|
354
|
+
else:
|
|
355
|
+
formatter.error("Configuration has errors:")
|
|
356
|
+
errors = validation_result.get('errors', [])
|
|
357
|
+
for error in errors:
|
|
358
|
+
print(f" • {error}")
|
|
359
|
+
sys.exit(1)
|
|
360
|
+
|
|
361
|
+
except Exception as e:
|
|
362
|
+
formatter.error(f"Failed to validate configuration: {e}")
|
|
363
|
+
sys.exit(1)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# Simplified implementations for remaining commands
|
|
367
|
+
async def config_benchmark_command(args: argparse.Namespace) -> None:
|
|
368
|
+
"""Handle config benchmark command."""
|
|
369
|
+
formatter = OutputFormatter()
|
|
370
|
+
formatter.info("Benchmark command not yet implemented in modular CLI")
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
async def config_switch_command(args: argparse.Namespace) -> None:
|
|
374
|
+
"""Handle config switch command."""
|
|
375
|
+
formatter = OutputFormatter()
|
|
376
|
+
formatter.info("Switch command not yet implemented in modular CLI")
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
async def config_discover_command(args: argparse.Namespace) -> None:
|
|
380
|
+
"""Handle config discover command."""
|
|
381
|
+
formatter = OutputFormatter()
|
|
382
|
+
formatter.info("Discover command not yet implemented in modular CLI")
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
async def config_export_command(args: argparse.Namespace) -> None:
|
|
386
|
+
"""Handle config export command."""
|
|
387
|
+
formatter = OutputFormatter()
|
|
388
|
+
formatter.info("Export command not yet implemented in modular CLI")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
async def config_import_command(args: argparse.Namespace) -> None:
|
|
392
|
+
"""Handle config import command."""
|
|
393
|
+
formatter = OutputFormatter()
|
|
394
|
+
formatter.info("Import command not yet implemented in modular CLI")
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
async def config_template_command(args: argparse.Namespace) -> None:
|
|
398
|
+
"""Handle config template command."""
|
|
399
|
+
formatter = OutputFormatter()
|
|
400
|
+
formatter.info("Template command not yet implemented in modular CLI")
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def config_batch_test_command(args: argparse.Namespace) -> None:
|
|
404
|
+
"""Handle config batch-test command."""
|
|
405
|
+
formatter = OutputFormatter()
|
|
406
|
+
formatter.info("Batch-test command not yet implemented in modular CLI")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def add_config_subparser(subparsers) -> argparse.ArgumentParser:
|
|
410
|
+
"""Add config command subparser to the main parser.
|
|
411
|
+
|
|
412
|
+
Args:
|
|
413
|
+
subparsers: Subparsers object from the main argument parser
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
The configured config subparser
|
|
417
|
+
"""
|
|
418
|
+
config_parser = subparsers.add_parser(
|
|
419
|
+
"config",
|
|
420
|
+
help="Manage embedding server configurations",
|
|
421
|
+
description="Configure and manage embedding server connections"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
config_subparsers = config_parser.add_subparsers(dest="config_command", help="Configuration commands")
|
|
425
|
+
|
|
426
|
+
# Config list command
|
|
427
|
+
list_parser = config_subparsers.add_parser("list", help="List all configured servers")
|
|
428
|
+
list_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
429
|
+
list_parser.add_argument("--show-health", action="store_true", help="Show health status for each server")
|
|
430
|
+
|
|
431
|
+
# Config add command
|
|
432
|
+
add_parser = config_subparsers.add_parser("add", help="Add a new embedding server")
|
|
433
|
+
add_parser.add_argument("name", help="Server name")
|
|
434
|
+
add_parser.add_argument("--type", required=True, choices=["openai", "openai-compatible", "tei", "bge-in-icl"], help="Server type")
|
|
435
|
+
add_parser.add_argument("--base-url", required=True, help="Server base URL")
|
|
436
|
+
add_parser.add_argument("--model", help="Model name (auto-detected for TEI, defaults to 'bge-in-icl' for BGE-IN-ICL)")
|
|
437
|
+
add_parser.add_argument("--api-key", help="API key for authentication")
|
|
438
|
+
add_parser.add_argument("--default", action="store_true", help="Set as default server")
|
|
439
|
+
add_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
440
|
+
add_parser.add_argument("--batch-size", type=int, help="Batch size for embeddings")
|
|
441
|
+
|
|
442
|
+
# Config remove command
|
|
443
|
+
remove_parser = config_subparsers.add_parser("remove", help="Remove a server")
|
|
444
|
+
remove_parser.add_argument("name", help="Server name to remove")
|
|
445
|
+
remove_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
446
|
+
|
|
447
|
+
# Config test command
|
|
448
|
+
test_parser = config_subparsers.add_parser("test", help="Test server connectivity")
|
|
449
|
+
test_parser.add_argument("name", nargs="?", help="Server name to test (uses default if not specified)")
|
|
450
|
+
test_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
451
|
+
|
|
452
|
+
# Config health command
|
|
453
|
+
health_parser = config_subparsers.add_parser("health", help="Check server health")
|
|
454
|
+
health_parser.add_argument("--monitor", action="store_true", help="Start continuous monitoring")
|
|
455
|
+
health_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
456
|
+
|
|
457
|
+
# Config enable command
|
|
458
|
+
enable_parser = config_subparsers.add_parser("enable", help="Enable a server")
|
|
459
|
+
enable_parser.add_argument("name", help="Server name to enable")
|
|
460
|
+
enable_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
461
|
+
|
|
462
|
+
# Config disable command
|
|
463
|
+
disable_parser = config_subparsers.add_parser("disable", help="Disable a server")
|
|
464
|
+
disable_parser.add_argument("name", help="Server name to disable")
|
|
465
|
+
disable_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
466
|
+
|
|
467
|
+
# Config set-default command
|
|
468
|
+
default_parser = config_subparsers.add_parser("set-default", help="Set default server")
|
|
469
|
+
default_parser.add_argument("name", help="Server name to set as default")
|
|
470
|
+
default_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
471
|
+
|
|
472
|
+
# Config validate command
|
|
473
|
+
validate_parser = config_subparsers.add_parser("validate", help="Validate configuration")
|
|
474
|
+
validate_parser.add_argument("--config", type=Path, help="Configuration file path")
|
|
475
|
+
|
|
476
|
+
# Add simplified parsers for remaining commands
|
|
477
|
+
config_subparsers.add_parser("benchmark", help="Benchmark server performance")
|
|
478
|
+
config_subparsers.add_parser("switch", help="Switch between server configurations")
|
|
479
|
+
config_subparsers.add_parser("discover", help="Discover configuration files")
|
|
480
|
+
config_subparsers.add_parser("export", help="Export configuration")
|
|
481
|
+
config_subparsers.add_parser("import", help="Import configuration")
|
|
482
|
+
config_subparsers.add_parser("template", help="Generate configuration templates")
|
|
483
|
+
config_subparsers.add_parser("batch-test", help="Test all enabled servers")
|
|
484
|
+
|
|
485
|
+
return config_parser
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
__all__ = ["config_command", "add_config_subparser"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""MCP command module - handles Model Context Protocol server operations."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def mcp_command(args: argparse.Namespace) -> None:
|
|
9
|
+
"""Execute the MCP server command.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
args: Parsed command-line arguments containing database path
|
|
13
|
+
"""
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
|
|
17
|
+
# Use the standalone MCP launcher that sets environment before any imports
|
|
18
|
+
mcp_launcher_path = Path(__file__).parent.parent.parent.parent.parent / "mcp_launcher.py"
|
|
19
|
+
cmd = [sys.executable, str(mcp_launcher_path), "--db", str(args.db)]
|
|
20
|
+
|
|
21
|
+
process = subprocess.run(
|
|
22
|
+
cmd,
|
|
23
|
+
stdin=sys.stdin,
|
|
24
|
+
stdout=sys.stdout,
|
|
25
|
+
stderr=sys.stderr # Allow stderr through for proper error handling
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Exit with the same code as the subprocess
|
|
29
|
+
sys.exit(process.returncode)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def add_mcp_subparser(subparsers) -> argparse.ArgumentParser:
|
|
33
|
+
"""Add MCP command subparser to the main parser.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
subparsers: Subparsers object from the main argument parser
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
The configured MCP subparser
|
|
40
|
+
"""
|
|
41
|
+
mcp_parser = subparsers.add_parser(
|
|
42
|
+
"mcp",
|
|
43
|
+
help="Run Model Context Protocol server",
|
|
44
|
+
description="Start the MCP server for integration with MCP-compatible clients"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
mcp_parser.add_argument(
|
|
48
|
+
"--db",
|
|
49
|
+
type=Path,
|
|
50
|
+
default=Path.home() / ".cache" / "chunkhound" / "chunks.duckdb",
|
|
51
|
+
help="DuckDB database file path (default: ~/.cache/chunkhound/chunks.duckdb)",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return mcp_parser
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
__all__ = ["mcp_command", "add_mcp_subparser"]
|