claudish/ai_docs/claudish-CODEBASE_ANALYSIS.md

14 KiB

Claudish Codebase Analysis

Overview

Claudish is a CLI tool that runs Claude Code with OpenRouter models via a local Anthropic API-compatible proxy server. It's located at mcp/claudish/ in the repository root and consists of a TypeScript/Bun project.

Current Version: v1.3.1

Directory Structure

mcp/claudish/
├── src/
│   ├── index.ts                  # Main entry point
│   ├── cli.ts                    # CLI argument parser
│   ├── config.ts                 # Configuration constants
│   ├── types.ts                  # TypeScript interfaces
│   ├── claude-runner.ts          # Claude Code execution & temp settings
│   ├── proxy-server.ts           # Hono-based proxy server (58KB file!)
│   ├── transform.ts              # OpenAI ↔ Anthropic API transformation
│   ├── logger.ts                 # Debug logging
│   ├── simple-selector.ts        # Interactive model/API key prompts
│   ├── port-manager.ts           # Port availability checking
│   └── adapters/                 # Model-specific adapters
│       ├── adapter-manager.ts
│       ├── base-adapter.ts
│       ├── grok-adapter.ts
│       └── index.ts
├── package.json                  # npm dependencies & scripts
├── tsconfig.json                 # TypeScript config
├── biome.json                    # Code formatting config
└── dist/                          # Compiled JavaScript

Key Components

1. Main Entry Point (src/index.ts)

Purpose: CLI orchestration and setup

Key Flow:

  1. Parses CLI arguments via parseArgs()
  2. Initializes logger if debug mode is enabled
  3. Checks if Claude Code is installed
  4. Prompts for OpenRouter API key if needed (interactive mode only)
  5. Prompts for model selection if not provided (interactive mode only)
  6. Reads stdin if --stdin flag is set
  7. Finds available port
  8. Creates proxy server
  9. Spawns Claude Code with proxy environment variables
  10. Cleans up proxy on exit

2. Configuration (src/config.ts)

Key Constants:

export const ENV = {
  OPENROUTER_API_KEY: "OPENROUTER_API_KEY",
  CLAUDISH_MODEL: "CLAUDISH_MODEL",
  CLAUDISH_PORT: "CLAUDISH_PORT",
  CLAUDISH_ACTIVE_MODEL_NAME: "CLAUDISH_ACTIVE_MODEL_NAME", // Set by claudish
} as const;

export const MODEL_INFO: Record<OpenRouterModel, {
  name: string;
  description: string;
  priority: number;
  provider: string;
}> = {
  "x-ai/grok-code-fast-1": { name: "Grok Code Fast", ... },
  "openai/gpt-5-codex": { name: "GPT-5 Codex", ... },
  "minimax/minimax-m2": { name: "MiniMax M2", ... },
  // ... etc
}

Available Models (Priority Order):

  1. x-ai/grok-code-fast-1 (Grok Code Fast)
  2. openai/gpt-5-codex (GPT-5 Codex)
  3. minimax/minimax-m2 (MiniMax M2)
  4. z-ai/glm-4.6 (GLM-4.6)
  5. qwen/qwen3-vl-235b-a22b-instruct (Qwen3 VL)
  6. anthropic/claude-sonnet-4.5 (Claude Sonnet)
  7. Custom (any OpenRouter model)

3. CLI Parser (src/cli.ts)

Responsibility: Parse command-line arguments and environment variables

Environment Variables Supported:

  • OPENROUTER_API_KEY - OpenRouter authentication (required for non-interactive mode)
  • CLAUDISH_MODEL - Default model (optional)
  • CLAUDISH_PORT - Default proxy port (optional)
  • ANTHROPIC_API_KEY - Placeholder to prevent Claude Code dialog (handled in claude-runner.ts)

Arguments:

  • -i, --interactive - Interactive mode
  • -m, --model <model> - Specify model
  • -p, --port <port> - Specify port
  • --json - JSON output
  • --debug, -d - Debug logging
  • --monitor - Monitor mode (passthrough to real Anthropic API)
  • --stdin - Read prompt from stdin
  • And many others...

Default Behavior:

  • If no prompt provided and not --stdin, defaults to interactive mode
  • In interactive mode, prompts for missing API key and model
  • In single-shot mode, requires --model flag or CLAUDISH_MODEL env var

4. Claude Runner (src/claude-runner.ts)

Purpose: Execute Claude Code with proxy and manage temp settings

