code_healer 0.1.14 → 0.1.16
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/CHANGELOG.md +25 -0
- data/config/routes.rb +16 -0
- data/lib/code_healer/controllers/dashboard_controller.rb +63 -30
- data/lib/code_healer/engine.rb +12 -0
- data/lib/code_healer/healing_job.rb +82 -2
- data/lib/code_healer/models/healing_metric.rb +15 -4
- data/lib/code_healer/services/metrics_collector.rb +4 -4
- data/lib/code_healer/version.rb +1 -1
- data/lib/code_healer/views/dashboard/healing_details.html.erb +356 -0
- data/lib/code_healer/views/dashboard/index.html.erb +370 -123
- data/lib/code_healer.rb +1 -23
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc8423f76ddef06527d9b8e70a219d0e958ee89e3f352d6bd816697d7d7bb49d
|
4
|
+
data.tar.gz: c37a4b4101bcda99e166364629d6075c64244f4ef9d04e12b22dccbc5a176f61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bea3163ae16b27e735c3aa21c89f2c7a4ffcaf7245c4eca00c39ea17896370ac5870116fa7d6f0499f8aa72fe3bf837d9d3164e4e27f175522dfc9998bc568ff
|
7
|
+
data.tar.gz: ecf0d683184721228fb6dc3e9d444bac88b2bc5aceb2488890af4356700b5442baf79f058eaee4169e103574eed690b905bc49e8acea4d6c1ea4be6cfc9d2268
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.1.16] - 2025-08-21
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Processing state: dashboard shows in-flight healings as "processing" (no longer treated as failed)
|
12
|
+
- API metrics payload now includes `status` and timezone-aware `created_at`
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- API endpoints default to JSON (`/code_healer/api/...`) to avoid template lookup
|
16
|
+
- Compact metrics JSON for dashboard list rendering
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
- Timezone correctness: all metrics timestamps use `Time.zone`
|
20
|
+
- Daily trend counts computed with timezone-aware day buckets
|
21
|
+
|
22
|
+
## [0.1.15] - 2025-08-21
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
- **Dashboard Template Loading**: Fixed template loading issues by explicitly specifying view paths
|
26
|
+
- **Engine Views Configuration**: Properly configured engine views path to resolve template missing errors
|
27
|
+
- **Controller Template Rendering**: Updated render calls to use explicit template paths
|
28
|
+
|
29
|
+
### Changed
|
30
|
+
- **Template Rendering**: Changed from implicit template rendering to explicit template path specification
|
31
|
+
- **View Path Configuration**: Enhanced engine configuration for proper view loading
|
32
|
+
|
8
33
|
## [0.1.14] - 2025-08-21
|
9
34
|
|
10
35
|
### Added
|
data/config/routes.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
CodeHealer::Engine.routes.draw do
|
2
|
+
get '/dashboard', to: 'dashboard#index'
|
3
|
+
get '/dashboard/metrics', to: 'dashboard#metrics'
|
4
|
+
get '/dashboard/trends', to: 'dashboard#trends'
|
5
|
+
get '/dashboard/performance', to: 'dashboard#performance'
|
6
|
+
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
7
|
+
|
8
|
+
# API endpoints (JSON only) - default to JSON format
|
9
|
+
scope path: '/api', defaults: { format: :json } do
|
10
|
+
get '/dashboard/summary', to: 'dashboard#summary'
|
11
|
+
get '/dashboard/metrics', to: 'dashboard#metrics'
|
12
|
+
get '/dashboard/trends', to: 'dashboard#trends'
|
13
|
+
get '/dashboard/performance', to: 'dashboard#performance'
|
14
|
+
get '/dashboard/healing/:healing_id', to: 'dashboard#healing_details'
|
15
|
+
end
|
16
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module CodeHealer
|
2
2
|
class DashboardController < ActionController::Base
|
3
|
+
# Set the view path to look in the engine's views directory
|
4
|
+
self.view_paths = ["#{CodeHealer::Engine.root}/lib/code_healer/views"]
|
5
|
+
|
3
6
|
def index
|
4
7
|
@summary = MetricsCollector.dashboard_summary
|
5
8
|
@recent_healings = HealingMetric.order(created_at: :desc).limit(10)
|
6
9
|
|
7
10
|
respond_to do |format|
|
8
|
-
format.html {
|
11
|
+
format.html { render template: "dashboard/index" }
|
9
12
|
format.json { render json: @summary }
|
10
13
|
end
|
11
14
|
end
|
@@ -18,32 +21,73 @@ module CodeHealer
|
|
18
21
|
@metrics = @metrics.by_evolution_method(params[:evolution_method]) if params[:evolution_method].present?
|
19
22
|
@metrics = @metrics.by_ai_provider(params[:ai_provider]) if params[:ai_provider].present?
|
20
23
|
@metrics = @metrics.recent(params[:days].to_i) if params[:days].present?
|
24
|
+
@metrics = @metrics.limit(params[:limit].to_i) if params[:limit].present?
|
21
25
|
|
26
|
+
# Render compact JSON suitable for dashboard list
|
27
|
+
payload = @metrics.map do |m|
|
28
|
+
{
|
29
|
+
healing_id: m.healing_id,
|
30
|
+
class_name: m.class_name,
|
31
|
+
method_name: m.method_name,
|
32
|
+
error_class: m.error_class,
|
33
|
+
error_message: m.error_message,
|
34
|
+
healing_successful: m.healing_successful,
|
35
|
+
status: m.display_status,
|
36
|
+
created_at: m.created_at.in_time_zone(Time.zone),
|
37
|
+
healing_branch: m.healing_branch,
|
38
|
+
pull_request_url: m.pull_request_url
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
22
42
|
respond_to do |format|
|
23
|
-
format.
|
24
|
-
format.
|
43
|
+
format.json { render json: payload }
|
44
|
+
format.any { render json: payload }
|
25
45
|
end
|
26
46
|
end
|
27
47
|
|
28
48
|
def healing_details
|
29
49
|
@healing = HealingMetric.find_by(healing_id: params[:healing_id])
|
30
50
|
|
31
|
-
|
32
|
-
render
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
respond_to do |format|
|
52
|
+
format.html { render template: "dashboard/healing_details" }
|
53
|
+
format.json do
|
54
|
+
if @healing
|
55
|
+
render json: {
|
56
|
+
healing: @healing,
|
57
|
+
timing: {
|
58
|
+
total_duration: @healing.duration_seconds,
|
59
|
+
ai_processing: @healing.ai_processing_seconds,
|
60
|
+
git_operations: @healing.git_operations_seconds
|
61
|
+
},
|
62
|
+
status: {
|
63
|
+
success: @healing.success_status,
|
64
|
+
evolution_method: @healing.evolution_method_display,
|
65
|
+
ai_provider: @healing.ai_provider_display
|
66
|
+
}
|
67
|
+
}
|
68
|
+
else
|
69
|
+
render json: { error: 'Healing not found' }, status: :not_found
|
70
|
+
end
|
71
|
+
end
|
72
|
+
format.any do
|
73
|
+
if @healing
|
74
|
+
render json: {
|
75
|
+
healing: @healing,
|
76
|
+
timing: {
|
77
|
+
total_duration: @healing.duration_seconds,
|
78
|
+
ai_processing: @healing.ai_processing_seconds,
|
79
|
+
git_operations: @healing.git_operations_seconds
|
80
|
+
},
|
81
|
+
status: {
|
82
|
+
success: @healing.success_status,
|
83
|
+
evolution_method: @healing.evolution_method_display,
|
84
|
+
ai_provider: @healing.ai_provider_display
|
85
|
+
}
|
86
|
+
}
|
87
|
+
else
|
88
|
+
render json: { error: 'Healing not found' }, status: :not_found
|
89
|
+
end
|
90
|
+
end
|
47
91
|
end
|
48
92
|
end
|
49
93
|
|
@@ -77,16 +121,5 @@ module CodeHealer
|
|
77
121
|
def summary
|
78
122
|
render json: MetricsCollector.dashboard_summary
|
79
123
|
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def render_dashboard
|
84
|
-
# This will be replaced with actual dashboard view
|
85
|
-
render plain: "CodeHealer Dashboard - Coming Soon!\n\n" \
|
86
|
-
"Total Healings: #{@summary[:total_healing]}\n" \
|
87
|
-
"Success Rate: #{@summary[:success_rate]}%\n" \
|
88
|
-
"Healings Today: #{@summary[:healings_today]}\n" \
|
89
|
-
"Healings This Week: #{@summary[:healings_this_week]}"
|
90
|
-
end
|
91
124
|
end
|
92
125
|
end
|
data/lib/code_healer/engine.rb
CHANGED
@@ -7,6 +7,11 @@ module CodeHealer
|
|
7
7
|
app.config.autoload_paths += %W(#{config.root}/lib/code_healer)
|
8
8
|
end
|
9
9
|
|
10
|
+
# Add views path to the main app
|
11
|
+
initializer "code_healer.add_views_path" do |app|
|
12
|
+
app.config.paths["app/views"] << "#{config.root}/lib/code_healer/views"
|
13
|
+
end
|
14
|
+
|
10
15
|
# Copy migrations
|
11
16
|
initializer "code_healer.copy_migrations" do |app|
|
12
17
|
if app.root.to_s.match root.to_s
|
@@ -15,5 +20,12 @@ module CodeHealer
|
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|
23
|
+
|
24
|
+
# Ensure the engine is properly loaded
|
25
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
26
|
+
config.eager_load_paths += %W(#{config.root}/lib)
|
27
|
+
|
28
|
+
# Configure the engine's own paths
|
29
|
+
config.paths["app/views"] = ["#{config.root}/lib/code_healer/views"]
|
18
30
|
end
|
19
31
|
end
|
@@ -15,42 +15,106 @@ module CodeHealer
|
|
15
15
|
puts "🚀 [HEALING_JOB] Parsed args - Error: #{error.class}, Class: #{class_name}, Method: #{method_name}, Evolution: #{evolution_method}"
|
16
16
|
puts "🚀 [HEALING_JOB] Backtrace length: #{backtrace&.length || 0}"
|
17
17
|
|
18
|
+
# Track start metric
|
19
|
+
healing_id = MetricsCollector.generate_healing_id
|
20
|
+
MetricsCollector.track_healing_start(
|
21
|
+
healing_id,
|
22
|
+
class_name.to_s,
|
23
|
+
method_name.to_s,
|
24
|
+
error.class.name,
|
25
|
+
error.message,
|
26
|
+
nil # file_path not available in this flow
|
27
|
+
)
|
28
|
+
MetricsCollector.track_error_occurrence(healing_id, Time.current)
|
29
|
+
|
18
30
|
puts "🚀 Evolution Job Started: #{class_name}##{method_name}"
|
19
31
|
|
20
|
-
|
32
|
+
puts "🏥 [HEALING_JOB] About to create isolated healing workspace..."
|
21
33
|
# Create isolated healing workspace
|
22
34
|
workspace_path = create_healing_workspace(class_name, method_name)
|
35
|
+
MetricsCollector.track_workspace_creation(healing_id, workspace_path)
|
23
36
|
puts "🏥 [HEALING_JOB] Workspace created: #{workspace_path}"
|
24
37
|
|
38
|
+
ai_time_ms = nil
|
39
|
+
git_time_ms = nil
|
40
|
+
test_success = false
|
41
|
+
overall_success = false
|
42
|
+
syntax_valid = false
|
43
|
+
failure_reason = nil
|
44
|
+
|
25
45
|
begin
|
26
46
|
puts "🔧 [HEALING_JOB] About to apply fixes in isolated environment..."
|
27
47
|
# Apply fixes in isolated environment
|
48
|
+
ai_started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
28
49
|
success = apply_fixes_in_workspace(workspace_path, error, class_name, method_name, evolution_method)
|
50
|
+
ai_time_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - ai_started_at) * 1000).round
|
29
51
|
|
30
52
|
if success
|
53
|
+
# Record AI processing success
|
54
|
+
MetricsCollector.track_ai_processing(
|
55
|
+
healing_id,
|
56
|
+
evolution_method,
|
57
|
+
ai_provider_for(evolution_method),
|
58
|
+
'success'
|
59
|
+
)
|
60
|
+
|
31
61
|
# Test fixes in isolated environment
|
32
62
|
test_success = CodeHealer::HealingWorkspaceManager.test_fixes_in_workspace(workspace_path)
|
33
63
|
|
34
64
|
if test_success
|
35
|
-
|
65
|
+
syntax_valid = true
|
66
|
+
# Create healing branch from workspace
|
67
|
+
git_started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
36
68
|
healing_branch = CodeHealer::HealingWorkspaceManager.create_healing_branch(
|
37
69
|
Rails.root.to_s,
|
38
70
|
workspace_path,
|
39
71
|
CodeHealer::ConfigManager.git_settings['pr_target_branch'] || 'main'
|
40
72
|
)
|
73
|
+
git_time_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - git_started_at) * 1000).round
|
41
74
|
|
42
75
|
if healing_branch
|
76
|
+
MetricsCollector.track_git_operations(
|
77
|
+
healing_id,
|
78
|
+
healing_branch,
|
79
|
+
nil, # PR URL (skipped in workspace flow)
|
80
|
+
false # pr_created
|
81
|
+
)
|
82
|
+
overall_success = true
|
43
83
|
puts "✅ Fixes applied, tested, and merged successfully! Branch: #{healing_branch}"
|
44
84
|
else
|
85
|
+
overall_success = false
|
86
|
+
failure_reason ||= 'healing_branch_creation_failed'
|
45
87
|
puts "⚠️ Fixes applied and tested, but merge failed"
|
46
88
|
end
|
47
89
|
else
|
90
|
+
overall_success = false
|
91
|
+
syntax_valid = false
|
92
|
+
failure_reason ||= 'workspace_tests_failed_or_syntax_error'
|
48
93
|
puts "⚠️ Fixes applied but failed tests, not merging back"
|
49
94
|
end
|
50
95
|
else
|
96
|
+
overall_success = false
|
97
|
+
failure_reason ||= 'ai_evolution_failed'
|
98
|
+
# Record AI processing failure
|
99
|
+
MetricsCollector.track_ai_failure(
|
100
|
+
healing_id,
|
101
|
+
evolution_method,
|
102
|
+
ai_provider_for(evolution_method),
|
103
|
+
failure_reason
|
104
|
+
)
|
51
105
|
puts "❌ Failed to apply fixes in workspace"
|
52
106
|
end
|
53
107
|
ensure
|
108
|
+
# Persist timing metrics if captured
|
109
|
+
MetricsCollector.track_timing(healing_id, ai_time_ms, git_time_ms) if ai_time_ms || git_time_ms
|
110
|
+
# Mark completion
|
111
|
+
MetricsCollector.track_healing_completion(
|
112
|
+
healing_id,
|
113
|
+
overall_success,
|
114
|
+
test_success,
|
115
|
+
syntax_valid,
|
116
|
+
failure_reason
|
117
|
+
)
|
54
118
|
# Clean up workspace
|
55
119
|
cleanup_workspace(workspace_path)
|
56
120
|
end
|
@@ -82,6 +146,19 @@ module CodeHealer
|
|
82
146
|
end
|
83
147
|
end
|
84
148
|
|
149
|
+
def ai_provider_for(evolution_method)
|
150
|
+
case evolution_method
|
151
|
+
when 'claude_code_terminal'
|
152
|
+
'claude'
|
153
|
+
when 'api'
|
154
|
+
'openai'
|
155
|
+
when 'hybrid'
|
156
|
+
'hybrid'
|
157
|
+
else
|
158
|
+
'unknown'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
85
162
|
def create_healing_workspace(class_name, method_name)
|
86
163
|
puts "🏥 Creating isolated healing workspace for #{class_name}##{method_name}"
|
87
164
|
|
@@ -144,6 +221,9 @@ module CodeHealer
|
|
144
221
|
error, class_name, method_name
|
145
222
|
)
|
146
223
|
|
224
|
+
# Optionally record business context used
|
225
|
+
# MetricsCollector.track_business_context(healing_id, business_context) # healing_id not accessible here
|
226
|
+
|
147
227
|
puts "📋 Business context loaded for API evolution"
|
148
228
|
|
149
229
|
# Change to workspace directory for API operations
|
@@ -60,13 +60,15 @@ module CodeHealer
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def daily_healing_trend(days = 30)
|
63
|
-
#
|
63
|
+
# Timezone-aware daily buckets using application time zone
|
64
64
|
start_date = days.days.ago.to_date
|
65
|
-
end_date =
|
66
|
-
|
65
|
+
end_date = Time.zone.today
|
66
|
+
|
67
67
|
trend_data = {}
|
68
68
|
(start_date..end_date).each do |date|
|
69
|
-
|
69
|
+
day_start = Time.zone.parse(date.to_s).beginning_of_day
|
70
|
+
day_end = day_start.end_of_day
|
71
|
+
count = where(created_at: day_start..day_end).count
|
70
72
|
trend_data[date.strftime('%Y-%m-%d')] = count
|
71
73
|
end
|
72
74
|
trend_data
|
@@ -86,6 +88,14 @@ module CodeHealer
|
|
86
88
|
end
|
87
89
|
|
88
90
|
# Instance methods
|
91
|
+
def processing?
|
92
|
+
healing_completed_at.nil?
|
93
|
+
end
|
94
|
+
|
95
|
+
def display_status
|
96
|
+
return 'processing' if processing?
|
97
|
+
healing_successful ? 'success' : 'failed'
|
98
|
+
end
|
89
99
|
def duration_seconds
|
90
100
|
return nil unless total_duration_ms
|
91
101
|
(total_duration_ms / 1000.0).round(2)
|
@@ -102,6 +112,7 @@ module CodeHealer
|
|
102
112
|
end
|
103
113
|
|
104
114
|
def success_status
|
115
|
+
return '⏳ Processing' if processing?
|
105
116
|
healing_successful ? '✅ Success' : '❌ Failed'
|
106
117
|
end
|
107
118
|
|
@@ -9,7 +9,7 @@ module CodeHealer
|
|
9
9
|
error_class: error_class,
|
10
10
|
error_message: error_message,
|
11
11
|
file_path: file_path,
|
12
|
-
healing_started_at: Time.
|
12
|
+
healing_started_at: Time.zone.now
|
13
13
|
)
|
14
14
|
|
15
15
|
metric.save!
|
@@ -66,13 +66,13 @@ module CodeHealer
|
|
66
66
|
|
67
67
|
# Calculate timing
|
68
68
|
total_duration = if metric.healing_started_at
|
69
|
-
((Time.
|
69
|
+
((Time.zone.now - metric.healing_started_at) * 1000).round
|
70
70
|
else
|
71
71
|
nil
|
72
72
|
end
|
73
73
|
|
74
74
|
metric.update!(
|
75
|
-
healing_completed_at: Time.
|
75
|
+
healing_completed_at: Time.zone.now,
|
76
76
|
total_duration_ms: total_duration,
|
77
77
|
healing_successful: success,
|
78
78
|
tests_passed: tests_passed,
|
@@ -106,7 +106,7 @@ module CodeHealer
|
|
106
106
|
metric = HealingMetric.find_by(healing_id: healing_id)
|
107
107
|
return unless metric
|
108
108
|
|
109
|
-
metric.update!(error_occurred_at: error_occurred_at)
|
109
|
+
metric.update!(error_occurred_at: error_occurred_at.in_time_zone(Time.zone))
|
110
110
|
end
|
111
111
|
|
112
112
|
# Generate unique healing ID
|
data/lib/code_healer/version.rb
CHANGED