code_healer 0.1.24 → 0.1.32

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: 704c23ee702e1f6a10a5fbbdcebe377d0523aa69d880aa40257e7351528130f3
4
- data.tar.gz: e6989f6bbe820a4891c4b1268cde29d9231875c7415d4fbcc8ba9e8d5b72bc91
3
+ metadata.gz: 2a36985eccac2d4e1f55a411b9872478f6ec9e2f5285ebe969f6a839e600e37a
4
+ data.tar.gz: 7f1c43c592b9b6f6ed953ac70cc13855048d7f62222b65ddbf56b047a711ece5
5
5
  SHA512:
6
- metadata.gz: e383c0afbc418195f3de44b0763bca3d18014d198d4cf0d204cf65be5b4c0a763a25ae86faef7c90eba11cb694bf76f5ed24c1322ae3ebeab293fe00509c1d25
7
- data.tar.gz: 694a3cad9243ece93065c849f5dba28e3316d9a678dc8b299d5b675d7125a96441a886b946f24956b0ec54052a3f6bb2e00b3cd0815b941da35094f26c7935ea
6
+ metadata.gz: 790508da0467e26544b1d89bab7caece0bc0afd0e8bc10cf1aee0166704d75a5ef717cab75049b32b6c51e296339df1e8a036194684b100c7ca7e840de5a8230
7
+ data.tar.gz: a34f3ec6ba73155e4adba2ed08ed30a3581dc587ec1d91df2386a69653ec46a83351d2f03bfae02e525e432c64410ea119e9293c44493b08210e7e4a4ba824bd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [0.1.32] - 2025-09-15
2
+
3
+ ### Added
4
+ - Targeted RSpec test-fix loop after code changes in `ClaudeCodeEvolutionHandler`:
5
+ - Runs specs for files related to recent modifications
6
+ - Parses failures and re-invokes Claude with failure summary
7
+ - Iterates up to `test_fix.max_iterations` (default 2)
8
+ - Configuration: `test_fix.max_iterations` with sensible default in `ConfigManager` and `setup.rb`.
9
+
10
+ ### Changed
11
+ - Removed demo mode code and comments across gem: prompts, setup script, logger phrasing, and workspace manager.
12
+ - Switched class targeting to excluded-classes-only model; removed `allowed_classes` usage in config managers and setup generation.
13
+
14
+ ### Notes
15
+ - Backwards compatible; no breaking API changes. Configure `test_fix.max_iterations` in `config/code_healer.yml` to tune retries.
16
+
1
17
  # Changelog
2
18
 
3
19
  All notable changes to this project will be documented in this file.
@@ -7,6 +23,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
23
 
8
24
  ## [0.1.24] - 2025-08-31
9
25
 
26
+ ## [0.1.26] - 2025-09-03
27
+
28
+ ### Added
29
+ - **Enhanced PresentationLogger**: Improved with backtrace truncation, timing display, and specialized action methods
30
+ - **Smart Value Truncation**: Long arrays and strings are intelligently truncated for clean presentation
31
+ - **Timing Integration**: Total healing time displayed in final outcome
32
+ - **Action-Specific Logging**: `claude_action`, `workspace_action`, `git_action` methods for contextual logging
33
+
34
+ ### Changed
35
+ - **Workspace Logs**: Dramatically reduced verbosity while maintaining key information
36
+ - **Backtrace Display**: Shows only first 3 relevant lines with clean formatting
37
+ - **Git Operations**: Streamlined git status and diff output for presentation clarity
38
+ - **Outcome Display**: Enhanced with timing and better formatting
39
+
40
+ ### Notes
41
+ - Perfect for conference demos with `CODE_HEALER_VERBOSE=true` for detailed debugging
42
+ - No API changes; safe minor release
43
+
44
+ ## [0.1.25] - 2025-09-03
45
+
46
+ ### Added
47
+ - PresentationLogger: clean, emoji-labeled, presentation-friendly logs with optional verbose mode (`CODE_HEALER_VERBOSE=true`)
48
+ - HealingJob and ClaudeCodeEvolutionHandler now use PresentationLogger for concise output
49
+
50
+ ### Changed
51
+ - Suppressed noisy prints in core healing flow in favor of high-signal steps and outcomes
52
+
53
+ ### Notes
54
+ - No API changes; safe minor release. Ideal for conference demos.
55
+
10
56
  ### Added
