cast-code 1.0.9 → 1.0.11

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 (30) hide show
  1. package/dist/modules/core/services/deep-agent.service.js +1 -1
  2. package/dist/modules/core/services/deep-agent.service.js.map +1 -1
  3. package/dist/modules/kanban/kanban.module.js +36 -0
  4. package/dist/modules/kanban/kanban.module.js.map +1 -0
  5. package/dist/modules/kanban/services/kanban-server.service.js +322 -0
  6. package/dist/modules/kanban/services/kanban-server.service.js.map +1 -0
  7. package/dist/modules/kanban/views/kanban-ui.js +858 -0
  8. package/dist/modules/kanban/views/kanban-ui.js.map +1 -0
  9. package/dist/modules/mentions/services/mentions.service.js +3 -3
  10. package/dist/modules/mentions/services/mentions.service.js.map +1 -1
  11. package/dist/modules/permissions/services/prompt.service.js +17 -70
  12. package/dist/modules/permissions/services/prompt.service.js.map +1 -1
  13. package/dist/modules/repl/repl.module.js +3 -1
  14. package/dist/modules/repl/repl.module.js.map +1 -1
  15. package/dist/modules/repl/services/commands/project-commands.service.js +7 -9
  16. package/dist/modules/repl/services/commands/project-commands.service.js.map +1 -1
  17. package/dist/modules/repl/services/repl.service.js +112 -38
  18. package/dist/modules/repl/services/repl.service.js.map +1 -1
  19. package/dist/modules/repl/services/smart-input.js +2 -2
  20. package/dist/modules/repl/services/smart-input.js.map +1 -1
  21. package/dist/modules/tasks/services/plan-executor.service.js +2 -2
  22. package/dist/modules/tasks/services/plan-executor.service.js.map +1 -1
  23. package/dist/modules/tasks/services/plan-mode.service.js +11 -10
  24. package/dist/modules/tasks/services/plan-mode.service.js.map +1 -1
  25. package/dist/modules/tasks/services/task-management.service.js +54 -21
  26. package/dist/modules/tasks/services/task-management.service.js.map +1 -1
  27. package/dist/modules/tasks/services/task-tools.service.js +2 -2
  28. package/dist/modules/tasks/services/task-tools.service.js.map +1 -1
  29. package/dist/modules/tasks/types/task.types.js.map +1 -1
  30. package/package.json +1 -1
@@ -0,0 +1,858 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "getKanbanHtml", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return getKanbanHtml;
9
+ }
10
+ });
11
+ function getKanbanHtml() {
12
+ return `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="UTF-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
+ <title>cast · kanban</title>
18
+ <style>
19
+ * { box-sizing: border-box; margin: 0; padding: 0; }
20
+
21
+ :root {
22
+ --bg: #0d1117;
23
+ --surface: #161b22;
24
+ --card: #21262d;
25
+ --border: #30363d;
26
+ --border-hover: #484f58;
27
+ --text: #e6edf3;
28
+ --muted: #7d8590;
29
+ --cyan: #58a6ff;
30
+ --green: #3fb950;
31
+ --yellow: #d29922;
32
+ --red: #f85149;
33
+ --orange: #e3b341;
34
+ --purple: #bc8cff;
35
+ }
36
+
37
+ body {
38
+ background: var(--bg);
39
+ color: var(--text);
40
+ font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
41
+ font-size: 13px;
42
+ height: 100vh;
43
+ display: flex;
44
+ flex-direction: column;
45
+ overflow: hidden;
46
+ }
47
+
48
+ header {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 12px;
52
+ padding: 12px 20px;
53
+ border-bottom: 1px solid var(--border);
54
+ background: var(--surface);
55
+ flex-shrink: 0;
56
+ }
57
+
58
+ .logo {
59
+ font-weight: 700;
60
+ font-size: 14px;
61
+ color: var(--cyan);
62
+ letter-spacing: 0.05em;
63
+ }
64
+
65
+ .logo span {
66
+ color: var(--muted);
67
+ font-weight: 400;
68
+ }
69
+
70
+ .live-dot {
71
+ width: 7px;
72
+ height: 7px;
73
+ border-radius: 50%;
74
+ background: var(--green);
75
+ box-shadow: 0 0 6px var(--green);
76
+ animation: pulse 2s infinite;
77
+ }
78
+
79
+ .live-dot.disconnected {
80
+ background: var(--red);
81
+ box-shadow: 0 0 6px var(--red);
82
+ animation: none;
83
+ }
84
+
85
+ @keyframes pulse {
86
+ 0%, 100% { opacity: 1; }
87
+ 50% { opacity: 0.4; }
88
+ }
89
+
90
+ .header-right {
91
+ margin-left: auto;
92
+ display: flex;
93
+ align-items: center;
94
+ gap: 16px;
95
+ }
96
+
97
+ .btn-primary {
98
+ background: var(--cyan);
99
+ color: var(--bg);
100
+ border: none;
101
+ padding: 6px 12px;
102
+ border-radius: 4px;
103
+ font-size: 12px;
104
+ font-weight: 700;
105
+ cursor: pointer;
106
+ transition: opacity 0.2s;
107
+ }
108
+
109
+ .btn-primary:hover {
110
+ opacity: 0.9;
111
+ }
112
+
113
+ .btn-primary:disabled {
114
+ opacity: 0.5;
115
+ cursor: not-allowed;
116
+ }
117
+
118
+ .task-count {
119
+ color: var(--muted);
120
+ font-size: 12px;
121
+ }
122
+
123
+ .plan-badge {
124
+ background: rgba(88, 166, 255, 0.1);
125
+ border: 1px solid rgba(88, 166, 255, 0.3);
126
+ color: var(--cyan);
127
+ padding: 2px 8px;
128
+ border-radius: 4px;
129
+ font-size: 11px;
130
+ }
131
+
132
+ /* Modal */
133
+ .modal-overlay {
134
+ position: fixed;
135
+ top: 0;
136
+ left: 0;
137
+ right: 0;
138
+ bottom: 0;
139
+ background: rgba(0, 0, 0, 0.7);
140
+ display: none;
141
+ align-items: center;
142
+ justify-content: center;
143
+ z-index: 1000;
144
+ backdrop-filter: blur(2px);
145
+ }
146
+
147
+ .modal {
148
+ background: var(--surface);
149
+ border: 1px solid var(--border);
150
+ border-radius: 8px;
151
+ width: 100%;
152
+ max-width: 450px;
153
+ padding: 24px;
154
+ box-shadow: 0 10px 25px rgba(0,0,0,0.5);
155
+ }
156
+
157
+ .modal-title {
158
+ font-size: 16px;
159
+ font-weight: 700;
160
+ margin-bottom: 20px;
161
+ color: var(--cyan);
162
+ }
163
+
164
+ .form-group {
165
+ margin-bottom: 16px;
166
+ }
167
+
168
+ .form-group label {
169
+ display: block;
170
+ font-size: 11px;
171
+ color: var(--muted);
172
+ text-transform: uppercase;
173
+ margin-bottom: 6px;
174
+ font-weight: 600;
175
+ }
176
+
177
+ .form-group input, .form-group textarea {
178
+ width: 100%;
179
+ background: var(--bg);
180
+ border: 1px solid var(--border);
181
+ border-radius: 4px;
182
+ color: var(--text);
183
+ padding: 8px 12px;
184
+ font-family: inherit;
185
+ font-size: 13px;
186
+ outline: none;
187
+ }
188
+
189
+ .form-group input:focus, .form-group textarea:focus {
190
+ border-color: var(--cyan);
191
+ }
192
+
193
+ .modal-actions {
194
+ display: flex;
195
+ justify-content: flex-end;
196
+ gap: 12px;
197
+ margin-top: 24px;
198
+ }
199
+
200
+ .btn-ghost {
201
+ background: transparent;
202
+ color: var(--muted);
203
+ border: 1px solid var(--border);
204
+ padding: 6px 12px;
205
+ border-radius: 4px;
206
+ font-size: 12px;
207
+ cursor: pointer;
208
+ }
209
+
210
+ .btn-ghost:hover {
211
+ border-color: var(--border-hover);
212
+ color: var(--text);
213
+ }
214
+
215
+ .board {
216
+ display: grid;
217
+ grid-template-columns: repeat(4, 1fr);
218
+ gap: 12px;
219
+ padding: 16px;
220
+ flex: 1;
221
+ overflow: hidden;
222
+ min-height: 0;
223
+ }
224
+
225
+ .column {
226
+ background: var(--surface);
227
+ border: 1px solid var(--border);
228
+ border-radius: 8px;
229
+ display: flex;
230
+ flex-direction: column;
231
+ overflow: hidden;
232
+ transition: background 0.2s;
233
+ }
234
+
235
+ .column.drag-over {
236
+ background: rgba(88, 166, 255, 0.05);
237
+ border-color: var(--cyan);
238
+ }
239
+
240
+ .column-header {
241
+ padding: 10px 14px;
242
+ border-bottom: 1px solid var(--border);
243
+ display: flex;
244
+ align-items: center;
245
+ gap: 8px;
246
+ flex-shrink: 0;
247
+ }
248
+
249
+ .column-label {
250
+ font-size: 11px;
251
+ font-weight: 700;
252
+ text-transform: uppercase;
253
+ letter-spacing: 0.08em;
254
+ }
255
+
256
+ .col-pending .column-label { color: var(--muted); }
257
+ .col-inprogress .column-label { color: var(--orange); }
258
+ .col-done .column-label { color: var(--green); }
259
+ .col-failed .column-label { color: var(--red); }
260
+
261
+ .col-inprogress { border-top: 2px solid var(--orange); }
262
+ .col-done { border-top: 2px solid var(--green); }
263
+ .col-failed { border-top: 2px solid var(--red); }
264
+ .col-pending { border-top: 2px solid var(--border); }
265
+
266
+ .column-count {
267
+ margin-left: auto;
268
+ background: var(--card);
269
+ border: 1px solid var(--border);
270
+ border-radius: 10px;
271
+ padding: 1px 7px;
272
+ font-size: 11px;
273
+ color: var(--muted);
274
+ min-width: 22px;
275
+ text-align: center;
276
+ }
277
+
278
+ .cards {
279
+ flex: 1;
280
+ overflow-y: auto;
281
+ padding: 10px;
282
+ display: flex;
283
+ flex-direction: column;
284
+ gap: 8px;
285
+ min-height: 50px;
286
+ }
287
+
288
+ .cards::-webkit-scrollbar { width: 4px; }
289
+ .cards::-webkit-scrollbar-track { background: transparent; }
290
+ .cards::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
291
+
292
+ .card {
293
+ background: var(--card);
294
+ border: 1px solid var(--border);
295
+ border-radius: 6px;
296
+ padding: 10px 12px;
297
+ cursor: grab;
298
+ transition: border-color 0.15s, transform 0.15s;
299
+ animation: cardIn 0.2s ease-out;
300
+ position: relative;
301
+ }
302
+
303
+ .card:active {
304
+ cursor: grabbing;
305
+ }
306
+
307
+ .card.dragging {
308
+ opacity: 0.4;
309
+ transform: scale(0.95);
310
+ }
311
+
312
+ @keyframes cardIn {
313
+ from { opacity: 0; transform: translateY(-4px); }
314
+ to { opacity: 1; transform: translateY(0); }
315
+ }
316
+
317
+ .card:hover {
318
+ border-color: var(--border-hover);
319
+ }
320
+
321
+ .card:hover .card-actions {
322
+ opacity: 1;
323
+ }
324
+
325
+ .card-actions {
326
+ position: absolute;
327
+ top: 8px;
328
+ right: 8px;
329
+ opacity: 0;
330
+ transition: opacity 0.2s;
331
+ }
332
+
333
+ .btn-mini {
334
+ background: var(--bg);
335
+ border: 1px solid var(--border);
336
+ color: var(--cyan);
337
+ border-radius: 3px;
338
+ padding: 2px 6px;
339
+ font-size: 9px;
340
+ font-weight: 700;
341
+ cursor: pointer;
342
+ text-transform: uppercase;
343
+ }
344
+
345
+ .btn-mini:hover {
346
+ border-color: var(--cyan);
347
+ }
348
+
349
+ .btn-mini:disabled {
350
+ opacity: 0.5;
351
+ cursor: not-allowed;
352
+ }
353
+
354
+ .card.highlight {
355
+ animation: highlight 0.6s ease-out;
356
+ }
357
+
358
+ @keyframes highlight {
359
+ 0% { border-color: var(--cyan); box-shadow: 0 0 8px rgba(88, 166, 255, 0.3); }
360
+ 100% { border-color: var(--border); box-shadow: none; }
361
+ }
362
+
363
+ .card-top {
364
+ display: flex;
365
+ align-items: flex-start;
366
+ gap: 8px;
367
+ margin-bottom: 6px;
368
+ padding-right: 40px;
369
+ }
370
+
371
+ .card-id {
372
+ color: var(--muted);
373
+ font-size: 10px;
374
+ flex-shrink: 0;
375
+ margin-top: 2px;
376
+ }
377
+
378
+ .card-subject {
379
+ font-weight: 600;
380
+ font-size: 12px;
381
+ line-height: 1.4;
382
+ flex: 1;
383
+ word-break: break-word;
384
+ }
385
+
386
+ .card-description {
387
+ color: var(--muted);
388
+ font-size: 11px;
389
+ line-height: 1.5;
390
+ margin-bottom: 8px;
391
+ display: -webkit-box;
392
+ -webkit-line-clamp: 2;
393
+ -webkit-box-orient: vertical;
394
+ overflow: hidden;
395
+ }
396
+
397
+ .card-footer {
398
+ display: flex;
399
+ flex-wrap: wrap;
400
+ align-items: center;
401
+ gap: 5px;
402
+ }
403
+
404
+ .badge {
405
+ padding: 2px 6px;
406
+ border-radius: 3px;
407
+ font-size: 10px;
408
+ font-weight: 600;
409
+ text-transform: uppercase;
410
+ letter-spacing: 0.04em;
411
+ }
412
+
413
+ .badge-pending { background: rgba(125, 133, 144, 0.15); color: var(--muted); }
414
+ .badge-in_progress { background: rgba(227, 179, 65, 0.15); color: var(--orange); }
415
+ .badge-completed { background: rgba(63, 185, 80, 0.15); color: var(--green); }
416
+ .badge-failed { background: rgba(248, 81, 73, 0.15); color: var(--red); }
417
+ .badge-blocked { background: rgba(210, 153, 34, 0.15); color: var(--yellow); }
418
+ .badge-cancelled { background: rgba(125, 133, 144, 0.1); color: var(--muted); }
419
+
420
+ .badge-agent {
421
+ background: rgba(188, 140, 255, 0.12);
422
+ color: var(--purple);
423
+ border: 1px solid rgba(188, 140, 255, 0.2);
424
+ font-weight: 400;
425
+ text-transform: none;
426
+ letter-spacing: 0;
427
+ }
428
+
429
+ .badge-dep {
430
+ background: rgba(88, 166, 255, 0.08);
431
+ color: rgba(88, 166, 255, 0.6);
432
+ border: 1px solid rgba(88, 166, 255, 0.15);
433
+ font-weight: 400;
434
+ text-transform: none;
435
+ letter-spacing: 0;
436
+ }
437
+
438
+ .spinner {
439
+ display: inline-block;
440
+ width: 10px;
441
+ height: 10px;
442
+ border: 1.5px solid rgba(227, 179, 65, 0.2);
443
+ border-top-color: var(--orange);
444
+ border-radius: 50%;
445
+ animation: spin 0.8s linear infinite;
446
+ flex-shrink: 0;
447
+ }
448
+
449
+ @keyframes spin {
450
+ to { transform: rotate(360deg); }
451
+ }
452
+
453
+ .empty-state {
454
+ flex: 1;
455
+ display: flex;
456
+ align-items: center;
457
+ justify-content: center;
458
+ color: var(--border);
459
+ font-size: 11px;
460
+ padding: 20px;
461
+ text-align: center;
462
+ }
463
+
464
+ .thinking-indicator {
465
+ color: var(--purple);
466
+ font-size: 11px;
467
+ display: flex;
468
+ align-items: center;
469
+ gap: 6px;
470
+ }
471
+ </style>
472
+ </head>
473
+ <body>
474
+
475
+ <header>
476
+ <div class="logo">cast <span>·</span> kanban</div>
477
+ <div class="live-dot" id="liveDot"></div>
478
+ <div id="header-right-container" class="header-right">
479
+ <button class="btn-primary" onclick="showModal()">+ New Task</button>
480
+ <span class="task-count" id="taskCount">0 tasks</span>
481
+ <span class="plan-badge" id="planBadge" style="display:none"></span>
482
+ </div>
483
+ </header>
484
+
485
+ <div class="board">
486
+ <div class="column col-pending" id="col-pending" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event, 'pending')">
487
+ <div class="column-header">
488
+ <span class="column-label">To-Do</span>
489
+ <button id="btn-auto-planner" class="btn-mini" style="margin-left: 10px; border-color: var(--purple); color: var(--purple);" onclick="runAutoPlanner()">⚡ Run</button>
490
+ <span class="column-count" id="count-pending">0</span>
491
+ </div>
492
+ <div class="cards" id="cards-pending"></div>
493
+ </div>
494
+ <div class="column col-inprogress" id="col-inprogress" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event, 'in_progress')">
495
+ <div class="column-header">
496
+ <span class="column-label">In Progress</span>
497
+ <span class="column-count" id="count-inprogress">0</span>
498
+ </div>
499
+ <div class="cards" id="cards-inprogress"></div>
500
+ </div>
501
+ <div class="column col-done" id="col-done" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event, 'completed')">
502
+ <div class="column-header">
503
+ <span class="column-label">Done</span>
504
+ <span class="column-count" id="count-done">0</span>
505
+ </div>
506
+ <div class="cards" id="cards-done"></div>
507
+ </div>
508
+ <div class="column col-failed" id="col-failed" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event, 'failed')">
509
+ <div class="column-header">
510
+ <span class="column-label">Failed / Blocked</span>
511
+ <span class="column-count" id="count-failed">0</span>
512
+ </div>
513
+ <div class="cards" id="cards-failed"></div>
514
+ </div>
515
+ </div>
516
+
517
+ <div class="modal-overlay" id="modalOverlay">
518
+ <div class="modal">
519
+ <div class="modal-title">Create New Task</div>
520
+ <div class="form-group">
521
+ <label>Subject</label>
522
+ <input type="text" id="taskSubject" placeholder="What needs to be done?">
523
+ </div>
524
+ <div class="form-group">
525
+ <label>Description (Optional)</label>
526
+ <textarea id="taskDescription" rows="3" placeholder="Add more details..."></textarea>
527
+ </div>
528
+ <div class="modal-actions">
529
+ <button class="btn-ghost" onclick="hideModal()">Cancel</button>
530
+ <button class="btn-primary" onclick="createTask()">Create Task</button>
531
+ </div>
532
+ </div>
533
+ </div>
534
+
535
+ <script>
536
+ const COL_MAP = {
537
+ pending: 'pending',
538
+ in_progress: 'inprogress',
539
+ completed: 'done',
540
+ failed: 'failed',
541
+ blocked: 'failed',
542
+ cancelled: 'failed',
543
+ };
544
+
545
+ let tasks = {};
546
+ let sseRetryTimer = null;
547
+ let isAutoPlanning = false;
548
+
549
+ // Drag and Drop State
550
+ let draggedTaskId = null;
551
+
552
+ function handleDragStart(e, taskId) {
553
+ draggedTaskId = taskId;
554
+ e.target.classList.add('dragging');
555
+ e.dataTransfer.setData('text/plain', taskId);
556
+ e.dataTransfer.effectAllowed = 'move';
557
+ }
558
+
559
+ function handleDragEnd(e) {
560
+ e.target.classList.remove('dragging');
561
+ draggedTaskId = null;
562
+ }
563
+
564
+ function handleDragOver(e) {
565
+ e.preventDefault();
566
+ e.currentTarget.classList.add('drag-over');
567
+ e.dataTransfer.dropEffect = 'move';
568
+ }
569
+
570
+ function handleDragLeave(e) {
571
+ e.currentTarget.classList.remove('drag-over');
572
+ }
573
+
574
+ async function handleDrop(e, targetStatus) {
575
+ e.preventDefault();
576
+ e.currentTarget.classList.remove('drag-over');
577
+
578
+ const taskId = e.dataTransfer.getData('text/plain');
579
+ const task = tasks[taskId];
580
+
581
+ if (!task || task.status === targetStatus) return;
582
+
583
+ // Rules
584
+ if (task.status === 'completed' && targetStatus === 'in_progress') {
585
+ const reason = prompt(\`Why are you redoing task "\${task.subject}"?\`);
586
+ if (!reason) return;
587
+
588
+ await updateTask(taskId, {
589
+ status: targetStatus,
590
+ metadata: { ...task.metadata, redoReason: reason, redoneAt: Date.now() }
591
+ });
592
+ } else {
593
+ await updateTask(taskId, { status: targetStatus });
594
+ }
595
+ }
596
+
597
+ async function runAutoPlanner() {
598
+ if (isAutoPlanning) return;
599
+ isAutoPlanning = true;
600
+ renderHeaderRight();
601
+ document.getElementById('btn-auto-planner').disabled = true;
602
+
603
+ try {
604
+ await fetch('/api/tasks/auto-execute', { method: 'POST' });
605
+ } catch (err) {
606
+ console.error('Failed to start auto-planner:', err);
607
+ isAutoPlanning = false;
608
+ renderHeaderRight();
609
+ document.getElementById('btn-auto-planner').disabled = false;
610
+ }
611
+ }
612
+
613
+ function renderHeaderRight() {
614
+ const container = document.getElementById('header-right-container');
615
+ const thinking = isAutoPlanning ? '<span class="thinking-indicator"><div class="spinner" style="border-top-color:var(--purple)"></div> Auto-Planner thinking...</span>' : '';
616
+
617
+ container.innerHTML = \`
618
+ \${thinking}
619
+ <button class="btn-primary" onclick="showModal()">+ New Task</button>
620
+ <span class="task-count" id="taskCount">\${Object.keys(tasks).length} tasks</span>
621
+ <span class="plan-badge" id="planBadge" style="display:none"></span>
622
+ \`;
623
+ }
624
+
625
+ function showModal() {
626
+ document.getElementById('modalOverlay').style.display = 'flex';
627
+ document.getElementById('taskSubject').focus();
628
+ }
629
+
630
+ function hideModal() {
631
+ document.getElementById('modalOverlay').style.display = 'none';
632
+ document.getElementById('taskSubject').value = '';
633
+ document.getElementById('taskDescription').value = '';
634
+ }
635
+
636
+ async function createTask() {
637
+ const subject = document.getElementById('taskSubject').value.trim();
638
+ const description = document.getElementById('taskDescription').value.trim();
639
+
640
+ if (!subject) return;
641
+
642
+ try {
643
+ const res = await fetch('/api/tasks', {
644
+ method: 'POST',
645
+ headers: { 'Content-Type': 'application/json' },
646
+ body: JSON.stringify({ subject, description })
647
+ });
648
+
649
+ if (res.ok) {
650
+ hideModal();
651
+ }
652
+ } catch (err) {
653
+ console.error('Failed to create task:', err);
654
+ }
655
+ }
656
+
657
+ async function updateTask(taskId, updates) {
658
+ try {
659
+ await fetch(\`/api/tasks/\${taskId}\`, {
660
+ method: 'PATCH',
661
+ headers: { 'Content-Type': 'application/json' },
662
+ body: JSON.stringify(updates)
663
+ });
664
+ } catch (err) {
665
+ console.error('Failed to update task:', err);
666
+ }
667
+ }
668
+
669
+ async function executeTask(taskId) {
670
+ try {
671
+ await fetch(\`/api/tasks/\${taskId}/execute\`, { method: 'POST' });
672
+ } catch (err) {
673
+ console.error('Failed to execute task:', err);
674
+ }
675
+ }
676
+
677
+ function col(status) {
678
+ return COL_MAP[status] || 'pending';
679
+ }
680
+
681
+ function makeCard(task) {
682
+ const colKey = col(task.status);
683
+ const isInProgress = task.status === 'in_progress';
684
+ const isPending = task.status === 'pending';
685
+
686
+ const depBadges = task.dependencies.length > 0
687
+ ? task.dependencies.map(d => '<span class="badge badge-dep">↳ ' + d + '</span>').join('')
688
+ : '';
689
+
690
+ const agentBadge = task.assignedAgent
691
+ ? '<span class="badge badge-agent">⬡ ' + task.assignedAgent + '</span>'
692
+ : '';
693
+
694
+ const spinner = isInProgress ? '<div class="spinner"></div>' : '';
695
+
696
+ const actionBtn = isPending
697
+ ? \`<div class="card-actions"><button class="btn-mini" onclick="executeTask('\${task.id}')">Run</button></div>\`
698
+ : '';
699
+
700
+ return \`
701
+ <div class="card"
702
+ id="card-\${task.id}"
703
+ data-status="\${task.status}"
704
+ draggable="true"
705
+ onsubmit="return false;"
706
+ ondragstart="handleDragStart(event, '\${task.id}')"
707
+ ondragend="handleDragEnd(event)">
708
+ \${actionBtn}
709
+ <div class="card-top">
710
+ \${spinner}
711
+ <span class="card-id">\${task.id}</span>
712
+ <span class="card-subject">\${escHtml(task.subject)}</span>
713
+ </div>
714
+ \${task.description ? '<div class="card-description">' + escHtml(task.description) + '</div>' : ''}
715
+ <div class="card-footer">
716
+ <span class="badge badge-\${task.status}">\${task.status.replace('_', ' ')}</span>
717
+ \${agentBadge}
718
+ \${depBadges}
719
+ </div>
720
+ </div>
721
+ \`;
722
+ }
723
+
724
+ function escHtml(str) {
725
+ return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
726
+ }
727
+
728
+ function renderAll() {
729
+ const cols = { pending: [], inprogress: [], done: [], failed: [] };
730
+
731
+ for (const task of Object.values(tasks)) {
732
+ cols[col(task.status)].push(task);
733
+ }
734
+
735
+ for (const [key, list] of Object.entries(cols)) {
736
+ const container = document.getElementById('cards-' + key);
737
+ const count = document.getElementById('count-' + key);
738
+
739
+ if (list.length === 0) {
740
+ container.innerHTML = '<div class="empty-state">—</div>';
741
+ } else {
742
+ container.innerHTML = list.map(makeCard).join('');
743
+ }
744
+ count.textContent = list.length;
745
+ }
746
+
747
+ renderHeaderRight();
748
+ }
749
+
750
+ function upsertTask(task) {
751
+ const existed = !!tasks[task.id];
752
+ tasks[task.id] = task;
753
+
754
+ if (task.status === 'in_progress' || task.status === 'completed') {
755
+ // If we get an update that is progress, stop showing auto-planner thinking
756
+ if (isAutoPlanning) {
757
+ isAutoPlanning = false;
758
+ renderHeaderRight();
759
+ document.getElementById('btn-auto-planner').disabled = false;
760
+ }
761
+ }
762
+
763
+ if (existed) {
764
+ const card = document.getElementById('card-' + task.id);
765
+ const newColKey = col(task.status);
766
+
767
+ if (card) {
768
+ const currentContainer = card.parentElement;
769
+ const targetContainer = document.getElementById('cards-' + newColKey);
770
+
771
+ if (currentContainer !== targetContainer) {
772
+ renderAll();
773
+ } else {
774
+ card.outerHTML = makeCard(task);
775
+ const updated = document.getElementById('card-' + task.id);
776
+ if (updated) {
777
+ updated.classList.add('highlight');
778
+ setTimeout(() => updated.classList.remove('highlight'), 700);
779
+ }
780
+ updateCounts();
781
+ }
782
+ } else {
783
+ renderAll();
784
+ }
785
+ } else {
786
+ renderAll();
787
+ }
788
+ }
789
+
790
+ function updateCounts() {
791
+ const cols = { pending: 0, inprogress: 0, done: 0, failed: 0 };
792
+ for (const task of Object.values(tasks)) {
793
+ cols[col(task.status)]++;
794
+ }
795
+ for (const [key, count] of Object.entries(cols)) {
796
+ document.getElementById('count-' + key).textContent = count;
797
+ }
798
+ renderHeaderRight();
799
+ }
800
+
801
+ async function loadState() {
802
+ try {
803
+ const res = await fetch('/api/state');
804
+ const data = await res.json();
805
+ tasks = {};
806
+ for (const t of data.tasks) tasks[t.id] = t;
807
+
808
+ if (data.plans && data.plans.length > 0) {
809
+ const plan = data.plans[data.plans.length - 1];
810
+ const badge = document.getElementById('planBadge');
811
+ badge.textContent = plan.title;
812
+ badge.style.display = 'inline';
813
+ }
814
+
815
+ renderAll();
816
+ } catch {}
817
+ }
818
+
819
+ function connectSSE() {
820
+ const dot = document.getElementById('liveDot');
821
+ const es = new EventSource('/api/events');
822
+
823
+ es.onopen = () => {
824
+ dot.classList.remove('disconnected');
825
+ if (sseRetryTimer) { clearTimeout(sseRetryTimer); sseRetryTimer = null; }
826
+ };
827
+
828
+ es.addEventListener('task:created', e => {
829
+ const task = JSON.parse(e.data);
830
+ upsertTask(task);
831
+ });
832
+
833
+ es.addEventListener('task:updated', e => {
834
+ const task = JSON.parse(e.data);
835
+ upsertTask(task);
836
+ });
837
+
838
+ es.addEventListener('plan:created', e => {
839
+ const plan = JSON.parse(e.data);
840
+ const badge = document.getElementById('planBadge');
841
+ badge.textContent = plan.title;
842
+ badge.style.display = 'inline';
843
+ });
844
+
845
+ es.onerror = () => {
846
+ dot.classList.add('disconnected');
847
+ es.close();
848
+ sseRetryTimer = setTimeout(connectSSE, 3000);
849
+ };
850
+ }
851
+
852
+ loadState().then(connectSSE);
853
+ </script>
854
+ </body>
855
+ </html>`;
856
+ }
857
+
858
+ //# sourceMappingURL=kanban-ui.js.map