@blogic-cz/agent-tools 0.14.48 → 0.14.49

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "0.14.48",
3
+ "version": "0.14.49",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -33,6 +33,7 @@ import {
33
33
  prDiscussionSummaryCommand,
34
34
  prReplyCommand,
35
35
  prResolveCommand,
36
+ prReviewsCommand,
36
37
  prSubmitReviewCommand,
37
38
  prChecksCommand,
38
39
  prChecksFailedCommand,
@@ -79,6 +80,7 @@ const prCommand = Command.make("pr", {}).pipe(
79
80
  prWaitMergeableCommand,
80
81
  prThreadsCommand,
81
82
  prCommentsCommand,
83
+ prReviewsCommand,
82
84
  prIssueCommentsCommand,
83
85
  prIssueCommentsLatestCommand,
84
86
  prCommentCommand,
@@ -46,6 +46,7 @@ import {
46
46
  fetchDiscussionSummary,
47
47
  fetchIssueComments,
48
48
  fetchLatestIssueComment,
49
+ fetchReviews,
49
50
  fetchThreads,
50
51
  postIssueComment,
51
52
  replyToComment,
@@ -606,6 +607,49 @@ export const prCommentsCommand = Command.make(
606
607
  ),
607
608
  ).pipe(Command.withDescription("Fetch review comments for a PR (optionally filter by --since)"));
608
609
 
610
+ export const prReviewsCommand = Command.make(
611
+ "reviews",
612
+ {
613
+ author: Flag.string("author").pipe(
614
+ Flag.withDescription("Filter by author login substring"),
615
+ Flag.optional,
616
+ ),
617
+ bodyContains: Flag.string("body-contains").pipe(
618
+ Flag.withDescription("Filter reviews by body substring"),
619
+ Flag.optional,
620
+ ),
621
+ format: formatOption,
622
+ pr: Flag.integer("pr").pipe(
623
+ Flag.withDescription("PR number (default: current branch PR)"),
624
+ Flag.optional,
625
+ ),
626
+ repo: repoOption,
627
+ state: Flag.string("state").pipe(
628
+ Flag.withDescription(
629
+ "Filter by review state (APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED)",
630
+ ),
631
+ Flag.optional,
632
+ ),
633
+ },
634
+ ({ author, bodyContains, format, pr, repo, state }) =>
635
+ withRepo(
636
+ repo,
637
+ Effect.gen(function* () {
638
+ const reviews = yield* fetchReviews(
639
+ Option.getOrNull(pr),
640
+ Option.getOrNull(author),
641
+ Option.getOrNull(bodyContains),
642
+ Option.getOrNull(state),
643
+ );
644
+ yield* logFormatted(reviews, format);
645
+ }),
646
+ ),
647
+ ).pipe(
648
+ Command.withDescription(
649
+ "Fetch submitted review summaries (state + body) for a PR — the review bodies that comments/issue-comments do not expose",
650
+ ),
651
+ );
652
+
609
653
  export const prIssueCommentsCommand = Command.make(
610
654
  "issue-comments",
611
655
  {
@@ -15,6 +15,7 @@ export {
15
15
  prRerunChecksCommand,
16
16
  prReplyAndResolveCommand,
17
17
  prResolveCommand,
18
+ prReviewsCommand,
18
19
  prStatusCommand,
19
20
  prSubmitReviewCommand,
20
21
  prThreadsCommand,
@@ -5,6 +5,7 @@ import type {
5
5
  IssueComment,
6
6
  IssueCommentId,
7
7
  IsoTimestamp,
8
+ PullRequestReview,
8
9
  ReviewComment,
9
10
  ReviewThread,
10
11
  } from "#gh/types";
@@ -158,6 +159,15 @@ type RawIssueComment = {
158
159
  html_url: string;
159
160
  };
160
161
 
162
+ type RawPullRequestReview = {
163
+ id: number;
164
+ user: { login: string } | null;
165
+ state: string;
166
+ body: string | null;
167
+ submitted_at: string | null;
168
+ html_url: string;
169
+ };
170
+
161
171
  type ReviewCommentById = {
162
172
  id: number;
163
173
  in_reply_to_id: number | null;
@@ -405,6 +415,55 @@ export const fetchIssueComments = Effect.fn("pr.fetchIssueComments")(function* (
405
415
  return comments;
406
416
  });
407
417
 
418
+ /**
419
+ * Fetch submitted review summaries (state + top-level body) for a PR via REST API.
420
+ * Complements `comments` (inline review comments) and `issue-comments` (discussion),
421
+ * which never expose the submitted review body. Optional author/state/body filters.
422
+ */
423
+ export const fetchReviews = Effect.fn("pr.fetchReviews")(function* (
424
+ pr: number | null,
425
+ author: string | null,
426
+ bodyContains: string | null,
427
+ state: string | null,
428
+ ) {
429
+ const service = yield* GitHubService;
430
+ const repoInfo = yield* service.getRepoInfo();
431
+
432
+ const resolvedPr = pr ?? (yield* viewPR(null)).number;
433
+
434
+ const raw = yield* fetchAllRestPages<RawPullRequestReview>(
435
+ `repos/${repoInfo.owner}/${repoInfo.name}/pulls/${resolvedPr}/reviews`,
436
+ "gh-tool pr reviews",
437
+ "Failed to parse response",
438
+ );
439
+
440
+ let reviews: PullRequestReview[] = raw.map((review) => ({
441
+ id: review.id,
442
+ author: review.user?.login ?? "unknown",
443
+ state: review.state,
444
+ body: review.body ?? "",
445
+ submittedAt: (review.submitted_at ?? null) as IsoTimestamp | null,
446
+ url: review.html_url,
447
+ }));
448
+
449
+ if (author !== null) {
450
+ const authorFilter = author.toLowerCase();
451
+ reviews = reviews.filter((review) => review.author.toLowerCase().includes(authorFilter));
452
+ }
453
+
454
+ if (state !== null) {
455
+ const stateFilter = state.toLowerCase();
456
+ reviews = reviews.filter((review) => review.state.toLowerCase() === stateFilter);
457
+ }
458
+
459
+ if (bodyContains !== null) {
460
+ const bodyFilter = bodyContains.toLowerCase();
461
+ reviews = reviews.filter((review) => review.body.toLowerCase().includes(bodyFilter));
462
+ }
463
+
464
+ return reviews;
465
+ });
466
+
408
467
  export const fetchLatestIssueComment = Effect.fn("pr.fetchLatestIssueComment")(function* (
409
468
  pr: number | null,
410
469
  author: string | null,
@@ -66,6 +66,15 @@ export type IssueComment = {
66
66
  url: GitHubIssueCommentUrl;
67
67
  };
68
68
 
69
+ export type PullRequestReview = {
70
+ id: number;
71
+ author: string;
72
+ state: string;
73
+ body: string;
74
+ submittedAt: IsoTimestamp | null;
75
+ url: string;
76
+ };
77
+
69
78
  export type CheckResult = {
70
79
  name: string;
71
80
  state: string;