claude-coder 1.7.0 → 1.8.0

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.
Files changed (52) hide show
  1. package/README.md +177 -125
  2. package/bin/cli.js +159 -161
  3. package/package.json +52 -47
  4. package/src/commands/auth.js +294 -0
  5. package/src/commands/setup-modules/helpers.js +105 -0
  6. package/src/commands/setup-modules/index.js +26 -0
  7. package/src/commands/setup-modules/mcp.js +95 -0
  8. package/src/commands/setup-modules/provider.js +261 -0
  9. package/src/commands/setup-modules/safety.js +62 -0
  10. package/src/commands/setup-modules/simplify.js +53 -0
  11. package/src/commands/setup.js +172 -0
  12. package/src/common/assets.js +192 -0
  13. package/src/{config.js → common/config.js} +138 -201
  14. package/src/common/constants.js +57 -0
  15. package/src/{indicator.js → common/indicator.js} +222 -217
  16. package/src/common/interaction.js +170 -0
  17. package/src/common/logging.js +77 -0
  18. package/src/common/sdk.js +51 -0
  19. package/src/{tasks.js → common/tasks.js} +157 -172
  20. package/src/common/utils.js +147 -0
  21. package/src/core/base.js +54 -0
  22. package/src/core/coding.js +55 -0
  23. package/src/core/context.js +132 -0
  24. package/src/core/hooks.js +529 -0
  25. package/src/{init.js → core/init.js} +163 -144
  26. package/src/core/plan.js +318 -0
  27. package/src/core/prompts.js +253 -0
  28. package/src/core/query.js +48 -0
  29. package/src/core/repair.js +58 -0
  30. package/src/{runner.js → core/runner.js} +352 -420
  31. package/src/core/scan.js +89 -0
  32. package/src/core/simplify.js +59 -0
  33. package/src/core/validator.js +138 -0
  34. package/{prompts/ADD_GUIDE.md → templates/addGuide.md} +98 -98
  35. package/templates/addUser.md +26 -0
  36. package/{prompts/CLAUDE.md → templates/agentProtocol.md} +195 -199
  37. package/templates/bash-process.md +5 -0
  38. package/{prompts/coding_user.md → templates/codingUser.md} +31 -23
  39. package/templates/guidance.json +35 -0
  40. package/templates/playwright.md +17 -0
  41. package/templates/requirements.example.md +56 -56
  42. package/{prompts/SCAN_PROTOCOL.md → templates/scanProtocol.md} +118 -118
  43. package/{prompts/scan_user.md → templates/scanUser.md} +17 -17
  44. package/templates/test_rule.md +194 -194
  45. package/prompts/add_user.md +0 -24
  46. package/src/auth.js +0 -245
  47. package/src/hooks.js +0 -160
  48. package/src/prompts.js +0 -295
  49. package/src/scanner.js +0 -62
  50. package/src/session.js +0 -352
  51. package/src/setup.js +0 -579
  52. package/src/validator.js +0 -181
@@ -0,0 +1,261 @@
1
+ 'use strict';
2
+
3
+ const { ask, askChoice, askApiKey } = require('./helpers');
4
+ const { log, COLOR, updateEnvVar } = require('../../common/config');
5
+
6
+ // ── 提供商菜单 ──
7
+
8
+ const PROVIDER_MENU = `
9
+ 请选择模型提供商:
10
+
11
+ 1) 默认 Claude 官方模型,使用系统登录态
12
+ 2) Coding Plan 自建 API,使用推荐的多模型路由配置
13
+ 3) API DeepSeek 或其他 Anthropic 兼容 API
14
+ `;
15
+
16
+ // ── 提供商配置函数 ──
17
+
18
+ async function configureDefault() {
19
+ return {
20
+ lines: [
21
+ '# Claude Coder 模型配置',
22
+ '# 提供商: Claude 官方',
23
+ '',
24
+ 'MODEL_PROVIDER=claude',
25
+ 'API_TIMEOUT_MS=3000000',
26
+ ],
27
+ summary: 'Claude 官方模型',
28
+ };
29
+ }
30
+
31
+ async function configureCodingPlan(rl, existing) {
32
+ // 1. 选择或输入 BASE_URL
33
+ console.log('请选择或输入 BASE_URL:');
34
+ console.log('');
35
+ console.log(' 1) 智谱 GLM https://open.bigmodel.cn/api/anthropic');
36
+ console.log(' 2) Z.AI https://api.z.ai/api/anthropic');
37
+ console.log(' 3) 阿里云百炼 https://coding.dashscope.aliyuncs.com/apps/anthropic');
38
+ console.log(' 4) 其他(手动输入)');
39
+ console.log('');
40
+
41
+ const urlChoice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
42
+ let finalUrl = '';
43
+
44
+ if (urlChoice === 4) {
45
+ const defaultUrl = existing.ANTHROPIC_BASE_URL || '';
46
+ console.log('');
47
+ let baseUrl = await ask(rl, ` BASE_URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
48
+ finalUrl = baseUrl.trim() || defaultUrl;
49
+ } else {
50
+ const urlMap = {
51
+ 1: 'https://open.bigmodel.cn/api/anthropic',
52
+ 2: 'https://api.z.ai/api/anthropic',
53
+ 3: 'https://coding.dashscope.aliyuncs.com/apps/anthropic',
54
+ };
55
+ finalUrl = urlMap[urlChoice];
56
+ }
57
+
58
+ if (!finalUrl) {
59
+ console.error('BASE_URL 不能为空');
60
+ process.exit(1);
61
+ }
62
+
63
+ // 2. 输入 API_KEY(提示获取地址)
64
+ const apiUrlMap = {
65
+ 'open.bigmodel.cn': 'https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys',
66
+ 'api.z.ai': 'https://z.ai/manage-apikey/apikey-list',
67
+ 'dashscope.aliyuncs.com': 'https://bailian.console.aliyun.com/?tab=model#/api-key',
68
+ };
69
+
70
+ let apiUrlHint = '';
71
+ for (const [host, url] of Object.entries(apiUrlMap)) {
72
+ if (finalUrl.includes(host)) {
73
+ apiUrlHint = url;
74
+ break;
75
+ }
76
+ }
77
+
78
+ console.log('');
79
+ if (apiUrlHint) {
80
+ console.log(` ${COLOR.blue}API Key 获取地址: ${apiUrlHint}${COLOR.reset}`);
81
+ }
82
+ const apiKey = await askApiKey(rl, 'Coding Plan', '', existing.ANTHROPIC_API_KEY);
83
+
84
+ // 3. 返回配置(使用「长时间自运行Agent」推荐配置)
85
+ return {
86
+ lines: [
87
+ '# Claude Coder 模型配置',
88
+ '# 提供商: Coding Plan',
89
+ '',
90
+ 'MODEL_PROVIDER=coding-plan',
91
+ `ANTHROPIC_BASE_URL=${finalUrl}`,
92
+ `ANTHROPIC_API_KEY=${apiKey}`,
93
+ '',
94
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
95
+ '',
96
+ '# 模型路由配置(可在 .claude-coder/.env 修改)',
97
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5',
98
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=qwen3-coder-next',
99
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen3-coder-plus',
100
+ 'ANTHROPIC_MODEL=kimi-k2.5',
101
+ '',
102
+ 'API_TIMEOUT_MS=3000000',
103
+ ],
104
+ summary: `Coding Plan (${finalUrl})`,
105
+ };
106
+ }
107
+
108
+ async function configureAPI(rl, existing) {
109
+ console.log('请选择 API 模式:');
110
+ console.log('');
111
+ console.log(' 1) DeepSeek Chat (V3) - 速度快成本低 [推荐]');
112
+ console.log(' 2) DeepSeek Reasoner (R1) - 全链路推理');
113
+ console.log(' 3) DeepSeek Hybrid (R1+V3) - 规划用R1,执行用V3');
114
+ console.log(' 4) 自定义 - 输入其他 API');
115
+ console.log('');
116
+ const choice = await askChoice(rl, '选择 [1-4,默认 1]: ', 1, 4, 1);
117
+
118
+ if (choice === 4) {
119
+ return await configureCustomAPI(rl, existing);
120
+ }
121
+
122
+ return await configureDeepSeekMode(rl, existing, choice);
123
+ }
124
+
125
+ async function configureDeepSeekMode(rl, existing, choice) {
126
+ const existingKey = existing.MODEL_PROVIDER === 'deepseek' ? existing.ANTHROPIC_API_KEY : '';
127
+ const apiKey = await askApiKey(rl, 'DeepSeek', 'https://platform.deepseek.com/api_keys', existingKey);
128
+
129
+ const dsModel = ['deepseek-chat', 'deepseek-reasoner', 'deepseek-hybrid'][choice - 1];
130
+
131
+ const lines = [
132
+ '# Claude Coder 模型配置',
133
+ `# 提供商: DeepSeek`,
134
+ `# 模型: ${dsModel} | API_TIMEOUT_MS=600000 防止长输出超时(10分钟)`,
135
+ '',
136
+ 'MODEL_PROVIDER=deepseek',
137
+ 'ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic',
138
+ `ANTHROPIC_API_KEY=${apiKey}`,
139
+ `ANTHROPIC_AUTH_TOKEN=${apiKey}`,
140
+ '',
141
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
142
+ ];
143
+
144
+ if (dsModel === 'deepseek-chat') {
145
+ lines.push(
146
+ '# [DeepSeek Chat 降本策略]',
147
+ 'ANTHROPIC_MODEL=deepseek-chat',
148
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-chat',
149
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
150
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
151
+ );
152
+ } else if (dsModel === 'deepseek-reasoner') {
153
+ lines.push(
154
+ '# [DeepSeek Pure Reasoner 模式]',
155
+ 'ANTHROPIC_MODEL=deepseek-reasoner',
156
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
157
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-reasoner',
158
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-reasoner'
159
+ );
160
+ } else {
161
+ lines.push(
162
+ '# [DeepSeek Hybrid 混合模式]',
163
+ 'ANTHROPIC_MODEL=deepseek-reasoner',
164
+ 'ANTHROPIC_DEFAULT_OPUS_MODEL=deepseek-reasoner',
165
+ 'ANTHROPIC_DEFAULT_SONNET_MODEL=deepseek-chat',
166
+ 'ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-chat'
167
+ );
168
+ }
169
+
170
+ lines.push('API_TIMEOUT_MS=600000');
171
+
172
+ return { lines, summary: `DeepSeek (${dsModel})` };
173
+ }
174
+
175
+ async function configureCustomAPI(rl, existing) {
176
+ const defaultUrl = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_BASE_URL || '' : '';
177
+ console.log('请输入 Anthropic 兼容的 BASE_URL:');
178
+ let baseUrl = await ask(rl, ` URL${defaultUrl ? ` (回车保留: ${defaultUrl})` : ''}: `);
179
+ baseUrl = baseUrl.trim() || defaultUrl;
180
+
181
+ if (!baseUrl) {
182
+ console.error('BASE_URL 不能为空');
183
+ process.exit(1);
184
+ }
185
+
186
+ const existingKey = existing.MODEL_PROVIDER === 'custom' ? existing.ANTHROPIC_API_KEY : '';
187
+ const apiKey = await askApiKey(rl, '自定义 API', '', existingKey);
188
+
189
+ console.log('');
190
+ const modelInput = await ask(rl, '默认模型名称(回车跳过): ');
191
+ const model = modelInput.trim();
192
+
193
+ const lines = [
194
+ '# Claude Coder 模型配置',
195
+ '# 提供商: 自定义 API',
196
+ '',
197
+ 'MODEL_PROVIDER=custom',
198
+ `ANTHROPIC_BASE_URL=${baseUrl}`,
199
+ `ANTHROPIC_API_KEY=${apiKey}`,
200
+ '',
201
+ 'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1',
202
+ ];
203
+
204
+ if (model) {
205
+ lines.push(`ANTHROPIC_MODEL=${model}`);
206
+ }
207
+
208
+ lines.push('API_TIMEOUT_MS=3000000');
209
+
210
+ return { lines, summary: `自定义 API (${baseUrl})` };
211
+ }
212
+
213
+ // ── 提供商选择 ──
214
+
215
+ const PROVIDER_CONFIG = [configureDefault, configureCodingPlan, configureAPI];
216
+
217
+ async function selectProvider(rl, existing, showHeader = true) {
218
+ if (showHeader) console.log(PROVIDER_MENU);
219
+ const choice = await askChoice(rl, '选择 [1-3]: ', 1, 3);
220
+ console.log('');
221
+ return PROVIDER_CONFIG[choice - 1](rl, existing);
222
+ }
223
+
224
+ // ── 更新 API Key ──
225
+
226
+ async function updateApiKeyOnly(rl, existing) {
227
+ const provider = existing.MODEL_PROVIDER;
228
+ if (!provider || provider === 'claude') {
229
+ log('warn', 'Claude 官方无需配置 API Key(使用系统登录态)');
230
+ return;
231
+ }
232
+
233
+ const apiUrlMap = {
234
+ 'coding-plan': '',
235
+ 'deepseek': 'https://platform.deepseek.com/api_keys',
236
+ 'custom': '',
237
+ };
238
+
239
+ const apiKey = await askApiKey(rl, provider, apiUrlMap[provider] || '', existing.ANTHROPIC_API_KEY);
240
+ if (apiKey === null) {
241
+ log('info', '已取消,返回菜单');
242
+ return;
243
+ }
244
+ updateEnvVar('ANTHROPIC_API_KEY', apiKey);
245
+ if (provider === 'deepseek') {
246
+ updateEnvVar('ANTHROPIC_AUTH_TOKEN', apiKey);
247
+ }
248
+ log('ok', 'API Key 已更新');
249
+ }
250
+
251
+ module.exports = {
252
+ PROVIDER_MENU,
253
+ PROVIDER_CONFIG,
254
+ configureDefault,
255
+ configureCodingPlan,
256
+ configureAPI,
257
+ configureDeepSeekMode,
258
+ configureCustomAPI,
259
+ selectProvider,
260
+ updateApiKeyOnly,
261
+ };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const { ask } = require('./helpers');
4
+ const { log, COLOR, updateEnvVar } = require('../../common/config');
5
+
6
+ // ── 安全限制配置 ──
7
+
8
+ async function updateSafetyLimits(rl, existing) {
9
+ const currentStall = existing.SESSION_STALL_TIMEOUT || '600';
10
+ const currentCompletion = existing.SESSION_COMPLETION_TIMEOUT || '300';
11
+ const currentTurns = existing.SESSION_MAX_TURNS || '0';
12
+
13
+ console.log(`${COLOR.blue}当前安全限制:${COLOR.reset}`);
14
+ console.log(` 停顿超时: ${currentStall} 秒 (${Math.floor(parseInt(currentStall) / 60)} 分钟)`);
15
+ console.log(` 完成检测超时: ${currentCompletion} 秒 (${Math.ceil(parseInt(currentCompletion) / 60)} 分钟)`);
16
+ console.log(` 最大工具轮次: ${currentTurns === '0' ? '无限制' : currentTurns}`);
17
+ console.log('');
18
+ console.log(`${COLOR.yellow}说明:${COLOR.reset}`);
19
+ console.log(' 完成检测 — 模型写入 session_result.json 后缩短等待,解决"完成但不退出"');
20
+ console.log(' 停顿超时 — 长时间无工具调用时自动中断(通用兜底)');
21
+ console.log(' 最大轮次 — 限制总轮次,仅 CI 推荐(默认 0 = 无限制)');
22
+ console.log('');
23
+
24
+ const stallInput = await ask(rl, `停顿超时秒数(回车保留 ${currentStall}): `);
25
+ if (stallInput.trim()) {
26
+ const seconds = parseInt(stallInput.trim(), 10);
27
+ if (isNaN(seconds) || seconds < 60) {
28
+ log('warn', '停顿超时需 >= 60 秒,跳过');
29
+ } else {
30
+ updateEnvVar('SESSION_STALL_TIMEOUT', String(seconds));
31
+ log('ok', `停顿超时已设置为 ${seconds} 秒 (${Math.floor(seconds / 60)} 分钟)`);
32
+ }
33
+ }
34
+
35
+ console.log('');
36
+ const compInput = await ask(rl, `完成检测超时秒数(回车保留 ${currentCompletion}): `);
37
+ if (compInput.trim()) {
38
+ const seconds = parseInt(compInput.trim(), 10);
39
+ if (isNaN(seconds) || seconds < 30) {
40
+ log('warn', '完成检测超时需 >= 30 秒,跳过');
41
+ } else {
42
+ updateEnvVar('SESSION_COMPLETION_TIMEOUT', String(seconds));
43
+ log('ok', `完成检测超时已设置为 ${seconds} 秒`);
44
+ }
45
+ }
46
+
47
+ console.log('');
48
+ const turnsInput = await ask(rl, `最大工具轮次(回车保留 ${currentTurns === '0' ? '无限制' : currentTurns},输入 0 = 无限制): `);
49
+ if (turnsInput.trim()) {
50
+ const turns = parseInt(turnsInput.trim(), 10);
51
+ if (isNaN(turns) || turns < 0) {
52
+ log('warn', '请输入 >= 0 的整数,跳过');
53
+ } else {
54
+ updateEnvVar('SESSION_MAX_TURNS', String(turns));
55
+ log('ok', `最大工具轮次已设置为 ${turns === 0 ? '无限制' : turns}`);
56
+ }
57
+ }
58
+ }
59
+
60
+ module.exports = {
61
+ updateSafetyLimits,
62
+ };
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const { ask } = require('./helpers');
4
+ const { log, COLOR, updateEnvVar } = require('../../common/config');
5
+
6
+ // ── 自动审查配置 ──
7
+
8
+ async function updateSimplifyConfig(rl, existing) {
9
+ const currentInterval = existing.SIMPLIFY_INTERVAL ?? '5';
10
+ const currentCommits = existing.SIMPLIFY_COMMITS ?? '5';
11
+
12
+ console.log(`${COLOR.blue}自动代码审查配置:${COLOR.reset}`);
13
+ console.log(` 当前状态: ${currentInterval === '0' ? '禁用' : `每 ${currentInterval} 个 session 运行一次`}`);
14
+ console.log(` 审查范围: ${currentCommits} 个 commit`);
15
+ console.log('');
16
+ console.log(`${COLOR.yellow}说明:${COLOR.reset}`);
17
+ console.log(' 自动审查 — 在 run() 循环中定期运行代码审查,检查代码复用性、质量、效率');
18
+ console.log(' 审查间隔 — 每 N 个成功的 session 后运行一次(0 = 禁用)');
19
+ console.log(' 审查范围 — 审查最近 N 个 commit 的代码变更');
20
+ console.log('');
21
+
22
+ const intervalInput = await ask(rl, `审查间隔(输入 0 禁用,回车保留 ${currentInterval === '0' ? '禁用' : currentInterval}): `);
23
+ if (intervalInput.trim()) {
24
+ const interval = parseInt(intervalInput.trim(), 10);
25
+ if (isNaN(interval) || interval < 0) {
26
+ log('warn', '请输入 >= 0 的整数,跳过');
27
+ } else {
28
+ updateEnvVar('SIMPLIFY_INTERVAL', String(interval));
29
+ log('ok', `自动审查已${interval === 0 ? '禁用' : `设置为每 ${interval} 个 session 运行一次`}`);
30
+ }
31
+ }
32
+
33
+ const effectiveInterval = intervalInput.trim()
34
+ ? String(parseInt(intervalInput.trim(), 10) || 0)
35
+ : currentInterval;
36
+ if (effectiveInterval !== '0') {
37
+ console.log('');
38
+ const commitsInput = await ask(rl, `审查 commit 数量(回车保留 ${currentCommits}): `);
39
+ if (commitsInput.trim()) {
40
+ const commits = parseInt(commitsInput.trim(), 10);
41
+ if (isNaN(commits) || commits < 1) {
42
+ log('warn', '请输入 >= 1 的整数,跳过');
43
+ } else {
44
+ updateEnvVar('SIMPLIFY_COMMITS', String(commits));
45
+ log('ok', `审查范围已设置为 ${commits} 个 commit`);
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ module.exports = {
52
+ updateSimplifyConfig,
53
+ };
@@ -0,0 +1,172 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const { log, COLOR, parseEnvFile } = require('../common/config');
5
+ const { assets } = require('../common/assets');
6
+ const {
7
+ createInterface,
8
+ ask,
9
+ askChoice,
10
+ writeConfig,
11
+ ensureGitignore,
12
+ showCurrentConfig,
13
+ selectProvider,
14
+ updateApiKeyOnly,
15
+ configureMCP,
16
+ appendMcpConfig,
17
+ updateMCPOnly,
18
+ updateSafetyLimits,
19
+ updateSimplifyConfig,
20
+ } = require('./setup-modules');
21
+
22
+ const PRESERVED_KEYS = [
23
+ 'SESSION_STALL_TIMEOUT', 'SESSION_COMPLETION_TIMEOUT',
24
+ 'SESSION_MAX_TURNS', 'SIMPLIFY_INTERVAL', 'SIMPLIFY_COMMITS',
25
+ ];
26
+
27
+ function preserveSafetyConfig(lines, existing) {
28
+ const preserved = PRESERVED_KEYS
29
+ .filter(k => existing[k])
30
+ .map(k => `${k}=${existing[k]}`);
31
+ if (preserved.length > 0) {
32
+ lines.push('', '# 保留的安全限制和审查配置');
33
+ lines.push(...preserved);
34
+ }
35
+ }
36
+
37
+ async function setup() {
38
+ assets.ensureDirs();
39
+ const rl = createInterface();
40
+
41
+ const envPath = assets.path('env');
42
+ let existing = {};
43
+ if (fs.existsSync(envPath)) {
44
+ existing = parseEnvFile(envPath);
45
+ }
46
+
47
+ console.log('');
48
+ console.log('============================================');
49
+ console.log(' Claude Coder 配置');
50
+ console.log('============================================');
51
+
52
+ if (!fs.existsSync(envPath) || !existing.MODEL_PROVIDER) {
53
+ console.log('');
54
+ console.log(' 检测到未配置,开始初始化...');
55
+ console.log('');
56
+
57
+ const configResult = await selectProvider(rl, existing);
58
+ const mcpConfig = await configureMCP(rl);
59
+
60
+ appendMcpConfig(configResult.lines, mcpConfig);
61
+ writeConfig(envPath, configResult.lines);
62
+ ensureGitignore();
63
+
64
+ if (mcpConfig.enabled && mcpConfig.mode) {
65
+ const { updateMcpConfig } = require('./auth');
66
+ const mcpPath = assets.path('mcpConfig');
67
+ updateMcpConfig(mcpPath, mcpConfig.mode);
68
+ }
69
+
70
+ console.log('');
71
+ log('info', '配置自动代码审查(可选)');
72
+ await updateSimplifyConfig(rl, {});
73
+
74
+ console.log('');
75
+ log('ok', `配置完成!提供商: ${configResult.summary}`);
76
+ console.log('');
77
+ console.log(` 配置文件: ${envPath}`);
78
+ console.log(' 使用方式: claude-coder run "你的需求"');
79
+ console.log(' 重新配置: claude-coder setup');
80
+ console.log('');
81
+ console.log(` ${COLOR.blue}当前默认值:${COLOR.reset}`);
82
+ console.log(` 停顿超时: 1200 秒 (20 分钟)`);
83
+ console.log(` 完成检测超时: 300 秒 (5 分钟)`);
84
+ console.log(` 自动审查: 每 5 个 session,审查 5 个 commit`);
85
+ console.log('');
86
+ console.log(` ${COLOR.yellow}调整方式: claude-coder setup → 配置安全限制 / 配置自动审查${COLOR.reset}`);
87
+ console.log('');
88
+
89
+ rl.close();
90
+ return;
91
+ }
92
+
93
+ while (true) {
94
+ existing = parseEnvFile(envPath);
95
+ showCurrentConfig(existing);
96
+
97
+ console.log('请选择要执行的操作:');
98
+ console.log('');
99
+ console.log(' 1) 切换模型提供商');
100
+ console.log(' 2) 更新 API Key');
101
+ console.log(' 3) 配置 MCP');
102
+ console.log(' 4) 配置安全限制');
103
+ console.log(' 5) 配置自动审查');
104
+ console.log(' 6) 完全重新配置');
105
+ console.log(' 7) 退出');
106
+ console.log('');
107
+
108
+ const action = await askChoice(rl, '选择 [1-7]: ', 1, 7);
109
+ console.log('');
110
+
111
+ if (action === 7) {
112
+ log('info', '退出配置');
113
+ break;
114
+ }
115
+
116
+ switch (action) {
117
+ case 1: {
118
+ log('info', '放心切换,旧配置会自动备份,安全限制和审查配置会保留');
119
+ const configResult = await selectProvider(rl, existing);
120
+ preserveSafetyConfig(configResult.lines, existing);
121
+ appendMcpConfig(configResult.lines, {
122
+ enabled: existing.MCP_PLAYWRIGHT === 'true',
123
+ mode: existing.MCP_PLAYWRIGHT_MODE || null,
124
+ });
125
+ writeConfig(envPath, configResult.lines);
126
+ log('ok', `已切换到: ${configResult.summary}`);
127
+ break;
128
+ }
129
+ case 2: {
130
+ await updateApiKeyOnly(rl, existing);
131
+ break;
132
+ }
133
+ case 3: {
134
+ await updateMCPOnly(rl);
135
+ break;
136
+ }
137
+ case 4: {
138
+ await updateSafetyLimits(rl, existing);
139
+ break;
140
+ }
141
+ case 5: {
142
+ await updateSimplifyConfig(rl, existing);
143
+ break;
144
+ }
145
+ case 6: {
146
+ log('info', '放心重新配置,旧配置会自动备份,安全限制和审查配置会保留');
147
+ const configResult = await selectProvider(rl, existing);
148
+ preserveSafetyConfig(configResult.lines, existing);
149
+ const mcpConfig = await configureMCP(rl);
150
+ appendMcpConfig(configResult.lines, mcpConfig);
151
+ writeConfig(envPath, configResult.lines);
152
+
153
+ if (mcpConfig.enabled && mcpConfig.mode) {
154
+ const { updateMcpConfig } = require('./auth');
155
+ const mcpPath = assets.path('mcpConfig');
156
+ updateMcpConfig(mcpPath, mcpConfig.mode);
157
+ }
158
+
159
+ log('ok', '配置已更新');
160
+ break;
161
+ }
162
+ }
163
+
164
+ console.log('');
165
+ const cont = await ask(rl, '继续配置其他项?(y/N) ');
166
+ if (!/^[Yy]/.test(cont.trim())) break;
167
+ }
168
+
169
+ rl.close();
170
+ }
171
+
172
+ module.exports = { setup };