mcp-osp-prompt 1.0.4 → 1.0.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 CHANGED
@@ -97,23 +97,6 @@ Add to your IDE's MCP configuration file (e.g., `~/.cursor/mcp.json`):
97
97
  }
98
98
  ```
99
99
 
100
- ### 2️⃣ Universal URL Parser - Handle Any Complex URL!
101
-
102
- Our advanced URL parser supports **any complex** GitLab and GitHub repository structure:
103
-
104
- #### Complex URL Examples That Work:
105
-
106
- ```bash
107
- # GitLab with multi-level projects and complex branches
108
- "https://gitlab.com/Keccac256-evg/opensocial/osp-prompt/-/tree/master/dev-arch"
109
- "https://gitlab.com/group/subgroup/project/-/tree/feature/new-ui/src/components"
110
- "https://gitlab.com/company/team/service/-/tree/v1.2.3/docs/api"
111
-
112
- # GitHub with deep nested paths and feature branches
113
- "https://github.com/feast-dev/feast/tree/master/examples/java-demo/feature_repo/data"
114
- "https://github.com/microsoft/vscode/tree/main/src/vs/editor/contrib"
115
- "https://github.com/facebook/react/tree/feature/concurrent/packages/react/src"
116
- ```
117
100
 
118
101
  #### Auto-Detection Results:
119
102
 
package/config.js CHANGED
@@ -87,7 +87,6 @@ export async function generateAutoPromptTools() {
87
87
 
88
88
  if (platform === 'local') {
89
89
  // 本地模式:扫描配置的本地目录
90
- console.log(`[Config] Using local prompt path: ${promptPath}`);
91
90
  scannedFiles = await scanPromptFiles(promptPath);
92
91
 
93
92
  // 如果本地扫描失败或没有找到文件,回退到基础工具
@@ -97,7 +96,6 @@ export async function generateAutoPromptTools() {
97
96
  }
98
97
  } else {
99
98
  // 远程模式:从GitHub或GitLab获取prompt文件列表
100
- console.log(`[Config] Using remote ${platform} prompt files from: ${promptPath}`);
101
99
  scannedFiles = await scanRemotePromptFiles();
102
100
 
103
101
  // 如果远程扫描失败或没有找到文件,回退到基础工具
@@ -119,7 +117,7 @@ export async function generateAutoPromptTools() {
119
117
  }
120
118
  }));
121
119
  } catch (error) {
122
- console.error('[Config] Auto prompt tool generation failed:', error.message);
120
+ if (CFG.debug) console.error('[Config] Auto prompt tool generation failed:', error.message);
123
121
  return getBasicPromptTools();
124
122
  }
125
123
  }
@@ -247,7 +245,6 @@ async function scanRemotePromptFiles() {
247
245
 
248
246
  if (cacheAge < CFG.ttl * 1000) {
249
247
  const cachedList = await fs.readFile(cacheFile, 'utf8');
250
- console.log(`[Config] Using cached file list (age: ${cacheAgeMinutes}min)`);
251
248
  return JSON.parse(cachedList);
252
249
  }
253
250
  } catch (_) {
@@ -257,8 +254,6 @@ async function scanRemotePromptFiles() {
257
254
 
258
255
  // 从远程获取文件列表
259
256
  try {
260
- console.log(`[Config] Fetching fresh file list from ${platform}...`);
261
-
262
257
  let scannedFiles = [];
263
258
 
264
259
  if (platform === 'github') {
@@ -269,18 +264,15 @@ async function scanRemotePromptFiles() {
269
264
 
270
265
  // 缓存结果
271
266
  await fs.writeFile(cacheFile, JSON.stringify(scannedFiles, null, 2), 'utf8');
272
- console.log(`[Config] Cached ${scannedFiles.length} files to ${cacheFile}`);
273
267
 
274
268
  return scannedFiles;
275
269
  } catch (error) {
276
270
  // API失败时尝试使用旧缓存
277
- console.warn(`[Config] Remote scanning failed: ${error.message}`);
278
271
  try {
279
272
  const cachedList = await fs.readFile(cacheFile, 'utf8');
280
- console.log(`[Config] Using stale cache due to API failure`);
281
273
  return JSON.parse(cachedList);
282
274
  } catch (_) {
283
- console.error('[Config] No cache available, returning empty list');
275
+ if (CFG.debug) console.error('[Config] No cache available, returning empty list');
284
276
  return [];
285
277
  }
286
278
  }
@@ -323,7 +315,6 @@ async function initializeTools() {
323
315
  ];
324
316
 
325
317
  const allTools = [...autoPromptTools, ...handlerTools];
326
- console.log(`[Config] Initialized ${allTools.length} tools (${autoPromptTools.length} prompt + ${handlerTools.length} handler)`);
327
318
 
328
319
  return allTools;
329
320
  }
package/dialog/browser.js CHANGED
@@ -8,6 +8,8 @@ import { BASE_DIALOG_BUTTONS, getSafeButtons, validateDialogButtons } from './co
8
8
 
9
9
  let serverPort = null;
10
10
 
11
+ const debug = process.env.DEBUG_LOG === 'true';
12
+
11
13
  async function ensureServer() {
12
14
  if (!serverPort) {
13
15
  serverPort = await startDialogServer();
@@ -18,7 +20,6 @@ async function ensureServer() {
18
20
  export async function showBrowserDialog(html) {
19
21
  // 确保HTTP服务器已启动
20
22
  const port = await ensureServer();
21
- console.log(`[Browser Dialog] Using HTTP server on port ${port}`);
22
23
 
23
24
  const tempFile = path.join(tmpdir(), `dialog_${Date.now()}.html`);
24
25
  writeFileSync(tempFile, html, 'utf8');
@@ -31,7 +32,7 @@ export async function showInputDialog(opts) {
31
32
 
32
33
  // Handle automated testing mode
33
34
  if (process.env.AUTOMATED_MODE === 'true') {
34
- console.log(`🤖 [AUTOMATED] Browser showInputDialog: ${title}, returning: "${defaultValue || ''}"`);
35
+ console.error(`🤖 [AUTOMATED] Browser showInputDialog: ${title}, returning: "${defaultValue || ''}"`);
35
36
  return defaultValue || '';
36
37
  }
37
38
 
@@ -128,7 +129,6 @@ export async function showInputDialog(opts) {
128
129
  </html>
129
130
  `;
130
131
 
131
- console.log(`[Browser Dialog] Opening input dialog - Server: http://localhost:${port}`);
132
132
  return await showBrowserDialog(html);
133
133
  }
134
134
 
@@ -145,7 +145,7 @@ export async function showConfirmDialog(opts) {
145
145
  if (!validateDialogButtons(finalButtons)) {
146
146
  console.warn(`⚠️ [AUTOMATED] Browser dialog missing '修改计划' option:`, finalButtons);
147
147
  }
148
- console.log(`🤖 [AUTOMATED] Browser showConfirmDialog: ${title}, returning: "${finalButtons[0]}" (buttons: ${JSON.stringify(finalButtons)})`);
148
+ console.error(`🤖 [AUTOMATED] Browser showConfirmDialog: ${title}, returning: "${finalButtons[0]}" (buttons: ${JSON.stringify(finalButtons)})`);
149
149
  return finalButtons[0];
150
150
  }
151
151
 
@@ -231,7 +231,6 @@ export async function showConfirmDialog(opts) {
231
231
  </html>
232
232
  `;
233
233
 
234
- console.log(`[Browser Dialog] Opening confirm dialog - Server: http://localhost:${port}`);
235
234
  return await showBrowserDialog(html);
236
235
  }
237
236
 
