mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
Merge 364941f17c into 7adbfec2a4
This commit is contained in:
commit
d426d6d594
41 changed files with 558 additions and 0 deletions
|
|
@ -47,6 +47,7 @@ export const commandIds = [
|
|||
"acceptInput",
|
||||
"focusPanel",
|
||||
"toggleAutoApprove",
|
||||
"generateCommitMessage",
|
||||
] as const
|
||||
|
||||
export type CommandId = (typeof commandIds)[number]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ import { CodeIndexManager } from "../services/code-index/manager"
|
|||
import { importSettingsWithFeedback } from "../core/config/importExport"
|
||||
import { MdmService } from "../services/mdm/MdmService"
|
||||
import { t } from "../i18n"
|
||||
import {
|
||||
getGitDiff,
|
||||
generateCommitMessageFromDiff,
|
||||
getWorkspaceRoot,
|
||||
setScmInputBoxMessage,
|
||||
} from "../utils/commit-message-generator"
|
||||
|
||||
/**
|
||||
* Helper to get the visible ClineProvider instance or log if not found.
|
||||
|
|
@ -195,6 +201,82 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt
|
|||
action: "toggleAutoApprove",
|
||||
})
|
||||
},
|
||||
generateCommitMessage: async () => {
|
||||
const workspaceRoot = getWorkspaceRoot()
|
||||
|
||||
if (!workspaceRoot) {
|
||||
vscode.window.showErrorMessage(t("common:commit.no_workspace"))
|
||||
return
|
||||
}
|
||||
|
||||
const visibleProvider = getVisibleProviderOrLog(outputChannel)
|
||||
|
||||
if (!visibleProvider) {
|
||||
return
|
||||
}
|
||||
|
||||
// Let the user optionally pick a different API profile
|
||||
const listApiConfigMeta = await visibleProvider.providerSettingsManager.listConfig()
|
||||
let apiConfiguration = visibleProvider.contextProxy.getProviderSettings()
|
||||
|
||||
if (listApiConfigMeta.length > 1) {
|
||||
const items = [
|
||||
{ label: t("common:commit.use_current_profile"), id: undefined },
|
||||
...listApiConfigMeta.map((config) => ({
|
||||
label: config.name ?? config.id,
|
||||
id: config.id,
|
||||
})),
|
||||
]
|
||||
|
||||
const selected = await vscode.window.showQuickPick(items, {
|
||||
placeHolder: t("common:commit.select_profile"),
|
||||
})
|
||||
|
||||
if (!selected) {
|
||||
return // User cancelled
|
||||
}
|
||||
|
||||
if (selected.id) {
|
||||
const { name: _, ...providerSettings } = await visibleProvider.providerSettingsManager.getProfile({
|
||||
id: selected.id,
|
||||
})
|
||||
|
||||
if (providerSettings.apiProvider) {
|
||||
apiConfiguration = providerSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: t("common:commit.generating"),
|
||||
cancellable: false,
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const diff = await getGitDiff(workspaceRoot)
|
||||
|
||||
if (!diff.trim()) {
|
||||
vscode.window.showInformationMessage(t("common:commit.no_changes"))
|
||||
return
|
||||
}
|
||||
|
||||
const commitMessage = await generateCommitMessageFromDiff(apiConfiguration, diff)
|
||||
const success = await setScmInputBoxMessage(commitMessage)
|
||||
|
||||
if (success) {
|
||||
// Focus the SCM view to show the generated message
|
||||
await vscode.commands.executeCommand("workbench.view.scm")
|
||||
}
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(
|
||||
`${t("common:commit.generation_failed")}: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export const openClineInNewTab = async ({ context, outputChannel }: Omit<RegisterCommandOptions, "provider">) => {
|
||||
|
|
|
|||
10
src/i18n/locales/ca/common.json
generated
10
src/i18n/locales/ca/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "No s'ha trobat cap carpeta de treball. Obre una carpeta per generar un missatge de commit.",
|
||||
"generating": "Generant missatge de commit...",
|
||||
"no_changes": "No s'han detectat canvis. Prepara o modifica fitxers abans de generar un missatge de commit.",
|
||||
"generation_failed": "No s'ha pogut generar el missatge de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Utilitza el perfil actual",
|
||||
"select_profile": "Selecciona un perfil d'API per a la generació de missatges de commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/de/common.json
generated
10
src/i18n/locales/de/common.json
generated
|
|
@ -246,5 +246,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Kein Arbeitsbereichsordner gefunden. Öffne einen Ordner, um eine Commit-Nachricht zu generieren.",
|
||||
"generating": "Commit-Nachricht wird generiert...",
|
||||
"no_changes": "Keine Änderungen erkannt. Bereite Dateien vor oder ändere sie, bevor du eine Commit-Nachricht generierst.",
|
||||
"generation_failed": "Commit-Nachricht konnte nicht generiert werden",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Aktuelles Profil verwenden",
|
||||
"select_profile": "Wähle ein API-Profil für die Commit-Nachricht-Generierung"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,5 +243,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "No workspace folder found. Open a folder to generate a commit message.",
|
||||
"generating": "Generating commit message...",
|
||||
"no_changes": "No changes detected. Stage or modify files before generating a commit message.",
|
||||
"generation_failed": "Failed to generate commit message",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Use current profile",
|
||||
"select_profile": "Select an API profile for commit message generation"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/es/common.json
generated
10
src/i18n/locales/es/common.json
generated
|
|
@ -246,5 +246,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "No se encontró carpeta de trabajo. Abre una carpeta para generar un mensaje de commit.",
|
||||
"generating": "Generando mensaje de commit...",
|
||||
"no_changes": "No se detectaron cambios. Prepara o modifica archivos antes de generar un mensaje de commit.",
|
||||
"generation_failed": "Error al generar el mensaje de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usar perfil actual",
|
||||
"select_profile": "Selecciona un perfil de API para la generación de mensajes de commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/fr/common.json
generated
10
src/i18n/locales/fr/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Aucun dossier de travail trouvé. Ouvre un dossier pour générer un message de commit.",
|
||||
"generating": "Génération du message de commit...",
|
||||
"no_changes": "Aucun changement détecté. Prépare ou modifie des fichiers avant de générer un message de commit.",
|
||||
"generation_failed": "Échec de la génération du message de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Utiliser le profil actuel",
|
||||
"select_profile": "Sélectionne un profil d'API pour la génération de messages de commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/hi/common.json
generated
10
src/i18n/locales/hi/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "कोई कार्यस्थान फ़ोल्डर नहीं मिला। कमिट संदेश बनाने के लिए एक फ़ोल्डर खोलें।",
|
||||
"generating": "कमिट संदेश बनाया जा रहा है...",
|
||||
"no_changes": "कोई बदलाव नहीं मिला। कमिट संदेश बनाने से पहले फ़ाइलें तैयार करें या बदलें।",
|
||||
"generation_failed": "कमिट संदेश बनाने में विफल",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "वर्तमान प्रोफ़ाइल का उपयोग करें",
|
||||
"select_profile": "कमिट संदेश बनाने के लिए एक API प्रोफ़ाइल चुनें"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/id/common.json
generated
10
src/i18n/locales/id/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Folder workspace tidak ditemukan. Buka folder untuk membuat pesan commit.",
|
||||
"generating": "Membuat pesan commit...",
|
||||
"no_changes": "Tidak ada perubahan terdeteksi. Siapkan atau ubah file sebelum membuat pesan commit.",
|
||||
"generation_failed": "Gagal membuat pesan commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Gunakan profil saat ini",
|
||||
"select_profile": "Pilih profil API untuk pembuatan pesan commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/it/common.json
generated
10
src/i18n/locales/it/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Nessuna cartella di lavoro trovata. Apri una cartella per generare un messaggio di commit.",
|
||||
"generating": "Generazione del messaggio di commit...",
|
||||
"no_changes": "Nessuna modifica rilevata. Prepara o modifica i file prima di generare un messaggio di commit.",
|
||||
"generation_failed": "Impossibile generare il messaggio di commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usa il profilo corrente",
|
||||
"select_profile": "Seleziona un profilo API per la generazione del messaggio di commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/ja/common.json
generated
10
src/i18n/locales/ja/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "ワークスペースフォルダーが見つかりません。コミットメッセージを生成するにはフォルダーを開いてください。",
|
||||
"generating": "コミットメッセージを生成中...",
|
||||
"no_changes": "変更が検出されませんでした。コミットメッセージを生成する前にファイルをステージするか変更してください。",
|
||||
"generation_failed": "コミットメッセージの生成に失敗しました",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "現在のプロファイルを使用",
|
||||
"select_profile": "コミットメッセージ生成用のAPIプロファイルを選択"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/ko/common.json
generated
10
src/i18n/locales/ko/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "워크스페이스 폴더를 찾을 수 없습니다. 커밋 메시지를 생성하려면 폴더를 열어주세요.",
|
||||
"generating": "커밋 메시지 생성 중...",
|
||||
"no_changes": "변경 사항이 없습니다. 커밋 메시지를 생성하기 전에 파일을 스테이지하거나 수정하세요.",
|
||||
"generation_failed": "커밋 메시지 생성에 실패했습니다",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "현재 프로필 사용",
|
||||
"select_profile": "커밋 메시지 생성에 사용할 API 프로필을 선택하세요"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/nl/common.json
generated
10
src/i18n/locales/nl/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Geen werkruimtemap gevonden. Open een map om een commitbericht te genereren.",
|
||||
"generating": "Commitbericht genereren...",
|
||||
"no_changes": "Geen wijzigingen gedetecteerd. Stage of wijzig bestanden voordat je een commitbericht genereert.",
|
||||
"generation_failed": "Kan commitbericht niet genereren",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Huidig profiel gebruiken",
|
||||
"select_profile": "Selecteer een API-profiel voor het genereren van commitberichten"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/pl/common.json
generated
10
src/i18n/locales/pl/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Nie znaleziono folderu roboczego. Otwórz folder, aby wygenerować wiadomość commita.",
|
||||
"generating": "Generowanie wiadomości commita...",
|
||||
"no_changes": "Nie wykryto zmian. Przygotuj lub zmodyfikuj pliki przed wygenerowaniem wiadomości commita.",
|
||||
"generation_failed": "Nie udało się wygenerować wiadomości commita",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Użyj bieżącego profilu",
|
||||
"select_profile": "Wybierz profil API do generowania wiadomości commita"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/pt-BR/common.json
generated
10
src/i18n/locales/pt-BR/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Nenhuma pasta de trabalho encontrada. Abra uma pasta para gerar uma mensagem de commit.",
|
||||
"generating": "Gerando mensagem de commit...",
|
||||
"no_changes": "Nenhuma alteração detectada. Prepare ou modifique arquivos antes de gerar uma mensagem de commit.",
|
||||
"generation_failed": "Falha ao gerar mensagem de commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Usar perfil atual",
|
||||
"select_profile": "Selecione um perfil de API para a geração de mensagens de commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/ru/common.json
generated
10
src/i18n/locales/ru/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Папка рабочего пространства не найдена. Откройте папку для генерации сообщения коммита.",
|
||||
"generating": "Генерация сообщения коммита...",
|
||||
"no_changes": "Изменения не обнаружены. Подготовьте или измените файлы перед генерацией сообщения коммита.",
|
||||
"generation_failed": "Не удалось сгенерировать сообщение коммита",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Использовать текущий профиль",
|
||||
"select_profile": "Выберите профиль API для генерации сообщения коммита"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/tr/common.json
generated
10
src/i18n/locales/tr/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Çalışma alanı klasörü bulunamadı. Commit mesajı oluşturmak için bir klasör aç.",
|
||||
"generating": "Commit mesajı oluşturuluyor...",
|
||||
"no_changes": "Değişiklik algılanmadı. Commit mesajı oluşturmadan önce dosyaları hazırla veya değiştir.",
|
||||
"generation_failed": "Commit mesajı oluşturulamadı",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Mevcut profili kullan",
|
||||
"select_profile": "Commit mesajı oluşturma için bir API profili seç"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/vi/common.json
generated
10
src/i18n/locales/vi/common.json
generated
|
|
@ -258,5 +258,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "Không tìm thấy thư mục làm việc. Mở một thư mục để tạo thông điệp commit.",
|
||||
"generating": "Đang tạo thông điệp commit...",
|
||||
"no_changes": "Không phát hiện thay đổi nào. Hãy chuẩn bị hoặc chỉnh sửa tệp trước khi tạo thông điệp commit.",
|
||||
"generation_failed": "Không thể tạo thông điệp commit",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "Sử dụng hồ sơ hiện tại",
|
||||
"select_profile": "Chọn một hồ sơ API để tạo thông điệp commit"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/zh-CN/common.json
generated
10
src/i18n/locales/zh-CN/common.json
generated
|
|
@ -256,5 +256,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "未找到工作区文件夹。请打开一个文件夹以生成提交信息。",
|
||||
"generating": "正在生成提交信息...",
|
||||
"no_changes": "未检测到更改。请在生成提交信息之前暂存或修改文件。",
|
||||
"generation_failed": "生成提交信息失败",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "使用当前配置",
|
||||
"select_profile": "选择用于生成提交信息的 API 配置"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
src/i18n/locales/zh-TW/common.json
generated
10
src/i18n/locales/zh-TW/common.json
generated
|
|
@ -251,5 +251,15 @@
|
|||
"docsLink": {
|
||||
"label": "Docs",
|
||||
"url": "https://docs.roocode.com"
|
||||
},
|
||||
"commit": {
|
||||
"no_workspace": "未找到工作區資料夾。請開啟一個資料夾以產生提交訊息。",
|
||||
"generating": "正在產生提交訊息...",
|
||||
"no_changes": "未偵測到變更。請在產生提交訊息之前暫存或修改檔案。",
|
||||
"generation_failed": "產生提交訊息失敗",
|
||||
"no_git_extension": "Git extension is not available.",
|
||||
"no_git_repo": "No git repository found.",
|
||||
"use_current_profile": "使用目前的設定檔",
|
||||
"select_profile": "選擇用於產生提交訊息的 API 設定檔"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,6 +169,12 @@
|
|||
"command": "roo-cline.toggleAutoApprove",
|
||||
"title": "%command.toggleAutoApprove.title%",
|
||||
"category": "%configuration.title%"
|
||||
},
|
||||
{
|
||||
"command": "roo-cline.generateCommitMessage",
|
||||
"title": "%command.generateCommitMessage.title%",
|
||||
"icon": "$(sparkle)",
|
||||
"category": "%configuration.title%"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
|
|
@ -275,6 +281,13 @@
|
|||
"group": "overflow@2",
|
||||
"when": "activeWebviewPanelId == roo-cline.TabPanelProvider"
|
||||
}
|
||||
],
|
||||
"scm/title": [
|
||||
{
|
||||
"command": "roo-cline.generateCommitMessage",
|
||||
"group": "navigation",
|
||||
"when": "scmProvider == git"
|
||||
}
|
||||
]
|
||||
},
|
||||
"keybindings": [
|
||||
|
|
|
|||
1
src/package.nls.ca.json
generated
1
src/package.nls.ca.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Explicar Aquesta Ordre",
|
||||
"command.acceptInput.title": "Acceptar Entrada/Suggeriment",
|
||||
"command.toggleAutoApprove.title": "Alternar Auto-Aprovació",
|
||||
"command.generateCommitMessage.title": "Generar Missatge de Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.de.json
generated
1
src/package.nls.de.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Diesen Befehl Erklären",
|
||||
"command.acceptInput.title": "Eingabe/Vorschlag Akzeptieren",
|
||||
"command.toggleAutoApprove.title": "Auto-Genehmigung Umschalten",
|
||||
"command.generateCommitMessage.title": "Commit-Nachricht Generieren",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.es.json
generated
1
src/package.nls.es.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Explicar Este Comando",
|
||||
"command.acceptInput.title": "Aceptar Entrada/Sugerencia",
|
||||
"command.toggleAutoApprove.title": "Alternar Auto-Aprobación",
|
||||
"command.generateCommitMessage.title": "Generar Mensaje de Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.fr.json
generated
1
src/package.nls.fr.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Expliquer cette Commande",
|
||||
"command.acceptInput.title": "Accepter l'Entrée/Suggestion",
|
||||
"command.toggleAutoApprove.title": "Basculer Auto-Approbation",
|
||||
"command.generateCommitMessage.title": "Générer un Message de Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.hi.json
generated
1
src/package.nls.hi.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "यह कमांड समझाएं",
|
||||
"command.acceptInput.title": "इनपुट/सुझाव स्वीकारें",
|
||||
"command.toggleAutoApprove.title": "ऑटो-अनुमोदन टॉगल करें",
|
||||
"command.generateCommitMessage.title": "कमिट संदेश बनाएं",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.id.json
generated
1
src/package.nls.id.json
generated
|
|
@ -25,6 +25,7 @@
|
|||
"command.terminal.explainCommand.title": "Jelaskan Perintah Ini",
|
||||
"command.acceptInput.title": "Terima Input/Saran",
|
||||
"command.toggleAutoApprove.title": "Alihkan Persetujuan Otomatis",
|
||||
"command.generateCommitMessage.title": "Buat Pesan Commit",
|
||||
"configuration.title": "Roo Code",
|
||||
"commands.allowedCommands.description": "Perintah yang dapat dijalankan secara otomatis ketika 'Selalu setujui operasi eksekusi' diaktifkan",
|
||||
"commands.deniedCommands.description": "Awalan perintah yang akan otomatis ditolak tanpa meminta persetujuan. Jika terjadi konflik dengan perintah yang diizinkan, pencocokan awalan terpanjang akan diprioritaskan. Tambahkan * untuk menolak semua perintah.",
|
||||
|
|
|
|||
1
src/package.nls.it.json
generated
1
src/package.nls.it.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Spiega Questo Comando",
|
||||
"command.acceptInput.title": "Accetta Input/Suggerimento",
|
||||
"command.toggleAutoApprove.title": "Attiva/Disattiva Auto-Approvazione",
|
||||
"command.generateCommitMessage.title": "Genera Messaggio di Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.ja.json
generated
1
src/package.nls.ja.json
generated
|
|
@ -25,6 +25,7 @@
|
|||
"command.terminal.explainCommand.title": "このコマンドを説明",
|
||||
"command.acceptInput.title": "入力/提案を承認",
|
||||
"command.toggleAutoApprove.title": "自動承認を切替",
|
||||
"command.generateCommitMessage.title": "コミットメッセージを生成",
|
||||
"configuration.title": "Roo Code",
|
||||
"commands.allowedCommands.description": "'常に実行操作を承認する'が有効な場合に自動実行できるコマンド",
|
||||
"commands.deniedCommands.description": "承認を求めずに自動的に拒否されるコマンドプレフィックス。許可されたコマンドとの競合がある場合、最長プレフィックスマッチが優先されます。すべてのコマンドを拒否するには * を追加してください。",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"command.terminal.explainCommand.title": "Explain This Command",
|
||||
"command.acceptInput.title": "Accept Input/Suggestion",
|
||||
"command.toggleAutoApprove.title": "Toggle Auto-Approve",
|
||||
"command.generateCommitMessage.title": "Generate Commit Message",
|
||||
"configuration.title": "Roo Code",
|
||||
"commands.allowedCommands.description": "Commands that can be auto-executed when 'Always approve execute operations' is enabled",
|
||||
"commands.deniedCommands.description": "Command prefixes that will be automatically denied without asking for approval. In case of conflicts with allowed commands, the longest prefix match takes precedence. Add * to deny all commands.",
|
||||
|
|
|
|||
1
src/package.nls.ko.json
generated
1
src/package.nls.ko.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "이 명령어 설명",
|
||||
"command.acceptInput.title": "입력/제안 수락",
|
||||
"command.toggleAutoApprove.title": "자동 승인 전환",
|
||||
"command.generateCommitMessage.title": "커밋 메시지 생성",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.nl.json
generated
1
src/package.nls.nl.json
generated
|
|
@ -25,6 +25,7 @@
|
|||
"command.terminal.explainCommand.title": "Leg Dit Commando Uit",
|
||||
"command.acceptInput.title": "Invoer/Suggestie Accepteren",
|
||||
"command.toggleAutoApprove.title": "Auto-Goedkeuring Schakelen",
|
||||
"command.generateCommitMessage.title": "Commitbericht Genereren",
|
||||
"configuration.title": "Roo Code",
|
||||
"commands.allowedCommands.description": "Commando's die automatisch kunnen worden uitgevoerd wanneer 'Altijd goedkeuren uitvoerbewerkingen' is ingeschakeld",
|
||||
"commands.deniedCommands.description": "Commando-prefixen die automatisch worden geweigerd zonder om goedkeuring te vragen. Bij conflicten met toegestane commando's heeft de langste prefix-match voorrang. Voeg * toe om alle commando's te weigeren.",
|
||||
|
|
|
|||
1
src/package.nls.pl.json
generated
1
src/package.nls.pl.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Wyjaśnij tę Komendę",
|
||||
"command.acceptInput.title": "Akceptuj Wprowadzanie/Sugestię",
|
||||
"command.toggleAutoApprove.title": "Przełącz Auto-Zatwierdzanie",
|
||||
"command.generateCommitMessage.title": "Generuj Wiadomość Commita",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.pt-BR.json
generated
1
src/package.nls.pt-BR.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Explicar Este Comando",
|
||||
"command.acceptInput.title": "Aceitar Entrada/Sugestão",
|
||||
"command.toggleAutoApprove.title": "Alternar Auto-Aprovação",
|
||||
"command.generateCommitMessage.title": "Gerar Mensagem de Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.ru.json
generated
1
src/package.nls.ru.json
generated
|
|
@ -25,6 +25,7 @@
|
|||
"command.terminal.explainCommand.title": "Объяснить эту команду",
|
||||
"command.acceptInput.title": "Принять ввод/предложение",
|
||||
"command.toggleAutoApprove.title": "Переключить Авто-Подтверждение",
|
||||
"command.generateCommitMessage.title": "Сгенерировать Сообщение Коммита",
|
||||
"configuration.title": "Roo Code",
|
||||
"commands.allowedCommands.description": "Команды, которые могут быть автоматически выполнены, когда включена опция 'Всегда подтверждать операции выполнения'",
|
||||
"commands.deniedCommands.description": "Префиксы команд, которые будут автоматически отклонены без запроса подтверждения. В случае конфликтов с разрешенными командами приоритет имеет самое длинное совпадение префикса. Добавьте * чтобы отклонить все команды.",
|
||||
|
|
|
|||
1
src/package.nls.tr.json
generated
1
src/package.nls.tr.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Bu Komutu Açıkla",
|
||||
"command.acceptInput.title": "Girişi/Öneriyi Kabul Et",
|
||||
"command.toggleAutoApprove.title": "Otomatik Onayı Değiştir",
|
||||
"command.generateCommitMessage.title": "Commit Mesajı Oluştur",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.vi.json
generated
1
src/package.nls.vi.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "Giải Thích Lệnh Này",
|
||||
"command.acceptInput.title": "Chấp Nhận Đầu Vào/Gợi Ý",
|
||||
"command.toggleAutoApprove.title": "Bật/Tắt Tự Động Phê Duyệt",
|
||||
"command.generateCommitMessage.title": "Tạo Thông Điệp Commit",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.zh-CN.json
generated
1
src/package.nls.zh-CN.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "解释此命令",
|
||||
"command.acceptInput.title": "接受输入/建议",
|
||||
"command.toggleAutoApprove.title": "切换自动批准",
|
||||
"command.generateCommitMessage.title": "生成提交信息",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
1
src/package.nls.zh-TW.json
generated
1
src/package.nls.zh-TW.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"command.terminal.explainCommand.title": "解釋此命令",
|
||||
"command.acceptInput.title": "接受輸入/建議",
|
||||
"command.toggleAutoApprove.title": "切換自動批准",
|
||||
"command.generateCommitMessage.title": "產生提交訊息",
|
||||
"views.activitybar.title": "Roo Code",
|
||||
"views.contextMenu.label": "Roo Code",
|
||||
"views.terminalMenu.label": "Roo Code",
|
||||
|
|
|
|||
157
src/utils/__tests__/commit-message-generator.spec.ts
Normal file
157
src/utils/__tests__/commit-message-generator.spec.ts
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import { generateCommitMessageFromDiff, getGitDiff } from "../commit-message-generator"
|
||||
import * as singleCompletionHandlerModule from "../single-completion-handler"
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
vi.mock("../single-completion-handler")
|
||||
|
||||
// Mock child_process.exec to simulate git commands
|
||||
const mockExecImpl = vi.fn()
|
||||
|
||||
vi.mock("child_process", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("child_process")>()
|
||||
return {
|
||||
...actual,
|
||||
exec: (...args: any[]) => mockExecImpl(...args),
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock("util", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("util")>()
|
||||
return {
|
||||
...actual,
|
||||
promisify:
|
||||
(fn: any) =>
|
||||
(...args: any[]) =>
|
||||
new Promise((resolve, reject) => {
|
||||
fn(...args, (err: Error | null, result: any) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(result)
|
||||
}
|
||||
})
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
describe("commit-message-generator", () => {
|
||||
let mockSingleCompletionHandler: ReturnType<typeof vi.fn>
|
||||
|
||||
const mockApiConfig: ProviderSettings = {
|
||||
apiProvider: "anthropic",
|
||||
apiKey: "test-key",
|
||||
apiModelId: "claude-sonnet-4-20250514",
|
||||
} as ProviderSettings
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockSingleCompletionHandler = vi.fn().mockResolvedValue("feat: add user authentication")
|
||||
vi.mocked(singleCompletionHandlerModule).singleCompletionHandler = mockSingleCompletionHandler
|
||||
})
|
||||
|
||||
describe("generateCommitMessageFromDiff", () => {
|
||||
it("generates a commit message from a diff", async () => {
|
||||
const diff = `diff --git a/src/auth.ts b/src/auth.ts
|
||||
+export function login(user: string) {
|
||||
+ return true
|
||||
+}`
|
||||
|
||||
const result = await generateCommitMessageFromDiff(mockApiConfig, diff)
|
||||
|
||||
expect(result).toBe("feat: add user authentication")
|
||||
expect(mockSingleCompletionHandler).toHaveBeenCalledWith(mockApiConfig, expect.stringContaining(diff))
|
||||
})
|
||||
|
||||
it("truncates very large diffs", async () => {
|
||||
const largeDiff = "a".repeat(15000)
|
||||
|
||||
await generateCommitMessageFromDiff(mockApiConfig, largeDiff)
|
||||
|
||||
const calledPrompt = mockSingleCompletionHandler.mock.calls[0][1]
|
||||
expect(calledPrompt).toContain("...(truncated)")
|
||||
})
|
||||
|
||||
it("strips markdown code blocks from the result", async () => {
|
||||
mockSingleCompletionHandler.mockResolvedValue("```\nfeat: add feature\n```")
|
||||
|
||||
const result = await generateCommitMessageFromDiff(mockApiConfig, "some diff")
|
||||
|
||||
expect(result).toBe("feat: add feature")
|
||||
})
|
||||
|
||||
it("strips language-tagged markdown code blocks from the result", async () => {
|
||||
mockSingleCompletionHandler.mockResolvedValue("```text\nfix: resolve null check\n```")
|
||||
|
||||
const result = await generateCommitMessageFromDiff(mockApiConfig, "some diff")
|
||||
|
||||
expect(result).toBe("fix: resolve null check")
|
||||
})
|
||||
|
||||
it("propagates errors from the completion handler", async () => {
|
||||
mockSingleCompletionHandler.mockRejectedValue(new Error("API Error"))
|
||||
|
||||
await expect(generateCommitMessageFromDiff(mockApiConfig, "some diff")).rejects.toThrow("API Error")
|
||||
})
|
||||
})
|
||||
|
||||
describe("getGitDiff", () => {
|
||||
it("returns staged diff when available", async () => {
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
cmd: string,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
if (cmd.includes("--cached")) {
|
||||
callback(null, { stdout: "staged changes", stderr: "" })
|
||||
} else {
|
||||
callback(null, { stdout: "unstaged changes", stderr: "" })
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const result = await getGitDiff("/workspace")
|
||||
|
||||
expect(result).toBe("staged changes")
|
||||
expect(mockExecImpl).toHaveBeenCalledWith(
|
||||
"git diff --cached --no-color",
|
||||
expect.objectContaining({ cwd: "/workspace" }),
|
||||
expect.any(Function),
|
||||
)
|
||||
})
|
||||
|
||||
it("falls back to unstaged diff when nothing is staged", async () => {
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
cmd: string,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
if (cmd.includes("--cached")) {
|
||||
callback(null, { stdout: "", stderr: "" })
|
||||
} else {
|
||||
callback(null, { stdout: "unstaged changes", stderr: "" })
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const result = await getGitDiff("/workspace")
|
||||
|
||||
expect(result).toBe("unstaged changes")
|
||||
})
|
||||
|
||||
it("throws an error when git command fails", async () => {
|
||||
mockExecImpl.mockImplementation(
|
||||
(
|
||||
_cmd: string,
|
||||
_opts: unknown,
|
||||
callback: (err: Error | null, result: { stdout: string; stderr: string }) => void,
|
||||
) => {
|
||||
callback(new Error("git not found"), { stdout: "", stderr: "" })
|
||||
},
|
||||
)
|
||||
|
||||
await expect(getGitDiff("/workspace")).rejects.toThrow("Failed to get git diff")
|
||||
})
|
||||
})
|
||||
})
|
||||
107
src/utils/commit-message-generator.ts
Normal file
107
src/utils/commit-message-generator.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import * as vscode from "vscode"
|
||||
import { exec } from "child_process"
|
||||
import { promisify } from "util"
|
||||
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
import { singleCompletionHandler } from "./single-completion-handler"
|
||||
import { t } from "../i18n"
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
const MAX_DIFF_LENGTH = 10000
|
||||
|
||||
const COMMIT_MESSAGE_PROMPT = `You are a commit message generator. Given the following git diff, generate a concise and descriptive commit message following the Conventional Commits format.
|
||||
|
||||
Rules:
|
||||
- Use one of these types: feat, fix, refactor, docs, style, test, chore, perf, ci, build
|
||||
- The first line should be the type, optional scope in parentheses, and a short description (max 72 chars)
|
||||
- If the changes are complex, add a blank line followed by a more detailed description
|
||||
- Focus on WHAT changed and WHY, not HOW
|
||||
- Do NOT include any markdown formatting, code blocks, or extra explanation
|
||||
- Output ONLY the commit message text
|
||||
|
||||
Git diff:
|
||||
`
|
||||
|
||||
/**
|
||||
* Gets the staged diff from the git repository. Falls back to unstaged diff
|
||||
* if nothing is staged.
|
||||
*/
|
||||
export async function getGitDiff(workspaceRoot: string): Promise<string> {
|
||||
try {
|
||||
// Try staged changes first
|
||||
const { stdout: stagedDiff } = await execAsync("git diff --cached --no-color", {
|
||||
cwd: workspaceRoot,
|
||||
maxBuffer: 1024 * 1024,
|
||||
})
|
||||
|
||||
if (stagedDiff.trim()) {
|
||||
return stagedDiff
|
||||
}
|
||||
|
||||
// Fall back to unstaged changes
|
||||
const { stdout: unstagedDiff } = await execAsync("git diff --no-color", {
|
||||
cwd: workspaceRoot,
|
||||
maxBuffer: 1024 * 1024,
|
||||
})
|
||||
|
||||
return unstagedDiff
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to get git diff: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a commit message from the given diff using the AI provider.
|
||||
*/
|
||||
export async function generateCommitMessageFromDiff(apiConfiguration: ProviderSettings, diff: string): Promise<string> {
|
||||
// Truncate very large diffs to avoid token limits
|
||||
const truncatedDiff = diff.length > MAX_DIFF_LENGTH ? diff.substring(0, MAX_DIFF_LENGTH) + "\n...(truncated)" : diff
|
||||
|
||||
const prompt = COMMIT_MESSAGE_PROMPT + truncatedDiff
|
||||
|
||||
const result = await singleCompletionHandler(apiConfiguration, prompt)
|
||||
|
||||
// Clean up the result - remove any markdown formatting the model might add
|
||||
return result
|
||||
.replace(/^```[^\n]*\n/, "")
|
||||
.replace(/\n```$/, "")
|
||||
.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the workspace root for git operations.
|
||||
*/
|
||||
export function getWorkspaceRoot(): string | undefined {
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders
|
||||
if (!workspaceFolders || workspaceFolders.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return workspaceFolders[0].uri.fsPath
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the SCM input box value with the generated commit message.
|
||||
*/
|
||||
export async function setScmInputBoxMessage(message: string): Promise<boolean> {
|
||||
const gitExtension = vscode.extensions.getExtension("vscode.git")
|
||||
|
||||
if (!gitExtension) {
|
||||
vscode.window.showErrorMessage(t("common:commit.no_git_extension"))
|
||||
return false
|
||||
}
|
||||
|
||||
const git = gitExtension.isActive ? gitExtension.exports : await gitExtension.activate()
|
||||
const api = git.getAPI(1)
|
||||
|
||||
if (!api || api.repositories.length === 0) {
|
||||
vscode.window.showErrorMessage(t("common:commit.no_git_repo"))
|
||||
return false
|
||||
}
|
||||
|
||||
// Use the first repository (or the one matching the workspace)
|
||||
const repo = api.repositories[0]
|
||||
repo.inputBox.value = message
|
||||
return true
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue