@hank-warren/pi-github-actions-watch
v0.3.1
Published
Watch GitHub Actions runs live from Pi: framed job transcripts, failed-log tails, cancellation, and background watches. Requires an authenticated gh CLI.
Maintainers
Readme
GitHub Actions Watch
A package-native Pi extension that exposes github_actions_watch and watches GitHub Actions runs and jobs in the background by default: the tool returns a watch ID immediately, streams progress to a bounded widget, and reports the final status back to the conversation when runs settle. A blocking mode renders one live-updating, framed transcript card instead.
Prerequisites
ghis installed.gh auth status --hostname github.comsucceeds.- The current checkout has a GitHub remote when
repo,branch, andrunare omitted.
The extension invokes gh and git directly with fixed argument arrays. It does not accept or request a pasted token.
Tool
github_actions_watch({
repo?: "owner/repo",
branch?: "branch-name",
run?: "123456789" | "https://github.com/owner/repo/actions/runs/123456789",
tail?: 15,
background?: true
})- With
run, it watches exactly that run. A run URL supplies its repository; a conflictingrepois rejected. - Without
run, it resolvesbranchremotely or uses the current checkout's branch and HEAD, then watches every run whosehead_shamatches that commit. - An explicit repository that differs from the current checkout requires
branchorrun. taildefaults to 15 and is capped at 200.backgrounddefaults totrue: the tool validates and resolves the target, starts session-owned polling, and returns a stable watch ID immediately so the conversation can continue. Withfalse, the tool blocks the turn and renders the live transcript card until the watch settles.
Background watches can be inspected or cancelled with:
github_actions_watch_status({ watch_id?: "actions-..." })
github_actions_watch_stop({ watch_id?: "actions-...", all?: false })At most three background watches may be active. Starting an identical resolved run or commit target returns the existing ID instead of polling twice. all: true must be explicit when stopping every watch.
Behavior
The first minute polls every three seconds; later polls use 15 seconds. Transient and rate-limit failures back off to the slow interval and fail after five consecutive errors. After the first empty commit poll, the watcher checks the repository's Actions workflow inventory and exits immediately when none are configured. If workflows exist—or GitHub does not expose the inventory—commit mode retains the 90-second bound for a run to appear. Successful commit watches poll once more with the same successful run-ID set so late workflows are not missed.
A watch settles only after every run it is watching reaches a terminal state. A job that fails while sibling jobs are still running is shown as failed in the live card but does not end the watch, so long-running build and publish jobs still finish and land in one final report instead of an early error. Once everything is terminal, a failure waits five seconds and refetches once before collecting logs. Originally detected failures survive an auto-retry reset during that grace window. Failed-log tails stay bounded in the card; complete logs are written with mode 0600 under a newly created /tmp/pi-github-actions-watch-* directory, and the path is returned in the final tool result.
Escape cancellation propagates through the parent tool's AbortSignal to subprocesses and timers in blocking mode. Background mode uses a dedicated AbortController per watch, so the initiating tool call can settle without cancelling polling. Stop requests and session shutdown abort both sleeps and gh subprocesses.
Background lifecycle and UI
In the TUI, active watches appear in a bounded widget above the editor and in a concise footer status. The widget is independent of normal tool-row expansion. RPC mode receives plain widget/status updates; JSON and print modes keep model-facing status and stop results without assuming terminal UI APIs exist.
The widget prioritizes what is still moving. Within a run, two or more passed jobs collapse into a single ✔ N passed (first, second, +K) line, while every in-progress and failed job is listed individually, so the job you are waiting on is never the one dropped. At most three runs are listed per watch, and job lines share a 12-line budget across active watches (a floor of three lines each), with overflow reported as separate workflow and job counts.
In-progress jobs and runs animate a braille spinner in both unicode and nerd mode, and rotating ASCII in ascii mode. Nerd mode deliberately reuses braille instead of a nerd-native sequence such as nf-md-circle_slice_1..8, which fills a pie rather than rotating and reads as a static circle at terminal font sizes; Nerd Fonts inherit braille from the patched base font. Frames are a pure function of wall-clock time, so every card and widget stays in phase. The 125 ms repaint timer exists only in the TUI, only while something is actually in progress, and is unref'd; it repaints the widget and never issues additional GitHub API calls, so animation is fully decoupled from the 3 s/15 s poll cadence.
When a watch settles, Pi removes it from the live widget and appends one custom session entry rendered as a final success/failure card. The entry is TUI-visible but excluded from LLM context. For completed and errored watches, the manager additionally sends one hidden custom message (github-actions-watch-settled) into LLM context with deliverAs: "followUp" and triggerTurn: true, so the agent receives the final workflow status—after its current work finishes, or immediately when idle—and can report or react to it. Explicit cancellations do not message the conversation. A non-modal notification reports completion. Full failed logs remain only in the private temporary artifact referenced by the final entry and status data.
Background watches are process- and session-local. /reload, /new, /resume, /fork, and process shutdown cancel them; they are not restored after reload or Pi exit. Lifecycle-triggered cancellation is silent and does not append a misleading completion entry to the session being closed.
Settled event
Every normal completion, explicit cancellation, or watcher error emits exactly one process-local extension event after the final entry is persisted and the live UI is cleaned up:
pi.events.on("github-actions-watch:settled", (data) => {
const event = data as {
eventVersion: 1;
watchId: string;
targetKey: string;
terminalState: "completed" | "cancelled" | "error";
summary: string;
details?: RunWatchDetails;
artifactPath?: string;
error?: string;
};
// Opt into a notification, deployment, or follow-up integration here.
});The payload also includes the original input plus startedAt and settledAt timestamps. Workflow failures are normal watcher completions with details.outcome === "failure"; terminalState: "error" means the watcher itself failed. Complete logs are not embedded—artifactPath points to the private artifact when one exists.
The event itself does not send a message or trigger an LLM turn; the separate settled conversation message described above handles agent delivery for completions and errors. Session-lifecycle cancellation deliberately emits no event, preventing hooks from running during teardown. Event delivery is process-local and is not replayed after reload.
Symbols
The default is Unicode. Set PI_GITHUB_ACTIONS_WATCH_SYMBOLS to one of:
| Mode | GitHub glyph | In-progress spinner |
|---|---|---|
| unicode | ⎇ | braille ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ |
| nerd | (U+EA84) | braille ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ |
| ascii | gh | [\|] [/] [-] [\] |
PI_SYMBOL_PRESET is accepted as a secondary setting. TERM=dumb selects ASCII when neither variable is set.
Rendering
The custom tool uses Pi's public self-shell renderer API. renderCall owns a mutable card component; partial and final renderResult updates mutate that same component and return an empty result slot. This avoids transcript-row spam without modifying Pi core. Collapsed mode shows five failed-log lines and Pi's configured tool-expansion key hint; expanded mode shows the entire configured tail.
See NOTICE.md for the pinned oh-my-pi reference and MIT attribution.
