Skip to content

Commit 1d3a44e

Browse files
mrubenshannesrudolph
authored andcommitted
Add a 'when to use' section to mode definitions (#3571)
* Add a 'when to use' section to mode definitions * Remove defaults for now
1 parent 65d25dd commit 1d3a44e

File tree

26 files changed

+284
-5
lines changed

26 files changed

+284
-5
lines changed

.changeset/dark-swans-marry.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Add a 'when to use' section to mode definitions

src/core/prompts/instructions/create-mode.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ If asked to create a project mode, create it in .roomodes in the workspace root.
2525
* roleDefinition: A detailed description of the mode's role and capabilities
2626
* groups: Array of allowed tool groups (can be empty). Each group can be specified either as a string (e.g., "edit" to allow editing any file) or with file restrictions (e.g., ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }] to only allow editing markdown files)
2727
28-
- The customInstructions field is optional.
28+
- The following fields are optional but highly recommended:
29+
* whenToUse: A clear description of when this mode should be selected and what types of tasks it's best suited for. This helps the Orchestrator mode make better decisions.
30+
* customInstructions: Additional instructions for how the mode should operate
2931
3032
- For multi-line text, include newline characters in the string like "This is the first line.\\nThis is the next line.\\n\\nThis is a double line break."
3133
@@ -36,6 +38,7 @@ Both files should follow this structure:
3638
"slug": "designer", // Required: unique slug with lowercase letters, numbers, and hyphens
3739
"name": "Designer", // Required: mode display name
3840
"roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty
41+
"whenToUse": "Use this mode when creating or modifying UI components, implementing design systems, or ensuring responsive web interfaces. This mode is especially effective with CSS, HTML, and modern frontend frameworks.", // Optional but recommended
3942
"groups": [ // Required: array of tool groups (can be empty)
4043
"read", // Read files group (read_file, fetch_instructions, search_files, list_files, list_code_definition_names)
4144
"edit", // Edit files group (apply_diff, write_to_file) - allows editing any file

src/core/prompts/sections/modes.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ export async function getModesSection(context: vscode.ExtensionContext): Promise
1616
MODES
1717
1818
- These are the currently available modes:
19-
${allModes.map((mode: ModeConfig) => ` * "${mode.name}" mode (${mode.slug}) - ${mode.roleDefinition.split(".")[0]}`).join("\n")}`
19+
${allModes
20+
.map((mode: ModeConfig) => {
21+
let description: string
22+
if (mode.whenToUse && mode.whenToUse.trim() !== "") {
23+
// Use whenToUse as the primary description, indenting subsequent lines for readability
24+
description = mode.whenToUse.replace(/\n/g, "\n ")
25+
} else {
26+
// Fallback to the first sentence of roleDefinition if whenToUse is not available
27+
description = mode.roleDefinition.split(".")[0]
28+
}
29+
return ` * "${mode.name}" mode (${mode.slug}) - ${description}`
30+
})
31+
.join("\n")}`
2032

2133
modesContent += `
2234
If the user asks you to create or edit a new mode for this project, you should read the instructions by using the fetch_instructions tool, like this:

src/exports/roo-code.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ type GlobalSettings = {
138138
slug: string
139139
name: string
140140
roleDefinition: string
141+
whenToUse?: string | undefined
141142
customInstructions?: string | undefined
142143
groups: (
143144
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -157,6 +158,7 @@ type GlobalSettings = {
157158
[x: string]:
158159
| {
159160
roleDefinition?: string | undefined
161+
whenToUse?: string | undefined
160162
customInstructions?: string | undefined
161163
}
162164
| undefined
@@ -822,6 +824,7 @@ type IpcMessage =
822824
slug: string
823825
name: string
824826
roleDefinition: string
827+
whenToUse?: string | undefined
825828
customInstructions?: string | undefined
826829
groups: (
827830
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -841,6 +844,7 @@ type IpcMessage =
841844
[x: string]:
842845
| {
843846
roleDefinition?: string | undefined
847+
whenToUse?: string | undefined
844848
customInstructions?: string | undefined
845849
}
846850
| undefined
@@ -1282,6 +1286,7 @@ type TaskCommand =
12821286
slug: string
12831287
name: string
12841288
roleDefinition: string
1289+
whenToUse?: string | undefined
12851290
customInstructions?: string | undefined
12861291
groups: (
12871292
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -1301,6 +1306,7 @@ type TaskCommand =
13011306
[x: string]:
13021307
| {
13031308
roleDefinition?: string | undefined
1309+
whenToUse?: string | undefined
13041310
customInstructions?: string | undefined
13051311
}
13061312
| undefined

src/exports/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ type GlobalSettings = {
138138
slug: string
139139
name: string
140140
roleDefinition: string
141+
whenToUse?: string | undefined
141142
customInstructions?: string | undefined
142143
groups: (
143144
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -157,6 +158,7 @@ type GlobalSettings = {
157158
[x: string]:
158159
| {
159160
roleDefinition?: string | undefined
161+
whenToUse?: string | undefined
160162
customInstructions?: string | undefined
161163
}
162164
| undefined
@@ -834,6 +836,7 @@ type IpcMessage =
834836
slug: string
835837
name: string
836838
roleDefinition: string
839+
whenToUse?: string | undefined
837840
customInstructions?: string | undefined
838841
groups: (
839842
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -853,6 +856,7 @@ type IpcMessage =
853856
[x: string]:
854857
| {
855858
roleDefinition?: string | undefined
859+
whenToUse?: string | undefined
856860
customInstructions?: string | undefined
857861
}
858862
| undefined
@@ -1296,6 +1300,7 @@ type TaskCommand =
12961300
slug: string
12971301
name: string
12981302
roleDefinition: string
1303+
whenToUse?: string | undefined
12991304
customInstructions?: string | undefined
13001305
groups: (
13011306
| ("read" | "edit" | "browser" | "command" | "mcp" | "modes")
@@ -1315,6 +1320,7 @@ type TaskCommand =
13151320
[x: string]:
13161321
| {
13171322
roleDefinition?: string | undefined
1323+
whenToUse?: string | undefined
13181324
customInstructions?: string | undefined
13191325
}
13201326
| undefined

src/schemas/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ export const modeConfigSchema = z.object({
217217
slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"),
218218
name: z.string().min(1, "Name is required"),
219219
roleDefinition: z.string().min(1, "Role definition is required"),
220+
whenToUse: z.string().optional(),
220221
customInstructions: z.string().optional(),
221222
groups: groupEntryArraySchema,
222223
source: z.enum(["global", "project"]).optional(),
@@ -256,6 +257,7 @@ export type CustomModesSettings = z.infer<typeof customModesSettingsSchema>
256257

257258
export const promptComponentSchema = z.object({
258259
roleDefinition: z.string().optional(),
260+
whenToUse: z.string().optional(),
259261
customInstructions: z.string().optional(),
260262
})
261263

src/shared/modes.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export const defaultPrompts: Readonly<CustomModePrompts> = Object.freeze(
233233
mode.slug,
234234
{
235235
roleDefinition: mode.roleDefinition,
236+
whenToUse: mode.whenToUse,
236237
customInstructions: mode.customInstructions,
237238
},
238239
]),
@@ -248,6 +249,7 @@ export async function getAllModesWithPrompts(context: vscode.ExtensionContext):
248249
return allModes.map((mode) => ({
249250
...mode,
250251
roleDefinition: customModePrompts[mode.slug]?.roleDefinition ?? mode.roleDefinition,
252+
whenToUse: customModePrompts[mode.slug]?.whenToUse ?? mode.whenToUse,
251253
customInstructions: customModePrompts[mode.slug]?.customInstructions ?? mode.customInstructions,
252254
}))
253255
}
@@ -271,6 +273,7 @@ export async function getFullModeDetails(
271273

272274
// Get the base custom instructions
273275
const baseCustomInstructions = promptComponent?.customInstructions || baseMode.customInstructions || ""
276+
const baseWhenToUse = promptComponent?.whenToUse || baseMode.whenToUse || ""
274277

275278
// If we have cwd, load and combine all custom instructions
276279
let fullCustomInstructions = baseCustomInstructions
@@ -288,6 +291,7 @@ export async function getFullModeDetails(
288291
return {
289292
...baseMode,
290293
roleDefinition: promptComponent?.roleDefinition || baseMode.roleDefinition,
294+
whenToUse: baseWhenToUse,
291295
customInstructions: fullCustomInstructions,
292296
}
293297
}
@@ -302,6 +306,16 @@ export function getRoleDefinition(modeSlug: string, customModes?: ModeConfig[]):
302306
return mode.roleDefinition
303307
}
304308

309+
// Helper function to safely get whenToUse
310+
export function getWhenToUse(modeSlug: string, customModes?: ModeConfig[]): string {
311+
const mode = getModeBySlug(modeSlug, customModes)
312+
if (!mode) {
313+
console.warn(`No mode found for slug: ${modeSlug}`)
314+
return ""
315+
}
316+
return mode.whenToUse ?? ""
317+
}
318+
305319
// Helper function to safely get custom instructions
306320
export function getCustomInstructions(modeSlug: string, customModes?: ModeConfig[]): string {
307321
const mode = getModeBySlug(modeSlug, customModes)

webview-ui/src/components/prompts/PromptsView.tsx

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
Mode,
88
PromptComponent,
99
getRoleDefinition,
10+
getWhenToUse,
1011
getCustomInstructions,
1112
getAllModes,
1213
ModeConfig,
@@ -106,6 +107,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
106107
if (updatedPrompt.roleDefinition === getRoleDefinition(mode)) {
107108
delete updatedPrompt.roleDefinition
108109
}
110+
if (updatedPrompt.whenToUse === getWhenToUse(mode)) {
111+
delete updatedPrompt.whenToUse
112+
}
109113

110114
vscode.postMessage({
111115
type: "updatePrompt",
@@ -195,6 +199,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
195199
const [newModeName, setNewModeName] = useState("")
196200
const [newModeSlug, setNewModeSlug] = useState("")
197201
const [newModeRoleDefinition, setNewModeRoleDefinition] = useState("")
202+
const [newModeWhenToUse, setNewModeWhenToUse] = useState("")
198203
const [newModeCustomInstructions, setNewModeCustomInstructions] = useState("")
199204
const [newModeGroups, setNewModeGroups] = useState<GroupEntry[]>(availableGroups)
200205
const [newModeSource, setNewModeSource] = useState<ModeSource>("global")
@@ -212,6 +217,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
212217
setNewModeSlug("")
213218
setNewModeGroups(availableGroups)
214219
setNewModeRoleDefinition("")
220+
setNewModeWhenToUse("")
215221
setNewModeCustomInstructions("")
216222
setNewModeSource("global")
217223
// Reset error states
@@ -258,6 +264,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
258264
slug: newModeSlug,
259265
name: newModeName,
260266
roleDefinition: newModeRoleDefinition.trim(),
267+
whenToUse: newModeWhenToUse.trim() || undefined,
261268
customInstructions: newModeCustomInstructions.trim() || undefined,
262269
groups: newModeGroups,
263270
source,
@@ -299,6 +306,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
299306
newModeName,
300307
newModeSlug,
301308
newModeRoleDefinition,
309+
newModeWhenToUse, // Add whenToUse dependency
302310
newModeCustomInstructions,
303311
newModeGroups,
304312
newModeSource,
@@ -396,7 +404,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
396404
})
397405
}
398406

399-
const handleAgentReset = (modeSlug: string, type: "roleDefinition" | "customInstructions") => {
407+
const handleAgentReset = (modeSlug: string, type: "roleDefinition" | "whenToUse" | "customInstructions") => {
400408
// Only reset for built-in modes
401409
const existingPrompt = customModePrompts?.[modeSlug] as PromptComponent
402410
const updatedPrompt = { ...existingPrompt }
@@ -699,6 +707,61 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
699707
data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`}
700708
/>
701709
</div>
710+
711+
{/* When to Use section */}
712+
<div className="mb-4">
713+
<div className="flex justify-between items-center mb-1">
714+
<div className="font-bold">{t("prompts:whenToUse.title")}</div>
715+
{!findModeBySlug(visualMode, customModes) && (
716+
<Button
717+
variant="ghost"
718+
size="icon"
719+
onClick={() => {
720+
const currentMode = getCurrentMode()
721+
if (currentMode?.slug) {
722+
handleAgentReset(currentMode.slug, "whenToUse")
723+
}
724+
}}
725+
title={t("prompts:whenToUse.resetToDefault")}
726+
data-testid="when-to-use-reset">
727+
<span className="codicon codicon-discard"></span>
728+
</Button>
729+
)}
730+
</div>
731+
<div className="text-sm text-vscode-descriptionForeground mb-2">
732+
{t("prompts:whenToUse.description")}
733+
</div>
734+
<VSCodeTextArea
735+
value={(() => {
736+
const customMode = findModeBySlug(visualMode, customModes)
737+
const prompt = customModePrompts?.[visualMode] as PromptComponent
738+
return customMode?.whenToUse ?? prompt?.whenToUse ?? getWhenToUse(visualMode)
739+
})()}
740+
onChange={(e) => {
741+
const value =
742+
(e as unknown as CustomEvent)?.detail?.target?.value ||
743+
((e as any).target as HTMLTextAreaElement).value
744+
const customMode = findModeBySlug(visualMode, customModes)
745+
if (customMode) {
746+
// For custom modes, update the JSON file
747+
updateCustomMode(visualMode, {
748+
...customMode,
749+
whenToUse: value.trim() || undefined,
750+
source: customMode.source || "global",
751+
})
752+
} else {
753+
// For built-in modes, update the prompts
754+
updateAgentPrompt(visualMode, {
755+
whenToUse: value.trim() || undefined,
756+
})
757+
}
758+
}}
759+
className="resize-y w-full"
760+
rows={3}
761+
data-testid={`${getCurrentMode()?.slug || "code"}-when-to-use-textarea`}
762+
/>
763+
</div>
764+
702765
{/* Mode settings */}
703766
<>
704767
<div className="mb-3">
@@ -1258,6 +1321,21 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
12581321
</div>
12591322
)}
12601323
</div>
1324+
1325+
<div className="mb-4">
1326+
<div className="font-bold mb-1">{t("prompts:createModeDialog.whenToUse.label")}</div>
1327+
<div className="text-[13px] text-vscode-descriptionForeground mb-2">
1328+
{t("prompts:createModeDialog.whenToUse.description")}
1329+
</div>
1330+
<VSCodeTextArea
1331+
value={newModeWhenToUse}
1332+
onChange={(e) => {
1333+
setNewModeWhenToUse((e.target as HTMLTextAreaElement).value)
1334+
}}
1335+
rows={3}
1336+
className="w-full resize-y"
1337+
/>
1338+
</div>
12611339
<div className="mb-4">
12621340
<div className="font-bold mb-1">{t("prompts:createModeDialog.tools.label")}</div>
12631341
<div className="text-[13px] text-vscode-descriptionForeground mb-2">

webview-ui/src/i18n/locales/ca/prompts.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
"resetToDefault": "Restablir a valors predeterminats",
3535
"description": "Definiu l'experiència i personalitat de Roo per a aquest mode. Aquesta descripció determina com Roo es presenta i aborda les tasques."
3636
},
37+
"whenToUse": {
38+
"title": "Quan utilitzar (opcional)",
39+
"description": "Descriviu quan s'hauria d'utilitzar aquest mode. Això ajuda l'Orchestrator a escollir el mode correcte per a una tasca.",
40+
"resetToDefault": "Restablir la descripció 'Quan utilitzar' a valors predeterminats"
41+
},
3742
"customInstructions": {
3843
"title": "Instruccions personalitzades específiques del mode (opcional)",
3944
"resetToDefault": "Restablir a valors predeterminats",
@@ -131,6 +136,10 @@
131136
"label": "Definició de rol",
132137
"description": "Definiu l'experiència i personalitat de Roo per a aquest mode."
133138
},
139+
"whenToUse": {
140+
"label": "Quan utilitzar (opcional)",
141+
"description": "Proporcioneu una descripció clara de quan aquest mode és més efectiu i per a quins tipus de tasques excel·leix."
142+
},
134143
"tools": {
135144
"label": "Eines disponibles",
136145
"description": "Seleccioneu quines eines pot utilitzar aquest mode."

webview-ui/src/i18n/locales/de/prompts.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
"resetToDefault": "Auf Standardwerte zurücksetzen",
3535
"description": "Definiere Roos Expertise und Persönlichkeit für diesen Modus. Diese Beschreibung prägt, wie Roo sich präsentiert und an Aufgaben herangeht."
3636
},
37+
"whenToUse": {
38+
"title": "Wann zu verwenden (optional)",
39+
"description": "Beschreibe, wann dieser Modus verwendet werden sollte. Dies hilft dem Orchestrator, den richtigen Modus für eine Aufgabe auszuwählen.",
40+
"resetToDefault": "Beschreibung 'Wann zu verwenden' auf Standardwerte zurücksetzen"
41+
},
3742
"customInstructions": {
3843
"title": "Modusspezifische benutzerdefinierte Anweisungen (optional)",
3944
"resetToDefault": "Auf Standardwerte zurücksetzen",
@@ -131,6 +136,10 @@
131136
"label": "Rollendefinition",
132137
"description": "Definiere Roos Expertise und Persönlichkeit für diesen Modus."
133138
},
139+
"whenToUse": {
140+
"label": "Wann zu verwenden (optional)",
141+
"description": "Gib eine klare Beschreibung, wann dieser Modus am effektivsten ist und für welche Arten von Aufgaben er sich besonders eignet."
142+
},
134143
"tools": {
135144
"label": "Verfügbare Werkzeuge",
136145
"description": "Wähle, welche Werkzeuge dieser Modus verwenden kann."

0 commit comments

Comments
 (0)