claudeck 1.1.1 → 1.2.0

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.
Files changed (37) hide show
  1. package/README.md +30 -4
  2. package/config/skillsmp-config.json +5 -0
  3. package/db.js +248 -0
  4. package/package.json +11 -2
  5. package/public/css/panels/git-panel.css +220 -0
  6. package/public/css/panels/skills-manager.css +975 -0
  7. package/public/css/ui/input-history.css +109 -0
  8. package/public/css/ui/messages.css +51 -0
  9. package/public/css/ui/notification-bell.css +421 -0
  10. package/public/css/ui/sessions.css +41 -0
  11. package/public/css/ui/worktree.css +442 -0
  12. package/public/index.html +43 -10
  13. package/public/js/core/api.js +83 -0
  14. package/public/js/core/dom.js +15 -0
  15. package/public/js/features/background-sessions.js +11 -0
  16. package/public/js/features/chat.js +501 -3
  17. package/public/js/features/input-history.js +122 -0
  18. package/public/js/features/projects.js +16 -1
  19. package/public/js/features/sessions.js +77 -30
  20. package/public/js/main.js +3 -0
  21. package/public/js/panels/git-panel.js +385 -6
  22. package/public/js/panels/skills-manager.js +1005 -0
  23. package/public/js/ui/messages.js +58 -0
  24. package/public/js/ui/notification-bell.js +240 -0
  25. package/public/js/ui/notification-history.js +210 -0
  26. package/public/js/ui/parallel.js +11 -0
  27. package/public/js/ui/tab-sdk.js +1 -1
  28. package/public/style.css +4 -0
  29. package/server/agent-loop.js +13 -0
  30. package/server/notification-logger.js +27 -0
  31. package/server/routes/notifications.js +57 -1
  32. package/server/routes/sessions.js +41 -0
  33. package/server/routes/skills.js +454 -0
  34. package/server/routes/worktrees.js +93 -0
  35. package/server/utils/git-worktree.js +297 -0
  36. package/server/ws-handler.js +708 -629
  37. package/server.js +17 -1
@@ -0,0 +1,442 @@
1
+ /* ── Worktree toggle button ────────────────────────────── */
2
+ #worktree-btn.active {
3
+ color: var(--accent);
4
+ border-color: var(--accent);
5
+ background: var(--accent-dim);
6
+ box-shadow: var(--glow);
7
+ }
8
+
9
+ /* ── Worktree active banner ────────────────────────────── */
10
+ .worktree-banner {
11
+ background: var(--accent-dim);
12
+ border: 1px solid var(--accent);
13
+ border-radius: var(--radius-md);
14
+ padding: 10px 14px;
15
+ margin: 8px 0;
16
+ animation: fadeInUp 0.2s var(--ease-out-expo);
17
+ }
18
+
19
+ .worktree-banner-content {
20
+ display: flex;
21
+ align-items: center;
22
+ gap: 8px;
23
+ font-size: 12px;
24
+ flex-wrap: wrap;
25
+ }
26
+
27
+ .worktree-banner-content svg {
28
+ color: var(--accent);
29
+ flex-shrink: 0;
30
+ }
31
+
32
+ .worktree-banner-label {
33
+ font-weight: 700;
34
+ color: var(--accent);
35
+ text-transform: uppercase;
36
+ font-size: 10px;
37
+ letter-spacing: 0.5px;
38
+ }
39
+
40
+ .worktree-banner-branch {
41
+ font-family: var(--font-mono);
42
+ font-size: 12px;
43
+ color: var(--text);
44
+ background: var(--bg-primary);
45
+ padding: 2px 6px;
46
+ border-radius: 3px;
47
+ border: 1px solid var(--border);
48
+ }
49
+
50
+ .worktree-banner-base {
51
+ color: var(--text-dim);
52
+ font-size: 11px;
53
+ }
54
+
55
+ .worktree-banner-base strong {
56
+ color: var(--text);
57
+ }
58
+
59
+ /* ── Worktree confirmation card (pre-send) ────────────── */
60
+ .worktree-confirm-card {
61
+ background: var(--bg-secondary);
62
+ border: 1px solid var(--accent);
63
+ border-radius: var(--radius-md);
64
+ padding: 14px 18px;
65
+ margin: 8px 0;
66
+ animation: fadeInUp 0.2s var(--ease-out-expo);
67
+ }
68
+
69
+ .worktree-confirm-card .wt-confirm-header {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 8px;
73
+ font-size: 13px;
74
+ font-weight: 600;
75
+ color: var(--text);
76
+ margin-bottom: 6px;
77
+ }
78
+
79
+ .worktree-confirm-card .wt-confirm-header svg {
80
+ color: var(--accent);
81
+ flex-shrink: 0;
82
+ }
83
+
84
+ .worktree-confirm-card .wt-confirm-desc {
85
+ font-size: 11px;
86
+ color: var(--text-dim);
87
+ margin-bottom: 12px;
88
+ line-height: 1.5;
89
+ }
90
+
91
+ .wt-confirm-btns {
92
+ display: flex;
93
+ gap: 8px;
94
+ }
95
+
96
+ .wt-confirm-btns button {
97
+ padding: 6px 14px;
98
+ font-size: 11px;
99
+ font-weight: 600;
100
+ border-radius: var(--radius);
101
+ cursor: pointer;
102
+ transition: all 0.15s var(--ease-smooth);
103
+ }
104
+
105
+ .wt-confirm-btns .wt-btn-worktree {
106
+ background: var(--accent);
107
+ color: var(--bg-primary);
108
+ border: 1px solid var(--accent);
109
+ }
110
+ .wt-confirm-btns .wt-btn-worktree:hover {
111
+ box-shadow: var(--glow-strong);
112
+ }
113
+
114
+ .wt-confirm-btns .wt-btn-current {
115
+ background: var(--bg-primary);
116
+ color: var(--text);
117
+ border: 1px solid var(--border);
118
+ }
119
+ .wt-confirm-btns .wt-btn-current:hover {
120
+ border-color: var(--text-dim);
121
+ }
122
+
123
+ /* ── Worktree action card (post-completion) ───────────── */
124
+ .worktree-action-card {
125
+ background: var(--bg-secondary);
126
+ border: 1px solid var(--border);
127
+ border-radius: var(--radius-md);
128
+ padding: 12px 16px;
129
+ margin: 8px 0;
130
+ animation: fadeInUp 0.2s var(--ease-out-expo);
131
+ }
132
+
133
+ .worktree-action-header {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 8px;
137
+ font-size: 12px;
138
+ color: var(--text);
139
+ margin-bottom: 8px;
140
+ }
141
+
142
+ .worktree-action-header svg {
143
+ color: var(--accent);
144
+ flex-shrink: 0;
145
+ }
146
+
147
+ .worktree-action-header strong {
148
+ color: var(--accent);
149
+ font-family: var(--font-mono);
150
+ }
151
+
152
+ .worktree-stats {
153
+ margin-left: auto;
154
+ font-family: var(--font-mono);
155
+ font-size: 11px;
156
+ color: var(--text-dim);
157
+ }
158
+
159
+ .worktree-action-btns {
160
+ display: flex;
161
+ gap: 8px;
162
+ }
163
+
164
+ .worktree-action-btns button {
165
+ padding: 5px 12px;
166
+ font-size: 11px;
167
+ border-radius: var(--radius);
168
+ border: 1px solid var(--border);
169
+ background: var(--bg-primary);
170
+ color: var(--text);
171
+ cursor: pointer;
172
+ transition: all 0.15s var(--ease-smooth);
173
+ }
174
+
175
+ .worktree-action-btns .wt-merge-btn:hover {
176
+ background: var(--accent);
177
+ color: var(--bg-primary);
178
+ border-color: var(--accent);
179
+ }
180
+
181
+ .worktree-action-btns .wt-discard-btn:hover {
182
+ background: var(--error);
183
+ color: #fff;
184
+ border-color: var(--error);
185
+ }
186
+
187
+ .worktree-action-btns .wt-diff-btn:hover {
188
+ border-color: var(--accent);
189
+ color: var(--accent);
190
+ }
191
+
192
+ .worktree-action-btns button:disabled {
193
+ opacity: 0.4;
194
+ cursor: not-allowed;
195
+ }
196
+
197
+ .worktree-merged {
198
+ opacity: 0.6;
199
+ border-color: var(--accent);
200
+ }
201
+
202
+ .worktree-discarded {
203
+ opacity: 0.4;
204
+ }
205
+
206
+ /* ── Diff modal ───────────────────────────────────────── */
207
+ .worktree-diff-content {
208
+ max-height: 60vh;
209
+ overflow: auto;
210
+ padding: 16px;
211
+ font-size: 12px;
212
+ font-family: var(--font-mono);
213
+ background: var(--bg-primary);
214
+ border: 1px solid var(--border);
215
+ border-radius: var(--radius);
216
+ white-space: pre;
217
+ line-height: 1.5;
218
+ color: var(--text);
219
+ tab-size: 4;
220
+ }
221
+
222
+ /* ── Git panel worktree section ───────────────────────── */
223
+ .git-worktree-list {
224
+ padding: 4px 0;
225
+ }
226
+
227
+ .git-worktree-item {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: 6px;
231
+ padding: 4px 10px;
232
+ font-size: 11px;
233
+ }
234
+
235
+ .git-worktree-item:hover {
236
+ background: var(--bg-tertiary);
237
+ }
238
+
239
+ .git-worktree-badge {
240
+ font-size: 9px;
241
+ font-weight: 700;
242
+ text-transform: uppercase;
243
+ padding: 1px 5px;
244
+ border-radius: 3px;
245
+ flex-shrink: 0;
246
+ }
247
+
248
+ .git-worktree-badge.running {
249
+ background: var(--warning);
250
+ color: #000;
251
+ }
252
+
253
+ .git-worktree-badge.ready {
254
+ background: var(--accent);
255
+ color: #fff;
256
+ }
257
+
258
+ .git-worktree-name {
259
+ flex: 1;
260
+ font-family: var(--font-mono);
261
+ overflow: hidden;
262
+ text-overflow: ellipsis;
263
+ white-space: nowrap;
264
+ color: var(--text);
265
+ }
266
+
267
+ .git-worktree-actions {
268
+ display: flex;
269
+ gap: 4px;
270
+ }
271
+
272
+ .git-worktree-actions button {
273
+ background: none;
274
+ border: none;
275
+ color: var(--text-dim);
276
+ cursor: pointer;
277
+ padding: 4px 5px;
278
+ font-size: 11px;
279
+ border-radius: 3px;
280
+ transition: all 0.15s;
281
+ display: flex;
282
+ align-items: center;
283
+ justify-content: center;
284
+ }
285
+
286
+ .git-worktree-actions button:hover {
287
+ color: var(--accent);
288
+ background: var(--accent-dim);
289
+ }
290
+
291
+ /* Worktree button tooltips */
292
+ .git-worktree-actions button[data-tooltip] {
293
+ position: relative;
294
+ }
295
+
296
+ .git-worktree-actions button[data-tooltip]::after {
297
+ content: attr(data-tooltip);
298
+ position: absolute;
299
+ bottom: calc(100% + 6px);
300
+ left: 50%;
301
+ transform: translateX(-50%) scale(0.9);
302
+ padding: 4px 10px;
303
+ font-family: var(--font-sans);
304
+ font-size: 10px;
305
+ font-weight: 500;
306
+ white-space: nowrap;
307
+ color: var(--text);
308
+ background: var(--bg-elevated);
309
+ border: 1px solid var(--border);
310
+ border-radius: var(--radius);
311
+ pointer-events: none;
312
+ opacity: 0;
313
+ transition: all 0.15s var(--ease-out-expo);
314
+ z-index: 100;
315
+ box-shadow: var(--shadow-sm);
316
+ }
317
+
318
+ .git-worktree-actions button[data-tooltip]::before {
319
+ content: "";
320
+ position: absolute;
321
+ bottom: calc(100% + 2px);
322
+ left: 50%;
323
+ transform: translateX(-50%);
324
+ border: 4px solid transparent;
325
+ border-top-color: var(--border);
326
+ pointer-events: none;
327
+ opacity: 0;
328
+ transition: opacity 0.15s;
329
+ z-index: 100;
330
+ }
331
+
332
+ .git-worktree-actions button[data-tooltip]:hover::after {
333
+ opacity: 1;
334
+ transform: translateX(-50%) scale(1);
335
+ }
336
+
337
+ .git-worktree-actions button[data-tooltip]:hover::before {
338
+ opacity: 1;
339
+ }
340
+
341
+ .git-worktree-actions .wt-discard:hover {
342
+ color: var(--error);
343
+ background: rgba(255, 80, 80, 0.1);
344
+ }
345
+
346
+ /* Worktree delete confirmation — git panel */
347
+ .git-wt-confirm {
348
+ display: flex;
349
+ align-items: center;
350
+ gap: 6px;
351
+ margin-left: auto;
352
+ }
353
+
354
+ .git-wt-confirm-label {
355
+ font-size: 10px;
356
+ color: var(--error);
357
+ font-weight: 600;
358
+ }
359
+
360
+ .git-wt-confirm-yes,
361
+ .git-wt-confirm-no {
362
+ font-size: 10px;
363
+ padding: 2px 8px;
364
+ border-radius: var(--radius);
365
+ cursor: pointer;
366
+ font-weight: 600;
367
+ border: 1px solid var(--border);
368
+ transition: all 0.15s;
369
+ }
370
+
371
+ .git-wt-confirm-yes {
372
+ background: var(--error);
373
+ color: #fff;
374
+ border-color: var(--error);
375
+ }
376
+
377
+ .git-wt-confirm-yes:hover {
378
+ opacity: 0.85;
379
+ }
380
+
381
+ .git-wt-confirm-no {
382
+ background: var(--bg-primary);
383
+ color: var(--text);
384
+ }
385
+
386
+ .git-wt-confirm-no:hover {
387
+ border-color: var(--text-dim);
388
+ }
389
+
390
+ .git-wt-confirm-yes:disabled,
391
+ .git-wt-confirm-no:disabled {
392
+ opacity: 0.4;
393
+ cursor: not-allowed;
394
+ }
395
+
396
+ /* Worktree delete confirmation — chat inline card */
397
+ .wt-confirm-label {
398
+ font-size: 11px;
399
+ color: var(--error);
400
+ font-weight: 600;
401
+ }
402
+
403
+ .wt-confirm-yes,
404
+ .wt-confirm-no {
405
+ padding: 5px 12px;
406
+ font-size: 11px;
407
+ border-radius: var(--radius);
408
+ cursor: pointer;
409
+ font-weight: 600;
410
+ border: 1px solid var(--border);
411
+ transition: all 0.15s;
412
+ }
413
+
414
+ .wt-confirm-yes {
415
+ background: var(--error);
416
+ color: #fff;
417
+ border-color: var(--error);
418
+ }
419
+
420
+ .wt-confirm-yes:hover {
421
+ opacity: 0.85;
422
+ }
423
+
424
+ .wt-confirm-no {
425
+ background: var(--bg-primary);
426
+ color: var(--text);
427
+ }
428
+
429
+ .wt-confirm-no:hover {
430
+ border-color: var(--text-dim);
431
+ }
432
+
433
+ .wt-confirm-yes:disabled,
434
+ .wt-confirm-no:disabled {
435
+ opacity: 0.4;
436
+ cursor: not-allowed;
437
+ }
438
+
439
+ @keyframes fadeInUp {
440
+ from { opacity: 0; transform: translateY(6px); }
441
+ to { opacity: 1; transform: translateY(0); }
442
+ }
package/public/index.html CHANGED
@@ -97,6 +97,18 @@
97
97
  <span class="term-project-name" id="header-project-name"></span>
