code_healer 0.1.0

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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +70 -0
  3. data/GEM_SUMMARY.md +307 -0
  4. data/README.md +281 -0
  5. data/code_healer.gemspec +77 -0
  6. data/config/code_healer.yml.example +104 -0
  7. data/docs/INSTALLATION.md +439 -0
  8. data/examples/basic_usage.rb +160 -0
  9. data/exe/code_healer-setup +7 -0
  10. data/lib/code_healer/application_job.rb +7 -0
  11. data/lib/code_healer/business_context_analyzer.rb +464 -0
  12. data/lib/code_healer/business_context_loader.rb +273 -0
  13. data/lib/code_healer/business_context_manager.rb +297 -0
  14. data/lib/code_healer/business_logic_generator.rb +94 -0
  15. data/lib/code_healer/business_rule_applier.rb +54 -0
  16. data/lib/code_healer/claude_code_evolution_handler.rb +224 -0
  17. data/lib/code_healer/claude_error_monitor.rb +48 -0
  18. data/lib/code_healer/config_manager.rb +275 -0
  19. data/lib/code_healer/context_aware_prompt_builder.rb +153 -0
  20. data/lib/code_healer/core.rb +513 -0
  21. data/lib/code_healer/error_handler.rb +141 -0
  22. data/lib/code_healer/evolution_job.rb +99 -0
  23. data/lib/code_healer/global_handler.rb +130 -0
  24. data/lib/code_healer/healing_job.rb +167 -0
  25. data/lib/code_healer/mcp.rb +108 -0
  26. data/lib/code_healer/mcp_prompts.rb +111 -0
  27. data/lib/code_healer/mcp_server.rb +389 -0
  28. data/lib/code_healer/mcp_tools.rb +2364 -0
  29. data/lib/code_healer/pull_request_creator.rb +143 -0
  30. data/lib/code_healer/setup.rb +390 -0
  31. data/lib/code_healer/simple_evolution.rb +737 -0
  32. data/lib/code_healer/simple_global_handler.rb +122 -0
  33. data/lib/code_healer/simple_healer.rb +515 -0
  34. data/lib/code_healer/terminal_integration.rb +87 -0
  35. data/lib/code_healer/usage_analyzer.rb +92 -0
  36. data/lib/code_healer/version.rb +5 -0
  37. data/lib/code_healer.rb +67 -0
  38. metadata +411 -0
@@ -0,0 +1,54 @@
1
+ module CodeHealer
2
+ # Business Rule Applier
3
+ # Simply provides business context to AI for natural language understanding
4
+ class BusinessRuleApplier
5
+ class << self
6
+
7
+ def apply_business_rules_to_code(business_analysis, original_code, error, class_name, method_name)
8
+ puts "🔧 Providing business context to AI..."
9
+
10
+ # Extract business context from the analysis
11
+ business_context = extract_business_context(business_analysis)
12
+
13
+ if business_context.any?
14
+ puts " 📋 Found business context: #{business_context.join(', ')}"
15
+ puts " đŸŽ¯ AI will understand and apply these business rules naturally"
16
+
17
+ # Simply return the original code - let the AI handle everything
18
+ # The AI will see the business context and apply it naturally
19
+ puts " ✅ Business context provided to AI - no code manipulation needed"
20
+ original_code
21
+ else
22
+ puts " â„šī¸ No business context found"
23
+ original_code
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def extract_business_context(business_analysis)
30
+ context = []
31
+
32
+ # Extract from business rules
33
+ if business_analysis[:business_context][:business_rules]&.any?
34
+ business_analysis[:business_context][:business_rules].each do |rule|
35
+ context << rule[:content]
36
+ end
37
+ end
38
+
39
+ # Extract from domain specific rules
40
+ if business_analysis[:business_context][:domain_specific]&.any?
41
+ business_analysis[:business_context][:domain_specific].each do |domain, rules|
42
+ if rules.is_a?(Array)
43
+ context.concat(rules)
44
+ elsif rules.is_a?(Hash)
45
+ context.concat(rules.values.map(&:to_s))
46
+ end
47
+ end
48
+ end
49
+
50
+ context
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,224 @@
1
+ require 'timeout'
2
+ require 'open3'
3
+
4
+ module CodeHealer
5
+ class ClaudeCodeEvolutionHandler
6
+ class << self
7
+ def handle_error_with_claude_code(error, class_name, method_name, file_path)
8
+ puts "🤖 Claude Code Terminal Evolution Triggered!"
9
+ puts "Error: #{error.class} - #{error.message}"
10
+ puts "Class: #{class_name}, Method: #{method_name}"
11
+ puts "File: #{file_path}"
12
+
13
+ begin
14
+ # Build comprehensive prompt
15
+ prompt = BusinessContextManager.build_claude_code_prompt(
16
+ error, class_name, method_name, file_path
17
+ )
18
+
19
+ # Execute Claude Code command
20
+ success = execute_claude_code_fix(prompt, class_name, method_name)
21
+
22
+ if success
23
+ puts "✅ Claude Code evolution completed successfully!"
24
+ # Reload modified files
25
+ reload_modified_files
26
+
27
+ # 🚀 Trigger Git operations (commit, push, PR creation)
28
+ puts "🔄 Starting Git operations..."
29
+ trigger_git_operations(error, class_name, method_name, file_path)
30
+
31
+ return true
32
+ else
33
+ puts "❌ Claude Code evolution failed"
34
+ return false
35
+ end
36
+
37
+ rescue => e
38
+ puts "❌ Claude Code evolution error: #{e.message}"
39
+ puts e.backtrace.first(5)
40
+ return false
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def execute_claude_code_fix(prompt, class_name, method_name)
47
+ config = ConfigManager.claude_code_settings
48
+
49
+ # Build command
50
+ command = build_claude_command(prompt, config)
51
+
52
+ puts "🚀 Executing Claude Code fix..."
53
+ puts "Command: #{command}"
54
+ puts "Timeout: #{config['timeout']} seconds"
55
+
56
+ begin
57
+ # Execute with timeout
58
+ Timeout.timeout(config['timeout']) do
59
+ stdout, stderr, status = Open3.capture3(command)
60
+
61
+ puts "📤 Claude Code Output:"
62
+ if stdout && !stdout.empty?
63
+ puts "✅ Response received:"
64
+ puts stdout
65
+
66
+ # Check if Claude Code is asking for permission
67
+ if stdout.include?("permission") || stdout.include?("grant") || stdout.include?("edit")
68
+ puts "🔐 Claude Code needs permission to edit files"
69
+ puts "💡 Make sure to grant Edit permissions when prompted"
70
+ end
71
+
72
+ # Check if fix was applied
73
+ if stdout.include?("fix") && (stdout.include?("applied") || stdout.include?("ready"))
74
+ puts "đŸŽ¯ Fix appears to be ready - checking if files were modified..."
75
+ end
76
+ else
77
+ puts "âš ī¸ No output received from Claude Code"
78
+ end
79
+
80
+ if stderr && !stderr.empty?
81
+ puts "âš ī¸ Claude Code Warnings/Errors:"
82
+ puts stderr
83
+ end
84
+
85
+ if status.success?
86
+ puts "✅ Claude Code execution completed successfully"
87
+ return true
88
+ else
89
+ puts "❌ Claude Code execution failed with status: #{status.exitstatus}"
90
+ return false
91
+ end
92
+ end
93
+
94
+ rescue Timeout::Error
95
+ puts "⏰ Claude Code execution timed out after #{config['timeout']} seconds"
96
+ return false
97
+ rescue => e
98
+ puts "❌ Claude Code execution error: #{e.message}"
99
+ return false
100
+ end
101
+ end
102
+
103
+ def build_claude_command(prompt, config)
104
+ # Escape prompt for shell
105
+ escaped_prompt = prompt.gsub("'", "'\"'\"'")
106
+
107
+ # Build command template
108
+ command_template = config['command_template'] || "claude --code '{prompt}'"
109
+
110
+ # Replace placeholder
111
+ command = command_template.gsub('{prompt}', escaped_prompt)
112
+
113
+ # Add additional options if configured
114
+ if config['include_tests']
115
+ command += " --append-system-prompt 'Include tests when fixing the code'"
116
+ end
117
+
118
+ if config['max_file_changes']
119
+ command += " --append-system-prompt 'Limit changes to #{config['max_file_changes']} files maximum'"
120
+ end
121
+
122
+ # Add file editing permissions
123
+ command += " --permission-mode acceptEdits --allowedTools Edit"
124
+
125
+ # Add current directory access
126
+ command += " --add-dir ."
127
+
128
+ command
129
+ end
130
+
131
+ def reload_modified_files
132
+ puts "🔄 Reloading modified files..."
133
+
134
+ # Get list of recently modified files (last 5 minutes)
135
+ recent_files = get_recently_modified_files
136
+
137
+ recent_files.each do |file_path|
138
+ if file_path.include?('/app/')
139
+ begin
140
+ load file_path
141
+ puts "✅ Reloaded: #{file_path}"
142
+ rescue => e
143
+ puts "âš ī¸ Failed to reload #{file_path}: #{e.message}"
144
+ end
145
+ end
146
+ end
147
+
148
+ puts "🔄 File reloading completed"
149
+ end
150
+
151
+ def get_recently_modified_files
152
+ # Get files modified in the last 5 minutes
153
+ cutoff_time = Time.now - 300 # 5 minutes ago
154
+
155
+ files = []
156
+ Dir.glob('**/*.rb').each do |file|
157
+ if File.mtime(file) > cutoff_time
158
+ files << file
159
+ end
160
+ end
161
+
162
+ files.sort_by { |f| File.mtime(f) }.reverse
163
+ end
164
+
165
+ def log_evolution_attempt(error, class_name, method_name, success)
166
+ log_entry = {
167
+ timestamp: Time.now.iso8601,
168
+ method: 'claude_code_terminal',
169
+ error_type: error.class.name,
170
+ error_message: error.message,
171
+ class_name: class_name,
172
+ method_name: method_name,
173
+ success: success,
174
+ execution_time: Time.now
175
+ }
176
+
177
+ # Log to file
178
+ log_file = 'log/claude_code_evolution.log'
179
+ FileUtils.mkdir_p(File.dirname(log_file))
180
+
181
+ File.open(log_file, 'a') do |f|
182
+ f.puts(log_entry.to_json)
183
+ end
184
+
185
+ puts "📝 Evolution attempt logged to #{log_file}"
186
+ end
187
+
188
+ def trigger_git_operations(error, class_name, method_name, file_path)
189
+ puts "🚀 Triggering Git operations for Claude Code evolution..."
190
+
191
+ begin
192
+ # Use the existing SimpleEvolution Git operations
193
+ require_relative 'simple_evolution'
194
+
195
+ # Create a mock business context for Git operations
196
+ business_context = {
197
+ error_type: error.class.name,
198
+ error_message: error.message,
199
+ class_name: class_name,
200
+ method_name: method_name
201
+ }
202
+
203
+ # Trigger the Git operations through SimpleEvolution
204
+ git_success = CodeHealer::SimpleEvolution.handle_git_operations_for_claude(
205
+ error, class_name, method_name, file_path
206
+ )
207
+
208
+ if git_success
209
+ puts "✅ Git operations completed successfully!"
210
+ puts " - Branch created and committed"
211
+ puts " - Changes pushed to remote"
212
+ puts " - Pull request created"
213
+ else
214
+ puts "❌ Git operations failed"
215
+ end
216
+
217
+ rescue => e
218
+ puts "❌ Error during Git operations: #{e.message}"
219
+ puts "💡 You may need to manually commit and create PR"
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,48 @@
1
+ module CodeHealer
2
+ class ClaudeErrorMonitor
3
+ def self.monitor_and_fix(error, context = {})
4
+ # Send error to Claude API for analysis and fix
5
+ claude_response = send_to_claude({
6
+ error: {
7
+ type: error.class.name,
8
+ message: error.message,
9
+ backtrace: error.backtrace&.first(5)
10
+ },
11
+ context: context,
12
+ request_type: 'error_analysis_and_fix'
13
+ })
14
+
15
+ # Apply fix if Claude provides one
16
+ if claude_response[:fix_available]
17
+ apply_automated_fix(claude_response[:fix])
18
+ end
19
+
20
+ claude_response
21
+ end
22
+
23
+ private
24
+
25
+ def self.send_to_claude(payload)
26
+ # Integration with Claude API
27
+ # This would use your preferred method to communicate with Claude
28
+ # Options:
29
+ # 1. Direct API calls to Claude
30
+ # 2. MCP server integration
31
+ # 3. WebSocket connection
32
+ # 4. File-based communication
33
+
34
+ {
35
+ analysis: "Error analyzed",
36
+ fix_available: false,
37
+ suggested_fix: nil,
38
+ user_message: "Something is wrong with your order data"
39
+ }
40
+ end
41
+
42
+ def self.apply_automated_fix(fix)
43
+ # Only apply safe, non-breaking fixes automatically
44
+ # Log all changes for review
45
+ Rails.logger.info "Claude applied automated fix: #{fix}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,275 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+
4
+ module CodeHealer
5
+ class ConfigManager
6
+ class << self
7
+ def config
8
+ @config ||= load_config
9
+ end
10
+
11
+ def reload_config
12
+ @config = load_config
13
+ end
14
+
15
+ def enabled?
16
+ config['enabled'] == true
17
+ end
18
+
19
+ def require_approval?
20
+ config['require_approval'] == true
21
+ end
22
+
23
+ def auto_create_pr?
24
+ # Use only the nested pull_request configuration for consistency
25
+ config.dig('pull_request', 'auto_create') == true ||
26
+ config.dig('pull_request', 'enabled') == true
27
+ end
28
+
29
+ def auto_generate_tests?
30
+ config['auto_generate_tests'] == true
31
+ end
32
+
33
+ def allowed_error_types
34
+ config['allowed_error_types'] || []
35
+ end
36
+
37
+ def allowed_classes
38
+ config['allowed_classes'] || []
39
+ end
40
+
41
+ def excluded_classes
42
+ config['excluded_classes'] || []
43
+ end
44
+
45
+ def can_evolve_class?(class_name)
46
+ return false unless enabled?
47
+ return false if excluded_classes.include?(class_name)
48
+ return true if allowed_classes.empty?
49
+ allowed_classes.include?(class_name)
50
+ end
51
+
52
+ def can_handle_error?(error)
53
+ return false unless enabled?
54
+ allowed_error_types.include?(error.class.name)
55
+ end
56
+
57
+ # Evolution Strategy Configuration
58
+ def evolution_strategy
59
+ config['evolution_strategy'] || {}
60
+ end
61
+
62
+ def evolution_method
63
+ evolution_strategy['method'] || 'api'
64
+ end
65
+
66
+ def claude_code_enabled?
67
+ evolution_method == 'claude_code_terminal' &&
68
+ config.dig('claude_code', 'enabled') == true
69
+ end
70
+
71
+ def api_enabled?
72
+ evolution_method == 'api' ||
73
+ (evolution_method == 'hybrid' && config.dig('api', 'enabled') != false)
74
+ end
75
+
76
+ def fallback_to_api?
77
+ evolution_strategy['fallback_to_api'] == true
78
+ end
79
+
80
+ # Claude Code Configuration
81
+ def claude_code_settings
82
+ config['claude_code'] || {}
83
+ end
84
+
85
+ # Business Context Configuration
86
+ def business_context_enabled?
87
+ config.dig('business_context', 'enabled') == true
88
+ end
89
+
90
+ def business_context_settings
91
+ config['business_context'] || {}
92
+ end
93
+
94
+ # API Configuration
95
+ def api_settings
96
+ config['api'] || {}
97
+ end
98
+
99
+ def git_settings
100
+ config['git'] || {}
101
+ end
102
+
103
+ def pull_request_settings
104
+ config['pull_request'] || {}
105
+ end
106
+
107
+ def notification_settings
108
+ config['notifications'] || {}
109
+ end
110
+
111
+ def safety_settings
112
+ config['safety'] || {}
113
+ end
114
+
115
+ def evolution_patterns
116
+ config['evolution_patterns'] || {}
117
+ end
118
+
119
+ def get_evolution_pattern(error_type)
120
+ evolution_patterns[error_type.to_s]
121
+ end
122
+
123
+ def max_evolutions_per_day
124
+ config['max_evolutions_per_day'] || 10
125
+ end
126
+
127
+ # Enhanced Git Configuration Methods
128
+ def branch_prefix
129
+ git_settings['branch_prefix'] || 'evolve'
130
+ end
131
+
132
+ def pr_target_branch
133
+ git_settings['pr_target_branch'] || 'main'
134
+ end
135
+
136
+ def commit_message_template
137
+ git_settings['commit_message_template'] || 'Fix {class_name}##{method_name}: {error_type}'
138
+ end
139
+
140
+ def auto_commit?
141
+ git_settings['auto_commit'] != false
142
+ end
143
+
144
+ def auto_push?
145
+ git_settings['auto_push'] != false
146
+ end
147
+
148
+ # Enhanced Safety Configuration Methods
149
+ def backup_before_evolution?
150
+ safety_settings['backup_before_evolution'] != false
151
+ end
152
+
153
+ def rollback_on_syntax_error?
154
+ safety_settings['rollback_on_syntax_error'] != false
155
+ end
156
+
157
+ # Enhanced Pull Request Configuration Methods
158
+ def pull_request_enabled?
159
+ pull_request_settings['enabled'] != false
160
+ end
161
+
162
+ def auto_create_pr?
163
+ pull_request_settings['auto_create'] == true
164
+ end
165
+
166
+ def pr_labels
167
+ pull_request_settings['labels'] || ['auto-fix', 'self-evolving', 'bug-fix']
168
+ end
169
+
170
+ # Enhanced Claude Code Configuration Methods
171
+ def claude_code_timeout
172
+ claude_code_settings['timeout'] || 300
173
+ end
174
+
175
+ def claude_code_max_file_changes
176
+ claude_code_settings['max_file_changes'] || 10
177
+ end
178
+
179
+ def claude_code_include_tests?
180
+ claude_code_settings['include_tests'] != false
181
+ end
182
+
183
+ def claude_code_command_template
184
+ claude_code_settings['command_template'] || "claude --print '{prompt}' --output-format text"
185
+ end
186
+
187
+ private
188
+
189
+ def load_config
190
+ # Try to find config file in current directory or parent directories
191
+ config_path = find_config_file
192
+
193
+ if config_path && File.exist?(config_path)
194
+ YAML.load(ERB.new(File.read(config_path)).result)
195
+ else
196
+ default_config
197
+ end
198
+ rescue => e
199
+ puts "Failed to load code-healer config: #{e.message}" if defined?(Rails)
200
+ default_config
201
+ end
202
+
203
+ def find_config_file
204
+ # Look for config file in current directory and parent directories
205
+ current_dir = Dir.pwd
206
+ max_depth = 5
207
+
208
+ max_depth.times do |depth|
209
+ config_path = File.join(current_dir, 'config', 'code_healer.yml')
210
+ return config_path if File.exist?(config_path)
211
+
212
+ # Go up one directory
213
+ current_dir = File.dirname(current_dir)
214
+ break if current_dir == '/'
215
+ end
216
+
217
+ nil
218
+ end
219
+
220
+ def default_config
221
+ {
222
+ 'enabled' => true,
223
+ 'require_approval' => false,
224
+ 'max_evolutions_per_day' => 10,
225
+ 'auto_generate_tests' => true,
226
+ 'allowed_error_types' => ['ZeroDivisionError', 'NoMethodError', 'ArgumentError', 'TypeError'],
227
+ 'allowed_classes' => ['User', 'Order', 'PaymentProcessor'],
228
+ 'excluded_classes' => ['ApplicationController', 'ApplicationRecord', 'ApplicationJob', 'ApplicationMailer'],
229
+ 'evolution_strategy' => {
230
+ 'method' => 'api',
231
+ 'fallback_to_api' => true
232
+ },
233
+ 'claude_code' => {
234
+ 'enabled' => false,
235
+ 'timeout' => 300,
236
+ 'max_file_changes' => 10,
237
+ 'include_tests' => true,
238
+ 'command_template' => "claude --print '{prompt}' --output-format text --permission-mode acceptEdits --allowedTools Edit",
239
+ 'business_context_sources' => [
240
+ 'config/business_rules.yml',
241
+ 'docs/business_logic.md',
242
+ 'spec/business_context_specs.rb'
243
+ ]
244
+ },
245
+ 'business_context' => {
246
+ 'enabled' => true,
247
+ 'sources' => ['docs/business_rules.md']
248
+ },
249
+ 'api' => {
250
+ 'provider' => 'openai',
251
+ 'model' => 'gpt-4',
252
+ 'max_tokens' => 2000,
253
+ 'temperature' => 0.1
254
+ },
255
+ 'git' => {
256
+ 'auto_commit' => true,
257
+ 'auto_push' => true,
258
+ 'branch_prefix' => 'evolve',
259
+ 'commit_message_template' => 'Fix {class_name}##{method_name}: {error_type}',
260
+ 'pr_target_branch' => 'main'
261
+ },
262
+ 'pull_request' => {
263
+ 'enabled' => true,
264
+ 'auto_create' => true,
265
+ 'labels' => ['auto-fix', 'self-evolving', 'bug-fix']
266
+ },
267
+ 'safety' => {
268
+ 'backup_before_evolution' => true,
269
+ 'rollback_on_syntax_error' => true
270
+ }
271
+ }
272
+ end
273
+ end
274
+ end
275
+ end