Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions docs/ai/design/feature-agent-list-cwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
phase: design
title: Display CWD in Agent List — Design
description: Technical design for adding CWD column to agent list table output
---

# Display CWD in Agent List — Design

## Architecture Overview

No new components are needed. This feature modifies the existing CLI table rendering in the `agent list` command.

```mermaid
graph LR
AgentAdapter -->|AgentInfo.projectPath| AgentManager
AgentManager -->|agents array| CLI["agent list command"]
CLI -->|formatCwd| TableRenderer["ui.table()"]
```

## Data Models

No changes to `AgentInfo`. The existing `projectPath: string` field is used as-is.

## Component Changes

### `packages/cli/src/commands/agent.ts`

1. **New helper function** — `formatCwd(projectPath: string): string`
- Replaces home directory prefix with `~` using `os.homedir()`
- Returns the shortened path or the original if no substitution applies
- Returns empty string for empty/undefined input

2. **Table modification** — Add "CWD" column:
- **Position**: Column index 1 (after "Agent", before "Type")
- **Data**: `formatCwd(agent.projectPath)`
- **Style**: `chalk.dim` for subdued visual weight

### Updated table structure

| Agent | CWD | Type | Status | Working On | Active |
|-------|-----|------|--------|------------|--------|
| my-project | ~/Code/my-project | Claude Code | 🟢 run | Investigating... | 5m ago |

## Design Decisions

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Path format | `~` substitution | Compact, familiar to CLI users |
| Column position | After Agent | CWD is a project identifier, logically grouped with name |
| Column style | `chalk.dim` | Secondary info, shouldn't dominate the table |

## Non-Functional Requirements

- No performance impact — `os.homedir()` is a synchronous, cached call
- No new dependencies required
38 changes: 38 additions & 0 deletions docs/ai/implementation/feature-agent-list-cwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
phase: implementation
title: Display CWD in Agent List — Implementation
description: Implementation notes for CWD column feature
---

# Display CWD in Agent List — Implementation

## Files to Modify

| File | Change |
|------|--------|
| `packages/cli/src/commands/agent.ts` | Add `formatCwd()` helper, add CWD column to table |
| `packages/cli/src/__tests__/commands/agent.test.ts` | Update tests for new column |

## Implementation Notes

### `formatCwd(projectPath: string): string`

```typescript
import os from 'os';

function formatCwd(projectPath?: string): string {
if (!projectPath) return '';
const home = os.homedir();
if (projectPath.startsWith(home)) {
return '~' + projectPath.slice(home.length);
}
return projectPath;
}
```

### Table changes

- Insert at index 1 in: headers, rows mapping, columnStyles
- Header: `'CWD'`
- Row value: `formatCwd(agent.projectPath)`
- Style: `(text) => chalk.dim(text)`
44 changes: 44 additions & 0 deletions docs/ai/planning/feature-agent-list-cwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
phase: planning
title: Display CWD in Agent List — Planning
description: Task breakdown for adding CWD column to agent list
---

# Display CWD in Agent List — Planning

## Milestones

- [ ] Milestone 1: CWD column visible in `agent list` output

## Task Breakdown

### Phase 1: Implementation

- [ ] Task 1.1: Add `formatCwd()` helper function to `packages/cli/src/commands/agent.ts`
- Import `os` module
- Implement home directory `~` substitution
- [ ] Task 1.2: Add CWD column to table rendering
- Add `formatCwd(agent.projectPath)` to rows array (index 1)
- Add "CWD" to headers array (index 1)
- Add `chalk.dim` column style (index 1)

### Phase 2: Testing

- [ ] Task 2.1: Update existing agent list tests to include CWD column
- [ ] Task 2.2: Add unit tests for `formatCwd()` helper

## Dependencies

- None — all data is already available in `AgentInfo.projectPath`

## Timeline & Estimates

- Total effort: Small (< 1 hour)
- Task 1.1 + 1.2: ~15 min implementation
- Task 2.1 + 2.2: ~15 min testing

## Risks & Mitigation

| Risk | Mitigation |
|------|------------|
| Table width with long paths | `~` substitution reduces length; terminal handles wrapping |
46 changes: 46 additions & 0 deletions docs/ai/requirements/feature-agent-list-cwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
phase: requirements
title: Display CWD in Agent List Command
description: Add a CWD column to the agent list table showing each agent's working directory
---

# Display CWD in Agent List Command

## Problem Statement

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.

The `projectPath` field already exists in the `AgentInfo` data model and is populated by adapters, but it is not surfaced in the table output.

## Goals & Objectives

**Primary goals:**
- Display each agent's current working directory (cwd) in the `agent list` table output

**Non-goals:**
- Changing the `--json` output format (it already includes `projectPath`)
- Adding filtering/sorting by cwd
- Modifying how `projectPath` is collected by adapters

## User Stories & Use Cases

- 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.
- As a developer with agents in nested directories, I want the path displayed in a compact, readable format (shortened with `~` for home directory).

## Success Criteria

- [ ] `agent list` table includes a "CWD" column showing the agent's `projectPath`
- [ ] Long paths are shortened (home directory replaced with `~`)
- [ ] Column is positioned after "Agent" name for quick visual association
- [ ] Existing tests updated to cover the new column
- [ ] No regressions in existing agent list functionality

## Constraints & Assumptions

- The `projectPath` field is already available in `AgentInfo` — no adapter changes needed
- Path shortening uses `os.homedir()` for `~` substitution
- Column styling uses `chalk.dim` to keep focus on agent name and status

## Questions & Open Items

- None — straightforward display addition using existing data.
29 changes: 29 additions & 0 deletions docs/ai/testing/feature-agent-list-cwd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
phase: testing
title: Display CWD in Agent List — Testing
description: Test strategy for CWD column feature
---

# Display CWD in Agent List — Testing

## Test Coverage Goals

- 100% coverage of `formatCwd()` helper
- Verify table output includes CWD column

## Unit Tests

### `formatCwd()` helper
- [ ] Returns `~`-prefixed path when projectPath starts with home directory
- [ ] Returns original path when projectPath doesn't start with home directory
- [ ] Returns empty string for empty/undefined input

### Table rendering
- [ ] Table headers include "CWD" column
- [ ] Table rows include formatted projectPath values
- [ ] CWD column appears in correct position (after Agent)

## Integration Tests

- [ ] `agent list` with agents shows CWD column in output
- [ ] `agent list --json` still returns full `projectPath` (no regression)
20 changes: 10 additions & 10 deletions packages/cli/src/__tests__/commands/agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ describe('agent command', () => {

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

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

const tableArg: any = (ui.table as any).mock.calls[0][0];
expect(tableArg.rows[0][1]).toBe('Claude Code');
expect(tableArg.rows[1][1]).toBe('Codex');
expect(tableArg.rows[2][1]).toBe('Gemini CLI');
expect(tableArg.rows[3][1]).toBe('Other');
expect(tableArg.rows[0][2]).toBe('Claude Code');
expect(tableArg.rows[1][2]).toBe('Codex');
expect(tableArg.rows[2][2]).toBe('Gemini CLI');
expect(tableArg.rows[3][2]).toBe('Other');
});

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

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

it('shows available agents when open target is not found', async () => {
Expand Down
14 changes: 13 additions & 1 deletion packages/cli/src/commands/agent.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os from 'os';
import { Command } from 'commander';
import chalk from 'chalk';
import inquirer from 'inquirer';
Expand Down Expand Up @@ -50,6 +51,15 @@ function formatType(type: AgentType): string {
return TYPE_LABELS[type] ?? type;
}

function formatCwd(projectPath?: string): string {
if (!projectPath) return '';
const home = os.homedir();
if (projectPath.startsWith(home)) {
return '~' + projectPath.slice(home.length);
}
return projectPath;
}

function formatWorkOn(summary?: string): string {
const firstLine = (summary ?? '').split(/\r?\n/, 1)[0] || '';
return firstLine || 'No active task';
Expand Down Expand Up @@ -89,18 +99,20 @@ export function registerAgentCommand(program: Command): void {

const rows = agents.map(agent => [
agent.name,
formatCwd(agent.projectPath),
formatType(agent.type),
formatStatus(agent.status),
formatWorkOn(agent.summary),
formatRelativeTime(agent.lastActive)
]);

ui.table({
headers: ['Agent', 'Type', 'Status', 'Working On', 'Active'],
headers: ['Agent', 'CWD', 'Type', 'Status', 'Working On', 'Active'],
rows: rows,
columnStyles: [
(text) => chalk.cyan(text),
(text) => chalk.dim(text),
(text) => chalk.dim(text),
(text) => {
if (text.includes(STATUS_DISPLAY[AgentStatus.RUNNING].label)) return chalk.green(text);
if (text.includes(STATUS_DISPLAY[AgentStatus.WAITING].label)) return chalk.yellow(text);
Expand Down
Loading