Skip to content

Commit 592a3aa

Browse files
committed
feat(cli): release agent list command and update docs
- Implement 'agent list' command to show running AI agents - Detect user interruption in Claude Code session logs - Fix duplicate agent detection by mapping processes to recent sessions - Refactor AgentStatus to use Enum for better type safety - Update AgentManager to use AgentStatus priority - Register agent command in CLI entry point - Update planning and design docs with new logic and bug fixes
1 parent 8897c42 commit 592a3aa

File tree

10 files changed

+326
-154
lines changed

10 files changed

+326
-154
lines changed

docs/ai/design/feature-agent-management.md

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ type AgentStatus = 'running' | 'waiting' | 'idle' | 'unknown';
107107

108108
// Status display configuration
109109
const STATUS_CONFIG = {
110-
running: { emoji: '🟢', label: 'run', color: 'green' },
111-
waiting: { emoji: '🟡', label: 'wait', color: 'yellow' },
110+
running: { emoji: '🟢', label: 'running', color: 'green' },
111+
waiting: { emoji: '🟡', label: 'waiting', color: 'yellow' },
112112
idle: { emoji: '', label: 'idle', color: 'dim' },
113-
unknown: { emoji: '', label: '???', color: 'gray' },
113+
unknown: { emoji: '', label: 'unknown', color: 'gray' },
114114
};
115115
```
116116

@@ -123,7 +123,7 @@ interface ClaudeCodeSession {
123123
sessionLogPath: string; // Path to the .jsonl session file
124124
debugLogPath?: string; // Path to the debug log file
125125
lastActivity?: Date; // Timestamp of last log entry
126-
lastEntryType?: string; // 'assistant', 'user', 'progress'
126+
lastEntryType?: string; // 'assistant', 'user', 'progress', 'thinking'
127127
}
128128

129129
interface SessionEntry {
@@ -147,16 +147,21 @@ interface HistoryEntry {
147147
1. **Process Detection**: Query running processes (`ps aux | grep claude`) → List of PIDs + TTYs
148148
2. **Session Discovery**: Read `~/.claude/projects/*/sessions-index.json` → List of sessions with project paths
149149
3. **Session-Process Correlation**:
150-
- Read `cwd` field from session JSONL entries
151-
- Match session's `cwd` to process's working directory (from `ps -o cwd=`)
152-
- Alternative: Match by recent debug log activity in `~/.claude/debug/{session-id}.txt`
150+
- Group running processes by CWD (project path)
151+
- Group available sessions by project path
152+
- **Duplicate Filtering**: If multiple sessions match a project path:
153+
- Sort sessions by last active time (newest first)
154+
- Take the top N sessions, where N is the number of active processes for that path
155+
- Strictly map active processes to these N sessions to avoid "ghost" agents
153156
4. **Terminal Location**: For each matched process, find terminal location:
154157
- Get TTY from PID: `ps -p {PID} -o tty=`
155158
- Query tmux: `tmux list-panes -a -F '#{pane_tty} #{session}:#{window}.#{pane}'`
156159
- Fallback to iTerm2/Terminal.app via AppleScript
157160
5. **Status Extraction**: Read last entries from session JSONL → Determine status from `type` field
158-
- `assistant` or `progress` → running
159-
- `user` → waiting
161+
- `assistant` → waiting (Assistant finished response, waiting for user)
162+
- `user` → running (User sent message, agent processing)
163+
- *Exception*: If user message contains "interrupted", status is waiting
164+
- `progress` or `thinking` → running
160165
- `system` or old timestamp → idle
161166
6. **Summary Extraction**: Read `~/.claude/history.jsonl` → Get last user prompt for each session
162167
7. **Agent Naming**:
@@ -454,20 +459,22 @@ packages/cli/src/
454459
**Status Hierarchy** (sorted by attention priority):
455460
| Priority | Status | Display | Meaning |
456461
|----------|--------|---------|--------|
457-
| 1 | waiting | 🟡 wait | **NEEDS ATTENTION** |
458-
| 2 | running | 🟢 run | Actively processing |
462+
| 1 | waiting | 🟡 waiting | **NEEDS ATTENTION** |
463+
| 2 | running | 🟢 running | Actively processing |
459464
| 3 | idle | ⚪ idle | No recent activity |
460465
| 4 | unknown | ❓ ??? | Status undetermined |
461466

462467
### Decision 5: Status Detection Heuristics
463468
**Choice**: Use session JSONL `type` field to determine status
464469
**Status Mapping**:
465-
| Last Entry Type | Status |
466-
|-----------------|--------|
467-
| `assistant` or `progress` with recent timestamp | running |
468-
| `user` or completed assistant response | waiting |
469-
| Any type but timestamp > 5 minutes ago | idle |
470-
| Unable to parse | unknown |
470+
| Last Entry Type | Status | Detailed Logic |
471+
|-----------------|--------|----------------|
472+
| `user` | running | User sent input, agent is processing it. |
473+
| `progress`, `thinking` | running | Agent is actively using tools or thinking. |
474+
| `assistant` | waiting | Assistant finished response, waiting for user input. |
475+
| `user` (interrupted) | waiting | User manually interrupted the agent (contains "[Request interrupted..."). |
476+
| Any type | idle | Time since last activity > 5 minutes. |
477+
| Other/Error | unknown | Unable to parse or active process not found using files. |
471478

472479
## Non-Functional Requirements
473480
**How should the system perform?**

docs/ai/planning/feature-agent-management.md

Lines changed: 92 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,115 +10,131 @@ feature: agent-management
1010
## Milestones
1111
**What are the major checkpoints?**
1212

13-
- [ ] **Milestone 1**: Foundation - Process detection and state file reading
14-
- [ ] **Milestone 2**: Claude Code Integration - Full adapter implementation
15-
- [ ] **Milestone 3**: CLI Integration - Complete `agent list` command
13+
- [x] **Milestone 1**: Foundation - Process detection and state file reading (Completed)
14+
- [x] **Milestone 2**: Claude Code Integration - Full adapter implementation (Completed)
15+
- [x] **Milestone 3**: CLI Integration - Complete `agent list` command (Completed)
1616

1717
## Task Breakdown
1818
**What specific work needs to be done?**
1919

2020
### Phase 1: Foundation
2121

2222
#### Task 1.1: Create Process Detection Utility
23-
**Status**: 🔲 Not Started
24-
- [ ] Create `packages/cli/src/util/process.ts`
25-
- [ ] Implement `listProcesses()` function to query running processes
26-
- [ ] Filter by process name pattern (e.g., `claude`)
27-
- [ ] Extract PID, command line, working directory
28-
- [ ] Handle cross-platform differences (macOS focus first)
29-
- [ ] Write unit tests
23+
**Status**: ✅ Completed (Commit: e35315d)
24+
- [x] Create `packages/cli/src/util/process.ts`
25+
- [x] Implement `listProcesses()` function to query running processes
26+
- [x] Filter by process name pattern (e.g., `claude`)
27+
- [x] Extract PID, command line, working directory
28+
- [x] Handle cross-platform differences (macOS focus first)
29+
- [x] Write unit tests
3030

3131
**Estimated Effort**: 2 hours
32+
**Actual Effort**: Part of commit e35315d (Jan 29, 2026)
3233

3334
#### Task 1.2: Create Agent Adapter Interface
34-
**Status**: 🔲 Not Started
35-
- [ ] Create `packages/cli/src/lib/adapters/AgentAdapter.ts`
36-
- [ ] Define `AgentAdapter` interface
37-
- [ ] Define `AgentInfo`, `AgentType`, `AgentStatus` types
38-
- [ ] Export types for use by implementations
35+
**Status**: ✅ Completed (Commit: e35315d)
36+
- [x] Create `packages/cli/src/lib/adapters/AgentAdapter.ts`
37+
- [x] Define `AgentAdapter` interface
38+
- [x] Define `AgentInfo`, `AgentType`, `AgentStatus` types
39+
- [x] Export types for use by implementations
40+
- [x] Added `STATUS_CONFIG` for display configuration
41+
- [x] Added `ProcessInfo` interface
3942

4043
**Estimated Effort**: 1 hour
44+
**Actual Effort**: Part of commit e35315d (Jan 29, 2026)
4145

4246
#### Task 1.3: Create AgentManager Class
43-
**Status**: 🔲 Not Started
44-
- [ ] Create `packages/cli/src/lib/AgentManager.ts`
45-
- [ ] Implement adapter registration
46-
- [ ] Implement `listAgents()` method
47-
- [ ] Aggregate results from all registered adapters
48-
- [ ] Handle adapter errors gracefully
49-
- [ ] Write unit tests
47+
**Status**: ✅ Completed (Commit: e35315d)
48+
- [x] Create `packages/cli/src/lib/AgentManager.ts`
49+
- [x] Implement adapter registration
50+
- [x] Implement `listAgents()` method
51+
- [x] Aggregate results from all registered adapters
52+
- [x] Handle adapter errors gracefully
53+
- [x] Write unit tests (252 lines of comprehensive tests)
54+
- [x] Added utility methods: `unregisterAdapter`, `hasAdapter`, `getAdapterCount`, `clear`
55+
- [x] Implemented status-based sorting (waiting > running > idle > unknown)
5056

5157
**Estimated Effort**: 2 hours
58+
**Actual Effort**: Part of commit e35315d (Jan 29, 2026)
5259

5360
### Phase 2: Claude Code Integration
5461

5562
#### Task 2.1: Implement Claude Code Session Reader
56-
**Status**: 🔲 Not Started
57-
- [ ] Create `packages/cli/src/lib/adapters/ClaudeCodeAdapter.ts`
58-
- [ ] Implement reading `~/.claude/projects/*/` directories
59-
- [ ] Parse `sessions-index.json` to get project paths
60-
- [ ] Read session JSONL files for conversation data
61-
- [ ] Read `~/.claude/history.jsonl` for user prompts
62-
- [ ] Extract session slug from JSONL entries
63-
- [ ] Write unit tests with fixture data
63+
**Status**: ✅ Completed (Commit: df346a6)
64+
- [x] Create `packages/cli/src/lib/adapters/ClaudeCodeAdapter.ts`
65+
- [x] Implement reading `~/.claude/projects/*/` directories
66+
- [x] Parse `sessions-index.json` to get project paths
67+
- [x] Read session JSONL files for conversation data
68+
- [x] Read `~/.claude/history.jsonl` for user prompts
69+
- [x] Extract session slug from JSONL entries
70+
- [x] Write unit tests with fixture data (272 lines of tests)
71+
- [x] Created helper utility `util/file.ts` with `readLastLines`, `readJsonLines`, `readJson` functions
6472

6573
**Estimated Effort**: 3 hours
74+
**Actual Effort**: Part of commit df346a6 (Jan 29, 2026)
6675

6776
#### Task 2.2: Implement Status Detection
68-
**Status**: 🔲 Not Started
69-
- [ ] Read last entries from session JSONL files
70-
- [ ] Implement `determineStatus()` method based on `type` field
71-
- [ ] Map entry types: `assistant`/`progress` → running, `user` → waiting
72-
- [ ] Add idle detection for sessions with old timestamps (>5 min)
73-
- [ ] Implement visual status display (🟡 wait, 🟢 run, ⚪ idle)
74-
- [ ] Write unit tests with sample session data
77+
**Status**: ✅ Completed (Commit: df346a6)
78+
- [x] Read last entries from session JSONL files
79+
- [x] Implement `determineStatus()` method based on `type` field
80+
- [x] Map entry types: `assistant`/`progress` → running, `user` → waiting
81+
- [x] Add idle detection for sessions with old timestamps (>5 min)
82+
- [x] Implement visual status display (🟡 wait, 🟢 run, ⚪ idle)
83+
- [x] Write unit tests with sample session data
84+
- [x] Handles edge cases: no last entry, unknown types
7585

7686
**Estimated Effort**: 2 hours
87+
**Actual Effort**: Part of commit df346a6 (Jan 29, 2026)
7788

7889
#### Task 2.3: Implement Summary and Time Extraction
79-
**Status**: 🔲 Not Started
80-
- [ ] Read `display` field from `~/.claude/history.jsonl`
81-
- [ ] Match history entries to sessions by `sessionId`
82-
- [ ] Implement `extractSummary()` with ~40 char truncation
83-
- [ ] Implement `getRelativeTime()` for "2m ago", "just now" display
84-
- [ ] Provide fallback summary: "Session started"
85-
- [ ] Write unit tests
90+
**Status**: ✅ Completed (Commit: df346a6)
91+
- [x] Read `display` field from `~/.claude/history.jsonl`
92+
- [x] Match history entries to sessions by `sessionId`
93+
- [x] Implement `truncateSummary()` with ~40 char truncation
94+
- [x] Implement `getRelativeTime()` for "2m ago", "just now" display
95+
- [x] Provide fallback summary: "Session started"
96+
- [x] Write unit tests
97+
- [x] Handles minutes, hours, and days display formats
8698

8799
**Estimated Effort**: 2 hours
100+
**Actual Effort**: Part of commit df346a6 (Jan 29, 2026)
88101

89102
#### Task 2.4: Complete Claude Code Adapter
90-
**Status**: 🔲 Not Started
91-
- [ ] Implement full `detectAgents()` method
92-
- [ ] Correlate running processes with active sessions
93-
- [ ] Implement agent naming: project basename + optional slug
94-
- [ ] Filter out stale sessions (no matching process)
95-
- [ ] Sort agents: waiting first, then running, then idle
96-
- [ ] Integration testing with real Claude Code
97-
- [ ] Write unit tests
103+
**Status**: ✅ Completed (Commit: df346a6)
104+
- [x] Implement full `detectAgents()` method
105+
- [x] Correlate running processes with active sessions
106+
- [x] Implement agent naming: project basename + optional slug
107+
- [x] Filter out stale sessions (no matching process)
108+
- [x] Sort agents: waiting first, then running, then idle (handled by AgentManager)
109+
- [x] Integration testing with real Claude Code
110+
- [x] Write unit tests
111+
- [x] Implemented `canHandle()` method for process detection
112+
- [x] Added `generateAgentName()` for unique naming with slug support
98113

99114
**Estimated Effort**: 2 hours
115+
**Actual Effort**: Part of commit df346a6 (Jan 29, 2026)
100116

101117
### Phase 3: CLI Integration (List Command)
102118

103119
#### Task 3.1: Create Agent CLI Command
104-
**Status**: 🔲 Not Started
105-
- [ ] Create `packages/cli/src/commands/agent.ts`
106-
- [ ] Register `agent` parent command
107-
- [ ] Register `agent list` subcommand
108-
- [ ] Use AgentManager to fetch agents
109-
- [ ] Format table with columns: Agent, Status, Working On, Active
110-
- [ ] Apply status colors and emoji
111-
- [ ] Add attention summary footer when agents waiting
112-
- [ ] Handle empty results with actionable guidance
113-
- [ ] Implement `--json` flag for JSON output
120+
**Status**: ✅ Completed
121+
- [x] Create `packages/cli/src/commands/agent.ts`
122+
- [x] Register `agent` parent command
123+
- [x] Register `agent list` subcommand
124+
- [x] Use AgentManager to fetch agents
125+
- [x] Format table with columns: Agent, Status, Working On, Active
126+
- [x] Apply status colors and emoji
127+
- [x] Add attention summary footer when agents waiting
128+
- [x] Handle empty results with actionable guidance
129+
- [x] Implement `--json` flag for JSON output
114130

115131
**Estimated Effort**: 2.5 hours
116132

117133
#### Task 3.2: Register Command in CLI
118-
**Status**: 🔲 Not Started
119-
- [ ] Import and register agent command in main CLI file
120-
- [ ] Update help text
121-
- [ ] Manual testing
134+
**Status**: ✅ Completed
135+
- [x] Import and register agent command in main CLI file
136+
- [x] Update help text
137+
- [x] Manual testing
122138

123139
**Estimated Effort**: 0.5 hours
124140

@@ -232,14 +248,16 @@ graph LR
232248
## Timeline & Estimates
233249
**When will things be done?**
234250

235-
| Phase | Tasks | Estimated Hours |
236-
|-------|-------|-----------------|
237-
| Phase 1: Foundation | 1.1, 1.2, 1.3 | 5 hours |
238-
| Phase 2: Claude Code Integration | 2.1, 2.2, 2.3, 2.4 | 9 hours |
239-
| Phase 3: CLI Integration (List) | 3.1, 3.2 | 3 hours |
240-
| Phase 4: Agent Open Command | 4.1, 4.2, 4.3, 4.4, 4.5, 4.6 | 8 hours |
241-
| Phase 5: Testing & Documentation | 5.1, 5.2 | 3 hours |
242-
| **Total** | | **28 hours** |
251+
| Phase | Tasks | Estimated Hours | Status |
252+
|-------|-------|-----------------|--------|
253+
| Phase 1: Foundation | 1.1, 1.2, 1.3 | 5 hours | ✅ Completed |
254+
| Phase 2: Claude Code Integration | 2.1, 2.2, 2.3, 2.4 | 9 hours | ✅ Completed |
255+
| Phase 3: CLI Integration (List) | 3.1, 3.2 | 3 hours | ✅ Completed |
256+
| Phase 4: Agent Open Command | 4.1, 4.2, 4.3, 4.4, 4.5, 4.6 | 8 hours | 🔲 Not Started |
257+
| Phase 5: Testing & Documentation | 5.1, 5.2 | 3 hours | 🔲 Not Started |
258+
| **Total** | | **28 hours** | |
259+
| **Completed** | | **17 hours** | 60% Complete |
260+
| **Remaining** | | **11 hours** | |
243261

244262
### Suggested Implementation Order
245263
1. Task 1.2 (Interface) - Define contracts first

packages/cli/src/__tests__/lib/AgentManager.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { describe, it, expect, beforeEach } from '@jest/globals';
66
import { AgentManager } from '../../lib/AgentManager';
77
import type { AgentAdapter, AgentInfo, AgentType } from '../../lib/adapters/AgentAdapter';
8+
import { AgentStatus } from '../../lib/adapters/AgentAdapter';
89

910
// Mock adapter for testing
1011
class MockAdapter implements AgentAdapter {
@@ -39,7 +40,7 @@ function createMockAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {
3940
return {
4041
name: 'test-agent',
4142
type: 'Claude Code',
42-
status: 'running',
43+
status: AgentStatus.RUNNING,
4344
statusDisplay: '🟢 run',
4445
summary: 'Test summary',
4546
pid: 12345,
@@ -179,10 +180,10 @@ describe('AgentManager', () => {
179180

180181
it('should sort agents by status priority (waiting first)', async () => {
181182
const mockAgents = [
182-
createMockAgent({ name: 'idle-agent', status: 'idle' }),
183-
createMockAgent({ name: 'waiting-agent', status: 'waiting' }),
184-
createMockAgent({ name: 'running-agent', status: 'running' }),
185-
createMockAgent({ name: 'unknown-agent', status: 'unknown' }),
183+
createMockAgent({ name: 'idle-agent', status: AgentStatus.IDLE }),
184+
createMockAgent({ name: 'waiting-agent', status: AgentStatus.WAITING }),
185+
createMockAgent({ name: 'running-agent', status: AgentStatus.RUNNING }),
186+
createMockAgent({ name: 'unknown-agent', status: AgentStatus.UNKNOWN }),
186187
];
187188
const adapter = new MockAdapter('Claude Code', mockAgents);
188189

0 commit comments

Comments
 (0)