diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a30550dce1..639a0b375c 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -131,6 +131,14 @@ export const globalSettingsSchema = z.object({ hasOpenedModeSelector: z.boolean().optional(), lastModeExportPath: z.string().optional(), lastModeImportPath: z.string().optional(), + rulesSettings: z + .object({ + selectedRuleTypes: z.array(z.string()), + addToGitignore: z.boolean(), + includeCustomRules: z.boolean(), + customRulesText: z.string(), + }) + .optional(), }) export type GlobalSettings = z.infer diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index fc9bac4f93..6b50c524dd 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1976,6 +1976,27 @@ export const webviewMessageHandler = async ( // Silently fail - not critical } break + case "updateRulesSettings": + // Save rules settings to global state + await updateGlobalState("rulesSettings", { + selectedRuleTypes: message.selectedRuleTypes || ["general", "code"], + addToGitignore: message.addToGitignore !== undefined ? message.addToGitignore : true, + includeCustomRules: message.includeCustomRules || false, + customRulesText: message.customRulesText || "", + }) + break + case "getRulesSettings": + // Send current rules settings to webview + const rulesSettings = getGlobalState("rulesSettings") || { + selectedRuleTypes: ["general", "code"], + addToGitignore: true, + } + await provider.postMessageToWebview({ + type: "rulesSettings", + selectedRuleTypes: rulesSettings.selectedRuleTypes, + addToGitignore: rulesSettings.addToGitignore, + }) + break case "humanRelayResponse": if (message.requestId && message.text) { vscode.commands.executeCommand(getCommand("handleHumanRelayResponse"), { diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 8fe3323941..210f1fac43 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -109,10 +109,13 @@ export interface ExtensionMessage { | "existingRuleFiles" | "showDeleteMessageDialog" | "showEditMessageDialog" + | "rulesSettings" text?: string payload?: any // Add a generic payload for now, can refine later files?: string[] // For existingRuleFiles sourceFileCount?: number // For existingRuleFiles to show warning for small repos + selectedRuleTypes?: string[] // For rulesSettings + addToGitignore?: boolean // For rulesSettings action?: | "chatButtonClicked" | "mcpButtonClicked" @@ -195,6 +198,7 @@ export type ExtensionState = Pick< | "allowedMaxRequests" | "browserToolEnabled" | "browserViewportSize" + | "rulesSettings" | "screenshotQuality" | "remoteBrowserEnabled" | "remoteBrowserHost" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 8921a26003..0feeac6af4 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -200,6 +200,8 @@ export interface WebviewMessage { | "requestCodeIndexSecretStatus" | "generateRules" | "checkExistingRuleFiles" + | "updateRulesSettings" + | "getRulesSettings" text?: string editedMessageContent?: string tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account" diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 6c541353eb..3e41e4d080 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -102,6 +102,7 @@ const ChatTextArea = forwardRef( const [fileSearchResults, setFileSearchResults] = useState([]) const [searchLoading, setSearchLoading] = useState(false) const [searchRequestId, setSearchRequestId] = useState("") + const [waitingForRulesSettings, setWaitingForRulesSettings] = useState(false) // Close dropdown when clicking outside. useEffect(() => { @@ -158,12 +159,26 @@ const ChatTextArea = forwardRef( if (message.requestId === searchRequestId) { setFileSearchResults(message.results || []) } + } else if (message.type === "rulesSettings") { + // Only trigger generation if we're waiting for it (user used /make-rules command) + if (waitingForRulesSettings) { + setWaitingForRulesSettings(false) + // Received rules settings, now trigger generation + vscode.postMessage({ + type: "generateRules", + selectedRuleTypes: message.selectedRuleTypes || ["general", "code"], + addToGitignore: message.addToGitignore !== undefined ? message.addToGitignore : true, + alwaysAllowWriteProtected: false, + includeCustomRules: message.includeCustomRules || false, + customRulesText: message.customRulesText || "", + }) + } } } window.addEventListener("message", messageHandler) return () => window.removeEventListener("message", messageHandler) - }, [setInputValue, searchRequestId]) + }, [setInputValue, searchRequestId, waitingForRulesSettings]) const [isDraggingOver, setIsDraggingOver] = useState(false) const [textAreaBaseHeight, setTextAreaBaseHeight] = useState(undefined) @@ -273,6 +288,18 @@ const ChatTextArea = forwardRef( return } + if (type === ContextMenuOptionType.Rules) { + // Handle rules generation command + setInputValue("") + setShowContextMenu(false) + // Set flag to indicate we're waiting for settings + setWaitingForRulesSettings(true) + // First get the saved settings + vscode.postMessage({ type: "getRulesSettings" }) + // The actual generation will be triggered when we receive the settings + return + } + if ( type === ContextMenuOptionType.File || type === ContextMenuOptionType.Folder || diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 049fb52f44..3de36bfdd4 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -8,7 +8,7 @@ import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments" import { useAppTranslation } from "@src/i18n/TranslationContext" import { cn } from "@src/lib/utils" -import { SetExperimentEnabled } from "./types" +import { SetExperimentEnabled, SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { ExperimentalFeature } from "./ExperimentalFeature" @@ -18,12 +18,21 @@ type ExperimentalSettingsProps = HTMLAttributes & { experiments: Experiments setExperimentEnabled: SetExperimentEnabled hasUnsavedChanges?: boolean + rulesSettings?: { + selectedRuleTypes: string[] + addToGitignore: boolean + includeCustomRules: boolean + customRulesText: string + } + setCachedStateField: SetCachedStateField<"rulesSettings"> } export const ExperimentalSettings = ({ experiments, setExperimentEnabled, hasUnsavedChanges, + rulesSettings, + setCachedStateField, className, ...props }: ExperimentalSettingsProps) => { @@ -70,7 +79,7 @@ export const ExperimentalSettings = ({ })} - + ) } diff --git a/webview-ui/src/components/settings/RulesSettings.tsx b/webview-ui/src/components/settings/RulesSettings.tsx index d3224b79ee..4d5ccefa95 100644 --- a/webview-ui/src/components/settings/RulesSettings.tsx +++ b/webview-ui/src/components/settings/RulesSettings.tsx @@ -1,18 +1,22 @@ import { HTMLAttributes, useState, useEffect } from "react" +import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { useAppTranslation } from "@/i18n/TranslationContext" -import { FileText, AlertTriangle, Info, Sparkles } from "lucide-react" -import { Button } from "@/components/ui/button" +import { FileText, AlertTriangle, Terminal } from "lucide-react" import { vscode } from "@/utils/vscode" import { cn } from "@/lib/utils" -import { useExtensionState } from "@/context/ExtensionStateContext" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@/components/ui" -import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" +import { SetCachedStateField } from "./types" type RulesSettingsProps = HTMLAttributes & { - hasUnsavedChanges?: boolean + rulesSettings?: { + selectedRuleTypes: string[] + addToGitignore: boolean + includeCustomRules: boolean + customRulesText: string + } + setCachedStateField: SetCachedStateField<"rulesSettings"> } interface RuleType { @@ -23,73 +27,127 @@ interface RuleType { exists?: boolean } -export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesSettingsProps) => { +export const RulesSettings = ({ rulesSettings, setCachedStateField, className, ...props }: RulesSettingsProps) => { const { t } = useAppTranslation() - const [addToGitignore, setAddToGitignore] = useState(true) - const [alwaysAllowWriteProtected, setAlwaysAllowWriteProtected] = useState(true) - const [selectedApiConfig, setSelectedApiConfig] = useState("") - const [includeCustomRules, setIncludeCustomRules] = useState(false) - const [customRulesText, setCustomRulesText] = useState("") const [sourceFileCount, setSourceFileCount] = useState(null) - const { listApiConfigMeta, currentApiConfigName } = useExtensionState() - - const [ruleTypes, setRuleTypes] = useState([ + const allRuleTypes = [ { id: "general", label: t("settings:rules.types.general.label"), description: t("settings:rules.types.general.description"), - checked: true, - exists: false, }, { id: "code", label: t("settings:rules.types.code.label"), description: t("settings:rules.types.code.description"), - checked: true, - exists: false, }, { id: "architect", label: t("settings:rules.types.architect.label"), description: t("settings:rules.types.architect.description"), - checked: true, - exists: false, }, { id: "debug", label: t("settings:rules.types.debug.label"), description: t("settings:rules.types.debug.description"), - checked: true, - exists: false, }, { id: "docs-extractor", label: t("settings:rules.types.docsExtractor.label"), description: t("settings:rules.types.docsExtractor.description"), - checked: true, - exists: false, }, - ]) + ] + + const [ruleTypes, setRuleTypes] = useState( + allRuleTypes.map((ruleType) => ({ + ...ruleType, + checked: rulesSettings?.selectedRuleTypes.includes(ruleType.id) ?? true, + exists: false, + })), + ) + + // Update rule types when rulesSettings prop changes + useEffect(() => { + if (rulesSettings) { + setRuleTypes((prev) => + prev.map((ruleType) => ({ + ...ruleType, + checked: rulesSettings.selectedRuleTypes.includes(ruleType.id), + })), + ) + } + }, [rulesSettings]) const handleRuleTypeToggle = (id: string) => { setRuleTypes((prev) => prev.map((rule) => (rule.id === id ? { ...rule, checked: !rule.checked } : rule))) + + // Update the cached state using the proper pattern + const updatedRules = ruleTypes.map((rule) => (rule.id === id ? { ...rule, checked: !rule.checked } : rule)) + const selectedRuleTypes = updatedRules.filter((rule) => rule.checked).map((rule) => rule.id) + + setCachedStateField("rulesSettings", { + selectedRuleTypes, + addToGitignore: rulesSettings?.addToGitignore ?? true, + includeCustomRules: rulesSettings?.includeCustomRules ?? false, + customRulesText: rulesSettings?.customRulesText ?? "", + }) } - // Check for existing files and get current settings when component mounts + const handleGitignoreToggle = (checked: boolean) => { + setCachedStateField("rulesSettings", { + selectedRuleTypes: rulesSettings?.selectedRuleTypes ?? [ + "general", + "code", + "architect", + "debug", + "docs-extractor", + ], + addToGitignore: checked, + includeCustomRules: rulesSettings?.includeCustomRules ?? false, + customRulesText: rulesSettings?.customRulesText ?? "", + }) + } + + const handleIncludeCustomRulesToggle = (checked: boolean) => { + setCachedStateField("rulesSettings", { + selectedRuleTypes: rulesSettings?.selectedRuleTypes ?? [ + "general", + "code", + "architect", + "debug", + "docs-extractor", + ], + addToGitignore: rulesSettings?.addToGitignore ?? true, + includeCustomRules: checked, + customRulesText: rulesSettings?.customRulesText ?? "", + }) + } + + const handleCustomRulesTextChange = (text: string) => { + setCachedStateField("rulesSettings", { + selectedRuleTypes: rulesSettings?.selectedRuleTypes ?? [ + "general", + "code", + "architect", + "debug", + "docs-extractor", + ], + addToGitignore: rulesSettings?.addToGitignore ?? true, + includeCustomRules: rulesSettings?.includeCustomRules ?? false, + customRulesText: text, + }) + } + + // Check for existing files when component mounts useEffect(() => { vscode.postMessage({ type: "checkExistingRuleFiles", }) - // Request current state to get alwaysAllowWriteProtected value - vscode.postMessage({ type: "webviewDidLaunch" }) - - // Set default API config - if (currentApiConfigName && !selectedApiConfig) { - setSelectedApiConfig(currentApiConfigName) - } - }, [currentApiConfigName, selectedApiConfig]) + // Request current rules settings + vscode.postMessage({ type: "getRulesSettings" }) + }, []) useEffect(() => { const handleMessage = (event: MessageEvent) => { @@ -106,11 +164,9 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS if (message.sourceFileCount !== undefined) { setSourceFileCount(message.sourceFileCount) } - } else if (message.type === "state") { - // Update alwaysAllowWriteProtected from the extension state - if (message.state?.alwaysAllowWriteProtected !== undefined) { - setAlwaysAllowWriteProtected(message.state.alwaysAllowWriteProtected) - } + } else if (message.type === "rulesSettings") { + // Update settings from saved preferences - this is now handled by props + // The component will re-render when rulesSettings prop changes } } @@ -118,27 +174,8 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS return () => window.removeEventListener("message", handleMessage) }, []) - const handleGenerateRules = () => { - const selectedRules = ruleTypes.filter((rule) => rule.checked) - if (selectedRules.length === 0) { - return - } - - // Send message to extension to generate rules - vscode.postMessage({ - type: "generateRules", - selectedRuleTypes: selectedRules.map((rule) => rule.id), - addToGitignore, - alwaysAllowWriteProtected, - apiConfigName: selectedApiConfig, - includeCustomRules, - customRulesText: includeCustomRules ? customRulesText : "", - }) - } - const existingRules = ruleTypes.filter((rule) => rule.checked && rule.exists) const hasExistingFiles = existingRules.length > 0 - const hasSelectedRules = ruleTypes.some((rule) => rule.checked) return (
@@ -150,198 +187,139 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS
-
- {/* Magic Rules Generation subsection */} -
-
-
- -
{t("settings:rules.magicGeneration.title")}
+
+ {/* Command Line Instructions */} +
+ +
+
+ {t("settings:rules.commandTitle")}{" "} + + /make-rules +
- {t("settings:rules.magicGeneration.description")} -
-
-
- {/* Recommendation box */} -
- -
- {t("settings:rules.autoApproveRecommendation")} -
-
- -
-

{t("settings:rules.selectTypes")}

-
- {ruleTypes.map((ruleType) => ( -
handleRuleTypeToggle(ruleType.id)} - className={cn( - "relative p-3 rounded-md border cursor-pointer transition-all", - "hover:border-vscode-focusBorder", - ruleType.checked - ? "bg-vscode-list-activeSelectionBackground border-vscode-focusBorder" - : "bg-vscode-editor-background border-vscode-panel-border", - )}> -
-
- {ruleType.label} - {ruleType.exists && ( - - • - - )} -
-
- {ruleType.description} -
-
-
- ))} -
-
- - {/* Small repository warning */} - {sourceFileCount !== null && sourceFileCount > 0 && sourceFileCount < 20 && ( -
- -
- {t("settings:rules.smallRepoWarning", { count: sourceFileCount })} -
-
- )} - {hasExistingFiles && ( -
- -
-
{t("settings:rules.overwriteWarning")}
-
    - {existingRules.map((rule) => ( -
  • {rule.label}
  • - ))} -
-
-
- )} - -
- - - - - - - {includeCustomRules && ( -
- { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value || - ((e as any).target as HTMLTextAreaElement).value - setCustomRulesText(value) - }} - placeholder={t("settings:rules.customRulesPlaceholder")} - rows={6} - className="w-full" - /> -
- {t("settings:rules.customRulesHint")} -
-
- )} -
- -
- - - - - - - + {t("settings:rules.commandDescription")}
+ + {/* Settings Content */} +
+ {/* Add to .gitignore option */} + + + {/* Rule Type Selection */} +
+

{t("settings:rules.selectTypes")}

+
+ {ruleTypes.map((ruleType) => ( +
handleRuleTypeToggle(ruleType.id)} + className={cn( + "relative p-3 rounded-md border cursor-pointer transition-all", + "hover:border-vscode-focusBorder", + ruleType.checked + ? "bg-vscode-list-activeSelectionBackground border-vscode-focusBorder" + : "bg-vscode-editor-background border-vscode-panel-border", + )}> +
+
+ {ruleType.label} + {ruleType.exists && ( + + • + + )} +
+
+ {ruleType.description} +
+
+
+ ))} +
+
+ + {/* Custom Rules Section */} +
+ + + {rulesSettings?.includeCustomRules && ( +
+ + { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value || + ((e as any).target as HTMLTextAreaElement).value + handleCustomRulesTextChange(value) + }} + placeholder={t("settings:rules.customRulesPlaceholder")} + rows={6} + className="w-full" + /> +
+ {t("settings:rules.customRulesHint")} +
+
+ )} +
+
+ + {/* Small repository warning */} + {sourceFileCount !== null && sourceFileCount > 0 && sourceFileCount < 20 && ( +
+ +
+ {t("settings:rules.smallRepoWarning", { count: sourceFileCount })} +
+
+ )} + + {/* Existing files warning */} + {hasExistingFiles && ( +
+ +
+
{t("settings:rules.overwriteWarning")}
+
    + {existingRules.map((rule) => ( +
  • {rule.label}
  • + ))} +
+
+
+ )}
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index d5ab060fc7..1ba1197cf4 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -177,6 +177,7 @@ const SettingsView = forwardRef(({ onDone, t alwaysAllowFollowupQuestions, alwaysAllowUpdateTodoList, followupAutoApproveTimeoutMs, + rulesSettings, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -702,6 +703,8 @@ const SettingsView = forwardRef(({ onDone, t setExperimentEnabled={setExperimentEnabled} experiments={experiments} hasUnsavedChanges={isChangeDetected} + rulesSettings={rulesSettings} + setCachedStateField={setCachedStateField} /> )} diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index c970733fba..8fa465088a 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -229,6 +229,12 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }, codebaseIndexModels: { ollama: {}, openai: {} }, alwaysAllowUpdateTodoList: true, + rulesSettings: { + selectedRuleTypes: ["general", "code", "architect", "debug", "docs-extractor"], + addToGitignore: true, + includeCustomRules: false, + customRulesText: "", + }, }) const [didHydrateState, setDidHydrateState] = useState(false) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 9c7267aa80..1a39e297cf 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Afegir automàticament els arxius de regles generats a .gitignore per evitar que es facin commit al control de versions", "autoApproveProtected": "Aprovar automàticament l'escriptura d'arxius protegits", "autoApproveProtectedDescription": "Permetre que Roo creï i escrigui al directori .roo sense requerir aprovació manual", - "autoApproveRecommendation": "Per a la millor experiència de generació de regles, recomanem habilitar l'aprovació automàtica tant per a operacions de lectura com d'escriptura a la configuració d'Aprovació Automàtica de dalt.", "selectApiConfig": "Seleccionar configuració d'API", "includeCustomRules": "Incloure les meves regles personalitzades", "includeCustomRulesDescription": "Afegir instruccions personalitzades al prompt de generació de regles", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 60f69f21db..ec8a4c189e 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Füge die generierten Regeldateien automatisch zu .gitignore hinzu, um zu verhindern, dass sie in die Versionskontrolle übernommen werden", "autoApproveProtected": "Geschützte Dateischreibvorgänge automatisch genehmigen", "autoApproveProtectedDescription": "Erlaube Roo, das .roo-Verzeichnis zu erstellen und zu beschreiben, ohne manuelle Genehmigung zu benötigen", - "autoApproveRecommendation": "Für die beste Erfahrung beim Generieren von Regeln empfehlen wir, die automatische Genehmigung sowohl für Lese- als auch für Schreibvorgänge in den Auto-Approve-Einstellungen oben zu aktivieren.", "selectApiConfig": "API-Konfiguration auswählen", "includeCustomRules": "Meine eigenen Regeln einbeziehen", "includeCustomRulesDescription": "Füge benutzerdefinierte Anweisungen zur Regelgenerierungsaufforderung hinzu", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index af3ca3e15d..4b30621fa5 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -653,13 +653,14 @@ "addToGitignoreDescription": "Automatically add the generated rule files to .gitignore to prevent them from being committed to version control", "autoApproveProtected": "Auto-approve protected file writes", "autoApproveProtectedDescription": "Allow Roo to make and write to .roo directory without requiring manual approval", - "autoApproveRecommendation": "For the best experience generating rules, we recommend enabling auto-approve for both read and write operations in the Auto-Approve settings above.", "selectApiConfig": "Select API configuration", "includeCustomRules": "Include my own rules", "includeCustomRulesDescription": "Add custom instructions to the rule generation prompt", "customRulesPlaceholder": "Enter your custom rules or instructions here...", "customRulesHint": "These will be included in the prompt when generating rules for your project", - "smallRepoWarning": "This repository contains only {{count}} files. With limited code examples, the generated rules may be overly specific to your current implementation. Consider regenerating rules as your project grows." + "smallRepoWarning": "This repository contains only {{count}} files. With limited code examples, the generated rules may be overly specific to your current implementation. Consider regenerating rules as your project grows.", + "commandTitle": "Initialize rules using the chat command:", + "commandDescription": "Type this command in the chat to generate AI rules based on your selected preferences below." }, "promptCaching": { "label": "Disable prompt caching", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 3a123fe97e..2cdf97633d 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Añadir automáticamente los archivos de reglas generados a .gitignore para evitar que se confirmen en el control de versiones", "autoApproveProtected": "Aprobar automáticamente escrituras de archivos protegidos", "autoApproveProtectedDescription": "Permitir a Roo crear y escribir en el directorio .roo sin requerir aprobación manual", - "autoApproveRecommendation": "Para la mejor experiencia generando reglas, recomendamos habilitar la aprobación automática tanto para operaciones de lectura como de escritura en la configuración de Aprobación Automática arriba.", "selectApiConfig": "Seleccionar configuración de API", "includeCustomRules": "Incluir mis propias reglas", "includeCustomRulesDescription": "Añadir instrucciones personalizadas al prompt de generación de reglas", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 1d9f8d0bda..4cdbf440bd 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Ajouter automatiquement les fichiers de règles générés à .gitignore pour éviter qu'ils soient validés dans le contrôle de version", "autoApproveProtected": "Approuver automatiquement les écritures de fichiers protégés", "autoApproveProtectedDescription": "Permettre à Roo de créer et d'écrire dans le répertoire .roo sans nécessiter d'approbation manuelle", - "autoApproveRecommendation": "Pour la meilleure expérience de génération de règles, nous recommandons d'activer l'approbation automatique pour les opérations de lecture et d'écriture dans les paramètres d'approbation automatique ci-dessus.", "selectApiConfig": "Sélectionner la configuration API", "includeCustomRules": "Inclure mes propres règles", "includeCustomRulesDescription": "Ajouter des instructions personnalisées à l'invite de génération de règles", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 74b6ccae04..c001590b59 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "जेनेरेटेड रूल फाइलों को ऑटोमेटिकली .gitignore में ऐड करो ताकि वे वर्जन कंट्रोल में कमिट न हों", "autoApproveProtected": "प्रोटेक्टेड फाइल राइटिंग को ऑटो अप्रूव करो", "autoApproveProtectedDescription": "Roo को मैन्युअल अप्रूवल की जरूरत के बिना .roo डायरेक्टरी में क्रिएट और राइट करने की अनुमति दो", - "autoApproveRecommendation": "बेस्ट रूल जेनेरेशन एक्सपीरियंस के लिए, हम ऊपर ऑटो अप्रूवल सेटिंग्स में रीड और राइट दोनों ऑपरेशन्स के लिए ऑटो अप्रूवल इनेबल करने की सिफारिश करते हैं।", "selectApiConfig": "API कॉन्फ़िगरेशन सेलेक्ट करो", "includeCustomRules": "मेरे कस्टम रूल्स इंक्लूड करो", "includeCustomRulesDescription": "रूल जेनेरेशन प्रॉम्प्ट में कस्टम इंस्ट्रक्शन्स ऐड करो", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 75cb66a71f..33e0f49c2b 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -623,7 +623,6 @@ "addToGitignoreDescription": "Secara otomatis menambahkan file aturan yang dihasilkan ke .gitignore untuk mencegah mereka di-commit ke version control", "autoApproveProtected": "Otomatis setujui penulisan file yang dilindungi", "autoApproveProtectedDescription": "Izinkan Roo untuk membuat dan menulis ke direktori .roo tanpa memerlukan persetujuan manual", - "autoApproveRecommendation": "Untuk pengalaman generasi aturan terbaik, kami merekomendasikan mengaktifkan persetujuan otomatis untuk operasi baca dan tulis di pengaturan Auto Approval di atas.", "selectApiConfig": "Pilih Konfigurasi API", "includeCustomRules": "Sertakan aturan kustom saya", "includeCustomRulesDescription": "Tambahkan instruksi kustom ke prompt generasi aturan", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 64a8a3bc9b..dd8a6f30c9 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Aggiungi automaticamente i file di regole generati a .gitignore per evitare che vengano committati nel controllo versione", "autoApproveProtected": "Approva automaticamente scritture file protetti", "autoApproveProtectedDescription": "Consenti a Roo di creare e scrivere nella directory .roo senza richiedere approvazione manuale", - "autoApproveRecommendation": "Per la migliore esperienza nella generazione di regole, raccomandiamo di abilitare l'approvazione automatica sia per le operazioni di lettura che di scrittura nelle impostazioni di Approvazione Automatica sopra.", "selectApiConfig": "Seleziona configurazione API", "includeCustomRules": "Includi le mie regole", "includeCustomRulesDescription": "Aggiungi istruzioni personalizzate al prompt di generazione regole", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 59bd4f15fd..fdf6f5d40a 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "生成されたルールファイルを自動的に.gitignoreに追加して、バージョン管理にコミットされないようにします", "autoApproveProtected": "保護されたファイル書き込みを自動承認", "autoApproveProtectedDescription": "Rooが手動承認を必要とせずに.rooディレクトリを作成・書き込みできるようにします", - "autoApproveRecommendation": "ルール生成の最良の体験のために、上記の自動承認設定で読み取りと書き込み操作の両方の自動承認を有効にすることをお勧めします。", "selectApiConfig": "API設定を選択", "includeCustomRules": "独自のルールを含める", "includeCustomRulesDescription": "ルール生成プロンプトにカスタム指示を追加", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 8ba19a0b5d..7a2f184fd8 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "생성된 규칙 파일을 자동으로 .gitignore에 추가하여 버전 관리에 커밋되지 않도록 합니다", "autoApproveProtected": "보호된 파일 쓰기 자동 승인", "autoApproveProtectedDescription": "Roo가 수동 승인 없이 .roo 디렉토리를 생성하고 쓸 수 있도록 허용", - "autoApproveRecommendation": "최상의 규칙 생성 경험을 위해 위의 자동 승인 설정에서 읽기 및 쓰기 작업 모두에 대한 자동 승인을 활성화하는 것을 권장합니다.", "selectApiConfig": "API 구성 선택", "includeCustomRules": "내 규칙 포함", "includeCustomRulesDescription": "규칙 생성 프롬프트에 사용자 정의 지침 추가", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index e0b4150371..6f8eb4eaea 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Voeg gegenereerde regelbestanden automatisch toe aan .gitignore om te voorkomen dat ze worden gecommit naar versiebeheer", "autoApproveProtected": "Automatisch goedkeuren van schrijven naar beschermde bestanden", "autoApproveProtectedDescription": "Sta Roo toe om te maken en schrijven naar de .roo directory zonder handmatige goedkeuring te vereisen", - "autoApproveRecommendation": "Voor de beste regelgeneratie-ervaring raden we aan om automatische goedkeuring in te schakelen voor zowel lees- als schrijfoperaties in de Auto Approval instellingen hierboven.", "selectApiConfig": "Selecteer API-configuratie", "includeCustomRules": "Mijn aangepaste regels opnemen", "includeCustomRulesDescription": "Voeg aangepaste instructies toe aan de regelgeneratie prompt", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index d7396e6579..3b935d0359 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Automatycznie dodaj wygenerowane pliki zasad do .gitignore, aby zapobiec ich commitowaniu do kontroli wersji", "autoApproveProtected": "Automatycznie zatwierdź pisanie chronionych plików", "autoApproveProtectedDescription": "Pozwól Roo na tworzenie i pisanie do katalogu .roo bez wymagania ręcznego zatwierdzenia", - "autoApproveRecommendation": "Dla najlepszego doświadczenia generowania zasad zalecamy włączenie automatycznego zatwierdzania zarówno dla operacji odczytu, jak i zapisu w ustawieniach Auto Approval powyżej.", "selectApiConfig": "Wybierz konfigurację API", "includeCustomRules": "Uwzględnij moje niestandardowe zasady", "includeCustomRulesDescription": "Dodaj niestandardowe instrukcje do promptu generowania zasad", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index d1cb3cddfa..876d16c178 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Adicionar automaticamente os arquivos de regras gerados ao .gitignore para evitar que sejam commitados no controle de versão", "autoApproveProtected": "Aprovar automaticamente gravações de arquivos protegidos", "autoApproveProtectedDescription": "Permitir que o Roo crie e grave no diretório .roo sem exigir aprovação manual", - "autoApproveRecommendation": "Para a melhor experiência gerando regras, recomendamos habilitar a aprovação automática tanto para operações de leitura quanto de escrita nas configurações de Aprovação Automática acima.", "selectApiConfig": "Selecionar configuração da API", "includeCustomRules": "Incluir minhas próprias regras", "includeCustomRulesDescription": "Adicionar instruções personalizadas ao prompt de geração de regras", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 57aa87e123..e96dc938ad 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Автоматически добавить сгенерированные файлы правил в .gitignore, чтобы предотвратить их коммит в систему контроля версий", "autoApproveProtected": "Автоматически одобрять запись защищённых файлов", "autoApproveProtectedDescription": "Разрешить Roo создавать и записывать в директорию .roo без требования ручного одобрения", - "autoApproveRecommendation": "Для лучшего опыта генерации правил мы рекомендуем включить автоматическое одобрение как для операций чтения, так и записи в настройках Автоматического Одобрения выше.", "selectApiConfig": "Выбрать конфигурацию API", "includeCustomRules": "Включить мои собственные правила", "includeCustomRulesDescription": "Добавить пользовательские инструкции в промпт генерации правил", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 56e2445ace..84d4287412 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Üretilen kural dosyalarını otomatik olarak .gitignore'a ekleyerek sürüm kontrolüne commit edilmesini önle", "autoApproveProtected": "Korumalı dosya yazımını otomatik onayla", "autoApproveProtectedDescription": "Roo'nun manuel onay gerektirmeden .roo dizininde oluşturma ve yazma yapmasına izin ver", - "autoApproveRecommendation": "En iyi kural üretimi deneyimi için yukarıdaki Otomatik Onay ayarlarında hem okuma hem de yazma işlemleri için otomatik onayı etkinleştirmenizi öneririz.", "selectApiConfig": "API Yapılandırması Seç", "includeCustomRules": "Kendi kurallarımı dahil et", "includeCustomRulesDescription": "Kural üretimi promptuna özel talimatlar ekle", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index c0dcdb1095..0c1dab9a2d 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "Tự động thêm các tệp quy tắc được tạo vào .gitignore để ngăn chúng được commit vào kiểm soát phiên bản", "autoApproveProtected": "Tự động phê duyệt ghi tệp được bảo vệ", "autoApproveProtectedDescription": "Cho phép Roo tạo và ghi vào thư mục .roo mà không cần phê duyệt thủ công", - "autoApproveRecommendation": "Để có trải nghiệm tạo quy tắc tốt nhất, chúng tôi khuyến nghị bật tự động phê duyệt cho cả hoạt động đọc và ghi trong cài đặt Tự động phê duyệt ở trên.", "selectApiConfig": "Chọn cấu hình API", "includeCustomRules": "Bao gồm quy tắc tùy chỉnh của tôi", "includeCustomRulesDescription": "Thêm hướng dẫn tùy chỉnh vào prompt tạo quy tắc", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 32ffc83cfc..db7d3b842d 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "自动将生成的规则文件添加到 .gitignore 以防止提交到版本控制", "autoApproveProtected": "自动批准受保护文件写入", "autoApproveProtectedDescription": "允许 Roo 创建和写入 .roo 目录而无需手动批准", - "autoApproveRecommendation": "为获得最佳规则生成体验,我们建议在上方的自动批准设置中启用读取和写入操作的自动批准。", "selectApiConfig": "选择 API 配置", "includeCustomRules": "包含我的自定义规则", "includeCustomRulesDescription": "向规则生成提示添加自定义指令", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index ac946d785d..46eaff68de 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -619,7 +619,6 @@ "addToGitignoreDescription": "自動將產生的規則檔案新增到 .gitignore 以防止提交到版本控制", "autoApproveProtected": "自動核准受保護檔案寫入", "autoApproveProtectedDescription": "允許 Roo 建立和寫入 .roo 目錄而無需手動核准", - "autoApproveRecommendation": "為獲得最佳規則產生體驗,我們建議在上方的自動核准設定中啟用讀取和寫入操作的自動核准。", "selectApiConfig": "選擇 API 設定", "includeCustomRules": "包含我的自訂規則", "includeCustomRulesDescription": "向規則產生提示新增自訂指令", diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index 889dca9dbe..fcbd349cc1 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -105,6 +105,7 @@ export enum ContextMenuOptionType { Git = "git", NoResults = "noResults", Mode = "mode", // Add mode type + Rules = "rules", // Add rules type } export interface ContextMenuQueryItem { @@ -123,38 +124,70 @@ export function getContextMenuOptions( dynamicSearchResults: SearchResult[] = [], modes?: ModeConfig[], ): ContextMenuQueryItem[] { - // Handle slash commands for modes + // Handle slash commands if (query.startsWith("/") && inputValue.startsWith("/")) { - const modeQuery = query.slice(1) - if (!modes?.length) return [{ type: ContextMenuOptionType.NoResults }] + const commandQuery = query.slice(1).toLowerCase() - // Create searchable strings array for fzf - const searchableItems = modes.map((mode) => ({ - original: mode, - searchStr: mode.name, - })) + // Check if it's the make-rules command + if (commandQuery === "" || "make-rules".startsWith(commandQuery)) { + const rulesCommand: ContextMenuQueryItem = { + type: ContextMenuOptionType.Rules, + value: "make-rules", + label: "Make Rules", + description: "Generate AI rules for your project", + icon: "$(book)", + } - // Initialize fzf instance for fuzzy search - const fzf = new Fzf(searchableItems, { - selector: (item) => item.searchStr, - }) + // If query is empty or matches "make-rules", show rules command + if (commandQuery === "" || "make-rules".startsWith(commandQuery)) { + // Also include mode options if query is empty + if (commandQuery === "" && modes?.length) { + const modeOptions = modes.map((mode) => ({ + type: ContextMenuOptionType.Mode, + value: mode.slug, + label: mode.name, + description: getModeDescription(mode), + })) + return [rulesCommand, ...modeOptions] + } + return [rulesCommand] + } + } - // Get fuzzy matching items - const matchingModes = modeQuery - ? fzf.find(modeQuery).map((result) => ({ - type: ContextMenuOptionType.Mode, - value: result.item.original.slug, - label: result.item.original.name, - description: getModeDescription(result.item.original), - })) - : modes.map((mode) => ({ - type: ContextMenuOptionType.Mode, - value: mode.slug, - label: mode.name, - description: getModeDescription(mode), - })) + // Handle mode selection + if (modes?.length) { + const modeQuery = commandQuery - return matchingModes.length > 0 ? matchingModes : [{ type: ContextMenuOptionType.NoResults }] + // Create searchable strings array for fzf + const searchableItems = modes.map((mode) => ({ + original: mode, + searchStr: mode.name.toLowerCase(), + })) + + // Initialize fzf instance for fuzzy search + const fzf = new Fzf(searchableItems, { + selector: (item) => item.searchStr, + }) + + // Get fuzzy matching items + const matchingModes = modeQuery + ? fzf.find(modeQuery).map((result) => ({ + type: ContextMenuOptionType.Mode, + value: result.item.original.slug, + label: result.item.original.name, + description: getModeDescription(result.item.original), + })) + : modes.map((mode) => ({ + type: ContextMenuOptionType.Mode, + value: mode.slug, + label: mode.name, + description: getModeDescription(mode), + })) + + return matchingModes.length > 0 ? matchingModes : [{ type: ContextMenuOptionType.NoResults }] + } + + return [{ type: ContextMenuOptionType.NoResults }] } const workingChanges: ContextMenuQueryItem = {