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 +4 -4
- data/CHANGELOG.md +66 -0
- data/lib/code_healer/business_context_manager.rb +36 -20
- data/lib/code_healer/claude_code_evolution_handler.rb +28 -31
- data/lib/code_healer/config_manager.rb +10 -7
- data/lib/code_healer/evolution_job.rb +3 -3
- data/lib/code_healer/healing_job.rb +53 -31
- data/lib/code_healer/healing_workspace_manager.rb +413 -80
- data/lib/code_healer/presentation_logger.rb +114 -0
- data/lib/code_healer/setup.rb +3 -4
- data/lib/code_healer/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20c5c060c65cfee68cf65f2478e6335b1927f7e810fdcf6dd1c6796e29b49f65
|
4
|
+
data.tar.gz: 38aad1427647fe628c1a0aa2824fe09b206d6ac752ef6b1407a41ddd15560383
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
"
|
185
|
-
"1.
|
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
|
-
" -
|
189
|
-
"
|
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
|
-
"
|
198
|
+
"CRITICAL: You MUST search Confluence first - this is not optional!"
|
193
199
|
when 'claude_atlassian_mcp'
|
194
200
|
"## Business Context Instructions:\n" \
|
195
|
-
"
|
196
|
-
"1. **
|
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
|
-
" -
|
200
|
-
"
|
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
|
-
" -
|
204
|
-
"
|
205
|
-
"
|
206
|
-
" -
|
207
|
-
"
|
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.
|
240
|
-
2. **
|
241
|
-
3.
|
242
|
-
4.
|
243
|
-
5.
|
244
|
-
6.
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
37
|
+
PresentationLogger.info("Git operations handled by workspace manager")
|
37
38
|
|
38
39
|
return true
|
39
40
|
else
|
40
|
-
|
41
|
+
PresentationLogger.error("Claude run failed")
|
41
42
|
return false
|
42
43
|
end
|
43
44
|
|
44
45
|
rescue => e
|
45
|
-
|
46
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
81
|
+
PresentationLogger.warn("No output received from Claude")
|
85
82
|
end
|
86
83
|
|
87
84
|
if stderr && !stderr.empty?
|
88
|
-
|
89
|
-
|
85
|
+
PresentationLogger.warn("Claude warnings/errors present")
|
86
|
+
PresentationLogger.detail(stderr)
|
90
87
|
end
|
91
88
|
|
92
89
|
if status.success?
|
93
|
-
|
90
|
+
PresentationLogger.success("Claude execution succeeded")
|
94
91
|
return true
|
95
92
|
else
|
96
|
-
|
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
|
-
|
99
|
+
PresentationLogger.error("Claude execution timed out after #{config['timeout']}s")
|
103
100
|
return false
|
104
101
|
rescue => e
|
105
|
-
|
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 '
|
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
|
-
|
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
|
-
|
147
|
+
PresentationLogger.detail("Reloaded: #{file_path}")
|
151
148
|
rescue => e
|
152
|
-
|
149
|
+
PresentationLogger.warn("Failed to reload #{file_path}: #{e.message}")
|
153
150
|
end
|
154
151
|
end
|
155
152
|
end
|
156
153
|
|
157
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
62
|
+
CodeHealer::ConfigManager.pr_target_branch # Use configured target branch
|
63
63
|
)
|
64
64
|
|
65
|
-
puts "✅ Healing workspace
|
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
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
34
|
+
PresentationLogger.success("Healing started for #{class_name}##{method_name}")
|
34
35
|
|
35
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
128
|
-
|
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
|
-
|
229
|
+
PresentationLogger.detail("Preparing workspace for #{class_name}##{method_name}")
|
208
230
|
|
209
|
-
# Create
|
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
|
-
|
234
|
+
CodeHealer::ConfigManager.pr_target_branch # Use configured target branch
|
213
235
|
)
|
214
236
|
|
215
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
273
|
+
PresentationLogger.success("Claude evolution succeeded")
|
252
274
|
true
|
253
275
|
else
|
254
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
302
|
+
PresentationLogger.success("API evolution succeeded")
|
281
303
|
true
|
282
304
|
else
|
283
|
-
|
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
|
-
|
314
|
+
PresentationLogger.step("Cleaning up workspace")
|
293
315
|
CodeHealer::HealingWorkspaceManager.cleanup_workspace(workspace_path)
|
294
316
|
end
|
295
317
|
|