@h-rig/cli 0.0.6-alpha.80 → 0.0.6-alpha.82

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.
Files changed (105) hide show
  1. package/dist/bin/rig.js +17040 -11249
  2. package/dist/src/app/board.js +32 -3
  3. package/dist/src/app/drone-ui.js +1 -1
  4. package/dist/src/app/theme.js +1 -0
  5. package/dist/src/app-opentui/adapters/common.d.ts +53 -0
  6. package/dist/src/app-opentui/adapters/common.js +149 -0
  7. package/dist/src/app-opentui/adapters/doctor.d.ts +11 -0
  8. package/dist/src/app-opentui/adapters/doctor.js +780 -0
  9. package/dist/src/app-opentui/adapters/fleet.d.ts +16 -0
  10. package/dist/src/app-opentui/adapters/fleet.js +874 -0
  11. package/dist/src/app-opentui/adapters/inbox.d.ts +33 -0
  12. package/dist/src/app-opentui/adapters/inbox.js +1454 -0
  13. package/dist/src/app-opentui/adapters/init.d.ts +13 -0
  14. package/dist/src/app-opentui/adapters/init.js +2357 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +16 -0
  16. package/dist/src/app-opentui/adapters/pi-attach.js +1299 -0
  17. package/dist/src/app-opentui/adapters/run-detail.d.ts +20 -0
  18. package/dist/src/app-opentui/adapters/run-detail.js +893 -0
  19. package/dist/src/app-opentui/adapters/server.d.ts +14 -0
  20. package/dist/src/app-opentui/adapters/server.js +798 -0
  21. package/dist/src/app-opentui/adapters/tasks.d.ts +55 -0
  22. package/dist/src/app-opentui/adapters/tasks.js +3472 -0
  23. package/dist/src/app-opentui/bootstrap.d.ts +16 -0
  24. package/dist/src/app-opentui/bootstrap.js +19515 -0
  25. package/dist/src/app-opentui/drone.d.ts +12 -0
  26. package/dist/src/app-opentui/drone.js +227 -0
  27. package/dist/src/app-opentui/events.d.ts +7 -0
  28. package/dist/src/app-opentui/events.js +28 -0
  29. package/dist/src/app-opentui/index.d.ts +8 -0
  30. package/dist/src/app-opentui/index.js +3485 -0
  31. package/dist/src/app-opentui/intent.d.ts +3 -0
  32. package/dist/src/app-opentui/intent.js +211 -0
  33. package/dist/src/app-opentui/layout.d.ts +15 -0
  34. package/dist/src/app-opentui/layout.js +44 -0
  35. package/dist/src/app-opentui/list-search.d.ts +8 -0
  36. package/dist/src/app-opentui/list-search.js +43 -0
  37. package/dist/src/app-opentui/pi-host-child.d.ts +2 -0
  38. package/dist/src/app-opentui/pi-host-child.js +778 -0
  39. package/dist/src/app-opentui/pi-pty-host.d.ts +64 -0
  40. package/dist/src/app-opentui/pi-pty-host.js +388 -0
  41. package/dist/src/app-opentui/registry.d.ts +4 -0
  42. package/dist/src/app-opentui/registry.js +6839 -0
  43. package/dist/src/app-opentui/render/graphics.d.ts +39 -0
  44. package/dist/src/app-opentui/render/graphics.js +537 -0
  45. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +2 -0
  46. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +1484 -0
  47. package/dist/src/app-opentui/render/image-visual-layer-worker.d.ts +1 -0
  48. package/dist/src/app-opentui/render/image-visual-layer-worker.js +1545 -0
  49. package/dist/src/app-opentui/render/image-visual-layer.d.ts +94 -0
  50. package/dist/src/app-opentui/render/image-visual-layer.js +949 -0
  51. package/dist/src/app-opentui/render/panels.d.ts +10 -0
  52. package/dist/src/app-opentui/render/panels.js +201 -0
  53. package/dist/src/app-opentui/render/scene.d.ts +16 -0
  54. package/dist/src/app-opentui/render/scene.js +110 -0
  55. package/dist/src/app-opentui/render/text.d.ts +10 -0
  56. package/dist/src/app-opentui/render/text.js +121 -0
  57. package/dist/src/app-opentui/render/type-bar.d.ts +14 -0
  58. package/dist/src/app-opentui/render/type-bar.js +137 -0
  59. package/dist/src/app-opentui/runtime.d.ts +2 -0
  60. package/dist/src/app-opentui/runtime.js +3414 -0
  61. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -0
  62. package/dist/src/app-opentui/scenes/doctor.js +91 -0
  63. package/dist/src/app-opentui/scenes/error.d.ts +2 -0
  64. package/dist/src/app-opentui/scenes/error.js +190 -0
  65. package/dist/src/app-opentui/scenes/fleet.d.ts +3 -0
  66. package/dist/src/app-opentui/scenes/fleet.js +207 -0
  67. package/dist/src/app-opentui/scenes/handoff.d.ts +2 -0
  68. package/dist/src/app-opentui/scenes/handoff.js +147 -0
  69. package/dist/src/app-opentui/scenes/help.d.ts +3 -0
  70. package/dist/src/app-opentui/scenes/help.js +138 -0
  71. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -0
  72. package/dist/src/app-opentui/scenes/inbox.js +118 -0
  73. package/dist/src/app-opentui/scenes/init.d.ts +2 -0
  74. package/dist/src/app-opentui/scenes/init.js +94 -0
  75. package/dist/src/app-opentui/scenes/main.d.ts +2 -0
  76. package/dist/src/app-opentui/scenes/main.js +96 -0
  77. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -0
  78. package/dist/src/app-opentui/scenes/run-detail.js +139 -0
  79. package/dist/src/app-opentui/scenes/server.d.ts +2 -0
  80. package/dist/src/app-opentui/scenes/server.js +88 -0
  81. package/dist/src/app-opentui/scenes/tasks.d.ts +3 -0
  82. package/dist/src/app-opentui/scenes/tasks.js +230 -0
  83. package/dist/src/app-opentui/state.d.ts +4 -0
  84. package/dist/src/app-opentui/state.js +286 -0
  85. package/dist/src/app-opentui/theme.d.ts +36 -0
  86. package/dist/src/app-opentui/theme.js +88 -0
  87. package/dist/src/app-opentui/types.d.ts +188 -0
  88. package/dist/src/app-opentui/types.js +1 -0
  89. package/dist/src/commands/_async-ui.js +1 -1
  90. package/dist/src/commands/_operator-view.js +1 -1
  91. package/dist/src/commands/_pi-frontend.js +1 -1
  92. package/dist/src/commands/_spinner.js +1 -1
  93. package/dist/src/commands/browser.js +1 -1
  94. package/dist/src/commands/doctor.js +1 -1
  95. package/dist/src/commands/github.js +1 -1
  96. package/dist/src/commands/inbox.js +1 -1
  97. package/dist/src/commands/init.js +1 -1
  98. package/dist/src/commands/inspect.js +1 -1
  99. package/dist/src/commands/run.js +1 -1
  100. package/dist/src/commands/setup.js +1 -1
  101. package/dist/src/commands/stats.js +1 -1
  102. package/dist/src/commands/task.js +1 -1
  103. package/dist/src/commands.js +33 -4
  104. package/dist/src/index.js +33 -4
  105. package/package.json +11 -8
@@ -0,0 +1,188 @@
1
+ import type { StyledText } from "@opentui/core";
2
+ import type { RunnerContext } from "../runner";
3
+ import type { StageLayout } from "./layout";
4
+ export type AppSceneId = "main" | "fleet" | "tasks" | "inbox" | "server" | "init" | "doctor" | "run-detail" | "handoff" | "help" | "error";
5
+ export type AppActionKind = "none" | "refresh" | "navigate" | "task-run-next" | "task-run-id" | "run-stop" | "run-attach" | "run-steer" | "inbox-approve" | "inbox-reject" | "inbox-answer" | "init-start" | "doctor-run";
6
+ export type AppIntent = {
7
+ readonly scene: AppSceneId;
8
+ readonly argv: readonly string[];
9
+ readonly raw?: string;
10
+ readonly action: {
11
+ readonly kind: AppActionKind;
12
+ readonly label?: string;
13
+ readonly payload?: Record<string, unknown>;
14
+ };
15
+ };
16
+ export type AppStatus = "idle" | "loading" | "action" | "success" | "error";
17
+ export type AppFooterState = {
18
+ readonly project?: string;
19
+ readonly server?: string;
20
+ readonly auth?: string;
21
+ readonly run?: string;
22
+ readonly message?: string;
23
+ readonly [key: string]: string | undefined;
24
+ };
25
+ export type AppTypeBarState = {
26
+ readonly value: string;
27
+ readonly placeholder: string;
28
+ readonly message?: string;
29
+ };
30
+ export type AppErrorState = {
31
+ readonly message: string;
32
+ readonly hint?: string;
33
+ readonly cause?: unknown;
34
+ };
35
+ export type AppSelectionState = {
36
+ readonly index: number;
37
+ readonly count: number;
38
+ };
39
+ export type AppState = {
40
+ readonly projectRoot: string;
41
+ readonly scene: AppSceneId;
42
+ readonly status: AppStatus;
43
+ readonly tick: number;
44
+ readonly argv: readonly string[];
45
+ readonly intent: AppIntent;
46
+ readonly typeBar: AppTypeBarState;
47
+ readonly footer: AppFooterState;
48
+ readonly selection: AppSelectionState;
49
+ readonly data: Record<string, unknown>;
50
+ readonly runtimeReady: boolean;
51
+ readonly actionLabel?: string;
52
+ readonly error?: AppErrorState;
53
+ };
54
+ export type AppStatePatch = Omit<Partial<AppState>, "typeBar" | "footer" | "selection" | "data"> & {
55
+ readonly typeBar?: Partial<AppTypeBarState>;
56
+ readonly footer?: Partial<AppFooterState>;
57
+ readonly selection?: Partial<AppSelectionState>;
58
+ readonly data?: Record<string, unknown>;
59
+ };
60
+ export type AppEvent = {
61
+ readonly type: "scene.change";
62
+ readonly scene: AppSceneId;
63
+ readonly intent?: AppIntent;
64
+ } | {
65
+ readonly type: "action.started";
66
+ readonly label: string;
67
+ readonly optimistic?: Record<string, unknown>;
68
+ } | {
69
+ readonly type: "action.progress";
70
+ readonly label: string;
71
+ readonly detail?: string;
72
+ readonly data?: Record<string, unknown>;
73
+ } | {
74
+ readonly type: "action.completed";
75
+ readonly label: string;
76
+ readonly data?: Record<string, unknown>;
77
+ } | {
78
+ readonly type: "action.failed";
79
+ readonly label: string;
80
+ readonly message: string;
81
+ readonly hint?: string;
82
+ readonly cause?: unknown;
83
+ } | {
84
+ readonly type: "data.patch";
85
+ readonly data: Record<string, unknown>;
86
+ } | {
87
+ readonly type: "footer.patch";
88
+ readonly footer: Partial<AppFooterState>;
89
+ } | {
90
+ readonly type: "typebar.patch";
91
+ readonly typeBar: Partial<AppTypeBarState>;
92
+ } | {
93
+ readonly type: "runtime.ready";
94
+ readonly runner: RunnerContext;
95
+ } | {
96
+ readonly type: "runtime.failed";
97
+ readonly message: string;
98
+ readonly hint?: string;
99
+ readonly cause?: unknown;
100
+ };
101
+ export type AppStore = {
102
+ getState(): AppState;
103
+ setState(next: AppState): void;
104
+ patch(patch: AppStatePatch): void;
105
+ subscribe(listener: (state: AppState) => void): () => void;
106
+ };
107
+ export type AppEventBus = {
108
+ emit(event: AppEvent): void;
109
+ subscribe(listener: (event: AppEvent) => void): () => void;
110
+ };
111
+ export type AppSceneLine = {
112
+ readonly text: string;
113
+ readonly styledText?: StyledText;
114
+ readonly fg?: string;
115
+ readonly bold?: boolean;
116
+ readonly dim?: boolean;
117
+ readonly align?: "left" | "center" | "right";
118
+ /** Zero-based selectable item index for keyboard/mouse navigation. */
119
+ readonly selectableIndex?: number;
120
+ /** Activate on single click instead of only selecting/double-clicking. */
121
+ readonly activateOnClick?: boolean;
122
+ };
123
+ export type AppScenePanel = {
124
+ readonly id: string;
125
+ readonly top: number;
126
+ readonly left?: number;
127
+ readonly width?: number;
128
+ readonly height: number;
129
+ readonly lines: readonly AppSceneLine[];
130
+ readonly backgroundColor?: string;
131
+ readonly backgroundAlpha?: number;
132
+ readonly border?: boolean;
133
+ readonly borderColor?: string;
134
+ readonly borderAlpha?: number;
135
+ readonly opacity?: number;
136
+ readonly zIndex?: number;
137
+ readonly stickyScroll?: boolean;
138
+ readonly stickyStart?: "bottom" | "top" | "left" | "right";
139
+ readonly chrome?: "ad-terminal" | "none";
140
+ readonly headerText?: string;
141
+ readonly headerHeight?: number;
142
+ readonly paddingX?: number;
143
+ readonly paddingY?: number;
144
+ };
145
+ export type AppSceneFrame = {
146
+ readonly scene: AppSceneId;
147
+ readonly title?: string;
148
+ readonly lines: readonly AppSceneLine[];
149
+ readonly panels?: readonly AppScenePanel[];
150
+ readonly footer?: Partial<AppFooterState>;
151
+ readonly typeBarPlaceholder?: string;
152
+ readonly live?: boolean;
153
+ /** Use the full terminal canvas instead of the centered app layout. */
154
+ readonly fullScreen?: boolean;
155
+ /** Hide and blur the command typebar while another terminal app owns input. */
156
+ readonly hideTypeBar?: boolean;
157
+ /** Raw keypresses should be forwarded to the embedded terminal host. */
158
+ readonly terminalActive?: boolean;
159
+ };
160
+ export type AppSceneRenderer = (state: AppState, layout?: StageLayout) => AppSceneFrame;
161
+ export type AppRuntime = {
162
+ getState(): AppState;
163
+ patchState(patch: AppStatePatch): void;
164
+ emit(event: AppEvent): void;
165
+ setScene(scene: AppSceneId, intent?: AppIntent): void;
166
+ runIntent(intent: AppIntent): Promise<void>;
167
+ getRunnerContext(): Promise<RunnerContext>;
168
+ suspend(): void;
169
+ resume(): void;
170
+ destroy(): void;
171
+ };
172
+ export type AppAdapter = {
173
+ readonly id: string;
174
+ start?(runtime: AppRuntime): void | Promise<void>;
175
+ handleIntent?(runtime: AppRuntime, intent: AppIntent): boolean | void | Promise<boolean | void>;
176
+ };
177
+ export type AppLaunchOptions = {
178
+ readonly projectRoot: string;
179
+ readonly argv: readonly string[];
180
+ readonly dryRun?: boolean;
181
+ readonly runId?: string;
182
+ readonly policyMode?: "off" | "observe" | "enforce";
183
+ readonly projectConfigured?: boolean;
184
+ readonly initialFooter?: AppFooterState;
185
+ readonly initializeRuntime?: () => Promise<RunnerContext>;
186
+ readonly adapters?: readonly AppAdapter[];
187
+ readonly sceneRenderers?: Partial<Record<AppSceneId, AppSceneRenderer>>;
188
+ };
@@ -0,0 +1 @@
1
+ // @bun
@@ -33,7 +33,7 @@ function createTtySpinner(input) {
33
33
  output.write("\r\x1B[2K");
34
34
  };
35
35
  render();
36
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
36
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
37
37
  return {
38
38
  setLabel(next) {
39
39
  label = next;
@@ -611,7 +611,7 @@ function createTtySpinner(input) {
611
611
  output.write("\r\x1B[2K");
612
612
  };
613
613
  render();
614
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
614
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
615
615
  return {
616
616
  setLabel(next) {
617
617
  label = next;
@@ -377,7 +377,7 @@ function createTtySpinner(input) {
377
377
  output.write("\r\x1B[2K");
378
378
  };
379
379
  render();
380
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
380
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
381
381
  return {
382
382
  setLabel(next) {
383
383
  label = next;
@@ -30,7 +30,7 @@ function createTtySpinner(input) {
30
30
  output.write("\r\x1B[2K");
31
31
  };
32
32
  render();
33
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
33
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
34
34
  return {
35
35
  setLabel(next) {
36
36
  label = next;
@@ -112,7 +112,7 @@ function createTtySpinner(input) {
112
112
  output.write("\r\x1B[2K");
113
113
  };
114
114
  render();
115
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
115
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
116
116
  return {
117
117
  setLabel(next) {
118
118
  label = next;
@@ -685,7 +685,7 @@ function createTtySpinner(input) {
685
685
  output.write("\r\x1B[2K");
686
686
  };
687
687
  render();
688
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
688
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
689
689
  return {
690
690
  setLabel(next) {
691
691
  label = next;
@@ -399,7 +399,7 @@ function createTtySpinner(input) {
399
399
  output.write("\r\x1B[2K");
400
400
  };
401
401
  render();
402
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
402
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
403
403
  return {
404
404
  setLabel(next) {
405
405
  label = next;
@@ -147,7 +147,7 @@ function createTtySpinner(input) {
147
147
  output.write("\r\x1B[2K");
148
148
  };
149
149
  render();
150
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
150
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
151
151
  return {
152
152
  setLabel(next) {
153
153
  label = next;
@@ -121,7 +121,7 @@ function createTtySpinner(input) {
121
121
  output.write("\r\x1B[2K");
122
122
  };
123
123
  render();
124
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
124
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
125
125
  return {
126
126
  setLabel(next) {
127
127
  label = next;
@@ -429,7 +429,7 @@ function createTtySpinner(input) {
429
429
  output.write("\r\x1B[2K");
430
430
  };
431
431
  render();
432
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
432
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
433
433
  return {
434
434
  setLabel(next) {
435
435
  label = next;
@@ -849,7 +849,7 @@ function createTtySpinner(input) {
849
849
  output.write("\r\x1B[2K");
850
850
  };
851
851
  render();
852
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
852
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
853
853
  return {
854
854
  setLabel(next) {
855
855
  label = next;
@@ -757,7 +757,7 @@ function createTtySpinner(input) {
757
757
  output.write("\r\x1B[2K");
758
758
  };
759
759
  render();
760
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
760
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
761
761
  return {
762
762
  setLabel(next) {
763
763
  label = next;
@@ -469,7 +469,7 @@ function createTtySpinner(input) {
469
469
  output.write("\r\x1B[2K");
470
470
  };
471
471
  render();
472
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
472
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
473
473
  return {
474
474
  setLabel(next) {
475
475
  label = next;
@@ -110,7 +110,7 @@ function createTtySpinner(input) {
110
110
  output.write("\r\x1B[2K");
111
111
  };
112
112
  render();
113
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
113
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
114
114
  return {
115
115
  setLabel(next) {
116
116
  label = next;
@@ -107,6 +107,7 @@ function statusColor(status) {
107
107
  case "validating":
108
108
  case "reviewing":
109
109
  case "closing-out":
110
+ case "stopping":
110
111
  return cyan;
111
112
  case "needs-attention":
112
113
  case "needs_attention":
@@ -243,7 +244,7 @@ function createTtySpinner(input) {
243
244
  output.write("\r\x1B[2K");
244
245
  };
245
246
  render();
246
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
247
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
247
248
  return {
248
249
  setLabel(next) {
249
250
  label = next;
@@ -3844,6 +3845,7 @@ function statusRank(status) {
3844
3845
  case "pending":
3845
3846
  case "preparing":
3846
3847
  case "adopted":
3848
+ case "stopping":
3847
3849
  return 2;
3848
3850
  case "completed":
3849
3851
  case "merged":
@@ -3883,6 +3885,10 @@ class RunsList {
3883
3885
  selectedRun() {
3884
3886
  return this.visibleRuns()[this.selected] ?? null;
3885
3887
  }
3888
+ patchRun(runId, patch) {
3889
+ this.runs = this.runs.map((run) => run.runId === runId ? { ...run, ...patch } : run);
3890
+ this.clampSelection();
3891
+ }
3886
3892
  cycleSort() {
3887
3893
  this.sortMode = this.sortMode === "active" ? "recent" : this.sortMode === "recent" ? "status" : "active";
3888
3894
  this.clampSelection();
@@ -4105,6 +4111,14 @@ class InboxView {
4105
4111
  selectedItem() {
4106
4112
  return this.items[this.selected] ?? null;
4107
4113
  }
4114
+ patchItem(requestId, summary) {
4115
+ this.items = this.items.map((item) => item.requestId === requestId ? { ...item, summary } : item);
4116
+ }
4117
+ removeItem(requestId) {
4118
+ const previousIndex = this.selected;
4119
+ this.items = this.items.filter((item) => item.requestId !== requestId);
4120
+ this.selected = Math.max(0, Math.min(previousIndex, Math.max(0, this.items.length - 1)));
4121
+ }
4108
4122
  invalidate() {}
4109
4123
  render(width) {
4110
4124
  return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
@@ -4326,9 +4340,14 @@ async function runOperatorBoard(context, options = {}) {
4326
4340
  tui.stop();
4327
4341
  };
4328
4342
  let refreshInFlight = false;
4329
- const refresh = async () => {
4343
+ const refresh = async (options2 = {}) => {
4330
4344
  if (refreshInFlight)
4331
4345
  return;
4346
+ if (!options2.quiet) {
4347
+ notice = "refreshing fleet\u2026";
4348
+ renderFooter();
4349
+ tui.requestRender();
4350
+ }
4332
4351
  if (!hasRigProject(context.projectRoot)) {
4333
4352
  list.loading = false;
4334
4353
  list.noProject = true;
@@ -4411,6 +4430,7 @@ async function runOperatorBoard(context, options = {}) {
4411
4430
  };
4412
4431
  const resolveApproval = async (item, decision) => {
4413
4432
  notice = `${decision === "approve" ? "approving" : "rejecting"} ${item.requestId.slice(0, 12)}\u2026`;
4433
+ inboxView.patchItem(item.requestId, `${decision === "approve" ? "approving" : "rejecting"}\u2026 ${item.summary}`);
4414
4434
  renderFooter();
4415
4435
  tui.requestRender();
4416
4436
  try {
@@ -4419,8 +4439,10 @@ async function runOperatorBoard(context, options = {}) {
4419
4439
  headers: { "content-type": "application/json" },
4420
4440
  body: JSON.stringify({ runId: item.runId, requestId: item.requestId, decision, note: null })
4421
4441
  });
4442
+ inboxView.removeItem(item.requestId);
4422
4443
  notice = `${decision === "approve" ? "approved" : "rejected"} \u2014 drone resumes`;
4423
4444
  } catch (error) {
4445
+ inboxView.patchItem(item.requestId, item.summary);
4424
4446
  notice = `resolve failed: ${oneLine(error instanceof Error ? error.message : String(error))}`;
4425
4447
  }
4426
4448
  renderFooter();
@@ -4429,6 +4451,7 @@ async function runOperatorBoard(context, options = {}) {
4429
4451
  };
4430
4452
  const answerInput = async (item, text2) => {
4431
4453
  notice = `answering ${item.requestId.slice(0, 12)}\u2026`;
4454
+ inboxView.patchItem(item.requestId, `answering\u2026 ${item.summary}`);
4432
4455
  renderFooter();
4433
4456
  tui.requestRender();
4434
4457
  try {
@@ -4441,8 +4464,10 @@ async function runOperatorBoard(context, options = {}) {
4441
4464
  answers: { [item.questionId ?? "response"]: text2 }
4442
4465
  })
4443
4466
  });
4467
+ inboxView.removeItem(item.requestId);
4444
4468
  notice = "answer sent \u2014 drone resumes";
4445
4469
  } catch (error) {
4470
+ inboxView.patchItem(item.requestId, item.summary);
4446
4471
  notice = `answer failed: ${oneLine(error instanceof Error ? error.message : String(error))}`;
4447
4472
  }
4448
4473
  renderFooter();
@@ -4591,6 +4616,7 @@ async function runOperatorBoard(context, options = {}) {
4591
4616
  const run = list.selectedRun();
4592
4617
  if (run) {
4593
4618
  notice = `stop requested for ${run.runId.slice(0, 8)}\u2026`;
4619
+ list.patchRun(run.runId, { status: "stopping" });
4594
4620
  renderFooter();
4595
4621
  tui.requestRender();
4596
4622
  stopRunViaServer(context, run.runId).then(() => refresh()).catch(() => refresh());
@@ -4598,6 +4624,9 @@ async function runOperatorBoard(context, options = {}) {
4598
4624
  return { consume: true };
4599
4625
  }
4600
4626
  if (data === "r") {
4627
+ notice = "refreshing fleet\u2026";
4628
+ renderFooter();
4629
+ tui.requestRender();
4601
4630
  refresh();
4602
4631
  return { consume: true };
4603
4632
  }
@@ -4627,7 +4656,7 @@ async function runOperatorBoard(context, options = {}) {
4627
4656
  refresh();
4628
4657
  }, BOARD_REFRESH_MS);
4629
4658
  tui.start();
4630
- refresh();
4659
+ refresh({ quiet: true });
4631
4660
  await new Promise((resolve24) => {
4632
4661
  const poll = setInterval(() => {
4633
4662
  if (stopped) {
@@ -4650,7 +4679,7 @@ async function runOperatorBoardLoop(context, attach, options = {}) {
4650
4679
  await attach(outcome.runId);
4651
4680
  }
4652
4681
  }
4653
- var BOARD_REFRESH_MS = 5000, BOARD_TICK_MS = 250, BOARD_TASK_PAGE = 20;
4682
+ var BOARD_REFRESH_MS = 5000, BOARD_TICK_MS = 16, BOARD_TASK_PAGE = 20;
4654
4683
  var init_board = __esm(() => {
4655
4684
  init_theme();
4656
4685
  init__help_catalog();
package/dist/src/index.js CHANGED
@@ -296,6 +296,7 @@ function statusColor(status) {
296
296
  case "validating":
297
297
  case "reviewing":
298
298
  case "closing-out":
299
+ case "stopping":
299
300
  return cyan;
300
301
  case "needs-attention":
301
302
  case "needs_attention":
@@ -432,7 +433,7 @@ function createTtySpinner(input) {
432
433
  output.write("\r\x1B[2K");
433
434
  };
434
435
  render();
435
- const timer = isTty ? setInterval(render, input.intervalMs ?? 120) : null;
436
+ const timer = isTty ? setInterval(render, input.intervalMs ?? 16) : null;
436
437
  return {
437
438
  setLabel(next) {
438
439
  label = next;
@@ -4033,6 +4034,7 @@ function statusRank(status) {
4033
4034
  case "pending":
4034
4035
  case "preparing":
4035
4036
  case "adopted":
4037
+ case "stopping":
4036
4038
  return 2;
4037
4039
  case "completed":
4038
4040
  case "merged":
@@ -4072,6 +4074,10 @@ class RunsList {
4072
4074
  selectedRun() {
4073
4075
  return this.visibleRuns()[this.selected] ?? null;
4074
4076
  }
4077
+ patchRun(runId, patch) {
4078
+ this.runs = this.runs.map((run) => run.runId === runId ? { ...run, ...patch } : run);
4079
+ this.clampSelection();
4080
+ }
4075
4081
  cycleSort() {
4076
4082
  this.sortMode = this.sortMode === "active" ? "recent" : this.sortMode === "recent" ? "status" : "active";
4077
4083
  this.clampSelection();
@@ -4294,6 +4300,14 @@ class InboxView {
4294
4300
  selectedItem() {
4295
4301
  return this.items[this.selected] ?? null;
4296
4302
  }
4303
+ patchItem(requestId, summary) {
4304
+ this.items = this.items.map((item) => item.requestId === requestId ? { ...item, summary } : item);
4305
+ }
4306
+ removeItem(requestId) {
4307
+ const previousIndex = this.selected;
4308
+ this.items = this.items.filter((item) => item.requestId !== requestId);
4309
+ this.selected = Math.max(0, Math.min(previousIndex, Math.max(0, this.items.length - 1)));
4310
+ }
4297
4311
  invalidate() {}
4298
4312
  render(width) {
4299
4313
  return this.renderLines(width).map((line) => truncateToWidth(line, Math.max(10, width - 1)));
@@ -4515,9 +4529,14 @@ async function runOperatorBoard(context, options = {}) {
4515
4529
  tui.stop();
4516
4530
  };
4517
4531
  let refreshInFlight = false;
4518
- const refresh = async () => {
4532
+ const refresh = async (options2 = {}) => {
4519
4533
  if (refreshInFlight)
4520
4534
  return;
4535
+ if (!options2.quiet) {
4536
+ notice = "refreshing fleet\u2026";
4537
+ renderFooter();
4538
+ tui.requestRender();
4539
+ }
4521
4540
  if (!hasRigProject(context.projectRoot)) {
4522
4541
  list.loading = false;
4523
4542
  list.noProject = true;
@@ -4600,6 +4619,7 @@ async function runOperatorBoard(context, options = {}) {
4600
4619
  };
4601
4620
  const resolveApproval = async (item, decision) => {
4602
4621
  notice = `${decision === "approve" ? "approving" : "rejecting"} ${item.requestId.slice(0, 12)}\u2026`;
4622
+ inboxView.patchItem(item.requestId, `${decision === "approve" ? "approving" : "rejecting"}\u2026 ${item.summary}`);
4603
4623
  renderFooter();
4604
4624
  tui.requestRender();
4605
4625
  try {
@@ -4608,8 +4628,10 @@ async function runOperatorBoard(context, options = {}) {
4608
4628
  headers: { "content-type": "application/json" },
4609
4629
  body: JSON.stringify({ runId: item.runId, requestId: item.requestId, decision, note: null })
4610
4630
  });
4631
+ inboxView.removeItem(item.requestId);
4611
4632
  notice = `${decision === "approve" ? "approved" : "rejected"} \u2014 drone resumes`;
4612
4633
  } catch (error) {
4634
+ inboxView.patchItem(item.requestId, item.summary);
4613
4635
  notice = `resolve failed: ${oneLine(error instanceof Error ? error.message : String(error))}`;
4614
4636
  }
4615
4637
  renderFooter();
@@ -4618,6 +4640,7 @@ async function runOperatorBoard(context, options = {}) {
4618
4640
  };
4619
4641
  const answerInput = async (item, text2) => {
4620
4642
  notice = `answering ${item.requestId.slice(0, 12)}\u2026`;
4643
+ inboxView.patchItem(item.requestId, `answering\u2026 ${item.summary}`);
4621
4644
  renderFooter();
4622
4645
  tui.requestRender();
4623
4646
  try {
@@ -4630,8 +4653,10 @@ async function runOperatorBoard(context, options = {}) {
4630
4653
  answers: { [item.questionId ?? "response"]: text2 }
4631
4654
  })
4632
4655
  });
4656
+ inboxView.removeItem(item.requestId);
4633
4657
  notice = "answer sent \u2014 drone resumes";
4634
4658
  } catch (error) {
4659
+ inboxView.patchItem(item.requestId, item.summary);
4635
4660
  notice = `answer failed: ${oneLine(error instanceof Error ? error.message : String(error))}`;
4636
4661
  }
4637
4662
  renderFooter();
@@ -4780,6 +4805,7 @@ async function runOperatorBoard(context, options = {}) {
4780
4805
  const run = list.selectedRun();
4781
4806
  if (run) {
4782
4807
  notice = `stop requested for ${run.runId.slice(0, 8)}\u2026`;
4808
+ list.patchRun(run.runId, { status: "stopping" });
4783
4809
  renderFooter();
4784
4810
  tui.requestRender();
4785
4811
  stopRunViaServer(context, run.runId).then(() => refresh()).catch(() => refresh());
@@ -4787,6 +4813,9 @@ async function runOperatorBoard(context, options = {}) {
4787
4813
  return { consume: true };
4788
4814
  }
4789
4815
  if (data === "r") {
4816
+ notice = "refreshing fleet\u2026";
4817
+ renderFooter();
4818
+ tui.requestRender();
4790
4819
  refresh();
4791
4820
  return { consume: true };
4792
4821
  }
@@ -4816,7 +4845,7 @@ async function runOperatorBoard(context, options = {}) {
4816
4845
  refresh();
4817
4846
  }, BOARD_REFRESH_MS);
4818
4847
  tui.start();
4819
- refresh();
4848
+ refresh({ quiet: true });
4820
4849
  await new Promise((resolve25) => {
4821
4850
  const poll = setInterval(() => {
4822
4851
  if (stopped) {
@@ -4839,7 +4868,7 @@ async function runOperatorBoardLoop(context, attach, options = {}) {
4839
4868
  await attach(outcome.runId);
4840
4869
  }
4841
4870
  }
4842
- var BOARD_REFRESH_MS = 5000, BOARD_TICK_MS = 250, BOARD_TASK_PAGE = 20;
4871
+ var BOARD_REFRESH_MS = 5000, BOARD_TICK_MS = 16, BOARD_TASK_PAGE = 20;
4843
4872
  var init_board = __esm(() => {
4844
4873
  init_theme();
4845
4874
  init__help_catalog();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/cli",
3
- "version": "0.0.6-alpha.80",
3
+ "version": "0.0.6-alpha.82",
4
4
  "type": "module",
5
5
  "description": "Rig package",
6
6
  "license": "UNLICENSED",
@@ -26,13 +26,16 @@
26
26
  "dependencies": {
27
27
  "@earendil-works/pi-coding-agent": "0.79.0",
28
28
  "@earendil-works/pi-tui": "^0.79.0",
29
- "@rig/client": "npm:@h-rig/client@0.0.6-alpha.80",
30
- "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.80",
31
- "@rig/core": "npm:@h-rig/core@0.0.6-alpha.80",
32
- "@rig/pi-rig": "npm:@h-rig/pi-rig@0.0.6-alpha.80",
33
- "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.80",
34
- "@rig/server": "npm:@h-rig/server@0.0.6-alpha.80",
35
- "@rig/standard-plugin": "npm:@h-rig/standard-plugin@0.0.6-alpha.80",
29
+ "@napi-rs/canvas": "^1.0.0",
30
+ "@opentui/core": "^0.4.1",
31
+ "@rig/client": "npm:@h-rig/client@0.0.6-alpha.82",
32
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.82",
33
+ "@rig/core": "npm:@h-rig/core@0.0.6-alpha.82",
34
+ "@rig/pi-rig": "npm:@h-rig/pi-rig@0.0.6-alpha.82",
35
+ "@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.82",
36
+ "@rig/server": "npm:@h-rig/server@0.0.6-alpha.82",
37
+ "@rig/standard-plugin": "npm:@h-rig/standard-plugin@0.0.6-alpha.82",
38
+ "@xterm/headless": "^6.0.0",
36
39
  "effect": "4.0.0-beta.78",
37
40
  "picocolors": "^1.1.1"
38
41
  }