ai
3 мин
28 августа 2026 г.
Источник: Dev.to AI Feed

I Built a Native Windows App That Writes Your Git Commit Messages With AI Here's How

nasir shahbaz
nasir shahbaz
RSS AI Ingest
I Built a Native Windows App That Writes Your Git Commit Messages With AI Here's How

AI Commit Generator: A Native Windows Desktop App That Writes Your Git Commits With AI You know that moment you've spent two hours debugging, the fix is finally in, and now you have to write a commit message. Ugh. I built AI Commit Generat...

AI Commit Generator: A Native Windows Desktop App That Writes Your Git Commits With AI You know that moment you've spent two hours debugging, the fix is finally in, and now you have to write a commit message. Ugh. I built AI Commit Generator to solve exactly that. It's a native Windows desktop app that: Grabs your git diff (staged or unstaged) Sends it to an AI model (via OpenRouter or Google AI Studio) Returns a clean, conventional commit message Lets you copy it, edit it, regenerate it, or just commit and push right there All in a 550×600 pixel window that lives in your system tray. Here's a breakdown of the architecture and the interesting technical decisions along the way. The Tech Stack: C++ + WebView2 The app is written in C++17 using Win32 APIs and Microsoft Edge WebView2 for the UI. Yes, really C++ and web technologies coexisting in the same binary. The core idea: C++ handles the heavy lifting: spawning git processes, managing the system tray, reading/writing config, HTTP requests to GitHub for update checks. WebView2 handles the UI: the entire interface is HTML/CSS/JS rendered inside an embedded Edge WebView2 control. This isn't a framework — it's raw Win32 CreateWindowExW, a message loop, and ICoreWebView2Controller. No Electron, no Tauri, no extra runtime dependencies beyond the Edge WebView2 runtime that ships with Windows 10/11. Why WebView2 and not a full web framework? I wanted a small, native-feeling Windows app. Electron bundles Chromium (~150MB+). WebView2 uses the Edge runtime already installed on the user's machine. The result is a tiny installer (~2MB compressed). The Bridge Pattern: C++ ↔ JavaScript IPC The most interesting architectural piece is the Bridge a C++ class that handles bidirectional communication between the WebView2 frontend and the native backend. Here's how it works: JS → C++ (frontend calls native code) The JavaScript frontend calls window.chrome.webview.postMessage() with a JSON-RPC-style message: { "id": "1", "method": "getGitDiff", "params": { "repoPath": "C:\\projects\\my-app", "staged": false } } The C++ Bridge class receives this via ICoreWebView2WebMessageReceivedEventHandler, parses the method name, dispatches to the appropriate handler, and sends a response back: { "id": "1", "result": "\"diff --git a/file.js b/file.js\\n...\"", "error": null } Available RPC Methods The bridge exposes these native operations to the frontend: Method What it does getConfig Read settings from %APPDATA%\AiCommitGen\settings.json setConfig Write a config key-value pair selectDirectory Open a native Win32 folder picker dialog getGitDiff Run git diff or git diff --cached getGitLog Run git log --oneline -10 commitAndPush Run git add -A, git commit -m "...", then git push with retry checkForUpdates Hit the GitHub Releases API for the latest version downloadUpdate Download the installer exe and launch it This RPC pattern means the JavaScript never directly executes shell commands — everything goes through the bridge, which validates inputs and handles errors cleanly. The AI Integration: Two Providers, One Prompt The app supports two AI providers: OpenRouter Uses the standard OpenAI-compatible chat completions API. Supports models like GPT-4o, Claude 3.5 Haiku, Gemini Flash, and Llama 3.3. const res = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json', 'HTTP-Referer': 'https://github.com/ai-commit-generator', }, body: JSON.stringify({ model: model, messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: 'Recent commits:\n...\n\nGit diff:\n...' }, ], temperature: 0.3, max_tokens: 300, }), }); Google AI Studio (Gemini) Uses the Gemini REST API directly, with special handling for thinking models: const config = { temperature: 0.3, maxOutputTokens: 1024 }; if (model.indexOf('gemini-2.5-flash') === 0) { config.thinkingConfig = { thinkingBudget: 0 }; } else if (model.indexOf('gemini-3') === 0) { config.thinkingConfig = { thinkingLevel: 'MINIMAL' }; } I learned the hard way that Gemini 2.5 Flash with thinking enabled will eat your entire token budget on chain-of-thought before producing the actual commit message. Disabling thinking (thinkingBudget: 0) for commit messages is a must. The System Prompt The prompt that actually generates the commit message follows conventional commits format: You are an expert at writing clear, concise git commit messages. Given a git diff and recent commit history, generate a fitting commit message. Rules: Use conventional commits format: type(scope): description Types: feat, fix, chore, docs, style, refactor, perf, test, ci, build, revert Keep the subject line under 72 characters Focus on WHAT and WHY, not HOW Match the style of recent commits shown in the log The key insight: sending the last 10 commits as context helps the AI match the existing commit style of the project. Smart Git Operations The commitAndPush handler does more than just run commands — it has real-world resilience built in: // Push with retry for transient server errors const int maxRetries = 3; for (int i = 0; i SetVirtualHostNameToFolderMapping( L"app.commitgen", (exeDir + L"\\web").c_str(), COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS); g_webview->Navigate(L"https://app.commitgen/index.html"); // 6. Message loop MSG msg; while (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } One subtle detail: virtual host mapping (SetVirtualHostNameToFolderMapping) is used instead of navigating to file:// directly. This avoids the file:// origin restrictions in WebView2 and gives the frontend a proper https:// origin for CORS and security features. System Tray: The Unsung UX The close button doesn't actually close the app — it hides to the system tray. This is intentional: commit messages are most useful when the app is always one double-click away. case WM_CLOSE: if (!g_isQuitting) { g_webviewController->put_IsVisible(FALSE); ShowWindow(hWnd, SW_HIDE); return 0; // Don't destroy — just hide } break; The tray icon supports: Double-click to restore Right-click menu with Open and Exit options "Start hidden in tray" option for power users who want it always available but never visible Auto-Update System The app checks GitHub Releases on startup. The update flow: C++ sends a WinHTTP request to github.com/NSTechBytes/ai-commit-generator/releases/latest Parses the redirect to extract the tag name Compares versions in JavaScript using a semver comparison If a newer version exists, downloads the installer exe and launches it The version is embedded in the Windows executable's VS_FIXEDFILEINFO resource and read at runtime: static std::wstring GetApplicationVersion() { // ... reads VS_FIXEDFILEINFO from the running .exe return L"1.1.0"; } Project Structure AiCommitGen.sln # Visual Studio solution AiCommitGen/ main.cpp # Win32 entry point + WebView2 init bridge.h / bridge.cpp # C++ ↔ JS RPC bridge web/ index.html # UI (single-page, all CSS inline) index.js # Frontend logic (~400 lines) assets/ # Icons, images installer/ setup.nsi # NSIS installer script setup.ps1 # Release build + installer pipeline Build.ps1 # Debug/Release build script Getting Started Prerequisites: Windows 10/11 Visual Studio 2022 (Desktop development with C++) WebView2 Runtime (comes with Windows) NSIS (for building the installer) Build: # Debug build ./Build.ps1 # Release + Installer ./setup.ps1 Configuration: API keys and settings are stored in %APPDATA%\AiCommitGen\settings.json. What I Learned WebView2 is surprisingly capable. It's basically a modern IE/Edge control that renders Chromium content. The virtual host mapping feature alone makes it viable for local-file apps. JSON parsing in C++ without libraries is tedious. I wrote a custom JsonGetString / ParseFlatStringObject implementation. It works for flat config files, but I wouldn't recommend it for nested JSON. Git commands through CreateProcessW need careful pipe management. Redirecting stdout/stderr through Win32 pipes, then reading from the read end until EOF, is a pattern that looks simple but has subtle gotchas around handle inheritance and buffer sizes. Conventional commits + recent commit history = better AI output. Sending the last 10 commits as context makes the AI match the project's actual style, not just a generic format. Try It Out The app is open source on GitHub: NSTechBytes/ai-commit-generator Install the latest release, paste your OpenRouter or Google AI Studio API key, select a repo, and hit Generate. It's the fastest way I've found to go from "I have no idea what to call this commit" to a clean push. Built with C++17, Win32, WebView2, and a healthy impatience for writing commit messages.

Хотите внедрить ИИ в ваш бренд?

Спроектируем и развернем автономных агентов и современный цифровой стек под ваши задачи.

Рассчитать проект