ctxo-mcp 0.6.1 → 0.6.3
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,12 +1209,25 @@ 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";
|
|
1211
1218
|
import { join as join4 } from "path";
|
|
1212
1219
|
var log = createLogger("ctxo:roslyn");
|
|
1213
1220
|
var IGNORE_DIRS = /* @__PURE__ */ new Set(["bin", "obj", "node_modules", ".git", ".ctxo", "packages"]);
|
|
1221
|
+
function findPackageRoot(startDir) {
|
|
1222
|
+
let dir = startDir;
|
|
1223
|
+
for (let i = 0; i < 10; i++) {
|
|
1224
|
+
if (existsSync3(join4(dir, "package.json"))) return dir;
|
|
1225
|
+
const parent = join4(dir, "..");
|
|
1226
|
+
if (parent === dir) break;
|
|
1227
|
+
dir = parent;
|
|
1228
|
+
}
|
|
1229
|
+
return null;
|
|
1230
|
+
}
|
|
1214
1231
|
function detectDotnetSdk() {
|
|
1215
1232
|
try {
|
|
1216
1233
|
const version = execFileSync("dotnet", ["--version"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
@@ -1260,15 +1277,16 @@ function findFiles(dir, ext, maxDepth, currentDepth = 0) {
|
|
|
1260
1277
|
return results;
|
|
1261
1278
|
}
|
|
1262
1279
|
function findCtxoRoslynProject() {
|
|
1280
|
+
const pkgRoot = findPackageRoot(import.meta.dirname);
|
|
1263
1281
|
const candidates = [
|
|
1282
|
+
...pkgRoot ? [join4(pkgRoot, "tools/ctxo-roslyn")] : [],
|
|
1283
|
+
// package root (npx, global, local dev)
|
|
1284
|
+
join4(import.meta.dirname, "../tools/ctxo-roslyn"),
|
|
1285
|
+
// dist/ -> tools/ (tsup bundled)
|
|
1264
1286
|
join4(import.meta.dirname, "../../../../tools/ctxo-roslyn"),
|
|
1265
|
-
//
|
|
1266
|
-
join4(import.meta.dirname, "../../../tools/ctxo-roslyn"),
|
|
1267
|
-
// built dist (dist/adapters/language/roslyn -> tools/)
|
|
1287
|
+
// src/adapters/language/roslyn -> tools/ (dev unbundled)
|
|
1268
1288
|
join4(process.cwd(), "node_modules/ctxo-mcp/tools/ctxo-roslyn"),
|
|
1269
1289
|
// npm install in project
|
|
1270
|
-
join4(process.cwd(), "node_modules/ctxo/tools/ctxo-roslyn"),
|
|
1271
|
-
// alt package name
|
|
1272
1290
|
join4(process.cwd(), "tools/ctxo-roslyn")
|
|
1273
1291
|
// local repo root
|
|
1274
1292
|
];
|
|
@@ -1283,7 +1301,7 @@ function findCtxoRoslynProject() {
|
|
|
1283
1301
|
import { spawn } from "child_process";
|
|
1284
1302
|
var log2 = createLogger("ctxo:roslyn");
|
|
1285
1303
|
async function runBatchIndex(projectDir, solutionPath, timeoutMs = 12e4) {
|
|
1286
|
-
return new Promise((
|
|
1304
|
+
return new Promise((resolve3) => {
|
|
1287
1305
|
const proc = spawn("dotnet", ["run", "--project", projectDir, "--", solutionPath], {
|
|
1288
1306
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1289
1307
|
timeout: timeoutMs
|
|
@@ -1329,14 +1347,14 @@ async function runBatchIndex(projectDir, solutionPath, timeoutMs = 12e4) {
|
|
|
1329
1347
|
proc.on("close", (code) => {
|
|
1330
1348
|
if (code !== 0) {
|
|
1331
1349
|
log2.error(`ctxo-roslyn exited with code ${code}: ${stderr.trim()}`);
|
|
1332
|
-
|
|
1350
|
+
resolve3({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
|
|
1333
1351
|
return;
|
|
1334
1352
|
}
|
|
1335
|
-
|
|
1353
|
+
resolve3({ files, projectGraph, totalFiles, elapsed });
|
|
1336
1354
|
});
|
|
1337
1355
|
proc.on("error", (err) => {
|
|
1338
1356
|
log2.error(`ctxo-roslyn spawn error: ${err.message}`);
|
|
1339
|
-
|
|
1357
|
+
resolve3({ files: [], projectGraph: null, totalFiles: 0, elapsed: "" });
|
|
1340
1358
|
});
|
|
1341
1359
|
});
|
|
1342
1360
|
}
|
|
@@ -1354,7 +1372,7 @@ var RoslynKeepAlive = class {
|
|
|
1354
1372
|
this.timeoutMs = timeoutMs;
|
|
1355
1373
|
}
|
|
1356
1374
|
async start() {
|
|
1357
|
-
return new Promise((
|
|
1375
|
+
return new Promise((resolve3) => {
|
|
1358
1376
|
this.proc = spawn("dotnet", ["run", "--project", this.projectDir, "--", this.solutionPath, "--keep-alive"], {
|
|
1359
1377
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1360
1378
|
});
|
|
@@ -1369,7 +1387,7 @@ var RoslynKeepAlive = class {
|
|
|
1369
1387
|
const obj = JSON.parse(line);
|
|
1370
1388
|
if (obj.type === "ready") {
|
|
1371
1389
|
log2.info(`Roslyn keep-alive ready: ${obj.projectCount} projects, ${obj.fileCount} files`);
|
|
1372
|
-
|
|
1390
|
+
resolve3(true);
|
|
1373
1391
|
} else if (obj.type === "file") {
|
|
1374
1392
|
const result = obj;
|
|
1375
1393
|
const callback = this.pending.get(result.file);
|
|
@@ -1396,7 +1414,7 @@ var RoslynKeepAlive = class {
|
|
|
1396
1414
|
});
|
|
1397
1415
|
this.proc.on("error", (err) => {
|
|
1398
1416
|
log2.error(`Roslyn keep-alive error: ${err.message}`);
|
|
1399
|
-
|
|
1417
|
+
resolve3(false);
|
|
1400
1418
|
});
|
|
1401
1419
|
this.resetInactivityTimer();
|
|
1402
1420
|
});
|
|
@@ -1406,14 +1424,14 @@ var RoslynKeepAlive = class {
|
|
|
1406
1424
|
return null;
|
|
1407
1425
|
}
|
|
1408
1426
|
this.resetInactivityTimer();
|
|
1409
|
-
return new Promise((
|
|
1410
|
-
this.pending.set(relativePath,
|
|
1427
|
+
return new Promise((resolve3) => {
|
|
1428
|
+
this.pending.set(relativePath, resolve3);
|
|
1411
1429
|
this.proc.stdin.write(JSON.stringify({ file: relativePath }) + "\n");
|
|
1412
1430
|
setTimeout(() => {
|
|
1413
1431
|
if (this.pending.has(relativePath)) {
|
|
1414
1432
|
this.pending.delete(relativePath);
|
|
1415
1433
|
log2.error(`Roslyn keep-alive timeout for ${relativePath}`);
|
|
1416
|
-
|
|
1434
|
+
resolve3(null);
|
|
1417
1435
|
}
|
|
1418
1436
|
}, 3e4);
|
|
1419
1437
|
});
|
|
@@ -1445,6 +1463,7 @@ var RoslynAdapter = class {
|
|
|
1445
1463
|
tier = "full";
|
|
1446
1464
|
roslynProjectDir = null;
|
|
1447
1465
|
solutionPath = null;
|
|
1466
|
+
rootDir = null;
|
|
1448
1467
|
cache = /* @__PURE__ */ new Map();
|
|
1449
1468
|
keepAlive = null;
|
|
1450
1469
|
initialized = false;
|
|
@@ -1470,6 +1489,7 @@ var RoslynAdapter = class {
|
|
|
1470
1489
|
log3.info("Roslyn adapter unavailable: no .sln or .csproj found");
|
|
1471
1490
|
return;
|
|
1472
1491
|
}
|
|
1492
|
+
this.rootDir = rootDir;
|
|
1473
1493
|
log3.info(`Roslyn adapter ready: SDK ${sdk.version}, solution ${this.solutionPath}`);
|
|
1474
1494
|
this.initialized = true;
|
|
1475
1495
|
}
|
|
@@ -1480,9 +1500,13 @@ var RoslynAdapter = class {
|
|
|
1480
1500
|
async batchIndex() {
|
|
1481
1501
|
if (!this.isReady()) return null;
|
|
1482
1502
|
const result = await runBatchIndex(this.roslynProjectDir, this.solutionPath);
|
|
1503
|
+
const solutionDir = dirname4(this.solutionPath);
|
|
1483
1504
|
this.cache.clear();
|
|
1484
1505
|
for (const file of result.files) {
|
|
1485
|
-
|
|
1506
|
+
const absolutePath = resolve2(solutionDir, file.file);
|
|
1507
|
+
const projectRelative = relative(this.rootDir, absolutePath).replace(/\\/g, "/");
|
|
1508
|
+
const rewritten = rewritePaths(file, file.file, projectRelative);
|
|
1509
|
+
this.cache.set(projectRelative, rewritten);
|
|
1486
1510
|
}
|
|
1487
1511
|
log3.info(`Roslyn batch index: ${result.totalFiles} files in ${result.elapsed}`);
|
|
1488
1512
|
return result;
|
|
@@ -1546,6 +1570,26 @@ var RoslynAdapter = class {
|
|
|
1546
1570
|
this.initialized = false;
|
|
1547
1571
|
}
|
|
1548
1572
|
};
|
|
1573
|
+
function rewritePaths(file, oldPrefix, newPrefix) {
|
|
1574
|
+
const rewrite = (s) => s.replace(oldPrefix, newPrefix);
|
|
1575
|
+
return {
|
|
1576
|
+
...file,
|
|
1577
|
+
file: newPrefix,
|
|
1578
|
+
symbols: file.symbols.map((s) => ({
|
|
1579
|
+
...s,
|
|
1580
|
+
symbolId: rewrite(s.symbolId)
|
|
1581
|
+
})),
|
|
1582
|
+
edges: file.edges.map((e) => ({
|
|
1583
|
+
...e,
|
|
1584
|
+
from: rewrite(e.from),
|
|
1585
|
+
to: rewrite(e.to)
|
|
1586
|
+
})),
|
|
1587
|
+
complexity: file.complexity.map((c) => ({
|
|
1588
|
+
...c,
|
|
1589
|
+
symbolId: rewrite(c.symbolId)
|
|
1590
|
+
}))
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1549
1593
|
|
|
1550
1594
|
// src/cli/index-command.ts
|
|
1551
1595
|
var IndexCommand = class {
|
|
@@ -1598,7 +1642,7 @@ var IndexCommand = class {
|
|
|
1598
1642
|
for (const filePath of files) {
|
|
1599
1643
|
const adapter = registry.getAdapter(filePath);
|
|
1600
1644
|
if (!adapter) continue;
|
|
1601
|
-
const relativePath =
|
|
1645
|
+
const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
1602
1646
|
try {
|
|
1603
1647
|
const source = readFileSync2(filePath, "utf-8");
|
|
1604
1648
|
const lastModified = Math.floor(Date.now() / 1e3);
|
|
@@ -1782,7 +1826,7 @@ var IndexCommand = class {
|
|
|
1782
1826
|
let staleCount = 0;
|
|
1783
1827
|
for (const filePath of files) {
|
|
1784
1828
|
if (!this.isSupportedExtension(filePath)) continue;
|
|
1785
|
-
const relativePath =
|
|
1829
|
+
const relativePath = relative2(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
1786
1830
|
const indexed = indexedMap.get(relativePath);
|
|
1787
1831
|
if (!indexed) {
|
|
1788
1832
|
console.error(`[ctxo] NOT INDEXED: ${relativePath}`);
|
|
@@ -1952,7 +1996,7 @@ import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync
|
|
|
1952
1996
|
|
|
1953
1997
|
// src/cli/ai-rules.ts
|
|
1954
1998
|
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
1955
|
-
import { join as join9, dirname as
|
|
1999
|
+
import { join as join9, dirname as dirname5 } from "path";
|
|
1956
2000
|
var PLATFORMS = [
|
|
1957
2001
|
{ id: "claude-code", name: "Claude Code", file: "CLAUDE.md", mode: "append", detectPaths: ["CLAUDE.md", ".claude"], starred: true },
|
|
1958
2002
|
{ id: "cursor", name: "Cursor", file: ".cursor/rules/ctxo-mcp.mdc", mode: "create", detectPaths: [".cursor", ".cursorrules"], starred: false },
|
|
@@ -2029,7 +2073,7 @@ function installRules(projectRoot, platformId) {
|
|
|
2029
2073
|
if (!platform) throw new Error(`Unknown platform: ${platformId}`);
|
|
2030
2074
|
const filePath = join9(projectRoot, platform.file);
|
|
2031
2075
|
const content = generateRules(platformId);
|
|
2032
|
-
const dir =
|
|
2076
|
+
const dir = dirname5(filePath);
|
|
2033
2077
|
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
2034
2078
|
if (platform.mode === "create") {
|
|
2035
2079
|
writeFileSync4(filePath, content, "utf-8");
|
|
@@ -2083,7 +2127,7 @@ version: "1.0"
|
|
|
2083
2127
|
`;
|
|
2084
2128
|
function ensureConfig(projectRoot) {
|
|
2085
2129
|
const filePath = join9(projectRoot, ".ctxo", "config.yaml");
|
|
2086
|
-
const dir =
|
|
2130
|
+
const dir = dirname5(filePath);
|
|
2087
2131
|
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
2088
2132
|
if (existsSync6(filePath)) {
|
|
2089
2133
|
return { file: ".ctxo/config.yaml", action: "skipped" };
|
|
@@ -2115,7 +2159,7 @@ function getMcpConfigTargets(selectedPlatformIds) {
|
|
|
2115
2159
|
}
|
|
2116
2160
|
function ensureMcpConfig(projectRoot, target) {
|
|
2117
2161
|
const filePath = join9(projectRoot, target.file);
|
|
2118
|
-
const dir =
|
|
2162
|
+
const dir = dirname5(filePath);
|
|
2119
2163
|
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
2120
2164
|
const entry = target.extraFields ? { ...target.extraFields, ...CTXO_MCP_ENTRY } : { ...CTXO_MCP_ENTRY };
|
|
2121
2165
|
if (!existsSync6(filePath)) {
|
|
@@ -2451,7 +2495,7 @@ var InitCommand = class {
|
|
|
2451
2495
|
};
|
|
2452
2496
|
|
|
2453
2497
|
// src/cli/watch-command.ts
|
|
2454
|
-
import { join as join11, extname as extname5, relative as
|
|
2498
|
+
import { join as join11, extname as extname5, relative as relative3 } from "path";
|
|
2455
2499
|
import { readFileSync as readFileSync5 } from "fs";
|
|
2456
2500
|
|
|
2457
2501
|
// src/adapters/watcher/chokidar-watcher-adapter.ts
|
|
@@ -2538,7 +2582,7 @@ var WatchCommand = class {
|
|
|
2538
2582
|
const reindexFile = async (filePath) => {
|
|
2539
2583
|
const adapter = registry.getAdapter(filePath);
|
|
2540
2584
|
if (!adapter) return;
|
|
2541
|
-
const relativePath =
|
|
2585
|
+
const relativePath = relative3(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
2542
2586
|
try {
|
|
2543
2587
|
if (roslynAdapter?.isReady() && filePath.endsWith(".cs")) {
|
|
2544
2588
|
const result = await roslynAdapter.reindexFile(relativePath);
|
|
@@ -2626,7 +2670,7 @@ var WatchCommand = class {
|
|
|
2626
2670
|
if (event === "unlink") {
|
|
2627
2671
|
const ext = extname5(filePath).toLowerCase();
|
|
2628
2672
|
if (!supportedExtensions.has(ext)) return;
|
|
2629
|
-
const relativePath =
|
|
2673
|
+
const relativePath = relative3(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
2630
2674
|
writer.delete(relativePath);
|
|
2631
2675
|
storage.deleteSymbolFile(relativePath);
|
|
2632
2676
|
console.error(`[ctxo] Removed from index: ${relativePath}`);
|
|
@@ -3240,6 +3284,9 @@ var DoctorCommand = class {
|
|
|
3240
3284
|
this.ctxoRoot = join18(projectRoot, ".ctxo");
|
|
3241
3285
|
}
|
|
3242
3286
|
async run(options = {}) {
|
|
3287
|
+
const { getVersion: getVersion2 } = await import("./cli-router-ZG3ELO34.js");
|
|
3288
|
+
console.error(`ctxo v${getVersion2()} \u2014 health check
|
|
3289
|
+
`);
|
|
3243
3290
|
const checks = [
|
|
3244
3291
|
new NodeVersionCheck(),
|
|
3245
3292
|
new GitBinaryCheck(),
|
|
@@ -3284,6 +3331,24 @@ var DoctorCommand = class {
|
|
|
3284
3331
|
};
|
|
3285
3332
|
|
|
3286
3333
|
// src/cli/cli-router.ts
|
|
3334
|
+
function getVersion() {
|
|
3335
|
+
let dir = import.meta.dirname;
|
|
3336
|
+
for (let i = 0; i < 10; i++) {
|
|
3337
|
+
const pkg = join19(dir, "package.json");
|
|
3338
|
+
if (existsSync15(pkg)) {
|
|
3339
|
+
try {
|
|
3340
|
+
const json = JSON.parse(readFileSync9(pkg, "utf-8"));
|
|
3341
|
+
return json.version ?? "unknown";
|
|
3342
|
+
} catch {
|
|
3343
|
+
break;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
const parent = join19(dir, "..");
|
|
3347
|
+
if (parent === dir) break;
|
|
3348
|
+
dir = parent;
|
|
3349
|
+
}
|
|
3350
|
+
return "unknown";
|
|
3351
|
+
}
|
|
3287
3352
|
var CliRouter = class {
|
|
3288
3353
|
projectRoot;
|
|
3289
3354
|
constructor(projectRoot) {
|
|
@@ -3291,6 +3356,10 @@ var CliRouter = class {
|
|
|
3291
3356
|
}
|
|
3292
3357
|
async route(args) {
|
|
3293
3358
|
const command = args[0];
|
|
3359
|
+
if (command === "--version" || command === "-v" || command === "-V") {
|
|
3360
|
+
console.error(`ctxo ${getVersion()}`);
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3294
3363
|
if (!command || command === "--help" || command === "-h") {
|
|
3295
3364
|
this.printHelp();
|
|
3296
3365
|
return;
|
|
@@ -3359,8 +3428,9 @@ var CliRouter = class {
|
|
|
3359
3428
|
}
|
|
3360
3429
|
}
|
|
3361
3430
|
printHelp() {
|
|
3431
|
+
const v = getVersion();
|
|
3362
3432
|
console.error(`
|
|
3363
|
-
ctxo \u2014 MCP server for dependency-aware codebase context
|
|
3433
|
+
ctxo v${v} \u2014 MCP server for dependency-aware codebase context
|
|
3364
3434
|
|
|
3365
3435
|
Usage:
|
|
3366
3436
|
ctxo Start MCP server (stdio transport)
|
|
@@ -3380,6 +3450,7 @@ Usage:
|
|
|
3380
3450
|
}
|
|
3381
3451
|
};
|
|
3382
3452
|
export {
|
|
3383
|
-
CliRouter
|
|
3453
|
+
CliRouter,
|
|
3454
|
+
getVersion
|
|
3384
3455
|
};
|
|
3385
|
-
//# sourceMappingURL=cli-router-
|
|
3456
|
+
//# sourceMappingURL=cli-router-ZG3ELO34.js.map
|