claude-coder 1.8.0 → 1.8.2

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 +167 -177
  2. package/bin/cli.js +172 -159
  3. package/package.json +52 -52
  4. package/src/commands/auth.js +240 -294
  5. package/src/commands/setup-modules/helpers.js +99 -105
  6. package/src/commands/setup-modules/index.js +25 -25
  7. package/src/commands/setup-modules/mcp.js +94 -94
  8. package/src/commands/setup-modules/provider.js +260 -260
  9. package/src/commands/setup-modules/safety.js +61 -61
  10. package/src/commands/setup-modules/simplify.js +52 -52
  11. package/src/commands/setup.js +172 -172
  12. package/src/common/assets.js +236 -192
  13. package/src/common/config.js +125 -138
  14. package/src/common/constants.js +55 -56
  15. package/src/common/indicator.js +222 -222
  16. package/src/common/interaction.js +170 -170
  17. package/src/common/logging.js +77 -76
  18. package/src/common/sdk.js +50 -50
  19. package/src/common/tasks.js +88 -157
  20. package/src/common/utils.js +161 -146
  21. package/src/core/coding.js +55 -55
  22. package/src/core/context.js +117 -132
  23. package/src/core/go.js +310 -0
  24. package/src/core/harness.js +484 -0
  25. package/src/core/hooks.js +533 -528
  26. package/src/core/init.js +171 -163
  27. package/src/core/plan.js +325 -318
  28. package/src/core/prompts.js +227 -253
  29. package/src/core/query.js +49 -47
  30. package/src/core/repair.js +46 -58
  31. package/src/core/runner.js +195 -352
  32. package/src/core/scan.js +89 -89
  33. package/src/core/{base.js → session.js} +56 -53
  34. package/src/core/simplify.js +52 -59
  35. package/templates/bash-process.md +12 -5
  36. package/templates/codingSystem.md +65 -0
  37. package/templates/codingUser.md +17 -31
  38. package/templates/coreProtocol.md +29 -0
  39. package/templates/goSystem.md +130 -0
  40. package/templates/guidance.json +52 -34
  41. package/templates/planSystem.md +78 -0
  42. package/templates/planUser.md +9 -0
  43. package/templates/playwright.md +16 -16
  44. package/templates/requirements.example.md +57 -56
  45. package/templates/scanSystem.md +120 -0
  46. package/templates/scanUser.md +10 -17
  47. package/templates/test_rule.md +194 -194
  48. package/src/core/validator.js +0 -138
  49. package/templates/addGuide.md +0 -98
  50. package/templates/addUser.md +0 -26
  51. package/templates/agentProtocol.md +0 -195
  52. package/templates/scanProtocol.md +0 -118
