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) ───────────────────────────