Skip to content

Repository files navigation

lcc — Launch Claude Code

License: MIT Latest release Rust GitHub stars

A lightweight Rust launcher that lets you run the official Claude Code CLI against any OpenAI/Anthropic-compatible provider — OpenRouter, Ollama, vLLM, LiteLLM, your own gateway — without juggling environment variables by hand.

lcc — Claude Code's /model picker showing the multi-model OpenRouter profile (opus → owl-alpha, sonnet → deepseek-v4-pro, haiku → deepseek-v4-flash, custom → kimi)

lcc start --profil openrouter

Why

Claude Code reads its provider configuration from environment variables (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL, …). Editing your shell rc file every time you want to swap from Claude → OpenRouter → local Ollama is painful. lcc keeps named profiles in a single JSON file and injects the right env vars per launch.

A single profile can also hold multiple models behind one provider, so once you're inside Claude Code, the built-in /model command lets you switch models on the fly without restarting the session.

Install

Requires Rust 1.85+ (edition 2024) and the official claude CLI installed (Claude Code docs).

git clone https://github.com/eRom/launch-ccode-cli.git
cd launch-ccode-cli
cargo install --path .

Then create your config at ~/.config/launch-claude-code/settings.json — see config-sample.json for a starting point.

Quick start — single-model profile

Map one model to one provider. Simplest form:

{
  "profiles": {
    "ollama-local": {
      "model": "gemma4",
      "base_url": "http://localhost:11434/v1",
      "api_key": "",
      "auth_token": "ollama"
    }
  }
}
lcc start --profil ollama-local

${VAR} placeholders in any string field are expanded from your shell environment at launch time, so secrets never live in the file:

"auth_token": "${OPENROUTER_API_KEY}"

Multi-model profile — /model switching at runtime

When you have one provider and several models behind it (typical OpenRouter setup), declare them all under one profile:

{
  "profiles": {
    "openrouter": {
      "base_url": "https://openrouter.ai/api",
      "auth_token": "${OPENROUTER_API_KEY}",
      "default": "deepseek-v4-pro",
      "models": {
        "owl-alpha":         { "id": "openrouter/owl-alpha",        "slot": "opus" },
        "deepseek-v4-pro":   { "id": "deepseek/deepseek-v4-pro",    "slot": "sonnet" },
        "deepseek-v4-flash": { "id": "deepseek/deepseek-v4-flash",  "slot": "haiku" },
        "kimi":              { "id": "moonshotai/kimi-k2.6", "slot": "custom",
                               "description": "Moonshot Kimi K2.6" },
        "gemma-4-26b":       { "id": "google/gemma-4-26b-a4b-it" },
        "glm-5.1":           { "id": "z-ai/glm-5.1" }
      }
    }
  }
}

Each slot maps to a Claude Code env var so the model appears in the interactive /model picker:

slot Env var /model behaviour
opus ANTHROPIC_DEFAULT_OPUS_MODEL /model opus switches instantly
sonnet ANTHROPIC_DEFAULT_SONNET_MODEL /model sonnet switches instantly
haiku ANTHROPIC_DEFAULT_HAIKU_MODEL (+ ANTHROPIC_SMALL_FAST_MODEL) /model haiku switches instantly; same model also used for background tasks (web summaries, session titles, hooks)
custom ANTHROPIC_CUSTOM_MODEL_OPTION (+ _NAME / _DESCRIPTION) adds an extra entry to the picker
(none) reachable via full slug: /model google/gemma-4-26b-a4b-it

Only one model can claim slot: "custom". Models without a slot still work — Claude Code accepts any arbitrary model id at the /model prompt and validates it through a small probe call.

lcc start --profil openrouter
# inside Claude Code:
#   /model            → opens the picker
#   /model opus       → switches to owl-alpha
#   /model haiku      → switches to deepseek-v4-flash
#   /model z-ai/glm-5.1

CLI

lcc list                            # show configured profiles
lcc settings                        # open the JSON file in your editor
lcc settings --validate             # parse-check the JSON
lcc start --profil <name> [args…]   # launch claude with the given profile

Anything after --profil <name> is passed straight through to claude, so you can override the model at launch time or use any native flag:

lcc start --profil openrouter --model moonshotai/kimi-k2.6
lcc start --profil openrouter -- --output-format json -p "hello"

Settings reference

~/.config/launch-claude-code/settings.json:

{
  "profiles": {
    "<name>": {
      // === Single-model profile ===
      "model":       "<model id>",
      "base_url":    "<provider URL>",
      "api_key":     "<key or ${ENV}>",     // wins if non-empty
      "auth_token":  "<token or ${ENV}>",   // used if api_key is empty
      "env":         { "EXTRA_VAR": "value" }   // optional, passed to claude

      // === OR Multi-model profile ===
      "base_url":    "<provider URL>",
      "auth_token":  "${ENV}",
      "default":     "<key in models>",
      "models": {
        "<friendly name>": {
          "id":          "<model id>",
          "slot":        "opus" | "sonnet" | "haiku" | "custom",  // optional
          "description": "<shown in custom picker>"               // optional
        }
      },
      "env": { "EXTRA_VAR": "value" }   // optional
    }
  }
}

Authentication: if both api_key and auth_token are set, the API key wins (set as ANTHROPIC_API_KEY). Empty api_key falls back to auth_token (set as ANTHROPIC_AUTH_TOKEN). At least one must be non-empty.

Known limitations

  • Claude Code's non-interactive print mode (-p) parses the model's response with a strict extractor that expects an Anthropic-native content shape. Some non-Anthropic models served via OpenAI-compat gateways return blocks the extractor doesn't surface, leaving result empty even though the model billed tokens. Interactive mode works fine for those models. This is a Claude Code ↔ provider compatibility issue, not an lcc issue.

Smoke test du proxy LiteLLM (V1)

Prerequisites:

  • macOS
  • brew install uv
  • OPENROUTER_API_KEY (or other provider key) exported in .zshrc
  • A profile in ~/.config/launch-claude-code/settings.json pointing to OpenRouter (e.g., qwen with model: qwen/qwen3.6-plus)
# 1. Build + install
cargo install --path .

# 2. Setup proxy (one-shot, ~30s)
lcc proxy install

# 3. Verify everything is green
lcc proxy doctor
# Expected: 7 lines "[✓]"

# 4. First call
lcc start --profil qwen -p "Say hello to me in one sentence"
# Expected: actual text response (not an empty terminal!)

# 5. Verify that `thinking` blocks are stripped
lcc proxy logs | grep -i thinking || echo "OK: no thinking in recent logs"

# 6. Test direct claude path (should always work)
claude -p "ping"
# Expected: native Anthropic response, completely independent from the proxy

If step 4 fails silently (empty terminal), it's a regression. Debug:

  • lcc proxy logs — the exact error returned by the provider
  • Direct call to the proxy via curl:
curl -sS http://localhost:4000/v1/messages \
  -H "x-api-key: $(security find-generic-password -s lcc.litellm.master_key -w)" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{"model":"qwen","max_tokens":50,"messages":[{"role":"user","content":"hi"}]}'

License

MIT — see LICENSE.

About

A lightweight launcher to run Claude Code against any OpenAI/Anthropic-compatible provider (OpenRouter, Ollama, vLLM, ...) with named profiles and runtime model switching.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages