package com.claudecode.command.impl; import com.claudecode.command.CommandContext; import com.claudecode.command.SlashCommand; import com.claudecode.console.AnsiStyle; import com.claudecode.permission.PermissionRuleEngine; import com.claudecode.permission.PermissionSettings; import com.claudecode.permission.PermissionTypes.PermissionMode; import java.util.List; import java.util.Map; /** * /config 命令 —— 查看和设置应用配置。 *

* 支持查看当前配置、设置单个配置项,以及权限管理子命令。 */ public class ConfigCommand implements SlashCommand { /** 支持的配置项及说明 */ private static final Map CONFIG_KEYS = Map.of( "model", "AI model name (e.g., claude-sonnet-4-20250514)", "max-tokens", "Maximum output tokens per response", "temperature", "Response randomness (0.0-1.0)", "verbose", "Enable verbose logging (true/false)", "auto-compact", "Auto compact when context is large (true/false)", "permission-mode", "Permission mode: default/accept-edits/bypass/dont-ask", "permission-list", "List all saved permission rules", "permission-reset", "Clear all permission rules" ); private PermissionSettings permissionSettings; public ConfigCommand() {} public ConfigCommand(PermissionSettings permissionSettings) { this.permissionSettings = permissionSettings; } public void setPermissionSettings(PermissionSettings settings) { this.permissionSettings = settings; } @Override public String name() { return "config"; } @Override public String description() { return "View or set configuration"; } @Override public List aliases() { return List.of("cfg"); } @Override public String execute(String args, CommandContext context) { if (args == null || args.isBlank()) { return showAllConfig(context); } String[] parts = args.strip().split("\\s+", 2); String key = parts[0]; if (parts.length == 1) { // 显示单个配置项 return showConfig(key, context); } // 设置配置项 String value = parts[1]; return setConfig(key, value, context); } private String showAllConfig(CommandContext context) { StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append(AnsiStyle.bold(" ⚙ Configuration\n")); sb.append(" ").append("─".repeat(40)).append("\n\n"); // 当前活跃配置 String model = context.agentLoop().getTokenTracker().getModelName(); sb.append(" ").append(AnsiStyle.bold("model: ")).append(AnsiStyle.cyan(model)).append("\n"); String apiKey = System.getenv("ANTHROPIC_API_KEY"); if (apiKey != null && apiKey.length() > 8) { sb.append(" ").append(AnsiStyle.bold("api-key: ")).append(AnsiStyle.dim( apiKey.substring(0, 8) + "..." + apiKey.substring(apiKey.length() - 4))).append("\n"); } else { sb.append(" ").append(AnsiStyle.bold("api-key: ")).append(AnsiStyle.yellow("(not set)")).append("\n"); } String baseUrl = System.getenv().getOrDefault("ANTHROPIC_BASE_URL", "https://api.anthropic.com"); sb.append(" ").append(AnsiStyle.bold("base-url: ")).append(AnsiStyle.dim(baseUrl)).append("\n"); sb.append("\n"); sb.append(AnsiStyle.dim(" Available keys:\n")); for (var entry : CONFIG_KEYS.entrySet()) { sb.append(AnsiStyle.dim(" " + entry.getKey() + " — " + entry.getValue())).append("\n"); } sb.append("\n"); sb.append(AnsiStyle.dim(" Usage: /config ")); return sb.toString(); } private String setConfig(String key, String value, CommandContext context) { return switch (key) { case "model" -> { context.agentLoop().getTokenTracker().setModel(value); yield AnsiStyle.green(" ✅ Model set to: " + value) + "\n" + AnsiStyle.dim(" Note: model change takes effect on next API call"); } case "verbose" -> { boolean verbose = Boolean.parseBoolean(value); yield AnsiStyle.green(" ✅ Verbose mode: " + (verbose ? "ON" : "OFF")); } case "permission-mode" -> setPermissionMode(value); case "permission-list" -> listPermissionRules(); case "permission-reset" -> resetPermissionRules(); default -> { if (!CONFIG_KEYS.containsKey(key)) { yield AnsiStyle.yellow(" ⚠ Unknown config key: " + key); } yield AnsiStyle.yellow(" ⚠ Setting '" + key + "' is not yet supported at runtime") + "\n" + AnsiStyle.dim(" Set via application.yml or environment variables"); } }; } private String showConfig(String key, CommandContext context) { // 无参数的权限子命令 if (key.equals("permission-list")) return listPermissionRules(); if (key.equals("permission-reset")) return resetPermissionRules(); if (!CONFIG_KEYS.containsKey(key)) { return AnsiStyle.yellow(" ⚠ Unknown config key: " + key) + "\n" + AnsiStyle.dim(" Available: " + String.join(", ", CONFIG_KEYS.keySet())); } String desc = CONFIG_KEYS.get(key); return " " + AnsiStyle.bold(key) + ": " + AnsiStyle.dim(desc) + "\n" + AnsiStyle.dim(" Set with: /config " + key + " "); } // ── 权限管理子命令 ── private String setPermissionMode(String value) { if (permissionSettings == null) { return AnsiStyle.yellow(" ⚠ Permission settings not initialized"); } try { PermissionMode mode = switch (value.toLowerCase()) { case "default" -> PermissionMode.DEFAULT; case "accept-edits", "acceptedits" -> PermissionMode.ACCEPT_EDITS; case "bypass" -> PermissionMode.BYPASS; case "dont-ask", "dontask" -> PermissionMode.DONT_ASK; default -> throw new IllegalArgumentException(value); }; permissionSettings.setCurrentMode(mode); return AnsiStyle.green(" ✅ Permission mode set to: " + mode); } catch (IllegalArgumentException e) { return AnsiStyle.yellow(" ⚠ Unknown mode: " + value) + "\n" + AnsiStyle.dim(" Available: default, accept-edits, bypass, dont-ask"); } } private String listPermissionRules() { if (permissionSettings == null) { return AnsiStyle.yellow(" ⚠ Permission settings not initialized"); } var rules = permissionSettings.listRules(); if (rules.isEmpty()) { return AnsiStyle.dim(" No saved permission rules") + "\n" + AnsiStyle.dim(" Mode: " + permissionSettings.getCurrentMode()); } StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append(AnsiStyle.bold(" 🔒 Permission Rules")).append("\n"); sb.append(" ").append("─".repeat(40)).append("\n"); sb.append(" ").append(AnsiStyle.bold("Mode: ")).append(permissionSettings.getCurrentMode()).append("\n\n"); for (String rule : rules) { sb.append(" ").append(rule).append("\n"); } return sb.toString(); } private String resetPermissionRules() { if (permissionSettings == null) { return AnsiStyle.yellow(" ⚠ Permission settings not initialized"); } permissionSettings.clearAll(); return AnsiStyle.green(" ✅ All permission rules cleared"); } }