Hooks
1. Overview
GitHub Copilot Hooks are a powerful feature that allows you to execute custom shell commands at key lifecycle points during an agent's execution. They enable you to automate workflows, enforce security policies, and integrate Copilot more deeply with your project’s environment.
Key Benefits:
Security: Block dangerous commands (e.g.,
rm -rf) before they run.Quality Control: Automatically run linters or formatters after Copilot edits code.
Custom Context: Inject project-specific data (API keys, versions, environment info) into the agent's conversation.
Audit Logging: Keep a record of every prompt and tool invocation for compliance.
2. Hook Lifecycle Events
Hooks are triggered at specific "strategic points." Understanding these events is critical for choosing where to place your automation.
Hook Event | When It Fires | Example Use Case |
| When a new agent session begins. | Initialize environment, log user entry. |
| After the user sends a message. | Audit requests or inject system-level instructions. |
| Before the agent runs a tool (terminal, edit, etc.). | Critical for Security: Deny or approve commands. |
| After a tool execution finishes. | Run |
| When the agent finishes its response. | Cleanup or status reporting. |
| When something goes wrong. | Log errors to an external monitoring service. |
3. Creating and Configuring Hooks
Step 1: File Location
Hooks are defined in JSON files. For repository-wide settings that work for the whole team, create them in:
{your-repo}/.github/hooks/my-hooks.json
Step 2: Configuration Format
Your JSON file must follow this structure:
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "./scripts/security-check.sh",
"powershell": "./scripts/security-check.ps1",
"cwd": "scripts",
"timeoutSec": 15
}
]
}
}Key Properties:
bash/powershell: Paths to your scripts. Copilot will use the appropriate one based on the OS.cwd: The directory the script runs in (relative to repo root).timeoutSec: Defaults to 30s. Keep hooks fast to avoid lagging the UI.
4. How Hooks Interact with Data
Hooks are not just "fire and forget" scripts; they receive and return JSON.
Input (Standard In)
Every hook receives a JSON object via stdin. For example, a preToolUse hook receives:
{
"tool_name": "runTerminalCommand",
"tool_input": { "command": "npm install" },
"cwd": "/path/to/project"
}Output (Standard Out)
Your script should output JSON to tell Copilot what to do next.
Example: Blocking a Command
If your script detects a dangerous command, output this:
{
"hookSpecificOutput": {
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive commands are not allowed in this repo."
}
}Example: Adding Context
In a sessionStart hook, you can inject info:
{
"hookSpecificOutput": {
"additionalContext": "Important: This project uses Python 3.11 and requires strict type hinting."
}
}5. Best Practices & Security
Fail Safely: If your script crashes, Copilot might block execution. Ensure your scripts handle their own errors.
Performance: Keep hooks under 2–5 seconds. If a hook takes too long, users will feel the latency in their chat experience.
No Secrets: Never log environment variables or tokens to standard output/logs that Copilot might see.
6. Troubleshooting
Executable Permissions: Ensure your scripts have execution rights (
chmod +x script.sh).Verbose Logging: In your script, you can pipe debug info to
stderr(e.g.,echo "Debug info" >&2) to see it without interfering with the JSON output onstdout.Local Testing: You can test your scripts manually by piping a mock JSON object into them:
echo '{"tool_name":"bash"}' | ./my-hook.sh