cursor-guard 4.4.1 → 4.5.4

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/ROADMAP.md CHANGED
@@ -3,8 +3,8 @@
3
3
  > 本文档描述 cursor-guard 从 V2 到 V7 的长期演进方向。
4
4
  > 每一代向下兼容,低版本功能永远不废弃。
5
5
  >
6
- > **当前版本**:`V4.4.0`(V4 收官版)
7
- > **文档状态**:`V2` ~ `V4.4.0` 已实施(含 V5 intent/audit 基础),`V5` 主体规划中
6
+ > **当前版本**:`V4.5.4`(V4 最终版)
7
+ > **文档状态**:`V2` ~ `V4.5.4` 已完成交付(含 V5 intent/audit 基础),`V5` 主体规划中
8
8
 
9
9
  ## 阅读导航
10
10
 
@@ -20,7 +20,7 @@
20
20
  |---|---|---|---|
21
21
  | `V2` | 能用 | Skill + Script | "AI 弄丢代码能恢复" |
22
22
  | `V3` | 更稳 | + Core 抽取 + 可选 MCP | "恢复操作更标准、更省 token" |
23
- | `V4` | 更聪明 | + 主动检测 + 可观测 + Web 仪表盘 + Intent + 增量摘要 | "cursor-guard 会主动提醒你,能看到为什么备份、改了什么" ✅ |
23
+ | `V4` | 更聪明 | + 主动检测 + 可观测 + Web 仪表盘 + Intent + 增量摘要 + 安全硬化 | "cursor-guard 会主动提醒你,能看到为什么备份、改了什么" ✅ |
24
24
  | `V5` | 成闭环 | + 变更控制层 | "AI 代码变更可预防、可追溯、可按事件恢复"(intent 基础已在 V4.3 落地) |
25
25
  | `V6` | 成标准 | + 开放协议 + 团队工作流 | "把 AI 代码变更安全做成跨工具标准" |
26
26
  | `V7` | 可证明 | + 可验证信任 + 治理层 | "能证明安全流程被执行了" |
@@ -457,15 +457,210 @@ V4 经过 4 轮系统性代码审查,修复了以下关键问题:
457
457
  | V4.3.4 | **运维加固**:`backup.log` 日志轮转(1MB / 3 文件);watcher 单实例保护加固(锁文件时间戳 + 24h 超时);`previewProjectRestore` 保护路径分组摘要(降低 token 消耗);SKILL.md 硬规则 #15(升级后提交 skill 文件) | ✅ |
458
458
  | V4.3.5 | **Summary 准确性修复 + UI 优化**:备份摘要改用 `diff-tree` 增量对比(修复 porcelain 假摘要 bug);仪表盘变更列三行堆叠布局;配色全面优化(背景层级 / 状态色 / 文字层级) | ✅ |
459
459
  | V4.4.0 | **V4 收官版**:首次快照 summary(无 parent 时生成 Added N: ...);doctor 新增 Git retention 警告(>500 commits + disabled)和 Backup integrity 校验(`cat-file -t` tree 可达性);`cursor-guard-init` 升级检测(已有配置提示) | ✅ |
