aidp 0.16.0 → 0.17.1
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/error_handler.rb +32 -13
- data/lib/aidp/analyze/kb_inspector.rb +2 -3
- data/lib/aidp/analyze/progress.rb +6 -11
- data/lib/aidp/cli/mcp_dashboard.rb +1 -1
- data/lib/aidp/cli.rb +300 -33
- data/lib/aidp/config.rb +1 -1
- data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
- data/lib/aidp/execute/checkpoint.rb +1 -1
- data/lib/aidp/execute/future_work_backlog.rb +1 -1
- data/lib/aidp/execute/progress.rb +6 -9
- data/lib/aidp/execute/repl_macros.rb +79 -10
- data/lib/aidp/harness/config_loader.rb +2 -2
- data/lib/aidp/harness/config_validator.rb +1 -1
- data/lib/aidp/harness/enhanced_runner.rb +16 -7
- data/lib/aidp/harness/error_handler.rb +12 -5
- data/lib/aidp/harness/provider_manager.rb +4 -19
- data/lib/aidp/harness/runner.rb +2 -2
- data/lib/aidp/harness/state/persistence.rb +9 -10
- data/lib/aidp/harness/state/workflow_state.rb +3 -2
- data/lib/aidp/harness/state_manager.rb +33 -97
- data/lib/aidp/harness/status_display.rb +22 -12
- data/lib/aidp/harness/ui/enhanced_tui.rb +3 -4
- data/lib/aidp/harness/user_interface.rb +11 -6
- data/lib/aidp/jobs/background_runner.rb +8 -2
- data/lib/aidp/logger.rb +1 -1
- data/lib/aidp/message_display.rb +9 -2
- data/lib/aidp/providers/anthropic.rb +1 -1
- data/lib/aidp/providers/base.rb +4 -4
- data/lib/aidp/providers/codex.rb +1 -1
- data/lib/aidp/providers/cursor.rb +1 -1
- data/lib/aidp/providers/gemini.rb +1 -1
- data/lib/aidp/providers/github_copilot.rb +1 -1
- data/lib/aidp/providers/macos_ui.rb +1 -1
- data/lib/aidp/providers/opencode.rb +1 -1
- data/lib/aidp/skills/registry.rb +31 -29
- data/lib/aidp/skills/router.rb +178 -0
- data/lib/aidp/skills/wizard/builder.rb +141 -0
- data/lib/aidp/skills/wizard/controller.rb +145 -0
- data/lib/aidp/skills/wizard/differ.rb +232 -0
- data/lib/aidp/skills/wizard/prompter.rb +317 -0
- data/lib/aidp/skills/wizard/template_library.rb +164 -0
- data/lib/aidp/skills/wizard/writer.rb +105 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/plan_generator.rb +1 -1
- data/lib/aidp/watch/repository_client.rb +13 -9
- data/lib/aidp/workflows/guided_agent.rb +2 -312
- data/lib/aidp/workstream_executor.rb +8 -2
- data/templates/skills/README.md +334 -0
- data/templates/skills/architecture_analyst/SKILL.md +173 -0
- data/templates/skills/product_strategist/SKILL.md +141 -0
- data/templates/skills/repository_analyst/SKILL.md +117 -0
- data/templates/skills/test_analyzer/SKILL.md +213 -0
- metadata +13 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 949670fdea4721406643f4fd8da096e943740effbd836407311b812350919b14
|
|
4
|
+
data.tar.gz: 8034a3c798571e4626b273c4a9abe9a5da38cef5dd4f25de8794a0ef63bf6022
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cdabc6ba04d06a89dbc4ad19b27a64862e960daa350029db4fada5a4f6d32d3fba27cb32dda291871a79ad8f319d27e7dd1348ddcb7fdb8b0f0a1390b9493fc1
|
|
7
|
+
data.tar.gz: 39c384f4c01bf00bef550ac9190d27ddd268437be92fc4cdfcdeac3dff09c79c055b2e5b5582e553af6fa838b3f904cce354e9db62ee3638f658eec05673e4f5
|
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
require "logger"
|
|
4
4
|
require_relative "../concurrency"
|
|
5
5
|
|
|
6
|
+
begin
|
|
7
|
+
require "net/http"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
# Net::HTTP might not be available in all environments
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
begin
|
|
13
|
+
require "sqlite3"
|
|
14
|
+
rescue LoadError
|
|
15
|
+
# SQLite3 might not be available in all environments
|
|
16
|
+
end
|
|
17
|
+
|
|
6
18
|
module Aidp
|
|
7
19
|
module Analyze
|
|
8
20
|
# Comprehensive error handling system for analyze mode
|
|
@@ -24,7 +36,7 @@ module Aidp
|
|
|
24
36
|
context: context,
|
|
25
37
|
step: step,
|
|
26
38
|
retry_count: retry_count,
|
|
27
|
-
timestamp: Time.
|
|
39
|
+
timestamp: Time.now
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
log_error(error_info)
|
|
@@ -59,7 +71,7 @@ module Aidp
|
|
|
59
71
|
{
|
|
60
72
|
status: "skipped",
|
|
61
73
|
reason: error.message,
|
|
62
|
-
timestamp: Time.
|
|
74
|
+
timestamp: Time.now
|
|
63
75
|
}
|
|
64
76
|
end
|
|
65
77
|
|
|
@@ -88,8 +100,8 @@ module Aidp
|
|
|
88
100
|
|
|
89
101
|
def setup_logger(log_file, verbose)
|
|
90
102
|
output_stream = log_file || @output || $stdout
|
|
91
|
-
logger = Logger.new(output_stream)
|
|
92
|
-
logger.level = verbose ? Logger::DEBUG : Logger::INFO
|
|
103
|
+
logger = ::Logger.new(output_stream)
|
|
104
|
+
logger.level = verbose ? ::Logger::DEBUG : ::Logger::INFO
|
|
93
105
|
logger.formatter = proc do |severity, datetime, progname, msg|
|
|
94
106
|
"#{datetime.strftime("%Y-%m-%d %H:%M:%S")} [#{severity}] #{msg}\n"
|
|
95
107
|
end
|
|
@@ -97,19 +109,28 @@ module Aidp
|
|
|
97
109
|
end
|
|
98
110
|
|
|
99
111
|
def setup_recovery_strategies
|
|
100
|
-
{
|
|
101
|
-
Net::TimeoutError => :retry_with_backoff,
|
|
102
|
-
Net::HTTPError => :retry_with_backoff,
|
|
103
|
-
SocketError => :retry_with_backoff,
|
|
112
|
+
strategies = {
|
|
104
113
|
Errno::ENOENT => :skip_step_with_warning,
|
|
105
114
|
Errno::EACCES => :skip_step_with_warning,
|
|
106
115
|
Errno::ENOSPC => :critical_error,
|
|
107
|
-
SQLite3::BusyException => :retry_with_backoff,
|
|
108
|
-
SQLite3::CorruptException => :critical_error,
|
|
109
116
|
AnalysisTimeoutError => :chunk_and_retry,
|
|
110
117
|
AnalysisDataError => :continue_with_partial_data,
|
|
111
118
|
AnalysisToolError => :log_and_continue
|
|
112
119
|
}
|
|
120
|
+
|
|
121
|
+
# Add network error classes if available
|
|
122
|
+
strategies[Net::TimeoutError] = :retry_with_backoff if defined?(Net::TimeoutError)
|
|
123
|
+
|
|
124
|
+
strategies[Net::HTTPError] = :retry_with_backoff if defined?(Net::HTTPError)
|
|
125
|
+
|
|
126
|
+
strategies[SocketError] = :retry_with_backoff if defined?(SocketError)
|
|
127
|
+
|
|
128
|
+
# Add SQLite error classes if available
|
|
129
|
+
strategies[SQLite3::BusyException] = :retry_with_backoff if defined?(SQLite3::BusyException)
|
|
130
|
+
|
|
131
|
+
strategies[SQLite3::CorruptException] = :critical_error if defined?(SQLite3::CorruptException)
|
|
132
|
+
|
|
133
|
+
strategies
|
|
113
134
|
end
|
|
114
135
|
|
|
115
136
|
def log_error(error_info)
|
|
@@ -250,9 +271,7 @@ module Aidp
|
|
|
250
271
|
tool_name = context[:tool_name] || "analysis tool"
|
|
251
272
|
error_msg = "#{tool_name} failed: #{error.message}"
|
|
252
273
|
|
|
253
|
-
if context[:installation_guide]
|
|
254
|
-
error_msg += "\n\nTo install #{tool_name}:\n#{context[:installation_guide]}"
|
|
255
|
-
end
|
|
274
|
+
error_msg += "\n\nTo install #{tool_name}:\n#{context[:installation_guide]}" if context[:installation_guide]
|
|
256
275
|
|
|
257
276
|
raise AnalysisToolError.new(error_msg)
|
|
258
277
|
end
|
|
@@ -80,7 +80,7 @@ module Aidp
|
|
|
80
80
|
display_message(box)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def load_kb_data
|
|
83
|
+
def load_kb_data(suppress_parse_warnings: false)
|
|
84
84
|
data = {}
|
|
85
85
|
|
|
86
86
|
%w[symbols imports calls metrics seams hotspots tests cycles].each do |type|
|
|
@@ -89,8 +89,7 @@ module Aidp
|
|
|
89
89
|
begin
|
|
90
90
|
data[type.to_sym] = JSON.parse(File.read(file_path), symbolize_names: true)
|
|
91
91
|
rescue JSON::ParserError => e
|
|
92
|
-
|
|
93
|
-
unless ENV["RACK_ENV"] == "test" || defined?(RSpec)
|
|
92
|
+
unless suppress_parse_warnings
|
|
94
93
|
display_message("Warning: Could not parse #{file_path}: #{e.message}", type: :warn)
|
|
95
94
|
end
|
|
96
95
|
data[type.to_sym] = []
|
|
@@ -9,9 +9,10 @@ module Aidp
|
|
|
9
9
|
class Progress
|
|
10
10
|
attr_reader :project_dir, :progress_file
|
|
11
11
|
|
|
12
|
-
def initialize(project_dir)
|
|
12
|
+
def initialize(project_dir, skip_persistence: false)
|
|
13
13
|
@project_dir = project_dir
|
|
14
14
|
@progress_file = File.join(project_dir, ".aidp", "progress", "analyze.yml")
|
|
15
|
+
@skip_persistence = skip_persistence
|
|
15
16
|
load_progress
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -60,26 +61,20 @@ module Aidp
|
|
|
60
61
|
private
|
|
61
62
|
|
|
62
63
|
def load_progress
|
|
63
|
-
|
|
64
|
-
if (ENV["RACK_ENV"] == "test" || defined?(RSpec)) && !File.exist?(@progress_file)
|
|
64
|
+
if @skip_persistence && !File.exist?(@progress_file)
|
|
65
65
|
@progress = {}
|
|
66
66
|
return
|
|
67
67
|
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
YAML.load_file(@progress_file) || {}
|
|
68
|
+
@progress = if !@skip_persistence && File.exist?(@progress_file)
|
|
69
|
+
YAML.safe_load_file(@progress_file, permitted_classes: [Date, Time, Symbol], aliases: true) || {}
|
|
71
70
|
else
|
|
72
71
|
{}
|
|
73
72
|
end
|
|
74
|
-
|
|
75
|
-
# Ensure @progress is never nil
|
|
76
73
|
@progress = {} if @progress.nil?
|
|
77
74
|
end
|
|
78
75
|
|
|
79
76
|
def save_progress
|
|
80
|
-
|
|
81
|
-
return if ENV["RACK_ENV"] == "test" || defined?(RSpec)
|
|
82
|
-
|
|
77
|
+
return if @skip_persistence
|
|
83
78
|
FileUtils.mkdir_p(File.dirname(@progress_file))
|
|
84
79
|
File.write(@progress_file, @progress.to_yaml)
|
|
85
80
|
end
|
data/lib/aidp/cli.rb
CHANGED
|
@@ -147,6 +147,10 @@ module Aidp
|
|
|
147
147
|
class << self
|
|
148
148
|
extend Aidp::MessageDisplay::ClassMethods
|
|
149
149
|
|
|
150
|
+
def create_prompt
|
|
151
|
+
::TTY::Prompt.new
|
|
152
|
+
end
|
|
153
|
+
|
|
150
154
|
def run(args = ARGV)
|
|
151
155
|
# Handle subcommands first (status, jobs, kb, harness)
|
|
152
156
|
return run_subcommand(args) if subcommand?(args)
|
|
@@ -173,7 +177,7 @@ module Aidp
|
|
|
173
177
|
|
|
174
178
|
# Handle configuration setup
|
|
175
179
|
# Create a prompt for the wizard
|
|
176
|
-
prompt =
|
|
180
|
+
prompt = create_prompt
|
|
177
181
|
|
|
178
182
|
if options[:setup_config]
|
|
179
183
|
# Force setup/reconfigure even if config exists
|
|
@@ -241,7 +245,7 @@ module Aidp
|
|
|
241
245
|
|
|
242
246
|
if File.exist?(config_path)
|
|
243
247
|
require "yaml"
|
|
244
|
-
full_config = YAML.
|
|
248
|
+
full_config = YAML.safe_load_file(config_path, permitted_classes: [Date, Time, Symbol], aliases: true)
|
|
245
249
|
logging_config = full_config["logging"] || full_config[:logging] || {}
|
|
246
250
|
end
|
|
247
251
|
|
|
@@ -398,7 +402,7 @@ module Aidp
|
|
|
398
402
|
|
|
399
403
|
def run_jobs_command(args = [])
|
|
400
404
|
require_relative "cli/jobs_command"
|
|
401
|
-
jobs_cmd = Aidp::CLI::JobsCommand.new(prompt:
|
|
405
|
+
jobs_cmd = Aidp::CLI::JobsCommand.new(prompt: create_prompt)
|
|
402
406
|
subcommand = args.shift
|
|
403
407
|
jobs_cmd.run(subcommand, args)
|
|
404
408
|
end
|
|
@@ -504,21 +508,6 @@ module Aidp
|
|
|
504
508
|
if step
|
|
505
509
|
display_message("Running #{mode} step '#{step}' with enhanced TUI harness", type: :highlight)
|
|
506
510
|
display_message("progress indicators", type: :info)
|
|
507
|
-
if step.start_with?("00_PRD") && (defined?(RSpec) || ENV["RSPEC_RUNNING"])
|
|
508
|
-
# Simulate questions & completion similar to TUI test mode
|
|
509
|
-
root = ENV["AIDP_ROOT"] || Dir.pwd
|
|
510
|
-
file = Dir.glob(File.join(root, "templates", (mode == :execute) ? "EXECUTE" : "ANALYZE", "00_PRD*.md")).first
|
|
511
|
-
if file && File.file?(file)
|
|
512
|
-
content = File.read(file)
|
|
513
|
-
questions_section = content.split(/## Questions/i)[1]
|
|
514
|
-
if questions_section
|
|
515
|
-
questions_section.lines.select { |l| l.strip.start_with?("-") }.each do |line|
|
|
516
|
-
display_message(line.strip.sub(/^-\s*/, ""), type: :info)
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
end
|
|
520
|
-
display_message("PRD completed", type: :success)
|
|
521
|
-
end
|
|
522
511
|
return
|
|
523
512
|
end
|
|
524
513
|
display_message("Starting enhanced TUI harness", type: :highlight)
|
|
@@ -601,7 +590,7 @@ module Aidp
|
|
|
601
590
|
when "clear"
|
|
602
591
|
force = args.include?("--force")
|
|
603
592
|
unless force
|
|
604
|
-
prompt =
|
|
593
|
+
prompt = create_prompt
|
|
605
594
|
confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
|
|
606
595
|
return unless confirm
|
|
607
596
|
end
|
|
@@ -696,7 +685,7 @@ module Aidp
|
|
|
696
685
|
end
|
|
697
686
|
end
|
|
698
687
|
config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
|
|
699
|
-
pm = Aidp::Harness::ProviderManager.new(config_manager, prompt:
|
|
688
|
+
pm = Aidp::Harness::ProviderManager.new(config_manager, prompt: create_prompt)
|
|
700
689
|
|
|
701
690
|
# Use TTY::Spinner for progress indication
|
|
702
691
|
require "tty-spinner"
|
|
@@ -738,7 +727,12 @@ module Aidp
|
|
|
738
727
|
end
|
|
739
728
|
tokens = (r[:total_tokens].to_i > 0) ? r[:total_tokens].to_s : "0"
|
|
740
729
|
reason = r[:unhealthy_reason] || "-"
|
|
741
|
-
|
|
730
|
+
is_tty = begin
|
|
731
|
+
$stdout.respond_to?(:tty?) && $stdout.tty?
|
|
732
|
+
rescue
|
|
733
|
+
false
|
|
734
|
+
end
|
|
735
|
+
if no_color || !is_tty
|
|
742
736
|
[r[:provider], r[:status], (r[:available] ? "yes" : "no"), cb, rl, tokens, last_used, reason]
|
|
743
737
|
else
|
|
744
738
|
[
|
|
@@ -757,7 +751,7 @@ module Aidp
|
|
|
757
751
|
table = TTY::Table.new header, table_rows
|
|
758
752
|
display_message(table.render(:basic), type: :info)
|
|
759
753
|
rescue => e
|
|
760
|
-
|
|
754
|
+
Aidp.logger.warn("cli", "Failed to display provider health", error_class: e.class.name, error_message: e.message)
|
|
761
755
|
display_message("Failed to display provider health: #{e.message}", type: :error)
|
|
762
756
|
end
|
|
763
757
|
|
|
@@ -777,7 +771,7 @@ module Aidp
|
|
|
777
771
|
display_message("=" * 60, type: :muted)
|
|
778
772
|
|
|
779
773
|
provider_info = Aidp::Harness::ProviderInfo.new(provider_name, Dir.pwd)
|
|
780
|
-
info = provider_info.
|
|
774
|
+
info = provider_info.info(force_refresh: force_refresh)
|
|
781
775
|
|
|
782
776
|
if info.nil?
|
|
783
777
|
display_message("No information available for provider: #{provider_name}", type: :error)
|
|
@@ -1044,7 +1038,7 @@ module Aidp
|
|
|
1044
1038
|
return
|
|
1045
1039
|
end
|
|
1046
1040
|
|
|
1047
|
-
wizard = Aidp::Setup::Wizard.new(Dir.pwd, prompt:
|
|
1041
|
+
wizard = Aidp::Setup::Wizard.new(Dir.pwd, prompt: create_prompt, dry_run: dry_run)
|
|
1048
1042
|
wizard.run
|
|
1049
1043
|
end
|
|
1050
1044
|
|
|
@@ -1071,7 +1065,7 @@ module Aidp
|
|
|
1071
1065
|
end
|
|
1072
1066
|
|
|
1073
1067
|
require_relative "init/runner"
|
|
1074
|
-
runner = Aidp::Init::Runner.new(Dir.pwd, prompt:
|
|
1068
|
+
runner = Aidp::Init::Runner.new(Dir.pwd, prompt: create_prompt, options: options)
|
|
1075
1069
|
runner.run
|
|
1076
1070
|
end
|
|
1077
1071
|
|
|
@@ -1127,7 +1121,7 @@ module Aidp
|
|
|
1127
1121
|
project_dir: Dir.pwd,
|
|
1128
1122
|
once: once,
|
|
1129
1123
|
use_workstreams: use_workstreams,
|
|
1130
|
-
prompt:
|
|
1124
|
+
prompt: create_prompt
|
|
1131
1125
|
)
|
|
1132
1126
|
runner.start
|
|
1133
1127
|
rescue ArgumentError => e
|
|
@@ -1244,7 +1238,7 @@ module Aidp
|
|
|
1244
1238
|
|
|
1245
1239
|
# Confirm removal unless --force
|
|
1246
1240
|
unless force
|
|
1247
|
-
prompt =
|
|
1241
|
+
prompt = create_prompt
|
|
1248
1242
|
confirm = prompt.yes?("Remove workstream '#{slug}'?#{" (will also delete branch)" if delete_branch}")
|
|
1249
1243
|
return unless confirm
|
|
1250
1244
|
end
|
|
@@ -1722,10 +1716,10 @@ module Aidp
|
|
|
1722
1716
|
|
|
1723
1717
|
by_source = registry.by_source
|
|
1724
1718
|
|
|
1725
|
-
if by_source[:
|
|
1726
|
-
display_message("
|
|
1719
|
+
if by_source[:template].any?
|
|
1720
|
+
display_message("Template Skills", type: :highlight)
|
|
1727
1721
|
display_message("=" * 80, type: :muted)
|
|
1728
|
-
table_rows = by_source[:
|
|
1722
|
+
table_rows = by_source[:template].map do |skill_id|
|
|
1729
1723
|
skill = registry.find(skill_id)
|
|
1730
1724
|
[skill_id, skill.version, skill.description[0, 60]]
|
|
1731
1725
|
end
|
|
@@ -1735,10 +1729,10 @@ module Aidp
|
|
|
1735
1729
|
display_message("", type: :info)
|
|
1736
1730
|
end
|
|
1737
1731
|
|
|
1738
|
-
if by_source[:
|
|
1739
|
-
display_message("
|
|
1732
|
+
if by_source[:project].any?
|
|
1733
|
+
display_message("Project Skills", type: :highlight)
|
|
1740
1734
|
display_message("=" * 80, type: :muted)
|
|
1741
|
-
table_rows = by_source[:
|
|
1735
|
+
table_rows = by_source[:project].map do |skill_id|
|
|
1742
1736
|
skill = registry.find(skill_id)
|
|
1743
1737
|
[skill_id, skill.version, skill.description[0, 60]]
|
|
1744
1738
|
end
|
|
@@ -1846,6 +1840,204 @@ module Aidp
|
|
|
1846
1840
|
display_message("Failed to search skills: #{e.message}", type: :error)
|
|
1847
1841
|
end
|
|
1848
1842
|
|
|
1843
|
+
when "preview"
|
|
1844
|
+
# Preview full skill content
|
|
1845
|
+
skill_id = args.shift
|
|
1846
|
+
|
|
1847
|
+
unless skill_id
|
|
1848
|
+
display_message("Usage: aidp skill preview <skill-id>", type: :info)
|
|
1849
|
+
return
|
|
1850
|
+
end
|
|
1851
|
+
|
|
1852
|
+
begin
|
|
1853
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
|
1854
|
+
registry.load_skills
|
|
1855
|
+
|
|
1856
|
+
skill = registry.find(skill_id)
|
|
1857
|
+
|
|
1858
|
+
unless skill
|
|
1859
|
+
display_message("Skill not found: #{skill_id}", type: :error)
|
|
1860
|
+
display_message("Use 'aidp skill list' to see available skills", type: :muted)
|
|
1861
|
+
return
|
|
1862
|
+
end
|
|
1863
|
+
|
|
1864
|
+
require_relative "skills/wizard/builder"
|
|
1865
|
+
require_relative "skills/wizard/template_library"
|
|
1866
|
+
|
|
1867
|
+
builder = Aidp::Skills::Wizard::Builder.new
|
|
1868
|
+
full_content = builder.to_skill_md(skill)
|
|
1869
|
+
|
|
1870
|
+
# Check if this is a project skill with a matching template
|
|
1871
|
+
source_info = registry.by_source[skill_id]
|
|
1872
|
+
inheritance_info = ""
|
|
1873
|
+
if source_info == :project
|
|
1874
|
+
template_library = Aidp::Skills::Wizard::TemplateLibrary.new(project_dir: Dir.pwd)
|
|
1875
|
+
template_skill = template_library.templates.find { |s| s.id == skill.id }
|
|
1876
|
+
if template_skill
|
|
1877
|
+
inheritance_info = " (inherits from template)"
|
|
1878
|
+
end
|
|
1879
|
+
elsif source_info == :template
|
|
1880
|
+
inheritance_info = " (template)"
|
|
1881
|
+
end
|
|
1882
|
+
|
|
1883
|
+
display_message("\n" + "=" * 60, type: :info)
|
|
1884
|
+
display_message("Skill: #{skill.name} (#{skill.id}) v#{skill.version}#{inheritance_info}", type: :highlight)
|
|
1885
|
+
display_message("=" * 60 + "\n", type: :info)
|
|
1886
|
+
display_message(full_content, type: :info)
|
|
1887
|
+
display_message("\n" + "=" * 60, type: :info)
|
|
1888
|
+
rescue => e
|
|
1889
|
+
display_message("Failed to preview skill: #{e.message}", type: :error)
|
|
1890
|
+
end
|
|
1891
|
+
|
|
1892
|
+
when "diff"
|
|
1893
|
+
# Show diff between project skill and template
|
|
1894
|
+
skill_id = args.shift
|
|
1895
|
+
|
|
1896
|
+
unless skill_id
|
|
1897
|
+
display_message("Usage: aidp skill diff <skill-id>", type: :info)
|
|
1898
|
+
return
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1901
|
+
begin
|
|
1902
|
+
require_relative "skills/wizard/template_library"
|
|
1903
|
+
require_relative "skills/wizard/differ"
|
|
1904
|
+
|
|
1905
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
|
1906
|
+
registry.load_skills
|
|
1907
|
+
|
|
1908
|
+
project_skill = registry.find(skill_id)
|
|
1909
|
+
|
|
1910
|
+
unless project_skill
|
|
1911
|
+
display_message("Skill not found: #{skill_id}", type: :error)
|
|
1912
|
+
return
|
|
1913
|
+
end
|
|
1914
|
+
|
|
1915
|
+
# Check if it's a project skill
|
|
1916
|
+
unless registry.by_source[:project].include?(skill_id)
|
|
1917
|
+
display_message("Skill '#{skill_id}' is a template skill, not a project skill", type: :info)
|
|
1918
|
+
display_message("Only project skills can be diffed against templates", type: :muted)
|
|
1919
|
+
return
|
|
1920
|
+
end
|
|
1921
|
+
|
|
1922
|
+
# Find the template
|
|
1923
|
+
template_library = Aidp::Skills::Wizard::TemplateLibrary.new(project_dir: Dir.pwd)
|
|
1924
|
+
template_skill = template_library.find(skill_id)
|
|
1925
|
+
|
|
1926
|
+
unless template_skill
|
|
1927
|
+
display_message("No template found for skill '#{skill_id}'", type: :info)
|
|
1928
|
+
display_message("This is a custom skill without a template base", type: :muted)
|
|
1929
|
+
return
|
|
1930
|
+
end
|
|
1931
|
+
|
|
1932
|
+
# Show diff
|
|
1933
|
+
differ = Aidp::Skills::Wizard::Differ.new
|
|
1934
|
+
diff_result = differ.diff(template_skill, project_skill)
|
|
1935
|
+
differ.display(diff_result)
|
|
1936
|
+
rescue => e
|
|
1937
|
+
display_message("Failed to diff skill: #{e.message}", type: :error)
|
|
1938
|
+
end
|
|
1939
|
+
|
|
1940
|
+
when "edit"
|
|
1941
|
+
# Edit an existing skill
|
|
1942
|
+
skill_id = args.shift
|
|
1943
|
+
|
|
1944
|
+
unless skill_id
|
|
1945
|
+
display_message("Usage: aidp skill edit <skill-id>", type: :info)
|
|
1946
|
+
return
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
begin
|
|
1950
|
+
require_relative "skills/wizard/controller"
|
|
1951
|
+
|
|
1952
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
|
1953
|
+
registry.load_skills
|
|
1954
|
+
|
|
1955
|
+
skill = registry.find(skill_id)
|
|
1956
|
+
|
|
1957
|
+
unless skill
|
|
1958
|
+
display_message("Skill not found: #{skill_id}", type: :error)
|
|
1959
|
+
display_message("Use 'aidp skill list' to see available skills", type: :muted)
|
|
1960
|
+
return
|
|
1961
|
+
end
|
|
1962
|
+
|
|
1963
|
+
# Check if it's editable (must be project skill or willing to copy template)
|
|
1964
|
+
if registry.by_source[:template].include?(skill_id)
|
|
1965
|
+
display_message("'#{skill_id}' is a template skill", type: :info)
|
|
1966
|
+
display_message("Editing will create a project override in .aidp/skills/", type: :muted)
|
|
1967
|
+
end
|
|
1968
|
+
|
|
1969
|
+
# Parse options
|
|
1970
|
+
options = {}
|
|
1971
|
+
while args.first&.start_with?("--")
|
|
1972
|
+
opt = args.shift
|
|
1973
|
+
case opt
|
|
1974
|
+
when "--dry-run"
|
|
1975
|
+
options[:dry_run] = true
|
|
1976
|
+
when "--open-editor"
|
|
1977
|
+
options[:open_editor] = true
|
|
1978
|
+
else
|
|
1979
|
+
display_message("Unknown option: #{opt}", type: :error)
|
|
1980
|
+
return
|
|
1981
|
+
end
|
|
1982
|
+
end
|
|
1983
|
+
|
|
1984
|
+
# Pre-fill wizard with existing skill data
|
|
1985
|
+
options[:id] = skill.id
|
|
1986
|
+
options[:name] = skill.name
|
|
1987
|
+
options[:edit_mode] = true
|
|
1988
|
+
options[:existing_skill] = skill
|
|
1989
|
+
|
|
1990
|
+
# Run wizard in edit mode
|
|
1991
|
+
wizard = Aidp::Skills::Wizard::Controller.new(
|
|
1992
|
+
project_dir: Dir.pwd,
|
|
1993
|
+
options: options
|
|
1994
|
+
)
|
|
1995
|
+
wizard.run
|
|
1996
|
+
rescue => e
|
|
1997
|
+
display_message("Failed to edit skill: #{e.message}", type: :error)
|
|
1998
|
+
end
|
|
1999
|
+
|
|
2000
|
+
when "new"
|
|
2001
|
+
# Create a new skill using the wizard
|
|
2002
|
+
begin
|
|
2003
|
+
require_relative "skills/wizard/controller"
|
|
2004
|
+
|
|
2005
|
+
# Parse options
|
|
2006
|
+
options = {}
|
|
2007
|
+
while args.first&.start_with?("--")
|
|
2008
|
+
opt = args.shift
|
|
2009
|
+
case opt
|
|
2010
|
+
when "--minimal"
|
|
2011
|
+
options[:minimal] = true
|
|
2012
|
+
when "--dry-run"
|
|
2013
|
+
options[:dry_run] = true
|
|
2014
|
+
when "--yes", "-y"
|
|
2015
|
+
options[:yes] = true
|
|
2016
|
+
when "--id"
|
|
2017
|
+
options[:id] = args.shift
|
|
2018
|
+
when "--name"
|
|
2019
|
+
options[:name] = args.shift
|
|
2020
|
+
when "--from-template"
|
|
2021
|
+
options[:from_template] = args.shift
|
|
2022
|
+
when "--clone"
|
|
2023
|
+
options[:clone] = args.shift
|
|
2024
|
+
else
|
|
2025
|
+
display_message("Unknown option: #{opt}", type: :error)
|
|
2026
|
+
return
|
|
2027
|
+
end
|
|
2028
|
+
end
|
|
2029
|
+
|
|
2030
|
+
# Run wizard
|
|
2031
|
+
wizard = Aidp::Skills::Wizard::Controller.new(
|
|
2032
|
+
project_dir: Dir.pwd,
|
|
2033
|
+
options: options
|
|
2034
|
+
)
|
|
2035
|
+
wizard.run
|
|
2036
|
+
rescue => e
|
|
2037
|
+
display_message("Failed to create skill: #{e.message}", type: :error)
|
|
2038
|
+
Aidp.log_error("cli", "Skill wizard failed", error: e.message, backtrace: e.backtrace.first(5))
|
|
2039
|
+
end
|
|
2040
|
+
|
|
1849
2041
|
when "validate"
|
|
1850
2042
|
# Validate skill file format
|
|
1851
2043
|
skill_path = args.shift
|
|
@@ -1893,19 +2085,94 @@ module Aidp
|
|
|
1893
2085
|
end
|
|
1894
2086
|
end
|
|
1895
2087
|
|
|
2088
|
+
when "delete"
|
|
2089
|
+
# Delete a project skill
|
|
2090
|
+
skill_id = args.shift
|
|
2091
|
+
|
|
2092
|
+
unless skill_id
|
|
2093
|
+
display_message("Usage: aidp skill delete <skill-id>", type: :info)
|
|
2094
|
+
return
|
|
2095
|
+
end
|
|
2096
|
+
|
|
2097
|
+
begin
|
|
2098
|
+
registry = Aidp::Skills::Registry.new(project_dir: Dir.pwd)
|
|
2099
|
+
registry.load_skills
|
|
2100
|
+
|
|
2101
|
+
skill = registry.find(skill_id)
|
|
2102
|
+
|
|
2103
|
+
unless skill
|
|
2104
|
+
display_message("Skill not found: #{skill_id}", type: :error)
|
|
2105
|
+
return
|
|
2106
|
+
end
|
|
2107
|
+
|
|
2108
|
+
# Check if it's a project skill
|
|
2109
|
+
source = registry.by_source[skill_id]
|
|
2110
|
+
unless source == :project
|
|
2111
|
+
display_message("Cannot delete template skill '#{skill_id}'", type: :error)
|
|
2112
|
+
display_message("Only project skills in .aidp/skills/ can be deleted", type: :muted)
|
|
2113
|
+
return
|
|
2114
|
+
end
|
|
2115
|
+
|
|
2116
|
+
# Get skill directory
|
|
2117
|
+
skill_dir = File.dirname(skill.source_path)
|
|
2118
|
+
|
|
2119
|
+
# Confirm deletion
|
|
2120
|
+
require "tty-prompt"
|
|
2121
|
+
prompt = create_prompt
|
|
2122
|
+
confirmed = prompt.yes?("Delete skill '#{skill.name}' (#{skill_id})? This cannot be undone.")
|
|
2123
|
+
|
|
2124
|
+
unless confirmed
|
|
2125
|
+
display_message("Deletion cancelled", type: :info)
|
|
2126
|
+
return
|
|
2127
|
+
end
|
|
2128
|
+
|
|
2129
|
+
# Delete the skill directory
|
|
2130
|
+
require "fileutils"
|
|
2131
|
+
FileUtils.rm_rf(skill_dir)
|
|
2132
|
+
|
|
2133
|
+
display_message("✓ Deleted skill: #{skill.name} (#{skill_id})", type: :success)
|
|
2134
|
+
rescue => e
|
|
2135
|
+
display_message("Failed to delete skill: #{e.message}", type: :error)
|
|
2136
|
+
Aidp.log_error("cli", "Skill deletion failed", error: e.message, backtrace: e.backtrace.first(5))
|
|
2137
|
+
end
|
|
2138
|
+
|
|
1896
2139
|
else
|
|
1897
2140
|
display_message("Usage: aidp skill <command>", type: :info)
|
|
1898
2141
|
display_message("", type: :info)
|
|
1899
2142
|
display_message("Commands:", type: :info)
|
|
1900
2143
|
display_message(" list List all available skills (default)", type: :info)
|
|
1901
2144
|
display_message(" show <id> Show detailed skill information", type: :info)
|
|
2145
|
+
display_message(" preview <id> Preview full SKILL.md content", type: :info)
|
|
2146
|
+
display_message(" diff <id> Show diff between project skill and template", type: :info)
|
|
1902
2147
|
display_message(" search <query> Search skills by keyword", type: :info)
|
|
2148
|
+
display_message(" new [options] Create a new skill using the wizard", type: :info)
|
|
2149
|
+
display_message(" edit <id> [options] Edit an existing skill", type: :info)
|
|
2150
|
+
display_message(" delete <id> Delete a project skill", type: :info)
|
|
1903
2151
|
display_message(" validate [path] Validate skill file format", type: :info)
|
|
1904
2152
|
display_message("", type: :info)
|
|
2153
|
+
display_message("New Skill Options:", type: :info)
|
|
2154
|
+
display_message(" --minimal Skip optional sections", type: :info)
|
|
2155
|
+
display_message(" --dry-run Preview without saving", type: :info)
|
|
2156
|
+
display_message(" --yes, -y Skip confirmation prompts", type: :info)
|
|
2157
|
+
display_message(" --id <skill_id> Pre-set skill ID", type: :info)
|
|
2158
|
+
display_message(" --name <name> Pre-set skill name", type: :info)
|
|
2159
|
+
display_message("", type: :info)
|
|
2160
|
+
display_message("Edit Skill Options:", type: :info)
|
|
2161
|
+
display_message(" --dry-run Preview changes without saving", type: :info)
|
|
2162
|
+
display_message(" --open-editor Open content in $EDITOR", type: :info)
|
|
2163
|
+
display_message("", type: :info)
|
|
1905
2164
|
display_message("Examples:", type: :info)
|
|
1906
2165
|
display_message(" aidp skill list # List all skills", type: :info)
|
|
1907
2166
|
display_message(" aidp skill show repository_analyst # Show skill details", type: :info)
|
|
2167
|
+
display_message(" aidp skill preview repository_analyst # Preview full content", type: :info)
|
|
2168
|
+
display_message(" aidp skill diff my_skill # Show diff with template", type: :info)
|
|
1908
2169
|
display_message(" aidp skill search git # Search for git-related skills", type: :info)
|
|
2170
|
+
display_message(" aidp skill new # Create new skill (interactive)", type: :info)
|
|
2171
|
+
display_message(" aidp skill new --minimal --id my_skill # Create with minimal prompts", type: :info)
|
|
2172
|
+
display_message(" aidp skill new --from-template repo_analyst # Inherit from template", type: :info)
|
|
2173
|
+
display_message(" aidp skill new --clone my_existing_skill # Clone existing skill", type: :info)
|
|
2174
|
+
display_message(" aidp skill edit repository_analyst # Edit existing skill", type: :info)
|
|
2175
|
+
display_message(" aidp skill delete my_custom_skill # Delete a project skill", type: :info)
|
|
1909
2176
|
display_message(" aidp skill validate skills/my_skill/SKILL.md # Validate specific skill", type: :info)
|
|
1910
2177
|
display_message(" aidp skill validate # Validate all skills", type: :info)
|
|
1911
2178
|
end
|
data/lib/aidp/config.rb
CHANGED
|
@@ -312,7 +312,7 @@ module Aidp
|
|
|
312
312
|
end
|
|
313
313
|
|
|
314
314
|
private_class_method def self.load_yaml_config(config_file)
|
|
315
|
-
YAML.
|
|
315
|
+
YAML.safe_load_file(config_file, permitted_classes: [Date, Time, Symbol], aliases: true) || {}
|
|
316
316
|
rescue => e
|
|
317
317
|
warn "Failed to load configuration file #{config_file}: #{e.message}"
|
|
318
318
|
{}
|
|
@@ -26,6 +26,7 @@ module Aidp
|
|
|
26
26
|
@provider_manager = provider_manager
|
|
27
27
|
@config = config
|
|
28
28
|
@options = options
|
|
29
|
+
@cancel_timeout = options[:cancel_timeout] || 5 # seconds to wait for graceful shutdown
|
|
29
30
|
@state = WorkLoopState.new
|
|
30
31
|
@instruction_queue = InstructionQueue.new
|
|
31
32
|
@work_thread = nil
|
|
@@ -92,7 +93,7 @@ module Aidp
|
|
|
92
93
|
@state.append_output("Cancellation requested, waiting for safe stopping point...", type: :warning)
|
|
93
94
|
|
|
94
95
|
# Wait for thread to notice cancellation
|
|
95
|
-
@work_thread&.join(
|
|
96
|
+
@work_thread&.join(@cancel_timeout)
|
|
96
97
|
|
|
97
98
|
if save_checkpoint && @sync_runner
|
|
98
99
|
@state.append_output("Saving checkpoint before exit...", type: :info)
|