From 852192dce6d5a269c87d6a7daa5d230251db6b33 Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Fri, 21 Nov 2025 21:32:53 +0800 Subject: [PATCH] feat: add Git Bash detection and requirement check for Windows agents (#11388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add Git Bash detection and requirement check for Windows agents - Add System_CheckGitBash IPC channel for detecting Git Bash installation - Implement detection logic checking common installation paths and PATH environment - Display non-closable error alert in AgentModal when Git Bash is not found - Disable agent creation/edit button until Git Bash is installed - Add recheck functionality to verify installation without restarting app Git Bash is required for agents to function properly on Windows systems. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * i18n: add Git Bash requirement translations for agent modal - Add English translations for Git Bash detection warnings - Add Simplified Chinese (zh-cn) translations - Add Traditional Chinese (zh-tw) translations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * format code --------- Co-authored-by: Claude --- packages/shared/IpcChannel.ts | 1 + src/main/ipc.ts | 38 ++++++++++++ src/preload/index.ts | 3 +- .../components/Popups/agent/AgentModal.tsx | 59 ++++++++++++++++++- src/renderer/src/i18n/locales/en-us.json | 9 +++ src/renderer/src/i18n/locales/zh-cn.json | 9 +++ src/renderer/src/i18n/locales/zh-tw.json | 9 +++ src/renderer/src/i18n/translate/de-de.json | 9 +++ src/renderer/src/i18n/translate/el-gr.json | 9 +++ src/renderer/src/i18n/translate/es-es.json | 9 +++ src/renderer/src/i18n/translate/fr-fr.json | 9 +++ src/renderer/src/i18n/translate/ja-jp.json | 9 +++ src/renderer/src/i18n/translate/pt-pt.json | 9 +++ src/renderer/src/i18n/translate/ru-ru.json | 9 +++ 14 files changed, 188 insertions(+), 3 deletions(-) diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index b90ef3b356..67bd137b8e 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -235,6 +235,7 @@ export enum IpcChannel { System_GetDeviceType = 'system:getDeviceType', System_GetHostname = 'system:getHostname', System_GetCpuName = 'system:getCpuName', + System_CheckGitBash = 'system:checkGitBash', // DevTools System_ToggleDevTools = 'system:toggleDevTools', diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 9750a4cf05..e537b85261 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -493,6 +493,44 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ipcMain.handle(IpcChannel.System_GetDeviceType, () => (isMac ? 'mac' : isWin ? 'windows' : 'linux')) ipcMain.handle(IpcChannel.System_GetHostname, () => require('os').hostname()) ipcMain.handle(IpcChannel.System_GetCpuName, () => require('os').cpus()[0].model) + ipcMain.handle(IpcChannel.System_CheckGitBash, () => { + if (!isWin) { + return true // Non-Windows systems don't need Git Bash + } + + try { + // Check common Git Bash installation paths + const commonPaths = [ + path.join(process.env.ProgramFiles || 'C:\\Program Files', 'Git', 'bin', 'bash.exe'), + path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'), + path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Git', 'bin', 'bash.exe') + ] + + // Check if any of the common paths exist + for (const bashPath of commonPaths) { + if (fs.existsSync(bashPath)) { + logger.debug('Git Bash found', { path: bashPath }) + return true + } + } + + // Check if git is in PATH + const { execSync } = require('child_process') + try { + execSync('git --version', { stdio: 'ignore' }) + logger.debug('Git found in PATH') + return true + } catch { + // Git not in PATH + } + + logger.debug('Git Bash not found on Windows system') + return false + } catch (error) { + logger.error('Error checking Git Bash', error as Error) + return false + } + }) ipcMain.handle(IpcChannel.System_ToggleDevTools, (e) => { const win = BrowserWindow.fromWebContents(e.sender) win && win.webContents.toggleDevTools() diff --git a/src/preload/index.ts b/src/preload/index.ts index 11a8e4589f..92f44075aa 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -122,7 +122,8 @@ const api = { system: { getDeviceType: () => ipcRenderer.invoke(IpcChannel.System_GetDeviceType), getHostname: () => ipcRenderer.invoke(IpcChannel.System_GetHostname), - getCpuName: () => ipcRenderer.invoke(IpcChannel.System_GetCpuName) + getCpuName: () => ipcRenderer.invoke(IpcChannel.System_GetCpuName), + checkGitBash: (): Promise => ipcRenderer.invoke(IpcChannel.System_CheckGitBash) }, devTools: { toggle: () => ipcRenderer.invoke(IpcChannel.System_ToggleDevTools) diff --git a/src/renderer/src/components/Popups/agent/AgentModal.tsx b/src/renderer/src/components/Popups/agent/AgentModal.tsx index 2574cbe669..e72433e88a 100644 --- a/src/renderer/src/components/Popups/agent/AgentModal.tsx +++ b/src/renderer/src/components/Popups/agent/AgentModal.tsx @@ -15,7 +15,7 @@ import type { UpdateAgentForm } from '@renderer/types' import { AgentConfigurationSchema, isAgentType } from '@renderer/types' -import { Button, Input, Modal, Select } from 'antd' +import { Alert, Button, Input, Modal, Select } from 'antd' import { AlertTriangleIcon } from 'lucide-react' import type { ChangeEvent, FormEvent } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -58,6 +58,7 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { const isEditing = (agent?: AgentWithTools) => agent !== undefined const [form, setForm] = useState(() => buildAgentForm(agent)) + const [hasGitBash, setHasGitBash] = useState(true) useEffect(() => { if (open) { @@ -65,6 +66,30 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { } }, [agent, open]) + const checkGitBash = useCallback( + async (showToast = false) => { + try { + const gitBashInstalled = await window.api.system.checkGitBash() + setHasGitBash(gitBashInstalled) + if (showToast) { + if (gitBashInstalled) { + window.toast.success(t('agent.gitBash.success', 'Git Bash detected successfully!')) + } else { + window.toast.error(t('agent.gitBash.notFound', 'Git Bash not found. Please install it first.')) + } + } + } catch (error) { + logger.error('Failed to check Git Bash:', error as Error) + setHasGitBash(true) // Default to true on error to avoid false warnings + } + }, + [t] + ) + + useEffect(() => { + checkGitBash() + }, [checkGitBash]) + const selectedPermissionMode = form.configuration?.permission_mode ?? 'default' const onPermissionModeChange = useCallback((value: PermissionMode) => { @@ -275,6 +300,36 @@ const PopupContainer: React.FC = ({ agent, afterSubmit, resolve }) => { footer={null}> + {!hasGitBash && ( + +
+ {t( + 'agent.gitBash.error.description', + 'Git Bash is required to run agents on Windows. The agent cannot function without it. Please install Git for Windows from' + )}{' '} + { + e.preventDefault() + window.api.openWebsite('https://git-scm.com/download/win') + }} + style={{ textDecoration: 'underline' }}> + git-scm.com + +
+ + + } + type="error" + showIcon + style={{ marginBottom: 16 }} + /> + )}