azdo-cli 0.5.0-017-pr-comments-threads.240 → 0.5.0-017-pr-comments-threads.244

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 (2) hide show
  1. package/dist/index.js +82 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2396,12 +2396,25 @@ function mapThread(thread) {
2396
2396
  comments
2397
2397
  };
2398
2398
  }
2399
+ var RESOLVED_THREAD_STATUSES = /* @__PURE__ */ new Set(["fixed", "wontFix", "closed", "byDesign"]);
2400
+ function isThreadResolved(status) {
2401
+ return RESOLVED_THREAD_STATUSES.has(status);
2402
+ }
2399
2403
  async function readJsonResponse(response) {
2400
2404
  if (!response.ok) {
2401
2405
  throw new Error(`HTTP_${response.status}`);
2402
2406
  }
2403
2407
  return response.json();
2404
2408
  }
2409
+ async function getPullRequestById(context, repo, pat, prId) {
2410
+ const url = new URL(
2411
+ `https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}`
2412
+ );
2413
+ url.searchParams.set("api-version", "7.1");
2414
+ const response = await fetchWithErrors(url.toString(), { headers: authHeaders(pat) });
2415
+ const data = await readJsonResponse(response);
2416
+ return mapPullRequest(repo, data);
2417
+ }
2405
2418
  async function listPullRequests(context, repo, pat, sourceBranch, opts) {
2406
2419
  const response = await fetchWithErrors(
2407
2420
  buildPullRequestsUrl(context, repo, sourceBranch, opts).toString(),
@@ -2471,6 +2484,13 @@ async function getPullRequestThreads(context, repo, pat, prId) {
2471
2484
  }
2472
2485
 
2473
2486
  // src/commands/pr.ts
2487
+ function parsePositivePrNumber(raw) {
2488
+ if (!/^\d+$/.test(raw)) {
2489
+ return null;
2490
+ }
2491
+ const n = Number.parseInt(raw, 10);
2492
+ return Number.isFinite(n) && n > 0 ? n : null;
2493
+ }
2474
2494
  function formatBranchName(refName) {
2475
2495
  return refName.startsWith("refs/heads/") ? refName.slice("refs/heads/".length) : refName;
2476
2496
  }
@@ -2525,20 +2545,33 @@ function formatPullRequestBlock(pullRequest) {
2525
2545
  ...formatPullRequestChecks(pullRequest.checks)
2526
2546
  ].join("\n");
2527
2547
  }
2548
+ function threadStatusLabel(status) {
2549
+ return isThreadResolved(status) ? "resolved" : status;
2550
+ }
2528
2551
  function formatThreads(prId, title, threads) {
2529
2552
  const lines = [`Active comments for pull request #${prId}: ${title}`];
2530
2553
  for (const thread of threads) {
2531
- lines.push("", `Thread #${thread.id} [${thread.status}] ${thread.threadContext ?? "(general)"}`);
2554
+ lines.push("", `Thread #${thread.id} [${threadStatusLabel(thread.status)}] ${thread.threadContext ?? "(general)"}`);
2532
2555
  for (const comment of thread.comments) {
2533
2556
  lines.push(` ${comment.author ?? "Unknown"}: ${comment.content}`);
2534
2557
  }
2535
2558
  }
2536
2559
  return lines.join("\n");
2537
2560
  }
2538
- async function resolvePrCommandContext(options) {
2561
+ async function resolvePrCommandContext(options, resolveOpts = {}) {
2562
+ const requireBranch = resolveOpts.requireBranch ?? true;
2539
2563
  const context = resolveContext(options);
2540
2564
  const repo = detectRepoName();
2541
- const branch = getCurrentBranch();
2565
+ let branch;
2566
+ if (requireBranch) {
2567
+ branch = getCurrentBranch();
2568
+ } else {
2569
+ try {
2570
+ branch = getCurrentBranch();
2571
+ } catch {
2572
+ branch = null;
2573
+ }
2574
+ }
2542
2575
  const credential = await requirePat(context.org);
2543
2576
  return {
2544
2577
  context,
@@ -2555,15 +2588,15 @@ function createPrStatusCommand() {
2555
2588
  try {
2556
2589
  const resolved = await resolvePrCommandContext(options);
2557
2590
  context = resolved.context;
2558
- const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch);
2591
+ const branch = resolved.branch;
2592
+ const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, branch);
2559
2593
  const pullRequestsWithChecks = await Promise.all(
2560
2594
  pullRequests.map(async (pullRequest) => ({
2561
2595
  ...pullRequest,
2562
2596
  checks: await getPullRequestChecks(resolved.context, resolved.repo, resolved.pat, pullRequest.id)
2563
2597
  }))
2564
2598
  );
2565
- const { branch, repo } = resolved;
2566
- const result = { branch, repository: repo, pullRequests: pullRequestsWithChecks };
2599
+ const result = { branch, repository: resolved.repo, pullRequests: pullRequestsWithChecks };
2567
2600
  if (options.json) {
2568
2601
  process.stdout.write(`${JSON.stringify(result, null, 2)}
2569
2602
  `);
@@ -2604,11 +2637,12 @@ function createPrOpenCommand() {
2604
2637
  writeError("Pull request creation requires a source branch other than develop.");
2605
2638
  return;
2606
2639
  }
2640
+ const openBranch = resolved.branch;
2607
2641
  const result = await openPullRequest(
2608
2642
  resolved.context,
2609
2643
  resolved.repo,
2610
2644
  resolved.pat,
2611
- resolved.branch,
2645
+ openBranch,
2612
2646
  title,
2613
2647
  description
2614
2648
  );
@@ -2641,27 +2675,52 @@ ${result.pullRequest.url ?? "\u2014"}
2641
2675
  }
2642
2676
  function createPrCommentsCommand() {
2643
2677
  const command = new Command12("comments");
2644
- command.description("List active pull request comments for the current branch").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--json", "output JSON").action(async (options) => {
2678
+ command.description("List pull request comment threads for the current branch").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--pr-number <N>", "target the pull request with this numeric id, instead of the current branch's PR").option("--hide-resolved", "hide threads whose status is resolved / won't fix / closed / by design").option("--json", "output JSON").action(async (options) => {
2645
2679
  validateOrgProjectPair(options);
2646
2680
  let context;
2647
- try {
2648
- const resolved = await resolvePrCommandContext(options);
2649
- context = resolved.context;
2650
- const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
2651
- status: "active"
2652
- });
2653
- if (pullRequests.length === 0) {
2654
- writeError(`No active pull request found for branch ${resolved.branch}.`);
2681
+ let explicitPrId = null;
2682
+ if (options.prNumber !== void 0) {
2683
+ explicitPrId = parsePositivePrNumber(options.prNumber);
2684
+ if (explicitPrId === null) {
2685
+ writeError(`Invalid --pr-number "${options.prNumber}"; expected a positive integer.`);
2655
2686
  return;
2656
2687
  }
2657
- if (pullRequests.length > 1) {
2658
- const ids = pullRequests.map((pullRequest2) => `#${pullRequest2.id}`).join(", ");
2659
- writeError(`Multiple active pull requests found for branch ${resolved.branch}: ${ids}. Use pr status to review them.`);
2660
- return;
2688
+ }
2689
+ try {
2690
+ const resolved = await resolvePrCommandContext(options, { requireBranch: explicitPrId === null });
2691
+ context = resolved.context;
2692
+ let pullRequest;
2693
+ let branchLabel;
2694
+ if (explicitPrId !== null) {
2695
+ try {
2696
+ pullRequest = await getPullRequestById(resolved.context, resolved.repo, resolved.pat, explicitPrId);
2697
+ } catch (err) {
2698
+ if (err instanceof Error && err.message.startsWith("NOT_FOUND")) {
2699
+ writeError(`Pull request #${explicitPrId} not found in ${resolved.context.org}/${resolved.context.project}/${resolved.repo}.`);
2700
+ return;
2701
+ }
2702
+ throw err;
2703
+ }
2704
+ branchLabel = resolved.branch ?? pullRequest.sourceRefName;
2705
+ } else {
2706
+ const pullRequests = await listPullRequests(resolved.context, resolved.repo, resolved.pat, resolved.branch, {
2707
+ status: "active"
2708
+ });
2709
+ if (pullRequests.length === 0) {
2710
+ writeError(`No active pull request found for branch ${resolved.branch}.`);
2711
+ return;
2712
+ }
2713
+ if (pullRequests.length > 1) {
2714
+ const ids = pullRequests.map((pr) => `#${pr.id}`).join(", ");
2715
+ writeError(`Multiple active pull requests found for branch ${resolved.branch}: ${ids}. Use pr status to review them.`);
2716
+ return;
2717
+ }
2718
+ pullRequest = pullRequests[0];
2719
+ branchLabel = resolved.branch;
2661
2720
  }
2662
- const pullRequest = pullRequests[0];
2663
- const threads = await getPullRequestThreads(resolved.context, resolved.repo, resolved.pat, pullRequest.id);
2664
- const result = { branch: resolved.branch, pullRequest, threads };
2721
+ const allThreads = await getPullRequestThreads(resolved.context, resolved.repo, resolved.pat, pullRequest.id);
2722
+ const threads = options.hideResolved ? allThreads.filter((thread) => !isThreadResolved(thread.status)) : allThreads;
2723
+ const result = { branch: branchLabel, pullRequest, threads };
2665
2724
  if (options.json) {
2666
2725
  process.stdout.write(`${JSON.stringify(result, null, 2)}
2667
2726
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azdo-cli",
3
- "version": "0.5.0-017-pr-comments-threads.240",
3
+ "version": "0.5.0-017-pr-comments-threads.244",
4
4
  "description": "Azure DevOps CLI tool",
5
5
  "type": "module",
6
6
  "bin": {