Skip to content

Commit 1e4b6b3

Browse files
Apply PR #12633: feat(tui): add auto-accept mode for permission requests
2 parents 6a5f89f + 5792a80 commit 1e4b6b3

4 files changed

Lines changed: 35 additions & 11 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export function Prompt(props: PromptProps) {
182182
const [warpNotice, setWarpNotice] = createSignal<string>()
183183
const currentProviderLabel = createMemo(() => local.model.parsed().provider)
184184
const hasRightContent = createMemo(() => Boolean(props.right))
185+
const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
185186

186187
function selectWorkspace(selection: WorkspaceSelection | undefined) {
187188
setWorkspaceSelection(selection)
@@ -383,6 +384,17 @@ export function Prompt(props: PromptProps) {
383384

384385
command.register(() => {
385386
return [
387+
{
388+
title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
389+
value: "permission.auto_accept.toggle",
390+
search: "toggle permissions",
391+
keybind: "permission_auto_accept_toggle",
392+
category: "Agent",
393+
onSelect: (dialog) => {
394+
setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
395+
dialog.clear()
396+
},
397+
},
386398
{
387399
title: "Clear prompt",
388400
value: "prompt.clear",
@@ -1459,11 +1471,14 @@ export function Prompt(props: PromptProps) {
14591471
)}
14601472
</Show>
14611473
</box>
1462-
<Show when={hasRightContent()}>
1463-
<box flexDirection="row" gap={1} alignItems="center">
1464-
{props.right}
1465-
</box>
1466-
</Show>
1474+
<box flexDirection="row" gap={1} alignItems="center">
1475+
<Show when={hasRightContent()}>{props.right}</Show>
1476+
<Show when={autoaccept() === "edit"}>
1477+
<text>
1478+
<span style={{ fg: theme.warning }}>autoedit</span>
1479+
</text>
1480+
</Show>
1481+
</box>
14671482
</box>
14681483
</box>
14691484
</box>

packages/opencode/src/cli/cmd/tui/context/sync.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ import { createSimpleContext } from "./helper"
2727
import type { Snapshot } from "@/snapshot"
2828
import { useExit } from "./exit"
2929
import { useArgs } from "./args"
30+
import { useKV } from "./kv"
3031
import { batch, onMount } from "solid-js"
3132
import * as Log from "@opencode-ai/core/util/log"
3233
import { emptyConsoleState, type ConsoleState } from "@/config/console-state"
3334
import path from "path"
34-
import { useKV } from "./kv"
3535

3636
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
3737
name: "Sync",
@@ -110,6 +110,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
110110
const project = useProject()
111111
const sdk = useSDK()
112112
const kv = useKV()
113+
const [autoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
113114

114115
const fullSyncedSessions = new Set<string>()
115116
let syncedWorkspace = project.workspace.current()
@@ -152,6 +153,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
152153

153154
case "permission.asked": {
154155
const request = event.properties
156+
if (autoaccept() === "edit" && request.permission === "edit") {
157+
sdk.client.permission.reply({
158+
reply: "once",
159+
requestID: request.id,
160+
})
161+
break
162+
}
155163
const requests = store.permission[request.sessionID]
156164
if (!requests) {
157165
setStore("permission", request.sessionID, [request])

packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface DialogSelectOption<T = any> {
3838
title: string
3939
value: T
4040
description?: string
41+
search?: string
4142
footer?: JSX.Element | string
4243
category?: string
4344
categoryView?: JSX.Element
@@ -94,8 +95,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
9495
// users typically search by the item name, and not its category.
9596
const result = fuzzysort
9697
.go(needle, options, {
97-
keys: ["title", "category"],
98-
scoreFn: (r) => r[0].score * 2 + r[1].score,
98+
keys: ["title", "category", "search"],
99+
scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
99100
})
100101
.map((x) => x.obj)
101102

packages/opencode/test/agent/agent.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ test("build agent has correct default properties", async () => {
4949
expect(build).toBeDefined()
5050
expect(build?.mode).toBe("primary")
5151
expect(build?.native).toBe(true)
52-
expect(evalPerm(build, "edit")).toBe("allow")
52+
expect(evalPerm(build, "edit")).toBe("ask")
5353
expect(evalPerm(build, "bash")).toBe("allow")
5454
},
5555
})
@@ -229,8 +229,8 @@ test("agent permission config merges with defaults", async () => {
229229
expect(build).toBeDefined()
230230
// Specific pattern is denied
231231
expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
232-
// Edit still allowed
233-
expect(evalPerm(build, "edit")).toBe("allow")
232+
// Edit still asks (default behavior)
233+
expect(evalPerm(build, "edit")).toBe("ask")
234234
},
235235
})
236236
})

0 commit comments

Comments
 (0)