@blogic-cz/agent-tools 0.14.57 → 0.14.59
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/README.md +19 -0
- package/package.json +1 -1
- package/src/gh-tool/index.ts +2 -0
- package/src/gh-tool/issue/core.ts +2 -0
- package/src/gh-tool/pr/commands.ts +196 -38
- package/src/gh-tool/pr/core.ts +533 -81
- package/src/gh-tool/pr/index.ts +1 -0
- package/src/gh-tool/pr/review.ts +186 -12
- package/src/gh-tool/types.ts +93 -0
- package/src/gh-tool/workflow.ts +68 -9
package/README.md
CHANGED
|
@@ -225,6 +225,7 @@ bun audit-tool list --limit 20
|
|
|
225
225
|
|
|
226
226
|
```bash
|
|
227
227
|
bun gh-tool pr review-triage # interactive summary of PR feedback
|
|
228
|
+
bun gh-tool pr watch --prs 12,34 --format jsonl # transition-only multi-PR CI stream
|
|
228
229
|
bun k8s-tool pods --env test # list pods (structured command)
|
|
229
230
|
```
|
|
230
231
|
|
|
@@ -251,6 +252,24 @@ export default { handleToolExecuteBefore };
|
|
|
251
252
|
|
|
252
253
|
All tools support `--help` for full usage documentation. Legacy `agent-tools-*` binary names (e.g. `agent-tools-gh`) still work for backwards compatibility.
|
|
253
254
|
|
|
255
|
+
### gh-tool machine contracts
|
|
256
|
+
|
|
257
|
+
`pr view` adds `headSha` and `baseSha`; failed-check evidence adds the same SHA pair. Review summaries, inline comments, and threads add `commitSha` plus `feedbackOrigin`: `current_head` only for an exact `commitSha === headSha`, `pre_existing` for a different known SHA (not an obsolescence verdict), and `unknown` when either SHA is absent. Issue comments always use `commitSha: null` and `feedbackOrigin: unknown`. `review-triage` preserves existing fields and adds `inlineComments` plus per-kind `feedbackOriginCounts`; batch triage returns the same object per PR.
|
|
258
|
+
|
|
259
|
+
`pr watch --prs 12,34 --until terminal --format jsonl` accepts at most 50 unique, digits-only PR numbers and emits only JSONL state transitions. Identity uses `repo/pr/headSha/runId/attempt/jobId`; `checkId` is nullable and reserved for actual check-run IDs. State/bucket revisions emit even when identity stays stable; `supersedes` appears only when identity changes. Open PRs with no checks become terminal only after three stable empty snapshots, allowing bounded GitHub eventual consistency. `pr checks`, batch checks, triage, and batch triage keep stderr silent with `--format json`; JSONL watch is also informationally silent. Failures still return structured nonzero errors on stderr.
|
|
260
|
+
|
|
261
|
+
Useful flows:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
bun gh-tool pr checks-failed --pr 123 --with-logs --format json # includes diagnosis
|
|
265
|
+
bun gh-tool pr rerun-checks --pr 123 --failed-only --watch --timeout 600
|
|
266
|
+
bun gh-tool pr watch --prs 123,124 --format jsonl --timeout 600
|
|
267
|
+
bun gh-tool pr reply-and-resolve --comment-id 456 --body "Done" # infers PR and thread
|
|
268
|
+
# Optional --pr/--thread-id retain legacy flow and are validated before either mutation.
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Reruns preflight every target before mutation and fail closed with `evidence_unavailable` when attempt jobs or logs cannot be read. Failed jobs use one `gh run rerun RUN --failed` mutation per workflow run. Without `--watch`, output returns current attempt metadata immediately; with `--watch`, discovery and watching share one absolute `--timeout` deadline and report `discovery_timeout` or `watch_timeout` with latest attempt state. Repeated matching pre-test infrastructure failures return `escalation_required` without mutation. See [`skills/gh-tool/SKILL.md`](skills/gh-tool/SKILL.md) for operating guidance; this section is canonical for added output fields.
|
|
272
|
+
|
|
254
273
|
`audit-tool` reads the same SQLite file the wrappers write to. By default that file lives at `~/.agent-tools/audit.sqlite`, and you can override both path and retention per repo with the global `audit` config section.
|
|
255
274
|
|
|
256
275
|
## Audit Logging
|
package/package.json
CHANGED
package/src/gh-tool/index.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
prReviewTriageBatchCommand,
|
|
45
45
|
prReviewTriageCommand,
|
|
46
46
|
prWaitMergeableCommand,
|
|
47
|
+
prWatchCommand,
|
|
47
48
|
} from "./pr/index";
|
|
48
49
|
import { branchRenameCommand } from "./branch";
|
|
49
50
|
import {
|
|
@@ -94,6 +95,7 @@ const prCommand = Command.make("pr", {}).pipe(
|
|
|
94
95
|
prSubmitReviewCommand,
|
|
95
96
|
prChecksCommand,
|
|
96
97
|
prChecksFailedCommand,
|
|
98
|
+
prWatchCommand,
|
|
97
99
|
prRerunChecksCommand,
|
|
98
100
|
prReplyAndResolveCommand,
|
|
99
101
|
prReviewTriageCommand,
|
|
@@ -82,6 +82,8 @@ const fetchAllRestPages = Effect.fn("issue.fetchAllRestPages")(function* <T>(
|
|
|
82
82
|
|
|
83
83
|
const mapRawIssueComment = (comment: RawIssueComment): IssueComment => ({
|
|
84
84
|
id: comment.id as IssueCommentId,
|
|
85
|
+
commitSha: null,
|
|
86
|
+
feedbackOrigin: "unknown",
|
|
85
87
|
author: comment.user.login,
|
|
86
88
|
body: comment.body,
|
|
87
89
|
createdAt: comment.created_at as IsoTimestamp,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
|
|
30
30
|
import {
|
|
31
31
|
closePR,
|
|
32
|
+
collectWithStableState,
|
|
32
33
|
createPR,
|
|
33
34
|
detectPRStatus,
|
|
34
35
|
editPR,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
listPRs,
|
|
39
40
|
mergePR,
|
|
40
41
|
rerunChecks,
|
|
42
|
+
watchPRs,
|
|
41
43
|
viewPR,
|
|
42
44
|
waitForMergeable,
|
|
43
45
|
} from "./core";
|
|
@@ -52,6 +54,7 @@ import {
|
|
|
52
54
|
fetchThreads,
|
|
53
55
|
postIssueComment,
|
|
54
56
|
replyToComment,
|
|
57
|
+
replyAndResolveComment,
|
|
55
58
|
resolveThread,
|
|
56
59
|
submitPendingReview,
|
|
57
60
|
} from "./review";
|
|
@@ -95,25 +98,115 @@ export const classifyReviewTriage = (
|
|
|
95
98
|
return { status: reasons.length > 0 ? "needs_investigation" : "clear", reasons };
|
|
96
99
|
};
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
const MAX_BATCH_PRS = 50;
|
|
102
|
+
|
|
103
|
+
export const parsePrNumbers = (
|
|
104
|
+
input: string,
|
|
105
|
+
): Effect.Effect<readonly number[], GitHubCommandError> => {
|
|
106
|
+
const tokens = input.split(",").map((part) => part.trim());
|
|
107
|
+
if (tokens.some((token) => !/^\d+$/.test(token) || Number(token) < 1)) {
|
|
108
|
+
return Effect.fail(emptyBatchError(input));
|
|
109
|
+
}
|
|
110
|
+
const numbers = [...new Set(tokens.map(Number))];
|
|
111
|
+
if (numbers.length > MAX_BATCH_PRS) {
|
|
112
|
+
return Effect.fail(
|
|
113
|
+
new GitHubCommandError({
|
|
114
|
+
message: `--prs supports at most ${MAX_BATCH_PRS} unique PRs`,
|
|
115
|
+
command: "gh pr --prs",
|
|
116
|
+
exitCode: 1,
|
|
117
|
+
stderr: "",
|
|
118
|
+
hint: `Split the request into batches of at most ${MAX_BATCH_PRS} PRs.`,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return Effect.succeed(numbers);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const withStableHead = Effect.fn("pr.withStableHead")(function* <A, E, R>(
|
|
126
|
+
pr: number | null,
|
|
127
|
+
collect: (pr: number, headSha: string | null) => Effect.Effect<A, E, R>,
|
|
128
|
+
context: { operation: string; command: string },
|
|
129
|
+
) {
|
|
130
|
+
const initial = yield* viewPR(pr);
|
|
131
|
+
const snapshot = yield* collectWithStableState(
|
|
132
|
+
initial,
|
|
133
|
+
(info) => collect(info.number, info.headSha),
|
|
134
|
+
(info) => viewPR(info.number),
|
|
135
|
+
(before, after) => after.headSha === before.headSha,
|
|
136
|
+
);
|
|
137
|
+
if (snapshot !== null) return { info: snapshot.state, value: snapshot.value };
|
|
138
|
+
return yield* Effect.fail(
|
|
139
|
+
new GitHubCommandError({
|
|
140
|
+
message: `PR head changed repeatedly while collecting ${context.operation}`,
|
|
141
|
+
command: context.command,
|
|
142
|
+
exitCode: 1,
|
|
143
|
+
stderr: "",
|
|
144
|
+
nextCommand: context.command,
|
|
145
|
+
retryable: true,
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export const fetchCurrentThreads = (
|
|
151
|
+
pr: number | null,
|
|
152
|
+
unresolvedOnly: boolean,
|
|
153
|
+
visibleOpenOnly: boolean,
|
|
154
|
+
) =>
|
|
155
|
+
withStableHead(
|
|
156
|
+
pr,
|
|
157
|
+
(number, headSha) => fetchThreads(number, unresolvedOnly, visibleOpenOnly, headSha),
|
|
158
|
+
{ operation: "review threads", command: "gh-tool pr threads" },
|
|
159
|
+
).pipe(Effect.map(({ value }) => value));
|
|
160
|
+
|
|
161
|
+
export const fetchCurrentComments = (pr: number | null, since: string | null) =>
|
|
162
|
+
withStableHead(pr, (number, headSha) => fetchComments(number, since, headSha), {
|
|
163
|
+
operation: "review comments",
|
|
164
|
+
command: "gh-tool pr comments",
|
|
165
|
+
}).pipe(Effect.map(({ value }) => value));
|
|
166
|
+
|
|
167
|
+
export const fetchCurrentReviews = (
|
|
168
|
+
pr: number | null,
|
|
169
|
+
author: string | null,
|
|
170
|
+
bodyContains: string | null,
|
|
171
|
+
state: string | null,
|
|
172
|
+
) =>
|
|
173
|
+
withStableHead(
|
|
174
|
+
pr,
|
|
175
|
+
(number, headSha) => fetchReviews(number, author, bodyContains, state, headSha),
|
|
176
|
+
{ operation: "reviews", command: "gh-tool pr reviews" },
|
|
177
|
+
).pipe(Effect.map(({ value }) => value));
|
|
178
|
+
|
|
179
|
+
export const fetchCurrentFeedback = (pr: number | null) =>
|
|
180
|
+
withStableHead(pr, (number, headSha) => fetchFeedback(number, headSha), {
|
|
181
|
+
operation: "feedback",
|
|
182
|
+
command: "gh-tool pr feedback",
|
|
183
|
+
}).pipe(Effect.map(({ value }) => value));
|
|
184
|
+
|
|
185
|
+
const countFeedbackOrigins = (items: ReadonlyArray<{ feedbackOrigin: string }>) => ({
|
|
186
|
+
current_head: items.filter((item) => item.feedbackOrigin === "current_head").length,
|
|
187
|
+
pre_existing: items.filter((item) => item.feedbackOrigin === "pre_existing").length,
|
|
188
|
+
unknown: items.filter((item) => item.feedbackOrigin === "unknown").length,
|
|
189
|
+
});
|
|
103
190
|
|
|
104
191
|
export const fetchReviewTriage = Effect.fn("pr.fetchReviewTriage")(function* (
|
|
105
192
|
prNumber: number | null,
|
|
193
|
+
format: "toon" | "json" = "toon",
|
|
106
194
|
) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
195
|
+
const { info, value } = yield* withStableHead(
|
|
196
|
+
prNumber,
|
|
197
|
+
(number, headSha) =>
|
|
198
|
+
Effect.all([
|
|
199
|
+
fetchThreads(number, false, false, headSha),
|
|
200
|
+
fetchDiscussionSummary(number),
|
|
201
|
+
fetchChecks(number, false, false, 0, format === "json"),
|
|
202
|
+
fetchReviews(number, null, null, null, headSha),
|
|
203
|
+
fetchComments(number, null, headSha),
|
|
204
|
+
]),
|
|
205
|
+
{ operation: "review triage", command: "gh-tool pr review-triage" },
|
|
116
206
|
);
|
|
207
|
+
const [allThreads, summary, checks, reviews, inlineComments] = value;
|
|
208
|
+
const unresolvedThreads = allThreads.filter((thread) => !thread.isResolved);
|
|
209
|
+
const visibleOpenThreads = allThreads.filter((thread) => thread.isVisibleOpen);
|
|
117
210
|
const classification = classifyReviewTriage(summary, checks);
|
|
118
211
|
|
|
119
212
|
// Single merge-readiness verdict so agents stop re-stitching mergeable + checks + threads +
|
|
@@ -142,6 +235,12 @@ export const fetchReviewTriage = Effect.fn("pr.fetchReviewTriage")(function* (
|
|
|
142
235
|
summary,
|
|
143
236
|
checks,
|
|
144
237
|
reviews,
|
|
238
|
+
inlineComments,
|
|
239
|
+
feedbackOriginCounts: {
|
|
240
|
+
reviews: countFeedbackOrigins(reviews),
|
|
241
|
+
inlineComments: countFeedbackOrigins(inlineComments),
|
|
242
|
+
threads: countFeedbackOrigins(allThreads),
|
|
243
|
+
},
|
|
145
244
|
};
|
|
146
245
|
});
|
|
147
246
|
|
|
@@ -165,8 +264,7 @@ export const prViewCommand = Command.make(
|
|
|
165
264
|
Effect.gen(function* () {
|
|
166
265
|
const batch = Option.getOrNull(prs);
|
|
167
266
|
if (batch !== null) {
|
|
168
|
-
const numbers = parsePrNumbers(batch);
|
|
169
|
-
if (numbers.length === 0) return yield* emptyBatchError(batch);
|
|
267
|
+
const numbers = yield* parsePrNumbers(batch);
|
|
170
268
|
const results = yield* Effect.all(
|
|
171
269
|
numbers.map((n) => viewPR(n).pipe(Effect.map((info) => ({ pr: n, info })))),
|
|
172
270
|
{ concurrency: 5 },
|
|
@@ -485,15 +583,16 @@ export const prChecksCommand = Command.make(
|
|
|
485
583
|
const batch = Option.getOrNull(prs);
|
|
486
584
|
if (batch !== null) {
|
|
487
585
|
if (watch) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
586
|
+
if (format !== "json") {
|
|
587
|
+
yield* Console.warn(
|
|
588
|
+
"ℹ️ --watch is ignored with --prs; batch mode returns a one-shot snapshot per PR.",
|
|
589
|
+
);
|
|
590
|
+
}
|
|
491
591
|
}
|
|
492
|
-
const numbers = parsePrNumbers(batch);
|
|
493
|
-
if (numbers.length === 0) return yield* emptyBatchError(batch);
|
|
592
|
+
const numbers = yield* parsePrNumbers(batch);
|
|
494
593
|
const results = yield* Effect.all(
|
|
495
594
|
numbers.map((n) =>
|
|
496
|
-
fetchChecks(n, false, failFast, timeout).pipe(
|
|
595
|
+
fetchChecks(n, false, failFast, timeout, format === "json").pipe(
|
|
497
596
|
Effect.map((checks) => ({ pr: n, checks })),
|
|
498
597
|
),
|
|
499
598
|
),
|
|
@@ -502,7 +601,13 @@ export const prChecksCommand = Command.make(
|
|
|
502
601
|
yield* logFormatted({ count: results.length, prs: results }, format);
|
|
503
602
|
return;
|
|
504
603
|
}
|
|
505
|
-
const checks = yield* fetchChecksForCommand(
|
|
604
|
+
const checks = yield* fetchChecksForCommand(
|
|
605
|
+
Option.getOrNull(pr),
|
|
606
|
+
watch,
|
|
607
|
+
failFast,
|
|
608
|
+
timeout,
|
|
609
|
+
format === "json",
|
|
610
|
+
);
|
|
506
611
|
yield* logFormatted(checks, format);
|
|
507
612
|
}),
|
|
508
613
|
),
|
|
@@ -539,6 +644,42 @@ export const prChecksFailedCommand = Command.make(
|
|
|
539
644
|
),
|
|
540
645
|
).pipe(Command.withDescription("Fetch only failed CI checks for a PR"));
|
|
541
646
|
|
|
647
|
+
export const prWatchCommand = Command.make(
|
|
648
|
+
"watch",
|
|
649
|
+
{
|
|
650
|
+
prs: Flag.string("prs").pipe(Flag.withDescription("Comma-separated PR numbers")),
|
|
651
|
+
until: Flag.choice("until", ["terminal"]).pipe(Flag.withDefault("terminal")),
|
|
652
|
+
format: Flag.choice("format", ["jsonl"]).pipe(Flag.withDefault("jsonl")),
|
|
653
|
+
interval: Flag.integer("interval").pipe(
|
|
654
|
+
Flag.withDefault(5),
|
|
655
|
+
Flag.filter(
|
|
656
|
+
(n) => n >= 1 && n <= 60,
|
|
657
|
+
() => "--interval must be 1..60 seconds",
|
|
658
|
+
),
|
|
659
|
+
),
|
|
660
|
+
timeout: Flag.integer("timeout").pipe(
|
|
661
|
+
Flag.withDefault(CI_CHECK_WATCH_TIMEOUT_MS / 1000),
|
|
662
|
+
Flag.filter(
|
|
663
|
+
(n) => n >= 1,
|
|
664
|
+
() => "--timeout must be at least 1 second",
|
|
665
|
+
),
|
|
666
|
+
),
|
|
667
|
+
repo: repoOption,
|
|
668
|
+
},
|
|
669
|
+
({ prs, until, interval, timeout, repo }) =>
|
|
670
|
+
withRepo(
|
|
671
|
+
repo,
|
|
672
|
+
Effect.gen(function* () {
|
|
673
|
+
const numbers = yield* parsePrNumbers(prs);
|
|
674
|
+
yield* watchPRs(
|
|
675
|
+
numbers,
|
|
676
|
+
{ intervalSeconds: interval, timeoutSeconds: timeout, until },
|
|
677
|
+
(event) => Console.log(JSON.stringify(event)),
|
|
678
|
+
);
|
|
679
|
+
}),
|
|
680
|
+
),
|
|
681
|
+
).pipe(Command.withDescription("Watch several PRs; emits JSONL state transitions only"));
|
|
682
|
+
|
|
542
683
|
export const prRerunChecksCommand = Command.make(
|
|
543
684
|
"rerun-checks",
|
|
544
685
|
{
|
|
@@ -552,13 +693,24 @@ export const prRerunChecksCommand = Command.make(
|
|
|
552
693
|
Flag.withDefault(true),
|
|
553
694
|
Flag.withDescription("Only rerun failed checks (default: true)"),
|
|
554
695
|
),
|
|
696
|
+
watch: Flag.boolean("watch").pipe(Flag.withDefault(false)),
|
|
697
|
+
timeout: Flag.integer("timeout").pipe(
|
|
698
|
+
Flag.withDefault(60),
|
|
699
|
+
Flag.filter(
|
|
700
|
+
(n) => n >= 1,
|
|
701
|
+
() => "--timeout must be at least 1 second",
|
|
702
|
+
),
|
|
703
|
+
),
|
|
555
704
|
},
|
|
556
|
-
({ failedOnly, format, pr, repo }) =>
|
|
705
|
+
({ failedOnly, format, pr, repo, watch, timeout }) =>
|
|
557
706
|
withRepo(
|
|
558
707
|
repo,
|
|
559
708
|
Effect.gen(function* () {
|
|
560
709
|
const prNumber = Option.getOrNull(pr);
|
|
561
|
-
const result = yield* rerunChecks(prNumber, failedOnly
|
|
710
|
+
const result = yield* rerunChecks(prNumber, failedOnly, {
|
|
711
|
+
watch,
|
|
712
|
+
timeoutSeconds: timeout,
|
|
713
|
+
});
|
|
562
714
|
yield* logFormatted(result, format);
|
|
563
715
|
}),
|
|
564
716
|
),
|
|
@@ -591,7 +743,7 @@ export const prThreadsCommand = Command.make(
|
|
|
591
743
|
repo,
|
|
592
744
|
Effect.gen(function* () {
|
|
593
745
|
const prNumber = Option.getOrNull(pr);
|
|
594
|
-
const threads = yield*
|
|
746
|
+
const threads = yield* fetchCurrentThreads(prNumber, unresolvedOnly, visibleOpenOnly);
|
|
595
747
|
yield* logFormatted(threads, format);
|
|
596
748
|
}),
|
|
597
749
|
),
|
|
@@ -621,7 +773,7 @@ export const prCommentsCommand = Command.make(
|
|
|
621
773
|
Effect.gen(function* () {
|
|
622
774
|
const prNumber = Option.getOrNull(pr);
|
|
623
775
|
const sinceValue = Option.getOrNull(since);
|
|
624
|
-
const comments = yield*
|
|
776
|
+
const comments = yield* fetchCurrentComments(prNumber, sinceValue);
|
|
625
777
|
yield* logFormatted(comments, format);
|
|
626
778
|
}),
|
|
627
779
|
),
|
|
@@ -679,7 +831,7 @@ export const prReviewsCommand = Command.make(
|
|
|
679
831
|
withRepo(
|
|
680
832
|
repo,
|
|
681
833
|
Effect.gen(function* () {
|
|
682
|
-
const reviews = yield*
|
|
834
|
+
const reviews = yield* fetchCurrentReviews(
|
|
683
835
|
Option.getOrNull(pr),
|
|
684
836
|
Option.getOrNull(author),
|
|
685
837
|
Option.getOrNull(bodyContains),
|
|
@@ -708,7 +860,7 @@ export const prFeedbackCommand = Command.make(
|
|
|
708
860
|
withRepo(
|
|
709
861
|
repo,
|
|
710
862
|
Effect.gen(function* () {
|
|
711
|
-
const feedback = yield*
|
|
863
|
+
const feedback = yield* fetchCurrentFeedback(Option.getOrNull(pr));
|
|
712
864
|
yield* logFormatted(feedback, format);
|
|
713
865
|
}),
|
|
714
866
|
),
|
|
@@ -971,7 +1123,7 @@ export const prReviewTriageCommand = Command.make(
|
|
|
971
1123
|
repo,
|
|
972
1124
|
Effect.gen(function* () {
|
|
973
1125
|
const prNumber = Option.getOrNull(pr);
|
|
974
|
-
const result = yield* fetchReviewTriage(prNumber);
|
|
1126
|
+
const result = yield* fetchReviewTriage(prNumber, format);
|
|
975
1127
|
yield* logFormatted(result, format);
|
|
976
1128
|
}),
|
|
977
1129
|
),
|
|
@@ -992,10 +1144,9 @@ export const prReviewTriageBatchCommand = Command.make(
|
|
|
992
1144
|
withRepo(
|
|
993
1145
|
repo,
|
|
994
1146
|
Effect.gen(function* () {
|
|
995
|
-
const numbers = parsePrNumbers(prs);
|
|
996
|
-
if (numbers.length === 0) return yield* emptyBatchError(prs);
|
|
1147
|
+
const numbers = yield* parsePrNumbers(prs);
|
|
997
1148
|
const results = yield* Effect.all(
|
|
998
|
-
numbers.map((prNumber) => fetchReviewTriage(prNumber)),
|
|
1149
|
+
numbers.map((prNumber) => fetchReviewTriage(prNumber, format)),
|
|
999
1150
|
{ concurrency: "unbounded" },
|
|
1000
1151
|
);
|
|
1001
1152
|
yield* logFormatted(results, format);
|
|
@@ -1025,7 +1176,10 @@ export const prReplyAndResolveCommand = Command.make(
|
|
|
1025
1176
|
),
|
|
1026
1177
|
repo: repoOption,
|
|
1027
1178
|
threadId: Flag.string("thread-id").pipe(
|
|
1028
|
-
Flag.withDescription(
|
|
1179
|
+
Flag.withDescription(
|
|
1180
|
+
"GraphQL node ID of the thread to resolve (inferred from comment when omitted)",
|
|
1181
|
+
),
|
|
1182
|
+
Flag.optional,
|
|
1029
1183
|
),
|
|
1030
1184
|
},
|
|
1031
1185
|
({ body, bodyFile, commentId, format, pr, repo, threadId }) =>
|
|
@@ -1041,13 +1195,17 @@ export const prReplyAndResolveCommand = Command.make(
|
|
|
1041
1195
|
fileFlag: "--body-file",
|
|
1042
1196
|
label: "body",
|
|
1043
1197
|
});
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1198
|
+
const result = yield* replyAndResolveComment(
|
|
1199
|
+
prNumber,
|
|
1200
|
+
commentId,
|
|
1201
|
+
Option.getOrNull(threadId),
|
|
1202
|
+
resolvedBody,
|
|
1203
|
+
);
|
|
1204
|
+
yield* logFormatted(result, format);
|
|
1047
1205
|
}),
|
|
1048
1206
|
),
|
|
1049
1207
|
).pipe(
|
|
1050
1208
|
Command.withDescription(
|
|
1051
|
-
"Composite: reply to a review comment and resolve its thread
|
|
1209
|
+
"Composite: reply to a review comment and resolve its thread (PR/thread inferred from comment)",
|
|
1052
1210
|
),
|
|
1053
1211
|
);
|