mcp-vector-search 0.8.2__py3-none-any.whl → 0.8.3__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.

Potentially problematic release.


This version of mcp-vector-search might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  """MCP Vector Search - CLI-first semantic code search with MCP integration."""
2
2
 
3
- __version__ = "0.8.2"
4
- __build__ = "31"
3
+ __version__ = "0.8.3"
4
+ __build__ = "32"
5
5
  __author__ = "Robert Matsuoka"
6
6
  __email__ = "bobmatnyc@gmail.com"
7
7
 
@@ -166,9 +166,30 @@ def serve(
166
166
  """
167
167
  import http.server
168
168
  import os
169
+ import socket
169
170
  import socketserver
170
171
  import webbrowser
171
172
 
173
+ # Find free port in range 8080-8099
174
+ def find_free_port(start_port: int = 8080, end_port: int = 8099) -> int:
175
+ """Find a free port in the given range."""
176
+ for test_port in range(start_port, end_port + 1):
177
+ try:
178
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
179
+ s.bind(("", test_port))
180
+ return test_port
181
+ except OSError:
182
+ continue
183
+ raise OSError(f"No free ports available in range {start_port}-{end_port}")
184
+
185
+ # Use specified port or find free one
186
+ if port == 8080: # Default port, try to find free one
187
+ try:
188
+ port = find_free_port(8080, 8099)
189
+ except OSError as e:
190
+ console.print(f"[red]✗ {e}[/red]")
191
+ raise typer.Exit(1)
192
+
172
193
  # Get visualization directory
173
194
  viz_dir = Path(__file__).parent.parent.parent / "visualization"
174
195
 
@@ -364,9 +385,8 @@ def _create_visualization_html(html_file: Path) -> None:
364
385
  <div id="controls">
365
386
  <h1>🔍 Code Graph</h1>
366
387
 
367
- <div class="control-group">
368
- <label>Load Graph Data:</label>
369
- <input type="file" id="fileInput" accept=".json">
388
+ <div class="control-group" id="loading">
389
+ <label>⏳ Loading graph data...</label>
370
390
  </div>
371
391
 
372
392
  <h3>Legend</h3>
@@ -408,34 +428,79 @@ def _create_visualization_html(html_file: Path) -> None:
408
428
  const g = svg.append("g");
409
429
  const tooltip = d3.select("#tooltip");
410
430
  let simulation;
431
+ let allNodes = [];
432
+ let allLinks = [];
433
+ let visibleNodes = new Set();
434
+ let collapsedNodes = new Set();
411
435
 
412
436
  function visualizeGraph(data) {
413
437
  g.selectAll("*").remove();
414
438
 
415
- simulation = d3.forceSimulation(data.nodes)
416
- .force("link", d3.forceLink(data.links).id(d => d.id).distance(100))
439
+ allNodes = data.nodes;
440
+ allLinks = data.links;
441
+
442
+ // Find root nodes (nodes without parents or depth 0/1)
443
+ const rootNodes = allNodes.filter(n =>
444
+ !n.parent_id || n.depth === 0 || n.depth === 1 || n.type === 'module'
445
+ );
446
+
447
+ // Start with only root nodes visible
448
+ visibleNodes = new Set(rootNodes.map(n => n.id));
449
+ collapsedNodes = new Set(rootNodes.map(n => n.id));
450
+
451
+ renderGraph();
452
+ }
453
+
454
+ function renderGraph() {
455
+ const visibleNodesList = allNodes.filter(n => visibleNodes.has(n.id));
456
+ const visibleLinks = allLinks.filter(l =>
457
+ visibleNodes.has(l.source.id || l.source) &&
458
+ visibleNodes.has(l.target.id || l.target)
459
+ );
460
+
461
+ simulation = d3.forceSimulation(visibleNodesList)
462
+ .force("link", d3.forceLink(visibleLinks).id(d => d.id).distance(100))
417
463
  .force("charge", d3.forceManyBody().strength(-400))
418
464
  .force("center", d3.forceCenter(width / 2, height / 2))
419
465
  .force("collision", d3.forceCollide().radius(40));
420
466
 
467
+ g.selectAll("*").remove();
468
+
421
469
  const link = g.append("g")
422
470
  .selectAll("line")
423
- .data(data.links)
471
+ .data(visibleLinks)
424
472
  .join("line")
425
473
  .attr("class", "link");
426
474
 
427
475
  const node = g.append("g")
428
476
  .selectAll("g")
429
- .data(data.nodes)
477
+ .data(visibleNodesList)
430
478
  .join("g")
431
479
  .attr("class", d => `node ${d.type}`)
432
480
  .call(drag(simulation))
481
+ .on("click", toggleNode)
433
482
  .on("mouseover", showTooltip)
434
483
  .on("mouseout", hideTooltip);
435
484
 
485
+ // Add circles with expand indicator
436
486
  node.append("circle")
437
- .attr("r", d => d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12);
438
-
487
+ .attr("r", d => d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12)
488
+ .attr("stroke", d => hasChildren(d) ? "#ffffff" : "none")
489
+ .attr("stroke-width", d => hasChildren(d) ? 2 : 0);
490
+
491
+ // Add expand/collapse indicator
492
+ node.filter(d => hasChildren(d))
493
+ .append("text")
494
+ .attr("class", "expand-indicator")
495
+ .attr("text-anchor", "middle")
496
+ .attr("dy", 5)
497
+ .style("font-size", "16px")
498
+ .style("font-weight", "bold")
499
+ .style("fill", "#ffffff")
500
+ .style("pointer-events", "none")
501
+ .text(d => collapsedNodes.has(d.id) ? "+" : "−");
502
+
503
+ // Add labels
439
504
  node.append("text")
440
505
  .text(d => d.name)
441
506
  .attr("dy", 30);
@@ -450,7 +515,61 @@ def _create_visualization_html(html_file: Path) -> None:
450
515
  node.attr("transform", d => `translate(${d.x},${d.y})`);
451
516
  });
452
517
 
453
- updateStats(data);
518
+ updateStats({nodes: visibleNodesList, links: visibleLinks, metadata: {total_files: allNodes.length}});
519
+ }
520
+
521
+ function hasChildren(node) {
522
+ return allLinks.some(l => (l.source.id || l.source) === node.id);
523
+ }
524
+
525
+ function toggleNode(event, d) {
526
+ event.stopPropagation();
527
+
528
+ if (!hasChildren(d)) return;
529
+
530
+ if (collapsedNodes.has(d.id)) {
531
+ // Expand: show children
532
+ expandNode(d);
533
+ } else {
534
+ // Collapse: hide children
535
+ collapseNode(d);
536
+ }
537
+
538
+ renderGraph();
539
+ }
540
+
541
+ function expandNode(node) {
542
+ collapsedNodes.delete(node.id);
543
+
544
+ // Find direct children
545
+ const children = allLinks
546
+ .filter(l => (l.source.id || l.source) === node.id)
547
+ .map(l => allNodes.find(n => n.id === (l.target.id || l.target)))
548
+ .filter(n => n);
549
+
550
+ children.forEach(child => {
551
+ visibleNodes.add(child.id);
552
+ collapsedNodes.add(child.id); // Children start collapsed
553
+ });
554
+ }
555
+
556
+ function collapseNode(node) {
557
+ collapsedNodes.add(node.id);
558
+
559
+ // Hide all descendants recursively
560
+ function hideDescendants(parentId) {
561
+ const children = allLinks
562
+ .filter(l => (l.source.id || l.source) === parentId)
563
+ .map(l => l.target.id || l.target);
564
+
565
+ children.forEach(childId => {
566
+ visibleNodes.delete(childId);
567
+ collapsedNodes.delete(childId);
568
+ hideDescendants(childId);
569
+ });
570
+ }
571
+
572
+ hideDescendants(node.id);
454
573
  }
455
574
 
456
575
  function showTooltip(event, d) {
@@ -504,23 +623,29 @@ def _create_visualization_html(html_file: Path) -> None:
504
623
  `);
505
624
  }
506
625
 
507
- document.getElementById("fileInput").addEventListener("change", (event) => {
508
- const file = event.target.files[0];
509
- if (file) {
510
- const reader = new FileReader();
511
- reader.onload = (e) => {
512
- const data = JSON.parse(e.target.result);
626
+ // Auto-load graph data on page load
627
+ window.addEventListener('DOMContentLoaded', () => {
628
+ const loadingEl = document.getElementById('loading');
629
+
630
+ fetch("chunk-graph.json")
631
+ .then(response => {
632
+ if (!response.ok) {
633
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
634
+ }
635
+ return response.json();
636
+ })
637
+ .then(data => {
638
+ loadingEl.innerHTML = '<label style="color: #238636;">✓ Graph loaded successfully</label>';
639
+ setTimeout(() => loadingEl.style.display = 'none', 2000);
513
640
  visualizeGraph(data);
514
- };
515
- reader.readAsText(file);
516
- }
641
+ })
642
+ .catch(err => {
643
+ loadingEl.innerHTML = `<label style="color: #f85149;">✗ Failed to load graph data</label><br>` +
644
+ `<small style="color: #8b949e;">${err.message}</small><br>` +
645
+ `<small style="color: #8b949e;">Run: mcp-vector-search visualize export</small>`;
646
+ console.error("Failed to load graph:", err);
647
+ });
517
648
  });
518
-
519
- // Try to load default data
520
- fetch("chunk-graph.json")
521
- .then(response => response.json())
522
- .then(data => visualizeGraph(data))
523
- .catch(err => console.log("No default graph found. Please load a JSON file."));
524
649
  </script>
525
650
  </body>
526
651
  </html>'''
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-vector-search
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: CLI-first semantic code search with MCP integration
5
5
  Project-URL: Homepage, https://github.com/bobmatnyc/mcp-vector-search
6
6
  Project-URL: Documentation, https://mcp-vector-search.readthedocs.io
@@ -1,4 +1,4 @@
1
- mcp_vector_search/__init__.py,sha256=SJWzBn_EMpgwyAGK_RAudIBKV_lD9imaFJvi0dh62pc,299
1
+ mcp_vector_search/__init__.py,sha256=O0yFOAUi3iVdWNh8T90tgKHdwJTCaw-s0DKDUi97H6Q,299
2
2
  mcp_vector_search/py.typed,sha256=lCKeV9Qcn9sGtbRsgg-LJO2ZwWRuknnnlmomq3bJFH0,43
3
3
  mcp_vector_search/cli/__init__.py,sha256=TNB7CaOASz8u3yHWLbNmo8-GtHF0qwUjVKWAuNphKgo,40
4
4
  mcp_vector_search/cli/didyoumean.py,sha256=F_ss-EX4F9RgnMsEhdTwLpyNCah9SqnBZc2tBtzASck,15918
@@ -19,7 +19,7 @@ mcp_vector_search/cli/commands/mcp.py,sha256=Mk4g43R9yRiJVMxsDFUsZldKqY0yi2coQmh
19
19
  mcp_vector_search/cli/commands/reset.py,sha256=bsIT6zjDf6gsvIkVaRaUClYzlTyNe--8t0NWkBY0ldU,13724
20
20
  mcp_vector_search/cli/commands/search.py,sha256=yyou7wO9qZ_w2oiKdyOrk2WUxvkFpc-Up8hpflxYlyw,24802
21
21
  mcp_vector_search/cli/commands/status.py,sha256=sa_0QHioCmPF5A7obqV2ls-9kmX_JYo7nq3XUe1dmrg,19630
22
- mcp_vector_search/cli/commands/visualize.py,sha256=QLefmVaeRUbdnabtEuCTkZXtVlL4DLVk4zeITFDGPww,16579
22
+ mcp_vector_search/cli/commands/visualize.py,sha256=tipe_QLjkZboqEz8SfIx5mjYrAenqrKsQPnXkgG7GBg,21398
23
23
  mcp_vector_search/cli/commands/watch.py,sha256=2pyWRoo4fIppFnyQ4sW4IBLHmpb_IwnTjRnzHkVBPcQ,8927
24
24
  mcp_vector_search/config/__init__.py,sha256=r_qAQkU5gc0EQ2pv8EQARACe4klhrR_WRJqCb9lfGc0,54
25
25
  mcp_vector_search/config/constants.py,sha256=afXR6SvLLd8QYY4MG4s1vq-hCJiQsE5PhnE-XG9lvb4,1092
@@ -57,8 +57,8 @@ mcp_vector_search/utils/__init__.py,sha256=Eq6lY-oPMfCt-GpPUbg9QbmTHuQVmTaVDBMU2
57
57
  mcp_vector_search/utils/gitignore.py,sha256=GiHQu9kv9PRLsWuNS8kbpXsTaBdhlsSHTu1NrZ8Ug5Y,8162
58
58
  mcp_vector_search/utils/timing.py,sha256=THC7mfbTYnUpnnDcblgQacYMzbEkfFoIShx6plmhCgg,11285
59
59
  mcp_vector_search/utils/version.py,sha256=d7fS-CLemxb8UzZ9j18zH0Y0Ud097ljKKYYOPulnGPE,1138
60
- mcp_vector_search-0.8.2.dist-info/METADATA,sha256=hyZnlaTWcDJ5SmHpQNVADg26RpQxy9pO-iZXgD-kWiw,19120
61
- mcp_vector_search-0.8.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
- mcp_vector_search-0.8.2.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
63
- mcp_vector_search-0.8.2.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
64
- mcp_vector_search-0.8.2.dist-info/RECORD,,
60
+ mcp_vector_search-0.8.3.dist-info/METADATA,sha256=Yk6nZLkTTIZYpXdH67gkobj9rnlUoPCyjn4yo9-0IXA,19120
61
+ mcp_vector_search-0.8.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ mcp_vector_search-0.8.3.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
63
+ mcp_vector_search-0.8.3.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
64
+ mcp_vector_search-0.8.3.dist-info/RECORD,,