mcp-vector-search 0.12.1__py3-none-any.whl → 0.12.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.

Potentially problematic release.


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

Files changed (32) hide show
  1. mcp_vector_search/__init__.py +2 -2
  2. mcp_vector_search/cli/commands/visualize.py +22 -12
  3. mcp_vector_search/visualization/favicon-v1-1024.png +0 -0
  4. mcp_vector_search/visualization/favicon-v1-128.png +0 -0
  5. mcp_vector_search/visualization/favicon-v1-16.png +0 -0
  6. mcp_vector_search/visualization/favicon-v1-256.png +0 -0
  7. mcp_vector_search/visualization/favicon-v1-32.png +0 -0
  8. mcp_vector_search/visualization/favicon-v1-512.png +0 -0
  9. mcp_vector_search/visualization/favicon-v1-64.png +0 -0
  10. mcp_vector_search/visualization/favicon-v1.ico +0 -0
  11. mcp_vector_search/visualization/favicon-v2-1024.png +0 -0
  12. mcp_vector_search/visualization/favicon-v2-128.png +0 -0
  13. mcp_vector_search/visualization/favicon-v2-16.png +0 -0
  14. mcp_vector_search/visualization/favicon-v2-256.png +0 -0
  15. mcp_vector_search/visualization/favicon-v2-32.png +0 -0
  16. mcp_vector_search/visualization/favicon-v2-512.png +0 -0
  17. mcp_vector_search/visualization/favicon-v2-64.png +0 -0
  18. mcp_vector_search/visualization/favicon-v2.ico +0 -0
  19. mcp_vector_search/visualization/favicon-v3-1024.png +0 -0
  20. mcp_vector_search/visualization/favicon-v3-128.png +0 -0
  21. mcp_vector_search/visualization/favicon-v3-16.png +0 -0
  22. mcp_vector_search/visualization/favicon-v3-256.png +0 -0
  23. mcp_vector_search/visualization/favicon-v3-32.png +0 -0
  24. mcp_vector_search/visualization/favicon-v3-512.png +0 -0
  25. mcp_vector_search/visualization/favicon-v3-64.png +0 -0
  26. mcp_vector_search/visualization/favicon-v3.ico +0 -0
  27. mcp_vector_search/visualization/index.html +522 -172
  28. {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/METADATA +62 -2
  29. {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/RECORD +32 -8
  30. {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/WHEEL +0 -0
  31. {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/entry_points.txt +0 -0
  32. {mcp_vector_search-0.12.1.dist-info → mcp_vector_search-0.12.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  """MCP Vector Search - CLI-first semantic code search with MCP integration."""
2
2
 
3
- __version__ = "0.12.1"
4
- __build__ = "51"
3
+ __version__ = "0.12.2"
4
+ __build__ = "52"
5
5
  __author__ = "Robert Matsuoka"
6
6
  __email__ = "bobmatnyc@gmail.com"
7
7
 
@@ -1069,12 +1069,18 @@ def _create_visualization_html(html_file: Path) -> None:
1069
1069
  .style("pointer-events", "none")
1070
1070
  .text(d => collapsedNodes.has(d.id) ? "+" : "−");
1071
1071
 
1072
- // Add labels (show "Import:" prefix for L1 nodes)
1072
+ // Add labels (show actual import statement for L1 nodes)
1073
1073
  node.append("text")
1074
1074
  .text(d => {
1075
1075
  // L1 (depth 1) nodes are imports
1076
1076
  if (d.depth === 1 && d.type !== 'directory' && d.type !== 'file') {
1077
- return `Import: ${d.name}`;
1077
+ if (d.content) {
1078
+ // Extract first line of import statement
1079
+ const importLine = d.content.split('\n')[0].trim();
1080
+ // Truncate if too long (max 60 chars)
1081
+ return importLine.length > 60 ? importLine.substring(0, 57) + '...' : importLine;
1082
+ }
1083
+ return d.name; // Fallback to name if no content
1078
1084
  }
1079
1085
  return d.name;
1080
1086
  })
@@ -1230,9 +1236,14 @@ def _create_visualization_html(html_file: Path) -> None:
1230
1236
  const meta = document.getElementById('pane-meta');
1231
1237
  const content = document.getElementById('pane-content');
1232
1238
 
1233
- // Set title with "Import:" prefix for L1 nodes
1239
+ // Set title with actual import statement for L1 nodes
1234
1240
  if (node.depth === 1 && node.type !== 'directory' && node.type !== 'file') {
1235
- title.textContent = `Import: ${node.name}`;
1241
+ if (node.content) {
1242
+ const importLine = node.content.split('\n')[0].trim();
1243
+ title.textContent = importLine;
1244
+ } else {
1245
+ title.textContent = `Import: ${node.name}`;
1246
+ }
1236
1247
  } else {
1237
1248
  title.textContent = node.name;
1238
1249
  }
@@ -1360,10 +1371,15 @@ def _create_visualization_html(html_file: Path) -> None:
1360
1371
  }
1361
1372
 
1362
1373
  function showImportDetails(node, container) {
1363
- // L1 nodes are import statements
1374
+ // L1 nodes are import statements - show import content prominently
1364
1375
  const importHtml = `
1365
1376
  <div class="import-details">
1366
- <div class="import-statement">${escapeHtml(node.name)}</div>
1377
+ ${node.content ? `
1378
+ <div style="margin-bottom: 16px;">
1379
+ <div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
1380
+ <pre><code>${escapeHtml(node.content)}</code></pre>
1381
+ </div>
1382
+ ` : '<p style="color: #8b949e;">No import content available</p>'}
1367
1383
  <div class="detail-row">
1368
1384
  <span class="detail-label">File:</span> ${node.file_path}
1369
1385
  </div>
@@ -1377,12 +1393,6 @@ def _create_visualization_html(html_file: Path) -> None:
1377
1393
  <span class="detail-label">Language:</span> ${node.language}
1378
1394
  </div>
1379
1395
  ` : ''}
1380
- ${node.content ? `
1381
- <div style="margin-top: 16px;">
1382
- <div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
1383
- <pre><code>${escapeHtml(node.content)}</code></pre>
1384
- </div>
1385
- ` : ''}
1386
1396
  </div>
1387
1397
  `;
1388
1398
 
@@ -2,10 +2,15 @@
2
2
  <html>
3
3
  <head>
4
4
  <meta charset="utf-8">
5
- <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
6
- <meta http-equiv="Pragma" content="no-cache">
7
- <meta http-equiv="Expires" content="0">
8
5
  <title>Code Chunk Relationship Graph</title>
6
+
7
+ <!-- Favicon -->
8
+ <link rel="icon" type="image/x-icon" href="favicon-v1.ico">
9
+ <link rel="icon" type="image/png" sizes="16x16" href="favicon-v1-16.png">
10
+ <link rel="icon" type="image/png" sizes="32x32" href="favicon-v1-32.png">
11
+ <link rel="icon" type="image/png" sizes="64x64" href="favicon-v1-64.png">
12
+ <link rel="apple-touch-icon" sizes="180x180" href="favicon-v1-512.png">
13
+
9
14
  <script src="https://d3js.org/d3.v7.min.js"></script>
10
15
  <style>
11
16
  body {
@@ -80,26 +85,43 @@
80
85
  cursor: pointer;
81
86
  stroke: #c9d1d9;
82
87
  stroke-width: 1.5px;
88
+ transition: all 0.2s ease;
89
+ }
90
+
91
+ .node circle:hover,
92
+ .node rect:hover {
93
+ stroke-width: 3px !important;
94
+ filter: brightness(1.2);
95
+ cursor: pointer;
83
96
  }
84
97
 
85
- .node.directory circle { fill: #ffa657; }
86
- .node.file circle { fill: #58a6ff; }
87
98
  .node.module circle { fill: #238636; }
88
99
  .node.class circle { fill: #1f6feb; }
89
100
  .node.function circle { fill: #d29922; }
90
101
  .node.method circle { fill: #8957e5; }
91
- .node.imports circle { fill: #6e7681; }
92
- .node.text circle { fill: #6e7681; }
93
102
  .node.code circle { fill: #6e7681; }
103
+ .node.file circle {
104
+ fill: none;
105
+ stroke: #58a6ff;
106
+ stroke-width: 2px;
107
+ stroke-dasharray: 5,3;
108
+ opacity: 0.6;
109
+ }
110
+ .node.directory circle {
111
+ fill: none;
112
+ stroke: #79c0ff;
113
+ stroke-width: 2px;
114
+ stroke-dasharray: 3,3;
115
+ opacity: 0.5;
116
+ }
94
117
  .node.subproject circle { fill: #da3633; stroke-width: 3px; }
95
118
 
96
- .node-icon {
97
- font-size: 35px !important;
98
- text-anchor: middle;
99
- pointer-events: none;
100
- user-select: none;
101
- fill: #0d1117;
102
- font-weight: bold;
119
+ /* Non-code document nodes - squares */
120
+ .node.docstring rect { fill: #8b949e; }
121
+ .node.comment rect { fill: #6e7681; }
122
+ .node rect {
123
+ stroke: #c9d1d9;
124
+ stroke-width: 1.5px;
103
125
  }
104
126
 
105
127
  .node text {
@@ -110,10 +132,19 @@
110
132
  user-select: none;
111
133
  }
112
134
 
135
+ .node-icon {
136
+ font-size: 35px !important;
137
+ text-anchor: middle;
138
+ pointer-events: none;
139
+ user-select: none;
140
+ fill: #0d1117;
141
+ font-weight: bold;
142
+ }
143
+
113
144
  .link {
114
- stroke: #58a6ff;
115
- stroke-opacity: 0.4;
116
- stroke-width: 2px;
145
+ stroke: #30363d;
146
+ stroke-opacity: 0.6;
147
+ stroke-width: 1.5px;
117
148
  }
118
149
 
119
150
  .link.dependency {
@@ -144,73 +175,141 @@
144
175
  color: #8b949e;
145
176
  }
146
177
 
147
- #code-viewer {
148
- position: absolute;
149
- top: 20px;
150
- right: 20px;
151
- width: 500px;
152
- max-height: 80vh;
153
- background: rgba(13, 17, 23, 0.95);
154
- border: 1px solid #30363d;
155
- border-radius: 6px;
156
- padding: 16px;
178
+ #content-pane {
179
+ position: fixed;
180
+ top: 0;
181
+ right: 0;
182
+ width: 600px;
183
+ height: 100vh;
184
+ background: rgba(13, 17, 23, 0.98);
185
+ border-left: 1px solid #30363d;
157
186
  overflow-y: auto;
158
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
159
- display: none;
187
+ box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
188
+ transform: translateX(100%);
189
+ transition: transform 0.3s ease-in-out;
190
+ z-index: 1000;
160
191
  }
161
192
 
162
- #code-viewer.visible {
163
- display: block;
193
+ #content-pane.visible {
194
+ transform: translateX(0);
164
195
  }
165
196
 
166
- #code-viewer .header {
167
- margin-bottom: 12px;
168
- padding-bottom: 12px;
197
+ #content-pane .pane-header {
198
+ position: sticky;
199
+ top: 0;
200
+ background: rgba(13, 17, 23, 0.98);
201
+ padding: 20px;
169
202
  border-bottom: 1px solid #30363d;
203
+ z-index: 1;
170
204
  }
171
205
 
172
- #code-viewer .title {
173
- font-size: 14px;
206
+ #content-pane .pane-title {
207
+ font-size: 16px;
174
208
  font-weight: bold;
175
209
  color: #58a6ff;
176
- margin-bottom: 4px;
210
+ margin-bottom: 8px;
211
+ padding-right: 30px;
177
212
  }
178
213
 
179
- #code-viewer .meta {
180
- font-size: 11px;
214
+ #content-pane .pane-meta {
215
+ font-size: 12px;
181
216
  color: #8b949e;
182
217
  }
183
218
 
184
- #code-viewer .close-btn {
185
- float: right;
219
+ #content-pane .collapse-btn {
220
+ position: absolute;
221
+ top: 20px;
222
+ right: 20px;
186
223
  cursor: pointer;
187
224
  color: #8b949e;
188
- font-size: 20px;
225
+ font-size: 24px;
189
226
  line-height: 1;
190
- margin-top: -4px;
227
+ background: none;
228
+ border: none;
229
+ padding: 0;
230
+ transition: color 0.2s;
191
231
  }
192
232
 
193
- #code-viewer .close-btn:hover {
233
+ #content-pane .collapse-btn:hover {
194
234
  color: #c9d1d9;
195
235
  }
196
236
 
197
- #code-viewer pre {
237
+ #content-pane .pane-content {
238
+ padding: 20px;
239
+ }
240
+
241
+ #content-pane pre {
198
242
  margin: 0;
199
- padding: 12px;
243
+ padding: 16px;
200
244
  background: #0d1117;
201
245
  border: 1px solid #30363d;
202
246
  border-radius: 6px;
203
247
  overflow-x: auto;
204
- font-size: 11px;
205
- line-height: 1.5;
248
+ font-size: 12px;
249
+ line-height: 1.6;
206
250
  }
207
251
 
208
- #code-viewer code {
252
+ #content-pane code {
209
253
  color: #c9d1d9;
210
254
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
211
255
  }
212
256
 
213
- .node.highlighted circle {
257
+ #content-pane .directory-list {
258
+ list-style: none;
259
+ padding: 0;
260
+ margin: 0;
261
+ }
262
+
263
+ #content-pane .directory-list li {
264
+ padding: 8px 12px;
265
+ margin: 4px 0;
266
+ background: #161b22;
267
+ border: 1px solid #30363d;
268
+ border-radius: 4px;
269
+ font-size: 12px;
270
+ display: flex;
271
+ align-items: center;
272
+ }
273
+
274
+ #content-pane .directory-list .item-icon {
275
+ margin-right: 8px;
276
+ font-size: 14px;
277
+ }
278
+
279
+ #content-pane .directory-list .item-type {
280
+ margin-left: auto;
281
+ padding-left: 12px;
282
+ font-size: 10px;
283
+ color: #8b949e;
284
+ }
285
+
286
+ #content-pane .import-details {
287
+ background: #161b22;
288
+ border: 1px solid #30363d;
289
+ border-radius: 6px;
290
+ padding: 16px;
291
+ }
292
+
293
+ #content-pane .import-details .import-statement {
294
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
295
+ font-size: 12px;
296
+ color: #79c0ff;
297
+ margin-bottom: 12px;
298
+ }
299
+
300
+ #content-pane .import-details .detail-row {
301
+ font-size: 11px;
302
+ color: #8b949e;
303
+ margin: 4px 0;
304
+ }
305
+
306
+ #content-pane .import-details .detail-label {
307
+ color: #c9d1d9;
308
+ font-weight: 600;
309
+ }
310
+
311
+ .node.highlighted circle,
312
+ .node.highlighted rect {
214
313
  stroke: #f0e68c;
215
314
  stroke-width: 3px;
216
315
  filter: drop-shadow(0 0 8px #f0e68c);
@@ -228,25 +327,37 @@
228
327
  <h3>Legend</h3>
229
328
  <div class="legend">
230
329
  <div class="legend-item">
231
- 📁 <span class="legend-color" style="background: #ffa657;"></span> Directory
330
+ <span class="legend-color" style="background: #da3633;"></span> Subproject
331
+ </div>
332
+ <div class="legend-item">
333
+ <span class="legend-color" style="border: 2px dashed #79c0ff; border-radius: 50%; background: transparent;"></span> Directory
334
+ </div>
335
+ <div class="legend-item">
336
+ <span class="legend-color" style="border: 2px dashed #58a6ff; border-radius: 50%; background: transparent;"></span> File
337
+ </div>
338
+ <div class="legend-item">
339
+ <span class="legend-color" style="background: #238636;"></span> Module
232
340
  </div>
233
341
  <div class="legend-item">
234
- 📄 <span class="legend-color" style="background: #58a6ff;"></span> File
342
+ <span class="legend-color" style="background: #1f6feb;"></span> Class
235
343
  </div>
236
344
  <div class="legend-item">
237
- C <span class="legend-color" style="background: #1f6feb;"></span> Class
345
+ <span class="legend-color" style="background: #d29922;"></span> Function
238
346
  </div>
239
347
  <div class="legend-item">
240
- ƒ <span class="legend-color" style="background: #d29922;"></span> Function
348
+ <span class="legend-color" style="background: #8957e5;"></span> Method
241
349
  </div>
242
350
  <div class="legend-item">
243
- m <span class="legend-color" style="background: #8957e5;"></span> Method
351
+ <span class="legend-color" style="background: #6e7681;"></span> Code
352
+ </div>
353
+ <div class="legend-item" style="font-style: italic; color: #79c0ff;">
354
+ <span class="legend-color" style="background: #6e7681;"></span> Import
244
355
  </div>
245
356
  <div class="legend-item">
246
- M <span class="legend-color" style="background: #238636;"></span> Module
357
+ <span class="legend-color" style="background: #8b949e; border-radius: 2px;"></span> Docstring ▢
247
358
  </div>
248
359
  <div class="legend-item">
249
- <span class="legend-color" style="background: #6e7681;"></span> Text/Other
360
+ <span class="legend-color" style="background: #6e7681; border-radius: 2px;"></span> Comment ▢
250
361
  </div>
251
362
  </div>
252
363
 
@@ -261,13 +372,13 @@
261
372
  <svg id="graph"></svg>
262
373
  <div id="tooltip" class="tooltip"></div>
263
374
 
264
- <div id="code-viewer">
265
- <div class="header">
266
- <span class="close-btn" onclick="closeCodeViewer()">×</span>
267
- <div class="title" id="viewer-title"></div>
268
- <div class="meta" id="viewer-meta"></div>
375
+ <div id="content-pane">
376
+ <div class="pane-header">
377
+ <button class="collapse-btn" onclick="closeContentPane()">×</button>
378
+ <div class="pane-title" id="pane-title"></div>
379
+ <div class="pane-meta" id="pane-meta"></div>
269
380
  </div>
270
- <pre><code id="viewer-code"></code></pre>
381
+ <div class="pane-content" id="pane-content"></div>
271
382
  </div>
272
383
 
273
384
  <script>
@@ -286,7 +397,6 @@
286
397
  let simulation;
287
398
  let allNodes = [];
288
399
  let allLinks = [];
289
- let allLinksOriginal = []; // Keep original link structure before D3 modifies it
290
400
  let visibleNodes = new Set();
291
401
  let collapsedNodes = new Set();
292
402
  let highlightedNode = null;
@@ -296,8 +406,6 @@
296
406
 
297
407
  allNodes = data.nodes;
298
408
  allLinks = data.links;
299
- // Deep copy links before D3 modifies them
300
- allLinksOriginal = JSON.parse(JSON.stringify(data.links));
301
409
 
302
410
  // Find root nodes - start with only top-level nodes
303
411
  let rootNodes;
@@ -305,21 +413,36 @@
305
413
  // In monorepos, subproject nodes are roots
306
414
  rootNodes = allNodes.filter(n => n.type === 'subproject');
307
415
  } else {
308
- // Regular projects: prefer directories, fallback to files
416
+ // Regular projects: Find root nodes by graph structure
309
417
  const dirNodes = allNodes.filter(n => n.type === 'directory');
310
418
  const fileNodes = allNodes.filter(n => n.type === 'file');
311
419
 
312
- if (dirNodes.length > 0) {
313
- // Show root-level directories (minimum depth)
314
- const minDirDepth = Math.min(...dirNodes.map(n => n.depth || 0));
315
- rootNodes = dirNodes.filter(n => (n.depth || 0) === minDirDepth);
316
- } else if (fileNodes.length > 0) {
317
- // No directories, show root-level files
318
- const minFileDepth = Math.min(...fileNodes.map(n => n.depth || 0));
319
- rootNodes = fileNodes.filter(n => (n.depth || 0) === minFileDepth);
320
- } else {
321
- // No directories or files, show all nodes
322
- rootNodes = allNodes;
420
+ // Find minimum depth for directories and files (for fallback)
421
+ let minDirDepth = Infinity;
422
+ let minFileDepth = Infinity;
423
+
424
+ dirNodes.forEach(d => {
425
+ if (d.depth < minDirDepth) minDirDepth = d.depth;
426
+ });
427
+
428
+ fileNodes.forEach(f => {
429
+ if (f.depth < minFileDepth) minFileDepth = f.depth;
430
+ });
431
+
432
+ // CORRECT: Find root nodes by checking graph structure
433
+ // Root nodes are nodes with no incoming links (no parent)
434
+ const targetIds = new Set(allLinks.map(link => link.target));
435
+ rootNodes = allNodes.filter(node => !targetIds.has(node.id));
436
+
437
+ console.log(`Root nodes found via links: ${rootNodes.length}`, rootNodes.map(n => n.name));
438
+
439
+ // Fallback only if we got 0 root nodes (shouldn't happen with correct data)
440
+ if (rootNodes.length === 0) {
441
+ console.warn('No root nodes found via links, falling back to depth-based detection');
442
+ rootNodes = [
443
+ ...dirNodes.filter(n => n.depth === minDirDepth),
444
+ ...fileNodes.filter(n => n.depth === minFileDepth)
445
+ ];
323
446
  }
324
447
  }
325
448
 
@@ -327,58 +450,35 @@
327
450
  visibleNodes = new Set(rootNodes.map(n => n.id));
328
451
  collapsedNodes = new Set(rootNodes.map(n => n.id));
329
452
 
453
+ console.log('=== INITIAL STATE ===');
454
+ console.log(`Total nodes: ${allNodes.length}`);
455
+ console.log(`Total links: ${allLinks.length}`);
456
+ console.log(`Root nodes: ${rootNodes.length}`, rootNodes.map(n => n.name));
457
+ console.log(`Initial visibleNodes:`, Array.from(visibleNodes));
458
+ console.log(`Initial collapsedNodes:`, Array.from(collapsedNodes));
459
+
330
460
  renderGraph();
331
461
  }
332
462
 
333
463
  function renderGraph() {
464
+ console.log('=== renderGraph() called ===');
465
+ console.log(`visibleNodes.size: ${visibleNodes.size}`);
466
+ console.log(`collapsedNodes.size: ${collapsedNodes.size}`);
467
+
334
468
  const visibleNodesList = allNodes.filter(n => visibleNodes.has(n.id));
469
+ console.log(`Rendering ${visibleNodesList.length} visible nodes`);
470
+
335
471
  const visibleLinks = allLinks.filter(l =>
336
472
  visibleNodes.has(l.source.id || l.source) &&
337
473
  visibleNodes.has(l.target.id || l.target)
338
474
  );
339
-
340
- // Dynamic collision radius based on node type
341
- const getNodeRadius = (d) => {
342
- if (d.type === 'directory') return 35;
343
- if (d.type === 'file') return 32;
344
- if (d.type === 'subproject') return 38;
345
- return d.complexity ? Math.min(20 + d.complexity * 2, 35) : 25;
346
- };
347
-
348
- // Dynamic link distance based on node types
349
- const getLinkDistance = (link) => {
350
- const source = typeof link.source === 'object' ? link.source : visibleNodesList.find(n => n.id === link.source);
351
- const target = typeof link.target === 'object' ? link.target : visibleNodesList.find(n => n.id === link.target);
352
-
353
- if (!source || !target) return 150;
354
-
355
- // Longer distances for directory relationships
356
- if (source.type === 'directory' || target.type === 'directory') return 200;
357
- if (source.type === 'file' || target.type === 'file') return 150;
358
- return 100;
359
- };
475
+ console.log(`Rendering ${visibleLinks.length} visible links`);
360
476
 
361
477
  simulation = d3.forceSimulation(visibleNodesList)
362
- .force("link", d3.forceLink(visibleLinks)
363
- .id(d => d.id)
364
- .distance(getLinkDistance)
365
- .strength(0.5))
366
- .force("charge", d3.forceManyBody()
367
- .strength(d => {
368
- // Stronger repulsion for directories and files
369
- if (d.type === 'directory') return -800;
370
- if (d.type === 'file') return -600;
371
- return -400;
372
- })
373
- .distanceMax(500))
478
+ .force("link", d3.forceLink(visibleLinks).id(d => d.id).distance(100))
479
+ .force("charge", d3.forceManyBody().strength(-400))
374
480
  .force("center", d3.forceCenter(width / 2, height / 2))
375
- .force("collision", d3.forceCollide()
376
- .radius(getNodeRadius)
377
- .strength(0.9))
378
- .force("x", d3.forceX(width / 2).strength(0.05))
379
- .force("y", d3.forceY(height / 2).strength(0.05))
380
- .alphaDecay(0.02)
381
- .velocityDecay(0.3);
481
+ .force("collision", d3.forceCollide().radius(40));
382
482
 
383
483
  g.selectAll("*").remove();
384
484
 
@@ -404,16 +504,59 @@
404
504
  .on("mouseover", showTooltip)
405
505
  .on("mouseout", hideTooltip);
406
506
 
407
- // Add circles
408
- node.append("circle")
507
+ // Add shapes based on node type (circles for code, squares for docs)
508
+ const isDocNode = d => ['docstring', 'comment'].includes(d.type);
509
+
510
+ // Add circles for code nodes
511
+ node.filter(d => !isDocNode(d))
512
+ .append("circle")
409
513
  .attr("r", d => {
410
- if (d.type === 'directory') return 25;
411
- if (d.type === 'file') return 22;
412
- if (d.type === 'subproject') return 28;
413
- return d.complexity ? Math.min(12 + d.complexity * 2, 25) : 15;
514
+ if (d.type === 'subproject') return 20;
515
+ if (d.type === 'directory') return 40; // Largest for directory containers
516
+ if (d.type === 'file') return 30; // Larger transparent circle for files
517
+ return d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
518
+ })
519
+ .attr("stroke", d => hasChildren(d) ? "#ffffff" : "none")
520
+ .attr("stroke-width", d => hasChildren(d) ? 3 : 0)
521
+ .style("fill", d => d.color || null); // Use custom color if available
522
+
523
+ // Add rectangles for document nodes
524
+ node.filter(d => isDocNode(d))
525
+ .append("rect")
526
+ .attr("width", d => {
527
+ const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
528
+ return size * 2;
529
+ })
530
+ .attr("height", d => {
531
+ const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
532
+ return size * 2;
414
533
  })
534
+ .attr("x", d => {
535
+ const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
536
+ return -size;
537
+ })
538
+ .attr("y", d => {
539
+ const size = d.complexity ? Math.min(8 + d.complexity * 2, 25) : 12;
540
+ return -size;
541
+ })
542
+ .attr("rx", 2) // Rounded corners
543
+ .attr("ry", 2)
544
+ .attr("stroke", d => hasChildren(d) ? "#ffffff" : "none")
545
+ .attr("stroke-width", d => hasChildren(d) ? 3 : 0)
415
546
  .style("fill", d => d.color || null);
416
547
 
548
+ // Add expand/collapse indicator
549
+ node.filter(d => hasChildren(d))
550
+ .append("text")
551
+ .attr("class", "expand-indicator")
552
+ .attr("text-anchor", "middle")
553
+ .attr("dy", 5)
554
+ .style("font-size", "16px")
555
+ .style("font-weight", "bold")
556
+ .style("fill", "#ffffff")
557
+ .style("pointer-events", "none")
558
+ .text(d => collapsedNodes.has(d.id) ? "+" : "−");
559
+
417
560
  // Add icons based on node type
418
561
  node.append("text")
419
562
  .attr("class", "node-icon")
@@ -426,24 +569,27 @@
426
569
  if (d.type === 'method') return 'm';
427
570
  if (d.type === 'module') return 'M';
428
571
  if (d.type === 'imports') return '⇄';
572
+ if (d.type === 'docstring') return '📝';
573
+ if (d.type === 'comment') return '💬';
574
+ if (d.type === 'subproject') return '📦';
429
575
  return '•';
430
576
  });
431
577
 
432
- // Add expand/collapse indicator for nodes with children
433
- node.filter(d => hasChildren(d))
434
- .append("text")
435
- .attr("class", "expand-indicator")
436
- .attr("text-anchor", "middle")
437
- .attr("dy", -20)
438
- .style("font-size", "14px")
439
- .style("font-weight", "bold")
440
- .style("fill", "#58a6ff")
441
- .style("pointer-events", "none")
442
- .text(d => collapsedNodes.has(d.id) ? "+" : "−");
443
-
444
- // Add labels
578
+ // Add labels (show actual import statement for import nodes)
445
579
  node.append("text")
446
- .text(d => d.name)
580
+ .text(d => {
581
+ // Import nodes have type === 'imports'
582
+ if (d.type === 'imports') {
583
+ if (d.content) {
584
+ // Extract first line of import statement
585
+ const importLine = d.content.split('\n')[0].trim();
586
+ // Truncate if too long (max 60 chars)
587
+ return importLine.length > 60 ? importLine.substring(0, 57) + '...' : importLine;
588
+ }
589
+ return d.name; // Fallback to name if no content
590
+ }
591
+ return d.name;
592
+ })
447
593
  .attr("dy", 30);
448
594
 
449
595
  simulation.on("tick", () => {
@@ -460,50 +606,87 @@
460
606
  }
461
607
 
462
608
  function hasChildren(node) {
463
- // Use original links (not modified by D3)
464
- return allLinksOriginal.some(l => l.source === node.id);
609
+ // Handle both pre-mutation (string IDs) and post-mutation (object references)
610
+ const result = allLinks.some(l => {
611
+ const sourceId = typeof l.source === 'object' ? l.source.id : l.source;
612
+ return sourceId === node.id;
613
+ });
614
+ console.log(`hasChildren(${node.name}):`, result,
615
+ `Checking ${allLinks.length} links for node ID: ${node.id}`);
616
+ return result;
465
617
  }
466
618
 
467
619
  function handleNodeClick(event, d) {
620
+ console.log('=== NODE CLICKED ===');
621
+ console.log('Node:', d.name, 'Type:', d.type, 'ID:', d.id);
622
+ console.log('Event:', event);
623
+
468
624
  event.stopPropagation();
469
625
 
470
- // If node has children, toggle expansion
471
- if (hasChildren(d)) {
472
- if (collapsedNodes.has(d.id)) {
626
+ // Always show content pane when clicking any node
627
+ showContentPane(d);
628
+
629
+ // If node has children, also toggle expansion
630
+ const nodeHasChildren = hasChildren(d);
631
+ console.log('Node has children:', nodeHasChildren);
632
+
633
+ if (nodeHasChildren) {
634
+ const isCollapsed = collapsedNodes.has(d.id);
635
+ console.log('Node is collapsed:', isCollapsed);
636
+
637
+ if (isCollapsed) {
638
+ console.log('Expanding node...');
473
639
  expandNode(d);
474
640
  } else {
641
+ console.log('Collapsing node...');
475
642
  collapseNode(d);
476
643
  }
644
+ console.log('Calling renderGraph()...');
477
645
  renderGraph();
646
+ console.log('renderGraph() complete');
478
647
  } else {
479
- // Leaf node - show code viewer
480
- showCodeViewer(d);
648
+ console.log('Node has no children, skipping expansion');
481
649
  }
482
650
  }
483
651
 
484
652
  function expandNode(node) {
653
+ console.log(`expandNode(${node.name}):`, 'Removing from collapsedNodes');
485
654
  collapsedNodes.delete(node.id);
486
655
 
487
- // Find direct children using original links (not modified by D3)
488
- const children = allLinksOriginal
489
- .filter(l => l.source === node.id)
490
- .map(l => allNodes.find(n => n.id === l.target))
656
+ // Find direct children
657
+ const childLinks = allLinks.filter(l => (l.source.id || l.source) === node.id);
658
+ console.log(` Found ${childLinks.length} child links for node ${node.id}`);
659
+
660
+ const children = childLinks
661
+ .map(l => {
662
+ const targetId = l.target.id || l.target;
663
+ const child = allNodes.find(n => n.id === targetId);
664
+ if (!child) {
665
+ console.warn(` Could not find child node with ID: ${targetId}`);
666
+ }
667
+ return child;
668
+ })
491
669
  .filter(n => n);
492
670
 
671
+ console.log(` Found ${children.length} child nodes:`, children.map(c => c.name));
672
+
493
673
  children.forEach(child => {
674
+ console.log(` Adding child to visibleNodes: ${child.name} (${child.id})`);
494
675
  visibleNodes.add(child.id);
495
676
  collapsedNodes.add(child.id); // Children start collapsed
496
677
  });
678
+
679
+ console.log(` visibleNodes now has ${visibleNodes.size} items`);
497
680
  }
498
681
 
499
682
  function collapseNode(node) {
500
683
  collapsedNodes.add(node.id);
501
684
 
502
- // Hide all descendants recursively using original links
685
+ // Hide all descendants recursively
503
686
  function hideDescendants(parentId) {
504
- const children = allLinksOriginal
505
- .filter(l => l.source === parentId)
506
- .map(l => l.target);
687
+ const children = allLinks
688
+ .filter(l => (l.source.id || l.source) === parentId)
689
+ .map(l => l.target.id || l.target);
507
690
 
508
691
  children.forEach(childId => {
509
692
  visibleNodes.delete(childId);
@@ -586,19 +769,30 @@
586
769
  }
587
770
  }
588
771
 
589
- function showCodeViewer(node) {
772
+ function showContentPane(node) {
590
773
  // Highlight the node
591
774
  highlightedNode = node;
592
775
  renderGraph();
593
776
 
594
- // Populate code viewer
595
- const viewer = document.getElementById('code-viewer');
596
- const title = document.getElementById('viewer-title');
597
- const meta = document.getElementById('viewer-meta');
598
- const code = document.getElementById('viewer-code');
599
-
600
- title.textContent = node.name;
777
+ // Populate content pane
778
+ const pane = document.getElementById('content-pane');
779
+ const title = document.getElementById('pane-title');
780
+ const meta = document.getElementById('pane-meta');
781
+ const content = document.getElementById('pane-content');
782
+
783
+ // Set title with actual import statement for import nodes
784
+ if (node.type === 'imports') {
785
+ if (node.content) {
786
+ const importLine = node.content.split('\n')[0].trim();
787
+ title.textContent = importLine;
788
+ } else {
789
+ title.textContent = `Import: ${node.name}`;
790
+ }
791
+ } else {
792
+ title.textContent = node.name;
793
+ }
601
794
 
795
+ // Set metadata
602
796
  let metaText = `${node.type} • ${node.file_path}`;
603
797
  if (node.start_line) {
604
798
  metaText += ` • Lines ${node.start_line}-${node.end_line}`;
@@ -608,22 +802,178 @@
608
802
  }
609
803
  meta.textContent = metaText;
610
804
 
611
- // Show content if available
805
+ // Display content based on node type
806
+ if (node.type === 'directory') {
807
+ showDirectoryContents(node, content);
808
+ } else if (node.type === 'file') {
809
+ showFileContents(node, content);
810
+ } else if (node.type === 'imports') {
811
+ // Import nodes show import details
812
+ showImportDetails(node, content);
813
+ } else {
814
+ // Class, function, method, code nodes
815
+ showCodeContent(node, content);
816
+ }
817
+
818
+ pane.classList.add('visible');
819
+ }
820
+
821
+ function showDirectoryContents(node, container) {
822
+ // Find all direct children of this directory
823
+ const children = allLinks
824
+ .filter(l => (l.source.id || l.source) === node.id)
825
+ .map(l => allNodes.find(n => n.id === (l.target.id || l.target)))
826
+ .filter(n => n);
827
+
828
+ if (children.length === 0) {
829
+ container.innerHTML = '<p style="color: #8b949e;">Empty directory</p>';
830
+ return;
831
+ }
832
+
833
+ // Group by type
834
+ const files = children.filter(n => n.type === 'file');
835
+ const subdirs = children.filter(n => n.type === 'directory');
836
+ const chunks = children.filter(n => n.type !== 'file' && n.type !== 'directory');
837
+
838
+ let html = '<ul class="directory-list">';
839
+
840
+ // Show subdirectories first
841
+ subdirs.forEach(child => {
842
+ html += `
843
+ <li>
844
+ <span class="item-icon">📁</span>
845
+ ${child.name}
846
+ <span class="item-type">directory</span>
847
+ </li>
848
+ `;
849
+ });
850
+
851
+ // Then files
852
+ files.forEach(child => {
853
+ html += `
854
+ <li>
855
+ <span class="item-icon">📄</span>
856
+ ${child.name}
857
+ <span class="item-type">file</span>
858
+ </li>
859
+ `;
860
+ });
861
+
862
+ // Then code chunks
863
+ chunks.forEach(child => {
864
+ const icon = child.type === 'class' ? '🔷' : child.type === 'function' ? '⚡' : '📝';
865
+ html += `
866
+ <li>
867
+ <span class="item-icon">${icon}</span>
868
+ ${child.name}
869
+ <span class="item-type">${child.type}</span>
870
+ </li>
871
+ `;
872
+ });
873
+
874
+ html += '</ul>';
875
+
876
+ // Add summary
877
+ const summary = `<p style="color: #8b949e; font-size: 11px; margin-top: 16px;">
878
+ Total: ${children.length} items (${subdirs.length} directories, ${files.length} files, ${chunks.length} code chunks)
879
+ </p>`;
880
+
881
+ container.innerHTML = html + summary;
882
+ }
883
+
884
+ function showFileContents(node, container) {
885
+ // Find all chunks in this file
886
+ const fileChunks = allLinks
887
+ .filter(l => (l.source.id || l.source) === node.id)
888
+ .map(l => allNodes.find(n => n.id === (l.target.id || l.target)))
889
+ .filter(n => n);
890
+
891
+ if (fileChunks.length === 0) {
892
+ container.innerHTML = '<p style="color: #8b949e;">No code chunks found in this file</p>';
893
+ return;
894
+ }
895
+
896
+ // Collect all content from chunks and sort by line number
897
+ const sortedChunks = fileChunks
898
+ .filter(c => c.content)
899
+ .sort((a, b) => a.start_line - b.start_line);
900
+
901
+ if (sortedChunks.length === 0) {
902
+ container.innerHTML = '<p style="color: #8b949e;">File content not available</p>';
903
+ return;
904
+ }
905
+
906
+ // Combine all chunks to show full file
907
+ const fullContent = sortedChunks.map(c => c.content).join('\n\n');
908
+
909
+ container.innerHTML = `
910
+ <p style="color: #8b949e; font-size: 11px; margin-bottom: 12px;">
911
+ Contains ${fileChunks.length} code chunks
912
+ </p>
913
+ <pre><code>${escapeHtml(fullContent)}</code></pre>
914
+ `;
915
+ }
916
+
917
+ function showImportDetails(node, container) {
918
+ // Import nodes (type === 'imports') - show import content prominently
919
+ const importHtml = `
920
+ <div class="import-details">
921
+ ${node.content ? `
922
+ <div style="margin-bottom: 16px;">
923
+ <div class="detail-label" style="margin-bottom: 8px;">Import Statement:</div>
924
+ <pre><code>${escapeHtml(node.content)}</code></pre>
925
+ </div>
926
+ ` : '<p style="color: #8b949e;">No import content available</p>'}
927
+ <div class="detail-row">
928
+ <span class="detail-label">File:</span> ${node.file_path}
929
+ </div>
930
+ ${node.start_line ? `
931
+ <div class="detail-row">
932
+ <span class="detail-label">Location:</span> Lines ${node.start_line}-${node.end_line}
933
+ </div>
934
+ ` : ''}
935
+ ${node.language ? `
936
+ <div class="detail-row">
937
+ <span class="detail-label">Language:</span> ${node.language}
938
+ </div>
939
+ ` : ''}
940
+ </div>
941
+ `;
942
+
943
+ container.innerHTML = importHtml;
944
+ }
945
+
946
+ function showCodeContent(node, container) {
947
+ // Show code for function, class, method, or code chunks
948
+ let html = '';
949
+
950
+ if (node.docstring) {
951
+ html += `
952
+ <div style="margin-bottom: 16px; padding: 12px; background: #161b22; border: 1px solid #30363d; border-radius: 6px;">
953
+ <div style="font-size: 11px; color: #8b949e; margin-bottom: 8px; font-weight: 600;">DOCSTRING</div>
954
+ <pre style="margin: 0; padding: 0; background: transparent; border: none;"><code>${escapeHtml(node.docstring)}</code></pre>
955
+ </div>
956
+ `;
957
+ }
958
+
612
959
  if (node.content) {
613
- code.textContent = node.content;
614
- } else if (node.docstring) {
615
- code.textContent = `// Docstring:
616
- ${node.docstring}`;
960
+ html += `<pre><code>${escapeHtml(node.content)}</code></pre>`;
617
961
  } else {
618
- code.textContent = '// No content available';
962
+ html += '<p style="color: #8b949e;">No content available</p>';
619
963
  }
620
964
 
621
- viewer.classList.add('visible');
965
+ container.innerHTML = html;
966
+ }
967
+
968
+ function escapeHtml(text) {
969
+ const div = document.createElement('div');
970
+ div.textContent = text;
971
+ return div.innerHTML;
622
972
  }
623
973
 
624
- function closeCodeViewer() {
625
- const viewer = document.getElementById('code-viewer');
626
- viewer.classList.remove('visible');
974
+ function closeContentPane() {
975
+ const pane = document.getElementById('content-pane');
976
+ pane.classList.remove('visible');
627
977
 
628
978
  // Remove highlight
629
979
  highlightedNode = null;
@@ -634,7 +984,7 @@ ${node.docstring}`;
634
984
  window.addEventListener('DOMContentLoaded', () => {
635
985
  const loadingEl = document.getElementById('loading');
636
986
 
637
- fetch(`chunk-graph.json?t=${Date.now()}`)
987
+ fetch("chunk-graph.json")
638
988
  .then(response => {
639
989
  if (!response.ok) {
640
990
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-vector-search
3
- Version: 0.12.1
3
+ Version: 0.12.2
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
@@ -490,6 +490,10 @@ uv sync
490
490
  # Install in development mode
491
491
  uv pip install -e .
492
492
 
493
+ # Test CLI from source (recommended during development)
494
+ ./dev-mcp version # Shows [DEV] indicator
495
+ ./dev-mcp search "test" # No reinstall needed after code changes
496
+
493
497
  # Run tests
494
498
  uv run pytest
495
499
 
@@ -498,6 +502,8 @@ uv run ruff check
498
502
  uv run mypy src/
499
503
  ```
500
504
 
505
+ For detailed development workflow and `dev-mcp` usage, see the [Development](#-development) section below.
506
+
501
507
  ### Adding Language Support
502
508
 
503
509
  1. Create a new parser in `src/mcp_vector_search/parsers/`
@@ -570,10 +576,64 @@ uv sync && uv pip install -e .
570
576
  # Run development tests
571
577
  ./scripts/dev-test.sh
572
578
 
573
- # Test CLI locally
579
+ # Run CLI from source (recommended during development)
580
+ ./dev-mcp version # Visual [DEV] indicator
581
+ ./dev-mcp status # Any command works
582
+ ./dev-mcp search "auth" # Immediate feedback on changes
583
+
584
+ # Alternative: use uv run directly
574
585
  uv run mcp-vector-search version
575
586
  ```
576
587
 
588
+ #### Using the `dev-mcp` Development Helper
589
+
590
+ The `./dev-mcp` script provides a streamlined way to run the CLI from source code during development, eliminating the need for repeated installations.
591
+
592
+ **Key Features:**
593
+ - **Visual [DEV] Indicator**: Shows `[DEV]` prefix to distinguish from installed version
594
+ - **No Reinstall Required**: Reflects code changes immediately
595
+ - **Complete Argument Forwarding**: Works with all CLI commands and options
596
+ - **Verbose Mode**: Debug output with `--verbose` flag
597
+ - **Built-in Help**: Script usage with `--help`
598
+
599
+ **Usage Examples:**
600
+ ```bash
601
+ # Basic commands (note the [DEV] prefix in output)
602
+ ./dev-mcp version
603
+ ./dev-mcp status
604
+ ./dev-mcp index
605
+ ./dev-mcp search "authentication logic"
606
+
607
+ # With CLI options
608
+ ./dev-mcp search "error handling" --limit 10
609
+ ./dev-mcp index --force
610
+
611
+ # Script verbose mode (shows Python interpreter, paths)
612
+ ./dev-mcp --verbose search "database"
613
+
614
+ # Script help (shows dev-mcp usage, not CLI help)
615
+ ./dev-mcp --help
616
+
617
+ # CLI command help (forwards --help to the CLI)
618
+ ./dev-mcp search --help
619
+ ./dev-mcp index --help
620
+ ```
621
+
622
+ **When to Use:**
623
+ - **`./dev-mcp`** → Development workflow (runs from source code)
624
+ - **`mcp-vector-search`** → Production usage (runs installed version via pipx/pip)
625
+
626
+ **Benefits:**
627
+ - **Instant Feedback**: Changes to source code are reflected immediately
628
+ - **No Build Step**: Skip the reinstall cycle during active development
629
+ - **Clear Context**: Visual `[DEV]` indicator prevents confusion about which version is running
630
+ - **Error Handling**: Built-in checks for uv installation and project structure
631
+
632
+ **Requirements:**
633
+ - Must have `uv` installed (`pip install uv`)
634
+ - Must run from project root directory
635
+ - Requires `pyproject.toml` in current directory
636
+
577
637
  **Stage B: Local Deployment Testing**
578
638
  ```bash
579
639
  # Build and test clean deployment
@@ -1,4 +1,4 @@
1
- mcp_vector_search/__init__.py,sha256=x50WkrKjIL9FUVrHMUHOyx99aKCd-VPkQ4dT60f0tlA,300
1
+ mcp_vector_search/__init__.py,sha256=VI7aQii3CQwhLiBXYs4cQi58X3uMRZ9oKNT2G7olYd4,300
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=8yF15w74xq9uUT46ww-XNidBs7cSPVYV7mKiMS4bRqY,16318
@@ -21,7 +21,7 @@ mcp_vector_search/cli/commands/reset.py,sha256=bsIT6zjDf6gsvIkVaRaUClYzlTyNe--8t
21
21
  mcp_vector_search/cli/commands/search.py,sha256=yyou7wO9qZ_w2oiKdyOrk2WUxvkFpc-Up8hpflxYlyw,24802
22
22
  mcp_vector_search/cli/commands/status.py,sha256=-Ke58000-bi74T0J-dy1zaD3C2TuZBSdERm9mIm3kXI,19814
23
23
  mcp_vector_search/cli/commands/uninstall.py,sha256=XjFoSJlQkM1GPg7zaK65jOvb-hb87F2Ciyldbn8y2jc,14953
24
- mcp_vector_search/cli/commands/visualize.py,sha256=1KlMP6upluYFWeu54Y7btHYBc73Qok-jKBdva0EoHm8,51677
24
+ mcp_vector_search/cli/commands/visualize.py,sha256=VwzrMCOTfdjzDOrP3ry3SwW68YKYNRFKV4vzlbxfqRM,52331
25
25
  mcp_vector_search/cli/commands/watch.py,sha256=2pyWRoo4fIppFnyQ4sW4IBLHmpb_IwnTjRnzHkVBPcQ,8927
26
26
  mcp_vector_search/config/__init__.py,sha256=r_qAQkU5gc0EQ2pv8EQARACe4klhrR_WRJqCb9lfGc0,54
27
27
  mcp_vector_search/config/constants.py,sha256=afXR6SvLLd8QYY4MG4s1vq-hCJiQsE5PhnE-XG9lvb4,1092
@@ -61,9 +61,33 @@ mcp_vector_search/utils/gitignore.py,sha256=hJHt5YsfEvLIfweaa968tGTavcbxqh3X5nSa
61
61
  mcp_vector_search/utils/monorepo.py,sha256=leTYx4ffN4IO0wDg7OWYfXMWMPp2Q_uEHl5WQFNk5Hs,8657
62
62
  mcp_vector_search/utils/timing.py,sha256=THC7mfbTYnUpnnDcblgQacYMzbEkfFoIShx6plmhCgg,11285
63
63
  mcp_vector_search/utils/version.py,sha256=d7fS-CLemxb8UzZ9j18zH0Y0Ud097ljKKYYOPulnGPE,1138
64
- mcp_vector_search/visualization/index.html,sha256=OHbMTjsyFXj0s8vKd9MjCgfIEflWfSlNNBTDUzOxgfM,22899
65
- mcp_vector_search-0.12.1.dist-info/METADATA,sha256=cO-orPU1CPd1VxYtrbNPdMPxhTWmAWfF_f7wE6C3Qw0,20606
66
- mcp_vector_search-0.12.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
67
- mcp_vector_search-0.12.1.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
68
- mcp_vector_search-0.12.1.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
69
- mcp_vector_search-0.12.1.dist-info/RECORD,,
64
+ mcp_vector_search/visualization/favicon-v1-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
65
+ mcp_vector_search/visualization/favicon-v1-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
66
+ mcp_vector_search/visualization/favicon-v1-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
67
+ mcp_vector_search/visualization/favicon-v1-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
68
+ mcp_vector_search/visualization/favicon-v1-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
69
+ mcp_vector_search/visualization/favicon-v1-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
70
+ mcp_vector_search/visualization/favicon-v1-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
71
+ mcp_vector_search/visualization/favicon-v1.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
72
+ mcp_vector_search/visualization/favicon-v2-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
73
+ mcp_vector_search/visualization/favicon-v2-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
74
+ mcp_vector_search/visualization/favicon-v2-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
75
+ mcp_vector_search/visualization/favicon-v2-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
76
+ mcp_vector_search/visualization/favicon-v2-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
77
+ mcp_vector_search/visualization/favicon-v2-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
78
+ mcp_vector_search/visualization/favicon-v2-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
79
+ mcp_vector_search/visualization/favicon-v2.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
80
+ mcp_vector_search/visualization/favicon-v3-1024.png,sha256=1pESJk22wsG29ryTC31884wnpP6tG1AAJZJlxUBcZu8,3129
81
+ mcp_vector_search/visualization/favicon-v3-128.png,sha256=29jmI1odupCm1eUZnbQhtunwN9VmQ9Ld7AGxivt65ko,127
82
+ mcp_vector_search/visualization/favicon-v3-16.png,sha256=WEitLws7EWu9zvgmYV0JNklwtRJNZmzhq_nuixD5_TY,74
83
+ mcp_vector_search/visualization/favicon-v3-256.png,sha256=o2hBl9H7pjH2Ovp5HRUF5uQS_BxQXTE2vyZLiiX-cRg,270
84
+ mcp_vector_search/visualization/favicon-v3-32.png,sha256=_t-DLHfL_pBXbYtKJLi02wOeGP_HH11QWdElS3fc9uU,83
85
+ mcp_vector_search/visualization/favicon-v3-512.png,sha256=Fwfv5FcwfQpeL1oewc_1WypEKG88TY3aMr85S-iWO4s,843
86
+ mcp_vector_search/visualization/favicon-v3-64.png,sha256=nD-ibvLuxrrmDjhiIf1hIWMsyOIkozk2W7fakDCmlF0,91
87
+ mcp_vector_search/visualization/favicon-v3.ico,sha256=VYru2inbgzlru_ZLpqOO_rMrGxCGU7_eFcMuLx2Bx58,96
88
+ mcp_vector_search/visualization/index.html,sha256=10dKa2itpjqxw9aHMs-HWpw5WWTe5EB1NqYRs5fnxxw,36830
89
+ mcp_vector_search-0.12.2.dist-info/METADATA,sha256=zZzHr70_Z_MueRsrzCSCrUJU5WcXl97fs7WrA6zrIhQ,22839
90
+ mcp_vector_search-0.12.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
91
+ mcp_vector_search-0.12.2.dist-info/entry_points.txt,sha256=y3Ygtc_JiBchNEIL-tPABo7EbzBExGAxwGdkkeP5D2I,86
92
+ mcp_vector_search-0.12.2.dist-info/licenses/LICENSE,sha256=FqZUgGJH_tZKZLQsMCpXaLawRyLmyFKRVfMwYyEcyTs,1072
93
+ mcp_vector_search-0.12.2.dist-info/RECORD,,