460
+ | V4.4.1 | **安全硬化版(5 项审计修复 + UX 优化)**:见下方详细说明 | ✅ |
461
+ | V4.5.0 | **V4 最终版(异常检测修复 + Dashboard 全面升级)**:见下方详细说明 | ✅ 收官 |
462
+ | V4.5.2 | **告警结构化文件列表**:见下方详细说明 | ✅ |
463
+ | V4.5.3 | **告警历史 UX 优化 + 备份结构化文件表格**:见下方详细说明 | ✅ |
464
+ | V4.5.4 | **Shadow 硬链接增量优化 + always_watch 强保护模式**:见下方详细说明 | ✅ |
465
+
466
+ #### V4.4.1 详细内容
467
+
468
+ **安全修复(2×P1 + 3×P2)**:
469
+
470
+ | 级别 | 问题 | 修复 |
471
+ |------|------|------|
472
+ | P1 | `restoreFile` 接受目录 pathspec(`src`、`.cursor`)和项目根(`.`),单文件 API 能恢复整个目录或回滚受保护资产 | `validateRelativePath` 拦截 `.` 和空路径;`isToolPath` 匹配裸 `.cursor`;git 路径用 `cat-file -t` 验证必须是 blob(文件),tree(目录)直接拒绝;shadow 路径用 `statSync` 拦截目录 |
473
+ | P1 | `restore_project` 对 HEAD 已删除的受保护文件不处理,旧快照会把它们复活 | 恢复前用 `ls-tree HEAD -- <path>` 检查存在性,HEAD 中不存在的路径用 `rmSync/unlinkSync` 清除 |
474
+ | P2 | Git retention 重建链只保留 subject(`%s`),丢失 V4.3 审计 trailers | 改用 `%B`(完整 body + trailers),重建用 `fullBody` 传入 `commit-tree -m` |
475
+ | P2 | Dashboard 仅绑定 127.0.0.1 但无 Host/token 防护,可被 DNS rebinding 读取 | 加 Host header 校验(`127.0.0.1`/`localhost`)+ per-process 随机 token 注入 index.html,API 请求必须携带 |
476
+ | P2 | `doctor_fix` 初始化 Git 时 `git add -A` 会提交 `node_modules/` | 在 `git add -A` 前写入 `node_modules/` 和 `.cursor/skills/**/node_modules/` 到 `.gitignore` |
477
+
478
+ **回归测试**:新增 3 条负例锁定 restore 防线(目录 pathspec / 受保护 .cursor / 项目根 `.`),总测试 143/143 全绿。
479
+
480
+ **Dashboard UX**:
481
+ - 骨架屏加载(shimmer 占位,消除白屏→弹出的突兀感)
482
+ - 渐进渲染(`page-data?scope=` 按需返回,overview 先渲染,backups+doctor 并行加载)
483
+ - 备份 summary 行级统计(`git diff-tree --numstat`,每文件 `(+N -M)`),分行显示
484
+ - Summary 可见性提升(12px + secondary 颜色 + monospace 字体)
485
+ - 去除变更列冗余 trigger badge(类型列已有)
486
+ - Pre-restore 快照记录恢复方向(`From: <HEAD短hash>`、`Restore-To: <目标短hash>`、`File: <文件路径>`),表格琥珀色显示 `ab1b45d → f4029e9`
487
+
488
+ **架构级防护缺口修复**:
489
+ - MCP 工具注入 watcher 未运行警告(`_warning` 字段),AI 第一次调任何工具就能看到保护缺口
490
+ - SKILL.md Hard Rule #1 升级:"任何文件写入/删除前必须 snapshot"(之前仅要求"高风险操作前")
491
+ - SKILL.md 新增 Hard Rule #3a:"必须检查 watcher 状态"——看到 `_warning` 必须告知用户
492
+
493
+ #### V4.5.0 详细内容
494
+
495
+ **Bug 修复**:
496
+
497
+ | 问题 | 根因 | 修复 |
498
+ |------|------|------|
499
+ | 异常检测 `changedFileCount` 虚高 | `auto-backup.js` 用 `git status --porcelain`(对比 HEAD)计数,而非对比上一次备份的增量 | 改用 `createGitSnapshot` 返回的 `changedCount`(来自 `diff-tree`),异常检测和 Summary 共享同一数据源。移除了未使用的 `execFileSync` 和 `unquoteGitPath` 导入 |
500
+ | 诊断锁文件状态判断不够智能 | `doctor.js` Lock file 检测只要存在就报 WARN,不区分 watcher 是否在运行 | 加入 PID 存活判断(`process.kill(pid, 0)`):PID 在线 → PASS(`watcher running`);PID 已死 → WARN(`stale lock file`);无 PID → WARN(兜底)。前端 i18n 同步补全 `detail.lock_running` / `detail.lock_stale` / `detail.lock_exists` |
501
+
502
+ **Dashboard 升级(10 项改进)**:
503
+
504
+ | 优先级 | 改进 | 说明 |
505
+ |--------|------|------|
506
+ | 高 | 告警卡片补全 | 显示触发时间、过期倒计时(实时递减)、具体数字(N 文件 / N 秒 / 阈值 N) |
507
+ | 高 | 告警历史 | 保留最近 20 条告警记录,即使过期也显示在卡片下方(最近 5 条) |
508
+ | 中 | 文件搜索框 | 备份表格上方输入框,按文件名/意图/摘要实时过滤相关备份 |
509
+ | 中 | 恢复命令复制 | 抽屉底部显示 `restore_project` 和 `restore_file` MCP 命令,一键复制 |
510
+ | 低 | 筛选按钮计数 | "Git 自动备份 (12)" 而非仅 "Git 自动备份";无数据的类型自动隐藏 |
511
+ | 低 | Watcher 最后扫描 | 卡片增加 "最后扫描: 3s 前",确认 watcher 实际在工作 |
512
+ | 中 | 摘要展开/收起 | 变更摘要超过 2 行时自动折叠,显示 `+N more` 按钮点击展开;避免行过长截断,无需进抽屉即可看全 |
513
+ | 修复 | `showLoading` 引用 | 项目切换时调用了不存在的 `showLoading()`,改为 `showSkeleton()` |
514
+ | 优化 | i18n 补全 | 新增 14 个双语 key(告警详情、告警历史、文件搜索、恢复命令、扫描时间) |
460
515
 
461
516
  > **注**:V4.2 的 Web 仪表盘最初在 V4.0 规划中标记为"不做",但用户需求明确后实施。事实证明只读仪表盘投入产出比合理,且不违反安全原则。
462
517
 
518
+ #### V4.5.2 详细内容
519
+
520
+ **告警结构化文件列表**:
521
+
522
+ | 层 | 改动 | 说明 |
523
+ |----|------|------|
524
+ | Core | `snapshot.js` 返回 `changedFiles` 数组 | 每项包含 `{ path, action, added, deleted }`,数据来源 `diff-tree --numstat`,按变化量降序排列 |
525
+ | Core | `auto-backup.js` 透传 `changedFiles` | `createGitSnapshot` → `recordChange(tracker, count, changedFiles)` |
526
+ | Core | `anomaly.js` alert 携带 `files` 字段 | 窗口内多次事件的文件按路径去重合并,最多保留 50 条,`saveAlert` 持久化到磁盘 |
527
+ | Dashboard | 告警卡片可展开文件详情表格 | 点击"展开文件详情"显示排序表格(文件路径 / 操作类型 / 变化量),操作类型用彩色 badge 区分(修改=蓝 / 新增=绿 / 删除=红 / 重命名=紫) |
528
+ | Dashboard | i18n 补全 | 新增 8 个双语 key(showFiles / hideFiles / col.file / col.action / col.changes / action.*) |
529
+
530
+ > 22 个文件被删除和 22 个文件被新增的风险完全不同——结构化文件列表让用户一眼判断严重程度。
531
+
532
+ #### V4.5.3 详细内容
533
+
534
+ **告警历史 UX 修复**:
535
+
536
+ | 问题 | 修复 | 实现细节 |
537
+ |------|------|----------|
538
+ | 告警过期后,"无活跃告警"绿色状态下方直接展示历史记录,上面说没事、下面列了两条记录,信息矛盾 | 历史默认完全隐藏,只显示灰色"历史(N 条)"可点击文字,展开后才显示历史列表 | `renderAlertCard` 无活跃告警分支:新增 `alert-history-toggle-btn`(灰色 11px 可点击按钮)+ `.alert-history-collapsed`(CSS `display:none`)。事件委托 `[data-alert-history-toggle]` 绑定在 `#card-alert` 上,toggle `alert-history-collapsed` class |
539
+
540
+ 告警卡片状态设计:
541
+
542
+ ```
543
+ 无告警时:
544
+ ✅ 无活跃告警
545
+ 历史(2 条) ← 灰色小字,点击展开
546
+
547
+ 有告警时:
548
+ ⚠ 活跃告警
549
+ 13:03:54 触发 · 剩余 1m 53s
550
+ 22 个文件在 10 秒内变更(阈值:20)
551
+ [展开文件详情]
552
+ ```
553
+
554
+ **备份结构化文件表格**:
555
+
556
+ | 层 | 改动 | 说明 |
557
+ |----|------|------|
558
+ | Core | `backups.js` 新增 `getBackupFiles(projectDir, commitHash)` | 对指定 commit 运行 `diff-tree --numstat + --name-status`,返回 `[{path, action, added, deleted}]`,按变化量降序。支持 rename 解析(`R` 前缀→取 tab 分割的最后一段)。无 parent 时退化为 `ls-tree` 列出全部文件 |
559
+ | Server | `GET /api/backup-files?id=<project>&hash=<commit>` | 懒加载端点,不在 `list_backups` 中批量计算(50 条备份×`diff-tree` 会很慢)。400 校验 `hash` 必填 |
560
+ | Dashboard | `parseSummaryToFiles(summary)` | 解析 summary 文本格式 `"Modified 3: a.js (+2 -1), b.js (+0 -5), ...; Added 2: c.js (+10 -0)"` → `[{path, action, added, deleted}]`。正则匹配 `(Modified|Added|Deleted|Renamed) N:` 段头,逐文件解析 `filename (+N -M)`,自动跳过 `...` 截断标记 |
561
+ | Dashboard | `fetchBackupFiles(hash)` | 调用 `/api/backup-files` 端点,返回完整文件数组。网络失败静默降级(返回空数组) |
562
+ | Dashboard | 备份表 `formatSummaryCell` | 行内 mini 文件表格:`parseSummaryToFiles` 取前 3 个文件,每文件显示路径(mono 字体 11px,`max-width:220px` 省略号截断)+ 操作 badge(彩色)+ `+N -M`。超出显示"等 N 个文件…"(斜体灰色),`N` 取 `filesChanged` 字段(当 summary 有 `...` 截断时)或实际剩余数 |
563
+ | Dashboard | 抽屉 `openRestoreDrawer` | summary 字段不再用 `<pre>` 文本,改为懒加载可排序文件表格。打开抽屉 → `fetchBackupFiles(hash)` → `renderDrawerFilesTable(files, sortKey)`。三列表头均可点击排序(path=字典序 / action=字母序 / changes=变化量降序),当前排序列高亮。API 失败时降级为 `parseSummaryToFiles` 本地解析 |
564
+ | Dashboard | `renderDrawerFilesTable(files, sortKey)` | 可排序表格渲染函数:sticky 表头、340px 最大高度滚动区域、表头带 ↕ 排序指示器、行内复用 `formatFileActionBadge` 统一操作 badge |
565
+ | Dashboard | i18n 补全 | 新增 `summary.andMore`("and {n} more…" / "等 {n} 个文件…")、`alert.historyCount`("History ({n})" / "历史({n} 条)") |
566
+
567
+ > 同一个 `getBackupFiles` 数据结构,告警详情和备份详情两个地方同时受益。备份表行内看 3 个关键文件,抽屉里看完整列表——信息层级清晰,不再一行文本挤 22 个文件名。
568
+
569
+ #### V4.5.4 详细内容
570
+
571
+ **Shadow 硬链接增量优化**:
572
+
573
+ | 层 | 改动 | 说明 |
574
+ |----|------|------|
575
+ | Core | `snapshot.js` `findPreviousSnapshot(backupDir)` | 新增辅助函数。扫描 backupDir 下所有 `YYYYMMDD_HHMMSS` 格式目录,按时间戳降序排列,返回最新的 snapshot 目录路径(`isDirectory` 校验) |
576
+ | Core | `snapshot.js` `createShadowCopy` 硬链接逻辑 | 备份循环中,对每个文件执行增量判断:① 读取源文件 `stat`(size + mtimeMs)② 读取上一个 snapshot 同路径文件 `stat` ③ 若 size 完全一致且 mtimeMs 差值 < 1ms → `fs.linkSync(prevFile, dest)` 硬链接 ④ 否则 → `fs.copyFileSync(src, dest)` + `fs.utimesSync(dest, atime, mtime)` 保留源文件时间戳 |
577
+ | Core | mtime 保留策略 | 每次 `copyFileSync` 后立即 `utimesSync` 将源文件的 mtime 同步到目标。这确保下一次 shadow 备份时,"上一个 snapshot 的文件 mtime" = "当时源文件的 mtime",硬链接比较才有基准。`utimesSync` 失败不阻塞备份(`try-catch` 静默) |
578
+ | Core | 跨卷容错 | `fs.linkSync` 在跨文件系统(如 backupDir 在不同磁盘卷)或 FAT32 分区上会抛 `EXDEV` 错误。外层 `try-catch` 捕获后自动 fall back 到 `copyFileSync`,不影响备份正确性 |
579
+ | Watcher | `auto-backup.js` 日志 | Shadow 日志增加硬链接统计:`Shadow copy 20260322_130000 (150 files [142 hard-linked])`。linkedCount = 0 时不显示 |
580
+ | 返回值 | `createShadowCopy` 新增 `linkedCount` 字段 | 表示本次备份中通过硬链接节省 I/O 的文件数。用于日志、Dashboard 未来可展示 |
581
+
582
+ 性能对比:
583
+
584
+ | 场景 | 文件总数 | 变更数 | 传统全量 copy | 硬链接增量 | 磁盘节省 |
585
+ |------|---------|--------|--------------|-----------|---------|
586
+ | 常规开发 | 150 | 8 | 150 次 copy | 8 copy + 142 link | ~95% I/O |
587
+ | 大规模重构 | 150 | 50 | 150 次 copy | 50 copy + 100 link | ~67% I/O |
588
+ | 首次备份 | 150 | 150 | 150 次 copy | 150 次 copy(无上一个 snapshot) | 0%(正常) |
589
+
590
+ > 硬链接在 NTFS(Windows)和 ext4/APFS(Linux/macOS)上均支持。同一 inode 共享磁盘块,150 个文件只改 8 个时磁盘写入降 95%。
591
+
592
+ **always_watch 强保护模式**:
593
+
594
+ | 层 | 改动 | 说明 |
595
+ |----|------|------|
596
+ | Config | `utils.js` DEFAULT_CONFIG | 新增 `always_watch: false` 默认配置项 |
597
+ | Config | `utils.js` loadConfig | 解析 `.cursor-guard.json` 中的 `always_watch` 布尔值。类型校验:非 `true`/`false` 值 → 警告 + 使用默认值 `false` |
598
+ | MCP | `mcp/server.js` `watchedProjects` Map | 进程级 Map,`key = projectPath`,`value = { pid, external }`。追踪已启动/检测到的 watcher,防止重复 spawn |
599
+ | MCP | `mcp/server.js` `ensureWatcher(projectPath)` | 自动 watcher 管理器,执行流程:① `watchedProjects.has(path)` → 已处理过则跳过 ② `loadConfig(path)` → 检查 `always_watch` 是否为 `true` ③ `isWatcherRunning(path)` → 已有外部 watcher 则标记 `external: true` 跳过 ④ `spawn(process.execPath, [cursor-guard-backup, --path, path])` → 创建 detached 子进程(`detached: true, stdio: 'ignore', windowsHide: true`) ⑤ `child.unref()` → 父进程退出不影响 watcher |
600
+ | MCP | 所有 9 个 tool handler | 入口处统一调用 `ensureWatcher(resolved)`。仅首次调用触发实际逻辑,后续调用通过 Map 缓存直接跳过(O(1)) |
601
+ | 安全 | 与现有锁文件机制兼容 | `ensureWatcher` 先检查 `isWatcherRunning`(读取 lock file + `process.kill(pid, 0)` PID 存活检测),不会在已有 watcher 时重复启动。手动启动的 watcher 和 auto-spawn 的 watcher 使用同一个 lock file,互斥保护 |
602
+
603
+ 用户配置方式:
604
+ ```json
605
+ {
606
+ "always_watch": true
607
+ }
608
+ ```
609
+
610
+ 两种保护模式对比:
611
+
612
+ | | 轻量模式(默认) | 强保护模式 |
613
+ |---|---|---|
614
+ | 配置 | `always_watch: false`(或不设) | `always_watch: true` |
615
+ | Watcher 启动 | 手动 `cursor-guard-backup` | MCP server 首次 tool 调用自动 spawn |
616
+ | 保护覆盖 | AI 需手动 `snapshot_now` | 全程自动备份,零保护缺口 |
617
+ | 适用场景 | 小项目、低频编辑 | 重要项目、高频 AI 协作 |
618
+ | 资源开销 | 无后台进程 | 后台 watcher 进程(低 CPU,周期性扫描) |
619
+
620
+ > 这个特性直接填补了 V4 最大的架构缺口——"Watcher 停止 = 裸奔"。详见下方"V4 遗留的架构缺口"中该条目已标记为 **已解决**。
621
+
622
+ #### V4.5.x 新增配置参考
623
+
624
+ | 字段 | 类型 | 默认值 | 引入版本 | 说明 |
625
+ |------|------|--------|---------|------|
626
+ | `always_watch` | `boolean` | `false` | V4.5.4 | 强保护模式。设为 `true` 后,MCP server 首次 tool 调用自动启动 watcher 进程 |
627
+
628
+ 完整 `.cursor-guard.json` 配置示例(含 V4.5.4 新增项):
629
+
630
+ ```json
631
+ {
632
+ "protect": ["src/**", "*.config.js"],
633
+ "ignore": ["node_modules/**", "dist/**"],
634
+ "backup_strategy": "git",
635
+ "auto_backup_interval_seconds": 60,
636
+ "always_watch": true,
637
+ "proactive_alert": true,
638
+ "alert_thresholds": {
639
+ "files_per_window": 20,
640
+ "window_seconds": 10,
641
+ "cooldown_seconds": 60
642
+ }
643
+ }
644
+ ```
645
+
463
646
  ### V4 不做的事
