rubycode 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +38 -0
  3. data/.rubocop.yml +4 -0
  4. data/CHANGELOG.md +52 -0
  5. data/README.md +101 -23
  6. data/USAGE.md +1 -23
  7. data/config/locales/en.yml +208 -1
  8. data/config/system_prompt.md +6 -1
  9. data/config/tools/bash.json +1 -1
  10. data/config/tools/fetch.json +22 -0
  11. data/config/tools/websearch.json +22 -0
  12. data/docs/images/demo.png +0 -0
  13. data/lib/rubycode/adapters/base.rb +92 -2
  14. data/lib/rubycode/adapters/concerns/debugging.rb +32 -0
  15. data/lib/rubycode/adapters/concerns/error_handling.rb +89 -0
  16. data/lib/rubycode/adapters/concerns/http_client.rb +67 -0
  17. data/lib/rubycode/adapters/deepseek.rb +97 -0
  18. data/lib/rubycode/adapters/gemini.rb +133 -0
  19. data/lib/rubycode/adapters/ollama.rb +114 -82
  20. data/lib/rubycode/adapters/openai.rb +97 -0
  21. data/lib/rubycode/adapters/openrouter.rb +102 -0
  22. data/lib/rubycode/agent_loop.rb +110 -18
  23. data/lib/rubycode/client/approval_handler.rb +14 -0
  24. data/lib/rubycode/client/display_formatter.rb +18 -10
  25. data/lib/rubycode/client/response_handler.rb +4 -23
  26. data/lib/rubycode/client.rb +9 -0
  27. data/lib/rubycode/config_manager.rb +81 -0
  28. data/lib/rubycode/configuration.rb +21 -10
  29. data/lib/rubycode/database.rb +19 -0
  30. data/lib/rubycode/errors.rb +12 -0
  31. data/lib/rubycode/models/api_key.rb +118 -0
  32. data/lib/rubycode/models/memory.rb +84 -10
  33. data/lib/rubycode/models.rb +1 -0
  34. data/lib/rubycode/pricing.rb +59 -0
  35. data/lib/rubycode/search_providers/base.rb +66 -0
  36. data/lib/rubycode/search_providers/brave_search.rb +60 -0
  37. data/lib/rubycode/search_providers/concerns/debugging.rb +37 -0
  38. data/lib/rubycode/search_providers/concerns/error_handling.rb +64 -0
  39. data/lib/rubycode/search_providers/concerns/http_client.rb +67 -0
  40. data/lib/rubycode/search_providers/duckduckgo_instant.rb +98 -0
  41. data/lib/rubycode/search_providers/exa_ai.rb +171 -0
  42. data/lib/rubycode/search_providers/multi_provider.rb +47 -0
  43. data/lib/rubycode/token_counter.rb +41 -0
  44. data/lib/rubycode/tools/bash.rb +38 -8
  45. data/lib/rubycode/tools/fetch.rb +120 -0
  46. data/lib/rubycode/tools/web_search.rb +122 -0
  47. data/lib/rubycode/tools.rb +5 -1
  48. data/lib/rubycode/value_objects.rb +8 -4
  49. data/lib/rubycode/version.rb +1 -1
  50. data/lib/rubycode/views/adapter/debug_delay.rb +20 -0
  51. data/lib/rubycode/views/adapter/debug_request.rb +33 -0
  52. data/lib/rubycode/views/adapter/debug_response.rb +31 -0
  53. data/lib/rubycode/views/agent_loop/token_summary.rb +54 -0
  54. data/lib/rubycode/views/agent_loop.rb +2 -0
  55. data/lib/rubycode/views/cli/api_key_missing.rb +37 -0
  56. data/lib/rubycode/views/cli/config_saved.rb +17 -0
  57. data/lib/rubycode/views/cli/configuration_table.rb +3 -3
  58. data/lib/rubycode/views/cli/first_time_setup.rb +17 -0
  59. data/lib/rubycode/views/cli/restart_message.rb +17 -0
  60. data/lib/rubycode/views/cli/setup_title.rb +17 -0
  61. data/lib/rubycode/views/cli.rb +5 -0
  62. data/lib/rubycode/views/formatter/fetch_summary.rb +54 -0
  63. data/lib/rubycode/views/formatter/web_search_summary.rb +53 -0
  64. data/lib/rubycode/views/formatter.rb +2 -0
  65. data/lib/rubycode/views/search_provider/debug_request.rb +30 -0
  66. data/lib/rubycode/views/search_provider/debug_response.rb +31 -0
  67. data/lib/rubycode/views/web_search_approval.rb +29 -0
  68. data/lib/rubycode/views.rb +5 -0
  69. data/lib/rubycode.rb +10 -0
  70. data/rubycode_cli.rb +228 -32
  71. metadata +81 -1
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module RubyCode
6
+ module Views
7
+ module Formatter
8
+ # Builds web search result summary display
9
+ class WebSearchSummary
10
+ def self.build(result:)
11
+ pastel = Pastel.new
12
+ metadata = result.metadata || {}
13
+
14
+ lines = build_header_lines(pastel, metadata)
15
+ lines.concat(build_result_lines(pastel, metadata[:results] || []))
16
+
17
+ lines.join("\n")
18
+ end
19
+
20
+ def self.build_header_lines(pastel, metadata)
21
+ count = metadata[:result_count] || 0
22
+ provider = metadata[:provider] || "Search"
23
+
24
+ [
25
+ "",
26
+ " #{pastel.cyan("✓")} Found #{count} result(s) from #{provider}:",
27
+ ""
28
+ ]
29
+ end
30
+
31
+ def self.build_result_lines(pastel, results)
32
+ results.flat_map.with_index do |item, idx|
33
+ build_single_result(pastel, item, idx)
34
+ end
35
+ end
36
+
37
+ def self.build_single_result(pastel, item, idx)
38
+ lines = [
39
+ " #{pastel.bold("#{idx + 1}. #{item[:title]}")}",
40
+ pastel.cyan(" #{item[:url]}")
41
+ ]
42
+ lines << pastel.dim(" #{item[:snippet]}") if snippet?(item)
43
+ lines << ""
44
+ lines
45
+ end
46
+
47
+ def self.snippet?(item)
48
+ item[:snippet] && !item[:snippet].empty?
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -5,3 +5,5 @@ require_relative "formatter/debug_tool_info"
5
5
  require_relative "formatter/tool_result"
