code_healer 0.1.19 → 0.1.22

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: c459c5cc71ce5eb54b3a1798f45698c8910c71de4c9359aca6ebe143c90c9b2a
4
- data.tar.gz: e521d7a05da6c0653efa4845c4d2e3770569331c699afb5a72b0df5fa7d70d90
3
+ metadata.gz: 7a14639ba1f9033b76d3cc5d3ec3b252b7568c14b22098a4efb6d71f50d95463
4
+ data.tar.gz: 1c3f66f4ffc22405485ad3048a3b1635ed5849a1a46a4881b20647bff33a2118
5
5
  SHA512:
6
- metadata.gz: 72c3f09042c407fa37e82e88bae86a3df5fe6c0dd5ad4db832dfa69fe5531da9c2b60e4b5ae2c9e871d97a7b1b0603e205ebda328d4bcf1b88bf2b04c5df3322
7
- data.tar.gz: defe95f38e8783909646739c05dc77ac8745847d13a00090fd8665027e506cb423eb367a4cb9ff05c5214594041ab3ba8ba9fe0f590f1231931bd3f28ecf0467
6
+ metadata.gz: b78c5c6c5917de5253fcf543132c26679f5d520e1a0d9e80dcfe5b28ee13e9e45b5751449db2addb6e0fb90563ee69d4830fbafb6526977f91b73b083c1e54b1
7
+ data.tar.gz: 78dfc2a6168f34a25fc0eab7be783e63a1b4d18f06c5236e25acf1f6835a262032e23526b4ae0b08f2fd81cfc992fbef6c0e038a5fb1d2e8aa199f0403e8061b
data/CHANGELOG.md CHANGED
@@ -5,7 +5,38 @@ 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
- ## [Unreleased]
8
+ ## [0.1.22] - 2025-08-27
9
+
10
+ ### Fixed
11
+ - **Critical Flag Fix**: Fixed Claude Terminal command to use correct `--print` flag instead of unsupported `--code` flag
12
+ - **Command Compatibility**: Ensured all Claude Terminal flags are compatible with the `--print` command
13
+ - **Fallback Template**: Updated fallback command template to use correct flags
14
+
15
+ ## [0.1.21] - 2025-08-27
16
+
17
+ ### Fixed
18
+ - **Critical Bug Fix**: Resolved `mcp_setup` undefined method error in Claude Code evolution handler
19
+ - **Claude Command Fix**: Fixed command template to use `--print` instead of `--code` for proper code editing
20
+ - **Demo Mode Enhancement**: Improved demo mode handling with optimized Claude command building
21
+ - **Command Cleanup**: Removed duplicate permission flags and incompatible command options
22
+
23
+ ## [0.1.20] - 2025-08-27
24
+
25
+ ### Added
26
+ - **Confluence MCP Integration**: Added direct Confluence MCP tools for business context fetching (optional usage)
27
+ - **Flexible MCP Usage**: Enhanced prompts to optionally use Confluence MCP tools when available
28
+ - **Non-interactive MCP**: Fixed Claude Terminal flags to avoid manual approval prompts
29
+ - **Business Context Strategy**: Added `confluence_only` strategy for focused Confluence documentation usage
30
+
31
+ ### Fixed
32
+ - **MCP Tool Access**: Removed restrictive tool flags that blocked MCP tool usage
33
+ - **Debug Logging**: Cleaned up unnecessary MCP debugging logs from initialization and job startup
34
+ - **Command Optimization**: Fixed Claude Terminal command flags for proper MCP integration
35
+
36
+ ### Changed
37
+ - **Prompt Strategy**: Updated business context prompts to optionally use MCP tools when available
38
+ - **Initialization**: Streamlined gem startup without MCP availability checks
39
+ - **Dependencies**: Maintained `httparty` for MCP API integration while removing debug overhead
9
40
 
10
41
  ## [0.1.19] - 2025-08-21
11
42
 
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Start Claude Terminal with code_healer_session for MCP tools access
4
+ # This session will be used by CodeHealer for healing jobs
5
+
6
+ puts "🚀 Starting Claude Terminal with code_healer_session for MCP tools..."
7
+ puts "This session will be used by CodeHealer for healing jobs with Jira/Confluence access"
8
+ puts ""
9
+ puts "Session name: code_healer_session"
10
+ puts "MCP tools: Jira, Confluence, and other Atlassian integrations"
11
+ puts ""
12
+ puts "Keep this terminal open while running healing jobs!"
13
+ puts "=" * 60
14
+
15
+ # Start Claude Terminal with the session
16
+ exec "claude --session code_healer_session"
data/code_healer.gemspec CHANGED
@@ -60,6 +60,8 @@ Gem::Specification.new do |spec|
60
60
  spec.add_runtime_dependency 'activesupport', '>= 6.0.0'
61
61
  spec.add_runtime_dependency 'actionpack', '>= 6.0.0'
62
62
  spec.add_runtime_dependency 'activemodel', '>= 6.0.0'
63
+ spec.add_runtime_dependency 'httparty', '~> 0.21.0', '>= 0.21.0'
64
+
63
65
 
64
66
  # Development dependencies
65
67
  spec.add_development_dependency "bundler", ">= 2.0.0"
@@ -49,7 +49,39 @@ claude_code:
49
49
  business_context:
50
50
  enabled: true
51
51
 
52
- # Define business rules for specific classes
52
+ # Business Context Strategy
53
+ strategy: "jira_mcp" # Options: "jira_mcp", "markdown", "hybrid"
54
+
55
+ # Jira MCP Configuration (when strategy is "jira_mcp" or "hybrid")
56
+ jira_mcp:
57
+ enabled: true
58
+ project_key: "DGTL" # Your Jira project key
59
+ search_tickets_on_error: true
60
+ include_business_rules: true
61
+ system_prompt: |
62
+ When fixing code, ALWAYS check Jira MCP for business context:
63
+ 1. Search for tickets about the class/method you're fixing
64
+ 2. Use Jira requirements to ensure your fix follows business rules
65
+ 3. Reference specific Jira tickets in your explanation
66
+ 4. Make sure fixes align with business requirements
67
+
68
+ # Markdown Configuration (when strategy is "markdown" or "hybrid")
69
+ markdown:
70
+ enabled: true
71
+ search_paths:
72
+ - "docs/business_rules.md"
73
+ - "docs/requirements.md"
74
+ - "business_requirements/"
75
+ include_patterns:
76
+ - "*.md"
77
+ - "*.txt"
78
+
79
+ # Hybrid Configuration (when strategy is "hybrid")
80
+ hybrid:
81
+ priority: ["jira_mcp", "markdown"] # Which source to check first
82
+ combine_results: true # Merge results from both sources
83
+
84
+ # Define business rules for specific classes (fallback when MCP unavailable)
53
85
  User:
54
86
  domain: "User Management"
55
87
  key_rules:
@@ -177,8 +177,44 @@ module CodeHealer
177
177
  end
178
178
 
179
179
  def build_claude_code_prompt(error, class_name, method_name, file_path)
180
- # Load business context from markdown files
181
- business_context = load_business_context_from_markdown_simple
180
+ # Get business context based on configured strategy
181
+ business_context = case CodeHealer::ConfigManager.business_context_strategy
182
+ when 'confluence_only'
183
+ "## Business Context Instructions:\n" \
184
+ "OPTIONAL: You can use Confluence MCP tools to enhance business context if available.\n\n" \
185
+ "1. If Confluence MCP is available:\n" \
186
+ " - Search for PRDs/specs related to '#{class_name}' / '#{method_name}'.\n" \
187
+ " - Query terms: '#{class_name}', '#{method_name}', 'business rules', 'validation patterns'.\n" \
188
+ " - Apply any found documentation to your fix.\n\n" \
189
+ "2. If Confluence MCP is not available:\n" \
190
+ " - Proceed with standard business logic and error handling practices.\n" \
191
+ " - Use common validation patterns and best practices.\n\n" \
192
+ "Note: MCP usage is optional - proceed with or without it."
193
+ when 'claude_atlassian_mcp'
194
+ "## Business Context Instructions:\n" \
195
+ "OPTIONAL: You can use Atlassian MCP tools to enhance business context if available.\n\n" \
196
+ "1. **If Jira MCP is available:**\n" \
197
+ " - Search for tickets related to '#{class_name}' or '#{method_name}'\n" \
198
+ " - Look for bug reports, requirements, or business rules\n" \
199
+ " - Apply any found ticket context to your fix\n\n" \
200
+ "2. **If Confluence MCP is available:**\n" \
201
+ " - Search for PRDs, technical specs, or business process docs\n" \
202
+ " - Look for domain-specific business rules related to '#{class_name}'\n" \
203
+ " - Apply any found documentation to your fix\n\n" \
204
+ "3. **If MCP tools are not available:**\n" \
205
+ " - Proceed with standard business logic and error handling practices\n" \
206
+ " - Use common validation patterns and best practices\n\n" \
207
+ "Note: MCP usage is optional - proceed with or without it."
208
+ when 'jira_mcp'
209
+ # Use Jira MCP context
210
+ get_jira_business_context(class_name)
211
+ when 'markdown'
212
+ # Use markdown files
213
+ load_business_context_from_markdown_simple
214
+ else
215
+ # Default fallback
216
+ load_business_context_from_markdown_simple
217
+ end
182
218
 
183
219
  prompt = <<~PROMPT
184
220
  I have a Ruby on Rails application with an error that needs fixing.
@@ -195,7 +231,7 @@ module CodeHealer
195
231
  #{error.backtrace&.join("\n") || "No backtrace available"}
196
232
  ```
197
233
 
198
- ## Business Context (from requirements):
234
+ ## Business Context:
199
235
  #{business_context}
200
236
 
201
237
  ## Instructions:
@@ -262,6 +298,24 @@ module CodeHealer
262
298
  prompt.strip
263
299
  end
264
300
 
301
+ def get_jira_business_context(class_name)
302
+ # Get Jira business context using MCP tools
303
+ begin
304
+ # This would integrate with Jira MCP tools
305
+ # For now, return instructions to use Jira MCP
306
+ "## Jira Business Context Instructions:\n" \
307
+ "Use Jira MCP tools to fetch business context:\n" \
308
+ "1. Search for tickets related to '#{class_name}'\n" \
309
+ "2. Look for business requirements and rules\n" \
310
+ "3. Apply the context to your fix\n" \
311
+ "4. Reference specific Jira tickets in your explanation"
312
+ rescue => e
313
+ "## Jira Business Context:\n" \
314
+ "Unable to fetch Jira context: #{e.message}\n" \
315
+ "Please use Jira MCP tools manually to get business context."
316
+ end
317
+ end
318
+
265
319
  private
266
320
 
267
321
  def load_business_context_from_markdown_simple
@@ -110,30 +110,30 @@ module CodeHealer
110
110
  def build_claude_command(prompt, config)
111
111
  # Escape prompt for shell
112
112
  escaped_prompt = prompt.gsub("'", "'\"'\"'")
113
-
114
- # Build command template
115
- command_template = config['command_template'] || "claude --code '{prompt}'"
116
-
113
+
114
+ # Build command template for MCP tools access
115
+ command_template = config['command_template'] || "claude --print '{prompt}' --output-format text"
116
+
117
117
  # Replace placeholder
118
118
  command = command_template.gsub('{prompt}', escaped_prompt)
119
-
120
- # Add additional options if configured
121
- if config['include_tests'] && !CodeHealer::ConfigManager.demo_mode?
122
- command += " --append-system-prompt 'Include tests when fixing the code'"
119
+
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
123
  else
124
- command += " --append-system-prompt 'Do NOT create or modify tests'"
125
- end
126
-
127
- if config['max_file_changes']
128
- command += " --append-system-prompt 'Limit changes to #{config['max_file_changes']} files maximum'"
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
129
131
  end
130
-
131
- # Add file editing permissions
132
- command += " --permission-mode acceptEdits --allowedTools Edit"
133
-
134
- # Add current directory access but advise not to scan everything
135
- command += " --add-dir . --append-system-prompt 'Do not scan the whole repo; open only files explicitly referenced.'"
136
-
132
+
133
+ # Add business context instructions
134
+ command += " --append-system-prompt 'Use available MCP tools for business context if needed, but proceed with the fix regardless.'"
135
+
136
+ # Return command
137
137
  command
138
138
  end
139
139
 
@@ -194,6 +194,8 @@ module CodeHealer
194
194
  puts "📝 Evolution attempt logged to #{log_file}"
195
195
  end
196
196
 
197
+
198
+
197
199
  def trigger_git_operations(error, class_name, method_name, file_path)
198
200
  puts "🚀 Triggering Git operations for Claude Code evolution..."
199
201
 
@@ -107,6 +107,57 @@ module CodeHealer
107
107
  config['business_context'] || {}
108
108
  end
109
109
 
110
+ # Business Context Strategy Configuration
111
+ def business_context_strategy
112
+ business_context_settings['strategy'] || 'markdown'
113
+ end
114
+
115
+ def use_jira_mcp?
116
+ business_context_strategy == 'jira_mcp' || business_context_strategy == 'hybrid'
117
+ end
118
+
119
+ def use_markdown_context?
120
+ business_context_strategy == 'markdown' || business_context_strategy == 'hybrid'
121
+ end
122
+
123
+ def use_hybrid_context?
124
+ business_context_strategy == 'hybrid'
125
+ end
126
+
127
+ def jira_mcp_settings
128
+ business_context_settings['jira_mcp'] || {}
129
+ end
130
+
131
+ def markdown_settings
132
+ business_context_settings['markdown'] || {}
133
+ end
134
+
135
+ def hybrid_settings
136
+ business_context_settings['hybrid'] || {}
137
+ end
138
+
139
+ def jira_mcp_enabled?
140
+ use_jira_mcp? && jira_mcp_settings['enabled'] != false
141
+ end
142
+
143
+ def markdown_enabled?
144
+ use_markdown_context? && markdown_settings['enabled'] != false
145
+ end
146
+
147
+ def jira_mcp_system_prompt
148
+ jira_mcp_settings['system_prompt'] || default_jira_mcp_prompt
149
+ end
150
+
151
+ def default_jira_mcp_prompt
152
+ <<~PROMPT
153
+ When fixing code, ALWAYS check Jira MCP for business context:
154
+ 1. Search for tickets about the class/method you're fixing
155
+ 2. Use Jira requirements to ensure your fix follows business rules
156
+ 3. Reference specific Jira tickets in your explanation
157
+ 4. Make sure fixes align with business requirements
158
+ PROMPT
159
+ end
160
+
110
161
  # API Configuration
111
162
  def api_settings
112
163
  config['api'] || {}
@@ -1,4 +1,5 @@
1
1
  require 'sidekiq'
2
+ require 'open3'
2
3
 
3
4
  module CodeHealer
4
5
  class HealingJob
@@ -9,6 +10,8 @@ module CodeHealer
9
10
  def perform(*args)
10
11
  puts "🚀 [HEALING_JOB] Starting job with args: #{args.inspect}"
11
12
 
13
+
14
+
12
15
  # Support both legacy and new invocation styles
13
16
  error, class_name, method_name, evolution_method, backtrace = parse_args(args)
14
17
 
@@ -127,6 +130,47 @@ module CodeHealer
127
130
  end
128
131
 
129
132
  private
133
+
134
+ def log_mcp_tools_availability
135
+ puts "🔍 [HEALING_JOB] Checking MCP tools availability..."
136
+
137
+ begin
138
+ # Check if Claude has MCP tools available
139
+ mcp_check_command = "claude --print 'List all available MCP tools' --output-format text"
140
+ stdout, stderr, status = Open3.capture3(mcp_check_command)
141
+
142
+ if status.success?
143
+ puts "✅ [HEALING_JOB] Claude Terminal is available"
144
+
145
+ # Extract MCP tools from output
146
+ if stdout.include?("MCP tools available") || stdout.include?("mcp__")
147
+ puts "🔧 [HEALING_JOB] MCP tools detected in Claude Terminal"
148
+
149
+ # Log specific MCP tools if found
150
+ if stdout.include?("mcp__atlassian")
151
+ puts " - Atlassian MCP tools: Available"
152
+ puts " - Jira integration: Available"
153
+ puts " - Confluence integration: Available"
154
+ end
155
+
156
+ if stdout.include?("mcp__")
157
+ puts " - Other MCP tools: Available"
158
+ end
159
+ else
160
+ puts "⚠️ [HEALING_JOB] No MCP tools detected in Claude Terminal"
161
+ puts "💡 Make sure Claude Terminal has MCP tools configured"
162
+ end
163
+ else
164
+ puts "❌ [HEALING_JOB] Claude Terminal is not available"
165
+ puts "💡 Make sure Claude Terminal is installed and accessible"
166
+ end
167
+ rescue => e
168
+ puts "⚠️ [HEALING_JOB] Could not check MCP tools: #{e.message}"
169
+ puts "💡 Make sure Claude Terminal is properly installed"
170
+ end
171
+
172
+ puts "🔍 [HEALING_JOB] MCP tools check complete"
173
+ end
130
174
 
131
175
  def parse_args(args)
132
176
  # Formats supported:
@@ -18,7 +18,9 @@ module CodeHealer
18
18
  tools: [
19
19
  ErrorAnalysisTool,
20
20
  CodeFixTool,
21
- ContextAnalysisTool
21
+ ContextAnalysisTool,
22
+ JIRAIntegrationTool, # Add Jira integration tool
23
+ ConfluenceIntegrationTool # Add Confluence integration tool
22
24
  ],
23
25
  server_context: {
24
26
  codebase_context: @codebase_context,
@@ -237,13 +239,231 @@ module CodeHealer
237
239
  end
238
240
 
239
241
  def get_business_context(class_name)
240
- {
242
+ base_context = {
241
243
  domain: determine_business_domain(class_name),
242
244
  criticality: assess_business_criticality(class_name),
243
245
  regulatory_requirements: identify_regulatory_requirements(class_name),
244
246
  sla_requirements: get_sla_requirements(class_name),
245
247
  user_impact: assess_user_impact(class_name)
246
248
  }
249
+
250
+ # Get business context based on configured sources
251
+ business_context = get_configured_business_context(class_name)
252
+ base_context.merge!(business_context)
253
+
254
+ base_context
255
+ end
256
+
257
+ def get_configured_business_context(class_name)
258
+ context = {}
259
+
260
+ # Get business context based on configured strategy
261
+ case CodeHealer::ConfigManager.business_context_strategy
262
+ when 'jira_mcp'
263
+ if CodeHealer::ConfigManager.jira_mcp_enabled?
264
+ context[:strategy] = 'jira_mcp'
265
+ context[:instructions] = CodeHealer::ConfigManager.jira_mcp_system_prompt
266
+ context[:project_key] = CodeHealer::ConfigManager.jira_mcp_settings['project_key']
267
+ end
268
+ when 'markdown'
269
+ if CodeHealer::ConfigManager.markdown_enabled?
270
+ markdown_context = get_markdown_business_context(class_name)
271
+ context[:strategy] = 'markdown'
272
+ context[:markdown_context] = markdown_context
273
+ end
274
+ when 'hybrid'
275
+ # Combine both approaches
276
+ context[:strategy] = 'hybrid'
277
+ if CodeHealer::ConfigManager.jira_mcp_enabled?
278
+ context[:jira_mcp_instructions] = CodeHealer::ConfigManager.jira_mcp_system_prompt
279
+ context[:project_key] = CodeHealer::ConfigManager.jira_mcp_settings['project_key']
280
+ end
281
+ if CodeHealer::ConfigManager.markdown_enabled?
282
+ markdown_context = get_markdown_business_context(class_name)
283
+ context[:markdown_context] = markdown_context
284
+ end
285
+ end
286
+
287
+ context
288
+ end
289
+
290
+ def get_jira_business_context(class_name)
291
+ return {} unless defined?(JIRAIntegrationTool)
292
+
293
+ begin
294
+ # Use MCP tool to get Jira context
295
+ result = JIRAIntegrationTool.call(
296
+ action: "search_tickets",
297
+ search_query: "#{class_name} business rules requirements",
298
+ project_key: get_default_jira_project_key,
299
+ server_context: {}
300
+ )
301
+
302
+ if result && result.content.any?
303
+ data = JSON.parse(result.content.first[:text])
304
+ return {} if data['error']
305
+
306
+ # Extract business context from Jira tickets
307
+ {
308
+ related_tickets: data['tickets']&.first(3) || [],
309
+ business_rules: extract_business_rules_from_tickets(data['tickets']),
310
+ requirements: extract_requirements_from_tickets(data['tickets'])
311
+ }
312
+ end
313
+ rescue => e
314
+ puts "⚠️ Failed to get Jira business context: #{e.message}"
315
+ end
316
+
317
+ {}
318
+ end
319
+
320
+ def get_default_jira_project_key
321
+ # Extract from environment or use default
322
+ ENV['JIRA_PROJECT_KEY'] || 'DGTL'
323
+ end
324
+
325
+ def extract_business_rules_from_tickets(tickets)
326
+ return [] unless tickets
327
+
328
+ tickets.flat_map do |ticket|
329
+ # Extract business rules from ticket summary, description, labels
330
+ rules = []
331
+ rules << ticket['summary'] if ticket['summary']&.include?('rule')
332
+ rules << ticket['summary'] if ticket['summary']&.include?('policy')
333
+ rules << ticket['summary'] if ticket['summary']&.include?('requirement')
334
+ rules
335
+ end.compact.uniq
336
+ end
337
+
338
+ def extract_requirements_from_tickets(tickets)
339
+ return [] unless tickets
340
+
341
+ tickets.flat_map do |ticket|
342
+ # Extract business context from ticket content
343
+ requirements = []
344
+ requirements << ticket['summary'] if ticket['summary']&.include?('requirement')
345
+ requirements << ticket['summary'] if ticket['summary']&.include?('should')
346
+ requirements << ticket['summary'] if ticket['summary']&.include?('need')
347
+ requirements
348
+ end.compact.uniq
349
+ end
350
+
351
+ # Public method for Claude Terminal to access Confluence business context
352
+ def get_confluence_business_context(class_name)
353
+ return {} unless defined?(ConfluenceIntegrationTool)
354
+
355
+ begin
356
+ # Use MCP tool to get Confluence context
357
+ result = ConfluenceIntegrationTool.call(
358
+ action: "search_documents",
359
+ search_query: "#{class_name} PRD requirements business rules",
360
+ space_key: get_default_confluence_space_key,
361
+ server_context: {}
362
+ )
363
+
364
+ if result && result.content.any?
365
+ data = JSON.parse(result.content.first[:text])
366
+ return {} if data['error']
367
+
368
+ # Extract business context from Confluence documents
369
+ {
370
+ related_documents: data['documents']&.first(3) || [],
371
+ prd_content: extract_prd_content(data['documents']),
372
+ business_processes: extract_business_processes(data['documents'])
373
+ }
374
+ end
375
+ rescue => e
376
+ puts "⚠️ Failed to get Confluence business context: #{e.message}"
377
+ end
378
+
379
+ {}
380
+ end
381
+
382
+ # Public method for Claude Terminal to get Confluence space key
383
+ def get_default_confluence_space_key
384
+ # Extract from environment or use default
385
+ ENV['CONFLUENCE_SPACE_KEY'] || 'DGTL'
386
+ end
387
+
388
+ # Public method for Claude Terminal to extract PRD content
389
+ def extract_prd_content(documents)
390
+ return [] unless documents
391
+
392
+ documents.flat_map do |doc|
393
+ # Extract PRD content from document title, content, labels
394
+ content = []
395
+ content << doc['title'] if doc['title']&.include?('PRD')
396
+ content << doc['title'] if doc['title']&.include?('Product Requirements')
397
+ content << doc['title'] if doc['title']&.include?('Requirements')
398
+ content << doc['content']&.truncate(200) if doc['content']
399
+ content
400
+ end.compact.uniq
401
+ end
402
+
403
+ # Public method for Claude Terminal to extract business processes
404
+ def extract_business_processes(documents)
405
+ return [] unless documents
406
+
407
+ documents.flat_map do |doc|
408
+ # Extract business process information from document content
409
+ processes = []
410
+ processes << doc['title'] if doc['title']&.include?('Process')
411
+ processes << doc['title'] if doc['title']&.include?('Workflow')
412
+ processes << doc['title'] if doc['title']&.include?('Procedure')
413
+ processes << doc['content']&.truncate(200) if doc['content']
414
+ processes
415
+ end.compact.uniq
416
+ end
417
+
418
+ # Public method for Claude Terminal to access Markdown business context
419
+ def get_markdown_business_context(class_name)
420
+ return {} unless CodeHealer::ConfigManager.use_markdown_context?
421
+
422
+ begin
423
+ # Load from existing markdown business context
424
+ markdown_context = load_business_requirements_from_markdown
425
+
426
+ if markdown_context.any?
427
+ {
428
+ markdown_files: markdown_context['markdown_requirements'] || [],
429
+ business_rules: extract_business_rules_from_markdown(markdown_context),
430
+ requirements: extract_requirements_from_markdown(markdown_context)
431
+ }
432
+ end
433
+ rescue => e
434
+ puts "⚠️ Failed to get Markdown business context: #{e.message}"
435
+ end
436
+
437
+ {}
438
+ end
439
+
440
+ # Public method for Claude Terminal to extract business rules from markdown
441
+ def extract_business_rules_from_markdown(markdown_context)
442
+ return [] unless markdown_context['markdown_requirements']
443
+
444
+ markdown_context['markdown_requirements'].flat_map do |file_info|
445
+ content = file_info[:content]
446
+ rules = []
447
+ rules << content if content.include?('rule')
448
+ rules << content if content.include?('policy')
449
+ rules << content if content.include?('requirement')
450
+ rules
451
+ end.compact.uniq
452
+ end
453
+
454
+ # Public method for Claude Terminal to extract requirements from markdown
455
+ def extract_requirements_from_markdown(markdown_context)
456
+ return [] unless markdown_context['markdown_requirements']
457
+
458
+ markdown_context['markdown_requirements'].flat_map do |file_info|
459
+ content = file_info[:content]
460
+ requirements = []
461
+ requirements << content if content.include?('requirement')
462
+ requirements << content if content.include?('must')
463
+ requirements << content if content.include?('should')
464
+ requirements << content if content.include?('need')
465
+ requirements
466
+ end.compact.uniq
247
467
  end
248
468
 
249
469
  def get_evolution_history(class_name, method_name)
@@ -1,3 +1,6 @@
1
+ require 'httparty'
2
+ require 'cgi'
3
+
1
4
  module CodeHealer
2
5
  # Tool for analyzing errors with context
3
6
  class ErrorAnalysisTool < MCP::Tool
@@ -1522,9 +1525,14 @@ module CodeHealer
1522
1525
  return { error: "Ticket ID required" } unless ticket_id
1523
1526
 
1524
1527
  begin
1525
- # Simulate JIRA API call - in production, this would use actual JIRA REST API
1526
- if simulate_jira_available?
1527
- ticket_data = simulate_jira_ticket(ticket_id)
1528
+ # Try real JIRA API first, fallback to simulation if it fails
1529
+ if jira_api_available?
1530
+ ticket_data = get_real_jira_ticket(ticket_id)
1531
+
1532
+ if ticket_data[:error]
1533
+ puts "⚠️ Real JIRA API failed, falling back to simulation: #{ticket_data[:error]}"
1534
+ ticket_data = simulate_jira_ticket(ticket_id)
1535
+ end
1528
1536
 
1529
1537
  {
1530
1538
  ticket_id: ticket_id,
@@ -1590,8 +1598,13 @@ module CodeHealer
1590
1598
  return { error: "Project key required" } unless project_key
1591
1599
 
1592
1600
  begin
1593
- if simulate_jira_available?
1594
- project_data = simulate_jira_project(project_key)
1601
+ if jira_api_available?
1602
+ project_data = get_real_jira_project(project_key)
1603
+
1604
+ if project_data[:error]
1605
+ puts "⚠️ Real JIRA API failed, falling back to simulation: #{project_data[:error]}"
1606
+ project_data = simulate_jira_project(project_key)
1607
+ end
1595
1608
 
1596
1609
  {
1597
1610
  project_key: project_key,
@@ -1845,6 +1858,378 @@ module CodeHealer
1845
1858
  priority_distribution: { "High": 20, "Medium": 60, "Low": 20 }
1846
1859
  }
1847
1860
  end
1861
+
1862
+ # Real JIRA API integration methods
1863
+ def self.jira_api_available?
1864
+ # Check if JIRA configuration is available
1865
+ ENV['JIRA_URL'] && ENV['JIRA_USERNAME'] && ENV['JIRA_API_TOKEN']
1866
+ end
1867
+
1868
+ def self.jira_api_call(endpoint, method = :get, body = nil)
1869
+ return { error: "JIRA configuration not available" } unless jira_api_available?
1870
+
1871
+ begin
1872
+ url = "#{ENV['JIRA_URL']}/rest/api/2#{endpoint}"
1873
+ auth = { username: ENV['JIRA_USERNAME'], password: ENV['JIRA_API_TOKEN'] }
1874
+
1875
+ options = {
1876
+ basic_auth: auth,
1877
+ headers: { 'Content-Type' => 'application/json' }
1878
+ }
1879
+
1880
+ case method
1881
+ when :get
1882
+ response = HTTParty.get(url, options)
1883
+ when :post
1884
+ response = HTTParty.post(url, options.merge(body: body.to_json))
1885
+ when :put
1886
+ response = HTTParty.put(url, options.merge(body: body.to_json))
1887
+ else
1888
+ return { error: "Unsupported HTTP method: #{method}" }
1889
+ end
1890
+
1891
+ if response.success?
1892
+ JSON.parse(response.body)
1893
+ else
1894
+ { error: "JIRA API error: #{response.code} - #{response.body}" }
1895
+ end
1896
+ rescue => e
1897
+ { error: "JIRA API call failed: #{e.message}" }
1898
+ end
1899
+ end
1900
+
1901
+ def self.get_real_jira_ticket(ticket_id)
1902
+ endpoint = "/issue/#{ticket_id}"
1903
+ response = jira_api_call(endpoint)
1904
+
1905
+ return response if response['error']
1906
+
1907
+ # Transform Jira API response to our format
1908
+ {
1909
+ id: response['key'],
1910
+ summary: response['fields']['summary'],
1911
+ description: response['fields']['description'],
1912
+ status: response['fields']['status']['name'],
1913
+ priority: response['fields']['priority']['name'],
1914
+ assignee: response['fields']['assignee']&.dig('emailAddress'),
1915
+ reporter: response['fields']['reporter']&.dig('emailAddress'),
1916
+ created: response['fields']['created'],
1917
+ updated: response['fields']['updated'],
1918
+ issue_type: response['fields']['issuetype']['name'],
1919
+ project: response['fields']['project']['key'],
1920
+ components: response['fields']['components']&.map { |c| c['name'] } || [],
1921
+ labels: response['fields']['labels'] || [],
1922
+ comments: response['fields']['comment']&.dig('comments')&.map { |c|
1923
+ { author: c['author']['emailAddress'], body: c['body'], created: c['created'] }
1924
+ } || [],
1925
+ attachments: response['fields']['attachment']&.map { |a| a['filename'] } || [],
1926
+ related_issues: [], # Jira doesn't provide this directly
1927
+ time_tracking: {
1928
+ original_estimate: response['fields']['timeoriginalestimate'],
1929
+ time_spent: response['fields']['timespent'],
1930
+ remaining_estimate: response['fields']['timeestimate']
1931
+ }
1932
+ }
1933
+ end
1934
+
1935
+ def self.get_real_jira_search(query, project_key, issue_type, status, assignee)
1936
+ jql_parts = []
1937
+ jql_parts << "project = #{project_key}" if project_key
1938
+ jql_parts << "issuetype = #{issue_type}" if issue_type
1939
+ jql_parts << "status = #{status}" if status
1940
+ jql_parts << "assignee = #{assignee}" if assignee
1941
+ jql_parts << "text ~ \"#{query}\"" if query
1942
+
1943
+ jql = jql_parts.join(" AND ")
1944
+ endpoint = "/search?jql=#{CGI.escape(jql)}&maxResults=10"
1945
+
1946
+ response = jira_api_call(endpoint)
1947
+ return [] if response['error']
1948
+
1949
+ response['issues']&.map do |issue|
1950
+ {
1951
+ id: issue['key'],
1952
+ summary: issue['fields']['summary'],
1953
+ status: issue['fields']['status']['name'],
1954
+ priority: issue['fields']['priority']['name'],
1955
+ assignee: issue['fields']['assignee']&.dig('emailAddress'),
1956
+ issue_type: issue['fields']['issuetype']['name'],
1957
+ created: issue['fields']['created'],
1958
+ updated: issue['fields']['updated']
1959
+ }
1960
+ end || []
1961
+ end
1962
+
1963
+ def self.get_real_jira_project(project_key)
1964
+ endpoint = "/project/#{project_key}"
1965
+ response = jira_api_call(endpoint)
1966
+
1967
+ return response if response['error']
1968
+
1969
+ {
1970
+ key: response['key'],
1971
+ name: response['name'],
1972
+ description: response['description'],
1973
+ lead: response['lead']['emailAddress'],
1974
+ url: "#{ENV['JIRA_URL']}/browse/#{project_key}",
1975
+ components: response['components']&.map { |c| c['name'] } || [],
1976
+ issue_types: response['issueTypes']&.map { |it| it['name'] } || [],
1977
+ statuses: response['statuses']&.map { |s| s['name'] } || [],
1978
+ versions: response['versions']&.map { |v| v['name'] } || [],
1979
+ permissions: response['permissions'] || []
1980
+ }
1981
+ end
1982
+
1983
+ # Fallback to simulation if real API fails
1984
+ def self.simulate_jira_available?
1985
+ # Check if JIRA configuration is available
1986
+ ENV['JIRA_URL'] && ENV['JIRA_USERNAME'] && ENV['JIRA_API_TOKEN']
1987
+ end
1988
+ end
1989
+
1990
+ # Tool for Confluence integration and business context retrieval
1991
+ class ConfluenceIntegrationTool < MCP::Tool
1992
+ description "Integrates with Confluence to retrieve PRDs, business requirements, and documentation for business context"
1993
+ input_schema(
1994
+ properties: {
1995
+ action: { type: "string", enum: ["search_documents", "get_document", "get_space_info", "search_prd"] },
1996
+ search_query: { type: "string" },
1997
+ space_key: { type: "string" },
1998
+ document_id: { type: "string" },
1999
+ server_context: { type: "object" }
2000
+ },
2001
+ required: ["action"]
2002
+ )
2003
+ annotations(
2004
+ title: "Confluence Integration Tool",
2005
+ read_only_hint: true,
2006
+ destructive_hint: false,
2007
+ idempotent_hint: true,
2008
+ open_world_hint: false
2009
+ )
2010
+
2011
+ def self.call(action:, search_query: nil, space_key: nil, document_id: nil, server_context:)
2012
+ case action
2013
+ when "search_documents"
2014
+ search_confluence_documents(search_query, space_key)
2015
+ when "get_document"
2016
+ get_confluence_document(document_id)
2017
+ when "get_space_info"
2018
+ get_confluence_space_info(space_key)
2019
+ when "search_prd"
2020
+ search_prd_documents(search_query, space_key)
2021
+ else
2022
+ MCP::Tool::Response.new([{ type: "text", text: { error: "Unknown action: #{action}" }.to_json }])
2023
+ end
2024
+ end
2025
+
2026
+ private
2027
+
2028
+ def self.search_confluence_documents(query, space_key)
2029
+ if confluence_api_available?
2030
+ get_real_confluence_search(query, space_key)
2031
+ else
2032
+ simulate_confluence_search(query, space_key)
2033
+ end
2034
+ end
2035
+
2036
+ def self.get_confluence_document(document_id)
2037
+ if confluence_api_available?
2038
+ get_real_confluence_document(document_id)
2039
+ else
2040
+ simulate_confluence_document(document_id)
2041
+ end
2042
+ end
2043
+
2044
+ def self.get_confluence_space_info(space_key)
2045
+ if confluence_api_available?
2046
+ get_real_confluence_space_info(space_key)
2047
+ else
2048
+ simulate_confluence_space_info(space_key)
2049
+ end
2050
+ end
2051
+
2052
+ def self.search_prd_documents(query, space_key)
2053
+ if confluence_api_available?
2054
+ get_real_confluence_prd_search(query, space_key)
2055
+ else
2056
+ simulate_confluence_prd_search(query, space_key)
2057
+ end
2058
+ end
2059
+
2060
+ # Real Confluence API calls
2061
+ def self.confluence_api_available?
2062
+ ENV['CONFLUENCE_URL'] && ENV['CONFLUENCE_USERNAME'] && ENV['CONFLUENCE_API_TOKEN']
2063
+ end
2064
+
2065
+ def self.confluence_api_call(endpoint, params = {})
2066
+ return nil unless confluence_api_available?
2067
+
2068
+ url = "#{ENV['CONFLUENCE_URL']}/rest/api#{endpoint}"
2069
+ auth = { username: ENV['CONFLUENCE_USERNAME'], password: ENV['CONFLUENCE_API_TOKEN'] }
2070
+
2071
+ begin
2072
+ response = HTTParty.get(url, basic_auth: auth, query: params)
2073
+ response.success? ? response.parsed_response : nil
2074
+ rescue => e
2075
+ puts "⚠️ Confluence API call failed: #{e.message}"
2076
+ nil
2077
+ end
2078
+ end
2079
+
2080
+ def self.get_real_confluence_search(query, space_key)
2081
+ params = {
2082
+ cql: "space = #{space_key} AND text ~ \"#{query}\"",
2083
+ limit: 10,
2084
+ expand: "content"
2085
+ }
2086
+
2087
+ data = confluence_api_call('/content/search', params)
2088
+ return { error: "Failed to search Confluence" } unless data
2089
+
2090
+ documents = data['results']&.map do |result|
2091
+ {
2092
+ id: result['id'],
2093
+ title: result['title'],
2094
+ type: result['type'],
2095
+ space_key: result['space']['key'],
2096
+ url: "#{ENV['CONFLUENCE_URL']}/wiki#{result['_links']['webui']}",
2097
+ content: result['excerpt'] || result['title'],
2098
+ labels: result['metadata']&.dig('labels', 'results')&.map { |l| l['name'] } || []
2099
+ }
2100
+ end || []
2101
+
2102
+ MCP::Tool::Response.new([{ type: "text", text: { documents: documents }.to_json }])
2103
+ end
2104
+
2105
+ def self.get_real_confluence_document(document_id)
2106
+ data = confluence_api_call("/content/#{document_id}")
2107
+ return { error: "Failed to get document" } unless data
2108
+
2109
+ document = {
2110
+ id: data['id'],
2111
+ title: data['title'],
2112
+ type: data['type'],
2113
+ space_key: data['space']['key'],
2114
+ url: "#{ENV['CONFLUENCE_URL']}/wiki#{data['_links']['webui']}",
2115
+ content: data['body']&.dig('storage', 'value') || data['title'],
2116
+ labels: data['metadata']&.dig('labels', 'results')&.map { |l| l['name'] } || []
2117
+ }
2118
+
2119
+ MCP::Tool::Response.new([{ type: "text", text: { document: document }.to_json }])
2120
+ end
2121
+
2122
+ def self.get_real_confluence_space_info(space_key)
2123
+ data = confluence_api_call("/space/#{space_key}")
2124
+ return { error: "Failed to get space info" } unless data
2125
+
2126
+ space_info = {
2127
+ key: data['key'],
2128
+ name: data['name'],
2129
+ type: data['type'],
2130
+ description: data['description'],
2131
+ url: "#{ENV['CONFLUENCE_URL']}/wiki/spaces/#{space_key}",
2132
+ permissions: data['permissions'] || []
2133
+ }
2134
+
2135
+ MCP::Tool::Response.new([{ type: "text", text: { space: space_info }.to_json }])
2136
+ end
2137
+
2138
+ def self.get_real_confluence_prd_search(query, space_key)
2139
+ params = {
2140
+ cql: "space = #{space_key} AND (text ~ \"PRD\" OR text ~ \"Product Requirements\" OR text ~ \"#{query}\")",
2141
+ limit: 5,
2142
+ expand: "content"
2143
+ }
2144
+
2145
+ data = confluence_api_call('/content/search', params)
2146
+ return { error: "Failed to search PRD documents" } unless data
2147
+
2148
+ prd_documents = data['results']&.map do |result|
2149
+ {
2150
+ id: result['id'],
2151
+ title: result['title'],
2152
+ type: result['type'],
2153
+ space_key: result['space']['key'],
2154
+ url: "#{ENV['CONFLUENCE_URL']}/wiki#{result['_links']['webui']}",
2155
+ content: result['excerpt'] || result['title'],
2156
+ is_prd: result['title'].include?('PRD') || result['title'].include?('Product Requirements'),
2157
+ labels: result['metadata']&.dig('labels', 'results')&.map { |l| l['name'] } || []
2158
+ }
2159
+ end || []
2160
+
2161
+ MCP::Tool::Response.new([{ type: "text", text: { documents: prd_documents }.to_json }])
2162
+ end
2163
+
2164
+ # Simulation methods for when Confluence API is not available
2165
+ def self.simulate_confluence_search(query, space_key)
2166
+ documents = [
2167
+ {
2168
+ id: "sim-1",
2169
+ title: "Business Rules for #{space_key}",
2170
+ type: "page",
2171
+ space_key: space_key,
2172
+ url: "https://confluence.example.com/wiki/spaces/#{space_key}/pages/sim-1",
2173
+ content: "Simulated business rules document for #{space_key} project",
2174
+ labels: ["business-rules", "requirements"]
2175
+ },
2176
+ {
2177
+ id: "sim-2",
2178
+ title: "Process Documentation",
2179
+ type: "page",
2180
+ space_key: space_key,
2181
+ url: "https://confluence.example.com/wiki/spaces/#{space_key}/pages/sim-2",
2182
+ content: "Simulated process documentation for #{space_key}",
2183
+ labels: ["process", "workflow"]
2184
+ }
2185
+ ]
2186
+
2187
+ MCP::Tool::Response.new([{ type: "text", text: { documents: documents }.to_json }])
2188
+ end
2189
+
2190
+ def self.simulate_confluence_document(document_id)
2191
+ document = {
2192
+ id: document_id,
2193
+ title: "Simulated Confluence Document",
2194
+ type: "page",
2195
+ space_key: "DGTL",
2196
+ url: "https://confluence.example.com/wiki/spaces/DGTL/pages/#{document_id}",
2197
+ content: "This is a simulated Confluence document content for testing purposes.",
2198
+ labels: ["simulated", "test"]
2199
+ }
2200
+
2201
+ MCP::Tool::Response.new([{ type: "text", text: { document: document }.to_json }])
2202
+ end
2203
+
2204
+ def self.simulate_confluence_space_info(space_key)
2205
+ space_info = {
2206
+ key: space_key,
2207
+ name: "#{space_key} Project Space",
2208
+ type: "global",
2209
+ description: "Simulated Confluence space for #{space_key} project",
2210
+ url: "https://confluence.example.com/wiki/spaces/#{space_key}",
2211
+ permissions: ["view", "edit", "create"]
2212
+ }
2213
+
2214
+ MCP::Tool::Response.new([{ type: "text", text: { space: space_info }.to_json }])
2215
+ end
2216
+
2217
+ def self.simulate_confluence_prd_search(query, space_key)
2218
+ prd_documents = [
2219
+ {
2220
+ id: "prd-1",
2221
+ title: "Product Requirements Document - #{space_key}",
2222
+ type: "page",
2223
+ space_key: space_key,
2224
+ url: "https://confluence.example.com/wiki/spaces/#{space_key}/pages/prd-1",
2225
+ content: "Simulated PRD for #{space_key} project with requirements and business rules",
2226
+ is_prd: true,
2227
+ labels: ["PRD", "requirements", "product"]
2228
+ }
2229
+ ]
2230
+
2231
+ MCP::Tool::Response.new([{ type: "text", text: { documents: prd_documents }.to_json }])
2232
+ end
1848
2233
  end
1849
2234
 
1850
2235
  # Tool for intelligent context analysis and runtime decision making
@@ -252,6 +252,36 @@ github_token = ask_for_input("Enter your GitHub personal access token (or press
252
252
  github_repo = ask_for_input("Enter your GitHub repository (username/repo or full URL):")
253
253
  github_repo_url = normalize_repository_url(github_repo, github_token)
254
254
 
255
+ # Jira Configuration
256
+ puts
257
+ puts "🎫 Jira Configuration:"
258
+ puts "Configure Jira integration for business context during healing operations."
259
+ puts "Get your API token at: https://id.atlassian.com/manage-profile/security/api-tokens"
260
+ puts
261
+
262
+ enable_jira = ask_for_yes_no("Enable Jira integration for business context?", default: false)
263
+
264
+ if enable_jira
265
+ jira_url = ask_for_input("Enter your Jira instance URL (e.g., https://your-company.atlassian.net):")
266
+ jira_username = ask_for_input("Enter your Jira username/email:")
267
+ jira_api_token = ask_for_input("Enter your Jira API token:")
268
+ jira_project_key = ask_for_input("Enter your default Jira project key (e.g., DGTL):")
269
+
270
+ # Validate Jira configuration
271
+ puts "🔍 Validating Jira configuration..."
272
+ if jira_url && jira_username && jira_api_token && jira_project_key
273
+ puts "✅ Jira configuration provided"
274
+ else
275
+ puts "⚠️ Incomplete Jira configuration - integration will be disabled"
276
+ enable_jira = false
277
+ end
278
+ else
279
+ jira_url = ""
280
+ jira_username = ""
281
+ jira_api_token = ""
282
+ jira_project_key = ""
283
+ end
284
+
255
285
  # Git Branch Configuration
256
286
  puts
257
287
  puts "🌿 Git Branch Configuration:"
@@ -307,6 +337,60 @@ puts
307
337
  puts "💼 Business Context Setup:"
308
338
  create_business_context = ask_for_yes_no("Would you like to create a business context file?", default: true)
309
339
 
340
+ # Business Context Strategy Configurationy
341
+ puts
342
+ puts "🔍 Business Context Strategy Configuration:"
343
+ puts "Choose how CodeHealer should get business context:"
344
+ puts "1. Jira MCP (Claude Terminal uses its own Jira MCP)"
345
+ puts "2. Markdown files (docs/business_rules.md)"
346
+ puts "3. Hybrid (both Jira MCP and Markdown)"
347
+ puts
348
+
349
+ business_context_strategy = ask_for_input("Enter business context strategy (1/2/3 or jira_mcp/markdown/hybrid):", default: "jira_mcp")
350
+ business_context_strategy = case business_context_strategy.downcase
351
+ when "1", "jira_mcp"
352
+ "jira_mcp"
353
+ when "2", "markdown"
354
+ "markdown"
355
+ when "3", "hybrid"
356
+ "hybrid"
357
+ else
358
+ "jira_mcp"
359
+ end
360
+
361
+ # Configure Confluence if selected
362
+ enable_confluence = false
363
+ confluence_url = ""
364
+ confluence_username = ""
365
+ confluence_api_token = ""
366
+ confluence_space_key = ""
367
+
368
+ if business_context_source == "confluence" || business_context_source == "hybrid"
369
+ puts
370
+ puts "📚 Confluence Configuration:"
371
+ puts "Configure Confluence integration for PRDs and documentation."
372
+ puts "Get your API token at: https://id.atlassian.com/manage-profile/security/api-tokens"
373
+ puts
374
+
375
+ enable_confluence = ask_for_yes_no("Enable Confluence integration for business context?", default: false)
376
+
377
+ if enable_confluence
378
+ confluence_url = ask_for_input("Enter your Confluence instance URL (e.g., https://your-company.atlassian.net/wiki):")
379
+ confluence_username = ask_for_input("Enter your Confluence username/email:")
380
+ confluence_api_token = ask_for_input("Enter your Confluence API token:")
381
+ confluence_space_key = ask_for_input("Enter your Confluence space key (e.g., DGTL):")
382
+
383
+ # Validate Confluence configuration
384
+ puts "🔍 Validating Confluence configuration..."
385
+ if confluence_url && confluence_username && confluence_api_token && confluence_space_key
386
+ puts "✅ Confluence configuration provided"
387
+ else
388
+ puts "⚠️ Incomplete Confluence configuration - integration will be disabled"
389
+ enable_confluence = false
390
+ end
391
+ end
392
+ end
393
+
310
394
  # Evolution Strategy Configuration
311
395
  puts
312
396
  puts "🧠 Evolution Strategy Configuration:"
@@ -374,6 +458,18 @@ puts
374
458
  GITHUB_TOKEN=#{github_token}
375
459
  GITHUB_REPOSITORY=#{github_repo}
376
460
 
461
+ # Jira Configuration
462
+ JIRA_URL=#{jira_url}
463
+ JIRA_USERNAME=#{jira_username}
464
+ JIRA_API_TOKEN=#{jira_api_token}
465
+ JIRA_PROJECT_KEY=#{jira_project_key}
466
+
467
+ # Confluence Configuration
468
+ CONFLUENCE_URL=#{confluence_url}
469
+ CONFLUENCE_USERNAME=#{confluence_username}
470
+ CONFLUENCE_API_TOKEN=#{confluence_api_token}
471
+ CONFLUENCE_SPACE_KEY=#{confluence_space_key}
472
+
377
473
  # Optional: Redis Configuration
378
474
  REDIS_URL=redis://localhost:6379/0
379
475
  ENV
@@ -436,8 +532,36 @@ create_file_with_content('.env', env_content, dry_run: options[:dry_run])
436
532
  # Business Context Configuration
437
533
  business_context:
438
534
  enabled: true
439
- sources:
440
- - "docs/business_rules.md"
535
+ strategy: "#{business_context_strategy}"
536
+
537
+ # Jira MCP Configuration
538
+ jira_mcp:
539
+ enabled: #{business_context_strategy == 'jira_mcp' || business_context_strategy == 'hybrid'}
540
+ project_key: "#{jira_project_key}"
541
+ search_tickets_on_error: true
542
+ include_business_rules: true
543
+ system_prompt: |
544
+ When fixing code, ALWAYS check Jira MCP for business context:
545
+ 1. Search for tickets about the class/method you're fixing
546
+ 2. Use Jira requirements to ensure your fix follows business rules
547
+ 3. Reference specific Jira tickets in your explanation
548
+ 4. Make sure fixes align with business requirements
549
+
550
+ # Markdown Configuration
551
+ markdown:
552
+ enabled: #{business_context_strategy == 'markdown' || business_context_strategy == 'hybrid'}
553
+ search_paths:
554
+ - "docs/business_rules.md"
555
+ - "docs/requirements.md"
556
+ - "business_requirements/"
557
+ include_patterns:
558
+ - "*.md"
559
+ - "*.txt"
560
+
561
+ # Hybrid Configuration
562
+ hybrid:
563
+ priority: ["jira_mcp", "markdown"]
564
+ combine_results: true
441
565
 
442
566
  # OpenAI API configuration
443
567
  api:
@@ -462,6 +586,16 @@ create_file_with_content('.env', env_content, dry_run: options[:dry_run])
462
586
  auto_create: true
463
587
  labels:
464
588
  - "auto-fix"
589
+
590
+ # Jira Integration Configuration
591
+ jira:
592
+ enabled: #{enable_jira}
593
+ url: "#{jira_url}"
594
+ username: "#{jira_username}"
595
+ project_key: "#{jira_project_key}"
596
+ business_context_enabled: #{enable_jira}
597
+ search_tickets_on_error: true
598
+ include_ticket_details: true
465
599
  - "self-evolving"
466
600
  - "bug-fix"
467
601
 
@@ -590,6 +724,11 @@ puts " - All features are pre-configured and ready to use"
590
724
  puts "🌍 Environment Variables:"
591
725
  puts " - Add 'gem \"dotenv-rails\"' to your Gemfile for automatic .env loading"
592
726
  puts " - Or export variables manually: export GITHUB_TOKEN=your_token"
727
+ if enable_jira
728
+ puts " - Jira integration: export JIRA_URL=#{jira_url}"
729
+ puts " - Jira credentials: export JIRA_USERNAME=#{jira_username}"
730
+ puts " - Jira project: export JIRA_PROJECT_KEY=#{jira_project_key}"
731
+ end
593
732
  puts " - Or load .env file in your application.rb: load '.env' if File.exist?('.env')"
594
733
  puts
595
734
  puts "CodeHealer will now automatically detect and heal errors in your application!"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CodeHealer
4
- VERSION = "0.1.19"
4
+ VERSION = "0.1.22"
5
5
  end
data/lib/code_healer.rb CHANGED
@@ -8,12 +8,13 @@ require 'openai'
8
8
  require 'sidekiq'
9
9
  require 'git'
10
10
  require 'octokit'
11
+ require 'open3'
11
12
 
12
13
  # CodeHealer - AI-Powered Code Healing and Self-Repair System
13
14
  module CodeHealer
14
15
  class Error < StandardError; end
15
16
 
16
- # Your code goes here...
17
+
17
18
  end
18
19
 
19
20
  # Autoload all the main classes
@@ -88,5 +89,7 @@ if defined?(Rails)
88
89
  puts "⚠️ Claude preload failed: #{e.message}"
89
90
  end
90
91
  end
92
+
93
+
91
94
  end
92
95
  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.19
4
+ version: 0.1.22
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-21 00:00:00.000000000 Z
11
+ date: 2025-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -160,6 +160,26 @@ dependencies:
160
160
  - - ">="
161
161
  - !ruby/object:Gem::Version
162
162
  version: 6.0.0
163
+ - !ruby/object:Gem::Dependency
164
+ name: httparty
165
+ requirement: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 0.21.0
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 0.21.0
173
+ type: :runtime
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: 0.21.0
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: 0.21.0
163
183
  - !ruby/object:Gem::Dependency
164
184
  name: bundler
165
185
  requirement: !ruby/object:Gem::Requirement
@@ -348,6 +368,7 @@ files:
348
368
  - DASHBOARD_README.md
349
369
  - GEM_SUMMARY.md
350
370
  - README.md
371
+ - bin/start_claude_session
351
372
  - code_healer.gemspec
352
373
  - config/code_healer.yml.example
353
374
  - config/routes.rb