cursor-guard 4.2.0 → 4.2.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-guard",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
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",
|
|
@@ -110,7 +110,16 @@ console.log(` MCP server: ${serverExists ? 'OK' : 'MISSING'}`);
|
|
|
110
110
|
console.log(` MCP SDK: ${sdkExists ? 'OK' : 'MISSING — run npm install in skill dir'}`);
|
|
111
111
|
|
|
112
112
|
console.log(`\n Installation complete!\n`);
|
|
113
|
-
|
|
113
|
+
|
|
114
|
+
// Detect git repo and recommend committing
|
|
115
|
+
let isGitRepoDir = false;
|
|
116
|
+
try { isGitRepoDir = fs.existsSync(path.join(projectDir, '.git')); } catch { /* ignore */ }
|
|
117
|
+
if (!isGlobal && isGitRepoDir) {
|
|
118
|
+
console.log(' ** Important: commit now to prevent restore from reverting the skill **');
|
|
119
|
+
console.log(` git add .cursor/ .cursor-guard.json && git commit -m "chore: install cursor-guard"\n`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(' If MCP was already configured, restart Cursor (or Ctrl+Shift+P ->');
|
|
114
123
|
console.log(' "Developer: Reload Window") to load the updated MCP server.\n');
|
|
115
124
|
console.log(' Next steps:');
|
|
116
125
|
console.log(' 1. The skill activates automatically in Cursor Agent conversations.');
|
|
@@ -166,7 +166,7 @@ const I18N = {
|
|
|
166
166
|
'detail.node_ok': '{v}',
|
|
167
167
|
'detail.node_old': '{v} — recommended >=18',
|
|
168
168
|
'detail.mcp_ok': 'server.js found, SDK {v}',
|
|
169
|
-
'detail.mcp_no_sdk': 'server.js found but @modelcontextprotocol/sdk not installed — run: cd <skill-dir
|
|
169
|
+
'detail.mcp_no_sdk': 'server.js found but @modelcontextprotocol/sdk not installed — run: cd <skill-dir>; npm install',
|
|
170
170
|
'detail.mcp_no_server': 'SDK installed ({v}) but server.js not found at expected path',
|
|
171
171
|
'detail.mcp_not_configured': 'MCP not configured (optional — cursor-guard works without it)',
|
|
172
172
|
'detail.mcp_version_mismatch': 'running v{mem} but disk has v{disk} — restart Cursor to load the new version',
|
|
@@ -332,7 +332,7 @@ const I18N = {
|
|
|
332
332
|
'detail.node_ok': '{v}',
|
|
333
333
|
'detail.node_old': '{v}——建议 >=18',
|
|
334
334
|
'detail.mcp_ok': 'server.js 已找到,SDK {v}',
|
|
335
|
-
'detail.mcp_no_sdk': 'server.js 已找到但 @modelcontextprotocol/sdk 未安装——请运行:cd <skill-dir
|
|
335
|
+
'detail.mcp_no_sdk': 'server.js 已找到但 @modelcontextprotocol/sdk 未安装——请运行:cd <skill-dir>; npm install',
|
|
336
336
|
'detail.mcp_no_server': 'SDK 已安装({v})但 server.js 未在预期路径找到',
|
|
337
337
|
'detail.mcp_not_configured': 'MCP 未配置(可选——cursor-guard 无需 MCP 也能工作)',
|
|
338
338
|
'detail.mcp_version_mismatch': '运行中 v{mem},磁盘为 v{disk}——请重启 Cursor 加载新版本',
|
|
@@ -378,7 +378,7 @@ function t(key, params) {
|
|
|
378
378
|
let text = dict[key] || I18N['en-US'][key] || key;
|
|
379
379
|
if (params) {
|
|
380
380
|
for (const [k, v] of Object.entries(params)) {
|
|
381
|
-
text = text.
|
|
381
|
+
text = text.replaceAll(`{${k}}`, String(v));
|
|
382
382
|
}
|
|
383
383
|
}
|
|
384
384
|
return text;
|
|
@@ -770,6 +770,7 @@ function renderFilterBar() {
|
|
|
770
770
|
{ key: 'all', label: 'backups.filterAll' },
|
|
771
771
|
{ key: 'git-auto-backup', label: 'type.git-auto-backup' },
|
|
772
772
|
{ key: 'git-pre-restore', label: 'type.git-pre-restore' },
|
|
773
|
+
{ key: 'git-snapshot', label: 'type.git-snapshot' },
|
|
773
774
|
{ key: 'shadow', label: 'type.shadow' },
|
|
774
775
|
{ key: 'shadow-pre-restore',label: 'type.shadow-pre-restore' },
|
|
775
776
|
];
|
|
@@ -8,7 +8,6 @@ const path = require('path');
|
|
|
8
8
|
const { getDashboard } = require('../lib/core/dashboard');
|
|
9
9
|
const { runDiagnostics } = require('../lib/core/doctor');
|
|
10
10
|
const { listBackups } = require('../lib/core/backups');
|
|
11
|
-
const { loadActiveAlert } = require('../lib/core/anomaly');
|
|
12
11
|
|
|
13
12
|
const PUBLIC_DIR = path.join(__dirname, 'public');
|
|
14
13
|
const DEFAULT_PORT = 3120;
|
|
@@ -221,7 +221,6 @@ function runDiagnostics(projectDir) {
|
|
|
221
221
|
// Search multiple candidate locations for SDK package.json
|
|
222
222
|
const sdkCandidates = [
|
|
223
223
|
path.join(skillRoot, 'node_modules', '@modelcontextprotocol', 'sdk', 'package.json'),
|
|
224
|
-
path.join(__dirname, '..', '..', '..', 'node_modules', '@modelcontextprotocol', 'sdk', 'package.json'),
|
|
225
224
|
];
|
|
226
225
|
for (const candidate of sdkCandidates) {
|
|
227
226
|
try {
|
|
@@ -21,10 +21,13 @@ function validateRelativePath(file) {
|
|
|
21
21
|
const VALID_SHADOW_SOURCE = /^\d{8}_\d{6}(_\d{3})?$|^pre-restore-\d{8}_\d{6}(_\d{3})?$/;
|
|
22
22
|
|
|
23
23
|
const TOOL_DIRS = ['.cursor/', '.cursor\\'];
|
|
24
|
+
const GUARD_CONFIGS = ['.cursor-guard.json'];
|
|
24
25
|
|
|
25
26
|
function isToolPath(filePath) {
|
|
26
27
|
const normalized = filePath.replace(/\\/g, '/');
|
|
27
|
-
|
|
28
|
+
if (TOOL_DIRS.some(d => normalized.startsWith(d))) return true;
|
|
29
|
+
if (GUARD_CONFIGS.includes(normalized)) return true;
|
|
30
|
+
return false;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
function validateShadowSource(source) {
|
|
@@ -217,7 +220,7 @@ function previewProjectRestore(projectDir, source) {
|
|
|
217
220
|
|
|
218
221
|
for (const f of files) {
|
|
219
222
|
if (isToolPath(f.path)) {
|
|
220
|
-
f.warning = '
|
|
223
|
+
f.warning = 'protected path — will be preserved from HEAD to prevent tool/config downgrade';
|
|
221
224
|
}
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -282,14 +285,16 @@ function executeProjectRestore(projectDir, source, opts = {}) {
|
|
|
282
285
|
cwd: projectDir, stdio: 'pipe',
|
|
283
286
|
});
|
|
284
287
|
|
|
285
|
-
// Restore
|
|
288
|
+
// Restore protected paths from HEAD to prevent tool/skill/config downgrade
|
|
286
289
|
const head = git(['rev-parse', 'HEAD'], { cwd: projectDir, allowFail: true });
|
|
287
290
|
if (head) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
for (const p of ['.cursor/', ...GUARD_CONFIGS]) {
|
|
292
|
+
try {
|
|
293
|
+
execFileSync('git', ['restore', `--source=HEAD`, '--', p], {
|
|
294
|
+
cwd: projectDir, stdio: 'pipe',
|
|
295
|
+
});
|
|
296
|
+
} catch { /* may not exist in HEAD, that's fine */ }
|
|
297
|
+
}
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
let untrackedCleaned = 0;
|