@syke1/mcp-server 1.8.0 → 1.8.2

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/index.js CHANGED
@@ -76,7 +76,7 @@ function resolveFilePath(fileArg, projectRoot, sourceDir) {
76
76
  // License state — set at startup
77
77
  let licenseStatus = { plan: "free", source: "default" };
78
78
  // Free tier limits
79
- const FREE_MAX_FILES = 50;
79
+ const FREE_MAX_FILES = 200;
80
80
  function isPro() {
81
81
  if (!(0, validator_1._verifyStatus)(licenseStatus)) {
82
82
  licenseStatus = { plan: "free", source: "default" };
@@ -103,7 +103,7 @@ function getMaxFiles() {
103
103
  }
104
104
  /**
105
105
  * Check if a resolved file path is within the free tier limit.
106
- * Free set = first 50 files sorted alphabetically by relative path.
106
+ * Free set = first 200 files sorted alphabetically by relative path.
107
107
  */
108
108
  function isFileInFreeSet(resolvedPath, graph) {
109
109
  if (isPro())
@@ -112,7 +112,7 @@ function isFileInFreeSet(resolvedPath, graph) {
112
112
  const idx = allFiles.indexOf(resolvedPath);
113
113
  return idx >= 0 && idx < FREE_MAX_FILES;
114
114
  }
115
- const PRO_UPGRADE_MSG = "This file exceeds the Free tier limit (50 files). Upgrade to Pro for unlimited analysis: https://syke.cloud/dashboard/";
115
+ const PRO_UPGRADE_MSG = "This file exceeds the Free tier limit (200 files). Upgrade to Pro for unlimited analysis: https://syke.cloud/dashboard/";
116
116
  function getProToolError(toolName) {
117
117
  if (licenseStatus.error) {
118
118
  return `${toolName}: ${licenseStatus.error}`;
@@ -147,7 +147,7 @@ async function main() {
147
147
  };
148
148
  process.on("SIGINT", shutdown);
149
149
  process.on("SIGTERM", shutdown);
150
- const server = new index_js_1.Server({ name: "syke", version: "1.8.0" }, { capabilities: { tools: {} } });
150
+ const server = new index_js_1.Server({ name: "syke", version: "1.8.2" }, { capabilities: { tools: {} } });
151
151
  // List tools
152
152
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
153
153
  tools: [
@@ -628,7 +628,7 @@ async function main() {
628
628
  }
629
629
  });
630
630
  // Pre-warm the graph (skip if no project root — e.g. Smithery scan)
631
- console.error(`[syke] Starting SYKE MCP Server v1.8.0`);
631
+ console.error(`[syke] Starting SYKE MCP Server v1.8.2`);
632
632
  console.error(`[syke] License: ${licenseStatus.plan.toUpperCase()} (${licenseStatus.source})`);
633
633
  if (licenseStatus.expiresAt) {
634
634
  console.error(`[syke] Expires: ${licenseStatus.expiresAt}`);
@@ -648,6 +648,24 @@ async function main() {
648
648
  console.error(`[syke] Free tier: ${FREE_MAX_FILES} file limit, 3 tools (gate_build, check_safe, get_dependencies)`);
649
649
  console.error(`[syke] Upgrade at https://syke.cloud/dashboard/`);
650
650
  }
651
+ // Non-blocking update check
652
+ (async () => {
653
+ try {
654
+ const res = await fetch("https://registry.npmjs.org/@syke1/mcp-server", {
655
+ signal: AbortSignal.timeout(5000),
656
+ });
657
+ const data = await res.json();
658
+ const latest = data["dist-tags"]?.latest;
659
+ if (latest && latest !== "1.8.2") {
660
+ const [lM, lm, lp] = latest.split(".").map(Number);
661
+ const [cM, cm, cp] = [1, 8, 1];
662
+ if (lM > cM || (lM === cM && lm > cm) || (lM === cM && lm === cm && lp > cp)) {
663
+ console.error(`[syke] Update available: v${latest}. Run: npx @syke1/mcp-server@latest`);
664
+ }
665
+ }
666
+ }
667
+ catch { }
668
+ })();
651
669
  let fileCache = null;
652
670
  if (currentProjectRoot) {
653
671
  const detectedLangs = (0, plugin_1.detectLanguages)(currentProjectRoot).map(p => p.name).join(", ") || "none";
@@ -798,7 +816,7 @@ main().catch((err) => {
798
816
  * See: https://smithery.ai/docs/deploy#sandbox-server
799
817
  */
800
818
  function createSandboxServer() {
801
- const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.8.0" }, { capabilities: { tools: {} } });
819
+ const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.8.2" }, { capabilities: { tools: {} } });
802
820
  sandboxServer.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
803
821
  tools: [
804
822
  {
@@ -469,6 +469,7 @@ document.addEventListener("DOMContentLoaded", async () => {
469
469
  setupFileTree();
470
470
  initSSE();
471
471
  startHealthCheck();
472
+ checkForUpdates();
472
473
  });
473
474
 
474
475
  // Periodic server health check (catches server down even without SSE)
@@ -3704,6 +3705,34 @@ async function updateBottomBar() {
3704
3705
  }
3705
3706
  }
3706
3707
 
3708
+ // ═══════════════════════════════════════════
3709
+ // UPDATE BANNER
3710
+ // ═══════════════════════════════════════════
3711
+ async function checkForUpdates() {
3712
+ try {
3713
+ const res = await fetch("/api/version", { signal: AbortSignal.timeout(8000) });
3714
+ const data = await res.json();
3715
+ if (!data.isOutdated) return;
3716
+
3717
+ // Remove existing banner if any
3718
+ const existing = document.getElementById("syke-update-banner");
3719
+ if (existing) existing.remove();
3720
+
3721
+ const banner = document.createElement("div");
3722
+ banner.id = "syke-update-banner";
3723
+ banner.style.cssText =
3724
+ "position:fixed;top:0;left:0;right:0;z-index:10000;padding:8px 16px;" +
3725
+ "background:linear-gradient(90deg,#0d1b2a,#1b2d45);border-bottom:1px solid rgba(0,212,255,0.3);" +
3726
+ "display:flex;align-items:center;justify-content:center;gap:12px;font-size:13px;color:#c8d6e5;font-family:Inter,sans-serif;";
3727
+ banner.innerHTML =
3728
+ '<span style="color:#00d4ff;font-weight:600;">Update available: v' + data.latestVersion + '</span>' +
3729
+ '<span style="color:#8899aa;">(current: v' + data.version + ')</span>' +
3730
+ '<code style="background:rgba(0,212,255,0.1);padding:2px 8px;border-radius:4px;font-size:12px;color:#00d4ff;">npx @syke1/mcp-server@latest</code>' +
3731
+ '<button onclick="this.parentElement.remove()" style="background:none;border:none;color:#556677;cursor:pointer;font-size:16px;margin-left:8px;" title="Dismiss">&times;</button>';
3732
+ document.body.prepend(banner);
3733
+ } catch {}
3734
+ }
3735
+
3707
3736
  let browsePath = null; // current path in folder browser
3708
3737
 
3709
3738
  async function browseDir(dirPath) {
@@ -49,6 +49,17 @@ const analyze_impact_1 = require("../tools/analyze-impact");
49
49
  const analyzer_1 = require("../ai/analyzer");
50
50
  const realtime_analyzer_1 = require("../ai/realtime-analyzer");
51
51
  const config_1 = require("../config");
52
+ // ── Semver comparison (returns >0 if a > b) ──
53
+ function compareVersions(a, b) {
54
+ const pa = a.split(".").map(Number);
55
+ const pb = b.split(".").map(Number);
56
+ for (let i = 0; i < 3; i++) {
57
+ const diff = (pa[i] || 0) - (pb[i] || 0);
58
+ if (diff !== 0)
59
+ return diff;
60
+ }
61
+ return 0;
62
+ }
52
63
  // ── Real-time AI analysis toggle ──
53
64
  let realtimeAIEnabled = true;
54
65
  function resolveFilePath(fileArg, projectRoot, sourceDir) {
@@ -380,6 +391,40 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
380
391
  wireFileCacheEvents(cache);
381
392
  console.error(`[syke:sse] FileCache re-wired (${cache.size} files)`);
382
393
  }
394
+ // ── npm version cache (24h TTL) ──
395
+ let npmVersionCache = null;
396
+ const NPM_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
397
+ async function fetchLatestNpmVersion() {
398
+ if (npmVersionCache && Date.now() - npmVersionCache.fetchedAt < NPM_CACHE_TTL) {
399
+ return npmVersionCache.version;
400
+ }
401
+ try {
402
+ const res = await fetch("https://registry.npmjs.org/@syke1/mcp-server", {
403
+ signal: AbortSignal.timeout(5000),
404
+ });
405
+ const data = await res.json();
406
+ const latest = data["dist-tags"]?.latest;
407
+ if (latest) {
408
+ npmVersionCache = { version: latest, fetchedAt: Date.now() };
409
+ return latest;
410
+ }
411
+ }
412
+ catch { }
413
+ return npmVersionCache?.version ?? null;
414
+ }
415
+ // GET /api/version — Lightweight version check endpoint
416
+ app.get("/api/version", async (_req, res) => {
417
+ let currentVersion = "unknown";
418
+ try {
419
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"));
420
+ currentVersion = pkg.version || "unknown";
421
+ }
422
+ catch { }
423
+ const latestVersion = await fetchLatestNpmVersion();
424
+ const isOutdated = !!(latestVersion && currentVersion !== "unknown" && latestVersion !== currentVersion
425
+ && compareVersions(latestVersion, currentVersion) > 0);
426
+ res.json({ version: currentVersion, latestVersion: latestVersion || currentVersion, isOutdated });
427
+ });
383
428
  // GET /api/cache-status — Memory cache stats
384
429
  app.get("/api/cache-status", (_req, res) => {
385
430
  if (!currentFileCache) {
@@ -408,7 +453,7 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
408
453
  app.get("/api/graph", (_req, res) => {
409
454
  const graph = getGraphFn();
410
455
  const isPro = isProPlan();
411
- const FREE_GRAPH_LIMIT = 50;
456
+ const FREE_GRAPH_LIMIT = 200;
412
457
  const nodes = [];
413
458
  const edges = [];
414
459
  // ── Compute depth for each file (BFS from roots) ──
@@ -429,7 +474,7 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
429
474
  queue.push([dep, d + 1]);
430
475
  }
431
476
  }
432
- // Free tier: limit to first 50 files sorted alphabetically
477
+ // Free tier: limit to first 200 files sorted alphabetically
433
478
  const allFiles = [...graph.files].sort();
434
479
  const visibleFiles = isPro ? allFiles : allFiles.slice(0, FREE_GRAPH_LIMIT);
435
480
  const visibleSet = new Set(visibleFiles);
@@ -521,14 +566,14 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
521
566
  if (!isPro) {
522
567
  const allFiles = [...graph.files].sort();
523
568
  const idx = allFiles.indexOf(resolved);
524
- if (idx < 0 || idx >= 50) {
525
- return res.status(403).json({ error: "This file exceeds the Free tier limit (50 files). Upgrade to Pro for unlimited analysis.", upgrade: "https://syke.cloud/dashboard/" });
569
+ if (idx < 0 || idx >= 200) {
570
+ return res.status(403).json({ error: "This file exceeds the Free tier limit (200 files). Upgrade to Pro for unlimited analysis.", upgrade: "https://syke.cloud/dashboard/" });
526
571
  }
527
572
  }
528
573
  const impactResult = await (0, analyze_impact_1.analyzeImpact)(resolved, graph);
529
574
  try {
530
575
  const aiResult = await (0, analyzer_1.analyzeWithAI)(resolved, impactResult, graph);
531
- const partial = !isPro && graph.files.size > 50;
576
+ const partial = !isPro && graph.files.size > 200;
532
577
  res.json({ file: impactResult.relativePath, analysis: aiResult, partial });
533
578
  }
534
579
  catch (err) {
@@ -792,7 +837,7 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
792
837
  planSource: license?.source || "default",
793
838
  expiresAt: license?.expiresAt || null,
794
839
  licenseKey: maskedKey,
795
- freeFileLimit: 50,
840
+ freeFileLimit: 200,
796
841
  sykeVersion,
797
842
  aiProvider: aiInfo.activeProvider,
798
843
  aiKeys: aiInfo.configured,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syke1/mcp-server",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
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",