@@ -252,7 +251,7 @@ export async function showPlanAdjustmentDialog(opts) {
252
251
  if (!validateDialogButtons(finalOptions)) {
253
252
  console.warn(`⚠️ [AUTOMATED] Browser plan dialog missing '修改计划' option:`, finalOptions);
254
253
  }
255
- console.log(`🤖 [AUTOMATED] Browser showPlanAdjustmentDialog: ${title}, returning: "${finalOptions[0]}" (options: ${JSON.stringify(finalOptions)})`);
254
+ console.error(`🤖 [AUTOMATED] Browser showPlanAdjustmentDialog: ${title}, returning: "${finalOptions[0]}" (options: ${JSON.stringify(finalOptions)})`);
256
255
  return finalOptions[0]; // Return first option
257
256
  }
258
257
 
@@ -420,7 +419,6 @@ export async function showPlanAdjustmentDialog(opts) {
420
419
  </html>
421
420
  `;
422
421
 
423
- console.log(`[Browser Dialog] Opening plan adjustment dialog - Server: http://localhost:${port}`);
424
422
  const result = await showBrowserDialog(html);
425
423
 
426
424
  // Try to parse as JSON for input responses
@@ -441,7 +439,7 @@ export async function showSelectDialog(opts) {
441
439
 
442
440
  // Handle automated testing mode
443
441
  if (process.env.AUTOMATED_MODE === 'true') {
444
- console.log(`🤖 [AUTOMATED] Browser showSelectDialog: ${title}, returning: "${items[0] || ''}"`);
442
+ console.error(`🤖 [AUTOMATED] Browser showSelectDialog: ${title}, returning: "${items[0] || ''}"`);
445
443
  return items[0] || '';
446
444
  }
447
445
 
@@ -527,6 +525,5 @@ export async function showSelectDialog(opts) {
527
525
  </html>
528
526
  `;
529
527
 
530
- console.log(`[Browser Dialog] Opening select dialog - Server: http://localhost:${port}`);
531
528
  return await showBrowserDialog(html);
532
529
  }
@@ -1,13 +1,14 @@
1
1
  import http from 'http';
2
2
  import url from 'url';
3
3
 
4
+ const debug = process.env.DEBUG_LOG === 'true';
5
+
4
6
  let resolverQueue = [];
5
7
  let server = null;
6
8
  let serverPort = null;
7
9
 
8
10
  export function startDialogServer() {
9
11
  if (server && serverPort) {
10
- console.log(`[Dialog Server] Reusing existing server on port ${serverPort}`);
11
12
  return Promise.resolve(serverPort);
12
13
  }
13
14
 
@@ -28,7 +29,6 @@ export function startDialogServer() {
28
29
 
29
30
  if (parsedUrl.pathname === '/submit') {
30
31
  const response = parsedUrl.query.text || '';
31
- console.log(`[Dialog Server] Received response: "${response}"`);
32
32
 
33
33
  // 设置CORS头并返回成功响应
34
34
  res.writeHead(200, {
@@ -40,16 +40,12 @@ export function startDialogServer() {
40
40
  // 解决最新的Promise
41
41
  if (resolverQueue.length > 0) {
42
42
  const resolver = resolverQueue.shift(); // 取出最早的resolver
43
- console.log(`[Dialog Server] Resolving promise with response: "${response}" (${resolverQueue.length} remaining)`);
44
43
 
45
44
  try {
46
45
  resolver(response);
47
- console.log('[Dialog Server] Promise resolved successfully');
48
46
  } catch (error) {
49
- console.log('[Dialog Server] Error resolving promise:', error);
47
+ console.error('[Dialog Server] Error resolving promise:', error);
50
48
  }
51
- } else {
52
- console.log('[Dialog Server] Warning: No resolver waiting for response');
53
49
  }
54
50
  } else {
55
51
  res.writeHead(404);
@@ -59,7 +55,6 @@ export function startDialogServer() {
59
55
 
60
56
  server.listen(0, 'localhost', () => {
61
57
  serverPort = server.address().port;
62
- console.log(`[Dialog Server] Started on http://localhost:${serverPort}`);
63
58
  resolve(serverPort);
64
59
  });
65
60
  });
@@ -67,18 +62,16 @@ export function startDialogServer() {
67
62
 
68
63
  export function waitForResponse() {
69
64
  return new Promise((resolve) => {
70
- console.log(`[Dialog Server] Adding resolver to queue (current queue size: ${resolverQueue.length})`);
71
65
  resolverQueue.push(resolve);
72
66
 
73
- // 30秒超时
67
+ // 60分钟超时
74
68
  setTimeout(() => {
75
69
  const index = resolverQueue.indexOf(resolve);
76
70
  if (index !== -1) {
77
- console.log('[Dialog Server] Response timeout (30s), removing resolver from queue');
78
71
  resolverQueue.splice(index, 1);
79
72
  resolve('');
80
73
  }
81
- }, 30000);
74
+ }, 3600000);
82
75
  });
83
76
  }
84
77
 
@@ -90,6 +83,5 @@ export function stopDialogServer() {
90
83
  // 清理所有pending resolvers
91
84
  resolverQueue.forEach(resolver => resolver(''));
92
85
  resolverQueue = [];
93
- console.log('[Dialog Server] Stopped');
94
86
  }
95
87
  }
package/dialog/native.js CHANGED
@@ -6,27 +6,9 @@ import { escapeAppleScriptString } from './utils.js';
6
6
  // 使用 promisify 将 exec 转为 Promise 版本,避免阻塞事件循环
7
7
  const execAsync = promisify(exec);
8
8
 
9
- // 🔧 JSON-RPC 心跳函数:通过 stdout 发送 notification,防止 MCP 客户端超时
10
- let heartbeatCounter = 0;
11
- function sendHeartbeat(context = 'dialog') {
12
- heartbeatCounter++;
13
-
14
- // 尝试使用 MCP 标准的 progress notification 格式
15
- const notification = {
16
- jsonrpc: '2.0',
17
- method: 'notifications/progress',
18
- params: {
19
- progressToken: `heartbeat-${Date.now()}`,
20
- progress: heartbeatCounter,
21
- total: -1 // -1 表示未知总数
22
- }
23
- };
24
-
25
- // 写入 stdout,这是 MCP 客户端监听的通道
26
- process.stdout.write(JSON.stringify(notification) + '\n');
27
- // 同时写入 stderr 供调试(只保留一行日志)
28
- console.error(`[Heartbeat #${heartbeatCounter}] progress notification sent`);
29
- }
9
+ // 调试模式标志
10
+ const debug = process.env.DEBUG_LOG === 'true';
11
+
30
12
  // 🟢 GREEN: Task 2.2 - 导入CFG配置用于native弹窗宽度设置
31
13
  import { CFG } from '../config.js';
32
14
  // ✅ UNIFIED: 导入统一的按钮管理常量
@@ -35,7 +17,7 @@ import { BASE_DIALOG_BUTTONS, getSafeButtons, validateDialogButtons } from './co
35
17
  export async function showInputDialog({ title = '输入', message = '请输入内容:', defaultValue = '' }) {
36
18
  // Handle automated testing mode - return default value without showing dialog
37
19
  if (process.env.AUTOMATED_MODE === 'true') {
38
- console.log(`🤖 [AUTOMATED] showInputDialog: ${title}, returning: "${defaultValue}"`);
20
+ console.error(`🤖 [AUTOMATED] showInputDialog: ${title}, returning: "${defaultValue}"`);
39
21
  return defaultValue;
40
22
  }
41
23
 
@@ -89,23 +71,14 @@ export async function showInputDialog({ title = '输入', message = '请输入
89
71
  end if
90
72
  `;
91
73
 
92
- // 🆕 添加保活机制(5秒间隔,通过 stdout 发送 JSON-RPC notification)
93
- const keepAliveInterval = setInterval(() => {
94
- sendHeartbeat('input');
95
- }, 5000);
96
-
97
74
  try {
98
- console.error('[Dialog] Calling osascript for input dialog with async exec (non-blocking)...');
99
-
100
- // 🔧 关键修改:使用异步 exec 替代 execSync,避免阻塞事件循环
75
+ // 🔧 使用异步 exec,避免阻塞事件循环
101
76
  const { stdout } = await execAsync(`osascript -e '${applescript}'`, {
102
77
  encoding: 'utf8',
103
- timeout: 1800000 // 30 minutes timeout
78
+ timeout: 3600000 // 60 minutes timeout
104
79
  });
105
80
 
106
81
  const result = stdout.trim();
107
-
108
- clearInterval(keepAliveInterval); // 清理定时器
109
82
 
110
83
  // Clean up the result - remove the placeholder text we added
111
84
  let cleanedResult = result;
@@ -116,7 +89,6 @@ export async function showInputDialog({ title = '输入', message = '请输入
116
89
  return cleanedResult;
117
90
 
118
91
  } catch (error) {
119
- clearInterval(keepAliveInterval); // 出错时也要清理定时器
120
92
  // 更精确的用户取消检测
121
93
  const isUserCancelled =
122
94
  error.message && (
@@ -128,11 +100,9 @@ export async function showInputDialog({ title = '输入', message = '请输入
128
100
  );
129
101
 
130
102
  if (isUserCancelled) {
131
- console.error(`[Native Dialog] 👤 User cancelled textarea dialog`);
132
103
  return defaultValue;
133
104
  }
134
105
 
135
- console.error(`[Native Dialog] 💥 Textarea system error: ${error.message}`);
136
106
  return defaultValue;
137
107
  }
138
108
 
@@ -152,12 +122,12 @@ export async function showInputDialog({ title = '输入', message = '请输入
152
122
  // Special handling for Linux multiline with default value
153
123
  result = execSync(`echo "${expandedDefaultText || defaultValue}" | "${command}" ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(' ')}`, {
154
124
  encoding: 'utf8',
155
- timeout: 30000
125
+ timeout: 3600000 // 60 minutes timeout
156
126
  });
157
127
  } else {
158
128
  result = execSync(`"${command}" ${args.map(a => `"${a.replace(/"/g, '\\"')}"`).join(' ')}`, {
159
129
  encoding: 'utf8',
160
- timeout: 30000
130
+ timeout: 3600000 // 60 minutes timeout
161
131
  });
162
132
  }
163
133
 
@@ -170,7 +140,6 @@ export async function showInputDialog({ title = '输入', message = '请输入
170
140
  return cleanedResult;
171
141
  }
172
142
  } catch (error) {
173
- console.error('❌ Dialog error:', error.message);
174
143
  return defaultValue;
175
144
  }
176
145
  }
@@ -184,17 +153,12 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
184
153
  if (!validateDialogButtons(buttons)) {
185
154
  console.warn(`⚠️ [AUTOMATED] Native dialog missing '修改计划' option:`, buttons);
186
155
  }
187
- console.log(`🤖 [AUTOMATED] Native showConfirmDialog: ${title}, returning: "${buttons[0]}" (buttons: ${JSON.stringify(buttons)})`);
156
+ console.error(`🤖 [AUTOMATED] Native showConfirmDialog: ${title}, returning: "${buttons[0]}" (buttons: ${JSON.stringify(buttons)})`);
188
157
  return buttons[0];
189
158
  }
190
159
 
191
160
  const platform = os.platform();
192
161
 
193
- // 🆕 添加保活机制(5秒间隔,通过 stdout 发送 JSON-RPC notification)
194
- const keepAliveInterval = setInterval(() => {
195
- sendHeartbeat('button selection');
196
- }, 5000);
197
-
198
162
  try {
199
163
  let result;
200
164
  if (platform === 'darwin') {
@@ -214,16 +178,12 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
214
178
  const buttonsList = safeButtons.map(b => `"${b}"`).join(', ');
215
179
  const script = `set selectedButton to button returned of (display dialog "${safeMessage}" with title "${safeTitle}" buttons {${buttonsList}} default button "${safeButtons[0]}")`;
216
180
 
217
- console.error('[Dialog] Calling osascript with async exec (non-blocking)...');
218
-
219
- // 🔧 关键修改:使用异步 exec 替代 execSync,避免阻塞事件循环
181
+ // 使用异步 exec,避免阻塞事件循环
220
182
  const { stdout } = await execAsync(`osascript -e '${script}'`, {
221
183
  encoding: 'utf8',
222
184
  timeout: 3600000 // 60 minutes timeout
223
185
  });
224
186
 
225
- clearInterval(keepAliveInterval); // 清理定时器
226
-
227
187
  const selectedButton = stdout.trim();
228
188
 
229
189
  // Map safe button back to original button
@@ -231,18 +191,14 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
231
191
  return safeIndex >= 0 ? buttons[safeIndex] : buttons[0];
232
192
 
233
193
  } else {
234
- // Linux zenity - 也改为异步
235
- console.log('🐧 [ZENITY] Showing dialog...');
194
+ // Linux zenity
236
195
  const { stdout } = await execAsync(`zenity --question --title="${title}" --text="${message}" --ok-label="${buttons[0]}" --cancel-label="${buttons[1]}"`, {
237
196
  encoding: 'utf8',
238
197
  timeout: 3600000 // 60 minutes timeout
239
198
  });
240
- clearInterval(keepAliveInterval); // 清理定时器
241
199
  return stdout.trim() || buttons[0];
242
200
  }
243
201
  } catch (error) {
244
- clearInterval(keepAliveInterval); // 出错时也要清理
245
- console.error('❌ [DIALOG ERROR]:', error.message);
246
202
  // Throw a specific error that indicates dialog system failure
247
203
  throw new Error(`DIALOG_SYSTEM_FAILURE: ${error.message}`);
248
204
  }
@@ -263,7 +219,7 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
263
219
  if (!validateDialogButtons(options)) {
264
220
  console.warn(`⚠️ [AUTOMATED] Native plan dialog missing '修改计划' option:`, options);
265
221
  }
266
- console.log(`🤖 [AUTOMATED] Native showPlanAdjustmentDialog: ${title}, returning: "${options[0]}" (options: ${JSON.stringify(options)})`);
222
+ console.error(`🤖 [AUTOMATED] Native showPlanAdjustmentDialog: ${title}, returning: "${options[0]}" (options: ${JSON.stringify(options)})`);
267
223
  return options[0]; // Return first option
268
224
  }
269
225
 
@@ -300,7 +256,7 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
300
256
  ];
301
257
 
302
258
  const script = scriptParts.join(' ');
303
- result = execSync(`osascript -e '${script}'`, { encoding: 'utf8', timeout: 30000 }).trim();
259
+ result = execSync(`osascript -e '${script}'`, { encoding: 'utf8', timeout: 3600000 }).trim(); // 60 minutes timeout
304
260
 
305
261
  // If "修改计划" is selected, show input dialog using improved showInputDialog
306
262
  if (result.includes('修改计划')) {
@@ -319,11 +275,11 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
319
275
 
320
276
  } else {
321
277
  // Linux fallback using zenity
322
- result = execSync(`zenity --list --title="${title}" --text="${fullMessage}" --column=Options ${options.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 30000 }).trim();
278
+ result = execSync(`zenity --list --title="${title}" --text="${fullMessage}" --column=Options ${options.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 3600000 }).trim(); // 60 minutes timeout
323
279
 
324
280
  // Handle input for adjustment if needed
325
281
  if (result.includes('修改计划')) {
326
- const inputValue = execSync(`zenity --text-info --editable --title="计划修改建议" --text="请输入您的调整建议:"`, { encoding: 'utf8', timeout: 300000 }).trim();
282
+ const inputValue = execSync(`zenity --text-info --editable --title="计划修改建议" --text="请输入您的调整建议:"`, { encoding: 'utf8', timeout: 3600000 }).trim(); // 60 minutes timeout
327
283
 
328
284
  return {
329
285
  action: result,
@@ -348,12 +304,12 @@ export async function showSelectDialog({ title = '选择', message = '请选择'
348
304
  const safeTitle = escapeAppleScriptString(title);
349
305
  const safeMessage = escapeAppleScriptString(message);
350
306
  const script = `set theChoice to choose from list {${safeItems}} with title \"${safeTitle}\" with prompt \"${safeMessage}\"`;
351
- const result = execSync(`osascript -e "${script}"`, { encoding: 'utf8', timeout: 30000 });
307
+ const result = execSync(`osascript -e "${script}"`, { encoding: 'utf8', timeout: 3600000 }); // 60 minutes timeout
352
308
  return result.trim();
353
309
 
354
310
  } else {
355
311
  // linux zenity list
356
- const result = execSync(`zenity --list --title="${title}" --column=Options ${items.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 30000 });
312
+ const result = execSync(`zenity --list --title="${title}" --column=Options ${items.map(i=>`\"${i}\"`).join(' ')}`, { encoding: 'utf8', timeout: 3600000 }); // 60 minutes timeout
357
313
  return result.trim();
358
314
  }
359
315
  } catch (_) {
package/dialog/objc.js CHANGED
@@ -9,8 +9,8 @@ import { BASE_DIALOG_BUTTONS, getSafeButtons, validateDialogButtons } from './co
9
9
  * Uses NSTextView for true multi-line text input with no external dependencies
10
10
  */
11
11
 
12
- // Default timeout for all dialogs (5 minutes)
13
- const DEFAULT_TIMEOUT = 300000;
12
+ // Default timeout for all dialogs (60 minutes)
13
+ const DEFAULT_TIMEOUT = 3600000;
14
14
 
15
15
  /**
16
16
  * Execute AppleScript using a temporary file to handle multiline scripts properly
@@ -38,7 +38,7 @@ async function executeAppleScript(script, timeout = DEFAULT_TIMEOUT) {
38
38
  export async function showInputDialog({ title = '输入', message = '请输入内容:', defaultValue = '' }) {
39
39
  // Handle automated testing mode
40
40
  if (process.env.AUTOMATED_MODE === 'true') {
41
- console.log(`🤖 [AUTOMATED] AppleScriptObjC showInputDialog: ${title}, returning: "${defaultValue}"`);
41
+ console.error(`🤖 [AUTOMATED] AppleScriptObjC showInputDialog: ${title}, returning: "${defaultValue}"`);
42
42
  return defaultValue;
43
43
  }
44
44
 
@@ -48,8 +48,6 @@ export async function showInputDialog({ title = '输入', message = '请输入
48
48
  }
49
49
 
50
50
  try {
51
- console.log('✨ [AppleScriptObjC] Showing multi-line capable input dialog...');
52
-
53
51
  // For AppleScriptObjC, minimal escaping to avoid syntax errors
54
52
  const safeTitle = title.replace(/"/g, '""');
55
53
  const safeMessage = message.replace(/"/g, '""');
@@ -113,7 +111,7 @@ return resultText`;
113
111
  if (errorMsg.includes('timeout')) {
114
112
  console.warn(`WARNING: AppleScriptObjC input dialog timed out after ${DEFAULT_TIMEOUT/1000} seconds. Returning default value.`);
115
113
  } else if (errorMsg.includes('User canceled')) {
116
- console.log(`INFO: User cancelled AppleScriptObjC input dialog.`);
114
+ // User cancelled - this is expected behavior
117
115
  } else {
118
116
  console.warn(`WARNING: AppleScriptObjC input dialog failed: ${errorMsg}. Returning default value.`);
119
117
  }
@@ -131,7 +129,7 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
131
129
  if (!validateDialogButtons(buttons)) {
132
130
  console.warn(`⚠️ [AUTOMATED] AppleScriptObjC dialog missing '修改计划' option:`, buttons);
133
131
  }
134
- console.log(`🤖 [AUTOMATED] AppleScriptObjC showConfirmDialog: ${title}, returning: "${buttons[0]}" (buttons: ${JSON.stringify(buttons)})`);
132
+ console.error(`🤖 [AUTOMATED] AppleScriptObjC showConfirmDialog: ${title}, returning: "${buttons[0]}" (buttons: ${JSON.stringify(buttons)})`);
135
133
  return buttons[0];
136
134
  }
137
135
 
@@ -141,8 +139,6 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
141
139
  }
142
140
 
143
141
  try {
144
- console.log('✨ [AppleScriptObjC] Showing confirmation dialog...');
145
-
146
142
  // Use safe string processing
147
143
  const safeTitle = cleanAppleScriptString(title) || 'Confirmation';
148
144
  const safeMessage = cleanAppleScriptString(message);
@@ -154,7 +150,6 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
154
150
  const result = await executeAppleScript(script, DEFAULT_TIMEOUT);
155
151
 
156
152
  const selectedButton = result.trim();
157
- console.log('✅ [AppleScriptObjC] User selected:', selectedButton);
158
153
 
159
154
  // Map clean button back to original button
160
155
  const cleanIndex = safeButtons.indexOf(selectedButton);
@@ -165,9 +160,7 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
165
160
  if (errorMsg.includes('timeout')) {
166
161
  console.warn(`WARNING: AppleScriptObjC confirm dialog timed out after ${DEFAULT_TIMEOUT/1000} seconds. Returning first button.`);
167
162
  } else if (errorMsg.includes('User canceled')) {
168
- console.log(`INFO: User cancelled AppleScriptObjC confirm dialog.`);
169
- } else {
170
- console.warn(`WARNING: AppleScriptObjC confirm dialog failed: ${errorMsg}. Returning first button.`);
163
+ // User cancelled - this is expected behavior
171
164
  }
172
165
  return buttons[0];
173
166
  }
@@ -175,7 +168,7 @@ export async function showConfirmDialog({ title = '确认', message = '请选择
175
168
 
176
169
  export async function showSelectDialog({ title = '选择', message = '请选择', items = [] }) {
177
170
  if (process.env.AUTOMATED_MODE === 'true') {
178
- console.log(`🤖 [AUTOMATED] AppleScriptObjC showSelectDialog: ${title}, returning: "${items[0] || ''}"`);
171
+ console.error(`🤖 [AUTOMATED] AppleScriptObjC showSelectDialog: ${title}, returning: "${items[0] || ''}"`);
179
172
  return items[0] || '';
180
173
  }
181
174
 
@@ -187,8 +180,6 @@ export async function showSelectDialog({ title = '选择', message = '请选择'
187
180
  if (!items.length) return '';
188
181
 
189
182
  try {
190
- console.log('✨ [AppleScriptObjC] Showing select dialog...');
191
-
192
183
  // Use safe string processing
193
184
  const safeTitle = cleanAppleScriptString(title);
194
185
  const safeMessage = cleanAppleScriptString(message);
@@ -219,13 +210,10 @@ end if`;
219
210
  if (errorMsg.includes('timeout')) {
220
211
  console.warn(`WARNING: AppleScriptObjC select dialog timed out after ${DEFAULT_TIMEOUT/1000} seconds.`);
221
212
  } else if (errorMsg.includes('User canceled')) {
222
- console.log('INFO: User cancelled AppleScriptObjC select dialog.');
223
- } else {
224
- console.warn(`WARNING: AppleScriptObjC select dialog failed: ${errorMsg}.`);
213
+ // User cancelled - this is expected behavior
225
214
  }
226
215
 
227
- // Enhanced fallback to input dialog
228
- console.log('🔄 Falling back to input dialog...');
216
+ // Fallback to input dialog
229
217
  return await showInputDialog({ title, message: `${message}\n输入选项:`, defaultValue: items[0] || '' });
230
218
  }
231
219
  }
@@ -244,7 +232,7 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
244
232
  if (!validateDialogButtons(options)) {
245
233
  console.warn(`⚠️ [AUTOMATED] AppleScriptObjC plan dialog missing '修改计划' option:`, options);
246
234
  }
247
- console.log(`🤖 [AUTOMATED] AppleScriptObjC showPlanAdjustmentDialog: ${title}, returning: "${options[0]}" (options: ${JSON.stringify(options)})`);
235
+ console.error(`🤖 [AUTOMATED] AppleScriptObjC showPlanAdjustmentDialog: ${title}, returning: "${options[0]}" (options: ${JSON.stringify(options)})`);
248
236
  return options[0]; // Return first option
249
237
  }
250
238
 
@@ -260,8 +248,6 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
260
248
  }
261
249
 
262
250
  try {
263
- console.log('✨ [AppleScriptObjC] Showing plan adjustment dialog...');
264
-
265
251
  // Use safe string processing
266
252
  const safeTitle = cleanAppleScriptString(title);
267
253
  const safeMessage = escapeAppleScriptString(fullMessage);
@@ -294,9 +280,7 @@ export async function showPlanAdjustmentDialog({ title = '', message = '', curre
294
280
  if (errorMsg.includes('timeout')) {
295
281
  console.warn(`WARNING: AppleScriptObjC plan adjustment dialog timed out after ${DEFAULT_TIMEOUT/1000} seconds. Returning first option.`);
296
282
  } else if (errorMsg.includes('User canceled')) {
297
- console.log('INFO: User cancelled AppleScriptObjC plan adjustment dialog.');
298
- } else {
299
- console.warn(`WARNING: AppleScriptObjC plan adjustment dialog failed: ${errorMsg}. Returning first option.`);
283
+ // User cancelled - this is expected behavior
300
284
  }
301
285
  return options[0];
302
286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-osp-prompt",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "MCP server for fetching and caching prompt templates from GitHub, GitLab, or local directories",
5
5
  "main": "server.js",
6
6
  "bin": {
package/prompt-manager.js CHANGED
@@ -2,6 +2,9 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { detectPlatformFromPath, parseRemotePath, createAuthHeaders } from './platform-utils.js';
4
4
 
5
+ // 调试模式标志
6
+ const debug = process.env.DEBUG_LOG === 'true';
7
+
5
8
  /**
6
9
  * Prompt管理器
7
10
  * 职责:
@@ -30,7 +33,7 @@ class PromptManager {
30
33
  return this.toolsConfig;
31
34
  }
32
35
 
33
- console.error('[PromptManager] Starting synchronous initialization...');
36
+ if (debug) console.error('[PromptManager] Starting synchronous initialization...');
34
37
 
35
38
  // 🟢 GREEN: Task 1.1 - 确保promptPath正确初始化
36
39
  this.promptPath = process.env.PROMPT_PATH;
@@ -39,7 +42,7 @@ class PromptManager {
39
42
  }
40
43
 
41
44
  this.platform = detectPlatformFromPath(this.promptPath);
42
- console.error(`[PromptManager] Platform: ${this.platform}, Path: ${this.promptPath}`);
45
+ if (debug) console.error(`[PromptManager] Platform: ${this.platform}, Path: ${this.promptPath}`);
43
46
 
44
47
  // 确保缓存目录存在
45
48
  await fs.mkdir(this.cacheDir, { recursive: true });
@@ -52,7 +55,7 @@ class PromptManager {
52
55
  this.validateToolsConfigExists();
53
56
 
54
57
  this.isInitialized = true;
55
- console.error(`[PromptManager] Initialization complete: ${this.toolsConfig.length} tools ready`);
58
+ if (debug) console.error(`[PromptManager] Initialization complete: ${this.toolsConfig.length} tools ready`);
56
59
 
57
60
  return this.toolsConfig;
58
61
  }
@@ -86,7 +89,7 @@ class PromptManager {
86
89
  };
87
90
  });
88
91
 
89
- console.error(`[PromptManager] Local scan: ${promptFiles.length} files found`);
92
+ if (debug) console.error(`[PromptManager] Local scan: ${promptFiles.length} files found`);
90
93
  return promptFiles;
91
94
  } catch (error) {
92
95
  throw new Error(`Failed to scan local prompt directory: ${error.message}`);
@@ -111,7 +114,7 @@ class PromptManager {
111
114
  const cachedList = JSON.parse(await fs.readFile(fileListCache, 'utf8'));
112
115
 
113
116
  // 🔧 关键修正:启动时不区分缓存新鲜度,只要有缓存就立即启动+异步更新
114
- console.error(`[PromptManager] Using cached file list for startup (age: ${cacheAgeMinutes}min), scheduling async update`);
117
+ if (debug) console.error(`[PromptManager] Using cached file list for startup (age: ${cacheAgeMinutes}min), scheduling async update`);
115
118
 
116
119
  // 启动阶段总是异步更新,确保缓存最新
117
120
  this.asyncUpdateFileListCache().catch(error => {
@@ -121,7 +124,7 @@ class PromptManager {
121
124
  return cachedList;
122
125
  } catch (_) {
123
126
  // 缓存不存在,必须同步获取
124
- console.error('[PromptManager] No cache found, fetching synchronously for startup');
127
+ if (debug) console.error('[PromptManager] No cache found, fetching synchronously for startup');
125
128
  }
126
129
  }
127
130
 
@@ -135,7 +138,7 @@ class PromptManager {
135
138
  async fetchRemoteFileListSync() {
136
139
  const fileListCache = path.join(this.cacheDir, 'file-list.json');
137
140
 
138
- console.error('[PromptManager] Fetching remote file list synchronously...');
141
+ if (debug) console.error('[PromptManager] Fetching remote file list synchronously...');
139
142
  const pathInfo = parseRemotePath(this.promptPath);
140
143
  if (!pathInfo) {
141
144
  throw new Error(`Unable to parse PROMPT_PATH: ${this.promptPath}`);
@@ -153,7 +156,7 @@ class PromptManager {
153
156
 
154
157
  // 缓存文件列表
155
158
  await fs.writeFile(fileListCache, JSON.stringify(promptFiles, null, 2), 'utf8');
156
- console.error(`[PromptManager] Remote scan: ${promptFiles.length} files cached`);
159
+ if (debug) console.error(`[PromptManager] Remote scan: ${promptFiles.length} files cached`);
157
160
 
158
161
  return promptFiles;
159
162
  } catch (error) {
@@ -173,12 +176,12 @@ class PromptManager {
173
176
  */
174
177
  async asyncUpdateFileListCache() {
175
178
  try {
176
- console.error('[PromptManager] Starting async file list update...');
179
+ if (debug) console.error('[PromptManager] Starting async file list update...');
177
180
  const promptFiles = await this.fetchRemoteFileListSync();
178
- console.error(`[PromptManager] Async file list update completed: ${promptFiles.length} files`);
181
+ if (debug) console.error(`[PromptManager] Async file list update completed: ${promptFiles.length} files`);
179
182
  return promptFiles;
180
183
  } catch (error) {
181
- console.warn(`[PromptManager] ⚠️ Async file list update failed: ${error.message}`);
184
+ if (debug) console.error(`[PromptManager] Async file list update failed: ${error.message}`);
182
185
  throw error;
183
186
  }
184
187
  }
@@ -188,7 +191,7 @@ class PromptManager {
188
191
  */
189
192
  async fetchGitLabFileList(pathInfo) {
190
193
  const headers = createAuthHeaders('gitlab', process.env.GIT_TOKEN);
191
- console.error(`[PromptManager] GitLab API call: ${pathInfo.apiUrl}`);
194
+ if (debug) console.error(`[PromptManager] GitLab API call: ${pathInfo.apiUrl}`);
192
195
  const requestOptions = { headers, timeout: 10000 };
193
196
  const response = await fetch(pathInfo.apiUrl, requestOptions);
194
197
 
@@ -227,7 +230,7 @@ class PromptManager {
227
230
  async fetchGitHubFileList(pathInfo) {
228
231
  const url = `${pathInfo.apiUrl}?ref=${pathInfo.branch}`;
229
232
  const headers = createAuthHeaders('github', process.env.GIT_TOKEN);
230
- console.error(`[PromptManager] GitHub API call: ${url}`);
233
+ if (debug) console.error(`[PromptManager] GitHub API call: ${url}`);
231
234
  const requestOptions = { headers, timeout: 10000 };
232
235
  const response = await fetch(url, requestOptions);
233
236
 
@@ -339,8 +342,8 @@ class PromptManager {
339
342
  throw new Error('No prompt tools configured after initialization');
340
343
  }
341
344
 
342
- console.error(`[PromptManager] Tools configured: ${promptTools.length} prompt tools available`);
343
- console.error(`[PromptManager] Available tools: ${promptTools.map(t => t.name).join(', ')}`);
345
+ if (debug) console.error(`[PromptManager] Tools configured: ${promptTools.length} prompt tools available`);
346
+ if (debug) console.error(`[PromptManager] Available tools: ${promptTools.map(t => t.name).join(', ')}`);
344
347
  }
345
348
 
346
349
  /**
@@ -360,11 +363,11 @@ class PromptManager {
360
363
 
361
364
  if (cacheAge < this.ttl && !this.force) {
362
365
  // 🟢 缓存新鲜:直接返回,不进行异步更新
363
- console.error(`[PromptManager] Using fresh cached ${fileName} (age: ${Math.round(cacheAge / 1000 / 60)}min)`);
366
+ if (debug) console.error(`[PromptManager] Using fresh cached ${fileName} (age: ${Math.round(cacheAge / 1000 / 60)}min)`);
364
367
  return content;
365
368
  } else {
366
369
  // 🟡 缓存过期:立即返回缓存内容,同时异步更新
367
- console.error(`[PromptManager] Using stale cached ${fileName} (age: ${Math.round(cacheAge / 1000 / 60)}min), scheduling async update`);
370
+ if (debug) console.error(`[PromptManager] Using stale cached ${fileName} (age: ${Math.round(cacheAge / 1000 / 60)}min), scheduling async update`);
368
371
 
369
372
  // 异步更新缓存(不阻塞本次返回)
370
373
  this.asyncUpdatePromptCache(fileName).catch(error => {
@@ -375,7 +378,7 @@ class PromptManager {
375
378
  }
376
379
  } catch (error) {
377
380
  // 🔴 缓存不存在:必须立即获取
378
- console.error(`[PromptManager] Cache miss for ${fileName}, fetching immediately`);
381
+ if (debug) console.error(`[PromptManager] Cache miss for ${fileName}, fetching immediately`);
379
382
  return await this.fetchPromptContent(fileName);
380
383
  }
381
384
  }
@@ -385,7 +388,7 @@ class PromptManager {
385
388
  * 🔧 重构:统一缓存保存逻辑,减少重复代码
386
389
  */
387
390
  async fetchPromptContent(fileName) {
388
- console.error(`[PromptManager] Fetching prompt content: ${fileName} (${this.platform} mode)`);
391
+ if (debug) console.error(`[PromptManager] Fetching prompt content: ${fileName} (${this.platform} mode)`);
389
392
 
390
393
  let content, version;
391
394
 
@@ -423,7 +426,7 @@ class PromptManager {
423
426
  throw new Error(`Unsupported platform: ${pathInfo.platform}`);
424
427
  }
425
428
 
426
- console.error(`[PromptManager] API call: ${url}`);
429
+ if (debug) console.error(`[PromptManager] API call: ${url}`);
427
430
 
428
431
  const requestOptions = { headers, timeout: 10000 };
429
432
  const response = await fetch(url, requestOptions);
@@ -456,7 +459,7 @@ class PromptManager {
456
459
 
457
460
  // 统一保存到缓存
458
461
  await this.saveToCacheWithVersion(fileName, content, version);
459
- console.error(`[PromptManager] Successfully fetched and cached ${fileName} with version ${version}`);
462
+ if (debug) console.error(`[PromptManager] Successfully fetched and cached ${fileName} with version ${version}`);
460
463
 
461
464
  return content;
462
465
  }
@@ -466,11 +469,11 @@ class PromptManager {
466
469
  */
467
470
  async asyncUpdatePromptCache(fileName) {
468
471
  try {
469
- console.error(`[PromptManager] Async updating cache for ${fileName}...`);
472
+ if (debug) console.error(`[PromptManager] Async updating cache for ${fileName}...`);
470
473
  await this.fetchPromptContent(fileName);
471
- console.error(`[PromptManager] Async cache update completed for ${fileName}`);
474
+ if (debug) console.error(`[PromptManager] Async cache update completed for ${fileName}`);
472
475
  } catch (error) {
473
- console.warn(`[PromptManager] ⚠️ Async cache update failed for ${fileName}: ${error.message}`);
476
+ if (debug) console.error(`[PromptManager] Async cache update failed for ${fileName}: ${error.message}`);
474
477
  }
475
478
  }
476
479
 
@@ -502,9 +505,9 @@ class PromptManager {
502
505
  };
503
506
  await fs.writeFile(versionFile, JSON.stringify(versionInfo, null, 2), 'utf8');
504
507
 
505
- console.error(`[PromptManager] Cached ${fileName} with version ${finalVersion}`);
508
+ if (debug) console.error(`[PromptManager] Cached ${fileName} with version ${finalVersion}`);
506
509
  } catch (error) {
507
- console.warn(`[PromptManager] Failed to cache ${fileName}: ${error.message}`);
510
+ if (debug) console.error(`[PromptManager] Failed to cache ${fileName}: ${error.message}`);
508
511
  }
509
512
  }
510
513
 
package/server.js CHANGED
@@ -19,15 +19,18 @@ import {
19
19
  const debug = process.env.DEBUG_LOG === 'true';
20
20
 
21
21
  process.on('uncaughtException', (error) => {
22
- console.error('🚨 [MCP-Server] Uncaught Exception:', error.message);
22
+ // 仅在debug模式下输出异常信息(使用stderr,不干扰MCP协议)
23
23
  if (debug) {
24
- console.error(error.stack);
24
+ console.error('[MCP-Server] Uncaught Exception:', error.message, error.stack);
25
25
  }
26
26
  // 不要退出进程,继续服务
27
27
  });
28
28
 
29
29
  process.on('unhandledRejection', (reason, promise) => {
30
- console.error('🚨 [MCP-Server] Unhandled Rejection:', reason);
30
+ // 仅在debug模式下输出(使用stderr,不干扰MCP协议)
31
+ if (debug) {
32
+ console.error('[MCP-Server] Unhandled Rejection:', reason);
33
+ }
31
34
  // 不要退出进程,继续服务
32
35
  });
33
36
 
@@ -43,12 +46,12 @@ if (debug) {
43
46
 
44
47
  // 🔄 优雅关闭处理
45
48
  process.on('SIGTERM', () => {
46
- console.error('📴 [MCP-Server] Received SIGTERM, shutting down gracefully');
49
+ if (debug) console.error('[MCP-Server] Received SIGTERM, shutting down gracefully');
47
50
  process.exit(0);
48
51
  });
49
52
 
50
53
  process.on('SIGINT', () => {
51
- console.error('📴 [MCP-Server] Received SIGINT, shutting down gracefully');
54
+ if (debug) console.error('[MCP-Server] Received SIGINT, shutting down gracefully');
52
55
  process.exit(0);
53
56
  });
54
57
 
@@ -58,47 +61,26 @@ let isServerReady = false;
58
61
 
59
62
  // 同步初始化函数 - 阻塞式等待初始化完成
60
63
  async function initializeServerSync() {
61
- // ✅ 添加keepAlive定时器,防止客户端超时
62
- let keepAliveInterval;
63
-
64
64
  try {
65
- console.error('[MCP-Server] Starting synchronous initialization...');
66
-
67
- // 每5秒输出一次日志,保持连接活跃(防止客户端超时)
68
- keepAliveInterval = setInterval(() => {
69
- console.error('[MCP-KeepAlive] I\'m alive, Heartbeat, Heartbeat...');
70
- }, 5000);
65
+ if (debug) console.error('[MCP-Server] Starting initialization...');
71
66
 
72
67
  // 同步初始化prompt管理器
73
68
  globalToolsConfig = await initializePrompts();
74
69
 
75
- // 清理keepAlive定时器
76
- clearInterval(keepAliveInterval);
77
- keepAliveInterval = null;
78
-
79
70
  // 标记服务器就绪
80
71
  isServerReady = true;
81
- console.error(`[MCP-Server] Server ready with ${globalToolsConfig.length} tools`);
72
+ if (debug) console.error(`[MCP-Server] Server ready with ${globalToolsConfig.length} tools`);
82
73
 
83
74
  return true;
84
75
  } catch (error) {
85
- // 清理keepAlive定时器(如果还在运行)
86
- if (keepAliveInterval) {
87
- clearInterval(keepAliveInterval);
88
- }
89
-
90
- console.error('[MCP-Server] ❌ Initialization failed:', error.message);
91
- console.error('[MCP-Server] Server will not accept requests');
76
+ if (debug) console.error('[MCP-Server] Initialization failed:', error.message);
92
77
  isServerReady = false;
93
78
 
94
- // 🟢 GREEN: Task 2.1 - 优雅降级而不是崩溃
95
- console.error('[MCP-Server] ⚠️ Entering degraded mode due to initialization failure');
96
-
97
- // 设置基本的降级工具配置
79
+ // 优雅降级而不是崩溃
98
80
  globalToolsConfig = getBasicFallbackTools();
99
81
  isServerReady = true; // 允许服务器继续运行
100
82
 
101
- console.error(`[MCP-Server] Degraded mode active with ${globalToolsConfig.length} basic tools`);
83
+ if (debug) console.error(`[MCP-Server] Degraded mode active with ${globalToolsConfig.length} basic tools`);
102
84
  return false; // 表示降级模式
103
85
  }
104
86
  }
@@ -211,8 +193,8 @@ async function handleToolCall(req) {
211
193
 
212
194
  // Format feedback result for AI consumption
213
195
  if (typeof feedbackResult === 'object' && feedbackResult.hasUserInput) {
214
- resultText = `📝 **用户反馈处理结果**
215
-
196
+ resultText = `
197
+ 📝 **用户反馈处理结果**
216
198
  **用户选择:** ${feedbackResult.action}
217
199
  **具体建议:** "${feedbackResult.input}"
218
200
 
@@ -221,16 +203,14 @@ async function handleToolCall(req) {
221
203
  2. 调整相应的实现方案
222
204
  3. 说明具体如何应用这个建议
223
205
  4. 继续执行调整后的计划
224
-
225
- ⚠️ **MANDATORY NEXT STEP REMINDER:** 在完成下一个实施步骤后,必须再次调用dev-feedback工具汇报进度并获取确认。
226
-
227
- **重要:** 不要忽略用户的建议,必须基于此建议修改后续的执行步骤。`;
206
+ ${devFeedbackContentSuffix}
207
+ `;
228
208
  } else {
229
- resultText = `✅ **用户确认:** ${feedbackResult}
230
-
231
- 继续按原计划执行。
232
-
233
- ⚠️ **MANDATORY NEXT STEP REMINDER:** 在完成下一个实施步骤后,必须再次调用dev-feedback工具汇报进度并获取确认。每个步骤完成后都需要调用feedback工具 - 这是强制要求!`;
209
+ resultText = `
210
+ ✅ **用户确认:** ${feedbackResult}
211
+ **决策:** 继续按原计划执行。
212
+ ${devFeedbackContentSuffix}
213
+ `;
234
214
  }
235
215
  break;
236
216
  case 'handleGitlabMrFetch':
@@ -253,33 +233,7 @@ async function handleToolCall(req) {
253
233
  **工作流提醒:** 按计划执行,完成后汇报结果。`;
254
234
  } else {
255
235
  // AUTO=false: 强制分步确认+自检
256
- resultText = `${promptResult}
257
-
258
- ---
259
- ## ⚠️ **CRITICAL WORKFLOW REQUIREMENT:**
260
-
261
- **每完成一步,必须调用 \`dev-feedback\` 并展示以下信息:**
262
-
263
- ### 展示内容
264
- 1. **当前阶段**:完成的步骤/阶段名称
265
- 2. **工作摘要**:完成的主要工作内容
266
- 3. **自检结果**:
267
- - 是否有多方案未选择
268
- - 是否有信息不明确或需要澄清的内容
269
- - 是否发现冲突(代码/逻辑/信息)
270
- - 是否偏离计划需要调整
271
- - 是否即将执行重要操作(删除代码、修改核心逻辑、数据库变更等)
272
- - 是否存在"TBD"、"待确认"、"Question"等需要用户确认的内容
273
- - 是否涉及项目/系统范围确认(无论已确认还是待确认,都必须调用 dev-feedback 让用户确认)
274
- 4. **下一步**:下一步计划(如有)
275
-
276
- **最后一步结束后,确认:是否满足所有需求和完成了计划?**
277
-
278
- ### 规则
279
- - 宁可多确认,不可漏确认
280
- - 严禁将待确认内容留到最终文档
281
-
282
- **⚠️ 立即调用 dev-feedback 开始确认!**`;
236
+ resultText = `${promptResult}\n\n${devFeedbackContentSuffix}`;
283
237
  }
284
238
  } else {
285
239
  throw new Error(`Unknown tool type: ${tool.type}`);
@@ -289,12 +243,11 @@ async function handleToolCall(req) {
289
243
  content: [{ type: 'text', text: resultText }]
290
244
  });
291
245
  } catch (error) {
292
- // 简洁的错误日志记录
293
- console.error(`[MCP错误] 工具: ${name}, 错误: ${error.message}`);
246
+ // 仅debug模式下输出错误日志(使用stderr,不干扰MCP协议)
247
+ if (debug) console.error(`[MCP] Tool error: ${name} - ${error.message}`);
294
248
 
295
249
  // 特殊处理:某些"错误"实际上是正常的用户交互结果
296
250
  if (error.message.includes('用户要求调整计划')) {
297
- console.error(`[MCP信息] 用户计划调整请求: ${error.message}`);
298
251
  return createSuccessResponse(req.id, {
299
252
  content: [{ type: 'text', text: error.message }]
300
253
  });
@@ -319,14 +272,10 @@ async function handleToolCall(req) {
319
272
  }
320
273
  }
321
274
 
322
- // 🟢 GREEN: Task 2.1 - 支持优雅降级的初始化
275
+ // 支持优雅降级的初始化
323
276
  const initSuccess = await initializeServerSync();
324
- if (!initSuccess) {
325
- console.error('[MCP-Server] ⚠️ Running in degraded mode');
326
- }
327
-
328
- if (CFG.debug) {
329
- console.error('[MCP-Server] Debug mode enabled');
277
+ if (!initSuccess && debug) {
278
+ console.error('[MCP-Server] Running in degraded mode');
330
279
  }
331
280
 
332
281
  // 初始化完成后才开始监听stdin
@@ -349,3 +298,35 @@ process.stdin.on('data', chunk => {
349
298
  });
350
299
  }
351
300
  });
301
+
302
+ const devFeedbackContentSuffix = `
303
+ ---
304
+ ## ⚠️ **CRITICAL WORKFLOW REQUIREMENT:**
305
+
306
+ **每完成一步,必须调用 \`dev-feedback\` 并展示以下信息:**
307
+
308
+ ### 触发时机
309
+ - 每一步**工作开始之前** 或 每一步**工作完成之后**
310
+ - 任何**计划开始执行之前** 或 任何**计划完成之后**
311
+ - 有设计,计划调整或重要变化的时候
312
+ - 有**重大问题或风险**需要用户确认的时候
313
+ - 有**决策/方案**需要用户确认或者有**任何疑问**需要用户确认的时候
314
+
315
+ ### 展示内容
316
+ 1. **当前阶段**:完成的步骤/阶段名称
317
+ 2. **工作摘要**:完成的主要工作内容
318
+ 3. **自检结果**:
319
+ - 是否有多方案未选择
320
+ - 是否有信息不明确或需要澄清的内容
321
+ - 是否发现冲突(代码/逻辑/信息)
322
+ - 是否偏离计划需要调整
323
+ - 是否即将执行重要操作(删除代码、修改核心逻辑、数据库变更等)
324
+ - 是否存在"TBD"、"待确认"、"Question"等需要用户确认的内容
325
+ - 是否涉及项目/系统范围确认(无论已确认还是待确认,都必须调用 dev-feedback 让用户确认)
326
+ 4. **下一步**:下一步计划(如有)
327
+
328
+ ### 规则
329
+ - 尽可能多确认,不可遗漏确认
330
+ - 严禁将待确认内容留到最终文档(包括代码、文档、配置等)
331
+
332
+ **⚠️ 立即调用 dev-feedback 开始确认!**`;
package/tools.js CHANGED
@@ -262,7 +262,7 @@ export async function enhancedDevFeedback(feedbackInput = {}) {
262
262
 
263
263
  // For automated mode (testing only), return first option
264
264
  if (process.env.AUTOMATED_MODE === 'true') {
265
- console.log('🤖 [AUTOMATED_MODE] Returning:', options[0]);
265
+ console.error('🤖 [AUTOMATED_MODE] Returning:', options[0]);
266
266
  return options[0];
267
267
  }
268
268
 
@@ -479,30 +479,23 @@ export async function handleDevManual() {
479
479
  }
480
480
 
481
481
  export async function handleDevFeedback(args) {
482
- console.error('[DEBUG] handleDevFeedback called with args:', JSON.stringify(args, null, 2));
483
-
484
482
  // Support both old simple format and new enhanced format
485
483
  if (args.phase || args.context || args.allowPlanAdjustment) {
486
- console.error('[DEBUG] Taking enhanced feedback branch');
487
484
  // Use enhanced feedback for new format
488
485
  return await enhancedDevFeedback(args);
489
486
  }
490
487
 
491
- console.error('[DEBUG] Taking legacy feedback branch');
492
-
493
488
  // Legacy support for old format
494
489
  const { title, message, options = [] } = args;
495
490
  if (process.env.AUTOMATED_MODE === 'true') {
496
491
  // Return first option or default for automated testing
497
- console.log('🤖 [AUTOMATED_MODE] Returning:', options.length ? options[0] : '继续');
492
+ console.error('🤖 [AUTOMATED_MODE] Returning:', options.length ? options[0] : '继续');
498
493
  return options.length ? options[0] : '继续';
499
494
  }
500
495
 
501
496
  // 简化逻辑:直接传入options,函数内部自动判断场景
502
497
  const finalOptions = getStandardButtons(options.length > 0 ? options : null);
503
498
 
504
- console.error('[DEBUG] Calling showConfirmDialog (heartbeat is handled in native.js)...');
505
-
506
499
  try {
507
500
  const result = await showConfirmDialog({
508
501
  title,
@@ -531,8 +524,6 @@ export async function handleDevFeedback(args) {
531
524
  }
532
525
  return result;
533
526
  } catch (error) {
534
- console.error('❌ [FEEDBACK ERROR]:', error.message);
535
-
536
527
  // Check if this is a dialog system failure
537
528
  if (error.message.includes('DIALOG_SYSTEM_FAILURE')) {
538
529
  // Critical failure - inform user and stop AI conversation
@@ -545,15 +536,11 @@ export async function handleDevFeedback(args) {
545
536
 
546
537
  **需要您确认:** 请输入 "confirmed" 确认已了解此错误,AI将停止当前对话。`;
547
538
 
548
- console.log('🚨 [CRITICAL] Dialog system failure - stopping AI conversation');
549
- console.log(failureMessage);
550
-
551
539
  // This will be returned to the AI, causing it to stop and wait for user input
552
540
  throw new Error(`CRITICAL_DIALOG_FAILURE: ${failureMessage}`);
553
541
  }
554
542
 
555
543
  // For other errors, try browser fallback
556
- console.log('🔄 [FALLBACK] Trying browser dialog...');
557
544
  const { showConfirmDialog: browserConfirm } = await import('./dialog/browser.js');
558
545
 
559
546
  return await browserConfirm({
@@ -609,7 +596,8 @@ export async function handleGitlabMrFetch(args) {
609
596
  const projectEnc = encodeURIComponent(projectPath);
610
597
  const headers = createAuthHeaders('gitlab', token);
611
598
 
612
- console.error(`[GitLab MR] Fetching MR ${mrIid} from ${projectPath}`);
599
+ const debug = process.env.DEBUG_LOG === 'true';
600
+ if (debug) console.error(`[GitLab MR] Fetching MR ${mrIid} from ${projectPath}`);
613
601
 
614
602
  try {
615
603
  // Fetch MR details
@@ -680,12 +668,12 @@ ${mrData.description || '_No description provided_'}
680
668
  output += '_No changes found_\n';
681
669
  }
682
670
 
683
- console.error(`[GitLab MR] Successfully fetched MR with ${changesData.changes?.length || 0} changed files`);
671
+ if (debug) console.error(`[GitLab MR] Successfully fetched MR with ${changesData.changes?.length || 0} changed files`);
684
672
 
685
673
  return output;
686
674
 
687
675
  } catch (error) {
688
- console.error(`[GitLab MR] Error:`, error.message);
676
+ if (debug) console.error(`[GitLab MR] Error:`, error.message);
689
677
  throw error;
690
678
  }
691
679
  }
package/utils.js CHANGED
@@ -113,6 +113,7 @@ export function validateRequest(req) {
113
113
  * @returns {Function} Wrapped handler
114
114
  */
115
115
  export function withErrorHandling(handler) {
116
+ const debug = process.env.DEBUG_LOG === 'true';
116
117
  return async (req) => {
117
118
  try {
118
119
  const validation = validateRequest(req);
@@ -123,7 +124,8 @@ export function withErrorHandling(handler) {
123
124
 
124
125
  return await handler(req);
125
126
  } catch (error) {
126
- console.error(`Handler error for ${req.method}:`, error);
127
+ // 使用stderr输出,不干扰MCP协议的stdout通信
128
+ if (debug) console.error(`Handler error for ${req.method}:`, error.message);
127
129
  return createErrorResponse(req.id, MCP_ERROR_CODES.INTERNAL_ERROR, error.message);
128
130
  }
129
131
  };