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,371 @@
1
+ /**
2
+ * 场景三:项目文档自动生成器
3
+ *
4
+ * 功能:
5
+ * 1. 使用 listFiles 递归列出项目文件
6
+ * 2. 使用 readFile 读取关键文件(README, package.json 等)
7
+ * 3. 使用 writeFile 生成结构化的项目文档
8
+ *
9
+ * 工具验证:listFiles, readFile, writeFile
10
+ */
11
+
12
+ import fs from 'fs/promises';
13
+ import path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ class MockToolExecutor {
20
+ constructor() {
21
+ this.workingDir = path.resolve(__dirname, '..');
22
+ this.callLog = [];
23
+ }
24
+
25
+ log(toolName, input, result) {
26
+ this.callLog.push({
27
+ tool: toolName,
28
+ input,
29
+ success: result.success,
30
+ timestamp: new Date().toISOString()
31
+ });
32
+ }
33
+
34
+ // listFiles 工具 - 递归列出目录
35
+ async listFiles({ dirPath, recursive = true, showHidden = false }) {
36
+ try {
37
+ const fullPath = dirPath ? path.resolve(this.workingDir, dirPath) : this.workingDir;
38
+ const files = [];
39
+
40
+ const walk = async (currentDir, baseRelative = '') => {
41
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
42
+
43
+ for (const entry of entries) {
44
+ const relativePath = path.join(baseRelative, entry.name);
45
+
46
+ // 跳过特定目录
47
+ if (['node_modules', '.git', 'dist', 'coverage'].includes(entry.name)) {
48
+ continue;
49
+ }
50
+
51
+ if (!showHidden && entry.name.startsWith('.')) {
52
+ continue;
53
+ }
54
+
55
+ files.push({
56
+ name: entry.name,
57
+ type: entry.isDirectory() ? 'directory' : 'file',
58
+ path: relativePath,
59
+ fullPath: path.join(currentDir, entry.name)
60
+ });
61
+
62
+ if (entry.isDirectory() && recursive) {
63
+ await walk(path.join(currentDir, entry.name), relativePath);
64
+ }
65
+ }
66
+ };
67
+
68
+ await walk(fullPath);
69
+
70
+ const result = {
71
+ success: true,
72
+ data: {
73
+ files,
74
+ count: files.length,
75
+ path: fullPath
76
+ }
77
+ };
78
+ this.log('listFiles', { dirPath, recursive, showHidden }, result);
79
+ return result;
80
+ } catch (error) {
81
+ const result = { success: false, error: error.message };
82
+ this.log('listFiles', { dirPath }, result);
83
+ return result;
84
+ }
85
+ }
86
+
87
+ // readFile 工具
88
+ async readFile({ filePath, encoding = 'utf-8' }) {
89
+ try {
90
+ const fullPath = path.resolve(this.workingDir, filePath);
91
+ const content = await fs.readFile(fullPath, encoding);
92
+
93
+ const result = {
94
+ success: true,
95
+ data: { content, path: fullPath }
96
+ };
97
+ this.log('readFile', { filePath, encoding }, result);
98
+ return result;
99
+ } catch (error) {
100
+ const result = { success: false, error: error.message };
101
+ this.log('readFile', { filePath }, result);
102
+ return result;
103
+ }
104
+ }
105
+
106
+ // writeFile 工具
107
+ async writeFile({ filePath, content, encoding = 'utf-8' }) {
108
+ try {
109
+ const fullPath = path.resolve(this.workingDir, filePath);
110
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
111
+ await fs.writeFile(fullPath, content, encoding);
112
+
113
+ const result = {
114
+ success: true,
115
+ data: { path: fullPath, size: content.length }
116
+ };
117
+ this.log('writeFile', { filePath, size: content.length }, result);
118
+ return result;
119
+ } catch (error) {
120
+ const result = { success: false, error: error.message };
121
+ this.log('writeFile', { filePath }, result);
122
+ return result;
123
+ }
124
+ }
125
+
126
+ getCallLog() {
127
+ return this.callLog;
128
+ }
129
+
130
+ printSummary() {
131
+ console.log('\n=== 工具调用统计 ===');
132
+ const stats = {};
133
+ this.callLog.forEach(log => {
134
+ stats[log.tool] = (stats[log.tool] || 0) + 1;
135
+ });
136
+ Object.entries(stats).forEach(([tool, count]) => {
137
+ console.log(` ${tool}: ${count} 次`);
138
+ });
139
+ console.log(` 总计: ${this.callLog.length} 次`);
140
+ }
141
+ }
142
+
143
+ // 项目文档生成器
144
+ async function runDocGeneratorScenario() {
145
+ console.log('\n========================================');
146
+ console.log('场景三:项目文档自动生成器');
147
+ console.log('========================================\n');
148
+
149
+ const executor = new MockToolExecutor();
150
+
151
+ try {
152
+ // 步骤 1: 列出项目文件
153
+ console.log('步骤 1: 扫描项目文件结构...');
154
+ const listResult = await executor.listFiles({
155
+ dirPath: '.',
156
+ recursive: true,
157
+ showHidden: false
158
+ });
159
+
160
+ if (!listResult.success) {
161
+ throw new Error('文件列表获取失败');
162
+ }
163
+
164
+ console.log(` 找到 ${listResult.data.count} 个文件/目录\n`);
165
+
166
+ // 分类文件
167
+ const directories = listResult.data.files.filter(f => f.type === 'directory');
168
+ const jsFiles = listResult.data.files.filter(f => f.type === 'file' && f.name.endsWith('.js'));
169
+ const jsonFiles = listResult.data.files.filter(f => f.type === 'file' && f.name.endsWith('.json'));
170
+ const mdFiles = listResult.data.files.filter(f => f.type === 'file' && f.name.endsWith('.md'));
171
+
172
+ console.log(` 目录: ${directories.length} 个`);
173
+ console.log(` JS 文件: ${jsFiles.length} 个`);
174
+ console.log(` JSON 文件: ${jsonFiles.length} 个`);
175
+ console.log(` Markdown 文件: ${mdFiles.length} 个\n`);
176
+
177
+ // 步骤 2: 读取关键配置文件
178
+ console.log('步骤 2: 读取项目配置...');
179
+
180
+ const docData = {
181
+ name: 'Unknown',
182
+ version: 'Unknown',
183
+ description: '',
184
+ scripts: {}
185
+ };
186
+
187
+ // 读取 package.json
188
+ const packageResult = await executor.readFile({ filePath: 'package.json' });
189
+ if (packageResult.success) {
190
+ try {
191
+ const packageJson = JSON.parse(packageResult.data.content);
192
+ docData.name = packageJson.name || 'Unnamed';
193
+ docData.version = packageJson.version || '0.0.0';
194
+ docData.description = packageJson.description || '';
195
+ docData.scripts = packageJson.scripts || {};
196
+ docData.dependencies = packageJson.dependencies || {};
197
+ docData.devDependencies = packageJson.devDependencies || {};
198
+ console.log(` ✓ 已读取 package.json: ${docData.name}@${docData.version}`);
199
+ } catch {
200
+ console.log(' ✗ 无法解析 package.json');
201
+ }
202
+ }
203
+
204
+ // 读取 README.md
205
+ const readmeResult = await executor.readFile({ filePath: 'README.md' });
206
+ let readmeContent = '';
207
+ if (readmeResult.success) {
208
+ readmeContent = readmeResult.data.content;
209
+ console.log(' ✓ 已读取 README.md');
210
+ }
211
+
212
+ // 读取 CLAUDE.md
213
+ const claudeResult = await executor.readFile({ filePath: 'CLAUDE.md' });
214
+ let claudeContent = '';
215
+ if (claudeResult.success) {
216
+ claudeContent = claudeResult.data.content;
217
+ console.log(' ✓ 已读取 CLAUDE.md\n');
218
+ }
219
+
220
+ // 步骤 3: 生成项目文档
221
+ console.log('步骤 3: 生成结构化文档...');
222
+
223
+ let markdown = `# ${docData.name} - 项目文档\n\n`;
224
+ markdown += `> 自动生成的项目文档\n\n`;
225
+ markdown += `**版本**: ${docData.version}\n\n`;
226
+ markdown += `**描述**: ${docData.description || '无描述'}\n\n`;
227
+
228
+ markdown += `---\n\n`;
229
+ markdown += `## 项目概述\n\n`;
230
+ markdown += `本项目包含以下内容:\n\n`;
231
+ markdown += `- **目录数**: ${directories.length}\n`;
232
+ markdown += `- **JavaScript 文件**: ${jsFiles.length}\n`;
233
+ markdown += `- **配置文件**: ${jsonFiles.length}\n`;
234
+ markdown += `- **文档文件**: ${mdFiles.length}\n\n`;
235
+
236
+ markdown += `### 目录结构\n\n`;
237
+ markdown += '```\n';
238
+
239
+ // 生成目录树
240
+ const dirTree = {};
241
+ directories.forEach(d => {
242
+ const parts = d.path.split(path.sep);
243
+ let current = dirTree;
244
+ parts.forEach((part, idx) => {
245
+ if (!current[part]) {
246
+ current[part] = idx === parts.length - 1 ? null : {};
247
+ }
248
+ if (current[part] !== null) {
249
+ current = current[part];
250
+ }
251
+ });
252
+ });
253
+
254
+ // 简单的树形显示
255
+ function printTree(obj, prefix = '') {
256
+ let result = '';
257
+ const keys = Object.keys(obj).slice(0, 10); // 限制显示数量
258
+ keys.forEach((key, idx) => {
259
+ const isLast = idx === keys.length - 1;
260
+ result += `${prefix}${isLast ? '└── ' : '├── '}${key}\n`;
261
+ if (obj[key] !== null) {
262
+ result += printTree(obj[key], prefix + (isLast ? ' ' : '│ '));
263
+ }
264
+ });
265
+ return result;
266
+ }
267
+
268
+ markdown += printTree(dirTree);
269
+ markdown += '```\n\n';
270
+
271
+ // 关键文件列表
272
+ markdown += `### 关键文件\n\n`;
273
+ markdown += `#### JavaScript 源文件\n\n`;
274
+ jsFiles.slice(0, 15).forEach(f => {
275
+ markdown += `- \`${f.path}\`\n`;
276
+ });
277
+ if (jsFiles.length > 15) {
278
+ markdown += `- ... 还有 ${jsFiles.length - 15} 个文件\n`;
279
+ }
280
+
281
+ markdown += `\n#### 配置文件\n\n`;
282
+ jsonFiles.forEach(f => {
283
+ markdown += `- \`${f.path}\`\n`;
284
+ });
285
+
286
+ // 脚本说明
287
+ if (Object.keys(docData.scripts).length > 0) {
288
+ markdown += `\n### 可用脚本\n\n`;
289
+ Object.entries(docData.scripts).forEach(([name, script]) => {
290
+ markdown += `#### npm run ${name}\n\n`;
291
+ markdown += `\`\`\`bash\n${script}\n\`\`\`\n\n`;
292
+ });
293
+ }
294
+
295
+ // 依赖关系
296
+ if (Object.keys(docData.dependencies || {}).length > 0) {
297
+ markdown += `\n### 生产依赖\n\n`;
298
+ Object.entries(docData.dependencies).forEach(([name, version]) => {
299
+ markdown += `- **${name}**: \`${version}\`\n`;
300
+ });
301
+ }
302
+
303
+ if (Object.keys(docData.devDependencies || {}).length > 0) {
304
+ markdown += `\n### 开发依赖\n\n`;
305
+ Object.entries(docData.devDependencies).forEach(([name, version]) => {
306
+ markdown += `- **${name}**: \`${version}\`\n`;
307
+ });
308
+ }
309
+
310
+ // 原始文档引用
311
+ if (readmeContent) {
312
+ markdown += `\n---\n\n`;
313
+ markdown += `## 原始 README\n\n`;
314
+ markdown += readmeContent;
315
+ }
316
+
317
+ // 步骤 4: 写入文档
318
+ console.log('步骤 4: 保存文档...');
319
+ const writeResult = await executor.writeFile({
320
+ filePath: 'scenarios/output/project-documentation.md',
321
+ content: markdown
322
+ });
323
+
324
+ if (writeResult.success) {
325
+ console.log(` ✓ 文档已保存: ${writeResult.data.path} (${writeResult.data.size} bytes)\n`);
326
+ }
327
+
328
+ // 打印统计
329
+ console.log('========================================');
330
+ console.log('文档生成完成');
331
+ console.log('========================================');
332
+ console.log(`项目名称: ${docData.name}`);
333
+ console.log(`项目版本: ${docData.version}`);
334
+ console.log(`文件总数: ${listResult.data.count}`);
335
+ executor.printSummary();
336
+
337
+ return {
338
+ success: true,
339
+ docData,
340
+ stats: {
341
+ totalFiles: listResult.data.count,
342
+ directories: directories.length,
343
+ jsFiles: jsFiles.length,
344
+ jsonFiles: jsonFiles.length,
345
+ mdFiles: mdFiles.length
346
+ },
347
+ toolCalls: executor.getCallLog()
348
+ };
349
+ } catch (error) {
350
+ console.error('\n场景执行失败:', error.message);
351
+ return { success: false, error: error.message };
352
+ }
353
+ }
354
+
355
+ async function main() {
356
+ const result = await runDocGeneratorScenario();
357
+
358
+ if (result.success) {
359
+ console.log('\n✓ 场景三执行成功\n');
360
+ process.exit(0);
361
+ } else {
362
+ console.log('\n✗ 场景三执行失败\n');
363
+ process.exit(1);
364
+ }
365
+ }
366
+
367
+ export { runDocGeneratorScenario };
368
+
369
+ if (import.meta.url === `file://${process.argv[1]}`) {
370
+ main();
371
+ }