@cluesmith/codev 1.3.0 → 1.4.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.
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +2 -1
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +12 -51
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +13 -1
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/servers/dashboard-server.js +107 -5
- package/dist/agent-farm/servers/dashboard-server.js.map +1 -1
- package/dist/agent-farm/types.d.ts +1 -0
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -17
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +27 -2
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +23 -7
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +51 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +23 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
- package/skeleton/DEPENDENCIES.md +3 -3
- package/skeleton/protocols/maintain/protocol.md +2 -2
- package/skeleton/{docs → resources}/commands/codev.md +0 -39
- package/skeleton/{docs → resources}/commands/consult.md +12 -2
- package/skeleton/{docs → resources}/commands/overview.md +0 -1
- package/skeleton/roles/architect.md +22 -0
- package/skeleton/roles/builder.md +22 -0
- package/skeleton/templates/arch.md +56 -0
- package/skeleton/templates/pr-overview.md +73 -0
- package/templates/dashboard-split.html +408 -41
- package/templates/open.html +278 -0
- package/templates/tower.html +71 -12
- package/dist/agent-farm/index.d.ts +0 -7
- package/dist/agent-farm/index.d.ts.map +0 -1
- package/dist/agent-farm/index.js +0 -373
- package/dist/agent-farm/index.js.map +0 -1
- package/skeleton/bin/agent-farm +0 -7
- package/skeleton/bin/codev-doctor +0 -335
- package/skeleton/resources/lessons-learned.md +0 -30
- /package/skeleton/{roles/review-types → consult-types}/impl-review.md +0 -0
- /package/skeleton/{roles/review-types → consult-types}/integration-review.md +0 -0
- /package/skeleton/{roles/review-types → consult-types}/plan-review.md +0 -0
- /package/skeleton/{roles/review-types → consult-types}/pr-ready.md +0 -0
- /package/skeleton/{roles/review-types → consult-types}/spec-review.md +0 -0
- /package/skeleton/{docs → resources}/commands/agent-farm.md +0 -0
- /package/skeleton/{AGENTS.md.template → templates/AGENTS.md} +0 -0
- /package/skeleton/{CLAUDE.md.template → templates/CLAUDE.md} +0 -0
|
@@ -1288,6 +1288,198 @@
|
|
|
1288
1288
|
color: var(--text-secondary);
|
|
1289
1289
|
font-size: 13px;
|
|
1290
1290
|
}
|
|
1291
|
+
|
|
1292
|
+
/* Dashboard Tab Styles (Spec 0057) */
|
|
1293
|
+
.dashboard-container {
|
|
1294
|
+
flex: 1;
|
|
1295
|
+
overflow-y: auto;
|
|
1296
|
+
display: flex;
|
|
1297
|
+
flex-direction: column;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.dashboard-header {
|
|
1301
|
+
display: grid;
|
|
1302
|
+
grid-template-columns: 1fr 1fr;
|
|
1303
|
+
gap: 16px;
|
|
1304
|
+
padding: 16px;
|
|
1305
|
+
flex-shrink: 0;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
@media (max-width: 900px) {
|
|
1309
|
+
.dashboard-header {
|
|
1310
|
+
grid-template-columns: 1fr;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
.dashboard-column {
|
|
1315
|
+
background: var(--bg-secondary);
|
|
1316
|
+
border: 1px solid var(--border);
|
|
1317
|
+
border-radius: 8px;
|
|
1318
|
+
padding: 12px;
|
|
1319
|
+
overflow: hidden;
|
|
1320
|
+
display: flex;
|
|
1321
|
+
flex-direction: column;
|
|
1322
|
+
max-height: 280px;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
.dashboard-column-header {
|
|
1326
|
+
display: flex;
|
|
1327
|
+
justify-content: space-between;
|
|
1328
|
+
align-items: center;
|
|
1329
|
+
margin-bottom: 8px;
|
|
1330
|
+
flex-shrink: 0;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
.dashboard-column-header h3 {
|
|
1334
|
+
font-size: 12px;
|
|
1335
|
+
text-transform: uppercase;
|
|
1336
|
+
color: var(--text-muted);
|
|
1337
|
+
letter-spacing: 0.5px;
|
|
1338
|
+
margin: 0;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
.dashboard-column-header .header-actions {
|
|
1342
|
+
display: flex;
|
|
1343
|
+
gap: 4px;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
.dashboard-column-header .header-actions button {
|
|
1347
|
+
padding: 4px 8px;
|
|
1348
|
+
border-radius: 4px;
|
|
1349
|
+
border: 1px solid var(--border);
|
|
1350
|
+
background: var(--bg-tertiary);
|
|
1351
|
+
color: var(--text-secondary);
|
|
1352
|
+
cursor: pointer;
|
|
1353
|
+
font-size: 11px;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
.dashboard-column-header .header-actions button:hover {
|
|
1357
|
+
background: var(--tab-hover);
|
|
1358
|
+
color: var(--text-primary);
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
.dashboard-tabs-list {
|
|
1362
|
+
flex: 1;
|
|
1363
|
+
overflow-y: auto;
|
|
1364
|
+
margin-bottom: 8px;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.dashboard-tab-item {
|
|
1368
|
+
display: flex;
|
|
1369
|
+
align-items: center;
|
|
1370
|
+
gap: 8px;
|
|
1371
|
+
padding: 6px 8px;
|
|
1372
|
+
border-radius: 4px;
|
|
1373
|
+
cursor: pointer;
|
|
1374
|
+
font-size: 13px;
|
|
1375
|
+
color: var(--text-secondary);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
.dashboard-tab-item:hover {
|
|
1379
|
+
background: var(--bg-tertiary);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
.dashboard-tab-item.active {
|
|
1383
|
+
background: var(--accent);
|
|
1384
|
+
color: white;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
.dashboard-tab-item .tab-icon {
|
|
1388
|
+
font-size: 14px;
|
|
1389
|
+
flex-shrink: 0;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
.dashboard-tab-item .tab-name {
|
|
1393
|
+
flex: 1;
|
|
1394
|
+
overflow: hidden;
|
|
1395
|
+
text-overflow: ellipsis;
|
|
1396
|
+
white-space: nowrap;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.dashboard-actions {
|
|
1400
|
+
flex-shrink: 0;
|
|
1401
|
+
display: flex;
|
|
1402
|
+
gap: 8px;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
.dashboard-actions .btn-action {
|
|
1406
|
+
flex: 1;
|
|
1407
|
+
padding: 8px 12px;
|
|
1408
|
+
border-radius: 4px;
|
|
1409
|
+
border: 1px dashed var(--border);
|
|
1410
|
+
background: transparent;
|
|
1411
|
+
color: var(--text-muted);
|
|
1412
|
+
cursor: pointer;
|
|
1413
|
+
font-size: 12px;
|
|
1414
|
+
display: flex;
|
|
1415
|
+
align-items: center;
|
|
1416
|
+
justify-content: center;
|
|
1417
|
+
gap: 4px;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
.dashboard-actions .btn-action:hover {
|
|
1421
|
+
border-style: solid;
|
|
1422
|
+
color: var(--text-secondary);
|
|
1423
|
+
background: var(--bg-tertiary);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
.dashboard-files-list {
|
|
1427
|
+
flex: 1;
|
|
1428
|
+
overflow-y: auto;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
.dashboard-files-list .tree-item {
|
|
1432
|
+
padding: 3px 6px;
|
|
1433
|
+
font-size: 12px;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
.dashboard-files-list .tree-item-name {
|
|
1437
|
+
font-size: 12px;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.dashboard-empty-state {
|
|
1441
|
+
color: var(--text-muted);
|
|
1442
|
+
font-size: 13px;
|
|
1443
|
+
padding: 12px;
|
|
1444
|
+
text-align: center;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
.dashboard-projects {
|
|
1448
|
+
flex: 1;
|
|
1449
|
+
overflow-y: auto;
|
|
1450
|
+
padding: 0 16px 16px 16px;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/* Status indicators in dashboard tab list */
|
|
1454
|
+
.dashboard-status-indicator {
|
|
1455
|
+
width: 8px;
|
|
1456
|
+
height: 8px;
|
|
1457
|
+
border-radius: 50%;
|
|
1458
|
+
flex-shrink: 0;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
.dashboard-status-working {
|
|
1462
|
+
background: var(--status-active);
|
|
1463
|
+
animation: status-pulse 2s ease-in-out infinite;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
.dashboard-status-idle {
|
|
1467
|
+
background: var(--status-waiting);
|
|
1468
|
+
animation: status-blink-slow 3s ease-in-out infinite;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
.dashboard-status-blocked {
|
|
1472
|
+
background: var(--status-error);
|
|
1473
|
+
animation: status-blink-fast 0.8s ease-in-out infinite;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1477
|
+
.dashboard-status-working,
|
|
1478
|
+
.dashboard-status-idle,
|
|
1479
|
+
.dashboard-status-blocked {
|
|
1480
|
+
animation: none;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1291
1483
|
</style>
|
|
1292
1484
|
</head>
|
|
1293
1485
|
<body>
|
|
@@ -1516,11 +1708,11 @@
|
|
|
1516
1708
|
const previousTabIds = new Set(tabs.map(t => t.id));
|
|
1517
1709
|
tabs = [];
|
|
1518
1710
|
|
|
1519
|
-
//
|
|
1711
|
+
// Dashboard tab is ALWAYS first and uncloseable (Spec 0045, 0057)
|
|
1520
1712
|
tabs.push({
|
|
1521
|
-
id: '
|
|
1522
|
-
type: '
|
|
1523
|
-
name: '
|
|
1713
|
+
id: 'dashboard',
|
|
1714
|
+
type: 'dashboard',
|
|
1715
|
+
name: 'Dashboard',
|
|
1524
1716
|
closeable: false
|
|
1525
1717
|
});
|
|
1526
1718
|
|
|
@@ -1569,7 +1761,7 @@
|
|
|
1569
1761
|
|
|
1570
1762
|
// Detect new tabs and auto-switch to them (skip projects tab)
|
|
1571
1763
|
for (const tab of tabs) {
|
|
1572
|
-
if (tab.id !== '
|
|
1764
|
+
if (tab.id !== 'dashboard' && tab.id !== 'files' && !knownTabIds.has(tab.id) && previousTabIds.size > 0) {
|
|
1573
1765
|
// This is a new tab - switch to it
|
|
1574
1766
|
activeTabId = tab.id;
|
|
1575
1767
|
break;
|
|
@@ -1579,9 +1771,9 @@
|
|
|
1579
1771
|
// Update known tab IDs
|
|
1580
1772
|
knownTabIds = new Set(tabs.map(t => t.id));
|
|
1581
1773
|
|
|
1582
|
-
// Set active tab to
|
|
1774
|
+
// Set active tab to Dashboard on first load if none selected
|
|
1583
1775
|
if (!activeTabId) {
|
|
1584
|
-
activeTabId = '
|
|
1776
|
+
activeTabId = 'dashboard';
|
|
1585
1777
|
}
|
|
1586
1778
|
}
|
|
1587
1779
|
|
|
@@ -1663,7 +1855,7 @@
|
|
|
1663
1855
|
// Get tab icon
|
|
1664
1856
|
function getTabIcon(type) {
|
|
1665
1857
|
switch (type) {
|
|
1666
|
-
case '
|
|
1858
|
+
case 'dashboard': return '📋';
|
|
1667
1859
|
case 'files': return '📁';
|
|
1668
1860
|
case 'file': return '📄';
|
|
1669
1861
|
case 'builder': return '🔨';
|
|
@@ -1762,12 +1954,12 @@
|
|
|
1762
1954
|
return;
|
|
1763
1955
|
}
|
|
1764
1956
|
|
|
1765
|
-
// Handle
|
|
1766
|
-
if (tab.type === '
|
|
1767
|
-
if (currentTabType !== '
|
|
1768
|
-
currentTabType = '
|
|
1957
|
+
// Handle dashboard tab specially (no iframe, inline content)
|
|
1958
|
+
if (tab.type === 'dashboard') {
|
|
1959
|
+
if (currentTabType !== 'dashboard') {
|
|
1960
|
+
currentTabType = 'dashboard';
|
|
1769
1961
|
currentTabPort = null;
|
|
1770
|
-
|
|
1962
|
+
renderDashboardTab();
|
|
1771
1963
|
}
|
|
1772
1964
|
return;
|
|
1773
1965
|
}
|
|
@@ -3113,13 +3305,26 @@
|
|
|
3113
3305
|
} else {
|
|
3114
3306
|
filesTreeExpanded.add(path);
|
|
3115
3307
|
}
|
|
3116
|
-
|
|
3308
|
+
rerenderFilesBrowser();
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
// Re-render file browser in current context (dashboard or files tab)
|
|
3312
|
+
function rerenderFilesBrowser() {
|
|
3313
|
+
if (activeTabId === 'dashboard') {
|
|
3314
|
+
// Re-render just the files column in dashboard
|
|
3315
|
+
const filesListEl = document.getElementById('dashboard-files-list');
|
|
3316
|
+
if (filesListEl) {
|
|
3317
|
+
filesListEl.innerHTML = renderDashboardFilesBrowser();
|
|
3318
|
+
}
|
|
3319
|
+
} else if (activeTabId === 'files') {
|
|
3320
|
+
renderFilesTabContent();
|
|
3321
|
+
}
|
|
3117
3322
|
}
|
|
3118
3323
|
|
|
3119
3324
|
// Collapse all folders
|
|
3120
3325
|
function collapseAllFolders() {
|
|
3121
3326
|
filesTreeExpanded.clear();
|
|
3122
|
-
|
|
3327
|
+
rerenderFilesBrowser();
|
|
3123
3328
|
}
|
|
3124
3329
|
|
|
3125
3330
|
// Expand all folders
|
|
@@ -3135,13 +3340,13 @@
|
|
|
3135
3340
|
}
|
|
3136
3341
|
}
|
|
3137
3342
|
collectPaths(filesTreeData);
|
|
3138
|
-
|
|
3343
|
+
rerenderFilesBrowser();
|
|
3139
3344
|
}
|
|
3140
3345
|
|
|
3141
3346
|
// Refresh files tree
|
|
3142
3347
|
async function refreshFilesTree() {
|
|
3143
3348
|
await loadFilesTree();
|
|
3144
|
-
|
|
3349
|
+
rerenderFilesBrowser();
|
|
3145
3350
|
showToast('Files refreshed', 'success');
|
|
3146
3351
|
}
|
|
3147
3352
|
|
|
@@ -3197,47 +3402,209 @@
|
|
|
3197
3402
|
`;
|
|
3198
3403
|
}
|
|
3199
3404
|
|
|
3200
|
-
// Render the
|
|
3201
|
-
function
|
|
3405
|
+
// Render the dashboard tab content (internal - called after data is loaded)
|
|
3406
|
+
function renderDashboardTabContent() {
|
|
3202
3407
|
const content = document.getElementById('tab-content');
|
|
3203
3408
|
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
<div class="
|
|
3207
|
-
|
|
3409
|
+
content.innerHTML = `
|
|
3410
|
+
<div class="dashboard-container">
|
|
3411
|
+
<div class="dashboard-header">
|
|
3412
|
+
<!-- Left Column: Tabs -->
|
|
3413
|
+
<div class="dashboard-column">
|
|
3414
|
+
<div class="dashboard-column-header">
|
|
3415
|
+
<h3>Tabs</h3>
|
|
3416
|
+
</div>
|
|
3417
|
+
<div class="dashboard-tabs-list" id="dashboard-tabs-list">
|
|
3418
|
+
${renderDashboardTabsList()}
|
|
3419
|
+
</div>
|
|
3420
|
+
<div class="dashboard-actions">
|
|
3421
|
+
<button class="btn-action" onclick="createNewShell()" title="New utility shell">+ New Shell</button>
|
|
3422
|
+
<button class="btn-action" onclick="createNewWorktreeShell()" title="New worktree shell">+ New Worktree</button>
|
|
3423
|
+
</div>
|
|
3424
|
+
</div>
|
|
3425
|
+
<!-- Right Column: Files -->
|
|
3426
|
+
<div class="dashboard-column">
|
|
3427
|
+
<div class="dashboard-column-header">
|
|
3428
|
+
<h3>Files</h3>
|
|
3429
|
+
<div class="header-actions">
|
|
3430
|
+
<button onclick="collapseAllFolders()" title="Collapse All">⊟</button>
|
|
3431
|
+
<button onclick="expandAllFolders()" title="Expand All">⊞</button>
|
|
3432
|
+
</div>
|
|
3433
|
+
</div>
|
|
3434
|
+
<div class="dashboard-files-list" id="dashboard-files-list">
|
|
3435
|
+
${renderDashboardFilesBrowser()}
|
|
3436
|
+
</div>
|
|
3437
|
+
</div>
|
|
3438
|
+
</div>
|
|
3439
|
+
<div class="dashboard-projects" id="dashboard-projects">
|
|
3440
|
+
${renderDashboardProjectsSection()}
|
|
3441
|
+
</div>
|
|
3442
|
+
</div>
|
|
3443
|
+
`;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
// Render the tabs list for dashboard
|
|
3447
|
+
function renderDashboardTabsList() {
|
|
3448
|
+
// Filter to show terminal tabs only (not Dashboard/Files tabs)
|
|
3449
|
+
const terminalTabs = tabs.filter(t => t.type !== 'dashboard' && t.type !== 'files');
|
|
3450
|
+
|
|
3451
|
+
if (terminalTabs.length === 0) {
|
|
3452
|
+
return '<div class="dashboard-empty-state">No tabs open</div>';
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
return terminalTabs.map(tab => {
|
|
3456
|
+
const isActive = tab.id === activeTabId;
|
|
3457
|
+
const icon = getTabIcon(tab.type);
|
|
3458
|
+
const statusIndicator = getDashboardStatusIndicator(tab);
|
|
3459
|
+
|
|
3460
|
+
return `
|
|
3461
|
+
<div class="dashboard-tab-item ${isActive ? 'active' : ''}" onclick="selectTab('${tab.id}')">
|
|
3462
|
+
${statusIndicator}
|
|
3463
|
+
<span class="tab-icon">${icon}</span>
|
|
3464
|
+
<span class="tab-name">${escapeHtml(tab.name)}</span>
|
|
3208
3465
|
</div>
|
|
3209
3466
|
`;
|
|
3210
|
-
|
|
3467
|
+
}).join('');
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
// Get status indicator for dashboard tab list
|
|
3471
|
+
function getDashboardStatusIndicator(tab) {
|
|
3472
|
+
if (tab.type !== 'builder') return '';
|
|
3473
|
+
|
|
3474
|
+
// Use builder status from state
|
|
3475
|
+
const builderState = (state.builders || []).find(b => `builder-${b.id}` === tab.id);
|
|
3476
|
+
if (!builderState) return '';
|
|
3477
|
+
|
|
3478
|
+
const status = builderState.status;
|
|
3479
|
+
if (['spawning', 'implementing'].includes(status)) {
|
|
3480
|
+
return '<span class="dashboard-status-indicator dashboard-status-working" title="Working"></span>';
|
|
3481
|
+
}
|
|
3482
|
+
if (status === 'blocked') {
|
|
3483
|
+
return '<span class="dashboard-status-indicator dashboard-status-blocked" title="Blocked"></span>';
|
|
3484
|
+
}
|
|
3485
|
+
if (['pr-ready', 'complete'].includes(status)) {
|
|
3486
|
+
return '<span class="dashboard-status-indicator dashboard-status-idle" title="Idle"></span>';
|
|
3487
|
+
}
|
|
3488
|
+
return '';
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
// Render compact file browser for dashboard
|
|
3492
|
+
function renderDashboardFilesBrowser() {
|
|
3493
|
+
if (filesTreeError) {
|
|
3494
|
+
return `<div class="dashboard-empty-state">${escapeHtml(filesTreeError)}</div>`;
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
if (!filesTreeLoaded || filesTreeData.length === 0) {
|
|
3498
|
+
return '<div class="dashboard-empty-state">Loading files...</div>';
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
return renderTreeNodes(filesTreeData, 0);
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
// Render the projects section for dashboard
|
|
3505
|
+
function renderDashboardProjectsSection() {
|
|
3506
|
+
if (projectlistError) {
|
|
3507
|
+
return renderErrorBanner(projectlistError);
|
|
3211
3508
|
}
|
|
3212
3509
|
|
|
3213
3510
|
if (projectsData.length === 0) {
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3511
|
+
// No welcome screen - just a helpful message
|
|
3512
|
+
return `
|
|
3513
|
+
<div class="dashboard-empty-state" style="padding: 24px;">
|
|
3514
|
+
No projects yet. Ask the Architect to create your first project.
|
|
3217
3515
|
</div>
|
|
3218
3516
|
`;
|
|
3219
|
-
return;
|
|
3220
3517
|
}
|
|
3221
3518
|
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
</div>
|
|
3519
|
+
// Render the existing project view (unchanged)
|
|
3520
|
+
return `
|
|
3521
|
+
${renderInfoHeader()}
|
|
3522
|
+
${renderKanbanGrid(projectsData)}
|
|
3523
|
+
${renderTerminalProjects(projectsData)}
|
|
3228
3524
|
`;
|
|
3229
3525
|
}
|
|
3230
3526
|
|
|
3231
|
-
//
|
|
3232
|
-
async function
|
|
3527
|
+
// Create new utility shell (quick action button)
|
|
3528
|
+
async function createNewShell() {
|
|
3529
|
+
try {
|
|
3530
|
+
const response = await fetch('/api/tabs/shell', { method: 'POST' });
|
|
3531
|
+
const data = await response.json();
|
|
3532
|
+
if (!data.success && data.error) {
|
|
3533
|
+
showToast(data.error || 'Failed to create shell', 'error');
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
await refresh();
|
|
3537
|
+
if (data.id) {
|
|
3538
|
+
selectTab(`shell-${data.id}`);
|
|
3539
|
+
}
|
|
3540
|
+
showToast('Shell created', 'success');
|
|
3541
|
+
} catch (err) {
|
|
3542
|
+
showToast('Network error: ' + err.message, 'error');
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
// Create new worktree shell (quick action button)
|
|
3547
|
+
async function createNewWorktreeShell() {
|
|
3548
|
+
const branch = prompt('Branch name (leave empty for temp worktree):');
|
|
3549
|
+
if (branch === null) return; // User cancelled
|
|
3550
|
+
|
|
3551
|
+
try {
|
|
3552
|
+
const response = await fetch('/api/tabs/shell', {
|
|
3553
|
+
method: 'POST',
|
|
3554
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3555
|
+
body: JSON.stringify({ worktree: true, branch: branch || undefined })
|
|
3556
|
+
});
|
|
3557
|
+
const data = await response.json();
|
|
3558
|
+
if (!data.success && data.error) {
|
|
3559
|
+
showToast(data.error || 'Failed to create worktree shell', 'error');
|
|
3560
|
+
return;
|
|
3561
|
+
}
|
|
3562
|
+
await refresh();
|
|
3563
|
+
// Auto-select the newly created tab (consistent with createNewShell behavior)
|
|
3564
|
+
if (data.id) {
|
|
3565
|
+
selectTab(`shell-${data.id}`);
|
|
3566
|
+
}
|
|
3567
|
+
showToast('Worktree shell created', 'success');
|
|
3568
|
+
} catch (err) {
|
|
3569
|
+
showToast('Network error: ' + err.message, 'error');
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3573
|
+
// Render the dashboard tab (entry point - loads data first)
|
|
3574
|
+
async function renderDashboardTab() {
|
|
3233
3575
|
const content = document.getElementById('tab-content');
|
|
3234
|
-
content.innerHTML = '<div class="
|
|
3576
|
+
content.innerHTML = '<div class="dashboard-container"><p style="color: var(--text-muted); padding: 16px;">Loading dashboard...</p></div>';
|
|
3235
3577
|
|
|
3236
|
-
|
|
3237
|
-
|
|
3578
|
+
// Load both projectlist and files tree in parallel
|
|
3579
|
+
await Promise.all([
|
|
3580
|
+
loadProjectlist(),
|
|
3581
|
+
loadFilesTreeIfNeeded()
|
|
3582
|
+
]);
|
|
3583
|
+
|
|
3584
|
+
renderDashboardTabContent();
|
|
3238
3585
|
checkStarterMode(); // Update polling state after initial load
|
|
3239
3586
|
}
|
|
3240
3587
|
|
|
3588
|
+
// Load files tree if not already loaded
|
|
3589
|
+
async function loadFilesTreeIfNeeded() {
|
|
3590
|
+
if (!filesTreeLoaded) {
|
|
3591
|
+
await loadFilesTree();
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
// Legacy function for backward compatibility (still used by polling)
|
|
3596
|
+
function renderProjectsTabContent() {
|
|
3597
|
+
// If dashboard tab is active, re-render dashboard instead
|
|
3598
|
+
if (activeTabId === 'dashboard') {
|
|
3599
|
+
renderDashboardTabContent();
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
// Legacy function alias
|
|
3604
|
+
async function renderProjectsTab() {
|
|
3605
|
+
await renderDashboardTab();
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3241
3608
|
// Load projectlist.md from disk
|
|
3242
3609
|
async function loadProjectlist() {
|
|
3243
3610
|
try {
|
|
@@ -3282,8 +3649,8 @@
|
|
|
3282
3649
|
|
|
3283
3650
|
// Poll projectlist for changes (every 5 seconds)
|
|
3284
3651
|
async function pollProjectlist() {
|
|
3285
|
-
// Only poll if
|
|
3286
|
-
if (activeTabId !== '
|
|
3652
|
+
// Only poll if dashboard tab is active
|
|
3653
|
+
if (activeTabId !== 'dashboard') return;
|
|
3287
3654
|
|
|
3288
3655
|
try {
|
|
3289
3656
|
const response = await fetch('/file?path=codev/projectlist.md');
|