ai-git-tool 1.3.0 → 1.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Imamura Kento
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # ai-git-tool
2
2
 
3
- Groq API を使って、ステージ済み差分からコミットメッセージと PR 説明文を自動生成する TypeScript 製 CLI です。
3
+ AIを使って、ステージ済み差分からコミットメッセージと PR 説明文を自動生成する TypeScript 製 CLI です。
4
+
5
+ <video src="https://github.com/user-attachments/assets/c3826580-eb80-409a-b3fc-cc19f006cceb" />  
4
6
 
5
7
  ## セットアップ
6
8
 
@@ -165,6 +167,19 @@ fix/api-error-handling
165
167
  docs/readme-update
166
168
  ```
167
169
 
170
+ ### ブランチ切り替え + 最新化
171
+
172
+ 既存ブランチ名を引数に渡すと、ブランチへ移動してから `git pull` で最新化します。
173
+
174
+ ```bash
175
+ ai-git checkout main
176
+ ```
177
+
178
+ 実行内容:
179
+
180
+ - `git checkout <branch>`
181
+ - `git pull`
182
+
168
183
  ### 言語設定
169
184
 
170
185
  デフォルト言語は日本語です。
@@ -8,7 +8,33 @@ const branch_js_1 = require("../services/branch.js");
8
8
  /**
9
9
  * checkout コマンドの実行
10
10
  */
11
- async function runCheckoutCommand() {
11
+ async function runCheckoutCommand(targetBranch) {
12
+ if (targetBranch) {
13
+ const checkoutResult = (0, child_process_1.spawnSync)("git", ["checkout", targetBranch], {
14
+ stdio: "inherit",
15
+ });
16
+ if (checkoutResult.status !== 0) {
17
+ (0, errors_js_1.showFriendlyError)(`ブランチへの移動に失敗しました: ${targetBranch}`, "git checkout コマンドが失敗しました", [
18
+ `ブランチが存在するか確認: git branch -a | grep ${targetBranch}`,
19
+ "現在の変更を確認: git status",
20
+ "未コミット変更がある場合は commit または stash を実施",
21
+ ], [`手動で移動: git checkout ${targetBranch}`]);
22
+ process.exit(1);
23
+ }
24
+ console.log(`✅ ブランチに移動しました: ${targetBranch}`);
25
+ console.log(`🔄 最新を取得中... (${targetBranch})`);
26
+ const pullResult = (0, child_process_1.spawnSync)("git", ["pull"], { stdio: "inherit" });
27
+ if (pullResult.status !== 0) {
28
+ (0, errors_js_1.showFriendlyError)(`pull に失敗しました: ${targetBranch}`, "git pull コマンドが失敗しました(競合や upstream 未設定の可能性があります)", [
29
+ "upstream を確認: git branch -vv",
30
+ "必要なら upstream を設定: git branch --set-upstream-to=origin/<branch>",
31
+ "競合がある場合は解消してから再実行",
32
+ ], [`手動で実行: git pull origin ${targetBranch}`]);
33
+ process.exit(1);
34
+ }
35
+ console.log(`✅ 最新状態に更新しました: ${targetBranch}`);
36
+ return;
37
+ }
12
38
  const suggested = await (0, ai_js_1.suggestBranchName)();
13
39
  const branchName = (0, branch_js_1.ensureAvailableBranchName)(suggested);
14
40
  const result = (0, child_process_1.spawnSync)("git", ["checkout", "-b", branchName], {
@@ -37,6 +37,8 @@ async function runPRCommand(language) {
37
37
  }
38
38
  process.exit(1);
39
39
  }
40
+ // ベースブランチとの同期(コンフリクトチェック・マージ)
41
+ await (0, github_js_1.syncWithBaseBranch)(baseBranch, currentBranch, language);
40
42
  // ブランチをプッシュ
41
43
  (0, github_js_1.pushBranchForPR)(currentBranch, language);
42
44
  console.log(`🤖 PR説明文を生成中... (${baseBranch}...${currentBranch}) [compact summary input]`);
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ Commands:
34
34
  commit Generate commit message from staged changes
35
35
  push Commit with AI message and push to remote (git add . + commit + push)
36
36
  pr Generate PR description and create pull request
37
- checkout Create branch from current changes
37
+ checkout [branch] Create branch from changes, or switch+pull existing branch
38
38
 
39
39
  Commit Options:
40
40
  --lang <ja|en> Set language for this run
@@ -64,7 +64,7 @@ if (!subcommand ||
64
64
  console.error(" ai-git commit - AI でコミットメッセージを生成してコミット");
65
65
  console.error(" ai-git push - コミット後、リモートにプッシュ");
66
66
  console.error(" ai-git pr - PR の説明を生成して Pull Request を作成");
67
- console.error(" ai-git checkout - 変更内容から新しいブランチを作成");
67
+ console.error(" ai-git checkout - 引数なし: 新規ブランチ作成 / 引数あり: ブランチ移動して pull");
68
68
  console.error("");
69
69
  console.error("💡 詳しい使い方:");
70
70
  console.error(" ai-git --help");
@@ -78,7 +78,7 @@ const language = (0, config_js_1.resolveLanguage)(langArg);
78
78
  // ── メイン ───────────────────────────────────────────────
79
79
  async function main() {
80
80
  if (subcommand === "checkout") {
81
- await (0, checkout_js_1.runCheckoutCommand)();
81
+ await (0, checkout_js_1.runCheckoutCommand)(subcommandArgs[0]);
82
82
  return;
83
83
  }
84
84
  if (subcommand === "pr") {
@@ -8,8 +8,11 @@ exports.parseGitHubRepoPath = parseGitHubRepoPath;
8
8
  exports.getPullRequestURL = getPullRequestURL;
9
9
  exports.createPR = createPR;
10
10
  exports.pushBranchForPR = pushBranchForPR;
11
+ exports.checkConflicts = checkConflicts;
12
+ exports.syncWithBaseBranch = syncWithBaseBranch;
11
13
  const child_process_1 = require("child_process");
12
14
  const errors_js_1 = require("../utils/errors.js");
15
+ const ui_js_1 = require("../utils/ui.js");
13
16
  /**
14
17
  * GitHub CLI がインストールされているかチェック
15
18
  */
@@ -330,3 +333,147 @@ function pushBranchForPR(currentBranch, language) {
330
333
  }
331
334
  }
332
335
  }
336
+ /**
337
+ * コンフリクトがあるかを非破壊的に検知
338
+ */
339
+ function checkConflicts(baseBranch) {
340
+ try {
341
+ const mergeBase = (0, child_process_1.execSync)(`git merge-base HEAD origin/${baseBranch}`, {
342
+ encoding: "utf-8",
343
+ stdio: "pipe",
344
+ }).trim();
345
+ const mergeTreeOutput = (0, child_process_1.execSync)(`git merge-tree ${mergeBase} origin/${baseBranch} HEAD`, {
346
+ encoding: "utf-8",
347
+ stdio: "pipe",
348
+ });
349
+ return mergeTreeOutput.includes("<<<<<<<");
350
+ }
351
+ catch {
352
+ return false;
353
+ }
354
+ }
355
+ /**
356
+ * ベースブランチとの同期処理(PR作成前)
357
+ */
358
+ async function syncWithBaseBranch(baseBranch, currentBranch, language) {
359
+ console.log(language === "ja"
360
+ ? `🔄 ${baseBranch} との同期を確認中...`
361
+ : `🔄 Checking sync with ${baseBranch}...`);
362
+ // 最新を取得
363
+ try {
364
+ (0, child_process_1.execSync)(`git fetch origin ${baseBranch}`, { stdio: "pipe" });
365
+ }
366
+ catch {
367
+ if (language === "ja") {
368
+ (0, errors_js_1.showFriendlyError)("リモートブランチの取得に失敗しました", `origin/${baseBranch} を取得できませんでした`, [
369
+ "ネットワーク接続を確認してください",
370
+ "リモートリポジトリの設定を確認: git remote -v",
371
+ `リモートに ${baseBranch} ブランチが存在するか確認`,
372
+ ], ["接続を確認後に ai-git pr を再実行"]);
373
+ }
374
+ else {
375
+ (0, errors_js_1.showFriendlyError)("Failed to fetch remote branch", `Could not fetch origin/${baseBranch}`, [
376
+ "Check network connection",
377
+ "Check remote repository: git remote -v",
378
+ `Check if ${baseBranch} branch exists on remote`,
379
+ ], ["Run ai-git pr again after fixing connection"]);
380
+ }
381
+ process.exit(1);
382
+ }
383
+ // コンフリクトチェック
384
+ const hasConflicts = checkConflicts(baseBranch);
385
+ if (!hasConflicts) {
386
+ // コンフリクトなし → 自動的にマージ
387
+ console.log(language === "ja"
388
+ ? `✅ コンフリクトなし - origin/${baseBranch} をマージ中...`
389
+ : `✅ No conflicts - merging origin/${baseBranch}...`);
390
+ const mergeResult = (0, child_process_1.spawnSync)("git", ["merge", `origin/${baseBranch}`], {
391
+ stdio: "inherit",
392
+ });
393
+ if (mergeResult.status !== 0) {
394
+ if (language === "ja") {
395
+ (0, errors_js_1.showFriendlyError)("マージに失敗しました", `origin/${baseBranch} のマージ中にエラーが発生しました`, [
396
+ "Git の状態を確認: git status",
397
+ "マージを中止する場合: git merge --abort",
398
+ ], ["問題を解決後に ai-git pr を再実行"]);
399
+ }
400
+ else {
401
+ (0, errors_js_1.showFriendlyError)("Merge failed", `Error occurred while merging origin/${baseBranch}`, ["Check git status: git status", "Abort merge: git merge --abort"], ["Run ai-git pr again after fixing"]);
402
+ }
403
+ process.exit(1);
404
+ }
405
+ console.log(language === "ja"
406
+ ? `✅ origin/${baseBranch} をマージしました`
407
+ : `✅ Merged origin/${baseBranch}`);
408
+ return;
409
+ }
410
+ // コンフリクトあり → 選択肢を提示
411
+ console.log(language === "ja"
412
+ ? `⚠️ origin/${baseBranch} とのコンフリクトを検知しました`
413
+ : `⚠️ Conflicts detected with origin/${baseBranch}`);
414
+ const choices = language === "ja"
415
+ ? `次の操作を選択してください:
416
+ [m] merge origin/${baseBranch} してコンフリクトを解消してからPR作成
417
+ [r] rebase onto origin/${baseBranch} してコンフリクトを解消してからPR作成
418
+ [s] スキップ(そのままPR作成)
419
+ [a] 中止
420
+ 選択 [m/r/s/a]: `
421
+ : `Choose an action:
422
+ [m] merge origin/${baseBranch} and resolve conflicts before creating PR
423
+ [r] rebase onto origin/${baseBranch} and resolve conflicts before creating PR
424
+ [s] skip (create PR anyway)
425
+ [a] abort
426
+ Choice [m/r/s/a]: `;
427
+ const choice = await (0, ui_js_1.askUser)(choices);
428
+ if (choice === "m" || choice === "merge") {
429
+ console.log(language === "ja"
430
+ ? `🔀 origin/${baseBranch} をマージ中...`
431
+ : `🔀 Merging origin/${baseBranch}...`);
432
+ const result = (0, child_process_1.spawnSync)("git", ["merge", `origin/${baseBranch}`], {
433
+ stdio: "inherit",
434
+ });
435
+ if (result.status !== 0) {
436
+ if (language === "ja") {
437
+ console.log(`\n⚠️ コンフリクトを解消してください。解消後、以下を実行:`);
438
+ console.log(` git add .`);
439
+ console.log(` git commit`);
440
+ console.log(` ai-git pr\n`);
441
+ }
442
+ else {
443
+ console.log(`\n⚠️ Please resolve conflicts. After resolving, run:`);
444
+ console.log(` git add .`);
445
+ console.log(` git commit`);
446
+ console.log(` ai-git pr\n`);
447
+ }
448
+ process.exit(1);
449
+ }
450
+ }
451
+ else if (choice === "r" || choice === "rebase") {
452
+ console.log(language === "ja"
453
+ ? `🔀 origin/${baseBranch} へ rebase 中...`
454
+ : `🔀 Rebasing onto origin/${baseBranch}...`);
455
+ const result = (0, child_process_1.spawnSync)("git", ["rebase", `origin/${baseBranch}`], {
456
+ stdio: "inherit",
457
+ });
458
+ if (result.status !== 0) {
459
+ if (language === "ja") {
460
+ console.log(`\n⚠️ コンフリクトを解消してください。解消後、以下を実行:`);
461
+ console.log(` git add .`);
462
+ console.log(` git rebase --continue`);
463
+ console.log(` ai-git pr\n`);
464
+ }
465
+ else {
466
+ console.log(`\n⚠️ Please resolve conflicts. After resolving, run:`);
467
+ console.log(` git add .`);
468
+ console.log(` git rebase --continue`);
469
+ console.log(` ai-git pr\n`);
470
+ }
471
+ process.exit(1);
472
+ }
473
+ }
474
+ else if (choice === "a" || choice === "abort") {
475
+ console.log(language === "ja" ? "中止しました。" : "Aborted.");
476
+ process.exit(0);
477
+ }
478
+ // 's' または 'skip' の場合はそのまま続行
479
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-git-tool",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "AI-powered git commit and PR description generator using Groq API",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -9,7 +9,10 @@
9
9
  "scripts": {
10
10
  "build": "tsc",
11
11
  "dev": "tsx src/index.ts",
12
- "prepublishOnly": "npm run build"
12
+ "prepublishOnly": "npm run build",
13
+ "release:patch": "npm version patch && git push --follow-tags",
14
+ "release:minor": "npm version minor && git push --follow-tags",
15
+ "release:major": "npm version major && git push --follow-tags"
13
16
  },
14
17
  "keywords": [
15
18
  "git",