6
6
  require_relative "formatter/info_message"
7
7
  require_relative "formatter/minimal_tool_info"
8
+ require_relative "formatter/web_search_summary"
9
+ require_relative "formatter/fetch_summary"
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module RubyCode
6
+ module Views
7
+ module SearchProvider
8
+ # Builds debug request text for search provider requests
9
+ class DebugRequest
10
+ def self.build(provider_name:, query:, max_results:, url:)
11
+ pastel = Pastel.new
12
+ content = build_content_lines(pastel, provider_name, query, max_results, url)
13
+ content.join("\n")
14
+ end
15
+
16
+ def self.build_content_lines(pastel, provider_name, query, max_results, url)
17
+ [
18
+ "",
19
+ pastel.yellow.bold("=== DEBUG SEARCH REQUEST ==="),
20
+ "#{pastel.bold("Provider:")} #{provider_name}",
21
+ "#{pastel.bold("Query:")} #{query}",
22
+ "#{pastel.bold("Max Results:")} #{max_results}",
23
+ "#{pastel.bold("URL:")} #{url}",
24
+ pastel.yellow.bold("=" * 28)
25
+ ]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module RubyCode
6
+ module Views
7
+ module SearchProvider
8
+ # Builds debug response text for search provider responses
9
+ class DebugResponse
10
+ def self.build(provider_name:, status_code:, body_preview:)
11
+ pastel = Pastel.new
12
+ content = build_content_lines(pastel, provider_name, status_code, body_preview)
13
+ content.join("\n")
14
+ end
15
+
16
+ def self.build_content_lines(pastel, provider_name, status_code, body_preview)
17
+ [
18
+ "",
19
+ pastel.green.bold("=== DEBUG SEARCH RESPONSE ==="),
20
+ "#{pastel.bold("Provider:")} #{provider_name}",
21
+ "#{pastel.bold("Status:")} #{status_code}",
22
+ "",
23
+ pastel.bold("Response Preview:"),
24
+ body_preview,
25
+ pastel.green.bold("=" * 29)
26
+ ]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module RubyCode
6
+ module Views
7
+ # Builds web search approval prompt display
8
+ class WebSearchApproval
9
+ def self.build(query:, max_results:)
10
+ pastel = Pastel.new
11
+
12
+ [
13
+ "",
14
+ pastel.cyan("━" * 80),
15
+ pastel.bold.cyan("🔍 Web Search Request"),
16
+ "#{pastel.cyan("Query:")} #{query}",
17
+ "#{pastel.cyan("Max results:")} #{max_results}",
18
+ pastel.cyan("─" * 80),
19
+ pastel.yellow("This will make HTTP requests to:"),
20
+ pastel.dim(" • DuckDuckGo Instant Answer API (free)"),
21
+ pastel.dim(" • Brave Search API (if BRAVE_API_KEY is set)"),
22
+ pastel.dim(" • Each result URL (HEAD request to verify)"),
23
+ "",
24
+ pastel.cyan("━" * 80)
25
+ ].join("\n")
26
+ end
27
+ end
28
+ end
29
+ end
@@ -6,7 +6,12 @@ require_relative "views/bash_approval"
6
6
  require_relative "views/write_approval"
7
7
  require_relative "views/update_approval"
8
8
  require_relative "views/skip_notification"
9
+ require_relative "views/web_search_approval"
9
10
  require_relative "views/cli"
10
11
  require_relative "views/agent_loop"
11
12
  require_relative "views/formatter"
12
13
  require_relative "views/response_handler"
14
+ require_relative "views/adapter/debug_request"
15
+ require_relative "views/adapter/debug_response"
16
+ require_relative "views/adapter/debug_delay"
17
+ require_relative "views/agent_loop/token_summary"
data/lib/rubycode.rb CHANGED
@@ -1,15 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Load environment variables from .env file if present
4
+ require "dotenv/load"
5
+
3
6
  require "i18n"
4
7
  require_relative "rubycode/version"
5
8
  require_relative "rubycode/errors"
6
9
  require_relative "rubycode/value_objects"
7
10
  require_relative "rubycode/configuration"
11
+ require_relative "rubycode/config_manager"
8
12
  require_relative "rubycode/database"
9
13
  require_relative "rubycode/models"
10
14
  require_relative "rubycode/context_builder"
15
+ require_relative "rubycode/token_counter"
16
+ require_relative "rubycode/pricing"
11
17
  require_relative "rubycode/adapters/base"
12
18
  require_relative "rubycode/adapters/ollama"
19
+ require_relative "rubycode/adapters/openrouter"
20
+ require_relative "rubycode/adapters/deepseek"
21
+ require_relative "rubycode/adapters/gemini"
22
+ require_relative "rubycode/adapters/openai"
13
23
  require_relative "rubycode/tools"
14
24
  require_relative "rubycode/agent_loop"
15
25
  require_relative "rubycode/client"
data/rubycode_cli.rb CHANGED
@@ -5,10 +5,152 @@ require_relative "lib/rubycode"
5
5
  require "tty-prompt"
6
6
 
7
7
  prompt = TTY::Prompt.new
8
- adapter = :ollama
9
8
 
