An AI agent built to do Ralph loops - plan mode for planning and ralph mode for implementing.

feat: add CLI structure with init, plan, and run subcommands

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+309 -2
+229
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "aho-corasick" 7 + version = "1.1.4" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 10 + dependencies = [ 11 + "memchr", 12 + ] 13 + 14 + [[package]] 15 + name = "anstream" 16 + version = "0.6.21" 17 + source = "registry+https://github.com/rust-lang/crates.io-index" 18 + checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 19 + dependencies = [ 20 + "anstyle", 21 + "anstyle-parse", 22 + "anstyle-query", 23 + "anstyle-wincon", 24 + "colorchoice", 25 + "is_terminal_polyfill", 26 + "utf8parse", 27 + ] 28 + 29 + [[package]] 30 + name = "anstyle" 31 + version = "1.0.13" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 34 + 35 + [[package]] 36 + name = "anstyle-parse" 37 + version = "0.2.7" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 40 + dependencies = [ 41 + "utf8parse", 42 + ] 43 + 44 + [[package]] 45 + name = "anstyle-query" 46 + version = "1.1.5" 47 + source = "registry+https://github.com/rust-lang/crates.io-index" 48 + checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" 49 + dependencies = [ 50 + "windows-sys 0.61.2", 51 + ] 52 + 53 + [[package]] 54 + name = "anstyle-wincon" 55 + version = "3.0.11" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" 58 + dependencies = [ 59 + "anstyle", 60 + "once_cell_polyfill", 61 + "windows-sys 0.61.2", 62 + ] 63 + 64 + [[package]] 6 65 name = "anyhow" 7 66 version = "1.0.100" 8 67 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 66 125 checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 67 126 68 127 [[package]] 128 + name = "clap" 129 + version = "4.5.54" 130 + source = "registry+https://github.com/rust-lang/crates.io-index" 131 + checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" 132 + dependencies = [ 133 + "clap_builder", 134 + "clap_derive", 135 + ] 136 + 137 + [[package]] 138 + name = "clap_builder" 139 + version = "4.5.54" 140 + source = "registry+https://github.com/rust-lang/crates.io-index" 141 + checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" 142 + dependencies = [ 143 + "anstream", 144 + "anstyle", 145 + "clap_lex", 146 + "strsim", 147 + ] 148 + 149 + [[package]] 150 + name = "clap_derive" 151 + version = "4.5.49" 152 + source = "registry+https://github.com/rust-lang/crates.io-index" 153 + checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 154 + dependencies = [ 155 + "heck", 156 + "proc-macro2", 157 + "quote", 158 + "syn", 159 + ] 160 + 161 + [[package]] 162 + name = "clap_lex" 163 + version = "0.7.7" 164 + source = "registry+https://github.com/rust-lang/crates.io-index" 165 + checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" 166 + 167 + [[package]] 168 + name = "colorchoice" 169 + version = "1.0.4" 170 + source = "registry+https://github.com/rust-lang/crates.io-index" 171 + checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 172 + 173 + [[package]] 69 174 name = "core-foundation" 70 175 version = "0.9.4" 71 176 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 102 207 ] 103 208 104 209 [[package]] 210 + name = "env_filter" 211 + version = "0.1.4" 212 + source = "registry+https://github.com/rust-lang/crates.io-index" 213 + checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" 214 + dependencies = [ 215 + "log", 216 + "regex", 217 + ] 218 + 219 + [[package]] 220 + name = "env_logger" 221 + version = "0.11.8" 222 + source = "registry+https://github.com/rust-lang/crates.io-index" 223 + checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" 224 + dependencies = [ 225 + "anstream", 226 + "anstyle", 227 + "env_filter", 228 + "jiff", 229 + "log", 230 + ] 231 + 232 + [[package]] 105 233 name = "equivalent" 106 234 version = "1.0.2" 107 235 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 246 374 version = "0.16.1" 247 375 source = "registry+https://github.com/rust-lang/crates.io-index" 248 376 checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 377 + 378 + [[package]] 379 + name = "heck" 380 + version = "0.5.0" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 249 383 250 384 [[package]] 251 385 name = "http" ··· 495 629 ] 496 630 497 631 [[package]] 632 + name = "is_terminal_polyfill" 633 + version = "1.70.2" 634 + source = "registry+https://github.com/rust-lang/crates.io-index" 635 + checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 636 + 637 + [[package]] 498 638 name = "itoa" 499 639 version = "1.0.17" 500 640 source = "registry+https://github.com/rust-lang/crates.io-index" 501 641 checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 502 642 503 643 [[package]] 644 + name = "jiff" 645 + version = "0.2.18" 646 + source = "registry+https://github.com/rust-lang/crates.io-index" 647 + checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50" 648 + dependencies = [ 649 + "jiff-static", 650 + "log", 651 + "portable-atomic", 652 + "portable-atomic-util", 653 + "serde_core", 654 + ] 655 + 656 + [[package]] 657 + name = "jiff-static" 658 + version = "0.2.18" 659 + source = "registry+https://github.com/rust-lang/crates.io-index" 660 + checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78" 661 + dependencies = [ 662 + "proc-macro2", 663 + "quote", 664 + "syn", 665 + ] 666 + 667 + [[package]] 504 668 name = "js-sys" 505 669 version = "0.3.85" 506 670 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 590 754 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 591 755 592 756 [[package]] 757 + name = "once_cell_polyfill" 758 + version = "1.70.2" 759 + source = "registry+https://github.com/rust-lang/crates.io-index" 760 + checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 761 + 762 + [[package]] 593 763 name = "openssl" 594 764 version = "0.10.75" 595 765 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 681 851 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 682 852 683 853 [[package]] 854 + name = "portable-atomic" 855 + version = "1.13.0" 856 + source = "registry+https://github.com/rust-lang/crates.io-index" 857 + checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" 858 + 859 + [[package]] 860 + name = "portable-atomic-util" 861 + version = "0.2.4" 862 + source = "registry+https://github.com/rust-lang/crates.io-index" 863 + checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 864 + dependencies = [ 865 + "portable-atomic", 866 + ] 867 + 868 + [[package]] 684 869 name = "potential_utf" 685 870 version = "0.1.4" 686 871 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 723 908 ] 724 909 725 910 [[package]] 911 + name = "regex" 912 + version = "1.12.2" 913 + source = "registry+https://github.com/rust-lang/crates.io-index" 914 + checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" 915 + dependencies = [ 916 + "aho-corasick", 917 + "memchr", 918 + "regex-automata", 919 + "regex-syntax", 920 + ] 921 + 922 + [[package]] 923 + name = "regex-automata" 924 + version = "0.4.13" 925 + source = "registry+https://github.com/rust-lang/crates.io-index" 926 + checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" 927 + dependencies = [ 928 + "aho-corasick", 929 + "memchr", 930 + "regex-syntax", 931 + ] 932 + 933 + [[package]] 934 + name = "regex-syntax" 935 + version = "0.8.8" 936 + source = "registry+https://github.com/rust-lang/crates.io-index" 937 + checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" 938 + 939 + [[package]] 726 940 name = "reqwest" 727 941 version = "0.12.28" 728 942 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 782 996 dependencies = [ 783 997 "anyhow", 784 998 "async-trait", 999 + "clap", 1000 + "env_logger", 1001 + "log", 785 1002 "reqwest", 786 1003 "serde", 787 1004 "serde_json", ··· 993 1210 version = "1.2.1" 994 1211 source = "registry+https://github.com/rust-lang/crates.io-index" 995 1212 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 1213 + 1214 + [[package]] 1215 + name = "strsim" 1216 + version = "0.11.1" 1217 + source = "registry+https://github.com/rust-lang/crates.io-index" 1218 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 996 1219 997 1220 [[package]] 998 1221 name = "subtle" ··· 1276 1499 version = "1.0.4" 1277 1500 source = "registry+https://github.com/rust-lang/crates.io-index" 1278 1501 checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1502 + 1503 + [[package]] 1504 + name = "utf8parse" 1505 + version = "0.2.2" 1506 + source = "registry+https://github.com/rust-lang/crates.io-index" 1507 + checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1279 1508 1280 1509 [[package]] 1281 1510 name = "vcpkg"
+3
Cargo.toml
··· 11 11 anyhow = "1.0" 12 12 async-trait = "0.1" 13 13 serde_json = "1.0" 14 + clap = { version = "4.5", features = ["derive"] } 15 + log = "0.4" 16 + env_logger = "0.11" 14 17 15 18 [dev-dependencies] 16 19 tempfile = "3.15"
+62 -2
src/main.rs
··· 1 1 pub mod config; 2 2 pub mod llm; 3 + pub mod spec; 3 4 pub mod tools; 4 5 5 - fn main() { 6 - println!("Hello, world!"); 6 + use clap::{Parser, Subcommand}; 7 + 8 + #[derive(Parser)] 9 + #[command(name = "rustagent")] 10 + #[command(about = "A Rust-based AI agent for task execution", long_about = None)] 11 + struct Cli { 12 + #[command(subcommand)] 13 + command: Commands, 14 + } 15 + 16 + #[derive(Subcommand)] 17 + enum Commands { 18 + /// Initialize a new agent specification 19 + Init { 20 + /// Directory to store the specification 21 + #[arg(long)] 22 + spec_dir: Option<String>, 23 + }, 24 + /// Create an execution plan from a specification 25 + Plan { 26 + /// Directory containing the specification 27 + #[arg(long)] 28 + spec_dir: Option<String>, 29 + }, 30 + /// Execute a plan 31 + Run { 32 + /// Path to the specification file 33 + spec_file: String, 34 + /// Maximum number of iterations 35 + #[arg(long)] 36 + max_iterations: Option<usize>, 37 + }, 38 + } 39 + 40 + #[tokio::main] 41 + async fn main() -> anyhow::Result<()> { 42 + env_logger::init(); 43 + 44 + let cli = Cli::parse(); 45 + 46 + match &cli.command { 47 + Commands::Init { spec_dir } => { 48 + let dir = spec_dir.clone().unwrap_or_else(|| ".".to_string()); 49 + println!("Initializing agent in directory: {}", dir); 50 + // TODO: Implement initialization logic 51 + } 52 + Commands::Plan { spec_dir } => { 53 + let dir = spec_dir.clone().unwrap_or_else(|| ".".to_string()); 54 + println!("Creating plan from spec directory: {}", dir); 55 + // TODO: Implement plan creation logic 56 + } 57 + Commands::Run { spec_file, max_iterations } => { 58 + println!("Running agent with spec: {}", spec_file); 59 + if let Some(max_iter) = max_iterations { 60 + println!("Maximum iterations: {}", max_iter); 61 + } 62 + // TODO: Implement execution logic 63 + } 64 + } 65 + 66 + Ok(()) 7 67 }
+15
tests/cli_test.rs
··· 1 + use std::process::Command; 2 + 3 + #[test] 4 + fn test_cli_help() { 5 + let output = Command::new("cargo") 6 + .args(["run", "--", "--help"]) 7 + .output() 8 + .unwrap(); 9 + 10 + let stdout = String::from_utf8_lossy(&output.stdout); 11 + assert!(stdout.contains("rustagent")); 12 + assert!(stdout.contains("init")); 13 + assert!(stdout.contains("plan")); 14 + assert!(stdout.contains("run")); 15 + }