Created
August 22, 2026 12:02
-
-
Save uacode/b4e1d4bfa95c995c2b6fcb14fd6e13b3 to your computer and use it in GitHub Desktop.
lcc — a tiny shell launcher that points Claude Code at a local LLM (llama.cpp / any Anthropic-Messages-API server). Auto-detects loaded models, no cloud, no API key
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # lcc - Local Claude Code launcher | |
| # Points Claude Code at a local LLM served by llama.cpp on your GB10 device | |
| # | |
| # Usage: | |
| # lcc <modelname> — launch Claude Code with the specified model | |
| # lcc <modelname> [args] — pass additional arguments to claude | |
| # lcc — auto-detect the loaded model (or pick one) and launch | |
| # lcc -h | --help — show help | |
| # lcc -V | --version — show version | |
| # | |
| # Prerequisites: | |
| # - Claude Code installed | |
| # - LLM provider with Anthropic Messages API support | |
| # - jq (for model auto-detection) | |
| # | |
| # | |
| # Configuration: change if needed: | |
| LCC_VERSION="1.0.0" | |
| LCC_HOST="${LCC_HOST:-192.168.1.6}" # This is my server IP, you should change to your most commonly used | |
| LCC_PORT="${LCC_PORT:-8000}" | |
| LCC_BASE_URL="http://${LCC_HOST}:${LCC_PORT}" | |
| LCC_CURL_TIMEOUT="${LCC_CURL_TIMEOUT:-2}" # seconds to wait for the server before giving up | |
| # ANSI color codes | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| MAGENTA='\033[0;35m' | |
| CYAN='\033[0;36m' | |
| WHITE='\033[1;37m' | |
| NC='\033[0m' | |
| BOLD='\033[1m' | |
| print_examples() { | |
| echo "" | |
| echo -e "${WHITE}Examples:${NC}" | |
| echo -e " ${GREEN}lcc qwen3-coder${NC}" | |
| echo -e " ${GREEN}lcc my-model --continue${NC} # extra flags pass through to claude" | |
| echo "" | |
| echo -e "${WHITE}Override host/port:${NC}" | |
| echo -e " ${CYAN}LCC_HOST=10.0.0.5 LCC_PORT=9090 lcc mymodel${NC}" | |
| } | |
| print_help() { | |
| echo -e "${BOLD}lcc${NC} — Local Claude Code launcher ${YELLOW}v${LCC_VERSION}${NC}" | |
| echo "" | |
| echo -e "${WHITE}Usage:${NC}" | |
| echo -e " ${GREEN}lcc <modelname> [args]${NC} launch Claude Code with the specified model" | |
| echo -e " ${GREEN}lcc${NC} auto-detect the loaded model and launch" | |
| echo -e " ${GREEN}lcc -h | --help${NC} show this help" | |
| echo -e " ${GREEN}lcc -V | --version${NC} show version" | |
| print_examples | |
| } | |
| launch() { | |
| local model="$1" | |
| shift | |
| echo -e "${GREEN}🚀 Launching${NC} ${BOLD}Claude Code${NC} ${CYAN}${LCC_HOST}:${LCC_PORT}${NC} / ${MAGENTA}${BOLD}${model}${NC}" | |
| export ANTHROPIC_BASE_URL="${LCC_BASE_URL}" | |
| export ANTHROPIC_AUTH_TOKEN="local" | |
| # Make sure claude can't pick up a real API key and bypass the local server | |
| unset ANTHROPIC_API_KEY | |
| # Share ~/.claude with native claude: same sessions, settings, plugins, | |
| # --resume list. CLAUDE_CONFIG_DIR is intentionally NOT set. | |
| # The earlier isolation (separate ~/.claude-lcc) existed to stop a logged-in | |
| # claude.ai OAuth account from leaking mid-session requests to | |
| # api.anthropic.com (404 on local model ids → "issue with selected model"). | |
| # That no longer happens: ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN force all | |
| # traffic onto the local server, and the native `claude --model ...:cloud` | |
| # flow already runs shared like this without leaks. Do NOT re-add a config | |
| # dir or a settings-seed here — it would clobber ~/.claude/settings.json. | |
| # Claude Code makes background requests with claude-* model ids (skill | |
| # triggering, session titles, etc.). The local server 404s on those, which | |
| # surfaces as "There's an issue with the selected model" mid-session — | |
| # point every model class at the local model instead. | |
| export ANTHROPIC_DEFAULT_HAIKU_MODEL="$model" | |
| export ANTHROPIC_DEFAULT_SONNET_MODEL="$model" | |
| export ANTHROPIC_DEFAULT_OPUS_MODEL="$model" | |
| export ANTHROPIC_DEFAULT_FABLE_MODEL="$model" | |
| export ANTHROPIC_SMALL_FAST_MODEL="$model" # deprecated alias, kept for older claude versions | |
| export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 | |
| export CLAUDE_CODE_ATTRIBUTION_HEADER=0 | |
| exec claude --model "$model" "$@" | |
| } | |
| case "${1:-}" in | |
| -h|--help) | |
| print_help | |
| exit 0 | |
| ;; | |
| -V|--version) | |
| echo "lcc ${LCC_VERSION}" | |
| exit 0 | |
| ;; | |
| -*) | |
| echo -e "${RED}✗ Unknown option: $1${NC} (first argument must be a model name)" >&2 | |
| echo -e "Run ${GREEN}lcc --help${NC} for usage." >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| # Model given explicitly — launch right away | |
| if [[ -n "${1:-}" ]]; then | |
| MODEL="$1" | |
| shift # remaining args pass through to claude | |
| launch "$MODEL" "$@" | |
| fi | |
| # No model argument: check the server and auto-detect | |
| echo -e "${BOLD}Local Claude Code${NC} (${CYAN}GB10 @ ${LCC_HOST}:${LCC_PORT}${NC})" | |
| echo "" | |
| if ! curl -sf --connect-timeout "$LCC_CURL_TIMEOUT" "${LCC_BASE_URL}/health" > /dev/null 2>&1; then | |
| echo -e "${RED}✗ Cannot reach llama-server at ${LCC_BASE_URL}${NC}" | |
| echo "" | |
| echo -e "${WHITE}Make sure you're using an LLM provider that supports${NC}" | |
| echo -e " ${CYAN}the Anthropic Messages API (/v1/messages endpoint)${NC}" | |
| print_examples | |
| exit 1 | |
| fi | |
| echo -e "${GREEN}✓ llama-server is running${NC}" | |
| MODEL_INFO=$(curl -sf --connect-timeout "$LCC_CURL_TIMEOUT" "${LCC_BASE_URL}/v1/models" 2>/dev/null) | |
| if [[ -z "$MODEL_INFO" ]]; then | |
| echo -e "${YELLOW}⚠ Server is up but /v1/models returned nothing${NC}" | |
| print_examples | |
| exit 1 | |
| fi | |
| if ! command -v jq &>/dev/null; then | |
| echo "" | |
| echo -e "${YELLOW}⚠ jq not found; install it for model auto-detection${NC}" | |
| print_examples | |
| exit 1 | |
| fi | |
| # `?` and `// empty` keep this safe even if the response isn't the JSON we expect | |
| mapfile -t MODELS < <(jq -r '.data[]?.id // empty' <<<"$MODEL_INFO" 2>/dev/null) | |
| MODEL_COUNT=${#MODELS[@]} | |
| if (( MODEL_COUNT == 0 )); then | |
| echo -e "${YELLOW}⚠ Server reported no loaded models${NC}" | |
| print_examples | |
| exit 1 | |
| fi | |
| if (( MODEL_COUNT == 1 )); then | |
| MODEL="${MODELS[0]}" | |
| echo "" | |
| echo -e "${WHITE}Loaded model:${NC} ${BOLD}${MODEL}${NC}" | |
| echo -e "${CYAN}Automatic model selection:${NC} Using ${BOLD}${MODEL}${NC}" | |
| echo "" | |
| launch "$MODEL" | |
| fi | |
| # Multiple models — let the user pick one | |
| echo "" | |
| echo -e "${WHITE}Loaded models:${NC}" | |
| for i in "${!MODELS[@]}"; do | |
| echo -e " ${WHITE}$((i+1)).${NC} ${MODELS[$i]}" | |
| done | |
| echo "" | |
| read -rp "$(echo -e "${CYAN}Select model${NC} [1-${MODEL_COUNT}], Enter to quit: ")" CHOICE | |
| if [[ "$CHOICE" =~ ^[0-9]+$ ]] && (( CHOICE >= 1 && CHOICE <= MODEL_COUNT )); then | |
| launch "${MODELS[CHOICE-1]}" | |
| fi | |
| print_examples | |
| exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment