mcp-vector-search 0.12.6__py3-none-any.whl → 1.1.22__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_vector_search/__init__.py +3 -3
- mcp_vector_search/analysis/__init__.py +111 -0
- mcp_vector_search/analysis/baseline/__init__.py +68 -0
- mcp_vector_search/analysis/baseline/comparator.py +462 -0
- mcp_vector_search/analysis/baseline/manager.py +621 -0
- mcp_vector_search/analysis/collectors/__init__.py +74 -0
- mcp_vector_search/analysis/collectors/base.py +164 -0
- mcp_vector_search/analysis/collectors/cohesion.py +463 -0
- mcp_vector_search/analysis/collectors/complexity.py +743 -0
- mcp_vector_search/analysis/collectors/coupling.py +1162 -0
- mcp_vector_search/analysis/collectors/halstead.py +514 -0
- mcp_vector_search/analysis/collectors/smells.py +325 -0
- mcp_vector_search/analysis/debt.py +516 -0
- mcp_vector_search/analysis/interpretation.py +685 -0
- mcp_vector_search/analysis/metrics.py +414 -0
- mcp_vector_search/analysis/reporters/__init__.py +7 -0
- mcp_vector_search/analysis/reporters/console.py +646 -0
- mcp_vector_search/analysis/reporters/markdown.py +480 -0
- mcp_vector_search/analysis/reporters/sarif.py +377 -0
- mcp_vector_search/analysis/storage/__init__.py +93 -0
- mcp_vector_search/analysis/storage/metrics_store.py +762 -0
- mcp_vector_search/analysis/storage/schema.py +245 -0
- mcp_vector_search/analysis/storage/trend_tracker.py +560 -0
- mcp_vector_search/analysis/trends.py +308 -0
- mcp_vector_search/analysis/visualizer/__init__.py +90 -0
- mcp_vector_search/analysis/visualizer/d3_data.py +534 -0
- mcp_vector_search/analysis/visualizer/exporter.py +484 -0
- mcp_vector_search/analysis/visualizer/html_report.py +2895 -0
- mcp_vector_search/analysis/visualizer/schemas.py +525 -0
- mcp_vector_search/cli/commands/analyze.py +1062 -0
- mcp_vector_search/cli/commands/chat.py +1455 -0
- mcp_vector_search/cli/commands/index.py +621 -5
- mcp_vector_search/cli/commands/index_background.py +467 -0
- mcp_vector_search/cli/commands/init.py +13 -0
- mcp_vector_search/cli/commands/install.py +597 -335
- mcp_vector_search/cli/commands/install_old.py +8 -4
- mcp_vector_search/cli/commands/mcp.py +78 -6
- mcp_vector_search/cli/commands/reset.py +68 -26
- mcp_vector_search/cli/commands/search.py +224 -8
- mcp_vector_search/cli/commands/setup.py +1184 -0
- mcp_vector_search/cli/commands/status.py +339 -5
- mcp_vector_search/cli/commands/uninstall.py +276 -357
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +292 -0
- mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
- mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
- mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +33 -0
- mcp_vector_search/cli/commands/visualize/graph_builder.py +647 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +600 -0
- mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
- mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
- mcp_vector_search/cli/commands/visualize/templates/base.py +234 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +4542 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +2522 -0
- mcp_vector_search/cli/didyoumean.py +27 -2
- mcp_vector_search/cli/main.py +127 -160
- mcp_vector_search/cli/output.py +158 -13
- mcp_vector_search/config/__init__.py +4 -0
- mcp_vector_search/config/default_thresholds.yaml +52 -0
- mcp_vector_search/config/settings.py +12 -0
- mcp_vector_search/config/thresholds.py +273 -0
- mcp_vector_search/core/__init__.py +16 -0
- mcp_vector_search/core/auto_indexer.py +3 -3
- mcp_vector_search/core/boilerplate.py +186 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/database.py +406 -94
- mcp_vector_search/core/embeddings.py +24 -0
- mcp_vector_search/core/exceptions.py +11 -0
- mcp_vector_search/core/git.py +380 -0
- mcp_vector_search/core/git_hooks.py +4 -4
- mcp_vector_search/core/indexer.py +632 -54
- mcp_vector_search/core/llm_client.py +756 -0
- mcp_vector_search/core/models.py +91 -1
- mcp_vector_search/core/project.py +17 -0
- mcp_vector_search/core/relationships.py +473 -0
- mcp_vector_search/core/scheduler.py +11 -11
- mcp_vector_search/core/search.py +179 -29
- mcp_vector_search/mcp/server.py +819 -9
- mcp_vector_search/parsers/python.py +285 -5
- mcp_vector_search/utils/__init__.py +2 -0
- mcp_vector_search/utils/gitignore.py +0 -3
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +66 -4
- mcp_vector_search/utils/timing.py +10 -6
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/METADATA +184 -53
- mcp_vector_search-1.1.22.dist-info/RECORD +120 -0
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/WHEEL +1 -1
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/entry_points.txt +1 -0
- mcp_vector_search/cli/commands/visualize.py +0 -1467
- mcp_vector_search-0.12.6.dist-info/RECORD +0 -68
- {mcp_vector_search-0.12.6.dist-info → mcp_vector_search-1.1.22.dist-info}/licenses/LICENSE +0 -0
|
@@ -62,6 +62,11 @@ class EnhancedDidYouMeanTyper(typer.Typer):
|
|
|
62
62
|
if click_group is None:
|
|
63
63
|
return None
|
|
64
64
|
|
|
65
|
+
# If click_group is an integer, it's an exit code from standalone_mode=False
|
|
66
|
+
# Return it as-is to preserve exit code propagation
|
|
67
|
+
if isinstance(click_group, int):
|
|
68
|
+
return click_group
|
|
69
|
+
|
|
65
70
|
# Create enhanced DYM group with original group's properties
|
|
66
71
|
enhanced_group = EnhancedDidYouMeanGroup(
|
|
67
72
|
name=click_group.name,
|
|
@@ -212,6 +217,17 @@ COMMON_TYPOS = {
|
|
|
212
217
|
"grep": "search",
|
|
213
218
|
"s": "search", # Single letter shortcut
|
|
214
219
|
"f": "search", # Alternative shortcut for find
|
|
220
|
+
# Chat command variations
|
|
221
|
+
"cht": "chat",
|
|
222
|
+
"caht": "chat",
|
|
223
|
+
"chta": "chat",
|
|
224
|
+
"ask": "chat",
|
|
225
|
+
"question": "chat",
|
|
226
|
+
"qa": "chat",
|
|
227
|
+
"llm": "chat",
|
|
228
|
+
"gpt": "chat",
|
|
229
|
+
"explain": "chat",
|
|
230
|
+
"answer": "chat",
|
|
215
231
|
# Index command variations
|
|
216
232
|
"indx": "index",
|
|
217
233
|
"idx": "index",
|
|
@@ -333,9 +349,18 @@ COMMAND_INFO = {
|
|
|
333
349
|
"examples": [
|
|
334
350
|
'mcp-vector-search search "authentication function"',
|
|
335
351
|
'mcp-vector-search search "error handling" --limit 5',
|
|
336
|
-
'mcp-vector-search
|
|
352
|
+
'mcp-vector-search search --files "*.ts" "query"',
|
|
353
|
+
],
|
|
354
|
+
"related": ["chat", "index", "status"],
|
|
355
|
+
},
|
|
356
|
+
"chat": {
|
|
357
|
+
"description": "Ask AI questions about your code (requires API key)",
|
|
358
|
+
"examples": [
|
|
359
|
+
'mcp-vector-search chat "where is the database configured?"',
|
|
360
|
+
'mcp-vector-search chat "how does authentication work?"',
|
|
361
|
+
'mcp-vector-search chat --limit 3 "explain error handling"',
|
|
337
362
|
],
|
|
338
|
-
"related": ["search
|
|
363
|
+
"related": ["search", "status", "index"],
|
|
339
364
|
},
|
|
340
365
|
"index": {
|
|
341
366
|
"description": "Index codebase for semantic search",
|
mcp_vector_search/cli/main.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"""Main CLI application for MCP Vector Search."""
|
|
2
2
|
|
|
3
|
+
import faulthandler
|
|
4
|
+
import signal
|
|
5
|
+
import sys
|
|
3
6
|
from pathlib import Path
|
|
4
7
|
|
|
5
8
|
import typer
|
|
@@ -9,9 +12,52 @@ from rich.traceback import install
|
|
|
9
12
|
|
|
10
13
|
from .. import __build__, __version__
|
|
11
14
|
from .didyoumean import add_common_suggestions, create_enhanced_typer
|
|
12
|
-
from .output import
|
|
15
|
+
from .output import setup_logging
|
|
13
16
|
from .suggestions import get_contextual_suggestions
|
|
14
17
|
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# SIGNAL HANDLERS - Register early for crash diagnostics
|
|
21
|
+
# ============================================================================
|
|
22
|
+
def _handle_segfault(signum: int, frame) -> None:
|
|
23
|
+
"""Handle segmentation faults with helpful error message.
|
|
24
|
+
|
|
25
|
+
Segmentation faults typically occur due to corrupted ChromaDB index data
|
|
26
|
+
or issues with native libraries (sentence-transformers, tree-sitter).
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
signum: Signal number (SIGSEGV = 11)
|
|
30
|
+
frame: Current stack frame (unused)
|
|
31
|
+
"""
|
|
32
|
+
error_message = """
|
|
33
|
+
╭─────────────────────────────────────────────────────────────────╮
|
|
34
|
+
│ ⚠️ Segmentation Fault Detected │
|
|
35
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
36
|
+
│ This usually indicates corrupted index data or a crash in │
|
|
37
|
+
│ native libraries (ChromaDB, sentence-transformers, tree-sitter).│
|
|
38
|
+
│ │
|
|
39
|
+
│ To fix this, please run: │
|
|
40
|
+
│ 1. mcp-vector-search reset index --force │
|
|
41
|
+
│ 2. mcp-vector-search index │
|
|
42
|
+
│ │
|
|
43
|
+
│ This will rebuild your search index from scratch. │
|
|
44
|
+
│ │
|
|
45
|
+
│ If the problem persists: │
|
|
46
|
+
│ - Try updating dependencies: pip install -U mcp-vector-search │
|
|
47
|
+
│ - Check GitHub issues: github.com/bobmatnyc/mcp-vector-search │
|
|
48
|
+
╰─────────────────────────────────────────────────────────────────╯
|
|
49
|
+
"""
|
|
50
|
+
print(error_message, file=sys.stderr)
|
|
51
|
+
sys.exit(139) # Standard segfault exit code (128 + 11)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Register signal handler for segmentation faults
|
|
55
|
+
signal.signal(signal.SIGSEGV, _handle_segfault)
|
|
56
|
+
|
|
57
|
+
# Enable faulthandler for better crash diagnostics
|
|
58
|
+
# This prints Python traceback on segfaults before signal handler runs
|
|
59
|
+
faulthandler.enable()
|
|
60
|
+
|
|
15
61
|
# Install rich traceback handler
|
|
16
62
|
install(show_locals=True)
|
|
17
63
|
|
|
@@ -22,45 +68,65 @@ console = Console()
|
|
|
22
68
|
app = create_enhanced_typer(
|
|
23
69
|
name="mcp-vector-search",
|
|
24
70
|
help="""
|
|
25
|
-
🔍 [bold]
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
unfamiliar
|
|
29
|
-
|
|
30
|
-
[bold cyan]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
[bold cyan]
|
|
71
|
+
🔍 [bold]MCP Vector Search - Semantic Code Search CLI[/bold]
|
|
72
|
+
|
|
73
|
+
Search your codebase by meaning, not just keywords. Find similar code patterns,
|
|
74
|
+
explore unfamiliar projects, and integrate with AI coding tools via MCP.
|
|
75
|
+
|
|
76
|
+
[bold cyan]QUICK START:[/bold cyan]
|
|
77
|
+
mcp-vector-search setup # One-time setup (recommended)
|
|
78
|
+
mcp-vector-search search "query" # Search by meaning
|
|
79
|
+
mcp-vector-search chat "question" # Ask AI about your code
|
|
80
|
+
|
|
81
|
+
[bold cyan]MAIN COMMANDS:[/bold cyan]
|
|
82
|
+
setup 🚀 Zero-config setup (indexes + configures MCP)
|
|
83
|
+
search 🔍 Semantic search (finds code by meaning)
|
|
84
|
+
chat/ask 🤖 LLM-powered Q&A about your code (needs API key)
|
|
85
|
+
status 📊 Show project status
|
|
86
|
+
visualize 📊 Interactive code graph
|
|
87
|
+
|
|
88
|
+
[bold cyan]AI CHAT SETUP:[/bold cyan]
|
|
89
|
+
The 'chat' command requires an OpenRouter API key:
|
|
90
|
+
1. Get key: [cyan]https://openrouter.ai/keys[/cyan]
|
|
91
|
+
2. Set: [yellow]export OPENROUTER_API_KEY='your-key'[/yellow]
|
|
92
|
+
|
|
93
|
+
[bold cyan]EXAMPLES:[/bold cyan]
|
|
94
|
+
mcp-vector-search search "error handling"
|
|
95
|
+
mcp-vector-search search --files "*.ts" "authentication"
|
|
96
|
+
mcp-vector-search chat "where is the database configured?"
|
|
97
|
+
mcp-vector-search ask "how does auth work in this project?"
|
|
98
|
+
|
|
99
|
+
[bold cyan]MORE COMMANDS:[/bold cyan]
|
|
36
100
|
install 📦 Install project and MCP integrations
|
|
37
101
|
uninstall 🗑️ Remove MCP integrations
|
|
38
|
-
init 🔧 Initialize project (
|
|
102
|
+
init 🔧 Initialize project (advanced)
|
|
39
103
|
demo 🎬 Run interactive demo
|
|
40
104
|
doctor 🩺 Check system health
|
|
41
|
-
status 📊 Show project status
|
|
42
|
-
search 🔍 Search code semantically
|
|
43
105
|
index 📇 Index codebase
|
|
106
|
+
reset 🔄 Reset and recovery operations
|
|
44
107
|
mcp 🔌 MCP server operations
|
|
45
108
|
config ⚙️ Configure settings
|
|
46
|
-
visualize 📊 Visualize code relationships
|
|
47
109
|
help ❓ Get help
|
|
48
110
|
version ℹ️ Show version
|
|
49
111
|
|
|
50
|
-
[dim]For
|
|
112
|
+
[dim]For more: [cyan]mcp-vector-search COMMAND --help[/cyan][/dim]
|
|
51
113
|
""",
|
|
52
114
|
add_completion=False,
|
|
53
115
|
rich_markup_mode="rich",
|
|
54
116
|
)
|
|
55
117
|
|
|
56
118
|
# Import command modules
|
|
119
|
+
from .commands.analyze import analyze_app # noqa: E402
|
|
120
|
+
from .commands.chat import chat_app # noqa: E402
|
|
57
121
|
from .commands.config import config_app # noqa: E402
|
|
58
122
|
from .commands.demo import demo_app # noqa: E402
|
|
59
123
|
from .commands.index import index_app # noqa: E402
|
|
60
124
|
from .commands.init import init_app # noqa: E402
|
|
61
125
|
from .commands.install import install_app # noqa: E402
|
|
62
126
|
from .commands.mcp import mcp_app # noqa: E402
|
|
127
|
+
from .commands.reset import reset_app # noqa: E402
|
|
63
128
|
from .commands.search import search_app, search_main # noqa: E402, F401
|
|
129
|
+
from .commands.setup import setup_app # noqa: E402
|
|
64
130
|
from .commands.status import main as status_main # noqa: E402
|
|
65
131
|
from .commands.uninstall import uninstall_app # noqa: E402
|
|
66
132
|
from .commands.visualize import app as visualize_app # noqa: E402
|
|
@@ -69,6 +135,9 @@ from .commands.visualize import app as visualize_app # noqa: E402
|
|
|
69
135
|
# MAIN COMMANDS - Clean hierarchy
|
|
70
136
|
# ============================================================================
|
|
71
137
|
|
|
138
|
+
# 0. SETUP - Smart zero-config setup (RECOMMENDED!)
|
|
139
|
+
app.add_typer(setup_app, name="setup", help="🚀 Smart zero-config setup (recommended)")
|
|
140
|
+
|
|
72
141
|
# 1. INSTALL - Install project and MCP integrations (NEW!)
|
|
73
142
|
app.add_typer(
|
|
74
143
|
install_app, name="install", help="📦 Install project and MCP integrations"
|
|
@@ -95,6 +164,12 @@ app.command("status", help="📊 Show project status and statistics")(status_mai
|
|
|
95
164
|
# Register search as both a command and a typer group
|
|
96
165
|
app.add_typer(search_app, name="search", help="🔍 Search code semantically")
|
|
97
166
|
|
|
167
|
+
# 7.5. CHAT - LLM-powered intelligent search
|
|
168
|
+
app.add_typer(chat_app, name="chat", help="🤖 Ask questions about code with LLM")
|
|
169
|
+
app.add_typer(
|
|
170
|
+
chat_app, name="ask", help="🤖 Ask questions about code with LLM (alias for chat)"
|
|
171
|
+
)
|
|
172
|
+
|
|
98
173
|
# 8. INDEX - Index codebase
|
|
99
174
|
app.add_typer(index_app, name="index", help="📇 Index codebase for semantic search")
|
|
100
175
|
|
|
@@ -104,152 +179,26 @@ app.add_typer(mcp_app, name="mcp", help="🔌 MCP server operations")
|
|
|
104
179
|
# 10. CONFIG - Configuration
|
|
105
180
|
app.add_typer(config_app, name="config", help="⚙️ Manage project configuration")
|
|
106
181
|
|
|
107
|
-
#
|
|
182
|
+
# 10.5. RESET - Reset and recovery operations
|
|
183
|
+
app.add_typer(reset_app, name="reset", help="🔄 Reset and recovery operations")
|
|
184
|
+
|
|
185
|
+
# 11. ANALYZE - Code complexity analysis
|
|
186
|
+
app.add_typer(
|
|
187
|
+
analyze_app, name="analyze", help="📈 Analyze code complexity and quality"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# 12. VISUALIZE - Code graph visualization
|
|
108
191
|
app.add_typer(
|
|
109
192
|
visualize_app, name="visualize", help="📊 Visualize code chunk relationships"
|
|
110
193
|
)
|
|
111
194
|
|
|
112
|
-
#
|
|
195
|
+
# 13. HELP - Enhanced help
|
|
113
196
|
# (defined below inline)
|
|
114
197
|
|
|
115
|
-
#
|
|
198
|
+
# 14. VERSION - Version info
|
|
116
199
|
# (defined below inline)
|
|
117
200
|
|
|
118
201
|
|
|
119
|
-
# ============================================================================
|
|
120
|
-
# DEPRECATED COMMANDS - With helpful suggestions
|
|
121
|
-
# ============================================================================
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def _deprecated_command(old_cmd: str, new_cmd: str):
|
|
125
|
-
"""Helper to create deprecated command with suggestion."""
|
|
126
|
-
|
|
127
|
-
def wrapper(*args, **kwargs):
|
|
128
|
-
print_warning(
|
|
129
|
-
f"⚠️ The command '{old_cmd}' is deprecated.\n"
|
|
130
|
-
f" Please use '{new_cmd}' instead.\n"
|
|
131
|
-
f" Run: [cyan]mcp-vector-search {new_cmd} --help[/cyan] for details."
|
|
132
|
-
)
|
|
133
|
-
raise typer.Exit(1)
|
|
134
|
-
|
|
135
|
-
return wrapper
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# NOTE: 'install' command is now the primary command for project installation
|
|
139
|
-
# Old 'install' was deprecated in favor of 'init' in v0.7.0
|
|
140
|
-
# Now 'install' is back as the hierarchical installation command in v0.13.0
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# Deprecated: find -> search
|
|
144
|
-
@app.command("find", hidden=True)
|
|
145
|
-
def deprecated_find():
|
|
146
|
-
"""[DEPRECATED] Use 'search' instead."""
|
|
147
|
-
_deprecated_command("find", "search")()
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# Deprecated: search-similar -> search --similar
|
|
151
|
-
@app.command("search-similar", hidden=True)
|
|
152
|
-
def deprecated_search_similar():
|
|
153
|
-
"""[DEPRECATED] Use 'search --similar' instead."""
|
|
154
|
-
_deprecated_command("search-similar", "search --similar")()
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# Deprecated: search-context -> search --context
|
|
158
|
-
@app.command("search-context", hidden=True)
|
|
159
|
-
def deprecated_search_context():
|
|
160
|
-
"""[DEPRECATED] Use 'search --context' instead."""
|
|
161
|
-
_deprecated_command("search-context", "search --context")()
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
# Deprecated: interactive -> search interactive
|
|
165
|
-
@app.command("interactive", hidden=True)
|
|
166
|
-
def deprecated_interactive():
|
|
167
|
-
"""[DEPRECATED] Use 'search interactive' instead."""
|
|
168
|
-
_deprecated_command("interactive", "search interactive")()
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# Deprecated: history -> search history
|
|
172
|
-
@app.command("history", hidden=True)
|
|
173
|
-
def deprecated_history():
|
|
174
|
-
"""[DEPRECATED] Use 'search history' instead."""
|
|
175
|
-
_deprecated_command("history", "search history")()
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
# Deprecated: favorites -> search favorites
|
|
179
|
-
@app.command("favorites", hidden=True)
|
|
180
|
-
def deprecated_favorites():
|
|
181
|
-
"""[DEPRECATED] Use 'search favorites' instead."""
|
|
182
|
-
_deprecated_command("favorites", "search favorites")()
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
# Deprecated: add-favorite -> search favorites add
|
|
186
|
-
@app.command("add-favorite", hidden=True)
|
|
187
|
-
def deprecated_add_favorite():
|
|
188
|
-
"""[DEPRECATED] Use 'search favorites add' instead."""
|
|
189
|
-
_deprecated_command("add-favorite", "search favorites add")()
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
# Deprecated: remove-favorite -> search favorites remove
|
|
193
|
-
@app.command("remove-favorite", hidden=True)
|
|
194
|
-
def deprecated_remove_favorite():
|
|
195
|
-
"""[DEPRECATED] Use 'search favorites remove' instead."""
|
|
196
|
-
_deprecated_command("remove-favorite", "search favorites remove")()
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
# Deprecated: health -> index health
|
|
200
|
-
@app.command("health", hidden=True)
|
|
201
|
-
def deprecated_health():
|
|
202
|
-
"""[DEPRECATED] Use 'index health' instead."""
|
|
203
|
-
_deprecated_command("health", "index health")()
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
# Deprecated: watch -> index watch
|
|
207
|
-
@app.command("watch", hidden=True)
|
|
208
|
-
def deprecated_watch():
|
|
209
|
-
"""[DEPRECATED] Use 'index watch' instead."""
|
|
210
|
-
_deprecated_command("watch", "index watch")()
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# Deprecated: auto-index -> index auto
|
|
214
|
-
@app.command("auto-index", hidden=True)
|
|
215
|
-
def deprecated_auto_index():
|
|
216
|
-
"""[DEPRECATED] Use 'index auto' instead."""
|
|
217
|
-
_deprecated_command("auto-index", "index auto")()
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
# Deprecated: reset -> mcp reset or config reset
|
|
221
|
-
@app.command("reset", hidden=True)
|
|
222
|
-
def deprecated_reset():
|
|
223
|
-
"""[DEPRECATED] Use 'mcp reset' or 'config reset' instead."""
|
|
224
|
-
print_warning(
|
|
225
|
-
"⚠️ The 'reset' command is deprecated.\n"
|
|
226
|
-
" Use [cyan]mcp-vector-search mcp reset[/cyan] for MCP reset\n"
|
|
227
|
-
" Use [cyan]mcp-vector-search config reset[/cyan] for config reset"
|
|
228
|
-
)
|
|
229
|
-
raise typer.Exit(1)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
# Deprecated: init-check -> init check
|
|
233
|
-
@app.command("init-check", hidden=True)
|
|
234
|
-
def deprecated_init_check():
|
|
235
|
-
"""[DEPRECATED] Use 'init check' instead."""
|
|
236
|
-
_deprecated_command("init-check", "init check")()
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
# Deprecated: init-mcp -> mcp install
|
|
240
|
-
@app.command("init-mcp", hidden=True)
|
|
241
|
-
def deprecated_init_mcp():
|
|
242
|
-
"""[DEPRECATED] Use 'mcp install' instead."""
|
|
243
|
-
_deprecated_command("init-mcp", "mcp install")()
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
# Deprecated: init-models -> config models
|
|
247
|
-
@app.command("init-models", hidden=True)
|
|
248
|
-
def deprecated_init_models():
|
|
249
|
-
"""[DEPRECATED] Use 'config models' instead."""
|
|
250
|
-
_deprecated_command("init-models", "config models")()
|
|
251
|
-
|
|
252
|
-
|
|
253
202
|
# ============================================================================
|
|
254
203
|
# MAIN INLINE COMMANDS
|
|
255
204
|
# ============================================================================
|
|
@@ -329,6 +278,15 @@ def version_command() -> None:
|
|
|
329
278
|
console.print("[dim]Built with ChromaDB, Tree-sitter, and modern Python[/dim]")
|
|
330
279
|
|
|
331
280
|
|
|
281
|
+
def _version_callback(value: bool) -> None:
|
|
282
|
+
"""Handle --version flag eagerly before command parsing."""
|
|
283
|
+
if value:
|
|
284
|
+
console.print(
|
|
285
|
+
f"[bold blue]mcp-vector-search[/bold blue] version [green]{__version__}[/green] [dim](build {__build__})[/dim]"
|
|
286
|
+
)
|
|
287
|
+
raise typer.Exit()
|
|
288
|
+
|
|
289
|
+
|
|
332
290
|
@app.callback()
|
|
333
291
|
def main(
|
|
334
292
|
ctx: typer.Context,
|
|
@@ -338,6 +296,8 @@ def main(
|
|
|
338
296
|
"-v",
|
|
339
297
|
help="Show version and exit",
|
|
340
298
|
rich_help_panel="ℹ️ Information",
|
|
299
|
+
is_eager=True,
|
|
300
|
+
callback=lambda v: _version_callback(v),
|
|
341
301
|
),
|
|
342
302
|
verbose: bool = typer.Option(
|
|
343
303
|
False,
|
|
@@ -368,9 +328,8 @@ def main(
|
|
|
368
328
|
A modern, lightweight tool for semantic code search using ChromaDB and Tree-sitter.
|
|
369
329
|
Designed for local development with optional MCP server integration.
|
|
370
330
|
"""
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
raise typer.Exit()
|
|
331
|
+
# Note: --version is handled by _version_callback with is_eager=True
|
|
332
|
+
# This ensures it runs before no_args_is_help check
|
|
374
333
|
|
|
375
334
|
# Setup logging
|
|
376
335
|
log_level = "DEBUG" if verbose else "ERROR" if quiet else "WARNING"
|
|
@@ -401,7 +360,11 @@ def cli_with_suggestions():
|
|
|
401
360
|
|
|
402
361
|
try:
|
|
403
362
|
# Call the app with standalone_mode=False to get exceptions instead of sys.exit
|
|
404
|
-
|
|
363
|
+
# Capture return value - when standalone_mode=False, typer.Exit returns code instead of raising
|
|
364
|
+
exit_code = app(standalone_mode=False)
|
|
365
|
+
# Propagate non-zero exit codes (e.g., from --fail-on-smell quality gate)
|
|
366
|
+
if exit_code is not None and exit_code != 0:
|
|
367
|
+
sys.exit(exit_code)
|
|
405
368
|
except click.UsageError as e:
|
|
406
369
|
# Check if it's a "No such command" error
|
|
407
370
|
if "No such command" in str(e):
|
|
@@ -439,8 +402,12 @@ def cli_with_suggestions():
|
|
|
439
402
|
except click.Abort:
|
|
440
403
|
# User interrupted (Ctrl+C)
|
|
441
404
|
sys.exit(1)
|
|
442
|
-
except SystemExit:
|
|
443
|
-
# Re-raise system exits
|
|
405
|
+
except (SystemExit, click.exceptions.Exit) as e:
|
|
406
|
+
# Re-raise system exits and typer.Exit with their exit codes
|
|
407
|
+
if hasattr(e, "exit_code"):
|
|
408
|
+
sys.exit(e.exit_code)
|
|
409
|
+
elif hasattr(e, "code"):
|
|
410
|
+
sys.exit(e.code if e.code is not None else 0)
|
|
444
411
|
raise
|
|
445
412
|
except Exception as e:
|
|
446
413
|
# For other exceptions, show error and exit if verbose logging is enabled
|
mcp_vector_search/cli/output.py
CHANGED
|
@@ -24,6 +24,46 @@ from ..core.models import ProjectInfo, SearchResult
|
|
|
24
24
|
console = Console()
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def _get_grade_color(grade: str) -> str:
|
|
28
|
+
"""Get color for complexity grade."""
|
|
29
|
+
grade_colors = {
|
|
30
|
+
"A": "green",
|
|
31
|
+
"B": "cyan",
|
|
32
|
+
"C": "yellow",
|
|
33
|
+
"D": "orange",
|
|
34
|
+
"F": "red",
|
|
35
|
+
}
|
|
36
|
+
return grade_colors.get(grade, "white")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _get_complexity_color(complexity: int) -> str:
|
|
40
|
+
"""Get color based on cognitive complexity value."""
|
|
41
|
+
if complexity <= 5:
|
|
42
|
+
return "green"
|
|
43
|
+
elif complexity <= 10:
|
|
44
|
+
return "cyan"
|
|
45
|
+
elif complexity <= 20:
|
|
46
|
+
return "yellow"
|
|
47
|
+
elif complexity <= 30:
|
|
48
|
+
return "orange"
|
|
49
|
+
else:
|
|
50
|
+
return "red"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_quality_color(quality: int) -> str:
|
|
54
|
+
"""Get color based on quality score (0-100)."""
|
|
55
|
+
if quality >= 80:
|
|
56
|
+
return "green"
|
|
57
|
+
elif quality >= 60:
|
|
58
|
+
return "cyan"
|
|
59
|
+
elif quality >= 40:
|
|
60
|
+
return "yellow"
|
|
61
|
+
elif quality >= 20:
|
|
62
|
+
return "orange"
|
|
63
|
+
else:
|
|
64
|
+
return "red"
|
|
65
|
+
|
|
66
|
+
|
|
27
67
|
def setup_logging(level: str = "WARNING") -> None:
|
|
28
68
|
"""Setup structured logging with rich formatting.
|
|
29
69
|
|
|
@@ -97,9 +137,11 @@ def print_project_info(project_info: ProjectInfo) -> None:
|
|
|
97
137
|
table.add_row("Initialized", "✓" if project_info.is_initialized else "✗")
|
|
98
138
|
table.add_row(
|
|
99
139
|
"Languages",
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
(
|
|
141
|
+
", ".join(project_info.languages)
|
|
142
|
+
if project_info.languages
|
|
143
|
+
else "None detected"
|
|
144
|
+
),
|
|
103
145
|
)
|
|
104
146
|
table.add_row("Indexable Files", str(project_info.file_count))
|
|
105
147
|
|
|
@@ -111,8 +153,17 @@ def print_search_results(
|
|
|
111
153
|
query: str,
|
|
112
154
|
show_content: bool = True,
|
|
113
155
|
max_content_lines: int = 10,
|
|
156
|
+
quality_weight: float = 0.0,
|
|
114
157
|
) -> None:
|
|
115
|
-
"""Print search results in a formatted display.
|
|
158
|
+
"""Print search results in a formatted display with quality-aware ranking.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
results: List of search results
|
|
162
|
+
query: Original search query
|
|
163
|
+
show_content: Whether to show code content
|
|
164
|
+
max_content_lines: Maximum lines of code to show
|
|
165
|
+
quality_weight: Weight for quality ranking (0.0-1.0), used to show score breakdown
|
|
166
|
+
"""
|
|
116
167
|
if not results:
|
|
117
168
|
print_warning(f"No results found for query: '{query}'")
|
|
118
169
|
return
|
|
@@ -120,7 +171,14 @@ def print_search_results(
|
|
|
120
171
|
console.print(
|
|
121
172
|
f"\n[bold blue]Search Results for:[/bold blue] [green]'{query}'[/green]"
|
|
122
173
|
)
|
|
123
|
-
|
|
174
|
+
|
|
175
|
+
# Show quality ranking info if enabled
|
|
176
|
+
if quality_weight > 0.0:
|
|
177
|
+
console.print(
|
|
178
|
+
f"[dim]Found {len(results)} results (quality-aware ranking: {quality_weight:.0%} quality, {(1 - quality_weight):.0%} relevance)[/dim]\n"
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
console.print(f"[dim]Found {len(results)} results[/dim]\n")
|
|
124
182
|
|
|
125
183
|
for i, result in enumerate(results, 1):
|
|
126
184
|
# Create result header
|
|
@@ -130,12 +188,85 @@ def print_search_results(
|
|
|
130
188
|
if result.class_name:
|
|
131
189
|
header += f" in [yellow]{result.class_name}[/yellow]"
|
|
132
190
|
|
|
133
|
-
# Add location
|
|
191
|
+
# Add location
|
|
134
192
|
location = f"[dim]{result.location}[/dim]"
|
|
135
|
-
similarity = f"[green]{result.similarity_score:.2%}[/green]"
|
|
136
193
|
|
|
137
194
|
console.print(f"{header}")
|
|
138
|
-
|
|
195
|
+
|
|
196
|
+
# Build metadata line with quality metrics
|
|
197
|
+
metadata_parts = [location]
|
|
198
|
+
|
|
199
|
+
# Show score breakdown if quality ranking is enabled
|
|
200
|
+
if quality_weight > 0.0 and hasattr(result, "_original_similarity"):
|
|
201
|
+
# Quality-aware ranking: show relevance, quality, and combined
|
|
202
|
+
relevance_score = result._original_similarity
|
|
203
|
+
combined_score = result.similarity_score
|
|
204
|
+
quality_score = result.quality_score or 0
|
|
205
|
+
|
|
206
|
+
metadata_parts.append(f"Relevance: [cyan]{relevance_score:.2%}[/cyan]")
|
|
207
|
+
metadata_parts.append(
|
|
208
|
+
f"Quality: [{_get_quality_color(quality_score)}]{quality_score}[/{_get_quality_color(quality_score)}]"
|
|
209
|
+
)
|
|
210
|
+
metadata_parts.append(f"Combined: [green]{combined_score:.2%}[/green]")
|
|
211
|
+
else:
|
|
212
|
+
# Pure semantic search: show only similarity score
|
|
213
|
+
similarity = f"[green]{result.similarity_score:.2%}[/green]"
|
|
214
|
+
metadata_parts.append(f"Similarity: {similarity}")
|
|
215
|
+
|
|
216
|
+
console.print(f" {' | '.join(metadata_parts)}")
|
|
217
|
+
|
|
218
|
+
# Add quality indicator line if quality metrics are available and not shown in scores
|
|
219
|
+
if result.complexity_grade and quality_weight == 0.0:
|
|
220
|
+
# Show quality metrics when not using quality ranking
|
|
221
|
+
quality_indicators = []
|
|
222
|
+
|
|
223
|
+
grade_color = _get_grade_color(result.complexity_grade)
|
|
224
|
+
quality_indicators.append(
|
|
225
|
+
f"Grade: [{grade_color}]{result.complexity_grade}[/{grade_color}]"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
if result.cognitive_complexity is not None:
|
|
229
|
+
complexity_color = _get_complexity_color(result.cognitive_complexity)
|
|
230
|
+
quality_indicators.append(
|
|
231
|
+
f"Complexity: [{complexity_color}]{result.cognitive_complexity}[/{complexity_color}]"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Show quality indicator with check/cross
|
|
235
|
+
smell_count = result.smell_count or 0
|
|
236
|
+
if smell_count == 0:
|
|
237
|
+
console.print(
|
|
238
|
+
f" [green]✓[/green] {' | '.join(quality_indicators)} | No smells"
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
# List smells if available
|
|
242
|
+
smells_text = f"{smell_count} smells"
|
|
243
|
+
if result.code_smells:
|
|
244
|
+
smell_names = ", ".join(result.code_smells[:3]) # Show first 3
|
|
245
|
+
if len(result.code_smells) > 3:
|
|
246
|
+
smell_names += f", +{len(result.code_smells) - 3} more"
|
|
247
|
+
smells_text = f"{smell_count} smells: [dim]{smell_names}[/dim]"
|
|
248
|
+
|
|
249
|
+
console.print(
|
|
250
|
+
f" [red]✗[/red] {' | '.join(quality_indicators)} | {smells_text}"
|
|
251
|
+
)
|
|
252
|
+
elif result.complexity_grade and quality_weight > 0.0:
|
|
253
|
+
# When using quality ranking, show simpler quality indicator
|
|
254
|
+
smell_count = result.smell_count or 0
|
|
255
|
+
if smell_count == 0:
|
|
256
|
+
console.print(
|
|
257
|
+
f" [green]✓[/green] Grade {result.complexity_grade}, No smells"
|
|
258
|
+
)
|
|
259
|
+
else:
|
|
260
|
+
smells_text = (
|
|
261
|
+
", ".join(result.code_smells[:3])
|
|
262
|
+
if result.code_smells
|
|
263
|
+
else f"{smell_count} smells"
|
|
264
|
+
)
|
|
265
|
+
if result.code_smells and len(result.code_smells) > 3:
|
|
266
|
+
smells_text += f", +{len(result.code_smells) - 3} more"
|
|
267
|
+
console.print(
|
|
268
|
+
f" [red]✗[/red] Grade {result.complexity_grade}, {smells_text}"
|
|
269
|
+
)
|
|
139
270
|
|
|
140
271
|
# Show code content if requested
|
|
141
272
|
if show_content and result.content:
|
|
@@ -248,16 +379,30 @@ def print_dependency_status(
|
|
|
248
379
|
|
|
249
380
|
|
|
250
381
|
def print_json(data: Any, title: str | None = None) -> None:
|
|
251
|
-
"""Print data as formatted JSON.
|
|
252
|
-
|
|
382
|
+
"""Print data as formatted JSON.
|
|
383
|
+
|
|
384
|
+
When title is None (typical for --json flag), outputs raw JSON to stdout
|
|
385
|
+
for machine consumption (e.g., piping to json.tool).
|
|
386
|
+
When title is provided, uses Rich formatting for human-readable display.
|
|
253
387
|
|
|
254
|
-
|
|
255
|
-
|
|
388
|
+
Args:
|
|
389
|
+
data: Data to serialize as JSON
|
|
390
|
+
title: Optional title for Rich panel display
|
|
391
|
+
"""
|
|
392
|
+
import json
|
|
256
393
|
|
|
257
394
|
if title:
|
|
395
|
+
# Human-readable display with Rich formatting
|
|
396
|
+
json_str = json.dumps(data, indent=2, default=str)
|
|
397
|
+
syntax = Syntax(json_str, "json", theme="monokai")
|
|
258
398
|
console.print(Panel(syntax, title=title, border_style="blue"))
|
|
259
399
|
else:
|
|
260
|
-
|
|
400
|
+
# Machine-readable output: raw JSON to stdout
|
|
401
|
+
# Use sys.stdout.write to bypass Rich console formatting
|
|
402
|
+
json_str = json.dumps(data, indent=2, default=str, ensure_ascii=False)
|
|
403
|
+
sys.stdout.write(json_str)
|
|
404
|
+
sys.stdout.write("\n")
|
|
405
|
+
sys.stdout.flush()
|
|
261
406
|
|
|
262
407
|
|
|
263
408
|
def print_panel(
|