aidp 0.32.0 → 0.33.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/lib/aidp/analyze/feature_analyzer.rb +322 -320
- 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.rb +2 -1
- data/lib/aidp/comment_consolidator.rb +78 -0
- data/lib/aidp/concurrency.rb +0 -3
- data/lib/aidp/config.rb +0 -1
- data/lib/aidp/config_paths.rb +71 -0
- data/lib/aidp/execute/work_loop_runner.rb +324 -15
- data/lib/aidp/harness/ai_filter_factory.rb +285 -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/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_manager.rb +90 -2
- data/lib/aidp/harness/runner.rb +0 -11
- data/lib/aidp/harness/test_runner.rb +179 -41
- data/lib/aidp/harness/thinking_depth_manager.rb +16 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +0 -2
- data/lib/aidp/loader.rb +195 -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 +2 -2
- data/lib/aidp/provider_manager.rb +1 -7
- data/lib/aidp/setup/wizard.rb +279 -9
- 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/change_request_processor.rb +152 -14
- data/lib/aidp/watch/repository_client.rb +41 -0
- data/lib/aidp/watch/runner.rb +29 -18
- data/lib/aidp/watch.rb +5 -7
- data/lib/aidp/workstream_cleanup.rb +0 -2
- data/lib/aidp/workstream_executor.rb +0 -4
- data/lib/aidp/worktree.rb +0 -1
- data/lib/aidp.rb +21 -106
- metadata +72 -36
- data/lib/aidp/config/paths.rb +0 -131
|
@@ -10,6 +10,7 @@ require_relative "agent_signal_parser"
|
|
|
10
10
|
require_relative "steps"
|
|
11
11
|
require_relative "../harness/test_runner"
|
|
12
12
|
require_relative "../errors"
|
|
13
|
+
require_relative "../style_guide/selector"
|
|
13
14
|
|
|
14
15
|
module Aidp
|
|
15
16
|
module Execute
|
|
@@ -54,6 +55,7 @@ module Aidp
|
|
|
54
55
|
@checkpoint = Checkpoint.new(project_dir)
|
|
55
56
|
@checkpoint_display = CheckpointDisplay.new(prompt: @prompt)
|
|
56
57
|
@guard_policy = GuardPolicy.new(project_dir, config.guards_config)
|
|
58
|
+
@work_context = {}
|
|
57
59
|
@persistent_tasklist = PersistentTasklist.new(project_dir)
|
|
58
60
|
@iteration_count = 0
|
|
59
61
|
@step_name = nil
|
|
@@ -68,6 +70,9 @@ module Aidp
|
|
|
68
70
|
@thinking_depth_manager = options[:thinking_depth_manager] || Aidp::Harness::ThinkingDepthManager.new(config)
|
|
69
71
|
@consecutive_failures = 0
|
|
70
72
|
@last_tier = nil
|
|
73
|
+
|
|
74
|
+
# Initialize style guide selector for intelligent section selection
|
|
75
|
+
@style_guide_selector = options[:style_guide_selector] || Aidp::StyleGuide::Selector.new(project_dir: project_dir)
|
|
71
76
|
end
|
|
72
77
|
|
|
73
78
|
# Execute a step using fix-forward work loop pattern
|
|
@@ -75,6 +80,7 @@ module Aidp
|
|
|
75
80
|
# Never rolls back - only moves forward through fixes
|
|
76
81
|
def execute_step(step_name, step_spec, context = {})
|
|
77
82
|
@step_name = step_name
|
|
83
|
+
@work_context = context
|
|
78
84
|
@iteration_count = 0
|
|
79
85
|
transition_to(:ready)
|
|
80
86
|
|
|
@@ -161,9 +167,20 @@ module Aidp
|
|
|
161
167
|
|
|
162
168
|
transition_to(:apply_patch)
|
|
163
169
|
|
|
170
|
+
# Preview provider/model selection and queued checks for this iteration
|
|
171
|
+
preview_provider, preview_model, _model_data = select_model_for_current_tier
|
|
172
|
+
prompt_length = @prompt_manager.read&.length || 0
|
|
173
|
+
checks_summary = planned_checks_summary
|
|
174
|
+
display_iteration_overview(preview_provider, preview_model, prompt_length, checks_summary)
|
|
175
|
+
log_iteration_status("running",
|
|
176
|
+
provider: preview_provider,
|
|
177
|
+
model: preview_model,
|
|
178
|
+
prompt_length: prompt_length,
|
|
179
|
+
checks: checks_summary)
|
|
180
|
+
|
|
164
181
|
# Wrap agent call in exception handling for true fix-forward
|
|
165
182
|
begin
|
|
166
|
-
agent_result = apply_patch
|
|
183
|
+
agent_result = apply_patch(preview_provider, preview_model)
|
|
167
184
|
rescue Aidp::Errors::ConfigurationError
|
|
168
185
|
# Configuration errors should crash immediately (crash-early principle)
|
|
169
186
|
# Re-raise without catching
|
|
@@ -258,6 +275,12 @@ module Aidp
|
|
|
258
275
|
display_task_summary
|
|
259
276
|
display_message("✅ Step #{@step_name} completed after #{@iteration_count} iterations", type: :success)
|
|
260
277
|
display_state_summary
|
|
278
|
+
log_iteration_status("completed",
|
|
279
|
+
provider: preview_provider,
|
|
280
|
+
model: preview_model,
|
|
281
|
+
prompt_length: prompt_length,
|
|
282
|
+
checks: checks_summary,
|
|
283
|
+
task_status: "complete")
|
|
261
284
|
archive_and_cleanup
|
|
262
285
|
|
|
263
286
|
return build_agentic_payload(
|
|
@@ -272,6 +295,12 @@ module Aidp
|
|
|
272
295
|
display_message(" All checks passed but tasks not complete", type: :warning)
|
|
273
296
|
display_message(" #{task_completion_result[:message]}", type: :warning)
|
|
274
297
|
display_task_summary
|
|
298
|
+
log_iteration_status("checks_passed_tasks_incomplete",
|
|
299
|
+
provider: preview_provider,
|
|
300
|
+
model: preview_model,
|
|
301
|
+
prompt_length: prompt_length,
|
|
302
|
+
checks: checks_summary,
|
|
303
|
+
task_status: "incomplete")
|
|
275
304
|
transition_to(:next_patch)
|
|
276
305
|
|
|
277
306
|
# Append task completion requirement to PROMPT.md
|
|
@@ -279,6 +308,11 @@ module Aidp
|
|
|
279
308
|
end
|
|
280
309
|
else
|
|
281
310
|
display_message(" All checks passed but work not marked complete", type: :info)
|
|
311
|
+
log_iteration_status("checks_passed_waiting_agent_completion",
|
|
312
|
+
provider: preview_provider,
|
|
313
|
+
model: preview_model,
|
|
314
|
+
prompt_length: prompt_length,
|
|
315
|
+
checks: checks_summary)
|
|
282
316
|
transition_to(:next_patch)
|
|
283
317
|
end
|
|
284
318
|
else
|
|
@@ -289,6 +323,12 @@ module Aidp
|
|
|
289
323
|
diagnostic = diagnose_failures(all_results)
|
|
290
324
|
|
|
291
325
|
transition_to(:next_patch)
|
|
326
|
+
log_iteration_status("checks_failed",
|
|
327
|
+
provider: preview_provider,
|
|
328
|
+
model: preview_model,
|
|
329
|
+
prompt_length: prompt_length,
|
|
330
|
+
checks: checks_summary,
|
|
331
|
+
failures: failure_summary_for_log(all_results))
|
|
292
332
|
prepare_next_iteration(all_results, diagnostic)
|
|
293
333
|
end
|
|
294
334
|
end
|
|
@@ -499,8 +539,8 @@ module Aidp
|
|
|
499
539
|
end
|
|
500
540
|
|
|
501
541
|
# Apply patch - send PROMPT.md to agent
|
|
502
|
-
def apply_patch
|
|
503
|
-
send_to_agent
|
|
542
|
+
def apply_patch(selected_provider = nil, selected_model = nil)
|
|
543
|
+
send_to_agent(selected_provider: selected_provider, selected_model: selected_model)
|
|
504
544
|
end
|
|
505
545
|
|
|
506
546
|
# Check if agent marked work complete
|
|
@@ -549,7 +589,9 @@ module Aidp
|
|
|
549
589
|
# Traditional prompt building (fallback or when optimization disabled)
|
|
550
590
|
template_content = load_template(step_spec["templates"]&.first)
|
|
551
591
|
prd_content = load_prd
|
|
552
|
-
|
|
592
|
+
# Use provider-aware style guide loading - skips for Claude/Copilot,
|
|
593
|
+
# selects relevant STYLE_GUIDE sections for other providers
|
|
594
|
+
style_guide = load_style_guide_for_provider(context)
|
|
553
595
|
user_input = format_user_input(context[:user_input])
|
|
554
596
|
deterministic_outputs = Array(context[:deterministic_outputs])
|
|
555
597
|
previous_summary = context[:previous_agent_summary]
|
|
@@ -729,7 +771,7 @@ module Aidp
|
|
|
729
771
|
parts.join("\n")
|
|
730
772
|
end
|
|
731
773
|
|
|
732
|
-
def send_to_agent
|
|
774
|
+
def send_to_agent(selected_provider: nil, selected_model: nil)
|
|
733
775
|
prompt_content = @prompt_manager.read
|
|
734
776
|
return {status: "error", message: "PROMPT.md not found"} unless prompt_content
|
|
735
777
|
|
|
@@ -737,9 +779,11 @@ module Aidp
|
|
|
737
779
|
full_prompt = build_work_loop_header(@step_name, @iteration_count) + "\n\n" + prompt_content
|
|
738
780
|
|
|
739
781
|
# Select model based on thinking depth tier
|
|
740
|
-
provider_name
|
|
782
|
+
provider_name = selected_provider
|
|
783
|
+
model_name = selected_model
|
|
784
|
+
provider_name, model_name, _model_data = select_model_for_current_tier if provider_name.nil? || model_name.nil?
|
|
741
785
|
|
|
742
|
-
if provider_name.nil?
|
|
786
|
+
if provider_name.nil?
|
|
743
787
|
Aidp.logger.error("work_loop", "Failed to select model for tier",
|
|
744
788
|
tier: @thinking_depth_manager.current_tier,
|
|
745
789
|
step: @step_name,
|
|
@@ -750,7 +794,8 @@ module Aidp
|
|
|
750
794
|
# Log model selection
|
|
751
795
|
tier = @thinking_depth_manager.current_tier
|
|
752
796
|
if @last_tier != tier
|
|
753
|
-
|
|
797
|
+
model_label = model_name || "auto"
|
|
798
|
+
display_message(" 💡 Using tier: #{tier} (#{provider_name}/#{model_label})", type: :info)
|
|
754
799
|
@last_tier = tier
|
|
755
800
|
end
|
|
756
801
|
|
|
@@ -772,6 +817,164 @@ module Aidp
|
|
|
772
817
|
end
|
|
773
818
|
end
|
|
774
819
|
|
|
820
|
+
def display_iteration_overview(provider_name, model_name, prompt_length, checks_summary = nil)
|
|
821
|
+
tier = @thinking_depth_manager.current_tier
|
|
822
|
+
checks = checks_summary
|
|
823
|
+
checks ||= summarize_checks(@test_runner.planned_commands) if @test_runner.respond_to?(:planned_commands)
|
|
824
|
+
model_label = model_name || "auto"
|
|
825
|
+
context_labels = iteration_context_labels
|
|
826
|
+
|
|
827
|
+
display_message(" • Step: #{@step_name} | Tier: #{tier} | Model: #{provider_name}/#{model_label}", type: :info)
|
|
828
|
+
display_message(" • Prompt size: #{prompt_length} chars | State: #{STATES[@current_state]}", type: :info)
|
|
829
|
+
display_message(" • Upcoming checks: #{checks}", type: :info) if checks && !checks.empty?
|
|
830
|
+
display_message(" • Context: #{context_labels.join(" | ")}", type: :info) if context_labels.any?
|
|
831
|
+
|
|
832
|
+
# Display output filtering configuration if enabled
|
|
833
|
+
filtering_info = summarize_output_filtering
|
|
834
|
+
display_message(" • Output filtering: #{filtering_info}", type: :info) if filtering_info
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# Summarize output filtering configuration
|
|
838
|
+
def summarize_output_filtering
|
|
839
|
+
return nil unless @config.respond_to?(:output_filtering_enabled?) && @config.output_filtering_enabled?
|
|
840
|
+
|
|
841
|
+
iteration = @test_runner.respond_to?(:iteration_count) ? @test_runner.iteration_count : 0
|
|
842
|
+
|
|
843
|
+
test_mode = if @config.respond_to?(:test_output_mode)
|
|
844
|
+
@config.test_output_mode
|
|
845
|
+
elsif iteration > 1
|
|
846
|
+
:failures_only
|
|
847
|
+
else
|
|
848
|
+
:full
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
lint_mode = if @config.respond_to?(:lint_output_mode)
|
|
852
|
+
@config.lint_output_mode
|
|
853
|
+
elsif iteration > 1
|
|
854
|
+
:failures_only
|
|
855
|
+
else
|
|
856
|
+
:full
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
if test_mode == :full && lint_mode == :full
|
|
860
|
+
nil # Don't show message when no filtering is active
|
|
861
|
+
else
|
|
862
|
+
"test=#{test_mode}, lint=#{lint_mode}"
|
|
863
|
+
end
|
|
864
|
+
rescue
|
|
865
|
+
nil
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
# Display output filtering statistics after test/lint runs
|
|
869
|
+
def display_filtering_stats
|
|
870
|
+
return unless @test_runner.respond_to?(:filter_stats)
|
|
871
|
+
|
|
872
|
+
stats = @test_runner.filter_stats
|
|
873
|
+
return if stats[:total_input_bytes].zero?
|
|
874
|
+
|
|
875
|
+
reduction = ((stats[:total_input_bytes] - stats[:total_output_bytes]).to_f / stats[:total_input_bytes] * 100).round(1)
|
|
876
|
+
return if reduction <= 0
|
|
877
|
+
|
|
878
|
+
display_message(" 📉 Token optimization: #{reduction}% reduction " \
|
|
879
|
+
"(#{format_bytes(stats[:total_input_bytes])} → #{format_bytes(stats[:total_output_bytes])})", type: :info)
|
|
880
|
+
rescue
|
|
881
|
+
# Silently ignore errors in stats display
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
def format_bytes(bytes)
|
|
885
|
+
if bytes >= 1024 * 1024
|
|
886
|
+
"#{(bytes / 1024.0 / 1024.0).round(1)}MB"
|
|
887
|
+
elsif bytes >= 1024
|
|
888
|
+
"#{(bytes / 1024.0).round(1)}KB"
|
|
889
|
+
else
|
|
890
|
+
"#{bytes}B"
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def summarize_checks(planned)
|
|
895
|
+
labels = {
|
|
896
|
+
tests: "tests",
|
|
897
|
+
lints: "linters",
|
|
898
|
+
formatters: "formatters",
|
|
899
|
+
builds: "builds",
|
|
900
|
+
docs: "docs"
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
summaries = planned.map do |category, commands|
|
|
904
|
+
count = Array(commands).size
|
|
905
|
+
next if count.zero?
|
|
906
|
+
|
|
907
|
+
label = labels[category] || category.to_s
|
|
908
|
+
cmd_names = Array(commands).map do |cmd|
|
|
909
|
+
cmd.is_a?(Hash) ? cmd[:command] : cmd
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
if cmd_names.size <= 2
|
|
913
|
+
"#{label} (#{cmd_names.join(", ")})"
|
|
914
|
+
else
|
|
915
|
+
"#{label} (#{cmd_names.first(2).join(", ")} +#{cmd_names.size - 2} more)"
|
|
916
|
+
end
|
|
917
|
+
end.compact
|
|
918
|
+
|
|
919
|
+
summaries.join(" | ")
|
|
920
|
+
rescue => e
|
|
921
|
+
Aidp.log_warn("work_loop", "summarize_checks_failed", error: e.message)
|
|
922
|
+
nil
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
def planned_checks_summary
|
|
926
|
+
return nil unless @test_runner.respond_to?(:planned_commands)
|
|
927
|
+
|
|
928
|
+
summarize_checks(@test_runner.planned_commands)
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
def failure_summary_for_log(all_results)
|
|
932
|
+
Array(all_results).each_with_object([]) do |(category, results), summary|
|
|
933
|
+
next if results[:success]
|
|
934
|
+
|
|
935
|
+
failures = results[:required_failures] || results[:failures] || []
|
|
936
|
+
count = failures.size
|
|
937
|
+
commands = Array(failures).map { |f| f[:command] }.compact
|
|
938
|
+
|
|
939
|
+
summary << if commands.any?
|
|
940
|
+
"#{category}: #{count} (#{commands.first(2).join(", ")})"
|
|
941
|
+
else
|
|
942
|
+
"#{category}: #{count}"
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
rescue => e
|
|
946
|
+
Aidp.log_warn("work_loop", "failure_summary_for_log_failed", error: e.message)
|
|
947
|
+
[]
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
def log_iteration_status(status, provider:, model:, prompt_length:, checks: nil, failures: nil, task_status: nil)
|
|
951
|
+
context_labels = iteration_context_labels
|
|
952
|
+
metadata = {
|
|
953
|
+
step: @step_name,
|
|
954
|
+
iteration: @iteration_count,
|
|
955
|
+
state: STATES[@current_state],
|
|
956
|
+
tier: @thinking_depth_manager.current_tier,
|
|
957
|
+
provider: provider,
|
|
958
|
+
model: model,
|
|
959
|
+
prompt_length: prompt_length,
|
|
960
|
+
checks: checks,
|
|
961
|
+
failures: failures,
|
|
962
|
+
task_status: task_status
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
metadata.merge!(iteration_context_metadata)
|
|
966
|
+
metadata.delete_if { |_, value| value.nil? || (value.respond_to?(:empty?) && value.empty?) }
|
|
967
|
+
|
|
968
|
+
message = "Iteration #{@iteration_count} for #{@step_name}: #{status}"
|
|
969
|
+
message += " | #{context_labels.join(" | ")}" if context_labels.any?
|
|
970
|
+
|
|
971
|
+
Aidp.log_info("work_loop_iteration",
|
|
972
|
+
message,
|
|
973
|
+
**metadata)
|
|
974
|
+
rescue => e
|
|
975
|
+
Aidp.log_warn("work_loop", "failed_to_log_iteration_status", error: e.message)
|
|
976
|
+
end
|
|
977
|
+
|
|
775
978
|
def build_work_loop_header(step_name, iteration)
|
|
776
979
|
parts = []
|
|
777
980
|
parts << "# Work Loop: #{step_name} (Iteration #{iteration})"
|
|
@@ -832,6 +1035,24 @@ module Aidp
|
|
|
832
1035
|
parts.join("\n")
|
|
833
1036
|
end
|
|
834
1037
|
|
|
1038
|
+
def iteration_context_metadata
|
|
1039
|
+
ctx = (@options || {}).merge(@work_context || {})
|
|
1040
|
+
{
|
|
1041
|
+
issue: issue_context_label(ctx),
|
|
1042
|
+
pr: pr_context_label(ctx),
|
|
1043
|
+
step_position: step_position_label(@step_name, ctx)
|
|
1044
|
+
}.compact
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
def iteration_context_labels
|
|
1048
|
+
meta = iteration_context_metadata
|
|
1049
|
+
labels = []
|
|
1050
|
+
labels << meta[:issue] if meta[:issue]
|
|
1051
|
+
labels << meta[:pr] if meta[:pr]
|
|
1052
|
+
labels << meta[:step_position] if meta[:step_position]
|
|
1053
|
+
labels
|
|
1054
|
+
end
|
|
1055
|
+
|
|
835
1056
|
def prompt_marked_complete?
|
|
836
1057
|
prompt_content = @prompt_manager.read
|
|
837
1058
|
return false unless prompt_content
|
|
@@ -938,30 +1159,50 @@ module Aidp
|
|
|
938
1159
|
|
|
939
1160
|
# Check if we should reinject the style guide at this iteration
|
|
940
1161
|
def should_reinject_style_guide?
|
|
1162
|
+
# Skip reinjection for providers with instruction files (Claude, GitHub Copilot)
|
|
1163
|
+
current_provider = @provider_manager&.current_provider
|
|
1164
|
+
return false unless @style_guide_selector.provider_needs_style_guide?(current_provider)
|
|
1165
|
+
|
|
941
1166
|
# Reinject on intervals (5, 10, 15, etc.) but not on iteration 1
|
|
942
1167
|
@iteration_count > 1 && (@iteration_count % STYLE_GUIDE_REMINDER_INTERVAL == 0)
|
|
943
1168
|
end
|
|
944
1169
|
|
|
945
1170
|
# Create style guide reminder text
|
|
946
1171
|
def reinject_style_guide_reminder
|
|
947
|
-
|
|
1172
|
+
current_provider = @provider_manager&.current_provider
|
|
1173
|
+
|
|
1174
|
+
# Skip for providers with instruction files
|
|
1175
|
+
unless @style_guide_selector.provider_needs_style_guide?(current_provider)
|
|
1176
|
+
Aidp.log_debug("work_loop", "skipping_style_guide_reminder",
|
|
1177
|
+
provider: current_provider,
|
|
1178
|
+
reason: "provider has instruction file")
|
|
1179
|
+
return ""
|
|
1180
|
+
end
|
|
1181
|
+
|
|
948
1182
|
template_content = load_current_template
|
|
949
1183
|
|
|
1184
|
+
# Use provider-aware style guide loading with context-based section selection
|
|
1185
|
+
style_guide = load_style_guide_for_provider(@work_context)
|
|
1186
|
+
|
|
950
1187
|
reminder = []
|
|
951
1188
|
reminder << "### 🔄 Style Guide & Template Reminder (Iteration #{@iteration_count})"
|
|
952
1189
|
reminder << ""
|
|
953
1190
|
reminder << "**IMPORTANT**: To prevent drift from project conventions, please review:"
|
|
954
1191
|
reminder << ""
|
|
955
1192
|
|
|
956
|
-
if style_guide
|
|
957
|
-
reminder << "####
|
|
958
|
-
reminder << "```"
|
|
959
|
-
# Include
|
|
960
|
-
style_guide_preview =
|
|
1193
|
+
if style_guide && !style_guide.empty?
|
|
1194
|
+
reminder << "#### Relevant Style Guide Sections"
|
|
1195
|
+
reminder << "```markdown"
|
|
1196
|
+
# Include selected sections (already limited by selector)
|
|
1197
|
+
style_guide_preview = if style_guide.length > 2000
|
|
1198
|
+
style_guide[0...2000] + "\n...(truncated)"
|
|
1199
|
+
else
|
|
1200
|
+
style_guide
|
|
1201
|
+
end
|
|
961
1202
|
reminder << style_guide_preview
|
|
962
1203
|
reminder << "```"
|
|
963
1204
|
reminder << ""
|
|
964
|
-
display_message(" [STYLE_GUIDE] Re-injecting
|
|
1205
|
+
display_message(" [STYLE_GUIDE] Re-injecting selected STYLE_GUIDE sections at iteration #{@iteration_count}", type: :info)
|
|
965
1206
|
end
|
|
966
1207
|
|
|
967
1208
|
if template_content
|
|
@@ -1051,6 +1292,74 @@ module Aidp
|
|
|
1051
1292
|
File.exist?(style_guide_path) ? File.read(style_guide_path) : nil
|
|
1052
1293
|
end
|
|
1053
1294
|
|
|
1295
|
+
# Load style guide content appropriate for the current provider and context
|
|
1296
|
+
# Returns nil for providers with instruction files (Claude, GitHub Copilot)
|
|
1297
|
+
# Returns selected STYLE_GUIDE sections for other providers
|
|
1298
|
+
#
|
|
1299
|
+
# @param context [Hash] Task context for keyword extraction
|
|
1300
|
+
# @return [String, nil] Style guide content or nil if not needed
|
|
1301
|
+
def load_style_guide_for_provider(context = {})
|
|
1302
|
+
current_provider = @provider_manager&.current_provider
|
|
1303
|
+
|
|
1304
|
+
# Skip style guide for providers with their own instruction files
|
|
1305
|
+
unless @style_guide_selector.provider_needs_style_guide?(current_provider)
|
|
1306
|
+
Aidp.log_debug("work_loop", "skipping_style_guide",
|
|
1307
|
+
provider: current_provider,
|
|
1308
|
+
reason: "provider has instruction file")
|
|
1309
|
+
return nil
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
# Extract keywords from context for intelligent section selection
|
|
1313
|
+
keywords = extract_style_guide_keywords(context)
|
|
1314
|
+
|
|
1315
|
+
# Select relevant sections from STYLE_GUIDE.md
|
|
1316
|
+
content = @style_guide_selector.select_sections(
|
|
1317
|
+
keywords: keywords,
|
|
1318
|
+
include_core: true,
|
|
1319
|
+
max_lines: 500 # Limit to keep prompt size manageable
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
return nil if content.nil? || content.empty?
|
|
1323
|
+
|
|
1324
|
+
Aidp.log_debug("work_loop", "style_guide_selected",
|
|
1325
|
+
provider: current_provider,
|
|
1326
|
+
keywords: keywords,
|
|
1327
|
+
content_lines: content.lines.count)
|
|
1328
|
+
|
|
1329
|
+
content
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1332
|
+
# Extract keywords from task context for style guide section selection
|
|
1333
|
+
#
|
|
1334
|
+
# @param context [Hash] Task context
|
|
1335
|
+
# @return [Array<String>] Keywords for section selection
|
|
1336
|
+
def extract_style_guide_keywords(context)
|
|
1337
|
+
keywords = []
|
|
1338
|
+
|
|
1339
|
+
# Extract from step name
|
|
1340
|
+
step_lower = @step_name.to_s.downcase
|
|
1341
|
+
keywords << "testing" if step_lower.include?("test")
|
|
1342
|
+
keywords << "implementation" if step_lower.include?("implement")
|
|
1343
|
+
keywords << "refactor" if step_lower.include?("refactor")
|
|
1344
|
+
|
|
1345
|
+
# Extract from user input
|
|
1346
|
+
user_input = context[:user_input]
|
|
1347
|
+
if user_input.is_a?(Hash)
|
|
1348
|
+
keywords.concat(@style_guide_selector.extract_keywords(user_input.values.join(" ")))
|
|
1349
|
+
elsif user_input.is_a?(String)
|
|
1350
|
+
keywords.concat(@style_guide_selector.extract_keywords(user_input))
|
|
1351
|
+
end
|
|
1352
|
+
|
|
1353
|
+
# Extract from affected files
|
|
1354
|
+
affected_files = context[:affected_files] || []
|
|
1355
|
+
affected_files.each do |file|
|
|
1356
|
+
keywords << "testing" if file.include?("spec") || file.include?("test")
|
|
1357
|
+
keywords << "tty" if file.include?("cli") || file.include?("tui")
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
keywords.uniq
|
|
1361
|
+
end
|
|
1362
|
+
|
|
1054
1363
|
def format_user_input(user_input)
|
|
1055
1364
|
return nil if user_input.nil? || user_input.empty?
|
|
1056
1365
|
|