@kridaydave/code-mapper 1.0.0 → 1.0.1
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/CHANGELOG.md +31 -0
- package/README.md +1 -0
- package/bin/code-mapper.mjs +86 -0
- package/dist/graph/GraphAnalyzer.js +32 -65
- package/dist/graph/GraphAnalyzer.js.map +1 -1
- package/dist/graph/GraphBuilder.js +18 -45
- package/dist/graph/GraphBuilder.js.map +1 -1
- package/dist/index.js +100 -23
- package/dist/index.js.map +1 -1
- package/dist/mcp/cache.js +8 -17
- package/dist/mcp/cache.js.map +1 -1
- package/dist/mcp/resources.js +5 -1
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.js +190 -35
- package/dist/mcp/tools.js.map +1 -1
- package/dist/parser/ComplexityAnalyzer.js +19 -2
- package/dist/parser/ComplexityAnalyzer.js.map +1 -1
- package/dist/parser/FileAnalyzer.js +8 -30
- package/dist/parser/FileAnalyzer.js.map +1 -1
- package/dist/parser/ProjectParser.js +8 -5
- package/dist/parser/ProjectParser.js.map +1 -1
- package/dist/parser/ProjectParser.test.js +1 -17
- package/dist/parser/ProjectParser.test.js.map +1 -1
- package/dist/tui/index.js +239 -0
- package/dist/tui/index.js.map +1 -0
- package/package.json +82 -35
- package/AGENTS.md +0 -174
- package/docs/PHASE2_PLAN.md +0 -435
- package/fixtures/test-project/calculator.ts +0 -28
- package/fixtures/test-project/index.ts +0 -2
- package/fixtures/test-project/math.ts +0 -11
- package/src/graph/Graph.test.ts +0 -222
- package/src/graph/GraphAnalyzer.ts +0 -502
- package/src/graph/GraphBuilder.ts +0 -258
- package/src/graph/types.ts +0 -42
- package/src/index.ts +0 -38
- package/src/mcp/cache.ts +0 -89
- package/src/mcp/resources.ts +0 -137
- package/src/mcp/tools.test.ts +0 -104
- package/src/mcp/tools.ts +0 -529
- package/src/parser/ComplexityAnalyzer.ts +0 -275
- package/src/parser/FileAnalyzer.ts +0 -215
- package/src/parser/ProjectParser.test.ts +0 -96
- package/src/parser/ProjectParser.ts +0 -172
- package/src/parser/types.ts +0 -77
- package/src/types/graphology-pagerank.d.ts +0 -20
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -15
package/dist/index.js
CHANGED
|
@@ -1,29 +1,106 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
const MIN_NODE_VERSION = 18;
|
|
5
|
+
const nodeVersion = process.versions.node;
|
|
6
|
+
const major = parseInt(nodeVersion.split(".")[0], 10);
|
|
7
|
+
const RED = "\x1b[31m";
|
|
8
|
+
const GREEN = "\x1b[32m";
|
|
9
|
+
const YELLOW = "\x1b[33m";
|
|
10
|
+
const CYAN = "\x1b[36m";
|
|
11
|
+
const BOLD = "\x1b[1m";
|
|
12
|
+
function log(...args) { console.error(...args); }
|
|
13
|
+
function err(...args) { console.error(...args); }
|
|
14
|
+
if (major < MIN_NODE_VERSION) {
|
|
15
|
+
err(`${RED}${BOLD}Node ${nodeVersion} not supported. Need 18+.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const pkgRoot = process.cwd();
|
|
19
|
+
const distIndex = path.join(pkgRoot, "dist", "index.js");
|
|
20
|
+
if (!fs.existsSync(distIndex)) {
|
|
21
|
+
log(`${YELLOW}Building...`);
|
|
22
|
+
const srcIndex = path.join(pkgRoot, "src", "index.ts");
|
|
23
|
+
if (!fs.existsSync(srcIndex)) {
|
|
24
|
+
err(`${RED}Source not found. Reinstall.`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (fs.existsSync(path.join(pkgRoot, "node_modules", "typescript"))) {
|
|
28
|
+
try {
|
|
29
|
+
const { execSync } = await import("child_process");
|
|
30
|
+
execSync("npm run build", { cwd: pkgRoot, stdio: "inherit" });
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
34
|
+
err(`${RED}Build failed: ${msg}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
err(`${RED}Install incomplete. Reinstall.`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const nodeModules = path.join(pkgRoot, "node_modules");
|
|
44
|
+
const missing = ["@modelcontextprotocol/sdk", "chalk", "zod"].filter(d => !fs.existsSync(path.join(nodeModules, d)));
|
|
45
|
+
if (missing.length) {
|
|
46
|
+
err(`${RED}Missing: ${missing.join(", ")}`);
|
|
47
|
+
err(`Run: npm install`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const args = process.argv.slice(2);
|
|
51
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
52
|
+
log(`
|
|
53
|
+
code-mapper v1.0.1
|
|
54
|
+
|
|
55
|
+
npx code-mapper - start server
|
|
56
|
+
npx code-mapper --setup - setup wizard
|
|
57
|
+
`);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
61
|
+
log("1.0.1");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
if (args.includes("--setup") || args.includes("-s")) {
|
|
65
|
+
const tuiDist = path.join(pkgRoot, "dist", "tui", "index.js");
|
|
66
|
+
const tuiSrc = path.join(pkgRoot, "src", "tui", "index.ts");
|
|
67
|
+
if (fs.existsSync(tuiDist)) {
|
|
68
|
+
const { startSetupWizard } = await import("./tui/index.js");
|
|
69
|
+
await startSetupWizard();
|
|
70
|
+
}
|
|
71
|
+
else if (fs.existsSync(tuiSrc)) {
|
|
72
|
+
log(`${YELLOW}Building...`);
|
|
73
|
+
const { execSync } = await import("child_process");
|
|
74
|
+
execSync("npm run build", { cwd: pkgRoot, stdio: "inherit" });
|
|
75
|
+
const { startSetupWizard } = await import("./tui/index.js");
|
|
76
|
+
await startSetupWizard();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
log(`${YELLOW}Setup not available.`);
|
|
80
|
+
}
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
85
|
+
const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
|
|
86
|
+
const { registerTools } = await import("./mcp/tools.js");
|
|
87
|
+
const { registerResources } = await import("./mcp/resources.js");
|
|
88
|
+
const server = new McpServer({ name: "code-mapper", version: "1.0.1" }, { instructions: "CodeMapper analyzes TypeScript/JavaScript codebases. Start with scan_codebase." });
|
|
18
89
|
registerTools(server);
|
|
19
90
|
registerResources(server);
|
|
20
|
-
// Connect via stdio
|
|
21
91
|
const transport = new StdioServerTransport();
|
|
22
|
-
|
|
23
|
-
|
|
92
|
+
transport.onerror = (e) => log(`Error: ${e.message}`);
|
|
93
|
+
transport.onclose = () => { log("Closed"); process.exit(0); };
|
|
94
|
+
try {
|
|
95
|
+
await server.connect(transport);
|
|
96
|
+
log("CodeMapper running on stdio");
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
100
|
+
err(`${RED}Failed: ${msg}`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
process.on("SIGINT", () => { log("Shutting down..."); process.exit(0); });
|
|
104
|
+
process.on("SIGTERM", () => { log("Shutting down..."); process.exit(0); });
|
|
24
105
|
}
|
|
25
|
-
main().catch((error) => {
|
|
26
|
-
console.error("Fatal error:", error);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
});
|
|
29
106
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEtD,MAAM,GAAG,GAAG,UAAU,CAAC;AACvB,MAAM,KAAK,GAAG,UAAU,CAAC;AACzB,MAAM,MAAM,GAAG,UAAU,CAAC;AAC1B,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,MAAM,IAAI,GAAG,SAAS,CAAC;AAEvB,SAAS,GAAG,CAAC,GAAG,IAAe,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5D,SAAS,GAAG,CAAC,GAAG,IAAe,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5D,IAAI,KAAK,GAAG,gBAAgB,EAAE,CAAC;IAC7B,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,QAAQ,WAAW,2BAA2B,CAAC,CAAC;IACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAEzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;IAC9B,GAAG,CAAC,GAAG,MAAM,aAAa,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,GAAG,GAAG,8BAA8B,CAAC,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YACnD,QAAQ,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,GAAG,CAAC,GAAG,GAAG,iBAAiB,GAAG,EAAE,CAAC,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,GAAG,gCAAgC,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AACvD,MAAM,OAAO,GAAG,CAAC,2BAA2B,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,CAClE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAC/C,CAAC;AAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,GAAG,GAAG,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACnD,GAAG,CAAC;;;;;CAKL,CAAC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACtD,GAAG,CAAC,OAAO,CAAC,CAAC;IACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAE5D,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;SAAM,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,GAAG,MAAM,aAAa,CAAC,CAAC;QAC5B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QACnD,QAAQ,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9D,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,MAAM,sBAAsB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;KAAM,CAAC;IACN,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAC;IAC3F,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,yCAAyC,CAAC,CAAC;IAC9E,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzD,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,EACzC,EAAE,YAAY,EAAE,gFAAgF,EAAE,CACnG,CAAC;IAEF,aAAa,CAAC,MAAM,CAAC,CAAC;IACtB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAE1B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,SAAS,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,SAAS,CAAC,OAAO,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,GAAG,CAAC,6BAA6B,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,GAAG,CAAC,GAAG,GAAG,WAAW,GAAG,EAAE,CAAC,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC"}
|
package/dist/mcp/cache.js
CHANGED
|
@@ -15,17 +15,8 @@ export function normalizeCacheKey(directory) {
|
|
|
15
15
|
}
|
|
16
16
|
function evictIfNeeded() {
|
|
17
17
|
if (analyzerCache.size >= MAX_CACHE_SIZE) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
for (const [key, entry] of analyzerCache) {
|
|
21
|
-
if (entry.timestamp < oldestTime) {
|
|
22
|
-
oldestTime = entry.timestamp;
|
|
23
|
-
oldestKey = key;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (oldestKey) {
|
|
27
|
-
analyzerCache.delete(oldestKey);
|
|
28
|
-
}
|
|
18
|
+
const entries = [...analyzerCache.entries()].sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
19
|
+
analyzerCache.delete(entries[0][0]);
|
|
29
20
|
}
|
|
30
21
|
}
|
|
31
22
|
function isEntryExpired(entry) {
|
|
@@ -34,13 +25,13 @@ function isEntryExpired(entry) {
|
|
|
34
25
|
export function getAnalyzerFromCache(directory) {
|
|
35
26
|
const normalizedDir = normalizeCacheKey(directory);
|
|
36
27
|
const entry = analyzerCache.get(normalizedDir);
|
|
37
|
-
if (entry
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
if (!entry || isEntryExpired(entry)) {
|
|
29
|
+
if (entry) {
|
|
30
|
+
analyzerCache.delete(normalizedDir);
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
42
33
|
}
|
|
43
|
-
return
|
|
34
|
+
return entry.analyzer;
|
|
44
35
|
}
|
|
45
36
|
export function setAnalyzerInCache(directory, analyzer) {
|
|
46
37
|
evictIfNeeded();
|
package/dist/mcp/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/mcp/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;AAC3D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkC,CAAC;AAEnE,IAAI,qBAAqB,GAAkB,IAAI,CAAC;AAEhD,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,qBAAqB,GAAG,GAAG,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,aAAa;IACpB,IAAI,aAAa,CAAC,IAAI,IAAI,cAAc,EAAE,CAAC;QACzC,
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/mcp/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/resources.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { ResourceTemplate } from "@modelcontextprotocol/server";
|
|
1
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { analyzerCache, getLastScannedDirectory } from "./cache.js";
|
|
3
3
|
const RANK_CACHE_LIMIT = 100;
|
|
4
4
|
const rankCache = new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Registers all MCP resources with the server.
|
|
7
|
+
* Provides: codebase://summary, codebase://graph/{format}
|
|
8
|
+
*/
|
|
5
9
|
export function registerResources(server) {
|
|
6
10
|
// Resource 1: codebase://summary
|
|
7
11
|
server.registerResource("codebase-summary", "codebase://summary", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/mcp/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../../src/mcp/resources.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -2,8 +2,6 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { ProjectParser } from "../parser/ProjectParser.js";
|
|
5
|
-
import { Project } from "ts-morph";
|
|
6
|
-
import { ComplexityAnalyzer } from "../parser/ComplexityAnalyzer.js";
|
|
7
5
|
import { GraphBuilder } from "../graph/GraphBuilder.js";
|
|
8
6
|
import { GraphAnalyzer } from "../graph/GraphAnalyzer.js";
|
|
9
7
|
import { normalizeCacheKey, clearAnalyzerCache, setLastScannedDirectory, getAnalyzerFromCache, setAnalyzerInCache, getPendingAnalyzer, setPendingAnalyzer } from "./cache.js";
|
|
@@ -54,14 +52,60 @@ async function getAnalyzer(directory) {
|
|
|
54
52
|
setPendingAnalyzer(normalizedDir, analyzerPromise);
|
|
55
53
|
return analyzerPromise;
|
|
56
54
|
}
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Wraps MCP tool handlers to catch errors and return structured error responses.
|
|
57
|
+
* Ensures all tool responses have consistent format regardless of success/failure.
|
|
58
|
+
*/
|
|
59
|
+
function getErrorMessage(error) {
|
|
60
|
+
const msg = error.message;
|
|
61
|
+
if (msg.includes("does not exist")) {
|
|
62
|
+
return {
|
|
63
|
+
message: `${msg}. Please check that the path is correct and try again.`,
|
|
64
|
+
errorCode: "ERR_DIRECTORY_NOT_FOUND",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (msg.includes("Too many files")) {
|
|
68
|
+
return {
|
|
69
|
+
message: `${msg}. Try scanning a subdirectory to reduce the file count.`,
|
|
70
|
+
errorCode: "ERR_TOO_MANY_FILES",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (msg.includes("Path is not a directory")) {
|
|
74
|
+
return {
|
|
75
|
+
message: `${msg}. Please provide a directory path, not a file path.`,
|
|
76
|
+
errorCode: "ERR_NOT_A_DIRECTORY",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (msg.includes("no TypeScript files") || msg.includes("No files found") || msg.includes("no files found")) {
|
|
80
|
+
return {
|
|
81
|
+
message: `${msg}. Ensure the directory contains TypeScript files and try again.`,
|
|
82
|
+
errorCode: "ERR_NO_FILES_FOUND",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (msg.includes("Invalid regex pattern")) {
|
|
86
|
+
return {
|
|
87
|
+
message: `${msg}. Regex patterns must be valid JavaScript regular expressions.`,
|
|
88
|
+
errorCode: "ERR_INVALID_REGEX",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
message: msg,
|
|
93
|
+
errorCode: "ERR_UNKNOWN",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async function safeHandler(fn) {
|
|
97
|
+
try {
|
|
98
|
+
return await fn();
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
59
101
|
const message = error instanceof Error ? error.message : String(error);
|
|
102
|
+
const result = getErrorMessage(error instanceof Error ? error : new Error(message));
|
|
60
103
|
return {
|
|
61
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
104
|
+
content: [{ type: "text", text: `Error: ${result.message}` }],
|
|
105
|
+
structuredContent: { errorCode: result.errorCode, message: result.message },
|
|
62
106
|
isError: true,
|
|
63
107
|
};
|
|
64
|
-
}
|
|
108
|
+
}
|
|
65
109
|
}
|
|
66
110
|
export { validateDirectory, getAnalyzer, safeHandler, clearAnalyzerCache };
|
|
67
111
|
export function registerTools(server) {
|
|
@@ -70,7 +114,7 @@ export function registerTools(server) {
|
|
|
70
114
|
title: "Scan Codebase",
|
|
71
115
|
description: "Scan a directory and return a summary of all files, functions, classes, and their relationships. Use this first before any other analysis.",
|
|
72
116
|
inputSchema: z.object({
|
|
73
|
-
directory: z.string().describe("Path to the directory to scan (relative or absolute)"),
|
|
117
|
+
directory: z.string().min(1).describe("Path to the directory to scan (relative or absolute)"),
|
|
74
118
|
}),
|
|
75
119
|
outputSchema: z.object({
|
|
76
120
|
directory: z.string(),
|
|
@@ -118,11 +162,12 @@ export function registerTools(server) {
|
|
|
118
162
|
// Tool 2: find_function
|
|
119
163
|
server.registerTool("find_function", {
|
|
120
164
|
title: "Find Function or Class",
|
|
121
|
-
description: "Search for a function or class by name across the codebase. Returns location, signature, callers, and callees.",
|
|
165
|
+
description: "Search for a function or class by name across the codebase. Returns location, signature, callers, and callees. When useRegex is true, the name is treated as a case-insensitive regex pattern.",
|
|
122
166
|
inputSchema: z.object({
|
|
123
|
-
name: z.string().describe("Name of the function or class to search for
|
|
167
|
+
name: z.string().min(1).describe("Name of the function or class to search for"),
|
|
124
168
|
directory: z.string().describe("Path to the codebase directory (must be scanned first)"),
|
|
125
169
|
type: z.enum(["function", "class", "any"]).default("any").describe("Type of symbol to search for"),
|
|
170
|
+
useRegex: z.boolean().default(false).describe("Whether to treat name as a regex pattern (case-insensitive)"),
|
|
126
171
|
}),
|
|
127
172
|
outputSchema: z.object({
|
|
128
173
|
matches: z.array(z.object({
|
|
@@ -139,11 +184,11 @@ export function registerTools(server) {
|
|
|
139
184
|
callees: z.array(z.string()),
|
|
140
185
|
totalMatches: z.number(),
|
|
141
186
|
}),
|
|
142
|
-
}, async ({ name, directory, type }) => {
|
|
187
|
+
}, async ({ name, directory, type, useRegex }) => {
|
|
143
188
|
return await safeHandler(async () => {
|
|
144
189
|
const validatedDir = validateDirectory(directory);
|
|
145
190
|
const analyzer = await getAnalyzer(validatedDir);
|
|
146
|
-
const matches = analyzer.findFunction(name, type);
|
|
191
|
+
const matches = analyzer.findFunction(name, type, useRegex);
|
|
147
192
|
const callers = [];
|
|
148
193
|
const callees = [];
|
|
149
194
|
for (const match of matches) {
|
|
@@ -181,7 +226,7 @@ export function registerTools(server) {
|
|
|
181
226
|
title: "Analyze Dependencies",
|
|
182
227
|
description: "Returns the dependency graph between files. Can return the full graph or a subgraph for a specific file. Supports JSON, Mermaid, DOT, and PlantUML output formats.",
|
|
183
228
|
inputSchema: z.object({
|
|
184
|
-
directory: z.string().describe("Path to the codebase directory (must be scanned first)"),
|
|
229
|
+
directory: z.string().min(1).describe("Path to the codebase directory (must be scanned first)"),
|
|
185
230
|
targetFile: z.string().optional().describe("Optional: filter to show only nodes related to this file"),
|
|
186
231
|
format: z.enum(["json", "mermaid", "dot", "plantuml"]).default("json").describe("Output format: json for data, mermaid/dot/plantuml for visual diagram"),
|
|
187
232
|
}),
|
|
@@ -214,7 +259,10 @@ export function registerTools(server) {
|
|
|
214
259
|
let edges = analyzer.getEdges();
|
|
215
260
|
let cycles = [];
|
|
216
261
|
if (targetFile) {
|
|
217
|
-
const sanitizedTarget = targetFile
|
|
262
|
+
const sanitizedTarget = targetFile
|
|
263
|
+
.replace(/[\/\\]/g, "")
|
|
264
|
+
.replace(/\.\./g, ".")
|
|
265
|
+
.slice(0, 200);
|
|
218
266
|
const matchingNodes = nodes.filter((n) => n.filePath.includes(targetFile) || n.label.includes(targetFile));
|
|
219
267
|
const matchingIds = new Set(matchingNodes.map((n) => n.id));
|
|
220
268
|
const expandedIds = new Set(matchingIds);
|
|
@@ -262,9 +310,9 @@ export function registerTools(server) {
|
|
|
262
310
|
title: "Rank Impact",
|
|
263
311
|
description: "Ranks files by centrality to identify the most important/central files in the codebase. Use this to answer questions like 'Where should I add a new feature?' or 'Which files are most critical?'",
|
|
264
312
|
inputSchema: z.object({
|
|
265
|
-
directory: z.string().describe("Path to the codebase directory (must be scanned first)"),
|
|
313
|
+
directory: z.string().min(1).describe("Path to the codebase directory (must be scanned first)"),
|
|
266
314
|
metric: z.enum(["inDegree", "outDegree", "betweenness", "pagerank"]).default("inDegree").describe("Centrality metric: inDegree (most depended upon), outDegree (most dependencies), betweenness (most on critical paths), pagerank (most influential based on random walk)"),
|
|
267
|
-
topN: z.number().default(10).describe("Number of top results to return"),
|
|
315
|
+
topN: z.number().min(1).default(10).describe("Number of top results to return"),
|
|
268
316
|
}),
|
|
269
317
|
outputSchema: z.object({
|
|
270
318
|
metric: z.string(),
|
|
@@ -301,6 +349,7 @@ export function registerTools(server) {
|
|
|
301
349
|
ranked: top.map((r) => ({
|
|
302
350
|
relativePath: r.relativePath,
|
|
303
351
|
score: r.score,
|
|
352
|
+
metric: r.metric,
|
|
304
353
|
functionCount: r.functionCount,
|
|
305
354
|
classCount: r.classCount,
|
|
306
355
|
importCount: r.importCount,
|
|
@@ -319,9 +368,9 @@ export function registerTools(server) {
|
|
|
319
368
|
title: "Trace Call Chain",
|
|
320
369
|
description: "Traces the call chain / dependency path from one function or file to another. Shows the full path through the codebase.",
|
|
321
370
|
inputSchema: z.object({
|
|
322
|
-
from: z.string().describe("Starting function, class, or file name
|
|
323
|
-
to: z.string().describe("Target function, class, or file name
|
|
324
|
-
directory: z.string().describe("Path to the codebase directory (must be scanned first)"),
|
|
371
|
+
from: z.string().min(1).describe("Starting function, class, or file name"),
|
|
372
|
+
to: z.string().min(1).describe("Target function, class, or file name"),
|
|
373
|
+
directory: z.string().min(1).describe("Path to the codebase directory (must be scanned first)"),
|
|
325
374
|
}),
|
|
326
375
|
outputSchema: z.object({
|
|
327
376
|
found: z.boolean(),
|
|
@@ -358,9 +407,9 @@ export function registerTools(server) {
|
|
|
358
407
|
title: "Analyze Code Complexity",
|
|
359
408
|
description: "Analyze code complexity metrics for each file in the codebase. Identifies files that may need refactoring based on cyclomatic complexity, cognitive complexity, nesting depth, and size.",
|
|
360
409
|
inputSchema: z.object({
|
|
361
|
-
directory: z.string().describe("Path to the codebase directory (must be scanned first)"),
|
|
410
|
+
directory: z.string().min(1).describe("Path to the codebase directory (must be scanned first)"),
|
|
362
411
|
threshold: z.number().optional().describe("Minimum complexity score to report (0-100)"),
|
|
363
|
-
topN: z.number().default(10).describe("Number of most complex files to return"),
|
|
412
|
+
topN: z.number().min(1).default(10).describe("Number of most complex files to return"),
|
|
364
413
|
}),
|
|
365
414
|
outputSchema: z.object({
|
|
366
415
|
totalFiles: z.number(),
|
|
@@ -386,22 +435,42 @@ export function registerTools(server) {
|
|
|
386
435
|
const validatedDir = validateDirectory(directory);
|
|
387
436
|
const parser = new ProjectParser();
|
|
388
437
|
const parseResult = await parser.parse(validatedDir);
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
438
|
+
const results = parseResult.files.map((fileInfo) => {
|
|
439
|
+
const cyclomaticComplexity = calculateCyclomaticComplexityFromBodies(fileInfo.functions);
|
|
440
|
+
const cognitiveComplexity = calculateCognitiveComplexityFromBodies(fileInfo.functions);
|
|
441
|
+
const nestingDepth = calculateNestingDepthFromBodies(fileInfo.functions);
|
|
442
|
+
const linesOfCode = fileInfo.totalLines;
|
|
443
|
+
const functionCount = fileInfo.functions.length;
|
|
444
|
+
const classCount = fileInfo.classes.length;
|
|
445
|
+
const issues = identifyComplexityIssues({
|
|
446
|
+
cyclomaticComplexity,
|
|
447
|
+
cognitiveComplexity,
|
|
448
|
+
nestingDepth,
|
|
449
|
+
linesOfCode,
|
|
450
|
+
functionCount,
|
|
451
|
+
classCount,
|
|
452
|
+
});
|
|
453
|
+
const overallScore = calculateComplexityScore({
|
|
454
|
+
cyclomaticComplexity,
|
|
455
|
+
cognitiveComplexity,
|
|
456
|
+
nestingDepth,
|
|
457
|
+
linesOfCode,
|
|
458
|
+
functionCount,
|
|
459
|
+
classCount,
|
|
460
|
+
});
|
|
461
|
+
return {
|
|
462
|
+
filePath: fileInfo.filePath,
|
|
463
|
+
relativePath: fileInfo.relativePath,
|
|
464
|
+
cyclomaticComplexity,
|
|
465
|
+
cognitiveComplexity,
|
|
466
|
+
nestingDepth,
|
|
467
|
+
linesOfCode,
|
|
468
|
+
functionCount,
|
|
469
|
+
classCount,
|
|
470
|
+
overallScore,
|
|
471
|
+
issues,
|
|
472
|
+
};
|
|
396
473
|
});
|
|
397
|
-
const sourceFiles = project.getSourceFiles();
|
|
398
|
-
for (const sf of sourceFiles) {
|
|
399
|
-
project.removeSourceFile(sf);
|
|
400
|
-
}
|
|
401
|
-
const files = parseResult.files.map(f => f.filePath);
|
|
402
|
-
project.addSourceFilesAtPaths(files);
|
|
403
|
-
const complexityAnalyzer = new ComplexityAnalyzer(project, validatedDir);
|
|
404
|
-
const results = complexityAnalyzer.analyzeProject(parseResult);
|
|
405
474
|
const filteredResults = threshold
|
|
406
475
|
? results.filter(r => r.overallScore >= threshold)
|
|
407
476
|
: results;
|
|
@@ -440,5 +509,91 @@ export function registerTools(server) {
|
|
|
440
509
|
};
|
|
441
510
|
});
|
|
442
511
|
});
|
|
512
|
+
function calculateCyclomaticComplexityFromBodies(functions) {
|
|
513
|
+
let complexity = 1;
|
|
514
|
+
for (const fn of functions) {
|
|
515
|
+
const body = fn.body;
|
|
516
|
+
const keywords = ['if ', 'for ', 'while ', 'switch ', 'catch ', '&&', '||', '? '];
|
|
517
|
+
for (const kw of keywords) {
|
|
518
|
+
const count = (body.match(new RegExp(kw.replace(' ', '\\b'), 'g')) || []).length;
|
|
519
|
+
complexity += count;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return complexity;
|
|
523
|
+
}
|
|
524
|
+
function calculateCognitiveComplexityFromBodies(functions) {
|
|
525
|
+
let complexity = 0;
|
|
526
|
+
let nestingLevel = 0;
|
|
527
|
+
for (const fn of functions) {
|
|
528
|
+
const body = fn.body;
|
|
529
|
+
let i = 0;
|
|
530
|
+
while (i < body.length) {
|
|
531
|
+
const char = body[i];
|
|
532
|
+
if (char === '{') {
|
|
533
|
+
nestingLevel++;
|
|
534
|
+
}
|
|
535
|
+
else if (char === '}') {
|
|
536
|
+
nestingLevel = Math.max(0, nestingLevel - 1);
|
|
537
|
+
}
|
|
538
|
+
const remaining = body.slice(i);
|
|
539
|
+
if (remaining.startsWith('if ') || remaining.startsWith('for ') ||
|
|
540
|
+
remaining.startsWith('while ') || remaining.startsWith('switch ') ||
|
|
541
|
+
remaining.startsWith('catch ') || remaining.startsWith('try ')) {
|
|
542
|
+
complexity++;
|
|
543
|
+
complexity += nestingLevel;
|
|
544
|
+
}
|
|
545
|
+
i++;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return complexity;
|
|
549
|
+
}
|
|
550
|
+
function calculateNestingDepthFromBodies(functions) {
|
|
551
|
+
let maxNesting = 0;
|
|
552
|
+
let currentNesting = 0;
|
|
553
|
+
for (const fn of functions) {
|
|
554
|
+
for (const char of fn.body) {
|
|
555
|
+
if (char === '{') {
|
|
556
|
+
currentNesting++;
|
|
557
|
+
maxNesting = Math.max(maxNesting, currentNesting);
|
|
558
|
+
}
|
|
559
|
+
else if (char === '}') {
|
|
560
|
+
currentNesting = Math.max(0, currentNesting - 1);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return maxNesting;
|
|
565
|
+
}
|
|
566
|
+
function identifyComplexityIssues(metrics) {
|
|
567
|
+
const issues = [];
|
|
568
|
+
if (metrics.cyclomaticComplexity > 10) {
|
|
569
|
+
issues.push(`Cyclomatic complexity (${metrics.cyclomaticComplexity}) exceeds threshold of 10`);
|
|
570
|
+
}
|
|
571
|
+
if (metrics.cognitiveComplexity > 15) {
|
|
572
|
+
issues.push(`Cognitive complexity (${metrics.cognitiveComplexity}) exceeds threshold of 15`);
|
|
573
|
+
}
|
|
574
|
+
if (metrics.nestingDepth > 4) {
|
|
575
|
+
issues.push(`Nesting depth (${metrics.nestingDepth}) exceeds threshold of 4`);
|
|
576
|
+
}
|
|
577
|
+
if (metrics.linesOfCode > 500) {
|
|
578
|
+
issues.push(`Lines of code (${metrics.linesOfCode}) exceeds 500 - file is large`);
|
|
579
|
+
}
|
|
580
|
+
if (metrics.functionCount > 20) {
|
|
581
|
+
issues.push(`Function count (${metrics.functionCount}) exceeds 20 - too many functions`);
|
|
582
|
+
}
|
|
583
|
+
if (metrics.classCount > 10) {
|
|
584
|
+
issues.push(`Class count (${metrics.classCount}) exceeds 10 - too many classes`);
|
|
585
|
+
}
|
|
586
|
+
return issues;
|
|
587
|
+
}
|
|
588
|
+
function calculateComplexityScore(metrics) {
|
|
589
|
+
let score = 0;
|
|
590
|
+
score += Math.min(metrics.cyclomaticComplexity * 4, 25);
|
|
591
|
+
score += Math.min(metrics.cognitiveComplexity * 3, 25);
|
|
592
|
+
score += Math.min(metrics.nestingDepth * 5, 25);
|
|
593
|
+
score += Math.min((metrics.linesOfCode / 500) * 15, 15);
|
|
594
|
+
score += Math.min((metrics.functionCount / 20) * 5, 5);
|
|
595
|
+
score += Math.min((metrics.classCount / 10) * 5, 5);
|
|
596
|
+
return Math.min(Math.round(score), 100);
|
|
597
|
+
}
|
|
443
598
|
}
|
|
444
599
|
//# sourceMappingURL=tools.js.map
|