cursor-guard 4.7.3 → 4.7.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.7.3`
7
- > **文档状态**:`V2` ~ `V4.7.3` 已完成交付(含 V5 intent/audit 基础),`V5` 主体规划中
6
+ > **当前版本**:`V4.7.4`
7
+ > **文档状态**:`V2` ~ `V4.7.4` 已完成交付(含 V5 intent/audit 基础),`V5` 主体规划中
8
8
 
9
9
  ## 阅读导航
10
10
 
@@ -734,6 +734,16 @@ V4 经过 4 轮系统性代码审查,修复了以下关键问题:
734
734
  }
735
735
  ```
736
736
 
737
+ ### V4.7.4:路径解析器 + 4 Bug 修复 ✅
738
+
739
+ | 修复 | 说明 |
740
+ |------|------|
741
+ | **根本修复:`paths.js` 路径解析器** | 新增智能路径解析模块,支持 3 种安装方式:① Skill 目录结构(`references/vscode-extension/lib/` → `../../`)② VSIX 扁平结构(`lib/` → `../`)③ 全局 skill 目录回退(`~/.cursor/skills/cursor-guard/references/`)。所有模块统一使用 `guardPath()` 替代硬编码 `../../` |
742
+ | **Bug #1: Dashboard 无法启动** | `dashboard-manager.js` 中 `require('../../dashboard/server')` 等 5 处路径在 VSIX 扁平结构中全部失败。改为 `require(guardPath('dashboard', 'server'))` |
743
+ | **Bug #2: Snapshot Now 报错** | `snapshotNow` 中 `require('../../lib/core/snapshot')` 路径错误 + `loadConfig` 解构已在 v4.7.1 修复但 `require` 路径仍错。改为 `guardPath('lib', 'core', 'snapshot')` |
744
+ | **Bug #3: snapshot skipped 未处理** | `createGitSnapshot` 无变更时返回 `{ status: 'skipped' }`,但 `extension.js` 只处理了 `created` 和 `unchanged`,导致显示 "snapshot failed"。新增 `skipped` 分支 |
745
+ | **Bug #4: WebView PUBLIC_DIR 多一层 `..`** | `webview-provider.js` 的 `path.resolve(__dirname, '..', '..', 'dashboard', 'public')` 多了一层。改为 `getPublicDir()` 动态解析 |
746
+
737
747
  ### V4.7.3:可视化图表侧边栏 ✅
738
748
 
739
749
  | 组件 | 说明 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-guard",
3
- "version": "4.7.3",
3
+ "version": "4.7.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",
@@ -36,10 +36,12 @@ async function activate(context) {
36
36
  const result = await dashMgr.snapshotNow(projectPath);
37
37
  if (result?.status === 'created') {
38
38
  vscode.window.showInformationMessage(`Cursor Guard: snapshot created (${result.changedCount || 0} changes)`);
39
- } else if (result?.status === 'unchanged') {
39
+ } else if (result?.status === 'unchanged' || result?.status === 'skipped') {
40
40
  vscode.window.showInformationMessage('Cursor Guard: no changes to snapshot');
41
+ } else if (result?.status === 'error') {
42
+ vscode.window.showWarningMessage(`Cursor Guard: ${result.error}`);
41
43
  } else {
42
- vscode.window.showWarningMessage(`Cursor Guard: ${result?.error || 'snapshot failed'}`);
44
+ vscode.window.showWarningMessage(`Cursor Guard: snapshot returned status "${result?.status || 'unknown'}"`);
43
45
  }
44
46
  poller.forceRefresh();
45
47
  }),
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const http = require('http');
6
6
  const { spawn } = require('child_process');
7
+ const { guardPath } = require('./paths');
7
8
 
8
9
  const CONFIG_FILE = '.cursor-guard.json';
9
10
 
