aidp 0.7.0 → 0.8.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/README.md +60 -214
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +38 -23
- data/lib/aidp/analysis/seams.rb +2 -31
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +1 -13
- data/lib/aidp/analysis/tree_sitter_scan.rb +3 -20
- data/lib/aidp/analyze/error_handler.rb +2 -75
- data/lib/aidp/analyze/json_file_storage.rb +292 -0
- data/lib/aidp/analyze/progress.rb +12 -0
- data/lib/aidp/analyze/progress_visualizer.rb +12 -17
- data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
- data/lib/aidp/analyze/runner.rb +256 -87
- data/lib/aidp/cli/jobs_command.rb +100 -432
- data/lib/aidp/cli.rb +309 -239
- data/lib/aidp/config.rb +298 -10
- data/lib/aidp/debug_logger.rb +195 -0
- data/lib/aidp/debug_mixin.rb +187 -0
- data/lib/aidp/execute/progress.rb +9 -0
- data/lib/aidp/execute/runner.rb +221 -40
- data/lib/aidp/execute/steps.rb +17 -7
- data/lib/aidp/execute/workflow_selector.rb +211 -0
- data/lib/aidp/harness/completion_checker.rb +268 -0
- data/lib/aidp/harness/condition_detector.rb +1526 -0
- data/lib/aidp/harness/config_loader.rb +373 -0
- data/lib/aidp/harness/config_manager.rb +382 -0
- data/lib/aidp/harness/config_schema.rb +1006 -0
- data/lib/aidp/harness/config_validator.rb +355 -0
- data/lib/aidp/harness/configuration.rb +477 -0
- data/lib/aidp/harness/enhanced_runner.rb +494 -0
- data/lib/aidp/harness/error_handler.rb +616 -0
- data/lib/aidp/harness/provider_config.rb +423 -0
- data/lib/aidp/harness/provider_factory.rb +306 -0
- data/lib/aidp/harness/provider_manager.rb +1269 -0
- data/lib/aidp/harness/provider_type_checker.rb +88 -0
- data/lib/aidp/harness/runner.rb +411 -0
- data/lib/aidp/harness/state/errors.rb +28 -0
- data/lib/aidp/harness/state/metrics.rb +219 -0
- data/lib/aidp/harness/state/persistence.rb +128 -0
- data/lib/aidp/harness/state/provider_state.rb +132 -0
- data/lib/aidp/harness/state/ui_state.rb +68 -0
- data/lib/aidp/harness/state/workflow_state.rb +123 -0
- data/lib/aidp/harness/state_manager.rb +586 -0
- data/lib/aidp/harness/status_display.rb +888 -0
- data/lib/aidp/harness/ui/base.rb +16 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
- data/lib/aidp/harness/ui/error_handler.rb +132 -0
- data/lib/aidp/harness/ui/frame_manager.rb +361 -0
- data/lib/aidp/harness/ui/job_monitor.rb +500 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
- data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
- data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
- data/lib/aidp/harness/ui/progress_display.rb +280 -0
- data/lib/aidp/harness/ui/question_collector.rb +141 -0
- data/lib/aidp/harness/ui/spinner_group.rb +184 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
- data/lib/aidp/harness/ui/status_manager.rb +312 -0
- data/lib/aidp/harness/ui/status_widget.rb +280 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
- data/lib/aidp/harness/user_interface.rb +2381 -0
- data/lib/aidp/provider_manager.rb +131 -7
- data/lib/aidp/providers/anthropic.rb +28 -103
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -181
- data/lib/aidp/providers/gemini.rb +24 -107
- data/lib/aidp/providers/macos_ui.rb +99 -5
- data/lib/aidp/providers/opencode.rb +194 -0
- data/lib/aidp/storage/csv_storage.rb +172 -0
- data/lib/aidp/storage/file_manager.rb +214 -0
- data/lib/aidp/storage/json_storage.rb +140 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +54 -39
- data/templates/COMMON/AGENT_BASE.md +11 -0
- data/templates/EXECUTE/00_PRD.md +4 -4
- data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
- data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
- data/templates/EXECUTE/08_TASKS.md +4 -4
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
- data/templates/README.md +279 -0
- data/templates/aidp-development.yml.example +373 -0
- data/templates/aidp-minimal.yml.example +48 -0
- data/templates/aidp-production.yml.example +475 -0
- data/templates/aidp.yml.example +598 -0
- metadata +93 -69
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -439
- data/lib/aidp/analyze/data_retention_manager.rb +0 -421
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -418
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -533
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -499
- data/lib/aidp/analyze/memory_manager.rb +0 -339
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -454
- data/lib/aidp/analyze/performance_optimizer.rb +0 -691
- data/lib/aidp/analyze/repository_chunker.rb +0 -697
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -655
- data/lib/aidp/analyze/tool_configuration.rb +0 -441
- data/lib/aidp/analyze/tool_modernization.rb +0 -750
- data/lib/aidp/database/pg_adapter.rb +0 -148
- data/lib/aidp/database_config.rb +0 -69
- data/lib/aidp/database_connection.rb +0 -72
- data/lib/aidp/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -45
- data/lib/aidp/jobs/provider_execution_job.rb +0 -83
- data/lib/aidp/project_detector.rb +0 -117
- data/lib/aidp/providers/agent_supervisor.rb +0 -348
- data/lib/aidp/providers/supervised_base.rb +0 -317
- data/lib/aidp/providers/supervised_cursor.rb +0 -22
- data/lib/aidp/sync.rb +0 -13
- data/lib/aidp/workspace.rb +0 -19
@@ -1,421 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
require "yaml"
|
5
|
-
require_relative "storage"
|
6
|
-
|
7
|
-
module Aidp
|
8
|
-
module Analyze
|
9
|
-
class DataRetentionManager
|
10
|
-
# Retention policies
|
11
|
-
RETENTION_POLICIES = {
|
12
|
-
"metrics" => "indefinite",
|
13
|
-
"analysis_results" => "configurable",
|
14
|
-
"execution_logs" => "configurable",
|
15
|
-
"temporary_data" => "immediate",
|
16
|
-
"embeddings" => "indefinite"
|
17
|
-
}.freeze
|
18
|
-
|
19
|
-
# Default retention periods (in days)
|
20
|
-
DEFAULT_RETENTION_PERIODS = {
|
21
|
-
"analysis_results" => 90, # 3 months
|
22
|
-
"execution_logs" => 30, # 1 month
|
23
|
-
"temporary_data" => 1 # 1 day
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
def initialize(project_dir = Dir.pwd, config = {})
|
27
|
-
@project_dir = project_dir
|
28
|
-
@config = config
|
29
|
-
@storage = config[:storage] || Aidp::AnalysisStorage.new(project_dir)
|
30
|
-
@retention_config = load_retention_config
|
31
|
-
end
|
32
|
-
|
33
|
-
# Apply retention policies
|
34
|
-
def apply_retention_policies(options = {})
|
35
|
-
dry_run = options[:dry_run] || false
|
36
|
-
options[:force] || false
|
37
|
-
|
38
|
-
results = {
|
39
|
-
cleaned_data: {},
|
40
|
-
retained_data: {},
|
41
|
-
errors: []
|
42
|
-
}
|
43
|
-
|
44
|
-
# Process each data type
|
45
|
-
RETENTION_POLICIES.each do |data_type, policy|
|
46
|
-
case policy
|
47
|
-
when "indefinite"
|
48
|
-
results[:retained_data][data_type] = retain_indefinitely(data_type)
|
49
|
-
when "configurable"
|
50
|
-
results[:cleaned_data][data_type] = apply_configurable_retention(data_type, dry_run)
|
51
|
-
when "immediate"
|
52
|
-
results[:cleaned_data][data_type] = clean_immediately(data_type, dry_run)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
results
|
57
|
-
end
|
58
|
-
|
59
|
-
# Handle force/rerun operations
|
60
|
-
def handle_force_rerun(execution_id, step_name, options = {})
|
61
|
-
operation = options[:operation] || "force"
|
62
|
-
|
63
|
-
case operation
|
64
|
-
when "force"
|
65
|
-
handle_force_operation(execution_id, step_name, options)
|
66
|
-
when "rerun"
|
67
|
-
handle_rerun_operation(execution_id, step_name, options)
|
68
|
-
else
|
69
|
-
raise "Unknown operation: #{operation}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Clean old data based on retention policies
|
74
|
-
def clean_old_data(options = {})
|
75
|
-
dry_run = options[:dry_run] || false
|
76
|
-
data_types = options[:data_types] || RETENTION_POLICIES.keys
|
77
|
-
|
78
|
-
results = {
|
79
|
-
cleaned: {},
|
80
|
-
errors: []
|
81
|
-
}
|
82
|
-
|
83
|
-
data_types.each do |data_type|
|
84
|
-
next unless RETENTION_POLICIES[data_type] == "configurable"
|
85
|
-
|
86
|
-
begin
|
87
|
-
cleaned = clean_data_by_type(data_type, dry_run)
|
88
|
-
results[:cleaned][data_type] = cleaned
|
89
|
-
rescue => e
|
90
|
-
results[:errors] << {
|
91
|
-
data_type: data_type,
|
92
|
-
error: e.message
|
93
|
-
}
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
results
|
98
|
-
end
|
99
|
-
|
100
|
-
# Get retention statistics
|
101
|
-
def get_retention_statistics
|
102
|
-
stats = {
|
103
|
-
policies: RETENTION_POLICIES,
|
104
|
-
config: @retention_config,
|
105
|
-
data_sizes: {},
|
106
|
-
retention_status: {}
|
107
|
-
}
|
108
|
-
|
109
|
-
# Calculate data sizes
|
110
|
-
RETENTION_POLICIES.keys.each do |data_type|
|
111
|
-
stats[:data_sizes][data_type] = calculate_data_size(data_type)
|
112
|
-
stats[:retention_status][data_type] = get_retention_status(data_type)
|
113
|
-
end
|
114
|
-
|
115
|
-
stats
|
116
|
-
end
|
117
|
-
|
118
|
-
# Export data with retention metadata
|
119
|
-
def export_data_with_retention(data_type, options = {})
|
120
|
-
include_retention_info = options[:include_retention_info] || true
|
121
|
-
|
122
|
-
data = export_data(data_type, options)
|
123
|
-
|
124
|
-
if include_retention_info
|
125
|
-
data[:retention_info] = {
|
126
|
-
policy: RETENTION_POLICIES[data_type],
|
127
|
-
retention_period: @retention_config[data_type],
|
128
|
-
last_cleaned: get_last_cleaned_date(data_type),
|
129
|
-
next_cleanup: calculate_next_cleanup(data_type)
|
130
|
-
}
|
131
|
-
end
|
132
|
-
|
133
|
-
data
|
134
|
-
end
|
135
|
-
|
136
|
-
# Set retention policy for a data type
|
137
|
-
def set_retention_policy(data_type, policy, options = {})
|
138
|
-
raise "Unknown data type: #{data_type}" unless RETENTION_POLICIES.key?(data_type)
|
139
|
-
|
140
|
-
case policy
|
141
|
-
when "indefinite"
|
142
|
-
@retention_config[data_type] = {policy: "indefinite"}
|
143
|
-
when "configurable"
|
144
|
-
retention_period = options[:retention_period] || DEFAULT_RETENTION_PERIODS[data_type]
|
145
|
-
@retention_config[data_type] = {
|
146
|
-
policy: "configurable",
|
147
|
-
retention_period: retention_period
|
148
|
-
}
|
149
|
-
when "immediate"
|
150
|
-
@retention_config[data_type] = {policy: "immediate"}
|
151
|
-
else
|
152
|
-
raise "Unknown retention policy: #{policy}"
|
153
|
-
end
|
154
|
-
|
155
|
-
save_retention_config
|
156
|
-
|
157
|
-
{
|
158
|
-
data_type: data_type,
|
159
|
-
policy: policy,
|
160
|
-
config: @retention_config[data_type]
|
161
|
-
}
|
162
|
-
end
|
163
|
-
|
164
|
-
# Get data retention status
|
165
|
-
def get_data_retention_status(data_type)
|
166
|
-
{
|
167
|
-
data_type: data_type,
|
168
|
-
policy: RETENTION_POLICIES[data_type],
|
169
|
-
config: @retention_config[data_type],
|
170
|
-
data_size: calculate_data_size(data_type),
|
171
|
-
last_cleaned: get_last_cleaned_date(data_type),
|
172
|
-
next_cleanup: calculate_next_cleanup(data_type),
|
173
|
-
retention_status: get_retention_status(data_type)
|
174
|
-
}
|
175
|
-
end
|
176
|
-
|
177
|
-
private
|
178
|
-
|
179
|
-
def load_retention_config
|
180
|
-
config_file = File.join(@project_dir, ".aidp-retention-config.yml")
|
181
|
-
|
182
|
-
if File.exist?(config_file)
|
183
|
-
YAML.load_file(config_file) || DEFAULT_RETENTION_PERIODS
|
184
|
-
else
|
185
|
-
DEFAULT_RETENTION_PERIODS
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def save_retention_config
|
190
|
-
config_file = File.join(@project_dir, ".aidp-retention-config.yml")
|
191
|
-
File.write(config_file, YAML.dump(@retention_config))
|
192
|
-
end
|
193
|
-
|
194
|
-
def retain_indefinitely(data_type)
|
195
|
-
{
|
196
|
-
data_type: data_type,
|
197
|
-
action: "retained",
|
198
|
-
reason: "Indefinite retention policy",
|
199
|
-
timestamp: Time.now
|
200
|
-
}
|
201
|
-
end
|
202
|
-
|
203
|
-
def apply_configurable_retention(data_type, dry_run)
|
204
|
-
retention_period = @retention_config[data_type] || DEFAULT_RETENTION_PERIODS[data_type]
|
205
|
-
cutoff_date = Time.now - (retention_period * 24 * 60 * 60)
|
206
|
-
|
207
|
-
# Get old data
|
208
|
-
old_data = get_old_data(data_type, cutoff_date)
|
209
|
-
|
210
|
-
if dry_run
|
211
|
-
{
|
212
|
-
data_type: data_type,
|
213
|
-
action: "would_clean",
|
214
|
-
records_to_clean: old_data.length,
|
215
|
-
cutoff_date: cutoff_date,
|
216
|
-
dry_run: true
|
217
|
-
}
|
218
|
-
else
|
219
|
-
# Actually clean the data
|
220
|
-
cleaned_count = clean_data(data_type, old_data)
|
221
|
-
|
222
|
-
{
|
223
|
-
data_type: data_type,
|
224
|
-
action: "cleaned",
|
225
|
-
records_cleaned: cleaned_count,
|
226
|
-
cutoff_date: cutoff_date,
|
227
|
-
timestamp: Time.now
|
228
|
-
}
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def clean_immediately(data_type, dry_run)
|
233
|
-
if dry_run
|
234
|
-
{
|
235
|
-
data_type: data_type,
|
236
|
-
action: "would_clean_immediately",
|
237
|
-
dry_run: true
|
238
|
-
}
|
239
|
-
else
|
240
|
-
# Clean all data of this type
|
241
|
-
cleaned_count = clean_all_data(data_type)
|
242
|
-
|
243
|
-
{
|
244
|
-
data_type: data_type,
|
245
|
-
action: "cleaned_immediately",
|
246
|
-
records_cleaned: cleaned_count,
|
247
|
-
timestamp: Time.now
|
248
|
-
}
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def handle_force_operation(execution_id, step_name, options)
|
253
|
-
# Force operation: overwrite main data, retain metrics
|
254
|
-
results = {
|
255
|
-
operation: "force",
|
256
|
-
execution_id: execution_id,
|
257
|
-
step_name: step_name,
|
258
|
-
actions: []
|
259
|
-
}
|
260
|
-
|
261
|
-
# Overwrite analysis results
|
262
|
-
if options[:analysis_data]
|
263
|
-
@storage.force_overwrite(execution_id, step_name, options[:analysis_data])
|
264
|
-
results[:actions] << "overwrote_analysis_data"
|
265
|
-
end
|
266
|
-
|
267
|
-
# Retain metrics (indefinite retention)
|
268
|
-
results[:actions] << "retained_metrics"
|
269
|
-
|
270
|
-
results
|
271
|
-
end
|
272
|
-
|
273
|
-
def handle_rerun_operation(execution_id, step_name, options)
|
274
|
-
# Rerun operation: overwrite main data, retain metrics
|
275
|
-
results = {
|
276
|
-
operation: "rerun",
|
277
|
-
execution_id: execution_id,
|
278
|
-
step_name: step_name,
|
279
|
-
actions: []
|
280
|
-
}
|
281
|
-
|
282
|
-
# Overwrite analysis results
|
283
|
-
if options[:analysis_data]
|
284
|
-
@storage.force_overwrite(execution_id, step_name, options[:analysis_data])
|
285
|
-
results[:actions] << "overwrote_analysis_data"
|
286
|
-
end
|
287
|
-
|
288
|
-
# Retain metrics (indefinite retention)
|
289
|
-
results[:actions] << "retained_metrics"
|
290
|
-
|
291
|
-
results
|
292
|
-
end
|
293
|
-
|
294
|
-
def get_old_data(data_type, cutoff_date)
|
295
|
-
case data_type
|
296
|
-
when "analysis_results"
|
297
|
-
get_old_analysis_results(cutoff_date)
|
298
|
-
when "execution_logs"
|
299
|
-
get_old_execution_logs(cutoff_date)
|
300
|
-
when "temporary_data"
|
301
|
-
get_old_temporary_data(cutoff_date)
|
302
|
-
else
|
303
|
-
[]
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
def get_old_analysis_results(cutoff_date)
|
308
|
-
# Get analysis results older than cutoff date
|
309
|
-
# This would query the database for old records
|
310
|
-
[]
|
311
|
-
end
|
312
|
-
|
313
|
-
def get_old_execution_logs(cutoff_date)
|
314
|
-
# Get execution logs older than cutoff date
|
315
|
-
[]
|
316
|
-
end
|
317
|
-
|
318
|
-
def get_old_temporary_data(cutoff_date)
|
319
|
-
# Get temporary data older than cutoff date
|
320
|
-
[]
|
321
|
-
end
|
322
|
-
|
323
|
-
def clean_data(data_type, old_data)
|
324
|
-
case data_type
|
325
|
-
when "analysis_results"
|
326
|
-
clean_analysis_results(old_data)
|
327
|
-
when "execution_logs"
|
328
|
-
clean_execution_logs(old_data)
|
329
|
-
when "temporary_data"
|
330
|
-
clean_temporary_data(old_data)
|
331
|
-
else
|
332
|
-
0
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
def clean_all_data(data_type)
|
337
|
-
case data_type
|
338
|
-
when "temporary_data"
|
339
|
-
clean_all_temporary_data
|
340
|
-
else
|
341
|
-
0
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
def clean_analysis_results(old_data)
|
346
|
-
# Clean old analysis results from database
|
347
|
-
# This would delete records from the database
|
348
|
-
old_data.length
|
349
|
-
end
|
350
|
-
|
351
|
-
def clean_execution_logs(old_data)
|
352
|
-
# Clean old execution logs
|
353
|
-
old_data.length
|
354
|
-
end
|
355
|
-
|
356
|
-
def clean_temporary_data(old_data)
|
357
|
-
# Clean old temporary data
|
358
|
-
old_data.length
|
359
|
-
end
|
360
|
-
|
361
|
-
def clean_all_temporary_data
|
362
|
-
# Clean all temporary data
|
363
|
-
0
|
364
|
-
end
|
365
|
-
|
366
|
-
def calculate_data_size(data_type)
|
367
|
-
case data_type
|
368
|
-
when "metrics"
|
369
|
-
@storage.get_analysis_statistics[:total_metrics] || 0
|
370
|
-
when "analysis_results"
|
371
|
-
@storage.get_analysis_statistics[:total_steps] || 0
|
372
|
-
when "execution_logs"
|
373
|
-
@storage.get_execution_history.length
|
374
|
-
else
|
375
|
-
0
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
def get_retention_status(data_type)
|
380
|
-
policy = RETENTION_POLICIES[data_type]
|
381
|
-
config = @retention_config[data_type]
|
382
|
-
|
383
|
-
{
|
384
|
-
policy: policy,
|
385
|
-
config: config,
|
386
|
-
status: "active"
|
387
|
-
}
|
388
|
-
end
|
389
|
-
|
390
|
-
def get_last_cleaned_date(data_type)
|
391
|
-
# This would be stored in a metadata table
|
392
|
-
nil
|
393
|
-
end
|
394
|
-
|
395
|
-
def calculate_next_cleanup(data_type)
|
396
|
-
policy = RETENTION_POLICIES[data_type]
|
397
|
-
|
398
|
-
case policy
|
399
|
-
when "indefinite"
|
400
|
-
nil
|
401
|
-
when "configurable"
|
402
|
-
retention_period = @retention_config[data_type] || DEFAULT_RETENTION_PERIODS[data_type]
|
403
|
-
Time.now + (retention_period * 24 * 60 * 60)
|
404
|
-
when "immediate"
|
405
|
-
Time.now
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
def export_data(data_type, options)
|
410
|
-
case data_type
|
411
|
-
when "metrics"
|
412
|
-
@storage.export_data("json", options)
|
413
|
-
when "analysis_results"
|
414
|
-
@storage.export_data("json", options)
|
415
|
-
else
|
416
|
-
{error: "Unknown data type: #{data_type}"}
|
417
|
-
end
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|
@@ -1,260 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "pg"
|
4
|
-
require "json"
|
5
|
-
require "fileutils"
|
6
|
-
|
7
|
-
module Aidp
|
8
|
-
class AnalysisDatabase
|
9
|
-
def initialize(project_dir = Dir.pwd)
|
10
|
-
@project_dir = project_dir
|
11
|
-
ensure_database_exists
|
12
|
-
end
|
13
|
-
|
14
|
-
# Store analysis results with retention policies
|
15
|
-
def store_analysis_result(step_name, data, metadata = {})
|
16
|
-
db = connect
|
17
|
-
|
18
|
-
# Store the main analysis result
|
19
|
-
db.exec_params(
|
20
|
-
<<~SQL,
|
21
|
-
INSERT INTO analysis_results (step_name, data, metadata, created_at, updated_at)
|
22
|
-
VALUES ($1, $2, $3, $4, $5)
|
23
|
-
ON CONFLICT (step_name)
|
24
|
-
DO UPDATE SET
|
25
|
-
data = EXCLUDED.data,
|
26
|
-
metadata = EXCLUDED.metadata,
|
27
|
-
updated_at = EXCLUDED.updated_at
|
28
|
-
SQL
|
29
|
-
[step_name, data.to_json, metadata.to_json, Time.now, Time.now]
|
30
|
-
)
|
31
|
-
|
32
|
-
# Store metrics for indefinite retention
|
33
|
-
store_metrics(step_name, metadata[:metrics]) if metadata[:metrics]
|
34
|
-
end
|
35
|
-
|
36
|
-
# Store metrics that should be retained indefinitely
|
37
|
-
def store_metrics(step_name, metrics)
|
38
|
-
db = connect
|
39
|
-
|
40
|
-
metrics.each do |metric_name, value|
|
41
|
-
db.exec_params(
|
42
|
-
<<~SQL,
|
43
|
-
INSERT INTO analysis_metrics (step_name, metric_name, value, recorded_at)
|
44
|
-
VALUES ($1, $2, $3, $4)
|
45
|
-
ON CONFLICT (step_name, metric_name, recorded_at)
|
46
|
-
DO UPDATE SET value = EXCLUDED.value
|
47
|
-
SQL
|
48
|
-
[step_name, metric_name.to_s, value.to_json, Time.now]
|
49
|
-
)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Store embedding vectors for future semantic analysis
|
54
|
-
def store_embeddings(step_name, embeddings_data)
|
55
|
-
db = connect
|
56
|
-
|
57
|
-
db.exec_params(
|
58
|
-
<<~SQL,
|
59
|
-
INSERT INTO embeddings (step_name, embeddings_data, created_at)
|
60
|
-
VALUES ($1, $2, $3)
|
61
|
-
ON CONFLICT (step_name)
|
62
|
-
DO UPDATE SET
|
63
|
-
embeddings_data = EXCLUDED.embeddings_data,
|
64
|
-
created_at = EXCLUDED.created_at
|
65
|
-
SQL
|
66
|
-
[step_name, embeddings_data.to_json, Time.now]
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Retrieve analysis results
|
71
|
-
def get_analysis_result(step_name)
|
72
|
-
db = connect
|
73
|
-
result = db.exec_params(
|
74
|
-
"SELECT data, metadata, created_at, updated_at FROM analysis_results WHERE step_name = $1",
|
75
|
-
[step_name]
|
76
|
-
).first
|
77
|
-
|
78
|
-
return nil unless result
|
79
|
-
|
80
|
-
{
|
81
|
-
data: JSON.parse(result["data"]),
|
82
|
-
metadata: JSON.parse(result["metadata"]),
|
83
|
-
created_at: result["created_at"],
|
84
|
-
updated_at: result["updated_at"]
|
85
|
-
}
|
86
|
-
end
|
87
|
-
|
88
|
-
# Retrieve metrics for a step
|
89
|
-
def get_metrics(step_name)
|
90
|
-
db = connect
|
91
|
-
results = db.exec_params(
|
92
|
-
"SELECT metric_name, value, recorded_at FROM analysis_metrics WHERE step_name = $1 ORDER BY recorded_at DESC",
|
93
|
-
[step_name]
|
94
|
-
)
|
95
|
-
|
96
|
-
results.map do |row|
|
97
|
-
{
|
98
|
-
metric_name: row["metric_name"],
|
99
|
-
value: JSON.parse(row["value"]),
|
100
|
-
recorded_at: row["recorded_at"]
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Get all metrics for trend analysis
|
106
|
-
def get_all_metrics
|
107
|
-
db = connect
|
108
|
-
results = db.exec("SELECT step_name, metric_name, value, recorded_at FROM analysis_metrics ORDER BY recorded_at DESC")
|
109
|
-
|
110
|
-
results.map do |row|
|
111
|
-
{
|
112
|
-
step_name: row["step_name"],
|
113
|
-
metric_name: row["metric_name"],
|
114
|
-
value: JSON.parse(row["value"]),
|
115
|
-
recorded_at: row["recorded_at"]
|
116
|
-
}
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
# Force overwrite analysis data (for --force/--rerun flags)
|
121
|
-
def force_overwrite(step_name, data, metadata = {})
|
122
|
-
db = connect
|
123
|
-
|
124
|
-
# Delete existing data
|
125
|
-
db.exec_params("DELETE FROM analysis_results WHERE step_name = $1", [step_name])
|
126
|
-
db.exec_params("DELETE FROM embeddings WHERE step_name = $1", [step_name])
|
127
|
-
|
128
|
-
# Store new data
|
129
|
-
db.exec_params(
|
130
|
-
<<~SQL,
|
131
|
-
INSERT INTO analysis_results (step_name, data, metadata, created_at, updated_at)
|
132
|
-
VALUES ($1, $2, $3, $4, $5)
|
133
|
-
SQL
|
134
|
-
[step_name, data.to_json, metadata.to_json, Time.now, Time.now]
|
135
|
-
)
|
136
|
-
|
137
|
-
# Store metrics (these are retained indefinitely)
|
138
|
-
store_metrics(step_name, metadata[:metrics]) if metadata[:metrics]
|
139
|
-
end
|
140
|
-
|
141
|
-
# Delete analysis data (for user cleanup)
|
142
|
-
def delete_analysis_data(step_name)
|
143
|
-
db = connect
|
144
|
-
db.exec_params("DELETE FROM analysis_results WHERE step_name = $1", [step_name])
|
145
|
-
db.exec_params("DELETE FROM embeddings WHERE step_name = $1", [step_name])
|
146
|
-
# NOTE: metrics are NOT deleted as they should be retained indefinitely
|
147
|
-
end
|
148
|
-
|
149
|
-
# Export data in different formats
|
150
|
-
def export_data(step_name, format = "json")
|
151
|
-
result = get_analysis_result(step_name)
|
152
|
-
return nil unless result
|
153
|
-
|
154
|
-
case format.downcase
|
155
|
-
when "json"
|
156
|
-
result.to_json
|
157
|
-
when "csv"
|
158
|
-
export_to_csv(result)
|
159
|
-
else
|
160
|
-
raise "Unsupported export format: #{format}"
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
# Get database statistics
|
165
|
-
def get_statistics
|
166
|
-
db = connect
|
167
|
-
|
168
|
-
{
|
169
|
-
total_analysis_results: db.exec("SELECT COUNT(*) FROM analysis_results").first["count"].to_i,
|
170
|
-
total_metrics: db.exec("SELECT COUNT(*) FROM analysis_metrics").first["count"].to_i,
|
171
|
-
total_embeddings: db.exec("SELECT COUNT(*) FROM embeddings").first["count"].to_i,
|
172
|
-
steps_analyzed: db.exec("SELECT DISTINCT step_name FROM analysis_results").map { |row| row["step_name"] },
|
173
|
-
oldest_metric: db.exec("SELECT MIN(recorded_at) FROM analysis_metrics").first["min"],
|
174
|
-
newest_metric: db.exec("SELECT MAX(recorded_at) FROM analysis_metrics").first["max"]
|
175
|
-
}
|
176
|
-
end
|
177
|
-
|
178
|
-
private
|
179
|
-
|
180
|
-
def ensure_database_exists
|
181
|
-
db = connect
|
182
|
-
create_schema(db)
|
183
|
-
end
|
184
|
-
|
185
|
-
def connect
|
186
|
-
@db ||= PG.connect(
|
187
|
-
host: ENV["AIDP_DB_HOST"] || "localhost",
|
188
|
-
port: ENV["AIDP_DB_PORT"] || 5432,
|
189
|
-
dbname: ENV["AIDP_DB_NAME"] || "aidp",
|
190
|
-
user: ENV["AIDP_DB_USER"] || ENV["USER"],
|
191
|
-
password: ENV["AIDP_DB_PASSWORD"]
|
192
|
-
)
|
193
|
-
@db.type_map_for_results = PG::BasicTypeMapForResults.new(@db)
|
194
|
-
@db
|
195
|
-
end
|
196
|
-
|
197
|
-
def create_schema(db)
|
198
|
-
# Create analysis_results table
|
199
|
-
db.exec(<<~SQL)
|
200
|
-
CREATE TABLE IF NOT EXISTS analysis_results (
|
201
|
-
step_name TEXT PRIMARY KEY,
|
202
|
-
data JSONB NOT NULL,
|
203
|
-
metadata JSONB,
|
204
|
-
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
205
|
-
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
|
206
|
-
)
|
207
|
-
SQL
|
208
|
-
|
209
|
-
# Create analysis_metrics table (indefinite retention)
|
210
|
-
db.exec(<<~SQL)
|
211
|
-
CREATE TABLE IF NOT EXISTS analysis_metrics (
|
212
|
-
id SERIAL PRIMARY KEY,
|
213
|
-
step_name TEXT NOT NULL,
|
214
|
-
metric_name TEXT NOT NULL,
|
215
|
-
value JSONB NOT NULL,
|
216
|
-
recorded_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
217
|
-
UNIQUE(step_name, metric_name, recorded_at)
|
218
|
-
)
|
219
|
-
SQL
|
220
|
-
|
221
|
-
# Create embeddings table (for future semantic analysis)
|
222
|
-
db.exec(<<~SQL)
|
223
|
-
CREATE TABLE IF NOT EXISTS embeddings (
|
224
|
-
step_name TEXT PRIMARY KEY,
|
225
|
-
embeddings_data JSONB NOT NULL,
|
226
|
-
created_at TIMESTAMP WITH TIME ZONE NOT NULL
|
227
|
-
)
|
228
|
-
SQL
|
229
|
-
|
230
|
-
# Create indexes for better performance
|
231
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_analysis_metrics_step_name ON analysis_metrics(step_name)")
|
232
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_analysis_metrics_recorded_at ON analysis_metrics(recorded_at)")
|
233
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_analysis_results_updated_at ON analysis_results(updated_at)")
|
234
|
-
end
|
235
|
-
|
236
|
-
def export_to_csv(data)
|
237
|
-
require "csv"
|
238
|
-
|
239
|
-
# For now, export a simplified CSV format
|
240
|
-
# This can be enhanced based on specific data structures
|
241
|
-
CSV.generate do |csv|
|
242
|
-
csv << %w[Field Value]
|
243
|
-
csv << ["created_at", data[:created_at]]
|
244
|
-
csv << ["updated_at", data[:updated_at]]
|
245
|
-
|
246
|
-
# Add metadata fields
|
247
|
-
data[:metadata].each do |key, value|
|
248
|
-
csv << ["metadata_#{key}", value]
|
249
|
-
end
|
250
|
-
|
251
|
-
# Add data summary
|
252
|
-
if data[:data].is_a?(Hash)
|
253
|
-
data[:data].each do |key, value|
|
254
|
-
csv << ["data_#{key}", value.to_s[0..100]] # Truncate long values
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|