claude-code-kanban 1.11.0 → 1.12.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025
3
+ Copyright (c) 2026
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,55 +1,27 @@
1
1
  # Claude Code Kanban
2
2
 
3
- A real-time Kanban board for **observing** Claude Code tasks. See what Claude is working on, track dependencies between tasks, and manage task cleanup and priority.
3
+ [![npm version](https://img.shields.io/npm/v/claude-code-kanban)](https://www.npmjs.com/package/claude-code-kanban)
4
+ [![license](https://img.shields.io/npm/l/claude-code-kanban)](LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/claude-code-kanban)](https://www.npmjs.com/package/claude-code-kanban)
4
6
 
5
- ![Dark mode](screenshot-dark-v2.png)
7
+ > Watch Claude Code think, in real time.
6
8
 
7
- ![Light mode](screenshot-light-v2.png)
9
+ ![Dark mode](assets/screenshot-dark-v2.png)
8
10
 
9
- ## Why Use This?
11
+ ![Light mode](assets/screenshot-light-v2.png)
10
12
 
11
- When Claude Code breaks down complex work into tasks, you get visibility into its thinking — but only in the terminal. Claude Code Kanban gives you a persistent, visual dashboard to:
13
+ ## Features
12
14
 
13
- - **See the big picture** — All your sessions and tasks in one place
14
- - **Know what's happening now** — Live Updates show exactly what Claude is doing across all sessions
15
- - **Understand task dependencies** — See which tasks are blocked and what's holding them up
16
- - **Clean up completed work** — Delete tasks when no longer needed (with dependency checking)
17
-
18
- ## Key Features
19
-
20
- ### Observation-Focused Design
21
- Claude Code controls task state — the viewer shows you what's happening:
22
- - **Real-time status** — See tasks move through Pending → In Progress → Completed as Claude works
23
- - **Active session detection** — Indicators show which sessions have in-progress tasks
24
- - **Task dependencies** — Visualise blockedBy/blocks relationships to understand the critical path
25
- - **Live activity feed** — Real-time stream of all in-progress tasks across every session
26
-
27
- ### Agent Teams Support
28
- - **Team detection** — Automatically detects team sessions with multiple agents
29
- - **Owner filtering** — Filter Kanban board by team member with color-coded agent indicators
30
- - **Member count badges** — See how many agents are working in each session
31
-
32
- ### Cleanup Operations
33
- - **Delete tasks** — Remove tasks with the delete button or press `D` (includes safety checks for dependencies)
34
- - **Bulk delete** — Delete all tasks in a session at once
35
-
36
- ### Session Management
37
- View and organize your Claude Code sessions:
38
- - **Session discovery** — Automatically finds all sessions in `~/.claude/tasks/` and `~/.claude/projects/`
39
- - **View project paths** — See the full filesystem path for each project
40
- - **Git branch display** — See which branch each session is working on
41
- - **Fuzzy search** — Search across session names, task descriptions, and project paths with instant filtering
42
- - **Session filters** — Filter by active/all sessions and by project
43
-
44
- ### Keyboard Shortcuts
45
- - `?` — Show help with all keyboard shortcuts
46
- - `D` — Delete the currently selected task (with confirmation and dependency checking)
47
- - `Esc` — Close detail panel or modals
15
+ - **Real-time updates** — Tasks move through Pending In Progress → Completed as Claude works
16
+ - **Agent teams** — Color-coded team members, owner filtering, member count badges
17
+ - **Task dependencies** — See blockedBy/blocks relationships and the critical path
18
+ - **Live activity feed** — Stream of all in-progress tasks across every session
19
+ - **Session management** — Fuzzy search, project/branch display, active session indicators
20
+ - **Cleanup** — Delete tasks (with dependency checks) or bulk-delete entire sessions
21
+ - **Keyboard shortcuts** — Press `?` for help
48
22
 
49
23
  ## Installation
50
24
 
51
- ### Quick start
52
-
53
25
  ```bash
54
26
  npx claude-code-kanban
55
27
  ```
@@ -63,78 +35,26 @@ npm install -g claude-code-kanban
63
35
  claude-code-kanban --open
64
36
  ```
65
37
 
66
- ### From source
67
-
68
- ```bash
69
- git clone https://github.com/NikiforovAll/claude-task-viewer.git
70
- cd claude-task-viewer
71
- npm install
72
- npm start
73
- ```
74
-
75
- ## How It Works
76
-
77
- Claude Code stores tasks in `~/.claude/tasks/`. Each session has its own folder:
78
-
79
- ```
80
- ~/.claude/tasks/
81
- └── {session-uuid}/
82
- ├── 1.json
83
- ├── 2.json
84
- └── ...
85
- ```
86
-
87
- The viewer watches this directory and pushes updates via Server-Sent Events. Changes appear instantly — no polling, no refresh needed.
88
-
89
- If port 3456 is already in use, the server automatically falls back to a random available port.
90
-
91
- ## Task Structure
92
-
93
- ```json
94
- {
95
- "id": "1",
96
- "subject": "Implement user authentication",
97
- "description": "Add JWT-based auth with refresh tokens",
98
- "activeForm": "Setting up auth middleware",
99
- "status": "in_progress",
100
- "blocks": ["2", "3"],
101
- "blockedBy": []
102
- }
103
- ```
104
-
105
- - `activeForm` — What Claude is doing right now (shown in Live Updates)
106
- - `blocks` / `blockedBy` — Task dependency relationships
107
-
108
38
  ## Configuration
109
39
 
110
40
  ```bash
111
- # Custom port
112
- PORT=8080 npx claude-code-kanban
113
-
114
- # Open browser automatically
115
- npx claude-code-kanban --open
116
-
117
- # Use a different Claude config directory (for multiple accounts)
118
- npx claude-code-kanban --dir=~/.claude-work
41
+ PORT=8080 npx claude-code-kanban # Custom port
42
+ npx claude-code-kanban --open # Auto-open browser
43
+ npx claude-code-kanban --dir=~/.claude-work # Custom Claude config dir
119
44
  ```
120
45
 
121
- ## API
46
+ If port 3456 is in use, the server falls back to a random available port.
122
47
 
123
- | Endpoint | Method | Description |
124
- |----------|--------|-------------|
125
- | `/api/sessions` | GET | List all sessions with task counts |
126
- | `/api/sessions/:id` | GET | Get all tasks for a session |
127
- | `/api/tasks/all` | GET | Get all tasks across all sessions |
128
- | `/api/tasks/:session/:task` | DELETE | Delete a task (checks dependencies) |
129
- | `/api/tasks/:session/:task/note` | POST | Add a note to a task |
130
- | `/api/teams/:name` | GET | Load team configuration |
131
- | `/api/events` | GET | SSE stream for live updates |
48
+ ## FAQ
132
49
 
133
- ## Design Philosophy
50
+ **Does this control Claude?**
51
+ No. Claude Code owns all task state. The viewer only observes — it never directs Claude's work.
134
52
 
135
- **Observation over Control**: Claude Code owns task state. The task viewer's job is to show you what Claude is doing, not to direct it. This keeps the viewer in sync with reality and prevents confusion about whether a task's status reflects what Claude is actually doing or just human intent.
53
+ **Does it work with agent teams?**
54
+ Yes. Team sessions are auto-detected with color-coded members and owner filtering.
136
55
 
137
- **Limited interaction:** You can delete tasks and add notes, but task status, subject, and description reflect Claude's actual work and can only be changed by Claude Code itself.
56
+ **Does it require Claude Code to be running?**
57
+ No. It reads task files from `~/.claude/tasks/`. You can view past sessions anytime.
138
58
 
139
59
  ## License
140
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-kanban",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
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/index.html CHANGED
@@ -65,15 +65,35 @@
65
65
 
66
66
  /* Sidebar */
67
67
  .sidebar {
68
- width: 300px;
68
+ width: var(--sidebar-width, 300px);
69
69
  background: var(--bg-surface);
70
70
  border-right: 1px solid var(--border);
71
71
  box-shadow: 1px 0 12px rgba(0, 0, 0, 0.04);
72
72
  display: flex;
73
73
  flex-direction: column;
74
74
  flex-shrink: 0;
75
+ position: relative;
76
+ overflow: hidden;
77
+ transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
75
78
  }
76
79
 
80
+ .sidebar.collapsed { width: 48px; }
81
+ .sidebar.collapsed .logo-text,
82
+ .sidebar.collapsed .connection,
83
+ .sidebar.collapsed .sidebar-section,
84
+ .sidebar.collapsed .sidebar-footer { display: none; }
85
+ .sidebar.collapsed .sidebar-header {
86
+ padding: 12px;
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ gap: 12px;
91
+ }
92
+ .sidebar.collapsed .sidebar-toggle-btn {
93
+ position: static;
94
+ }
95
+ .sidebar.resizing { transition: none; }
96
+
77
97
  .sidebar-header {
78
98
  padding: 20px 20px 16px;
79
99
  border-bottom: none;
@@ -81,8 +101,61 @@
81
101
  background-size: 100% 1px;
82
102
  background-repeat: no-repeat;
83
103
  background-position: bottom;
104
+ position: relative;
105
+ }
106
+
107
+ .sidebar-toggle-btn {
108
+ position: absolute;
109
+ top: 20px;
110
+ right: 8px;
111
+ width: 28px;
112
+ height: 28px;
113
+ display: flex;
114
+ align-items: center;
115
+ justify-content: center;
116
+ background: transparent;
117
+ border: 1px solid transparent;
118
+ border-radius: 6px;
119
+ color: var(--text-muted);
120
+ cursor: pointer;
121
+ transition: all 0.15s ease;
122
+ padding: 0;
123
+ }
124
+
125
+ .sidebar-toggle-btn:hover {
126
+ color: var(--text-secondary);
127
+ background: var(--bg-hover);
128
+ border-color: var(--border);
129
+ }
130
+
131
+ .sidebar-toggle-btn svg {
132
+ width: 16px;
133
+ height: 16px;
134
+ transition: transform 0.25s ease;
135
+ }
136
+
137
+ .sidebar.collapsed .sidebar-toggle-btn svg {
138
+ transform: rotate(180deg);
139
+ }
140
+
141
+ .sidebar-resize-handle {
142
+ position: absolute;
143
+ top: 0;
144
+ right: 0;
145
+ width: 4px;
146
+ height: 100%;
147
+ cursor: col-resize;
148
+ z-index: 10;
149
+ transition: background 0.15s;
150
+ }
151
+
152
+ .sidebar-resize-handle:hover,
153
+ .sidebar-resize-handle.dragging {
154
+ background: var(--accent-dim);
84
155
  }
85
156
 
157
+ .sidebar.collapsed .sidebar-resize-handle { display: none; }
158
+
86
159
  .logo {
87
160
  display: flex;
88
161
  align-items: center;
@@ -115,9 +188,9 @@
115
188
  .connection {
116
189
  display: flex;
117
190
  align-items: center;
118
- gap: 6px;
119
- margin-top: 12px;
120
- font-size: 12px;
191
+ gap: 5px;
192
+ margin-top: 10px;
193
+ font-size: 10px;
121
194
  color: var(--text-tertiary);
122
195
  text-transform: uppercase;
123
196
  letter-spacing: 0.05em;
@@ -387,6 +460,7 @@
387
460
 
388
461
  /* Footer */
389
462
  .sidebar-footer {
463
+ flex-shrink: 0;
390
464
  padding: 14px 20px;
391
465
  border-top: none;
392
466
  background-image: linear-gradient(to right, transparent, var(--border), transparent);
@@ -616,6 +690,7 @@
616
690
  display: flex;
617
691
  flex-direction: column;
618
692
  gap: 8px;
693
+ padding-right: 8px;
619
694
  }
620
695
 
621
696
  .column-empty {
@@ -979,6 +1054,17 @@
979
1054
  margin-bottom: 0;
980
1055
  }
981
1056
 
1057
+ .detail-desc ol,
1058
+ .detail-desc ul {
1059
+ padding-left: 1.5em;
1060
+ margin: 0 0 12px 0;
1061
+ }
1062
+
1063
+ .detail-desc ol:last-child,
1064
+ .detail-desc ul:last-child {
1065
+ margin-bottom: 0;
1066
+ }
1067
+
982
1068
  /* Note form */
983
1069
  .note-section {
984
1070
  margin-top: 24px;
@@ -1609,6 +1695,11 @@
1609
1695
  <span class="connection-dot"></span>
1610
1696
  <span>Connecting</span>
1611
1697
  </div>
1698
+ <button id="sidebar-toggle" class="sidebar-toggle-btn" onclick="toggleSidebar()" title="Toggle sidebar" aria-label="Toggle sidebar">
1699
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1700
+ <path d="M15 18l-6-6 6-6"/>
1701
+ </svg>
1702
+ </button>
1612
1703
  </header>
1613
1704
 
1614
1705
  <!-- Live Updates -->
@@ -1667,6 +1758,8 @@
1667
1758
  <div id="sessions-list" class="sessions-list"></div>
1668
1759
  </div>
1669
1760
 
1761
+ <div class="sidebar-footer" id="sidebar-footer"></div>
1762
+ <div class="sidebar-resize-handle" id="sidebar-resize"></div>
1670
1763
  </aside>
1671
1764
 
1672
1765
  <!-- Main -->
@@ -2119,8 +2212,8 @@
2119
2212
  <div class="live-item" onclick="openLiveTask('${task.sessionId}', '${task.id}')">
2120
2213
  <span class="pulse"></span>
2121
2214
  <div class="live-item-content">
2122
- <div class="live-item-action">${escapeHtml(task.activeForm || task.subject)}</div>
2123
- <div class="live-item-session">${escapeHtml(task.sessionName || task.sessionId.slice(0, 8))}</div>
2215
+ <div class="live-item-action" title="${escapeHtml(task.activeForm || task.subject)}">${escapeHtml(task.activeForm || task.subject)}</div>
2216
+ <div class="live-item-session" title="${escapeHtml(task.sessionName || task.sessionId)}">${escapeHtml(task.sessionName || task.sessionId)}</div>
2124
2217
  </div>
2125
2218
  </div>
2126
2219
  `).join('');
@@ -2554,6 +2647,10 @@
2554
2647
 
2555
2648
  focusZone = zone;
2556
2649
  if (zone === 'sidebar') {
2650
+ if (sidebar.classList.contains('collapsed')) {
2651
+ sidebar.classList.remove('collapsed');
2652
+ localStorage.setItem('sidebar-collapsed', false);
2653
+ }
2557
2654
  sidebar.classList.add('sidebar-focused');
2558
2655
  const items = getSessionItems();
2559
2656
  if (selectedSessionIdx < 0 && items.length > 0) {
@@ -2877,6 +2974,12 @@
2877
2974
 
2878
2975
  if (document.querySelector('.modal-overlay.visible')) return;
2879
2976
 
2977
+ if (e.key === '[') {
2978
+ e.preventDefault();
2979
+ toggleSidebar();
2980
+ return;
2981
+ }
2982
+
2880
2983
  // Tab toggles focus zone
2881
2984
  if (e.key === 'Tab') {
2882
2985
  e.preventDefault();
@@ -3130,6 +3233,62 @@
3130
3233
  updateThemeIcon();
3131
3234
  }
3132
3235
 
3236
+ function toggleSidebar() {
3237
+ const sidebar = document.querySelector('.sidebar');
3238
+ const collapsed = sidebar.classList.toggle('collapsed');
3239
+ localStorage.setItem('sidebar-collapsed', collapsed);
3240
+ if (collapsed) {
3241
+ sidebar.style.width = '';
3242
+ } else {
3243
+ const w = getComputedStyle(sidebar).getPropertyValue('--sidebar-width');
3244
+ if (w) sidebar.style.width = w;
3245
+ }
3246
+ }
3247
+
3248
+ function loadSidebarState() {
3249
+ const sidebar = document.querySelector('.sidebar');
3250
+ if (localStorage.getItem('sidebar-collapsed') === 'true') {
3251
+ sidebar.classList.add('collapsed');
3252
+ }
3253
+ const w = localStorage.getItem('sidebar-width');
3254
+ if (w) {
3255
+ sidebar.style.setProperty('--sidebar-width', w);
3256
+ }
3257
+ }
3258
+
3259
+ function initSidebarResize() {
3260
+ const sidebar = document.querySelector('.sidebar');
3261
+ const handle = document.getElementById('sidebar-resize');
3262
+ let startX, startWidth;
3263
+
3264
+ handle.addEventListener('mousedown', (e) => {
3265
+ if (sidebar.classList.contains('collapsed')) return;
3266
+ startX = e.clientX;
3267
+ startWidth = sidebar.offsetWidth;
3268
+ sidebar.classList.add('resizing');
3269
+ handle.classList.add('dragging');
3270
+ document.body.style.userSelect = 'none';
3271
+ document.addEventListener('mousemove', onMove);
3272
+ document.addEventListener('mouseup', onUp);
3273
+ e.preventDefault();
3274
+ });
3275
+
3276
+ function onMove(e) {
3277
+ const w = Math.min(600, Math.max(200, startWidth + e.clientX - startX));
3278
+ sidebar.style.setProperty('--sidebar-width', w + 'px');
3279
+ sidebar.style.width = w + 'px';
3280
+ }
3281
+
3282
+ function onUp() {
3283
+ sidebar.classList.remove('resizing');
3284
+ handle.classList.remove('dragging');
3285
+ document.body.style.userSelect = '';
3286
+ document.removeEventListener('mousemove', onMove);
3287
+ document.removeEventListener('mouseup', onUp);
3288
+ localStorage.setItem('sidebar-width', sidebar.style.getPropertyValue('--sidebar-width'));
3289
+ }
3290
+ }
3291
+
3133
3292
  function loadPreferences() {
3134
3293
  document.getElementById('session-filter').value = sessionFilter;
3135
3294
  document.getElementById('session-limit').value = sessionLimit;
@@ -3248,8 +3407,20 @@
3248
3407
  renderKanban();
3249
3408
  }
3250
3409
 
3410
+ // Sync sidebar header height with view header
3411
+ const sidebarHeader = document.querySelector('.sidebar-header');
3412
+ const viewHeader = document.querySelector('.view-header');
3413
+ new ResizeObserver(() => {
3414
+ sidebarHeader.style.height = viewHeader.offsetHeight + 'px';
3415
+ }).observe(viewHeader);
3416
+
3251
3417
  // Init
3252
3418
  loadTheme();
3419
+ loadSidebarState();
3420
+ initSidebarResize();
3421
+ fetch('/api/version').then(r => r.json()).then(d => {
3422
+ document.getElementById('sidebar-footer').textContent = 'v' + d.version;
3423
+ }).catch(() => {});
3253
3424
 
3254
3425
  const urlState = getUrlState();
3255
3426
  sessionFilter = urlState.filter || 'active';
@@ -3315,6 +3486,10 @@
3315
3486
  <td style="padding: 4px 0; color: var(--text-secondary);"><kbd style="background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-family: monospace;">Tab</kbd></td>
3316
3487
  <td style="padding: 4px 0; color: var(--text-primary);">Toggle sidebar / board focus</td>
3317
3488
  </tr>
3489
+ <tr>
3490
+ <td style="padding: 4px 0; color: var(--text-secondary);"><kbd style="background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-family: monospace;">[</kbd></td>
3491
+ <td style="padding: 4px 0; color: var(--text-primary);">Toggle sidebar collapse</td>
3492
+ </tr>
3318
3493
  </table>
3319
3494
  </div>
3320
3495
  <div>
package/server.js CHANGED
@@ -329,6 +329,11 @@ app.get('/api/teams/:name', (req, res) => {
329
329
  res.json(config);
330
330
  });
331
331
 
332
+ app.get('/api/version', (req, res) => {
333
+ const pkg = require('./package.json');
334
+ res.json({ version: pkg.version });
335
+ });
336
+
332
337
  // API: Get all tasks across all sessions
333
338
  app.get('/api/tasks/all', async (req, res) => {
334
339
  try {
@@ -516,6 +521,10 @@ projectsWatcher.on('all', (event, filePath) => {
516
521
  }
517
522
  });
518
523
 
524
+ app.use('/api', (req, res) => {
525
+ res.status(404).json({ error: 'Not found' });
526
+ });
527
+
519
528
  // Start server
520
529
  const server = app.listen(PORT, () => {
521
530
  const actualPort = server.address().port;