cursor-guard 4.9.0 → 4.9.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.
Files changed (54) hide show
  1. package/README.md +94 -28
  2. package/README.zh-CN.md +91 -25
  3. package/ROADMAP.md +51 -9
  4. package/SKILL.md +32 -22
  5. package/package.json +1 -1
  6. package/references/config-reference.md +68 -7
  7. package/references/config-reference.zh-CN.md +68 -7
  8. package/references/cursor-guard.example.json +11 -7
  9. package/references/cursor-guard.schema.json +30 -7
  10. package/references/dashboard/public/app.js +73 -27
  11. package/references/dashboard/public/index.html +8 -7
  12. package/references/lib/auto-backup.js +40 -2
  13. package/references/lib/core/backups.js +46 -16
  14. package/references/lib/core/core.test.js +101 -22
  15. package/references/lib/core/dashboard.js +37 -23
  16. package/references/lib/core/doctor.js +19 -13
  17. package/references/lib/core/pre-warning.js +296 -0
  18. package/references/lib/core/snapshot.js +24 -2
  19. package/references/lib/core/status.js +15 -7
  20. package/references/lib/utils.js +46 -20
  21. package/references/mcp/mcp.test.js +60 -12
  22. package/references/mcp/server.js +72 -60
  23. package/references/quickstart.zh-CN.md +46 -21
  24. package/references/vscode-extension/build-vsix.js +4 -3
  25. package/references/vscode-extension/dist/LICENSE +65 -0
  26. package/references/vscode-extension/dist/{cursor-guard-ide-4.9.0.vsix → cursor-guard-ide-4.9.6.vsix} +0 -0
  27. package/references/vscode-extension/dist/dashboard/public/app.js +73 -27
  28. package/references/vscode-extension/dist/dashboard/public/index.html +8 -7
  29. package/references/vscode-extension/dist/extension.js +498 -296
  30. package/references/vscode-extension/dist/guard-version.json +1 -1
  31. package/references/vscode-extension/dist/lib/auto-backup.js +40 -2
  32. package/references/vscode-extension/dist/lib/core/backups.js +46 -16
  33. package/references/vscode-extension/dist/lib/core/dashboard.js +37 -23
  34. package/references/vscode-extension/dist/lib/core/doctor.js +19 -13
  35. package/references/vscode-extension/dist/lib/core/pre-warning.js +296 -0
  36. package/references/vscode-extension/dist/lib/core/snapshot.js +24 -2
  37. package/references/vscode-extension/dist/lib/core/status.js +15 -7
  38. package/references/vscode-extension/dist/lib/sidebar-webview.js +502 -433
  39. package/references/vscode-extension/dist/lib/status-bar.js +95 -68
  40. package/references/vscode-extension/dist/lib/tree-view.js +174 -114
  41. package/references/vscode-extension/dist/lib/utils.js +46 -20
  42. package/references/vscode-extension/dist/mcp/server.js +393 -30
  43. package/references/vscode-extension/dist/package.json +1 -1
  44. package/references/vscode-extension/dist/skill/ROADMAP.md +51 -9
  45. package/references/vscode-extension/dist/skill/SKILL.md +32 -22
  46. package/references/vscode-extension/dist/skill/config-reference.md +68 -7
  47. package/references/vscode-extension/dist/skill/config-reference.zh-CN.md +68 -7
  48. package/references/vscode-extension/dist/skill/cursor-guard.example.json +11 -7
  49. package/references/vscode-extension/dist/skill/cursor-guard.schema.json +30 -7
  50. package/references/vscode-extension/extension.js +498 -296
  51. package/references/vscode-extension/lib/sidebar-webview.js +502 -433
  52. package/references/vscode-extension/lib/status-bar.js +95 -68
  53. package/references/vscode-extension/lib/tree-view.js +174 -114
  54. package/references/vscode-extension/package.json +1 -1
@@ -1,68 +1,95 @@
1
- 'use strict';
2
-
3
- const vscode = require('vscode');
4
-
5
- class StatusBarController {
6
- constructor(poller) {
7
- this._item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
8
- this._item.command = 'cursorGuard.openDashboard';
9
- this._item.tooltip = 'Cursor Guard click to open dashboard';
10
- this._setIdle();
11
- this._item.show();
12
-
13
- this._sub = poller.onChange(data => this._update(data));
14
- }
15
-
16
- _setIdle() {
17
- this._item.text = '$(shield) Guard: init...';
18
- this._item.backgroundColor = undefined;
19
- this._item.color = new vscode.ThemeColor('statusBar.foreground');
20
- }
21
-
22
- _update(data) {
23
- let hasAlert = false;
24
- let watcherRunning = false;
25
- let alertFileCount = 0;
26
- let projectCount = 0;
27
-
28
- for (const [, p] of data) {
29
- projectCount++;
30
- const d = p.dashboard;
31
- if (!d) continue;
32
- if (d.alerts?.active) {
33
- hasAlert = true;
34
- alertFileCount = d.alerts.latest?.fileCount || 0;
35
- }
36
- if (d.watcher?.running) watcherRunning = true;
37
- }
38
-
39
- if (hasAlert) {
40
- this._item.text = `$(bell~spin) Guard: ${alertFileCount} files!`;
41
- this._item.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
42
- this._item.color = undefined;
43
- this._item.tooltip = `Cursor Guard — ALERT: ${alertFileCount} files changed rapidly. Click to open dashboard.`;
44
- } else if (watcherRunning) {
45
- this._item.text = '$(shield) Guard: OK';
46
- this._item.backgroundColor = undefined;
47
- this._item.color = new vscode.ThemeColor('statusBar.foreground');
48
- this._item.tooltip = 'Cursor Guard — watcher running, no alerts. Click to open dashboard.';
49
- } else if (projectCount > 0) {
50
- this._item.text = '$(eye-closed) Guard: Unprotected';
51
- this._item.backgroundColor = undefined;
52
- this._item.color = new vscode.ThemeColor('statusBar.foreground');
53
- this._item.tooltip = 'Cursor Guard — watcher NOT running. Click to open dashboard, or use Command Palette > Start Watcher.';
54
- } else {
55
- this._item.text = '$(shield) Guard';
56
- this._item.backgroundColor = undefined;
57
- this._item.color = new vscode.ThemeColor('statusBar.foreground');
58
- this._item.tooltip = 'Cursor Guard — no projects detected';
59
- }
60
- }
61
-
62
- dispose() {
63
- this._sub?.dispose();
64
- this._item.dispose();
65
- }
66
- }
67
-
68
- module.exports = { StatusBarController };
1
+ 'use strict';
2
+
3
+ const vscode = require('vscode');
4
+
5
+ class StatusBarController {
6
+ constructor(poller) {
7
+ this._item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
8
+ this._item.command = 'cursorGuard.openDashboard';
9
+ this._item.tooltip = 'Cursor Guard - click to open dashboard';
10
+ this._setIdle();
11
+ this._item.show();
12
+
13
+ this._sub = poller.onChange(data => this._update(data));
14
+ }
15
+
16
+ _setIdle() {
17
+ this._item.text = '$(shield) Guard: init...';
18
+ this._item.backgroundColor = undefined;
19
+ this._item.color = new vscode.ThemeColor('statusBar.foreground');
20
+ }
21
+
22
+ _update(data) {
23
+ let hasPreWarning = false;
24
+ let hasAlert = false;
25
+ let watcherRunning = false;
26
+ let preWarningLabel = '';
27
+ let alertFileCount = 0;
28
+ let projectCount = 0;
29
+
30
+ for (const [, project] of data) {
31
+ projectCount++;
32
+ const dashboard = project.dashboard;
33
+ if (!dashboard) continue;
34
+
35
+ if (dashboard.preWarnings?.active && !hasPreWarning) {
36
+ hasPreWarning = true;
37
+ const latest = dashboard.preWarnings.latest;
38
+ preWarningLabel = latest?.file
39
+ ? `${latest.file} (${latest.riskPercent || '?'}%)`
40
+ : `${dashboard.preWarnings.count || 1} pending`;
41
+ }
42
+
43
+ if (dashboard.alerts?.active) {
44
+ hasAlert = true;
45
+ alertFileCount = dashboard.alerts.latest?.fileCount || 0;
46
+ }
47
+
48
+ if (dashboard.watcher?.running) watcherRunning = true;
49
+ }
50
+
51
+ if (hasPreWarning) {
52
+ this._item.text = `$(warning) Guard: Delete Risk`;
53
+ this._item.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
54
+ this._item.color = undefined;
55
+ this._item.tooltip = `Cursor Guard - pre-warning active: ${preWarningLabel}. Click to open dashboard.`;
56
+ return;
57
+ }
58
+
59
+ if (hasAlert) {
60
+ this._item.text = `$(bell~spin) Guard: ${alertFileCount} files!`;
61
+ this._item.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
62
+ this._item.color = undefined;
63
+ this._item.tooltip = `Cursor Guard - alert: ${alertFileCount} files changed rapidly. Click to open dashboard.`;
64
+ return;
65
+ }
66
+
67
+ if (watcherRunning) {
68
+ this._item.text = '$(shield) Guard: OK';
69
+ this._item.backgroundColor = undefined;
70
+ this._item.color = new vscode.ThemeColor('statusBar.foreground');
71
+ this._item.tooltip = 'Cursor Guard - watcher running, no active alerts. Click to open dashboard.';
72
+ return;
73
+ }
74
+
75
+ if (projectCount > 0) {
76
+ this._item.text = '$(eye-closed) Guard: Unprotected';
77
+ this._item.backgroundColor = undefined;
78
+ this._item.color = new vscode.ThemeColor('statusBar.foreground');
79
+ this._item.tooltip = 'Cursor Guard - watcher not running. Click to open dashboard or start watcher from the Command Palette.';
80
+ return;
81
+ }
82
+
83
+ this._item.text = '$(shield) Guard';
84
+ this._item.backgroundColor = undefined;
85
+ this._item.color = new vscode.ThemeColor('statusBar.foreground');
86
+ this._item.tooltip = 'Cursor Guard - no projects detected';
87
+ }
88
+
89
+ dispose() {
90
+ this._sub?.dispose();
91
+ this._item.dispose();
92
+ }
93
+ }
94
+
95
+ module.exports = { StatusBarController };
@@ -1,114 +1,174 @@
1
- 'use strict';
2
-
3
- const vscode = require('vscode');
4
-
5
- const C = {
6
- green: new vscode.ThemeColor('charts.green'),
7
- red: new vscode.ThemeColor('charts.red'),
8
- yellow: new vscode.ThemeColor('charts.yellow'),
9
- blue: new vscode.ThemeColor('charts.blue'),
10
- purple: new vscode.ThemeColor('charts.purple'),
11
- orange: new vscode.ThemeColor('charts.orange'),
12
- };
13
-
14
- class GuardTreeView {
15
- constructor(poller, dashMgr) {
16
- this._poller = poller;
17
- this._dashMgr = dashMgr;
18
- this._onDidChange = new vscode.EventEmitter();
19
- this.onDidChangeTreeData = this._onDidChange.event;
20
-
21
- this._treeView = vscode.window.createTreeView('cursorGuardProjects', {
22
- treeDataProvider: this,
23
- showCollapseAll: false,
24
- });
25
-
26
- this._sub = poller.onChange(() => this._onDidChange.fire());
27
- }
28
-
29
- refresh() { this._onDidChange.fire(); }
30
- getTreeItem(el) { return el; }
31
-
32
- getChildren(el) {
33
- if (!el) return this._getRootItems();
34
- if (el.contextValue === 'project') return this._getProjectStatus(el.projectId);
35
- return [];
36
- }
37
-
38
- _getRootItems() {
39
- const data = this._poller.data;
40
- if (data.size === 0) {
41
- return [_item('No projects detected', 'info', { icon: 'info', color: C.yellow, desc: 'Add .cursor-guard.json' })];
42
- }
43
- const items = [];
44
- for (const [id, p] of data) {
45
- const d = p.dashboard;
46
- const hasAlert = d?.alerts?.active;
47
- const watcherOk = d?.watcher?.running;
48
- const color = hasAlert ? C.red : watcherOk ? C.green : C.yellow;
49
- const status = hasAlert ? `ALERT ${d.alerts.latest?.fileCount || ''} files` : watcherOk ? 'Protected' : 'Unprotected';
50
- const item = _item(p.name || id, 'project', {
51
- icon: hasAlert ? 'bell' : watcherOk ? 'shield' : 'eye-closed',
52
- color,
53
- desc: status,
54
- collapsible: vscode.TreeItemCollapsibleState.Expanded,
55
- });
56
- item.projectId = id;
57
- items.push(item);
58
- }
59
- return items;
60
- }
61
-
62
- _getProjectStatus(pid) {
63
- const p = this._poller.data.get(pid);
64
- if (!p?.dashboard) return [_item('Loading...', 'loading', { icon: 'loading~spin', color: C.blue })];
65
- const d = p.dashboard;
66
- const items = [];
67
-
68
- if (d.watcher?.running) {
69
- const w = _item('Watcher: Running', 'watcher', { icon: 'eye', color: C.green, desc: `PID ${d.watcher.pid || '?'}` });
70
- items.push(w);
71
- } else {
72
- const w = _item('Watcher: Stopped', 'watcher', { icon: 'eye-closed', color: C.red });
73
- w.command = { command: 'cursorGuard.startWatcher', title: 'Start Watcher' };
74
- w.tooltip = 'Click to start watcher';
75
- items.push(w);
76
- }
77
-
78
- const gitC = d.counts?.git?.commits || 0;
79
- const shadowC = d.counts?.shadow?.snapshots || 0;
80
- const lastAgo = d.lastBackup?.git?.relativeTime || 'never';
81
- items.push(_item(`Backups: ${gitC + shadowC}`, 'stat', { icon: 'history', color: C.blue, desc: `last ${lastAgo}` }));
82
-
83
- const health = d.health?.status || 'unknown';
84
- const hColor = health === 'healthy' ? C.green : health === 'critical' ? C.red : C.yellow;
85
- const hIcon = health === 'healthy' ? 'pass-filled' : health === 'critical' ? 'error' : 'warning';
86
- items.push(_item(`Health: ${health}`, 'health', { icon: hIcon, color: hColor }));
87
-
88
- const openItem = _item('Open Dashboard', 'action', { icon: 'browser', color: C.blue });
89
- openItem.command = { command: 'cursorGuard.openDashboard', title: 'Open' };
90
- items.push(openItem);
91
-
92
- const snapItem = _item('Snapshot Now', 'action', { icon: 'device-camera', color: C.purple });
93
- snapItem.command = { command: 'cursorGuard.snapshotNow', title: 'Snap' };
94
- items.push(snapItem);
95
-
96
- return items;
97
- }
98
-
99
- dispose() {
100
- this._sub?.dispose();
101
- this._treeView.dispose();
102
- this._onDidChange.dispose();
103
- }
104
- }
105
-
106
- function _item(label, ctx, opts = {}) {
107
- const ti = new vscode.TreeItem(label, opts.collapsible || vscode.TreeItemCollapsibleState.None);
108
- ti.contextValue = ctx;
109
- if (opts.icon) ti.iconPath = new vscode.ThemeIcon(opts.icon, opts.color);
110
- if (opts.desc) ti.description = opts.desc;
111
- return ti;
112
- }
113
-
114
- module.exports = { GuardTreeView };
1
+ 'use strict';
2
+
3
+ const vscode = require('vscode');
4
+
5
+ const C = {
6
+ green: new vscode.ThemeColor('charts.green'),
7
+ red: new vscode.ThemeColor('charts.red'),
8
+ yellow: new vscode.ThemeColor('charts.yellow'),
9
+ blue: new vscode.ThemeColor('charts.blue'),
10
+ purple: new vscode.ThemeColor('charts.purple'),
11
+ orange: new vscode.ThemeColor('charts.orange'),
12
+ };
13
+
14
+ class GuardTreeView {
15
+ constructor(poller, dashMgr) {
16
+ this._poller = poller;
17
+ this._dashMgr = dashMgr;
18
+ this._onDidChange = new vscode.EventEmitter();
19
+ this.onDidChangeTreeData = this._onDidChange.event;
20
+
21
+ this._treeView = vscode.window.createTreeView('cursorGuardProjects', {
22
+ treeDataProvider: this,
23
+ showCollapseAll: false,
24
+ });
25
+
26
+ this._sub = poller.onChange(() => this._onDidChange.fire());
27
+ }
28
+
29
+ refresh() { this._onDidChange.fire(); }
30
+ getTreeItem(el) { return el; }
31
+
32
+ getChildren(el) {
33
+ if (!el) return this._getRootItems();
34
+ if (el.contextValue === 'project') return this._getProjectStatus(el.projectId);
35
+ return [];
36
+ }
37
+
38
+ _getRootItems() {
39
+ const data = this._poller.data;
40
+ if (data.size === 0) {
41
+ return [_item('No projects detected', 'info', {
42
+ icon: 'info',
43
+ color: C.yellow,
44
+ desc: 'Add .cursor-guard.json',
45
+ })];
46
+ }
47
+
48
+ const items = [];
49
+ for (const [id, project] of data) {
50
+ const dashboard = project.dashboard;
51
+ const hasPreWarning = dashboard?.preWarnings?.active;
52
+ const hasAlert = dashboard?.alerts?.active;
53
+ const watcherOk = dashboard?.watcher?.running;
54
+
55
+ let color = C.yellow;
56
+ let status = 'Unprotected';
57
+ let icon = 'eye-closed';
58
+
59
+ if (hasPreWarning) {
60
+ color = C.orange;
61
+ icon = 'warning';
62
+ const latest = dashboard.preWarnings.latest;
63
+ status = latest?.file
64
+ ? `Delete risk in ${latest.file}`
65
+ : `Delete risk (${dashboard.preWarnings.count || 1})`;
66
+ } else if (hasAlert) {
67
+ color = C.red;
68
+ icon = 'bell';
69
+ status = `Alert ${dashboard.alerts.latest?.fileCount || ''} files`;
70
+ } else if (watcherOk) {
71
+ color = C.green;
72
+ icon = 'shield';
73
+ status = 'Protected';
74
+ }
75
+
76
+ const item = _item(project.name || id, 'project', {
77
+ icon,
78
+ color,
79
+ desc: status,
80
+ collapsible: vscode.TreeItemCollapsibleState.Expanded,
81
+ });
82
+ item.projectId = id;
83
+ items.push(item);
84
+ }
85
+
86
+ return items;
87
+ }
88
+
89
+ _getProjectStatus(pid) {
90
+ const project = this._poller.data.get(pid);
91
+ if (!project?.dashboard) {
92
+ return [_item('Loading...', 'loading', { icon: 'loading~spin', color: C.blue })];
93
+ }
94
+
95
+ const dashboard = project.dashboard;
96
+ const items = [];
97
+
98
+ if (dashboard.preWarnings?.active) {
99
+ const latest = dashboard.preWarnings.latest || {};
100
+ items.push(_item('Pre-Warning: Active', 'prewarning', {
101
+ icon: 'warning',
102
+ color: C.orange,
103
+ desc: latest.riskPercent ? `${latest.riskPercent}% risk` : undefined,
104
+ }));
105
+
106
+ const detail = latest.file
107
+ ? `${latest.file} - ${latest.summary || 'Review pending deletion'}`
108
+ : `${dashboard.preWarnings.count || 1} destructive edit warning(s) pending`;
109
+ items.push(_item(detail, 'prewarning-detail', {
110
+ icon: 'note',
111
+ color: C.orange,
112
+ }));
113
+ }
114
+
115
+ if (dashboard.watcher?.running) {
116
+ items.push(_item('Watcher: Running', 'watcher', {
117
+ icon: 'eye',
118
+ color: C.green,
119
+ desc: `PID ${dashboard.watcher.pid || '?'}`,
120
+ }));
121
+ } else {
122
+ const watcherItem = _item('Watcher: Stopped', 'watcher', {
123
+ icon: 'eye-closed',
124
+ color: C.red,
125
+ });
126
+ watcherItem.command = { command: 'cursorGuard.startWatcher', title: 'Start Watcher' };
127
+ watcherItem.tooltip = 'Click to start watcher';
128
+ items.push(watcherItem);
129
+ }
130
+
131
+ const gitCount = dashboard.counts?.git?.commits || 0;
132
+ const shadowCount = dashboard.counts?.shadow?.snapshots || 0;
133
+ const lastAgo = dashboard.lastBackup?.git?.relativeTime || 'never';
134
+ items.push(_item(`Backups: ${gitCount + shadowCount}`, 'stat', {
135
+ icon: 'history',
136
+ color: C.blue,
137
+ desc: `last ${lastAgo}`,
138
+ }));
139
+
140
+ const health = dashboard.health?.status || 'unknown';
141
+ const healthColor = health === 'healthy' ? C.green : health === 'critical' ? C.red : C.yellow;
142
+ const healthIcon = health === 'healthy' ? 'pass-filled' : health === 'critical' ? 'error' : 'warning';
143
+ items.push(_item(`Health: ${health}`, 'health', {
144
+ icon: healthIcon,
145
+ color: healthColor,
146
+ }));
147
+
148
+ const openItem = _item('Open Dashboard', 'action', { icon: 'browser', color: C.blue });
149
+ openItem.command = { command: 'cursorGuard.openDashboard', title: 'Open' };
150
+ items.push(openItem);
151
+
152
+ const snapItem = _item('Snapshot Now', 'action', { icon: 'device-camera', color: C.purple });
153
+ snapItem.command = { command: 'cursorGuard.snapshotNow', title: 'Snap' };
154
+ items.push(snapItem);
155
+
156
+ return items;
157
+ }
158
+
159
+ dispose() {
160
+ this._sub?.dispose();
161
+ this._treeView.dispose();
162
+ this._onDidChange.dispose();
163
+ }
164
+ }
165
+
166
+ function _item(label, ctx, opts = {}) {
167
+ const treeItem = new vscode.TreeItem(label, opts.collapsible || vscode.TreeItemCollapsibleState.None);
168
+ treeItem.contextValue = ctx;
169
+ if (opts.icon) treeItem.iconPath = new vscode.ThemeIcon(opts.icon, opts.color);
170
+ if (opts.desc) treeItem.description = opts.desc;
171
+ return treeItem;
172
+ }
173
+
174
+ module.exports = { GuardTreeView };
@@ -2,7 +2,7 @@
2
2
  "name": "cursor-guard-ide",
3
3
  "displayName": "Cursor Guard",
4
4
  "description": "AI code protection dashboard embedded in your IDE — real-time alerts, backup history, one-click snapshots",
5
- "version": "4.9.0",
5
+ "version": "4.9.6",
6
6
  "publisher": "zhangqiang8vipp",
7
7
  "license": "BUSL-1.1",
8
8
  "engines": {