# snapshot-query Project Instructions

## Project Overview

> **⚠️ Important:** You must read `cursor-agents-md` skills every time before write or update this `AGENTS.md`.

snapshot-query is a Python tool for efficiently querying and analyzing snapshot log files generated by cursor-ide-browser. The tool parses YAML-format accessibility snapshots and provides various query methods to find elements by name, role, ref, or text content.

## Code Style

- Use Python 3.8+ syntax and type hints where appropriate
- Follow PEP 8 style guidelines
- Use descriptive variable and function names (snake_case)
- Maximum line length: 100 characters (soft limit)
- Use docstrings for all public classes and methods (Google style)
- Prefer f-strings for string formatting
- Use pathlib.Path for file path operations
- Import order: standard library, third-party, local imports

## Architecture

- **Core Logic**: `snapshot_query/query.py` contains the `SnapshotQuery` class with all query methods
- **Data Models**: `snapshot_query/models.py` contains Pydantic models for data validation (`SnapshotElement`, `SnapshotData`)
- **CLI Interface**: `snapshot_query/cli.py` handles command-line argument parsing and execution
- **Package Entry**: `snapshot_query/__main__.py` enables `python -m snapshot_query` usage
- **MCP Server**: `snapshot_query/mcp_server.py` provides MCP (Model Context Protocol) server interface
- **Legacy Support**: `query_snapshot.py` at root provides backward compatibility for direct script execution
- Keep query logic separate from CLI presentation
- Use recursive functions for tree traversal (YAML structure is tree-like)
- Return structured data (lists, dicts) from query methods, let CLI handle formatting

## File Structure

- `snapshot_query/` - Main package directory
  - `__init__.py` - Package initialization, exports `SnapshotQuery`, `SnapshotElement`, `SnapshotData`
  - `__main__.py` - Module entry point for `python -m snapshot_query`
  - `models.py` - Pydantic data models for snapshot elements (`SnapshotElement`, `SnapshotData`)
  - `cli.py` - Command-line interface implementation
  - `query.py` - Core `SnapshotQuery` class with all query methods
  - `mcp_server.py` - MCP (Model Context Protocol) server implementation
- `query_snapshot.py` - Legacy standalone script (maintains backward compatibility)
- `pyproject.toml` - Project configuration (build system, dependencies, metadata)
- `README.md` - Project overview and quick start
- `USAGE.md` - Detailed usage guide
- `SNAPSHOT_QUERY_GUIDE.md` - Comprehensive query guide with examples
- `MCP_README.md` - MCP server interface documentation
- `cursor-ide-browser.md` - Documentation for cursor-ide-browser tool

## Dependencies

- **PyYAML** (>=6.0) - For parsing YAML snapshot files
- **pydantic** (>=2.0.0) - For data validation and type checking
- **mcp** (>=1.0.0) - MCP SDK for server implementation (optional, for MCP interface)
- **Python** (>=3.8) - Minimum Python version
- **Hatchling** - Build backend (for packaging)

## Installation and Usage

### Installation

**Using uvx (recommended, no installation needed):**
```bash
# From local project
uvx --from . snapshot-query <file_path> <command> [args]

# From PyPI (after publishing)
uvx snapshot-query <file_path> <command> [args]
```

**Using pip:**
```bash
pip install snapshot-query
```

### Running the Tool

**Method 1: Using uvx (recommended)**
```bash
uvx --from . snapshot-query snapshot.log find-name "搜索"
```

**Method 2: Using Python module**
```bash
python -m snapshot_query snapshot.log find-name "搜索"
```

**Method 3: Using legacy script**
```bash
python query_snapshot.py snapshot.log find-name "搜索"
```

## Available Commands

### find-name
Find elements by name (fuzzy match)

```bash
uvx --from . snapshot-query snapshot.log find-name "搜索"
```

### find-name-exact
Find elements by name (exact match)

```bash
uvx --from . snapshot-query snapshot.log find-name-exact "搜索"
```

### find-name-bm25
Find elements by name using BM25 algorithm (ranked by relevance)

```bash
# Return all results sorted by relevance
uvx --from . snapshot-query snapshot.log find-name-bm25 "搜索"

# Return top 10 most relevant results
uvx --from . snapshot-query snapshot.log find-name-bm25 "搜索" 10
```

