code_healer 0.1.24 โ†’ 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: 704c23ee702e1f6a10a5fbbdcebe377d0523aa69d880aa40257e7351528130f3
4
- data.tar.gz: e6989f6bbe820a4891c4b1268cde29d9231875c7415d4fbcc8ba9e8d5b72bc91
3
+ metadata.gz: 20c5c060c65cfee68cf65f2478e6335b1927f7e810fdcf6dd1c6796e29b49f65
4
+ data.tar.gz: 38aad1427647fe628c1a0aa2824fe09b206d6ac752ef6b1407a41ddd15560383
5
5
  SHA512:
6
- metadata.gz: e383c0afbc418195f3de44b0763bca3d18014d198d4cf0d204cf65be5b4c0a763a25ae86faef7c90eba11cb694bf76f5ed24c1322ae3ebeab293fe00509c1d25
7
- data.tar.gz: 694a3cad9243ece93065c849f5dba28e3316d9a678dc8b299d5b675d7125a96441a886b946f24956b0ec54052a3f6bb2e00b3cd0815b941da35094f26c7935ea
6
+ metadata.gz: eb2f391d38dfb0e444ff60e66d92625409f09c8389705a08c3ca3783f9aa56ae1d10dffc36109472cf2158c1e44b3df1eaba6100263f57d7d82bec713d58bbe6
7
+ data.tar.gz: 7864c4f255da0a1682d8c5787009998fe0e554a29fadbaf486f84d3c7664cdb12596381d8affa303110dc68a7b42d83c00f10da94c7f5f7368bc8bed88d13189
data/CHANGELOG.md CHANGED
@@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [0.1.24] - 2025-08-31
9
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
+
10
40
  ### Added
11
41
  - **Aggressive File Filtering**: Implemented comprehensive filtering to prevent `tmp/`, `log/`, and other temporary files from being committed
12
42
  - **Pre-Commit Validation**: Added workspace validation before commit to ensure no temporary files slip through
@@ -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
@@ -138,7 +135,7 @@ module CodeHealer
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
 
@@ -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
 
@@ -222,7 +226,7 @@ module CodeHealer
222
226
  end
223
227
 
224
228
  def create_healing_workspace(class_name, method_name)
225
- puts "๐Ÿฅ Creating isolated healing workspace for #{class_name}##{method_name}"
229
+ PresentationLogger.detail("Preparing workspace for #{class_name}##{method_name}")
226
230
 
227
231
  # Create persistent workspace and checkout to target branch
228
232
  workspace_path = CodeHealer::HealingWorkspaceManager.create_healing_workspace(
@@ -230,12 +234,12 @@ module CodeHealer
230
234
  CodeHealer::ConfigManager.pr_target_branch # Use configured target branch
231
235
  )
232
236
 
233
- puts "โœ… Healing workspace ready: #{workspace_path}"
237
+ PresentationLogger.detail("Workspace ready: #{workspace_path}")
234
238
  workspace_path
235
239
  end
236
240
 
237
241
  def apply_fixes_in_workspace(workspace_path, error, class_name, method_name, evolution_method)
238
- puts "๐Ÿ”ง Applying fixes in isolated workspace"
242
+ PresentationLogger.detail("Applying fixes in workspace #{workspace_path}")
239
243
 
240
244
  case evolution_method
241
245
  when 'claude_code_terminal'
@@ -251,13 +255,13 @@ module CodeHealer
251
255
  end
252
256
  handle_api_evolution_in_workspace(workspace_path, error, class_name, method_name)
253
257
  else
254
- puts "โŒ Unknown evolution method: #{evolution_method}"
258
+ PresentationLogger.error("Unknown evolution method: #{evolution_method}")
255
259
  false
256
260
  end
257
261
  end
258
262
 
259
263
  def handle_claude_code_evolution_in_workspace(workspace_path, error, class_name, method_name)
260
- puts "๐Ÿค– Using Claude Code Terminal for evolution in workspace..."
264
+ PresentationLogger.step("Claude Code Terminal evolution")
261
265
 
262
266
  # Change to workspace directory for Claude Code operations
263
267
  Dir.chdir(workspace_path) do
