cursor-guard 4.3.4 → 4.3.5

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 CHANGED
@@ -377,6 +377,12 @@ The skill activates on these signals:
377
377
 
378
378
  ## Changelog
379
379
 
380
+ ### v4.3.5
381
+
382
+ - **Fix**: Backup summary now uses incremental `diff-tree` instead of `git status --porcelain` — previously summary always showed cumulative changes since HEAD, now correctly shows changes since the last auto-backup
383
+ - **Improve**: Dashboard backup table "Changes" column uses stacked layout (file count + trigger / intent / detail on separate rows) for better readability
384
+ - **Improve**: Refined color palette — deeper background contrast, softer status colors (green `#4ade80`, amber `#f59e0b`, red `#ef4444`), deeper brand blue `#3b82f6`, wider text hierarchy gap
385
+
380
386
  ### v4.3.4
381
387
 
382
388
  - **Improve**: Log rotation — `backup.log` now rotates at 1MB, keeping up to 3 old files (`backup.log.1`, `.2`, `.3`). Rotation runs on watcher startup and every 100 writes
package/README.zh-CN.md CHANGED
@@ -377,6 +377,12 @@ node references\dashboard\server.js --path "D:\MyProject"
377
377
 
378
378
  ## 更新日志
379
379
 
380
+ ### v4.3.5
381
+
382
+ - **修复**:备份摘要(Summary)现使用增量 `diff-tree` 替代 `git status --porcelain`——之前 summary 始终显示自 HEAD 以来的累计差异,现在正确显示自上次 auto-backup 以来的增量变化
383
+ - **改进**:仪表盘备份表格"变更"列改为分层堆叠布局(文件数 + 触发方式 / 意图 / 明细分行显示),可读性更好
384
+ - **改进**:配色优化——背景层级差距加大,状态色柔和(绿 `#4ade80`、琥珀 `#f59e0b`、红 `#ef4444`),品牌蓝加深 `#3b82f6`,文字层级对比更清晰
385
+
380
386
  ### v4.3.4
381
387
 
382
388
  - **改进**:日志轮转——`backup.log` 超过 1MB 自动轮转,保留最近 3 个旧文件。watcher 启动时和每 100 次写入时检查
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-guard",
3
- "version": "4.3.4",
3
+ "version": "4.3.5",
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",
@@ -819,22 +819,28 @@ function translateSummary(raw) {
819
819
  }
820
820
 
821
821
  function formatSummaryCell(b) {
822
- const parts = [];
823
- if (b.filesChanged != null) parts.push(`<span class="text-sm">${b.filesChanged} ${t('summary.files')}</span>`);
824
- if (b.trigger) parts.push(`<span class="badge badge-trigger">${t('trigger.' + b.trigger)}</span>`);
825
- if (b.summary) {
826
- const translated = translateSummary(b.summary);
827
- const short = translated.length > 80 ? translated.substring(0, 77) + '...' : translated;
828
- parts.push(`<span class="text-muted text-sm">${esc(short)}</span>`);
829
- }
822
+ const line1 = [];
823
+ if (b.filesChanged != null) line1.push(`<span class="summary-files">${b.filesChanged} ${t('summary.files')}</span>`);
824
+ if (b.trigger) line1.push(`<span class="badge badge-trigger">${t('trigger.' + b.trigger)}</span>`);
825
+
826
+ let line2 = '';
830
827
  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>`);
828
+ const intentShort = b.intent.length > 70 ? b.intent.substring(0, 67) + '...' : b.intent;
829
+ line2 = `<div class="summary-intent">${esc(intentShort)}</div>`;
833
830
  } else if (b.message && !b.message.startsWith('guard:')) {
834
- const msgShort = b.message.length > 50 ? b.message.substring(0, 47) + '...' : b.message;
835
- parts.push(`<span class="text-muted text-sm">${esc(msgShort)}</span>`);
831
+ const msgShort = b.message.length > 70 ? b.message.substring(0, 67) + '...' : b.message;
832
+ line2 = `<div class="summary-message">${esc(msgShort)}</div>`;
836
833
  }
837
- return parts.length > 0 ? parts.join(' ') : '<span class="text-muted text-sm">-</span>';
834
+
835
+ let line3 = '';
836
+ if (b.summary) {
837
+ const translated = translateSummary(b.summary);
838
+ const short = translated.length > 90 ? translated.substring(0, 87) + '...' : translated;
839
+ line3 = `<div class="summary-detail">${esc(short)}</div>`;
840
+ }
841
+
842
+ if (!line1.length && !line2 && !line3) return '<span class="text-muted text-sm">-</span>';
843
+ return `<div class="summary-stack">${line1.length ? '<div class="summary-meta">' + line1.join(' ') + '</div>' : ''}${line2}${line3}</div>`;
838
844
  }
839
845
 
840
846
  function renderBackupTable(backups) {
@@ -1,37 +1,50 @@
1
1
  /* ═══════════════════════════════════════════════════════════════
2
- Cursor Guard Dashboard — Ops Console Theme
2
+ Cursor Guard Dashboard — Ops Console Theme v2
3
3
  ═══════════════════════════════════════════════════════════════ */
4
4
 
5
5
  :root {
6
- --bg-primary: #0f172a;
7
- --bg-secondary: #1e293b;
8
- --bg-tertiary: #334155;
9
- --bg-hover: rgba(148,163,184,.08);
10
- --border: #334155;
11
- --border-subtle:#1e293b;
12
-
13
- --text-primary: #f1f5f9;
14
- --text-secondary: #94a3b8;
15
- --text-tertiary: #64748b;
16
-
17
- --green: #22c55e;
18
- --green-bg: rgba(34,197,94,.12);
19
- --yellow: #eab308;
20
- --yellow-bg: rgba(234,179,8,.12);
6
+ --bg-primary: #0a0f1a;
7
+ --bg-secondary: #141c2b;
8
+ --bg-tertiary: #1e293b;
9
+ --bg-elevated: #182030;
10
+ --bg-hover: rgba(148,163,184,.06);
11
+
12
+ --border: rgba(71,85,105,.5);
13
+ --border-subtle:rgba(71,85,105,.25);
14
+ --border-glow: rgba(59,130,246,.2);
15
+
16
+ --text-primary: #cbd5e1;
17
+ --text-secondary: #8b9cb7;
18
+ --text-tertiary: #475569;
19
+ --text-heading: #f8fafc;
20
+
21
+ --green: #4ade80;
22
+ --green-bg: rgba(74,222,128,.12);
23
+ --green-glow: rgba(74,222,128,.2);
24
+ --yellow: #f59e0b;
25
+ --yellow-bg: rgba(245,158,11,.12);
26
+ --yellow-glow: rgba(245,158,11,.2);
21
27
  --red: #ef4444;
22
28
  --red-bg: rgba(239,68,68,.12);
29
+ --red-glow: rgba(239,68,68,.2);
23
30
  --blue: #3b82f6;
24
31
  --blue-bg: rgba(59,130,246,.12);
25
- --purple: #a855f7;
26
- --purple-bg: rgba(168,85,247,.12);
32
+ --blue-glow: rgba(59,130,246,.2);
33
+ --purple: #a78bfa;
34
+ --purple-bg: rgba(167,139,250,.12);
27
35
  --amber: #f59e0b;
28
36
  --amber-bg: rgba(245,158,11,.12);
29
37
  --gray: #64748b;
30
38
  --gray-bg: rgba(100,116,139,.12);
31
39
 
32
- --radius: 8px;
33
- --topbar-h: 52px;
34
- --drawer-w: 480px;
40
+ --radius: 10px;
41
+ --radius-sm: 6px;
42
+ --topbar-h: 56px;
43
+ --drawer-w: 500px;
44
+ --shadow-sm: 0 1px 3px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.25);
45
+ --shadow-md: 0 4px 20px rgba(0,0,0,.45);
46
+ --shadow-lg: 0 8px 32px rgba(0,0,0,.5);
47
+ --transition: .2s cubic-bezier(.4,0,.2,1);
35
48
  }
36
49
 
37
50
  /* ── Reset ────────────────────────────────────────────────── */
@@ -39,13 +52,17 @@
39
52
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
40
53
 
41
54
  body {
42
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
55
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
43
56
  font-size: 14px;
44
- line-height: 1.5;
57
+ line-height: 1.6;
45
58
  color: var(--text-primary);
46
59
  background: var(--bg-primary);
60
+ background-image:
61
+ radial-gradient(ellipse 80% 50% at 50% -20%, rgba(59,130,246,.06), transparent),
62
+ radial-gradient(ellipse 60% 40% at 80% 100%, rgba(74,222,128,.03), transparent);
47
63
  padding-top: var(--topbar-h);
48
64
  -webkit-font-smoothing: antialiased;
65
+ -moz-osx-font-smoothing: grayscale;
49
66
  }
50
67
 
51
68
  /* ── Top Bar ──────────────────────────────────────────────── */
@@ -54,12 +71,14 @@ body {
54
71
  position: fixed;
55
72
  top: 0; left: 0; right: 0;
56
73
  height: var(--topbar-h);
57
- background: var(--bg-secondary);
74
+ background: rgba(14,19,30,.85);
75
+ backdrop-filter: blur(16px) saturate(1.5);
76
+ -webkit-backdrop-filter: blur(16px) saturate(1.5);
58
77
  border-bottom: 1px solid var(--border);
59
78
  display: flex;
60
79
  align-items: center;
61
80
  justify-content: space-between;
62
- padding: 0 20px;
81
+ padding: 0 24px;
63
82
  z-index: 50;
64
83
  gap: 12px;
65
84
  }
@@ -70,67 +89,89 @@ body {
70
89
  gap: 10px;
71
90
  }
72
91
 
73
- .logo-icon { color: var(--green); flex-shrink: 0; }
74
- .logo-text { font-weight: 600; font-size: 15px; white-space: nowrap; }
92
+ .logo-icon {
93
+ color: var(--green);
94
+ flex-shrink: 0;
95
+ filter: drop-shadow(0 0 6px var(--green-glow));
96
+ }
97
+ .logo-text {
98
+ font-weight: 700;
99
+ font-size: 15px;
100
+ white-space: nowrap;
101
+ letter-spacing: -.01em;
102
+ color: var(--text-heading);
103
+ }
75
104
 
76
105
  #project-select {
77
106
  background: var(--bg-tertiary);
78
107
  color: var(--text-primary);
79
108
  border: 1px solid var(--border);
80
- border-radius: 4px;
81
- padding: 4px 8px;
109
+ border-radius: var(--radius-sm);
110
+ padding: 5px 10px;
82
111
  font-size: 13px;
83
- max-width: 200px;
112
+ max-width: 220px;
84
113
  cursor: pointer;
114
+ transition: border-color var(--transition);
85
115
  }
86
- #project-select:focus { outline: 1px solid var(--blue); }
116
+ #project-select:hover { border-color: var(--blue); }
117
+ #project-select:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-bg); }
87
118
 
88
- #last-refresh { font-size: 12px; white-space: nowrap; }
119
+ #last-refresh { font-size: 12px; white-space: nowrap; opacity: .7; }
89
120
 
90
121
  /* ── Buttons ──────────────────────────────────────────────── */
91
122
 
92
123
  .btn {
93
124
  display: inline-flex;
94
125
  align-items: center;
95
- gap: 5px;
96
- padding: 6px 12px;
126
+ gap: 6px;
127
+ padding: 7px 14px;
97
128
  border: 1px solid var(--border);
98
- border-radius: 4px;
129
+ border-radius: var(--radius-sm);
99
130
  background: var(--bg-tertiary);
100
131
  color: var(--text-primary);
101
132
  font-size: 13px;
133
+ font-weight: 500;
102
134
  cursor: pointer;
103
- transition: background .15s;
135
+ transition: all var(--transition);
104
136
  white-space: nowrap;
105
137
  }
106
- .btn:hover { background: var(--bg-hover); }
138
+ .btn:hover {
139
+ background: rgba(59,130,246,.1);
140
+ border-color: rgba(59,130,246,.3);
141
+ color: var(--text-heading);
142
+ }
143
+ .btn:active { transform: scale(.97); }
107
144
 
108
145
  .btn-ghost {
109
146
  background: transparent;
110
147
  border-color: transparent;
111
148
  }
112
- .btn-ghost:hover { background: var(--bg-hover); }
149
+ .btn-ghost:hover {
150
+ background: var(--bg-hover);
151
+ border-color: transparent;
152
+ }
113
153
 
114
- .btn-sm { padding: 3px 8px; font-size: 12px; }
154
+ .btn-sm { padding: 4px 10px; font-size: 12px; }
115
155
 
116
156
  /* ── Main ─────────────────────────────────────────────────── */
117
157
 
118
158
  main {
119
159
  max-width: 1200px;
120
160
  margin: 0 auto;
121
- padding: 24px 20px 60px;
161
+ padding: 28px 24px 60px;
122
162
  }
123
163
 
124
164
  /* ── Sections ─────────────────────────────────────────────── */
125
165
 
126
- .screen { margin-bottom: 40px; }
166
+ .screen { margin-bottom: 44px; }
127
167
 
128
168
  .section-title {
129
- font-size: 16px;
130
- font-weight: 600;
131
- color: var(--text-secondary);
132
- margin-bottom: 16px;
133
- letter-spacing: .02em;
169
+ font-size: 13px;
170
+ font-weight: 700;
171
+ color: var(--text-tertiary);
172
+ margin-bottom: 18px;
173
+ letter-spacing: .08em;
174
+ text-transform: uppercase;
134
175
  }
135
176
 
136
177
  /* ── Card Grid ────────────────────────────────────────────── */
@@ -138,45 +179,66 @@ main {
138
179
  .card-grid {
139
180
  display: grid;
140
181
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
141
- gap: 14px;
182
+ gap: 16px;
142
183
  }
143
184
 
144
185
  .card {
145
186
  background: var(--bg-secondary);
146
187
  border: 1px solid var(--border);
147
188
  border-radius: var(--radius);
148
- padding: 16px 20px;
149
- transition: transform .15s, box-shadow .15s;
189
+ padding: 18px 22px;
190
+ transition: all var(--transition);
191
+ position: relative;
192
+ overflow: hidden;
193
+ }
194
+ .card::before {
195
+ content: '';
196
+ position: absolute;
197
+ inset: 0;
198
+ border-radius: var(--radius);
199
+ padding: 1px;
200
+ background: linear-gradient(135deg, rgba(59,130,246,.1), transparent 50%);
201
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
202
+ -webkit-mask-composite: xor;
203
+ mask-composite: exclude;
204
+ pointer-events: none;
205
+ opacity: 0;
206
+ transition: opacity var(--transition);
150
207
  }
151
208
  .card:hover {
152
- transform: translateY(-1px);
153
- box-shadow: 0 4px 12px rgba(0,0,0,.25);
209
+ transform: translateY(-2px);
210
+ box-shadow: var(--shadow-md);
211
+ border-color: var(--border-glow);
154
212
  }
213
+ .card:hover::before { opacity: 1; }
155
214
 
156
215
  .card-health {
157
216
  grid-column: 1 / -1;
158
217
  display: flex;
159
218
  align-items: center;
160
- gap: 16px;
219
+ gap: 18px;
161
220
  flex-wrap: wrap;
221
+ background: linear-gradient(135deg, var(--bg-secondary), var(--bg-elevated));
162
222
  }
163
223
 
164
224
  .card-label {
165
- font-size: 11px;
166
- font-weight: 600;
225
+ font-size: 10px;
226
+ font-weight: 700;
167
227
  text-transform: uppercase;
168
- letter-spacing: .06em;
228
+ letter-spacing: .1em;
169
229
  color: var(--text-tertiary);
170
- margin-bottom: 8px;
230
+ margin-bottom: 10px;
171
231
  }
172
232
 
173
233
  .card-value {
174
- font-size: 22px;
175
- font-weight: 700;
176
- line-height: 1.2;
234
+ font-size: 24px;
235
+ font-weight: 800;
236
+ line-height: 1.15;
237
+ letter-spacing: -.02em;
238
+ color: var(--text-heading);
177
239
  }
178
240
 
179
- .card-detail { margin-top: 6px; }
241
+ .card-detail { margin-top: 8px; }
180
242
 
181
243
  .card-empty {
182
244
  color: var(--text-tertiary);
@@ -192,22 +254,27 @@ main {
192
254
  display: inline-block;
193
255
  width: 10px; height: 10px;
194
256
  border-radius: 50%;
195
- margin-right: 6px;
257
+ margin-right: 8px;
196
258
  flex-shrink: 0;
197
259
  }
198
- .status-dot.status-healthy { background: var(--green); box-shadow: 0 0 6px var(--green); }
199
- .status-dot.status-warning { background: var(--yellow); box-shadow: 0 0 6px var(--yellow); }
200
- .status-dot.status-critical { background: var(--red); box-shadow: 0 0 6px var(--red); }
201
- .status-dot.status-unknown { background: var(--gray); }
202
- .status-dot.status-running { background: var(--green); box-shadow: 0 0 6px var(--green); }
203
- .status-dot.status-stopped { background: var(--gray); }
204
- .status-dot.status-stale { background: var(--yellow); box-shadow: 0 0 6px var(--yellow); }
260
+ .status-dot.status-healthy { background: var(--green); box-shadow: 0 0 8px var(--green-glow), 0 0 2px var(--green); animation: pulse-green 2s ease-in-out infinite; }
261
+ .status-dot.status-warning { background: var(--yellow); box-shadow: 0 0 8px var(--yellow-glow), 0 0 2px var(--yellow); animation: pulse-yellow 2s ease-in-out infinite; }
262
+ .status-dot.status-critical { background: var(--red); box-shadow: 0 0 8px var(--red-glow), 0 0 2px var(--red); animation: pulse-red 1.5s ease-in-out infinite; }
263
+ .status-dot.status-unknown { background: var(--gray); opacity: .6; }
264
+ .status-dot.status-running { background: var(--green); box-shadow: 0 0 8px var(--green-glow), 0 0 2px var(--green); animation: pulse-green 2s ease-in-out infinite; }
265
+ .status-dot.status-stopped { background: var(--gray); opacity: .6; }
266
+ .status-dot.status-stale { background: var(--yellow); box-shadow: 0 0 8px var(--yellow-glow); }
267
+
268
+ @keyframes pulse-green { 0%,100%{ box-shadow: 0 0 4px var(--green-glow); } 50%{ box-shadow: 0 0 12px var(--green-glow), 0 0 4px var(--green); } }
269
+ @keyframes pulse-yellow { 0%,100%{ box-shadow: 0 0 4px var(--yellow-glow); } 50%{ box-shadow: 0 0 12px var(--yellow-glow), 0 0 4px var(--yellow); } }
270
+ @keyframes pulse-red { 0%,100%{ box-shadow: 0 0 4px var(--red-glow); } 50%{ box-shadow: 0 0 14px var(--red-glow), 0 0 6px var(--red); } }
205
271
 
206
272
  .card-status {
207
273
  display: flex;
208
274
  align-items: center;
209
- font-size: 18px;
210
- font-weight: 700;
275
+ font-size: 20px;
276
+ font-weight: 800;
277
+ letter-spacing: -.01em;
211
278
  }
212
279
 
213
280
  .status-text.status-healthy { color: var(--green); }
@@ -217,13 +284,21 @@ main {
217
284
 
218
285
  .issue-list {
219
286
  list-style: none;
220
- margin-top: 8px;
221
- padding-left: 16px;
287
+ margin-top: 10px;
288
+ padding-left: 18px;
289
+ }
290
+ .issue-list li {
291
+ position: relative;
292
+ padding: 2px 0;
293
+ font-size: 13px;
222
294
  }
223
295
  .issue-list li::before {
224
- content: '';
225
- color: var(--yellow);
226
- margin-right: 6px;
296
+ content: '';
297
+ position: absolute;
298
+ left: -14px; top: 10px;
299
+ width: 5px; height: 5px;
300
+ border-radius: 50%;
301
+ background: var(--yellow);
227
302
  }
228
303
 
229
304
  /* ── Badges ───────────────────────────────────────────────── */
@@ -231,57 +306,92 @@ main {
231
306
  .badge {
232
307
  display: inline-flex;
233
308
  align-items: center;
234
- padding: 2px 8px;
235
- border-radius: 10px;
309
+ padding: 3px 10px;
310
+ border-radius: 12px;
236
311
  font-size: 11px;
237
312
  font-weight: 600;
238
- letter-spacing: .03em;
313
+ letter-spacing: .02em;
314
+ border: 1px solid transparent;
239
315
  }
240
316
 
241
- .badge-strategy { background: var(--blue-bg); color: var(--blue); }
317
+ .badge-strategy { background: var(--blue-bg); color: var(--blue); border-color: rgba(59,130,246,.18); }
242
318
 
243
- .badge-pass { background: var(--green-bg); color: var(--green); }
244
- .badge-warn { background: var(--yellow-bg); color: var(--yellow); }
245
- .badge-fail { background: var(--red-bg); color: var(--red); }
319
+ .badge-pass { background: var(--green-bg); color: var(--green); border-color: rgba(74,222,128,.18); }
320
+ .badge-warn { background: var(--yellow-bg); color: var(--yellow); border-color: rgba(245,158,11,.18); }
321
+ .badge-fail { background: var(--red-bg); color: var(--red); border-color: rgba(239,68,68,.18); }
246
322
 
247
- .badge-git { background: var(--blue-bg); color: var(--blue); }
248
- .badge-shadow { background: var(--amber-bg); color: var(--amber); }
249
- .badge-pre { background: var(--purple-bg); color: var(--purple); }
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; }
323
+ .badge-git { background: var(--blue-bg); color: var(--blue); border-color: rgba(59,130,246,.18); }
324
+ .badge-shadow { background: var(--amber-bg); color: var(--amber); border-color: rgba(245,158,11,.18); }
325
+ .badge-pre { background: var(--purple-bg); color: var(--purple); border-color: rgba(167,139,250,.18); }
326
+ .badge-trigger { background: var(--bg-tertiary); color: var(--text-secondary); font-size: 0.7rem; border-color: var(--border-subtle); }
327
+ .badge-intent { background: var(--blue-bg); color: var(--blue); font-size: 0.7rem; border-color: rgba(59,130,246,.18); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle; }
252
328
 
253
- .backup-summary-cell { max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
254
- .backup-summary-cell .badge-trigger { margin-right: 4px; }
255
- .backup-summary-cell .badge-intent { margin-left: 4px; }
256
- .backup-summary-cell .text-sm { font-size: 0.75rem; }
329
+ .backup-summary-cell { max-width: 400px; }
330
+
331
+ .summary-stack { display: flex; flex-direction: column; gap: 4px; }
332
+ .summary-meta { display: flex; align-items: center; gap: 6px; }
333
+ .summary-files { font-size: 13px; font-weight: 600; color: var(--text-heading); }
334
+ .summary-intent {
335
+ font-size: 12px;
336
+ color: var(--blue);
337
+ background: var(--blue-bg);
338
+ padding: 2px 8px;
339
+ border-radius: 3px;
340
+ border-left: 2px solid var(--blue);
341
+ overflow: hidden;
342
+ text-overflow: ellipsis;
343
+ white-space: nowrap;
344
+ max-width: 380px;
345
+ }
346
+ .summary-message {
347
+ font-size: 12px;
348
+ color: var(--text-secondary);
349
+ overflow: hidden;
350
+ text-overflow: ellipsis;
351
+ white-space: nowrap;
352
+ max-width: 380px;
353
+ }
354
+ .summary-detail {
355
+ font-size: 11px;
356
+ color: var(--text-tertiary);
357
+ overflow: hidden;
358
+ text-overflow: ellipsis;
359
+ white-space: nowrap;
360
+ max-width: 380px;
361
+ }
257
362
 
258
363
  /* ── Stats Row ────────────────────────────────────────────── */
259
364
 
260
365
  .stats-row {
261
366
  display: flex;
262
367
  flex-wrap: wrap;
263
- gap: 12px;
264
- margin-bottom: 16px;
368
+ gap: 14px;
369
+ margin-bottom: 18px;
265
370
  }
266
371
 
267
372
  .stat-card {
268
373
  background: var(--bg-secondary);
269
374
  border: 1px solid var(--border);
270
375
  border-radius: var(--radius);
271
- padding: 12px 16px;
272
- min-width: 140px;
376
+ padding: 14px 18px;
377
+ min-width: 150px;
273
378
  flex: 1;
379
+ transition: border-color var(--transition);
274
380
  }
381
+ .stat-card:hover { border-color: var(--border-glow); }
275
382
  .stat-label {
276
- font-size: 11px;
383
+ font-size: 10px;
384
+ font-weight: 700;
277
385
  text-transform: uppercase;
278
- letter-spacing: .05em;
386
+ letter-spacing: .08em;
279
387
  color: var(--text-tertiary);
280
- margin-bottom: 4px;
388
+ margin-bottom: 6px;
281
389
  }
282
390
  .stat-value {
283
- font-size: 20px;
284
- font-weight: 700;
391
+ font-size: 22px;
392
+ font-weight: 800;
393
+ letter-spacing: -.02em;
394
+ color: var(--text-heading);
285
395
  }
286
396
 
287
397
  /* ── Filter Bar ───────────────────────────────────────────── */
@@ -289,25 +399,31 @@ main {
289
399
  .filter-bar {
290
400
  display: flex;
291
401
  flex-wrap: wrap;
292
- gap: 6px;
293
- margin-bottom: 12px;
402
+ gap: 8px;
403
+ margin-bottom: 14px;
294
404
  }
295
405
 
296
406
  .filter-btn {
297
- padding: 4px 10px;
407
+ padding: 5px 14px;
298
408
  border: 1px solid var(--border);
299
- border-radius: 14px;
409
+ border-radius: 16px;
300
410
  background: transparent;
301
411
  color: var(--text-secondary);
302
412
  font-size: 12px;
413
+ font-weight: 500;
303
414
  cursor: pointer;
304
- transition: all .15s;
415
+ transition: all var(--transition);
416
+ }
417
+ .filter-btn:hover {
418
+ background: var(--bg-hover);
419
+ color: var(--text-primary);
420
+ border-color: rgba(100,116,139,.3);
305
421
  }
306
- .filter-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
307
422
  .filter-btn.active {
308
423
  background: var(--blue-bg);
309
424
  color: var(--blue);
310
- border-color: var(--blue);
425
+ border-color: rgba(59,130,246,.35);
426
+ font-weight: 600;
311
427
  }
312
428
 
313
429
  /* ── Table ────────────────────────────────────────────────── */
@@ -317,7 +433,7 @@ main {
317
433
  border: 1px solid var(--border);
318
434
  border-radius: var(--radius);
319
435
  overflow: auto;
320
- max-height: 420px;
436
+ max-height: 440px;
321
437
  }
322
438
 
323
439
  .data-table {
@@ -327,115 +443,140 @@ main {
327
443
  }
328
444
  .data-table th {
329
445
  text-align: left;
330
- padding: 10px 14px;
331
- font-weight: 600;
332
- font-size: 11px;
446
+ padding: 12px 16px;
447
+ font-weight: 700;
448
+ font-size: 10px;
333
449
  text-transform: uppercase;
334
- letter-spacing: .05em;
450
+ letter-spacing: .08em;
335
451
  color: var(--text-tertiary);
336
452
  border-bottom: 1px solid var(--border);
337
453
  position: sticky;
338
454
  top: 0;
339
- background: var(--bg-secondary);
455
+ background: var(--bg-elevated);
456
+ z-index: 2;
340
457
  }
341
458
  .data-table td {
342
- padding: 8px 14px;
343
- border-bottom: 1px solid rgba(51,65,85,.4);
459
+ padding: 10px 16px;
460
+ border-bottom: 1px solid var(--border-subtle);
344
461
  vertical-align: top;
345
462
  }
346
- .data-table tbody tr { cursor: pointer; transition: background .1s; }
347
- .data-table tbody tr:hover { background: var(--bg-hover); }
463
+ .data-table tbody tr {
464
+ cursor: pointer;
465
+ transition: background var(--transition);
466
+ }
467
+ .data-table tbody tr:hover {
468
+ background: rgba(59,130,246,.05);
469
+ }
470
+ .data-table tbody tr:nth-child(even) {
471
+ background: rgba(255,255,255,.015);
472
+ }
473
+ .data-table tbody tr:nth-child(even):hover {
474
+ background: rgba(59,130,246,.05);
475
+ }
348
476
 
349
477
  /* ── Protection ───────────────────────────────────────────── */
350
478
 
351
479
  .protection-grid {
352
480
  display: grid;
353
481
  grid-template-columns: 1fr 1fr;
354
- gap: 14px;
482
+ gap: 16px;
355
483
  }
356
484
 
357
485
  .pattern-card {
358
486
  background: var(--bg-secondary);
359
487
  border: 1px solid var(--border);
360
488
  border-radius: var(--radius);
361
- padding: 16px;
489
+ padding: 18px;
490
+ transition: border-color var(--transition);
362
491
  }
492
+ .pattern-card:hover { border-color: var(--border-glow); }
363
493
  .pattern-card h4 {
364
- font-size: 12px;
494
+ font-size: 10px;
495
+ font-weight: 700;
365
496
  text-transform: uppercase;
366
- letter-spacing: .05em;
497
+ letter-spacing: .08em;
367
498
  color: var(--text-tertiary);
368
- margin-bottom: 10px;
499
+ margin-bottom: 12px;
369
500
  }
370
501
 
371
502
  .pattern-list {
372
503
  list-style: none;
373
504
  display: flex;
374
505
  flex-wrap: wrap;
375
- gap: 6px;
506
+ gap: 8px;
376
507
  }
377
508
  .pattern-item {
378
- font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
509
+ font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
379
510
  font-size: 12px;
380
511
  background: var(--bg-tertiary);
381
- padding: 2px 8px;
382
- border-radius: 3px;
383
- color: var(--text-primary);
512
+ border: 1px solid var(--border-subtle);
513
+ padding: 3px 10px;
514
+ border-radius: 4px;
515
+ color: var(--blue);
516
+ transition: border-color var(--transition);
384
517
  }
518
+ .pattern-item:hover { border-color: var(--blue); }
385
519
 
386
520
  .protection-note {
387
- margin-top: 14px;
521
+ margin-top: 16px;
388
522
  font-size: 12px;
389
523
  color: var(--text-tertiary);
390
524
  font-style: italic;
525
+ line-height: 1.5;
391
526
  }
392
527
 
393
528
  .protection-count {
394
- margin-top: 12px;
529
+ margin-top: 14px;
395
530
  font-size: 14px;
396
- font-weight: 600;
531
+ font-weight: 700;
532
+ color: var(--text-heading);
397
533
  }
398
534
 
399
535
  /* ── Diagnostics Summary ──────────────────────────────────── */
400
536
 
401
537
  .diag-summary {
402
- background: var(--bg-secondary);
538
+ background: linear-gradient(135deg, var(--bg-secondary), var(--bg-elevated));
403
539
  border: 1px solid var(--border);
404
540
  border-radius: var(--radius);
405
- padding: 16px 20px;
541
+ padding: 20px 24px;
406
542
  display: flex;
407
543
  align-items: center;
408
- gap: 20px;
544
+ gap: 24px;
409
545
  cursor: pointer;
410
- transition: transform .15s, box-shadow .15s;
546
+ transition: all var(--transition);
411
547
  }
412
548
  .diag-summary:hover {
413
- transform: translateY(-1px);
414
- box-shadow: 0 4px 12px rgba(0,0,0,.25);
549
+ transform: translateY(-2px);
550
+ box-shadow: var(--shadow-md);
551
+ border-color: var(--border-glow);
415
552
  }
416
553
 
417
554
  .diag-counts {
418
555
  display: flex;
419
- gap: 12px;
556
+ gap: 16px;
420
557
  }
421
558
  .diag-count {
422
559
  text-align: center;
560
+ min-width: 48px;
423
561
  }
424
562
  .diag-count .num {
425
- font-size: 24px;
426
- font-weight: 700;
563
+ font-size: 26px;
564
+ font-weight: 800;
427
565
  display: block;
566
+ letter-spacing: -.02em;
428
567
  }
429
568
  .diag-count .label {
430
- font-size: 11px;
569
+ font-size: 10px;
570
+ font-weight: 600;
431
571
  text-transform: uppercase;
432
- letter-spacing: .04em;
572
+ letter-spacing: .06em;
433
573
  }
434
574
 
435
575
  .diag-hint {
436
576
  margin-left: auto;
437
577
  font-size: 12px;
438
578
  color: var(--text-tertiary);
579
+ opacity: .7;
439
580
  }
440
581
 
441
582
  /* ── Drawer ───────────────────────────────────────────────── */
@@ -443,11 +584,12 @@ main {
443
584
  .drawer-overlay {
444
585
  position: fixed;
445
586
  inset: 0;
446
- background: rgba(0,0,0,.5);
587
+ background: rgba(0,0,0,.6);
588
+ backdrop-filter: blur(4px);
447
589
  z-index: 100;
448
590
  opacity: 0;
449
591
  visibility: hidden;
450
- transition: opacity .2s, visibility .2s;
592
+ transition: opacity .25s, visibility .25s;
451
593
  }
452
594
  .drawer-overlay.active {
453
595
  opacity: 1;
@@ -461,12 +603,13 @@ main {
461
603
  max-width: 92vw;
462
604
  background: var(--bg-secondary);
463
605
  border-left: 1px solid var(--border);
606
+ box-shadow: -8px 0 32px rgba(0,0,0,.4);
464
607
  z-index: 101;
465
608
  display: flex;
466
609
  flex-direction: column;
467
610
  transform: translateX(100%);
468
611
  visibility: hidden;
469
- transition: transform .25s ease, visibility .25s;
612
+ transition: transform .3s cubic-bezier(.32,.72,0,1), visibility .3s;
470
613
  }
471
614
  .drawer.active {
472
615
  transform: translateX(0);
@@ -477,99 +620,113 @@ main {
477
620
  display: flex;
478
621
  align-items: center;
479
622
  justify-content: space-between;
480
- padding: 16px 20px;
623
+ padding: 18px 24px;
481
624
  border-bottom: 1px solid var(--border);
482
625
  }
483
- .drawer-header h3 { font-size: 15px; font-weight: 600; }
626
+ .drawer-header h3 {
627
+ font-size: 15px;
628
+ font-weight: 700;
629
+ color: var(--text-heading);
630
+ }
484
631
 
485
632
  .drawer-close {
486
633
  background: none;
487
634
  border: none;
488
- color: var(--text-secondary);
489
- font-size: 22px;
635
+ color: var(--text-tertiary);
636
+ font-size: 24px;
490
637
  cursor: pointer;
491
638
  padding: 0 4px;
492
639
  line-height: 1;
640
+ transition: color var(--transition);
641
+ border-radius: 4px;
493
642
  }
494
- .drawer-close:hover { color: var(--text-primary); }
643
+ .drawer-close:hover { color: var(--text-primary); background: var(--bg-hover); }
495
644
 
496
645
  .drawer-body {
497
646
  flex: 1;
498
647
  overflow-y: auto;
499
- padding: 20px;
648
+ padding: 24px;
500
649
  }
501
650
 
502
651
  /* ── Drawer: Restore Point ────────────────────────────────── */
503
652
 
504
653
  .restore-field {
505
- margin-bottom: 14px;
654
+ margin-bottom: 16px;
506
655
  }
507
656
  .restore-field-label {
508
- font-size: 11px;
657
+ font-size: 10px;
658
+ font-weight: 700;
509
659
  text-transform: uppercase;
510
- letter-spacing: .05em;
660
+ letter-spacing: .08em;
511
661
  color: var(--text-tertiary);
512
- margin-bottom: 3px;
662
+ margin-bottom: 4px;
513
663
  }
514
664
  .restore-field-value {
515
665
  font-size: 14px;
516
666
  word-break: break-all;
667
+ line-height: 1.5;
517
668
  }
518
669
 
519
670
  .restore-actions {
520
671
  display: flex;
521
672
  gap: 8px;
522
- margin-top: 20px;
523
- padding-top: 16px;
673
+ margin-top: 24px;
674
+ padding-top: 18px;
524
675
  border-top: 1px solid var(--border);
525
676
  }
526
677
 
527
678
  .json-preview {
528
679
  margin-top: 16px;
529
- padding: 12px;
680
+ padding: 14px;
530
681
  background: var(--bg-primary);
531
- border-radius: 4px;
532
- font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
682
+ border: 1px solid var(--border-subtle);
683
+ border-radius: var(--radius-sm);
684
+ font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
533
685
  font-size: 12px;
534
686
  white-space: pre-wrap;
535
687
  word-break: break-all;
536
688
  max-height: 300px;
537
689
  overflow: auto;
538
690
  color: var(--text-secondary);
691
+ line-height: 1.6;
539
692
  }
540
693
 
541
694
  /* ── Drawer: Doctor Checks ────────────────────────────────── */
542
695
 
543
696
  .check-item {
544
- border-bottom: 1px solid rgba(51,65,85,.4);
697
+ border-bottom: 1px solid var(--border-subtle);
545
698
  }
546
699
  .check-item:last-child { border-bottom: none; }
547
700
 
548
701
  .check-item summary {
549
702
  display: flex;
550
703
  align-items: center;
551
- gap: 8px;
552
- padding: 10px 0;
704
+ gap: 10px;
705
+ padding: 12px 0;
553
706
  cursor: pointer;
554
707
  list-style: none;
555
708
  font-size: 13px;
709
+ transition: color var(--transition);
556
710
  }
711
+ .check-item summary:hover { color: var(--text-heading); }
557
712
  .check-item summary::-webkit-details-marker { display: none; }
558
713
  .check-item summary::before {
559
714
  content: '▸';
560
- font-size: 11px;
715
+ font-size: 10px;
561
716
  color: var(--text-tertiary);
562
- transition: transform .15s;
563
- width: 12px;
717
+ transition: transform .2s ease;
718
+ width: 14px;
564
719
  text-align: center;
720
+ flex-shrink: 0;
565
721
  }
566
722
  .check-item[open] summary::before { transform: rotate(90deg); }
567
723
 
568
- .check-name { flex: 1; }
724
+ .check-name { flex: 1; font-weight: 500; }
569
725
  .check-detail {
570
- padding: 0 0 10px 20px;
726
+ padding: 0 0 12px 24px;
571
727
  font-size: 12px;
572
728
  color: var(--text-secondary);
729
+ line-height: 1.6;
573
730
  }
574
731
 
575
732
  /* ── State panels ─────────────────────────────────────────── */
@@ -579,27 +736,28 @@ main {
579
736
  flex-direction: column;
580
737
  align-items: center;
581
738
  justify-content: center;
582
- min-height: 300px;
739
+ min-height: 320px;
583
740
  text-align: center;
584
741
  color: var(--text-secondary);
585
742
  }
586
743
 
587
744
  .error-icon {
588
- font-size: 36px;
589
- margin-bottom: 12px;
745
+ font-size: 40px;
746
+ margin-bottom: 14px;
590
747
  color: var(--yellow);
748
+ filter: drop-shadow(0 0 8px var(--yellow-glow));
591
749
  }
592
750
 
593
751
  .error-panel {
594
752
  text-align: center;
595
- padding: 40px 20px;
753
+ padding: 48px 24px;
596
754
  color: var(--text-tertiary);
597
755
  }
598
- .error-panel .error-icon { font-size: 28px; margin-bottom: 8px; }
756
+ .error-panel .error-icon { font-size: 32px; margin-bottom: 10px; }
599
757
 
600
758
  .empty-state {
601
759
  text-align: center;
602
- padding: 32px 16px;
760
+ padding: 36px 20px;
603
761
  color: var(--text-tertiary);
604
762
  font-style: italic;
605
763
  }
@@ -607,12 +765,12 @@ main {
607
765
  /* ── Spinner ──────────────────────────────────────────────── */
608
766
 
609
767
  .spinner {
610
- width: 32px; height: 32px;
768
+ width: 36px; height: 36px;
611
769
  border: 3px solid var(--border);
612
770
  border-top-color: var(--blue);
613
771
  border-radius: 50%;
614
- animation: spin .8s linear infinite;
615
- margin-bottom: 12px;
772
+ animation: spin .7s linear infinite;
773
+ margin-bottom: 14px;
616
774
  }
617
775
  @keyframes spin { to { transform: rotate(360deg); } }
618
776
 
@@ -624,7 +782,7 @@ main {
624
782
 
625
783
  .text-muted { color: var(--text-secondary); }
626
784
  .text-sm { font-size: 12px; }
627
- .text-mono { font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace; font-size: 12px; }
785
+ .text-mono { font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', 'Consolas', monospace; font-size: 12px; }
628
786
 
629
787
  .flex-between { display: flex; align-items: center; justify-content: space-between; }
630
788
  .gap-4 { gap: 4px; }
@@ -632,19 +790,20 @@ main {
632
790
 
633
791
  .copy-toast {
634
792
  position: fixed;
635
- bottom: 24px;
793
+ bottom: 28px;
636
794
  left: 50%;
637
795
  transform: translateX(-50%) translateY(20px);
638
796
  background: var(--green);
639
797
  color: #000;
640
- padding: 6px 16px;
641
- border-radius: 4px;
798
+ padding: 8px 20px;
799
+ border-radius: var(--radius-sm);
642
800
  font-size: 13px;
643
- font-weight: 600;
801
+ font-weight: 700;
644
802
  opacity: 0;
645
- transition: opacity .2s, transform .2s;
803
+ transition: opacity .25s, transform .25s;
646
804
  z-index: 200;
647
805
  pointer-events: none;
806
+ box-shadow: 0 4px 16px rgba(74,222,128,.3);
648
807
  }
649
808
  .copy-toast.show {
650
809
  opacity: 1;
@@ -658,12 +817,19 @@ main {
658
817
  ::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 3px; }
659
818
  ::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
660
819
 
820
+ /* ── Selection ───────────────────────────────────────────── */
821
+
822
+ ::selection {
823
+ background: rgba(59,130,246,.3);
824
+ color: var(--text-heading);
825
+ }
826
+
661
827
  /* ── Responsive ───────────────────────────────────────────── */
662
828
 
663
829
  @media (max-width: 768px) {
664
- #topbar { padding: 0 12px; gap: 8px; }
830
+ #topbar { padding: 0 14px; gap: 8px; }
665
831
  .logo-text { display: none; }
666
- main { padding: 16px 12px 40px; }
832
+ main { padding: 18px 14px 40px; }
667
833
  .card-grid { grid-template-columns: 1fr; }
668
834
  .card-health { grid-column: auto; }
669
835
  .protection-grid { grid-template-columns: 1fr; }
@@ -276,39 +276,6 @@ async function runBackup(projectDir, intervalOverride) {
276
276
  // Git snapshot via Core
277
277
  if ((cfg.backup_strategy === 'git' || cfg.backup_strategy === 'both') && repo) {
278
278
  const context = { trigger: 'auto', changedFileCount };
279
- if (porcelain) {
280
- let pLines = porcelain.split('\n').filter(Boolean);
281
- if (cfg.protect.length > 0 || cfg.ignore.length > 0) {
282
- pLines = pLines.filter(line => {
283
- const filePart = line.substring(3);
284
- const arrowIdx = filePart.indexOf(' -> ');
285
- const raw = arrowIdx >= 0 ? filePart.substring(arrowIdx + 4) : filePart;
286
- const rel = unquoteGitPath(raw);
287
- const fakeFile = { rel, full: path.join(projectDir, rel) };
288
- return filterFiles([fakeFile], cfg).length > 0;
289
- });
290
- }
291
- if (pLines.length > 0) {
292
- const groups = { M: [], A: [], D: [], R: [] };
293
- for (const l of pLines) {
294
- const code = l.substring(0, 2).trim();
295
- const filePart = l.substring(3);
296
- const arrowIdx = filePart.indexOf(' -> ');
297
- const file = arrowIdx >= 0 ? filePart.substring(arrowIdx + 4) : filePart;
298
- const key = code.startsWith('R') ? 'R'
299
- : code.includes('D') ? 'D'
300
- : code.includes('A') || code === '??' ? 'A'
301
- : 'M';
302
- groups[key].push(file);
303
- }
304
- const parts = [];
305
- if (groups.M.length) parts.push(`Modified ${groups.M.length}: ${groups.M.slice(0, 5).join(', ')}`);
306
- if (groups.A.length) parts.push(`Added ${groups.A.length}: ${groups.A.slice(0, 5).join(', ')}`);
307
- if (groups.D.length) parts.push(`Deleted ${groups.D.length}: ${groups.D.slice(0, 5).join(', ')}`);
308
- if (groups.R.length) parts.push(`Renamed ${groups.R.length}: ${groups.R.slice(0, 5).join(', ')}`);
309
- context.summary = parts.join('; ');
310
- }
311
- }
312
279
  const snapResult = createGitSnapshot(projectDir, cfg, { branchRef, context });
313
280
  if (snapResult.status === 'created') {
314
281
  let msg = `Git snapshot ${snapResult.shortHash} (${snapResult.fileCount} files)`;
@@ -138,6 +138,46 @@ function createGitSnapshot(projectDir, cfg, opts = {}) {
138
138
  return { status: 'skipped', reason: 'tree unchanged' };
139
139
  }
140
140
 
141
+ // Build incremental summary from actual tree diff (not working-dir status)
142
+ let changedCount;
143
+ let incrementalSummary;
144
+ if (parentTree) {
145
+ const diffOut = git(['diff-tree', '--no-commit-id', '--name-status', '-r', parentTree, newTree], { cwd, allowFail: true });
146
+ if (diffOut) {
147
+ const diffLines = diffOut.split('\n').filter(Boolean);
148
+ changedCount = diffLines.length;
149
+ const groups = { M: [], A: [], D: [], R: [] };
150
+ for (const line of diffLines) {
151
+ const tab = line.indexOf('\t');
152
+ if (tab < 0) continue;
153
+ const code = line.substring(0, tab).trim();
154
+ const filePart = line.substring(tab + 1);
155
+ const key = code.startsWith('R') ? 'R'
156
+ : code === 'D' ? 'D'
157
+ : code === 'A' ? 'A'
158
+ : 'M';
159
+ const fileName = filePart.split('\t').pop();
160
+ groups[key].push(fileName);
161
+ }
162
+ const parts = [];
163
+ if (groups.M.length) parts.push(`Modified ${groups.M.length}: ${groups.M.slice(0, 5).join(', ')}`);
164
+ if (groups.A.length) parts.push(`Added ${groups.A.length}: ${groups.A.slice(0, 5).join(', ')}`);
165
+ if (groups.D.length) parts.push(`Deleted ${groups.D.length}: ${groups.D.slice(0, 5).join(', ')}`);
166
+ if (groups.R.length) parts.push(`Renamed ${groups.R.length}: ${groups.R.slice(0, 5).join(', ')}`);
167
+ if (parts.length) incrementalSummary = parts.join('; ');
168
+ }
169
+ }
170
+
171
+ // Override context summary with the accurate incremental one
172
+ if (incrementalSummary && opts.context) {
173
+ opts.context.summary = incrementalSummary;
174
+ } else if (incrementalSummary && !opts.context) {
175
+ opts.context = { summary: incrementalSummary };
176
+ }
177
+ if (changedCount != null && opts.context) {
178
+ opts.context.changedFileCount = changedCount;
179
+ }
180
+
141
181
  const ts = formatTimestamp(new Date());
142
182
  const msg = buildCommitMessage(ts, opts);
143
183
  const commitArgs = parentHash
@@ -154,18 +194,13 @@ function createGitSnapshot(projectDir, cfg, opts = {}) {
154
194
  const lsOut = git(['ls-tree', '--name-only', '-r', newTree], { cwd, allowFail: true });
155
195
  const fileCount = lsOut ? lsOut.split('\n').filter(Boolean).length : 0;
156
196
 
157
- let changedCount;
158
- if (parentTree) {
159
- const diff = git(['diff-tree', '--no-commit-id', '--name-only', '-r', parentTree, newTree], { cwd, allowFail: true });
160
- changedCount = diff ? diff.split('\n').filter(Boolean).length : 0;
161
- }
162
-
163
197
  return {
164
198
  status: 'created',
165
199
  commitHash,
166
200
  shortHash: commitHash.substring(0, 7),
167
201
  fileCount,
168
202
  changedCount,
203
+ incrementalSummary,
169
204
  secretsExcluded: secretsExcluded.length > 0 ? secretsExcluded : undefined,
170
205
  };
171
206
  } catch (e) {