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,62 @@
1
+ /**
2
+ * 批处理模式命令
3
+ * 支持单次对话和 workflow 模式
4
+ */
5
+
6
+ export default async function batchCommand(args, options) {
7
+ // Workflow 模式:支持多轮迭代和验收标准验证
8
+ if (options.workflow) {
9
+ const { runWorkflow } = await import('../utils/workflow.js');
10
+ const result = await runWorkflow(options.workflow, args, options);
11
+
12
+ // 根据 JSON 选项格式化输出
13
+ if (options.json || options.j) {
14
+ console.log(JSON.stringify(result, null, 2));
15
+ }
16
+
17
+ // 设置退出码
18
+ process.exit(result.success ? 0 : 1);
19
+ return;
20
+ }
21
+
22
+ // 常规批处理模式:单次对话
23
+ // 临时保存原始 process.argv
24
+ const originalArgv = process.argv.slice();
25
+
26
+ // 构建新的 argv 数组,模拟直接调用 batch-cli.js
27
+ process.argv = [
28
+ process.argv[0], // node
29
+ process.argv[1], // 当前脚本
30
+ ];
31
+
32
+ // 添加选项
33
+ if (options.help || options.h) {
34
+ process.argv.push('--help');
35
+ }
36
+ if (options.json || options.j) {
37
+ process.argv.push('--json');
38
+ }
39
+ if (options.verbose || options.v) {
40
+ process.argv.push('--verbose');
41
+ }
42
+ if (options.debug || options.d) {
43
+ process.argv.push('--debug');
44
+ }
45
+ if (options.file || options.f) {
46
+ process.argv.push('--file', options.file || options.f);
47
+ }
48
+
49
+ // 添加提示词参数
50
+ if (args.length > 0) {
51
+ process.argv.push(args.join(' '));
52
+ }
53
+
54
+ try {
55
+ // 导入并运行批处理模式
56
+ const { runBatch } = await import('../batch-cli.js');
57
+ await runBatch();
58
+ } finally {
59
+ // 恢复原始 process.argv
60
+ process.argv = originalArgv;
61
+ }
62
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 交互模式命令
3
+ * 暂时桥接到现有的 closer-cli.jsx
4
+ */
5
+
6
+ export default async function chatCommand(args, options) {
7
+ // 动态导入并运行现有的交互模式
8
+ // 由于 closer-cli.jsx 是 jsx 文件且使用 ink,我们直接导入它的主逻辑
9
+ await import('../closer-cli.jsx');
10
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 配置管理命令
3
+ */
4
+
5
+ import { loadConfig, saveConfig, updateConfig } from '../config.js';
6
+ import { showError, showSuccess, showTip, showInfo } from '../utils/cli.js';
7
+ import { execSync } from 'child_process';
8
+ import { join } from 'path';
9
+ import os from 'os';
10
+
11
+ const CONFIG_DIR = join(os.homedir(), '.closer-code');
12
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
13
+
14
+ /**
15
+ * 显示配置
16
+ */
17
+ function showConfig(config, key = null) {
18
+ if (key) {
19
+ // 显示特定配置项
20
+ const keys = key.split('.');
21
+ let value = config;
22
+ for (const k of keys) {
23
+ value = value?.[k];
24
+ }
25
+ if (value === undefined) {
26
+ showError(`配置项不存在: ${key}`);
27
+ } else {
28
+ console.log(`${key}: ${JSON.stringify(value, null, 2)}`);
29
+ }
30
+ } else {
31
+ // 显示所有配置
32
+ console.log(JSON.stringify(config, null, 2));
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 设置配置项
38
+ */
39
+ function setConfig(key, value) {
40
+ if (!key || value === undefined) {
41
+ showError('用法: cloco config set <key> <value>');
42
+ return;
43
+ }
44
+
45
+ try {
46
+ // 解析值(支持 JSON 和原始类型)
47
+ let parsedValue;
48
+ try {
49
+ parsedValue = JSON.parse(value);
50
+ } catch {
51
+ parsedValue = value;
52
+ }
53
+
54
+ // 使用点号表示法设置嵌套属性
55
+ const keys = key.split('.');
56
+ const update = {};
57
+ let current = update;
58
+ for (let i = 0; i < keys.length - 1; i++) {
59
+ current[keys[i]] = {};
60
+ current = current[keys[i]];
61
+ }
62
+ current[keys[keys.length - 1]] = parsedValue;
63
+
64
+ updateConfig(update);
65
+ showSuccess(`已设置 ${key} = ${JSON.stringify(parsedValue)}`);
66
+ } catch (error) {
67
+ showError(`设置配置失败: ${error.message}`);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * 获取配置项
73
+ */
74
+ function getConfigValue(key) {
75
+ if (!key) {
76
+ showError('用法: cloco config get <key>');
77
+ return;
78
+ }
79
+
80
+ const config = loadConfig();
81
+ showConfig(config, key);
82
+ }
83
+
84
+ /**
85
+ * 打开配置文件编辑器
86
+ */
87
+ function openConfigInEditor() {
88
+ try {
89
+ const editor = process.env.EDITOR || process.env.VISUAL || 'code';
90
+
91
+ if (!editor) {
92
+ showTip('未设置编辑器。请设置环境变量 EDITOR 或 VISUAL');
93
+ showTip('例如: export EDITOR=code');
94
+ showInfo(`配置文件位置: ${CONFIG_FILE}`);
95
+ return;
96
+ }
97
+
98
+ showInfo(`正在使用 ${editor} 打开配置文件...`);
99
+ execSync(`${editor} "${CONFIG_FILE}"`, { stdio: 'inherit' });
100
+ } catch (error) {
101
+ showError(`打开编辑器失败: ${error.message}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * 重置配置
107
+ */
108
+ function resetConfig() {
109
+ try {
110
+ // 导入默认配置
111
+ const { DEFAULT_CONFIG } = '../config.js';
112
+ saveConfig(DEFAULT_CONFIG);
113
+ showSuccess('配置已重置为默认值');
114
+ } catch (error) {
115
+ showError(`重置配置失败: ${error.message}`);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * 配置管理主命令
121
+ */
122
+ export default async function configCommand(args, options) {
123
+ const action = args[0];
124
+ const config = loadConfig();
125
+
126
+ switch (action) {
127
+ case 'set':
128
+ setConfig(args[1], args[2]);
129
+ break;
130
+
131
+ case 'get':
132
+ getConfigValue(args[1]);
133
+ break;
134
+
135
+ case 'edit':
136
+ openConfigInEditor();
137
+ break;
138
+
139
+ case 'reset':
140
+ resetConfig();
141
+ break;
142
+
143
+ case 'show':
144
+ case undefined:
145
+ showConfig(config);
146
+ break;
147
+
148
+ default:
149
+ showError(`未知操作: ${action}`);
150
+ showTip('可用操作: set, get, edit, reset, show');
151
+ showTip('运行 "cloco help" 查看更多信息');
152
+ process.exit(1);
153
+ }
154
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 帮助文档系统
3
+ */
4
+
5
+ import { getVersion } from '../utils/version.js';
6
+
7
+ /**
8
+ * 显示主帮助信息
9
+ */
10
+ export async function showHelp() {
11
+ const version = getVersion();
12
+
13
+ console.log(`
14
+ Cloco - AI 编程助手 v${version}
15
+
16
+ 使用方式:
17
+ cloco [选项] [提示词]
18
+ cloco -b|--batch [选项] <提示词>
19
+ cloco config <操作> [参数]
20
+ cloco setup|upgrade|version|help
21
+
22
+ 交互模式:
23
+ cloco # 启动交互式对话(默认)
24
+ # 首次使用会自动运行配置向导
25
+
26
+ 批处理模式:
27
+ cloco -b "分析代码" # 短选项
28
+ cloco --batch "列出文件" # 长选项
29
+ cloco -b --json "生成代码" > out.js # JSON 格式输出
30
+ cloco -b --file prompt.txt # 从文件读取提示词
31
+ cloco -b --verbose "分析" # 详细输出(包含工具调用)
32
+
33
+ 配置管理:
34
+ cloco config # 查看当前配置
35
+ cloco config set <key> <value> # 设置配置项
36
+ cloco config get <key> # 获取配置项
37
+ cloco config edit # 打开配置文件
38
+ cloco config reset # 重置为默认配置
39
+
40
+ 其他命令:
41
+ cloco setup # 手动运行配置向导
42
+ cloco upgrade # 检查版本更新
43
+ cloco version # 显示版本信息
44
+ cloco help # 显示此帮助信息
45
+
46
+ 选项:
47
+ -b, --batch # 批处理模式
48
+ -j, --json # JSON 格式输出
49
+ -f, --file <文件> # 从文件读取提示词
50
+ --verbose # 详细输出模式
51
+ -d, --debug # 启用调试日志
52
+ -h, --help # 显示帮助信息
53
+ -v, --version # 显示版本信息
54
+
55
+ 环境变量:
56
+ CLOSER_AI_PROVIDER # AI 提供商 (anthropic, openai, ollama)
57
+ CLOSER_ANTHROPIC_API_KEY # Anthropic API Key
58
+ CLOSER_OPENAI_API_KEY # OpenAI API Key
59
+ CLOSER_DEBUG_LOG # 启用调试日志 (设为 1)
60
+
61
+ 文档:
62
+ https://github.com/your-repo/closer-code
63
+
64
+ 示例:
65
+ # 快速开始
66
+ cloco # 首次使用会引导配置
67
+
68
+ # 批处理任务
69
+ cloco -b "解释这个函数"
70
+ cloco -b --json "生成代码" > output.json
71
+
72
+ # 配置管理
73
+ cloco config set ai.provider openai
74
+ cloco config get ai.provider
75
+ `);
76
+ }
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 历史管理命令
4
+ *
5
+ * 用法:
6
+ * node history.js list - 列出所有项目的历史
7
+ * node history.js show <path> - 显示指定项目的历史
8
+ * node history.js clear <path> - 清除指定项目的历史
9
+ * node history.js export <path> - 导出指定项目的历史
10
+ * node history.js migrate - 迁移旧的历史文件
11
+ */
12
+
13
+ import {
14
+ loadHistory,
15
+ saveHistory,
16
+ clearHistory,
17
+ listHistory,
18
+ migrateHistory
19
+ } from '../config.js';
20
+ import fs from 'fs';
21
+
22
+ const colors = {
23
+ reset: '\x1b[0m',
24
+ green: '\x1b[32m',
25
+ red: '\x1b[31m',
26
+ blue: '\x1b[34m',
27
+ yellow: '\x1b[33m',
28
+ cyan: '\x1b[36m',
29
+ magenta: '\x1b[35m'
30
+ };
31
+
32
+ function log(message, color = 'reset') {
33
+ console.log(`${colors[color]}${message}${colors.reset}`);
34
+ }
35
+
36
+ function formatMessage(msg, index) {
37
+ const role = msg.role || 'unknown';
38
+ const content = msg.content || '';
39
+ const preview = typeof content === 'string'
40
+ ? content.substring(0, 60) + (content.length > 60 ? '...' : '')
41
+ : '[Non-text content]';
42
+
43
+ return `${index + 1}. [${role}] ${preview}`;
44
+ }
45
+
46
+ async function cmdList() {
47
+ log('\n📚 所有项目历史\n', 'cyan');
48
+
49
+ const projects = listHistory();
50
+
51
+ if (projects.length === 0) {
52
+ log(' 没有找到任何项目历史', 'yellow');
53
+ return;
54
+ }
55
+
56
+ // 按最后更新时间排序
57
+ projects.sort((a, b) => new Date(b.lastUpdated) - new Date(a.lastUpdated));
58
+
59
+ projects.forEach((p, i) => {
60
+ log(`\n${i + 1}. ${p.projectPath}`, 'green');
61
+ log(` 消息数: ${p.messageCount}`, 'blue');
62
+ log(` 最后更新: ${new Date(p.lastUpdated).toLocaleString('zh-CN')}`, 'blue');
63
+ });
64
+
65
+ log(`\n共 ${projects.length} 个项目\n`, 'cyan');
66
+ }
67
+
68
+ async function cmdShow(projectPath) {
69
+ if (!projectPath) {
70
+ log('错误: 请指定项目路径', 'red');
71
+ log('用法: node history.js show <project-path>', 'yellow');
72
+ return;
73
+ }
74
+
75
+ log(`\n📖 项目历史: ${projectPath}\n`, 'cyan');
76
+
77
+ const history = loadHistory(projectPath);
78
+
79
+ if (history.length === 0) {
80
+ log(' 该项目没有历史记录', 'yellow');
81
+ return;
82
+ }
83
+
84
+ log(`共 ${history.length} 条消息:\n`, 'blue');
85
+
86
+ history.forEach((msg, i) => {
87
+ log(formatMessage(msg, i), 'reset');
88
+ });
89
+
90
+ log('\n', 'reset');
91
+ }
92
+
93
+ async function cmdClear(projectPath) {
94
+ if (!projectPath) {
95
+ log('错误: 请指定项目路径', 'red');
96
+ log('用法: node history.js clear <project-path>', 'yellow');
97
+ return;
98
+ }
99
+
100
+ log(`\n⚠️ 确定要清除项目历史吗: ${projectPath}?`, 'yellow');
101
+ log('这将删除该项目的所有对话历史,且无法恢复。\n', 'yellow');
102
+
103
+ // 在实际使用中,这里可以添加确认逻辑
104
+ clearHistory(projectPath);
105
+
106
+ log('✓ 项目历史已清除\n', 'green');
107
+ }
108
+
109
+ async function cmdExport(projectPath) {
110
+ if (!projectPath) {
111
+ log('错误: 请指定项目路径', 'red');
112
+ log('用法: node history.js export <project-path>', 'yellow');
113
+ return;
114
+ }
115
+
116
+ const history = loadHistory(projectPath);
117
+
118
+ if (history.length === 0) {
119
+ log(`项目 ${projectPath} 没有历史记录`, 'yellow');
120
+ return;
121
+ }
122
+
123
+ // 生成导出文件名
124
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
125
+ const exportFile = `history-export-${timestamp}.json`;
126
+
127
+ try {
128
+ fs.writeFileSync(exportFile, JSON.stringify(history, null, 2));
129
+ log(`\n✓ 历史已导出到: ${exportFile}`, 'green');
130
+ log(` 共 ${history.length} 条消息\n`, 'blue');
131
+ } catch (error) {
132
+ log(`\n✗ 导出失败: ${error.message}\n`, 'red');
133
+ }
134
+ }
135
+
136
+ async function cmdMigrate() {
137
+ log('\n🔄 开始迁移旧的历史文件...\n', 'cyan');
138
+
139
+ migrateHistory();
140
+
141
+ log('\n✓ 迁移完成\n', 'green');
142
+ }
143
+
144
+ async function main() {
145
+ const args = process.argv.slice(2);
146
+ const command = args[0];
147
+ const param = args[1];
148
+
149
+ log('\n' + '='.repeat(60), 'cyan');
150
+ log(' 项目历史管理工具', 'cyan');
151
+ log('='.repeat(60) + '\n', 'cyan');
152
+
153
+ switch (command) {
154
+ case 'list':
155
+ await cmdList();
156
+ break;
157
+ case 'show':
158
+ await cmdShow(param);
159
+ break;
160
+ case 'clear':
161
+ await cmdClear(param);
162
+ break;
163
+ case 'export':
164
+ await cmdExport(param);
165
+ break;
166
+ case 'migrate':
167
+ await cmdMigrate();
168
+ break;
169
+ case 'help':
170
+ case '--help':
171
+ case '-h':
172
+ log('用法: node history.js <command> [options]\n', 'cyan');
173
+ log('命令:', 'yellow');
174
+ log(' list 列出所有项目的历史', 'blue');
175
+ log(' show <path> 显示指定项目的历史', 'blue');
176
+ log(' clear <path> 清除指定项目的历史', 'blue');
177
+ log(' export <path> 导出指定项目的历史', 'blue');
178
+ log(' migrate 迁移旧的历史文件', 'blue');
179
+ log(' help 显示此帮助信息\n', 'blue');
180
+ break;
181
+ default:
182
+ log('错误: 未知命令', 'red');
183
+ log('使用 "node history.js help" 查看帮助\n', 'yellow');
184
+ process.exit(1);
185
+ }
186
+ }
187
+
188
+ main().catch(error => {
189
+ log(`\n✗ 错误: ${error.message}\n`, 'red');
190
+ console.error(error);
191
+ process.exit(1);
192
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 初始化向导命令
3
+ * 导入并运行现有的设置脚本
4
+ */
5
+
6
+ export default async function setupCommand(args, options) {
7
+ // 导入现有的设置脚本
8
+ const setupModule = await import('../setup.js');
9
+
10
+ // 如果 setup.js 导出了 main 函数,调用它
11
+ if (setupModule.main) {
12
+ await setupModule.main();
13
+ } else {
14
+ // 否则尝试运行默认导出
15
+ setupModule.default?.();
16
+ }
17
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * 版本更新检查命令
3
+ */
4
+
5
+ import { getVersion } from '../utils/version.js';
6
+ import { showInfo, showSuccess, showTip } from '../utils/cli.js';
7
+
8
+ /**
9
+ * 检查 npm 上的最新版本
10
+ */
11
+ async function checkLatestVersion() {
12
+ try {
13
+ const https = await import('https');
14
+ const packageJson = await import('../../package.json', { assert: { type: 'json' } });
15
+
16
+ return new Promise((resolve, reject) => {
17
+ const options = {
18
+ hostname: 'registry.npmjs.org',
19
+ path: `/${packageJson.default.name}`,
20
+ method: 'GET',
21
+ headers: {
22
+ 'User-Agent': 'cloco'
23
+ }
24
+ };
25
+
26
+ const req = https.request(options, (res) => {
27
+ let data = '';
28
+ res.on('data', (chunk) => { data += chunk; });
29
+ res.on('end', () => {
30
+ try {
31
+ const packageInfo = JSON.parse(data);
32
+ resolve(packageInfo['dist-tags']?.latest || null);
33
+ } catch (error) {
34
+ reject(error);
35
+ }
36
+ });
37
+ });
38
+
39
+ req.on('error', reject);
40
+ req.setTimeout(5000, () => {
41
+ req.destroy();
42
+ reject(new Error('请求超时'));
43
+ });
44
+ req.end();
45
+ });
46
+ } catch (error) {
47
+ console.error('无法检查更新:', error.message);
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * 比较版本号
54
+ */
55
+ function compareVersions(current, latest) {
56
+ const currentParts = current.split('.').map(Number);
57
+ const latestParts = latest.split('.').map(Number);
58
+
59
+ for (let i = 0; i < Math.max(currentParts.length, latestParts.length); i++) {
60
+ const currentPart = currentParts[i] || 0;
61
+ const latestPart = latestParts[i] || 0;
62
+
63
+ if (currentPart < latestPart) return -1;
64
+ if (currentPart > latestPart) return 1;
65
+ }
66
+
67
+ return 0;
68
+ }
69
+
70
+ /**
71
+ * 升级命令主函数
72
+ */
73
+ export default async function upgradeCommand(args, options) {
74
+ showInfo('检查更新...');
75
+
76
+ const currentVersion = getVersion();
77
+ const latestVersion = await checkLatestVersion();
78
+
79
+ if (!latestVersion) {
80
+ showTip('无法获取最新版本信息');
81
+ showInfo(`当前版本: ${currentVersion}`);
82
+ return;
83
+ }
84
+
85
+ console.log(`✅ 当前版本: ${currentVersion}`);
86
+ console.log(`📦 最新版本: ${latestVersion}`);
87
+
88
+ const comparison = compareVersions(currentVersion, latestVersion);
89
+
90
+ if (comparison < 0) {
91
+ console.log('\n💡 有新版本可用!');
92
+ showTip('运行以下命令更新:');
93
+ console.log(' npm update -g closer-code');
94
+ console.log('\n 或查看更新日志:');
95
+ console.log(` https://github.com/your-repo/closer-code/releases/tag/v${latestVersion}`);
96
+ } else if (comparison === 0) {
97
+ showSuccess('您正在使用最新版本!');
98
+ } else {
99
+ showInfo('您正在使用开发版本,比发布版本更新');
100
+ }
101
+ }