Key Responsibilities:

  1. Create Temporary Settings File:

    • Location: /tmp/claudish-settings-{timestamp}.json
    • Contains: Custom status line command
    • Purpose: Show model info in Claude Code status line without modifying global settings
  2. Environment Variables Passed to Claude Code:

    env: {
      ...process.env,
      ANTHROPIC_BASE_URL: proxyUrl,           // Point to local proxy
      CLAUDISH_ACTIVE_MODEL_NAME: modelId,    // Used in status line
      ANTHROPIC_API_KEY: placeholder          // Prevent dialog (OpenRouter mode)
    }
    
  3. Status Line Format:

    • Shows: [directory] • [model] • $[cost] • [context%]
    • Uses ANSI colors for visual enhancement
    • Reads token data from file written by proxy server
    • Model name comes from $CLAUDISH_ACTIVE_MODEL_NAME environment variable
  4. Context Window Tracking:

    • Model context sizes hardcoded in MODEL_CONTEXT object
    • Reads cumulative token counts from /tmp/claudish-tokens-{PORT}.json
    • Calculates context percentage remaining
    • Defaults to 100k tokens for unknown models
  5. Signal Handling:

    • Cleans up temp settings file on SIGINT/SIGTERM/SIGHUP
    • Ensures no zombie processes

5. Proxy Server (src/proxy-server.ts)

Size: 58,460 bytes (large file!)

Architecture:

  • Built with Hono.js + @hono/node-server
  • Implements Anthropic API-compatible endpoints
  • Transforms requests between Anthropic and OpenRouter formats

Key Endpoints:

  • GET / - Health check
  • GET /health - Alternative health check
  • POST /v1/messages/count_tokens - Token counting
  • POST /v1/messages - Main chat completion endpoint (streaming and non-streaming)

Modes:

  1. OpenRouter Mode (default)

    • Routes requests to OpenRouter API
    • Uses provided OpenRouter API key
    • Filters Claude identity claims from system prompts
  2. Monitor Mode (--monitor flag)

    • Passes through to real Anthropic API
    • Logs all traffic for debugging
    • Extracts API key from Claude Code requests

Key Features:

  • CORS headers enabled
  • Streaming response support
  • Token counting and tracking
  • System prompt filtering (removes Claude identity claims)
  • Error handling with detailed messages

Token File Writing:

const tokenFilePath = `/tmp/claudish-tokens-${port}.json`;

writeFileSync(tokenFilePath, JSON.stringify({
  input_tokens: cumulativeInputTokens,
  output_tokens: cumulativeOutputTokens,
  total_tokens: cumulativeInputTokens + cumulativeOutputTokens,
  updated_at: Date.now()
}), "utf-8");

6. Type Definitions (src/types.ts)

Main Interfaces:

  • ClaudishConfig - CLI configuration object
  • OpenRouterModel - Union type of available models
  • AnthropicMessage, AnthropicRequest, AnthropicResponse - Anthropic API types
  • OpenRouterMessage, OpenRouterRequest, OpenRouterResponse - OpenRouter API types
  • ProxyServer - Proxy server interface with shutdown() method

How Model Information is Communicated

Current Mechanism (v1.3.1)

  1. CLI receives model: From --model flag, CLAUDISH_MODEL env var, or interactive selection

  2. Model is passed to proxy creation:

    const proxy = await createProxyServer(
      port,
      config.openrouterApiKey,
      config.model,           // <-- Model ID passed here
      config.monitor,
      config.anthropicApiKey
    );
    
  3. Model is set as environment variable:

    env: {
      CLAUDISH_ACTIVE_MODEL_NAME: modelId, // Set in claude-runner.ts
    }
    
  4. Status line reads from environment: In the temporary settings file, the status line command uses:

    printf "... ${YELLOW}%s${RESET} ..." "$CLAUDISH_ACTIVE_MODEL_NAME"
    

How Token Information Flows

  1. Proxy server tracks tokens:

    • Accumulates input/output tokens during conversation
    • Writes to /tmp/claudish-tokens-{PORT}.json after each request
  2. Status line reads token file:

    • Claude runner creates status line command that reads the token file
    • Calculates remaining context percentage
    • Displays as part of status line
  3. Environment Variables Used in Status Line:

    CLAUDISH_ACTIVE_MODEL_NAME - The OpenRouter model ID
    

Environment Variable Handling Details

Variables Currently Supported

Variable Set By Read By Purpose
OPENROUTER_API_KEY User (.env or prompt) cli.ts, proxy-server.ts OpenRouter authentication
CLAUDISH_MODEL User (.env) cli.ts Default model selection
CLAUDISH_PORT User (.env) cli.ts Default proxy port
CLAUDISH_ACTIVE_MODEL_NAME claude-runner.ts Status line script Display model in status line
ANTHROPIC_BASE_URL claude-runner.ts Claude Code Point to local proxy
ANTHROPIC_API_KEY claude-runner.ts Claude Code Prevent authentication dialog

Variable Flow Chart

User Input (.env, CLI flags)
    ↓
parseArgs() in cli.ts
    ↓
ClaudishConfig object
    ↓
createProxyServer() + runClaudeWithProxy()
    ↓
Environment variables passed to Claude Code:
  - ANTHROPIC_BASE_URL → proxy URL
  - CLAUDISH_ACTIVE_MODEL_NAME → model ID
  - ANTHROPIC_API_KEY → placeholder
    ↓
Claude Code spawned with:
  - Temporary settings file (for status line)
  - Environment variables
  - CLI arguments

Missing Environment Variable Support

Not Yet Implemented

  1. ANTHROPIC_MODEL - Not used anywhere in Claudish

    • Could be used to override model for status line display
    • Could help Claude Code identify which model is active
  2. ANTHROPIC_SMALL_FAST_MODEL - Not used anywhere

    • Could be used for smaller tasks within Claude Code
    • Not applicable since Claudish uses OpenRouter models
  3. Model Display Name Customization - No way to provide a friendly display name

    • Currently always shows the OpenRouter model ID (e.g., "x-ai/grok-code-fast-1")
    • Could benefit from showing provider + model name (e.g., "xAI Grok Fast")

Interesting Implementation Details

1. Token File Path Convention

// Uses port number to ensure each Claudish instance has its own token file
const tokenFilePath = `/tmp/claudish-tokens-${port}.json`;

2. Temporary Settings File Pattern

// Each instance gets unique temp file to avoid conflicts
const tempPath = join(tmpdir(), `claudish-settings-${timestamp}.json`);

3. Model Context Hardcoding

const MODEL_CONTEXT: Record<string, number> = {
  "x-ai/grok-code-fast-1": 256000,
  "openai/gpt-5-codex": 400000,
  // ... etc with fallback to 100k
};

4. Status Line Script Generation

  • Creates a complex bash script that:
    • Reads token data from temp file
    • Calculates context percentage
    • Formats output with ANSI colors
    • All embedded in JSON settings file!

5. API Key Handling Strategy

  • OpenRouter mode: Sets placeholder ANTHROPIC_API_KEY to prevent Claude dialog
  • Monitor mode: Deletes ANTHROPIC_API_KEY to allow Claude to use native auth
  • Both: Actually uses the key specified in the proxy or from Claude's request

Integration Points

With Claude Code

  1. Temporary Settings File - Passed via --settings flag
  2. Environment Variables - ANTHROPIC_BASE_URL, CLAUDISH_ACTIVE_MODEL_NAME, ANTHROPIC_API_KEY
  3. Proxy Server - Running on localhost, acts as Anthropic API
  4. Token File - Status line reads from /tmp/claudish-tokens-{PORT}.json

With OpenRouter

  1. API Requests - Proxy transforms Anthropic → OpenRouter format
  2. Authentication - Uses OPENROUTER_API_KEY environment variable
  3. Model Selection - Any OpenRouter model ID is supported

Recommendations for Environment Variable Support

Based on this analysis, here are recommendations for adding proper environment variable support:

1. Add Model Display Name Support

// In config.ts
export const ENV = {
  // ... existing
  ANTHROPIC_MODEL: "ANTHROPIC_MODEL",              // Display name override
  CLAUDISH_MODEL_DISPLAY_NAME: "CLAUDISH_MODEL_DISPLAY_NAME",  // Custom display name
};

2. Modify claude-runner.ts

// Extract display name from config
const displayName = config.modelDisplayName || config.model;

// Pass to status line command via environment variable
env[ENV.CLAUDISH_MODEL_DISPLAY_NAME] = displayName;

3. Update Status Line Script

# Instead of:
printf "... ${YELLOW}%s${RESET} ..." "$CLAUDISH_ACTIVE_MODEL_NAME"

# Could support:
DISPLAY_NAME=${CLAUDISH_MODEL_DISPLAY_NAME:-$CLAUDISH_ACTIVE_MODEL_NAME}
printf "... ${YELLOW}%s${RESET} ..." "$DISPLAY_NAME"

4. Support ANTHROPIC_MODEL Variable

// In cli.ts, after parsing CLAUDISH_MODEL
const envModel = process.env[ENV.CLAUDISH_MODEL];
const anthropicModel = process.env[ENV.ANTHROPIC_MODEL];
if (!config.model) {
  config.model = anthropicModel || envModel;
}

Summary

Claudish is a well-structured CLI tool that:

  • Manages model selection through multiple channels (flags, env vars, interactive prompts)
  • Communicates active model to Claude Code via CLAUDISH_ACTIVE_MODEL_NAME environment variable
  • Tracks tokens in a file for status line consumption
  • Uses temporary settings files to avoid modifying global configuration
  • Has clear separation of concerns between CLI, proxy, and runner components

Current environment variable handling is functional but could be enhanced with:

  • Support for ANTHROPIC_MODEL for consistency with Claude Code
  • Custom display names for models
  • More flexible model identification system

The token file mechanism at /tmp/claudish-tokens-{PORT}.json is clever and allows the status line to display real-time token usage without modifying the proxy or Claude Code itself.