agentchrome 1.62.0

A CLI tool for browser automation via the Chrome DevTools Protocol
Documentation
# Tasks: Element Finding

**Issues**: #11
**Date**: 2026-02-12
**Status**: Planning
**Author**: Claude (writing-specs)

---

## Summary

| Phase | Tasks | Status |
|-------|-------|--------|
| Setup | 2 | [ ] |
| Backend | 3 | [ ] |
| Integration | 1 | [ ] |
| Testing | 2 | [ ] |
| **Total** | **8** | |

---

## Task Format

Each task follows this structure:

```
### T[NNN]: [Task Title]

**File(s)**: `{layer}/path/to/file`
**Type**: Create | Modify | Delete
**Depends**: T[NNN], T[NNN] (or None)
**Acceptance**:
- [ ] [Verifiable criterion 1]
- [ ] [Verifiable criterion 2]

**Notes**: [Optional implementation hints]
```

Map `{layer}/` placeholders to actual project paths using `structure.md`.

---

## Phase 1: Setup

### T001: Add `PageFindArgs` and `Find` variant to CLI definitions

**File(s)**: `src/cli/mod.rs`
**Type**: Modify
**Depends**: None
**Acceptance**:
- [ ] `PageFindArgs` struct defined with: `query: Option<String>`, `--selector`, `--role`, `--exact`, `--limit` (default 10)
- [ ] `PageCommand::Find(PageFindArgs)` variant added to `PageCommand` enum
- [ ] `--selector` documented as alternative to text query
- [ ] `agentchrome page find --help` displays correct usage
- [ ] Compiles without errors or clippy warnings

**Notes**: Follow the same patterns as `PageTextArgs` and `PageSnapshotArgs`. The `query` argument is positional and optional (required only when `--selector` is not provided). Validation of "at least one of query/selector" happens at runtime in the executor, not in clap.

### T002: Define output types for find results

**File(s)**: `src/page.rs`
**Type**: Modify
**Depends**: None
**Acceptance**:
- [ ] `FindMatch` struct defined with fields: `uid: Option<String>`, `role: String`, `name: String`, `bounding_box: Option<BoundingBox>`
- [ ] `BoundingBox` struct defined with fields: `x: f64`, `y: f64`, `width: f64`, `height: f64`
- [ ] Both structs derive `Debug`, `Serialize`
- [ ] `bounding_box` serializes as `boundingBox` (camelCase) via `#[serde(rename)]`
- [ ] `bounding_box` is skipped when `None` via `#[serde(skip_serializing_if)]`
- [ ] Unit tests for serialization (with and without bounding box, with and without uid)

---

## Phase 2: Backend Implementation

### T003: Implement accessibility tree search logic

**File(s)**: `src/snapshot.rs`
**Type**: Modify
**Depends**: T001
**Acceptance**:
- [ ] New public function `search_tree(root, query, role_filter, exact, limit) -> Vec<SearchHit>` added
- [ ] `SearchHit` struct with: `uid: Option<String>`, `role: String`, `name: String`, `backend_dom_node_id: Option<i64>`
- [ ] Substring match: case-insensitive contains on node name (when `exact` is false)
- [ ] Exact match: case-sensitive equality on node name (when `exact` is true)
- [ ] Role filter: only includes nodes matching the specified role string
- [ ] Results in depth-first (document) order
- [ ] Stops collecting after `limit` matches
- [ ] Unit tests for: substring match, exact match, role filter, combined role+text, limit, no matches, empty tree

**Notes**: Walk the `SnapshotNode` tree depth-first. Also pass the `uid_map` (from `BuildResult`) to resolve `backend_dom_node_id` for each matched node. Alternatively, the search can return uid strings and the caller resolves backend IDs from the map.

### T004: Implement CSS selector search via CDP DOM methods

**File(s)**: `src/page.rs`
**Type**: Modify
**Depends**: T002
**Acceptance**:
- [ ] Helper function `find_by_selector(managed, selector, limit)` that:
  1. Enables DOM domain
  2. Calls `DOM.getDocument` to get root `nodeId`
  3. Calls `DOM.querySelectorAll` with the CSS selector
  4. For each returned `nodeId` (up to `limit`):
     - Calls `DOM.describeNode` to get `backendDOMNodeId`
     - Calls `Accessibility.getPartialAXTree` with `backendDOMNodeId` to get role/name
     - Calls `DOM.getBoxModel` for bounding box (returns `None` on error)
  5. Returns `Vec<FindMatch>`
- [ ] Invalid CSS selector results in a CDP protocol error which propagates as `AppError`
- [ ] Invisible elements (getBoxModel fails) get `None` bounding box

### T005: Implement bounding box resolution for accessibility search path

**File(s)**: `src/page.rs`
**Type**: Modify
**Depends**: T003
**Acceptance**:
- [ ] Helper function `resolve_bounding_box(managed, backend_dom_node_id) -> Option<BoundingBox>` that:
  1. Enables DOM domain
  2. Calls `DOM.describeNode` with `backendNodeId` param to get `nodeId`
  3. Calls `DOM.getBoxModel` with that `nodeId`
  4. Extracts content quad from box model → computes x, y, width, height
  5. Returns `None` if any step fails (element invisible, removed, etc.)
