The Custom (OpenAI-compat) /setup option was saving kind: openai and
injecting OPENAI_API_KEY / OPENAI_BASE_URL. That collides with users who
have real OpenAI/NeuralWatt credentials in their environment.
Introduce a dedicated custom-openai provider kind that uses its own
environment variables:
- CLAWCUSTOMOPENAI_API_KEY
- CLAWCUSTOMOPENAI_BASE_URL
A new custom/ routing prefix selects the OpenAI-compatible client with
those env vars and is stripped on the wire, so the proxy receives the
bare model id. /setup now saves kind: custom-openai and prompts for the
new env vars. Bare model names saved by /setup are normalized to
custom/<model>.
Manual verification against http://100.96.49.42:4001/v1 succeeds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Move config-to-env injection out of AnthropicRuntimeClient::new so
parallel unit tests are not affected by global env mutations.
- Call inject_config_as_env_fallbacks() once at binary startup in run(),
preserving the env-var > .env > stored-config precedence.
- Normalize bare model names (e.g. openclaw) to openai/openclaw when a
custom OpenAI-compatible base URL is configured, so validation passes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The /setup wizard saves apiKey and baseUrl to ~/.claw/settings.json,
but the API client constructors (OpenAiCompatClient::from_env,
AnthropicClient::from_env) only read environment variables. This caused
saved provider settings to be silently ignored — you'd run /setup,
set a custom URL and API key, and the runtime would still try to use
the default endpoint.
Now AnthropicRuntimeClient::new() calls inject_config_as_env_fallbacks()
before constructing the API client. This function loads the config file's
provider settings and sets the corresponding env vars (OPENAI_API_KEY,
OPENAI_BASE_URL, etc.) only when they aren't already set — preserving
the 3-tier resolution order: env var > .env file > stored config.
This is a process-level env injection (set_var), so it only affects
the current claw process and its children, not the parent shell.
- OLLAMA_HOST takes priority over OPENAI_BASE_URL for local Ollama instances
- No API key required; placeholder token used for Authorization header
- Model names like 'qwen3:8b' bypass strict provider/model syntax validation
- detect_provider_kind() checks OLLAMA_HOST first in routing cascade
- ProviderClient dispatch uses from_ollama_env() when OLLAMA_HOST is set
- Updated USAGE.md and docs with OLLAMA_HOST as preferred env var
- Added OLLAMA_CONFIG constant and from_ollama_env() to openai_compat
- Added test_ollama_host_bypasses_model_validation unit test
- Supersedes PR #3213 (which had a duplicate if-let bug in mod.rs)
- Update slash_command_specs().len() assertion from 139 to 140.
The /setup command added by this PR increased the spec count by 1
but the test's expected count was not updated, causing CI failure.
- Add assert!(help.contains("/setup")) to the
renders_help_from_shared_specs test so the new command is
verified in the help output.
Fixes CI Build ❌ and Test ❌ on #3218.
The setup wizard was merged in PR #3017 but was orphaned -- it was not
declared as a module in main.rs, making it unreachable. Additionally,
the setup_wizard.rs imports RuntimeProviderConfig which did not exist
on upstream/main. This commit makes the wizard accessible and adds the
necessary RuntimeProviderConfig type.
Changes:
- Add RuntimeProviderConfig struct to runtime/src/config.rs with
kind(), api_key(), base_url(), model() accessors.
- Add parse_optional_provider_config() to parse the provider object
from merged settings JSON.
- Add provider() method to RuntimeConfig and RuntimeFeatureConfig.
- Export RuntimeProviderConfig, save_user_provider_settings,
clear_user_provider_settings, and default_config_home from runtime
crate public API (runtime/src/lib.rs).
- Add mod setup_wizard to rusty-claude-cli/src/main.rs.
- Add claw setup CLI subcommand.
- Add /setup slash command.
- Add Setup variant to SlashCommand enum.
- Add Setup to LocalHelpTopic enum.
- Add setup to diagnostic subcommand matching.
- Add subagentModel to TOP_LEVEL_FIELDS in config_validate.rs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Updated the agents_and_skills_usage_support_help_and_unexpected_args
test to match the new skills help text that includes [--project].
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
Session save_to_path now wraps ENOENT errors from rotate and atomic
write with a clear "possible concurrent modification" message instead
of surfacing raw OS errno. Helps operators debugging race conditions
when multiple claw invocations touch the same session file.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
SessionLifecycleSummary now collects all matching tmux panes into an
all_panes field and includes them in the JSON output. Previously the
status command returned on the first non-idle pane, losing all other
active panes in the same workspace/session.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
401 and 403 errors now include a hint explaining which env vars to
check for each provider (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
and suggesting claw doctor for credential verification.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
claw skills install --project <path> now installs to .claw/skills/
in the current project instead of the user-level registry. Skills
installed at project level are already discovered by the existing
registry system. Both text and JSON handlers updated.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
MCP server config now expands ${VAR} environment variable references
and ~/ home directory prefix in command, args, and url fields. Previously
these values were passed verbatim to execve/URL-parse, causing silent
"No such file or directory" failures for standard config patterns.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
deep_merge_objects now concatenates arrays when both layers provide
the same key. Previously permissions.allow, hooks.PreToolUse, etc.
from earlier config layers (e.g. ~/.claw/settings.json) were silently
discarded when a later layer (e.g. project .claw/settings.json) set
the same key. Now arrays are merged additively across layers.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
PermissionRule::parse now normalizes tool_name to lowercase, matching
the runtime convention. Previously "Bash(rm:*)" would never match
because the runtime tool name is lowercase "bash". Same fix applied
to denied_tools list.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
/resume mode /clear now preserves the original session_id instead of
generating a new one. This prevents the filename/meta-header divergence
where /session list reported an id that --resume couldn't find.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
/session switch and /session fork now return structured JSON with
kind:error, error_kind:unsupported_resumed_command, and actionable
hint instead of a raw error string that resume callers couldn't parse.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
Verified with a roundtrip test: creating a session with usage data
({input_tokens:100, output_tokens:50}), saving to JSONL, loading via
--resume, and running /cost and /stats both return correct values.
The UsageTracker::from_session reads message.usage which is properly
serialized/deserialized by ConversationMessage::to_json/from_json.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
Add friendly_cwd() helper that strips /private prefix on macOS so
status, doctor, and diff JSON output matches user-visible invocation
cwd instead of the canonicalized /private/tmp path.
Applied to status_context() and render_doctor_report().
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
342: /commands command-index alias is now covered by /help --output-format
json which returns structured commands array with 139 entries (name,
summary, resume_supported per command). Implemented in #325.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>
/model was in the unsupported resume group but is a read-only command
that can safely return model configuration. Now returns default_model,
configured_model, resolved_model, and requested_model in JSON mode.
Generated with https://github.com/Yeachan-Heo/gajae-code
Co-authored-by: Gajae Code <dev@gajae-code.com>