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:
- Extracts the session identifier, workspace directory, and environment variables from the event payload
- Invokes
transferSession()from the session transfer library to normalize the data - 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.mjshandles both startup and shutdown events via differentiated exportsclaude-session-transfer.mjsbridges Claude's internal session representation with plugin-compatible data structuresstate.mjsprovides 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
onSessionEndensures 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →