aidp 0.14.1 → 0.14.2

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: 8d97cff94f784545b96549c70d910e913bf19a7c94b908e45dae2a999c57e45b
4
- data.tar.gz: 170bf8fe45dbb081cad38ef69eb8a0f58b962d495ed300b3d58d3f78ef553462
3
+ metadata.gz: e9ed49c611f18d153eef9794e7ad5077a4bd369988b21dc6849b26b4f9489691
4
+ data.tar.gz: 97d9812b8cb6437471b4e4bdfdff218b588e20d0187fe6878dd03fdc6fed9b25
5
5
  SHA512:
6
- metadata.gz: a371614e261335e71b6e78cbe6d6410f26f03a4910e37e6b4102d3681fc9ef2abf617a8f4199b918f8efcdb43fc978a3eef9c1d04aa485297af90394bd34cbd8
7
- data.tar.gz: 549e21d089121d0212698307bd8ed520499452ad829ff70e8ca1dea7fd50b6c63db9be2a55d4b56c9c60d822703710e84f57f91354bad8892e7367347e6b7777
6
+ metadata.gz: f88b7ff6548415eb12a364782a7b2f1a8dfdbc2983c9a776a4714c8d2cc0c58501c4180795ff62734b6dee26e2448d0cbfcab1a1a0e5ecb577b49bb24d2184b9
7
+ data.tar.gz: 67867d11f4909e48f2b6954f966e06291a3b4e3ad2196fc993bf5ebe0ad5f411547f6ed0bdd76bb864a0f0850148a87a2d0ba5265207a447788bb020f72b29b4
data/lib/aidp/cli.rb CHANGED
@@ -8,11 +8,13 @@ require_relative "harness/ui/enhanced_tui"
8
8
  require_relative "harness/ui/enhanced_workflow_selector"
9
9
  require_relative "harness/enhanced_runner"
10
10
  require_relative "cli/first_run_wizard"
11
+ require_relative "rescue_logging"
11
12
 
12
13
  module Aidp
13
14
  # CLI interface for AIDP
14
15
  class CLI
15
16
  include Aidp::MessageDisplay
17
+ include Aidp::RescueLogging
16
18
 
17
19
  # Simple options holder for instance methods (used in specs)
18
20
  attr_accessor :options
@@ -126,6 +128,7 @@ module Aidp
126
128
  {harness: {state: "unknown"}}
127
129
  end
128
130
  rescue => e
131
+ log_rescue(e, component: "cli", action: "fetch_harness_status", fallback: {harness: {state: "error"}}, mode: mode)
129
132
  {harness: {state: "error", error: e.message}}
130
133
  end
131
134
 
@@ -159,6 +162,10 @@ module Aidp
159
162
  return 0
160
163
  end
161
164
 
165
+ # Initialize logger from aidp.yml config
166
+ # Priority: ENV variable > aidp.yml > default (info)
167
+ setup_logging(Dir.pwd)
168
+
162
169
  # Start the interactive TUI
163
170
  display_message("AIDP initializing...", type: :info)
164
171
  display_message(" Press Ctrl+C to stop\n", type: :highlight)
@@ -216,6 +223,7 @@ module Aidp
216
223
  display_message("\n\n⏹️ Interrupted by user", type: :warning)
217
224
  1
218
225
  rescue => e
226
+ log_rescue(e, component: "cli", action: "run_harness", fallback: 1, mode: actual_mode)
219
227
  display_message("\n❌ Error: #{e.message}", type: :error)
220
228
  1
221
229
  ensure
@@ -225,6 +233,29 @@ module Aidp
225
233
 
226
234
  private
227
235
 
236
+ def setup_logging(project_dir)
237
+ # Load logging config from aidp.yml
238
+ config_path = File.join(project_dir, ".aidp", "aidp.yml")
239
+ logging_config = {}
240
+
241
+ if File.exist?(config_path)
242
+ require "yaml"
243
+ full_config = YAML.load_file(config_path)
244
+ logging_config = full_config["logging"] || full_config[:logging] || {}
245
+ end
246
+
247
+ # Set up logger with config (ENV variable AIDP_LOG_LEVEL takes precedence)
248
+ Aidp.setup_logger(project_dir, logging_config)
249
+
250
+ # Log initialization
251
+ Aidp.logger.info("cli", "AIDP starting", version: Aidp::VERSION, log_level: Aidp.logger.level)
252
+ rescue => e
253
+ log_rescue(e, component: "cli", action: "setup_logger", fallback: "default_config", project_dir: project_dir)
254
+ # If logging setup fails, continue with default logger
255
+ Aidp.setup_logger(project_dir, {})
256
+ Aidp.logger.warn("cli", "Failed to load logging config, using defaults", error: e.message)
257
+ end
258
+
228
259
  def parse_options(args)
229
260
  options = {}
230
261
 
@@ -699,6 +730,7 @@ module Aidp
699
730
  table = TTY::Table.new header, table_rows
700
731
  display_message(table.render(:basic), type: :info)
701
732
  rescue => e
733
+ log_rescue(e, component: "cli", action: "display_provider_health", fallback: "error_message")
702
734
  display_message("Failed to display provider health: #{e.message}", type: :error)
703
735
  end
704
736
 
@@ -1049,6 +1081,7 @@ module Aidp
1049
1081
  )
1050
1082
  runner.start
1051
1083
  rescue ArgumentError => e
1084
+ log_rescue(e, component: "cli", action: "start_watch_command", fallback: "error_display")
1052
1085
  display_message("❌ #{e.message}", type: :error)
1053
1086
  end
1054
1087
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "debug_logger"
4
-
5
3
  module Aidp
6
4
  # Mixin module for easy debug integration across the codebase
7
5
  module DebugMixin
@@ -27,7 +25,7 @@ module Aidp
27
25
 
28
26
  # Shared logger instance across all classes using DebugMixin
29
27
  def self.shared_logger
30
- @shared_logger ||= Aidp::DebugLogger.new
28
+ Aidp.logger
31
29
  end
32
30
 
33
31
  # Instance-level debug methods
@@ -56,7 +54,8 @@ module Aidp
56
54
  def debug_log(message, level: :info, data: nil)
57
55
  return unless debug_enabled?
58
56
 
59
- debug_logger.log(message, level: level, data: data)
57
+ debug_logger.log(level, component_name, message, **data) if data
58
+ debug_logger.log(level, component_name, message) unless data
60
59
  end
61
60
 
62
61
  # Log command execution with debug details
@@ -65,30 +64,30 @@ module Aidp
65
64
 
66
65
  command_str = [cmd, *args].join(" ")
67
66
 
68
- debug_log("🔧 Executing command: #{command_str}", level: :info)
67
+ debug_logger.info(component_name, "🔧 Executing command: #{command_str}")
69
68
 
70
69
  if input
71
70
  if input.is_a?(String) && input.length > 200
72
71
  # If input is long, show first 100 chars and indicate it's truncated
73
- debug_log("📝 Input (truncated): #{input[0..100]}...", level: :info)
72
+ debug_logger.info(component_name, "📝 Input (truncated): #{input[0..100]}...")
74
73
  elsif input.is_a?(String) && File.exist?(input)
75
- debug_log("📝 Input file: #{input}", level: :info)
74
+ debug_logger.info(component_name, "📝 Input file: #{input}")
76
75
  else
77
- debug_log("📝 Input: #{input}", level: :info)
76
+ debug_logger.info(component_name, "📝 Input: #{input}")
78
77
  end
79
78
  end
80
79
 
81
80
  if error && !error.empty?
82
- debug_log("❌ Error output: #{error}", level: :error)
81
+ debug_logger.error(component_name, "❌ Error output: #{error}")
83
82
  end
84
83
 
85
84
  if debug_verbose?
86
85
  if output && !output.empty?
87
- debug_log("📤 Output: #{output}", level: :info)
86
+ debug_logger.debug(component_name, "📤 Output: #{output}")
88
87
  end
89
88
 
90
89
  if exit_code
91
- debug_log("🏁 Exit code: #{exit_code}", level: :info)
90
+ debug_logger.debug(component_name, "🏁 Exit code: #{exit_code}")
92
91
  end
93
92
  end
94
93
  end
@@ -98,25 +97,15 @@ module Aidp
98
97
  return unless debug_basic?
99
98
 
100
99
  message = "🔄 #{action}: #{step_name}"
101
- if details.any?
102
- detail_str = details.map { |k, v| "#{k}=#{v}" }.join(", ")
103
- message += " (#{detail_str})"
104
- end
105
-
106
- debug_log(message, level: :info, data: details)
100
+ debug_logger.info(component_name, message, **details)
107
101
  end
108
102
 
109
103
  # Log provider interaction
110
104
  def debug_provider(provider_name, action, details = {})
111
105
  return unless debug_basic?
112
106
 
113
- message = "🤖 #{provider_name}: #{action}"
114
- if details.any?
115
- detail_str = details.map { |k, v| "#{k}=#{v}" }.join(", ")
116
- message += " (#{detail_str})"
117
- end
118
-
119
- debug_log(message, level: :info, data: details)
107
+ message = "🤖 #{action}"
108
+ debug_logger.info("provider_#{provider_name}", message, **details)
120
109
  end
121
110
 
122
111
  # Log error with debug context
@@ -124,10 +113,10 @@ module Aidp
124
113
  return unless debug_basic?
125
114
 
126
115
  error_message = "💥 Error: #{error.class.name}: #{error.message}"
127
- debug_log(error_message, level: :error, data: {error: error, context: context})
116
+ debug_logger.error(component_name, error_message, error: error.class.name, **context)
128
117
 
129
118
  if debug_verbose? && error.backtrace
130
- debug_log("📍 Backtrace: #{error.backtrace.first(5).join("\n")}", level: :error)
119
+ debug_logger.debug(component_name, "📍 Backtrace: #{error.backtrace.first(5).join("\n")}")
131
120
  end
132
121
  end
133
122
 
@@ -136,12 +125,7 @@ module Aidp
136
125
  return unless debug_verbose?
137
126
 
138
127
  message = "⏱️ #{operation}: #{duration.round(2)}s"
139
- if details.any?
140
- detail_str = details.map { |k, v| "#{k}=#{v}" }.join(", ")
141
- message += " (#{detail_str})"
142
- end
143
-
144
- debug_log(message, level: :info, data: {duration: duration, details: details})
128
+ debug_logger.debug(component_name, message, duration: duration, **details)
145
129
  end
146
130
 
147
131
  # Execute command with debug logging
@@ -151,7 +135,7 @@ module Aidp
151
135
  command_str = [cmd, *args].join(" ")
152
136
  start_time = Time.now
153
137
 
154
- debug_log("🚀 Starting command execution: #{command_str}", level: :info)
138
+ debug_logger.info(component_name, "🚀 Starting command execution: #{command_str}")
155
139
 
156
140
  begin
157
141
  # Configure printer based on streaming mode
@@ -190,5 +174,22 @@ module Aidp
190
174
  raise
191
175
  end
192
176
  end
177
+
178
+ private
179
+
180
+ # Safely derive a component name for logging (memoized).
181
+ # Handles anonymous classes and modules gracefully.
182
+ def component_name
183
+ @component_name ||= begin
184
+ klass = self.class
185
+ name = klass.name
186
+ return "anonymous" unless name && !name.empty?
187
+ # Take the last constant segment, normalize to snake-ish lowercase
188
+ segment = name.split("::").last
189
+ segment.gsub(/([a-z\d])([A-Z])/, '\\1_\\2').downcase
190
+ rescue
191
+ "anonymous"
192
+ end
193
+ end
193
194
  end
194
195
  end
@@ -3,6 +3,7 @@
3
3
  require_relative "work_loop_runner"
4
4
  require_relative "work_loop_state"
5
5
  require_relative "instruction_queue"
6
+ require_relative "../rescue_logging"
6
7
 
7
8
  module Aidp
8
9
  module Execute
@@ -16,6 +17,8 @@ module Aidp
16
17
  # - Stream output to main thread for display
17
18
  # - Handle graceful cancellation with checkpoint save
18
19
  class AsyncWorkLoopRunner
20
+ include Aidp::RescueLogging
21
+
19
22
  attr_reader :state, :instruction_queue, :work_thread
20
23
 
21
24
  def initialize(project_dir, provider_manager, config, options = {})
@@ -42,6 +45,7 @@ module Aidp
42
45
  @work_thread = Thread.new do
43
46
  run_async_loop
44
47
  rescue => e
48
+ log_rescue(e, component: "async_work_loop_runner", action: "thread_execution", fallback: "error_state", step: @step_name)
45
49
  @state.error!(e)
46
50
  @state.append_output("Work loop error: #{e.message}", type: :error)
47
51
  ensure
@@ -149,6 +153,7 @@ module Aidp
149
153
 
150
154
  result
151
155
  rescue => e
156
+ log_rescue(e, component: "async_work_loop_runner", action: "run_async_loop", fallback: "error_state", step: @step_name, iteration: @state.iteration)
152
157
  @state.error!(e)
153
158
  @state.append_output("Error in work loop: #{e.message}\n#{e.backtrace.first(3).join("\n")}", type: :error)
154
159
  raise
@@ -3,12 +3,15 @@
3
3
  require "yaml"
4
4
  require "time"
5
5
  require "json"
6
+ require "aidp/rescue_logging"
6
7
 
7
8
  module Aidp
8
9
  module Execute
9
10
  # Manages periodic checkpoints during work loop execution
10
11
  # Tracks progress metrics, code quality, and task completion
11
12
  class Checkpoint
13
+ include Aidp::RescueLogging
14
+
12
15
  attr_reader :project_dir, :checkpoint_file, :history_file
13
16
 
14
17
  def initialize(project_dir)
@@ -104,7 +107,11 @@ module Aidp
104
107
  end
105
108
 
106
109
  total_lines
107
- rescue
110
+ rescue => e
111
+ log_rescue(e,
112
+ component: "checkpoint",
113
+ action: "count_lines_of_code",
114
+ fallback: 0)
108
115
  0
109
116
  end
110
117
 
@@ -121,7 +128,11 @@ module Aidp
121
128
  end
122
129
 
123
130
  count
124
- rescue
131
+ rescue => e
132
+ log_rescue(e,
133
+ component: "checkpoint",
134
+ action: "count_project_files",
135
+ fallback: 0)
125
136
  0
126
137
  end
127
138
 
@@ -134,7 +145,11 @@ module Aidp
134
145
 
135
146
  coverage_ratio = (test_files.to_f / source_files * 100).round(2)
136
147
  [coverage_ratio, 100].min
137
- rescue
148
+ rescue => e
149
+ log_rescue(e,
150
+ component: "checkpoint",
151
+ action: "estimate_test_coverage",
152
+ fallback: 0)
138
153
  0
139
154
  end
140
155
 
@@ -173,7 +188,11 @@ module Aidp
173
188
  offense_count = data["summary"]["offense_count"] || 0
174
189
  # Simple scoring: fewer offenses = higher score
175
190
  [100 - (offense_count / total_files.to_f * 10), 0].max.round(2)
176
- rescue
191
+ rescue => e
192
+ log_rescue(e,
193
+ component: "checkpoint",
194
+ action: "run_rubocop_check",
195
+ fallback: nil)
177
196
  nil
178
197
  end
179
198
 
@@ -191,7 +210,11 @@ module Aidp
191
210
  return 0 if total_tasks == 0
192
211
 
193
212
  (completed_tasks.to_f / total_tasks * 100).round(2)
194
- rescue
213
+ rescue => e
214
+ log_rescue(e,
215
+ component: "checkpoint",
216
+ action: "calculate_prd_task_progress",
217
+ fallback: 0)
195
218
  0
196
219
  end
197
220
 
@@ -4,6 +4,7 @@ require "tty-prompt"
4
4
  require "tty-spinner"
5
5
  require_relative "async_work_loop_runner"
6
6
  require_relative "repl_macros"
7
+ require_relative "../rescue_logging"
7
8
 
8
9
  module Aidp
9
10
  module Execute
@@ -19,6 +20,8 @@ module Aidp
19
20
  # repl = InteractiveRepl.new(project_dir, provider_manager, config)
20
21
  # repl.start_work_loop(step_name, step_spec, context)
21
22
  class InteractiveRepl
23
+ include Aidp::RescueLogging
24
+
22
25
  def initialize(project_dir, provider_manager, config, options = {})
23
26
  @project_dir = project_dir
24
27
  @provider_manager = provider_manager
@@ -85,6 +88,7 @@ module Aidp
85
88
  rescue Interrupt
86
89
  handle_interrupt
87
90
  rescue => e
91
+ log_rescue(e, component: "interactive_repl", action: "repl_command_loop", fallback: "error_display")
88
92
  @prompt.error("REPL error: #{e.message}")
89
93
  end
90
94
  end
@@ -98,6 +102,7 @@ module Aidp
98
102
  command = $stdin.gets&.chomp
99
103
  command&.strip
100
104
  rescue => e
105
+ log_rescue(e, component: "interactive_repl", action: "read_command", fallback: nil)
101
106
  @prompt.error("Input error: #{e.message}")
102
107
  nil
103
108
  end
@@ -165,6 +170,7 @@ module Aidp
165
170
  @prompt.error(result[:message])
166
171
  end
167
172
  rescue => e
173
+ log_rescue(e, component: "interactive_repl", action: "handle_command", fallback: "error_display", command: result[:data])
168
174
  @prompt.error("Command error: #{e.message}")
169
175
  end
170
176
 
@@ -215,6 +221,7 @@ module Aidp
215
221
  message: success ? "Reset #{count} commit(s)" : output
216
222
  }
217
223
  rescue => e
224
+ log_rescue(e, component: "interactive_repl", action: "git_reset", fallback: "error_result", count: count)
218
225
  {success: false, message: e.message}
219
226
  end
220
227
 
@@ -63,6 +63,8 @@ module Aidp
63
63
  @iteration_count = 0
64
64
  transition_to(:ready)
65
65
 
66
+ Aidp.logger.info("work_loop", "Starting fix-forward execution", step: step_name, max_iterations: MAX_ITERATIONS)
67
+
66
68
  display_message("🔄 Starting fix-forward work loop for step: #{step_name}", type: :info)
67
69
  display_message(" State machine: READY → APPLY_PATCH → TEST → {PASS → DONE | FAIL → DIAGNOSE → NEXT_PATCH}", type: :info)
68
70
 
@@ -77,7 +79,10 @@ module Aidp
77
79
  @iteration_count += 1
78
80
  display_message(" Iteration #{@iteration_count} [State: #{STATES[@current_state]}]", type: :info)
79
81
 
80
- break if @iteration_count > MAX_ITERATIONS
82
+ if @iteration_count > MAX_ITERATIONS
83
+ Aidp.logger.error("work_loop", "Max iterations exceeded", step: @step_name, iterations: @iteration_count)
84
+ break
85
+ end
81
86
 
82
87
  # State: READY - Starting new iteration
83
88
  transition_to(:ready) unless @current_state == :ready
@@ -143,6 +148,7 @@ module Aidp
143
148
  def transition_to(new_state)
144
149
  raise "Invalid state: #{new_state}" unless STATES.key?(new_state)
145
150
 
151
+ old_state = @current_state
146
152
  @state_history << {
147
153
  from: @current_state,
148
154
  to: new_state,
@@ -150,6 +156,7 @@ module Aidp
150
156
  timestamp: Time.now
151
157
  }
152
158
  @current_state = new_state
159
+ Aidp.logger.debug("work_loop", "State transition", from: old_state, to: new_state, iteration: @iteration_count, step: @step_name)
153
160
  end
154
161
 
155
162
  # Display summary of state transitions
@@ -81,6 +81,8 @@ module Aidp
81
81
  @state = STATES[:running]
82
82
  @start_time = Time.now
83
83
 
84
+ Aidp.logger.info("harness_runner", "Starting harness execution", mode: @mode, workflow_type: @workflow_type, steps_count: @selected_steps.size)
85
+
84
86
  @tui.show_message("🚀 Starting #{@mode.to_s.capitalize} Mode", :info)
85
87
 
86
88
  begin
@@ -3,11 +3,14 @@
3
3
  require "json"
4
4
  require "yaml"
5
5
  require "fileutils"
6
+ require_relative "../rescue_logging"
6
7
 
7
8
  module Aidp
8
9
  module Harness
9
10
  # Stores detailed information about AI providers gathered from their CLI tools
10
11
  class ProviderInfo
12
+ include Aidp::RescueLogging
13
+
11
14
  attr_reader :provider_name, :info_file_path
12
15
 
13
16
  def initialize(provider_name, root_dir = nil)
@@ -56,6 +59,7 @@ module Aidp
56
59
 
57
60
  YAML.safe_load_file(@info_file_path, permitted_classes: [Time, Symbol])
58
61
  rescue => e
62
+ log_rescue(e, component: "provider_info", action: "load_yaml", fallback: nil, provider: @provider_name, path: @info_file_path)
59
63
  warn "Failed to load provider info for #{@provider_name}: #{e.message}"
60
64
  nil
61
65
  end
@@ -133,7 +137,8 @@ module Aidp
133
137
 
134
138
  last_checked = Time.parse(info[:last_checked].to_s)
135
139
  (Time.now - last_checked) > max_age
136
- rescue
140
+ rescue => e
141
+ log_rescue(e, component: "provider_info", action: "parse_last_checked_time", fallback: true, provider: @provider_name, timestamp: info[:last_checked])
137
142
  true
138
143
  end
139
144
 
@@ -147,6 +152,7 @@ module Aidp
147
152
 
148
153
  provider_instance.fetch_mcp_servers
149
154
  rescue => e
155
+ log_rescue(e, component: "provider_info", action: "fetch_mcp_servers", fallback: [], provider: @provider_name)
150
156
  warn "Failed to fetch MCP servers for #{@provider_name}: #{e.message}" if ENV["AIDP_DEBUG"]
151
157
  []
152
158
  end
@@ -157,7 +163,8 @@ module Aidp
157
163
  # Try to find the binary
158
164
  path = begin
159
165
  Aidp::Util.which(binary_name)
160
- rescue
166
+ rescue => e
167
+ log_rescue(e, component: "provider_info", action: "locate_provider_binary", fallback: nil, provider: @provider_name, binary: binary_name)
161
168
  nil
162
169
  end
163
170
  return nil unless path
@@ -183,7 +190,8 @@ module Aidp
183
190
  Process.kill("TERM", pid)
184
191
  sleep 0.1
185
192
  Process.kill("KILL", pid)
186
- rescue
193
+ rescue => e
194
+ log_rescue(e, component: "provider_info", action: "kill_timeout_provider_command", fallback: nil, provider: @provider_name, binary: binary_name, pid: pid)
187
195
  nil
188
196
  end
189
197
  return nil
@@ -192,7 +200,8 @@ module Aidp
192
200
  output = r.read
193
201
  r.close
194
202
  output
195
- rescue
203
+ rescue => e
204
+ log_rescue(e, component: "provider_info", action: "execute_provider_command", fallback: nil, provider: @provider_name, binary: binary_name, args: args)
196
205
  nil
197
206
  end
198
207
  end
@@ -355,6 +364,7 @@ module Aidp
355
364
  # Create provider instance
356
365
  @provider_instance = provider_class.new
357
366
  rescue => e
367
+ log_rescue(e, component: "provider_info", action: "create_provider_instance", fallback: nil, provider: @provider_name)
358
368
  warn "Failed to create provider instance for #{@provider_name}: #{e.message}" if ENV["AIDP_DEBUG"]
359
369
  nil
360
370
  end