Просмотр исходного кода

Make CLI auto-approve by default with require-approval opt-in (#11424)

Co-authored-by: Roo Code <[email protected]>
Chris Estreich 2 дней назад
Родитель
Сommit
6c8b9dfa26

+ 1 - 1
apps/cli/CHANGELOG.md

@@ -67,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Changed
 
 - Skip onboarding flow when a provider is explicitly specified via `--provider` flag or saved in settings
-- Unified permission flags: Combined `-y`, `--yes`, and `--dangerously-skip-permissions` into a single option for Claude Code-like CLI compatibility
+- Unified permission flags: Combined approval-skipping flags into a single option for Claude Code-like CLI compatibility
 - Improved Roo Code Router authentication flow and error messaging
 
 ### Fixed

+ 28 - 29
apps/cli/README.md

@@ -66,7 +66,7 @@ pnpm --filter @roo-code/cli build
 
 ### Interactive Mode (Default)
 
-By default, the CLI prompts for approval before executing actions:
+By default, the CLI auto-approves actions and runs in interactive TUI mode:
 
 ```bash
 export OPENROUTER_API_KEY=sk-or-v1-...
@@ -82,24 +82,23 @@ roo -w ~/Documents/my-project
 
 In interactive mode:
 
-- Tool executions prompt for yes/no approval
-- Commands prompt for yes/no approval
-- Followup questions show suggestions and wait for user input
-- Browser and MCP actions prompt for approval
+- Tool executions are auto-approved
+- Commands are auto-approved
+- Followup questions show suggestions with a 60-second timeout, then auto-select the first suggestion
+- Browser and MCP actions are auto-approved
 
-### Non-Interactive Mode (`-y`)
+### Approval-Required Mode (`--require-approval`)
 
-For automation and scripts, use `-y` to auto-approve all actions:
+If you want manual approval prompts, enable approval-required mode:
 
 ```bash
-roo "Refactor the utils.ts file" -y -w ~/Documents/my-project
+roo "Refactor the utils.ts file" --require-approval -w ~/Documents/my-project
 ```
 
-In non-interactive mode:
+In approval-required mode:
 
-- Tool, command, browser, and MCP actions are auto-approved
-- Followup questions show a 60-second timeout, then auto-select the first suggestion
-- Typing any key cancels the timeout and allows manual input
+- Tool, command, browser, and MCP actions prompt for yes/no approval
+- Followup questions wait for manual input (no auto-timeout)
 
 ### Roo Code Cloud Authentication
 
@@ -147,23 +146,23 @@ Tokens are valid for 90 days. The CLI will prompt you to re-authenticate when yo
 
 ## Options
 
-| Option                                      | Description                                                                             | Default                                  |
-| ------------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------- |
-| `[prompt]`                                  | Your prompt (positional argument, optional)                                             | None                                     |
-| `--prompt-file <path>`                      | Read prompt from a file instead of command line argument                                | None                                     |
-| `-w, --workspace <path>`                    | Workspace path to operate in                                                            | Current directory                        |
-| `-p, --print`                               | Print response and exit (non-interactive mode)                                          | `false`                                  |
-| `-e, --extension <path>`                    | Path to the extension bundle directory                                                  | Auto-detected                            |
-| `-d, --debug`                               | Enable debug output (includes detailed debug information, prompts, paths, etc)          | `false`                                  |
-| `-y, --yes, --dangerously-skip-permissions` | Auto-approve all actions (use with caution)                                             | `false`                                  |
-| `-k, --api-key <key>`                       | API key for the LLM provider                                                            | From env var                             |
-| `--provider <provider>`                     | API provider (roo, anthropic, openai, openrouter, etc.)                                 | `openrouter` (or `roo` if authenticated) |
-| `-m, --model <model>`                       | Model to use                                                                            | `anthropic/claude-opus-4.6`              |
-| `--mode <mode>`                             | Mode to start in (code, architect, ask, debug, etc.)                                    | `code`                                   |
-| `-r, --reasoning-effort <effort>`           | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium`                                 |
-| `--ephemeral`                               | Run without persisting state (uses temporary storage)                                   | `false`                                  |
-| `--oneshot`                                 | Exit upon task completion                                                               | `false`                                  |
-| `--output-format <format>`                  | Output format with `--print`: `text`, `json`, or `stream-json`                          | `text`                                   |
+| Option                            | Description                                                                             | Default                                  |
+| --------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------- |
+| `[prompt]`                        | Your prompt (positional argument, optional)                                             | None                                     |
+| `--prompt-file <path>`            | Read prompt from a file instead of command line argument                                | None                                     |
+| `-w, --workspace <path>`          | Workspace path to operate in                                                            | Current directory                        |
+| `-p, --print`                     | Print response and exit (non-interactive mode)                                          | `false`                                  |
+| `-e, --extension <path>`          | Path to the extension bundle directory                                                  | Auto-detected                            |
+| `-d, --debug`                     | Enable debug output (includes detailed debug information, prompts, paths, etc)          | `false`                                  |
+| `-a, --require-approval`          | Require manual approval before actions execute                                          | `false`                                  |
+| `-k, --api-key <key>`             | API key for the LLM provider                                                            | From env var                             |
+| `--provider <provider>`           | API provider (roo, anthropic, openai, openrouter, etc.)                                 | `openrouter` (or `roo` if authenticated) |
+| `-m, --model <model>`             | Model to use                                                                            | `anthropic/claude-opus-4.6`              |
+| `--mode <mode>`                   | Mode to start in (code, architect, ask, debug, etc.)                                    | `code`                                   |
+| `-r, --reasoning-effort <effort>` | Reasoning effort level (unspecified, disabled, none, minimal, low, medium, high, xhigh) | `medium`                                 |
+| `--ephemeral`                     | Run without persisting state (uses temporary storage)                                   | `false`                                  |
+| `--oneshot`                       | Exit upon task completion                                                               | `false`                                  |
+| `--output-format <format>`        | Output format with `--print`: `text`, `json`, or `stream-json`                          | `text`                                   |
 
 ## Auth Commands
 

+ 3 - 2
apps/cli/docs/AGENT_LOOP.md

@@ -242,7 +242,8 @@ Routes asks to appropriate handlers:
 
 - Uses type guards: `isIdleAsk()`, `isInteractiveAsk()`, etc.
 - Coordinates between `OutputManager` and `PromptManager`
-- In non-interactive mode (`-y` flag), auto-approves everything
+- By default, the CLI auto-approves tool/command/browser/MCP actions
+- In `--require-approval` mode, those actions prompt for manual approval
 
 ### OutputManager
 
@@ -320,7 +321,7 @@ if (isInteractiveAsk(ask)) {
 Enable with `-d` flag. Logs go to `~/.roo/cli-debug.log`:
 
 ```bash
-roo -d -y -P "Build something" --no-tui
+roo -d -P "Build something" --no-tui
 ```
 
 View logs:

+ 1 - 1
apps/cli/package.json

@@ -15,7 +15,7 @@
 		"test": "vitest run",
 		"build": "tsup",
 		"build:extension": "pnpm --filter roo-cline bundle",
-		"dev": "ROO_AUTH_BASE_URL=https://app.roocode.com ROO_SDK_BASE_URL=https://cloud-api.roocode.com ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy tsx src/index.ts -y",
+		"dev": "ROO_AUTH_BASE_URL=https://app.roocode.com ROO_SDK_BASE_URL=https://cloud-api.roocode.com ROO_CODE_PROVIDER_URL=https://api.roocode.com/proxy tsx src/index.ts",
 		"dev:local": "ROO_AUTH_BASE_URL=http://localhost:3000 ROO_SDK_BASE_URL=http://localhost:3001 ROO_CODE_PROVIDER_URL=http://localhost:8080/proxy tsx src/index.ts",
 		"clean": "rimraf dist .turbo"
 	},

+ 5 - 3
apps/cli/src/commands/cli/run.ts

@@ -65,8 +65,10 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
 		flagOptions.reasoningEffort || settings.reasoningEffort || DEFAULT_FLAGS.reasoningEffort
 	const effectiveProvider = flagOptions.provider ?? settings.provider ?? (rooToken ? "roo" : "openrouter")
 	const effectiveWorkspacePath = flagOptions.workspace ? path.resolve(flagOptions.workspace) : process.cwd()
-	const effectiveDangerouslySkipPermissions =
-		flagOptions.yes || flagOptions.dangerouslySkipPermissions || settings.dangerouslySkipPermissions || false
+	const legacyRequireApprovalFromSettings =
+		settings.requireApproval ??
+		(settings.dangerouslySkipPermissions === undefined ? undefined : !settings.dangerouslySkipPermissions)
+	const effectiveRequireApproval = flagOptions.requireApproval || legacyRequireApprovalFromSettings || false
 	const effectiveExitOnComplete = flagOptions.print || flagOptions.oneshot || settings.oneshot || false
 
 	const extensionHostOptions: ExtensionHostOptions = {
@@ -77,7 +79,7 @@ export async function run(promptArg: string | undefined, flagOptions: FlagOption
 		model: effectiveModel,
 		workspacePath: effectiveWorkspacePath,
 		extensionPath: path.resolve(flagOptions.extension || getDefaultExtensionPath(__dirname)),
-		nonInteractive: effectiveDangerouslySkipPermissions,
+		nonInteractive: !effectiveRequireApproval,
 		exitOnError: flagOptions.exitOnError,
 		ephemeral: flagOptions.ephemeral,
 		debug: flagOptions.debug,

+ 1 - 2
apps/cli/src/index.ts

@@ -18,8 +18,7 @@ program
 	.option("-p, --print", "Print response and exit (non-interactive mode)", false)
 	.option("-e, --extension <path>", "Path to the extension bundle directory")
 	.option("-d, --debug", "Enable debug output (includes detailed debug information)", false)
-	.option("-y, --yes", "Auto-approve all prompts (use with caution)", false)
-	.option("--dangerously-skip-permissions", "Alias for --yes", false)
+	.option("-a, --require-approval", "Require manual approval for actions", false)
 	.option("-k, --api-key <key>", "API key for the LLM provider")
 	.option("--provider <provider>", "API provider (roo, anthropic, openai, openrouter, etc.)")
 	.option("-m, --model <model>", "Model to use", DEFAULT_FLAGS.model)

+ 15 - 8
apps/cli/src/lib/storage/__tests__/settings.test.ts

@@ -179,20 +179,20 @@ describe("Settings Storage", () => {
 			expect(loaded.reasoningEffort).toBe("low")
 		})
 
-		it("should support dangerouslySkipPermissions setting", async () => {
-			await saveSettings({ dangerouslySkipPermissions: true })
+		it("should support requireApproval setting", async () => {
+			await saveSettings({ requireApproval: true })
 			const loaded = await loadSettings()
 
-			expect(loaded.dangerouslySkipPermissions).toBe(true)
+			expect(loaded.requireApproval).toBe(true)
 		})
 
-		it("should support all settings together including dangerouslySkipPermissions", async () => {
+		it("should support all settings together including requireApproval", async () => {
 			const allSettings = {
 				mode: "architect",
 				provider: "anthropic" as const,
 				model: "claude-sonnet-4-20250514",
 				reasoningEffort: "high" as const,
-				dangerouslySkipPermissions: true,
+				requireApproval: true,
 			}
 
 			await saveSettings(allSettings)
@@ -202,7 +202,7 @@ describe("Settings Storage", () => {
 			expect(loaded.provider).toBe("anthropic")
 			expect(loaded.model).toBe("claude-sonnet-4-20250514")
 			expect(loaded.reasoningEffort).toBe("high")
-			expect(loaded.dangerouslySkipPermissions).toBe(true)
+			expect(loaded.requireApproval).toBe(true)
 		})
 
 		it("should support oneshot setting", async () => {
@@ -218,7 +218,7 @@ describe("Settings Storage", () => {
 				provider: "anthropic" as const,
 				model: "claude-sonnet-4-20250514",
 				reasoningEffort: "high" as const,
-				dangerouslySkipPermissions: true,
+				requireApproval: true,
 				oneshot: true,
 			}
 
@@ -229,8 +229,15 @@ describe("Settings Storage", () => {
 			expect(loaded.provider).toBe("anthropic")
 			expect(loaded.model).toBe("claude-sonnet-4-20250514")
 			expect(loaded.reasoningEffort).toBe("high")
-			expect(loaded.dangerouslySkipPermissions).toBe(true)
+			expect(loaded.requireApproval).toBe(true)
 			expect(loaded.oneshot).toBe(true)
 		})
+
+		it("should still load legacy dangerouslySkipPermissions setting", async () => {
+			await saveSettings({ dangerouslySkipPermissions: true })
+			const loaded = await loadSettings()
+
+			expect(loaded.dangerouslySkipPermissions).toBe(true)
+		})
 	})
 })

+ 4 - 3
apps/cli/src/types/types.ts

@@ -24,8 +24,7 @@ export type FlagOptions = {
 	print: boolean
 	extension?: string
 	debug: boolean
-	yes: boolean
-	dangerouslySkipPermissions: boolean
+	requireApproval: boolean
 	exitOnError: boolean
 	apiKey?: string
 	provider?: SupportedProvider
@@ -58,7 +57,9 @@ export interface CliSettings {
 	model?: string
 	/** Default reasoning effort level */
 	reasoningEffort?: ReasoningEffortFlagOptions
-	/** Auto-approve all prompts (use with caution) */
+	/** Require manual approval for tools/commands/browser/MCP actions */
+	requireApproval?: boolean
+	/** @deprecated Legacy inverse setting kept for backward compatibility */
 	dangerouslySkipPermissions?: boolean
 	/** Exit upon task completion */
 	oneshot?: boolean

+ 0 - 1
packages/evals/src/cli/runTaskInCli.ts

@@ -43,7 +43,6 @@ export const runTaskWithCli = async ({ run, task, publish, logger, jobToken }: R
 		promptSourcePath,
 		"--workspace",
 		workspacePath,
-		"--yes",
 		"--reasoning-effort",
 		"disabled",
 		"--oneshot",