Use when populating worklog.yaml with today's work based on GitHub PR activity, or when user says "update worklog", "populate worklog", "what did I do today", or "log my PRs"
Queries GitHub for all PR activity (authored, reviewed, commented) on a given date and generates worklog.yaml task entries with contextual descriptions, auto-detected JIRA tickets, and inferred status.
/populate-from-prs [--file PATH] [--date YYYY-MM-DD] [--dry-run]
--file: Path to worklog.yaml (default: ~/worklog/worklog.yaml)--date: Date to populate (default: today's date from system)--dry-run: Show generated entries without writing to fileExecute the following workflow step by step:
Extract from user input:
FILE_PATH: worklog file path (default ~/worklog/worklog.yaml)TARGET_DATE: date in YYYY-MM-DD format. If not provided, determine today's date by running date +%Y-%m-%dDRY_RUN: boolean (default: false)Load incremental timestamp: Read the existing worklog file and check if the TARGET_DATE entry has a last_updated field. This ISO 8601 timestamp (e.g., "2026-04-02T15:22:00Z") records when /update-worklog last ran for this date.
LAST_UPDATED — subsequent phases will only look at activity after this timestampLAST_UPDATED to empty — phases will use date-level filtering (startswith("{TARGET_DATE}"))Run these three gh search commands in parallel using the Bash tool. Each command includes body for JIRA ticket detection but pipes through a Python script that extracts the JIRA ticket from the body and then discards the body text to keep output compact and avoid truncation.
gh search prs --author=@me --updated=">=${TARGET_DATE}" --json number,title,url,state,isDraft,repository,body,updatedAt --limit 1000 | python3 -c "
import sys, json, re
prs = json.load(sys.stdin)
for pr in prs:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('title',''))
if not m:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('body','') or '')
pr['jira'] = m.group(0) if m else ''
pr.pop('body', None)
json.dump(prs, sys.stdout)
"
gh search prs --reviewed-by=@me --updated=">=${TARGET_DATE}" --json number,title,url,state,isDraft,repository,body,updatedAt --limit 1000 | python3 -c "
import sys, json, re
prs = json.load(sys.stdin)
for pr in prs:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('title',''))
if not m:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('body','') or '')
pr['jira'] = m.group(0) if m else ''
pr.pop('body', None)
json.dump(prs, sys.stdout)
"
gh search prs --commenter=@me --updated=">=${TARGET_DATE}" --json number,title,url,state,isDraft,repository,body,updatedAt --limit 1000 | python3 -c "
import sys, json, re
prs = json.load(sys.stdin)
for pr in prs:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('title',''))
if not m:
m = re.search(r'[A-Z][A-Z0-9]+-\d+', pr.get('body','') or '')
pr['jira'] = m.group(0) if m else ''
pr.pop('body', None)
json.dump(prs, sys.stdout)
"
For each unique PR (by URL):
Track activity types - a single PR may appear in multiple queries:
--author query: authored--reviewed-by query: reviewed--commenter query: commentedDetect JIRA ticket — use the jira field already extracted by the Python script in Phase 2. The script checks the PR title first, then the body. If the jira field is empty, set jira_ticket to "".
Split into two buckets:
--author query)--reviewed-by and/or --commenter queries)Important: The gh search --author query returns PRs you authored that were updated on TARGET_DATE. This does NOT mean you did anything — CI, bots, or other users may have triggered the update. You must verify your own activity.
For each authored PR, fetch activity to check for YOUR work and generate specific descriptions. Run these in parallel per PR (batch as many as practical).
Timestamp-aware filtering: If LAST_UPDATED is set, use > "{LAST_UPDATED}" to only fetch activity since the last run. If LAST_UPDATED is empty (first run), use startswith("{TARGET_DATE}") to get all activity for the day.
When LAST_UPDATED is empty (first run):
gh api repos/{owner}/{repo}/pulls/{number}/commits --jq '[.[] | select(.commit.author.date | startswith("{TARGET_DATE}")) | .commit.message]'
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.submitted_at | startswith("{TARGET_DATE}")) | {user: .user.login, state: .state}]'
gh api repos/{owner}/{repo}/issues/{number}/comments --jq '[.[] | select(.user.login == "{USERNAME}") | select(.created_at | startswith("{TARGET_DATE}")) | {user: .user.login, body: .body}]'
When LAST_UPDATED is set (incremental run):
gh api repos/{owner}/{repo}/pulls/{number}/commits --jq '[.[] | select(.commit.author.date > "{LAST_UPDATED}") | .commit.message]'
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.submitted_at > "{LAST_UPDATED}") | {user: .user.login, state: .state}]'
gh api repos/{owner}/{repo}/issues/{number}/comments --jq '[.[] | select(.user.login == "{USERNAME}") | select(.created_at > "{LAST_UPDATED}") | {user: .user.login, body: .body}]'
Filter out CI retrigger commands: Before checking for activity, discard any comments that are purely CI slash commands (e.g., /retest, /test <job-name>, /override <job-name>). These are not meaningful work activity. Comments like /jira backport ..., /lgtm, /approve, or substantive discussion DO count as activity.
Exclude authored PRs with no user activity: If ALL three queries return empty arrays (no commits pushed, no reviews received, no meaningful comments by the user after filtering out CI retrigger commands):
For each authored PR with verified activity, generate a separate task:
Descriptions — use the fetched activity data to generate a concise, specific summary of what was actually done today. Consider:
Synthesize this into 1-3 short description strings that read naturally for a work log. Examples:
"Addressed Alberto's review comments on PLS controller logic""Pushed fix for DNS zone pagination and added unit tests""Merged PR after final approval from Seth""Rebased on main and resolved merge conflicts"Status: merged/closed → "completed", open → "in progress"
upnext_description — must ALWAYS be populated for non-completed tasks:
"" (nothing left to do)"Finish implementation, dev test, and mark the PR ready for review""Get PR reviewed and merged""" (abandoned)status is "in progress", upnext_description must NEVER be empty. Always provide a meaningful next step.Build task object:
- jira_ticket: "<detected JIRA or empty>"
descriptions:
- "<specific description from activity>"
status: "<inferred status>"
upnext_description: "<inferred next step>"
github_pr: "<PR URL>"
blocker: ""
Important: The gh search queries return PRs where you reviewed/commented at any point AND the PR was updated on TARGET_DATE. This does NOT mean your activity happened on TARGET_DATE. You must verify.
For each non-authored PR, check whether the user actually reviewed or commented within the relevant time window by running in parallel (batch as many as practical).
Where {USERNAME} is obtained from gh api user --jq '.login' (run once, cache the result).
When LAST_UPDATED is empty (first run):
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.user.login == "{USERNAME}") | .submitted_at] | map(select(startswith("{TARGET_DATE}")))'
gh api repos/{owner}/{repo}/issues/{number}/comments --jq '[.[] | select(.user.login == "{USERNAME}") | .created_at] | map(select(startswith("{TARGET_DATE}")))'
When LAST_UPDATED is set (incremental run):
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.user.login == "{USERNAME}") | .submitted_at] | map(select(. > "{LAST_UPDATED}"))'
gh api repos/{owner}/{repo}/issues/{number}/comments --jq '[.[] | select(.user.login == "{USERNAME}") | .created_at] | map(select(. > "{LAST_UPDATED}"))'
"Reviewed" if the reviews query matched, otherwise "Commented on"Then consolidate ALL verified non-authored PRs into one task:
"Reviewing Pull Requests""Reviewed <PR URL>" or "Commented on <PR URL>"
"Reviewed <PR URL>" (review implies comments)"completed""" (empty, since there are multiple PRs)- jira_ticket: "Reviewing Pull Requests"
descriptions:
- "Reviewed https://github.com/openshift/hypershift/pull/7835"
- "Reviewed https://github.com/openshift/enhancements/pull/1945"
- "Commented on https://github.com/openshift/hypershift/pull/7810"
status: "completed"
upnext_description: ""
github_pr: ""
blocker: ""
For authored PRs only, if multiple share the same JIRA ticket (non-empty), merge them into a single task:
descriptions arraygithub_pr"in progress", use that; if all completed, use "completed"Authored PRs with empty JIRA tickets remain as separate tasks.
TARGET_DATE already has an entry:
github_pr URLs for that dateTARGET_DATE has the same github_pr URL and status: "in progress". If found, update that entry in-place: set status: "completed", upnext_description: "", and append "PR merged" to the descriptions array. Do NOT create a duplicate entry.github_pr already appears in existing entries for that date (and was not just updated by same-day merge detection above)github_pr URL: Scan ALL dates in the worklog (not just TARGET_DATE) for an existing entry with the same github_pr URL and status: "in progress". If found, the PR was previously logged as in-progress on an earlier date and has since merged — create a new completed entry for TARGET_DATE with description "Merged PR: <title without JIRA prefix>", status: "completed", and upnext_description: "". Do NOT modify the earlier date's entry.jira_ticket: If the URL match above did not find anything AND the merged PR has a non-empty JIRA ticket, scan ALL dates in the worklog for an existing entry with the same jira_ticket value and status: "in progress". This catches cases where multiple PRs were grouped under one JIRA ticket but the github_pr field only stored one PR's URL. If found, create a new completed entry for TARGET_DATE with description "Merged PR: <title without JIRA prefix>", status: "completed", and upnext_description: "". Do NOT modify the earlier date's entry."Reviewing Pull Requests" task already exists for that date. If so, check which PR URLs are already listed in its descriptions. Append only new descriptions (for PRs not already listed) to the existing task's array. If no new PRs to add, skip.--dry-run: stop here--- line)last_updated timestamp: After successfully writing tasks, set the last_updated field on the TARGET_DATE entry to the current UTC time in ISO 8601 format. Get the timestamp by running date -u +%Y-%m-%dT%H:%M:%SZ. Write it as the first field under the date key, before work_log:
"2026-04-02":
last_updated: "2026-04-02T15:22:00Z"
work_log:
...
If last_updated already exists for this date, replace it with the new timestamp.Match the existing worklog.yaml style exactly:
"2026-03-03":"in progress", "CNTRLPLANE-1985"""descriptions (plural, array) not description (singular)- prefix with appropriate indentationlast_updated is placed as the first field under the date key, before work_log"2026-03-03":
last_updated: "2026-03-03T17:45:00Z"
work_log:
- start_time: "08:00"
end_time: ""
tasks:
- jira_ticket: "CNTRLPLANE-1985"
descriptions:
- "Addressed Cesar's review comments on private topology enhancement"
- "Updated API field naming per Alberto's feedback"
status: "in progress"
upnext_description: "Get PR reviewed and merged"
github_pr: "https://github.com/openshift/enhancements/pull/1949"
blocker: ""
- jira_ticket: "OCPBUGS-74495"
descriptions:
- "Fixed build error in legacy storage client and added unit tests"
status: "in progress"
upnext_description: "Get PR reviewed and merged"
github_pr: "https://github.com/openshift/cluster-image-registry-operator/pull/1287"
blocker: ""
- jira_ticket: "Reviewing Pull Requests"
descriptions:
- "Reviewed https://github.com/openshift/hypershift/pull/7835"
- "Reviewed https://github.com/openshift/enhancements/pull/1945"
- "Commented on https://github.com/openshift/hypershift/pull/7810"
status: "completed"
upnext_description: ""
github_pr: ""
blocker: ""
descriptionstasks array"YYYY-MM-DD":
work_log:
- start_time: "08:00"
end_time: ""
tasks: [...]