code_healer 0.1.22 → 0.1.26

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a14639ba1f9033b76d3cc5d3ec3b252b7568c14b22098a4efb6d71f50d95463
4
- data.tar.gz: 1c3f66f4ffc22405485ad3048a3b1635ed5849a1a46a4881b20647bff33a2118
3
+ metadata.gz: 20c5c060c65cfee68cf65f2478e6335b1927f7e810fdcf6dd1c6796e29b49f65
4
+ data.tar.gz: 38aad1427647fe628c1a0aa2824fe09b206d6ac752ef6b1407a41ddd15560383
5
5
  SHA512:
6
- metadata.gz: b78c5c6c5917de5253fcf543132c26679f5d520e1a0d9e80dcfe5b28ee13e9e45b5751449db2addb6e0fb90563ee69d4830fbafb6526977f91b73b083c1e54b1
7
- data.tar.gz: 78dfc2a6168f34a25fc0eab7be783e63a1b4d18f06c5236e25acf1f6835a262032e23526b4ae0b08f2fd81cfc992fbef6c0e038a5fb1d2e8aa199f0403e8061b
6
+ metadata.gz: eb2f391d38dfb0e444ff60e66d92625409f09c8389705a08c3ca3783f9aa56ae1d10dffc36109472cf2158c1e44b3df1eaba6100263f57d7d82bec713d58bbe6
7
+ data.tar.gz: 7864c4f255da0a1682d8c5787009998fe0e554a29fadbaf486f84d3c7664cdb12596381d8affa303110dc68a7b42d83c00f10da94c7f5f7368bc8bed88d13189
data/CHANGELOG.md CHANGED
@@ -5,6 +5,72 @@ 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.24] - 2025-08-31
9
+
10
+ ## [0.1.26] - 2025-09-03
11
+
12
+ ### Added
13
+ - **Enhanced PresentationLogger**: Improved with backtrace truncation, timing display, and specialized action methods
14
+ - **Smart Value Truncation**: Long arrays and strings are intelligently truncated for clean presentation
15
+ - **Timing Integration**: Total healing time displayed in final outcome
16
+ - **Action-Specific Logging**: `claude_action`, `workspace_action`, `git_action` methods for contextual logging
17
+
18
+ ### Changed
19
+ - **Workspace Logs**: Dramatically reduced verbosity while maintaining key information
20
+ - **Backtrace Display**: Shows only first 3 relevant lines with clean formatting
21
+ - **Git Operations**: Streamlined git status and diff output for presentation clarity
22
+ - **Outcome Display**: Enhanced with timing and better formatting
23
+
24
+ ### Notes
25
+ - Perfect for conference demos with `CODE_HEALER_VERBOSE=true` for detailed debugging
26
+ - No API changes; safe minor release
27
+
28
+ ## [0.1.25] - 2025-09-03
29
+
30
+ ### Added
31
+ - PresentationLogger: clean, emoji-labeled, presentation-friendly logs with optional verbose mode (`CODE_HEALER_VERBOSE=true`)
32
+ - HealingJob and ClaudeCodeEvolutionHandler now use PresentationLogger for concise output
33
+
34
+ ### Changed
35
+ - Suppressed noisy prints in core healing flow in favor of high-signal steps and outcomes
36
+
37
+ ### Notes
38
+ - No API changes; safe minor release. Ideal for conference demos.
39
+
40
+ ### Added
41
+ - **Aggressive File Filtering**: Implemented comprehensive filtering to prevent `tmp/`, `log/`, and other temporary files from being committed
42
+ - **Pre-Commit Validation**: Added workspace validation before commit to ensure no temporary files slip through
43
+ - **Mandatory MCP Usage**: Enhanced business context prompts to force Claude to use Atlassian MCP tools for Confluence/Jira integration
44
+ - **Missing Method Fix**: Added `extract_file_path_from_error` method to resolve undefined method errors
45
+
46
+ ### Changed
47
+ - **File Filtering**: Enhanced `should_skip_file?` method with aggressive patterns for temporary files at any level
48
+ - **Git Cleanup**: Comprehensive cleanup of tracked temporary files using `git rm --cached` and `find` commands
49
+ - **Business Context**: Changed from "optional" to "mandatory" MCP tool usage in prompts
50
+ - **Setup Configuration**: Fixed `setup.rb` to include `mcp__atlassian` permission in command template
51
+
52
+ ### Fixed
53
+ - **Temporary File Commits**: Prevents `tmp/`, `log/`, `storage/`, `coverage/` directories from being committed
54
+ - **MCP Tool Access**: Fixed missing `mcp__atlassian` permission in setup script
55
+ - **Syntax Error**: Removed stray `y` character that caused Ruby syntax errors
56
+ - **Method Missing**: Added missing `extract_file_path_from_error` method to `HealingJob` class
57
+
58
+ ## [0.1.23] - 2025-08-27
59
+
60
+ ### Added
61
+ - **Persistent Isolated Workspaces**: Implemented persistent workspaces that reuse the same isolated environment instead of cloning each time
62
+ - **Smart Branch Checkout**: Workspaces now checkout to the configured target branch instead of cloning the current working branch
63
+ - **Workspace Reset**: Added ability to reset workspace to clean state without deleting the entire workspace
64
+ - **Configuration Control**: Added `persistent_workspaces` and `sticky_workspace` configuration options
65
+
66
+ ### Changed
67
+ - **Workspace Strategy**: Changed from cloning new workspaces each time to reusing persistent workspaces with branch checkout
68
+ - **Performance**: Significantly faster healing operations by avoiding repeated repository cloning
69
+ - **Branch Targeting**: Workspaces now target the configured `pr_target_branch` instead of the current working branch
70
+
71
+ ### Fixed
72
+ - **Branch Consistency**: Ensures fixes are always applied to the correct target branch regardless of current working branch
73
+
8
74
  ## [0.1.22] - 2025-08-27
9
75
 
10
76
  ### Fixed
@@ -181,30 +181,43 @@ module CodeHealer
181
181
  business_context = case CodeHealer::ConfigManager.business_context_strategy
182
182
  when 'confluence_only'
183
183
  "## Business Context Instructions:\n" \
184
- "OPTIONAL: You can use Confluence MCP tools to enhance business context if available.\n\n" \
185
- "1. If Confluence MCP is available:\n" \
184
+ "MANDATORY: You MUST use Confluence MCP tools to search for business context before fixing any code.\n\n" \
185
+ "1. **REQUIRED - Search Confluence for business context:**\n" \
186
186
  " - Search for PRDs/specs related to '#{class_name}' / '#{method_name}'.\n" \
187
187
  " - Query terms: '#{class_name}', '#{method_name}', 'business rules', 'validation patterns'.\n" \
188
- " - Apply any found documentation to your fix.\n\n" \
189
- "2. If Confluence MCP is not available:\n" \
188
+ " - Search for domain-specific business logic and requirements.\n" \
189
+ " - Look for validation rules and business constraints.\n\n" \
190
+ "2. **After finding business context:**\n" \
191
+ " - Apply ALL found business rules to your fix.\n" \
192
+ " - Ensure the fix follows the documented business requirements.\n" \
193
+ " - Validate that your solution aligns with business logic.\n\n" \
194
+ "3. **If no documentation is found:**\n" \
195
+ " - Note this explicitly in your response.\n" \
190
196
  " - Proceed with standard business logic and error handling practices.\n" \
191
197
  " - Use common validation patterns and best practices.\n\n" \
192
- "Note: MCP usage is optional - proceed with or without it."
198
+ "CRITICAL: You MUST search Confluence first - this is not optional!"
193
199
  when 'claude_atlassian_mcp'
194
200
  "## Business Context Instructions:\n" \
195
- "OPTIONAL: You can use Atlassian MCP tools to enhance business context if available.\n\n" \
196
- "1. **If Jira MCP is available:**\n" \
201
+ "MANDATORY: You MUST use Atlassian MCP tools to search for business context before fixing any code.\n\n" \
202
+ "1. **REQUIRED - Search Jira for business context:**\n" \
197
203
  " - Search for tickets related to '#{class_name}' or '#{method_name}'\n" \
198
204
  " - Look for bug reports, requirements, or business rules\n" \
199
- " - Apply any found ticket context to your fix\n\n" \
200
- "2. **If Confluence MCP is available:**\n" \
205
+ " - Search for domain-specific business logic and constraints\n" \
206
+ " - Apply ALL found ticket context to your fix\n\n" \
207
+ "2. **REQUIRED - Search Confluence for business context:**\n" \
201
208
  " - Search for PRDs, technical specs, or business process docs\n" \
202
209
  " - Look for domain-specific business rules related to '#{class_name}'\n" \
203
- " - Apply any found documentation to your fix\n\n" \
204
- "3. **If MCP tools are not available:**\n" \
205
- " - Proceed with standard business logic and error handling practices\n" \
206
- " - Use common validation patterns and best practices\n\n" \
207
- "Note: MCP usage is optional - proceed with or without it."
210
+ " - Search for validation patterns and business requirements\n" \
211
+ " - Apply ALL found documentation to your fix\n\n" \
212
+ "3. **After finding business context:**\n" \
213
+ " - Ensure your fix follows ALL documented business requirements\n" \
214
+ " - Validate that your solution aligns with business logic\n" \
215
+ " - Apply business rules and constraints to your solution\n\n" \
216
+ "4. **If no documentation is found:**\n" \
217
+ " - Note this explicitly in your response.\n" \
218
+ " - Proceed with standard business logic and error handling practices.\n" \
219
+ " - Use common validation patterns and best practices.\n\n" \
220
+ "CRITICAL: You MUST search both Jira and Confluence first - this is not optional!"
208
221
  when 'jira_mcp'
209
222
  # Use Jira MCP context
210
223
  get_jira_business_context(class_name)
@@ -236,12 +249,15 @@ module CodeHealer
236
249
 
237
250
  ## Instructions:
238
251
  Please:
239
- 1. Analyze the error and understand the root cause
240
- 2. **IMPORTANT: Check if the fix requires changes to multiple files or if the root cause is in a different file**
241
- 3. Fix the issue considering the business rules above
242
- 4. Ensure the fix doesn't break other functionality
243
- 5. Follow Rails conventions and patterns
244
- 6. Make sure to write testcases for the fix
252
+ 1. **CRITICAL: First, use Atlassian MCP tools to search for business context about '#{class_name}' and '#{method_name}'**
253
+ 2. **REQUIRED: Search Confluence for business rules, PRDs, and validation patterns**
254
+ 3. **REQUIRED: Search Jira for related tickets and business requirements**
255
+ 4. Analyze the error and understand the root cause
256
+ 5. **IMPORTANT: Check if the fix requires changes to multiple files or if the root cause is in a different file**
257
+ 6. Fix the issue considering the business rules found in MCP tools
258
+ 7. Ensure the fix doesn't break other functionality
259
+ 8. Follow Rails conventions and patterns
260
+ 9. Make sure to write testcases for the fix
245
261
 
246
262
  ## IMPORTANT: Full Codebase Access & Multi-File Fixes
247
263
  You have permission to edit ANY files in the codebase. Please:
@@ -1,14 +1,15 @@
1
1
  require 'timeout'
2
2
  require 'open3'
3
+ require_relative 'presentation_logger'
3
4
 
4
5
  module CodeHealer
5
6
  class ClaudeCodeEvolutionHandler
6
7
  class << self
7
8
  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}"
9
+ PresentationLogger.section("Claude Code Terminal Evolution")
10
+ PresentationLogger.kv("Error", "#{error.class}: #{error.message}")
11
+ PresentationLogger.kv("Target", "#{class_name}##{method_name}")
12
+ PresentationLogger.detail("File: #{file_path}")
12
13
 
13
14
  begin
14
15
  # Build concise, demo-optimized prompt (no repo-wide scan, no tests)
