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 +4 -4
- data/CHANGELOG.md +46 -0
- data/lib/code_healer/claude_code_evolution_handler.rb +141 -53
- data/lib/code_healer/config_manager.rb +16 -23
- data/lib/code_healer/core.rb +36 -25
- data/lib/code_healer/healing_job.rb +33 -29
- data/lib/code_healer/healing_workspace_manager.rb +132 -118
- data/lib/code_healer/mcp_server.rb +8 -7
- data/lib/code_healer/presentation_logger.rb +114 -0
- data/lib/code_healer/setup.rb +9 -45
- data/lib/code_healer/version.rb +1 -1
- metadata +3 -2
@@ -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
|
-
|
10
|
-
|
11
|
-
|
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,82 +26,79 @@ module CodeHealer
|
|
28
26
|
end
|
29
27
|
|
30
28
|
if CodeHealer::ConfigManager.persistent_workspaces_enabled?
|
31
|
-
|
29
|
+
PresentationLogger.detail("Using persistent workspace: #{workspace_id}")
|
32
30
|
else
|
33
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
49
|
+
PresentationLogger.detail("Creating new workspace")
|
55
50
|
create_persistent_workspace(repo_path, workspace_path, branch_name)
|
56
51
|
end
|
57
52
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
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
|
67
62
|
end
|
68
63
|
|
69
64
|
def apply_fixes_in_workspace(workspace_path, fixes, class_name, method_name)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
65
|
+
PresentationLogger.detail("Starting fix application...")
|
66
|
+
PresentationLogger.detail("Workspace: #{workspace_path}")
|
67
|
+
PresentationLogger.detail("Class: #{class_name}, Method: #{method_name}")
|
68
|
+
PresentationLogger.detail("Fixes to apply: #{fixes.inspect}")
|
74
69
|
|
75
70
|
begin
|
76
|
-
|
71
|
+
PresentationLogger.detail("Processing #{fixes.length} fixes...")
|
77
72
|
# Apply each fix to the workspace
|
78
73
|
fixes.each_with_index do |fix, index|
|
79
|
-
|
74
|
+
PresentationLogger.detail("Processing fix #{index + 1}: #{fix.inspect}")
|
80
75
|
file_path = File.join(workspace_path, fix[:file_path])
|
81
|
-
|
82
|
-
|
76
|
+
PresentationLogger.detail("Target file: #{file_path}")
|
77
|
+
PresentationLogger.detail("File exists: #{File.exist?(file_path)}")
|
83
78
|
|
84
79
|
next unless File.exist?(file_path)
|
85
80
|
|
86
|
-
|
81
|
+
PresentationLogger.detail("Creating backup...")
|
87
82
|
# Backup original file
|
88
83
|
backup_file(file_path)
|
89
84
|
|
90
|
-
|
85
|
+
PresentationLogger.detail("Applying fix to file...")
|
91
86
|
# Apply the fix
|
92
87
|
apply_fix_to_file(file_path, fix[:new_code], class_name, method_name)
|
93
88
|
end
|
94
89
|
|
95
90
|
# Show workspace Git status after applying fixes
|
96
91
|
Dir.chdir(workspace_path) do
|
97
|
-
|
92
|
+
PresentationLogger.detail("Git status after fixes:")
|
98
93
|
system("git status --porcelain")
|
99
|
-
|
94
|
+
PresentationLogger.detail("Git diff after fixes:")
|
100
95
|
system("git diff")
|
101
96
|
end
|
102
97
|
|
103
|
-
|
98
|
+
PresentationLogger.success("Fixes applied successfully in workspace")
|
104
99
|
true
|
105
100
|
rescue => e
|
106
|
-
|
101
|
+
PresentationLogger.error("Failed to apply fixes in workspace: #{e.message}")
|
107
102
|
false
|
108
103
|
end
|
109
104
|
end
|
@@ -111,42 +106,47 @@ module CodeHealer
|
|
111
106
|
def test_fixes_in_workspace(workspace_path)
|
112
107
|
config = CodeHealer::ConfigManager.code_heal_directory_config
|
113
108
|
|
114
|
-
|
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
|
-
|
116
|
+
unless syntax_check
|
117
|
+
PresentationLogger.error("Syntax validation failed")
|
118
|
+
return false
|
119
|
+
end
|
122
120
|
|
123
|
-
#
|
124
|
-
unless CodeHealer::ConfigManager.demo_skip_tests?
|
121
|
+
# Run tests if available
|
125
122
|
# Run tests if available
|
126
123
|
if File.exist?('Gemfile')
|
127
124
|
bundle_check = system("bundle check >/dev/null 2>&1")
|
128
|
-
|
125
|
+
unless bundle_check
|
126
|
+
PresentationLogger.error("Bundle check failed")
|
127
|
+
return false
|
128
|
+
end
|
129
129
|
|
130
130
|
# Run tests if RSpec is available
|
131
131
|
if File.exist?('spec') || File.exist?('test')
|
132
132
|
test_result = system("bundle exec rspec --dry-run >/dev/null 2>&1") ||
|
133
133
|
system("bundle exec rake test:prepare >/dev/null 2>&1")
|
134
|
-
|
134
|
+
PresentationLogger.detail("Test preparation: #{test_result ? 'passed' : 'skipped'}")
|
135
135
|
end
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
|
-
|
139
|
+
PresentationLogger.success("Validation passed")
|
140
140
|
true
|
141
141
|
end
|
142
142
|
rescue => e
|
143
|
-
|
143
|
+
PresentationLogger.error("Validation failed: #{e.message}")
|
144
144
|
false
|
145
145
|
end
|
146
146
|
end
|
147
147
|
|
148
148
|
def validate_workspace_for_commit(workspace_path)
|
149
|
-
|
149
|
+
PresentationLogger.detail("Validating workspace for commit...")
|
150
150
|
|
151
151
|
Dir.chdir(workspace_path) do
|
152
152
|
# Check for any temporary files that might have been added
|
@@ -155,44 +155,42 @@ module CodeHealer
|
|
155
155
|
|
156
156
|
all_files = (staged_files + working_files).uniq.reject(&:empty?)
|
157
157
|
|
158
|
-
|
158
|
+
PresentationLogger.detail("Files to be committed: #{all_files.join(', ')}")
|
159
159
|
|
160
160
|
# Check for any temporary files
|
161
161
|
temp_files = all_files.select { |file| should_skip_file?(file) }
|
162
162
|
|
163
163
|
if temp_files.any?
|
164
|
-
|
165
|
-
temp_files.each { |file|
|
164
|
+
PresentationLogger.warn("Temporary files detected in commit:")
|
165
|
+
temp_files.each { |file| PresentationLogger.detail(" - #{file}") }
|
166
166
|
|
167
167
|
# Remove them from staging
|
168
168
|
temp_files.each do |file|
|
169
|
-
|
169
|
+
PresentationLogger.detail("Removing temporary file from staging: #{file}")
|
170
170
|
system("git reset HEAD '#{file}' 2>/dev/null || true")
|
171
171
|
end
|
172
172
|
|
173
|
-
|
173
|
+
PresentationLogger.detail("Temporary files removed from staging")
|
174
174
|
return false
|
175
175
|
end
|
176
176
|
|
177
|
-
|
177
|
+
PresentationLogger.detail("Workspace validation passed - no temporary files detected")
|
178
178
|
return true
|
179
179
|
end
|
180
180
|
rescue => e
|
181
|
-
|
181
|
+
PresentationLogger.error("Workspace validation failed: #{e.message}")
|
182
182
|
return false
|
183
183
|
end
|
184
184
|
|
185
185
|
def create_healing_branch(repo_path, workspace_path, branch_name)
|
186
|
-
|
186
|
+
PresentationLogger.git_action("Creating healing branch and PR")
|
187
187
|
|
188
188
|
begin
|
189
189
|
# All Git operations happen in the isolated workspace
|
190
190
|
Dir.chdir(workspace_path) do
|
191
|
-
|
192
|
-
|
193
|
-
#
|
194
|
-
puts "🌿 [WORKSPACE] Git remote origin: #{`git config --get remote.origin.url`.strip}"
|
195
|
-
puts "🌿 [WORKSPACE] Current branch: #{`git branch --show-current`.strip}"
|
191
|
+
PresentationLogger.detail("Working in isolated workspace")
|
192
|
+
PresentationLogger.detail("Remote: #{`git config --get remote.origin.url`.strip}")
|
193
|
+
PresentationLogger.detail("Current branch: #{`git branch --show-current`.strip}")
|
196
194
|
|
197
195
|
# Ensure we're on the target branch
|
198
196
|
system("git checkout #{branch_name}")
|
@@ -202,94 +200,80 @@ module CodeHealer
|
|
202
200
|
healing_branch = "code-healer-fix-#{Time.now.to_i}"
|
203
201
|
system("git checkout -b #{healing_branch}")
|
204
202
|
|
205
|
-
# Check Git status
|
206
|
-
puts "📊 [WORKSPACE] Git status in workspace:"
|
207
|
-
system("git status --porcelain")
|
208
|
-
|
209
203
|
# Add all changes (the fixes are already applied in the workspace)
|
210
204
|
add_only_relevant_files(workspace_path)
|
211
205
|
|
212
206
|
# Validate workspace before commit to ensure no temporary files
|
213
207
|
unless validate_workspace_for_commit(workspace_path)
|
214
|
-
|
208
|
+
PresentationLogger.detail("Retrying file addition after validation")
|
215
209
|
add_only_relevant_files(workspace_path)
|
216
210
|
end
|
217
211
|
|
218
212
|
# Check if there are changes to commit
|
219
213
|
if system("git diff --cached --quiet") == false
|
220
|
-
|
214
|
+
PresentationLogger.detail("Committing changes to healing branch")
|
221
215
|
commit_message = "Fix applied by CodeHealer: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
|
222
216
|
system("git commit -m '#{commit_message}'")
|
223
217
|
|
224
218
|
# Push healing branch from workspace
|
225
|
-
|
219
|
+
PresentationLogger.detail("Pushing healing branch")
|
226
220
|
system("git push origin #{healing_branch}")
|
227
221
|
|
228
|
-
|
229
|
-
|
230
|
-
puts "📝 All changes committed in isolated workspace"
|
222
|
+
PresentationLogger.success("Branch created and pushed: #{healing_branch}")
|
223
|
+
PresentationLogger.detail("Main repository remains untouched")
|
231
224
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
225
|
+
# Create pull request if auto-create is enabled
|
226
|
+
if should_create_pull_request?
|
227
|
+
PresentationLogger.detail("Creating pull request")
|
228
|
+
pr_url = create_pull_request(healing_branch, branch_name)
|
229
|
+
if pr_url
|
230
|
+
PresentationLogger.success("Pull request created: #{pr_url}")
|
231
|
+
else
|
232
|
+
PresentationLogger.warn("Failed to create pull request")
|
233
|
+
end
|
234
|
+
else
|
235
|
+
PresentationLogger.info("Review changes and create PR when ready")
|
236
|
+
end
|
244
237
|
|
245
238
|
healing_branch
|
246
239
|
else
|
247
|
-
|
248
|
-
|
249
|
-
puts " - The fixes were not applied to the workspace"
|
250
|
-
puts " - There was an issue with the healing process"
|
240
|
+
PresentationLogger.warn("No changes detected in workspace")
|
241
|
+
PresentationLogger.detail("This might indicate fixes were not applied or there was an issue")
|
251
242
|
|
252
243
|
# Delete the empty branch
|
253
244
|
system("git checkout #{branch_name}")
|
254
245
|
system("git branch -D #{healing_branch}")
|
255
|
-
|
246
|
+
PresentationLogger.detail("Deleted empty healing branch: #{healing_branch}")
|
256
247
|
nil
|
257
248
|
end
|
258
249
|
end
|
259
250
|
rescue => e
|
260
|
-
|
251
|
+
PresentationLogger.error("Failed to create healing branch: #{e.message}")
|
261
252
|
nil
|
262
253
|
end
|
263
254
|
end
|
264
255
|
|
265
256
|
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
257
|
return unless Dir.exist?(workspace_path)
|
272
258
|
|
273
259
|
# Check if this is a persistent workspace
|
274
260
|
is_persistent = workspace_path.include?('persistent_')
|
275
261
|
|
276
262
|
if is_persistent && !force
|
277
|
-
|
278
|
-
puts "🧹 [WORKSPACE] Use force=true to override"
|
263
|
+
PresentationLogger.detail("Skipping cleanup of persistent workspace")
|
279
264
|
return
|
280
265
|
end
|
281
266
|
|
267
|
+
PresentationLogger.detail("Cleaning up workspace")
|
268
|
+
|
282
269
|
# Remove .git directory first to avoid conflicts
|
283
270
|
git_dir = File.join(workspace_path, '.git')
|
284
271
|
if Dir.exist?(git_dir)
|
285
|
-
puts "🧹 [WORKSPACE] Removing .git directory to prevent conflicts..."
|
286
272
|
FileUtils.rm_rf(git_dir)
|
287
273
|
end
|
288
274
|
|
289
|
-
puts "🧹 [WORKSPACE] Removing workspace directory..."
|
290
275
|
FileUtils.rm_rf(workspace_path)
|
291
|
-
|
292
|
-
puts "🧹 [WORKSPACE] Directory still exists: #{Dir.exist?(workspace_path)}"
|
276
|
+
PresentationLogger.detail("Workspace cleanup completed")
|
293
277
|
end
|
294
278
|
|
295
279
|
def cleanup_expired_workspaces
|
@@ -348,7 +332,23 @@ module CodeHealer
|
|
348
332
|
|
349
333
|
puts "🔗 Creating PR for repository: #{repo_name}"
|
350
334
|
|
351
|
-
|
335
|
+
# Configure Octokit with better error handling
|
336
|
+
client = Octokit::Client.new(
|
337
|
+
access_token: github_token,
|
338
|
+
api_endpoint: 'https://api.github.com',
|
339
|
+
web_endpoint: 'https://github.com',
|
340
|
+
auto_paginate: true,
|
341
|
+
per_page: 100
|
342
|
+
)
|
343
|
+
|
344
|
+
# Test the connection first
|
345
|
+
begin
|
346
|
+
user = client.user
|
347
|
+
puts "✅ GitHub authentication successful for user: #{user.login}"
|
348
|
+
rescue => auth_error
|
349
|
+
puts "❌ GitHub authentication failed: #{auth_error.message}"
|
350
|
+
return nil
|
351
|
+
end
|
352
352
|
|
353
353
|
# Create pull request
|
354
354
|
pr = client.create_pull_request(
|
@@ -363,8 +363,21 @@ module CodeHealer
|
|
363
363
|
|
364
364
|
puts "✅ Pull request created successfully: #{pr.html_url}"
|
365
365
|
pr.html_url
|
366
|
+
rescue Octokit::Unauthorized => e
|
367
|
+
puts "❌ GitHub authentication failed: #{e.message}"
|
368
|
+
puts "💡 Check your GitHub token permissions and validity"
|
369
|
+
nil
|
370
|
+
rescue Octokit::NotFound => e
|
371
|
+
puts "❌ Repository not found: #{e.message}"
|
372
|
+
puts "💡 Check repository name and access permissions"
|
373
|
+
nil
|
374
|
+
rescue Octokit::UnprocessableEntity => e
|
375
|
+
puts "❌ Invalid pull request data: #{e.message}"
|
376
|
+
puts "💡 Check branch names and repository state"
|
377
|
+
nil
|
366
378
|
rescue => e
|
367
379
|
puts "❌ Failed to create pull request: #{e.message}"
|
380
|
+
puts "💡 Error class: #{e.class}"
|
368
381
|
puts "💡 Check your GitHub token and repository access"
|
369
382
|
nil
|
370
383
|
end
|
@@ -413,66 +426,66 @@ module CodeHealer
|
|
413
426
|
end
|
414
427
|
|
415
428
|
def create_persistent_workspace(repo_path, workspace_path, branch_name)
|
416
|
-
|
429
|
+
PresentationLogger.detail("Creating new persistent workspace...")
|
417
430
|
|
418
431
|
# Get the GitHub remote URL
|
419
432
|
Dir.chdir(repo_path) do
|
420
433
|
remote_url = `git config --get remote.origin.url`.strip
|
421
434
|
if remote_url.empty?
|
422
|
-
|
435
|
+
PresentationLogger.error("No remote origin found in #{repo_path}")
|
423
436
|
return false
|
424
437
|
end
|
425
438
|
|
426
|
-
|
427
|
-
|
439
|
+
PresentationLogger.detail("Cloning from: #{remote_url}")
|
440
|
+
PresentationLogger.detail("To workspace: #{workspace_path}")
|
428
441
|
|
429
442
|
# Clone the full repository for persistent use
|
430
443
|
result = system("git clone #{remote_url} #{workspace_path}")
|
431
444
|
if result
|
432
|
-
|
445
|
+
PresentationLogger.detail("Repository cloned successfully")
|
433
446
|
# Now checkout to the target branch
|
434
447
|
checkout_to_branch(workspace_path, branch_name, repo_path)
|
435
448
|
else
|
436
|
-
|
449
|
+
PresentationLogger.error("Failed to clone repository")
|
437
450
|
return false
|
438
451
|
end
|
439
452
|
end
|
440
453
|
end
|
441
454
|
|
442
455
|
def checkout_to_branch(workspace_path, branch_name, repo_path)
|
443
|
-
|
456
|
+
PresentationLogger.detail("Checking out to target branch...")
|
444
457
|
|
445
458
|
# Determine target branch
|
446
459
|
target_branch = branch_name || CodeHealer::ConfigManager.pr_target_branch || get_default_branch(repo_path)
|
447
|
-
|
460
|
+
PresentationLogger.detail("Target branch: #{target_branch}")
|
448
461
|
|
449
462
|
Dir.chdir(workspace_path) do
|
450
463
|
# Fetch latest changes
|
451
|
-
|
464
|
+
PresentationLogger.detail("Fetching latest changes...")
|
452
465
|
system("git fetch origin")
|
453
466
|
|
454
467
|
# Check if branch exists locally
|
455
468
|
local_branch_exists = system("git show-ref --verify --quiet refs/heads/#{target_branch}")
|
456
469
|
|
457
470
|
if local_branch_exists
|
458
|
-
|
471
|
+
PresentationLogger.detail("Checking out existing local branch: #{target_branch}")
|
459
472
|
system("git checkout #{target_branch}")
|
460
473
|
else
|
461
|
-
|
474
|
+
PresentationLogger.detail("Checking out remote branch: #{target_branch}")
|
462
475
|
system("git checkout -b #{target_branch} origin/#{target_branch}")
|
463
476
|
end
|
464
477
|
|
465
478
|
# Pull latest changes
|
466
|
-
|
479
|
+
PresentationLogger.detail("Pulling latest changes...")
|
467
480
|
system("git pull origin #{target_branch}")
|
468
481
|
|
469
482
|
# Ensure workspace is clean
|
470
|
-
|
483
|
+
PresentationLogger.detail("Ensuring workspace is clean...")
|
471
484
|
system("git reset --hard HEAD")
|
472
485
|
system("git clean -fd")
|
473
486
|
|
474
487
|
# Remove any tracked temporary files that shouldn't be committed - AGGRESSIVE cleanup
|
475
|
-
|
488
|
+
PresentationLogger.detail("Removing tracked temporary files...")
|
476
489
|
|
477
490
|
# Remove root level temporary directories
|
478
491
|
system("git rm -r --cached tmp/ 2>/dev/null || true")
|
@@ -492,7 +505,7 @@ module CodeHealer
|
|
492
505
|
system("find . -name '*.log' -exec git rm --cached {} + 2>/dev/null || true")
|
493
506
|
system("find . -name '*.cache' -exec git rm --cached {} + 2>/dev/null || true")
|
494
507
|
|
495
|
-
|
508
|
+
PresentationLogger.detail("Successfully checked out to: #{target_branch}")
|
496
509
|
end
|
497
510
|
end
|
498
511
|
|
@@ -518,25 +531,26 @@ module CodeHealer
|
|
518
531
|
end
|
519
532
|
|
520
533
|
def add_only_relevant_files(workspace_path)
|
521
|
-
|
534
|
+
PresentationLogger.detail("Adding only relevant files, respecting .gitignore...")
|
522
535
|
|
523
536
|
Dir.chdir(workspace_path) do
|
524
537
|
# First, ensure .gitignore is respected
|
525
538
|
if File.exist?('.gitignore')
|
526
|
-
|
539
|
+
PresentationLogger.detail("Using repository's .gitignore file")
|
527
540
|
else
|
528
|
-
|
541
|
+
PresentationLogger.detail("No .gitignore found, using default patterns")
|
529
542
|
end
|
530
543
|
|
531
544
|
# Get list of modified files
|
532
545
|
modified_files = `git status --porcelain | grep '^ M\\|^M \\|^A ' | awk '{print $2}'`.strip.split("\n")
|
546
|
+
|
533
547
|
|
534
548
|
if modified_files.empty?
|
535
|
-
|
549
|
+
PresentationLogger.detail("No modified files to add")
|
536
550
|
return
|
537
551
|
end
|
538
552
|
|
539
|
-
|
553
|
+
PresentationLogger.detail("Modified files: #{modified_files.join(', ')}")
|
540
554
|
|
541
555
|
# Add each modified file individually
|
542
556
|
modified_files.each do |file|
|
@@ -544,15 +558,15 @@ module CodeHealer
|
|
544
558
|
|
545
559
|
# Skip temporary and generated files
|
546
560
|
if should_skip_file?(file)
|
547
|
-
|
561
|
+
PresentationLogger.detail("Skipping temporary file: #{file}")
|
548
562
|
next
|
549
563
|
end
|
550
564
|
|
551
|
-
|
565
|
+
PresentationLogger.detail("Adding file: #{file}")
|
552
566
|
system("git add '#{file}'")
|
553
567
|
end
|
554
568
|
|
555
|
-
|
569
|
+
PresentationLogger.detail("File addition completed")
|
556
570
|
end
|
557
571
|
end
|
558
572
|
|
@@ -609,7 +623,7 @@ module CodeHealer
|
|
609
623
|
|
610
624
|
# Additional check: if path contains 'tmp' or 'log' anywhere, skip it
|
611
625
|
if file_path.include?('tmp') || file_path.include?('log')
|
612
|
-
|
626
|
+
PresentationLogger.detail("Skipping file containing 'tmp' or 'log': #{file_path}")
|
613
627
|
return true
|
614
628
|
end
|
615
629
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative 'mcp_tools'
|
2
2
|
require_relative 'mcp_prompts'
|
3
|
+
require_relative 'presentation_logger'
|
3
4
|
|
4
5
|
module CodeHealer
|
5
6
|
class McpServer
|
@@ -45,7 +46,7 @@ module CodeHealer
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def analyze_error(error, context)
|
48
|
-
|
49
|
+
PresentationLogger.detail("MCP analyzing error: #{error.class} - #{error.message}")
|
49
50
|
|
50
51
|
# Extract class and method names from context
|
51
52
|
class_name = context[:class_name] || 'UnknownClass'
|
@@ -61,11 +62,11 @@ module CodeHealer
|
|
61
62
|
server_context: { codebase_context: context }
|
62
63
|
)
|
63
64
|
|
64
|
-
|
65
|
+
PresentationLogger.detail("MCP analysis complete")
|
65
66
|
# Parse the JSON response from MCP tool
|
66
67
|
JSON.parse(result.content.first[:text])
|
67
68
|
else
|
68
|
-
|
69
|
+
PresentationLogger.detail("ErrorAnalysisTool not available, using fallback analysis")
|
69
70
|
# Fallback analysis
|
70
71
|
{
|
71
72
|
severity: 'medium',
|
@@ -78,13 +79,13 @@ module CodeHealer
|
|
78
79
|
end
|
79
80
|
|
80
81
|
def generate_contextual_fix(error, analysis, context)
|
81
|
-
|
82
|
+
PresentationLogger.detail("MCP generating contextual fix...")
|
82
83
|
|
83
84
|
# Extract class and method names from context
|
84
85
|
class_name = context[:class_name] || 'UnknownClass'
|
85
86
|
method_name = context[:method_name] || 'unknown_method'
|
86
87
|
|
87
|
-
|
88
|
+
PresentationLogger.detail("Debug: class_name = #{class_name}, method_name = #{method_name}")
|
88
89
|
|
89
90
|
# Use MCP tool to generate fix
|
90
91
|
if defined?(CodeFixTool)
|
@@ -101,11 +102,11 @@ module CodeHealer
|
|
101
102
|
}
|
102
103
|
)
|
103
104
|
|
104
|
-
|
105
|
+
PresentationLogger.detail("MCP generated intelligent fix")
|
105
106
|
# Parse the JSON response from MCP tool
|
106
107
|
JSON.parse(result.content.first[:text])
|
107
108
|
else
|
108
|
-
|
109
|
+
PresentationLogger.detail("CodeFixTool not available, using fallback fix generation")
|
109
110
|
# Fallback fix generation
|
110
111
|
generate_fallback_fix(error, class_name, method_name)
|
111
112
|
end
|