Skip to content

feat(desktop): AIコミットメッセージ自動生成#4

Merged
MocA-Love merged 2 commits intomainfrom
feat/desktop-ai-commit-message
Mar 27, 2026
Merged

feat(desktop): AIコミットメッセージ自動生成#4
MocA-Love merged 2 commits intomainfrom
feat/desktop-ai-commit-message

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

@MocA-Love MocA-Love commented Mar 27, 2026

概要

コミットメッセージ入力欄の右上にスパークルボタンを追加し、AIによるコミットメッセージの自動生成機能を実装。

既存の callSmallModel インフラを活用し、OpenAI / Anthropic のどちらでも動作する(自動フォールバック)。

変更内容

UIの変更(CommitInput.tsx)

  • テキストエリアの右上にスパークル(✨)アイコンのボタンを配置
  • 生成中は animate-pulse でアニメーション表示
  • 失敗時はトーストでエラーメッセージを表示

バックエンドの変更(git-operations.ts)

  • changes.generateCommitMessage tRPCプロシージャを新規追加
  • **階層的要約方式(gptcommit式)**で最高精度を実現:
    • Phase 1: ファイルごとのdiffを並列でLLM要約(Promise.all
    • Phase 2: 全ファイルの要約を統合して最終コミットメッセージを生成
  • ロックファイル(*.lock, package-lock.json 等)・バイナリはスキップしファイル名のみ渡す
  • 300文字未満の小さなdiffはLLM呼び出しをスキップしそのまま渡す
  • OpenAI OAuth認証時は generateTitleFromMessageWithStreamingModel を使用
  • 全プロバイダー失敗時はコンソールに詳細な診断ログを出力

対応する入力ソース

  • staged(ステージ済み)の変更
  • unstaged(未ステージ)の変更
  • untracked(新規追加)ファイル

生成フォーマット

  • 日本語の conventional commit 形式: type(scope): 日本語の説明
  • 72文字以内

テスト項目

  • スパークルボタンが表示されること
  • 変更がある状態でボタンをクリック → コミットメッセージが自動入力されること
  • staged / unstaged / untracked いずれの変更でも動作すること
  • 大量ファイル変更時でも安定して生成されること
  • AI プロバイダー未設定時にエラーメッセージが表示されること
  • 生成中にボタンがdisabledになり、アニメーションが表示されること

Summary by CodeRabbit

Release Notes

New Features

  • AI-assisted commit message generation: A new button featuring a sparkle icon in the commit input area enables automatic generation of conventional commit messages. The feature analyzes staged changes, unstaged modifications, untracked files, and other modifications to produce comprehensive, contextual commit messages. The button is disabled during processing to ensure timely completion.

Add a sparkle button to the commit message input that generates a
conventional commit message using the configured AI provider (OpenAI or
Anthropic). Uses the existing callSmallModel infrastructure with
automatic provider fallback.

- Gathers staged, unstaged, and untracked file changes for context
- Supports both OAuth and API key authentication for OpenAI
- Generates messages in Japanese with conventional commit format
- Logs detailed provider diagnostics on failure
…generation

Replace the simple truncated-diff approach with a gptcommit-style
two-phase strategy for the highest accuracy on large diffs:

Phase 1: Summarize each changed file independently in parallel via LLM.
  - Lock files, binaries, and other low-value files are skipped
    (file names only passed to Phase 2)
  - Small diffs (<300 chars) are passed directly without an LLM call
  - Large per-file diffs are truncated at 4000 chars before summarizing

Phase 2: Combine all file summaries + diff stats into a single prompt
  and generate the final conventional commit message.

This avoids token-limit truncation that loses important context and
produces significantly better messages for multi-file changes.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Two new components add AI-powered commit message generation: a TRPC backend endpoint that collects staged, unstaged, and untracked changes via git commands and generates conventional commit messages through LLM models, and a frontend UI component with a button to trigger the generation and display the result.

Changes

Cohort / File(s) Summary
Backend TRPC Endpoint
apps/desktop/src/lib/trpc/routers/changes/git-operations.ts
Added generateCommitMessage TRPC procedure that collects git changes (staged/unstaged/untracked) via git commands, filters certain file types and binaries, generates hierarchical per-file summaries, then synthesizes a final commit message using LLM models (callSmallModel with OpenAI or fallback providers).
Frontend Commit Input UI
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitInput/CommitInput.tsx
Integrated generateCommitMessage mutation with loading state handling; wrapped Textarea in a relative container and added AI-generation button (disabled while pending, shows pulsing sparkle icon) that populates commit message or displays error toast on failure.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as CommitInput UI
    participant TRPC as Git Operations<br/>Endpoint
    participant Git as Git Commands
    participant LLM as LLM Models

    User->>UI: Clicks AI Generate Button
    UI->>TRPC: generateCommitMessage(worktreePath)
    TRPC->>Git: git diff --cached --stat
    Git-->>TRPC: Staged changes
    TRPC->>Git: git diff (per-file cached)
    Git-->>TRPC: Per-file diffs
    TRPC->>Git: git status
    Git-->>TRPC: Untracked files
    TRPC->>TRPC: Filter & collect changes<br/>(staged/unstaged/untracked)
    TRPC->>LLM: callSmallModel(per-file summaries)
    LLM-->>TRPC: Per-file summaries
    TRPC->>LLM: callSmallModel(final message)
    LLM-->>TRPC: Commit message
    TRPC-->>UI: { message: string | null }
    UI->>UI: Populate commitMessage field<br/>or show error toast
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Whiskers twitch with AI delight,
Git diffs dance in morning light,
Commits flow like carrot juice,
LLM magic, set it loose!
Messages born from sparkling wit,
Auto-crafted, a perfect fit.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding AI-powered commit message auto-generation to the desktop application.
Description check ✅ Passed The description provides comprehensive coverage of the changes, technical implementation details, and testing criteria, though it does not follow the exact template structure (missing formal sections for Related Issues, Type of Change, etc.).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/desktop-ai-commit-message

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MocA-Love MocA-Love self-assigned this Mar 27, 2026
@MocA-Love MocA-Love merged commit a625f09 into main Mar 27, 2026
1 check was pending
@MocA-Love MocA-Love deleted the feat/desktop-ai-commit-message branch March 29, 2026 05:05
MocA-Love added a commit that referenced this pull request Apr 4, 2026
#1 ダッシュボード戻り時のポーリング永続: activeWorkspaceId=null時に
   deactivateAll()を呼ぶ。setActiveWorkspace(null)対応追加

#2 deactivateされたWSのMapエントリ蓄積: 問題の根本はregisterが増え続ける
   ことではなく、全WSがisActive:trueで起動していたこと。#3で解消

#3 起動直後の全WSポーリング並走: registerWorkspaceをisActive:falseに変更。
   タイマーはactivateWorkspace/setActiveWorkspaceでのみ起動

#4 WS切替時の初回表示5s遅延: activateWorkspace/setActiveWorkspace時に
   即時sync(syncPRStatus+syncPRComments)を実行

追加改善:
- startTimersに防衛的stopTimers追加(二重タイマー防止)
- onWindowFocus()をデッドコードとして削除
- deactivateAll()メソッド追加
MocA-Love pushed a commit that referenced this pull request Apr 18, 2026
…uperset-sh#3517)

* remove 7 day rule

* Upgrade mastra

* upgrade ai

* Ad mastra

* refactor(desktop): remove dead provider-diagnostics plumbing

The provider-diagnostics store was fed by callSmallModel's per-attempt
reporting, which was removed when small-model tasks moved to direct AI-SDK
+ mastracode's AuthStorage. Nothing writes to the issue map anymore, so the
clearIssue mutation, getStatuses query, and diagnosticStatus plumbing in
ModelsSettings were all no-ops.

Settings still surfaces "Session expired / Reconnect" via auth-status alone.
ProviderIssue type collapsed from 8 codes to just "expired" to match.

* fix(auth): auto-refresh expired Anthropic OAuth tokens

Anthropic credentials were read via authStorage.get() everywhere, so
mastracode's built-in refresh flow never ran. Once the 1-hour access
token expired, status flipped to "Reconnect" and users had to do a
full PKCE re-auth, even though a valid refresh token was already
stored.

Resolvers now call authStorage.getApiKey() for oauth creds on expiry,
which triggers refreshToken() and persists the refreshed credential.
getAnthropicAuthStatus does the same before declaring issue: "expired".
Mirrors the pattern already used for OpenAI small-model auth.

* review: address PR feedback from cubic + coderabbit + greptile

- host-service ai-branch-name: run trailing-trim after slice so a
  100-char truncation can't re-introduce a bare "." or "-" that git
  rejects as an invalid ref (coderabbit / cubic #2, #7).
- host-service workspace-creation.generateBranchName: reuse the
  existing listBranchNames helper instead of the inline git walk,
  which classified off the short refname and could conflate a local
  "origin/foo" with refs/remotes/origin/foo (coderabbit #3).
- packages/chat shared/small-model: drop the unused
  hasSmallModelCredentials export; only a test mock consumed it
  (greptile #4).
- resolveAnthropicCredential: on refresh failure, return null instead
  of kind:"oauth" with a stale expiresAt so callers fall back cleanly
  (cubic #8).
- chat-service.getAnthropicAuthStatus: log context when refresh throws
  instead of silently swallowing (cubic #9).

* fix(chat): read auth.json directly instead of importing mastracode

Importing createAuthStorage from mastracode loads the entire CLI tree
(fastembed → onnxruntime-node's 208 MB native binary) via eager
top-level requires in mastracode's CJS entry. This crashed
electron-vite bundling and bloated the get-small-model chunk.

getSmallModel now reads mastracode's auth.json file directly using
the same path resolution logic (~/Library/Application Support/mastracode/
on macOS). Zero mastracode import, zero bundle impact. The chunk stays
at 1.2 MB (just @ai-sdk/anthropic + @ai-sdk/openai).

Production build verified: compile:app succeeds, Electron main process
boots with no onnxruntime error.

* docs(desktop): add manual testing plan for PR superset-sh#3517

* fix api key storage slot

* fix(auth): store API keys in dedicated slot so OAuth doesn't clobber them

setApiKeyForProvider and setStoredAnthropicApiKeyFromEnvVariables now
use authStorage.setStoredApiKey() (writes to "apikey:<provider>")
instead of authStorage.set() (writes to the main "<provider>" slot
shared with OAuth). This way connecting/disconnecting OAuth doesn't
overwrite or delete a stored API key.

resolveAuthMethodForProvider falls back to hasStoredApiKey() after
checking the main slot, so status correctly reports authenticated
when only an API key is stored.

* fix(auth): backup/restore API keys across OAuth connect/disconnect

mastracode's resolveModel only reads API keys from the main
authStorage slot (authStorage.get("anthropic")). OAuth login
overwrites this slot, and disconnect removes it — losing any
previously saved API key.

Fix: backup the API key to the dedicated apikey: slot before OAuth
connect, restore it after disconnect. setApiKeyForProvider now writes
to both slots (main for resolveModel compatibility, apikey: for
backup). resolveAuthMethodForProvider checks both.

Applies to both Anthropic and OpenAI providers.

* chore: add upstream PR reference to auth workaround

Point to mastra-ai/mastra#15483 so the backup/restore code can be
removed once upstream lands and we bump mastracode.

* refactor(desktop): derive settings provider action from status

Replace the cascade of if/else + canDisconnect flag with a single
getProviderAction(status) → connect | reconnect | logout | null.
Fixes "Active" badge + "Connect" button showing simultaneously
when authenticated via API key.

* fix(desktop): always show Logout when provider is active

Active providers now always show a Logout button. Clears OAuth or
API key depending on authMethod — no more "Active" badge with no
way to disconnect.

* fix(desktop): simplify OpenAI OAuth dialog + auto-open browser

Match Anthropic dialog's layout: remove the raw OAuth URL display
and "Tip" block, auto-open the browser on OAuth start. Change
"Back" to "Cancel" for consistency.

* refactor(desktop): unify OAuth dialogs into shared OAuthDialog

Extract shared OAuthDialog component with provider config object.
AnthropicOAuthDialog and OpenAIOAuthDialog become thin wrappers
that pass provider-specific labels and options.

* fix(desktop): show 'Copied!' feedback on Copy URL button

* refactor(desktop): merge provider account + API key into single card

Each provider section now renders AccountCard + ConfigRow inside
one rounded card with a divider, instead of two separate cards.
Removes the standalone "API Keys" collapsible section.

* refactor(desktop): compact OAuth row in provider settings card

OAuth row is now a single inline row (label + status + action)
instead of a stacked AccountCard. Both providers share the same
2-row card layout: OAuth row + API key row with divider.

* fix(desktop): contextual buttons in provider settings

Connect is now primary (filled). Save only shows when there's input.
Clear only shows when a key is saved. Removes visual noise from
empty-state provider cards.

* ui(desktop): add provider icons to settings section headers

* ui(desktop): show 'Not connected' badge instead of subtitle for disconnected providers

* ui: remove redundant disconnected subtitle

* ui: remove subtitle text from OAuth rows

* chore: remove dead AccountCard + getProviderSubtitle

* docs: update test plan to match current UI

* chore: move shipped plans to done/

---------

Co-authored-by: AviPeltz <aj.peltz@gmail.com>
MocA-Love pushed a commit that referenced this pull request Apr 18, 2026
…uperset-sh#3517)

* remove 7 day rule

* Upgrade mastra

* upgrade ai

* Ad mastra

* refactor(desktop): remove dead provider-diagnostics plumbing

The provider-diagnostics store was fed by callSmallModel's per-attempt
reporting, which was removed when small-model tasks moved to direct AI-SDK
+ mastracode's AuthStorage. Nothing writes to the issue map anymore, so the
clearIssue mutation, getStatuses query, and diagnosticStatus plumbing in
ModelsSettings were all no-ops.

Settings still surfaces "Session expired / Reconnect" via auth-status alone.
ProviderIssue type collapsed from 8 codes to just "expired" to match.

* fix(auth): auto-refresh expired Anthropic OAuth tokens

Anthropic credentials were read via authStorage.get() everywhere, so
mastracode's built-in refresh flow never ran. Once the 1-hour access
token expired, status flipped to "Reconnect" and users had to do a
full PKCE re-auth, even though a valid refresh token was already
stored.

Resolvers now call authStorage.getApiKey() for oauth creds on expiry,
which triggers refreshToken() and persists the refreshed credential.
getAnthropicAuthStatus does the same before declaring issue: "expired".
Mirrors the pattern already used for OpenAI small-model auth.

* review: address PR feedback from cubic + coderabbit + greptile

- host-service ai-branch-name: run trailing-trim after slice so a
  100-char truncation can't re-introduce a bare "." or "-" that git
  rejects as an invalid ref (coderabbit / cubic #2, #7).
- host-service workspace-creation.generateBranchName: reuse the
  existing listBranchNames helper instead of the inline git walk,
  which classified off the short refname and could conflate a local
  "origin/foo" with refs/remotes/origin/foo (coderabbit #3).
- packages/chat shared/small-model: drop the unused
  hasSmallModelCredentials export; only a test mock consumed it
  (greptile #4).
- resolveAnthropicCredential: on refresh failure, return null instead
  of kind:"oauth" with a stale expiresAt so callers fall back cleanly
  (cubic #8).
- chat-service.getAnthropicAuthStatus: log context when refresh throws
  instead of silently swallowing (cubic #9).

* fix(chat): read auth.json directly instead of importing mastracode

Importing createAuthStorage from mastracode loads the entire CLI tree
(fastembed → onnxruntime-node's 208 MB native binary) via eager
top-level requires in mastracode's CJS entry. This crashed
electron-vite bundling and bloated the get-small-model chunk.

getSmallModel now reads mastracode's auth.json file directly using
the same path resolution logic (~/Library/Application Support/mastracode/
on macOS). Zero mastracode import, zero bundle impact. The chunk stays
at 1.2 MB (just @ai-sdk/anthropic + @ai-sdk/openai).

Production build verified: compile:app succeeds, Electron main process
boots with no onnxruntime error.

* docs(desktop): add manual testing plan for PR superset-sh#3517

* fix api key storage slot

* fix(auth): store API keys in dedicated slot so OAuth doesn't clobber them

setApiKeyForProvider and setStoredAnthropicApiKeyFromEnvVariables now
use authStorage.setStoredApiKey() (writes to "apikey:<provider>")
instead of authStorage.set() (writes to the main "<provider>" slot
shared with OAuth). This way connecting/disconnecting OAuth doesn't
overwrite or delete a stored API key.

resolveAuthMethodForProvider falls back to hasStoredApiKey() after
checking the main slot, so status correctly reports authenticated
when only an API key is stored.

* fix(auth): backup/restore API keys across OAuth connect/disconnect

mastracode's resolveModel only reads API keys from the main
authStorage slot (authStorage.get("anthropic")). OAuth login
overwrites this slot, and disconnect removes it — losing any
previously saved API key.

Fix: backup the API key to the dedicated apikey: slot before OAuth
connect, restore it after disconnect. setApiKeyForProvider now writes
to both slots (main for resolveModel compatibility, apikey: for
backup). resolveAuthMethodForProvider checks both.

Applies to both Anthropic and OpenAI providers.

* chore: add upstream PR reference to auth workaround

Point to mastra-ai/mastra#15483 so the backup/restore code can be
removed once upstream lands and we bump mastracode.

* refactor(desktop): derive settings provider action from status

Replace the cascade of if/else + canDisconnect flag with a single
getProviderAction(status) → connect | reconnect | logout | null.
Fixes "Active" badge + "Connect" button showing simultaneously
when authenticated via API key.

* fix(desktop): always show Logout when provider is active

Active providers now always show a Logout button. Clears OAuth or
API key depending on authMethod — no more "Active" badge with no
way to disconnect.

* fix(desktop): simplify OpenAI OAuth dialog + auto-open browser

Match Anthropic dialog's layout: remove the raw OAuth URL display
and "Tip" block, auto-open the browser on OAuth start. Change
"Back" to "Cancel" for consistency.

* refactor(desktop): unify OAuth dialogs into shared OAuthDialog

Extract shared OAuthDialog component with provider config object.
AnthropicOAuthDialog and OpenAIOAuthDialog become thin wrappers
that pass provider-specific labels and options.

* fix(desktop): show 'Copied!' feedback on Copy URL button

* refactor(desktop): merge provider account + API key into single card

Each provider section now renders AccountCard + ConfigRow inside
one rounded card with a divider, instead of two separate cards.
Removes the standalone "API Keys" collapsible section.

* refactor(desktop): compact OAuth row in provider settings card

OAuth row is now a single inline row (label + status + action)
instead of a stacked AccountCard. Both providers share the same
2-row card layout: OAuth row + API key row with divider.

* fix(desktop): contextual buttons in provider settings

Connect is now primary (filled). Save only shows when there's input.
Clear only shows when a key is saved. Removes visual noise from
empty-state provider cards.

* ui(desktop): add provider icons to settings section headers

* ui(desktop): show 'Not connected' badge instead of subtitle for disconnected providers

* ui: remove redundant disconnected subtitle

* ui: remove subtitle text from OAuth rows

* chore: remove dead AccountCard + getProviderSubtitle

* docs: update test plan to match current UI

* chore: move shipped plans to done/

---------

Co-authored-by: AviPeltz <aj.peltz@gmail.com>
MocA-Love added a commit that referenced this pull request Apr 23, 2026
upstream 取り込み PR #4: terminal 系 / 3 commits + dedupe fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant