@syke1/mcp-server 1.8.4 → 1.8.6
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/README.md +6 -0
- package/dist/graph.d.ts +8 -0
- package/dist/graph.js +100 -0
- package/dist/index.js +15 -9
- package/dist/languages/cpp.js +8 -7
- package/dist/languages/dart.js +21 -15
- package/dist/languages/go.js +21 -15
- package/dist/languages/java.js +8 -7
- package/dist/languages/plugin.d.ts +2 -1
- package/dist/languages/plugin.js +36 -4
- package/dist/languages/python.js +8 -7
- package/dist/languages/ruby.js +8 -7
- package/dist/languages/rust.js +8 -7
- package/dist/languages/typescript.js +15 -7
- package/dist/watcher/file-cache.d.ts +8 -0
- package/dist/watcher/file-cache.js +13 -0
- package/dist/web/public/app.js +44 -0
- package/dist/web/public/index.html +20 -0
- package/dist/web/public/style.css +105 -0
- package/dist/web/server.d.ts +1 -1
- package/dist/web/server.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -281,6 +281,12 @@ We're giving the **first 20 developers** full Pro access for **30 days** — no
|
|
|
281
281
|
|
|
282
282
|
Spots are limited. Once they're gone, they're gone.
|
|
283
283
|
|
|
284
|
+
## Source Code
|
|
285
|
+
|
|
286
|
+
This repository contains the **Free tier source code** — the core dependency graph engine, language plugins, and 3 free MCP tools (`gate_build`, `check_safe`, `get_dependencies`).
|
|
287
|
+
|
|
288
|
+
Pro and Cortex features (advanced algorithms, AI analysis, real-time monitoring, web dashboard) are included in the [npm package](https://www.npmjs.com/package/@syke1/mcp-server) as compiled code.
|
|
289
|
+
|
|
284
290
|
## License
|
|
285
291
|
|
|
286
292
|
[Elastic License 2.0 (ELv2)](LICENSE)
|
package/dist/graph.d.ts
CHANGED
|
@@ -12,5 +12,13 @@ export interface DependencyGraph {
|
|
|
12
12
|
scc?: SCCResult;
|
|
13
13
|
}
|
|
14
14
|
export declare function buildGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
|
|
15
|
+
/**
|
|
16
|
+
* Async graph builder — reads all files in parallel batches for faster startup.
|
|
17
|
+
* Returns the graph + contentMap (reusable by FileCache to avoid re-reading).
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildGraphAsync(projectRoot: string, packageName?: string, maxFiles?: number): Promise<{
|
|
20
|
+
graph: DependencyGraph;
|
|
21
|
+
contentMap: Map<string, string>;
|
|
22
|
+
}>;
|
|
15
23
|
export declare function getGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
|
|
16
24
|
export declare function rebuildGraph(projectRoot: string, packageName?: string, maxFiles?: number): DependencyGraph;
|
package/dist/graph.js
CHANGED
|
@@ -34,9 +34,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.buildGraph = buildGraph;
|
|
37
|
+
exports.buildGraphAsync = buildGraphAsync;
|
|
37
38
|
exports.getGraph = getGraph;
|
|
38
39
|
exports.rebuildGraph = rebuildGraph;
|
|
39
40
|
const path = __importStar(require("path"));
|
|
41
|
+
const fs = __importStar(require("fs/promises"));
|
|
40
42
|
const plugin_1 = require("./languages/plugin");
|
|
41
43
|
const typescript_1 = require("./languages/typescript");
|
|
42
44
|
const scc_1 = require("./graph/scc");
|
|
@@ -109,6 +111,104 @@ function buildGraph(projectRoot, packageName, maxFiles) {
|
|
|
109
111
|
console.error(`[syke] Graph built (${languages.join("+")}): ${files.size} files, ${countEdges(forward)} edges, ${scc.components.length} SCCs (${cyclicCount} cyclic)`);
|
|
110
112
|
return graph;
|
|
111
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Async graph builder — reads all files in parallel batches for faster startup.
|
|
116
|
+
* Returns the graph + contentMap (reusable by FileCache to avoid re-reading).
|
|
117
|
+
*/
|
|
118
|
+
async function buildGraphAsync(projectRoot, packageName, maxFiles) {
|
|
119
|
+
const detectedPlugins = (0, plugin_1.detectLanguages)(projectRoot);
|
|
120
|
+
const languages = detectedPlugins.map(p => p.id);
|
|
121
|
+
const forward = new Map();
|
|
122
|
+
const reverse = new Map();
|
|
123
|
+
const files = new Set();
|
|
124
|
+
const allSourceDirs = [];
|
|
125
|
+
let totalDiscovered = 0;
|
|
126
|
+
let fileLimitHit = false;
|
|
127
|
+
// Phase 1: Discover files (sync — directory walking is I/O-light)
|
|
128
|
+
const pluginDirFiles = new Map();
|
|
129
|
+
for (const plugin of detectedPlugins) {
|
|
130
|
+
const dirs = plugin.getSourceDirs(projectRoot);
|
|
131
|
+
const dirMap = new Map();
|
|
132
|
+
for (const dir of dirs) {
|
|
133
|
+
if (!allSourceDirs.includes(dir))
|
|
134
|
+
allSourceDirs.push(dir);
|
|
135
|
+
const sourceFiles = plugin.discoverFiles(dir);
|
|
136
|
+
totalDiscovered += sourceFiles.length;
|
|
137
|
+
dirMap.set(dir, sourceFiles);
|
|
138
|
+
for (const f of sourceFiles) {
|
|
139
|
+
if (maxFiles && files.size >= maxFiles) {
|
|
140
|
+
fileLimitHit = true;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
files.add(f);
|
|
144
|
+
if (!forward.has(f))
|
|
145
|
+
forward.set(f, []);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
pluginDirFiles.set(plugin, dirMap);
|
|
149
|
+
}
|
|
150
|
+
if (fileLimitHit) {
|
|
151
|
+
console.error(`[syke] Free tier: loaded ${files.size}/${totalDiscovered} files (limit: ${maxFiles}). Upgrade to Pro for unlimited.`);
|
|
152
|
+
}
|
|
153
|
+
// Phase 2: Parallel file reading (biggest performance win)
|
|
154
|
+
const contentMap = new Map();
|
|
155
|
+
const filesToRead = [...files];
|
|
156
|
+
const BATCH_SIZE = 100;
|
|
157
|
+
for (let i = 0; i < filesToRead.length; i += BATCH_SIZE) {
|
|
158
|
+
const batch = filesToRead.slice(i, i + BATCH_SIZE);
|
|
159
|
+
const results = await Promise.all(batch.map(async (f) => {
|
|
160
|
+
try {
|
|
161
|
+
const content = await fs.readFile(f, "utf-8");
|
|
162
|
+
return [f, content];
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
for (const r of results) {
|
|
169
|
+
if (r)
|
|
170
|
+
contentMap.set(r[0], r[1]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Phase 3: Parse imports using pre-read content (no I/O)
|
|
174
|
+
for (const [plugin, dirMap] of pluginDirFiles) {
|
|
175
|
+
for (const [dir, sourceFiles] of dirMap) {
|
|
176
|
+
for (const f of sourceFiles) {
|
|
177
|
+
if (!files.has(f))
|
|
178
|
+
continue;
|
|
179
|
+
const content = contentMap.get(f);
|
|
180
|
+
const imports = plugin.parseImports(f, projectRoot, dir, content);
|
|
181
|
+
const validImports = [];
|
|
182
|
+
for (const imp of imports) {
|
|
183
|
+
if (!files.has(imp))
|
|
184
|
+
continue;
|
|
185
|
+
validImports.push(imp);
|
|
186
|
+
const rev = reverse.get(imp) || [];
|
|
187
|
+
rev.push(f);
|
|
188
|
+
reverse.set(imp, rev);
|
|
189
|
+
}
|
|
190
|
+
forward.set(f, validImports);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const sourceDir = allSourceDirs[0] || path.join(projectRoot, "src");
|
|
195
|
+
const graph = {
|
|
196
|
+
forward,
|
|
197
|
+
reverse,
|
|
198
|
+
files,
|
|
199
|
+
projectRoot,
|
|
200
|
+
languages,
|
|
201
|
+
sourceDirs: allSourceDirs,
|
|
202
|
+
sourceDir,
|
|
203
|
+
};
|
|
204
|
+
(0, memo_cache_1.resetMemoCache)();
|
|
205
|
+
const scc = (0, scc_1.computeSCC)(graph);
|
|
206
|
+
graph.scc = scc;
|
|
207
|
+
const cyclicCount = scc.condensed.nodes.filter(n => n.isCyclic).length;
|
|
208
|
+
cachedGraph = graph;
|
|
209
|
+
console.error(`[syke] Graph built (${languages.join("+")}): ${files.size} files, ${countEdges(forward)} edges, ${scc.components.length} SCCs (${cyclicCount} cyclic)`);
|
|
210
|
+
return { graph, contentMap };
|
|
211
|
+
}
|
|
112
212
|
function countEdges(forward) {
|
|
113
213
|
let count = 0;
|
|
114
214
|
for (const deps of forward.values()) {
|
package/dist/index.js
CHANGED
|
@@ -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.
|
|
150
|
+
const server = new index_js_1.Server({ name: "syke", version: "1.8.6" }, { 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.
|
|
631
|
+
console.error(`[syke] Starting SYKE MCP Server v1.8.5`);
|
|
632
632
|
console.error(`[syke] License: ${licenseStatus.plan.toUpperCase()} (${licenseStatus.source})`);
|
|
633
633
|
if (licenseStatus.expiresAt) {
|
|
634
634
|
console.error(`[syke] Expires: ${licenseStatus.expiresAt}`);
|
|
@@ -656,9 +656,9 @@ async function main() {
|
|
|
656
656
|
});
|
|
657
657
|
const data = await res.json();
|
|
658
658
|
const latest = data["dist-tags"]?.latest;
|
|
659
|
-
if (latest && latest !== "1.8.
|
|
659
|
+
if (latest && latest !== "1.8.6") {
|
|
660
660
|
const [lM, lm, lp] = latest.split(".").map(Number);
|
|
661
|
-
const [cM, cm, cp] = [1, 8,
|
|
661
|
+
const [cM, cm, cp] = [1, 8, 6];
|
|
662
662
|
if (lM > cM || (lM === cM && lm > cm) || (lM === cM && lm === cm && lp > cp)) {
|
|
663
663
|
console.error(`[syke] Update available: v${latest}. Run: npx @syke1/mcp-server@latest`);
|
|
664
664
|
}
|
|
@@ -667,17 +667,21 @@ async function main() {
|
|
|
667
667
|
catch { }
|
|
668
668
|
})();
|
|
669
669
|
let fileCache = null;
|
|
670
|
+
let graphLoadTimeMs = 0;
|
|
670
671
|
if (currentProjectRoot) {
|
|
671
672
|
const detectedLangs = (0, plugin_1.detectLanguages)(currentProjectRoot).map(p => p.name).join(", ") || "none";
|
|
672
673
|
console.error(`[syke] Project root: ${currentProjectRoot}`);
|
|
673
674
|
console.error(`[syke] Detected languages: ${detectedLangs}`);
|
|
674
675
|
console.error(`[syke] Package name: ${currentPackageName}`);
|
|
675
|
-
const
|
|
676
|
-
|
|
676
|
+
const startTime = performance.now();
|
|
677
|
+
const { graph, contentMap } = await (0, graph_1.buildGraphAsync)(currentProjectRoot, currentPackageName, getMaxFiles());
|
|
678
|
+
// Initialize file cache from pre-read content (no re-reading files)
|
|
677
679
|
fileCache = new file_cache_1.FileCache(currentProjectRoot);
|
|
678
|
-
fileCache.
|
|
680
|
+
fileCache.initializeFromContentMap(contentMap);
|
|
679
681
|
fileCache.setGraph(graph); // Enable incremental graph updates on file changes
|
|
680
682
|
fileCache.startWatching();
|
|
683
|
+
graphLoadTimeMs = Math.round(performance.now() - startTime);
|
|
684
|
+
console.error(`[syke] Total load time: ${graphLoadTimeMs}ms`);
|
|
681
685
|
}
|
|
682
686
|
// Web server handle (set after server starts)
|
|
683
687
|
let webServerHandle = null;
|
|
@@ -784,7 +788,9 @@ async function main() {
|
|
|
784
788
|
activeProvider: (0, provider_1.getProviderName)(),
|
|
785
789
|
forced,
|
|
786
790
|
};
|
|
787
|
-
}
|
|
791
|
+
},
|
|
792
|
+
// getGraphLoadTimeMs
|
|
793
|
+
() => graphLoadTimeMs);
|
|
788
794
|
webServerHandle = { setFileCache: setWebFileCache };
|
|
789
795
|
webApp.listen(WEB_PORT, () => {
|
|
790
796
|
const dashUrl = `http://localhost:${WEB_PORT}`;
|
|
@@ -816,7 +822,7 @@ main().catch((err) => {
|
|
|
816
822
|
* See: https://smithery.ai/docs/deploy#sandbox-server
|
|
817
823
|
*/
|
|
818
824
|
function createSandboxServer() {
|
|
819
|
-
const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.8.
|
|
825
|
+
const sandboxServer = new index_js_1.Server({ name: "syke", version: "1.8.6" }, { capabilities: { tools: {} } });
|
|
820
826
|
sandboxServer.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
821
827
|
tools: [
|
|
822
828
|
{
|
package/dist/languages/cpp.js
CHANGED
|
@@ -73,13 +73,14 @@ exports.cppPlugin = {
|
|
|
73
73
|
discoverFiles(dir) {
|
|
74
74
|
return (0, plugin_1.discoverAllFiles)(dir, [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp", ".hxx"]);
|
|
75
75
|
},
|
|
76
|
-
parseImports(filePath, projectRoot, sourceDir) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
parseImports(filePath, projectRoot, sourceDir, content) {
|
|
77
|
+
if (!content) {
|
|
78
|
+
try {
|
|
79
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
83
84
|
}
|
|
84
85
|
const fileDir = path.dirname(filePath);
|
|
85
86
|
const imports = [];
|
package/dist/languages/dart.js
CHANGED
|
@@ -38,6 +38,7 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const plugin_1 = require("./plugin");
|
|
40
40
|
const IMPORT_RE = /^import\s+['"](.+?)['"]/;
|
|
41
|
+
const dartPackageNameCache = new Map();
|
|
41
42
|
exports.dartPlugin = {
|
|
42
43
|
id: "dart",
|
|
43
44
|
name: "Dart",
|
|
@@ -63,25 +64,30 @@ exports.dartPlugin = {
|
|
|
63
64
|
discoverFiles(dir) {
|
|
64
65
|
return (0, plugin_1.discoverAllFiles)(dir, [".dart"]);
|
|
65
66
|
},
|
|
66
|
-
parseImports(filePath, projectRoot, sourceDir) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
parseImports(filePath, projectRoot, sourceDir, content) {
|
|
68
|
+
if (!content) {
|
|
69
|
+
try {
|
|
70
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
73
75
|
}
|
|
74
76
|
const libDir = sourceDir;
|
|
75
77
|
const imports = [];
|
|
76
|
-
// Read package name
|
|
77
|
-
let packageName =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
// Read package name (cached per projectRoot)
|
|
79
|
+
let packageName = dartPackageNameCache.get(projectRoot);
|
|
80
|
+
if (!packageName) {
|
|
81
|
+
packageName = path.basename(projectRoot);
|
|
82
|
+
try {
|
|
83
|
+
const pubspec = fs.readFileSync(path.join(projectRoot, "pubspec.yaml"), "utf-8");
|
|
84
|
+
const match = pubspec.match(/^name:\s*(\S+)/m);
|
|
85
|
+
if (match)
|
|
86
|
+
packageName = match[1];
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
dartPackageNameCache.set(projectRoot, packageName);
|
|
83
90
|
}
|
|
84
|
-
catch { }
|
|
85
91
|
for (const line of content.split("\n")) {
|
|
86
92
|
const trimmed = line.trim();
|
|
87
93
|
if (trimmed.length > 0 &&
|
package/dist/languages/go.js
CHANGED
|
@@ -38,6 +38,7 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const plugin_1 = require("./plugin");
|
|
40
40
|
const IMPORT_LINE_RE = /^\s*"([^"]+)"/;
|
|
41
|
+
const goModuleCache = new Map();
|
|
41
42
|
exports.goPlugin = {
|
|
42
43
|
id: "go",
|
|
43
44
|
name: "Go",
|
|
@@ -62,23 +63,28 @@ exports.goPlugin = {
|
|
|
62
63
|
discoverFiles(dir) {
|
|
63
64
|
return (0, plugin_1.discoverAllFiles)(dir, [".go"]).filter(f => !f.endsWith("_test.go"));
|
|
64
65
|
},
|
|
65
|
-
parseImports(filePath, projectRoot, _sourceDir) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
parseImports(filePath, projectRoot, _sourceDir, content) {
|
|
67
|
+
if (!content) {
|
|
68
|
+
try {
|
|
69
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
72
74
|
}
|
|
73
|
-
// Get module prefix from go.mod
|
|
74
|
-
let modulePrefix =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
// Get module prefix from go.mod (cached per projectRoot)
|
|
76
|
+
let modulePrefix = goModuleCache.get(projectRoot);
|
|
77
|
+
if (modulePrefix === undefined) {
|
|
78
|
+
modulePrefix = "";
|
|
79
|
+
try {
|
|
80
|
+
const goMod = fs.readFileSync(path.join(projectRoot, "go.mod"), "utf-8");
|
|
81
|
+
const match = goMod.match(/^module\s+(\S+)/m);
|
|
82
|
+
if (match)
|
|
83
|
+
modulePrefix = match[1];
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
goModuleCache.set(projectRoot, modulePrefix);
|
|
80
87
|
}
|
|
81
|
-
catch { }
|
|
82
88
|
const imports = [];
|
|
83
89
|
// Parse import block or single imports
|
|
84
90
|
const importBlockMatch = content.match(/import\s*\(([\s\S]*?)\)/);
|
package/dist/languages/java.js
CHANGED
|
@@ -73,13 +73,14 @@ exports.javaPlugin = {
|
|
|
73
73
|
discoverFiles(dir) {
|
|
74
74
|
return (0, plugin_1.discoverAllFiles)(dir, [".java"]);
|
|
75
75
|
},
|
|
76
|
-
parseImports(filePath, _projectRoot, sourceDir) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
parseImports(filePath, _projectRoot, sourceDir, content) {
|
|
77
|
+
if (!content) {
|
|
78
|
+
try {
|
|
79
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
83
84
|
}
|
|
84
85
|
const imports = [];
|
|
85
86
|
for (const line of content.split("\n")) {
|
|
@@ -7,7 +7,7 @@ export interface LanguagePlugin {
|
|
|
7
7
|
getSourceDirs(root: string): string[];
|
|
8
8
|
getPackageName(root: string): string;
|
|
9
9
|
discoverFiles(dir: string): string[];
|
|
10
|
-
parseImports(filePath: string, projectRoot: string, sourceDir: string): string[];
|
|
10
|
+
parseImports(filePath: string, projectRoot: string, sourceDir: string, content?: string): string[];
|
|
11
11
|
classifyLayer?(relPath: string): string | null;
|
|
12
12
|
}
|
|
13
13
|
export declare function registerPlugin(plugin: LanguagePlugin): void;
|
|
@@ -15,6 +15,7 @@ export declare function getPlugins(): LanguagePlugin[];
|
|
|
15
15
|
export declare function getPluginById(id: string): LanguagePlugin | undefined;
|
|
16
16
|
export declare function getPluginForFile(filePath: string): LanguagePlugin | undefined;
|
|
17
17
|
export declare function detectLanguages(root: string): LanguagePlugin[];
|
|
18
|
+
export declare function clearDetectCache(): void;
|
|
18
19
|
export declare function detectProjectRoot(startDir?: string): string;
|
|
19
20
|
export declare function detectPackageName(root: string, detectedPlugins: LanguagePlugin[]): string;
|
|
20
21
|
export declare function discoverAllFiles(rootDir: string, extensions: string[], extraSkipDirs?: string[]): string[];
|
package/dist/languages/plugin.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.getPlugins = getPlugins;
|
|
|
38
38
|
exports.getPluginById = getPluginById;
|
|
39
39
|
exports.getPluginForFile = getPluginForFile;
|
|
40
40
|
exports.detectLanguages = detectLanguages;
|
|
41
|
+
exports.clearDetectCache = clearDetectCache;
|
|
41
42
|
exports.detectProjectRoot = detectProjectRoot;
|
|
42
43
|
exports.detectPackageName = detectPackageName;
|
|
43
44
|
exports.discoverAllFiles = discoverAllFiles;
|
|
@@ -60,9 +61,17 @@ function getPluginForFile(filePath) {
|
|
|
60
61
|
const ext = path.extname(filePath).toLowerCase();
|
|
61
62
|
return plugins.find(p => p.extensions.includes(ext));
|
|
62
63
|
}
|
|
63
|
-
// ── Auto-detect ──
|
|
64
|
+
// ── Auto-detect (cached) ──
|
|
65
|
+
let detectCache = null;
|
|
64
66
|
function detectLanguages(root) {
|
|
65
|
-
|
|
67
|
+
if (detectCache && detectCache.root === root)
|
|
68
|
+
return detectCache.result;
|
|
69
|
+
const result = plugins.filter(p => p.detectProject(root));
|
|
70
|
+
detectCache = { root, result };
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
function clearDetectCache() {
|
|
74
|
+
detectCache = null;
|
|
66
75
|
}
|
|
67
76
|
function detectProjectRoot(startDir) {
|
|
68
77
|
let dir = startDir || process.cwd();
|
|
@@ -156,6 +165,29 @@ function hasManifestFile(root, manifests) {
|
|
|
156
165
|
catch { }
|
|
157
166
|
return false;
|
|
158
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Quick check: does a directory (recursively) contain any file with matching extensions?
|
|
170
|
+
* Short-circuits on the first match — much faster than collecting all files.
|
|
171
|
+
*/
|
|
172
|
+
function hasAnySourceFile(dir, extensions, skipSet) {
|
|
173
|
+
try {
|
|
174
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Check subdirectories only if no file found at this level
|
|
181
|
+
for (const entry of entries) {
|
|
182
|
+
if (entry.isDirectory() && !skipSet.has(entry.name) && !entry.name.startsWith(".")) {
|
|
183
|
+
if (hasAnySourceFile(path.join(dir, entry.name), extensions, skipSet))
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch { }
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
159
191
|
/**
|
|
160
192
|
* Find first-level subdirectories (and optionally root) that contain files with given extensions.
|
|
161
193
|
* Skips hidden dirs and common non-source dirs (.venv, node_modules, etc.).
|
|
@@ -168,14 +200,14 @@ function findSourceDirsWithFiles(root, extensions) {
|
|
|
168
200
|
if (entries.some(e => e.isFile() && extensions.some(ext => e.name.endsWith(ext)))) {
|
|
169
201
|
dirs.push(root);
|
|
170
202
|
}
|
|
171
|
-
// Check first-level subdirectories
|
|
203
|
+
// Check first-level subdirectories (quick existence check, not full walk)
|
|
172
204
|
for (const entry of entries) {
|
|
173
205
|
if (!entry.isDirectory())
|
|
174
206
|
continue;
|
|
175
207
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
176
208
|
continue;
|
|
177
209
|
const subdir = path.join(root, entry.name);
|
|
178
|
-
if (
|
|
210
|
+
if (hasAnySourceFile(subdir, extensions, SKIP_DIRS)) {
|
|
179
211
|
dirs.push(subdir);
|
|
180
212
|
}
|
|
181
213
|
}
|
package/dist/languages/python.js
CHANGED
|
@@ -64,13 +64,14 @@ exports.pythonPlugin = {
|
|
|
64
64
|
discoverFiles(dir) {
|
|
65
65
|
return (0, plugin_1.discoverAllFiles)(dir, [".py"]);
|
|
66
66
|
},
|
|
67
|
-
parseImports(filePath, projectRoot, sourceDir) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
parseImports(filePath, projectRoot, sourceDir, content) {
|
|
68
|
+
if (!content) {
|
|
69
|
+
try {
|
|
70
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
74
75
|
}
|
|
75
76
|
const imports = [];
|
|
76
77
|
for (const line of content.split("\n")) {
|
package/dist/languages/ruby.js
CHANGED
|
@@ -69,13 +69,14 @@ exports.rubyPlugin = {
|
|
|
69
69
|
discoverFiles(dir) {
|
|
70
70
|
return (0, plugin_1.discoverAllFiles)(dir, [".rb"]);
|
|
71
71
|
},
|
|
72
|
-
parseImports(filePath, _projectRoot, _sourceDir) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
parseImports(filePath, _projectRoot, _sourceDir, content) {
|
|
73
|
+
if (!content) {
|
|
74
|
+
try {
|
|
75
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
79
80
|
}
|
|
80
81
|
const fileDir = path.dirname(filePath);
|
|
81
82
|
const imports = [];
|
package/dist/languages/rust.js
CHANGED
|
@@ -64,13 +64,14 @@ exports.rustPlugin = {
|
|
|
64
64
|
discoverFiles(dir) {
|
|
65
65
|
return (0, plugin_1.discoverAllFiles)(dir, [".rs"]);
|
|
66
66
|
},
|
|
67
|
-
parseImports(filePath, _projectRoot, sourceDir) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
parseImports(filePath, _projectRoot, sourceDir, content) {
|
|
68
|
+
if (!content) {
|
|
69
|
+
try {
|
|
70
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
74
75
|
}
|
|
75
76
|
const imports = [];
|
|
76
77
|
for (const line of content.split("\n")) {
|
|
@@ -142,18 +142,26 @@ exports.typescriptPlugin = {
|
|
|
142
142
|
discoverFiles(dir) {
|
|
143
143
|
return (0, plugin_1.discoverAllFiles)(dir, [".ts", ".tsx", ".js", ".jsx"]).filter(f => !f.endsWith(".d.ts"));
|
|
144
144
|
},
|
|
145
|
-
parseImports(filePath, projectRoot, _sourceDir) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
parseImports(filePath, projectRoot, _sourceDir, content) {
|
|
146
|
+
if (!content) {
|
|
147
|
+
try {
|
|
148
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
152
153
|
}
|
|
153
154
|
const fileDir = path.dirname(filePath);
|
|
154
155
|
const imports = [];
|
|
155
156
|
for (const line of content.split("\n")) {
|
|
156
157
|
const trimmed = line.trim();
|
|
158
|
+
// Quick prefix guard — skip lines that can't be imports
|
|
159
|
+
if (trimmed.length === 0 || !(trimmed.charCodeAt(0) === 105 /* i */ ||
|
|
160
|
+
trimmed.charCodeAt(0) === 101 /* e */ ||
|
|
161
|
+
trimmed.charCodeAt(0) === 99 /* c */ ||
|
|
162
|
+
trimmed.charCodeAt(0) === 108 /* l */ ||
|
|
163
|
+
trimmed.charCodeAt(0) === 118 /* v */))
|
|
164
|
+
continue;
|
|
157
165
|
let importPath = null;
|
|
158
166
|
const match = trimmed.match(TS_IMPORT_RE) || trimmed.match(TS_SIDE_EFFECT_RE) || trimmed.match(JS_REQUIRE_RE);
|
|
159
167
|
if (match)
|
|
@@ -44,6 +44,14 @@ export declare class FileCache extends EventEmitter {
|
|
|
44
44
|
fileCount: number;
|
|
45
45
|
totalLines: number;
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Initialize from a pre-read content map (avoids re-reading files after graph build).
|
|
49
|
+
* Used when buildGraphAsync already read all files into memory.
|
|
50
|
+
*/
|
|
51
|
+
initializeFromContentMap(contentMap: Map<string, string>): {
|
|
52
|
+
fileCount: number;
|
|
53
|
+
totalLines: number;
|
|
54
|
+
};
|
|
47
55
|
/** Start watching source directories for changes */
|
|
48
56
|
startWatching(): void;
|
|
49
57
|
private isWatchedFile;
|
|
@@ -106,6 +106,19 @@ class FileCache extends events_1.EventEmitter {
|
|
|
106
106
|
console.error(`[syke:cache] Loaded ${this.cache.size} files (${totalLines.toLocaleString()} lines) into memory`);
|
|
107
107
|
return { fileCount: this.cache.size, totalLines };
|
|
108
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Initialize from a pre-read content map (avoids re-reading files after graph build).
|
|
111
|
+
* Used when buildGraphAsync already read all files into memory.
|
|
112
|
+
*/
|
|
113
|
+
initializeFromContentMap(contentMap) {
|
|
114
|
+
let totalLines = 0;
|
|
115
|
+
for (const [filePath, content] of contentMap) {
|
|
116
|
+
this.cache.set(path.normalize(filePath), content);
|
|
117
|
+
totalLines += content.split("\n").length;
|
|
118
|
+
}
|
|
119
|
+
console.error(`[syke:cache] Loaded ${this.cache.size} files (${totalLines.toLocaleString()} lines) from graph build (no re-read)`);
|
|
120
|
+
return { fileCount: this.cache.size, totalLines };
|
|
121
|
+
}
|
|
109
122
|
/** Start watching source directories for changes */
|
|
110
123
|
startWatching() {
|
|
111
124
|
if (this.watcher)
|
package/dist/web/public/app.js
CHANGED
|
@@ -2094,6 +2094,40 @@ function setupEventListeners() {
|
|
|
2094
2094
|
document.getElementById("shortcuts-overlay").classList.add("hidden");
|
|
2095
2095
|
});
|
|
2096
2096
|
|
|
2097
|
+
// Onboarding (Cortex-only)
|
|
2098
|
+
document.getElementById("btn-onboarding").addEventListener("click", () => {
|
|
2099
|
+
const modal = document.getElementById("cortex-modal");
|
|
2100
|
+
const planInfo = document.getElementById("cortex-plan-info");
|
|
2101
|
+
const upgradeLink = document.getElementById("cortex-upgrade-link");
|
|
2102
|
+
const plan = _currentPlan.toLowerCase();
|
|
2103
|
+
|
|
2104
|
+
if (plan === "cortex" || plan === "cortex_trial") {
|
|
2105
|
+
// Already on Cortex — trigger scan_project via MCP (show coming-soon notice for now)
|
|
2106
|
+
alert("Onboarding document generation is coming in the next update. Stay tuned!");
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// Show modal with plan-specific messaging
|
|
2111
|
+
if (plan === "pro" || plan === "pro_trial") {
|
|
2112
|
+
planInfo.textContent = "You're on Pro ($9/mo). Upgrade to Cortex for just $20/mo more.";
|
|
2113
|
+
upgradeLink.textContent = "UPGRADE TO CORTEX (+$20/mo)";
|
|
2114
|
+
} else {
|
|
2115
|
+
planInfo.textContent = "You're on the Free plan. Upgrade to Cortex to unlock AI-powered analysis.";
|
|
2116
|
+
upgradeLink.textContent = "UPGRADE TO CORTEX";
|
|
2117
|
+
}
|
|
2118
|
+
modal.classList.remove("hidden");
|
|
2119
|
+
});
|
|
2120
|
+
|
|
2121
|
+
document.getElementById("btn-close-cortex").addEventListener("click", () => {
|
|
2122
|
+
document.getElementById("cortex-modal").classList.add("hidden");
|
|
2123
|
+
});
|
|
2124
|
+
|
|
2125
|
+
document.getElementById("cortex-modal").addEventListener("click", (e) => {
|
|
2126
|
+
if (e.target.id === "cortex-modal") {
|
|
2127
|
+
document.getElementById("cortex-modal").classList.add("hidden");
|
|
2128
|
+
}
|
|
2129
|
+
});
|
|
2130
|
+
|
|
2097
2131
|
// Cycles
|
|
2098
2132
|
document.getElementById("btn-cycles").addEventListener("click", detectCycles);
|
|
2099
2133
|
document.getElementById("btn-close-cycles").addEventListener("click", () => {
|
|
@@ -3668,10 +3702,13 @@ function hideServerOffline() {
|
|
|
3668
3702
|
if (topbar) topbar.style.opacity = "1";
|
|
3669
3703
|
}
|
|
3670
3704
|
|
|
3705
|
+
let _currentPlan = "free"; // track current plan for Cortex modal
|
|
3706
|
+
|
|
3671
3707
|
async function loadProjectInfo() {
|
|
3672
3708
|
try {
|
|
3673
3709
|
const res = await fetch("/api/project-info");
|
|
3674
3710
|
const info = await res.json();
|
|
3711
|
+
_currentPlan = info.plan || "free";
|
|
3675
3712
|
const el = document.getElementById("current-project");
|
|
3676
3713
|
if (el) {
|
|
3677
3714
|
const short = info.projectRoot.length > 50
|
|
@@ -3680,6 +3717,13 @@ async function loadProjectInfo() {
|
|
|
3680
3717
|
el.textContent = short;
|
|
3681
3718
|
el.title = info.projectRoot + " | " + info.languages.join(", ") + " | " + info.fileCount + " files";
|
|
3682
3719
|
}
|
|
3720
|
+
// Display graph load time
|
|
3721
|
+
const loadTimeEl = document.getElementById("stat-load-time");
|
|
3722
|
+
if (loadTimeEl && info.graphLoadTimeMs) {
|
|
3723
|
+
const ms = info.graphLoadTimeMs;
|
|
3724
|
+
loadTimeEl.textContent = ms >= 1000 ? (ms / 1000).toFixed(1) + "s" : ms + "ms";
|
|
3725
|
+
loadTimeEl.title = "Project scan + graph build time: " + ms + "ms";
|
|
3726
|
+
}
|
|
3683
3727
|
hideServerOffline();
|
|
3684
3728
|
updateLicenseBadge(info.plan, info.expiresAt);
|
|
3685
3729
|
updateLicenseButton(info.plan);
|
|
@@ -35,11 +35,16 @@
|
|
|
35
35
|
<button id="btn-change-project" class="top-btn" title="Switch project">OPEN</button>
|
|
36
36
|
</div>
|
|
37
37
|
<div class="top-controls">
|
|
38
|
+
<button id="btn-onboarding" class="top-btn cortex-btn" title="Generate onboarding document (Cortex)">ONBOARDING</button>
|
|
38
39
|
<button id="btn-cycles" class="top-btn" title="Detect circular dependencies">CYCLES</button>
|
|
39
40
|
<button id="btn-stats" class="top-btn" title="Toggle statistics panel">STATS</button>
|
|
40
41
|
<button id="btn-shortcuts" class="top-btn" title="Keyboard shortcuts (?)">?</button>
|
|
41
42
|
</div>
|
|
42
43
|
<div class="stats" id="stats">
|
|
44
|
+
<div class="stat-block">
|
|
45
|
+
<span class="stat-label">LOADED</span>
|
|
46
|
+
<span class="stat-value" id="stat-load-time">---</span>
|
|
47
|
+
</div>
|
|
43
48
|
<div class="stat-block">
|
|
44
49
|
<span class="stat-label">NODES</span>
|
|
45
50
|
<span class="stat-value" id="stat-files">---</span>
|
|
@@ -499,6 +504,21 @@
|
|
|
499
504
|
</div>
|
|
500
505
|
</div>
|
|
501
506
|
|
|
507
|
+
<!-- Cortex Upgrade Modal -->
|
|
508
|
+
<div id="cortex-modal" class="hidden">
|
|
509
|
+
<div class="cortex-modal-panel">
|
|
510
|
+
<button id="btn-close-cortex" class="cortex-close">×</button>
|
|
511
|
+
<div class="cortex-modal-icon">🧠</div>
|
|
512
|
+
<h3 class="cortex-modal-title">CORTEX REQUIRED</h3>
|
|
513
|
+
<p class="cortex-modal-desc">
|
|
514
|
+
<strong>Onboarding Document</strong> uses AI to scan your entire project and generate a comprehensive architecture guide — key files, patterns, dependencies, and team context.
|
|
515
|
+
</p>
|
|
516
|
+
<p id="cortex-plan-info" class="cortex-plan-info"></p>
|
|
517
|
+
<a id="cortex-upgrade-link" href="https://syke.cloud/dashboard/" target="_blank" class="cortex-upgrade-btn">UPGRADE TO CORTEX</a>
|
|
518
|
+
<p class="cortex-price-note">$29/mo · $249/yr · 14-day money-back guarantee</p>
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
|
|
502
522
|
<!-- Bottom status bar -->
|
|
503
523
|
<div id="bottom-bar">
|
|
504
524
|
<span id="bottom-info">SYKE v--- · ---</span>
|
|
@@ -2628,6 +2628,111 @@ main {
|
|
|
2628
2628
|
}
|
|
2629
2629
|
|
|
2630
2630
|
/* ── Bottom status bar ── */
|
|
2631
|
+
/* ═══════════════════════════════════════════ */
|
|
2632
|
+
/* CORTEX BUTTON & MODAL */
|
|
2633
|
+
/* ═══════════════════════════════════════════ */
|
|
2634
|
+
.cortex-btn {
|
|
2635
|
+
background: linear-gradient(135deg, rgba(192, 132, 252, 0.15), rgba(0, 212, 255, 0.15)) !important;
|
|
2636
|
+
border: 1px solid rgba(192, 132, 252, 0.4) !important;
|
|
2637
|
+
color: #c084fc !important;
|
|
2638
|
+
position: relative;
|
|
2639
|
+
}
|
|
2640
|
+
.cortex-btn:hover {
|
|
2641
|
+
background: linear-gradient(135deg, rgba(192, 132, 252, 0.3), rgba(0, 212, 255, 0.3)) !important;
|
|
2642
|
+
border-color: rgba(192, 132, 252, 0.7) !important;
|
|
2643
|
+
box-shadow: 0 0 12px rgba(192, 132, 252, 0.3);
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
#cortex-modal {
|
|
2647
|
+
position: fixed;
|
|
2648
|
+
inset: 0;
|
|
2649
|
+
z-index: 9000;
|
|
2650
|
+
background: rgba(0, 0, 0, 0.7);
|
|
2651
|
+
backdrop-filter: blur(6px);
|
|
2652
|
+
display: flex;
|
|
2653
|
+
align-items: center;
|
|
2654
|
+
justify-content: center;
|
|
2655
|
+
}
|
|
2656
|
+
#cortex-modal.hidden { display: none; }
|
|
2657
|
+
|
|
2658
|
+
.cortex-modal-panel {
|
|
2659
|
+
background: linear-gradient(160deg, #0d1b2a, #132244);
|
|
2660
|
+
border: 1px solid rgba(192, 132, 252, 0.3);
|
|
2661
|
+
border-radius: 12px;
|
|
2662
|
+
padding: 32px 36px;
|
|
2663
|
+
max-width: 420px;
|
|
2664
|
+
width: 90%;
|
|
2665
|
+
text-align: center;
|
|
2666
|
+
position: relative;
|
|
2667
|
+
box-shadow: 0 0 40px rgba(192, 132, 252, 0.15);
|
|
2668
|
+
}
|
|
2669
|
+
.cortex-close {
|
|
2670
|
+
position: absolute;
|
|
2671
|
+
top: 12px;
|
|
2672
|
+
right: 16px;
|
|
2673
|
+
background: none;
|
|
2674
|
+
border: none;
|
|
2675
|
+
color: #556677;
|
|
2676
|
+
font-size: 20px;
|
|
2677
|
+
cursor: pointer;
|
|
2678
|
+
}
|
|
2679
|
+
.cortex-close:hover { color: #fff; }
|
|
2680
|
+
.cortex-modal-icon {
|
|
2681
|
+
font-size: 36px;
|
|
2682
|
+
margin-bottom: 12px;
|
|
2683
|
+
}
|
|
2684
|
+
.cortex-modal-title {
|
|
2685
|
+
font-family: 'JetBrains Mono', monospace;
|
|
2686
|
+
font-size: 16px;
|
|
2687
|
+
color: #c084fc;
|
|
2688
|
+
letter-spacing: 2px;
|
|
2689
|
+
margin-bottom: 16px;
|
|
2690
|
+
}
|
|
2691
|
+
.cortex-modal-desc {
|
|
2692
|
+
font-size: 13px;
|
|
2693
|
+
color: #8899aa;
|
|
2694
|
+
line-height: 1.6;
|
|
2695
|
+
margin-bottom: 16px;
|
|
2696
|
+
}
|
|
2697
|
+
.cortex-modal-desc strong {
|
|
2698
|
+
color: #c8d6e5;
|
|
2699
|
+
}
|
|
2700
|
+
.cortex-plan-info {
|
|
2701
|
+
font-size: 12px;
|
|
2702
|
+
color: #00d4ff;
|
|
2703
|
+
margin-bottom: 20px;
|
|
2704
|
+
font-family: 'JetBrains Mono', monospace;
|
|
2705
|
+
}
|
|
2706
|
+
.cortex-upgrade-btn {
|
|
2707
|
+
display: inline-block;
|
|
2708
|
+
padding: 10px 28px;
|
|
2709
|
+
background: linear-gradient(135deg, #c084fc, #7c3aed);
|
|
2710
|
+
color: #fff;
|
|
2711
|
+
font-family: 'JetBrains Mono', monospace;
|
|
2712
|
+
font-size: 13px;
|
|
2713
|
+
font-weight: 600;
|
|
2714
|
+
letter-spacing: 1px;
|
|
2715
|
+
border-radius: 6px;
|
|
2716
|
+
text-decoration: none;
|
|
2717
|
+
transition: all 0.2s;
|
|
2718
|
+
}
|
|
2719
|
+
.cortex-upgrade-btn:hover {
|
|
2720
|
+
background: linear-gradient(135deg, #d4a5ff, #8b5cf6);
|
|
2721
|
+
box-shadow: 0 0 20px rgba(192, 132, 252, 0.4);
|
|
2722
|
+
transform: translateY(-1px);
|
|
2723
|
+
}
|
|
2724
|
+
.cortex-price-note {
|
|
2725
|
+
font-size: 11px;
|
|
2726
|
+
color: #556677;
|
|
2727
|
+
margin-top: 12px;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
/* Load time stat */
|
|
2731
|
+
#stat-load-time {
|
|
2732
|
+
color: #30d158;
|
|
2733
|
+
font-variant-numeric: tabular-nums;
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2631
2736
|
#bottom-bar {
|
|
2632
2737
|
position: fixed;
|
|
2633
2738
|
bottom: 0;
|
package/dist/web/server.d.ts
CHANGED
package/dist/web/server.js
CHANGED
|
@@ -240,7 +240,7 @@ function acknowledgeWarnings() {
|
|
|
240
240
|
function getAllWarnings() {
|
|
241
241
|
return [...warningStore];
|
|
242
242
|
}
|
|
243
|
-
function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProjectRoot, getPackageName, getLicenseStatus, hasAIKeyFn, setLicenseKeyFn, setAIKeyFn, getAIInfoFn, setAIProviderFn) {
|
|
243
|
+
function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProjectRoot, getPackageName, getLicenseStatus, hasAIKeyFn, setLicenseKeyFn, setAIKeyFn, getAIInfoFn, setAIProviderFn, getGraphLoadTimeMs) {
|
|
244
244
|
const app = (0, express_1.default)();
|
|
245
245
|
app.use(express_1.default.json());
|
|
246
246
|
/** Check if current license is Pro (includes pro_trial) */
|
|
@@ -842,6 +842,7 @@ function createWebServer(getGraphFn, initialFileCache, switchProjectFn, getProje
|
|
|
842
842
|
aiProvider: aiInfo.activeProvider,
|
|
843
843
|
aiKeys: aiInfo.configured,
|
|
844
844
|
aiProviderForced: aiInfo.forced,
|
|
845
|
+
graphLoadTimeMs: getGraphLoadTimeMs ? getGraphLoadTimeMs() : 0,
|
|
845
846
|
});
|
|
846
847
|
});
|
|
847
848
|
// POST /api/set-license-key — Set or remove license key via dashboard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syke1/mcp-server",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.6",
|
|
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",
|