aidp 0.13.0 → 0.14.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -0
  3. data/lib/aidp/cli/first_run_wizard.rb +28 -303
  4. data/lib/aidp/cli/issue_importer.rb +359 -0
  5. data/lib/aidp/cli.rb +151 -3
  6. data/lib/aidp/daemon/process_manager.rb +146 -0
  7. data/lib/aidp/daemon/runner.rb +232 -0
  8. data/lib/aidp/execute/async_work_loop_runner.rb +216 -0
  9. data/lib/aidp/execute/future_work_backlog.rb +411 -0
  10. data/lib/aidp/execute/guard_policy.rb +246 -0
  11. data/lib/aidp/execute/instruction_queue.rb +131 -0
  12. data/lib/aidp/execute/interactive_repl.rb +335 -0
  13. data/lib/aidp/execute/repl_macros.rb +651 -0
  14. data/lib/aidp/execute/steps.rb +8 -0
  15. data/lib/aidp/execute/work_loop_runner.rb +322 -36
  16. data/lib/aidp/execute/work_loop_state.rb +162 -0
  17. data/lib/aidp/harness/config_schema.rb +88 -0
  18. data/lib/aidp/harness/configuration.rb +48 -1
  19. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +2 -0
  20. data/lib/aidp/init/doc_generator.rb +256 -0
  21. data/lib/aidp/init/project_analyzer.rb +343 -0
  22. data/lib/aidp/init/runner.rb +83 -0
  23. data/lib/aidp/init.rb +5 -0
  24. data/lib/aidp/logger.rb +279 -0
  25. data/lib/aidp/setup/wizard.rb +777 -0
  26. data/lib/aidp/tooling_detector.rb +115 -0
  27. data/lib/aidp/version.rb +1 -1
  28. data/lib/aidp/watch/build_processor.rb +282 -0
  29. data/lib/aidp/watch/plan_generator.rb +166 -0
  30. data/lib/aidp/watch/plan_processor.rb +83 -0
  31. data/lib/aidp/watch/repository_client.rb +243 -0
  32. data/lib/aidp/watch/runner.rb +93 -0
  33. data/lib/aidp/watch/state_store.rb +105 -0
  34. data/lib/aidp/watch.rb +9 -0
  35. data/lib/aidp.rb +14 -0
  36. data/templates/implementation/simple_task.md +36 -0
  37. metadata +26 -1
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "net/http"
5
+ require "open3"
6
+ require "uri"
7
+
8
+ module Aidp
9
+ module Watch
10
+ # Lightweight adapter around GitHub for watch mode. Prefers the GitHub CLI
11
+ # (works for private repositories) and falls back to public REST endpoints
12
+ # when the CLI is unavailable.
13
+ class RepositoryClient
14
+ attr_reader :owner, :repo
15
+
16
+ def self.parse_issues_url(issues_url)
17
+ case issues_url
18
+ when %r{\Ahttps://github\.com/([^/]+)/([^/]+)(?:/issues)?/?\z}
19
+ [$1, $2]
20
+ when %r{\A([^/]+)/([^/]+)\z}
21
+ [$1, $2]
22
+ else
23
+ raise ArgumentError, "Unsupported issues URL: #{issues_url}"
24
+ end
25
+ end
26
+
27
+ def initialize(owner:, repo:, gh_available: nil)
28
+ @owner = owner
29
+ @repo = repo
30
+ @gh_available = gh_available.nil? ? gh_cli_available? : gh_available
31
+ end
32
+
33
+ def gh_available?
34
+ @gh_available
35
+ end
36
+
37
+ def full_repo
38
+ "#{owner}/#{repo}"
39
+ end
40
+
41
+ def list_issues(labels: [], state: "open")
42
+ gh_available? ? list_issues_via_gh(labels: labels, state: state) : list_issues_via_api(labels: labels, state: state)
43
+ end
44
+
45
+ def fetch_issue(number)
46
+ gh_available? ? fetch_issue_via_gh(number) : fetch_issue_via_api(number)
47
+ end
48
+
49
+ def post_comment(number, body)
50
+ gh_available? ? post_comment_via_gh(number, body) : post_comment_via_api(number, body)
51
+ end
52
+
53
+ def create_pull_request(title:, body:, head:, base:, issue_number:)
54
+ gh_available? ? create_pull_request_via_gh(title: title, body: body, head: head, base: base, issue_number: issue_number) : raise("GitHub CLI not available - cannot create PR")
55
+ end
56
+
57
+ private
58
+
59
+ def gh_cli_available?
60
+ _stdout, _stderr, status = Open3.capture3("gh", "--version")
61
+ status.success?
62
+ rescue Errno::ENOENT
63
+ false
64
+ end
65
+
66
+ def list_issues_via_gh(labels:, state:)
67
+ json_fields = %w[number title labels updatedAt state url assignees]
68
+ cmd = ["gh", "issue", "list", "--repo", full_repo, "--state", state, "--json", json_fields.join(",")]
69
+ labels.each do |label|
70
+ cmd += ["--label", label]
71
+ end
72
+
73
+ stdout, stderr, status = Open3.capture3(*cmd)
74
+ unless status.success?
75
+ warn("GitHub CLI list failed: #{stderr}")
76
+ return []
77
+ end
78
+
79
+ JSON.parse(stdout).map { |raw| normalize_issue(raw) }
80
+ rescue JSON::ParserError => e
81
+ warn("Failed to parse GH CLI response: #{e.message}")
82
+ []
83
+ end
84
+
85
+ def list_issues_via_api(labels:, state:)
86
+ label_param = labels.join(",")
87
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues?state=#{state}")
88
+ uri.query = [uri.query, "labels=#{URI.encode_www_form_component(label_param)}"].compact.join("&") unless label_param.empty?
89
+
90
+ response = Net::HTTP.get_response(uri)
91
+ return [] unless response.code == "200"
92
+
93
+ JSON.parse(response.body).reject { |item| item["pull_request"] }.map { |raw| normalize_issue_api(raw) }
94
+ rescue => e
95
+ warn("GitHub API list failed: #{e.message}")
96
+ []
97
+ end
98
+
99
+ def fetch_issue_via_gh(number)
100
+ fields = %w[number title body comments labels state assignees url updatedAt author]
101
+ cmd = ["gh", "issue", "view", number.to_s, "--repo", full_repo, "--json", fields.join(",")]
102
+
103
+ stdout, stderr, status = Open3.capture3(*cmd)
104
+ raise "GitHub CLI error: #{stderr.strip}" unless status.success?
105
+
106
+ data = JSON.parse(stdout)
107
+ normalize_issue_detail(data)
108
+ rescue JSON::ParserError => e
109
+ raise "Failed to parse GitHub CLI issue response: #{e.message}"
110
+ end
111
+
112
+ def fetch_issue_via_api(number)
113
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}")
114
+ response = Net::HTTP.get_response(uri)
115
+ raise "GitHub API error (#{response.code})" unless response.code == "200"
116
+
117
+ data = JSON.parse(response.body)
118
+ comments = fetch_comments_via_api(number)
119
+ data["comments"] = comments
120
+ normalize_issue_detail_api(data)
121
+ end
122
+
123
+ def fetch_comments_via_api(number)
124
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/comments")
125
+ response = Net::HTTP.get_response(uri)
126
+ return [] unless response.code == "200"
127
+
128
+ JSON.parse(response.body).map do |raw|
129
+ {
130
+ "body" => raw["body"],
131
+ "author" => raw.dig("user", "login"),
132
+ "createdAt" => raw["created_at"]
133
+ }
134
+ end
135
+ rescue
136
+ []
137
+ end
138
+
139
+ def post_comment_via_gh(number, body)
140
+ cmd = ["gh", "issue", "comment", number.to_s, "--repo", full_repo, "--body", body]
141
+ stdout, stderr, status = Open3.capture3(*cmd)
142
+ raise "Failed to post comment via gh: #{stderr.strip}" unless status.success?
143
+
144
+ stdout.strip
145
+ end
146
+
147
+ def post_comment_via_api(number, body)
148
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/comments")
149
+ request = Net::HTTP::Post.new(uri)
150
+ request["Content-Type"] = "application/json"
151
+ request.body = JSON.dump({body: body})
152
+
153
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
154
+ http.request(request)
155
+ end
156
+
157
+ raise "GitHub API comment failed (#{response.code})" unless response.code.start_with?("2")
158
+ response.body
159
+ end
160
+
161
+ def create_pull_request_via_gh(title:, body:, head:, base:, issue_number:)
162
+ cmd = [
163
+ "gh", "pr", "create",
164
+ "--repo", full_repo,
165
+ "--title", title,
166
+ "--body", body,
167
+ "--head", head,
168
+ "--base", base
169
+ ]
170
+ cmd += ["--issue", issue_number.to_s] if issue_number
171
+
172
+ stdout, stderr, status = Open3.capture3(*cmd)
173
+ raise "Failed to create PR via gh: #{stderr.strip}" unless status.success?
174
+
175
+ stdout.strip
176
+ end
177
+
178
+ def normalize_issue(raw)
179
+ {
180
+ number: raw["number"],
181
+ title: raw["title"],
182
+ labels: Array(raw["labels"]).map { |label| label.is_a?(Hash) ? label["name"] : label },
183
+ updated_at: raw["updatedAt"],
184
+ state: raw["state"],
185
+ url: raw["url"],
186
+ assignees: Array(raw["assignees"]).map { |assignee| assignee.is_a?(Hash) ? assignee["login"] : assignee }
187
+ }
188
+ end
189
+
190
+ def normalize_issue_api(raw)
191
+ {
192
+ number: raw["number"],
193
+ title: raw["title"],
194
+ labels: Array(raw["labels"]).map { |label| label["name"] },
195
+ updated_at: raw["updated_at"],
196
+ state: raw["state"],
197
+ url: raw["html_url"],
198
+ assignees: Array(raw["assignees"]).map { |assignee| assignee["login"] }
199
+ }
200
+ end
201
+
202
+ def normalize_issue_detail(raw)
203
+ {
204
+ number: raw["number"],
205
+ title: raw["title"],
206
+ body: raw["body"] || "",
207
+ comments: Array(raw["comments"]).map { |comment| normalize_comment(comment) },
208
+ labels: Array(raw["labels"]).map { |label| label.is_a?(Hash) ? label["name"] : label },
209
+ state: raw["state"],
210
+ assignees: Array(raw["assignees"]).map { |assignee| assignee.is_a?(Hash) ? assignee["login"] : assignee },
211
+ url: raw["url"],
212
+ updated_at: raw["updatedAt"]
213
+ }
214
+ end
215
+
216
+ def normalize_issue_detail_api(raw)
217
+ {
218
+ number: raw["number"],
219
+ title: raw["title"],
220
+ body: raw["body"] || "",
221
+ comments: Array(raw["comments"]).map { |comment| normalize_comment(comment) },
222
+ labels: Array(raw["labels"]).map { |label| label["name"] },
223
+ state: raw["state"],
224
+ assignees: Array(raw["assignees"]).map { |assignee| assignee["login"] },
225
+ url: raw["html_url"],
226
+ updated_at: raw["updated_at"]
227
+ }
228
+ end
229
+
230
+ def normalize_comment(comment)
231
+ if comment.is_a?(Hash)
232
+ {
233
+ "body" => comment["body"],
234
+ "author" => comment["author"] || comment.dig("user", "login"),
235
+ "createdAt" => comment["createdAt"] || comment["created_at"]
236
+ }
237
+ else
238
+ {"body" => comment.to_s}
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+
5
+ require_relative "../message_display"
6
+ require_relative "repository_client"
7
+ require_relative "state_store"
8
+ require_relative "plan_generator"
9
+ require_relative "plan_processor"
10
+ require_relative "build_processor"
11
+
12
+ module Aidp
13
+ module Watch
14
+ # Coordinates the watch mode loop: monitors issues, handles plan/build
15
+ # triggers, and keeps running until interrupted.
16
+ class Runner
17
+ include Aidp::MessageDisplay
18
+
19
+ DEFAULT_INTERVAL = 30
20
+
21
+ def initialize(issues_url:, interval: DEFAULT_INTERVAL, provider_name: nil, gh_available: nil, project_dir: Dir.pwd, once: false, prompt: TTY::Prompt.new)
22
+ @prompt = prompt
23
+ @interval = interval
24
+ @once = once
25
+ @project_dir = project_dir
26
+
27
+ owner, repo = RepositoryClient.parse_issues_url(issues_url)
28
+ @repository_client = RepositoryClient.new(owner: owner, repo: repo, gh_available: gh_available)
29
+ @state_store = StateStore.new(project_dir: project_dir, repository: "#{owner}/#{repo}")
30
+ @plan_processor = PlanProcessor.new(
31
+ repository_client: @repository_client,
32
+ state_store: @state_store,
33
+ plan_generator: PlanGenerator.new(provider_name: provider_name)
34
+ )
35
+ @build_processor = BuildProcessor.new(
36
+ repository_client: @repository_client,
37
+ state_store: @state_store,
38
+ project_dir: project_dir
39
+ )
40
+ end
41
+
42
+ def start
43
+ display_message("👀 Watch mode enabled for #{@repository_client.full_repo}", type: :highlight)
44
+ display_message("Polling every #{@interval} seconds. Press Ctrl+C to stop.", type: :muted)
45
+
46
+ loop do
47
+ process_cycle
48
+ break if @once
49
+ sleep @interval
50
+ end
51
+ rescue Interrupt
52
+ display_message("\n⏹️ Watch mode interrupted by user", type: :warning)
53
+ end
54
+
55
+ private
56
+
57
+ def process_cycle
58
+ process_plan_triggers
59
+ process_build_triggers
60
+ end
61
+
62
+ def process_plan_triggers
63
+ issues = @repository_client.list_issues(labels: [PlanProcessor::PLAN_LABEL], state: "open")
64
+ issues.each do |issue|
65
+ next unless issue_has_label?(issue, PlanProcessor::PLAN_LABEL)
66
+
67
+ detailed = @repository_client.fetch_issue(issue[:number])
68
+ @plan_processor.process(detailed)
69
+ end
70
+ end
71
+
72
+ def process_build_triggers
73
+ issues = @repository_client.list_issues(labels: [BuildProcessor::BUILD_LABEL], state: "open")
74
+ issues.each do |issue|
75
+ next unless issue_has_label?(issue, BuildProcessor::BUILD_LABEL)
76
+
77
+ status = @state_store.build_status(issue[:number])
78
+ next if status["status"] == "completed"
79
+
80
+ detailed = @repository_client.fetch_issue(issue[:number])
81
+ @build_processor.process(detailed)
82
+ end
83
+ end
84
+
85
+ def issue_has_label?(issue, label)
86
+ Array(issue[:labels]).any? do |issue_label|
87
+ name = issue_label.is_a?(Hash) ? issue_label["name"] : issue_label.to_s
88
+ name.casecmp(label).zero?
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+ require "time"
6
+
7
+ module Aidp
8
+ module Watch
9
+ # Persists watch mode progress for each repository/issue pair. Used to
10
+ # avoid re-processing plan/build triggers and to retain generated plan
11
+ # context between runs.
12
+ class StateStore
13
+ attr_reader :path
14
+
15
+ def initialize(project_dir:, repository:)
16
+ @project_dir = project_dir
17
+ @repository = repository
18
+ @path = File.join(project_dir, ".aidp", "watch", "#{sanitize_repository(repository)}.yml")
19
+ ensure_directory
20
+ end
21
+
22
+ def plan_processed?(issue_number)
23
+ plans.key?(issue_number.to_s)
24
+ end
25
+
26
+ def plan_data(issue_number)
27
+ plans[issue_number.to_s]
28
+ end
29
+
30
+ def record_plan(issue_number, data)
31
+ payload = {
32
+ "summary" => data[:summary],
33
+ "tasks" => data[:tasks],
34
+ "questions" => data[:questions],
35
+ "comment_body" => data[:comment_body],
36
+ "comment_hint" => data[:comment_hint],
37
+ "posted_at" => data[:posted_at] || Time.now.utc.iso8601
38
+ }.compact
39
+
40
+ plans[issue_number.to_s] = payload
41
+ save!
42
+ end
43
+
44
+ def build_status(issue_number)
45
+ builds[issue_number.to_s] || {}
46
+ end
47
+
48
+ def record_build_status(issue_number, status:, details: {})
49
+ builds[issue_number.to_s] = {
50
+ "status" => status,
51
+ "updated_at" => Time.now.utc.iso8601
52
+ }.merge(stringify_keys(details))
53
+ save!
54
+ end
55
+
56
+ private
57
+
58
+ def ensure_directory
59
+ FileUtils.mkdir_p(File.dirname(@path))
60
+ end
61
+
62
+ def sanitize_repository(repository)
63
+ repository.tr("/", "_")
64
+ end
65
+
66
+ def load_state
67
+ @state ||= if File.exist?(@path)
68
+ YAML.safe_load_file(@path, permitted_classes: [Time]) || {}
69
+ else
70
+ {}
71
+ end
72
+ end
73
+
74
+ def save!
75
+ File.write(@path, YAML.dump(state))
76
+ end
77
+
78
+ def state
79
+ @state = nil if @state && !@state.is_a?(Hash)
80
+ @state ||= begin
81
+ base = load_state
82
+ base["plans"] ||= {}
83
+ base["builds"] ||= {}
84
+ base
85
+ end
86
+ end
87
+
88
+ def plans
89
+ state["plans"]
90
+ end
91
+
92
+ def builds
93
+ state["builds"]
94
+ end
95
+
96
+ def stringify_keys(hash)
97
+ return {} unless hash
98
+
99
+ hash.each_with_object({}) do |(key, value), memo|
100
+ memo[key.to_s] = value
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
data/lib/aidp/watch.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "message_display"
4
+ require_relative "watch/repository_client"
5
+ require_relative "watch/state_store"
6
+ require_relative "watch/plan_generator"
7
+ require_relative "watch/plan_processor"
8
+ require_relative "watch/build_processor"
9
+ require_relative "watch/runner"
data/lib/aidp.rb CHANGED
@@ -8,6 +8,9 @@ require_relative "aidp/version"
8
8
  require_relative "aidp/config"
