Polish permission inspection and switching output

Rework /permissions output into the same operator-console format used by status, config, and model so the command feels intentional and self-explanatory. Switching modes now reports previous and current state, while inspection shows the available modes and their meaning without adding fake policy logic.

Constraint: Permission output must stay aligned with the real three-mode runtime policy already implemented
Rejected: Add richer permission-policy previews per tool | would require more UI surface and risks overstating current policy fidelity
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep permission-mode docs in the CLI consistent with normalize_permission_mode and permission_policy behavior
Tested: cargo fmt --manifest-path ./rust/Cargo.toml --all; cargo clippy --manifest-path ./rust/Cargo.toml --workspace --all-targets -- -D warnings; cargo test --manifest-path ./rust/Cargo.toml --workspace
Not-tested: Manual operator UX review of /permissions flows in a live REPL
This commit is contained in:
Yeachan-Heo
2026-03-31 21:01:21 +00:00
parent 3db3dfa60d
commit d9c5f60598

View File

@@ -291,6 +291,26 @@ fn format_model_switch_report(previous: &str, next: &str, message_count: usize)
) )
} }
fn format_permissions_report(mode: &str) -> String {
format!(
"Permissions
Current mode {mode}
Available modes
read-only Allow read/search tools only
workspace-write Allow editing within the workspace
danger-full-access Allow unrestricted tool access"
)
}
fn format_permissions_switch_report(previous: &str, next: &str) -> String {
format!(
"Permissions updated
Previous {previous}
Current {next}"
)
}
fn run_resume_command( fn run_resume_command(
session_path: &Path, session_path: &Path,
session: &Session, session: &Session,
@@ -548,7 +568,7 @@ impl LiveCli {
fn set_permissions(&mut self, mode: Option<String>) -> Result<(), Box<dyn std::error::Error>> { fn set_permissions(&mut self, mode: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
let Some(mode) = mode else { let Some(mode) = mode else {
println!("Current permission mode: {}", permission_mode_label()); println!("{}", format_permissions_report(permission_mode_label()));
return Ok(()); return Ok(());
}; };
@@ -559,10 +579,11 @@ impl LiveCli {
})?; })?;
if normalized == permission_mode_label() { if normalized == permission_mode_label() {
println!("Permission mode already set to {normalized}."); println!("{}", format_permissions_report(normalized));
return Ok(()); return Ok(());
} }
let previous = permission_mode_label().to_string();
let session = self.runtime.session().clone(); let session = self.runtime.session().clone();
self.runtime = build_runtime_with_permission_mode( self.runtime = build_runtime_with_permission_mode(
session, session,
@@ -571,7 +592,10 @@ impl LiveCli {
true, true,
normalized, normalized,
)?; )?;
println!("Switched permission mode to {normalized}."); println!(
"{}",
format_permissions_switch_report(&previous, normalized)
);
Ok(()) Ok(())
} }
@@ -1234,10 +1258,10 @@ fn print_help() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{
format_model_report, format_model_switch_report, format_status_report, format_model_report, format_model_switch_report, format_permissions_report,
normalize_permission_mode, parse_args, render_init_claude_md, render_repl_help, format_permissions_switch_report, format_status_report, normalize_permission_mode,
resume_supported_slash_commands, status_context, CliAction, SlashCommand, StatusUsage, parse_args, render_init_claude_md, render_repl_help, resume_supported_slash_commands,
DEFAULT_MODEL, status_context, CliAction, SlashCommand, StatusUsage, DEFAULT_MODEL,
}; };
use runtime::{ContentBlock, ConversationMessage, MessageRole}; use runtime::{ContentBlock, ConversationMessage, MessageRole};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -1352,6 +1376,23 @@ mod tests {
); );
} }
#[test]
fn permissions_report_uses_sectioned_layout() {
let report = format_permissions_report("workspace-write");
assert!(report.contains("Permissions"));
assert!(report.contains("Current mode workspace-write"));
assert!(report.contains("Available modes"));
assert!(report.contains("danger-full-access"));
}
#[test]
fn permissions_switch_report_is_structured() {
let report = format_permissions_switch_report("read-only", "workspace-write");
assert!(report.contains("Permissions updated"));
assert!(report.contains("Previous read-only"));
assert!(report.contains("Current workspace-write"));
}
#[test] #[test]
fn model_report_uses_sectioned_layout() { fn model_report_uses_sectioned_layout() {
let report = format_model_report("claude-sonnet", 12, 4); let report = format_model_report("claude-sonnet", 12, 4);