aidp 0.44.0 → 0.46.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.
@@ -15,7 +15,11 @@ module Aidp
15
15
  priority: "Priority",
16
16
  skills: "Skills",
17
17
  personas: "Personas",
18
- blocking: "Blocking"
18
+ blocking: "Blocking",
19
+ start_date: "Start Date",
20
+ target_date: "Target Date",
21
+ dependencies: "Dependencies",
22
+ critical_path: "Critical Path"
19
23
  }.freeze
20
24
 
21
25
  # Status values for different issue states
@@ -27,17 +31,25 @@ module Aidp
27
31
  done: "Done",
28
32
  blocked: "Blocked"
29
33
  }.freeze
34
+ CRITICAL_PATH_VALUES = %w[Yes No].freeze
30
35
 
31
36
  attr_reader :repository_client, :state_store, :project_id
32
37
 
33
- def initialize(repository_client:, state_store:, project_id:, config: {})
38
+ def initialize(repository_client:, state_store:, project_id:, config: {}, gantt_synchronizer: nil)
34
39
  @repository_client = repository_client
35
40
  @state_store = state_store
36
41
  @project_id = project_id
37
- @config = config
38
- @field_mappings = config[:field_mappings] || DEFAULT_FIELD_MAPPINGS
39
- @auto_create_fields = config[:auto_create_fields] != false
42
+ @config = normalize_config(config)
43
+ @field_mappings = DEFAULT_FIELD_MAPPINGS.merge(@config[:field_mappings] || {})
44
+ @auto_create_fields = @config[:auto_create_fields] != false
40
45
  @project_fields_cache = nil
46
+ @gantt_synchronizer = gantt_synchronizer || GanttSynchronizer.new(
47
+ repository_client: repository_client,
48
+ state_store: state_store,
49
+ project_id: project_id,
50
+ field_mappings: @field_mappings,
51
+ auto_create_fields: @auto_create_fields
52
+ )
41
53
  end
42
54
 
43
55
  # Sync a single issue to the project
@@ -66,7 +78,7 @@ module Aidp
66
78
 
67
79
  # Update status if provided
68
80
  if status
69
- update_issue_status(issue_number, status)
81
+ return false unless update_issue_status(issue_number, status)
70
82
  end
71
83
 
72
84
  # Check and update blocking status
@@ -76,6 +88,7 @@ module Aidp
76
88
  last_sync: Time.now.utc.iso8601,
77
89
  status: status
78
90
  })
91
+ sync_issue_status_to_gantt(issue_number, status) if status && gantt_sync_enabled?
79
92
 
80
93
  true
81
94
  rescue => e
@@ -178,6 +191,16 @@ module Aidp
178
191
  {synced: synced, failed: failed}
179
192
  end
180
193
 
194
+ def sync_from_gantt(prd_path = configured_prd_path, issue_numbers_by_title: {})
195
+ return {synced: 0, skipped: 0, critical_path: []} unless prd_path
196
+
197
+ @gantt_synchronizer.sync_from_prd(
198
+ prd_path: prd_path,
199
+ format: configured_gantt_format,
200
+ issue_numbers_by_title: issue_numbers_by_title
201
+ )
202
+ end
203
+
181
204
  # Initialize required project fields if they don't exist
182
205
  # @return [Boolean] True if all fields are ready
183
206
  def ensure_project_fields
@@ -185,11 +208,6 @@ module Aidp
185
208
 
186
209
  Aidp.log_debug("projects_processor", "ensure_project_fields", project_id: @project_id)
187
210
 
188
- required_fields = [
189
- {name: @field_mappings[:status], type: "SINGLE_SELECT", options: STATUS_VALUES.values},
190
- {name: @field_mappings[:blocking], type: "TEXT"}
191
- ]
192
-
193
211
  all_ready = true
194
212
  required_fields.each do |field_spec|
195
213
  field = find_or_create_field(field_spec[:name], field_spec[:type], field_spec[:options])
@@ -210,6 +228,19 @@ module Aidp
210
228
  end
211
229
  end
212
230
 
231
+ def normalize_config(value)
232
+ case value
233
+ when Hash
234
+ value.each_with_object({}) do |(key, nested_value), normalized|
235
+ normalized[key.to_sym] = normalize_config(nested_value)
236
+ end
237
+ when Array
238
+ value.map { |item| normalize_config(item) }
239
+ else
240
+ value
241
+ end
242
+ end
243
+
213
244
  def invalidate_fields_cache
214
245
  @project_fields_cache = nil
215
246
  end
@@ -281,6 +312,52 @@ module Aidp
281
312
  def clear_blocking_field(issue_number)
282
313
  update_blocking_field(issue_number, [])
283
314
  end
315
+
316
+ def gantt_sync_enabled?
317
+ @config[:auto_sync_gantt] == true && !configured_prd_path.to_s.empty?
318
+ end
319
+
320
+ def required_fields
321
+ [
322
+ {name: @field_mappings[:status], type: "SINGLE_SELECT", options: STATUS_VALUES.values},
323
+ {name: @field_mappings[:blocking], type: "TEXT"}
324
+ ] + gantt_required_fields
325
+ end
326
+
327
+ def gantt_required_fields
328
+ return [] unless gantt_sync_enabled?
329
+
330
+ [
331
+ {name: @field_mappings[:start_date], type: "DATE"},
332
+ {name: @field_mappings[:target_date], type: "DATE"},
333
+ {name: @field_mappings[:dependencies], type: "TEXT"},
334
+ {name: @field_mappings[:critical_path], type: "SINGLE_SELECT", options: CRITICAL_PATH_VALUES}
335
+ ]
336
+ end
337
+
338
+ def configured_prd_path
339
+ @config[:prd_path]
340
+ end
341
+
342
+ def configured_gantt_format
343
+ format = @config[:gantt_format]
344
+ return nil if format.nil? || format.to_s == "auto"
345
+
346
+ format
347
+ end
348
+
349
+ def sync_issue_status_to_gantt(issue_number, status)
350
+ @gantt_synchronizer.sync_issue_status_to_gantt(
351
+ prd_path: configured_prd_path,
352
+ issue_number: issue_number,
353
+ status: status,
354
+ format: configured_gantt_format
355
+ )
356
+ rescue => e
357
+ Aidp.log_warn("projects_processor", "gantt_status_sync_failed",
358
+ issue_number: issue_number, error: e.message)
359
+ false
360
+ end
284
361
  end
285
362
  end
286
363
  end
@@ -1644,6 +1644,21 @@ module Aidp
1644
1644
  }
1645
1645
  }
1646
1646
  GRAPHQL
1647
+ elsif value.is_a?(Hash) && value[:date]
1648
+ <<~GRAPHQL
1649
+ mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $date: Date!) {
1650
+ updateProjectV2ItemFieldValue(input: {
1651
+ projectId: $projectId
1652
+ itemId: $itemId
1653
+ fieldId: $fieldId
1654
+ value: {date: $date}
1655
+ }) {
1656
+ projectV2Item {
1657
+ id
1658
+ }
1659
+ }
1660
+ }
1661
+ GRAPHQL
1647
1662
  else
1648
1663
  # Text field
1649
1664
  <<~GRAPHQL
@@ -1675,8 +1690,10 @@ module Aidp
1675
1690
 
1676
1691
  if value.is_a?(Hash) && value[:option_id]
1677
1692
  variables[:optionId] = value[:option_id]
1693
+ elsif value.is_a?(Hash) && value[:date]
1694
+ variables[:date] = value[:date]
1678
1695
  else
1679
- variables[:text] = value.to_s
1696
+ variables[:text] = value.is_a?(Hash) ? value[:text].to_s : value.to_s
1680
1697
  end
1681
1698
 
1682
1699
  result = execute_graphql_query(mutation, **variables)
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.44.0
4
+ version: 0.46.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -551,6 +551,7 @@ files:
551
551
  - lib/aidp/security/work_loop_adapter.rb
552
552
  - lib/aidp/setup/devcontainer/backup_manager.rb
553
553
  - lib/aidp/setup/devcontainer/generator.rb
554
+ - lib/aidp/setup/devcontainer/jsonc_document.rb
554
555
  - lib/aidp/setup/devcontainer/parser.rb
555
556
  - lib/aidp/setup/devcontainer/port_manager.rb
556
557
  - lib/aidp/setup/in_memory_config_adapter.rb
@@ -609,11 +610,13 @@ files:
609
610
  - lib/aidp/watch/ci_fix_processor.rb
610
611
  - lib/aidp/watch/ci_log_extractor.rb
611
612
  - lib/aidp/watch/feedback_collector.rb
613
+ - lib/aidp/watch/gantt_synchronizer.rb
612
614
  - lib/aidp/watch/github_state_extractor.rb
613
615
  - lib/aidp/watch/hierarchical_pr_strategy.rb
614
616
  - lib/aidp/watch/implementation_verifier.rb
615
617
  - lib/aidp/watch/plan_generator.rb
616
618
  - lib/aidp/watch/plan_processor.rb
619
+ - lib/aidp/watch/prd_parser.rb
617
620
  - lib/aidp/watch/projects_processor.rb
618
621
  - lib/aidp/watch/rebase_processor.rb
619
622
  - lib/aidp/watch/repository_client.rb