Observed since 20 Oct, 19:47 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ado-search
Sync and search Azure DevOps work items and wiki pages for AI agents
Sync and search Azure DevOps work items and wiki pages locally for AI agents.
pip install ado-search
Or from source:
pip install git+https://github.com/HurleySk/ado-search.git
# 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
show to render full content — minimal contextSync produces two text files that are safe to commit, push, and pull:
work-items.jsonl — one JSON object per line, sorted by IDwiki-pages.jsonl — one JSON object per line, sorted by pathThe SQLite index (index.db) is .gitignored — it auto-rebuilds from JSONL on first search or show.
Each work item includes: id, title, type, state, area, iteration, assigned_to, tags, priority, parent_id, created, updated, description, acceptance_criteria, dev_notes (Custom.DevelopmentNotes), notes (Custom.Notes), 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.
When include_attachments = true in config, or --include-attachments is passed on the command line, sync/fetch downloads:
.ado-search/attachments/{work_item_id}/.ado-search/attachments/{work_item_id}/inline/fetch --include-attachments automatically enables comment fetching to capture these.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:
# Fetch specific items with their attachments (also fetches comment images)
ado-search fetch 73541 --include-attachments
# One-time full sync with attachments
ado-search sync --include-attachments
| Command | Description |
|---|---|
ado-search init | Configure organization, project, and auth |
ado-search sync | Pull latest data from Azure DevOps (--full for complete re-fetch, --project to limit) |
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 edit-comment <id> <comment_id> <text> | Replace the text of a comment |
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, with their ids (live) |
ado-search get-fields <id> | Print raw field values as JSON, HTML intact (live) |
ado-search mentions | List comments that @mention you, newest first (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 and update work items directly from the CLI:
# 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
Description, acceptance criteria, and comment text accept HTML. For multi-line content, use the @file convention to read from a file:
# 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 (CTR), please review"
# Text that starts with an @mention needs @@ so it is not read as a file path
ado-search add-comment 12345 "@@Jane Doe, can you retest?"
# Refuse to post if any mention is unknown or ambiguous
ado-search add-comment 12345 "@@Jane Doe, can you retest?" --strict-mentions
# 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"
# Replace the text of a comment (ids come from list-comments)
ado-search edit-comment 12345 5335866 "@@Jane Doe, retest with build 2" --strict-mentions
edit-comment takes the same text forms and options as add-comment and replaces the whole comment.
@Display Name and @email patterns in comment text are resolved via the ADO Identity Picker API and replaced with mention HTML so the mentioned user receives a notification. A mention takes the longest run of up to five words that names exactly one person by whole words, so @Jane Doe (CTR), please tags Jane Doe and keeps , please, and @Jan never tags Janet. A first name shared by several people is reported as ambiguous and left as text. Existing mention anchors, <code>, and <pre> are left alone; write @ for a literal @ in the comment. Resolved, unresolved, and ambiguous mentions are reported on stderr.
After create/update/add-comment/edit-comment/add-link, the item is automatically re-fetched and merged into the local JSONL store so it appears in search immediately.
# 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 with their ids (live from ADO)
ado-search list-comments 12345
show and the local store keep descriptions as plain text. get-fields reads the item live and prints raw values as JSON, so HTML can be edited and written back with update --description @file.html:
# Type, title, state, assignee, tags, story points, description, acceptance criteria
ado-search get-fields 12345
# Specific fields by reference name (repeatable)
ado-search get-fields 12345 --field System.Description --field Custom.Effort
# Write the JSON to a UTF-8 file instead of stdout
ado-search get-fields 12345 --out fields.json
Identity fields show the uniqueName (email); a field with no value is null. Nothing is written to ADO or the local store.
mentions lists the comments that @mention you (the authenticated user), newest first, read live from ADO. Work items come from the WIQL macro @RecentMentions, so it covers the last 30 days:
# Every comment that mentions you (up to 50)
ado-search mentions
# Only comments created or modified after a UTC datetime, as JSON
ado-search mentions --since 2026-10-15T00:00:00Z --json
# Keep your own comments that mention you, and show up to 200
ado-search mentions --include-own --limit 200
Each record shows the work item id, title, state, type and assignee, then the comment id, author, created date (UTC) and plain text, with mentions written as @Name. A comment counts when one of its mention anchors carries your identity id; a comment with no mention anchors at all counts when its text holds @Your Name or @your email. Nothing is written to ADO or the local store.
Default sync includes Bug, User Story, Epic, and Feature work item types. To include Tasks or customize:
# .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
One data dir can hold several projects from the same organization:
[organization]
url = "https://dev.azure.com/yourorg"
project = "Alpha" # default project for create/update/comments/links and wiki
projects = ["Alpha", "Beta"] # or ["*"] for every project you can see
ado-search sync # syncs every configured project
ado-search sync --project Beta # just one
ado-search search "timeout" --project Beta
ado-search grep "retry" --project Alpha
Each project keeps its own incremental watermark under [sync.last_sync_by_project]. Wiki pages are synced for the default project only.
Project names are matched case-insensitively, as in Azure DevOps. sync.area_paths entries apply to the project they start with (Alpha\Web filters Alpha only); a project with no matching entry syncs all its areas. work_item_types and states apply to every project, so list the types of every process you sync (e.g. both User Story and Product Backlog Item).
az devops commands. Requires az login.Connect-AzAccount.Set via ado-search init --auth-method <method> or in config.toml.
# 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>
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
Search work items with regex patterns across fields:
# 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, dev_notes, notes, comments. Use --field / -f to scope to specific fields (title, description, acceptance_criteria, dev_notes, notes, 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.
Query work item parent-child hierarchy from local data:
# 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.
azure-devops extension (az extension add --name azure-devops)az login completedDownload counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
clickEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | click>=8.0 | runtime |
tomliEnvironment{
"extras": "",
"marker": "python_version < \"3.11\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | tomli>=2.0; python_version < "3.11" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.0; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio>=0.21; extra == "dev" | runtime |
bdist_wheel · 63.48 KB · Python py3
sdist · 95.54 KB · Python source
Observed since 20 Oct, 19:47 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:474997b0bbd6aac6e9d9986b6e1426ebd3bcd3a515e43e928b43da5612adb4cd