@dongowu/git-ai-cli 1.0.2 → 1.0.6

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.md CHANGED
@@ -69,6 +69,9 @@ git-ai
69
69
  | 🏠 **本地部署** | 支持 Ollama、LM Studio,数据不出本机 |
70
70
  | 🔍 **智能 Diff** | 自动过滤 lock 文件,Token 优化截断 |
71
71
  | 💬 **交互式** | 提交 / 编辑 / 重新生成 / 取消 |
72
+ | ⚡ **一键提交** | `-y` 参数跳过确认,CI/CD 友好 |
73
+ | 🎯 **多条候选** | `-n 3` 生成多条消息供选择 |
74
+ | 🪝 **Git Hook** | 自动集成到 git commit 流程 |
72
75
  | 📝 **规范化** | 遵循 Conventional Commits 标准 |
73
76
  | 🌍 **中英双语** | 支持中文和英文输出 |
74
77
 
@@ -124,6 +127,91 @@ git-ai config
124
127
 
125
128
  ## 🔧 高级功能
126
129
 
130
+ ### 命令结构
131
+
132
+ ```bash
133
+ git-ai # 交互式生成并提交(默认)
134
+ git-ai commit # 同上,显式提交命令
135
+ git-ai msg # 仅生成消息,输出到 stdout(供脚本/Hook 使用)
136
+ git-ai config # 配置 AI 服务商
137
+ git-ai hook # 管理 Git Hook
138
+ ```
139
+
140
+ ### 命令行选项
141
+
142
+ ```bash
143
+ # 一键提交(跳过确认,CI/CD 友好)
144
+ git-ai -y
145
+ git-ai --yes
146
+
147
+ # 生成多条候选消息
148
+ git-ai -n 3
149
+ git-ai --num 3
150
+
151
+ # 组合使用
152
+ git-ai -y -n 3 # 生成 3 条,自动选择第一条提交
153
+ ```
154
+
155
+ ### `git-ai msg` - 脚本友好模式
156
+
157
+ 专为 Hook 和脚本设计,仅输出 commit message:
158
+
159
+ ```bash
160
+ # 纯文本输出
161
+ git-ai msg
162
+
163
+ # JSON 格式输出(含元数据)
164
+ git-ai msg --json
165
+
166
+ # 静默模式(无 spinner)
167
+ git-ai msg --quiet
168
+
169
+ # 生成多条
170
+ git-ai msg -n 3
171
+ ```
172
+
173
+ JSON 输出示例:
174
+ ```json
175
+ {
176
+ "success": true,
177
+ "message": "feat(auth): add OAuth2 login support",
178
+ "metadata": {
179
+ "stagedFiles": ["src/auth.ts", "src/login.ts"],
180
+ "truncated": false,
181
+ "ignoredFiles": ["package-lock.json"]
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### Git Hook 集成
187
+
188
+ 自动集成到 `git commit` 流程,无需手动运行 `git-ai`:
189
+
190
+ ```bash
191
+ # 安装 hook
192
+ git-ai hook install
193
+
194
+ # 查看状态
195
+ git-ai hook status
196
+
197
+ # 移除 hook
198
+ git-ai hook remove
199
+ ```
200
+
201
+ **Hook 特性:**
202
+ - **链式执行**:如果已有 `prepare-commit-msg` hook,git-ai 会作为 wrapper 执行,原 hook 仍会运行
203
+ - **递归保护**:通过环境变量防止 hook 递归调用
204
+ - **智能跳过**:使用 `-m` 参数或 merge/amend 时自动跳过
205
+
206
+ 安装后,直接运行 `git commit`(不带 `-m`)会自动生成 commit message:
207
+
208
+ ```bash
209
+ git add .
210
+ git commit # 自动调用 git-ai msg 生成消息
211
+ ```
212
+
213
+ > 💡 跳过 hook: `git commit --no-verify`
214
+
127
215
  ### 自动忽略的文件
128
216
 
129
217
  以下文件会自动从 Diff 分析中排除,避免浪费 Token:
@@ -166,8 +254,47 @@ git-ai
166
254
  - **Local deployment**: Ollama, LM Studio - keep your data private
167
255
  - **Smart diff**: Auto-filter lock files, token optimization
168
256
  - **Interactive**: Commit / Edit / Regenerate / Cancel
257
+ - **One-click commit**: `-y` flag for CI/CD pipelines
258
+ - **Multiple choices**: `-n 3` to generate multiple options
259
+ - **Git Hook**: Auto-integrate with `git commit`
169
260
  - **Conventional Commits**: Standard commit message format
170
261
 
262
+ ### Commands
263
+
264
+ ```bash
265
+ git-ai # Interactive commit (default)
266
+ git-ai commit # Same as above, explicit command
267
+ git-ai msg # Generate message only (stdout, for scripts/hooks)
268
+ git-ai config # Configure AI provider
269
+ git-ai hook # Manage Git hooks
270
+ ```
271
+
272
+ ### CLI Options
273
+
274
+ ```bash
275
+ # Auto commit (skip confirmation)
276
+ git-ai -y
277
+
278
+ # Generate multiple choices
279
+ git-ai -n 3
280
+
281
+ # Script-friendly message generation
282
+ git-ai msg # Plain text output
283
+ git-ai msg --json # JSON output with metadata
284
+ git-ai msg --quiet # No spinner
285
+
286
+ # Git Hook management
287
+ git-ai hook install # Install hook (chains with existing hooks)
288
+ git-ai hook remove # Remove hook
289
+ git-ai hook status # Check status
290
+ ```
291
+
292
+ ### Git Hook Features
293
+
294
+ - **Hook chaining**: Works alongside existing `prepare-commit-msg` hooks
295
+ - **Recursion protection**: Prevents infinite loops via environment variable
296
+ - **Smart skip**: Skips when using `-m`, merge, or amend
297
+
171
298
  ### Workflow
172
299
 
173
300
  ```
package/dist/cli.js CHANGED
@@ -4,14 +4,29 @@ import chalk from 'chalk';
4
4
  import { createRequire } from 'node:module';
5
5
  import { runConfig } from './commands/config.js';
6
6
  import { runCommit } from './commands/commit.js';
7
+ import { runMsg } from './commands/msg.js';
8
+ import { runHook } from './commands/hook.js';
7
9
  const require = createRequire(import.meta.url);
8
10
  const pkg = require('../package.json');
9
11
  const cli = cac('git-ai');
12
+ // Default command (backward compatible) - interactive commit
10
13
  cli
11
- .command('', 'Generate AI-powered commit message')
12
- .action(async () => {
14
+ .command('', 'Generate AI-powered commit message (interactive)')
15
+ .option('-y, --yes', 'Skip confirmation and commit directly')
16
+ .option('-n, --num <count>', 'Generate multiple commit messages to choose from', { default: 1 })
17
+ .option('--hook', '[deprecated] Use `git-ai msg` instead')
18
+ .action(async (options) => {
13
19
  try {
14
- await runCommit();
20
+ // Deprecated --hook redirects to msg command behavior
21
+ if (options.hook) {
22
+ const { runMsg } = await import('./commands/msg.js');
23
+ await runMsg({ quiet: true });
24
+ return;
25
+ }
26
+ await runCommit({
27
+ autoCommit: options.yes ?? false,
28
+ numChoices: Math.min(Math.max(Number(options.num) || 1, 1), 5),
29
+ });
15
30
  }
16
31
  catch (error) {
17
32
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -19,6 +34,49 @@ cli
19
34
  process.exit(1);
20
35
  }
21
36
  });
37
+ // Explicit commit subcommand
38
+ cli
39
+ .command('commit', 'Generate and commit with AI message (interactive)')
40
+ .option('-y, --yes', 'Skip confirmation and commit directly')
41
+ .option('-n, --num <count>', 'Generate multiple commit messages to choose from', { default: 1 })
42
+ .action(async (options) => {
43
+ try {
44
+ await runCommit({
45
+ autoCommit: options.yes ?? false,
46
+ numChoices: Math.min(Math.max(Number(options.num) || 1, 1), 5),
47
+ });
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : 'Unknown error';
51
+ console.error(chalk.red(`\n❌ Error: ${message}\n`));
52
+ process.exit(1);
53
+ }
54
+ });
55
+ // Message-only command (for hooks and scripts)
56
+ cli
57
+ .command('msg', 'Generate commit message only (stdout, for hooks/scripts)')
58
+ .option('-n, --num <count>', 'Generate multiple messages', { default: 1 })
59
+ .option('--json', 'Output as JSON')
60
+ .option('--quiet', 'Suppress spinner and colors')
61
+ .action(async (options) => {
62
+ try {
63
+ await runMsg({
64
+ num: Math.min(Math.max(Number(options.num) || 1, 1), 5),
65
+ json: options.json ?? false,
66
+ quiet: options.quiet ?? false,
67
+ });
68
+ }
69
+ catch (error) {
70
+ if (options.json) {
71
+ console.log(JSON.stringify({ success: false, error: String(error) }));
72
+ }
73
+ else if (!options.quiet) {
74
+ const message = error instanceof Error ? error.message : 'Unknown error';
75
+ console.error(chalk.red(`\n❌ Error: ${message}\n`));
76
+ }
77
+ process.exit(1);
78
+ }
79
+ });
22
80
  cli
23
81
  .command('config', 'Configure AI provider settings')
24
82
  .action(async () => {
@@ -31,6 +89,18 @@ cli
31
89
  process.exit(1);
32
90
  }
33
91
  });
92
+ cli
93
+ .command('hook <action>', 'Manage Git hooks (install/remove/status)')
94
+ .action(async (action) => {
95
+ try {
96
+ await runHook(action);
97
+ }
98
+ catch (error) {
99
+ const message = error instanceof Error ? error.message : 'Unknown error';
100
+ console.error(chalk.red(`\n❌ Error: ${message}\n`));
101
+ process.exit(1);
102
+ }
103
+ });
34
104
  cli.help();
35
105
  cli.version(pkg.version || '0.0.0');
36
106
  cli.parse();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAyB,CAAC;AAE/D,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;AAE1B,GAAG;KACA,OAAO,CAAC,EAAE,EAAE,oCAAoC,CAAC;KACjD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,GAAG;KACA,OAAO,CAAC,QAAQ,EAAE,gCAAgC,CAAC;KACnD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,GAAG,CAAC,IAAI,EAAE,CAAC;AACX,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;AAEpC,GAAG,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAyB,CAAC;AAE/D,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;AAE1B,6DAA6D;AAC7D,GAAG;KACA,OAAO,CAAC,EAAE,EAAE,kDAAkD,CAAC;KAC/D,MAAM,CAAC,WAAW,EAAE,uCAAuC,CAAC;KAC5D,MAAM,CAAC,mBAAmB,EAAE,kDAAkD,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;KAC/F,MAAM,CAAC,QAAQ,EAAE,uCAAuC,CAAC;KACzD,MAAM,CAAC,KAAK,EAAE,OAAwD,EAAE,EAAE;IACzE,IAAI,CAAC;QACH,sDAAsD;QACtD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACrD,MAAM,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,SAAS,CAAC;YACd,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;YAChC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;SAC/D,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,6BAA6B;AAC7B,GAAG;KACA,OAAO,CAAC,QAAQ,EAAE,mDAAmD,CAAC;KACtE,MAAM,CAAC,WAAW,EAAE,uCAAuC,CAAC;KAC5D,MAAM,CAAC,mBAAmB,EAAE,kDAAkD,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;KAC/F,MAAM,CAAC,KAAK,EAAE,OAAwC,EAAE,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,SAAS,CAAC;YACd,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK;YAChC,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;SAC/D,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,+CAA+C;AAC/C,GAAG;KACA,OAAO,CAAC,KAAK,EAAE,0DAA0D,CAAC;KAC1E,MAAM,CAAC,mBAAmB,EAAE,4BAA4B,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;KACzE,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;KAClC,MAAM,CAAC,SAAS,EAAE,6BAA6B,CAAC;KAChD,MAAM,CAAC,KAAK,EAAE,OAA0D,EAAE,EAAE;IAC3E,IAAI,CAAC;QACH,MAAM,MAAM,CAAC;YACX,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YACvD,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK;YAC3B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK;SAC9B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,GAAG;KACA,OAAO,CAAC,QAAQ,EAAE,gCAAgC,CAAC;KACnD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,GAAG;KACA,OAAO,CAAC,eAAe,EAAE,0CAA0C,CAAC;KACpE,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,EAAE;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,GAAG,CAAC,IAAI,EAAE,CAAC;AACX,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;AAEpC,GAAG,CAAC,KAAK,EAAE,CAAC"}
@@ -1,2 +1,7 @@
1
- export declare function runCommit(): Promise<void>;
1
+ export interface CommitOptions {
2
+ autoCommit?: boolean;
3
+ numChoices?: number;
4
+ hookMode?: boolean;
5
+ }
6
+ export declare function runCommit(options?: CommitOptions): Promise<void>;
2
7
  //# sourceMappingURL=commit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/commands/commit.ts"],"names":[],"mappings":"AA4BA,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAoG/C"}
1
+ {"version":3,"file":"commit.d.ts","sourceRoot":"","sources":["../../src/commands/commit.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAYD,wBAAsB,SAAS,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmJ1E"}
@@ -4,48 +4,97 @@ import ora from 'ora';
4
4
  import { getConfig } from '../utils/config.js';
5
5
  import { isGitInstalled, isInGitRepo, getStagedFiles, getFilteredDiff, commit, } from '../utils/git.js';
6
6
  import { createAIClient, generateCommitMessage, } from '../utils/ai.js';
7
- function exitWithError(message, hint) {
8
- console.error(chalk.red(`❌ ${message}`));
9
- if (hint) {
10
- console.log(chalk.gray(` ${hint}`));
7
+ function exitWithError(message, hint, silent = false) {
8
+ if (!silent) {
9
+ console.error(chalk.red(`❌ ${message}`));
10
+ if (hint) {
11
+ console.log(chalk.gray(` ${hint}`));
12
+ }
11
13
  }
12
14
  process.exit(1);
13
15
  }
14
- export async function runCommit() {
16
+ export async function runCommit(options = {}) {
17
+ const { autoCommit = false, numChoices = 1, hookMode = false } = options;
18
+ // Recursion guard - prevent hook from triggering another git-ai
19
+ if (process.env.GIT_AI_RUNNING === '1') {
20
+ if (!hookMode) {
21
+ console.log(chalk.yellow('⚠️ git-ai is already running (recursion prevented)'));
22
+ }
23
+ process.exit(0);
24
+ }
25
+ process.env.GIT_AI_RUNNING = '1';
15
26
  // Environment checks
16
27
  if (!(await isGitInstalled())) {
17
- exitWithError('Git is not installed. Please install git first.');
28
+ exitWithError('Git is not installed. Please install git first.', undefined, hookMode);
18
29
  }
19
30
  if (!(await isInGitRepo())) {
20
- exitWithError('Not in a git repository.');
31
+ exitWithError('Not in a git repository.', undefined, hookMode);
21
32
  }
22
33
  const stagedFiles = await getStagedFiles();
23
34
  if (stagedFiles.length === 0) {
24
- exitWithError('No staged changes found.', 'Use `git add <files>` to stage your changes first.');
35
+ exitWithError('No staged changes found.', 'Use `git add <files>` to stage your changes first.', hookMode);
25
36
  }
26
37
  // Check config
27
38
  const config = getConfig();
28
39
  if (!config) {
29
40
  exitWithError('Configuration not found.', 'Run `git-ai config` to set up your AI provider.');
30
41
  }
31
- // Show staged files
32
- console.log(chalk.cyan('\n📁 Staged files:'));
33
- stagedFiles.forEach((file) => {
34
- console.log(chalk.gray(` ${file}`));
35
- });
42
+ // Show staged files (skip in hook mode)
43
+ if (!hookMode) {
44
+ console.log(chalk.cyan('\n📁 Staged files:'));
45
+ stagedFiles.forEach((file) => {
46
+ console.log(chalk.gray(` ${file}`));
47
+ });
48
+ }
36
49
  // Get filtered diff
37
50
  const { diff, truncated, ignoredFiles } = await getFilteredDiff(stagedFiles);
38
- if (ignoredFiles.length > 0) {
39
- const preview = ignoredFiles.slice(0, 8).join(', ');
40
- const more = ignoredFiles.length > 8 ? ` (+${ignoredFiles.length - 8} more)` : '';
41
- console.log(chalk.gray(`\n🧹 Ignored from diff (token optimization): ${preview}${more}`));
42
- }
43
- if (truncated) {
44
- console.log(chalk.yellow('\n⚠️ Diff was truncated due to size limits.'));
51
+ if (!hookMode) {
52
+ if (ignoredFiles.length > 0) {
53
+ const preview = ignoredFiles.slice(0, 8).join(', ');
54
+ const more = ignoredFiles.length > 8 ? ` (+${ignoredFiles.length - 8} more)` : '';
55
+ console.log(chalk.gray(`\n🧹 Ignored from diff (token optimization): ${preview}${more}`));
56
+ }
57
+ if (truncated) {
58
+ console.log(chalk.yellow('\n⚠️ Diff was truncated due to size limits.'));
59
+ }
45
60
  }
46
61
  // Create AI client
47
62
  const client = createAIClient(config);
48
- let commitMessage = await generateWithSpinner(client, { diff, stagedFiles, ignoredFiles, truncated }, config);
63
+ const input = { diff, stagedFiles, ignoredFiles, truncated };
64
+ // Generate commit message(s)
65
+ let commitMessage;
66
+ if (hookMode) {
67
+ // Hook mode: silent generation, output only the message
68
+ try {
69
+ commitMessage = await generateCommitMessage(client, input, config);
70
+ console.log(commitMessage);
71
+ return;
72
+ }
73
+ catch {
74
+ process.exit(1);
75
+ }
76
+ }
77
+ if (numChoices > 1) {
78
+ // Generate multiple choices
79
+ const messages = await generateMultipleWithSpinner(client, input, config, numChoices);
80
+ if (autoCommit) {
81
+ // Auto mode: use first message
82
+ commitMessage = messages[0];
83
+ }
84
+ else {
85
+ commitMessage = await selectFromChoices(messages);
86
+ }
87
+ }
88
+ else {
89
+ commitMessage = await generateWithSpinner(client, input, config);
90
+ }
91
+ // Auto commit mode
92
+ if (autoCommit) {
93
+ console.log(chalk.green('\n✨ Generated commit message:\n'));
94
+ console.log(chalk.white.bold(` ${commitMessage.split('\n').join('\n ')}`));
95
+ await performCommit(commitMessage);
96
+ return;
97
+ }
49
98
  // Interactive loop
50
99
  while (true) {
51
100
  console.log(chalk.green('\n✨ Generated commit message:\n'));
@@ -84,7 +133,13 @@ export async function runCommit() {
84
133
  }
85
134
  }
86
135
  else if (action === 'regenerate') {
87
- commitMessage = await generateWithSpinner(client, { diff, stagedFiles, ignoredFiles, truncated }, config);
136
+ if (numChoices > 1) {
137
+ const messages = await generateMultipleWithSpinner(client, input, config, numChoices);
138
+ commitMessage = await selectFromChoices(messages);
139
+ }
140
+ else {
141
+ commitMessage = await generateWithSpinner(client, input, config);
142
+ }
88
143
  }
89
144
  else {
90
145
  console.log(chalk.gray('\n👋 Commit cancelled.\n'));
@@ -92,6 +147,23 @@ export async function runCommit() {
92
147
  }
93
148
  }
94
149
  }
150
+ async function selectFromChoices(messages) {
151
+ if (messages.length === 1) {
152
+ return messages[0];
153
+ }
154
+ const { selected } = await inquirer.prompt([
155
+ {
156
+ type: 'list',
157
+ name: 'selected',
158
+ message: 'Select a commit message:',
159
+ choices: messages.map((msg, i) => ({
160
+ name: `${i + 1}. ${msg.split('\n')[0]}`,
161
+ value: msg,
162
+ })),
163
+ },
164
+ ]);
165
+ return selected;
166
+ }
95
167
  async function generateWithSpinner(client, input, config) {
96
168
  const spinner = ora({
97
169
  text: 'Generating commit message...',
@@ -108,6 +180,25 @@ async function generateWithSpinner(client, input, config) {
108
180
  exitWithError(errorMessage);
109
181
  }
110
182
  }
183
+ async function generateMultipleWithSpinner(client, input, config, count) {
184
+ const spinner = ora({
185
+ text: `Generating ${count} commit messages...`,
186
+ color: 'cyan',
187
+ }).start();
188
+ try {
189
+ const promises = Array.from({ length: count }, () => generateCommitMessage(client, input, config));
190
+ const messages = await Promise.all(promises);
191
+ // Dedupe
192
+ const unique = [...new Set(messages)];
193
+ spinner.succeed(`Generated ${unique.length} commit message(s)!`);
194
+ return unique;
195
+ }
196
+ catch (error) {
197
+ spinner.fail('Failed to generate commit messages');
198
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
199
+ exitWithError(errorMessage);
200
+ }
201
+ }
111
202
  async function performCommit(message) {
112
203
  const spinner = ora({
113
204
  text: 'Creating commit...',
@@ -1 +1 @@
1
- {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../src/commands/commit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../src/commands/commit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runHook(action: string): Promise<void>;
2
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"AA6KA,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB3D"}
@@ -0,0 +1,296 @@
1
+ import chalk from 'chalk';
2
+ import { execa } from 'execa';
3
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ const HOOK_NAME = 'prepare-commit-msg';
6
+ const HOOK_MARKER = '# git-ai-hook-start';
7
+ const HOOK_END_MARKER = '# git-ai-hook-end';
8
+ // Hook script with all protections
9
+ const HOOK_SCRIPT = `#!/bin/sh
10
+ ${HOOK_MARKER}
11
+ # git-ai hook - auto-generate commit message
12
+ # To disable: git commit --no-verify
13
+
14
+ COMMIT_MSG_FILE="$1"
15
+ COMMIT_SOURCE="$2"
16
+
17
+ # Recursion guard
18
+ if [ "$GIT_AI_RUNNING" = "1" ]; then
19
+ exit 0
20
+ fi
21
+ export GIT_AI_RUNNING=1
22
+
23
+ # Only run for regular commits (not merge, squash, amend, etc.)
24
+ if [ -n "$COMMIT_SOURCE" ]; then
25
+ exit 0
26
+ fi
27
+
28
+ # Check if there's already a non-comment message
29
+ if [ -s "$COMMIT_MSG_FILE" ]; then
30
+ EXISTING_MSG=$(grep -v "^#" "$COMMIT_MSG_FILE" | grep -v "^$" | head -1)
31
+ if [ -n "$EXISTING_MSG" ]; then
32
+ exit 0
33
+ fi
34
+ fi
35
+
36
+ # Generate commit message using git-ai msg
37
+ if command -v git-ai >/dev/null 2>&1; then
38
+ MSG=$(git-ai msg --quiet 2>/dev/null)
39
+ EXIT_CODE=$?
40
+
41
+ if [ $EXIT_CODE -eq 0 ] && [ -n "$MSG" ]; then
42
+ # Preserve existing comments, prepend AI message
43
+ if [ -f "$COMMIT_MSG_FILE" ]; then
44
+ COMMENTS=$(grep "^#" "$COMMIT_MSG_FILE" || true)
45
+ printf '%s\\n\\n%s\\n' "$MSG" "$COMMENTS" > "$COMMIT_MSG_FILE"
46
+ else
47
+ printf '%s\\n' "$MSG" > "$COMMIT_MSG_FILE"
48
+ fi
49
+ fi
50
+ fi
51
+ ${HOOK_END_MARKER}
52
+
53
+ `;
54
+ // Wrapper template for chaining with existing hook
55
+ const WRAPPER_TEMPLATE = `#!/bin/sh
56
+ ${HOOK_MARKER}
57
+ # git-ai hook wrapper - chains with existing hook
58
+ # To disable: git commit --no-verify
59
+
60
+ COMMIT_MSG_FILE="$1"
61
+ COMMIT_SOURCE="$2"
62
+ SHA1="$3"
63
+
64
+ # Recursion guard
65
+ if [ "$GIT_AI_RUNNING" = "1" ]; then
66
+ # Still call original hook if exists
67
+ if [ -x "__ORIGINAL_HOOK__" ]; then
68
+ "__ORIGINAL_HOOK__" "$COMMIT_MSG_FILE" "$COMMIT_SOURCE" "$SHA1"
69
+ fi
70
+ exit $?
71
+ fi
72
+ export GIT_AI_RUNNING=1
73
+
74
+ # Only run for regular commits
75
+ if [ -n "$COMMIT_SOURCE" ]; then
76
+ # Still call original hook
77
+ if [ -x "__ORIGINAL_HOOK__" ]; then
78
+ "__ORIGINAL_HOOK__" "$COMMIT_MSG_FILE" "$COMMIT_SOURCE" "$SHA1"
79
+ fi
80
+ exit $?
81
+ fi
82
+
83
+ # Check if there's already a non-comment message
84
+ if [ -s "$COMMIT_MSG_FILE" ]; then
85
+ EXISTING_MSG=$(grep -v "^#" "$COMMIT_MSG_FILE" | grep -v "^$" | head -1)
86
+ if [ -n "$EXISTING_MSG" ]; then
87
+ # Still call original hook
88
+ if [ -x "__ORIGINAL_HOOK__" ]; then
89
+ "__ORIGINAL_HOOK__" "$COMMIT_MSG_FILE" "$COMMIT_SOURCE" "$SHA1"
90
+ fi
91
+ exit $?
92
+ fi
93
+ fi
94
+
95
+ # Generate commit message using git-ai msg
96
+ if command -v git-ai >/dev/null 2>&1; then
97
+ MSG=$(git-ai msg --quiet 2>/dev/null)
98
+ EXIT_CODE=$?
99
+
100
+ if [ $EXIT_CODE -eq 0 ] && [ -n "$MSG" ]; then
101
+ if [ -f "$COMMIT_MSG_FILE" ]; then
102
+ COMMENTS=$(grep "^#" "$COMMIT_MSG_FILE" || true)
103
+ printf '%s\\n\\n%s\\n' "$MSG" "$COMMENTS" > "$COMMIT_MSG_FILE"
104
+ else
105
+ printf '%s\\n' "$MSG" > "$COMMIT_MSG_FILE"
106
+ fi
107
+ fi
108
+ fi
109
+
110
+ # Call original hook
111
+ if [ -x "__ORIGINAL_HOOK__" ]; then
112
+ "__ORIGINAL_HOOK__" "$COMMIT_MSG_FILE" "$COMMIT_SOURCE" "$SHA1"
113
+ exit $?
114
+ fi
115
+ ${HOOK_END_MARKER}
116
+
117
+ exit 0
118
+ `;
119
+ async function getGitHooksPath() {
120
+ try {
121
+ // Check if custom hooks path is configured
122
+ const { stdout } = await execa('git', ['config', '--get', 'core.hooksPath']);
123
+ return stdout.trim();
124
+ }
125
+ catch {
126
+ // Default to .git/hooks
127
+ const { stdout } = await execa('git', ['rev-parse', '--git-dir']);
128
+ return join(stdout.trim(), 'hooks');
129
+ }
130
+ }
131
+ async function isInGitRepo() {
132
+ try {
133
+ await execa('git', ['rev-parse', '--is-inside-work-tree']);
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
140
+ function isGitAiHook(content) {
141
+ return content.includes(HOOK_MARKER) || content.includes('git-ai');
142
+ }
143
+ function hasOtherHookContent(content) {
144
+ // Remove git-ai hook section and check if there's other content
145
+ const lines = content.split('\n');
146
+ let inGitAiSection = false;
147
+ const otherLines = [];
148
+ for (const line of lines) {
149
+ if (line.includes(HOOK_MARKER)) {
150
+ inGitAiSection = true;
151
+ continue;
152
+ }
153
+ if (line.includes(HOOK_END_MARKER)) {
154
+ inGitAiSection = false;
155
+ continue;
156
+ }
157
+ if (!inGitAiSection) {
158
+ // Skip shebang and empty lines
159
+ if (!line.startsWith('#!') && line.trim() !== '') {
160
+ otherLines.push(line);
161
+ }
162
+ }
163
+ }
164
+ return otherLines.some(line => !line.startsWith('#') && line.trim() !== '');
165
+ }
166
+ export async function runHook(action) {
167
+ if (!['install', 'remove', 'status'].includes(action)) {
168
+ console.error(chalk.red(`❌ Unknown action: ${action}`));
169
+ console.log(chalk.gray(' Available actions: install, remove, status'));
170
+ process.exit(1);
171
+ }
172
+ if (!(await isInGitRepo())) {
173
+ console.error(chalk.red('❌ Not in a git repository.'));
174
+ process.exit(1);
175
+ }
176
+ const hooksPath = await getGitHooksPath();
177
+ const hookFile = join(hooksPath, HOOK_NAME);
178
+ if (action === 'status') {
179
+ await showStatus(hookFile);
180
+ }
181
+ else if (action === 'install') {
182
+ await installHook(hookFile, hooksPath);
183
+ }
184
+ else if (action === 'remove') {
185
+ await removeHook(hookFile);
186
+ }
187
+ }
188
+ async function showStatus(hookFile) {
189
+ if (existsSync(hookFile)) {
190
+ const content = readFileSync(hookFile, 'utf-8');
191
+ if (isGitAiHook(content)) {
192
+ console.log(chalk.green('✅ git-ai hook is installed'));
193
+ console.log(chalk.gray(` Location: ${hookFile}`));
194
+ // Check if it's a wrapper
195
+ const originalHook = `${hookFile}.original`;
196
+ if (existsSync(originalHook)) {
197
+ console.log(chalk.cyan(' Mode: Wrapper (chained with original hook)'));
198
+ }
199
+ else {
200
+ console.log(chalk.cyan(' Mode: Standalone'));
201
+ }
202
+ }
203
+ else {
204
+ console.log(chalk.yellow('⚠️ A prepare-commit-msg hook exists but is not from git-ai'));
205
+ console.log(chalk.gray(` Location: ${hookFile}`));
206
+ console.log(chalk.gray(' Run `git-ai hook install` to add git-ai (will chain with existing hook)'));
207
+ }
208
+ }
209
+ else {
210
+ console.log(chalk.gray('❌ git-ai hook is not installed'));
211
+ }
212
+ }
213
+ async function installHook(hookFile, hooksPath) {
214
+ // Ensure hooks directory exists
215
+ if (!existsSync(hooksPath)) {
216
+ await execa('mkdir', ['-p', hooksPath]);
217
+ }
218
+ // Check if hook already exists
219
+ if (existsSync(hookFile)) {
220
+ const content = readFileSync(hookFile, 'utf-8');
221
+ if (isGitAiHook(content)) {
222
+ // Already has git-ai, update it
223
+ console.log(chalk.yellow('⚠️ git-ai hook already installed, updating...'));
224
+ // Check if there's an original hook
225
+ const originalHook = `${hookFile}.original`;
226
+ if (existsSync(originalHook)) {
227
+ // Re-create wrapper with updated script
228
+ const wrapper = WRAPPER_TEMPLATE.replace(/__ORIGINAL_HOOK__/g, originalHook);
229
+ writeFileSync(hookFile, wrapper);
230
+ chmodSync(hookFile, 0o755);
231
+ console.log(chalk.green('✅ git-ai hook updated (wrapper mode)'));
232
+ }
233
+ else {
234
+ writeFileSync(hookFile, HOOK_SCRIPT);
235
+ chmodSync(hookFile, 0o755);
236
+ console.log(chalk.green('✅ git-ai hook updated'));
237
+ }
238
+ return;
239
+ }
240
+ // Existing non-git-ai hook - use wrapper mode
241
+ const originalHook = `${hookFile}.original`;
242
+ // Backup original
243
+ writeFileSync(originalHook, content);
244
+ chmodSync(originalHook, 0o755);
245
+ console.log(chalk.gray(` Original hook saved to: ${originalHook}`));
246
+ // Install wrapper
247
+ const wrapper = WRAPPER_TEMPLATE.replace(/__ORIGINAL_HOOK__/g, originalHook);
248
+ writeFileSync(hookFile, wrapper);
249
+ chmodSync(hookFile, 0o755);
250
+ console.log(chalk.green('✅ git-ai hook installed (wrapper mode)'));
251
+ console.log(chalk.cyan(' Your original hook will still be executed after git-ai'));
252
+ }
253
+ else {
254
+ // No existing hook - install standalone
255
+ writeFileSync(hookFile, HOOK_SCRIPT);
256
+ chmodSync(hookFile, 0o755);
257
+ console.log(chalk.green('✅ git-ai hook installed'));
258
+ }
259
+ console.log(chalk.gray(` Location: ${hookFile}`));
260
+ console.log('');
261
+ console.log(chalk.cyan('📝 How it works:'));
262
+ console.log(chalk.gray(' When you run `git commit` without -m flag,'));
263
+ console.log(chalk.gray(' git-ai will auto-generate a commit message.'));
264
+ console.log('');
265
+ console.log(chalk.cyan('💡 Tips:'));
266
+ console.log(chalk.gray(' • Skip hook: git commit --no-verify'));
267
+ console.log(chalk.gray(' • Remove hook: git-ai hook remove'));
268
+ }
269
+ async function removeHook(hookFile) {
270
+ if (!existsSync(hookFile)) {
271
+ console.log(chalk.gray('❌ No hook to remove'));
272
+ return;
273
+ }
274
+ const content = readFileSync(hookFile, 'utf-8');
275
+ if (!isGitAiHook(content)) {
276
+ console.log(chalk.yellow('⚠️ The existing hook is not from git-ai, skipping removal'));
277
+ return;
278
+ }
279
+ // Check for original hook
280
+ const originalHook = `${hookFile}.original`;
281
+ if (existsSync(originalHook)) {
282
+ // Restore original hook
283
+ const originalContent = readFileSync(originalHook, 'utf-8');
284
+ writeFileSync(hookFile, originalContent);
285
+ chmodSync(hookFile, 0o755);
286
+ unlinkSync(originalHook);
287
+ console.log(chalk.green('✅ git-ai hook removed'));
288
+ console.log(chalk.gray(' Original hook restored'));
289
+ }
290
+ else {
291
+ // Just remove the hook
292
+ unlinkSync(hookFile);
293
+ console.log(chalk.green('✅ git-ai hook removed'));
294
+ }
295
+ }
296
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/commands/hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface MsgOptions {
2
+ json?: boolean;
3
+ quiet?: boolean;
4
+ num?: number;
5
+ }
6
+ export interface MsgResult {
7
+ success: boolean;
8
+ message?: string;
9
+ messages?: string[];
10
+ error?: string;
11
+ metadata?: {
12
+ stagedFiles: string[];
13
+ truncated: boolean;
14
+ ignoredFiles: string[];
15
+ };
16
+ }
17
+ export declare function runMsg(options?: MsgOptions): Promise<void>;
18
+ //# sourceMappingURL=msg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msg.d.ts","sourceRoot":"","sources":["../../src/commands/msg.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,OAAO,CAAC;QACnB,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;CACH;AAYD,wBAAsB,MAAM,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAmGpE"}
@@ -0,0 +1,103 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { getConfig } from '../utils/config.js';
4
+ import { isGitInstalled, isInGitRepo, getStagedFiles, getFilteredDiff, } from '../utils/git.js';
5
+ import { createAIClient, generateCommitMessage, } from '../utils/ai.js';
6
+ function exitWithError(message, options) {
7
+ if (options.json) {
8
+ const result = { success: false, error: message };
9
+ console.log(JSON.stringify(result, null, 2));
10
+ }
11
+ else if (!options.quiet) {
12
+ console.error(chalk.red(`❌ ${message}`));
13
+ }
14
+ process.exit(1);
15
+ }
16
+ export async function runMsg(options = {}) {
17
+ const { json = false, quiet = false, num = 1 } = options;
18
+ // Check for recursion guard
19
+ if (process.env.GIT_AI_RUNNING === '1') {
20
+ if (json) {
21
+ console.log(JSON.stringify({ success: false, error: 'Recursion detected' }));
22
+ }
23
+ process.exit(0);
24
+ }
25
+ // Set recursion guard
26
+ process.env.GIT_AI_RUNNING = '1';
27
+ // Environment checks
28
+ if (!(await isGitInstalled())) {
29
+ exitWithError('Git is not installed.', options);
30
+ }
31
+ if (!(await isInGitRepo())) {
32
+ exitWithError('Not in a git repository.', options);
33
+ }
34
+ const stagedFiles = await getStagedFiles();
35
+ if (stagedFiles.length === 0) {
36
+ exitWithError('No staged changes found. Use `git add <files>` first.', options);
37
+ }
38
+ // Check config
39
+ const config = getConfig();
40
+ if (!config) {
41
+ exitWithError('Configuration not found. Run `git-ai config` first.', options);
42
+ }
43
+ // Get filtered diff
44
+ const { diff, truncated, ignoredFiles } = await getFilteredDiff(stagedFiles);
45
+ // Create AI client
46
+ const client = createAIClient(config);
47
+ const input = { diff, stagedFiles, ignoredFiles, truncated };
48
+ // Generate message(s)
49
+ try {
50
+ let spinner = null;
51
+ if (!quiet && !json) {
52
+ spinner = ora({
53
+ text: num > 1 ? `Generating ${num} commit messages...` : 'Generating commit message...',
54
+ color: 'cyan',
55
+ }).start();
56
+ }
57
+ if (num > 1) {
58
+ // Generate multiple messages
59
+ const promises = Array.from({ length: num }, () => generateCommitMessage(client, input, config));
60
+ const messages = await Promise.all(promises);
61
+ const unique = [...new Set(messages)];
62
+ if (spinner) {
63
+ spinner.stop();
64
+ }
65
+ if (json) {
66
+ const result = {
67
+ success: true,
68
+ messages: unique,
69
+ metadata: { stagedFiles, truncated, ignoredFiles },
70
+ };
71
+ console.log(JSON.stringify(result, null, 2));
72
+ }
73
+ else {
74
+ // Plain output: one message per line
75
+ unique.forEach((msg) => console.log(msg));
76
+ }
77
+ }
78
+ else {
79
+ // Single message
80
+ const message = await generateCommitMessage(client, input, config);
81
+ if (spinner) {
82
+ spinner.stop();
83
+ }
84
+ if (json) {
85
+ const result = {
86
+ success: true,
87
+ message,
88
+ metadata: { stagedFiles, truncated, ignoredFiles },
89
+ };
90
+ console.log(JSON.stringify(result, null, 2));
91
+ }
92
+ else {
93
+ // Plain output
94
+ console.log(message);
95
+ }
96
+ }
97
+ }
98
+ catch (error) {
99
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
100
+ exitWithError(errorMessage, options);
101
+ }
102
+ }
103
+ //# sourceMappingURL=msg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"msg.js","sourceRoot":"","sources":["../../src/commands/msg.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongowu/git-ai-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.6",
4
4
  "description": "Fast, privacy-first, bring-your-own-model Git commit message generator",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -11,7 +11,13 @@
11
11
  "build": "tsc",
12
12
  "dev": "tsc --watch",
13
13
  "start": "node dist/cli.js",
14
- "prepublishOnly": "npm run build"
14
+ "prepublishOnly": "npm run build",
15
+ "release": "npm version patch && git push && git push --tags && npm publish --access=public",
16
+ "release:minor": "npm version minor && git push && git push --tags && npm publish --access=public",
17
+ "release:major": "npm version major && git push && git push --tags && npm publish --access=public",
18
+ "tag": "npm version patch && git push && git push --tags",
19
+ "tag:minor": "npm version minor && git push && git push --tags",
20
+ "tag:major": "npm version major && git push && git push --tags"
15
21
  },
16
22
  "keywords": [
17
23
  "git",
@@ -0,0 +1,34 @@
1
+ #!/bin/bash
2
+ # release.sh - 自动发布脚本
3
+
4
+ set -e
5
+
6
+ # 颜色
7
+ GREEN='\033[0;32m'
8
+ YELLOW='\033[1;33m'
9
+ NC='\033[0m'
10
+
11
+ # 获取版本类型
12
+ VERSION_TYPE=${1:-patch}
13
+
14
+ echo -e "${GREEN}🚀 Starting release...${NC}"
15
+
16
+ # 1. 构建
17
+ echo -e "${YELLOW}📦 Building...${NC}"
18
+ npm run build
19
+
20
+ # 2. 更新版本
21
+ echo -e "${YELLOW}📝 Bumping ${VERSION_TYPE} version...${NC}"
22
+ npm version $VERSION_TYPE
23
+
24
+ # 3. 推送到 GitHub
25
+ echo -e "${YELLOW}📤 Pushing to GitHub...${NC}"
26
+ git push
27
+ git push --tags
28
+
29
+ # 4. 发布到 npm
30
+ echo -e "${YELLOW}📦 Publishing to npm...${NC}"
31
+ read -p "Enter OTP code: " OTP
32
+ npm publish --access=public --otp=$OTP
33
+
34
+ echo -e "${GREEN}✅ Release complete!${NC}"