Open11

Cursor: コミットメッセージを作成してコミットするのを自動化したい

kabeyakabeya

最近の自分の流行は、CursorのComposerモードに以下をやらせることです。


  1. 「〜する動作のテストコードを書いて」(結果は特に指定しないでもそこそこ書いてくれる)
  2. 結果が間違っていたら「結果はこうなるはずです」って指摘する。
  3. テストケースが作れたら「OKです。コミットメッセージを作成して」
  4. コミットメッセージを手動でコピーする
  5. ソース管理ビューで更新ファイルを手動でステージングする
  6. コミットメッセージを手動でペーストしてコミットボタンを押してコミットする
  7. 新しくできたテストを実行する
  8. テストはだいたいパスしないので、テスト結果をComposerにコピペして「テストがパスしません。何が起きていますか?」とか聞く
  9. 修正コードが提示されるのでAccept Fileする(もうコードをいちいち見ない)
  10. 8に戻る。この繰り返しが4回とかなってきたら「何が問題か切り分けるためにデバッグ出力を追加できますか?」とか言う。これにより解決することが多いけど、ダメな場合はさすがにコードを読んで「1つの状態変数で扱う状態変化が多すぎないですか。ブーリアンでなく列挙型を使うとか、状態変数を分けるとかするのはどうですか?」とかいう
  11. テストが通るようになったら「OKです。コミットメッセージを作成して」
  12. 4〜6相当のことをもう1回やる

もうね、自分でコードを書きません。これがノーコードかと感動しています(違う)。

コミットメッセージはあまりに便利すぎて困りますね。もう自分でコミットメッセージを書く環境に戻れない。

というわけで、3〜6のコミットメッセージを作成させるところから、コミットするところまでをなんとか自動化(というか、ショートカットで充分ですが)したいですね。

コミットメッセージを確認せずに自動コミットするとかいうのもどうかという気はしますが、画面には出力されているのだから必要に応じてあとで直せばいいという割り切りです。

何ができればいいんでしょうか。

  1. Composerのビュー?メッセージを打つ場所?に「コミットメッセージを作成して」というメッセージを打つ
  2. 出力結果を取得する
  3. 更新されているファイルを自動でステージングする
  4. コミットウィンドウにペーストする

というか、たぶん3と4はgitのコマンドを直接実行した方が早いでしょうね。

kabeyakabeya
  1. Composerのビュー?メッセージを打つ場所?に「コミットメッセージを作成して」というメッセージを打つ

これが難しい。

keybindings.jsonに

keybindings.json
[
    {
        "key": "cmd+ctrl+c",
        "command": "runCommands",
        "args": {
          "commands": [
            {
                "command": "workbench.panel.composerViewPane2.view.focus",
            },
            {
                "command": "type",
                "args": { "text": "コミットメッセージを作成して" }
            }
          ]
        }
    }      
]

とか書いてcmd+ctrl+cを押すと、フォーカスがComposerパネルに移る前にタイプされてしまって、テキストエディタ側に「コミットメッセージを作成して」って入ってしまいます。

multi-command、という拡張機能を見つけたので試してみました。intervalを持たせられるんですね。

https://ryuta46.com/173

settings.json
    "multiCommand.commands": [
        {
            "command": "multiCommand.typeMakeCommitMessage",
            "interval": 100,
            "sequence": [
                "workbench.panel.composerViewPane2.view.focus",
                { "command": "type", "args": { "text": "コミットメッセージを作成してください" } }
            ]
        }
    ]
keybindings.json
[
    {
        "key": "cmd+ctrl+c",
        "command": "multiCommand.typeMakeCommitMessage"
    }
]

フォーカスが移るのを待ってくれるようにはなりました。
でも、なんでしょう?おそらくですがtypeコマンドがテキストエディタにしか効かないんじゃないかと思うのですが、Composerのパネルにテキストが入らないんですね。

どうしましょうかね。

kabeyakabeya

executePasteで貼り付けられるようなので、クリップボードに指定したテキストが入れられればいいんですね。

って調べていましたが、拡張機能を書いたほうが早いような気もしてきました…

ペーストできたとしてもsubmitできない気がしますし、それがうまく行ったとしてもそのあとの、作成されたコミットメッセージを取得するのもちょっと大変そうなので。

kabeyakabeya

Cursor自身に、VS Code拡張機能からComposerにテキストを送信するにはどうしたらよいか聞いたところ、AppleScriptのSystemEventでキーストロークをシミュレートするという方法があります、と答えました。
サンプルコードも提示されましたが、Cmd+Vでクリップボードから貼り付けて、returnをタイプするというものでした。

なるほど。

kabeyakabeya

作成した拡張機能、VS Codeでは実行できるんですが、CursorのComposerがないので動作はしません。
一方で、Cursorだとどういうわけか実行できません。有効にならないんですね[1]

ローカルに作成した拡張機能の場合、vsixというファイルを作ればVS Codeにローカルインストールできる、ということでVSIXを作成しました。以下を参考にしました。

https://qiita.com/yuu_1st/items/d2d5a18de4859a165260

VS Codeでは「拡張機能」を選ぶと、パネルの一番上に「最新の情報に更新」「ビューとその他のアクション」の2つのボタンがあり、その他のアクションのプルダウンメニューに「VSIXからのインストール」があるんですが、Cursorにはそれがありません。

コマンドパレットを開き、「Install from VSIX」って打つと実行できます。

インストールして、実行すると初回は、セキュリティ設定を変更するよう依頼されます(キーを送信するので)。

変更して再度実行すると、無事に「コミットメッセージを作成して」をComposerにsubmitできるようになりました。

import * as vscode from 'vscode';
const { exec } = require('child_process');

export function activate(context: vscode.ExtensionContext) {

	const disposableSendToCursor = vscode.commands.registerCommand('makecommitmessage.sendToCursor', async (args?: string) => {
		try {
			if (!args) {
				vscode.window.showErrorMessage('テキストが指定されていません');
				return;
			}

			// まずComposerViewPaneにフォーカスを移動
			await vscode.commands.executeCommand('workbench.panel.composerViewPane2.view.focus');

			// 少し待ってからクリップボードの操作を行う
			await new Promise(resolve => setTimeout(resolve, 100));

			// クリップボードにテキストをコピー
			await vscode.env.clipboard.writeText(args);
			
			// macOSでCursorをアクティブにして、ペーストとSubmitを実行
			exec(`osascript -e '
				tell application "Cursor" to activate
				delay 0.5
				tell application "System Events"
					keystroke "v" using command down
					delay 0.1
					keystroke return
				end tell
			'`);
			
		} catch (error) {
			vscode.window.showErrorMessage(`エラーが発生しました: ${error}`);
		}
	});

	context.subscriptions.push(disposableSendToCursor);
}

// This method is called when your extension is deactivated
export function deactivate() {}

コマンド名が適当過ぎますが、おいおい見直します。

このあと、生成された回答をコピーするという大仕事があります。

脚注
  1. package.jsonにVS Codeのバージョンが指定してあって、1.97.0になっていたのでそれが原因の気がします。Cursorは現時点で1.96.2ベースだそうです ↩︎

kabeyakabeya

私の挑戦はここで終わりそうです。
どうもComposerとの会話を取得できそうにありません。

kabeyakabeya

今日?(2025/2/25)にCursorをアップデート(0.46.4)したら、Calude-3.5 Sonnetのままでも、

  1. 新しくできたテストを実行する
  2. テストはだいたいパスしないので、テスト結果をComposerにコピペして「テストがパスしません。何が起きていますか?」とか聞く
  3. 修正コードが提示されるのでAccept Fileする(もうコードをいちいち見ない)
  4. 8に戻る。この繰り返しが4回とかなってきたら「何が問題か切り分けるためにデバッグ出力を追加できますか?」とか言う。これにより解決することが多いけど、ダメな場合はさすがにコードを読んで「1つの状態変数で扱う状態変化が多すぎないですか。ブーリアンでなく列挙型を使うとか、状態変数を分けるとかするのはどうですか?」とかいう

この辺を勝手にやりはじめるようになりました。前からなのかな。
Yoloモードというのをオンにすると、もっと勝手にやるみたいですね。すごい。

kabeyakabeya

何のことはありませんでした。

https://zenn.dev/rikika/articles/d65e6e676e890d

CursorのAIコミットメッセージは、コードの変更内容を自動で要約し、適切なコミットメッセージを生成する機能です。これにより、開発者は手動でコミットメッセージを考える手間を省き、一貫性のあるメッセージを維持できます。

コミットメッセージ打つところの横のキラキラマークは、コミットメッセージを自動生成するボタンなのでした。
マジですか。

ということは?ということで調べると、「Generate Commit Message」(cursor.generateGitCommitMessage)というのが定義されています。
そして、これにキーバインドを割り当てることができました。

これだとコミットメッセージが生成されるのが、Composerのコンテキスト内ではなく直接コミットメッセージ欄なので、コンテキストを汚さずより便利です。

ステージング&コミットも自動化したらなお良いのですが、メッセージ生成は時間がかかるうえ必ずしも同じ時間で終わるわけでもないので、メッセージ生成とステージング&コミットは分けたほうが良さそうです。

kabeyakabeya

この辺を勝手にやりはじめるようになりました。前からなのかな。

Cursorのバージョンを0.49.4に変えたからなのか何なのか分かりませんが、Agentモード+Auto(=Yoloモード?)で、以下のようなメッセージを出してきました。

なんか修正担当者とマネージャが居るような、そんなやりとりを内部で勝手にしてますね。

前からこうだったのかな。
進化が早すぎるのか、もともとの機能(あるいは機能構成)に自分が気がついていないのか…

kabeyakabeya

以前まで、自動生成されるコミットメッセージに「feat:」「fix:」「refactor:」「test:」などが付いていたのですが、いつからか付かなくなりました。

Cursor自体に付けるようお願いしてみました。

「cursor.generateGitCommitMessage」コマンドで自動で生成されるコミットメッセージに
最近「feat:」「test:」「fix:」などが付かなくなっているようです。
これらの接頭辞を付けるようにできますか?
.cursor/settings.json
{
  "cursor.ai.generateCommitMessage": {
    "enabled": true,
    "useConventionalCommits": true,
    "template": "次のルールに従ってコミットメッセージを生成してください:\n\n1. Conventional Commitsの形式を使用\n2. 適切な接頭辞を選択:\n   - feat: 新機能\n   - fix: バグ修正  \n   - test: テスト関連\n   - refactor: リファクタリング\n   - docs: ドキュメント\n   - style: スタイル・フォーマット\n   - chore: 雑務・メンテナンス\n   - perf: パフォーマンス改善\n   - ci: CI/CD関連\n   - build: ビルド関連\n\n3. 日本語で簡潔に記述\n4. 例: 'feat: 表示機能を追加'\n\n変更内容:",
    "maxLength": 72,
    "includeBody": false
  }
}
.vscode/settings.json
{
  "git.useCommitInputAsStashMessage": true,
  "git.inputValidation": true,
  "scm.inputFontFamily": "Menlo, Monaco, 'Courier New', monospace",
  "scm.inputFontSize": 13,
  "cursor.chat.commitMessageGeneration.enabled": true,
  "cursor.chat.commitMessageGeneration.maxTokens": 100,
  "cursor.chat.commitMessageGeneration.temperature": 0.3,
  "cursor.ai.commitMessage.template": "以下のルールに従ってコミットメッセージを作成してください:\n\n1. Conventional Commitsの形式で作成する\n2. 接頭辞は以下から選択:\n   - feat: 新機能の追加\n   - fix: バグ修正\n   - docs: ドキュメントのみの変更\n   - style: コードの動作に影響しない変更(空白、フォーマット、セミコロンの欠落など)\n   - refactor: バグの修正や機能の追加を行わないコードの変更\n   - test: テストの追加や既存テストの修正\n   - chore: ビルドプロセスやツール、ライブラリの変更\n   - perf: パフォーマンス向上のためのコードの変更\n   - ci: CI設定ファイルやスクリプトの変更\n   - build: ビルドシステムや外部依存関係に影響する変更\n\n3. メッセージは日本語で簡潔に記述\n4. 50文字以内で要約\n5. 例: 'feat: 入力機能を追加'\n\n変更内容を分析して適切な接頭辞を選択してください。",
  "cursor.ai.commitMessage.useConventionalCommits": true,
  "aiCommits.conventionalCommits": true,
  "aiCommits.template": "$type: $subject",
  "git.aiCommitMessage": {
    "enabled": true,
    "conventionalCommits": true,
    "template": "Generate a commit message using Conventional Commits format. Choose appropriate prefix (feat/fix/test/refactor/docs/style/chore/perf/ci/build) and write in Japanese. Example: 'feat: 計算機能を追加'"
  }
}

一瞬「おおやるね!」と思ったんですが、なんか効いていませんね。

kabeyakabeya

自分で一回、「fix:」とか付けるよう修正してコミットしたら、次から再び付けてくれるようになりました。
設定は関係ないかも知れませんね。