@@ -266,17 +270,17 @@ module CodeHealer
266
270
  )
267
271
 
268
272
  if success
269
- puts "โœ… Claude Code evolution completed successfully in workspace!"
273
+ PresentationLogger.success("Claude evolution succeeded")
270
274
  true
271
275
  else
272
- puts "โŒ Claude Code evolution failed in workspace"
276
+ PresentationLogger.error("Claude evolution failed")
273
277
  false
274
278
  end
275
279
  end
276
280
  end
277
281
 
278
282
  def handle_api_evolution_in_workspace(workspace_path, error, class_name, method_name)
279
- puts "๐ŸŒ Using OpenAI API for evolution in workspace..."
283
+ PresentationLogger.step("OpenAI API evolution")
280
284
 
281
285
  # Load business context for API evolution
282
286
  business_context = CodeHealer::BusinessContextManager.get_context_for_error(
@@ -286,7 +290,7 @@ module CodeHealer
286
290
  # Optionally record business context used
287
291
  # MetricsCollector.track_business_context(healing_id, business_context) # healing_id not accessible here
288
292
 
289
- puts "๐Ÿ“‹ Business context loaded for API evolution"
293
+ PresentationLogger.detail("Business context loaded for API evolution")
290
294
 
291
295
  # Change to workspace directory for API operations
292
296
  Dir.chdir(workspace_path) do
@@ -295,10 +299,10 @@ module CodeHealer
295
299
  )
296
300
 
297
301
  if success
298
- puts "โœ… API evolution completed successfully in workspace!"
302
+ PresentationLogger.success("API evolution succeeded")
299
303
  true
300
304
  else
301
- puts "โŒ API evolution failed in workspace"
305
+ PresentationLogger.error("API evolution failed")
302
306
  false
303
307
  end
304
308
  end
@@ -307,7 +311,7 @@ module CodeHealer
307
311
  def cleanup_workspace(workspace_path)
308
312
  return unless workspace_path && Dir.exist?(workspace_path)
309
313
 
310
- puts "๐Ÿงน Cleaning up healing workspace: #{workspace_path}"
314
+ PresentationLogger.step("Cleaning up workspace")
311
315
  CodeHealer::HealingWorkspaceManager.cleanup_workspace(workspace_path)
312
316
  end
313
317
 
@@ -1,20 +1,18 @@
1
1
  require 'fileutils'
2
2
  require 'securerandom'
3
+ require_relative 'presentation_logger'
3
4
 
4
5
  module CodeHealer
5
6
  # Manages isolated healing workspaces for safe code evolution
6
7
  class HealingWorkspaceManager
7
8
  class << self
8
9
  def create_healing_workspace(repo_path, branch_name = nil)
9
- puts "๐Ÿฅ [WORKSPACE] Starting workspace creation..."
10
- puts "๐Ÿฅ [WORKSPACE] Repo path: #{repo_path}"
11
- puts "๐Ÿฅ [WORKSPACE] Target branch: #{branch_name || 'default'}"
10
+ PresentationLogger.workspace_action("Preparing isolated workspace")
11
+ PresentationLogger.detail("Repo: #{repo_path}")
12
+ PresentationLogger.detail("Target branch: #{branch_name || 'default'}")
12
13
 
13
14
  config = CodeHealer::ConfigManager.code_heal_directory_config
14
- puts "๐Ÿฅ [WORKSPACE] Raw config: #{config.inspect}"
15
-
16
15
  base_path = config['path'] || config[:path] || '/tmp/code_healer_workspaces'
17
- puts "๐Ÿฅ [WORKSPACE] Base heal dir: #{base_path}"
18
16
 
19
17
  # Use persistent workspace ID based on repo (if enabled)
20
18
  if CodeHealer::ConfigManager.persistent_workspaces_enabled?
@@ -28,39 +26,36 @@ module CodeHealer
28
26
  end
29
27
 
30
28
  if CodeHealer::ConfigManager.persistent_workspaces_enabled?
31
- puts "๐Ÿฅ [WORKSPACE] Persistent workspace ID: #{workspace_id}"
29
+ PresentationLogger.detail("Using persistent workspace: #{workspace_id}")
32
30
  else
