patchrelay 0.20.1 → 0.20.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "service": "patchrelay",
3
- "version": "0.20.1",
4
- "commit": "d3e7ac8f36d2",
5
- "builtAt": "2026-03-25T21:27:48.361Z"
3
+ "version": "0.20.3",
4
+ "commit": "2f7ed5dc8a34",
5
+ "builtAt": "2026-03-25T21:54:17.794Z"
6
6
  }
@@ -1,12 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { HelpBar } from "./HelpBar.js";
4
4
  const TAIL_SIZE = 30;
5
- const LEVEL_COLORS = {
6
- info: "white",
7
- warn: "yellow",
8
- error: "red",
9
- };
10
5
  const KIND_COLORS = {
11
6
  stage: "cyan",
12
7
  turn: "yellow",
@@ -16,17 +11,17 @@ const KIND_COLORS = {
16
11
  service: "white",
17
12
  workflow: "cyan",
18
13
  linear: "blue",
14
+ comment: "cyan",
19
15
  };
20
16
  function formatTime(iso) {
21
17
  return new Date(iso).toLocaleTimeString("en-GB", { hour12: false });
22
18
  }
23
19
  function FeedEventRow({ event }) {
24
20
  const kindColor = KIND_COLORS[event.kind] ?? "white";
25
- const levelColor = LEVEL_COLORS[event.level] ?? "white";
26
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: formatTime(event.at) }), _jsx(Text, { color: kindColor, children: event.kind.padEnd(10) }), event.issueKey && _jsx(Text, { bold: true, children: event.issueKey.padEnd(10) }), event.stage && _jsx(Text, { color: "cyan", children: event.stage.padEnd(16) }), _jsx(Text, { color: levelColor, children: event.summary })] }));
21
+ return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(event.at), " "] }), _jsx(Text, { color: kindColor, children: (event.status ?? event.kind).padEnd(14) }), event.issueKey && _jsx(Text, { bold: true, children: ` ${event.issueKey.padEnd(9)}` }), _jsxs(Text, { children: [" ", event.summary] })] }));
27
22
  }
28
23
  export function FeedView({ events, connected }) {
29
24
  const visible = events.length > TAIL_SIZE ? events.slice(-TAIL_SIZE) : events;
30
25
  const skipped = events.length - visible.length;
31
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, children: "Operator Feed" }), _jsx(Text, { color: connected ? "green" : "red", children: connected ? "\u25cf connected" : "\u25cb disconnected" })] }), _jsx(Text, { dimColor: true, children: "\u2500".repeat(72) }), events.length === 0 ? (_jsx(Text, { dimColor: true, children: "No feed events yet." })) : (_jsxs(Box, { flexDirection: "column", children: [skipped > 0 && _jsxs(Text, { dimColor: true, children: [" ... ", skipped, " earlier events"] }), visible.map((event) => (_jsx(FeedEventRow, { event: event }, event.id)))] })), _jsx(Text, { dimColor: true, children: "\u2500".repeat(72) }), _jsx(HelpBar, { view: "feed" })] }));
26
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, children: "Operator Feed" }), _jsx(Text, { color: connected ? "green" : "red", children: connected ? "\u25cf connected" : "\u25cb disconnected" })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: events.length === 0 ? (_jsx(Text, { dimColor: true, children: "No feed events yet." })) : (_jsxs(_Fragment, { children: [skipped > 0 && _jsxs(Text, { dimColor: true, children: [" ... ", skipped, " earlier"] }), visible.map((event) => (_jsx(FeedEventRow, { event: event }, event.id)))] })) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "feed" }) })] }));
32
27
  }
@@ -35,26 +35,23 @@ function planStepColor(status) {
35
35
  return "yellow";
36
36
  return "white";
37
37
  }
38
- // ─── Compact Issue Sidebar (#4 split-pane) ───────────────────────
39
38
  const SIDEBAR_STATE_COLORS = {
40
39
  delegated: "blue", preparing: "blue",
41
40
  implementing: "yellow", awaiting_input: "yellow",
42
- pr_open: "cyan", awaiting_review: "cyan",
41
+ pr_open: "cyan",
43
42
  changes_requested: "magenta", repairing_ci: "magenta", repairing_queue: "magenta",
44
43
  awaiting_queue: "green", done: "green",
45
44
  failed: "red", escalated: "red",
46
45
  };
47
46
  function CompactSidebar({ issues, activeKey }) {
48
- return (_jsxs(Box, { flexDirection: "column", width: 28, borderStyle: "single", borderColor: "gray", paddingLeft: 1, paddingRight: 1, children: [_jsx(Text, { bold: true, dimColor: true, children: "Issues" }), issues.map((issue) => {
49
- const key = issue.issueKey ?? issue.projectId;
50
- const isCurrent = key === activeKey;
51
- const stateColor = SIDEBAR_STATE_COLORS[issue.factoryState] ?? "white";
52
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: isCurrent ? "blueBright" : "white", bold: isCurrent, children: isCurrent ? "\u25b8" : " " }), _jsx(Text, { bold: isCurrent, children: key.padEnd(10) }), _jsx(Text, { color: stateColor, children: issue.factoryState.slice(0, 12) })] }, key));
53
- })] }));
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
+ }) }));
54
53
  }
55
- // ─── Issue Context Panel (#5) ────────────────────────────────────
56
54
  const PRIORITY_LABELS = {
57
- 0: { label: "none", color: "" },
58
55
  1: { label: "urgent", color: "red" },
59
56
  2: { label: "high", color: "yellow" },
60
57
  3: { label: "medium", color: "cyan" },
@@ -63,48 +60,37 @@ const PRIORITY_LABELS = {
63
60
  function ContextPanel({ issue, ctx }) {
64
61
  const parts = [];
65
62
  if (ctx.priority != null && ctx.priority > 0) {
66
- const p = PRIORITY_LABELS[ctx.priority] ?? { label: String(ctx.priority), color: "" };
67
- parts.push({ label: "priority", value: p.label, color: p.color });
68
- }
69
- if (ctx.estimate != null) {
70
- parts.push({ label: "estimate", value: String(ctx.estimate), color: "" });
71
- }
72
- if (ctx.currentLinearState) {
73
- parts.push({ label: "linear", value: ctx.currentLinearState, color: "" });
63
+ const p = PRIORITY_LABELS[ctx.priority];
64
+ parts.push(p ? `${p.label}` : `p${ctx.priority}`);
74
65
  }
75
66
  if (issue.prNumber) {
76
- const prInfo = `#${issue.prNumber}${issue.prReviewState === "approved" ? " \u2713" : issue.prReviewState === "changes_requested" ? " \u2717" : ""}${issue.prCheckStatus ? ` ci:${issue.prCheckStatus}` : ""}`;
77
- const prColor = issue.prReviewState === "approved" ? "green" : issue.prReviewState === "changes_requested" ? "red" : "";
78
- parts.push({ label: "pr", value: prInfo, color: prColor });
79
- }
80
- if (ctx.runCount > 0) {
81
- parts.push({ label: "runs", value: String(ctx.runCount), color: "" });
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);
82
73
  }
74
+ if (ctx.runCount > 0)
75
+ parts.push(`${ctx.runCount} runs`);
83
76
  const retries = [
84
77
  ctx.ciRepairAttempts > 0 ? `ci:${ctx.ciRepairAttempts}` : "",
85
- ctx.queueRepairAttempts > 0 ? `queue:${ctx.queueRepairAttempts}` : "",
86
- ctx.reviewFixAttempts > 0 ? `review:${ctx.reviewFixAttempts}` : "",
78
+ ctx.queueRepairAttempts > 0 ? `q:${ctx.queueRepairAttempts}` : "",
79
+ ctx.reviewFixAttempts > 0 ? `rev:${ctx.reviewFixAttempts}` : "",
87
80
  ].filter(Boolean).join(" ");
88
- if (retries) {
89
- parts.push({ label: "retries", value: retries, color: "yellow" });
90
- }
91
- if (ctx.branchName) {
92
- parts.push({ label: "branch", value: ctx.branchName, color: "" });
93
- }
94
- const hasDescription = Boolean(ctx.description);
95
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { gap: 2, flexWrap: "wrap", children: parts.map((p) => (_jsxs(Text, { dimColor: true, children: [p.label, ": ", p.color ? _jsx(Text, { color: p.color, children: p.value }) : _jsx(Text, { dimColor: true, children: p.value })] }, p.label))) }), hasDescription && (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: [ctx.description.slice(0, 200), ctx.description.length > 200 ? "\u2026" : ""] }))] }));
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" : ""] }))] }));
96
84
  }
97
- // ─── Detail Panel (right side of split) ──────────────────────────
98
85
  function DetailPanel({ issue, timeline, follow, activeRunStartedAt, tokenUsage, diffSummary, plan, issueContext, }) {
99
86
  if (!issue) {
100
87
  return _jsx(Text, { color: "red", children: "Issue not found." });
101
88
  }
102
89
  const key = issue.issueKey ?? issue.projectId;
103
- return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, 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: ["PR #", issue.prNumber] }), activeRunStartedAt && _jsx(ElapsedTime, { startedAt: activeRunStartedAt })] }), issue.title && _jsx(Text, { dimColor: true, children: issue.title }), _jsxs(Box, { gap: 2, children: [tokenUsage && (_jsxs(Text, { dimColor: true, children: ["tokens: ", formatTokens(tokenUsage.inputTokens), " in / ", formatTokens(tokenUsage.outputTokens), " out"] })), diffSummary && diffSummary.filesChanged > 0 && (_jsxs(Text, { dimColor: true, children: ["diff: ", diffSummary.filesChanged, " file", diffSummary.filesChanged !== 1 ? "s" : "", " ", "+", diffSummary.linesAdded, " -", diffSummary.linesRemoved] })), follow && _jsx(Text, { color: "yellow", children: "follow" })] }), issueContext && _jsx(ContextPanel, { issue: issue, ctx: issueContext }), plan && plan.length > 0 && (_jsx(Box, { flexDirection: "column", 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(Text, { dimColor: true, children: "\u2500".repeat(60) }), _jsx(Timeline, { entries: timeline, follow: follow })] }));
90
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, 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 })] }), issue.title && _jsx(Text, { children: issue.title }), (tokenUsage || (diffSummary && diffSummary.filesChanged > 0)) && (_jsxs(Box, { gap: 2, children: [tokenUsage && _jsxs(Text, { dimColor: true, children: [formatTokens(tokenUsage.inputTokens), " in / ", formatTokens(tokenUsage.outputTokens), " out"] }), diffSummary && diffSummary.filesChanged > 0 && (_jsxs(Text, { dimColor: true, children: [diffSummary.filesChanged, "f +", diffSummary.linesAdded, " -", diffSummary.linesRemoved] })), follow && _jsx(Text, { color: "yellow", children: "follow" })] })), issueContext && _jsx(ContextPanel, { issue: issue, ctx: issueContext }), 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 }) })] }));
104
91
  }
105
- // ─── Main Detail View (split layout) ─────────────────────────────
106
92
  export function IssueDetailView(props) {
107
93
  const { allIssues, activeDetailKey, follow, ...detailProps } = props;
108
94
  const showSidebar = allIssues.length > 1;
109
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [showSidebar && _jsx(CompactSidebar, { issues: allIssues, activeKey: activeDetailKey }), _jsx(DetailPanel, { ...detailProps, follow: follow })] }), _jsx(Text, { dimColor: true, children: "\u2500".repeat(72) }), _jsx(HelpBar, { view: "detail", follow: follow })] }));
95
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [showSidebar && _jsx(CompactSidebar, { issues: allIssues, activeKey: activeDetailKey }), _jsx(DetailPanel, { ...detailProps, follow: follow })] }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "detail", follow: follow }) })] }));
110
96
  }
@@ -1,8 +1,13 @@
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 { 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
+ const FIXED_COLS = 51;
6
8
  export function IssueListView({ issues, allIssues, selectedIndex, connected, filter, totalCount }) {
7
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusBar, { issues: issues, totalCount: totalCount, filter: filter, connected: connected, allIssues: allIssues }), _jsx(Text, { dimColor: true, children: "\u2500".repeat(72) }), issues.length === 0 ? (_jsx(Text, { dimColor: true, children: "No issues match the current filter." })) : (_jsx(Box, { flexDirection: "column", children: issues.map((issue, index) => (_jsx(IssueRow, { issue: issue, selected: index === selectedIndex }, issue.issueKey ?? `${issue.projectId}-${index}`))) })), _jsx(Text, { dimColor: true, children: "\u2500".repeat(72) }), _jsx(HelpBar, { view: "list" })] }));
9
+ const { stdout } = useStdout();
10
+ const cols = stdout?.columns ?? 80;
11
+ const titleWidth = Math.max(0, cols - FIXED_COLS);
12
+ 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." })) : (issues.map((issue, index) => (_jsx(IssueRow, { issue: issue, selected: index === selectedIndex, titleWidth: titleWidth }, issue.issueKey ?? `${issue.projectId}-${index}`)))) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "list" }) })] }));
8
13
  }
@@ -14,9 +14,44 @@ const STATE_COLORS = {
14
14
  escalated: "red",
15
15
  awaiting_input: "yellow",
16
16
  };
17
+ const STATE_SHORT = {
18
+ delegated: "queued",
19
+ preparing: "prep",
20
+ implementing: "impl",
21
+ pr_open: "pr open",
22
+ changes_requested: "changes",
23
+ repairing_ci: "ci fix",
24
+ awaiting_queue: "merging",
25
+ repairing_queue: "merge fix",
26
+ done: "done",
27
+ failed: "failed",
28
+ escalated: "escalated",
29
+ awaiting_input: "input",
30
+ };
31
+ const RUN_SHORT = {
32
+ implementation: "impl",
33
+ ci_repair: "ci",
34
+ review_fix: "review",
35
+ queue_repair: "merge",
36
+ };
37
+ const STATUS_SHORT = {
38
+ running: "\u25b8",
39
+ completed: "\u2713",
40
+ failed: "\u2717",
41
+ released: "\u2013",
42
+ };
17
43
  function stateColor(state) {
18
44
  return STATE_COLORS[state] ?? "white";
19
45
  }
46
+ function formatRun(issue) {
47
+ const run = issue.activeRunType ?? issue.latestRunType;
48
+ if (!run)
49
+ return "";
50
+ const runLabel = RUN_SHORT[run] ?? run;
51
+ const status = issue.activeRunType ? "running" : issue.latestRunStatus;
52
+ const statusLabel = status ? STATUS_SHORT[status] ?? status : "";
53
+ return `${runLabel} ${statusLabel}`;
54
+ }
20
55
  function formatPr(issue) {
21
56
  if (!issue.prNumber)
22
57
  return "";
@@ -44,15 +79,17 @@ function relativeTime(iso) {
44
79
  return `${days}d`;
45
80
  }
46
81
  function truncate(text, max) {
82
+ if (max <= 0)
83
+ return "";
47
84
  return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
48
85
  }
49
- export function IssueRow({ issue, selected }) {
86
+ export function IssueRow({ issue, selected, titleWidth }) {
50
87
  const key = issue.issueKey ?? issue.projectId;
51
- const state = issue.factoryState;
52
- const run = issue.activeRunType ?? issue.latestRunType;
53
- const runStatus = issue.activeRunType ? "running" : issue.latestRunStatus;
88
+ const state = STATE_SHORT[issue.factoryState] ?? issue.factoryState;
89
+ const run = formatRun(issue);
54
90
  const pr = formatPr(issue);
55
91
  const ago = relativeTime(issue.updatedAt);
56
- const title = issue.title ? truncate(issue.title, 40) : "";
57
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: selected ? "blueBright" : "white", bold: selected, children: selected ? "\u25b8" : " " }), _jsx(Text, { bold: true, children: key.padEnd(10) }), _jsx(Text, { color: stateColor(state), children: state.padEnd(18) }), _jsx(Text, { dimColor: true, children: run ? `${run}:${runStatus ?? "?"}`.padEnd(22) : "".padEnd(22) }), pr ? _jsx(Text, { dimColor: true, children: pr.padEnd(6) }) : _jsx(Text, { dimColor: true, children: "".padEnd(6) }), _jsx(Text, { dimColor: true, children: ago.padStart(4) }), title ? _jsxs(Text, { dimColor: true, children: [" ", title] }) : null] }));
92
+ const tw = titleWidth ?? 30;
93
+ const title = issue.title ? truncate(issue.title, tw) : "";
94
+ return (_jsxs(Box, { children: [_jsx(Text, { color: selected ? "blueBright" : "white", bold: selected, children: selected ? "\u25b8" : " " }), _jsx(Text, { bold: true, children: ` ${key.padEnd(9)}` }), _jsx(Text, { color: stateColor(issue.factoryState), children: ` ${state.padEnd(10)}` }), _jsx(Text, { dimColor: true, children: ` ${run.padEnd(10)}` }), _jsx(Text, { dimColor: true, children: ` ${pr.padEnd(6)}` }), _jsx(Text, { dimColor: true, children: ` ${ago.padStart(3)}` }), title ? _jsx(Text, { dimColor: true, children: ` ${title}` }) : null] }));
58
95
  }
