diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 54248f96f3d..9f1099bdf75 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -11,6 +11,7 @@ import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2" import { Server } from "../../server/server" import { Provider } from "../../provider/provider" import { Agent } from "../../agent/agent" +import { PermissionNext } from "../../permission/next" const TOOL: Record = { todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD], @@ -91,8 +92,16 @@ export const RunCommand = cmd({ type: "string", describe: "model variant (provider-specific reasoning effort, e.g., high, max, minimal)", }) + .option("dangerously-skip-permissions", { + type: "boolean", + describe: "Skip all permission prompts (use with extreme caution)", + }) }, handler: async (args) => { + if (args.dangerouslySkipPermissions || Flag.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS) { + PermissionNext.setSkipPermissions(true) + } + let message = [...args.message, ...(args["--"] || [])] .map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg)) .join(" ") diff --git a/packages/opencode/src/cli/cmd/tui/context/args.tsx b/packages/opencode/src/cli/cmd/tui/context/args.tsx index ffd43009a41..1f04136796a 100644 --- a/packages/opencode/src/cli/cmd/tui/context/args.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/args.tsx @@ -6,6 +6,7 @@ export interface Args { prompt?: string continue?: boolean sessionID?: string + dangerouslySkipPermissions?: boolean } export const { use: useArgs, provider: ArgsProvider } = createSimpleContext({ diff --git a/packages/opencode/src/cli/cmd/tui/routes/home.tsx b/packages/opencode/src/cli/cmd/tui/routes/home.tsx index 59923c69d94..16307864b4d 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/home.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/home.tsx @@ -110,6 +110,9 @@ export function Home() { + + △ YOLO mode + diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx index 8ace2fff372..c542b6ac679 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx @@ -5,11 +5,13 @@ import { useDirectory } from "../../context/directory" import { useConnected } from "../../component/dialog-model" import { createStore } from "solid-js/store" import { useRoute } from "../../context/route" +import { useArgs } from "../../context/args" export function Footer() { const { theme } = useTheme() const sync = useSync() const route = useRoute() + const args = useArgs() const mcp = createMemo(() => Object.values(sync.data.mcp).filter((x) => x.status === "connected").length) const mcpError = createMemo(() => Object.values(sync.data.mcp).some((x) => x.status === "failed")) const lsp = createMemo(() => Object.keys(sync.data.lsp)) @@ -53,6 +55,9 @@ export function Footer() { {directory()} + + △ YOLO mode + diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index c0f4bd74abd..2ea7c9ccb61 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -11,10 +11,12 @@ import { useKeybind } from "../../context/keybind" import { useDirectory } from "../../context/directory" import { useKV } from "../../context/kv" import { TodoItem } from "../../component/todo-item" +import { useArgs } from "../../context/args" export function Sidebar(props: { sessionID: string; overlay?: boolean }) { const sync = useSync() const { theme } = useTheme() + const args = useArgs() const session = createMemo(() => sync.session.get(props.sessionID)!) const diff = createMemo(() => sync.data.session_diff[props.sessionID] ?? []) const todo = createMemo(() => sync.data.todo[props.sessionID] ?? []) @@ -312,6 +314,9 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { {" "} {Installation.VERSION} + + △ YOLO mode + diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 05714268545..9c4eb7af5b8 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -9,6 +9,7 @@ import { Log } from "@/util/log" import { withNetworkOptions, resolveNetworkOptions } from "@/cli/network" import type { Event } from "@opencode-ai/sdk/v2" import type { EventSource } from "./context/sdk" +import { Flag } from "@/flag/flag" declare global { const OPENCODE_WORKER_PATH: string @@ -71,8 +72,16 @@ export const TuiThreadCommand = cmd({ .option("agent", { type: "string", describe: "agent to use", + }) + .option("dangerously-skip-permissions", { + type: "boolean", + describe: "Skip all permission prompts (use with extreme caution)", }), handler: async (args) => { + if (args.dangerouslySkipPermissions || Flag.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS) { + process.env.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS = "true" + } + // Resolve relative paths against PWD to preserve behavior when using --cwd flag const baseCwd = process.env.PWD ?? process.cwd() const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd() @@ -150,6 +159,7 @@ export const TuiThreadCommand = cmd({ agent: args.agent, model: args.model, prompt, + dangerouslySkipPermissions: args.dangerouslySkipPermissions || Flag.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS, }, onExit: async () => { await client.call("shutdown", undefined) diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 4cdb549096a..c501cb9e05d 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -8,6 +8,7 @@ export namespace Flag { export const OPENCODE_DISABLE_PRUNE = truthy("OPENCODE_DISABLE_PRUNE") export const OPENCODE_DISABLE_TERMINAL_TITLE = truthy("OPENCODE_DISABLE_TERMINAL_TITLE") export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"] + export const OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS = truthy("OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS") export const OPENCODE_DISABLE_DEFAULT_PLUGINS = truthy("OPENCODE_DISABLE_DEFAULT_PLUGINS") export const OPENCODE_DISABLE_LSP_DOWNLOAD = truthy("OPENCODE_DISABLE_LSP_DOWNLOAD") export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS") diff --git a/packages/opencode/src/permission/next.ts b/packages/opencode/src/permission/next.ts index f95aaf34525..723b179443c 100644 --- a/packages/opencode/src/permission/next.ts +++ b/packages/opencode/src/permission/next.ts @@ -8,10 +8,17 @@ import { fn } from "@/util/fn" import { Log } from "@/util/log" import { Wildcard } from "@/util/wildcard" import z from "zod" +import { Flag } from "@/flag/flag" export namespace PermissionNext { const log = Log.create({ service: "permission" }) + let skipPermissions = Flag.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS + + export function setSkipPermissions(value: boolean) { + skipPermissions = value + } + export const Action = z.enum(["allow", "deny", "ask"]).meta({ ref: "PermissionAction", }) @@ -126,6 +133,7 @@ export namespace PermissionNext { if (rule.action === "deny") throw new DeniedError(ruleset.filter((r) => Wildcard.match(request.permission, r.permission))) if (rule.action === "ask") { + if (skipPermissions) continue const id = input.id ?? Identifier.ascending("permission") return new Promise((resolve, reject) => { const info: Request = {