git-ai-shen 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.
- package/README.en.md +36 -0
- package/README.md +23 -0
- package/bin/git-ai.js +97 -0
- package/git-ai-working.js +393 -0
- package/lib/commands/commit.js +256 -0
- package/lib/core/ai-reviewer.js +321 -0
- package/lib/core/config.js +120 -0
- package/lib/core/git-operations.js +285 -0
- package/lib/core/simple-config.js +171 -0
- package/lib/index.js +19 -0
- package/lib/utils/file-utils.js +98 -0
- package/lib/utils/logger.js +71 -0
- package/node +0 -0
- package/package.json +25 -0
- package/simple-init.js +76 -0
- package/test-ai.js +42 -0
- package/test-init.js +28 -0
package/README.en.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# git-ai
|
|
2
|
+
|
|
3
|
+
#### Description
|
|
4
|
+
智能git提交
|
|
5
|
+
|
|
6
|
+
#### Software Architecture
|
|
7
|
+
Software architecture description
|
|
8
|
+
|
|
9
|
+
#### Installation
|
|
10
|
+
|
|
11
|
+
1. xxxx
|
|
12
|
+
2. xxxx
|
|
13
|
+
3. xxxx
|
|
14
|
+
|
|
15
|
+
#### Instructions
|
|
16
|
+
|
|
17
|
+
1. xxxx
|
|
18
|
+
2. xxxx
|
|
19
|
+
3. xxxx
|
|
20
|
+
|
|
21
|
+
#### Contribution
|
|
22
|
+
|
|
23
|
+
1. Fork the repository
|
|
24
|
+
2. Create Feat_xxx branch
|
|
25
|
+
3. Commit your code
|
|
26
|
+
4. Create Pull Request
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#### Gitee Feature
|
|
30
|
+
|
|
31
|
+
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
|
32
|
+
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
|
33
|
+
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
|
34
|
+
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
|
35
|
+
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
|
36
|
+
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Git AI - 智能Git提交工具
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/git-ai)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
将git add, commit, pull, push合并,并集成AI代码审查的智能Git工具。
|
|
7
|
+
|
|
8
|
+
## ✨ 特性
|
|
9
|
+
|
|
10
|
+
- 🚀 一键完成 `add` → `commit` → `pull` → `push`
|
|
11
|
+
- 🤖 AI生成规范的提交信息(支持OpenAI)
|
|
12
|
+
- 🔍 AI代码审查,检测风险项和优化建议
|
|
13
|
+
- ⚡ 基础静态检查(硬编码密钥、TODO、调试代码等)
|
|
14
|
+
- 🎨 美观的命令行界面
|
|
15
|
+
- ⚙️ 灵活的配置选项
|
|
16
|
+
- 🔌 支持自定义API
|
|
17
|
+
|
|
18
|
+
## 📦 安装
|
|
19
|
+
|
|
20
|
+
### 全局安装
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g git-ai
|
package/bin/git-ai.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
const { program } = require('commander');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
// 获取版本号
|
|
8
|
+
let version = '1.0.0';
|
|
9
|
+
try {
|
|
10
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
11
|
+
version = packageJson.version;
|
|
12
|
+
} catch (error) {
|
|
13
|
+
// 忽略错误
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.name('git-ai')
|
|
18
|
+
.description('智能Git提交工具 - 集成AI代码审查')
|
|
19
|
+
.version(version, '-v, --version', '显示版本号');
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.command('commit [message]')
|
|
23
|
+
.description('执行智能提交')
|
|
24
|
+
.option('-m, --message <message>', '提交信息')
|
|
25
|
+
.option('--no-push', '只提交不推送')
|
|
26
|
+
.option('--skip-review', '跳过AI代码审查')
|
|
27
|
+
.option('--no-auto-add', '不自动添加变更')
|
|
28
|
+
.action(async (message, options) => {
|
|
29
|
+
try {
|
|
30
|
+
const { CommitCommand } = require('../lib/commands/commit');
|
|
31
|
+
const commitCmd = new CommitCommand();
|
|
32
|
+
await commitCmd.run(message || options.message, options);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(chalk.red(`❌ 错误: ${error.message}`));
|
|
35
|
+
console.error(chalk.red(error.stack));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
program
|
|
41
|
+
.command('init')
|
|
42
|
+
.description('初始化配置')
|
|
43
|
+
.action(() => {
|
|
44
|
+
console.log(chalk.cyan('执行 init 命令...'));
|
|
45
|
+
try {
|
|
46
|
+
// 使用 simple-config 而不是 config
|
|
47
|
+
const { SimpleConfigManager } = require('../lib/core/simple-config');
|
|
48
|
+
console.log(chalk.gray('SimpleConfigManager 加载成功'));
|
|
49
|
+
|
|
50
|
+
const config = new SimpleConfigManager();
|
|
51
|
+
console.log(chalk.gray('SimpleConfigManager 实例化成功'));
|
|
52
|
+
|
|
53
|
+
config.init();
|
|
54
|
+
console.log(chalk.gray('config.init() 执行完成'));
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(chalk.red(`❌ 错误: ${error.message}`));
|
|
57
|
+
console.error(chalk.red(error.stack));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
.command('config')
|
|
64
|
+
.description('管理配置')
|
|
65
|
+
.option('-l, --list', '列出所有配置')
|
|
66
|
+
.option('-g, --get <key>', '获取配置项')
|
|
67
|
+
.option('-s, --set <key=value>', '设置配置项')
|
|
68
|
+
.action((options) => {
|
|
69
|
+
try {
|
|
70
|
+
const { SimpleConfigManager } = require('../lib/core/simple-config');
|
|
71
|
+
const config = new SimpleConfigManager();
|
|
72
|
+
|
|
73
|
+
if (options.list) {
|
|
74
|
+
console.log(chalk.cyan('当前配置:'));
|
|
75
|
+
console.log(JSON.stringify(config.getAll(), null, 2));
|
|
76
|
+
} else if (options.get) {
|
|
77
|
+
const value = config.get(options.get);
|
|
78
|
+
console.log(`${options.get}=${value}`);
|
|
79
|
+
} else if (options.set) {
|
|
80
|
+
const [key, value] = options.set.split('=');
|
|
81
|
+
config.set(key, value);
|
|
82
|
+
console.log(chalk.green(`✅ 已设置 ${key}=${value}`));
|
|
83
|
+
} else {
|
|
84
|
+
program.help();
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(chalk.red(`❌ 错误: ${error.message}`));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 如果没有提供命令,显示帮助
|
|
93
|
+
if (process.argv.length === 2) {
|
|
94
|
+
program.help();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { exec } = require('child_process');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
// ==================== 配置管理 ====================
|
|
9
|
+
const CONFIG_DIR = path.join(os.homedir(), '.git-ai');
|
|
10
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
11
|
+
|
|
12
|
+
// 确保配置目录存在
|
|
13
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
14
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 颜色函数
|
|
18
|
+
const colors = {
|
|
19
|
+
cyan: (text) => `\x1b[36m${text}\x1b[0m`,
|
|
20
|
+
green: (text) => `\x1b[32m${text}\x1b[0m`,
|
|
21
|
+
yellow: (text) => `\x1b[33m${text}\x1b[0m`,
|
|
22
|
+
gray: (text) => `\x1b[90m${text}\x1b[0m`,
|
|
23
|
+
red: (text) => `\x1b[31m${text}\x1b[0m`,
|
|
24
|
+
blue: (text) => `\x1b[34m${text}\x1b[0m`
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 默认配置
|
|
28
|
+
const DEFAULT_CONFIG = {
|
|
29
|
+
ai: {
|
|
30
|
+
apiKey: '',
|
|
31
|
+
apiUrl: 'https://api.openai.com/v1',
|
|
32
|
+
model: 'gpt-3.5-turbo'
|
|
33
|
+
},
|
|
34
|
+
git: {
|
|
35
|
+
autoAdd: true,
|
|
36
|
+
autoPush: true,
|
|
37
|
+
remote: 'origin'
|
|
38
|
+
},
|
|
39
|
+
review: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
skipPatterns: ['*.lock', '*.log', 'node_modules', 'dist', 'build'],
|
|
42
|
+
maxFileSize: 100 * 1024,
|
|
43
|
+
checkSecrets: true,
|
|
44
|
+
checkTodos: true,
|
|
45
|
+
checkDebug: true
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// 读取配置
|
|
50
|
+
function getConfig() {
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
53
|
+
const data = fs.readFileSync(CONFIG_FILE, 'utf8');
|
|
54
|
+
return JSON.parse(data);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.log(colors.yellow(`⚠️ 读取配置失败: ${error.message}`));
|
|
58
|
+
}
|
|
59
|
+
return DEFAULT_CONFIG;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 保存配置
|
|
63
|
+
function saveConfig(config) {
|
|
64
|
+
try {
|
|
65
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
|
|
66
|
+
return true;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log(colors.red(`❌ 保存配置失败: ${error.message}`));
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ==================== Git 操作 ====================
|
|
74
|
+
async function getChangedFiles() {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
exec('git status --porcelain', (error, stdout) => {
|
|
77
|
+
if (error) {
|
|
78
|
+
reject(error);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const files = [];
|
|
83
|
+
const lines = stdout.split('\n').filter(line => line.trim());
|
|
84
|
+
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const file = line.substring(3); // 跳过状态标记
|
|
87
|
+
files.push(file);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
resolve(files);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function gitAdd() {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
exec('git add .', (error) => {
|
|
98
|
+
if (error) reject(error);
|
|
99
|
+
else resolve();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function gitCommit(message) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
exec(`git commit -m "${message}"`, (error) => {
|
|
107
|
+
if (error) reject(error);
|
|
108
|
+
else resolve();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function gitPush() {
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
exec('git push', (error) => {
|
|
116
|
+
if (error) reject(error);
|
|
117
|
+
else resolve();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function isGitRepo() {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
exec('git rev-parse --git-dir', (error) => {
|
|
125
|
+
resolve(!error);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ==================== 代码审查 ====================
|
|
131
|
+
async function reviewFile(filePath, content) {
|
|
132
|
+
const risks = [];
|
|
133
|
+
const suggestions = [];
|
|
134
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
135
|
+
|
|
136
|
+
// 检查硬编码密钥
|
|
137
|
+
if (/password\s*[:=]\s*['"][^'"]+['"]/i.test(content)) {
|
|
138
|
+
risks.push(`${filePath}: 可能包含密码硬编码`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (/api[_-]?key\s*[:=]\s*['"][^'"]+['"]/i.test(content)) {
|
|
142
|
+
risks.push(`${filePath}: 可能包含API密钥硬编码`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (/token\s*[:=]\s*['"][^'"]+['"]/i.test(content)) {
|
|
146
|
+
risks.push(`${filePath}: 可能包含令牌硬编码`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 检查TODO
|
|
150
|
+
if (/TODO/i.test(content)) {
|
|
151
|
+
suggestions.push(`${filePath}: 包含待办事项(TODO)`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (/FIXME/i.test(content)) {
|
|
155
|
+
suggestions.push(`${filePath}: 包含待修复项(FIXME)`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// 检查调试代码
|
|
159
|
+
if (ext === '.js' || ext === '.ts' || ext === '.jsx' || ext === '.tsx') {
|
|
160
|
+
if (/console\.log/.test(content)) {
|
|
161
|
+
suggestions.push(`${filePath}: 包含console.log,考虑移除`);
|
|
162
|
+
}
|
|
163
|
+
if (/debugger;/.test(content)) {
|
|
164
|
+
risks.push(`${filePath}: 包含debugger语句`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return { risks, suggestions };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function reviewFiles(files) {
|
|
172
|
+
const results = { risks: [], suggestions: [] };
|
|
173
|
+
|
|
174
|
+
for (const file of files) {
|
|
175
|
+
try {
|
|
176
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
177
|
+
const review = await reviewFile(file, content);
|
|
178
|
+
results.risks.push(...review.risks);
|
|
179
|
+
results.suggestions.push(...review.suggestions);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
// 忽略无法读取的文件
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return results;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ==================== 命令处理 ====================
|
|
189
|
+
async function initCommand() {
|
|
190
|
+
console.log(colors.cyan('\n' + '='.repeat(50)));
|
|
191
|
+
console.log(colors.cyan('🔧 Git AI 配置初始化'));
|
|
192
|
+
console.log(colors.cyan('='.repeat(50)) + '\n');
|
|
193
|
+
|
|
194
|
+
const config = DEFAULT_CONFIG;
|
|
195
|
+
const rl = readline.createInterface({
|
|
196
|
+
input: process.stdin,
|
|
197
|
+
output: process.stdout
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log(colors.yellow('(可选) 配置 OpenAI API 密钥'));
|
|
201
|
+
console.log(colors.gray('如果不配置,将使用基础功能\n'));
|
|
202
|
+
|
|
203
|
+
rl.question(colors.cyan('OpenAI API密钥 (直接回车跳过): '), (apiKey) => {
|
|
204
|
+
if (apiKey.trim()) {
|
|
205
|
+
config.ai.apiKey = apiKey.trim();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (saveConfig(config)) {
|
|
209
|
+
console.log(colors.green('\n✅ 配置初始化完成!'));
|
|
210
|
+
console.log(colors.gray(`配置文件: ${CONFIG_FILE}`));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
rl.close();
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async function commitCommand(message, options) {
|
|
218
|
+
console.log(colors.cyan('\n' + '='.repeat(50)));
|
|
219
|
+
console.log(colors.cyan('🔍 Git AI 智能提交'));
|
|
220
|
+
console.log(colors.cyan('='.repeat(50)) + '\n');
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
// 检查是否是git仓库
|
|
224
|
+
if (!await isGitRepo()) {
|
|
225
|
+
console.log(colors.red('❌ 当前目录不是Git仓库'));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 获取变更文件
|
|
230
|
+
console.log(colors.blue('📝 检测文件变更...'));
|
|
231
|
+
const changedFiles = await getChangedFiles();
|
|
232
|
+
|
|
233
|
+
if (changedFiles.length === 0) {
|
|
234
|
+
console.log(colors.yellow('⚠️ 没有检测到文件变更'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log(colors.green(`✅ 检测到 ${changedFiles.length} 个变更文件:`));
|
|
239
|
+
changedFiles.forEach(file => {
|
|
240
|
+
console.log(` ${colors.gray('-')} ${file}`);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// 代码审查
|
|
244
|
+
const config = getConfig();
|
|
245
|
+
if (!options.skipReview && config.review.enabled) {
|
|
246
|
+
console.log(colors.blue('\n🔍 代码审查中...'));
|
|
247
|
+
const review = await reviewFiles(changedFiles);
|
|
248
|
+
|
|
249
|
+
if (review.risks.length > 0) {
|
|
250
|
+
console.log(colors.yellow('\n⚠️ 检测到风险项:'));
|
|
251
|
+
review.risks.forEach(risk => {
|
|
252
|
+
console.log(` ${colors.red('[风险]')} ${risk}`);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (review.suggestions.length > 0) {
|
|
257
|
+
console.log(colors.blue('\n💡 优化建议:'));
|
|
258
|
+
review.suggestions.forEach(suggestion => {
|
|
259
|
+
console.log(` ${colors.yellow('[建议]')} ${suggestion}`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (review.risks.length > 0) {
|
|
264
|
+
const rl = readline.createInterface({
|
|
265
|
+
input: process.stdin,
|
|
266
|
+
output: process.stdout
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const answer = await new Promise(resolve => {
|
|
270
|
+
rl.question(colors.yellow('\n存在风险项,是否继续提交?(y/N): '), resolve);
|
|
271
|
+
});
|
|
272
|
+
rl.close();
|
|
273
|
+
|
|
274
|
+
if (answer.toLowerCase() !== 'y') {
|
|
275
|
+
console.log(colors.yellow('⚠️ 提交已取消'));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// 获取提交信息
|
|
282
|
+
let commitMessage = message;
|
|
283
|
+
if (!commitMessage) {
|
|
284
|
+
const rl = readline.createInterface({
|
|
285
|
+
input: process.stdin,
|
|
286
|
+
output: process.stdout
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
commitMessage = await new Promise(resolve => {
|
|
290
|
+
rl.question(colors.cyan('\n请输入提交信息: '), resolve);
|
|
291
|
+
});
|
|
292
|
+
rl.close();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!commitMessage) {
|
|
296
|
+
console.log(colors.red('❌ 提交信息不能为空'));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 添加文件
|
|
301
|
+
if (config.git.autoAdd && !options.noAutoAdd) {
|
|
302
|
+
console.log(colors.blue('\n📦 添加文件到暂存区...'));
|
|
303
|
+
await gitAdd();
|
|
304
|
+
console.log(colors.green('✅ 添加成功'));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 提交
|
|
308
|
+
console.log(colors.blue('💾 提交变更...'));
|
|
309
|
+
await gitCommit(commitMessage);
|
|
310
|
+
console.log(colors.green('✅ 提交成功'));
|
|
311
|
+
|
|
312
|
+
// 推送
|
|
313
|
+
if (config.git.autoPush && !options.noPush) {
|
|
314
|
+
const rl = readline.createInterface({
|
|
315
|
+
input: process.stdin,
|
|
316
|
+
output: process.stdout
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const answer = await new Promise(resolve => {
|
|
320
|
+
rl.question(colors.cyan('\n是否推送到远程?(y/n): '), resolve);
|
|
321
|
+
});
|
|
322
|
+
rl.close();
|
|
323
|
+
|
|
324
|
+
if (answer.toLowerCase() === 'y') {
|
|
325
|
+
console.log(colors.blue('⬆️ 推送到远程...'));
|
|
326
|
+
await gitPush();
|
|
327
|
+
console.log(colors.green('✅ 推送成功'));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.log(colors.green(`\n✨ 完成!提交信息: ${commitMessage}`));
|
|
332
|
+
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.log(colors.red(`❌ 错误: ${error.message}`));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function showHelp() {
|
|
339
|
+
console.log(`
|
|
340
|
+
${colors.cyan('Git AI - 智能Git提交工具')}
|
|
341
|
+
|
|
342
|
+
${colors.yellow('使用方法:')}
|
|
343
|
+
git-ai init 初始化配置
|
|
344
|
+
git-ai commit [message] 执行提交
|
|
345
|
+
git-ai [message] 同上
|
|
346
|
+
git-ai --help 显示帮助
|
|
347
|
+
|
|
348
|
+
${colors.yellow('选项:')}
|
|
349
|
+
--no-push 只提交不推送
|
|
350
|
+
--skip-review 跳过代码审查
|
|
351
|
+
--no-auto-add 不自动添加文件
|
|
352
|
+
|
|
353
|
+
${colors.yellow('示例:')}
|
|
354
|
+
git-ai init
|
|
355
|
+
git-ai commit "feat: 新功能"
|
|
356
|
+
git-ai "fix: 修复bug" --no-push
|
|
357
|
+
`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ==================== 主函数 ====================
|
|
361
|
+
async function main() {
|
|
362
|
+
const args = process.argv.slice(2);
|
|
363
|
+
const command = args[0];
|
|
364
|
+
const options = {
|
|
365
|
+
noPush: args.includes('--no-push'),
|
|
366
|
+
skipReview: args.includes('--skip-review'),
|
|
367
|
+
noAutoAdd: args.includes('--no-auto-add')
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// 过滤掉选项,获取真正的消息
|
|
371
|
+
const message = args.filter(arg => !arg.startsWith('--'))[1];
|
|
372
|
+
|
|
373
|
+
if (command === 'init') {
|
|
374
|
+
await initCommand();
|
|
375
|
+
} else if (command === 'commit' || !command || command.startsWith('--')) {
|
|
376
|
+
// 如果第一个参数是选项,或者没有参数,执行commit
|
|
377
|
+
const msg = command && !command.startsWith('--') ? command : message;
|
|
378
|
+
await commitCommand(msg, options);
|
|
379
|
+
} else if (command === '--help' || command === '-h') {
|
|
380
|
+
showHelp();
|
|
381
|
+
} else {
|
|
382
|
+
// 默认当作提交信息
|
|
383
|
+
await commitCommand(command, options);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// 捕获未处理的错误
|
|
388
|
+
process.on('uncaughtException', (error) => {
|
|
389
|
+
console.log(colors.red(`\n❌ 未捕获的错误: ${error.message}`));
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// 启动
|
|
393
|
+
main();
|