closer-code 1.0.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.
- package/.env.example +83 -0
- package/API_GUIDE.md +1411 -0
- package/AUTO_MKDIR_IMPROVEMENT.md +354 -0
- package/CLAUDE.md +55 -0
- package/CTRL_C_EXPERIMENT.md +90 -0
- package/PROJECT_CLEANUP_SUMMARY.md +121 -0
- package/README.md +686 -0
- package/cloco.md +51 -0
- package/config.example.json +116 -0
- package/dist/bash-runner.js +128 -0
- package/dist/batch-cli.js +20736 -0
- package/dist/closer-cli.js +21190 -0
- package/dist/index.js +31228 -0
- package/docs/EXPORT_COMMAND.md +152 -0
- package/docs/FILE_NAMING_IMPROVEMENT.md +168 -0
- package/docs/GLOBAL_CONFIG.md +128 -0
- package/docs/LONG_MESSAGE_DISPLAY_FIX.md +202 -0
- package/docs/PROJECT_HISTORY_ISOLATION.md +315 -0
- package/docs/QUICK_START_HISTORY.md +207 -0
- package/docs/TASK_PROGRESS_FEATURE.md +190 -0
- package/docs/THINKING_CONTENT_RESEARCH.md +267 -0
- package/docs/THINKING_FEATURE.md +187 -0
- package/docs/THINKING_IMPROVEMENT_COMPARISON.md +193 -0
- package/docs/THINKING_OPTIMIZATION_SUMMARY.md +242 -0
- package/docs/UI_IMPROVEMENTS_2025-01-18.md +256 -0
- package/docs/WHY_THINKING_SHORT.md +201 -0
- package/package.json +49 -0
- package/scenarios/README.md +234 -0
- package/scenarios/run-all-scenarios.js +342 -0
- package/scenarios/scenario1-batch-converter.js +247 -0
- package/scenarios/scenario2-code-analyzer.js +375 -0
- package/scenarios/scenario3-doc-generator.js +371 -0
- package/scenarios/scenario4-log-analyzer.js +496 -0
- package/scenarios/scenario5-tdd-helper.js +681 -0
- package/src/ai-client-legacy.js +171 -0
- package/src/ai-client.js +221 -0
- package/src/bash-runner.js +148 -0
- package/src/batch-cli.js +327 -0
- package/src/cli.jsx +166 -0
- package/src/closer-cli.jsx +1103 -0
- package/src/closer-cli.jsx.backup +948 -0
- package/src/commands/batch.js +62 -0
- package/src/commands/chat.js +10 -0
- package/src/commands/config.js +154 -0
- package/src/commands/help.js +76 -0
- package/src/commands/history.js +192 -0
- package/src/commands/setup.js +17 -0
- package/src/commands/upgrade.js +101 -0
- package/src/commands/workflow-tests.js +125 -0
- package/src/config.js +343 -0
- package/src/conversation.js +962 -0
- package/src/git-helper.js +349 -0
- package/src/index.js +88 -0
- package/src/logger.js +347 -0
- package/src/plan.js +193 -0
- package/src/planner.js +397 -0
- package/src/search.js +195 -0
- package/src/setup.js +147 -0
- package/src/shortcuts.js +269 -0
- package/src/snippets.js +430 -0
- package/src/test-modules.js +118 -0
- package/src/tools.js +398 -0
- package/src/utils/cli.js +124 -0
- package/src/utils/validator.js +184 -0
- package/src/utils/version.js +33 -0
- package/src/utils/workflow-test.js +271 -0
- package/src/utils/workflow.js +268 -0
- package/test/demo-file-naming.js +92 -0
- package/test/demo-thinking.js +124 -0
- package/test/final-verification-report.md +303 -0
- package/test/research-thinking.js +130 -0
- package/test/test-auto-mkdir.js +123 -0
- package/test/test-e2e-empty-dir.md +108 -0
- package/test/test-export-logic.js +119 -0
- package/test/test-global-cloco.js +126 -0
- package/test/test-history-isolation.js +291 -0
- package/test/test-improved-thinking.js +43 -0
- package/test/test-long-message.js +65 -0
- package/test/test-plan-functionality.js +95 -0
- package/test/test-real-scenario.js +216 -0
- package/test/test-thinking-display.js +65 -0
- package/test/ui-verification-test.js +203 -0
- package/test/verify-history-isolation.sh +71 -0
- package/test/verify-thinking.js +339 -0
- package/test/workflows/empty-dir-creation.md +51 -0
- package/test/workflows/inventor/ascii-teacup.js +199 -0
- package/test/workflows/inventor/ascii-teacup.mjs +199 -0
- package/test/workflows/inventor/ascii_apple.hs +84 -0
- package/test/workflows/inventor/ascii_apple.py +91 -0
- package/test/workflows/inventor/cloco.md +3 -0
- package/test/workflows/longtalk/cloco.md +19 -0
- package/test/workflows/longtalk/emoji_500.txt +63 -0
- package/test/workflows/longtalk/emoji_list.txt +20 -0
- package/test/workflows/programmer/adder.md +33 -0
- package/test/workflows/programmer/expect.md +2 -0
- package/test/workflows/programmer/prompt.md +3 -0
- package/test/workflows/test-empty-dir-creation.js +113 -0
- package/test-ctrl-c.jsx +126 -0
- package/test-manual-file-creation.js +151 -0
- package/winfix.md +3 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 场景四:进程监控与日志分析器
|
|
3
|
+
*
|
|
4
|
+
* 功能:
|
|
5
|
+
* 1. 使用 bash 监控进程状态和资源使用
|
|
6
|
+
* 2. 使用 editFile 创建和更新日志文件
|
|
7
|
+
* 3. 使用 analyzeError 分析错误日志
|
|
8
|
+
*
|
|
9
|
+
* 工具验证:bash, editFile, analyzeError
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs/promises';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { spawn } from 'child_process';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
class MockToolExecutor {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.workingDir = path.resolve(__dirname, '..');
|
|
23
|
+
this.callLog = [];
|
|
24
|
+
this.errorDatabase = new Map();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
log(toolName, input, result) {
|
|
28
|
+
this.callLog.push({
|
|
29
|
+
tool: toolName,
|
|
30
|
+
input,
|
|
31
|
+
success: result.success,
|
|
32
|
+
timestamp: new Date().toISOString()
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// bash 工具
|
|
37
|
+
async bash({ command, timeout = 30000 }) {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const proc = spawn('bash', ['-lc', command], {
|
|
40
|
+
cwd: this.workingDir,
|
|
41
|
+
shell: true,
|
|
42
|
+
env: { ...process.env }
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
let stdout = '';
|
|
46
|
+
let stderr = '';
|
|
47
|
+
|
|
48
|
+
proc.stdout.on('data', (data) => {
|
|
49
|
+
stdout += data.toString();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
proc.stderr.on('data', (data) => {
|
|
53
|
+
stderr += data.toString();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const timer = setTimeout(() => {
|
|
57
|
+
proc.kill('SIGKILL');
|
|
58
|
+
resolve({
|
|
59
|
+
success: false,
|
|
60
|
+
data: { error: 'Command timeout' },
|
|
61
|
+
error: 'Command timeout'
|
|
62
|
+
});
|
|
63
|
+
}, timeout);
|
|
64
|
+
|
|
65
|
+
proc.on('close', (exitCode) => {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
const result = {
|
|
68
|
+
success: exitCode === 0,
|
|
69
|
+
data: {
|
|
70
|
+
stdout: stdout.trim(),
|
|
71
|
+
stderr: stderr.trim(),
|
|
72
|
+
exitCode
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
this.log('bash', { command }, result);
|
|
76
|
+
resolve(result);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
proc.on('error', (error) => {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
const result = {
|
|
82
|
+
success: false,
|
|
83
|
+
data: null,
|
|
84
|
+
error: error.message
|
|
85
|
+
};
|
|
86
|
+
this.log('bash', { command }, result);
|
|
87
|
+
resolve(result);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// editFile 工具
|
|
93
|
+
async editFile({ filePath, oldText, newText, replaceAll = false }) {
|
|
94
|
+
try {
|
|
95
|
+
const fullPath = path.resolve(this.workingDir, filePath);
|
|
96
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
97
|
+
|
|
98
|
+
let content = '';
|
|
99
|
+
try {
|
|
100
|
+
content = await fs.readFile(fullPath, 'utf-8');
|
|
101
|
+
} catch {
|
|
102
|
+
// 文件不存在,创建新文件
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let replacements = 0;
|
|
106
|
+
if (replaceAll) {
|
|
107
|
+
const matches = content.split(oldText);
|
|
108
|
+
replacements = matches.length - 1;
|
|
109
|
+
content = matches.join(newText);
|
|
110
|
+
} else {
|
|
111
|
+
if (oldText && !content.includes(oldText)) {
|
|
112
|
+
// 如果文件为空或oldText不存在,直接添加新内容
|
|
113
|
+
content = newText;
|
|
114
|
+
replacements = 1;
|
|
115
|
+
} else {
|
|
116
|
+
content = content.replace(oldText, newText);
|
|
117
|
+
replacements = 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await fs.writeFile(fullPath, content, 'utf-8');
|
|
122
|
+
|
|
123
|
+
const result = {
|
|
124
|
+
success: true,
|
|
125
|
+
data: { path: fullPath, replacements, contentLength: content.length }
|
|
126
|
+
};
|
|
127
|
+
this.log('editFile', { filePath, replacements }, result);
|
|
128
|
+
return result;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
const result = { success: false, error: error.message };
|
|
131
|
+
this.log('editFile', { filePath }, result);
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// analyzeError 工具
|
|
137
|
+
async analyzeError({ error, context }) {
|
|
138
|
+
try {
|
|
139
|
+
const analysis = {
|
|
140
|
+
error,
|
|
141
|
+
context,
|
|
142
|
+
timestamp: new Date().toISOString(),
|
|
143
|
+
patterns: [],
|
|
144
|
+
severity: 'unknown',
|
|
145
|
+
suggestions: []
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// 分析错误类型
|
|
149
|
+
if (error.includes('ECONNREFUSED')) {
|
|
150
|
+
analysis.patterns.push('Connection Refused');
|
|
151
|
+
analysis.severity = 'high';
|
|
152
|
+
analysis.suggestions.push('检查服务是否运行');
|
|
153
|
+
analysis.suggestions.push('验证端口是否正确');
|
|
154
|
+
} else if (error.includes('ENOENT')) {
|
|
155
|
+
analysis.patterns.push('File Not Found');
|
|
156
|
+
analysis.severity = 'medium';
|
|
157
|
+
analysis.suggestions.push('检查文件路径是否正确');
|
|
158
|
+
analysis.suggestions.push('确认文件是否存在');
|
|
159
|
+
} else if (error.includes('SyntaxError')) {
|
|
160
|
+
analysis.patterns.push('Syntax Error');
|
|
161
|
+
analysis.severity = 'high';
|
|
162
|
+
analysis.suggestions.push('检查代码语法');
|
|
163
|
+
analysis.suggestions.push('验证括号和引号匹配');
|
|
164
|
+
} else if (error.includes('TypeError')) {
|
|
165
|
+
analysis.patterns.push('Type Error');
|
|
166
|
+
analysis.severity = 'medium';
|
|
167
|
+
analysis.suggestions.push('检查数据类型');
|
|
168
|
+
analysis.suggestions.push('验证变量是否已定义');
|
|
169
|
+
} else if (error.includes('ENOMEM')) {
|
|
170
|
+
analysis.patterns.push('Out of Memory');
|
|
171
|
+
analysis.severity = 'critical';
|
|
172
|
+
analysis.suggestions.push('增加可用内存');
|
|
173
|
+
analysis.suggestions.push('检查内存泄漏');
|
|
174
|
+
} else if (error.includes('ETIMEDOUT')) {
|
|
175
|
+
analysis.patterns.push('Timeout');
|
|
176
|
+
analysis.severity = 'medium';
|
|
177
|
+
analysis.suggestions.push('增加超时时间');
|
|
178
|
+
analysis.suggestions.push('检查网络连接');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 存储到错误数据库
|
|
182
|
+
const errorKey = error.split(':')[0];
|
|
183
|
+
if (!this.errorDatabase.has(errorKey)) {
|
|
184
|
+
this.errorDatabase.set(errorKey, []);
|
|
185
|
+
}
|
|
186
|
+
this.errorDatabase.get(errorKey).push({
|
|
187
|
+
error,
|
|
188
|
+
context,
|
|
189
|
+
analysis,
|
|
190
|
+
timestamp: analysis.timestamp
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const result = {
|
|
194
|
+
success: true,
|
|
195
|
+
data: analysis
|
|
196
|
+
};
|
|
197
|
+
this.log('analyzeError', { error: errorKey, severity: analysis.severity }, result);
|
|
198
|
+
return result;
|
|
199
|
+
} catch (err) {
|
|
200
|
+
const result = { success: false, error: err.message };
|
|
201
|
+
this.log('analyzeError', { error }, result);
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
getCallLog() {
|
|
207
|
+
return this.callLog;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
getErrorDatabase() {
|
|
211
|
+
return this.errorDatabase;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
printSummary() {
|
|
215
|
+
console.log('\n=== 工具调用统计 ===');
|
|
216
|
+
const stats = {};
|
|
217
|
+
this.callLog.forEach(log => {
|
|
218
|
+
stats[log.tool] = (stats[log.tool] || 0) + 1;
|
|
219
|
+
});
|
|
220
|
+
Object.entries(stats).forEach(([tool, count]) => {
|
|
221
|
+
console.log(` ${tool}: ${count} 次`);
|
|
222
|
+
});
|
|
223
|
+
console.log(` 总计: ${this.callLog.length} 次`);
|
|
224
|
+
|
|
225
|
+
if (this.errorDatabase.size > 0) {
|
|
226
|
+
console.log('\n=== 错误分析统计 ===');
|
|
227
|
+
this.errorDatabase.forEach((errors, type) => {
|
|
228
|
+
console.log(` ${type}: ${errors.length} 次`);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 进程监控与日志分析场景
|
|
235
|
+
async function runLogAnalyzerScenario() {
|
|
236
|
+
console.log('\n========================================');
|
|
237
|
+
console.log('场景四:进程监控与日志分析器');
|
|
238
|
+
console.log('========================================\n');
|
|
239
|
+
|
|
240
|
+
const executor = new MockToolExecutor();
|
|
241
|
+
const logFilePath = 'scenarios/output/process-monitor.log';
|
|
242
|
+
const reportPath = 'scenarios/output/error-analysis-report.md';
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const monitoringData = {
|
|
246
|
+
startTime: new Date().toISOString(),
|
|
247
|
+
checks: [],
|
|
248
|
+
errors: [],
|
|
249
|
+
processes: []
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// 步骤 1: 检查系统资源
|
|
253
|
+
console.log('步骤 1: 系统资源检查...');
|
|
254
|
+
|
|
255
|
+
// 检查 Node.js 进程
|
|
256
|
+
console.log(' 检查 Node.js 进程...');
|
|
257
|
+
const nodeProcResult = await executor.bash({
|
|
258
|
+
command: 'ps aux | grep -i node | grep -v grep | head -5 || echo "No Node processes found"'
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (nodeProcResult.success && nodeProcResult.data.stdout) {
|
|
262
|
+
monitoringData.processes.push({
|
|
263
|
+
type: 'nodejs',
|
|
264
|
+
output: nodeProcResult.data.stdout
|
|
265
|
+
});
|
|
266
|
+
console.log(' ✓ Node.js 进程检查完成');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 检查内存使用
|
|
270
|
+
console.log(' 检查内存使用...');
|
|
271
|
+
const memResult = await executor.bash({
|
|
272
|
+
command: 'free -h 2>/dev/null || vmstat 2>/dev/null || echo "Memory info not available"'
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
if (memResult.success) {
|
|
276
|
+
monitoringData.checks.push({
|
|
277
|
+
type: 'memory',
|
|
278
|
+
data: memResult.data.stdout,
|
|
279
|
+
timestamp: new Date().toISOString()
|
|
280
|
+
});
|
|
281
|
+
console.log(' ✓ 内存检查完成');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 检查磁盘使用
|
|
285
|
+
console.log(' 检查磁盘使用...');
|
|
286
|
+
const diskResult = await executor.bash({
|
|
287
|
+
command: 'df -h . | tail -1'
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (diskResult.success && diskResult.data.stdout) {
|
|
291
|
+
monitoringData.checks.push({
|
|
292
|
+
type: 'disk',
|
|
293
|
+
data: diskResult.data.stdout,
|
|
294
|
+
timestamp: new Date().toISOString()
|
|
295
|
+
});
|
|
296
|
+
console.log(' ✓ 磁盘检查完成');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
console.log('');
|
|
300
|
+
|
|
301
|
+
// 步骤 2: 模拟各种错误并分析
|
|
302
|
+
console.log('步骤 2: 错误分析与处理...\n');
|
|
303
|
+
|
|
304
|
+
// 模拟连接错误
|
|
305
|
+
console.log(' 测试 1: 连接错误分析');
|
|
306
|
+
await executor.editFile({
|
|
307
|
+
filePath: logFilePath,
|
|
308
|
+
oldText: '',
|
|
309
|
+
newText: `[${new Date().toISOString()}] ERROR: ECONNREFUSED - Connection refused to localhost:9999\n`
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const connError = await executor.analyzeError({
|
|
313
|
+
error: 'ECONNREFUSED: Connection refused',
|
|
314
|
+
context: '尝试连接到 localhost:9999'
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
if (connError.success) {
|
|
318
|
+
console.log(` 严重性: ${connError.data.severity}`);
|
|
319
|
+
console.log(` 建议: ${connError.data.suggestions.join(', ')}`);
|
|
320
|
+
monitoringData.errors.push(connError.data);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 模拟文件错误
|
|
324
|
+
console.log(' 测试 2: 文件错误分析');
|
|
325
|
+
await executor.editFile({
|
|
326
|
+
filePath: logFilePath,
|
|
327
|
+
oldText: '\n',
|
|
328
|
+
newText: `[${new Date().toISOString()}] ERROR: ENOENT - File not found: /nonexistent/file.txt\n`,
|
|
329
|
+
replaceAll: false
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const fileError = await executor.analyzeError({
|
|
333
|
+
error: 'ENOENT: no such file or directory',
|
|
334
|
+
context: '尝试读取不存在的文件'
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (fileError.success) {
|
|
338
|
+
console.log(` 严重性: ${fileError.data.severity}`);
|
|
339
|
+
console.log(` 建议: ${fileError.data.suggestions.join(', ')}`);
|
|
340
|
+
monitoringData.errors.push(fileError.data);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 模拟超时错误
|
|
344
|
+
console.log(' 测试 3: 超时错误分析');
|
|
345
|
+
await executor.editFile({
|
|
346
|
+
filePath: logFilePath,
|
|
347
|
+
oldText: '\n',
|
|
348
|
+
newText: `[${new Date().toISOString()}] ERROR: ETIMEDOUT - Operation timed out after 30000ms\n`,
|
|
349
|
+
replaceAll: false
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const timeoutError = await executor.analyzeError({
|
|
353
|
+
error: 'ETIMEDOUT: Operation timed out',
|
|
354
|
+
context: 'API 请求超时'
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
if (timeoutError.success) {
|
|
358
|
+
console.log(` 严重性: ${timeoutError.data.severity}`);
|
|
359
|
+
console.log(` 建议: ${timeoutError.data.suggestions.join(', ')}`);
|
|
360
|
+
monitoringData.errors.push(timeoutError.data);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 步骤 3: 检查项目构建状态
|
|
364
|
+
console.log('\n步骤 3: 项目构建检查...');
|
|
365
|
+
|
|
366
|
+
const buildCheckResult = await executor.bash({
|
|
367
|
+
command: 'node --check dist/closer-cli.js 2>&1 || echo "Build check failed"'
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
if (buildCheckResult.success) {
|
|
371
|
+
console.log(' ✓ 构建文件检查通过');
|
|
372
|
+
monitoringData.checks.push({
|
|
373
|
+
type: 'build',
|
|
374
|
+
status: 'success',
|
|
375
|
+
data: buildCheckResult.data.stdout,
|
|
376
|
+
timestamp: new Date().toISOString()
|
|
377
|
+
});
|
|
378
|
+
} else {
|
|
379
|
+
console.log(' ✗ 构建文件检查失败');
|
|
380
|
+
|
|
381
|
+
// 分析构建错误
|
|
382
|
+
const buildError = await executor.analyzeError({
|
|
383
|
+
error: buildCheckResult.data.stderr || 'Unknown build error',
|
|
384
|
+
context: '构建文件语法检查'
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
if (buildError.success) {
|
|
388
|
+
monitoringData.errors.push(buildError.data);
|
|
389
|
+
console.log(` 错误类型: ${buildError.data.patterns.join(', ')}`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
monitoringData.checks.push({
|
|
393
|
+
type: 'build',
|
|
394
|
+
status: 'failed',
|
|
395
|
+
error: buildCheckResult.data.stderr,
|
|
396
|
+
timestamp: new Date().toISOString()
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 步骤 4: 生成分析报告
|
|
401
|
+
console.log('\n步骤 4: 生成分析报告...');
|
|
402
|
+
|
|
403
|
+
let report = `# 进程监控与错误分析报告\n\n`;
|
|
404
|
+
report += `**生成时间**: ${new Date().toISOString()}\n\n`;
|
|
405
|
+
report += `---\n\n`;
|
|
406
|
+
|
|
407
|
+
report += `## 监控摘要\n\n`;
|
|
408
|
+
report += `- 检查项目: ${monitoringData.checks.length}\n`;
|
|
409
|
+
report += `- 错误数量: ${monitoringData.errors.length}\n`;
|
|
410
|
+
report += `- 监控进程: ${monitoringData.processes.length}\n\n`;
|
|
411
|
+
|
|
412
|
+
// 系统检查结果
|
|
413
|
+
report += `## 系统检查结果\n\n`;
|
|
414
|
+
monitoringData.checks.forEach(check => {
|
|
415
|
+
report += `### ${check.type.toUpperCase()} 检查\n\n`;
|
|
416
|
+
if (check.type === 'build') {
|
|
417
|
+
report += `- 状态: ${check.status === 'success' ? '✓ 通过' : '✗ 失败'}\n`;
|
|
418
|
+
} else {
|
|
419
|
+
report += `\`\`\`\n${check.data}\n\`\`\`\n\n`;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// 错误分析
|
|
424
|
+
if (monitoringData.errors.length > 0) {
|
|
425
|
+
report += `## 错误分析\n\n`;
|
|
426
|
+
monitoringData.errors.forEach((error, idx) => {
|
|
427
|
+
report += `### 错误 ${idx + 1}: ${error.patterns.join(' / ')}\n\n`;
|
|
428
|
+
report += `- **严重性**: ${error.severity}\n`;
|
|
429
|
+
report += `- **时间**: ${error.timestamp}\n`;
|
|
430
|
+
report += `- **错误信息**: ${error.error}\n`;
|
|
431
|
+
report += `- **上下文**: ${error.context}\n\n`;
|
|
432
|
+
report += `**建议措施**:\n\n`;
|
|
433
|
+
error.suggestions.forEach(s => {
|
|
434
|
+
report += `- ${s}\n`;
|
|
435
|
+
});
|
|
436
|
+
report += '\n';
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 错误统计
|
|
441
|
+
report += `## 错误统计\n\n`;
|
|
442
|
+
const errorDb = executor.getErrorDatabase();
|
|
443
|
+
errorDb.forEach((errors, type) => {
|
|
444
|
+
report += `- **${type}**: ${errors.length} 次\n`;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// 保存报告
|
|
448
|
+
const writeResult = await executor.editFile({
|
|
449
|
+
filePath: reportPath,
|
|
450
|
+
oldText: '',
|
|
451
|
+
newText: report
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
if (writeResult.success) {
|
|
455
|
+
console.log(` ✓ 报告已保存: ${writeResult.data.path}`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// 打印总结
|
|
459
|
+
console.log('\n========================================');
|
|
460
|
+
console.log('监控与分析完成');
|
|
461
|
+
console.log('========================================');
|
|
462
|
+
console.log(`系统检查: ${monitoringData.checks.length} 项`);
|
|
463
|
+
console.log(`错误分析: ${monitoringData.errors.length} 个`);
|
|
464
|
+
console.log(`日志文件: ${logFilePath}`);
|
|
465
|
+
console.log(`分析报告: ${reportPath}`);
|
|
466
|
+
|
|
467
|
+
executor.printSummary();
|
|
468
|
+
|
|
469
|
+
return {
|
|
470
|
+
success: true,
|
|
471
|
+
monitoringData,
|
|
472
|
+
toolCalls: executor.getCallLog()
|
|
473
|
+
};
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.error('\n场景执行失败:', error.message);
|
|
476
|
+
return { success: false, error: error.message };
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function main() {
|
|
481
|
+
const result = await runLogAnalyzerScenario();
|
|
482
|
+
|
|
483
|
+
if (result.success) {
|
|
484
|
+
console.log('\n✓ 场景四执行成功\n');
|
|
485
|
+
process.exit(0);
|
|
486
|
+
} else {
|
|
487
|
+
console.log('\n✗ 场景四执行失败\n');
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export { runLogAnalyzerScenario };
|
|
493
|
+
|
|
494
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
495
|
+
main();
|
|
496
|
+
}
|