464
647
 
465
648
  - 不做自动恢复(恢复永远需要人确认,这是产品底线)
466
649
  - ~~不做 Web 仪表盘~~ → V4.2 已实施(只读、本地、零依赖)
467
650
  - 不做云端同步
468
651
 
652
+ ### V4 遗留的架构缺口(V5 接手)
653
+
654
+ 通过 V4.4.1 的安全审计和真实场景测试,发现以下架构层面的保护缺口。这些不是代码 bug,而是设计边界:
655
+
656
+ | 缺口 | 现状 | 影响 | V5 改进方向 | 状态 |
657
+ |------|------|------|------------|------|
658
+ | ~~**Watcher 停止 = 裸奔**~~ | ~~Watcher 不运行期间无自动备份~~ | ~~变更永久丢失~~ | ~~`always_watch` 配置项~~ | ✅ **V4.5.4 已解决**:`always_watch: true` 时 MCP server 首次 tool 调用自动 spawn watcher 进程,与现有锁文件互斥机制兼容 |
659
+ | **保护依赖 AI 自觉** | SKILL.md 要求 AI 在写入前 snapshot,但没有强制机制 | AI 不遵守协议就直接写,保护形同虚设 | **embedded watcher + `begin_edit` 意图绑定**:MCP server 内嵌 watcher 循环 + 按文件路径匹配 intent,消除进程边界和并发竞争(详见 V5 设计) | 🔮 V5 |
660
+ | **自动备份无意图上下文** | auto-backup 只有 `trigger: auto`,不知道是谁改的、为什么改 | 事后回溯只能看到时间点快照,不知道操作意图 | **`begin_edit` → 文件路径绑定**:AI 编辑前声明意图和目标文件,embedded watcher 检测变更时按路径匹配 intent,自动备份也能带上下文 | 🔮 V5 |
661
+ | **无跨进程写拦截** | 当前 MCP 架构下无法拦截 Cursor 编辑器的文件写入 | 只能在写后检测,不能写前阻止 | 等待 MCP 协议支持 `notification` / `resource subscription`,或探索 fs watch + pre-commit 组合 | 🔮 V5+ |
662
+ | **意图队列并发问题** | 曾考虑"意图队列"(AI 写文件 → watcher 读文件关联 intent),但存在 4 类并发竞态:多 Agent 竞争、意图-变更错位、意图堆积、空意图残留 | 文件 I/O 跨进程 + 时间顺序绑定 = 不可靠 | **同进程内存 Map + 文件路径绑定**:消灭进程边界后无 IPC,按路径而非时间匹配消除歧义(详见 V5 设计) | 🔮 V5 |
663
+
469
664
  ### 进入 V5 的衡量标准
