cfix 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 (52) hide show
  1. package/.env.example +69 -0
  2. package/README.md +1590 -0
  3. package/bin/cfix +14 -0
  4. package/bin/cfix.cmd +6 -0
  5. package/cli/commands/config.js +58 -0
  6. package/cli/commands/doctor.js +240 -0
  7. package/cli/commands/fix.js +211 -0
  8. package/cli/commands/help.js +62 -0
  9. package/cli/commands/init.js +226 -0
  10. package/cli/commands/logs.js +161 -0
  11. package/cli/commands/monitor.js +151 -0
  12. package/cli/commands/project.js +331 -0
  13. package/cli/commands/service.js +133 -0
  14. package/cli/commands/status.js +115 -0
  15. package/cli/commands/task.js +412 -0
  16. package/cli/commands/version.js +19 -0
  17. package/cli/index.js +269 -0
  18. package/cli/lib/config-manager.js +612 -0
  19. package/cli/lib/formatter.js +224 -0
  20. package/cli/lib/process-manager.js +233 -0
  21. package/cli/lib/service-client.js +271 -0
  22. package/cli/scripts/install-completion.js +133 -0
  23. package/package.json +85 -0
  24. package/public/monitor.html +1096 -0
  25. package/scripts/completion.bash +87 -0
  26. package/scripts/completion.zsh +102 -0
  27. package/src/assets/README.md +32 -0
  28. package/src/assets/error.png +0 -0
  29. package/src/assets/icon.png +0 -0
  30. package/src/assets/success.png +0 -0
  31. package/src/claude-cli-service.js +216 -0
  32. package/src/config/index.js +69 -0
  33. package/src/database/manager.js +391 -0
  34. package/src/database/migration.js +252 -0
  35. package/src/git-service.js +1278 -0
  36. package/src/index.js +1658 -0
  37. package/src/logger.js +139 -0
  38. package/src/metrics/collector.js +184 -0
  39. package/src/middleware/auth.js +86 -0
  40. package/src/middleware/rate-limit.js +85 -0
  41. package/src/queue/integration-example.js +283 -0
  42. package/src/queue/task-queue.js +333 -0
  43. package/src/services/notification-limiter.js +48 -0
  44. package/src/services/notification-service.js +115 -0
  45. package/src/services/system-notifier.js +130 -0
  46. package/src/task-manager.js +289 -0
  47. package/src/utils/exec.js +87 -0
  48. package/src/utils/project-lock.js +246 -0
  49. package/src/utils/retry.js +110 -0
  50. package/src/utils/sanitizer.js +174 -0
  51. package/src/websocket/notifier.js +363 -0
  52. package/src/wechat-notifier.js +97 -0
package/bin/cfix ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CodeFix CLI 入口文件
4
+ * 命令: cfix
5
+ */
6
+
7
+ const path = require('path');
8
+
9
+ // 设置根目录环境变量
10
+ process.env.CFIX_ROOT = path.resolve(__dirname, '..');
11
+
12
+ // 运行 CLI
13
+ const cli = require('../cli/index');
14
+ cli.run();
package/bin/cfix.cmd ADDED
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ REM CodeFix CLI Windows 入口文件
3
+
4
+ SETLOCAL
5
+ SET "NODE_PATH=%NODE_PATH%;%~dp0..\node_modules"
6
+ node "%~dp0..\cli\index.js" %*
@@ -0,0 +1,58 @@
1
+ /**
2
+ * 配置管理命令
3
+ */
4
+
5
+ const config = require('../lib/config-manager');
6
+
7
+ async function get(key) {
8
+ try {
9
+ const value = config.get(key);
10
+ console.log(`${key}: ${JSON.stringify(value, null, 2)}`);
11
+ } catch (error) {
12
+ console.error(`❌ 获取配置失败: ${error.message}`);
13
+ process.exit(1);
14
+ }
15
+ }
16
+
17
+ async function set(key, value) {
18
+ try {
19
+ // 尝试解析为 JSON
20
+ let parsedValue;
21
+ try {
22
+ parsedValue = JSON.parse(value);
23
+ } catch {
24
+ parsedValue = value;
25
+ }
26
+
27
+ config.set(key, parsedValue);
28
+ console.log(`✅ 已设置 ${key} = ${JSON.stringify(parsedValue)}`);
29
+
30
+ // 🔑 工作目录同步提示
31
+ if (key === 'server.workDir' || key === 'projects.defaultWorkDir') {
32
+ console.log(`💡 提示: server.workDir 和 projects.defaultWorkDir 已自动同步`);
33
+ }
34
+ } catch (error) {
35
+ console.error(`❌ 设置配置失败: ${error.message}`);
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ async function list() {
41
+ try {
42
+ const allConfig = config.get();
43
+ console.log(JSON.stringify(allConfig, null, 2));
44
+ } catch (error) {
45
+ console.error(`❌ 获取配置失败: ${error.message}`);
46
+ process.exit(1);
47
+ }
48
+ }
49
+
50
+ async function edit() {
51
+ console.log('🚧 编辑配置命令正在开发中...');
52
+ }
53
+
54
+ async function reset() {
55
+ console.log('🚧 重置配置命令正在开发中...');
56
+ }
57
+
58
+ module.exports = { get, set, list, edit, reset };
@@ -0,0 +1,240 @@
1
+ /**
2
+ * 环境检查命令
3
+ */
4
+
5
+ const { execSync } = require('child_process');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const os = require('os');
9
+
10
+ /**
11
+ * 检查 Node.js
12
+ */
13
+ function checkNode() {
14
+ const version = process.version;
15
+ const major = parseInt(version.slice(1).split('.')[0]);
16
+
17
+ if (major < 18) {
18
+ return { status: 'warning', message: `Node.js: ${version} (推荐 18+)` };
19
+ }
20
+
21
+ return { status: 'ok', message: `Node.js: ${version}` };
22
+ }
23
+
24
+ /**
25
+ * 检查 Git
26
+ */
27
+ function checkGit() {
28
+ try {
29
+ const version = execSync('git --version', { encoding: 'utf-8' }).trim();
30
+ return { status: 'ok', message: version };
31
+ } catch {
32
+ return { status: 'error', message: 'Git 未安装' };
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 检查配置文件
38
+ */
39
+ function checkConfig() {
40
+ const config = require('../lib/config-manager');
41
+ const configPath = config.getConfigPath();
42
+
43
+ if (fs.existsSync(configPath)) {
44
+ return { status: 'ok', message: `配置文件: ${configPath}` };
45
+ }
46
+
47
+ return { status: 'warning', message: '配置文件不存在,将使用默认配置' };
48
+ }
49
+
50
+ /**
51
+ * 检查服务状态
52
+ */
53
+ async function checkService() {
54
+ const ProcessManager = require('../lib/process-manager');
55
+ const config = require('../lib/config-manager');
56
+ const pm = new ProcessManager(config);
57
+
58
+ try {
59
+ const isRunning = await pm.isRunning();
60
+ if (isRunning) {
61
+ const pid = pm.getPid();
62
+ return { status: 'ok', message: `服务运行中 (PID: ${pid})` };
63
+ }
64
+ return { status: 'warning', message: '服务未运行' };
65
+ } catch {
66
+ return { status: 'error', message: '服务连接失败' };
67
+ }
68
+ }
69
+
70
+ /**
71
+ * 检查依赖
72
+ */
73
+ function checkDependencies() {
74
+ const rootDir = process.env.CFIX_ROOT || path.resolve(__dirname, '../..');
75
+ const packageJson = path.join(rootDir, 'package.json');
76
+ const nodeModules = path.join(rootDir, 'node_modules');
77
+
78
+ if (!fs.existsSync(packageJson)) {
79
+ return { status: 'error', message: 'package.json 不存在' };
80
+ }
81
+
82
+ if (!fs.existsSync(nodeModules)) {
83
+ return { status: 'error', message: '依赖未安装,请运行 npm install' };
84
+ }
85
+
86
+ return { status: 'ok', message: '依赖已安装' };
87
+ }
88
+
89
+ /**
90
+ * 检查 AI 引擎
91
+ */
92
+ function checkAIEngines() {
93
+ const engines = [];
94
+
95
+ // 检查 Claude CLI
96
+ try {
97
+ execSync('claude --version', { encoding: 'utf-8', stdio: 'pipe' });
98
+ engines.push('Claude CLI');
99
+ } catch {
100
+ // Claude CLI 不可用
101
+ }
102
+
103
+ // 检查 Claude API 配置
104
+ if (process.env.ANTHROPIC_API_KEY) {
105
+ engines.push('Claude API');
106
+ }
107
+
108
+ // 检查 Zhipu GLM
109
+ if (process.env.ZHIPU_API_KEY) {
110
+ engines.push('Zhipu GLM');
111
+ }
112
+
113
+ if (engines.length === 0) {
114
+ return { status: 'warning', message: '未配置 AI 引擎' };
115
+ }
116
+
117
+ return { status: 'ok', message: `可用引擎: ${engines.join(', ')}` };
118
+ }
119
+
120
+ /**
121
+ * 检查端口占用
122
+ */
123
+ function checkPort() {
124
+ const config = require('../lib/config-manager');
125
+ const serviceConfig = config.getServiceConfig();
126
+ const port = serviceConfig.port || 3008;
127
+
128
+ try {
129
+ const net = require('net');
130
+ const server = net.createServer();
131
+
132
+ return new Promise((resolve) => {
133
+ server.once('error', () => {
134
+ resolve({ status: 'warning', message: `端口 ${port} 已被占用` });
135
+ });
136
+
137
+ server.once('listening', () => {
138
+ server.close();
139
+ resolve({ status: 'ok', message: `端口 ${port} 可用` });
140
+ });
141
+
142
+ server.listen(port);
143
+ });
144
+ } catch {
145
+ return { status: 'error', message: `端口 ${port} 检查失败` };
146
+ }
147
+ }
148
+
149
+ /**
150
+ * 检查系统资源
151
+ */
152
+ function checkSystemResources() {
153
+ const totalMem = Math.round(os.totalmem() / 1024 / 1024 / 1024);
154
+ const freeMem = Math.round(os.freemem() / 1024 / 1024 / 1024);
155
+ const cpus = os.cpus().length;
156
+
157
+ if (freeMem < 1) {
158
+ return { status: 'warning', message: `内存: ${freeMem}GB/${totalMem}GB (可用内存不足)` };
159
+ }
160
+
161
+ return { status: 'ok', message: `CPU: ${cpus}核 | 内存: ${freeMem}GB/${totalMem}GB` };
162
+ }
163
+
164
+ /**
165
+ * 检查项目注册表
166
+ */
167
+ function checkProjectRegistry() {
168
+ const config = require('../lib/config-manager');
169
+ const projects = config.listProjects();
170
+
171
+ if (projects.length === 0) {
172
+ return { status: 'warning', message: '未注册项目' };
173
+ }
174
+
175
+ return { status: 'ok', message: `已注册 ${projects.length} 个项目` };
176
+ }
177
+
178
+ /**
179
+ * 主处理函数
180
+ */
181
+ async function handle(options = {}) {
182
+ console.log('🔍 CodeFix 环境检查');
183
+ console.log('');
184
+
185
+ const checks = [
186
+ { name: 'Node.js', fn: checkNode },
187
+ { name: 'Git', fn: checkGit },
188
+ { name: '配置', fn: checkConfig },
189
+ { name: '依赖', fn: checkDependencies },
190
+ { name: 'AI 引擎', fn: checkAIEngines },
191
+ { name: '端口', fn: checkPort },
192
+ { name: '系统资源', fn: checkSystemResources },
193
+ { name: '项目注册表', fn: checkProjectRegistry },
194
+ { name: '服务', fn: checkService }
195
+ ];
196
+
197
+ let errors = 0;
198
+ let warnings = 0;
199
+
200
+ for (const check of checks) {
201
+ try {
202
+ const result = await check.fn();
203
+ const icon = result.status === 'ok' ? '✅' : result.status === 'warning' ? '⚠️' : '❌';
204
+
205
+ console.log(`${icon} ${check.name.padEnd(12)} ${result.message}`);
206
+
207
+ if (result.status === 'error') errors++;
208
+ if (result.status === 'warning') warnings++;
209
+ } catch (error) {
210
+ console.log(`❌ ${check.name.padEnd(12)} 检查失败: ${error.message}`);
211
+ errors++;
212
+ }
213
+ }
214
+
215
+ console.log('');
216
+ console.log('======================================');
217
+
218
+ if (errors > 0) {
219
+ console.log(`❌ 发现 ${errors} 个错误,请修复后重试`);
220
+ console.log('======================================');
221
+ process.exit(1);
222
+ } else if (warnings > 0) {
223
+ console.log(`⚠️ 发现 ${warnings} 个警告`);
224
+ console.log('');
225
+ console.log('建议操作:');
226
+ console.log(' cfix start # 启动服务');
227
+ console.log(' cfix project add <path> # 添加项目');
228
+ console.log(' cfix config set ai.defaultEngine <engine> # 配置 AI 引擎');
229
+ console.log('======================================');
230
+ } else {
231
+ console.log('✅ 所有检查通过!');
232
+ console.log('');
233
+ console.log('下一步:');
234
+ console.log(' cfix start # 启动服务');
235
+ console.log(' cfix "你的需求" # 创建修复任务');
236
+ console.log('======================================');
237
+ }
238
+ }
239
+
240
+ module.exports = { handle };
@@ -0,0 +1,211 @@
1
+ /**
2
+ * 修复命令
3
+ */
4
+
5
+ const ServiceClient = require("../lib/service-client");
6
+ const ProcessManager = require("../lib/process-manager");
7
+ const config = require("../lib/config-manager");
8
+ const { createFormatter } = require("../lib/formatter");
9
+ const path = require("path");
10
+
11
+ const pm = new ProcessManager(config);
12
+
13
+ /**
14
+ * 创建修复任务
15
+ */
16
+ async function handle(requirement, options) {
17
+ // 创建格式化器
18
+ const formatter = createFormatter(options.output);
19
+
20
+ // 如果没有提供需求,显示帮助
21
+ if (!requirement) {
22
+ formatter.error("请提供修复需求");
23
+ process.exit(1);
24
+ }
25
+
26
+ // 静默模式跳过所有装饰性输出
27
+ if (options.output !== "quiet") {
28
+ console.log("🔧 创建修复任务");
29
+ console.log("");
30
+ console.log("======================================");
31
+ console.log(`需求: ${requirement}`);
32
+ console.log("======================================");
33
+ console.log("");
34
+ }
35
+
36
+ try {
37
+ // 确保服务运行
38
+ await ensureServiceRunning(options.output);
39
+
40
+ // 检测或获取项目路径
41
+ const projectPath = await resolveProjectPath(options.project);
42
+
43
+ if (options.output !== "quiet") {
44
+ console.log(`📁 项目路径: ${projectPath}`);
45
+ console.log("");
46
+ }
47
+
48
+ // 创建任务
49
+ const client = new ServiceClient();
50
+ const taskOptions = {
51
+ projectPath,
52
+ repoUrl: options.repoUrl,
53
+ requirement,
54
+ runTests: options.runTests || false,
55
+ mergeToAlpha: options.mergeToAlpha || false,
56
+ mergeToBeta: options.mergeToBeta || false,
57
+ autoConfirm: options.yes || false,
58
+ aiEngine: options.engine,
59
+ maxTurns: options.maxTurns,
60
+ };
61
+
62
+ if (options.output !== "quiet") {
63
+ console.log("⏳ 创建任务...");
64
+ }
65
+
66
+ const result = await client.createFixTask(taskOptions);
67
+
68
+ // JSON 模式直接输出结果
69
+ if (options.output === "json") {
70
+ formatter.output(result);
71
+ return result.taskId;
72
+ }
73
+
74
+ // Quiet 模式只输出任务 ID
75
+ if (options.output === "quiet") {
76
+ console.log(result.taskId);
77
+ return result.taskId;
78
+ }
79
+
80
+ console.log("");
81
+ console.log("======================================");
82
+ console.log("✅ 任务已创建");
83
+ console.log("======================================");
84
+ console.log("");
85
+ console.log(`任务 ID: ${result.taskId}`);
86
+ console.log(`状态: ${result.status || "padding"}`);
87
+ console.log("");
88
+
89
+ // 如果需要等待任务完成
90
+ if (options.wait) {
91
+ console.log("⏳ 等待任务完成...");
92
+ console.log("");
93
+
94
+ const finalTask = await client.waitForTask(result.taskId, {
95
+ timeout: 1800000, // 30分钟
96
+ interval: 2000,
97
+ onProgress: (task) => {
98
+ if (task.progress) {
99
+ process.stdout.write(`\r进度: ${task.progress}%`);
100
+ }
101
+ },
102
+ });
103
+
104
+ console.log("");
105
+ console.log("");
106
+ console.log("======================================");
107
+ console.log("✅ 任务完成");
108
+ console.log("======================================");
109
+ console.log("");
110
+
111
+ // 显示结果
112
+ if (finalTask.result) {
113
+ console.log("修复结果:");
114
+ console.log(
115
+ ` 修改文件: ${finalTask.result.files?.modified?.length || 0} 个`
116
+ );
117
+ console.log(
118
+ ` 新增文件: ${finalTask.result.files?.added?.length || 0} 个`
119
+ );
120
+ console.log(
121
+ ` 删除文件: ${finalTask.result.files?.deleted?.length || 0} 个`
122
+ );
123
+ }
124
+
125
+ // 如果需要确认
126
+ if (finalTask.status === "waiting_approval") {
127
+ console.log("");
128
+ console.log("⚠️ 任务需要人工确认");
129
+ console.log("");
130
+ console.log("使用以下命令查看修改:");
131
+ console.log(` cfix task ${result.taskId} # 查看详情`);
132
+ console.log(` cfix approve ${result.taskId} # 批准修改`);
133
+ console.log(` cfix reject ${result.taskId} # 拒绝修改`);
134
+ }
135
+ } else {
136
+ console.log("提示:");
137
+ console.log(` cfix task ${result.taskId} # 查看任务详情`);
138
+ console.log(` cfix tasks # 查看所有任务`);
139
+ console.log(` cfix wait ${result.taskId} # 等待任务完成`);
140
+ }
141
+
142
+ return result.taskId;
143
+ } catch (error) {
144
+ if (options.output === "json") {
145
+ formatter.error("创建任务失败", error);
146
+ } else if (options.output === "quiet") {
147
+ console.error(`错误: ${error.message}`);
148
+ } else {
149
+ console.error("");
150
+ console.error("======================================");
151
+ console.error(`❌ 创建任务失败: ${error.message}`);
152
+ console.error("======================================");
153
+ }
154
+ process.exit(1);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 确保服务运行
160
+ */
161
+ async function ensureServiceRunning(outputFormat = "interactive") {
162
+ const isRunning = await pm.isRunning();
163
+
164
+ if (!isRunning) {
165
+ if (outputFormat !== "quiet") {
166
+ console.log("⚠️ 服务未运行,正在自动启动...");
167
+ console.log("");
168
+ }
169
+
170
+ await pm.start({ daemon: true, wait: true });
171
+
172
+ if (outputFormat !== "quiet") {
173
+ console.log("");
174
+ console.log("✅ 服务已启动");
175
+ console.log("");
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * 解析项目路径
182
+ */
183
+ async function resolveProjectPath(projectOption) {
184
+ // 如果指定了项目路径
185
+ if (projectOption) {
186
+ // 检查是否是已注册的项目别名/名称
187
+ const project = config.findProject(projectOption);
188
+ if (project) {
189
+ return project.path;
190
+ }
191
+ // 直接返回路径
192
+ return path.resolve(projectOption);
193
+ }
194
+
195
+ // 尝试检测当前目录
196
+ const currentProject = config.detectCurrentProject();
197
+ if (currentProject) {
198
+ return currentProject.path;
199
+ }
200
+
201
+ // 使用默认项目
202
+ const defaultProject = config.getDefaultProject();
203
+ if (defaultProject) {
204
+ return defaultProject.path;
205
+ }
206
+
207
+ // 如果没有找到项目
208
+ throw new Error("未找到项目,请使用 --project 指定项目路径");
209
+ }
210
+
211
+ module.exports = { handle };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 帮助命令
3
+ */
4
+
5
+ const config = require('../lib/config-manager');
6
+
7
+ /**
8
+ * 显示帮助信息
9
+ */
10
+ async function handle() {
11
+ console.log(`
12
+ ╔═══════════════════════════════════════════════════════════════╗
13
+ ║ CodeFix CLI v${require('../../package.json').version} ║
14
+ ║ AI 自动代码修复工具 ║
15
+ ╚═══════════════════════════════════════════════════════════════╝
16
+
17
+ 核心命令:
18
+ cfix <requirement> 创建修复任务
19
+ cfix start 启动服务
20
+ cfix stop 停止服务
21
+ cfix restart 重启服务
22
+ cfix status 查看服务状态
23
+ cfix logs [-f] 查看日志
24
+
25
+ 项目管理:
26
+ cfix project add <path> 添加项目
27
+ cfix project remove <path> 移除项目
28
+ cfix project list 列出所有项目
29
+ cfix project info <path> 项目详情
30
+
31
+ 配置管理:
32
+ cfix config get <key> 获取配置
33
+ cfix config set <key> <value> 设置配置
34
+ cfix config list 列出配置
35
+ cfix config edit 编辑配置
36
+
37
+ 任务管理:
38
+ cfix tasks [-p <project>] 查看任务列表
39
+ cfix task <taskId> 查看任务详情
40
+ cfix approve <taskId> 批准任务
41
+ cfix reject <taskId> 拒绝任务
42
+ cfix cancel <taskId> 取消任务
43
+
44
+ 工具命令:
45
+ cfix init [path] 初始化项目配置
46
+ cfix monitor 打开监控面板
47
+ cfix doctor 环境检查
48
+ cfix version 版本信息
49
+
50
+ 更多帮助:
51
+ cfix <command> --help 查看命令详细帮助
52
+
53
+ 配置文件:
54
+ 全局配置: ${config.getConfigPath()}
55
+ 项目配置: <project>/.cfix.json
56
+
57
+ 文档:
58
+ https://github.com/your-repo/cfix
59
+ `);
60
+ }
61
+
62
+ module.exports = { handle };