rubycode 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/README.md +33 -4
- data/config/locales/en.yml +87 -0
- data/config/system_prompt.md +54 -0
- data/config/tools/done.json +2 -2
- data/config/tools/update.json +25 -0
- data/config/tools/write.json +21 -0
- data/docs/images/demo.png +0 -0
- data/lib/rubycode/adapters/ollama.rb +76 -3
- data/lib/rubycode/agent_loop.rb +41 -16
- data/lib/rubycode/client/approval_handler.rb +70 -0
- data/lib/rubycode/client/display_formatter.rb +32 -12
- data/lib/rubycode/client/response_handler.rb +20 -12
- data/lib/rubycode/client.rb +25 -36
- data/lib/rubycode/configuration.rb +8 -1
- data/lib/rubycode/database.rb +50 -0
- data/lib/rubycode/errors.rb +12 -0
- data/lib/rubycode/models/base.rb +68 -0
- data/lib/rubycode/models/memory.rb +57 -0
- data/lib/rubycode/models.rb +4 -0
- data/lib/rubycode/tools/base.rb +1 -10
- data/lib/rubycode/tools/bash.rb +10 -7
- data/lib/rubycode/tools/read.rb +3 -0
- data/lib/rubycode/tools/update.rb +80 -0
- data/lib/rubycode/tools/write.rb +57 -0
- data/lib/rubycode/tools.rb +4 -0
- data/lib/rubycode/version.rb +1 -1
- data/lib/rubycode/views/agent_loop/adapter_error.rb +14 -0
- data/lib/rubycode/views/agent_loop/iteration_footer.rb +17 -0
- data/lib/rubycode/views/agent_loop/iteration_header.rb +24 -0
- data/lib/rubycode/views/agent_loop/response_received.rb +17 -0
- data/lib/rubycode/views/agent_loop/retry_status.rb +14 -0
- data/lib/rubycode/views/agent_loop/thinking_status.rb +17 -0
- data/lib/rubycode/views/agent_loop/tool_error.rb +14 -0
- data/lib/rubycode/views/agent_loop.rb +8 -0
- data/lib/rubycode/views/bash_approval.rb +28 -0
- data/lib/rubycode/views/cli/configuration_table.rb +28 -0
- data/lib/rubycode/views/cli/error_display.rb +19 -0
- data/lib/rubycode/views/cli/error_message.rb +17 -0
- data/lib/rubycode/views/cli/exit_message.rb +17 -0
- data/lib/rubycode/views/cli/interrupt_message.rb +17 -0
- data/lib/rubycode/views/cli/memory_cleared_message.rb +17 -0
- data/lib/rubycode/views/cli/ready_message.rb +17 -0
- data/lib/rubycode/views/cli/response_box.rb +29 -0
- data/lib/rubycode/views/cli.rb +11 -0
- data/lib/rubycode/views/formatter/debug_tool_info.rb +17 -0
- data/lib/rubycode/views/formatter/info_message.rb +17 -0
- data/lib/rubycode/views/formatter/minimal_tool_info.rb +26 -0
- data/lib/rubycode/views/formatter/tool_result.rb +20 -0
- data/lib/rubycode/views/formatter.rb +7 -0
- data/lib/rubycode/views/response_handler/agent_finished.rb +31 -0
- data/lib/rubycode/views/response_handler/complete_message.rb +31 -0
- data/lib/rubycode/views/response_handler/max_iterations.rb +29 -0
- data/lib/rubycode/views/response_handler/max_tool_calls.rb +29 -0
- data/lib/rubycode/views/response_handler/tool_injection_warning.rb +17 -0
- data/lib/rubycode/views/response_handler.rb +8 -0
- data/lib/rubycode/views/skip_notification.rb +15 -0
- data/lib/rubycode/views/update_approval.rb +36 -0
- data/lib/rubycode/views/welcome.rb +27 -0
- data/lib/rubycode/views/write_approval.rb +42 -0
- data/lib/rubycode/views.rb +12 -0
- data/lib/rubycode.rb +9 -1
- data/rubycode_cli.rb +41 -51
- metadata +220 -5
- data/lib/rubycode/history.rb +0 -39
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pastel"
|
|
4
|
+
|
|
5
|
+
module RubyCode
|
|
6
|
+
module Views
|
|
7
|
+
# Builds skip notification display
|
|
8
|
+
class SkipNotification
|
|
9
|
+
def self.build(message:)
|
|
10
|
+
pastel = Pastel.new
|
|
11
|
+
pastel.yellow(" ⓘ Skipped: #{message}")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pastel"
|
|
4
|
+
|
|
5
|
+
module RubyCode
|
|
6
|
+
module Views
|
|
7
|
+
# Builds file update approval prompt display with diff
|
|
8
|
+
class UpdateApproval
|
|
9
|
+
def self.build(file_path:, old_string:, new_string:)
|
|
10
|
+
pastel = Pastel.new
|
|
11
|
+
|
|
12
|
+
[
|
|
13
|
+
"",
|
|
14
|
+
pastel.yellow("━" * 80),
|
|
15
|
+
pastel.bold("Update Operation - Approval Required"),
|
|
16
|
+
"#{pastel.cyan("File:")} #{file_path}",
|
|
17
|
+
pastel.yellow("─" * 80),
|
|
18
|
+
format_removal_section(pastel, old_string),
|
|
19
|
+
"",
|
|
20
|
+
format_addition_section(pastel, new_string),
|
|
21
|
+
pastel.yellow("━" * 80)
|
|
22
|
+
].join("\n")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.format_removal_section(pastel, old_string)
|
|
26
|
+
old_lines = old_string.lines.map { |line| pastel.red(" - #{line.chomp}") }
|
|
27
|
+
[pastel.red("- REMOVE:"), old_lines.join("\n")].join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.format_addition_section(pastel, new_string)
|
|
31
|
+
new_lines = new_string.lines.map { |line| pastel.green(" + #{line.chomp}") }
|
|
32
|
+
[pastel.green("+ ADD:"), new_lines.join("\n")].join("\n")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pastel"
|
|
4
|
+
|
|
5
|
+
module RubyCode
|
|
6
|
+
module Views
|
|
7
|
+
# Displays welcome message when CLI starts
|
|
8
|
+
class Welcome
|
|
9
|
+
def self.build
|
|
10
|
+
pastel = Pastel.new
|
|
11
|
+
TTY::Box.frame(
|
|
12
|
+
width: 80,
|
|
13
|
+
border: :thick,
|
|
14
|
+
title: { top_left: " RubyCode v#{RubyCode::VERSION} " }
|
|
15
|
+
) do
|
|
16
|
+
[
|
|
17
|
+
pastel.bold.cyan("AI Ruby/Rails Code Assistant"),
|
|
18
|
+
"",
|
|
19
|
+
"Built with: #{pastel.dim("Ollama + DeepSeek + TTY Toolkit")}",
|
|
20
|
+
pastel.dim("─" * 76),
|
|
21
|
+
"#{pastel.yellow("Commands:")} exit, quit, clear"
|
|
22
|
+
].join("\n")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pastel"
|
|
4
|
+
|
|
5
|
+
module RubyCode
|
|
6
|
+
module Views
|
|
7
|
+
# Builds file write approval prompt display
|
|
8
|
+
class WriteApproval
|
|
9
|
+
def self.build(file_path:, content:)
|
|
10
|
+
pastel = Pastel.new
|
|
11
|
+
|
|
12
|
+
[
|
|
13
|
+
"",
|
|
14
|
+
header_section(pastel),
|
|
15
|
+
file_info_section(pastel, file_path, content),
|
|
16
|
+
pastel.yellow("─" * 80),
|
|
17
|
+
preview_section(content),
|
|
18
|
+
truncation_notice(pastel, content),
|
|
19
|
+
pastel.yellow("━" * 80)
|
|
20
|
+
].compact.join("\n")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.header_section(pastel)
|
|
24
|
+
[pastel.yellow("━" * 80), pastel.bold("Write Operation - Approval Required")].join("\n")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.file_info_section(pastel, file_path, content)
|
|
28
|
+
["#{pastel.cyan("File:")} #{file_path}", "#{pastel.cyan("Lines:")} #{content.lines.count}"].join("\n")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.preview_section(content)
|
|
32
|
+
content.lines.first(20).join
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.truncation_notice(pastel, content)
|
|
36
|
+
return "" unless content.lines.count > 20
|
|
37
|
+
|
|
38
|
+
pastel.dim("... (#{content.lines.count - 20} more lines)")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# View components for CLI display and user interaction
|
|
4
|
+
require_relative "views/welcome"
|
|
5
|
+
require_relative "views/bash_approval"
|
|
6
|
+
require_relative "views/write_approval"
|
|
7
|
+
require_relative "views/update_approval"
|
|
8
|
+
require_relative "views/skip_notification"
|
|
9
|
+
require_relative "views/cli"
|
|
10
|
+
require_relative "views/agent_loop"
|
|
11
|
+
require_relative "views/formatter"
|
|
12
|
+
require_relative "views/response_handler"
|
data/lib/rubycode.rb
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "i18n"
|
|
3
4
|
require_relative "rubycode/version"
|
|
4
5
|
require_relative "rubycode/errors"
|
|
5
6
|
require_relative "rubycode/value_objects"
|
|
6
7
|
require_relative "rubycode/configuration"
|
|
7
|
-
require_relative "rubycode/
|
|
8
|
+
require_relative "rubycode/database"
|
|
9
|
+
require_relative "rubycode/models"
|
|
8
10
|
require_relative "rubycode/context_builder"
|
|
9
11
|
require_relative "rubycode/adapters/base"
|
|
10
12
|
require_relative "rubycode/adapters/ollama"
|
|
11
13
|
require_relative "rubycode/tools"
|
|
12
14
|
require_relative "rubycode/agent_loop"
|
|
13
15
|
require_relative "rubycode/client"
|
|
16
|
+
require_relative "rubycode/views"
|
|
14
17
|
|
|
15
18
|
# Rubycode is a Ruby-native AI coding agent with pluggable LLM adapters
|
|
16
19
|
module RubyCode
|
|
@@ -26,4 +29,9 @@ module RubyCode
|
|
|
26
29
|
def self.config
|
|
27
30
|
self.configuration ||= Configuration.new
|
|
28
31
|
end
|
|
32
|
+
|
|
33
|
+
# Configure I18n
|
|
34
|
+
I18n.load_path += Dir[File.join(__dir__, "..", "config", "locales", "*.yml")]
|
|
35
|
+
I18n.backend.load_translations
|
|
36
|
+
I18n.default_locale = :en
|
|
29
37
|
end
|
data/rubycode_cli.rb
CHANGED
|
@@ -2,87 +2,77 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require_relative "lib/rubycode"
|
|
5
|
-
require "
|
|
5
|
+
require "tty-prompt"
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
puts "=" * 80
|
|
7
|
+
prompt = TTY::Prompt.new
|
|
8
|
+
adapter = :ollama
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
model = "deepseek-v3.1:671b-cloud"
|
|
11
|
+
url = "http://localhost:11434"
|
|
12
|
+
|
|
13
|
+
puts "\n#{RubyCode::Views::Welcome.build}"
|
|
14
|
+
|
|
15
|
+
directory = prompt.ask("What directory do you want to work on?") do |q|
|
|
16
|
+
q.default Dir.pwd
|
|
17
|
+
q.required false
|
|
18
|
+
end
|
|
19
|
+
directory = Dir.pwd if directory.nil? || directory.empty?
|
|
15
20
|
|
|
16
|
-
# Resolve the full path
|
|
17
21
|
full_path = File.expand_path(directory)
|
|
18
22
|
|
|
19
23
|
unless Dir.exist?(full_path)
|
|
20
|
-
puts
|
|
24
|
+
puts RubyCode::Views::Cli::ErrorMessage.build(message: "Directory '#{full_path}' does not exist!")
|
|
21
25
|
exit 1
|
|
22
26
|
end
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
print "Enable debug mode? (shows JSON requests/responses) [y/N]: "
|
|
28
|
-
debug_input = gets.chomp.downcase
|
|
29
|
-
debug_mode = %w[y yes].include?(debug_input)
|
|
28
|
+
debug_mode = prompt.yes?("Enable debug mode?") do |q|
|
|
29
|
+
q.default false
|
|
30
|
+
end
|
|
30
31
|
|
|
31
|
-
# Configure the client
|
|
32
32
|
RubyCode.configure do |config|
|
|
33
|
-
config.adapter =
|
|
34
|
-
config.url =
|
|
35
|
-
config.model =
|
|
33
|
+
config.adapter = adapter
|
|
34
|
+
config.url = url
|
|
35
|
+
config.model = model
|
|
36
36
|
config.root_path = full_path
|
|
37
37
|
config.debug = debug_mode
|
|
38
38
|
|
|
39
|
-
# Enable workaround to force tool-calling for models that don't follow instructions
|
|
40
39
|
config.enable_tool_injection_workaround = true
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
puts
|
|
42
|
+
puts RubyCode::Views::Cli::ConfigurationTable.build(
|
|
43
|
+
directory: full_path,
|
|
44
|
+
model: model,
|
|
45
|
+
debug_mode: debug_mode
|
|
46
|
+
)
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
client = RubyCode::Client.new
|
|
48
|
+
client = RubyCode::Client.new(tty_prompt: prompt)
|
|
47
49
|
|
|
48
|
-
puts
|
|
49
|
-
puts "✨ Agent initialized! You can now ask questions or request code changes."
|
|
50
|
-
puts " Type 'exit' or 'quit' to exit, 'clear' to clear history"
|
|
51
|
-
puts "=" * 80
|
|
50
|
+
puts RubyCode::Views::Cli::ReadyMessage.build
|
|
52
51
|
|
|
53
|
-
# Interactive loop
|
|
54
52
|
loop do
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
user_input = begin
|
|
54
|
+
prompt.ask("You: ")
|
|
55
|
+
rescue StandardError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
break if user_input.nil?
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
case prompt.strip.downcase
|
|
60
|
+
case user_input.strip.downcase
|
|
63
61
|
when "exit", "quit"
|
|
64
|
-
puts
|
|
62
|
+
puts RubyCode::Views::Cli::ExitMessage.build
|
|
65
63
|
break
|
|
66
64
|
when "clear"
|
|
67
|
-
client.
|
|
68
|
-
puts
|
|
65
|
+
client.clear_memory
|
|
66
|
+
puts RubyCode::Views::Cli::MemoryClearedMessage.build
|
|
69
67
|
next
|
|
70
68
|
end
|
|
71
69
|
|
|
72
|
-
puts "\n#{"-" * 80}"
|
|
73
|
-
|
|
74
70
|
begin
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
puts "\n🤖 Agent:"
|
|
79
|
-
puts "-" * 80
|
|
80
|
-
puts response
|
|
81
|
-
puts "-" * 80
|
|
71
|
+
response = client.ask(prompt: user_input)
|
|
72
|
+
puts RubyCode::Views::Cli::ResponseBox.build(response: response)
|
|
82
73
|
rescue Interrupt
|
|
83
|
-
puts
|
|
74
|
+
puts RubyCode::Views::Cli::InterruptMessage.build
|
|
84
75
|
rescue StandardError => e
|
|
85
|
-
puts
|
|
86
|
-
puts e.backtrace.first(3).join("\n")
|
|
76
|
+
puts RubyCode::Views::Cli::ErrorDisplay.build(error: e)
|
|
87
77
|
end
|
|
88
78
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,185 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubycode
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonas Medeiros
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
12
|
-
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: i18n
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.14'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.14'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: sequel
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.87'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.87'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sqlite3
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.4'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.4'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: pastel
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.8'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.8'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: tty-box
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.7'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.7'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: tty-logger
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.6'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.6'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: tty-markdown
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0.7'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0.7'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: tty-pager
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0.14'
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0.14'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: tty-progressbar
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0.18'
|
|
131
|
+
type: :runtime
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0.18'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: tty-prompt
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - "~>"
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0.23'
|
|
145
|
+
type: :runtime
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - "~>"
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0.23'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: tty-spinner
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - "~>"
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: '0.9'
|
|
159
|
+
type: :runtime
|
|
160
|
+
prerelease: false
|
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - "~>"
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '0.9'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: tty-table
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - "~>"
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '0.12'
|
|
173
|
+
type: :runtime
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - "~>"
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0.12'
|
|
180
|
+
description: Ruby-native AI coding agent with pluggable LLM adapters, persistent memory,
|
|
181
|
+
and file editing tools. Features automatic retry logic, SQLite-backed conversation
|
|
182
|
+
history, and user approval workflows for file modifications.
|
|
13
183
|
email:
|
|
14
184
|
- jonas.g.medeiros@gmail.com
|
|
15
185
|
executables: []
|
|
@@ -22,29 +192,74 @@ files:
|
|
|
22
192
|
- README.md
|
|
23
193
|
- Rakefile
|
|
24
194
|
- USAGE.md
|
|
195
|
+
- config/locales/en.yml
|
|
196
|
+
- config/system_prompt.md
|
|
25
197
|
- config/tools/bash.json
|
|
26
198
|
- config/tools/done.json
|
|
27
199
|
- config/tools/read.json
|
|
28
200
|
- config/tools/search.json
|
|
201
|
+
- config/tools/update.json
|
|
202
|
+
- config/tools/write.json
|
|
203
|
+
- docs/images/demo.png
|
|
29
204
|
- lib/rubycode.rb
|
|
30
205
|
- lib/rubycode/adapters/base.rb
|
|
31
206
|
- lib/rubycode/adapters/ollama.rb
|
|
32
207
|
- lib/rubycode/agent_loop.rb
|
|
33
208
|
- lib/rubycode/client.rb
|
|
209
|
+
- lib/rubycode/client/approval_handler.rb
|
|
34
210
|
- lib/rubycode/client/display_formatter.rb
|
|
35
211
|
- lib/rubycode/client/response_handler.rb
|
|
36
212
|
- lib/rubycode/configuration.rb
|
|
37
213
|
- lib/rubycode/context_builder.rb
|
|
214
|
+
- lib/rubycode/database.rb
|
|
38
215
|
- lib/rubycode/errors.rb
|
|
39
|
-
- lib/rubycode/
|
|
216
|
+
- lib/rubycode/models.rb
|
|
217
|
+
- lib/rubycode/models/base.rb
|
|
218
|
+
- lib/rubycode/models/memory.rb
|
|
40
219
|
- lib/rubycode/tools.rb
|
|
41
220
|
- lib/rubycode/tools/base.rb
|
|
42
221
|
- lib/rubycode/tools/bash.rb
|
|
43
222
|
- lib/rubycode/tools/done.rb
|
|
44
223
|
- lib/rubycode/tools/read.rb
|
|
45
224
|
- lib/rubycode/tools/search.rb
|
|
225
|
+
- lib/rubycode/tools/update.rb
|
|
226
|
+
- lib/rubycode/tools/write.rb
|
|
46
227
|
- lib/rubycode/value_objects.rb
|
|
47
228
|
- lib/rubycode/version.rb
|
|
229
|
+
- lib/rubycode/views.rb
|
|
230
|
+
- lib/rubycode/views/agent_loop.rb
|
|
231
|
+
- lib/rubycode/views/agent_loop/adapter_error.rb
|
|
232
|
+
- lib/rubycode/views/agent_loop/iteration_footer.rb
|
|
233
|
+
- lib/rubycode/views/agent_loop/iteration_header.rb
|
|
234
|
+
- lib/rubycode/views/agent_loop/response_received.rb
|
|
235
|
+
- lib/rubycode/views/agent_loop/retry_status.rb
|
|
236
|
+
- lib/rubycode/views/agent_loop/thinking_status.rb
|
|
237
|
+
- lib/rubycode/views/agent_loop/tool_error.rb
|
|
238
|
+
- lib/rubycode/views/bash_approval.rb
|
|
239
|
+
- lib/rubycode/views/cli.rb
|
|
240
|
+
- lib/rubycode/views/cli/configuration_table.rb
|
|
241
|
+
- lib/rubycode/views/cli/error_display.rb
|
|
242
|
+
- lib/rubycode/views/cli/error_message.rb
|
|
243
|
+
- lib/rubycode/views/cli/exit_message.rb
|
|
244
|
+
- lib/rubycode/views/cli/interrupt_message.rb
|
|
245
|
+
- lib/rubycode/views/cli/memory_cleared_message.rb
|
|
246
|
+
- lib/rubycode/views/cli/ready_message.rb
|
|
247
|
+
- lib/rubycode/views/cli/response_box.rb
|
|
248
|
+
- lib/rubycode/views/formatter.rb
|
|
249
|
+
- lib/rubycode/views/formatter/debug_tool_info.rb
|
|
250
|
+
- lib/rubycode/views/formatter/info_message.rb
|
|
251
|
+
- lib/rubycode/views/formatter/minimal_tool_info.rb
|
|
252
|
+
- lib/rubycode/views/formatter/tool_result.rb
|
|
253
|
+
- lib/rubycode/views/response_handler.rb
|
|
254
|
+
- lib/rubycode/views/response_handler/agent_finished.rb
|
|
255
|
+
- lib/rubycode/views/response_handler/complete_message.rb
|
|
256
|
+
- lib/rubycode/views/response_handler/max_iterations.rb
|
|
257
|
+
- lib/rubycode/views/response_handler/max_tool_calls.rb
|
|
258
|
+
- lib/rubycode/views/response_handler/tool_injection_warning.rb
|
|
259
|
+
- lib/rubycode/views/skip_notification.rb
|
|
260
|
+
- lib/rubycode/views/update_approval.rb
|
|
261
|
+
- lib/rubycode/views/welcome.rb
|
|
262
|
+
- lib/rubycode/views/write_approval.rb
|
|
48
263
|
- rubycode_cli.rb
|
|
49
264
|
- sig/rubycode.rbs
|
|
50
265
|
homepage: https://github.com/jonasmedeiros/rubycode
|
|
@@ -71,5 +286,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
71
286
|
requirements: []
|
|
72
287
|
rubygems_version: 3.6.9
|
|
73
288
|
specification_version: 4
|
|
74
|
-
summary:
|
|
289
|
+
summary: AI coding assistant with autonomous task execution
|
|
75
290
|
test_files: []
|
data/lib/rubycode/history.rb
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyCode
|
|
4
|
-
# Manages conversation history between user and assistant
|
|
5
|
-
class History
|
|
6
|
-
attr_reader :messages
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
@messages = []
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Accept either Message objects or keyword arguments for backwards compatibility
|
|
13
|
-
def add_message(message = nil, role: nil, content: nil)
|
|
14
|
-
if message.is_a?(Message)
|
|
15
|
-
@messages << message
|
|
16
|
-
elsif role && content
|
|
17
|
-
@messages << Message.new(role: role, content: content)
|
|
18
|
-
else
|
|
19
|
-
raise ArgumentError, "Must provide either a Message object or role: and content: keyword arguments"
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def to_llm_format
|
|
24
|
-
@messages.map(&:to_h)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def clear
|
|
28
|
-
@messages = []
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def last_user_message
|
|
32
|
-
@messages.reverse.find { |msg| msg.role == "user" }
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def last_assistant_message
|
|
36
|
-
@messages.reverse.find { |msg| msg.role == "assistant" }
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|