470
665
 
471
666
  - V4 的主动提醒功能误报率 < 10%
@@ -522,8 +717,101 @@ V5 不是"三个方向选一个",而是把下面这条链路做完整:
522
717
  | `impact set` | 为高风险编辑记录受影响文件 / 符号 / 测试集合 | `impact_set` 字段 | 查询事件时能看到"这次改动可能波及哪里" |
523
718
  | `MCP / CLI surface` | 暴露最小可用接口给 Agent 和终端 | `register_intent` / `list_active_intents` / `audit_query` / `get_event` / `restore_from_event` | AI 不需要拼复杂 shell,就能完成查询与恢复 |
524
719
  | `dashboard / doctor` | 把活跃会话、冲突告警、最近 AI 事件纳入诊断和看板 | `dashboard` / `doctor` 扩展字段 | 用户能看见"现在谁在改、最近改了什么、哪里有冲突" |
720
+ | `always_watch` | 配置项 `"always_watch": true`,MCP server 启动时自动内嵌 watcher 循环;用户可选两种保护模式:**轻量模式**(默认,AI 手动 `snapshot_now`)vs **强保护模式**(watcher 始终在后台,所有变更自动备份) | `.cursor-guard.json` 配置 + MCP server 启动逻辑 | MCP server 启动后,`always_watch: true` 的项目自动有 watcher 保护,无需额外命令;选择权在用户 |
721
+ | `embedded watcher` | **消灭进程边界**:不再是独立后台进程,而是 MCP server 同进程内的 watcher 循环。同进程 = 无 IPC、无文件桥接、无并发竞争。检测到文件变更时自动创建 snapshot,不依赖 AI 手动调用 | MCP server 内部模块 | watcher 停止 = 裸奔的保护缺口彻底消除;AI 忘记 snapshot 也有兜底 |
722
+ | `begin_edit` / `end_edit` | **意图-变更原子绑定**:AI 编辑前调 `begin_edit({ intent, files[], agent, session })`,在内存 `Map<session, EditScope>` 注册编辑意图和目标文件。embedded watcher 检测到变更时按**文件路径**匹配 intent,自动备份带完整上下文。`end_edit(session)` 或 TTL(默认 5 分钟)自动清除 | `begin_edit` / `end_edit` MCP 工具 + 内存 `activeEdits` Map | auto-backup 也能带 intent/agent/session;并发多 Agent 按文件路径消歧,不按时间顺序 |
525
723
  | `tests / docs` | 为事件链路补齐单测、集成测试和文档 | tests + schema docs | V5.0 的所有核心事件和恢复路径都有测试覆盖 |
526
724
 
725
+ ### V5 核心设计:Embedded Watcher + 文件路径意图绑定
726
+
727
+ #### 问题根因
728
+
729
+ V4 的自动备份(auto-backup)和意图上下文(intent)分属两个独立进程:
730
+
731
+ ```
732
+ MCP Server 进程(知道 intent) ←——×——→ Watcher 进程(知道文件变了)
733
+ ↑ ↑
734
+ AI agent 调用 fs 检测循环
735
+ 有上下文 无上下文
736
+ ```
737
+
738
+ - `auto-backup.js` 调用 snapshot 时只传 `{ trigger: 'auto', changedFileCount }`,没有 intent/agent/session
739
+ - 只有 `snapshot_now` MCP 工具支持 intent 参数
740
+ - 曾考虑"意图队列"(AI 写文件 → watcher 读文件关联),但存在 4 类并发竞态:
741
+
742
+ | 竞态场景 | 描述 |
743
+ |---------|------|
744
+ | 多 Agent 竞争 | A 提交意图 → B 提交意图 → watcher 触发 → 绑谁的? |
745
+ | 意图-变更错位 | A 提交意图但未改文件 → B 改了另一个文件 → A 的意图被错绑到 B 的变更 |
746
+ | 意图堆积 | 一个 cycle 内多个 agent 提交意图 → watcher 只产生一次 commit → 意图丢失或错配 |
747
+ | 空意图残留 | Agent 提交意图后放弃 → 意图留在队列 → 被绑到下一次无关变更 |
748
+
749
+ 根本原因:**意图提交和文件变更是两个独立事件,跨进程 + 按时间绑定 = 无法原子化**。
750
+
751
+ #### 方案:同进程 + 按文件路径绑定
752
+
753
+ ```
754
+ ┌─────────────── MCP Server 进程(V5) ──────────────────┐
755
+ │ │
756
+ │ AI agent 调用 内存 activeEdits │
757
+ │ begin_edit({ Map<session, { │
758
+ │ intent, → intent, files[], │
759
+ │ files[], agent, timestamp, │
760
+ │ agent, ttl │
761
+ │ session }> │
762
+ │ }) ↓ 直接读取(同进程) │
763
+ │ │
764
+ │ Embedded Watcher 循环 ←─── 检测到 src/auth.ts 变更 │
765
+ │ 查 activeEdits: │
766
+ │ s1 声明了 [src/auth.ts] → 匹配 ✅ │
767
+ │ s2 声明了 [src/style.css] → 不匹配 ❌ │
768
+ │ → 创建 auto-backup commit 带 s1 的 intent │
769
+ │ │
770
+ └─────────────────────────────────────────────────────────┘
771
+ ```
772
+
773
+ **核心优势**:
774
+ - **同进程**:无 IPC、无文件 I/O、无并发竞争(Node.js 单线程 event loop)
775
+ - **按文件路径绑定**:不同 Agent 改不同文件时各自匹配,无歧义
776
+ - **TTL 自动过期**:空意图 5 分钟后自动清除,不会残留
777
+ - **多 Agent 同文件**:标记为 `multi-agent overlap`,附上所有匹配意图(同时作为冲突检测信号)
778
+
779
+ #### 并发场景处理
780
+
781
+ | 场景 | 处理方式 |
782
+ |------|---------|
783
+ | A 改 `auth.ts`,B 改 `style.css` | 各自匹配各自的 `begin_edit` scope,零歧义 |
784
+ | A、B 都声明要改 `auth.ts` | `begin_edit` 时即检测到重叠,返回 advisory warning;auto-backup 附上两个 intent |
785
+ | A 声明了意图但没改文件 | TTL 到期自动清除;`end_edit` 可提前关闭 |
786
+ | 文件变更但没有任何 `begin_edit` | 降级为普通 auto-backup(和 V4 行为一致,无退化) |
787
+ | AI 直接调 `snapshot_now(intent=...)` | 和现在一模一样,完全兼容 |
788
+
789
+ #### 实现分期
790
+
791
+ ```
792
+ Phase 1 (V5.0):
793
+ ├── always_watch: true → MCP server 内嵌 watcher 循环
794
+ ├── 新增 begin_edit / end_edit MCP 工具
795
+ ├── 内存 Map<session, EditScope> 管理活跃编辑意图
796
+ └── watcher 变更检测时查 Map,按文件路径匹配 intent → 写入 commit trailer
797
+
798
+ Phase 2 (V5.x):
799
+ ├── begin_edit → 产生 intent_registered 事件(写入审计存储)
800
+ ├── 文件变更 → 产生 edit_applied 事件(关联 intent + before_ref)
801
+ ├── end_edit → 产生 intent_released 事件
802
+ └── 完整审计链闭环,支持 restore_from_event
803
+ ```
804
+
805
+ #### 与"意图队列"方案的本质区别
806
+
807
+ | | 意图队列(已否决) | embedded watcher + begin_edit |
808
+ |---|---|---|
809
+ | 通信方式 | 文件 I/O(跨进程) | 内存 Map(同进程) |
810
+ | 绑定维度 | 时间顺序(先进先出) | 文件路径(精确匹配) |
811
+ | 并发安全 | 4 类竞态条件 | 无竞态(单进程 event loop) |
812
+ | 空意图处理 | 残留在文件中 | TTL 自动过期 + `end_edit` 显式关闭 |
813
+ | 多 Agent 消歧 | 无法消歧 | 文件路径级消歧 + 冲突检测信号 |
814
+
527
815
  ### V5 主线 A:并发编辑安全(预防层)
528
816
 
529
817
  ```
@@ -650,6 +938,10 @@ V5 的关键不是"多打一行日志",而是建立完整证据链:
650
938
 
651
939
  ### V5 完成标志(Definition of Done)
652
940
 
941
+ - `always_watch: true` 配置生效后,MCP server 启动自动内嵌 watcher 循环,用户无需额外命令
942
+ - `begin_edit` / `end_edit` MCP 工具可用,AI 能声明编辑意图和目标文件
943
+ - embedded watcher 的自动备份能通过文件路径匹配关联 `begin_edit` 中的 intent/agent/session
944
+ - 无 `begin_edit` 时自动备份行为与 V4 一致(无退化)
653
945
  - AI 能在高风险写入前注册意图并创建 `pre-edit` 快照
654
946
  - 用户能查询最近一次 AI 编辑的完整上下文
655
947
  - 给定 `event_id` 能找到对应快照并执行恢复
package/SKILL.md CHANGED
@@ -571,9 +571,10 @@ Skip the block for unrelated turns.
571
571
 
572
572
  ## Hard Rules (Non-Negotiable)
573
573
 
574
- 1. **MUST snapshot before high-risk ops** — git commit or shadow copy. No exceptions unless user explicitly declines.
574
+ 1. **MUST snapshot before ANY file write or delete** — call `snapshot_now` (with `intent`) before creating, editing, or deleting files. This is NOT optional and NOT just for "high-risk" operations. Every write is potentially destructive. No exceptions unless user explicitly says "不用备份" / "skip backup". If the MCP response includes `_warning` about the watcher being stopped, tell the user immediately.
575
575
  2. **MUST Read before Write** — never overwrite a file the agent hasn't read in the current turn.
576
576
  3. **MUST preserve current version before restore** — every restore operation must first snapshot the current state (§5a Step 4). Skip ONLY when: (a) user explicitly opts out, (b) current state is identical to target, or (c) no changes exist. If preservation fails, abort restore by default.
577
+ 3a. **MUST check watcher status** — if any MCP tool returns `_warning` about the watcher not running, immediately warn the user: "自动备份守护进程未运行,当前处于无保护状态。建议先启动 watcher 或在每次修改前手动调用 snapshot_now。" Do NOT silently continue editing files without protection.
577
578
  4. **Do not** treat Timeline/Checkpoints as the only or primary recovery path.
578
579
  5. **Do not** recommend Checkpoints as long-term or sole backup.
579
580
  6. **No automatic push** to remotes; local commits only unless user requests push.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-guard",
3
- "version": "4.4.1",
3
+ "version": "4.5.4",
4
4
  "description": "Protects code from accidental AI overwrite or deletion in Cursor IDE — mandatory pre-write snapshots, review-before-apply, local Git safety net, and deterministic recovery. | 保护代码免受 Cursor AI 代理意外覆写或删除——强制写前快照、预览再执行、本地 Git 安全网、确定性恢复。",
5
5
  "keywords": [
6
6
  "cursor",