Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sessionport

CI License: MIT Node deps

Export, normalize and convert coding-agent session transcripts. sessionport is a small, zero-dependency CLI that reads a session from one coding agent and writes it out in another format — normalizing everything through a single canonical schema so you can archive, diff, share, or move conversations between tools.

Status: early. It speaks the three major LLM API message formats (OpenAI, Anthropic, Gemini) plus specific agent CLIs (Claude Code, Codex) and the ChatGPT data export. Vendor exporters are best-effort for text content and are not guaranteed to be replayable by the original app — treat them as archival/handoff output, not a perfect restore. See Validation status below for how thoroughly each format has been checked.

Why

People routinely switch between agent CLIs (Claude Code, Codex, and others), but each stores its session transcript in its own JSONL shape. There's no common way to read one, hand it to another, or export a clean copy for review. sessionport gives you a single convert command and a documented canonical format.

Install

Published as @williamsuchun/sessionport (the unscoped name was taken).

# Run without installing
npx @williamsuchun/sessionport formats

# Or install globally (exposes the `sessionport` command)
npm install -g @williamsuchun/sessionport
From source
git clone https://github.com/williamsuchun/sessionport && cd sessionport
npm install && npm run build
npm link            # exposes `sessionport` on your PATH

Requires Node.js >= 20.

Usage

# Auto-detect input, convert to a portable canonical JSON
sessionport convert session.jsonl --to portable -o session.portable.json

# Export a Codex session to readable Markdown
sessionport convert ~/.codex/sessions/2026/.../rollout-*.jsonl --to markdown -o out.md

# Move a Claude Code transcript into the OpenAI chat shape
sessionport convert ~/.claude/projects/<proj>/<id>.jsonl --from claude-code --to openai-chat

# Summarize a session without converting it
sessionport inspect session.jsonl

# List supported formats
sessionport formats

inspect prints only metadata and per-role message counts (no message bodies):

source:     codex
sessionId:  019f3b85-…
cwd:        /path/to/project
messages:   411
by role:    system=4  user=6  assistant=67  tool=334
metadata:   {"cliVersion":"0.142.5","modelProvider":"azure"}

Formats

sessionport speaks the three major LLM API message shapes as a generic substrate, plus specific agent CLIs and export files:

Format Import Export Kind Notes
portable ✓ ✓ canonical sessionport's own schema (sessionport/v1); lossless round-trip
anthropic-messages ✓ ✓ API standard Anthropic Messages API ([{role,content-blocks}]); also Cline / Roo Code api_conversation_history.json
openai-chat ✓ ✓ API standard OpenAI Chat Completions ({messages:[{role,content,tool_calls}]})
gemini ✓ ✓ API standard Google Gemini Content ({role,parts}); Gemini API / gemini-cli
claude-code ✓ ✓ agent CLI Claude Code transcript JSONL (~/.claude/projects/**/<id>.jsonl)
codex ✓ ✓ agent CLI Codex CLI rollout JSONL (~/.codex/sessions/**/rollout-*.jsonl)
chatgpt-export ✓ export OpenAI ChatGPT data export conversations.json (tree, linearized)
markdown ✓ human Human-readable transcript

Input format is auto-detected by default; override with --from.

Validation status (honest)

Formats differ in how thoroughly they've been checked against real data:

  • Validated against real local files: claude-code, codex, anthropic-messages (verified against a real Cline api_conversation_history.json).
  • Implemented from documented API shapes: openai-chat, gemini — the message structure follows each provider's public API; the exact on-disk wrapper used by a given CLI may vary, so pass --from if detection is unsure.
  • Implemented from the widely-documented export shape: chatgpt-export — not yet checked against a fresh export.

If an importer misreads a file from a client you use, that's a bug worth an issue — please attach the (redacted) structure.

Why these formats (design)

There is no industry standard for the session envelope (session id, working directory, timestamps), so sessionport keeps a small custom canonical model (sessionport/v1). At the message level, though, almost every agent reduces to one of three provider API shapes — OpenAI, Anthropic, Gemini — so those are first-class adapters. Many clients persist raw API messages (e.g. Cline/Roo store Anthropic messages verbatim), so one API adapter often covers several clients. Telemetry schemas (OpenTelemetry GenAI) and protocols (MCP) were considered and deliberately not used as the canonical model — they describe spans/RPC, not stored transcripts.

Canonical schema (portable)

{
  "schema": "sessionport/v1",
  "source": "claude-code",
  "sessionId": "…",
  "cwd": "/path/to/project",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "metadata": { "version": "2.1.0" },
  "messages": [
    { "role": "user", "text": "…", "timestamp": "…" },
    { "role": "assistant", "text": "…" },
    { "role": "tool", "toolName": "read_file", "toolCallId": "tu_1", "text": "…",
      "meta": { "kind": "tool_use" } }
  ]
}

Roles are normalized to user | assistant | system | tool.

How importers map real transcripts

These mappings are derived from actual local session files:

  • Claude Code — each JSONL line is an event; user/assistant events carry a message whose content is either a string or an array of blocks. Text blocks become message text; tool_use / tool_result blocks become separate tool messages. Content blocks that aren't text (e.g. images) become compact placeholders like [image] rather than being dropped or dumped as base64. Non-conversation events (queue operations, file-history snapshots) are skipped.
  • Codex — the conversation is taken from response_item entries of payload.type === "message" / agent_message (content blocks input_text / output_text); function_call, function_call_output, and custom tool calls become tool messages, with list-form outputs flattened the same way. UI event_msg entries and encrypted reasoning items are skipped.

Limitations

  • Importers preserve text; non-text blocks (images, references) are reduced to placeholders, and vendor-specific fields (reasoning, encrypted content, token accounting) are not carried into the canonical model.
  • The openai-chat exporter emits an API-valid shape (tool calls as assistant.tool_calls, results as role:"tool"); when the source lacked call ids they are synthesized best-effort, so call/result pairing may be imperfect.
  • Vendor exporters (claude-code, codex) are best-effort for text and are not guaranteed to be replayable by the original app. portable is the only lossless round-trip format.
  • Auto-detection is heuristic; pass --from if a file is ambiguous.
  • On-disk formats can change between agent versions; importers target the shapes observed at time of writing and may need updates.

Development

npm run build      # tsc -> dist/
npm test           # build + vitest
npm run test:watch

License

MIT

About

Convert coding-agent session transcripts between Claude Code, Codex and portable formats. Zero-dependency CLI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages