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
data/lib/aidp/loader.rb
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
# Zeitwerk-based class loader with hot code reloading support
|
|
7
|
+
#
|
|
8
|
+
# This module configures Zeitwerk for autoloading AIDP classes and provides
|
|
9
|
+
# a reload capability similar to Rails development mode. When files change
|
|
10
|
+
# (e.g., after git pull in watch mode), calling Aidp::Loader.reload! will
|
|
11
|
+
# unload all classes and allow them to be reloaded on next reference.
|
|
12
|
+
#
|
|
13
|
+
# @example Enable reloading in development
|
|
14
|
+
# Aidp::Loader.setup(enable_reloading: true)
|
|
15
|
+
# # ... code changes on disk ...
|
|
16
|
+
# Aidp::Loader.reload!
|
|
17
|
+
#
|
|
18
|
+
# @example Production mode (no reloading)
|
|
19
|
+
# Aidp::Loader.setup(enable_reloading: false)
|
|
20
|
+
# Aidp::Loader.eager_load!
|
|
21
|
+
module Loader
|
|
22
|
+
class << self
|
|
23
|
+
# @return [Zeitwerk::Loader, nil] The configured loader instance
|
|
24
|
+
attr_accessor :loader
|
|
25
|
+
|
|
26
|
+
# @return [Boolean] Whether reloading is enabled
|
|
27
|
+
attr_accessor :reloading_enabled
|
|
28
|
+
|
|
29
|
+
# Set up the Zeitwerk loader for AIDP
|
|
30
|
+
#
|
|
31
|
+
# @param enable_reloading [Boolean] Whether to enable hot reloading
|
|
32
|
+
# @param eager_load [Boolean] Whether to eager load all classes
|
|
33
|
+
# @return [Zeitwerk::Loader] The configured loader
|
|
34
|
+
def setup(enable_reloading: false, eager_load: false)
|
|
35
|
+
return @loader if @loader
|
|
36
|
+
|
|
37
|
+
Aidp.log_debug("loader", "setup_started",
|
|
38
|
+
enable_reloading: enable_reloading,
|
|
39
|
+
eager_load: eager_load)
|
|
40
|
+
|
|
41
|
+
@reloading_enabled = enable_reloading
|
|
42
|
+
@loader = create_loader
|
|
43
|
+
configure_inflections(@loader)
|
|
44
|
+
configure_ignores(@loader)
|
|
45
|
+
|
|
46
|
+
if enable_reloading
|
|
47
|
+
@loader.enable_reloading
|
|
48
|
+
Aidp.log_debug("loader", "reloading_enabled")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
@loader.setup
|
|
52
|
+
|
|
53
|
+
if eager_load && !enable_reloading
|
|
54
|
+
@loader.eager_load
|
|
55
|
+
Aidp.log_debug("loader", "eager_load_complete")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Aidp.log_info("loader", "setup_complete",
|
|
59
|
+
reloading: enable_reloading,
|
|
60
|
+
eager_loaded: eager_load && !enable_reloading)
|
|
61
|
+
|
|
62
|
+
@loader
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Reload all autoloaded classes
|
|
66
|
+
#
|
|
67
|
+
# This unloads all classes managed by Zeitwerk and allows them to be
|
|
68
|
+
# reloaded on next reference. Only works if enable_reloading was true
|
|
69
|
+
# during setup.
|
|
70
|
+
#
|
|
71
|
+
# @return [Boolean] Whether reload was performed
|
|
72
|
+
def reload!
|
|
73
|
+
unless @loader
|
|
74
|
+
Aidp.log_warn("loader", "reload_skipped", reason: "loader_not_setup")
|
|
75
|
+
return false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless @reloading_enabled
|
|
79
|
+
Aidp.log_warn("loader", "reload_skipped", reason: "reloading_disabled")
|
|
80
|
+
return false
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
Aidp.log_info("loader", "reload_started")
|
|
84
|
+
|
|
85
|
+
begin
|
|
86
|
+
@loader.reload
|
|
87
|
+
Aidp.log_info("loader", "reload_complete")
|
|
88
|
+
true
|
|
89
|
+
rescue => e
|
|
90
|
+
Aidp.log_error("loader", "reload_failed", error: e.message)
|
|
91
|
+
false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Check if loader is set up for reloading
|
|
96
|
+
#
|
|
97
|
+
# @return [Boolean]
|
|
98
|
+
def reloading?
|
|
99
|
+
@reloading_enabled == true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Check if loader is set up
|
|
103
|
+
#
|
|
104
|
+
# @return [Boolean]
|
|
105
|
+
def setup?
|
|
106
|
+
!@loader.nil?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Eager load all classes (production mode)
|
|
110
|
+
#
|
|
111
|
+
# @return [void]
|
|
112
|
+
def eager_load!
|
|
113
|
+
@loader&.eager_load
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Reset the loader (mainly for testing)
|
|
117
|
+
#
|
|
118
|
+
# @return [void]
|
|
119
|
+
def reset!
|
|
120
|
+
if @loader
|
|
121
|
+
begin
|
|
122
|
+
@loader.unload if @reloading_enabled
|
|
123
|
+
rescue Zeitwerk::SetupRequired
|
|
124
|
+
# If loader was never set up, skip unload
|
|
125
|
+
end
|
|
126
|
+
@loader.unregister
|
|
127
|
+
end
|
|
128
|
+
@loader = nil
|
|
129
|
+
@reloading_enabled = false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def create_loader
|
|
135
|
+
loader = Zeitwerk::Loader.new
|
|
136
|
+
loader.tag = "aidp"
|
|
137
|
+
|
|
138
|
+
# Set the root directory for autoloading
|
|
139
|
+
lib_path = File.expand_path("..", __dir__)
|
|
140
|
+
loader.push_dir(lib_path, namespace: Object)
|
|
141
|
+
|
|
142
|
+
loader
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Configure inflections for non-standard class names
|
|
146
|
+
def configure_inflections(loader)
|
|
147
|
+
loader.inflector.inflect(
|
|
148
|
+
# AI-prefixed classes
|
|
149
|
+
"ai_decision_engine" => "AIDecisionEngine",
|
|
150
|
+
"ai_filter_factory" => "AIFilterFactory",
|
|
151
|
+
|
|
152
|
+
# Acronym-based names
|
|
153
|
+
"kb_inspector" => "KBInspector",
|
|
154
|
+
"ui_state" => "UIState",
|
|
155
|
+
"ui_error" => "UIError",
|
|
156
|
+
"rspec_filter_strategy" => "RSpecFilterStrategy",
|
|
157
|
+
|
|
158
|
+
# TTY-related
|
|
159
|
+
"tui" => "TUI",
|
|
160
|
+
"enhanced_tui" => "EnhancedTUI",
|
|
161
|
+
|
|
162
|
+
# Other acronyms
|
|
163
|
+
"pr_worktree_manager" => "PRWorktreeManager",
|
|
164
|
+
"csv_storage" => "CSVStorage",
|
|
165
|
+
"cli" => "CLI",
|
|
166
|
+
"ruby_llm_registry" => "RubyLLMRegistry",
|
|
167
|
+
"rubygems_api_adapter" => "RubyGemsAPIAdapter",
|
|
168
|
+
"submenu" => "SubMenu",
|
|
169
|
+
"terminal_io" => "TerminalIO",
|
|
170
|
+
|
|
171
|
+
# Module folders
|
|
172
|
+
"ui" => "UI"
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Configure files/directories to ignore
|
|
177
|
+
def configure_ignores(loader)
|
|
178
|
+
# Ignore files that are manually required before Zeitwerk
|
|
179
|
+
loader.ignore(File.expand_path("version.rb", __dir__))
|
|
180
|
+
loader.ignore(File.expand_path("core_ext", __dir__))
|
|
181
|
+
loader.ignore(File.expand_path("logger.rb", __dir__))
|
|
182
|
+
loader.ignore(File.expand_path("cli/issue_importer.rb", __dir__))
|
|
183
|
+
loader.ignore(File.expand_path("config/paths.rb", __dir__))
|
|
184
|
+
|
|
185
|
+
# Ignore the loader itself
|
|
186
|
+
loader.ignore(__FILE__)
|
|
187
|
+
|
|
188
|
+
# Ignore files with multiple constants (require manually after setup)
|
|
189
|
+
loader.ignore(File.expand_path("auto_update/errors.rb", __dir__))
|
|
190
|
+
loader.ignore(File.expand_path("errors.rb", __dir__))
|
|
191
|
+
loader.ignore(File.expand_path("harness/state/errors.rb", __dir__))
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
data/lib/aidp/logger.rb
CHANGED
|
@@ -307,6 +307,9 @@ module Aidp
|
|
|
307
307
|
|
|
308
308
|
# Module-level logger accessor
|
|
309
309
|
class << self
|
|
310
|
+
# Expose for testability
|
|
311
|
+
attr_writer :logger
|
|
312
|
+
|
|
310
313
|
# Set up global logger instance
|
|
311
314
|
def setup_logger(project_dir = Dir.pwd, config = {})
|
|
312
315
|
@logger = Logger.new(project_dir, config)
|
data/lib/aidp/message_display.rb
CHANGED
|
@@ -8,6 +8,11 @@ module Aidp
|
|
|
8
8
|
# include Aidp::MessageDisplay
|
|
9
9
|
# display_message("Hello", type: :success)
|
|
10
10
|
# Supports color types: :error, :success, :warning, :info, :highlight, :muted
|
|
11
|
+
#
|
|
12
|
+
# Quiet mode:
|
|
13
|
+
# When quiet mode is enabled (via CLI --quiet flag or setting quiet=true on instance),
|
|
14
|
+
# only :error, :warning, and :success messages are displayed. Info, highlight, and muted
|
|
15
|
+
# messages are suppressed to reduce output noise.
|
|
11
16
|
module MessageDisplay
|
|
12
17
|
COLOR_MAP = {
|
|
13
18
|
error: :red,
|
|
@@ -19,12 +24,28 @@ module Aidp
|
|
|
19
24
|
muted: :bright_black
|
|
20
25
|
}.freeze
|
|
21
26
|
|
|
27
|
+
# Message types that are always shown even in quiet mode
|
|
28
|
+
CRITICAL_TYPES = %i[error warning warn success].freeze
|
|
29
|
+
|
|
22
30
|
def self.included(base)
|
|
23
31
|
base.extend(ClassMethods)
|
|
24
32
|
end
|
|
25
33
|
|
|
34
|
+
# Check if quiet mode is enabled
|
|
35
|
+
# Priority: instance @quiet variable > CLI.last_options[:quiet]
|
|
36
|
+
def quiet_mode?
|
|
37
|
+
return @quiet if instance_variable_defined?(:@quiet) && !@quiet.nil?
|
|
38
|
+
|
|
39
|
+
Aidp::CLI.last_options&.dig(:quiet) || false
|
|
40
|
+
rescue
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
26
44
|
# Instance helper for displaying a colored message via TTY::Prompt
|
|
27
45
|
def display_message(message, type: :info)
|
|
46
|
+
# In quiet mode, suppress non-critical messages
|
|
47
|
+
return if quiet_mode? && !CRITICAL_TYPES.include?(type)
|
|
48
|
+
|
|
28
49
|
# Ensure message is UTF-8 encoded to handle emoji and special characters
|
|
29
50
|
message_str = message.to_s
|
|
30
51
|
message_str = message_str.force_encoding("UTF-8") if message_str.encoding.name == "ASCII-8BIT"
|
|
@@ -43,8 +64,18 @@ module Aidp
|
|
|
43
64
|
end
|
|
44
65
|
|
|
45
66
|
module ClassMethods
|
|
67
|
+
# Check if quiet mode is enabled at class level
|
|
68
|
+
def quiet_mode?
|
|
69
|
+
Aidp::CLI.last_options&.dig(:quiet) || false
|
|
70
|
+
rescue
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
46
74
|
# Class-level display helper (uses fresh prompt to respect $stdout changes)
|
|
47
75
|
def display_message(message, type: :info)
|
|
76
|
+
# In quiet mode, suppress non-critical messages
|
|
77
|
+
return if quiet_mode? && !CRITICAL_TYPES.include?(type)
|
|
78
|
+
|
|
48
79
|
# Ensure message is UTF-8 encoded to handle emoji and special characters
|
|
49
80
|
message_str = message.to_s
|
|
50
81
|
message_str = message_str.force_encoding("UTF-8") if message_str.encoding.name == "ASCII-8BIT"
|
|
@@ -39,7 +39,7 @@ module Aidp
|
|
|
39
39
|
Aidp.log_info("metadata", "Compiling tool directory", directories: @directories, output: output_path)
|
|
40
40
|
|
|
41
41
|
# Scan all directories
|
|
42
|
-
scanner = Scanner.new(@directories)
|
|
42
|
+
scanner = Scanner.new(@directories, strict: @strict)
|
|
43
43
|
@tools = scanner.scan_all
|
|
44
44
|
|
|
45
45
|
# Validate tools
|
|
@@ -47,14 +47,24 @@ module Aidp
|
|
|
47
47
|
validation_results = validator.validate_all
|
|
48
48
|
|
|
49
49
|
# Handle validation failures
|
|
50
|
-
|
|
50
|
+
parse_error_results = scanner.parse_errors.map do |err|
|
|
51
|
+
Validator::ValidationResult.new(
|
|
52
|
+
tool_id: "(unknown)",
|
|
53
|
+
file_path: err[:file],
|
|
54
|
+
valid: false,
|
|
55
|
+
errors: [err[:error]],
|
|
56
|
+
warnings: []
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
invalid_results = handle_validation_results(validation_results + parse_error_results)
|
|
51
61
|
|
|
52
62
|
# Build indexes and graphs
|
|
53
63
|
build_indexes
|
|
54
64
|
build_dependency_graph
|
|
55
65
|
|
|
56
66
|
# Create directory structure
|
|
57
|
-
directory = create_directory_structure
|
|
67
|
+
directory = create_directory_structure(invalid_results: invalid_results)
|
|
58
68
|
|
|
59
69
|
# Write to file
|
|
60
70
|
write_directory(directory, output_path)
|
|
@@ -142,22 +152,23 @@ module Aidp
|
|
|
142
152
|
# Create directory structure for serialization
|
|
143
153
|
#
|
|
144
154
|
# @return [Hash] Directory structure
|
|
145
|
-
def create_directory_structure
|
|
155
|
+
def create_directory_structure(invalid_results: [])
|
|
146
156
|
{
|
|
147
|
-
version
|
|
148
|
-
compiled_at
|
|
149
|
-
tools
|
|
150
|
-
indexes
|
|
151
|
-
by_type
|
|
152
|
-
|
|
153
|
-
by_work_unit_type
|
|
157
|
+
"version" => "1.0.0",
|
|
158
|
+
"compiled_at" => Time.now.iso8601,
|
|
159
|
+
"tools" => @tools.map(&:to_h),
|
|
160
|
+
"indexes" => {
|
|
161
|
+
"by_type" => @indexes[:by_type].transform_values { |tools| tools.map(&:id) },
|
|
162
|
+
"by_tags" => @indexes[:by_tag].transform_values { |tools| tools.map(&:id) },
|
|
163
|
+
"by_work_unit_type" => @indexes[:by_work_unit_type].transform_values { |tools| tools.map(&:id) }
|
|
154
164
|
},
|
|
155
|
-
dependency_graph
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
"dependency_graph" => @dependency_graph,
|
|
166
|
+
"errors" => invalid_results.map { |res| {"tool_id" => res.tool_id, "errors" => res.errors, "warnings" => res.warnings} },
|
|
167
|
+
"statistics" => {
|
|
168
|
+
"total_tools" => @tools.size,
|
|
169
|
+
"by_type" => @tools.group_by(&:type).transform_values(&:size),
|
|
170
|
+
"total_tags" => @indexes[:by_tag].size,
|
|
171
|
+
"total_work_unit_types" => @indexes[:by_work_unit_type].size
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
174
|
end
|
|
@@ -223,6 +234,7 @@ module Aidp
|
|
|
223
234
|
warnings: result.warnings
|
|
224
235
|
)
|
|
225
236
|
end
|
|
237
|
+
invalid_results
|
|
226
238
|
end
|
|
227
239
|
end
|
|
228
240
|
end
|
data/lib/aidp/metadata/query.rb
CHANGED
|
@@ -64,7 +64,7 @@ module Aidp
|
|
|
64
64
|
Aidp.log_debug("metadata", "Finding by tags", tags: tags, match_all: match_all)
|
|
65
65
|
|
|
66
66
|
tags = Array(tags).map(&:downcase)
|
|
67
|
-
indexes = directory["indexes"]["
|
|
67
|
+
indexes = directory["indexes"]["by_tags"] || {}
|
|
68
68
|
|
|
69
69
|
if match_all
|
|
70
70
|
# Find tools that have ALL specified tags
|
|
@@ -17,16 +17,21 @@ module Aidp
|
|
|
17
17
|
# Initialize scanner with directory paths
|
|
18
18
|
#
|
|
19
19
|
# @param directories [Array<String>] Directories to scan
|
|
20
|
-
def initialize(directories = [])
|
|
20
|
+
def initialize(directories = [], strict: false)
|
|
21
21
|
@directories = Array(directories)
|
|
22
|
+
@strict = strict
|
|
23
|
+
@parse_errors = []
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
attr_reader :parse_errors
|
|
27
|
+
|
|
24
28
|
# Scan all configured directories
|
|
25
29
|
#
|
|
26
30
|
# @return [Array<ToolMetadata>] All discovered tool metadata
|
|
27
31
|
def scan_all
|
|
28
32
|
Aidp.log_debug("metadata", "Scanning directories", directories: @directories)
|
|
29
33
|
|
|
34
|
+
@parse_errors = []
|
|
30
35
|
all_tools = []
|
|
31
36
|
@directories.each do |dir|
|
|
32
37
|
tools = scan_directory(dir)
|
|
@@ -69,6 +74,8 @@ module Aidp
|
|
|
69
74
|
file: file_path,
|
|
70
75
|
error: e.message
|
|
71
76
|
)
|
|
77
|
+
@parse_errors << {file: file_path, error: e.message}
|
|
78
|
+
raise if @strict
|
|
72
79
|
end
|
|
73
80
|
|
|
74
81
|
Aidp.log_debug(
|
|
@@ -146,19 +146,19 @@ module Aidp
|
|
|
146
146
|
# @return [Hash] Metadata as hash (for JSON)
|
|
147
147
|
def to_h
|
|
148
148
|
{
|
|
149
|
-
type
|
|
150
|
-
id
|
|
151
|
-
title
|
|
152
|
-
summary
|
|
153
|
-
version
|
|
154
|
-
applies_to
|
|
155
|
-
work_unit_types
|
|
156
|
-
priority
|
|
157
|
-
capabilities
|
|
158
|
-
dependencies
|
|
159
|
-
experimental
|
|
160
|
-
source_path
|
|
161
|
-
file_hash
|
|
149
|
+
"type" => type,
|
|
150
|
+
"id" => id,
|
|
151
|
+
"title" => title,
|
|
152
|
+
"summary" => summary,
|
|
153
|
+
"version" => version,
|
|
154
|
+
"applies_to" => applies_to,
|
|
155
|
+
"work_unit_types" => work_unit_types,
|
|
156
|
+
"priority" => priority,
|
|
157
|
+
"capabilities" => capabilities,
|
|
158
|
+
"dependencies" => dependencies,
|
|
159
|
+
"experimental" => experimental,
|
|
160
|
+
"source_path" => source_path,
|
|
161
|
+
"file_hash" => file_hash
|
|
162
162
|
}
|
|
163
163
|
end
|
|
164
164
|
|
|
@@ -84,6 +84,10 @@ module Aidp
|
|
|
84
84
|
warnings << "Tool content is very short (#{tool.content.length} characters)"
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
unless valid_version_format?(tool.version)
|
|
88
|
+
warnings << "Version '#{tool.version}' is not in semver format (MAJOR.MINOR.PATCH)"
|
|
89
|
+
end
|
|
90
|
+
|
|
87
91
|
ValidationResult.new(
|
|
88
92
|
tool_id: tool.id,
|
|
89
93
|
file_path: tool.source_path,
|
|
@@ -156,6 +160,12 @@ module Aidp
|
|
|
156
160
|
# For now, this is a placeholder for future deprecation warnings
|
|
157
161
|
end
|
|
158
162
|
|
|
163
|
+
def valid_version_format?(version)
|
|
164
|
+
version.to_s.match?(/\A\d+\.\d+\.\d+\z/)
|
|
165
|
+
rescue
|
|
166
|
+
false
|
|
167
|
+
end
|
|
168
|
+
|
|
159
169
|
# Write validation errors to log file
|
|
160
170
|
#
|
|
161
171
|
# @param results [Array<ValidationResult>] Validation results
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aidp
|
|
4
|
+
# Namespace marker for metadata components
|
|
5
|
+
module Metadata
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Load key metadata components
|
|
10
|
+
require_relative "metadata/parser"
|
|
11
|
+
require_relative "metadata/tool_metadata"
|
|
12
|
+
require_relative "metadata/scanner"
|
|
13
|
+
require_relative "metadata/validator"
|
|
14
|
+
require_relative "metadata/compiler"
|
|
15
|
+
require_relative "metadata/cache"
|
|
16
|
+
require_relative "metadata/query"
|
|
@@ -3,15 +3,27 @@ require "fileutils"
|
|
|
3
3
|
require "shellwords"
|
|
4
4
|
|
|
5
5
|
module Aidp
|
|
6
|
+
# Simple shell command executor wrapper for testability
|
|
7
|
+
class ShellExecutor
|
|
8
|
+
def run(command)
|
|
9
|
+
`#{command}`
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def success?
|
|
13
|
+
$?.success?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
6
17
|
# Manages worktrees specifically for Pull Request branches
|
|
7
|
-
class
|
|
8
|
-
def initialize(base_repo_path: nil, project_dir: nil, worktree_registry_path: nil)
|
|
18
|
+
class PRWorktreeManager
|
|
19
|
+
def initialize(base_repo_path: nil, project_dir: nil, worktree_registry_path: nil, shell_executor: nil)
|
|
9
20
|
@base_repo_path = base_repo_path || project_dir || Dir.pwd
|
|
10
21
|
@project_dir = project_dir
|
|
11
22
|
@worktree_registry_path = worktree_registry_path || File.join(
|
|
12
23
|
project_dir || File.expand_path("~/.aidp"),
|
|
13
24
|
"pr_worktrees.json"
|
|
14
25
|
)
|
|
26
|
+
@shell_executor = shell_executor || ShellExecutor.new
|
|
15
27
|
FileUtils.mkdir_p(File.dirname(@worktree_registry_path))
|
|
16
28
|
@worktrees = load_registry
|
|
17
29
|
end
|
|
@@ -258,7 +270,7 @@ module Aidp
|
|
|
258
270
|
|
|
259
271
|
# Advanced change detection patterns
|
|
260
272
|
file_patterns = [
|
|
261
|
-
/(
|
|
273
|
+
/(modify|update|add|delete)\s+file:\s*([^\n]+)/i,
|
|
262
274
|
/\[(\w+)\]\s*([^\n]+)/, # GitHub-style change indicators
|
|
263
275
|
/(?:Action:\s*(\w+))\s*File:\s*([^\n]+)/i
|
|
264
276
|
]
|
|
@@ -427,7 +439,7 @@ module Aidp
|
|
|
427
439
|
|
|
428
440
|
Dir.chdir(worktree_path) do
|
|
429
441
|
# Check staged changes with more robust capture
|
|
430
|
-
staged_changes_output =
|
|
442
|
+
staged_changes_output = @shell_executor.run("git diff --staged --name-only").strip
|
|
431
443
|
|
|
432
444
|
if !staged_changes_output.empty?
|
|
433
445
|
push_result[:git_actions][:staged_changes] = true
|
|
@@ -436,16 +448,16 @@ module Aidp
|
|
|
436
448
|
# More robust commit command with additional logging
|
|
437
449
|
commit_message = "Changes applied via AIDP request-changes workflow for PR ##{pr_number}"
|
|
438
450
|
commit_command = "git commit -m '#{commit_message}' 2>&1"
|
|
439
|
-
commit_output =
|
|
451
|
+
commit_output = @shell_executor.run(commit_command).strip
|
|
440
452
|
|
|
441
|
-
if
|
|
453
|
+
if @shell_executor.success?
|
|
442
454
|
push_result[:git_actions][:committed] = true
|
|
443
455
|
|
|
444
456
|
# Enhanced push with verbose tracking
|
|
445
457
|
push_command = "git push origin #{head_branch} 2>&1"
|
|
446
|
-
push_output =
|
|
458
|
+
push_output = @shell_executor.run(push_command).strip
|
|
447
459
|
|
|
448
|
-
if
|
|
460
|
+
if @shell_executor.success?
|
|
449
461
|
push_result[:git_actions][:pushed] = true
|
|
450
462
|
push_result[:success] = true
|
|
451
463
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "tty-prompt"
|
|
4
|
-
require_relative "harness/provider_factory"
|
|
5
4
|
|
|
6
5
|
module Aidp
|
|
7
6
|
class ProviderManager
|
|
8
7
|
class << self
|
|
8
|
+
# Expose for testability
|
|
9
|
+
attr_accessor :harness_factory
|
|
10
|
+
|
|
9
11
|
def get_provider(provider_type, options = {})
|
|
10
12
|
factory = get_harness_factory
|
|
11
13
|
raise "Harness factory not available" unless factory
|
|
@@ -20,12 +22,7 @@ module Aidp
|
|
|
20
22
|
|
|
21
23
|
# Get harness factory instance
|
|
22
24
|
def get_harness_factory
|
|
23
|
-
@harness_factory ||=
|
|
24
|
-
require_relative "harness/config_manager"
|
|
25
|
-
Aidp::Harness::ProviderFactory.new
|
|
26
|
-
rescue LoadError
|
|
27
|
-
nil
|
|
28
|
-
end
|
|
25
|
+
@harness_factory ||= Aidp::Harness::ProviderFactory.new
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
# Create provider using harness configuration
|
data/lib/aidp/providers/base.rb
CHANGED