pr-shepherd 0.15.2 → 0.16.0

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 (52) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/README.md +23 -89
  4. package/bin/cli/args.mjs +0 -1
  5. package/bin/cli/default-iterate.mjs +2 -7
  6. package/bin/cli/exit-codes.mjs +0 -4
  7. package/bin/cli/fix-formatter.mjs +8 -8
  8. package/bin/cli/handlers.mjs +3 -72
  9. package/bin/cli/iterate-formatter.mjs +8 -17
  10. package/bin/cli/iterate-instructions.mjs +13 -47
  11. package/bin/cli/iterate-lean.mjs +4 -11
  12. package/bin/cli/list-formatters.mjs +6 -3
  13. package/bin/cli-parser.iterate-fixtures.mjs +4 -7
  14. package/bin/cli-parser.mjs +4 -33
  15. package/bin/commands/check-status.mjs +2 -2
  16. package/bin/commands/check.mjs +7 -6
  17. package/bin/commands/commit-suggestion-instruction.mjs +23 -0
  18. package/bin/commands/iterate/check-instructions.mjs +1 -1
  19. package/bin/commands/iterate/classify.mjs +9 -4
  20. package/bin/commands/iterate/escalate.mjs +5 -6
  21. package/bin/commands/iterate/fix-code.mjs +3 -3
  22. package/bin/commands/iterate/helpers.mjs +0 -29
  23. package/bin/commands/iterate/index.mjs +18 -19
  24. package/bin/commands/iterate/render.mjs +5 -17
  25. package/bin/commands/resolve-instructions.mjs +2 -10
  26. package/bin/commands/resolve-mutate.mjs +16 -0
  27. package/bin/commands/resolve.mjs +6 -17
  28. package/bin/commands/shepherd-journal.mjs +1 -1
  29. package/bin/comments/minimize-policy.mjs +15 -0
  30. package/bin/comments/visible-comments.mjs +20 -0
  31. package/bin/config/load.mjs +10 -3
  32. package/bin/config.json +2 -3
  33. package/bin/github/batch-parsers.mjs +10 -0
  34. package/bin/github/gql/batch-pr.gql +6 -0
  35. package/bin/github/queries.mjs +0 -2
  36. package/bin/index.mjs +0 -2
  37. package/bin/merge-status/derive.mjs +6 -6
  38. package/bin/reporters/agent.mjs +10 -3
  39. package/bin/state/iterate-stall.mjs +9 -0
  40. package/package.json +5 -6
  41. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  42. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +31 -26
  43. package/bin/commands/iterate.mjs +0 -2
  44. package/bin/commands/monitor.mjs +0 -139
  45. package/bin/commands/status.mjs +0 -126
  46. package/bin/github/gql/multi-pr-status-paged.gql +0 -31
  47. package/bin/reporters/check-instructions.mjs +0 -69
  48. package/bin/reporters/json.mjs +0 -10
  49. package/bin/reporters/text.mjs +0 -156
  50. package/plugin/skills/check/SKILL.md +0 -37
  51. package/plugin/skills/monitor/SKILL.md +0 -40
  52. package/plugin/skills/resolve/SKILL.md +0 -38
@@ -1,126 +0,0 @@
1
- /**
2
- * `shepherd status PR1 [PR2 PR3 …]`
3
- *
4
- * Fetches readiness status for one or more PRs and prints a table.
5
- * Issues a single GraphQL request with one alias per PR number rather than
6
- * N separate requests, so the round-trip count is always 1 (plus optional
7
- * per-PR pagination calls when a PR has > 100 review threads).
8
- *
9
- * Exit code: 0 if all PRs are READY, non-zero otherwise.
10
- */
11
- import { graphql, getRepoInfo } from "../github/client.mjs";
12
- import { MULTI_PR_STATUS_QUERY_WITH_CURSOR } from "../github/queries.mjs";
13
- export async function runStatus(opts) {
14
- if (opts.prNumbers.length === 0)
15
- return [];
16
- const repo = await getRepoInfo();
17
- const doc = buildBatchStatusQuery(opts.prNumbers);
18
- const result = await graphql(doc, {
19
- owner: repo.owner,
20
- repo: repo.name,
21
- });
22
- const summaries = await Promise.all(opts.prNumbers.map((pr) => {
23
- const rawPr = result.data.repository[`pr_${pr}`];
24
- if (!rawPr) {
25
- throw new Error(`PR #${pr} not found in ${repo.owner}/${repo.name}`);
26
- }
27
- return paginateAndBuild(pr, rawPr, repo.owner, repo.name);
28
- }));
29
- return summaries;
30
- }
31
- // ---------------------------------------------------------------------------
32
- // Internal
33
- // ---------------------------------------------------------------------------
34
- function buildBatchStatusQuery(prNumbers) {
35
- const uniquePrs = [...new Set(prNumbers.filter((n) => n > 0))];
36
- const f = "number title state isDraft mergeStateStatus reviewDecision " +
37
- "reviewThreads(last:100){totalCount pageInfo{hasPreviousPage startCursor} nodes{isResolved}} " +
38
- "commits(last:1){nodes{commit{statusCheckRollup{state}}}}";
39
- const aliases = uniquePrs.map((n) => `pr_${n}:pullRequest(number:${n}){${f}}`).join(" ");
40
- return `query MultiPrStatusBatch($owner:String!,$repo:String!){repository(owner:$owner,name:$repo){${aliases}}}`;
41
- }
42
- async function paginateAndBuild(pr, p, owner, repo) {
43
- let allNodes = p.reviewThreads.nodes;
44
- if (p.reviewThreads.totalCount > p.reviewThreads.nodes.length) {
45
- const MAX_THREAD_PAGES = 10;
46
- let pagesFetched = 0;
47
- const totalCount = p.reviewThreads.totalCount;
48
- let cursor = p.reviewThreads.pageInfo?.startCursor ?? null;
49
- while (cursor !== null) {
50
- if (++pagesFetched > MAX_THREAD_PAGES) {
51
- process.stderr.write(`pr-shepherd: thread pagination cap reached for PR #${pr}: fetched ${allNodes.length} of ${totalCount} threads\n`);
52
- break;
53
- }
54
- // eslint-disable-next-line no-await-in-loop
55
- const extra = await graphql(MULTI_PR_STATUS_QUERY_WITH_CURSOR, {
56
- owner,
57
- repo,
58
- pr,
59
- cursor,
60
- });
61
- const p2 = extra.data.repository.pullRequest;
62
- if (!p2)
63
- break;
64
- allNodes = [...p2.reviewThreads.nodes, ...allNodes];
65
- if (!p2.reviewThreads.pageInfo?.hasPreviousPage || !p2.reviewThreads.pageInfo.startCursor) {
66
- break;
67
- }
68
- cursor = p2.reviewThreads.pageInfo.startCursor;
69
- }
70
- }
71
- const unresolvedThreads = allNodes.filter((n) => !n.isResolved).length;
72
- const ciState = p.commits.nodes[0]?.commit.statusCheckRollup?.state ?? null;
73
- const threadsTruncated = p.reviewThreads.totalCount > allNodes.length;
74
- return {
75
- number: p.number,
76
- title: p.title,
77
- state: p.state,
78
- isDraft: p.isDraft,
79
- mergeStateStatus: p.mergeStateStatus,
80
- reviewDecision: p.reviewDecision,
81
- unresolvedThreads,
82
- ciState,
83
- threadsTruncated,
84
- };
85
- }
86
- // ---------------------------------------------------------------------------
87
- // Output helpers
88
- // ---------------------------------------------------------------------------
89
- export function formatStatusTable(summaries, repoFull) {
90
- const heading = `# ${repoFull} — PR status (${summaries.length})`;
91
- if (summaries.length === 0)
92
- return heading;
93
- const rows = summaries.map((s) => {
94
- const raw = s.title.length > 50 ? `${s.title.slice(0, 47)}...` : s.title;
95
- const title = raw.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\n/g, " ");
96
- return `| #${s.number} | ${title} | ${deriveVerdict(s)} | ${s.ciState ?? "—"} |`;
97
- });
98
- const table = ["| PR | Title | Verdict | CI |", "| --- | --- | --- | --- |", ...rows].join("\n");
99
- const footnotes = summaries
100
- .filter((s) => s.threadsTruncated)
101
- .map((s) => `> Note: PR #${s.number} threads truncated — run \`pr-shepherd check ${s.number}\` for full count.`);
102
- return [heading, table, ...footnotes].join("\n\n");
103
- }
104
- export function deriveVerdict(s) {
105
- if (s.state === "MERGED")
106
- return "MERGED";
107
- if (s.state === "CLOSED")
108
- return "CLOSED";
109
- if (s.isDraft)
110
- return "DRAFT";
111
- if (s.mergeStateStatus === "CLEAN" &&
112
- s.unresolvedThreads === 0 &&
113
- s.ciState === "SUCCESS" &&
114
- s.reviewDecision !== "CHANGES_REQUESTED") {
115
- return "READY";
116
- }
117
- if (s.mergeStateStatus === "BLOCKED" || s.mergeStateStatus === "HAS_HOOKS")
118
- return "BLOCKED";
119
- if (s.mergeStateStatus === "DIRTY")
120
- return "CONFLICTS";
121
- if (s.ciState === "PENDING" || s.ciState === "EXPECTED")
122
- return "IN PROGRESS";
123
- if (s.ciState === "FAILURE" || s.ciState === "ERROR")
124
- return "FAILING";
125
- return s.mergeStateStatus;
126
- }
@@ -1,31 +0,0 @@
1
- query MultiPrStatusPaged($owner: String!, $repo: String!, $pr: Int!, $cursor: String) {
2
- repository(owner: $owner, name: $repo) {
3
- pullRequest(number: $pr) {
4
- number
5
- title
6
- state
7
- isDraft
8
- mergeStateStatus
9
- reviewDecision
10
- reviewThreads(last: 100, before: $cursor) {
11
- totalCount
12
- pageInfo {
13
- hasPreviousPage
14
- startCursor
15
- }
16
- nodes {
17
- isResolved
18
- }
19
- }
20
- commits(last: 1) {
21
- nodes {
22
- commit {
23
- statusCheckRollup {
24
- state
25
- }
26
- }
27
- }
28
- }
29
- }
30
- }
31
- }
@@ -1,69 +0,0 @@
1
- import { buildPrShepherdCommand } from "../cli/runner.mjs";
2
- /**
3
- * Build the numbered instruction steps for the agent to follow after a `check` run.
4
- * All rebase policy, CI budget policy, and ready-to-merge gating live here so the
5
- * skill stays a thin dispatcher and these rules co-evolve with the CLI data model.
6
- */
7
- export function buildCheckInstructions(report, opts) {
8
- const runtime = opts?.runtime ?? "claude";
9
- const { mergeStatus, checks, threads, comments, changesRequestedReviews, status } = report;
10
- const instructions = [];
11
- // 1. Summary
12
- const totalActionable = threads.actionable.length +
13
- threads.resolutionOnly.length +
14
- comments.actionable.length +
15
- changesRequestedReviews.length;
16
- const total = checks.passing.length +
17
- checks.failing.length +
18
- checks.inProgress.length +
19
- checks.skipped.length;
20
- const copilotNote = mergeStatus.copilotReviewInProgress ? " (Copilot review in progress)" : "";
21
- instructions.push(`Report: merge status is ${mergeStatus.status}${copilotNote}, CI ${checks.passing.length}/${total} passed` +
22
- (checks.failing.length > 0 ? ` (${checks.failing.length} failing)` : "") +
23
- (checks.inProgress.length > 0 ? ` (${checks.inProgress.length} in progress)` : "") +
24
- `, ${totalActionable} actionable review item(s).`);
25
- // 2. Rebase policy (only emit when relevant)
26
- if (mergeStatus.status === "CONFLICTS") {
27
- instructions.push("Rebase required: the branch has merge conflicts that must be resolved before this PR can land.");
28
- }
29
- else if (mergeStatus.status === "BEHIND") {
30
- instructions.push("The PR is behind the base branch. A rebase is optional if all CI checks pass.");
31
- }
32
- // 3. CI budget policy — one instruction per failing check
33
- for (const c of checks.failing) {
34
- const stepHint = c.failedStep ? ` (failed step: \`${c.failedStep}\`)` : "";
35
- const diagnosisHint = c.runId
36
- ? c.conclusion === "CANCELLED"
37
- ? `cancelled — if unintended, rerun with \`gh run rerun ${c.runId}\``
38
- : c.conclusion === "STARTUP_FAILURE"
39
- ? `startup failure — run \`gh run view ${c.runId}\` to inspect the workflow run; rerun with \`gh run rerun ${c.runId}\` if appropriate`
40
- : `run \`gh run view ${c.runId} --log-failed\`${stepHint} to diagnose — if transient, rerun with \`gh run rerun ${c.runId} --failed\`; otherwise apply a fix`
41
- : c.detailsUrl
42
- ? `open the check details (${c.detailsUrl}) to diagnose the failure`
43
- : `no run or details URL available — escalate to a human`;
44
- instructions.push(`Failing check: \`${c.name}\` — ${diagnosisHint}.`);
45
- }
46
- // 4. Ready-to-merge gate
47
- const isClean = mergeStatus.mergeStateStatus === "CLEAN";
48
- const isReady = isClean && status === "READY" && !mergeStatus.copilotReviewInProgress;
49
- if (isReady) {
50
- instructions.push("This PR is ready to merge: mergeStateStatus is CLEAN, status is READY, and no Copilot review is in progress.");
51
- }
52
- else {
53
- const blockers = [];
54
- if (!isClean)
55
- blockers.push(`mergeStateStatus is ${mergeStatus.mergeStateStatus} (not CLEAN)`);
56
- if (status !== "READY")
57
- blockers.push(`status is ${status} (not READY)`);
58
- if (mergeStatus.copilotReviewInProgress)
59
- blockers.push("Copilot review is still in progress");
60
- instructions.push(`Do not declare this PR ready to merge: ${blockers.join("; ")}.`);
61
- }
62
- // 5. Continuous monitoring pointer (suppressed only when truly ready to merge)
63
- if (!isReady) {
64
- instructions.push(runtime === "codex"
65
- ? `This is a one-shot check. For follow-up monitoring, run \`${buildPrShepherdCommand([String(report.pr)], { runner: opts?.runner }).text}\`.`
66
- : "This is a one-shot check. For continuous monitoring that acts on these signals automatically, use `/pr-shepherd:monitor`.");
67
- }
68
- return instructions;
69
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Machine-readable JSON reporter for shepherd check output.
3
- *
4
- * Slash commands parse this output to extract IDs, status, and actionable items
5
- * without string-scraping the human-readable text reporter.
6
- */
7
- import { buildCheckInstructions } from "./check-instructions.mjs";
8
- export function formatJson(report, opts) {
9
- return JSON.stringify({ ...report, instructions: buildCheckInstructions(report, opts) }, null, 2);
10
- }
@@ -1,156 +0,0 @@
1
- import { buildCheckInstructions } from "./check-instructions.mjs";
2
- import { renderThreadBullet, renderCommentBullet, renderFirstLookStatusTag, renderReviewListSection, renderThreadResolutionStatusTag, } from "../cli/list-formatters.mjs";
3
- import { joinSections } from "../util/markdown.mjs";
4
- export function formatText(report, opts) {
5
- const header = [
6
- `# PR #${report.pr} [CHECK] — ${report.repo}`,
7
- `Status: ${report.status}`,
8
- `Base: ${report.baseBranch}`,
9
- ].join("\n");
10
- const ms = report.mergeStatus;
11
- const mergeStatusSection = [
12
- "## Merge Status",
13
- "",
14
- `- status: \`${ms.status}\``,
15
- `- mergeStateStatus: \`${ms.mergeStateStatus}\``,
16
- `- mergeable: \`${ms.mergeable}\``,
17
- `- reviewDecision: \`${ms.reviewDecision ?? "(none)"}\``,
18
- `- isDraft: \`${ms.isDraft}\``,
19
- `- copilotReviewInProgress: \`${ms.copilotReviewInProgress}\``,
20
- ].join("\n");
21
- const { passing, failing, inProgress, skipped } = report.checks;
22
- const total = passing.length + failing.length + inProgress.length + skipped.length;
23
- const ciSubsections = [`${passing.length}/${total} passed`];
24
- if (failing.length > 0) {
25
- const lines = [`### Failed (${failing.length})`, ""];
26
- for (const c of failing) {
27
- const triaged = c;
28
- const prefix = triaged.workflowName ? `${triaged.workflowName} › ` : "";
29
- lines.push(`- ${prefix}${c.name}: ${c.conclusion ?? c.status}`);
30
- if (triaged.failedStep) {
31
- lines.push(` failed step: ${triaged.failedStep}`);
32
- }
33
- if (triaged.summary) {
34
- lines.push(` summary: ${triaged.summary}`);
35
- }
36
- }
37
- ciSubsections.push(lines.join("\n"));
38
- }
39
- if (inProgress.length > 0) {
40
- const lines = [`### In Progress (${inProgress.length})`, ""];
41
- for (const c of inProgress) {
42
- lines.push(`- ${c.name}: ${c.status}`);
43
- }
44
- ciSubsections.push(lines.join("\n"));
45
- }
46
- if (skipped.length > 0) {
47
- ciSubsections.push(`### Skipped (${skipped.length}): ${skipped.map((c) => c.name).join(", ")}`);
48
- }
49
- if (report.checks.filtered.length > 0) {
50
- const lines = [
51
- `### Filtered non-PR-trigger (${report.checks.filtered.length}): ${report.checks.filtered.map((c) => c.name).join(", ")}`,
52
- "",
53
- ];
54
- if (report.checks.blockedByFilteredCheck) {
55
- lines.push("> Note: PR is BLOCKED and all filtered checks are non-PR-trigger — one of these filtered checks may be a required status check blocking merge.");
56
- }
57
- else if (report.mergeStatus.status === "BLOCKED") {
58
- lines.push("> Note: one or more of these filtered checks may be a required status check blocking merge.");
59
- }
60
- ciSubsections.push(lines.join("\n"));
61
- }
62
- const ciSection = `## CI Checks\n\n${ciSubsections.join("\n\n")}`;
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;
68
- let threadsSection = null;
69
- if (hasThreadSection) {
70
- const subparts = [];
71
- if (autoResolved.length > 0) {
72
- const lines = [`Auto-resolved outdated (${autoResolved.length}):`];
73
- for (const t of autoResolved) {
74
- lines.push(renderThreadBullet(t));
75
- }
76
- subparts.push(lines.join("\n"));
77
- }
78
- if (autoResolveErrors.length > 0) {
79
- const lines = [`Auto-resolve errors (${autoResolveErrors.length}):`];
80
- for (const e of autoResolveErrors) {
81
- lines.push(`- ${e}`);
82
- }
83
- subparts.push(lines.join("\n"));
84
- }
85
- if (actionableThreads.length > 0) {
86
- const lines = [`### Actionable (${actionableThreads.length})`, ""];
87
- for (const t of actionableThreads) {
88
- lines.push(renderThreadBullet(t));
89
- }
90
- subparts.push(lines.join("\n"));
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
- }
99
- threadsSection = `## Review Threads\n\n${subparts.join("\n\n")}`;
100
- }
101
- const { actionable: actionableComments, firstLook: firstLookComments } = report.comments;
102
- let commentsSection = null;
103
- if (actionableComments.length > 0) {
104
- const lines = [`### Actionable (${actionableComments.length})`, ""];
105
- for (const c of actionableComments) {
106
- lines.push(renderCommentBullet(c));
107
- }
108
- commentsSection = `## PR Comments\n\n${lines.join("\n")}`;
109
- }
110
- const changesRequestedSection = renderReviewListSection("CHANGES_REQUESTED Reviews", report.changesRequestedReviews);
111
- const allSummaries = [...report.firstLookSummaries, ...report.reviewSummaries];
112
- const reviewSummariesSection = renderReviewListSection("Review Summaries", allSummaries);
113
- const approvedReviewsSection = renderReviewListSection("Approved Reviews", report.approvedReviews);
114
- const firstLookTotal = firstLookThreads.length + firstLookComments.length;
115
- let firstLookSection = null;
116
- if (firstLookTotal > 0) {
117
- const lines = [
118
- `## First-look items (${firstLookTotal}) — acknowledge status before acting`,
119
- "",
120
- ];
121
- for (const t of firstLookThreads) {
122
- lines.push(renderThreadBullet(t, { statusTag: renderFirstLookStatusTag(t) }));
123
- }
124
- for (const c of firstLookComments) {
125
- const editedSuffix = c.edited ? ", edited" : "";
126
- lines.push(renderCommentBullet(c, { statusTag: `[status: minimized${editedSuffix}]` }));
127
- }
128
- firstLookSection = lines.join("\n");
129
- }
130
- const totalActionable = actionableThreads.length +
131
- resolutionOnlyThreads.length +
132
- actionableComments.length +
133
- report.changesRequestedReviews.length;
134
- const counts = [];
135
- if (totalActionable > 0)
136
- counts.push(`${totalActionable} actionable`);
137
- if (firstLookTotal > 0)
138
- counts.push(`${firstLookTotal} first-look`);
139
- const summaryLine = counts.join(", ") || "0 actionable — no unresolved review items";
140
- const summarySection = `## Summary\n\n${summaryLine}`;
141
- const instructions = buildCheckInstructions(report, opts);
142
- const instructionsSection = `## Instructions\n\n${instructions.map((step, i) => `${i + 1}. ${step}`).join("\n")}`;
143
- return joinSections([
144
- header,
145
- mergeStatusSection,
146
- ciSection,
147
- threadsSection,
148
- commentsSection,
149
- changesRequestedSection,
150
- reviewSummariesSection,
151
- approvedReviewsSection,
152
- firstLookSection,
153
- summarySection,
154
- instructionsSection,
155
- ]);
156
- }
@@ -1,37 +0,0 @@
1
- ---
2
- name: check
3
- description: "Check GitHub CI status and review comments for the current PR"
4
- argument-hint: "[PR number or URL ...]"
5
- user-invocable: true
6
- allowed-tools: ["Bash"]
7
- ---
8
-
9
- # pr-shepherd check — PR Status
10
-
11
- ## Arguments: $ARGUMENTS
12
-
13
- ## Steps
14
-
15
- 1. **Parse `$ARGUMENTS`:** extract PR numbers or GitHub PR URLs. If none, infer:
16
- `gh pr list --head "$(git rev-parse --abbrev-ref HEAD)" --json number --jq '.[0].number'`
17
- If no PR found, report an error and stop.
18
-
19
- 2. **Short-circuit if merged:**
20
-
21
- ```bash
22
- gh pr view <N> --json state --jq '.state'
23
- ```
24
-
25
- If `MERGED`, output: `PR #N is already merged. Nothing to check.` and skip.
26
-
27
- 3. **Run the check and follow instructions:**
28
- Use the repository package runner selected by `packageManager` or lockfile
29
- (for example, `pnpm exec`, `yarn run`, or `npx --no-install`).
30
-
31
- ```bash
32
- <runner> pr-shepherd check <N>
33
- ```
34
-
35
- Print the full output. Follow the `## Instructions` section exactly.
36
-
37
- 4. For multiple PRs, repeat steps 2–3 for each.
@@ -1,40 +0,0 @@
1
- ---
2
- name: monitor
3
- description: "Start continuous CI monitoring — marks PR ready for review when all checks pass"
4
- argument-hint: "[PR number or URL]"
5
- user-invocable: true
6
- allowed-tools:
7
- [
8
- "Bash",
9
- "Read",
10
- "Grep",
11
- "Edit",
12
- "Write",
13
- "Glob",
14
- "Skill",
15
- "ScheduleWakeup",
16
- "CronList",
17
- "CronDelete",
18
- ]
19
- ---
20
-
21
- # pr-shepherd monitor — Continuous PR Monitor
22
-
23
- ## Arguments: $ARGUMENTS
24
-
25
- ## Steps
26
-
27
- 1. **Resolve PR number:**
28
- - If `$ARGUMENTS` contains a PR number or GitHub PR URL, extract the number.
29
- - Otherwise, infer: `gh pr list --head "$(git rev-parse --abbrev-ref HEAD)" --json number --jq '.[0].number'`
30
- - If no PR found, report an error and stop.
31
-
32
- 2. **Run the bootstrap command and follow its instructions:**
33
- Use the repository package runner selected by `packageManager` or lockfile
34
- (for example, `pnpm exec`, `yarn run`, or `npx --no-install`).
35
-
36
- ```bash
37
- <runner> pr-shepherd monitor <PR_NUMBER>
38
- ```
39
-
40
- Print the full output. Follow the `## Instructions` section exactly.
@@ -1,38 +0,0 @@
1
- ---
2
- name: resolve
3
- description: "Resolve all inline review comments on the current PR"
4
- argument-hint: "[PR number or URL]"
5
- user-invocable: true
6
- allowed-tools: ["Bash", "Read", "Grep", "Edit", "Write", "Glob", "Skill"]
7
- ---
8
-
9
- # pr-shepherd resolve — Fix and Resolve Review Comments
10
-
11
- Resolve unresolved review threads and minimize PR comments on the current PR — from ALL authors.
12
-
13
- ## Arguments: $ARGUMENTS
14
-
15
- ## Steps
16
-
17
- 1. **Resolve PR number:**
18
- - If `$ARGUMENTS` contains a PR number or GitHub PR URL, extract the number.
19
- - Otherwise, infer: `gh pr list --head "$(git rev-parse --abbrev-ref HEAD)" --json number --jq '.[0].number'`
20
- - If no PR found, report an error and stop.
21
-
22
- 2. **Short-circuit if merged:**
23
-
24
- ```bash
25
- gh pr view <N> --json state --jq '.state'
26
- ```
27
-
28
- If `MERGED`, invoke `/loop cancel` via the Skill tool, output a merged message, and stop.
29
-
30
- 3. **Fetch and follow instructions:**
31
- Use the repository package runner selected by `packageManager` or lockfile
32
- (for example, `pnpm exec`, `yarn run`, or `npx --no-install`).
33
-
34
- ```bash
35
- <runner> pr-shepherd resolve <N> --fetch
36
- ```
37
-
38
- Print the full output. Follow the `## Instructions` section exactly.