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,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Ctrl+C 双击退出功能测试
4
+ *
5
+ * 预期行为:
6
+ * 1. 第一次 Ctrl+C:显示红色提示框(1.5秒后消失)
7
+ * 2. 1.5秒内第二次 Ctrl+C:退出程序
8
+ * 3. 超时后重置:需要再次双击
9
+ */
10
+
11
+ import React, { useState, useEffect } from 'react';
12
+ import { render, Box, Text } from 'ink';
13
+ import TextInput from 'ink-text-input';
14
+ import { useInput } from 'ink';
15
+
16
+ const useKeepAlive = () => useEffect(() => {
17
+ // refresh the app every 100ms to keep the process alive
18
+ const intervalId = setInterval(() => {}, 100);
19
+ return () => clearInterval(intervalId); // Cleanup on unmount
20
+ }, []);
21
+
22
+ function App() {
23
+ const [input, setInput] = useState('');
24
+ const [lastCtrlC, setLastCtrlC] = useState(0);
25
+ const [showExitHint, setShowExitHint] = useState(false);
26
+ const [message, setMessage] = useState('等待输入...');
27
+
28
+ useKeepAlive();
29
+
30
+ // 定时消息提示
31
+ useEffect(() => {
32
+ const messages = [
33
+ '提示:按 Ctrl+C 测试退出功能',
34
+ '第一次 Ctrl+C 会显示提示',
35
+ '1.5秒内第二次 Ctrl+C 才会退出',
36
+ '试试在输入框输入一些内容',
37
+ ];
38
+ let index = 0;
39
+
40
+ const timer = setInterval(() => {
41
+ index = (index + 1) % messages.length;
42
+ setMessage(messages[index]);
43
+ }, 1500);
44
+
45
+ return () => clearInterval(timer);
46
+ }, []);
47
+
48
+ // Ctrl+C 处理 - 使用 capture: true
49
+ useInput((input, key) => {
50
+ if ((key.ctrl && input === 'c') || key.escape) {
51
+ const now = Date.now();
52
+
53
+ if (now - lastCtrlC < 1500) {
54
+ // 1.5秒内第二次按下 - 退出
55
+ console.log('\n✅ 测试成功!程序将在双击 Ctrl+C 后退出\n');
56
+ process.exit(0);
57
+ } else {
58
+ // 第一次按下 - 显示提示
59
+ setShowExitHint(true);
60
+ setLastCtrlC(now);
61
+ setTimeout(() => setShowExitHint(false), 1500);
62
+ }
63
+ return;
64
+ }
65
+
66
+ // 其他按键(可选:显示按键信息)
67
+ if (input) {
68
+ // 可以在这里处理其他按键
69
+ }
70
+ }, { capture: true }); // 关键:捕获 Ctrl+C
71
+
72
+ return (
73
+ <Box flexDirection="column" paddingX={2}>
74
+ {/* 标题 */}
75
+ <Box marginBottom={1}>
76
+ <Text bold color="cyan">🧪 Ctrl+C 双击退出测试</Text>
77
+ </Box>
78
+
79
+ {/* 定时消息提示 */}
80
+ <Box marginBottom={1}>
81
+ <Text color="yellow">💡 {message}</Text>
82
+ </Box>
83
+
84
+ {/* 退出提示 */}
85
+ {showExitHint && (
86
+ <Box
87
+ borderStyle="round"
88
+ borderColor="red"
89
+ paddingX={1}
90
+ marginBottom={1}
91
+ >
92
+ <Text bold color="red">⚠️ 再次按 Ctrl+C 或 ESC 退出程序 (1.5秒内)</Text>
93
+ </Box>
94
+ )}
95
+
96
+ {/* 输入框 */}
97
+ <Box>
98
+ <Text color="green">❯ </Text>
99
+ <TextInput
100
+ value={input}
101
+ onChange={setInput}
102
+ placeholder="输入任何内容测试..."
103
+ />
104
+ </Box>
105
+
106
+ {/* 状态信息 */}
107
+ <Box marginTop={1}>
108
+ <Text dimColor>
109
+ 上次 Ctrl+C: {lastCtrlC > 0 ? new Date(lastCtrlC).toLocaleTimeString() : '未按下'} |
110
+ 提示状态: {showExitHint ? '显示中' : '隐藏'}
111
+ </Text>
112
+ </Box>
113
+
114
+ {/* 帮助信息 */}
115
+ <Box marginTop={1}>
116
+ <Text dimColor>
117
+ 测试说明:按一次 Ctrl+C 应该显示红色提示,再按一次(1.5秒内)应该退出
118
+ </Text>
119
+ </Box>
120
+ </Box>
121
+ );
122
+ }
123
+
124
+ render(<App />, {exitOnCtrlC: false});
125
+
126
+ process.once('SIGINT', () => {console.log('FORK');});
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 手动测试:在空目录中创建文件
4
+ *
5
+ * 验证 writeFile 工具自动创建父目录的功能
6
+ */
7
+
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ import fs from 'fs/promises';
11
+ import { getConfig } from './src/config.js';
12
+ import { createConversation } from './src/conversation.js';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ async function testEmptyDirCreation() {
18
+ console.log('🧪 测试:在空目录中创建文件\n');
19
+ console.log('═'.repeat(60));
20
+
21
+ // 创建临时测试目录
22
+ const testDir = path.join(__dirname, 'temp-test-creation');
23
+ try {
24
+ await fs.mkdir(testDir, { recursive: true });
25
+ console.log(`✅ 创建测试目录: ${testDir}\n`);
26
+ } catch (error) {
27
+ console.log('⚠️ 测试目录已存在\n');
28
+ }
29
+
30
+ try {
31
+ // 修改配置的工作目录
32
+ const config = getConfig();
33
+ config.behavior.workingDir = testDir;
34
+
35
+ console.log('📝 任务: 创建一个多章节的玄幻故事\n');
36
+ console.log('要求:');
37
+ console.log(' 1. 故事名称:《天道诀》');
38
+ console.log(' 2. 分为3个章节(简化测试)');
39
+ console.log(' 3. 章节保存为 chapters/chapter-01.txt 到 chapters/chapter-03.txt');
40
+ console.log(' 4. 创建 README.md 文件');
41
+ console.log('\n' + '─'.repeat(60) + '\n');
42
+
43
+ // 创建对话
44
+ const conversation = await createConversation(config, false);
45
+
46
+ // 发送任务
47
+ const prompt = `写一个玄幻故事,要求:
48
+ 1. 故事名称为《天道诀》
49
+ 2. 分为3个章节(简化测试)
50
+ 3. 每个章节保存为独立的文件,格式为 chapters/chapter-01.txt 到 chapters/chapter-03.txt
51
+ 4. 创建一个 README.md 文件,包含故事简介和章节列表
52
+
53
+ 请立即使用 writeFile 工具创建这些文件。`;
54
+
55
+ console.log('🤖 AI 正在处理...\n');
56
+
57
+ const response = await conversation.sendMessage(prompt, (progress) => {
58
+ if (progress.type === 'tool_start') {
59
+ console.log(`⚡ 执行工具: ${progress.tool}`);
60
+ } else if (progress.type === 'tool_complete') {
61
+ const result = progress.result;
62
+ if (result.success) {
63
+ console.log(` ✅ 成功: ${result.path || 'OK'}`);
64
+ } else {
65
+ console.log(` ❌ 失败: ${result.error || 'Unknown error'}`);
66
+ }
67
+ }
68
+ });
69
+
70
+ console.log('\n' + '─'.repeat(60));
71
+ console.log('\n📄 AI 响应:\n');
72
+ console.log(response.content);
73
+ console.log('\n' + '═'.repeat(60));
74
+
75
+ // 验证结果
76
+ console.log('\n🔍 验证结果:\n');
77
+
78
+ // 检查目录结构
79
+ const testDirExists = await fs.access(testDir).then(() => true).catch(() => false);
80
+ console.log(` ${testDirExists ? '✅' : '❌'} 测试目录存在`);
81
+
82
+ // 检查 chapters 目录
83
+ const chaptersDir = path.join(testDir, 'chapters');
84
+ const chaptersDirExists = await fs.access(chaptersDir).then(() => true).catch(() => false);
85
+ console.log(` ${chaptersDirExists ? '✅' : '❌'} chapters/ 目录被自动创建`);
86
+
87
+ // 检查文件
88
+ const expectedFiles = [
89
+ 'README.md',
90
+ 'chapters/chapter-01.txt',
91
+ 'chapters/chapter-02.txt',
92
+ 'chapters/chapter-03.txt'
93
+ ];
94
+
95
+ for (const file of expectedFiles) {
96
+ const filePath = path.join(testDir, file);
97
+ const exists = await fs.access(filePath).then(() => true).catch(() => false);
98
+ if (exists) {
99
+ const stat = await fs.stat(filePath);
100
+ console.log(` ✅ ${file} (${stat.size} bytes)`);
101
+ } else {
102
+ console.log(` ❌ ${file} (不存在)`);
103
+ }
104
+ }
105
+
106
+ console.log('\n' + '═'.repeat(60));
107
+
108
+ // 读取 README.md 内容
109
+ const readmePath = path.join(testDir, 'README.md');
110
+ try {
111
+ const readmeContent = await fs.readFile(readmePath, 'utf-8');
112
+ console.log('\n📖 README.md 内容预览:\n');
113
+ console.log(readmeContent.split('\n').slice(0, 10).join('\n'));
114
+ console.log('\n... (内容已截断)\n');
115
+ } catch (error) {
116
+ console.log('\n⚠️ 无法读取 README.md\n');
117
+ }
118
+
119
+ // 读取第一章内容
120
+ const chapter1Path = path.join(testDir, 'chapters/chapter-01.txt');
121
+ try {
122
+ const chapter1Content = await fs.readFile(chapter1Path, 'utf-8');
123
+ console.log('📖 第一章内容预览:\n');
124
+ console.log(chapter1Content.split('\n').slice(0, 15).join('\n'));
125
+ console.log('\n... (内容已截断)\n');
126
+ } catch (error) {
127
+ console.log('\n⚠️ 无法读取 chapter-01.txt\n');
128
+ }
129
+
130
+ // 询问是否清理
131
+ console.log('═'.repeat(60));
132
+ console.log('\n✅ 测试完成!');
133
+ console.log(`\n测试目录: ${testDir}`);
134
+ console.log('是否保留测试目录以便检查?(按 Ctrl+C 取消清理)\n');
135
+
136
+ // 延迟清理
137
+ await new Promise(resolve => setTimeout(resolve, 3000));
138
+
139
+ console.log('🧹 清理测试目录...');
140
+ await fs.rm(testDir, { recursive: true, force: true });
141
+ console.log('✅ 清理完成\n');
142
+
143
+ } catch (error) {
144
+ console.error('\n❌ 测试失败:', error.message);
145
+ console.error(error.stack);
146
+ console.log(`\n⚠️ 测试目录保留在: ${testDir}`);
147
+ }
148
+ }
149
+
150
+ // 运行测试
151
+ testEmptyDirCreation().catch(console.error);
package/winfix.md ADDED
@@ -0,0 +1,3 @@
1
+ #附加环境变量: /c/Users/Joyer/AppData/Roaming/npm/npm
2
+ #附加环境变量: /c/Program Files/nodejs/node
3
+ #附加环境变量: /s/Apps/Git/cmd/