@@ -26,24 +27,24 @@ module CodeHealer
26
27
  success = execute_claude_code_fix(prompt, class_name, method_name)
27
28
 
28
29
  if success
29
- puts "Claude Code evolution completed successfully!"
30
+ PresentationLogger.success("Claude run completed")
30
31
  # Reload modified files
31
32
  reload_modified_files
32
33
 
33
34
  # 🚀 Trigger Git operations (commit, push, PR creation)
34
35
  # Note: Git operations are now handled by the isolated workspace manager
35
36
  # to prevent duplication and ensure proper isolation
36
- puts "🔄 Git operations will be handled by isolated workspace manager..."
37
+ PresentationLogger.info("Git operations handled by workspace manager")
37
38
 
38
39
  return true
39
40
  else
40
- puts "Claude Code evolution failed"
41
+ PresentationLogger.error("Claude run failed")
41
42
  return false
42
43
  end
43
44
 
44
45
  rescue => e
45
- puts "Claude Code evolution error: #{e.message}"
46
- puts e.backtrace.first(5)
46
+ PresentationLogger.error("Claude evolution error: #{e.message}")
47
+ PresentationLogger.detail("Backtrace: #{Array(e.backtrace).first(5).join("\n")}")
47
48
  return false
48
49
  end
49
50
  end
@@ -56,24 +57,20 @@ module CodeHealer
56
57
  # Build command
57
58
  command = build_claude_command(prompt, config)
58
59
 
59
- puts "🚀 Executing Claude Code fix..."
60
- puts "Command: #{command}"
61
- puts "Timeout: #{config['timeout']} seconds"
60
+ PresentationLogger.claude_action("Executing Claude Code (timeout: #{config['timeout']}s)")
62
61
 
63
62
  begin
64
63
  # Execute with timeout
65
64
  Timeout.timeout(config['timeout']) do
66
65
  stdout, stderr, status = Open3.capture3(command)
67
66
 
68
- puts "📤 Claude Code Output:"
69
67
  if stdout && !stdout.empty?
70
- puts "Response received:"
71
- puts stdout
68
+ PresentationLogger.success("Response received from Claude")
69
+ PresentationLogger.detail(stdout)
72
70
 
73
71
  # Check if Claude Code is asking for permission
74
72
  if stdout.include?("permission") || stdout.include?("grant") || stdout.include?("edit")
75
- puts "🔐 Claude Code needs permission to edit files"
76
- puts "💡 Make sure to grant Edit permissions when prompted"
73
+ PresentationLogger.warn("Claude requested edit permissions. Ensure permissions are granted.")
77
74
  end
78
75
 
79
76
  # Check if fix was applied
@@ -81,28 +78,28 @@ module CodeHealer
81
78
  puts "🎯 Fix appears to be ready - checking if files were modified..."
82
79
  end
83
80
  else
84
- puts "⚠️ No output received from Claude Code"
81
+ PresentationLogger.warn("No output received from Claude")
85
82
  end
86
83
 
87
84
  if stderr && !stderr.empty?
88
- puts "⚠️ Claude Code Warnings/Errors:"
89
- puts stderr
85
+ PresentationLogger.warn("Claude warnings/errors present")
86
+ PresentationLogger.detail(stderr)
90
87
  end
91
88
 
92
89
  if status.success?
93
- puts "Claude Code execution completed successfully"
90
+ PresentationLogger.success("Claude execution succeeded")
94
91
  return true
95
92
  else
96
- puts "Claude Code execution failed with status: #{status.exitstatus}"
93
+ PresentationLogger.error("Claude execution failed (status #{status.exitstatus})")
97
94
  return false
98
95
  end
99
96
  end
100
97
 
101
98
  rescue Timeout::Error
102
- puts "Claude Code execution timed out after #{config['timeout']} seconds"
99
+ PresentationLogger.error("Claude execution timed out after #{config['timeout']}s")
103
100
  return false
104
101
  rescue => e
105
- puts "Claude Code execution error: #{e.message}"
102
+ PresentationLogger.error("Claude execution error: #{e.message}")
106
103
  return false
107
104
  end
108
105
  end
@@ -112,7 +109,7 @@ module CodeHealer
112
109
  escaped_prompt = prompt.gsub("'", "'\"'\"'")
113
110
 
114
111
  # Build command template for MCP tools access
115
- command_template = config['command_template'] || "claude --print '{prompt}' --output-format text"
112
+ command_template = config['command_template'] || "claude --print '{prompt}' --output-format text --allowedTools Edit,mcp__atlassian"
116
113
 
117
114
  # Replace placeholder
118
115
  command = command_template.gsub('{prompt}', escaped_prompt)
@@ -131,14 +128,14 @@ module CodeHealer
131
128
  end
132
129
 
133
130
  # Add business context instructions
134
- command += " --append-system-prompt 'Use available MCP tools for business context if needed, but proceed with the fix regardless.'"
131
+ command += " --append-system-prompt 'CRITICAL: Before fixing any code, you MUST use the Atlassian MCP tools to search Confluence for business context. Search for documentation about the class and method you are fixing. Only proceed with the fix after reviewing the business requirements from Confluence. If no documentation is found, note this in your response.'"
135
132
 
136
133
  # Return command
137
134
  command
138
135
  end
139
136
 
140
137
  def reload_modified_files
141
- puts "🔄 Reloading modified files..."
138
+ PresentationLogger.step("Reloading modified files")
142
139
 
143
140
  # Get list of recently modified files (last 5 minutes)
144
141
  recent_files = get_recently_modified_files
@@ -147,14 +144,14 @@ module CodeHealer
147
144
  if file_path.include?('/app/')
148
145
  begin
149
146
  load file_path
150
- puts "Reloaded: #{file_path}"
147
+ PresentationLogger.detail("Reloaded: #{file_path}")
151
148
  rescue => e
152
- puts "⚠️ Failed to reload #{file_path}: #{e.message}"
149
+ PresentationLogger.warn("Failed to reload #{file_path}: #{e.message}")
153
150
  end
154
151
  end
155
152
  end
156
153
 
157
- puts "🔄 File reloading completed"
154
+ PresentationLogger.detail("File reloading completed")
158
155
  end
159
156
 
160
157
  def get_recently_modified_files
@@ -191,7 +188,7 @@ module CodeHealer
191
188
  f.puts(log_entry.to_json)
192
189
  end
193
190
 
194
- puts "📝 Evolution attempt logged to #{log_file}"
191
+ PresentationLogger.detail("Evolution attempt logged to #{log_file}")
195
192
  end
196
193
 
197
194
 
@@ -176,9 +176,7 @@ module CodeHealer
176
176
  demo_mode? && demo_settings['skip_tests'] != false
177
177
  end
178
178
 
179
- def demo_skip_pr?
180
- demo_mode? && demo_settings['skip_pr'] != false
181
- end
179
+
182
180
 
183
181
  def git_settings
184
182
  config['git'] || {}
@@ -197,6 +195,10 @@ module CodeHealer
197
195
  cfg = code_heal_directory_config
198
196
  cfg['sticky_workspace'] == true || cfg[:sticky_workspace] == true
199
197
  end
198
+
199
+ def persistent_workspaces_enabled?
200
+ code_heal_directory_config['persistent_workspaces'] != false
201
+ end
200
202
 
201
203
  def auto_cleanup_workspaces?
202
204
  code_heal_directory_config['auto_cleanup'] != false
@@ -346,7 +348,7 @@ module CodeHealer
346
348
  'timeout' => 300,
347
349
  'max_file_changes' => 10,
348
350
  'include_tests' => true,
349
- 'command_template' => "claude --print '{prompt}' --output-format text --permission-mode acceptEdits --allowedTools Edit",
351
+ 'command_template' => "claude --print '{prompt}' --output-format text --permission-mode acceptEdits --allowedTools Edit,mcp__atlassian",
350
352
  'business_context_sources' => [
351
353
  'config/business_rules.yml',
352
354
  'docs/business_logic.md',
@@ -365,8 +367,7 @@ module CodeHealer
365
367
  },