@@ -30,7 +31,7 @@ class DashboardManager {
30
31
 
31
32
  async start(paths) {
32
33
  if (!this._serverModule) {
33
- this._serverModule = require('../../dashboard/server');
34
+ this._serverModule = require(guardPath('dashboard', 'server'));
34
35
  }
35
36
  const { startDashboardServer, getInstance } = this._serverModule;
36
37
  const existing = getInstance();
@@ -46,7 +47,7 @@ class DashboardManager {
46
47
  async fetchApi(endpoint) {
47
48
  if (!this._instance) return null;
48
49
  const url = `${this.baseUrl}${endpoint}${endpoint.includes('?') ? '&' : '?'}token=${this.token}`;
49
- return new Promise((resolve, reject) => {
50
+ return new Promise((resolve) => {
50
51
  http.get(url, (res) => {
51
52
  let data = '';
52
53
  res.on('data', chunk => data += chunk);
@@ -78,8 +79,8 @@ class DashboardManager {
78
79
  async snapshotNow(projectPath) {
79
80
  if (!projectPath) return;
80
81
  try {
81
- const { createGitSnapshot } = require('../../lib/core/snapshot');
82
- const { loadConfig } = require('../../lib/utils');
82
+ const { createGitSnapshot } = require(guardPath('lib', 'core', 'snapshot'));
83
+ const { loadConfig } = require(guardPath('lib', 'utils'));
83
84
  const { cfg } = loadConfig(projectPath);
84
85
  return createGitSnapshot(projectPath, cfg, { message: 'guard: manual snapshot via IDE extension' });
85
86
  } catch (e) {
@@ -91,7 +92,7 @@ class DashboardManager {
91
92
  if (!projectPath) return null;
92
93
  const existingPid = this.getWatcherPid(projectPath);
93
94
  if (existingPid) return existingPid;
94
- const cliScript = path.resolve(__dirname, '..', '..', 'bin', 'cursor-guard-backup.js');
95
+ const cliScript = guardPath('bin', 'cursor-guard-backup.js');
95
96
  const child = spawn(process.execPath, [cliScript, '--path', projectPath], {
96
97
  cwd: projectPath,
97
98
  stdio: 'ignore',
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ let _guardRoot = null;
7
+
8
+ function getGuardRoot() {
9
+ if (_guardRoot) return _guardRoot;
10
+
11
+ const marker = path.join('dashboard', 'server.js');
12
+
13
+ // Strategy 1: skill dir structure — lib/ is inside references/vscode-extension/
14
+ const fromSkill = path.resolve(__dirname, '..', '..');
15
+ if (fs.existsSync(path.join(fromSkill, marker))) {
16
+ _guardRoot = fromSkill;
17
+ return _guardRoot;
18
+ }
19
+
20
+ // Strategy 2: VSIX flat — dashboard/ is sibling to lib/
21
+ const fromFlat = path.resolve(__dirname, '..');
22
+ if (fs.existsSync(path.join(fromFlat, marker))) {
23
+ _guardRoot = fromFlat;
24
+ return _guardRoot;
25
+ }
26
+
27
+ // Strategy 3: search common skill install locations
28
+ const home = process.env.USERPROFILE || process.env.HOME || '';
29
+ const candidates = [
30
+ path.join(home, '.cursor', 'skills', 'cursor-guard', 'references'),
31
+ path.join(home, '.cursor', 'skills', 'cursor-guard'),
32
+ ];
33
+
34
+ // Strategy 4: search workspace node_modules
35
+ if (typeof require.main?.filename === 'string') {
36
+ const wsRoot = path.dirname(require.main.filename);
37
+ candidates.push(path.join(wsRoot, 'node_modules', 'cursor-guard', 'references'));
38
+ }
39
+
40
+ for (const dir of candidates) {
41
+ if (fs.existsSync(path.join(dir, marker))) {
42
+ _guardRoot = dir;
43
+ return _guardRoot;
44
+ }
45
+ }
46
+
47
+ throw new Error(
48
+ 'Cannot locate cursor-guard installation. '
49
+ + 'Ensure cursor-guard is installed as a skill or via npm.'
50
+ );
51
+ }
52
+
53
+ function guardPath(...segments) {
54
+ return path.join(getGuardRoot(), ...segments);
55
+ }
56
+
57
+ function getPackageJson() {
58
+ const root = getGuardRoot();
59
+ // package.json is one level above references/ in skill structure
60
+ const skillPkg = path.resolve(root, '..', 'package.json');
61
+ if (fs.existsSync(skillPkg)) return skillPkg;
62
+ // or at the same level in flat structure
63
+ const flatPkg = path.join(root, 'package.json');
64
+ if (fs.existsSync(flatPkg)) return flatPkg;
65
+ return null;
66
+ }
67
+
68
+ function getPublicDir() {
69
+ return guardPath('dashboard', 'public');
70
+ }
71
+
72
+ module.exports = { getGuardRoot, guardPath, getPackageJson, getPublicDir };
@@ -3,8 +3,9 @@
3
3
  const vscode = require('vscode');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const { getPublicDir } = require('./paths');
6
7
 
7
- const PUBLIC_DIR = path.resolve(__dirname, '..', '..', 'dashboard', 'public');
8
+ const PUBLIC_DIR = getPublicDir();
8
9
 
9
10
  class WebViewProvider {
10
11
  constructor(context, dashMgr) {