Skip to content

Commit 878c1b8

Browse files
committed
feat(tui): add auto-accept mode for permission requests
Add a toggleable auto-accept mode that automatically accepts all incoming permission requests with a 'once' reply. This is useful for users who want to streamline their workflow when they trust the agent's actions. Changes: - Add permission_auto_accept keybind (default: shift+tab) to config - Remove default for agent_cycle_reverse (was shift+tab) - Add auto-accept logic in sync.tsx to auto-reply when enabled - Add command bar action to toggle auto-accept mode (copy: "Toggle autoaccept permissions") - Add visual indicator showing 'auto-accept' when active - Store auto-accept state in KV for persistence across sessions
1 parent 4b7abc0 commit 878c1b8

3 files changed

Lines changed: 51 additions & 17 deletions

File tree

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

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export function Prompt(props: PromptProps) {
7474
const renderer = useRenderer()
7575
const { theme, syntax } = useTheme()
7676
const kv = useKV()
77+
const [autoaccept, setAutoaccept] = kv.signal("permission_auto_accept", false)
7778

7879
function promptModelWarning() {
7980
toast.show({
@@ -157,6 +158,16 @@ export function Prompt(props: PromptProps) {
157158

158159
command.register(() => {
159160
return [
161+
{
162+
title: "Toggle autoaccept permissions",
163+
value: "permission.auto_accept.toggle",
164+
keybind: "permission_auto_accept_toggle",
165+
category: "Permission",
166+
onSelect: (dialog) => {
167+
setAutoaccept(!autoaccept() as any)
168+
dialog.clear()
169+
},
170+
},
160171
{
161172
title: "Clear prompt",
162173
value: "prompt.clear",
@@ -973,23 +984,30 @@ export function Prompt(props: PromptProps) {
973984
cursorColor={theme.text}
974985
syntaxStyle={syntax()}
975986
/>
976-
<box flexDirection="row" flexShrink={0} paddingTop={1} gap={1}>
977-
<text fg={highlight()}>
978-
{store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "}
979-
</text>
980-
<Show when={store.mode === "normal"}>
981-
<box flexDirection="row" gap={1}>
982-
<text flexShrink={0} fg={keybind.leader ? theme.textMuted : theme.text}>
983-
{local.model.parsed().model}
984-
</text>
985-
<text fg={theme.textMuted}>{local.model.parsed().provider}</text>
986-
<Show when={showVariant()}>
987-
<text fg={theme.textMuted}>·</text>
988-
<text>
989-
<span style={{ fg: theme.warning, bold: true }}>{local.model.variant.current()}</span>
987+
<box flexDirection="row" flexShrink={0} paddingTop={1} gap={1} justifyContent="space-between">
988+
<box flexDirection="row" gap={1}>
989+
<text fg={highlight()}>
990+
{store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "}
991+
</text>
992+
<Show when={store.mode === "normal"}>
993+
<box flexDirection="row" gap={1}>
994+
<text flexShrink={0} fg={keybind.leader ? theme.textMuted : theme.text}>
995+
{local.model.parsed().model}
990996
</text>
991-
</Show>
992-
</box>
997+
<text fg={theme.textMuted}>{local.model.parsed().provider}</text>
998+
<Show when={showVariant()}>
999+
<text fg={theme.textMuted}>·</text>
1000+
<text>
1001+
<span style={{ fg: theme.warning, bold: true }}>{local.model.variant.current()}</span>
1002+
</text>
1003+
</Show>
1004+
</box>
1005+
</Show>
1006+
</box>
1007+
<Show when={autoaccept()}>
1008+
<text>
1009+
<span style={{ fg: theme.warning, bold: true }}>auto-accept</span>
1010+
</text>
9931011
</Show>
9941012
</box>
9951013
</box>

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { createSimpleContext } from "./helper"
2525
import type { Snapshot } from "@/snapshot"
2626
import { useExit } from "./exit"
2727
import { useArgs } from "./args"
28+
import { useKV } from "./kv"
2829
import { batch, onMount } from "solid-js"
2930
import { Log } from "@/util/log"
3031
import type { Path } from "@opencode-ai/sdk"
@@ -103,6 +104,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
103104
})
104105

105106
const sdk = useSDK()
107+
const kv = useKV()
108+
const [autoaccept] = kv.signal("permission_auto_accept", false)
106109

107110
sdk.event.listen((e) => {
108111
const event = e.details
@@ -127,6 +130,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
127130

128131
case "permission.asked": {
129132
const request = event.properties
133+
if (autoaccept()) {
134+
sdk.client.permission.reply({
135+
reply: "once",
136+
requestID: request.id,
137+
})
138+
break
139+
}
130140
const requests = store.permission[request.sessionID]
131141
if (!requests) {
132142
setStore("permission", request.sessionID, [request])
@@ -423,6 +433,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
423433
get ready() {
424434
return store.status !== "loading"
425435
},
436+
426437
session: {
427438
get(sessionID: string) {
428439
const match = Binary.search(store.session, sessionID, (s) => s.id)

packages/opencode/src/config/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,12 @@ export namespace Config {
818818
command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
819819
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
820820
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
821-
agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
821+
agent_cycle_reverse: z.string().optional().default("none").describe("Previous agent"),
822+
permission_auto_accept_toggle: z
823+
.string()
824+
.optional()
825+
.default("shift+tab")
826+
.describe("Toggle auto-accept mode for permissions"),
822827
variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"),
823828
input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
824829
input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),

0 commit comments

Comments
 (0)