sirchmunk 0.0.1.post1__py3-none-any.whl → 0.0.2__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 (43) hide show
  1. sirchmunk/api/__init__.py +1 -0
  2. sirchmunk/api/chat.py +1123 -0
  3. sirchmunk/api/components/__init__.py +0 -0
  4. sirchmunk/api/components/history_storage.py +402 -0
  5. sirchmunk/api/components/monitor_tracker.py +518 -0
  6. sirchmunk/api/components/settings_storage.py +353 -0
  7. sirchmunk/api/history.py +254 -0
  8. sirchmunk/api/knowledge.py +411 -0
  9. sirchmunk/api/main.py +120 -0
  10. sirchmunk/api/monitor.py +219 -0
  11. sirchmunk/api/run_server.py +54 -0
  12. sirchmunk/api/search.py +230 -0
  13. sirchmunk/api/settings.py +309 -0
  14. sirchmunk/api/tools.py +315 -0
  15. sirchmunk/cli/__init__.py +11 -0
  16. sirchmunk/cli/cli.py +789 -0
  17. sirchmunk/learnings/knowledge_base.py +5 -2
  18. sirchmunk/llm/prompts.py +12 -1
  19. sirchmunk/retrieve/text_retriever.py +186 -2
  20. sirchmunk/scan/file_scanner.py +2 -2
  21. sirchmunk/schema/knowledge.py +119 -35
  22. sirchmunk/search.py +384 -26
  23. sirchmunk/storage/__init__.py +2 -2
  24. sirchmunk/storage/{knowledge_manager.py → knowledge_storage.py} +265 -60
  25. sirchmunk/utils/constants.py +7 -5
  26. sirchmunk/utils/embedding_util.py +217 -0
  27. sirchmunk/utils/tokenizer_util.py +36 -1
  28. sirchmunk/version.py +1 -1
  29. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/METADATA +124 -9
  30. sirchmunk-0.0.2.dist-info/RECORD +69 -0
  31. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/WHEEL +1 -1
  32. sirchmunk-0.0.2.dist-info/top_level.txt +2 -0
  33. sirchmunk_mcp/__init__.py +25 -0
  34. sirchmunk_mcp/cli.py +478 -0
  35. sirchmunk_mcp/config.py +276 -0
  36. sirchmunk_mcp/server.py +355 -0
  37. sirchmunk_mcp/service.py +327 -0
  38. sirchmunk_mcp/setup.py +15 -0
  39. sirchmunk_mcp/tools.py +410 -0
  40. sirchmunk-0.0.1.post1.dist-info/RECORD +0 -45
  41. sirchmunk-0.0.1.post1.dist-info/top_level.txt +0 -1
  42. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/entry_points.txt +0 -0
  43. {sirchmunk-0.0.1.post1.dist-info → sirchmunk-0.0.2.dist-info}/licenses/LICENSE +0 -0
