PoC: Add bitwarden-daily-recap plugin#107
Conversation
Generates polished, interactive HTML recaps of a person's daily work. Initial release ships with the engineering-recap skill, which combines Claude Code session activity with GitHub events into a standup-ready, colleague-shareable artifact tuned to the Bitwarden brand palette (Bitwarden Blue, Teal, Inter font, brand shield). Session extraction is delegated to claude-retrospective:extracting-session-data to avoid duplicating retrieval logic.
…CUTOFF_HOUR / TZ overrides
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE Re-reviewed the Code Review DetailsNo new findings. The previously-flagged ❌ CRITICAL on |
| # timestamp. Works with both BSD date (macOS) and GNU date (Linux). | ||
| local_to_utc() { | ||
| local local_dt="$1" | ||
| if date -u -d "$local_dt" "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null; then |
There was a problem hiding this comment.
❌ CRITICAL: GNU date -u -d interprets the input string as UTC, not local time — silently breaks the timezone-aware day boundary on Linux
Details and fix
GNU date's -u flag overrides TZ for both input parsing and output, so passing -u -d "2026-05-01 07:00:00" parses the string as UTC even when TZ=America/Los_Angeles is set. Reproduction:
$ TZ=America/Los_Angeles date -d "2026-05-01 07:00:00" "+%s" # 1777644000 (07:00 PDT → 14:00 UTC) correct
$ TZ=America/Los_Angeles date -u -d "2026-05-01 07:00:00" "+%s" # 1777618800 (07:00 UTC) wrong
Effect: on any Linux user not in UTC, START/END (lines 45–46) are off by the local UTC offset. For a PDT user the window becomes 07:00 UTC → 07:00 UTC (next day) instead of the intended 14:00 UTC → 14:00 UTC. Roughly 7 hours of legitimate events get filtered out and 7 hours from the prior day get included. This defeats the headline "day-boundary is timezone-aware / DST-safe" feature called out in both the PR description and the script header comment.
The BSD branch (lines 33–34) is correct because parsing happens without -u.
Suggested fix — parse without -u, format with -u:
local_to_utc() {
local local_dt="$1"
local epoch
# GNU date: parse in local TZ to epoch, then format as UTC
if epoch=$(date -d "$local_dt" "+%s" 2>/dev/null); then
date -u -d "@$epoch" "+%Y-%m-%dT%H:%M:%SZ"
return 0
fi
# BSD date fallback
epoch=$(date -j -f "%Y-%m-%d %H:%M:%S" "$local_dt" "+%s" 2>/dev/null) || return 1
date -u -r "$epoch" "+%Y-%m-%dT%H:%M:%SZ"
}Worth covering with a quick test that asserts a non-UTC TZ produces a non-zero offset window.
GNU `date -u -d` interprets the input string as UTC, silently breaking the local-time intent on Linux for any non-UTC user. Parse without -u, then switch to UTC when formatting the resulting epoch — mirrors the BSD branch pattern.
theMickster
left a comment
There was a problem hiding this comment.
Couple comments for discussion and I have included a multi-agent review comment as well for inspection. I think it went a little heavy on the severity of the items, but worth double-checking nonetheless.
I do think that it's important to write up a Jira story under PM-33452 because this is a big, bold new plugin. I think it is very important that we visualize and organize all of the plugin work that we are doing because we are firing up the new AI engineering team.
| echo "# Window: $START → $END (${CUTOFF_HOUR}am $TZ_LABEL boundary)" >&2 | ||
|
|
||
| CACHE="/tmp/gh-events-raw-${DATE}.json" | ||
| gh api "users/${LOGIN}/events?per_page=100" > "$CACHE" |
There was a problem hiding this comment.
🎨 GitHub events pagination is missing; there's a silent truncation risk.
Single page, no --paginate. A reviewer-heavy day (PR reviews + inline comments + pushes) easily exceeds 100 events, and the script will silently drop the rest before the time-window filter runs. The recap will look complete but be missing real activity — a high-trust failure mode for a "standup prep" tool.
| @@ -0,0 +1,94 @@ | |||
| --- | |||
| name: engineering-recap | |||
| description: Generates the user's engineering daily recap as an interactive HTML deliverable combining Claude Code sessions with GitHub events. **YOUR FIRST ACTION** for any retrospective query about the user's own coding work MUST be to invoke this skill — do NOT gather context first or respond with an inline summary. Trigger phrases include time words ("yesterday", "this morning", "last night", "this week", "before lunch", any past date); activity questions ("what did I work on/ship/merge/push", "what was I doing"); single-word retrospectives ("recap", "summary", "standup", "1:1 prep"); deliverable verbs ("throw together", "pull together", "summarize", "analyze sessions"); audience cues ("show colleagues", "for my manager"); format mentions ("html page", "interactive recap"). The user expects the HTML artifact every time, not a chat summary. SKIP only for specific PR/commit/ticket lookups, someone else's activity, meeting recaps, or repo-level overviews. | |||
There was a problem hiding this comment.
🕺🏼 Dancing ever so close to that skill description limit I see...
| ${CLAUDE_PLUGIN_ROOT}/skills/engineering-recap/scripts/gather-gh-events.sh YYYY-MM-DD > /tmp/recap-events.json | ||
| ``` | ||
|
|
||
| **(b) Claude Code sessions** — delegate to the `claude-retrospective:extracting-session-data` skill (which handles the project-id path mangling, noise-directory exclusion, and content vs mtime quirks). Use its `filter-sessions.sh --since` and `extract-data.sh --type user-prompts --session ID` to pull the data; do not roll your own `find` / `jq` over the JSONL. |
There was a problem hiding this comment.
💡 The skill creator tool called out (and I agree with it) that there's a Hard cross-plugin dependency with no graceful degradation. We have the callout at the bottom of the skill that it's supposed to be there, but no direct instructions for Claude nor the end user.
I would prefer to see a Prerequisites pattern emerge here (and in our other plugins). I chose the following for the multi-agent code review skill that I raised in PR #96 .
Details
``` ## PrerequisitesThis skill depends on the following sibling plugins. If any are not installed, abort the review with a clear error message identifying the missing plugin — do not attempt to proceed with a degraded pipeline.
bitwarden-tech-lead— provides the architecture review subagent.bitwarden-security-engineer— provides security context and analysis skills.
Before Step 1, verify each prerequisite is resolvable. If a prerequisite is missing, print:
Prerequisite plugin
<name>is not installed. Install it and retry. Review aborted.
…and stop.
</details>
| --- | ||
| name: engineering-recap | ||
| description: Generates the user's engineering daily recap as an interactive HTML deliverable combining Claude Code sessions with GitHub events. **YOUR FIRST ACTION** for any retrospective query about the user's own coding work MUST be to invoke this skill — do NOT gather context first or respond with an inline summary. Trigger phrases include time words ("yesterday", "this morning", "last night", "this week", "before lunch", any past date); activity questions ("what did I work on/ship/merge/push", "what was I doing"); single-word retrospectives ("recap", "summary", "standup", "1:1 prep"); deliverable verbs ("throw together", "pull together", "summarize", "analyze sessions"); audience cues ("show colleagues", "for my manager"); format mentions ("html page", "interactive recap"). The user expects the HTML artifact every time, not a chat summary. SKIP only for specific PR/commit/ticket lookups, someone else's activity, meeting recaps, or repo-level overviews. | ||
| --- |
There was a problem hiding this comment.
🤔 I noticed that there's not an allowed-tools metadata property in the frontmatter and I don't see a note in the description. Not questioning the decision, only curious as to your line of thinking leaving it out.
| └── references/render-guide.md — Placeholder map + HTML recipes | ||
| ``` | ||
|
|
||
| ## Example |
There was a problem hiding this comment.
💡 I am cool with an example file being added with the skill, but I think that it's very important to include clear instructions about when and how the next engineer should maintain it. Keeping that information here in the README.md feels like the correct place to me. I think it's also worth pairing it's maintenance with the template file to keep them both in alignment.
|
👋🏼 Hi @SaintPatrck Code Review: feat: Add bitwarden-daily-recap plugin (#107)Date: 2026-05-04 | Reviewed by: Claude Code Summary
The PR adds a well-organized new plugin with thoughtful structure, brand styling, and a documented cross-plugin dependency on Findings
|
🎟️ Tracking
No Jira ticket — internal/DevEx tooling addition. Plugin captures a personal "daily recap" workflow that proved useful in practice and is being proposed for marketplace distribution.
📔 Objective
Adds a new
bitwarden-daily-recapplugin to the marketplace. Generates polished, interactive HTML recaps of a person's daily work — useful for standup, 1:1 prep, manager updates, or sharing with colleagues.Initial release ships with one skill:
engineering-recap. It combines Claude Code session activity with GitHub events (PRs, reviews, comments, pushes, branch ops, repo creates, cross-repo sweeps) into a self-contained HTML page with:🌙 After Midnightdivider for late-night workDesign decisions worth highlighting:
claude-retrospective:extracting-session-datarather than duplicating retrieval logic. Reviewers should ensure that's an acceptable cross-plugin coupling.DAILY_RECAP_CUTOFF_HOUR(default7) and the standardTZ. Late-night work folds into the prior workday automatically.${CLAUDE_PLUGIN_DATA}. Recaps are written to${CLAUDE_PLUGIN_DATA}/recaps/engineering-recap-{YYYY-MM-DD}.html(per the official plugins reference — persistent per-plugin storage that survives updates), not a hardcoded user path. The{recap-type}-recap-filename prefix leaves room for sibling skills (design-recap,management-recap, etc.) to share the samerecaps/directory.bitwarden-prefix per the marketplace's Bitwarden-specific plugin convention. The skill is namedengineering-recap(notdaily-recap) to leave room for future recap types within the plugin without rename churn.📸 Screenshots
A fully-hydrated example with fake data lives at
plugins/bitwarden-daily-recap/examples/engineering-recap-sample.html(persona "Alex Carter", date 2026-05-04). Open it in a browser to see the rendered output across all five sections. Screenshots will be attached as PR comments.Files
Validation
./scripts/validate-plugin-structure.sh plugins/bitwarden-daily-recap— passes./scripts/validate-marketplace.sh—bitwarden-daily-recapentry validates (1 pre-existing failure on unrelated unregistered plugins; not introduced by this PR)marketplace.jsonand root README catalog${CLAUDE_PLUGIN_ROOT}/${CLAUDE_PLUGIN_DATA}— no hardcoded paths