Skip to main content

Local dashboard for browsing and rejoining AI coding-agent sessions (Claude Code, Codex, OpenCode, Pi, OpenClaw, Hermes).

Project description

rejoin

PyPI Python 3.11+ License: MIT CI

One dashboard for every coding agent you use. rejoin indexes Claude Code, Codex, OpenCode, Pi, OpenClaw, and Hermes sessions into a single searchable view — web or terminal — and lets you tmux-resume any of them with one keystroke.

Why

Every coding agent ships its own little session picker:

  • Claude Code has claude -r, but only lists Claude sessions.
  • Codex has codex resume, but only lists Codex sessions.
  • OpenCode and Pi each keep their sessions somewhere else again.

So if you run more than one — and most of us do — you can't answer basic questions:

  • "I remember debugging that webhook thing last Tuesday. Was it in Claude or Codex?"
  • "Show me every session I had open in ~/projects/my-app, across all agents."
  • "What was that Codex thread about auth tokens — the one I had open alongside the Claude session?"

rejoin is the unified view. It reads all four harnesses' session files locally, auto-titles them into scannable headlines ("HTTP Client Query String Redaction", not e0a57d18-…), and gives you one keyboard-first dashboard — web or TUI — to search across them, group by project, pin favorites, and pick back up in tmux.

Try it in 30 seconds

pipx install rejoin
rejoin         # web UI at http://127.0.0.1:8767
rejoin-tui     # terminal UI (tmux-aware)

Or pip install rejoin if you're not using pipx.

Full install with API-key setup → Install.    Walkthrough → docs/TUTORIAL.md.


Two front-ends, one SQLite cache

🖥️ Web UI  — FastAPI + HTMX

A warm-beige browser dashboard borrowed from Claude.ai's visual identity. Scan sessions in a two-pane layout, read the transcript in typographic serif/sans, click rejoin in tmux to pick back up where you left off.

web dashboard screenshot

⌨️ Terminal UI  — Textual, tmux-aware

The same dashboard as a first-class TUI. Inside tmux, pressing Enter on a row opens a new window in the current session and switches to it — no browser, no attach command, zero friction.

TUI demo


What rejoin is — and isn't

✅ Indexes all your coding agents in one place ❌ Not tied to any one harness
Reads your local session files Never writes to them
✅ Cross-agent search, group-by-cwd, pinning ❌ No cloud sync
✅ One-click tmux rejoin ❌ No auth / multi-user
✅ Web + terminal UIs ❌ Not a CLI replacement for claude -ra superset
✅ MIT, pure Python, small deps ❌ Not a framework

Features

  • Four tools: Claude Code and Codex (our own parsers — richer detail); OpenCode and Pi via agent-sessions.
  • Auto titles: qwen/qwen3-30b-a3b-instruct-2507 (~$7e-6 per title). Falls back to the first prompt if no key.
  • Rejoin in tmux: one click. Detached by default; inside tmux the TUI opens a new window in the current server.
  • Incremental reindex every 60 s, skipping unchanged mtimes.
  • Search (FTS5) with hit highlighting; group by cwd; pin favorites (★ floats to top).
  • Active indicator: pulses when (a) session file touched in last 2 min OR (b) a matching --resume <id> / codex resume <id> / pi <id> process is live.
  • Keyboard-first: j/k// navigate, Enter rejoins, p pins, / focuses search.

Install (agent-friendly, copy-paste this exact sequence)

1. Prerequisites

Verify each of these returns a success exit code before continuing.

python3 --version              # must print 3.11 or higher
git --version                  # any version
tmux -V                        # any version; required only for rejoin clicks

If python3 is older than 3.11:

  • Ubuntu/Debian: sudo apt install python3.12 python3.12-venv
  • macOS (Homebrew): brew install python@3.12

If tmux is missing:

  • Ubuntu/Debian: sudo apt install tmux
  • macOS: brew install tmux

2. Clone

git clone https://github.com/akakabrian/rejoin.git ~/AI/tools/rejoin
cd ~/AI/tools/rejoin

