diff --git a/.env.example b/.env.example
index 66c8475e..389caeaa 100644
--- a/.env.example
+++ b/.env.example
@@ -1,11 +1,51 @@
-ANTHROPIC_AUTH_TOKEN=your_token_here
-ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
-ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
-ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
-API_TIMEOUT_MS=3000000
-CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+# ============================================================
+# MiniMax(直连 Anthropic 兼容接口)
+# ============================================================
+# ANTHROPIC_AUTH_TOKEN=your_token_here
+# ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
+# ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
+# ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
+# ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
+# ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
+# API_TIMEOUT_MS=3000000
+
+# ============================================================
+# OpenAI(通过 LiteLLM 代理)
+# 先启动: litellm --config litellm_config.yaml --port 4000
+# ============================================================
+# ANTHROPIC_AUTH_TOKEN=sk-anything
+# ANTHROPIC_BASE_URL=http://localhost:4000
+# ANTHROPIC_MODEL=gpt-4o
+# ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
+# ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
+# ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
+# API_TIMEOUT_MS=3000000
+
+# ============================================================
+# DeepSeek(通过 LiteLLM 代理)
+# 先启动: litellm --config litellm_config.yaml --port 4000
+# ============================================================
+# ANTHROPIC_AUTH_TOKEN=sk-anything
+# ANTHROPIC_BASE_URL=http://localhost:4000
+# ANTHROPIC_MODEL=deepseek-chat
+# ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat
+# ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat
+# ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat
+# API_TIMEOUT_MS=3000000
+
+# ============================================================
+# OpenRouter(直连 Anthropic 兼容接口)
+# ============================================================
+# ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
+# ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
+# ANTHROPIC_MODEL=openai/gpt-4o
+# ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
+# ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
+# ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
+
+# ============================================================
+# 通用设置(建议始终开启)
+# ============================================================
DISABLE_TELEMETRY=1
# Azure OpenAI (Codex)
@@ -14,3 +54,4 @@ AZURE_OPENAI_BASE_URL=https://your-resource.cognitiveservices.azure.com
AZURE_OPENAI_API_VERSION=2025-04-01-preview
AZURE_OPENAI_API_KEY=your_azure_openai_key
AZURE_OPENAI_CODEX_DEPLOYMENT=your_codex_deployment
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
diff --git a/README.en.md b/README.en.md
index dae946f9..c2101a88 100644
--- a/README.en.md
+++ b/README.en.md
@@ -10,12 +10,26 @@ A **locally runnable version** repaired from the leaked Claude Code source, with
+## 目录
+
+- [功能](#功能)
+- [架构概览](#架构概览)
+- [快速开始](#快速开始)
+- [环境变量说明](#环境变量说明)
+- [降级模式](#降级模式)
+- [常见问题](#常见问题)
+- [相对于原始泄露源码的修复](#相对于原始泄露源码的修复)
+- [项目结构](#项目结构)
+- [技术栈](#技术栈)
+
+---
+
## 功能
- 完整的 Ink TUI 交互界面(与官方 Claude Code 一致)
- `--print` 无头模式(脚本/CI 场景)
- 支持 MCP 服务器、插件、Skills
-- 支持自定义 API 端点和模型
+- 支持自定义 API 端点和模型([第三方模型使用指南](docs/third-party-models.md))
- 降级 Recovery CLI 模式
---
@@ -87,7 +101,7 @@ bun install
cp .env.example .env
```
-编辑 `.env`:
+编辑 `.env`(以下示例使用 [MiniMax](https://platform.minimaxi.com/subscribe/token-plan?code=1TG2Cseab2&source=link) 作为 API 提供商,也可替换为其他兼容服务):
```env
# API 认证(二选一)
@@ -111,6 +125,20 @@ DISABLE_TELEMETRY=1
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
```
+> **提示**:除了 `.env` 文件,你也可以通过 `~/.claude/settings.json` 的 `env` 字段配置环境变量。这与官方 Claude Code 的配置方式一致:
+>
+> ```json
+> {
+> "env": {
+> "ANTHROPIC_AUTH_TOKEN": "sk-xxx",
+> "ANTHROPIC_BASE_URL": "https://api.minimaxi.com/anthropic",
+> "ANTHROPIC_MODEL": "MiniMax-M2.7-highspeed"
+> }
+> }
+> ```
+>
+> 配置优先级:环境变量 > `.env` 文件 > `~/.claude/settings.json`
+
### 4. 启动
#### macOS / Linux
@@ -271,6 +299,42 @@ src/
---
+## 常见问题
+
+### Q: `undefined is not an object (evaluating 'usage.input_tokens')`
+
+**原因**:`ANTHROPIC_BASE_URL` 配置不正确,API 端点返回的不是 Anthropic 协议格式的 JSON,而是 HTML 页面或其他格式。
+
+本项目使用 **Anthropic Messages API 协议**,`ANTHROPIC_BASE_URL` 必须指向一个兼容 Anthropic `/v1/messages` 接口的端点。Anthropic SDK 会自动在 base URL 后面拼接 `/v1/messages`,所以:
+
+- MiniMax:`ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic` ✅
+- OpenRouter:`ANTHROPIC_BASE_URL=https://openrouter.ai/api` ✅
+- OpenRouter 错误写法:`ANTHROPIC_BASE_URL=https://openrouter.ai/anthropic` ❌(返回 HTML)
+
+如果你的模型供应商只支持 OpenAI 协议,需要通过 LiteLLM 等代理做协议转换,详见 [第三方模型使用指南](docs/third-party-models.md)。
+
+### Q: `Cannot find package 'bundle'`
+
+```
+error: Cannot find package 'bundle' from '.../claude-code-haha/src/entrypoints/cli.tsx'
+```
+
+**原因**:Bun 版本过低,不支持项目所需的 `bun:bundle` 等内置模块。
+
+**解决**:升级 Bun 到最新版本:
+
+```bash
+bun upgrade
+```
+
+### Q: 怎么接入 OpenAI / DeepSeek / Ollama 等非 Anthropic 模型?
+
+本项目只支持 Anthropic 协议。如果模型供应商不直接支持 Anthropic 协议,需要用 [LiteLLM](https://github.com/BerriAI/litellm) 等代理做协议转换(OpenAI → Anthropic)。
+
+详细配置步骤请参考:[第三方模型使用指南](docs/third-party-models.md)
+
+---
+
## Disclaimer
本仓库基于 2026-03-31 从 Anthropic npm registry 泄露的 Claude Code 源码。所有原始源码版权归 [Anthropic](https://www.anthropic.com) 所有。仅供学习和研究用途。
diff --git a/docs/third-party-models.en.md b/docs/third-party-models.en.md
new file mode 100644
index 00000000..66416c50
--- /dev/null
+++ b/docs/third-party-models.en.md
@@ -0,0 +1,254 @@
+# Using Third-Party Models (OpenAI / DeepSeek / Local Models)
+
+This project communicates with LLMs via the Anthropic protocol. By using a protocol translation proxy, you can use any model including OpenAI, DeepSeek, Ollama, etc.
+
+## How It Works
+
+```
+claude-code-haha ──Anthropic protocol──▶ LiteLLM Proxy ──OpenAI protocol──▶ Target Model API
+ (translation)
+```
+
+This project sends Anthropic Messages API requests. The LiteLLM proxy automatically translates them to OpenAI Chat Completions API format and forwards them to the target model.
+
+---
+
+## Option 1: LiteLLM Proxy (Recommended)
+
+[LiteLLM](https://github.com/BerriAI/litellm) is a unified proxy gateway supporting 100+ LLMs (41k+ GitHub Stars), with native support for receiving Anthropic protocol requests.
+
+### 1. Install LiteLLM
+
+```bash
+pip install 'litellm[proxy]'
+```
+
+### 2. Create Configuration File
+
+Create `litellm_config.yaml`:
+
+#### Using OpenAI Models
+
+```yaml
+model_list:
+ - model_name: gpt-4o
+ litellm_params:
+ model: openai/gpt-4o
+ api_key: os.environ/OPENAI_API_KEY
+
+litellm_settings:
+ drop_params: true # Drop Anthropic-specific params (thinking, etc.)
+```
+
+#### Using DeepSeek Models
+
+```yaml
+model_list:
+ - model_name: deepseek-chat
+ litellm_params:
+ model: deepseek/deepseek-chat
+ api_key: os.environ/DEEPSEEK_API_KEY
+ api_base: https://api.deepseek.com
+
+litellm_settings:
+ drop_params: true
+```
+
+#### Using Ollama Local Models
+
+```yaml
+model_list:
+ - model_name: llama3
+ litellm_params:
+ model: ollama/llama3
+ api_base: http://localhost:11434
+
+litellm_settings:
+ drop_params: true
+```
+
+#### Using Multiple Models (switchable after startup)
+
+```yaml
+model_list:
+ - model_name: gpt-4o
+ litellm_params:
+ model: openai/gpt-4o
+ api_key: os.environ/OPENAI_API_KEY
+
+ - model_name: deepseek-chat
+ litellm_params:
+ model: deepseek/deepseek-chat
+ api_key: os.environ/DEEPSEEK_API_KEY
+ api_base: https://api.deepseek.com
+
+ - model_name: llama3
+ litellm_params:
+ model: ollama/llama3
+ api_base: http://localhost:11434
+
+litellm_settings:
+ drop_params: true
+```
+
+### 3. Start the Proxy
+
+```bash
+# Set your target model's API key
+export OPENAI_API_KEY=sk-xxx
+# or
+export DEEPSEEK_API_KEY=sk-xxx
+
+# Start the proxy
+litellm --config litellm_config.yaml --port 4000
+```
+
+The proxy will listen on `http://localhost:4000` and expose an Anthropic-compatible `/v1/messages` endpoint.
+
+### 4. Configure This Project
+
+Choose one of two configuration methods:
+
+#### Method A: Via `.env` File
+
+```env
+ANTHROPIC_AUTH_TOKEN=sk-anything
+ANTHROPIC_BASE_URL=http://localhost:4000
+ANTHROPIC_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
+API_TIMEOUT_MS=3000000
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+#### Method B: Via `~/.claude/settings.json`
+
+```json
+{
+ "env": {
+ "ANTHROPIC_AUTH_TOKEN": "sk-anything",
+ "ANTHROPIC_BASE_URL": "http://localhost:4000",
+ "ANTHROPIC_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-4o",
+ "API_TIMEOUT_MS": "3000000",
+ "DISABLE_TELEMETRY": "1",
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
+ }
+}
+```
+
+> **Note**: The `ANTHROPIC_AUTH_TOKEN` value can be any string when using the LiteLLM proxy (LiteLLM uses its own configured key for forwarding), unless you've set a `master_key` on the LiteLLM side.
+
+### 5. Start and Verify
+
+```bash
+./bin/claude-haha
+```
+
+If everything is configured correctly, you should see the normal chat interface, with your configured target model handling the requests.
+
+---
+
+## Option 2: Direct Connection to Anthropic-Compatible Services
+
+Some third-party services directly support the Anthropic Messages API, no proxy needed:
+
+### OpenRouter
+
+```env
+ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
+ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
+ANTHROPIC_MODEL=openai/gpt-4o
+ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
+ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
+ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+### MiniMax (pre-configured in .env.example)
+
+```env
+ANTHROPIC_AUTH_TOKEN=your_token_here
+ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
+ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
+API_TIMEOUT_MS=3000000
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+---
+
+## Option 3: Other Proxy Tools
+
+The community has built several proxy tools specifically for Claude Code:
+
+| Tool | Description | Link |
+|------|-------------|------|
+| **a2o** | Anthropic → OpenAI single binary, zero dependencies | [Twitter](https://x.com/mantou543/status/2018846154855940200) |
+| **Empero Proxy** | Full Anthropic Messages API to OpenAI translation | [Twitter](https://x.com/EmperoAI/status/2036840854065762551) |
+| **Alma** | Client with built-in OpenAI → Anthropic proxy | [Twitter](https://x.com/yetone/status/2003508782127833332) |
+| **Chutes** | Docker container supporting 60+ open-source models | [Twitter](https://x.com/chutes_ai/status/2027039742915662232) |
+
+---
+
+## Known Limitations
+
+### 1. `drop_params: true` Is Essential
+
+This project sends Anthropic-specific parameters (e.g., `thinking`, `cache_control`) that don't exist in the OpenAI API. You must set `drop_params: true` in the LiteLLM config, otherwise requests will fail.
+
+### 2. Extended Thinking Unavailable
+
+Anthropic's Extended Thinking is a proprietary feature not supported by other models. It is automatically disabled when using third-party models.
+
+### 3. Prompt Caching Unavailable
+
+`cache_control` is an Anthropic-specific feature. Prompt caching won't work with third-party models (but won't cause errors — it's silently ignored by `drop_params`).
+
+### 4. Tool Calling Compatibility
+
+This project heavily uses tool calling (tool_use). LiteLLM automatically translates Anthropic's tool_use format to OpenAI's function_calling format. This works in most cases, but some complex tool calls may have compatibility issues. If you encounter problems, try using a more capable model (e.g., GPT-4o).
+
+### 5. Telemetry and Non-Essential Requests
+
+Configure these environment variables to avoid unnecessary network requests:
+```
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+---
+
+## FAQ
+
+### Q: LiteLLM proxy returns `/v1/responses` not found?
+
+Some OpenAI-compatible services only support `/v1/chat/completions`. Add this to your LiteLLM config:
+
+```yaml
+litellm_settings:
+ use_chat_completions_url_for_anthropic_messages: true
+```
+
+### Q: What's the difference between `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN`?
+
+- `ANTHROPIC_API_KEY` → Sent via `x-api-key` header
+- `ANTHROPIC_AUTH_TOKEN` → Sent via `Authorization: Bearer` header
+
+LiteLLM proxy accepts Bearer Token format by default, so `ANTHROPIC_AUTH_TOKEN` is recommended.
+
+### Q: Can I configure multiple models?
+
+Yes. Define multiple `model_name` entries in `litellm_config.yaml`, then switch by changing the `ANTHROPIC_MODEL` value.
+
+### Q: Local Ollama models don't work well?
+
+This project's system prompts and tool calls require strong model capabilities. Use larger models (e.g., Llama 3 70B+, Qwen 72B+). Smaller models may fail to handle tool calling correctly.
diff --git a/docs/third-party-models.md b/docs/third-party-models.md
new file mode 100644
index 00000000..1bc746a6
--- /dev/null
+++ b/docs/third-party-models.md
@@ -0,0 +1,254 @@
+# 使用第三方模型(OpenAI / DeepSeek / 本地模型)
+
+本项目基于 Anthropic 协议与 LLM 通信。通过协议转换代理,可以使用 OpenAI、DeepSeek、Ollama 等任意模型。
+
+## 原理
+
+```
+claude-code-haha ──Anthropic协议──▶ LiteLLM Proxy ──OpenAI协议──▶ 目标模型 API
+ (协议转换)
+```
+
+本项目发出 Anthropic Messages API 请求,LiteLLM 代理将其自动转换为 OpenAI Chat Completions API 格式并转发给目标模型。
+
+---
+
+## 方式一:LiteLLM 代理(推荐)
+
+[LiteLLM](https://github.com/BerriAI/litellm) 是一个支持 100+ LLM 的统一代理网关(41k+ GitHub Stars),原生支持接收 Anthropic 协议请求。
+
+### 1. 安装 LiteLLM
+
+```bash
+pip install 'litellm[proxy]'
+```
+
+### 2. 创建配置文件
+
+新建 `litellm_config.yaml`:
+
+#### 使用 OpenAI 模型
+
+```yaml
+model_list:
+ - model_name: gpt-4o
+ litellm_params:
+ model: openai/gpt-4o
+ api_key: os.environ/OPENAI_API_KEY
+
+litellm_settings:
+ drop_params: true # 丢弃 Anthropic 专有参数(thinking 等)
+```
+
+#### 使用 DeepSeek 模型
+
+```yaml
+model_list:
+ - model_name: deepseek-chat
+ litellm_params:
+ model: deepseek/deepseek-chat
+ api_key: os.environ/DEEPSEEK_API_KEY
+ api_base: https://api.deepseek.com
+
+litellm_settings:
+ drop_params: true
+```
+
+#### 使用 Ollama 本地模型
+
+```yaml
+model_list:
+ - model_name: llama3
+ litellm_params:
+ model: ollama/llama3
+ api_base: http://localhost:11434
+
+litellm_settings:
+ drop_params: true
+```
+
+#### 使用多个模型(可在启动后切换)
+
+```yaml
+model_list:
+ - model_name: gpt-4o
+ litellm_params:
+ model: openai/gpt-4o
+ api_key: os.environ/OPENAI_API_KEY
+
+ - model_name: deepseek-chat
+ litellm_params:
+ model: deepseek/deepseek-chat
+ api_key: os.environ/DEEPSEEK_API_KEY
+ api_base: https://api.deepseek.com
+
+ - model_name: llama3
+ litellm_params:
+ model: ollama/llama3
+ api_base: http://localhost:11434
+
+litellm_settings:
+ drop_params: true
+```
+
+### 3. 启动代理
+
+```bash
+# 设置目标模型的 API Key
+export OPENAI_API_KEY=sk-xxx
+# 或
+export DEEPSEEK_API_KEY=sk-xxx
+
+# 启动代理
+litellm --config litellm_config.yaml --port 4000
+```
+
+代理启动后会在 `http://localhost:4000` 监听,并暴露 Anthropic 兼容的 `/v1/messages` 端点。
+
+### 4. 配置本项目
+
+有两种配置方式,任选其一:
+
+#### 方式 A:通过 `.env` 文件
+
+```env
+ANTHROPIC_AUTH_TOKEN=sk-anything
+ANTHROPIC_BASE_URL=http://localhost:4000
+ANTHROPIC_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-4o
+ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-4o
+API_TIMEOUT_MS=3000000
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+#### 方式 B:通过 `~/.claude/settings.json`
+
+```json
+{
+ "env": {
+ "ANTHROPIC_AUTH_TOKEN": "sk-anything",
+ "ANTHROPIC_BASE_URL": "http://localhost:4000",
+ "ANTHROPIC_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-4o",
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "gpt-4o",
+ "API_TIMEOUT_MS": "3000000",
+ "DISABLE_TELEMETRY": "1",
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
+ }
+}
+```
+
+> **说明**:`ANTHROPIC_AUTH_TOKEN` 的值在使用 LiteLLM 代理时可以是任意字符串(LiteLLM 会用自己配置的 key 转发),除非你在 LiteLLM 端设置了 `master_key` 校验。
+
+### 5. 启动并验证
+
+```bash
+./bin/claude-haha
+```
+
+如果一切正常,你应该能看到正常的对话界面,实际调用的是你配置的目标模型。
+
+---
+
+## 方式二:直连兼容 Anthropic 协议的第三方服务
+
+部分第三方服务直接兼容 Anthropic Messages API,无需额外代理:
+
+### OpenRouter
+
+```env
+ANTHROPIC_AUTH_TOKEN=sk-or-v1-xxx
+ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
+ANTHROPIC_MODEL=openai/gpt-4o
+ANTHROPIC_DEFAULT_SONNET_MODEL=openai/gpt-4o
+ANTHROPIC_DEFAULT_HAIKU_MODEL=openai/gpt-4o-mini
+ANTHROPIC_DEFAULT_OPUS_MODEL=openai/gpt-4o
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+### MiniMax(已在 .env.example 中配置)
+
+```env
+ANTHROPIC_AUTH_TOKEN=your_token_here
+ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
+ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
+ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
+API_TIMEOUT_MS=3000000
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+---
+
+## 方式三:其他代理工具
+
+社区还有一些专门为 Claude Code 做的代理工具:
+
+| 工具 | 说明 | 链接 |
+|------|------|------|
+| **a2o** | Anthropic → OpenAI 单二进制文件,零依赖 | [Twitter](https://x.com/mantou543/status/2018846154855940200) |
+| **Empero Proxy** | 完整的 Anthropic Messages API 转 OpenAI 代理 | [Twitter](https://x.com/EmperoAI/status/2036840854065762551) |
+| **Alma** | 内置 OpenAI → Anthropic 转换代理的客户端 | [Twitter](https://x.com/yetone/status/2003508782127833332) |
+| **Chutes** | Docker 容器,支持 60+ 开源模型 | [Twitter](https://x.com/chutes_ai/status/2027039742915662232) |
+
+---
+
+## 注意事项与已知限制
+
+### 1. `drop_params: true` 很重要
+
+本项目会发送 Anthropic 专有参数(如 `thinking`、`cache_control`),这些参数在 OpenAI API 中不存在。LiteLLM 配置中必须设置 `drop_params: true`,否则请求会报错。
+
+### 2. Extended Thinking 不可用
+
+Anthropic 的 Extended Thinking 功能是专有特性,其他模型不支持。使用第三方模型时此功能自动失效。
+
+### 3. Prompt Caching 不可用
+
+`cache_control` 是 Anthropic 专有功能。使用第三方模型时,prompt caching 不会生效(但不会导致报错,会被 `drop_params` 忽略)。
+
+### 4. 工具调用兼容性
+
+本项目大量使用工具调用(tool_use),LiteLLM 会自动转换 Anthropic tool_use 格式到 OpenAI function_calling 格式。大部分情况下可以正常工作,但某些复杂工具调用可能存在兼容性问题。如遇问题,建议使用能力较强的模型(如 GPT-4o)。
+
+### 5. 遥测和非必要网络请求
+
+建议配置以下环境变量以避免不必要的网络请求:
+```
+DISABLE_TELEMETRY=1
+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
+```
+
+---
+
+## FAQ
+
+### Q: LiteLLM 代理报错 `/v1/responses` 找不到?
+
+部分 OpenAI 兼容服务只支持 `/v1/chat/completions`。在 LiteLLM 配置中添加:
+
+```yaml
+litellm_settings:
+ use_chat_completions_url_for_anthropic_messages: true
+```
+
+### Q: `ANTHROPIC_API_KEY` 和 `ANTHROPIC_AUTH_TOKEN` 有什么区别?
+
+- `ANTHROPIC_API_KEY` → 通过 `x-api-key` 请求头发送
+- `ANTHROPIC_AUTH_TOKEN` → 通过 `Authorization: Bearer` 请求头发送
+
+LiteLLM 代理默认接受 Bearer Token 格式,建议使用 `ANTHROPIC_AUTH_TOKEN`。
+
+### Q: 可以同时配置多个模型吗?
+
+可以。在 `litellm_config.yaml` 中配置多个 `model_name`,然后通过修改 `ANTHROPIC_MODEL` 切换。
+
+### Q: 本地 Ollama 模型效果不好怎么办?
+
+本项目的系统提示和工具调用对模型能力要求较高。建议使用参数量较大的模型(如 Llama 3 70B+, Qwen 72B+),小模型可能无法正确处理工具调用。
diff --git a/src/buddy/CompanionSprite.tsx b/src/buddy/CompanionSprite.tsx
index f7f1f720..e60afa68 100644
--- a/src/buddy/CompanionSprite.tsx
+++ b/src/buddy/CompanionSprite.tsx
@@ -1,5 +1,4 @@
import { c as _c } from "react/compiler-runtime";
-import { feature } from 'bun:bundle';
import figures from 'figures';
import React, { useEffect, useRef, useState } from 'react';
import { useTerminalSize } from '../hooks/useTerminalSize.js';
@@ -165,7 +164,6 @@ function spriteColWidth(nameWidth: number): number {
// Narrow terminals: 0 — REPL.tsx stacks the one-liner on its own row
// (above input in fullscreen, below in scrollback), so no reservation.
export function companionReservedColumns(terminalColumns: number, speaking: boolean): number {
- if (!feature('BUDDY')) return 0;
const companion = getCompanion();
if (!companion || getGlobalConfig().companionMuted) return 0;
if (terminalColumns < MIN_COLS_FOR_FULL_SPRITE) return 0;
@@ -212,7 +210,6 @@ export function CompanionSprite(): React.ReactNode {
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps -- tick intentionally captured at reaction-change, not tracked
}, [reaction, setAppState]);
- if (!feature('BUDDY')) return null;
const companion = getCompanion();
if (!companion || getGlobalConfig().companionMuted) return null;
const color = RARITY_COLORS[companion.rarity];
@@ -337,7 +334,7 @@ export function CompanionFloatingBubble() {
t3 = $[4];
}
useEffect(t2, t3);
- if (!feature("BUDDY") || !reaction) {
+ if (!reaction) {
return null;
}
const companion = getCompanion();
diff --git a/src/buddy/observer.ts b/src/buddy/observer.ts
new file mode 100644
index 00000000..ede25145
--- /dev/null
+++ b/src/buddy/observer.ts
@@ -0,0 +1,67 @@
+import type { Message } from '../types/message.js'
+import { getCompanion } from './companion.js'
+import { getGlobalConfig } from '../utils/config.js'
+
+// Simple companion observer: picks a reaction based on the last assistant message.
+// This is a lightweight placeholder that generates fun reactions without an LLM call.
+
+const DEBUGGING_QUIPS = [
+ 'Found it!',
+ 'Interesting...',
+ 'Have you tried rubber duck debugging?',
+ 'Stack trace time!',
+ 'I see what happened.',
+]
+
+const GENERAL_QUIPS = [
+ 'Looking good!',
+ 'Keep it up!',
+ 'Nice work!',
+ 'I believe in you!',
+ 'You got this!',
+]
+
+const CODE_QUIPS = [
+ 'Fancy!',
+ 'Clean code!',
+ 'Elegant solution!',
+ 'Ship it!',
+]
+
+function pickQuip(messages: Message[]): string | undefined {
+ const lastAssistant = [...messages].reverse().find(m => m.role === 'assistant')
+ if (!lastAssistant) return undefined
+
+ const content = Array.isArray(lastAssistant.content)
+ ? lastAssistant.content.map(c => (typeof c === 'string' ? c : c.type === 'text' ? c.text : '')).join('')
+ : typeof lastAssistant.content === 'string'
+ ? lastAssistant.content
+ : ''
+
+ if (!content) return undefined
+
+ // Only react occasionally (1 in 5 turns)
+ if (Math.random() > 0.2) return undefined
+
+ const lower = content.toLowerCase()
+ if (lower.includes('error') || lower.includes('bug') || lower.includes('fix') || lower.includes('debug')) {
+ return DEBUGGING_QUIPS[Math.floor(Math.random() * DEBUGGING_QUIPS.length)]
+ }
+ if (lower.includes('function') || lower.includes('class') || lower.includes('const') || lower.includes('```')) {
+ return CODE_QUIPS[Math.floor(Math.random() * CODE_QUIPS.length)]
+ }
+ return GENERAL_QUIPS[Math.floor(Math.random() * GENERAL_QUIPS.length)]
+}
+
+export async function fireCompanionObserver(
+ messages: Message[],
+ onReaction: (reaction: string) => void,
+): Promise