| name | pm-report |
| description | Use this skill when the user asks for a "status report", "what changed", "what's stuck", "what shipped", "velocity report", "what's next to pick up", "review prs", "open prs", "ready to merge", "is this project on track", "project health", "are projects at risk", "which projects are off track", or any project status summary that should be persisted as a document or sent as email. **Eight profiles** — movement (default — what changed), stuck (what's blocked), throughput (velocity vs prior), next-up (what's ready), exec (one-pager standup), prs (open-PR review), full (legacy daily/weekly behavior), **project-health** (per-project on-track / at-risk / off-track classification with milestone burndown — for project-tier health questions like "is this project on track" or "which projects are at risk"). Produces Markdown plus HTML and/or PDF; optional handoff to resend-cli for delivery. |
| version | 0.44.4 |
PM Report
Generate an actionable, profile-driven status report for the active project. Each profile renders only the sections that answer one operator question — no more "show everything by default." Output formats: Markdown (always), HTML, PDF, or both.
If the user asks for a briefing in the conversation (no document, no email) — that is pm-status's job, not this skill. Reports here are intended to be persisted and shared.
When to use this skill
Trigger phrases grouped by the profile they imply:
- movement (default): "what changed", "what shipped", "what got done today/this week", "daily movement"
- stuck: "what's stuck", "what's blocked", "what's dragging", "anything stalled"
- throughput: "velocity report", "are we on track", "pace this week", "throughput vs last week"
- next-up: "what's next", "what's ready to pick up", "what should I grab next"
- exec: "standup", "status report", "daily report", "weekly report", "one-pager"
- prs: "review prs", "open prs", "what prs need attention", "ready to merge", "waiting on me to review"
- full: "everything", "full report", legacy
--kind daily|weekly without --profile
Also fires on the /pm-report slash command.
Profiles
Each profile is a composition of lenses (pure functions in hooks/lib/report-lenses.js) plus a bundled template.
- movement (default) — Answers "what changed in the window?" Renders completions, started/reopened, created, and updated-only buckets plus a per-team movement rollup and per-assignee accountability. Use when the operator wants a record of what moved.
- stuck — Answers "where are we blocked?" Renders newly-stale issues (crossed the threshold inside the window), drifting WIP, unassigned-with-priority, blocked-labeled, and awaiting-info-from-operator buckets, plus a WIP-aging distribution. Use when the operator wants an unblock-list.
- throughput — Answers "are we moving faster or slower?" Renders completion-rate compare, cycle-time compare, per-team and per-assignee deltas vs the prior equal-length window, and end-of-window WIP aging. Use when the operator wants a velocity readout.
- next-up — Answers "what's ready to pick up?" Renders ready-to-work issues (priority + assignee + template-validated title) plus watchouts (close-to-ready with missing signals) and a compact "stuck on the operator's desk" tail. Use when the operator is planning the next slice of work.
- exec — One-pager standup. Renders single-line headlines for movement, stuck, and throughput plus top-N changes / top-N stuck. Use when the operator wants something to paste into a stakeholder update or email.
prs
Reports across open pull requests rather than issues. Buckets PRs by funnel stage (draft, no_reviewers, awaiting_review, changes_requested, checks_failing, conflicts, approved_pending, ready_to_merge), shows what's waiting on you (to review / push / merge), flags stale and orphan PRs, and cross-references the issue stuck lens to surface PRs whose merge would directly unstick blocked work. Requires pull-prs (or prs-sync verb) to populate the prs.json cache first. Needs the gh CLI installed and authenticated.
- full — Legacy "render everything" behavior. Maps to
daily.md (kind=daily) or weekly.md (kind=weekly). Preserved for back-compat with scheduled reports and existing scripts.
Profile resolution
The skill picks a profile using this decision tree (first match wins):
- Explicit flag —
--profile <name> is always honored.
- Legacy kind —
--kind daily|weekly with no --profile resolves to full (the legacy templates).
- Natural-language match against the operator message:
| Phrase | Profile |
|---|
| "stuck" / "blocked" / "dragging" | stuck |
| "shipped" / "what got done" / "what changed" | movement |
| "velocity" / "pace" / "on track" / "throughput" | throughput |
| "ready" / "what's next" / "pick up" | next-up |
| "standup" / "status report" / "daily report" / "weekly report" | exec |
| "everything" / "full" | full |
- Fallback —
movement.
If the operator message is ambiguous and the cache is empty (cold start), ask once: "Movement (what changed), Stuck (what needs unblocking), or Throughput (velocity)?"
Legacy single-slug inputs (still supported)
For back-compat, the original per-slug invocation still works: --slug <slug> --kind daily|weekly (without --profile) reads the slug's cache + snapshots and renders the legacy daily / weekly templates via the full profile. Prefer the profile + scope form for new reports.
Workflow
Step 1: Resolve the profile
Apply the decision tree above. If the user passes --profile, use it. Otherwise pattern-match the message and fall back to movement.
Step 2: Resolve the scope
One of workspace:<slug> (default), team:<KEY>[+subteams], project:<slug>[+deps[:upstream|downstream]], repo:<org/repo>, or all. Ask if not specified: "Scope this report to workspace / team / project / repo / all?"
Step 3: Resolve the window
today (since UTC midnight), week (last 7 days), month (last 30 days), or custom <YYYY-MM-DD> <YYYY-MM-DD>. Ask if not specified.
Step 4: Render + run the matching pm-script
Each profile has a bundled pm-script that refreshes the cache deterministically (pull-movement, pull-stuck, pull-throughput, pull-next-up, pull-exec). Render the first ~20 lines of the substituted script and ask "Run this?" before executing — unless the caller passes --auto-pull (CI / cron). Skip this step entirely if pm-status already synced the workspace this session and the profile doesn't need extra snapshots.
Step 5: Invoke the generator
node "$CLAUDE_PLUGIN_ROOT/skills/pm-report/scripts/generate-report.js" \
--profile <movement|stuck|throughput|next-up|exec|full> \
--scope "<scope-expression>" \
--from "<YYYY-MM-DD>" --to "<YYYY-MM-DD>" \
--format both \
--out "$HOME/.claude/project-manager/reports/<slug>"
Key flags:
--profile <name> — movement | stuck | throughput | next-up | exec | full
--kind daily|weekly — back-compat (without --profile, maps to full)
--scope <expr> — scope grammar (workspace/team/project/repo/all)
--from, --to — explicit window endpoints (overrides --date)
--operator <login> — needed by the needs_info_self reason in the stuck lens
--auto-pull — skip the "Run this?" gate (CI / cron)
--format pdf|html|both|md — default both (always also writes .md)
--out <dir> — default ~/.claude/project-manager/reports/<slug>
--template <path> — override the bundled template
--date <YYYY-MM-DD> — override "today" for testing / backfilling
The script prints JSON describing what it wrote:
{ "ok": true, "profile": "movement", "files": ["...md", "...html", "...pdf"], "comparedTo": { "...": "..." }, "lensesUsed": ["movement","accountability","inProgress"] }
Step 6: Show paths + offer delivery
Read the JSON output and tell the operator where each file landed and what it compared against (so they can spot when a snapshot was missing). Then offer delivery via the resend-cli skill — only ask if the project has a .resend.md configured or the user mentioned sending. If emailing, hand off the HTML file as the body; do not attach the PDF unless the user asks.
First-time PDF setup (one-time)
PDF rendering uses md-to-pdf (Puppeteer + Chromium). On first use the script installs it into the plugin's local .cache/ (no system-wide install) and downloads Chromium (~300MB). Subsequent runs are fast. If the user asks for HTML only, this step is skipped. If the install fails (offline, sandboxed) the script falls back to MD + HTML and reports pdf: skipped (reason) — the user can rerun later.
Lenses
Profiles are compositions of pure-function lenses defined in hooks/lib/report-lenses.js. Each lens (e.g. movement, stuck, agingWIP, throughput, readyForNext, firstTimeStale, accountability, inProgress) takes plain JS inputs and returns plain JS outputs — no I/O, no implicit Date.now(). The same lens module is consumed by pm-status, pm-handoff, and pm-improve, so "stuck" means the same thing everywhere.
Custom templates can be built by referencing the lenses + placeholder list in references/placeholders.md. Override with --template <path> at invocation time.
Snapshots and accuracy
The generator diffs the current cache against per-day snapshots written by the pm-session-end hook (once per UTC day). Profiles select the comparison point automatically — movement and stuck compare against the snapshot at window.from; throughput additionally needs the snapshot at window.from − duration for the prior-window delta.
A project with no activity for several days produces reports that diff against the last day there was activity — that's intended; it keeps reports meaningful instead of empty. Profiles requiring a missing snapshot render the relevant section with a notes: "insufficient_history" marker instead of a misleading delta. If no prior snapshot exists at all (fresh project, first use), the report renders without a diff section and notes "No prior snapshot — this is the first report."
Templates
Bundled templates live in assets/templates/. Each is ≤ ~60 lines and uses the placeholders documented in references/placeholders.md.
| Profile | Template |
|---|
| movement | assets/templates/movement.md |
| stuck | assets/templates/stuck.md |
| throughput | assets/templates/throughput.md |
| next-up | assets/templates/next-up.md |
| exec | assets/templates/exec.md |
| prs | assets/templates/prs.md |
| full (daily) | assets/templates/daily.md |
| full (weekly) | assets/templates/weekly.md |
Override with --template <path> if a project needs a custom layout.