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.0",
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
- console.log(' ⚠ If MCP was already configured, restart Cursor (or Ctrl+Shift+P →');
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> && npm install',
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> && npm install',
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.replace(`{${k}}`, v);
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
- return TOOL_DIRS.some(d => normalized.startsWith(d));
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 = 'tool directoryrestoring may downgrade cursor-guard or other tools';
223
+ f.warning = 'protected pathwill 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 .cursor/ back from HEAD to prevent tool/skill downgrade
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
- try {
289
- execFileSync('git', ['restore', `--source=HEAD`, '--', '.cursor/'], {
290
- cwd: projectDir, stdio: 'pipe',
291
- });
292
- } catch { /* .cursor/ may not exist in HEAD, that's fine */ }
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;