Hook System
Overview
The SessionEnd hook is the data collection layer of Primer. It runs automatically at the end of each AI coding session, extracts metadata from the transcript, and uploads it to your Primer server. Zero configuration after installation — it captures every session silently in the background.
How It Works
When a session ends, the hook:
- Reads the transcript — The session file containing every message, tool call, and metadata block from the session.
- Selects the right extractor — Based on the
--agentflag, dispatches to the appropriate parser (Claude Code JSONL, Gemini CLI JSON, etc.). - Parses structured data — Extracts token counts, tool usage, model names, timing, git context, and session metadata from the raw transcript.
- Loads facets — For Claude Code sessions, checks
~/.claude/usage-data/facets/for qualitative analysis data (goal, outcome, friction) if available. - Uploads to the server — POSTs the extracted
SessionMetadatapayload to/api/v1/ingest/session. - Fails silently — If the server is unreachable, the hook exits without error. Use
primer syncto recover missed sessions later.
The entire process completes in under a second and never blocks your terminal.
Supported Agents
Primer supports multiple AI coding tools through agent-specific extraction logic and hook installation.
| Agent | Hook Support | Collection Method | Config File |
|---|---|---|---|
| Claude Code | SessionEnd hook | Real-time | ~/.claude/settings.json |
| Gemini CLI | SessionEnd hook | Real-time | ~/.gemini/settings.json |
| Codex CLI | No SessionEnd hook | primer sync (manual/watch) | N/A |
All agents upload to the same /api/v1/ingest/session endpoint. The agent_type field on each session record identifies the source, allowing you to filter and compare across tools in the dashboard.
Multi-agent teams
Teams using multiple AI coding tools get a unified view in the dashboard. Session analytics, cost tracking, and friction reports work across all agent types.
What Data Is Extracted
The extractor produces a SessionMetadata object covering identity, timing, volume, tokens, content, and git activity.
Identity
| Field | Source | Description |
|---|---|---|
session_id | Transcript metadata | Unique identifier for the session |
project_path | Session environment | Absolute path to the project directory |
project_name | Derived from path | Last component of the project path |
git_branch | Git environment | Current branch at session start |
git_remote_url | Git config | Remote origin URL for repository matching |
Agent
| Field | Source | Description |
|---|---|---|
agent_type | Session metadata | claude_code, codex_cli, or gemini_cli |
agent_version | Session metadata | Version string of the agent |
permission_mode | Session metadata | Permission level the agent was running under |
end_reason | Session metadata | Why the session ended (user exit, error, timeout) |
Timing
| Field | Source | Description |
|---|---|---|
started_at | Transcript timestamp | When the session began |
ended_at | Transcript timestamp | When the session ended |
duration_seconds | Calculated | Wall-clock duration of the session |
Volume
| Field | Source | Description |
|---|---|---|
user_message_count | Transcript messages | Number of user prompts |
assistant_message_count | Transcript messages | Number of assistant responses |
total_message_count | Transcript messages | Total messages in the conversation |
tool_call_count | Tool use blocks | Total tool invocations across all tools |
Tokens
Token counts are captured at both the session level (aggregated) and per model.
| Field | Source | Description |
|---|---|---|
input_tokens | Usage fields in messages | Total tokens sent to the model |
output_tokens | Usage fields in messages | Total tokens generated by the model |
cache_read_tokens | Cache usage fields | Tokens served from prompt cache |
cache_creation_tokens | Cache usage fields | Tokens written to prompt cache |
Per-model breakdowns are stored separately in the model_usages table, allowing cost calculation at the model level using Primer’s pricing config.
Content
| Field | Source | Description |
|---|---|---|
first_prompt | First user message | First 500 characters of the opening prompt |
summary | Session summary | Summary generated by the agent, if available |
Git Commits
For each commit made during the session, the extractor captures:
| Field | Source | Description |
|---|---|---|
sha | Git log | Full commit SHA |
message | Git log | Commit message |
author | Git log | Commit author name and email |
files_changed | Git diff stats | Number of files modified |
lines_added | Git diff stats | Lines added across all files |
lines_deleted | Git diff stats | Lines deleted across all files |
Git commit data powers the GitHub Integration features, linking sessions to pull requests for AI contribution tracking.
Installation
Claude Code
The default agent. Install with the CLI:
primer hook install
This adds a SessionEnd hook entry to ~/.claude/settings.json.
What Gets Written (Claude Code)
{
"hooks": {
"SessionEnd": [
{
"command": "python -m primer.hook.session_end",
"timeout": 10000
}
]
}
}
Gemini CLI
Install the hook for Gemini CLI:
primer hook install --agent gemini
This adds an exit hook entry to ~/.gemini/settings.json.
What Gets Written (Gemini CLI)
{
"hooks": [
{
"matcher": "exit",
"hooks": [
{
"name": "primer-hook",
"type": "command",
"command": "python -m primer.hook.session_end --agent gemini"
}
]
}
]
}
Codex CLI
Codex CLI does not have a SessionEnd hook system. Use primer sync to upload sessions manually, or run it in watch mode for continuous collection:
# One-time sync
primer sync
# Continuous watch mode (syncs every 60 seconds)
primer sync --watch
# Custom interval
primer sync --watch --interval 30
Press Ctrl+C to stop watch mode.
Existing hooks preserved
The installer appends to your existing hooks configuration. Any other hooks you have configured are not affected.
Manual Installation
If you prefer manual setup, set your environment variables and run the install script directly:
export PRIMER_SERVER_URL=http://your-primer-server:8000
export PRIMER_API_KEY=primer_...
# Claude Code (default)
python scripts/install_hook.py
# Gemini CLI
python scripts/install_hook.py --agent gemini
Verification
Check hook status for a specific agent:
primer hook status # Claude (default)
primer hook status --agent gemini # Gemini
Check all agents at once:
primer hook list
This shows the installation status for every supported agent.
After completing a session, verify the data was uploaded:
curl http://localhost:8000/api/v1/sessions \
-H "x-admin-key: your-admin-key" | python -m json.tool
You should see your session with token counts, tool usage, and timing data populated.
Offline Resilience
If the Primer server is unreachable when a session ends, the hook exits silently without disrupting your workflow. No error messages are shown, and your agent behaves normally.
Timeout
The hook has a 10-second timeout. If your server is slow or unreachable, the hook gives up quickly so it never blocks your terminal.
Recovering Missed Sessions
Use primer sync to backfill any sessions that were missed due to server downtime or network issues:
primer sync
This scans your local transcript directories for all supported agents, identifies sessions not yet on the server, and uploads them in bulk.
For continuous recovery, use watch mode:
primer sync --watch --interval 60
You can also sync from within Claude Code using the MCP sidecar:
> Sync my recent sessions.
Claude calls the sync MCP tool, which performs the same backfill operation. See MCP Sidecar for setup.
Troubleshooting
Hook not firing
Verify the hook exists in your settings:
primer hook status # Claude
primer hook status --agent gemini # Gemini
If it reports “not installed”, re-run the install command for that agent. Also check that the session_end module runs without import errors:
python -m primer.hook.session_end --help
Sessions not appearing
If the hook fires but sessions don’t appear on the server:
- Check the server is running:
curl http://localhost:8000/health - Verify your API key is valid: the key in
~/.primer/config.tomlmust match an active engineer account. - Check ingest events for errors:
curl http://localhost:8000/api/v1/sessions -H "x-admin-key: ..."and look for recent entries.
Token counts are zero
Some older agent versions don’t include usage data in the transcript. Update to the latest version of your agent for full token tracking.
Uninstalling
primer hook uninstall # Claude (default)
primer hook uninstall --agent gemini # Gemini
This removes the hook entry from the agent’s settings file. Your existing session data on the Primer server is not affected — only future sessions stop being captured.
To re-enable later, run the install command again.