From 6ef09361800a6268ac6ae2f89e37c771e5e516f1 Mon Sep 17 00:00:00 2001 From: Lee Jackson <130007945+Imagineer99@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:25:42 +0100 Subject: [PATCH] Fix OpenClaw start default to local TUI (#6937) * fix: launch OpenClaw local TUI by default * Fix/adjust OpenClaw launch paths for PR #6937 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Default OpenClaw to the local TUI only on a bare invocation The first-arg startswith('-') branch rewrote passthrough globals into a broken command: OpenClaw's grammar is openclaw [--dev] [--profile ] , so 'unsloth start openclaw --profile test' became 'openclaw tui --local --profile test', but tui does not accept --profile (or --dev), so the invocation failed. A leading '--flag value' is ambiguous between a global (--profile test) and a tui option (--message hi), so it cannot be reinterpreted safely. Default to the local TUI only when no passthrough args are given, and forward everything else verbatim so OpenClaw parses it under its own grammar. The bare-launch default (the point of this change) is preserved; explicit subcommands and global flags pass through. --------- Co-authored-by: wasimysaid <112766706+wasimysaid@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: danielhanchen Co-authored-by: Wasim Yousef Said --- .github/scripts/agent-guides-drive.sh | 4 ++-- unsloth_cli/commands/start.py | 12 +++++++++++- unsloth_cli/tests/test_start.py | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/.github/scripts/agent-guides-drive.sh b/.github/scripts/agent-guides-drive.sh index d430d2c172..defdb498c7 100755 --- a/.github/scripts/agent-guides-drive.sh +++ b/.github/scripts/agent-guides-drive.sh @@ -376,7 +376,7 @@ case "$MODE" in hermes) patch_hermes_tools none invoke_via_connect "$OUT" -z "$PROMPT" ;; openclaw) patch_openclaw_agent notools - invoke_via_connect "$OUT" agent --local --agent ci \ + CONNECT_CMD_OVERRIDE=openclaw invoke_via_connect "$OUT" agent --local --agent ci \ --model "unsloth/${UNSLOTH_MODEL_ID}" --message "$PROMPT" ;; *) invoke_via_connect "$OUT" "$PROMPT" ;; esac @@ -449,7 +449,7 @@ case "$MODE" in fi ;; opencode) invoke_via_connect "$out" run "$prompt" ;; hermes) invoke_via_connect "$out" -z "$prompt" ;; - openclaw) invoke_via_connect "$out" agent --local --agent ci \ + openclaw) CONNECT_CMD_OVERRIDE=openclaw invoke_via_connect "$out" agent --local --agent ci \ --model "unsloth/${UNSLOTH_MODEL_ID}" --message "$prompt" ;; *) invoke_via_connect "$out" "$prompt" ;; esac diff --git a/unsloth_cli/commands/start.py b/unsloth_cli/commands/start.py index 477a47cc3d..764f5c7963 100644 --- a/unsloth_cli/commands/start.py +++ b/unsloth_cli/commands/start.py @@ -1568,7 +1568,17 @@ def openclaw( serve = serve, launch = launch, ) - command = ["openclaw", *ctx.args] + openclaw_args = list(ctx.args) + # Default a bare `unsloth start openclaw` to the local TUI. Anything the caller + # passes through is forwarded verbatim so OpenClaw parses it under its own grammar + # (openclaw [global-flags] [options]): an explicit subcommand, a global + # flag that must precede the command such as --profile/--dev, or a tui option. We + # cannot reinterpret those safely because a leading "--flag value" is ambiguous + # between a global (`--profile test`) and a tui option (`--message hi`); prepending + # `tui --local` would break the global form, so only the empty case is defaulted. + if not openclaw_args: + openclaw_args = ["tui", "--local"] + command = ["openclaw", *openclaw_args] install_hint = ( "iwr -useb https://openclaw.ai/install.ps1 | iex" if os.name == "nt" diff --git a/unsloth_cli/tests/test_start.py b/unsloth_cli/tests/test_start.py index ee5b442c27..18cb40f18d 100644 --- a/unsloth_cli/tests/test_start.py +++ b/unsloth_cli/tests/test_start.py @@ -1634,10 +1634,34 @@ def test_connect_openclaw_no_launch(fake_studio, tmp_path): config = json.loads(config_path.read_text()) assert config["models"]["providers"]["unsloth"]["apiKey"] == "sk-unsloth-feedfacefeedface" assert config["agents"]["defaults"]["model"]["primary"] == f"unsloth/{MODEL['id']}" + assert _launch_command(result.output) == ["openclaw", "tui", "--local"] # OpenAI /v1/chat/completions works on either backend — no GGUF gate. assert not any(c[1].endswith("/api/inference/status") for c in fake_studio) +def test_connect_openclaw_no_launch_keeps_explicit_subcommand(fake_studio): + result = CliRunner().invoke(start.start_app, ["openclaw", "--no-launch", "crestodian"]) + assert result.exit_code == 0, result.output + assert _launch_command(result.output) == ["openclaw", "crestodian"] + + +def test_connect_openclaw_no_launch_passes_global_flags_through(fake_studio): + # OpenClaw globals (openclaw [--dev] [--profile ] ) precede the + # command, and tui does not accept them, so any passthrough args must be forwarded + # verbatim rather than rewritten into `openclaw tui --local `. + result = CliRunner().invoke(start.start_app, ["openclaw", "--no-launch", "--profile", "test"]) + assert result.exit_code == 0, result.output + assert _launch_command(result.output) == ["openclaw", "--profile", "test"] + + +def test_connect_openclaw_no_launch_keeps_explicit_tui(fake_studio): + result = CliRunner().invoke( + start.start_app, ["openclaw", "--no-launch", "tui", "--message", "hi"] + ) + assert result.exit_code == 0, result.output + assert _launch_command(result.output) == ["openclaw", "tui", "--message", "hi"] + + # ── OpenCode (OpenAI /v1/chat/completions) ───────────────────────────