aidp 0.32.0 → 0.34.0
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/README.md +35 -0
- data/lib/aidp/analyze/feature_analyzer.rb +322 -320
- data/lib/aidp/analyze/tree_sitter_scan.rb +3 -0
- data/lib/aidp/auto_update/coordinator.rb +97 -7
- data/lib/aidp/auto_update.rb +0 -12
- data/lib/aidp/cli/devcontainer_commands.rb +0 -5
- data/lib/aidp/cli/eval_command.rb +399 -0
- data/lib/aidp/cli/harness_command.rb +1 -1
- data/lib/aidp/cli/security_command.rb +416 -0
- data/lib/aidp/cli/tools_command.rb +6 -4
- data/lib/aidp/cli.rb +172 -4
- data/lib/aidp/comment_consolidator.rb +78 -0
- data/lib/aidp/concurrency/exec.rb +3 -0
- data/lib/aidp/concurrency.rb +0 -3
- data/lib/aidp/config.rb +113 -1
- data/lib/aidp/config_paths.rb +91 -0
- data/lib/aidp/daemon/runner.rb +8 -4
- data/lib/aidp/errors.rb +134 -0
- data/lib/aidp/evaluations/context_capture.rb +205 -0
- data/lib/aidp/evaluations/evaluation_record.rb +114 -0
- data/lib/aidp/evaluations/evaluation_storage.rb +250 -0
- data/lib/aidp/evaluations.rb +23 -0
- data/lib/aidp/execute/async_work_loop_runner.rb +4 -1
- data/lib/aidp/execute/interactive_repl.rb +6 -2
- data/lib/aidp/execute/prompt_evaluator.rb +359 -0
- data/lib/aidp/execute/repl_macros.rb +100 -1
- data/lib/aidp/execute/work_loop_runner.rb +719 -58
- data/lib/aidp/execute/work_loop_state.rb +4 -1
- data/lib/aidp/execute/workflow_selector.rb +3 -0
- data/lib/aidp/harness/ai_decision_engine.rb +79 -0
- data/lib/aidp/harness/ai_filter_factory.rb +285 -0
- data/lib/aidp/harness/capability_registry.rb +2 -0
- data/lib/aidp/harness/condition_detector.rb +3 -0
- data/lib/aidp/harness/config_loader.rb +3 -0
- data/lib/aidp/harness/config_schema.rb +97 -1
- data/lib/aidp/harness/config_validator.rb +1 -1
- data/lib/aidp/harness/configuration.rb +61 -5
- data/lib/aidp/harness/enhanced_runner.rb +14 -11
- data/lib/aidp/harness/error_handler.rb +3 -0
- data/lib/aidp/harness/filter_definition.rb +212 -0
- data/lib/aidp/harness/generated_filter_strategy.rb +197 -0
- data/lib/aidp/harness/output_filter.rb +50 -25
- data/lib/aidp/harness/output_filter_config.rb +129 -0
- data/lib/aidp/harness/provider_factory.rb +3 -0
- data/lib/aidp/harness/provider_manager.rb +96 -2
- data/lib/aidp/harness/runner.rb +5 -12
- data/lib/aidp/harness/state/persistence.rb +3 -0
- data/lib/aidp/harness/state_manager.rb +3 -0
- data/lib/aidp/harness/status_display.rb +28 -20
- data/lib/aidp/harness/test_runner.rb +179 -41
- data/lib/aidp/harness/thinking_depth_manager.rb +44 -28
- data/lib/aidp/harness/ui/enhanced_tui.rb +4 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +4 -0
- data/lib/aidp/harness/ui/error_handler.rb +3 -0
- data/lib/aidp/harness/ui/job_monitor.rb +4 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +2 -2
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +6 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +3 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +3 -0
- data/lib/aidp/harness/user_interface.rb +3 -0
- data/lib/aidp/loader.rb +195 -0
- data/lib/aidp/logger.rb +3 -0
- data/lib/aidp/message_display.rb +31 -0
- data/lib/aidp/metadata/compiler.rb +29 -17
- data/lib/aidp/metadata/query.rb +1 -1
- data/lib/aidp/metadata/scanner.rb +8 -1
- data/lib/aidp/metadata/tool_metadata.rb +13 -13
- data/lib/aidp/metadata/validator.rb +10 -0
- data/lib/aidp/metadata.rb +16 -0
- data/lib/aidp/pr_worktree_manager.rb +20 -8
- data/lib/aidp/provider_manager.rb +4 -7
- data/lib/aidp/providers/base.rb +2 -0
- data/lib/aidp/security/rule_of_two_enforcer.rb +210 -0
- data/lib/aidp/security/secrets_proxy.rb +328 -0
- data/lib/aidp/security/secrets_registry.rb +227 -0
- data/lib/aidp/security/trifecta_state.rb +220 -0
- data/lib/aidp/security/watch_mode_handler.rb +306 -0
- data/lib/aidp/security/work_loop_adapter.rb +277 -0
- data/lib/aidp/security.rb +56 -0
- data/lib/aidp/setup/wizard.rb +283 -11
- data/lib/aidp/skills.rb +0 -5
- data/lib/aidp/storage/csv_storage.rb +3 -0
- data/lib/aidp/style_guide/selector.rb +360 -0
- data/lib/aidp/tooling_detector.rb +283 -16
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/auto_merger.rb +274 -0
- data/lib/aidp/watch/auto_pr_processor.rb +125 -7
- data/lib/aidp/watch/build_processor.rb +16 -1
- data/lib/aidp/watch/change_request_processor.rb +682 -150
- data/lib/aidp/watch/ci_fix_processor.rb +262 -4
- data/lib/aidp/watch/feedback_collector.rb +191 -0
- data/lib/aidp/watch/hierarchical_pr_strategy.rb +256 -0
- data/lib/aidp/watch/implementation_verifier.rb +142 -1
- data/lib/aidp/watch/plan_generator.rb +70 -13
- data/lib/aidp/watch/plan_processor.rb +12 -5
- data/lib/aidp/watch/projects_processor.rb +286 -0
- data/lib/aidp/watch/repository_client.rb +871 -22
- data/lib/aidp/watch/review_processor.rb +33 -6
- data/lib/aidp/watch/runner.rb +80 -29
- data/lib/aidp/watch/state_store.rb +233 -0
- data/lib/aidp/watch/sub_issue_creator.rb +221 -0
- data/lib/aidp/watch.rb +5 -7
- data/lib/aidp/workflows/guided_agent.rb +4 -0
- data/lib/aidp/workstream_cleanup.rb +0 -2
- data/lib/aidp/workstream_executor.rb +3 -4
- data/lib/aidp/worktree.rb +61 -12
- data/lib/aidp/worktree_branch_manager.rb +347 -101
- data/lib/aidp.rb +21 -106
- data/templates/implementation/iterative_implementation.md +46 -3
- metadata +91 -36
- data/lib/aidp/config/paths.rb +0 -131
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
require "open3"
|
|
4
4
|
require_relative "../tooling_detector"
|
|
5
5
|
require_relative "output_filter"
|
|
6
|
+
require_relative "output_filter_config"
|
|
6
7
|
|
|
7
8
|
module Aidp
|
|
8
9
|
module Harness
|
|
9
10
|
# Executes test and linter commands configured in aidp.yml
|
|
10
11
|
# Returns results with exit status and output
|
|
12
|
+
# Applies intelligent output filtering to reduce token consumption
|
|
11
13
|
class TestRunner
|
|
12
14
|
def initialize(project_dir, config)
|
|
13
15
|
@project_dir = project_dir
|
|
14
16
|
@config = config
|
|
15
17
|
@iteration_count = 0
|
|
18
|
+
@filter_stats = {total_input_bytes: 0, total_output_bytes: 0}
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
# Run all configured tests
|
|
@@ -47,6 +50,29 @@ module Aidp
|
|
|
47
50
|
run_command_category(:documentation, "Documentation")
|
|
48
51
|
end
|
|
49
52
|
|
|
53
|
+
# Preview the commands that will run for each category so callers can log intent
|
|
54
|
+
# Returns a hash of category => array of normalized command entries
|
|
55
|
+
def planned_commands
|
|
56
|
+
{
|
|
57
|
+
tests: resolved_commands(:test),
|
|
58
|
+
lints: resolved_commands(:lint),
|
|
59
|
+
formatters: resolved_commands(:formatter),
|
|
60
|
+
builds: resolved_commands(:build),
|
|
61
|
+
docs: resolved_commands(:documentation)
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get current iteration count
|
|
66
|
+
attr_reader :iteration_count
|
|
67
|
+
|
|
68
|
+
# Get filtering statistics
|
|
69
|
+
attr_reader :filter_stats
|
|
70
|
+
|
|
71
|
+
# Reset iteration counter (useful for testing)
|
|
72
|
+
def reset_iteration_count
|
|
73
|
+
@iteration_count = 0
|
|
74
|
+
end
|
|
75
|
+
|
|
50
76
|
private
|
|
51
77
|
|
|
52
78
|
# Run commands for a specific category (test, lint, formatter, build, documentation)
|
|
@@ -59,6 +85,12 @@ module Aidp
|
|
|
59
85
|
# Determine output mode based on category
|
|
60
86
|
mode = determine_output_mode(category)
|
|
61
87
|
|
|
88
|
+
Aidp.log_debug("test_runner", "running_category",
|
|
89
|
+
category: category,
|
|
90
|
+
command_count: commands.length,
|
|
91
|
+
iteration: @iteration_count,
|
|
92
|
+
output_mode: mode)
|
|
93
|
+
|
|
62
94
|
# Execute all commands
|
|
63
95
|
results = commands.map do |cmd_config|
|
|
64
96
|
# Handle both string commands (legacy) and hash format (new)
|
|
@@ -71,6 +103,23 @@ module Aidp
|
|
|
71
103
|
end
|
|
72
104
|
end
|
|
73
105
|
|
|
106
|
+
aggregate_results(results, display_name, mode: mode)
|
|
107
|
+
rescue NameError
|
|
108
|
+
# Logging not available
|
|
109
|
+
commands = resolved_commands(category)
|
|
110
|
+
return {success: true, output: "", failures: [], required_failures: []} if commands.empty?
|
|
111
|
+
|
|
112
|
+
mode = determine_output_mode(category)
|
|
113
|
+
results = commands.map do |cmd_config|
|
|
114
|
+
if cmd_config.is_a?(String)
|
|
115
|
+
result = execute_command(cmd_config, category.to_s)
|
|
116
|
+
result.merge(required: true)
|
|
117
|
+
else
|
|
118
|
+
result = execute_command(cmd_config[:command], category.to_s)
|
|
119
|
+
result.merge(required: cmd_config[:required])
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
74
123
|
aggregate_results(results, display_name, mode: mode)
|
|
75
124
|
end
|
|
76
125
|
|
|
@@ -107,6 +156,11 @@ module Aidp
|
|
|
107
156
|
(optional_failures.any? ? "\n" + format_failures(optional_failures, "#{category} - Optional", mode: mode) : "")
|
|
108
157
|
end
|
|
109
158
|
|
|
159
|
+
# Add filtering summary if filtering was applied
|
|
160
|
+
if mode != :full && all_failures.any?
|
|
161
|
+
output += "\n[Output filtered: mode=#{mode}]"
|
|
162
|
+
end
|
|
163
|
+
|
|
110
164
|
{
|
|
111
165
|
success: success,
|
|
112
166
|
output: output,
|
|
@@ -124,9 +178,12 @@ module Aidp
|
|
|
124
178
|
output << "Exit Code: #{failure[:exit_code]}"
|
|
125
179
|
output << "--- Output ---"
|
|
126
180
|
|
|
181
|
+
# Detect framework for filtering
|
|
182
|
+
framework = detect_framework_from_command(failure[:command])
|
|
183
|
+
|
|
127
184
|
# Apply filtering based on mode and framework
|
|
128
|
-
filtered_stdout = filter_output(failure[:stdout], mode,
|
|
129
|
-
filtered_stderr = filter_output(failure[:stderr], mode, :unknown)
|
|
185
|
+
filtered_stdout = filter_output(failure[:stdout], mode, framework, :test)
|
|
186
|
+
filtered_stderr = filter_output(failure[:stderr], mode, :unknown, :lint)
|
|
130
187
|
|
|
131
188
|
output << filtered_stdout unless filtered_stdout.strip.empty?
|
|
132
189
|
output << filtered_stderr unless filtered_stderr.strip.empty?
|
|
@@ -136,67 +193,139 @@ module Aidp
|
|
|
136
193
|
output.join("\n")
|
|
137
194
|
end
|
|
138
195
|
|
|
139
|
-
def filter_output(raw_output, mode, framework)
|
|
196
|
+
def filter_output(raw_output, mode, framework, category)
|
|
140
197
|
return raw_output if mode == :full || raw_output.nil? || raw_output.empty?
|
|
141
198
|
|
|
142
|
-
|
|
199
|
+
# Build filter config from configuration or defaults
|
|
200
|
+
filter_config = build_filter_config(mode, category)
|
|
201
|
+
|
|
202
|
+
# Load AI-generated filter definition if available (AGD pattern)
|
|
203
|
+
filter_definition = load_filter_definition_for(category)
|
|
204
|
+
|
|
205
|
+
# Track input size for stats
|
|
206
|
+
@filter_stats[:total_input_bytes] += raw_output.bytesize
|
|
207
|
+
|
|
208
|
+
filter = OutputFilter.new(filter_config, filter_definition: filter_definition)
|
|
209
|
+
filtered = filter.filter(raw_output, framework: framework)
|
|
210
|
+
|
|
211
|
+
# Track output size for stats
|
|
212
|
+
@filter_stats[:total_output_bytes] += filtered.bytesize
|
|
213
|
+
|
|
214
|
+
Aidp.log_debug("test_runner", "output_filtered",
|
|
215
|
+
framework: framework,
|
|
143
216
|
mode: mode,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
217
|
+
input_size: raw_output.bytesize,
|
|
218
|
+
output_size: filtered.bytesize,
|
|
219
|
+
has_custom_definition: !filter_definition.nil?)
|
|
148
220
|
|
|
149
|
-
|
|
150
|
-
filter.filter(raw_output, framework: framework)
|
|
221
|
+
filtered
|
|
151
222
|
rescue NameError
|
|
152
223
|
# Logging infrastructure not available
|
|
153
224
|
raw_output
|
|
154
225
|
rescue => e
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
226
|
+
begin
|
|
227
|
+
Aidp.log_warn("test_runner", "filter_failed",
|
|
228
|
+
error: e.message,
|
|
229
|
+
framework: framework)
|
|
230
|
+
rescue NameError
|
|
231
|
+
# Logging not available
|
|
232
|
+
end
|
|
158
233
|
raw_output # Fallback to unfiltered on error
|
|
159
234
|
end
|
|
160
235
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
when
|
|
170
|
-
|
|
171
|
-
else
|
|
172
|
-
:unknown
|
|
236
|
+
# Load AI-generated filter definition for a category
|
|
237
|
+
# @param category [Symbol] :test or :lint
|
|
238
|
+
# @return [FilterDefinition, nil]
|
|
239
|
+
def load_filter_definition_for(category)
|
|
240
|
+
return nil unless @config.respond_to?(:filter_definition_for)
|
|
241
|
+
|
|
242
|
+
# Map category to definition key (matches setup wizard keys)
|
|
243
|
+
definition_key = case category
|
|
244
|
+
when :test then "unit_test"
|
|
245
|
+
when :lint then "lint"
|
|
246
|
+
else category.to_s
|
|
173
247
|
end
|
|
248
|
+
|
|
249
|
+
@config.filter_definition_for(definition_key)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def build_filter_config(mode, category)
|
|
253
|
+
config_hash = {
|
|
254
|
+
mode: mode,
|
|
255
|
+
include_context: output_filtering_include_context,
|
|
256
|
+
context_lines: output_filtering_context_lines,
|
|
257
|
+
max_lines: max_output_lines_for_category(category)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
OutputFilterConfig.from_hash(config_hash)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def detect_framework_from_command(command)
|
|
264
|
+
# Use ToolingDetector's enhanced framework detection
|
|
265
|
+
Aidp::ToolingDetector.framework_from_command(command)
|
|
174
266
|
end
|
|
175
267
|
|
|
176
268
|
def determine_output_mode(category)
|
|
177
269
|
# Check config for category-specific mode
|
|
178
270
|
case category
|
|
179
271
|
when :test
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
:full
|
|
186
|
-
end
|
|
272
|
+
configured_mode = test_output_mode_from_config
|
|
273
|
+
return configured_mode if configured_mode
|
|
274
|
+
|
|
275
|
+
# Default: full on first iteration, failures_only after
|
|
276
|
+
(@iteration_count > 1) ? :failures_only : :full
|
|
187
277
|
when :lint
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
:full
|
|
194
|
-
end
|
|
278
|
+
configured_mode = lint_output_mode_from_config
|
|
279
|
+
return configured_mode if configured_mode
|
|
280
|
+
|
|
281
|
+
# Default: full on first iteration, failures_only after
|
|
282
|
+
(@iteration_count > 1) ? :failures_only : :full
|
|
195
283
|
else
|
|
196
284
|
:full
|
|
197
285
|
end
|
|
198
286
|
end
|
|
199
287
|
|
|
288
|
+
def test_output_mode_from_config
|
|
289
|
+
return nil unless @config.respond_to?(:test_output_mode)
|
|
290
|
+
mode = @config.test_output_mode
|
|
291
|
+
return nil if mode.nil?
|
|
292
|
+
mode.to_sym
|
|
293
|
+
rescue
|
|
294
|
+
nil
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def lint_output_mode_from_config
|
|
298
|
+
return nil unless @config.respond_to?(:lint_output_mode)
|
|
299
|
+
mode = @config.lint_output_mode
|
|
300
|
+
return nil if mode.nil?
|
|
301
|
+
mode.to_sym
|
|
302
|
+
rescue
|
|
303
|
+
nil
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def max_output_lines_for_category(category)
|
|
307
|
+
case category
|
|
308
|
+
when :test
|
|
309
|
+
return @config.test_max_output_lines if @config.respond_to?(:test_max_output_lines) && @config.test_max_output_lines
|
|
310
|
+
OutputFilterConfig::DEFAULT_MAX_LINES
|
|
311
|
+
when :lint
|
|
312
|
+
return @config.lint_max_output_lines if @config.respond_to?(:lint_max_output_lines) && @config.lint_max_output_lines
|
|
313
|
+
300 # Default smaller for lint output
|
|
314
|
+
else
|
|
315
|
+
OutputFilterConfig::DEFAULT_MAX_LINES
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def output_filtering_include_context
|
|
320
|
+
return @config.output_filtering_include_context if @config.respond_to?(:output_filtering_include_context)
|
|
321
|
+
OutputFilterConfig::DEFAULT_INCLUDE_CONTEXT
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def output_filtering_context_lines
|
|
325
|
+
return @config.output_filtering_context_lines if @config.respond_to?(:output_filtering_context_lines)
|
|
326
|
+
OutputFilterConfig::DEFAULT_CONTEXT_LINES
|
|
327
|
+
end
|
|
328
|
+
|
|
200
329
|
# Resolve commands for a specific category
|
|
201
330
|
# Returns normalized command configs (array of {command:, required:} hashes)
|
|
202
331
|
def resolved_commands(category)
|
|
@@ -239,8 +368,17 @@ module Aidp
|
|
|
239
368
|
def detected_tooling
|
|
240
369
|
@detected_tooling ||= Aidp::ToolingDetector.detect(@project_dir)
|
|
241
370
|
rescue => e
|
|
242
|
-
|
|
243
|
-
|
|
371
|
+
begin
|
|
372
|
+
Aidp.log_warn("test_runner", "tooling_detection_failed", error: e.message)
|
|
373
|
+
rescue NameError
|
|
374
|
+
# Logging not available
|
|
375
|
+
end
|
|
376
|
+
Aidp::ToolingDetector::Result.new(
|
|
377
|
+
test_commands: [],
|
|
378
|
+
lint_commands: [],
|
|
379
|
+
formatter_commands: [],
|
|
380
|
+
frameworks: {}
|
|
381
|
+
)
|
|
244
382
|
end
|
|
245
383
|
|
|
246
384
|
def log_fallback(type, commands)
|
|
@@ -15,7 +15,7 @@ module Aidp
|
|
|
15
15
|
|
|
16
16
|
def initialize(configuration, registry: nil, root_dir: nil)
|
|
17
17
|
@configuration = configuration
|
|
18
|
-
@registry = registry || CapabilityRegistry.new(root_dir: root_dir || configuration.
|
|
18
|
+
@registry = registry || CapabilityRegistry.new(root_dir: root_dir || configuration.project_dir)
|
|
19
19
|
@current_tier = nil
|
|
20
20
|
@session_max_tier = nil
|
|
21
21
|
@tier_history = []
|
|
@@ -152,6 +152,15 @@ module Aidp
|
|
|
152
152
|
def select_model_for_tier(tier = nil, provider: nil)
|
|
153
153
|
tier ||= current_tier
|
|
154
154
|
validate_tier!(tier)
|
|
155
|
+
provider_has_no_tiers = provider && configuration.configured_tiers(provider).empty?
|
|
156
|
+
provider_has_catalog_models = provider && !@registry.models_for_provider(provider).empty?
|
|
157
|
+
|
|
158
|
+
if provider_has_no_tiers && !provider_has_catalog_models
|
|
159
|
+
Aidp.log_info("thinking_depth_manager", "No configured tiers for provider, deferring to provider auto model selection",
|
|
160
|
+
requested_tier: tier,
|
|
161
|
+
provider: provider)
|
|
162
|
+
return [provider, nil, {auto_model: true, reason: "provider_has_no_tiers"}]
|
|
163
|
+
end
|
|
155
164
|
|
|
156
165
|
# First, try to get models from user's configuration for this tier and provider
|
|
157
166
|
if provider
|
|
@@ -234,43 +243,47 @@ module Aidp
|
|
|
234
243
|
model: model_name)
|
|
235
244
|
return [provider, model_name, model_data]
|
|
236
245
|
end
|
|
237
|
-
|
|
238
|
-
# If provider doesn't support tier and switching allowed, try others
|
|
239
|
-
unless configuration.allow_provider_switch_for_tier?
|
|
240
|
-
Aidp.log_warn("thinking_depth_manager", "Provider lacks tier in catalog, switching disabled",
|
|
241
|
-
tier: tier,
|
|
242
|
-
provider: provider)
|
|
243
|
-
return nil
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# Try all providers in catalog
|
|
248
|
-
if provider && !configuration.allow_provider_switch_for_tier?
|
|
249
|
-
return nil
|
|
246
|
+
# Per issue #323: Don't return nil here - let fallback logic handle missing tiers
|
|
250
247
|
end
|
|
251
248
|
|
|
252
|
-
|
|
249
|
+
# Try all providers in catalog if provider switching is allowed
|
|
250
|
+
if configuration.allow_provider_switch_for_tier?
|
|
251
|
+
providers_to_try = provider ? (@registry.provider_names - [provider]) : @registry.provider_names
|
|
253
252
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
253
|
+
providers_to_try.each do |prov_name|
|
|
254
|
+
model_name, model_data = @registry.best_model_for_tier(tier, prov_name)
|
|
255
|
+
if model_name
|
|
256
|
+
Aidp.log_info("thinking_depth_manager", "Selected model from catalog (alternate provider)",
|
|
257
|
+
tier: tier,
|
|
258
|
+
original_provider: provider,
|
|
259
|
+
selected_provider: prov_name,
|
|
260
|
+
model: model_name)
|
|
261
|
+
return [prov_name, model_name, model_data]
|
|
262
|
+
end
|
|
263
263
|
end
|
|
264
264
|
end
|
|
265
265
|
|
|
266
266
|
# No model found for requested tier - try fallback to other tiers
|
|
267
|
-
|
|
267
|
+
# Per issue #323: fallback events log at debug level
|
|
268
|
+
Aidp.log_debug("thinking_depth_manager", "tier_not_found_trying_fallback",
|
|
268
269
|
tier: tier,
|
|
269
270
|
provider: provider)
|
|
270
271
|
|
|
271
272
|
result = try_fallback_tiers(tier, provider)
|
|
272
273
|
|
|
274
|
+
# If no model found after fallback, defer to provider auto model selection
|
|
275
|
+
# This allows providers to select their own model when no explicit tier config exists
|
|
276
|
+
# Per issue #323: log at debug level, don't constrain model selection
|
|
277
|
+
if result.nil? && provider
|
|
278
|
+
Aidp.log_debug("thinking_depth_manager", "no_model_for_tier_deferring_to_provider",
|
|
279
|
+
requested_tier: tier,
|
|
280
|
+
provider: provider,
|
|
281
|
+
reason: provider_has_no_tiers ? "provider_has_no_tiers" : "tier_not_configured")
|
|
282
|
+
return [provider, nil, {auto_model: true, reason: provider_has_no_tiers ? "provider_has_no_tiers" : "tier_not_configured"}]
|
|
283
|
+
end
|
|
284
|
+
|
|
273
285
|
unless result
|
|
286
|
+
# This path should only be reached when no provider is specified
|
|
274
287
|
# Enhanced error message with discovery hints
|
|
275
288
|
display_enhanced_tier_error(tier, provider)
|
|
276
289
|
|
|
@@ -436,7 +449,8 @@ module Aidp
|
|
|
436
449
|
|
|
437
450
|
if configured_models.any?
|
|
438
451
|
model_name = configured_models.first
|
|
439
|
-
|
|
452
|
+
# Per issue #323: fallback events log at debug level
|
|
453
|
+
Aidp.log_debug("thinking_depth_manager", "tier_fallback_from_config",
|
|
440
454
|
requested_tier: requested_tier,
|
|
441
455
|
fallback_tier: fallback_tier,
|
|
442
456
|
provider: provider,
|
|
@@ -450,7 +464,8 @@ module Aidp
|
|
|
450
464
|
if provider
|
|
451
465
|
model_name, model_data = @registry.best_model_for_tier(fallback_tier, provider)
|
|
452
466
|
if model_name
|
|
453
|
-
|
|
467
|
+
# Per issue #323: fallback events log at debug level
|
|
468
|
+
Aidp.log_debug("thinking_depth_manager", "tier_fallback_from_catalog",
|
|
454
469
|
requested_tier: requested_tier,
|
|
455
470
|
fallback_tier: fallback_tier,
|
|
456
471
|
provider: provider,
|
|
@@ -466,7 +481,8 @@ module Aidp
|
|
|
466
481
|
|
|
467
482
|
model_name, model_data = @registry.best_model_for_tier(fallback_tier, prov_name)
|
|
468
483
|
if model_name
|
|
469
|
-
|
|
484
|
+
# Per issue #323: fallback events log at debug level
|
|
485
|
+
Aidp.log_debug("thinking_depth_manager", "tier_fallback_provider_switch",
|
|
470
486
|
requested_tier: requested_tier,
|
|
471
487
|
fallback_tier: fallback_tier,
|
|
472
488
|
requested_provider: provider,
|
|
@@ -20,6 +20,10 @@ module Aidp
|
|
|
20
20
|
|
|
21
21
|
class DisplayError < TUIError; end
|
|
22
22
|
|
|
23
|
+
# Expose state for testability
|
|
24
|
+
attr_accessor :headless, :current_mode, :workflow_active, :current_step
|
|
25
|
+
attr_reader :jobs
|
|
26
|
+
|
|
23
27
|
def initialize(prompt: TTY::Prompt.new, tty: $stdin)
|
|
24
28
|
@cursor = TTY::Cursor
|
|
25
29
|
@screen = TTY::Screen
|
|
@@ -11,6 +11,10 @@ module Aidp
|
|
|
11
11
|
class EnhancedWorkflowSelector
|
|
12
12
|
class WorkflowError < StandardError; end
|
|
13
13
|
|
|
14
|
+
# Expose state for testability
|
|
15
|
+
attr_reader :tui, :project_dir, :user_input
|
|
16
|
+
attr_accessor :workflow_selector
|
|
17
|
+
|
|
14
18
|
def initialize(tui = nil, project_dir: Dir.pwd)
|
|
15
19
|
@tui = tui || EnhancedTUI.new
|
|
16
20
|
@user_input = {}
|
|
@@ -18,6 +18,9 @@ module Aidp
|
|
|
18
18
|
|
|
19
19
|
class InteractionError < UIError; end
|
|
20
20
|
|
|
21
|
+
# Expose for testability
|
|
22
|
+
attr_reader :logger
|
|
23
|
+
|
|
21
24
|
def initialize(ui_components = {})
|
|
22
25
|
@logger = ui_components[:logger] || default_logger
|
|
23
26
|
@formatter = ui_components[:formatter] || ErrorFormatter.new
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "main_menu"
|
|
4
|
-
|
|
5
3
|
module Aidp
|
|
6
4
|
module Harness
|
|
7
5
|
module UI
|
|
@@ -24,6 +22,8 @@ module Aidp
|
|
|
24
22
|
|
|
25
23
|
attr_reader :title, :parent_menu
|
|
26
24
|
attr_accessor :drill_down_enabled, :max_depth
|
|
25
|
+
# Expose current_level for testability
|
|
26
|
+
attr_accessor :current_level
|
|
27
27
|
|
|
28
28
|
def add_submenu_item(item)
|
|
29
29
|
validate_submenu_item(item)
|
|
@@ -17,6 +17,9 @@ module Aidp
|
|
|
17
17
|
|
|
18
18
|
class SelectionError < WorkflowError; end
|
|
19
19
|
|
|
20
|
+
# Expose for testability
|
|
21
|
+
attr_reader :prompt
|
|
22
|
+
|
|
20
23
|
WORKFLOW_MODES = {
|
|
21
24
|
simple: {
|
|
22
25
|
name: "Simple Mode",
|
|
@@ -132,6 +135,9 @@ module Aidp
|
|
|
132
135
|
|
|
133
136
|
# Formats workflow selection display
|
|
134
137
|
class WorkflowFormatter
|
|
138
|
+
# Expose for testability
|
|
139
|
+
attr_reader :pastel
|
|
140
|
+
|
|
135
141
|
def initialize
|
|
136
142
|
@pastel = Pastel.new
|
|
137
143
|
end
|
|
@@ -9,6 +9,9 @@ module Aidp
|
|
|
9
9
|
class UserInterface
|
|
10
10
|
include Aidp::MessageDisplay
|
|
11
11
|
|
|
12
|
+
# Expose for testability
|
|
13
|
+
attr_reader :auto_confirm_defaults, :show_help_automatically, :verbose_mode, :file_selection_enabled
|
|
14
|
+
|
|
12
15
|
def initialize(prompt: TTY::Prompt.new)
|
|
13
16
|
@input_history = []
|
|
14
17
|
@file_selection_enabled = false
|