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

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 (100) hide show
  1. package/dist/bin/build-rig-binaries.js +40 -8
  2. package/dist/bin/rig.js +2798 -1440
  3. package/dist/src/app/board.js +62 -13
  4. package/dist/src/app-opentui/adapters/command.d.ts +2 -0
  5. package/dist/src/app-opentui/adapters/command.js +329 -0
  6. package/dist/src/app-opentui/adapters/common.js +2 -2
  7. package/dist/src/app-opentui/adapters/doctor.js +63 -14
  8. package/dist/src/app-opentui/adapters/fleet.js +84 -20
  9. package/dist/src/app-opentui/adapters/inbox.js +83 -19
  10. package/dist/src/app-opentui/adapters/init.js +87 -23
  11. package/dist/src/app-opentui/adapters/pi-attach.js +96 -34
  12. package/dist/src/app-opentui/adapters/run-detail.js +83 -19
  13. package/dist/src/app-opentui/adapters/server.js +100 -23
  14. package/dist/src/app-opentui/adapters/tasks.d.ts +11 -0
  15. package/dist/src/app-opentui/adapters/tasks.js +742 -94
  16. package/dist/src/app-opentui/bootstrap.d.ts +1 -6
  17. package/dist/src/app-opentui/bootstrap.js +13769 -12368
  18. package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
  19. package/dist/src/app-opentui/command-pty-host.js +248 -0
  20. package/dist/src/app-opentui/events.js +1 -1
  21. package/dist/src/app-opentui/focus-manager.d.ts +14 -0
  22. package/dist/src/app-opentui/focus-manager.js +24 -0
  23. package/dist/src/app-opentui/index.js +3802 -2882
  24. package/dist/src/app-opentui/intent.js +154 -48
  25. package/dist/src/app-opentui/keymap.d.ts +20 -0
  26. package/dist/src/app-opentui/keymap.js +707 -0
  27. package/dist/src/app-opentui/launch-routing.d.ts +16 -0
  28. package/dist/src/app-opentui/launch-routing.js +55 -0
  29. package/dist/src/app-opentui/layout.js +2 -2
  30. package/dist/src/app-opentui/pi-host-child.js +66 -16
  31. package/dist/src/app-opentui/pi-pty-host.d.ts +9 -0
  32. package/dist/src/app-opentui/pi-pty-host.js +9 -11
  33. package/dist/src/app-opentui/registry.js +3231 -2403
  34. package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
  35. package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
  36. package/dist/src/app-opentui/render/graphics.d.ts +1 -1
  37. package/dist/src/app-opentui/render/graphics.js +131 -16
  38. package/dist/src/app-opentui/render/image-visual-layer-worker.js +408 -957
  39. package/dist/src/app-opentui/render/image-visual-layer.d.ts +18 -10
  40. package/dist/src/app-opentui/render/image-visual-layer.js +386 -356
  41. package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
  42. package/dist/src/app-opentui/render/panel-layout.js +48 -0
  43. package/dist/src/app-opentui/render/panels.d.ts +2 -0
  44. package/dist/src/app-opentui/render/panels.js +62 -29
  45. package/dist/src/app-opentui/render/preloader.d.ts +10 -0
  46. package/dist/src/app-opentui/render/preloader.js +163 -0
  47. package/dist/src/app-opentui/render/scene.d.ts +3 -0
  48. package/dist/src/app-opentui/render/scene.js +12 -0
  49. package/dist/src/app-opentui/render/text.js +5 -1
  50. package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
  51. package/dist/src/app-opentui/render/type-bar.js +51 -16
  52. package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
  53. package/dist/src/app-opentui/runtime-resources.js +62 -0
  54. package/dist/src/app-opentui/runtime.js +2322 -1513
  55. package/dist/src/app-opentui/scenes/command.d.ts +3 -0
  56. package/dist/src/app-opentui/scenes/command.js +103 -0
  57. package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
  58. package/dist/src/app-opentui/scenes/doctor.js +53 -14
  59. package/dist/src/app-opentui/scenes/error.js +44 -14
  60. package/dist/src/app-opentui/scenes/fleet.js +40 -21
  61. package/dist/src/app-opentui/scenes/handoff.js +142 -27
  62. package/dist/src/app-opentui/scenes/help.js +63 -48
  63. package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
  64. package/dist/src/app-opentui/scenes/inbox.js +64 -17
  65. package/dist/src/app-opentui/scenes/init.d.ts +2 -1
  66. package/dist/src/app-opentui/scenes/init.js +62 -21
  67. package/dist/src/app-opentui/scenes/main.d.ts +2 -1
  68. package/dist/src/app-opentui/scenes/main.js +80 -24
  69. package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
  70. package/dist/src/app-opentui/scenes/run-detail.js +39 -25
  71. package/dist/src/app-opentui/scenes/server.d.ts +2 -1
  72. package/dist/src/app-opentui/scenes/server.js +71 -20
  73. package/dist/src/app-opentui/scenes/tasks.js +44 -22
  74. package/dist/src/app-opentui/state.js +70 -30
  75. package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
  76. package/dist/src/app-opentui/terminal-capabilities.js +15 -0
  77. package/dist/src/app-opentui/types.d.ts +10 -2
  78. package/dist/src/commands/_doctor-checks.js +62 -13
  79. package/dist/src/commands/_operator-view.js +63 -13
  80. package/dist/src/commands/_pi-frontend.js +63 -13
  81. package/dist/src/commands/_preflight.js +64 -14
  82. package/dist/src/commands/_server-client.js +82 -18
  83. package/dist/src/commands/_snapshot-upload.js +62 -13
  84. package/dist/src/commands/connect.js +7 -1
  85. package/dist/src/commands/doctor.js +62 -13
  86. package/dist/src/commands/github.js +144 -23
  87. package/dist/src/commands/inbox.js +62 -13
  88. package/dist/src/commands/init.js +82 -18
  89. package/dist/src/commands/inspect.js +62 -13
  90. package/dist/src/commands/run.js +66 -13
  91. package/dist/src/commands/server.js +69 -14
  92. package/dist/src/commands/setup.js +62 -13
  93. package/dist/src/commands/stats.js +62 -13
  94. package/dist/src/commands/task-run-driver.js +62 -13
  95. package/dist/src/commands/task.js +65 -14
  96. package/dist/src/commands.js +227 -92
  97. package/dist/src/index.js +234 -99
  98. package/package.json +8 -9
  99. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
  100. package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
@@ -41,95 +41,110 @@ var styles = {
41
41
  function line(text, options = {}) {
42
42
  return { text, ...options };
43
43
  }
44
+ function deckRow(input) {
45
+ const prefix = input.active ? "\u25B8" : " ";
46
+ const label = input.label.toUpperCase().padEnd(10);
47
+ return line(`${prefix} ${label} ${input.detail}`, {
48
+ fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
49
+ bold: input.active,
50
+ selectableIndex: input.index,
51
+ activateOnClick: input.activateOnClick
52
+ });
53
+ }
44
54
  function makeSceneFrame(input) {
45
55
  return input;
46
56
  }
47
57
 
48
58
  // packages/cli/src/app-opentui/scenes/help.ts
49
- var COMMANDS = [
50
- ["runs", "show runs"],
51
- ["tasks", "show task source"],
52
- ["type in Runs", "search loaded runs"],
53
- ["type in Tasks", "search loaded tasks"],
54
- ["tasks open", "filter to open tasks"],
55
- ["tasks all", "show every task returned by the source"],
56
- ["tasks mine", "show tasks assigned to @me"],
57
- ["tasks blocked", "show blocked work"],
58
- ["tasks closed", "show closed work"],
59
- ["tasks sort priority", "rank by priority"],
60
- ["tasks sort recency", "rank by update time"],
61
- ["tasks sort status", "group by status"],
62
- ["run <task-id>", "dispatch selected task id"],
59
+ var ACTIONS = [
60
+ ["runs", "show live/completed runs"],
61
+ ["tasks", "browse task source and dispatch work"],
63
62
  ["run next", "dispatch next runnable task"],
64
- ["attach <run>", "open bundled Pi for a run"],
65
- ["tab / \u2192", "next screen"],
66
- ["\u2190", "previous screen"],
67
- ["\u2191 \u2193", "move selection in Runs/Tasks"],
68
- ["j k", "move selection outside search-first list screens"],
69
- ["enter", "activate selected row"],
63
+ ["server", "local/remote server controls"],
64
+ ["init", "setup/reconfigure/repair wizard"],
65
+ ["doctor", "diagnose project/server/auth"],
66
+ ["github", "GitHub auth/repo actions"],
67
+ ["plugin", "plugin and source actions"],
68
+ ["setup", "bootstrap/check/preflight"],
69
+ ["inspect", "inspect project/server state"],
70
+ ["stats", "run/task metrics"],
71
+ ["repo", "repo utilities"],
72
+ ["profile", "profile/config actions"],
73
+ ["review", "review automation"],
74
+ ["browser", "browser tooling/wizards"],
75
+ ["pi", "Pi install/attach/status"],
76
+ ["queue", "queue operations"],
77
+ ["agent", "agent/runtime operations"],
78
+ ["inspector", "inspector tools"],
79
+ ["dist", "distribution helpers"],
80
+ ["workspace", "workspace/project utilities"],
81
+ ["remote", "remote endpoint utilities"],
82
+ ["git", "git helper actions"],
83
+ ["harness", "harness operations"],
84
+ ["test", "test helpers"]
85
+ ];
86
+ var SHORTCUTS = [
87
+ ["tab / \u2192", "next workspace"],
88
+ ["\u2190 / esc", "previous/back or clear search"],
89
+ ["\u2191 \u2193 / j k", "move selection"],
90
+ ["enter / space / click", "activate selected action"],
70
91
  ["mouse wheel", "scroll panel under cursor"],
71
- ["ctrl-]", "detach from embedded Pi"],
92
+ ["ctrl-]", "detach from embedded Pi/command terminal"],
72
93
  ["ctrl-c / q", "exit"]
73
94
  ];
74
95
  function panelWidth(layout) {
75
96
  return layout?.centerWidth ?? 100;
76
97
  }
77
- function panelHeight(layout, top = 3) {
78
- return Math.max(4, (layout?.centerHeight ?? 24) - top);
79
- }
80
- function clip(value, width) {
81
- if (width <= 0)
82
- return "";
83
- return value.length <= width ? value : `${value.slice(0, Math.max(0, width - 1))}\u2026`;
84
- }
85
- function commandRow(width, label, detail, index) {
86
- const keyWidth = Math.min(22, Math.max(12, Math.floor(width * 0.28)));
87
- const key = clip(label, keyWidth).padEnd(keyWidth);
88
- return line(`${key} ${clip(detail, Math.max(8, width - keyWidth - 4))}`, { fg: index < 3 ? RIG_UI.ink : RIG_UI.ink2 });
98
+ function panelHeight(layout, top = 1) {
99
+ return Math.max(8, (layout?.centerHeight ?? 24) - top);
89
100
  }
90
101
  function unsupportedCommand(state) {
91
102
  const value = state.intent.action.payload?.unsupported ?? state.intent.action.payload?.unknown;
92
- return typeof value === "string" && value.trim() ? value.trim() : null;
103
+ if (typeof value !== "string" || !value.trim())
104
+ return null;
105
+ const reason = state.intent.action.payload?.reason;
106
+ return { command: value.trim(), ...typeof reason === "string" && reason.trim() ? { reason: reason.trim() } : {} };
93
107
  }
94
108
  function renderHelpScene(state, layout) {
95
- const width = panelWidth(layout);
96
109
  const unsupported = unsupportedCommand(state);
97
- const panelTop = 0;
110
+ const selected = Math.max(0, Math.min(ACTIONS.length - 1, state.selection.index));
98
111
  const panelLines = [
99
112
  ...unsupported ? [
100
- line(`${unsupported} uses the plain CLI path.`, { fg: RIG_UI.yellow, bold: true }),
101
- line("Exit and rerun with RIG_PLAIN=1 if you want legacy text output.", { fg: RIG_UI.ink3 }),
113
+ line(`${unsupported.command} needs the action catalog.`, { fg: RIG_UI.yellow, bold: true }),
114
+ line(unsupported.reason ?? "Pick an action below; explicit plain mode remains available with RIG_PLAIN=1.", { fg: RIG_UI.ink3 }),
102
115
  line("", { fg: RIG_UI.ink3 })
103
116
  ] : [],
104
- line("COMMAND ACTION", { fg: RIG_UI.ink3, bold: true }),
117
+ line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
118
+ ...ACTIONS.map(([label, detail], index) => deckRow({ label, detail, index, active: selected === index, activateOnClick: true })),
105
119
  line("", { fg: RIG_UI.ink3 }),
106
- ...COMMANDS.map(([label, detail], index) => commandRow(width - 6, label, detail, index))
120
+ line("KEYS / MOUSE", { fg: RIG_UI.ink3, bold: true }),
121
+ ...SHORTCUTS.map(([label, detail]) => line(`${label.padEnd(22)} ${detail}`, { fg: RIG_UI.ink2 }))
107
122
  ];
108
123
  return makeSceneFrame({
109
124
  scene: "help",
110
- title: "Rig help",
125
+ title: "Command catalog",
111
126
  lines: [
112
127
  line("", { fg: RIG_UI.ink4 }),
113
- line(" rig help", { fg: RIG_UI.ink3 })
128
+ line(" command catalog \xB7 TUI-first Rig", { fg: RIG_UI.ink3, bold: true })
114
129
  ],
115
130
  panels: [{
116
131
  id: "help-reference",
117
- top: panelTop,
118
- width,
119
- height: panelHeight(layout, panelTop),
132
+ top: 0,
133
+ width: panelWidth(layout),
134
+ height: panelHeight(layout),
120
135
  lines: panelLines,
121
136
  backgroundColor: RIG_UI.panel,
122
137
  backgroundAlpha: 184,
123
138
  opacity: 1,
124
139
  border: false,
125
140
  chrome: "ad-terminal",
126
- headerText: "rig help",
141
+ headerText: "actions",
127
142
  headerHeight: 3,
128
143
  paddingX: layout?.compact ? 2 : 3,
129
144
  paddingY: 1
130
145
  }],
131
- typeBarPlaceholder: "runs \xB7 tasks \xB7 help \xB7 attach <run>",
132
- footer: { message: "help" },
146
+ hideTypeBar: true,
147
+ footer: { message: "enter/click opens a workspace" },
133
148
  live: true
134
149
  });
135
150
  }
@@ -1,2 +1,3 @@
1
+ import type { StageLayout } from "../layout";
1
2
  import type { AppSceneFrame, AppState } from "../types";
2
- export declare function renderInboxScene(state: AppState): AppSceneFrame;
3
+ export declare function renderInboxScene(state: AppState, layout?: StageLayout): AppSceneFrame;
@@ -67,11 +67,15 @@ function statusColor(status) {
67
67
  function line(text, options = {}) {
68
68
  return { text, ...options };
69
69
  }
70
- function blank() {
71
- return { text: "" };
72
- }
73
- function center(text, fg = RIG_UI.ink2, bold = false) {
74
- return { text, fg, bold, align: "center" };
70
+ function deckRow(input) {
71
+ const prefix = input.active ? "\u25B8" : " ";
72
+ const label = input.label.toUpperCase().padEnd(10);
73
+ return line(`${prefix} ${label} ${input.detail}`, {
74
+ fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
75
+ bold: input.active,
76
+ selectableIndex: input.index,
77
+ activateOnClick: input.activateOnClick
78
+ });
75
79
  }
76
80
  function makeSceneFrame(input) {
77
81
  return input;
@@ -88,28 +92,71 @@ function inboxRecords(state) {
88
92
  inputs: Array.isArray(record.inputs) ? record.inputs : []
89
93
  };
90
94
  }
91
- function row(record) {
92
- const prefix = record.kind === "approvals" ? "approve" : "answer";
93
- return `\u25B8 ${prefix.padEnd(7)} ${record.requestId.slice(0, 10).padEnd(10)} run ${record.runId.slice(0, 8)} ${record.title}`;
95
+ function panelWidth(layout) {
96
+ return layout?.centerWidth ?? 100;
97
+ }
98
+ function panelHeight(layout) {
99
+ return Math.max(8, (layout?.centerHeight ?? 24) - 1);
94
100
  }
95
- function renderInboxScene(state) {
101
+ function requestLines(record, startIndex, selected) {
102
+ const title = `${record.title || record.requestId} \xB7 run ${record.runId.slice(0, 8)}`;
103
+ if (record.kind === "approvals") {
104
+ return [
105
+ line(title, { fg: statusColor(record.status), bold: true }),
106
+ deckRow({ label: "approve", detail: `approve ${record.requestId.slice(0, 10)}`, index: startIndex, active: selected === startIndex, activateOnClick: true }),
107
+ deckRow({ label: "reject", detail: `reject ${record.requestId.slice(0, 10)}`, index: startIndex + 1, active: selected === startIndex + 1, activateOnClick: true }),
108
+ deckRow({ label: "attach", detail: `open run ${record.runId.slice(0, 8)}`, index: startIndex + 2, active: selected === startIndex + 2, activateOnClick: true }),
109
+ line("", { fg: RIG_UI.ink3 })
110
+ ];
111
+ }
112
+ return [
113
+ line(title, { fg: statusColor(record.status), bold: true }),
114
+ deckRow({ label: "answer", detail: `open answer command for ${record.requestId.slice(0, 10)}`, index: startIndex, active: selected === startIndex, activateOnClick: true }),
115
+ deckRow({ label: "attach", detail: `open run ${record.runId.slice(0, 8)}`, index: startIndex + 1, active: selected === startIndex + 1, activateOnClick: true }),
116
+ line("", { fg: RIG_UI.ink3 })
117
+ ];
118
+ }
119
+ function renderInboxScene(state, layout) {
96
120
  const inbox = inboxRecords(state);
97
121
  const all = [...inbox.approvals, ...inbox.inputs];
122
+ const selected = Math.max(0, state.selection.index);
98
123
  const optimistic = state.data.inboxOptimistic;
99
124
  const optimisticLine = optimistic && typeof optimistic === "object" && !Array.isArray(optimistic) ? `${optimistic.requestId ?? "request"} ${optimistic.status ?? "pending"}` : "idle";
125
+ const lines = [];
126
+ let selectableIndex = 0;
127
+ if (all.length === 0) {
128
+ lines.push(line("No pending approvals or input requests.", { fg: RIG_UI.ink3 }), line("Runs that need a decision will appear here with approve/reject/answer actions.", { fg: RIG_UI.ink4 }), line("", { fg: RIG_UI.ink3 }), line("ACTIONS", { fg: RIG_UI.ink3, bold: true }), deckRow({ label: "refresh", detail: "reload pending requests", index: 0, active: selected === 0, activateOnClick: true }), deckRow({ label: "runs", detail: "open runs", index: 1, active: selected === 1, activateOnClick: true }), deckRow({ label: "tasks", detail: "open tasks", index: 2, active: selected === 2, activateOnClick: true }), deckRow({ label: "doctor", detail: "diagnose inbox/server/auth", index: 3, active: selected === 3, activateOnClick: true }));
129
+ } else {
130
+ for (const record of all.slice(0, 8)) {
131
+ lines.push(...requestLines(record, selectableIndex, selected));
132
+ selectableIndex += record.kind === "approvals" ? 3 : 2;
133
+ }
134
+ }
100
135
  return makeSceneFrame({
101
136
  scene: "inbox",
102
137
  title: "Inbox",
103
138
  lines: [
104
- center("inbox", RIG_UI.ink, true),
105
- center(`${inbox.approvals.length} approvals \xB7 ${inbox.inputs.length} inputs`, RIG_UI.ink3),
106
- blank(),
107
- ...all.length > 0 ? all.slice(0, 14).map((record, index) => line(row(record), { fg: statusColor(record.status), align: "center", selectableIndex: index })) : [line("no pending approvals or answers", { fg: RIG_UI.ink3, align: "center" })],
108
- blank(),
109
- line(`status ${optimisticLine}`, { fg: RIG_UI.ink4, align: "center" })
139
+ line("", { fg: RIG_UI.ink4 }),
140
+ line(` inbox \xB7 ${inbox.approvals.length} approvals \xB7 ${inbox.inputs.length} inputs \xB7 ${optimisticLine}`, { fg: RIG_UI.ink3 })
110
141
  ],
111
- footer: { message: `${all.length} pending` },
112
- typeBarPlaceholder: "approve <request> \xB7 reject <request> \xB7 answer <request>",
142
+ panels: [{
143
+ id: "inbox-actions",
144
+ top: 0,
145
+ width: panelWidth(layout),
146
+ height: panelHeight(layout),
147
+ lines,
148
+ backgroundColor: RIG_UI.panel,
149
+ backgroundAlpha: 184,
150
+ opacity: 1,
151
+ border: false,
152
+ chrome: "ad-terminal",
153
+ headerText: `inbox \xB7 ${all.length} pending`,
154
+ headerHeight: 3,
155
+ paddingX: layout?.compact ? 2 : 3,
156
+ paddingY: 1
157
+ }],
158
+ footer: { message: all.length > 0 ? "enter/click activates selected request action" : "no pending requests" },
159
+ hideTypeBar: true,
113
160
  live: state.status === "action" || all.length === 0
114
161
  });
115
162
  }
@@ -1,2 +1,3 @@
1
1
  import type { AppSceneFrame, AppState } from "../types";
2
- export declare function renderInitScene(state: AppState): AppSceneFrame;
2
+ import type { StageLayout } from "../layout";
3
+ export declare function renderInitScene(state: AppState, layout?: StageLayout): AppSceneFrame;
@@ -44,8 +44,15 @@ function line(text, options = {}) {
44
44
  function blank() {
45
45
  return { text: "" };
46
46
  }
47
- function center(text, fg = RIG_UI.ink2, bold = false) {
48
- return { text, fg, bold, align: "center" };
47
+ function deckRow(input) {
48
+ const prefix = input.active ? "\u25B8" : " ";
49
+ const label = input.label.toUpperCase().padEnd(10);
50
+ return line(`${prefix} ${label} ${input.detail}`, {
51
+ fg: input.active ? RIG_UI.lime : RIG_UI.ink2,
52
+ bold: input.active,
53
+ selectableIndex: input.index,
54
+ activateOnClick: input.activateOnClick
55
+ });
49
56
  }
50
57
  function makeSceneFrame(input) {
51
58
  return input;
@@ -60,32 +67,66 @@ function facts(state) {
60
67
  const value = initData(state).facts;
61
68
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
62
69
  }
63
- function renderInitScene(state) {
70
+ function panelWidth(layout) {
71
+ return layout?.centerWidth ?? 118;
72
+ }
73
+ function panelHeight(layout) {
74
+ return Math.max(18, layout?.centerHeight ?? 34);
75
+ }
76
+ function selectedIndex(state, count) {
77
+ return Math.max(0, Math.min(count - 1, state.selection.index));
78
+ }
79
+ function statusRows(factState, status, configured) {
80
+ return [
81
+ line(`config ${configured ? factState?.configPath ?? "present" : "needs setup"}`, { fg: configured ? RIG_UI.ink2 : RIG_UI.yellow }),
82
+ line(`git ${factState?.hasGit ? "present" : "missing / not checked"}`, { fg: factState?.hasGit ? RIG_UI.ink3 : RIG_UI.yellow }),
83
+ line(`state ${status}`, { fg: configured ? RIG_UI.limeDim : RIG_UI.ink3 })
84
+ ];
85
+ }
86
+ function renderInitScene(state, layout) {
64
87
  const factState = facts(state);
65
88
  const init = initData(state);
66
89
  const status = typeof init.status === "string" ? init.status : factState?.hasConfig ? "configured" : "needs-init";
67
90
  const configured = factState?.hasConfig === true;
91
+ const selected = selectedIndex(state, 6);
92
+ const actionRows = [
93
+ deckRow({ label: "wizard", detail: "full interactive setup wizard", index: 0, active: selected === 0, activateOnClick: true }),
94
+ deckRow({ label: "reconfig", detail: "repair/reconfigure generated config and private state", index: 1, active: selected === 1, activateOnClick: true }),
95
+ deckRow({ label: "demo", detail: "seed offline demo task source", index: 2, active: selected === 2, activateOnClick: true }),
96
+ deckRow({ label: "remote", detail: "configure remote server and checkout link", index: 3, active: selected === 3, activateOnClick: true }),
97
+ deckRow({ label: "auth", detail: "import GitHub auth into selected server", index: 4, active: selected === 4, activateOnClick: true }),
98
+ deckRow({ label: "doctor", detail: "verify setup after changes", index: 5, active: selected === 5, activateOnClick: true })
99
+ ];
100
+ const panelLines = [
101
+ line(configured ? "project setup \xB7 configured" : "project setup \xB7 choose a path", { fg: RIG_UI.ink3 }),
102
+ blank(),
103
+ ...statusRows(factState, status, configured),
104
+ blank(),
105
+ line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
106
+ ...actionRows,
107
+ blank(),
108
+ line("enter/click runs action \xB7 esc returns home \xB7 tab switches surfaces", { fg: RIG_UI.ink4 })
109
+ ];
68
110
  return makeSceneFrame({
69
111
  scene: "init",
70
112
  title: "Init",
71
- lines: [
72
- center(configured ? "project configured" : "project init", RIG_UI.ink, true),
73
- center(configured ? "configuration detected" : "create config, server link, auth, and task source", RIG_UI.ink3),
74
- blank(),
75
- ...configured ? [
76
- line(`config ${factState.configPath ?? "present"}`, { fg: RIG_UI.ink2, align: "center" }),
77
- line(`git ${factState.hasGit ? "present" : "missing"}`, { fg: RIG_UI.ink3, align: "center" }),
78
- blank(),
79
- line("ready", { fg: RIG_UI.ink4, align: "center" })
80
- ] : [
81
- line(`git ${factState?.hasGit ? "present" : "missing"}`, { fg: RIG_UI.ink3, align: "center" }),
82
- line(`status ${status}`, { fg: RIG_UI.limeDim, align: "center" }),
83
- blank(),
84
- line("type init --yes", { fg: RIG_UI.ink4, align: "center" })
85
- ]
86
- ],
87
- footer: { message: configured ? "configured" : "not configured" },
88
- typeBarPlaceholder: configured ? "doctor \xB7 tasks \xB7 server" : "init --yes \xB7 init --server remote \xB7 help",
113
+ lines: [],
114
+ panels: [{
115
+ id: "init-actions",
116
+ top: 0,
117
+ width: panelWidth(layout),
118
+ height: panelHeight(layout),
119
+ lines: panelLines,
120
+ backgroundColor: RIG_UI.panel,
121
+ backgroundAlpha: 184,
122
+ opacity: 0.98,
123
+ border: false,
124
+ chrome: "ad-terminal",
125
+ headerText: "rig init \xB7 setup / repair / reconfigure",
126
+ paddingX: 5,
127
+ paddingY: 2
128
+ }],
129
+ footer: { message: configured ? "configured" : "setup needed" },
89
130
  live: !configured || state.status === "action"
90
131
  });
91
132
  }
@@ -1,2 +1,3 @@
1
+ import type { StageLayout } from "../layout";
1
2
  import type { AppSceneFrame, AppState } from "../types";
2
- export declare function renderMainScene(state: AppState): AppSceneFrame;
3
+ export declare function renderMainScene(state: AppState, layout?: StageLayout): AppSceneFrame;
@@ -44,9 +44,6 @@ function line(text, options = {}) {
44
44
  function blank() {
45
45
  return { text: "" };
46
46
  }
47
- function center(text, fg = RIG_UI.ink2, bold = false) {
48
- return { text, fg, bold, align: "center" };
49
- }
50
47
  function deckRow(input) {
51
48
  const prefix = input.active ? "\u25B8" : " ";
52
49
  const label = input.label.toUpperCase().padEnd(10);
@@ -57,38 +54,97 @@ function deckRow(input) {
57
54
  activateOnClick: input.activateOnClick
58
55
  });
59
56
  }
57
+ function kv(label, value, fg = RIG_UI.ink2) {
58
+ return line(` ${label.toUpperCase().padEnd(10)} ${value}`, { fg });
59
+ }
60
60
  function makeSceneFrame(input) {
61
61
  return input;
62
62
  }
63
63
 
64
64
  // packages/cli/src/app-opentui/scenes/main.ts
65
- var CONFIGURED_OPTIONS = [
66
- ["runs", "run list and attach"],
67
- ["tasks", "task source and dispatch"],
68
- ["help", "command reference"]
65
+ var CONFIGURED_MENU = [
66
+ ["init", "setup, reconfigure, repair project state"],
67
+ ["server", "local/remote target, auth, linkage"],
68
+ ["github", "auth, selected repo, stored token"],
69
+ ["tasks", "browse, filter, dispatch, attach active work"],
70
+ ["runs", "attach, stop, resume, inspect live sessions"],
71
+ ["inbox", "approve, reject, answer, attach"],
72
+ ["doctor", "diagnose and repair broken surfaces"],
73
+ ["help", "all actions, shortcuts, mouse controls"]
69
74
  ];
70
- function renderMainScene(state) {
75
+ var UNCONFIGURED_MENU = [
76
+ ["init", "full setup wizard and reconfigure flow"],
77
+ ["server", "select local/remote target"],
78
+ ["github", "connect or import authentication"],
79
+ ["doctor", "find missing setup pieces"],
80
+ ["help", "all actions and controls"]
81
+ ];
82
+ function record(value) {
83
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
84
+ }
85
+ function serverSummary(state) {
86
+ const server = record(state.data.server);
87
+ const auth = record(server?.auth);
88
+ const signedIn = auth?.signedIn === true || auth?.authenticated === true || auth?.status === "authenticated";
89
+ const login = typeof auth?.login === "string" && auth.login.trim() ? auth.login.trim() : undefined;
90
+ return {
91
+ label: typeof server?.label === "string" && server.label.trim() ? server.label.trim() : state.footer.server ?? "not checked yet",
92
+ target: typeof server?.kind === "string" ? `${server.kind} endpoint` : "open Server to probe",
93
+ health: server ? server.reachable === false ? "unreachable" : "reachable" : "pending",
94
+ auth: server ? signedIn ? `signed in${login ? ` as ${login}` : ""}` : "not signed in / unknown" : "pending"
95
+ };
96
+ }
97
+ function panelWidth(layout) {
98
+ return layout?.centerWidth ?? 118;
99
+ }
100
+ function panelHeight(layout) {
101
+ return Math.max(22, layout?.centerHeight ?? 36);
102
+ }
103
+ function actionRows(state, configured) {
104
+ const options = configured ? CONFIGURED_MENU : UNCONFIGURED_MENU;
105
+ const selected = Math.max(0, Math.min(options.length - 1, state.selection.index));
106
+ return options.map(([label, detail], index) => deckRow({ label, detail, index, active: index === selected, activateOnClick: true }));
107
+ }
108
+ function renderMainScene(state, layout) {
71
109
  const configured = state.data.projectConfigured === true;
72
- const selected = configured ? Math.max(0, Math.min(CONFIGURED_OPTIONS.length - 1, state.selection.index)) : Math.max(0, Math.min(1, state.selection.index));
110
+ const server = serverSummary(state);
73
111
  const lines = [
74
- center(configured ? "rig" : "rig setup", RIG_UI.ink, true),
75
- center(configured ? "runs \xB7 tasks \xB7 help" : "initialize this repo", RIG_UI.ink3),
76
- blank()
112
+ line("PROJECT COCKPIT", { fg: RIG_UI.ink3, bold: true }),
113
+ line("click a row or use arrows + enter \xB7 esc returns here \xB7 ctrl-c exits", { fg: RIG_UI.ink4 }),
114
+ blank(),
115
+ line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
116
+ ...actionRows(state, configured),
117
+ blank(),
118
+ line("CONTEXT", { fg: RIG_UI.ink3, bold: true }),
119
+ kv("workspace", "current repository", RIG_UI.ink2),
120
+ kv("config", configured ? "configured" : "needs setup", configured ? RIG_UI.limeDim : RIG_UI.yellow),
121
+ kv("server", server.label, server.health === "reachable" ? RIG_UI.limeDim : RIG_UI.yellow),
122
+ kv("target", server.target, RIG_UI.ink3),
123
+ kv("github", server.auth, server.auth.startsWith("signed in") ? RIG_UI.limeDim : RIG_UI.yellow),
124
+ blank(),
125
+ line(state.actionLabel ?? "ready", { fg: state.status === "action" ? RIG_UI.cyan : RIG_UI.ink4 })
77
126
  ];
78
- if (!configured) {
79
- lines.push(deckRow({ label: "init", detail: "create config and server link", index: 0, active: selected === 0 }), deckRow({ label: "help", detail: "show command reference", index: 1, active: selected === 1 }), blank(), line("enter opens selection", { fg: RIG_UI.ink4, align: "center" }));
80
- } else {
81
- CONFIGURED_OPTIONS.forEach(([label, detail], index) => {
82
- lines.push(deckRow({ label, detail, index, active: index === selected }));
83
- });
84
- lines.push(blank(), line(` ${state.actionLabel ?? "ready"}`, { fg: state.status === "action" ? RIG_UI.cyan : RIG_UI.ink4 }));
85
- }
86
127
  return makeSceneFrame({
87
128
  scene: "main",
88
- title: "Rig",
89
- lines,
90
- typeBarPlaceholder: configured ? "runs \xB7 tasks \xB7 help" : "init \xB7 help",
91
- live: true
129
+ title: "Project cockpit",
130
+ lines: [],
131
+ panels: [{
132
+ id: "project-cockpit",
133
+ top: 0,
134
+ width: panelWidth(layout),
135
+ height: panelHeight(layout),
136
+ lines,
137
+ backgroundColor: RIG_UI.panel,
138
+ backgroundAlpha: 184,
139
+ opacity: 0.98,
140
+ border: false,
141
+ chrome: "ad-terminal",
142
+ headerText: "project cockpit",
143
+ paddingX: layout?.compact ? 3 : 5,
144
+ paddingY: 2
145
+ }],
146
+ live: true,
147
+ hideTypeBar: true
92
148
  });
93
149
  }
94
150
  export {
@@ -1,2 +1,3 @@
1
1
  import type { AppSceneFrame, AppState } from "../types";
2
- export declare function renderRunDetailScene(state: AppState): AppSceneFrame;
2
+ import type { StageLayout } from "../layout";
3
+ export declare function renderRunDetailScene(state: AppState, layout?: StageLayout): AppSceneFrame;
@@ -67,12 +67,6 @@ function statusColor(status) {
67
67
  function line(text, options = {}) {
68
68
  return { text, ...options };
69
69
  }
70
- function blank() {
71
- return { text: "" };
72
- }
73
- function center(text, fg = RIG_UI.ink2, bold = false) {
74
- return { text, fg, bold, align: "center" };
75
- }
76
70
  function deckRow(input) {
77
71
  const prefix = input.active ? "\u25B8" : " ";
78
72
  const label = input.label.toUpperCase().padEnd(10);
@@ -103,34 +97,54 @@ function selectedRunId(state, run) {
103
97
  function entryText(entry) {
104
98
  return String(entry.detail ?? entry.message ?? entry.text ?? entry.title ?? entry.type ?? "event").replace(/\s+/g, " ").slice(0, 96);
105
99
  }
106
- function renderRunDetailScene(state) {
100
+ function panelWidth(layout) {
101
+ return layout?.centerWidth ?? 100;
102
+ }
103
+ function panelHeight(layout) {
104
+ return Math.max(10, (layout?.centerHeight ?? 24) - 1);
105
+ }
106
+ function renderRunDetailScene(state, layout) {
107
107
  const run = snapshot(state);
108
108
  const runId = selectedRunId(state, run);
109
109
  const selected = Math.max(0, Math.min(2, state.selection.index));
110
110
  const timeline = run?.timeline.slice(-5).map((entry) => entryText(entry)) ?? [];
111
111
  const logs = run?.logs.slice(-3).map((entry) => entryText(entry)) ?? [];
112
112
  const status = run?.status ?? "loading";
113
- const lines = [
114
- center(run ? run.title : "run", RIG_UI.ink, true),
115
- center(runId ? `${runId.slice(0, 8)} \xB7 ${status}` : "loading run", run ? statusColor(status) : RIG_UI.ink3),
116
- blank(),
117
- ...runId ? [
118
- deckRow({ label: "pi", detail: "attach bundled Pi", index: 0, active: selected === 0, activateOnClick: true }),
119
- deckRow({ label: "stop", detail: "request stop", index: 1, active: selected === 1, activateOnClick: true }),
120
- deckRow({ label: "runs", detail: "return to list", index: 2, active: selected === 2, activateOnClick: true }),
121
- blank(),
122
- kv("status", status, statusColor(status)),
123
- kv("run", runId, RIG_UI.ink2)
124
- ] : [line("loading run telemetry", { fg: RIG_UI.ink3, align: "center" })],
125
- ...timeline.length > 0 ? [blank(), line(" timeline", { fg: RIG_UI.ink4, bold: true }), ...timeline.map((entry) => line(` \xB7 ${entry}`, { fg: RIG_UI.ink2 }))] : [],
126
- ...logs.length > 0 ? [blank(), line(" logs", { fg: RIG_UI.ink4, bold: true }), ...logs.map((entry) => line(` \xB7 ${entry}`, { fg: RIG_UI.ink4 }))] : []
127
- ];
128
113
  return makeSceneFrame({
129
114
  scene: "run-detail",
130
115
  title: "Run",
131
- lines,
132
- footer: { run: runId ? `${runId.slice(0, 8)} ${status}` : undefined },
133
- typeBarPlaceholder: runId ? `attach ${runId.slice(0, 8)} \xB7 stop ${runId.slice(0, 8)} \xB7 runs` : "runs \xB7 tasks",
116
+ lines: [
117
+ line("", { fg: RIG_UI.ink4 }),
118
+ line(` run \xB7 ${runId ? `${runId.slice(0, 8)} \xB7 ${status}` : "loading"}`, { fg: run ? statusColor(status) : RIG_UI.ink3 })
119
+ ],
120
+ panels: [{
121
+ id: "run-detail",
122
+ top: 0,
123
+ width: panelWidth(layout),
124
+ height: panelHeight(layout),
125
+ lines: [
126
+ line(run ? run.title : "Run telemetry", { fg: RIG_UI.ink, bold: true }),
127
+ ...runId ? [kv("run", runId, RIG_UI.ink2), kv("status", status, statusColor(status))] : [line("loading run telemetry", { fg: RIG_UI.ink3 })],
128
+ line("", { fg: RIG_UI.ink3 }),
129
+ line("ACTIONS", { fg: RIG_UI.ink3, bold: true }),
130
+ deckRow({ label: "pi", detail: "attach bundled Pi", index: 0, active: selected === 0, activateOnClick: true }),
131
+ deckRow({ label: "stop", detail: "request stop", index: 1, active: selected === 1, activateOnClick: true }),
132
+ deckRow({ label: "runs", detail: "return to fleet", index: 2, active: selected === 2, activateOnClick: true }),
133
+ ...timeline.length > 0 ? [line("", { fg: RIG_UI.ink3 }), line("TIMELINE", { fg: RIG_UI.ink3, bold: true }), ...timeline.map((entry) => line(`\xB7 ${entry}`, { fg: RIG_UI.ink2 }))] : [],
134
+ ...logs.length > 0 ? [line("", { fg: RIG_UI.ink3 }), line("LOGS", { fg: RIG_UI.ink3, bold: true }), ...logs.map((entry) => line(`\xB7 ${entry}`, { fg: RIG_UI.ink4 }))] : []
135
+ ],
136
+ backgroundColor: RIG_UI.panel,
137
+ backgroundAlpha: 184,
138
+ opacity: 1,
139
+ border: false,
140
+ chrome: "ad-terminal",
141
+ headerText: runId ? `run ${runId.slice(0, 8)}` : "run",
142
+ headerHeight: 3,
143
+ paddingX: layout?.compact ? 2 : 3,
144
+ paddingY: 1
145
+ }],
146
+ footer: { run: runId ? `${runId.slice(0, 8)} ${status}` : undefined, message: "enter/click action" },
147
+ hideTypeBar: true,
134
148
  live: state.status === "action" || !run
135
149
  });
136
150
  }
@@ -1,2 +1,3 @@
1
1
  import type { AppSceneFrame, AppState } from "../types";
2
- export declare function renderServerScene(state: AppState): AppSceneFrame;
2
+ import type { StageLayout } from "../layout";
3
+ export declare function renderServerScene(state: AppState, layout?: StageLayout): AppSceneFrame;