9
9
  require_relative "aidp/util"
10
10
  require_relative "aidp/message_display"
11
+ require_relative "aidp/setup/wizard"
12
+ require_relative "aidp/init"
13
+ require_relative "aidp/watch"
11
14
  require_relative "aidp/cli"
12
15
 
13
16
  # Jobs and background execution
@@ -54,6 +57,17 @@ require_relative "aidp/execute/runner"
54
57
  require_relative "aidp/execute/progress"
55
58
  require_relative "aidp/execute/checkpoint"
56
59
  require_relative "aidp/execute/checkpoint_display"
60
+ require_relative "aidp/execute/work_loop_state"
61
+ require_relative "aidp/execute/instruction_queue"
62
+ require_relative "aidp/execute/async_work_loop_runner"
63
+ require_relative "aidp/execute/interactive_repl"
64
+
65
+ # Logging
66
+ require_relative "aidp/logger"
67
+
68
+ # Daemon mode
69
+ require_relative "aidp/daemon/process_manager"
70
+ require_relative "aidp/daemon/runner"
57
71
 
58
72
  # Harness mode
59
73
  require_relative "aidp/harness/configuration"
@@ -0,0 +1,36 @@
1
+ # Simple Task Execution
2
+
3
+ You are executing a simple, focused task within the AIDP work loop.
4
+
5
+ ## Your Task
6
+
7
+ {{task_description}}
8
+
9
+ ## Important Instructions
10
+
11
+ 1. **Read the task above carefully**
12
+ 2. **Execute the task exactly as described**
13
+ 3. **Verify your work** by running any validation commands specified
14
+ 4. **Edit this PROMPT.md** to track progress and mark complete when done
15
+
16
+ ## Completion Criteria
17
+
18
+ When the task is 100% complete:
19
+
20
+ 1. The task description requirements are fully met
21
+ 2. Any specified validation commands pass
22
+ 3. You've added this line to PROMPT.md:
23
+
24
+ ```
25
+ STATUS: COMPLETE
26
+ ```
27
+
28
+ ## Context
29
+
30
+ {{additional_context}}
31
+
32
+ ## Notes
33
+
34
+ - Keep your changes minimal and focused on the task
35
+ - If the task involves running commands, show the command output
36
+ - If the task involves fixing issues, list what was fixed
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aidp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -250,21 +250,31 @@ files:
250
250
  - lib/aidp/cli/checkpoint_command.rb
