cc-sidebar 0.1.7 → 0.1.8

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/README.md CHANGED
@@ -90,11 +90,37 @@ Data is stored in `~/.claude-sidebar/`:
90
90
 
91
91
  ## Claude Code Setup
92
92
 
93
- Two optional integrations to make Claude aware of the sidebar:
93
+ Three optional integrations to make Claude aware of the sidebar:
94
94
 
95
- ### 1. Auto-completion (Recommended)
95
+ ### 1. TodoWrite Sync Hook (Recommended)
96
96
 
97
- Add this to your `~/.claude/CLAUDE.md` so Claude automatically marks sidebar tasks as done:
97
+ This hook syncs Claude's TodoWrite output to the sidebar, so you can see Claude's progress in real-time.
98
+
99
+ Add to your `~/.claude/settings.json`:
100
+
101
+ ```json
102
+ {
103
+ "hooks": {
104
+ "PostToolUse": [
105
+ {
106
+ "matcher": "TodoWrite",
107
+ "hooks": [
108
+ {
109
+ "type": "command",
110
+ "command": "bun ~/.bun/install/global/node_modules/cc-sidebar/src/sync-todos.ts"
111
+ }
112
+ ]
113
+ }
114
+ ]
115
+ }
116
+ }
117
+ ```
118
+
119
+ If you already have other hooks, merge the `PostToolUse` array with your existing hooks.
120
+
121
+ ### 2. Auto-completion (Optional)
122
+
123
+ Add this to your `~/.claude/CLAUDE.md` so Claude automatically marks sidebar tasks as done when it finishes work:
98
124
 
99
125
  ```markdown
100
126
  ## Sidebar Integration
@@ -122,7 +148,7 @@ Keep it simple - if no clear match, don't move anything. User can manually mark
122
148
  ```
123
149
  ```
124
150
 
125
- ### 2. Install Skills (Recommended)
151
+ ### 3. Install Skills
126
152
 
127
153
  cc-sidebar includes skills that integrate with Claude Code:
128
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-sidebar",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Visual sidebar for managing todos, tasks, and context alongside Claude Code",
5
5
  "author": "Tyler Nishida",
6
6
  "license": "MIT",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: clarify
3
3
  description: |
4
- Clarify tasks through conversation - works for new ideas or existing sidebar tasks.
4
+ Clarify tasks through deep conversation - works for new ideas or existing sidebar tasks.
5
5
 
6
6
  Use when: (1) You have thoughts/ideas that need clarification before becoming actionable,
7
7
  (2) You want to clarify a single task through conversation,
@@ -9,12 +9,12 @@ description: |
9
9
 
10
10
  Triggers: "/clarify", "clarify this", "let me explain this task".
11
11
 
12
- Requires: Claude Code sidebar for todo integration, Atomic Plans for plan output.
12
+ Requires: Claude Code sidebar for todo integration.
13
13
  ---
14
14
 
15
15
  # Clarify Skill
16
16
 
17
- Clarify tasks through structured conversation. Works for both new ideas (creates todos) and existing sidebar tasks (updates them).
17
+ Clarify tasks through structured, in-depth conversation. Creates a **spec for each todo** (not a shared plan).
18
18
 
19
19
  ## Invocation
20
20
 
@@ -24,111 +24,65 @@ Clarify tasks through structured conversation. Works for both new ideas (creates
24
24
 
25
25
  ## Process
26
26
 
27
- Follow these steps in order:
28
-
29
27
  ### 1. Detect Mode
30
28
 
31
29
  Check if invoked with `--task-id`:
32
30
  - **With task ID**: Updating an existing sidebar task
33
31
  - **Without task ID**: Creating new todo(s) from scratch
34
32
 
35
- ### 2. Locate Configuration
36
-
37
- Check the project's CLAUDE.md for the Atomic Plans folder path. Look for a section like:
38
-
39
- ```markdown
40
- ## Clarify Configuration
41
- Plan folder: /path/to/plans
42
- ```
43
-
44
- Or check if Atomic Plans is already configured:
45
-
46
- ```markdown
47
- ## Project Planning
48
- /atomic-plans /path/to/folder
49
- ```
50
-
51
- **If no path is found:**
52
- 1. Ask the user: "Where should I save plans for this project?"
53
- 2. After they provide a path, ask: "Would you like me to add this to CLAUDE.md so I remember next time?"
54
- 3. If yes, append the configuration to CLAUDE.md
55
-
56
- ### 3. Interview Phase
33
+ ### 2. Interview Phase
57
34
 
58
- The goal is to fully understand the task(s) through conversation.
35
+ **Interview me in detail using AskUserQuestion about literally anything:**
36
+ - Technical implementation details
37
+ - UI & UX considerations
38
+ - Concerns and risks
39
+ - Tradeoffs between approaches
40
+ - Edge cases and error handling
41
+ - Dependencies and blockers
42
+ - Success criteria
59
43
 
60
- **IMPORTANT: Never short-circuit the process.** Whether the user provides one clear task or a chaotic mess of ideas, always interview to clarify. A "single clear task" still benefits from clarification - edge cases, implementation approach, constraints, etc.
44
+ **CRITICAL RULES:**
45
+ - Make sure questions are NOT obvious - don't ask things that are clear from context
46
+ - Be very in-depth - continue interviewing continually until complete
47
+ - Multiple rounds of questions are expected and encouraged
48
+ - Always end with "Anything else I should know?" as a final question
61
49
 
62
- **If the user provided text with the command** (`/clarify [text]`):
63
- - Skip the opening question - they already told you what's on their mind
64
- - Go straight to AskUserQuestion to clarify specifics:
65
- - Ambiguous parts that need clarification
66
- - Implementation approach (if relevant)
67
- - Edge cases or constraints
68
- - Missing context: priorities, dependencies
69
- - Anything else you need to fully understand the task(s)
50
+ **If the user provided text** (`/clarify [text]`):
51
+ - Skip opening invitation - go straight to clarifying questions
70
52
 
71
53
  **If no text was provided** (`/clarify` alone):
72
- - Start with a plain text invitation (NOT AskUserQuestion):
73
-
74
- "What's on your mind? Tell me everything - tasks, concerns, ideas,
75
- whatever's floating around. I'll help clarify and organize it."
76
-
77
- - Let the user ramble freely in their response
78
- - THEN use AskUserQuestion to clarify specifics about what they said
79
-
80
- **Interview guidelines:**
81
- - Don't ask obvious questions - if something is clear from the task description, skip it
82
- - Be thorough - keep interviewing until you have complete clarity
83
- - Always include "Anything else I should know?" as a final question
54
+ - Start with: "What's on your mind? Tell me everything - tasks, concerns, ideas, whatever's floating around."
55
+ - Let them ramble, then ask clarifying questions
84
56
 
85
- **Key questions to consider** (use AskUserQuestion for these):
86
- - What's the most important thing here?
87
- - Are there dependencies between these tasks?
88
- - What constraints should I know about?
89
- - Is there anything blocking progress on any of these?
57
+ ### 3. Write Spec for Each Todo
90
58
 
91
- **Scope filtering:**
92
- If the user mentions things unrelated to the current project (personal tasks, other projects), ask:
93
- "Some of this seems unrelated to [current project]. Should I include those tasks anyway, or focus only on [project]-related work?"
59
+ After interviewing, create a **spec for each todo**. The spec is stored directly on the task (in the `spec` field), not in a separate plan file.
94
60
 
95
- ### 4. Create Plan
96
-
97
- Create a new plan in the Atomic Plans folder with:
98
-
99
- **Filename:** `NNN-clarify-[topic].md` where NNN is the next number in sequence.
100
-
101
- **Content:**
102
- ```markdown
103
- ---
104
- plan: NNN
105
- title: [Topic]
106
- state: active
107
- created: YYYY-MM-DD
108
- updated: YYYY-MM-DD
109
- ---
61
+ **Spec format:**
62
+ ```
63
+ ## [Task Title]
110
64
 
111
- # [Topic]
65
+ ### What
66
+ [Clear description of what needs to be done]
112
67
 
113
- ## Intent
114
- [Summary of what the user wants to accomplish]
68
+ ### Why
69
+ [The purpose/motivation]
115
70
 
116
- ## Specs
117
- [Detailed specifications gathered during interview]
71
+ ### How
72
+ [Implementation approach based on interview]
118
73
 
119
- ## Context
120
- [Key decisions, constraints, and context gathered during interview]
74
+ ### Acceptance Criteria
75
+ - [ ] Criterion 1
76
+ - [ ] Criterion 2
121
77
 
122
- ## Next
123
- - [ ] Execute the task(s)
78
+ ### Notes
79
+ [Any constraints, risks, or considerations from interview]
124
80
  ```
125
81
 
126
- ### 5. Update or Create Todos
82
+ ### 4. Update or Create Todos
127
83
 
128
84
  **If updating existing task** (has `--task-id`):
129
85
 
130
- Update the existing task in the sidebar:
131
-
132
86
  ```bash
133
87
  node << 'SCRIPT'
134
88
  const fs = require('fs');
@@ -141,30 +95,28 @@ let tasks = JSON.parse(fs.readFileSync(tasksPath, 'utf-8'));
141
95
  const task = tasks.find(t => t.id === 'TASK_ID_HERE'); // REPLACE with actual task ID
142
96
  if (task) {
143
97
  task.clarified = true;
144
- task.planPath = 'PLAN_FILENAME.md'; // REPLACE with actual plan filename
98
+ task.section = 'clarified';
99
+ task.spec = `SPEC_CONTENT_HERE`; // REPLACE with actual spec
145
100
  fs.writeFileSync(tasksPath, JSON.stringify(tasks, null, 2));
146
- console.log('Task clarified and linked to plan');
101
+ console.log('Task clarified with spec');
147
102
  }
148
103
  SCRIPT
149
104
  ```
150
105
 
151
106
  **If creating new todos** (no `--task-id`):
152
107
 
153
- Extract discrete, actionable tasks from the interview. Each todo should be:
108
+ Extract discrete, actionable tasks. Each todo should be:
154
109
  - **Specific**: Clear what needs to be done
155
110
  - **Actionable**: Can be worked on independently
156
- - **Scoped**: Relevant to the current project (unless user said otherwise)
157
-
158
- **Act as a staff engineer to prioritize:**
159
- Assign each task a numeric priority (1 = most important). Consider:
160
- - **Dependencies**: Blockers come first
161
- - **Urgency**: Bugs, deadlines, user-facing issues
162
- - **Impact**: High-value features over nice-to-haves
163
- - **Effort**: Quick wins can be prioritized to build momentum
111
+ - **Has a spec**: Detailed specification from interview
164
112
 
165
- Mark the top 3 most important tasks as "recommended" (shown with star in sidebar).
113
+ **Prioritization (act as staff engineer):**
114
+ - Dependencies first (blockers)
115
+ - Urgency (bugs, deadlines)
116
+ - Impact (high-value over nice-to-have)
117
+ - Quick wins for momentum
166
118
 
167
- Write todos to the **project-specific** tasks file:
119
+ Mark top 3 as "recommended".
168
120
 
169
121
  ```bash
170
122
  node << 'SCRIPT'
@@ -177,144 +129,119 @@ const hash = crypto.createHash('sha256').update(process.cwd()).digest('hex').sli
177
129
  const projectDir = path.join(sidebarDir, 'projects', hash);
178
130
  const tasksPath = path.join(projectDir, 'tasks.json');
179
131
 
180
- // Ensure directory exists
181
132
  fs.mkdirSync(projectDir, { recursive: true });
182
133
 
183
- // Read existing todos
184
134
  let existing = [];
185
135
  try { existing = JSON.parse(fs.readFileSync(tasksPath, 'utf-8')); } catch {}
186
136
 
187
- // Plan filename (just the filename, not full path)
188
- const planPath = "NNN-clarify-topic.md"; // REPLACE with actual plan filename
189
-
190
- // New todos - REPLACE THIS ARRAY with extracted tasks
191
- // Each task needs: content, priority (1=highest), recommended (true for top 3)
137
+ // REPLACE with extracted tasks - each has its own spec
192
138
  const newTodos = [
193
- { content: "Task 1 here", priority: 1, recommended: true },
194
- { content: "Task 2 here", priority: 2, recommended: true },
195
- { content: "Task 3 here", priority: 3, recommended: true },
196
- { content: "Task 4 here", priority: 4, recommended: false }
139
+ {
140
+ content: "Task 1 here",
141
+ priority: 1,
142
+ recommended: true,
143
+ spec: `## Task 1\n\n### What\n...\n\n### Why\n...\n\n### How\n...\n\n### Acceptance Criteria\n- [ ] ...`
144
+ },
145
+ // ... more tasks
197
146
  ].map(task => ({
198
147
  id: crypto.randomUUID(),
199
148
  content: task.content,
200
149
  createdAt: new Date().toISOString(),
201
- clarified: true, // Clarify IS the clarification process
202
- priority: task.priority, // Numeric priority (1 = most important)
203
- recommended: task.recommended, // Top 3 get star indicator
204
- planPath: planPath // Links task to the plan file
150
+ section: 'clarified',
151
+ clarified: true,
152
+ priority: task.priority,
153
+ recommended: task.recommended,
154
+ spec: task.spec
205
155
  }));
206
156
 
207
- // Append and write
208
157
  const combined = [...existing, ...newTodos];
209
158
  fs.writeFileSync(tasksPath, JSON.stringify(combined, null, 2));
210
- console.log('Added ' + newTodos.length + ' todos to sidebar');
159
+ console.log('Added ' + newTodos.length + ' clarified todos to sidebar');
211
160
  SCRIPT
212
161
  ```
213
162
 
214
- ### 6. Present and Prompt
215
-
216
- After creating/updating todos and plan:
217
-
218
- 1. Show a summary of what was created/updated
219
- 2. Ask: **"Execute this task now, or save for later?"**
220
- - If execute → work on the task immediately
221
- - If save → confirm the task is clarified and stop
163
+ ### 5. Present and Prompt
222
164
 
223
- **Example for existing task:**
224
- ```
225
- Clarified your task and created plan 007-auth-refactor.md
165
+ After creating/updating todos:
226
166
 
227
- Task: Refactor authentication module
228
- Specs: JWT-based, 24hr expiry, refresh tokens...
167
+ 1. Show summary of what was clarified
168
+ 2. Show the spec for each task
169
+ 3. Ask: **"Execute this task now, or save for later?"**
170
+ - Execute → start working immediately
171
+ - Save → confirm clarified and stop
229
172
 
230
- Execute this task now, or save for later?
173
+ **Example output:**
231
174
  ```
175
+ Clarified 2 todos:
232
176
 
233
- **Example for new tasks:**
234
- ```
235
- Created 3 todos in your sidebar and plan 008-api-improvements.md
177
+ 1. Implement user authentication
178
+ Spec: JWT-based auth with 24hr expiry, refresh tokens...
236
179
 
237
- Your todos:
238
- 1. Add rate limiting to API endpoints
239
- 2. Implement request validation
240
- 3. Add API documentation
180
+ 2. ★ Add rate limiting to API
181
+ Spec: 100 req/min per user, 429 response, Redis backend...
241
182
 
242
183
  Execute the first task now, or save for later?
243
184
  ```
244
185
 
245
- ## Relationship to Other Features
246
-
247
- | Feature | Purpose | When to Use |
248
- |---------|---------|-------------|
249
- | `/clarify` | Interview and clarify task(s) | When you need to think through a task before doing it |
250
- | Sidebar `c` key | Invokes `/clarify --task-id` | Quick way to clarify a queued task |
251
- | `/prioritize` | Re-evaluate and reorder all tasks | When context changes or you want fresh prioritization |
252
- | Ralph-loop | Batch process todos autonomously | After clarifying, to work through all todos |
253
-
254
- ## Example Sessions
255
-
256
- ### From Sidebar (existing task)
257
- ```
258
- [User presses 'c' on task "Add user authentication"]
259
-
260
- Sidebar sends: /clarify --task-id abc123 Add user authentication
261
-
262
- Claude: [Uses AskUserQuestion]
263
- - What auth method? (JWT, session, OAuth?)
264
- - Any specific security requirements?
265
- - Which routes need protection?
186
+ ## Relationship to Other Skills
266
187
 
267
- User: [Answers]
188
+ | Skill | Purpose |
189
+ |-------|---------|
190
+ | `/clarify` | Interview and write spec for task(s) |
191
+ | `/prioritize` | Re-evaluate all tasks as staff engineer |
192
+ | Atomic Plans | Track execution progress when working on task |
268
193
 
269
- Claude: [Creates plan, updates task with clarified=true]
194
+ **Flow:**
195
+ 1. **Clarify** creates specs (the "what")
196
+ 2. **Prioritize** decides what's next
197
+ 3. **Atomic Plans** tracks execution (the "how/progress")
270
198
 
271
- Clarified your task and created plan 005-user-auth.md
199
+ ## Example Session
272
200
 
273
- Execute this task now, or save for later?
274
201
  ```
202
+ User: /clarify I need to add dark mode support
275
203
 
276
- ### From Chat (new task)
277
- ```
278
- User: /clarify I need to add shift+enter support for multiline input
204
+ Claude: [AskUserQuestion]
205
+ - Should this affect the entire app or just certain components?
206
+ - Do you want system preference detection (prefers-color-scheme)?
207
+ - Are there specific colors/themes you want, or should I design them?
208
+ - Should the preference persist across sessions (localStorage)?
279
209
 
280
- Claude: [Uses AskUserQuestion]
281
- - Which input modes need this? (adding new todos, editing existing, both?)
282
- - Should Enter still submit, with Shift+Enter for newlines?
210
+ User: [Answers each question]
283
211
 
284
- User: [Answers]
285
-
286
- Claude: [Creates todo, writes to sidebar, creates plan]
287
-
288
- Created 1 todo in your sidebar and plan 009-multiline-input.md
289
-
290
- Execute this task now, or save for later?
291
- ```
212
+ Claude: [AskUserQuestion - follow-up]
213
+ - For the color palette, do you have brand colors to work with?
214
+ - Should transitions be animated when switching themes?
215
+ - Any components that should NOT support dark mode?
216
+ - Anything else I should know?
292
217
 
293
- ### From Chat (multiple tasks)
294
- ```
295
- User: /clarify
218
+ User: [Answers]
296
219
 
297
- Claude: What's on your mind? Tell me everything.
220
+ Claude: [Creates todo with spec]
298
221
 
299
- User: ugh so much. the auth is a mess, we're using like 3 different
300
- patterns. also need to update the docs. oh and there's that bug with
301
- the session timeout.
222
+ Clarified 1 todo:
302
223
 
303
- Claude: [Uses AskUserQuestion]
304
- - Which auth pattern should we standardize on?
305
- - Is the session timeout bug blocking anything?
224
+ Add dark mode support
225
+ Spec:
226
+ ## Dark Mode Support
306
227
 
307
- User: [Answers]
228
+ ### What
229
+ Add system-wide dark mode toggle with OS preference detection
308
230
 
309
- Claude: [Creates todos, writes to sidebar, creates plan]
231
+ ### Why
232
+ User requested for better nighttime usability
310
233
 
311
- Created 4 todos in your sidebar and plan 010-auth-cleanup.md
234
+ ### How
235
+ - CSS custom properties for colors
236
+ - React context for theme state
237
+ - localStorage for persistence
238
+ - prefers-color-scheme media query for initial
312
239
 
313
- Your todos:
314
- 1. Standardize auth on JWT pattern
315
- 2. Fix session timeout bug
316
- 3. Update auth documentation
317
- 4. Remove deprecated OAuth1 code
240
+ ### Acceptance Criteria
241
+ - [ ] Toggle switches between light/dark
242
+ - [ ] Respects OS preference on first visit
243
+ - [ ] Persists choice in localStorage
244
+ - [ ] Smooth transition animation
318
245
 
319
- Execute the first task now, or save for later?
246
+ Execute this task now, or save for later?
320
247
  ```
@@ -51,6 +51,8 @@ As a staff engineer, evaluate each task considering:
51
51
  - **Impact**: High-value features over nice-to-haves.
52
52
  - **Effort**: Quick wins can build momentum; large tasks may need breakdown.
53
53
  - **Context**: If user provided context, weight it heavily.
54
+ - **Specs**: Read the `spec` field of clarified tasks for detailed requirements.
55
+ - **Section**: Clarified tasks (with specs) are generally ready to execute.
54
56
 
55
57
  Assign each task:
56
58
  - `priority`: Numeric (1 = most important, higher = less important)
@@ -124,9 +126,18 @@ Other tasks:
124
126
  Tasks have been re-prioritized in your sidebar.
125
127
  ```
126
128
 
129
+ ## Skill Flow
130
+
131
+ This skill is part of a chain:
132
+
133
+ 1. **Clarify** → Creates specs for each task (the "what")
134
+ 2. **Prioritize** → Staff engineer decides what's next (this skill)
135
+ 3. **Atomic Plans** → Track execution progress when working
136
+
127
137
  ## Notes
128
138
 
129
139
  - This skill reads and modifies the sidebar's tasks.json directly
130
- - It preserves all existing task fields (id, content, clarified, planPath, etc.)
140
+ - It preserves all existing task fields (id, content, clarified, spec, etc.)
131
141
  - Only updates `priority` and `recommended` fields
132
142
  - Can be run anytime - safe to run multiple times
143
+ - Read specs to understand task complexity when prioritizing
@@ -13,6 +13,7 @@ import {
13
13
  updateTask,
14
14
  removeTask,
15
15
  markTaskClarified,
16
+ setTaskSection,
16
17
  getActiveTask,
17
18
  setActiveTask,
18
19
  activateTask,
@@ -21,6 +22,7 @@ import {
21
22
  removeFromDone,
22
23
  returnToActive,
23
24
  type Task,
25
+ type TaskSection,
24
26
  type ActiveTask,
25
27
  type DoneTask,
26
28
  type StatuslineData,
@@ -110,15 +112,16 @@ function wrapText(text: string, maxWidth: number): string[] {
110
112
  return lines.length > 0 ? lines : [''];
111
113
  }
112
114
 
115
+ type SidebarSection = "inbox" | "clarified" | "in_progress" | "review";
116
+
113
117
  interface State {
114
118
  tasks: Task[];
115
119
  activeTask: ActiveTask | null;
116
120
  doneTasks: DoneTask[];
117
121
  claudeTodos: ClaudeTodo[];
118
122
  statusline: StatuslineData | null;
119
- selectedSection: "queue" | "done";
123
+ selectedSection: SidebarSection;
120
124
  selectedIndex: number;
121
- doneSelectedIndex: number;
122
125
  inputMode: InputMode;
123
126
  editingTaskId: string | null;
124
127
  inputBuffer: string;
@@ -132,9 +135,8 @@ export class RawSidebar {
132
135
  doneTasks: [],
133
136
  claudeTodos: [],
134
137
  statusline: null,
135
- selectedSection: "queue",
138
+ selectedSection: "inbox",
136
139
  selectedIndex: 0,
137
- doneSelectedIndex: 0,
138
140
  inputMode: "none",
139
141
  editingTaskId: null,
140
142
  inputBuffer: "",
@@ -169,6 +171,44 @@ export class RawSidebar {
169
171
  });
170
172
  }
171
173
 
174
+ // Get tasks for a specific section
175
+ private getTasksForSection(section: "inbox" | "clarified"): Task[] {
176
+ return this.getSortedTasks().filter(t => {
177
+ const taskSection = t.section || (t.clarified ? "clarified" : "inbox");
178
+ return taskSection === section;
179
+ });
180
+ }
181
+
182
+ // Get items for the currently selected section
183
+ private getCurrentSectionItems(): { type: "task" | "todo" | "done"; item: Task | ClaudeTodo | DoneTask }[] {
184
+ const { selectedSection, claudeTodos, doneTasks } = this.state;
185
+
186
+ if (selectedSection === "inbox") {
187
+ return this.getTasksForSection("inbox").map(t => ({ type: "task" as const, item: t }));
188
+ }
189
+ if (selectedSection === "clarified") {
190
+ return this.getTasksForSection("clarified").map(t => ({ type: "task" as const, item: t }));
191
+ }
192
+ if (selectedSection === "in_progress") {
193
+ return claudeTodos.map(t => ({ type: "todo" as const, item: t }));
194
+ }
195
+ if (selectedSection === "review") {
196
+ return doneTasks.map(t => ({ type: "done" as const, item: t }));
197
+ }
198
+ return [];
199
+ }
200
+
201
+ // Get the ordered list of non-empty sections for navigation
202
+ private getNavigableSections(): SidebarSection[] {
203
+ const sections: SidebarSection[] = [];
204
+ if (this.getTasksForSection("inbox").length > 0) sections.push("inbox");
205
+ if (this.getTasksForSection("clarified").length > 0) sections.push("clarified");
206
+ // in_progress always navigable (shows active task or Claude todos)
207
+ sections.push("in_progress");
208
+ if (this.state.doneTasks.length > 0) sections.push("review");
209
+ return sections;
210
+ }
211
+
172
212
  constructor(onClose?: () => void) {
173
213
  this.width = process.stdout.columns || 50;
174
214
  this.height = process.stdout.rows || 40;
@@ -594,124 +634,86 @@ export class RawSidebar {
594
634
  process.exit(0);
595
635
  }
596
636
 
597
- // Up arrow or k (navigates queue and done sections)
637
+ // Up arrow or k - navigate within section or to previous section
598
638
  if (str === '\x1b[A' || str === '\x1bOA' || str === 'k') {
599
- const { selectedSection, selectedIndex, doneSelectedIndex, tasks, doneTasks } = this.state;
600
-
601
- if (selectedSection === "queue") {
602
- if (tasks.length === 0) {
603
- // No queue items, try to go to done
604
- if (doneTasks.length > 0) {
605
- this.state.selectedSection = "done";
606
- this.state.doneSelectedIndex = doneTasks.length - 1;
607
- }
608
- } else if (selectedIndex > 0) {
609
- this.state.selectedIndex--;
610
- } else {
611
- // At top of queue, wrap to bottom of done (or bottom of queue)
612
- if (doneTasks.length > 0) {
613
- this.state.selectedSection = "done";
614
- this.state.doneSelectedIndex = Math.min(doneTasks.length - 1, 4); // Max 5 shown
615
- } else {
616
- this.state.selectedIndex = tasks.length - 1;
617
- }
618
- }
639
+ const items = this.getCurrentSectionItems();
640
+ const { selectedIndex } = this.state;
641
+
642
+ if (selectedIndex > 0) {
643
+ this.state.selectedIndex--;
619
644
  } else {
620
- // In done section
621
- if (doneSelectedIndex > 0) {
622
- this.state.doneSelectedIndex--;
623
- } else {
624
- // At top of done, wrap to bottom of queue (or bottom of done)
625
- if (tasks.length > 0) {
626
- this.state.selectedSection = "queue";
627
- this.state.selectedIndex = tasks.length - 1;
628
- } else {
629
- this.state.doneSelectedIndex = Math.min(doneTasks.length - 1, 4);
630
- }
645
+ // Move to previous section
646
+ const sections = this.getNavigableSections();
647
+ const currentIdx = sections.indexOf(this.state.selectedSection);
648
+ if (currentIdx > 0) {
649
+ const prevSection = sections[currentIdx - 1];
650
+ this.state.selectedSection = prevSection!;
651
+ // Select last item in previous section
652
+ const prevItems = this.getCurrentSectionItems();
653
+ this.state.selectedIndex = Math.max(0, prevItems.length - 1);
631
654
  }
632
655
  }
633
656
  this.render();
634
657
  return;
635
658
  }
636
659
 
637
- // Down arrow or j (navigates queue and done sections)
660
+ // Down arrow or j - navigate within section or to next section
638
661
  if (str === '\x1b[B' || str === '\x1bOB' || str === 'j') {
639
- const { selectedSection, selectedIndex, doneSelectedIndex, tasks, doneTasks } = this.state;
640
-
641
- if (selectedSection === "queue") {
642
- if (tasks.length === 0) {
643
- // No queue items, try to go to done
644
- if (doneTasks.length > 0) {
645
- this.state.selectedSection = "done";
646
- this.state.doneSelectedIndex = 0;
647
- }
648
- } else if (selectedIndex < tasks.length - 1) {
649
- this.state.selectedIndex++;
650
- } else {
651
- // At bottom of queue, wrap to top of done (or top of queue)
652
- if (doneTasks.length > 0) {
653
- this.state.selectedSection = "done";
654
- this.state.doneSelectedIndex = 0;
655
- } else {
656
- this.state.selectedIndex = 0;
657
- }
658
- }
662
+ const items = this.getCurrentSectionItems();
663
+ const { selectedIndex } = this.state;
664
+
665
+ if (selectedIndex < items.length - 1) {
666
+ this.state.selectedIndex++;
659
667
  } else {
660
- // In done section
661
- const maxDoneIndex = Math.min(doneTasks.length - 1, 4); // Max 5 shown
662
- if (doneSelectedIndex < maxDoneIndex) {
663
- this.state.doneSelectedIndex++;
664
- } else {
665
- // At bottom of done, wrap to top of queue (or top of done)
666
- if (tasks.length > 0) {
667
- this.state.selectedSection = "queue";
668
- this.state.selectedIndex = 0;
669
- } else {
670
- this.state.doneSelectedIndex = 0;
671
- }
668
+ // Move to next section
669
+ const sections = this.getNavigableSections();
670
+ const currentIdx = sections.indexOf(this.state.selectedSection);
671
+ if (currentIdx < sections.length - 1) {
672
+ const nextSection = sections[currentIdx + 1];
673
+ this.state.selectedSection = nextSection!;
674
+ this.state.selectedIndex = 0;
672
675
  }
673
676
  }
674
677
  this.render();
675
678
  return;
676
679
  }
677
680
 
678
- // Number keys 1-9 (select queue item, switches to queue section)
679
- if (/^[1-9]$/.test(str)) {
680
- const index = parseInt(str, 10) - 1;
681
- const sortedTasks = this.getSortedTasks();
682
- if (index < sortedTasks.length) {
683
- this.state.selectedSection = "queue";
684
- this.state.selectedIndex = index;
685
- this.render();
686
- }
681
+ // Tab - switch between sections
682
+ if (str === '\t') {
683
+ const sections = this.getNavigableSections();
684
+ const currentIdx = sections.indexOf(this.state.selectedSection);
685
+ const nextIdx = (currentIdx + 1) % sections.length;
686
+ this.state.selectedSection = sections[nextIdx]!;
687
+ this.state.selectedIndex = 0;
688
+ this.render();
687
689
  return;
688
690
  }
689
691
 
690
- // Enter - send task to Claude (only works in queue section)
692
+ // Enter - send task to Claude (works in inbox and clarified sections)
691
693
  if (str === '\r' || str === '\n') {
692
- if (this.state.selectedSection !== "queue") return;
693
- const sortedTasks = this.getSortedTasks();
694
- const task = sortedTasks[this.state.selectedIndex];
694
+ const { selectedSection, selectedIndex } = this.state;
695
+ if (selectedSection !== "inbox" && selectedSection !== "clarified") return;
696
+
697
+ const tasks = this.getTasksForSection(selectedSection);
698
+ const task = tasks[selectedIndex];
695
699
  if (task) {
696
700
  // Send to Claude and move to active
697
701
  sendToClaudePane(task.content);
698
702
  activateTask(task.id);
699
703
  this.loadData();
700
- this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
704
+ this.state.selectedIndex = Math.max(0, selectedIndex - 1);
701
705
  this.render();
702
706
  focusClaudePane();
703
707
  }
704
708
  return;
705
709
  }
706
710
 
707
- // Ctrl+Enter or 'c' - clarify mode (only works in queue section)
708
- // CSI u format: \x1b[13;5u (iTerm2), 'c' as fallback
711
+ // 'c' - clarify (only works in inbox section)
709
712
  if (str === '\x1b[13;5u' || str === '\x1b\r' || str === '\x1b\n' || str === 'c') {
710
- if (this.state.selectedSection !== "queue") return;
711
- const sortedTasks = this.getSortedTasks();
712
- const task = sortedTasks[this.state.selectedIndex];
713
+ if (this.state.selectedSection !== "inbox") return;
714
+ const tasks = this.getTasksForSection("inbox");
715
+ const task = tasks[this.state.selectedIndex];
713
716
  if (task) {
714
- // Invoke the clarify skill with task ID
715
717
  sendToClaudePane(`/clarify --task-id ${task.id} ${task.content}`);
716
718
  this.render();
717
719
  focusClaudePane();
@@ -719,24 +721,26 @@ export class RawSidebar {
719
721
  return;
720
722
  }
721
723
 
722
- // 'a' - add task (always switches to queue section)
724
+ // 'a' - add task (adds to inbox)
723
725
  if (str === 'a') {
724
726
  this.pausePolling();
725
- this.state.selectedSection = "queue";
727
+ this.state.selectedSection = "inbox";
726
728
  this.state.inputMode = "add";
727
729
  this.state.inputBuffer = "";
728
730
  this.state.inputCursor = 0;
729
- this.prevInputLineCount = 1; // Start with 1 empty line
731
+ this.prevInputLineCount = 1;
730
732
  this.render();
731
733
  this.setupInputCursor();
732
734
  return;
733
735
  }
734
736
 
735
- // 'e' - edit task (only works in queue section)
737
+ // 'e' - edit task (only in inbox/clarified sections)
736
738
  if (str === 'e') {
737
- if (this.state.selectedSection !== "queue") return;
738
- const sortedTasks = this.getSortedTasks();
739
- const task = sortedTasks[this.state.selectedIndex];
739
+ const { selectedSection, selectedIndex } = this.state;
740
+ if (selectedSection !== "inbox" && selectedSection !== "clarified") return;
741
+
742
+ const tasks = this.getTasksForSection(selectedSection);
743
+ const task = tasks[selectedIndex];
740
744
  if (task) {
741
745
  this.pausePolling();
742
746
  this.state.inputMode = "edit";
@@ -751,33 +755,31 @@ export class RawSidebar {
751
755
  return;
752
756
  }
753
757
 
754
- // 'd' - delete from queue, or confirm done in Review section
758
+ // 'd' - delete from inbox/clarified, or confirm done in review
755
759
  if (str === 'd') {
756
- if (this.state.selectedSection === "queue") {
757
- const sortedTasks = this.getSortedTasks();
758
- const task = sortedTasks[this.state.selectedIndex];
760
+ const { selectedSection, selectedIndex } = this.state;
761
+
762
+ if (selectedSection === "inbox" || selectedSection === "clarified") {
763
+ const tasks = this.getTasksForSection(selectedSection);
764
+ const task = tasks[selectedIndex];
759
765
  if (task) {
760
766
  removeTask(task.id);
761
767
  this.state.tasks = getTasks();
762
- this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
768
+ this.state.selectedIndex = Math.max(0, selectedIndex - 1);
763
769
  this.render();
764
770
  }
765
- } else {
766
- // Confirm done - remove from Review section
767
- const task = this.state.doneTasks[this.state.doneSelectedIndex];
771
+ } else if (selectedSection === "review") {
772
+ const task = this.state.doneTasks[selectedIndex];
768
773
  if (task) {
769
774
  removeFromDone(task.id);
770
775
  this.state.doneTasks = getRecentlyDone();
771
- // Adjust selection if needed
772
776
  if (this.state.doneTasks.length === 0) {
773
- // No more review tasks, go back to queue
774
- this.state.selectedSection = "queue";
775
- this.state.selectedIndex = Math.max(0, this.state.tasks.length - 1);
777
+ // Move to another section
778
+ const sections = this.getNavigableSections();
779
+ this.state.selectedSection = sections[0] || "inbox";
780
+ this.state.selectedIndex = 0;
776
781
  } else {
777
- this.state.doneSelectedIndex = Math.min(
778
- this.state.doneSelectedIndex,
779
- this.state.doneTasks.length - 1
780
- );
782
+ this.state.selectedIndex = Math.min(selectedIndex, this.state.doneTasks.length - 1);
781
783
  }
782
784
  this.render();
783
785
  }
@@ -785,21 +787,21 @@ export class RawSidebar {
785
787
  return;
786
788
  }
787
789
 
788
- // 'r' - return Review item to In Progress (not done yet)
790
+ // 'r' - return review item to in_progress
789
791
  if (str === 'r') {
790
- if (this.state.selectedSection === "done") {
791
- const task = this.state.doneTasks[this.state.doneSelectedIndex];
792
+ if (this.state.selectedSection === "review") {
793
+ const task = this.state.doneTasks[this.state.selectedIndex];
792
794
  if (task) {
793
795
  returnToActive(task.id);
794
796
  this.loadData();
795
- // Adjust selection if needed
796
797
  if (this.state.doneTasks.length === 0) {
797
- this.state.selectedSection = "queue";
798
- this.state.selectedIndex = Math.max(0, this.state.tasks.length - 1);
798
+ const sections = this.getNavigableSections();
799
+ this.state.selectedSection = sections[0] || "inbox";
800
+ this.state.selectedIndex = 0;
799
801
  } else {
800
- this.state.doneSelectedIndex = Math.min(
801
- this.state.doneSelectedIndex,
802
- Math.max(0, this.state.doneTasks.length - 1)
802
+ this.state.selectedIndex = Math.min(
803
+ this.state.selectedIndex,
804
+ this.state.doneTasks.length - 1
803
805
  );
804
806
  }
805
807
  this.render();
@@ -904,7 +906,7 @@ export class RawSidebar {
904
906
  if (!this.running) return;
905
907
 
906
908
  const lines: string[] = [];
907
- const { tasks, selectedIndex, inputMode, editingTaskId, inputBuffer, inputCursor } = this.state;
909
+ const { inputMode, editingTaskId, inputBuffer, inputCursor } = this.state;
908
910
 
909
911
  // Use dimmed colors when unfocused
910
912
  const bg = this.focused ? ansi.bgGray : ansi.dimBg;
@@ -936,120 +938,156 @@ export class RawSidebar {
936
938
  lines.push(`${bg} ${text}${headerContent}${ansi.clearToEnd}${ansi.reset}`);
937
939
  lines.push(bgLine); // Space after header
938
940
 
939
- // In Progress section - combines sidebar active task + Claude's TodoWrite items
940
- const { claudeTodos } = this.state;
941
- const { activeTask, doneTasks } = this.state;
942
- const activeTodos = claudeTodos.filter(t => t.status !== "completed");
943
941
  // Content width: total width - 2 (margin) - 4 (indicator like "[ ] ") - 2 (right padding)
944
942
  const maxContentWidth = this.width - 8;
945
-
946
- // Always show In Progress section
947
- lines.push(`${bg} ${bold}${text}In Progress${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
948
-
949
- // Show sidebar active task first (sent from queue)
950
- if (activeTask) {
951
- const content = activeTask.content.slice(0, maxContentWidth);
952
- lines.push(`${bg} ${ansi.green}▸ ${content}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
953
- }
954
-
955
- // Show Claude's TodoWrite items (what Claude is tracking)
956
- activeTodos.forEach((todo) => {
957
- let statusIcon: string;
958
- let todoColor = text;
959
- if (todo.status === "in_progress") {
960
- statusIcon = "● ";
961
- todoColor = ansi.green;
962
- } else {
963
- statusIcon = "○ ";
964
- }
965
- const content = todo.content.slice(0, maxContentWidth);
966
- lines.push(`${bg} ${todoColor}${statusIcon}${content}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
967
- });
968
-
969
- lines.push(bgLine);
970
-
971
- // Review section (tasks Claude thinks are done, awaiting user confirmation)
972
- const { selectedSection, doneSelectedIndex } = this.state;
973
- if (doneTasks.length > 0) {
974
- lines.push(`${bg} ${bold}${text}Review (${doneTasks.length})${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
975
- doneTasks.slice(0, 5).forEach((task, index) => {
976
- const isSelected = selectedSection === "done" && index === doneSelectedIndex && this.focused;
977
- const content = task.content.slice(0, maxContentWidth);
978
- const icon = isSelected ? "[?] " : " ? ";
979
- const color = isSelected ? text : muted;
980
- lines.push(`${bg} ${color}${icon}${content}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
981
- });
982
- lines.push(bgLine);
983
- }
984
-
985
- // To-dos section - single flat list sorted by priority
986
- const sortedTasks = this.getSortedTasks();
943
+ const { claudeTodos, activeTask, doneTasks, selectedSection, selectedIndex } = this.state;
987
944
 
988
945
  // Track where the input line is for cursor positioning
989
946
  let inputLineRow = 0;
990
947
 
991
- // Helper to render a task
992
- // Design: for recommended, [>] for selected, [ ] for unselected
993
- // Clarified tasks show planPath on second line when selected
994
- const renderTask = (task: Task, index: number) => {
995
- const isSelected = selectedSection === "queue" && index === selectedIndex;
948
+ // ANSI strikethrough
949
+ const strikethrough = '\x1b[9m';
950
+ const noStrike = '\x1b[29m';
951
+
952
+ // Helper to render a task in inbox/clarified sections
953
+ const renderTask = (task: Task, index: number, section: "inbox" | "clarified") => {
954
+ const isSelected = selectedSection === section && index === selectedIndex;
996
955
  const isEditing = inputMode === "edit" && editingTaskId === task.id;
997
956
  const star = task.recommended ? "★ " : " ";
998
957
  const bracket = (isSelected && this.focused) ? "[>] " : "[ ] ";
999
- const color = task.clarified ? text : muted;
958
+ const color = section === "clarified" ? text : muted;
1000
959
 
1001
960
  if (isEditing) {
1002
961
  inputLineRow = lines.length + 1;
1003
962
  this.inputRow = inputLineRow;
1004
- const wrappedLines = wrapText(inputBuffer, maxContentWidth - 2); // Account for star
963
+ const wrappedLines = wrapText(inputBuffer, maxContentWidth - 2);
1005
964
  if (wrappedLines.length === 0) wrappedLines.push('');
1006
965
  wrappedLines.forEach((line, i) => {
1007
- const prefix = i === 0 ? `${star}${bracket}` : " "; // 6 spaces to align with text
966
+ const prefix = i === 0 ? `${star}${bracket}` : " ";
1008
967
  const padding = ' '.repeat(Math.max(0, maxContentWidth - 2 - line.length));
1009
968
  lines.push(`${bg} ${prefix}${text}${line}${padding}${ansi.reset}`);
1010
969
  });
1011
970
  } else if (isSelected && this.focused && (task.content.length > maxContentWidth - 2 || task.content.includes('\n'))) {
1012
- // Wrap long content or content with newlines when selected
1013
971
  const wrappedLines = wrapText(task.content, maxContentWidth - 2);
1014
972
  wrappedLines.forEach((line, i) => {
1015
973
  const prefix = i === 0 ? `${star}${bracket}` : " ";
1016
974
  lines.push(`${bg} ${color}${prefix}${line}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1017
975
  });
1018
976
  } else {
1019
- // For non-selected or short content without newlines, show first line only
1020
977
  const firstLine = task.content.split('\n')[0] || task.content;
1021
978
  const content = firstLine.slice(0, maxContentWidth - 2);
1022
979
  lines.push(`${bg} ${color}${star}${bracket}${content}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1023
980
  }
1024
981
 
1025
- // Show plan path on second line when selected and has planPath
1026
- if (isSelected && this.focused && task.planPath && !isEditing) {
1027
- const planDisplay = `→ ${task.planPath}`.slice(0, maxContentWidth - 2);
1028
- lines.push(`${bg} ${muted} ${planDisplay}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
982
+ // Show spec preview or plan path when selected
983
+ if (isSelected && this.focused && (task.spec || task.planPath) && !isEditing) {
984
+ const detail = task.spec
985
+ ? task.spec.split('\n')[0]?.slice(0, maxContentWidth - 4) || ''
986
+ : `→ ${task.planPath}`;
987
+ lines.push(`${bg} ${muted} ${detail.slice(0, maxContentWidth - 2)}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1029
988
  }
1030
989
  };
1031
990
 
1032
- // Render To-dos header and tasks
1033
- const todoCount = sortedTasks.length > 0 ? ` (${sortedTasks.length})` : '';
1034
- lines.push(`${bg} ${bold}${text}To-dos${todoCount}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1035
- sortedTasks.forEach((task, index) => {
1036
- renderTask(task, index);
1037
- });
991
+ // === INBOX SECTION ===
992
+ const inboxTasks = this.getTasksForSection("inbox");
993
+ if (inboxTasks.length > 0 || inputMode === "add") {
994
+ const count = inboxTasks.length > 0 ? ` (${inboxTasks.length})` : '';
995
+ lines.push(`${bg} ${bold}${text}Inbox${count}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
996
+ inboxTasks.forEach((task, index) => renderTask(task, index, "inbox"));
1038
997
 
1039
- // Add new task input
1040
- if (inputMode === "add") {
1041
- inputLineRow = lines.length + 1; // 1-indexed row number
1042
- this.inputRow = inputLineRow; // Store for cursor positioning
1043
- const wrappedLines = wrapText(inputBuffer, maxContentWidth - 2); // Account for star area
1044
- if (wrappedLines.length === 0) wrappedLines.push('');
1045
- wrappedLines.forEach((line, i) => {
1046
- const prefix = i === 0 ? ' [ ] ' : ' '; // 2 space star area + 4 char bracket
1047
- const padding = ' '.repeat(Math.max(0, maxContentWidth - 2 - line.length));
1048
- lines.push(`${bg} ${prefix}${text}${line}${padding}${ansi.reset}`);
998
+ // Add new task input (in inbox section)
999
+ if (inputMode === "add") {
1000
+ inputLineRow = lines.length + 1;
1001
+ this.inputRow = inputLineRow;
1002
+ const wrappedLines = wrapText(inputBuffer, maxContentWidth - 2);
1003
+ if (wrappedLines.length === 0) wrappedLines.push('');
1004
+ wrappedLines.forEach((line, i) => {
1005
+ const prefix = i === 0 ? ' [ ] ' : ' ';
1006
+ const padding = ' '.repeat(Math.max(0, maxContentWidth - 2 - line.length));
1007
+ lines.push(`${bg} ${prefix}${text}${line}${padding}${ansi.reset}`);
1008
+ });
1009
+ } else if (this.focused && selectedSection === "inbox") {
1010
+ lines.push(`${bg} ${ansi.gray} [ ] press a to add${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1011
+ }
1012
+ lines.push(bgLine);
1013
+ }
1014
+
1015
+ // === CLARIFIED SECTION ===
1016
+ const clarifiedTasks = this.getTasksForSection("clarified");
1017
+ if (clarifiedTasks.length > 0) {
1018
+ lines.push(`${bg} ${bold}${text}Clarified (${clarifiedTasks.length})${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1019
+ clarifiedTasks.forEach((task, index) => renderTask(task, index, "clarified"));
1020
+ lines.push(bgLine);
1021
+ }
1022
+
1023
+ // === IN PROGRESS SECTION ===
1024
+ // Show active task with Claude's TodoWrite items indented as subtasks
1025
+ lines.push(`${bg} ${bold}${text}In Progress${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1026
+
1027
+ if (activeTask) {
1028
+ const content = activeTask.content.slice(0, maxContentWidth);
1029
+ lines.push(`${bg} ${ansi.green}▸ ${content}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1030
+
1031
+ // Show Claude's TodoWrite items indented as subtasks
1032
+ claudeTodos.forEach((todo) => {
1033
+ let statusIcon: string;
1034
+ let todoColor = text;
1035
+ let strike = '';
1036
+ let strikeEnd = '';
1037
+
1038
+ if (todo.status === "completed") {
1039
+ statusIcon = "✓ ";
1040
+ todoColor = muted;
1041
+ strike = strikethrough;
1042
+ strikeEnd = noStrike;
1043
+ } else if (todo.status === "in_progress") {
1044
+ statusIcon = "● ";
1045
+ todoColor = ansi.green;
1046
+ } else {
1047
+ statusIcon = "○ ";
1048
+ }
1049
+ const todoContent = todo.content.slice(0, maxContentWidth - 4); // Extra indent
1050
+ lines.push(`${bg} ${todoColor}${strike}${statusIcon}${todoContent}${strikeEnd}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1051
+ });
1052
+ } else if (claudeTodos.length > 0) {
1053
+ // No active task but Claude has todos
1054
+ claudeTodos.forEach((todo) => {
1055
+ let statusIcon: string;
1056
+ let todoColor = text;
1057
+ let strike = '';
1058
+ let strikeEnd = '';
1059
+
1060
+ if (todo.status === "completed") {
1061
+ statusIcon = "✓ ";
1062
+ todoColor = muted;
1063
+ strike = strikethrough;
1064
+ strikeEnd = noStrike;
1065
+ } else if (todo.status === "in_progress") {
1066
+ statusIcon = "● ";
1067
+ todoColor = ansi.green;
1068
+ } else {
1069
+ statusIcon = "○ ";
1070
+ }
1071
+ const todoContent = todo.content.slice(0, maxContentWidth);
1072
+ lines.push(`${bg} ${todoColor}${strike}${statusIcon}${todoContent}${strikeEnd}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1049
1073
  });
1050
- } else if (this.focused) {
1051
- // Show hint to add task (only when focused)
1052
- lines.push(`${bg} ${ansi.gray} [ ] press a to add${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1074
+ } else {
1075
+ lines.push(`${bg} ${muted} (nothing active)${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1076
+ }
1077
+ lines.push(bgLine);
1078
+
1079
+ // === REVIEW SECTION ===
1080
+ if (doneTasks.length > 0) {
1081
+ lines.push(`${bg} ${bold}${text}Review (${doneTasks.length})${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1082
+ doneTasks.slice(0, 5).forEach((task, index) => {
1083
+ const isSelected = selectedSection === "review" && index === selectedIndex && this.focused;
1084
+ const content = task.content.slice(0, maxContentWidth);
1085
+ const icon = isSelected ? "[✓] " : " ✓ ";
1086
+ const color = isSelected ? text : muted;
1087
+ // Strikethrough for completed items
1088
+ lines.push(`${bg} ${color}${strikethrough}${icon}${content}${noStrike}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1089
+ });
1090
+ lines.push(bgLine);
1053
1091
  }
1054
1092
 
1055
1093
  // Fill remaining space
@@ -1064,10 +1102,14 @@ export class RawSidebar {
1064
1102
  let helpText: string;
1065
1103
  if (inputMode !== "none") {
1066
1104
  helpText = "↵: submit | ⇧↵: newline | Esc: cancel";
1067
- } else if (selectedSection === "done") {
1068
- helpText = "d: done | r: return to progress | ↑↓: navigate";
1105
+ } else if (selectedSection === "review") {
1106
+ helpText = "d: confirm done | r: return | ↑↓: nav | Tab: section";
1107
+ } else if (selectedSection === "inbox") {
1108
+ helpText = "a: add | c: clarify | ↵: send | d: del | Tab: section";
1109
+ } else if (selectedSection === "clarified") {
1110
+ helpText = "↵: send | e: edit | d: del | Tab: section";
1069
1111
  } else {
1070
- helpText = "a: add | e: edit | d: del | ↵: send | c: clarify";
1112
+ helpText = "↑↓: navigate | Tab: switch section";
1071
1113
  }
1072
1114
  lines.push(`${bg} ${muted}${helpText}${ansi.reset}${bg}${ansi.clearToEnd}${ansi.reset}`);
1073
1115
  lines.push(bgLine);
@@ -48,14 +48,18 @@ function updateProjectMapping(): void {
48
48
  writeFileSync(mappingPath, JSON.stringify(mapping, null, 2));
49
49
  }
50
50
 
51
+ export type TaskSection = "inbox" | "clarified" | "in_progress" | "review";
52
+
51
53
  export interface Task {
52
54
  id: string;
53
55
  content: string;
54
56
  createdAt: string;
57
+ section?: TaskSection; // Which section the task belongs to (default: inbox)
55
58
  clarified?: boolean; // Was this task clarified via /clarify skill?
56
59
  priority?: number; // Sort order (lower = higher priority, 1 = most important)
57
60
  recommended?: boolean; // Claude's top picks (shown with star)
58
61
  planPath?: string; // Filename of associated Atomic Plan
62
+ spec?: string; // Spec content for the task (from /clarify)
59
63
  }
60
64
 
61
65
  export interface ActiveTask {
@@ -162,10 +166,12 @@ export function setTasks(tasks: Task[]): void {
162
166
  export function addTask(
163
167
  content: string,
164
168
  options?: {
169
+ section?: TaskSection;
165
170
  clarified?: boolean;
166
171
  priority?: number;
167
172
  recommended?: boolean;
168
173
  planPath?: string;
174
+ spec?: string;
169
175
  }
170
176
  ): Task {
171
177
  const tasks = getTasks();
@@ -173,10 +179,12 @@ export function addTask(
173
179
  id: crypto.randomUUID(),
174
180
  content,
175
181
  createdAt: new Date().toISOString(),
182
+ section: options?.section || "inbox",
176
183
  clarified: options?.clarified,
177
184
  priority: options?.priority,
178
185
  recommended: options?.recommended,
179
186
  planPath: options?.planPath,
187
+ spec: options?.spec,
180
188
  };
181
189
  tasks.push(task);
182
190
  setTasks(tasks);
@@ -192,11 +200,22 @@ export function updateTask(id: string, content: string): void {
192
200
  }
193
201
  }
194
202
 
195
- export function markTaskClarified(id: string): void {
203
+ export function markTaskClarified(id: string, spec?: string): void {
196
204
  const tasks = getTasks();
197
205
  const task = tasks.find((t) => t.id === id);
198
206
  if (task) {
199
207
  task.clarified = true;
208
+ task.section = "clarified";
209
+ if (spec) task.spec = spec;
210
+ setTasks(tasks);
211
+ }
212
+ }
213
+
214
+ export function setTaskSection(id: string, section: TaskSection): void {
215
+ const tasks = getTasks();
216
+ const task = tasks.find((t) => t.id === id);
217
+ if (task) {
218
+ task.section = section;
200
219
  setTasks(tasks);
201
220
  }
202
221
  }