98
98
 
99
99
  <div class="header-right">
100
+ <!-- Notification Bell -->
101
+ <div class="notif-bell" id="notif-bell">
102
+ <button class="notif-bell-btn" id="notif-bell-btn" aria-label="Notifications">
103
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
104
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
105
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
106
+ </svg>
107
+ <span class="notif-badge hidden" id="notif-badge">0</span>
108
+ </button>
109
+ <div class="notif-dropdown hidden" id="notif-dropdown"></div>
110
+ </div>
111
+
100
112
  <!-- Session Settings Dropdown -->
101
113
  <div class="header-dropdown">
102
114
  <button class="header-dropdown-trigger">
@@ -387,6 +399,11 @@
387
399
  <line x1="8" y1="23" x2="16" y2="23"/>
388
400
  </svg>
389
401
  </button>
402
+ <button id="worktree-btn" data-tooltip="Worktree Mode" class="toolbox-toggle">
403
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
404
+ <line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/>
405
+ </svg>
406
+ </button>
390
407
  <button id="toolbox-btn" data-tooltip="Saved Prompts" class="toolbox-toggle">
391
408
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
392
409
  <rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 7V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v3"/><line x1="12" y1="12" x2="12" y2="16"/><line x1="10" y1="14" x2="14" y2="14"/>
