Skip to content

leeovery/portal

Repository files navigation

Portal

Interactive session picker for tmux

A CLI that gives you fast, fuzzy session management from bare shell,
with project memory, path aliases, and a keyboard-driven TUI.

License: MIT Go

Install · Quick Start · Commands · Shell Integration · Configuration


Portal is a CLI that runs at bare shell (before entering tmux) and provides an interactive TUI for picking, creating, and managing tmux sessions. It remembers your projects, resolves paths via aliases and zoxide, auto-detects git roots for new sessions, and automatically starts the tmux server and restores your saved sessions after a reboot.

After shell integration, you interact with Portal through two functions: x (session picker / opener) and xctl (subcommands like list, kill, alias). The function names are customizable — see --cmd below.

Install

Requirements

  • tmux ≥ 3.0 (released Feb 2020) — Portal uses array-indexed global hooks (set-hook -ga) which require 3.0+. Earlier versions are not supported; Portal exits with Portal requires tmux ≥ 3.0 (found <version>). Please upgrade.
  • Go (build from source), macOS or Linux.

macOS

brew install leeovery/tools/portal

Linux

curl -fsSL https://raw.githubusercontent.com/leeovery/portal/main/scripts/install.sh | bash

Go

go install github.com/leeovery/portal@latest

Quick Start

# Add shell integration (creates x() and xctl() functions)
echo 'eval "$(portal init zsh)"' >> ~/.zshrc

# Launch the interactive picker
x

# Open a session at a path
x ~/Code/myproject

# Open with a command
x ~/Code/myproject -e "make dev"

# Set up an alias
xctl alias set work ~/Code/work-project
x work

# List running sessions
xctl list

# Kill a session
xctl kill myproject

Shell Integration

Portal generates shell functions via portal init. Add to your shell profile:

# zsh
eval "$(portal init zsh)"

# bash
eval "$(portal init bash)"

# fish
portal init fish | source

This creates two functions:

  • x() — launches Portal (interactive picker or path-based session creation)
  • xctl() — direct access to Portal subcommands (list, kill, alias, etc.)

Customize the function name with --cmd:

eval "$(portal init zsh --cmd p)"   # creates p() and pctl()

Commands

Examples below use the default x / xctl function names. If you used --cmd p, substitute p and pctl. You can also call the portal binary directly.

x (open)

Interactive session picker or path-based session creation. x maps to portal open.

x                                    # interactive TUI
x ~/Code/myproject                   # open session at path
x myalias                            # resolve alias → path → session
x ~/Code/app -e "make dev"           # run command in new session
x ~/Code/app -- npm start            # alternative command syntax
Flag Description
-e, --exec Command to execute in the new session

Path resolution order: aliases → zoxide → TUI with filter.

New sessions auto-resolve to the git repository root when applicable.

xctl attach

Attach to an existing tmux session by name.

xctl attach myproject

xctl list

List running tmux sessions.

xctl list                            # auto-detect format
xctl list --long                     # full details
xctl list --short                    # names only
Flag Description
--long Full session details (name, status, window count)
--short Session names only, one per line

xctl kill

Kill a tmux session by name.

xctl kill myproject

xctl alias

Manage path aliases for quick session access.

xctl alias set work ~/Code/work      # create alias
xctl alias rm work                   # remove alias
xctl alias list                      # list all aliases

xctl hooks

Register per-pane commands that re-execute automatically when a session is attached after a reboot. Must be run from inside a tmux pane.

xctl hooks set --on-resume "npm start"    # register a resume hook
xctl hooks rm --on-resume                 # remove the hook
xctl hooks list                           # list all hooks

When hooks fire: Portal fires resume hooks ONLY when a pane is freshly recreated from saved state on reboot recovery — i.e., the tmux server has just been started fresh and Portal has restored sessions. Hooks do NOT fire on every detach / reattach within a single server lifetime. If a pane still exists, its hook process either already ran or was explicitly killed; firing again would double-launch long-running processes like claude --resume. This is deliberate.

xctl clean

Remove stale projects whose directories no longer exist on disk, and prune hooks for panes that no longer exist.

xctl clean

xctl state

Inspect or tear down Portal's saved-session state used for reboot restoration.

xctl state status                    # daemon + state health
xctl state cleanup                   # remove hooks + stop daemon
xctl state cleanup --purge           # also wipe ~/.config/portal/state/
  • xctl state status — print daemon status, last save time, captured counts, state size, and recent warnings. Exits non-zero when the daemon is down, last save is stale, or warnings are present in the last hour.
  • xctl state cleanup [--purge] — kills the daemon and removes Portal's tmux hook entries. With --purge, also removes ~/.config/portal/state/.

xctl version

Print the Portal version.

xctl version

portal init

Output shell integration script for eval. See Shell Integration. This is the one command you call via the portal binary directly.

portal init zsh
portal init bash --cmd p

TUI Keybindings

Key Action
/k Move up
/j Move down
Enter Select session / confirm
/ Filter mode (fuzzy search)
R Rename session
K Kill session
q/Esc Quit

The TUI has three views: session list, project picker, and file browser.

Automatic Server Bootstrap & Restoration

Portal automatically starts the tmux server if absent AND restores saved sessions in the same bootstrap step. After a reboot, your sessions return with structure, layout, zoom, working directories, and scrollback (including ANSI colour). On any tmux-needing command, Portal checks the server, starts it if missing, and re-creates saved sessions that aren't already live. Scrollback injects lazily when you attach. Resume hooks fire on freshly-recreated panes. The TUI shows a "Restoring sessions…" loading screen for at most ~1.2s; the CLI is silent.

Replaces tmux-continuum/tmux-resurrect for session persistence — uninstall those plugins if you have them (or set @continuum-restore off for tmux-resurrect/continuum) to avoid duplicate restoration.

Pair this with resume hooks to automatically re-run pane commands (dev servers, editors, etc.) after a reboot.

Configuration

Portal resolves its config directory using XDG: $XDG_CONFIG_HOME/portal/ if set, otherwise ~/.config/portal/. Each file also has a per-file env var override that takes full precedence.

File Purpose Env override
aliases Path aliases (key=value, one per line) PORTAL_ALIASES_FILE
projects.json Remembered project directories PORTAL_PROJECTS_FILE
hooks.json Per-pane resume hooks (pane → event → command) PORTAL_HOOKS_FILE
state/ Saved session structure + scrollback for automatic restoration on reboot. Contains: sessions.json (structure index), scrollback/*.bin (per-pane content), daemon.pid + daemon.version (liveness markers), portal.log (diagnostics). See Privacy Considerations. PORTAL_STATE_DIR

Projects are auto-populated when you create new sessions and cleaned with xctl clean.

Privacy Considerations

Portal persists pane scrollback to ~/.config/portal/state/ (override via PORTAL_STATE_DIR) so it can rehydrate sessions after a reboot. Files are written mode 0600, directories 0700.

  • Same local-filesystem trust model as your shell history — anything visible in your terminal can end up in the saved state.
  • No encryption at rest. If a pane displays secrets (tokens, credentials, diffs of sensitive files), they will be captured.
  • Mitigations: for sensitive panes, run tmux set-option -w history-limit 0 to prevent scrollback from accumulating, or tmux clear-history on demand (run before the next save, which lands at most ~30s later).
  • v1 has no per-session opt-out; tmux-native workarounds above are the supported path.

Uninstall

Two paths depending on whether you want to keep your saved state:

  • Just remove the binarybrew uninstall portal or rm $(which portal). The defensive command -v portal guard in the registered tmux hooks short-circuits when the binary is gone, so tmux keeps running normally. Your saved state is preserved; reinstalling Portal picks up where it left off.
  • Explicit teardown — run portal state cleanup (kills the daemon and removes Portal's tmux hook entries), or portal state cleanup --purge to also wipe saved state under ~/.config/portal/state/. Then uninstall the binary. Use --purge for a completely clean slate. Non-state config (hooks.json, projects.json, aliases) is preserved either way — remove manually if desired.

License

MIT

About

Interactive Tmux session manager - Go CLI that gives you fast, fuzzy session management from bare shell, with project memory, path aliases, and a keyboard-driven TUI.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors