@viraatdas/rudder 2.10.7 → 2.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,10 +48,9 @@ rudder
48
48
  ```
49
49
 
50
50
  With no arguments, `rudder` opens the dashboard. Type in the bottom input and
51
- press `Enter` to start a one-off agent in your main checkout. This is the default
52
- for questions, repo inspection, docs/API research, and small direct changes. Use
53
- `/plan <text>` when you want the orchestrator to plan a DAG and run isolated
54
- workers.
51
+ press `Enter` to hand the request to the orchestrator. It can answer, inspect the
52
+ repo, or plan a DAG and run isolated workers. Use `/ask <text>` when you want a
53
+ one-off agent in the main checkout with no DAG.
55
54
 
56
55
  If a task needs shared local context like API tokens, private URLs, account ids,
57
56
  or environment values, save it with `/share <text>` in the task input. Rudder
@@ -91,7 +90,7 @@ to `~/.rudder/config.json`.
91
90
  │ task list │ live Claude Code or Codex terminal │
92
91
  │ status/model │ DAG-over-orchestrator, scrollback, review │
93
92
  ├───────────────┴────────────────────────────────────────────┤
94
- │ task input: one-off agent by default, /plan for DAGs
93
+ │ task input: orchestrator by default, /ask for one-off
95
94
  └──────────────────────────────────────────────────────────────┘
96
95
  ```
97
96
 
@@ -191,13 +190,12 @@ reused next time. Rudder refreshes model metadata from
191
190
  ## One-Off and Planning
192
191
 
193
192
  Type a fresh request in the bottom task input. When no plan is active, Rudder
194
- starts one conversational agent in the main checkout and shows it in the
195
- `one-off` section. This keeps ordinary questions like "where is the app?" or "is
196
- it ready to use?" out of the DAG planner.
193
+ starts the orchestrator, which can inspect the repo, plan the work, and spawn
194
+ isolated workers when needed.
197
195
 
198
- Use `/plan <text>` for planned implementation work. This starts the orchestrator,
199
- which decomposes the work into isolated workers. `/ask <text>` is kept as an
200
- explicit alias for the default one-off path.
196
+ Use `/ask <text>` for a one-off conversational agent in the main checkout, with
197
+ no DAG. Use `/plan <text>` when you want to be explicit about the orchestrator /
198
+ DAG path; it is the same route as plain input.
201
199
 
202
200
  For planned work, Rudder runs a dedicated Claude Code orchestrator PTY with a DAG
203
201
  pane above it. The flow stays inside Rudder: the orchestrator researches
@@ -325,16 +323,6 @@ rudder sync <runId>
325
323
  rudder cleanup
326
324
  ```
327
325
 
328
- ## Other interfaces
329
-
330
- The native dashboard is the default. Older interfaces remain available:
331
-
332
- ```bash
333
- rudder tmux # legacy tmux dashboard
334
- rudder tui # Ink-based interactive TUI
335
- rudder --no-native # skip the native binary
336
- ```
337
-
338
326
  ## Troubleshooting
339
327
 
340
328
  - Stale behavior after an upgrade: restart any already-running Rudder dashboards
@@ -0,0 +1,6 @@
1
+ import type { HookConfig, HookInvocation, HookResult, RudderEvent } from "./types.js";
2
+ export declare function hooksConfigPath(repoRoot: string): string;
3
+ export declare function loadHookConfig(repoRoot: string): Promise<HookConfig>;
4
+ export declare function runHooks(invocation: HookInvocation, emit?: (event: RudderEvent) => Promise<void> | void): Promise<HookResult[]>;
5
+ export declare function firstBlockingHook(results: HookResult[]): HookResult | undefined;
6
+ export declare function initHookTemplates(repoRoot: string): Promise<string>;
package/dist/hooks.js ADDED
@@ -0,0 +1,203 @@
1
+ import { spawn } from "node:child_process";
2
+ import path from "node:path";
3
+ import { projectStateDir, loadConfig } from "./state.js";
4
+ import { nowIso, readJson, writeJson } from "./util.js";
5
+ export function hooksConfigPath(repoRoot) {
6
+ return path.join(projectStateDir(repoRoot), "hooks.json");
7
+ }
8
+ export async function loadHookConfig(repoRoot) {
9
+ const config = await loadConfig();
10
+ if (config.hooks?.enabled === false) {
11
+ return { version: 1, hooks: [] };
12
+ }
13
+ const configured = config.hooks?.configPath
14
+ ? path.resolve(repoRoot, config.hooks.configPath)
15
+ : hooksConfigPath(repoRoot);
16
+ const raw = await readJson(configured);
17
+ if (!raw || raw.version !== 1 || !Array.isArray(raw.hooks)) {
18
+ return { version: 1, hooks: [] };
19
+ }
20
+ return {
21
+ version: 1,
22
+ hooks: raw.hooks.filter(isHookDefinition),
23
+ };
24
+ }
25
+ export async function runHooks(invocation, emit) {
26
+ const config = await loadHookConfig(invocation.repoRoot);
27
+ const hooks = config.hooks.filter((hook) => hook.event === invocation.event && hookMatches(hook, invocation));
28
+ const results = [];
29
+ for (const hook of hooks) {
30
+ await emit?.({
31
+ ts: nowIso(),
32
+ runId: invocation.runId ?? "hooks",
33
+ nodeId: invocation.nodeId,
34
+ type: "hook.started",
35
+ message: `hook ${hook.name ?? hook.command} on ${invocation.event}`,
36
+ data: hookData(hook),
37
+ });
38
+ const result = await runHookCommand(hook, invocation);
39
+ results.push(result);
40
+ if (result.status === "blocked") {
41
+ await emit?.({
42
+ ts: nowIso(),
43
+ runId: invocation.runId ?? "hooks",
44
+ nodeId: invocation.nodeId,
45
+ type: "hook.blocked",
46
+ message: result.reason ?? result.stderr ?? `hook blocked ${invocation.event}`,
47
+ data: hookData(hook),
48
+ });
49
+ }
50
+ else if (result.status === "failed" || result.status === "timeout") {
51
+ await emit?.({
52
+ ts: nowIso(),
53
+ runId: invocation.runId ?? "hooks",
54
+ nodeId: invocation.nodeId,
55
+ type: "hook.failed",
56
+ message: result.reason ?? result.stderr ?? `hook failed ${invocation.event}`,
57
+ data: hookData(hook),
58
+ });
59
+ }
60
+ if (isBlockingEvent(invocation.event) && result.status === "blocked") {
61
+ break;
62
+ }
63
+ }
64
+ return results;
65
+ }
66
+ export function firstBlockingHook(results) {
67
+ return results.find((result) => result.status === "blocked");
68
+ }
69
+ export async function initHookTemplates(repoRoot) {
70
+ const target = hooksConfigPath(repoRoot);
71
+ const initial = {
72
+ version: 1,
73
+ hooks: [
74
+ {
75
+ event: "PromptSubmit",
76
+ name: "secret-prompt-warning",
77
+ command: "node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s); if(/(sk-|ghp_|github_pat_|api[_-]?key=)/i.test(j.message||'')){console.error('prompt appears to contain a secret; use rudder share instead'); process.exit(2)}})\"",
78
+ timeoutMs: 5000,
79
+ },
80
+ ],
81
+ };
82
+ await writeJson(target, initial, { mode: 0o600 });
83
+ return target;
84
+ }
85
+ function runHookCommand(hook, invocation) {
86
+ const timeoutMs = hook.timeoutMs ?? defaultTimeout(invocation.event);
87
+ const input = JSON.stringify(invocation);
88
+ return new Promise((resolve) => {
89
+ const child = spawn(hook.command, {
90
+ cwd: invocation.repoRoot,
91
+ shell: true,
92
+ stdio: ["pipe", "pipe", "pipe"],
93
+ env: {
94
+ ...process.env,
95
+ RUDDER_REPO: invocation.repoRoot,
96
+ RUDDER_EVENT: invocation.event,
97
+ ...(invocation.runId ? { RUDDER_RUN_ID: invocation.runId } : {}),
98
+ ...(invocation.nodeId ? { RUDDER_NODE_ID: invocation.nodeId } : {}),
99
+ },
100
+ });
101
+ let stdout = "";
102
+ let stderr = "";
103
+ let settled = false;
104
+ const timer = setTimeout(() => {
105
+ if (settled) {
106
+ return;
107
+ }
108
+ settled = true;
109
+ child.kill("SIGTERM");
110
+ resolve({ hook, status: "timeout", stdout, stderr, reason: `hook timed out after ${timeoutMs}ms` });
111
+ }, timeoutMs);
112
+ timer.unref?.();
113
+ child.stdout?.on("data", (chunk) => {
114
+ stdout += String(chunk);
115
+ });
116
+ child.stderr?.on("data", (chunk) => {
117
+ stderr += String(chunk);
118
+ });
119
+ child.on("error", (error) => {
120
+ if (settled) {
121
+ return;
122
+ }
123
+ settled = true;
124
+ clearTimeout(timer);
125
+ resolve({ hook, status: "failed", stdout, stderr, reason: error.message });
126
+ });
127
+ child.on("exit", (code) => {
128
+ if (settled) {
129
+ return;
130
+ }
131
+ settled = true;
132
+ clearTimeout(timer);
133
+ const parsed = parseHookJson(stdout);
134
+ const blocked = code === 2 || parsed?.decision === "block";
135
+ resolve({
136
+ hook,
137
+ status: blocked ? "blocked" : code === 0 ? "allowed" : "failed",
138
+ stdout,
139
+ stderr,
140
+ ...(blocked ? { reason: parsed?.reason ?? firstLine(stderr) ?? "blocked by hook" } : {}),
141
+ ...(code !== 0 && !blocked ? { reason: firstLine(stderr) ?? `hook exited ${code}` } : {}),
142
+ });
143
+ });
144
+ child.stdin?.end(input);
145
+ });
146
+ }
147
+ function hookMatches(hook, invocation) {
148
+ if (!hook.matcher) {
149
+ return true;
150
+ }
151
+ const haystack = `${invocation.message ?? ""}\n${JSON.stringify(invocation.data ?? {})}`;
152
+ try {
153
+ return new RegExp(hook.matcher).test(haystack);
154
+ }
155
+ catch {
156
+ return haystack.includes(hook.matcher);
157
+ }
158
+ }
159
+ function isHookDefinition(value) {
160
+ const hook = value;
161
+ return Boolean(hook && isHookEvent(hook.event) && typeof hook.command === "string" && hook.command.trim());
162
+ }
163
+ function isHookEvent(value) {
164
+ return (value === "PromptSubmit" ||
165
+ value === "RunStart" ||
166
+ value === "RunComplete" ||
167
+ value === "NodeReview" ||
168
+ value === "NodeMerged" ||
169
+ value === "MergeConflict" ||
170
+ value === "BudgetExceeded" ||
171
+ value === "WorktreeCreate" ||
172
+ value === "SessionEnd");
173
+ }
174
+ function isBlockingEvent(event) {
175
+ return event === "PromptSubmit" || event === "RunStart" || event === "WorktreeCreate";
176
+ }
177
+ function defaultTimeout(event) {
178
+ return event === "PromptSubmit" ? 30_000 : 600_000;
179
+ }
180
+ function parseHookJson(stdout) {
181
+ const trimmed = stdout.trim();
182
+ if (!trimmed.startsWith("{")) {
183
+ return null;
184
+ }
185
+ try {
186
+ return JSON.parse(trimmed);
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ function firstLine(value) {
193
+ return value.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
194
+ }
195
+ function hookData(hook) {
196
+ return {
197
+ event: hook.event,
198
+ command: hook.command,
199
+ ...(hook.name ? { name: hook.name } : {}),
200
+ ...(hook.matcher ? { matcher: hook.matcher } : {}),
201
+ };
202
+ }
203
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
package/dist/main.js CHANGED
@@ -3,7 +3,7 @@ import fsp from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { authStoreExists, runDoctor, runOnboard } from "./auth.js";
6
- import { codexEnvVars, codexLaunchEnv } from "./codex-binary.js";
6
+ import { codexLaunchEnv } from "./codex-binary.js";
7
7
  import { runCloudCommand } from "./cloud.js";
8
8
  import { printContextAudit } from "./context-audit.js";
9
9
  import { currentBranch, findRepoRoot } from "./git.js";
@@ -15,13 +15,9 @@ import { backfillLlmTaskSummaries, listRuns, popUndoEntry, projectStateDir, regi
15
15
  import { discoverModelOptions } from "./models.js";
16
16
  import { resolveNativeBinaryPath } from "./native-binary.js";
17
17
  import { cleanupRuns, deleteRun, listProjectRuns, mergeRun, printLogs, startRun, statusRuns, stopRun, syncRun, watchRun, workerRun, } from "./run-manager.js";
18
- import { runInteractiveShell } from "./repl.js";
19
18
  import { appendCompletionNote, appendDecision, appendSharedContext, parseCompletionNoteArg, RUDDER_DONE_END, RUDDER_DONE_START, SHARED_CONTEXT_FILE, syncSharedContextToWorkspaces, writeCompletionNoteFile } from "./surfaces.js";
20
- import { runTmuxAgentPane, runTmuxTaskPane, runTmuxWorkerIdle } from "./tmux-dashboard.js";
21
- import { runInteractiveTui } from "./tui.js";
22
- import { commandExists, isTty, MissingToolError, newRunId, runCommand } from "./util.js";
19
+ import { commandExists, isTty, MissingToolError, newRunId } from "./util.js";
23
20
  import { autoUpdateAndRerunIfNeeded, getUpdateAvailable } from "./version-check.js";
24
- import { attachTmuxSession, ensureTmuxDashboardSession, hasTmux, repoTmuxSessionName, shellCommand } from "./tmux.js";
25
21
  export async function main() {
26
22
  const parsed = parseArgs(process.argv.slice(2));
27
23
  if (shouldAutoUpdateForCommand(parsed.command) && await autoUpdateAndRerunIfNeeded(process.argv.slice(2))) {
@@ -64,52 +60,8 @@ export async function main() {
64
60
  return;
65
61
  }
66
62
  switch (parsed.command) {
67
- case "__agents": {
68
- const repo = parsed.flags.repo;
69
- const tmuxSessionName = parsed.flags.tmuxSession;
70
- if (!repo || !tmuxSessionName) {
71
- throw new Error("__agents requires --repo and --tmux-session");
72
- }
73
- process.chdir(repo);
74
- await runTmuxAgentPane({
75
- tmuxSessionName,
76
- backend: parsed.flags.backend,
77
- model: parsed.flags.model,
78
- });
79
- return;
80
- }
81
- case "__task": {
82
- const repo = parsed.flags.repo;
83
- const tmuxSessionName = parsed.flags.tmuxSession;
84
- if (!repo || !tmuxSessionName) {
85
- throw new Error("__task requires --repo and --tmux-session");
86
- }
87
- process.chdir(repo);
88
- await runTmuxTaskPane({
89
- tmuxSessionName,
90
- backend: parsed.flags.backend,
91
- model: parsed.flags.model,
92
- });
93
- return;
94
- }
95
- case "__worker-idle": {
96
- const repo = parsed.flags.repo;
97
- const tmuxSessionName = parsed.flags.tmuxSession;
98
- if (!repo || !tmuxSessionName) {
99
- throw new Error("__worker-idle requires --repo and --tmux-session");
100
- }
101
- process.chdir(repo);
102
- await runTmuxWorkerIdle({
103
- tmuxSessionName,
104
- backend: parsed.flags.backend,
105
- model: parsed.flags.model,
106
- });
107
- return;
108
- }
109
63
  case "tmux":
110
- await maybeOnboard();
111
- await openTmuxDashboard(parsed);
112
- return;
64
+ throw new Error("The tmux dashboard has been removed. Run `rudder` or `rudder dashboard` to open the native dashboard.");
113
65
  case "dashboard":
114
66
  await maybeOnboard();
115
67
  await openDashboard(parsed);
@@ -123,33 +75,10 @@ export async function main() {
123
75
  await runNativeCommand(["mouse-test", ...parsed.args]);
124
76
  return;
125
77
  case "tui":
126
- await maybeOnboard();
127
- await runInteractiveTui({
128
- backend: parsed.flags.backend,
129
- model: parsed.flags.model,
130
- worktree: parsed.flags.worktree,
131
- detach: parsed.flags.detach,
132
- });
133
- return;
134
78
  case "shell":
135
79
  case "interactive":
136
- await maybeOnboard();
137
- await runInteractiveTui({
138
- backend: parsed.flags.backend,
139
- model: parsed.flags.model,
140
- worktree: parsed.flags.worktree,
141
- detach: parsed.flags.detach,
142
- });
143
- return;
144
80
  case "legacy-shell":
145
- await maybeOnboard();
146
- await runInteractiveShell({
147
- backend: parsed.flags.backend,
148
- model: parsed.flags.model,
149
- worktree: parsed.flags.worktree,
150
- detach: parsed.flags.detach,
151
- });
152
- return;
81
+ throw new Error("Legacy interactive shells have been removed. Run `rudder` or `rudder dashboard` to open the native dashboard.");
153
82
  case "__worker": {
154
83
  const repo = parsed.flags.repo;
155
84
  const run = parsed.flags.run;
@@ -435,17 +364,8 @@ function parseArgs(argv) {
435
364
  parsed.flags.nonInteractive = true;
436
365
  continue;
437
366
  }
438
- if (arg === "--no-tmux") {
439
- parsed.flags.noTmux = true;
440
- continue;
441
- }
442
- if (arg === "--no-native") {
443
- parsed.flags.noNative = true;
444
- continue;
445
- }
446
- if (arg === "--headless") {
447
- parsed.flags.headless = true;
448
- continue;
367
+ if (arg === "--no-tmux" || arg === "--no-native" || arg === "--headless") {
368
+ throw new Error(`${arg} was removed with the legacy dashboards. Run \`rudder\` for the native dashboard.`);
449
369
  }
450
370
  if (arg === "--open") {
451
371
  parsed.flags.open = true;
@@ -503,10 +423,6 @@ function parseArgs(argv) {
503
423
  parsed.flags.base = readValue(argv, ++i, arg);
504
424
  continue;
505
425
  }
506
- if (takesValue(arg, "--tmux-session")) {
507
- parsed.flags.tmuxSession = readValue(argv, ++i, arg);
508
- continue;
509
- }
510
426
  if (takesValue(arg, "--home-path")) {
511
427
  parsed.flags.homePaths = [...(parsed.flags.homePaths ?? []), readValue(argv, ++i, arg)];
512
428
  continue;
@@ -547,10 +463,6 @@ function parseArgs(argv) {
547
463
  parsed.flags.base = arg.slice("--base=".length);
548
464
  continue;
549
465
  }
550
- if (arg.startsWith("--tmux-session=")) {
551
- parsed.flags.tmuxSession = arg.slice("--tmux-session=".length);
552
- continue;
553
- }
554
466
  if (arg.startsWith("--home-path=")) {
555
467
  parsed.flags.homePaths = [...(parsed.flags.homePaths ?? []), arg.slice("--home-path=".length)];
556
468
  continue;
@@ -598,29 +510,11 @@ async function maybeOnboard() {
598
510
  await runOnboard();
599
511
  }
600
512
  async function openDashboard(parsed) {
601
- if (parsed.flags.noTmux || parsed.flags.headless) {
602
- await runInteractiveTui({
603
- backend: parsed.flags.backend,
604
- model: parsed.flags.model,
605
- worktree: parsed.flags.worktree,
606
- detach: parsed.flags.detach,
607
- });
608
- return;
609
- }
610
513
  await refreshModelCache();
611
- if (!parsed.flags.noNative && process.env.RUDDER_LEGACY_TMUX !== "1" && await runNativeDashboard()) {
514
+ if (await runNativeDashboard()) {
612
515
  return;
613
516
  }
614
- if (hasTmux()) {
615
- await openTmuxDashboard(parsed);
616
- return;
617
- }
618
- await runInteractiveTui({
619
- backend: parsed.flags.backend,
620
- model: parsed.flags.model,
621
- worktree: parsed.flags.worktree,
622
- detach: parsed.flags.detach,
623
- });
517
+ throw new Error("rudder-native binary is not available. Reinstall or rebuild Rudder, then run `rudder` again.");
624
518
  }
625
519
  async function refreshModelCache() {
626
520
  await Promise.all([
@@ -719,54 +613,6 @@ async function runNativeCommand(args) {
719
613
  });
720
614
  process.exitCode = code ?? 1;
721
615
  }
722
- async function openTmuxDashboard(parsed) {
723
- if (!hasTmux()) {
724
- await runInteractiveTui({
725
- backend: parsed.flags.backend,
726
- model: parsed.flags.model,
727
- worktree: parsed.flags.worktree,
728
- detach: parsed.flags.detach,
729
- });
730
- return;
731
- }
732
- const repoRoot = findRepoRoot();
733
- const sessionName = parsed.flags.tmuxSession ?? repoTmuxSessionName(repoRoot);
734
- const entry = process.argv[1];
735
- if (!entry) {
736
- throw new Error("Cannot locate Rudder entrypoint.");
737
- }
738
- const common = [
739
- entry,
740
- ];
741
- const commonFlags = [
742
- "--repo",
743
- repoRoot,
744
- "--tmux-session",
745
- sessionName,
746
- ...(parsed.flags.backend ? ["--backend", parsed.flags.backend] : []),
747
- ...(parsed.flags.model ? ["--model", parsed.flags.model] : []),
748
- ];
749
- const managedCodexEnv = await codexEnvVars();
750
- // Only inject codex env vars that are actually present. On platforms without a
751
- // managed Codex binary codexEnvVars() returns {}, and interpolating the missing
752
- // values would emit a literal `RUDDER_CODEX_BIN=undefined` that hard-fails Codex
753
- // (and breaks the graceful optional-Codex behavior).
754
- const codexEnvArgs = Object.entries(managedCodexEnv)
755
- .filter(([, value]) => value != null && value !== "")
756
- .map(([key, value]) => `${key}=${value}`);
757
- const withManagedCodex = (args) => shellCommand("env", [...codexEnvArgs, ...args]);
758
- await ensureTmuxDashboardSession({
759
- repoRoot,
760
- sessionName,
761
- agentCommand: withManagedCodex([process.execPath, ...common, "__agents", ...commonFlags]),
762
- workerCommand: withManagedCodex([process.execPath, ...common, "__worker-idle", ...commonFlags]),
763
- taskCommand: withManagedCodex([process.execPath, ...common, "__task", ...commonFlags]),
764
- backend: parsed.flags.backend === "codex" ? "codex" : "claude",
765
- model: parsed.flags.model,
766
- });
767
- const code = await attachTmuxSession(sessionName);
768
- process.exitCode = code;
769
- }
770
616
  async function resetLocalRudderSession(parsed) {
771
617
  const repoRoot = findRepoRoot();
772
618
  const stateDir = projectStateDir(repoRoot);
@@ -776,10 +622,6 @@ async function resetLocalRudderSession(parsed) {
776
622
  fsp.rm(path.join(stateDir, "tmux"), { recursive: true, force: true }),
777
623
  ]);
778
624
  await fsp.mkdir(runsDir(repoRoot), { recursive: true });
779
- if (hasTmux()) {
780
- const sessionName = parsed.flags.tmuxSession ?? repoTmuxSessionName(repoRoot);
781
- await runCommand("tmux", ["kill-session", "-t", sessionName], { allowFailure: true });
782
- }
783
625
  }
784
626
  async function packageVersion() {
785
627
  const packageFile = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
@@ -1020,9 +862,7 @@ function printHelp() {
1020
862
 
1021
863
  Usage:
1022
864
  rudder Open native dashboard with real agent panes
1023
- rudder tmux Open tmux dashboard with native agent panes
1024
865
  rudder restart Clear local session and open dashboard
1025
- rudder tui Open legacy full-screen stream TUI
1026
866
  rudder "task"
1027
867
  rudder run [options] "task"
1028
868
  rudder claude [options] "task"
@@ -1098,9 +938,6 @@ Options:
1098
938
  -p, --port <port> Board server port (default 4774)
1099
939
  --n <count> Number of fan-out variants for rudder fanout (default 3)
1100
940
  --open / --no-open Open (or do not open) the browser for the board
1101
- --no-tmux Use the legacy TUI for bare rudder
1102
- --no-native Use the tmux dashboard instead of native
1103
- --headless Alias for --no-tmux on bare rudder
1104
941
  --home-path <path> Include extra HOME path in cloud snapshot
1105
942
  --ssh <host> BYOC SSH host from ~/.ssh/config
1106
943
  --json Machine-readable output