跳到主要内容
    ↑↓ 选择↵ 打开esc 关闭
    中文English
    luchev

    Session Surf

    v0.1.7界面与主题
    opencode-session-surf

    Session list sidebar plugin for opencode TUI with cross-instance status sync and fuzzy session switching

    GitHub 星标

    0

    月装机量

    959

    近 7 天 658

    综合评分SCORE

    38.3

    生态多维模型

    最近提交

    4 天前

    2026-08-16

    快速安装与配置

    opencode.json

    写入当前项目的 opencode.json,只对这个仓库生效。

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["opencode-session-surf@0.1.7"]
    }

    opencode 启动时会通过内嵌运行时自动加载 npm 依赖并缓存至本地目录,无需手动在全局环境执行安装。

    Session list sidebar plugin for opencode TUI. Shows all sessions from the local opencode database, with live status (busy/waiting) synced across opencode instances, plus quick session switching.

    Features

    • Sidebar session list — all sessions, grouped by recency, with project directory and relative update time
    • Cross-instance status sync — each opencode process broadcasts session statuses (busy/waiting) to a temp-dir status file; other instances pick them up so you can see when a session is active elsewhere
    • Ctrl+O fuzzy picker — fuzzy-search sessions by title or directory and switch instantly; close matches are ordered by recency, most recent on top. Arrow keys or ctrl+j/ctrl+k move the highlight (wrapping at both ends), which stays centred as the list scrolls; long names are truncated with
    • Rename / delete / fork from the picker — act on the highlighted session without leaving the popup (rename applies instantly)
    • Compact or comfortable rows — one line, or two lines per session, via the density option
    • Configurable look — spinner, waiting indicator, and marker glyph styles, or a whole preset; all keybinds are rebindable
    • Next/previous session commands for quick navigation

    Install

    From npm (published version):

    {
      "plugin": [
        "opencode-session-surf"
      ]
    }
    

    From the GitHub repo:

    {
      "plugin": [
        "https://github.com/luchev/opencode-session-surf"
      ]
    }
    

    From a local checkout:

    {
      "plugin": [
        "file:///path/to/opencode-session-surf/index.tsx"
      ]
    }
    

    Requires opencode with TUI plugin support and bun.

    Configuration

    Plugin options are set through the plugin array in tui.json (tuple form):

    {
      "plugin": [
        ["opencode-session-surf", { "spinner": "dots" }]
      ]
    }
    

    Options

    Option Values Default Description
    spinner dots, arc, sweep, fill, bounce, sparkle, block, battery, gauge, speed, none, "" dots Working spinner style; none renders a blank cell, "" hides it
    waiting emoji, ellipsis, question, pulse, block, bounce, eyeblink, bell, help, bulb, ghost, none, "" pulse Waiting-for-input indicator; none renders a blank cell, "" hides it
    marker dot, square, arrow, star, none, caret, ping, creation, sprout dot Active-session marker glyph
    preset ping, term, braille, hex, moon, pie Predefined look that overrides spinner/waiting/marker (see below)
    pollMs number (ms) 3000 Sidebar refresh interval; values below 1000 are ignored
    openElsewhere boolean false Show a dot on sessions open in another opencode instance
    density compact, comfortable comfortable Session manager picker row layout: compact puts name + age + dir on one line; comfortable uses two lines (name, then age left / dir right)
    subagents collapsed, tree tree Subagent display (see below)
    childTtlMs number (ms) 10000 How long a completed subagent row stays visible after going idle; values below 1000 are ignored
    debug boolean false Append diagnostics to $TMPDIR/opencode-session-surf-status/debug.log

    Some symbols (battery, gauge, speed, eyeblink, bell, help, bulb, ghost, creation, sprout, and the ping, hex, and moon presets) are Nerd Font glyphs and require a patched Nerd Font installed in your terminal — without one they render as boxes or nothing.

    With openElsewhere enabled, sessions running in another opencode instance show a marker; the active-session glyph stays visible even while its spinner is running. The sidebar is split into two sections, each collapsible on click via the / toggle:

    Subagent display is controlled by subagents:

    • tree (default) — each session also lists its subagents as indented rows with their own status spinner (waiting/working) and title; subagent rows have no time or directory and are clickable to open the subagent session. Busy state still folds up the tree, so a parent whose grandchild subagent is running shows busy too. Completed subagents drop off the list once they've been idle past the freshness window (childTtlMs, 10 s by default) — opencode never archives them, so without this the tree would fill up with finished children. When opencode retries a failed model call it spawns a duplicate subagent with the same title (e.g. the first attempt exhausts its quota and is retried on a fallback model); the failed attempt can keep a retry status for a while, so duplicates are deduped by creation order instead — the newest-created member of each same-title group is kept and the rest hidden, whether they're busy or idle.

    • collapsed — subagent sessions are hidden, but a session that spawned subagents stays busy while any of them (or their sub-subagents) is still running, so it never looks done mid-flight.

    • preset: "ping" — a predefined look that ignores the individual spinner/waiting/marker options: the current session shows (ping), waiting uses the bell style, working uses the arc spinner, and idle Active sessions show a dot. The marker and spinner share one cell, so the current glyph replaces the spinner instead of sitting next to it, and no column is reserved for each separately.

    • preset: "term" — pure ASCII, works in any terminal: > for the current session, ... while waiting, -\|/ while working. Also combined.

    • preset: "braille" marker, ⣾⣿ pulse while waiting, braille dots while working. Keeps the marker and spinner in separate cells.

    • preset: "hex" — hexagon theme: hexagon marker, hexagon/outline blink while waiting, hexagon slices filling and draining while working. Combined.

    • preset: "moon" — lunar theme: new-moon marker, full/new moon blink while waiting, the full 28-phase moon cycle (new → full → new) while working. Combined.

    • preset: "pie" — progress-pie theme: full-slice marker, slice/full blink while waiting, circle slices filling and draining while working. Combined.

    • Active — the session you're in, plus anything busy, waiting, or updated in the last 15 minutes. Idle rows in Active are green; no Active session renders white.

    • Recent — the last 24 hours of work, plus the previous block of work before it (so a quiet gap, like a weekend, doesn't hide the last real batch of sessions). Rows are white, as usual.

    Keybinds are configured through tui.json's keybinds map, keyed by command name (custom keybinds are additive to the defaults):

    Keybinds

    Action Default Command
    Open session picker ctrl+o session_surf.picker.open
    Next session ctrl+x j session_surf.next
    Previous session ctrl+x k session_surf.previous
    Move session into a directory ctrl+x w session_surf.chdir

    The picker itself has its own actions, also configurable through the same keybinds map and shown at the bottom of the popup:

    Picker action Default Command
    Switch to selected session enter session_surf.picker.switch
    Move selection up , ctrl+k session_surf.picker.up
    Move selection down , ctrl+j session_surf.picker.down
    Rename selected session ctrl+r session_surf.picker.rename
    Delete selected session ctrl+d session_surf.picker.delete
    Fork selected session ctrl+f session_surf.picker.fork
    Move selected session ctrl+w session_surf.picker.move
    New session ctrl+n session_surf.picker.new
    Close the picker esc session_surf.picker.close

    Selection wraps around both ends. Rename and delete keep the picker open (a confirmation prompt appears first, and rename shows the new title immediately); fork closes it and switches to the new session, which keeps the original name the first time and gets a numbered suffix afterwards (name (fork 2), name (fork 3), …); move closes it and prompts for a directory, exactly like ctrl+x w. Plain letter keys always stay free for search — only the control/arrow/enter/esc bindings above are captured while the picker is open.

    ctrl+x w moves the current session into a directory you type or tab-complete: the conversation is forked into a new session whose working directory is the one you chose, the new session keeps the original name, and the original session is deleted. The server's fork API copies the conversation but cannot set a directory (fork/update/import all ignore it), so after forking the plugin writes the chosen directory straight into the new session's row in ~/.local/share/opencode/opencode.db — the server re-reads it from the database on every request.

    {
      "keybinds": {
        "session_surf.next": "ctrl+]",
        "session_surf.previous": "ctrl+["
      }
    }
    

    Set a command to "none" to disable its keybind.

    Rebind the leader key

    The leader is opencode's own setting, not this plugin's. Change it in tui.json:

    {
      "keybinds": {
        "leader": "ctrl+space"
      }
    }
    

    Development

    bun install
    bun run dev     # build watch → dist/index.js
    bun run build   # one-shot build
    bun run test    # unit + render tests (uses the @opentui/solid preload)
    

    To test the plugin locally, point tui.json's plugin array at the local checkout and restart opencode.

    The plugin reads session data from ~/.local/share/opencode/opencode.db (via bun:sqlite).

    License

    MIT