# Github Reader

> Summarize GitHub issues, PRs, milestones, backlog health, release readiness, and recent activity from live data or a dated export. Use after pm-agent routes repository-status work.

- Skill: `neplich/github-reader` (Agent Skill)
- Install (CLI): `npx skillmds@latest add neplich/github-reader`
- Raw SKILL.md: https://api.skillmd.com/api/skills/neplich/github-reader/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: Neplich (https://skillmd.com/u/neplich)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/neplich/github-reader

---


# GitHub Reader

Read a GitHub repository's current state and produce a structured Markdown summary. Live data comes from `gh` CLI — no MCP needed. A user-supplied `gh` JSON export is also valid when the environment cannot reach GitHub: verify its repository and capture time, use its raw `total_count` and collection payloads, and label the report “as of” that capture instead of presenting it as a live query.

This skill serves two purposes:
1. **Direct answer**: respond to the user's question about the repo
2. **Data feed**: provide structured input for downstream skills like `roadmap-gen` or `weekly-report`

## Reader-Facing Writing Composition

For substantial reader-facing prose, co-load `human-writing` even on direct
invocation; use the same context, not a later pass. This Skill retains evidence,
facts, required structure, paths, gates, and verification. Skip code-, config-, schema-,
lockfile-, and data-only output.

## Mandatory Status Shape

Full status includes a milestone progress table with percentages, separate PR
queues for pending review and merged work, and each PR number linked to its
absolute GitHub URL. Use the repository status emoji vocabulary (`✅`, `🟢`, `🟡`,
`🔴`, `⚪`) consistently. Feed mode must also emit a
`github_reader_data` YAML block. Every aggregate issue/PR total must carry the
raw GitHub search `total_count` query evidence; if unavailable, label the total
unavailable rather than deriving or fabricating it.

## Step 1 — Establish repo context

If the request names a repository and supplies a local GitHub evidence export,
read that export first. It must identify the repository, `captured_at`, raw search
query results, and any fetched milestone/issue/PR collections used by the
report. Do not call GitHub or invent missing collections; surface absent or
incomplete fields with the same completeness rules as a live query.

宿主存在 `docs/site/standards/change-map.yaml` 时，项目探索先按 pm-agent 唯一维护源 `consumption-contract.md`（the active installed `idea-to-spec` skill's `_internal/_shared/consumption-contract.md`）执行“任务落点 → change-map 反查 → 精准读取 → 关键判断回代码验证”；不存在时静默沿用当前代码探索。

```bash
gh repo view --json nameWithOwner,url,defaultBranchRef,description
```

If the user specified a repo (e.g. `owner/repo`), use that. Otherwise use the current directory's remote. Capture `OWNER`, `REPO`, and `REPO_URL` for link formatting.

## Step 2 — Determine query scope

Read the user's request to decide what to fetch. Three modes:

| Mode | When | What to fetch |
|------|------|---------------|
| **Full status** | "项目状态" / "health check" / no specific focus | Milestones + open issues summary + PR queue |
| **Focused query** | Specific question ("哪些 PR 等 review", "milestone X 进度") | Only the relevant data |
| **Feed mode** | Called by another skill needing raw data | Full status, output structured for downstream use |

Default to **Full status** if the intent is ambiguous.

## Step 3 — Fetch data

Run only what the scope requires. Commands below are the full toolkit — use selectively.

### Step 3a — Count collections before fetching

Full status / Feed mode 下，在获取各计算集合前，必须先通过 search API 取得精确总数。先计算近 14 天窗口的 `{DATE}`，再执行：

```bash
gh api search/issues -X GET -f q='repo:{OWNER}/{REPO} is:issue is:open' --jq '{total_count, incomplete_results}'
gh api search/issues -X GET -f q='repo:{OWNER}/{REPO} is:pr is:open' --jq '{total_count, incomplete_results}'
gh api search/issues -X GET -f q='repo:{OWNER}/{REPO} is:pr is:merged merged:>{DATE}' --jq '{total_count, incomplete_results}'
gh api search/issues -X GET -f q='repo:{OWNER}/{REPO} is:issue is:closed closed:>{DATE}' --jq '{total_count, incomplete_results}'
```

Focused query 可按需只获取相关集合的 `total_count`。

> **[强制规则] 数据完整性声明：**
> 1. 健康摘要与汇总数字一律以 Step 3a 的 `total_count` 为准，不得用获取集合长度冒充总数
> 2. 任一 search 查询返回 `incomplete_results: true` 时，对应集合的总数必须标注「GitHub 标记结果不完整（incomplete_results），总数为参考值」，不得呈现为精确总数；健康摘要中相关数字带同样限定
> 3. 任一集合的获取数小于 `total_count` 时，必须在该集合小节和健康摘要中显式声明：`⚠️ 数据截断：已获取 {fetched}/{total} 条，分类统计基于已获取部分`
> 4. 禁止在未声明截断的情况下，把部分集合的分类统计或健康信号呈现为完整状态
> 5. **展示限行**仅控制报告展示数量（例如待 Review 表格最多 10 行并附汇总行）；**计算集合限行**控制用于分类和健康信号的获取集合（上限 1000）。两者互不影响
> 6. Milestones 集合必须通过 `--paginate` 获取完整集合；若分页获取失败或集合不完整，必须在 Milestones 小节和健康摘要中声明数据不完整，不得呈现为完整状态

### Milestones
```bash
gh api repos/{OWNER}/{REPO}/milestones \
  --paginate \
  --jq '.[] | {title, state, open_issues, closed_issues, due_on, html_url}' \
  -X GET -f state=open
```

### Open issues (grouped by milestone)
```bash
gh issue list \
  --json number,title,state,labels,milestone,assignees,createdAt,updatedAt \
  --state open --limit 1000
```

上述 `--limit 1000` 是计算集合限行；open issue 分类与健康信号基于该获取集合计算。

### Recently closed issues (last 14 days)
```bash
gh issue list \
  --json number,title,closedAt,milestone \
  --state closed --limit 1000 \
  --search "closed:>$(date -d '14 days ago' +%Y-%m-%d 2>/dev/null || date -v-14d +%Y-%m-%d)"
```

上述 `--limit 1000` 是计算集合限行；近期 closed issue 分类与健康信号基于该获取集合计算。

### PR queue
```bash
gh pr list \
  --json number,title,state,author,reviewDecision,createdAt,labels,isDraft \
  --state open --limit 1000
```

上述 `--limit 1000` 是计算集合限行；open PR 分类与健康信号基于该获取集合计算。

> **[强制规则] PR 分类处理流程：**
> 1. 先按 `author.login` 过滤 bot（含 `[bot]` 后缀）→ 归入 Bot 区域
> 2. 再按 `isDraft` 过滤草稿 → 归入草稿区域
> 3. 剩余按 `reviewDecision` 分：`CHANGES_REQUESTED` → 需作者跟进；其余 → 待 Review
> 4. 待 Review 表格必须包含 `labels` 列（取 label name，逗号分隔；无标签写 `-`）
> 5. Full-status 模式下待 Review 表格最多 10 行，超出写汇总行

### Recently merged PRs (last 14 days)
```bash
gh pr list \
  --json number,title,mergedAt,author \
  --state merged --limit 1000 \
  --search "merged:>$(date -d '14 days ago' +%Y-%m-%d 2>/dev/null || date -v-14d +%Y-%m-%d)"
```

上述 `--limit 1000` 是计算集合限行；近期 merged PR 分类与健康信号基于该获取集合计算。

## Step 4 — Compute health signals

各信号基于 Step 3 获取的计算集合；总数类数字来自 Step 3a 的 `total_count`。

From the fetched data, derive:

- **Milestone completion %** = `closed_issues / (open_issues + closed_issues) * 100`
- **Overdue milestones** = milestones with `due_on` in the past and `open_issues > 0`
- **Stale issues** = open issues not updated in > 30 days
- **Unassigned open issues** = issues with empty `assignees`
- **PRs waiting review** = open PRs where `reviewDecision` is `REVIEW_REQUIRED` or null, and not draft
- **Draft PRs** = open PRs where `isDraft` is true

## Step 5 — Format the output

Use this structure. Omit sections that have no relevant data.

```markdown
## 项目状态：{OWNER}/{REPO} — {TODAY}

### Milestones
| Milestone | 进度 | 截止日期 | 状态 |
|-----------|------|---------|------|
| [v2.0]({URL}) | 12/20 (60%) | 2024-04-01 | 🟡 进行中 |
| [v1.5]({URL}) | 8/8 (100%) | 2024-03-01 | ✅ 完成 |

> 状态图例：✅ 完成 / 🟢 顺利 / 🟡 进行中 / 🔴 逾期 / ⚪ 无截止日期

### Open Issues ({N} 个)

**按 Milestone 分组：**
- **v2.0**（{N} 个）：[#{N} 标题]({URL})、...
- **无 Milestone**（{N} 个）：[#{N} 标题]({URL})、...

**需关注：**
- 🙋 无 assignee：{N} 个
- 😴 30 天未更新：{N} 个

### PR 队列

**待 Review — 人工贡献（{N} 个，按等待时间排序）：**

| # | 标题 | 作者 | 等待 | 标签 |
|---|------|------|------|------|
| [#{N}]({URL}) | 标题 | @author | {N} 天 | external, needs-triage |

> **[强制规则] Full-status 模式下，此表格最多显示 10 条**。超出部分只写一行汇总："还有 {N} 个待 review PR 未列出，最老的是 #{X}（{Y} 天）"。Focused 模式不受此限制，显示全部。

**Bot/自动化 PR（{N} 个，可批量处理）：**
- Dependabot: {N} 个，Renovate: {N} 个

> **[强制规则] Bot PR 必须从"待 Review"表格中剥离。** 判断标准：author 为 `dependabot[bot]`、`renovate[bot]`、`github-actions[bot]`、`stainless-app[bot]`、`copilot-swe-agent[bot]` 或任何 `[bot]` 后缀的 author。这些 PR 只在此 Bot 区域统计，不出现在上方人工 PR 表格中。

**草稿（{N} 个）：**
- [#{N} 标题]({URL}) — @{author}，{N} 天

**需作者跟进（Changes Requested，{N} 个）：**
- [#{N} 标题]({URL}) — @{author}

**近 14 天已合并（{N} 个）：**
- [#{N} 标题]({URL})

### 健康摘要
- 共 {N} 个 open issue，{N} 个 open PR
- {N} 个 milestone 进行中，{N} 个逾期
- 近 14 天：合并 {N} 个 PR，关闭 {N} 个 issue
- ⚠️ 积压风险（等待 > 90 天的人工 PR）：{N} 个
```

**Milestone 状态判断：**
- `due_on` 为空 → ⚪ 无截止日期
- 完成率 100% → ✅ 完成
- `due_on` 已过期且 `open_issues > 0` → 🔴 逾期
- 完成率 ≥ 70% → 🟢 顺利
- 其他 → 🟡 进行中

## Feed mode output

When called by another skill, append a `---` separator and a machine-friendly YAML block after the Markdown report:

```yaml
---
github_reader_data:
  repo: owner/repo
  fetched_at: 2024-03-20
  open_issues_total: 15
  open_prs_total: 8
  merged_prs_14d_total: 4
  closed_issues_14d_total: 3
  truncated_collections: []
  incomplete_totals: []
  milestones:
    - title: v2.0
      completion_pct: 60
      open: 8
      closed: 12
      due_on: 2024-04-01
      overdue: false
  prs_awaiting_review: 3
  prs_draft: 1
  unassigned_issues: 4
  stale_issues: 2
```

健康数字字段（如 `open_issues_total`、`open_prs_total`、`merged_prs_14d_total`、`closed_issues_14d_total`）必须使用 Step 3a 的 `total_count`。任一计算集合被截断时，必须把集合名加入 `truncated_collections`（例如 `[open_issues, open_prs]`）；任一 search 返回 `incomplete_results: true`，或 milestones 分页失败/集合不完整时，必须把对应集合名加入 `incomplete_totals`（例如 `[open_issues, milestones]`），告知下游对应总数为参考值。两个字段在无触发时均可省略。

This lets downstream skills parse state without re-fetching.

## Edge cases

- **No milestones**: skip that section, note "暂无 milestone" in the summary
- **Large repos**: milestones 通过 `--paginate` 获取完整集合；open issues、open PR 队列和近期 merged PR 的计算集合上限均为 1000，超出时优先用 `--milestone`、`--label` 或时间窗收窄。收窄后仍超出上限时，按 Step 3a 的数据完整性规则显式声明截断
- **No GitHub auth**: `gh auth status` will fail — surface the error clearly and tell the user to run `gh auth login`
- **Focused query shortcut**: if the user only asks about PRs, skip issue fetching entirely to save time

