@syke1/mcp-server 1.4.12 → 1.4.14
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/ai/analyzer.js +48 -6
- package/dist/ai/provider.js +1 -1
- package/dist/ai/realtime-analyzer.js +75 -18
- package/dist/config.d.ts +5 -0
- package/dist/config.js +17 -0
- package/package.json +1 -1
package/dist/ai/analyzer.js
CHANGED
|
@@ -38,6 +38,7 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const provider_1 = require("./provider");
|
|
40
40
|
const context_extractor_1 = require("./context-extractor");
|
|
41
|
+
const config_1 = require("../config");
|
|
41
42
|
function readFileContent(filePath) {
|
|
42
43
|
try {
|
|
43
44
|
return fs.readFileSync(filePath, "utf-8");
|
|
@@ -48,7 +49,9 @@ function readFileContent(filePath) {
|
|
|
48
49
|
}
|
|
49
50
|
function buildSystemPrompt(languages) {
|
|
50
51
|
const langNames = languages.length > 0 ? languages.join("/") : "source";
|
|
51
|
-
|
|
52
|
+
const ko = (0, config_1.getLanguage)() === "ko";
|
|
53
|
+
if (ko) {
|
|
54
|
+
return `당신은 ${langNames} 코드 영향도 분석 전문가입니다.
|
|
52
55
|
주어진 파일의 소스코드와 이 파일에 의존하는 파일들의 코드를 분석하여,
|
|
53
56
|
이 파일을 수정할 때 어떤 부분이 깨질 수 있는지 구체적으로 설명해주세요.
|
|
54
57
|
|
|
@@ -66,15 +69,39 @@ function buildSystemPrompt(languages) {
|
|
|
66
69
|
이 파일을 수정할 때 주의할 점과 추천 접근법
|
|
67
70
|
|
|
68
71
|
한국어로 답변하세요. 간결하되 구체적으로 작성하세요.`;
|
|
72
|
+
}
|
|
73
|
+
return `You are an expert in ${langNames} code impact analysis.
|
|
74
|
+
Analyze the source code of the given file and its dependents to identify
|
|
75
|
+
what could break when this file is modified.
|
|
76
|
+
|
|
77
|
+
Analysis format:
|
|
78
|
+
## Core Role
|
|
79
|
+
Describe the file's role in the project in one sentence
|
|
80
|
+
|
|
81
|
+
## Risk Points on Modification
|
|
82
|
+
Specific parts that could break (include function/class names)
|
|
83
|
+
|
|
84
|
+
## Dependent File Analysis
|
|
85
|
+
How dependent files use specific parts of this file
|
|
86
|
+
|
|
87
|
+
## Safe Modification Guide
|
|
88
|
+
Precautions and recommended approaches for modifying this file
|
|
89
|
+
|
|
90
|
+
Be concise but specific.`;
|
|
69
91
|
}
|
|
70
92
|
async function analyzeWithAI(filePath, impactResult, graph) {
|
|
71
93
|
const provider = (0, provider_1.getAIProvider)();
|
|
94
|
+
const ko = (0, config_1.getLanguage)() === "ko";
|
|
72
95
|
if (!provider) {
|
|
73
|
-
return
|
|
96
|
+
return ko
|
|
97
|
+
? "AI 분석 비활성화 — GEMINI_KEY, OPENAI_KEY, 또는 ANTHROPIC_KEY를 설정하세요."
|
|
98
|
+
: "AI analysis disabled — set GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY.";
|
|
74
99
|
}
|
|
75
100
|
const targetSource = readFileContent(filePath);
|
|
76
101
|
if (!targetSource) {
|
|
77
|
-
return
|
|
102
|
+
return ko
|
|
103
|
+
? `파일을 읽을 수 없습니다: ${filePath}`
|
|
104
|
+
: `Cannot read file: ${filePath}`;
|
|
78
105
|
}
|
|
79
106
|
const codeBlockLang = graph.languages[0] || "text";
|
|
80
107
|
// Build smart context for the target file
|
|
@@ -90,7 +117,8 @@ async function analyzeWithAI(filePath, impactResult, graph) {
|
|
|
90
117
|
dependentSources.push(`### ${rel}\n\`\`\`${codeBlockLang}\n${smartDep}\n\`\`\``);
|
|
91
118
|
}
|
|
92
119
|
}
|
|
93
|
-
const userPrompt =
|
|
120
|
+
const userPrompt = ko
|
|
121
|
+
? `## 분석 대상 파일: ${impactResult.relativePath}
|
|
94
122
|
- 위험도: ${impactResult.riskLevel}
|
|
95
123
|
- 직접 의존 파일 수: ${impactResult.directDependents.length}
|
|
96
124
|
- 전이적 의존 파일 수: ${impactResult.transitiveDependents.length}
|
|
@@ -101,12 +129,26 @@ async function analyzeWithAI(filePath, impactResult, graph) {
|
|
|
101
129
|
${smartTarget}
|
|
102
130
|
\`\`\`
|
|
103
131
|
|
|
104
|
-
${dependentSources.length > 0 ? `### 이 파일에 의존하는 파일들 (상위 ${dependentSources.length}개)\n${dependentSources.join("\n\n")}` : "이 파일에 의존하는 내부 파일이 없습니다."}
|
|
132
|
+
${dependentSources.length > 0 ? `### 이 파일에 의존하는 파일들 (상위 ${dependentSources.length}개)\n${dependentSources.join("\n\n")}` : "이 파일에 의존하는 내부 파일이 없습니다."}`
|
|
133
|
+
: `## Target file: ${impactResult.relativePath}
|
|
134
|
+
- Risk level: ${impactResult.riskLevel}
|
|
135
|
+
- Direct dependents: ${impactResult.directDependents.length}
|
|
136
|
+
- Transitive dependents: ${impactResult.transitiveDependents.length}
|
|
137
|
+
- Total impacted files: ${impactResult.totalImpacted}
|
|
138
|
+
|
|
139
|
+
### Target file source code
|
|
140
|
+
\`\`\`${codeBlockLang}
|
|
141
|
+
${smartTarget}
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
${dependentSources.length > 0 ? `### Files depending on this file (top ${dependentSources.length})\n${dependentSources.join("\n\n")}` : "No internal files depend on this file."}`;
|
|
105
145
|
try {
|
|
106
146
|
const systemPrompt = buildSystemPrompt(graph.languages);
|
|
107
147
|
return await provider.analyze(systemPrompt, userPrompt);
|
|
108
148
|
}
|
|
109
149
|
catch (err) {
|
|
110
|
-
return
|
|
150
|
+
return ko
|
|
151
|
+
? `AI 분석 중 오류 발생: ${err.message || err}`
|
|
152
|
+
: `AI analysis error: ${err.message || err}`;
|
|
111
153
|
}
|
|
112
154
|
}
|
package/dist/ai/provider.js
CHANGED
|
@@ -115,7 +115,7 @@ class AnthropicProvider {
|
|
|
115
115
|
return data.content[0].text;
|
|
116
116
|
}
|
|
117
117
|
async analyzeJSON(systemPrompt, userPrompt) {
|
|
118
|
-
const text = await this.analyze(systemPrompt + "\n\
|
|
118
|
+
const text = await this.analyze(systemPrompt + "\n\nRespond with JSON only. No explanatory text, just pure JSON.", userPrompt);
|
|
119
119
|
let jsonStr = text;
|
|
120
120
|
const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
121
121
|
if (jsonMatch)
|
|
@@ -38,7 +38,11 @@ const analyze_impact_1 = require("../tools/analyze-impact");
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const provider_1 = require("./provider");
|
|
40
40
|
const context_extractor_1 = require("./context-extractor");
|
|
41
|
-
const
|
|
41
|
+
const config_1 = require("../config");
|
|
42
|
+
function getSystemPrompt() {
|
|
43
|
+
const ko = (0, config_1.getLanguage)() === "ko";
|
|
44
|
+
if (ko) {
|
|
45
|
+
return `당신은 20년 경력의 풀스택 아키텍트이자, 코드 영향도 감시 AI입니다.
|
|
42
46
|
역할: 파일이 수정/추가/삭제될 때, 빌드 전에 잠재적 오류와 연쇄 영향을 감지합니다.
|
|
43
47
|
|
|
44
48
|
분석 원칙:
|
|
@@ -51,11 +55,11 @@ const REALTIME_SYSTEM_PROMPT = `당신은 20년 경력의 풀스택 아키텍트
|
|
|
51
55
|
응답 형식 (반드시 JSON):
|
|
52
56
|
{
|
|
53
57
|
"riskLevel": "CRITICAL|HIGH|MEDIUM|LOW|SAFE",
|
|
54
|
-
"summary": "한 줄 요약
|
|
58
|
+
"summary": "한 줄 요약",
|
|
55
59
|
"brokenImports": ["깨질 수 있는 import 목록"],
|
|
56
60
|
"sideEffects": ["예상되는 부작용 목록"],
|
|
57
61
|
"warnings": ["주의사항 목록"],
|
|
58
|
-
"suggestion": "추천 조치
|
|
62
|
+
"suggestion": "추천 조치"
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
CRITICAL: 빌드 실패 확실
|
|
@@ -65,6 +69,35 @@ LOW: 사소한 영향
|
|
|
65
69
|
SAFE: 안전한 변경
|
|
66
70
|
|
|
67
71
|
JSON만 응답하세요. 설명 텍스트 없이 순수 JSON만.`;
|
|
72
|
+
}
|
|
73
|
+
return `You are a senior full-stack architect and code impact monitoring AI with 20 years of experience.
|
|
74
|
+
Role: Detect potential errors and cascading impacts before build when files are modified/added/deleted.
|
|
75
|
+
|
|
76
|
+
Analysis principles:
|
|
77
|
+
1. Broken imports/exports: Check if deleted/renamed classes/functions/variables are referenced in other files
|
|
78
|
+
2. Type mismatches: Verify parameter types and return type changes match call sites
|
|
79
|
+
3. State management cascade: Impact of Provider/Notifier changes on UI and business logic
|
|
80
|
+
4. Routing impact: Effect of route/parameter changes on navigation
|
|
81
|
+
5. Missing initialization: Whether newly added Providers are properly registered
|
|
82
|
+
|
|
83
|
+
Response format (must be JSON):
|
|
84
|
+
{
|
|
85
|
+
"riskLevel": "CRITICAL|HIGH|MEDIUM|LOW|SAFE",
|
|
86
|
+
"summary": "One-line summary",
|
|
87
|
+
"brokenImports": ["List of potentially broken imports"],
|
|
88
|
+
"sideEffects": ["List of expected side effects"],
|
|
89
|
+
"warnings": ["List of warnings"],
|
|
90
|
+
"suggestion": "Recommended action"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
CRITICAL: Build failure certain
|
|
94
|
+
HIGH: Runtime error possible
|
|
95
|
+
MEDIUM: Behavior change possible
|
|
96
|
+
LOW: Minor impact
|
|
97
|
+
SAFE: Safe change
|
|
98
|
+
|
|
99
|
+
Respond with JSON only. No explanatory text, just pure JSON.`;
|
|
100
|
+
}
|
|
68
101
|
/**
|
|
69
102
|
* Analyze a file change in real-time using the configured AI provider.
|
|
70
103
|
* Receives the diff + connected files context from memory cache.
|
|
@@ -73,6 +106,7 @@ async function analyzeChangeRealtime(change, graph, getFileContent) {
|
|
|
73
106
|
const start = Date.now();
|
|
74
107
|
const relPath = change.relativePath;
|
|
75
108
|
const codeBlockLang = graph.languages[0] || "text";
|
|
109
|
+
const ko = (0, config_1.getLanguage)() === "ko";
|
|
76
110
|
// Get impacted files from graph
|
|
77
111
|
const absPath = path.normalize(path.join(graph.sourceDir, relPath));
|
|
78
112
|
let affectedNodes = [];
|
|
@@ -96,11 +130,13 @@ async function analyzeChangeRealtime(change, graph, getFileContent) {
|
|
|
96
130
|
// Build diff summary with signature changes
|
|
97
131
|
let diffSummary = "";
|
|
98
132
|
if (change.type === "deleted") {
|
|
99
|
-
|
|
133
|
+
const label = ko ? "파일이 삭제됨. 이전 내용:" : "File deleted. Previous content:";
|
|
134
|
+
diffSummary = `${label}\n\`\`\`${codeBlockLang}\n${(change.oldContent || "").split("\n").slice(0, 40).join("\n")}\n\`\`\``;
|
|
100
135
|
}
|
|
101
136
|
else if (change.type === "added") {
|
|
102
137
|
const smartNew = (0, context_extractor_1.buildSmartContext)(change.newContent || "", codeBlockLang);
|
|
103
|
-
|
|
138
|
+
const label = ko ? "새 파일 추가됨:" : "New file added:";
|
|
139
|
+
diffSummary = `${label}\n\`\`\`${codeBlockLang}\n${smartNew}\n\`\`\``;
|
|
104
140
|
}
|
|
105
141
|
else {
|
|
106
142
|
// Modified — include signature diff
|
|
@@ -111,31 +147,40 @@ async function analyzeChangeRealtime(change, graph, getFileContent) {
|
|
|
111
147
|
return `- L${d.line}: ${d.old}`;
|
|
112
148
|
return `~ L${d.line}: ${d.old} → ${d.new}`;
|
|
113
149
|
});
|
|
114
|
-
|
|
150
|
+
const label = ko
|
|
151
|
+
? `변경된 라인 (${change.diff.length}개 중 상위 30개):`
|
|
152
|
+
: `Changed lines (top 30 of ${change.diff.length}):`;
|
|
153
|
+
diffSummary = `${label}\n\`\`\`\n${diffLines.join("\n")}\n\`\`\``;
|
|
115
154
|
// Add structural signature changes
|
|
116
155
|
if (change.oldContent && change.newContent) {
|
|
117
156
|
const sigChanges = (0, context_extractor_1.diffSignatures)(change.oldContent, change.newContent, codeBlockLang);
|
|
118
157
|
if (sigChanges.length > 0) {
|
|
119
|
-
diffSummary +=
|
|
158
|
+
diffSummary += ko
|
|
159
|
+
? "\n\n### 구조적 변경 (시그니처 비교)"
|
|
160
|
+
: "\n\n### Structural changes (signature diff)";
|
|
120
161
|
for (const sc of sigChanges) {
|
|
121
162
|
if (sc.type === "added") {
|
|
122
|
-
diffSummary += `\n+ 추가: ${sc.newSignature}`;
|
|
163
|
+
diffSummary += ko ? `\n+ 추가: ${sc.newSignature}` : `\n+ Added: ${sc.newSignature}`;
|
|
123
164
|
}
|
|
124
165
|
else if (sc.type === "removed") {
|
|
125
|
-
diffSummary += `\n- 삭제: ${sc.oldSignature}`;
|
|
166
|
+
diffSummary += ko ? `\n- 삭제: ${sc.oldSignature}` : `\n- Removed: ${sc.oldSignature}`;
|
|
126
167
|
}
|
|
127
168
|
else {
|
|
128
|
-
diffSummary +=
|
|
169
|
+
diffSummary += ko
|
|
170
|
+
? `\n~ 변경: ${sc.oldSignature}\n → ${sc.newSignature}`
|
|
171
|
+
: `\n~ Changed: ${sc.oldSignature}\n → ${sc.newSignature}`;
|
|
129
172
|
}
|
|
130
173
|
}
|
|
131
174
|
}
|
|
132
175
|
}
|
|
133
176
|
if (change.newContent) {
|
|
134
177
|
const smartNew = (0, context_extractor_1.buildSmartContext)(change.newContent, codeBlockLang);
|
|
135
|
-
|
|
178
|
+
const label2 = ko ? "전체 수정 후 파일:" : "Full file after modification:";
|
|
179
|
+
diffSummary += `\n\n${label2}\n\`\`\`${codeBlockLang}\n${smartNew}\n\`\`\``;
|
|
136
180
|
}
|
|
137
181
|
}
|
|
138
|
-
const userPrompt =
|
|
182
|
+
const userPrompt = ko
|
|
183
|
+
? `## 파일 변경 감지: ${relPath}
|
|
139
184
|
변경 유형: ${change.type.toUpperCase()}
|
|
140
185
|
프로젝트 언어: ${graph.languages.join(", ") || "unknown"}
|
|
141
186
|
영향받는 파일 수: ${affectedNodes.length}
|
|
@@ -144,20 +189,30 @@ ${diffSummary}
|
|
|
144
189
|
|
|
145
190
|
${connectedFiles.length > 0 ? `## 연결된 파일들 (${connectedFiles.length}개)\n${connectedFiles.join("\n\n")}` : "연결된 파일 없음"}
|
|
146
191
|
|
|
147
|
-
이 변경이 프로젝트에 미치는 영향을
|
|
192
|
+
이 변경이 프로젝트에 미치는 영향을 분석하세요.`
|
|
193
|
+
: `## File change detected: ${relPath}
|
|
194
|
+
Change type: ${change.type.toUpperCase()}
|
|
195
|
+
Project languages: ${graph.languages.join(", ") || "unknown"}
|
|
196
|
+
Affected files: ${affectedNodes.length}
|
|
197
|
+
|
|
198
|
+
${diffSummary}
|
|
199
|
+
|
|
200
|
+
${connectedFiles.length > 0 ? `## Connected files (${connectedFiles.length})\n${connectedFiles.join("\n\n")}` : "No connected files"}
|
|
201
|
+
|
|
202
|
+
Analyze the impact of this change on the project.`;
|
|
148
203
|
try {
|
|
149
204
|
const provider = (0, provider_1.getAIProvider)();
|
|
150
205
|
if (!provider) {
|
|
151
206
|
throw new Error("No AI provider available (set GEMINI_KEY, OPENAI_KEY, or ANTHROPIC_KEY)");
|
|
152
207
|
}
|
|
153
|
-
const parsed = await provider.analyzeJSON(
|
|
208
|
+
const parsed = await provider.analyzeJSON(getSystemPrompt(), userPrompt);
|
|
154
209
|
const analysisMs = Date.now() - start;
|
|
155
210
|
return {
|
|
156
211
|
file: relPath,
|
|
157
212
|
changeType: change.type,
|
|
158
213
|
timestamp: change.timestamp,
|
|
159
214
|
riskLevel: parsed.riskLevel || "LOW",
|
|
160
|
-
summary: parsed.summary || "분석 완료",
|
|
215
|
+
summary: parsed.summary || (ko ? "분석 완료" : "Analysis complete"),
|
|
161
216
|
brokenImports: parsed.brokenImports || [],
|
|
162
217
|
sideEffects: parsed.sideEffects || [],
|
|
163
218
|
warnings: parsed.warnings || [],
|
|
@@ -174,11 +229,13 @@ ${connectedFiles.length > 0 ? `## 연결된 파일들 (${connectedFiles.length}
|
|
|
174
229
|
changeType: change.type,
|
|
175
230
|
timestamp: change.timestamp,
|
|
176
231
|
riskLevel: affectedNodes.length >= 10 ? "HIGH" : affectedNodes.length >= 5 ? "MEDIUM" : "LOW",
|
|
177
|
-
summary:
|
|
232
|
+
summary: ko
|
|
233
|
+
? `AI 분석 실패 — 그래프 기반 분석: ${affectedNodes.length}개 파일 영향`
|
|
234
|
+
: `AI analysis failed — graph-based analysis: ${affectedNodes.length} files impacted`,
|
|
178
235
|
brokenImports: [],
|
|
179
236
|
sideEffects: [],
|
|
180
|
-
warnings: [`AI 분석 오류: ${err.message}`],
|
|
181
|
-
suggestion: "수동 확인 필요",
|
|
237
|
+
warnings: [ko ? `AI 분석 오류: ${err.message}` : `AI analysis error: ${err.message}`],
|
|
238
|
+
suggestion: ko ? "수동 확인 필요" : "Manual review required",
|
|
182
239
|
affectedNodes,
|
|
183
240
|
analysisMs,
|
|
184
241
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ interface SykeConfig {
|
|
|
4
4
|
openaiKey?: string;
|
|
5
5
|
anthropicKey?: string;
|
|
6
6
|
aiProvider?: string;
|
|
7
|
+
language?: string;
|
|
7
8
|
port?: number;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
@@ -18,6 +19,10 @@ export declare function getAllConfig(): Record<string, string | undefined>;
|
|
|
18
19
|
* Set a config value in ~/.syke/config.json
|
|
19
20
|
*/
|
|
20
21
|
export declare function setConfig(key: keyof SykeConfig, value: string | null): void;
|
|
22
|
+
/**
|
|
23
|
+
* Detect language: config > env > system locale > default "en"
|
|
24
|
+
*/
|
|
25
|
+
export declare function getLanguage(): "ko" | "en";
|
|
21
26
|
export declare const CONFIG_DIR_PATH: string;
|
|
22
27
|
export declare const CONFIG_FILE_PATH: string;
|
|
23
28
|
export {};
|
package/dist/config.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.CONFIG_FILE_PATH = exports.CONFIG_DIR_PATH = void 0;
|
|
|
37
37
|
exports.getConfig = getConfig;
|
|
38
38
|
exports.getAllConfig = getAllConfig;
|
|
39
39
|
exports.setConfig = setConfig;
|
|
40
|
+
exports.getLanguage = getLanguage;
|
|
40
41
|
/**
|
|
41
42
|
* Central config reader for SYKE MCP Server.
|
|
42
43
|
*
|
|
@@ -117,5 +118,21 @@ function setConfig(key, value) {
|
|
|
117
118
|
// ignore write errors
|
|
118
119
|
}
|
|
119
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Detect language: config > env > system locale > default "en"
|
|
123
|
+
*/
|
|
124
|
+
function getLanguage() {
|
|
125
|
+
const configured = getConfig("language", "SYKE_LANGUAGE");
|
|
126
|
+
if (configured) {
|
|
127
|
+
return configured.startsWith("ko") ? "ko" : "en";
|
|
128
|
+
}
|
|
129
|
+
// System locale detection
|
|
130
|
+
const locale = (process.env.LANG ||
|
|
131
|
+
process.env.LC_ALL ||
|
|
132
|
+
process.env.LANGUAGE ||
|
|
133
|
+
Intl.DateTimeFormat().resolvedOptions().locale ||
|
|
134
|
+
"").toLowerCase();
|
|
135
|
+
return locale.startsWith("ko") ? "ko" : "en";
|
|
136
|
+
}
|
|
120
137
|
exports.CONFIG_DIR_PATH = CONFIG_DIR;
|
|
121
138
|
exports.CONFIG_FILE_PATH = CONFIG_FILE;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syke1/mcp-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.14",
|
|
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",
|