@openhands/extensions 0.4.0 → 0.4.2

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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.4.0"
2
+ ".": "0.4.2"
3
3
  }
@@ -2,12 +2,12 @@
2
2
  "id": "github-pr-reviewer",
3
3
  "name": "GitHub Code Review Agent",
4
4
  "category": "Code review",
5
- "description": "Watch pull requests, inspect the diff, and leave a concise review with risks and suggested follow-ups.",
5
+ "description": "Watch for a configurable label on GitHub pull requests, inspect full PR and repository context, and post an AI review comment once per label event.",
6
6
  "requiredIntegrationIds": [
7
7
  "github"
8
8
  ],
9
9
  "popularityRank": 100,
10
10
  "estimatedSetupMinutes": 4,
11
11
  "prompt": "/pr-reviewer:setup",
12
- "exampleImplementation": "Trigger: github.pull_request.opened and github.pull_request.synchronize\nRequired MCP: GitHub\n\n1. Read repository, pull_request, and sender from the event payload.\n2. Fetch the PR diff and related comments through the GitHub MCP.\n3. Run a review rubric focused on bugs, security, tests, and maintainability.\n4. TODO: decide whether to publish inline comments, a summary comment, or a draft artifact.\n5. Post or save the review with links back to the exact files and lines."
12
+ "exampleImplementation": "Trigger: cron polling for open GitHub PRs with a configured label such as openhands-review\nRequired secret: GITHUB_PERSONAL_ACCESS_TOKEN\n\n1. Read repository, trigger label, review tone, and polling schedule from setup.\n2. List open PRs and find the latest matching GitHub labeled issue event for each labeled PR.\n3. Deduplicate on the label event ID so every label application queues exactly one review.\n4. Start an OpenHands conversation that clones the repo, checks out the exact PR head SHA, and inspects PR discussion, review comments, changed files, diff, and surrounding code.\n5. Post an acknowledgement with the conversation link, then post the final AI review comment only if the PR is still open and the head SHA has not changed."
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/extensions",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Public OpenHands extension catalogs for skills, plugins, integrations, and automation templates.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -160,7 +160,7 @@ runs:
160
160
  # so we enforce the time limit via the coreutils timeout command.
161
161
  TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
162
162
  timeout "${TIMEOUT_SECONDS}" \
163
- uv run --no-project --with openhands-sdk --with openhands-tools --with lmnr \
163
+ uv run --no-project --with openhands-sdk --with openhands-tools --with 'lmnr<0.7.53' \
164
164
  python ../extensions/plugins/qa-changes/scripts/agent_script.py
165
165
 
166
166
  - name: Upload logs as artifact
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "extensions"
3
- version = "0.4.0"
3
+ version = "0.4.2"
4
4
  description = "OpenHands extensions, plugins, and skills"
5
5
  requires-python = ">=3.12"
6
6
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "github-pr-reviewer",
3
3
  "version": "1.0.0",
4
- "description": "Create an automation that reviews GitHub pull requests when they are opened or updated. Inspects the diff, changed files, tests, and existing discussion via GitHub MCP, then posts a concise review highlighting risks, security issues, missing tests, and next steps.",
4
+ "description": "Create an automation that reviews GitHub pull requests when a configurable trigger label is applied. It starts one OpenHands review conversation per label event, inspects full PR and repository context, and posts the final review comment back to GitHub.",
5
5
  "author": {
6
6
  "name": "OpenHands",
7
7
  "email": "contact@all-hands.dev"
@@ -1,34 +1,40 @@
1
1
  # GitHub PR Reviewer
2
2
 
3
- Create an automation that reviews GitHub pull requests on open or update.
3
+ Create an automation that reviews GitHub pull requests when a configurable
4
+ trigger label is applied.
4
5
 
5
- ## Triggers
6
+ ## Trigger
6
7
 
7
- This skill is activated by keywords:
8
+ This skill is activated by:
8
9
 
9
- - `review pull requests`
10
- - `PR review automation`
11
- - `auto-review PRs`
10
+ - `/pr-reviewer:setup`
12
11
 
13
12
  ## Features
14
13
 
15
- - **Inspects PR diff, changed files, and test coverage**
16
- - **Posts review with correctness risks, security issues, missing tests**
17
- - **Supports event-based (webhook) or cron-based (polling) triggers**
18
- - **Configurable review tone (thorough, concise, friendly)**
19
- - **Auto-post or draft mode for human approval**
14
+ - Reviews PRs on demand by watching for a GitHub label event
15
+ - Processes each label application exactly once, with persistent state
16
+ - Re-review support by removing and re-applying the label
17
+ - Suppresses stale reviews when the PR head commit changes mid-review
18
+ - Uses a real cloned checkout and full PR context instead of only a truncated diff
19
+ - Posts acknowledgement and final review comments with AI disclosure
20
+ - Configurable review tone and polling schedule
20
21
 
21
22
  ## Prerequisites
22
23
 
23
- GitHub MCP installed in Settings → MCP
24
+ Set `GITHUB_PERSONAL_ACCESS_TOKEN` in OpenHands Settings -> Secrets. The token
25
+ must be able to read the repository, read pull requests, read issue events, and
26
+ write issue comments.
24
27
 
25
28
  ## Quick Start
26
29
 
27
30
  Ask OpenHands:
28
31
 
29
- > "Set up a PR review automation for my myorg/backend repo that posts
30
- > concise reviews when PRs are opened"
32
+ > "Set up a PR review automation for my `myorg/backend` repo using the
33
+ > `openhands-review` label and concise reviews."
34
+
35
+ After setup, apply the configured label to a pull request to queue a review. To
36
+ request another review later, remove and re-apply the label.
31
37
 
32
38
  ## See Also
33
39
 
34
- - [SKILL.md](SKILL.md) — Full setup workflow reference
40
+ - [SKILL.md](SKILL.md) - Full setup workflow reference
@@ -1,89 +1,251 @@
1
1
  ---
2
2
  name: github-pr-reviewer
3
3
  description: >
4
- Create an automation that reviews GitHub pull requests when they are opened
5
- or updated. Inspects the diff, changed files, tests, and existing discussion
6
- via GitHub MCP, then posts a concise review highlighting risks, security
7
- issues, missing tests, and next steps.
4
+ Create an automation that reviews GitHub pull requests when a configurable
5
+ trigger label is applied. Polls GitHub deterministically, starts one
6
+ OpenHands review conversation per label event, inspects full repository and
7
+ PR context, and posts the final review comment back to GitHub.
8
8
  triggers:
9
9
  - /pr-reviewer:setup
10
10
  ---
11
11
 
12
12
  # GitHub PR Reviewer Automation
13
13
 
14
- Set up a cron or event-triggered automation that reviews GitHub pull requests.
14
+ Create a cron automation that watches a GitHub repository for pull requests
15
+ with a review trigger label, starts an OpenHands review conversation once per
16
+ label event, and posts the AI review as a GitHub comment.
17
+
18
+ The automation script is deterministic: PR discovery, label-event tracking,
19
+ state persistence, stale-result suppression, and GitHub comment posting are
20
+ handled in Python. The LLM is invoked only for the review itself.
15
21
 
16
22
  ---
17
23
 
18
24
  ## Prerequisites
19
25
 
20
- ### Required integration
26
+ ### Required secret
21
27
 
22
- - **GitHub MCP** must be installed in Settings → MCP.
28
+ Verify that the following secret is set in **OpenHands Settings -> Secrets**:
23
29
 
24
- ### Information to collect
30
+ | Secret name | Token type | Minimum permissions |
31
+ |---|---|---|
32
+ | `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` for private repos or `public_repo` for public repos |
33
+ | `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read, Metadata: Read, Pull requests: Read, Issues: Read and Write |
25
34
 
26
- Ask the user for:
35
+ Check with:
36
+ ```bash
37
+ curl -s https://api.github.com/user \
38
+ -H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
39
+ | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))"
40
+ ```
27
41
 
28
- 1. **Repositories** — which repos should be watched (e.g. `myorg/backend`, `myorg/frontend`)
29
- 2. **Trigger type** — event-based (reacts to `pull_request.opened` / `pull_request.synchronize`) or cron-based polling
30
- 3. **Review tone** — thorough, concise, or friendly
31
- 4. **Auto-post or draft** — should reviews be posted as GitHub comments automatically, or saved as draft artifacts for human approval first?
32
- 5. **Filters** — any label, branch, or author filters (e.g. skip drafts, only review PRs with `needs-review` label)
42
+ If the token is missing or invalid, inform the user and stop.
33
43
 
34
44
  ---
35
45
 
36
46
  ## Setup Workflow
37
47
 
38
- ### Step 1 — Verify GitHub MCP access
48
+ Follow these steps in order.
49
+
50
+ ### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`
51
+
52
+ Run the `curl` check above.
53
+
54
+ - If absent: *"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in
55
+ OpenHands Settings -> Secrets."* Stop.
56
+ - If the API returns `{"message": "Bad credentials"}`: tell the user the
57
+ token is invalid and ask them to update it. Stop.
39
58
 
40
- Confirm the GitHub MCP integration is working:
59
+ ### Step 2 - Collect repository
60
+
61
+ Ask: *"Which GitHub repository should be monitored?
62
+ (Format: `owner/repo`, e.g. `myorg/backend`)"*
63
+
64
+ Validate access:
65
+ ```bash
66
+ curl -s "https://api.github.com/repos/{owner}/{repo}" \
67
+ -H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
68
+ | python3 -c "
69
+ import json, sys
70
+ d = json.load(sys.stdin)
71
+ if 'message' in d:
72
+ print('ERROR:', d['message'])
73
+ else:
74
+ print(f\"Accessible. Private: {d.get('private')}. Permissions: {d.get('permissions')}\")
75
+ "
41
76
  ```
42
- Use the GitHub MCP to list recent pull requests in one of the target repositories.
77
+
78
+ Record `REPO = "{owner}/{repo}"`.
79
+
80
+ ### Step 3 - Collect trigger label
81
+
82
+ Ask: *"Which PR label should trigger a review?
83
+ (Press Enter for the default: `openhands-review`.)"*
84
+
85
+ Record the answer as `TRIGGER_LABEL`. If the label does not exist yet, tell the
86
+ user that GitHub will still record the event once the label is created and
87
+ applied to a PR.
88
+
89
+ The automation reviews a PR when it sees the latest matching `labeled` event for
90
+ that label. To request another review later, remove and re-apply the label.
91
+
92
+ ### Step 4 - Collect review tone
93
+
94
+ Ask: *"What review tone should the reviewer use?
95
+ 1. Thorough (default) - comprehensive coverage of correctness, security, tests, style
96
+ 2. Concise - high-signal only, skips minor style feedback
97
+ 3. Friendly - constructive and encouraging
98
+ (Press Enter for Thorough, or type your choice or any custom style description)"*
99
+
100
+ Map the choice to `REVIEW_TONE`:
101
+
102
+ | Answer | `REVIEW_TONE` | `REVIEW_STYLE_INSTRUCTIONS` |
103
+ |---|---|---|
104
+ | 1 / Enter | `"thorough"` | `""` |
105
+ | 2 | `"concise"` | `""` |
106
+ | 3 | `"friendly"` | `""` |
107
+ | Custom text, e.g. `strict but kind` | `"thorough"` | the custom text verbatim |
108
+
109
+ ### Step 5 - Collect cron schedule
110
+
111
+ Ask: *"How often should the automation poll for labeled PRs?
112
+ (Press Enter for the default: every 5 minutes.
113
+ Use a cron expression for a different interval, e.g. `0 * * * *` = hourly)"*
114
+
115
+ Default: `*/5 * * * *`.
116
+
117
+ Record as `CRON_SCHEDULE`.
118
+
119
+ ### Step 6 - Generate the automation script
120
+
121
+ Read `scripts/main.py` from this skill's directory. Apply exactly five constant
122
+ substitutions near the top of the file:
123
+
124
+ | Placeholder | Replace with |
125
+ |---|---|
126
+ | `REPO = "owner/repo"` | `REPO = "{owner_repo}"` |
127
+ | `TRIGGER_LABEL = "openhands-review"` | `TRIGGER_LABEL = "{trigger_label}"` |
128
+ | `REVIEW_TONE = "thorough"` | `REVIEW_TONE = "{review_tone}"` |
129
+ | `REVIEW_STYLE_INSTRUCTIONS = ""` | `REVIEW_STYLE_INSTRUCTIONS = "{style_instructions}"` |
130
+ | `DEFAULT_OPENHANDS_URL = "http://localhost:8000"` | leave unchanged unless the user has a preference |
131
+
132
+ Use a safe string writer such as `json.dumps(value)` when inserting user-provided
133
+ repository names, labels, or style instructions into Python string literals.
134
+
135
+ Write the customized script to a temporary build directory:
136
+ ```bash
137
+ mkdir -p /tmp/pr-reviewer-build
138
+ # write the customized main.py to /tmp/pr-reviewer-build/main.py
43
139
  ```
44
140
 
45
- If it fails, tell the user to install the GitHub MCP integration first.
141
+ Validate syntax before packaging:
142
+ ```bash
143
+ python3 -m py_compile /tmp/pr-reviewer-build/main.py && echo "Syntax OK"
144
+ ```
46
145
 
47
- ### Step 2 — Determine trigger type
146
+ Fix any syntax errors before proceeding.
48
147
 
49
- **Event-based (recommended if publicly reachable):**
50
- Check `<RUNTIME_SERVICES>` for deployment reachability. If the deployment has a public URL, recommend an event trigger on `pull_request.opened` and `pull_request.synchronize`.
148
+ ### Step 7 - Package and upload
51
149
 
52
- **Cron-based (local/private deployments):**
53
- If the deployment is local, set up a cron schedule (e.g. every 5 minutes) that polls for recently updated PRs.
150
+ Determine the Automation backend URL and auth from the `<RUNTIME_SERVICES>`
151
+ block in your system context:
152
+ - **OPENHANDS_HOST**: the Automation backend `url_from_agent`
153
+ - **Auth**: `X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY`
54
154
 
55
- ### Step 3 — Build the review prompt
155
+ ```bash
156
+ tar -czf /tmp/pr-reviewer.tar.gz -C /tmp/pr-reviewer-build .
56
157
 
57
- Construct a prompt for the automation that includes:
58
- - The user's chosen repositories
59
- - Review focus areas (correctness, security, tests, maintainability)
60
- - Review tone preference
61
- - Whether to auto-post or draft
158
+ TARBALL_PATH=$(curl -s -X POST \
159
+ "${OPENHANDS_HOST}/api/automation/v1/uploads?name=github-pr-reviewer" \
160
+ -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
161
+ -H "Content-Type: application/gzip" \
162
+ --data-binary @/tmp/pr-reviewer.tar.gz \
163
+ | python3 -c "import json,sys; print(json.load(sys.stdin)['tarball_path'])")
62
164
 
63
- ### Step 4 — Create the automation
165
+ echo "Uploaded: $TARBALL_PATH"
166
+ ```
64
167
 
65
- Read the Automation backend URL and auth from `<RUNTIME_SERVICES>`:
66
- - Use the **Automation backend** `url_from_agent` as `OPENHANDS_HOST`
67
- - Auth: `X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY`
168
+ ### Step 8 - Register the automation
68
169
 
69
- Use the **prompt preset** endpoint:
70
170
  ```bash
71
- curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1/preset/prompt" \
171
+ curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1" \
72
172
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
73
173
  -H "Content-Type: application/json" \
74
- -d '{
75
- "name": "GitHub PR Reviewer",
76
- "prompt": "<constructed review prompt>",
77
- "trigger": <trigger config from step 2>
78
- }'
174
+ -d "{
175
+ \"name\": \"GitHub PR Reviewer: {owner}/{repo} label {trigger_label}\",
176
+ \"trigger\": {\"type\": \"cron\", \"schedule\": \"{cron_schedule}\"},
177
+ \"tarball_path\": \"$TARBALL_PATH\",
178
+ \"entrypoint\": \"python3 main.py\",
179
+ \"timeout\": 300
180
+ }" | python3 -m json.tool
79
181
  ```
80
182
 
81
- ### Step 5 — Confirm
183
+ Record the returned `id`.
184
+
185
+ ### Step 9 - Confirm
82
186
 
83
187
  Tell the user:
188
+
84
189
  > ✅ **GitHub PR Reviewer** is running!
85
190
  >
86
191
  > - Automation ID: `{id}`
87
- > - Repositories: `{repo list}`
88
- > - Trigger: `{trigger description}`
89
- > - Review style: `{tone}`, `{auto-post or draft}`
192
+ > - Repository: `{owner}/{repo}`
193
+ > - Trigger label: `{trigger_label}`
194
+ > - Review tone: `{tone}`
195
+ > - Polling schedule: `{cron_schedule}`
196
+ > - State file: `~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_{id}.json`
197
+ >
198
+ > Apply the `{trigger_label}` label to a pull request to queue a review. Each
199
+ > label event is processed once. To request another review, remove and re-apply
200
+ > the label.
201
+
202
+ ---
203
+
204
+ ## Runtime Behaviour (per poll)
205
+
206
+ Each cron run executes `main.py`, which:
207
+
208
+ 1. Loads state from the JSON file (see `references/state-schema.md`).
209
+ 2. Resolves and validates `GITHUB_PERSONAL_ACCESS_TOKEN` and repository access.
210
+ 3. Lists open PRs, newest-updated first.
211
+ 4. For each open PR carrying `TRIGGER_LABEL`:
212
+ - Refetches current PR metadata to avoid acting on stale list data.
213
+ - Finds the latest matching GitHub `labeled` issue event.
214
+ - Skips the event if it has already been tracked.
215
+ - Starts an OpenHands conversation with a review prompt that includes PR
216
+ metadata, the exact head SHA, label event details, and instructions to
217
+ clone the repo, inspect PR discussion, review comments, changed files,
218
+ diff, and surrounding code.
219
+ - Posts an acknowledgement comment with the label event, head SHA, and
220
+ conversation link.
221
+ - Records the label-event review in state with `status: "active"`.
222
+ 5. For each active review conversation:
223
+ - Marks it closed without posting if the PR has closed or merged.
224
+ - Suppresses stale results if the PR head SHA changed after the review was
225
+ queued.
226
+ - When the conversation reaches `idle`, `finished`, `error`, or `stuck`,
227
+ posts the agent's final response as a GitHub comment and marks the review
228
+ closed.
229
+ 6. Saves state atomically and fires the completion callback.
230
+
231
+ ---
232
+
233
+ ## Additional Resources
234
+
235
+ - **`references/state-schema.md`** - State JSON schema, field definitions, and
236
+ review lifecycle diagram.
237
+ - **`scripts/main.py`** - The complete automation script. Customize the five
238
+ constants at the top before packaging.
239
+
240
+ ---
241
+
242
+ ## Troubleshooting
243
+
244
+ | Symptom | Likely cause | Fix |
245
+ |---|---|---|
246
+ | Bot never queues reviews | Trigger label not present or no matching `labeled` event | Apply the configured label to the PR |
247
+ | "Bad credentials" in run logs | Token expired | Rotate and update `GITHUB_PERSONAL_ACCESS_TOKEN` |
248
+ | 404 on repo access | Repo name wrong or no access | Re-check `owner/repo` and token permissions |
249
+ | Same PR not reviewed after new commits | Label event was already processed | Remove and re-apply the trigger label |
250
+ | Review result never posts | Conversation still running or stuck | Open the conversation link from the acknowledgement comment |
251
+ | Stale review suppressed | PR head SHA changed while the agent was reviewing | Re-apply the trigger label after the latest commit |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  # auto-generated by sync_extensions.py
3
- description: Create an automation that reviews GitHub pull requests when they are opened or updated. Inspects the diff, changed files, tests, and existing discussion via GitHub MCP, then posts a concise review highlighting risks, security issues, missing tests, and next steps.
3
+ description: Create an automation that reviews GitHub pull requests when a configurable trigger label is applied. Polls GitHub deterministically, starts one OpenHands review conversation per label event, inspects full repository and PR context, and posts the final review comment back to GitHub.
4
4
  ---
5
5
 
6
6
  Read and follow the complete instructions in the SKILL.md file located in this skill's directory.
@@ -0,0 +1,126 @@
1
+ # State File Schema
2
+
3
+ The automation maintains a JSON state file that persists across polling runs.
4
+ It is the source of truth for which trigger-label events have queued reviews
5
+ and which conversations are still active.
6
+
7
+ ---
8
+
9
+ ## File Location
10
+
11
+ ```
12
+ {WORKSPACE_BASE_ROOT}/automation-state/github_pr_reviewer_label_event_{automation_id}.json
13
+ ```
14
+
15
+ `WORKSPACE_BASE_ROOT` is derived by going two levels up from the `WORKSPACE_BASE`
16
+ environment variable, stripping `automation-runs/{run_id}`.
17
+
18
+ Example on a local install:
19
+
20
+ ```
21
+ ~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_abc12345-....json
22
+ ```
23
+
24
+ The `automation_id` is read from the `AUTOMATION_EVENT_PAYLOAD` environment
25
+ variable, field `automation_id`.
26
+
27
+ ---
28
+
29
+ ## Top-Level Schema
30
+
31
+ ```jsonc
32
+ {
33
+ "version": 2,
34
+ "repo": "owner/repo",
35
+ "trigger_label": "openhands-review",
36
+ "updated_at": 1717200000.0,
37
+ "reviews": {},
38
+ "prs": {}
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## `reviews` Map
45
+
46
+ Key: `"{pr_number}:label:{label_event_id}"`. This makes the latest GitHub
47
+ `labeled` event the idempotency key. Re-applying the trigger label creates a new
48
+ GitHub event and therefore a new review request.
49
+
50
+ Value: **ReviewRecord**
51
+
52
+ ```jsonc
53
+ {
54
+ "pr_number": 42,
55
+ "head_sha": "0123456789abcdef...",
56
+ "trigger_label_event_id": 123456789,
57
+ "trigger_label_event_created_at": "2026-06-12T00:00:00Z",
58
+ "html_url": "https://github.com/owner/repo/pull/42",
59
+ "status": "active",
60
+ "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
61
+ "last_activity": 1717200000.0
62
+ }
63
+ ```
64
+
65
+ `status` values:
66
+
67
+ | Status | Meaning |
68
+ |---|---|
69
+ | `active` | Review conversation is running or waiting to be collected |
70
+ | `closed` | Final result was posted, or the PR closed before collection |
71
+ | `stale` | PR head SHA changed before the review completed, so the result was suppressed |
72
+
73
+ When a review becomes stale, `stale_reason` records the old and new head SHAs.
74
+ When a review closes after posting, `completed_at` records the completion time.
75
+
76
+ ---
77
+
78
+ ## `prs` Map
79
+
80
+ Key: `"{pr_number}"`.
81
+
82
+ Value: latest PR snapshot observed during polling:
83
+
84
+ ```jsonc
85
+ {
86
+ "head_sha": "0123456789abcdef...",
87
+ "label_present": true,
88
+ "labels": ["openhands-review", "bug"],
89
+ "last_seen": 1717200000.0
90
+ }
91
+ ```
92
+
93
+ This snapshot is informational and helps diagnose whether a PR was skipped
94
+ because the trigger label was absent.
95
+
96
+ ---
97
+
98
+ ## Review Lifecycle
99
+
100
+ ```
101
+ Trigger label applied on GitHub
102
+ |
103
+ v
104
+ [active] - conversation created, acknowledgement comment posted
105
+ |
106
+ +-- PR closes/merges before collection --> [closed] without posting
107
+ |
108
+ +-- PR head SHA changes before collection --> [stale] without posting
109
+ |
110
+ v
111
+ [closed] - final review comment posted
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Resetting State
117
+
118
+ To force the automation to reconsider previous label events, delete the state
119
+ file:
120
+
121
+ ```bash
122
+ rm ~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_<id>.json
123
+ ```
124
+
125
+ Usually, prefer removing and re-applying the trigger label. That preserves
126
+ history while creating a new review request.