From cca1122ddc0054488d16ba1ecfd31ef05c109c86 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 28 Apr 2025 13:46:22 -0700 Subject: [PATCH] fix: make the TUI the default/"interactive" CLI in Rust (#711) Originally, the `interactive` crate was going to be a placeholder for building out a UX that was comparable to that of the existing TypeScript CLI. Though after researching how Ratatui works, that seems difficult to do because it is designed around the idea that it will redraw the full screen buffer each time (and so any scrolling should be "internal" to your Ratatui app) whereas the TypeScript CLI expects to render the full history of the conversation every time(*) (which is why you can use your terminal scrollbar to scroll it). While it is possible to use Ratatui in a way that acts more like what the TypeScript CLI is doing, it is awkward and seemingly results in tedious code, so I think we should abandon that approach. As such, this PR deletes the `interactive/` folder and the code that depended on it. Further, since we added support for mousewheel scrolling in the TUI in https://github.com/openai/codex/pull/641, it certainly feels much better and the need for scroll support via the terminal scrollbar is greatly diminished. This is now a more appropriate default UX for the "multitool" CLI. (*) Incidentally, I haven't verified this, but I think this results in O(N^2) work in rendering, which seems potentially problematic for long conversations. --- codex-rs/Cargo.lock | 11 ----------- codex-rs/Cargo.toml | 1 - codex-rs/README.md | 1 - codex-rs/cli/Cargo.toml | 1 - codex-rs/cli/src/main.rs | 12 ++---------- codex-rs/interactive/Cargo.toml | 24 ----------------------- codex-rs/interactive/src/cli.rs | 33 -------------------------------- codex-rs/interactive/src/lib.rs | 7 ------- codex-rs/interactive/src/main.rs | 11 ----------- 9 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 codex-rs/interactive/Cargo.toml delete mode 100644 codex-rs/interactive/src/cli.rs delete mode 100644 codex-rs/interactive/src/lib.rs delete mode 100644 codex-rs/interactive/src/main.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index f866ed6b..ef98511f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -475,7 +475,6 @@ dependencies = [ "clap", "codex-core", "codex-exec", - "codex-interactive", "codex-repl", "codex-tui", "serde_json", @@ -554,16 +553,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "codex-interactive" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "codex-core", - "tokio", -] - [[package]] name = "codex-repl" version = "0.1.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 69c4e8a8..1335d58f 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -7,7 +7,6 @@ members = [ "core", "exec", "execpolicy", - "interactive", "repl", "tui", ] diff --git a/codex-rs/README.md b/codex-rs/README.md index 309ef033..c01323e5 100644 --- a/codex-rs/README.md +++ b/codex-rs/README.md @@ -17,7 +17,6 @@ Currently, the Rust implementation is materially behind the TypeScript implement This folder is the root of a Cargo workspace. It contains quite a bit of experimental code, but here are the key crates: - [`core/`](./core) contains the business logic for Codex. Ultimately, we hope this to be a library crate that is generally useful for building other Rust/native applications that use Codex. -- [`interactive/`](./interactive) CLI with a UX comparable to the TypeScript Codex CLI. - [`exec/`](./exec) "headless" CLI for use in automation. - [`tui/`](./tui) CLI that launches a fullscreen TUI built with [Ratatui](https://ratatui.rs/). - [`repl/`](./repl) CLI that launches a lightweight REPL similar to the Python or Node.js REPL. diff --git a/codex-rs/cli/Cargo.toml b/codex-rs/cli/Cargo.toml index 12dab8c0..3dc13e23 100644 --- a/codex-rs/cli/Cargo.toml +++ b/codex-rs/cli/Cargo.toml @@ -12,7 +12,6 @@ anyhow = "1" clap = { version = "4", features = ["derive"] } codex-core = { path = "../core" } codex-exec = { path = "../exec" } -codex-interactive = { path = "../interactive" } codex-repl = { path = "../repl" } codex-tui = { path = "../tui" } serde_json = "1" diff --git a/codex-rs/cli/src/main.rs b/codex-rs/cli/src/main.rs index d79f0f33..7d8987c0 100644 --- a/codex-rs/cli/src/main.rs +++ b/codex-rs/cli/src/main.rs @@ -7,7 +7,6 @@ use clap::ArgAction; use clap::Parser; use codex_core::SandboxModeCliArg; use codex_exec::Cli as ExecCli; -use codex_interactive::Cli as InteractiveCli; use codex_repl::Cli as ReplCli; use codex_tui::Cli as TuiCli; @@ -25,7 +24,7 @@ use crate::proto::ProtoCli; )] struct MultitoolCli { #[clap(flatten)] - interactive: InteractiveCli, + interactive: TuiCli, #[clap(subcommand)] subcommand: Option, @@ -37,10 +36,6 @@ enum Subcommand { #[clap(visible_alias = "e")] Exec(ExecCli), - /// Run the TUI. - #[clap(visible_alias = "t")] - Tui(TuiCli), - /// Run the REPL. #[clap(visible_alias = "r")] Repl(ReplCli), @@ -89,14 +84,11 @@ async fn main() -> anyhow::Result<()> { match cli.subcommand { None => { - codex_interactive::run_main(cli.interactive).await?; + codex_tui::run_main(cli.interactive)?; } Some(Subcommand::Exec(exec_cli)) => { codex_exec::run_main(exec_cli).await?; } - Some(Subcommand::Tui(tui_cli)) => { - codex_tui::run_main(tui_cli)?; - } Some(Subcommand::Repl(repl_cli)) => { codex_repl::run_main(repl_cli).await?; } diff --git a/codex-rs/interactive/Cargo.toml b/codex-rs/interactive/Cargo.toml deleted file mode 100644 index b2a7234e..00000000 --- a/codex-rs/interactive/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "codex-interactive" -version = "0.1.0" -edition = "2021" - -[[bin]] -name = "codex-interactive" -path = "src/main.rs" - -[lib] -name = "codex_interactive" -path = "src/lib.rs" - -[dependencies] -anyhow = "1" -clap = { version = "4", features = ["derive"] } -codex-core = { path = "../core", features = ["cli"] } -tokio = { version = "1", features = [ - "io-std", - "macros", - "process", - "rt-multi-thread", - "signal", -] } diff --git a/codex-rs/interactive/src/cli.rs b/codex-rs/interactive/src/cli.rs deleted file mode 100644 index 6d35a49a..00000000 --- a/codex-rs/interactive/src/cli.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::Parser; -use codex_core::ApprovalModeCliArg; -use codex_core::SandboxModeCliArg; -use std::path::PathBuf; - -#[derive(Parser, Debug)] -#[command(version)] -pub struct Cli { - /// Optional image(s) to attach to the initial prompt. - #[arg(long = "image", short = 'i', value_name = "FILE", value_delimiter = ',', num_args = 1..)] - pub images: Vec, - - /// Model the agent should use. - #[arg(long, short = 'm')] - pub model: Option, - - /// Configure when the model requires human approval before executing a command. - #[arg(long = "ask-for-approval", short = 'a', value_enum, default_value_t = ApprovalModeCliArg::OnFailure)] - pub approval_policy: ApprovalModeCliArg, - - /// Configure the process restrictions when a command is executed. - /// - /// Uses OS-specific sandboxing tools; Seatbelt on OSX, landlock+seccomp on Linux. - #[arg(long = "sandbox", short = 's')] - pub sandbox_policy: Option, - - /// Allow running Codex outside a Git repository. - #[arg(long = "skip-git-repo-check", default_value_t = false)] - pub skip_git_repo_check: bool, - - /// Initial instructions for the agent. - pub prompt: Option, -} diff --git a/codex-rs/interactive/src/lib.rs b/codex-rs/interactive/src/lib.rs deleted file mode 100644 index a36a0ee2..00000000 --- a/codex-rs/interactive/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod cli; -pub use cli::Cli; - -pub async fn run_main(_cli: Cli) -> anyhow::Result<()> { - eprintln!("Interactive mode is not implemented yet."); - std::process::exit(1); -} diff --git a/codex-rs/interactive/src/main.rs b/codex-rs/interactive/src/main.rs deleted file mode 100644 index 20f3fb1d..00000000 --- a/codex-rs/interactive/src/main.rs +++ /dev/null @@ -1,11 +0,0 @@ -use clap::Parser; -use codex_interactive::run_main; -use codex_interactive::Cli; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let cli = Cli::parse(); - run_main(cli).await?; - - Ok(()) -}