@henryqw/pi-pr 2.0.4 → 2.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.
package/README.md CHANGED
@@ -23,7 +23,9 @@ Run `/pr` without arguments in a GitHub checkout. It reads the current branch pu
23
23
  | Surface | Type | Purpose |
24
24
  | --- | --- | --- |
25
25
  | Footer | ui | Show a linked `PR #number` and one plain-language status. |
26
- | Widget hint | ui | Show at most one hint for the next `/pr` step. |
26
+ | Widget | ui | Show one actionable icon-prefixed `Run /pr to …` hint. |
27
+
28
+ The footer already shows the pull request and status. Actionable widgets omit duplicate identity and status. Each uses one semantic status icon, a space, and a plain `Run /pr to …` route. `✗` marks errors, `!` warnings, `✓` success, and `●` accent or neutral routes. In TUI, only the icon uses a theme color. RPC and non-TUI output use the same plain text without ANSI.
27
29
 
28
30
  ## Flow
29
31
 
@@ -65,7 +67,7 @@ Ordinary conversation comments do not trigger a route or block a merge. Changes
65
67
 
66
68
  The footer and widget load at session start. They refresh after local commits, PR creation, pushes, and each dispatched workflow settles. They also poll every 30 seconds. Polling updates presentation only and may be stale.
67
69
 
68
- The create hint stays hidden until the local branch has a commit beyond its creation point. Any displayed hint clears as soon as `/pr` starts. A dispatched workflow keeps it hidden until the agent settles. A direct merge, no-action route, or failed command refreshes the hint when the handler finishes.
70
+ The create widget stays hidden until the local branch has a commit beyond its creation point. Any displayed widget clears as soon as `/pr` starts. A dispatched workflow keeps it hidden until the agent settles. A direct merge, no-action route, or failed command refreshes the widget when the handler finishes.
69
71
 
70
72
  Presentation uses route priority, so draft appears before running CI. `/pr` reads fresh state before routing or merging. The command is authoritative for actions.
71
73
 
@@ -1,4 +1,4 @@
1
- import { getCapabilities, hyperlink } from "@earendil-works/pi-tui";
1
+ import { getCapabilities, hyperlink, truncateToWidth } from "@earendil-works/pi-tui";
2
2
  import {
3
3
  deriveNextStep,
4
4
  type NextStep,
@@ -93,6 +93,10 @@ export function formatPrFooter(display: PrDisplay, theme: PrTheme): string | und
93
93
  return `${link} · ${theme.fg(display.footer.color, display.footer.text)}`;
94
94
  }
95
95
 
96
- export function formatPrWidget(display: PrDisplay): string | undefined {
97
- return display.widget;
96
+ export function formatPrWidget(display: PrDisplay, theme?: PrTheme, width?: number): string[] | undefined {
97
+ if (display.widget === undefined) return undefined;
98
+ const color = display.footer?.color ?? "accent";
99
+ const icon = color === "error" ? "✗" : color === "warning" ? "!" : color === "success" ? "✓" : "●";
100
+ const line = `${theme ? theme.fg(color, icon) : icon} ${display.widget}`;
101
+ return [width === undefined ? line : truncateToWidth(line, Math.max(1, width))];
98
102
  }
package/extensions/pr.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  formatPrFooter,
13
13
  formatPrWidget,
14
14
  projectPrDisplay,
15
+ type PrDisplay,
15
16
  } from "./pr-ui.ts";
16
17
 
17
18
  const POLL_INTERVAL_MS = 30_000;
@@ -39,10 +40,25 @@ export default function pullRequestExtension(
39
40
  let active: AbortController | undefined;
40
41
  let queued = false;
41
42
  let refreshFailureReported = false;
42
- let displayedWidget: string | undefined;
43
+ let displayedWidget: PrDisplay | undefined;
43
44
  let commandGeneration = 0;
44
45
  const activeInvocations = new Map<number, "routing" | "workflow">();
45
46
 
47
+ const setWidget = (ctx: ExtensionContext, display: PrDisplay | undefined): void => {
48
+ if (display?.widget === undefined) {
49
+ ctx.ui.setWidget(UI_KEY, undefined);
50
+ return;
51
+ }
52
+ if (ctx.mode === "tui") {
53
+ ctx.ui.setWidget(UI_KEY, (_tui, theme) => ({
54
+ invalidate() {},
55
+ render: (width) => formatPrWidget(display, theme, width)!,
56
+ }));
57
+ return;
58
+ }
59
+ ctx.ui.setWidget(UI_KEY, formatPrWidget(display));
60
+ };
61
+
46
62
  const render = (
47
63
  ctx: ExtensionContext,
48
64
  pullRequest: Awaited<ReturnType<typeof loadCurrentPullRequest>>,
@@ -53,10 +69,10 @@ export default function pullRequestExtension(
53
69
  if (pullRequest !== null && footer === undefined) {
54
70
  throw new Error("Current pull request display is missing a footer");
55
71
  }
56
- displayedWidget = formatPrWidget(display);
72
+ displayedWidget = display.widget === undefined ? undefined : display;
57
73
  const widget = activeInvocations.size > 0 ? undefined : displayedWidget;
58
74
  ctx.ui.setStatus(UI_KEY, footer);
59
- ctx.ui.setWidget(UI_KEY, widget === undefined ? undefined : [widget]);
75
+ setWidget(ctx, widget);
60
76
  };
61
77
 
62
78
  const stop = (): void => {
@@ -172,7 +188,7 @@ export default function pullRequestExtension(
172
188
  const generation = sessionGeneration;
173
189
  const invocation = ++commandGeneration;
174
190
  activeInvocations.set(invocation, "routing");
175
- if (displayedWidget !== undefined) ctx.ui.setWidget(UI_KEY, undefined);
191
+ if (displayedWidget !== undefined) setWidget(ctx, undefined);
176
192
  let nextStep: Awaited<ReturnType<typeof commandHandler>>;
177
193
  try {
178
194
  nextStep = await commandHandler(args, ctx);
@@ -181,7 +197,7 @@ export default function pullRequestExtension(
181
197
  cancelRefresh();
182
198
  activeInvocations.delete(invocation);
183
199
  if (!activeInvocations.size) {
184
- ctx.ui.setWidget(UI_KEY, displayedWidget === undefined ? undefined : [displayedWidget]);
200
+ setWidget(ctx, displayedWidget);
185
201
  }
186
202
  refreshInBackground();
187
203
  }
@@ -192,7 +208,7 @@ export default function pullRequestExtension(
192
208
  if (nextStep !== "none" && nextStep !== "merge") {
193
209
  activeInvocations.set(invocation, "workflow");
194
210
  if (nextStep === "create") ctx.ui.setStatus(UI_KEY, undefined);
195
- ctx.ui.setWidget(UI_KEY, undefined);
211
+ setWidget(ctx, undefined);
196
212
  } else {
197
213
  activeInvocations.delete(invocation);
198
214
  refreshInBackground();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-pr",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "Run /pr to automatically choose the current pull request's next safe lifecycle step: create, update, address feedback, fix CI, or merge when ready.",
5
5
  "keywords": [
6
6
  "pi-package",