Skip to content

feat: Implement azdo boards work-item list command #136

@tmeckel

Description

@tmeckel

This issue tracks the implementation of the azdo boards work-item list command.

Command Description

List work items belonging to a project within an Azure DevOps organization. Work items are the fundamental units used to plan and track work in Azure Boards (tasks, bugs, user stories, etc.), and the REST API supports retrieving up to 200 items per batch fetch (Work Items - List (REST 7.1)). This command provides an ergonomic way to view a project backlog and quickly filter the results by workflow status, assignee, creator, tags, state, dates, classification/severity, and other backlog attributes without having to author WIQL manually.

Important REST constraints:

  • The Work Items - List endpoint fetches work items by ID (max 200 IDs). It does not list a project backlog by itself.
  • To list work items by criteria, the command should run a WIQL query to obtain IDs, then fetch work items in batches.
  • WIQL supports both WHERE clauses on work item fields and ORDER BY on one or more sortable fields (WIQL syntax reference).

Command Signature

azdo boards work-item list [ORGANIZATION/]PROJECT
  • Aliases: ls, l
  • Positional parsing must follow the standard project-scoped [ORGANIZATION/]PROJECT splitter. When the organization segment is omitted, resolve it via the configured default. Return a flag error when no organization is provided or configured.

Flags

  • --status, -s: Repeatable flag that accepts the state categories open, closed, resolved, all. Convert the shorthand into WIQL filters by resolving the concrete workflow state names for the selected work item types. Continue using the current category mapping (open → proposed/in-progress style states, closed → completed/removed, resolved → resolved). Default is open.
  • --state: Repeatable flag for one or more exact workflow state names (for example Active, Resolved, Closed, Ready for Review). Build a WIQL predicate on System.State using the literal state names. This flag complements --status rather than replacing it.
    • If both --status and --state are provided, intersect them by appending both predicates with AND so users can narrow a broad category to specific named states.
  • --type, -t: Repeatable flag for one or more work item types (for example User Story, Task). Build a WIQL predicate on System.WorkItemType. When omitted, do not filter by type.
  • --assigned-to, -a: Repeatable flag that accepts the same values supported by pull request reviewer resolution (email, org/team, descriptor). Also accept the special token @me to scope to the authenticated user (see Identity-based queries). Resolve identities using the same helpers as internal/cmd/pr/create (identity client lookups) and map @me via extensions.Client.GetSelfID.
  • --created-by: Repeatable flag that mirrors the --assigned-to identity handling but filters on System.CreatedBy. Support email, descriptor, and @me using the same lookup helpers as --assigned-to. This is the ergonomic “who reported/authored this work item” filter.
  • --authored-by: Alias for --created-by. Both flags should populate the same option slice so the command has one implementation path.
  • --classification, -c: Repeatable flag that filters on the severity classification Microsoft.VSTS.Common.Severity. Accept the canonical values documented for bug severity (1 - Critical, 2 - High, 3 - Medium, 4 - Low) and emit a flag error for unknown values (Severity guidance).
  • --priority, -p: Repeatable flag that filters on Microsoft.VSTS.Common.Priority. Accept integer values 1–4 as defined in Azure Boards planning guidance (Priority field reference).
  • --area: Repeatable flag that filters work items assigned to matching area paths. Accept either a fully-qualified area path or the Under: prefix to include an entire subtree (for example Under:Web/Payments). Map to System.AreaPath/Under operators per Classification field reference.
  • --iteration: Repeatable flag that filters by iteration (sprint) path. Accept the same syntax as --area, including Under: semantics. Map to System.IterationPath/Under operators per the same classification guidance.
  • --tag: Repeatable flag that filters on System.Tags. Tags are a first-class Azure Boards filtering concept and WIQL supports querying them via Contains clauses (Query for work items based on tags).
    • Treat repeated --tag flags as an AND of Contains predicates so users can narrow to work items carrying all specified tags.
    • Reject empty tag values.
  • --changed-after: Filter on System.ChangedDate >= <timestamp>.
  • --created-after: Filter on System.CreatedDate >= <timestamp>.
    • Accept RFC3339 timestamps first. If the project already has a date parsing helper used by other commands, reuse it; otherwise document the accepted format explicitly in help text.
    • Wrap parse failures with util.FlagErrorWrap.
    • These date filters are intentionally “after” only for the main command scope. More advanced date-window or historical-query support can be a follow-up issue.
  • --sort: Control the WIQL ORDER BY clause. Default remains newest changed first for continuity with current behavior.
    • Supported values should be a small ergonomic set mapped onto Azure DevOps fields: changed, created, id, state, title, assigned-to, type, tags.
    • Support optional :asc / :desc suffixes (for example --sort created:asc). Default direction is desc for changed, created, and id; default asc for the other fields.
    • Field mapping:
      • changedSystem.ChangedDate
      • createdSystem.CreatedDate
      • idSystem.Id
      • stateSystem.State
      • titleSystem.Title
      • assigned-toSystem.AssignedTo
      • typeSystem.WorkItemType
      • tagsSystem.Tags
  • --limit, -L: Maximum number of results to return (>=1).
    • Apply $top to WIQL.
    • Fetch work items via GetWorkItemsBatch in chunks of 200 IDs (hard REST limit).
    • Default 50.
  • JSON output flags: call util.AddJSONFlags(cmd, &opts.exporter, []string{...}) so callers can select top-level fields.

JSON Output Contract

Emit an array of Azure DevOps work item objects. Keep the JSON payload aligned with the existing SDK work item model so the command remains predictable for callers already using --json.

Supported top-level JSON fields:

  • id: raw SDK WorkItem.Id
  • rev: raw SDK WorkItem.Rev
  • fields: raw SDK WorkItem.Fields; includes values such as System.Title, System.State, System.Tags, System.CreatedBy, System.CreatedDate, System.ChangedDate, and any process-specific fields returned by the batch request
  • relations: raw SDK WorkItem.Relations
  • url: raw SDK WorkItem.Url
  • _links: raw SDK WorkItem.Links
  • commentVersionRef: raw SDK WorkItem.CommentVersionRef

JSON rules:

  • Register exactly these field names with util.AddJSONFlags.
  • Do not add derived top-level fields for assignedTo, createdBy, tags, or formatted dates in this issue. Those remain under fields.
  • When the command needs extra fields for table rendering or sorting validation, request them through the batch call but keep the JSON surface unchanged.

Command Wiring

This remains a leaf command under the existing hierarchy.

  • Package path: internal/cmd/boards/workitem/list
  • Factory: internal/cmd/boards/workitem/list/list.go with NewCmd(ctx util.CmdContext) *cobra.Command
  • Parent command file: internal/cmd/boards/workitem/workitem.go
  • Wiring requirement: internal/cmd/boards/workitem/workitem.go must continue to call AddCommand(list.NewCmd(ctx))
  • Existing higher-level parents that must already remain wired: boardswork-itemlist

API Surface

Use the vendored Azure DevOps Go SDK work item tracking client already exposed by the repository.

Primary SDK calls / REST surfaces:

  • workitemtracking.Client.QueryByWiqlWIQL Query (REST 7.1)
  • workitemtracking.Client.GetWorkItemsBatchGet Work Items Batch (REST 7.1)
  • workitemtracking.Client.GetWorkItemTypes / GetWorkItemTypeStates for resolving --status category filters into concrete state names
  • extensions.Client.GetSelfID and existing identity resolution helpers for --assigned-to, --created-by, and --authored-by

Expected query/build behavior:

  • Always scope WIQL to [System.TeamProject] = @project.
  • Add optional WHERE clauses for each supported filter.
  • Combine repeated values for the same equality-style field using IN (...) where appropriate (type, assigned-to, created-by, state, classification, priority).
  • Use repeated Contains clauses for tags.
  • Emit a single ORDER BY clause derived from --sort, defaulting to [System.ChangedDate] DESC.
  • Continue passing AsOf from WIQL response into the batch fetch when present.

Request / response expectations:

  • QueryByWiql only returns IDs; full work item details must come from GetWorkItemsBatch.
  • GetWorkItemsBatch must continue chunking at 200 IDs.
  • Request the field set needed for both table output and filters under review: at minimum System.WorkItemType, System.State, System.Title, System.AssignedTo, System.CreatedBy, System.AreaPath, System.IterationPath, System.Tags, System.CreatedDate, and System.ChangedDate.
  • Nil pointer fields from SDK responses must still be handled with types.GetValue.

Implementation Notes (filled checklist)

  • Update internal/cmd/boards/workitem/list/list.go to add the five new filtering/sorting capabilities while preserving existing behavior.
  • Keep positional parsing on util.ParseProjectScope(ctx, scopeArg) and wrap parse errors with util.FlagErrorWrap.
  • Reuse the existing identity-resolution path for both --assigned-to and --created-by / --authored-by.
  • Extend WIQL builder logic to support:
    • exact state names via --state
    • creator identity filters via System.CreatedBy
    • tag filters via System.Tags Contains ...
    • created/changed date lower bounds
    • configurable ORDER BY
  • Preserve default status behavior (open) when neither --status nor --state is supplied.
  • Preserve default sort behavior as [System.ChangedDate] DESC when --sort is omitted.
  • Continue using progress indicator handling already established in the command.
  • Table output should remain stable and human-readable. If new columns are added, do so deliberately and document them in docs; otherwise keep current columns and let the new filters affect selection only.
  • JSON output should keep the current top-level field contract.
  • Add/update hermetic tests in internal/cmd/boards/workitem/list/list_test.go covering:
    • --state only
    • --status + --state combined
    • --created-by with email/descriptor/@me
    • --authored-by alias behavior
    • repeated --tag semantics
    • --changed-after and --created-after parsing and WIQL generation
    • --sort value parsing, default directions, and invalid values
    • continued batch chunking at 200
  • Run formatting/build/lint/test verification after implementation.

Tooling and Verification Checklist

  • run gofmt / gofumpt on touched Go files
  • run targeted tests for internal/cmd/boards/workitem/list
  • run go test ./...
  • run make lint
  • update generated docs if help text or examples change (make docs)

Reference Existing Patterns

Reuse the established helpers and patterns already used by the current implementation and nearby commands.

Required helpers / patterns:

  • util.ParseProjectScope for [ORGANIZATION/]PROJECT
  • util.AddJSONFlags for JSON field registration
  • util.FlagErrorWrap / util.FlagErrorf for user-facing validation errors
  • types.GetValue for safe SDK dereferencing
  • existing identity helper flow used by resolveAssignedToFilter and internal/azdo/extensions
  • current work item batch-fetch pattern and progress indicator lifecycle in internal/cmd/boards/workitem/list/list.go

Comparable implementation references:

  • internal/cmd/boards/workitem/list/list.go — current WIQL-builder, batching, JSON/table split
  • internal/cmd/pr/create — identity resolution patterns mentioned in the current issue body
  • internal/cmd/repo/list/list.go — project-scope parsing style for [ORGANIZATION/]PROJECT

References

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions