cursor-feedback 2.7.1 → 2.7.2
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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/README_CN.md +1 -1
- package/dist/cli-launcher.js +6 -111
- package/dist/mcp-server.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.7.2](https://github.com/jianger666/cursor-feedback-extension/compare/v2.7.1...v2.7.2) (2026-07-07)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **cli:** 移除所有 Max Mode 限制——Cursor 新定价按 token 计费,不再需要强制 maxMode=false 和配置守护 ([6724230](https://github.com/jianger666/cursor-feedback-extension/commit/67242301436ce0953d6fff6a86bfec93ab928587))
|
|
11
|
+
|
|
5
12
|
### [2.7.1](https://github.com/jianger666/cursor-feedback-extension/compare/v2.7.0...v2.7.1) (2026-07-07)
|
|
6
13
|
|
|
7
14
|
|
package/README.md
CHANGED
|
@@ -251,7 +251,7 @@ Send these directly in the bot chat:
|
|
|
251
251
|
| `/model [modelId]` | Show / set the session model (persisted) |
|
|
252
252
|
| `/help` | Show command usage |
|
|
253
253
|
|
|
254
|
-
- Sessions run in **non-interactive mode
|
|
254
|
+
- Sessions run in **non-interactive mode** with the model specified via `--model` flag.
|
|
255
255
|
- The launched agent talks to you through this extension's feedback cards: confirmations and progress reports are pushed to Feishu; just reply to the cards.
|
|
256
256
|
- Put custom rules in `~/.cursor-feedback/cli-rules.md` (e.g. "always reply in Chinese"); they are injected into every `/new` session.
|
|
257
257
|
- When a session ends (done / error / `/stop` / 3-hour cap), a wrap-up message with the final output is sent to Feishu.
|
package/README_CN.md
CHANGED
|
@@ -255,7 +255,7 @@ npm install -g cursor-feedback
|
|
|
255
255
|
| `/model [模型id]` | 查看 / 设置会话模型(持久化) |
|
|
256
256
|
| `/help` | 查看命令用法 |
|
|
257
257
|
|
|
258
|
-
-
|
|
258
|
+
- 会话以**非交互模式**运行,通过 `--model` 参数指定模型。
|
|
259
259
|
- 拉起的 Agent 也通过本插件的反馈卡片与你沟通:需要确认、汇报进展都会推到飞书,直接回复卡片即可。
|
|
260
260
|
- 可在 `~/.cursor-feedback/cli-rules.md` 写自定义规则(如「永远用中文」),每次 `/new` 自动注入。
|
|
261
261
|
- 会话结束(完成 / 异常 / `/stop` / 超 3 小时兜底终止)都会发飞书收尾消息附最终输出。
|
package/dist/cli-launcher.js
CHANGED
|
@@ -38,20 +38,6 @@ exports.CliLauncher = void 0;
|
|
|
38
38
|
* CLI 会话拉起器:飞书 /new 命令 → 非交互模式 spawn cursor-agent。
|
|
39
39
|
*
|
|
40
40
|
* 关键设计(均来自实测结论,勿随意更改):
|
|
41
|
-
* - 默认模型用**非 Max 变体**(见 DEFAULT_MODEL 注释)。
|
|
42
|
-
* - spawn 前每次都强制重写 ~/.cursor/cli-config.json:maxMode=false + 目标模型,
|
|
43
|
-
* 并删除 selectedModel 残留——三件缺一不可,不能信任上次的状态。
|
|
44
|
-
* 实测机制(2026-07-07 文件监视 + 前日账单交叉验证):
|
|
45
|
-
* 1. CLI 启动约 5~10s 内会把 --model 解析结果持久化回 cli-config.json 的
|
|
46
|
-
* selectedModel(含 effort 参数),会话结束时再写一次——任何一次会话
|
|
47
|
-
* (含 IDE 终端手动跑的)都会留下与该会话模型匹配的残留;
|
|
48
|
-
* 2. 本次会话的实际计费档取决于启动前的文件状态:干净状态(无 selectedModel、
|
|
49
|
-
* maxMode=false)时即使 --model 传 -max 模型也按非 Max 计 1 次;
|
|
50
|
-
* 但残留 selectedModel(effort=max) 在场时同样命令扣 40+ 并带 MAX 标签。
|
|
51
|
-
* 3. 残留与 --model 冲突时 --model 赢(实验:残留 effort=xhigh + --model low
|
|
52
|
-
* → 会话按 low 跑)——所以用非 Max 模型时即使清理和别的进程写残留发生竞态
|
|
53
|
-
* 也不会翻车;仅「用户显式选 -max 模型 + 恰好有并发进程写入 max 残留」
|
|
54
|
-
* 这一极小竞态窗口无法从我们这侧根除。
|
|
55
41
|
* - 必须用非交互 print 模式(-p):交互式 TUI 会持久改写 cli-config.json 的模型选择。
|
|
56
42
|
* - CLI 不读 IDE 的全局 User Rules,用户的个人规则要显式注入到 prompt 里
|
|
57
43
|
* (从 ~/.cursor-feedback/cli-rules.md 读取,没有则只注入 cursor-feedback 沟通协议)。
|
|
@@ -61,6 +47,11 @@ exports.CliLauncher = void 0;
|
|
|
61
47
|
* mcp.json(IDE 却能容忍),spawn 前做一次归一化。
|
|
62
48
|
* - 拉起的 agent 会通过全局注册的 cursor-feedback MCP 发飞书卡片,用户在手机上
|
|
63
49
|
* 直接和这个会话对话——本模块只负责拉起和收尾,过程中的交互走既有反馈链路。
|
|
50
|
+
*
|
|
51
|
+
* 计费说明(2026-07 Cursor 新定价):
|
|
52
|
+
* - Cursor 已从按请求次数计费全面转向按 token 计费(个人/Teams 均是)。
|
|
53
|
+
* - Max Mode 只影响上下文窗口大小(token 消耗更多),不再有"固定 1 次 vs N 次"的区别。
|
|
54
|
+
* - 因此不再干预 cli-config.json 的 maxMode/selectedModel——用户自由选择模型和档位。
|
|
64
55
|
*/
|
|
65
56
|
const child_process_1 = require("child_process");
|
|
66
57
|
const fs = __importStar(require("fs"));
|
|
@@ -204,7 +195,7 @@ class CliLauncher {
|
|
|
204
195
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
205
196
|
fs.writeFileSync(p, JSON.stringify(merged, null, 2));
|
|
206
197
|
}
|
|
207
|
-
/** 当前生效模型:env 覆盖 > /model 持久化设置 >
|
|
198
|
+
/** 当前生效模型:env 覆盖 > /model 持久化设置 > 默认 */
|
|
208
199
|
model() {
|
|
209
200
|
return (process.env.CURSOR_FEEDBACK_CLI_MODEL ||
|
|
210
201
|
this.readSettings().model ||
|
|
@@ -318,85 +309,6 @@ class CliLauncher {
|
|
|
318
309
|
// mcp.json 不存在或损坏:不是本模块的职责,交给 cursor-agent 自己报错
|
|
319
310
|
}
|
|
320
311
|
}
|
|
321
|
-
static cliConfigPath() {
|
|
322
|
-
return path.join(os.homedir(), '.cursor', 'cli-config.json');
|
|
323
|
-
}
|
|
324
|
-
/** 配置里是否存在会触发 Max 计费的状态(两个独立触发源,任一命中即危险) */
|
|
325
|
-
static hasMaxResidue(cfg) {
|
|
326
|
-
if (cfg.maxMode === true)
|
|
327
|
-
return true;
|
|
328
|
-
const sel = cfg.selectedModel;
|
|
329
|
-
if (sel?.parameters?.some((p) => p?.id === 'effort' && p?.value === 'max'))
|
|
330
|
-
return true;
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* spawn 前强制写 cli-config.json:maxMode=false + 目标模型 + 删除 selectedModel。
|
|
335
|
-
* 每次都写——上次会话(含用户在终端手动跑的)会留下残留,状态不可信。
|
|
336
|
-
*/
|
|
337
|
-
ensureMaxModeOff() {
|
|
338
|
-
const p = CliLauncher.cliConfigPath();
|
|
339
|
-
let cfg = {};
|
|
340
|
-
try {
|
|
341
|
-
cfg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
// 文件不存在或损坏时从空对象重建(cursor-agent 会补齐其余字段)
|
|
345
|
-
}
|
|
346
|
-
cfg.maxMode = false;
|
|
347
|
-
cfg.model = { modelId: this.model(), maxMode: false };
|
|
348
|
-
cfg.hasChangedDefaultModel = true;
|
|
349
|
-
// 清掉 CLI 自己写入的 selectedModel(Max 模型会话会留下 effort=max 的残留,
|
|
350
|
-
// 不删的话下次会话可能沿用 Max 档计费)
|
|
351
|
-
delete cfg.selectedModel;
|
|
352
|
-
fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
|
|
353
|
-
clog(`cli-config.json 已写入 maxMode=false, model=${this.model()},并清除 selectedModel`);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* 会话运行期间的配置守护:监听 cli-config.json,一旦出现 Max 残留立即改回干净状态。
|
|
357
|
-
* 动机:spawn 前的清理只保证「启动瞬间」干净;会话运行中 CLI 自己会把当前模型写回
|
|
358
|
-
* selectedModel(-max 模型就是 effort=max),若 CLI 存在断线重连等重读配置的路径,
|
|
359
|
-
* 残留可能被中途拾取。守护用 fs.watch 事件监听做到毫秒级响应,再加 1s 轮询兜底
|
|
360
|
-
* (fs.watch 少数场景会漏事件)。只在「危险」时才写(maxMode=true 或 effort=max),
|
|
361
|
-
* 非 Max 状态一律不动,避免与 CLI 的正常写回互相打架。
|
|
362
|
-
* @returns 停止守护的清理函数(会话结束时调用)
|
|
363
|
-
*/
|
|
364
|
-
startConfigGuard() {
|
|
365
|
-
const p = CliLauncher.cliConfigPath();
|
|
366
|
-
const checkAndClean = () => {
|
|
367
|
-
try {
|
|
368
|
-
const cfg = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
369
|
-
if (CliLauncher.hasMaxResidue(cfg)) {
|
|
370
|
-
this.ensureMaxModeOff();
|
|
371
|
-
clog('配置守护:检测到 Max 残留被写回,已立即清除');
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
catch {
|
|
375
|
-
// 文件正被写入(半包 JSON)或暂不可读:跳过,等下一次事件/轮询
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
// 监听目录而不是文件本身:CLI 若用「写临时文件再 rename」的原子写法,
|
|
379
|
-
// 直接 watch 文件会在第一次替换后失去目标(inode 变了)
|
|
380
|
-
let watcher = null;
|
|
381
|
-
try {
|
|
382
|
-
watcher = fs.watch(path.dirname(p), (_event, filename) => {
|
|
383
|
-
// filename 在个别平台可能为 null,此时也检查一次(checkAndClean 幂等且廉价)
|
|
384
|
-
if (!filename || filename === path.basename(p))
|
|
385
|
-
checkAndClean();
|
|
386
|
-
});
|
|
387
|
-
watcher.on('error', () => {
|
|
388
|
-
// 监听挂掉不影响安全性,轮询兜底还在
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
catch {
|
|
392
|
-
// 平台不支持 fs.watch 时只靠轮询
|
|
393
|
-
}
|
|
394
|
-
const timer = setInterval(checkAndClean, 1000);
|
|
395
|
-
return () => {
|
|
396
|
-
clearInterval(timer);
|
|
397
|
-
watcher?.close();
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
312
|
/** 用户自定义注入规则:~/.cursor-feedback/cli-rules.md(可选) */
|
|
401
313
|
readUserRules() {
|
|
402
314
|
try {
|
|
@@ -439,13 +351,6 @@ class CliLauncher {
|
|
|
439
351
|
if (!this.tryAcquireLock(task)) {
|
|
440
352
|
return '已有一个 CLI 会话在运行:' + (this.describe() || '(另一个窗口实例刚刚抢先拉起)');
|
|
441
353
|
}
|
|
442
|
-
try {
|
|
443
|
-
this.ensureMaxModeOff();
|
|
444
|
-
}
|
|
445
|
-
catch (e) {
|
|
446
|
-
this.releaseLock();
|
|
447
|
-
return '写入 cli-config.json 失败:' + e;
|
|
448
|
-
}
|
|
449
354
|
this.normalizeMcpJsonEnv();
|
|
450
355
|
const bin = this.findBinary();
|
|
451
356
|
if (!bin) {
|
|
@@ -502,13 +407,10 @@ class CliLauncher {
|
|
|
502
407
|
clog('会话超过时长上限,强制终止');
|
|
503
408
|
CliLauncher.killTree(child, 'SIGKILL');
|
|
504
409
|
}, CliLauncher.SESSION_MAX_MS);
|
|
505
|
-
// 会话全程守护 cli-config.json,Max 残留一出现就清(防 CLI 中途重读配置拾取残留)
|
|
506
|
-
const stopConfigGuard = this.startConfigGuard();
|
|
507
410
|
child.stdout?.on('data', (chunk) => { stdout = append(stdout, chunk); });
|
|
508
411
|
child.stderr?.on('data', (chunk) => { stderr = append(stderr, chunk); });
|
|
509
412
|
const finish = (code) => {
|
|
510
413
|
clearTimeout(killTimer);
|
|
511
|
-
stopConfigGuard();
|
|
512
414
|
const elapsedMs = Date.now() - this.startedAt;
|
|
513
415
|
// 跨实例 /stop 会在锁上打 stopRequested 标记再杀进程,这里一并算「主动终止」
|
|
514
416
|
const lockAtFinish = this.readLock();
|
|
@@ -620,13 +522,6 @@ class CliLauncher {
|
|
|
620
522
|
}
|
|
621
523
|
}
|
|
622
524
|
exports.CliLauncher = CliLauncher;
|
|
623
|
-
/**
|
|
624
|
-
* 默认模型:用非 Max 变体(2026-07-06 账单实测)。`-max` 后缀模型(显示名
|
|
625
|
-
* "Fable 5 1M Max Thinking")跑过一次后,CLI 会把 selectedModel(effort=max)
|
|
626
|
-
* 持久化进 cli-config.json,这份残留不清则后续会话按 Max 计费(实测扣 40+)。
|
|
627
|
-
* ensureMaxModeOff 已做残留清理,但默认模型仍选 thinking-xhigh(非 Max 最高档,
|
|
628
|
-
* 固定扣 1 次)从根上避开这类状态机风险。
|
|
629
|
-
*/
|
|
630
525
|
CliLauncher.DEFAULT_MODEL = 'claude-fable-5-thinking-xhigh';
|
|
631
526
|
/** 会话时长兜底:防止无人回复的 headless 会话无限续期挂着 */
|
|
632
527
|
CliLauncher.SESSION_MAX_MS = 3 * 60 * 60 * 1000;
|
package/dist/mcp-server.js
CHANGED
|
@@ -497,11 +497,11 @@ class McpFeedbackServer {
|
|
|
497
497
|
}
|
|
498
498
|
/**
|
|
499
499
|
* 处理飞书斜杠命令。返回是否已消费该消息。
|
|
500
|
-
* - /new [目录或项目名] 任务描述:拉起 headless CLI
|
|
500
|
+
* - /new [目录或项目名] 任务描述:拉起 headless CLI 会话(非交互模式)。
|
|
501
501
|
* 工作目录刻意保持简单:显式指定 > 主目录,与当前开着哪些 IDE 窗口无关。
|
|
502
502
|
* - /projects:列出 Cursor 打开过的项目路径(供手机上查路径 / 复制给 /new)
|
|
503
503
|
* - /stop:终止运行中的 CLI 会话
|
|
504
|
-
* - /model [模型id]:查看 / 设置 CLI
|
|
504
|
+
* - /model [模型id]:查看 / 设置 CLI 会话模型(持久化)
|
|
505
505
|
*/
|
|
506
506
|
handleCliCommand(rawText, chatId) {
|
|
507
507
|
// 输入容错:首尾空格(飞书入站已 trim,这里兜底;trim 同样覆盖全角空格 U+3000);
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "cursor-feedback",
|
|
3
3
|
"displayName": "Cursor Feedback",
|
|
4
4
|
"description": "One Cursor conversation, unlimited AI interactions - Save your monthly request quota! Interactive feedback loop for AI chat via MCP",
|
|
5
|
-
"version": "2.7.
|
|
5
|
+
"version": "2.7.2",
|
|
6
6
|
"icon": "icon.png",
|
|
7
7
|
"author": "jianger666",
|
|
8
8
|
"license": "MIT",
|