How the Codex-CC Plugin Intercepts Claude Code Sessions Using SessionStart and SessionEnd Hooks

The Codex-CC plugin intercepts Claude Code sessions via two lifecycle hooks declared in hooks.json and implemented in session-lifecycle-hook.mjs, which capture session metadata at startup and clean up resources at termination.

The openai/codex-plugin-cc repository provides a plugin architecture that extends Claude Code with external tooling capabilities. A core mechanism enabling this integration is the session lifecycle interception system, which allows the plugin to monitor and respond to Claude Code session boundaries through declarative hooks.

Hook Registration in hooks.json

The plugin's entry point for session interception is plugins/codex/hooks/hooks.json. This JSON configuration file declares which lifecycle events the plugin handles and maps them to their implementations.

According to the repository structure, this file contains the hook definitions:

{
  "sessionStart": "plugins/codex/scripts/session-lifecycle-hook.mjs",
  "sessionEnd": "plugins/codex/scripts/session-lifecycle-hook.mjs"
}

Both hooks point to the same module, which exports differentiated handlers for each event type. This centralization simplifies maintenance while keeping the hook-to-handler mapping explicit and version-controllable.

Session Lifecycle Implementation

The plugins/codex/scripts/session-lifecycle-hook.mjs module contains the actual interception logic. It imports utilities from claude-session-transfer.mjs to bridge Claude Code's internal session representation with the plugin's state management system.

SessionStart Handler

When Claude Code initializes a new session, the sessionStart hook executes immediately. The handler:

  1. Extracts the session identifier, workspace directory, and environment variables from the event payload
  2. Invokes transferSession() from the session transfer library to normalize the data
  3. Persists this metadata via state.set() for cross-command accessibility
import { transferSession } from "./lib/claude-session-transfer.mjs";
import { state } from "./lib/state.mjs";

export async function onSessionStart(event) {
  const { sessionId, workspace } = await transferSession(event);
  await state.set("currentSession", { sessionId, workspace });
}

This persistence layer enables subsequent plugin commands—such as codex review or codex result—to retrieve the active session context without re-initializing connections to Claude Code.

SessionEnd Handler

When the Claude Code session terminates, the sessionEnd hook triggers cleanup operations:

import { state } from "./lib/state.mjs";

export async function onSessionEnd() {
  await state.delete("currentSession");
  // terminate background jobs, flush temporary files, etc.
}

The handler:

  • Removes the stored session entry via state.delete()
  • Releases any broker-registered resources
  • Prevents orphaned background processes from persisting across sessions

Broker Integration Architecture

The lifecycle hooks do not operate in isolation. They register with the App-Server Broker implemented in plugins/codex/scripts/app-server-broker.mjs, which serves as the event routing layer between Claude Code and installed plugins.

This broker architecture provides two critical capabilities:

  • Real-time event forwarding — Claude Code lifecycle events reach the plugin without core code modification
  • Isolation boundary — Plugin failures in hook handlers do not crash the host Claude Code process

Session State Persistence

The plugins/codex/scripts/lib/state.mjs module provides the storage backend for session metadata. By using this abstraction rather than in-memory variables, the plugin:

  • Survives individual command invocations within the same session
  • Enables inspection of session state for debugging
  • Supports future extensions like session serialization or replay

Key Files and Responsibilities

Component Path Function
Hook declarations plugins/codex/hooks/hooks.json Maps lifecycle events to handler modules
Lifecycle handlers plugins/codex/scripts/session-lifecycle-hook.mjs Implements onSessionStart and onSessionEnd
Session transfer plugins/codex/scripts/lib/claude-session-transfer.mjs Normalizes Claude-specific session data
State management plugins/codex/scripts/lib/state.mjs Persistent key-value storage for session metadata
Event broker plugins/codex/scripts/app-server-broker.mjs Routes Claude Code events to registered plugins

Practical Impact of Session Interception

The SessionStart and SessionEnd hooks enable several plugin capabilities that would otherwise require deep Claude Code integration:

  • Workspace persistence — Commands executed across multiple invocations share the same session context and working directory
  • Metadata propagation — Session identifiers flow automatically to render pipelines and job tracking systems
  • Resource hygiene — Guaranteed cleanup prevents accumulation of temporary files and zombie processes
  • Cross-session isolation — Complete state reset between sessions eliminates data leakage risks

Summary

  • The Codex-CC plugin intercepts Claude Code sessions through declarative hooks configured in hooks.json
  • session-lifecycle-hook.mjs handles both startup and shutdown events via differentiated exports
  • claude-session-transfer.mjs bridges Claude's internal session representation with plugin-compatible data structures
  • state.mjs provides persistent storage that survives individual command executions within a session
  • The App-Server Broker routes events safely without modifying Claude Code core functionality
  • Cleanup in onSessionEnd ensures resources are released and session isolation is maintained

Frequently Asked Questions

How does the plugin know when a Claude Code session starts?

The sessionStart hook declared in hooks.json registers a callback with the App-Server Broker. When Claude Code initializes a new session, the broker invokes this callback with session metadata including the workspace path and environment variables.

What happens if the session end hook fails to execute?

The broker architecture isolates plugin failures from the host process. If onSessionEnd throws or hangs, Claude Code still terminates normally, though temporary files or background jobs may persist. The plugin implements defensive cleanup on subsequent session starts to handle such edge cases.

Can multiple plugins register the same lifecycle hooks?

Yes. The broker supports multiple plugin registrations for identical hooks. Each registered handler executes independently, and failures in one plugin do not cascade to others. Execution order follows plugin load sequence unless explicitly prioritized.

Where is session data stored between commands?

Session metadata persists in the state module at plugins/codex/scripts/lib/state.mjs. This abstraction uses a backing store (typically filesystem-based) that survives process boundaries between individual Codex CLI invocations within the same Claude Code session.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →