xray-code 0.1.5 → 0.2.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.
- package/dist/cli.js +179 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.js +32 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -312,4 +312,183 @@ queryOpts(program
|
|
|
312
312
|
process.exit(1);
|
|
313
313
|
}
|
|
314
314
|
});
|
|
315
|
+
// ─── Call graph commands ────────────────────────────────────────
|
|
316
|
+
queryOpts(program
|
|
317
|
+
.command("callers <symbol>")
|
|
318
|
+
.description("Who calls this symbol?")
|
|
319
|
+
.option("-n, --limit <n>", "Max results", "20")
|
|
320
|
+
.option("--json", "Output as JSON"))
|
|
321
|
+
.action(async (symbol, opts) => {
|
|
322
|
+
try {
|
|
323
|
+
const client = getClient(opts);
|
|
324
|
+
const res = await client.callers(symbol, parseInt(opts.limit));
|
|
325
|
+
if (opts.json) {
|
|
326
|
+
console.log(JSON.stringify(res, null, 2));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (res.length === 0) {
|
|
330
|
+
console.log(`No callers found for '${symbol}'`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
console.log(`\n ${res.length} callers of '${symbol}':\n`);
|
|
334
|
+
res.forEach((c) => {
|
|
335
|
+
console.log(` ${c.caller_file}:${c.call_site_line} ${c.caller_name} [${c.confidence}]`);
|
|
336
|
+
});
|
|
337
|
+
console.log("");
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
console.error(`Query failed: ${e}`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
queryOpts(program
|
|
345
|
+
.command("callees <symbol>")
|
|
346
|
+
.description("What does this symbol call?")
|
|
347
|
+
.option("-n, --limit <n>", "Max results", "20")
|
|
348
|
+
.option("--json", "Output as JSON"))
|
|
349
|
+
.action(async (symbol, opts) => {
|
|
350
|
+
try {
|
|
351
|
+
const client = getClient(opts);
|
|
352
|
+
const res = await client.callees(symbol, parseInt(opts.limit));
|
|
353
|
+
if (opts.json) {
|
|
354
|
+
console.log(JSON.stringify(res, null, 2));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (res.length === 0) {
|
|
358
|
+
console.log(`'${symbol}' doesn't call anything`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
console.log(`\n ${res.length} callees of '${symbol}':\n`);
|
|
362
|
+
res.forEach((c) => {
|
|
363
|
+
console.log(` ${c.callee_file} ${c.callee_name} (line ${c.call_site_line})`);
|
|
364
|
+
});
|
|
365
|
+
console.log("");
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
console.error(`Query failed: ${e}`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
// ─── Analysis commands ─────────────────────────────────────────
|
|
373
|
+
queryOpts(program
|
|
374
|
+
.command("blast-radius <symbol>")
|
|
375
|
+
.description("Transitive impact — what breaks if I change this?")
|
|
376
|
+
.option("-d, --depth <n>", "Traversal depth", "2")
|
|
377
|
+
.option("--json", "Output as JSON"))
|
|
378
|
+
.action(async (symbol, opts) => {
|
|
379
|
+
try {
|
|
380
|
+
const client = getClient(opts);
|
|
381
|
+
const res = await client.transitiveImpact(symbol, parseInt(opts.depth));
|
|
382
|
+
if (opts.json) {
|
|
383
|
+
console.log(JSON.stringify(res, null, 2));
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
console.log(JSON.stringify(res, null, 2));
|
|
387
|
+
}
|
|
388
|
+
catch (e) {
|
|
389
|
+
console.error(`Blast radius failed: ${e}`);
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
queryOpts(program
|
|
394
|
+
.command("plan-change <symbol>")
|
|
395
|
+
.description("Safe phased refactoring plan with risk assessment")
|
|
396
|
+
.option("-d, --depth <n>", "Traversal depth", "2")
|
|
397
|
+
.option("--json", "Output as JSON"))
|
|
398
|
+
.action(async (symbol, opts) => {
|
|
399
|
+
try {
|
|
400
|
+
const client = getClient(opts);
|
|
401
|
+
const res = await client.planChange(symbol, parseInt(opts.depth));
|
|
402
|
+
if (opts.json) {
|
|
403
|
+
console.log(JSON.stringify(res, null, 2));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
console.log(`\n Change Plan for '${symbol}'`);
|
|
407
|
+
console.log(` Risk: ${res.risk_level}\n`);
|
|
408
|
+
res.phases.forEach((p) => {
|
|
409
|
+
console.log(` Phase ${p.phase}: ${p.description}`);
|
|
410
|
+
p.files.forEach((f) => console.log(` - ${f}`));
|
|
411
|
+
});
|
|
412
|
+
console.log("");
|
|
413
|
+
}
|
|
414
|
+
catch (e) {
|
|
415
|
+
console.error(`Plan failed: ${e}`);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
queryOpts(program
|
|
420
|
+
.command("audit")
|
|
421
|
+
.description("Project health — dead code, hotspots, coupling")
|
|
422
|
+
.option("--json", "Output as JSON"))
|
|
423
|
+
.action(async (opts) => {
|
|
424
|
+
try {
|
|
425
|
+
const client = getClient(opts);
|
|
426
|
+
const res = await client.audit();
|
|
427
|
+
if (opts.json) {
|
|
428
|
+
console.log(JSON.stringify(res, null, 2));
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
console.log(`\n Project Health Audit`);
|
|
432
|
+
console.log(` ──────────────────────`);
|
|
433
|
+
if (res.overall_score != null)
|
|
434
|
+
console.log(` Score: ${res.overall_score}`);
|
|
435
|
+
console.log(` Dead code: ${res.dead_symbols.length} unused symbols`);
|
|
436
|
+
console.log(` Hotspots: ${res.hotspots.length}`);
|
|
437
|
+
console.log(` Coupling: ${res.coupling_warnings.length} warnings`);
|
|
438
|
+
console.log(`\n Use --json for full details.\n`);
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
console.error(`Audit failed: ${e}`);
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
queryOpts(program
|
|
446
|
+
.command("manifest")
|
|
447
|
+
.description("What capabilities does this project offer?")
|
|
448
|
+
.option("--json", "Output as JSON"))
|
|
449
|
+
.action(async (opts) => {
|
|
450
|
+
try {
|
|
451
|
+
const client = getClient(opts);
|
|
452
|
+
const res = await client.manifest();
|
|
453
|
+
console.log(JSON.stringify(res, null, 2));
|
|
454
|
+
}
|
|
455
|
+
catch (e) {
|
|
456
|
+
console.error(`Manifest failed: ${e}`);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
queryOpts(program
|
|
461
|
+
.command("context <target>")
|
|
462
|
+
.description("Get AI-ready markdown context for a symbol or file")
|
|
463
|
+
.option("--file", "Treat target as a file path instead of symbol name"))
|
|
464
|
+
.action(async (target, opts) => {
|
|
465
|
+
try {
|
|
466
|
+
const client = getClient(opts);
|
|
467
|
+
const res = opts.file
|
|
468
|
+
? await client.context({ file: target })
|
|
469
|
+
: await client.context({ symbol: target });
|
|
470
|
+
console.log(res.markdown);
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
console.error(`Context failed: ${e}`);
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
queryOpts(program
|
|
478
|
+
.command("prospect")
|
|
479
|
+
.description("Diamond prospecting — rank files by mining fertility")
|
|
480
|
+
.option("-n, --top <n>", "Top N results", "20")
|
|
481
|
+
.option("--prefix <prefix>", "Filter by path prefix")
|
|
482
|
+
.option("--json", "Output as JSON"))
|
|
483
|
+
.action(async (opts) => {
|
|
484
|
+
try {
|
|
485
|
+
const client = getClient(opts);
|
|
486
|
+
const res = await client.prospect(parseInt(opts.top), opts.prefix);
|
|
487
|
+
console.log(JSON.stringify(res, null, 2));
|
|
488
|
+
}
|
|
489
|
+
catch (e) {
|
|
490
|
+
console.error(`Prospect failed: ${e}`);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
315
494
|
program.parse();
|
package/dist/client.d.ts
CHANGED
|
@@ -33,4 +33,17 @@ export declare class XRayClient {
|
|
|
33
33
|
count: number;
|
|
34
34
|
symbols: unknown[];
|
|
35
35
|
}>;
|
|
36
|
+
callers(symbol: string, limit?: number): Promise<unknown[]>;
|
|
37
|
+
callees(symbol: string, limit?: number): Promise<unknown[]>;
|
|
38
|
+
transitiveImpact(symbol: string, depth?: number): Promise<unknown[]>;
|
|
39
|
+
planChange(symbol: string, depth?: number): Promise<unknown>;
|
|
40
|
+
audit(): Promise<unknown>;
|
|
41
|
+
manifest(): Promise<unknown>;
|
|
42
|
+
context(opts: {
|
|
43
|
+
symbol?: string;
|
|
44
|
+
file?: string;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
markdown: string;
|
|
47
|
+
}>;
|
|
48
|
+
prospect(top?: number, prefix?: string): Promise<unknown>;
|
|
36
49
|
}
|
package/dist/client.js
CHANGED
|
@@ -66,5 +66,37 @@ class XRayClient {
|
|
|
66
66
|
async symbolsIn(file) {
|
|
67
67
|
return this.request("/symbols-in", { file });
|
|
68
68
|
}
|
|
69
|
+
async callers(symbol, limit = 20) {
|
|
70
|
+
return this.request("/callers", { symbol, limit: String(limit) });
|
|
71
|
+
}
|
|
72
|
+
async callees(symbol, limit = 20) {
|
|
73
|
+
return this.request("/callees", { symbol, limit: String(limit) });
|
|
74
|
+
}
|
|
75
|
+
async transitiveImpact(symbol, depth = 2) {
|
|
76
|
+
return this.request("/transitive-impact", { symbol, depth: String(depth) });
|
|
77
|
+
}
|
|
78
|
+
async planChange(symbol, depth = 2) {
|
|
79
|
+
return this.request("/plan-change", { symbol, depth: String(depth) });
|
|
80
|
+
}
|
|
81
|
+
async audit() {
|
|
82
|
+
return this.request("/audit");
|
|
83
|
+
}
|
|
84
|
+
async manifest() {
|
|
85
|
+
return this.request("/manifest");
|
|
86
|
+
}
|
|
87
|
+
async context(opts) {
|
|
88
|
+
const params = {};
|
|
89
|
+
if (opts.symbol)
|
|
90
|
+
params.symbol = opts.symbol;
|
|
91
|
+
if (opts.file)
|
|
92
|
+
params.file = opts.file;
|
|
93
|
+
return this.request("/context", params);
|
|
94
|
+
}
|
|
95
|
+
async prospect(top = 20, prefix) {
|
|
96
|
+
const params = { top: String(top) };
|
|
97
|
+
if (prefix)
|
|
98
|
+
params.prefix = prefix;
|
|
99
|
+
return this.request("/prospect", params);
|
|
100
|
+
}
|
|
69
101
|
}
|
|
70
102
|
exports.XRayClient = XRayClient;
|