- [ ] Does not error on failure — returns `None` gracefully

---

## Phase 3: Frontend Implementation

### T007: [Client-side model]

**File(s)**: `{presentation-layer}/models/...`
**Type**: Create
**Depends**: T002
**Acceptance**:
- [ ] Model matches API response schema
- [ ] Serialization/deserialization works
- [ ] Immutable with update method (if applicable)
- [ ] Unit tests for serialization

### T008: [Client-side service / API client]

**File(s)**: `{presentation-layer}/services/...`
**Type**: Create
**Depends**: T007
**Acceptance**:
- [ ] All API calls implemented
- [ ] Error handling with typed exceptions
- [ ] Uses project's HTTP client pattern
- [ ] Unit tests pass

### T009: [State management]

**File(s)**: `{presentation-layer}/state/...` or `{presentation-layer}/providers/...`
**Type**: Create
**Depends**: T008
**Acceptance**:
- [ ] State class defined (immutable if applicable)
- [ ] Loading/error states handled
- [ ] State transitions match design spec
- [ ] Unit tests for state transitions

### T010: [UI components]

**File(s)**: `{presentation-layer}/components/...` or `{presentation-layer}/widgets/...`
**Type**: Create
**Depends**: T009
**Acceptance**:
- [ ] Components match design specs
- [ ] Uses project's design tokens (no hardcoded values)
- [ ] Loading/error/empty states
- [ ] Component tests pass

### T011: [Screen / Page]

**File(s)**: `{presentation-layer}/screens/...` or `{presentation-layer}/pages/...`
**Type**: Create
**Depends**: T010
**Acceptance**:
- [ ] Screen layout matches design
- [ ] State management integration working
- [ ] Navigation implemented

---

## Phase 3: Integration

### T006: Wire up `execute_find()` dispatcher and output

**File(s)**: `src/page.rs`, `src/main.rs` (if needed)
**Type**: Modify
**Depends**: T003, T004, T005
**Acceptance**:
- [ ] `execute_find(global, args)` function implemented:
  1. Validates that at least one of `query` or `selector` is provided (returns `AppError` if neither)
  2. Sets up CDP session via existing `setup_session()`
  3. If `--selector` provided: calls CSS selector path (T004) — also triggers full snapshot for UID assignment
  4. If text query provided: captures snapshot via `Accessibility.getFullAXTree`, builds tree, searches (T003), resolves bounding boxes (T005)
  5. Persists snapshot state (uid_map) to `~/.agentchrome/snapshot.json`
  6. Outputs results as JSON array (default) or plain text (`--plain`)
- [ ] `PageCommand::Find` arm added to `execute_page()` dispatcher
- [ ] JSON output: `Vec<FindMatch>` serialized directly (compact or pretty based on flags)
- [ ] Plain text output: `[uid] role "name" (x,y widthxheight)` format, one per line
- [ ] Empty results → empty JSON array `[]`, exit code 0
- [ ] `--limit` respected in both search paths

---

## Phase 4: Testing

### T007: Create BDD feature file

**File(s)**: `tests/features/page_find.feature`
**Type**: Create
**Depends**: T006
**Acceptance**:
- [ ] All 12 acceptance criteria from requirements.md are Gherkin scenarios
- [ ] Uses Given/When/Then format
- [ ] Background includes "Chrome is connected with a page loaded"
- [ ] Includes error scenarios (no matches, invalid selector)
- [ ] Feature file is valid Gherkin syntax

### T008: Add unit tests for search and output logic

**File(s)**: `src/snapshot.rs`, `src/page.rs`
**Type**: Modify
**Depends**: T003, T006
**Acceptance**:
- [ ] `snapshot.rs` tests: `search_tree` with various inputs (substring, exact, role, combined, limit, empty)
- [ ] `page.rs` tests: `FindMatch` and `BoundingBox` serialization, plain text formatting
- [ ] All tests pass with `cargo test`

---

## Dependency Graph

```
T001 (CLI args) ──┐
                   ├──▶ T003 (AX search) ──┐
T002 (types)   ──┤                          ├──▶ T006 (wire up) ──▶ T007 (BDD)
                   ├──▶ T004 (CSS search) ──┤                        T008 (unit tests)
                   └──▶ T005 (bounding box)─┘
```

---

## Change History

| Issue | Date | Summary |
|-------|------|---------|
| #11 | 2026-02-12 | Initial feature spec |

## Validation Checklist

- [x] Each task has single responsibility
- [x] Dependencies are correctly mapped
- [x] Tasks can be completed independently (given dependencies)
- [x] Acceptance criteria are verifiable
- [x] File paths reference actual project structure
- [x] Test tasks are included
- [x] No circular dependencies
- [x] Tasks are in logical execution order