HookStack
Back to catalogue
ContextSessionStart

GitHub context loader

Open PRs and CI status loaded before you ask

At session start, injects the repository GitHub state via the gh CLI: open pull requests (up to 5) and check status for the current branch PR. Local git context tells the agent where the working tree is; this hook covers the blind spot of what is happening on GitHub. Silent no-op when gh is missing, unauthenticated, or the repo has no GitHub remote.

What does the GitHub context loader hook do?

GitHub context loader is a Claude Code SessionStart hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Open PRs and CI status loaded before you ask.

As a SessionStart hook it runs after the action, reacting to what just happened rather than blocking it. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Resuming work with awareness of PRs awaiting review
  • Reacting to failing CI checks without being asked to look
  • Avoiding duplicate work on changes already in an open PR

Tags

#context#github#ci#productivity

settings.json fragment

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/session-start-github-context.mjs",
            "type": "command"
          }
        ]
      }
    ]
  }
}

Script · .claude/hooks/session-start-github-context.mjs

#!/usr/bin/env node
// @hookstack session-start-github-context
import { execSync } from "node:child_process";
// @hookstack session-start-github-context
// Injecte l'état GitHub (PRs ouvertes, checks de la branche) au démarrage de session (SessionStart)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";

function defaultExec(cmd) {
	try {
		return execSync(cmd, {
			encoding: "utf8",
			timeout: 10_000,
			stdio: "pipe",
		}).trim();
	} catch {
		return "";
	}
}

export function run(_input, { exec = defaultExec } = {}) {
	// Silencieux si gh absent, non authentifié ou dépôt sans remote GitHub
	const prs = exec("gh pr list --state open --limit 5");
	const checks = exec("gh pr checks 2>/dev/null");

	if (!prs && !checks) return null;

	const lines = ["## GitHub Context"];
	if (prs) lines.push("### Open PRs", "```", prs, "```");
	if (checks)
		lines.push("### Checks on current branch PR", "```", checks, "```");

	return {
		hookSpecificOutput: {
			hookEventName: "SessionStart",
			additionalContext: lines.join("\n"),
		},
	};
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
	const input = JSON.parse(readFileSync(0, "utf8"));
	const result = run(input);
	if (result) process.stdout.write(JSON.stringify(result));
}

Learn more

Related hooks