aidp 0.34.0 → 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 (133) 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/ui.rb +11 -0
  61. data/lib/aidp/harness/usage_limit.rb +179 -0
  62. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  63. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  64. data/lib/aidp/harness/usage_period.rb +234 -0
  65. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  66. data/lib/aidp/init/runner.rb +25 -4
  67. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  68. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  69. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  70. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  71. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  72. data/lib/aidp/logger.rb +25 -7
  73. data/lib/aidp/orchestration_adapter.rb +279 -0
  74. data/lib/aidp/pr_worktree_manager.rb +4 -15
  75. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  76. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  77. data/lib/aidp/prompts/template_evolver.rb +305 -0
  78. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  79. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  80. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  81. data/lib/aidp/setup/wizard.rb +616 -176
  82. data/lib/aidp/shell_executor.rb +58 -0
  83. data/lib/aidp/style_guide/selector.rb +56 -1
  84. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  85. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  86. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  87. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  88. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  89. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  90. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  91. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  92. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  93. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  94. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  95. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  96. data/lib/aidp/temporal/configuration.rb +169 -0
  97. data/lib/aidp/temporal/connection.rb +93 -0
  98. data/lib/aidp/temporal/worker.rb +119 -0
  99. data/lib/aidp/temporal/workflow_client.rb +171 -0
  100. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  101. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  102. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  103. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  104. data/lib/aidp/temporal.rb +120 -0
  105. data/lib/aidp/version.rb +1 -1
  106. data/lib/aidp/watch/base_processor.rb +35 -0
  107. data/lib/aidp/watch/build_processor.rb +132 -0
  108. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  109. data/lib/aidp/watch/rebase_processor.rb +245 -0
  110. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  111. data/lib/aidp/watch/runner.rb +366 -285
  112. data/lib/aidp/watch/state_store.rb +80 -0
  113. data/lib/aidp/watch/work_item.rb +96 -0
  114. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  115. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  116. data/lib/aidp/workflows/guided_agent.rb +1 -1
  117. data/lib/aidp/worktree.rb +62 -0
  118. data/templates/aidp.yml.example +70 -0
  119. metadata +118 -16
  120. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  121. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  122. data/lib/aidp/providers/adapter.rb +0 -239
  123. data/lib/aidp/providers/aider.rb +0 -264
  124. data/lib/aidp/providers/anthropic.rb +0 -614
  125. data/lib/aidp/providers/base.rb +0 -617
  126. data/lib/aidp/providers/capability_registry.rb +0 -204
  127. data/lib/aidp/providers/codex.rb +0 -267
  128. data/lib/aidp/providers/cursor.rb +0 -304
  129. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  130. data/lib/aidp/providers/gemini.rb +0 -110
  131. data/lib/aidp/providers/github_copilot.rb +0 -227
  132. data/lib/aidp/providers/kilocode.rb +0 -157
  133. data/lib/aidp/providers/opencode.rb +0 -139
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sqlite3"
4
+ require "json"
5
+ require "fileutils"
6
+
7
+ module Aidp
8
+ # Database module for SQLite-based storage
9
+ # Provides connection management, schema migrations, and base repository functionality
10
+ module Database
11
+ class Error < StandardError; end
12
+ class MigrationError < Error; end
13
+ class ConnectionError < Error; end
14
+
15
+ # Thread-safe connection cache
16
+ @connections = {}
17
+ @mutex = Mutex.new
18
+
19
+ class << self
20
+ # Get or create a database connection for the given project directory
21
+ # Connections are cached per project_dir and are thread-safe
22
+ #
23
+ # @param project_dir [String] Project directory path
24
+ # @return [SQLite3::Database] Database connection
25
+ def connection(project_dir = Dir.pwd)
26
+ db_path = ConfigPaths.database_file(project_dir)
27
+
28
+ @mutex.synchronize do
29
+ # Return cached connection if valid
30
+ if @connections[db_path]&.closed? == false
31
+ return @connections[db_path]
32
+ end
33
+
34
+ # Ensure directory exists
35
+ FileUtils.mkdir_p(File.dirname(db_path))
36
+
37
+ # Create new connection with optimal settings
38
+ db = SQLite3::Database.new(db_path)
39
+ configure_connection(db)
40
+
41
+ @connections[db_path] = db
42
+ Aidp.log_debug("database", "connection_opened", path: db_path)
43
+ db
44
+ end
45
+ end
46
+
47
+ # Run pending migrations for the given project
48
+ #
49
+ # @param project_dir [String] Project directory path
50
+ # @return [Array<Integer>] List of applied migration versions
51
+ def migrate!(project_dir = Dir.pwd)
52
+ require_relative "database/migrations"
53
+ Migrations.run!(project_dir)
54
+ end
55
+
56
+ # Check if database exists and is initialized
57
+ #
58
+ # @param project_dir [String] Project directory path
59
+ # @return [Boolean] True if database exists
60
+ def exists?(project_dir = Dir.pwd)
61
+ File.exist?(ConfigPaths.database_file(project_dir))
62
+ end
63
+
64
+ # Get current schema version
65
+ #
66
+ # @param project_dir [String] Project directory path
67
+ # @return [Integer] Current schema version (0 if not initialized)
68
+ def schema_version(project_dir = Dir.pwd)
69
+ return 0 unless exists?(project_dir)
70
+
71
+ db = connection(project_dir)
72
+ result = db.get_first_value("SELECT MAX(version) FROM schema_migrations")
73
+ result || 0
74
+ rescue SQLite3::SQLException
75
+ # Table doesn't exist yet
76
+ 0
77
+ end
78
+
79
+ # Close connection for a specific project
80
+ #
81
+ # @param project_dir [String] Project directory path
82
+ def close(project_dir = Dir.pwd)
83
+ db_path = ConfigPaths.database_file(project_dir)
84
+
85
+ @mutex.synchronize do
86
+ if @connections[db_path] && !@connections[db_path].closed?
87
+ @connections[db_path].close
88
+ Aidp.log_debug("database", "connection_closed", path: db_path)
89
+ end
90
+ @connections.delete(db_path)
91
+ end
92
+ end
93
+
94
+ # Close all open connections
95
+ def close_all
96
+ @mutex.synchronize do
97
+ @connections.each do |path, db|
98
+ db.close unless db.closed?
99
+ Aidp.log_debug("database", "connection_closed", path: path)
100
+ end
101
+ @connections.clear
102
+ end
103
+ end
104
+
105
+ # Execute a block within a transaction
106
+ #
107
+ # @param project_dir [String] Project directory path
108
+ # @yield [db] Block to execute within transaction
109
+ # @return [Object] Result of the block
110
+ def transaction(project_dir = Dir.pwd)
111
+ db = connection(project_dir)
112
+ db.transaction do
113
+ yield db
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # Configure database connection with optimal settings
120
+ def configure_connection(db)
121
+ db.results_as_hash = true
122
+
123
+ # Enable WAL mode for better concurrency
124
+ db.execute("PRAGMA journal_mode = WAL")
125
+
126
+ # Enable foreign keys
127
+ db.execute("PRAGMA foreign_keys = ON")
128
+
129
+ # Optimize for performance
130
+ db.execute("PRAGMA synchronous = NORMAL")
131
+ db.execute("PRAGMA cache_size = -2000") # 2MB cache
132
+ db.execute("PRAGMA temp_store = MEMORY")
133
+
134
+ # Set busy timeout to 5 seconds
135
+ db.busy_timeout = 5000
136
+ end
137
+ end
138
+ end
139
+ end
@@ -15,11 +15,11 @@ module Aidp
15
15
  module ClassMethods
16
16
  # Class-level debug configuration
17
17
  def debug_enabled?
18
- ENV["DEBUG"] && ENV["DEBUG"].to_i > 0
18
+ Aidp.debug_env_level.positive?
19
19
  end
20
20
 
21
21
  def debug_level
22
- ENV["DEBUG"]&.to_i || DEBUG_OFF
22
+ Aidp.debug_env_level
23
23
  end
24
24
  end
25
25
 
@@ -18,15 +18,19 @@ module Aidp
18
18
  @project_dir = project_dir
19
19
  @checkpoint_file = File.join(project_dir, ".aidp", "checkpoint.yml")
20
20
  @history_file = File.join(project_dir, ".aidp", "checkpoint_history.jsonl")
21
+ @run_loop_file = File.join(project_dir, ".aidp", "run_loop_started_at")
21
22
  ensure_checkpoint_directory
22
23
  end
23
24
 
24
25
  # Record a checkpoint during work loop iteration
25
26
  def record_checkpoint(step_name, iteration, metrics = {})
27
+ run_loop_started_at = read_run_loop_started_at || Time.now.iso8601
28
+ write_run_loop_started_at(run_loop_started_at) if iteration == 1
26
29
  checkpoint_data = {
27
30
  step_name: step_name,
28
31
  iteration: iteration,
29
32
  timestamp: Time.now.iso8601,
33
+ run_loop_started_at: run_loop_started_at,
30
34
  metrics: collect_metrics.merge(metrics),
31
35
  status: determine_status(metrics)
32
36
  }
@@ -40,6 +44,7 @@ module Aidp
40
44
  # Get the latest checkpoint data
41
45
  def latest_checkpoint
42
46
  return nil unless File.exist?(@checkpoint_file)
47
+
43
48
  YAML.safe_load_file(@checkpoint_file, permitted_classes: [Date, Time, Symbol], aliases: true)
44
49
  end
45
50
 
@@ -300,6 +305,27 @@ module Aidp
300
305
  f.puts(data.to_json)
301
306
  end
302
307
  end
308
+
309
+ def read_run_loop_started_at
310
+ return nil unless File.exist?(@run_loop_file)
311
+
312
+ File.read(@run_loop_file).strip
313
+ rescue => e
314
+ log_rescue(e,
315
+ component: "checkpoint",
316
+ action: "read_run_loop_started_at",
317
+ fallback: nil)
318
+ nil
319
+ end
320
+
321
+ def write_run_loop_started_at(timestamp)
322
+ File.write(@run_loop_file, timestamp)
323
+ rescue => e
324
+ log_rescue(e,
325
+ component: "checkpoint",
326
+ action: "write_run_loop_started_at",
327
+ fallback: nil)
328
+ end
303
329
  end
304
330
  end
305
331
  end
@@ -43,6 +43,22 @@ module Aidp
43
43
  current = summary[:current]
44
44
  @prompt.say("Step: #{@pastel.cyan(current[:step_name])}")
45
45
  @prompt.say("Iteration: #{current[:iteration]}")
46
+ if current[:run_loop_started_at]
47
+ started = begin
48
+ Time.parse(current[:run_loop_started_at])
49
+ rescue
50
+ current[:run_loop_started_at]
51
+ end
52
+ @prompt.say("Run Loop Started: #{@pastel.blue(started.is_a?(Time) ? started.strftime("%Y-%m-%d %H:%M:%S") : started)}")
53
+ end
54
+ if current[:timestamp]
55
+ ts = begin
56
+ Time.parse(current[:timestamp])
57
+ rescue
58
+ current[:timestamp]
59
+ end
60
+ @prompt.say("Last Run: #{@pastel.magenta(ts.is_a?(Time) ? ts.strftime("%Y-%m-%d %H:%M:%S") : ts)}")
61
+ end
46
62
  @prompt.say("Status: #{format_status(current[:status])}")
47
63
  @prompt.say("")
48
64
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../harness/ai_decision_engine"
4
+ require_relative "../prompts/prompt_template_manager"
4
5
 
5
6
  module Aidp
6
7
  module Execute
@@ -15,6 +16,11 @@ module Aidp
15
16
  # - Whether the agent has sufficient context
16
17
  # - If there are blockers preventing progress
17
18
  #
19
+ # Prompts can be customized via YAML templates at:
20
+ # - Project level: .aidp/prompts/prompt_evaluator/<name>.yml
21
+ # - User level: ~/.aidp/prompts/prompt_evaluator/<name>.yml
22
+ # - Built-in: lib/aidp/prompts/defaults/prompt_evaluator/<name>.yml
23
+ #
18
24
  # @example
19
25
  # evaluator = PromptEvaluator.new(config)
20
26
  # result = evaluator.evaluate(
@@ -26,6 +32,12 @@ module Aidp
26
32
  # # => { effective: false, issues: [...], suggestions: [...] }
27
33
  #
28
34
  class PromptEvaluator
35
+ # Template paths for dynamic prompts
36
+ TEMPLATE_PATHS = {
37
+ evaluation: "prompt_evaluator/evaluation",
38
+ improvement: "prompt_evaluator/improvement"
39
+ }.freeze
40
+
29
41
  # Threshold for triggering evaluation
30
42
  EVALUATION_ITERATION_THRESHOLD = 10
31
43
 
@@ -33,10 +45,12 @@ module Aidp
33
45
  EVALUATION_INTERVAL = 5
34
46
 
35
47
  # Expose for testability
36
- attr_reader :ai_decision_engine
48
+ attr_reader :ai_decision_engine, :prompt_template_manager
37
49
 
38
- def initialize(config, ai_decision_engine: nil)
50
+ def initialize(config, ai_decision_engine: nil, prompt_template_manager: nil, project_dir: Dir.pwd)
39
51
  @config = config
52
+ @project_dir = project_dir
53
+ @prompt_template_manager = prompt_template_manager || Prompts::PromptTemplateManager.new(project_dir: project_dir)
40
54
  @ai_decision_engine = ai_decision_engine || safely_build_ai_decision_engine
41
55
  end
42
56
 
@@ -232,66 +246,25 @@ module Aidp
232
246
  private
233
247
 
234
248
  def build_evaluation_prompt(prompt_content:, iteration_count:, task_summary:, recent_failures:)
235
- <<~PROMPT
236
- You are evaluating the effectiveness of a work loop prompt that has been running for #{iteration_count} iterations without completion.
237
-
238
- ## Current Prompt Content
239
- #{truncate_content(prompt_content, 8000)}
240
-
241
- ## Task Summary
242
- #{format_task_summary(task_summary)}
243
-
244
- ## Recent Check Results
245
- #{format_failures(recent_failures)}
246
-
247
- ## Evaluation Criteria
248
-
249
- Analyze why this prompt may not be leading to completion:
250
-
251
- 1. **Clarity of Goals**: Are the implementation requirements clearly defined?
252
- 2. **Task Breakdown**: Does the prompt guide proper task decomposition?
253
- 3. **Completion Criteria**: Are the completion criteria specific and achievable?
254
- 4. **Context Sufficiency**: Does the agent have enough context to proceed?
255
- 5. **Blockers**: Are there technical blockers or missing information?
256
- 6. **Scope**: Is the scope realistic for an AI agent to complete?
257
-
258
- ## Your Assessment
259
-
260
- Provide:
261
- - Whether this prompt is likely effective (true/false)
262
- - Specific issues with the current prompt
263
- - Actionable suggestions for improvement
264
- - Likely blockers preventing progress
265
- - Prioritized recommended actions
266
- - Your confidence in this assessment (0.0-1.0)
249
+ variables = {
250
+ iteration_count: iteration_count.to_s,
251
+ prompt_content: truncate_content(prompt_content, 8000),
252
+ task_summary: format_task_summary(task_summary),
253
+ recent_failures: format_failures(recent_failures)
254
+ }
267
255
 
268
- Be specific and actionable. Focus on what can be changed to achieve completion.
269
- PROMPT
256
+ @prompt_template_manager.render(TEMPLATE_PATHS[:evaluation], **variables)
270
257
  end
271
258
 
272
259
  def build_improvement_prompt(evaluation_result, original_template)
273
- <<~PROMPT
274
- Based on the following prompt evaluation, suggest improvements to the template.
275
-
276
- ## Evaluation Results
277
- - Effective: #{evaluation_result[:effective]}
278
- - Issues: #{(evaluation_result[:issues] || []).join(", ")}
279
- - Suggestions: #{(evaluation_result[:suggestions] || []).join(", ")}
280
-
281
- ## Original Template
282
- #{truncate_content(original_template, 4000)}
283
-
284
- ## Your Task
285
-
286
- Suggest specific improvements to make the template more effective:
287
- 1. Identify sections that need improvement
288
- 2. Propose new sections if needed
289
- 3. Focus especially on completion criteria clarity
290
- 4. Ensure task breakdown instructions are explicit
291
- 5. Add guidance for common failure modes
260
+ variables = {
261
+ effective: evaluation_result[:effective].to_s,
262
+ issues: (evaluation_result[:issues] || []).join(", "),
263
+ suggestions: (evaluation_result[:suggestions] || []).join(", "),
264
+ original_template: truncate_content(original_template, 4000)
265
+ }
292
266
 
293
- Be specific - provide actual text that could replace or supplement the template.
294
- PROMPT
267
+ @prompt_template_manager.render(TEMPLATE_PATHS[:improvement], **variables)
295
268
  end
296
269
 
297
270
  def format_task_summary(task_summary)