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
# 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?
    debug_logger.error(component_name, "❌ Error output: #{error}")
  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



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

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, streaming: false, **options) ⇒ Object

Execute command with debug logging



133
134
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
173
174
175
176
177
# File 'lib/aidp/debug_mixin.rb', line 133

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

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

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

  begin
    # Configure printer based on streaming mode
    if streaming
      # Use progress printer for real-time output
      cmd_obj = TTY::Command.new(printer: :progress)
      debug_log("📺 Streaming mode enabled - showing real-time output", level: :info)
    else
      cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
    end

    # 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



104
105
106
107
108
109
# File 'lib/aidp/debug_mixin.rb', line 104

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



96
97
98
99
100
101
# File 'lib/aidp/debug_mixin.rb', line 96

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



125
126
127
128
129
130
# File 'lib/aidp/debug_mixin.rb', line 125

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