@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.
@@ -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
- return `당신은 ${langNames} 코드 영향도 분석 전문가입니다.
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 "AI 분석 비활성화 — GEMINI_KEY, OPENAI_KEY, 또는 ANTHROPIC_KEY를 설정하세요.";
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 `파일을 읽을 수 없습니다: ${filePath}`;
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 = `## 분석 대상 파일: ${impactResult.relativePath}
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 `AI 분석 중 오류 발생: ${err.message || err}`;
150
+ return ko
151
+ ? `AI 분석 중 오류 발생: ${err.message || err}`
152
+ : `AI analysis error: ${err.message || err}`;
111
153
  }
112
154
  }
@@ -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\nJSON만 응답하세요. 설명 텍스트 없이 순수 JSON만.", userPrompt);
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 REALTIME_SYSTEM_PROMPT = `당신은 20년 경력의 풀스택 아키텍트이자, 코드 영향도 감시 AI입니다.
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
- diffSummary = `파일이 삭제됨. 이전 내용:\n\`\`\`${codeBlockLang}\n${(change.oldContent || "").split("\n").slice(0, 40).join("\n")}\n\`\`\``;
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
- diffSummary = `새 파일 추가됨:\n\`\`\`${codeBlockLang}\n${smartNew}\n\`\`\``;
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
- diffSummary = `변경된 라인 (${change.diff.length}개 중 상위 30개):\n\`\`\`\n${diffLines.join("\n")}\n\`\`\``;
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 += "\n\n### 구조적 변경 (시그니처 비교)";
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 += `\n~ 변경: ${sc.oldSignature}\n → ${sc.newSignature}`;
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
- diffSummary += `\n\n전체 수정 후 파일:\n\`\`\`${codeBlockLang}\n${smartNew}\n\`\`\``;
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 = `## 파일 변경 감지: ${relPath}
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(REALTIME_SYSTEM_PROMPT, userPrompt);
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: `AI 분석 실패 — 그래프 기반 분석: ${affectedNodes.length}개 파일 영향`,
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.12",
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",