251
251
  - lib/aidp/cli/enhanced_input.rb
252
252
  - lib/aidp/cli/first_run_wizard.rb
253
+ - lib/aidp/cli/issue_importer.rb
253
254
  - lib/aidp/cli/jobs_command.rb
254
255
  - lib/aidp/cli/mcp_dashboard.rb
255
256
  - lib/aidp/cli/terminal_io.rb
256
257
  - lib/aidp/config.rb
257
258
  - lib/aidp/config/paths.rb
258
259
  - lib/aidp/core_ext/class_attribute.rb
260
+ - lib/aidp/daemon/process_manager.rb
261
+ - lib/aidp/daemon/runner.rb
259
262
  - lib/aidp/debug_logger.rb
260
263
  - lib/aidp/debug_mixin.rb
264
+ - lib/aidp/execute/async_work_loop_runner.rb
261
265
  - lib/aidp/execute/checkpoint.rb
262
266
  - lib/aidp/execute/checkpoint_display.rb
267
+ - lib/aidp/execute/future_work_backlog.rb
268
+ - lib/aidp/execute/guard_policy.rb
269
+ - lib/aidp/execute/instruction_queue.rb
270
+ - lib/aidp/execute/interactive_repl.rb
263
271
  - lib/aidp/execute/progress.rb
264
272
  - lib/aidp/execute/prompt_manager.rb
273
+ - lib/aidp/execute/repl_macros.rb
265
274
  - lib/aidp/execute/runner.rb
266
275
  - lib/aidp/execute/steps.rb
267
276
  - lib/aidp/execute/work_loop_runner.rb
277
+ - lib/aidp/execute/work_loop_state.rb
268
278
  - lib/aidp/execute/workflow_selector.rb
269
279
  - lib/aidp/harness/completion_checker.rb
270
280
  - lib/aidp/harness/condition_detector.rb
@@ -311,7 +321,12 @@ files:
311
321
  - lib/aidp/harness/ui/status_widget.rb
312
322
  - lib/aidp/harness/ui/workflow_controller.rb
313
323
  - lib/aidp/harness/user_interface.rb
324
+ - lib/aidp/init.rb
325
+ - lib/aidp/init/doc_generator.rb
326
+ - lib/aidp/init/project_analyzer.rb
327
+ - lib/aidp/init/runner.rb
314
328
  - lib/aidp/jobs/background_runner.rb
329
+ - lib/aidp/logger.rb
315
330
  - lib/aidp/message_display.rb
316
331
  - lib/aidp/provider_manager.rb
317
332
  - lib/aidp/providers/anthropic.rb
