@syke1/mcp-server 1.3.7 → 1.3.9

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/graph.js CHANGED
@@ -38,6 +38,7 @@ exports.getGraph = getGraph;
38
38
  exports.refreshGraph = refreshGraph;
39
39
  const path = __importStar(require("path"));
40
40
  const plugin_1 = require("./languages/plugin");
41
+ const typescript_1 = require("./languages/typescript");
41
42
  let cachedGraph = null;
42
43
  function buildGraph(projectRoot, packageName) {
43
44
  const detectedPlugins = (0, plugin_1.detectLanguages)(projectRoot);
@@ -103,5 +104,6 @@ function getGraph(projectRoot, packageName) {
103
104
  }
104
105
  function refreshGraph(projectRoot, packageName) {
105
106
  cachedGraph = null;
107
+ (0, typescript_1.clearAliasCache)();
106
108
  return buildGraph(projectRoot, packageName);
107
109
  }
package/dist/index.js CHANGED
@@ -522,6 +522,8 @@ async function main() {
522
522
  fileCache.initialize();
523
523
  fileCache.startWatching();
524
524
  }
525
+ // Web server handle (set after server starts)
526
+ let webServerHandle = null;
525
527
  // Switch project callback — reinitializes graph + file cache
526
528
  function switchProject(newRoot) {
527
529
  currentProjectRoot = newRoot;
@@ -533,6 +535,10 @@ async function main() {
533
535
  fileCache = new file_cache_1.FileCache(newRoot);
534
536
  fileCache.initialize();
535
537
  fileCache.startWatching();
538
+ // Re-wire SSE events to the new FileCache
539
+ if (webServerHandle) {
540
+ webServerHandle.setFileCache(fileCache);
541
+ }
536
542
  // Rebuild graph
537
543
  const graph = (0, graph_1.refreshGraph)(newRoot, currentPackageName);
538
544
  console.error(`[syke] Switched to project: ${newRoot}`);
@@ -552,7 +558,8 @@ async function main() {
552
558
  }
553
559
  // Start Express web server with file cache for SSE (only if project detected)
554
560
  if (currentProjectRoot) {
555
- const webApp = (0, server_1.createWebServer)(() => (0, graph_1.getGraph)(currentProjectRoot, currentPackageName), fileCache, switchProject, () => currentProjectRoot, () => currentPackageName, () => licenseStatus, () => !!(0, provider_1.getAIProvider)());
561
+ const { app: webApp, setFileCache: setWebFileCache } = (0, server_1.createWebServer)(() => (0, graph_1.getGraph)(currentProjectRoot, currentPackageName), fileCache, switchProject, () => currentProjectRoot, () => currentPackageName, () => licenseStatus, () => !!(0, provider_1.getAIProvider)());
562
+ webServerHandle = { setFileCache: setWebFileCache };
556
563
  webApp.listen(WEB_PORT, () => {
557
564
  const dashUrl = `http://localhost:${WEB_PORT}`;
558
565
  console.error(`[syke] Web dashboard: ${dashUrl}`);
@@ -1,2 +1,4 @@
1
1
  import { LanguagePlugin } from "./plugin";
2
+ /** Clear cached aliases (call on project switch / graph refresh) */
3
+ export declare function clearAliasCache(): void;
2
4
  export declare const typescriptPlugin: LanguagePlugin;
@@ -34,12 +34,53 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.typescriptPlugin = void 0;
37
+ exports.clearAliasCache = clearAliasCache;
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
39
40
  const plugin_1 = require("./plugin");
40
41
  const TS_IMPORT_RE = /(?:import|export)\s+.*?from\s+['"](.+?)['"]/;
41
42
  const TS_SIDE_EFFECT_RE = /^import\s+['"](.+?)['"]/;
42
43
  const JS_REQUIRE_RE = /(?:const|let|var)\s+\w+\s*=\s*require\s*\(\s*['"](.+?)['"]\s*\)/;
44
+ const aliasCache = new Map(); // projectRoot → aliases
45
+ function loadPathAliases(projectRoot) {
46
+ if (aliasCache.has(projectRoot))
47
+ return aliasCache.get(projectRoot);
48
+ const aliases = [];
49
+ try {
50
+ const tsconfigPath = path.join(projectRoot, "tsconfig.json");
51
+ if (!fs.existsSync(tsconfigPath)) {
52
+ aliasCache.set(projectRoot, aliases);
53
+ return aliases;
54
+ }
55
+ // Strip single-line comments (// ...) and trailing commas for lenient parsing
56
+ const raw = fs.readFileSync(tsconfigPath, "utf-8")
57
+ .replace(/\/\/.*$/gm, "")
58
+ .replace(/,\s*([\]}])/g, "$1");
59
+ const tsconfig = JSON.parse(raw);
60
+ const paths = tsconfig?.compilerOptions?.paths;
61
+ const baseUrl = tsconfig?.compilerOptions?.baseUrl || ".";
62
+ if (!paths) {
63
+ aliasCache.set(projectRoot, aliases);
64
+ return aliases;
65
+ }
66
+ const baseDir = path.resolve(projectRoot, baseUrl);
67
+ for (const [pattern, targets] of Object.entries(paths)) {
68
+ // Pattern like "@/*" → prefix "@/", target "./src/*" → baseDir + "src"
69
+ const prefix = pattern.endsWith("/*") ? pattern.slice(0, -1) : pattern;
70
+ const resolvedTargets = [];
71
+ for (const target of targets) {
72
+ const stripped = target.endsWith("/*") ? target.slice(0, -1) : target;
73
+ resolvedTargets.push(path.resolve(baseDir, stripped));
74
+ }
75
+ aliases.push({ prefix, targets: resolvedTargets });
76
+ }
77
+ }
78
+ catch (_) {
79
+ // Ignore parse errors
80
+ }
81
+ aliasCache.set(projectRoot, aliases);
82
+ return aliases;
83
+ }
43
84
  function resolveTsImport(fromDir, importPath) {
44
85
  const base = path.resolve(fromDir, importPath);
45
86
  const candidates = [
@@ -49,6 +90,7 @@ function resolveTsImport(fromDir, importPath) {
49
90
  base + ".js",
50
91
  base + ".jsx",
51
92
  path.join(base, "index.ts"),
93
+ path.join(base, "index.tsx"),
52
94
  path.join(base, "index.js"),
53
95
  ];
54
96
  for (const candidate of candidates) {
@@ -58,6 +100,24 @@ function resolveTsImport(fromDir, importPath) {
58
100
  }
59
101
  return null;
60
102
  }
103
+ /** Clear cached aliases (call on project switch / graph refresh) */
104
+ function clearAliasCache() {
105
+ aliasCache.clear();
106
+ }
107
+ function resolveAliasImport(importPath, projectRoot) {
108
+ const aliases = loadPathAliases(projectRoot);
109
+ for (const alias of aliases) {
110
+ if (importPath.startsWith(alias.prefix)) {
111
+ const rest = importPath.slice(alias.prefix.length);
112
+ for (const targetDir of alias.targets) {
113
+ const resolved = resolveTsImport(targetDir, "./" + rest);
114
+ if (resolved)
115
+ return resolved;
116
+ }
117
+ }
118
+ }
119
+ return null;
120
+ }
61
121
  exports.typescriptPlugin = {
62
122
  id: "typescript",
63
123
  name: "TypeScript",
@@ -82,7 +142,7 @@ exports.typescriptPlugin = {
82
142
  discoverFiles(dir) {
83
143
  return (0, plugin_1.discoverAllFiles)(dir, [".ts", ".tsx", ".js", ".jsx"]).filter(f => !f.endsWith(".d.ts"));
84
144
  },
85
- parseImports(filePath, _projectRoot, _sourceDir) {
145
+ parseImports(filePath, projectRoot, _sourceDir) {
86
146
  let content;
87
147
  try {
88
148
  content = fs.readFileSync(filePath, "utf-8");
@@ -100,11 +160,19 @@ exports.typescriptPlugin = {
100
160
  importPath = match[1];
101
161
  if (!importPath)
102
162
  continue;
103
- if (!importPath.startsWith("."))
104
- continue;
105
- const resolved = resolveTsImport(fileDir, importPath);
106
- if (resolved)
107
- imports.push(resolved);
163
+ // Skip bare package imports (e.g. "react", "next/link", "firebase/app")
164
+ if (importPath.startsWith(".")) {
165
+ // Relative import
166
+ const resolved = resolveTsImport(fileDir, importPath);
167
+ if (resolved)
168
+ imports.push(resolved);
169
+ }
170
+ else {
171
+ // Try path alias resolution (e.g. @/components/Sidebar → projectRoot/components/Sidebar)
172
+ const resolved = resolveAliasImport(importPath, projectRoot);
173
+ if (resolved)
174
+ imports.push(resolved);
175
+ }
108
176
  }
109
177
  return imports;
110
178
  },
@@ -1,3 +1,4 @@
1
+ import express from "express";
1
2
  import { DependencyGraph } from "../graph";
2
3
  import { FileCache } from "../watcher/file-cache";
3
4
  import { RealtimeAnalysis } from "../ai/realtime-analyzer";
@@ -24,9 +25,14 @@ export interface SwitchProjectResult {
24
25
  fileCount: number;
25
26
  edgeCount: number;
26
27
  }
27
- export declare function createWebServer(getGraphFn: () => DependencyGraph, fileCache?: FileCache, switchProjectFn?: (newRoot: string) => SwitchProjectResult, getProjectRoot?: () => string, getPackageName?: () => string, getLicenseStatus?: () => {
28
+ export interface WebServerHandle {
29
+ app: ReturnType<typeof express>;
30
+ /** Re-wire SSE events when FileCache is replaced (e.g. after switchProject) */
31
+ setFileCache(cache: FileCache): void;
32
+ }
33
+ export declare function createWebServer(getGraphFn: () => DependencyGraph, initialFileCache?: FileCache, switchProjectFn?: (newRoot: string) => SwitchProjectResult, getProjectRoot?: () => string, getPackageName?: () => string, getLicenseStatus?: () => {
28
34
  plan: string;
29
35
  expiresAt?: string;
30
36
  error?: string;
31
37
  source?: string;
32
- }, hasAIKeyFn?: () => boolean): import("express-serve-static-core").Express;
38
+ }, hasAIKeyFn?: () => boolean): WebServerHandle;
@@ -226,7 +226,7 @@ function acknowledgeWarnings() {
226
226
  function getAllWarnings() {
227
227
  return [...warningStore];
228
228
  }
229
- function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot, getPackageName, getLicenseStatus, hasAIKeyFn) {
229
+ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProjectRoot, getPackageName, getLicenseStatus, hasAIKeyFn) {
230
230
  const app = (0, express_1.default)();
231
231
  app.use(express_1.default.json());
232
232
  // Serve static files from public/
@@ -234,6 +234,7 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
234
234
  app.use(express_1.default.static(publicDir));
235
235
  // ── SSE: Server-Sent Events for real-time updates ──
236
236
  const sseClients = new Set();
237
+ let currentFileCache = initialFileCache || null;
237
238
  function broadcastSSE(event, data) {
238
239
  const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
239
240
  for (const client of sseClients) {
@@ -274,7 +275,7 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
274
275
  "Access-Control-Allow-Origin": "*",
275
276
  });
276
277
  // Send initial connection event
277
- res.write(`event: connected\ndata: ${JSON.stringify({ clients: sseClients.size + 1, cacheSize: fileCache?.size || 0 })}\n\n`);
278
+ res.write(`event: connected\ndata: ${JSON.stringify({ clients: sseClients.size + 1, cacheSize: currentFileCache?.size || 0 })}\n\n`);
278
279
  sseClients.add(res);
279
280
  console.error(`[syke:sse] Client connected (${sseClients.size} total)`);
280
281
  _req.on("close", () => {
@@ -283,8 +284,8 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
283
284
  });
284
285
  });
285
286
  // Wire FileCache change events → SSE broadcast + AI analysis
286
- if (fileCache) {
287
- fileCache.on("change", async (change) => {
287
+ function wireFileCacheEvents(cache) {
288
+ cache.on("change", async (change) => {
288
289
  const graph = getGraphFn();
289
290
  const absPath = path.normalize(path.join(graph.sourceDir, change.relativePath));
290
291
  // Compute affected nodes for visual pulse
@@ -317,7 +318,7 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
317
318
  if (license && license.plan === "pro") {
318
319
  broadcastSSE("analysis-start", { file: change.relativePath });
319
320
  try {
320
- const analysis = await (0, realtime_analyzer_1.analyzeChangeRealtime)(change, graph, (relPath) => fileCache.getFileByRelPath(relPath));
321
+ const analysis = await (0, realtime_analyzer_1.analyzeChangeRealtime)(change, graph, (relPath) => currentFileCache?.getFileByRelPath(relPath) ?? null);
321
322
  broadcastSSE("analysis-result", analysis);
322
323
  // Store warnings for MCP check_warnings tool
323
324
  addWarning(analysis);
@@ -341,15 +342,23 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
341
342
  }
342
343
  });
343
344
  }
345
+ if (currentFileCache)
346
+ wireFileCacheEvents(currentFileCache);
347
+ /** Replace the FileCache (called after switchProject) */
348
+ function setFileCache(cache) {
349
+ currentFileCache = cache;
350
+ wireFileCacheEvents(cache);
351
+ console.error(`[syke:sse] FileCache re-wired (${cache.size} files)`);
352
+ }
344
353
  // GET /api/cache-status — Memory cache stats
345
354
  app.get("/api/cache-status", (_req, res) => {
346
- if (!fileCache) {
355
+ if (!currentFileCache) {
347
356
  return res.json({ enabled: false });
348
357
  }
349
358
  res.json({
350
359
  enabled: true,
351
- fileCount: fileCache.size,
352
- totalLines: fileCache.totalLines,
360
+ fileCount: currentFileCache.size,
361
+ totalLines: currentFileCache.totalLines,
353
362
  sseClients: sseClients.size,
354
363
  });
355
364
  });
@@ -804,5 +813,5 @@ function createWebServer(getGraphFn, fileCache, switchProjectFn, getProjectRoot,
804
813
  res.status(500).json({ error: err.message || "Failed to switch project" });
805
814
  }
806
815
  });
807
- return app;
816
+ return { app, setFileCache };
808
817
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syke1/mcp-server",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "mcpName": "io.github.khalomsky/syke",
5
5
  "description": "AI code impact analysis MCP server — dependency graphs, cascade detection, and a mandatory build gate for AI coding agents",
6
6
  "main": "dist/index.js",