ai-git-tool 1.2.0 → 1.4.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
 
@@ -41,7 +43,7 @@ echo 'export GROQ_API_KEY="your_api_key"' >> ~/.bashrc
41
43
  source ~/.bashrc
42
44
  ```
43
45
 
44
- 任意でモデル指定も可能です(デフォルト: `llama-3.1-8b-instant`)。
46
+ 任意でモデル指定も可能です(デフォルト: `Llama 3.3 70B Versatile`)。
45
47
 
46
48
  ```bash
47
49
  # macOS / Linux / Git Bash
@@ -68,11 +70,11 @@ ai-git commit --no-add
68
70
 
69
71
  確認プロンプトで操作を選択できます。
70
72
 
71
- | 入力 | 動作 |
72
- |------|------|
73
- | `y` | そのままコミット |
74
- | `n` | 中止 |
75
- | `e` | エディタで編集してからコミット |
73
+ | 入力 | 動作 |
74
+ | ---- | ------------------------------ |
75
+ | `y` | そのままコミット |
76
+ | `n` | 中止 |
77
+ | `e` | エディタで編集してからコミット |
76
78
 
77
79
  **生成されるコミットメッセージの形式(Conventional Commits):**
78
80
 
@@ -97,11 +99,11 @@ ai-git push
97
99
 
98
100
  確認プロンプトで操作を選択できます。
99
101
 
100
- | 入力 | 動作 |
101
- |------|------|
102
- | `y` | そのままコミット & プッシュ |
103
- | `n` | 中止 |
104
- | `e` | エディタで編集してからコミット & プッシュ |
102
+ | 入力 | 動作 |
103
+ | ---- | ----------------------------------------- |
104
+ | `y` | そのままコミット & プッシュ |
105
+ | `n` | 中止 |
106
+ | `e` | エディタで編集してからコミット & プッシュ |
105
107
 
106
108
  ```bash
107
109
  # ステージ済みの差分のみ使う
@@ -122,22 +124,25 @@ ai-git pr
122
124
 
123
125
  確認プロンプトで操作を選択できます。
124
126
 
125
- | 入力 | 動作 |
126
- |------|------|
127
- | `y` | PR を作成 |
128
- | `n` | 中止 |
129
- | `e` | エディタで編集してから作成 |
127
+ | 入力 | 動作 |
128
+ | ---- | -------------------------- |
129
+ | `y` | PR を作成 |
130
+ | `n` | 中止 |
131
+ | `e` | エディタで編集してから作成 |
130
132
 
131
133
  **生成される PR 説明文の形式:**
132
134
 
133
135
  ```markdown
134
136
  ## Summary
135
- 変更の全体像を 2〜3 文で説明
137
+
138
+ 変更の全体像を 1〜2 文で説明
136
139
 
137
140
  ## Changes
141
+
138
142
  - 具体的な変更内容(3〜7 項目)
139
143
 
140
144
  ## Test plan
145
+
141
146
  - テスト・確認方法(2〜4 項目)
142
147
  ```
143
148
 
@@ -196,8 +201,54 @@ npm run dev
196
201
 
197
202
  ## トラブルシューティング
198
203
 
199
- | エラー | 対処 |
200
- |--------|------|
201
- | `No staged changes found.` | `git add` してから実行 |
202
- | `GROQ_API_KEY が未設定です` | `GROQ_API_KEY` を設定 |
203
- | `413 Request too large` / TPM 超過 | `git add -p` でステージを分割 |
204
+ ### エラーメッセージについて
205
+
206
+ ai-git Git 初心者にも優しいエラーメッセージを表示します。エラーが発生した場合、以下の情報が表示されます:
207
+
208
+ - **何が起こったか**(エラーの内容)
209
+ - **なぜ起こったか**(原因)
210
+ - **どうすればいいか**(解決方法)
211
+ - **ai-git のどのコマンドが使えるか**(次のステップ)
212
+
213
+ ### よくあるエラーと対処法
214
+
215
+ | エラー | 原因 | 対処 |
216
+ | ---------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------- |
217
+ | `ステージされた変更が見つかりません` | コミットする変更がステージングエリアにない | `git add .` でステージ、または `ai-git push` を使用 |
218
+ | `GROQ_API_KEY が未設定です` | AI 機能に必要な API キーが設定されていない | [Groq Console](https://console.groq.com/keys) で API キーを取得して設定 |
219
+ | `これは Git リポジトリではありません` | Git が初期化されていないディレクトリで実行 | `git init` で初期化、または Git リポジトリに移動 |
220
+ | `GitHub CLI (gh) がインストールされていません` | PR 作成に必要な gh コマンドがない | [GitHub CLI](https://cli.github.com/) をインストール |
221
+ | `GitHub CLI の認証が必要です` | GitHub にログインしていない | `gh auth login` で認証 |
222
+ | `ベースブランチを検出できませんでした` | main/master/develop ブランチが存在しない | リモートから取得: `git fetch origin` |
223
+ | `413 Request too large` / TPM 超過 | 差分が大きすぎる、またはレート制限 | 自動で縮小して再試行されます。それでも失敗する場合は少し待つ |
224
+
225
+ ### Git の基本操作
226
+
227
+ ai-git を使う前に、以下の Git コマンドを覚えておくと便利です:
228
+
229
+ ```bash
230
+ # 現在の状態を確認
231
+ git status
232
+
233
+ # 変更をステージング
234
+ git add <ファイル名> # 特定のファイルだけ
235
+ git add . # すべての変更
236
+
237
+ # コミット履歴を確認
238
+ git log --oneline
239
+
240
+ # ブランチの確認
241
+ git branch -a
242
+
243
+ # リモートの確認
244
+ git remote -v
245
+ ```
246
+
247
+ ### 推奨ワークフロー
248
+
249
+ Git 初心者の方は、以下の流れで使うのがおすすめです:
250
+
251
+ 1. **変更を加える** - ファイルを編集
252
+ 2. **状態を確認** - `git status` で変更を確認
253
+ 3. **コミット** - `ai-git commit` または `ai-git push`
254
+ 4. **PR 作成** - `ai-git pr`(必要に応じて)
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCheckoutCommand = runCheckoutCommand;
4
+ const child_process_1 = require("child_process");
5
+ const errors_js_1 = require("../utils/errors.js");
6
+ const ai_js_1 = require("../services/ai.js");
7
+ const branch_js_1 = require("../services/branch.js");
8
+ /**
9
+ * checkout コマンドの実行
10
+ */
11
+ async function runCheckoutCommand() {
12
+ const suggested = await (0, ai_js_1.suggestBranchName)();
13
+ const branchName = (0, branch_js_1.ensureAvailableBranchName)(suggested);
14
+ const result = (0, child_process_1.spawnSync)("git", ["checkout", "-b", branchName], {
15
+ stdio: "inherit",
16
+ });
17
+ if (result.status !== 0) {
18
+ (0, errors_js_1.showFriendlyError)("ブランチ作成に失敗しました", "git checkout -b コマンドが失敗しました(未コミットの変更がある可能性があります)", [
19
+ "現在の変更を確認: git status",
20
+ "変更をコミットしてから新しいブランチを作成する",
21
+ "変更を一時退避: git stash",
22
+ "または、変更を含めたままブランチを作成: git checkout -b <ブランチ名>",
23
+ ], [
24
+ "ai-git commit でまず変更をコミット",
25
+ "ai-git push でコミット&プッシュしてから新しいブランチを作成",
26
+ ]);
27
+ process.exit(1);
28
+ }
29
+ console.log(`✅ ブランチを作成しました: ${branchName}`);
30
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCommitCommand = runCommitCommand;
4
+ const git_js_1 = require("../utils/git.js");
5
+ const ui_js_1 = require("../utils/ui.js");
6
+ const errors_js_1 = require("../utils/errors.js");
7
+ const ai_js_1 = require("../services/ai.js");
8
+ /**
9
+ * commit コマンドの実行
10
+ */
11
+ async function runCommitCommand(language, noAdd) {
12
+ if (!noAdd) {
13
+ console.log("📦 変更をステージしています... (git add .)");
14
+ (0, git_js_1.stageAllChanges)();
15
+ }
16
+ const diff = (0, git_js_1.getStagedDiff)();
17
+ if (!diff.trim()) {
18
+ (0, errors_js_1.showFriendlyError)("ステージされた変更が見つかりません", "コミットする変更がステージングエリアにありません", [
19
+ "変更したファイルを確認: git status",
20
+ "すべての変更をステージ: git add .",
21
+ "特定のファイルだけステージ: git add <ファイル名>",
22
+ ], [
23
+ "ai-git commit --no-add (手動で git add した後)",
24
+ "ai-git push (自動で git add してコミット&プッシュ)",
25
+ ]);
26
+ process.exit(1);
27
+ }
28
+ console.log("🤖 コミットメッセージを生成中... (compact summary input)");
29
+ const message = await (0, ai_js_1.generateCommitMessage)(diff, language);
30
+ console.log(`\n📝 Generated commit message:\n`);
31
+ message.split("\n").forEach((line) => {
32
+ console.log(` ${line}`);
33
+ });
34
+ console.log();
35
+ const answer = await (0, ui_js_1.askUser)("Use this message? [y/n/e(edit)]: ");
36
+ let finalMessage = message;
37
+ if (answer === "e" || answer === "edit") {
38
+ finalMessage = (0, ui_js_1.editInEditor)(message);
39
+ if (!finalMessage) {
40
+ console.log("Aborted: empty message.");
41
+ process.exit(0);
42
+ }
43
+ console.log(`\n✏️ Edited message:\n`);
44
+ finalMessage.split("\n").forEach((line) => {
45
+ console.log(` ${line}`);
46
+ });
47
+ console.log();
48
+ }
49
+ else if (answer !== "y" && answer !== "yes") {
50
+ console.log("Aborted.");
51
+ process.exit(0);
52
+ }
53
+ (0, git_js_1.doCommit)(finalMessage);
54
+ console.log(`\n✅ Committed successfully!`);
55
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runPRCommand = runPRCommand;
4
+ const git_js_1 = require("../utils/git.js");
5
+ const ui_js_1 = require("../utils/ui.js");
6
+ const errors_js_1 = require("../utils/errors.js");
7
+ const github_js_1 = require("../services/github.js");
8
+ const ai_js_1 = require("../services/ai.js");
9
+ /**
10
+ * pr コマンドの実行
11
+ */
12
+ async function runPRCommand(language) {
13
+ (0, github_js_1.checkGHCLI)(language);
14
+ (0, github_js_1.checkGHAuth)(language);
15
+ const baseBranch = (0, github_js_1.detectBaseBranch)(language);
16
+ const currentBranch = (0, git_js_1.getCurrentBranch)();
17
+ if (currentBranch === baseBranch) {
18
+ if (language === "ja") {
19
+ (0, errors_js_1.showFriendlyError)(`ベースブランチ (${baseBranch}) から PR を作成できません`, "PR は異なるブランチから作成する必要があります", [
20
+ "新しいブランチを作成してください",
21
+ "または、既存のブランチに切り替えてください: git checkout <ブランチ名>",
22
+ ], [
23
+ "ai-git checkout で新しいブランチを作成",
24
+ "ブランチ作成後に変更をコミット: ai-git commit",
25
+ "その後 PR を作成: ai-git pr",
26
+ ]);
27
+ }
28
+ else {
29
+ (0, errors_js_1.showFriendlyError)(`Cannot create PR from base branch (${baseBranch})`, "PRs must be created from a different branch", [
30
+ "Create a new branch",
31
+ "Or switch to an existing branch: git checkout <branch-name>",
32
+ ], [
33
+ "Use ai-git checkout to create a new branch",
34
+ "Commit changes after creating branch: ai-git commit",
35
+ "Then create PR: ai-git pr",
36
+ ]);
37
+ }
38
+ process.exit(1);
39
+ }
40
+ // ベースブランチとの同期(コンフリクトチェック・マージ)
41
+ await (0, github_js_1.syncWithBaseBranch)(baseBranch, currentBranch, language);
42
+ // ブランチをプッシュ
43
+ (0, github_js_1.pushBranchForPR)(currentBranch, language);
44
+ console.log(`🤖 PR説明文を生成中... (${baseBranch}...${currentBranch}) [compact summary input]`);
45
+ const description = await (0, ai_js_1.generatePRDescription)(baseBranch, language, github_js_1.getBranchDiff);
46
+ console.log(`\n📝 Generated PR:\n`);
47
+ console.log(` Title: ${(0, ai_js_1.extractPRTitle)(description)}`);
48
+ console.log();
49
+ (0, ai_js_1.stripTitleLine)(description)
50
+ .split("\n")
51
+ .forEach((line) => {
52
+ console.log(` ${line}`);
53
+ });
54
+ console.log();
55
+ const answer = await (0, ui_js_1.askUser)("Create PR with this description? [y/n/e(edit)]: ");
56
+ let finalDescription = description;
57
+ if (answer === "e" || answer === "edit") {
58
+ finalDescription = (0, ui_js_1.editInEditor)(description);
59
+ if (!finalDescription) {
60
+ console.log("Aborted: empty description.");
61
+ process.exit(0);
62
+ }
63
+ console.log(`\n✏️ Edited description:\n`);
64
+ finalDescription.split("\n").forEach((line) => {
65
+ console.log(` ${line}`);
66
+ });
67
+ console.log();
68
+ }
69
+ else if (answer !== "y" && answer !== "yes") {
70
+ console.log("Aborted.");
71
+ process.exit(0);
72
+ }
73
+ const fallbackURL = (0, github_js_1.getPullRequestURL)(currentBranch);
74
+ (0, github_js_1.createPR)(finalDescription, baseBranch, fallbackURL, language, ai_js_1.extractPRTitle, ai_js_1.stripTitleLine);
75
+ console.log(`\n✅ PR created successfully!`);
76
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runPushCommand = runPushCommand;
4
+ const child_process_1 = require("child_process");
5
+ const commit_js_1 = require("./commit.js");
6
+ const git_js_1 = require("../utils/git.js");
7
+ /**
8
+ * 未プッシュのコミットがあるかチェック
9
+ */
10
+ function hasUnpushedCommits(currentBranch) {
11
+ try {
12
+ // upstream が設定されているかチェック
13
+ (0, child_process_1.execSync)(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`, {
14
+ encoding: "utf-8",
15
+ stdio: "pipe",
16
+ });
17
+ // upstream と比較
18
+ const localCommit = (0, child_process_1.execSync)(`git rev-parse ${currentBranch}`, {
19
+ encoding: "utf-8",
20
+ stdio: "pipe",
21
+ }).trim();
22
+ const remoteCommit = (0, child_process_1.execSync)(`git rev-parse ${currentBranch}@{upstream}`, {
23
+ encoding: "utf-8",
24
+ stdio: "pipe",
25
+ }).trim();
26
+ return localCommit !== remoteCommit;
27
+ }
28
+ catch {
29
+ // upstream が設定されていない場合、コミットがあればプッシュすべき
30
+ try {
31
+ const commitCount = (0, child_process_1.execSync)("git rev-list --count HEAD", {
32
+ encoding: "utf-8",
33
+ stdio: "pipe",
34
+ }).trim();
35
+ return parseInt(commitCount, 10) > 0;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ }
42
+ /**
43
+ * push コマンドの実行
44
+ */
45
+ async function runPushCommand(language, noAdd) {
46
+ const currentBranch = (0, git_js_1.getCurrentBranch)();
47
+ // ステージされた変更をチェック(--no-add の場合は git add をスキップ)
48
+ if (!noAdd) {
49
+ console.log("📦 変更をステージしています... (git add .)");
50
+ (0, git_js_1.stageAllChanges)();
51
+ }
52
+ const diff = (0, git_js_1.getStagedDiff)();
53
+ const hasStagedChanges = diff.trim().length > 0;
54
+ if (hasStagedChanges) {
55
+ // ステージされた変更がある場合は、通常のコミットフローを実行
56
+ await (0, commit_js_1.runCommitCommand)(language, true); // noAdd=true(すでにステージ済み)
57
+ }
58
+ else {
59
+ // ステージされた変更がない場合、未プッシュのコミットがあるかチェック
60
+ if (hasUnpushedCommits(currentBranch)) {
61
+ console.log("💡 ステージされた変更はありませんが、未プッシュのコミットがあります");
62
+ console.log("📤 コミットをプッシュします...");
63
+ }
64
+ else {
65
+ console.error("❌ プッシュするものがありません");
66
+ console.error("");
67
+ console.error("📋 原因: ステージされた変更も未プッシュのコミットもありません");
68
+ console.error("");
69
+ console.error("💡 解決方法:");
70
+ console.error(" 1. 変更を確認: git status");
71
+ console.error(" 2. 変更をステージ: git add .");
72
+ console.error(" 3. または、まずコミット: ai-git commit");
73
+ process.exit(1);
74
+ }
75
+ }
76
+ (0, git_js_1.pushCurrentBranch)(currentBranch);
77
+ console.log(`\n✅ Push 完了!`);
78
+ }