Skip to content

Commit 8654032

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

6 files changed

Lines changed: 37 additions & 7 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
@@ -109,6 +109,7 @@ export function Prompt(props: PromptProps) {
109109
const dimensions = useTerminalDimensions()
110110
const { theme, syntax } = useTheme()
111111
const kv = useKV()
112+
const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
112113
const animationsEnabled = createMemo(() => kv.get("animations_enabled", true))
113114
const list = createMemo(() => props.placeholders?.normal ?? [])
114115
const shell = createMemo(() => props.placeholders?.shell ?? [])
@@ -136,7 +137,7 @@ export function Prompt(props: PromptProps) {
136137
})
137138
const [auto, setAuto] = createSignal<AutocompleteRef>()
138139
const currentProviderLabel = createMemo(() => local.model.parsed().provider)
139-
const hasRightContent = createMemo(() => Boolean(props.right))
140+
const hasRightContent = createMemo(() => Boolean(props.right) || autoaccept() === "edit")
140141

141142
function promptModelWarning() {
142143
toast.show({
@@ -253,6 +254,17 @@ export function Prompt(props: PromptProps) {
253254

254255
command.register(() => {
255256
return [
257+
{
258+
title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
259+
value: "permission.auto_accept.toggle",
260+
search: "toggle permissions",
261+
keybind: "permission_auto_accept_toggle",
262+
category: "Agent",
263+
onSelect: (dialog) => {
264+
setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
265+
dialog.clear()
266+
},
267+
},
256268
{
257269
title: "Clear prompt",
258270
value: "prompt.clear",
@@ -1271,6 +1283,11 @@ export function Prompt(props: PromptProps) {
12711283
<Show when={hasRightContent()}>
12721284
<box flexDirection="row" gap={1} alignItems="center">
12731285
{props.right}
1286+
<Show when={autoaccept() === "edit"}>
1287+
<text>
1288+
<span style={{ fg: theme.warning }}>autoedit</span>
1289+
</text>
1290+
</Show>
12741291
</box>
12751292
</Show>
12761293
</box>

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ export function Session() {
605605
{
606606
title: sidebarVisible() ? "Hide sidebar" : "Show sidebar",
607607
value: "session.sidebar.toggle",
608+
search: "toggle sidebar",
608609
keybind: "sidebar_toggle",
609610
category: "Session",
610611
onSelect: (dialog) => {
@@ -620,6 +621,7 @@ export function Session() {
620621
title: conceal() ? "Disable code concealment" : "Enable code concealment",
621622
value: "session.toggle.conceal",
622623
keybind: "messages_toggle_conceal",
624+
search: "toggle code concealment",
623625
category: "Session",
624626
onSelect: (dialog) => {
625627
setConceal((prev) => !prev)
@@ -629,6 +631,7 @@ export function Session() {
629631
{
630632
title: showTimestamps() ? "Hide timestamps" : "Show timestamps",
631633
value: "session.toggle.timestamps",
634+
search: "toggle timestamps",
632635
category: "Session",
633636
slash: {
634637
name: "timestamps",
@@ -642,6 +645,7 @@ export function Session() {
642645
{
643646
title: showThinking() ? "Hide thinking" : "Show thinking",
644647
value: "session.toggle.thinking",
648+
search: "toggle thinking",
645649
keybind: "display_thinking",
646650
category: "Session",
647651
slash: {
@@ -656,6 +660,7 @@ export function Session() {
656660
{
657661
title: showDetails() ? "Hide tool details" : "Show tool details",
658662
value: "session.toggle.actions",
663+
search: "toggle tool details",
659664
keybind: "tool_details",
660665
category: "Session",
661666
onSelect: (dialog) => {
@@ -664,8 +669,9 @@ export function Session() {
664669
},
665670
},
666671
{
667-
title: "Toggle session scrollbar",
672+
title: showScrollbar() ? "Hide session scrollbar" : "Show session scrollbar",
668673
value: "session.toggle.scrollbar",
674+
search: "toggle session scrollbar",
669675
keybind: "scrollbar_toggle",
670676
category: "Session",
671677
onSelect: (dialog) => {

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/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)