ares-runtime 2.0.3 → 2.0.5
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/exe/ares +13 -14
- data/lib/ares/runtime/adapters/base_adapter.rb +21 -18
- data/lib/ares/runtime/adapters/cursor_adapter.rb +13 -2
- data/lib/ares/runtime/adapters/ollama_adapter.rb +4 -3
- data/lib/ares/runtime/core_subsystem.rb +1 -2
- data/lib/ares/runtime/diagnostic_runner.rb +96 -0
- data/lib/ares/runtime/engine_chain.rb +48 -39
- data/lib/ares/runtime/fix_applicator.rb +134 -0
- data/lib/ares/runtime/model_selector.rb +2 -2
- data/lib/ares/runtime/planner/ollama_planner.rb +2 -2
- data/lib/ares/runtime/router.rb +23 -198
- data/lib/ares/runtime/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24fc0fcd364ccd0c506af86b1757e8c29542184245ec22d5b6a3cb357af839b9
|
|
4
|
+
data.tar.gz: 44c8d7a31b0b3996613934510d64117634a3ac3614ae0ac34fdf67c8127e5ca3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c3b8198c8f11b94ebfbd9845ec17de889afc98bf01870e4e1f874c5ca6d94a95b3021ba2c9237dad4ee40c26dcbb6946a43afa313b1c31f03df634bd22226af
|
|
7
|
+
data.tar.gz: add502d6a5cf0ac14ae20fc369ccf947253105b0904a6f5019ce2079fe109b25b5b6ab11cd44fa1a4b1fe62d02da1e9def57809b26766adc47e1d1dd2d90ef43
|
data/exe/ares
CHANGED
|
@@ -7,16 +7,14 @@ require 'optparse'
|
|
|
7
7
|
module Ares
|
|
8
8
|
class CLI
|
|
9
9
|
def self.start
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
when '
|
|
14
|
-
when '
|
|
15
|
-
when '
|
|
16
|
-
when 'version' then version
|
|
17
|
-
when 'logs' then logs
|
|
10
|
+
case ARGV.first
|
|
11
|
+
when 'init' then ARGV.shift; init
|
|
12
|
+
when 'config' then ARGV.shift; config
|
|
13
|
+
when 'doctor' then ARGV.shift; doctor
|
|
14
|
+
when 'version' then ARGV.shift; version
|
|
15
|
+
when 'logs' then ARGV.shift; logs
|
|
18
16
|
else
|
|
19
|
-
run_task
|
|
17
|
+
run_task
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
|
|
@@ -40,7 +38,7 @@ module Ares
|
|
|
40
38
|
Ares::Runtime::LogsCLI.run
|
|
41
39
|
end
|
|
42
40
|
|
|
43
|
-
def self.run_task
|
|
41
|
+
def self.run_task
|
|
44
42
|
options = {
|
|
45
43
|
dry_run: false,
|
|
46
44
|
git: false,
|
|
@@ -50,9 +48,10 @@ module Ares
|
|
|
50
48
|
OptionParser.new do |opts|
|
|
51
49
|
opts.banner = 'Usage: ares [options] "task description"'
|
|
52
50
|
|
|
53
|
-
opts.on('-d', '--dry-run')
|
|
54
|
-
opts.on('-g', '--git')
|
|
55
|
-
opts.on('--tui')
|
|
51
|
+
opts.on('-d', '--dry-run') { options[:dry_run] = true }
|
|
52
|
+
opts.on('-g', '--git') { options[:git] = true }
|
|
53
|
+
opts.on('--tui') { options[:tui] = true }
|
|
54
|
+
opts.on('--fail-fast') { options[:fail_fast] = true }
|
|
56
55
|
end.parse!
|
|
57
56
|
|
|
58
57
|
if options[:tui]
|
|
@@ -60,7 +59,7 @@ module Ares
|
|
|
60
59
|
exit
|
|
61
60
|
end
|
|
62
61
|
|
|
63
|
-
task =
|
|
62
|
+
task = ARGV.join(' ').strip
|
|
64
63
|
|
|
65
64
|
if task.empty?
|
|
66
65
|
puts 'No task provided.'
|
|
@@ -10,52 +10,55 @@ module Ares
|
|
|
10
10
|
class BaseAdapter
|
|
11
11
|
DEFAULT_TIMEOUT = 30
|
|
12
12
|
|
|
13
|
-
# Template Method: The core algorithm skeleton
|
|
14
13
|
def call(prompt, model = nil, **options)
|
|
15
14
|
cmd = build_command(prompt, model, **options)
|
|
16
|
-
|
|
17
|
-
output, status = execute_with_timeout(cmd, prompt, timeout_seconds)
|
|
18
|
-
|
|
19
|
-
if should_retry?(status, output)
|
|
20
|
-
cmd = build_retry_command(cmd, prompt, **options)
|
|
21
|
-
output, status = execute_with_timeout(cmd, prompt, timeout_seconds)
|
|
22
|
-
end
|
|
15
|
+
output, status = execute_with_retry(cmd, prompt, options)
|
|
23
16
|
|
|
24
17
|
handle_errors(status, output)
|
|
25
|
-
|
|
26
18
|
output
|
|
27
19
|
end
|
|
28
20
|
|
|
29
21
|
protected
|
|
30
22
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
def execute_with_retry(cmd, prompt, options)
|
|
24
|
+
output, status = run_with_timeout(cmd, prompt)
|
|
25
|
+
return [output, status] unless should_retry?(status, output)
|
|
26
|
+
|
|
27
|
+
run_with_timeout(build_retry_command(cmd, prompt, **options), prompt)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def run_with_timeout(cmd, prompt)
|
|
31
|
+
Timeout.timeout(timeout_seconds) { run_command(cmd, prompt) }
|
|
35
32
|
rescue Timeout::Error => e
|
|
36
|
-
raise "#{adapter_name} timed out after #{
|
|
33
|
+
raise "#{adapter_name} timed out after #{timeout_seconds}s: #{e.message}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def run_command(cmd, prompt)
|
|
37
|
+
return Open3.capture2e(*cmd, stdin_data: prompt) if pipes_prompt_to_stdin?
|
|
38
|
+
|
|
39
|
+
Open3.capture2e(*cmd)
|
|
37
40
|
end
|
|
38
41
|
|
|
39
42
|
def handle_errors(status, output)
|
|
40
43
|
raise "#{adapter_name} command failed: #{output}" unless status.success?
|
|
41
44
|
end
|
|
42
45
|
|
|
43
|
-
# Subclasses MUST implement this
|
|
44
46
|
def build_command(prompt, model, **options)
|
|
45
47
|
raise NotImplementedError, "#{self.class} must implement #build_command"
|
|
46
48
|
end
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
def pipes_prompt_to_stdin?
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
49
54
|
def should_retry?(_status, _output)
|
|
50
55
|
false
|
|
51
56
|
end
|
|
52
57
|
|
|
53
|
-
# Hook: Customize the command for the retry attempt
|
|
54
58
|
def build_retry_command(cmd, _prompt, **_options)
|
|
55
59
|
cmd
|
|
56
60
|
end
|
|
57
61
|
|
|
58
|
-
# Hook: Override for specific adapter timeouts
|
|
59
62
|
def timeout_seconds
|
|
60
63
|
DEFAULT_TIMEOUT
|
|
61
64
|
end
|
|
@@ -12,14 +12,21 @@ module Ares
|
|
|
12
12
|
|
|
13
13
|
protected
|
|
14
14
|
|
|
15
|
-
def build_command(
|
|
15
|
+
def build_command(prompt, _model, resume: true, cloud: false, **_options)
|
|
16
|
+
# Force strict non-conversational behavior for Cursor Agent
|
|
17
|
+
agent_prompt = "ACT AS AN AUTONOMOUS AGENT. PERFORM THE FOLLOWING TASK. DO NOT CHAT.\nTASK: #{prompt}"
|
|
16
18
|
# --trust --yolo ensures no interactive prompts in headless mode
|
|
17
|
-
cmd = ['agent',
|
|
19
|
+
cmd = ['agent', agent_prompt, '--print', '--trust', '--yolo']
|
|
18
20
|
cmd << '-c' if cloud
|
|
19
21
|
cmd << '--continue' if resume && !cloud
|
|
20
22
|
cmd
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
|
|
26
|
+
def pipes_prompt_to_stdin?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
23
30
|
def should_retry?(status, output)
|
|
24
31
|
!status.success? && output.include?('No previous chats found')
|
|
25
32
|
end
|
|
@@ -27,6 +34,10 @@ module Ares
|
|
|
27
34
|
def build_retry_command(cmd, _prompt, **_options)
|
|
28
35
|
cmd.dup.tap { |c| c.delete('--continue') }
|
|
29
36
|
end
|
|
37
|
+
|
|
38
|
+
def timeout_seconds
|
|
39
|
+
300 # Increased timeout for complex agent tasks
|
|
40
|
+
end
|
|
30
41
|
end
|
|
31
42
|
end
|
|
32
43
|
end
|
|
@@ -15,7 +15,8 @@ module Ares
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def call(prompt, model = nil, schema: nil)
|
|
18
|
-
|
|
18
|
+
available = @client.list_model_names
|
|
19
|
+
model = available.include?(model) ? model : best_available_model(available)
|
|
19
20
|
|
|
20
21
|
options = { prompt: prompt, model: model }
|
|
21
22
|
options[:schema] = schema if schema
|
|
@@ -25,8 +26,8 @@ module Ares
|
|
|
25
26
|
|
|
26
27
|
private
|
|
27
28
|
|
|
28
|
-
def best_available_model
|
|
29
|
-
available
|
|
29
|
+
def best_available_model(available = nil)
|
|
30
|
+
available ||= @client.list_model_names
|
|
30
31
|
return 'qwen3:latest' if available.include?('qwen3:latest')
|
|
31
32
|
return 'qwen3:8b' if available.include?('qwen3:8b')
|
|
32
33
|
|
|
@@ -9,14 +9,13 @@ module Ares
|
|
|
9
9
|
module Runtime
|
|
10
10
|
# Subsystem Facade that initializes and bundles core dependencies for the Router.
|
|
11
11
|
class CoreSubsystem
|
|
12
|
-
attr_reader :logger, :planner, :
|
|
12
|
+
attr_reader :logger, :planner, :tiny_processor, :ollama_healthy
|
|
13
13
|
|
|
14
14
|
def initialize
|
|
15
15
|
@logger = TaskLogger.new
|
|
16
16
|
@ollama_healthy = initialize_ollama
|
|
17
17
|
|
|
18
18
|
@planner = OllamaPlanner.new(healthy: @ollama_healthy)
|
|
19
|
-
@selector = ModelSelector.new
|
|
20
19
|
@tiny_processor = TinyTaskProcessor.new(healthy: @ollama_healthy)
|
|
21
20
|
end
|
|
22
21
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'fix_applicator'
|
|
4
|
+
|
|
5
|
+
module Ares
|
|
6
|
+
module Runtime
|
|
7
|
+
# Runs diagnostic commands (tests, syntax, lint) and escalates failures to AI fix.
|
|
8
|
+
class DiagnosticRunner
|
|
9
|
+
def initialize(core:, spinner:)
|
|
10
|
+
@core = core
|
|
11
|
+
@spinner = spinner
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run_tests(options = {})
|
|
15
|
+
run_loop('bundle exec rspec', options.merge(type: :test, title: 'Running tests'))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run_syntax_check(options = {})
|
|
19
|
+
cmd = "ruby -e 'Dir.glob(\"{lib,bin,exe,spec}/**/*.rb\").each { |f| (puts \"Checking \#{f}\"; system(\"ruby -c \#{f}\")) or exit(1) }'"
|
|
20
|
+
run_loop(cmd, options.merge(type: :syntax, title: 'Checking syntax'))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run_lint(options = {})
|
|
24
|
+
run_loop('bundle exec rubocop -A', options.merge(type: :lint, title: 'Running RuboCop'))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def run_loop(command, options)
|
|
28
|
+
title = options[:title] || 'Running verification'
|
|
29
|
+
result = run_with_spinner(command, title)
|
|
30
|
+
|
|
31
|
+
return report_success(title) if result[:exit_status].zero?
|
|
32
|
+
|
|
33
|
+
summary = parse_summary(result[:output], title, options[:type] || :test)
|
|
34
|
+
print_summary(summary, options[:type] || :test, title: title)
|
|
35
|
+
|
|
36
|
+
return skip_escalation if options[:dry_run]
|
|
37
|
+
|
|
38
|
+
applicator = FixApplicator.new(core: @core, spinner: @spinner, diagnostic_runner: self)
|
|
39
|
+
result = applicator.escalate(
|
|
40
|
+
type: options[:type] || :test,
|
|
41
|
+
summary: summary,
|
|
42
|
+
verify_command: command,
|
|
43
|
+
fix_first_only: !!options[:fix_first_only]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return result if options[:fail_fast]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def print_summary(summary, type, title: nil)
|
|
50
|
+
header = title || "Diagnostic Summary (#{type.to_s.upcase})"
|
|
51
|
+
table = TTY::Table.new(header: %w[Attribute Value])
|
|
52
|
+
table << ['Failed Items', Array(summary['failed_items'] || summary['failed_tests']).join("\n")]
|
|
53
|
+
table << ['Error Summary', summary['error_summary']]
|
|
54
|
+
puts "\n--- #{header} ---"
|
|
55
|
+
puts table.render(:unicode, multiline: true)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def run_with_spinner(command, title)
|
|
61
|
+
@spinner.update(title: "#{title}...")
|
|
62
|
+
result = nil
|
|
63
|
+
@spinner.run { result = TerminalRunner.run(command) }
|
|
64
|
+
result
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_summary(output, title, type)
|
|
68
|
+
@spinner.update(title: "#{title} failed. Summarizing diagnostic output...")
|
|
69
|
+
summary = nil
|
|
70
|
+
@spinner.run do
|
|
71
|
+
parsed = DiagnosticParser.parse(output, type: type)
|
|
72
|
+
if parsed['files'].empty? && parsed['failed_items'].empty?
|
|
73
|
+
@spinner.update(title: "#{title} failed. LLM Fallback (Slow)...")
|
|
74
|
+
summary = @core.tiny_processor.summarize_output(output, type: type)
|
|
75
|
+
else
|
|
76
|
+
summary = parsed
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
summary
|
|
80
|
+
rescue StandardError => e
|
|
81
|
+
@spinner.update(title: "#{title} failed. Error in fast-path: #{e.message}. LLM Fallback...")
|
|
82
|
+
@spinner.run { @core.tiny_processor.summarize_output(output, type: type) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def report_success(title)
|
|
86
|
+
puts "#{title} passed! ✅"
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def skip_escalation
|
|
91
|
+
puts 'Dry run: skipping escalation.'
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -9,57 +9,30 @@ module Ares
|
|
|
9
9
|
module Runtime
|
|
10
10
|
# Chain of Responsibility Handler linking CLI engines for automated fallback.
|
|
11
11
|
class EngineChain
|
|
12
|
+
CAPABLE_ENGINES = %w[claude codex cursor ollama].freeze
|
|
13
|
+
|
|
12
14
|
attr_accessor :next_handler
|
|
13
15
|
attr_reader :engine_name
|
|
14
16
|
|
|
17
|
+
def self.build_fallback(initial_engine)
|
|
18
|
+
initial = initial_engine.to_s
|
|
19
|
+
fallback_order = ([initial] + (CAPABLE_ENGINES - [initial])).uniq
|
|
20
|
+
chain = build(fallback_order)
|
|
21
|
+
{ chain: chain, size: fallback_order.size }
|
|
22
|
+
end
|
|
23
|
+
|
|
15
24
|
def initialize(engine_name)
|
|
16
25
|
@engine_name = engine_name
|
|
17
26
|
@next_handler = nil
|
|
18
27
|
@adapter = get_adapter(engine_name)
|
|
19
28
|
end
|
|
20
29
|
|
|
21
|
-
# Standard execution chain
|
|
22
30
|
def call(prompt, options, attempt: 1, total: 1)
|
|
23
|
-
|
|
24
|
-
puts "Falling back to #{@engine_name} (attempt #{attempt}/#{total})..."
|
|
25
|
-
else
|
|
26
|
-
puts "Executing task via #{@engine_name} (attempt #{attempt}/#{total})..."
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
begin
|
|
30
|
-
QuotaManager.increment_usage(@engine_name)
|
|
31
|
-
|
|
32
|
-
@adapter.call(prompt, options[:model], **adapter_options(options))
|
|
33
|
-
rescue StandardError => e
|
|
34
|
-
puts "\n⚠️ #{@engine_name} failed: #{e.message.split("\n").first}"
|
|
35
|
-
|
|
36
|
-
raise 'All available AI engines failed to execute the task.' unless @next_handler
|
|
37
|
-
|
|
38
|
-
@next_handler.call(prompt, options, attempt: attempt + 1, total: total)
|
|
39
|
-
end
|
|
31
|
+
execute_with_fallback(prompt, options, attempt, total, mode: :task)
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if attempt > 1
|
|
45
|
-
puts "Falling back to #{@engine_name} for fix (attempt #{attempt}/#{total})..."
|
|
46
|
-
else
|
|
47
|
-
puts "Applying fix via #{@engine_name} (attempt #{attempt}/#{total})..."
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
checkpoint_block&.call(@engine_name)
|
|
51
|
-
|
|
52
|
-
begin
|
|
53
|
-
QuotaManager.increment_usage(@engine_name)
|
|
54
|
-
|
|
55
|
-
@adapter.call(prompt, options[:model], **adapter_options(options))
|
|
56
|
-
rescue StandardError => e
|
|
57
|
-
puts "\n⚠️ #{@engine_name} failed during fix: #{e.message.split("\n").first}"
|
|
58
|
-
|
|
59
|
-
raise 'All available AI engines failed to apply the fix.' unless @next_handler
|
|
60
|
-
|
|
61
|
-
@next_handler.call_fix(prompt, options, attempt: attempt + 1, total: total, &checkpoint_block)
|
|
62
|
-
end
|
|
34
|
+
def call_fix(prompt, options, attempt: 1, total: 1, &block)
|
|
35
|
+
execute_with_fallback(prompt, options, attempt, total, mode: :fix, &block)
|
|
63
36
|
end
|
|
64
37
|
|
|
65
38
|
# Factory method to build the chain
|
|
@@ -80,6 +53,42 @@ module Ares
|
|
|
80
53
|
|
|
81
54
|
private
|
|
82
55
|
|
|
56
|
+
def execute_with_fallback(prompt, options, attempt, total, mode:, &block)
|
|
57
|
+
puts status_message(attempt, total, mode)
|
|
58
|
+
block&.call(@engine_name)
|
|
59
|
+
|
|
60
|
+
QuotaManager.increment_usage(@engine_name)
|
|
61
|
+
opts = adapter_options(options)
|
|
62
|
+
opts[:schema] = options[:schema] if options[:schema]
|
|
63
|
+
@adapter.call(prompt, options[:model], **opts)
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
failed_msg = mode == :fix ? "#{@engine_name} failed during fix:" : "#{@engine_name} failed:"
|
|
66
|
+
puts "\n❌ #{failed_msg} #{e.message.split("\n").first}"
|
|
67
|
+
raise all_engines_failed_message(mode) unless @next_handler
|
|
68
|
+
|
|
69
|
+
next_method = mode == :fix ? :call_fix : :call
|
|
70
|
+
@next_handler.public_send(next_method, prompt, options, attempt: attempt + 1, total: total, &block)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def status_message(attempt, total, mode)
|
|
74
|
+
action = if attempt > 1
|
|
75
|
+
'Falling back to'
|
|
76
|
+
elsif mode == :fix
|
|
77
|
+
'Applying fix via'
|
|
78
|
+
else
|
|
79
|
+
'Executing task via'
|
|
80
|
+
end
|
|
81
|
+
"#{action} #{@engine_name} (attempt #{attempt}/#{total})..."
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def all_engines_failed_message(mode)
|
|
85
|
+
if mode == :fix
|
|
86
|
+
'All available AI engines failed to apply the fix.'
|
|
87
|
+
else
|
|
88
|
+
'All available AI engines failed to execute the task.'
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
83
92
|
def get_adapter(engine)
|
|
84
93
|
case engine
|
|
85
94
|
when 'claude' then Ares::Runtime::ClaudeAdapter.new
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ares
|
|
4
|
+
module Runtime
|
|
5
|
+
# Escalates diagnostic failures to AI engines and applies patches.
|
|
6
|
+
class FixApplicator
|
|
7
|
+
MAX_LINT_ITERATIONS = 20
|
|
8
|
+
|
|
9
|
+
def initialize(core:, spinner:, diagnostic_runner:)
|
|
10
|
+
@core = core
|
|
11
|
+
@spinner = spinner
|
|
12
|
+
@diagnostic_runner = diagnostic_runner
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def escalate(type:, summary:, verify_command:, fix_first_only: false)
|
|
16
|
+
type == :lint ? escalate_lint_iteratively(summary, verify_command) : escalate_once(summary, type, verify_command)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def escalate_lint_iteratively(summary, verify_command)
|
|
22
|
+
current_summary = summary
|
|
23
|
+
|
|
24
|
+
MAX_LINT_ITERATIONS.times do |iteration|
|
|
25
|
+
puts "\n--- Fix iteration #{iteration + 1}/#{MAX_LINT_ITERATIONS} ---" if iteration.positive?
|
|
26
|
+
|
|
27
|
+
success = escalate_once(current_summary, :lint, verify_command, fix_first_only: true)
|
|
28
|
+
return true if success
|
|
29
|
+
|
|
30
|
+
verify_result = rerun_and_summarize(verify_command)
|
|
31
|
+
return false if verify_result[:exit_status].zero?
|
|
32
|
+
|
|
33
|
+
current_summary = verify_result[:summary]
|
|
34
|
+
@diagnostic_runner.print_summary(current_summary, :lint, title: 'Remaining offenses')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
puts "\nReached max iterations (#{MAX_LINT_ITERATIONS}). Some offenses may remain."
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def escalate_once(summary, type, verify_command, fix_first_only: false)
|
|
42
|
+
selection = ModelSelector.select({ 'task_type' => 'refactor', 'risk_level' => 'medium' })
|
|
43
|
+
puts "Selected Engine for fix: #{selection[:engine]} (#{selection[:model] || 'default'})"
|
|
44
|
+
|
|
45
|
+
result = apply_fix_with_fallbacks(build_fix_prompt(summary, type, fix_first_only), selection)
|
|
46
|
+
return false unless result
|
|
47
|
+
|
|
48
|
+
apply_patches(result) if result['patches']&.any?
|
|
49
|
+
|
|
50
|
+
verify_result = run_verify(verify_command)
|
|
51
|
+
handle_verification(verify_result, type)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def rerun_and_summarize(verify_command)
|
|
55
|
+
@spinner.update(title: 'Re-running RuboCop...')
|
|
56
|
+
verify_result = nil
|
|
57
|
+
@spinner.run { verify_result = TerminalRunner.run(verify_command) }
|
|
58
|
+
|
|
59
|
+
return { exit_status: verify_result[:exit_status], summary: nil } if verify_result[:exit_status].zero?
|
|
60
|
+
|
|
61
|
+
@spinner.update(title: 'Summarizing remaining offenses...')
|
|
62
|
+
summary = nil
|
|
63
|
+
@spinner.run { summary = @core.tiny_processor.summarize_output(verify_result[:output], type: :lint) }
|
|
64
|
+
{ exit_status: verify_result[:exit_status], summary: summary }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_fix_prompt(summary, type, fix_first_only)
|
|
68
|
+
builder = PromptBuilder.new
|
|
69
|
+
.add_context(ContextLoader.load)
|
|
70
|
+
.add_diagnostic(type, summary['failed_items'] || summary['failed_tests'], summary['error_summary'])
|
|
71
|
+
.add_instruction("TASK: Fix the #{type} failures identified above.")
|
|
72
|
+
|
|
73
|
+
builder.add_instruction('Fix ONLY the first offense listed.') if fix_first_only
|
|
74
|
+
|
|
75
|
+
builder.add_instruction("You MUST provide JSON with 'explanation' and 'patches' (with 'file' and 'content' fields).")
|
|
76
|
+
.add_files(summary['files'])
|
|
77
|
+
.build
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def apply_fix_with_fallbacks(fix_prompt, selection)
|
|
81
|
+
fallback = EngineChain.build_fallback(selection[:engine] || :claude)
|
|
82
|
+
adapter_opts = { model: selection[:model], fork_session: true, resume: true }
|
|
83
|
+
|
|
84
|
+
raw = fallback[:chain].call_fix(fix_prompt, adapter_opts, total: fallback[:size]) do |engine|
|
|
85
|
+
@spinner.update(title: checkpoint_message(engine))
|
|
86
|
+
end
|
|
87
|
+
parse_json(raw)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def parse_json(raw)
|
|
91
|
+
json_str = raw.match(/```(?:json)?\s*(.*?)\s*```/m)&.captures&.first || raw
|
|
92
|
+
JSON.parse(json_str)
|
|
93
|
+
rescue JSON::ParserError => e
|
|
94
|
+
puts "\n⚠️ Failed to parse valid JSON from the AI engine's response."
|
|
95
|
+
puts "--- Raw Output ---\n#{raw}\n----------------"
|
|
96
|
+
raise e
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def apply_patches(result)
|
|
100
|
+
result['patches'].each do |patch|
|
|
101
|
+
path = File.expand_path(patch['file'], Dir.pwd)
|
|
102
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
103
|
+
File.write(path, patch['content'])
|
|
104
|
+
puts "Applied fix to #{patch['file']} ✅"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def run_verify(verify_command)
|
|
109
|
+
@spinner.update(title: 'Verifying fix...')
|
|
110
|
+
result = nil
|
|
111
|
+
@spinner.run { result = TerminalRunner.run(verify_command) }
|
|
112
|
+
result
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def handle_verification(verify_result, type)
|
|
116
|
+
if verify_result[:exit_status].zero?
|
|
117
|
+
puts "Fix successful! #{type.to_s.capitalize} issues resolved. ✅"
|
|
118
|
+
true
|
|
119
|
+
else
|
|
120
|
+
puts "Fix failed. #{type.to_s.capitalize} issues still persist. ❌"
|
|
121
|
+
false
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def checkpoint_message(engine)
|
|
126
|
+
case engine.to_s
|
|
127
|
+
when 'claude' then 'Leveraging Claude auto-checkpoint...'
|
|
128
|
+
when 'codex' then 'Leveraging Codex session persistence...'
|
|
129
|
+
else "Ensuring state persistence for #{engine}..."
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -6,7 +6,7 @@ module Ares
|
|
|
6
6
|
CONFIDENCE_THRESHOLD = 0.7
|
|
7
7
|
|
|
8
8
|
def self.select(plan)
|
|
9
|
-
|
|
9
|
+
config = ConfigManager.load_models
|
|
10
10
|
|
|
11
11
|
task_type = plan['task_type'] || 'refactor'
|
|
12
12
|
confidence = plan['confidence'] || 1.0
|
|
@@ -15,7 +15,7 @@ module Ares
|
|
|
15
15
|
return { engine: :claude, model: 'opus' } if confidence < CONFIDENCE_THRESHOLD || plan['risk_level'] == 'high'
|
|
16
16
|
|
|
17
17
|
# Use string key lookup as ConfigManager returns keys as strings sometimes
|
|
18
|
-
rule =
|
|
18
|
+
rule = config[task_type.to_sym] || config[:refactor]
|
|
19
19
|
engine = rule[:engine].to_sym
|
|
20
20
|
|
|
21
21
|
# Safety: restrict Ollama from code-modifying tasks if configured incorrectly
|
|
@@ -8,8 +8,8 @@ require_relative '../ollama_client_factory'
|
|
|
8
8
|
module Ares
|
|
9
9
|
module Runtime
|
|
10
10
|
class OllamaPlanner
|
|
11
|
-
PLANNER_TIMEOUT =
|
|
12
|
-
HARD_TIMEOUT =
|
|
11
|
+
PLANNER_TIMEOUT = 60 # Longer once health is verified
|
|
12
|
+
HARD_TIMEOUT = 65
|
|
13
13
|
|
|
14
14
|
def initialize(healthy: true)
|
|
15
15
|
@healthy = healthy
|
data/lib/ares/runtime/router.rb
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'diagnostic_runner'
|
|
4
|
+
|
|
3
5
|
module Ares
|
|
4
6
|
module Runtime
|
|
5
7
|
class Router
|
|
8
|
+
SHORTCUT_PATTERNS = {
|
|
9
|
+
/\A(run\s+|check\s+)?(test|rspec|fix|diagnostic)(s|ing)?\s*\z/i => :run_test_diagnostic,
|
|
10
|
+
/\A(run\s+|check\s+)?(syntax|compile)(\s+check)?\s*\z/i => :run_syntax_check,
|
|
11
|
+
/\A(run\s+|check\s+)?(lint|format|style)(ting|ing|s)?\s*\z/i => :run_lint
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
6
14
|
def initialize
|
|
7
15
|
@core = CoreSubsystem.new
|
|
8
16
|
end
|
|
@@ -12,6 +20,7 @@ module Ares
|
|
|
12
20
|
check_quota!
|
|
13
21
|
|
|
14
22
|
@spinner = TTY::Spinner.new('[:spinner] :title', format: :dots)
|
|
23
|
+
@diagnostic = DiagnosticRunner.new(core: @core, spinner: @spinner)
|
|
15
24
|
|
|
16
25
|
shortcut_result = match_shortcut_task(task, options)
|
|
17
26
|
return shortcut_result if shortcut_result
|
|
@@ -28,112 +37,15 @@ module Ares
|
|
|
28
37
|
private
|
|
29
38
|
|
|
30
39
|
def run_test_diagnostic(options)
|
|
31
|
-
|
|
40
|
+
@diagnostic.run_tests(options)
|
|
32
41
|
end
|
|
33
42
|
|
|
34
43
|
def run_syntax_check(options)
|
|
35
|
-
|
|
36
|
-
cmd = "ruby -e 'Dir.glob(\"{lib,bin,exe,spec}/**/*.rb\").each { |f| (puts \"Checking \#{f}\"; system(\"ruby -c \#{f}\")) or exit(1) }'"
|
|
37
|
-
run_diagnostic_loop(cmd, options.merge(type: :syntax, title: 'Checking syntax'))
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def run_diagnostic_loop(command, options)
|
|
41
|
-
title = options[:title] || 'Running verification'
|
|
42
|
-
@spinner.update(title: "#{title}...")
|
|
43
|
-
result = nil
|
|
44
|
-
@spinner.run { result = TerminalRunner.run(command) }
|
|
45
|
-
|
|
46
|
-
if result[:exit_status].zero?
|
|
47
|
-
puts "#{title} passed! ✅"
|
|
48
|
-
return true
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
type = options[:type] || :test
|
|
52
|
-
@spinner.update(title: "#{title} failed. Summarizing diagnostic output...")
|
|
53
|
-
summary = nil
|
|
54
|
-
|
|
55
|
-
@spinner.run do
|
|
56
|
-
# Try fast-path regex/JSON parsing first
|
|
57
|
-
summary = DiagnosticParser.parse(result[:output], type: type)
|
|
58
|
-
|
|
59
|
-
# Fallback to LLM only if fast-path failed to identify files
|
|
60
|
-
if summary['files'].empty? || summary['failed_items'].empty?
|
|
61
|
-
@spinner.update(title: "#{title} failed. LLM Fallback (Slow)...")
|
|
62
|
-
summary = @core.tiny_processor.summarize_output(result[:output], type: type)
|
|
63
|
-
end
|
|
64
|
-
rescue StandardError => e
|
|
65
|
-
@spinner.update(title: "#{title} failed. Error in fast-path: #{e.message}. LLM Fallback...")
|
|
66
|
-
summary = @core.tiny_processor.summarize_output(result[:output], type: type)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
puts "\n--- Diagnostic Summary (#{type.to_s.upcase}) ---"
|
|
70
|
-
table = TTY::Table.new(header: %w[Attribute Value])
|
|
71
|
-
table << ['Failed Items', Array(summary['failed_items'] || summary['failed_tests']).join("\n")]
|
|
72
|
-
table << ['Error Summary', summary['error_summary']]
|
|
73
|
-
puts table.render(:unicode, multiline: true)
|
|
74
|
-
|
|
75
|
-
if options[:dry_run]
|
|
76
|
-
puts 'Dry run: skipping escalation.'
|
|
77
|
-
return false
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Escalate to executor for fix
|
|
81
|
-
if type == :lint
|
|
82
|
-
escalate_lint_one_at_a_time(summary, options.merge(verify_command: command))
|
|
83
|
-
else
|
|
84
|
-
escalate_to_executor(summary, options.merge(verify_command: command))
|
|
85
|
-
end
|
|
44
|
+
@diagnostic.run_syntax_check(options)
|
|
86
45
|
end
|
|
87
46
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
current_summary = summary
|
|
91
|
-
|
|
92
|
-
max_iterations.times do |iteration|
|
|
93
|
-
puts "\n--- Fix iteration #{iteration + 1}/#{max_iterations} ---" if iteration.positive?
|
|
94
|
-
|
|
95
|
-
success = escalate_to_executor(current_summary, options.merge(fix_first_only: true))
|
|
96
|
-
return true if success
|
|
97
|
-
|
|
98
|
-
@spinner.update(title: 'Re-running RuboCop...')
|
|
99
|
-
verify_result = nil
|
|
100
|
-
@spinner.run { verify_result = TerminalRunner.run(options[:verify_command]) }
|
|
101
|
-
|
|
102
|
-
return false if verify_result[:exit_status].zero?
|
|
103
|
-
|
|
104
|
-
@spinner.update(title: 'Summarizing remaining offenses...')
|
|
105
|
-
current_summary = nil
|
|
106
|
-
@spinner.run { current_summary = @core.tiny_processor.summarize_output(verify_result[:output], type: :lint) }
|
|
107
|
-
|
|
108
|
-
puts "\n--- Remaining offenses ---"
|
|
109
|
-
table = TTY::Table.new(header: %w[Attribute Value])
|
|
110
|
-
table << ['Failed Items', Array(current_summary['failed_items']).join("\n")]
|
|
111
|
-
table << ['Error Summary', current_summary['error_summary']]
|
|
112
|
-
puts table.render(:unicode, multiline: true)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
puts "\nReached max iterations (#{max_iterations}). Some offenses may remain."
|
|
116
|
-
false
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def escalate_to_executor(summary, options)
|
|
120
|
-
type = options[:type] || :test
|
|
121
|
-
verify_command = options[:verify_command] || 'bundle exec rspec'
|
|
122
|
-
fix_prompt = generate_fix_prompt(summary, options)
|
|
123
|
-
|
|
124
|
-
selection = ModelSelector.select({ 'task_type' => 'refactor', 'risk_level' => 'medium' })
|
|
125
|
-
puts "Selected Engine for fix: #{selection[:engine]} (#{selection[:model] || 'default'})"
|
|
126
|
-
|
|
127
|
-
result = apply_fix_with_fallbacks(fix_prompt, selection)
|
|
128
|
-
return false unless result
|
|
129
|
-
|
|
130
|
-
apply_patches(result) if result['patches']&.any?
|
|
131
|
-
|
|
132
|
-
@spinner.update(title: 'Verifying fix...')
|
|
133
|
-
verify_result = nil
|
|
134
|
-
@spinner.run { verify_result = TerminalRunner.run(verify_command) }
|
|
135
|
-
|
|
136
|
-
handle_verification_result(verify_result, type)
|
|
47
|
+
def run_lint(options)
|
|
48
|
+
@diagnostic.run_lint(options)
|
|
137
49
|
end
|
|
138
50
|
|
|
139
51
|
def check_quota!
|
|
@@ -144,27 +56,20 @@ module Ares
|
|
|
144
56
|
end
|
|
145
57
|
|
|
146
58
|
def match_shortcut_task(task, options)
|
|
147
|
-
|
|
148
|
-
return
|
|
59
|
+
SHORTCUT_PATTERNS.each do |pattern, method|
|
|
60
|
+
return send(method, options) if task.match?(pattern)
|
|
149
61
|
end
|
|
150
|
-
return run_syntax_check(options) if task.match?(/\A(run\s+|check\s+)?(syntax|compile)(\s+check)?\s*\z/i)
|
|
151
|
-
return run_lint(options) if task.match?(/\A(run\s+|check\s+)?(lint|format|style)(ting|ing|s)?\s*\z/i)
|
|
152
|
-
|
|
153
62
|
nil
|
|
154
63
|
end
|
|
155
64
|
|
|
156
65
|
def plan_task(task)
|
|
157
|
-
plan = nil
|
|
158
66
|
@spinner.update(title: 'Planning task...')
|
|
159
|
-
@spinner.run {
|
|
160
|
-
plan
|
|
67
|
+
@spinner.run { @core.planner.plan(task) }
|
|
161
68
|
end
|
|
162
69
|
|
|
163
70
|
def select_model_for_plan(plan)
|
|
164
|
-
selection = nil
|
|
165
71
|
@spinner.update(title: 'Selecting optimal model...')
|
|
166
|
-
@spinner.run {
|
|
167
|
-
selection
|
|
72
|
+
@spinner.run { ModelSelector.select(plan) }
|
|
168
73
|
end
|
|
169
74
|
|
|
170
75
|
def handle_low_confidence(selection, plan)
|
|
@@ -188,16 +93,15 @@ module Ares
|
|
|
188
93
|
|
|
189
94
|
def execute_engine_task(task, plan, selection, options)
|
|
190
95
|
puts "Engine Selected: #{selection[:engine]} (#{selection[:model] || 'default'})"
|
|
191
|
-
|
|
96
|
+
if options[:dry_run]
|
|
97
|
+
puts '--- DRY RUN MODE ---'
|
|
98
|
+
return
|
|
99
|
+
end
|
|
192
100
|
|
|
193
101
|
@core.logger.log_task(task, plan, selection)
|
|
194
102
|
GitManager.create_branch(@core.logger.task_id, task) if options[:git]
|
|
195
103
|
|
|
196
|
-
|
|
197
|
-
initial_engine = selection[:engine]&.to_s || 'claude'
|
|
198
|
-
fallback_chain = ([initial_engine] + (capable_engines - [initial_engine])).uniq
|
|
199
|
-
|
|
200
|
-
chain = EngineChain.build(fallback_chain)
|
|
104
|
+
fallback = EngineChain.build_fallback(selection[:engine] || :claude)
|
|
201
105
|
prompt = PromptBuilder.new
|
|
202
106
|
.add_context(ContextLoader.load)
|
|
203
107
|
.add_task(task)
|
|
@@ -205,92 +109,13 @@ module Ares
|
|
|
205
109
|
|
|
206
110
|
adapter_opts = { model: selection[:model], fork_session: true, resume: true, cloud: options[:cloud] }
|
|
207
111
|
|
|
208
|
-
output = chain.call(prompt, adapter_opts, total:
|
|
112
|
+
output = fallback[:chain].call(prompt, adapter_opts, total: fallback[:size])
|
|
209
113
|
@core.logger.log_result(output)
|
|
210
114
|
|
|
211
115
|
GitManager.commit_changes(@core.logger.task_id, task) if options[:git]
|
|
212
116
|
puts output
|
|
213
117
|
end
|
|
214
118
|
|
|
215
|
-
def generate_fix_prompt(summary, options)
|
|
216
|
-
type = options[:type] || :test
|
|
217
|
-
|
|
218
|
-
builder = PromptBuilder.new
|
|
219
|
-
.add_context(ContextLoader.load)
|
|
220
|
-
.add_diagnostic(type, summary['failed_items'] || summary['failed_tests'], summary['error_summary'])
|
|
221
|
-
.add_instruction("TASK: Fix the #{type} failures identifying above.")
|
|
222
|
-
|
|
223
|
-
builder.add_instruction('Fix ONLY the first offense listed.') if options[:fix_first_only]
|
|
224
|
-
|
|
225
|
-
builder.add_instruction("You MUST provide JSON with 'explanation' and 'patches' (with 'file' and 'content' fields).")
|
|
226
|
-
.add_files(summary['files'])
|
|
227
|
-
.build
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def apply_fix_with_fallbacks(fix_prompt, selection)
|
|
231
|
-
capable_engines = %w[claude codex cursor]
|
|
232
|
-
initial_engine = selection[:engine]&.to_s || 'claude'
|
|
233
|
-
fallback_chain = ([initial_engine] + (capable_engines - [initial_engine])).uniq
|
|
234
|
-
|
|
235
|
-
chain = EngineChain.build(fallback_chain)
|
|
236
|
-
adapter_opts = { model: selection[:model], fork_session: true, resume: true }
|
|
237
|
-
|
|
238
|
-
raw = chain.call_fix(fix_prompt, adapter_opts, total: fallback_chain.size) do |current_engine|
|
|
239
|
-
create_checkpoint(current_engine)
|
|
240
|
-
end
|
|
241
|
-
parse_robust_json(raw)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def parse_robust_json(raw)
|
|
245
|
-
json_str = raw.match(/```(?:json)?\s*(.*?)\s*```/m)&.captures&.first || raw
|
|
246
|
-
begin
|
|
247
|
-
JSON.parse(json_str)
|
|
248
|
-
rescue JSON::ParserError => e
|
|
249
|
-
puts "\n⚠️ Failed to parse valid JSON from the AI engine's response."
|
|
250
|
-
puts "--- Raw Output ---\n#{raw}\n----------------"
|
|
251
|
-
raise e
|
|
252
|
-
end
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
def apply_patches(result)
|
|
256
|
-
result['patches'].each do |patch|
|
|
257
|
-
path = File.expand_path(patch['file'], Dir.pwd)
|
|
258
|
-
FileUtils.mkdir_p(File.dirname(path))
|
|
259
|
-
File.write(path, patch['content'])
|
|
260
|
-
puts "Applied fix to #{patch['file']} ✅"
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
def handle_verification_result(verify_result, type)
|
|
265
|
-
if verify_result[:exit_status].zero?
|
|
266
|
-
puts "Fix successful! #{type.to_s.capitalize} issues resolved. ✅"
|
|
267
|
-
true
|
|
268
|
-
else
|
|
269
|
-
puts "Fix failed. #{type.to_s.capitalize} issues still persist. ❌"
|
|
270
|
-
false
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
def create_checkpoint(engine)
|
|
275
|
-
# Checkpointing logic varies by engine.
|
|
276
|
-
# For now, we ensure a git-based safety net if not in a git repo or if native fails.
|
|
277
|
-
case engine
|
|
278
|
-
when :claude
|
|
279
|
-
# Claude Code does automatic checkpointing on every prompt.
|
|
280
|
-
@spinner.update(title: 'Leveraging Claude auto-checkpoint...')
|
|
281
|
-
when :codex
|
|
282
|
-
# Codex sessions are automatically persisted.
|
|
283
|
-
@spinner.update(title: 'Leveraging Codex session persistence...')
|
|
284
|
-
else
|
|
285
|
-
# Fallback to git stash or similar if we wanted a hard checkpoint,
|
|
286
|
-
# but since we are often on a task branch, git is our checkpoint.
|
|
287
|
-
@spinner.update(title: "Ensuring state persistence for #{engine}...")
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def run_lint(options)
|
|
292
|
-
run_diagnostic_loop('bundle exec rubocop -A', options.merge(type: :lint, title: 'Running RuboCop'))
|
|
293
|
-
end
|
|
294
119
|
end
|
|
295
120
|
end
|
|
296
121
|
end
|
data/lib/ares/runtime/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ares-runtime
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shubham
|
|
@@ -192,8 +192,10 @@ files:
|
|
|
192
192
|
- lib/ares/runtime/context_loader.rb
|
|
193
193
|
- lib/ares/runtime/core_subsystem.rb
|
|
194
194
|
- lib/ares/runtime/diagnostic_parser.rb
|
|
195
|
+
- lib/ares/runtime/diagnostic_runner.rb
|
|
195
196
|
- lib/ares/runtime/doctor.rb
|
|
196
197
|
- lib/ares/runtime/engine_chain.rb
|
|
198
|
+
- lib/ares/runtime/fix_applicator.rb
|
|
197
199
|
- lib/ares/runtime/git_manager.rb
|
|
198
200
|
- lib/ares/runtime/initializer.rb
|
|
199
201
|
- lib/ares/runtime/logs_cli.rb
|