@@ -1,58 +1,46 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { runSession } = require('./base');
6
- const { buildQueryOptions } = require('./query');
7
- const { log } = require('../common/config');
8
-
9
- /**
10
- * 通用 AI 文件修复/维护工具
11
- * 可嵌入 runner.js、plan.js 及任何需要 AI 修复文件的场景
12
- *
13
- * @param {string} filePath - 待修复文件的绝对路径
14
- * @param {object} [opts] - 选项
15
- * @param {string} [opts.prompt] - 自定义 prompt(省略则使用默认 JSON 修复 prompt)
16
- * @param {string} [opts.model] - 模型覆盖
17
- * @returns {Promise<{success: boolean}>}
18
- */
19
- async function repairFile(filePath, opts = {}) {
20
- if (!fs.existsSync(filePath)) {
21
- log('error', `修复目标不存在: ${filePath}`);
22
- return { success: false };
23
- }
24
-
25
- const content = fs.readFileSync(filePath, 'utf8');
26
- const filename = path.basename(filePath);
27
-
28
- const defaultPrompt = `以下文件出现格式错误,请修复并用 Write 工具写回原路径。
29
- 只修复格式问题(JSON 语法、截断、尾逗号等),不改变数据内容。
30
-
31
- 文件路径: ${filePath}
32
- 当前内容:
33
- \`\`\`
34
- ${content.slice(0, 30000)}
35
- \`\`\`
36
-
37
- 修复后用 Write 写回 ${filePath}`;
38
-
39
- const prompt = opts.prompt || defaultPrompt;
40
- const ts = Date.now();
41
-
42
- return runSession('repair', {
43
- opts,
44
- sessionNum: 0,
45
- logFileName: `repair_${filename}_${ts}.log`,
46
- label: `repair ${filename}`,
47
-
48
- async execute(sdk, ctx) {
49
- const queryOpts = buildQueryOptions(ctx.config, opts);
50
- queryOpts.hooks = ctx.hooks;
51
- queryOpts.abortController = ctx.abortController;
52
- await ctx.runQuery(sdk, prompt, queryOpts);
53
- return { success: true };
54
- },
55
- });
56
- }
57
-
58
- module.exports = { repairFile };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { runSession } = require('./session');
6
+ const { buildQueryOptions } = require('./query');
7
+ const { log } = require('../common/config');
8
+
9
+ /**
10
+ * 使用 AI 修复损坏的 JSON 文件
11
+ * @param {string} filePath - 文件绝对路径
12
+ * @param {object} [opts] - 透传给 runSession 的选项
13
+ */
14
+ async function repairJsonFile(filePath, opts = {}) {
15
+ if (!fs.existsSync(filePath)) return;
16
+
17
+ const rawContent = fs.readFileSync(filePath, 'utf8');
18
+ if (!rawContent || !rawContent.trim()) return;
19
+
20
+ const fileName = path.basename(filePath);
21
+ log('info', `正在使用 AI 修复 ${fileName}...`);
22
+
23
+ const prompt = `文件 ${filePath} 的 JSON 格式已损坏,请修复并用 Write 工具写入原路径。\n\n当前损坏内容:\n${rawContent}`;
24
+
25
+ try {
26
+ await runSession('repair', {
27
+ opts,
28
+ sessionNum: 0,
29
+ logFileName: `repair_${fileName.replace('.json', '')}.log`,
30
+ label: `repair:${fileName}`,
31
+
32
+ async execute(sdk, ctx) {
33
+ const queryOpts = buildQueryOptions(ctx.config, opts);
34
+ queryOpts.hooks = ctx.hooks;
35
+ queryOpts.abortController = ctx.abortController;
36
+ await ctx.runQuery(sdk, prompt, queryOpts);
37
+ log('ok', `AI 修复 ${fileName} 完成`);
38
+ return {};
39
+ },
40
+ });
41
+ } catch (err) {
42
+ log('warn', `AI 修复 ${fileName} 失败: ${err.message}`);
43
+ }
44
+ }
45
+
46
+ module.exports = { repairJsonFile };
@@ -1,352 +1,195 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const readline = require('readline');
5
- const { execSync } = require('child_process');
6
- const { log, loadConfig } = require('../common/config');
7
- const { assets } = require('../common/assets');
8
- const { getGitHead, isGitRepo, sleep } = require('../common/utils');
9
- const { RETRY } = require('../common/constants');
10
- const { loadTasks, getFeatures, getStats, findNextTask, forceStatus, printStats } = require('../common/tasks');
11
- const { validate } = require('./validator');
12
- const { runCodingSession } = require('./coding');
13
- const { simplify } = require('./simplify');
14
- const { repairFile } = require('./repair');
15
- const { buildArchivePrompt } = require('./prompts');
16
- const { loadSDK } = require('../common/sdk');
17
-
18
- const MAX_RETRY = RETRY.MAX_ATTEMPTS;
19
-
20
- function getHead() {
21
- return getGitHead(assets.projectRoot);
22
- }
23
-
24
- function killServicesByProfile() {
25
- const profile = assets.readJson('profile', null);
26
- if (!profile) return;
27
- try {
28
- const services = profile.services || [];
29
- const ports = services.map(s => s.port).filter(Boolean);
30
- if (ports.length === 0) return;
31
-
32
- const isWin = process.platform === 'win32';
33
- for (const port of ports) {
34
- try {
35
- if (isWin) {
36
- const out = execSync(`netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: 'utf8', stdio: 'pipe' }).trim();
37
- const pids = [...new Set(out.split('\n').map(l => l.trim().split(/\s+/).pop()).filter(Boolean))];
38
- for (const pid of pids) { try { execSync(`taskkill /F /PID ${pid}`, { stdio: 'pipe' }); } catch { /* ignore */ } }
39
- } else {
40
- execSync(`lsof -ti :${port} | xargs kill -9 2>/dev/null`, { stdio: 'pipe' });
41
- }
42
- } catch { /* no process on port */ }
43
- }
44
- log('info', `已停止端口 ${ports.join(', ')} 上的服务`);
45
- } catch { /* ignore profile read errors */ }
46
- }
47
-
48
- async function rollback(headBefore, reason) {
49
- if (!headBefore || headBefore === 'none') return;
50
-
51
- killServicesByProfile();
52
-
53
- if (process.platform === 'win32') await sleep(1500);
54
-
55
- const cwd = assets.projectRoot;
56
- const gitEnv = { ...process.env, GIT_TERMINAL_PROMPT: '0' };
57
-
58
- log('warn', `回滚到 ${headBefore} ...`);
59
-
60
- let success = false;
61
- for (let attempt = 1; attempt <= 2; attempt++) {
62
- try {
63
- execSync(`git reset --hard ${headBefore}`, { cwd, stdio: 'pipe', env: gitEnv });
64
- log('ok', '回滚完成');
65
- success = true;
66
- break;
67
- } catch (err) {
68
- if (attempt === 1) {
69
- log('warn', `回滚首次失败,等待后重试: ${err.message}`);
70
- await sleep(2000);
71
- } else {
72
- log('error', `回滚失败: ${err.message}`);
73
- }
74
- }
75
- }
76
-
77
- appendProgress({
78
- type: 'rollback',
79
- timestamp: new Date().toISOString(),
80
- reason: reason || 'harness 校验失败',
81
- rollbackTo: headBefore,
82
- success,
83
- });
84
- }
85
-
86
- function markTaskFailed() {
87
- const data = loadTasks();
88
- if (!data) return;
89
- const result = forceStatus(data, 'failed');
90
- if (result) {
91
- log('warn', `已将任务 ${result.id} 强制标记为 failed`);
92
- }
93
- }
94
-
95
- function tryPush() {
96
- try {
97
- const cwd = assets.projectRoot;
98
- const remotes = execSync('git remote', { cwd, encoding: 'utf8' }).trim();
99
- if (!remotes) return;
100
- log('info', '正在推送代码...');
101
- execSync('git push', { cwd, stdio: 'inherit' });
102
- log('ok', '推送成功');
103
- } catch {
104
- log('warn', '推送失败 (请检查网络或权限),继续执行...');
105
- }
106
- }
107
-
108
- function appendProgress(entry) {
109
- let progress = assets.readJson('progress', { sessions: [] });
110
- if (!Array.isArray(progress.sessions)) progress.sessions = [];
111
- progress.sessions.push(entry);
112
- assets.writeJson('progress', progress);
113
- }
114
-
115
- async function archiveDoneTasks() {
116
- const data = loadTasks();
117
- if (!data) return;
118
- const features = data.features || [];
119
- const done = features.filter(f => f.status === 'done');
120
- if (done.length < 3) return;
121
-
122
- const tasksPath = assets.path('tasks');
123
- const prompt = buildArchivePrompt(done, data.completed_milestones, tasksPath);
124
-
125
- log('info', `归档 ${done.length} 个完成任务...`);
126
- await repairFile(tasksPath, { prompt });
127
- log('ok', '任务归档完成');
128
- }
129
-
130
- async function promptContinue() {
131
- if (!process.stdin.isTTY) return true;
132
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
133
- return new Promise(resolve => {
134
- rl.question('是否继续?(y/n) ', answer => {
135
- rl.close();
136
- resolve(/^[Yy]/.test(answer.trim()));
137
- });
138
- });
139
- }
140
-
141
- async function run(opts = {}) {
142
- assets.ensureDirs();
143
- const projectRoot = assets.projectRoot;
144
-
145
- const maxSessions = opts.max || 50;
146
- const pauseEvery = opts.pause ?? 0;
147
- const dryRun = opts.dryRun || false;
148
-
149
- console.log('');
150
- console.log('============================================');
151
- console.log(` Claude Coder${dryRun ? ' (预览模式)' : ''}`);
152
- console.log('============================================');
153
- console.log('');
154
-
155
- const config = loadConfig();
156
- if (config.provider !== 'claude' && config.baseUrl) {
157
- log('ok', `模型配置已加载: ${config.provider}${config.model ? ` (${config.model})` : ''}`);
158
- }
159
-
160
- if (!isGitRepo(projectRoot)) {
161
- log('info', '初始化 git 仓库...');
162
- execSync('git init', { cwd: projectRoot, stdio: 'inherit' });
163
- execSync('git add -A && git commit -m "init: 项目初始化" --allow-empty', {
164
- cwd: projectRoot,
165
- stdio: 'inherit',
166
- });
167
- }
168
-
169
- if (!assets.exists('profile')) {
170
- log('error', 'profile 不存在,请先运行 claude-coder init 初始化项目');
171
- process.exit(1);
172
- }
173
-
174
- if (!assets.exists('tasks')) {
175
- log('error', 'tasks.json 不存在,请先运行 claude-coder plan 生成任务');
176
- process.exit(1);
177
- }
178
-
179
- printStats();
180
-
181
- if (!dryRun) {
182
- await loadSDK();
183
- }
184
- log('info', `开始编码循环 (最多 ${maxSessions} 个会话) ...`);
185
- console.log('');
186
-
187
- let consecutiveFailures = 0;
188
-
189
- for (let session = 1; session <= maxSessions; session++) {
190
- console.log('');
191
- console.log('--------------------------------------------');
192
- log('info', `Session ${session} / ${maxSessions}`);
193
- console.log('--------------------------------------------');
194
-
195
- let taskData = loadTasks();
196
- if (!taskData) {
197
- log('warn', 'tasks.json 读取异常,尝试 AI 修复...');
198
- await repairFile(assets.path('tasks'));
199
- taskData = loadTasks();
200
- if (!taskData) {
201
- log('error', 'tasks.json 无法修复,终止循环');
202
- break;
203
- }
204
- log('ok', 'tasks.json AI 修复成功');
205
- await archiveDoneTasks();
206
- }
207
-
208
- const features = getFeatures(taskData);
209
- if (features.length > 0 && features.every(f => f.status === 'done')) {
210
- console.log('');
211
- log('ok', '所有任务已完成!');
212
- printStats();
213
- break;
214
- }
215
-
216
- const stats = getStats(taskData);
217
- log('info', `进度: ${stats.done}/${stats.total} done, ${stats.in_progress} in_progress, ${stats.testing} testing, ${stats.failed} failed, ${stats.pending} pending`);
218
-
219
- if (dryRun) {
220
- const next = findNextTask(taskData);
221
- log('info', `[DRY-RUN] 下一个任务: ${next ? `${next.id} - ${next.description}` : '无待处理任务'}`);
222
- if (!next) {
223
- log('ok', '[DRY-RUN] 无可执行任务,预览结束');
224
- } else {
225
- console.log('');
226
- log('info', '[DRY-RUN] 任务队列:');
227
- const allFeatures = getFeatures(taskData);
228
- for (const f of allFeatures) {
229
- const st = f.status || 'unknown';
230
- const statusTag = { done: '✓', in_progress: '▸', pending: '○', failed: '✗', testing: '◇' }[st] || '?';
231
- log('info', ` ${statusTag} [${st.padEnd(11)}] ${f.id} - ${f.description || ''}`);
232
- }
233
- }
234
- break;
235
- }
236
-
237
- const headBefore = getHead();
238
- const nextTask = findNextTask(taskData);
239
- const taskId = nextTask?.id || 'unknown';
240
-
241
- const sessionResult = await runCodingSession(session, {
242
- projectRoot,
243
- taskId,
244
- consecutiveFailures,
245
- maxSessions,
246
- lastValidateLog: consecutiveFailures > 0 ? '上次校验失败' : '',
247
- });
248
-
249
- if (sessionResult.stalled) {
250
- log('warn', `Session ${session} 因停顿超时中断,跳过校验直接重试`);
251
- consecutiveFailures++;
252
- await rollback(headBefore, '停顿超时');
253
- if (consecutiveFailures >= MAX_RETRY) {
254
- log('error', `连续失败 ${MAX_RETRY} 次,跳过当前任务`);
255
- markTaskFailed();
256
- consecutiveFailures = 0;
257
- }
258
- appendProgress({
259
- session,
260
- timestamp: new Date().toISOString(),
261
- result: 'stalled',
262
- cost: sessionResult.cost,
263
- taskId,
264
- });
265
- continue;
266
- }
267
-
268
- log('info', '开始 harness 校验 ...');
269
- const validateResult = await validate(headBefore, taskId);
270
-
271
- if (!validateResult.fatal) {
272
- if (validateResult.hasWarnings) {
273
- log('warn', `Session ${session} 校验通过 (有自动修复或警告)`);
274
- } else {
275
- log('ok', `Session ${session} 校验通过`);
276
- }
277
-
278
- // 定期归档 + 代码审查
279
- const simplifyInterval = config.simplifyInterval;
280
- if (simplifyInterval > 0 && session % simplifyInterval === 0) {
281
- await archiveDoneTasks();
282
- log('info', `每 ${simplifyInterval} 个 session 运行代码审查...`);
283
- await simplify(null, { n: config.simplifyCommits });
284
-
285
- // 检查是否有代码变更
286
- try {
287
- execSync('git diff --quiet HEAD', { cwd: projectRoot, stdio: 'pipe' });
288
- } catch {
289
- // 有变更,自动提交
290
- execSync('git add -A && git commit -m "style: simplify optimization"', { cwd: projectRoot, stdio: 'pipe' });
291
- log('ok', '代码优化已提交');
292
- }
293
- }
294
-
295
- tryPush();
296
- consecutiveFailures = 0;
297
-
298
- appendProgress({
299
- session,
300
- timestamp: new Date().toISOString(),
301
- result: 'success',
302
- cost: sessionResult.cost,
303
- taskId,
304
- statusAfter: validateResult.sessionData?.status_after || null,
305
- notes: validateResult.sessionData?.notes || null,
306
- });
307
-
308
- } else {
309
- consecutiveFailures++;
310
- log('error', `Session ${session} 校验失败 (连续失败: ${consecutiveFailures}/${MAX_RETRY})`);
311
-
312
- appendProgress({
313
- session,
314
- timestamp: new Date().toISOString(),
315
- result: 'fatal',
316
- cost: sessionResult.cost,
317
- taskId,
318
- reason: validateResult.sessionData?.reason || '校验失败',
319
- });
320
-
321
- await rollback(headBefore, '校验失败');
322
-
323
- if (consecutiveFailures >= MAX_RETRY) {
324
- log('error', `连续失败 ${MAX_RETRY} 次,跳过当前任务`);
325
- markTaskFailed();
326
- consecutiveFailures = 0;
327
- log('warn', '已将任务标记为 failed,继续下一个任务');
328
- }
329
- }
330
-
331
- if (pauseEvery > 0 && session % pauseEvery === 0) {
332
- console.log('');
333
- printStats();
334
- const shouldContinue = await promptContinue();
335
- if (!shouldContinue) {
336
- log('info', '手动停止');
337
- break;
338
- }
339
- }
340
- }
341
-
342
- killServicesByProfile();
343
-
344
- console.log('');
345
- console.log('============================================');
346
- console.log(' 运行结束');
347
- console.log('============================================');
348
- console.log('');
349
- printStats();
350
- }
351
-
352
- module.exports = { run };
1
+ 'use strict';
2
+
3
+ const readline = require('readline');
4
+ const { log, loadConfig } = require('../common/config');
5
+ const { assets } = require('../common/assets');
6
+ const { loadTasks, getFeatures, getStats, printStats } = require('../common/tasks');
7
+ const { runCodingSession } = require('./coding');
8
+ const { Harness, selectNextTask } = require('./harness');
9
+ const { simplify } = require('./simplify');
10
+ const { repairJsonFile } = require('./repair');
11
+
12
+ // ─── Display Helpers ──────────────────────────────────────────
13
+
14
+ function printBanner(dryRun) {
15
+ console.log('');
16
+ console.log('============================================');
17
+ console.log(` Claude Coder${dryRun ? ' (预览模式)' : ''}`);
18
+ console.log('============================================');
19
+ console.log('');
20
+ }
21
+
22
+ function printSessionHeader(session, maxSessions) {
23
+ console.log('');
24
+ console.log('--------------------------------------------');
25
+ log('info', `Session ${session} / ${maxSessions}`);
26
+ console.log('--------------------------------------------');
27
+ }
28
+
29
+ function printProgress(taskData) {
30
+ const stats = getStats(taskData);
31
+ log('info', `进度: ${stats.done}/${stats.total} done, ${stats.in_progress} in_progress, ${stats.testing} testing, ${stats.failed} failed, ${stats.pending} pending`);
32
+ }
33
+
34
+ function printDryRun(taskData) {
35
+ const next = selectNextTask(taskData);
36
+ log('info', `[DRY-RUN] 下一个任务: ${next ? `${next.id} - ${next.description}` : '无待处理任务'}`);
37
+
38
+ if (!next) {
39
+ log('ok', '[DRY-RUN] 无可执行任务,预览结束');
40
+ return;
41
+ }
42
+
43
+ console.log('');
44
+ log('info', '[DRY-RUN] 任务队列:');
45
+ const features = getFeatures(taskData);
46
+ for (const f of features) {
47
+ const st = f.status || 'unknown';
48
+ const icon = { done: '✓', in_progress: '▸', pending: '○', failed: '✗', testing: '◇' }[st] || '?';
49
+ log('info', ` ${icon} [${st.padEnd(11)}] ${f.id} - ${f.description || ''}`);
50
+ }
51
+ }
52
+
53
+ function printEndBanner() {
54
+ console.log('');
55
+ console.log('============================================');
56
+ console.log(' 运行结束');
57
+ console.log('============================================');
58
+ console.log('');
59
+ }
60
+
61
+ async function promptContinue() {
62
+ if (!process.stdin.isTTY) return true;
63
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
64
+ return new Promise(resolve => {
65
+ rl.question('是否继续?(y/n) ', answer => {
66
+ rl.close();
67
+ resolve(/^[Yy]/.test(answer.trim()));
68
+ });
69
+ });
70
+ }
71
+
72
+ // ─── Simplify Helper ─────────────────────────────────────────
73
+
74
+ async function tryRunSimplify(harness, config, msg, commitMsg) {
75
+ log('info', msg || `每 ${config.simplifyInterval} 个成功 session 运行代码审查...`);
76
+ try {
77
+ await simplify(null, { n: config.simplifyCommits });
78
+ harness.afterSimplify(commitMsg);
79
+ } catch (err) {
80
+ log('warn', `代码审查失败,跳过: ${err.message}`);
81
+ }
82
+ }
83
+
84
+ // ─── Main Orchestration Loop ──────────────────────────────────
85
+
86
+ async function run(opts = {}) {
87
+ const config = loadConfig();
88
+ const harness = new Harness(config);
89
+
90
+ harness.ensureEnvironment();
91
+
92
+ const dryRun = opts.dryRun || false;
93
+ const maxSessions = opts.max || 50;
94
+ const pauseEvery = opts.pause ?? 0;
95
+
96
+ printBanner(dryRun);
97
+
98
+ if (config.provider !== 'claude' && config.baseUrl) {
99
+ log('ok', `模型配置已加载: ${config.provider}${config.model ? ` (${config.model})` : ''}`);
100
+ }
101
+
102
+ const prereq = harness.checkPrerequisites();
103
+ if (!prereq.ok) {
104
+ log('error', prereq.msg);
105
+ process.exit(1);
106
+ }
107
+
108
+ printStats();
109
+
110
+ log('info', `开始编码循环 (最多 ${maxSessions} 个会话) ...`);
111
+ console.log('');
112
+
113
+ let state = { consecutiveFailures: 0, lastFailReason: '' };
114
+
115
+ for (let session = 1; session <= maxSessions; session++) {
116
+ printSessionHeader(session, maxSessions);
117
+
118
+ let taskData = loadTasks();
119
+ if (!taskData) {
120
+ const tasksPath = assets.path('tasks');
121
+ if (tasksPath) await repairJsonFile(tasksPath);
122
+ taskData = loadTasks();
123
+ if (!taskData) {
124
+ log('error', 'tasks.json 无法读取且修复失败,终止循环');
125
+ break;
126
+ }
127
+ }
128
+
129
+ if (harness.isAllDone(taskData)) {
130
+ if (!dryRun) {
131
+ if (harness.needsFinalSimplify()) {
132
+ await tryRunSimplify(harness, config, '所有任务完成,运行最终代码审查...', 'style: final simplify');
133
+ }
134
+ harness.tryPush();
135
+ }
136
+ console.log('');
137
+ log('ok', '所有任务已完成!');
138
+ printStats();
139
+ break;
140
+ }
141
+
142
+ printProgress(taskData);
143
+
144
+ if (dryRun) {
145
+ printDryRun(taskData);
146
+ break;
147
+ }
148
+
149
+ const { headBefore, taskId } = harness.snapshot(taskData);
150
+
151
+ const sessionResult = await runCodingSession(session, {
152
+ projectRoot: harness.projectRoot,
153
+ taskId,
154
+ consecutiveFailures: state.consecutiveFailures,
155
+ maxSessions,
156
+ lastValidateLog: state.lastFailReason,
157
+ });
158
+
159
+ if (sessionResult.stalled) {
160
+ state = await harness.onStall(session, { headBefore, taskId, sessionResult, ...state });
161
+ continue;
162
+ }
163
+
164
+ log('info', '开始 harness 校验 ...');
165
+ const validateResult = await harness.validate(headBefore, taskId);
166
+
167
+ if (!validateResult.fatal) {
168
+ const level = validateResult.hasWarnings ? 'warn' : 'ok';
169
+ log(level, `Session ${session} 校验通过${validateResult.hasWarnings ? ' (有警告)' : ''}`);
170
+ state = await harness.onSuccess(session, { taskId, sessionResult, validateResult });
171
+
172
+ if (harness.shouldSimplify()) {
173
+ await tryRunSimplify(harness, config);
174
+ }
175
+ harness.tryPush();
176
+ } else {
177
+ state = await harness.onFailure(session, { headBefore, taskId, sessionResult, validateResult, ...state });
178
+ }
179
+
180
+ if (pauseEvery > 0 && session % pauseEvery === 0) {
181
+ console.log('');
182
+ printStats();
183
+ if (!await promptContinue()) {
184
+ log('info', '手动停止');
185
+ break;
186
+ }
187
+ }
188
+ }
189
+
190
+ harness.cleanup();
191
+ printEndBanner();
192
+ printStats();
193
+ }
194
+
195
+ module.exports = { run };