azdo-cli 0.22.0-develop.787 → 0.22.0-develop.790

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -3
  2. package/dist/index.js +66 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@ Azure DevOps CLI focused on work item read/write workflows.
15
15
  - Attach a local file to a work item, or remove a named attachment (`add-attachment`, `delete-attachment`)
16
16
  - Read/write rich-text fields as markdown (`get-md-field`, `set-md-field`)
17
17
  - Download images embedded in rich-text fields, optionally resized for LLM use (`get-item`/`get-md-field` `--download-images`, `--resize-images`)
18
- - Check branch pull request status, open PRs to `develop` (optionally pre-filled from a repository-defined template), update an existing PR's title or description (`pr update`), abandon or reactivate one (`pr abandon` / `pr reactivate`), list PR comment threads for any PR (`--pr-number`), resolve/reopen threads, link/unlink work items, and add/remove required or optional reviewers — all from the CLI (`pr`)
18
+ - List pull requests with their draft state, dates, reviewers, labels and linked work item ids (`pr list --work-items`), check branch pull request status, open PRs to `develop` (optionally pre-filled from a repository-defined template), update an existing PR's title or description (`pr update`), abandon or reactivate one (`pr abandon` / `pr reactivate`), list PR comment threads for any PR (`--pr-number`), resolve/reopen threads, link/unlink work items, and add/remove required or optional reviewers — all from the CLI (`pr`)
19
19
  - Feed long PR titles, descriptions and comment bodies from a file or a pipe instead of the shell (`--title-file`, `--description-file`, `--file`; `-` reads standard input)
20
20
  - Persist org/project/default fields in local config (`config`)
21
21
  - List all fields of a work item (`list-fields`)
@@ -67,9 +67,11 @@ azdo delete-attachment 12345 screenshot.png --yes # skip the prompt (scr
67
67
  azdo delete-attachment 12345 screenshot.png --id <guid> # disambiguate when the name is shared
68
68
 
69
69
  # Find a pull request — one API call, any branch
70
- azdo pr list # active PRs in the repository
71
- azdo pr list --branch feature/x --json # id, title, source/target, author, url, description
70
+ azdo pr list # active PRs in the repository ([active, draft] marks a draft)
71
+ azdo pr list --branch feature/x --json # id, title, source/target, author, url, description, isDraft,
72
+ # creationDate, closedDate, reviewers (uniqueName, vote), labels
72
73
  azdo pr list --status all --top 50
74
+ azdo pr list --work-items --json # + workItemIds per PR: the whole PR ↔ work item map in one call
73
75
 
74
76
  # PR comment threads — list, filter, target by number, resolve or reopen
75
77
  azdo pr comments # active-branch PR; code-anchored threads show file:line
package/dist/index.js CHANGED
@@ -3704,7 +3704,12 @@ function mapPullRequest(context, repo, pullRequest) {
3704
3704
  url: pullRequest._links?.web?.href ?? buildPullRequestWebUrl(context, repo, pullRequest.pullRequestId),
3705
3705
  description: pullRequest.description?.trim() || null,
3706
3706
  createdByUniqueName: pullRequest.createdBy?.uniqueName ?? null,
3707
- createdById: pullRequest.createdBy?.id ?? null
3707
+ createdById: pullRequest.createdBy?.id ?? null,
3708
+ isDraft: pullRequest.isDraft ?? false,
3709
+ creationDate: pullRequest.creationDate ?? null,
3710
+ closedDate: pullRequest.closedDate ?? null,
3711
+ reviewers: (pullRequest.reviewers ?? []).map(mapReviewer),
3712
+ labels: (pullRequest.labels ?? []).filter((label) => label.active !== false).map((label) => label.name)
3708
3713
  };
3709
3714
  }
3710
3715
  function mapPullRequestCheckName(status2) {
@@ -3884,6 +3889,15 @@ async function listRepositoryPullRequests(context, repo, cred, opts) {
3884
3889
  const data = await readJsonResponse(response);
3885
3890
  return data.value.map((pullRequest) => mapPullRequest(context, repo, pullRequest));
3886
3891
  }
3892
+ async function getPullRequestWorkItemIds(context, repo, cred, prId) {
3893
+ const url = new URL(
3894
+ `https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}/workitems`
3895
+ );
3896
+ url.searchParams.set("api-version", "7.1");
3897
+ const response = await fetchWithErrors(url.toString(), { headers: authHeaders(cred) });
3898
+ const data = await readJsonResponse(response);
3899
+ return data.value.map((ref) => Number.parseInt(ref.id, 10)).filter((id) => Number.isInteger(id) && id > 0).sort((a, b) => a - b);
3900
+ }
3887
3901
  async function getPullRequestChecks(context, repo, cred, prId) {
3888
3902
  const response = await fetchWithErrors(
3889
3903
  buildPullRequestStatusesUrl(context, repo, prId).toString(),
@@ -5811,42 +5825,73 @@ function createPrCommentDeleteCommand() {
5811
5825
  }
5812
5826
  var LIST_STATUS_VALUES = ["active", "completed", "abandoned", "all"];
5813
5827
  var DEFAULT_LIST_TOP = 25;
5828
+ var WORK_ITEM_LOOKUP_CONCURRENCY = 5;
5814
5829
  function formatPullRequestListEntry(pullRequest) {
5815
- return [
5816
- `#${pullRequest.id} [${pullRequest.status}] ${pullRequest.title}`,
5830
+ const state = pullRequest.isDraft ? `${pullRequest.status}, draft` : pullRequest.status;
5831
+ const lines = [
5832
+ `#${pullRequest.id} [${state}] ${pullRequest.title}`,
5817
5833
  ` ${formatBranchName(pullRequest.sourceRefName)} -> ${formatBranchName(pullRequest.targetRefName)}`,
5818
- ` Author: ${pullRequest.createdBy ?? "Unknown"}`,
5819
- ` ${pullRequest.url ?? "\u2014"}`
5820
- ].join("\n");
5834
+ ` Author: ${pullRequest.createdBy ?? "Unknown"}`
5835
+ ];
5836
+ if ("workItemIds" in pullRequest) {
5837
+ const ids = pullRequest.workItemIds.map((id) => `#${id}`).join(", ");
5838
+ lines.push(` Work items: ${ids || "none"}`);
5839
+ }
5840
+ lines.push(` ${pullRequest.url ?? "\u2014"}`);
5841
+ return lines.join("\n");
5842
+ }
5843
+ function parseListOptions(options) {
5844
+ const status2 = options.status ?? "active";
5845
+ if (!LIST_STATUS_VALUES.includes(status2)) {
5846
+ writeError(`Invalid --status "${status2}"; expected one of ${LIST_STATUS_VALUES.join(", ")}.`);
5847
+ return null;
5848
+ }
5849
+ let top = DEFAULT_LIST_TOP;
5850
+ if (options.top !== void 0) {
5851
+ const parsed = parsePositivePrNumber(options.top);
5852
+ if (parsed === null) {
5853
+ writeError(`Invalid --top "${options.top}"; expected a positive integer.`);
5854
+ return null;
5855
+ }
5856
+ top = parsed;
5857
+ }
5858
+ const branch = options.branch?.trim().replace(/^refs\/heads\//, "") || null;
5859
+ return { status: status2, top, branch };
5860
+ }
5861
+ async function attachWorkItemIds(context, repo, cred, pullRequests) {
5862
+ const result = new Array(pullRequests.length);
5863
+ let next = 0;
5864
+ const worker = async () => {
5865
+ while (next < pullRequests.length) {
5866
+ const index = next;
5867
+ next += 1;
5868
+ const pullRequest = pullRequests[index];
5869
+ const workItemIds = await getPullRequestWorkItemIds(context, repo, cred, pullRequest.id);
5870
+ result[index] = { ...pullRequest, workItemIds };
5871
+ }
5872
+ };
5873
+ await Promise.all(Array.from({ length: Math.min(WORK_ITEM_LOOKUP_CONCURRENCY, pullRequests.length) }, worker));
5874
+ return result;
5821
5875
  }
5822
5876
  function createPrListCommand() {
5823
5877
  const command = new Command12("list");
5824
- withCommonPrOptions(configureUnwrappedHelp(command)).description("List pull requests in the repository, optionally filtered by source branch").option("--branch <name>", "only pull requests whose source branch is this one (with or without the refs/heads/ prefix)").option("--status <status>", `pull request status filter (${LIST_STATUS_VALUES.join(" | ")})`, "active").option("--top <N>", `maximum number of pull requests to return (default ${DEFAULT_LIST_TOP})`).option("--json", "output JSON").action(async (options) => {
5878
+ withCommonPrOptions(configureUnwrappedHelp(command)).description("List pull requests in the repository, optionally filtered by source branch").option("--branch <name>", "only pull requests whose source branch is this one (with or without the refs/heads/ prefix)").option("--status <status>", `pull request status filter (${LIST_STATUS_VALUES.join(" | ")})`, "active").option("--top <N>", `maximum number of pull requests to return (default ${DEFAULT_LIST_TOP})`).option("--work-items", "also return the ids of each pull request's linked work items (one extra API call per pull request)").option("--json", "output JSON").action(async (options) => {
5825
5879
  validateOrgProjectPair(options);
5826
- const status2 = options.status ?? "active";
5827
- if (!LIST_STATUS_VALUES.includes(status2)) {
5828
- writeError(`Invalid --status "${status2}"; expected one of ${LIST_STATUS_VALUES.join(", ")}.`);
5880
+ const parsed = parseListOptions(options);
5881
+ if (parsed === null) {
5829
5882
  return;
5830
5883
  }
5831
- let top = DEFAULT_LIST_TOP;
5832
- if (options.top !== void 0) {
5833
- const parsed = parsePositivePrNumber(options.top);
5834
- if (parsed === null) {
5835
- writeError(`Invalid --top "${options.top}"; expected a positive integer.`);
5836
- return;
5837
- }
5838
- top = parsed;
5839
- }
5840
- const branch = options.branch?.trim().replace(/^refs\/heads\//, "") || null;
5884
+ const { status: status2, top, branch } = parsed;
5841
5885
  let context;
5842
5886
  try {
5843
5887
  const resolved = await resolvePrCommandContext(options, { requireBranch: false });
5844
5888
  context = resolved.context;
5845
- const pullRequests = await listRepositoryPullRequests(resolved.context, resolved.repo, resolved.pat, {
5889
+ const listed = await listRepositoryPullRequests(resolved.context, resolved.repo, resolved.pat, {
5846
5890
  sourceBranch: branch ?? void 0,
5847
5891
  status: status2,
5848
5892
  top
5849
5893
  });
5894
+ const pullRequests = options.workItems ? await attachWorkItemIds(resolved.context, resolved.repo, resolved.pat, listed) : listed;
5850
5895
  const result = {
5851
5896
  repository: resolved.repo,
5852
5897
  branch,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.22.0-develop.787",
3
+ "version": "0.22.0-develop.790",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {