Skip to content

Commit 8f48b96

Browse files
Add cwd in agent list command (#47)
1 parent 1024be7 commit 8f48b96

7 files changed

Lines changed: 235 additions & 11 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
phase: design
3+
title: Display CWD in Agent List — Design
4+
description: Technical design for adding CWD column to agent list table output
5+
---
6+
7+
# Display CWD in Agent List — Design
8+
9+
## Architecture Overview
10+
11+
No new components are needed. This feature modifies the existing CLI table rendering in the `agent list` command.
12+
13+
```mermaid
14+
graph LR
15+
AgentAdapter -->|AgentInfo.projectPath| AgentManager
16+
AgentManager -->|agents array| CLI["agent list command"]
17+
CLI -->|formatCwd| TableRenderer["ui.table()"]
18+
```
19+
20+
## Data Models
21+
22+
No changes to `AgentInfo`. The existing `projectPath: string` field is used as-is.
23+
24+
## Component Changes
25+
26+
### `packages/cli/src/commands/agent.ts`
27+
28+
1. **New helper function**`formatCwd(projectPath: string): string`
29+
- Replaces home directory prefix with `~` using `os.homedir()`
30+
- Returns the shortened path or the original if no substitution applies
31+
- Returns empty string for empty/undefined input
32+
33+
2. **Table modification** — Add "CWD" column:
34+
- **Position**: Column index 1 (after "Agent", before "Type")
35+
- **Data**: `formatCwd(agent.projectPath)`
36+
- **Style**: `chalk.dim` for subdued visual weight
37+
38+
### Updated table structure
39+
40+
| Agent | CWD | Type | Status | Working On | Active |
41+
|-------|-----|------|--------|------------|--------|
42+
| my-project | ~/Code/my-project | Claude Code | 🟢 run | Investigating... | 5m ago |
43+
44+
## Design Decisions
45+
46+
| Decision | Choice | Rationale |
47+
|----------|--------|-----------|
48+
| Path format | `~` substitution | Compact, familiar to CLI users |
49+
| Column position | After Agent | CWD is a project identifier, logically grouped with name |
50+
| Column style | `chalk.dim` | Secondary info, shouldn't dominate the table |
51+
52+
## Non-Functional Requirements
53+
54+
- No performance impact — `os.homedir()` is a synchronous, cached call
55+
- No new dependencies required
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
phase: implementation
3+
title: Display CWD in Agent List — Implementation
4+
description: Implementation notes for CWD column feature
5+
---
6+
7+
# Display CWD in Agent List — Implementation
8+
9+
## Files to Modify
10+
11+
| File | Change |
12+
|------|--------|
13+
| `packages/cli/src/commands/agent.ts` | Add `formatCwd()` helper, add CWD column to table |
14+
| `packages/cli/src/__tests__/commands/agent.test.ts` | Update tests for new column |
15+
16+
## Implementation Notes
17+
18+
### `formatCwd(projectPath: string): string`
19+
20+
```typescript
21+
import os from 'os';
22+
23+
function formatCwd(projectPath?: string): string {
24+
if (!projectPath) return '';
25+
const home = os.homedir();
26+
if (projectPath.startsWith(home)) {
27+
return '~' + projectPath.slice(home.length);
28+
}
29+
return projectPath;
30+
}
31+
```
32+
33+
### Table changes
34+
35+
- Insert at index 1 in: headers, rows mapping, columnStyles
36+
- Header: `'CWD'`
37+
- Row value: `formatCwd(agent.projectPath)`
38+
- Style: `(text) => chalk.dim(text)`
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
phase: planning
3+
title: Display CWD in Agent List — Planning
4+
description: Task breakdown for adding CWD column to agent list
5+
---
6+
7+
# Display CWD in Agent List — Planning
8+
9+
## Milestones
10+
11+
- [ ] Milestone 1: CWD column visible in `agent list` output
12+
13+
## Task Breakdown
14+
15+
### Phase 1: Implementation
16+
17+
- [ ] Task 1.1: Add `formatCwd()` helper function to `packages/cli/src/commands/agent.ts`
18+
- Import `os` module
19+
- Implement home directory `~` substitution
20+
- [ ] Task 1.2: Add CWD column to table rendering
21+
- Add `formatCwd(agent.projectPath)` to rows array (index 1)
22+
- Add "CWD" to headers array (index 1)
23+
- Add `chalk.dim` column style (index 1)
24+
25+
### Phase 2: Testing
26+
27+
- [ ] Task 2.1: Update existing agent list tests to include CWD column
28+
- [ ] Task 2.2: Add unit tests for `formatCwd()` helper
29+
30+
## Dependencies
31+
32+
- None — all data is already available in `AgentInfo.projectPath`
33+
34+
## Timeline & Estimates
35+
36+
- Total effort: Small (< 1 hour)
37+
- Task 1.1 + 1.2: ~15 min implementation
38+
- Task 2.1 + 2.2: ~15 min testing
39+
40+
## Risks & Mitigation
41+
42+
| Risk | Mitigation |
43+
|------|------------|
44+
| Table width with long paths | `~` substitution reduces length; terminal handles wrapping |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
phase: requirements
3+
title: Display CWD in Agent List Command
4+
description: Add a CWD column to the agent list table showing each agent's working directory
5+
---
6+
7+
# Display CWD in Agent List Command
8+
9+
## Problem Statement
10+
11+
When running `ai-devkit agent list`, users see a table with Agent, Type, Status, Working On, and Active columns. However, there is no way to tell **which directory** each agent is working in. This makes it difficult to distinguish between multiple agents of the same type running in different projects.
12+
13+
The `projectPath` field already exists in the `AgentInfo` data model and is populated by adapters, but it is not surfaced in the table output.
14+
15+
## Goals & Objectives
16+
17+
**Primary goals:**
18+
- Display each agent's current working directory (cwd) in the `agent list` table output
19+
20+
**Non-goals:**
21+
- Changing the `--json` output format (it already includes `projectPath`)
22+
- Adding filtering/sorting by cwd
23+
- Modifying how `projectPath` is collected by adapters
24+
25+
## User Stories & Use Cases
26+
27+
- As a developer running multiple agents across projects, I want to see each agent's working directory so I can quickly identify which agent belongs to which project.
28+
- As a developer with agents in nested directories, I want the path displayed in a compact, readable format (shortened with `~` for home directory).
29+
30+
## Success Criteria
31+
32+
- [ ] `agent list` table includes a "CWD" column showing the agent's `projectPath`
33+
- [ ] Long paths are shortened (home directory replaced with `~`)
34+
- [ ] Column is positioned after "Agent" name for quick visual association
35+
- [ ] Existing tests updated to cover the new column
36+
- [ ] No regressions in existing agent list functionality
37+
38+
## Constraints & Assumptions
39+
40+
- The `projectPath` field is already available in `AgentInfo` — no adapter changes needed
41+
- Path shortening uses `os.homedir()` for `~` substitution
42+
- Column styling uses `chalk.dim` to keep focus on agent name and status
43+
44+
## Questions & Open Items
45+
46+
- None — straightforward display addition using existing data.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
phase: testing
3+
title: Display CWD in Agent List — Testing
4+
description: Test strategy for CWD column feature
5+
---
6+
7+
# Display CWD in Agent List — Testing
8+
9+
## Test Coverage Goals
10+
11+
- 100% coverage of `formatCwd()` helper
12+
- Verify table output includes CWD column
13+
14+
## Unit Tests
15+
16+
### `formatCwd()` helper
17+
- [ ] Returns `~`-prefixed path when projectPath starts with home directory
18+
- [ ] Returns original path when projectPath doesn't start with home directory
19+
- [ ] Returns empty string for empty/undefined input
20+
21+
### Table rendering
22+
- [ ] Table headers include "CWD" column
23+
- [ ] Table rows include formatted projectPath values
24+
- [ ] CWD column appears in correct position (after Agent)
25+
26+
## Integration Tests
27+
28+
- [ ] `agent list` with agents shows CWD column in output
29+
- [ ] `agent list --json` still returns full `projectPath` (no regression)

packages/cli/src/__tests__/commands/agent.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ describe('agent command', () => {
127127

128128
expect(ui.table).toHaveBeenCalled();
129129
const tableArg: any = (ui.table as any).mock.calls[0][0];
130-
expect(tableArg.headers).toEqual(['Agent', 'Type', 'Status', 'Working On', 'Active']);
131-
expect(tableArg.rows[0][1]).toBe('Claude Code');
132-
expect(tableArg.rows[1][1]).toBe('Codex');
133-
expect(tableArg.rows[0][2]).toContain('wait');
134-
expect(tableArg.rows[0][4]).toBe('just now');
130+
expect(tableArg.headers).toEqual(['Agent', 'CWD', 'Type', 'Status', 'Working On', 'Active']);
131+
expect(tableArg.rows[0][2]).toBe('Claude Code');
132+
expect(tableArg.rows[1][2]).toBe('Codex');
133+
expect(tableArg.rows[0][3]).toContain('wait');
134+
expect(tableArg.rows[0][5]).toBe('just now');
135135
expect(ui.warning).toHaveBeenCalledWith('1 agent(s) waiting for input.');
136136
});
137137

@@ -149,10 +149,10 @@ describe('agent command', () => {
149149
await program.parseAsync(['node', 'test', 'agent', 'list']);
150150

151151
const tableArg: any = (ui.table as any).mock.calls[0][0];
152-
expect(tableArg.rows[0][1]).toBe('Claude Code');
153-
expect(tableArg.rows[1][1]).toBe('Codex');
154-
expect(tableArg.rows[2][1]).toBe('Gemini CLI');
155-
expect(tableArg.rows[3][1]).toBe('Other');
152+
expect(tableArg.rows[0][2]).toBe('Claude Code');
153+
expect(tableArg.rows[1][2]).toBe('Codex');
154+
expect(tableArg.rows[2][2]).toBe('Gemini CLI');
155+
expect(tableArg.rows[3][2]).toBe('Other');
156156
});
157157

158158
it('truncates working-on text to first line', async () => {
@@ -174,7 +174,7 @@ Waiting on user input`,
174174
await program.parseAsync(['node', 'test', 'agent', 'list']);
175175

176176
const tableArg: any = (ui.table as any).mock.calls[0][0];
177-
expect(tableArg.rows[0][3]).toBe('Investigating parser bug');
177+
expect(tableArg.rows[0][4]).toBe('Investigating parser bug');
178178
});
179179

180180
it('shows available agents when open target is not found', async () => {

packages/cli/src/commands/agent.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os from 'os';
12
import { Command } from 'commander';
23
import chalk from 'chalk';
34
import inquirer from 'inquirer';
@@ -50,6 +51,15 @@ function formatType(type: AgentType): string {
5051
return TYPE_LABELS[type] ?? type;
5152
}
5253

54+
function formatCwd(projectPath?: string): string {
55+
if (!projectPath) return '';
56+
const home = os.homedir();
57+
if (projectPath.startsWith(home)) {
58+
return '~' + projectPath.slice(home.length);
59+
}
60+
return projectPath;
61+
}
62+
5363
function formatWorkOn(summary?: string): string {
5464
const firstLine = (summary ?? '').split(/\r?\n/, 1)[0] || '';
5565
return firstLine || 'No active task';
@@ -89,18 +99,20 @@ export function registerAgentCommand(program: Command): void {
8999

90100
const rows = agents.map(agent => [
91101
agent.name,
102+
formatCwd(agent.projectPath),
92103
formatType(agent.type),
93104
formatStatus(agent.status),
94105
formatWorkOn(agent.summary),
95106
formatRelativeTime(agent.lastActive)
96107
]);
97108

98109
ui.table({
99-
headers: ['Agent', 'Type', 'Status', 'Working On', 'Active'],
110+
headers: ['Agent', 'CWD', 'Type', 'Status', 'Working On', 'Active'],
100111
rows: rows,
101112
columnStyles: [
102113
(text) => chalk.cyan(text),
103114
(text) => chalk.dim(text),
115+
(text) => chalk.dim(text),
104116
(text) => {
105117
if (text.includes(STATUS_DISPLAY[AgentStatus.RUNNING].label)) return chalk.green(text);
106118
if (text.includes(STATUS_DISPLAY[AgentStatus.WAITING].label)) return chalk.yellow(text);

0 commit comments

Comments
 (0)