patchrelay 0.20.3 → 0.20.5
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
CHANGED
package/dist/cli/watch/App.js
CHANGED
|
@@ -29,11 +29,17 @@ async function postRetry(baseUrl, issueKey, bearerToken) {
|
|
|
29
29
|
const headers = { "content-type": "application/json" };
|
|
30
30
|
if (bearerToken)
|
|
31
31
|
headers.authorization = `Bearer ${bearerToken}`;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(new URL(`/api/issues/${encodeURIComponent(issueKey)}/retry`, baseUrl), {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers,
|
|
36
|
+
signal: AbortSignal.timeout(5000),
|
|
37
|
+
});
|
|
38
|
+
return await response.json();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return { reason: "request failed" };
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
export function App({ baseUrl, bearerToken, initialIssueKey }) {
|
|
39
45
|
const { exit } = useApp();
|
|
@@ -48,9 +54,13 @@ export function App({ baseUrl, bearerToken, initialIssueKey }) {
|
|
|
48
54
|
const [promptMode, setPromptMode] = useState(false);
|
|
49
55
|
const [promptBuffer, setPromptBuffer] = useState("");
|
|
50
56
|
const handleRetry = useCallback(() => {
|
|
51
|
-
if (state.activeDetailKey)
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
if (!state.activeDetailKey)
|
|
58
|
+
return;
|
|
59
|
+
setPromptStatus("retrying...");
|
|
60
|
+
void postRetry(baseUrl, state.activeDetailKey, bearerToken).then((result) => {
|
|
61
|
+
setPromptStatus(result.ok ? "retry queued" : `retry failed: ${result.reason ?? "unknown"}`);
|
|
62
|
+
setTimeout(() => setPromptStatus(null), 3000);
|
|
63
|
+
});
|
|
54
64
|
}, [baseUrl, bearerToken, state.activeDetailKey]);
|
|
55
65
|
const [promptStatus, setPromptStatus] = useState(null);
|
|
56
66
|
const handlePromptSubmit = useCallback(() => {
|
|
@@ -149,5 +159,5 @@ export function App({ baseUrl, bearerToken, initialIssueKey }) {
|
|
|
149
159
|
}
|
|
150
160
|
}
|
|
151
161
|
});
|
|
152
|
-
return (_jsx(Box, { flexDirection: "column", children: state.view === "list" ? (_jsx(IssueListView, { issues: filtered, allIssues: state.issues, selectedIndex: state.selectedIndex, connected: state.connected, filter: state.filter, totalCount: state.issues.length })) : state.view === "detail" ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(IssueDetailView, { issue: state.issues.find((i) => i.issueKey === state.activeDetailKey), timeline: state.timeline, follow: state.follow, activeRunStartedAt: state.activeRunStartedAt, tokenUsage: state.tokenUsage, diffSummary: state.diffSummary, plan: state.plan, issueContext: state.issueContext
|
|
162
|
+
return (_jsx(Box, { flexDirection: "column", children: state.view === "list" ? (_jsx(IssueListView, { issues: filtered, allIssues: state.issues, selectedIndex: state.selectedIndex, connected: state.connected, filter: state.filter, totalCount: state.issues.length })) : state.view === "detail" ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(IssueDetailView, { issue: state.issues.find((i) => i.issueKey === state.activeDetailKey), timeline: state.timeline, follow: state.follow, activeRunStartedAt: state.activeRunStartedAt, tokenUsage: state.tokenUsage, diffSummary: state.diffSummary, plan: state.plan, issueContext: state.issueContext }), promptMode && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "prompt> " }), _jsx(Text, { children: promptBuffer }), _jsx(Text, { dimColor: true, children: "_" })] })), promptStatus && !promptMode && (_jsx(Text, { dimColor: true, children: promptStatus }))] })) : (_jsx(FeedView, { events: state.feedEvents, connected: state.connected })) }));
|
|
153
163
|
}
|
|
@@ -35,62 +35,17 @@ function planStepColor(status) {
|
|
|
35
35
|
return "yellow";
|
|
36
36
|
return "white";
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
delegated: "blue", preparing: "blue",
|
|
40
|
-
implementing: "yellow", awaiting_input: "yellow",
|
|
41
|
-
pr_open: "cyan",
|
|
42
|
-
changes_requested: "magenta", repairing_ci: "magenta", repairing_queue: "magenta",
|
|
43
|
-
awaiting_queue: "green", done: "green",
|
|
44
|
-
failed: "red", escalated: "red",
|
|
45
|
-
};
|
|
46
|
-
function CompactSidebar({ issues, activeKey }) {
|
|
47
|
-
return (_jsx(Box, { flexDirection: "column", width: 24, paddingRight: 1, children: issues.map((issue) => {
|
|
48
|
-
const key = issue.issueKey ?? issue.projectId;
|
|
49
|
-
const isCurrent = key === activeKey;
|
|
50
|
-
const sc = SIDEBAR_STATE_COLORS[issue.factoryState] ?? "white";
|
|
51
|
-
return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: isCurrent ? "blueBright" : "white", bold: isCurrent, children: isCurrent ? "\u25b8" : " " }), _jsx(Text, { bold: isCurrent, children: key.padEnd(9) }), _jsx(Text, { color: sc, children: issue.factoryState.slice(0, 10) })] }, key));
|
|
52
|
-
}) }));
|
|
53
|
-
}
|
|
54
|
-
const PRIORITY_LABELS = {
|
|
55
|
-
1: { label: "urgent", color: "red" },
|
|
56
|
-
2: { label: "high", color: "yellow" },
|
|
57
|
-
3: { label: "medium", color: "cyan" },
|
|
58
|
-
4: { label: "low", color: "" },
|
|
59
|
-
};
|
|
60
|
-
function ContextPanel({ issue, ctx }) {
|
|
61
|
-
const parts = [];
|
|
62
|
-
if (ctx.priority != null && ctx.priority > 0) {
|
|
63
|
-
const p = PRIORITY_LABELS[ctx.priority];
|
|
64
|
-
parts.push(p ? `${p.label}` : `p${ctx.priority}`);
|
|
65
|
-
}
|
|
66
|
-
if (issue.prNumber) {
|
|
67
|
-
let pr = `#${issue.prNumber}`;
|
|
68
|
-
if (issue.prReviewState === "approved")
|
|
69
|
-
pr += " \u2713";
|
|
70
|
-
else if (issue.prReviewState === "changes_requested")
|
|
71
|
-
pr += " \u2717";
|
|
72
|
-
parts.push(pr);
|
|
73
|
-
}
|
|
74
|
-
if (ctx.runCount > 0)
|
|
75
|
-
parts.push(`${ctx.runCount} runs`);
|
|
76
|
-
const retries = [
|
|
77
|
-
ctx.ciRepairAttempts > 0 ? `ci:${ctx.ciRepairAttempts}` : "",
|
|
78
|
-
ctx.queueRepairAttempts > 0 ? `q:${ctx.queueRepairAttempts}` : "",
|
|
79
|
-
ctx.reviewFixAttempts > 0 ? `rev:${ctx.reviewFixAttempts}` : "",
|
|
80
|
-
].filter(Boolean).join(" ");
|
|
81
|
-
if (retries)
|
|
82
|
-
parts.push(retries);
|
|
83
|
-
return (_jsxs(Box, { flexDirection: "column", children: [parts.length > 0 && _jsx(Text, { dimColor: true, children: parts.join(" ") }), ctx.description && (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [ctx.description.slice(0, 160), ctx.description.length > 160 ? "\u2026" : ""] }))] }));
|
|
84
|
-
}
|
|
85
|
-
function DetailPanel({ issue, timeline, follow, activeRunStartedAt, tokenUsage, diffSummary, plan, issueContext, }) {
|
|
38
|
+
export function IssueDetailView({ issue, timeline, follow, activeRunStartedAt, tokenUsage, diffSummary, plan, issueContext, }) {
|
|
86
39
|
if (!issue) {
|
|
87
|
-
return _jsx(Text, { color: "red", children: "Issue not found." });
|
|
40
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "Issue not found." }), _jsx(HelpBar, { view: "detail", follow: follow })] }));
|
|
88
41
|
}
|
|
89
42
|
const key = issue.issueKey ?? issue.projectId;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
43
|
+
const meta = [];
|
|
44
|
+
if (tokenUsage)
|
|
45
|
+
meta.push(`${formatTokens(tokenUsage.inputTokens)} in / ${formatTokens(tokenUsage.outputTokens)} out`);
|
|
46
|
+
if (diffSummary && diffSummary.filesChanged > 0)
|
|
47
|
+
meta.push(`${diffSummary.filesChanged}f +${diffSummary.linesAdded} -${diffSummary.linesRemoved}`);
|
|
48
|
+
if (issueContext?.runCount)
|
|
49
|
+
meta.push(`${issueContext.runCount} runs`);
|
|
50
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: key }), _jsx(Text, { color: "cyan", children: issue.factoryState }), issue.activeRunType && _jsx(Text, { color: "yellow", children: issue.activeRunType }), issue.prNumber !== undefined && _jsxs(Text, { dimColor: true, children: ["#", issue.prNumber] }), activeRunStartedAt && _jsx(ElapsedTime, { startedAt: activeRunStartedAt }), meta.length > 0 && _jsx(Text, { dimColor: true, children: meta.join(" ") }), follow && _jsx(Text, { color: "yellow", children: "follow" })] }), issue.title && _jsx(Text, { children: issue.title }), plan && plan.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: plan.map((entry, i) => (_jsxs(Box, { gap: 1, children: [_jsxs(Text, { color: planStepColor(entry.status), children: ["[", planStepSymbol(entry.status), "]"] }), _jsx(Text, { children: entry.step })] }, `plan-${i}`))) })), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Timeline, { entries: timeline, follow: follow }) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "detail", follow: follow }) })] }));
|
|
96
51
|
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useStdout } from "ink";
|
|
3
3
|
import { IssueRow } from "./IssueRow.js";
|
|
4
4
|
import { StatusBar } from "./StatusBar.js";
|
|
5
5
|
import { HelpBar } from "./HelpBar.js";
|
|
6
|
-
// Fixed columns: selector(2) + key(10) + state(11) + run(11) + pr(7) + ago(4) + gaps(6) = ~51
|
|
7
6
|
const FIXED_COLS = 51;
|
|
7
|
+
const CHROME_ROWS = 4;
|
|
8
8
|
export function IssueListView({ issues, allIssues, selectedIndex, connected, filter, totalCount }) {
|
|
9
9
|
const { stdout } = useStdout();
|
|
10
10
|
const cols = stdout?.columns ?? 80;
|
|
11
|
+
const rows = stdout?.rows ?? 24;
|
|
11
12
|
const titleWidth = Math.max(0, cols - FIXED_COLS);
|
|
12
|
-
|
|
13
|
+
const maxVisible = Math.max(1, rows - CHROME_ROWS);
|
|
14
|
+
let startIndex = 0;
|
|
15
|
+
if (issues.length > maxVisible) {
|
|
16
|
+
startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), issues.length - maxVisible));
|
|
17
|
+
}
|
|
18
|
+
const visible = issues.slice(startIndex, startIndex + maxVisible);
|
|
19
|
+
const hiddenAbove = startIndex;
|
|
20
|
+
const hiddenBelow = Math.max(0, issues.length - startIndex - maxVisible);
|
|
21
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { issues: issues, totalCount: totalCount, filter: filter, connected: connected, allIssues: allIssues }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: issues.length === 0 ? (_jsx(Text, { dimColor: true, children: "No issues match the current filter." })) : (_jsxs(_Fragment, { children: [hiddenAbove > 0 && _jsxs(Text, { dimColor: true, children: [" ", hiddenAbove, " more above"] }), visible.map((issue, i) => (_jsx(IssueRow, { issue: issue, selected: startIndex + i === selectedIndex, titleWidth: titleWidth }, issue.issueKey ?? `${issue.projectId}-${startIndex + i}`))), hiddenBelow > 0 && _jsxs(Text, { dimColor: true, children: [" ", hiddenBelow, " more below"] })] })) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "list" }) })] }));
|
|
13
22
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
const STATUS_SYMBOL = {
|
|
4
4
|
completed: "\u2713",
|
|
@@ -23,7 +23,7 @@ function truncate(text, max) {
|
|
|
23
23
|
return line.length > max ? `${line.slice(0, max - 3)}...` : line;
|
|
24
24
|
}
|
|
25
25
|
function renderAgentMessage(item) {
|
|
26
|
-
return (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "message: " }), _jsx(Text, { children:
|
|
26
|
+
return (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "message: " }), _jsx(Text, { wrap: "wrap", children: item.text ?? "" })] }));
|
|
27
27
|
}
|
|
28
28
|
function renderCommand(item) {
|
|
29
29
|
const cmd = item.command ?? "?";
|
|
@@ -64,9 +64,13 @@ export function ItemLine({ item, isLast }) {
|
|
|
64
64
|
case "plan":
|
|
65
65
|
content = renderPlan(item);
|
|
66
66
|
break;
|
|
67
|
-
case "userMessage":
|
|
68
|
-
|
|
67
|
+
case "userMessage": {
|
|
68
|
+
const userText = item.text?.trim();
|
|
69
|
+
if (!userText)
|
|
70
|
+
return _jsx(_Fragment, {});
|
|
71
|
+
content = (_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "you: " }), _jsx(Text, { wrap: "wrap", children: userText })] }));
|
|
69
72
|
break;
|
|
73
|
+
}
|
|
70
74
|
default:
|
|
71
75
|
content = renderDefault(item);
|
|
72
76
|
break;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from "ink";
|
|
2
|
+
import { Box, Text, useStdout } from "ink";
|
|
3
3
|
import { TimelineRow } from "./TimelineRow.js";
|
|
4
|
-
const
|
|
4
|
+
const DETAIL_CHROME_ROWS = 10;
|
|
5
5
|
export function Timeline({ entries, follow }) {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const { stdout } = useStdout();
|
|
7
|
+
const rows = stdout?.rows ?? 24;
|
|
8
|
+
const maxVisible = Math.max(5, rows - DETAIL_CHROME_ROWS);
|
|
9
|
+
const tailSize = follow ? Math.min(maxVisible, entries.length) : Math.min(maxVisible, entries.length);
|
|
10
|
+
const visible = entries.length > tailSize ? entries.slice(-tailSize) : entries;
|
|
9
11
|
const skipped = entries.length - visible.length;
|
|
10
12
|
if (entries.length === 0) {
|
|
11
13
|
return _jsx(Text, { dimColor: true, children: "No timeline events yet." });
|
|
12
14
|
}
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", children: [skipped > 0 && _jsxs(Text, { dimColor: true, children: [" ... ", skipped, " earlier
|
|
15
|
+
return (_jsxs(Box, { flexDirection: "column", children: [skipped > 0 && _jsxs(Text, { dimColor: true, children: [" ... ", skipped, " earlier"] }), visible.map((entry) => (_jsx(TimelineRow, { entry: entry }, entry.id)))] }));
|
|
14
16
|
}
|