pr-shepherd 0.60.1 → 1.1.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 (245) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +7 -5
  3. package/bin/api.d.mts +35 -24
  4. package/bin/api.mjs +68 -48
  5. package/bin/checks/classify.d.mts +1 -1
  6. package/bin/checks/classify.mjs +4 -4
  7. package/bin/checks/log-excerpt.mjs +1 -1
  8. package/bin/cli/arg-flags.mjs +0 -3
  9. package/bin/cli/clean-handler.d.mts +1 -1
  10. package/bin/cli/clean-handler.mjs +5 -15
  11. package/bin/cli/default-poll.d.mts +2 -0
  12. package/bin/cli/default-poll.mjs +9 -6
  13. package/bin/cli/formatters.d.mts +2 -3
  14. package/bin/cli/formatters.mjs +0 -26
  15. package/bin/cli/handlers.d.mts +3 -3
  16. package/bin/cli/handlers.mjs +45 -35
  17. package/bin/cli/help-command-pages.d.mts +13 -158
  18. package/bin/cli/help-command-pages.mjs +12 -157
  19. package/bin/cli/help-iterate-poll-pages.d.mts +4 -3
  20. package/bin/cli/help-iterate-poll-pages.mjs +76 -36
  21. package/bin/cli/help-log-file-page.d.mts +1 -1
  22. package/bin/cli/help-log-file-page.mjs +3 -5
  23. package/bin/cli/help-top-page.d.mts +1 -1
  24. package/bin/cli/help-top-page.mjs +19 -20
  25. package/bin/cli/help.d.mts +14 -159
  26. package/bin/cli/iterate-checks-formatter.mjs +1 -1
  27. package/bin/cli/iterate-flags.d.mts +2 -4
  28. package/bin/cli/iterate-flags.mjs +4 -20
  29. package/bin/cli/iterate-formatter.d.mts +1 -1
  30. package/bin/cli/iterate-formatter.mjs +6 -6
  31. package/bin/cli/iterate-instructions.d.mts +1 -1
  32. package/bin/cli/iterate-instructions.mjs +5 -6
  33. package/bin/cli/iterate-lean.mjs +32 -40
  34. package/bin/cli/iterate-verbose.mjs +1 -1
  35. package/bin/cli/journal-formatter.mjs +0 -3
  36. package/bin/cli/journal-handler.d.mts +1 -1
  37. package/bin/cli/journal-handler.mjs +2 -1
  38. package/bin/cli/mutate-formatter.mjs +0 -9
  39. package/bin/cli/next-check-format.d.mts +0 -4
  40. package/bin/cli/next-check-format.mjs +0 -4
  41. package/bin/cli/poll-handler.d.mts +3 -0
  42. package/bin/cli/poll-handler.mjs +88 -47
  43. package/bin/cli/poll-summary-emitter.mjs +2 -2
  44. package/bin/cli/poll-summary-formatter.mjs +5 -2
  45. package/bin/cli/poll-targets.d.mts +2 -2
  46. package/bin/cli/poll-targets.mjs +15 -35
  47. package/bin/cli/removed-flags.d.mts +3 -0
  48. package/bin/cli/removed-flags.mjs +24 -0
  49. package/bin/cli/rule-auto-resolve-lines.d.mts +3 -0
  50. package/bin/cli/rule-auto-resolve-lines.mjs +12 -0
  51. package/bin/cli/runner.d.mts +1 -1
  52. package/bin/cli/runner.mjs +2 -2
  53. package/bin/cli/stack-layer-view.d.mts +2 -1
  54. package/bin/cli/stack-overview.d.mts +0 -1
  55. package/bin/cli/stack-overview.mjs +4 -2
  56. package/bin/cli/{fix-formatter-extra.d.mts → triage-formatter-extra.d.mts} +1 -2
  57. package/bin/cli/{fix-formatter-extra.mjs → triage-formatter-extra.mjs} +0 -6
  58. package/bin/cli/triage-formatter.d.mts +4 -0
  59. package/bin/cli/{fix-formatter.mjs → triage-formatter.mjs} +31 -42
  60. package/bin/cli/unknown-flags.d.mts +6 -0
  61. package/bin/cli/unknown-flags.mjs +29 -0
  62. package/bin/cli-parser.mjs +41 -62
  63. package/bin/commands/check-activity.d.mts +8 -0
  64. package/bin/commands/check-activity.mjs +26 -0
  65. package/bin/commands/check-annotations.d.mts +1 -1
  66. package/bin/commands/check-annotations.mjs +3 -2
  67. package/bin/commands/check-review-budget.d.mts +1 -1
  68. package/bin/commands/check-review-budget.mjs +1 -1
  69. package/bin/commands/check-wait-detectors.d.mts +1 -1
  70. package/bin/commands/check-wait-detectors.mjs +12 -1
  71. package/bin/commands/check.d.mts +1 -1
  72. package/bin/commands/check.mjs +13 -8
  73. package/bin/commands/commit-suggestion-instruction.d.mts +2 -2
  74. package/bin/commands/commit-suggestion-instruction.mjs +3 -3
  75. package/bin/commands/iterate/api-usage.mjs +8 -8
  76. package/bin/commands/iterate/check-instructions.d.mts +4 -4
  77. package/bin/commands/iterate/check-instructions.mjs +8 -8
  78. package/bin/commands/iterate/classify.mjs +2 -2
  79. package/bin/commands/iterate/escalate.mjs +6 -6
  80. package/bin/commands/iterate/index.mjs +20 -23
  81. package/bin/commands/iterate/merge-plan.mjs +3 -3
  82. package/bin/commands/iterate/merge-state.mjs +4 -7
  83. package/bin/commands/iterate/native-stack-rebase.mjs +1 -1
  84. package/bin/commands/iterate/render.d.mts +1 -1
  85. package/bin/commands/iterate/render.mjs +2 -2
  86. package/bin/commands/iterate/stall.mjs +11 -9
  87. package/bin/commands/iterate/{fix-loop-instruction.mjs → triage-loop-instruction.mjs} +7 -3
  88. package/bin/commands/iterate/{fix-code.d.mts → triage.d.mts} +2 -2
  89. package/bin/commands/iterate/{fix-code.mjs → triage.mjs} +14 -23
  90. package/bin/commands/iterate/unreported-required.mjs +2 -5
  91. package/bin/commands/journal/index.d.mts +1 -3
  92. package/bin/commands/journal/index.mjs +1 -1
  93. package/bin/commands/mark-files-as-viewed.d.mts +1 -3
  94. package/bin/commands/mark-files-as-viewed.mjs +1 -1
  95. package/bin/commands/next-check.d.mts +2 -2
  96. package/bin/commands/next-check.mjs +4 -2
  97. package/bin/commands/poll-progress.mjs +1 -1
  98. package/bin/commands/poll-summary-explicit-instructions.mjs +1 -1
  99. package/bin/commands/poll-summary-instructions.mjs +7 -7
  100. package/bin/commands/poll-summary-signature.d.mts +6 -0
  101. package/bin/commands/poll-summary-signature.mjs +19 -2
  102. package/bin/commands/poll-summary.mjs +11 -4
  103. package/bin/commands/poll.d.mts +1 -1
  104. package/bin/commands/poll.mjs +4 -4
  105. package/bin/commands/ready-delay.d.mts +1 -0
  106. package/bin/commands/ready-delay.mjs +8 -3
  107. package/bin/commands/resolve-mutate.d.mts +1 -1
  108. package/bin/commands/resolve-mutate.mjs +1 -1
  109. package/bin/commands/rest-stack-merge-readiness.mjs +1 -1
  110. package/bin/commands/rule-auto-resolve-format.d.mts +7 -0
  111. package/bin/commands/rule-auto-resolve-format.mjs +11 -4
  112. package/bin/commands/rule-auto-resolve.d.mts +14 -2
  113. package/bin/commands/single-tick.d.mts +5 -0
  114. package/bin/commands/single-tick.mjs +7 -0
  115. package/bin/commands/stack-drain.mjs +1 -1
  116. package/bin/commands/stack-merge-flag.mjs +1 -1
  117. package/bin/commands/stack-stall.d.mts +2 -2
  118. package/bin/commands/stack-stall.mjs +2 -2
  119. package/bin/commands/stack-work.d.mts +2 -2
  120. package/bin/commands/stack-work.mjs +2 -2
  121. package/bin/commands/suggestion-patch-item.d.mts +0 -1
  122. package/bin/commands/suggestion-patch-item.mjs +0 -12
  123. package/bin/commands/suggestion-patches.d.mts +2 -2
  124. package/bin/commands/suggestion-patches.mjs +1 -1
  125. package/bin/commands/tick-instructions.d.mts +9 -0
  126. package/bin/commands/tick-instructions.mjs +23 -0
  127. package/bin/commands/tick.d.mts +15 -0
  128. package/bin/commands/{event-mode.mjs → tick.mjs} +39 -55
  129. package/bin/comments/minimize-policy.mjs +1 -3
  130. package/bin/comments/resolve.d.mts +0 -23
  131. package/bin/comments/resolve.mjs +0 -4
  132. package/bin/comments/review-output-budget.d.mts +1 -1
  133. package/bin/comments/review-output-budget.mjs +1 -1
  134. package/bin/comments/sha-poll.mjs +3 -1
  135. package/bin/comments/thread-visibility.d.mts +6 -0
  136. package/bin/comments/thread-visibility.mjs +30 -15
  137. package/bin/config/default-bot-usernames.d.mts +2 -0
  138. package/bin/config/default-bot-usernames.mjs +15 -0
  139. package/bin/config/load.d.mts +4 -138
  140. package/bin/config/load.mjs +88 -443
  141. package/bin/config/merge-command-args.mjs +2 -2
  142. package/bin/config/merge-method.d.mts +1 -2
  143. package/bin/config/merge-method.mjs +1 -8
  144. package/bin/config/renames.d.mts +8 -0
  145. package/bin/config/renames.mjs +87 -0
  146. package/bin/config/resolve.d.mts +31 -0
  147. package/bin/config/resolve.mjs +89 -0
  148. package/bin/config/schema.d.mts +161 -0
  149. package/bin/config/schema.mjs +168 -0
  150. package/bin/exit-codes.d.mts +1 -1
  151. package/bin/exit-codes.mjs +3 -3
  152. package/bin/github/batch-parse-suites.d.mts +14 -0
  153. package/bin/github/batch-parse-suites.mjs +4 -1
  154. package/bin/github/batch-parser-helpers.mjs +3 -0
  155. package/bin/github/check-annotation-cache.d.mts +5 -0
  156. package/bin/github/check-annotation-cache.mjs +6 -3
  157. package/bin/github/fingerprint.mjs +4 -0
  158. package/bin/github/poll-summary-annotation-probe.d.mts +5 -0
  159. package/bin/github/poll-summary-annotation-probe.mjs +17 -3
  160. package/bin/github/poll-summary-annotations.d.mts +14 -0
  161. package/bin/github/poll-summary-annotations.mjs +75 -0
  162. package/bin/github/poll-summary-blocking-reviewer.d.mts +2 -0
  163. package/bin/github/poll-summary-blocking-reviewer.mjs +9 -0
  164. package/bin/github/poll-summary-check-blockers.mjs +1 -1
  165. package/bin/github/poll-summary-checks.d.mts +1 -0
  166. package/bin/github/poll-summary-checks.mjs +20 -2
  167. package/bin/github/poll-summary-command.mjs +8 -6
  168. package/bin/github/poll-summary-fingerprint.d.mts +7 -0
  169. package/bin/github/poll-summary-fingerprint.mjs +7 -0
  170. package/bin/github/poll-summary-projector.d.mts +3 -1
  171. package/bin/github/poll-summary-projector.mjs +14 -25
  172. package/bin/github/poll-summary-queue-removal.d.mts +2 -0
  173. package/bin/github/poll-summary-queue-removal.mjs +11 -0
  174. package/bin/github/poll-summary-readiness.mjs +1 -0
  175. package/bin/github/poll-summary-review-parity.d.mts +24 -0
  176. package/bin/github/poll-summary-review-parity.mjs +82 -0
  177. package/bin/github/poll-summary-review.d.mts +11 -2
  178. package/bin/github/poll-summary-review.mjs +88 -60
  179. package/bin/github/poll-summary-route.mjs +9 -9
  180. package/bin/github/poll-summary-row-error.d.mts +16 -0
  181. package/bin/github/poll-summary-row-error.mjs +74 -0
  182. package/bin/github/poll-summary-rule-auto-resolve.d.mts +32 -0
  183. package/bin/github/poll-summary-rule-auto-resolve.mjs +119 -0
  184. package/bin/github/poll-summary-rule-handoff.d.mts +7 -0
  185. package/bin/github/poll-summary-rule-handoff.mjs +20 -0
  186. package/bin/github/poll-summary-rule-items.d.mts +20 -0
  187. package/bin/github/poll-summary-rule-items.mjs +28 -0
  188. package/bin/github/poll-summary-unreported.mjs +1 -1
  189. package/bin/github/poll-summary.mjs +16 -8
  190. package/bin/github/queries.d.mts +1 -1
  191. package/bin/github/queries.mjs +1 -1
  192. package/bin/github/rest-reader-core.mjs +13 -2
  193. package/bin/github/rest-summary-read.mjs +12 -1
  194. package/bin/mcp/server.mjs +48 -54
  195. package/bin/merge-status/derive.mjs +1 -1
  196. package/bin/state/durable-state.d.mts +1 -3
  197. package/bin/state/durable-state.mjs +2 -8
  198. package/bin/state/fix-attempts.d.mts +2 -2
  199. package/bin/state/fix-attempts.mjs +1 -1
  200. package/bin/state/merge-request.mjs +1 -1
  201. package/bin/state/pr-fingerprint.mjs +4 -11
  202. package/bin/state/seen-comments.d.mts +4 -5
  203. package/bin/state/seen-comments.mjs +8 -14
  204. package/bin/state/stack-stall.d.mts +1 -1
  205. package/bin/state/stack-stall.mjs +1 -1
  206. package/bin/state/stall-state-store.d.mts +1 -1
  207. package/bin/state/stall-state-store.mjs +1 -1
  208. package/bin/types/escalate.d.mts +2 -2
  209. package/bin/types/github.d.mts +2 -0
  210. package/bin/types/iterate.d.mts +12 -26
  211. package/bin/types/merge-queue.d.mts +2 -2
  212. package/bin/types/next-check.d.mts +1 -1
  213. package/bin/types/poll-summary.d.mts +26 -6
  214. package/bin/types/report.d.mts +2 -2
  215. package/bin/types/suggestion-patch.d.mts +1 -8
  216. package/package.json +3 -2
  217. package/plugins/pr-shepherd/.claude-plugin/plugin.json +1 -1
  218. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  219. package/plugins/pr-shepherd/.codex.mcp.json +1 -1
  220. package/plugins/pr-shepherd/.mcp.json +1 -1
  221. package/plugins/pr-shepherd/skills/pr-shepherd/SKILL.md +6 -6
  222. package/plugins/pr-shepherd/skills/pr-shepherd/references/ci-failure-triage.md +1 -1
  223. package/plugins/pr-shepherd/skills/pr-shepherd/references/{cloud-event-loop.md → event-loop.md} +5 -5
  224. package/plugins/pr-shepherd/skills/pr-shepherd/references/mcp-fallback.md +3 -3
  225. package/plugins/pr-shepherd/skills/pr-shepherd/references/merge-queue-ejection.md +1 -1
  226. package/plugins/pr-shepherd/skills/pr-shepherd/references/suggestion-patches.md +1 -1
  227. package/plugins/pr-shepherd/skills/pr-shepherd/references/{fix-code-loop.md → triage-loop.md} +3 -3
  228. package/plugins/pr-shepherd/skills/reduce-pr-noise/SKILL.md +1 -1
  229. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/classifiers.md +2 -2
  230. package/plugins/pr-shepherd/skills/reduce-pr-noise/references/settings.md +11 -11
  231. package/pr-shepherd.schema.json +361 -0
  232. package/bin/cli/event-state-scope.d.mts +0 -19
  233. package/bin/cli/event-state-scope.mjs +0 -60
  234. package/bin/cli/fix-formatter.d.mts +0 -4
  235. package/bin/commands/commit-suggestion.d.mts +0 -8
  236. package/bin/commands/commit-suggestion.mjs +0 -26
  237. package/bin/commands/event-instructions.d.mts +0 -11
  238. package/bin/commands/event-instructions.mjs +0 -27
  239. package/bin/commands/event-mode.d.mts +0 -14
  240. package/bin/commands/poll-mode.d.mts +0 -8
  241. package/bin/commands/poll-mode.mjs +0 -14
  242. package/bin/config.json +0 -68
  243. package/bin/types/protected-run.d.mts +0 -6
  244. package/bin/types/protected-run.mjs +0 -1
  245. /package/bin/commands/iterate/{fix-loop-instruction.d.mts → triage-loop-instruction.d.mts} +0 -0
