Analyze GitHub project activity
Produce a succinct report on a GitHub project's activity — throughput, backlog burn-rate, and responsiveness — backed by matplotlib charts. The bundled analyze.py does the fetching, windowing, and plotting; your job is to run it and turn its JSON summary into a short, insightful report.
How to run
- Resolve the target repo (
owner/name), in this order:
- Resolve the window. Default is the last 6 months. Honor any user phrasing ("last 3 months", "this year") via
--months N.
- Query the Emacs foreground color once (reuse if already known this session):
emacsclient --eval '(face-foreground (quote default))'
Returns a hex like "#eeffff". Pass it as --fg.
- Run the analyzer (it shells out to
gh, computes metrics, writes charts, prints a JSON summary):uv run --with matplotlib "$CLAUDE_PLUGIN_ROOT/skills/github-activity/analyze.py" \
--repo owner/name --months 6 --fg "#eeffff" --outdir /tmp
If $CLAUDE_PLUGIN_ROOT is unset, use the skill's own directory path.
- Read the JSON printed on stdout and write the report (structure below), embedding each returned chart path as a markdown image on its own line:

Report structure (keep it tight)
Order the report bottom-line-up-front: lead with the takeaway, then the evidence, and keep each chart directly next to the prose that interprets it. A skimmer should get the whole story from the TL;DR alone; everything below it is drill-down.
TL;DR — 2–3 bullets at the very top: the verdict on how active/healthy the project is, the one or two numbers that prove it, and the implication (e.g. "issues need a triage pass"). This is the report for anyone who reads only the top.
Snapshot table — metrics as rows, PRs and Issues as the two columns, numeric columns right-aligned. Put the window in a caption line above the table (not as a row — it applies to both columns and would leave blank cells). One metric per row:
Activity — last N months (FROM → TO)
| Metric |
PRs |
Issues |
| Opened |
… |
… |
| Closed |
… |
… |
| Net (opened − closed) |
… |
… |
| Open now |
… |
… |
| Median days to close |
… |
… |
| p90 days to close |
… |
… |
Emphasis: bold at most one or two cells — the value(s) that justify the headline (typically the concerning one: a large p90, a positive/growing net, or a high open now). Do not bold routinely, do not bold both the good and bad value, and don't use color or emoji (they don't render in terminal tables).
Throughput — the chart, immediately followed by 1–2 bullets that read it (don't defer interpretation to a separate block). Draw from:
- Is intake steady or spiking? Is closing keeping up (net trend)?
- Are PRs healthier than issues (or vice versa)?
- Steady drip vs lumpy cleanup bursts (look at
busiest_weeks).
Backlog — include only if it adds signal (a clear growth/decline trend); skip if flat. When included, put the chart with a single bullet noting the trend.
Most-wanted open issues (optional) — the top entries from top_open_issues (open issues by 👍 reactions — a demand signal). List up to 3 as [#N](url) — title (👍 R, C comments). Skip if the list is empty (no issue has reactions). This is distinct from oldest_open: reactions show demand, age shows staleness.
Next steps (optional, ≤3 bullets) — only when the data motivates a concrete action, and each must be specific: cite the actual items from oldest_open_issues / oldest_open_prs / top_open_issues as linked references (e.g. "Triage or close #142 — open 156 days"; "#511 has 8 👍 and 9 comments — high demand, still open"). Prefer high-demand items over merely old ones when prioritizing. Point at real signals (a growing net, an aging p90). Skip this section entirely when the project is healthy; never emit generic filler like "keep triaging issues" or "review PRs promptly". A bad actionable is worse than none.
Prefer concrete numbers over adjectives. Do not dump the raw JSON. Do not restate every weekly value — summarize the shape. Don't repeat the TL;DR verbatim lower down — the bullets under each chart should add detail, not echo.
Always link issue/PR references. Every time you mention a PR or issue anywhere in the report (TL;DR, bullets, tables, next steps), render it as a markdown link [#N](url) using the url field the analyzer provides for that item (oldest_open_* and top_open_issues each carry a url). Never leave a bare #N.
Rules
- Charts follow the
matplotlib skill conventions: transparent background, Emacs FG color for text/spines/ticks, dark opaque legend box (facecolor='#1a1a2e', labelcolor=FG), hidden top/right spines, subtle grid. analyze.py already does this — pass the real --fg.
- Timestamped filenames (the script handles this). Never overwrite a fixed name.
- The final (current) week is partial; the script hatches it and flags
partial: true — never compare it head-to-head with full weeks. Say "in-progress" when referencing it.
- If the repo has very little activity in the window (few PRs/issues), say so plainly and skip charts that would be noise.
- Requires
gh (authenticated) and uv. If gh errors on auth or a missing repo, surface that instead of guessing.
1---2name: github-activity3description: This skill should be used when the user invokes "/github-activity" to analyze a GitHub project's recent activity (PRs and issues) and produce a succinct, chart-backed health report.4---56# Analyze GitHub project activity78Produce a **succinct** report on a GitHub project's activity — throughput, backlog burn-rate, and responsiveness — backed by matplotlib charts. The bundled `analyze.py` does the fetching, windowing, and plotting; your job is to run it and turn its JSON summary into a short, insightful report.910## How to run11121. **Resolve the target repo** (`owner/name`), in this order:13 - An argument the user passed to `/github-activity` (e.g. `/github-activity xenodium/agent-shell`).14 - Otherwise, infer from the current directory's `origin` remote:15 ```sh16 gh repo view --json nameWithOwner -q .nameWithOwner17 ```18 - If neither works, ask the user which repo.192. **Resolve the window.** Default is the last **6 months**. Honor any user phrasing ("last 3 months", "this year") via `--months N`.203. **Query the Emacs foreground color** once (reuse if already known this session):21 ```sh22 emacsclient --eval '(face-foreground (quote default))'23 ```24 Returns a hex like `"#eeffff"`. Pass it as `--fg`.254. **Run the analyzer** (it shells out to `gh`, computes metrics, writes charts, prints a JSON summary):26 ```sh27 uv run --with matplotlib "$CLAUDE_PLUGIN_ROOT/skills/github-activity/analyze.py" \28 --repo owner/name --months 6 --fg "#eeffff" --outdir /tmp29 ```30 If `$CLAUDE_PLUGIN_ROOT` is unset, use the skill's own directory path.315. **Read the JSON** printed on stdout and **write the report** (structure below), embedding each returned chart path as a markdown image on its own line:32 ```33 34 ```3536## Report structure (keep it tight)3738Order the report **bottom-line-up-front**: lead with the takeaway, then the evidence, and keep each chart directly next to the prose that interprets it. A skimmer should get the whole story from the TL;DR alone; everything below it is drill-down.3940- **TL;DR** — 2–3 bullets at the very top: the verdict on how active/healthy the project is, the one or two numbers that prove it, and the implication (e.g. "issues need a triage pass"). This is the report for anyone who reads only the top.41- **Snapshot table** — metrics as rows, PRs and Issues as the two columns, numeric columns right-aligned. Put the window in a **caption line above** the table (not as a row — it applies to both columns and would leave blank cells). One metric per row:4243 **Activity — last N months (FROM → TO)**4445 | Metric | PRs | Issues |46 |---|--:|--:|47 | Opened | … | … |48 | Closed | … | … |49 | Net (opened − closed) | … | … |50 | Open now | … | … |51 | Median days to close | … | … |52 | p90 days to close | … | … |5354 Emphasis: **bold at most one or two cells** — the value(s) that justify the headline (typically the concerning one: a large `p90`, a positive/growing `net`, or a high `open now`). Do not bold routinely, do not bold both the good and bad value, and don't use color or emoji (they don't render in terminal tables).55- **Throughput** — the chart, **immediately followed by 1–2 bullets that read it** (don't defer interpretation to a separate block). Draw from:56 - Is intake steady or spiking? Is closing keeping up (net trend)?57 - Are PRs healthier than issues (or vice versa)?58 - Steady drip vs lumpy cleanup bursts (look at `busiest_weeks`).59- **Backlog** — include only if it adds signal (a clear growth/decline trend); skip if flat. When included, put the chart with a single bullet noting the trend.60- **Most-wanted open issues** (optional) — the top entries from `top_open_issues` (open issues by 👍 reactions — a demand signal). List up to 3 as `[#N](url) — title (👍 R, C comments)`. Skip if the list is empty (no issue has reactions). This is distinct from `oldest_open`: reactions show *demand*, age shows *staleness*.61- **Next steps** (optional, ≤3 bullets) — only when the data motivates a concrete action, and each must be **specific**: cite the actual items from `oldest_open_issues` / `oldest_open_prs` / `top_open_issues` as linked references (e.g. "Triage or close [#142](url) — open 156 days"; "[#511](url) has 8 👍 and 9 comments — high demand, still open"). Prefer high-demand items over merely old ones when prioritizing. Point at real signals (a growing `net`, an aging `p90`). Skip this section entirely when the project is healthy; never emit generic filler like "keep triaging issues" or "review PRs promptly". A bad actionable is worse than none.6263Prefer concrete numbers over adjectives. Do not dump the raw JSON. Do not restate every weekly value — summarize the shape. Don't repeat the TL;DR verbatim lower down — the bullets under each chart should add detail, not echo.6465**Always link issue/PR references.** Every time you mention a PR or issue anywhere in the report (TL;DR, bullets, tables, next steps), render it as a markdown link `[#N](url)` using the `url` field the analyzer provides for that item (`oldest_open_*` and `top_open_issues` each carry a `url`). Never leave a bare `#N`.6667## Rules6869- Charts follow the `matplotlib` skill conventions: transparent background, Emacs FG color for text/spines/ticks, dark opaque legend box (`facecolor='#1a1a2e'`, `labelcolor=FG`), hidden top/right spines, subtle grid. `analyze.py` already does this — pass the real `--fg`.70- Timestamped filenames (the script handles this). Never overwrite a fixed name.71- The final (current) week is partial; the script hatches it and flags `partial: true` — never compare it head-to-head with full weeks. Say "in-progress" when referencing it.72- If the repo has very little activity in the window (few PRs/issues), say so plainly and skip charts that would be noise.73- Requires `gh` (authenticated) and `uv`. If `gh` errors on auth or a missing repo, surface that instead of guessing.