diff --git a/README.md b/README.md index 6efb7004..bc486651 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ TUI shows live GPU utilization, active model servers, and a chat tab. | Command | Description | |---|---| | `rocm` | Open the TUI (runs setup on first launch) | -| `rocm doctor` | Check GPU, ROCm install, engines, and managed folders | +| `rocm examine` | Check GPU, ROCm install, engines, and managed folders | | `rocm install sdk` | Install TheRock ROCm wheels into a managed Python environment | | `rocm install driver` | Install the AMD kernel driver on Linux | | `rocm serve ` | Start a local OpenAI-compatible model server | diff --git a/apps/rocm/src/main.rs b/apps/rocm/src/main.rs index c20d3df1..03516237 100644 --- a/apps/rocm/src/main.rs +++ b/apps/rocm/src/main.rs @@ -19,7 +19,7 @@ use clap::{Parser, Subcommand, ValueEnum}; use rocm_core::{ AppPaths, AuditEventRecord, AutomationEventRecord, AutomationProposalRecord, AutomationRuntimeState, CodexBridgeEngine, CodexBridgeGpuSnapshot, CodexBridgeSnapshot, - DEFAULT_LOCAL_HOST, DoctorSummary, ManagedServiceRecord, ModelRecipeRecord, + DEFAULT_LOCAL_HOST, ExamineSummary, ManagedServiceRecord, ModelRecipeRecord, ModelRecipeRegistry, ModelRecipeRegistrySource, RocmCliConfig, TELEMETRY_MODE_LOCAL, TELEMETRY_MODE_OFF, WatcherMode, append_audit_event, builtin_model_recipes, builtin_watcher, builtin_watchers, connect_tcp_stream, daemon_binary_path, default_engine_for_platform, @@ -65,7 +65,7 @@ struct Cli { #[derive(Subcommand, Debug)] enum Command { /// Check this computer's GPU, ROCm install, engines, and setup folders. - Doctor, + Examine, /// Print the rocm-cli version. Version, #[command(hide = true)] @@ -876,8 +876,8 @@ fn render_freeform_read_only_answer( return Ok(None); } match plan.actions[0].args.as_slice() { - [command] if command == "doctor" => { - render_freeform_doctor_answer(request, paths, config).map(Some) + [command] if command == "examine" => { + render_freeform_examine_answer(request, paths, config).map(Some) } [command, subcommand] if command == "comfyui" && subcommand == "status" => { render_freeform_comfyui_status_answer(paths, config).map(Some) @@ -890,13 +890,13 @@ fn render_freeform_read_only_answer( } } -fn render_freeform_doctor_answer( +fn render_freeform_examine_answer( request: &str, paths: &AppPaths, config: &RocmCliConfig, ) -> Result { recover_setup_runtime_registration(paths, config)?; - let doctor = DoctorSummary::gather()?; + let examine = ExamineSummary::gather()?; let manifests = therock::load_runtime_manifests(paths)?; let active = current_runtime_manifest(config, &manifests); let lower = request.to_ascii_lowercase(); @@ -913,14 +913,14 @@ fn render_freeform_doctor_answer( ); let _ = writeln!(output); - if let Some(detail) = doctor.driver.detail.as_deref() { + if let Some(detail) = examine.driver.detail.as_deref() { let _ = writeln!(output, "GPU: {detail}"); - } else if let Some(target) = doctor.detected_gfx_target.as_deref() { + } else if let Some(target) = examine.detected_gfx_target.as_deref() { let _ = writeln!(output, "GPU: AMD GPU target {target}"); } else { let _ = writeln!(output, "GPU: I could not identify an AMD GPU yet."); } - if let Some(target) = doctor.detected_gfx_target.as_deref() { + if let Some(target) = examine.detected_gfx_target.as_deref() { let _ = writeln!(output, "Target: {target}"); } @@ -974,7 +974,7 @@ fn render_freeform_doctor_answer( } } - if doctor.legacy_rocm.status == "not_detected" && active.is_some() { + if examine.legacy_rocm.status == "not_detected" && active.is_some() { let _ = writeln!( output, "Note: ROCm CLI is using its managed TheRock runtime, not a global ROCm install." @@ -1035,7 +1035,7 @@ fn dispatch(cli: Cli) -> Result<()> { } match cli.command { - Some(Command::Doctor) => doctor(), + Some(Command::Examine) => examine(), Some(Command::Version) => { println!("rocm {}", env!("CARGO_PKG_VERSION")); Ok(()) @@ -1341,7 +1341,7 @@ fn build_codex_bridge_snapshot(paths: &AppPaths) -> Result Ok(CodexBridgeSnapshot { protocol: "rocmd-codex-bridge-v0".to_owned(), generated_at_unix_ms: rocm_core::unix_time_millis(), - doctor: DoctorSummary::gather()?, + examine: ExamineSummary::gather()?, gpu: build_codex_bridge_gpu_snapshot(&config), config, automation_runtime: AutomationRuntimeState::load(paths)?, @@ -1365,7 +1365,7 @@ fn build_codex_bridge_gpu_snapshot(config: &RocmCliConfig) -> CodexBridgeGpuSnap amd_smi_available: false, static_snapshot: None, monitor_snapshot: None, - note: Some("Use `rocm doctor` for the current local AMD GPU summary.".to_owned()), + note: Some("Use `rocm examine` for the current local AMD GPU summary.".to_owned()), } } @@ -1412,8 +1412,8 @@ const fn builtin_engine_inventory() -> &'static [(&'static str, &'static str)] { ] } -fn doctor() -> Result<()> { - print!("{}", render_doctor_text()?); +fn examine() -> Result<()> { + print!("{}", render_examine_text()?); Ok(()) } @@ -1705,10 +1705,10 @@ fn install_driver( yes: bool, dry_run: bool, ) -> std::result::Result { - let doctor = - DoctorSummary::gather().map_err(|source| DriverInstallError::new(source, false))?; + let examine = + ExamineSummary::gather().map_err(|source| DriverInstallError::new(source, false))?; let os_release = read_os_release().unwrap_or_default(); - let plan = build_driver_install_plan(&doctor, &os_release, dkms); + let plan = build_driver_install_plan(&examine, &os_release, dkms); let mut output = render_driver_install_plan(&plan, yes, dry_run); if !yes || dry_run || !plan.supported || !plan.mutating { return Ok(DriverInstallResult { @@ -1721,7 +1721,7 @@ fn install_driver( let mut state = DriverInstallState { approved_at_unix_ms: rocm_core::unix_time_millis(), executed_at_unix_ms: None, - pre_driver: doctor.driver, + pre_driver: examine.driver, post_driver: None, boot_id_at_execution: boot_id, reboot_required: false, @@ -1745,7 +1745,7 @@ fn install_driver( .map_err(|source| DriverInstallError::new(source, true))?; } - let post_driver = DoctorSummary::gather() + let post_driver = ExamineSummary::gather() .map_err(|source| DriverInstallError::new(source, true))? .driver; state.executed_at_unix_ms = Some(rocm_core::unix_time_millis()); @@ -1787,9 +1787,9 @@ fn reconcile_driver_install(paths: &AppPaths) -> Result { ); return Ok(output); }; - let doctor = DoctorSummary::gather()?; + let examine = ExamineSummary::gather()?; let checks = passive_driver_checks(); - reconcile_driver_install_state(paths, &mut state, doctor.driver, current_boot_id(), checks) + reconcile_driver_install_state(paths, &mut state, examine.driver, current_boot_id(), checks) } fn reconcile_driver_install_state( @@ -1892,12 +1892,12 @@ fn render_driver_reconciliation(paths: &AppPaths, state: &DriverInstallState) -> { let _ = writeln!( output, - " action: reconciliation recorded missing passive checks; run `rocm doctor` and inspect driver logs" + " action: reconciliation recorded missing passive checks; run `rocm examine` and inspect driver logs" ); } else { let _ = writeln!( output, - " action: reconciliation complete; run `rocm doctor` for the full host summary" + " action: reconciliation complete; run `rocm examine` for the full host summary" ); } } @@ -1961,9 +1961,9 @@ fn passive_render_node_check() -> DriverPassiveCheck { fn driver_install_args_require_tui_approval(args: &[&str]) -> Result { let flags = parse_driver_install_flags(args)?; - let doctor = DoctorSummary::gather()?; + let examine = ExamineSummary::gather()?; let os_release = read_os_release().unwrap_or_default(); - let plan = build_driver_install_plan(&doctor, &os_release, flags.dkms); + let plan = build_driver_install_plan(&examine, &os_release, flags.dkms); Ok(driver_install_flags_require_tui_approval(&plan, &flags)) } @@ -2117,12 +2117,12 @@ struct DriverPassiveCheck { } fn build_driver_install_plan( - doctor: &DoctorSummary, + examine: &ExamineSummary, os_release_text: &str, dkms: bool, ) -> DriverInstallPlan { let repo_version_expr = "${ROCM_CLI_AMDGPU_VERSION:-7.2.4}".to_owned(); - if doctor.os == "windows" { + if examine.os == "windows" { return DriverInstallPlan { supported: false, mutating: false, @@ -2131,13 +2131,13 @@ fn build_driver_install_plan( version_id: String::new(), codename: String::new(), repo_version_expr, - reason: "Windows driver install is validate-only in rocm-cli; use `rocm doctor` to inspect the AMD display driver.".to_owned(), + reason: "Windows driver install is validate-only in rocm-cli; use `rocm examine` to inspect the AMD display driver.".to_owned(), preflight_checks: Vec::new(), commands: Vec::new(), - checks: vec!["rocm doctor".to_owned()], + checks: vec!["rocm examine".to_owned()], }; } - if doctor.wsl.as_ref().is_some_and(|wsl| wsl.is_wsl) { + if examine.wsl.as_ref().is_some_and(|wsl| wsl.is_wsl) { return DriverInstallPlan { supported: false, mutating: false, @@ -2149,7 +2149,7 @@ fn build_driver_install_plan( reason: "WSL uses the Windows host driver plus ROCDXG; run `scripts/wsl_setup_rocdxg.sh` inside WSL instead of installing Linux DKMS.".to_owned(), preflight_checks: Vec::new(), commands: Vec::new(), - checks: vec!["rocm doctor".to_owned(), "scripts/wsl_preflight.py".to_owned()], + checks: vec!["rocm examine".to_owned(), "scripts/wsl_preflight.py".to_owned()], }; } @@ -2218,7 +2218,7 @@ fn build_driver_install_plan( reason: "Linux DKMS driver install is currently planned only for AMD-documented Ubuntu, Debian, RHEL, Oracle Linux, SLES, and Rocky versions; no commands were guessed for this distro.".to_owned(), preflight_checks: Vec::new(), commands: Vec::new(), - checks: vec!["rocm doctor".to_owned()], + checks: vec!["rocm examine".to_owned()], }, } } @@ -6175,8 +6175,8 @@ fn fallback_rocm_tool_call_for_prompt(prompt: &str) -> Option Option Option { .position(|window| window.eq_ignore_ascii_case(needle)) } -const ROCM_CHAT_TOOL_SYSTEM_PROMPT: &str = "You are ROCm CLI's local assistant. Speak in simple English for non-technical Windows users. Use the provided ROCm tools when you need to inspect this machine, preview setup, read service logs, check updates, inspect automations, install or start ROCm-managed apps, or request ROCm/TheRock, config, engine, app, and local model server changes. For simple greetings or thanks like hello, hi, hey, ok, or thank you, reply normally; do not inspect ROCm, do not call tools, and do not launch or propose a model server. Tool-use rules: inspect first with read-only tools; call rocm_command only with argv-style args and no shell text; use natural_language_plan for ROCm requests that do not fit another read-only tool; ask for a mutating tool call only after explaining why it is needed; summarize tool results after they are returned. Read-only tools may run immediately. Tools that install, launch, stop, delete, or change state require user approval; request rocm_command and explain why. For 'is X running?', 'what is running?', status, or port questions, inspect before answering and do not start, stop, install, or serve anything. For ComfyUI or port 8188 use [\"comfyui\",\"status\"] or port_status. For vLLM, SGLang, Lemonade, PyTorch, llama.cpp, qwen, or local model servers use [\"services\",\"list\",\"--all\"] for running state and [\"engines\",\"list\"] for installed/available engine state. Treat ready/running as running, starting/recovering as starting, failed/stopped as not running, and no matching record as unknown or not managed by ROCm CLI. Interpret Doctor carefully: active_runtime_status=ready means ROCm CLI has an active managed TheRock/ROCm runtime; legacy_rocm_status=not_detected only means no global system ROCm install was found. If active_runtime_status=ready, tell the user ROCm/TheRock is installed and active for ROCm CLI. For 'is TheRock installed', 'is ROCm installed', or 'which GPU is on this machine', use doctor or gpu_snapshot before answering. For 'how do I setup TheRock' or install/setup requests, guide the user to choose an install folder first; do not answer with only a status check. For 'which LLMs can this machine support', use rocm_command args [\"model\"] or natural_language_plan before answering. For TheRock installs, always let the user choose the install folder. If the user names a folder or prefix, preserve that exact folder with [\"--prefix\",\"PATH\"]; you may call path_exists first to check whether that user-provided folder or its parent exists. If the user asks you to install TheRock/ROCm but has not named a folder, ask for the folder or let the guided setup folder picker collect it; do not invent a hidden default folder and do not request an install command without --prefix. Use rocm_command args [\"install\",\"sdk\",\"--channel\",\"release\",\"--format\",\"wheel\",\"--prefix\",\"PATH\"] only when the user asks you to install it and a folder is known; for a requested build date add [\"--build-date\",\"YYYY-MM-DD\"] and for a requested exact version add [\"--version\",\"VERSION\"]. For config changes, inspect with [\"config\",\"show\"] first when useful, then request config subcommands such as [\"config\",\"set-default-engine\",\"lemonade\"], [\"config\",\"set-default-runtime\",\"RUNTIME_KEY\"], or [\"config\",\"set-telemetry\",\"local\"] only after explaining why. For ComfyUI, use rocm_command with args like [\"comfyui\",\"status\"], [\"comfyui\",\"logs\"], [\"comfyui\",\"install\"], [\"comfyui\",\"start\"], or [\"comfyui\",\"stop\"]. First-time setup is the same thing as bootstrap in ROCm CLI; it is a deterministic ROCm setup flow, not a separate model chat. The built-in local assistant is fixed to qwen, which maps to Qwen3-4B-Instruct-2507-GGUF served by Lemonade with gpu_required. vLLM, SGLang, PyTorch, and Lemonade are general serving engines; inspect or manage them when the user asks about general model serving, but do not switch the built-in assistant away from Lemonade. Use qwen-smoke only for a quick server smoke test. For llama.cpp, use the llama.cpp engine backed by upstream llama-server: request rocm_command args like [\"engines\",\"install\",\"llama.cpp\"] or [\"serve\",\"MODEL.gguf\",\"--engine\",\"llama.cpp\",\"--device\",\"gpu_required\",\"--managed\"]. On native Windows, vLLM and SGLang are skipped; use WSL/Linux for those ROCm GPU engines. For vLLM management, inspect engines first and use [\"engines\",\"install\",\"vllm\"] or [\"serve\",\"MODEL\",\"--engine\",\"vllm\",\"--device\",\"gpu_required\",\"--managed\"] only where the host supports it. Do not invent shell commands and do not request CPU fallback."; +const ROCM_CHAT_TOOL_SYSTEM_PROMPT: &str = "You are ROCm CLI's local assistant. Speak in simple English for non-technical Windows users. Use the provided ROCm tools when you need to inspect this machine, preview setup, read service logs, check updates, inspect automations, install or start ROCm-managed apps, or request ROCm/TheRock, config, engine, app, and local model server changes. For simple greetings or thanks like hello, hi, hey, ok, or thank you, reply normally; do not inspect ROCm, do not call tools, and do not launch or propose a model server. Tool-use rules: inspect first with read-only tools; call rocm_command only with argv-style args and no shell text; use natural_language_plan for ROCm requests that do not fit another read-only tool; ask for a mutating tool call only after explaining why it is needed; summarize tool results after they are returned. Read-only tools may run immediately. Tools that install, launch, stop, delete, or change state require user approval; request rocm_command and explain why. For 'is X running?', 'what is running?', status, or port questions, inspect before answering and do not start, stop, install, or serve anything. For ComfyUI or port 8188 use [\"comfyui\",\"status\"] or port_status. For vLLM, SGLang, Lemonade, PyTorch, llama.cpp, qwen, or local model servers use [\"services\",\"list\",\"--all\"] for running state and [\"engines\",\"list\"] for installed/available engine state. Treat ready/running as running, starting/recovering as starting, failed/stopped as not running, and no matching record as unknown or not managed by ROCm CLI. Interpret Examine carefully: active_runtime_status=ready means ROCm CLI has an active managed TheRock/ROCm runtime; legacy_rocm_status=not_detected only means no global system ROCm install was found. If active_runtime_status=ready, tell the user ROCm/TheRock is installed and active for ROCm CLI. For 'is TheRock installed', 'is ROCm installed', or 'which GPU is on this machine', use examine or gpu_snapshot before answering. For 'how do I setup TheRock' or install/setup requests, guide the user to choose an install folder first; do not answer with only a status check. For 'which LLMs can this machine support', use rocm_command args [\"model\"] or natural_language_plan before answering. For TheRock installs, always let the user choose the install folder. If the user names a folder or prefix, preserve that exact folder with [\"--prefix\",\"PATH\"]; you may call path_exists first to check whether that user-provided folder or its parent exists. If the user asks you to install TheRock/ROCm but has not named a folder, ask for the folder or let the guided setup folder picker collect it; do not invent a hidden default folder and do not request an install command without --prefix. Use rocm_command args [\"install\",\"sdk\",\"--channel\",\"release\",\"--format\",\"wheel\",\"--prefix\",\"PATH\"] only when the user asks you to install it and a folder is known; for a requested build date add [\"--build-date\",\"YYYY-MM-DD\"] and for a requested exact version add [\"--version\",\"VERSION\"]. For config changes, inspect with [\"config\",\"show\"] first when useful, then request config subcommands such as [\"config\",\"set-default-engine\",\"lemonade\"], [\"config\",\"set-default-runtime\",\"RUNTIME_KEY\"], or [\"config\",\"set-telemetry\",\"local\"] only after explaining why. For ComfyUI, use rocm_command with args like [\"comfyui\",\"status\"], [\"comfyui\",\"logs\"], [\"comfyui\",\"install\"], [\"comfyui\",\"start\"], or [\"comfyui\",\"stop\"]. First-time setup is the same thing as bootstrap in ROCm CLI; it is a deterministic ROCm setup flow, not a separate model chat. The built-in local assistant is fixed to qwen, which maps to Qwen3-4B-Instruct-2507-GGUF served by Lemonade with gpu_required. vLLM, SGLang, PyTorch, and Lemonade are general serving engines; inspect or manage them when the user asks about general model serving, but do not switch the built-in assistant away from Lemonade. Use qwen-smoke only for a quick server smoke test. For llama.cpp, use the llama.cpp engine backed by upstream llama-server: request rocm_command args like [\"engines\",\"install\",\"llama.cpp\"] or [\"serve\",\"MODEL.gguf\",\"--engine\",\"llama.cpp\",\"--device\",\"gpu_required\",\"--managed\"]. On native Windows, vLLM and SGLang are skipped; use WSL/Linux for those ROCm GPU engines. For vLLM management, inspect engines first and use [\"engines\",\"install\",\"vllm\"] or [\"serve\",\"MODEL\",\"--engine\",\"vllm\",\"--device\",\"gpu_required\",\"--managed\"] only where the host supports it. Do not invent shell commands and do not request CPU fallback."; const ROCM_CHAT_TOOL_SKILL: &str = include_str!("../../../skills/rocm-cli-assistant/SKILL.md"); fn rocm_chat_tool_system_prompt() -> String { @@ -6921,7 +6921,7 @@ pub(crate) fn validate_chat_tool_call(call: &providers::ChatToolCall) -> Result< bail!("ROCm tool `{}` arguments must be a JSON object", call.name); } match call.name.as_str() { - "doctor" + "examine" | "bridge_snapshot" | "gpu_snapshot" | "engines" @@ -7191,7 +7191,7 @@ fn chat_rocm_command_action_from_args(mut args: Vec) -> Result { + Some("examine" | "version" | "model" | "models" | "daemon" | "logs") => { Ok(ChatRocmCommandAction::ReadOnly(args)) } Some("update") if !args.iter().any(|arg| arg == "--apply") => { @@ -7492,7 +7492,7 @@ pub(crate) fn chat_tool_call_is_read_only(call: &providers::ChatToolCall) -> boo } matches!( call.name.as_str(), - "doctor" + "examine" | "bridge_snapshot" | "gpu_snapshot" | "engines" @@ -7508,7 +7508,7 @@ pub(crate) fn chat_tool_call_is_read_only(call: &providers::ChatToolCall) -> boo } fn deterministic_rocm_tool_summary(tool_text: &str) -> Option { - if !tool_text.contains("doctor:") { + if !tool_text.contains("examine:") { return None; } let mut lines = Vec::new(); @@ -7720,7 +7720,7 @@ fn deterministic_model_tool_summary(tool_text: &str) -> Option { if !lines.is_empty() { lines.push( - " Run `rocm doctor` to refresh GPU memory details before starting anything large." + " Run `rocm examine` to refresh GPU memory details before starting anything large." .to_owned(), ); } @@ -8120,17 +8120,17 @@ fn run_internal_mcp_call( } match name { - "doctor" => { - let doctor = DoctorSummary::gather()?; - let text = render_doctor_text()?; - Ok(internal_mcp_tool_success(text, serde_json::json!(doctor))) + "examine" => { + let examine = ExamineSummary::gather()?; + let text = render_examine_text()?; + Ok(internal_mcp_tool_success(text, serde_json::json!(examine))) } "bridge_snapshot" => { let snapshot = build_codex_bridge_snapshot(paths)?; Ok(internal_mcp_tool_success( format!( "Captured bridge snapshot for {} / {} with default engine `{}`.", - snapshot.doctor.os, snapshot.doctor.arch, snapshot.doctor.default_engine + snapshot.examine.os, snapshot.examine.arch, snapshot.examine.default_engine ), serde_json::json!(snapshot), )) @@ -8143,7 +8143,7 @@ fn run_internal_mcp_call( } else if gpu.amd_smi_available { "Captured amd-smi GPU snapshot." } else { - "Use `rocm doctor` for the current local AMD GPU summary." + "Use `rocm examine` for the current local AMD GPU summary." }; Ok(internal_mcp_tool_success( status.to_owned(), @@ -8397,8 +8397,8 @@ fn run_rocm_read_only_in_process(paths: &AppPaths, args: &[String]) -> Result bail!("rocm command requires at least one argument"), - [command] if command.eq_ignore_ascii_case("doctor") => { - render_doctor_text_with_paths(paths, &config) + [command] if command.eq_ignore_ascii_case("examine") => { + render_examine_text_with_paths(paths, &config) } [command] if command.eq_ignore_ascii_case("version") @@ -8834,7 +8834,7 @@ const fn chat_read_only_tool_status_label(is_error: bool) -> &'static str { fn chat_tool_display_label(name: &str) -> String { match name { - "doctor" => "Checked this computer".to_owned(), + "examine" => "Checked this computer".to_owned(), "gpu_snapshot" => "Checked GPU status".to_owned(), "engines" => "Checked local engines".to_owned(), "services" => "Checked model servers".to_owned(), @@ -8864,7 +8864,7 @@ fn chat_tool_call_display_label(call: &providers::ChatToolCall) -> String { return "rocm command".to_owned(); }; match args.as_slice() { - [command] if command.eq_ignore_ascii_case("doctor") => "Checked this computer".to_owned(), + [command] if command.eq_ignore_ascii_case("examine") => "Checked this computer".to_owned(), [command] if command.eq_ignore_ascii_case("model") || command.eq_ignore_ascii_case("models") => { @@ -9025,23 +9025,23 @@ fn json_string(object: &serde_json::Map, key: &str) - .filter(|value| !value.trim().is_empty()) } -pub(crate) fn render_doctor_text() -> Result { +pub(crate) fn render_examine_text() -> Result { let paths = AppPaths::discover()?; let config = RocmCliConfig::load(&paths).unwrap_or_default(); - render_doctor_text_with_paths(&paths, &config) + render_examine_text_with_paths(&paths, &config) } -fn render_doctor_text_with_paths(paths: &AppPaths, config: &RocmCliConfig) -> Result { +fn render_examine_text_with_paths(paths: &AppPaths, config: &RocmCliConfig) -> Result { recover_setup_runtime_registration(paths, config)?; - let summary = DoctorSummary::gather()?; - let mut output = render_doctor_plain_header(&summary); + let summary = ExamineSummary::gather()?; + let mut output = render_examine_plain_header(&summary); output.push_str(&summary.render_text()); - append_doctor_runtime_state(&mut output, paths, config)?; - append_doctor_engine_inventory(&mut output, paths, config); + append_examine_runtime_state(&mut output, paths, config)?; + append_examine_engine_inventory(&mut output, paths, config); Ok(output) } -fn render_doctor_plain_header(summary: &DoctorSummary) -> String { +fn render_examine_plain_header(summary: &ExamineSummary) -> String { let gpu = if summary.detected_gfx_target.is_some() { "AMD GPU detected" } else { @@ -9098,7 +9098,7 @@ fn render_engine_inventory_text_with_paths(paths: Option<&AppPaths>) -> String { output } -fn append_doctor_runtime_state( +fn append_examine_runtime_state( output: &mut String, paths: &AppPaths, config: &RocmCliConfig, @@ -9193,7 +9193,7 @@ fn append_doctor_runtime_state( Ok(()) } -fn append_doctor_engine_inventory(output: &mut String, paths: &AppPaths, config: &RocmCliConfig) { +fn append_examine_engine_inventory(output: &mut String, paths: &AppPaths, config: &RocmCliConfig) { let configured_default = config.default_engine.as_deref(); let effective_default = match configured_default { Some(engine) => engine, @@ -9643,7 +9643,7 @@ fn append_model_host_ram_fit_lines( ); let _ = writeln!( output, - " system_ram_action: run /doctor to refresh host telemetry" + " system_ram_action: run /examine to refresh host telemetry" ); } } @@ -9710,7 +9710,7 @@ fn append_model_fit_lines( ); let _ = writeln!( output, - " action: run /doctor or refresh GPU telemetry, then retry /model {}", + " action: run /examine or refresh GPU telemetry, then retry /model {}", recipe_display_ref(recipe) ); } @@ -11779,7 +11779,7 @@ pub(crate) fn tui_help_text() -> String { let _ = writeln!(output, " /help show this help"); let _ = writeln!( output, - " /doctor check this computer and ROCm setup" + " /examine check this computer and ROCm setup" ); let _ = writeln!( output, @@ -12162,7 +12162,7 @@ fn build_freeform_plan_with_recipes( actions: vec![ PlannedToolCall::read_only( "Inspect host/runtime state", - vec!["doctor".to_owned()], + vec!["examine".to_owned()], "read-only inspection", ), PlannedToolCall::read_only( @@ -12205,7 +12205,7 @@ fn build_freeform_plan_with_recipes( actions: vec![ PlannedToolCall::read_only( "Inspect host/driver state", - vec!["doctor".to_owned()], + vec!["examine".to_owned()], "read-only inspection", ), PlannedToolCall::approval_required( @@ -12351,7 +12351,7 @@ fn build_freeform_plan_with_recipes( actions: vec![ PlannedToolCall::read_only( "Inspect current runtime", - vec!["doctor".to_owned()], + vec!["examine".to_owned()], "read-only inspection", ), PlannedToolCall::approval_required( @@ -12435,7 +12435,7 @@ fn build_freeform_plan_with_recipes( parsed: vec![("engine".to_owned(), default_engine.to_owned())], actions: vec![PlannedToolCall::read_only( "Inspect local ROCm state", - vec!["doctor".to_owned()], + vec!["examine".to_owned()], "read-only inspection", )], notes: vec![ @@ -12601,7 +12601,7 @@ fn resolve_freeform_plan_with_provider( fn build_provider_planner_prompt(request: &str, deterministic: &StructuredRequestPlan) -> String { let next_tool_call = deterministic.actions.last().map_or_else( - || "rocm doctor".to_owned(), + || "rocm examine".to_owned(), |action| format_structured_tool_call(action.tool, &action.args), ); format!( @@ -12610,7 +12610,7 @@ fn build_provider_planner_prompt(request: &str, deterministic: &StructuredReques \"confidence\":\"high|medium|low\",\ \"tool_call\":{{\"tool\":\"rocm\",\"args\":[\"...\"]}},\ \"notes\":[\"short note\"]}}.\n\ -Allowed rocm actions: doctor; engines list; install sdk; install driver; update; serve; uninstall. Install sdk must include --prefix PATH chosen by the user, and may include --build-date YYYY-MM-DD or --version VERSION.\n\ +Allowed rocm actions: examine; engines list; install sdk; install driver; update; serve; uninstall. Install sdk must include --prefix PATH chosen by the user, and may include --build-date YYYY-MM-DD or --version VERSION.\n\ Do not invent CPU fallback. Do not include shell commands. Do not include markdown.\n\ User request: {request}\n\ Deterministic planner intent: {}\n\ @@ -12728,7 +12728,7 @@ fn validate_provider_planner_tool_call(call: &ProviderPlannerToolCall) -> Result .context("provider planner returned a rocm command that is not valid")?; match call.args.as_slice() { - [command] if command == "doctor" => {} + [command] if command == "examine" => {} [command, subcommand] if command == "engines" && subcommand == "list" => {} [command, subcommand, ..] if command == "install" && subcommand == "sdk" => { validate_chat_rocm_command_safety(&call.args)?; @@ -12772,7 +12772,7 @@ fn planner_intent_from_provider_response(intent: &str, args: &[String]) -> Resul "install_driver" | "install driver" => PlannerIntent::InstallDriver, "update" => PlannerIntent::Update, "uninstall" => PlannerIntent::Uninstall, - "inspect" | "doctor" => PlannerIntent::Inspect, + "inspect" | "examine" => PlannerIntent::Inspect, _ => bail!("provider planner returned unsupported intent `{intent}`"), }; if declared != args_intent { @@ -12796,7 +12796,7 @@ fn planner_intent_from_args(args: &[String]) -> Result { } Some("update") => Ok(PlannerIntent::Update), Some("uninstall") => Ok(PlannerIntent::Uninstall), - Some("doctor" | "engines") => Ok(PlannerIntent::Inspect), + Some("examine" | "engines") => Ok(PlannerIntent::Inspect), _ => bail!("unsupported provider planner args"), } } @@ -12887,7 +12887,7 @@ fn planner_is_inspect_request(lower: &str) -> bool { let inspectish = contains_planner_word(lower, "inspect") || contains_planner_word(lower, "check") || contains_planner_word(lower, "status") - || contains_planner_word(lower, "doctor") + || contains_planner_word(lower, "examine") || contains_planner_word(lower, "which") || contains_planner_word(lower, "where") || lower.contains("what is installed") @@ -14068,7 +14068,7 @@ fn service_model_names_match(left: &str, right: &str) -> bool { fn treat_as_natural_language(args: &[String]) -> bool { const STRUCTURED: &[&str] = &[ - "doctor", + "examine", "status", "bridge-snapshot", "sandbox-run", @@ -14281,8 +14281,8 @@ mod tests { Ok(()) } - fn test_doctor(os: &str, wsl: bool) -> DoctorSummary { - DoctorSummary { + fn test_examine(os: &str, wsl: bool) -> ExamineSummary { + ExamineSummary { os: os.to_owned(), arch: "x86_64".to_owned(), kernel: Some("6.8.0-test".to_owned()), @@ -14744,7 +14744,7 @@ mod tests { assert!( plan.actions .iter() - .all(|action| action.args == vec!["doctor".to_owned()]) + .all(|action| action.args == vec!["examine".to_owned()]) ); } @@ -14761,7 +14761,7 @@ mod tests { assert_eq!(plan.approval, "not required for inspection", "{prompt}"); assert_eq!(plan.actions.len(), 1, "{prompt}"); assert_eq!(plan.actions[0].approval, "not required", "{prompt}"); - assert_eq!(plan.actions[0].args, vec!["doctor".to_owned()], "{prompt}"); + assert_eq!(plan.actions[0].args, vec!["examine".to_owned()], "{prompt}"); } } @@ -15244,7 +15244,7 @@ mod tests { fn deterministic_rocm_tool_summary_interprets_managed_runtime_as_installed() { let summary = deterministic_rocm_tool_summary( "\ -doctor: +examine: driver_detail: AMD Radeon RX 9070 XT driver 32.0.23033.1002 legacy_rocm_status: not_detected runtime_state: @@ -15255,7 +15255,7 @@ runtime_state: active_runtime_family: gfx120X-all ", ) - .expect("doctor output should summarize"); + .expect("examine output should summarize"); assert!(summary.contains("GPU: AMD Radeon RX 9070 XT driver 32.0.23033.1002")); assert!(summary.contains("ROCm/TheRock: installed and active for ROCm CLI")); @@ -15266,7 +15266,7 @@ runtime_state: } #[test] - fn fallback_tool_call_routes_where_installed_to_read_only_doctor() { + fn fallback_tool_call_routes_where_installed_to_read_only_examine() { for prompt in [ "where is rocm installed?", "where is TheRock installed?", @@ -15274,7 +15274,7 @@ runtime_state: "where did rocm install to?", ] { let call = fallback_rocm_tool_call_for_prompt(prompt).unwrap(); - assert_eq!(call.name, "doctor", "{prompt}"); + assert_eq!(call.name, "examine", "{prompt}"); assert!(chat_tool_call_is_read_only(&call), "{prompt}"); } } @@ -15284,7 +15284,7 @@ runtime_state: let tool_result = ChatToolRunResult { approval: None, follow_up_text: "\ -doctor: +examine: legacy_rocm_status: not_detected runtime_state: active_runtime_status: ready @@ -15349,7 +15349,7 @@ model recipes assert!(summary.contains("pytorch, llama.cpp")); assert!(summary.contains("Qwen/Qwen3.5-4B asks for 12 GiB")); assert!(summary.contains("Native Windows note")); - assert!(summary.contains("Run `rocm doctor`")); + assert!(summary.contains("Run `rocm examine`")); } #[test] @@ -15385,9 +15385,9 @@ model recipes providers::ChatToolCall { id: None, name: "rocm_command".to_owned(), - arguments: serde_json::json!({ "args": ["doctor"] }), + arguments: serde_json::json!({ "args": ["examine"] }), }, - Some("rocm doctor"), + Some("rocm examine"), true, ), ( @@ -16032,7 +16032,7 @@ model recipes "Check this ROCm setup.", ] { let call = fallback_rocm_tool_call_for_prompt(prompt).unwrap(); - assert_eq!(call.name, "doctor"); + assert_eq!(call.name, "examine"); assert_eq!(call.arguments, serde_json::json!({})); } } @@ -16204,7 +16204,7 @@ model recipes let (_root, paths) = test_paths("chat-install-intent-latest-message"); let prompt = "\ Conversation so far: -Assistant: Use /doctor to refresh actual GPU memory fit before starting anything large. +Assistant: Use /examine to refresh actual GPU memory fit before starting anything large. Assistant: Native Windows note: models may use WSL/Linux through Windows. New message: @@ -16457,7 +16457,7 @@ install therock"; content: "The active runtime root is /opt/rocm.".to_owned(), tool_calls: vec![providers::ChatToolCall { id: Some("call-1".to_owned()), - name: "doctor".to_owned(), + name: "examine".to_owned(), arguments: serde_json::json!({}), }], }; @@ -16491,7 +16491,7 @@ install therock"; content: "The runtime root is /opt/rocml.".to_owned(), tool_calls: vec![providers::ChatToolCall { id: Some("call-2".to_owned()), - name: "doctor".to_owned(), + name: "examine".to_owned(), arguments: serde_json::json!({}), }], }; @@ -16525,7 +16525,7 @@ install therock"; fn chat_tool_result_errors_use_plain_failure_wording() { assert_eq!(chat_read_only_tool_status_label(false), "done"); assert_eq!(chat_read_only_tool_status_label(true), "reported an error"); - assert_eq!(chat_tool_display_label("doctor"), "Checked this computer"); + assert_eq!(chat_tool_display_label("examine"), "Checked this computer"); assert_eq!( chat_tool_display_label("gpu_snapshot"), "Checked GPU status" @@ -17086,7 +17086,7 @@ install therock"; #[test] fn top_level_cli_commands_are_not_treated_as_freeform() { for command in [ - "doctor", + "examine", "bootstrap", "version", "setup", @@ -17382,7 +17382,7 @@ ID=ubuntu VERSION_ID="24.04" VERSION_CODENAME=noble "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let commands = plan .commands .iter() @@ -17436,10 +17436,10 @@ ID=ubuntu VERSION_ID="24.04" VERSION_CODENAME=noble "#; - let dkms_plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let dkms_plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let preflight_plan = - build_driver_install_plan(&test_doctor("linux", false), os_release, false); - let windows_plan = build_driver_install_plan(&test_doctor("windows", false), "", true); + build_driver_install_plan(&test_examine("linux", false), os_release, false); + let windows_plan = build_driver_install_plan(&test_examine("windows", false), "", true); let dkms_flags = parse_driver_install_flags(&["driver", "--dkms"])?; let dry_run_flags = parse_driver_install_flags(&["driver", "--dkms", "--dry-run"])?; @@ -17598,7 +17598,7 @@ ID=ubuntu VERSION_ID="24.04" VERSION_CODENAME=noble "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, false); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, false); let rendered = render_driver_install_plan(&plan, false, false); assert!(plan.supported); @@ -17617,7 +17617,7 @@ ID=debian VERSION_ID="12" VERSION_CODENAME=bookworm "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, true); assert!(plan.supported); @@ -17634,7 +17634,7 @@ VERSION_CODENAME=bookworm ID=rhel VERSION_ID="9.7" "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, false); assert!(plan.supported); @@ -17658,7 +17658,7 @@ VERSION_ID="9.7" ID=ol VERSION_ID="10.1" "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, true); assert!(plan.supported); @@ -17679,7 +17679,7 @@ VERSION_ID="10.1" ID=rocky VERSION_ID="9.7" "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, false); assert!(plan.supported); @@ -17701,7 +17701,7 @@ VERSION_ID="9.7" ID=sles VERSION_ID="15.7" "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, false); assert!(plan.supported); @@ -17723,7 +17723,7 @@ VERSION_ID="15.7" ID=fedora VERSION_ID="41" "#; - let plan = build_driver_install_plan(&test_doctor("linux", false), os_release, true); + let plan = build_driver_install_plan(&test_examine("linux", false), os_release, true); let rendered = render_driver_install_plan(&plan, false, false); assert!(!plan.supported); @@ -17736,7 +17736,7 @@ VERSION_ID="41" #[test] fn windows_install_driver_is_validate_only() { - let plan = build_driver_install_plan(&test_doctor("windows", false), "", true); + let plan = build_driver_install_plan(&test_examine("windows", false), "", true); let rendered = render_driver_install_plan(&plan, false, true); assert!(!plan.supported); @@ -17745,14 +17745,14 @@ VERSION_ID="41" assert!(rendered.contains("approval: not required")); assert!(rendered.contains("execution_commands: ")); assert!(rendered.contains("post_reboot_checks:")); - assert!(rendered.contains("use `rocm doctor`")); - assert!(rendered.contains("rocm doctor")); + assert!(rendered.contains("use `rocm examine`")); + assert!(rendered.contains("rocm examine")); assert!(plan.commands.is_empty()); } #[test] fn wsl_install_driver_uses_rocdxg_guidance_without_dkms() { - let plan = build_driver_install_plan(&test_doctor("linux", true), "", true); + let plan = build_driver_install_plan(&test_examine("linux", true), "", true); let rendered = render_driver_install_plan(&plan, false, false); assert!(!plan.supported); @@ -18033,10 +18033,10 @@ VERSION_ID="41" assert!(!success.contains("config:")); assert!(!success.contains("marker:")); - let mut doctor = String::new(); - append_doctor_runtime_state(&mut doctor, &rebased_paths, &config)?; - assert!(doctor.contains("active_runtime_status: ready")); - assert!(doctor.contains("setup_runtime_root:")); + let mut examine = String::new(); + append_examine_runtime_state(&mut examine, &rebased_paths, &config)?; + assert!(examine.contains("active_runtime_status: ready")); + assert!(examine.contains("setup_runtime_root:")); let _ = fs::remove_dir_all(root); Ok(()) @@ -18197,9 +18197,9 @@ VERSION_ID="41" let runtimes = render_runtimes_text(&paths, &config)?; assert!(runtimes.contains("version=7.14.0a20260601 (build 2026-06-01)")); - let mut doctor = String::new(); - append_doctor_runtime_state(&mut doctor, &paths, &config)?; - assert!(doctor.contains("active_runtime_version: 7.14.0a20260601 (build 2026-06-01)")); + let mut examine = String::new(); + append_examine_runtime_state(&mut examine, &paths, &config)?; + assert!(examine.contains("active_runtime_version: 7.14.0a20260601 (build 2026-06-01)")); let _ = fs::remove_dir_all(root); Ok(()) @@ -19469,8 +19469,8 @@ VERSION_ID="41" } #[test] - fn doctor_runtime_state_reports_active_runtime_key_and_status() -> Result<()> { - let (root, paths) = test_paths("doctor-runtime-state"); + fn examine_runtime_state_reports_active_runtime_key_and_status() -> Result<()> { + let (root, paths) = test_paths("examine-runtime-state"); let manifest = write_test_pip_runtime( &paths, "release-pip-gfx120x-all", @@ -19486,7 +19486,7 @@ VERSION_ID="41" }; let mut output = String::new(); - append_doctor_runtime_state(&mut output, &paths, &config)?; + append_examine_runtime_state(&mut output, &paths, &config)?; assert!(output.contains("runtime_state:")); assert!(output.contains("active_runtime_id: therock-release:gfx120X-all")); @@ -19500,8 +19500,8 @@ VERSION_ID="41" } #[test] - fn doctor_runtime_state_reports_ambiguous_default_runtime_id() -> Result<()> { - let (root, paths) = test_paths("doctor-runtime-ambiguous"); + fn examine_runtime_state_reports_ambiguous_default_runtime_id() -> Result<()> { + let (root, paths) = test_paths("examine-runtime-ambiguous"); write_test_pip_runtime( &paths, "release-pip-gfx120x-all", @@ -19522,7 +19522,7 @@ VERSION_ID="41" }; let mut output = String::new(); - append_doctor_runtime_state(&mut output, &paths, &config)?; + append_examine_runtime_state(&mut output, &paths, &config)?; assert!(output.contains("active_runtime_status: ambiguous_runtime_id")); assert!(output.contains("active_runtime_matches:")); @@ -19533,7 +19533,7 @@ VERSION_ID="41" config.active_runtime_key = Some("release-pip-gfx120x-all".to_owned()); output.clear(); - append_doctor_runtime_state(&mut output, &paths, &config)?; + append_examine_runtime_state(&mut output, &paths, &config)?; assert!(output.contains("active_runtime_status: ready")); assert!(!output.contains("ambiguous_runtime_id")); @@ -19542,8 +19542,8 @@ VERSION_ID="41" } #[test] - fn doctor_engine_inventory_reports_config_without_engine_detect() { - let (root, paths) = test_paths("doctor-engine-inventory"); + fn examine_engine_inventory_reports_config_without_engine_detect() { + let (root, paths) = test_paths("examine-engine-inventory"); let mut config = RocmCliConfig { default_engine: Some("llama.cpp".to_owned()), ..RocmCliConfig::default() @@ -19552,7 +19552,7 @@ VERSION_ID="41" Some("therock-release:gfx120X-all".to_owned()); let mut output = String::new(); - append_doctor_engine_inventory(&mut output, &paths, &config); + append_examine_engine_inventory(&mut output, &paths, &config); assert!(output.contains("engine_inventory:")); assert!(output.contains("configured_default_engine: llama.cpp")); diff --git a/apps/rocm/src/providers.rs b/apps/rocm/src/providers.rs index 9460fa61..1e56e93a 100644 --- a/apps/rocm/src/providers.rs +++ b/apps/rocm/src/providers.rs @@ -968,7 +968,7 @@ fn openai_chat_request_body_with_stream( fn rocm_openai_tool_definitions() -> Vec { vec![ rocm_openai_tool( - "doctor", + "examine", "Read the current ROCm host, GPU, runtime, driver, and engine status.", serde_json::json!({ "type": "object", @@ -978,7 +978,7 @@ fn rocm_openai_tool_definitions() -> Vec { ), rocm_openai_tool( "bridge_snapshot", - "Read a full ROCm snapshot including doctor data, engines, services, automations, and GPU telemetry.", + "Read a full ROCm snapshot including examine data, engines, services, automations, and GPU telemetry.", serde_json::json!({ "type": "object", "properties": {}, @@ -2241,7 +2241,7 @@ mod tests { listener, "tiny.gguf", move |stream, _request| { - let body = r#"{"choices":[{"message":{"content":"I will check first.","tool_calls":[{"id":"call-1","type":"function","function":{"name":"doctor","arguments":"{}"}}]}}]}"#; + let body = r#"{"choices":[{"message":{"content":"I will check first.","tool_calls":[{"id":"call-1","type":"function","function":{"name":"examine","arguments":"{}"}}]}}]}"#; write!( stream, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}", @@ -2287,10 +2287,10 @@ mod tests { assert!(request.contains("\"tools\"")); assert!(request.contains("\"tool_choice\":\"auto\"")); - assert!(request.contains("\"name\":\"doctor\"")); + assert!(request.contains("\"name\":\"examine\"")); assert_eq!(response.content, "I will check first."); assert_eq!(response.tool_calls.len(), 1); - assert_eq!(response.tool_calls[0].name, "doctor"); + assert_eq!(response.tool_calls[0].name, "examine"); Ok(()) } diff --git a/apps/rocm/src/tui.rs b/apps/rocm/src/tui.rs index aa888163..8d26e2cd 100644 --- a/apps/rocm/src/tui.rs +++ b/apps/rocm/src/tui.rs @@ -4,7 +4,7 @@ use crate::{ format_structured_tool_call, freeform_plan_next_action_with_context, freeform_plan_uses_provider, load_managed_services, logs_browser_page_count, managed_service_is_live, managed_service_sidebar_counts, provider_keys, - render_automations_text, render_chat_text, render_daemon_text, render_doctor_text, + render_automations_text, render_chat_text, render_daemon_text, render_examine_text, render_freeform_plan, render_logs_browser_page_text_for_tui, render_service_logs_text_for_tui, render_sidebar_text, render_uninstall_dry_run, render_update_text, runtime_usability_status, therock, @@ -131,8 +131,8 @@ const SLASH_COMMANDS: &[SlashCommandSpec] = &[ usage: "/?", }, SlashCommandSpec { - name: "doctor", - usage: "/doctor", + name: "examine", + usage: "/examine", }, SlashCommandSpec { name: "setup", @@ -371,7 +371,7 @@ struct App { running_job_log_scroll: usize, running_job_output: VecDeque, running_job_cancel_requested: Option>, - doctor_manager: Option, + examine_manager: Option, logs_view: Option, runtime_manager: Option, install_manager: Option, @@ -418,7 +418,7 @@ enum TuiMode { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum HomeDashboardAction { Setup, - Doctor, + Examine, Serve, Chat, ComfyUi, @@ -443,8 +443,8 @@ fn tui_mode_for_command(head: &str, has_args: bool) -> TuiMode { } } -fn is_doctor_command_input(command: &str) -> bool { - let Some(rest) = command.strip_prefix("doctor") else { +fn is_examine_command_input(command: &str) -> bool { + let Some(rest) = command.strip_prefix("examine") else { return false; }; rest.is_empty() || rest.chars().next().is_some_and(char::is_whitespace) @@ -619,7 +619,7 @@ struct LogsViewState { } #[derive(Debug, Clone, PartialEq, Eq)] -struct DoctorManagerState { +struct ExamineManagerState { selected: usize, detail_scroll: u16, report: String, @@ -627,7 +627,7 @@ struct DoctorManagerState { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum DoctorManagerChoice { +enum ExamineManagerChoice { Overview, Gpu, Runtimes, @@ -1268,7 +1268,7 @@ struct RunningJob { #[derive(Debug, Clone)] enum RunningJobKind { Cli, - DoctorRefresh, + ExamineRefresh, Chat { provider: String, rocm_tools: bool, @@ -2126,7 +2126,7 @@ impl App { running_job_log_scroll: 0, running_job_output: VecDeque::new(), running_job_cancel_requested: None, - doctor_manager: None, + examine_manager: None, logs_view: None, runtime_manager: None, install_manager: None, @@ -2210,7 +2210,7 @@ impl App { } fn running_job_blocks_screen_close(&mut self) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); let Some(job) = self.running_job.as_ref() else { return false; }; @@ -2222,7 +2222,7 @@ impl App { } fn running_job_blocks_screen_switch(&mut self) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); let Some(job) = self.running_job.as_ref() else { return false; }; @@ -2234,7 +2234,7 @@ impl App { } fn running_job_blocks_action(&mut self) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); if self.running_job.is_some() { self.status = "A command is already running; wait for it to finish.".to_owned(); true @@ -2243,28 +2243,28 @@ impl App { } } - fn doctor_blocked_by_current_workflow(&mut self) -> bool { + fn examine_blocked_by_current_workflow(&mut self) -> bool { if let Some(title) = self .pending_approval .as_ref() .map(|pending| pending.title.clone()) { self.set_active_screen_message(format!( - "{title} is waiting for your review.\n\nApprove it, cancel it, or go Back before opening Doctor." + "{title} is waiting for your review.\n\nApprove it, cancel it, or go Back before opening Examine." )); - self.status = "Finish the current approval before opening Doctor.".to_owned(); + self.status = "Finish the current approval before opening Examine.".to_owned(); return true; } - let Some((title, is_doctor_refresh)) = self.running_job.as_ref().map(|job| { + let Some((title, is_examine_refresh)) = self.running_job.as_ref().map(|job| { ( job.title.clone(), - matches!(job.kind, RunningJobKind::DoctorRefresh), + matches!(job.kind, RunningJobKind::ExamineRefresh), ) }) else { return false; }; - if is_doctor_refresh { + if is_examine_refresh { return false; } @@ -2274,14 +2274,14 @@ impl App { title.as_str() }; self.set_active_screen_message(format!( - "{running_label} is still running.\n\nDoctor can check this computer after it finishes. Keep this screen open so progress and results stay visible." + "{running_label} is still running.\n\nExamine can check this computer after it finishes. Keep this screen open so progress and results stay visible." )); self.status = - format!("{running_label} is still running. Doctor can run after it finishes."); + format!("{running_label} is still running. Examine can run after it finishes."); true } - fn open_doctor_manager(&mut self) { + fn open_examine_manager(&mut self) { self.refresh_config(); self.logs_view = None; self.runtime_manager = None; @@ -2295,85 +2295,85 @@ impl App { self.config_manager = None; self.services_manager = None; self.command_screen = None; - self.doctor_manager = Some(DoctorManagerState { + self.examine_manager = Some(ExamineManagerState { selected: 0, detail_scroll: 0, report: String::new(), message: Some("Checking this computer...\n\nYou can keep using the arrow keys while ROCm CLI checks the GPU, ROCm installs, engines, driver, WSL, and folders.".to_owned()), }); - self.status = "Doctor opened. Checking this computer...".to_owned(); - self.start_doctor_refresh_job(); + self.status = "Examine opened. Checking this computer...".to_owned(); + self.start_examine_refresh_job(); } - fn close_doctor_manager(&mut self) { + fn close_examine_manager(&mut self) { if self .running_job .as_ref() - .is_some_and(|job| matches!(job.kind, RunningJobKind::DoctorRefresh)) + .is_some_and(|job| matches!(job.kind, RunningJobKind::ExamineRefresh)) { - self.doctor_manager = None; + self.examine_manager = None; self.return_to_home_after_surface_close( - "Doctor closed. The check will finish in the background.", + "Examine closed. The check will finish in the background.", ); return; } if self.running_job_blocks_screen_close() { return; } - self.doctor_manager = None; - self.return_to_home_after_surface_close("Doctor closed."); + self.examine_manager = None; + self.return_to_home_after_surface_close("Examine closed."); } - fn refresh_doctor_manager(&mut self) { + fn refresh_examine_manager(&mut self) { let selected = self - .doctor_manager + .examine_manager .as_ref() .map_or(0, |state| state.selected); - if let Some(state) = self.doctor_manager.as_mut() { - state.selected = selected.min(doctor_manager_choices().len().saturating_sub(1)); + if let Some(state) = self.examine_manager.as_mut() { + state.selected = selected.min(examine_manager_choices().len().saturating_sub(1)); state.detail_scroll = 0; state.message = Some( "Checking this computer again...\n\nYou can keep using the arrow keys while ROCm CLI refreshes the checks." .to_owned(), ); } else { - self.doctor_manager = Some(DoctorManagerState { - selected: selected.min(doctor_manager_choices().len().saturating_sub(1)), + self.examine_manager = Some(ExamineManagerState { + selected: selected.min(examine_manager_choices().len().saturating_sub(1)), detail_scroll: 0, report: String::new(), message: Some("Checking this computer...".to_owned()), }); } - self.status = "Doctor refresh started.".to_owned(); - self.start_doctor_refresh_job(); + self.status = "Examine refresh started.".to_owned(); + self.start_examine_refresh_job(); } - fn move_doctor_manager_selection(&mut self, direction: CompletionDirection) { - let Some(state) = self.doctor_manager.as_mut() else { + fn move_examine_manager_selection(&mut self, direction: CompletionDirection) { + let Some(state) = self.examine_manager.as_mut() else { return; }; state.message = None; - state.selected = cycle_index(state.selected, doctor_manager_choices().len(), direction); + state.selected = cycle_index(state.selected, examine_manager_choices().len(), direction); state.detail_scroll = 0; - self.status = match self.selected_doctor_manager_choice() { - DoctorManagerChoice::Back => "Press Enter to go back.".to_owned(), - _ => "Doctor check selected.".to_owned(), + self.status = match self.selected_examine_manager_choice() { + ExamineManagerChoice::Back => "Press Enter to go back.".to_owned(), + _ => "Examine check selected.".to_owned(), }; } - fn selected_doctor_manager_choice(&self) -> DoctorManagerChoice { + fn selected_examine_manager_choice(&self) -> ExamineManagerChoice { let selected = self - .doctor_manager + .examine_manager .as_ref() .map_or(0, |state| state.selected); - doctor_manager_choices() + examine_manager_choices() .get(selected) .copied() - .unwrap_or(DoctorManagerChoice::Overview) + .unwrap_or(ExamineManagerChoice::Overview) } - const fn scroll_doctor_manager_detail(&mut self, lines: i16) { - let Some(state) = self.doctor_manager.as_mut() else { + const fn scroll_examine_manager_detail(&mut self, lines: i16) { + let Some(state) = self.examine_manager.as_mut() else { return; }; if lines < 0 { @@ -2383,9 +2383,9 @@ impl App { } } - fn perform_doctor_manager_selected_action(&mut self) { - match self.selected_doctor_manager_choice() { - DoctorManagerChoice::Back => self.close_doctor_manager(), + fn perform_examine_manager_selected_action(&mut self) { + match self.selected_examine_manager_choice() { + ExamineManagerChoice::Back => self.close_examine_manager(), _ => { self.status = "Use Up/Down to choose another check, F5 to refresh, Esc to go back." .to_owned(); @@ -2395,7 +2395,7 @@ impl App { fn open_runtime_manager(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.install_manager = None; self.engine_manager = None; self.model_picker = None; @@ -3026,7 +3026,7 @@ impl App { } fn open_install_manager(&mut self) { - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.engine_manager = None; self.model_picker = None; @@ -3396,7 +3396,7 @@ impl App { fn open_engine_manager(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.model_picker = None; @@ -3622,7 +3622,7 @@ impl App { fn open_model_picker(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.engine_manager = None; @@ -3746,7 +3746,7 @@ impl App { fn open_serve_wizard(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.engine_manager = None; @@ -4346,7 +4346,7 @@ impl App { } fn open_update_manager(&mut self) { - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.engine_manager = None; @@ -4508,7 +4508,7 @@ impl App { fn open_automations_manager(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -4982,7 +4982,7 @@ impl App { } fn open_provider_manager(&mut self) { - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -5013,7 +5013,7 @@ impl App { fn open_config_manager(&mut self) { self.refresh_config(); - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -5210,7 +5210,7 @@ impl App { } fn open_onboarding_after_setup_reset(&mut self) { - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -5775,7 +5775,7 @@ impl App { fn open_services_manager(&mut self) { self.remember_current_chat_session(); - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -6957,7 +6957,7 @@ impl App { const fn should_draw_home_dashboard(&self) -> bool { self.home_dashboard_visible && !self.onboarding_active - && self.doctor_manager.is_none() + && self.examine_manager.is_none() && self.logs_view.is_none() && self.runtime_manager.is_none() && self.install_manager.is_none() @@ -7018,7 +7018,7 @@ impl App { if self.running_job_blocks_screen_switch() { return; } - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -7146,7 +7146,7 @@ impl App { self.remember_current_chat_session(); self.command_screen_last_area.set(None); self.overlay_card = None; - self.doctor_manager = None; + self.examine_manager = None; self.logs_view = None; self.runtime_manager = None; self.install_manager = None; @@ -7283,7 +7283,7 @@ impl App { if let Some(job) = self .running_job .as_ref() - .filter(|job| !matches!(job.kind, RunningJobKind::DoctorRefresh)) + .filter(|job| !matches!(job.kind, RunningJobKind::ExamineRefresh)) { self.status = format!( "{} is still running. Wait for it to finish before quitting.", @@ -8919,7 +8919,7 @@ impl App { let (rendered, page, page_count) = self.render_logs_browser_view(query.as_deref(), page, follow, false); self.remember_current_chat_session(); - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.engine_manager = None; @@ -8963,7 +8963,7 @@ impl App { let rendered = render_service_logs_text_for_tui(&self.paths, &service_id, false) .unwrap_or_else(|error| format!("Service log lookup failed.\n\n{error}")); self.remember_current_chat_session(); - self.doctor_manager = None; + self.examine_manager = None; self.runtime_manager = None; self.install_manager = None; self.engine_manager = None; @@ -9606,20 +9606,20 @@ impl App { if self .running_job .as_ref() - .is_some_and(|job| matches!(job.kind, RunningJobKind::DoctorRefresh)) + .is_some_and(|job| matches!(job.kind, RunningJobKind::ExamineRefresh)) { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); } if self .running_job .as_ref() - .is_some_and(|job| !matches!(job.kind, RunningJobKind::DoctorRefresh)) + .is_some_and(|job| !matches!(job.kind, RunningJobKind::ExamineRefresh)) && input .strip_prefix('/') - .is_some_and(|command| is_doctor_command_input(command.trim_start())) + .is_some_and(|command| is_examine_command_input(command.trim_start())) { - self.doctor_blocked_by_current_workflow(); + self.examine_blocked_by_current_workflow(); return; } @@ -9739,7 +9739,7 @@ impl App { None, vec![ CommandScreenAction::OpenCommand("setup"), - CommandScreenAction::OpenCommand("doctor"), + CommandScreenAction::OpenCommand("examine"), CommandScreenAction::OpenCommand("serve"), CommandScreenAction::OpenCommand("help"), CommandScreenAction::Back, @@ -9756,7 +9756,7 @@ impl App { None, vec![ CommandScreenAction::OpenCommand("setup"), - CommandScreenAction::OpenCommand("doctor"), + CommandScreenAction::OpenCommand("examine"), CommandScreenAction::OpenCommand("serve"), CommandScreenAction::OpenCommand("help"), CommandScreenAction::Back, @@ -9798,8 +9798,8 @@ impl App { if !head.is_empty() { self.mode = tui_mode_for_command(head, !args.is_empty()); } - if !matches!(head, "" | "doctor") { - self.discard_background_doctor_refresh(); + if !matches!(head, "" | "examine") { + self.discard_background_examine_refresh(); } match head { @@ -9812,9 +9812,9 @@ impl App { self.open_help_overlay(); true } - "doctor" => { - if !self.doctor_blocked_by_current_workflow() { - self.open_doctor_manager(); + "examine" => { + if !self.examine_blocked_by_current_workflow() { + self.open_examine_manager(); } true } @@ -10796,18 +10796,18 @@ impl App { self.start_cli_command_with_kind(title, args, RunningJobKind::Cli, None); } - fn start_doctor_refresh_job(&mut self) -> bool { + fn start_examine_refresh_job(&mut self) -> bool { if self.running_job.is_some() { let message = if self .running_job .as_ref() - .is_some_and(|job| matches!(job.kind, RunningJobKind::DoctorRefresh)) + .is_some_and(|job| matches!(job.kind, RunningJobKind::ExamineRefresh)) { - "Doctor is already checking this computer.\n\nYou can keep using the arrow keys while it finishes." + "Examine is already checking this computer.\n\nYou can keep using the arrow keys while it finishes." } else { - "Another action is already running.\n\nWait for it to finish, then refresh Doctor again." + "Another action is already running.\n\nWait for it to finish, then refresh Examine again." }; - if let Some(state) = self.doctor_manager.as_mut() { + if let Some(state) = self.examine_manager.as_mut() { state.message = Some(message.to_owned()); } self.status = "A command is already running; wait for it to finish.".to_owned(); @@ -10815,7 +10815,7 @@ impl App { } let (sender, receiver) = mpsc::channel(); std::thread::spawn(move || { - let result = render_doctor_text() + let result = render_examine_text() .map(|rendered| CommandOutput { ok: true, rendered, @@ -10825,8 +10825,8 @@ impl App { let _ = sender.send(RunningJobEvent::Finished(result)); }); self.running_job = Some(RunningJob { - title: "Doctor".to_owned(), - kind: RunningJobKind::DoctorRefresh, + title: "Examine".to_owned(), + kind: RunningJobKind::ExamineRefresh, receiver, started_at: Instant::now(), streamed_lines: 0, @@ -10836,20 +10836,20 @@ impl App { self.running_job_log_scroll = 0; self.running_job_output.clear(); self.running_job_cancel_requested = None; - self.record_activity("doctor check started"); + self.record_activity("examine check started"); self.status = "Checking this computer...".to_owned(); true } - fn discard_background_doctor_refresh(&mut self) { + fn discard_background_examine_refresh(&mut self) { if self .running_job .as_ref() - .is_some_and(|job| matches!(job.kind, RunningJobKind::DoctorRefresh)) + .is_some_and(|job| matches!(job.kind, RunningJobKind::ExamineRefresh)) { self.running_job = None; self.running_job_cancel_requested = None; - self.record_activity("doctor check superseded"); + self.record_activity("examine check superseded"); } } @@ -10869,7 +10869,7 @@ impl App { kind: RunningJobKind, display_command: Option, ) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); if self.running_job.is_some() { self.status = "A command is already running; wait for it to finish.".to_owned(); return false; @@ -10917,7 +10917,7 @@ impl App { } fn active_screen_handles_command_output(&self, title: &str) -> bool { - (self.doctor_manager.is_some() && title == "Doctor") + (self.examine_manager.is_some() && title == "Examine") || (self.runtime_manager.is_some() && matches!(title, "Runtimes" | "Install")) || (self.install_manager.is_some() && matches!(title, "Install" | "Install Preview")) || (self.engine_manager.is_some() && title == "Engine") @@ -10943,7 +10943,7 @@ impl App { args: Vec, kind: RunningJobKind, ) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); if self.running_job.is_some() { self.status = "A command is already running; wait for it to finish.".to_owned(); return false; @@ -10992,7 +10992,7 @@ impl App { rocm_tools: bool, model: Option, ) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); if self.running_job.is_some() { self.status = "A command is already running; wait for it to finish.".to_owned(); return false; @@ -11058,7 +11058,7 @@ impl App { } fn start_planner_job(&mut self, title: &str, request: &str) -> bool { - self.discard_background_doctor_refresh(); + self.discard_background_examine_refresh(); if self.running_job.is_some() { self.status = "A command is already running; wait for it to finish.".to_owned(); return false; @@ -11517,35 +11517,35 @@ impl App { let mut advance_onboarding_after_job = false; let mut chat_auto_follow_up = false; match (kind, result) { - (RunningJobKind::DoctorRefresh, Ok(output)) => { - if let Some(state) = self.doctor_manager.as_mut() { + (RunningJobKind::ExamineRefresh, Ok(output)) => { + if let Some(state) = self.examine_manager.as_mut() { if output.ok { state.report = output.rendered; state.message = None; state.detail_scroll = 0; } else { state.message = - Some(format!("Doctor check failed.\n\n{}", output.rendered)); + Some(format!("Examine check failed.\n\n{}", output.rendered)); } } self.status = if output.ok { - "Doctor refreshed.".to_owned() + "Examine refreshed.".to_owned() } else { - "Doctor check failed.".to_owned() + "Examine check failed.".to_owned() }; self.record_activity(if output.ok { - "doctor check completed".to_owned() + "examine check completed".to_owned() } else { - "doctor check failed".to_owned() + "examine check failed".to_owned() }); } - (RunningJobKind::DoctorRefresh, Err(error)) => { - if let Some(state) = self.doctor_manager.as_mut() { - state.message = Some(format!("Doctor check failed.\n\n{error}")); + (RunningJobKind::ExamineRefresh, Err(error)) => { + if let Some(state) = self.examine_manager.as_mut() { + state.message = Some(format!("Examine check failed.\n\n{error}")); state.detail_scroll = 0; } - self.status = "Doctor check failed.".to_owned(); - self.record_activity("doctor check failed"); + self.status = "Examine check failed.".to_owned(); + self.record_activity("examine check failed"); } (RunningJobKind::Cli, Ok(output)) => { let rendered = output.rendered.clone(); @@ -12935,7 +12935,7 @@ fn should_parse_plain_input_as_command(input: &str) -> bool { }; let args = parts.iter().skip(1).map(String::as_str).collect::>(); match head { - "home" | "help" | "?" | "doctor" | "setup" | "permissions" | "runtimes" | "engine" + "home" | "help" | "?" | "examine" | "setup" | "permissions" | "runtimes" | "engine" | "model" | "plan" | "config" | "automations" | "reviews" | "proposals" | "approve" | "reject" | "edit" | "services" | "logs" | "gpu" | "daemon" | "chat" | "provider" | "clear" | "quit" | "exit" => true, @@ -12997,7 +12997,7 @@ fn is_plain_casual_input(input: &str) -> bool { fn is_plain_inspection_request(lower: &str) -> bool { let inspectish = lower .split(|ch: char| !ch.is_ascii_alphanumeric() && ch != '.') - .any(|word| matches!(word, "inspect" | "check" | "status" | "doctor")) + .any(|word| matches!(word, "inspect" | "check" | "status" | "examine")) || lower.contains("what is installed") || lower.contains("what's installed") || lower.contains("is installed"); @@ -15866,7 +15866,7 @@ fn matching_slash_commands(prefix: &str) -> Vec<&'static SlashCommandSpec> { fn slash_command_completion_label(command: &SlashCommandSpec) -> String { let description = match command.name { "help" | "?" => "choose a command", - "doctor" => "check this computer", + "examine" => "check this computer", "setup" => "set up ROCm", "permissions" => "change approvals", "runtimes" => "choose ROCm install", @@ -16671,8 +16671,8 @@ fn handle_mouse(app: &mut App, mouse: MouseEvent) { app.scroll_logs_view_detail_modal(lines); return; } - if app.doctor_manager.is_some() { - app.scroll_doctor_manager_detail(lines); + if app.examine_manager.is_some() { + app.scroll_examine_manager_detail(lines); } else if app.logs_view.is_some() { app.scroll_logs_view_detail(lines); } else if app.runtime_manager.is_some() @@ -16893,7 +16893,7 @@ fn handle_key(app: &mut App, key: KeyEvent) { return; } - if app.doctor_manager.is_some() && handle_doctor_manager_key(app, key) { + if app.examine_manager.is_some() && handle_examine_manager_key(app, key) { return; } @@ -17232,7 +17232,7 @@ fn clear_prompt_for_active_surface_action(app: &mut App, key: KeyEvent) { } if app.onboarding_active - || app.doctor_manager.is_some() + || app.examine_manager.is_some() || app.logs_view.is_some() || app.runtime_manager.is_some() || app.install_manager.is_some() @@ -17487,7 +17487,7 @@ fn handle_running_job_modal_key(app: &mut App, key: KeyEvent) -> bool { true } -fn handle_doctor_manager_key(app: &mut App, key: KeyEvent) -> bool { +fn handle_examine_manager_key(app: &mut App, key: KeyEvent) -> bool { if active_surface_should_defer_to_prompt(app, key) { return false; } @@ -17495,50 +17495,50 @@ fn handle_doctor_manager_key(app: &mut App, key: KeyEvent) -> bool { match (key.modifiers, key.code) { (KeyModifiers::CONTROL, KeyCode::Char('c')) => false, (_, KeyCode::Up | KeyCode::BackTab) => { - app.move_doctor_manager_selection(CompletionDirection::Previous); + app.move_examine_manager_selection(CompletionDirection::Previous); true } (_, KeyCode::Down | KeyCode::Tab) => { - app.move_doctor_manager_selection(CompletionDirection::Next); + app.move_examine_manager_selection(CompletionDirection::Next); true } (_, KeyCode::PageUp) => { - app.scroll_doctor_manager_detail(-10); + app.scroll_examine_manager_detail(-10); true } (_, KeyCode::PageDown) => { - app.scroll_doctor_manager_detail(10); + app.scroll_examine_manager_detail(10); true } (_, KeyCode::Home) => { - if let Some(state) = app.doctor_manager.as_mut() { + if let Some(state) = app.examine_manager.as_mut() { state.selected = 0; state.message = None; state.detail_scroll = 0; } - app.status = "Moved to first doctor check.".to_owned(); + app.status = "Moved to first examine check.".to_owned(); true } (_, KeyCode::End) => { - if let Some(state) = app.doctor_manager.as_mut() { - state.selected = doctor_manager_choices().len().saturating_sub(1); + if let Some(state) = app.examine_manager.as_mut() { + state.selected = examine_manager_choices().len().saturating_sub(1); state.message = None; state.detail_scroll = 0; } - app.status = "Moved to last doctor choice.".to_owned(); + app.status = "Moved to last examine choice.".to_owned(); true } (_, KeyCode::F(5)) => { - app.refresh_doctor_manager(); + app.refresh_examine_manager(); true } (KeyModifiers::CONTROL, KeyCode::Char('j' | 'm')) | (_, KeyCode::Char('\n' | '\r') | KeyCode::Enter) => { - app.perform_doctor_manager_selected_action(); + app.perform_examine_manager_selected_action(); true } (_, KeyCode::Esc | KeyCode::Char('q' | 'Q')) => { - app.close_doctor_manager(); + app.close_examine_manager(); true } _ => false, @@ -19257,8 +19257,8 @@ fn draw(frame: &mut Frame<'_>, app: &App) { .constraints([Constraint::Min(60), Constraint::Length(sidebar_width)]) .split(layout[0]); - if app.doctor_manager.is_some() { - draw_doctor_manager(frame, app, body[0]); + if app.examine_manager.is_some() { + draw_examine_manager(frame, app, body[0]); } else if app.logs_view.is_some() { draw_logs_view(frame, app, body[0]); } else if app.runtime_manager.is_some() { @@ -19403,7 +19403,7 @@ fn draw(frame: &mut Frame<'_>, app: &App) { .is_some_and(|state| state.detail_modal.is_some()) { "Up/Down scroll | PageUp/PageDown scroll | Esc close" - } else if app.doctor_manager.is_some() { + } else if app.examine_manager.is_some() { "Up/Down choose | Enter select | PageUp/PageDown scroll | F5 refresh | Esc back" } else if app.runtime_manager.is_some() && app.pending_approval.is_none() { "Up/Down choose | Enter use highlighted row | PageUp/PageDown scroll | Esc back" @@ -19577,7 +19577,7 @@ fn should_draw_prompt_box(app: &App) -> bool { { return true; } - app.doctor_manager.is_none() + app.examine_manager.is_none() && app.logs_view.is_none() && app.runtime_manager.is_none() && app.install_manager.is_none() @@ -19797,7 +19797,7 @@ fn render_home_dashboard_detail_text(app: &App) -> String { HomeDashboardAction::Setup => { "Install ROCm into a Python folder managed by rocm-cli.\n\nThis is the first thing to do on a new machine." } - HomeDashboardAction::Doctor => { + HomeDashboardAction::Examine => { "Check your GPU, ROCm install, model runner, driver, and useful folders.\n\nThis does not change your computer." } HomeDashboardAction::Serve => { @@ -19850,7 +19850,7 @@ fn home_dashboard_actions(app: &App) -> Vec { HomeDashboardAction::Chat, HomeDashboardAction::ComfyUi, HomeDashboardAction::Services, - HomeDashboardAction::Doctor, + HomeDashboardAction::Examine, HomeDashboardAction::Engine, HomeDashboardAction::Help, HomeDashboardAction::Quit, @@ -19858,7 +19858,7 @@ fn home_dashboard_actions(app: &App) -> Vec { } else { vec![ HomeDashboardAction::Setup, - HomeDashboardAction::Doctor, + HomeDashboardAction::Examine, HomeDashboardAction::Permissions, HomeDashboardAction::Help, HomeDashboardAction::Quit, @@ -19869,7 +19869,7 @@ fn home_dashboard_actions(app: &App) -> Vec { const fn home_dashboard_action_label(action: HomeDashboardAction) -> &'static str { match action { HomeDashboardAction::Setup => "Set up ROCm", - HomeDashboardAction::Doctor => "Run setup check", + HomeDashboardAction::Examine => "Run setup check", HomeDashboardAction::Serve => "Start a local model", HomeDashboardAction::Chat => "Chat with assistant", HomeDashboardAction::ComfyUi => "Open ComfyUI", @@ -19884,7 +19884,7 @@ const fn home_dashboard_action_label(action: HomeDashboardAction) -> &'static st const fn home_dashboard_action_status(action: HomeDashboardAction) -> &'static str { match action { HomeDashboardAction::Setup => "Set up ROCm selected. Press Enter.", - HomeDashboardAction::Doctor => "Setup check selected. Press Enter.", + HomeDashboardAction::Examine => "Setup check selected. Press Enter.", HomeDashboardAction::Serve => "Start a local model selected. Press Enter.", HomeDashboardAction::Chat => "Local assistant selected. Press Enter.", HomeDashboardAction::ComfyUi => "ComfyUI selected. Press Enter.", @@ -19899,7 +19899,7 @@ const fn home_dashboard_action_status(action: HomeDashboardAction) -> &'static s const fn home_dashboard_action_command(action: HomeDashboardAction) -> &'static str { match action { HomeDashboardAction::Setup => "setup", - HomeDashboardAction::Doctor => "doctor", + HomeDashboardAction::Examine => "examine", HomeDashboardAction::Serve => "serve", HomeDashboardAction::Chat => "chat --tools", HomeDashboardAction::ComfyUi => "comfyui", @@ -20465,23 +20465,23 @@ fn modal_detail_scroll(app: &App) -> u16 { .map_or(0, |pending| pending.detail_scroll) } -fn draw_doctor_manager(frame: &mut Frame<'_>, app: &App, area: Rect) { - let Some(state) = app.doctor_manager.as_ref() else { +fn draw_examine_manager(frame: &mut Frame<'_>, app: &App, area: Rect) { + let Some(state) = app.examine_manager.as_ref() else { return; }; let panes = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(34), Constraint::Min(38)]) .split(area); - let choices = doctor_manager_choices(); + let choices = examine_manager_choices(); let items = choices .iter() - .map(|choice| ListItem::new(doctor_manager_label(*choice))) + .map(|choice| ListItem::new(examine_manager_label(*choice))) .collect::>(); let mut list_state = ListState::default(); list_state.select(Some(state.selected.min(items.len().saturating_sub(1)))); let title = format!( - "Doctor {}/{}", + "Examine {}/{}", state.selected.saturating_add(1).min(choices.len()), choices.len() ); @@ -20493,7 +20493,7 @@ fn draw_doctor_manager(frame: &mut Frame<'_>, app: &App, area: Rect) { draw_scrollable_text_with_block( frame, - doctor_manager_detail_text(app), + examine_manager_detail_text(app), panes[1], detail_block("Details"), Style::default().fg(THEME_TEXT).bg(THEME_PANEL), @@ -21072,27 +21072,27 @@ fn draw_logs_view_detail_modal(frame: &mut Frame<'_>, app: &App, area: Rect) { ); } -const fn doctor_manager_choices() -> &'static [DoctorManagerChoice] { +const fn examine_manager_choices() -> &'static [ExamineManagerChoice] { &[ - DoctorManagerChoice::Overview, - DoctorManagerChoice::Gpu, - DoctorManagerChoice::Runtimes, - DoctorManagerChoice::Engines, - DoctorManagerChoice::DriverAndWsl, - DoctorManagerChoice::Folders, - DoctorManagerChoice::Back, + ExamineManagerChoice::Overview, + ExamineManagerChoice::Gpu, + ExamineManagerChoice::Runtimes, + ExamineManagerChoice::Engines, + ExamineManagerChoice::DriverAndWsl, + ExamineManagerChoice::Folders, + ExamineManagerChoice::Back, ] } -const fn doctor_manager_label(choice: DoctorManagerChoice) -> &'static str { +const fn examine_manager_label(choice: ExamineManagerChoice) -> &'static str { match choice { - DoctorManagerChoice::Overview => "Overview", - DoctorManagerChoice::Gpu => "GPU", - DoctorManagerChoice::Runtimes => "ROCm installs", - DoctorManagerChoice::Engines => "Engines", - DoctorManagerChoice::DriverAndWsl => "Driver and WSL", - DoctorManagerChoice::Folders => "Folders", - DoctorManagerChoice::Back => "Back", + ExamineManagerChoice::Overview => "Overview", + ExamineManagerChoice::Gpu => "GPU", + ExamineManagerChoice::Runtimes => "ROCm installs", + ExamineManagerChoice::Engines => "Engines", + ExamineManagerChoice::DriverAndWsl => "Driver and WSL", + ExamineManagerChoice::Folders => "Folders", + ExamineManagerChoice::Back => "Back", } } @@ -22431,7 +22431,7 @@ fn config_manager_detail_text(app: &App) -> String { "", &format!("Current: {mode}"), "", - "When this is on, rocm-cli reads local AMD GPU status for doctor and model-fit screens.", + "When this is on, rocm-cli reads local AMD GPU status for examine and model-fit screens.", "No cloud service is used.", "", "Press Enter to toggle this.", @@ -22743,7 +22743,7 @@ fn help_topic_detail(topic: HelpTopic) -> String { "- Start a local model.", "- Chat with the assistant.", "- Open ComfyUI.", - "- Check this computer with Doctor.", + "- Check this computer with Examine.", "", "ROCm CLI keeps settings in ~/.rocm.", "The pip cache stays inside the ROCm install folder you choose.", @@ -22858,7 +22858,7 @@ fn help_topic_detail(topic: HelpTopic) -> String { "", "Common commands:", " rocm", - " rocm doctor", + " rocm examine", " rocm setup reset", " rocm install sdk --channel release --format wheel --prefix ", " rocm runtimes list", @@ -22873,7 +22873,7 @@ fn help_topic_detail(topic: HelpTopic) -> String { "", "Inside the TUI, slash commands still work:", " /setup", - " /doctor", + " /examine", " /engine", " /serve", " /comfyui", @@ -22925,7 +22925,7 @@ fn help_topic_detail(topic: HelpTopic) -> String { "- Use F5 to refresh setup status.", "", "If GPU checks fail:", - "- Run rocm doctor.", + "- Run rocm examine.", "- On Windows, make sure the AMD driver is installed.", "- On WSL, run from the Linux filesystem when possible, not /mnt.", "", @@ -22963,7 +22963,7 @@ fn help_command_screen_actions() -> Vec { [ "home", "setup", - "doctor", + "examine", "install", "runtimes", "engine", @@ -23000,7 +23000,7 @@ fn help_command_label(command: &str) -> &'static str { "home" => "Home dashboard", "help" => "Command list", "setup" => "Set up ROCm", - "doctor" => "Check this computer", + "examine" => "Check this computer", "install" => "Install ROCm", "runtimes" => "Choose ROCm install", "engine" => "Choose engine", @@ -23049,7 +23049,7 @@ fn help_command_detail(command: &str) -> String { "Set up ROCm", "Install ROCm into a Python folder managed by rocm-cli. This is the first screen new users see.", ), - "doctor" => ( + "examine" => ( "Check this computer", "Inspect GPU detection, driver state, ROCm installs, engines, WSL, and folders.", ), @@ -23687,41 +23687,41 @@ fn install_sdk_detail_text(app: &App) -> String { output.trim_end().to_owned() } -fn doctor_manager_detail_text(app: &App) -> String { - let Some(state) = app.doctor_manager.as_ref() else { +fn examine_manager_detail_text(app: &App) -> String { + let Some(state) = app.examine_manager.as_ref() else { return String::new(); }; if let Some(message) = state.message.as_deref() { return message.to_owned(); } let report = &state.report; - match doctor_manager_choices() + match examine_manager_choices() .get(state.selected) .copied() - .unwrap_or(DoctorManagerChoice::Overview) + .unwrap_or(ExamineManagerChoice::Overview) { - DoctorManagerChoice::Overview => doctor_overview_text(report), - DoctorManagerChoice::Gpu => doctor_gpu_text(report), - DoctorManagerChoice::Runtimes => doctor_runtimes_text(report), - DoctorManagerChoice::Engines => doctor_engines_text(report), - DoctorManagerChoice::DriverAndWsl => doctor_driver_wsl_text(report), - DoctorManagerChoice::Folders => doctor_folders_text(report), - DoctorManagerChoice::Back => "Return to the main screen.".to_owned(), - } -} - -fn doctor_overview_text(report: &str) -> String { - let os = doctor_report_value(report, "os"); - let arch = doctor_report_value(report, "arch"); - let distro = doctor_report_value(report, "distro"); - let cpu = doctor_report_value(report, "cpu"); - let ram = doctor_report_value(report, "system_ram"); - let gfx = doctor_report_value(report, "detected_gfx_target"); - let family = doctor_report_value(report, "compatible_therock_family"); - let installed_family = doctor_report_value(report, "detected_therock_family"); - let runtime_status = doctor_report_value(report, "active_runtime_status"); - let driver_status = doctor_report_value(report, "driver_status"); - let default_engine = doctor_report_value(report, "effective_default_engine"); + ExamineManagerChoice::Overview => examine_overview_text(report), + ExamineManagerChoice::Gpu => examine_gpu_text(report), + ExamineManagerChoice::Runtimes => examine_runtimes_text(report), + ExamineManagerChoice::Engines => examine_engines_text(report), + ExamineManagerChoice::DriverAndWsl => examine_driver_wsl_text(report), + ExamineManagerChoice::Folders => examine_folders_text(report), + ExamineManagerChoice::Back => "Return to the main screen.".to_owned(), + } +} + +fn examine_overview_text(report: &str) -> String { + let os = examine_report_value(report, "os"); + let arch = examine_report_value(report, "arch"); + let distro = examine_report_value(report, "distro"); + let cpu = examine_report_value(report, "cpu"); + let ram = examine_report_value(report, "system_ram"); + let gfx = examine_report_value(report, "detected_gfx_target"); + let family = examine_report_value(report, "compatible_therock_family"); + let installed_family = examine_report_value(report, "detected_therock_family"); + let runtime_status = examine_report_value(report, "active_runtime_status"); + let driver_status = examine_report_value(report, "driver_status"); + let default_engine = examine_report_value(report, "effective_default_engine"); let mut output = String::new(); let _ = writeln!(output, "ROCm check"); let _ = writeln!(output); @@ -23732,7 +23732,7 @@ fn doctor_overview_text(report: &str) -> String { let _ = writeln!(output, " Memory: {ram}"); let _ = writeln!(output); let _ = writeln!(output, "GPU"); - let _ = writeln!(output, " AMD GPU: {}", doctor_gpu_display_name(report)); + let _ = writeln!(output, " AMD GPU: {}", examine_gpu_display_name(report)); let _ = writeln!(output, " Target: {gfx}"); let _ = writeln!(output, " ROCm package: {family}"); let _ = writeln!(output, " Installed ROCm package: {installed_family}"); @@ -23757,16 +23757,16 @@ fn doctor_overview_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_gpu_text(report: &str) -> String { - let gfx = doctor_report_value(report, "detected_gfx_target"); - let family = doctor_report_value(report, "compatible_therock_family"); - let installed_family = doctor_report_value(report, "detected_therock_family"); - let driver_status = doctor_report_value(report, "driver_status"); +fn examine_gpu_text(report: &str) -> String { + let gfx = examine_report_value(report, "detected_gfx_target"); + let family = examine_report_value(report, "compatible_therock_family"); + let installed_family = examine_report_value(report, "detected_therock_family"); + let driver_status = examine_report_value(report, "driver_status"); let mut output = String::new(); let _ = writeln!(output, "GPU"); let _ = writeln!(output); let _ = writeln!(output, "Detected AMD GPU"); - let _ = writeln!(output, " {}", doctor_gpu_display_name(report)); + let _ = writeln!(output, " {}", examine_gpu_display_name(report)); let _ = writeln!(output); let _ = writeln!(output, "Target"); let _ = writeln!(output, " {gfx}"); @@ -23789,26 +23789,26 @@ fn doctor_gpu_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_gpu_display_name(report: &str) -> String { - let detail = doctor_report_value(report, "driver_detail"); +fn examine_gpu_display_name(report: &str) -> String { + let detail = examine_report_value(report, "driver_detail"); if detail != "not checked" && detail != "not detected" && detail != "none found" { return detail; } - let gfx = doctor_report_value(report, "detected_gfx_target"); + let gfx = examine_report_value(report, "detected_gfx_target"); if gfx != "not checked" && gfx != "not detected" && gfx != "none found" { return "detected AMD GPU".to_owned(); } "not detected yet".to_owned() } -fn doctor_runtimes_text(report: &str) -> String { - let count = doctor_report_value(report, "managed_runtimes"); - let active_id = doctor_report_value(report, "active_runtime_id"); - let active_status = doctor_report_value(report, "active_runtime_status"); - let active_root = doctor_report_value(report, "active_runtime_root"); - let active_version = doctor_report_value(report, "active_runtime_version"); - let active_family = doctor_report_value(report, "active_runtime_family"); - let registered = doctor_report_value(report, "registered_runtime_keys"); +fn examine_runtimes_text(report: &str) -> String { + let count = examine_report_value(report, "managed_runtimes"); + let active_id = examine_report_value(report, "active_runtime_id"); + let active_status = examine_report_value(report, "active_runtime_status"); + let active_root = examine_report_value(report, "active_runtime_root"); + let active_version = examine_report_value(report, "active_runtime_version"); + let active_family = examine_report_value(report, "active_runtime_family"); + let registered = examine_report_value(report, "registered_runtime_keys"); let mut output = String::new(); let _ = writeln!(output, "ROCm installs"); let _ = writeln!(output); @@ -23835,10 +23835,10 @@ fn doctor_runtimes_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_engines_text(report: &str) -> String { - let configured = doctor_report_value(report, "configured_default_engine"); - let effective = doctor_report_value(report, "effective_default_engine"); - let services = doctor_report_value(report, "managed_services"); +fn examine_engines_text(report: &str) -> String { + let configured = examine_report_value(report, "configured_default_engine"); + let effective = examine_report_value(report, "effective_default_engine"); + let services = examine_report_value(report, "managed_services"); let mut output = String::new(); let _ = writeln!(output, "Engines"); let _ = writeln!(output); @@ -23857,17 +23857,17 @@ fn doctor_engines_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_driver_wsl_text(report: &str) -> String { - let driver_policy = doctor_report_value(report, "driver_policy"); - let driver_status = doctor_report_value(report, "driver_status"); - let driver_detail = doctor_report_value(report, "driver_detail"); - let wsl = doctor_report_bool_value(report, "wsl"); - let dxg = doctor_report_bool_value(report, "wsl_dxg_device"); - let dxcore = doctor_report_bool_value(report, "wsl_dxcore"); - let librocdxg = doctor_report_bool_value(report, "wsl_librocdxg"); - let dids = doctor_report_bool_value(report, "wsl_rocdxg_dids"); - let cargo = doctor_report_bool_value(report, "wsl_cargo"); - let detail = doctor_report_value(report, "wsl_detail"); +fn examine_driver_wsl_text(report: &str) -> String { + let driver_policy = examine_report_value(report, "driver_policy"); + let driver_status = examine_report_value(report, "driver_status"); + let driver_detail = examine_report_value(report, "driver_detail"); + let wsl = examine_report_bool_value(report, "wsl"); + let dxg = examine_report_bool_value(report, "wsl_dxg_device"); + let dxcore = examine_report_bool_value(report, "wsl_dxcore"); + let librocdxg = examine_report_bool_value(report, "wsl_librocdxg"); + let dids = examine_report_bool_value(report, "wsl_rocdxg_dids"); + let cargo = examine_report_bool_value(report, "wsl_cargo"); + let detail = examine_report_value(report, "wsl_detail"); let mut output = String::new(); let _ = writeln!(output, "Driver and WSL"); let _ = writeln!(output); @@ -23891,23 +23891,23 @@ fn doctor_driver_wsl_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_folders_text(report: &str) -> String { - let config = doctor_report_value(report, "config_dir"); - let data = doctor_report_value(report, "data_dir"); - let cache = doctor_report_value(report, "cache_dir"); - let rocm_downloads = doctor_report_raw_value(report, "active_runtime_pip_cache_dir") - .or_else(|| doctor_report_raw_value(report, "setup_runtime_pip_cache_dir")) +fn examine_folders_text(report: &str) -> String { + let config = examine_report_value(report, "config_dir"); + let data = examine_report_value(report, "data_dir"); + let cache = examine_report_value(report, "cache_dir"); + let rocm_downloads = examine_report_raw_value(report, "active_runtime_pip_cache_dir") + .or_else(|| examine_report_raw_value(report, "setup_runtime_pip_cache_dir")) .map_or_else( || "choose a ROCm install folder first".to_owned(), - friendly_doctor_value, + friendly_examine_value, ); - let models = doctor_report_value(report, "model_cache_entries"); - let active_status = doctor_report_value(report, "active_runtime_status"); - let active_install = doctor_report_raw_value(report, "active_runtime_root") - .or_else(|| doctor_report_raw_value(report, "setup_runtime_root")) + let models = examine_report_value(report, "model_cache_entries"); + let active_status = examine_report_value(report, "active_runtime_status"); + let active_install = examine_report_raw_value(report, "active_runtime_root") + .or_else(|| examine_report_raw_value(report, "setup_runtime_root")) .map_or_else( || friendly_runtime_status(&active_status), - friendly_doctor_value, + friendly_examine_value, ); let mut output = String::new(); let _ = writeln!(output, "Folders"); @@ -23926,12 +23926,12 @@ fn doctor_folders_text(report: &str) -> String { output.trim_end().to_owned() } -fn doctor_report_value(report: &str, key: &str) -> String { - doctor_report_raw_value(report, key) - .map_or_else(|| "not checked".to_owned(), friendly_doctor_value) +fn examine_report_value(report: &str, key: &str) -> String { + examine_report_raw_value(report, key) + .map_or_else(|| "not checked".to_owned(), friendly_examine_value) } -fn doctor_report_raw_value<'a>(report: &'a str, key: &str) -> Option<&'a str> { +fn examine_report_raw_value<'a>(report: &'a str, key: &str) -> Option<&'a str> { let prefix = format!("{key}:"); report.lines().find_map(|line| { let trimmed = line.trim(); @@ -23942,8 +23942,8 @@ fn doctor_report_raw_value<'a>(report: &'a str, key: &str) -> Option<&'a str> { }) } -fn doctor_report_bool_value(report: &str, key: &str) -> &'static str { - match doctor_report_raw_value(report, key) { +fn examine_report_bool_value(report: &str, key: &str) -> &'static str { + match examine_report_raw_value(report, key) { Some("true") => "yes", Some("false") => "no", Some(_) => "not checked", @@ -23951,7 +23951,7 @@ fn doctor_report_bool_value(report: &str, key: &str) -> &'static str { } } -fn friendly_doctor_value(value: &str) -> String { +fn friendly_examine_value(value: &str) -> String { match value { "" | "" => "not checked".to_owned(), "" => "not set".to_owned(), @@ -27721,7 +27721,7 @@ fn proposal_sandbox_args(proposal: &AutomationProposalRecord) -> Result {} + "check_updates" | "driver_plan" | "examine_snapshot" | "list_servers" => {} _ => bail!( "review request `{}` cannot be run; it asks for an unsupported action", proposal.proposal_id @@ -28507,17 +28507,17 @@ mod tests { let mut app = test_app(); app.complete_running_job( - "Doctor".to_owned(), + "Examine".to_owned(), super::RunningJobKind::Cli, Ok(super::CommandOutput { ok: true, - rendered: "doctor ok".to_owned(), + rendered: "examine ok".to_owned(), chat_approval: None, }), super::StreamedOutputCounts::default(), ); - assert!(app.activity_text().contains("completed: Doctor")); + assert!(app.activity_text().contains("completed: Examine")); } #[test] @@ -28949,22 +28949,22 @@ mod tests { fn push_block_scrolls_to_new_block_header() { let mut app = test_app(); app.push_block("Old", "one\ntwo"); - app.push_block("Doctor", "summary\nmore"); + app.push_block("Examine", "summary\nmore"); - let doctor_line = app + let examine_line = app .transcript .iter() - .position(|line| line == "[Doctor]") - .expect("doctor block should be present"); - assert_eq!(usize::from(app.transcript_scroll), doctor_line); + .position(|line| line == "[Examine]") + .expect("examine block should be present"); + assert_eq!(usize::from(app.transcript_scroll), examine_line); } #[test] fn typed_transcript_records_line_kinds() { let mut app = test_app(); - app.push_user_input("/doctor"); - app.push_block("Doctor", "summary"); + app.push_user_input("/examine"); + app.push_block("Examine", "summary"); app.push_stream_line(super::CommandOutputStream::Stdout, "line"); assert!(matches!( @@ -28973,11 +28973,11 @@ mod tests { )); assert!(matches!( &app.transcript[2].kind, - super::TranscriptLineKind::BlockHeader { title } if title == "Doctor" + super::TranscriptLineKind::BlockHeader { title } if title == "Examine" )); assert!(matches!( &app.transcript[3].kind, - super::TranscriptLineKind::BlockBody { title } if title == "Doctor" + super::TranscriptLineKind::BlockBody { title } if title == "Examine" )); assert!(matches!( app.transcript.last().map(|line| &line.kind), @@ -29004,26 +29004,26 @@ mod tests { #[test] fn enter_accepts_unique_slash_completion() { let mut app = test_app(); - app.input = "/doc".to_owned(); + app.input = "/exa".to_owned(); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); - assert_eq!(app.input, "/doctor "); + assert_eq!(app.input, "/examine "); assert_eq!(app.status, "Accepted completion."); } #[test] fn exact_command_enter_submits_without_completion_menu() { let mut app = test_app(); - app.input = "/doctor".to_owned(); + app.input = "/examine".to_owned(); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); - assert!(app.doctor_manager.is_some()); + assert!(app.examine_manager.is_some()); assert!(app.command_screen.is_none()); - assert!(!app.transcript.iter().any(|line| line == "[Doctor]")); + assert!(!app.transcript.iter().any(|line| line == "[Examine]")); let rendered = render_test_terminal(&app, 120, 24); - assert!(rendered.contains("Doctor")); + assert!(rendered.contains("Examine")); assert!(rendered.contains("Overview")); assert!(rendered.contains("ROCm installs")); assert!(!rendered.contains("runtime_state:")); @@ -29033,23 +29033,23 @@ mod tests { } #[test] - fn doctor_screen_is_arrow_key_navigable() { + fn examine_screen_is_arrow_key_navigable() { let mut app = test_app(); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(matches!( app.running_job.as_ref().map(|job| &job.kind), - Some(super::RunningJobKind::DoctorRefresh) + Some(super::RunningJobKind::ExamineRefresh) )); assert_eq!( - app.doctor_manager.as_ref().map(|state| state.selected), + app.examine_manager.as_ref().map(|state| state.selected), Some(0) ); let rendered = render_test_terminal(&app, 120, 24); assert!(rendered.contains("Checking this computer")); handle_key(&mut app, key_event(KeyCode::Down, KeyModifiers::NONE)); assert_eq!( - app.doctor_manager.as_ref().map(|state| state.selected), + app.examine_manager.as_ref().map(|state| state.selected), Some(1) ); @@ -29059,14 +29059,14 @@ mod tests { } #[test] - fn doctor_background_completion_updates_report_without_transcript() { + fn examine_background_completion_updates_report_without_transcript() { let mut app = test_app(); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); app.running_job = None; app.complete_running_job( - "Doctor".to_owned(), - super::RunningJobKind::DoctorRefresh, + "Examine".to_owned(), + super::RunningJobKind::ExamineRefresh, Ok(super::CommandOutput { ok: true, rendered: "os: windows\narch: x86_64\ndetected_gfx_target: gfx1201\ncompatible_therock_family: gfx120X-all\ndetected_therock_family: \nactive_runtime_status: ready\ndriver_status: ready\neffective_default_engine: llama.cpp".to_owned(), @@ -29076,9 +29076,9 @@ mod tests { ); assert!(app.transcript.is_empty()); - assert_eq!(app.status, "Doctor refreshed."); + assert_eq!(app.status, "Examine refreshed."); assert_eq!( - app.doctor_manager + app.examine_manager .as_ref() .and_then(|state| state.message.as_deref()), None @@ -29090,7 +29090,7 @@ mod tests { } #[test] - fn doctor_folders_separates_app_data_from_custom_rocm_install_folder() { + fn examine_folders_separates_app_data_from_custom_rocm_install_folder() { let report = [ r"config_dir: C:\Users\jam\.rocm", r"data_dir: C:\Users\jam\.rocm", @@ -29102,7 +29102,7 @@ mod tests { ] .join("\n"); - let rendered = super::doctor_folders_text(&report); + let rendered = super::examine_folders_text(&report); assert!(rendered.contains(r"Settings: C:\Users\jam\.rocm")); assert!(rendered.contains(r"Logs and app data: C:\Users\jam\.rocm")); @@ -29114,7 +29114,7 @@ mod tests { } #[test] - fn doctor_folders_uses_setup_folder_for_rocm_downloads_before_activation() { + fn examine_folders_uses_setup_folder_for_rocm_downloads_before_activation() { let report = [ r"config_dir: C:\Users\jam\.rocm", r"data_dir: C:\Users\jam\.rocm", @@ -29126,7 +29126,7 @@ mod tests { ] .join("\n"); - let rendered = super::doctor_folders_text(&report); + let rendered = super::examine_folders_text(&report); assert!(rendered.contains(r"App metadata cache: C:\Users\jam\.rocm\cache")); assert!(rendered.contains(r"ROCm install downloads: D:\jam\temp\therock_venvs\pip-cache")); @@ -29134,9 +29134,9 @@ mod tests { } #[test] - fn hidden_doctor_refresh_does_not_block_next_prompt_commands() -> anyhow::Result<()> { + fn hidden_examine_refresh_does_not_block_next_prompt_commands() -> anyhow::Result<()> { let mut app = test_app(); - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/install"); assert!(app.install_manager.is_some()); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); @@ -29144,10 +29144,10 @@ mod tests { app.install_manager.as_ref().map(|state| &state.screen), Some(super::InstallManagerScreen::Sdk { .. }) )); - assert_no_background_doctor_block(&app, "install"); + assert_no_background_examine_block(&app, "install"); let mut app = test_app(); - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/runtimes"); assert!(app.runtime_manager.is_some()); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); @@ -29155,10 +29155,10 @@ mod tests { app.install_manager.as_ref().map(|state| &state.screen), Some(super::InstallManagerScreen::Sdk { .. }) )); - assert_no_background_doctor_block(&app, "runtimes"); + assert_no_background_examine_block(&app, "runtimes"); let mut app = test_app(); - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/update"); assert!(app.update_manager.is_some()); handle_key(&mut app, key_event(KeyCode::Down, KeyModifiers::NONE)); @@ -29169,7 +29169,7 @@ mod tests { Some(super::ApprovalAction::CliCommand { args, .. }) if args == &vec!["update".to_owned(), "--apply".to_owned()] )); - assert_no_background_doctor_block(&app, "update"); + assert_no_background_examine_block(&app, "update"); let mut app = test_app(); let mut record = ManagedServiceRecord::new( @@ -29188,7 +29188,7 @@ mod tests { ); record.status = "ready".to_owned(); record.write()?; - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/services"); assert!(app.services_manager.is_some()); let stop_index = app @@ -29223,86 +29223,86 @@ mod tests { } if service_id == "svc-qwen" ) )); - assert_no_background_doctor_block(&app, "services"); + assert_no_background_examine_block(&app, "services"); let mut app = test_app(); - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/clear"); assert_eq!( app.overlay_card.as_ref().map(|state| state.title.as_str()), Some("Clear") ); - assert_no_background_doctor_block(&app, "clear"); + assert_no_background_examine_block(&app, "clear"); let mut app = test_app(); - open_hidden_doctor_refresh_from_prompt(&mut app); + open_hidden_examine_refresh_from_prompt(&mut app); submit_prompt_command(&mut app, "/quit"); assert_eq!( app.overlay_card.as_ref().map(|state| state.title.as_str()), Some("Quit") ); - assert_no_background_doctor_block(&app, "quit"); + assert_no_background_examine_block(&app, "quit"); Ok(()) } #[test] - fn doctor_command_does_not_steal_running_workflow_screens() -> anyhow::Result<()> { + fn examine_command_does_not_steal_running_workflow_screens() -> anyhow::Result<()> { let mut app = test_app(); assert!(app.handle_command("install")); let sender = attach_running_job(&mut app, "Install", super::RunningJobKind::Cli); - submit_prompt_command(&mut app, "/doctor"); + submit_prompt_command(&mut app, "/examine"); assert!(app.install_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Install is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "install finished"); assert!(app.install_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); assert!(app.handle_command("install")); let sender = attach_running_job(&mut app, "Install", super::RunningJobKind::Cli); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(app.install_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Install is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "install finished"); assert!(app.install_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); assert!(app.handle_command("update")); let sender = attach_running_job(&mut app, "Update", super::RunningJobKind::Cli); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(app.update_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Update is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "update finished"); assert!(app.update_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); @@ -29332,19 +29332,19 @@ mod tests { }, ); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(app.services_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Service action is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "service stopped"); assert!(app.services_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); @@ -29363,7 +29363,7 @@ mod tests { }, ); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert_eq!( app.command_screen @@ -29371,11 +29371,11 @@ mod tests { .map(|state| state.title.as_str()), Some("Chat") ); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Chat is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "provider: local"); @@ -29385,7 +29385,7 @@ mod tests { .map(|state| state.title.as_str()), Some("Chat") ); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); @@ -29404,7 +29404,7 @@ mod tests { }, ); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert_eq!( app.command_screen @@ -29412,11 +29412,11 @@ mod tests { .map(|state| state.title.as_str()), Some("Plan") ); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Planning is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "plan ready"); @@ -29426,33 +29426,33 @@ mod tests { .map(|state| state.title.as_str()), Some("Plan") ); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); let mut app = test_app(); app.open_serve_wizard(); let sender = attach_running_job(&mut app, "Serve", super::RunningJobKind::Cli); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(app.serve_wizard.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Serve is still running") - && message.contains("Doctor can check this computer after it finishes") + && message.contains("Examine can check this computer after it finishes") }) ); finish_running_job(&mut app, sender, "serve finished"); assert!(app.serve_wizard.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.transcript.is_empty()); Ok(()) } #[test] - fn doctor_command_does_not_hide_pending_approval() -> anyhow::Result<()> { + fn examine_command_does_not_hide_pending_approval() -> anyhow::Result<()> { let mut app = test_app(); fs::create_dir_all(app.paths.data_dir.join("envs"))?; assert!(app.handle_command("runtimes")); @@ -29461,15 +29461,15 @@ mod tests { handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); assert!(app.pending_approval.is_some()); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); assert!(app.install_manager.is_some()); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(app.pending_approval.is_some()); assert!( super::active_screen_message_text(&app).is_some_and(|message| { message.contains("Install ROCm is waiting for your review") - && message.contains("before opening Doctor") + && message.contains("before opening Examine") }) ); handle_key(&mut app, key_event(KeyCode::Esc, KeyModifiers::NONE)); @@ -29481,25 +29481,29 @@ mod tests { } #[test] - fn doctor_page_keys_scroll_details_without_moving_selection() { + fn examine_page_keys_scroll_details_without_moving_selection() { let mut app = test_app(); - assert!(app.handle_command("doctor")); + assert!(app.handle_command("examine")); handle_key(&mut app, key_event(KeyCode::Down, KeyModifiers::NONE)); - let selected = app.doctor_manager.as_ref().map(|state| state.selected); + let selected = app.examine_manager.as_ref().map(|state| state.selected); handle_key(&mut app, key_event(KeyCode::PageDown, KeyModifiers::NONE)); assert_eq!( - app.doctor_manager.as_ref().map(|state| state.selected), + app.examine_manager.as_ref().map(|state| state.selected), selected ); assert_eq!( - app.doctor_manager.as_ref().map(|state| state.detail_scroll), + app.examine_manager + .as_ref() + .map(|state| state.detail_scroll), Some(10) ); handle_key(&mut app, key_event(KeyCode::PageUp, KeyModifiers::NONE)); assert_eq!( - app.doctor_manager.as_ref().map(|state| state.detail_scroll), + app.examine_manager + .as_ref() + .map(|state| state.detail_scroll), Some(0) ); } @@ -29514,7 +29518,7 @@ mod tests { .expect("slash input should render suggestions"); assert_eq!(menu.title, "Completions"); - assert!(menu.items.iter().any(|item| item.contains("/doctor"))); + assert!(menu.items.iter().any(|item| item.contains("/examine"))); assert!(menu.items.iter().any(|item| item.contains("/help"))); assert!(menu.items.iter().any(|item| item.contains("/reviews"))); assert!(!menu.items.iter().any(|item| item.starts_with("/proposals"))); @@ -29525,17 +29529,17 @@ mod tests { #[test] fn friendly_slash_completion_inserts_exact_command() { let mut app = test_app(); - app.input = "/do".to_owned(); + app.input = "/ex".to_owned(); app.input_cursor = app.input_len_chars(); let menu = app .slash_completion_menu() - .expect("/do should render the /doctor suggestion"); + .expect("/ex should render the /examine suggestion"); assert!(menu.items[menu.selected].contains("check this computer")); assert!(app.accept_completion()); - assert_eq!(app.input, "/doctor "); + assert_eq!(app.input, "/examine "); assert_eq!(app.input_cursor, app.input_len_chars()); } @@ -29569,17 +29573,17 @@ mod tests { assert!(super::surface_command_prompt_active(&app)); let rendered = render_test_terminal(&app, 120, 30); assert!(rendered.contains("Command")); - assert!(rendered.contains("/doctor")); + assert!(rendered.contains("/examine")); assert!(!rendered.contains("Prompt")); - for ch in "doctor".chars() { + for ch in "examine".chars() { handle_key(&mut app, key_event(KeyCode::Char(ch), KeyModifiers::NONE)); } - assert_eq!(app.input, "/doctor"); + assert_eq!(app.input, "/examine"); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); - assert!(app.doctor_manager.is_some()); + assert!(app.examine_manager.is_some()); assert!(app.input.is_empty()); assert!(app.overlay_card.is_none()); } @@ -30203,17 +30207,17 @@ mod tests { height: 30, }; let list = super::home_dashboard_list_inner_rect(&app, area).expect("home list"); - let doctor_row = 1; + let examine_row = 1; assert!(super::click_home_dashboard( &mut app, list.x, - list.y + doctor_row, + list.y + examine_row, area.width, area.height, )); - assert!(app.doctor_manager.is_some()); + assert!(app.examine_manager.is_some()); assert!(!app.should_draw_home_dashboard()); assert!(app.transcript.is_empty()); } @@ -30242,7 +30246,7 @@ mod tests { #[test] fn home_dashboard_preserves_transcript_when_reopened() { let mut app = test_app(); - app.push_block("Doctor", "GPU check output"); + app.push_block("Examine", "GPU check output"); assert!(!app.should_draw_home_dashboard()); assert!(app.transcript_text().contains("GPU check output")); @@ -31268,12 +31272,12 @@ mod tests { assert!(rendered.contains("Model type recommended model")); let mut app = test_app(); - assert!(app.handle_command("doctor")); - assert!(app.doctor_manager.is_some()); + assert!(app.handle_command("examine")); + assert!(app.examine_manager.is_some()); assert!(app.command_screen.is_none()); assert!(app.transcript.is_empty()); let rendered = render_test_terminal(&app, 120, 24); - assert!(rendered.contains("Doctor")); + assert!(rendered.contains("Examine")); assert!(rendered.contains("Overview")); assert!(!rendered.contains("> Refresh")); assert!(!rendered.contains(" Refresh")); @@ -31301,7 +31305,7 @@ mod tests { handle_key(&mut app, key_event(KeyCode::Down, KeyModifiers::NONE)); handle_key(&mut app, key_event(KeyCode::Enter, KeyModifiers::NONE)); - assert!(app.doctor_manager.is_some()); + assert!(app.examine_manager.is_some()); assert!(app.command_screen.is_none()); assert!(app.overlay_card.is_none()); assert!(app.transcript.is_empty()); @@ -32394,7 +32398,7 @@ mod tests { #[test] fn static_command_screens_page_keys_scroll_details_without_changing_selection() { - for command in ["doctor", "logs", "help", "permissions"] { + for command in ["examine", "logs", "help", "permissions"] { let mut app = test_app(); assert!(app.handle_command(command), "{command}"); @@ -33929,7 +33933,7 @@ mod tests { assert!(app.command_screen_is_chat_session(), "{prompt}"); assert!(app.transcript.is_empty(), "{prompt}"); assert!(app.services_manager.is_none(), "{prompt}"); - assert!(app.doctor_manager.is_none(), "{prompt}"); + assert!(app.examine_manager.is_none(), "{prompt}"); assert!(app.install_manager.is_none(), "{prompt}"); poll_app_until_idle(&mut app); } else { @@ -34338,7 +34342,7 @@ mod tests { app.open_local_rocm_tools_chat_session(Some(record)); app.push_chat_session_turn( super::ChatSessionRole::Assistant, - "Use /doctor to refresh actual GPU memory fit before starting anything large.\nNative Windows note: Qwen/Qwen3.5-4B uses WSL/Linux through Windows.", + "Use /examine to refresh actual GPU memory fit before starting anything large.\nNative Windows note: Qwen/Qwen3.5-4B uses WSL/Linux through Windows.", ); app.set_input(prompt.to_owned()); @@ -34598,7 +34602,7 @@ chat response Assistant is checking ROCm first. ROCm checks used - Doctor: ok + Examine: ok os: windows data_dir: C:\\Users\\jam\\.rocm cache_dir: C:\\Users\\jam\\.rocm\\cache @@ -34627,7 +34631,7 @@ chat response Assistant is checking ROCm first. ROCm checks used - Doctor: ok + Examine: ok os: windows data_dir: C:\\Users\\jam\\.rocm @@ -34867,7 +34871,7 @@ Full log fn chat_session_approved_command_result_uses_detail_card_without_auto_followup() -> anyhow::Result<()> { let mut app = test_app(); - let (port, request_receiver) = spawn_fake_local_chat_server("Next, run Doctor.")?; + let (port, request_receiver) = spawn_fake_local_chat_server("Next, run Examine.")?; let mut record = ManagedServiceRecord::new( &app.paths, "svc-qwen", @@ -38768,7 +38772,7 @@ Full log #[test] fn history_navigation_does_not_replace_footer_status() { let mut app = test_app(); - app.history = vec!["/doctor".to_owned(), "/gpu".to_owned()]; + app.history = vec!["/examine".to_owned(), "/gpu".to_owned()]; app.status = "Ready.".to_owned(); app.home_dashboard_visible = false; @@ -38786,7 +38790,7 @@ Full log #[test] fn arrow_keys_cycle_completion_instead_of_history_when_menu_is_open() { let mut app = test_app(); - app.history = vec!["/doctor".to_owned()]; + app.history = vec!["/examine".to_owned()]; app.set_input("/".to_owned()); handle_key(&mut app, key_event(KeyCode::Down, KeyModifiers::NONE)); @@ -38824,23 +38828,23 @@ Full log fn prompt_supports_midline_editing() { let mut app = test_app(); app.home_dashboard_visible = false; - app.set_input("/dctor".to_owned()); - app.input_cursor = 2; + app.set_input("/exmine".to_owned()); + app.input_cursor = 3; - handle_key(&mut app, key_event(KeyCode::Char('o'), KeyModifiers::NONE)); + handle_key(&mut app, key_event(KeyCode::Char('a'), KeyModifiers::NONE)); - assert_eq!(app.input, "/doctor"); - assert_eq!(app.input_cursor, 3); + assert_eq!(app.input, "/examine"); + assert_eq!(app.input_cursor, 4); handle_key(&mut app, key_event(KeyCode::Left, KeyModifiers::NONE)); handle_key(&mut app, key_event(KeyCode::Backspace, KeyModifiers::NONE)); - assert_eq!(app.input, "/octor"); - assert_eq!(app.input_cursor, 1); + assert_eq!(app.input, "/eamine"); + assert_eq!(app.input_cursor, 2); handle_key(&mut app, key_event(KeyCode::Delete, KeyModifiers::NONE)); - assert_eq!(app.input, "/ctor"); + assert_eq!(app.input, "/emine"); } #[test] @@ -39774,7 +39778,7 @@ Full log let mut app = test_app(); let (sender, receiver) = mpsc::channel(); app.running_job = Some(super::RunningJob { - title: "Doctor".to_owned(), + title: "Examine".to_owned(), kind: super::RunningJobKind::Cli, receiver, started_at: Instant::now(), @@ -39798,7 +39802,7 @@ Full log sender .send(super::RunningJobEvent::Finished(Ok(super::CommandOutput { ok: true, - rendered: "doctor ok".to_owned(), + rendered: "examine ok".to_owned(), chat_approval: None, }))) .unwrap(); @@ -39812,7 +39816,7 @@ Full log .any(|line| line == "checking runtime") ); assert!(app.running_job_output.iter().any(|line| line == "warning")); - assert!(app.transcript.iter().any(|line| line == "[Doctor]")); + assert!(app.transcript.iter().any(|line| line == "[Examine]")); assert!(!app.transcript.iter().any(|line| line == "stdout:")); assert!( app.transcript @@ -39824,7 +39828,7 @@ Full log .iter() .any(|line| line == "streamed_stderr: 1 line(s) shown in live output while it ran") ); - assert!(app.activity_text().contains("completed: Doctor")); + assert!(app.activity_text().contains("completed: Examine")); } #[test] @@ -40529,7 +40533,7 @@ Full log let mut app = test_app(); let (sender, receiver) = mpsc::channel(); app.running_job = Some(super::RunningJob { - title: "Doctor".to_owned(), + title: "Examine".to_owned(), kind: super::RunningJobKind::Cli, receiver, started_at: Instant::now(), @@ -40549,7 +40553,7 @@ Full log sender .send(super::RunningJobEvent::Finished(Ok(super::CommandOutput { ok: true, - rendered: "doctor ok".to_owned(), + rendered: "examine ok".to_owned(), chat_approval: None, }))) .unwrap(); @@ -40569,7 +40573,7 @@ Full log app.poll_running_job(); assert!(app.running_job.is_none()); - assert!(app.transcript.iter().any(|line| line == "[Doctor]")); + assert!(app.transcript.iter().any(|line| line == "[Examine]")); } #[test] @@ -40605,7 +40609,7 @@ Full log app.transcript_scroll = 0; let (sender, receiver) = mpsc::channel(); app.running_job = Some(super::RunningJob { - title: "Doctor".to_owned(), + title: "Examine".to_owned(), kind: super::RunningJobKind::Cli, receiver, started_at: Instant::now(), @@ -40616,7 +40620,7 @@ Full log sender .send(super::RunningJobEvent::Finished(Ok(super::CommandOutput { ok: true, - rendered: "command: rocm doctor\nstatus: ok\n\nstdout:\nready".to_owned(), + rendered: "command: rocm examine\nstatus: ok\n\nstdout:\nready".to_owned(), chat_approval: None, }))) .unwrap(); @@ -40624,7 +40628,7 @@ Full log app.poll_running_job(); assert_eq!(app.transcript_scroll, 0); - assert!(app.transcript.iter().any(|line| line == "[Doctor]")); + assert!(app.transcript.iter().any(|line| line == "[Examine]")); } #[test] @@ -41033,7 +41037,7 @@ Full log assert!(screen.detail.contains("Run: review and start this action")); assert!(matches!( screen.plan_action.as_ref().map(|action| action.args.as_slice()), - Some(args) if args.len() == 1 && args[0] == "doctor" + Some(args) if args.len() == 1 && args[0] == "examine" )); } @@ -44023,7 +44027,7 @@ Full log running_job_log_scroll: 0, running_job_output: std::collections::VecDeque::new(), running_job_cancel_requested: None, - doctor_manager: None, + examine_manager: None, logs_view: None, runtime_manager: None, install_manager: None, @@ -44144,19 +44148,19 @@ Full log .engine_index = index; } - fn open_hidden_doctor_refresh_from_prompt(app: &mut App) { - submit_prompt_command(app, "/doctor"); + fn open_hidden_examine_refresh_from_prompt(app: &mut App) { + submit_prompt_command(app, "/examine"); assert!(matches!( app.running_job.as_ref().map(|job| &job.kind), - Some(super::RunningJobKind::DoctorRefresh) + Some(super::RunningJobKind::ExamineRefresh) )); handle_key(app, key_event(KeyCode::Esc, KeyModifiers::NONE)); - assert!(app.doctor_manager.is_none()); + assert!(app.examine_manager.is_none()); assert!(matches!( app.running_job.as_ref().map(|job| &job.kind), - Some(super::RunningJobKind::DoctorRefresh) + Some(super::RunningJobKind::ExamineRefresh) )); assert!(app.transcript.is_empty()); } @@ -44380,18 +44384,18 @@ Full log assert!(app.running_job.is_none()); } - fn assert_no_background_doctor_block(app: &App, command: &str) { + fn assert_no_background_examine_block(app: &App, command: &str) { assert!( !app.status.contains("already running") && !app.status.contains("still running. Wait for it to finish"), - "{command} should not be blocked by a hidden Doctor refresh: {}", + "{command} should not be blocked by a hidden Examine refresh: {}", app.status ); assert!( app.running_job .as_ref() - .is_none_or(|job| !matches!(job.kind, super::RunningJobKind::DoctorRefresh)), - "{command} should discard hidden Doctor refresh before continuing" + .is_none_or(|job| !matches!(job.kind, super::RunningJobKind::ExamineRefresh)), + "{command} should discard hidden Examine refresh before continuing" ); assert!( app.transcript.is_empty(), @@ -44404,7 +44408,7 @@ Full log return 1; } [ - app.doctor_manager.is_some(), + app.examine_manager.is_some(), app.logs_view.is_some(), app.runtime_manager.is_some(), app.install_manager.is_some(), @@ -44447,11 +44451,11 @@ Full log row_count: state.actions.len(), }); } - if let Some(state) = app.doctor_manager.as_ref() { + if let Some(state) = app.examine_manager.as_ref() { return Some(SurfaceSelection { - surface: "doctor", + surface: "examine", selected: state.selected, - row_count: super::doctor_manager_choices().len(), + row_count: super::examine_manager_choices().len(), }); } if let Some(state) = app.logs_view.as_ref() { @@ -44594,7 +44598,7 @@ Full log if let Some(state) = app.overlay_card.as_ref() { return Some(state.detail_scroll); } - if let Some(state) = app.doctor_manager.as_ref() { + if let Some(state) = app.examine_manager.as_ref() { return Some(state.detail_scroll); } if let Some(state) = app.logs_view.as_ref() { diff --git a/apps/rocmd/src/lib.rs b/apps/rocmd/src/lib.rs index fceb9910..476d8cbc 100644 --- a/apps/rocmd/src/lib.rs +++ b/apps/rocmd/src/lib.rs @@ -12,7 +12,7 @@ use rocm_core::engine_plugin_dirs; use rocm_core::{ AppPaths, AuditEventRecord, AutomationEventRecord, AutomationProposalRecord, AutomationRuntimeState, AutomationTriggerEvent, CodexBridgeEngine, CodexBridgeGpuSnapshot, - CodexBridgeSnapshot, DEFAULT_LOCAL_HOST, DoctorSummary, ManagedServiceRecord, + CodexBridgeSnapshot, DEFAULT_LOCAL_HOST, ExamineSummary, ManagedServiceRecord, ModelRecipeArtifactRecord, RocmCliConfig, WatcherMode, WatcherRuntimeSnapshot, append_audit_event, append_automation_event, append_automation_proposal, builtin_watcher, builtin_watchers, daemon_binary_path, default_engine_for_platform, format_host_port, @@ -168,7 +168,7 @@ enum Command { enum SandboxToolArg { CheckUpdates, DriverPlan, - DoctorSnapshot, + ExamineSnapshot, ListServers, RestartServer, StopServer, @@ -181,7 +181,7 @@ impl SandboxToolArg { match self { Self::CheckUpdates => "check_updates", Self::DriverPlan => "driver_plan", - Self::DoctorSnapshot => "doctor_snapshot", + Self::ExamineSnapshot => "examine_snapshot", Self::ListServers => "list_servers", Self::RestartServer => "restart_server", Self::StopServer => "stop_server", @@ -396,7 +396,7 @@ fn build_bridge_snapshot(paths: &AppPaths) -> Result { Ok(CodexBridgeSnapshot { protocol: "rocmd-codex-bridge-v0".to_owned(), generated_at_unix_ms: unix_time_millis(), - doctor: DoctorSummary::gather()?, + examine: ExamineSummary::gather()?, gpu: gather_gpu_snapshot_for_config(&config), config, automation_runtime: AutomationRuntimeState::load(paths)?, @@ -563,13 +563,13 @@ fn run_sandbox_tool( )?; Ok(sandbox_driver_plan_value(output)) } - SandboxToolArg::DoctorSnapshot => { - let doctor = DoctorSummary::gather()?; + SandboxToolArg::ExamineSnapshot => { + let examine = ExamineSummary::gather()?; Ok(json!({ "tool": tool.as_cli_value(), "status": "captured", "mutating": false, - "doctor": doctor, + "examine": examine, })) } SandboxToolArg::ListServers => { @@ -1583,7 +1583,7 @@ fn print_json(value: &T) -> Result<()> { fn rocm_mcp_tools() -> Vec { vec![ rocm_mcp_tool( - "doctor", + "examine", "Read the current ROCm AI Command Center host summary.", json!({ "type": "object", @@ -1595,7 +1595,7 @@ fn rocm_mcp_tools() -> Vec { ), rocm_mcp_tool( "bridge_snapshot", - "Read the full ROCm bridge snapshot including doctor data, engines, services, automations, and gpu telemetry.", + "Read the full ROCm bridge snapshot including examine data, engines, services, automations, and gpu telemetry.", json!({ "type": "object", "properties": {}, @@ -1959,17 +1959,17 @@ fn handle_mcp_tool_call(paths: &AppPaths, params: &Value) -> Result { .unwrap_or_default(); match name { - "doctor" => { - let doctor = DoctorSummary::gather()?; - let output = run_rocm_capture(&["doctor"])?; + "examine" => { + let examine = ExamineSummary::gather()?; + let output = run_rocm_capture(&["examine"])?; let text = command_capture_text(&output); if output.exit_status == 0 { - Ok(tool_success(text, json!(doctor))) + Ok(tool_success(text, json!(examine))) } else { Ok(tool_error( text, json!({ - "doctor": doctor, + "examine": examine, "argv": output.argv, "exit_status": output.exit_status, "stderr": output.stderr, @@ -1982,7 +1982,7 @@ fn handle_mcp_tool_call(paths: &AppPaths, params: &Value) -> Result { Ok(tool_success( format!( "Captured bridge snapshot for {} / {} with default engine `{}`.", - snapshot.doctor.os, snapshot.doctor.arch, snapshot.doctor.default_engine + snapshot.examine.os, snapshot.examine.arch, snapshot.examine.default_engine ), json!(snapshot), )) @@ -2372,7 +2372,7 @@ fn ensure_rocm_command_is_read_only(args: &[String]) -> Result<()> { let first = args.first().map(|value| value.to_ascii_lowercase()); let second = args.get(1).map(|value| value.to_ascii_lowercase()); let read_only = match first.as_deref() { - Some("doctor" | "version" | "model" | "models" | "daemon" | "logs") => true, + Some("examine" | "version" | "model" | "models" | "daemon" | "logs") => true, Some("update") => !args.iter().any(|arg| arg == "--apply"), Some("runtimes") => second.as_deref().is_none_or(|value| value == "list"), Some("engines") => second.as_deref().is_some_and(|value| value == "list"), @@ -5104,7 +5104,7 @@ mod tests { #[test] fn direct_mcp_call_guard_blocks_mutation_without_explicit_ack() { - ensure_direct_mcp_call_allowed("doctor", false) + ensure_direct_mcp_call_allowed("examine", false) .expect("read-only direct MCP helper calls should not need mutation approval"); let error = ensure_direct_mcp_call_allowed("install_sdk", false) @@ -7201,7 +7201,7 @@ mod tests { &event, |_paths| { Ok(json!({ - "tool": "doctor_snapshot", + "tool": "examine_snapshot", "status": "captured", "mutating": false, })) @@ -7372,7 +7372,7 @@ mod tests { fn sandbox_tool_cli_values_cover_restricted_plan_api() { let names = [ SandboxToolArg::CheckUpdates, - SandboxToolArg::DoctorSnapshot, + SandboxToolArg::ExamineSnapshot, SandboxToolArg::ListServers, SandboxToolArg::RestartServer, SandboxToolArg::StopServer, @@ -7386,7 +7386,7 @@ mod tests { for expected in [ "check_updates", - "doctor_snapshot", + "examine_snapshot", "list_servers", "restart_server", "stop_server", @@ -7399,11 +7399,11 @@ mod tests { } #[test] - fn sandbox_tool_doctor_snapshot_is_read_only() -> Result<()> { - let (root, paths) = temp_app_paths("sandbox-doctor-snapshot"); + fn sandbox_tool_examine_snapshot_is_read_only() -> Result<()> { + let (root, paths) = temp_app_paths("sandbox-examine-snapshot"); let value = run_sandbox_tool( &paths, - SandboxToolArg::DoctorSnapshot, + SandboxToolArg::ExamineSnapshot, None, None, None, @@ -7413,14 +7413,14 @@ mod tests { assert_eq!( value.get("tool").and_then(Value::as_str), - Some("doctor_snapshot") + Some("examine_snapshot") ); assert_eq!( value.get("status").and_then(Value::as_str), Some("captured") ); assert_eq!(value.get("mutating").and_then(Value::as_bool), Some(false)); - assert!(value.get("doctor").is_some()); + assert!(value.get("examine").is_some()); Ok(()) } diff --git a/crates/rocm-core/src/lib.rs b/crates/rocm-core/src/lib.rs index 2aa39b1a..7c78c8d6 100644 --- a/crates/rocm-core/src/lib.rs +++ b/crates/rocm-core/src/lib.rs @@ -834,7 +834,7 @@ fn env_flag(name: &str) -> bool { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DoctorSummary { +pub struct ExamineSummary { pub os: String, pub arch: String, pub kernel: Option, @@ -895,7 +895,7 @@ pub struct HostGpuSummary { } #[derive(Debug, Clone, Default)] -struct WindowsDoctorInventory { +struct WindowsExamineInventory { cpu_model: Option, system_ram_gib: Option, displays: Vec, @@ -908,14 +908,14 @@ struct WindowsDisplayAdapter { pnp_device_id: Option, } -impl WindowsDoctorInventory { +impl WindowsExamineInventory { #[cfg(windows)] fn is_empty(&self) -> bool { self.cpu_model.is_none() && self.system_ram_gib.is_none() && self.displays.is_empty() } #[cfg(windows)] - fn merge_missing_from(&mut self, mut other: WindowsDoctorInventory) { + fn merge_missing_from(&mut self, mut other: WindowsExamineInventory) { if self.cpu_model.is_none() { self.cpu_model = other.cpu_model.take(); } @@ -1014,12 +1014,12 @@ impl WindowsDoctorInventory { } } -impl DoctorSummary { +impl ExamineSummary { pub fn gather() -> Result { let paths = AppPaths::discover()?; - let windows_inventory = detect_windows_doctor_inventory(); + let windows_inventory = detect_windows_examine_inventory(); let wsl = detect_wsl_summary(); - let detected_gfx_target = detect_doctor_gfx_target_fast(windows_inventory.as_ref()); + let detected_gfx_target = detect_examine_gfx_target_fast(windows_inventory.as_ref()); let compatible_therock_family = detected_gfx_target .as_deref() .and_then(normalize_therock_family); @@ -1068,7 +1068,7 @@ impl DoctorSummary { }; let wsl = self.wsl.as_ref(); format!( - "rocm doctor\n os: {}\n arch: {}\n kernel: {}\n distro: {}\n cpu: {}\n system_ram: {}\n interactive_terminal: {}\n default_engine: {}\n detected_gfx_target: {}\n compatible_therock_family: {}\n detected_therock_family: {}\n driver_policy: {}\n driver_status: {}\n driver_detail: {}\n legacy_rocm_status: {}\n legacy_rocm_paths: {}\n legacy_rocm_detail: {}\n legacy_rocm_guidance: {}\n wsl: {}\n wsl_dxg_device: {}\n wsl_dxcore: {}\n wsl_librocdxg: {}\n wsl_rocdxg_dids: {}\n wsl_ldconfig_librocdxg: {}\n wsl_global_rocminfo: {}\n wsl_cargo: {}\n wsl_detail: {}\n managed_runtimes: {}\n managed_services: {}\n model_cache_entries: {}\n config_dir: {}\n data_dir: {}\n cache_dir: {}\n", + "rocm examine\n os: {}\n arch: {}\n kernel: {}\n distro: {}\n cpu: {}\n system_ram: {}\n interactive_terminal: {}\n default_engine: {}\n detected_gfx_target: {}\n compatible_therock_family: {}\n detected_therock_family: {}\n driver_policy: {}\n driver_status: {}\n driver_detail: {}\n legacy_rocm_status: {}\n legacy_rocm_paths: {}\n legacy_rocm_detail: {}\n legacy_rocm_guidance: {}\n wsl: {}\n wsl_dxg_device: {}\n wsl_dxcore: {}\n wsl_librocdxg: {}\n wsl_rocdxg_dids: {}\n wsl_ldconfig_librocdxg: {}\n wsl_global_rocminfo: {}\n wsl_cargo: {}\n wsl_detail: {}\n managed_runtimes: {}\n managed_services: {}\n model_cache_entries: {}\n config_dir: {}\n data_dir: {}\n cache_dir: {}\n", self.os, self.arch, self.kernel.as_deref().unwrap_or(""), @@ -1164,7 +1164,7 @@ fn parse_os_release_pretty_name(text: &str) -> Option { } fn detect_cpu_model_with_windows_inventory( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, ) -> Option { if runtime_is_windows() && let Some(inventory) = windows_inventory @@ -1210,7 +1210,7 @@ fn detect_cpu_model() -> Option { } fn detect_system_ram_gib_with_windows_inventory( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, ) -> Option { if runtime_is_windows() && let Some(inventory) = windows_inventory @@ -1321,12 +1321,12 @@ fn detect_wsl_summary() -> Option { } fn detect_driver_summary_with_windows_inventory( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, wsl: Option<&WslSummary>, ) -> DriverSummary { if runtime_is_windows() { let detail = windows_inventory - .and_then(WindowsDoctorInventory::amd_display_driver_detail) + .and_then(WindowsExamineInventory::amd_display_driver_detail) .or_else(|| { if windows_inventory.is_none() { detect_windows_amd_display_driver() @@ -1495,21 +1495,21 @@ const fn detect_windows_amd_display_driver() -> Option { } #[cfg(windows)] -fn detect_windows_doctor_inventory() -> Option { +fn detect_windows_examine_inventory() -> Option { if !runtime_is_windows() { return None; } - let mut inventory = WindowsDoctorInventory::default(); - if let Some(pnp_util) = detect_windows_doctor_inventory_from_pnputil() { + let mut inventory = WindowsExamineInventory::default(); + if let Some(pnp_util) = detect_windows_examine_inventory_from_pnputil() { inventory.merge_missing_from(pnp_util); } if inventory.displays.is_empty() - && let Some(video) = detect_windows_doctor_inventory_from_video_controller() + && let Some(video) = detect_windows_examine_inventory_from_video_controller() { inventory.merge_missing_from(video); } if inventory.displays.is_empty() - && let Some(pnp) = detect_windows_doctor_inventory_from_pnp_entity() + && let Some(pnp) = detect_windows_examine_inventory_from_pnp_entity() { inventory.merge_missing_from(pnp); } @@ -1523,7 +1523,7 @@ fn detect_windows_doctor_inventory() -> Option { } #[cfg(windows)] -fn detect_windows_doctor_inventory_from_pnputil() -> Option { +fn detect_windows_examine_inventory_from_pnputil() -> Option { if !runtime_is_windows() { return None; } @@ -1536,7 +1536,7 @@ fn detect_windows_doctor_inventory_from_pnputil() -> Option Option { +fn detect_windows_examine_inventory_from_video_controller() -> Option { if !runtime_is_windows() { return None; } @@ -1551,11 +1551,11 @@ fn detect_windows_doctor_inventory_from_video_controller() -> Option Option { +fn detect_windows_system_inventory_from_cim() -> Option { if !runtime_is_windows() { return None; } @@ -1570,11 +1570,11 @@ fn detect_windows_system_inventory_from_cim() -> Option ], OPTIONAL_COMMAND_TIMEOUT, ) - .map(|output| parse_windows_doctor_inventory(&output)) + .map(|output| parse_windows_examine_inventory(&output)) } #[cfg(windows)] -fn detect_windows_doctor_inventory_from_pnp_entity() -> Option { +fn detect_windows_examine_inventory_from_pnp_entity() -> Option { if !runtime_is_windows() { return None; } @@ -1589,11 +1589,11 @@ fn detect_windows_doctor_inventory_from_pnp_entity() -> Option Option { +const fn detect_windows_examine_inventory() -> Option { None } @@ -1605,8 +1605,8 @@ fn clean_windows_display_name(value: &str) -> String { } #[cfg_attr(not(windows), allow(dead_code))] -fn parse_windows_doctor_inventory(text: &str) -> WindowsDoctorInventory { - let mut inventory = WindowsDoctorInventory::default(); +fn parse_windows_examine_inventory(text: &str) -> WindowsExamineInventory { + let mut inventory = WindowsExamineInventory::default(); for line in text.lines().map(str::trim).filter(|line| !line.is_empty()) { let mut fields = line.split('\t'); @@ -1650,8 +1650,8 @@ fn parse_windows_doctor_inventory(text: &str) -> WindowsDoctorInventory { } #[cfg(any(windows, test))] -fn parse_windows_pnputil_display_inventory(text: &str) -> WindowsDoctorInventory { - let mut inventory = WindowsDoctorInventory::default(); +fn parse_windows_pnputil_display_inventory(text: &str) -> WindowsExamineInventory { + let mut inventory = WindowsExamineInventory::default(); let mut name: Option = None; let mut instance_id: Option = None; let mut driver_version: Option = None; @@ -1700,7 +1700,7 @@ fn parse_windows_pnputil_display_inventory(text: &str) -> WindowsDoctorInventory #[cfg(any(windows, test))] fn push_windows_pnputil_display( - inventory: &mut WindowsDoctorInventory, + inventory: &mut WindowsExamineInventory, name: &mut Option, instance_id: &mut Option, driver_version: &mut Option, @@ -1802,7 +1802,7 @@ fn append_windows_gpu_probe_diagnostics(output: &mut String) { WINDOWS_VIDEO_CONTROLLER_INVENTORY_SCRIPT, ], WINDOWS_INVENTORY_QUERY_TIMEOUT, - parse_windows_doctor_inventory, + parse_windows_examine_inventory, ); append_windows_probe_diagnostics( output, @@ -1816,7 +1816,7 @@ fn append_windows_gpu_probe_diagnostics(output: &mut String) { WINDOWS_PNP_ENTITY_INVENTORY_SCRIPT, ], WINDOWS_INVENTORY_QUERY_TIMEOUT, - parse_windows_doctor_inventory, + parse_windows_examine_inventory, ); } @@ -1830,7 +1830,7 @@ fn append_windows_probe_diagnostics( program: &str, args: &[&str], timeout: Duration, - parse: fn(&str) -> WindowsDoctorInventory, + parse: fn(&str) -> WindowsExamineInventory, ) { use std::fmt::Write as _; let result = capture_diagnostic_command(program, args, timeout); @@ -2061,12 +2061,12 @@ pub fn detect_host_gpu_summary(paths: Option<&AppPaths>) -> HostGpuSummary { #[cfg(windows)] fn detect_host_gpu_summary_fast(_paths: Option<&AppPaths>) -> HostGpuSummary { - let windows_inventory = detect_windows_doctor_inventory(); + let windows_inventory = detect_windows_examine_inventory(); let gfx_target = detect_windows_display_gfx_target_with_inventory(windows_inventory.as_ref()); let therock_family = gfx_target.as_deref().and_then(normalize_therock_family); let name = windows_inventory .as_ref() - .and_then(WindowsDoctorInventory::amd_display_name); + .and_then(WindowsExamineInventory::amd_display_name); HostGpuSummary { name, gfx_target, @@ -2077,13 +2077,13 @@ fn detect_host_gpu_summary_fast(_paths: Option<&AppPaths>) -> HostGpuSummary { #[cfg(target_os = "linux")] fn detect_host_gpu_summary_fast(_paths: Option<&AppPaths>) -> HostGpuSummary { if runtime_is_windows() { - let windows_inventory = detect_windows_doctor_inventory(); + let windows_inventory = detect_windows_examine_inventory(); let gfx_target = detect_windows_display_gfx_target_with_inventory(windows_inventory.as_ref()); let therock_family = gfx_target.as_deref().and_then(normalize_therock_family); let name = windows_inventory .as_ref() - .and_then(WindowsDoctorInventory::amd_display_name); + .and_then(WindowsExamineInventory::amd_display_name); return HostGpuSummary { name, gfx_target, @@ -2123,7 +2123,7 @@ fn detect_host_gpu_summary_fast(_paths: Option<&AppPaths>) -> HostGpuSummary { #[allow(dead_code)] fn detect_host_gpu_summary_full(paths: Option<&AppPaths>) -> HostGpuSummary { - let windows_inventory = detect_windows_doctor_inventory(); + let windows_inventory = detect_windows_examine_inventory(); let wsl = detect_wsl_summary(); let gfx_target = detect_host_gfx_target_with_context(windows_inventory.as_ref(), wsl.as_ref(), paths); @@ -2137,11 +2137,11 @@ fn detect_host_gpu_summary_full(paths: Option<&AppPaths>) -> HostGpuSummary { } fn detect_host_gpu_name_with_context( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, wsl: Option<&WslSummary>, ) -> Option { windows_inventory - .and_then(WindowsDoctorInventory::amd_display_name) + .and_then(WindowsExamineInventory::amd_display_name) .or_else(detect_linux_primary_gpu_name) .or_else(|| detect_wsl_windows_display_name(wsl)) } @@ -2535,8 +2535,8 @@ pub fn detect_host_gfx_target() -> Option { detect_host_gpu_summary_fast(paths.as_ref()).gfx_target } -fn detect_doctor_gfx_target_fast( - windows_inventory: Option<&WindowsDoctorInventory>, +fn detect_examine_gfx_target_fast( + windows_inventory: Option<&WindowsExamineInventory>, ) -> Option { if runtime_is_windows() { return detect_windows_display_gfx_target_with_inventory(windows_inventory); @@ -2551,7 +2551,7 @@ fn detect_doctor_gfx_target_fast( #[allow(dead_code)] fn detect_host_gfx_target_with_context( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, wsl: Option<&WslSummary>, paths: Option<&AppPaths>, ) -> Option { @@ -2848,7 +2848,7 @@ fn detect_windows_display_gfx_target() -> Option { ], WINDOWS_INVENTORY_QUERY_TIMEOUT, ) - .map(|output| parse_windows_doctor_inventory(&output).display_gfx_probe_text()) + .map(|output| parse_windows_examine_inventory(&output).display_gfx_probe_text()) .and_then(|output| parse_windows_display_gfx_target(&output)) } @@ -2858,11 +2858,11 @@ const fn detect_windows_display_gfx_target() -> Option { } fn detect_windows_display_gfx_target_with_inventory( - windows_inventory: Option<&WindowsDoctorInventory>, + windows_inventory: Option<&WindowsExamineInventory>, ) -> Option { if runtime_is_windows() { return windows_inventory - .and_then(WindowsDoctorInventory::display_gfx_target) + .and_then(WindowsExamineInventory::display_gfx_target) .or_else(|| { if windows_inventory.is_none() { detect_windows_display_gfx_target() @@ -2920,7 +2920,7 @@ fn detect_wsl_windows_display_probe_text() -> Option { WINDOWS_INVENTORY_QUERY_TIMEOUT, ) .map(|output| { - parse_windows_doctor_inventory(&output) + parse_windows_examine_inventory(&output) .display_gfx_probe_text() .trim() .to_owned() @@ -5614,7 +5614,7 @@ impl ManagedServiceRecord { pub struct CodexBridgeSnapshot { pub protocol: String, pub generated_at_unix_ms: u128, - pub doctor: DoctorSummary, + pub examine: ExamineSummary, pub gpu: CodexBridgeGpuSnapshot, pub config: RocmCliConfig, #[serde(default)] @@ -6086,8 +6086,8 @@ mod tests { } #[test] - fn windows_doctor_inventory_parser_feeds_cpu_driver_and_gfx_detection() { - let inventory = parse_windows_doctor_inventory( + fn windows_examine_inventory_parser_feeds_cpu_driver_and_gfx_detection() { + let inventory = parse_windows_examine_inventory( "CPU\t AMD Ryzen 9 9950X 16-Core Processor \nRAM\t68719476736\nGPU\tAMD Radeon RX 9070 XT\t32.0.13031.9001\tPCI\\VEN_1002&DEV_7550&SUBSYS_2435148C&REV_C0\n", ); @@ -6138,8 +6138,8 @@ Class Name: Display } #[test] - fn windows_doctor_inventory_prefers_real_gpu_over_noisy_amd_pnp_entries() { - let inventory = parse_windows_doctor_inventory( + fn windows_examine_inventory_prefers_real_gpu_over_noisy_amd_pnp_entries() { + let inventory = parse_windows_examine_inventory( "GPU\tAMD Bluetooth Capture Audio Device\t\t{2101C4C0-2C15-4035-A0D0-EEC3C2277B11}\\CAPTURE&CP_111215637\nGPU\tAMD-OpenGL User Mode Driver\t\tSWD\\DRIVERENUM\\AMDOGL&5&BAA66E4&0\nGPU\tAMD Radeon 780M Graphics\t\tPCI\\VEN_1002&DEV_1900&SUBSYS_50EE17AA&REV_D0\\4&EB5E2B6&0&0041\n", ); @@ -6151,11 +6151,11 @@ Class Name: Display } #[test] - fn windows_doctor_gfx_detection_uses_inventory_without_rocm_tools() { + fn windows_examine_gfx_detection_uses_inventory_without_rocm_tools() { if !cfg!(windows) { return; } - let inventory = parse_windows_doctor_inventory( + let inventory = parse_windows_examine_inventory( "GPU\tAMD Radeon RX 9070 XT\t32.0.23033.1002\tPCI\\VEN_1002&DEV_7550", ); @@ -6246,8 +6246,8 @@ Class Name: Display } #[test] - fn counts_json_files_and_model_cache_entries_for_doctor() -> Result<()> { - let (root, paths) = temp_app_paths("doctor-counts"); + fn counts_json_files_and_model_cache_entries_for_examine() -> Result<()> { + let (root, paths) = temp_app_paths("examine-counts"); let registry = paths.data_dir.join("runtimes").join("registry"); let models = paths.data_dir.join("models"); fs::create_dir_all(®istry)?; @@ -6407,8 +6407,8 @@ Class Name: Display } #[test] - fn doctor_render_includes_driver_and_state_counts() { - let summary = DoctorSummary { + fn examine_render_includes_driver_and_state_counts() { + let summary = ExamineSummary { os: "windows".to_owned(), arch: "x86_64".to_owned(), kernel: Some("10.0.26100".to_owned()), @@ -6459,8 +6459,8 @@ Class Name: Display } #[test] - fn doctor_render_guides_managed_runtime_install_when_only_legacy_rocm_exists() { - let summary = DoctorSummary { + fn examine_render_guides_managed_runtime_install_when_only_legacy_rocm_exists() { + let summary = ExamineSummary { os: "linux".to_owned(), arch: "x86_64".to_owned(), kernel: None, diff --git a/crates/rocm-dash-tui/src/app.rs b/crates/rocm-dash-tui/src/app.rs index 51157f44..d9a00807 100644 --- a/crates/rocm-dash-tui/src/app.rs +++ b/crates/rocm-dash-tui/src/app.rs @@ -337,8 +337,8 @@ pub struct AppState { pub serve_wizard: Option, /// Engine manager overlay (Phase 3 Wave 1). `None` = closed. pub engine_manager: Option, - /// Doctor overlay (Phase 3 Wave 2). `None` = closed. - pub doctor_manager: Option, + /// Examine overlay (Phase 3 Wave 2). `None` = closed. + pub examine_manager: Option, /// Update overlay (Phase 3 Wave 2). `None` = closed. pub update_manager: Option, /// Install overlay (Phase 3 Wave 2). `None` = closed. @@ -408,7 +408,7 @@ impl AppState { services: None, serve_wizard: None, engine_manager: None, - doctor_manager: None, + examine_manager: None, update_manager: None, install_manager: None, logs_view: None, @@ -430,7 +430,7 @@ impl AppState { self.services = None; self.serve_wizard = None; self.engine_manager = None; - self.doctor_manager = None; + self.examine_manager = None; self.update_manager = None; self.install_manager = None; self.logs_view = None; @@ -974,11 +974,11 @@ async fn event_loop(terminal: &mut Tui, args: &ResolvedArgs) -> color_eyre::Resu ); crate::jobs::run_effects(fx, &job_tx); } - // The doctor overlay, when open, owns all keys (read-only - // `rocm doctor` job through the job-bridge). - Some(Ok(CtEvent::Key(k))) if state.doctor_manager.is_some() => { - let fx = crate::ui::doctor_manager::on_key( - &mut state.doctor_manager, + // The examine overlay, when open, owns all keys (read-only + // `rocm examine` job through the job-bridge). + Some(Ok(CtEvent::Key(k))) if state.examine_manager.is_some() => { + let fx = crate::ui::examine_manager::on_key( + &mut state.examine_manager, &mut state.jobs, k, ); @@ -1271,9 +1271,10 @@ fn apply_action(state: &mut AppState, action: KeyAction) -> bool { state.close_overlays(); state.engine_manager = Some(crate::ui::engine_manager::EngineManagerState::default()); } - KeyAction::OpenDoctor => { + KeyAction::OpenExamine => { state.close_overlays(); - state.doctor_manager = Some(crate::ui::doctor_manager::DoctorManagerState::default()); + state.examine_manager = + Some(crate::ui::examine_manager::ExamineManagerState::default()); } KeyAction::OpenUpdate => { state.close_overlays(); @@ -1452,8 +1453,8 @@ pub enum KeyAction { OpenServeWizard, /// Open the engine-manager overlay (Phase 3 Wave 1). OpenEngineManager, - /// Open the doctor overlay (Phase 3 Wave 2). - OpenDoctor, + /// Open the examine overlay (Phase 3 Wave 2). + OpenExamine, /// Open the update overlay (Phase 3 Wave 2). OpenUpdate, /// Open the install overlay (Phase 3 Wave 2). @@ -1620,9 +1621,9 @@ fn handle_key(k: KeyEvent, current: ActiveTab, modal: &Modal, chat: ChatKeyCtx) KeyCode::Char('e') if matches!(current, ActiveTab::Overview | ActiveTab::Instances) => { KeyAction::OpenEngineManager } - // Doctor: read-only environment check. + // Examine: read-only environment check. KeyCode::Char('d') if matches!(current, ActiveTab::Overview | ActiveTab::Instances) => { - KeyAction::OpenDoctor + KeyAction::OpenExamine } // Update: check/preview/apply ROCm package updates. KeyCode::Char('u') if matches!(current, ActiveTab::Overview | ActiveTab::Instances) => { @@ -1857,10 +1858,10 @@ mod tests { KeyAction::Nothing ); assert_eq!(hk(KeyCode::Char('e'), ActiveTab::Bench), KeyAction::Nothing); - // Doctor / update open from Overview + Instances. + // Examine / update open from Overview + Instances. assert_eq!( hk(KeyCode::Char('d'), ActiveTab::Overview), - KeyAction::OpenDoctor + KeyAction::OpenExamine ); assert_eq!( hk(KeyCode::Char('u'), ActiveTab::Instances), @@ -1904,10 +1905,10 @@ mod tests { apply_action(&mut s, KeyAction::OpenEngineManager); assert!(s.engine_manager.is_some() && s.services.is_none() && s.serve_wizard.is_none()); // Wave 2/3 overlays join the mutual-exclusion set. - apply_action(&mut s, KeyAction::OpenDoctor); - assert!(s.doctor_manager.is_some() && s.engine_manager.is_none()); + apply_action(&mut s, KeyAction::OpenExamine); + assert!(s.examine_manager.is_some() && s.engine_manager.is_none()); apply_action(&mut s, KeyAction::OpenUpdate); - assert!(s.update_manager.is_some() && s.doctor_manager.is_none()); + assert!(s.update_manager.is_some() && s.examine_manager.is_none()); apply_action(&mut s, KeyAction::OpenInstall); assert!(s.install_manager.is_some() && s.update_manager.is_none()); apply_action(&mut s, KeyAction::OpenLogs); diff --git a/crates/rocm-dash-tui/src/ui/command_screen.rs b/crates/rocm-dash-tui/src/ui/command_screen.rs index d26ce2ab..ad95a2cf 100644 --- a/crates/rocm-dash-tui/src/ui/command_screen.rs +++ b/crates/rocm-dash-tui/src/ui/command_screen.rs @@ -212,7 +212,7 @@ pub fn draw_command_screen( ); let shown = if c.input.is_empty() { - "(e.g. doctor · services list · version)".to_string() + "(e.g. examine · services list · version)".to_string() } else { format!("rocm {}", c.input) }; @@ -283,11 +283,11 @@ mod tests { let mut cs = Some(CommandScreenState::default()); let mut jobs = State::default(); // Even a read-only-looking command goes through approval (safe default). - type_str(&mut cs, &mut jobs, "doctor"); + type_str(&mut cs, &mut jobs, "examine"); let fx = on_key(&mut cs, &mut jobs, key(KeyCode::Enter)); assert!(fx.is_empty(), "must not run before approval"); let pending = cs.as_ref().unwrap().approval.as_ref().unwrap(); - assert_eq!(pending.args, vec!["doctor"]); + assert_eq!(pending.args, vec!["examine"]); assert!(jobs.jobs.is_empty()); let fx = on_key(&mut cs, &mut jobs, key(KeyCode::Char('y'))); assert_eq!(fx.len(), 1); @@ -337,7 +337,7 @@ mod tests { fn q_escapes_overlay_while_job_runs() { let mut cs = Some(CommandScreenState::default()); let mut jobs = State::default(); - type_str(&mut cs, &mut jobs, "doctor"); + type_str(&mut cs, &mut jobs, "examine"); on_key(&mut cs, &mut jobs, key(KeyCode::Enter)); // stage on_key(&mut cs, &mut jobs, key(KeyCode::Char('y'))); // spawn on_key(&mut cs, &mut jobs, key(KeyCode::Char('q'))); @@ -361,7 +361,7 @@ mod tests { ); // A plain command does NOT get the warning. let mut cs2 = Some(CommandScreenState::default()); - type_str(&mut cs2, &mut jobs, "doctor"); + type_str(&mut cs2, &mut jobs, "examine"); on_key(&mut cs2, &mut jobs, key(KeyCode::Enter)); let p2 = cs2.as_ref().unwrap().approval.as_ref().unwrap(); assert!( @@ -376,7 +376,7 @@ mod tests { fn relaunch_while_job_running_surfaces_message_not_stale_console() { let mut jobs = State::default(); let mut c1 = Some(CommandScreenState::default()); - type_str(&mut c1, &mut jobs, "doctor"); + type_str(&mut c1, &mut jobs, "examine"); on_key(&mut c1, &mut jobs, key(KeyCode::Enter)); on_key(&mut c1, &mut jobs, key(KeyCode::Char('y'))); assert_eq!(c1.as_ref().unwrap().active_job.as_deref(), Some("command")); @@ -405,7 +405,7 @@ mod tests { let backend = TestBackend::new(104, 24); let mut term = Terminal::new(backend).unwrap(); let c = CommandScreenState { - input: "doctor".into(), + input: "examine".into(), ..Default::default() }; let jobs = State::default(); @@ -419,6 +419,6 @@ mod tests { .map(ratatui::buffer::Cell::symbol) .collect(); assert!(out.contains("Run a command")); - assert!(out.contains("rocm doctor")); + assert!(out.contains("rocm examine")); } } diff --git a/crates/rocm-dash-tui/src/ui/doctor_manager.rs b/crates/rocm-dash-tui/src/ui/examine_manager.rs similarity index 78% rename from crates/rocm-dash-tui/src/ui/doctor_manager.rs rename to crates/rocm-dash-tui/src/ui/examine_manager.rs index 60976251..c806b98c 100644 --- a/crates/rocm-dash-tui/src/ui/doctor_manager.rs +++ b/crates/rocm-dash-tui/src/ui/examine_manager.rs @@ -1,6 +1,6 @@ -//! Doctor overlay (Phase 3 Wave 2). +//! Examine overlay (Phase 3 Wave 2). //! -//! Runs `rocm doctor` — a read-only environment check — through the job-bridge +//! Runs `rocm examine` — a read-only environment check — through the job-bridge //! and shows its streamed output in the shared job console. Read-only, so it //! needs no approval gate (the gate is for mutating actions only). This is the //! read-only-report archetype every diagnostic screen reuses. @@ -21,25 +21,25 @@ use crate::ui::theme::Theme; /// Overlay state. `None` on `AppState` means the overlay is closed. #[derive(Debug, Clone, Default)] -pub struct DoctorManagerState { - /// In-flight (or just-finished) `rocm doctor` job id. +pub struct ExamineManagerState { + /// In-flight (or just-finished) `rocm examine` job id. pub active_job: Option, } /// Handle a key while the overlay is open. Mirrors the operational-screen seam. pub fn on_key( - doctor: &mut Option, + examine: &mut Option, jobs: &mut State, key: KeyEvent, ) -> Vec { - let Some(d) = doctor.as_mut() else { + let Some(d) = examine.as_mut() else { return Vec::new(); }; if let Some(job_id) = d.active_job.clone() { match on_console_key(&job_id, jobs, key) { ConsoleOutcome::Cancelled(fx) => return fx, - ConsoleOutcome::Closed => *doctor = None, + ConsoleOutcome::Closed => *examine = None, ConsoleOutcome::Dismissed => d.active_job = None, ConsoleOutcome::Unhandled => { // `r` re-runs after a terminal result. @@ -49,7 +49,7 @@ pub fn on_key( .is_none_or(rocm_dash_core::state::JobState::is_terminal) { d.active_job = None; - return run_doctor(d, jobs); + return run_examine(d, jobs); } } } @@ -57,23 +57,23 @@ pub fn on_key( } match key.code { - KeyCode::Esc | KeyCode::Char('q') => *doctor = None, - KeyCode::Enter | KeyCode::Char('r') => return run_doctor(d, jobs), + KeyCode::Esc | KeyCode::Char('q') => *examine = None, + KeyCode::Enter | KeyCode::Char('r') => return run_examine(d, jobs), _ => {} } Vec::new() } -/// Spawn `rocm doctor` (read-only). A stable id replaces any prior console. -fn run_doctor(d: &mut DoctorManagerState, jobs: &mut State) -> Vec { +/// Spawn `rocm examine` (read-only). A stable id replaces any prior console. +fn run_examine(d: &mut ExamineManagerState, jobs: &mut State) -> Vec { let cmd = resolve_exe(); - let id = "doctor".to_string(); + let id = "examine".to_string(); let fx = jobs.apply(StateEvent::StartJob { id: id.clone(), cmd, - args: vec!["doctor".to_string()], + args: vec!["examine".to_string()], }); - // Doctor uses a single stable id, so a no-op (a prior run still going) + // Examine uses a single stable id, so a no-op (a prior run still going) // means re-attach to that same console — intentional, unlike the // distinct-id screens (serve/engine/update) where a no-op surfaces an // "already running" message instead. Either way `active_job` points at the @@ -83,10 +83,10 @@ fn run_doctor(d: &mut DoctorManagerState, jobs: &mut State) -> Vec { } /// Render the overlay (intro card, or the job console once running). -pub fn draw_doctor_manager( +pub fn draw_examine_manager( f: &mut Frame, area: Rect, - d: &DoctorManagerState, + d: &ExamineManagerState, jobs: &State, theme: &Theme, ) { @@ -98,7 +98,7 @@ pub fn draw_doctor_manager( } let popup = centered_rect(70, 50, 90, 14, area); - let inner = draw_popup_frame(f, popup, "Doctor — environment check", theme); + let inner = draw_popup_frame(f, popup, "Examine — environment check", theme); if inner.height == 0 { return; } @@ -121,7 +121,7 @@ pub fn draw_doctor_manager( )), Line::from(""), Line::from(Span::styled( - " [ Enter: run `rocm doctor` ] ", + " [ Enter: run `rocm examine` ] ", Style::default() .bg(theme.accent) .fg(theme.bg) @@ -150,18 +150,18 @@ mod tests { } #[test] - fn enter_runs_doctor_read_only_no_approval() { - let mut d = Some(DoctorManagerState::default()); + fn enter_runs_examine_read_only_no_approval() { + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); let fx = on_key(&mut d, &mut jobs, key(KeyCode::Enter)); assert_eq!(fx.len(), 1, "spawns one job, no approval step"); assert!(matches!(fx[0], SideEffect::SpawnJob { .. })); - assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("doctor")); + assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("examine")); } #[test] fn esc_closes_when_idle() { - let mut d = Some(DoctorManagerState::default()); + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); on_key(&mut d, &mut jobs, key(KeyCode::Esc)); assert!(d.is_none()); @@ -169,7 +169,7 @@ mod tests { #[test] fn q_escapes_overlay_while_job_runs() { - let mut d = Some(DoctorManagerState::default()); + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); on_key(&mut d, &mut jobs, key(KeyCode::Enter)); on_key(&mut d, &mut jobs, key(KeyCode::Char('q'))); @@ -178,32 +178,32 @@ mod tests { #[test] fn r_reruns_after_a_terminal_result() { - let mut d = Some(DoctorManagerState::default()); + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); on_key(&mut d, &mut jobs, key(KeyCode::Enter)); // first run jobs.apply(StateEvent::JobDone { - id: "doctor".into(), + id: "examine".into(), code: 0, }); // `r` on a terminal job re-runs (spawns again). let fx = on_key(&mut d, &mut jobs, key(KeyCode::Char('r'))); assert_eq!(fx.len(), 1, "r re-runs after a terminal result"); assert!(matches!(fx[0], SideEffect::SpawnJob { .. })); - assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("doctor")); + assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("examine")); } #[test] - fn r_at_idle_runs_doctor() { - let mut d = Some(DoctorManagerState::default()); + fn r_at_idle_runs_examine() { + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); let fx = on_key(&mut d, &mut jobs, key(KeyCode::Char('r'))); assert_eq!(fx.len(), 1); - assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("doctor")); + assert_eq!(d.as_ref().unwrap().active_job.as_deref(), Some("examine")); } #[test] fn esc_dismisses_console_only_when_terminal() { - let mut d = Some(DoctorManagerState::default()); + let mut d = Some(ExamineManagerState::default()); let mut jobs = State::default(); on_key(&mut d, &mut jobs, key(KeyCode::Enter)); // Running: Esc does not dismiss. @@ -211,7 +211,7 @@ mod tests { assert!(d.as_ref().unwrap().active_job.is_some()); // Terminal: Esc returns to the intro card. jobs.apply(StateEvent::JobDone { - id: "doctor".into(), + id: "examine".into(), code: 0, }); on_key(&mut d, &mut jobs, key(KeyCode::Esc)); @@ -226,9 +226,9 @@ mod tests { let theme = Theme::from_name("default-dark"); let backend = TestBackend::new(100, 20); let mut term = Terminal::new(backend).unwrap(); - let d = DoctorManagerState::default(); + let d = ExamineManagerState::default(); let jobs = State::default(); - term.draw(|f| draw_doctor_manager(f, f.area(), &d, &jobs, &theme)) + term.draw(|f| draw_examine_manager(f, f.area(), &d, &jobs, &theme)) .unwrap(); let out: String = term .backend() @@ -237,7 +237,7 @@ mod tests { .iter() .map(ratatui::buffer::Cell::symbol) .collect(); - assert!(out.contains("Doctor")); - assert!(out.contains("rocm doctor")); + assert!(out.contains("Examine")); + assert!(out.contains("rocm examine")); } } diff --git a/crates/rocm-dash-tui/src/ui/logs_view.rs b/crates/rocm-dash-tui/src/ui/logs_view.rs index 67dfed0d..cd77cc74 100644 --- a/crates/rocm-dash-tui/src/ui/logs_view.rs +++ b/crates/rocm-dash-tui/src/ui/logs_view.rs @@ -193,7 +193,7 @@ mod tests { #[test] fn second_enter_while_running_reattaches_to_console() { // Single stable id: a second view while the prior job still runs re-uses - // the same console (read-only re-attach, like doctor) — never an error. + // the same console (read-only re-attach, like examine) — never an error. let mut l = Some(LogsViewState::default()); let mut jobs = State::default(); on_key(&mut l, &mut jobs, key(KeyCode::Enter)); diff --git a/crates/rocm-dash-tui/src/ui/mod.rs b/crates/rocm-dash-tui/src/ui/mod.rs index 35c2cd5e..d81430d0 100644 --- a/crates/rocm-dash-tui/src/ui/mod.rs +++ b/crates/rocm-dash-tui/src/ui/mod.rs @@ -4,8 +4,8 @@ pub mod bench; pub mod command_screen; pub mod config_manager; pub mod core_bars; -pub mod doctor_manager; pub mod engine_manager; +pub mod examine_manager; pub mod exec; pub mod folder_browser; pub mod format; @@ -96,8 +96,8 @@ pub fn draw(f: &mut Frame, state: &mut AppState) { serve_wizard::draw_serve_wizard(f, outer[2], w, &state.jobs, &state.model_recipes, &theme); } else if let Some(em) = &state.engine_manager { engine_manager::draw_engine_manager(f, outer[2], em, &state.jobs, &theme); - } else if let Some(d) = &state.doctor_manager { - doctor_manager::draw_doctor_manager(f, outer[2], d, &state.jobs, &theme); + } else if let Some(d) = &state.examine_manager { + examine_manager::draw_examine_manager(f, outer[2], d, &state.jobs, &theme); } else if let Some(u) = &state.update_manager { update_manager::draw_update_manager(f, outer[2], u, &state.jobs, &theme); } else if let Some(im) = &state.install_manager { @@ -236,7 +236,7 @@ fn draw_footer(f: &mut Frame, area: Rect, state: &AppState, theme: &Theme) { spans.push(chip("e")); spans.push(Span::raw(" engines ")); spans.push(chip("d")); - spans.push(Span::raw(" doctor ")); + spans.push(Span::raw(" examine ")); spans.push(chip("u")); spans.push(Span::raw(" update ")); spans.push(chip("i")); diff --git a/crates/rocm-dash-tui/src/ui/tabs/instances.rs b/crates/rocm-dash-tui/src/ui/tabs/instances.rs index d146041b..99ac4df0 100644 --- a/crates/rocm-dash-tui/src/ui/tabs/instances.rs +++ b/crates/rocm-dash-tui/src/ui/tabs/instances.rs @@ -790,7 +790,7 @@ mod tests { services: None, serve_wizard: None, engine_manager: None, - doctor_manager: None, + examine_manager: None, update_manager: None, install_manager: None, logs_view: None, diff --git a/docs/automations.md b/docs/automations.md index 60d46f48..11b4ca5f 100644 --- a/docs/automations.md +++ b/docs/automations.md @@ -58,7 +58,7 @@ Automation actions never receive arbitrary shell access. Reviewed requests and contained watcher work can only call this restricted tool surface: - `check_updates`: run a read-only `rocm update` check. -- `doctor_snapshot`: capture a read-only doctor summary. +- `examine_snapshot`: capture a read-only examine summary. - `list_servers`: list rocm-cli managed servers. - `restart_server`: restart one explicit managed server. - `stop_server`: stop one explicit managed server. diff --git a/docs/ci-hardware-testing.md b/docs/ci-hardware-testing.md index 84bd57ec..12f115d2 100644 --- a/docs/ci-hardware-testing.md +++ b/docs/ci-hardware-testing.md @@ -16,7 +16,7 @@ records the intended design so it can be reviewed and wired up as a unit. `windows-build-and-test` (Windows) jobs publish their per-OS binaries (`rocm`, `rocmd`, `rocm-engine-*`) as workflow artifacts. 2. **Test on dedicated self-hosted runners.** Hardware-test jobs download those - exact artifacts and run only the checks hosted runners cannot: `rocm doctor` + exact artifacts and run only the checks hosted runners cannot: `rocm examine` host/GPU detection, engine `detect`/`capabilities`, the no-CPU-fallback smoke (`scripts/smoke_local.py --skip-build`), and the `scripts/*_therock_gpu_test.py` end-to-end GPU harnesses. diff --git a/docs/codex-vendoring-plan.md b/docs/codex-vendoring-plan.md index 1d2efca8..640fc8bc 100644 --- a/docs/codex-vendoring-plan.md +++ b/docs/codex-vendoring-plan.md @@ -186,7 +186,7 @@ The key shift is from command-oriented local CLI behavior to a session/tool/even The backend tool surface should include: -- `doctor` +- `examine` - `gpu_snapshot` - `list_gpus` - `list_runtimes` @@ -299,7 +299,7 @@ The vendored TUI should be re-skinned and re-contextualized rather than rewritte ### Required slash commands -- `/doctor` +- `/examine` - `/gpu` - `/runtimes` - `/install` @@ -330,7 +330,7 @@ Codex approval flows should be repurposed directly for ROCm operations. ### No approval required -- `doctor` +- `examine` - GPU telemetry - dry-run planning - runtime listing @@ -404,7 +404,7 @@ Deliverables: Deliverables: -- transcript can render `doctor` +- transcript can render `examine` - transcript can render GPU snapshot - transcript can render config and engine inventory @@ -508,7 +508,7 @@ Build this sequence first: 2. compile vendored TUI in `rocm-cli` CI 3. wrap launch from `rocm --experimental-codex-tui` 4. implement backend bridge for: - - `doctor` + - `examine` - `gpu_snapshot` - `config_get` - `list_engines` diff --git a/docs/current-tui-therock-correction-plan.md b/docs/current-tui-therock-correction-plan.md index 045694da..43692597 100644 --- a/docs/current-tui-therock-correction-plan.md +++ b/docs/current-tui-therock-correction-plan.md @@ -58,13 +58,13 @@ only for deterministic tests or explicit troubleshooting. Completed behavior: -- `rocm doctor` should report a compatible TheRock family when Windows GPU +- `rocm examine` should report a compatible TheRock family when Windows GPU inventory is available. - `rocm install sdk --format wheel` should use that detected compatible family. - `ROCM_CLI_THEROCK_FAMILY` or `--family gfx120X-all` should appear only in deterministic test instructions, not normal quick-start instructions. -If the installer cannot choose a family on a machine where doctor detects one, +If the installer cannot choose a family on a machine where examine detects one, treat that as a bug. ### 3. Auto-Start First-Time Setup diff --git a/docs/implementation-completion-audit.md b/docs/implementation-completion-audit.md index 98b04150..7ffc4fb5 100644 --- a/docs/implementation-completion-audit.md +++ b/docs/implementation-completion-audit.md @@ -20,7 +20,7 @@ The branch currently has local implementations for the V1 product surfaces: - First-time TUI setup with an arrow-key folder picker, approval cards, live setup logs, local pip cache under the selected ROCm install folder, and persisted settings under `~/.rocm`. -- Fast host/GPU checks for setup and Doctor: native Windows registry/system +- Fast host/GPU checks for setup and Examine: native Windows registry/system probes avoid PowerShell/CIM on the common path, native Linux uses sysfs/KFD/IP discovery first, and WSL collapses host display lookup to one bridge query only when Linux-side sysfs cannot answer. @@ -30,7 +30,7 @@ The branch currently has local implementations for the V1 product surfaces: - Runtime list, activate, uninstall, import, adopt, active-runtime markers, and previous-runtime validation. User-facing selection is list-based, not rollback-first. -- Doctor, GPU detection, managed runtime inventory, services inventory, logs, +- Examine, GPU detection, managed runtime inventory, services inventory, logs, update reports, and bounded startup update checks. - PyTorch and `llama.cpp` GPU serving with managed TheRock library propagation. The `llama.cpp` adapter is backed by upstream `llama-server`. @@ -123,7 +123,7 @@ python3 scripts/local_assistant_therock_gpu_test.py --self-test For WSL/Linux, build and run the native Linux binary directly: ```bash -/home/user/rocm-cli-e2e/bin/rocm doctor +/home/user/rocm-cli-e2e/bin/rocm examine python3 scripts/local_assistant_therock_gpu_test.py --rocm /home/user/rocm-cli-e2e/bin/rocm --engine lemonade --model qwen --require-tool-call python3 scripts/local_assistant_therock_gpu_test.py --rocm /home/user/rocm-cli-e2e/bin/rocm --engine pytorch --model qwen --require-tool-call python3 scripts/comfyui_therock_gpu_test.py --rocm /home/user/rocm-cli-e2e/bin/rocm diff --git a/docs/manual-testing.md b/docs/manual-testing.md index e841a591..bf7c56b7 100644 --- a/docs/manual-testing.md +++ b/docs/manual-testing.md @@ -15,7 +15,7 @@ cargo build --workspace --release ``` This writes `target/release/rocm.exe` on Windows and `target/release/rocm` on -Linux/WSL. Run the binary directly on each platform. On WSL/Linux the doctor +Linux/WSL. Run the binary directly on each platform. On WSL/Linux the examine output must report `os: linux` and `wsl: true`. Do not set `ROCM_CLI_THEROCK_FAMILY` during normal setup tests. rocm-cli should @@ -82,13 +82,13 @@ Quiet UI rule: After setup, check the machine state: ```powershell -rocm doctor +rocm examine rocm runtimes list ``` Expected result: -- `rocm doctor` shows a managed runtime. +- `rocm examine` shows a managed runtime. - `rocm runtimes list` shows the runtime key for the installed TheRock venv. - The active runtime is ready, or the output gives one clear next command. @@ -100,7 +100,7 @@ This tests the command-line install path without using the TUI: rocm install sdk --channel release --format wheel --prefix .\.rocm-work\data\envs\default rocm runtimes list rocm runtimes activate -rocm doctor +rocm examine ``` Replace `` with the exact key printed by `rocm runtimes list`. @@ -118,7 +118,7 @@ Expected result: - Runtime validation uses TheRock's runtime/devel package roots and `rocm_sdk.find_libraries`; `rocm-sdk path --root` is expected after the pinned `rocm[libraries,devel]` install succeeds. -- `rocm doctor` reports the active runtime as ready. +- `rocm examine` reports the active runtime as ready. Developer-only deterministic override: @@ -277,7 +277,7 @@ Expected result: - `rocm config show` says the key is saved in the OS secure store, or that the current session is using `OPENAI_API_KEY`. - The key value itself is never printed. -- `config.json`, logs, and doctor output do not contain the key. +- `config.json`, logs, and examine output do not contain the key. To remove the saved key: diff --git a/docs/testing.md b/docs/testing.md index 03bd0776..ef119bc1 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -32,7 +32,7 @@ The smoke path is the cross-platform local no-fallback acceptance surface. It uses an isolated config/data/cache root, does not install TheRock wheels, does not require a managed runtime, and verifies: -- first-run doctor and engine inventory state +- first-run examine and engine inventory state - telemetry-off config behavior - GPU-required recipe planning for `tiny-gpt2` - exact-runtime rejection for `rocm engines install` before any runtime exists @@ -64,19 +64,19 @@ cargo test --workspace ``` On WSL/Linux, build and run the native Linux binary directly (no APE launcher -prefix is required). The doctor output on WSL must include `os: linux` and +prefix is required). The examine output on WSL must include `os: linux` and `wsl: true`, and must not include `os: windows`. Use isolated `ROCM_CLI_CONFIG_DIR`, `ROCM_CLI_DATA_DIR`, and `ROCM_CLI_CACHE_DIR` roots for smoke tests, then delete those roots after the test so the user's real `.rocm` state stays clean. -Focused doctor guidance coverage: +Focused examine guidance coverage: ```bash -cargo test -p rocm-core doctor_render +cargo test -p rocm-core examine_render cargo test -p rocm-core managed_sdk_probe -cargo test -p rocm --bin rocm doctor_runtime_state_reports_ambiguous_default_runtime_id +cargo test -p rocm --bin rocm examine_runtime_state_reports_ambiguous_default_runtime_id cargo test -p rocm --bin rocm engine_runtime_selection_rejects_ambiguous_default_runtime_id cargo test -p rocm --bin rocm first_run_shows_dedicated_setup_screen_when_rocm_is_not_installed cargo test -p rocm --bin rocm onboarding_enter_requests_rocm_install_with_folder_prefix @@ -96,10 +96,10 @@ cargo test -p rocm tui::tests::slash_first_views_hide_backend_jargon -- --nocapt cargo test -p rocm tui::tests::install_sdk_bad_format_uses_friendly_labels_without_backend_jargon -- --nocapture cargo test -p rocm tui::tests::onboarding_ctrl_c_during_install_confirms_before_cancelling -- --nocapture cargo test -p rocm tui::tests::automations_review_actions_without_review_show_body_guidance -- --nocapture -cargo test -p rocm tui::tests::doctor_background_completion_updates_report_without_transcript -- --nocapture -cargo test -p rocm tui::tests::hidden_doctor_refresh_does_not_block_next_prompt_commands -- --nocapture -cargo test -p rocm tui::tests::doctor_command_does_not_steal_running_workflow_screens -- --nocapture -cargo test -p rocm tui::tests::doctor_command_does_not_hide_pending_approval -- --nocapture +cargo test -p rocm tui::tests::examine_background_completion_updates_report_without_transcript -- --nocapture +cargo test -p rocm tui::tests::hidden_examine_refresh_does_not_block_next_prompt_commands -- --nocapture +cargo test -p rocm tui::tests::examine_command_does_not_steal_running_workflow_screens -- --nocapture +cargo test -p rocm tui::tests::examine_command_does_not_hide_pending_approval -- --nocapture cargo test -p rocm tui::tests::daemon_status_uses_background_helper_language -- --nocapture cargo test -p rocm tui::tests::automations_bad_watcher_and_mode_stay_screen_local -- --nocapture cargo test -p rocm tui::tests::services_manager_stop_requests_rocmd_approval_without_transcript -- --nocapture @@ -119,17 +119,17 @@ cargo test -p rocm tui::tests::tui_help_teaches_engine_not_engines -- --nocaptur ``` Self-hosted GPU CI smoke is intentionally non-mutating. The MI300X job builds -the workspace, runs `rocm doctor`, then runs `detect` and `capabilities` for +the workspace, runs `rocm examine`, then runs `detect` and `capabilities` for all first-party engine adapters: PyTorch, llama.cpp, ATOM, vLLM, and SGLang. Live serving acceptance remains separate because it needs engine-specific runtime installs, model artifacts, and supported upstream GPU targets. -WSL live doctor sanity after building with a Linux target directory: +WSL live examine sanity after building with a Linux target directory: ```bash export CARGO_TARGET_DIR=/home/jam/.cache/rocm-cli-target cargo build --workspace -rocm doctor +rocm examine ``` Expected WSL fields when a managed TheRock runtime is registered and ROCDXG is @@ -562,7 +562,7 @@ Restricted sandbox tool API tests: ```bash cargo test -p rocmd sandbox_tool_cli_values_cover_restricted_plan_api -cargo test -p rocmd sandbox_tool_doctor_snapshot_is_read_only +cargo test -p rocmd sandbox_tool_examine_snapshot_is_read_only cargo test -p rocmd sandbox_tool_list_servers_returns_records cargo test -p rocmd sandbox_tool_list_servers_first_run_returns_empty_list cargo test -p rocmd sandbox_tool_requires_service_id_for_restart @@ -576,7 +576,7 @@ cargo test -p rocm --bin rocm proposal_sandbox_args_support_update_check ``` These cover the plan-listed restricted internal tool API: `check_updates`, -`doctor_snapshot`, `list_servers`, `restart_server`, `stop_server`, +`examine_snapshot`, `list_servers`, `restart_server`, `stop_server`, `prefetch_artifact`, and `notify_user`, plus the plan-derived `driver_plan` read-only extension. Read-only tools must report `mutating: false`; `notify_user` must record a local notification audit event; server restart/stop @@ -587,7 +587,7 @@ a sandbox audit event. Manual restricted-tool smoke: ```bash -rocmd sandbox-run doctor_snapshot --allow-native-fallback +rocmd sandbox-run examine_snapshot --allow-native-fallback rocmd sandbox-run list_servers --allow-native-fallback rocmd sandbox-run notify_user --message "ROCm check complete" --allow-native-fallback ``` @@ -601,11 +601,11 @@ Direct MCP helper safety smoke: ```bash cargo test -p rocmd direct_mcp_call -- --nocapture cargo test -p rocmd rocm_mcp_tools -- --nocapture -rocmd mcp-call doctor --arguments-json '{}' +rocmd mcp-call examine --arguments-json '{}' rocmd mcp-call install_sdk --arguments-json '{}' ``` -The read-only `doctor` helper call should run. The `install_sdk` helper call +The read-only `examine` helper call should run. The `install_sdk` helper call must fail unless `--allow-mutation` is supplied after an explicit user approval. User-facing TUI/chat flows should still route mutating tool calls through their normal approval cards rather than relying on this hidden helper @@ -880,7 +880,7 @@ By default, the Windows script keeps temporary state under under `.rocm-work/acceptance-linux`. Both roots are cleaned up unless the script fails or `ROCM_CLI_KEEP_ACCEPTANCE_ROOT=1` is set for debugging. Installed binary smoke checks set isolated config/data/cache directories inside those -roots and fail if `rocm doctor` reads the real user `.rocm` state. +roots and fail if `rocm examine` reads the real user `.rocm` state. For historical platform-bundle acceptance, the Linux bundle still verifies the vendored `rocm-codex` binary. The native rocm-cli binary does not include a diff --git a/docs/ux-guidelines.md b/docs/ux-guidelines.md index b0a38a64..863f5571 100644 --- a/docs/ux-guidelines.md +++ b/docs/ux-guidelines.md @@ -115,11 +115,11 @@ These guidelines are project constraints for user-facing ROCm CLI flows. as install, update, engine setup, service lifecycle, or automation approval. Keep live progress visible until the command finishes, then let the user leave. -- Passive background checks, such as Doctor refresh, may be closed while they +- Passive background checks, such as Examine refresh, may be closed while they finish as long as they do not block later setup actions or dump output into the transcript. -- Doctor must not interrupt an active install, update, service action, chat, - plan, or approval. Keep the current screen visible and explain that Doctor +- Examine must not interrupt an active install, update, service action, chat, + plan, or approval. Keep the current screen visible and explain that Examine can run after the active work finishes. - Usage/error text in the TUI should be a plain fix in the relevant screen. Reserve raw command syntax for explicit help or non-TUI command output. @@ -137,7 +137,7 @@ not append raw command reports to the transcript as the primary experience. Hidden compatibility aliases should follow the same navigability rules when typed, even when they are intentionally left out of completions and help. -Current navigable surfaces include `/home`, `/doctor`, `/setup`, `/permissions`, +Current navigable surfaces include `/home`, `/examine`, `/setup`, `/permissions`, `/runtimes`, `/engine`, `/model`, `/plan`, `/config`, `/automations`, `/reviews`, `/approve`, `/reject`, `/edit`, `/install`, `/services`, `/logs`, `/gpu`, `/update`, `/daemon`, `/chat`, `/provider`, `/uninstall`, `/comfyui`, @@ -156,7 +156,7 @@ Command navigability follow-up notes: provider-key, Logs/service detail, all-command navigability, model-picker, install-completion, Shift+Tab reverse-navigation, completion simplification, default-runtime validation, progress-label, engine-detail, setup-cancel, - body-visible error, and async Doctor background-blocking follow-ups. + body-visible error, and async Examine background-blocking follow-ups. - Keep primary panes free of raw backend labels after new command surfaces are added; raw command output belongs in Logs or explicit help/debug views. - Hide advanced file locations in TUI first views by default and expose them diff --git a/docs/wsl.md b/docs/wsl.md index 334cea9f..0138876d 100644 --- a/docs/wsl.md +++ b/docs/wsl.md @@ -136,9 +136,9 @@ For `rocm-cli`, the command itself should resolve the managed runtime manifest and apply that environment before launching non-PyTorch HIP apps such as `llama.cpp`. Users should not have to hand-export these values. -## Doctor And Install UX Recommendations +## Examine And Install UX Recommendations -`rocm doctor` should detect WSL cheaply and report: +`rocm examine` should detect WSL cheaply and report: - `wsl: true` - WSL distro/version diff --git a/install.ps1 b/install.ps1 index 043399a4..30ca73bd 100644 --- a/install.ps1 +++ b/install.ps1 @@ -471,10 +471,10 @@ try { Write-Host "run:" if (Test-PathInList $env:Path $InstallDir) { - Write-Host " rocm doctor" + Write-Host " rocm examine" } else { $rocmExe = Join-Path $InstallDir "rocm.exe" - Write-Host " & `"$rocmExe`" doctor" + Write-Host " & `"$rocmExe`" examine" } } finally { if (Test-Path -LiteralPath $tempRoot) { diff --git a/install.sh b/install.sh index 41d465a6..0d7b4e3b 100755 --- a/install.sh +++ b/install.sh @@ -401,13 +401,13 @@ case ":${PATH}:" in ;; *) echo "note: rocm is installed but this shell could not update PATH" - echo " run: ${INSTALL_DIR}/rocm doctor" + echo " run: ${INSTALL_DIR}/rocm examine" ;; esac echo "next:" if [ "${UPDATE_SHELL_PATH}" = "1" ]; then - echo " open a new terminal, then run: rocm doctor" + echo " open a new terminal, then run: rocm examine" else - echo " ${INSTALL_DIR}/rocm doctor" + echo " ${INSTALL_DIR}/rocm examine" fi diff --git a/plans/rocm-cli-implementation-plan.md b/plans/rocm-cli-implementation-plan.md index 9813631a..4908cc49 100644 --- a/plans/rocm-cli-implementation-plan.md +++ b/plans/rocm-cli-implementation-plan.md @@ -18,7 +18,7 @@ live tracker. - Implemented locally: the Rust CLI/TUI, first-time setup, TheRock managed `pip` venv installs, runtime activation/import/adopt/uninstall, bounded - Doctor reports, service records/logs/actions, the model registry CLI/TUI, + Examine reports, service records/logs/actions, the model registry CLI/TUI, provider adapters, PyTorch/llama.cpp/Lemonade/vLLM engine adapters, automation proposals/sandbox runner, packaging scripts, installer verification, release-readiness self-tests, and native per-OS release builds. @@ -53,13 +53,13 @@ live tracker. Done locally: - TUI, first-time setup, setup reset, navigable command screens, overlapping - approval/progress/detail cards, logs, Doctor, services, runtimes, update, and + approval/progress/detail cards, logs, Examine, services, runtimes, update, and provider/local-chat surfaces. - Managed TheRock wheel runtime installs with rocm-cli-owned venvs, local pip cache, exact TheRock torch/runtime selection, runtime activation, import, adopt, uninstall, and list-based install selection. -- Fast setup/Doctor host detection: setup shows detected GPU name/target/package - without running full Doctor; Windows Doctor uses native registry/system probes +- Fast setup/Examine host detection: setup shows detected GPU name/target/package + without running full Examine; Windows Examine uses native registry/system probes instead of PowerShell/CIM on the common path; native Linux uses KFD/sysfs/IP discovery before any ROCm userland tool; WSL avoids duplicate host display probes. @@ -151,7 +151,7 @@ Left or externally gated: - Interactive TTY: - `rocm` launches the full-screen TUI. - Non-interactive shell or explicit subcommand: - - `rocm doctor` + - `rocm examine` - `rocm install sdk --channel release` - `rocm serve qwen3.5 --engine vllm` - On startup, `rocm` should: @@ -180,7 +180,7 @@ use these surfaces instead: - First-time setup: a dedicated setup screen before Home, with arrow-key folder choices, plain approval cards, live progress, and visible install output. - Home and slash-command screens: stable left-side choices plus a plain-English - detail pane. Commands such as `/doctor`, `/runtimes`, `/engine`, `/services`, + detail pane. Commands such as `/examine`, `/runtimes`, `/engine`, `/services`, `/logs`, `/comfyui`, and `/serve` should open navigable screens, not append command dumps to a transcript. - Focused work: approvals, install/update progress, log details, connection @@ -220,7 +220,7 @@ use these surfaces instead: ### TUI Commands and Shortcuts - Slash commands: - - `/doctor` + - `/examine` - `/install sdk` - `/install driver` - `/update` @@ -257,7 +257,7 @@ use these surfaces instead: ### Primary Commands - `rocm` -- `rocm doctor` +- `rocm examine` - `rocm install sdk --channel release|nightly [--format wheel|tarball] [--prefix PATH]` - `rocm install driver --dkms` - `rocm update` @@ -376,7 +376,7 @@ use these surfaces instead: - The bootstrap should not install drivers or large runtimes by default. ### Host Detection -- `rocm doctor` should detect: +- `rocm examine` should detect: - OS, kernel, distro, architecture - CPU features - AMD GPU presence and GPU family @@ -422,7 +422,7 @@ use these surfaces instead: - silent kernel changes - Windows: - assume an AMD driver is already installed - - detect and report driver presence and compatibility in `doctor` + - detect and report driver presence and compatibility in `examine` - defer any Windows driver installation or upgrade flow to a later phase ### Existing Installations @@ -431,7 +431,7 @@ use these surfaces instead: - import into managed state - leave unmanaged and use read-only - If only a legacy ROCm runtime is found, `rocm-cli` should: - - report it in `doctor` + - report it in `examine` - warn that management and upgrades are out of scope - still allow CPU-only or provider-backed chat flows @@ -590,7 +590,7 @@ use these surfaces instead: - Sandboxed jobs should not receive raw shell access. - They should invoke a restricted internal tool API such as: - `check_updates` - - `doctor_snapshot` + - `examine_snapshot` - `list_servers` - `restart_server` - `stop_server` @@ -662,7 +662,7 @@ mode = "propose" | Capability | Windows V1 status | Notes | |------------|-------------------|-------| | `rocm` CLI and TUI | Supported | Native Windows terminal experience is in scope | -| `rocm doctor` | Supported | Detect host, GPU family, driver presence, and TheRock runtime state | +| `rocm examine` | Supported | Detect host, GPU family, driver presence, and TheRock runtime state | | TheRock runtime install | Supported | `pip` venv only | | TheRock tarball install | Deferred | Do not target `Program Files`-style system installs in V1 | | Windows driver install/upgrade | Deferred | Assume driver already installed; report compatibility only | @@ -690,9 +690,9 @@ mode = "propose" ### Phase 0: Product Skeleton - Create the repo layout and Rust workspace. - Implement config loading, state directories, logging, and signed manifest fetch. -- Implement `rocm doctor` with host detection and a plain CLI output path. +- Implement `rocm examine` with host detection and a plain CLI output path. - Exit criteria: - - `rocm doctor` works on supported Linux and Windows hosts + - `rocm examine` works on supported Linux and Windows hosts - bootstrap install downloads and runs the launcher ### Phase 1: Runtime Management @@ -775,7 +775,7 @@ mode = "propose" ## MVP Definition - `rocm` launches a TUI on Linux and Windows. -- `rocm doctor` detects host and runtime state. +- `rocm examine` detects host and runtime state. - `rocm install sdk --channel release|nightly` installs a managed TheRock runtime. - `rocm update` prompts for newer CLI/runtime versions. - `rocm serve` can launch: diff --git a/plans/rocm-cli-pytorch-engine-spec.md b/plans/rocm-cli-pytorch-engine-spec.md index 97371d33..69727cdf 100644 --- a/plans/rocm-cli-pytorch-engine-spec.md +++ b/plans/rocm-cli-pytorch-engine-spec.md @@ -330,7 +330,7 @@ Output: ### Windows GPU Detection - The engine should treat Windows GPU usability as a runtime fact, not an assumption. - Detection path: - - validate installed driver in `rocm doctor` + - validate installed driver in `rocm examine` - import `torch` - query `torch.cuda.is_available()` - inspect device properties through the PyTorch HIP-compatible API surface @@ -613,7 +613,7 @@ compile = "off" ### Product Integration - `rocm` TUI can resolve `pytorch` when the user chooses or requests it. - `rocmd` can restart a crashed `pytorch` service. -- `rocm logs` and `rocm doctor` reflect engine state accurately. +- `rocm logs` and `rocm examine` reflect engine state accurately. ## Deferred Work - embeddings diff --git a/plans/rocm-cli-remaining-implementation-plan.md b/plans/rocm-cli-remaining-implementation-plan.md index 5c909223..43c92c69 100644 --- a/plans/rocm-cli-remaining-implementation-plan.md +++ b/plans/rocm-cli-remaining-implementation-plan.md @@ -23,7 +23,7 @@ path is unavailable. | Phase | Status | What Exists | Main Remaining Plan-Derived Gaps | |---|---:|---|---| -| Phase 0: Product Skeleton | Partial | Rust workspace, app/crate layout, config/state dirs, deterministic first-time setup/bootstrap, bootstrap installers that verify signatures/checksums, seed a minimal config without overwriting user settings, require no preinstalled ROCm/Python/Rust/Cargo, and set PATH automatically for future terminals plus the current directly-invoked Windows PowerShell process, expanded bounded-latency `rocm doctor` host/driver/runtime/cache report, fast setup/Doctor GPU detection through native Windows registry/system APIs, native Linux KFD/sysfs/IP discovery, and one-shot WSL host display probing when needed, WSL managed-TheRock SDK gfx detection when sysfs/PATH tools are unavailable, active runtime state with explicit ambiguous-runtime reporting, adapter/plugin inventory, registered runtime inventory, CLI lifecycle audit records plus text lifecycle logs for update/install/runtime/engine/service actions, quiet bounded startup update check, optional signed metadata cache verification, and native per-OS rocm-cli binaries that run directly on Windows and Linux/WSL with first-party engines and the `rocmd` helper surface linked into `rocm` | Production signed metadata publication and native-Linux server/bare-metal (Instinct) validation | +| Phase 0: Product Skeleton | Partial | Rust workspace, app/crate layout, config/state dirs, deterministic first-time setup/bootstrap, bootstrap installers that verify signatures/checksums, seed a minimal config without overwriting user settings, require no preinstalled ROCm/Python/Rust/Cargo, and set PATH automatically for future terminals plus the current directly-invoked Windows PowerShell process, expanded bounded-latency `rocm examine` host/driver/runtime/cache report, fast setup/Examine GPU detection through native Windows registry/system APIs, native Linux KFD/sysfs/IP discovery, and one-shot WSL host display probing when needed, WSL managed-TheRock SDK gfx detection when sysfs/PATH tools are unavailable, active runtime state with explicit ambiguous-runtime reporting, adapter/plugin inventory, registered runtime inventory, CLI lifecycle audit records plus text lifecycle logs for update/install/runtime/engine/service actions, quiet bounded startup update check, optional signed metadata cache verification, and native per-OS rocm-cli binaries that run directly on Windows and Linux/WSL with first-party engines and the `rocmd` helper surface linked into `rocm` | Production signed metadata publication and native-Linux server/bare-metal (Instinct) validation | | Phase 1: Runtime Management | Partial | TheRock release/nightly resolution, pip venv installs using a single TheRock-index pinned `rocm[libraries,devel]` plus `torch`/`torchvision`/`torchaudio` transaction selected by exact ROCm wheel suffix for the current Python/platform, Windows pip-only enforcement, tarball flow on non-Windows, localized pip cache, cached TheRock index metadata with ETag, optional detached metadata signature verification, quiet freshness-gated startup update check, explicit runtime update apply/dry-run flow, versioned side-by-side runtime keys, active runtime activation marker, previous-runtime validation, read-only import/adopt, legacy ROCm migration guidance, explicit `rocm update` report with CLI/engine/recipe surface inventory | Production metadata public key and signed sidecar publication | | Phase 2: TUI Foundation | Implemented | Ratatui shell, typed transcript/status/prompt/sidebar, first-view `/home` dashboard with arrow-key status/action cards, internal activity buffer seeded from persisted lifecycle logs, first-time setup flow with visible arrow-key/folder-change guidance, sidebar mode state (`ask`, `act`, `serve`, `logs`, `automations`), vertical slash completion with cycling/scrolling, plain command-shaped prompt inputs route to navigable command screens before natural-language planning, `/engine` singular surface, `/runtimes` ROCm install picker with list/activate/uninstall/import/adopt and legacy typed rollback guidance back to explicit install selection, `/update --apply` approval/preview flow, streamed command output, overlapping running-progress cards for contained CLI/proposal/service work, idle Ctrl-C quit confirmation, unknown slash-command help cards, overlapping approval cards with Enter/Y approval plus edit/cancel flow for mutating commands and queued review requests, `/reviews` review-request alias with hidden legacy `/proposals` compatibility, provider-key entry/clear screens with safe default cancel, `/logs` action-log listing, lifecycle tail, hidden-by-default file locations, detail scrolling, bounded query search, stateful arrow-key/Left-Right/PageUp/PageDown pagination, and `/logs follow [query]` live refreshed log following over recent lifecycle/action logs | None known within V1 plan constraints | | Phase 3: Engine Plugin MVP | Implemented | Protocol types, PyTorch engine binary, `llama.cpp` adapter binary, Lemonade adapter for strict ROCm serving, `vllm` external-runtime adapter for Linux/WSL ROCm GPU serving, `detect/install/capabilities/resolve_model/launch/endpoint/healthcheck/stop/logs`, first-party stdio protocol routing coverage, foreground/managed launch, plugin-directory discovery helper, external plugin directory policy surfaced in CLI/TUI/docs, non-TUI `rocm services` list/logs/stop/restart parity with living services shown by default and failed/stopped history available through `--all`, active runtime wiring, shared recipe resolver before default engine selection, TheRock SDK env propagation for non-PyTorch HIP apps, explicit CPU-policy rejection for PyTorch/vLLM/Lemonade without implicit CPU fallback, Windows and WSL PyTorch GPU smoke coverage, Windows and WSL llama.cpp GPU smoke coverage, Windows and WSL Lemonade ROCm smoke coverage, WSL vLLM TheRock GPU smoke coverage, and in-process first-party engine/helper routes for the native per-OS binary | None known within V1 plan constraints | @@ -91,7 +91,7 @@ Locally implemented and recently verified: Linux-path smoke, TUI smoke, PyTorch local-assistant E2E, Lemonade local-assistant E2E, and ComfyUI install/start/status/stop E2E using temporary state roots. -- WSL ROCDXG readiness, WSL doctor gfx/family detection, and WSL acceptance +- WSL ROCDXG readiness, WSL examine gfx/family detection, and WSL acceptance commands with isolated workspace-local state. - TUI slash-command surfaces are navigable instead of transcript dumps; the latest focused pass covers arrow-key selection after accidental typing, @@ -122,7 +122,7 @@ Locally implemented and recently verified: installer PowerShell process; Linux/WSL writes the shell profile and explains the new-terminal step after `curl | sh`. Windows acceptance and focused WSL installer tests verify those paths. -- `rocm doctor` and first-time setup now use fast host GPU detection on the +- `rocm examine` and first-time setup now use fast host GPU detection on the normal path: Windows avoids PowerShell/CIM by reading registry/system APIs, native Linux uses sysfs/KFD/IP discovery before ROCm userland tools, and WSL uses a single host display bridge query only when Linux-side files cannot @@ -162,7 +162,7 @@ Locally implemented and recently verified: - Live local-assistant acceptance now passes on Windows with the `qwen` alias: `scripts/local_assistant_therock_gpu_test.py --model qwen --require-tool-call` launched the managed PyTorch GPU-required service, reached the local - provider, required a ROCm tool call, and observed the model use `rocm doctor` + provider, required a ROCm tool call, and observed the model use `rocm examine` through the tool bridge before stopping the managed service. - Live local-assistant acceptance now also passes on WSL with the `qwen` alias after path propagation fixes for managed child processes: @@ -177,7 +177,7 @@ Locally implemented and recently verified: detail card, Esc closes that card first, and ComfyUI start approval text uses the actual requested loopback address. - WSL lightweight acceptance currently passes for the Python self-tests plus - WSL `rocm doctor`, `rocm engines list`, `rocm comfyui status`, and + WSL `rocm examine`, `rocm engines list`, `rocm comfyui status`, and `rocm services list`. The first WSL sidecar found a stale `/mnt/d` debug binary that rejected newer `comfyui`/`services` subcommands; a forced rebuild into `/home/jam/.cache/rocm-cli-target` fixed the artifact and verified those @@ -189,7 +189,7 @@ Locally implemented and recently verified: user-facing TUI/chat flows continue to route mutating calls through approval cards. Focused tests tie this guard to the current MCP tool annotations. - TheRock version strings with embedded dates now render with a readable build - date in install/update output, non-TUI runtime lists, Doctor runtime state, + date in install/update output, non-TUI runtime lists, Examine runtime state, the setup screen, and TUI ROCm install lists/details. The setup screen's `Show install log` row now opens a focused saved-log view on ready and failed setup screens, includes the backing file path or missing-file details, resets @@ -274,7 +274,7 @@ These are narrow plan-derived fixes that unblock truthful V1 status. - `rocmd supervise` now prefers engine protocol health checks for readiness and keeps port probing as a compatibility fallback. - `server-recover` now uses protocol health status for running/ready managed services and can recover services whose endpoint is failed, unreachable, or exited. - TUI now accepts `/engine`, `/model`, and `/plan` commands using the existing inventory/planning paths. -- `rocm doctor` now reports kernel, driver policy/status/detail, managed runtime count, managed service count, and model cache entry count. +- `rocm examine` now reports kernel, driver policy/status/detail, managed runtime count, managed service count, and model cache entry count. - `rocm chat` now uses a provider status adapter surface for `local`, `openai`, and `anthropic`, including auth status, model list, and the shared ROCm tool schema id. - `rocm chat --prompt` now sends a non-interactive prompt through the provider contract; the local provider posts to a ready managed OpenAI-compatible service. - The local provider implements OpenAI-style SSE stream parsing and `stream: true` requests for managed local services. @@ -325,7 +325,7 @@ These are narrow plan-derived fixes that unblock truthful V1 status. - Runtime activation and previous-runtime validation now use exact versioned runtime keys and an active runtime marker, so release/nightly or old/new installs can coexist. - Existing rocm-cli runtime manifests can be imported read-only, and existing TheRock Python environments can be adopted read-only after probing `rocm_sdk`. - TheRock pip and tarball metadata resolution now uses a rocm-cli metadata cache with ETag support. -- `rocm doctor` now uses bounded optional probes, Windows display inventory before ROCm tools, and reports dedicated gfx target/compatible TheRock family without requiring an existing ROCm install. +- `rocm examine` now uses bounded optional probes, Windows display inventory before ROCm tools, and reports dedicated gfx target/compatible TheRock family without requiring an existing ROCm install. - The TUI `/runtimes` flow now covers list/activate/uninstall/import/adopt with option-aware adopt validation and flag completions. Normal users switch ROCm installs by selecting an installed item from the list; legacy typed rollback commands are guided back to that picker instead of being advertised as a primary action. - WSL support now includes ROCDXG preflight/setup documentation and strict llama.cpp GPU E2E validation against managed TheRock libraries. - Deferred/unsupported expanded engines now fail with explicit no-fallback messages when no external plugin is installed. @@ -335,7 +335,7 @@ These are narrow plan-derived fixes that unblock truthful V1 status. registry in non-TUI mode instead of falling through to natural-language planning. - Normal CLI startup now performs a quiet bounded TheRock update check when a managed runtime exists, using cached metadata, a freshness gate, and a hard metadata fetch timeout. First-run hosts with no managed runtimes are left untouched, and the localized pip wheel cache is not created. -- `rocm doctor` now appends active runtime state and non-invasive engine adapter/plugin inventory without invoking engine protocol detection, preserving bounded doctor latency. +- `rocm examine` now appends active runtime state and non-invasive engine adapter/plugin inventory without invoking engine protocol detection, preserving bounded examine latency. - CLI lifecycle actions now write both JSONL audit records and plain-text logs: a global `logs/cli-lifecycle.log` and per-action logs under `logs/cli`. `/logs` shows these paths for TUI/CLI inspection. - `/logs` now lists per-action CLI log files and tails recent lifecycle lines inline without creating first-run log directories. @@ -358,7 +358,7 @@ These are narrow plan-derived fixes that unblock truthful V1 status. Goal: make the currently implemented CLI/TUI/runtime/engine skeleton honest, testable, and aligned with V1 constraints. -- Expand `rocm doctor` with kernel, distro, CPU, runtime state, driver state, engine inventory, model cache, and legacy ROCm detection. +- Expand `rocm examine` with kernel, distro, CPU, runtime state, driver state, engine inventory, model cache, and legacy ROCm detection. - Add basic logging paths and log writes for install/update/serve/automation lifecycle. - CLI audit events now cover update/install/runtime/engine/managed-service actions. TUI screen-owned commands now also persist full-output screen @@ -366,7 +366,7 @@ Goal: make the currently implemented CLI/TUI/runtime/engine skeleton honest, tes visible log browser/search surface alongside lifecycle and CLI action logs. - Add bounded cached update checks on every `rocm` invocation. - Completed with a quiet, freshness-gated startup check that records status under the TheRock metadata cache and skips first-run/no-runtime hosts. -- Complete deeper engine inventory and active runtime reporting in `rocm doctor`. +- Complete deeper engine inventory and active runtime reporting in `rocm examine`. - Completed with `runtime_state` and `engine_inventory` sections that report active runtime keys/status and adapter/plugin availability without slow engine detect calls. - Complete per-command lifecycle log writes for install/update/serve/automation operations. - Completed for CLI lifecycle audit callers with global and action-specific @@ -400,7 +400,7 @@ Goal: satisfy Phase 1 runtime exit criteria. - Add existing TheRock detection with adopt/import/read-only modes. - Completed for explicit import/adopt commands. - Report legacy ROCm installs as unmanaged with migration guidance. - - Completed in `rocm doctor`. + - Completed in `rocm examine`. ## Milestone 3: TUI, Chat, and Planning @@ -497,7 +497,7 @@ Goal: satisfy Phase 3 and Phase 5 before expanded engines. expectations, and manual alternative recipe hints. `/model` renders these advisory checks and recommends alternatives only as explicit manual choices. - - `rocm doctor` now reports host system RAM, and TUI `/model` compares that + - `rocm examine` now reports host system RAM, and TUI `/model` compares that telemetry against each recipe's recommended system RAM. - Signed recipe index artifact descriptors can now render `metadata_available` or `blocked`. @@ -564,7 +564,7 @@ Goal: satisfy Phase 6 without expanding beyond the plan. 10.1/9.7/8.10, Rocky 9.7, and SLES 15.7. Unsupported Linux IDs remain non-mutating instead of guessing commands. - Remaining work: privileged live acceptance coverage on supported hosts. -- Windows: add driver presence and compatibility reporting to `rocm doctor`; do not add Windows driver install or upgrade. +- Windows: add driver presence and compatibility reporting to `rocm examine`; do not add Windows driver install or upgrade. ## Milestone 6: Automations, Sandbox, and Hardening @@ -648,7 +648,7 @@ called out in the phase table and `Current Remaining Gates`. | Worker Lane | Owns | Current State | |---|---|---| -| Runtime/Doctor | `apps/rocm/src/therock.rs`, doctor data in `crates/rocm-core` | Implemented locally; production metadata signing remains an owner publication gate | +| Runtime/Examine | `apps/rocm/src/therock.rs`, examine data in `crates/rocm-core` | Implemented locally; production metadata signing remains an owner publication gate | | Engine Lifecycle | `crates/rocm-engine-protocol`, `engines/pytorch`, service log/status paths | Implemented locally for PyTorch/llama.cpp plus gated Linux/WSL adapters | | TUI/Planner | `apps/rocm/src/tui.rs`, planner code in `apps/rocm/src/main.rs` or a new planner module | Implemented locally with navigable screens, approvals, progress cards, and structured routing | | Providers | Provider modules plus config/key storage | Implemented locally for local/OpenAI/Anthropic providers and served-model ROCm tools | diff --git a/scripts/acceptance-install-upgrade-tui-uninstall.ps1 b/scripts/acceptance-install-upgrade-tui-uninstall.ps1 index b3820bc5..3084ccac 100644 --- a/scripts/acceptance-install-upgrade-tui-uninstall.ps1 +++ b/scripts/acceptance-install-upgrade-tui-uninstall.ps1 @@ -26,7 +26,7 @@ $MissingSignatureLog = Join-Path $AcceptanceRoot "missing-signature-failure.log" $NoPublicKeyLog = Join-Path $AcceptanceRoot "no-public-key-failure.log" $PemInstallLog = Join-Path $AcceptanceRoot "pem-install.log" $PathUpdateInstallLog = Join-Path $AcceptanceRoot "path-update-install.log" -$DoctorLog = Join-Path $AcceptanceRoot "doctor.log" +$ExamineLog = Join-Path $AcceptanceRoot "examine.log" $UninstallLog = Join-Path $AcceptanceRoot "uninstall.log" function Fail { @@ -446,18 +446,18 @@ try { $env:LOCALAPPDATA = $LocalAppDataDir Invoke-Checked "acceptance: installed rocm version" $rocmExe @("version") - Invoke-Checked "acceptance: installed rocm doctor" $rocmExe @("doctor") $DoctorLog - if (-not (Select-String -LiteralPath $DoctorLog -SimpleMatch -Pattern $ConfigDir -Quiet)) { - Fail "installed rocm doctor did not use the isolated config dir" + Invoke-Checked "acceptance: installed rocm examine" $rocmExe @("examine") $ExamineLog + if (-not (Select-String -LiteralPath $ExamineLog -SimpleMatch -Pattern $ConfigDir -Quiet)) { + Fail "installed rocm examine did not use the isolated config dir" } - if (-not (Select-String -LiteralPath $DoctorLog -SimpleMatch -Pattern $DataDir -Quiet)) { - Fail "installed rocm doctor did not use the isolated data dir" + if (-not (Select-String -LiteralPath $ExamineLog -SimpleMatch -Pattern $DataDir -Quiet)) { + Fail "installed rocm examine did not use the isolated data dir" } - if (-not (Select-String -LiteralPath $DoctorLog -SimpleMatch -Pattern $CacheDir -Quiet)) { - Fail "installed rocm doctor did not use the isolated cache dir" + if (-not (Select-String -LiteralPath $ExamineLog -SimpleMatch -Pattern $CacheDir -Quiet)) { + Fail "installed rocm examine did not use the isolated cache dir" } - if (Select-String -LiteralPath $DoctorLog -SimpleMatch -Pattern $RealUserRocmDir -Quiet) { - Fail "installed rocm doctor read the real user rocm state" + if (Select-String -LiteralPath $ExamineLog -SimpleMatch -Pattern $RealUserRocmDir -Quiet) { + Fail "installed rocm examine read the real user rocm state" } Invoke-Checked "acceptance: installed rocm engines list" $rocmExe @("engines", "list") Invoke-Checked "acceptance: installed rocmd status" $rocmdExe @("status") diff --git a/scripts/pytorch_therock_gpu_test.py b/scripts/pytorch_therock_gpu_test.py index ad3736f0..c78b2cc6 100644 --- a/scripts/pytorch_therock_gpu_test.py +++ b/scripts/pytorch_therock_gpu_test.py @@ -206,7 +206,7 @@ def assert_rocm_gpu_detected(detect: dict[str, Any]) -> None: if not rocm_gpu or not rocm_gpu.get("available"): raise RuntimeError( "PyTorch could not see an AMD GPU. No CPU fallback is allowed.\n" - "Run `rocm doctor`, fix AMD driver/GPU detection there, then retry this test.\n" + "Run `rocm examine`, fix AMD driver/GPU detection there, then retry this test.\n" + json.dumps(detect, indent=2) ) diff --git a/scripts/smoke_local.py b/scripts/smoke_local.py index 46de55fc..283ed263 100644 --- a/scripts/smoke_local.py +++ b/scripts/smoke_local.py @@ -258,11 +258,11 @@ def main() -> int: version = run("rocm version", [rocm, "version"], env=env) assert_contains(version, "rocm ", "rocm version") - doctor = run("rocm doctor", [rocm, "doctor"], env=env) - assert_contains(doctor, "rocm doctor", "rocm doctor") - assert_contains(doctor, "default_engine:", "rocm doctor") - assert_contains(doctor, "managed_runtimes: 0", "rocm doctor first-run state") - assert_contains(doctor, "managed_services: 0", "rocm doctor first-run state") + examine = run("rocm examine", [rocm, "examine"], env=env) + assert_contains(examine, "rocm examine", "rocm examine") + assert_contains(examine, "default_engine:", "rocm examine") + assert_contains(examine, "managed_runtimes: 0", "rocm examine first-run state") + assert_contains(examine, "managed_services: 0", "rocm examine first-run state") engines = run("rocm engines list", [rocm, "engines", "list"], env=env) assert_contains(engines, "llama.cpp", "rocm engines list") @@ -419,20 +419,20 @@ def main() -> int: ): fail(f"unexpected bridge snapshot protocol: {bridge}") - sandbox_doctor = parse_json( + sandbox_examine = parse_json( run( - "rocmd sandbox doctor snapshot", - [rocmd, "sandbox-run", "doctor_snapshot", "--allow-native-fallback"], + "rocmd sandbox examine snapshot", + [rocmd, "sandbox-run", "examine_snapshot", "--allow-native-fallback"], env=env, ), - "sandbox doctor snapshot", + "sandbox examine snapshot", ) if ( - not isinstance(sandbox_doctor, dict) - or sandbox_doctor.get("tool") != "doctor_snapshot" - or not sandbox_doctor.get("ok") + not isinstance(sandbox_examine, dict) + or sandbox_examine.get("tool") != "examine_snapshot" + or not sandbox_examine.get("ok") ): - fail(f"unexpected sandbox doctor result: {sandbox_doctor}") + fail(f"unexpected sandbox examine result: {sandbox_examine}") sandbox_servers = parse_json( run( diff --git a/scripts/therock_sdk_install_test.py b/scripts/therock_sdk_install_test.py index ff9110a2..e0f1566d 100644 --- a/scripts/therock_sdk_install_test.py +++ b/scripts/therock_sdk_install_test.py @@ -456,10 +456,10 @@ def main() -> int: bootstrap_python = ensure_bootstrap_python(test_root, args.python) env = isolated_env(test_root, bootstrap_python, args.family) - doctor = run( - "rocm doctor before SDK install", [str(rocm), "doctor"], env=env, timeout=120 + examine = run( + "rocm examine before SDK install", [str(rocm), "examine"], env=env, timeout=120 ) - assert_contains(doctor, "rocm doctor", "doctor") + assert_contains(examine, "rocm examine", "examine") install_argv = [ str(rocm),