# Task ID: 22

**Title:** Implement Numbers CLI Commands (Read Operations)

**Status:** done

**Dependencies:** 8, 11, 19

**Priority:** high

**Description:** Create the numbers command group with list, show, and search commands.

**Details:**

Create `src/teams_phone/cli/numbers.py`:
```python
numbers_app = typer.Typer(name="numbers", help="Phone number management")

@numbers_app.command("list")
def list_numbers(
    ctx: typer.Context,
    status: Optional[str] = typer.Option(None, "--status", help="assigned/unassigned"),
    number_type: Optional[str] = typer.Option(None, "--type", help="callingPlan/directRouting/operatorConnect"),
    city: Optional[str] = typer.Option(None, "--city"),
    limit: int = typer.Option(100, "--limit"),
):
    """List phone numbers in inventory."""
    # Display: telephoneNumber, status, type, assignedTo, city
    ...

@numbers_app.command()
def show(ctx: typer.Context, number: str = typer.Argument(..., help="Phone number")):
    """Show detailed information for a phone number."""
    ...

@numbers_app.command()
def search(ctx: typer.Context, pattern: str = typer.Argument(..., help="Search pattern")):
    """Search numbers by pattern (e.g., 206*, *1234)."""
    ...
```

**Test Strategy:**

CLI tests with mocked NumberService: list with filters, show displays all fields, search with patterns, JSON output validation.

---

## Implementation Notes

### Session: 2026-01-29 - Agent: claude-opus-4-5-20251101

**Status:** Implemented

#### What Was Done
- Created `src/teams_phone/cli/numbers.py` with `numbers_app` Typer instance
- Implemented `_run_async()` helper following the pattern from `users.py`
- Implemented helper functions for display formatting:
  - `_format_status()` - Human-readable assignment status
  - `_format_number_type()` - Human-readable number type
  - `_format_activation_state()` - Human-readable activation state
  - `_format_capabilities()` - Shortened capability names
  - `_number_to_list_dict()` - Table display conversion
  - `_number_to_json_dict()` - JSON output conversion
- Implemented `list` command with filters: `--status`, `--type`, `--city`, `--limit`, `--all`
- Implemented `show` command with detailed sections: Number Details, Assignment, Activation, Source, Capabilities, Location, Network
- Implemented `search` command with pattern argument and `--limit` option
- Updated `src/teams_phone/cli/main.py` to import `numbers_app` from new module (replaced placeholder)
- Created `tests/unit/test_cli_numbers.py` with 29 unit tests covering all commands

#### Key Decisions Made
- **Filter value format**: Decided to use exact API enum values (e.g., `unassigned`, `userAssigned`, `callingPlan`, `directRouting`) rather than CLI-friendly aliases. This matches Typer's native enum parsing and avoids conversion complexity.
- **Default limit**: Set to 100 for `list` command (vs 50 for users) to match expected inventory sizes
- **City filter note**: Added help text noting client-side filtering which may impact performance
- **Search pattern note**: Added docstring explaining client-side search for large inventories

#### Patterns Discovered
- Use `model_validate()` with dict when creating Pydantic models in tests that have field aliases (see `tests/unit/test_cli_numbers.py:40-52`)
- Phone numbers in output may have ANSI escape codes from Rich even with `--no-color` flag - test for digit presence rather than exact string match

#### Open Items for Next Agent
- [ ] Task 23: Implement `numbers assign` command (write operation)
- [ ] Task 24: Implement `numbers unassign` command (write operation)

#### Gotchas and Warnings
- The `NumberAssignment` model uses camelCase aliases (`telephoneNumber`, `numberType`, etc.) with `populate_by_name=True`. When constructing test fixtures, use `model_validate()` with a dict using the alias names.
- Rich table output may include ANSI codes that affect string matching in tests

---