366
368
  'demo' => {
367
369
  'enabled' => false,
368
- 'skip_tests' => true,
369
- 'skip_pr' => true
370
+ 'skip_tests' => true
370
371
  },
371
372
  'git' => {
372
373
  'auto_commit' => true,
@@ -389,7 +390,9 @@ module CodeHealer
389
390
  'auto_cleanup' => true,
390
391
  'cleanup_after_hours' => 24,
391
392
  'max_workspaces' => 10,
392
- 'clone_strategy' => 'branch'
393
+ 'clone_strategy' => 'branch',
394
+ 'persistent_workspaces' => true,
395
+ 'sticky_workspace' => false
393
396
  }
394
397
  }
395
398
  end
@@ -56,13 +56,13 @@ class EvolutionJob
56
56
  def create_healing_workspace(class_name, method_name)
57
57
  puts "🏥 Creating isolated healing workspace for #{class_name}##{method_name}"
58
58
 
59
- # Create unique workspace
59
+ # Create persistent workspace and checkout to target branch
60
60
  workspace_path = CodeHealer::HealingWorkspaceManager.create_healing_workspace(
61
61
  Rails.root.to_s,
62
- nil # Use current branch
62
+ CodeHealer::ConfigManager.pr_target_branch # Use configured target branch
63
63
  )
64
64
 
65
- puts "✅ Healing workspace created: #{workspace_path}"
65
+ puts "✅ Healing workspace ready: #{workspace_path}"
66
66
  workspace_path
67
67
  end
68
68
 
@@ -1,5 +1,6 @@
1
1
  require 'sidekiq'
2
2
  require 'open3'
3
+ require_relative 'presentation_logger'
3
4
 
4
5
  module CodeHealer
5
6
  class HealingJob
@@ -8,15 +9,15 @@ module CodeHealer
8
9
  sidekiq_options retry: 3, backtrace: true, queue: 'evolution'
9
10
 
10
11
  def perform(*args)
11
- puts "🚀 [HEALING_JOB] Starting job with args: #{args.inspect}"
12
-
12
+ start_time = Time.now
13
+ PresentationLogger.section("CodeHealer – Healing Job")
13
14
 
14
-
15
15
  # Support both legacy and new invocation styles
16
16
  error, class_name, method_name, evolution_method, backtrace = parse_args(args)
17
-
18
- puts "🚀 [HEALING_JOB] Parsed args - Error: #{error.class}, Class: #{class_name}, Method: #{method_name}, Evolution: #{evolution_method}"
19
- puts "🚀 [HEALING_JOB] Backtrace length: #{backtrace&.length || 0}"
17
+ PresentationLogger.kv("Error", "#{error.class}: #{error.message}")
18
+ PresentationLogger.kv("Target", "#{class_name}##{method_name}")
19
+ PresentationLogger.kv("Strategy", evolution_method)
20
+ PresentationLogger.backtrace(backtrace)
20
21
 
21
22
  # Track start metric
22
23
  healing_id = MetricsCollector.generate_healing_id
@@ -30,13 +31,13 @@ module CodeHealer
30
31
  )
31
32
  MetricsCollector.track_error_occurrence(healing_id, Time.current)
32
33
 
33
- puts "🚀 Evolution Job Started: #{class_name}##{method_name}"
34
+ PresentationLogger.success("Healing started for #{class_name}##{method_name}")
34
35
 
35
- puts "🏥 [HEALING_JOB] About to create isolated healing workspace..."
36
+ PresentationLogger.step("Creating isolated healing workspace")
36
37
  # Create isolated healing workspace
37
38
  workspace_path = create_healing_workspace(class_name, method_name)
38
39
  MetricsCollector.track_workspace_creation(healing_id, workspace_path)
39
- puts "🏥 [HEALING_JOB] Workspace created: #{workspace_path}"
40
+ PresentationLogger.kv("Workspace", workspace_path)
40
41
 
41
42
  ai_time_ms = nil
