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.
Files changed (100) hide show
  1. package/.env.example +83 -0
  2. package/API_GUIDE.md +1411 -0
  3. package/AUTO_MKDIR_IMPROVEMENT.md +354 -0
  4. package/CLAUDE.md +55 -0
  5. package/CTRL_C_EXPERIMENT.md +90 -0
  6. package/PROJECT_CLEANUP_SUMMARY.md +121 -0
  7. package/README.md +686 -0
  8. package/cloco.md +51 -0
  9. package/config.example.json +116 -0
  10. package/dist/bash-runner.js +128 -0
  11. package/dist/batch-cli.js +20736 -0
  12. package/dist/closer-cli.js +21190 -0
  13. package/dist/index.js +31228 -0
  14. package/docs/EXPORT_COMMAND.md +152 -0
  15. package/docs/FILE_NAMING_IMPROVEMENT.md +168 -0
  16. package/docs/GLOBAL_CONFIG.md +128 -0
  17. package/docs/LONG_MESSAGE_DISPLAY_FIX.md +202 -0
  18. package/docs/PROJECT_HISTORY_ISOLATION.md +315 -0
  19. package/docs/QUICK_START_HISTORY.md +207 -0
  20. package/docs/TASK_PROGRESS_FEATURE.md +190 -0
  21. package/docs/THINKING_CONTENT_RESEARCH.md +267 -0
  22. package/docs/THINKING_FEATURE.md +187 -0
  23. package/docs/THINKING_IMPROVEMENT_COMPARISON.md +193 -0
  24. package/docs/THINKING_OPTIMIZATION_SUMMARY.md +242 -0
  25. package/docs/UI_IMPROVEMENTS_2025-01-18.md +256 -0
  26. package/docs/WHY_THINKING_SHORT.md +201 -0
  27. package/package.json +49 -0
  28. package/scenarios/README.md +234 -0
  29. package/scenarios/run-all-scenarios.js +342 -0
  30. package/scenarios/scenario1-batch-converter.js +247 -0
  31. package/scenarios/scenario2-code-analyzer.js +375 -0
  32. package/scenarios/scenario3-doc-generator.js +371 -0
  33. package/scenarios/scenario4-log-analyzer.js +496 -0
  34. package/scenarios/scenario5-tdd-helper.js +681 -0
  35. package/src/ai-client-legacy.js +171 -0
  36. package/src/ai-client.js +221 -0
  37. package/src/bash-runner.js +148 -0
  38. package/src/batch-cli.js +327 -0
  39. package/src/cli.jsx +166 -0
  40. package/src/closer-cli.jsx +1103 -0
  41. package/src/closer-cli.jsx.backup +948 -0
  42. package/src/commands/batch.js +62 -0
  43. package/src/commands/chat.js +10 -0
  44. package/src/commands/config.js +154 -0
  45. package/src/commands/help.js +76 -0
  46. package/src/commands/history.js +192 -0
  47. package/src/commands/setup.js +17 -0
  48. package/src/commands/upgrade.js +101 -0
  49. package/src/commands/workflow-tests.js +125 -0
  50. package/src/config.js +343 -0
  51. package/src/conversation.js +962 -0
  52. package/src/git-helper.js +349 -0
  53. package/src/index.js +88 -0
  54. package/src/logger.js +347 -0
  55. package/src/plan.js +193 -0
  56. package/src/planner.js +397 -0
  57. package/src/search.js +195 -0
  58. package/src/setup.js +147 -0
  59. package/src/shortcuts.js +269 -0
  60. package/src/snippets.js +430 -0
  61. package/src/test-modules.js +118 -0
  62. package/src/tools.js +398 -0
  63. package/src/utils/cli.js +124 -0
  64. package/src/utils/validator.js +184 -0
  65. package/src/utils/version.js +33 -0
  66. package/src/utils/workflow-test.js +271 -0
  67. package/src/utils/workflow.js +268 -0
  68. package/test/demo-file-naming.js +92 -0
  69. package/test/demo-thinking.js +124 -0
  70. package/test/final-verification-report.md +303 -0
  71. package/test/research-thinking.js +130 -0
  72. package/test/test-auto-mkdir.js +123 -0
  73. package/test/test-e2e-empty-dir.md +108 -0
  74. package/test/test-export-logic.js +119 -0
  75. package/test/test-global-cloco.js +126 -0
  76. package/test/test-history-isolation.js +291 -0
  77. package/test/test-improved-thinking.js +43 -0
  78. package/test/test-long-message.js +65 -0
  79. package/test/test-plan-functionality.js +95 -0
  80. package/test/test-real-scenario.js +216 -0
  81. package/test/test-thinking-display.js +65 -0
  82. package/test/ui-verification-test.js +203 -0
  83. package/test/verify-history-isolation.sh +71 -0
  84. package/test/verify-thinking.js +339 -0
  85. package/test/workflows/empty-dir-creation.md +51 -0
  86. package/test/workflows/inventor/ascii-teacup.js +199 -0
  87. package/test/workflows/inventor/ascii-teacup.mjs +199 -0
  88. package/test/workflows/inventor/ascii_apple.hs +84 -0
  89. package/test/workflows/inventor/ascii_apple.py +91 -0
  90. package/test/workflows/inventor/cloco.md +3 -0
  91. package/test/workflows/longtalk/cloco.md +19 -0
  92. package/test/workflows/longtalk/emoji_500.txt +63 -0
  93. package/test/workflows/longtalk/emoji_list.txt +20 -0
  94. package/test/workflows/programmer/adder.md +33 -0
  95. package/test/workflows/programmer/expect.md +2 -0
  96. package/test/workflows/programmer/prompt.md +3 -0
  97. package/test/workflows/test-empty-dir-creation.js +113 -0
  98. package/test-ctrl-c.jsx +126 -0
  99. package/test-manual-file-creation.js +151 -0
  100. package/winfix.md +3 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * 场景一:批量文件格式转换器
3
+ *
4
+ * 功能:
5
+ * 1. 使用 searchFiles 查找特定类型的文件
6
+ * 2. 使用 readFile 读取每个文件内容
7
+ * 3. 转换文件格式(例如:JSON -> YAML)
8
+ * 4. 使用 writeFile 写入转换后的文件
9
+ *
10
+ * 工具验证:searchFiles, readFile, writeFile
11
+ */
12
+
13
+ import fs from 'fs/promises';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+ import { glob } from 'glob';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ // 模拟工具执行器
22
+ class MockToolExecutor {
23
+ constructor() {
24
+ this.workingDir = path.resolve(__dirname, '..');
25
+ this.callLog = [];
26
+ }
27
+
28
+ log(toolName, input, result) {
29
+ this.callLog.push({
30
+ tool: toolName,
31
+ input,
32
+ success: result.success,
33
+ timestamp: new Date().toISOString()
34
+ });
35
+ }
36
+
37
+ // searchFiles 工具
38
+ async searchFiles({ pattern, cwd }) {
39
+ const searchDir = cwd ? path.resolve(this.workingDir, cwd) : this.workingDir;
40
+ const files = await glob(pattern, { cwd: searchDir });
41
+
42
+ const result = {
43
+ success: true,
44
+ data: { files, count: files.length }
45
+ };
46
+ this.log('searchFiles', { pattern, cwd }, result);
47
+ return result;
48
+ }
49
+
50
+ // readFile 工具
51
+ async readFile({ filePath, encoding = 'utf-8' }) {
52
+ try {
53
+ const fullPath = path.resolve(this.workingDir, filePath);
54
+ const content = await fs.readFile(fullPath, encoding);
55
+
56
+ const result = {
57
+ success: true,
58
+ data: { content, path: fullPath }
59
+ };
60
+ this.log('readFile', { filePath, encoding }, result);
61
+ return result;
62
+ } catch (error) {
63
+ const result = { success: false, error: error.message };
64
+ this.log('readFile', { filePath }, result);
65
+ return result;
66
+ }
67
+ }
68
+
69
+ // writeFile 工具
70
+ async writeFile({ filePath, content, encoding = 'utf-8' }) {
71
+ try {
72
+ const fullPath = path.resolve(this.workingDir, filePath);
73
+ await fs.writeFile(fullPath, content, encoding);
74
+
75
+ const result = {
76
+ success: true,
77
+ data: { path: fullPath, size: content.length }
78
+ };
79
+ this.log('writeFile', { filePath, size: content.length }, result);
80
+ return result;
81
+ } catch (error) {
82
+ const result = { success: false, error: error.message };
83
+ this.log('writeFile', { filePath }, result);
84
+ return result;
85
+ }
86
+ }
87
+
88
+ // 获取调用日志
89
+ getCallLog() {
90
+ return this.callLog;
91
+ }
92
+
93
+ // 打印统计信息
94
+ printSummary() {
95
+ console.log('\n=== 工具调用统计 ===');
96
+ const stats = {};
97
+ this.callLog.forEach(log => {
98
+ stats[log.tool] = (stats[log.tool] || 0) + 1;
99
+ });
100
+ Object.entries(stats).forEach(([tool, count]) => {
101
+ console.log(` ${tool}: ${count} 次`);
102
+ });
103
+ console.log(` 总计: ${this.callLog.length} 次`);
104
+ }
105
+ }
106
+
107
+ // JSON 到 YAML 的简单转换器
108
+ function jsonToYaml(obj, indent = 0) {
109
+ const spaces = ' '.repeat(indent);
110
+ let yaml = '';
111
+
112
+ for (const [key, value] of Object.entries(obj)) {
113
+ if (Array.isArray(value)) {
114
+ yaml += `${spaces}${key}:\n`;
115
+ value.forEach(item => {
116
+ if (typeof item === 'object' && item !== null) {
117
+ yaml += `${spaces}-\n${jsonToYaml(item, indent + 2)}`;
118
+ } else {
119
+ yaml += `${spaces} - ${JSON.stringify(item)}\n`;
120
+ }
121
+ });
122
+ } else if (typeof value === 'object' && value !== null) {
123
+ yaml += `${spaces}${key}:\n${jsonToYaml(value, indent + 1)}`;
124
+ } else {
125
+ yaml += `${spaces}${key}: ${JSON.stringify(value)}\n`;
126
+ }
127
+ }
128
+
129
+ return yaml;
130
+ }
131
+
132
+ // 批量转换场景
133
+ async function runBatchConverterScenario() {
134
+ console.log('\n========================================');
135
+ console.log('场景一:批量文件格式转换器');
136
+ console.log('========================================\n');
137
+
138
+ const executor = new MockToolExecutor();
139
+
140
+ try {
141
+ // 步骤 1: 搜索所有 JSON 配置文件
142
+ console.log('步骤 1: 搜索 JSON 配置文件...');
143
+ const searchResult = await executor.searchFiles({
144
+ pattern: '*.json',
145
+ cwd: '.'
146
+ });
147
+
148
+ if (!searchResult.success) {
149
+ throw new Error('搜索文件失败');
150
+ }
151
+
152
+ console.log(` 找到 ${searchResult.data.count} 个 JSON 文件`);
153
+ const jsonFiles = searchResult.data.files.filter(f =>
154
+ !f.includes('node_modules') && !f.includes('package-lock.json')
155
+ );
156
+ console.log(` 过滤后: ${jsonFiles.length} 个文件\n`);
157
+
158
+ // 步骤 2: 读取并转换每个文件
159
+ const conversions = [];
160
+ for (const jsonFile of jsonFiles) {
161
+ console.log(`步骤 2: 读取 ${jsonFile}...`);
162
+ const readResult = await executor.readFile({ filePath: jsonFile });
163
+
164
+ if (!readResult.success) {
165
+ console.log(` ✗ 读取失败: ${readResult.error}`);
166
+ continue;
167
+ }
168
+
169
+ try {
170
+ const jsonData = JSON.parse(readResult.data.content);
171
+ const yamlContent = jsonToYaml(jsonData);
172
+
173
+ // 步骤 3: 写入 YAML 文件
174
+ const yamlFile = jsonFile.replace('.json', '.yaml');
175
+ console.log(`步骤 3: 写入 ${yamlFile}...`);
176
+ const writeResult = await executor.writeFile({
177
+ filePath: `scenarios/output/${path.basename(yamlFile)}`,
178
+ content: yamlContent
179
+ });
180
+
181
+ if (writeResult.success) {
182
+ console.log(` ✓ 转换成功: ${jsonFile} -> ${yamlFile}`);
183
+ conversions.push({
184
+ from: jsonFile,
185
+ to: yamlFile,
186
+ size: writeResult.data.size
187
+ });
188
+ }
189
+ } catch (parseError) {
190
+ console.log(` ✗ 解析失败: ${parseError.message}`);
191
+ }
192
+ console.log('');
193
+ }
194
+
195
+ // 打印结果
196
+ console.log('========================================');
197
+ console.log('转换结果汇总');
198
+ console.log('========================================');
199
+ console.log(`成功转换: ${conversions.length} 个文件`);
200
+ conversions.forEach(c => {
201
+ console.log(` ${c.from} -> ${c.to} (${c.size} bytes)`);
202
+ });
203
+
204
+ executor.printSummary();
205
+
206
+ return {
207
+ success: true,
208
+ conversions,
209
+ toolCalls: executor.getCallLog()
210
+ };
211
+ } catch (error) {
212
+ console.error('\n场景执行失败:', error.message);
213
+ return { success: false, error: error.message };
214
+ }
215
+ }
216
+
217
+ // 创建输出目录
218
+ async function ensureOutputDir() {
219
+ const dir = path.resolve(__dirname, 'output');
220
+ try {
221
+ await fs.mkdir(dir, { recursive: true });
222
+ } catch {
223
+ // 目录可能已存在
224
+ }
225
+ }
226
+
227
+ // 主函数
228
+ async function main() {
229
+ await ensureOutputDir();
230
+ const result = await runBatchConverterScenario();
231
+
232
+ if (result.success) {
233
+ console.log('\n✓ 场景一执行成功\n');
234
+ process.exit(0);
235
+ } else {
236
+ console.log('\n✗ 场景一执行失败\n');
237
+ process.exit(1);
238
+ }
239
+ }
240
+
241
+ // 导出以便其他脚本使用
242
+ export { runBatchConverterScenario };
243
+
244
+ // 如果直接运行此脚本
245
+ if (import.meta.url === `file://${process.argv[1]}`) {
246
+ main();
247
+ }
@@ -0,0 +1,375 @@
1
+ /**
2
+ * 场景二:代码分析报告生成器
3
+ *
4
+ * 功能:
5
+ * 1. 使用 searchCode 搜索代码模式(函数定义、类定义等)
6
+ * 2. 使用 bash 执行代码统计命令(cloc, tokei 等)
7
+ * 3. 使用 writeFile 生成分析报告
8
+ *
9
+ * 工具验证:searchCode, bash, writeFile
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
+ import { glob } from 'glob';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ class MockToolExecutor {
22
+ constructor() {
23
+ this.workingDir = path.resolve(__dirname, '..');
24
+ this.callLog = [];
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
+ // searchCode 工具 - 使用 ripgrep 搜索代码
37
+ async searchCode({ pattern, path: searchPath, fileType }) {
38
+ try {
39
+ const searchDir = searchPath ? path.resolve(this.workingDir, searchPath) : this.workingDir;
40
+ const rgPath = path.join(this.workingDir, 'node_modules', '.bin', 'rg');
41
+
42
+ // 构建 ripgrep 命令
43
+ let args = [pattern, searchDir, '--json', '-n'];
44
+ if (fileType) {
45
+ args.push('-t', fileType);
46
+ }
47
+
48
+ const results = [];
49
+ const proc = spawn(rgPath, args, { shell: true });
50
+
51
+ for await (const line of proc.stdout) {
52
+ try {
53
+ const data = JSON.parse(line.toString());
54
+ if (data.type === 'match') {
55
+ results.push({
56
+ file: path.relative(this.workingDir, data.path),
57
+ line: data.line_number,
58
+ text: data.lines.text
59
+ });
60
+ }
61
+ } catch {
62
+ // 忽略解析错误
63
+ }
64
+ }
65
+
66
+ const result = {
67
+ success: true,
68
+ data: { matches: results, count: results.length }
69
+ };
70
+ this.log('searchCode', { pattern, searchPath, fileType }, result);
71
+ return result;
72
+ } catch (error) {
73
+ const result = { success: false, error: error.message };
74
+ this.log('searchCode', { pattern }, result);
75
+ return result;
76
+ }
77
+ }
78
+
79
+ // bash 工具 - 执行 shell 命令
80
+ async bash({ command, timeout = 30000 }) {
81
+ return new Promise((resolve) => {
82
+ const proc = spawn('bash', ['-lc', command], {
83
+ cwd: this.workingDir,
84
+ shell: true,
85
+ env: { ...process.env, PATH: process.env.PATH }
86
+ });
87
+
88
+ let stdout = '';
89
+ let stderr = '';
90
+
91
+ proc.stdout.on('data', (data) => {
92
+ stdout += data.toString();
93
+ });
94
+
95
+ proc.stderr.on('data', (data) => {
96
+ stderr += data.toString();
97
+ });
98
+
99
+ const timer = setTimeout(() => {
100
+ proc.kill('SIGKILL');
101
+ resolve({
102
+ success: false,
103
+ data: { error: 'Command timeout' },
104
+ error: 'Command timeout'
105
+ });
106
+ }, timeout);
107
+
108
+ proc.on('close', (exitCode) => {
109
+ clearTimeout(timer);
110
+ const result = {
111
+ success: exitCode === 0,
112
+ data: {
113
+ stdout: stdout.trim(),
114
+ stderr: stderr.trim(),
115
+ exitCode
116
+ }
117
+ };
118
+ this.log('bash', { command }, result);
119
+ resolve(result);
120
+ });
121
+
122
+ proc.on('error', (error) => {
123
+ clearTimeout(timer);
124
+ const result = {
125
+ success: false,
126
+ data: null,
127
+ error: error.message
128
+ };
129
+ this.log('bash', { command }, result);
130
+ resolve(result);
131
+ });
132
+ });
133
+ }
134
+
135
+ // writeFile 工具
136
+ async writeFile({ filePath, content, encoding = 'utf-8' }) {
137
+ try {
138
+ const fullPath = path.resolve(this.workingDir, filePath);
139
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
140
+ await fs.writeFile(fullPath, content, encoding);
141
+
142
+ const result = {
143
+ success: true,
144
+ data: { path: fullPath, size: content.length }
145
+ };
146
+ this.log('writeFile', { filePath, size: content.length }, result);
147
+ return result;
148
+ } catch (error) {
149
+ const result = { success: false, error: error.message };
150
+ this.log('writeFile', { filePath }, result);
151
+ return result;
152
+ }
153
+ }
154
+
155
+ getCallLog() {
156
+ return this.callLog;
157
+ }
158
+
159
+ printSummary() {
160
+ console.log('\n=== 工具调用统计 ===');
161
+ const stats = {};
162
+ this.callLog.forEach(log => {
163
+ stats[log.tool] = (stats[log.tool] || 0) + 1;
164
+ });
165
+ Object.entries(stats).forEach(([tool, count]) => {
166
+ console.log(` ${tool}: ${count} 次`);
167
+ });
168
+ console.log(` 总计: ${this.callLog.length} 次`);
169
+ }
170
+ }
171
+
172
+ // 代码分析报告生成器
173
+ async function runCodeAnalyzerScenario() {
174
+ console.log('\n========================================');
175
+ console.log('场景二:代码分析报告生成器');
176
+ console.log('========================================\n');
177
+
178
+ const executor = new MockToolExecutor();
179
+
180
+ try {
181
+ const report = {
182
+ timestamp: new Date().toISOString(),
183
+ project: path.basename(executor.workingDir),
184
+ sections: []
185
+ };
186
+
187
+ // 步骤 1: 统计代码行数
188
+ console.log('步骤 1: 统计代码行数...');
189
+ const locResult = await executor.bash({
190
+ command: 'find src -name "*.js" -type f | wc -l'
191
+ });
192
+
193
+ if (locResult.success) {
194
+ const fileCount = locResult.data.stdout;
195
+ console.log(` 找到 ${fileCount} 个 JavaScript 文件`);
196
+
197
+ // 统计总行数
198
+ const linesResult = await executor.bash({
199
+ command: 'find src -name "*.js" -type f -exec cat {} \\; | wc -l'
200
+ });
201
+
202
+ if (linesResult.success) {
203
+ report.sections.push({
204
+ title: '代码行数统计',
205
+ data: {
206
+ jsFiles: parseInt(fileCount),
207
+ totalLines: parseInt(linesResult.data.stdout)
208
+ }
209
+ });
210
+ console.log(` 总行数: ${linesResult.data.stdout}\n`);
211
+ }
212
+ }
213
+
214
+ // 步骤 2: 搜索函数定义
215
+ console.log('步骤 2: 搜索函数定义...');
216
+ const functionResult = await executor.searchCode({
217
+ pattern: 'function\\s+\\w+|const\\s+\\w+\\s*=\\s*(async\\s*)?\\(',
218
+ path: 'src',
219
+ fileType: 'js'
220
+ });
221
+
222
+ if (functionResult.success) {
223
+ console.log(` 找到 ${functionResult.data.count} 个函数定义`);
224
+ report.sections.push({
225
+ title: '函数定义统计',
226
+ data: {
227
+ count: functionResult.data.count,
228
+ samples: functionResult.data.matches.slice(0, 5).map(m => ({
229
+ file: m.file,
230
+ line: m.line,
231
+ text: m.text.trim().substring(0, 60)
232
+ }))
233
+ }
234
+ });
235
+ console.log(` 示例:\n`);
236
+ functionResult.data.matches.slice(0, 3).forEach(m => {
237
+ console.log(` ${m.file}:${m.line} - ${m.text.trim().substring(0, 50)}...`);
238
+ });
239
+ console.log('');
240
+ }
241
+
242
+ // 步骤 3: 搜索类定义
243
+ console.log('步骤 3: 搜索类定义...');
244
+ const classResult = await executor.searchCode({
245
+ pattern: 'class\\s+\\w+',
246
+ path: 'src',
247
+ fileType: 'js'
248
+ });
249
+
250
+ if (classResult.success) {
251
+ console.log(` 找到 ${classResult.data.count} 个类定义`);
252
+ report.sections.push({
253
+ title: '类定义统计',
254
+ data: {
255
+ count: classResult.data.count,
256
+ classes: classResult.data.matches.map(m => ({
257
+ file: m.file,
258
+ line: m.line,
259
+ name: m.text.match(/class\s+(\w+)/)?.[1] || 'Unknown'
260
+ }))
261
+ }
262
+ });
263
+ console.log('');
264
+ }
265
+
266
+ // 步骤 4: 搜索导入语句
267
+ console.log('步骤 4: 分析依赖关系...');
268
+ const importResult = await executor.searchCode({
269
+ pattern: '^import\\s+.*from',
270
+ path: 'src',
271
+ fileType: 'js'
272
+ });
273
+
274
+ if (importResult.success) {
275
+ const dependencies = new Set();
276
+ importResult.data.matches.forEach(m => {
277
+ const match = m.text.match(/from\s+['"]([^'"]+)['"]/);
278
+ if (match && !match[1].startsWith('.')) {
279
+ dependencies.add(match[1]);
280
+ }
281
+ });
282
+
283
+ console.log(` 找到 ${importResult.data.count} 个导入语句`);
284
+ console.log(` 外部依赖: ${dependencies.size} 个\n`);
285
+
286
+ report.sections.push({
287
+ title: '依赖关系分析',
288
+ data: {
289
+ totalImports: importResult.data.count,
290
+ externalDependencies: Array.from(dependencies)
291
+ }
292
+ });
293
+ }
294
+
295
+ // 步骤 5: 生成 Markdown 报告
296
+ console.log('步骤 5: 生成分析报告...');
297
+ let markdown = `# 代码分析报告\n\n`;
298
+ markdown += `- 项目: ${report.project}\n`;
299
+ markdown += `- 生成时间: ${report.timestamp}\n\n`;
300
+
301
+ report.sections.forEach(section => {
302
+ markdown += `## ${section.title}\n\n`;
303
+ if (section.title === '代码行数统计') {
304
+ markdown += `- JavaScript 文件数: ${section.data.jsFiles}\n`;
305
+ markdown += `- 总代码行数: ${section.data.totalLines}\n\n`;
306
+ } else if (section.title === '函数定义统计') {
307
+ markdown += `- 函数总数: ${section.data.count}\n\n`;
308
+ markdown += `### 示例函数\n\n`;
309
+ section.data.samples.forEach(s => {
310
+ markdown += `\`${s.file}:${s.line}\` - ${s.text}\n\n`;
311
+ });
312
+ } else if (section.title === '类定义统计') {
313
+ markdown += `- 类总数: ${section.data.count}\n\n`;
314
+ markdown += `### 类列表\n\n`;
315
+ section.data.classes.forEach(c => {
316
+ markdown += `- \`${c.name}\` (${c.file}:${c.line})\n`;
317
+ });
318
+ markdown += '\n';
319
+ } else if (section.title === '依赖关系分析') {
320
+ markdown += `- 总导入语句: ${section.data.totalImports}\n`;
321
+ markdown += `- 外部依赖: ${section.data.externalDependencies.length}\n\n`;
322
+ markdown += `### 外部依赖列表\n\n`;
323
+ section.data.externalDependencies.forEach(dep => {
324
+ markdown += `- ${dep}\n`;
325
+ });
326
+ markdown += '\n';
327
+ }
328
+ });
329
+
330
+ const writeResult = await executor.writeFile({
331
+ filePath: 'scenarios/output/code-analysis-report.md',
332
+ content: markdown
333
+ });
334
+
335
+ if (writeResult.success) {
336
+ console.log(` ✓ 报告已生成: ${writeResult.data.path} (${writeResult.data.size} bytes)\n`);
337
+ }
338
+
339
+ // 打印统计
340
+ console.log('========================================');
341
+ console.log('分析完成');
342
+ console.log('========================================');
343
+ report.sections.forEach(section => {
344
+ console.log(`${section.title}: ✓`);
345
+ });
346
+ executor.printSummary();
347
+
348
+ return {
349
+ success: true,
350
+ report,
351
+ toolCalls: executor.getCallLog()
352
+ };
353
+ } catch (error) {
354
+ console.error('\n场景执行失败:', error.message);
355
+ return { success: false, error: error.message };
356
+ }
357
+ }
358
+
359
+ async function main() {
360
+ const result = await runCodeAnalyzerScenario();
361
+
362
+ if (result.success) {
363
+ console.log('\n✓ 场景二执行成功\n');
364
+ process.exit(0);
365
+ } else {
366
+ console.log('\n✗ 场景二执行失败\n');
367
+ process.exit(1);
368
+ }
369
+ }
370
+
371
+ export { runCodeAnalyzerScenario };
372
+
373
+ if (import.meta.url === `file://${process.argv[1]}`) {
374
+ main();
375
+ }