**BM25 Features:**
- Uses BM25 ranking algorithm for intelligent fuzzy matching
- Results are sorted by relevance score
- Better than simple substring matching for finding semantically similar elements
- Supports Chinese and English text
- Can limit results with top_k parameter

### find-role
Find elements by role (button, link, textbox, etc.)

```bash
uvx --from . snapshot-query snapshot.log find-role button
```

### find-ref
Find element by reference identifier

```bash
uvx --from . snapshot-query snapshot.log find-ref ref-b9k8zlttiah
```

### find-text
Find elements containing specified text

```bash
uvx --from . snapshot-query snapshot.log find-text "登录"
```

### find-grep
Find elements using regular expressions (grep syntax)

```bash
# Search by name field (default)
uvx --from . snapshot-query snapshot.log find-grep "^搜索$" name

# Search with pattern (OR operator)
uvx --from . snapshot-query snapshot.log find-grep "Google|搜索" name

# Search by role field
uvx --from . snapshot-query snapshot.log find-grep "button|link" role

# Search by ref field
uvx --from . snapshot-query snapshot.log find-grep "ref-.*abc" ref
```

**Grep Syntax Examples:**
- `^搜索$` - Exact match (starts and ends with "搜索")
- `搜索.*按钮` - Contains "搜索" followed by "按钮"
- `Google|搜索` - Contains "Google" OR "搜索"
- `[0-9]+` - Contains one or more digits
- `ref-.*abc` - Ref starts with "ref-" and contains "abc"

### find-selector
Find elements using CSS/jQuery selector syntax

```bash
# Tag selector (role)
uvx --from . snapshot-query snapshot.log find-selector "button"

# ID selector (ref)
uvx --from . snapshot-query snapshot.log find-selector "#ref-rv7cgg62t9g"

# Attribute selector
uvx --from . snapshot-query snapshot.log find-selector "[name='搜索']"

# Combined selector
uvx --from . snapshot-query snapshot.log find-selector "button[name='搜索']"

# Contains match
uvx --from . snapshot-query snapshot.log find-selector "[name*='Google']"
```

**Selector Examples:**
- `button` - All buttons
- `button[name="搜索"]` - Button with exact name
- `button[name*="搜索"]` - Buttons containing "搜索"
- `#ref-xxx` - Element by ref
- `[name^="搜索"]` - Elements starting with "搜索"
- `generic button` - Buttons within generic containers

### interactive
List all interactive elements

```bash
uvx --from . snapshot-query snapshot.log interactive
```

### count
Count elements by type

```bash
uvx --from . snapshot-query snapshot.log count
```

### path
Show element path in tree

```bash
uvx --from . snapshot-query snapshot.log path ref-b9k8zlttiah
```

### all-refs
List all reference identifiers

```bash
uvx --from . snapshot-query snapshot.log all-refs
```

## Common Query Scenarios

### Scenario 1: Find button ref
**Problem**: Need to find the ref of a "搜索" button to click it

**Solution**:
```bash
uvx --from . snapshot-query snapshot.log find-name "搜索"
```

**Output example**:
```
找到 1 个匹配的元素:
role: button
ref: ref-b9k8zlttiah
name: 搜索
```

**Usage**:
```javascript
browser_click(
  element="搜索按钮",
  ref="ref-b9k8zlttiah"
)
```

### Scenario 2: Find all clickable elements
**Problem**: Want to see all interactive elements on the page

**Solution**:
```bash
uvx --from . snapshot-query snapshot.log interactive
```

### Scenario 3: Find input boxes
**Problem**: Need to find the ref of a username input box

**Solution**:
```bash
# Method 1: Find all textboxes
uvx --from . snapshot-query snapshot.log find-role textbox

# Method 2: Find by name
uvx --from . snapshot-query snapshot.log find-name "用户名"
```

### Scenario 4: Understand page structure
**Problem**: Want to know where an element is located in the page

**Solution**:
```bash
uvx --from . snapshot-query snapshot.log path ref-b9k8zlttiah
```

## YAML Snapshot Format

