aidp 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +47 -0
  3. data/lib/aidp/analyze/error_handler.rb +14 -15
  4. data/lib/aidp/analyze/runner.rb +27 -5
  5. data/lib/aidp/analyze/steps.rb +4 -0
  6. data/lib/aidp/cli/jobs_command.rb +2 -1
  7. data/lib/aidp/cli.rb +812 -3
  8. data/lib/aidp/concurrency/backoff.rb +148 -0
  9. data/lib/aidp/concurrency/exec.rb +192 -0
  10. data/lib/aidp/concurrency/wait.rb +148 -0
  11. data/lib/aidp/concurrency.rb +71 -0
  12. data/lib/aidp/config.rb +20 -0
  13. data/lib/aidp/daemon/runner.rb +9 -8
  14. data/lib/aidp/debug_mixin.rb +1 -0
  15. data/lib/aidp/errors.rb +12 -0
  16. data/lib/aidp/execute/interactive_repl.rb +102 -11
  17. data/lib/aidp/execute/repl_macros.rb +776 -2
  18. data/lib/aidp/execute/runner.rb +27 -5
  19. data/lib/aidp/execute/steps.rb +2 -0
  20. data/lib/aidp/harness/config_loader.rb +24 -2
  21. data/lib/aidp/harness/enhanced_runner.rb +16 -2
  22. data/lib/aidp/harness/error_handler.rb +1 -1
  23. data/lib/aidp/harness/provider_info.rb +19 -15
  24. data/lib/aidp/harness/provider_manager.rb +47 -41
  25. data/lib/aidp/harness/runner.rb +3 -11
  26. data/lib/aidp/harness/state/persistence.rb +1 -6
  27. data/lib/aidp/harness/state_manager.rb +115 -7
  28. data/lib/aidp/harness/status_display.rb +11 -18
  29. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  30. data/lib/aidp/harness/ui/workflow_controller.rb +1 -1
  31. data/lib/aidp/harness/user_interface.rb +12 -15
  32. data/lib/aidp/jobs/background_runner.rb +15 -5
  33. data/lib/aidp/providers/codex.rb +0 -1
  34. data/lib/aidp/providers/cursor.rb +0 -1
  35. data/lib/aidp/providers/github_copilot.rb +0 -1
  36. data/lib/aidp/providers/opencode.rb +0 -1
  37. data/lib/aidp/skills/composer.rb +178 -0
  38. data/lib/aidp/skills/loader.rb +205 -0
  39. data/lib/aidp/skills/registry.rb +220 -0
  40. data/lib/aidp/skills/skill.rb +174 -0
  41. data/lib/aidp/skills.rb +30 -0
  42. data/lib/aidp/version.rb +1 -1
  43. data/lib/aidp/watch/build_processor.rb +93 -28
  44. data/lib/aidp/watch/runner.rb +3 -2
  45. data/lib/aidp/workstream_executor.rb +244 -0
  46. data/lib/aidp/workstream_state.rb +212 -0
  47. data/lib/aidp/worktree.rb +208 -0
  48. data/lib/aidp.rb +6 -0
  49. metadata +17 -4
@@ -5,6 +5,7 @@ require "tty-spinner"
5
5
  require_relative "async_work_loop_runner"
6
6
  require_relative "repl_macros"
7
7
  require_relative "../rescue_logging"
8
+ require_relative "../concurrency"
8
9
 
9
10
  module Aidp
10
11
  module Execute
@@ -32,6 +33,7 @@ module Aidp
32
33
  @repl_macros = ReplMacros.new
33
34
  @output_display_thread = nil
34
35
  @running = false
36
+ @completion_setup_needed = true
35
37
  end
36
38
 
37
39
  # Start work loop and enter interactive REPL
@@ -96,10 +98,18 @@ module Aidp
96
98
 
97
99
  # Read command with timeout to allow checking work loop status
98
100
  def read_command_with_timeout
99
- # Use TTY::Prompt's ask with timeout is not directly supported
100
- # So we'll use a simple gets with a prompt
101
- print_prompt
102
- command = $stdin.gets&.chomp
101
+ # Use Reline for readline-style editing with tab completion
102
+ require "reline"
103
+ setup_completion if @completion_setup_needed
104
+
105
+ print_prompt_text
106
+ Reline.output = $stdout
107
+ Reline.input = $stdin
108
+ Reline.completion_append_character = " "
109
+
110
+ command = Reline.readline("", false)
111
+ return nil if command.nil? # Ctrl-D
112
+
103
113
  command&.strip
104
114
  rescue => e
105
115
  log_rescue(e, component: "interactive_repl", action: "read_command", fallback: nil)
@@ -107,26 +117,106 @@ module Aidp
107
117
  nil
108
118
  end
109
119
 
110
- # Print REPL prompt
111
- def print_prompt
120
+ # Setup tab completion for REPL commands
121
+ def setup_completion
122
+ require "reline"
123
+
124
+ # Define completion proc
125
+ Reline.completion_proc = proc do |input|
126
+ # Get list of commands from repl_macros
127
+ all_commands = @repl_macros.list_commands
128
+
129
+ # Extract the word being completed
130
+ words = input.split(/\s+/)
131
+ current_word = words.last || ""
132
+
133
+ # If we're completing the first word (command), offer command names
134
+ if words.size <= 1 || (words.size == 1 && !input.end_with?(" "))
135
+ all_commands.select { |cmd| cmd.start_with?(current_word) }
136
+ # If completing after /ws, offer subcommands
137
+ elsif words.first == "/ws"
138
+ if words.size == 2 && !input.end_with?(" ")
139
+ # Completing subcommand
140
+ subcommands = %w[list new switch rm status pause resume complete dashboard pause-all resume-all stop-all]
141
+ subcommands.select { |sc| sc.start_with?(current_word) }
142
+ elsif %w[switch rm status pause resume complete].include?(words[1])
143
+ # Completing workstream slug
144
+ require_relative "../worktree"
145
+ workstreams = Aidp::Worktree.list(project_dir: @project_dir)
146
+ slugs = workstreams.map { |ws| ws[:slug] }
147
+ slugs.select { |slug| slug.start_with?(current_word) }
148
+ else
149
+ []
150
+ end
151
+ # If completing after /skill, offer subcommands or skill IDs
152
+ elsif words.first == "/skill"
153
+ if words.size == 2 && !input.end_with?(" ")
154
+ # Completing subcommand
155
+ subcommands = %w[list show search]
156
+ subcommands.select { |sc| sc.start_with?(current_word) }
157
+ elsif %w[show].include?(words[1])
158
+ # Completing skill ID
159
+ require_relative "../skills"
160
+ registry = Aidp::Skills::Registry.new(project_dir: @project_dir)
161
+ registry.load_skills
162
+ skill_ids = registry.all.map(&:id)
163
+ skill_ids.select { |id| id.start_with?(current_word) }
164
+ else
165
+ []
166
+ end
167
+ else
168
+ []
169
+ end
170
+ end
171
+
172
+ @completion_setup_needed = false
173
+ end
174
+
175
+ # Print REPL prompt text (for Reline)
176
+ def print_prompt_text
112
177
  status = @async_runner.status
113
178
  state = status[:state]
114
179
  iteration = status[:iteration]
115
180
  queued = status.dig(:queued_instructions, :total) || 0
181
+ # Workstream context (if any)
182
+ current_ws = if @repl_macros.respond_to?(:current_workstream)
183
+ @repl_macros.current_workstream
184
+ elsif @repl_macros.instance_variable_defined?(:@current_workstream)
185
+ @repl_macros.instance_variable_get(:@current_workstream)
186
+ end
187
+
188
+ ws_fragment = ""
189
+ if current_ws
190
+ begin
191
+ require_relative "../workstream_state"
192
+ ws_state = Aidp::WorkstreamState.read(slug: current_ws, project_dir: @project_dir) || {}
193
+ iter = ws_state[:iterations] || 0
194
+ elapsed = Aidp::WorkstreamState.elapsed_seconds(slug: current_ws, project_dir: @project_dir)
195
+ ws_fragment = "|ws:#{current_ws}:#{iter}i/#{elapsed}s"
196
+ rescue
197
+ ws_fragment = "|ws:#{current_ws}"
198
+ end
199
+ end
116
200
 
117
201
  prompt_text = case state
118
202
  when "RUNNING"
119
- "aidp[#{iteration}]"
203
+ "aidp[#{iteration}#{ws_fragment}]"
120
204
  when "PAUSED"
121
- "aidp[#{iteration}|PAUSED]"
205
+ "aidp[#{iteration}|PAUSED#{ws_fragment}]"
206
+ when "CANCELLED"
207
+ "aidp[#{iteration}|CANCELLED#{ws_fragment}]"
208
+ when "IDLE"
209
+ queued_suffix = (queued > 0) ? "+#{queued}" : ""
210
+ "aidp[#{iteration}#{queued_suffix}#{ws_fragment}]"
122
211
  else
123
- "aidp"
212
+ "aidp[#{iteration}#{ws_fragment}]"
124
213
  end
125
214
 
126
- prompt_text += " (#{queued} queued)" if queued > 0
127
- print "#{prompt_text}> "
215
+ prompt_text += " > "
216
+ print prompt_text
128
217
  end
129
218
 
219
+ # Print REPL prompt
130
220
  # Handle REPL command
131
221
  def handle_command(command)
132
222
  return if command.empty?
@@ -312,6 +402,7 @@ module Aidp
312
402
  @prompt.say(" /merge <plan> - Update plan/contract")
313
403
  @prompt.say(" /update guard <key>=<value> - Update guard rails")
314
404
  @prompt.say(" /rollback <n>, /undo last - Rollback commits")
405
+ @prompt.say(" /skill list, /skill show <id> - Manage skills/personas")
315
406
  @prompt.say(" /status - Show current state")
316
407
  @prompt.say(" /help - Show all commands")
317
408
  @prompt.say("\nPress Ctrl-C for interrupt menu")