claude-coder 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +8 -1
- package/docs/ARCHITECTURE.md +59 -1
- package/package.json +1 -1
- package/src/runner.js +2 -2
- package/src/session.js +4 -3
- package/src/tasks.js +7 -1
- package/src/validator.js +7 -3
package/bin/cli.js
CHANGED
|
@@ -46,6 +46,9 @@ function parseArgs(argv) {
|
|
|
46
46
|
case '--dry-run':
|
|
47
47
|
opts.dryRun = true;
|
|
48
48
|
break;
|
|
49
|
+
case '--view':
|
|
50
|
+
opts.viewMode = true;
|
|
51
|
+
break;
|
|
49
52
|
case '--help':
|
|
50
53
|
case '-h':
|
|
51
54
|
showHelp();
|
|
@@ -78,7 +81,11 @@ async function main() {
|
|
|
78
81
|
switch (command) {
|
|
79
82
|
case 'run': {
|
|
80
83
|
const runner = require('../src/runner');
|
|
81
|
-
|
|
84
|
+
if (opts.viewMode) {
|
|
85
|
+
await runner.view(positional[0] || null, opts);
|
|
86
|
+
} else {
|
|
87
|
+
await runner.run(positional[0] || null, opts);
|
|
88
|
+
}
|
|
82
89
|
break;
|
|
83
90
|
}
|
|
84
91
|
case 'setup': {
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -278,7 +278,65 @@ sequenceDiagram
|
|
|
278
278
|
|
|
279
279
|
---
|
|
280
280
|
|
|
281
|
-
## 9.
|
|
281
|
+
## 9. Claude Agent SDK V1/V2 对比与迁移计划
|
|
282
|
+
|
|
283
|
+
当前使用 **V1 稳定 API**(`query()`),V2 为 preview 状态(`unstable_` 前缀)。
|
|
284
|
+
|
|
285
|
+
### V1 vs V2 API 对比
|
|
286
|
+
|
|
287
|
+
| 维度 | V1 `query()` | V2 `send()/stream()` |
|
|
288
|
+
|------|-------------|---------------------|
|
|
289
|
+
| **状态** | 稳定,生产可用 | `unstable_` 前缀,preview |
|
|
290
|
+
| **入口函数** | `query({ prompt, options })` | `unstable_v2_createSession(opts)` / `unstable_v2_prompt()` |
|
|
291
|
+
| **多轮会话** | 需手动管理 AsyncGenerator | `session.send()` + `session.stream()`,更简洁 |
|
|
292
|
+
| **会话恢复** | `options.resume: sessionId` | `unstable_v2_resumeSession(id)` |
|
|
293
|
+
| **Hooks** | `options.hooks: { PreToolUse, PostToolUse, ... }` | 未支持 |
|
|
294
|
+
| **Subagents** | `options.agents: { name: AgentDefinition }` | 未支持 |
|
|
295
|
+
| **Session Fork** | `options.forkSession: true` | 未支持 |
|
|
296
|
+
| **Plugins** | `options.plugins: [{ type, path }]` | 未支持 |
|
|
297
|
+
| **结构化输出** | `options.outputFormat: { type: 'json_schema', schema }` | 支持 |
|
|
298
|
+
| **文件检查点** | `options.enableFileCheckpointing + rewindFiles()` | 未明确 |
|
|
299
|
+
| **Cost Tracking** | `SDKResultMessage.total_cost_usd` | `SDKResultMessage.total_cost_usd` |
|
|
300
|
+
| **权限控制** | `canUseTool`, `permissionMode`, `allowedTools`, `disallowedTools` | 继承 |
|
|
301
|
+
|
|
302
|
+
### 当前实现使用的 V1 特性
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
query({
|
|
306
|
+
prompt,
|
|
307
|
+
options: {
|
|
308
|
+
systemPrompt, // 注入 CLAUDE.md
|
|
309
|
+
allowedTools, // 工具白名单
|
|
310
|
+
permissionMode: 'bypassPermissions',
|
|
311
|
+
allowDangerouslySkipPermissions: true,
|
|
312
|
+
model, // 从 .env 传入
|
|
313
|
+
env, // 环境变量透传
|
|
314
|
+
settingSources: ['project'], // 加载项目 CLAUDE.md
|
|
315
|
+
hooks: { PreToolUse: [...] }, // 实时 spinner 监控
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### V2 迁移条件(等待稳定后)
|
|
321
|
+
|
|
322
|
+
1. V2 去掉 `unstable_` 前缀,正式发布
|
|
323
|
+
2. V2 支持 Hooks(当前项目依赖 PreToolUse 做 spinner 和 activity log)
|
|
324
|
+
3. V2 支持 Subagents(未来可能用于扫描 Agent / 编码 Agent 分离)
|
|
325
|
+
|
|
326
|
+
### 可利用但尚未使用的 V1 特性
|
|
327
|
+
|
|
328
|
+
| 特性 | 说明 | 优先级 |
|
|
329
|
+
|------|------|--------|
|
|
330
|
+
| `maxBudgetUsd` | SDK 内置成本上限,替代自研追踪 | P0 |
|
|
331
|
+
| `effort` | 控制思考深度(`low`/`medium`/`high`/`max`) | P1 |
|
|
332
|
+
| `enableFileCheckpointing` | 文件操作检查点,比 git reset 更精细 | P1 |
|
|
333
|
+
| `outputFormat` | 结构化输出,让 Agent 直接输出 JSON 格式 | P1 |
|
|
334
|
+
| `agents` | 定义子 Agent,不同模型/工具集 | P2 |
|
|
335
|
+
| `betas` | 扩展上下文窗口 | P2 |
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 10. 后续优化方向
|
|
282
340
|
|
|
283
341
|
### P0 — 近期
|
|
284
342
|
|
package/package.json
CHANGED
package/src/runner.js
CHANGED
|
@@ -100,7 +100,7 @@ function appendProgress(entry) {
|
|
|
100
100
|
const p = paths();
|
|
101
101
|
let progress = { sessions: [] };
|
|
102
102
|
if (fs.existsSync(p.progressFile)) {
|
|
103
|
-
try { progress = JSON.parse(fs.readFileSync(p.progressFile, 'utf8')); } catch { /* reset */ }
|
|
103
|
+
try { progress = JSON.parse(fs.readFileSync(p.progressFile, 'utf8').replace(/[\u201c\u201d]/g, '"')); } catch { /* reset */ }
|
|
104
104
|
}
|
|
105
105
|
if (!Array.isArray(progress.sessions)) progress.sessions = [];
|
|
106
106
|
progress.sessions.push(entry);
|
|
@@ -111,7 +111,7 @@ function updateSessionHistory(sessionData, sessionNum) {
|
|
|
111
111
|
const p = paths();
|
|
112
112
|
let sr = { current: null, history: [] };
|
|
113
113
|
if (fs.existsSync(p.sessionResult)) {
|
|
114
|
-
try { sr = JSON.parse(fs.readFileSync(p.sessionResult, 'utf8')); } catch { /* reset */ }
|
|
114
|
+
try { sr = JSON.parse(fs.readFileSync(p.sessionResult, 'utf8').replace(/[\u201c\u201d]/g, '"')); } catch { /* reset */ }
|
|
115
115
|
if (!sr.history && sr.session_result) {
|
|
116
116
|
sr = { current: sr, history: [] };
|
|
117
117
|
}
|
package/src/session.js
CHANGED
|
@@ -62,10 +62,11 @@ function extractResult(messages) {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
function logMessage(message, logStream) {
|
|
65
|
+
function logMessage(message, logStream, indicator) {
|
|
66
66
|
if (message.type === 'assistant' && message.message?.content) {
|
|
67
67
|
for (const block of message.message.content) {
|
|
68
68
|
if (block.type === 'text' && block.text) {
|
|
69
|
+
if (indicator) process.stderr.write('\r\x1b[K');
|
|
69
70
|
process.stdout.write(block.text);
|
|
70
71
|
if (logStream) logStream.write(block.text);
|
|
71
72
|
}
|
|
@@ -106,7 +107,7 @@ async function runCodingSession(sessionNum, opts = {}) {
|
|
|
106
107
|
const collected = [];
|
|
107
108
|
for await (const message of session) {
|
|
108
109
|
collected.push(message);
|
|
109
|
-
logMessage(message, logStream);
|
|
110
|
+
logMessage(message, logStream, indicator);
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
logStream.end();
|
|
@@ -168,7 +169,7 @@ async function runScanSession(requirement, opts = {}) {
|
|
|
168
169
|
const collected = [];
|
|
169
170
|
for await (const message of session) {
|
|
170
171
|
collected.push(message);
|
|
171
|
-
logMessage(message, logStream);
|
|
172
|
+
logMessage(message, logStream, indicator);
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
logStream.end();
|
package/src/tasks.js
CHANGED
|
@@ -13,10 +13,16 @@ const TRANSITIONS = {
|
|
|
13
13
|
done: [],
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
function normalizeJson(text) {
|
|
17
|
+
return text
|
|
18
|
+
.replace(/[\u201c\u201d]/g, '"')
|
|
19
|
+
.replace(/[\u2018\u2019]/g, "'");
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
function loadTasks() {
|
|
17
23
|
const p = paths();
|
|
18
24
|
if (!fs.existsSync(p.tasksFile)) return null;
|
|
19
|
-
return JSON.parse(fs.readFileSync(p.tasksFile, 'utf8'));
|
|
25
|
+
return JSON.parse(normalizeJson(fs.readFileSync(p.tasksFile, 'utf8')));
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
function saveTasks(data) {
|
package/src/validator.js
CHANGED
|
@@ -4,6 +4,10 @@ const fs = require('fs');
|
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
5
|
const { paths, log, getProjectRoot } = require('./config');
|
|
6
6
|
|
|
7
|
+
function normalizeJson(text) {
|
|
8
|
+
return text.replace(/[\u201c\u201d]/g, '"').replace(/[\u2018\u2019]/g, "'");
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
function validateSessionResult() {
|
|
8
12
|
const p = paths();
|
|
9
13
|
|
|
@@ -14,7 +18,7 @@ function validateSessionResult() {
|
|
|
14
18
|
|
|
15
19
|
let data;
|
|
16
20
|
try {
|
|
17
|
-
data = JSON.parse(fs.readFileSync(p.sessionResult, 'utf8'));
|
|
21
|
+
data = JSON.parse(normalizeJson(fs.readFileSync(p.sessionResult, 'utf8')));
|
|
18
22
|
} catch {
|
|
19
23
|
log('error', 'session_result.json JSON 格式错误');
|
|
20
24
|
return { valid: false, fatal: true, reason: 'JSON 格式错误' };
|
|
@@ -86,9 +90,9 @@ function checkTestCoverage() {
|
|
|
86
90
|
if (!fs.existsSync(p.testsFile) || !fs.existsSync(p.sessionResult)) return;
|
|
87
91
|
|
|
88
92
|
try {
|
|
89
|
-
const sr = JSON.parse(fs.readFileSync(p.sessionResult, 'utf8'));
|
|
93
|
+
const sr = JSON.parse(normalizeJson(fs.readFileSync(p.sessionResult, 'utf8')));
|
|
90
94
|
const current = sr.current || sr;
|
|
91
|
-
const tests = JSON.parse(fs.readFileSync(p.testsFile, 'utf8'));
|
|
95
|
+
const tests = JSON.parse(normalizeJson(fs.readFileSync(p.testsFile, 'utf8')));
|
|
92
96
|
|
|
93
97
|
const taskId = current.task_id || '';
|
|
94
98
|
const testCases = tests.test_cases || [];
|