@@ -1,8 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
3
  import { ItemLine } from "./ItemLine.js";
4
4
  function formatTime(iso) {
5
- return new Date(iso).toLocaleTimeString("en-GB", { hour12: false });
5
+ return new Date(iso).toLocaleTimeString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit" });
6
6
  }
7
7
  function formatDuration(startedAt, endedAt) {
8
8
  const ms = new Date(endedAt).getTime() - new Date(startedAt).getTime();
@@ -10,62 +10,45 @@ function formatDuration(startedAt, endedAt) {
10
10
  if (seconds < 60)
11
11
  return `${seconds}s`;
12
12
  const minutes = Math.floor(seconds / 60);
13
- const remainingSeconds = seconds % 60;
14
- return `${minutes}m ${remainingSeconds}s`;
15
- }
16
- const CHECK_SYMBOLS = {
17
- passed: "\u2713",
18
- failed: "\u2717",
19
- pending: "\u25cf",
20
- };
21
- const CHECK_COLORS = {
22
- passed: "green",
23
- failed: "red",
24
- pending: "yellow",
13
+ const s = seconds % 60;
14
+ return `${minutes}m${s > 0 ? ` ${s}s` : ""}`;
15
+ }
16
+ const CHECK_SYMBOLS = { passed: "\u2713", failed: "\u2717", pending: "\u25cf" };
17
+ const CHECK_COLORS = { passed: "green", failed: "red", pending: "yellow" };
18
+ const RUN_LABELS = {
19
+ implementation: "implement",
20
+ ci_repair: "ci fix",
21
+ review_fix: "review fix",
22
+ queue_repair: "merge fix",
25
23
  };
26
24
  function FeedRow({ entry }) {
27
25
  const feed = entry.feed;
28
- const statusLabel = feed.status ?? feed.feedKind;
29
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: formatTime(entry.at) }), _jsx(Text, { color: "cyan", children: statusLabel.padEnd(16) }), _jsx(Text, { children: feed.summary })] }));
30
- }
31
- const RUN_TYPE_LABELS = {
32
- implementation: "implementing",
33
- ci_repair: "repairing checks",
34
- review_fix: "addressing feedback",
35
- queue_repair: "repairing merge",
36
- };
37
- function runLabel(runType) {
38
- return RUN_TYPE_LABELS[runType] ?? runType;
26
+ const label = feed.status ?? feed.feedKind;
27
+ return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { color: "cyan", children: label.padEnd(14) }), _jsxs(Text, { children: [" ", feed.summary] })] }));
39
28
  }
40
29
  function RunStartRow({ entry }) {
41
30
  const run = entry.run;
42
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: formatTime(entry.at) }), _jsx(Text, { bold: true, color: "yellow", children: runLabel(run.runType).padEnd(20) }), _jsx(Text, { bold: true, children: "started" })] }));
31
+ return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { bold: true, color: "yellow", children: (RUN_LABELS[run.runType] ?? run.runType).padEnd(14) }), _jsx(Text, { bold: true, children: " started" })] }));
43
32
  }
44
33
  function RunEndRow({ entry }) {
45
34
  const run = entry.run;
46
35
  const color = run.status === "completed" ? "green" : "red";
47
- const duration = run.endedAt ? formatDuration(run.startedAt, run.endedAt) : "";
48
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: formatTime(entry.at) }), _jsx(Text, { bold: true, color: color, children: runLabel(run.runType).padEnd(20) }), _jsx(Text, { bold: true, color: color, children: run.status }), duration ? _jsxs(Text, { dimColor: true, children: ["(", duration, ")"] }) : null] }));
36
+ const dur = run.endedAt ? ` ${formatDuration(run.startedAt, run.endedAt)}` : "";
37
+ return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { bold: true, color: color, children: (RUN_LABELS[run.runType] ?? run.runType).padEnd(14) }), _jsxs(Text, { bold: true, color: color, children: [" ", run.status] }), dur ? _jsx(Text, { dimColor: true, children: dur }) : null] }));
49
38
  }
50
39
  function ItemRow({ entry }) {
51
- const item = entry.item;
52
- return (_jsx(Box, { paddingLeft: 2, children: _jsx(ItemLine, { item: item, isLast: false }) }));
40
+ return (_jsx(Box, { paddingLeft: 2, children: _jsx(ItemLine, { item: entry.item, isLast: false }) }));
53
41
  }
54
42
  function CIChecksRow({ entry }) {
55
43
  const ci = entry.ciChecks;
56
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: formatTime(entry.at) }), _jsx(Text, { color: CHECK_COLORS[ci.overall] ?? "white", children: "ci_checks".padEnd(16) }), ci.checks.map((check, i) => (_jsxs(Text, { children: [_jsx(Text, { color: CHECK_COLORS[check.status] ?? "white", children: CHECK_SYMBOLS[check.status] ?? " " }), _jsxs(Text, { dimColor: true, children: [" ", check.name, " "] })] }, `check-${i}`)))] }));
44
+ return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { color: CHECK_COLORS[ci.overall] ?? "white", children: "checks".padEnd(14) }), _jsx(Text, { children: " " }), ci.checks.map((check, i) => (_jsxs(Text, { children: [_jsx(Text, { color: CHECK_COLORS[check.status] ?? "white", children: CHECK_SYMBOLS[check.status] ?? " " }), _jsxs(Text, { dimColor: true, children: [check.name, " "] })] }, `c-${i}`)))] }));
57
45
  }
58
46
  export function TimelineRow({ entry }) {
59
47
  switch (entry.kind) {
60
- case "feed":
61
- return _jsx(FeedRow, { entry: entry });
62
- case "run-start":
63
- return _jsx(RunStartRow, { entry: entry });
64
- case "run-end":
65
- return _jsx(RunEndRow, { entry: entry });
66
- case "item":
67
- return _jsx(ItemRow, { entry: entry });
68
- case "ci-checks":
69
- return _jsx(CIChecksRow, { entry: entry });
48
+ case "feed": return _jsx(FeedRow, { entry: entry });
49
+ case "run-start": return _jsx(RunStartRow, { entry: entry });
50
+ case "run-end": return _jsx(RunEndRow, { entry: entry });
51
+ case "item": return _jsx(ItemRow, { entry: entry });
52
+ case "ci-checks": return _jsx(CIChecksRow, { entry: entry });
70
53
  }
71
54
  }
@@ -99,7 +99,9 @@ export class CodexAppServerClient extends EventEmitter {
99
99
  title: "PatchRelay",
100
100
  version: "0.1.0",
101
101
  },
102
- capabilities: null,
102
+ capabilities: {
103
+ experimentalApi: true,
104
+ },
103
105
  });
104
106
  const serverInfo = initializeResponse && typeof initializeResponse === "object" && "serverInfo" in initializeResponse
105
107
  ? initializeResponse.serverInfo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {