diff --git a/docs/ai/design/feature-agent-list-cwd.md b/docs/ai/design/feature-agent-list-cwd.md new file mode 100644 index 00000000..7664625e --- /dev/null +++ b/docs/ai/design/feature-agent-list-cwd.md @@ -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 diff --git a/docs/ai/implementation/feature-agent-list-cwd.md b/docs/ai/implementation/feature-agent-list-cwd.md new file mode 100644 index 00000000..a61ea816 --- /dev/null +++ b/docs/ai/implementation/feature-agent-list-cwd.md @@ -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)` diff --git a/docs/ai/planning/feature-agent-list-cwd.md b/docs/ai/planning/feature-agent-list-cwd.md new file mode 100644 index 00000000..f88d372f --- /dev/null +++ b/docs/ai/planning/feature-agent-list-cwd.md @@ -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 | diff --git a/docs/ai/requirements/feature-agent-list-cwd.md b/docs/ai/requirements/feature-agent-list-cwd.md new file mode 100644 index 00000000..b8195447 --- /dev/null +++ b/docs/ai/requirements/feature-agent-list-cwd.md @@ -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. diff --git a/docs/ai/testing/feature-agent-list-cwd.md b/docs/ai/testing/feature-agent-list-cwd.md new file mode 100644 index 00000000..2125cd5a --- /dev/null +++ b/docs/ai/testing/feature-agent-list-cwd.md @@ -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) diff --git a/packages/cli/src/__tests__/commands/agent.test.ts b/packages/cli/src/__tests__/commands/agent.test.ts index b0829d81..8bbb2cb9 100644 --- a/packages/cli/src/__tests__/commands/agent.test.ts +++ b/packages/cli/src/__tests__/commands/agent.test.ts @@ -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.'); }); @@ -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 () => { @@ -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 () => { diff --git a/packages/cli/src/commands/agent.ts b/packages/cli/src/commands/agent.ts index cd821655..dc40c878 100644 --- a/packages/cli/src/commands/agent.ts +++ b/packages/cli/src/commands/agent.ts @@ -1,3 +1,4 @@ +import os from 'os'; import { Command } from 'commander'; import chalk from 'chalk'; import inquirer from 'inquirer'; @@ -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'; @@ -89,6 +99,7 @@ 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), @@ -96,11 +107,12 @@ export function registerAgentCommand(program: Command): void { ]); 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);