@@ -409,16 +426,24 @@
409
426
  </span>
410
427
  </div>
411
428
  </div>
412
- <button id="send-btn" title="Send">
413
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
414
- <path d="M3 10l7-7m0 0l7 7m-7-7v14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" transform="rotate(90, 10, 10)"/>
415
- </svg>
416
- </button>
417
- <button id="stop-btn" title="Stop" class="hidden">
418
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
419
- <rect x="4" y="4" width="12" height="12" rx="2" fill="currentColor"/>
420
- </svg>
421
- </button>
429
+ <div class="send-history-group">
430
+ <button id="send-btn" title="Send">
431
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
432
+ <path d="M3 10l7-7m0 0l7 7m-7-7v14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" transform="rotate(90, 10, 10)"/>
433
+ </svg>
434
+ </button>
435
+ <button id="stop-btn" title="Stop" class="hidden">
436
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
437
+ <rect x="4" y="4" width="12" height="12" rx="2" fill="currentColor"/>
438
+ </svg>
439
+ </button>
440
+ <button id="history-btn" class="hidden" data-tooltip="Message History" title="Message history" aria-label="Message history">
441
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
442
+ <circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>
443
+ </svg>
444
+ </button>
445
+ </div>
446
+ <div id="history-popover" class="history-popover hidden"></div>
422
447
  <div id="slash-autocomplete" class="slash-autocomplete hidden"></div>
