codex-session-tui is a terminal-first session explorer for Codex.
It gives you a VS Code style workflow for ~/.codex: a browser tree on the left, a rich preview on the right, searchable conversations, foldable blocks, multi-select, drag and drop, and cross-machine session management.
It can also act as a Codex session command center: connect to SSH machines, step through container prefixes such as lxc-attach, inspect all those session stores in one browser, and move conversations between them without leaving the TUI.
If codex resume stops finding conversations after a repo move, folder rename, machine migration, or container hop, this tool is built to recover that state safely.
Run directly:
npx -y codex-session-tui@latestInstall globally:
npm i -g codex-session-tui
codex-session-tuiUse a different Codex home:
CODEX_HOME=/path/to/.codex codex-session-tuiCode in 2.x releases is Apache-2.0. Repository documentation is CC BY-SA 4.0. Prior 1.x releases remain under their original terms.
Non-interactive session operations:
codex-session-tui copy <session-id> pi@openclaw:/home/pi/data/cases
codex-session-tui move <session-id> /home/pi/work/repo
codex-session-tui fork <session-id> dev:/srv/project
codex-session-tui export <session-id> user@host:/remote/project/path
codex-session-tui tree
codex-session-tui ls pi@openclaw
codex-session-tui ls pi@openclaw:/home/pi/data/cases
codex-session-tui repair-index
codex-session-tui repair-index pi@openclawCodex sessions are tied to recorded cwd values.
That means:
- rename a repository and sessions can disappear from
codex resume - move work to another disk or mountpoint and sessions can look lost
- shift between local, SSH, and container environments and session continuity breaks
codex-session-tui fixes that workflow:
- browse sessions as conversations, not raw JSON
- search by text, path, and hash
- move, copy, fork, or export sessions into a new project context
- repair stale
cwdmappings so Codex can see the sessions again - work across local and remote machines from one browser
This is not a file dumper. It is meant to feel like an editor/workspace browser:
- GitHub-style grouped folder tree
- VS Code style left-browser plus right-preview workflow
- discoverable status bar shortcuts
- mouse support for splitters, scrolling, folding, selection, and drag/drop
- multi-machine browsing with health indicators
- direct open-in-Codex flow from the current session
- Unified browser for
localplus SSH-connected remotes - Remote session command center for multiple hosts and containers from one screen
- Grouped project tree with compressed single-child folder chains
- Session list ordered by recent activity
- Rich preview with markdown rendering, foldable blocks, timestamps, and per-role grouping
- Search that filters the browser and jumps the preview to relevant matches
- Multi-select session operations
- Drag-to-move and
Ctrl+drag-to-copy across folders and machines - Folder-tree drag/drop that preserves grouped subpaths when moving across machines
- Keyboard copy/cut/paste across local and remote folders
- Remote machine support including nested container entry via
exec_prefix - Virtual folder creation for cwd targets that do not exist on disk yet
- Export to another machine as a real Codex session, not a loose JSONL dump
- Repair of previously broken local
cwdmappings and Codex thread-index sync - Recovery flatten for complex sessions when Codex resumes only a prefix of the visible chat
This is where the remote model becomes useful in practice.
You can use one TUI instance to:
- browse your local sessions and multiple SSH machines in one tree
- hop through container boundaries such as
lxc-attach -n dev -- - inspect a chat running inside a remote container without opening another terminal
- drag a session from one machine into another machine's project folder
- copy a conversation from a laptop to a server, or from a server into a container
- recover chats after a mountpoint change, repo rename, or machine migration
If your workflow spans local dev, remote boxes, and containers, this app is meant to be the control plane above all of them.
The left pane is the workspace/session browser.
It shows:
- top-level machine roots:
localplus configured remotes - grouped folder tree under each machine
- sessions underneath their project folder
- machine health badges:
[ok],[cached],[offline] - user-only sessions marked with
!
It is designed for the same scanning pattern as a code editor sidebar: move through structure first, then inspect detail.
The right pane is the conversation viewer.
It shows:
- chat, not raw event JSON
- adjacent user messages merged into one block
- adjacent assistant messages merged into one block
- readable timestamps
- total user and assistant message counts in the header
- full session id in the header
- default focus at the end of the conversation
Assistant blocks start collapsed by default. User blocks start expanded, except the first large prompt block, which starts collapsed.
The footer is part of the UI, not decoration.
It always shows the important shortcuts for the current context so you do not have to memorize the app.
Start the app:
codex-session-tuiOn launch:
- the browser starts on the
localmachine root - the first machine root is visible immediately
- folders start collapsed so the tree is readable
- local and remote sessions stream in incrementally instead of blocking the first frame
- folder rows update their session counts as the browser fills
- no session preview is shown until you actually select a session
Up/Down: move through visible rowsTab: toggle the selected folder open/closedRight: expand a folder or enter its sessionsLeft: collapse a folder or return from a session to its folder rowEnter: expand/collapse folder or open the selected sessionAlt+Left/Alt+Up: move focus to the previous paneAlt+Right/Alt+Down: move focus to the next paneCtrl+Up/Ctrl+Down: jump between projectsCtrl+Left: collapse all folders except the current oneCtrl+Right: expand all foldersF5/Ctrl+R: refresh local and remote state in the backgroundR: add or update a remote machined: delete the selected remote machine entryn: create a new virtual folder under the selected machine or folderm/x: cut into the browser clipboardc: copy into the browser clipboardf: fork into the browser clipboardv: paste into the selected folderM/C: typed move/copy-to-target-path flow for the selected folder or subtreer: typed rename of the selected folder or subtree
Mouse:
- click to select folders and sessions
- double-click folders to expand/collapse
- double-click sessions to open Preview
- drag splitters to resize panes
- drag scrollbars to jump quickly
Esc: return focus to BrowserTab: fold/unfold current blockShift+Tab: fold/unfold all blocksAlt+Left/Alt+Up: move focus to the previous paneAlt+Right/Alt+Down: move focus to the next paneUp/Down: move between preview blocksPageUp/PageDown: page through large conversationsHome/End: jump to top or bottomCtrl+Up/Ctrl+Down: jump to top or bottom like a spreadsheetCtrl+Left/Ctrl+Right: move to previous or next folded blockn/N: jump to next/previous match in the current chato: leave the TUI and open the selected session incodex resumeb: flatten the selected session into a fresh linear recovery clone in the same machine and folder
Mouse:
- scroll
- fold blocks
- select text
- copy selected preview text through OSC52-capable terminals
Press / to search.
Search is built to feel like editor search, not fuzzy guesswork.
It:
- updates the query immediately and starts searching without waiting for the old deferred background-search pause
- filters the browser tree
- searches conversation text, path, session id/hash, and file name
- supports multi-word search
- supports quoted phrases such as
"openrouter error" auth - auto-selects the best matching session
- jumps the preview to the first relevant occurrence
- expands the browser tree to reveal the active matching session as you step through search results
- highlights matches in Browser and Preview
- highlights the primary preview hit more strongly than later hits
- keeps Preview attached to the current filtered browser context instead of going blank
Search navigation:
Enter: keep the current resultEsc: close searchLeft/Right: move inside the search textCtrl+A/Ctrl+E: jump to start/end of the search textTab/Shift+Tab: move focus out of the search box[/]: previous/next matching session in the filtered resultsn/N: previous/next occurrence in the current session only- when
n/Nreaches the end or beginning of the current session, the status bar tells you it wrapped and keeps navigation inside that session - footer buttons
[Prev Session]/[Next Session]and[Prev Hit]/[Next Hit]are clickable by mouse - the Preview header shows the current hit count as
hits=x/y
On a session:
morx: cut into the browser clipboardc: copy into the browser clipboardf: fork into the browser clipboardv: paste into the selected folder or grouped subtreeM: typed move to/pathormachine:/pathC: typed copy to/pathormachine:/pathF: typed fork to/pathormachine:/pathd: delete- delete now runs with live status/progress feedback instead of freezing the UI during long removals
e: export over SSHo: open in Codex
Selection:
Space: toggle selectiona: select all sessions in the current projecti: invert selection
Targets can be:
- a local path, for example
/home/me/work/repo - a machine-qualified path, for example
pi:/home/pi/work/repo
Semantics:
copy: duplicate the chat into another folder and keep the conversation shapefork: duplicate the chat as a new branch with a fresh session id and fresh fork/start metadatamove: rewrite the existing session into another folder context
Example:
copyis for "same chat, second location". If you copy a session fromlocal:/home/pi/gh/apptopi:/home/pi/gh/app, the duplicate carries the same conversation history into the remote folder as another copy of that chat.forkis for "continue from here as a new branch". If you fork a session after a useful checkpoint, the new session starts with the same history up to that point, but it becomes a distinct conversation with its own identity and fork metadata.
If you do not want to type paths, you do not have to.
Browser actions work across connected machines as if everything were local:
corCtrl+C: copy current session selection, current project, or current grouped folder sessionsm,x, orCtrl+X: cut current selectionf: prepare a fork of the current selectionvorCtrl+V: paste into the selected folderd: delete the selected session, folder, or folder subtree- drag: move into the hovered folder
Ctrl+drag: copy into the hovered folder- dragging a grouped folder preserves that folder as a subtree instead of flattening all sessions into one cwd
The intent is file-manager style session handling: you should be able to move operational context around your estate the same way you move files in Explorer or VS Code's workspace tree, without stopping to type paths every time.
For reproducible operations, automation, or debugging a suspicious session transfer, use the non-interactive CLI mode.
Supported commands:
codex-session-tui copy <session-id> <target>codex-session-tui move <session-id> <target>codex-session-tui fork <session-id> <target>codex-session-tui export <session-id> <target>codex-session-tui treecodex-session-tui ls [machine|machine:/path]
Examples:
codex-session-tui copy 019aee85-21cf-78a2-9a65-5286d2f341b6 pi@openclaw:/home/pi/data/cases
codex-session-tui move 019aee85-21cf-78a2-9a65-5286d2f341b6 /home/pi/data/cases-debugCLI mode loads the local Codex store directly and does not wait for remote browser scans before running the requested session action. That makes it suitable for recovery work and for isolating transfer bugs without going through the interactive UI.
tree and ls use the Browser's grouped tree model instead of dumping raw files. They are useful for checking exactly what the TUI thinks exists on each machine and folder when debugging remote visibility problems.
repair-index backs up the Codex thread database and removes stale rows whose rollout path no longer exists. Use it after older buggy copies, manual filesystem cleanup, or interrupted migrations. You can run it for local, for one remote machine name, or across all configured machines.
This works for:
- local to local
- local to remote
- remote to local
- remote to remote
Typical examples:
- drag a local chat into
pi:/home/pi/work/repo Ctrl+draga production debugging conversation from one remote machine into another machine's staging repo- cut a session from a host machine and paste it into a container-backed machine configured with
lxc-attach -n dev -- - drag the grouped
gitfolder fromlocalonto a remote machine root and keep thegit/...subtree intact there - create
ghas a virtual folder on a remote machine, then drag sessions into it before the actual repo exists on disk
Folder and grouped-tree targets resolve automatically, so the Browser can be used like a workspace explorer instead of a path prompt.
Project and grouped-folder rows also support folder-wide copy/rename style workflows, so you can remap entire project histories instead of one session at a time.
Important distinction:
- drag/drop on a grouped folder preserves that folder name as part of the destination subtree
- typed
r,M, orCon a grouped folder performs a prefix remap
Example:
- drag
gitontopi:/home/pi/work-> sessions land underpi:/home/pi/work/git/... - rename grouped
/rootto/home/pi-> sessions land under/home/pi/...rather than/home/pi/root/...
Sometimes you want a destination cwd before the actual repository exists on that machine.
Use n on a machine root or folder row to create a virtual folder.
Important behavior:
- this does not create a real directory on disk
- it creates a persistent browser destination in the TUI config
- you can use it as a drop target, paste target, or typed move/copy/fork target later
- once sessions are moved there, that cwd becomes part of the normal session tree even if no real repo exists yet
Typical use:
- create
ghon a remote machine - drag local
git/...session groups intogh - keep session organization aligned with the repo layout you intend to create later
Move, copy, paste, export, and folder-wide operations can take time, especially across SSH machines.
When that happens, the status bar shows:
- an explicit
Working...state immediately - a slower blinking
Working...indicator while the operation is active - a live progress bar
- counts for completed, skipped, and failed session transfers
Startup and refresh use the same model:
- the UI stays responsive while the browser populates
- local sessions stream in first
- remote machines update as each scan finishes
- new async-loaded machine and folder rows arrive collapsed so the existing tree state is preserved
That keeps long remote copies and grouped drag/drop operations understandable instead of looking like a stalled terminal UI.
Export is for sending a real Codex session to another machine.
Enter targets like:
user@host:/remote/project/path
Important behavior:
- the path is the remote project
cwd - it is not the remote
~/.codexstorage path - the session is installed under the remote machine's
${CODEX_HOME:-~/.codex}/sessions/... - the session JSONL is rewritten to the remote project path
- the remote Codex thread index is updated so
codex resumecan see it - existing remote rollout files are not overwritten
Remote support is not an add-on. It is one of the main reasons this project exists.
Once configured, remote machines appear directly in the Browser beside local, so the app behaves more like a distributed workspace explorer than a single-machine viewer.
Configured remotes are loaded from:
.codex-session-tui.tomlin the current working directory~/.config/codex-session-tui.toml
You can add or update a machine from inside the app with R.
Supported input forms:
user@hostname=user@hostname=user@host:/absolute/path/to/.codexname=user@host|exec-prefixname=user@host|exec-prefix|/absolute/path/to/.codex
If name= is omitted, the machine name is derived from the SSH host.
Examples:
pi@192.168.0.124
192.168.0.124=pi@192.168.0.124
pi=pi@192.168.0.124:/home/pi/.codex
root@example-host|lxc-attach -n dev --|/root/.codex
dev=root@example-host|lxc-attach -n dev --|/root/.codex
Practical meaning of those examples:
pi@192.168.0.124connects to a plain remote Codex home with pubkey SSHpi=pi@192.168.0.124:/home/pi/.codexpins a friendly machine name and a non-default Codex homedev=root@example-host|lxc-attach -n dev --|/root/.codexSSHes to the host, enters thedevcontainer, and then treats that container as another first-class Codex machine in the Browser
Reusing R with the same connection details but a new name updates the existing machine entry in place.
[[machines]]
name = "pi"
ssh_target = "pi@192.168.0.124"
codex_home = "/home/pi/.codex"If codex_home is omitted, the remote defaults to ~/.codex.
[[machines]]
name = "dev"
ssh_target = "root@example-host"
exec_prefix = "lxc-attach -n dev --"
codex_home = "/root/.codex"This lets the TUI:
- SSH to the host
- enter the container
- scan sessions
- preview chats
- move/copy/fork sessions
- launch
codex resumeinside that environment
That is the intended model: a container is not a second-class target. If Codex runs there, codex-session-tui should let you manage it as if it were just another workspace root.
- machine roots are marked
[ok],[cached], or[offline] - failed scans keep the last good snapshot instead of dropping the machine
g,F5, andCtrl+Rforce a fresh remote scan- internal reloads may reuse a recent cached scan for responsiveness
Preferred:
- SSH keys
- SSH agent
- host aliases in
~/.ssh/config
Supported but less ideal:
- password-based SSH when your environment already handles the prompt externally
The app uses non-interactive remote scanning so startup does not hang waiting on password prompts.
For serious use, key-based auth is the right setup. It makes the Browser feel immediate and keeps the remote command-center workflow usable. Password-based SSH can work, but it is operationally worse and should be treated as fallback.
The app does more than browse.
It also repairs common Codex session breakage:
- local move/copy/fork targets are normalized before being written
- relative paths become absolute
- trailing slashes and
./..segments are cleaned - previously broken local
cwdrewrites are repaired on startup - session files whose internal
session_meta.iddrifted away from the rollout filename are repaired on startup - stale local
threadsrows pointing outside the active~/.codex/sessionsroot are removed on startup - Codex's local
threadsSQLite index is reconciled so repaired sessions reappear incodex resume - repaired thread metadata skips AGENTS/system preambles so
codex resumeis more likely to show the real first prompt
There is one Codex-side failure mode the TUI does not try to rewrite in place:
- some very large or fork-heavy sessions are stored on disk, and the TUI Preview can show the visible transcript, but
codex resumemay reopen only an earlier branch prefix
For that case the TUI now offers a conservative recovery path:
- Preview marks some event-heavy sessions as complex and warns that Codex may resume only a prefix
- press
bon a selected session or from Preview to create a fresh linear recovery clone in the same machine andcwd - the original session is left untouched
- if the recovery clone looks right, you can then move/copy it elsewhere using the normal browser workflows
User-only sessions are also marked clearly:
- Browser shows
! - Preview warns that the session may not be resumable by Codex
- Launch
codex-session-tui. - Press
/and search by old path, session hash, or conversation text. - Inspect the conversation in Preview.
- Move it with
m, paste it withCtrl+V, or drag it into the correct project. - For large tree-level rewrites, select the grouped parent folder and use
rorM. - Return to Codex and resume from the corrected working directory.
The tool is conservative by design:
- backups are created before mutating or deleting session files
- writes use atomic temp-file plus rename
- unknown JSON fields are preserved
- only targeted fields are rewritten during remap/fork/export operations
Backups are created next to the original session file under ${CODEX_HOME:-~/.codex}/sessions.
Backup filename format:
<original>.jsonl.bak.YYYYMMDDHHMMSS
Find backups:
find "${CODEX_HOME:-$HOME/.codex}/sessions" -type f -name "*.jsonl.bak.*"Restore a backup:
cp "/path/to/session.jsonl.bak.20260224101530" "/path/to/session.jsonl"Prebuilt binaries are published for:
- Linux:
x86_64,aarch64,armv7 - macOS:
x86_64,aarch64 - Windows (MSVC):
x86_64,aarch64
Run locally:
cargo runRun against a different Codex home:
CODEX_HOME=/path/to/.codex cargo run