sirchmunk_mcp/cli.py ADDED
@@ -0,0 +1,478 @@
1
+ # Copyright (c) ModelScope Contributors. All rights reserved.
2
+ """
3
+ Command-line interface for Sirchmunk MCP Server.
4
+
5
+ Provides commands for server management, initialization, and diagnostics.
6
+ """
7
+
8
+ import argparse
9
+ import asyncio
10
+ import logging
11
+ import os
12
+ import shutil
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ from . import __version__
17
+ from .config import Config
18
+ from .server import run_stdio_server, run_http_server
19
+
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def get_mcp_version():
25
+ try:
26
+ import importlib.metadata
27
+ return importlib.metadata.version("mcp")
28
+ except ImportError:
29
+ return None
30
+
31
+
32
+ def _setup_stdio_safe_environment():
33
+ """Configure environment for safe stdio MCP communication.
34
+
35
+ In MCP stdio mode, stdout is reserved exclusively for JSON-RPC messages.
36
+ Any non-JSON output to stdout will break the protocol. This function
37
+ sets environment variables to suppress verbose output from third-party
38
+ libraries (ModelScope, transformers, tqdm, etc.) that might print to stdout.
39
+ """
40
+ # Set environment variables to suppress third-party library outputs
41
+ # These must be set BEFORE importing the libraries
42
+ os.environ["MODELSCOPE_LOG_LEVEL"] = "ERROR"
43
+ os.environ["MODELSCOPE_CACHE"] = os.path.expanduser("~/.sirchmunk/.cache/models")
44
+ os.environ["TRANSFORMERS_VERBOSITY"] = "error"
45
+ os.environ["TRANSFORMERS_NO_ADVISORY_WARNINGS"] = "1"
46
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
47
+
48
+ # Suppress warnings
49
+ import warnings
50
+ warnings.filterwarnings("ignore")
51
+
52
+
53
+ def cmd_serve(args: argparse.Namespace) -> int:
54
+ """Run MCP server.
55
+
56
+ Args:
57
+ args: Command-line arguments
58
+
59
+ Returns:
60
+ Exit code
61
+ """
62
+ try:
63
+ # Override transport from command-line if specified
64
+ if args.transport:
65
+ os.environ["MCP_TRANSPORT"] = args.transport
66
+
67
+ if args.host:
68
+ os.environ["MCP_HOST"] = args.host
69
+
70
+ if args.port:
71
+ os.environ["MCP_PORT"] = str(args.port)
72
+
73
+ # Determine transport mode early (before loading config triggers imports)
74
+ transport = args.transport or os.environ.get("MCP_TRANSPORT", "stdio")
75
+
76
+ # Set up safe environment BEFORE any sirchmunk imports for stdio mode
77
+ # This prevents ModelScope/transformers from printing to stdout
78
+ if transport == "stdio":
79
+ os.environ["MCP_TRANSPORT"] = "stdio" # Signal to service.py
80
+ _setup_stdio_safe_environment()
81
+
82
+ # Load configuration (may trigger sirchmunk imports)
83
+ config = Config.from_env()
84
+
85
+ # Configure logging - MUST use stderr for stdio transport
86
+ # stdout is reserved for JSON-RPC messages in MCP stdio mode
87
+ log_level = args.log_level or config.mcp.log_level
88
+ logging.basicConfig(
89
+ level=getattr(logging, log_level.upper()),
90
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
91
+ stream=sys.stderr, # Critical: logs must go to stderr, not stdout
92
+ )
93
+
94
+ logger.info(f"Sirchmunk MCP Server v{__version__}")
95
+ logger.info(f"Transport: {config.mcp.transport}")
96
+
97
+ # Run server
98
+ if config.mcp.transport == "stdio":
99
+ # Check if running in interactive terminal
100
+ if sys.stdin.isatty():
101
+ print()
102
+ print("=" * 60)
103
+ print(" ⚠️ Sirchmunk MCP Server - STDIO Mode")
104
+ print("=" * 60)
105
+ print()
106
+ print(" STDIO mode is designed to be launched by an MCP client")
107
+ print(" (e.g., Claude Desktop, Cursor IDE), not run directly")
108
+ print(" in an interactive terminal.")
109
+ print()
110
+ print(" The server expects JSON-RPC messages from an MCP client.")
111
+ print(" Running in a terminal will cause errors.")
112
+ print()
113
+ print(" Options:")
114
+ print(" 1. Configure your MCP client to launch this server")
115
+ print(" 2. Use HTTP mode: sirchmunk-mcp serve --transport http")
116
+ print()
117
+ print("=" * 60)
118
+ print()
119
+
120
+ # Ask user if they want to continue anyway
121
+ try:
122
+ response = input("Continue anyway? (y/N): ").strip().lower()
123
+ if response != 'y':
124
+ print("Server not started. Use an MCP client to launch this server.")
125
+ return 0
126
+ print()
127
+ print("Starting server... Press Ctrl+C to stop.")
128
+ print("(Any input you type will cause errors)")
129
+ print()
130
+ except (EOFError, KeyboardInterrupt):
131
+ print("\nServer not started.")
132
+ return 0
133
+
134
+ asyncio.run(run_stdio_server(config))
135
+ elif config.mcp.transport == "http":
136
+ asyncio.run(run_http_server(config))
137
+ else:
138
+ logger.error(f"Unknown transport: {config.mcp.transport}")
139
+ return 1
140
+
141
+ return 0
142
+
143
+ except KeyboardInterrupt:
144
+ logger.info("Server stopped by user")
145
+ return 0
146
+
147
+ except Exception as e:
148
+ logger.error(f"Server error: {e}", exc_info=True)
149
+ return 1
150
+
151
+
152
+ def cmd_init(args: argparse.Namespace) -> int:
153
+ """Initialize Sirchmunk environment.
154
+
155
+ Creates working directory, configuration files, and performs
156
+ dependency checks.
157
+
158
+ Args:
159
+ args: Command-line arguments
160
+
161
+ Returns:
162
+ Exit code
163
+ """
164
+ try:
165
+ print(f"Initializing Sirchmunk MCP v{__version__}")
166
+ print()
167
+
168
+ # Determine work path
169
+ if args.work_path:
170
+ work_path = Path(args.work_path)
171
+ else:
172
+ work_path = Path(os.getenv("SIRCHMUNK_WORK_PATH", str(Path.home() / ".sirchmunk")))
173
+
174
+ work_path = work_path.expanduser().resolve()
175
+
176
+ print(f"Work path: {work_path}")
177
+
178
+ # Create work directory
179
+ work_path.mkdir(parents=True, exist_ok=True)
180
+ print(f"✓ Created work directory")
181
+
182
+ # Create subdirectories
183
+ (work_path / ".cache").mkdir(exist_ok=True)
184
+ (work_path / ".cache" / "models").mkdir(exist_ok=True)
185
+ (work_path / "data").mkdir(exist_ok=True)
186
+ print(f"✓ Created subdirectories")
187
+
188
+ # Check dependencies
189
+ print()
190
+ print("Checking dependencies...")
191
+
192
+ # Check ripgrep-all
193
+ if shutil.which("rga"):
194
+ print("✓ ripgrep-all (rga) is installed")
195
+ else:
196
+ print("✗ ripgrep-all (rga) is not installed")
197
+ print(" Installing ripgrep-all...")
198
+ try:
199
+ from sirchmunk.utils.install_rga import install_rga
200
+ install_rga()
201
+ print("✓ ripgrep-all installed successfully")
202
+ except Exception as e:
203
+ print(f"✗ Failed to install ripgrep-all: {e}")
204
+ print(" Please install manually: https://github.com/phiresky/ripgrep-all")
205
+
206
+ # Check Python packages
207
+ mcp_version = get_mcp_version()
208
+ if mcp_version:
209
+ print(f"✓ MCP package installed (v{mcp_version})")
210
+ else:
211
+ print("✗ MCP package not found")
212
+ print(" Install with: pip install mcp")
213
+
214
+ try:
215
+ import sirchmunk
216
+ print(f"✓ sirchmunk package installed")
217
+ except ImportError:
218
+ print("✗ sirchmunk package not found")
219
+ print(" Install with: pip install sirchmunk")
220
+
221
+ # Check environment variables
222
+ print()
223
+ print("Checking environment variables...")
224
+
225
+ llm_api_key = os.getenv("LLM_API_KEY")
226
+ if llm_api_key:
227
+ print(f"✓ LLM_API_KEY is set ({llm_api_key[:8]}...)")
228
+ else:
229
+ print("✗ LLM_API_KEY is not set")
230
+ print(" Set it in your .mcp_env file or environment")
231
+
232
+ print()
233
+ print("Initialization complete!")
234
+ print()
235
+ print("Next steps:")
236
+ print("1. Configure LLM_API_KEY in .mcp_env file")
237
+ print("2. Run 'sirchmunk-mcp serve' to start the server")
238
+ print("3. Configure your MCP client (e.g., Claude Desktop)")
239
+
240
+ return 0
241
+
242
+ except Exception as e:
243
+ logger.error(f"Initialization failed: {e}", exc_info=True)
244
+ return 1
245
+
246
+
247
+ def cmd_config(args: argparse.Namespace) -> int:
248
+ """Show or generate configuration.
249
+
250
+ Args:
251
+ args: Command-line arguments
252
+
253
+ Returns:
254
+ Exit code
255
+ """
256
+ try:
257
+ if args.generate:
258
+ # Determine work path
259
+ work_path = Path(os.getenv("SIRCHMUNK_WORK_PATH", str(Path.home() / ".sirchmunk")))
260
+ work_path = work_path.expanduser().resolve()
261
+ work_path.mkdir(parents=True, exist_ok=True)
262
+
263
+ # Generate .mcp_env file in work_path
264
+ env_file = work_path / ".mcp_env"
265
+
266
+ env_content = """# ===== LLM Configuration =====
267
+ LLM_BASE_URL=https://api.openai.com/v1
268
+ LLM_API_KEY=your-api-key
269
+ LLM_MODEL_NAME=gpt-5.2
270
+
271
+ # ===== Sirchmunk Settings =====
272
+ SIRCHMUNK_WORK_PATH=~/.sirchmunk
273
+ SIRCHMUNK_VERBOSE=false
274
+ SIRCHMUNK_ENABLE_CLUSTER_REUSE=true
275
+
276
+ # ===== Cluster Similarity Settings =====
277
+ CLUSTER_SIM_THRESHOLD=0.85
278
+ CLUSTER_SIM_TOP_K=3
279
+ MAX_QUERIES_PER_CLUSTER=5
280
+
281
+ # ===== Search Settings =====
282
+ DEFAULT_MAX_DEPTH=5
283
+ DEFAULT_TOP_K_FILES=3
284
+ DEFAULT_KEYWORD_LEVELS=3
285
+ GREP_TIMEOUT=60.0
286
+
287
+ # ===== MCP Server Settings =====
288
+ MCP_SERVER_NAME=sirchmunk
289
+ MCP_SERVER_VERSION=0.1.0
290
+ MCP_LOG_LEVEL=INFO
291
+ MCP_TRANSPORT=stdio
292
+ """
293
+
294
+ env_file.write_text(env_content)
295
+ print(f"Generated {env_file}")
296
+
297
+ # Generate MCP client config
298
+ if args.output:
299
+ output_path = Path(args.output)
300
+ else:
301
+ output_path = Path.cwd() / "mcp_config.json"
302
+
303
+ mcp_config = """{
304
+ "mcpServers": {
305
+ "sirchmunk": {
306
+ "command": "sirchmunk-mcp",
307
+ "args": ["serve"],
308
+ "env": {
309
+ "LLM_BASE_URL": "https://api.openai.com/v1",
310
+ "LLM_API_KEY": "your-api-key",
311
+ "LLM_MODEL_NAME": "gpt-5.2",
312
+ "SIRCHMUNK_WORK_PATH": "~/.sirchmunk",
313
+ "SIRCHMUNK_VERBOSE": "false"
314
+ }
315
+ }
316
+ }
317
+ }
318
+ """
319
+
320
+ output_path.write_text(mcp_config)
321
+ print(f"Generated {output_path}")
322
+
323
+ print()
324
+ print("Configuration files generated successfully!")
325
+ print()
326
+ print(f"⚠️ Please edit {env_file} to set your LLM_API_KEY")
327
+ print()
328
+ print("To use with Claude Desktop:")
329
+ print("1. Edit mcp_config.json with your API key")
330
+ if sys.platform == "darwin":
331
+ config_dir = Path.home() / "Library/Application Support/Claude"
332
+ elif sys.platform == "linux":
333
+ config_dir = Path.home() / ".config/Claude"
334
+ else:
335
+ config_dir = Path(os.getenv("APPDATA", "")) / "Claude"
336
+
337
+ print(f"2. Copy to: {config_dir}/claude_desktop_config.json")
338
+ print("3. Restart Claude Desktop")
339
+
340
+ else:
341
+ # Show current configuration
342
+ config = Config.from_env()
343
+ print("Current Configuration:")
344
+ print()
345
+ print(f"LLM:")
346
+ print(f" Base URL: {config.llm.base_url}")
347
+ print(f" Model: {config.llm.model_name}")
348
+ print(f" API Key: {config.llm.api_key[:8]}..." if config.llm.api_key else " API Key: (not set)")
349
+ print()
350
+ print(f"Sirchmunk:")
351
+ print(f" Work Path: {config.sirchmunk.work_path}")
352
+ print(f" Verbose: {config.sirchmunk.verbose}")
353
+ print(f" Cluster Reuse: {config.sirchmunk.enable_cluster_reuse}")
354
+ print()
355
+ print(f"MCP Server:")
356
+ print(f" Name: {config.mcp.server_name}")
357
+ print(f" Version: {config.mcp.server_version}")
358
+ print(f" Transport: {config.mcp.transport}")
359
+ print(f" Log Level: {config.mcp.log_level}")
360
+
361
+ return 0
362
+
363
+ except Exception as e:
364
+ logger.error(f"Config command failed: {e}", exc_info=True)
365
+ return 1
366
+
367
+
368
+ def cmd_version(args: argparse.Namespace) -> int:
369
+ """Show version information.
370
+
371
+ Args:
372
+ args: Command-line arguments
373
+
374
+ Returns:
375
+ Exit code
376
+ """
377
+ print(f"Sirchmunk MCP Server v{__version__}")
378
+
379
+ mcp_version = get_mcp_version()
380
+ if mcp_version:
381
+ print(f"MCP package v{mcp_version}")
382
+ else:
383
+ print("MCP package (not installed)")
384
+
385
+ try:
386
+ import sirchmunk
387
+ print(f"Sirchmunk Core (installed)")
388
+ except ImportError:
389
+ pass
390
+
391
+ return 0
392
+
393
+
394
+ def main() -> int:
395
+ """Main CLI entry point.
396
+
397
+ Returns:
398
+ Exit code
399
+ """
400
+ parser = argparse.ArgumentParser(
401
+ description="Sirchmunk MCP Server - Intelligent code search as an MCP service",
402
+ formatter_class=argparse.RawDescriptionHelpFormatter,
403
+ )
404
+
405
+ parser.add_argument(
406
+ "--version",
407
+ action="store_true",
408
+ help="Show version information",
409
+ )
410
+
411
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
412
+
413
+ # serve command
414
+ serve_parser = subparsers.add_parser("serve", help="Run MCP server")
415
+ serve_parser.add_argument(
416
+ "--transport",
417
+ choices=["stdio", "http"],
418
+ help="Transport protocol (default: stdio)",
419
+ )
420
+ serve_parser.add_argument(
421
+ "--host",
422
+ help="Host for HTTP transport (default: localhost)",
423
+ )
424
+ serve_parser.add_argument(
425
+ "--port",
426
+ type=int,
427
+ help="Port for HTTP transport (default: 8080)",
428
+ )
429
+ serve_parser.add_argument(
430
+ "--log-level",
431
+ choices=["DEBUG", "INFO", "WARNING", "ERROR"],
432
+ help="Logging level",
433
+ )
434
+
435
+ # init command
436
+ init_parser = subparsers.add_parser("init", help="Initialize Sirchmunk environment")
437
+ init_parser.add_argument(
438
+ "--work-path",
439
+ help="Working directory path (default: ~/.sirchmunk)",
440
+ )
441
+
442
+ # config command
443
+ config_parser = subparsers.add_parser("config", help="Manage configuration")
444
+ config_parser.add_argument(
445
+ "--generate",
446
+ action="store_true",
447
+ help="Generate configuration templates",
448
+ )
449
+ config_parser.add_argument(
450
+ "--output",
451
+ help="Output path for generated config",
452
+ )
453
+
454
+ # version command
455
+ version_parser = subparsers.add_parser("version", help="Show version information")
456
+
457
+ args = parser.parse_args()
458
+
459
+ # Handle --version flag
460
+ if args.version:
461
+ return cmd_version(args)
462
+
463
+ # Handle commands
464
+ if args.command == "serve":
465
+ return cmd_serve(args)
466
+ elif args.command == "init":
467
+ return cmd_init(args)
468
+ elif args.command == "config":
469
+ return cmd_config(args)
470
+ elif args.command == "version":
471
+ return cmd_version(args)
472
+ else:
473
+ parser.print_help()
474
+ return 1
475
+
476
+
477
+ if __name__ == "__main__":
478
+ sys.exit(main())