42
43
  git_time_ms = nil
@@ -46,7 +47,7 @@ module CodeHealer
46
47
  failure_reason = nil
47
48
 
48
49
  begin
49
- puts "🔧 [HEALING_JOB] About to apply fixes in isolated environment..."
50
+ PresentationLogger.step("Applying fixes in isolated environment")
50
51
  # Apply fixes in isolated environment
51
52
  ai_started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
52
53
  success = apply_fixes_in_workspace(workspace_path, error, class_name, method_name, evolution_method)
@@ -83,17 +84,18 @@ module CodeHealer
83
84
  false # pr_created
84
85
  )
85
86
  overall_success = true
86
- puts "Fixes applied, tested, and merged successfully! Branch: #{healing_branch}"
87
+ PresentationLogger.success("Fixes applied and validated")
88
+ PresentationLogger.kv("Branch", healing_branch)
87
89
  else
88
90
  overall_success = false
89
91
  failure_reason ||= 'healing_branch_creation_failed'
90
- puts "⚠️ Fixes applied and tested, but merge failed"
92
+ PresentationLogger.warn("Fixes applied and tested, but merge failed")
91
93
  end
92
94
  else
93
95
  overall_success = false
94
96
  syntax_valid = false
95
97
  failure_reason ||= 'workspace_tests_failed_or_syntax_error'
96
- puts "⚠️ Fixes applied but failed tests, not merging back"
98
+ PresentationLogger.warn("Fixes applied but validation failed; skipping merge")
97
99
  end
98
100
  else
99
101
  overall_success = false
@@ -105,7 +107,7 @@ module CodeHealer
105
107
  ai_provider_for(evolution_method),
106
108
  failure_reason
107
109
  )
108
- puts "Failed to apply fixes in workspace"
110
+ PresentationLogger.error("Failed to apply fixes in workspace")
109
111
  end
110
112
  ensure
111
113
  # Persist timing metrics if captured
@@ -122,10 +124,12 @@ module CodeHealer
122
124
  cleanup_workspace(workspace_path)
123
125
  end
124
126
 
125
- puts "✅ Evolution Job Completed: #{class_name}##{method_name}"
127
+ total_time = ((Time.now - start_time) * 1000).round
128
+ timing = "#{total_time}ms total"
129
+ PresentationLogger.outcome(success: overall_success, branch: healing_branch, pr_url: nil, reason: failure_reason, timing: timing)
126
130
  rescue => e
127
- puts "Evolution Job Failed: #{e.message}"
128
- puts "📍 Backtrace: #{e.backtrace.first(5)}"
131
+ PresentationLogger.error("Evolution Job Failed: #{e.message}")
132
+ PresentationLogger.detail("Backtrace: #{Array(e.backtrace).first(5).join("\n")}")
129
133
  raise e # Re-raise to trigger Sidekiq retry
130
134
  end
131
135
 
@@ -172,6 +176,24 @@ module CodeHealer
172
176
  puts "🔍 [HEALING_JOB] MCP tools check complete"
173
177
  end
174
178
 
179
+ def extract_file_path_from_error(error)
180
+ return nil unless error&.backtrace
181
+
182
+ # Look for the first line that contains a file path
183
+ error.backtrace.each do |line|
184
+ if line.match?(/^(.+\.rb):\d+:in/)
185
+ file_path = $1
186
+ # Convert to absolute path if it's relative
187
+ if file_path.start_with?('./') || !file_path.start_with?('/')
188
+ file_path = File.expand_path(file_path)
189
+ end
190
+ return file_path
191
+ end
192
+ end
193
+
194
+ nil
195
+ end
196
+
175
197
  def parse_args(args)
176
198
  # Formats supported:
177
199
  # 1) [error_class, error_message, class_name, method_name, evolution_method, backtrace]
@@ -204,20 +226,20 @@ module CodeHealer
204
226
  end
205
227
 
206
228
  def create_healing_workspace(class_name, method_name)
