ag2space-voice
s.space voice surface — binds the room the s.space client announces to the voice session's origin, so work delegated by voice in…
Event-driven GitHub PR watching. Use when the owner wants to be kept on top of pull requests: re-review a person's PRs whenever they change, follow up on any PR the owner has already reviewed, react when a review is requested from the owner, or note merges, closes and submitted
Claude Code
$ npx -y skills add sonichi/sutando --skill pr-watch --agent claude-code
Codex
$ npx skills add sonichi/sutando --skill pr-watch -a codex -y
Cursor
$ npx skills add sonichi/sutando --skill pr-watch -a cursor -y
Gemini CLI
$ npx skills add sonichi/sutando --skill pr-watch -a gemini-cli -y
GitHub Copilot
$ npx skills add sonichi/sutando --skill pr-watch -a github-copilot -y
OpenCode
$ npx skills add sonichi/sutando --skill pr-watch -a opencode -y
Windsurf
$ npx skills add sonichi/sutando --skill pr-watch -a windsurf -y
name: pr-watch description: "Event-driven GitHub PR watching. Use when the owner wants to be kept on top of pull requests: re-review a person's PRs whenever they change, follow up on any PR the owner has already reviewed, react when a review is requested from the owner, or note merges, closes and submitted or dismissed reviews, without polling and without a Monitor that expires after 30 minutes. A Claude Code mod runs `gh webhook forward` for each watched repo and turns each relevant event into a new turn of this session. Optionally keeps an HTML tracker page of the watched PRs in a configured Room Commons room." user-invocable: true
Watches GitHub pull requests by event, not by polling. A Claude Code mod (a plugin of function hooks) starts `pr_watch.py run` when the session starts; the runner keeps one `gh webhook forward` per watched repo and prints one line per relevant event. Nothing polls GitHub, and nothing expires the way a `Monitor` does at 30 minutes.
How an event reaches the session depends on whether a turn is running:
event and attaches it to the next tool result of the main loop (the tool call's `context`, which the model reads right after that tool's output). Events still held when the turn ends are submitted together as one prompt. Subagents' tool calls never carry them.
Not for one-off checks of a single PR: read it with `gh pr view` instead.
Per watched repo (`config.json`), filtered by `pr_watch.py`:
| Event | Forwarded when | Line | |---|---|---| | `pull_request` `opened`, `synchronize`, `reopened`, `ready_for_review`, `closed` | the PR author is a watched `--author` (`*` = anyone) | `PR_EVENT o/r#N <action> author=… head=<sha8> merged=true\|false reason=author` | | `pull_request` `synchronize`, `reopened`, `ready_for_review`, `closed` | the watch has `--reviewed` and one of the `--me` logins has submitted a review on the PR, whoever wrote it | `PR_EVENT o/r#N <action> author=… head=<sha8> merged=… reason=reviewed` | | `pull_request` `review_requested` | the requested reviewer is one of the `--me` logins, on any PR in the repo | `PR_EVENT o/r#N review_requested reviewer=… author=… head=…` | | `pull_request_review` `submitted` | on a watched author's PR, by someone other than the `--me` logins (the owner's own review echoing back is dropped, though it still marks the PR reviewed) | `PR_EVENT o/r#N review_submitted by=… state=approved\|changes_requested\|commented author=… head=…` | | `pull_request_review` `dismissed` | the dismissed review is by one of the `--me` logins, on any PR in the repo | `PR_EVENT o/r#N review_dismissed reviewer=… author=… head=…` |
An event matching both the author rule and the reviewed rule yields one line, `reason=author`. The webhook payload does not list a PR's reviewers, so the reviewed rule asks `GET repos/{repo}/pulls/{n}/reviews` (see Costs). When that call fails or times out (10 s), the event is forwarded anyway with `reason=reviewed reviewed=unknown`, never dropped.
Only logins, numbers, states and short shas reach the prompt; titles and bodies never do, so a PR's text cannot steer the session. The agent reads the PR itself when it acts. (The runner does store each PR's title in `tracker.json` for the tracker page's About column; see Tracker.)
`gh extension install cli/gh-webhook`. Without it the runner prints a `PR_WATCH_NOTE` and exits.
S=skills/pr-watch/scripts/pr_watch.py python3 $S add --repo acme/widgets --author alice --author bob --me owner-login python3 $S add --repo acme/gadgets --me owner-login --reviewed # every PR owner-login reviewed python3 $S install-mod --mods-dir "<the session's mods folder>"
`install-mod` writes the mod into `<mods-dir>/pr-watch/` and copies the runner (and `manifest.json`, for its settings) to `<workspace>/state/pr-watch/`, so the forwarder keeps working when the engine checkout changes branch or updates. Re-run it after updating this skill.
Loading the mod is the owner's one step: **enable hot reload of the mods folder in the session's terminal** (one click), or start the session with `claude --plugin-dir <mods-dir>/pr-watch`. Never enable it for them.
Other commands:
python3 $S list # the watch config python3 $S remove --repo acme/widgets python3 $S status # runner, forwarders, recorded hook ids python3 $S cleanup # delete the hook ids this skill recorded (refused while a runner is active; --force) python3 $S tracker-kind --kind html-<id> --room '!room:server' # the page a session publish wrote to
`add` and `remove` signal a running runner (SIGHUP), which starts or stops that repo's forwarder without a reload.
The watches live in `<workspace>/state/pr-watch/config.json`, written by `add` and `remove`:
{
"watches": [{"repo": "acme/widgets", "authors": ["alice"], "me": ["owner-login"], "reviewed": true}],
"settings": {"PR_WATCH_TRACKER_ROOM": "!room:server"}
}`reviewed` (set by `add --reviewed`) needs at least one `me` login other than `*`.
The skill's settings are declared, with their defaults, in the `config` block of `manifest.json` (see `skills/MANIFEST.md`, "Config-only manifests"):
| Key | D
My AI Stand — Realtime by Day, Rewriting Itself by Night. Summon my AI superpower. Voice, vision, screen, meetings, calls when I'm engaged. Learns my patterns, ships its own code when I'm not. Runs across my Macs, interacts with people & their Stands.
s.space voice surface — binds the room the s.space client announces to the voice session's origin, so work delegated by voice in…
Streams what the agent is doing into the room, as rows the desktop client renders in an **events drawer** above the composer…
Local Agent Registry — a standalone, dependency-free service that tracks running Claude Code (and other) agent instances. Agents…
For an all-room analysis sweep, use the tested window collector and coverage contract in…
Deterministic final-answer normalizer — a last-step pass for any task that ends in a *precise* answer (a number, a short string,…
Transcribes audio files and voice notes to text via Gemini 2.5-flash. Integrates with Slack, Discord, and Telegram bridges so…
Load immediately after an Agent intent, before the first agent_builder_* call. Governs how you create, configure, test, and…
Direct browser control via CDP for web interaction: automation, scraping, testing, screenshots, and site/app work. Also covers…
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling…
Add local office-document-to-Markdown conversion to NanoClaw agent containers with the pinned Firecrawl AnyDoc CLI. Use when…