visor-mcp 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. visor/__init__.py +8 -0
  2. visor/__pycache__/__init__.cpython-310.pyc +0 -0
  3. visor/__pycache__/__init__.cpython-312.pyc +0 -0
  4. visor/__pycache__/cli.cpython-310.pyc +0 -0
  5. visor/__pycache__/server.cpython-310.pyc +0 -0
  6. visor/cli.py +287 -0
  7. visor/db/__init__.py +0 -0
  8. visor/db/__pycache__/__init__.cpython-310.pyc +0 -0
  9. visor/db/__pycache__/__init__.cpython-312.pyc +0 -0
  10. visor/db/__pycache__/client.cpython-310.pyc +0 -0
  11. visor/db/__pycache__/client.cpython-312.pyc +0 -0
  12. visor/db/__pycache__/embeddings.cpython-310.pyc +0 -0
  13. visor/db/__pycache__/migration.cpython-310.pyc +0 -0
  14. visor/db/client.py +526 -0
  15. visor/db/embeddings.py +27 -0
  16. visor/db/migration.py +291 -0
  17. visor/extension/.gitignore +4 -0
  18. visor/extension/.vscodeignore +10 -0
  19. visor/extension/LICENSE +21 -0
  20. visor/extension/assets/hero.png +0 -0
  21. visor/extension/assets/visor-icon.svg +4 -0
  22. visor/extension/build.log +35 -0
  23. visor/extension/esbuild.js +18 -0
  24. visor/extension/package-lock.json +1670 -0
  25. visor/extension/package.json +87 -0
  26. visor/extension/src/extension.ts +343 -0
  27. visor/extension/test_connect.mjs +97 -0
  28. visor/extension/test_mcp.mjs +32 -0
  29. visor/extension/tsconfig.json +14 -0
  30. visor/extension/visor-hud-0.10.0.vsix +0 -0
  31. visor/hud/.gitignore +24 -0
  32. visor/hud/README.md +73 -0
  33. visor/hud/dump_dom.mjs +16 -0
  34. visor/hud/e2e/drift.spec.ts +16 -0
  35. visor/hud/eslint.config.js +23 -0
  36. visor/hud/index.html +13 -0
  37. visor/hud/package-lock.json +4186 -0
  38. visor/hud/package.json +37 -0
  39. visor/hud/playwright-report/index.html +90 -0
  40. visor/hud/playwright.config.ts +30 -0
  41. visor/hud/public/favicon.svg +1 -0
  42. visor/hud/public/icons.svg +24 -0
  43. visor/hud/run_playwright.mjs +27 -0
  44. visor/hud/src/App.css +184 -0
  45. visor/hud/src/App.tsx +41 -0
  46. visor/hud/src/assets/hero.png +0 -0
  47. visor/hud/src/assets/react.svg +1 -0
  48. visor/hud/src/assets/vite.svg +1 -0
  49. visor/hud/src/components/ErrorBoundary.tsx +38 -0
  50. visor/hud/src/components/GraphCanvas.tsx +832 -0
  51. visor/hud/src/components/TelemetryHUD.tsx +359 -0
  52. visor/hud/src/index.css +160 -0
  53. visor/hud/src/main.tsx +10 -0
  54. visor/hud/test-results/.last-run.json +4 -0
  55. visor/hud/test_three.js +7 -0
  56. visor/hud/tsconfig.app.json +25 -0
  57. visor/hud/tsconfig.json +7 -0
  58. visor/hud/tsconfig.node.json +24 -0
  59. visor/hud/vite.config.ts +8 -0
  60. visor/parser/__init__.py +0 -0
  61. visor/parser/__pycache__/__init__.cpython-310.pyc +0 -0
  62. visor/parser/__pycache__/__init__.cpython-312.pyc +0 -0
  63. visor/parser/__pycache__/treesitter.cpython-310.pyc +0 -0
  64. visor/parser/__pycache__/watcher.cpython-310.pyc +0 -0
  65. visor/parser/__pycache__/watcher.cpython-312.pyc +0 -0
  66. visor/parser/treesitter.py +263 -0
  67. visor/parser/watcher.py +261 -0
  68. visor/server.py +151 -0
  69. visor/tools/__init__.py +0 -0
  70. visor/tools/__pycache__/__init__.cpython-310.pyc +0 -0
  71. visor/tools/__pycache__/context_engine.cpython-310.pyc +0 -0
  72. visor/tools/__pycache__/core.cpython-310.pyc +0 -0
  73. visor/tools/context_engine.py +342 -0
  74. visor/tools/core.py +504 -0
  75. visor_mcp-1.0.0.dist-info/METADATA +256 -0
  76. visor_mcp-1.0.0.dist-info/RECORD +78 -0
  77. visor_mcp-1.0.0.dist-info/WHEEL +4 -0
  78. visor_mcp-1.0.0.dist-info/entry_points.txt +4 -0
