Generate Quarterly Report
Step 1 — Determine Period
Parse the user’s request to extract the quarter and year. Convert to date range:
| Input | Start | End |
|---|---|---|
| Q1 2026 | 2026-01-01 | 2026-03-31 |
| Q2 2026 | 2026-04-01 | 2026-06-30 |
| Q3 2026 | 2026-07-01 | 2026-09-30 |
| Q4 2026 | 2026-10-01 | 2026-12-31 |
| “last quarter” | Calculate from current date |
Step 2 — Collect Data (Parallel)
Run all data collection concurrently.
2a. daily-plan review (GitHub + Jira + Org + AI)
daily-plan review "<START>..<END>" --json --no-discussions --no-comments
This returns JSON with:
org_done— completed org-mode TODO itemsai_sessions— AI coding sessionsjira_completed— Jira issues resolved viaresolutiondateJQL (keys:key,summary,status,priority,url)github_merged— PRs authored and merged (keys:repo,number,title,url)github_reviewed— PRs reviewed, excluding own PRs (keys:repo,number,title,author,url)github_issues_created— Issues filed (keys:repo,number,title,url)github_security_advisories— Security advisories where user is credited or author (keys:ghsa_id,cve_id,summary,severity,state,repo,url,credits,role). Filtered to only advisories where the user appears increditsor is theauthor—collaborating_usersis skipped (indicates team access, not active involvement). Comments on advisories are not accessible via the API (private fork).
daily-plan automatically splits queries by month and uses --limit 1000 to avoid GitHub Search API truncation. Results are scoped to the configured owners (tektoncd, openshift-pipelines).
daily-plan’s GitHub queries are scoped to the configured owners (tektoncd, openshift-pipelines). PRs in other orgs (NixOS/nixpkgs, containers/, chmouel/, github/advisory-database, etc.) are not included — add those orgs to daily-plan’s config if they should be tracked. Security advisories are filtered to only those where the user is in credits or is author — team-based collaborating_users access is excluded.
2b. GitLab (gitlab.cee.redhat.com)
The internal Red Hat GitLab instance requires REST API with a personal access token. The mcp__plugin_gitlab_gitlab__* MCP plugin targets gitlab.com only — do NOT use it for internal GitLab.
Load token:
export GITLAB_TOKEN=$(passage show gitlab/cee-redhat/token 2>/dev/null || echo "")
If empty, ask the user to provide it or skip GitLab. Generate tokens at: https://gitlab.cee.redhat.com/-/user_settings/personal_access_tokens (scope: read_api).
Resolve user ID:
curl -s "https://gitlab.cee.redhat.com/api/v4/users?username=vdemeest" \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" | python3 -c "import json,sys; u=json.load(sys.stdin)[0]; print(u['id'], u['name'])"
MRs authored:
curl -s "https://gitlab.cee.redhat.com/api/v4/merge_requests?author_id=<ID>&state=merged&scope=all&order_by=updated_at&sort=desc&per_page=100&created_after=<START>&created_before=<END>" \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN"
MRs reviewed:
curl -s "https://gitlab.cee.redhat.com/api/v4/merge_requests?reviewer_id=<ID>&state=merged&scope=all&order_by=updated_at&sort=desc&per_page=100&created_after=<START>&created_before=<END>" \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN"
Note: author_username filter does NOT work on the global MR endpoint — always use author_id (numeric) + scope=all.
Extract from each MR: iid, title, references.full, merged_at, web_url. Group by namespace/project.
2c. Google Calendar — Meetings
Use GoogleWorkspace skill scripts to query calendar events for the quarter:
cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js call calendar events.list '{
"calendarId": "primary",
"timeMin": "<START>T00:00:00Z",
"timeMax": "<END>T23:59:59Z",
"singleEvents": true,
"orderBy": "startTime",
"maxResults": 2500,
"fields": "items(summary,start,end,status,attendees)"
}'
Filter to work meetings (exclude personal blocks like “Focus time”, “Home”, etc.). Count by meeting name to identify recurring series. Only count meetings with 2+ attendees.
2d. Gmail / mu — Meeting Notes
Use mu find (Maildir) to search for Gemini meeting notes in Q1:
mu find 'from:gemini-notes@google.com AND date:<START_YYYYMMDD>..<END_YYYYMMDD>' --fields 's' 2>/dev/null
Alternatively use GoogleWorkspace scripts:
cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js gmail-search \
"from:gemini-notes@google.com newer_than:120d"
If MCP tools are available instead (e.g. in Claude Desktop):
mcp__google-workspace__search_emails: from:gemini-notes@google.com to:vdemeest@redhat.com after:<START> before:<END>
2e. Google Drive — Documents
cd ~/.config/claude/skills/GoogleWorkspace/scripts && node workspace.js drive-search \
"modifiedTime > '<START>T00:00:00' and modifiedTime < '<END>T23:59:59' and trashed=false"
2f. Slack
Use the Slack skill:
~/.config/claude/skills/Slack/tools/SlackRead.sh search "from:vdemeest <relevant_terms>" --after <START> --before <END>
Or via MCP if available:
mcp__slack__search_messages: from:vdemeest after:<START> before:<END>
Step 3 — Analyze & Categorize
Before generating HTML, analyze the raw data:
- Count totals for the stats strip (PRs merged, reviewed, GitLab MRs, issues filed, repos contributed to)
- Group GitHub PRs by repository, ordered by PR count descending
- Group GitHub issues by theme (security, infrastructure, features, governance, code modernization)
- Group GitLab MRs by release track (OSP 1.15, 1.20, 1.21, 1.22, cross-release)
- Identify Jira epics — filter
jira_completedfor Epics and Features only (exclude Stories, Tasks, Sub-tasks, Bugs for the main epic section; keep notable bugs separate) - Identify key initiatives — look for patterns: security campaigns, governance work, community leadership, tooling, release management
- Extract meeting patterns from Gmail/Gemini notes — recurring meetings, frequency
Step 4 — Generate HTML
Use the template from reference/Template.html in this skill directory. The template is a complete, self-contained HTML file with all CSS inline.
Read the template:
read reference/Template.html
Fill in all data sections. Key rules:
Linking — every item must be clickable
| Item type | URL format |
|---|---|
| Jira keys | <a href="https://issues.redhat.com/browse/<KEY>" target="_blank" class="key-badge">KEY</a> |
| GitHub PRs/issues | Use the url field from gh search JSON output: <a href="<url>" target="_blank" class="pr-link">title</a> |
| GitLab MRs | Use web_url from API: <a href="<web_url>" target="_blank" class="pr-link">!iid title</a> |
Section summaries
Every data section must open with a <div class="section-summary"> containing 2–3 sentences synthesizing the section’s impact. Cover: total volume, standout items, and business/technical significance. Interpret — don’t just repeat the table.
Executive summary
3–5 sentences in third person, past tense, executive-readable prose. Cover:
- Major outcomes (epics closed, features shipped, security work)
- Breadth of contribution (repos, systems, teams)
- Standout contributions (CVEs, governance, proposals → merged)
- Quantified signal (“50+ PRs merged across 15 repos”)
Sections to include (omit any with zero results)
- Executive Summary (always)
- Jira — Epics & Features Closed
- GitHub — PRs Authored & Merged (grouped by repo, with group headers like “⚡ Tekton Core”, “🏛 Community”)
- GitHub — PRs Reviewed (grouped by repo)
- GitHub — Issues Filed (grouped by theme)
- GitHub — Security Advisories (credited/authored only)
- GitLab — MRs Reviewed (grouped by release track)
- Key Initiatives & Strategic Contributions (achievement grid)
- Meeting & Cross-Team Engagement (Google Calendar)
- Documents & Presentations (Google Drive)
- Communication Highlights (mu/Gmail — notable outbound threads)
- Areas of Focus (tag cloud)
Stats strip values
Count from the collected data:
- Epics Closed (Jira epics/features with Done status)
- GitHub PRs Merged (authored)
- GitHub PRs Reviewed
- Issues Filed
- Security Advisories (credited/authored)
- Jira Issues Closed
- GitLab MRs Reviewed (if available)
Step 5 — Write Output
# Write the HTML report
write ~/desktop/downloads/q<N>-<YEAR>-achievements-vincent-demeester.html
Confirm to the user: file path, total counts, any data sources that were unavailable.
Common Mistakes
| Mistake | Fix |
|---|---|
| Including personal repos (vdemeester/*) | Exclude — only include org repos (tektoncd, openshift-pipelines, etc.) |
| Plain text Jira keys, PR titles, MR titles | Always hyperlink with <a> tags |
| Including Jira Stories/Tasks in the epics section | Filter for issuetype in (Epic, Feature) only |
| Using MCP gitlab plugin for internal GitLab | MCP targets gitlab.com — use REST API for gitlab.cee.redhat.com |
author_username on global MR endpoint |
Use author_id (numeric) + scope=all |
| Missing section summaries | Every card must have a section-summary div |
| Sections with zero results | Omit entirely — no “No items found” text |
| Forgetting to load GITLAB_TOKEN | Load from passage or ask user before GitLab queries |
Graceful Degradation
If a data source is unavailable:
- GitLab token missing → Skip GitLab section, note in footer
- Google Workspace not configured → Skip Gmail/Drive sections
- Slack not configured → Skip Slack section
- daily-plan not found → Fall back to direct
gh+ Jira CLI queries - Always note unavailable sources in the footer “Data sources” line