33
- puts "๐Ÿฅ [WORKSPACE] Temporary workspace ID: #{workspace_id}"
31
+ PresentationLogger.detail("Creating temporary workspace: #{workspace_id}")
34
32
  end
35
- puts "๐Ÿฅ [WORKSPACE] Full workspace path: #{workspace_path}"
36
33
 
37
34
  begin
38
- puts "๐Ÿฅ [WORKSPACE] Creating base directory..."
39
35
  # Ensure code heal directory exists
40
36
  FileUtils.mkdir_p(base_path)
41
- puts "๐Ÿฅ [WORKSPACE] Base directory created/verified: #{base_path}"
42
37
 
43
38
  # Check if workspace already exists
44
39
  if Dir.exist?(workspace_path) && Dir.exist?(File.join(workspace_path, '.git'))
45
40
  if CodeHealer::ConfigManager.persistent_workspaces_enabled?
46
- puts "๐Ÿฅ [WORKSPACE] Persistent workspace exists, checking out to target branch..."
41
+ PresentationLogger.detail("Reusing existing workspace, checking out target branch")
47
42
  checkout_to_branch(workspace_path, branch_name, repo_path)
48
43
  else
49
- puts "๐Ÿฅ [WORKSPACE] Workspace exists but persistent mode disabled, creating new one..."
44
+ PresentationLogger.detail("Recreating workspace (persistent mode disabled)")
50
45
  cleanup_workspace(workspace_path, true)
51
46
  create_persistent_workspace(repo_path, workspace_path, branch_name)
52
47
  end
53
48
  else
54
- puts "๐Ÿฅ [WORKSPACE] Creating new workspace..."
49
+ PresentationLogger.detail("Creating new workspace")
55
50
  create_persistent_workspace(repo_path, workspace_path, branch_name)
56
51
  end
57
52
 
58
- puts "๐Ÿฅ [WORKSPACE] Workspace ready successfully!"
59
- puts "๐Ÿฅ [WORKSPACE] Final workspace path: #{workspace_path}"
60
- puts "๐Ÿฅ [WORKSPACE] Current branch: #{get_current_branch(workspace_path)}"
53
+ PresentationLogger.success("Workspace ready")
54
+ PresentationLogger.detail("Path: #{workspace_path}")
55
+ PresentationLogger.detail("Branch: #{get_current_branch(workspace_path)}")
61
56
  workspace_path
62
57
  rescue => e
63
- puts "โŒ Failed to create/prepare healing workspace: #{e.message}"
58
+ PresentationLogger.error("Failed to create workspace: #{e.message}")
64
59
  # Don't cleanup persistent workspace on error
65
60
  raise e
66
61
  end
@@ -111,36 +106,42 @@ module CodeHealer
111
106
  def test_fixes_in_workspace(workspace_path)
112
107
  config = CodeHealer::ConfigManager.code_heal_directory_config
113
108
 
114
- puts "๐Ÿงช Testing fixes in workspace: #{workspace_path}"
109
+ PresentationLogger.step("Validating fixes")
115
110
 
116
111
  begin
117
112
  # Change to workspace directory
118
113
  Dir.chdir(workspace_path) do
119
114
  # Run basic syntax check
120
115
  syntax_check = system("ruby -c #{find_ruby_files.join(' ')} 2>/dev/null")
121
- return false unless syntax_check
116
+ unless syntax_check
117
+ PresentationLogger.error("Syntax validation failed")
118
+ return false
119
+ end
122
120
 
123
121
  # Optionally skip heavy tests in demo mode
124
122
  unless CodeHealer::ConfigManager.demo_skip_tests?
125
123
  # Run tests if available
126
124
  if File.exist?('Gemfile')
127
125
  bundle_check = system("bundle check >/dev/null 2>&1")
128
- return false unless bundle_check
126
+ unless bundle_check
127
+ PresentationLogger.error("Bundle check failed")
128
+ return false
129
+ end
129
130
 
130
131
  # Run tests if RSpec is available
131
132
  if File.exist?('spec') || File.exist?('test')
132
133
  test_result = system("bundle exec rspec --dry-run >/dev/null 2>&1") ||
133
134
  system("bundle exec rake test:prepare >/dev/null 2>&1")
134
- puts "๐Ÿงช Test preparation: #{test_result ? 'โœ…' : 'โš ๏ธ'}"
135
+ PresentationLogger.detail("Test preparation: #{test_result ? 'passed' : 'skipped'}")
135
136
  end
136
137
  end
137
138
  end
138
139
 
139
- puts "โœ… Workspace validation passed"
140
+ PresentationLogger.success("Validation passed")
140
141
  true
141
142
  end
142
143
  rescue => e
143
- puts "โŒ Workspace validation failed: #{e.message}"
144
+ PresentationLogger.error("Validation failed: #{e.message}")
144
145
  false
145
146
  end
146
147
  end
@@ -183,16 +184,14 @@ module CodeHealer
183
184
  end
184
185
 
185
186
  def create_healing_branch(repo_path, workspace_path, branch_name)
186
- puts "๐Ÿ”„ Creating healing branch and PR from isolated workspace"
187
+ PresentationLogger.git_action("Creating healing branch and PR")
187
188
 
188
189
  begin
189
190
  # All Git operations happen in the isolated workspace
190
191
  Dir.chdir(workspace_path) do
191
- puts "๐ŸŒฟ [WORKSPACE] Working in isolated workspace: #{workspace_path}"
192
-
193
- # Debug Git configuration
194
- puts "๐ŸŒฟ [WORKSPACE] Git remote origin: #{`git config --get remote.origin.url`.strip}"
195
- puts "๐ŸŒฟ [WORKSPACE] Current branch: #{`git branch --show-current`.strip}"
192
+ PresentationLogger.detail("Working in isolated workspace")
193
+ PresentationLogger.detail("Remote: #{`git config --get remote.origin.url`.strip}")
194
+ PresentationLogger.detail("Current branch: #{`git branch --show-current`.strip}")
196
195
 
197
196
  # Ensure we're on the target branch
198
197
  system("git checkout #{branch_name}")
@@ -202,94 +201,80 @@ module CodeHealer
202
201
  healing_branch = "code-healer-fix-#{Time.now.to_i}"
203
202
  system("git checkout -b #{healing_branch}")
204
203
 
205
- # Check Git status
206
- puts "๐Ÿ“Š [WORKSPACE] Git status in workspace:"
207
- system("git status --porcelain")
208
-
209
204
  # Add all changes (the fixes are already applied in the workspace)
210
205
  add_only_relevant_files(workspace_path)
211
206
 
212
207
  # Validate workspace before commit to ensure no temporary files
213
208
  unless validate_workspace_for_commit(workspace_path)
214
- puts "โš ๏ธ [WORKSPACE] Workspace validation failed, retrying file addition..."
209
+ PresentationLogger.detail("Retrying file addition after validation")
215
210
  add_only_relevant_files(workspace_path)
216
211
  end
217
212
 
218
213
  # Check if there are changes to commit
219
214
  if system("git diff --cached --quiet") == false
220
- puts "๐Ÿ“ [WORKSPACE] Changes detected, committing to healing branch..."
215
+ PresentationLogger.detail("Committing changes to healing branch")
221
216
  commit_message = "Fix applied by CodeHealer: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
222
217
  system("git commit -m '#{commit_message}'")
223
218
 
224
219
  # Push healing branch from workspace
225
- puts "๐Ÿš€ [WORKSPACE] Pushing healing branch from workspace..."
220
+ PresentationLogger.detail("Pushing healing branch")
226
221
  system("git push origin #{healing_branch}")
227
222
 
228
- puts "โœ… [WORKSPACE] Healing branch created and pushed: #{healing_branch}"
229
- puts "๐Ÿ”’ Main repository (#{repo_path}) remains completely untouched"
230
- puts "๐Ÿ“ All changes committed in isolated workspace"
223
+ PresentationLogger.success("Branch created and pushed: #{healing_branch}")
224
+ PresentationLogger.detail("Main repository remains untouched")
231
225
 
