ctxo-mcp 0.6.2 → 0.6.4

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.
@@ -517,10 +517,14 @@ var init_go_adapter = __esm({
517
517
  }
518
518
  });
519
519
 
520
+ // src/cli/cli-router.ts
521
+ import { readFileSync as readFileSync9, existsSync as existsSync15 } from "fs";
522
+ import { join as join19 } from "path";
523
+
520
524
  // src/cli/index-command.ts
521
525
  import { execFileSync as execFileSync2 } from "child_process";
522
526
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, statSync, readdirSync as readdirSync2 } from "fs";
523
- import { join as join5, relative, extname as extname4 } from "path";
527
+ import { join as join5, relative as relative2, extname as extname4 } from "path";
524
528
 
525
529
  // src/core/staleness/content-hasher.ts
526
530
  import { createHash } from "crypto";
@@ -1205,6 +1209,9 @@ var SchemaManager = class {
1205
1209
  }
1206
1210
  };
1207
1211
 
1212
+ // src/adapters/language/roslyn/roslyn-adapter.ts
1213
+ import { relative, dirname as dirname4, resolve as resolve2 } from "path";
1214
+
1208
1215
  // src/adapters/language/roslyn/solution-discovery.ts
1209
1216
  import { execFileSync } from "child_process";
1210
1217
  import { existsSync as existsSync3, readdirSync } from "fs";
