From ee228545d822de157518dec714dca4f13cbd3374 Mon Sep 17 00:00:00 2001 From: stefanstokic-oai Date: Wed, 10 Jun 2026 15:33:22 -0400 Subject: [PATCH 1/2] tui: add explicit external agent import command --- codex-rs/app-server-client/src/lib.rs | 8 ++ codex-rs/app-server/src/in_process.rs | 10 +- codex-rs/cli/src/main.rs | 8 ++ codex-rs/tui/src/app/app_server_events.rs | 11 +- codex-rs/tui/src/app/event_dispatch.rs | 26 ++++ codex-rs/tui/src/app_event.rs | 3 + codex-rs/tui/src/app_server_session.rs | 67 ++++++++++ codex-rs/tui/src/chatwidget/slash_dispatch.rs | 5 + .../src/chatwidget/tests/slash_commands.rs | 12 ++ .../external_agent_config_migration_flow.rs | 124 ++++++++++++++++++ ...ernal_agent_config_migration_flow_tests.rs | 21 +++ codex-rs/tui/src/lib.rs | 2 +- codex-rs/tui/src/slash_command.rs | 3 + ...ernal_agent_config_migration_messages.snap | 12 ++ 14 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 codex-rs/tui/src/external_agent_config_migration_flow.rs create mode 100644 codex-rs/tui/src/external_agent_config_migration_flow_tests.rs create mode 100644 codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 11684c63600d..f703bab1a650 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -158,6 +158,7 @@ pub(crate) fn server_notification_requires_delivery(notification: &ServerNotific ServerNotification::TurnCompleted(_) | ServerNotification::ThreadSettingsUpdated(_) | ServerNotification::ItemCompleted(_) + | ServerNotification::ExternalAgentConfigImportCompleted(_) | ServerNotification::AgentMessageDelta(_) | ServerNotification::PlanDelta(_) | ServerNotification::ReasoningSummaryTextDelta(_) @@ -2139,6 +2140,13 @@ mod tests { ) ) )); + assert!(event_requires_delivery( + &InProcessServerEvent::ServerNotification( + codex_app_server_protocol::ServerNotification::ExternalAgentConfigImportCompleted( + codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification {}, + ) + ) + )); assert!(!event_requires_delivery(&InProcessServerEvent::Lagged { skipped: 1 })); diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index 448a3898511c..27191e721538 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -104,7 +104,9 @@ type PendingClientRequestResponse = std::result::Result bool { matches!( notification, - ServerNotification::TurnCompleted(_) | ServerNotification::ThreadSettingsUpdated(_) + ServerNotification::TurnCompleted(_) + | ServerNotification::ThreadSettingsUpdated(_) + | ServerNotification::ExternalAgentConfigImportCompleted(_) ) } @@ -729,6 +731,7 @@ mod tests { use super::*; use codex_app_server_protocol::ClientInfo; use codex_app_server_protocol::ConfigRequirementsReadResponse; + use codex_app_server_protocol::ExternalAgentConfigImportCompletedNotification; use codex_app_server_protocol::SessionSource as ApiSessionSource; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; @@ -893,5 +896,10 @@ mod tests { }, }) )); + assert!(server_notification_requires_delivery( + &ServerNotification::ExternalAgentConfigImportCompleted( + ExternalAgentConfigImportCompletedNotification {}, + ) + )); } } diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index 13acf40d4e61..be7a09feb497 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -2600,6 +2600,14 @@ mod tests { ); } + #[test] + fn import_remains_an_interactive_prompt() { + let cli = MultitoolCli::try_parse_from(["codex", "import"]).expect("parse"); + + assert!(cli.subcommand.is_none()); + assert_eq!(cli.interactive.prompt.as_deref(), Some("import")); + } + #[test] fn profile_v2_rejects_non_plain_names_at_parse_time() { assert!( diff --git a/codex-rs/tui/src/app/app_server_events.rs b/codex-rs/tui/src/app/app_server_events.rs index 268dbd305b22..6f6940c06753 100644 --- a/codex-rs/tui/src/app/app_server_events.rs +++ b/codex-rs/tui/src/app/app_server_events.rs @@ -93,16 +93,25 @@ impl App { return; } ServerNotification::ExternalAgentConfigImportCompleted(_) => { - let cwd = self.chat_widget.config_ref().cwd.to_path_buf(); + let should_report_completion = + app_server_client.consume_external_agent_config_import_completion(); if let Err(err) = self.refresh_in_memory_config_from_disk().await { tracing::warn!( error = %err, "failed to refresh config after external agent config import" ); } + let cwd = self.chat_widget.config_ref().cwd.to_path_buf(); self.chat_widget.refresh_plugin_mentions(); self.chat_widget.submit_op(AppCommand::reload_user_config()); self.fetch_plugins_list(app_server_client, cwd); + if should_report_completion { + self.chat_widget.add_info_message( + crate::external_agent_config_migration_flow::EXTERNAL_AGENT_CONFIG_MIGRATION_FINISHED_MESSAGE + .to_string(), + /*hint*/ None, + ); + } return; } ServerNotification::AppListUpdated(notification) => { diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 2d97a513c0c3..dd7be1d84235 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -6,6 +6,7 @@ use super::resize_reflow::trailing_run_start; use super::*; use crate::config_update::format_config_error; +use crate::external_agent_config_migration_flow::ExternalAgentConfigMigrationFlowOutcome; #[cfg(target_os = "windows")] use codex_config::types::WindowsSandboxModeToml; @@ -110,6 +111,31 @@ impl App { // Leaving alt-screen may blank the inline viewport; force a redraw either way. tui.frame_requester().schedule_frame(); } + AppEvent::OpenExternalAgentConfigMigration => { + match crate::external_agent_config_migration_flow::handle_external_agent_config_migration_prompt( + tui, + app_server, + &self.config, + ) + .await + { + Ok(ExternalAgentConfigMigrationFlowOutcome::Started(message)) => { + self.chat_widget.add_info_message(message, /*hint*/ None); + } + Ok(ExternalAgentConfigMigrationFlowOutcome::NoItems) => { + self.chat_widget.add_info_message( + crate::external_agent_config_migration_flow::EXTERNAL_AGENT_CONFIG_MIGRATION_NO_ITEMS_MESSAGE + .to_string(), + /*hint*/ None, + ); + } + Ok(ExternalAgentConfigMigrationFlowOutcome::Cancelled) => {} + Err(error_message) => { + self.chat_widget.add_error_message(error_message); + } + } + tui.frame_requester().schedule_frame(); + } AppEvent::ResumeSessionByIdOrName(id_or_name) => { match crate::lookup_session_target_with_app_server(app_server, &id_or_name).await? { Some(target_session) => { diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index 41fa6bdce4a0..48667b4325da 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -221,6 +221,9 @@ pub(crate) enum AppEvent { /// Open the resume picker inside the running TUI session. OpenResumePicker, + /// Open the Claude Code migration picker inside the running TUI session. + OpenExternalAgentConfigMigration, + /// Resume a thread by UUID or thread name inside the running TUI session. ResumeSessionByIdOrName(String), diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index e249575d41ed..36fde31b809e 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -22,6 +22,11 @@ use codex_app_server_protocol::AuthMode; use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::ConfigBatchWriteParams; use codex_app_server_protocol::ConfigWriteResponse; +use codex_app_server_protocol::ExternalAgentConfigDetectParams; +use codex_app_server_protocol::ExternalAgentConfigDetectResponse; +use codex_app_server_protocol::ExternalAgentConfigImportParams; +use codex_app_server_protocol::ExternalAgentConfigImportResponse; +use codex_app_server_protocol::ExternalAgentConfigMigrationItem; use codex_app_server_protocol::GetAccountParams; use codex_app_server_protocol::GetAccountRateLimitsResponse; use codex_app_server_protocol::GetAccountResponse; @@ -123,12 +128,16 @@ use color_eyre::eyre::Result; use color_eyre::eyre::WrapErr; use std::collections::HashMap; use std::path::PathBuf; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; use std::time::Duration; use std::time::Instant; use uuid::Uuid; const JSONRPC_INVALID_REQUEST: i64 = -32600; const JSONRPC_METHOD_NOT_FOUND: i64 = -32601; +pub(crate) const EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE: &str = + "A previous Claude Code import is still running. Wait for it to finish before importing again."; const THREAD_SETTINGS_UPDATE_METHOD: &str = "thread/settings/update"; fn bootstrap_request_error(context: &'static str, err: TypedRequestError) -> color_eyre::Report { @@ -171,6 +180,7 @@ pub(crate) struct AppServerSession { thread_settings_update_supported: bool, default_model: Option, available_models: Vec, + external_agent_config_import_completion_pending: AtomicBool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -214,6 +224,7 @@ impl AppServerSession { thread_settings_update_supported: true, default_model: None, available_models: Vec::new(), + external_agent_config_import_completion_pending: AtomicBool::new(false), } } @@ -230,6 +241,10 @@ impl AppServerSession { matches!(self.thread_params_mode, ThreadParamsMode::Remote) } + pub(crate) fn uses_embedded_app_server(&self) -> bool { + matches!(&self.client, AppServerClient::InProcess(_)) + } + pub(crate) fn server_version(&self) -> Option<&str> { let AppServerClient::Remote(client) = &self.client else { return None; @@ -344,6 +359,58 @@ impl AppServerSession { .map_err(|err| bootstrap_request_error("account/read failed during TUI bootstrap", err)) } + pub(crate) async fn external_agent_config_detect( + &mut self, + params: ExternalAgentConfigDetectParams, + ) -> Result { + let request_id = self.next_request_id(); + self.client + .request_typed(ClientRequest::ExternalAgentConfigDetect { request_id, params }) + .await + .wrap_err("externalAgentConfig/detect failed during Claude Code import") + } + + pub(crate) async fn external_agent_config_import( + &mut self, + migration_items: Vec, + ) -> Result<()> { + // Mark the import active before sending the request so a fast completion notification + // cannot arrive before the TUI records it. + if self + .external_agent_config_import_completion_pending + .swap(true, Ordering::Relaxed) + { + color_eyre::eyre::bail!(EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE); + } + let request_id = self.next_request_id(); + let response: Result = self + .client + .request_typed(ClientRequest::ExternalAgentConfigImport { + request_id, + params: ExternalAgentConfigImportParams { migration_items }, + }) + .await + .wrap_err("externalAgentConfig/import failed during Claude Code import"); + match response { + Ok(_) => Ok(()), + Err(err) => { + self.external_agent_config_import_completion_pending + .store(false, Ordering::Relaxed); + Err(err) + } + } + } + + pub(crate) fn external_agent_config_import_in_progress(&self) -> bool { + self.external_agent_config_import_completion_pending + .load(Ordering::Relaxed) + } + + pub(crate) fn consume_external_agent_config_import_completion(&self) -> bool { + self.external_agent_config_import_completion_pending + .swap(false, Ordering::Relaxed) + } + pub(crate) async fn next_event(&mut self) -> Option { self.client.next_event().await } diff --git a/codex-rs/tui/src/chatwidget/slash_dispatch.rs b/codex-rs/tui/src/chatwidget/slash_dispatch.rs index e7776a300768..0bcc02601883 100644 --- a/codex-rs/tui/src/chatwidget/slash_dispatch.rs +++ b/codex-rs/tui/src/chatwidget/slash_dispatch.rs @@ -398,6 +398,10 @@ impl ChatWidget { SlashCommand::Skills => { self.open_skills_menu(); } + SlashCommand::Import => { + self.app_event_tx + .send(AppEvent::OpenExternalAgentConfigMigration); + } SlashCommand::Hooks => { self.add_hooks_output(); } @@ -1010,6 +1014,7 @@ impl ChatWidget { | SlashCommand::Logout | SlashCommand::Mention | SlashCommand::Skills + | SlashCommand::Import | SlashCommand::Hooks | SlashCommand::Title | SlashCommand::Statusline diff --git a/codex-rs/tui/src/chatwidget/tests/slash_commands.rs b/codex-rs/tui/src/chatwidget/tests/slash_commands.rs index 02faef89f4ca..e257db69f118 100644 --- a/codex-rs/tui/src/chatwidget/tests/slash_commands.rs +++ b/codex-rs/tui/src/chatwidget/tests/slash_commands.rs @@ -1861,6 +1861,18 @@ async fn slash_resume_opens_picker() { assert_matches!(rx.try_recv(), Ok(AppEvent::OpenResumePicker)); } +#[tokio::test] +async fn slash_import_opens_claude_code_import_picker() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + + chat.dispatch_command(SlashCommand::Import); + + assert_matches!( + rx.try_recv(), + Ok(AppEvent::OpenExternalAgentConfigMigration) + ); +} + #[tokio::test] async fn slash_archive_confirmation_requests_current_thread_archive() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; diff --git a/codex-rs/tui/src/external_agent_config_migration_flow.rs b/codex-rs/tui/src/external_agent_config_migration_flow.rs new file mode 100644 index 000000000000..e8611d571878 --- /dev/null +++ b/codex-rs/tui/src/external_agent_config_migration_flow.rs @@ -0,0 +1,124 @@ +use crate::app_server_session::AppServerSession; +use crate::app_server_session::EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE; +use crate::external_agent_config_migration::ExternalAgentConfigMigrationOutcome; +use crate::external_agent_config_migration::run_external_agent_config_migration_prompt; +use crate::legacy_core::config::Config; +use crate::tui; +use codex_app_server_protocol::ExternalAgentConfigDetectParams; + +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_FINISHED_MESSAGE: &str = + "Claude Code import finished. Run /import again to check for additional items."; +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_NO_ITEMS_MESSAGE: &str = + "No Claude Code setup was found to import."; +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_REMOTE_UNAVAILABLE_MESSAGE: &str = "Import from Claude Code is unavailable in remote sessions. Start Codex locally and run /import."; +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_DAEMON_UNAVAILABLE_MESSAGE: &str = "Import from Claude Code is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import."; + +pub(crate) enum ExternalAgentConfigMigrationFlowOutcome { + Started(String), + NoItems, + Cancelled, +} + +fn external_agent_config_migration_success_message(remaining_item_count: usize) -> String { + let message = "Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats."; + match remaining_items_handoff(remaining_item_count) { + Some(remaining_items_handoff) => format!("{message} {remaining_items_handoff}"), + None => message.to_string(), + } +} + +fn remaining_items_handoff(remaining_item_count: usize) -> Option { + match remaining_item_count { + 0 => None, + 1 => Some( + "1 additional item remains. After it finishes, run /import again to review it." + .to_string(), + ), + _ => Some(format!( + "{remaining_item_count} additional items remain. After it finishes, run /import again to review them." + )), + } +} + +pub(crate) async fn handle_external_agent_config_migration_prompt( + tui: &mut tui::Tui, + app_server: &mut AppServerSession, + config: &Config, +) -> Result { + if app_server.uses_remote_workspace() { + return Err(EXTERNAL_AGENT_CONFIG_MIGRATION_REMOTE_UNAVAILABLE_MESSAGE.to_string()); + } + if !app_server.uses_embedded_app_server() { + return Err(EXTERNAL_AGENT_CONFIG_MIGRATION_DAEMON_UNAVAILABLE_MESSAGE.to_string()); + } + if app_server.external_agent_config_import_in_progress() { + return Err(EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE.to_string()); + } + + let cwd = config.cwd.to_path_buf(); + let detected_items = match app_server + .external_agent_config_detect(ExternalAgentConfigDetectParams { + include_home: true, + cwds: Some(vec![cwd.clone()]), + }) + .await + { + Ok(response) => response.items, + Err(err) => { + tracing::warn!( + error = %err, + cwd = %cwd.display(), + "failed to detect external agent config migrations" + ); + return Err(format!("Could not check for Claude Code setup: {err}")); + } + }; + + if detected_items.is_empty() { + return Ok(ExternalAgentConfigMigrationFlowOutcome::NoItems); + } + + let mut selected_items = detected_items.clone(); + let mut error: Option = None; + + loop { + match run_external_agent_config_migration_prompt( + tui, + &detected_items, + &selected_items, + error.as_deref(), + ) + .await + { + ExternalAgentConfigMigrationOutcome::Proceed(items) => { + selected_items = items.clone(); + match app_server.external_agent_config_import(items).await { + Ok(()) => { + let remaining_item_count = + detected_items.len().saturating_sub(selected_items.len()); + let success_message = + external_agent_config_migration_success_message(remaining_item_count); + return Ok(ExternalAgentConfigMigrationFlowOutcome::Started( + success_message, + )); + } + Err(err) => { + tracing::warn!( + error = %err, + cwd = %cwd.display(), + "failed to import external agent config migration items" + ); + error = Some(format!("Import failed: {err}")); + } + } + } + ExternalAgentConfigMigrationOutcome::Skip => { + return Ok(ExternalAgentConfigMigrationFlowOutcome::Cancelled); + } + } + } +} + +#[cfg(test)] +#[path = "external_agent_config_migration_flow_tests.rs"] +mod tests; diff --git a/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs b/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs new file mode 100644 index 000000000000..913b55607fe4 --- /dev/null +++ b/codex-rs/tui/src/external_agent_config_migration_flow_tests.rs @@ -0,0 +1,21 @@ +use super::*; + +#[test] +fn external_agent_config_migration_messages_snapshot() { + let cases = [0, 1, 2]; + + let messages = cases + .map(external_agent_config_migration_success_message) + .into_iter() + .chain([ + EXTERNAL_AGENT_CONFIG_MIGRATION_FINISHED_MESSAGE.to_string(), + EXTERNAL_AGENT_CONFIG_MIGRATION_NO_ITEMS_MESSAGE.to_string(), + EXTERNAL_AGENT_CONFIG_MIGRATION_REMOTE_UNAVAILABLE_MESSAGE.to_string(), + EXTERNAL_AGENT_CONFIG_MIGRATION_DAEMON_UNAVAILABLE_MESSAGE.to_string(), + EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE.to_string(), + ]) + .collect::>() + .join("\n"); + + insta::assert_snapshot!("external_agent_config_migration_messages", messages); +} diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 3b6cc3452e96..04633a803848 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -130,8 +130,8 @@ mod diff_model; mod diff_render; mod exec_cell; mod exec_command; -#[allow(dead_code)] mod external_agent_config_migration; +mod external_agent_config_migration_flow; mod external_agent_config_migration_model; mod external_editor; mod file_search; diff --git a/codex-rs/tui/src/slash_command.rs b/codex-rs/tui/src/slash_command.rs index 805f266e0afe..710c974a2cd4 100644 --- a/codex-rs/tui/src/slash_command.rs +++ b/codex-rs/tui/src/slash_command.rs @@ -26,6 +26,7 @@ pub enum SlashCommand { AutoReview, Memories, Skills, + Import, Hooks, Review, Rename, @@ -98,6 +99,7 @@ impl SlashCommand { SlashCommand::Diff => "show git diff (including untracked files)", SlashCommand::Mention => "mention a file", SlashCommand::Skills => "use skills to improve how Codex performs specific tasks", + SlashCommand::Import => "import setup, this project, and recent chats from Claude Code", SlashCommand::Hooks => "view and manage lifecycle hooks", SlashCommand::Status => "show current session configuration and token usage", SlashCommand::DebugConfig => "show config layers and requirement sources for debugging", @@ -198,6 +200,7 @@ impl SlashCommand { | SlashCommand::SandboxReadRoot | SlashCommand::Experimental | SlashCommand::Memories + | SlashCommand::Import | SlashCommand::Review | SlashCommand::Plan | SlashCommand::Clear diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap new file mode 100644 index 000000000000..ad3d3ac063b5 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap @@ -0,0 +1,12 @@ +--- +source: tui/src/external_agent_config_migration_flow_tests.rs +expression: messages +--- +Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. +Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. 1 additional item remains. After it finishes, run /import again to review it. +Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. 2 additional items remain. After it finishes, run /import again to review them. +Claude Code import finished. Run /import again to check for additional items. +No Claude Code setup was found to import. +Import from Claude Code is unavailable in remote sessions. Start Codex locally and run /import. +Import from Claude Code is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import. +A previous Claude Code import is still running. Wait for it to finish before importing again. From 15bbbdf663d80d399cdadefd6987bf17999faab6 Mon Sep 17 00:00:00 2001 From: stefanstokic-oai Date: Wed, 10 Jun 2026 15:35:59 -0400 Subject: [PATCH 2/2] tui: make external agent import copy generic --- codex-rs/tui/src/app_event.rs | 2 +- codex-rs/tui/src/app_server_session.rs | 6 +++--- .../tui/src/external_agent_config_migration.rs | 2 +- .../external_agent_config_migration/render.rs | 10 +++++----- .../src/external_agent_config_migration_flow.rs | 13 +++++++------ codex-rs/tui/src/slash_command.rs | 4 +++- ...xternal_agent_config_migration_customize.snap | 6 +++--- ..._agent_config_migration_customize_action.snap | 6 +++--- ...onfig_migration_customize_action_windows.snap | 6 +++--- ...agent_config_migration_customize_windows.snap | 6 +++--- ...__external_agent_config_migration_prompt.snap | 6 +++--- ...al_agent_config_migration_prompt_windows.snap | 6 +++--- ...external_agent_config_migration_messages.snap | 16 ++++++++-------- 13 files changed, 46 insertions(+), 43 deletions(-) diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index 48667b4325da..b282a618cdcd 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -221,7 +221,7 @@ pub(crate) enum AppEvent { /// Open the resume picker inside the running TUI session. OpenResumePicker, - /// Open the Claude Code migration picker inside the running TUI session. + /// Open the external agent migration picker inside the running TUI session. OpenExternalAgentConfigMigration, /// Resume a thread by UUID or thread name inside the running TUI session. diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index 36fde31b809e..6b546c4ee2a5 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -137,7 +137,7 @@ use uuid::Uuid; const JSONRPC_INVALID_REQUEST: i64 = -32600; const JSONRPC_METHOD_NOT_FOUND: i64 = -32601; pub(crate) const EXTERNAL_AGENT_CONFIG_IMPORT_IN_PROGRESS_MESSAGE: &str = - "A previous Claude Code import is still running. Wait for it to finish before importing again."; + "A previous agent import is still running. Wait for it to finish before importing again."; const THREAD_SETTINGS_UPDATE_METHOD: &str = "thread/settings/update"; fn bootstrap_request_error(context: &'static str, err: TypedRequestError) -> color_eyre::Report { @@ -367,7 +367,7 @@ impl AppServerSession { self.client .request_typed(ClientRequest::ExternalAgentConfigDetect { request_id, params }) .await - .wrap_err("externalAgentConfig/detect failed during Claude Code import") + .wrap_err("externalAgentConfig/detect failed during agent import") } pub(crate) async fn external_agent_config_import( @@ -390,7 +390,7 @@ impl AppServerSession { params: ExternalAgentConfigImportParams { migration_items }, }) .await - .wrap_err("externalAgentConfig/import failed during Claude Code import"); + .wrap_err("externalAgentConfig/import failed during agent import"); match response { Ok(_) => Ok(()), Err(err) => { diff --git a/codex-rs/tui/src/external_agent_config_migration.rs b/codex-rs/tui/src/external_agent_config_migration.rs index 9a7e8f329257..d1c4582d6820 100644 --- a/codex-rs/tui/src/external_agent_config_migration.rs +++ b/codex-rs/tui/src/external_agent_config_migration.rs @@ -758,7 +758,7 @@ mod tests { }, ExternalAgentConfigMigrationItem { item_type: ExternalAgentConfigMigrationItemType::Sessions, - description: "Migrate recent Claude Code sessions".to_string(), + description: "Migrate recent chat sessions".to_string(), cwd: None, details: Some(codex_app_server_protocol::MigrationDetails { sessions: vec![SessionMigration { diff --git a/codex-rs/tui/src/external_agent_config_migration/render.rs b/codex-rs/tui/src/external_agent_config_migration/render.rs index e0e4926a9e24..4d518906c48f 100644 --- a/codex-rs/tui/src/external_agent_config_migration/render.rs +++ b/codex-rs/tui/src/external_agent_config_migration/render.rs @@ -80,13 +80,13 @@ impl WidgetRef for &ExternalAgentConfigMigrationScreen { MigrationView::Summary => vec![ Line::from("Bring over your setup, current project, and recent chats."), Line::from("Codex may add files to your current project folder."), - Line::from("Your existing Claude Code setup will not be changed."), - Line::from("Standard Claude Chat data cannot be imported."), + Line::from("Your existing agent setup will not be changed."), + Line::from("Cloud-hosted chat data cannot be imported."), ], MigrationView::Customize => vec![ - Line::from("Choose the Claude Code items to import."), + Line::from("Choose the items to import."), Line::from("Codex may add files to your current project folder."), - Line::from("Your existing Claude Code setup will not be changed."), + Line::from("Your existing agent setup will not be changed."), ], }; let intro_height = intro_lines.len() as u16; @@ -119,7 +119,7 @@ impl WidgetRef for &ExternalAgentConfigMigrationScreen { .areas(inner_area); let title = match self.view { - MigrationView::Summary => "Import from Claude Code", + MigrationView::Summary => "Import from another coding agent", MigrationView::Customize => "Choose what to import", }; let heading = Line::from(vec!["> ".into(), title.bold()]); diff --git a/codex-rs/tui/src/external_agent_config_migration_flow.rs b/codex-rs/tui/src/external_agent_config_migration_flow.rs index e8611d571878..f0fabe3a0156 100644 --- a/codex-rs/tui/src/external_agent_config_migration_flow.rs +++ b/codex-rs/tui/src/external_agent_config_migration_flow.rs @@ -7,11 +7,12 @@ use crate::tui; use codex_app_server_protocol::ExternalAgentConfigDetectParams; pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_FINISHED_MESSAGE: &str = - "Claude Code import finished. Run /import again to check for additional items."; + "Agent import finished. Run /import again to check for additional items."; pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_NO_ITEMS_MESSAGE: &str = - "No Claude Code setup was found to import."; -pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_REMOTE_UNAVAILABLE_MESSAGE: &str = "Import from Claude Code is unavailable in remote sessions. Start Codex locally and run /import."; -pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_DAEMON_UNAVAILABLE_MESSAGE: &str = "Import from Claude Code is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import."; + "No supported agent setup was found to import."; +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_REMOTE_UNAVAILABLE_MESSAGE: &str = + "Agent import is unavailable in remote sessions. Start Codex locally and run /import."; +pub(crate) const EXTERNAL_AGENT_CONFIG_MIGRATION_DAEMON_UNAVAILABLE_MESSAGE: &str = "Agent import is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import."; pub(crate) enum ExternalAgentConfigMigrationFlowOutcome { Started(String), @@ -20,7 +21,7 @@ pub(crate) enum ExternalAgentConfigMigrationFlowOutcome { } fn external_agent_config_migration_success_message(remaining_item_count: usize) -> String { - let message = "Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats."; + let message = "Agent import started. You can keep working while it finishes. Imported setup will apply to new chats."; match remaining_items_handoff(remaining_item_count) { Some(remaining_items_handoff) => format!("{message} {remaining_items_handoff}"), None => message.to_string(), @@ -70,7 +71,7 @@ pub(crate) async fn handle_external_agent_config_migration_prompt( cwd = %cwd.display(), "failed to detect external agent config migrations" ); - return Err(format!("Could not check for Claude Code setup: {err}")); + return Err(format!("Could not check for agent setup: {err}")); } }; diff --git a/codex-rs/tui/src/slash_command.rs b/codex-rs/tui/src/slash_command.rs index 710c974a2cd4..7f79488da3da 100644 --- a/codex-rs/tui/src/slash_command.rs +++ b/codex-rs/tui/src/slash_command.rs @@ -99,7 +99,9 @@ impl SlashCommand { SlashCommand::Diff => "show git diff (including untracked files)", SlashCommand::Mention => "mention a file", SlashCommand::Skills => "use skills to improve how Codex performs specific tasks", - SlashCommand::Import => "import setup, this project, and recent chats from Claude Code", + SlashCommand::Import => { + "import setup, this project, and recent chats from another coding agent" + } SlashCommand::Hooks => "view and manage lifecycle hooks", SlashCommand::Status => "show current session configuration and token usage", SlashCommand::DebugConfig => "show config layers and requirement sources for debugging", diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize.snap index 880933428116..f23691b6f3a4 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize.snap @@ -4,14 +4,14 @@ expression: rendered --- > Choose what to import - Choose the Claude Code items to import. + Choose the items to import. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. + Your existing agent setup will not be changed. Home › [x] Settings (settings.json -> config.toml) Import /Users/alex/.claude/settings.json into /Users/alex/.codex/conf… [x] Recent chat sessions - Import recent Claude Code sessions + Import recent chat sessions 1 chat session: Investigate migration UX Current project: /workspace/project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action.snap index 70132d75ae87..70df423cee54 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action.snap @@ -4,14 +4,14 @@ expression: rendered --- > Choose what to import - Choose the Claude Code items to import. + Choose the items to import. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. + Your existing agent setup will not be changed. Home [x] Settings (settings.json -> config.toml) Import /Users/alex/.claude/settings.json into /Users/alex/.codex/conf… [x] Recent chat sessions - Import recent Claude Code sessions + Import recent chat sessions 1 chat session: Investigate migration UX Current project: /workspace/project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action_windows.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action_windows.snap index 48dfe2e22b04..e6e35384aec7 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action_windows.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_action_windows.snap @@ -4,14 +4,14 @@ expression: rendered --- > Choose what to import - Choose the Claude Code items to import. + Choose the items to import. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. + Your existing agent setup will not be changed. Home [x] Settings (settings.json -> config.toml) Import /Users/alex/.claude/settings.json into /Users/alex/.codex/conf… [x] Recent chat sessions - Import recent Claude Code sessions + Import recent chat sessions 1 chat session: Investigate migration UX Current project: C:\workspace\project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_windows.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_windows.snap index db6c14a0ca3e..12dff7a4e576 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_windows.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_customize_windows.snap @@ -4,14 +4,14 @@ expression: rendered --- > Choose what to import - Choose the Claude Code items to import. + Choose the items to import. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. + Your existing agent setup will not be changed. Home › [x] Settings (settings.json -> config.toml) Import /Users/alex/.claude/settings.json into /Users/alex/.codex/conf… [x] Recent chat sessions - Import recent Claude Code sessions + Import recent chat sessions 1 chat session: Investigate migration UX Current project: C:\workspace\project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt.snap index d7274eefc2bc..64a5f0b1b088 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt.snap @@ -3,11 +3,11 @@ source: tui/src/external_agent_config_migration.rs expression: rendered --- - > Import from Claude Code + > Import from another coding agent Bring over your setup, current project, and recent chats. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. - Standard Claude Chat data cannot be imported. + Your existing agent setup will not be changed. + Cloud-hosted chat data cannot be imported. [x] Tools & setup Settings, instructions, integrations, agents, commands, and skills [x] Current project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt_windows.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt_windows.snap index d7274eefc2bc..64a5f0b1b088 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt_windows.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration__tests__external_agent_config_migration_prompt_windows.snap @@ -3,11 +3,11 @@ source: tui/src/external_agent_config_migration.rs expression: rendered --- - > Import from Claude Code + > Import from another coding agent Bring over your setup, current project, and recent chats. Codex may add files to your current project folder. - Your existing Claude Code setup will not be changed. - Standard Claude Chat data cannot be imported. + Your existing agent setup will not be changed. + Cloud-hosted chat data cannot be imported. [x] Tools & setup Settings, instructions, integrations, agents, commands, and skills [x] Current project diff --git a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap index ad3d3ac063b5..5e80f8ac297c 100644 --- a/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap +++ b/codex-rs/tui/src/snapshots/codex_tui__external_agent_config_migration_flow__tests__external_agent_config_migration_messages.snap @@ -2,11 +2,11 @@ source: tui/src/external_agent_config_migration_flow_tests.rs expression: messages --- -Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. -Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. 1 additional item remains. After it finishes, run /import again to review it. -Claude Code import started. You can keep working while it finishes. Imported setup will apply to new chats. 2 additional items remain. After it finishes, run /import again to review them. -Claude Code import finished. Run /import again to check for additional items. -No Claude Code setup was found to import. -Import from Claude Code is unavailable in remote sessions. Start Codex locally and run /import. -Import from Claude Code is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import. -A previous Claude Code import is still running. Wait for it to finish before importing again. +Agent import started. You can keep working while it finishes. Imported setup will apply to new chats. +Agent import started. You can keep working while it finishes. Imported setup will apply to new chats. 1 additional item remains. After it finishes, run /import again to review it. +Agent import started. You can keep working while it finishes. Imported setup will apply to new chats. 2 additional items remain. After it finishes, run /import again to review them. +Agent import finished. Run /import again to check for additional items. +No supported agent setup was found to import. +Agent import is unavailable in remote sessions. Start Codex locally and run /import. +Agent import is unavailable while Codex is connected to the local app-server daemon. Stop the daemon, restart Codex, and run /import. +A previous agent import is still running. Wait for it to finish before importing again.