11
57
  - **Aggressive File Filtering**: Implemented comprehensive filtering to prevent `tmp/`, `log/`, and other temporary files from being committed
12
58
  - **Pre-Commit Validation**: Added workspace validation before commit to ensure no temporary files slip through
@@ -1,49 +1,44 @@
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
- # Build concise, demo-optimized prompt (no repo-wide scan, no tests)
15
+ # Build concise prompt
15
16
  prompt = BusinessContextManager.build_claude_code_prompt(
16
17
  error, class_name, method_name, file_path
17
18
  )
19
+
18
20
  prompt << "\n\nStrict instructions:" \
19
21
  "\n- Do NOT scan the entire codebase." \
20
22
  "\n- Work only with the provided file/method context and backtrace." \
21
23
  "\n- Return a unified diff (no prose)." \
22
- "\n- Keep changes minimal and safe." \
23
- "\n- Do NOT create or run tests." if CodeHealer::ConfigManager.demo_mode?
24
+ "\n- Keep changes minimal and safe."
24
25
 
25
26
  # Execute Claude Code command
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
- # Reload modified files
30
+ PresentationLogger.success("Claude run completed")
31
31
  reload_modified_files
32
-
33
- # 🚀 Trigger Git operations (commit, push, PR creation)
34
- # Note: Git operations are now handled by the isolated workspace manager
35
- # to prevent duplication and ensure proper isolation
36
- puts "🔄 Git operations will be handled by isolated workspace manager..."
37
-
38
- return true
32
+ # Run targeted RSpec and iterate on failures
33
+ return run_tests_and_iterate_fixes(class_name, method_name)
39
34
  else
40
- puts "Claude Code evolution failed"
35
+ PresentationLogger.error("Claude run failed")
41
36
  return false
42
37
  end
43
38
 
44
39
  rescue => e
45
- puts "Claude Code evolution error: #{e.message}"
46
- puts e.backtrace.first(5)
40
+ PresentationLogger.error("Claude evolution error: #{e.message}")
41
+ PresentationLogger.detail("Backtrace: #{Array(e.backtrace).first(5).join("\n")}")
47
42
  return false
48
43
  end
49
44
  end
@@ -56,24 +51,24 @@ module CodeHealer
56
51
  # Build command
57
52
  command = build_claude_command(prompt, config)
58
53
 
59
- puts "🚀 Executing Claude Code fix..."
60
- puts "Command: #{command}"
61
- puts "Timeout: #{config['timeout']} seconds"
54
+ PresentationLogger.claude_action("Executing Claude Code (timeout: #{config['timeout']}s)")
62
55
 
63
56
  begin
64
57
  # Execute with timeout
65
58
  Timeout.timeout(config['timeout']) do
66
59
  stdout, stderr, status = Open3.capture3(command)
67
60
 
68
- puts "📤 Claude Code Output:"
69
61
  if stdout && !stdout.empty?
70
- puts "Response received:"
71
- puts stdout
62
+ PresentationLogger.success("Response received from Claude")
63
+ PresentationLogger.detail(stdout)
64
+
65
+
66
+
67
+ # Business context references are intentionally not logged
72
68
 
73
69
  # Check if Claude Code is asking for permission
74
70
  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"
71
+ PresentationLogger.warn("Claude requested edit permissions. Ensure permissions are granted.")
77
72
  end
78
73
 
79
74
  # Check if fix was applied
@@ -81,28 +76,28 @@ module CodeHealer
81
76
  puts "🎯 Fix appears to be ready - checking if files were modified..."
