@syke1/mcp-server 1.0.0

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.
Files changed (41) hide show
  1. package/README.md +112 -0
  2. package/dist/ai/analyzer.d.ts +3 -0
  3. package/dist/ai/analyzer.js +120 -0
  4. package/dist/ai/realtime-analyzer.d.ts +20 -0
  5. package/dist/ai/realtime-analyzer.js +182 -0
  6. package/dist/graph.d.ts +13 -0
  7. package/dist/graph.js +105 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +518 -0
  10. package/dist/languages/cpp.d.ts +2 -0
  11. package/dist/languages/cpp.js +109 -0
  12. package/dist/languages/dart.d.ts +2 -0
  13. package/dist/languages/dart.js +162 -0
  14. package/dist/languages/go.d.ts +2 -0
  15. package/dist/languages/go.js +111 -0
  16. package/dist/languages/java.d.ts +2 -0
  17. package/dist/languages/java.js +113 -0
  18. package/dist/languages/plugin.d.ts +20 -0
  19. package/dist/languages/plugin.js +148 -0
  20. package/dist/languages/python.d.ts +2 -0
  21. package/dist/languages/python.js +129 -0
  22. package/dist/languages/ruby.d.ts +2 -0
  23. package/dist/languages/ruby.js +97 -0
  24. package/dist/languages/rust.d.ts +2 -0
  25. package/dist/languages/rust.js +121 -0
  26. package/dist/languages/typescript.d.ts +2 -0
  27. package/dist/languages/typescript.js +138 -0
  28. package/dist/license/validator.d.ts +23 -0
  29. package/dist/license/validator.js +297 -0
  30. package/dist/tools/analyze-impact.d.ts +23 -0
  31. package/dist/tools/analyze-impact.js +102 -0
  32. package/dist/tools/gate-build.d.ts +25 -0
  33. package/dist/tools/gate-build.js +243 -0
  34. package/dist/watcher/file-cache.d.ts +56 -0
  35. package/dist/watcher/file-cache.js +241 -0
  36. package/dist/web/public/app.js +2398 -0
  37. package/dist/web/public/index.html +258 -0
  38. package/dist/web/public/style.css +1827 -0
  39. package/dist/web/server.d.ts +29 -0
  40. package/dist/web/server.js +744 -0
  41. package/package.json +50 -0
package/dist/index.js ADDED
@@ -0,0 +1,518 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const dotenv = __importStar(require("dotenv"));
38
+ dotenv.config();
39
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
40
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
41
+ const child_process_1 = require("child_process");
42
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
43
+ const path = __importStar(require("path"));
44
+ const graph_1 = require("./graph");
45
+ const plugin_1 = require("./languages/plugin");
46
+ const analyze_impact_1 = require("./tools/analyze-impact");
47
+ const gate_build_1 = require("./tools/gate-build");
48
+ const analyzer_1 = require("./ai/analyzer");
49
+ const server_1 = require("./web/server");
50
+ const file_cache_1 = require("./watcher/file-cache");
51
+ const validator_1 = require("./license/validator");
52
+ // Configuration — auto-detect if env vars not set
53
+ let currentProjectRoot = process.env.SYKE_currentProjectRoot || (0, plugin_1.detectProjectRoot)();
54
+ let currentPackageName = process.env.SYKE_currentPackageName || (0, plugin_1.detectPackageName)(currentProjectRoot, (0, plugin_1.detectLanguages)(currentProjectRoot));
55
+ const WEB_PORT = parseInt(process.env.SYKE_WEB_PORT || "3333", 10);
56
+ function resolveFilePath(fileArg, projectRoot, sourceDir) {
57
+ const srcDir = sourceDir || path.join(projectRoot, "src");
58
+ const srcDirName = path.basename(srcDir); // "lib" or "src"
59
+ if (path.isAbsolute(fileArg)) {
60
+ return path.normalize(fileArg);
61
+ }
62
+ if (fileArg.startsWith(srcDirName + "/") || fileArg.startsWith(srcDirName + "\\")) {
63
+ return path.normalize(path.join(projectRoot, fileArg));
64
+ }
65
+ return path.normalize(path.join(srcDir, fileArg));
66
+ }
67
+ // License state — set at startup
68
+ let licenseStatus = { plan: "free", source: "default" };
69
+ // Free tier limits
70
+ const FREE_MAX_FILES = 50;
71
+ const FREE_MAX_HUB_FILES = 3;
72
+ /**
73
+ * Check if a resolved file path is within the free tier limit.
74
+ * Free set = first 50 files sorted alphabetically by relative path.
75
+ */
76
+ function isFileInFreeSet(resolvedPath, graph) {
77
+ if (licenseStatus.plan === "pro")
78
+ return true;
79
+ const allFiles = [...graph.files].sort();
80
+ const idx = allFiles.indexOf(resolvedPath);
81
+ return idx >= 0 && idx < FREE_MAX_FILES;
82
+ }
83
+ const PRO_UPGRADE_MSG = "This file exceeds the Free tier limit (50 files). Upgrade to Pro for unlimited analysis: https://syke.cloud/dashboard/";
84
+ async function main() {
85
+ if (!currentProjectRoot) {
86
+ console.error("[syke] ERROR: Could not detect project root. Set SYKE_currentProjectRoot or run from project directory.");
87
+ process.exit(1);
88
+ }
89
+ // Check license before starting
90
+ licenseStatus = await (0, validator_1.checkLicense)();
91
+ // Show device binding errors
92
+ if (licenseStatus.error) {
93
+ console.error(`[syke] LICENSE ERROR: ${licenseStatus.error}`);
94
+ }
95
+ // Graceful shutdown — deactivate session so another device can use the license
96
+ const shutdown = async () => {
97
+ console.error("[syke] Shutting down — deactivating session...");
98
+ await (0, validator_1.stopAndDeactivate)();
99
+ process.exit(0);
100
+ };
101
+ process.on("SIGINT", shutdown);
102
+ process.on("SIGTERM", shutdown);
103
+ const server = new index_js_1.Server({ name: "syke", version: "0.4.0" }, { capabilities: { tools: {} } });
104
+ // List tools
105
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
106
+ tools: [
107
+ {
108
+ name: "gate_build",
109
+ description: "MANDATORY: Call this BEFORE any build, deploy, or test command. "
110
+ + "Returns PASS, WARN, or FAIL verdict with detailed reasons. "
111
+ + "If FAIL, do NOT proceed with build — fix issues first. "
112
+ + "Always call this as the final check before any compilation or deployment.",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {
116
+ files: {
117
+ type: "array",
118
+ items: { type: "string" },
119
+ description: "Optional list of modified files (absolute or relative to source directory)",
120
+ },
121
+ },
122
+ },
123
+ },
124
+ {
125
+ name: "analyze_impact",
126
+ description: "Analyze which files are impacted when a given file is modified. Returns direct and transitive dependents with risk level.",
127
+ inputSchema: {
128
+ type: "object",
129
+ properties: {
130
+ file: {
131
+ type: "string",
132
+ description: 'Source file path (absolute or relative to source directory). Example: "features/auth/data/auth_repository.dart"',
133
+ },
134
+ },
135
+ required: ["file"],
136
+ },
137
+ },
138
+ {
139
+ name: "check_safe",
140
+ description: "Quick safety check for modifying a file. Returns a one-line verdict: HIGH/MEDIUM/LOW/NONE risk with impacted file count.",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ file: { type: "string", description: "Source file path to check" },
145
+ },
146
+ required: ["file"],
147
+ },
148
+ },
149
+ {
150
+ name: "get_dependencies",
151
+ description: "List the internal files that a given file imports (forward dependencies).",
152
+ inputSchema: {
153
+ type: "object",
154
+ properties: {
155
+ file: { type: "string", description: "Source file path" },
156
+ },
157
+ required: ["file"],
158
+ },
159
+ },
160
+ {
161
+ name: "get_hub_files",
162
+ description: "Rank files by how many other files depend on them. High-hub files are risky to modify.",
163
+ inputSchema: {
164
+ type: "object",
165
+ properties: {
166
+ top_n: {
167
+ type: "number",
168
+ description: "Number of top files to return (default 10)",
169
+ },
170
+ },
171
+ },
172
+ },
173
+ {
174
+ name: "refresh_graph",
175
+ description: "Re-scan all source files and rebuild the dependency graph. Use after adding/removing files.",
176
+ inputSchema: {
177
+ type: "object",
178
+ properties: {},
179
+ },
180
+ },
181
+ {
182
+ name: "ai_analyze",
183
+ description: "Use Gemini AI to perform semantic analysis on a file. Reads the file's source code and its dependents to explain what might break when modified and how to safely make changes. Returns Korean analysis.",
184
+ inputSchema: {
185
+ type: "object",
186
+ properties: {
187
+ file: {
188
+ type: "string",
189
+ description: "Source file path to analyze with AI",
190
+ },
191
+ },
192
+ required: ["file"],
193
+ },
194
+ },
195
+ {
196
+ name: "check_warnings",
197
+ description: "Check for unresolved warnings from SYKE's real-time monitoring. Returns warnings about file changes that may have broken dependents. Use this AFTER modifying files to see if SYKE caught any issues you may have missed. Pass acknowledge=true to clear warnings after reading them.",
198
+ inputSchema: {
199
+ type: "object",
200
+ properties: {
201
+ acknowledge: {
202
+ type: "boolean",
203
+ description: "If true, mark all warnings as acknowledged after returning them (default: false)",
204
+ },
205
+ },
206
+ },
207
+ },
208
+ ],
209
+ }));
210
+ // Handle tool calls
211
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
212
+ const { name, arguments: args } = request.params;
213
+ switch (name) {
214
+ case "gate_build": {
215
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
216
+ const files = args.files?.map((f) => resolveFilePath(f, currentProjectRoot, graph.sourceDir));
217
+ const result = (0, gate_build_1.gateCheck)(graph, files);
218
+ return {
219
+ content: [
220
+ { type: "text", text: (0, gate_build_1.formatGateResult)(result) },
221
+ ],
222
+ isError: result.verdict === "FAIL",
223
+ };
224
+ }
225
+ case "analyze_impact": {
226
+ const file = args.file;
227
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
228
+ const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
229
+ if (!graph.files.has(resolved)) {
230
+ return {
231
+ content: [
232
+ {
233
+ type: "text",
234
+ text: `File not found in dependency graph: ${file}\nResolved to: ${resolved}\nTip: Use a path relative to the source directory`,
235
+ },
236
+ ],
237
+ };
238
+ }
239
+ if (!isFileInFreeSet(resolved, graph)) {
240
+ return { content: [{ type: "text", text: PRO_UPGRADE_MSG }] };
241
+ }
242
+ const result = (0, analyze_impact_1.analyzeImpact)(resolved, graph);
243
+ const lines = [
244
+ `## Impact Analysis: ${result.relativePath}`,
245
+ `**Risk Level:** ${result.riskLevel}`,
246
+ `**Total impacted files:** ${result.totalImpacted}`,
247
+ "",
248
+ ];
249
+ if (result.directDependents.length > 0) {
250
+ lines.push(`### Direct Dependents (${result.directDependents.length})`);
251
+ for (const d of result.directDependents) {
252
+ lines.push(`- ${d}`);
253
+ }
254
+ }
255
+ if (result.transitiveDependents.length > 0) {
256
+ lines.push("");
257
+ lines.push(`### Transitive Dependents (${result.transitiveDependents.length})`);
258
+ for (const d of result.transitiveDependents) {
259
+ lines.push(`- ${d}`);
260
+ }
261
+ }
262
+ return { content: [{ type: "text", text: lines.join("\n") }] };
263
+ }
264
+ case "check_safe": {
265
+ const file = args.file;
266
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
267
+ const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
268
+ if (!graph.files.has(resolved)) {
269
+ return {
270
+ content: [
271
+ {
272
+ type: "text",
273
+ text: `UNKNOWN — file not found in graph: ${file}`,
274
+ },
275
+ ],
276
+ };
277
+ }
278
+ if (!isFileInFreeSet(resolved, graph)) {
279
+ return { content: [{ type: "text", text: PRO_UPGRADE_MSG }] };
280
+ }
281
+ const result = (0, analyze_impact_1.analyzeImpact)(resolved, graph);
282
+ const rel = path.relative(graph.sourceDir, resolved).replace(/\\/g, "/");
283
+ return {
284
+ content: [
285
+ {
286
+ type: "text",
287
+ text: `${result.riskLevel} — ${rel} impacts ${result.totalImpacted} file(s)`,
288
+ },
289
+ ],
290
+ };
291
+ }
292
+ case "get_dependencies": {
293
+ const file = args.file;
294
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
295
+ const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
296
+ if (!graph.files.has(resolved)) {
297
+ return {
298
+ content: [
299
+ {
300
+ type: "text",
301
+ text: `File not found in graph: ${file}`,
302
+ },
303
+ ],
304
+ };
305
+ }
306
+ if (!isFileInFreeSet(resolved, graph)) {
307
+ return { content: [{ type: "text", text: PRO_UPGRADE_MSG }] };
308
+ }
309
+ const deps = graph.forward.get(resolved) || [];
310
+ const rel = path.relative(graph.sourceDir, resolved).replace(/\\/g, "/");
311
+ if (deps.length === 0) {
312
+ return {
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text: `${rel} has no internal dependencies.`,
317
+ },
318
+ ],
319
+ };
320
+ }
321
+ const lines = [`## Dependencies of ${rel}`, ""];
322
+ for (const d of deps) {
323
+ lines.push(`- ${path.relative(graph.sourceDir, d).replace(/\\/g, "/")}`);
324
+ }
325
+ return { content: [{ type: "text", text: lines.join("\n") }] };
326
+ }
327
+ case "get_hub_files": {
328
+ const requestedN = args.top_n || 10;
329
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
330
+ // Free: limited to top 3, Pro: unlimited
331
+ const maxN = licenseStatus.plan === "pro" ? requestedN : Math.min(requestedN, FREE_MAX_HUB_FILES);
332
+ const hubs = (0, analyze_impact_1.getHubFiles)(graph, maxN);
333
+ const lines = [
334
+ `## Hub Files (Top ${hubs.length})`,
335
+ "",
336
+ "| # | File | Dependents | Risk |",
337
+ "|---|------|-----------|------|",
338
+ ];
339
+ hubs.forEach((h, i) => {
340
+ lines.push(`| ${i + 1} | ${h.relativePath} | ${h.dependentCount} | ${h.riskLevel} |`);
341
+ });
342
+ lines.push("");
343
+ lines.push(`Total files in graph: ${graph.files.size}`);
344
+ if (licenseStatus.plan !== "pro" && requestedN > FREE_MAX_HUB_FILES) {
345
+ lines.push("");
346
+ lines.push(`Free tier shows top ${FREE_MAX_HUB_FILES} hub files. Upgrade to Pro for full ranking: https://syke.cloud/dashboard/`);
347
+ }
348
+ return { content: [{ type: "text", text: lines.join("\n") }] };
349
+ }
350
+ case "refresh_graph": {
351
+ const graph = (0, graph_1.refreshGraph)(currentProjectRoot, currentPackageName);
352
+ return {
353
+ content: [
354
+ {
355
+ type: "text",
356
+ text: `Graph refreshed (${graph.languages.join("+")}): ${graph.files.size} files scanned.`,
357
+ },
358
+ ],
359
+ };
360
+ }
361
+ case "ai_analyze": {
362
+ // Pro-only feature
363
+ if (licenseStatus.plan !== "pro") {
364
+ return {
365
+ content: [
366
+ {
367
+ type: "text",
368
+ text: "ai_analyze requires SYKE Pro. Upgrade at https://syke.cloud/dashboard/\n\nSet SYKE_LICENSE_KEY in your MCP config to activate Pro features.",
369
+ },
370
+ ],
371
+ };
372
+ }
373
+ const file = args.file;
374
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
375
+ const resolved = resolveFilePath(file, currentProjectRoot, graph.sourceDir);
376
+ if (!graph.files.has(resolved)) {
377
+ return {
378
+ content: [
379
+ {
380
+ type: "text",
381
+ text: `File not found in graph: ${file}`,
382
+ },
383
+ ],
384
+ };
385
+ }
386
+ const impactResult = (0, analyze_impact_1.analyzeImpact)(resolved, graph);
387
+ const aiResult = await (0, analyzer_1.analyzeWithAI)(resolved, impactResult, graph);
388
+ return {
389
+ content: [{ type: "text", text: aiResult }],
390
+ };
391
+ }
392
+ case "check_warnings": {
393
+ const shouldAck = args.acknowledge || false;
394
+ const warnings = (0, server_1.getUnacknowledgedWarnings)();
395
+ if (warnings.length === 0) {
396
+ return {
397
+ content: [
398
+ {
399
+ type: "text",
400
+ text: "No unresolved warnings. All clear.",
401
+ },
402
+ ],
403
+ };
404
+ }
405
+ const lines = [
406
+ `SYKE detected ${warnings.length} unresolved warning(s):`,
407
+ "",
408
+ ];
409
+ for (const w of warnings) {
410
+ const time = new Date(w.timestamp).toLocaleTimeString();
411
+ lines.push(`### [${w.riskLevel}] ${w.file} (${time})`);
412
+ lines.push(`**Summary:** ${w.summary}`);
413
+ if (w.brokenImports.length > 0) {
414
+ lines.push(`**Broken imports:** ${w.brokenImports.join(", ")}`);
415
+ }
416
+ if (w.sideEffects.length > 0) {
417
+ lines.push(`**Side effects:** ${w.sideEffects.join("; ")}`);
418
+ }
419
+ if (w.warnings.length > 0) {
420
+ lines.push(`**Warnings:** ${w.warnings.join("; ")}`);
421
+ }
422
+ if (w.suggestion) {
423
+ lines.push(`**Suggestion:** ${w.suggestion}`);
424
+ }
425
+ lines.push(`**Affected files:** ${w.affectedCount}`);
426
+ lines.push("");
427
+ }
428
+ if (shouldAck) {
429
+ const count = (0, server_1.acknowledgeWarnings)();
430
+ lines.push(`---`);
431
+ lines.push(`${count} warning(s) acknowledged.`);
432
+ }
433
+ return {
434
+ content: [{ type: "text", text: lines.join("\n") }],
435
+ };
436
+ }
437
+ default:
438
+ return {
439
+ content: [
440
+ { type: "text", text: `Unknown tool: ${name}` },
441
+ ],
442
+ isError: true,
443
+ };
444
+ }
445
+ });
446
+ // Pre-warm the graph
447
+ const detectedLangs = (0, plugin_1.detectLanguages)(currentProjectRoot).map(p => p.name).join(", ") || "none";
448
+ console.error(`[syke] Starting SYKE MCP Server v0.4.0`);
449
+ console.error(`[syke] License: ${licenseStatus.plan.toUpperCase()} (${licenseStatus.source})`);
450
+ if (licenseStatus.plan === "pro") {
451
+ console.error(`[syke] Pro activated for: ${licenseStatus.email || "unknown"}`);
452
+ }
453
+ else {
454
+ console.error(`[syke] Free tier: ${FREE_MAX_FILES} file limit, ai_analyze disabled`);
455
+ console.error(`[syke] Upgrade at https://syke.cloud/dashboard/`);
456
+ }
457
+ console.error(`[syke] Project root: ${currentProjectRoot}`);
458
+ console.error(`[syke] Detected languages: ${detectedLangs}`);
459
+ console.error(`[syke] Package name: ${currentPackageName}`);
460
+ const graph = (0, graph_1.getGraph)(currentProjectRoot, currentPackageName);
461
+ // Free tier: warn if over file limit
462
+ if (licenseStatus.plan === "free" && graph.files.size > FREE_MAX_FILES) {
463
+ console.error(`[syke] WARNING: Free tier limited to ${FREE_MAX_FILES} files. Your project has ${graph.files.size} files.`);
464
+ console.error(`[syke] Only the first ${FREE_MAX_FILES} files will be analyzed. Upgrade to Pro for unlimited files.`);
465
+ }
466
+ // Initialize file cache (load ALL source files into memory)
467
+ let fileCache = new file_cache_1.FileCache(currentProjectRoot);
468
+ fileCache.initialize();
469
+ fileCache.startWatching();
470
+ // Switch project callback — reinitializes graph + file cache
471
+ function switchProject(newRoot) {
472
+ currentProjectRoot = newRoot;
473
+ const plugins = (0, plugin_1.detectLanguages)(newRoot);
474
+ currentPackageName = (0, plugin_1.detectPackageName)(newRoot, plugins);
475
+ // Stop old file cache and create new one
476
+ fileCache.stop();
477
+ fileCache = new file_cache_1.FileCache(newRoot);
478
+ fileCache.initialize();
479
+ fileCache.startWatching();
480
+ // Rebuild graph
481
+ const graph = (0, graph_1.refreshGraph)(newRoot, currentPackageName);
482
+ console.error(`[syke] Switched to project: ${newRoot}`);
483
+ console.error(`[syke] Languages: ${plugins.map(p => p.name).join(", ")}`);
484
+ console.error(`[syke] Package: ${currentPackageName}`);
485
+ console.error(`[syke] Files: ${graph.files.size}`);
486
+ let edgeCount = 0;
487
+ for (const deps of graph.forward.values())
488
+ edgeCount += deps.length;
489
+ return {
490
+ projectRoot: newRoot,
491
+ packageName: currentPackageName,
492
+ languages: graph.languages,
493
+ fileCount: graph.files.size,
494
+ edgeCount,
495
+ };
496
+ }
497
+ // Start Express web server with file cache for SSE
498
+ const webApp = (0, server_1.createWebServer)(() => (0, graph_1.getGraph)(currentProjectRoot, currentPackageName), fileCache, switchProject, () => currentProjectRoot, () => currentPackageName, () => licenseStatus);
499
+ webApp.listen(WEB_PORT, () => {
500
+ const dashUrl = `http://localhost:${WEB_PORT}`;
501
+ console.error(`[syke] Web dashboard: ${dashUrl}`);
502
+ // Auto-open browser (disable with SYKE_NO_BROWSER=1)
503
+ if (process.env.SYKE_NO_BROWSER !== "1") {
504
+ const cmd = process.platform === "win32" ? `start ${dashUrl}`
505
+ : process.platform === "darwin" ? `open ${dashUrl}`
506
+ : `xdg-open ${dashUrl}`;
507
+ (0, child_process_1.exec)(cmd, () => { });
508
+ }
509
+ });
510
+ // Connect via stdio
511
+ const transport = new stdio_js_1.StdioServerTransport();
512
+ await server.connect(transport);
513
+ console.error("[syke] MCP server connected via stdio");
514
+ }
515
+ main().catch((err) => {
516
+ console.error("[syke] Fatal error:", err);
517
+ process.exit(1);
518
+ });
@@ -0,0 +1,2 @@
1
+ import { LanguagePlugin } from "./plugin";
2
+ export declare const cppPlugin: LanguagePlugin;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.cppPlugin = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const plugin_1 = require("./plugin");
40
+ const INCLUDE_RE = /^#include\s+"(.+?)"/;
41
+ exports.cppPlugin = {
42
+ id: "cpp",
43
+ name: "C/C++",
44
+ extensions: [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"],
45
+ codeBlockLang: "cpp",
46
+ detectProject(root) {
47
+ return fs.existsSync(path.join(root, "CMakeLists.txt")) ||
48
+ fs.existsSync(path.join(root, "Makefile")) ||
49
+ fs.existsSync(path.join(root, "meson.build"));
50
+ },
51
+ getSourceDirs(root) {
52
+ const dirs = [];
53
+ const srcDir = path.join(root, "src");
54
+ const includeDir = path.join(root, "include");
55
+ if (fs.existsSync(srcDir))
56
+ dirs.push(srcDir);
57
+ if (fs.existsSync(includeDir))
58
+ dirs.push(includeDir);
59
+ if (dirs.length === 0)
60
+ dirs.push(root);
61
+ return dirs;
62
+ },
63
+ getPackageName(root) {
64
+ try {
65
+ const cmake = fs.readFileSync(path.join(root, "CMakeLists.txt"), "utf-8");
66
+ const match = cmake.match(/project\s*\(\s*(\w+)/i);
67
+ return match ? match[1] : path.basename(root);
68
+ }
69
+ catch {
70
+ return path.basename(root);
71
+ }
72
+ },
73
+ discoverFiles(dir) {
74
+ return (0, plugin_1.discoverAllFiles)(dir, [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"]);
75
+ },
76
+ parseImports(filePath, projectRoot, sourceDir) {
77
+ let content;
78
+ try {
79
+ content = fs.readFileSync(filePath, "utf-8");
80
+ }
81
+ catch {
82
+ return [];
83
+ }
84
+ const fileDir = path.dirname(filePath);
85
+ const imports = [];
86
+ for (const line of content.split("\n")) {
87
+ const trimmed = line.trim();
88
+ // Only process local includes (#include "..."), skip system <...>
89
+ const match = trimmed.match(INCLUDE_RE);
90
+ if (!match)
91
+ continue;
92
+ const includePath = match[1];
93
+ // Try resolving: relative to file, then to sourceDir, then to projectRoot
94
+ const candidates = [
95
+ path.normalize(path.resolve(fileDir, includePath)),
96
+ path.normalize(path.join(sourceDir, includePath)),
97
+ path.normalize(path.join(projectRoot, "include", includePath)),
98
+ path.normalize(path.join(projectRoot, includePath)),
99
+ ];
100
+ for (const c of candidates) {
101
+ if (fs.existsSync(c)) {
102
+ imports.push(c);
103
+ break;
104
+ }
105
+ }
106
+ }
107
+ return imports;
108
+ },
109
+ };
@@ -0,0 +1,2 @@
1
+ import { LanguagePlugin } from "./plugin";
2
+ export declare const dartPlugin: LanguagePlugin;