cursor-guard 4.9.1 → 4.9.8
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 +130 -10
- package/README.zh-CN.md +130 -10
- package/ROADMAP.md +65 -8
- package/SKILL.md +32 -22
- package/package.json +3 -2
- package/references/config-reference.md +68 -7
- package/references/config-reference.zh-CN.md +68 -7
- package/references/cursor-guard.example.json +11 -7
- package/references/cursor-guard.schema.json +30 -7
- package/references/dashboard/public/app.js +73 -27
- package/references/dashboard/public/index.html +8 -7
- package/references/lib/auto-backup.js +40 -2
- package/references/lib/core/backups.js +46 -16
- package/references/lib/core/core.test.js +101 -22
- package/references/lib/core/dashboard.js +37 -23
- package/references/lib/core/doctor.js +19 -13
- package/references/lib/core/pre-warning.js +296 -0
- package/references/lib/core/snapshot.js +24 -2
- package/references/lib/core/status.js +15 -7
- package/references/lib/utils.js +46 -20
- package/references/mcp/mcp.test.js +60 -12
- package/references/mcp/server.js +72 -60
- package/references/quickstart.zh-CN.md +46 -21
- package/references/vscode-extension/build-vsix.js +4 -1
- package/references/vscode-extension/dist/LICENSE +65 -0
- package/references/vscode-extension/dist/{cursor-guard-ide-4.9.1.vsix → cursor-guard-ide-4.9.8.vsix} +0 -0
- package/references/vscode-extension/dist/dashboard/public/app.js +73 -27
- package/references/vscode-extension/dist/dashboard/public/index.html +8 -7
- package/references/vscode-extension/dist/extension.js +406 -5
- package/references/vscode-extension/dist/guard-version.json +1 -1
- package/references/vscode-extension/dist/lib/auto-backup.js +40 -2
- package/references/vscode-extension/dist/lib/core/backups.js +46 -16
- package/references/vscode-extension/dist/lib/core/dashboard.js +37 -23
- package/references/vscode-extension/dist/lib/core/doctor.js +19 -13
- package/references/vscode-extension/dist/lib/core/pre-warning.js +296 -0
- package/references/vscode-extension/dist/lib/core/snapshot.js +24 -2
- package/references/vscode-extension/dist/lib/core/status.js +15 -7
- package/references/vscode-extension/dist/lib/dashboard-manager.js +102 -52
- package/references/vscode-extension/dist/lib/locale.js +36 -0
- package/references/vscode-extension/dist/lib/sidebar-webview.js +1027 -281
- package/references/vscode-extension/dist/lib/status-bar.js +95 -68
- package/references/vscode-extension/dist/lib/tree-view.js +174 -114
- package/references/vscode-extension/dist/lib/utils.js +46 -20
- package/references/vscode-extension/dist/mcp/server.js +395 -31
- package/references/vscode-extension/dist/media/brand-placeholder.png +0 -0
- package/references/vscode-extension/dist/package.json +1 -1
- package/references/vscode-extension/dist/skill/ROADMAP.md +65 -8
- package/references/vscode-extension/dist/skill/SKILL.md +32 -22
- package/references/vscode-extension/dist/skill/config-reference.md +68 -7
- package/references/vscode-extension/dist/skill/config-reference.zh-CN.md +68 -7
- package/references/vscode-extension/dist/skill/cursor-guard.example.json +11 -7
- package/references/vscode-extension/dist/skill/cursor-guard.schema.json +30 -7
- package/references/vscode-extension/extension.js +406 -5
- package/references/vscode-extension/lib/dashboard-manager.js +102 -52
- package/references/vscode-extension/lib/locale.js +36 -0
- package/references/vscode-extension/lib/sidebar-webview.js +1027 -281
- package/references/vscode-extension/lib/status-bar.js +95 -68
- package/references/vscode-extension/lib/tree-view.js +174 -114
- package/references/vscode-extension/media/brand-placeholder.png +0 -0
- 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
|
|
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
|
|
24
|
-
let
|
|
25
|
-
let
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this._item.
|
|
53
|
-
this._item.
|
|
54
|
-
|
|
55
|
-
this._item.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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', {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
items
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
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 };
|
|
@@ -133,19 +133,23 @@ const VALID_GIT_RETENTION_MODES = ['days', 'count'];
|
|
|
133
133
|
|
|
134
134
|
const DEFAULT_IGNORE = ['.cursor/skills/**'];
|
|
135
135
|
|
|
136
|
-
const DEFAULT_CONFIG = {
|
|
137
|
-
protect: [],
|
|
138
|
-
ignore: [...DEFAULT_IGNORE],
|
|
139
|
-
secrets_patterns: DEFAULT_SECRETS,
|
|
140
|
-
backup_strategy: 'git',
|
|
136
|
+
const DEFAULT_CONFIG = {
|
|
137
|
+
protect: [],
|
|
138
|
+
ignore: [...DEFAULT_IGNORE],
|
|
139
|
+
secrets_patterns: DEFAULT_SECRETS,
|
|
140
|
+
backup_strategy: 'git',
|
|
141
141
|
auto_backup_interval_seconds: 60,
|
|
142
142
|
pre_restore_backup: 'always',
|
|
143
143
|
retention: { mode: 'days', days: 30, max_count: 100, max_size_mb: 500 },
|
|
144
|
-
git_retention: { enabled: false, mode: 'count', days: 30, max_count: 200 },
|
|
145
|
-
proactive_alert: true,
|
|
146
|
-
alert_thresholds: { files_per_window: 20, window_seconds: 10, cooldown_seconds: 60 },
|
|
147
|
-
always_watch: false,
|
|
148
|
-
|
|
144
|
+
git_retention: { enabled: false, mode: 'count', days: 30, max_count: 200 },
|
|
145
|
+
proactive_alert: true,
|
|
146
|
+
alert_thresholds: { files_per_window: 20, window_seconds: 10, cooldown_seconds: 60 },
|
|
147
|
+
always_watch: false,
|
|
148
|
+
enable_pre_warning: false,
|
|
149
|
+
pre_warning_threshold: 30,
|
|
150
|
+
pre_warning_mode: 'popup',
|
|
151
|
+
pre_warning_exclude_patterns: [],
|
|
152
|
+
};
|
|
149
153
|
|
|
150
154
|
function loadConfig(projectDir) {
|
|
151
155
|
const cfgPath = path.join(projectDir, '.cursor-guard.json');
|
|
@@ -234,16 +238,38 @@ function loadConfig(projectDir) {
|
|
|
234
238
|
if (typeof raw.alert_thresholds.cooldown_seconds === 'number' && raw.alert_thresholds.cooldown_seconds > 0)
|
|
235
239
|
cfg.alert_thresholds.cooldown_seconds = raw.alert_thresholds.cooldown_seconds;
|
|
236
240
|
}
|
|
237
|
-
if (raw.always_watch === true) {
|
|
238
|
-
cfg.always_watch = true;
|
|
239
|
-
} else if (raw.always_watch !== undefined && raw.always_watch !== false) {
|
|
240
|
-
warnings.push(`always_watch should be a boolean, got ${JSON.stringify(raw.always_watch)} — using default (false)`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
241
|
+
if (raw.always_watch === true) {
|
|
242
|
+
cfg.always_watch = true;
|
|
243
|
+
} else if (raw.always_watch !== undefined && raw.always_watch !== false) {
|
|
244
|
+
warnings.push(`always_watch should be a boolean, got ${JSON.stringify(raw.always_watch)} — using default (false)`);
|
|
245
|
+
}
|
|
246
|
+
if (raw.enable_pre_warning === true) {
|
|
247
|
+
cfg.enable_pre_warning = true;
|
|
248
|
+
} else if (raw.enable_pre_warning !== undefined && raw.enable_pre_warning !== false) {
|
|
249
|
+
warnings.push(`enable_pre_warning should be a boolean, got ${JSON.stringify(raw.enable_pre_warning)} — using default (false)`);
|
|
250
|
+
}
|
|
251
|
+
if (typeof raw.pre_warning_threshold === 'number') {
|
|
252
|
+
if (raw.pre_warning_threshold >= 1 && raw.pre_warning_threshold <= 100) {
|
|
253
|
+
cfg.pre_warning_threshold = raw.pre_warning_threshold;
|
|
254
|
+
} else {
|
|
255
|
+
warnings.push(`pre_warning_threshold should be 1-100, got ${JSON.stringify(raw.pre_warning_threshold)} — using default (${cfg.pre_warning_threshold})`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (typeof raw.pre_warning_mode === 'string') {
|
|
259
|
+
if (['popup', 'dashboard', 'silent'].includes(raw.pre_warning_mode)) {
|
|
260
|
+
cfg.pre_warning_mode = raw.pre_warning_mode;
|
|
261
|
+
} else {
|
|
262
|
+
warnings.push(`Unknown pre_warning_mode "${raw.pre_warning_mode}", using default "${cfg.pre_warning_mode}"`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (Array.isArray(raw.pre_warning_exclude_patterns)) {
|
|
266
|
+
cfg.pre_warning_exclude_patterns = sanitizeStringArray(raw.pre_warning_exclude_patterns, 'pre_warning_exclude_patterns');
|
|
267
|
+
}
|
|
268
|
+
return { cfg, loaded: true, error: null, warnings };
|
|
269
|
+
} catch (e) {
|
|
270
|
+
return { cfg, loaded: false, error: e.message };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
247
273
|
|
|
248
274
|
// ── Git helpers ─────────────────────────────────────────────────
|
|
249
275
|
|