visor/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """V.I.S.O.R — Visual Intelligence System for Orchestrated Reasoning."""
2
+
3
+ from importlib.metadata import version, PackageNotFoundError
4
+
5
+ try:
6
+ __version__ = version("visor-mcp")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0-dev"
Binary file
Binary file
visor/cli.py ADDED
@@ -0,0 +1,287 @@
1
+ """
2
+ V.I.S.O.R. CLI — Terminal interface for the Context Intelligence Engine
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ Usage:
5
+ visor context "how is authentication handled"
6
+ visor fix "login crash on null user"
7
+ visor explain "database client module"
8
+ visor trace src/auth.py src/db/client.py
9
+ visor drift
10
+ """
11
+ import argparse
12
+ import json
13
+ import sys
14
+ import os
15
+
16
+
17
+ def _init_workspace():
18
+ """Ensure WORKSPACE_ROOT is set for the engine."""
19
+ if not os.environ.get("WORKSPACE_ROOT"):
20
+ os.environ["WORKSPACE_ROOT"] = os.getcwd()
21
+
22
+
23
+ def _format_output(data: dict, human: bool = True) -> str:
24
+ """Format output as human-readable table or raw JSON."""
25
+ if not human:
26
+ return json.dumps(data, indent=2)
27
+
28
+ lines = []
29
+
30
+ # Header: query + intent + skill
31
+ debug = data.get("debug", {})
32
+ lines.append(f"\n{'='*60}")
33
+ lines.append(" V.I.S.O.R. Context Intelligence Engine")
34
+ lines.append(f"{'='*60}")
35
+ lines.append(f" Query: {data.get('query', '?')}")
36
+ lines.append(f" Intent: {debug.get('intent', '?')}")
37
+ if debug.get("skill"):
38
+ lines.append(f" Skill: {debug['skill']}")
39
+ lines.append(f"{'─'*60}")
40
+
41
+ # Metrics
42
+ metrics = data.get("metrics", {})
43
+ if metrics:
44
+ without = metrics.get("estimated_tokens_without", 0)
45
+ with_t = metrics.get("estimated_tokens_with", 0)
46
+ reduction = metrics.get("reduction_percent", 0)
47
+ lines.append(f" Tokens without V.I.S.O.R.: {without:,}")
48
+ lines.append(f" Tokens with V.I.S.O.R.: {with_t:,}")
49
+ lines.append(f" Reduction: {reduction}%")
50
+ lines.append(f"{'─'*60}")
51
+
52
+ # Context nodes
53
+ context = data.get("context", [])
54
+ if context:
55
+ lines.append(f" Selected {len(context)} nodes (truncated={data.get('truncated', False)}):")
56
+ lines.append("")
57
+ for node in context:
58
+ nid = str(node.get("id", "?"))
59
+ score = node.get("relevance_score", 0)
60
+ lines.append(f" [{score:.4f}] {node.get('file_path', '?')}:{node.get('name', '?')}")
61
+
62
+ # Reasoning
63
+ reasoning = debug.get("reasoning", {}).get(nid, [])
64
+ for r in reasoning:
65
+ lines.append(f" → {r}")
66
+ lines.append("")
67
+ else:
68
+ lines.append(" No relevant context found.")
69
+ lines.append(" → V.I.S.O.R. needs to index your workspace first.")
70
+ lines.append(" → Run the MCP server or use the IDE extension to trigger indexing.")
71
+
72
+ # Recommended next tools
73
+ rec = data.get("recommended_next", [])
74
+ if rec:
75
+ lines.append(f" Recommended next: {', '.join(rec)}")
76
+
77
+ lines.append(f"{'='*60}\n")
78
+ return "\n".join(lines)
79
+
80
+
81
+ def cmd_context(args):
82
+ """Run build_context with optional skill."""
83
+ _init_workspace()
84
+ from visor.tools.context_engine import build_context
85
+ result = build_context(args.query, skill_name=args.skill)
86
+ print(_format_output(result, human=not args.json))
87
+
88
+
89
+ def cmd_fix(args):
90
+ """Shortcut: build_context with bug-fixer skill."""
91
+ _init_workspace()
92
+ from visor.tools.context_engine import build_context
93
+ result = build_context(args.query, skill_name="bug-fixer")
94
+ print(_format_output(result, human=not args.json))
95
+
96
+
97
+ def cmd_explain(args):
98
+ """Shortcut: build_context with architecture-explainer skill."""
99
+ _init_workspace()
100
+ from visor.tools.context_engine import build_context
101
+ result = build_context(args.query, skill_name="architecture-explainer")
102
+ print(_format_output(result, human=not args.json))
103
+
104
+
105
+ def cmd_trace(args):
106
+ """Trace architectural path between two files."""
107
+ _init_workspace()
108
+ import networkx as nx
109
+ from visor.db.client import db_client
110
+
111
+ db_client.conn.commit()
112
+ cursor = db_client.conn.cursor()
113
+ cursor.execute("SELECT from_node, to_node FROM edges")
114
+
115
+ G = nx.DiGraph()
116
+ for from_n, to_n in cursor.fetchall():
117
+ G.add_edge(from_n, to_n)
118
+
119
+ if not G.has_node(args.source) or not G.has_node(args.target):
120
+ print(json.dumps({"error": "Source or target node not found in graph."}))
121
+ return
122
+
123
+ try:
124
+ path = nx.shortest_path(G, source=args.source, target=args.target)
125
+ result = {"source": args.source, "target": args.target, "path": path, "hops": len(path) - 1}
126
+ except nx.NetworkXNoPath:
127
+ result = {"error": "No path found between these nodes."}
128
+
129
+ print(json.dumps(result, indent=2))
130
+
131
+
132
+ def cmd_drift(args):
133
+ """Check drift status for all tracked files."""
134
+ _init_workspace()
135
+ from visor.db.client import db_client
136
+
137
+ db_client.conn.commit()
138
+ cursor = db_client.conn.cursor()
139
+
140
+ try:
141
+ cursor.execute("SELECT file_path, changed_at FROM file_changelog ORDER BY changed_at DESC LIMIT 20")
142
+ rows = cursor.fetchall()
143
+ except Exception:
144
+ rows = []
145
+
146
+ if not rows:
147
+ print("No file changes tracked yet. The file watcher may not have started.")
148
+ return
149
+
150
+ print(f"\n{'='*60}")
151
+ print(" V.I.S.O.R. Drift Monitor — Recent Changes")
152
+ print(f"{'='*60}")
153
+ for path, changed_at in rows:
154
+ print(f" {changed_at} {path}")
155
+ print(f"{'='*60}\n")
156
+
157
+
158
+ def cmd_init(args):
159
+ """Auto-detect IDE and generate MCP config for V.I.S.O.R."""
160
+ import shutil
161
+
162
+ visor_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
163
+
164
+ # Try to find the visor executable/package path
165
+ visor_bin = shutil.which("visor-mcp")
166
+
167
+ mcp_entry = {
168
+ "command": "uv",
169
+ "args": [
170
+ "--directory", visor_path,
171
+ "run", "-q", os.path.join(visor_path, "src", "visor", "server.py"),
172
+ ],
173
+ "env": {}
174
+ }
175
+
176
+ # If installed via pip, use the entry point directly
177
+ if visor_bin:
178
+ mcp_entry = {
179
+ "command": "visor-mcp",
180
+ "args": [],
181
+ "env": {}
182
+ }
183
+
184
+ # Detect IDE configs
185
+ ide_configs = [
186
+ ("Antigravity", os.path.expanduser("~/.gemini/antigravity/mcp_config.json")),
187
+ ("Cursor", os.path.expanduser("~/.cursor/mcp.json")),
188
+ ]
189
+
190
+ config_written = False
191
+ for ide_name, config_path in ide_configs:
192
+ if os.path.exists(os.path.dirname(config_path)):
193
+ # Read existing config or create new
194
+ if os.path.exists(config_path):
195
+ with open(config_path, "r") as f:
196
+ try:
197
+ config = json.load(f)
198
+ except json.JSONDecodeError:
199
+ config = {}
200
+ else:
201
+ config = {}
202
+
203
+ if "mcpServers" not in config:
204
+ config["mcpServers"] = {}
205
+
206
+ if "visor" in config["mcpServers"]:
207
+ print(f" ✓ {ide_name}: V.I.S.O.R. already configured at {config_path}")
208
+ config_written = True
209
+ continue
210
+
211
+ config["mcpServers"]["visor"] = mcp_entry
212
+
213
+ with open(config_path, "w") as f:
214
+ json.dump(config, f, indent=2)
215
+
216
+ print(f" ✓ {ide_name}: Config written to {config_path}")
217
+ config_written = True
218
+
219
+ if not config_written:
220
+ # Fallback: print the config for manual setup
221
+ print(" No supported IDE detected. Add this to your MCP config:\n")
222
+ print(json.dumps({"visor": mcp_entry}, indent=2))
223
+ print()
224
+
225
+ print(f"\n{'='*60}")
226
+ print(" V.I.S.O.R. is ready!")
227
+ print(f"{'='*60}")
228
+ print(" Next steps:")
229
+ print(" 1. Restart your IDE / AI session")
230
+ print(" 2. Your AI agent now has access to 16 V.I.S.O.R. tools")
231
+ print(" 3. Try: ask your agent to 'use build_context to find auth code'")
232
+ print(f"{'='*60}\n")
233
+
234
+
235
+ def main():
236
+ parser = argparse.ArgumentParser(
237
+ prog="visor",
238
+ description="V.I.S.O.R. — Context Intelligence Engine CLI",
239
+ )
240
+ from visor import __version__
241
+ parser.add_argument("--version", "-v", action="version", version=f"V.I.S.O.R. {__version__}")
242
+ sub = parser.add_subparsers(dest="command", help="Available commands")
243
+
244
+ # visor init
245
+ p_init = sub.add_parser("init", help="Auto-configure V.I.S.O.R. for your IDE")
246
+ p_init.set_defaults(func=cmd_init)
247
+
248
+ # visor context
249
+ p_ctx = sub.add_parser("context", help="Build ranked context for a query")
250
+ p_ctx.add_argument("query", help="Natural language query")
251
+ p_ctx.add_argument("--skill", "-s", default=None, help="Apply a skill strategy")
252
+ p_ctx.add_argument("--json", "-j", action="store_true", help="Output raw JSON")
253
+ p_ctx.set_defaults(func=cmd_context)
254
+
255
+ # visor fix
256
+ p_fix = sub.add_parser("fix", help="Find context for a bug (uses bug-fixer skill)")
257
+ p_fix.add_argument("query", help="Bug description")
258
+ p_fix.add_argument("--json", "-j", action="store_true", help="Output raw JSON")
259
+ p_fix.set_defaults(func=cmd_fix)
260
+
261
+ # visor explain
262
+ p_exp = sub.add_parser("explain", help="Explain a module (uses architecture-explainer skill)")
263
+ p_exp.add_argument("query", help="What to explain")
264
+ p_exp.add_argument("--json", "-j", action="store_true", help="Output raw JSON")
265
+ p_exp.set_defaults(func=cmd_explain)
266
+
267
+ # visor trace
268
+ p_trace = sub.add_parser("trace", help="Trace path between two files")
269
+ p_trace.add_argument("source", help="Source file path")
270
+ p_trace.add_argument("target", help="Target file path")
271
+ p_trace.set_defaults(func=cmd_trace)
272
+
273
+ # visor drift
274
+ p_drift = sub.add_parser("drift", help="Show recent file changes (drift detection)")
275
+ p_drift.set_defaults(func=cmd_drift)
276
+
277
+ args = parser.parse_args()
278
+ if not args.command:
279
+ parser.print_help()
280
+ sys.exit(1)
281
+
282
+ args.func(args)
283
+
284
+
285
+ if __name__ == "__main__":
286
+ main()
287
+
visor/db/__init__.py ADDED
File without changes