patchrelay 0.35.12 → 0.35.14

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.
@@ -2,8 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { summarizeIssueStatusNote } from "./issue-status-note.js";
4
4
  import { relativeTime, truncate } from "./format-utils.js";
5
+ import { hasDisplayPrBlocker, isRereviewNeeded, prChecksFact } from "./pr-status.js";
5
6
  // ─── State display ──────────────────────────────────────────────
6
7
  const TERMINAL_STATES = new Set(["done", "failed", "escalated"]);
8
+ function needsOperatorIntervention(issue) {
9
+ return issue.sessionState === "failed" || issue.factoryState === "failed" || issue.factoryState === "escalated";
10
+ }
7
11
  function effectiveState(issue) {
8
12
  if (issue.sessionState === "done")
9
13
  return "done";
@@ -11,13 +15,16 @@ function effectiveState(issue) {
11
15
  return "failed";
12
16
  if (issue.blockedByCount > 0 && !issue.activeRunType)
13
17
  return "blocked";
14
- if (issue.readyForExecution && !issue.activeRunType)
15
- return "ready";
16
18
  if (issue.sessionState === "waiting_input")
17
19
  return "awaiting_input";
20
+ if (issue.readyForExecution && !issue.activeRunType && !hasDisplayPrBlocker(issue))
21
+ return "ready";
18
22
  return issue.factoryState;
19
23
  }
20
24
  function sessionDisplay(issue) {
25
+ if (needsOperatorIntervention(issue)) {
26
+ return { label: "needs help", color: "red" };
27
+ }
21
28
  switch (issue.sessionState) {
22
29
  case "running":
23
30
  return { label: "running", color: "cyan" };
@@ -55,9 +62,7 @@ function stageLabel(issue) {
55
62
  // ─── Context facts (what matters right now) ─────────────────────
56
63
  function buildFacts(issue, selected) {
57
64
  const facts = [];
58
- const rereviewNeeded = issue.prReviewState === "changes_requested"
59
- && (issue.prCheckStatus === "passed" || issue.prCheckStatus === "success")
60
- && !issue.activeRunType;
65
+ const rereviewNeeded = isRereviewNeeded(issue);
61
66
  // PR number
62
67
  if (issue.prNumber !== undefined) {
63
68
  facts.push({ text: `PR #${issue.prNumber}` });
@@ -71,6 +76,9 @@ function buildFacts(issue, selected) {
71
76
  if (issue.waitingReason && issue.sessionState === "waiting_input") {
72
77
  facts.push({ text: issue.waitingReason, color: "yellow" });
73
78
  }
79
+ if (needsOperatorIntervention(issue)) {
80
+ facts.push({ text: "operator action needed", color: "red" });
81
+ }
74
82
  // Review state — only show when it matters (not yet approved, or changes requested)
75
83
  if (issue.prReviewState === "approved") {
76
84
  facts.push({ text: "approved", color: "green" });
@@ -85,23 +93,9 @@ function buildFacts(issue, selected) {
85
93
  facts.push({ text: "awaiting review", color: "yellow" });
86
94
  }
87
95
  // Check status — compact
88
- if (issue.prCheckStatus === "passed" || issue.prCheckStatus === "success") {
89
- facts.push({ text: "checks passed", color: "green" });
90
- }
91
- else if (issue.prCheckStatus === "failed" || issue.prCheckStatus === "failure") {
92
- const failedNames = issue.prChecksSummary?.failedNames ?? [];
93
- const checkInfo = issue.latestFailureCheckName
94
- ?? (failedNames.length > 0 ? failedNames.slice(0, 2).join(", ") : "checks");
95
- facts.push({ text: `${checkInfo} failed`, color: "red" });
96
- }
97
- else if (issue.prCheckStatus === "pending" || issue.prCheckStatus === "in_progress") {
98
- const summary = issue.prChecksSummary;
99
- if (summary && summary.total > 0) {
100
- facts.push({ text: `checks ${summary.completed}/${summary.total}`, color: "yellow" });
101
- }
102
- else {
103
- facts.push({ text: "checks running", color: "yellow" });
104
- }
96
+ const checksFact = prChecksFact(issue);
97
+ if (checksFact) {
98
+ facts.push(checksFact);
105
99
  }
106
100
  // Blocker
107
101
  if (issue.blockedByCount > 0) {
@@ -111,11 +105,11 @@ function buildFacts(issue, selected) {
111
105
  }
112
106
  // ─── What's blocking progress ───────────────────────────────────
113
107
  function blockerText(issue) {
114
- const rereviewNeeded = issue.prReviewState === "changes_requested"
115
- && (issue.prCheckStatus === "passed" || issue.prCheckStatus === "success")
116
- && !issue.activeRunType;
108
+ const rereviewNeeded = isRereviewNeeded(issue);
117
109
  if (issue.sessionState === "waiting_input")
118
110
  return issue.waitingReason ?? "Waiting for input";
111
+ if (needsOperatorIntervention(issue))
112
+ return issue.statusNote ?? issue.waitingReason ?? "Needs operator intervention";
119
113
  if (issue.waitingReason && !issue.activeRunType)
120
114
  return issue.waitingReason;
121
115
  if (issue.blockedByCount > 0)
@@ -126,9 +120,12 @@ function blockerText(issue) {
126
120
  const check = issue.latestFailureCheckName ?? "CI";
127
121
  return `Repairing ${check}`;
128
122
  }
129
- if (issue.prCheckStatus === "failed" || issue.prCheckStatus === "failure") {
130
- const check = issue.latestFailureCheckName ?? "checks";
131
- return `${check} failed`;
123
+ const checksFact = prChecksFact(issue);
124
+ if (checksFact?.color === "red") {
125
+ return checksFact.text;
126
+ }
127
+ if (checksFact?.color === "yellow" && checksFact.text.startsWith("checks ")) {
128
+ return `${checksFact.text} still running`;
132
129
  }
133
130
  if (rereviewNeeded)
134
131
  return "Awaiting re-review after requested changes";
@@ -13,7 +13,8 @@ export function StatusBar({ issues, totalCount, filter, connected, lastServerMes
13
13
  const agg = computeAggregates(aggregateSource);
14
14
  const withPr = aggregateSource.filter((i) => i.prNumber !== undefined).length;
15
15
  const waitingInput = aggregateSource.filter((i) => i.sessionState === "waiting_input" || i.factoryState === "awaiting_input").length;
16
+ const intervention = aggregateSource.filter((i) => i.sessionState === "failed" || i.factoryState === "failed" || i.factoryState === "escalated").length;
16
17
  const running = aggregateSource.filter((i) => i.sessionState === "running").length;
17
18
  const idle = aggregateSource.filter((i) => i.sessionState === "idle").length;
18
- return (_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: showing }), _jsxs(Text, { dimColor: true, children: ["[", FILTER_LABELS[filter], "]"] }), _jsx(Text, { dimColor: true, children: "|" }), running > 0 && _jsxs(Text, { color: "cyan", children: [running, " running"] }), idle > 0 && _jsxs(Text, { color: "blueBright", children: [idle, " idle"] }), agg.ready > 0 && _jsxs(Text, { color: "blueBright", children: [agg.ready, " ready"] }), agg.blocked > 0 && _jsxs(Text, { color: "yellow", children: [agg.blocked, " blocked"] }), withPr > 0 && _jsxs(Text, { dimColor: true, children: [withPr, " PRs"] }), waitingInput > 0 && _jsxs(Text, { color: "yellow", children: [waitingInput, " needs input"] }), agg.done > 0 && _jsxs(Text, { color: "green", children: [agg.done, " done"] }), agg.failed > 0 && _jsxs(Text, { color: "red", children: [agg.failed, " failed"] }), frozen && _jsx(Text, { color: "magenta", children: "frozen" })] }), _jsx(FreshnessBadge, { connected: connected, lastServerMessageAt: lastServerMessageAt })] }));
19
+ return (_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { bold: true, children: showing }), _jsxs(Text, { dimColor: true, children: ["[", FILTER_LABELS[filter], "]"] }), _jsx(Text, { dimColor: true, children: "|" }), running > 0 && _jsxs(Text, { color: "cyan", children: [running, " running"] }), idle > 0 && _jsxs(Text, { color: "blueBright", children: [idle, " idle"] }), agg.ready > 0 && _jsxs(Text, { color: "blueBright", children: [agg.ready, " ready"] }), agg.blocked > 0 && _jsxs(Text, { color: "yellow", children: [agg.blocked, " blocked"] }), withPr > 0 && _jsxs(Text, { dimColor: true, children: [withPr, " PRs"] }), waitingInput > 0 && _jsxs(Text, { color: "yellow", children: [waitingInput, " needs input"] }), intervention > 0 && _jsxs(Text, { color: "red", children: [intervention, " needs help"] }), agg.done > 0 && _jsxs(Text, { color: "green", children: [agg.done, " done"] }), agg.failed > 0 && _jsxs(Text, { color: "red", children: [agg.failed, " failed"] }), frozen && _jsx(Text, { color: "magenta", children: "frozen" })] }), _jsx(FreshnessBadge, { connected: connected, lastServerMessageAt: lastServerMessageAt })] }));
19
20
  }