From b707fa84f95f501772d2047cfad895e055af9105 Mon Sep 17 00:00:00 2001 From: "Yuyao Huang (Sam)" Date: Mon, 30 Mar 2026 01:54:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20default=20permission=20mode=20+?= =?UTF-8?q?=20rename=20accept=E2=86=92edit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add "default" permission mode: no flags passed to CC, uses its own built-in default (asks about everything). Now the system default. - Rename "accept" alias to "edit" (acceptEdits) for clarity - Remove "skip" alias (duplicate of "bypass") - Update all help text, error messages, tests, and LLM prompts Co-Authored-By: Claude Sonnet 4.6 (1M context) --- agent/cc_runner.py | 5 ++-- bot/commands.py | 39 +++++++++++++++------------- orchestrator/agent.py | 4 +-- orchestrator/tools.py | 4 +-- tests/features/commands/perm.feature | 18 ++++++------- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/agent/cc_runner.py b/agent/cc_runner.py index 9cca00b..769cd6e 100644 --- a/agent/cc_runner.py +++ b/agent/cc_runner.py @@ -18,12 +18,13 @@ def strip_ansi(text: str) -> str: PERMISSION_MODE_FLAGS: dict[str, list[str]] = { - "bypassPermissions": ["--dangerously-skip-permissions"], + "default": [], # CC's own default: asks about everything "acceptEdits": ["--permission-mode", "acceptEdits"], "plan": ["--permission-mode", "plan"], + "bypassPermissions": ["--dangerously-skip-permissions"], } VALID_PERMISSION_MODES = list(PERMISSION_MODE_FLAGS) -DEFAULT_PERMISSION_MODE = "bypassPermissions" +DEFAULT_PERMISSION_MODE = "default" async def run_claude( diff --git a/bot/commands.py b/bot/commands.py index 839ece9..eee1be6 100644 --- a/bot/commands.py +++ b/bot/commands.py @@ -20,14 +20,15 @@ logger = logging.getLogger(__name__) # Permission mode aliases (user-facing shorthand → internal CC mode) _PERM_ALIASES: dict[str, str] = { - "bypass": "bypassPermissions", - "skip": "bypassPermissions", - "accept": "acceptEdits", - "plan": "plan", + "bypass": "bypassPermissions", + "default": "default", + "edit": "acceptEdits", + "plan": "plan", } _PERM_LABELS: dict[str, str] = { + "default": "default", "bypassPermissions": "bypass", - "acceptEdits": "accept", + "acceptEdits": "edit", "plan": "plan", } @@ -113,14 +114,14 @@ async def handle_command(user_id: str, text: str) -> Optional[str]: async def _cmd_new(user_id: str, args: str) -> str: """Create a new session.""" if not args: - return "Usage: /new [initial_message] [--timeout N] [--perm MODE]\nModes: bypass (default), accept, plan" + return "Usage: /new [initial_message] [--timeout N] [--perm MODE]\nModes: default, edit, plan, bypass" parser = argparse.ArgumentParser() parser.add_argument("working_dir", nargs="?", help="Project directory") parser.add_argument("rest", nargs="*", help="Initial message") parser.add_argument("--timeout", type=int, default=None, help="CC timeout in seconds") parser.add_argument("--idle", type=int, default=None, help="Idle timeout in seconds") - parser.add_argument("--perm", default=None, help="Permission mode: bypass, accept, plan") + parser.add_argument("--perm", default=None, help="Permission mode: default, edit, plan, bypass") try: parsed = parser.parse_args(args.split()) @@ -132,7 +133,7 @@ async def _cmd_new(user_id: str, args: str) -> str: permission_mode = _resolve_perm(parsed.perm) if parsed.perm else DEFAULT_PERMISSION_MODE if permission_mode is None: - return f"Invalid --perm. Valid modes: bypass, accept, plan" + return f"Invalid --perm. Valid modes: default, edit, plan, bypass" working_dir = parsed.working_dir initial_msg = " ".join(parsed.rest) if parsed.rest else None @@ -288,10 +289,11 @@ async def _cmd_perm(user_id: str, args: str) -> str: if not parts: return ( "Usage: /perm [conv_id]\n" - "Modes: bypass (default), accept, plan\n" - " bypass — skip all permission checks\n" - " accept — auto-accept file edits, confirm shell commands\n" - " plan — plan only, no writes" + "Modes: default, edit, plan, bypass\n" + " default — default mode\n" + " edit — auto-accept file edits, confirm shell commands\n" + " plan — plan only, no writes\n" + " bypass — skip all permission checks" ) alias = parts[0] @@ -299,7 +301,7 @@ async def _cmd_perm(user_id: str, args: str) -> str: permission_mode = _resolve_perm(alias) if permission_mode is None: - return f"Unknown mode '{alias}'. Valid: bypass, accept, plan" + return f"Unknown mode '{alias}'. Valid: default, edit, plan, bypass" if not conv_id: return "No active session. Use `//perm ` or activate a session first." @@ -442,7 +444,7 @@ def _cmd_help() -> str: {P}status - Show sessions and current mode {P}close [n] - Close session (active or by number) {P}switch - Switch to session by number -{P}perm [conv_id] - Set permission mode (bypass/accept/plan) +{P}perm [conv_id] - Set permission mode (default/edit/plan/bypass) {P}direct - Direct mode: messages → Claude Code (no LLM overhead) {P}smart - Smart mode: messages → LLM routing (default) {P}shell - Run shell command (bypasses LLM) @@ -454,9 +456,10 @@ def _cmd_help() -> str: {P}help - Show this help **Permission modes** (used by {P}perm and {P}new --perm): - bypass — 跳过所有权限确认,CC 自动执行一切操作(默认) - 适合:受信任的沙盒环境、自动化任务 - accept — 自动接受文件编辑,但 shell 命令仍需手动确认 + default — 默认模式,遇到文件编辑操作时手动确认 + edit — 自动接受文件编辑,但 shell 命令仍需手动确认 适合:日常开发,需要对命令执行保持控制 plan — 只规划、不执行任何写操作 - 适合:先预览 CC 的操作计划再决定是否执行""" + 适合:先预览 CC 的操作计划再决定是否执行 + bypass — 跳过所有权限确认,CC 自动执行一切操作 + 适合:受信任的沙盒环境、自动化任务""" \ No newline at end of file diff --git a/orchestrator/agent.py b/orchestrator/agent.py index d09f037..37a7de3 100644 --- a/orchestrator/agent.py +++ b/orchestrator/agent.py @@ -53,10 +53,10 @@ Bot command prefix: {prefix} - Any other command the user would type manually Available bot commands (pass verbatim to run_command): - {prefix}new [msg] [--perm bypass|accept|plan] — create session + {prefix}new [msg] [--perm default|edit|plan|bypass] — create session {prefix}close [n|conv_id] — close session {prefix}switch — switch active session - {prefix}perm [conv_id] — permission mode: bypass (default), accept, plan + {prefix}perm [conv_id] — permission mode: default, edit, plan, bypass {prefix}direct — direct mode (bypass LLM for CC messages) {prefix}smart — smart mode (LLM routing, default) {prefix}status — list sessions diff --git a/orchestrator/tools.py b/orchestrator/tools.py index f64632b..d2226ad 100644 --- a/orchestrator/tools.py +++ b/orchestrator/tools.py @@ -720,7 +720,7 @@ class RunCommandInput(BaseModel): command: str = Field( ..., description=( - "A bot slash command to execute (e.g. '/perm accept', '/close 1', '/switch 2'). " + "A bot slash command to execute (e.g. '//perm edit', '//close 1', '//switch 2'). " "This runs bot control commands — NOT shell commands on the host machine. " "Use run_shell for host shell commands (git, ls, etc.)." ), @@ -732,7 +732,7 @@ class RunCommandTool(BaseTool): description: str = ( "Execute a PhoneWork bot slash command on behalf of the user. " "Use this to control sessions, switch modes, change permissions, etc. " - "Examples: '/perm accept', '/close 1', '/switch 2', '/direct', '/smart', '/status'. " + "Examples: '/perm edit', '/close 1', '/switch 2', '/direct', '/smart', '/status'. " "Do NOT use this for shell commands — use run_shell for those." ) args_schema: Type[BaseModel] = RunCommandInput diff --git a/tests/features/commands/perm.feature b/tests/features/commands/perm.feature index 4882344..82b53ef 100644 --- a/tests/features/commands/perm.feature +++ b/tests/features/commands/perm.feature @@ -7,14 +7,14 @@ Feature: /perm command — change session permission mode When user sends "/perm" Then reply contains "Usage" And reply contains "bypass" - And reply contains "accept" + And reply contains "edit" And reply contains "plan" - Scenario: Set active session to accept mode + Scenario: Set active session to edit mode Given user has session "sess01" in "/tmp/proj1" And active session is "sess01" - When user sends "/perm accept" - Then reply contains "accept" + When user sends "/perm edit" + Then reply contains "edit" And reply contains "sess01" And session "sess01" has permission mode "acceptEdits" @@ -40,7 +40,7 @@ Feature: /perm command — change session permission mode Scenario: No active session returns error Given no active session for user "user_abc123" - When user sends "/perm accept" + When user sends "/perm edit" Then reply contains "No active session" Scenario: Set permission on specific conv_id @@ -53,12 +53,12 @@ Feature: /perm command — change session permission mode Scenario: Cannot change permission of another user's session Given session "sess01" in "/tmp/proj1" belongs to user "other_user" - When user sends "/perm accept sess01" + When user sends "/perm edit sess01" Then reply contains "another user" - Scenario: New session with --perm accept - When user sends "/new myproject --perm accept" - Then reply contains "accept" + Scenario: New session with --perm edit + When user sends "/new myproject --perm edit" + Then reply contains "edit" And session manager has 1 session for user "user_abc123" Scenario: New session with --perm plan