Skip to content

Commit d30eb3e

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

7 files changed

Lines changed: 41 additions & 6 deletions

File tree

packages/opencode/src/agent/agent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export const layer = Layer.effect(
9494
question: "deny",
9595
plan_enter: "deny",
9696
plan_exit: "deny",
97+
edit: "ask",
9798
// mirrors github.com/github/gitignore Node.gitignore pattern for .env files
9899
read: {
99100
"*": "allow",

packages/opencode/src/cli/cmd/run.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ export const RunCommand = cmd({
367367
action: "deny",
368368
pattern: "*",
369369
},
370+
{
371+
permission: "edit",
372+
action: "allow",
373+
pattern: "*",
374+
},
370375
]
371376

372377
function title() {

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export function Prompt(props: PromptProps) {
112112
const [auto, setAuto] = createSignal<AutocompleteRef>()
113113
const currentProviderLabel = createMemo(() => local.model.parsed().provider)
114114
const hasRightContent = createMemo(() => Boolean(props.right))
115+
const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
115116

116117
function promptModelWarning() {
117118
toast.show({
@@ -228,6 +229,17 @@ export function Prompt(props: PromptProps) {
228229

229230
command.register(() => {
230231
return [
232+
{
233+
title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
234+
value: "permission.auto_accept.toggle",
235+
search: "toggle permissions",
236+
keybind: "permission_auto_accept_toggle",
237+
category: "Agent",
238+
onSelect: (dialog) => {
239+
setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
240+
dialog.clear()
241+
},
242+
},
231243
{
232244
title: "Clear prompt",
233245
value: "prompt.clear",
@@ -1221,8 +1233,13 @@ export function Prompt(props: PromptProps) {
12211233
)}
12221234
</Show>
12231235
</box>
1224-
<Show when={hasRightContent()}>
1236+
<Show when={hasRightContent() || autoaccept() === "edit"}>
12251237
<box flexDirection="row" gap={1} alignItems="center">
1238+
<Show when={autoaccept() === "edit"}>
1239+
<text>
1240+
<span style={{ fg: theme.warning }}>autoedit</span>
1241+
</text>
1242+
</Show>
12261243
{props.right}
12271244
</box>
12281245
</Show>

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ 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 { Log } from "@/util"
3233
import { emptyConsoleState, type ConsoleState } from "@/config/console-state"
@@ -107,6 +108,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
107108
const event = useEvent()
108109
const project = useProject()
109110
const sdk = useSDK()
111+
const kv = useKV()
112+
const [autoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
110113

111114
const fullSyncedSessions = new Set<string>()
112115
let syncedWorkspace = project.workspace.current()
@@ -133,6 +136,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
133136

134137
case "permission.asked": {
135138
const request = event.properties
139+
if (autoaccept() === "edit" && request.permission === "edit") {
140+
sdk.client.permission.reply({
141+
reply: "once",
142+
requestID: request.id,
143+
})
144+
break
145+
}
136146
const requests = store.permission[request.sessionID]
137147
if (!requests) {
138148
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
@@ -37,6 +37,7 @@ export interface DialogSelectOption<T = any> {
3737
title: string
3838
value: T
3939
description?: string
40+
search?: string
4041
footer?: JSX.Element | string
4142
category?: string
4243
categoryView?: JSX.Element
@@ -93,8 +94,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
9394
// users typically search by the item name, and not its category.
9495
const result = fuzzysort
9596
.go(needle, options, {
96-
keys: ["title", "category"],
97-
scoreFn: (r) => r[0].score * 2 + r[1].score,
97+
keys: ["title", "category", "search"],
98+
scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
9899
})
99100
.map((x) => x.obj)
100101

packages/opencode/src/config/keybinds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const KeybindsSchema = Schema.Struct({
6262
agent_list: keybind("<leader>a", "List agents"),
6363
agent_cycle: keybind("tab", "Next agent"),
6464
agent_cycle_reverse: keybind("shift+tab", "Previous agent"),
65+
permission_auto_accept_toggle: keybind("none", "Toggle auto-accept mode for permissions"),
6566
variant_cycle: keybind("ctrl+t", "Cycle model variants"),
6667
variant_list: keybind("none", "List model variants"),
6768
input_clear: keybind("ctrl+c", "Clear input field"),

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ test("build agent has correct default properties", async () => {
4747
expect(build).toBeDefined()
4848
expect(build?.mode).toBe("primary")
4949
expect(build?.native).toBe(true)
50-
expect(evalPerm(build, "edit")).toBe("allow")
50+
expect(evalPerm(build, "edit")).toBe("ask")
5151
expect(evalPerm(build, "bash")).toBe("allow")
5252
},
5353
})
@@ -224,8 +224,8 @@ test("agent permission config merges with defaults", async () => {
224224
expect(build).toBeDefined()
225225
// Specific pattern is denied
226226
expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
227-
// Edit still allowed
228-
expect(evalPerm(build, "edit")).toBe("allow")
227+
// Edit still asks (default behavior)
228+
expect(evalPerm(build, "edit")).toBe("ask")
229229
},
230230
})
231231
})

0 commit comments

Comments
 (0)