agent-session-migrator
v0.4.0
Published
Read Claude Code, Codex, and Grok sessions and hand them off to another agent.
Readme
agent-session-migrator
Read your local Claude Code, Codex, and Grok sessions and hand one off to a different agent — read-only, offline, no transcript uploads.

You hit a wall in one coding agent and want to continue in another. The context lives in a JSONL transcript on your disk, but pasting it by hand loses the goal, the files, and the last thing you asked for. asm reads those transcripts, normalizes them into one shape, and produces a handoff prompt you can copy, save, or pipe straight into the other agent's CLI.
$ asm list
claude 764c9b37-d923-4748-bcf8-f4d19da2a1b6 Fix flaky retry test 2026-09-02T09:58:28.922Z
grok 01a05c58-159f-7330-8641-326d77e47489 Restyle web frontend 2026-09-02T09:53:20.247Z
$ asm migrate "Fix flaky retry" --to codex
/tmp/asm-handoff-1788343763053.md Fix flaky retry testFeatures
- Three sources, one shape. Claude Code, Codex, and Grok transcripts parse into a single session IR — same fields, same tool-call model, regardless of origin.
- Read-only by construction. Native session stores are never written. Target agents get a fresh process and a prompt, never a patched JSONL row.
- Handoff modes that fit a prompt.
briefdistills goal, last request, environment, files split by whether they were written or only read, and failed tool calls.recentadds the last N turns.fulldumps everything to a file. - Direct spawn.
--to claude|codex|groklaunches the target CLI with the handoff on stdin, and refuses anything over 32 KiB instead of truncating silently. - Localhost web browser.
asm webserves a two-column session reader on127.0.0.1behind a per-run random token. - Resolve sessions the lazy way. A
<ref>can be a session id, a transcript path, or a unique substring of a title.
Requirements
- Node.js 20 or newer
- At least one of Claude Code, Codex, or Grok used locally (that's where the transcripts come from)
zstdonPATH— optional, only needed to read Codex.jsonl.zstsessions
Quick start
npm i -g agent-session-migrator
asm listOr without a global install:
npx agent-session-migrator listFrom source:
git clone https://github.com/Misty-Star/agent-session-migrator.git
cd agent-session-migrator
npm install
node dist/cli/index.js --help[!NOTE]
asm listis scoped to a working directory — by defaultprocess.cwd(). Run it inside a project you've actually used an agent in, or pass--cwd <dir>.
Usage
asm list [--agent claude|codex|grok] [--cwd <dir>] [--json]
asm show <ref> [--agent …] [--cwd <dir>] [--json]
asm export <ref> [--format md|json] [--mode brief|recent|full] [--out <file>]
asm migrate <ref> [--to grok|claude|codex] [--mode brief|recent]
[--turns N] [--dry-run] [--print] [--out <file>]
asm web [--port 8787] [--cwd <dir>]Every command accepts --help, e.g. asm migrate --help.
Inspect a session
asm list --agent claude --json
asm show 764c9b37-d923-4748-bcf8-f4d19da2a1b6show prints source, id, title, turn count, and any parse warnings. <ref> resolution order is: existing path → session id → unique title substring. An ambiguous ref exits 1 and prints the candidates on stderr.
Export a handoff
# Markdown to stdout
asm export "Fix flaky retry" --mode brief
# Everything, to a file
asm export 764c9b37-d923-4748-bcf8-f4d19da2a1b6 --mode full --out handoff.md
# Machine-readable: { ir, handoff }
asm export "Fix flaky retry" --format json --out session.jsonMigrate to another agent
# Build the handoff, copy to clipboard, print the file path
asm migrate "Fix flaky retry"
# See the spawn command without running it
asm migrate "Fix flaky retry" --to codex --dry-run
# Launch Claude Code with the last 20 turns
asm migrate "Fix flaky retry" --to claude --turns 20migrate always writes a Markdown export (to --out, else a temp file) and copies the handoff to the clipboard on a best-effort basis (pbcopy / clip / wl-copy). With --to, the handoff goes to the target CLI on stdin — codex exec for Codex, -p for Claude and Grok. --print writes the handoff to stdout instead of the path line.
[!WARNING] Spawning refuses prompts over 32 KiB with
mode_not_launchable, and--mode fullis never launchable. Useasm export --mode fulland paste the file yourself.
Handoff modes
| Mode | Contents | Launchable |
| --- | --- | --- |
| brief | Inert-history banner, goal, last user request, environment (cwd/branch/model), files changed, files read, files mentioned, failed tool calls, open warnings, next action | Yes |
| recent (default) | brief plus the last 12 turns (--turns N to change) | Yes |
| full | brief plus every turn, with tool output clipped at 20,000 chars instead of 300 | No — export only |
Every handoff opens with a line telling the receiving agent that the block is inert history from another agent and its tool calls must not be executed.
Web UI
asm web --port 8787
# Open http://127.0.0.1:8787/?token=<random-hex>Sidebar lists sessions grouped by source agent; the conversation pane renders user and assistant messages with collapsible tool rows, and the header carries mode, target, Export, Copy, and Launch.
[!IMPORTANT] The server binds
127.0.0.1only and refuses any other host. API requests need the random per-run token, passed as?token=or anx-asm-tokenheader; without it they return 401. The working directory is locked at startup — pass--cwdto change it, sincePUT /api/cwdreturns 405 by design. Static assets (/,/app.js,/app.css, …) are served without the token; session data is not.
| Endpoint | Method | Purpose |
| --- | --- | --- |
| /api/health | GET | Liveness |
| /api/cwd | GET | The locked working directory |
| /api/sessions | GET | List sessions (?agent=) |
| /api/sessions/:id | GET | Full session IR |
| /api/export | POST | { id, mode, format, out } → export |
| /api/migrate | POST | { id, to, mode, dryRun, out } → handoff or spawn |
Configuration
Agent homes are read from the environment. Values may start with ~, which expands to your home directory.
| Variable | Default | Sessions read from |
| --- | --- | --- |
| CLAUDE_CONFIG_DIR | ~/.claude | projects/<slugified-cwd>/*.jsonl |
| CODEX_HOME | ~/.codex | sessions/ and archived_sessions/ rollout files |
| GROK_HOME | ~/.grok | sessions/<urlencoded-cwd>/<id>/ |
A missing home is reported as a home_missing warning on stderr, not a failure — the other agents still list.
Project layout
src/
core/ # discovery, IR, export, handoff, spawn — no I/O assumptions about entrypoint
adapters/ # claude.ts, codex.ts, grok.ts: transcript → SessionIR
cli/index.ts # citty commands
web/ # Hono server + static UI (no framework, no bundler)
tests/ # vitest, mirrors src/; fixtures under tests/fixtures/
docs/ # design specs and planscore is also importable as a library:
import { listSessions, readSession, buildHandoff } from 'agent-session-migrator'
const sessions = await listSessions({ cwd: process.cwd() })
const ir = await readSession({ ref: sessions[0].sessionId, cwd: process.cwd() })
console.log(buildHandoff(ir, { mode: 'brief' }))Development
npm test # vitest, 94 tests across 16 files
npm run build # tsc + copy web assets into dist/Known limits
- Sessions are discovered per working directory. There's no global "all sessions everywhere" listing.
- Thinking blocks are stripped at the adapters and not restored in any mode.
- Codex
.jsonl.zstsessions are skipped with azst_unavailablewarning whenzstdisn't installed. - Spawn targets are
claude,codex, andgrokonly, resolved fromPATH.