82
77
  end
83
78
  else
84
- puts "⚠️ No output received from Claude Code"
79
+ PresentationLogger.warn("No output received from Claude")
85
80
  end
86
81
 
87
82
  if stderr && !stderr.empty?
88
- puts "⚠️ Claude Code Warnings/Errors:"
89
- puts stderr
83
+ PresentationLogger.warn("Claude warnings/errors present")
84
+ PresentationLogger.detail(stderr)
90
85
  end
91
86
 
92
87
  if status.success?
93
- puts "Claude Code execution completed successfully"
88
+ PresentationLogger.success("Claude execution succeeded")
94
89
  return true
95
90
  else
96
- puts "Claude Code execution failed with status: #{status.exitstatus}"
91
+ PresentationLogger.error("Claude execution failed (status #{status.exitstatus})")
97
92
  return false
98
93
  end
99
94
  end
100
95
 
101
96
  rescue Timeout::Error
102
- puts "Claude Code execution timed out after #{config['timeout']} seconds"
97
+ PresentationLogger.error("Claude execution timed out after #{config['timeout']}s")
103
98
  return false
104
99
  rescue => e
105
- puts "Claude Code execution error: #{e.message}"
100
+ PresentationLogger.error("Claude execution error: #{e.message}")
106
101
  return false
107
102
  end
108
103
  end
@@ -117,28 +112,121 @@ module CodeHealer
117
112
  # Replace placeholder
118
113
  command = command_template.gsub('{prompt}', escaped_prompt)
119
114
 
120
- # Add demo mode specific instructions
121
- if CodeHealer::ConfigManager.demo_mode?
122
- command += " --append-system-prompt 'DEMO MODE: Focus on quick fixes, skip tests, limit file changes to #{config['max_file_changes'] || 3} files maximum'"
123
- else
124
- if config['include_tests']
125
- command += " --append-system-prompt 'Include tests when fixing the code'"
126
- end
127
-
128
- if config['max_file_changes']
129
- command += " --append-system-prompt 'Limit changes to #{config['max_file_changes']} files maximum'"
130
- end
115
+ # Add limits and testing hints
116
+ if config['include_tests']
117
+ command += " --append-system-prompt 'Include tests when fixing the code'"
118
+ end
119
+
120
+ if config['max_file_changes']
121
+ command += " --append-system-prompt 'Limit changes to #{config['max_file_changes']} files maximum'"
131
122
  end
132
123
 
133
- # Add business context instructions
134
- 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
-
124
+ # Add business context instructions and require a delimited summary we can parse (Confluence only)
125
+ # command += " --append-system-prompt 'CRITICAL: Before fixing any code, use the Atlassian MCP tools to fetch business context from Confluence ONLY. Summarize the relevant findings in a concise, bullet list between the markers <<CONTEXT_START>> and <<CONTEXT_END>>. Include Confluence page titles and links, and key rules. Keep to <=5 bullets. Then proceed with the fix.'"
126
+
127
+ # Explicit Confluence page fetch (env override with default fallback)
128
+ explicit_confluence_page_id = (ENV['CONFLUENCE_PAGE_ID'] || '4949770295').to_s.strip
129
+ unless explicit_confluence_page_id.empty?
130
+ command += " --append-system-prompt 'CRITICAL: Before fixing any code, Explicitly fetch Confluence page ID #{explicit_confluence_page_id} using Atlassian MCP (mcp__atlassian), extract applicable business logic/rules, and APPLY those rules in the fix.'"
131
+ end
132
+
136
133
  # Return command
137
134
  command
138
135
  end
