feat: add default permission mode + rename accept→edit

- 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) <noreply@anthropic.com>
This commit is contained in:
Yuyao Huang (Sam) 2026-03-30 01:54:32 +08:00
parent 9c04d47c8e
commit b707fa84f9
5 changed files with 37 additions and 33 deletions

View File

@ -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(

View File

@ -21,13 +21,14 @@ logger = logging.getLogger(__name__)
# Permission mode aliases (user-facing shorthand → internal CC mode)
_PERM_ALIASES: dict[str, str] = {
"bypass": "bypassPermissions",
"skip": "bypassPermissions",
"accept": "acceptEdits",
"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 <project_dir> [initial_message] [--timeout N] [--perm MODE]\nModes: bypass (default), accept, plan"
return "Usage: /new <project_dir> [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 <mode> [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 <mode> <conv_id>` 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 <n> - Switch to session by number
{P}perm <mode> [conv_id] - Set permission mode (bypass/accept/plan)
{P}perm <mode> [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 <cmd> - 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 自动执行一切操作
适合受信任的沙盒环境自动化任务"""

View File

@ -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 <dir> [msg] [--perm bypass|accept|plan] create session
{prefix}new <dir> [msg] [--perm default|edit|plan|bypass] create session
{prefix}close [n|conv_id] close session
{prefix}switch <n> switch active session
{prefix}perm <mode> [conv_id] permission mode: bypass (default), accept, plan
{prefix}perm <mode> [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

View File

@ -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

View File

@ -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