aidp 0.34.1 → 0.39.3

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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -14
  3. data/lib/aidp/agent_harness_adapter.rb +309 -0
  4. data/lib/aidp/cli/issue_importer.rb +20 -6
  5. data/lib/aidp/cli/jobs_command.rb +1 -1
  6. data/lib/aidp/cli/models_command.rb +9 -16
  7. data/lib/aidp/cli/prompts_command.rb +253 -0
  8. data/lib/aidp/cli/security_command.rb +4 -10
  9. data/lib/aidp/cli/storage_command.rb +277 -0
  10. data/lib/aidp/cli/temporal_command.rb +460 -0
  11. data/lib/aidp/cli.rb +24 -2
  12. data/lib/aidp/config.rb +15 -0
  13. data/lib/aidp/config_paths.rb +12 -2
  14. data/lib/aidp/database/migrations.rb +114 -0
  15. data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
  16. data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
  17. data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
  18. data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
  19. data/lib/aidp/database/repositories/job_repository.rb +278 -0
  20. data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
  21. data/lib/aidp/database/repositories/progress_repository.rb +210 -0
  22. data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
  23. data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
  24. data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
  25. data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
  26. data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
  27. data/lib/aidp/database/repositories/task_repository.rb +231 -0
  28. data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
  29. data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
  30. data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
  31. data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
  32. data/lib/aidp/database/repository.rb +166 -0
  33. data/lib/aidp/database/schema.rb +347 -0
  34. data/lib/aidp/database/storage_migrator.rb +693 -0
  35. data/lib/aidp/database.rb +139 -0
  36. data/lib/aidp/debug_mixin.rb +2 -2
  37. data/lib/aidp/execute/checkpoint.rb +26 -0
  38. data/lib/aidp/execute/checkpoint_display.rb +16 -0
  39. data/lib/aidp/execute/prompt_evaluator.rb +30 -57
  40. data/lib/aidp/execute/work_loop_runner.rb +294 -46
  41. data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
  42. data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
  43. data/lib/aidp/harness/ai_decision_engine.rb +78 -296
  44. data/lib/aidp/harness/config_schema.rb +59 -0
  45. data/lib/aidp/harness/configuration.rb +229 -10
  46. data/lib/aidp/harness/deprecation_cache.rb +53 -11
  47. data/lib/aidp/harness/enhanced_runner.rb +1 -1
  48. data/lib/aidp/harness/error_handler.rb +11 -5
  49. data/lib/aidp/harness/provider_config.rb +36 -0
  50. data/lib/aidp/harness/provider_factory.rb +26 -24
  51. data/lib/aidp/harness/provider_info.rb +9 -5
  52. data/lib/aidp/harness/provider_manager.rb +2 -2
  53. data/lib/aidp/harness/provider_metrics.rb +103 -1
  54. data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
  55. data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
  56. data/lib/aidp/harness/runner.rb +7 -2
  57. data/lib/aidp/harness/state_manager.rb +1 -1
  58. data/lib/aidp/harness/test_runner.rb +167 -11
  59. data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
  60. data/lib/aidp/harness/usage_limit.rb +179 -0
  61. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  62. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  63. data/lib/aidp/harness/usage_period.rb +234 -0
  64. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  65. data/lib/aidp/init/runner.rb +25 -4
  66. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  67. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  68. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  69. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  70. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  71. data/lib/aidp/logger.rb +25 -7
  72. data/lib/aidp/orchestration_adapter.rb +279 -0
  73. data/lib/aidp/pr_worktree_manager.rb +4 -15
  74. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  75. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  76. data/lib/aidp/prompts/template_evolver.rb +305 -0
  77. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  78. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  79. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  80. data/lib/aidp/setup/wizard.rb +616 -176
  81. data/lib/aidp/shell_executor.rb +58 -0
  82. data/lib/aidp/style_guide/selector.rb +56 -1
  83. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  84. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  85. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  86. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  87. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  88. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  89. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  90. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  91. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  92. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  93. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  94. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  95. data/lib/aidp/temporal/configuration.rb +169 -0
  96. data/lib/aidp/temporal/connection.rb +93 -0
  97. data/lib/aidp/temporal/worker.rb +119 -0
  98. data/lib/aidp/temporal/workflow_client.rb +171 -0
  99. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  100. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  101. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  102. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  103. data/lib/aidp/temporal.rb +120 -0
  104. data/lib/aidp/version.rb +1 -1
  105. data/lib/aidp/watch/base_processor.rb +35 -0
  106. data/lib/aidp/watch/build_processor.rb +132 -0
  107. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  108. data/lib/aidp/watch/rebase_processor.rb +245 -0
  109. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  110. data/lib/aidp/watch/runner.rb +366 -285
  111. data/lib/aidp/watch/state_store.rb +80 -0
  112. data/lib/aidp/watch/work_item.rb +96 -0
  113. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  114. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  115. data/lib/aidp/workflows/guided_agent.rb +1 -1
  116. data/lib/aidp/worktree.rb +62 -0
  117. data/templates/aidp.yml.example +70 -0
  118. metadata +117 -16
  119. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  120. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  121. data/lib/aidp/providers/adapter.rb +0 -239
  122. data/lib/aidp/providers/aider.rb +0 -264
  123. data/lib/aidp/providers/anthropic.rb +0 -614
  124. data/lib/aidp/providers/base.rb +0 -617
  125. data/lib/aidp/providers/capability_registry.rb +0 -204
  126. data/lib/aidp/providers/codex.rb +0 -267
  127. data/lib/aidp/providers/cursor.rb +0 -304
  128. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  129. data/lib/aidp/providers/gemini.rb +0 -110
  130. data/lib/aidp/providers/github_copilot.rb +0 -227
  131. data/lib/aidp/providers/kilocode.rb +0 -157
  132. data/lib/aidp/providers/opencode.rb +0 -139
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tty-table"
5
+ require_relative "../prompts/prompt_template_manager"
6
+ require_relative "../message_display"
7
+
8
+ module Aidp
9
+ class CLI
10
+ # Command handler for `aidp prompts` subcommand
11
+ #
12
+ # Provides commands for managing prompt templates:
13
+ # - List all available templates
14
+ # - Show template details
15
+ # - Customize templates for project
16
+ # - Reset customized templates
17
+ #
18
+ # Usage:
19
+ # aidp prompts list
20
+ # aidp prompts show <template_id>
21
+ # aidp prompts customize <template_id>
22
+ # aidp prompts reset <template_id>
23
+ class PromptsCommand
24
+ include Aidp::MessageDisplay
25
+
26
+ def initialize(prompt: TTY::Prompt.new, project_dir: Dir.pwd)
27
+ @prompt = prompt
28
+ @project_dir = project_dir
29
+ @template_manager = Prompts::PromptTemplateManager.new(project_dir: project_dir)
30
+ end
31
+
32
+ # Main entry point for prompts command
33
+ def run(args)
34
+ subcommand = args.shift
35
+
36
+ case subcommand
37
+ when "list", "ls"
38
+ list_templates(args)
39
+ when "show", "view"
40
+ show_template(args)
41
+ when "customize", "edit"
42
+ customize_template(args)
43
+ when "reset"
44
+ reset_template(args)
45
+ when "-h", "--help", nil
46
+ display_usage
47
+ else
48
+ display_message("Unknown subcommand: #{subcommand}", type: :error)
49
+ display_usage
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def list_templates(args)
56
+ category_filter = nil
57
+
58
+ # Parse options
59
+ until args.empty?
60
+ token = args.shift
61
+ case token
62
+ when "--category", "-c"
63
+ category_filter = args.shift
64
+ when "-h", "--help"
65
+ display_list_usage
66
+ return
67
+ end
68
+ end
69
+
70
+ templates = @template_manager.list_templates
71
+
72
+ if templates.empty?
73
+ display_message("No prompt templates found.", type: :info)
74
+ return
75
+ end
76
+
77
+ # Filter by category if specified
78
+ if category_filter
79
+ templates = templates.select { |t| t[:category] == category_filter }
80
+ if templates.empty?
81
+ display_message("No templates found in category: #{category_filter}", type: :info)
82
+ return
83
+ end
84
+ end
85
+
86
+ # Group by category
87
+ grouped = templates.group_by { |t| t[:category] }
88
+
89
+ display_message("\nAvailable Prompt Templates", type: :highlight)
90
+ display_message("=" * 50, type: :muted)
91
+
92
+ grouped.each do |category, category_templates|
93
+ display_message("\n#{category}/", type: :info)
94
+
95
+ table_data = category_templates.map do |t|
96
+ source_badge = case @template_manager.determine_source(t[:path])
97
+ when :project then "[project]"
98
+ when :user then "[user]"
99
+ else "[builtin]"
100
+ end
101
+ [
102
+ " #{File.basename(t[:id])}",
103
+ t[:version] || "1.0.0",
104
+ source_badge,
105
+ truncate(t[:description] || "", 40)
106
+ ]
107
+ end
108
+
109
+ table = TTY::Table.new(
110
+ header: ["Template", "Version", "Source", "Description"],
111
+ rows: table_data
112
+ )
113
+ display_message(table.render(:unicode, padding: [0, 1]), type: :info)
114
+ end
115
+
116
+ display_message("\nUse 'aidp prompts show <template_id>' to view details", type: :muted)
117
+ end
118
+
119
+ def show_template(args)
120
+ template_id = args.shift
121
+
122
+ if template_id.nil? || template_id.start_with?("-")
123
+ display_message("Error: Template ID required", type: :error)
124
+ display_show_usage
125
+ return
126
+ end
127
+
128
+ info = @template_manager.template_info(template_id)
129
+
130
+ if info.nil?
131
+ display_message("Template not found: #{template_id}", type: :error)
132
+ display_message("Use 'aidp prompts list' to see available templates", type: :muted)
133
+ return
134
+ end
135
+
136
+ display_message("\nTemplate: #{info[:id]}", type: :highlight)
137
+ display_message("=" * 50, type: :muted)
138
+ display_message("Name: #{info[:name]}", type: :info)
139
+ display_message("Description: #{info[:description]}", type: :info) if info[:description]
140
+ display_message("Version: #{info[:version]}", type: :info) if info[:version]
141
+ display_message("Source: #{info[:source]}", type: :info)
142
+ display_message("Path: #{info[:path]}", type: :muted)
143
+
144
+ if info[:variables]&.any?
145
+ display_message("\nVariables:", type: :highlight)
146
+ info[:variables].each { |v| display_message(" - {{#{v}}}", type: :info) }
147
+ end
148
+
149
+ display_message("\nPrompt Preview:", type: :highlight)
150
+ display_message("-" * 50, type: :muted)
151
+ display_message(info[:prompt_preview] || "(empty)", type: :info)
152
+ display_message("-" * 50, type: :muted)
153
+
154
+ if info[:source] != :project
155
+ display_message("\nTo customize this template for your project:", type: :muted)
156
+ display_message(" aidp prompts customize #{template_id}", type: :muted)
157
+ end
158
+ end
159
+
160
+ def customize_template(args)
161
+ template_id = args.shift
162
+
163
+ if template_id.nil? || template_id.start_with?("-")
164
+ display_message("Error: Template ID required", type: :error)
165
+ display_customize_usage
166
+ return
167
+ end
168
+
169
+ begin
170
+ path = @template_manager.customize_template(template_id)
171
+ display_message("Template customized successfully!", type: :success)
172
+ display_message("Edit the template at: #{path}", type: :info)
173
+ display_message("\nThe template will now be loaded from your project directory.", type: :muted)
174
+ display_message("Use 'aidp prompts reset #{template_id}' to restore the default.", type: :muted)
175
+ rescue Prompts::TemplateNotFoundError
176
+ display_message("Template not found: #{template_id}", type: :error)
177
+ display_message("Use 'aidp prompts list' to see available templates", type: :muted)
178
+ rescue => e
179
+ display_message("Failed to customize template: #{e.message}", type: :error)
180
+ end
181
+ end
182
+
183
+ def reset_template(args)
184
+ template_id = args.shift
185
+
186
+ if template_id.nil? || template_id.start_with?("-")
187
+ display_message("Error: Template ID required", type: :error)
188
+ display_reset_usage
189
+ return
190
+ end
191
+
192
+ # Confirm reset
193
+ unless @prompt.yes?("Reset #{template_id} to default? This will delete your customizations.")
194
+ display_message("Reset cancelled", type: :info)
195
+ return
196
+ end
197
+
198
+ if @template_manager.reset_template(template_id)
199
+ display_message("Template reset to default successfully!", type: :success)
200
+ else
201
+ display_message("No customization found for: #{template_id}", type: :info)
202
+ end
203
+ end
204
+
205
+ def truncate(text, max_length)
206
+ return "" if text.nil?
207
+ return text if text.length <= max_length
208
+
209
+ "#{text[0, max_length - 3]}..."
210
+ end
211
+
212
+ def display_usage
213
+ display_message("\nUsage: aidp prompts <subcommand> [options]", type: :info)
214
+ display_message("\nSubcommands:", type: :info)
215
+ display_message(" list, ls List all available prompt templates", type: :info)
216
+ display_message(" show, view <id> Show template details", type: :info)
217
+ display_message(" customize, edit <id> Copy template to project for customization", type: :info)
218
+ display_message(" reset <id> Reset customized template to default", type: :info)
219
+ display_message("\nExamples:", type: :info)
220
+ display_message(" aidp prompts list", type: :info)
221
+ display_message(" aidp prompts list --category decision_engine", type: :info)
222
+ display_message(" aidp prompts show decision_engine/condition_detection", type: :info)
223
+ display_message(" aidp prompts customize decision_engine/condition_detection", type: :info)
224
+ display_message(" aidp prompts reset decision_engine/condition_detection", type: :info)
225
+ end
226
+
227
+ def display_list_usage
228
+ display_message("\nUsage: aidp prompts list [options]", type: :info)
229
+ display_message("\nOptions:", type: :info)
230
+ display_message(" --category, -c <name> Filter by category", type: :info)
231
+ display_message(" -h, --help Show this help", type: :info)
232
+ end
233
+
234
+ def display_show_usage
235
+ display_message("\nUsage: aidp prompts show <template_id>", type: :info)
236
+ display_message("\nExamples:", type: :info)
237
+ display_message(" aidp prompts show decision_engine/condition_detection", type: :info)
238
+ end
239
+
240
+ def display_customize_usage
241
+ display_message("\nUsage: aidp prompts customize <template_id>", type: :info)
242
+ display_message("\nExamples:", type: :info)
243
+ display_message(" aidp prompts customize decision_engine/condition_detection", type: :info)
244
+ end
245
+
246
+ def display_reset_usage
247
+ display_message("\nUsage: aidp prompts reset <template_id>", type: :info)
248
+ display_message("\nExamples:", type: :info)
249
+ display_message(" aidp prompts reset decision_engine/condition_detection", type: :info)
250
+ end
251
+ end
252
+ end
253
+ end
@@ -160,9 +160,7 @@ module Aidp
160
160
  # Check if env var exists
161
161
  unless ENV.key?(env_var)
162
162
  @prompt.warn("Warning: Environment variable '#{env_var}' is not currently set")
163
- unless @prompt.yes?("Continue anyway?")
164
- return 1
165
- end
163
+ return 1 unless @prompt.yes?("Continue anyway?")
166
164
  end
167
165
 
168
166
  # Ask for optional description
@@ -189,9 +187,7 @@ module Aidp
189
187
  @prompt.say(" Env var: #{env_var}")
190
188
  @prompt.say(" Registered at: #{result[:registered_at]}")
191
189
 
192
- if scope_list.any?
193
- @prompt.say(" Scopes: #{scope_list.join(", ")}")
194
- end
190
+ @prompt.say(" Scopes: #{scope_list.join(", ")}") if scope_list.any?
195
191
 
196
192
  @prompt.say("\nThe secret value will be proxied through short-lived tokens.")
197
193
  @prompt.say("Agent processes will not have direct access to '#{env_var}'.")
@@ -229,9 +225,7 @@ module Aidp
229
225
 
230
226
  if registry.unregister(name: secret_name)
231
227
  @prompt.ok("Secret '#{secret_name}' unregistered")
232
- if revoked_count > 0
233
- @prompt.say(" Revoked #{revoked_count} active token(s)")
234
- end
228
+ @prompt.say(" Revoked #{revoked_count} active token(s)") if revoked_count > 0
235
229
  0
236
230
  else
237
231
  @prompt.error("Failed to unregister secret")
@@ -349,7 +343,7 @@ module Aidp
349
343
  end
350
344
 
351
345
  # Run RSpec with timeout protection
352
- cmd = "bundle exec rspec #{rspec_path} --format documentation"
346
+ cmd = "bundle exec rspec #{rspec_path} --format failures"
353
347
  @prompt.say("$ #{cmd}\n")
354
348
  @prompt.say("(timeout: #{AUDIT_TIMEOUT_SECONDS / 60} minutes)\n")
355
349
 
@@ -0,0 +1,277 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require_relative "../database/storage_migrator"
5
+
6
+ module Aidp
7
+ class CLI
8
+ # Command handler for `aidp storage` subcommand
9
+ #
10
+ # Provides commands for managing AIDP storage:
11
+ # - migrate: Migrate file-based storage to SQLite
12
+ # - status: Show storage migration status
13
+ # - cleanup: Remove old file-based storage after migration
14
+ #
15
+ # Usage:
16
+ # aidp storage migrate
17
+ # aidp storage migrate --dry-run
18
+ # aidp storage migrate --no-backup
19
+ # aidp storage status
20
+ # aidp storage cleanup
21
+ class StorageCommand
22
+ include Aidp::MessageDisplay
23
+
24
+ def initialize(prompt: TTY::Prompt.new, project_dir: nil)
25
+ @prompt = prompt
26
+ @project_dir = project_dir || Dir.pwd
27
+ end
28
+
29
+ # Main entry point for storage command
30
+ def run(args)
31
+ subcommand = args.shift
32
+
33
+ case subcommand
34
+ when "migrate"
35
+ run_migrate(args)
36
+ when "status"
37
+ run_status
38
+ when "cleanup"
39
+ run_cleanup(args)
40
+ when "-h", "--help", nil
41
+ display_usage
42
+ else
43
+ display_message("Unknown subcommand: #{subcommand}", type: :error)
44
+ display_usage
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def run_migrate(args)
51
+ dry_run = false
52
+ backup = true
53
+ force = false
54
+
55
+ until args.empty?
56
+ token = args.shift
57
+ case token
58
+ when "--dry-run"
59
+ dry_run = true
60
+ when "--no-backup"
61
+ backup = false
62
+ when "--force"
63
+ force = true
64
+ when "-h", "--help"
65
+ display_migrate_usage
66
+ return
67
+ else
68
+ display_message("Unknown option: #{token}", type: :error)
69
+ display_migrate_usage
70
+ return
71
+ end
72
+ end
73
+
74
+ migrator = Database::StorageMigrator.new(
75
+ project_dir: @project_dir,
76
+ dry_run: dry_run
77
+ )
78
+
79
+ # Check if migration is needed
80
+ unless migrator.migration_needed?
81
+ display_message("No file-based storage found. Nothing to migrate.", type: :info)
82
+ return
83
+ end
84
+
85
+ # Check if already migrated
86
+ if migrator.already_migrated? && !force
87
+ display_message("Database already contains migrated data.", type: :warning)
88
+ display_message("Use --force to migrate anyway (may create duplicates).", type: :info)
89
+ return
90
+ end
91
+
92
+ # Confirm migration
93
+ if dry_run
94
+ display_message("DRY RUN: No changes will be made.", type: :highlight)
95
+ else
96
+ unless force
97
+ confirmed = @prompt.yes?("Migrate file-based storage to SQLite?")
98
+ return unless confirmed
99
+ end
100
+ end
101
+
102
+ # Run migration
103
+ display_message("Starting storage migration...", type: :info)
104
+
105
+ result = migrator.migrate!(backup: backup)
106
+
107
+ # Display results
108
+ display_migration_results(result)
109
+ end
110
+
111
+ def run_status
112
+ migrator = Database::StorageMigrator.new(project_dir: @project_dir)
113
+
114
+ display_message("\nStorage Migration Status", type: :info)
115
+ display_message("=" * 40, type: :muted)
116
+
117
+ if migrator.migration_needed?
118
+ display_message("File-based storage: Found", type: :warning)
119
+ display_message(" Migration recommended", type: :info)
120
+ else
121
+ display_message("File-based storage: Not found", type: :success)
122
+ end
123
+
124
+ if migrator.already_migrated?
125
+ display_message("SQLite database: Contains data", type: :success)
126
+ else
127
+ display_message("SQLite database: Empty or not initialized", type: :info)
128
+ end
129
+
130
+ db_file = ConfigPaths.database_file(@project_dir)
131
+ if File.exist?(db_file)
132
+ size = File.size(db_file)
133
+ display_message("Database file: #{db_file}", type: :info)
134
+ display_message("Database size: #{format_size(size)}", type: :info)
135
+ end
136
+ end
137
+
138
+ def run_cleanup(args)
139
+ keep_config = true
140
+ force = false
141
+
142
+ until args.empty?
143
+ token = args.shift
144
+ case token
145
+ when "--include-config"
146
+ keep_config = false
147
+ when "--force"
148
+ force = true
149
+ when "-h", "--help"
150
+ display_cleanup_usage
151
+ return
152
+ else
153
+ display_message("Unknown option: #{token}", type: :error)
154
+ display_cleanup_usage
155
+ return
156
+ end
157
+ end
158
+
159
+ migrator = Database::StorageMigrator.new(project_dir: @project_dir)
160
+
161
+ # Verify migration was completed
162
+ unless migrator.already_migrated?
163
+ display_message("Cannot cleanup: No migrated data found in database.", type: :error)
164
+ display_message("Run 'aidp storage migrate' first.", type: :info)
165
+ return
166
+ end
167
+
168
+ unless migrator.migration_needed?
169
+ display_message("No file-based storage to clean up.", type: :info)
170
+ return
171
+ end
172
+
173
+ # Confirm cleanup
174
+ unless force
175
+ display_message("This will permanently delete old file-based storage.", type: :warning)
176
+ confirmed = @prompt.yes?("Proceed with cleanup?")
177
+ return unless confirmed
178
+ end
179
+
180
+ display_message("Cleaning up old storage...", type: :info)
181
+ migrator.cleanup_old_storage!(keep_config: keep_config)
182
+
183
+ display_message("Cleanup complete.", type: :success)
184
+ display_message("Files removed: #{migrator.stats[:files_removed]}", type: :info)
185
+ end
186
+
187
+ def display_migration_results(result)
188
+ display_message("\nMigration Results", type: :info)
189
+ display_message("=" * 40, type: :muted)
190
+
191
+ case result[:status]
192
+ when :success
193
+ display_message("Status: Success", type: :success)
194
+ when :partial
195
+ display_message("Status: Partial (some errors occurred)", type: :warning)
196
+ when :skipped
197
+ display_message("Status: Skipped - #{result[:reason]}", type: :info)
198
+ return
199
+ end
200
+
201
+ if result[:dry_run]
202
+ display_message("(Dry run - no changes made)", type: :highlight)
203
+ end
204
+
205
+ stats = result[:stats]
206
+ if stats && !stats.empty?
207
+ display_message("\nMigrated:", type: :info)
208
+ stats.each do |key, count|
209
+ next if count.zero?
210
+ label = key.to_s.tr("_", " ").capitalize
211
+ display_message(" #{label}: #{count}", type: :info)
212
+ end
213
+ end
214
+
215
+ errors = result[:errors]
216
+ if errors && !errors.empty?
217
+ display_message("\nErrors:", type: :error)
218
+ errors.each do |error|
219
+ display_message(" #{error[:type]}: #{error[:error]}", type: :error)
220
+ end
221
+ end
222
+
223
+ unless result[:dry_run]
224
+ display_message("\nNext steps:", type: :info)
225
+ display_message(" 1. Verify data with 'aidp storage status'", type: :info)
226
+ display_message(" 2. Test AIDP functionality", type: :info)
227
+ display_message(" 3. Run 'aidp storage cleanup' to remove old files", type: :info)
228
+ end
229
+ end
230
+
231
+ def display_usage
232
+ display_message("\nUsage: aidp storage <subcommand> [options]", type: :info)
233
+ display_message("\nSubcommands:", type: :info)
234
+ display_message(" migrate Migrate file-based storage to SQLite", type: :info)
235
+ display_message(" status Show storage migration status", type: :info)
236
+ display_message(" cleanup Remove old file-based storage", type: :info)
237
+ display_message("\nOptions:", type: :info)
238
+ display_message(" -h, --help Show this help message", type: :info)
239
+ display_message("\nExamples:", type: :info)
240
+ display_message(" aidp storage migrate", type: :info)
241
+ display_message(" aidp storage migrate --dry-run", type: :info)
242
+ display_message(" aidp storage status", type: :info)
243
+ display_message(" aidp storage cleanup", type: :info)
244
+ end
245
+
246
+ def display_migrate_usage
247
+ display_message("\nUsage: aidp storage migrate [options]", type: :info)
248
+ display_message("\nOptions:", type: :info)
249
+ display_message(" --dry-run Show what would be migrated without making changes", type: :info)
250
+ display_message(" --no-backup Skip creating backup of .aidp directory", type: :info)
251
+ display_message(" --force Migrate even if database already has data", type: :info)
252
+ display_message(" -h, --help Show this help message", type: :info)
253
+ end
254
+
255
+ def display_cleanup_usage
256
+ display_message("\nUsage: aidp storage cleanup [options]", type: :info)
257
+ display_message("\nOptions:", type: :info)
258
+ display_message(" --include-config Also remove aidp.yml config file", type: :info)
259
+ display_message(" --force Skip confirmation prompt", type: :info)
260
+ display_message(" -h, --help Show this help message", type: :info)
261
+ end
262
+
263
+ def format_size(bytes)
264
+ units = %w[B KB MB GB]
265
+ unit = 0
266
+ size = bytes.to_f
267
+
268
+ while size >= 1024 && unit < units.length - 1
269
+ size /= 1024
270
+ unit += 1
271
+ end
272
+
273
+ "%.1f %s" % [size, units[unit]]
274
+ end
275
+ end
276
+ end
277
+ end