232
- # Create pull request if auto-create is enabled
233
- if should_create_pull_request?
234
- puts "๐Ÿ” [WORKSPACE] Auto-creating pull request..."
235
- pr_url = create_pull_request(healing_branch, branch_name)
236
- if pr_url
237
- puts "โœ… [WORKSPACE] Pull request created: #{pr_url}"
238
- else
239
- puts "โš ๏ธ [WORKSPACE] Failed to create pull request"
240
- end
241
- else
242
- puts "๐Ÿ” [WORKSPACE] Review the changes and create a pull request when ready"
243
- end
226
+ # Create pull request if auto-create is enabled
227
+ if should_create_pull_request?
228
+ PresentationLogger.detail("Creating pull request")
229
+ pr_url = create_pull_request(healing_branch, branch_name)
230
+ if pr_url
231
+ PresentationLogger.success("Pull request created: #{pr_url}")
232
+ else
233
+ PresentationLogger.warn("Failed to create pull request")
234
+ end
235
+ else
236
+ PresentationLogger.info("Review changes and create PR when ready")
237
+ end
244
238
 
245
239
  healing_branch
246
240
  else
247
- puts "โš ๏ธ [WORKSPACE] No changes detected in workspace"
248
- puts "๐Ÿ” This might indicate that:"
249
- puts " - The fixes were not applied to the workspace"
250
- puts " - There was an issue with the healing process"
241
+ PresentationLogger.warn("No changes detected in workspace")
242
+ PresentationLogger.detail("This might indicate fixes were not applied or there was an issue")
251
243
 
252
244
  # Delete the empty branch
253
245
  system("git checkout #{branch_name}")
254
246
  system("git branch -D #{healing_branch}")
255
- puts "๐Ÿ—‘๏ธ [WORKSPACE] Deleted empty healing branch: #{healing_branch}"
247
+ PresentationLogger.detail("Deleted empty healing branch: #{healing_branch}")
256
248
  nil
257
249
  end
258
250
  end
259
251
  rescue => e
260
- puts "โŒ Failed to create healing branch from workspace: #{e.message}"
252
+ PresentationLogger.error("Failed to create healing branch: #{e.message}")
261
253
  nil
262
254
  end
263
255
  end
264
256
 
265
257
  def cleanup_workspace(workspace_path, force = false)
266
- puts "๐Ÿงน [WORKSPACE] Starting workspace cleanup..."
267
- puts "๐Ÿงน [WORKSPACE] Target: #{workspace_path}"
268
- puts "๐Ÿงน [WORKSPACE] Force cleanup: #{force}"
269
- puts "๐Ÿงน [WORKSPACE] Exists: #{Dir.exist?(workspace_path)}"
270
-
271
258
  return unless Dir.exist?(workspace_path)
272
259
 
273
260
  # Check if this is a persistent workspace
274
261
  is_persistent = workspace_path.include?('persistent_')
275
262
 
276
263
  if is_persistent && !force
277
- puts "๐Ÿงน [WORKSPACE] This is a persistent workspace - skipping cleanup"
278
- puts "๐Ÿงน [WORKSPACE] Use force=true to override"
264
+ PresentationLogger.detail("Skipping cleanup of persistent workspace")
279
265
  return
280
266
  end
281
267
 
268
+ PresentationLogger.detail("Cleaning up workspace")
269
+
282
270
  # Remove .git directory first to avoid conflicts
283
271
  git_dir = File.join(workspace_path, '.git')
284
272
  if Dir.exist?(git_dir)
285
- puts "๐Ÿงน [WORKSPACE] Removing .git directory to prevent conflicts..."
286
273
  FileUtils.rm_rf(git_dir)
287
274
  end
288
275
 
289
- puts "๐Ÿงน [WORKSPACE] Removing workspace directory..."
290
276
  FileUtils.rm_rf(workspace_path)
291
- puts "๐Ÿงน [WORKSPACE] Workspace cleanup completed"
292
- puts "๐Ÿงน [WORKSPACE] Directory still exists: #{Dir.exist?(workspace_path)}"
277
+ PresentationLogger.detail("Workspace cleanup completed")
293
278
  end
294
279
 
295
280
  def cleanup_expired_workspaces
@@ -0,0 +1,114 @@
1
+ module CodeHealer
2
+ # Presentation-focused logger for conference demos and clean operator output
3
+ class PresentationLogger
4
+ class << self
5
+ def verbose?
6
+ env_flag = ENV.fetch('CODE_HEALER_VERBOSE', 'false')
7
+ env_flag.to_s.downcase == 'true'
8
+ end
9
+
10
+ def section(title)
11
+ divider
12
+ puts "\n๐ŸŽค #{title}\n"
13
+ end
14
+
15
+ def step(message)
16
+ puts "โžก๏ธ #{message}"
17
+ end
18
+
19
+ def info(message)
20
+ puts "โ„น๏ธ #{message}"
21
+ end
22
+
23
+ def success(message)
24
+ puts "โœ… #{message}"
25
+ end
26
+
27
+ def warn(message)
28
+ puts "โš ๏ธ #{message}"
29
+ end
30
+
31
+ def error(message)
32
+ puts "โŒ #{message}"
33
+ end
34
+
35
+ def detail(message)
36
+ return unless verbose?
37
+ puts " ยท #{message}"
38
+ end
39
+
40
+ def kv(label, value)
41
+ # Truncate long values for presentation
42
+ display_value = case value
43
+ when Array
44
+ if value.length > 3
45
+ "#{value.first(2).join(', ')}... (#{value.length} total)"
46
+ else
47
+ value.join(', ')
48
+ end
49
+ when String
50
+ value.length > 100 ? "#{value[0..97]}..." : value
51
+ else
52
+ value.to_s
53
+ end
54
+ puts " โ€ข #{label}: #{display_value}"
55
+ end
56
+
57
+ def backtrace(backtrace_array)
58
+ return unless backtrace_array&.any?
59
+
60
+ # Show only the first 3 relevant lines for presentation
61
+ relevant_lines = backtrace_array.first(3).map do |line|
62
+ # Extract just the file and line number for cleaner display
63
+ if line.match?(/^(.+\.rb):(\d+):in/)
64
+ file = File.basename($1)
65
+ line_num = $2
66
+ method = line.match(/in `(.+)'/)&.[](1) || 'unknown'
67
+ "#{file}:#{line_num} in #{method}"
68
+ else
69
+ line
70
+ end
71
+ end
72
+
73
+ puts " โ€ข Backtrace: #{relevant_lines.join(' โ†’ ')}"
74
+ detail("Full backtrace available with CODE_HEALER_VERBOSE=true") if backtrace_array.length > 3
75
+ end
76
+
77
+ def time(label, ms)
78
+ puts "โฑ๏ธ #{label}: #{ms} ms"
79
+ end
80
+
81
+ def divider
82
+ puts "\nโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n"
83
+ end
84
+
85
+ def outcome(success:, branch: nil, pr_url: nil, reason: nil, timing: nil)
86
+ if success
87
+ success_msg = "๐ŸŽ‰ Healing complete"
88
+ success_msg << " (#{timing})" if timing
89
+ success(success_msg)
90
+ puts " โ€ข Branch: #{branch}" if branch
91
+ puts " โ€ข PR: #{pr_url}" if pr_url
92
+ else
93
+ error_msg = "๐Ÿ’ฅ Healing failed"
94
+ error_msg << " (#{reason})" if reason
95
+ error(error_msg)
96
+ end
97
+ end
98
+
99
+ def claude_action(action)
100
+ puts "๐Ÿค– #{action}"
101
+ end
102
+
103
+ def workspace_action(action)
104
+ puts "๐Ÿ—๏ธ #{action}"
105
+ end
106
+
107
+ def git_action(action)
108
+ puts "๐Ÿ“ #{action}"
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CodeHealer
4
- VERSION = "0.1.24"
4
+ VERSION = "0.1.26"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code_healer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.24
4
+ version: 0.1.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Deepan Kumar
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-31 00:00:00.000000000 Z
11
+ date: 2025-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -401,6 +401,7 @@ files:
401
401
  - lib/code_healer/mcp_server.rb
402
402
  - lib/code_healer/mcp_tools.rb
403
403
  - lib/code_healer/models/healing_metric.rb
404
+ - lib/code_healer/presentation_logger.rb
404
405
  - lib/code_healer/pull_request_creator.rb
405
406
  - lib/code_healer/routes.rb
406
407
  - lib/code_healer/services/metrics_collector.rb