10
- model = "deepseek-v3.1:671b-cloud"
11
- url = "http://localhost:11434"
9
+ # Setup wizard for first-time configuration or reconfiguration
10
+ def setup_wizard(prompt)
11
+ puts RubyCode::Views::Cli::SetupTitle.build
12
+
13
+ adapter = select_adapter(prompt)
14
+ model = select_model_for_adapter(prompt, adapter)
15
+ url = get_url_for_adapter(prompt, adapter)
16
+
17
+ configure_adapter_api_key(prompt, adapter) if adapter_requires_key?(adapter)
18
+ configure_exa_api_key(prompt) if prompt.yes?(I18n.t("rubycode.setup.configure_exa"), default: false)
19
+
20
+ save_and_return_config(adapter, model, url)
21
+ end
22
+
23
+ def select_adapter(prompt)
24
+ prompt.select(I18n.t("rubycode.setup.adapter_prompt"), {
25
+ I18n.t("rubycode.adapters.ollama.name") => :ollama,
26
+ I18n.t("rubycode.adapters.deepseek.name") => :deepseek,
27
+ I18n.t("rubycode.adapters.gemini.name") => :gemini,
28
+ I18n.t("rubycode.adapters.openai.name") => :openai,
29
+ I18n.t("rubycode.adapters.openrouter.name") => :openrouter
30
+ })
31
+ end
32
+
33
+ def select_model_for_adapter(prompt, adapter)
34
+ models = I18n.t("rubycode.models.#{adapter}")
35
+ choices = {}
36
+ models.each do |key, model_data|
37
+ # Skip the :default key which is just a string
38
+ next if %i[default].include?(key) || ["default"].include?(key)
39
+ # Skip if model_data is not a hash (defensive)
40
+ next unless model_data.is_a?(Hash)
41
+
42
+ label = model_data[:label] || model_data["label"]
43
+ name = model_data[:name] || model_data["name"]
44
+ choices[label] = name
45
+ end
46
+ prompt.select(I18n.t("rubycode.setup.model_prompt"), choices)
47
+ end
48
+
49
+ def get_url_for_adapter(prompt, adapter)
50
+ case adapter
51
+ when :ollama
52
+ prompt.ask(I18n.t("rubycode.setup.url_prompt"), default: "https://api.ollama.com")
53
+ when :deepseek
54
+ "https://api.deepseek.com/v1/chat/completions"
55
+ when :gemini
56
+ "https://generativelanguage.googleapis.com/v1beta/models"
57
+ when :openai
58
+ "https://api.openai.com/v1/chat/completions"
59
+ when :openrouter
60
+ "https://openrouter.ai/api/v1/chat/completions"
61
+ end
62
+ end
63
+
64
+ def adapter_requires_key?(adapter)
65
+ I18n.t("rubycode.adapters.#{adapter}")[:requires_key]
66
+ end
67
+
68
+ def configure_adapter_api_key(prompt, adapter)
69
+ env_var_name = "#{adapter.to_s.upcase}_API_KEY"
70
+ saved_exists = RubyCode::Models::ApiKey.key_exists?(adapter: adapter)
71
+ env_exists = ENV.fetch(env_var_name, nil)
72
+
73
+ if saved_exists
74
+ handle_existing_saved_key(prompt, adapter)
75
+ elsif env_exists
76
+ handle_existing_env_key(prompt, adapter, env_var_name)
77
+ else
78
+ prompt_for_required_key(prompt, adapter)
79
+ end
80
+ end
81
+
82
+ def handle_existing_saved_key(prompt, adapter)
83
+ use_saved = prompt.yes?(I18n.t("rubycode.setup.use_saved_api_key", adapter: adapter.to_s.upcase), default: true)
84
+ return if use_saved
85
+
86
+ new_key = prompt.mask("#{I18n.t("rubycode.setup.api_key_prompt", adapter: adapter.to_s.upcase)} " \
87
+ "#{I18n.t("rubycode.setup.api_key_optional")}")
88
+ RubyCode::Models::ApiKey.save_key(adapter: adapter, api_key: new_key) if new_key && !new_key.empty?
89
+ end
90
+
91
+ def handle_existing_env_key(prompt, adapter, env_var_name)
92
+ save_to_db = prompt.yes?(I18n.t("rubycode.setup.save_env_key_to_db", adapter: adapter.to_s.upcase), default: true)
93
+ RubyCode::Models::ApiKey.save_key(adapter: adapter, api_key: ENV.fetch(env_var_name, nil)) if save_to_db
94
+ end
95
+
96
+ def prompt_for_required_key(prompt, adapter)
97
+ puts RubyCode::Views::Cli::ApiKeyMissing.build(adapter: adapter)
98
+ api_key = prompt.mask(I18n.t("rubycode.setup.api_key_prompt", adapter: adapter.to_s.upcase))
99
+
100
+ if api_key.nil? || api_key.empty?
101
+ puts "\n#{I18n.t("rubycode.setup.api_key_required")}\n"
102
+ exit 1
103
+ end
104
+
105
+ RubyCode::Models::ApiKey.save_key(adapter: adapter, api_key: api_key)
106
+ end
107
+
108
+ def configure_exa_api_key(prompt)
109
+ saved_exists = RubyCode::Models::ApiKey.key_exists?(adapter: :exa)
110
+ env_exists = ENV.fetch("EXA_API_KEY", nil)
111
+
112
+ if saved_exists
113
+ handle_existing_saved_key(prompt, :exa)
114
+ elsif env_exists
115
+ handle_exa_env_key(prompt)
116
+ else
117
+ prompt_for_optional_exa_key(prompt)
118
+ end
119
+ end
120
+
121
+ def handle_exa_env_key(prompt)
122
+ save_to_db = prompt.yes?(I18n.t("rubycode.setup.save_env_key_to_db", adapter: "EXA"), default: true)
123
+ RubyCode::Models::ApiKey.save_key(adapter: :exa, api_key: ENV.fetch("EXA_API_KEY", nil)) if save_to_db
124
+ end
125
+
126
+ def prompt_for_optional_exa_key(prompt)
127
+ exa_key = prompt.mask("#{I18n.t("rubycode.setup.api_key_prompt", adapter: "EXA")} " \
128
+ "#{I18n.t("rubycode.setup.api_key_optional")}")
129
+ RubyCode::Models::ApiKey.save_key(adapter: :exa, api_key: exa_key) if exa_key && !exa_key.empty?
130
+ end
131
+
132
+ def save_and_return_config(adapter, model, url)
133
+ config = { adapter: adapter, model: model, url: url }
134
+ RubyCode::ConfigManager.save(config)
135
+ puts RubyCode::Views::Cli::ConfigSaved.build
136
+ config
137
+ end
138
+
139
+ # Load or setup configuration
140
+ if RubyCode::ConfigManager.exists?
141
+ saved_config = RubyCode::ConfigManager.load
142
+ use_saved = prompt.yes?(I18n.t("rubycode.setup.use_saved",
143
+ adapter: saved_config[:adapter],
144
+ model: saved_config[:model]),
145
+ default: true)
146
+ config = use_saved ? saved_config : setup_wizard(prompt)
147
+ else
148
+ puts RubyCode::Views::Cli::FirstTimeSetup.build
149
+ config = setup_wizard(prompt)
150
+ end
151
+ adapter = config[:adapter]
152
+ model = config[:model]
153
+ url = config[:url]
12
154
 
13
155
  puts "\n#{RubyCode::Views::Welcome.build}"
14
156
 
@@ -25,54 +167,108 @@ unless Dir.exist?(full_path)
25
167
  exit 1
26
168
  end
27
169
 
28
- debug_mode = prompt.yes?("Enable debug mode?") do |q|
29
- q.default false
30
- end
31
-
32
170
  RubyCode.configure do |config|
33
171
  config.adapter = adapter
34
172
  config.url = url
35
173
  config.model = model
36
174
  config.root_path = full_path
37
- config.debug = debug_mode
38
-
39
- config.enable_tool_injection_workaround = true
40
175
  end
41
176
 
42
177
  puts RubyCode::Views::Cli::ConfigurationTable.build(
43
- directory: full_path,
178
+ adapter: adapter,
44
179
  model: model,
45
- debug_mode: debug_mode
180
+ directory: full_path
46
181
  )
47
182
 
48
- client = RubyCode::Client.new(tty_prompt: prompt)
183
+ # Ensure database is connected before checking for API keys
184
+ RubyCode::Database.connect
49
185
 
50
- puts RubyCode::Views::Cli::ReadyMessage.build
186
+ # Check if adapter requires API key and ensure it's available
187
+ adapter_info = I18n.t("rubycode.adapters.#{adapter}")
188
+ if adapter_info[:requires_key]
189
+ env_var_name = "#{adapter.to_s.upcase}_API_KEY"
190
+ db_key_exists = RubyCode::Models::ApiKey.key_exists?(adapter: adapter)
191
+ env_key_exists = ENV.fetch(env_var_name, nil)
192
+
193
+ unless db_key_exists || env_key_exists
194
+ # No API key found - prompt user
195
+ puts RubyCode::Views::Cli::ApiKeyMissing.build(adapter: adapter)
196
+ api_key = prompt.mask(I18n.t("rubycode.setup.api_key_prompt", adapter: adapter.to_s.upcase))
197
+
198
+ if api_key.nil? || api_key.empty?
199
+ puts "\n#{I18n.t("rubycode.setup.api_key_required")}\n"
200
+ exit 1
201
+ end
202
+
203
+ # Save to database
204
+ RubyCode::Models::ApiKey.save_key(adapter: adapter, api_key: api_key)
205
+ puts "\n✓ API key saved\n"
206
+ end
207
+ end
51
208
 
52
- loop do
53
- user_input = begin
54
- prompt.ask("You: ")
55
- rescue StandardError
56
- nil
209
+ ChatContext = Struct.new(:prompt, :client, :adapter, :model, :full_path, :debug_mode)
210
+
211
+ def run_chat_loop(context)
212
+ loop do
213
+ user_input = get_user_input(context.prompt)
214
+ break if user_input.nil?
215
+
216
+ next if handle_special_command(user_input, context)
217
+
218
+ process_user_message(context.client, user_input)
57
219
  end
58
- break if user_input.nil?
220
+ end
59
221
 
60
- case user_input.strip.downcase
222
+ def get_user_input(prompt)
223
+ prompt.ask("You: ")
224
+ rescue StandardError
225
+ nil
226
+ end
227
+
228
+ def handle_special_command(input, context)
229
+ case input.strip.downcase
61
230
  when "exit", "quit"
62
231
  puts RubyCode::Views::Cli::ExitMessage.build
63
- break
232
+ exit 0
64
233
  when "clear"
65
- client.clear_memory
234
+ context.client.clear_memory
66
235
  puts RubyCode::Views::Cli::MemoryClearedMessage.build
67
- next
236
+ true
237
+ when "config"
238
+ show_config_and_reconfigure(context)
239
+ true
240
+ else
241
+ false
68
242
  end
243
+ end
69
244
 
70
- begin
71
- response = client.ask(prompt: user_input)
72
- puts RubyCode::Views::Cli::ResponseBox.build(response: response)
73
- rescue Interrupt
74
- puts RubyCode::Views::Cli::InterruptMessage.build
75
- rescue StandardError => e
76
- puts RubyCode::Views::Cli::ErrorDisplay.build(error: e)
77
- end
245
+ def show_config_and_reconfigure(context)
246
+ puts RubyCode::Views::Cli::ConfigurationTable.build(
247
+ adapter: context.adapter,
248
+ model: context.model,
249
+ directory: context.full_path,
250
+ debug_mode: context.debug_mode
251
+ )
252
+
253
+ return unless context.prompt.yes?(I18n.t("rubycode.setup.reconfigure"), default: false)
254
+
255
+ puts RubyCode::Views::Cli::RestartMessage.build
256
+ setup_wizard(context.prompt)
257
+ end
258
+
259
+ def process_user_message(client, user_input)
260
+ response = client.ask(prompt: user_input)
261
+ puts RubyCode::Views::Cli::ResponseBox.build(response: response)
262
+ rescue Interrupt
263
+ puts RubyCode::Views::Cli::InterruptMessage.build
264
+ rescue StandardError => e
265
+ puts RubyCode::Views::Cli::ErrorDisplay.build(error: e)
78
266
  end
