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
|
@@ -14,6 +14,9 @@ module Aidp
|
|
|
14
14
|
class TreeSitterScan
|
|
15
15
|
include Aidp::MessageDisplay
|
|
16
16
|
|
|
17
|
+
# Expose state for testability
|
|
18
|
+
attr_accessor :symbols, :imports, :calls, :metrics, :seams, :hotspots, :tests, :cycles
|
|
19
|
+
|
|
17
20
|
def initialize(root: Dir.pwd, kb_dir: ".aidp/kb", langs: %w[ruby], threads: Etc.nprocessors, prompt: TTY::Prompt.new)
|
|
18
21
|
@root = File.expand_path(root)
|
|
19
22
|
@kb_dir = File.expand_path(kb_dir, @root)
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "version_detector"
|
|
4
|
-
require_relative "checkpoint_store"
|
|
5
|
-
require_relative "update_logger"
|
|
6
|
-
require_relative "failure_tracker"
|
|
7
|
-
require_relative "update_policy"
|
|
8
|
-
require_relative "errors"
|
|
9
|
-
|
|
10
3
|
module Aidp
|
|
11
4
|
module AutoUpdate
|
|
12
5
|
# Facade for orchestrating the complete auto-update workflow
|
|
@@ -188,8 +181,105 @@ module Aidp
|
|
|
188
181
|
}
|
|
189
182
|
end
|
|
190
183
|
|
|
184
|
+
# Perform hot code reload without restarting the process
|
|
185
|
+
#
|
|
186
|
+
# This method performs a git pull and then uses Zeitwerk to reload
|
|
187
|
+
# all Ruby classes. Unlike initiate_update (which exits with code 75),
|
|
188
|
+
# this method keeps the process running.
|
|
189
|
+
#
|
|
190
|
+
# @param update_check [UpdateCheck] Update check result
|
|
191
|
+
# @return [Boolean] Whether reload was successful
|
|
192
|
+
# @raise [UpdateError] If updates are disabled or reload fails
|
|
193
|
+
def hot_reload_update(update_check = nil)
|
|
194
|
+
raise UpdateError, "Updates disabled by configuration" unless @policy.enabled
|
|
195
|
+
|
|
196
|
+
# Verify Zeitwerk loader is set up for reloading
|
|
197
|
+
unless Aidp::Loader.setup? && Aidp::Loader.reloading?
|
|
198
|
+
Aidp.log_warn("auto_update_coordinator", "hot_reload_not_available",
|
|
199
|
+
reason: "Zeitwerk loader not configured for reloading")
|
|
200
|
+
raise UpdateError, "Hot reloading not available. Loader must be configured with enable_reloading: true"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
update_check ||= check_for_update
|
|
204
|
+
return false unless update_check.should_update?
|
|
205
|
+
|
|
206
|
+
from_version = update_check.current_version
|
|
207
|
+
to_version = update_check.available_version
|
|
208
|
+
|
|
209
|
+
Aidp.log_info("auto_update_coordinator", "hot_reload_starting",
|
|
210
|
+
from_version: from_version,
|
|
211
|
+
to_version: to_version)
|
|
212
|
+
|
|
213
|
+
# Perform git pull
|
|
214
|
+
unless perform_git_pull
|
|
215
|
+
raise UpdateError, "Git pull failed"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Reload all classes using Zeitwerk
|
|
219
|
+
unless Aidp::Loader.reload!
|
|
220
|
+
@failure_tracker.record_failure
|
|
221
|
+
@update_logger.log_failure("Zeitwerk reload failed")
|
|
222
|
+
raise UpdateError, "Zeitwerk reload failed"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Log success
|
|
226
|
+
@update_logger.log_success(
|
|
227
|
+
from_version: from_version,
|
|
228
|
+
to_version: to_version
|
|
229
|
+
)
|
|
230
|
+
@failure_tracker.reset_on_success
|
|
231
|
+
|
|
232
|
+
Aidp.log_info("auto_update_coordinator", "hot_reload_complete",
|
|
233
|
+
from_version: from_version,
|
|
234
|
+
to_version: to_version)
|
|
235
|
+
|
|
236
|
+
true
|
|
237
|
+
rescue UpdateError
|
|
238
|
+
raise
|
|
239
|
+
rescue => e
|
|
240
|
+
@failure_tracker.record_failure
|
|
241
|
+
@update_logger.log_failure("Hot reload failed: #{e.message}")
|
|
242
|
+
Aidp.log_error("auto_update_coordinator", "hot_reload_failed",
|
|
243
|
+
error: e.message)
|
|
244
|
+
raise UpdateError, "Hot reload failed: #{e.message}"
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Check if hot reloading is available
|
|
248
|
+
# @return [Boolean]
|
|
249
|
+
def hot_reload_available?
|
|
250
|
+
Aidp::Loader.setup? && Aidp::Loader.reloading?
|
|
251
|
+
end
|
|
252
|
+
|
|
191
253
|
private
|
|
192
254
|
|
|
255
|
+
# Perform git pull to fetch latest code
|
|
256
|
+
# @return [Boolean] Whether pull was successful
|
|
257
|
+
def perform_git_pull
|
|
258
|
+
require "open3"
|
|
259
|
+
|
|
260
|
+
Aidp.log_debug("auto_update_coordinator", "git_pull_starting",
|
|
261
|
+
project_dir: @project_dir)
|
|
262
|
+
|
|
263
|
+
Dir.chdir(@project_dir) do
|
|
264
|
+
stdout, stderr, status = Open3.capture3("git", "pull", "--ff-only")
|
|
265
|
+
|
|
266
|
+
if status.success?
|
|
267
|
+
Aidp.log_info("auto_update_coordinator", "git_pull_success",
|
|
268
|
+
output: stdout.strip)
|
|
269
|
+
true
|
|
270
|
+
else
|
|
271
|
+
Aidp.log_error("auto_update_coordinator", "git_pull_failed",
|
|
272
|
+
stderr: stderr.strip,
|
|
273
|
+
exit_code: status.exitstatus)
|
|
274
|
+
false
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
rescue => e
|
|
278
|
+
Aidp.log_error("auto_update_coordinator", "git_pull_error",
|
|
279
|
+
error: e.message)
|
|
280
|
+
false
|
|
281
|
+
end
|
|
282
|
+
|
|
193
283
|
def build_checkpoint(current_state, target_version)
|
|
194
284
|
Checkpoint.new(
|
|
195
285
|
mode: current_state[:mode] || "watch",
|
data/lib/aidp/auto_update.rb
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "auto_update/errors"
|
|
4
|
-
require_relative "auto_update/update_policy"
|
|
5
|
-
require_relative "auto_update/update_check"
|
|
6
|
-
require_relative "auto_update/checkpoint"
|
|
7
|
-
require_relative "auto_update/bundler_adapter"
|
|
8
|
-
require_relative "auto_update/rubygems_api_adapter"
|
|
9
|
-
require_relative "auto_update/version_detector"
|
|
10
|
-
require_relative "auto_update/checkpoint_store"
|
|
11
|
-
require_relative "auto_update/update_logger"
|
|
12
|
-
require_relative "auto_update/failure_tracker"
|
|
13
|
-
require_relative "auto_update/coordinator"
|
|
14
|
-
|
|
15
3
|
module Aidp
|
|
16
4
|
# Auto-update functionality for Aidp in devcontainers
|
|
17
5
|
module AutoUpdate
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../setup/devcontainer/parser"
|
|
4
|
-
require_relative "../setup/devcontainer/generator"
|
|
5
|
-
require_relative "../setup/devcontainer/port_manager"
|
|
6
|
-
require_relative "../setup/devcontainer/backup_manager"
|
|
7
|
-
require_relative "../message_display"
|
|
8
3
|
require "json"
|
|
9
4
|
require "yaml"
|
|
10
5
|
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-prompt"
|
|
4
|
+
require "tty-table"
|
|
5
|
+
require_relative "../evaluations"
|
|
6
|
+
require_relative "../message_display"
|
|
7
|
+
|
|
8
|
+
module Aidp
|
|
9
|
+
class CLI
|
|
10
|
+
# Command handler for `aidp eval` subcommand
|
|
11
|
+
#
|
|
12
|
+
# Provides commands for managing evaluations:
|
|
13
|
+
# - list: List recent evaluations
|
|
14
|
+
# - view <id>: View details of a specific evaluation
|
|
15
|
+
# - stats: Show evaluation statistics
|
|
16
|
+
# - add <rating>: Add a new evaluation
|
|
17
|
+
# - clear: Clear all evaluation data
|
|
18
|
+
#
|
|
19
|
+
# Usage:
|
|
20
|
+
# aidp eval list
|
|
21
|
+
# aidp eval list --rating good
|
|
22
|
+
# aidp eval view eval_20241115_123456_abc1
|
|
23
|
+
# aidp eval stats
|
|
24
|
+
# aidp eval add good "Great output"
|
|
25
|
+
# aidp eval clear --force
|
|
26
|
+
class EvalCommand
|
|
27
|
+
include Aidp::MessageDisplay
|
|
28
|
+
|
|
29
|
+
def initialize(prompt: TTY::Prompt.new, storage: nil, project_dir: nil)
|
|
30
|
+
@prompt = prompt
|
|
31
|
+
@project_dir = project_dir || Dir.pwd
|
|
32
|
+
@storage = storage || Aidp::Evaluations::EvaluationStorage.new(project_dir: @project_dir)
|
|
33
|
+
|
|
34
|
+
Aidp.log_debug("eval_command", "initialize", project_dir: @project_dir)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Main entry point for eval subcommands
|
|
38
|
+
def run(args)
|
|
39
|
+
sub = args.shift || "list"
|
|
40
|
+
|
|
41
|
+
case sub
|
|
42
|
+
when "list"
|
|
43
|
+
run_list_command(args)
|
|
44
|
+
when "view"
|
|
45
|
+
run_view_command(args)
|
|
46
|
+
when "stats"
|
|
47
|
+
run_stats_command
|
|
48
|
+
when "add"
|
|
49
|
+
run_add_command(args)
|
|
50
|
+
when "watch"
|
|
51
|
+
run_watch_command(args)
|
|
52
|
+
when "clear"
|
|
53
|
+
run_clear_command(args)
|
|
54
|
+
else
|
|
55
|
+
display_usage
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def run_list_command(args)
|
|
62
|
+
Aidp.log_debug("eval_command", "list", args: args)
|
|
63
|
+
|
|
64
|
+
options = parse_list_options(args)
|
|
65
|
+
evaluations = @storage.list(
|
|
66
|
+
limit: options[:limit],
|
|
67
|
+
rating: options[:rating],
|
|
68
|
+
target_type: options[:target_type]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if evaluations.empty?
|
|
72
|
+
display_message("No evaluations found.", type: :info)
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
display_evaluations_table(evaluations)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def run_view_command(args)
|
|
80
|
+
id = args.shift
|
|
81
|
+
unless id
|
|
82
|
+
display_message("Error: Please provide an evaluation ID", type: :error)
|
|
83
|
+
display_message("Usage: aidp eval view <evaluation_id>", type: :info)
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
Aidp.log_debug("eval_command", "view", id: id)
|
|
88
|
+
|
|
89
|
+
record = @storage.load(id)
|
|
90
|
+
unless record
|
|
91
|
+
display_message("Evaluation not found: #{id}", type: :error)
|
|
92
|
+
return
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
display_evaluation_details(record)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def run_stats_command
|
|
99
|
+
Aidp.log_debug("eval_command", "stats")
|
|
100
|
+
|
|
101
|
+
stats = @storage.stats
|
|
102
|
+
|
|
103
|
+
display_message("", type: :info)
|
|
104
|
+
display_message("Evaluation Statistics", type: :highlight)
|
|
105
|
+
display_message("=" * 50, type: :muted)
|
|
106
|
+
display_message("", type: :info)
|
|
107
|
+
|
|
108
|
+
display_message("Total evaluations: #{stats[:total]}", type: :info)
|
|
109
|
+
display_message("", type: :info)
|
|
110
|
+
|
|
111
|
+
display_message("By rating:", type: :info)
|
|
112
|
+
display_rating_bar(" Good", stats[:by_rating][:good], stats[:total], :green)
|
|
113
|
+
display_rating_bar(" Neutral", stats[:by_rating][:neutral], stats[:total], :yellow)
|
|
114
|
+
display_rating_bar(" Bad", stats[:by_rating][:bad], stats[:total], :red)
|
|
115
|
+
|
|
116
|
+
if stats[:by_target_type]&.any?
|
|
117
|
+
display_message("", type: :info)
|
|
118
|
+
display_message("By target type:", type: :info)
|
|
119
|
+
stats[:by_target_type].each do |type, count|
|
|
120
|
+
display_message(" #{type || "unspecified"}: #{count}", type: :muted)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if stats[:first_evaluation]
|
|
125
|
+
display_message("", type: :info)
|
|
126
|
+
display_message("First evaluation: #{format_timestamp(stats[:first_evaluation])}", type: :muted)
|
|
127
|
+
display_message("Last evaluation: #{format_timestamp(stats[:last_evaluation])}", type: :muted)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
display_message("", type: :info)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def run_add_command(args)
|
|
134
|
+
# Check for watch mode options
|
|
135
|
+
watch_opts = extract_watch_options(args)
|
|
136
|
+
|
|
137
|
+
rating = args.shift
|
|
138
|
+
comment = args.join(" ").strip
|
|
139
|
+
comment = nil if comment.empty?
|
|
140
|
+
|
|
141
|
+
unless rating
|
|
142
|
+
display_message("Error: Please provide a rating (good, neutral, or bad)", type: :error)
|
|
143
|
+
display_message("Usage: aidp eval add <rating> [comment]", type: :info)
|
|
144
|
+
display_message(" aidp eval add --watch <type> <repo> <number> <rating> [comment]", type: :info)
|
|
145
|
+
return
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
Aidp.log_debug("eval_command", "add", rating: rating, has_comment: !comment.nil?, watch: watch_opts)
|
|
149
|
+
|
|
150
|
+
begin
|
|
151
|
+
context_capture = Aidp::Evaluations::ContextCapture.new(project_dir: @project_dir)
|
|
152
|
+
|
|
153
|
+
if watch_opts[:enabled]
|
|
154
|
+
context = context_capture.capture_watch(
|
|
155
|
+
repo: watch_opts[:repo],
|
|
156
|
+
number: watch_opts[:number],
|
|
157
|
+
processor_type: watch_opts[:type]
|
|
158
|
+
)
|
|
159
|
+
target_type = watch_opts[:type]
|
|
160
|
+
target_id = "#{watch_opts[:repo]}##{watch_opts[:number]}"
|
|
161
|
+
else
|
|
162
|
+
context = context_capture.capture_minimal
|
|
163
|
+
target_type = nil
|
|
164
|
+
target_id = nil
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
record = Aidp::Evaluations::EvaluationRecord.new(
|
|
168
|
+
rating: rating,
|
|
169
|
+
comment: comment,
|
|
170
|
+
target_type: target_type,
|
|
171
|
+
target_id: target_id,
|
|
172
|
+
context: context
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
result = @storage.store(record)
|
|
176
|
+
|
|
177
|
+
if result[:success]
|
|
178
|
+
display_message("Evaluation recorded: #{record.id}", type: :success)
|
|
179
|
+
display_message(" Rating: #{rating_with_emoji(record.rating)}", type: :info)
|
|
180
|
+
display_message(" Target: #{target_type} (#{target_id})", type: :info) if target_type
|
|
181
|
+
display_message(" Comment: #{record.comment}", type: :muted) if record.comment
|
|
182
|
+
else
|
|
183
|
+
display_message("Failed to store evaluation: #{result[:error]}", type: :error)
|
|
184
|
+
end
|
|
185
|
+
rescue ArgumentError => e
|
|
186
|
+
display_message("Error: #{e.message}", type: :error)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def run_watch_command(args)
|
|
191
|
+
# aidp eval watch <plan|review|build|ci_fix|change_request> <repo> <number> <rating> [comment]
|
|
192
|
+
processor_type = args.shift
|
|
193
|
+
repo = args.shift
|
|
194
|
+
number = args.shift&.to_i
|
|
195
|
+
rating = args.shift
|
|
196
|
+
comment = args.join(" ").strip
|
|
197
|
+
comment = nil if comment.empty?
|
|
198
|
+
|
|
199
|
+
unless processor_type && repo && number && rating
|
|
200
|
+
display_message("Error: Missing required arguments", type: :error)
|
|
201
|
+
display_message("Usage: aidp eval watch <type> <repo> <number> <rating> [comment]", type: :info)
|
|
202
|
+
display_message("", type: :info)
|
|
203
|
+
display_message("Types: plan, review, build, ci_fix, change_request", type: :info)
|
|
204
|
+
display_message("Example: aidp eval watch plan owner/repo 123 good \"Clear plan\"", type: :muted)
|
|
205
|
+
return
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
Aidp.log_debug("eval_command", "watch",
|
|
209
|
+
processor_type: processor_type, repo: repo, number: number, rating: rating)
|
|
210
|
+
|
|
211
|
+
begin
|
|
212
|
+
context_capture = Aidp::Evaluations::ContextCapture.new(project_dir: @project_dir)
|
|
213
|
+
context = context_capture.capture_watch(
|
|
214
|
+
repo: repo,
|
|
215
|
+
number: number,
|
|
216
|
+
processor_type: processor_type
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
record = Aidp::Evaluations::EvaluationRecord.new(
|
|
220
|
+
rating: rating,
|
|
221
|
+
comment: comment,
|
|
222
|
+
target_type: processor_type,
|
|
223
|
+
target_id: "#{repo}##{number}",
|
|
224
|
+
context: context
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
result = @storage.store(record)
|
|
228
|
+
|
|
229
|
+
if result[:success]
|
|
230
|
+
display_message("Watch evaluation recorded: #{record.id}", type: :success)
|
|
231
|
+
display_message(" Rating: #{rating_with_emoji(record.rating)}", type: :info)
|
|
232
|
+
display_message(" Type: #{processor_type}", type: :info)
|
|
233
|
+
display_message(" Target: #{repo}##{number}", type: :info)
|
|
234
|
+
display_message(" Comment: #{record.comment}", type: :muted) if record.comment
|
|
235
|
+
else
|
|
236
|
+
display_message("Failed to store evaluation: #{result[:error]}", type: :error)
|
|
237
|
+
end
|
|
238
|
+
rescue ArgumentError => e
|
|
239
|
+
display_message("Error: #{e.message}", type: :error)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def run_clear_command(args)
|
|
244
|
+
force = args.include?("--force")
|
|
245
|
+
|
|
246
|
+
unless force
|
|
247
|
+
confirm = @prompt.yes?("Are you sure you want to clear all evaluation data?")
|
|
248
|
+
return unless confirm
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
Aidp.log_debug("eval_command", "clear", force: force)
|
|
252
|
+
|
|
253
|
+
result = @storage.clear
|
|
254
|
+
|
|
255
|
+
if result[:success]
|
|
256
|
+
display_message("Cleared #{result[:count]} evaluation(s).", type: :success)
|
|
257
|
+
else
|
|
258
|
+
display_message("Failed to clear evaluations: #{result[:error]}", type: :error)
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def parse_list_options(args)
|
|
263
|
+
options = {limit: 20, rating: nil, target_type: nil}
|
|
264
|
+
|
|
265
|
+
args.each_with_index do |arg, i|
|
|
266
|
+
case arg
|
|
267
|
+
when "--limit", "-n"
|
|
268
|
+
options[:limit] = args[i + 1].to_i if args[i + 1]
|
|
269
|
+
when "--rating", "-r"
|
|
270
|
+
options[:rating] = args[i + 1] if args[i + 1]
|
|
271
|
+
when "--type", "-t"
|
|
272
|
+
options[:target_type] = args[i + 1] if args[i + 1]
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
options
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def extract_watch_options(args)
|
|
280
|
+
options = {enabled: false, type: nil, repo: nil, number: nil}
|
|
281
|
+
|
|
282
|
+
watch_idx = args.index("--watch")
|
|
283
|
+
return options unless watch_idx
|
|
284
|
+
|
|
285
|
+
# Remove --watch and extract following arguments
|
|
286
|
+
args.delete_at(watch_idx)
|
|
287
|
+
|
|
288
|
+
# Expect: --watch <type> <repo> <number>
|
|
289
|
+
if args[watch_idx] && args[watch_idx + 1] && args[watch_idx + 2]
|
|
290
|
+
options[:enabled] = true
|
|
291
|
+
options[:type] = args.delete_at(watch_idx)
|
|
292
|
+
options[:repo] = args.delete_at(watch_idx)
|
|
293
|
+
options[:number] = args.delete_at(watch_idx).to_i
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
options
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def display_evaluations_table(evaluations)
|
|
300
|
+
header = ["ID", "Rating", "Target", "Comment", "Created"]
|
|
301
|
+
|
|
302
|
+
rows = evaluations.map do |eval|
|
|
303
|
+
[
|
|
304
|
+
truncate(eval.id, 25),
|
|
305
|
+
rating_with_emoji(eval.rating),
|
|
306
|
+
eval.target_type || "-",
|
|
307
|
+
truncate(eval.comment || "-", 30),
|
|
308
|
+
format_timestamp(eval.created_at)
|
|
309
|
+
]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
table = TTY::Table.new(header: header, rows: rows)
|
|
313
|
+
@prompt.say(table.render(:unicode, padding: [0, 1]))
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def display_evaluation_details(record)
|
|
317
|
+
display_message("", type: :info)
|
|
318
|
+
display_message("Evaluation Details", type: :highlight)
|
|
319
|
+
display_message("=" * 50, type: :muted)
|
|
320
|
+
display_message("", type: :info)
|
|
321
|
+
|
|
322
|
+
display_message("ID: #{record.id}", type: :info)
|
|
323
|
+
display_message("Rating: #{rating_with_emoji(record.rating)}", type: :info)
|
|
324
|
+
display_message("Comment: #{record.comment || "(none)"}", type: :info)
|
|
325
|
+
display_message("Target Type: #{record.target_type || "(none)"}", type: :info)
|
|
326
|
+
display_message("Target ID: #{record.target_id || "(none)"}", type: :info)
|
|
327
|
+
display_message("Created: #{format_timestamp(record.created_at)}", type: :info)
|
|
328
|
+
|
|
329
|
+
if record.context&.any?
|
|
330
|
+
display_message("", type: :info)
|
|
331
|
+
display_message("Context:", type: :highlight)
|
|
332
|
+
display_context(record.context)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
display_message("", type: :info)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def display_context(context, indent: 2)
|
|
339
|
+
prefix = " " * indent
|
|
340
|
+
context.each do |key, value|
|
|
341
|
+
if value.is_a?(Hash)
|
|
342
|
+
display_message("#{prefix}#{key}:", type: :muted)
|
|
343
|
+
display_context(value, indent: indent + 2)
|
|
344
|
+
elsif value.is_a?(Array)
|
|
345
|
+
display_message("#{prefix}#{key}: #{value.join(", ")}", type: :muted)
|
|
346
|
+
else
|
|
347
|
+
display_message("#{prefix}#{key}: #{value}", type: :muted)
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def display_rating_bar(label, count, total, color)
|
|
353
|
+
percentage = (total > 0) ? (count.to_f / total * 100).round(1) : 0
|
|
354
|
+
bar_width = (total > 0) ? (count.to_f / total * 20).round : 0
|
|
355
|
+
bar = "#" * bar_width + "-" * (20 - bar_width)
|
|
356
|
+
|
|
357
|
+
display_message("#{label}: [#{bar}] #{count} (#{percentage}%)", type: :info)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def rating_with_emoji(rating)
|
|
361
|
+
case rating
|
|
362
|
+
when "good" then "good (+)"
|
|
363
|
+
when "neutral" then "neutral (~)"
|
|
364
|
+
when "bad" then "bad (-)"
|
|
365
|
+
else rating
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def truncate(str, max_length)
|
|
370
|
+
return str if str.nil? || str.length <= max_length
|
|
371
|
+
str[0, max_length - 3] + "..."
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def format_timestamp(timestamp)
|
|
375
|
+
return "-" unless timestamp
|
|
376
|
+
Time.parse(timestamp).strftime("%Y-%m-%d %H:%M")
|
|
377
|
+
rescue
|
|
378
|
+
timestamp.to_s[0, 16]
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def display_usage
|
|
382
|
+
display_message("Usage: aidp eval <list|view|stats|add|watch|clear>", type: :info)
|
|
383
|
+
display_message("", type: :info)
|
|
384
|
+
display_message("Commands:", type: :info)
|
|
385
|
+
display_message(" list [options] - List recent evaluations", type: :info)
|
|
386
|
+
display_message(" --limit, -n <N> - Limit results (default: 20)", type: :muted)
|
|
387
|
+
display_message(" --rating, -r <rating> - Filter by rating", type: :muted)
|
|
388
|
+
display_message(" --type, -t <type> - Filter by target type", type: :muted)
|
|
389
|
+
display_message(" view <id> - View evaluation details", type: :info)
|
|
390
|
+
display_message(" stats - Show evaluation statistics", type: :info)
|
|
391
|
+
display_message(" add <rating> [comment] - Add a new evaluation", type: :info)
|
|
392
|
+
display_message(" watch <type> <repo> <number> <rating> [comment]", type: :info)
|
|
393
|
+
display_message(" - Rate a watch mode output", type: :info)
|
|
394
|
+
display_message(" Types: plan, review, build, ci_fix, change_request", type: :muted)
|
|
395
|
+
display_message(" clear [--force] - Clear all evaluation data", type: :info)
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
end
|
|
@@ -58,7 +58,7 @@ module Aidp
|
|
|
58
58
|
|
|
59
59
|
# Build a runner to access state manager
|
|
60
60
|
runner = @runner_class.new(@project_dir, mode.to_sym, {})
|
|
61
|
-
state_manager = runner.
|
|
61
|
+
state_manager = runner.state_manager
|
|
62
62
|
state_manager.reset_all if state_manager.respond_to?(:reset_all)
|
|
63
63
|
display_message("✅ Reset harness state for #{mode} mode", type: :success)
|
|
64
64
|
end
|