@@ -322,11 +337,20 @@ files:
322
337
  - lib/aidp/providers/github_copilot.rb
323
338
  - lib/aidp/providers/macos_ui.rb
324
339
  - lib/aidp/providers/opencode.rb
340
+ - lib/aidp/setup/wizard.rb
325
341
  - lib/aidp/storage/csv_storage.rb
326
342
  - lib/aidp/storage/file_manager.rb
327
343
  - lib/aidp/storage/json_storage.rb
344
+ - lib/aidp/tooling_detector.rb
328
345
  - lib/aidp/util.rb
329
346
  - lib/aidp/version.rb
347
+ - lib/aidp/watch.rb
348
+ - lib/aidp/watch/build_processor.rb
349
+ - lib/aidp/watch/plan_generator.rb
350
+ - lib/aidp/watch/plan_processor.rb
351
+ - lib/aidp/watch/repository_client.rb
352
+ - lib/aidp/watch/runner.rb
353
+ - lib/aidp/watch/state_store.rb
330
354
  - lib/aidp/workflows/definitions.rb
331
355
  - lib/aidp/workflows/guided_agent.rb
332
356
  - lib/aidp/workflows/selector.rb
@@ -356,6 +380,7 @@ files:
356
380
  - templates/implementation/plan_delivery.md
357
381
  - templates/implementation/review_post_release.md
358
382
  - templates/implementation/setup_scaffolding.md
383
+ - templates/implementation/simple_task.md
359
384
  - templates/planning/ask_architecture_questions.md
360
385
  - templates/planning/create_prd.md
361
386
  - templates/planning/create_tasks.md