267
+
268
+ client = RubyCode::Client.new(tty_prompt: prompt)
269
+
270
+ puts RubyCode::Views::Cli::ReadyMessage.build
271
+
272
+ debug_mode = false # Debug mode not yet implemented
273
+ context = ChatContext.new(prompt, client, adapter, model, full_path, debug_mode)
274
+ run_chat_loop(context)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubycode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Medeiros
@@ -23,6 +23,48 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.14'
26
+ - !ruby/object:Gem::Dependency
27
+ name: base64
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: dotenv
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: nokogiri
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.16'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.16'
26
68
  - !ruby/object:Gem::Dependency
27
69
  name: sequel
28
70
  requirement: !ruby/object:Gem::Requirement
@@ -186,6 +228,7 @@ executables: []
186
228
  extensions: []
187
229
  extra_rdoc_files: []
188
230
  files:
231
+ - ".env.example"
189
232
  - ".rubocop.yml"
190
233
  - CHANGELOG.md
191
234
  - LICENSE.txt
@@ -196,37 +239,63 @@ files:
196
239
  - config/system_prompt.md
197
240
  - config/tools/bash.json
198
241
  - config/tools/done.json
242
+ - config/tools/fetch.json
199
243
  - config/tools/read.json
200
244
  - config/tools/search.json
201
245
  - config/tools/update.json
246
+ - config/tools/websearch.json
202
247
  - config/tools/write.json
203
248
  - docs/images/demo.png
204
249
  - lib/rubycode.rb
205
250
  - lib/rubycode/adapters/base.rb
251
+ - lib/rubycode/adapters/concerns/debugging.rb
252
+ - lib/rubycode/adapters/concerns/error_handling.rb
253
+ - lib/rubycode/adapters/concerns/http_client.rb
254
+ - lib/rubycode/adapters/deepseek.rb
255
+ - lib/rubycode/adapters/gemini.rb
206
256
  - lib/rubycode/adapters/ollama.rb
257
+ - lib/rubycode/adapters/openai.rb
258
+ - lib/rubycode/adapters/openrouter.rb
207
259
  - lib/rubycode/agent_loop.rb
208
260
  - lib/rubycode/client.rb
209
261
  - lib/rubycode/client/approval_handler.rb
210
262
  - lib/rubycode/client/display_formatter.rb
211
263
  - lib/rubycode/client/response_handler.rb
264
+ - lib/rubycode/config_manager.rb
212
265
  - lib/rubycode/configuration.rb
213
266
  - lib/rubycode/context_builder.rb
214
267
  - lib/rubycode/database.rb
215
268
  - lib/rubycode/errors.rb
216
269
  - lib/rubycode/models.rb
270
+ - lib/rubycode/models/api_key.rb
217
271
  - lib/rubycode/models/base.rb
218
272
  - lib/rubycode/models/memory.rb
273
+ - lib/rubycode/pricing.rb
274
+ - lib/rubycode/search_providers/base.rb
275
+ - lib/rubycode/search_providers/brave_search.rb
276
+ - lib/rubycode/search_providers/concerns/debugging.rb
277
+ - lib/rubycode/search_providers/concerns/error_handling.rb
278
+ - lib/rubycode/search_providers/concerns/http_client.rb
279
+ - lib/rubycode/search_providers/duckduckgo_instant.rb
280
+ - lib/rubycode/search_providers/exa_ai.rb
281
+ - lib/rubycode/search_providers/multi_provider.rb
282
+ - lib/rubycode/token_counter.rb
219
283
  - lib/rubycode/tools.rb