The target path is arbitrary; ~/AI/tools/rejoin is an example.

3. Virtual env + install

python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e '.[dev]'

Verify:

.venv/bin/python -c "from rejoin.app import main; from rejoin.tui import main as tmain; print('ok')"
# expected output: ok

4. Provide an OpenRouter API key (optional but recommended)

Without a key, sessions get a fallback title (truncated first prompt). With a key, they get a ~5-word LLM-generated title for about $7×10⁻⁶ each.

Pick one of three methods, in priority order:

Method A — shell env (most portable):

export OPENROUTER_API_KEY="sk-or-v1-…"

Method B — project-local .env (default for agents):

echo 'OPENROUTER_API_KEY=sk-or-v1-…' > .env
chmod 600 .env

This file is gitignored. It's the method most automated setups should use.

Method C — point at an existing .env:

export OPENROUTER_ENV_FILE="/path/to/existing/.env"

Verify:

.venv/bin/python -c "from rejoin.config import openrouter_api_key; print('key:', 'yes' if openrouter_api_key() else 'no')"
# expected: key: yes  (or 'no' if you skipped this step)

5. Run

./run.sh

Expected output on stdout:

INFO:     Started server process [NNNN]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8767

Open http://127.0.0.1:8767/ in a browser. Or launch as a Chrome app window:

google-chrome --app=http://127.0.0.1:8767/ --user-data-dir=/tmp/chrome-rejoin &

6. Verify end-to-end

curl -s http://127.0.0.1:8767/status
# expected: {"last_indexed_age_s": <small number>}

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8767/
# expected: 200

To use the terminal UI instead:

./run-tui.sh

Configuration

Create ~/.config/rejoin/config.toml to override defaults. Every key is optional; see config.example.toml for the full annotated list.

# ~/.config/rejoin/config.toml
host                 = "127.0.0.1"    # "0.0.0.0" ONLY on trusted networks
port                 = 8767
model                = "qwen/qwen3-30b-a3b-instruct-2507"
transcript_tail      = 40
active_window_sec    = 120
long_turn_lines      = 30
long_turn_chars      = 1500
refresh_interval_sec = 60
title_concurrency    = 8
turn_cache_size      = 16

Bad TOML prints a warning to stderr and falls back to defaults. Missing file → all defaults.

Shortcuts

key action
j / next session
k / previous session
g jump to top
Enter rejoin the selected session in tmux
p pin / unpin the open session
/ focus search
Esc blur / clear search

Click the amber on any row to pin/unpin without opening the session. Click in the header to force a reindex; the indexed Ns ago label confirms the background loop is alive.

Storage

path purpose
~/.local/share/rejoin/index.db SQLite cache (sessions, titles, pins, FTS5) — safe to delete
~/.config/rejoin/config.toml your overrides (optional)
<project>/.env project-local OpenRouter key (optional, gitignored)
~/.claude/projects/**/*.jsonl Claude Code source (read-only)
~/.codex/sessions/**/*.jsonl Codex source (read-only)
~/.local/share/opencode/opencode.db OpenCode source (read-only)
~/.pi/agent/sessions/**/*.jsonl Pi source (read-only)

rejoin never writes to session files. It only reads. The SQLite index is a pure cache; deleting it forces a clean reindex on next launch (titles re-generate).

Security / privacy

The dashboard exposes your full transcript history — every prompt, every tool call, every response. Treat it as sensitive:

  • Default bind is 127.0.0.1. The server is reachable only from the same machine.
  • Only set host = "0.0.0.0" on machines where you trust every peer on the network (e.g. a Tailnet-only host). There is no authentication.
  • Transcripts may include pasted secrets. Deleting a session file in ~/.claude or ~/.codex leaves the data in rejoin's SQLite cache until the next reindex-with-delete. If you want to purge: stop the server, delete ~/.local/share/rejoin/index.db, restart.

Architecture

