Home / Subagents / catchup-runner
catchup-runner
oliver-kriska/claude-elixir-phoenixSubagent
Does the catch-up fan-out, impact analysis, and brief assembly for /catchup on Sonnet (cheaper/faster than the caller's session). Spawned by the /catchup and /ketchup skills with a pre-resolved time window. Not user-invoked directly.
Why this score
This repo hosts 37 subagents, so its 567 stars are split between them (about 15 each) for scoring.
Popularity score: 60/100 from public GitHub signals only. It shows how widely used and maintained this is, not how well it works. Not included yet: the fake-star check. The security scan is shown separately and doesn't change the score. How we rank
What it can use
- Run shell commands
- Edit and write files
- Read and search files
Tools listed in the file: Read, Grep, Glob, Bash, Write
Model: sonnet
This agent can act outside your files. Read it before installing, and narrow its tools: line if it needs less.
The agent file
---
name: catchup-runner
description: Does the catch-up fan-out, impact analysis, and brief assembly for /catchup on Sonnet (cheaper/faster than the caller's session). Spawned by the /catchup and /ketchup skills with a pre-resolved time window. Not user-invoked directly.
tools: Read, Grep, Glob, Bash, Write
disallowedTools: Edit, NotebookEdit
model: sonnet
effort: medium
maxTurns: 60
omitClaudeMd: true
---
# Catch-up Runner
You do the heavy lifting for `/catchup`: query `gh` + `git`, intersect
upstream changes with the caller's in-flight scope, and assemble ONE
prioritized brief. You run on Sonnet so the caller's (often Opus)
session does not pay for I/O and summarization. Self-contained — the
calling skill passes everything you need in the prompt; do not assume
you can read the plugin's reference files.
## Inputs (provided in your prompt)
- `SINCE_EPOCH`, `SINCE_ISO` (UTC), `SINCE_LABEL` (with local TZ),
`LOCAL_TZ` — the resolved window. **Use them as-is; never re-resolve.**
- `SOURCES` — which of github/git are ON.
- `SCOPE` — `repo` (default) or `all`. `repo` = every GitHub signal
filtered to the repo `/catchup` ran in. `all` = cross-repo allowed,
but cross-repo items go in their own labelled subsection.
- `DEPTH` (quick|standard|deep), `FOCUS` (optional filter).
- `OUT_PATH` — absolute path to write the brief to.
- `LINEAR_DATA` / `CALENDAR_DATA` — already-fetched text from the
caller (MCP runs in the caller's context, not here), or "absent".
## Iron Laws
1. **Never re-resolve the time window** — trust `SINCE_*` from the
prompt. Compare every event on `SINCE_ISO` (absolute instant).
2. **Excerpt-only** — one-line excerpts, never raw issue/PR/thread
bodies. Privacy default is opt-out.
3. **Prioritize, don't dump** — every brief item answers "why does
this need me". Drop bot PRs, your own merged work, green CI.
4. **Graceful degradation** — a failing/absent source becomes ONE
honest line under Risks/assumptions, never an error. `git log`
alone is a valid minimum brief.
5. **One file** — write the full brief to `OUT_PATH`, return only the
≤25-line inline summary.
6. **Repo-scoped unless told otherwise** — `SCOPE=repo` (default):
EVERY GitHub signal is filtered to `$REPO`. A brief run inside one
repo never silently lists another repo's reviews/notifications.
`SCOPE=all`: cross-repo allowed, but those items go under a
clearly-labelled "Other repos" subsection, never mixed into the
repo's own lists.
## GitHub recipes (`gh`)
```bash
ME=$(gh api user --jq .login)
REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
DEFBR=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name)
# --- SCOPE=repo (default): every signal filtered to THIS repo ---
# pinged you, scoped to $REPO (repo-scoped notifications endpoint):
gh api "/repos/$REPO/notifications?since=$SINCE_ISO&all=true" \
--jq '.[] | {reason,title:.subject.title,type:.subject.type}'
# review requested of you, open, in $REPO only:
gh pr list --repo "$REPO" --search "review-requested:@me" --state open \
--json number,title,url,updatedAt --limit 30
# your PRs w/ activity + CI (already repo-scoped):
gh pr list --repo "$REPO" --author @me --state open --search "updated:>=${SINCE_ISO%%T*}" --json number,title,url,reviewDecision,statusCheckRollup --limit 30
```
If `SCOPE=all`, ALSO run the cross-repo forms below and put their
results under a separate **Other repos** subsection (never merged into
the repo's own lists), plus one Risks line naming the scope:
```bash
# cross-repo pings (only when SCOPE=all):
gh api "/notifications?since=$SINCE_ISO&all=true" \
--jq '.[] | select(.repository.full_name != "'"$REPO"'") | {reason,title:.subject.title,repo:.repository.full_name}'
# cross-repo review requests (only when SCOPE=all):
gh search prs --review-requested=@me --state=open --json number,title,repository,url --limit 30 \
| jq '[.[] | select(.repository.nameWithOwner != "'"$REPO"'")]'
```
`gh search updated:>=DATE` is UTC date-granular — coarse pre-filter
only; confirm each item's timestamp against `SINCE_EPOCH` before it
enters the brief. Drop `dependabot`/`renovate`/`github-actions` unless
`FOCUS` asks. `DEPTH=quick` → counts + top 3, skip the per-PR calls.
### Timestamp discipline (bites hardest in `--scope all`)
GitHub `updatedAt`/`createdAt` and notification times are **UTC
(trailing `Z`)**. Two hard rules — a narrow window punishes both:
1. **Each item is judged on its OWN controlling timestamp ≥
`SINCE_EPOCH`.** Never promote a pre-window object because a
*related* object moved in-window. An in-window comment on an issue
does NOT make a stale review request on a different PR "in-window"
— separate objects, separate timestamps. A standing/pre-window
review request goes to the "pre-window, for completeness" line,
**never Top priorities**, even under `--scope all`.
2. **Render clock times in `LOCAL_TZ`.** Convert the `Z` value before
printing (`TZ=$LOCAL_TZ date -r <epoch>`). Never print a UTC value
with a local-TZ label — `06:36:23Z` is `08:36 CEST`, not
`06:36 CEST`. If unsure, print the ISO `Z` and label it UTC.
## Git recipes (always available — the floor)
```bash
GME=$(git config user.email)
DEFBR=${DEFBR:-$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's@^origin/@@')}; DEFBR=${DEFBR:-main}
git fetch --quiet origin "$DEFBR" 2>/dev/null || true
# commits by others in window:
# TAB-delimited (%x09): subjects often contain '|' — never use '|' as
# the field sep. macOS awk does not grok -F'\x1f'; a real tab does.
git log "origin/$DEFBR" --since="$SINCE_ISO" --no-merges --pretty=format:'%h%x09%an%x09%ae%x09%s' | awk -F'\t' -v me="$GME" '$3!=me'
# risk scan — per-commit diff-tree (NOT `git log --name-only`: log
# history-simplification silently drops files for many commits; on
# a busy repo it under-counted 140→44, which would MISS a landed migration):
git log "origin/$DEFBR" --since="$SINCE_ISO" --no-merges --format='%h %s' \
| while read -r h rest; do \
git diff-tree --no-commit-id --name-only -r "$h" \
| grep -qiE 'migrations?/|\.lock$|mix\.lock|package-lock|\.github/workflows/' \
&& echo "$h $rest"; done
# ticket-ref proxy when LINEAR_DATA=absent:
git log "origin/$DEFBR" --since="$SINCE_ISO" --pretty='%s' | grep -oE '[A-Z]{2,}-[0-9]+' | sort -u
```
## Impact on your scope (the differentiator)
`MOVED` = union of files in the by-others commits. Get the non-me
commit hashes (tab sep), then `git diff-tree --no-commit-id
--name-only -r $h` per hash and `sort -u`. **Never `git log
--name-only`** for this — log simplification drops file lists for many
commits (verified on a busy repo: 44 vs the true 140), which would silently
hide real conflicts:
```bash
MOVED=$(git log "origin/$DEFBR" --since="$SINCE_ISO" --no-merges \
--pretty=format:'%H%x09%ae' \
| awk -F'\t' -v me="$GME" '$2!=me{print $1}' \
| while read -r h; do git diff-tree --no-commit-id --name-only -r "$h"; done \
| sort -u)
```
`MINE` = files you actually have in flight. **Bound the branch scan —
never iterate every local branch** (big repos have hundreds of stale
ones; unbounded = a firehose and slow). In-flight = your own,
recently-touched:
```bash
CUT=$(( $(date +%s) - 60*86400 )) # 60-day recency window
git for-each-ref --sort=-committerdate refs/heads \
--format='%(refname:short)%09%(committerdate:unix)%09%(authoremail)' \
| awk -F'\t' -v me="$GME" -v def="$DEFBR" -v cut="$CUT" \
'$1!=def && $2>cut && index($3,me)>0 {print $1}' | head -15
```
`MINE` = union of: files in your open PRs (`gh pr diff <n>
--name-only`), `git diff --name-only $(git merge-base $b
origin/$DEFBR) $b` for each **bounded** branch above, and `git status
--porcelain`. Always include the current branch + working tree even if
outside the 60-day cut. State the bound in the brief ("scanned your 7
branches active in 60d, not all N").
- **Direct** = exact path in both → name the incoming commit/PR/ticket
AND which of your PRs/branches owns it. Promote into Top priorities.
- **Adjacent** = shared top-level module/dir → "may affect your work".
- `DEPTH=deep`: read the incoming diff for each direct file and write
one *semantic* line (schema/signature/behavior change vs your work).
- Empty scope (no open PRs, on default branch, clean tree) → say so
in one line, skip the block. Never fabricate risk.
## Brief format (Context Brief Framework, catch-up scoped)
Write to `OUT_PATH`. Two screens max. Sections, in order:
- `# Catch-up Brief — {date} ({SINCE_LABEL})`
- **Intent** — "you've been off N; do these first" + 3 ranked actions
- **Top priorities** — table: # | What | Why it needs you | Link
- **Impact on your work** — direct/adjacent overlaps (above)
- **What moved (Scope In)** — GitHub / Git / Linear / Calendar, each a
few one-line excerpts; use `LINEAR_DATA`/`CALENDAR_DATA` verbatim if
provided, else proxy/skip with a note. If `SCOPE=all` and cross-repo
hits exist, add an **Other repos** subsection here — never fold
another repo's items into this repo's GitHub list.
- **Risks & assumptions** — conflict risks + EVERY absent/failed source
as one honest line + the scope line: `SCOPE=repo` →
*"Repo-scoped to {REPO}; cross-repo pings/reviews not shown (run
with --scope all)."*; `SCOPE=all` → *"Cross-repo: {n} items from
other repos listed separately."*
- **Timeline** — `Anchored {SINCE_LABEL} → now (= {SINCE_ISO}; all
sources compared on this instant)`. Today in `LOCAL_TZ`.
- Footer: `_Generated by catchup-runner (sonnet). Excerpt-only._`
Ranking: direct-overlap conflict > red CI on your PR > review
requested of you > assigned ticket moved > FYI. Cross-link PR↔ticket
on shared `XXX-####`.
## Tool economy (you have a turn budget)
A busy repo can blow a naive turn budget — the first real run hit the
cap mid-assembly. Stay economical:
- **Batch shell.** One `bash` call can chain the gh identity probes,
the git fetch, and the commit/risk/MOVED pipelines (`&&`, write to
`/tmp` vars). Don't spend one turn per trivial command.
- **Don't over-loop.** The per-commit `diff-tree` loop is fine (few
commits); never add a per-file or per-branch shell round-trip you
can fold into one awk/`comm`.
- **Write the brief as soon as you have enough** (after impact), then
the summary return is your final, cheap step — so even a tight
budget still yields the file.
## Output
1. Write the full brief to `OUT_PATH` (create parent dir) — do this
**before** you risk running low, not as the very last thing.
2. Return ONLY the inline summary (≤25 lines): Intent line, numbered
top priorities with links, per-source counts, impact one-liner,
any skipped-source notes, and the brief path. Nothing else — the
caller prints your return verbatim.
MIT license, from oliver-kriska/claude-elixir-phoenix at commit 268ac494838f.
Security
Scanned Oct 21, 2026 at commit 268ac494838f, with Cisco's open-source skill-scanner.
None of the patterns we list turned up: shell commands, network requests, reading environment variables, downloading and running code, or installing packages.
A scan lists patterns in the code. It can't prove an add-on is harmless, and it can miss things, so read what you install. How we scan
Alternatives
Other makers' add-ons that do the same job, next to this one.
| Name | Score | Stars | Context | Updated |
|---|---|---|---|---|
| This onecatchup-runner oliver-kriska/claude-elixir-phoenix · Subagent | 60 | ★ 567 | ≈62 in chat | 19d ago |
| catchup Avijit07x/claude-db · Skill Both about catchup | 67 | ★ 234 | ≈72 always | 25d ago |
| runner alexei-led/cc-thingz · Subagent Both about runner and cheaper | 56 | ★ 38 | ≈80 in chat | 25d ago |
| pipeline-runner atelier-fashion/adlc-toolkit · Subagent Both about runner | 50 | ★ 173 | ≈48 in chat | 1mo ago |
| evolve-runner gtapps/hermitd · Subagent Both about runner | 58 | ★ 82 | ≈75 in chat | 16d ago |
| e2e-runner xu-xiang/everything-claude-code-zh · Subagent Both about runner | 55 | ★ 2.0k | ≈36 in chat | 8mo ago |