Skip to content

Commit d022ea3

Browse files
committed
feat: add demo video, env var config, default to claude-opus-4-6
- Support ANTHROPIC_BASE_URL, OPENAI_BASE_URL, MINI_CLAUDE_MODEL env vars - Default model changed to claude-opus-4-6 - Add demo video to README - Update quick start with clear API configuration docs
1 parent e2c4c68 commit d022ea3

5 files changed

Lines changed: 121 additions & 27 deletions

File tree

README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
**~1300 行 TypeScript 复现 Claude Code 的核心能力。** 这不是 demo,而是一份分步教程——每一步都对照 Claude Code 真实源码讲解"它怎么做的 → 我们怎么简化的",帮你彻底理解 coding agent 的工作原理。
88

9+
<https://github.com/Windy3f3f3f3f/claude-code-from-scratch/raw/main/demo.mp4>
10+
911
## 分步教程
1012

1113
**[在线阅读 →](https://windy3f3f3f3f.github.io/claude-code-from-scratch/)**
@@ -29,18 +31,48 @@
2931
git clone https://github.com/Windy3f3f3f3f/claude-code-from-scratch.git
3032
cd claude-code-from-scratch
3133
npm install && npm run build
32-
export ANTHROPIC_API_KEY="your-key-here"
34+
```
35+
36+
### 配置 API
37+
38+
支持两种后端,通过环境变量自动识别:
39+
40+
**方式一:Anthropic 格式(推荐)**
41+
42+
```bash
43+
export ANTHROPIC_API_KEY="sk-ant-xxx"
44+
# 可选:使用代理
45+
export ANTHROPIC_BASE_URL="https://aihubmix.com"
46+
```
47+
48+
**方式二:OpenAI 兼容格式**
3349

34-
npm start # 交互式 REPL 模式
35-
npm start -- "修复 src/app.ts 的 bug" # 单次执行模式
36-
npm start -- --yolo "跑所有测试并修复" # 跳过安全确认
37-
npm start -- --resume # 恢复上次会话
50+
```bash
51+
export OPENAI_API_KEY="sk-xxx"
52+
export OPENAI_BASE_URL="https://api.openai.com/v1"
53+
```
54+
55+
默认模型为 `claude-opus-4-6`,可通过环境变量或命令行参数自定义:
56+
57+
```bash
58+
export MINI_CLAUDE_MODEL="claude-sonnet-4-6" # 环境变量方式
59+
npm start -- --model gpt-4o # 命令行方式(优先级更高)
60+
```
61+
62+
### 运行
63+
64+
```bash
65+
npm start # 交互式 REPL 模式(推荐)
66+
npm start -- --resume # 恢复上次会话继续对话
67+
npm start -- --yolo # 跳过安全确认(危险命令自动执行)
3868
```
3969

40-
### 使用 OpenAI 兼容 API
70+
全局安装后可在任意目录使用:
4171

4272
```bash
43-
npm start -- --api-base https://aihubmix.com/v1 --api-key sk-xxx --model gpt-4o "hello"
73+
npm link # 全局安装
74+
cd ~/your-project
75+
mini-claude # 直接启动
4476
```
4577

4678
### REPL 命令

README_EN.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
**Recreate Claude Code's core capabilities in ~1300 lines of TypeScript.** This isn't a demo — it's a step-by-step tutorial where each chapter compares Claude Code's real source with our simplified implementation, helping you truly understand how coding agents work.
88

9+
<https://github.com/Windy3f3f3f3f/claude-code-from-scratch/raw/main/demo.mp4>
10+
911
## Step-by-Step Tutorial
1012

1113
**[Read Online →](https://windy3f3f3f3f.github.io/claude-code-from-scratch/)**
@@ -29,18 +31,48 @@
2931
git clone https://github.com/Windy3f3f3f3f/claude-code-from-scratch.git
3032
cd claude-code-from-scratch
3133
npm install && npm run build
32-
export ANTHROPIC_API_KEY="your-key-here"
34+
```
35+
36+
### API Configuration
37+
38+
Two backends supported, auto-detected via environment variables:
39+
40+
**Option 1: Anthropic Format (Recommended)**
41+
42+
```bash
43+
export ANTHROPIC_API_KEY="sk-ant-xxx"
44+
# Optional: use a proxy
45+
export ANTHROPIC_BASE_URL="https://aihubmix.com"
46+
```
47+
48+
**Option 2: OpenAI-Compatible Format**
3349

34-
npm start # Interactive REPL
35-
npm start -- "fix the bug in src/app.ts" # One-shot mode
36-
npm start -- --yolo "run tests and fix" # Skip confirmations
37-
npm start -- --resume # Restore last session
50+
```bash
51+
export OPENAI_API_KEY="sk-xxx"
52+
export OPENAI_BASE_URL="https://api.openai.com/v1"
53+
```
54+
55+
Default model is `claude-opus-4-6`. Customize via env var or CLI flag:
56+
57+
```bash
58+
export MINI_CLAUDE_MODEL="claude-sonnet-4-6" # env var
59+
npm start -- --model gpt-4o # CLI flag (higher priority)
60+
```
61+
62+
### Run
63+
64+
```bash
65+
npm start # Interactive REPL mode (recommended)
66+
npm start -- --resume # Resume last session
67+
npm start -- --yolo # Skip safety confirmations
3868
```
3969

40-
### OpenAI-Compatible API
70+
Install globally to use from any directory:
4171

4272
```bash
43-
npm start -- --api-base https://aihubmix.com/v1 --api-key sk-xxx --model gpt-4o "hello"
73+
npm link # Global install
74+
cd ~/your-project
75+
mini-claude # Launch directly
4476
```
4577

4678
### REPL Commands

demo.mp4

843 KB
Binary file not shown.

src/agent.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ async function withRetry<T>(
4949
// ─── Model context windows ──────────────────────────────────
5050

5151
const MODEL_CONTEXT: Record<string, number> = {
52+
"claude-opus-4-6": 200000,
53+
"claude-sonnet-4-6": 200000,
5254
"claude-sonnet-4-20250514": 200000,
53-
"claude-haiku-4-20250414": 200000,
55+
"claude-haiku-4-5-20251001": 200000,
5456
"claude-opus-4-20250514": 200000,
5557
"gpt-4o": 128000,
5658
"gpt-4o-mini": 128000,
@@ -78,7 +80,8 @@ function toOpenAITools(): OpenAI.ChatCompletionTool[] {
7880
interface AgentOptions {
7981
yolo?: boolean;
8082
model?: string;
81-
apiBase?: string;
83+
apiBase?: string; // OpenAI-compatible base URL
84+
anthropicBaseURL?: string; // Anthropic base URL (e.g. proxy)
8285
apiKey?: string;
8386
thinking?: boolean;
8487
}
@@ -111,7 +114,7 @@ export class Agent {
111114
constructor(options: AgentOptions = {}) {
112115
this.yolo = options.yolo || false;
113116
this.thinking = options.thinking || false;
114-
this.model = options.model || "claude-sonnet-4-20250514";
117+
this.model = options.model || "claude-opus-4-6";
115118
this.useOpenAI = !!options.apiBase;
116119
this.systemPrompt = buildSystemPrompt();
117120
this.effectiveWindow = getContextWindow(this.model) - 20000;
@@ -125,7 +128,10 @@ export class Agent {
125128
});
126129
this.openaiMessages.push({ role: "system", content: this.systemPrompt });
127130
} else {
128-
this.anthropicClient = new Anthropic({ apiKey: options.apiKey });
131+
this.anthropicClient = new Anthropic({
132+
apiKey: options.apiKey,
133+
...(options.anthropicBaseURL ? { baseURL: options.anthropicBaseURL } : {}),
134+
});
129135
}
130136
}
131137

src/cli.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function parseArgs(): ParsedArgs {
1919
const args = process.argv.slice(2);
2020
let yolo = false;
2121
let thinking = false;
22-
let model = "claude-sonnet-4-20250514";
22+
let model = process.env.MINI_CLAUDE_MODEL || "claude-opus-4-6";
2323
let apiBase: string | undefined;
2424
let apiKey: string | undefined;
2525
let resume = false;
@@ -45,7 +45,7 @@ Usage: mini-claude [options] [prompt]
4545
Options:
4646
--yolo, -y Skip all confirmation prompts
4747
--thinking Enable extended thinking (Anthropic only)
48-
--model, -m Model to use (default: claude-sonnet-4-20250514)
48+
--model, -m Model to use (default: claude-opus-4-6, or MINI_CLAUDE_MODEL env)
4949
--api-base URL Use OpenAI-compatible API endpoint
5050
--api-key KEY API key for the specified endpoint
5151
--resume Resume the last session
@@ -164,21 +164,45 @@ async function runRepl(agent: Agent) {
164164
async function main() {
165165
const { yolo, model, apiBase, apiKey, prompt, resume, thinking } = parseArgs();
166166

167-
// Determine API key: --api-key flag > env vars
168-
const resolvedApiKey =
169-
apiKey ||
170-
(apiBase ? process.env.OPENAI_API_KEY : process.env.ANTHROPIC_API_KEY);
167+
// Resolve API config: CLI flags > env vars
168+
// Priority: --api-base/--api-key flags first, then env vars
169+
let resolvedApiBase = apiBase;
170+
let resolvedApiKey = apiKey;
171+
let resolvedUseOpenAI = !!apiBase;
172+
173+
if (!resolvedApiKey) {
174+
// Check OPENAI env vars first (if OPENAI_BASE_URL is set, use OpenAI format)
175+
if (process.env.OPENAI_API_KEY && process.env.OPENAI_BASE_URL) {
176+
resolvedApiKey = process.env.OPENAI_API_KEY;
177+
resolvedApiBase = resolvedApiBase || process.env.OPENAI_BASE_URL;
178+
resolvedUseOpenAI = true;
179+
} else if (process.env.ANTHROPIC_API_KEY) {
180+
resolvedApiKey = process.env.ANTHROPIC_API_KEY;
181+
resolvedApiBase = resolvedApiBase || process.env.ANTHROPIC_BASE_URL;
182+
resolvedUseOpenAI = false;
183+
} else if (process.env.OPENAI_API_KEY) {
184+
resolvedApiKey = process.env.OPENAI_API_KEY;
185+
resolvedApiBase = resolvedApiBase || process.env.OPENAI_BASE_URL;
186+
resolvedUseOpenAI = true;
187+
}
188+
}
171189

172190
if (!resolvedApiKey) {
173-
const envVar = apiBase ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY";
174191
printError(
175192
`API key is required.\n` +
176-
` Use --api-key flag or set ${envVar} environment variable.`
193+
` Set ANTHROPIC_API_KEY (+ optional ANTHROPIC_BASE_URL) for Anthropic format,\n` +
194+
` or OPENAI_API_KEY + OPENAI_BASE_URL for OpenAI-compatible format,\n` +
195+
` or use --api-key / --api-base flags.`
177196
);
178197
process.exit(1);
179198
}
180199

181-
const agent = new Agent({ yolo, model, apiBase, apiKey: resolvedApiKey, thinking });
200+
const agent = new Agent({
201+
yolo, model, thinking,
202+
apiBase: resolvedUseOpenAI ? resolvedApiBase : undefined,
203+
anthropicBaseURL: !resolvedUseOpenAI ? resolvedApiBase : undefined,
204+
apiKey: resolvedApiKey,
205+
});
182206

183207
// Resume session if requested
184208
if (resume) {

0 commit comments

Comments
 (0)