rejoin/
├── common.py      # Tool Literal, iter_jsonl, text_of, utcnow_iso, short_cwd
├── config.py      # tomllib loader + defaults
├── db.py          # SQLite schema (sessions, titles, pins, session_fts); schema-version guard
├── indexer.py     # Claude + Codex parsers; PARSERS registry; OpenCode+Pi via external.py
├── titler.py      # async OpenRouter batch; concurrency cap; content-hash skip
├── transcript.py  # turn extraction (claude, codex); opencode+pi delegate to external.py
├── resume.py      # `cd <cwd> && <tool> --resume <id>`; tmux launch; missing-binary check
├── external.py    # adapter for Lars de Ridder's agent-sessions library
├── app.py         # FastAPI routes, background refresh loop, HTMX templates
├── tui.py         # Textual TUI app, tmux-aware rejoin
├── tui.tcss       # TUI stylesheet
├── templates/     # HTMX-rendered HTML fragments
└── static/        # web CSS + JS

Troubleshooting

symptom fix
'tmux' not found on PATH in the resume response install tmux (sudo apt install tmux or brew install tmux)
port 8767 already in use set port = <other> in config, or REJOIN_PORT=<other> ./run.sh
titles stuck at truncated first prompt OPENROUTER_API_KEY not found; see step 4 above
dashboard is empty you have no Claude/Codex/OpenCode/Pi sessions yet; run one and click ↻
Schema version mismatch on startup back up and delete ~/.local/share/rejoin/index.db, restart
TUI shows no transcript on first launch click any row; the first-paint transcript load races with the cursor init

Tests

.venv/bin/pytest -q
.venv/bin/ruff check rejoin tests

Credits

  • Visual identity inspired by Claude.ai — the warm Pampas/Crail beige palette is an homage to Anthropic's Styrene + Tiempos design system, implemented here with free Fraunces / DM Sans / Source Serif 4 / IBM Plex Mono.
  • OpenCode + Pi providers and running-process detection come from agent-sessions by Lars de Ridder (MIT).
  • Textual by Textualize powers the TUI.

License

MIT. See LICENSE.


Show HN / Reddit launch copy

Title: Show HN: rejoin — a unified session browser for Claude Code, Codex, OpenCode, and Pi

Body:

Every coding agent ships its own one-harness session picker (claude -r, codex resume, and so on). If you use more than one — and most of us do — you can't answer "which of my agents had that webhook debugging thread?" without grepping through JSONL by hand.

rejoin is the cross-harness view. It reads session files from all four agents locally, auto-titles them with a cheap OpenRouter model, and gives you one keyboard-first dashboard — web or TUI — to search, filter by project, pin favorites, and rejoin any of them in tmux with one keystroke.

  • Reads ~/.claude/projects, ~/.codex/sessions, OpenCode's SQLite, and Pi.
  • FTS5 search across prompts, titles, and Codex compaction summaries — regardless of which agent generated them.
  • Group-by-cwd shows every agent you've used in a given project side by side.
  • Pampas-beige web UI borrowed from Claude.ai's aesthetic; matching TUI.
  • pipx install rejoin

Python 3.11+, MIT. Runs locally — no auth, no cloud, never writes to your session files. https://github.com/akakabrian/rejoin

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rejoin-0.3.0.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rejoin-0.3.0-py3-none-any.whl (44.3 kB view details)

Uploaded Python 3

File details

Details for the file rejoin-0.3.0.tar.gz.

File metadata

  • Download URL: rejoin-0.3.0.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for rejoin-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e46ac4d925a1d36fc5926678407c0938b06dc54c403b492ebb3484fbc9c22ca5
MD5 3609a6c83a7e2243022bfd32729fc385
BLAKE2b-256 a7288241bc0f9d2bf9fc00136a74be8b7c89f31c631e306c5968f26a8d98c2ad

See more details on using hashes here.

File details

Details for the file rejoin-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: rejoin-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 44.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for rejoin-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5b060be2734adf050da58a2cf5bb8deeb92b5eae76b75d79ce4267b98a472df4
MD5 6837957b2e85567ed3a9f978c96bec6e
BLAKE2b-256 5cf48124511e70e56e06744828e3d07ee101e7dbd427a75018eba531131555e0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page