Snapshot files contain accessibility tree structures with:

### Basic Structure
```yaml
- role: {element role}
  ref: {unique reference identifier}
  name: {optional element name/text content}
  children:
    - {child elements list}
```

### Field Descriptions

1. **`role`** (required)
   - Element role type
   - Common values: `generic`, `link`, `button`, `textbox`, `img`, `list`, `listitem`, `heading`, `pagedescription`
   - Follows WAI-ARIA role specifications

2. **`ref`** (required)
   - Unique reference identifier
   - Format: `ref-{random string}`
   - Examples: `ref-zketxgetcys`, `ref-b8rs5tdhk3e`
   - **Important**: This `ref` value is used for subsequent element interaction operations (e.g., `browser_click`, `browser_type`)

3. **`name`** (optional)
   - Element name or text content
   - Usually what screen readers would read
   - May contain: button text, link text, input labels, image alt text, etc.

4. **`children`** (optional)
   - List of child elements
   - Present if element contains child elements
   - Child elements follow the same structure

### File Location
Snapshot log files are saved in:
```
C:\Users\{username}\.cursor\browser-logs\
```

### File Naming
Format: `snapshot-{ISO 8601 timestamp}.log`

Examples:
- `snapshot-2026-01-09T15-00-42-849Z.log`
- `snapshot-2026-01-09T16-30-15-123Z.log`

## MCP Server Interface

snapshot-query provides MCP (Model Context Protocol) server interface, allowing AI assistants to query snapshot files through standardized protocol.

### Installation
Ensure MCP SDK is installed:
```bash
pip install mcp
```

Or use official SDK:
```bash
pip install @modelcontextprotocol/server-sdk-python
```

### Starting MCP Server

**Method 1: Using command entry point**
```bash
snapshot-query-mcp
```

**Method 2: Using Python module**
```bash
python -m snapshot_query.mcp_server
```

**Method 3: Direct script execution**
```bash
python snapshot_query/mcp_server.py
```

### Client Configuration

Configure in MCP client config file (usually `~/.cursor/mcp.json` or similar):

```json
{
  "mcpServers": {
    "snapshot-query": {
      "command": "snapshot-query-mcp",
      "args": []
    }
  }
}
```

Or using Python module:
```json
{
  "mcpServers": {
    "snapshot-query": {
      "command": "python",
      "args": ["-m", "snapshot_query.mcp_server"]
    }
  }
}
```

### Available MCP Tools

1. **find_by_name** - Find elements by name (supports fuzzy and exact matching)
   - Parameters: `file_path` (string, required), `name` (string, required), `exact` (boolean, optional, default: false)

2. **find_by_role** - Find elements by role type
   - Parameters: `file_path` (string, required), `role` (string, required)

3. **find_by_ref** - Find element by reference identifier
   - Parameters: `file_path` (string, required), `ref` (string, required)

4. **find_by_text** - Find elements containing specified text
   - Parameters: `file_path` (string, required), `text` (string, required), `case_sensitive` (boolean, optional, default: false)

5. **find_interactive_elements** - Find all interactive elements
   - Parameters: `file_path` (string, required)

6. **count_elements** - Count elements by type
   - Parameters: `file_path` (string, required)

7. **get_element_path** - Get element path in tree
   - Parameters: `file_path` (string, required), `ref` (string, required)

8. **extract_all_refs** - Extract all reference identifiers
   - Parameters: `file_path` (string, required)

### MCP Usage Scenarios

**In AI Assistant:**
After configuring MCP server, AI assistant can directly call these tools:
```
User: Find all buttons in snapshot.log
AI: [Calls find_by_role tool, file_path="snapshot.log", role="button"]
```

**With cursor-ide-browser:**
1. Use `browser_snapshot()` to get page snapshot
2. Snapshot saved as log file
3. Query snapshot file through MCP interface to find element ref
4. Use ref for browser interaction operations

## cursor-ide-browser Integration

### Overview

cursor-ide-browser is an MCP-based browser automation tool that allows AI assistants to interact with web pages directly in Cursor IDE. It uses accessibility snapshots for precise element location and operation.

### Architecture

```
AI Assistant (Auto)
  ↓ (MCP Protocol)
MCP Server (cursor-ide-browser)
  ↓ (Browser API)
Actual Browser Instance (embedded in Cursor IDE)
```

### Core Mechanisms

1. **Accessibility Snapshot**
   - Uses browser Accessibility API
   - Gets DOM accessibility tree
   - Contains semantic information of all interactive elements
   - More accurate than screenshots, better for programmatic operations

2. **Element Reference System**
   - Each element has two identifiers:
     - `element`: Human-readable description (for permission confirmation)
     - `ref`: Precise technical reference (obtained from snapshot)
   - `ref` is a stable element identifier, can accurately locate even when page changes dynamically

3. **Permission Verification**
   - Each interaction requires `element` description
   - Used to show user what operation will be performed
   - Ensures transparency and security

### Standard Workflow

1. **Navigate to page**
   ```javascript
   browser_navigate(url="https://example.com")
   ```

2. **Get page snapshot**
   ```javascript
   browser_snapshot()
   ```
   This step is required to get precise element references.

3. **Interact with elements**
   Use `ref` from snapshot for interaction:
   ```javascript
   browser_click(element="按钮", ref="ref-from-snapshot")
   browser_type(element="输入框", ref="ref-from-snapshot", text="文本")
   ```

4. **Wait for content to load**
   ```javascript
   browser_wait_for(text="某个文本")
   ```

5. **Get information**
   ```javascript
   browser_take_screenshot()
   browser_console_messages()
   browser_network_requests()
   ```

### Complete Example

```javascript
// 1. Open webpage
browser_navigate(url="https://example.com/login", position="side")

// 2. Get page snapshot
const snapshot = browser_snapshot()

// 3. Input username
browser_type(
  element="用户名输入框",
  ref="input#username",  // From snapshot
  text="myusername"
)

// 4. Input password
browser_type(
  element="密码输入框",
  ref="input#password",  // From snapshot
  text="mypassword"
)

// 5. Click login button
browser_click(
  element="登录按钮",
  ref="button#login"  // From snapshot
)

// 6. Wait for login to complete
browser_wait_for(text="欢迎")

// 7. Take screenshot to confirm
browser_take_screenshot(filename="login-success.png")
```

### Integration with snapshot-query

**Typical workflow:**
```bash
# 1. Get page snapshot (in browser)
browser_snapshot()

# 2. Find target element
uvx --from . snapshot-query snapshot.log find-name "登录"

# 3. Get ref: ref-xxxxx

# 4. Use ref for operation
browser_click(element="登录按钮", ref="ref-xxxxx")
```

## Query Patterns

- Use recursive functions for tree traversal
- Collect results in lists during traversal
- Return early when searching for single elements (like `find_by_ref`)
- Handle missing fields gracefully (use `.get()` with defaults)
- Case-insensitive text matching by default (convert to lowercase)

## Grep/Regex Support

The tool supports regular expressions (grep syntax) for advanced pattern matching:

### Features
- **Full regex support**: Uses Python's `re` module, supports all standard regex features
- **Multiple fields**: Can search in `name`, `role`, or `ref` fields
- **Case sensitivity**: Optional case-sensitive matching
- **Error handling**: Validates regex patterns and provides clear error messages

### Usage Examples

**In Python:**
```python
from snapshot_query import SnapshotQuery

query = SnapshotQuery("snapshot.log")

# Exact match
results = query.find_by_regex("^搜索$", field="name")

# Pattern matching (OR)
results = query.find_by_regex("Google|搜索", field="name")

# Search by role
results = query.find_by_regex("button|link", field="role")

# Case-sensitive search
results = query.find_by_regex("Google", field="name", case_sensitive=True)
```

**Via CLI:**
```bash
# Exact match
snapshot-query snapshot.log find-grep "^搜索$" name

# Pattern matching
snapshot-query snapshot.log find-grep "Google|搜索" name

# Search by role
snapshot-query snapshot.log find-grep "button|link" role
```

**Common Regex Patterns:**
- `^搜索$` - Exact match
- `搜索.*按钮` - Contains "搜索" followed by "按钮"
- `Google|搜索` - Contains "Google" OR "搜索"
- `[0-9]+` - Contains one or more digits
- `ref-.*abc` - Ref starts with "ref-" and contains "abc"