@@ -1294,7 +1301,7 @@ function findCtxoRoslynProject() {
1294
1301
  import { spawn } from "child_process";
1295
1302
  var log2 = createLogger("ctxo:roslyn");
1296
1303
  async function runBatchIndex(projectDir, solutionPath, timeoutMs = 12e4) {
1297
- return new Promise((resolve2) => {
1304
+ return new Promise((resolve3) => {
1298
1305
  const proc = spawn("dotnet", ["run", "--project", projectDir, "--", solutionPath], {
1299
1306
  stdio: ["ignore", "pipe", "pipe"],
1300
1307
  timeout: timeoutMs
@@ -1340,14 +1347,14 @@ async function runBatchIndex(projectDir, solutionPath, timeoutMs = 12e4) {
1340
1347
  proc.on("close", (code) => {
1341
1348
  if (code !== 0) {
1342
1349
  log2.error(`ctxo-roslyn exited with code ${code}: ${stderr.trim()}`);
1343
- resolve2({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
1350
+ resolve3({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
1344
1351
  return;
1345
1352
  }
1346
- resolve2({ files, projectGraph, totalFiles, elapsed });
1353
+ resolve3({ files, projectGraph, totalFiles, elapsed });
1347
1354
  });
1348
1355
  proc.on("error", (err) => {
1349
1356
  log2.error(`ctxo-roslyn spawn error: ${err.message}`);
1350
- resolve2({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
1357
+ resolve3({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
1351
1358
  });
1352
1359
  });
1353
1360
  }
@@ -1365,7 +1372,7 @@ var RoslynKeepAlive = class {
1365
1372
  this.timeoutMs = timeoutMs;
1366
1373
  }
1367
1374
  async start() {
1368
- return new Promise((resolve2) => {
1375
+ return new Promise((resolve3) => {
1369
1376
  this.proc = spawn("dotnet", ["run", "--project", this.projectDir, "--", this.solutionPath, "--keep-alive"], {
1370
1377
  stdio: ["pipe", "pipe", "pipe"]
1371
1378
  });
@@ -1380,7 +1387,7 @@ var RoslynKeepAlive = class {
1380
1387
  const obj = JSON.parse(line);
1381
1388
  if (obj.type === "ready") {
1382
1389
  log2.info(`Roslyn keep-alive ready: ${obj.projectCount} projects, ${obj.fileCount} files`);
1383
- resolve2(true);
1390
+ resolve3(true);
1384
1391
  } else if (obj.type === "file") {
1385
1392
  const result = obj;
1386
1393
  const callback = this.pending.get(result.file);
@@ -1407,7 +1414,7 @@ var RoslynKeepAlive = class {
1407
1414
  });
1408
1415
  this.proc.on("error", (err) => {
1409
1416
  log2.error(`Roslyn keep-alive error: ${err.message}`);
1410
- resolve2(false);
1417
+ resolve3(false);
1411
1418
  });
1412
1419
  this.resetInactivityTimer();
1413
1420
  });
@@ -1417,14 +1424,14 @@ var RoslynKeepAlive = class {
1417
1424
  return null;
1418
1425
  }
1419
1426
  this.resetInactivityTimer();
1420
- return new Promise((resolve2) => {
1421
- this.pending.set(relativePath, resolve2);
1427
+ return new Promise((resolve3) => {
1428
+ this.pending.set(relativePath, resolve3);
1422
1429
  this.proc.stdin.write(JSON.stringify({ file: relativePath }) + "\n");
1423
1430
  setTimeout(() => {
1424
1431
  if (this.pending.has(relativePath)) {
1425
1432
  this.pending.delete(relativePath);
1426
1433
  log2.error(`Roslyn keep-alive timeout for ${relativePath}`);
1427
- resolve2(null);
1434
+ resolve3(null);
1428
1435
  }
1429
1436
  }, 3e4);
1430
1437
  });
@@ -1456,6 +1463,7 @@ var RoslynAdapter = class {
1456
1463
  tier = "full";
1457
1464
  roslynProjectDir = null;
1458
1465
  solutionPath = null;
1466
+ rootDir = null;
1459
1467
  cache = /* @__PURE__ */ new Map();
1460
1468
  keepAlive = null;
1461
1469
  initialized = false;
@@ -1481,6 +1489,7 @@ var RoslynAdapter = class {
1481
1489
  log3.info("Roslyn adapter unavailable: no .sln or .csproj found");
1482
1490
  return;
1483
1491
  }
1492
+ this.rootDir = rootDir;
1484
1493
  log3.info(`Roslyn adapter ready: SDK ${sdk.version}, solution ${this.solutionPath}`);
1485
1494
  this.initialized = true;
1486
1495
  }
@@ -1491,9 +1500,18 @@ var RoslynAdapter = class {
1491
1500
  async batchIndex() {
1492
1501
  if (!this.isReady()) return null;
1493
1502
  const result = await runBatchIndex(this.roslynProjectDir, this.solutionPath);
1503
+ const solutionDir = dirname4(this.solutionPath);
1504
+ const pathMap = /* @__PURE__ */ new Map();
1505
+ for (const file of result.files) {
1506
+ const absolutePath = resolve2(solutionDir, file.file);
1507
+ const projectRelative = relative(this.rootDir, absolutePath).replace(/\\/g, "/");
1508
+ pathMap.set(file.file, projectRelative);
1509
+ }
1494
1510
  this.cache.clear();
1495
1511
  for (const file of result.files) {
1496
- this.cache.set(file.file, file);
1512
+ const projectRelative = pathMap.get(file.file);
1513
+ const rewritten = rewriteAllPaths(file, projectRelative, pathMap);
1514
+ this.cache.set(projectRelative, rewritten);
1497
1515
  }
1498
1516
  log3.info(`Roslyn batch index: ${result.totalFiles} files in ${result.elapsed}`);
1499
1517
  return result;
@@ -1557,6 +1575,33 @@ var RoslynAdapter = class {
1557
1575
  this.initialized = false;
1558
1576
  }
1559
1577
  };
1578
+ function rewriteAllPaths(file, newFilePath, pathMap) {
1579
+ const rewrite = (s) => {
1580
+ for (const [oldPath, newPath] of pathMap) {
1581
+ if (s.includes(oldPath)) {
1582
+ return s.replaceAll(oldPath, newPath);
1583
+ }
1584
+ }
1585
+ return s;
1586
+ };
1587
+ return {
1588
+ ...file,
1589
+ file: newFilePath,
1590
+ symbols: file.symbols.map((s) => ({
1591
+ ...s,
1592
+ symbolId: rewrite(s.symbolId)
1593
+ })),
1594
+ edges: file.edges.map((e) => ({
1595
+ ...e,
1596
+ from: rewrite(e.from),
1597
+ to: rewrite(e.to)
1598
+ })),
1599
+ complexity: file.complexity.map((c) => ({
1600
+ ...c,
1601
+ symbolId: rewrite(c.symbolId)
1602
+ }))
1603
+ };
1604
+ }
1560
1605
 
1561
1606
  // src/cli/index-command.ts
1562
1607
  var IndexCommand = class {
@@ -1609,7 +1654,7 @@ var IndexCommand = class {
1609
1654
  for (const filePath of files) {
1610
1655
  const adapter = registry.getAdapter(filePath);
1611
1656
  if (!adapter) continue;
1612
- const relativePath = relative(this.projectRoot, filePath).replace(/\\/g, "/");
1657
+ const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
1613
1658
  try {
1614
1659
  const source = readFileSync2(filePath, "utf-8");
1615
1660
  const lastModified = Math.floor(Date.now() / 1e3);
@@ -1793,7 +1838,7 @@ var IndexCommand = class {
1793
1838
  let staleCount = 0;
1794
1839
  for (const filePath of files) {
1795
1840
  if (!this.isSupportedExtension(filePath)) continue;
1796
- const relativePath = relative(this.projectRoot, filePath).replace(/\\/g, "/");
1841
+ const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
1797
1842
  const indexed = indexedMap.get(relativePath);
1798
1843
  if (!indexed) {
1799
1844
  console.error(`[ctxo] NOT INDEXED: ${relativePath}`);
@@ -1963,7 +2008,7 @@ import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync
1963
2008
 
1964
2009
  // src/cli/ai-rules.ts
1965
2010
  import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
1966
- import { join as join9, dirname as dirname4 } from "path";
2011
+ import { join as join9, dirname as dirname5 } from "path";
1967
2012
  var PLATFORMS = [
1968
2013
  { id: "claude-code", name: "Claude Code", file: "CLAUDE.md", mode: "append", detectPaths: ["CLAUDE.md", ".claude"], starred: true },
1969
2014
  { id: "cursor", name: "Cursor", file: ".cursor/rules/ctxo-mcp.mdc", mode: "create", detectPaths: [".cursor", ".cursorrules"], starred: false },
@@ -2040,7 +2085,7 @@ function installRules(projectRoot, platformId) {
2040
2085
  if (!platform) throw new Error(`Unknown platform: ${platformId}`);
2041
2086
  const filePath = join9(projectRoot, platform.file);
2042
2087
  const content = generateRules(platformId);
2043
- const dir = dirname4(filePath);
2088
+ const dir = dirname5(filePath);
2044
2089
  if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2045
2090
  if (platform.mode === "create") {
2046
2091
  writeFileSync4(filePath, content, "utf-8");
@@ -2094,7 +2139,7 @@ version: "1.0"
2094
2139
  `;
2095
2140
  function ensureConfig(projectRoot) {
2096
2141
  const filePath = join9(projectRoot, ".ctxo", "config.yaml");
2097
- const dir = dirname4(filePath);
2142
+ const dir = dirname5(filePath);
2098
2143
  if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2099
2144
  if (existsSync6(filePath)) {
2100
2145
  return { file: ".ctxo/config.yaml", action: "skipped" };
@@ -2126,7 +2171,7 @@ function getMcpConfigTargets(selectedPlatformIds) {
2126
2171
  }
2127
2172
  function ensureMcpConfig(projectRoot, target) {
2128
2173
  const filePath = join9(projectRoot, target.file);
2129
- const dir = dirname4(filePath);
2174
+ const dir = dirname5(filePath);
2130
2175
  if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2131
2176
  const entry = target.extraFields ? { ...target.extraFields, ...CTXO_MCP_ENTRY } : { ...CTXO_MCP_ENTRY };
2132
2177
  if (!existsSync6(filePath)) {
@@ -2462,7 +2507,7 @@ var InitCommand = class {
2462
2507
  };
2463
2508
 
2464
2509
  // src/cli/watch-command.ts
2465
- import { join as join11, extname as extname5, relative as relative2 } from "path";
2510
+ import { join as join11, extname as extname5, relative as relative3 } from "path";
2466
2511
  import { readFileSync as readFileSync5 } from "fs";
2467
2512
 
2468
2513
  // src/adapters/watcher/chokidar-watcher-adapter.ts
@@ -2549,7 +2594,7 @@ var WatchCommand = class {
2549
2594
  const reindexFile = async (filePath) => {
2550
2595
  const adapter = registry.getAdapter(filePath);
2551
2596
  if (!adapter) return;
2552
- const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
2597
+ const relativePath = relative3(this.projectRoot, filePath).replace(/\\/g, "/");
2553
2598
  try {
2554
2599
  if (roslynAdapter?.isReady() && filePath.endsWith(".cs")) {
2555
2600
  const result = await roslynAdapter.reindexFile(relativePath);
@@ -2637,7 +2682,7 @@ var WatchCommand = class {
2637
2682
  if (event === "unlink") {
2638
2683
  const ext = extname5(filePath).toLowerCase();
2639
2684
  if (!supportedExtensions.has(ext)) return;
2640
- const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
2685
+ const relativePath = relative3(this.projectRoot, filePath).replace(/\\/g, "/");
2641
2686
  writer.delete(relativePath);
2642
2687
  storage.deleteSymbolFile(relativePath);
2643
2688
  console.error(`[ctxo] Removed from index: ${relativePath}`);
@@ -3251,6 +3296,9 @@ var DoctorCommand = class {
3251
3296
  this.ctxoRoot = join18(projectRoot, ".ctxo");
3252
3297
  }
3253
3298
  async run(options = {}) {
3299
+ const { getVersion: getVersion2 } = await import("./cli-router-JX7ZFVAW.js");
3300
+ console.error(`ctxo v${getVersion2()} \u2014 health check
3301
+ `);
3254
3302
  const checks = [
3255
3303
  new NodeVersionCheck(),
3256
3304
  new GitBinaryCheck(),
@@ -3295,6 +3343,24 @@ var DoctorCommand = class {
3295
3343
  };
3296
3344
 
3297
3345
  // src/cli/cli-router.ts
3346
+ function getVersion() {
3347
+ let dir = import.meta.dirname;
3348
+ for (let i = 0; i < 10; i++) {
3349
+ const pkg = join19(dir, "package.json");
3350
+ if (existsSync15(pkg)) {
3351
+ try {
3352
+ const json = JSON.parse(readFileSync9(pkg, "utf-8"));
3353
+ return json.version ?? "unknown";
3354
+ } catch {
3355
+ break;
3356
+ }
3357
+ }
3358
+ const parent = join19(dir, "..");
3359
+ if (parent === dir) break;
3360
+ dir = parent;
3361
+ }
3362
+ return "unknown";
3363
+ }
3298
3364
  var CliRouter = class {
3299
3365
  projectRoot;
3300
3366
  constructor(projectRoot) {
@@ -3302,6 +3368,10 @@ var CliRouter = class {
3302
3368
  }
3303
3369
  async route(args) {
3304
3370
  const command = args[0];
3371
+ if (command === "--version" || command === "-v" || command === "-V") {
3372
+ console.error(`ctxo ${getVersion()}`);
3373
+ return;
3374
+ }
3305
3375
  if (!command || command === "--help" || command === "-h") {
3306
3376
  this.printHelp();
3307
3377
  return;
@@ -3370,8 +3440,9 @@ var CliRouter = class {
3370
3440
  }
3371
3441
  }
3372
3442
  printHelp() {
3443
+ const v = getVersion();
3373
3444
  console.error(`
3374
- ctxo \u2014 MCP server for dependency-aware codebase context
3445
+ ctxo v${v} \u2014 MCP server for dependency-aware codebase context
3375
3446
 
3376
3447
  Usage:
3377
3448
  ctxo Start MCP server (stdio transport)
@@ -3391,6 +3462,7 @@ Usage:
3391
3462
  }
3392
3463
  };
3393
3464
  export {
3394
- CliRouter
3465
+ CliRouter,
3466
+ getVersion
3395
3467
  };
3396
- //# sourceMappingURL=cli-router-LW4YTNHW.js.map
3468
+ //# sourceMappingURL=cli-router-JX7ZFVAW.js.map