Module: Aidp::DebugMixin

Overview

Mixin module for easy debug integration across the codebase

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

DEBUG_OFF =

Debug levels

0
DEBUG_BASIC =

Commands and stderr

1
DEBUG_VERBOSE =

Everything including prompts and stdout

2

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



11
12
13
# File 'lib/aidp/debug_mixin.rb', line 11

def self.included(base)
  base.extend(ClassMethods)
end

.shared_loggerObject

Shared logger instance across all classes using DebugMixin



27
28
29
# File 'lib/aidp/debug_mixin.rb', line 27

def self.shared_logger
  Aidp.logger
end

Instance Method Details

#debug_basic?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/aidp/debug_mixin.rb', line 40

def debug_basic?
  debug_level >= DEBUG_BASIC
end

#debug_command(cmd, args: [], input: nil, output: nil, error: nil, exit_code: nil) ⇒ Object

Log command execution with debug details



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/aidp/debug_mixin.rb', line 62

def debug_command(cmd, args: [], input: nil, output: nil, error: nil, exit_code: nil)
  return unless debug_basic?

  command_str = [cmd, *args].join(" ")

  debug_logger.info(component_name, "🔧 Executing command: #{command_str}")

  if input
    if input.is_a?(String) && input.length > 200
      # If input is long, show first 100 chars and indicate it's truncated
      debug_logger.info(component_name, "📝 Input (truncated): #{input[0..100]}...")
    elsif input.is_a?(String) && File.exist?(input)
      debug_logger.info(component_name, "📝 Input file: #{input}")
    else
      debug_logger.info(component_name, "📝 Input: #{input}")
    end
  end

  if error && !error.empty?
    # Filter out benign sandbox debug messages from Claude CLI
    filtered_error = filter_benign_errors(error)
    debug_logger.error(component_name, "❌ Error output: #{filtered_error}") unless filtered_error.empty?
  end

  if debug_verbose?
    if output && !output.empty?
      debug_logger.debug(component_name, "📤 Output: #{output}")
    end

    if exit_code
      debug_logger.debug(component_name, "🏁 Exit code: #{exit_code}")
    end
  end
end

#debug_enabled?Boolean

Instance-level debug methods

Returns:

  • (Boolean)


32
33
34
# File 'lib/aidp/debug_mixin.rb', line 32

def debug_enabled?
  self.class.debug_enabled?
end

#debug_error(error, context = {}) ⇒ Object

Log error with debug context



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/aidp/debug_mixin.rb', line 114

def debug_error(error, context = {})
  return unless debug_basic?
  return unless error # Handle nil error gracefully

  error_message = "💥 Error: #{error.class.name}: #{error.message}"
  debug_logger.error(component_name, error_message, error: error.class.name, **context)

  if debug_verbose? && error.backtrace
    debug_logger.debug(component_name, "📍 Backtrace: #{error.backtrace.first(5).join("\n")}")
  end
end

#debug_execute_command(cmd, args: [], input: nil, timeout: nil, **options) ⇒ Object

Execute command with debug logging



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/aidp/debug_mixin.rb', line 135

def debug_execute_command(cmd, args: [], input: nil, timeout: nil, **options)
  require "tty-command"

  command_str = [cmd, *args].join(" ")
  start_time = Time.now

  debug_logger.info(component_name, "🚀 Starting command execution: #{command_str}")

  begin
    cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output

    # Prepare input
    input_data = nil
    if input
      if input.is_a?(String) && File.exist?(input)
        input_data = File.read(input)
        debug_log("📁 Reading input from file: #{input}", level: :info)
      else
        input_data = input
      end
    end

    # Execute command
    result = cmd_obj.run(cmd, *args, input: input_data, timeout: timeout, **options)

    duration = Time.now - start_time

    # Log results
    debug_command(cmd, args: args, input: input, output: result.out, error: result.err, exit_code: result.exit_status)
    debug_timing("Command execution", duration, {exit_code: result.exit_status})

    result
  rescue => e
    duration = Time.now - start_time
    debug_error(e, {command: command_str, duration: duration})
    raise
  end
end

#debug_levelObject



36
37
38
# File 'lib/aidp/debug_mixin.rb', line 36

def debug_level
  self.class.debug_level
end

#debug_log(message, level: :info, data: nil) ⇒ Object

Log debug information with automatic level detection



54
55
56
57
58
59
# File 'lib/aidp/debug_mixin.rb', line 54

def debug_log(message, level: :info, data: nil)
  return unless debug_enabled?

  debug_logger.log(level, component_name, message, **data) if data
  debug_logger.log(level, component_name, message) unless data
end

#debug_loggerObject

Get or create debug logger instance (shared across all instances)



49
50
51
# File 'lib/aidp/debug_mixin.rb', line 49

def debug_logger
  Aidp::DebugMixin.shared_logger
end

#debug_provider(provider_name, action, details = {}) ⇒ Object

Log provider interaction



106
107
108
109
110
111
# File 'lib/aidp/debug_mixin.rb', line 106

def debug_provider(provider_name, action, details = {})
  return unless debug_basic?

  message = "🤖 #{action}"
  debug_logger.info("provider_#{provider_name}", message, **details)
end

#debug_step(step_name, action, details = {}) ⇒ Object

Log step execution with context



98
99
100
101
102
103
# File 'lib/aidp/debug_mixin.rb', line 98

def debug_step(step_name, action, details = {})
  return unless debug_basic?

  message = "🔄 #{action}: #{step_name}"
  debug_logger.info(component_name, message, **details)
end

#debug_timing(operation, duration, details = {}) ⇒ Object

Log timing information



127
128
129
130
131
132
# File 'lib/aidp/debug_mixin.rb', line 127

def debug_timing(operation, duration, details = {})
  return unless debug_verbose?

  message = "⏱️  #{operation}: #{duration.round(2)}s"
  debug_logger.debug(component_name, message, duration: duration, **details)
end

#debug_verbose?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/aidp/debug_mixin.rb', line 44

def debug_verbose?
  debug_level >= DEBUG_VERBOSE
end