Przeglądaj źródła

Apply PR #12633: feat(tui): add auto-accept mode for permission requests

opencode-agent[bot] 2 dni temu
rodzic
commit
a469162a4f

+ 1 - 0
packages/opencode/src/agent/agent.ts

@@ -94,6 +94,7 @@ export const layer = Layer.effect(
           question: "deny",
           plan_enter: "deny",
           plan_exit: "deny",
+          edit: "ask",
           // mirrors github.com/github/gitignore Node.gitignore pattern for .env files
           read: {
             "*": "allow",

+ 5 - 0
packages/opencode/src/cli/cmd/run.ts

@@ -367,6 +367,11 @@ export const RunCommand = cmd({
         action: "deny",
         pattern: "*",
       },
+      {
+        permission: "edit",
+        action: "allow",
+        pattern: "*",
+      },
     ]
 
     function title() {

+ 7 - 0
packages/opencode/src/cli/cmd/tui/app.tsx

@@ -502,6 +502,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     {
       title: "Toggle MCPs",
       value: "mcp.list",
+      search: "toggle mcps",
       category: "Agent",
       slash: {
         name: "mcps",
@@ -608,6 +609,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     {
       title: mode() === "dark" ? "Switch to light mode" : "Switch to dark mode",
       value: "theme.switch_mode",
+      search: "toggle appearance",
       onSelect: (dialog) => {
         setMode(mode() === "dark" ? "light" : "dark")
         dialog.clear()
@@ -656,6 +658,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     },
     {
       title: "Toggle debug panel",
+      search: "toggle debug",
       category: "System",
       value: "app.debug",
       onSelect: (dialog) => {
@@ -665,6 +668,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     },
     {
       title: "Toggle console",
+      search: "toggle console",
       category: "System",
       value: "app.console",
       onSelect: (dialog) => {
@@ -706,6 +710,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     {
       title: terminalTitleEnabled() ? "Disable terminal title" : "Enable terminal title",
       value: "terminal.title.toggle",
+      search: "toggle terminal title",
       keybind: "terminal_title_toggle",
       category: "System",
       onSelect: (dialog) => {
@@ -721,6 +726,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     {
       title: kv.get("animations_enabled", true) ? "Disable animations" : "Enable animations",
       value: "app.toggle.animations",
+      search: "toggle animations",
       category: "System",
       onSelect: (dialog) => {
         kv.set("animations_enabled", !kv.get("animations_enabled", true))
@@ -730,6 +736,7 @@ function App(props: { onSnapshot?: () => Promise<string[]> }) {
     {
       title: kv.get("diff_wrap_mode", "word") === "word" ? "Disable diff wrapping" : "Enable diff wrapping",
       value: "app.toggle.diffwrap",
+      search: "toggle diff wrapping",
       category: "System",
       onSelect: (dialog) => {
         const current = kv.get("diff_wrap_mode", "word")

+ 18 - 1
packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

@@ -112,6 +112,7 @@ export function Prompt(props: PromptProps) {
   const [auto, setAuto] = createSignal<AutocompleteRef>()
   const currentProviderLabel = createMemo(() => local.model.parsed().provider)
   const hasRightContent = createMemo(() => Boolean(props.right))
+  const [autoaccept, setAutoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
 
   function promptModelWarning() {
     toast.show({
@@ -228,6 +229,17 @@ export function Prompt(props: PromptProps) {
 
   command.register(() => {
     return [
+      {
+        title: autoaccept() === "none" ? "Enable autoedit" : "Disable autoedit",
+        value: "permission.auto_accept.toggle",
+        search: "toggle permissions",
+        keybind: "permission_auto_accept_toggle",
+        category: "Agent",
+        onSelect: (dialog) => {
+          setAutoaccept(() => (autoaccept() === "none" ? "edit" : "none"))
+          dialog.clear()
+        },
+      },
       {
         title: "Clear prompt",
         value: "prompt.clear",
@@ -1221,8 +1233,13 @@ export function Prompt(props: PromptProps) {
                   )}
                 </Show>
               </box>
-              <Show when={hasRightContent()}>
+              <Show when={hasRightContent() || autoaccept() === "edit"}>
                 <box flexDirection="row" gap={1} alignItems="center">
+                  <Show when={autoaccept() === "edit"}>
+                    <text>
+                      <span style={{ fg: theme.warning }}>autoedit</span>
+                    </text>
+                  </Show>
                   {props.right}
                 </box>
               </Show>

+ 10 - 0
packages/opencode/src/cli/cmd/tui/context/sync.tsx

@@ -27,6 +27,7 @@ import { createSimpleContext } from "./helper"
 import type { Snapshot } from "@/snapshot"
 import { useExit } from "./exit"
 import { useArgs } from "./args"
+import { useKV } from "./kv"
 import { batch, onMount } from "solid-js"
 import { Log } from "@/util"
 import { emptyConsoleState, type ConsoleState } from "@/config/console-state"
@@ -107,6 +108,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
     const event = useEvent()
     const project = useProject()
     const sdk = useSDK()
+    const kv = useKV()
+    const [autoaccept] = kv.signal<"none" | "edit">("permission_auto_accept", "edit")
 
     const fullSyncedSessions = new Set<string>()
     let syncedWorkspace = project.workspace.current()
@@ -133,6 +136,13 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
 
         case "permission.asked": {
           const request = event.properties
+          if (autoaccept() === "edit" && request.permission === "edit") {
+            sdk.client.permission.reply({
+              reply: "once",
+              requestID: request.id,
+            })
+            break
+          }
           const requests = store.permission[request.sessionID]
           if (!requests) {
             setStore("permission", request.sessionID, [request])

+ 6 - 1
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx

@@ -591,6 +591,7 @@ export function Session() {
     {
       title: sidebarVisible() ? "Hide sidebar" : "Show sidebar",
       value: "session.sidebar.toggle",
+      search: "toggle sidebar",
       keybind: "sidebar_toggle",
       category: "Session",
       onSelect: (dialog) => {
@@ -615,6 +616,7 @@ export function Session() {
     {
       title: showTimestamps() ? "Hide timestamps" : "Show timestamps",
       value: "session.toggle.timestamps",
+      search: "toggle timestamps",
       category: "Session",
       slash: {
         name: "timestamps",
@@ -628,6 +630,7 @@ export function Session() {
     {
       title: showThinking() ? "Hide thinking" : "Show thinking",
       value: "session.toggle.thinking",
+      search: "toggle thinking",
       keybind: "display_thinking",
       category: "Session",
       slash: {
@@ -642,6 +645,7 @@ export function Session() {
     {
       title: showDetails() ? "Hide tool details" : "Show tool details",
       value: "session.toggle.actions",
+      search: "toggle tool details",
       keybind: "tool_details",
       category: "Session",
       onSelect: (dialog) => {
@@ -650,8 +654,9 @@ export function Session() {
       },
     },
     {
-      title: "Toggle session scrollbar",
+      title: showScrollbar() ? "Hide session scrollbar" : "Show session scrollbar",
       value: "session.toggle.scrollbar",
+      search: "toggle session scrollbar",
       keybind: "scrollbar_toggle",
       category: "Session",
       onSelect: (dialog) => {

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

@@ -37,6 +37,7 @@ export interface DialogSelectOption<T = any> {
   title: string
   value: T
   description?: string
+  search?: string
   footer?: JSX.Element | string
   category?: string
   categoryView?: JSX.Element
@@ -93,8 +94,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
     // users typically search by the item name, and not its category.
     const result = fuzzysort
       .go(needle, options, {
-        keys: ["title", "category"],
-        scoreFn: (r) => r[0].score * 2 + r[1].score,
+        keys: ["title", "category", "search"],
+        scoreFn: (r) => r[0].score * 2 + r[1].score + r[2].score,
       })
       .map((x) => x.obj)
 

+ 3 - 3
packages/opencode/test/agent/agent.test.ts

@@ -47,7 +47,7 @@ test("build agent has correct default properties", async () => {
       expect(build).toBeDefined()
       expect(build?.mode).toBe("primary")
       expect(build?.native).toBe(true)
-      expect(evalPerm(build, "edit")).toBe("allow")
+      expect(evalPerm(build, "edit")).toBe("ask")
       expect(evalPerm(build, "bash")).toBe("allow")
     },
   })
@@ -224,8 +224,8 @@ test("agent permission config merges with defaults", async () => {
       expect(build).toBeDefined()
       // Specific pattern is denied
       expect(Permission.evaluate("bash", "rm -rf *", build!.permission).action).toBe("deny")
-      // Edit still allowed
-      expect(evalPerm(build, "edit")).toBe("allow")
+      // Edit still asks (default behavior)
+      expect(evalPerm(build, "edit")).toBe("ask")
     },
   })
 })