فهرست منبع

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

opencode-agent[bot] 1 روز پیش
والد
کامیت
6019bb5233

+ 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() {

+ 22 - 5
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,11 +1233,16 @@ export function Prompt(props: PromptProps) {
                   )}
                 </Show>
               </box>
-              <Show when={hasRightContent()}>
-                <box flexDirection="row" gap={1} alignItems="center">
-                  {props.right}
-                </box>
-              </Show>
+              <box flexDirection="row" gap={1} alignItems="center">
+                <Show when={autoaccept() === "edit"}>
+                  <text>
+                    <span style={{ fg: theme.warning }}>autoedit</span>
+                  </text>
+                </Show>
+                <Show when={hasRightContent()}>
+                  <>{props.right}</>
+                </Show>
+              </box>
             </box>
           </box>
         </box>

+ 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") {
+            void sdk.client.permission.reply({
+              reply: "once",
+              requestID: request.id,
+            })
+            break
+          }
           const requests = store.permission[request.sessionID]
           if (!requests) {
             setStore("permission", request.sessionID, [request])

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

+ 2 - 1
packages/opencode/src/config/keybinds.ts

@@ -61,7 +61,8 @@ const KeybindsSchema = Schema.Struct({
   command_list: keybind("ctrl+p", "List available commands"),
   agent_list: keybind("<leader>a", "List agents"),
   agent_cycle: keybind("tab", "Next agent"),
-  agent_cycle_reverse: keybind("shift+tab", "Previous agent"),
+  agent_cycle_reverse: keybind("none", "Previous agent"),
+  permission_auto_accept_toggle: keybind("shift+tab", "Toggle auto-accept mode for permissions"),
   variant_cycle: keybind("ctrl+t", "Cycle model variants"),
   variant_list: keybind("none", "List model variants"),
   input_clear: keybind("ctrl+c", "Clear input field"),

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

@@ -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")
     },
   })
 })