From 368b39215265c70d26d2ce16f3c03065f304cdf2 Mon Sep 17 00:00:00 2001 From: Dhanji Prasanna Date: Sat, 6 Sep 2025 14:15:31 +1000 Subject: [PATCH] Scrollback --- Cargo.lock | 137 ++++++++++++++++++++++++++++++++++++++- crates/g3-cli/Cargo.toml | 2 + crates/g3-cli/src/lib.rs | 77 ++++++++++++++++------ 3 files changed, 192 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a684345..edaba94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clang-sys" version = "1.8.1" @@ -281,6 +287,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -405,13 +420,34 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", ] [[package]] @@ -422,7 +458,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.61.0", ] @@ -461,6 +497,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "equivalent" version = "1.0.2" @@ -477,12 +519,29 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix 1.0.8", + "windows-sys 0.59.0", +] + [[package]] name = "find-msvc-tools" version = "0.1.0" @@ -623,8 +682,10 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "dirs 5.0.1", "g3-config", "g3-core", + "rustyline", "serde", "serde_json", "tokio", @@ -1222,6 +1283,27 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1472,6 +1554,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -1481,6 +1573,17 @@ dependencies = [ "bitflags 2.9.4", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -1647,6 +1750,28 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rustyline" +version = "17.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6614df0b6d4cfb20d1d5e295332921793ce499af3ebc011bf1e393380e1e492" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "windows-sys 0.60.2", +] + [[package]] name = "ryu" version = "1.0.20" @@ -1776,7 +1901,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ - "dirs", + "dirs 6.0.0", ] [[package]] @@ -2167,6 +2292,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + [[package]] name = "url" version = "2.5.7" diff --git a/crates/g3-cli/Cargo.toml b/crates/g3-cli/Cargo.toml index a2af92b..78a9ac5 100644 --- a/crates/g3-cli/Cargo.toml +++ b/crates/g3-cli/Cargo.toml @@ -14,3 +14,5 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +rustyline = "17.0.1" +dirs = "5.0" diff --git a/crates/g3-cli/src/lib.rs b/crates/g3-cli/src/lib.rs index 9f070f2..2092014 100644 --- a/crates/g3-cli/src/lib.rs +++ b/crates/g3-cli/src/lib.rs @@ -3,6 +3,8 @@ use g3_core::Agent; use g3_config::Config; use anyhow::Result; use tracing::{info, error}; +use rustyline::error::ReadlineError; +use rustyline::DefaultEditor; #[derive(Parser)] #[command(name = "g3")] @@ -70,33 +72,66 @@ async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Re println!("🤖 G3 AI Coding Agent - Interactive Mode"); println!("I solve problems by writing and executing code. Tell me what you need to accomplish!"); println!(); - println!("Type 'exit' or 'quit' to exit"); + println!("Type 'exit' or 'quit' to exit, use Up/Down arrows for command history"); println!(); + // Initialize rustyline editor with history + let mut rl = DefaultEditor::new()?; + + // Try to load history from a file in the user's home directory + let history_file = dirs::home_dir() + .map(|mut path| { + path.push(".g3_history"); + path + }); + + if let Some(ref history_path) = history_file { + let _ = rl.load_history(history_path); + } + loop { - print!("g3> "); - use std::io::{self, Write}; - io::stdout().flush()?; - - let mut input = String::new(); - io::stdin().read_line(&mut input)?; - - let input = input.trim(); - if input == "exit" || input == "quit" { - break; - } - - if input.is_empty() { - continue; - } - - // Execute task (code-first approach) - match agent.execute_task_with_timing(input, None, false, show_prompt, show_code, true).await { - Ok(response) => println!("{}", response), - Err(e) => error!("Error: {}", e), + let readline = rl.readline("g3> "); + match readline { + Ok(line) => { + let input = line.trim(); + + if input == "exit" || input == "quit" { + break; + } + + if input.is_empty() { + continue; + } + + // Add to history + rl.add_history_entry(input)?; + + // Execute task (code-first approach) + match agent.execute_task_with_timing(input, None, false, show_prompt, show_code, true).await { + Ok(response) => println!("{}", response), + Err(e) => error!("Error: {}", e), + } + }, + Err(ReadlineError::Interrupted) => { + println!("CTRL-C"); + continue; + }, + Err(ReadlineError::Eof) => { + println!("CTRL-D"); + break; + }, + Err(err) => { + error!("Error: {:?}", err); + break; + } } } + // Save history before exiting + if let Some(ref history_path) = history_file { + let _ = rl.save_history(history_path); + } + println!("👋 Goodbye!"); Ok(()) }