Move AI coding sessions between Claude Code and Codex CLI without losing context.
Local-only JSONL conversion. No cloud service. No API keys.
Install • Quick Start • Commands • Architecture • Safety
Both tools store sessions as JSONL files on disk. session-porter reads, normalizes, and converts between them — so you can start a conversation in Claude Code and continue it in Codex (or vice versa).
npm install -g @liwala/session-porterOr clone and link:
git clone https://github.com/liwala/session-porter.git
cd session-porter
npm install && npm run build && npm link# See what can be moved
session-porter list --claude -n 10
session-porter list --codex -n 10
# Inspect one session before moving it
session-porter summary <session-id>
# Move a Claude Code session into Codex
session-porter transfer <session-id> --from claude --to codex --mode compact
# Continue the moved session
codex resume <session-id>session-porter list # All sessions (Claude + Codex), sorted by date
session-porter list --claude # Claude Code sessions only
session-porter list --codex # Codex CLI sessions only
session-porter list -n 10 # Limit resultssession-porter show <session-id> # User/assistant pairs
session-porter show <session-id> --tools # Include tool callsSession IDs are prefix-matched — first 8 characters are enough.
session-porter summary <session-id>Shows turn counts, tool operations, files touched, and token usage.
session-porter export <id> -f json # Clean user/assistant JSON
session-porter export <id> -f markdown # Readable markdown
session-porter export <id> -f codex # Codex CLI native JSONL
session-porter export <id> -f claude # Claude Code native JSONL
session-porter export <id> -f json -o out.json # Write to file# Claude Code → Codex CLI
session-porter transfer <session-id> --to codex
# Codex CLI → Claude Code
session-porter transfer <session-id> --to claude
# Transfer modes:
session-porter transfer <id> --to codex --mode raw # Exact chat (default)
session-porter transfer <id> --to codex --mode full # Chat + handoff context
session-porter transfer <id> --to codex --mode compact # Handoff + user/assistant only
session-porter transfer <id> --to codex --mode resume-only # Just a context briefTransfer writes the converted session directly into the target tool's session directory and registers it in the Codex SQLite database (for codex resume).
session-porter continue <session-id> --in codex
session-porter continue <session-id> --in claude --mode fullTransfers the session, then spawns the target CLI so you can keep working immediately.
session-porter fork <session-id> --to codex # Full fork
session-porter fork <session-id> --to codex --last-n 3 # Last 3 user turns only
session-porter fork <session-id> --to claude --from-turn 5 # From turn 5 onwardsOriginal session stays untouched.
session-porter handoff <session-id> # Print handoff JSON + resume prompt
session-porter handoff <session-id> -o handoff.md # Save to fileProduces a compressed context brief you can paste into any agent (including ones without adapters).
session-porter search "database migration" # Text search
session-porter search "fn\s+\w+" --regex # Regex search
session-porter search "bug" --agent codex # Filter by agent
session-porter search "deploy" -n 5 # Limit resultssession-porter statsShows session counts per agent, date range, total messages, and models used.
session-porter doctor
session-porter doctor --cwd /path/to/projectChecks for broken SKILL.md files, missing CLAUDE.md, MCP server configs, and CLI availability.
session-porter adaptersEvery tool call from any agent maps to a universal operation:
| Op | Claude Code | Codex CLI |
|---|---|---|
file.read |
Read | cat via exec_command |
file.write |
Write | cat > via exec_command |
file.edit |
Edit | apply_patch / sed |
file.glob |
Glob | rg --files -g |
search.content |
Grep | rg |
shell.exec |
Bash | exec_command |
web.search |
WebSearch | — |
agent.spawn |
Agent | — |
Adding a new agent requires implementing the AgentAdapter interface:
interface AgentAdapter {
name: AgentType;
displayName: string;
hasSessions(): boolean;
listSessions(): SessionInfo[];
loadSession(sessionId: string): PortableSession | null;
writeSession(session: PortableSession): WriteResult;
mapToolToNative(tool: UniversalToolCall): { name: string; input: unknown };
mapToolFromNative(nativeName: string, input?: unknown): UniversalToolOp;
}Register it in src/adapters.ts and it works with all commands automatically.
| Tool | Path | Format |
|---|---|---|
| Claude Code | ~/.claude/projects/<slug>/<session-id>.jsonl |
JSONL envelope: {type, message, uuid, timestamp} |
| Codex CLI | ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl |
JSONL: session_meta + response_item + event_msg |
- User prompts (full text)
- Assistant responses (full text)
- Tool calls (name, input, output) — mapped to universal ops
- Timestamps
- Session metadata (model, cwd, title)
- Thinking blocks (Claude-specific, contains signatures)
- Token usage / billing data
- System prompts and injected context
- File history snapshots
npm install
npm run build # Compile TypeScript
npm test # Compile and run node:test tests
npm run pack:check # Verify npm tarball contents
npm run dev # Watch mode
node dist/index.js list # Run directlyThe published npm package includes only compiled files under dist/, this
README, and npm's required package metadata. It does not include local Claude or
Codex session files, .env files, setup screenshots, generated archives, or
project planning folders.
Before publishing, run:
npm test
npm run pack:check
npm whoami
npm publish --access publicThe package does not require API keys. Do not commit npm tokens; authenticate
locally with npm login or publish through the GitHub release workflow using an
npm automation token stored only as the NPM_TOKEN repository secret.
MIT