pr-shepherd 0.11.0 → 0.12.1
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/.claude-plugin/plugin.json +2 -2
- package/README.md +52 -10
- package/bin/agent-runtime.mjs +7 -0
- package/bin/cli/args.mjs +10 -2
- package/bin/cli/default-iterate.mjs +54 -0
- package/bin/cli/duration-flag.mjs +22 -0
- package/bin/cli/fix-formatter.mjs +14 -4
- package/bin/cli/formatters.mjs +10 -3
- package/bin/cli/handlers.mjs +24 -13
- package/bin/cli/iterate-formatter.mjs +14 -10
- package/bin/cli/iterate-instructions.mjs +75 -0
- package/bin/cli/iterate-lean.mjs +56 -6
- package/bin/cli/list-formatters.mjs +11 -0
- package/bin/cli-parser.iterate-fixtures.mjs +1 -0
- package/bin/cli-parser.mjs +15 -1
- package/bin/commands/check.mjs +5 -3
- package/bin/commands/iterate/classify.mjs +5 -4
- package/bin/commands/iterate/escalate.mjs +2 -1
- package/bin/commands/iterate/fix-code.mjs +11 -13
- package/bin/commands/iterate/index.mjs +1 -0
- package/bin/commands/iterate/render.mjs +13 -7
- package/bin/commands/iterate/stall.mjs +3 -1
- package/bin/commands/iterate.mjs +1 -1
- package/bin/commands/monitor.mjs +68 -17
- package/bin/commands/ready-delay.mjs +2 -1
- package/bin/commands/resolve-instructions.mjs +7 -3
- package/bin/commands/resolve.mjs +4 -4
- package/bin/github/http.mjs +6 -1
- package/bin/index.mjs +1 -0
- package/bin/reporters/check-instructions.mjs +9 -3
- package/bin/reporters/json.mjs +2 -2
- package/bin/reporters/text.mjs +24 -16
- package/package.json +3 -2
package/bin/commands/resolve.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import { loadConfig } from "../config/load.mjs";
|
|
|
6
6
|
import { extractSuggestion } from "../suggestions/extract.mjs";
|
|
7
7
|
import { buildFetchInstructions } from "./resolve-instructions.mjs";
|
|
8
8
|
import { loadSeenMap, markSeen, classifyItem } from "../state/seen-comments.mjs";
|
|
9
|
-
/** Fetch mode: auto-resolve outdated threads and return all active items for LLM triage. */
|
|
10
9
|
export async function runResolveFetch(opts) {
|
|
11
10
|
const repo = await getRepoInfo();
|
|
12
11
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
|
@@ -15,7 +14,7 @@ export async function runResolveFetch(opts) {
|
|
|
15
14
|
}
|
|
16
15
|
const { data } = await fetchPrBatch(prNumber, repo);
|
|
17
16
|
const stateKey = { owner: repo.owner, repo: repo.name, pr: prNumber };
|
|
18
|
-
const unresolvedThreads = data.reviewThreads.filter((t) => !t.isResolved
|
|
17
|
+
const unresolvedThreads = data.reviewThreads.filter((t) => !t.isResolved);
|
|
19
18
|
const visibleComments = data.comments.filter((c) => !c.isMinimized);
|
|
20
19
|
const outdatedCandidates = data.reviewThreads.filter((t) => t.isOutdated);
|
|
21
20
|
const resolvedCandidates = data.reviewThreads.filter((t) => t.isResolved && !t.isOutdated);
|
|
@@ -68,7 +67,8 @@ export async function runResolveFetch(opts) {
|
|
|
68
67
|
process.stderr.write(`pr-shepherd: auto-resolve outdated threads failed (continuing): ${errors.join(", ")}\n`);
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
|
-
const activeThreads = unresolvedThreads.filter((t) => !t.isOutdated);
|
|
70
|
+
const activeThreads = unresolvedThreads.filter((t) => !t.isOutdated && !t.isMinimized);
|
|
71
|
+
const resolutionOnlyThreads = unresolvedThreads.filter((t) => !autoResolvedIds.has(t.id) && (t.isOutdated || t.isMinimized));
|
|
72
72
|
const cfg = loadConfig();
|
|
73
73
|
const actionableThreads = activeThreads.map(({ isResolved: _r, isOutdated: _o, ...rest }) => {
|
|
74
74
|
const thread = rest;
|
|
@@ -118,6 +118,7 @@ export async function runResolveFetch(opts) {
|
|
|
118
118
|
const result = {
|
|
119
119
|
prNumber,
|
|
120
120
|
actionableThreads,
|
|
121
|
+
resolutionOnlyThreads,
|
|
121
122
|
firstLookThreads,
|
|
122
123
|
actionableComments: visibleComments,
|
|
123
124
|
firstLookComments,
|
|
@@ -127,7 +128,6 @@ export async function runResolveFetch(opts) {
|
|
|
127
128
|
};
|
|
128
129
|
return { ...result, instructions: buildFetchInstructions(prNumber, result) };
|
|
129
130
|
}
|
|
130
|
-
/** Mutation mode: resolve/minimize/dismiss by ID. */
|
|
131
131
|
export async function runResolveMutate(opts) {
|
|
132
132
|
const repo = await getRepoInfo();
|
|
133
133
|
const prNumber = opts.prNumber ?? (await getCurrentPrNumber());
|
package/bin/github/http.mjs
CHANGED
|
@@ -30,7 +30,12 @@ async function resolveToken() {
|
|
|
30
30
|
catch {
|
|
31
31
|
// fall through to error
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
const codexToken = process.env["GITHUB_PERSONAL_ACCESS_TOKEN"];
|
|
34
|
+
if (codexToken) {
|
|
35
|
+
_token = codexToken;
|
|
36
|
+
return _token;
|
|
37
|
+
}
|
|
38
|
+
throw new Error("No GitHub token found. Set GH_TOKEN, GITHUB_TOKEN, or GITHUB_PERSONAL_ACCESS_TOKEN, or run `gh auth login`.");
|
|
34
39
|
}
|
|
35
40
|
async function makeHeaders() {
|
|
36
41
|
return {
|
package/bin/index.mjs
CHANGED
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
* All rebase policy, CI budget policy, and ready-to-merge gating live here so the
|
|
4
4
|
* skill stays a thin dispatcher and these rules co-evolve with the CLI data model.
|
|
5
5
|
*/
|
|
6
|
-
export function buildCheckInstructions(report) {
|
|
6
|
+
export function buildCheckInstructions(report, opts) {
|
|
7
|
+
const runtime = opts?.runtime ?? "claude";
|
|
7
8
|
const { mergeStatus, checks, threads, comments, changesRequestedReviews, status } = report;
|
|
8
9
|
const instructions = [];
|
|
9
10
|
// 1. Summary
|
|
10
|
-
const totalActionable = threads.actionable.length +
|
|
11
|
+
const totalActionable = threads.actionable.length +
|
|
12
|
+
threads.resolutionOnly.length +
|
|
13
|
+
comments.actionable.length +
|
|
14
|
+
changesRequestedReviews.length;
|
|
11
15
|
const total = checks.passing.length +
|
|
12
16
|
checks.failing.length +
|
|
13
17
|
checks.inProgress.length +
|
|
@@ -54,7 +58,9 @@ export function buildCheckInstructions(report) {
|
|
|
54
58
|
}
|
|
55
59
|
// 5. Continuous monitoring pointer (suppressed only when truly ready to merge)
|
|
56
60
|
if (!isReady) {
|
|
57
|
-
instructions.push(
|
|
61
|
+
instructions.push(runtime === "codex"
|
|
62
|
+
? `This is a one-shot check. For follow-up monitoring, run \`npx pr-shepherd ${report.pr}\`.`
|
|
63
|
+
: "This is a one-shot check. For continuous monitoring that acts on these signals automatically, use `/pr-shepherd:monitor`.");
|
|
58
64
|
}
|
|
59
65
|
return instructions;
|
|
60
66
|
}
|
package/bin/reporters/json.mjs
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* without string-scraping the human-readable text reporter.
|
|
6
6
|
*/
|
|
7
7
|
import { buildCheckInstructions } from "./check-instructions.mjs";
|
|
8
|
-
export function formatJson(report) {
|
|
9
|
-
return JSON.stringify({ ...report, instructions: buildCheckInstructions(report) }, null, 2);
|
|
8
|
+
export function formatJson(report, opts) {
|
|
9
|
+
return JSON.stringify({ ...report, instructions: buildCheckInstructions(report, opts) }, null, 2);
|
|
10
10
|
}
|
package/bin/reporters/text.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildCheckInstructions } from "./check-instructions.mjs";
|
|
2
|
-
import { renderThreadBullet, renderCommentBullet,
|
|
2
|
+
import { renderThreadBullet, renderCommentBullet, renderFirstLookStatusTag, renderReviewListSection, renderThreadResolutionStatusTag, } from "../cli/list-formatters.mjs";
|
|
3
3
|
import { joinSections } from "../util/markdown.mjs";
|
|
4
|
-
export function formatText(report) {
|
|
4
|
+
export function formatText(report, opts) {
|
|
5
5
|
const header = [
|
|
6
6
|
`# PR #${report.pr} [CHECK] — ${report.repo}`,
|
|
7
7
|
`Status: ${report.status}`,
|
|
@@ -60,8 +60,11 @@ export function formatText(report) {
|
|
|
60
60
|
ciSubsections.push(lines.join("\n"));
|
|
61
61
|
}
|
|
62
62
|
const ciSection = `## CI Checks\n\n${ciSubsections.join("\n\n")}`;
|
|
63
|
-
const { actionable: actionableThreads, autoResolved, autoResolveErrors, firstLook: firstLookThreads, } = report.threads;
|
|
64
|
-
const hasThreadSection = autoResolved.length > 0 ||
|
|
63
|
+
const { actionable: actionableThreads, resolutionOnly: resolutionOnlyThreads, autoResolved, autoResolveErrors, firstLook: firstLookThreads, } = report.threads;
|
|
64
|
+
const hasThreadSection = autoResolved.length > 0 ||
|
|
65
|
+
autoResolveErrors.length > 0 ||
|
|
66
|
+
actionableThreads.length > 0 ||
|
|
67
|
+
resolutionOnlyThreads.length > 0;
|
|
65
68
|
let threadsSection = null;
|
|
66
69
|
if (hasThreadSection) {
|
|
67
70
|
const subparts = [];
|
|
@@ -86,6 +89,13 @@ export function formatText(report) {
|
|
|
86
89
|
}
|
|
87
90
|
subparts.push(lines.join("\n"));
|
|
88
91
|
}
|
|
92
|
+
if (resolutionOnlyThreads.length > 0) {
|
|
93
|
+
const lines = [`### Needs resolution only (${resolutionOnlyThreads.length})`, ""];
|
|
94
|
+
for (const t of resolutionOnlyThreads) {
|
|
95
|
+
lines.push(renderThreadBullet(t, { statusTag: renderThreadResolutionStatusTag(t) }));
|
|
96
|
+
}
|
|
97
|
+
subparts.push(lines.join("\n"));
|
|
98
|
+
}
|
|
89
99
|
threadsSection = `## Review Threads\n\n${subparts.join("\n\n")}`;
|
|
90
100
|
}
|
|
91
101
|
const { actionable: actionableComments, firstLook: firstLookComments } = report.comments;
|
|
@@ -97,15 +107,15 @@ export function formatText(report) {
|
|
|
97
107
|
}
|
|
98
108
|
commentsSection = `## PR Comments\n\n${lines.join("\n")}`;
|
|
99
109
|
}
|
|
100
|
-
const changesRequestedSection =
|
|
110
|
+
const changesRequestedSection = renderReviewListSection("CHANGES_REQUESTED Reviews", report.changesRequestedReviews);
|
|
101
111
|
const allSummaries = [...report.firstLookSummaries, ...report.reviewSummaries];
|
|
102
|
-
const reviewSummariesSection =
|
|
103
|
-
const approvedReviewsSection =
|
|
112
|
+
const reviewSummariesSection = renderReviewListSection("Review Summaries", allSummaries);
|
|
113
|
+
const approvedReviewsSection = renderReviewListSection("Approved Reviews", report.approvedReviews);
|
|
104
114
|
const firstLookTotal = firstLookThreads.length + firstLookComments.length;
|
|
105
115
|
let firstLookSection = null;
|
|
106
116
|
if (firstLookTotal > 0) {
|
|
107
117
|
const lines = [
|
|
108
|
-
`## First-look items (${firstLookTotal}) —
|
|
118
|
+
`## First-look items (${firstLookTotal}) — acknowledge status before acting`,
|
|
109
119
|
"",
|
|
110
120
|
];
|
|
111
121
|
for (const t of firstLookThreads) {
|
|
@@ -117,15 +127,18 @@ export function formatText(report) {
|
|
|
117
127
|
}
|
|
118
128
|
firstLookSection = lines.join("\n");
|
|
119
129
|
}
|
|
120
|
-
const totalActionable = actionableThreads.length +
|
|
130
|
+
const totalActionable = actionableThreads.length +
|
|
131
|
+
resolutionOnlyThreads.length +
|
|
132
|
+
actionableComments.length +
|
|
133
|
+
report.changesRequestedReviews.length;
|
|
121
134
|
const counts = [];
|
|
122
135
|
if (totalActionable > 0)
|
|
123
136
|
counts.push(`${totalActionable} actionable`);
|
|
124
137
|
if (firstLookTotal > 0)
|
|
125
138
|
counts.push(`${firstLookTotal} first-look`);
|
|
126
|
-
const summaryLine = counts.join(", ") || "0 actionable —
|
|
139
|
+
const summaryLine = counts.join(", ") || "0 actionable — no unresolved review items";
|
|
127
140
|
const summarySection = `## Summary\n\n${summaryLine}`;
|
|
128
|
-
const instructions = buildCheckInstructions(report);
|
|
141
|
+
const instructions = buildCheckInstructions(report, opts);
|
|
129
142
|
const instructionsSection = `## Instructions\n\n${instructions.map((step, i) => `${i + 1}. ${step}`).join("\n")}`;
|
|
130
143
|
return joinSections([
|
|
131
144
|
header,
|
|
@@ -141,8 +154,3 @@ export function formatText(report) {
|
|
|
141
154
|
instructionsSection,
|
|
142
155
|
]);
|
|
143
156
|
}
|
|
144
|
-
function reviewListSection(heading, items) {
|
|
145
|
-
if (items.length === 0)
|
|
146
|
-
return null;
|
|
147
|
-
return `## ${heading}\n\n${items.map((r) => renderReviewBullet(r, { includeBody: true })).join("\n")}`;
|
|
148
|
-
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pr-shepherd",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Autonomous PR CI monitor and review-comment resolver for
|
|
3
|
+
"version": "0.12.1",
|
|
4
|
+
"description": "Autonomous PR CI monitor and review-comment resolver for agentic coding tools",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonathan Ong",
|
|
7
7
|
"type": "module",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"ci",
|
|
50
50
|
"code-review",
|
|
51
51
|
"automation",
|
|
52
|
+
"codex",
|
|
52
53
|
"claude",
|
|
53
54
|
"claude-code"
|
|
54
55
|
],
|