423
448
  </div>
424
449
  <div id="toolbox-panel" class="toolbox-panel hidden"></div>
@@ -483,6 +508,7 @@
483
508
  </svg>
484
509
  </button>
485
510
  </div>
511
+ <div id="git-branch-info" class="git-branch-info hidden"></div>
486
512
  <div id="git-status-list" class="git-status-list"></div>
487
513
  <div class="git-commit-section">
488
514
  <textarea id="git-commit-msg" class="git-commit-msg" placeholder="Commit message..." rows="2"></textarea>
@@ -492,6 +518,10 @@
492
518
  <h4 class="git-log-title">Recent Commits</h4>
493
519
  <div id="git-log-list" class="git-log-list"></div>
494
520
  </div>
521
+ <div id="git-worktree-section" class="git-section hidden">
522
+ <h4 class="git-log-title">Worktrees</h4>
523
+ <div id="git-worktree-list" class="git-worktree-list"></div>
524
+ </div>
495
525
  </div>
496
526
 
497
527
 
@@ -834,6 +864,9 @@
834
864
  <tr><td><span class="kbd">Enter</span></td><td>Send message</td></tr>
835
865
  <tr><td><span class="kbd">Shift+Enter</span></td><td>New line in input</td></tr>
836
866
  <tr><td><span class="kbd">/</span></td><td>Slash commands autocomplete</td></tr>
