diff --git a/packages/types/src/mode.ts b/packages/types/src/mode.ts index 175ec095fc2..177343104bb 100644 --- a/packages/types/src/mode.ts +++ b/packages/types/src/mode.ts @@ -70,6 +70,7 @@ export const modeConfigSchema = z.object({ customInstructions: z.string().optional(), groups: groupEntryArraySchema, source: z.enum(["global", "project"]).optional(), + disableTaskLists: z.boolean().optional(), }) export type ModeConfig = z.infer diff --git a/src/core/prompts/tools/__tests__/index.spec.ts b/src/core/prompts/tools/__tests__/index.spec.ts new file mode 100644 index 00000000000..43d32d051a2 --- /dev/null +++ b/src/core/prompts/tools/__tests__/index.spec.ts @@ -0,0 +1,119 @@ +// npx vitest run src/core/prompts/tools/__tests__/index.spec.ts + +import type { ModeConfig } from "@roo-code/types" + +import { getToolDescriptionsForMode } from "../index" + +describe("getToolDescriptionsForMode", () => { + describe("disableTaskLists", () => { + it("excludes update_todo_list from tool descriptions when disableTaskLists is true", () => { + const customModes: ModeConfig[] = [ + { + slug: "no-tasks-mode", + name: "No Tasks Mode", + roleDefinition: "A mode without task lists", + groups: ["read"] as const, + disableTaskLists: true, + }, + ] + + const toolDescriptions = getToolDescriptionsForMode( + "no-tasks-mode", + "/test/cwd", + false, // supportsComputerUse + undefined, // codeIndexManager + undefined, // diffStrategy + undefined, // browserViewportSize + undefined, // mcpHub + customModes, + {}, // experiments + false, // partialReadsEnabled + {}, // settings + ) + + // Should not contain update_todo_list tool description + expect(toolDescriptions).not.toContain("update_todo_list") + expect(toolDescriptions).not.toContain("## update_todo_list") + + // Should still contain other always available tools + expect(toolDescriptions).toContain("ask_followup_question") + expect(toolDescriptions).toContain("attempt_completion") + }) + + it("includes update_todo_list in tool descriptions when disableTaskLists is false", () => { + const customModes: ModeConfig[] = [ + { + slug: "tasks-mode", + name: "Tasks Mode", + roleDefinition: "A mode with task lists", + groups: ["read"] as const, + disableTaskLists: false, + }, + ] + + const toolDescriptions = getToolDescriptionsForMode( + "tasks-mode", + "/test/cwd", + false, // supportsComputerUse + undefined, // codeIndexManager + undefined, // diffStrategy + undefined, // browserViewportSize + undefined, // mcpHub + customModes, + {}, // experiments + false, // partialReadsEnabled + {}, // settings + ) + + // Should contain update_todo_list tool description + expect(toolDescriptions).toContain("## update_todo_list") + }) + + it("includes update_todo_list in tool descriptions when disableTaskLists is undefined", () => { + const customModes: ModeConfig[] = [ + { + slug: "default-mode", + name: "Default Mode", + roleDefinition: "A mode with default task list behavior", + groups: ["read"] as const, + }, + ] + + const toolDescriptions = getToolDescriptionsForMode( + "default-mode", + "/test/cwd", + false, // supportsComputerUse + undefined, // codeIndexManager + undefined, // diffStrategy + undefined, // browserViewportSize + undefined, // mcpHub + customModes, + {}, // experiments + false, // partialReadsEnabled + {}, // settings + ) + + // Should contain update_todo_list tool description by default + expect(toolDescriptions).toContain("## update_todo_list") + }) + + it("includes update_todo_list in built-in mode tool descriptions", () => { + const toolDescriptions = getToolDescriptionsForMode( + "code", + "/test/cwd", + false, // supportsComputerUse + undefined, // codeIndexManager + undefined, // diffStrategy + undefined, // browserViewportSize + undefined, // mcpHub + [], // customModes + {}, // experiments + false, // partialReadsEnabled + {}, // settings + ) + + // Built-in modes should always include update_todo_list + expect(toolDescriptions).toContain("## update_todo_list") + }) + }) +}) diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 3fd5a636a4f..11a682080e8 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -98,8 +98,14 @@ export function getToolDescriptionsForMode( } }) - // Add always available tools - ALWAYS_AVAILABLE_TOOLS.forEach((tool) => tools.add(tool)) + // Add always available tools, but check for mode-specific exclusions + ALWAYS_AVAILABLE_TOOLS.forEach((tool) => { + // Check if update_todo_list should be excluded for this mode + if (tool === "update_todo_list" && config.disableTaskLists) { + return // Skip adding this tool + } + tools.add(tool) + }) // Conditionally exclude codebase_search if feature is disabled or not configured if ( diff --git a/src/core/tools/__tests__/validateToolUse.spec.ts b/src/core/tools/__tests__/validateToolUse.spec.ts index 7802674605b..e35f6485dd9 100644 --- a/src/core/tools/__tests__/validateToolUse.spec.ts +++ b/src/core/tools/__tests__/validateToolUse.spec.ts @@ -128,6 +128,59 @@ describe("mode-validator", () => { expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false) }) }) + + describe("disableTaskLists", () => { + it("disallows update_todo_list when disableTaskLists is true", () => { + const customModes: ModeConfig[] = [ + { + slug: "no-tasks-mode", + name: "No Tasks Mode", + roleDefinition: "A mode without task lists", + groups: ["read"] as const, + disableTaskLists: true, + }, + ] + // Should not allow update_todo_list tool + expect(isToolAllowedForMode("update_todo_list", "no-tasks-mode", customModes)).toBe(false) + // Should still allow other always available tools + expect(isToolAllowedForMode("ask_followup_question", "no-tasks-mode", customModes)).toBe(true) + expect(isToolAllowedForMode("attempt_completion", "no-tasks-mode", customModes)).toBe(true) + }) + + it("allows update_todo_list when disableTaskLists is false", () => { + const customModes: ModeConfig[] = [ + { + slug: "tasks-mode", + name: "Tasks Mode", + roleDefinition: "A mode with task lists", + groups: ["read"] as const, + disableTaskLists: false, + }, + ] + // Should allow update_todo_list tool + expect(isToolAllowedForMode("update_todo_list", "tasks-mode", customModes)).toBe(true) + }) + + it("allows update_todo_list when disableTaskLists is undefined", () => { + const customModes: ModeConfig[] = [ + { + slug: "default-mode", + name: "Default Mode", + roleDefinition: "A mode with default task list behavior", + groups: ["read"] as const, + }, + ] + // Should allow update_todo_list tool by default + expect(isToolAllowedForMode("update_todo_list", "default-mode", customModes)).toBe(true) + }) + + it("allows update_todo_list in built-in modes", () => { + // Built-in modes should always allow update_todo_list + expect(isToolAllowedForMode("update_todo_list", codeMode, [])).toBe(true) + expect(isToolAllowedForMode("update_todo_list", architectMode, [])).toBe(true) + expect(isToolAllowedForMode("update_todo_list", askMode, [])).toBe(true) + }) + }) }) describe("validateToolUse", () => { diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 9c790236ebc..a9138dc109a 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -231,8 +231,15 @@ export function isToolAllowedForMode( toolParams?: Record, // All tool parameters experiments?: Record, ): boolean { - // Always allow these tools + // Always allow these tools, but check for mode-specific exclusions if (ALWAYS_AVAILABLE_TOOLS.includes(tool as any)) { + // Check if update_todo_list should be excluded for this mode + if (tool === "update_todo_list") { + const mode = getModeBySlug(modeSlug, customModes) + if (mode?.disableTaskLists) { + return false + } + } return true } if (experiments && Object.values(EXPERIMENT_IDS).includes(tool as ExperimentId)) {