Class: Aidp::Watch::GitHubStateExtractor

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/watch/github_state_extractor.rb

Overview

Extracts state information from GitHub issues/PRs using labels and comments as the single source of truth, enabling multiple AIDP instances to coordinate without local state files.

Constant Summary collapse

COMPLETION_PATTERN =

Pattern for detecting completion comments

/✅ Implementation complete for #(\d+)/i
DETECTION_PATTERN =

Pattern for detecting detection comments

/aidp detected `([^`]+)` at ([^\n]+) and is working on it/i
PLAN_PROPOSAL_PATTERN =

Pattern for detecting plan proposal comments

/<!-- PLAN_SUMMARY_START -->/i

Instance Method Summary collapse

Constructor Details

#initialize(repository_client:) ⇒ GitHubStateExtractor

Returns a new instance of GitHubStateExtractor.



20
21
22
# File 'lib/aidp/watch/github_state_extractor.rb', line 20

def initialize(repository_client:)
  @repository_client = repository_client
end

Instance Method Details

#build_completed?(issue) ⇒ Boolean

Check if build is already completed for an issue Looks for completion comment from AIDP

Returns:

  • (Boolean)


26
27
28
29
30
31
32
# File 'lib/aidp/watch/github_state_extractor.rb', line 26

def build_completed?(issue)
  return false unless issue[:comments]

  issue[:comments].any? do |comment|
    comment["body"]&.match?(COMPLETION_PATTERN)
  end
end

#change_request_processed?(pr) ⇒ Boolean

Check if change request has been processed for a PR

Returns:

  • (Boolean)


104
105
106
107
108
109
110
111
112
113
# File 'lib/aidp/watch/github_state_extractor.rb', line 104

def change_request_processed?(pr)
  return false unless pr[:comments]

  pr[:comments].any? do |comment|
    body = comment["body"]
    next false unless body

    body.include?("") && body.match?(/Change requests? (?:addressed|applied|complete)/i)
  end
end

#ci_fix_completed?(pr) ⇒ Boolean

Check if CI fix has been completed for a PR

Returns:

  • (Boolean)


89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/aidp/watch/github_state_extractor.rb', line 89

def ci_fix_completed?(pr)
  return false unless pr[:comments]

  pr[:comments].any? do |comment|
    body = comment["body"]
    next false unless body

    body.include?("") && (
      body.match?(/CI fixes applied/i) ||
      (body.match?(/CI check/i) && body.match?(/passed/i))
    )
  end
end

#detection_comment_posted?(item, label) ⇒ Boolean

Check if detection comment was already posted for this label

Returns:

  • (Boolean)


68
69
70
71
72
73
74
75
76
77
# File 'lib/aidp/watch/github_state_extractor.rb', line 68

def detection_comment_posted?(item, label)
  return false unless item[:comments]

  item[:comments].any? do |comment|
    next unless comment["body"]

    match = comment["body"].match(DETECTION_PATTERN)
    match && match[1] == label
  end
end

#extract_linked_issue(pr_body) ⇒ Object

Extract linked issue number from PR description Looks for patterns like "Fixes #123", "Closes #456", "Resolves #789" Returns the issue number as an integer, or nil if not found



118
119
120
121
122
123
124
125
# File 'lib/aidp/watch/github_state_extractor.rb', line 118

def extract_linked_issue(pr_body)
  return nil unless pr_body

  # Match common GitHub issue linking patterns
  # https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue
  match = pr_body.match(/(?:Fixes|Closes|Resolves|Close|Fix|Resolve)\s+#(\d+)/i)
  match ? match[1].to_i : nil
end

#extract_plan_data(issue) ⇒ Object

Extract the most recent plan data from comments



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/aidp/watch/github_state_extractor.rb', line 44

def extract_plan_data(issue)
  return nil unless issue[:comments]

  # Find the most recent plan proposal comment
  plan_comment = issue[:comments].reverse.find do |comment|
    comment["body"]&.match?(PLAN_PROPOSAL_PATTERN)
  end

  return nil unless plan_comment

  body = plan_comment["body"]

  {
    summary: extract_section(body, "PLAN_SUMMARY"),
    tasks: extract_tasks(body),
    questions: extract_questions(body),
    comment_body: body,
    comment_hint: "## 🤖 AIDP Plan Proposal",
    comment_id: plan_comment["id"],
    posted_at: plan_comment["createdAt"] || Time.now.utc.iso8601
  }
end

#plan_posted?(issue) ⇒ Boolean

Check if a plan has been posted for an issue

Returns:

  • (Boolean)


35
36
37
38
39
40
41
# File 'lib/aidp/watch/github_state_extractor.rb', line 35

def plan_posted?(issue)
  return false unless issue[:comments]

  issue[:comments].any? do |comment|
    comment["body"]&.match?(PLAN_PROPOSAL_PATTERN)
  end
end

#review_completed?(pr) ⇒ Boolean

Check if review has been completed for a PR

Returns:

  • (Boolean)


80
81
82
83
84
85
86
# File 'lib/aidp/watch/github_state_extractor.rb', line 80

def review_completed?(pr)
  return false unless pr[:comments]

  pr[:comments].any? do |comment|
    comment["body"]&.match?(/🔍.*Review complete/i)
  end
end