cursor-guard 4.3.1 → 4.3.3
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 +51 -0
- package/README.zh-CN.md +51 -0
- package/SKILL.md +6 -2
- package/package.json +1 -1
- package/references/bin/cursor-guard-init.js +1 -1
- package/references/dashboard/public/app.js +13 -1
- package/references/dashboard/public/style.css +3 -1
- package/references/lib/core/backups.js +13 -4
- package/references/lib/core/doctor.js +1 -1
- package/references/lib/core/snapshot.js +7 -1
- package/references/mcp/server.js +9 -2
package/README.md
CHANGED
|
@@ -375,6 +375,57 @@ The skill activates on these signals:
|
|
|
375
375
|
|
|
376
376
|
---
|
|
377
377
|
|
|
378
|
+
## Changelog
|
|
379
|
+
|
|
380
|
+
### v4.3.3
|
|
381
|
+
|
|
382
|
+
- **Feature**: Intent context for snapshots — `snapshot_now` now accepts `intent`, `agent`, and `session` parameters, stored as Git commit trailers to form an audit trail per operation
|
|
383
|
+
- **Feature**: Dashboard displays intent badge in backup table and full intent/agent/session fields in restore-point drawer
|
|
384
|
+
- **Improve**: `parseCommitTrailers` refactored to a data-driven map, supporting all 6 trailer fields (Files-Changed, Summary, Trigger, Intent, Agent, Session)
|
|
385
|
+
- **Improve**: SKILL.md updated to guide AI agents to pass `intent` describing the operation about to happen
|
|
386
|
+
|
|
387
|
+
### v4.3.2
|
|
388
|
+
|
|
389
|
+
- **Fix**: `cursor-guard-init` now adds `node_modules/` (root-level) to `.gitignore` — prevents `git add -A` from scanning thousands of npm dependency files after `npm install cursor-guard --save-dev`
|
|
390
|
+
- **Improve**: Doctor "MCP version" mismatch warning now includes the reload keybinding (`Ctrl+Shift+P -> "Developer: Reload Window"`) for faster action
|
|
391
|
+
|
|
392
|
+
### v4.3.1
|
|
393
|
+
|
|
394
|
+
- **Fix**: `restore_project` now protects `.gitignore` — added to `GUARD_CONFIGS` so it is restored from HEAD after recovery, preventing post-restore full-tree scans (2500+ files)
|
|
395
|
+
- **Fix**: `cursor-guard-index.lock` cleanup — `createGitSnapshot` now removes stale `.lock` files on entry and in the `finally` block, preventing lock file remnants from blocking subsequent operations
|
|
396
|
+
- **Improve**: Auto-backup summary now filtered by `protect`/`ignore` patterns, excluding `.cursor/skills/` and other non-protected files
|
|
397
|
+
- **Improve**: Summary format changed from flat `M file1, A file2` to categorized `Modified 3: a.js; Added 1: b.js` with i18n support
|
|
398
|
+
- **Improve**: Manual snapshot `message` (from `snapshot_now`) now displayed in dashboard backup table and restore-point drawer
|
|
399
|
+
- **Improve**: SKILL.md adds best-practice guidance for AI agents to provide descriptive `message` when calling `snapshot_now`
|
|
400
|
+
|
|
401
|
+
### v4.3.0
|
|
402
|
+
|
|
403
|
+
- **Feature**: Backup context metadata — structured Git commit messages with `Files-Changed`, `Summary`, and `Trigger` trailers
|
|
404
|
+
- **Feature**: `listBackups` parses commit trailers and returns `filesChanged`, `summary`, `trigger` fields
|
|
405
|
+
- **Feature**: Dashboard backup table adds "Changes" column; restore-point drawer shows trigger, files changed, and summary
|
|
406
|
+
|
|
407
|
+
### v4.2.2
|
|
408
|
+
|
|
409
|
+
- **Fix**: `restore_project` now protects `.cursor-guard.json` during restore (prevents config loss)
|
|
410
|
+
- **Fix**: Post-restore HEAD recovery loop extended to restore both `.cursor/` and `.cursor-guard.json`
|
|
411
|
+
- **Improve**: `cursor-guard-init` now reminds users to `git commit` after installation in Git repos
|
|
412
|
+
|
|
413
|
+
### v4.2.1
|
|
414
|
+
|
|
415
|
+
- **Fix**: `t()` function uses `replaceAll` for i18n placeholder substitution
|
|
416
|
+
- **Fix**: Removed unused `loadActiveAlert` import from dashboard server
|
|
417
|
+
- **Fix**: Added `git-snapshot` type to dashboard filter bar
|
|
418
|
+
- **Fix**: Replaced `&&` with `;` in `detail.mcp_no_sdk` i18n string for cross-platform compatibility
|
|
419
|
+
- **Fix**: Deduplicated `sdkCandidates` in `doctor.js`
|
|
420
|
+
|
|
421
|
+
### v4.2.0
|
|
422
|
+
|
|
423
|
+
- **Feature**: Web dashboard — local read-only UI with health overview, backup table, restore-point drawers, diagnostics, protection scope
|
|
424
|
+
- **Feature**: Dual-language (zh-CN / en-US) with full i18n coverage including doctor checks, health issues, alert messages
|
|
425
|
+
- **Feature**: Multi-project support via CLI `--path` args and frontend project selector
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
378
429
|
## Known Limitations
|
|
379
430
|
|
|
380
431
|
- **Binary files**: Git diffs and snapshots work on text files. Binary files (images, compiled assets) are stored but cannot be meaningfully diffed or partially restored.
|
package/README.zh-CN.md
CHANGED
|
@@ -375,6 +375,57 @@ node references\dashboard\server.js --path "D:\MyProject"
|
|
|
375
375
|
|
|
376
376
|
---
|
|
377
377
|
|
|
378
|
+
## 更新日志
|
|
379
|
+
|
|
380
|
+
### v4.3.3
|
|
381
|
+
|
|
382
|
+
- **功能**:快照意图上下文——`snapshot_now` 新增 `intent`(操作意图)、`agent`(AI 模型)、`session`(会话 ID)参数,作为 Git commit trailer 存储,形成按操作事件的审计链
|
|
383
|
+
- **功能**:仪表盘备份表格显示意图徽章,恢复点抽屉完整展示 intent/agent/session 字段
|
|
384
|
+
- **改进**:`parseCommitTrailers` 重构为数据驱动映射表,支持全部 6 个 trailer 字段
|
|
385
|
+
- **改进**:SKILL.md 更新指引,要求 AI agent 在调用 `snapshot_now` 时传入 `intent` 描述即将执行的操作
|
|
386
|
+
|
|
387
|
+
### v4.3.2
|
|
388
|
+
|
|
389
|
+
- **修复**:`cursor-guard-init` 现在会将根目录 `node_modules/` 加入 `.gitignore`——防止 `npm install cursor-guard --save-dev` 后 `git add -A` 扫描数千个依赖文件导致极度缓慢
|
|
390
|
+
- **改进**:Doctor "MCP version" 版本不一致警告现在包含重载快捷键提示(`Ctrl+Shift+P -> "Developer: Reload Window"`),方便快速操作
|
|
391
|
+
|
|
392
|
+
### v4.3.1
|
|
393
|
+
|
|
394
|
+
- **修复**:`restore_project` 现在保护 `.gitignore`——加入 `GUARD_CONFIGS`,恢复后从 HEAD 还原,防止 `.gitignore` 丢失导致全量扫描(2500+ 文件)
|
|
395
|
+
- **修复**:`cursor-guard-index.lock` 清理——`createGitSnapshot` 在入口和 `finally` 块中清除过期 `.lock` 文件,防止锁文件残留阻塞后续操作
|
|
396
|
+
- **改进**:自动备份 summary 现按 `protect`/`ignore` 模式过滤,排除 `.cursor/skills/` 等非保护文件
|
|
397
|
+
- **改进**:summary 格式从扁平的 `M file1, A file2` 改为分类格式 `修改 3: a.js; 新增 1: b.js`,支持中英双语
|
|
398
|
+
- **改进**:手动快照的 `message`(来自 `snapshot_now`)现显示在仪表盘备份表格和恢复点抽屉中
|
|
399
|
+
- **改进**:SKILL.md 新增最佳实践指引,建议 AI agent 在调用 `snapshot_now` 时传入描述性 `message`
|
|
400
|
+
|
|
401
|
+
### v4.3.0
|
|
402
|
+
|
|
403
|
+
- **功能**:备份上下文元数据——Git commit 消息使用结构化 trailer(`Files-Changed`、`Summary`、`Trigger`)
|
|
404
|
+
- **功能**:`listBackups` 解析 commit trailer,返回 `filesChanged`、`summary`、`trigger` 字段
|
|
405
|
+
- **功能**:仪表盘备份表格新增"变更"列;恢复点抽屉展示触发方式、变更文件数、变更摘要
|
|
406
|
+
|
|
407
|
+
### v4.2.2
|
|
408
|
+
|
|
409
|
+
- **修复**:`restore_project` 恢复时保护 `.cursor-guard.json`(防止配置丢失)
|
|
410
|
+
- **修复**:恢复后 HEAD 恢复循环扩展为同时还原 `.cursor/` 和 `.cursor-guard.json`
|
|
411
|
+
- **改进**:`cursor-guard-init` 在 Git 仓库中安装后提醒用户执行 `git commit`
|
|
412
|
+
|
|
413
|
+
### v4.2.1
|
|
414
|
+
|
|
415
|
+
- **修复**:`t()` 函数改用 `replaceAll` 替换 i18n 占位符
|
|
416
|
+
- **修复**:移除仪表盘 server 中未使用的 `loadActiveAlert` 导入
|
|
417
|
+
- **修复**:仪表盘过滤栏补充 `git-snapshot` 类型选项
|
|
418
|
+
- **修复**:`detail.mcp_no_sdk` i18n 字符串中 `&&` 替换为 `;`,确保跨平台兼容
|
|
419
|
+
- **修复**:`doctor.js` 中 `sdkCandidates` 去重
|
|
420
|
+
|
|
421
|
+
### v4.2.0
|
|
422
|
+
|
|
423
|
+
- **功能**:Web 仪表盘——本地只读 UI,健康总览、备份表格、恢复点抽屉、诊断、保护范围
|
|
424
|
+
- **功能**:中英双语(zh-CN / en-US),完整 i18n 覆盖含 doctor 检查项、健康问题、告警消息
|
|
425
|
+
- **功能**:多项目支持——CLI `--path` 参数 + 前端项目选择器
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
378
429
|
## 已知限制
|
|
379
430
|
|
|
380
431
|
- **二进制文件**:Git 快照可以存储二进制文件(图片、编译产物),但无法进行有意义的 diff 或部分恢复。
|
package/SKILL.md
CHANGED
|
@@ -147,14 +147,18 @@ When the target file of an edit **falls outside the protected scope**, the agent
|
|
|
147
147
|
|
|
148
148
|
> **MCP shortcut**: if `snapshot_now` tool is available, call it with `{ "path": "<project>", "strategy": "git" }` instead of the shell commands below. The tool handles temp index, secrets exclusion, and ref creation internally, and returns `{ "git": { "status": "created", "commitHash": "...", "shortHash": "..." } }`. Report the `shortHash` to the user and proceed.
|
|
149
149
|
>
|
|
150
|
-
> **Best practice —
|
|
150
|
+
> **Best practice — intent context**: Always provide `intent` to describe *what operation you are about to perform and why*. This creates an audit trail so the user can later understand "what was the AI doing when this backup was made". Also pass `message` for the commit subject. Example:
|
|
151
151
|
> ```json
|
|
152
152
|
> {
|
|
153
153
|
> "path": "/project",
|
|
154
154
|
> "strategy": "git",
|
|
155
|
-
> "message": "guard: before refactoring auth middleware
|
|
155
|
+
> "message": "guard: before refactoring auth middleware",
|
|
156
|
+
> "intent": "用户要求将 app.js 中的 session 逻辑拆分到 middleware/auth.js,涉及 3 个函数重写",
|
|
157
|
+
> "agent": "claude-4-opus",
|
|
158
|
+
> "session": "6290c87f"
|
|
156
159
|
> }
|
|
157
160
|
> ```
|
|
161
|
+
> The `intent`, `agent`, and `session` fields are stored as Git commit trailers and displayed in the dashboard restore-point list and detail drawer, forming a complete audit trail per operation.
|
|
158
162
|
|
|
159
163
|
Use a **temporary index and dedicated ref** so the user's staged/unstaged state is never touched:
|
|
160
164
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-guard",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.3",
|
|
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",
|
|
@@ -81,7 +81,7 @@ try {
|
|
|
81
81
|
// Step 3: Add .gitignore entries for skill node_modules
|
|
82
82
|
console.log(' [3/4] Updating .gitignore...');
|
|
83
83
|
const gitignorePath = path.join(projectDir, '.gitignore');
|
|
84
|
-
const entries = ['.cursor/skills/**/node_modules/'];
|
|
84
|
+
const entries = ['node_modules/', '.cursor/skills/**/node_modules/'];
|
|
85
85
|
let gitignoreUpdated = false;
|
|
86
86
|
if (!isGlobal) {
|
|
87
87
|
let existing = '';
|
|
@@ -100,6 +100,9 @@ const I18N = {
|
|
|
100
100
|
'summary.deleted': 'Deleted',
|
|
101
101
|
'summary.renamed': 'Renamed',
|
|
102
102
|
'summary.files': 'files',
|
|
103
|
+
'drawer.field.intent': 'Intent',
|
|
104
|
+
'drawer.field.agent': 'Agent',
|
|
105
|
+
'drawer.field.session': 'Session',
|
|
103
106
|
|
|
104
107
|
'error.fetchFailed': 'Failed to fetch data',
|
|
105
108
|
'error.sectionFailed': 'This section failed to load',
|
|
@@ -278,6 +281,9 @@ const I18N = {
|
|
|
278
281
|
'summary.deleted': '删除',
|
|
279
282
|
'summary.renamed': '重命名',
|
|
280
283
|
'summary.files': '个文件',
|
|
284
|
+
'drawer.field.intent': '操作意图',
|
|
285
|
+
'drawer.field.agent': 'AI 模型',
|
|
286
|
+
'drawer.field.session': '会话 ID',
|
|
281
287
|
|
|
282
288
|
'error.fetchFailed': '数据拉取失败',
|
|
283
289
|
'error.sectionFailed': '此区块加载失败',
|
|
@@ -821,7 +827,10 @@ function formatSummaryCell(b) {
|
|
|
821
827
|
const short = translated.length > 80 ? translated.substring(0, 77) + '...' : translated;
|
|
822
828
|
parts.push(`<span class="text-muted text-sm">${esc(short)}</span>`);
|
|
823
829
|
}
|
|
824
|
-
if (b.
|
|
830
|
+
if (b.intent) {
|
|
831
|
+
const intentShort = b.intent.length > 60 ? b.intent.substring(0, 57) + '...' : b.intent;
|
|
832
|
+
parts.push(`<span class="badge badge-intent">${esc(intentShort)}</span>`);
|
|
833
|
+
} else if (b.message && !b.message.startsWith('guard:')) {
|
|
825
834
|
const msgShort = b.message.length > 50 ? b.message.substring(0, 47) + '...' : b.message;
|
|
826
835
|
parts.push(`<span class="text-muted text-sm">${esc(msgShort)}</span>`);
|
|
827
836
|
}
|
|
@@ -952,6 +961,9 @@ function openRestoreDrawer(backup) {
|
|
|
952
961
|
if (backup.ref) fields.push({ key: 'drawer.field.ref', val: backup.ref });
|
|
953
962
|
if (backup.commitHash) fields.push({ key: 'drawer.field.hash', val: backup.commitHash });
|
|
954
963
|
if (backup.path) fields.push({ key: 'drawer.field.path', val: backup.path });
|
|
964
|
+
if (backup.intent) fields.push({ key: 'drawer.field.intent', val: backup.intent });
|
|
965
|
+
if (backup.agent) fields.push({ key: 'drawer.field.agent', val: backup.agent });
|
|
966
|
+
if (backup.session) fields.push({ key: 'drawer.field.session', val: backup.session });
|
|
955
967
|
if (backup.message) fields.push({ key: 'drawer.field.message', val: backup.message });
|
|
956
968
|
if (backup.summary) fields.push({ key: 'drawer.field.summary', val: translateSummary(backup.summary) });
|
|
957
969
|
|
|
@@ -248,9 +248,11 @@ main {
|
|
|
248
248
|
.badge-shadow { background: var(--amber-bg); color: var(--amber); }
|
|
249
249
|
.badge-pre { background: var(--purple-bg); color: var(--purple); }
|
|
250
250
|
.badge-trigger { background: var(--bg-tertiary); color: var(--text-secondary); font-size: 0.7rem; }
|
|
251
|
+
.badge-intent { background: var(--blue-bg); color: var(--blue); font-size: 0.7rem; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle; }
|
|
251
252
|
|
|
252
|
-
.backup-summary-cell { max-width:
|
|
253
|
+
.backup-summary-cell { max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
253
254
|
.backup-summary-cell .badge-trigger { margin-right: 4px; }
|
|
255
|
+
.backup-summary-cell .badge-intent { margin-left: 4px; }
|
|
254
256
|
.backup-summary-cell .text-sm { font-size: 0.75rem; }
|
|
255
257
|
|
|
256
258
|
/* ── Stats Row ────────────────────────────────────────────── */
|
|
@@ -41,15 +41,24 @@ function entryToMs(entry) {
|
|
|
41
41
|
return d ? d.getTime() : 0;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
const TRAILER_MAP = {
|
|
45
|
+
'Files-Changed': { key: 'filesChanged', parse: v => parseInt(v, 10) },
|
|
46
|
+
'Summary': { key: 'summary' },
|
|
47
|
+
'Trigger': { key: 'trigger' },
|
|
48
|
+
'Intent': { key: 'intent' },
|
|
49
|
+
'Agent': { key: 'agent' },
|
|
50
|
+
'Session': { key: 'session' },
|
|
51
|
+
};
|
|
52
|
+
|
|
44
53
|
function parseCommitTrailers(body) {
|
|
45
54
|
if (!body) return {};
|
|
46
55
|
const result = {};
|
|
56
|
+
const pattern = new RegExp(`^(${Object.keys(TRAILER_MAP).join('|')}):\\s*(.+)$`);
|
|
47
57
|
for (const line of body.split('\n')) {
|
|
48
|
-
const m = line.match(
|
|
58
|
+
const m = line.match(pattern);
|
|
49
59
|
if (m) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
result[key] = key === 'filesChanged' ? parseInt(m[2], 10) : m[2];
|
|
60
|
+
const def = TRAILER_MAP[m[1]];
|
|
61
|
+
result[def.key] = def.parse ? def.parse(m[2]) : m[2];
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
return result;
|
|
@@ -266,7 +266,7 @@ function runDiagnostics(projectDir) {
|
|
|
266
266
|
const memPkg = require('../../../package.json');
|
|
267
267
|
if (diskPkg.version !== memPkg.version) {
|
|
268
268
|
check('MCP version', 'WARN',
|
|
269
|
-
`running v${memPkg.version} but disk has v${diskPkg.version} — restart Cursor to load the new version`);
|
|
269
|
+
`running v${memPkg.version} but disk has v${diskPkg.version} — restart Cursor (Ctrl+Shift+P -> "Developer: Reload Window") to load the new version`);
|
|
270
270
|
} else {
|
|
271
271
|
check('MCP version', 'PASS', `v${memPkg.version}`);
|
|
272
272
|
}
|
|
@@ -65,6 +65,9 @@ function buildCommitMessage(ts, opts) {
|
|
|
65
65
|
if (ctx.changedFileCount != null) trailers.push(`Files-Changed: ${ctx.changedFileCount}`);
|
|
66
66
|
if (ctx.summary) trailers.push(`Summary: ${ctx.summary}`);
|
|
67
67
|
if (ctx.trigger) trailers.push(`Trigger: ${ctx.trigger}`);
|
|
68
|
+
if (ctx.intent) trailers.push(`Intent: ${ctx.intent}`);
|
|
69
|
+
if (ctx.agent) trailers.push(`Agent: ${ctx.agent}`);
|
|
70
|
+
if (ctx.session) trailers.push(`Session: ${ctx.session}`);
|
|
68
71
|
|
|
69
72
|
if (trailers.length === 0) return subject;
|
|
70
73
|
return subject + '\n\n' + trailers.join('\n');
|
|
@@ -84,7 +87,10 @@ function buildCommitMessage(ts, opts) {
|
|
|
84
87
|
* @param {object} [opts.context] - Backup context metadata
|
|
85
88
|
* @param {string} [opts.context.trigger] - 'auto' | 'manual' | 'pre-restore'
|
|
86
89
|
* @param {number} [opts.context.changedFileCount] - Number of changed files
|
|
87
|
-
* @param {string} [opts.context.summary] - Short change summary (e.g. "
|
|
90
|
+
* @param {string} [opts.context.summary] - Short change summary (e.g. "Modified 3: a.js, b.js; Added 1: c.js")
|
|
91
|
+
* @param {string} [opts.context.intent] - Why this snapshot was created (e.g. "refactoring auth middleware")
|
|
92
|
+
* @param {string} [opts.context.agent] - AI model identifier (e.g. "claude-4-opus")
|
|
93
|
+
* @param {string} [opts.context.session] - Conversation/session ID
|
|
88
94
|
* @returns {{ status: 'created'|'skipped'|'error', commitHash?: string, shortHash?: string, fileCount?: number, reason?: string, error?: string, secretsExcluded?: string[] }}
|
|
89
95
|
*/
|
|
90
96
|
function createGitSnapshot(projectDir, cfg, opts = {}) {
|
package/references/mcp/server.js
CHANGED
|
@@ -92,8 +92,11 @@ server.tool(
|
|
|
92
92
|
strategy: z.enum(['git', 'shadow', 'both']).optional().describe('Backup strategy (default: from config, or "git")'),
|
|
93
93
|
message: z.string().optional().describe('Custom commit message for git snapshot'),
|
|
94
94
|
scope: z.enum(['protected', 'all']).optional().describe('Snapshot scope: "protected" = only files matching protect patterns (default when protect is configured); "all" = all files regardless of protect config'),
|
|
95
|
+
intent: z.string().optional().describe('Why this snapshot is being created — describe the operation about to happen (e.g. "refactoring auth middleware to use JWT")'),
|
|
96
|
+
agent: z.string().optional().describe('AI model identifier (e.g. "claude-4-opus")'),
|
|
97
|
+
session: z.string().optional().describe('Conversation or session ID for audit trail'),
|
|
95
98
|
},
|
|
96
|
-
async ({ path: projectPath, strategy, message, scope }) => {
|
|
99
|
+
async ({ path: projectPath, strategy, message, scope, intent, agent, session }) => {
|
|
97
100
|
const resolved = path.resolve(projectPath);
|
|
98
101
|
const { loadConfig } = require('../lib/utils');
|
|
99
102
|
const { cfg } = loadConfig(resolved);
|
|
@@ -109,10 +112,14 @@ server.tool(
|
|
|
109
112
|
const results = {};
|
|
110
113
|
|
|
111
114
|
if (effectiveStrategy === 'git' || effectiveStrategy === 'both') {
|
|
115
|
+
const context = { trigger: 'manual' };
|
|
116
|
+
if (intent) context.intent = intent;
|
|
117
|
+
if (agent) context.agent = agent;
|
|
118
|
+
if (session) context.session = session;
|
|
112
119
|
results.git = createGitSnapshot(resolved, cfg, {
|
|
113
120
|
branchRef: 'refs/guard/snapshot',
|
|
114
121
|
message: message || `guard: manual snapshot ${new Date().toISOString()}`,
|
|
115
|
-
context
|
|
122
|
+
context,
|
|
116
123
|
});
|
|
117
124
|
}
|
|
118
125
|
|