feat(cli): add yolo permission alias

This commit is contained in:
opencode-agent[bot]
2026-06-20 23:27:34 +00:00
parent d99f86b28d
commit 46c75db883
10 changed files with 152 additions and 89 deletions
+1
View File
@@ -229,6 +229,7 @@ export const RunCommand = effectCmd({
default: false,
})
.option("dangerously-skip-permissions", {
alias: ["yolo"],
type: "boolean",
describe: "auto-approve permissions that are not explicitly denied (dangerous!)",
default: false,
+6
View File
@@ -103,6 +103,12 @@ export const TuiThreadCommand = cmd({
.option("agent", {
type: "string",
describe: "agent to use",
})
.option("dangerously-skip-permissions", {
alias: ["yolo"],
type: "boolean",
describe: "auto-approve permissions that are not explicitly denied (dangerous!)",
default: false,
}),
handler: async (args) => {
const unguard = win32InstallCtrlCGuard()
@@ -15,6 +15,7 @@ const enabledByExperimental = (name: string) =>
export class Service extends ConfigService.Service<Service>()("@opencode/RuntimeFlags", {
autoShare: bool("OPENCODE_AUTO_SHARE"),
dangerouslySkipPermissions: bool("OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS"),
pure: bool("OPENCODE_PURE"),
disableDefaultPlugins: bool("OPENCODE_DISABLE_DEFAULT_PLUGINS"),
disableEmbeddedWebUi: bool("OPENCODE_DISABLE_EMBEDDED_WEB_UI"),
+1
View File
@@ -66,6 +66,7 @@ const cli = yargs(args)
.middleware(async (opts) => {
if (opts.printLogs) process.env.OPENCODE_PRINT_LOGS = "1"
if (opts.logLevel) process.env.OPENCODE_LOG_LEVEL = opts.logLevel
if (opts.dangerouslySkipPermissions) process.env.OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS = "1"
if (opts.pure) {
process.env.OPENCODE_PURE = "1"
}
+8 -2
View File
@@ -7,6 +7,7 @@ import os from "os"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { EventV2Bridge } from "@/event-v2-bridge"
import { EventV2 } from "@opencode-ai/core/event"
import { RuntimeFlags } from "@/effect/runtime-flags"
export const Event = {
Asked: EventV2.define({ type: "permission.asked", schema: PermissionV1.Request.fields }),
@@ -54,6 +55,7 @@ export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2Bridge.Service
const flags = yield* RuntimeFlags.Service
const state = yield* InstanceState.make<State>(
Effect.fn("Permission.state")(function* (ctx) {
void ctx
@@ -89,6 +91,7 @@ export const layer = Layer.effect(
})
}
if (rule.action === "allow") continue
if (flags.dangerouslySkipPermissions) continue
needsAsk = true
}
@@ -223,8 +226,11 @@ export function disabled(tools: string[], ruleset: PermissionV1.Ruleset): Set<st
)
}
export const defaultLayer = layer.pipe(Layer.provide(EventV2Bridge.defaultLayer))
export const defaultLayer = layer.pipe(
Layer.provide(EventV2Bridge.defaultLayer),
Layer.provide(RuntimeFlags.defaultLayer),
)
export const node = LayerNode.make(layer, [EventV2Bridge.node])
export const node = LayerNode.make(layer, [EventV2Bridge.node, RuntimeFlags.node])
export * as Permission from "."
@@ -72,47 +72,50 @@ Positionals:
message message to send [array] [default: []]
Options:
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level [string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--command the command to run, use message for args [string]
-c, --continue continue the last session [boolean]
-s, --session session id to continue [string]
--fork fork the session before continuing (requires --continue or
--session) [boolean]
--share share the session [boolean]
-m, --model model to use in the format of provider/model [string]
--agent agent to use [string]
--format format: default (formatted) or json (raw JSON events)
-h, --help show help [boolean]
-v, --version show version number [boolean]
--print-logs print logs to stderr [boolean]
--log-level log level
[string] [choices: "DEBUG", "INFO", "WARN", "ERROR"]
--pure run without external plugins [boolean]
--command the command to run, use message for args [string]
-c, --continue continue the last session [boolean]
-s, --session session id to continue [string]
--fork fork the session before continuing (requires
--continue or --session) [boolean]
--share share the session [boolean]
-m, --model model to use in the format of provider/model [string]
--agent agent to use [string]
--format format: default (formatted) or json (raw JSON events)
[string] [choices: "default", "json"] [default: "default"]
-f, --file file(s) to attach to message [array]
--title title for the session (uses truncated prompt if no value
provided) [string]
--attach attach to a running opencode server (e.g.,
http://localhost:4096) [string]
-p, --password basic auth password (defaults to OPENCODE_SERVER_PASSWORD)
[string]
-u, --username basic auth username (defaults to OPENCODE_SERVER_USERNAME or
'opencode') [string]
--dir directory to run in, path on remote server if attaching
[string]
--port port for the local server (defaults to random port if no value
provided) [number]
--variant model variant (provider-specific reasoning effort, e.g., high,
max, minimal) [string]
--thinking show thinking blocks [boolean]
--replay replay interactive session history on resume and after resize
(use --no-replay to disable) [boolean] [default: true]
--replay-limit cap visible interactive replay to the newest N messages
[number]
-i, --interactive run in direct interactive split-footer mode
-f, --file file(s) to attach to message [array]
--title title for the session (uses truncated prompt if no
value provided) [string]
--attach attach to a running opencode server (e.g.,
http://localhost:4096) [string]
-p, --password basic auth password (defaults to
OPENCODE_SERVER_PASSWORD) [string]
-u, --username basic auth username (defaults to
OPENCODE_SERVER_USERNAME or 'opencode') [string]
--dir directory to run in, path on remote server if
attaching [string]
--port port for the local server (defaults to random port if
no value provided) [number]
--variant model variant (provider-specific reasoning effort,
e.g., high, max, minimal) [string]
--thinking show thinking blocks [boolean]
--replay replay interactive session history on resume and after
resize (use --no-replay to disable)
[boolean] [default: true]
--replay-limit cap visible interactive replay to the newest N
messages [number]
-i, --interactive run in direct interactive split-footer mode
[boolean] [default: false]
--dangerously-skip-permissions auto-approve permissions that are not explicitly denied
(dangerous!) [boolean] [default: false]
--demo enable direct interactive demo slash commands; pass one as the
message to run it immediately [boolean] [default: false]"
--dangerously-skip-permissions, --yolo auto-approve permissions that are not explicitly
denied (dangerous!) [boolean] [default: false]
--demo enable direct interactive demo slash commands; pass
one as the message to run it immediately
[boolean] [default: false]"
`;
exports[`opencode CLI help-text snapshots every documented command emits stable help text: opencode debug --help 1`] = `
@@ -102,6 +102,8 @@ describe("opencode CLI help-text snapshots", () => {
const topLevel = yield* opencode.spawn(["--help"], { env: SNAPSHOT_ENV })
expect(topLevel.exitCode).toBe(0)
expect(topLevel.stderr.endsWith(EOL)).toBe(true)
expect(topLevel.stderr).toContain("--dangerously-skip-permissions")
expect(topLevel.stderr).toContain("--yolo")
const argvs: Array<readonly string[]> = [...TOP_LEVEL.map((c) => [c] as const), ...SUBCOMMANDS]
@@ -14,6 +14,7 @@ describe("RuntimeFlags", () => {
const flags = yield* readFlags.pipe(Effect.provide(fromConfig({})))
expect(flags.autoShare).toBe(false)
expect(flags.dangerouslySkipPermissions).toBe(false)
}),
)
@@ -23,6 +24,7 @@ describe("RuntimeFlags", () => {
Effect.provide(
fromConfig({
OPENCODE_PURE: "true",
OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS: "true",
OPENCODE_DISABLE_DEFAULT_PLUGINS: "true",
OPENCODE_AUTO_SHARE: "true",
OPENCODE_DISABLE_EMBEDDED_WEB_UI: "true",
@@ -39,6 +41,7 @@ describe("RuntimeFlags", () => {
)
expect(flags.pure).toBe(true)
expect(flags.dangerouslySkipPermissions).toBe(true)
expect(flags.autoShare).toBe(true)
expect(flags.disableDefaultPlugins).toBe(true)
expect(flags.disableEmbeddedWebUi).toBe(true)
+45 -7
View File
@@ -11,16 +11,21 @@ import { InstanceStore } from "../../src/project/instance-store"
import { TestInstance, tmpdirScoped } from "../fixture/fixture"
import { testEffect } from "../lib/effect"
import { MessageID, SessionID } from "../../src/session/schema"
import { RuntimeFlags } from "../../src/effect/runtime-flags"
const events = EventV2Bridge.defaultLayer
const noopBootstrap = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
const env = Layer.mergeAll(
Permission.layer.pipe(Layer.provide(Database.defaultLayer), Layer.provide(events)),
events,
CrossSpawnSpawner.defaultLayer,
InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)),
)
const it = testEffect(env)
const makeEnv = (flags: Partial<RuntimeFlags.Info> = {}) => {
const runtimeFlags = RuntimeFlags.layer(flags)
return Layer.mergeAll(
Permission.layer.pipe(Layer.provide(Database.defaultLayer), Layer.provide(events), Layer.provide(runtimeFlags)),
events,
CrossSpawnSpawner.defaultLayer,
InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrap)),
)
}
const it = testEffect(makeEnv())
const dangerousIt = testEffect(makeEnv({ dangerouslySkipPermissions: true }))
const rejectAll = (message?: string) =>
Effect.gen(function* () {
@@ -613,6 +618,39 @@ it.instance(
{ git: true },
)
dangerousIt.instance(
"ask - resolves ask rules when dangerous permission skipping is enabled",
() =>
ask({
sessionID: SessionID.make("session_test"),
permission: "bash",
patterns: ["ls"],
metadata: {},
always: [],
ruleset: [{ permission: "bash", pattern: "*", action: "ask" }],
}),
{ git: true },
)
dangerousIt.instance(
"ask - preserves deny rules when dangerous permission skipping is enabled",
() =>
Effect.gen(function* () {
const err = yield* fail(
ask({
sessionID: SessionID.make("session_test"),
permission: "bash",
patterns: ["rm -rf /"],
metadata: {},
always: [],
ruleset: [{ permission: "bash", pattern: "*", action: "deny" }],
}),
)
expect(err).toBeInstanceOf(PermissionV1.DeniedError)
}),
{ git: true },
)
it.instance(
"ask - adds request to pending list",
() =>
+43 -41
View File
@@ -29,19 +29,20 @@ opencode [project]
#### Flags
| Flag | Short | Description |
| ------------------------------------------- | ----- | ----------------------------------------------------------------------- |
| <nobr><code>{"--continue"}</code></nobr> | `-c` | Continue the last session |
| <nobr><code>{"--session"}</code></nobr> | `-s` | Session ID to continue |
| <nobr><code>{"--fork"}</code></nobr> | | Fork the session when continuing (use with `--continue` or `--session`) |
| <nobr><code>{"--prompt"}</code></nobr> | | Prompt to use |
| <nobr><code>{"--model"}</code></nobr> | `-m` | Model to use in the form of provider/model |
| <nobr><code>{"--agent"}</code></nobr> | | Agent to use |
| <nobr><code>{"--port"}</code></nobr> | | Port to listen on |
| <nobr><code>{"--hostname"}</code></nobr> | | Hostname to listen on |
| <nobr><code>{"--mdns"}</code></nobr> | | Enable mDNS discovery |
| <nobr><code>{"--mdns-domain"}</code></nobr> | | Custom mDNS domain name |
| <nobr><code>{"--cors"}</code></nobr> | | Additional browser origin(s) to allow CORS |
| Flag | Short | Description |
| ------------------------------------------------------------ | ----- | ----------------------------------------------------------------------- |
| <nobr><code>{"--continue"}</code></nobr> | `-c` | Continue the last session |
| <nobr><code>{"--session"}</code></nobr> | `-s` | Session ID to continue |
| <nobr><code>{"--fork"}</code></nobr> | | Fork the session when continuing (use with `--continue` or `--session`) |
| <nobr><code>{"--prompt"}</code></nobr> | | Prompt to use |
| <nobr><code>{"--model"}</code></nobr> | `-m` | Model to use in the form of provider/model |
| <nobr><code>{"--agent"}</code></nobr> | | Agent to use |
| <nobr><code>{"--port"}</code></nobr> | | Port to listen on |
| <nobr><code>{"--hostname"}</code></nobr> | | Hostname to listen on |
| <nobr><code>{"--mdns"}</code></nobr> | | Enable mDNS discovery |
| <nobr><code>{"--mdns-domain"}</code></nobr> | | Custom mDNS domain name |
| <nobr><code>{"--cors"}</code></nobr> | | Additional browser origin(s) to allow CORS |
| <nobr><code>{"--dangerously-skip-permissions"}</code></nobr> | | Auto-approve permissions not explicitly denied. Alias: `--yolo` |
---
@@ -380,7 +381,7 @@ opencode run --attach http://localhost:4096 "Explain async/await in JavaScript"
| <nobr><code>{"--port"}</code></nobr> | | Port for the local server (defaults to random port) |
| <nobr><code>{"--variant"}</code></nobr> | | Model variant (provider-specific reasoning effort) |
| <nobr><code>{"--thinking"}</code></nobr> | | Show thinking blocks |
| <nobr><code>{"--dangerously-skip-permissions"}</code></nobr> | | Auto-approve permissions that are not explicitly denied (dangerous!) |
| <nobr><code>{"--dangerously-skip-permissions"}</code></nobr> | | Auto-approve permissions that are not explicitly denied. Alias: `--yolo` |
---
@@ -676,33 +677,34 @@ The opencode CLI takes the following global flags.
OpenCode can be configured using environment variables.
| Variable | Type | Description |
| ------------------------------------- | ------- | ------------------------------------------------- |
| `OPENCODE_AUTO_SHARE` | boolean | Automatically share sessions |
| `OPENCODE_GIT_BASH_PATH` | string | Path to Git Bash executable on Windows |
| `OPENCODE_CONFIG` | string | Path to config file |
| `OPENCODE_TUI_CONFIG` | string | Path to TUI config file |
| `OPENCODE_CONFIG_DIR` | string | Path to config directory |
| `OPENCODE_CONFIG_CONTENT` | string | Inline json config content |
| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | Disable automatic update checks |
| `OPENCODE_DISABLE_PRUNE` | boolean | Disable pruning of old data |
| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | Disable automatic terminal title updates |
| `OPENCODE_PERMISSION` | string | Inlined json permissions config |
| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | Disable default plugins |
| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | Disable automatic LSP server downloads |
| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | Enable experimental models |
| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | Disable automatic context compaction |
| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | Disable reading from `.claude` (prompt + skills) |
| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | Disable reading `~/.claude/CLAUDE.md` |
| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | Disable loading `.claude/skills` |
| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | Disable fetching models from remote sources |
| `OPENCODE_DISABLE_MOUSE` | boolean | Disable mouse capture in the TUI |
| `OPENCODE_FAKE_VCS` | string | Fake VCS provider for testing purposes |
| `OPENCODE_CLIENT` | string | Client identifier (defaults to `cli`) |
| `OPENCODE_ENABLE_EXA` | boolean | Enable Exa web search tools |
| `OPENCODE_SERVER_PASSWORD` | string | Enable basic auth for `serve`/`web` |
| `OPENCODE_SERVER_USERNAME` | string | Override basic auth username (default `opencode`) |
| `OPENCODE_MODELS_URL` | string | Custom URL for fetching models configuration |
| Variable | Type | Description |
| --------------------------------------- | ------- | ------------------------------------------------- |
| `OPENCODE_AUTO_SHARE` | boolean | Automatically share sessions |
| `OPENCODE_GIT_BASH_PATH` | string | Path to Git Bash executable on Windows |
| `OPENCODE_CONFIG` | string | Path to config file |
| `OPENCODE_TUI_CONFIG` | string | Path to TUI config file |
| `OPENCODE_CONFIG_DIR` | string | Path to config directory |
| `OPENCODE_CONFIG_CONTENT` | string | Inline json config content |
| `OPENCODE_DISABLE_AUTOUPDATE` | boolean | Disable automatic update checks |
| `OPENCODE_DISABLE_PRUNE` | boolean | Disable pruning of old data |
| `OPENCODE_DISABLE_TERMINAL_TITLE` | boolean | Disable automatic terminal title updates |
| `OPENCODE_PERMISSION` | string | Inlined json permissions config |
| `OPENCODE_DANGEROUSLY_SKIP_PERMISSIONS` | boolean | Auto-approve permissions not explicitly denied |
| `OPENCODE_DISABLE_DEFAULT_PLUGINS` | boolean | Disable default plugins |
| `OPENCODE_DISABLE_LSP_DOWNLOAD` | boolean | Disable automatic LSP server downloads |
| `OPENCODE_ENABLE_EXPERIMENTAL_MODELS` | boolean | Enable experimental models |
| `OPENCODE_DISABLE_AUTOCOMPACT` | boolean | Disable automatic context compaction |
| `OPENCODE_DISABLE_CLAUDE_CODE` | boolean | Disable reading from `.claude` (prompt + skills) |
| `OPENCODE_DISABLE_CLAUDE_CODE_PROMPT` | boolean | Disable reading `~/.claude/CLAUDE.md` |
| `OPENCODE_DISABLE_CLAUDE_CODE_SKILLS` | boolean | Disable loading `.claude/skills` |
| `OPENCODE_DISABLE_MODELS_FETCH` | boolean | Disable fetching models from remote sources |
| `OPENCODE_DISABLE_MOUSE` | boolean | Disable mouse capture in the TUI |
| `OPENCODE_FAKE_VCS` | string | Fake VCS provider for testing purposes |
| `OPENCODE_CLIENT` | string | Client identifier (defaults to `cli`) |
| `OPENCODE_ENABLE_EXA` | boolean | Enable Exa web search tools |
| `OPENCODE_SERVER_PASSWORD` | string | Enable basic auth for `serve`/`web` |
| `OPENCODE_SERVER_USERNAME` | string | Override basic auth username (default `opencode`) |
| `OPENCODE_MODELS_URL` | string | Custom URL for fetching models configuration |
---