@srgay/cursor-extension 1.0.0 → 1.0.1

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
@@ -3,7 +3,7 @@
3
3
  一组本机 Cursor workbench 增强脚本,包含三部分:
4
4
 
5
5
  1. **MAX Mode 守护**:保持 `MAX Mode` 关闭,并在彩色 `MAX` 标记出现且聊天框有内容时阻止发送。
6
- 2. **MCP Follow-up 面板**:在 Cursor 聊天框上方注入一个 MCP 反馈面板,连接 `mcp-feedback-enhanced` 的 WebSocket,让你直接在 Cursor 里回复 `interactive_feedback`,并支持端口自动扫描与自定义。
6
+ 2. **MCP Follow-up 面板**:在 Cursor 聊天框上方注入一个 MCP 反馈面板,连接 `mcp-feedback-enhanced` 的 WebSocket,让你直接在 Cursor 里回复 `interactive_feedback`,支持端口自动扫描/自定义,并能按当前项目自动匹配对应端口(多项目、多端口同时在线时也不误连)。
7
7
  3. **输入法回车修复**:修复用中文输入法(拼音/注音等)组字时,按回车上屏候选词会被 Cursor 误当成「提交」的问题(含自带的 AskQuestion「Other」输入框)。
8
8
 
9
9
  ## 安装与使用(npx)
@@ -29,6 +29,7 @@ npx @srgay/cursor-extension help
29
29
  - Cursor 更新可能覆盖 `workbench.html`,功能失效时重新执行 `install`。
30
30
  - 自定义 Cursor 安装目录:设置环境变量 `CURSOR_WORKBENCH_DIR`。
31
31
  - `inject` 自定义调试端口:设置环境变量 `CURSOR_DEBUG_PORT`(默认 `9222`)。
32
+ - MCP 面板配置读写所用的 python 命令:默认按平台自动探测(`python` / `py` / `python3`),可用环境变量 `CURSOR_MCP_PYTHON` 强制指定(某些 Windows 环境用得上)。
32
33
 
33
34
  > 说明:下文「推荐方式 / 备用方式」里的 `npm run patch`、`npm run inject` 等是**本仓库源码开发**用法;最终用户用上面的 `npx` 命令即可,二者等价。
34
35
 
@@ -52,10 +53,10 @@ npm run patch
52
53
  Patch 行为:
53
54
 
54
55
  - 注入 `<script src="./cursor-max-mode-guard.js">`、`<script src="./cursor-mcp-followup.js">`、`<script src="./cursor-ime-enter-fix.js">` 到 `workbench.html`(各自带注释标记,便于幂等与回退)。
55
- - 把 `cursor-mcp-followup.js` 拷入 workbench 目录时,将其中的 `__MCP_SETTINGS_PATH__` 占位符替换为本机真实路径 `~/.config/mcp-feedback-enhanced/ui_settings.json`(面板借 WS `run_command` 读写该文件,实现常用提示词与自动提交配置的查看/编辑)。
56
+ - 把 `cursor-mcp-followup.js` 拷入 workbench 目录时,按当前系统替换其中的路径/命令占位符:配置路径 `~/.config/mcp-feedback-enhanced/ui_settings.json`(Windows 为 `C:/Users/<你>/.config/...`,统一用正斜杠),以及读取命令(Mac/Linux 用 `cat`、Windows 用 `python -X utf8` 读取)与写入用的 python 命令名(面板借 WS `run_command` 读写该文件,实现常用提示词与自动提交配置的查看/编辑)。
56
57
  - 同步更新 Cursor `product.json` 里的 `workbench.html` checksum,避免 Cursor 把这次修改识别为安装损坏。
57
58
 
58
- > 注意:占位符在 patch 时按当前用户主目录写死。若更换用户/主目录,重新执行 `npm run patch`。
59
+ > 注意:占位符在 patch 时按当前用户主目录与操作系统写死。若更换用户/主目录或操作系统,重新执行 `npm run patch`。
59
60
 
60
61
  然后完整退出 Cursor,再重新打开 Cursor。
61
62
 
@@ -117,13 +118,14 @@ npm run inject
117
118
  - 端口下拉:动态填充扫描到的活跃端口,末尾提供「自定义端口…」可手动输入任意端口。
118
119
  - 下拉展开时每项显示「状态 · 项目名」(如 `Port 8766 · waiting · my-project`),收起时只显示「Port X · 状态」以贴合宽度,避免与右侧项目名重复。
119
120
  - 放大镜按钮可随时重新扫描。
121
+ - **自动匹配当前项目端口**:从 Cursor 原生 `window.vscode` 取当前窗口的工作区路径,优先连接 `project_directory` 与之相等的 MCP 端口;多个端口同时在线(如别的项目窗口或常驻实例)时也能选对本项目,绝不误连。本项目端口尚未出现时持续重扫等待,出现后自动连上;取不到工作区(多根工作区/空窗口)时回退为原有的端口选择逻辑。
120
122
  - 持续保持为 WebSocket「最后连接」(每 3 秒重连 + 输入框聚焦时重连),确保始终能收到当前会话并成功提交反馈。
121
123
  - 底部「常用提示词」下拉:一键把 `ui_settings.json` 里的提示词追加到输入框;旁边刷新按钮可重新拉取配置。
122
124
  - 齿轮按钮展开**配置抽屉**:
123
125
  - **常用提示词**增删改(写回 `ui_settings.json`,删除带二次确认)。
124
126
  - **自动提交**配置:启用开关、超时(秒)、选择提示词,状态实时回显。
125
127
  - **自动提交运行时**:仅在「等待反馈」时倒计时,输入框右侧显示 `自动提交 m:ss` 小标签;到点自动以所选提示词发送(走与手动一致的重连抢占)。用户输入/手动插入提示词/手动发送/点击倒计时/会话离开等待态,本轮即取消自动提交。
126
- - 配置读写均借现有 WebSocket 的 `run_command` 完成(读 `cat`、写 `python3` base64 解码落盘),不依赖本地文件系统访问或 CORS
128
+ - 配置读写均借现有 WebSocket 的 `run_command` 完成(Mac/Linux 读用 `cat`,Windows 读用 `python -X utf8`;写入两平台都用 python base64 解码落盘),不依赖本地文件系统访问或 CORS。服务端以 `shell=False` + `shlex.split` 执行命令,故 Windows 路径统一用正斜杠以规避转义。
127
129
 
128
130
  ### 加载方式
129
131
 
@@ -140,11 +142,13 @@ open -na /Applications/Cursor.app --args --remote-debugging-port=9222
140
142
  npm run inject:followup
