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.
- package/dist/build-info.json +3 -3
- package/dist/cli/args.js +19 -0
- package/dist/cli/commands/issues.js +17 -1
- package/dist/cli/data.js +50 -0
- package/dist/cli/formatters/text.js +45 -0
- package/dist/cli/help.js +12 -0
- package/dist/cli/index.js +3 -10
- package/dist/cli/watch/App.js +22 -2
- package/dist/cli/watch/HelpBar.js +1 -1
- package/dist/cli/watch/IssueDetailView.js +63 -161
- package/dist/cli/watch/IssueRow.js +25 -28
- package/dist/cli/watch/StatusBar.js +2 -1
- package/dist/cli/watch/detail-rows.js +590 -0
- package/dist/cli/watch/pr-status.js +74 -0
- package/dist/cli/watch/render-rich-text.js +225 -0
- package/dist/cli/watch/watch-state.js +111 -6
- package/dist/github-webhook-handler.js +176 -2
- package/dist/github-webhooks.js +2 -0
- package/dist/linear-session-sync.js +1 -1
- package/dist/run-orchestrator.js +75 -7
- package/dist/status-note.js +1 -1
- package/package.json +3 -2
|
@@ -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
|
|
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
|
-
|
|
89
|
-
|
|
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
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
return
|
|
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
|
}
|