Skip to content

Commit 7730016

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

5 files changed

Lines changed: 47 additions & 12 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
@@ -160,6 +160,7 @@ export function Prompt(props: PromptProps) {
160160
const dimensions = useTerminalDimensions()
161161
const { theme, syntax } = useTheme()
162162
const kv = useKV()
163+
const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
163164
const animationsEnabled = createMemo(() => kv.get("animations_enabled", true))
164165
const list = createMemo(() => props.placeholders?.normal ?? [])
165166
const shell = createMemo(() => props.placeholders?.shell ?? [])
@@ -407,6 +408,17 @@ export function Prompt(props: PromptProps) {
407408

408409
const promptCommands = createMemo(() =>
409410
[
411+
{
412+
title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
413+
name: "permission.auto_accept.toggle",
414+
search: "toggle permissions",
415+
keybind: "permission_auto_accept_toggle",
416+
category: "Agent",
417+
run: () => {
418+
setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
419+
dialog.clear()
420+
},
421+
},
410422
{
411423
title: "Clear prompt",
412424
name: "prompt.clear",
@@ -1587,11 +1599,14 @@ export function Prompt(props: PromptProps) {
15871599
)}
15881600
</Show>
15891601
</box>
1590-
<Show when={hasRightContent()}>
1591-
<box flexDirection="row" gap={1} alignItems="center">
1592-
{props.right}
1593-
</box>
1594-
</Show>
1602+
<box flexDirection="row" gap={1} alignItems="center">
1603+
<Show when={hasRightContent()}>{props.right}</Show>
1604+
<Show when={autoaccept() === "edit"}>
1605+
<text>
1606+
<span style={{ fg: theme.warning }}>autoedit</span>
1607+
</text>
1608+
</Show>
1609+
</box>
15951610
</box>
15961611
</box>
15971612
</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/routes/session/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ export function Session() {
650650
{
651651
title: sidebarVisible() ? "Hide sidebar" : "Show sidebar",
652652
value: "session.sidebar.toggle",
653+
search: "toggle sidebar",
654+
keybind: "sidebar_toggle",
653655
category: "Session",
654656
run: () => {
655657
batch(() => {
@@ -663,6 +665,8 @@ export function Session() {
663665
{
664666
title: conceal() ? "Disable code concealment" : "Enable code concealment",
665667
value: "session.toggle.conceal",
668+
search: "toggle code concealment",
669+
keybind: "messages_toggle_conceal" as any,
666670
category: "Session",
667671
run: () => {
668672
setConceal((prev) => !prev)
@@ -672,6 +676,7 @@ export function Session() {
672676
{
673677
title: showTimestamps() ? "Hide timestamps" : "Show timestamps",
674678
value: "session.toggle.timestamps",
679+
search: "toggle timestamps",
675680
category: "Session",
676681
slash: {
677682
name: "timestamps",
@@ -685,6 +690,8 @@ export function Session() {
685690
{
686691
title: showThinking() ? "Hide thinking" : "Show thinking",
687692
value: "session.toggle.thinking",
693+
search: "toggle thinking",
694+
keybind: "display_thinking",
688695
category: "Session",
689696
slash: {
690697
name: "thinking",
@@ -698,15 +705,19 @@ export function Session() {
698705
{
699706
title: showDetails() ? "Hide tool details" : "Show tool details",
700707
value: "session.toggle.actions",
708+
search: "toggle tool details",
709+
keybind: "tool_details",
701710
category: "Session",
702711
run: () => {
703712
setShowDetails((prev) => !prev)
704713
dialog.clear()
705714
},
706715
},
707716
{
708-
title: "Toggle session scrollbar",
717+
title: showScrollbar() ? "Hide session scrollbar" : "Show session scrollbar",
709718
value: "session.toggle.scrollbar",
719+
search: "toggle session scrollbar",
720+
keybind: "scrollbar_toggle",
710721
category: "Session",
711722
run: () => {
712723
setShowScrollbar((prev) => !prev)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface DialogSelectOption<T = any> {
4646
title: string
4747
value: T
4848
description?: string
49+
search?: string
4950
footer?: JSX.Element | string
5051
category?: string
5152
categoryView?: JSX.Element
@@ -121,8 +122,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
121122
// users typically search by the item name, and not its category.
122123
const result = fuzzysort
123124
.go(needle, options, {
124-
keys: ["title", "category"],
125-
scoreFn: (r) => r[0].score * 2 + r[1].score,
125+
keys: ["title", "category", "search"],
126+
scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
126127
})
127128
.map((x) => x.obj)
128129

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ test("build agent has correct default properties", async () => {
6262
expect(build).toBeDefined()
6363
expect(build?.mode).toBe("primary")
6464
expect(build?.native).toBe(true)
65-
expect(evalPerm(build, "edit")).toBe("allow")
65+
expect(evalPerm(build, "edit")).toBe("ask")
6666
expect(evalPerm(build, "bash")).toBe("allow")
6767
expect(evalPerm(build, "repo_clone")).toBe("deny")
6868
expect(evalPerm(build, "repo_overview")).toBe("deny")
@@ -323,8 +323,8 @@ test("agent permission config merges with defaults", async () => {
323323
expect(build).toBeDefined()
324324
// Specific pattern is denied
325325
expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
326-
// Edit still allowed
327-
expect(evalPerm(build, "edit")).toBe("allow")
326+
// Edit still asks (default behavior)
327+
expect(evalPerm(build, "edit")).toBe("ask")
328328
},
329329
})
330330
})

0 commit comments

Comments
 (0)