Documentation Architecture Hook System

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:

  1. Reads the transcript — The session file containing every message, tool call, and metadata block from the session.
  2. Selects the right extractor — Based on the --agent flag, dispatches to the appropriate parser (Claude Code JSONL, Gemini CLI JSON, etc.).
  3. Parses structured data — Extracts token counts, tool usage, model names, timing, git context, and session metadata from the raw transcript.
  4. Loads facets — For Claude Code sessions, checks ~/.claude/usage-data/facets/ for qualitative analysis data (goal, outcome, friction) if available.
  5. Uploads to the server — POSTs the extracted SessionMetadata payload to /api/v1/ingest/session.
  6. Fails silently — If the server is unreachable, the hook exits without error. Use primer sync to 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.

AgentHook SupportCollection MethodConfig File
Claude CodeSessionEnd hookReal-time~/.claude/settings.json
Gemini CLISessionEnd hookReal-time~/.gemini/settings.json
Codex CLINo SessionEnd hookprimer 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

FieldSourceDescription
session_idTranscript metadataUnique identifier for the session
project_pathSession environmentAbsolute path to the project directory
project_nameDerived from pathLast component of the project path
git_branchGit environmentCurrent branch at session start
git_remote_urlGit configRemote origin URL for repository matching

Agent

FieldSourceDescription
agent_typeSession metadataclaude_code, codex_cli, or gemini_cli
agent_versionSession metadataVersion string of the agent
permission_modeSession metadataPermission level the agent was running under
end_reasonSession metadataWhy the session ended (user exit, error, timeout)

Timing

FieldSourceDescription
started_atTranscript timestampWhen the session began
ended_atTranscript timestampWhen the session ended
duration_secondsCalculatedWall-clock duration of the session

Volume

FieldSourceDescription
user_message_countTranscript messagesNumber of user prompts
assistant_message_countTranscript messagesNumber of assistant responses
total_message_countTranscript messagesTotal messages in the conversation
tool_call_countTool use blocksTotal tool invocations across all tools

Tokens

Token counts are captured at both the session level (aggregated) and per model.

FieldSourceDescription
input_tokensUsage fields in messagesTotal tokens sent to the model
output_tokensUsage fields in messagesTotal tokens generated by the model
cache_read_tokensCache usage fieldsTokens served from prompt cache
cache_creation_tokensCache usage fieldsTokens 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

FieldSourceDescription
first_promptFirst user messageFirst 500 characters of the opening prompt
summarySession summarySummary generated by the agent, if available

Git Commits

For each commit made during the session, the extractor captures:

FieldSourceDescription
shaGit logFull commit SHA
messageGit logCommit message
authorGit logCommit author name and email
files_changedGit diff statsNumber of files modified
lines_addedGit diff statsLines added across all files
lines_deletedGit diff statsLines 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:

  1. Check the server is running: curl http://localhost:8000/health
  2. Verify your API key is valid: the key in ~/.primer/config.toml must match an active engineer account.
  3. 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.