## CSS/jQuery Selector Support

The tool supports CSS/jQuery selector syntax for intuitive element selection:

### Supported Selectors

1. **Tag Selector** (maps to role)
   - `button` - Find all buttons
   - `link` - Find all links
   - `textbox` - Find all textboxes

2. **ID Selector** (maps to ref)
   - `#ref-xxx` - Find element by ref
   - `#ref-rv7cgg62t9g` - Find specific element

3. **Attribute Selector**
   - `[name="搜索"]` - Exact match
   - `[name*="搜索"]` - Contains (wildcard)
   - `[name^="搜索"]` - Starts with
   - `[name$="搜索"]` - Ends with
   - `[role="button"]` - Match by role
   - `[ref="ref-xxx"]` - Match by ref

4. **Combined Selector**
   - `button[name="搜索"]` - Button with name "搜索"
   - `link[name*="Google"]` - Links containing "Google"

5. **Descendant Selector**
   - `parent child` - Find child elements within parent
   - `generic button` - Find buttons within generic containers

6. **Direct Child Selector**
   - `parent > child` - Find direct child elements only

### Usage Examples

**In Python:**
```python
from snapshot_query import SnapshotQuery

query = SnapshotQuery("snapshot.log")

# Tag selector
buttons = query.find_by_selector("button")

# ID selector
element = query.find_by_selector("#ref-rv7cgg62t9g")

# Attribute selector
results = query.find_by_selector("[name='搜索']")

# Combined selector
results = query.find_by_selector("button[name='搜索']")

# Descendant selector
results = query.find_by_selector("generic button")
```

**Via CLI:**
```bash
# Tag selector
snapshot-query snapshot.log find-selector "button"

# ID selector
snapshot-query snapshot.log find-selector "#ref-rv7cgg62t9g"

# Attribute selector
snapshot-query snapshot.log find-selector "[name='搜索']"

# Combined selector
snapshot-query snapshot.log find-selector "button[name='搜索']"

# Contains match
snapshot-query snapshot.log find-selector "[name*='Google']"
```

**Common Selector Patterns:**
- `button` - All buttons
- `button[name="搜索"]` - Button with exact name
- `button[name*="搜索"]` - Buttons containing "搜索"
- `#ref-xxx` - Element by ref
- `[name^="搜索"]` - Elements starting with "搜索"
- `generic button` - Buttons within generic containers

## BM25 Fuzzy Matching

BM25 (Best Matching 25) is a ranking function used to estimate the relevance of documents to a given search query. The implementation includes:

### Features
- **Intelligent ranking**: Results sorted by relevance score, not just substring matches
- **Multi-language support**: Handles both Chinese characters and English words
- **Tokenization**: Smart tokenization for Chinese (character-level) and English (word-level)
- **Configurable parameters**: k1 (term frequency saturation) and b (length normalization) can be adjusted
- **Lazy indexing**: BM25 index is built only when needed (on first BM25 query)

### Usage

**In Python:**
```python
from snapshot_query import SnapshotQuery

query = SnapshotQuery("snapshot.log")

# BM25 search (returns all results sorted by relevance)
results = query.find_by_name_bm25("搜索")

# BM25 search with top_k limit
top_results = query.find_by_name_bm25("搜索", top_k=10)
```

**Via CLI:**
```bash
# All results
snapshot-query snapshot.log find-name-bm25 "搜索"

# Top 10 results
snapshot-query snapshot.log find-name-bm25 "搜索" 10
```

**Via MCP:**
```json
{
  "name": "find_by_name_bm25",
  "arguments": {
    "file_path": "snapshot.log",
    "name": "搜索",
    "top_k": 10
  }
}
```

### When to Use BM25

- **Better relevance**: When you want results ranked by relevance, not just matches
- **Partial matches**: When search terms might not exactly match element names
- **Semantic similarity**: When looking for conceptually similar elements
- **Large result sets**: When you want the most relevant results from many matches

### Performance

- First BM25 query: Builds index (one-time cost, typically < 1 second for 1000+ elements)
- Subsequent queries: Fast retrieval using pre-built index
- Memory: Index stored in memory, minimal overhead

## Advanced Usage

### Using Python Scripts for Complex Queries

```python
from snapshot_query import SnapshotQuery

# Load snapshot file
query = SnapshotQuery("snapshot-2026-01-09T15-00-42-849Z.log")

# Find all buttons
buttons = query.find_by_role("button")
print(f"Found {len(buttons)} buttons")

# Find elements containing "登录"
login_elements = query.find_by_name("登录")
for elem in login_elements:
    print(f"Name: {elem.get('name')}, ref: {elem.get('ref')}")

# Get element path
path = query.get_element_path("ref-b9k8zlttiah")
print(f"Element path depth: {len(path)}")
```

### Using grep for Quick Search (for large files)

**Windows (PowerShell):**
```powershell
Select-String -Path "snapshot.log" -Pattern "name: 搜索" -Context 0,2
```

**Linux/Mac:**
```bash
grep -A 2 "name: 搜索" snapshot.log
```

### Batch Processing Multiple Snapshot Files

```python
from pathlib import Path
from snapshot_query import SnapshotQuery

log_dir = Path(r"C:\Users\{username}\.cursor\browser-logs")

for log_file in log_dir.glob("snapshot-*.log"):
    print(f"\nProcessing file: {log_file.name}")
    query = SnapshotQuery(log_file)
    # Execute queries...
```

## Building and Publishing

### Build Package
```bash
pip install build
python -m build
```

### Install Locally
```bash
pip install -e .
```

### Publish to PyPI
```bash
pip install twine
twine upload dist/*
```

After publishing, anyone can use:
```bash
uvx snapshot-query snapshot.log find-name "搜索"
```

## Testing

- Write tests for all query methods in `SnapshotQuery` class
- Test with real snapshot files (use `example-snapshot.log` for examples)
- Test edge cases: empty files, missing fields, malformed YAML
- Test CLI argument parsing and error handling
- Place tests in `tests/` directory (if added)

## Error Handling

- Raise `FileNotFoundError` when snapshot file doesn't exist
- Handle YAML parsing errors gracefully with clear error messages
- Validate command arguments before processing
- Print user-friendly error messages to stderr
- Exit with appropriate exit codes (0 for success, 1 for errors)

## Performance Considerations

- For large snapshot files, consider streaming or chunked processing if needed
- Current recursive approach is sufficient for typical snapshot sizes (100-200KB)
- Limit output for commands that might return many results (show first N, indicate total)
- For very large files, use `grep` or `ripgrep` for simple text searches (faster than loading entire YAML)

## Boundaries

- **Never modify** the original snapshot file - only read and query
- **Don't assume** all elements have `name` or `children` fields - always check
- **Don't hardcode** ref values - they change with each snapshot
- **Ask before** adding new dependencies to `pyproject.toml`
- **Don't remove** backward compatibility with `query_snapshot.py` script
- **Preserve** the module structure - keep `SnapshotQuery` class in `query.py`
- **Don't modify** existing public method signatures without good reason
- **Note**: ref values are only valid for current page state - page refresh or navigation invalidates them

## Documentation

- Keep README.md updated with usage examples
- Document new commands in all relevant docs (README, USAGE, SNAPSHOT_QUERY_GUIDE, MCP_README)
- Include code examples in documentation
- Maintain consistency between different documentation files

## Common Issues and Solutions

### Q: Why can't I find an element?
A: Check if element name is correct, try using fuzzy search `find-name` instead of exact search

### Q: Do ref values change?
A: Yes, ref values change with each page refresh or new snapshot. Need to re-acquire snapshot to get new ref values

### Q: How to batch process multiple snapshot files?
A: Use script to loop through files:
```python
from pathlib import Path
from snapshot_query import SnapshotQuery

for log_file in Path(".cursor/browser-logs").glob("snapshot-*.log"):
    query = SnapshotQuery(log_file)
    # Execute queries...
```

### Q: MCP SDK import errors
A: Ensure MCP SDK is installed:
```bash
pip install mcp
```

### Q: Server won't start
A: Check Python environment is correct, ensure all dependencies are installed:
```bash
pip install -e .
```