141
143
  ```
142
144
 
143
- > CDP 注入是临时的,**重启 Cursor 后需要重新执行**;`install-cursor-mcp-followup.mjs` 会在注入时替换 `__MCP_SETTINGS_PATH__` 占位符。
145
+ > CDP 注入是临时的,**重启 Cursor 后需要重新执行**;`install-cursor-mcp-followup.mjs` 会在注入时按平台替换路径/命令占位符(详见 `src/shared/mcp-settings.mjs`)。
144
146
 
145
147
  ### 端口扫描与自定义
146
148
 
147
149
  - 面板安装时自动扫描一次 `8765–8769`。
150
+ - **按项目自动选端口**:扫描后优先选中 `project_directory` 等于当前窗口工作区路径的端口;若当前选中端口已知属于别的项目则不连接,避免误连到其它项目或常驻 feedback 实例。
151
+ - 本项目端口还没出现时,每 3 秒重扫一次去发现它;连上本项目端口后改为定期重连以保持「最后连接」。
148
152
  - 点击放大镜按钮可手动重扫。
149
153
  - 在端口下拉里选择「自定义端口…」,就地输入端口号回车即可连接;输入框失焦或按 `Esc` 会回退到第一个可用端口。
150
154
 
@@ -220,6 +224,7 @@ window.__cursorImeEnterFix.uninstall() // 卸载
220
224
  - `install-cursor-ime-enter-fix.mjs`: 通过 CDP 临时注入输入法回车修复。
221
225
  - `src/shared/` — 共享工具
222
226
  - `cursor-workbench-paths.mjs`: macOS/Windows workbench 路径解析。
227
+ - `mcp-settings.mjs`: 按平台生成 MCP 面板的配置路径与读/写命令(探测 python,Windows 用正斜杠)。
223
228
  - `patch-cursor-workbench.mjs`: patch Cursor 的 `workbench.html`(注入全部三个脚本)。
224
229
  - `unpatch-cursor-workbench.mjs`: 恢复 patch。
225
230
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srgay/cursor-extension",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "本机 Cursor workbench 增强:MAX Mode 守护、MCP Follow-up 面板、输入法回车修复(支持随 Cursor 启动持久加载,或 CDP 临时注入)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "node": ">=20"
17
17
  },
18
18
  "scripts": {
19
- "check": "node --check src/max-mode-guard/cursor-max-mode-guard.js && node --check src/mcp-followup/cursor-mcp-followup.js && node --check src/ime-enter-fix/cursor-ime-enter-fix.js && node --check src/shared/cursor-workbench-paths.mjs && node --check src/max-mode-guard/install-cursor-max-mode-guard.mjs && node --check src/mcp-followup/install-cursor-mcp-followup.mjs && node --check src/ime-enter-fix/install-cursor-ime-enter-fix.mjs && node --check src/shared/patch-cursor-workbench.mjs && node --check src/shared/unpatch-cursor-workbench.mjs && node --check bin/cli.mjs",
19
+ "check": "node --check src/max-mode-guard/cursor-max-mode-guard.js && node --check src/mcp-followup/cursor-mcp-followup.js && node --check src/ime-enter-fix/cursor-ime-enter-fix.js && node --check src/shared/cursor-workbench-paths.mjs && node --check src/shared/mcp-settings.mjs && node --check src/max-mode-guard/install-cursor-max-mode-guard.mjs && node --check src/mcp-followup/install-cursor-mcp-followup.mjs && node --check src/ime-enter-fix/install-cursor-ime-enter-fix.mjs && node --check src/shared/patch-cursor-workbench.mjs && node --check src/shared/unpatch-cursor-workbench.mjs && node --check bin/cli.mjs",
20
20
  "inject": "node src/max-mode-guard/install-cursor-max-mode-guard.mjs",
21
21
  "inject:followup": "node src/mcp-followup/install-cursor-mcp-followup.mjs",
22
22
  "inject:ime": "node src/ime-enter-fix/install-cursor-ime-enter-fix.mjs",
@@ -17,9 +17,13 @@
17
17
  lang: "zh-CN",
18
18
  // 发送场景重连成功(onopen)后,若服务端未及时推送会话状态,则等待此毫秒数后兜底直接发送。
19
19
  sendAfterOpenMs: 350,
20
- // 常用提示词所在 ui_settings.json 的绝对路径;注入脚本会用真实 home 路径替换此占位符。
21
- // 面板借道现有 WS 的 run_command(cat 该文件)拉取提示词,不读本地 fs、不依赖 CORS。
20
+ // 常用提示词所在 ui_settings.json 的绝对路径;注入脚本会按平台替换此占位符(Windows 用正斜杠)。
21
+ // 面板借道现有 WS 的 run_command 读写该文件,不读本地 fs、不依赖 CORS。
22
22
  settingsPath: "__MCP_SETTINGS_PATH__",
23
+ // 读取配置的完整命令:Mac/Linux 为 `cat <path>`,Windows 为 python UTF-8 读取(注入时按平台生成)。
24
+ readCmd: "__MCP_READ_CMD__",
25
+ // 写回配置时使用的 python 命令名(注入时探测:python / py / python3)。
26
+ pyCmd: "__MCP_PY__",
23
27
  // run_command 拉取提示词的兜底超时(毫秒)。
24
28
  promptLoadTimeoutMs: 4000,
25
29
  };
@@ -36,6 +40,8 @@
36
40
  scanning: false,
37
41
  foundPorts: [],
38
42
  portProjects: {},
43
+ // 端口 → 完整 project_directory(扫描/会话时记录),用于把面板锁定到当前窗口项目对应的端口。
44
+ portPaths: {},
39
45
  portStatuses: {},
40
46
  selectedStatus: null,
41
47
  expanded: false,
@@ -593,6 +599,37 @@
593
599
  );
594
600
  }
595
601
 
602
+ // 路径规范化:统一分隔符并去掉尾部斜杠,便于和 session 的 project_directory 精确比较。
603
+ function normalizePath(dir) {
604
+ return String(dir || "")
605
+ .replace(/\\/g, "/")
606
+ .replace(/\/+$/, "");
607
+ }
608
+
609
+ // 当前 Cursor 窗口的工作区绝对路径,取自原生 window.vscode(无需额外注入信息)。
610
+ // 取不到(多根工作区 / 空窗口 / 接口缺失)时返回 null,调用方据此降级为原有端口选择逻辑。
611
+ function getWorkspacePath() {
612
+ try {
613
+ const ctx = window.vscode && window.vscode.context;
614
+ const cfg = ctx && typeof ctx.configuration === "function" ? ctx.configuration() : null;
615
+ const uri = cfg && cfg.workspace && cfg.workspace.uri;
616
+ const p = uri && (uri.path || uri.fsPath || uri._fsPath);
617
+ return p ? normalizePath(p) : null;
618
+ } catch (error) {
619
+ return null;
620
+ }
621
+ }
622
+
623
+ // 从一轮扫描结果里挑出 project_directory 与当前窗口工作区相等的端口(按项目锁定端口的核心优先级)。
624
+ function pickWorkspacePort(results) {
625
+ const ws = getWorkspacePath();
626
+ if (!ws) return null;
627
+ const hit = results.find(
628
+ (item) => item.alive && item.project && normalizePath(item.project) === ws
629
+ );
630
+ return hit ? String(hit.port) : null;
631
+ }
632
+
596
633
  // 选项文本:Port {port}[ · 状态][ · 项目名](状态在前、项目在后)。
597
634
  // 下拉展开时所有项都带「状态 · 项目名」,便于按项目/状态区分端口;
598
635
  // 收起时框里显示的是「选中项」,为避免与右侧项目名重复,选中项收起时不带项目名。
@@ -685,6 +722,7 @@
685
722
 
686
723
  const project = basename(info && info.project_directory);
687
724
  if (project) state.portProjects[state.socketPort] = project;
725
+ if (info && info.project_directory) state.portPaths[state.socketPort] = info.project_directory;
688
726
  state.portStatuses[state.socketPort] = deriveStatusLabel(info);
689
727
 
690
728
  const status = info && info.status;
@@ -763,8 +801,8 @@
763
801
  state.cmdBuffer = "";
764
802
  if (els.promptRefresh) els.promptRefresh.classList.add("scanning");
765
803
  try {
766
- // shell=False,路径无空格,直接传绝对路径即可(~ 不会被展开)。
767
- state.socket.send(JSON.stringify({ type: "run_command", command: "cat " + config.settingsPath }));
804
+ // 读取命令由注入脚本按平台生成(Mac/Linux: cat;Windows: python -X utf8 读取)。
805
+ state.socket.send(JSON.stringify({ type: "run_command", command: config.readCmd }));
768
806
  } catch (error) {
769
807
  state.loadingPrompts = false;
770
808
  state.cmdBuffer = null;
@@ -836,7 +874,7 @@
836
874
  "').write_bytes(__import__('base64').b64decode('" +
837
875
  b64 +
838
876
  "'))";
839
- command = 'python3 -c "' + py + '"';
877
+ command = config.pyCmd + ' -c "' + py + '"';
840
878
  const lower = command.toLowerCase();
841
879
  safe = dangerous.every((p) => lower.indexOf(p) === -1);
842
880
  if (!safe) payload += "\n";
@@ -1282,20 +1320,29 @@
1282
1320
  for (const item of results) {
1283
1321
  if (item.alive) {
1284
1322
  found.push(item.port);
1285
- if (item.project) state.portProjects[item.port] = basename(item.project);
1286
- else delete state.portProjects[item.port];
1323
+ if (item.project) {
1324
+ state.portProjects[item.port] = basename(item.project);
1325
+ state.portPaths[item.port] = item.project;
1326
+ } else {
1327
+ delete state.portProjects[item.port];
1328
+ delete state.portPaths[item.port];
1329
+ }
1287
1330
  if (item.statusLabel) state.portStatuses[item.port] = item.statusLabel;
1288
1331
  else delete state.portStatuses[item.port];
1289
1332
  } else {
1290
1333
  delete state.portProjects[item.port];
1334
+ delete state.portPaths[item.port];
1291
1335
  delete state.portStatuses[item.port];
1292
1336
  }
1293
1337
  }
1294
1338
  state.foundPorts = found;
1295
1339
 
1296
1340
  const wasCustom = els.portSelect.value === config.customValue;
1297
- fillPortOptions(found, wasCustom ? null : els.portSelect.value);
1298
- if (wasCustom) els.portSelect.value = config.customValue;
1341
+ // 优先选中 project_directory 与当前窗口工作区相等的端口;取不到工作区或无匹配端口时维持原有选择
1342
+ //(combo 降级),连接层再由 connectSelectedPort 守卫兜底,避免连到别的项目。
1343
+ const preferred = pickWorkspacePort(results);
1344
+ fillPortOptions(found, preferred || (wasCustom ? null : els.portSelect.value));
1345
+ if (wasCustom && !preferred) els.portSelect.value = config.customValue;
1299
1346
 
1300
1347
  if (els.scanBtn) {
1301
1348
  els.scanBtn.classList.remove("scanning");
@@ -1352,6 +1399,23 @@
1352
1399
  function connectSelectedPort(auto) {
1353
1400
  const port = els.portSelect.value;
1354
1401
  if (port === config.customValue) return;
1402
+
1403
+ // 守卫:能确定当前窗口项目、且该端口已知属于别的项目时不连接,
1404
+ // 避免误连到其它项目或常驻 feedback 实例(等本项目端口出现后由 refreshTick 重扫接管)。
1405
+ const ws = getWorkspacePath();
1406
+ if (ws && state.portPaths[port] && normalizePath(state.portPaths[port]) !== ws) {
1407
+ if (state.socket) {
1408
+ state.socket.onclose = null;
1409
+ state.socket.onerror = null;
1410
+ state.socket.onmessage = null;
1411
+ state.socket.close();
1412
+ state.socket = null;
1413
+ }
1414
+ setOptionLabel(port, "other project");
1415
+ setVisualState("offline", port + " 属于其它项目,等待本项目的 MCP 会话…");
1416
+ return;
1417
+ }
1418
+
1355
1419
  const seq = ++state.connectSeq;
1356
1420
 
1357
1421
  if (state.socket) {
@@ -1436,6 +1500,18 @@
1436
1500
  if (els.portSelect.value === config.customValue) return;
1437
1501
  if (els.customInput === document.activeElement) return;
1438
1502
  if (els.prompt === document.activeElement && els.prompt.value.trim()) return;
1503
+
1504
+ // 还没连到「本项目」端口时,定期重扫去发现它(scanPorts 会优先选中并连上);
1505
+ // 已在本项目端口(或取不到工作区时)则维持原有重连,持续保持「最后连接」。
1506
+ const ws = getWorkspacePath();
1507
+ if (ws) {
1508
+ const cur = els.portSelect.value;
1509
+ const onWorkspacePort = !!state.portPaths[cur] && normalizePath(state.portPaths[cur]) === ws;
1510
+ if (!onWorkspacePort) {
1511
+ scanPorts();
1512
+ return;
1513
+ }
1514
+ }
1439
1515
  connectSelectedPort(true);
1440
1516
  }
1441
1517
 
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { dirname, join, resolve } from "node:path";
2
+ import { dirname, resolve } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
4
+ import { applyMcpFollowupReplacements } from "../shared/mcp-settings.mjs";
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  const port = Number(process.env.CURSOR_DEBUG_PORT || "9222");
@@ -69,10 +69,9 @@ if (!target) {
69
69
  );
70
70
  }
71
71
 
72
- // 面板运行在浏览器环境(无 fs),改用 WS run_command 读取常用提示词;
73
- // 这里在 Node 侧把占位符替换成 ui_settings.json 的真实绝对路径(shell=False ~ 不展开)。
74
- const settingsPath = join(homedir(), ".config", "mcp-feedback-enhanced", "ui_settings.json");
75
- const source = (await readFile(sourcePath, "utf8")).replace("__MCP_SETTINGS_PATH__", settingsPath);
72
+ // 面板运行在浏览器环境(无 fs),改用 WS run_command 读写 ui_settings.json;
73
+ // 这里在 Node 侧按平台把占位符替换成真实路径与命令(Windows 用正斜杠 + python,详见 shared/mcp-settings.mjs)。
74
+ const source = applyMcpFollowupReplacements(await readFile(sourcePath, "utf8"));
76
75
  const client = connect(target.webSocketDebuggerUrl);
77
76
 
78
77
  await client.opened;
@@ -0,0 +1,54 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ // MCP Follow-up 面板运行在浏览器环境(无 fs),通过现有 WS 的 run_command 读/写 ui_settings.json。
6
+ // 服务端(mcp-feedback-enhanced)以 shell=False + shlex.split(POSIX) 执行命令,因此:
7
+ // - 路径里的反斜杠会被 shlex 与 Python 字面量双重转义 → Windows 统一改用正斜杠;
8
+ // - Windows 没有 cat / 不一定有 python3 → 读取改用探测到的 python,并用 -X utf8 规避编码问题。
9
+ // 这里在注入脚本(Node 侧、与 Cursor/MCP 同机)按平台生成真实路径与命令,替换面板里的占位符。
10
+
11
+ // 探测可用的 python 命令名:优先环境变量 CURSOR_MCP_PYTHON,其次按平台候选逐一 `--version` 验证。
12
+ function detectPython() {
13
+ const override = process.env.CURSOR_MCP_PYTHON;
14
+ if (override && override.trim()) return override.trim();
15
+
16
+ const isWin = process.platform === "win32";
17
+ const candidates = isWin ? ["python", "py", "python3"] : ["python3", "python"];
18
+ for (const cmd of candidates) {
19
+ try {
20
+ const result = spawnSync(cmd, ["--version"], { stdio: "ignore" });
21
+ if (!result.error && result.status === 0) return cmd;
22
+ } catch {
23
+ // 忽略,尝试下一个候选
24
+ }
25
+ }
26
+ return isWin ? "python" : "python3";
27
+ }
28
+
29
+ // 生成面板占位符的真实取值:配置文件绝对路径、python 命令名、完整读取命令。
30
+ export function buildMcpFollowupReplacements() {
31
+ const isWin = process.platform === "win32";
32
+
33
+ // 与 mcp-feedback-enhanced 一致:~/.config/mcp-feedback-enhanced/ui_settings.json(无平台分支)。
34
+ let settingsPath = join(homedir(), ".config", "mcp-feedback-enhanced", "ui_settings.json");
35
+ if (isWin) settingsPath = settingsPath.replace(/\\/g, "/");
36
+
37
+ const pyCmd = detectPython();
38
+
39
+ // 读取命令:Mac/Linux 用现成的 cat;Windows 无 cat,用 python 的 UTF-8 模式读取。
40
+ const readCmd = isWin
41
+ ? `${pyCmd} -X utf8 -c "print(open('${settingsPath}').read())"`
42
+ : `cat ${settingsPath}`;
43
+
44
+ return { settingsPath, pyCmd, readCmd };
45
+ }
46
+
47
+ // 把面板源码里的占位符替换为真实取值。用函数式替换以避免 $ 等特殊字符被当作替换模式。
48
+ export function applyMcpFollowupReplacements(code) {
49
+ const { settingsPath, pyCmd, readCmd } = buildMcpFollowupReplacements();
50
+ return code
51
+ .replace("__MCP_SETTINGS_PATH__", () => settingsPath)
52
+ .replace("__MCP_READ_CMD__", () => readCmd)
53
+ .replace("__MCP_PY__", () => pyCmd);
54
+ }
@@ -1,8 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { copyFile, readFile, stat, writeFile } from "node:fs/promises";
3
- import { homedir } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
3
+ import { dirname, resolve } from "node:path";
5
4
  import { fileURLToPath } from "node:url";
5
+ import { applyMcpFollowupReplacements, buildMcpFollowupReplacements } from "./mcp-settings.mjs";
6
6
  import { resolveWorkbenchPaths } from "./cursor-workbench-paths.mjs";
7
7
 
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -10,8 +10,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
  const guardSource = resolve(__dirname, "../max-mode-guard/cursor-max-mode-guard.js");
11
11
  const followupSource = resolve(__dirname, "../mcp-followup/cursor-mcp-followup.js");
12
12
  const imeFixSource = resolve(__dirname, "../ime-enter-fix/cursor-ime-enter-fix.js");
13
- // 面板在浏览器环境无 fs,靠 WS run_command 读取 ui_settings.json;patch 时把占位符替换成真实绝对路径。
14
- const settingsPath = join(homedir(), ".config", "mcp-feedback-enhanced", "ui_settings.json");
13
+ // 面板在浏览器环境无 fs,靠 WS run_command 读写 ui_settings.json;settingsPath 仅用于下方日志输出。
14
+ const { settingsPath } = buildMcpFollowupReplacements();
15
15
  const {
16
16
  workbenchDir,
17
17
  workbenchHtml,
@@ -113,8 +113,8 @@ async function main() {
113
113
 
114
114
  await copyFile(guardSource, guardTarget);
115
115
 
116
- // MCP 面板:不是单纯拷贝,需把 __MCP_SETTINGS_PATH__ 占位符替换成真实路径后再写入。
117
- const followupCode = (await readFile(followupSource, "utf8")).replace("__MCP_SETTINGS_PATH__", settingsPath);
116
+ // MCP 面板:不是单纯拷贝,需按平台把占位符(路径/命令)替换后再写入。
117
+ const followupCode = applyMcpFollowupReplacements(await readFile(followupSource, "utf8"));
118
118
  await writeFile(followupTarget, followupCode);
119
119
 
120
120
  // 输入法回车修复:无占位符,直接拷贝。