Class: Ralph::CLI
- Inherits:
-
Object
- Object
- Ralph::CLI
- Defined in:
- lib/ralph/cli.rb
Instance Method Summary collapse
-
#initialize(argv = ARGV) ⇒ CLI
constructor
A new instance of CLI.
- #run(argv = ARGV.dup) ⇒ Object
Constructor Details
#initialize(argv = ARGV) ⇒ CLI
Returns a new instance of CLI.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/ralph/cli.rb', line 5 def initialize(argv = ARGV) @config = Config.new @parser = OptionParser.new do |o| o. = <<~BANNER Ralph Wiggum Loop - Iterative AI development with AI agents Usage: ralph "<prompt>" [options] Commands: --status Show current Ralph loop status and history --add-context TEXT Add context for the next iteration --clear-context Clear any pending context --list-tasks Display the current task list with indices --add-task "desc" Add a new task to the list --remove-task N Remove task at index N (including subtasks) Options: BANNER o.on("--agent AGENT", Agents.valid_agent_names, "AI agent: #{Agents.valid_agent_names.join(', ')} (default: opencode)") do |v| @config.chosen_agent = v end o.on("--min-iterations N", Integer, "Minimum iterations before completion (default: 1)") do |v| @config.min_iterations = v end o.on("--max-iterations N", Integer, "Maximum iterations before stopping (default: unlimited)") do |v| @config.max_iterations = v end o.on("--completion-promise TEXT", "Phrase that signals completion (default: COMPLETE)") do |v| @config.completion_promise = v end o.on("-t", "--tasks", "Enable Tasks Mode for structured task tracking") do @config.tasks_mode = true end o.on("--task-promise TEXT", "Phrase that signals task completion (default: READY_FOR_NEXT_TASK)") do |v| @config.task_promise = v end o.on("--model MODEL", "Model to use (agent-specific)") do |v| @config.model = v end o.on("--[no-]stream", "Stream agent output in real-time (default: on)") do |v| @config.stream_output = v end o.on("--verbose-tools", "Print every tool line (disable compact summary)") do @config.verbose_tools = true end o.on("--no-plugins", "Disable non-auth OpenCode plugins (opencode only)") do @config.disable_plugins = true end o.on("--[no-]allow-all", "Auto-approve all tool permissions (default: on)") do |v| @config. = v end # Subcommands -- these set a command to dispatch after parsing o.on("-v", "--version", "Show version") do puts "ralph #{VERSION}" exit 0 end o.on("--status", "Show current loop status and history") do Output::Status.call(options: @config.to_h) exit 0 end o.on("--add-context TEXT", "Add context for the next iteration") do |context_text| Storage::Context.new.append( "\n## Context added at #{Time.now.utc.iso8601}\n#{context_text}\n" ) puts "✅ Context added for next iteration" puts " File: #{Storage::Context.new.path}" Storage::State.load.then do |state| if state&.active puts " Will be picked up in iteration #{state.iteration + 1}" else puts " Will be used when loop starts" end end exit 0 end o.on("--clear-context", "Clear any pending context") do Storage::Context.new.then do |context| if context.present? context.clear puts "✅ Context cleared" else puts "ℹ️ No pending context to clear" end end exit 0 end o.on("--list-tasks", "Display the current task list") do begin Storage::Tasks.new.load_tasks.then do |tasks| if tasks tasks.display_with_indices else puts "No tasks file found. Use --add-task to create your first task." end end rescue StandardError => e $stderr.puts "Error reading tasks file: #{e}" exit 1 end exit 0 end o.on("--add-task DESC", "Add a new task to the list") do |description| begin Storage::Tasks.new.add_task(description) puts "✅ Task added: \"#{description}\"" rescue StandardError => e $stderr.puts "Error adding task: #{e}" exit 1 end exit 0 end o.on("--remove-task N", Integer, "Remove task at index N") do |task_index| begin Storage::Tasks.new.remove_task(task_index) puts "✅ Removed task #{task_index} and its subtasks" rescue IndexError => e $stderr.puts "Error: #{e.}" exit 1 rescue RuntimeError => e $stderr.puts "Error: #{e.}" exit 1 rescue StandardError => e $stderr.puts "Error removing task: #{e}" exit 1 end exit 0 end o.separator "" o.separator "Examples:" o.separator ' ralph "Build a REST API for todos"' o.separator ' ralph "Fix the auth bug" --max-iterations 10' o.separator ' ralph "Add tests" --completion-promise "ALL TESTS PASS" --model openai/gpt-5.1' o.separator ' ralph --status' o.separator ' ralph --add-context "Focus on the auth module first"' o.separator "" o.separator "How it works:" o.separator " 1. Sends your prompt to the selected AI agent" o.separator " 2. AI agent works on the task" o.separator " 3. Checks output for completion promise" o.separator " 4. If not complete, repeats with same prompt" o.separator " 5. AI sees its previous work in files" o.separator " 6. Continues until promise detected or max iterations" o.separator "" o.separator "To stop manually: Ctrl+C" o.separator "Learn more: https://ghuntley.com/ralph/" end end |
Instance Method Details
#run(argv = ARGV.dup) ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/ralph/cli.rb', line 183 def run(argv = ARGV.dup) @user_prompt = @parser.parse(argv).then do |remaining_args| if $stdin.tty? remaining_args.join(" ").strip else [$stdin.read, remaining_args.join(" ")].join("\n").strip end end if @user_prompt.empty? abort " Error: No prompt provided Usage: ralph 'Your task description' [options] Run 'ralph --help' for more information " end tasks = Storage::Tasks.new context = Storage::Context.new PromptTemplate.inject(@user_prompt, context: context, tasks: tasks).then do |prompt| @config.prompt = prompt if @config.max_iterations > 0 && @config.min_iterations > @config.max_iterations abort "Error: --min-iterations (#{@config.min_iterations}) cannot be greater than --max-iterations (#{@config.max_iterations})" end state = Storage::State.from_config(@config, prompt: prompt) history = Storage::History.new Ralph::Loop.new(@config, state, history, context, tasks).run end rescue StandardError => e $stderr.puts "Fatal error: #{e}" Storage::State.clear exit 1 end |