867
+ <tr><td><span class="kbd">&uarr;</span></td><td>Recall previous message (empty input)</td></tr>
868
+ <tr><td><span class="kbd">&darr;</span></td><td>Recall next message (in history)</td></tr>
869
+ <tr><td><span class="kbd">Esc</span></td><td>Cancel history navigation</td></tr>
837
870
  </table>
838
871
  </div>
839
872
  </div>
@@ -407,6 +407,29 @@ export async function generateSummary(sessionId) {
407
407
  return res.json();
408
408
  }
409
409
 
410
+ export async function forkSession(sessionId, messageId) {
411
+ const res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/fork`, {
412
+ method: "POST",
413
+ headers: { "Content-Type": "application/json" },
414
+ body: JSON.stringify({ messageId }),
415
+ });
416
+ if (!res.ok) {
417
+ const err = await res.json();
418
+ throw new Error(err.error || "Fork failed");
419
+ }
420
+ return res.json();
421
+ }
422
+
423
+ export async function fetchBranches(sessionId) {
424
+ const res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/branches`);
425
+ return res.json();
426
+ }
427
+
428
+ export async function fetchLineage(sessionId) {
429
+ const res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/lineage`);
430
+ return res.json();
431
+ }
432
+
410
433
  export async function saveSystemPromptApi(path, systemPrompt) {
411
434
  await fetch("/api/projects/system-prompt", {
412
435
  method: "PUT",
@@ -602,3 +625,63 @@ export async function deleteRepoGroup(id) {
602
625
  if (!res.ok) await throwApiError(res);
603
626
  return res.json();
604
627
  }
628
+
629
+ // ── Skills Marketplace (SkillsMP) ───────────────────────
630
+
631
+ export async function fetchSkillsConfig() {
632
+ const res = await fetch("/api/skills/config");
633
+ return res.json();
634
+ }
635
+
636
+ export async function saveSkillsConfig(config) {
637
+ const res = await fetch("/api/skills/config", {
638
+ method: "PUT",
639
+ headers: { "Content-Type": "application/json" },
640
+ body: JSON.stringify(config),
641
+ });
642
+ return res.json();
643
+ }
644
+
645
+ export async function searchSkills(q, page, limit, sortBy) {
646
+ let url = `/api/skills/search?q=${encodeURIComponent(q)}`;
647
+ if (page) url += `&page=${page}`;
648
+ if (limit) url += `&limit=${limit}`;
649
+ if (sortBy) url += `&sortBy=${encodeURIComponent(sortBy)}`;
650
+ const res = await fetch(url);
651
+ return { data: await res.json(), headers: res.headers };
652
+ }
653
+
654
+ export async function aiSearchSkills(q) {
655
+ const res = await fetch(`/api/skills/ai-search?q=${encodeURIComponent(q)}`);
656
+ return { data: await res.json(), headers: res.headers };
657
+ }
658
+
659
+ export async function fetchInstalledSkills(projectPath) {
660
+ let url = "/api/skills/installed";
661
+ if (projectPath) url += `?projectPath=${encodeURIComponent(projectPath)}`;
662
+ const res = await fetch(url);
663
+ return res.json();
664
+ }
665
+
666
+ export async function installSkill(body) {
667
+ const res = await fetch("/api/skills/install", {
668
+ method: "POST",
669
+ headers: { "Content-Type": "application/json" },
670
+ body: JSON.stringify(body),
671
+ });
672
+ return res.json();
673
+ }
674
+
675
+ export async function uninstallSkill(name, scope, projectPath) {
676
+ let url = `/api/skills/${encodeURIComponent(name)}?scope=${scope}`;
677
+ if (projectPath) url += `&projectPath=${encodeURIComponent(projectPath)}`;
678
+ const res = await fetch(url, { method: "DELETE" });
679
+ return res.json();
680
+ }
681
+
682
+ export async function toggleSkill(name, scope, projectPath) {
683
+ let url = `/api/skills/${encodeURIComponent(name)}/toggle?scope=${scope}`;
684
+ if (projectPath) url += `&projectPath=${encodeURIComponent(projectPath)}`;
685
+ const res = await fetch(url, { method: "PUT" });
686
+ return res.json();
687
+ }
@@ -226,6 +226,9 @@ export const $ = {
226
226
  gitCommitMsg: document.getElementById("git-commit-msg"),
227
227
  gitCommitBtn: document.getElementById("git-commit-btn"),
228
228
  gitLogList: document.getElementById("git-log-list"),
229
+ gitBranchInfo: document.getElementById("git-branch-info"),
230
+ gitWorktreeSection: document.getElementById("git-worktree-section"),
231
+ gitWorktreeList: document.getElementById("git-worktree-list"),
229
232
 
230
233
  // MCP manager
231
234
  mcpToggleBtn: document.getElementById("mcp-toggle-btn"),
@@ -247,6 +250,18 @@ export const $ = {
247
250
  mcpFormSave: document.getElementById("mcp-form-save"),
248
251
  mcpAddBtn: document.getElementById("mcp-add-btn"),
249
252
 
253
+ // Notification bell
254
+ notifBellBtn: document.getElementById("notif-bell-btn"),
255
+ notifBadge: document.getElementById("notif-badge"),
256
+ notifDropdown: document.getElementById("notif-dropdown"),
257
+
258
+ // Input history
259
+ historyBtn: document.getElementById("history-btn"),
260
+ historyPopover: document.getElementById("history-popover"),
261
+
262
+ // Worktree toggle
263
+ worktreeBtn: document.getElementById("worktree-btn"),
264
+
250
265
  // Sidebar toggle (mobile)
251
266
  sidebarToggleBtn: document.getElementById("sidebar-toggle-btn"),
252
267
  sidebarBackdrop: document.getElementById("sidebar-backdrop"),
@@ -9,6 +9,14 @@ import { sendNotification } from '../ui/notifications.js';
9
9
 
10
10
  const BG_STORAGE_KEY = "claudeck-bg-sessions";
11
11
 
12
+ function createBellNotification(type, title, body, sourceSessionId) {
13
+ fetch('/api/notifications/create', {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify({ type, title, body, sourceSessionId }),
17
+ }).catch(() => {});
18
+ }
19
+
12
20
  // ── localStorage helpers ──
13
21
 
14
22
  function persistBgSessions() {
@@ -99,6 +107,7 @@ export function reconcileBackgroundSessions(activeSessionIds) {
99
107
 
100
108
  export function showErrorToast(sessionId, title, error) {
101
109
  sendNotification('Session Error', `${title}: ${error}`, `error-${sessionId}`);
110
+ createBellNotification('error', `Session "${title}" failed`, error?.slice(0, 200), sessionId);
102
111
 
103
112
  const container = document.getElementById("toast-container");
104
113
  if (!container) return;
@@ -124,6 +133,7 @@ export function showErrorToast(sessionId, title, error) {
124
133
 
125
134
  export function showCompletionToast(sessionId, title, projectPath) {
126
135
  sendNotification('Session Completed', title, `done-${sessionId}`);
136
+ createBellNotification('session', `Session "${title}" completed`, null, sessionId);
127
137
 
128
138
  const container = document.getElementById("toast-container");
129
139
  if (!container) return;
@@ -154,6 +164,7 @@ export function showCompletionToast(sessionId, title, projectPath) {
154
164
 
155
165
  export function showInputNeededToast(sessionId, title, projectPath) {
156
166
  sendNotification('Input Needed', `${title} is waiting for your response`, `input-${sessionId}`);
167
+ createBellNotification('approval', `"${title}" needs your input`, 'Waiting for your response', sessionId);
157
168
 
158
169
  const container = document.getElementById("toast-container");
159
170
  if (!container) return;