@@ -2,18 +2,20 @@ import { parseArgs } from "node:util";
2
2
  import { EXIT, ShepherdError } from "../exit-codes.mjs";
3
3
  import { getRepoInfo } from "../github/client.mjs";
4
4
  import { parseCliPrReference, normalizeRepositoryIdentity, resolveParsedPrTarget, } from "../pr-reference.mjs";
5
+ import { rejectRemovedFlags } from "./removed-flags.mjs";
5
6
  const VALUE_FLAGS = new Set([
6
7
  "--format",
7
8
  "--ready-delay",
8
9
  "--stall-timeout",
9
10
  "--instructions",
10
- "--poll-mode",
11
11
  "--interval",
12
12
  "--timeout",
13
13
  "--debounce",
14
- "--stack",
15
14
  ]);
15
+ /** Splits PR references from flags. Returns null after printing a usage error. */
16
16
  export function parsePollTargets(args) {
17
+ if (rejectRemovedFlags(args))
18
+ return null;
17
19
  const { values } = parseArgs({
18
20
  args,
19
21
  strict: false,
@@ -22,39 +24,22 @@ export function parsePollTargets(args) {
22
24
  });
23
25
  const consumed = new Set();
24
26
  const refs = [];
25
- let stack;
26
27
  for (let index = 0; index < args.length; index += 1) {
27
28
  const arg = args[index];
28
29
  if (VALUE_FLAGS.has(arg)) {
29
- if (arg === "--format" || arg === "--stack") {
30
+ if (arg === "--format") {
30
31
  consumed.add(index);
31
32
  if (index + 1 < args.length)
32
33
  consumed.add(index + 1);
33
34
  }
34
- if (arg === "--stack") {
35
- const parsed = parseCliPrReference(args[index + 1] ?? "");
36
- if (!parsed)
37
- return usage(`invalid --stack PR reference: ${args[index + 1] ?? "(missing)"}`);
38
- if (stack)
39
- return usage("--stack may only be specified once");
40
- stack = parsed;
41
- }
42
35
  index += 1;
43
36
  continue;
44
37
  }
45
38
  const equals = arg.indexOf("=");
46
39
  const name = equals < 0 ? arg : arg.slice(0, equals);
47
40
  if (VALUE_FLAGS.has(name)) {
48
- if (name === "--format" || name === "--stack")
41
+ if (name === "--format")
49
42
  consumed.add(index);
50
- if (name === "--stack") {
51
- const parsed = parseCliPrReference(arg.slice(equals + 1));
52
- if (!parsed)
53
- return usage(`invalid --stack PR reference: ${arg.slice(equals + 1)}`);
54
- if (stack)
55
- return usage("--stack may only be specified once");
56
- stack = parsed;
57
- }
58
43
  continue;
59
44
  }
60
45
  if (arg === "--verbose") {
@@ -64,23 +49,25 @@ export function parsePollTargets(args) {
64
49
  if (arg.startsWith("--"))
65
50
  continue;
66
51
  const parsed = parseCliPrReference(arg);
67
- if (!parsed)
68
- continue;
52
+ if (!parsed) {
53
+ // A typo must not silently drop a PR from an exact multi-PR selection.
54
+ process.stderr.write(`pr-shepherd: invalid PR reference "${arg}".\n`);
55
+ process.exitCode = EXIT.USAGE;
56
+ return null;
57
+ }
69
58
  refs.push(parsed);
70
59
  consumed.add(index);
71
60
  }
72
- if (stack && refs.length > 0)
73
- return usage("--stack cannot be combined with explicit PRs");
74
61
  const format = (values.format ?? "text");
75
62
  return {
76
63
  refs,
77
- stack,
78
64
  global: { format, verbose: values.verbose === true },
79
65
  extra: args.filter((_, index) => !consumed.has(index)),
80
66
  };
81
67
  }
68
+ /** Resolves PR references that must all name one repository. */
82
69
  export async function resolvePollTargets(parsed) {
83
- const all = parsed.stack ? [parsed.stack] : parsed.refs;
70
+ const all = parsed.refs;
84
71
  if (all.length === 0)
85
72
  return { prNumbers: [] };
86
73
  const checkoutRepo = all.some((ref) => ref.repository === undefined)
@@ -102,14 +89,7 @@ export async function resolvePollTargets(parsed) {
102
89
  if (!prNumbers.includes(target.prNumber))
103
90
  prNumbers.push(target.prNumber);
104
91
  }
105
- return parsed.stack
106
- ? { prNumbers: [], stackPrNumber: prNumbers[0], targetRepository: selectedRepo }
107
- : { prNumbers, targetRepository: selectedRepo };
108
- }
109
- function usage(message) {
110
- process.stderr.write(`pr-shepherd: ${message}\n`);
111
- process.exitCode = EXIT.USAGE;
112
- return null;
92
+ return { prNumbers, targetRepository: selectedRepo };
113
93
  }
114
94
  function usageThrow(message) {
115
95
  throw new ShepherdError(message, EXIT.USAGE);
@@ -0,0 +1,3 @@
1
+ export declare function isRemovedFlag(arg: string): boolean;
2
+ /** Prints a usage error and returns true when `args` contains a removed flag. */
3
+ export declare function rejectRemovedFlags(args: string[]): boolean;
@@ -0,0 +1,24 @@
1
+ import { EXIT } from "../exit-codes.mjs";
2
+ /** Flags removed in v1, with the replacement each usage error names. */
3
+ const REMOVED_FLAGS = new Map([
4
+ ["--stack", "`--stack` was removed; use `pr-shepherd stack <PR>`."],
5
+ [
6
+ "--poll-mode",
7
+ "`--poll-mode` was removed; `pr-shepherd iterate` runs one tick, and looping commands run one tick when CLAUDE_CODE_REMOTE=true.",
8
+ ],
9
+ ]);
10
+ export function isRemovedFlag(arg) {
11
+ return REMOVED_FLAGS.has(arg.split("=", 1)[0]);
12
+ }
13
+ /** Prints a usage error and returns true when `args` contains a removed flag. */
14
+ export function rejectRemovedFlags(args) {
15
+ for (const arg of args) {
16
+ const message = REMOVED_FLAGS.get(arg.split("=", 1)[0]);
17
+ if (message === undefined)
18
+ continue;
19
+ process.stderr.write(`pr-shepherd: ${message}\n`);
20
+ process.exitCode = EXIT.USAGE;
21
+ return true;
22
+ }
23
+ return false;
24
+ }
@@ -0,0 +1,3 @@
1
+ import type { PollSummaryRuleAutoResolve } from "../types.mts";
2
+ /** Row lines for an aggregate row or stack layer: the one-PR `## Classification auto-resolve` content. */
3
+ export declare function formatRuleAutoResolveLines(event: PollSummaryRuleAutoResolve | undefined): string[];
@@ -0,0 +1,12 @@
1
+ /** Row lines for an aggregate row or stack layer: the one-PR `## Classification auto-resolve` content. */
2
+ export function formatRuleAutoResolveLines(event) {
3
+ if (!event)
4
+ return [];
5
+ const link = (kind, id, url) => ` - ${kind} \`${id}\`${url ? ` ${url}` : ""}`;
6
+ return [
7
+ ` - classification auto-resolve: ${event.summary}`,
8
+ ...(event.threads ?? []).map((thread) => link("thread", thread.id, thread.url)),
9
+ ...(event.minimized ?? []).map((item) => link(item.kind, item.id, item.url)),
10
+ ...(event.errors ?? []).map((error) => ` - ${error}`),
11
+ ];
12
+ }
@@ -2,7 +2,7 @@ interface PrShepherdCommand {
2
2
  argv: string[];
3
3
  text: string;
4
4
  }
5
- /** Every emitted pr-shepherd command starts with the configured `cliCommand` launcher. */
5
+ /** Every emitted pr-shepherd command starts with the configured `cli-command` launcher. */
6
6
  export declare function buildPrShepherdCommand(args: string[]): PrShepherdCommand;
7
7
  export declare function renderShellCommand(argv: string[]): string;
8
8
  export {};
@@ -1,12 +1,12 @@
1
1
  import { loadConfig } from "../config/load.mjs";
2
2
  import { getGithubTransport } from "../github/transport.mjs";
3
- /** Every emitted pr-shepherd command starts with the configured `cliCommand` launcher. */
3
+ /** Every emitted pr-shepherd command starts with the configured `cli-command` launcher. */
4
4
  export function buildPrShepherdCommand(args) {
5
5
  const transport = getGithubTransport() === "rest" &&
6
6
  !args.some((arg) => arg === "--transport" || arg.startsWith("--transport="))
7
7
  ? ["--transport", "rest"]
8
8
  : [];
9
- const argv = [...loadConfig().cliCommand, ...args, ...transport];
9
+ const argv = [...loadConfig()["cli-command"], ...args, ...transport];
10
10
  return { argv, text: renderShellCommand(argv) };
11
11
  }
12
12
  export function renderShellCommand(argv) {
@@ -1,4 +1,4 @@
1
- import type { PollSummaryItem } from "../types.mts";
1
+ import type { PollSummaryItem, PollSummaryRuleAutoResolve } from "../types.mts";
2
2
  /** One lean stack row shared by Markdown, JSON, and MCP. */
3
3
  export interface StackLayerView {
4
4
  transport?: "rest";
@@ -26,6 +26,7 @@ export interface StackLayerView {
26
26
  actionable?: number;
27
27
  checksIncomplete?: true;
28
28
  reviewIncomplete?: true;
29
+ ruleAutoResolve?: PollSummaryRuleAutoResolve;
29
30
  queueRemoval?: {
30
31
  reason: string | null;
31
32
  actor?: string;
@@ -21,7 +21,6 @@ export interface StackOverview {
21
21
  instructions?: string[];
22
22
  apiUsage?: ApiUsage;
23
23
  quotaWarning?: GraphqlQuotaWarning;
24
- pollMode?: "event";
25
24
  nextCheck?: NextCheck;
26
25
  }
27
26
  /** Lean stack payload shared by Markdown, JSON, and MCP. */
@@ -3,6 +3,7 @@ import { formatApiUsage, formatQuotaWarning } from "./api-usage-formatter.mjs";
3
3
  import { formatTransportEvidence } from "./transport-formatter.mjs";
4
4
  import { formatNextCheckLines } from "./next-check-format.mjs";
5
5
  import { formatPrUrl } from "../pr-reference.mjs";
6
+ import { formatRuleAutoResolveLines } from "./rule-auto-resolve-lines.mjs";
6
7
  /** Lean stack payload shared by Markdown, JSON, and MCP. */
7
8
  export function projectStackOverview(result) {
8
9
  const selection = result.selection;
@@ -26,7 +27,6 @@ export function projectStackOverview(result) {
26
27
  ...(result.instructions?.length && { instructions: result.instructions }),
27
28
  ...(result.apiUsage && { apiUsage: result.apiUsage }),
28
29
  ...(result.quotaWarning && { quotaWarning: result.quotaWarning }),
29
- ...(result.pollMode && { pollMode: result.pollMode }),
30
30
  ...(result.nextCheck && { nextCheck: result.nextCheck }),
31
31
  };
32
32
  }
@@ -60,6 +60,7 @@ function projectLayer(item, stale) {
60
60
  (item.review?.actionable ?? 0) > 0 && { actionable: item.review?.actionable }),
61
61
  ...(item.checks?.incomplete && { checksIncomplete: true }),
62
62
  ...(item.review?.incomplete && { reviewIncomplete: true }),
63
+ ...(item.ruleAutoResolve && { ruleAutoResolve: item.ruleAutoResolve }),
63
64
  ...(removal && {
64
65
  queueRemoval: {
65
66
  reason: removal.reason,
@@ -94,7 +95,7 @@ export function formatStackOverview(overview) {
94
95
  const lines = [
95
96
  `# ${overview.repo} stack #${stack.stackNumber}${stackTerminalTag(overview.nextAction)} — ${overview.reason}`,
96
97
  "",
97
- `anchor PR #${stack.anchor} · ${stack.stackSize} layers · mode \`${overview.mode}\`${overview.pollMode ? ` · pollMode \`${overview.pollMode}\`` : ""}`,
98
+ `anchor PR #${stack.anchor} · ${stack.stackSize} layers · mode \`${overview.mode}\``,
98
99
  ...(overview.stackMergeable ? ["stackMergeable: true"] : []),
99
100
  ...(overview.nextAction ? [`nextAction: ${overview.nextAction}`] : []),
100
101
  ...formatNextCheckLines(overview.nextCheck),
@@ -157,6 +158,7 @@ function formatLayerLines(layer, index, overview) {
157
158
  return [
158
159
  `- ${layer.url === formatPrUrl(overview.repo, layer.pr) ? `PR #${layer.pr}: ${layer.title}` : `[PR #${layer.pr}: ${layer.title}](${layer.url})`}${facts.length > 0 ? ` — ${facts.join(" · ")}` : ""}`,
159
160
  ` - ${details.join(" · ")}`,
161
+ ...formatRuleAutoResolveLines(layer.ruleAutoResolve),
160
162
  ...(layer.transport ? [` - transport \`${layer.transport}\``] : []),
161
163
  ...(layer.transportUnavailable ?? []).map(({ field, reason }) => ` - unavailable \`${field}\`: ${reason}`),
162
164
  ];
@@ -1,4 +1,3 @@
1
- import type { CheckAnnotation, IterateResultFixCode } from "../types.mts";
1
+ import type { CheckAnnotation } from "../types.mts";
2
2
  /** Returns null when the annotation adds nothing beyond the check's own conclusion tag. */
3
3
  export declare function renderCheckAnnotation(a: CheckAnnotation, logExcerpt?: string): string | null;
4
- export declare function renderProtectedRun(run: IterateResultFixCode["fix"]["protectedRuns"][number]): string;
@@ -25,12 +25,6 @@ export function renderCheckAnnotation(a, logExcerpt) {
25
25
  function duplicatesLog(text, logExcerpt) {
26
26
  return logExcerpt !== undefined && logExcerpt.includes(text.trim());
27
27
  }
28
- export function renderProtectedRun(run) {
29
- const label = run.workflowName
30
- ? `${run.workflowName} (${run.checkNames.join(", ")})`
31
- : run.checkNames.join(", ");
32
- return `- \`${run.runId}\` — \`${label}\` [matched: \`${run.matchedPattern}\`]`;
33
- }
34
28
  function renderAnnotationRange(a) {
35
29
  if (a.startLine === null && a.endLine === null)
36
30
  return "?";
@@ -0,0 +1,4 @@
1
+ import type { IterateResultTriage } from "../types.mts";
2
+ export declare function formatTriageResult(header: string, result: IterateResultTriage, opts?: {
3
+ verbose?: boolean;
4
+ }): string;
@@ -4,17 +4,17 @@ import { renderSuggestionBlock, renderLineRange } from "./suggestion-renderer.mj
4
4
  import { renderThreadBullet, renderReviewBullet, renderThreadResolutionStatusTag, renderAuthor, buildFirstLookBullets, renderThreadConversation, blockquote, } from "./list-formatters.mjs";
5
5
  import { BODY_TRUNCATE_MAX_CHARS } from "./body-truncate.mjs";
6
6
  import { numberInstructions } from "./iterate-instructions.mjs";
7
- import { renderCheckAnnotation, renderProtectedRun } from "./fix-formatter-extra.mjs";
7
+ import { renderCheckAnnotation } from "./triage-formatter-extra.mjs";
8
8
  import { renderRelatedJobLines } from "./related-jobs-format.mjs";
9
9
  import { isFailingAgentCheck } from "../checks/conclusions.mjs";
10
10
  import { renderMergeCommand } from "../commands/iterate/merge.mjs";
11
11
  import { partitionFixThreads } from "../commands/iterate/fix-instruction-threads.mjs";
12
- export function formatFixCodeResult(header, result, opts = {}) {
12
+ export function formatTriageResult(header, result, opts = {}) {
13
13
  const verbose = opts.verbose ?? false;
14
14
  const topCap = verbose ? undefined : BODY_TRUNCATE_MAX_CHARS;
15
15
  const sections = [header];
16
- if (result.fix.reviewItems) {
17
- const { shown, total } = result.fix.reviewItems;
16
+ if (result.triage.reviewItems) {
17
+ const { shown, total } = result.triage.reviewItems;
18
18
  sections.push(`**showing** ${shown} of ${total} review items`);
19
19
  }
20
20
  if (result.mergedBasePullRequests?.length) {
@@ -41,25 +41,25 @@ export function formatFixCodeResult(header, result, opts = {}) {
41
41
  }
42
42
  }
43
43
  };
44
- const { locatedThreads, unlocatedMutatedThreads, unlocatedThreads } = partitionFixThreads(result.fix.threads, result.fix.resolveCommand, result.fix.resolveOnlyCommand);
44
+ const { locatedThreads, unlocatedMutatedThreads, unlocatedThreads } = partitionFixThreads(result.triage.threads, result.triage.resolveCommand, result.triage.resolveOnlyCommand);
45
45
  renderThreads("## Review threads", [...locatedThreads, ...unlocatedMutatedThreads]);
46
46
  renderThreads("## Unlocated review threads (logged once — no mutation)", unlocatedThreads);
47
- if (result.fix.resolutionOnlyThreads.length > 0) {
47
+ if (result.triage.resolutionOnlyThreads.length > 0) {
48
48
  sections.push("## Review threads to resolve");
49
- sections.push(result.fix.resolutionOnlyThreads
49
+ sections.push(result.triage.resolutionOnlyThreads
50
50
  .map((t) => renderThreadBullet(t, { statusTag: renderThreadResolutionStatusTag(t), verbose }))
51
51
  .join("\n"));
52
52
  }
53
- if (result.fix.actionableComments.length > 0) {
53
+ if (result.triage.actionableComments.length > 0) {
54
54
  sections.push("## Actionable comments");
55
- for (const c of result.fix.actionableComments) {
55
+ for (const c of result.triage.actionableComments) {
56
56
  const heading = c.url ? `[commentId=${c.id}](${c.url})` : `\`commentId=${c.id}\``;
57
57
  const authorizationMarker = c.viewerCanMinimize === false ? " [viewer cannot minimize]" : "";
58
58
  sections.push(`### ${heading} (${renderAuthor(c.author, c.authorType, c.authorAssociation)})${authorizationMarker}`);
59
59
  sections.push(blockquote(c.body, topCap, c.url));
60
60
  }
61
61
  }
62
- const failingChecks = result.fix.checks.filter((ch) => isFailingAgentCheck(ch));
62
+ const failingChecks = result.triage.checks.filter((ch) => isFailingAgentCheck(ch));
63
63
  if (failingChecks.length > 0) {
64
64
  sections.push("## Failing checks");
65
65
  // Multiple failing CheckRuns (matrix jobs, etc.) can share one runId; the rerun command is
@@ -99,7 +99,7 @@ export function formatFixCodeResult(header, result, opts = {}) {
99
99
  });
100
100
  sections.push(bullets.join("\n\n"));
101
101
  }
102
- const annotatedChecks = result.fix.checks
102
+ const annotatedChecks = result.triage.checks
103
103
  .map((ch) => ({
104
104
  ch,
105
105
  rendered: (ch.annotations ?? [])
@@ -121,9 +121,9 @@ export function formatFixCodeResult(header, result, opts = {}) {
121
121
  sections.push(rendered.join("\n\n"));
122
122
  }
123
123
  }
124
- if (result.fix.changesRequestedReviews.length > 0) {
124
+ if (result.triage.changesRequestedReviews.length > 0) {
125
125
  sections.push("## Changes-requested reviews");
126
- for (const r of result.fix.changesRequestedReviews) {
126
+ for (const r of result.triage.changesRequestedReviews) {
127
127
  // A stale bot CR that already had its full body surfaced on a prior tick stays a terse
128
128
  // one-line reminder (renderReviewBullet's staleBotCr branch) — repeating the full body
129
129
  // every tick would be noise, not new content. Every other review (a bot CR's first
@@ -141,65 +141,54 @@ export function formatFixCodeResult(header, result, opts = {}) {
141
141
  sections.push(r.body.trim() === "" ? "(no review body)" : blockquote(r.body, topCap));
142
142
  }
143
143
  }
144
- if (result.fix.firstLookSummaries.length > 0) {
144
+ if (result.triage.firstLookSummaries.length > 0) {
145
145
  sections.push("## Review summaries (first look)");
146
- for (const r of result.fix.firstLookSummaries) {
146
+ for (const r of result.triage.firstLookSummaries) {
147
147
  sections.push(`### \`reviewId=${r.id}\` (${renderAuthor(r.author, r.authorType, r.authorAssociation)})${r.viewerCanMinimize === false ? " [viewer cannot minimize]" : ""}`);
148
148
  sections.push(r.body.trim() === "" ? "(no review body)" : blockquote(r.body, topCap));
149
149
  }
150
150
  }
151
- if (result.fix.editedSummaries.length > 0) {
151
+ if (result.triage.editedSummaries.length > 0) {
152
152
  sections.push("## Review summaries (edited since first look — already minimized; do not re-minimize)");
153
- for (const r of result.fix.editedSummaries) {
153
+ for (const r of result.triage.editedSummaries) {
154
154
  sections.push(`### \`reviewId=${r.id}\` (${renderAuthor(r.author, r.authorType, r.authorAssociation)})${r.viewerCanMinimize === false ? " [viewer cannot minimize]" : ""}`);
155
155
  sections.push(r.body.trim() === "" ? "(no review body)" : blockquote(r.body, topCap));
156
156
  }
157
157
  }
158
- const firstLookSummaryIds = new Set(result.fix.firstLookSummaries.map((r) => r.id));
159
- const seenSummaryIds = result.fix.reviewSummaryIds.filter((id) => !firstLookSummaryIds.has(id));
158
+ const firstLookSummaryIds = new Set(result.triage.firstLookSummaries.map((r) => r.id));
159
+ const seenSummaryIds = result.triage.reviewSummaryIds.filter((id) => !firstLookSummaryIds.has(id));
160
160
  if (seenSummaryIds.length > 0) {
161
161
  sections.push("## Review IDs to minimize queue");
162
162
  sections.push(seenSummaryIds.map((id) => `- \`${id}\``).join("\n"));
163
163
  }
164
- if (result.fix.surfacedApprovals.length > 0) {
164
+ if (result.triage.surfacedApprovals.length > 0) {
165
165
  sections.push("## Approvals (surfaced — not minimized)");
166
- for (const r of result.fix.surfacedApprovals) {
166
+ for (const r of result.triage.surfacedApprovals) {
167
167
  sections.push(`### \`reviewId=${r.id}\` (${renderAuthor(r.author, r.authorType, r.authorAssociation)})${r.viewerCanMinimize === false ? " [viewer cannot minimize]" : ""}`);
168
168
  sections.push(r.body.trim() === "" ? "(no review body)" : blockquote(r.body, topCap));
169
169
  }
170
170
  }
171
- const firstLookTotal = result.fix.firstLookThreads.length + result.fix.firstLookComments.length;
171
+ const firstLookTotal = result.triage.firstLookThreads.length + result.triage.firstLookComments.length;
172
172
  if (firstLookTotal > 0) {
173
173
  sections.push(`## First-look items (${firstLookTotal}) — acknowledge status before acting`);
174
- const resolutionOnlyIds = new Set(result.fix.resolutionOnlyThreads.map((t) => t.id));
175
- sections.push(buildFirstLookBullets(result.fix.firstLookThreads, resolutionOnlyIds, result.fix.firstLookComments, verbose).join("\n"));
176
- }
177
- if (result.fix.inProgressRunIds.length > 0) {
178
- sections.push("## In-progress runs");
179
- sections.push(result.fix.inProgressRunIds.map((id) => `- \`${id}\``).join("\n"));
180
- }
181
- if (result.fix.protectedRuns.length > 0) {
182
- sections.push("## Protected runs", result.fix.protectedRuns.map(renderProtectedRun).join("\n"));
183
- }
184
- if (result.cancelled.length > 0) {
185
- sections.push("## Cancelled runs");
186
- sections.push(result.cancelled.map((id) => `- \`${id}\``).join("\n"));
174
+ const resolutionOnlyIds = new Set(result.triage.resolutionOnlyThreads.map((t) => t.id));
175
+ sections.push(buildFirstLookBullets(result.triage.firstLookThreads, resolutionOnlyIds, result.triage.firstLookComments, verbose).join("\n"));
187
176
  }
188
177
  // The review mutation commands render inline in their `## Instructions` steps.
189
178
  const postFixLines = verbose ? [`- base: \`${result.baseBranch}\``] : [];
190
- if (result.fix.requeue) {
191
- postFixLines.push(`- requeue: ${inlineCode(renderMergeCommand(result.fix.requeue.command))}`);
192
- if (result.fix.requeue.queueApiFallbackCommand) {
193
- postFixLines.push(`- requeue API fallback: ${inlineCode(renderMergeCommand(result.fix.requeue.queueApiFallbackCommand))}`);
179
+ if (result.triage.requeue) {
180
+ postFixLines.push(`- requeue: ${inlineCode(renderMergeCommand(result.triage.requeue.command))}`);
181
+ if (result.triage.requeue.queueApiFallbackCommand) {
182
+ postFixLines.push(`- requeue API fallback: ${inlineCode(renderMergeCommand(result.triage.requeue.queueApiFallbackCommand))}`);
194
183
  }
195
184
  }
196
- if (result.fix.queueRemovalAcknowledgment) {
197
- postFixLines.push(`- acknowledge queue removal: ${inlineCode(renderMergeCommand(result.fix.queueRemovalAcknowledgment))}`);
185
+ if (result.triage.queueRemovalAcknowledgment) {
186
+ postFixLines.push(`- acknowledge queue removal: ${inlineCode(renderMergeCommand(result.triage.queueRemovalAcknowledgment))}`);
198
187
  }
199
188
  if (postFixLines.length > 0)
200
189
  sections.push("## Post-fix actions", postFixLines.join("\n"));
201
190
  sections.push("## Instructions");
202
- sections.push(numberInstructions(result.fix.instructions));
191
+ sections.push(numberInstructions(result.triage.instructions));
203
192
  return joinSections(sections);
204
193
  }
205
194
  function indentBlockquote(body, indent) {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Reject flags a subcommand does not accept before its PR target is resolved. Without this, an
3
+ * unknown or removed flag is treated as value-taking and swallows the PR number that follows it,
4
+ * so the command silently targets the branch-inferred PR instead.
5
+ */
6
+ export declare function rejectUnknownFlags(args: string[], command: string, knownFlags: readonly string[]): boolean;
@@ -0,0 +1,29 @@
1
+ import { EXIT } from "../exit-codes.mjs";
2
+ import { FLAGS_WITH_VALUES } from "./arg-flags.mjs";
3
+ const GLOBAL_FLAGS = ["--format", "--transport", "--verbose", "--help", "-h"];
4
+ /**
5
+ * Reject flags a subcommand does not accept before its PR target is resolved. Without this, an
6
+ * unknown or removed flag is treated as value-taking and swallows the PR number that follows it,
7
+ * so the command silently targets the branch-inferred PR instead.
8
+ */
9
+ export function rejectUnknownFlags(args, command, knownFlags) {
10
+ const known = new Set([...GLOBAL_FLAGS, ...knownFlags]);
11
+ let unknown;
12
+ for (let i = 0; i < args.length; i += 1) {
13
+ const arg = args[i];
14
+ if (!arg.startsWith("-"))
15
+ continue;
16
+ if (!known.has(arg.split("=", 1)[0])) {
17
+ unknown = arg;
18
+ break;
19
+ }
20
+ // A separate value (`--message -1`) is the flag's argument, not another flag.
21
+ if (FLAGS_WITH_VALUES.has(arg))
22
+ i += 1;
23
+ }
24
+ if (unknown === undefined)
25
+ return false;
26
+ process.stderr.write(`pr-shepherd: ${command}: unknown flag "${unknown}".\n`);
27
+ process.exitCode = EXIT.USAGE;
28
+ return true;
29
+ }