207
- puts "🏥 Creating isolated healing workspace for #{class_name}##{method_name}"
229
+ PresentationLogger.detail("Preparing workspace for #{class_name}##{method_name}")
208
230
 
209
- # Create unique workspace
231
+ # Create persistent workspace and checkout to target branch
210
232
  workspace_path = CodeHealer::HealingWorkspaceManager.create_healing_workspace(
211
233
  Rails.root.to_s,
212
- nil # Use current branch
234
+ CodeHealer::ConfigManager.pr_target_branch # Use configured target branch
213
235
  )
214
236
 
215
- puts " Healing workspace created: #{workspace_path}"
237
+ PresentationLogger.detail("Workspace ready: #{workspace_path}")
216
238
  workspace_path
217
239
  end
218
240
 
219
241
  def apply_fixes_in_workspace(workspace_path, error, class_name, method_name, evolution_method)
220
- puts "🔧 Applying fixes in isolated workspace"
242
+ PresentationLogger.detail("Applying fixes in workspace #{workspace_path}")
221
243
 
222
244
  case evolution_method
223
245
  when 'claude_code_terminal'
@@ -233,13 +255,13 @@ module CodeHealer
233
255
  end
234
256
  handle_api_evolution_in_workspace(workspace_path, error, class_name, method_name)
235
257
  else
236
- puts "Unknown evolution method: #{evolution_method}"
258
+ PresentationLogger.error("Unknown evolution method: #{evolution_method}")
237
259
  false
238
260
  end
239
261
  end
240
262
 
241
263
  def handle_claude_code_evolution_in_workspace(workspace_path, error, class_name, method_name)
242
- puts "🤖 Using Claude Code Terminal for evolution in workspace..."
264
+ PresentationLogger.step("Claude Code Terminal evolution")
243
265
 
244
266
  # Change to workspace directory for Claude Code operations
245
267
  Dir.chdir(workspace_path) do
@@ -248,17 +270,17 @@ module CodeHealer
248
270
  )
249
271
 
250
272
  if success
251
- puts "Claude Code evolution completed successfully in workspace!"
273
+ PresentationLogger.success("Claude evolution succeeded")
252
274
  true
253
275
  else
254
- puts "Claude Code evolution failed in workspace"
276
+ PresentationLogger.error("Claude evolution failed")
255
277
  false
256
278
  end
257
279
  end
258
280
  end
259
281
 
260
282
  def handle_api_evolution_in_workspace(workspace_path, error, class_name, method_name)
261
- puts "🌐 Using OpenAI API for evolution in workspace..."
283
+ PresentationLogger.step("OpenAI API evolution")
262
284
 
263
285
  # Load business context for API evolution
264
286
  business_context = CodeHealer::BusinessContextManager.get_context_for_error(
@@ -268,7 +290,7 @@ module CodeHealer
268
290
  # Optionally record business context used
269
291
  # MetricsCollector.track_business_context(healing_id, business_context) # healing_id not accessible here
270
292
 
271
- puts "📋 Business context loaded for API evolution"
293
+ PresentationLogger.detail("Business context loaded for API evolution")
272
294
 
273
295
  # Change to workspace directory for API operations
274
296
  Dir.chdir(workspace_path) do
@@ -277,10 +299,10 @@ module CodeHealer
277
299
  )
278
300
 
279
301
  if success
280
- puts "API evolution completed successfully in workspace!"
302
+ PresentationLogger.success("API evolution succeeded")
281
303
  true
282
304
  else
283
- puts "API evolution failed in workspace"
305
+ PresentationLogger.error("API evolution failed")
284
306
  false
285
307
  end
286
308
  end
@@ -289,7 +311,7 @@ module CodeHealer
289
311
  def cleanup_workspace(workspace_path)
290
312
  return unless workspace_path && Dir.exist?(workspace_path)
291
313
 
292
- puts "🧹 Cleaning up healing workspace: #{workspace_path}"
314
+ PresentationLogger.step("Cleaning up workspace")
293
315
  CodeHealer::HealingWorkspaceManager.cleanup_workspace(workspace_path)
294
316
  end
295
317