136
+
137
+ # Run targeted specs for changed files and iterate fixes up to a configured limit
138
+ def run_tests_and_iterate_fixes(class_name, method_name)
139
+ max_iters = ConfigManager.max_test_fix_iterations
140
+ it = 0
141
+ loop do
142
+ it += 1
143
+ PresentationLogger.step("RSpec run #{it}/#{max_iters}")
144
+ failures = run_targeted_rspec_for_changes
145
+ if failures.nil?
146
+ PresentationLogger.warn("No RSpec detected or no changed files with specs. Skipping test loop.")
147
+ return true
148
+ end
149
+ if failures.empty?
150
+ PresentationLogger.success("All targeted specs passed")
151
+ return true
152
+ end
153
+ PresentationLogger.warn("Failures detected (#{failures.size}). Attempting fix iteration...")
154
+ break if it >= max_iters
155
+ attempt_fix_from_failures(failures, class_name, method_name)
156
+ end
157
+ PresentationLogger.warn("Reached max test-fix iterations (#{max_iters}).")
158
+ false
159
+ end
160
+
161
+ def run_targeted_rspec_for_changes
162
+ changed = get_recently_modified_files.select { |f| f.end_with?('.rb') }
163
+ spec_files = changed.map { |f| f.sub(%r{^app/}, 'spec/').sub(/\.rb\z/, '_spec.rb') }
164
+ spec_files.select! { |s| File.exist?(s) }
165
+ return nil if spec_files.empty? || !File.exist?('spec')
166
+ cmd = ["bundle exec rspec --format documentation --no-color", spec_files.map { |s| "'#{s}'" }.join(' ')].join(' ')
167
+ stdout, stderr, status = Open3.capture3(cmd)
168
+ PresentationLogger.detail(stdout) if stdout && !stdout.empty?
169
+ PresentationLogger.warn(stderr) if stderr && !stderr.empty?
170
+ parse_rspec_failures(stdout)
171
+ rescue => e
172
+ PresentationLogger.warn("RSpec execution failed: #{e.message}")
173
+ nil
174
+ end
175
+
176
+ def parse_rspec_failures(output)
177
+ return [] unless output
178
+ failures = []
179
+ current = nil
180
+ output.each_line do |line|
181
+ if line =~ /^\s*\d+\)\s+(.*)$/
182
+ current = { title: $1.strip, details: [] }
183
+ failures << current
184
+ elsif current
185
+ current[:details] << line
186
+ end
187
+ end
188
+ failures
189
+ end
190
+
191
+ def attempt_fix_from_failures(failures, class_name, method_name)
192
+ summary = failures.map { |f| "- #{f[:title]}\n #{f[:details].first(5).join}" }.join("\n")
193
+ PresentationLogger.claude_action("Sending failure summary to Claude for iterative fix")
194
+ iterative_prompt = <<~PROMPT
195
+ The previous fix compiled, but targeted RSpec tests failed. Here is a concise failure summary:\n\n#{summary}\n\nUpdate the relevant code to make these tests pass. Return only a unified diff. Keep changes minimal and safe.
196
+ PROMPT
197
+ config = ConfigManager.claude_code_settings
198
+ command = build_claude_command(iterative_prompt, config)
199
+ stdout, stderr, status = Open3.capture3(command)
200
+ PresentationLogger.detail(stdout) if stdout && !stdout.empty?
201
+ PresentationLogger.warn(stderr) if stderr && !stderr.empty?
202
+ if status.success?
203
+ PresentationLogger.success("Iterative Claude run succeeded")
204
+ reload_modified_files
205
+ true
206
+ else
207
+ PresentationLogger.error("Iterative Claude run failed (status #{status.exitstatus})")
208
+ false
209
+ end
210
+ end
211
+
212
+ # Parse Claude Terminal output for Confluence links/titles (Confluence only)
213
+ def extract_business_context_references(text)
214
+ refs = []
215
+ return refs unless text
216
+
217
+ # Match Confluence URLs
218
+ confluence_regex = /(https?:\/\/[^\s]+confluence[^\s]+\/(display|spaces|pages)\/[^\s)"']+)/i
219
+ text.scan(confluence_regex).each do |match|
220
+ url = match[0]
221
+ title = url.split('/').last.gsub('-', ' ')[0..80]
222
+ refs << { source: 'Confluence', display: "#{title} (#{url})" }
223
+ end
224
+
225
+ refs.uniq { |r| r[:display] }
226
+ end
139
227
 
140
228
  def reload_modified_files
141
- puts "🔄 Reloading modified files..."
229
+ PresentationLogger.step("Reloading modified files")
142
230
 
143
231
  # Get list of recently modified files (last 5 minutes)
144
232
  recent_files = get_recently_modified_files
@@ -147,14 +235,14 @@ module CodeHealer
147
235
  if file_path.include?('/app/')
148
236
  begin
149
237
  load file_path
150
- puts "Reloaded: #{file_path}"
238
+ PresentationLogger.detail("Reloaded: #{file_path}")
151
239
  rescue => e
152
- puts "⚠️ Failed to reload #{file_path}: #{e.message}"
240
+ PresentationLogger.warn("Failed to reload #{file_path}: #{e.message}")
153
241
  end
154
242
  end
155
243
  end
156
244
 
157
- puts "🔄 File reloading completed"
245
+ PresentationLogger.detail("File reloading completed")
158
246
  end
159
247
 
160
248
  def get_recently_modified_files
@@ -191,7 +279,7 @@ module CodeHealer
191
279
  f.puts(log_entry.to_json)
192
280
  end
193
281
 
194
- puts "📝 Evolution attempt logged to #{log_file}"
282
+ PresentationLogger.detail("Evolution attempt logged to #{log_file}")
195
283
  end
196
284
 
197
285
 
@@ -34,8 +34,9 @@ module CodeHealer
34
34
  config['allowed_error_types'] || []
35
35
  end
36
36
 
37
+ # Deprecated: allowed classes are no longer used. We rely solely on excluded_classes.
37
38
  def allowed_classes
38
- config['allowed_classes'] || []
39
+ []
39
40
  end
40
41
 
41
42
  def excluded_classes
@@ -45,8 +46,7 @@ module CodeHealer
45
46
  def can_evolve_class?(class_name)
46
47
  return false unless enabled?
47
48
  return false if excluded_classes.include?(class_name)
48
- return true if allowed_classes.empty?
49
- allowed_classes.include?(class_name)
49
+ true
50
50
  end
51
51
 
52
52
  def can_handle_error?(error)
@@ -82,6 +82,15 @@ module CodeHealer
82
82
  config['claude_code'] || {}
83
83
  end
84
84
 
85
+ # Test-fix iteration settings
86
+ def test_fix_settings
87
+ config['test_fix'] || {}
88
+ end
89
+
90
+ def max_test_fix_iterations
91
+ (test_fix_settings['max_iterations'] || 2).to_i
92
+ end
93
+
85
94
  def claude_persist_session?
86
95
  claude_code_settings['persist_session'] == true
87
96
  end
@@ -163,21 +172,6 @@ module CodeHealer
163
172
  config['api'] || {}
164
173
  end
165
174
 
166
- # Demo Configuration
167
- def demo_settings
168
- config['demo'] || {}
169
- end
170
-
171
- def demo_mode?
172
- demo_settings['enabled'] == true
173
- end
174
-
175
- def demo_skip_tests?
176
- demo_mode? && demo_settings['skip_tests'] != false
177
- end
178
-
179
-
180
-
181
175
  def git_settings
182
176
  config['git'] || {}
183
177
  end
@@ -337,7 +331,6 @@ module CodeHealer
337
331
  'max_evolutions_per_day' => 10,
338
332
  'auto_generate_tests' => true,
339
333
  'allowed_error_types' => ['ZeroDivisionError', 'NoMethodError', 'ArgumentError', 'TypeError'],
340
- 'allowed_classes' => ['User', 'Order', 'PaymentProcessor'],
341
334
  'excluded_classes' => ['ApplicationController', 'ApplicationRecord', 'ApplicationJob', 'ApplicationMailer'],
342
335
  'evolution_strategy' => {
343
336
  'method' => 'api',
@@ -355,6 +348,9 @@ module CodeHealer
355
348
  'spec/business_context_specs.rb'
356
349
  ]
357
350
  },
