@emtai/xray-vision 1.0.0

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 (2) hide show
  1. package/bin/xray-vision.mjs +378 -0
  2. package/package.json +25 -0
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * XRay-Vision CLI — One-time setup for Claude Code
5
+ *
6
+ * Usage:
7
+ * npx @emtai/xray-vision init
8
+ * npx @emtai/xray-vision init --no-instructions
9
+ * npx @emtai/xray-vision init --no-browser
10
+ * npx @emtai/xray-vision status
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import os from 'node:os';
16
+ import { execSync } from 'node:child_process';
17
+
18
+ // ============================================================================
19
+ // Constants
20
+ // ============================================================================
21
+
22
+ const MCP_SERVER_URL = 'https://mcp.emtailabs.com/mcp';
23
+ const AUTH_URL = 'https://mcp.emtailabs.com/authorize';
24
+ const DASHBOARD_URL = 'https://emtai-xray.emtailabs.com/dashboard';
25
+
26
+ const MCP_CONFIG = {
27
+ 'xray-vision': {
28
+ type: 'http',
29
+ url: MCP_SERVER_URL,
30
+ },
31
+ };
32
+
33
+ // ============================================================================
34
+ // ANSI Colors (no dependencies)
35
+ // ============================================================================
36
+
37
+ const c = {
38
+ reset: '\x1b[0m',
39
+ bold: '\x1b[1m',
40
+ dim: '\x1b[2m',
41
+ green: '\x1b[32m',
42
+ blue: '\x1b[34m',
43
+ cyan: '\x1b[36m',
44
+ yellow: '\x1b[33m',
45
+ red: '\x1b[31m',
46
+ magenta: '\x1b[35m',
47
+ };
48
+
49
+ const ok = (msg) => console.log(`${c.green}✓${c.reset} ${msg}`);
50
+ const info = (msg) => console.log(`${c.blue}ℹ${c.reset} ${msg}`);
51
+ const warn = (msg) => console.log(`${c.yellow}⚠${c.reset} ${msg}`);
52
+ const err = (msg) => console.log(`${c.red}✗${c.reset} ${msg}`);
53
+ const step = (n, msg) => console.log(`\n${c.cyan}[${n}]${c.reset} ${c.bold}${msg}${c.reset}`);
54
+
55
+ // ============================================================================
56
+ // Paths
57
+ // ============================================================================
58
+
59
+ const HOME = os.homedir();
60
+ const CLAUDE_DIR = path.join(HOME, '.claude');
61
+ const MCP_JSON_PATH = path.join(CLAUDE_DIR, 'mcp.json');
62
+ const INSTRUCTIONS_PATH = path.join(CLAUDE_DIR, 'instructions.md');
63
+
64
+ // ============================================================================
65
+ // XRay Instructions (embedded — preloaded into Claude's context)
66
+ // ============================================================================
67
+
68
+ const XRAY_INSTRUCTIONS = `# XRay-Vision — AI Code Intelligence
69
+
70
+ You have access to XRay-Vision MCP tools for deep code analysis. Use them for ALL code-related work.
71
+
72
+ ## Core Rules
73
+
74
+ 1. **XRay-first search** — Use \`fnid_lookup\`, \`graph_query\`, \`fnid_source\` instead of Grep/Glob/Read for code searches
75
+ 2. **Before code changes** — Run \`fnid_lookup\` to understand the function, \`graph_query\` to check callers and dependencies
76
+ 3. **After code changes** — Run \`indexer_changed\` to update the index, then \`xray_full\` to verify no regressions
77
+ 4. **Before commits** — Run \`xray_security\` to catch vulnerabilities
78
+
79
+ ## Quick Reference
80
+
81
+ ### Finding Code
82
+ | Task | XRay Tool | NOT This |
83
+ |------|-----------|----------|
84
+ | Find a function | \`fnid_lookup name="foo"\` | Grep for "function foo" |
85
+ | Read function source | \`fnid_source fnid="..."\` | Read entire file |
86
+ | Find callers | \`graph_query "MATCH (c)-[:CALLS]->(f {name:'foo'}) RETURN c"\` | Grep for "foo(" |
87
+ | Find dependencies | \`graph_query\` with CALLS/IMPORTS patterns | Manual file reading |
88
+
89
+ ### Repository Setup
90
+ \`\`\`
91
+ github_clone url="https://github.com/owner/repo" # Clone
92
+ indexer_full repo="<repoId>" # Full index (first time)
93
+ indexer_changed repo="<repoId>" # Incremental (after changes)
94
+ \`\`\`
95
+
96
+ ### Analysis Tools
97
+ - \`xray_full\` — Comprehensive health check (run after every change)
98
+ - \`xray_security\` — Security vulnerabilities (run before every commit)
99
+ - \`xray_complexity\` — Cyclomatic/cognitive complexity hotspots
100
+ - \`xray_architecture\` — Circular deps, layer violations, modularity
101
+ - \`xray_debt\` — Technical debt estimation with quick wins
102
+ - \`xray_smells\` — God objects, long methods, anti-patterns
103
+ - \`xray_testing\` — Test coverage, fragile tests
104
+ - \`xray_hotspots\` — High-churn + high-complexity files
105
+
106
+ ### Code Navigation
107
+ - \`fnid_lookup\` — Find function by name/file
108
+ - \`fnid_source\` — Get function source code
109
+ - \`graph_query\` — Custom Cypher queries (no LIMIT clause!)
110
+ - \`file_read_range\` — Read specific lines from a file
111
+
112
+ ### Remediation
113
+ - \`remediate_dead_code\` — Remove dead code (creates backups)
114
+ - \`remediate_duplicate\` — Merge duplicate functions
115
+ - \`refactor_extract_function\` — Extract code to new function
116
+ - \`refactor_rename\` — Rename across codebase
117
+
118
+ ### Memory
119
+ - \`remember\` — Store findings, patterns, decisions
120
+ - \`evoke\` — Search memories by query/category
121
+
122
+ ## Workflow: Feature Development
123
+ 1. \`fnid_lookup\` + \`graph_query\` → understand existing code
124
+ 2. \`xray_architecture\` → verify where new code fits
125
+ 3. Implement the feature
126
+ 4. \`indexer_changed\` → update index
127
+ 5. \`xray_full\` + \`xray_security\` → verify no regressions
128
+ 6. \`remember\` → record patterns/decisions
129
+
130
+ ## Graph Query Rules
131
+ - **NEVER** add \`LIMIT\` to queries — you WILL miss critical data
132
+ - Use \`WHERE\` to filter, \`ORDER BY\` to organize
133
+ - Always include \`tenantId\` in queries for multi-tenant isolation
134
+ `;
135
+
136
+ // ============================================================================
137
+ // Helpers
138
+ // ============================================================================
139
+
140
+ function openBrowser(url) {
141
+ try {
142
+ const platform = process.platform;
143
+ if (platform === 'darwin') {
144
+ execSync(`open "${url}"`, { stdio: 'ignore' });
145
+ } else if (platform === 'win32') {
146
+ execSync(`start "" "${url}"`, { stdio: 'ignore' });
147
+ } else {
148
+ execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
149
+ }
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+
156
+ function readJsonFile(filePath) {
157
+ try {
158
+ const content = fs.readFileSync(filePath, 'utf-8');
159
+ return JSON.parse(content);
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+
165
+ function writeJsonFile(filePath, data) {
166
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
167
+ }
168
+
169
+ // ============================================================================
170
+ // Commands
171
+ // ============================================================================
172
+
173
+ function cmdInit(flags) {
174
+ const skipInstructions = flags.includes('--no-instructions');
175
+ const skipBrowser = flags.includes('--no-browser');
176
+
177
+ console.log(`\n${c.bold}${c.magenta} XRay-Vision${c.reset} ${c.dim}— One-time setup for Claude Code${c.reset}\n`);
178
+
179
+ // ── Step 1: Ensure ~/.claude/ exists ──
180
+ step(1, 'Checking Claude Code directory');
181
+
182
+ if (!fs.existsSync(CLAUDE_DIR)) {
183
+ fs.mkdirSync(CLAUDE_DIR, { recursive: true });
184
+ ok(`Created ${c.cyan}~/.claude/${c.reset}`);
185
+ } else {
186
+ ok(`Found ${c.cyan}~/.claude/${c.reset}`);
187
+ }
188
+
189
+ // ── Step 2: Add MCP config ──
190
+ step(2, 'Configuring MCP server');
191
+
192
+ let mcpJson = readJsonFile(MCP_JSON_PATH);
193
+ let configChanged = false;
194
+
195
+ if (!mcpJson) {
196
+ mcpJson = { mcpServers: {} };
197
+ configChanged = true;
198
+ info('Creating new mcp.json');
199
+ }
200
+
201
+ if (!mcpJson.mcpServers) {
202
+ mcpJson.mcpServers = {};
203
+ configChanged = true;
204
+ }
205
+
206
+ const existing = mcpJson.mcpServers['xray-vision'];
207
+ if (existing) {
208
+ // Check if it's the old npx/stdio config or has Bearer headers
209
+ const isOldConfig =
210
+ existing.command ||
211
+ existing.args ||
212
+ existing.headers?.Authorization?.includes('Bearer xray-') ||
213
+ existing.url !== MCP_SERVER_URL;
214
+
215
+ if (isOldConfig) {
216
+ mcpJson.mcpServers['xray-vision'] = { ...MCP_CONFIG['xray-vision'] };
217
+ configChanged = true;
218
+ ok(`Updated xray-vision config ${c.dim}(replaced old config)${c.reset}`);
219
+ } else {
220
+ ok(`xray-vision already configured ${c.dim}(up to date)${c.reset}`);
221
+ }
222
+ } else {
223
+ mcpJson.mcpServers['xray-vision'] = { ...MCP_CONFIG['xray-vision'] };
224
+ configChanged = true;
225
+ ok('Added xray-vision MCP server');
226
+ }
227
+
228
+ if (configChanged) {
229
+ writeJsonFile(MCP_JSON_PATH, mcpJson);
230
+ ok(`Saved ${c.cyan}~/.claude/mcp.json${c.reset}`);
231
+ }
232
+
233
+ console.log(`\n ${c.dim}Config:${c.reset}`);
234
+ console.log(` ${c.cyan}${JSON.stringify(MCP_CONFIG['xray-vision'], null, 2).split('\n').join('\n ')}${c.reset}`);
235
+
236
+ // ── Step 3: Install instructions ──
237
+ if (!skipInstructions) {
238
+ step(3, 'Installing XRay instructions');
239
+
240
+ const existingInstructions = fs.existsSync(INSTRUCTIONS_PATH)
241
+ ? fs.readFileSync(INSTRUCTIONS_PATH, 'utf-8')
242
+ : '';
243
+
244
+ if (existingInstructions.includes('XRay-Vision')) {
245
+ ok(`Instructions already present ${c.dim}(~/.claude/instructions.md)${c.reset}`);
246
+ } else {
247
+ // Append to existing instructions or create new
248
+ const content = existingInstructions
249
+ ? existingInstructions.trimEnd() + '\n\n' + XRAY_INSTRUCTIONS
250
+ : XRAY_INSTRUCTIONS;
251
+
252
+ fs.writeFileSync(INSTRUCTIONS_PATH, content, 'utf-8');
253
+ ok(`Installed XRay instructions → ${c.cyan}~/.claude/instructions.md${c.reset}`);
254
+ }
255
+ }
256
+
257
+ // ── Step 4: Open browser for auth ──
258
+ const browserStep = skipInstructions ? 3 : 4;
259
+
260
+ if (!skipBrowser) {
261
+ step(browserStep, 'Opening browser for authentication');
262
+
263
+ info('Claude Code will authenticate via Cloudflare Access on first connect.');
264
+ info('Opening browser to verify your account is set up...\n');
265
+
266
+ const opened = openBrowser(DASHBOARD_URL);
267
+ if (opened) {
268
+ ok('Browser opened — sign in to verify your account');
269
+ } else {
270
+ warn(`Could not open browser. Visit manually:\n ${c.cyan}${DASHBOARD_URL}${c.reset}`);
271
+ }
272
+ }
273
+
274
+ // ── Done ──
275
+ console.log(`\n${c.green}${c.bold} Setup complete!${c.reset}\n`);
276
+ console.log(` ${c.bold}Next steps:${c.reset}`);
277
+ console.log(` ${c.cyan}1.${c.reset} Restart Claude Code ${c.dim}(required for MCP config to load)${c.reset}`);
278
+ console.log(` ${c.cyan}2.${c.reset} Run ${c.cyan}/mcp${c.reset} to verify xray-vision is connected`);
279
+ console.log(` ${c.cyan}3.${c.reset} A browser window will open for Cloudflare auth on first connect`);
280
+ console.log(` ${c.cyan}4.${c.reset} Clone a repo: ${c.cyan}github_clone url="https://github.com/you/repo"${c.reset}`);
281
+ console.log(` ${c.cyan}5.${c.reset} Run full analysis: ${c.cyan}indexer_full${c.reset} then ${c.cyan}xray_full${c.reset}\n`);
282
+ }
283
+
284
+ function cmdStatus() {
285
+ console.log(`\n${c.bold}${c.magenta} XRay-Vision${c.reset} ${c.dim}— Status check${c.reset}\n`);
286
+
287
+ // Check mcp.json
288
+ const mcpJson = readJsonFile(MCP_JSON_PATH);
289
+ const xrayConfig = mcpJson?.mcpServers?.['xray-vision'];
290
+
291
+ if (xrayConfig) {
292
+ if (xrayConfig.url === MCP_SERVER_URL && xrayConfig.type === 'http' && !xrayConfig.headers) {
293
+ ok(`MCP config: ${c.green}OAuth (current)${c.reset}`);
294
+ } else if (xrayConfig.headers?.Authorization) {
295
+ warn(`MCP config: ${c.yellow}API key auth (outdated)${c.reset} — run ${c.cyan}npx @emtai/xray-vision init${c.reset} to upgrade`);
296
+ } else if (xrayConfig.command) {
297
+ warn(`MCP config: ${c.yellow}npx bridge (outdated)${c.reset} — run ${c.cyan}npx @emtai/xray-vision init${c.reset} to upgrade`);
298
+ } else {
299
+ info(`MCP config: ${c.blue}unknown format${c.reset}`);
300
+ }
301
+ console.log(` ${c.dim}URL: ${xrayConfig.url || 'not set'}${c.reset}`);
302
+ console.log(` ${c.dim}Type: ${xrayConfig.type || 'not set'}${c.reset}`);
303
+ } else {
304
+ err(`MCP config: ${c.red}not found${c.reset} — run ${c.cyan}npx @emtai/xray-vision init${c.reset}`);
305
+ }
306
+
307
+ // Check instructions
308
+ if (fs.existsSync(INSTRUCTIONS_PATH)) {
309
+ const content = fs.readFileSync(INSTRUCTIONS_PATH, 'utf-8');
310
+ if (content.includes('XRay-Vision')) {
311
+ ok(`Instructions: ${c.green}installed${c.reset} ${c.dim}(~/.claude/instructions.md)${c.reset}`);
312
+ } else {
313
+ warn(`Instructions: ${c.yellow}file exists but no XRay content${c.reset}`);
314
+ }
315
+ } else {
316
+ info(`Instructions: ${c.blue}not installed${c.reset} ${c.dim}(optional)${c.reset}`);
317
+ }
318
+
319
+ console.log('');
320
+ }
321
+
322
+ function cmdHelp() {
323
+ console.log(`
324
+ ${c.bold}${c.magenta} XRay-Vision CLI${c.reset} — Setup tool for Claude Code
325
+
326
+ ${c.bold} Usage:${c.reset}
327
+ npx @emtai/xray-vision ${c.cyan}init${c.reset} Set up XRay-Vision in Claude Code
328
+ npx @emtai/xray-vision ${c.cyan}status${c.reset} Check current configuration
329
+ npx @emtai/xray-vision ${c.cyan}help${c.reset} Show this help
330
+
331
+ ${c.bold} Init Flags:${c.reset}
332
+ ${c.dim}--no-instructions${c.reset} Skip installing XRay instructions
333
+ ${c.dim}--no-browser${c.reset} Skip opening browser for authentication
334
+
335
+ ${c.bold} What init does:${c.reset}
336
+ 1. Adds xray-vision to ${c.cyan}~/.claude/mcp.json${c.reset}
337
+ 2. Installs XRay workflow instructions to ${c.cyan}~/.claude/instructions.md${c.reset}
338
+ 3. Opens browser for Cloudflare authentication
339
+
340
+ ${c.bold} Learn more:${c.reset}
341
+ Dashboard: ${c.cyan}https://emtai-xray.emtailabs.com/dashboard${c.reset}
342
+ Pricing: ${c.cyan}https://emtai-xray.emtailabs.com/pricing${c.reset}
343
+ `);
344
+ }
345
+
346
+ // ============================================================================
347
+ // Main
348
+ // ============================================================================
349
+
350
+ const args = process.argv.slice(2);
351
+ const command = args[0] || 'help';
352
+ const flags = args.slice(1);
353
+
354
+ switch (command) {
355
+ case 'init':
356
+ case 'setup':
357
+ case 'install':
358
+ cmdInit(flags);
359
+ break;
360
+ case 'status':
361
+ case 'check':
362
+ cmdStatus();
363
+ break;
364
+ case 'help':
365
+ case '--help':
366
+ case '-h':
367
+ cmdHelp();
368
+ break;
369
+ default:
370
+ // If no recognized command, default to init (for `npx @emtai/xray-vision` with no args)
371
+ if (command.startsWith('-')) {
372
+ cmdInit([command, ...flags]);
373
+ } else {
374
+ err(`Unknown command: ${command}`);
375
+ cmdHelp();
376
+ process.exit(1);
377
+ }
378
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@emtai/xray-vision",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "One-time setup for XRay-Vision MCP in Claude Code — adds config, instructions, and authenticates",
6
+ "bin": {
7
+ "xray-vision": "./bin/xray-vision.mjs"
8
+ },
9
+ "files": [
10
+ "bin/**/*"
11
+ ],
12
+ "keywords": [
13
+ "xray-vision",
14
+ "mcp",
15
+ "claude-code",
16
+ "code-analysis",
17
+ "model-context-protocol",
18
+ "setup"
19
+ ],
20
+ "author": "eMTAi Labs",
21
+ "license": "MIT",
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }