github
v0.4.1GitHub CLI and event-driven PR monitoring — typed queries and mutations, authenticated temporary webhooks, CI/comment/review notifications, and merge-triggered cleanup.
Install
iii trigger compose::add worker=github@0.4.1Run in your project with the iii engine running. Exact versions are immutable and digest-pinned.
skill doc
PR webhook monitoring (opt-in)
For durable PR monitoring, use github::pr::event, NOT github::called.
First arm the trigger with a caller-chosen watch_id, then call
github::pr::watch with that same ID, pr_url OR repo + number, optional
events: [ci,comments,reviews,pr], stop_on: merged|closed (default merged),
and mandatory future RFC3339 expires_at. Read github::pr::watch-status
once after watch to close the initial race. Never interval-poll PRs.
Inspect status and health.last_error: preparing is not active ingress;
cleanup_pending is not successful cleanup. unwatch and manual recover
are mutations requiring approval. Internal github::webhooks::* functions
must not be called by an agent. Event callbacks must dedupe event_id;
namespace and metadata are preserved, errors propagate to the durable queue.
A single passing check is NOT aggregate CI success. Closed without merge
remains watched until expiry when stop_on is merged.
Operator setup requires persistent SQLite, queue builtin file_based (current Redis adapter is NOT durable), HTTP dedicated public listener, Quick Tunnel, cron, and gh auth with Webhooks write plus repository reads. Default enabled is false. See the webhook guide. Quick Tunnels are not zero-loss; recovery lists failed deliveries and requests bounded redelivery, reconciling current state. Do not enable merely because a public repository can be read.
Guided webhook setup
quick-tunnel is NOT a dependency of this worker: it is only needed for PR
webhooks. Call github::setup::webhooks-status (read-only) for the checklist,
in order:
quick_tunnel— the quick-tunnel worker is installed (fix:install_worker,compose::add { worker: "quick-tunnel" }; ask the user first).cloudflared— quick-tunnel found the cloudflared binary (fix:open_url, Cloudflare's install page). Nothing downloads cloudflared; the user installs it.http_listener— the http worker's restricted webhook listener is on (fix:enable_http_listener,github::setup::enable-http-listener, default port 3112).
Each check is ok, missing, blocked (waits for an earlier one) or
unknown (older worker version or transient error; never blocking).
github::setup::enable-webhooks { enabled: true } is refused until every check
is ok and reports restart_required: webhook storage opens at startup, so
the github worker must restart. github::pr::watch refuses definite blockers
with the same fix list. The console's GitHub page → Webhooks runs these steps
with buttons; both enable functions keep needs_approval for agents.
github
The github worker wraps the GitHub CLI (gh). Thirty typed functions cover
the high-traffic surface (pr, issue, repo, run, workflow, release, search);
github::exec runs any other gh command verbatim, and github::api reaches
any GitHub REST endpoint. Auth comes from the worker's GH_TOKEN configuration
or the host's ambient gh auth login state. There is no local checkout:
every repo-scoped call takes an explicit repo: "owner/name".
When to Use
- Check a PR before landing:
github::pr::view,github::pr::checks(failing/pending checks are data, not errors),github::pr::diff. - Open or shepherd a PR:
github::pr::create/review/merge. - File and triage issues:
github::issue::create/list/comment/close. - Watch or poke CI:
github::run::list/view/rerun/cancel; dispatch withgithub::workflow::run. - Cut or inspect releases:
github::release::create/list/view. - Find things org-wide:
github::search::repos/issues/prs/code(qualifiers likerepo:o/r is:opengo in the query string). - Anything gh does that has no typed function →
github::exec { args: [...] }. - Any REST endpoint →
github::api { path: "repos/o/r/…", jq? }.
Boundaries
- Needs the gh CLI on the worker host; a missing binary errors at call time
with code
gh_not_found(functions still register without it). - Mutations (create/edit/merge/comment/review/close/rerun/cancel/workflow run/release create) and both escape hatches are approval-gated by default; the read-only surface is allowed (see iii-permissions.yaml).
- Curated functions error on a non-zero gh exit (the message carries gh's
stderr);
github::execreturns exit_code/stderr/timed_out as data instead. - Output is capped per stream (default 1 MiB) with
*_truncatedflags; per-calltimeout_msclamps tomax_timeout_ms(default 120 s; 30 s when omitted). github::apipaths take concrete owner/repo values —{owner}/{repo}placeholders need a checkout the worker does not have.
Functions
github::pr::*— list, view, create, edit, merge, comment, review, diff, checks.github::issue::*— list, view, create, edit, comment, close.github::repo::*— view, list.github::run::*— list, view, rerun, cancel (Actions runs).github::workflow::*— list, run (workflow_dispatch).github::release::*— list, view, create.github::search::*— repos, issues, prs, code.github::exec—{ args, stdin?, timeout_ms? }→ the full outcome as data (stdout,stderr,exit_code,timed_out, truncation flags).github::api—{ path, method?, fields?, body?, jq?, paginate?, timeout_ms? }→ parsed JSON.
Actionable PR notifications (agent_actionable)
The default webhooks.notifications.profile in config.yaml delivers only what
needs an agent action, as one kind: "digest" payload per watch:
ci.faileditems:failure,timed_out,startup_failure,action_requiredon the current head. A failed workflow is dropped when its failed job is already reported. Failures of a superseded head are dropped at delivery.- New comments and reviews (created/submitted, approved, changes requested,
unresolved threads), excluding edits, deletions, the agent's own login
(
ignore_self, discovered viaGET /user) and known bot noise. merged/ closed lifecycle, delivered immediately with anything pending.
Never delivered: CI success or progress, cancelled/skipped/neutral runs, pushes,
labels, assignments. The agent checks current CI with github::pr::checks when it
acts. Every item carries event_id; fetch the full event with
github::pr::event-detail.
Timing: a digest is sent after quiet_ms (15s) without new items, and never
later than max_wait_ms (2min) after its first item. max_items (30) bounds the
payload; extra items are counted in omitted_items.
Watch lifetime follows its listeners
A watch is kept alive by the github::pr::event bindings that listen to it
(same watch_id, or a repo/number filter that matches it).
- When the last listening binding goes away (its chat was deleted, or it
unregistered), the watch is marked orphaned. If no binding listens again
within
webhooks.orphan_grace_minutes(default 60), maintenance stops it, releases its quick-tunnel lease and deletes the repository hook once no live watch remains there; the tunnel stops with its last lease. One-shot wakes are unregistered right after they fire, so re-arm before the grace period ends. - Bindings removed while the worker was offline are reconciled against the engine at most every five minutes.
- Calling
github::pr::watchagain with the samewatch_idand spec resumes a stopped watch; completed (merged/closed) and expired watches stay terminal. expires_atmust be withinwebhooks.max_watch_days(1..=30, default 30): a watch holds a quick-tunnel lease, and leases last at most 30 days.