351
+ 'test_fix' => {
352
+ 'max_iterations' => 2
353
+ },
358
354
  'business_context' => {
359
355
  'enabled' => true,
360
356
  'sources' => ['docs/business_rules.md']
@@ -365,10 +361,7 @@ module CodeHealer
365
361
  'max_tokens' => 2000,
366
362
  'temperature' => 0.1
367
363
  },
368
- 'demo' => {
369
- 'enabled' => false,
370
- 'skip_tests' => true
371
- },
364
+
372
365
  'git' => {
373
366
  'auto_commit' => true,
374
367
  'auto_push' => true,
@@ -1,6 +1,7 @@
1
1
  require 'logger'
2
2
  require 'git'
3
3
  require 'octokit'
4
+ require_relative 'presentation_logger'
4
5
 
5
6
  module CodeHealer
6
7
  class Core
@@ -180,15 +181,25 @@ module CodeHealer
180
181
  end
181
182
 
182
183
  def patch_classes_for_evolution
183
- # Get allowed classes from config
184
- allowed_classes = CodeHealer::ConfigManager.allowed_classes
185
-
186
- allowed_classes.each do |class_name|
187
- begin
188
- klass = class_name.constantize
189
- patch_class_for_evolution(klass)
190
- rescue NameError => e
191
- puts "⚠️ Could not load class #{class_name}: #{e.message}"
184
+ # Patch all autoloaded classes except excluded ones
185
+ excluded = CodeHealer::ConfigManager.excluded_classes
186
+ # Attempt to iterate over loaded application classes
187
+ if defined?(Rails) && Rails.application
188
+ app_paths = [Rails.root.join('app', 'models'), Rails.root.join('app', 'controllers')]
189
+ app_paths.each do |path|
190
+ Dir.glob(File.join(path.to_s, '**', '*.rb')).each do |file|
191
+ begin
192
+ relative = file.sub(Rails.root.join('app').to_s + '/', '')
193
+ class_name = relative.gsub('.rb', '').split('/').map(&:classify).join('::')
194
+ next if class_name.nil? || class_name.empty?
195
+ next if excluded.include?(class_name)
196
+ klass = class_name.constantize rescue nil
197
+ next unless klass.is_a?(Class) || klass.is_a?(Module)
198
+ patch_class_for_evolution(klass)
199
+ rescue => e
200
+ puts "⚠️ Skipping #{file}: #{e.message}"
201
+ end
202
+ end
192
203
  end
193
204
  end
194
205
  end
@@ -229,26 +240,26 @@ module CodeHealer
229
240
  def extract_from_backtrace(backtrace)
230
241
  return [nil, nil] unless backtrace
231
242
 
232
- puts "🔍 DEBUG: Starting backtrace analysis..."
233
- puts "🔍 DEBUG: First 5 backtrace lines:"
234
- backtrace.first(5).each_with_index { |line, i| puts " #{i}: #{line}" }
243
+ PresentationLogger.detail("Starting backtrace analysis...")
244
+ PresentationLogger.detail("First 5 backtrace lines:")
245
+ backtrace.first(5).each_with_index { |line, i| PresentationLogger.detail(" #{i}: #{line}") }
235
246
 
236
247
  # Use the exact working implementation from SelfRuby
237
248
  core_methods = %w[* + - / % ** == != < > <= >= <=> === =~ !~ & | ^ ~ << >> [] []= `]
238
249
  app_file_line = backtrace.find { |line| line.include?('/app/') }
239
250
  return [nil, nil] unless app_file_line
240
251
 
241
- puts "🔍 DEBUG: Found app file line: #{app_file_line}"
252
+ PresentationLogger.detail("Found app file line: #{app_file_line}")
242
253
 
243
254
  if app_file_line =~ /(.+):(\d+):in `(.+)'/
244
255
  file_path = $1
245
256
  method_name = $3
246
257
 
247
- puts "🔍 DEBUG: Extracted file_path=#{file_path}, method_name=#{method_name}"
258
+ PresentationLogger.detail("Extracted file_path=#{file_path}, method_name=#{method_name}")
248
259
 
249
260
  # If it's a core method, look deeper in the backtrace
250
261
  if core_methods.include?(method_name)
251
- puts "🔍 DEBUG: #{method_name} is a core method, looking deeper..."
262
+ PresentationLogger.detail("#{method_name} is a core method, looking deeper...")
252
263
  deeper_app_line = backtrace.find do |line|
253
264
  line.include?('/app/') &&
254
265
  line =~ /in `(.+)'/ &&
@@ -260,14 +271,14 @@ module CodeHealer
260
271
  end
261
272
 
262
273
  if deeper_app_line
263
- puts "🔍 DEBUG: Found deeper app line: #{deeper_app_line}"
274
+ PresentationLogger.detail("Found deeper app line: #{deeper_app_line}")
264
275
  if deeper_app_line =~ /(.+):(\d+):in `(.+)'/
265
276
  file_path = $1
266
277
  method_name = $3
267
- puts "🔍 DEBUG: Updated to file_path=#{file_path}, method_name=#{method_name}"
278
+ PresentationLogger.detail("Updated to file_path=#{file_path}, method_name=#{method_name}")
268
279
  end
269
280
  else
270
- puts "🔍 DEBUG: No deeper app line found"
281
+ PresentationLogger.detail("No deeper app line found")
271
282
  end
272
283
  end
273
284
 
@@ -279,7 +290,7 @@ module CodeHealer
279
290
  method_name.include?('reduce') ||
280
291
  method_name.include?('sum')
281
292
  )
282
- puts "🔍 DEBUG: #{method_name} is a block/iterator, looking for containing method..."
293
+ PresentationLogger.detail("#{method_name} is a block/iterator, looking for containing method...")
283
294
  # Look for the FIRST valid method in the backtrace, not just any method
284
295
  containing_line = backtrace.find do |line|
285
296
  line.include?('/app/') &&
@@ -294,14 +305,14 @@ module CodeHealer
294
305
  end
295
306
 
296
307
  if containing_line
297
- puts "🔍 DEBUG: Found containing line: #{containing_line}"
308
+ PresentationLogger.detail("Found containing line: #{containing_line}")
298
309
  if containing_line =~ /(.+):(\d+):in `(.+)'/
299
310
  file_path = $1
300
311
  method_name = $3
301
- puts "🔍 DEBUG: Updated to file_path=#{file_path}, method_name=#{method_name}"
312
+ PresentationLogger.detail("Updated to file_path=#{file_path}, method_name=#{method_name}")
302
313
  end
303
314
  else
304
- puts "🔍 DEBUG: No containing line found"
315
+ PresentationLogger.detail("No containing line found")
305
316
  end
306
317
  end
307
318
 
@@ -320,13 +331,13 @@ module CodeHealer
320
331
  end
321
332
  end
322
333
 
323
- puts "🔍 DEBUG: Final result - class_name=#{class_name}, method_name=#{method_name}"
324
- puts "🔍 Extracted: #{class_name}##{method_name} from #{file_path}"
334
+ PresentationLogger.detail("Final result - class_name=#{class_name}, method_name=#{method_name}")
335
+ PresentationLogger.detail("Extracted: #{class_name}##{method_name} from #{file_path}")
325
336
  return [class_name, method_name]
326
337
  end
327
338
  end
328
339
 
329
- puts "🔍 DEBUG: No valid method found in backtrace"
340
+ PresentationLogger.detail("No valid method found in backtrace")
330
341
  [nil, nil]
331
342
  end
332
343