Generate a daily standup summary of what was accomplished since the last standup, from a business logic perspective. Reads PR bodies to understand what changes actually enabled.
Installs into .claude/skills of the current project.
Are you the author of Standup?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/auerbachb-standup)
---
name: standup
description: Generate a daily standup summary of what was accomplished since the last standup, from a business logic perspective. Reads PR bodies to understand what changes actually enabled.
triggers:
- daily standup
- what did I do yesterday
- recent work summary
argument-hint: "[since-time] — omit for smart default (skips weekends/holidays); e.g. \"Friday at noon ET\""
model: sonnet
allowed-tools:
- Read
- Glob
- Grep
- Bash
- WebFetch
- WebSearch
---
Generate a standup report summarizing what was accomplished since $ARGUMENTS (default: smart lookback to previous workday noon ET — skips weekends and US federal holidays).
## How to gather data
### Step 1: Find repos and set time range
1. **Find all repos the user works in.** Check recent git activity across known repo paths. Start with the current working directory, then check other repos mentioned in conversation context or memory.
2. **Determine the lookback cutoff.** If the user provided an explicit `$ARGUMENTS` time reference, use it directly (skip to the ISO conversion below). If no argument was given, compute the smart default: find the most recent prior workday by walking backwards from yesterday, skipping weekends, US federal holidays, and the day after Thanksgiving (a de facto holiday for most organizations).
**Smart lookback algorithm** (run only when `$ARGUMENTS` is empty):
```bash
# Delegates to workday.sh, which implements the full weekend +
# US-federal-holiday + day-after-Thanksgiving calculator with observed-date
# rules (Sat → Fri, Sun → Mon) and cross-year lookbacks.
#
# Resolve the script path robustly: /standup runs from arbitrary repos,
# so a bare `.claude/scripts/workday.sh` only works when CWD happens to be
# this config repo. Check in this order: skills-worktree (canonical),
# ~/.claude/scripts (global symlink fallback), git root of the current repo,
# then CWD-relative.
# Capture GIT_ROOT first and only include that candidate when non-empty —
# otherwise `$(git rev-parse …)` expands to "" and the candidate becomes
# `/.claude/scripts/workday.sh` (absolute-root path), which would
# incorrectly match an unrelated root-level file if one existed.
WORKDAY_SH=""
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
for candidate in \
"$HOME/.claude/skills-worktree/.claude/scripts/workday.sh" \
"$HOME/.claude/scripts/workday.sh" \
"${GIT_ROOT:+$GIT_ROOT/.claude/scripts/workday.sh}" \
".claude/scripts/workday.sh"; do
[ -n "$candidate" ] || continue
if [ -x "$candidate" ]; then
WORKDAY_SH="$candidate"
break
fi
done
if [ -z "$WORKDAY_SH" ]; then
echo "Error: could not locate workday.sh" >&2
exit 1
fi
# Preserve workday.sh's exit-code contract (exit 3 = runtime failure).
# A plain `LOOKBACK_DATE=$(...)` would swallow non-zero exits and later
# collapse them to a generic `exit 1`; `|| exit $?` keeps the original.
LOOKBACK_DATE=$("$WORKDAY_SH" --last-workday) || exit $?
# LOOKBACK_DATE is now the most recent prior workday (YYYY-MM-DD)
```
3. **Convert to an ISO 8601 timestamp** with the correct UTC offset (handles EST/EDT automatically):
```bash
if [ -n "$ARGUMENTS" ]; then
# User provided an explicit time reference — parse it directly, bypass smart lookback
# The agent should convert $ARGUMENTS to the appropriate date -d / date -v expression.
# Example for "yesterday at noon ET": date -d 'yesterday 12:00' or date -v-1d -v12H -v0M -v0S
SINCE_ISO=$(TZ='America/New_York' date -d "$ARGUMENTS" '+%Y-%m-%dT%H:%M:%S%z' 2>/dev/null || TZ='America/New_York' date -jf '%Y-%m-%d %H:%M' "$ARGUMENTS" '+%Y-%m-%dT%H:%M:%S%z' 2>/dev/null)
if [ -z "$SINCE_ISO" ]; then
echo "Error: Could not parse time reference: $ARGUMENTS" >&2
exit 1
fi
else
# Smart default — LOOKBACK_DATE was set above by workday.sh; any failure
# there already propagated via `|| exit $?`, so LOOKBACK_DATE is non-empty
# here by contract.
# On Windows (Git Bash), TZ may be wrong — try PowerShell first for noon ET on LOOKBACK_DATE.
SINCE_ISO=$(powershell -Command "\$tz=[System.TimeZoneInfo]::FindSystemTimeZoneById('Eastern Standard Time'); \$d=[DateTime]::ParseExact('${LOOKBACK_DATE}','yyyy-MM-dd',[Globalization.CultureInfo]::InvariantCulture); \$local=[DateTime]::SpecifyKind(\$d.Date.AddHours(12), [DateTimeKind]::Unspecified); \$dto=[DateTimeOffset]::new(\$local, \$tz.GetUtcOffset(\$local)); \$dto.ToString('yyyy-MM-ddTHH:mm:sszzz')" 2>/dev/null \
|| TZ='America/New_York' date -d "${LOOKBACK_DATE} 12:00" '+%Y-%m-%dT%H:%M:%S%z' 2>/dev/null \
|| TZ='America/New_York' date -jf '%Y-%m-%d %H:%M' "${LOOKBACK_DATE} 12:00" '+%Y-%m-%dT%H:%M:%S%z')
if [ -z "$SINCE_ISO" ]; then
echo "Error: Failed to convert lookback date to ISO timestamp" >&2
exit 1
fi
fi
SINCE_ISO=$(printf '%s' "$SINCE_ISO" | sed -E 's/([+-][0-9]{2})([0-9]{2})$/\1:\2/')
```
The explicit `if/else` ensures `$ARGUMENTS` overrides the smart lookback cleanly. Both branches produce `SINCE_ISO` in the same format for downstream use.
### Step 2: Pull issues, PRs, and line counts
For each repo, run:
```bash
# Closed issues since the cutoff
gh issue list --state closed --search "closed:>$SINCE_ISO" --json number,title,closedAt --limit 100
# Merged PRs since the cutoff
gh pr list --state merged --search "merged:>$SINCE_ISO" --json number,title,mergedAt,additions,deletions --limit 100
# Currently open PRs (in progress work)
gh pr list --state open --author @me --json number,title,createdAt,additions,deletions
```
### Step 3: Read PR bodies (CRITICAL — do not skip)
**This is what makes the report useful.** Titles alone cannot convey business context.
For every merged PR and every open PR (using the `number` field from Step 2's JSON output), read the PR body:
```bash
# For each PR number from Step 2:
gh pr view "$PR_NUMBER" --json body,title,additions,deletions
```
Scan each PR body for:
- **What the change enables** — the "so what" for the business
- **Concrete numbers** — record counts, accuracy metrics, coverage stats, thresholds
- **Which part of the system** this advances — classification, scraping, data pipeline, UI, etc.
If a PR body is thin or template-only, extract the linked issue number via `pr-issue-ref.sh --first` (matches all nine GitHub closing keywords — `close`/`closes`/`closed`/`fix`/`fixes`/`fixed`/`resolve`/`resolves`/`resolved`, case-insensitive) and read the issue body. `--first` is required: default mode is set-valued (issue #1492), and this call feeds a single `gh issue view`. /standup runs from arbitrary repos, so resolve the script path with the same multi-candidate lookup used for `workday.sh` above. The helper exits 1 with empty stdout when no link is found — distinguish that benign case from exits 2/3/4 (real errors) so genuine failures surface:
```bash
PR_ISSUE_REF_SH=""
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
for candidate in \
"$HOME/.claude/skills-worktree/.claude/scripts/pr-issue-ref.sh" \
"$HOME/.claude/scripts/pr-issue-ref.sh" \
"${GIT_ROOT:+$GIT_ROOT/.claude/scripts/pr-issue-ref.sh}" \
".claude/scripts/pr-issue-ref.sh"; do
[ -n "$candidate" ] || continue
if [ -x "$candidate" ]; then
PR_ISSUE_REF_SH="$candidate"
break
fi
done
if [ -z "$PR_ISSUE_REF_SH" ]; then
echo "Error: could not locate pr-issue-ref.sh" >&2
exit 1
fi
ISSUE_NUMBER=""
if RAW_REF=$("$PR_ISSUE_REF_SH" --first "$PR_NUMBER" 2>&1); then
ISSUE_NUMBER="$RAW_REF"
else
REF_RC=$?
if [ "$REF_RC" -ne 1 ]; then
echo "Warning: pr-issue-ref.sh exit $REF_RC for PR #$PR_NUMBER: $RAW_REF — skipping linked-issue lookup" >&2
fi
fi
if [ -n "$ISSUE_NUMBER" ]; then
gh issue view "$ISSUE_NUMBER" --json body,title
fi
```
### Step 4: Identify business themes
Group the PRs/issues into **2-5 business themes** based on what they collectively accomplish. A theme is a capability or milestone, not a file or module. Examples of good themes:
- "Carrier classification pipeline is production-ready"
- "Portal coverage map is now accurate and scrapable"
- "Batch scraping infrastructure is ready to execute"
Each theme should map to one section of the report.
## How to write the report
### Opening line
Lead with scale stats in a single line:
```text
Since [time reference]: [N] PRs merged, [K] open, ~[M] issues closed, ~[L] lines added / ~[D] removed (~[net] net)
```
- Lines = sum additions and deletions across merged PRs separately, then compute net
### Body: themed sections
For each business theme, write a short paragraph (2-5 sentences) that explains:
1. **What the system can now do** that it couldn't before (lead with this)
2. **Key concrete numbers** from the PR bodies — record counts, accuracy percentages, coverage stats, state counts, etc. These make the report credible and useful.
3. **How it fits** into the broader goal or next step
Name each theme with a bold one-liner that captures the business outcome, not the technical action. e.g., "**Carrier classification pipeline is production-ready**" not "**Added classification code**".
### Open PRs
Mention open PRs inline if they relate to a theme, or as a standalone line at the end:
```text
**Open PR:** #N (short description of what it does and why)
```
### Closing synthesis
End with a 1-2 sentence "net effect" that answers: "What can the system do now that it couldn't at the start of this period?" This is the single most important line — it's what a PM or exec would read if they read nothing else.
## Writing rules
- Frame everything in terms of **business value and system capabilities**, not file names or technical implementation
- **Include concrete numbers** from PR bodies — these are what make the report useful vs. generic. Counts, percentages, thresholds, coverage metrics.
- Group related issues/PRs into a single theme — never list PRs individually unless there are fewer than 4 total
- Write from the user's perspective ("I" / "we") for direct paste into standup
- No word limit — let the report be as long as it needs to be to convey meaningful context, but stay concise. Typical range: 150-400 words depending on volume of work.
- Do NOT mention CR review cycles, code review tooling, or process details — focus on outcomes
- Do NOT pad with filler or repeat the same point in different words