claude-code-kanban 2.2.0 → 2.2.1-rc.1

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/lib/parsers.js CHANGED
@@ -541,6 +541,7 @@ function buildAgentProgressMap(jsonlPath) {
541
541
  const tmToolIdRe = /"tool_use_id":"([^"]+)"/;
542
542
  const tmAgentIdRe = /agent_id: ([a-zA-Z0-9_@-]+)/;
543
543
  const nameByToolUseId = {};
544
+ const descByToolUseId = {};
544
545
  for (const line of content.split('\n')) {
545
546
  if (line.includes('"agent_progress"')) {
546
547
  const agentMatch = re.exec(line);
@@ -574,8 +575,24 @@ function buildAgentProgressMap(jsonlPath) {
574
575
  const blocks = obj.message?.content;
575
576
  if (Array.isArray(blocks)) {
576
577
  for (const b of blocks) {
577
- if (b.type === 'tool_use' && b.name === 'Agent' && b.id && b.input?.name) {
578
- nameByToolUseId[b.id] = b.input.name;
578
+ if (b.type === 'tool_use' && b.name === 'Agent' && b.id) {
579
+ if (b.input?.name) nameByToolUseId[b.id] = b.input.name;
580
+ if (b.input?.description) descByToolUseId[b.id] = b.input.description;
581
+ }
582
+ }
583
+ }
584
+ } catch (_) {}
585
+ } else if (line.includes('"toolUseResult"') && line.includes('"agentId"') && line.includes('"tool_result"')) {
586
+ try {
587
+ const obj = JSON.parse(line);
588
+ const tur = obj.toolUseResult;
589
+ if (tur?.agentId) {
590
+ const blocks = obj.message?.content;
591
+ if (Array.isArray(blocks)) {
592
+ for (const b of blocks) {
593
+ if (b.type === 'tool_result' && b.tool_use_id && !map[b.tool_use_id]) {
594
+ map[b.tool_use_id] = { agentId: tur.agentId, prompt: tur.prompt || null };
595
+ }
579
596
  }
580
597
  }
581
598
  }
@@ -584,6 +601,7 @@ function buildAgentProgressMap(jsonlPath) {
584
601
  }
585
602
  for (const [key, entry] of Object.entries(map)) {
586
603
  if (nameByToolUseId[key]) entry.name = nameByToolUseId[key];
604
+ if (descByToolUseId[key]) entry.description = descByToolUseId[key];
587
605
  }
588
606
  } catch (_) {}
589
607
  return map;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-kanban",
3
- "version": "2.2.0",
3
+ "version": "2.2.1-rc.1",
4
4
  "description": "A web-based Kanban board for viewing Claude Code tasks with agent teams support",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/app.js CHANGED
@@ -627,7 +627,10 @@ async function viewAgentLog(agentId) {
627
627
  await fetchAgents(currentSessionId);
628
628
  agent = findAgentById(agentId);
629
629
  }
630
- if (!agent) return;
630
+ if (!agent) {
631
+ if (!currentSessionId) return;
632
+ agent = { agentId: agentId, type: 'Agent', _sourceSessionId: currentSessionId };
633
+ }
631
634
  const resolvedId = agent.agentId;
632
635
  const shortId = resolvedId.length > 8 ? resolvedId.slice(0, 8) : resolvedId;
633
636
  const agentSessionId = agent._sourceSessionId || currentSessionId;
@@ -652,6 +655,7 @@ async function viewAgentLog(agentId) {
652
655
  const data = JSON.parse(e.data);
653
656
  currentMessages = data.messages;
654
657
  if (messagePanelOpen) renderMessages(data.messages);
658
+ maybeFollowLatest();
655
659
  } catch (_) {}
656
660
  });
657
661
  agentLogSSE.onerror = () => {};
@@ -684,6 +688,7 @@ async function fetchAgentMessages() {
684
688
  if (!agentLogMode || agentLogMode.agentId !== agentId) return;
685
689
  currentMessages = data.messages;
686
690
  if (messagePanelOpen) renderMessages(data.messages);
691
+ maybeFollowLatest();
687
692
  } catch (e) {
688
693
  console.error('[fetchAgentMessages]', e);
689
694
  }
@@ -737,9 +742,7 @@ async function fetchMessages(sessionId) {
737
742
  }
738
743
  }
739
744
 
740
- if (msgDetailFollowLatest && currentMessages.length) {
741
- showMsgDetail(currentMessages.length - 1);
742
- }
745
+ maybeFollowLatest();
743
746
  } catch (e) {
744
747
  console.error('[fetchMessages]', e);
745
748
  }
@@ -1828,10 +1831,12 @@ function renderAgentFooter() {
1828
1831
  : a.status === 'idle'
1829
1832
  ? `idle · ${formatDuration(elapsed)}`
1830
1833
  : `active · ${formatDuration(elapsed)}`;
1834
+ const descText = a.description || '';
1831
1835
  const promptTrimmed = stripAnsi(stripTeammateWrapper((a.prompt || '').trim())).replace(/[\r\n]+/g, ' ');
1832
- const promptTrunc = promptTrimmed.length > 60 ? `${promptTrimmed.substring(0, 60)}…` : promptTrimmed;
1833
- const msgHtml = promptTrunc
1834
- ? `<div class="agent-message" title="${escapeHtml(promptTrimmed)}">${escapeHtml(promptTrunc)}</div>`
1836
+ const displayText = descText || promptTrimmed;
1837
+ const displayTrunc = displayText.length > 60 ? `${displayText.substring(0, 60)}…` : displayText;
1838
+ const msgHtml = displayTrunc
1839
+ ? `<div class="agent-message" title="${escapeHtml(displayText)}">${escapeHtml(displayTrunc)}</div>`
1835
1840
  : '';
1836
1841
  const rawType = a.type || 'unknown';
1837
1842
  const colonIdx = rawType.indexOf(':');
@@ -2770,7 +2775,7 @@ function navigateSession(direction, items) {
2770
2775
  }
2771
2776
  const currentEl = items[selectedSessionIdx];
2772
2777
  let newIdx = selectedSessionIdx + direction;
2773
- if (!currentEl || !currentEl.isConnected) {
2778
+ if (!currentEl?.isConnected) {
2774
2779
  const restoredIdx = selectedSessionKbId ? items.findIndex((el) => getKbId(el) === selectedSessionKbId) : -1;
2775
2780
  newIdx = restoredIdx >= 0 ? restoredIdx : 0;
2776
2781
  }
@@ -4081,7 +4086,12 @@ function setupEventSource() {
4081
4086
  }
4082
4087
 
4083
4088
  if (data.type === 'team-update') {
4084
- debouncedRefresh(data.teamName, false);
4089
+ const teamSession = sessions.find((s) => s.isTeam && s.teamName === data.teamName);
4090
+ if (teamSession) {
4091
+ debouncedRefresh(teamSession.id, false);
4092
+ } else if (currentSessionId) {
4093
+ debouncedRefresh(currentSessionId, false);
4094
+ }
4085
4095
  }
4086
4096
  };
4087
4097
  }
@@ -4238,6 +4248,12 @@ function renderContextDetail(raw) {
4238
4248
  //#endregion
4239
4249
 
4240
4250
  //#region UTILS
4251
+ function maybeFollowLatest() {
4252
+ if (msgDetailFollowLatest && currentMessages.length) {
4253
+ showMsgDetail(currentMessages.length - 1);
4254
+ }
4255
+ }
4256
+
4241
4257
  function isSessionActive(s) {
4242
4258
  return s.hasRecentLog || s.inProgress > 0 || s.hasActiveAgents || s.hasWaitingForUser;
4243
4259
  }
@@ -4999,7 +5015,7 @@ function updateOwnerFilter() {
4999
5015
  const select = document.getElementById('owner-filter');
5000
5016
 
5001
5017
  const session = sessions.find((s) => s.id === currentSessionId);
5002
- if (!session || !session.isTeam) {
5018
+ if (!session?.isTeam) {
5003
5019
  bar.classList.remove('visible');
5004
5020
  return;
5005
5021
  }
package/server.js CHANGED
@@ -1016,14 +1016,17 @@ app.get('/api/sessions/:sessionId/agents', (req, res) => {
1016
1016
 
1017
1017
  const agentsNeedingPrompt = agents.filter(a => !a.prompt);
1018
1018
  const agentsNeedingName = agents.filter(a => !a.agentName);
1019
- if ((agentsNeedingPrompt.length || agentsNeedingName.length) && meta.jsonlPath) {
1019
+ const agentsNeedingDesc = agents.filter(a => !a.description);
1020
+ if ((agentsNeedingPrompt.length || agentsNeedingName.length || agentsNeedingDesc.length) && meta.jsonlPath) {
1020
1021
  let byAgentId = {};
1021
1022
  let nameByAgentId = {};
1023
+ let descByAgentId = {};
1022
1024
  try {
1023
1025
  const progressMap = getProgressMap(meta.jsonlPath);
1024
1026
  for (const entry of Object.values(progressMap)) {
1025
1027
  if (entry.prompt && !byAgentId[entry.agentId]) byAgentId[entry.agentId] = entry.prompt;
1026
1028
  if (entry.name && !nameByAgentId[entry.agentId]) nameByAgentId[entry.agentId] = entry.name;
1029
+ if (entry.description && !descByAgentId[entry.agentId]) descByAgentId[entry.agentId] = entry.description;
1027
1030
  }
1028
1031
  } catch (_) {}
1029
1032
  for (const agent of agentsNeedingPrompt) {
@@ -1034,6 +1037,9 @@ app.get('/api/sessions/:sessionId/agents', (req, res) => {
1034
1037
  for (const agent of agentsNeedingName) {
1035
1038
  if (nameByAgentId[agent.agentId]) agent.agentName = nameByAgentId[agent.agentId];
1036
1039
  }
1040
+ for (const agent of agentsNeedingDesc) {
1041
+ if (descByAgentId[agent.agentId]) agent.description = descByAgentId[agent.agentId];
1042
+ }
1037
1043
  }
1038
1044
 
1039
1045
  const agentsNeedingModel = agents.filter(a => !a.model);
@@ -1199,6 +1205,7 @@ app.get('/api/sessions/:sessionId/messages', (req, res) => {
1199
1205
  const entry = progressMap[msg.toolUseId];
1200
1206
  if (entry) {
1201
1207
  msg.agentId = entry.agentId;
1208
+ if (entry.description) msg.agentDescription = entry.description;
1202
1209
  if (entry.prompt && !msg.agentPrompt) msg.agentPrompt = entry.prompt;
1203
1210
  try {
1204
1211
  const agentFile = path.join(agentDir, entry.agentId + '.json');
@@ -1445,9 +1452,16 @@ watcher.on('all', (event, filePath) => {
1445
1452
  function broadcastToMappedSessions(taskListName, event, filePath) {
1446
1453
  const { listToSessions } = loadAllTaskMaps();
1447
1454
  const map = listToSessions[taskListName];
1448
- if (!map) return;
1449
- for (const sid of Object.keys(map)) {
1450
- broadcast({ type: 'update', event, sessionId: sid, file: path.basename(filePath) });
1455
+ if (map) {
1456
+ for (const sid of Object.keys(map)) {
1457
+ broadcast({ type: 'update', event, sessionId: sid, file: path.basename(filePath) });
1458
+ }
1459
+ return;
1460
+ }
1461
+ // Fallback: check if taskListName is a team name
1462
+ const cfg = loadTeamConfig(taskListName);
1463
+ if (cfg?.leadSessionId) {
1464
+ broadcast({ type: 'update', event, sessionId: cfg.leadSessionId, file: path.basename(filePath) });
1451
1465
  }
1452
1466
  }
1453
1467