diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts index dcbe796eb70..02fb5dfc5a1 100644 --- a/src/core/checkpoints/index.ts +++ b/src/core/checkpoints/index.ts @@ -6,6 +6,8 @@ import { TelemetryService } from "@roo-code/telemetry" import { Task } from "../task/Task" import { getWorkspacePath } from "../../utils/path" +import { checkGitInstalled } from "../../utils/git" +import { t } from "../../i18n" import { ClineApiReqInfo } from "../../shared/ExtensionMessage" import { getApiMetrics } from "../../shared/getApiMetrics" @@ -70,6 +72,47 @@ export function getCheckpointService(cline: Task) { cline.checkpointServiceInitializing = true + // Check if Git is installed before initializing the service + // Note: This is intentionally fire-and-forget to match the original IIFE pattern + // The service is returned immediately while Git check happens asynchronously + checkGitInstallation(cline, service, log, provider) + + return service + } catch (err) { + log(`[Task#getCheckpointService] ${err.message}`) + cline.enableCheckpoints = false + return undefined + } +} + +async function checkGitInstallation( + cline: Task, + service: RepoPerTaskCheckpointService, + log: (message: string) => void, + provider: any, +) { + try { + const gitInstalled = await checkGitInstalled() + + if (!gitInstalled) { + log("[Task#getCheckpointService] Git is not installed, disabling checkpoints") + cline.enableCheckpoints = false + cline.checkpointServiceInitializing = false + + // Show user-friendly notification + const selection = await vscode.window.showWarningMessage( + t("common:errors.git_not_installed"), + t("common:buttons.learn_more"), + ) + + if (selection === t("common:buttons.learn_more")) { + await vscode.env.openExternal(vscode.Uri.parse("https://git-scm.com/downloads")) + } + + return + } + + // Git is installed, proceed with initialization service.on("initialize", () => { log("[Task#getCheckpointService] service initialized") @@ -115,12 +158,11 @@ export function getCheckpointService(cline: Task) { log(`[Task#getCheckpointService] initShadowGit -> ${err.message}`) cline.enableCheckpoints = false }) - - return service } catch (err) { - log(`[Task#getCheckpointService] ${err.message}`) + log(`[Task#getCheckpointService] Unexpected error during Git check: ${err.message}`) + console.error("Git check error:", err) cline.enableCheckpoints = false - return undefined + cline.checkpointServiceInitializing = false } } diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 772156286e5..82c636ed14a 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -32,6 +32,7 @@ "could_not_open_file_generic": "No s'ha pogut obrir el fitxer!", "checkpoint_timeout": "S'ha esgotat el temps en intentar restaurar el punt de control.", "checkpoint_failed": "Ha fallat la restauració del punt de control.", + "git_not_installed": "Git és necessari per a la funció de punts de control. Si us plau, instal·la Git per activar els punts de control.", "no_workspace": "Si us plau, obre primer una carpeta de projecte", "update_support_prompt": "Ha fallat l'actualització del missatge de suport", "reset_support_prompt": "Ha fallat el restabliment del missatge de suport", @@ -111,7 +112,8 @@ }, "buttons": { "save": "Desar", - "edit": "Editar" + "edit": "Editar", + "learn_more": "Més informació" }, "tasks": { "canceled": "Error de tasca: Ha estat aturada i cancel·lada per l'usuari.", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index c136fba8092..65a6f441c24 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Datei konnte nicht geöffnet werden!", "checkpoint_timeout": "Zeitüberschreitung beim Versuch, den Checkpoint wiederherzustellen.", "checkpoint_failed": "Fehler beim Wiederherstellen des Checkpoints.", + "git_not_installed": "Git ist für die Checkpoint-Funktion erforderlich. Bitte installiere Git, um Checkpoints zu aktivieren.", "no_workspace": "Bitte öffne zuerst einen Projektordner", "update_support_prompt": "Fehler beim Aktualisieren der Support-Nachricht", "reset_support_prompt": "Fehler beim Zurücksetzen der Support-Nachricht", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Speichern", - "edit": "Bearbeiten" + "edit": "Bearbeiten", + "learn_more": "Mehr erfahren" }, "tasks": { "canceled": "Aufgabenfehler: Die Aufgabe wurde vom Benutzer gestoppt und abgebrochen.", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b0fdb9d8df8..4f69b2c8947 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Could not open file!", "checkpoint_timeout": "Timed out when attempting to restore checkpoint.", "checkpoint_failed": "Failed to restore checkpoint.", + "git_not_installed": "Git is required for the checkpoints feature. Please install Git to enable checkpoints.", "no_workspace": "Please open a project folder first", "update_support_prompt": "Failed to update support prompt", "reset_support_prompt": "Failed to reset support prompt", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Save", - "edit": "Edit" + "edit": "Edit", + "learn_more": "Learn More" }, "tasks": { "canceled": "Task error: It was stopped and canceled by the user.", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 39cf48383eb..aeea8952e42 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "¡No se pudo abrir el archivo!", "checkpoint_timeout": "Se agotó el tiempo al intentar restaurar el punto de control.", "checkpoint_failed": "Error al restaurar el punto de control.", + "git_not_installed": "Git es necesario para la función de puntos de control. Por favor, instala Git para activar los puntos de control.", "no_workspace": "Por favor, abre primero una carpeta de proyecto", "update_support_prompt": "Error al actualizar el mensaje de soporte", "reset_support_prompt": "Error al restablecer el mensaje de soporte", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Guardar", - "edit": "Editar" + "edit": "Editar", + "learn_more": "Más información" }, "tasks": { "canceled": "Error de tarea: Fue detenida y cancelada por el usuario.", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index ace5bbe47a6..fb17b68f677 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Impossible d'ouvrir le fichier !", "checkpoint_timeout": "Expiration du délai lors de la tentative de rétablissement du checkpoint.", "checkpoint_failed": "Échec du rétablissement du checkpoint.", + "git_not_installed": "Git est requis pour la fonctionnalité des points de contrôle. Veuillez installer Git pour activer les points de contrôle.", "no_workspace": "Veuillez d'abord ouvrir un espace de travail", "update_support_prompt": "Erreur lors de la mise à jour du prompt de support", "reset_support_prompt": "Erreur lors de la réinitialisation du prompt de support", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Enregistrer", - "edit": "Modifier" + "edit": "Modifier", + "learn_more": "En savoir plus" }, "tasks": { "canceled": "Erreur de tâche : Elle a été arrêtée et annulée par l'utilisateur.", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 84dbe9052af..d88d1ce37be 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "फ़ाइल नहीं खोली जा सकी!", "checkpoint_timeout": "चेकपॉइंट को पुनर्स्थापित करने का प्रयास करते समय टाइमआउट हो गया।", "checkpoint_failed": "चेकपॉइंट पुनर्स्थापित करने में विफल।", + "git_not_installed": "चेकपॉइंट सुविधा के लिए Git आवश्यक है। कृपया चेकपॉइंट সক্ষম करने के लिए Git इंस्टॉल करें।", "no_workspace": "कृपया पहले प्रोजेक्ट फ़ोल्डर खोलें", "update_support_prompt": "सपोर्ट प्रॉम्प्ट अपडेट करने में विफल", "reset_support_prompt": "सपोर्ट प्रॉम्प्ट रीसेट करने में विफल", @@ -107,7 +108,8 @@ }, "buttons": { "save": "सहेजें", - "edit": "संपादित करें" + "edit": "संपादित करें", + "learn_more": "और अधिक जानें" }, "tasks": { "canceled": "टास्क त्रुटि: इसे उपयोगकर्ता द्वारा रोका और रद्द किया गया था।", diff --git a/src/i18n/locales/id/common.json b/src/i18n/locales/id/common.json index fb2a30994ee..d50f78a0236 100644 --- a/src/i18n/locales/id/common.json +++ b/src/i18n/locales/id/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Tidak dapat membuka file!", "checkpoint_timeout": "Timeout saat mencoba memulihkan checkpoint.", "checkpoint_failed": "Gagal memulihkan checkpoint.", + "git_not_installed": "Git diperlukan untuk fitur checkpoint. Silakan instal Git untuk mengaktifkan checkpoint.", "no_workspace": "Silakan buka folder proyek terlebih dahulu", "update_support_prompt": "Gagal memperbarui support prompt", "reset_support_prompt": "Gagal mereset support prompt", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Simpan", - "edit": "Edit" + "edit": "Edit", + "learn_more": "Pelajari Lebih Lanjut" }, "tasks": { "canceled": "Error tugas: Dihentikan dan dibatalkan oleh pengguna.", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index 4681612e9d7..6620914d834 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Impossibile aprire il file!", "checkpoint_timeout": "Timeout durante il tentativo di ripristinare il checkpoint.", "checkpoint_failed": "Impossibile ripristinare il checkpoint.", + "git_not_installed": "Git è richiesto per la funzione di checkpoint. Per favore, installa Git per abilitare i checkpoint.", "no_workspace": "Per favore, apri prima una cartella di progetto", "update_support_prompt": "Errore durante l'aggiornamento del messaggio di supporto", "reset_support_prompt": "Errore durante il ripristino del messaggio di supporto", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Salva", - "edit": "Modifica" + "edit": "Modifica", + "learn_more": "Scopri di più" }, "tasks": { "canceled": "Errore attività: È stata interrotta e annullata dall'utente.", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 38fc9d27c53..79c0d37df0b 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "ファイルを開けませんでした!", "checkpoint_timeout": "チェックポイントの復元を試みる際にタイムアウトしました。", "checkpoint_failed": "チェックポイントの復元に失敗しました。", + "git_not_installed": "チェックポイント機能にはGitが必要です。チェックポイントを有効にするにはGitをインストールしてください。", "no_workspace": "まずプロジェクトフォルダを開いてください", "update_support_prompt": "サポートメッセージの更新に失敗しました", "reset_support_prompt": "サポートメッセージのリセットに失敗しました", @@ -107,7 +108,8 @@ }, "buttons": { "save": "保存", - "edit": "編集" + "edit": "編集", + "learn_more": "詳細" }, "tasks": { "canceled": "タスクエラー:ユーザーによって停止およびキャンセルされました。", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index d76a82a7c20..3f25199595d 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "파일을 열 수 없습니다!", "checkpoint_timeout": "체크포인트 복원을 시도하는 중 시간 초과되었습니다.", "checkpoint_failed": "체크포인트 복원에 실패했습니다.", + "git_not_installed": "체크포인트 기능을 사용하려면 Git이 필요합니다. 체크포인트를 활성화하려면 Git을 설치하세요.", "no_workspace": "먼저 프로젝트 폴더를 열어주세요", "update_support_prompt": "지원 프롬프트 업데이트에 실패했습니다", "reset_support_prompt": "지원 프롬프트 재설정에 실패했습니다", @@ -107,7 +108,8 @@ }, "buttons": { "save": "저장", - "edit": "편집" + "edit": "편집", + "learn_more": "더 알아보기" }, "tasks": { "canceled": "작업 오류: 사용자에 의해 중지 및 취소되었습니다.", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index 5caa0534ee8..0beebc8f874 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Kon bestand niet openen!", "checkpoint_timeout": "Time-out bij het herstellen van checkpoint.", "checkpoint_failed": "Herstellen van checkpoint mislukt.", + "git_not_installed": "Git is vereist voor de checkpoint-functie. Installeer Git om checkpoints in te schakelen.", "no_workspace": "Open eerst een projectmap", "update_support_prompt": "Bijwerken van ondersteuningsprompt mislukt", "reset_support_prompt": "Resetten van ondersteuningsprompt mislukt", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Opslaan", - "edit": "Bewerken" + "edit": "Bewerken", + "learn_more": "Meer informatie" }, "tasks": { "canceled": "Taakfout: gestopt en geannuleerd door gebruiker.", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index 77008aa0abc..727d1d58685 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Nie można otworzyć pliku!", "checkpoint_timeout": "Upłynął limit czasu podczas próby przywrócenia punktu kontrolnego.", "checkpoint_failed": "Nie udało się przywrócić punktu kontrolnego.", + "git_not_installed": "Funkcja punktów kontrolnych wymaga oprogramowania Git. Zainstaluj Git, aby włączyć punkty kontrolne.", "no_workspace": "Najpierw otwórz folder projektu", "update_support_prompt": "Nie udało się zaktualizować komunikatu wsparcia", "reset_support_prompt": "Nie udało się zresetować komunikatu wsparcia", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Zapisz", - "edit": "Edytuj" + "edit": "Edytuj", + "learn_more": "Dowiedz się więcej" }, "tasks": { "canceled": "Błąd zadania: Zostało zatrzymane i anulowane przez użytkownika.", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index 6f63d9d1ed8..4fed2375c6e 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -32,6 +32,7 @@ "could_not_open_file_generic": "Não foi possível abrir o arquivo!", "checkpoint_timeout": "Tempo esgotado ao tentar restaurar o ponto de verificação.", "checkpoint_failed": "Falha ao restaurar o ponto de verificação.", + "git_not_installed": "O Git é necessário para o recurso de checkpoints. Por favor, instale o Git para habilitar os checkpoints.", "no_workspace": "Por favor, abra primeiro uma pasta de projeto", "update_support_prompt": "Falha ao atualizar o prompt de suporte", "reset_support_prompt": "Falha ao redefinir o prompt de suporte", @@ -111,7 +112,8 @@ }, "buttons": { "save": "Salvar", - "edit": "Editar" + "edit": "Editar", + "learn_more": "Saiba Mais" }, "tasks": { "canceled": "Erro na tarefa: Foi interrompida e cancelada pelo usuário.", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index 4e354bcbc5a..2100a9b5aa0 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Не удалось открыть файл!", "checkpoint_timeout": "Превышено время ожидания при попытке восстановления контрольной точки.", "checkpoint_failed": "Не удалось восстановить контрольную точку.", + "git_not_installed": "Для функции контрольных точек требуется Git. Пожалуйста, установите Git, чтобы включить контрольные точки.", "no_workspace": "Пожалуйста, сначала откройте папку проекта", "update_support_prompt": "Не удалось обновить промпт поддержки", "reset_support_prompt": "Не удалось сбросить промпт поддержки", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Сохранить", - "edit": "Редактировать" + "edit": "Редактировать", + "learn_more": "Узнать больше" }, "tasks": { "canceled": "Ошибка задачи: Она была остановлена и отменена пользователем.", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 5de82d00c6e..150cc7d3f88 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Dosya açılamadı!", "checkpoint_timeout": "Kontrol noktasını geri yüklemeye çalışırken zaman aşımına uğradı.", "checkpoint_failed": "Kontrol noktası geri yüklenemedi.", + "git_not_installed": "Kontrol noktaları özelliği için Git gereklidir. Kontrol noktalarını etkinleştirmek için lütfen Git'i yükleyin.", "no_workspace": "Lütfen önce bir proje klasörü açın", "update_support_prompt": "Destek istemi güncellenemedi", "reset_support_prompt": "Destek istemi sıfırlanamadı", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Kaydet", - "edit": "Düzenle" + "edit": "Düzenle", + "learn_more": "Daha Fazla Bilgi" }, "tasks": { "canceled": "Görev hatası: Kullanıcı tarafından durduruldu ve iptal edildi.", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 014bddda58f..61477237b5f 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "Không thể mở tệp!", "checkpoint_timeout": "Đã hết thời gian khi cố gắng khôi phục điểm kiểm tra.", "checkpoint_failed": "Không thể khôi phục điểm kiểm tra.", + "git_not_installed": "Yêu cầu Git cho tính năng điểm kiểm tra. Vui lòng cài đặt Git để bật điểm kiểm tra.", "no_workspace": "Vui lòng mở thư mục dự án trước", "update_support_prompt": "Không thể cập nhật lời nhắc hỗ trợ", "reset_support_prompt": "Không thể đặt lại lời nhắc hỗ trợ", @@ -107,7 +108,8 @@ }, "buttons": { "save": "Lưu", - "edit": "Chỉnh sửa" + "edit": "Chỉnh sửa", + "learn_more": "Tìm hiểu thêm" }, "tasks": { "canceled": "Lỗi nhiệm vụ: Nó đã bị dừng và hủy bởi người dùng.", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index 268ee5fbb18..8c2d2183050 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -33,6 +33,7 @@ "could_not_open_file_generic": "无法打开文件!", "checkpoint_timeout": "尝试恢复检查点时超时。", "checkpoint_failed": "恢复检查点失败。", + "git_not_installed": "存档点功能需要 Git。请安装 Git 以启用存档点。", "no_workspace": "请先打开项目文件夹", "update_support_prompt": "更新支持消息失败", "reset_support_prompt": "重置支持消息失败", @@ -112,7 +113,8 @@ }, "buttons": { "save": "保存", - "edit": "编辑" + "edit": "编辑", + "learn_more": "了解更多" }, "tasks": { "canceled": "任务错误:它已被用户停止并取消。", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index dec20a1f9a5..4d3dbad9296 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -28,6 +28,7 @@ "could_not_open_file_generic": "無法開啟檔案!", "checkpoint_timeout": "嘗試恢復檢查點時超時。", "checkpoint_failed": "恢復檢查點失敗。", + "git_not_installed": "存檔點功能需要 Git。請安裝 Git 以啟用存檔點。", "no_workspace": "請先開啟專案資料夾", "update_support_prompt": "更新支援訊息失敗", "reset_support_prompt": "重設支援訊息失敗", @@ -107,7 +108,8 @@ }, "buttons": { "save": "儲存", - "edit": "編輯" + "edit": "編輯", + "learn_more": "了解更多" }, "tasks": { "canceled": "工作錯誤:它已被使用者停止並取消。", diff --git a/src/utils/__tests__/git.spec.ts b/src/utils/__tests__/git.spec.ts index 3ab306feec3..f87ae5667b8 100644 --- a/src/utils/__tests__/git.spec.ts +++ b/src/utils/__tests__/git.spec.ts @@ -4,6 +4,7 @@ import * as fs from "fs" import * as path from "path" import { + checkGitInstalled, searchCommits, getCommitInfo, getWorkingState, @@ -83,6 +84,54 @@ describe("git utils", () => { vitest.clearAllMocks() }) + describe("checkGitInstalled", () => { + it("should return true when git --version succeeds", async () => { + vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { + if (command === "git --version") { + callback(null, { stdout: "git version 2.39.2", stderr: "" }) + return {} as any + } + callback(new Error("Unexpected command")) + return {} as any + }) + + const result = await checkGitInstalled() + expect(result).toBe(true) + expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) + }) + + it("should return false when git --version fails", async () => { + vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { + if (command === "git --version") { + callback(new Error("git not found")) + return {} as any + } + callback(new Error("Unexpected command")) + return {} as any + }) + + const result = await checkGitInstalled() + expect(result).toBe(false) + expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) + }) + + it("should handle unexpected errors gracefully", async () => { + vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => { + if (command === "git --version") { + // Simulate an unexpected error + callback(new Error("Unexpected system error")) + return {} as any + } + callback(new Error("Unexpected command")) + return {} as any + }) + + const result = await checkGitInstalled() + expect(result).toBe(false) + expect(vitest.mocked(exec)).toHaveBeenCalledWith("git --version", {}, expect.any(Function)) + }) + }) + describe("searchCommits", () => { const mockCommitData = [ "abc123def456", diff --git a/src/utils/git.ts b/src/utils/git.ts index fd6abfa3097..42d069416e8 100644 --- a/src/utils/git.ts +++ b/src/utils/git.ts @@ -210,7 +210,16 @@ async function checkGitRepo(cwd: string): Promise { } } -async function checkGitInstalled(): Promise { +/** + * Checks if Git is installed on the system by attempting to run git --version + * @returns {Promise} True if Git is installed and accessible, false otherwise + * @example + * const isGitInstalled = await checkGitInstalled(); + * if (!isGitInstalled) { + * console.log("Git is not installed"); + * } + */ +export async function checkGitInstalled(): Promise { try { await execAsync("git --version") return true