poe-code 3.0.337 → 3.0.339
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/dist/cli/program.js +19 -0
- package/dist/cli/program.js.map +1 -1
- package/dist/index.js +95 -25
- package/dist/index.js.map +3 -3
- package/dist/metafile.json +1 -1
- package/dist/providers/poe-agent.js +75 -24
- package/dist/providers/poe-agent.js.map +3 -3
- package/package.json +1 -1
- package/packages/github-review/dist/diff.d.ts +3 -0
- package/packages/github-review/dist/diff.js +22 -3
- package/packages/github-review/dist/gh.js +8 -7
- package/packages/github-review/dist/pr-url.js +8 -5
- package/packages/github-review/dist/review-history.js +23 -2
- package/packages/github-review/dist/review.js +12 -9
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export interface ReviewInlineComment {
|
|
2
2
|
path: string;
|
|
3
3
|
line: number;
|
|
4
|
+
side?: "LEFT" | "RIGHT";
|
|
4
5
|
body: string;
|
|
5
6
|
}
|
|
6
7
|
export interface ReviewDiffLine {
|
|
@@ -23,6 +24,8 @@ export interface ReviewDiffContext {
|
|
|
23
24
|
files: ReviewDiffFile[];
|
|
24
25
|
fileOrder: Map<string, number>;
|
|
25
26
|
reviewableLinesByPath: Map<string, Set<number>>;
|
|
27
|
+
leftLinesByPath: Map<string, Set<number>>;
|
|
28
|
+
rightContextLinesByPath: Map<string, Set<number>>;
|
|
26
29
|
rendered: string;
|
|
27
30
|
}
|
|
28
31
|
export declare function parseReviewDiff(diffText: string): ReviewDiffContext;
|
|
@@ -121,6 +121,8 @@ export function parseReviewDiff(diffText) {
|
|
|
121
121
|
status: "modified",
|
|
122
122
|
hunks: [],
|
|
123
123
|
reviewableLines: [],
|
|
124
|
+
leftLineSet: new Set(),
|
|
125
|
+
rightContextLineSet: new Set(),
|
|
124
126
|
reviewableLineSet: new Set()
|
|
125
127
|
};
|
|
126
128
|
continue;
|
|
@@ -173,11 +175,13 @@ export function parseReviewDiff(diffText) {
|
|
|
173
175
|
}
|
|
174
176
|
if (line.startsWith("-") && !line.startsWith("---")) {
|
|
175
177
|
currentHunk.lines.push({ side: "LEFT", line: oldLine, text: line });
|
|
178
|
+
currentFile.leftLineSet.add(oldLine);
|
|
176
179
|
oldLine += 1;
|
|
177
180
|
continue;
|
|
178
181
|
}
|
|
179
182
|
if (line.startsWith(" ")) {
|
|
180
183
|
currentHunk.lines.push({ side: "RIGHT", line: newLine, text: line });
|
|
184
|
+
currentFile.rightContextLineSet.add(newLine);
|
|
181
185
|
currentFile.reviewableLineSet.add(newLine);
|
|
182
186
|
oldLine += 1;
|
|
183
187
|
newLine += 1;
|
|
@@ -195,6 +199,8 @@ export function parseReviewDiff(diffText) {
|
|
|
195
199
|
files: publicFiles,
|
|
196
200
|
fileOrder: new Map(publicFiles.map((file, index) => [file.path, index])),
|
|
197
201
|
reviewableLinesByPath: new Map(files.map((file) => [file.path, file.reviewableLineSet])),
|
|
202
|
+
leftLinesByPath: new Map(files.map((file) => [file.path, file.leftLineSet])),
|
|
203
|
+
rightContextLinesByPath: new Map(files.map((file) => [file.path, file.rightContextLineSet])),
|
|
198
204
|
rendered: publicFiles.map(renderReviewFile).join("\n\n")
|
|
199
205
|
};
|
|
200
206
|
}
|
|
@@ -202,18 +208,31 @@ export function validateInlineComments(comments, context) {
|
|
|
202
208
|
const accepted = [];
|
|
203
209
|
const seen = new Set();
|
|
204
210
|
for (const comment of comments) {
|
|
205
|
-
const path = comment.path
|
|
211
|
+
const path = comment.path;
|
|
206
212
|
const body = comment.body.trim();
|
|
207
|
-
if (
|
|
213
|
+
if (path.length === 0 || !body || !Number.isInteger(comment.line) || comment.line < 1) {
|
|
208
214
|
throw new Error("Inline review comments require path, positive line, and body.");
|
|
209
215
|
}
|
|
216
|
+
if (comment.side === "LEFT") {
|
|
217
|
+
throw new Error(`Inline comment target ${path}:${comment.line} is a left-side diff target; only right-side comments are supported.`);
|
|
218
|
+
}
|
|
210
219
|
if (!context.reviewableLinesByPath.get(path)?.has(comment.line)) {
|
|
211
220
|
throw new Error(`Inline comment target ${path}:${comment.line} is not a valid right-side diff target.`);
|
|
212
221
|
}
|
|
222
|
+
if (!comment.side &&
|
|
223
|
+
context.leftLinesByPath.get(path)?.has(comment.line) &&
|
|
224
|
+
context.rightContextLinesByPath.get(path)?.has(comment.line)) {
|
|
225
|
+
throw new Error(`Inline comment target ${path}:${comment.line} is ambiguous; specify side RIGHT to target the right-side diff line.`);
|
|
226
|
+
}
|
|
213
227
|
const key = `${path}\0${comment.line}\0${body}`;
|
|
214
228
|
if (!seen.has(key)) {
|
|
215
229
|
seen.add(key);
|
|
216
|
-
accepted.push({
|
|
230
|
+
accepted.push({
|
|
231
|
+
path,
|
|
232
|
+
line: comment.line,
|
|
233
|
+
...(comment.side ? { side: comment.side } : {}),
|
|
234
|
+
body
|
|
235
|
+
});
|
|
217
236
|
}
|
|
218
237
|
}
|
|
219
238
|
accepted.sort((left, right) => {
|
|
@@ -54,12 +54,13 @@ function parseJson(stdout, action) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
export function ghPrView(prUrl, fields, options = {}) {
|
|
57
|
-
requirePullRequestRef(prUrl);
|
|
57
|
+
const ref = requirePullRequestRef(prUrl);
|
|
58
|
+
const canonicalPrUrl = ref.url;
|
|
58
59
|
const requestedFields = dedupe([...fields]);
|
|
59
|
-
const result = runGh(["pr", "view",
|
|
60
|
+
const result = runGh(["pr", "view", canonicalPrUrl, "--json", requestedFields.join(",")], options);
|
|
60
61
|
if (result.code === 0) {
|
|
61
62
|
const parsed = parseJsonRecord(result.stdout, "gh pr view");
|
|
62
|
-
parsed.url ??=
|
|
63
|
+
parsed.url ??= canonicalPrUrl;
|
|
63
64
|
return parsed;
|
|
64
65
|
}
|
|
65
66
|
const availableFields = parseGhAvailableFields(result.stderr);
|
|
@@ -67,17 +68,17 @@ export function ghPrView(prUrl, fields, options = {}) {
|
|
|
67
68
|
if (fallbackFields.length === 0 || fallbackFields.length === requestedFields.length) {
|
|
68
69
|
throw new Error(result.stderr.trim() || result.stdout.trim() || "gh pr view failed");
|
|
69
70
|
}
|
|
70
|
-
const fallback = runGh(["pr", "view",
|
|
71
|
+
const fallback = runGh(["pr", "view", canonicalPrUrl, "--json", fallbackFields.join(",")], options);
|
|
71
72
|
if (fallback.code !== 0) {
|
|
72
73
|
throw new Error(fallback.stderr.trim() || fallback.stdout.trim() || "gh pr view failed");
|
|
73
74
|
}
|
|
74
75
|
const parsed = parseJsonRecord(fallback.stdout, "gh pr view");
|
|
75
|
-
parsed.url ??=
|
|
76
|
+
parsed.url ??= canonicalPrUrl;
|
|
76
77
|
return parsed;
|
|
77
78
|
}
|
|
78
79
|
export function ghPrDiff(prUrl, options = {}) {
|
|
79
|
-
requirePullRequestRef(prUrl);
|
|
80
|
-
return runGhOrThrow(["pr", "diff",
|
|
80
|
+
const ref = requirePullRequestRef(prUrl);
|
|
81
|
+
return runGhOrThrow(["pr", "diff", ref.url], options);
|
|
81
82
|
}
|
|
82
83
|
export function ghApiJson(prUrl, args, payload, options = {}) {
|
|
83
84
|
const ref = requirePullRequestRef(prUrl);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
function parseUrl(value) {
|
|
2
2
|
try {
|
|
3
|
-
return new URL(value);
|
|
3
|
+
return new URL(value.trim());
|
|
4
4
|
}
|
|
5
5
|
catch {
|
|
6
6
|
return null;
|
|
@@ -22,6 +22,9 @@ function parsePathPart(value) {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
+
function formatPullRequestUrl(ref) {
|
|
26
|
+
return `https://${ref.host}/${encodeURIComponent(ref.owner)}/${encodeURIComponent(ref.repo)}/pull/${ref.number}`;
|
|
27
|
+
}
|
|
25
28
|
export function parseGitHubPullRequestRef(prUrl) {
|
|
26
29
|
const parsed = parseUrl(prUrl);
|
|
27
30
|
if (!parsed ||
|
|
@@ -41,18 +44,18 @@ export function parseGitHubPullRequestRef(prUrl) {
|
|
|
41
44
|
if (!owner || !repo || !Number.isSafeInteger(number) || number <= 0) {
|
|
42
45
|
return null;
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
const ref = {
|
|
45
48
|
host: parsed.host.toLowerCase(),
|
|
46
49
|
owner,
|
|
47
50
|
repo,
|
|
48
|
-
number
|
|
49
|
-
url: prUrl
|
|
51
|
+
number
|
|
50
52
|
};
|
|
53
|
+
return { ...ref, url: formatPullRequestUrl(ref) };
|
|
51
54
|
}
|
|
52
55
|
export function canonicalPullRequestUrl(prUrl) {
|
|
53
56
|
const ref = parseGitHubPullRequestRef(prUrl);
|
|
54
57
|
if (!ref) {
|
|
55
58
|
return prUrl;
|
|
56
59
|
}
|
|
57
|
-
return
|
|
60
|
+
return ref.url;
|
|
58
61
|
}
|
|
@@ -76,11 +76,32 @@ function isAtOrAfter(value, since) {
|
|
|
76
76
|
return false;
|
|
77
77
|
return new Date(value).getTime() >= new Date(since).getTime();
|
|
78
78
|
}
|
|
79
|
-
function
|
|
79
|
+
function rawCreatedAt(record, kind) {
|
|
80
80
|
return kind === "review_body"
|
|
81
81
|
? (text(record, "submitted_at") ?? text(record, "created_at"))
|
|
82
82
|
: (text(record, "created_at") ?? text(record, "submitted_at"));
|
|
83
83
|
}
|
|
84
|
+
function createdAt(record, kind) {
|
|
85
|
+
const value = rawCreatedAt(record, kind);
|
|
86
|
+
if (!value) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const timestamp = new Date(value);
|
|
90
|
+
if (Number.isNaN(timestamp.getTime())) {
|
|
91
|
+
throw new Error(`Invalid GitHub review-history ${kind} timestamp: ${value}`);
|
|
92
|
+
}
|
|
93
|
+
return timestamp.toISOString();
|
|
94
|
+
}
|
|
95
|
+
function dedupeRepos(repos) {
|
|
96
|
+
const deduped = new Map();
|
|
97
|
+
for (const repo of repos) {
|
|
98
|
+
const key = repo.toLowerCase();
|
|
99
|
+
if (!deduped.has(key)) {
|
|
100
|
+
deduped.set(key, repo);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [...deduped.values()];
|
|
104
|
+
}
|
|
84
105
|
class ReviewHistoryFetcher {
|
|
85
106
|
options;
|
|
86
107
|
runner;
|
|
@@ -314,7 +335,7 @@ export async function* fetchReviewHistory(options) {
|
|
|
314
335
|
(!Number.isInteger(options.maxComments) || options.maxComments < 1)) {
|
|
315
336
|
throw new Error("Review-history maxComments must be a positive integer.");
|
|
316
337
|
}
|
|
317
|
-
const repos =
|
|
338
|
+
const repos = dedupeRepos(options.repos);
|
|
318
339
|
for (const repo of repos) {
|
|
319
340
|
if (!validRepo(repo)) {
|
|
320
341
|
throw new Error(`Invalid GitHub repository name: ${repo}`);
|
|
@@ -35,18 +35,21 @@ function requirePositiveId(commentId) {
|
|
|
35
35
|
}
|
|
36
36
|
return normalized;
|
|
37
37
|
}
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
function normalizeReviewComments(comments) {
|
|
39
|
+
return comments.map((comment) => {
|
|
40
|
+
const path = comment.path.trim();
|
|
41
|
+
const body = comment.body.trim();
|
|
42
|
+
if (!path) {
|
|
41
43
|
throw new Error("Review comment path must be non-empty.");
|
|
42
44
|
}
|
|
43
45
|
if (!Number.isSafeInteger(comment.line) || comment.line <= 0) {
|
|
44
46
|
throw new Error("Review comment line must be a positive integer.");
|
|
45
47
|
}
|
|
46
|
-
if (!
|
|
48
|
+
if (!body) {
|
|
47
49
|
throw new Error("Review comment body must be non-empty.");
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
return { path, line: comment.line, body };
|
|
52
|
+
});
|
|
50
53
|
}
|
|
51
54
|
function submissionFromResponse(response) {
|
|
52
55
|
return {
|
|
@@ -59,8 +62,7 @@ export function submitPullRequestReview(input) {
|
|
|
59
62
|
if (!REVIEW_DECISIONS.has(input.decision)) {
|
|
60
63
|
throw new Error(`Invalid pull request review decision: ${input.decision}`);
|
|
61
64
|
}
|
|
62
|
-
const comments = input.comments ?? [];
|
|
63
|
-
validateReviewComments(comments);
|
|
65
|
+
const comments = normalizeReviewComments(input.comments ?? []);
|
|
64
66
|
const response = ghApiJson(prUrl, ["--method", "POST", reviewEndpoint(prUrl)], {
|
|
65
67
|
body: input.summary,
|
|
66
68
|
event: input.decision,
|
|
@@ -75,10 +77,11 @@ export function submitPullRequestReview(input) {
|
|
|
75
77
|
}
|
|
76
78
|
export function editPullRequestReviewComment(input) {
|
|
77
79
|
const prUrl = inputPullRequestUrl(input);
|
|
78
|
-
|
|
80
|
+
const body = input.body.trim();
|
|
81
|
+
if (!body) {
|
|
79
82
|
throw new Error("Review comment body must be non-empty.");
|
|
80
83
|
}
|
|
81
|
-
const response = ghApiJson(prUrl, ["--method", "PATCH", commentEndpoint(prUrl, input.commentId)], { body
|
|
84
|
+
const response = ghApiJson(prUrl, ["--method", "PATCH", commentEndpoint(prUrl, input.commentId)], { body }, input);
|
|
82
85
|
return submissionFromResponse(response);
|
|
83
86
|
}
|
|
84
87
|
export function deletePullRequestReviewComment(input) {
|