GitHub Copilot CLI
Copilot CLI in your repo - agent activity on the lock screen.

GitHub Copilot CLI (`gh copilot`) supports a repo-scoped hooks system at `.github/hooks/hooks.json`. The hooks fire on session lifecycle events: `sessionStart`, `preToolUse`, `postToolUse`, `errorOccurred`, `sessionEnd`. Chirp ships a single Python script that handles all five via argv dispatch - same shape as the Claude Code and Kiro hooks.
Unlike Claude Code (where hooks live in `~/.claude/settings.json`, global to the user), Copilot's hooks live in the repo. This is intentional - Copilot CLI is GitHub's pitch as the "agent that lives in your repo," so the hooks are repo-scoped too. Multiple developers on the same repo all get Chirp activities without per-laptop setup, as long as each ran `chirp login`.
Prerequisites
- GitHub Copilot CLI installed (`gh extension install github/gh-copilot`).
- Python 3 on PATH.
- `chirp login` run once per developer machine.
Setup
- 1
Drop the hook script into .github/hooks/
Single Python file. argv[1] is the event name (Copilot CLI passes it on the command line as configured in hooks.json). The script reads the event JSON from stdin and posts to Chirp's normalizer for the @copilot-cli activity.
.github/hooks/chirp-hook.py#!/usr/bin/env python3 """GitHub Copilot CLI → Chirp.""" import sys, os, urllib.request def main(): event = sys.argv[1] if len(sys.argv) > 1 else "unknown" payload = sys.stdin.read() creds_path = os.path.expanduser("~/.chirp/credentials") if not os.path.exists(creds_path): return # not paired; silently skip token = open(creds_path).read().strip() req = urllib.request.Request( f"https://api.chirpapp.dev/v1/webhooks/copilot-cli?event={event}", data=payload.encode(), headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, ) try: urllib.request.urlopen(req, timeout=2) except Exception: pass if __name__ == "__main__": main() - 2
Commit the hooks.json manifest
Maps each Copilot CLI lifecycle event to the script + the event name.
version: 1is required (Copilot rejects unversioned hook configs)..github/hooks/hooks.json{ "version": 1, "hooks": { "sessionStart": [{ "command": "python3 .github/hooks/chirp-hook.py sessionStart" }], "preToolUse": [{ "command": "python3 .github/hooks/chirp-hook.py preToolUse" }], "postToolUse": [{ "command": "python3 .github/hooks/chirp-hook.py postToolUse" }], "errorOccurred": [{ "command": "python3 .github/hooks/chirp-hook.py errorOccurred" }], "sessionEnd": [{ "command": "python3 .github/hooks/chirp-hook.py sessionEnd" }] } } - 3
Pair each developer's CLI
Every dev on the repo runs
chirp loginonce on their laptop. The hooks read~/.chirp/credentials; without it, the script silently no-ops (so a teammate without Chirp doesn't get errors).shellchirp login - 4
Run gh copilot in the repo
From the repo root:
gh copilot suggestorgh copilot explain. Copilot auto-discovers.github/hooks/hooks.jsonand fires the configured hooks on each lifecycle event.shellgh copilot suggest "how do I revert the last commit"
What you’ll see
Card header: GitHub Copilot mark + "Copilot CLI · WORKING" + repo path. Action line shows the current tool call (`bash · git log -5`, `read · src/auth.ts`). errorOccurred surfaces as a red badge on the timeline (with the error message) without ending the card; sessionEnd closes it green or red based on the final state. Multiple parallel `gh copilot` invocations in the same repo coalesce on session ID.
Troubleshooting
- Copilot CLI doesn't fire hooks at all.
gh copilot --version- hooks were added in v0.5.4. Older versions silently ignore hooks.json.gh extension upgrade gh-copilotfixes it.- Hook fires but card never appears.
- The script silently skips when ~/.chirp/credentials is missing. Run
chirp loginon the laptop where Copilot is running. To debug, change the script'spasstoprint('chirp:', e, file=sys.stderr)and re-run. - I want this for one repo only, not all repos.
- It already is - Copilot CLI's hooks are per-repo by design. The file lives in
.github/hooks/, so only repos you've added it to wire up Chirp.