| 1 | //! Config command area: settings, modes, themes, trust, and status surfaces. |
| 2 | |
| 3 | // This group dir intentionally has a `config.rs` child module with the same |
| 4 | // name. The module_inception allow is a permanent structure rationale, not |
| 5 | // migration scaffolding; see docs/architecture/command-dispatch.md. |
| 6 | #[allow(clippy::module_inception)] |
| 7 | pub mod config; |
| 8 | mod permissions; |
| 9 | mod status; |
| 10 | |
| 11 | use crate::commands::CommandResult; |
| 12 | use crate::commands::traits::{Command, CommandGroup, CommandInfo, FunctionCommand}; |
| 13 | use crate::localization::MessageId; |
| 14 | use crate::tui::app::App; |
| 15 | |
| 16 | pub struct ConfigCommands; |
| 17 | |
| 18 | impl CommandGroup for ConfigCommands { |
| 19 | fn commands(&self) -> &'static [Box<dyn Command>] { |
| 20 | cached_command_list!(vec![ |
| 21 | Box::new(FunctionCommand::new(&CONFIG_INFO, run_config)), |
| 22 | Box::new(FunctionCommand::new(&PERMISSIONS_INFO, run_permissions)), |
| 23 | Box::new(FunctionCommand::new(&AUTH_INFO, run_auth)), |
| 24 | Box::new(FunctionCommand::new(&RAIL_INFO, run_rail)), |
| 25 | Box::new(FunctionCommand::new(&SETTINGS_INFO, run_settings)), |
| 26 | Box::new(FunctionCommand::new(&STATUS_INFO, run_status)), |
| 27 | Box::new(FunctionCommand::new(&STATUSLINE_INFO, run_statusline)), |
| 28 | Box::new(FunctionCommand::new(&MODE_INFO, run_mode)), |
| 29 | Box::new(FunctionCommand::new(&THEME_INFO, run_theme)), |
| 30 | Box::new(FunctionCommand::new(&VERBOSE_INFO, run_verbose)), |
| 31 | Box::new(FunctionCommand::new(&TRUST_INFO, run_trust)), |
| 32 | Box::new(FunctionCommand::new(&LOGOUT_INFO, run_logout)), |
| 33 | ]) |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | static CONFIG_INFO: CommandInfo = CommandInfo { |
| 38 | name: "config", |
| 39 | // /experiments is a discoverable entry to the same view: the Experimental |
| 40 | // section exposes the Workflow, goal, and sub-agent opt-ins (#3182). |
| 41 | aliases: &["experiments", "experimental"], |
| 42 | usage: "/config [ask-rules|status|<key> [value]]", |
| 43 | description_id: MessageId::CmdConfigDescription, |
| 44 | }; |
| 45 | static PERMISSIONS_INFO: CommandInfo = CommandInfo { |
| 46 | name: "permissions", |
| 47 | aliases: &["permission-rules", "permission_rules"], |
| 48 | usage: "/permissions [list|remove <rule-number> [--confirm <token>]]", |
| 49 | description_id: MessageId::CmdPermissionsDescription, |
| 50 | }; |
| 51 | static AUTH_INFO: CommandInfo = CommandInfo { |
| 52 | name: "auth", |
| 53 | aliases: &[], |
| 54 | usage: "/auth xai-device", |
| 55 | description_id: MessageId::CmdAuthDescription, |
| 56 | }; |
| 57 | static RAIL_INFO: CommandInfo = CommandInfo { |
| 58 | name: "rail", |
| 59 | // /sidebar is the name users already know; it now drives the one rail. |
| 60 | aliases: &["sidebar"], |
| 61 | usage: "/rail [top|left|right|off|tasks|agents|context|pinned] [--save]", |
| 62 | description_id: MessageId::CmdSidebarDescription, |
| 63 | }; |
| 64 | static SETTINGS_INFO: CommandInfo = CommandInfo { |
| 65 | name: "settings", |
| 66 | aliases: &[], |
| 67 | usage: "/settings [text]", |
| 68 | description_id: MessageId::CmdSettingsDescription, |
| 69 | }; |
| 70 | static STATUS_INFO: CommandInfo = CommandInfo { |
| 71 | name: "status", |
| 72 | aliases: &[], |
| 73 | usage: "/status", |
| 74 | description_id: MessageId::CmdStatusDescription, |
| 75 | }; |
| 76 | static STATUSLINE_INFO: CommandInfo = CommandInfo { |
| 77 | name: "statusline", |
| 78 | aliases: &[], |
| 79 | usage: "/statusline", |
| 80 | description_id: MessageId::CmdStatuslineDescription, |
| 81 | }; |
| 82 | static MODE_INFO: CommandInfo = CommandInfo { |
| 83 | name: "mode", |
| 84 | aliases: &["jihua", "zidong"], |
| 85 | usage: "/mode [act|plan|operate|1|2|3]", |
| 86 | description_id: MessageId::CmdModeDescription, |
| 87 | }; |
| 88 | static THEME_INFO: CommandInfo = CommandInfo { |
| 89 | name: "theme", |
| 90 | aliases: &[], |
| 91 | usage: "/theme [name|custom:<name>|schema|path]", |
| 92 | description_id: MessageId::CmdThemeDescription, |
| 93 | }; |
| 94 | static VERBOSE_INFO: CommandInfo = CommandInfo { |
| 95 | name: "verbose", |
| 96 | aliases: &[], |
| 97 | usage: "/verbose [on|off]", |
| 98 | description_id: MessageId::CmdVerboseDescription, |
| 99 | }; |
| 100 | static TRUST_INFO: CommandInfo = CommandInfo { |
| 101 | name: "trust", |
| 102 | aliases: &["xinren"], |
| 103 | usage: "/trust [on|off|add <path>|remove <path>|list]", |
| 104 | description_id: MessageId::CmdTrustDescription, |
| 105 | }; |
| 106 | static LOGOUT_INFO: CommandInfo = CommandInfo { |
| 107 | name: "logout", |
| 108 | aliases: &[], |
| 109 | usage: "/logout", |
| 110 | description_id: MessageId::CmdLogoutDescription, |
| 111 | }; |
| 112 | fn run_registered(app: &mut App, name: &str, arg: Option<&str>) -> CommandResult { |
| 113 | dispatch(app, name, arg).expect("registered config command should dispatch") |
| 114 | } |
| 115 | |
| 116 | fn run_config(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 117 | run_registered(app, "config", arg) |
| 118 | } |
| 119 | fn run_permissions(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 120 | run_registered(app, "permissions", arg) |
| 121 | } |
| 122 | fn run_auth(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 123 | run_registered(app, "auth", arg) |
| 124 | } |
| 125 | fn run_rail(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 126 | run_registered(app, "rail", arg) |
| 127 | } |
| 128 | fn run_settings(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 129 | run_registered(app, "settings", arg) |
| 130 | } |
| 131 | fn run_status(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 132 | run_registered(app, "status", arg) |
| 133 | } |
| 134 | fn run_statusline(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 135 | run_registered(app, "statusline", arg) |
| 136 | } |
| 137 | fn run_mode(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 138 | run_registered(app, "mode", arg) |
| 139 | } |
| 140 | fn run_theme(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 141 | run_registered(app, "theme", arg) |
| 142 | } |
| 143 | fn run_verbose(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 144 | run_registered(app, "verbose", arg) |
| 145 | } |
| 146 | fn run_trust(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 147 | run_registered(app, "trust", arg) |
| 148 | } |
| 149 | fn run_logout(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 150 | run_registered(app, "logout", arg) |
| 151 | } |
| 152 | pub(in crate::commands) fn dispatch( |
| 153 | app: &mut App, |
| 154 | command: &str, |
| 155 | arg: Option<&str>, |
| 156 | ) -> Option<CommandResult> { |
| 157 | let result = match command { |
| 158 | "config" | "experiments" | "experimental" => config::config_command(app, arg), |
| 159 | "permissions" | "permission-rules" | "permission_rules" => { |
| 160 | permissions::permissions_command(app, arg) |
| 161 | } |
| 162 | "auth" => match arg.map(str::trim) { |
| 163 | Some("xai-device") | Some("xai_device") => { |
| 164 | CommandResult::action(crate::tui::app::AppAction::StartXaiDeviceLogin) |
| 165 | } |
| 166 | _ => CommandResult::error("Usage: /auth xai-device"), |
| 167 | }, |
| 168 | "rail" | "sidebar" => config::sidebar(app, arg), |
| 169 | "settings" => config::settings_command(app, arg), |
| 170 | "status" => status::status(app), |
| 171 | "statusline" => config::status_line(app), |
| 172 | "mode" => config::mode(app, arg), |
| 173 | "jihua" => config::mode(app, Some("plan")), |
| 174 | "zidong" => config::mode(app, Some("yolo")), |
| 175 | "theme" => config::theme(app, arg), |
| 176 | "verbose" => config::verbose(app, arg), |
| 177 | "trust" | "xinren" => config::trust(app, arg), |
| 178 | "logout" => config::logout(app), |
| 179 | _ => return None, |
| 180 | }; |
| 181 | Some(result) |
| 182 | } |
| 183 |