@h-rig/cli 0.0.6-alpha.83 → 0.0.6-alpha.85

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 (163) hide show
  1. package/dist/bin/rig.js +22060 -14620
  2. package/dist/src/app/board.js +155 -28
  3. package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
  4. package/dist/src/app-opentui/adapters/doctor.js +1 -25
  5. package/dist/src/app-opentui/adapters/family.d.ts +62 -0
  6. package/dist/src/app-opentui/adapters/family.js +14305 -0
  7. package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
  8. package/dist/src/app-opentui/adapters/fleet.js +6 -40
  9. package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
  10. package/dist/src/app-opentui/adapters/inbox.js +54 -59
  11. package/dist/src/app-opentui/adapters/init.d.ts +0 -2
  12. package/dist/src/app-opentui/adapters/init.js +0 -26
  13. package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
  14. package/dist/src/app-opentui/adapters/inspect.js +1024 -0
  15. package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
  16. package/dist/src/app-opentui/adapters/pi-attach.js +348 -93
  17. package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
  18. package/dist/src/app-opentui/adapters/pi.js +363 -0
  19. package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
  20. package/dist/src/app-opentui/adapters/plugin.js +544 -0
  21. package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
  22. package/dist/src/app-opentui/adapters/repo.js +186 -0
  23. package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
  24. package/dist/src/app-opentui/adapters/run-detail.js +97 -44
  25. package/dist/src/app-opentui/adapters/server.d.ts +10 -2
  26. package/dist/src/app-opentui/adapters/server.js +96 -27
  27. package/dist/src/app-opentui/adapters/tasks.d.ts +0 -2
  28. package/dist/src/app-opentui/adapters/tasks.js +437 -105
  29. package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
  30. package/dist/src/app-opentui/adapters/workspace.js +333 -0
  31. package/dist/src/app-opentui/autocomplete.d.ts +20 -0
  32. package/dist/src/app-opentui/autocomplete.js +576 -0
  33. package/dist/src/app-opentui/bootstrap.js +24313 -16963
  34. package/dist/src/app-opentui/command-palette.d.ts +3 -0
  35. package/dist/src/app-opentui/command-palette.js +1010 -0
  36. package/dist/src/app-opentui/drone.js +8 -6
  37. package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
  38. package/dist/src/app-opentui/fleet-stats.js +114 -0
  39. package/dist/src/app-opentui/index.js +3438 -1724
  40. package/dist/src/app-opentui/intent.js +71 -48
  41. package/dist/src/app-opentui/keymap.d.ts +6 -5
  42. package/dist/src/app-opentui/keymap.js +1064 -23
  43. package/dist/src/app-opentui/layout.d.ts +7 -0
  44. package/dist/src/app-opentui/layout.js +13 -6
  45. package/dist/src/app-opentui/list-search.d.ts +24 -0
  46. package/dist/src/app-opentui/list-search.js +88 -1
  47. package/dist/src/app-opentui/pi-host-child.js +33 -1
  48. package/dist/src/app-opentui/pi-pty-host.d.ts +5 -0
  49. package/dist/src/app-opentui/pi-pty-host.js +21 -3
  50. package/dist/src/app-opentui/react/App.d.ts +9 -0
  51. package/dist/src/app-opentui/react/App.js +5099 -0
  52. package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
  53. package/dist/src/app-opentui/react/Backdrop.js +829 -0
  54. package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
  55. package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
  56. package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
  57. package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
  58. package/dist/src/app-opentui/react/context.d.ts +17 -0
  59. package/dist/src/app-opentui/react/context.js +37 -0
  60. package/dist/src/app-opentui/react/launch.d.ts +2 -0
  61. package/dist/src/app-opentui/react/launch.js +5698 -0
  62. package/dist/src/app-opentui/react/nav.d.ts +18 -0
  63. package/dist/src/app-opentui/react/nav.js +54 -0
  64. package/dist/src/app-opentui/react/scroll.d.ts +12 -0
  65. package/dist/src/app-opentui/react/scroll.js +21 -0
  66. package/dist/src/app-opentui/react/syntax.d.ts +2 -0
  67. package/dist/src/app-opentui/react/syntax.js +64 -0
  68. package/dist/src/app-opentui/registry.js +19923 -5151
  69. package/dist/src/app-opentui/render/constants.d.ts +31 -0
  70. package/dist/src/app-opentui/render/constants.js +66 -0
  71. package/dist/src/app-opentui/render/graphics.d.ts +2 -1
  72. package/dist/src/app-opentui/render/graphics.js +106 -39
  73. package/dist/src/app-opentui/render/image-visual-layer-worker.js +108 -20
  74. package/dist/src/app-opentui/render/image-visual-layer.d.ts +7 -0
  75. package/dist/src/app-opentui/render/image-visual-layer.js +109 -20
  76. package/dist/src/app-opentui/render/native-host.d.ts +37 -0
  77. package/dist/src/app-opentui/render/native-host.js +179 -0
  78. package/dist/src/app-opentui/render/panels.js +18 -11
  79. package/dist/src/app-opentui/render/preloader.js +8 -6
  80. package/dist/src/app-opentui/render/scene.d.ts +50 -1
  81. package/dist/src/app-opentui/render/scene.js +183 -6
  82. package/dist/src/app-opentui/render/text.d.ts +7 -1
  83. package/dist/src/app-opentui/render/text.js +10 -7
  84. package/dist/src/app-opentui/render/type-bar.js +69 -30
  85. package/dist/src/app-opentui/runtime.d.ts +44 -1
  86. package/dist/src/app-opentui/runtime.js +3231 -1363
  87. package/dist/src/app-opentui/scenes/command.js +20 -6
  88. package/dist/src/app-opentui/scenes/doctor.js +176 -11
  89. package/dist/src/app-opentui/scenes/error.js +20 -10
  90. package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
  91. package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
  92. package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
  93. package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
  94. package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
  95. package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
  96. package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
  97. package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
  98. package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
  99. package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
  100. package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
  101. package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
  102. package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
  103. package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
  104. package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
  105. package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
  106. package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
  107. package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
  108. package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
  109. package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
  110. package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
  111. package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
  112. package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
  113. package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
  114. package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
  115. package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
  116. package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
  117. package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
  118. package/dist/src/app-opentui/scenes/family.d.ts +3 -0
  119. package/dist/src/app-opentui/scenes/family.js +2144 -0
  120. package/dist/src/app-opentui/scenes/fleet.js +521 -31
  121. package/dist/src/app-opentui/scenes/handoff.js +204 -12
  122. package/dist/src/app-opentui/scenes/help.js +609 -40
  123. package/dist/src/app-opentui/scenes/inbox.js +278 -17
  124. package/dist/src/app-opentui/scenes/init.js +84 -39
  125. package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
  126. package/dist/src/app-opentui/scenes/inspect.js +793 -0
  127. package/dist/src/app-opentui/scenes/main.js +218 -39
  128. package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
  129. package/dist/src/app-opentui/scenes/pi.js +508 -0
  130. package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
  131. package/dist/src/app-opentui/scenes/plugin.js +486 -0
  132. package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
  133. package/dist/src/app-opentui/scenes/repo.js +424 -0
  134. package/dist/src/app-opentui/scenes/run-detail.js +333 -20
  135. package/dist/src/app-opentui/scenes/server.js +181 -15
  136. package/dist/src/app-opentui/scenes/tasks.js +486 -31
  137. package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
  138. package/dist/src/app-opentui/scenes/workspace.js +426 -0
  139. package/dist/src/app-opentui/selectable.d.ts +19 -0
  140. package/dist/src/app-opentui/selectable.js +79 -0
  141. package/dist/src/app-opentui/state.js +83 -30
  142. package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
  143. package/dist/src/app-opentui/surface-catalog.js +540 -0
  144. package/dist/src/app-opentui/theme.d.ts +28 -6
  145. package/dist/src/app-opentui/theme.js +61 -8
  146. package/dist/src/app-opentui/types.d.ts +121 -4
  147. package/dist/src/commands/_authority-runs.d.ts +1 -1
  148. package/dist/src/commands/_authority-runs.js +2 -12
  149. package/dist/src/commands/_help-catalog.js +95 -15
  150. package/dist/src/commands/_operator-view.js +34 -2
  151. package/dist/src/commands/_pi-frontend.d.ts +2 -0
  152. package/dist/src/commands/_pi-frontend.js +34 -0
  153. package/dist/src/commands/_server-events.d.ts +26 -0
  154. package/dist/src/commands/_server-events.js +310 -0
  155. package/dist/src/commands/agent.js +2 -12
  156. package/dist/src/commands/run.js +34 -2
  157. package/dist/src/commands/server.js +2 -12
  158. package/dist/src/commands/stats.js +95 -15
  159. package/dist/src/commands/task-run-driver.js +2 -12
  160. package/dist/src/commands/task.js +131 -29
  161. package/dist/src/commands.js +192 -31
  162. package/dist/src/index.js +192 -31
  163. package/package.json +11 -9
@@ -0,0 +1,370 @@
1
+ // @bun
2
+ // packages/cli/src/app-opentui/theme.ts
3
+ import {
4
+ bold as otuiBold,
5
+ dim as otuiDim,
6
+ fg as otuiFg,
7
+ t,
8
+ TextAttributes
9
+ } from "@opentui/core";
10
+ var RIG_UI = {
11
+ bg: "#070809",
12
+ bg2: "#0b0c0e",
13
+ panel: "#101115",
14
+ panel2: "#14161b",
15
+ glass: "#1e2230",
16
+ border: "#2a2e3a",
17
+ ink: "#f4f5f8",
18
+ ink2: "#c7c9d3",
19
+ ink3: "#9aa0ae",
20
+ ink4: "#787b86",
21
+ lime: "#ccff4d",
22
+ limeDim: "#a9d63f",
23
+ cyan: "#56d8ff",
24
+ cyanDim: "#3f9fbd",
25
+ red: "#ff5d5d",
26
+ yellow: "#ffd24d",
27
+ magenta: "#ff79b0"
28
+ };
29
+ var styles = {
30
+ ink: otuiFg(RIG_UI.ink),
31
+ ink2: otuiFg(RIG_UI.ink2),
32
+ ink3: otuiFg(RIG_UI.ink3),
33
+ ink4: otuiFg(RIG_UI.ink4),
34
+ lime: otuiFg(RIG_UI.lime),
35
+ limeDim: otuiFg(RIG_UI.limeDim),
36
+ cyan: otuiFg(RIG_UI.cyan),
37
+ red: otuiFg(RIG_UI.red),
38
+ yellow: otuiFg(RIG_UI.yellow),
39
+ magenta: otuiFg(RIG_UI.magenta)
40
+ };
41
+
42
+ // packages/cli/src/app-opentui/drone.ts
43
+ import { RGBA, StyledText, TextAttributes as TextAttributes2 } from "@opentui/core";
44
+ var COLOR = {
45
+ body: RGBA.fromHex(RIG_UI.lime),
46
+ mini: RGBA.fromHex(RIG_UI.limeDim),
47
+ rotor: RGBA.fromHex(RIG_UI.cyan),
48
+ path: RGBA.fromHex(RIG_UI.cyan),
49
+ eye: RGBA.fromHex(RIG_UI.ink),
50
+ dim: RGBA.fromHex(RIG_UI.ink4),
51
+ ink: RGBA.fromHex(RIG_UI.ink2)
52
+ };
53
+
54
+ // packages/cli/src/app-opentui/render/scene.ts
55
+ function line(text, options = {}) {
56
+ return { text, ...options };
57
+ }
58
+ function blank() {
59
+ return { text: "" };
60
+ }
61
+
62
+ // packages/cli/src/app-opentui/fleet-stats.ts
63
+ var ACTIVE_STATUSES = new Set([
64
+ "running",
65
+ "preparing",
66
+ "created",
67
+ "validating",
68
+ "reviewing",
69
+ "closing-out",
70
+ "stopping",
71
+ "loading"
72
+ ]);
73
+ var BAR_CHARS = ["\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589", "\u2588"];
74
+ function barString(ratio, width) {
75
+ if (width <= 0)
76
+ return "";
77
+ const clamped = Math.max(0, Math.min(1, ratio));
78
+ const totalEighths = Math.round(clamped * width * 8);
79
+ const full = Math.floor(totalEighths / 8);
80
+ const remainder = totalEighths % 8;
81
+ let bar = "\u2588".repeat(Math.min(width, full));
82
+ if (full < width && remainder > 0)
83
+ bar += BAR_CHARS[remainder - 1];
84
+ return bar.padEnd(width, " ").slice(0, width);
85
+ }
86
+ function statusSegments(stats, width) {
87
+ if (width <= 0 || stats.total === 0)
88
+ return { active: "", done: "", failed: "", other: "" };
89
+ const other = Math.max(0, stats.total - stats.active - stats.succeeded - stats.failed);
90
+ const raw = [
91
+ { key: "active", value: stats.active },
92
+ { key: "done", value: stats.succeeded },
93
+ { key: "failed", value: stats.failed },
94
+ { key: "other", value: other }
95
+ ];
96
+ const exact = raw.map((entry) => ({ key: entry.key, exact: entry.value / stats.total * width }));
97
+ const floored = exact.map((entry) => ({ key: entry.key, n: Math.floor(entry.exact), frac: entry.exact - Math.floor(entry.exact) }));
98
+ let remaining = width - floored.reduce((sum, entry) => sum + entry.n, 0);
99
+ for (const entry of [...floored].sort((a, b) => b.frac - a.frac)) {
100
+ if (remaining <= 0)
101
+ break;
102
+ entry.n += 1;
103
+ remaining -= 1;
104
+ }
105
+ const widths = new Map(floored.map((entry) => [entry.key, entry.n]));
106
+ return {
107
+ active: "\u2588".repeat(widths.get("active") ?? 0),
108
+ done: "\u2588".repeat(widths.get("done") ?? 0),
109
+ failed: "\u2588".repeat(widths.get("failed") ?? 0),
110
+ other: "\u2592".repeat(widths.get("other") ?? 0)
111
+ };
112
+ }
113
+
114
+ // packages/cli/src/app-opentui/scenes/family-domains/kit.ts
115
+ function actionItemFor(family, sub) {
116
+ const base = sub.base.join(" ");
117
+ if (sub.direct) {
118
+ const label = `run rig ${family} ${base}`.trim();
119
+ return {
120
+ id: `action:${sub.id}`,
121
+ label,
122
+ intent: { scene: "command", argv: [family, ...sub.base], action: { kind: "command-run", label: `Run rig ${family} ${base}`.trim(), payload: { argv: [family, ...sub.base], family } } },
123
+ message: label
124
+ };
125
+ }
126
+ return {
127
+ id: `action:${sub.id}`,
128
+ label: `configure ${family} ${base}`.trim(),
129
+ intent: { scene: "family", argv: [family], action: { kind: "refresh", label: `Configure ${sub.template}`, payload: { family, formSub: sub.id } } },
130
+ message: `configure & run rig ${family} ${sub.template}`
131
+ };
132
+ }
133
+ function actionDetail(sub) {
134
+ return sub.direct ? sub.description : `${sub.description} \xB7 ${sub.fields.length} field${sub.fields.length === 1 ? "" : "s"}`;
135
+ }
136
+ function actionDeck(ctx, subs, heading = "ACTIONS") {
137
+ const out = [line(heading, { fg: RIG_UI.ink3, bold: true })];
138
+ for (const sub of subs) {
139
+ out.push(ctx.cursor.deck({ label: sub.base.join(" ") || ctx.family, detail: actionDetail(sub) }, actionItemFor(ctx.family, sub)));
140
+ }
141
+ return out;
142
+ }
143
+ function metric(label, value, opts = {}) {
144
+ return line(` ${label.padEnd(22)} ${value}`, { fg: opts.fg ?? RIG_UI.ink2, bold: opts.bold });
145
+ }
146
+ function heading(text) {
147
+ return line(text, { fg: RIG_UI.ink, bold: true });
148
+ }
149
+
150
+ // packages/cli/src/app-opentui/scenes/family-domains/stats.ts
151
+ function num(value) {
152
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
153
+ }
154
+ function numOrNull(value) {
155
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
156
+ }
157
+ function strOrNull(value) {
158
+ return typeof value === "string" && value.length > 0 ? value : null;
159
+ }
160
+ function statusCounts(value) {
161
+ if (typeof value !== "object" || value === null)
162
+ return {};
163
+ const out = {};
164
+ for (const [key, raw] of Object.entries(value)) {
165
+ if (typeof raw === "number" && Number.isFinite(raw))
166
+ out[key] = raw;
167
+ }
168
+ return out;
169
+ }
170
+ function readStats(details) {
171
+ if (!details)
172
+ return null;
173
+ if (typeof details.totalRuns !== "number")
174
+ return null;
175
+ return {
176
+ since: strOrNull(details.since),
177
+ totalRuns: num(details.totalRuns),
178
+ statusCounts: statusCounts(details.statusCounts),
179
+ completedRuns: num(details.completedRuns),
180
+ failedRuns: num(details.failedRuns),
181
+ needsAttentionRuns: num(details.needsAttentionRuns),
182
+ completionRate: numOrNull(details.completionRate),
183
+ failureRate: numOrNull(details.failureRate),
184
+ needsAttentionRate: numOrNull(details.needsAttentionRate),
185
+ medianCompletionMs: numOrNull(details.medianCompletionMs),
186
+ steeringTotal: num(details.steeringTotal),
187
+ steeringPerRun: numOrNull(details.steeringPerRun),
188
+ stallTotal: num(details.stallTotal),
189
+ approvalsRequested: num(details.approvalsRequested),
190
+ approvalsApproved: num(details.approvalsApproved),
191
+ approvalsRejected: num(details.approvalsRejected),
192
+ approvalsPending: num(details.approvalsPending)
193
+ };
194
+ }
195
+ function percent(value) {
196
+ return value === null ? "\u2014" : `${(value * 100).toFixed(1)}%`;
197
+ }
198
+ function duration(ms) {
199
+ if (ms === null)
200
+ return "\u2014";
201
+ if (ms < 1000)
202
+ return `${Math.round(ms)}ms`;
203
+ const seconds = ms / 1000;
204
+ if (seconds < 60)
205
+ return `${seconds.toFixed(0)}s`;
206
+ const minutes = seconds / 60;
207
+ if (minutes < 60)
208
+ return `${Math.floor(minutes)}m ${Math.round(seconds % 60)}s`;
209
+ const hours = Math.floor(minutes / 60);
210
+ return `${hours}h ${Math.round(minutes % 60)}m`;
211
+ }
212
+ function perRun(value) {
213
+ return value === null ? "\u2014" : `${value.toFixed(2)}/run`;
214
+ }
215
+ var ACTIVE_STATUSES2 = new Set([
216
+ "running",
217
+ "preparing",
218
+ "created",
219
+ "validating",
220
+ "reviewing",
221
+ "closing-out",
222
+ "stopping",
223
+ "loading"
224
+ ]);
225
+ function distributionStats(stats) {
226
+ let active = 0;
227
+ const byStatus = new Map;
228
+ for (const [status, count] of Object.entries(stats.statusCounts)) {
229
+ byStatus.set(status, count);
230
+ if (ACTIVE_STATUSES2.has(status))
231
+ active += count;
232
+ }
233
+ return {
234
+ total: stats.totalRuns,
235
+ active,
236
+ succeeded: stats.completedRuns,
237
+ failed: stats.failedRuns,
238
+ byStatus,
239
+ load: stats.totalRuns === 0 ? 0 : active / stats.totalRuns,
240
+ sparkline: []
241
+ };
242
+ }
243
+ function distributionBar(stats) {
244
+ if (stats.totalRuns === 0)
245
+ return [];
246
+ const width = 40;
247
+ const fleet = distributionStats(stats);
248
+ const seg = statusSegments(fleet, width);
249
+ const bar = `${seg.active}${seg.done}${seg.failed}${seg.other}`;
250
+ const completionBar = barString(stats.completionRate ?? 0, width);
251
+ const out = [heading("DISTRIBUTION")];
252
+ out.push(line(` ${"completion".padEnd(12)} ${completionBar} ${percent(stats.completionRate)}`, { fg: RIG_UI.lime }));
253
+ out.push(line(` ${"status mix".padEnd(12)} ${bar}`, { fg: RIG_UI.ink2 }));
254
+ const other = Math.max(0, stats.totalRuns - fleet.active - fleet.succeeded - fleet.failed);
255
+ out.push(line(` active ${fleet.active} \xB7 done ${fleet.succeeded} \xB7 failed ${fleet.failed} \xB7 other ${other} \xB7 total ${stats.totalRuns}`, { fg: RIG_UI.ink3 }));
256
+ return out;
257
+ }
258
+ var SINCE_WINDOWS = [
259
+ { key: "24h", label: "24h", arg: "1d" },
260
+ { key: "7d", label: "7d", arg: "7d" },
261
+ { key: "30d", label: "30d", arg: "30d" },
262
+ { key: "all", label: "all", arg: null }
263
+ ];
264
+ function activeWindowKey(since) {
265
+ if (since === null)
266
+ return "all";
267
+ const parsed = Date.parse(since);
268
+ if (!Number.isFinite(parsed))
269
+ return null;
270
+ const ageDays = (Date.now() - parsed) / (24 * 60 * 60 * 1000);
271
+ if (ageDays <= 1.5)
272
+ return "24h";
273
+ if (ageDays <= 14)
274
+ return "7d";
275
+ return "30d";
276
+ }
277
+ function sinceSwitcher(ctx, since) {
278
+ const active = activeWindowKey(since);
279
+ const out = [heading("WINDOW")];
280
+ for (const window of SINCE_WINDOWS) {
281
+ const isActive = window.key === active;
282
+ const argv = window.arg ? ["stats", "show", "--since", window.arg] : ["stats", "show"];
283
+ const label = isActive ? `[\u25CF ${window.label}]` : `[ ${window.label}]`;
284
+ const item = {
285
+ id: `stats-since:${window.key}`,
286
+ label: `stats since ${window.label}`,
287
+ intent: { scene: "command", argv: [...argv], action: { kind: "command-run", label: `rig ${argv.join(" ")}`, payload: { argv: [...argv], family: ctx.family } } },
288
+ message: window.arg ? `recompute fleet stats over the last ${window.label}` : "recompute fleet stats over all time"
289
+ };
290
+ const content = line(` ${label.padEnd(10)} ${window.arg ? `since ${window.arg}` : "all time"}`, {
291
+ fg: isActive ? RIG_UI.bg : RIG_UI.ink2,
292
+ bg: isActive ? RIG_UI.lime : undefined,
293
+ bold: isActive
294
+ });
295
+ out.push(ctx.cursor.row(content, item));
296
+ }
297
+ return out;
298
+ }
299
+ function volumeTile(label, value, status, fg) {
300
+ return {
301
+ id: `stats-volume:${status}`,
302
+ label: `${label} runs`,
303
+ intent: { scene: "fleet", argv: ["run", "list", "--status", status], action: { kind: "navigate", label: `Fleet \xB7 ${status}`, payload: { fleetStatusFilter: status } } },
304
+ message: `open the fleet filtered to ${status} runs`,
305
+ data: { fg, value }
306
+ };
307
+ }
308
+ var renderStats = (ctx) => {
309
+ const lines = [];
310
+ const stats = readStats(ctx.details);
311
+ if (stats) {
312
+ const window = stats.since ? `since ${stats.since}` : "all time";
313
+ lines.push(line(`FLEET STATS \xB7 ${window}`, { fg: RIG_UI.lime, bold: true }));
314
+ lines.push(blank());
315
+ lines.push(...sinceSwitcher(ctx, stats.since));
316
+ lines.push(blank());
317
+ const dist = distributionBar(stats);
318
+ if (dist.length > 0) {
319
+ lines.push(...dist);
320
+ lines.push(blank());
321
+ }
322
+ lines.push(heading("VOLUME"));
323
+ lines.push(metric("total runs", String(stats.totalRuns), { fg: RIG_UI.ink, bold: true }));
324
+ lines.push(ctx.cursor.row(line(` ${"completed".padEnd(22)} ${stats.completedRuns} (${percent(stats.completionRate)}) \u2192`, { fg: RIG_UI.lime }), volumeTile("completed", String(stats.completedRuns), "completed", RIG_UI.lime)));
325
+ lines.push(ctx.cursor.row(line(` ${"failed".padEnd(22)} ${stats.failedRuns} (${percent(stats.failureRate)}) \u2192`, { fg: RIG_UI.red }), volumeTile("failed", String(stats.failedRuns), "failed", RIG_UI.red)));
326
+ lines.push(ctx.cursor.row(line(` ${"needs attention".padEnd(22)} ${stats.needsAttentionRuns} (${percent(stats.needsAttentionRate)}) \u2192`, { fg: RIG_UI.yellow }), volumeTile("needs-attention", String(stats.needsAttentionRuns), "needs-attention", RIG_UI.yellow)));
327
+ lines.push(blank());
328
+ lines.push(heading("FLOW"));
329
+ lines.push(metric("median run time", duration(stats.medianCompletionMs), { fg: RIG_UI.cyan }));
330
+ lines.push(metric("steering", `${stats.steeringTotal} total \xB7 ${perRun(stats.steeringPerRun)}`, { fg: RIG_UI.ink2 }));
331
+ lines.push(metric("stalls", String(stats.stallTotal), {
332
+ fg: stats.stallTotal > 0 ? RIG_UI.yellow : RIG_UI.ink2
333
+ }));
334
+ lines.push(blank());
335
+ lines.push(heading("APPROVALS"));
336
+ lines.push(metric("requested", String(stats.approvalsRequested), { fg: RIG_UI.ink2 }));
337
+ lines.push(metric("approved", String(stats.approvalsApproved), { fg: RIG_UI.lime }));
338
+ lines.push(metric("rejected", String(stats.approvalsRejected), { fg: RIG_UI.red }));
339
+ lines.push(metric("pending", String(stats.approvalsPending), {
340
+ fg: stats.approvalsPending > 0 ? RIG_UI.yellow : RIG_UI.ink2
341
+ }));
342
+ const other = Object.entries(stats.statusCounts).filter(([status]) => !["completed", "failed", "needs-attention"].includes(status)).sort(([, a], [, b]) => b - a);
343
+ if (other.length > 0) {
344
+ lines.push(blank());
345
+ lines.push(heading("OTHER STATUSES"));
346
+ for (const [status, count] of other)
347
+ lines.push(metric(status, String(count), { fg: RIG_UI.ink2 }));
348
+ }
349
+ lines.push(blank());
350
+ } else if (ctx.state.ran && ctx.state.lines.length > 0) {
351
+ lines.push(line("FLEET STATS", { fg: RIG_UI.lime, bold: true }));
352
+ lines.push(blank());
353
+ lines.push(...sinceSwitcher(ctx, null));
354
+ lines.push(blank());
355
+ for (const text of ctx.state.lines.slice(0, 60))
356
+ lines.push(line(` ${text}`, { fg: RIG_UI.ink2 }));
357
+ lines.push(blank());
358
+ } else {
359
+ lines.push(line("FLEET STATS", { fg: RIG_UI.lime, bold: true }));
360
+ lines.push(line(" no run data captured yet", { fg: RIG_UI.ink4 }));
361
+ lines.push(blank());
362
+ }
363
+ lines.push(...actionDeck(ctx, ctx.snap.subcommands, "ACTIONS"));
364
+ lines.push(blank());
365
+ lines.push(line("pick a window cell to re-scope \xB7 a volume tile drills into the fleet \xB7 direct commands run", { fg: RIG_UI.ink4 }));
366
+ return lines;
367
+ };
368
+ export {
369
+ renderStats
370
+ };
@@ -0,0 +1,3 @@
1
+ import type { StageLayout } from "../layout";
2
+ import type { AppSceneFrame, AppState } from "../types";
3
+ export declare function renderFamilyScene(state: AppState, layout?: StageLayout): AppSceneFrame;