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.
- package/README.md +167 -177
- package/bin/cli.js +172 -159
- package/package.json +52 -52
- package/src/commands/auth.js +240 -294
- package/src/commands/setup-modules/helpers.js +99 -105
- package/src/commands/setup-modules/index.js +25 -25
- package/src/commands/setup-modules/mcp.js +94 -94
- package/src/commands/setup-modules/provider.js +260 -260
- package/src/commands/setup-modules/safety.js +61 -61
- package/src/commands/setup-modules/simplify.js +52 -52
- package/src/commands/setup.js +172 -172
- package/src/common/assets.js +236 -192
- package/src/common/config.js +125 -138
- package/src/common/constants.js +55 -56
- package/src/common/indicator.js +222 -222
- package/src/common/interaction.js +170 -170
- package/src/common/logging.js +77 -76
- package/src/common/sdk.js +50 -50
- package/src/common/tasks.js +88 -157
- package/src/common/utils.js +161 -146
- package/src/core/coding.js +55 -55
- package/src/core/context.js +117 -132
- package/src/core/go.js +310 -0
- package/src/core/harness.js +484 -0
- package/src/core/hooks.js +533 -528
- package/src/core/init.js +171 -163
- package/src/core/plan.js +325 -318
- package/src/core/prompts.js +227 -253
- package/src/core/query.js +49 -47
- package/src/core/repair.js +46 -58
- package/src/core/runner.js +195 -352
- package/src/core/scan.js +89 -89
- package/src/core/{base.js → session.js} +56 -53
- package/src/core/simplify.js +52 -59
- package/templates/bash-process.md +12 -5
- package/templates/codingSystem.md +65 -0
- package/templates/codingUser.md +17 -31
- package/templates/coreProtocol.md +29 -0
- package/templates/goSystem.md +130 -0
- package/templates/guidance.json +52 -34
- package/templates/planSystem.md +78 -0
- package/templates/planUser.md +9 -0
- package/templates/playwright.md +16 -16
- package/templates/requirements.example.md +57 -56
- package/templates/scanSystem.md +120 -0
- package/templates/scanUser.md +10 -17
- package/templates/test_rule.md +194 -194
- package/src/core/validator.js +0 -138
- package/templates/addGuide.md +0 -98
- package/templates/addUser.md +0 -26
- package/templates/agentProtocol.md +0 -195
- package/templates/scanProtocol.md +0 -118
package/src/core/repair.js
CHANGED
|
@@ -1,58 +1,46 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { runSession } = require('./
|
|
6
|
-
const { buildQueryOptions } = require('./query');
|
|
7
|
-
const { log } = require('../common/config');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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 };
|
package/src/core/runner.js
CHANGED
|
@@ -1,352 +1,195 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
log('
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
log('
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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 };
|