220
284
  - lib/rubycode/tools/base.rb
221
285
  - lib/rubycode/tools/bash.rb
222
286
  - lib/rubycode/tools/done.rb
287
+ - lib/rubycode/tools/fetch.rb
223
288
  - lib/rubycode/tools/read.rb
224
289
  - lib/rubycode/tools/search.rb
225
290
  - lib/rubycode/tools/update.rb
291
+ - lib/rubycode/tools/web_search.rb
226
292
  - lib/rubycode/tools/write.rb
227
293
  - lib/rubycode/value_objects.rb
228
294
  - lib/rubycode/version.rb
229
295
  - lib/rubycode/views.rb
296
+ - lib/rubycode/views/adapter/debug_delay.rb
297
+ - lib/rubycode/views/adapter/debug_request.rb
298
+ - lib/rubycode/views/adapter/debug_response.rb
230
299
  - lib/rubycode/views/agent_loop.rb
231
300
  - lib/rubycode/views/agent_loop/adapter_error.rb
232
301
  - lib/rubycode/views/agent_loop/iteration_footer.rb
@@ -234,30 +303,41 @@ files:
234
303
  - lib/rubycode/views/agent_loop/response_received.rb
235
304
  - lib/rubycode/views/agent_loop/retry_status.rb
236
305
  - lib/rubycode/views/agent_loop/thinking_status.rb
306
+ - lib/rubycode/views/agent_loop/token_summary.rb
237
307
  - lib/rubycode/views/agent_loop/tool_error.rb
238
308
  - lib/rubycode/views/bash_approval.rb
239
309
  - lib/rubycode/views/cli.rb
310
+ - lib/rubycode/views/cli/api_key_missing.rb
311
+ - lib/rubycode/views/cli/config_saved.rb
240
312
  - lib/rubycode/views/cli/configuration_table.rb
241
313
  - lib/rubycode/views/cli/error_display.rb
242
314
  - lib/rubycode/views/cli/error_message.rb
243
315
  - lib/rubycode/views/cli/exit_message.rb
316
+ - lib/rubycode/views/cli/first_time_setup.rb
244
317
  - lib/rubycode/views/cli/interrupt_message.rb
245
318
  - lib/rubycode/views/cli/memory_cleared_message.rb
246
319
  - lib/rubycode/views/cli/ready_message.rb
247
320
  - lib/rubycode/views/cli/response_box.rb
321
+ - lib/rubycode/views/cli/restart_message.rb
322
+ - lib/rubycode/views/cli/setup_title.rb
248
323
  - lib/rubycode/views/formatter.rb
249
324
  - lib/rubycode/views/formatter/debug_tool_info.rb
325
+ - lib/rubycode/views/formatter/fetch_summary.rb
250
326
  - lib/rubycode/views/formatter/info_message.rb
251
327
  - lib/rubycode/views/formatter/minimal_tool_info.rb
252
328
  - lib/rubycode/views/formatter/tool_result.rb
329
+ - lib/rubycode/views/formatter/web_search_summary.rb
253
330
  - lib/rubycode/views/response_handler.rb
254
331
  - lib/rubycode/views/response_handler/agent_finished.rb
255
332
  - lib/rubycode/views/response_handler/complete_message.rb
256
333
  - lib/rubycode/views/response_handler/max_iterations.rb
257
334
  - lib/rubycode/views/response_handler/max_tool_calls.rb
258
335
  - lib/rubycode/views/response_handler/tool_injection_warning.rb
336
+ - lib/rubycode/views/search_provider/debug_request.rb
337
+ - lib/rubycode/views/search_provider/debug_response.rb
259
338
  - lib/rubycode/views/skip_notification.rb
260
339
  - lib/rubycode/views/update_approval.rb
340
+ - lib/rubycode/views/web_search_approval.rb
261
341
  - lib/rubycode/views/welcome.rb
262
342
  - lib/rubycode/views/write_approval.rb
263
343
  - rubycode_cli.rb