Metadata-Version: 2.4
Name: ado-search
Version: 1.12.0
Summary: Sync and search Azure DevOps work items and wiki pages for AI agents
Author: Samuel Hurley
License-Expression: MIT
Project-URL: Homepage, https://github.com/HurleySk/ado-search
Project-URL: Repository, https://github.com/HurleySk/ado-search
Project-URL: Issues, https://github.com/HurleySk/ado-search/issues
Keywords: azure-devops,search,work-items,wiki,agents
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: click>=8.0
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"

# ado-search

Sync and search Azure DevOps work items and wiki pages locally for AI agents.

## Install

```bash
pip install ado-search
```

Or from source:

```bash
pip install git+https://github.com/HurleySk/ado-search.git
```

## Quick Start

```bash
# Configure (requires az login first)
ado-search init --org https://dev.azure.com/yourorg --project YourProject

# Pull data
ado-search sync

# Search
ado-search search "login bug"
ado-search search "auth" --type Bug --state Active
ado-search search "setup guide" --format paths
```

## How It Works

1. **Sync** pulls work items and wiki pages from Azure DevOps
   - Tries **OData analytics** first (fetches all items in one call — fast)
   - Falls back to **az devops CLI** if analytics isn't available
2. Data is stored as **sorted JSONL files** — git-friendly, diffable, one file per entity type
3. A **SQLite FTS5 index** is derived from the JSONL and auto-rebuilt when stale
4. Agents search the index, then use `show` to render full content — minimal context

### Git-Friendly Storage

Sync produces two text files that are safe to commit, push, and pull:

- `work-items.jsonl` — one JSON object per line, sorted by ID
- `wiki-pages.jsonl` — one JSON object per line, sorted by path

The SQLite index (`index.db`) is `.gitignore`d — it auto-rebuilds from JSONL on first search or show.

## Synced Fields

Each work item includes: id, title, type, state, area, iteration, assigned_to, tags, priority, parent_id, created, updated, description, acceptance_criteria, story_points, state_history, attachments, and inline_images.

Story points are sourced from `StoryPoints` or `Effort` fields. State history tracks every state change (e.g., New → Active → Resolved → Closed) with date and author.

### Attachments & Inline Images

When `include_attachments = true` in config, or `--include-attachments` is passed on the command line, sync/fetch downloads:

- **File attachments** — stored in `.ado-search/attachments/{work_item_id}/`
- **Inline images** — images embedded in Description/Acceptance Criteria HTML, stored in `.ado-search/attachments/{work_item_id}/inline/`
- **Comment images** — images pasted into work item comments (screenshots, diagrams), stored alongside other inline images. Requires both `include_comments` and `include_attachments` to be enabled.

Attachment filenames are indexed and searchable. Inline images are referenced as `[image: path]` in text output so agents can locate them. Downloads are incremental — existing files with correct size are skipped on re-sync.

Note: attachments require the WIQL/REST sync path (OData doesn't include relations), so OData fast path is skipped when attachments are enabled.

Use `--include-attachments` on `fetch` or `sync` for per-invocation attachment downloads without changing config:

```bash
# Fetch specific items with their attachments
ado-search fetch 73541 --include-attachments

# One-time full sync with attachments
ado-search sync --include-attachments
```

## Commands

| Command | Description |
|---------|-------------|
| `ado-search init` | Configure organization, project, and auth |
| `ado-search sync` | Pull latest data from Azure DevOps (`--full` for complete re-fetch) |
| `ado-search search "query"` | Full-text search with filters |
| `ado-search show <id>` | Display full content of an item |
| `ado-search create` | Create a new work item |
| `ado-search update <id>` | Update an existing work item |
| `ado-search add-comment <id> <text>` | Add a comment to a work item |
| `ado-search add-link <source> <target>` | Add a link between two work items |
| `ado-search remove-link <source> <target>` | Remove a link between two work items |
| `ado-search upload-attachment <id> <file>` | Upload a local file as an attachment to a work item |
| `ado-search list-links <id>` | List links on a work item (live) |
| `ado-search list-comments <id>` | List comments on a work item (live) |
| `ado-search grep "pattern"` | Regex search across work item fields |
| `ado-search children <parent_id>` | List children (or full tree) of a work item |

## Create & Update

Create and update work items directly from the CLI:

```bash
# Create a new bug
ado-search create --type Bug --title "Login button broken" --state New --priority 1

# Create with description and tags
ado-search create --type "User Story" --title "Add dark mode" \
  --description "Users want a dark theme option" \
  --tags "ui; theme" --story-points 5

# Create a child task under a parent work item
ado-search create --type Task --title "Subtask" --parent 62434

# Update a work item
ado-search update 12345 --state Active --assigned-to "user@example.com"

# Close as duplicate (--reason sets Microsoft.VSTS.Common.ResolvedReason)
ado-search update 67154 --state Closed --reason Duplicate

# Set arbitrary ADO fields (including custom fields)
ado-search create --type Task --title "Research" --field "Custom.Effort=3"
ado-search update 12345 --field "System.AreaPath=Project\Team" --field "Custom.Sprint=Sprint 5"

# Preview without writing
ado-search create --type Bug --title "Test" --dry-run
```

### HTML Content from Files

Description, acceptance criteria, and comment text accept HTML. For multi-line content, use the `@file` convention to read from a file:

```bash
# Set description from an HTML file
ado-search create --type Bug --title "Rendering issue" --description @bug-details.html

# Update acceptance criteria from a file
ado-search update 12345 --acceptance-criteria @criteria.html

# Add a comment from a file
ado-search add-comment 12345 @review-notes.html

# Inline HTML still works
ado-search add-comment 12345 "<p>Looks good!</p>"

# @mentions are auto-resolved to ADO identity links
ado-search add-comment 12345 "Great work @John.Smith, please review"

# Skip mention resolution (post raw text as-is)
ado-search add-comment 12345 "@not-a-user just a note" --no-mentions

# Escape a literal @ with @@
ado-search update 12345 --description "@@mention is not a file reference"
```

`@DisplayName` patterns in comment text are resolved via the ADO Identity Picker API and replaced with mention HTML so the mentioned user receives a notification.

After create/update/add-comment/add-link, the item is automatically re-fetched and merged into the local JSONL store so it appears in search immediately.

## Links

```bash
# Add a parent link
ado-search add-link 12345 67890 --type parent

# Add a related link with a comment
ado-search add-link 12345 67891 --type related --comment "See also this item"

# Link types: related, parent, child, duplicate, duplicate-of, depends-on, predecessor, successor
ado-search add-link 12345 67892 --type depends-on

# Use a raw ADO relation type
ado-search add-link 12345 67893 --type "System.LinkTypes.Related"

# Preview without creating
ado-search add-link 12345 67890 --type child --dry-run

# Remove a link
ado-search remove-link 12345 67890 --type parent

# List all links on a work item (live from ADO)
ado-search list-links 12345

# List all comments on a work item (live from ADO)
ado-search list-comments 12345
```

## Configuration

Default sync includes Bug, User Story, Epic, and Feature work item types. To include Tasks or customize:

```toml
# .ado-search/config.toml
[sync]
work_item_types = ["Bug", "User Story", "Task", "Epic", "Feature"]
include_comments = false
include_attachments = false  # set to true to download file attachments and inline images
```

## Auth Methods

- **az-cli** (default): Uses `az devops` commands. Requires `az login`.
- **az-powershell**: Uses Azure PowerShell + REST. Requires `Connect-AzAccount`.
- **pat**: Uses a Personal Access Token via REST. For cross-cloud or service account scenarios.

Set via `ado-search init --auth-method <method>` or in `config.toml`.

```bash
# PAT example (token can also be set via ADO_PAT env var)
ado-search init --org https://dev.azure.com/yourorg --project YourProject --auth-method pat --pat <token>
```

## Search Formats

```bash
ado-search search "query"                          # compact (default)
ado-search search "query" --format detail           # with description snippets
ado-search search "query" --format json             # machine-readable
ado-search search "query" --format paths            # file paths only (for agent piping)
ado-search search "query" --type Bug --state Active # filtered
```

## Grep — Regex Pattern Matching

Search work items with regex patterns across fields:

```bash
# Find IP addresses anywhere in title/description/comments
ado-search grep '\d+\.\d+\.\d+\.\d+'

# Find "deprecated" only in comments, case-insensitive
ado-search grep -i 'deprecated' -f comments

# Find URL patterns in active bugs, brief output
ado-search grep 'https?://\S+' --type Bug --state Active --brief

# JSON output for scripting
ado-search grep 'TODO|FIXME' --format json
```

Default fields searched: title, description, comments. Use `--field` / `-f` to scope to specific fields (`title`, `description`, `acceptance_criteria`, `comments`, `tags`, `assigned_to`, `area`, `iteration`, `state_history`).

Metadata pre-filters (`--type`, `--state`, `--area`, `--assigned-to`, `--tag`) narrow candidates via SQLite indexes before the regex scan. Exit codes follow grep convention: 0 = matches found, 1 = no matches, 2 = error.

## Children — Hierarchy Queries

Query work item parent-child hierarchy from local data:

```bash
# Show direct children of an epic
ado-search children 61123

# Full recursive tree (features, stories, tasks, ...)
ado-search children 61123 --recursive

# Indented tree view
ado-search children 61123 --recursive --format tree

# Filter to user stories only
ado-search children 61123 --recursive --type "User Story"

# Include closed date from state history
ado-search children 61123 --recursive --include-closed-date

# JSON for scripting
ado-search children 61123 --recursive --format json --include-closed-date
```

The `children` command queries the local SQLite index (no API calls). Use `sync` or `fetch` first to ensure data is current. Exit codes: 0 = children found, 1 = no children, 2 = parent not found.

## Prerequisites

- Python 3.10+
- Azure CLI with `azure-devops` extension (`az extension add --name azure-devops`)
- `az login` completed
