claude-code-kanban 1.14.0 → 1.16.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/package.json +1 -1
- package/public/index.html +38 -1
- package/server.js +44 -7
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
--warning-dim: rgba(240, 180, 41, 0.18);
|
|
37
37
|
--team: #60a5fa;
|
|
38
38
|
--team-dim: rgba(96, 165, 250, 0.18);
|
|
39
|
+
--plan: #86a886;
|
|
40
|
+
--plan-dim: rgba(134, 168, 134, 0.18);
|
|
39
41
|
--mono: 'IBM Plex Mono', monospace;
|
|
40
42
|
--serif: 'Playfair Display', serif;
|
|
41
43
|
}
|
|
@@ -426,6 +428,15 @@
|
|
|
426
428
|
opacity: 0.7;
|
|
427
429
|
}
|
|
428
430
|
|
|
431
|
+
.session-plan {
|
|
432
|
+
font-size: 10px;
|
|
433
|
+
color: var(--plan);
|
|
434
|
+
margin-top: 2px;
|
|
435
|
+
white-space: nowrap;
|
|
436
|
+
overflow: hidden;
|
|
437
|
+
text-overflow: ellipsis;
|
|
438
|
+
}
|
|
439
|
+
|
|
429
440
|
.session-progress {
|
|
430
441
|
display: flex;
|
|
431
442
|
align-items: center;
|
|
@@ -1274,6 +1285,26 @@
|
|
|
1274
1285
|
border-color: var(--team);
|
|
1275
1286
|
}
|
|
1276
1287
|
|
|
1288
|
+
.plan-indicator {
|
|
1289
|
+
width: 24px;
|
|
1290
|
+
height: 24px;
|
|
1291
|
+
display: inline-flex;
|
|
1292
|
+
align-items: center;
|
|
1293
|
+
justify-content: center;
|
|
1294
|
+
background: var(--plan-dim);
|
|
1295
|
+
border: 1px solid transparent;
|
|
1296
|
+
border-radius: 4px;
|
|
1297
|
+
color: var(--plan);
|
|
1298
|
+
cursor: pointer;
|
|
1299
|
+
flex-shrink: 0;
|
|
1300
|
+
transition: all 0.15s ease;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
.plan-indicator:hover {
|
|
1304
|
+
background: var(--plan-dim);
|
|
1305
|
+
border-color: var(--plan);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1277
1308
|
/* Task owner badge */
|
|
1278
1309
|
.task-owner-badge {
|
|
1279
1310
|
display: inline-flex;
|
|
@@ -1375,6 +1406,8 @@
|
|
|
1375
1406
|
--success-dim: rgba(26, 138, 90, 0.15);
|
|
1376
1407
|
--warning: #b07d0a;
|
|
1377
1408
|
--warning-dim: rgba(176, 125, 10, 0.15);
|
|
1409
|
+
--plan: #5a7a5a;
|
|
1410
|
+
--plan-dim: rgba(90, 122, 90, 0.15);
|
|
1378
1411
|
}
|
|
1379
1412
|
|
|
1380
1413
|
body.light::before {
|
|
@@ -1711,6 +1744,8 @@
|
|
|
1711
1744
|
--success-dim: rgba(26, 138, 90, 0.15);
|
|
1712
1745
|
--warning: #b07d0a;
|
|
1713
1746
|
--warning-dim: rgba(176, 125, 10, 0.15);
|
|
1747
|
+
--plan: #5a7a5a;
|
|
1748
|
+
--plan-dim: rgba(90, 122, 90, 0.15);
|
|
1714
1749
|
}
|
|
1715
1750
|
|
|
1716
1751
|
body:not(.dark-forced)::before {
|
|
@@ -2444,7 +2479,7 @@
|
|
|
2444
2479
|
|
|
2445
2480
|
let filteredSessions = sessions;
|
|
2446
2481
|
if (sessionFilter === 'active') {
|
|
2447
|
-
filteredSessions = filteredSessions.filter(s => s.pending > 0 || s.inProgress > 0);
|
|
2482
|
+
filteredSessions = filteredSessions.filter(s => s.pending > 0 || s.inProgress > 0 || s.hasPlan);
|
|
2448
2483
|
}
|
|
2449
2484
|
if (filterProject) {
|
|
2450
2485
|
filteredSessions = filteredSessions.filter(s => matchesProjectFilter(s.project));
|
|
@@ -2530,10 +2565,12 @@
|
|
|
2530
2565
|
<div class="session-name">${escapeHtml(primaryName)}</div>
|
|
2531
2566
|
${secondaryName ? `<div class="session-secondary">${escapeHtml(secondaryName)}</div>` : ''}
|
|
2532
2567
|
${gitBranch ? `<div class="session-branch">${gitBranch}</div>` : ''}
|
|
2568
|
+
${session.planTitle ? `<div class="session-plan">${escapeHtml(session.planTitle)}</div>` : ''}
|
|
2533
2569
|
<div class="session-progress">
|
|
2534
2570
|
<span class="session-indicators">
|
|
2535
2571
|
${isTeam ? `<span class="team-badge" title="${memberCount} team members"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>${memberCount}</span>` : ''}
|
|
2536
2572
|
${(isTeam || session.project) ? `<span class="team-info-btn" onclick="event.stopPropagation(); showSessionInfoModal('${session.id}')" title="View session info">ℹ</span>` : ''}
|
|
2573
|
+
${session.hasPlan ? `<span class="plan-indicator" onclick="event.stopPropagation(); openPlanForSession('${session.id}')" title="View plan"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg></span>` : ''}
|
|
2537
2574
|
${hasInProgress ? '<span class="pulse"></span>' : ''}
|
|
2538
2575
|
</span>
|
|
2539
2576
|
<div class="progress-bar"><div class="progress-fill" style="width: ${percent}%"></div></div>
|
package/server.js
CHANGED
|
@@ -206,7 +206,7 @@ function loadSessionMetadata() {
|
|
|
206
206
|
console.error('Error loading session metadata:', e);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
// For team sessions with no JSONL match,
|
|
209
|
+
// For team sessions with no JSONL match, resolve from team config + parent session
|
|
210
210
|
if (existsSync(TASKS_DIR)) {
|
|
211
211
|
const taskDirs = readdirSync(TASKS_DIR, { withFileTypes: true })
|
|
212
212
|
.filter(d => d.isDirectory());
|
|
@@ -214,11 +214,18 @@ function loadSessionMetadata() {
|
|
|
214
214
|
if (!metadata[dir.name]) {
|
|
215
215
|
const teamConfig = loadTeamConfig(dir.name);
|
|
216
216
|
if (teamConfig) {
|
|
217
|
+
const parentMeta = teamConfig.leadSessionId ? metadata[teamConfig.leadSessionId] : null;
|
|
218
|
+
const leadMember = teamConfig.members?.find(m => m.agentId === teamConfig.leadAgentId) || teamConfig.members?.[0];
|
|
219
|
+
const project = parentMeta?.project || leadMember?.cwd || teamConfig.working_dir || null;
|
|
220
|
+
|
|
217
221
|
metadata[dir.name] = {
|
|
218
|
-
customTitle: null,
|
|
219
|
-
slug: null,
|
|
220
|
-
project
|
|
221
|
-
jsonlPath: null
|
|
222
|
+
customTitle: parentMeta?.customTitle || null,
|
|
223
|
+
slug: parentMeta?.slug || null,
|
|
224
|
+
project,
|
|
225
|
+
jsonlPath: parentMeta?.jsonlPath || null,
|
|
226
|
+
description: parentMeta?.description || teamConfig.description || null,
|
|
227
|
+
gitBranch: parentMeta?.gitBranch || null,
|
|
228
|
+
created: parentMeta?.created || null
|
|
222
229
|
};
|
|
223
230
|
}
|
|
224
231
|
}
|
|
@@ -233,6 +240,19 @@ function loadSessionMetadata() {
|
|
|
233
240
|
/**
|
|
234
241
|
* Get display name for a session: customTitle > slug > null (frontend shows UUID)
|
|
235
242
|
*/
|
|
243
|
+
function getPlanInfo(slug) {
|
|
244
|
+
if (!slug) return { hasPlan: false, planTitle: null };
|
|
245
|
+
const planPath = path.join(PLANS_DIR, `${slug}.md`);
|
|
246
|
+
if (!existsSync(planPath)) return { hasPlan: false, planTitle: null };
|
|
247
|
+
try {
|
|
248
|
+
const head = readFileSync(planPath, 'utf8').slice(0, 512);
|
|
249
|
+
const match = head.match(/^#\s+(.+)$/m);
|
|
250
|
+
return { hasPlan: true, planTitle: match ? match[1].trim() : null };
|
|
251
|
+
} catch (e) {
|
|
252
|
+
return { hasPlan: true, planTitle: null };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
236
256
|
function getSessionDisplayName(sessionId, meta) {
|
|
237
257
|
if (meta?.customTitle) return meta.customTitle;
|
|
238
258
|
if (meta?.slug) return meta.slug;
|
|
@@ -296,6 +316,7 @@ app.get('/api/sessions', async (req, res) => {
|
|
|
296
316
|
|
|
297
317
|
const isTeam = isTeamSession(entry.name);
|
|
298
318
|
const memberCount = isTeam ? (loadTeamConfig(entry.name)?.members?.length || 0) : 0;
|
|
319
|
+
const planInfo = getPlanInfo(meta.slug);
|
|
299
320
|
|
|
300
321
|
sessionsMap.set(entry.name, {
|
|
301
322
|
id: entry.name,
|
|
@@ -311,7 +332,8 @@ app.get('/api/sessions', async (req, res) => {
|
|
|
311
332
|
createdAt: meta.created || null,
|
|
312
333
|
modifiedAt: modifiedAt,
|
|
313
334
|
isTeam,
|
|
314
|
-
memberCount
|
|
335
|
+
memberCount,
|
|
336
|
+
...planInfo
|
|
315
337
|
});
|
|
316
338
|
}
|
|
317
339
|
}
|
|
@@ -324,6 +346,7 @@ app.get('/api/sessions', async (req, res) => {
|
|
|
324
346
|
if (!modifiedAt && meta.jsonlPath) {
|
|
325
347
|
try { modifiedAt = statSync(meta.jsonlPath).mtime.toISOString(); } catch (e) {}
|
|
326
348
|
}
|
|
349
|
+
const planInfo = getPlanInfo(meta.slug);
|
|
327
350
|
sessionsMap.set(sessionId, {
|
|
328
351
|
id: sessionId,
|
|
329
352
|
name: getSessionDisplayName(sessionId, meta),
|
|
@@ -338,7 +361,8 @@ app.get('/api/sessions', async (req, res) => {
|
|
|
338
361
|
createdAt: meta.created || null,
|
|
339
362
|
modifiedAt: modifiedAt || new Date(0).toISOString(),
|
|
340
363
|
isTeam: false,
|
|
341
|
-
memberCount: 0
|
|
364
|
+
memberCount: 0,
|
|
365
|
+
...planInfo
|
|
342
366
|
});
|
|
343
367
|
}
|
|
344
368
|
}
|
|
@@ -672,6 +696,19 @@ projectsWatcher.on('all', (event, filePath) => {
|
|
|
672
696
|
}
|
|
673
697
|
});
|
|
674
698
|
|
|
699
|
+
const plansWatcher = chokidar.watch(PLANS_DIR, {
|
|
700
|
+
persistent: true,
|
|
701
|
+
ignoreInitial: true,
|
|
702
|
+
depth: 0
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
plansWatcher.on('all', (event, filePath) => {
|
|
706
|
+
if ((event === 'add' || event === 'change' || event === 'unlink') && filePath.endsWith('.md')) {
|
|
707
|
+
lastMetadataRefresh = 0;
|
|
708
|
+
broadcast({ type: 'metadata-update' });
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
|
|
675
712
|
app.use('/api', (req, res) => {
|
|
676
713
|
res.status(404).json({ error: 'Not found' });
|
|
677
714
|
});
|