opencode-sesh 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -11,24 +11,25 @@ territory: read this first, then the specific file you need.
11
11
  any terminal. Lists **all** sessions across **all** project directories,
12
12
  full-text searchable, with transcript preview and resume/fork/delete.
13
13
  2. **`tui-plugins/sesh-panel/tui.tsx`** — a SolidJS/OpenTUI plugin for the opencode TUI: a
14
- sessions section in the sidebar plus an `option+o` / `/sesh` picker.
14
+ sessions section in the sidebar and on the home screen, plus an `option+o` / `/sesh` picker.
15
15
  3. **`plugins/sesh-list.ts`** — the `sesh_list` custom tool, so the agent
16
16
  can list sessions across every directory by querying the global opencode
17
17
  SQLite store (the native `opencode session list` is project-scoped).
18
18
 
19
19
  No iTerm2, no AppleScript, no panes, no window management — **never add any.**
20
- The one exception is the TUI's right-click action, which shells out to
20
+ The one exception is the TUI's right-click workspace action, which shells out to
21
21
  `cmux new-workspace` when running inside cmux.
22
- opencode has no persistent-sidebar API, so the "sidebar" is a `sidebar_content`
23
- slot section plus an xlarge modal picker; the terminal UI is fullscreen fzf.
22
+ opencode has no persistent-sidebar API, so the TUI uses `sidebar_content` and
23
+ `home_bottom` slot sections plus an xlarge modal picker; the terminal UI is fullscreen fzf.
24
24
 
25
25
  ## Start here
26
26
 
27
27
  ```bash
28
28
  bun install # dev deps (TypeScript, opencode/OpenTUI types)
29
- bun run test # fixture-DB regression suite — must stay green
29
+ bun run build # compile the panel to dist/tui.js
30
+ bun run test # build + fixture-DB regression suite — must stay green
30
31
  bun run test:changelog # release-tooling regressions (scripts/changelog.sh)
31
- bun run test:picker # PTY picker suite (needs fzf >= 0.73)
32
+ bun run test:picker # PTY picker suite
32
33
  bun run typecheck # tsc over tui-plugins/ and plugins/
33
34
  bun run lint:sh # bash -n on every script
34
35
  HOME=/tmp/fakehome bash install.sh # installer smoke test (never touches ~/.config)
@@ -39,7 +40,8 @@ HOME=/tmp/fakehome bash install.sh # installer smoke test (never touches ~/.co
39
40
  - `bin/` — flat; no subdirs. Scripts resolve siblings from their own location.
40
41
  Never hardcode install paths. `sesh-waiting.sh` owns the needs-input query
41
42
  (shared heuristic with the TUI sidebar copy, marked `NEEDS_INPUT_SQL` in
42
- both); `sesh-prune.sh` deletes stale sessions; `sesh-costs.sh` sums
43
+ both); `sesh-pins.sh` manages persistent session/directory pins;
44
+ `sesh-prune.sh` deletes stale sessions; `sesh-costs.sh` sums
43
45
  assistant-message cost per directory. Reads go through `sqlite3 -readonly`
44
46
  (required; `opencode db` no longer exists). Sesh never writes the database
45
47
  directly; deletes go through `opencode session delete`.
@@ -47,9 +49,16 @@ HOME=/tmp/fakehome bash install.sh # installer smoke test (never touches ~/.co
47
49
  window management to sesh itself.
48
50
  - `tui-plugins/sesh-panel/tui.tsx` — SolidJS panel, `/** @jsxImportSource @opentui/solid */`.
49
51
  - `plugins/sesh-list.ts` — the agent-facing `sesh_list` tool.
52
+ - `scripts/build-tui.mjs` — compiles the panel with Babel + `babel-preset-solid`
53
+ (`moduleName: "@opentui/solid"`, universal) to `dist/tui.js` (gitignored).
54
+ `package.json` exports `./tui` from `dist/tui.js`; `bun run build`, `bun run
55
+ test`, `prepack` and CI all run it. OpenCode applies its Solid transform only
56
+ to sources outside `node_modules`, so shipping raw `tui.tsx` in the npm package
57
+ renders a non-reactive (empty) panel — never export the `.tsx` source.
50
58
  - `scripts/changelog.sh` — release tooling: drafts conventional commits,
51
59
  writes a dated version, prints release notes and
52
60
  checks the compare links. `tests/changelog.sh` covers it in a throwaway repo.
61
+ - `assets/` — README logo (`logo.svg`) and square mark (`icon.svg`); wordmark is outlined paths, no font dependency.
53
62
  - `themes/`, `tests/`, `install.sh`, `README.md`.
54
63
 
55
64
  The tool name in opencode is `sesh_list`. Do not rename it without updating the docs.
@@ -58,13 +67,13 @@ The tool name in opencode is `sesh_list`. Do not rename it without updating the
58
67
 
59
68
  `bin/sesh-list.sh` owns the fzf picker's session store access (opencode SQLite
60
69
  DB: `session_v2` / `session_message` tables); the `sesh_list` agent tool queries
61
- the same store read-only through `bun:sqlite`. V2 discovers the tool from the
70
+ the same store read-only through `bun:sqlite`. OpenCode discovers the tool from the
62
71
  global config directory's `plugins/`. Keep its SQL column names in sync with
63
72
  the schema below. Keystrokes never touch the database:
64
73
 
65
74
  - `--refresh` (expensive): bulk-reads session metadata and per-session text
66
75
  parts, validates the persistent extraction cache
67
- (`$SESH_CACHE_DIR/extractions/<ses_*.json>`, schema 3) against `time_updated`,
76
+ (`$SESH_CACHE_DIR/extractions/<ses_*.json>`) against `time_updated`,
68
77
  message count and the latest message timestamp, re-extracts only changed sessions,
69
78
  prunes vanished ones, and atomically publishes `STATE_DIR/snapshot.jsonl`.
70
79
  Extraction reads user `data.text` and assistant `data.content` text entries,
@@ -91,8 +100,10 @@ the schema below. Keystrokes never touch the database:
91
100
  `ctrl+d` itself (composer delete-char, session delete), so ctrl pins would
92
101
  shadow the composer. Pinned directories sort before groups
93
102
  containing pinned sessions. The terminal picker puts pinned sessions first
94
- inside each group; the TUI picker and sidebar keep sessions recency-ordered
95
- within each directory. The home list puts pinned sessions first.
103
+ inside each group; the TUI picker ranks pinned sessions first before filtering
104
+ and keeps sessions within each directory ordered by recency; the sidebar keeps
105
+ sessions within each directory ordered by recency; the home list puts pinned
106
+ sessions first.
96
107
 
97
108
  **Output contract** (TSV, 6 fields):
98
109
  `agentId sessionId liveState display cwd trackingId`. `display` is the only
@@ -100,8 +111,8 @@ rendered column; `trackingId` is fzf's `--id-nth`. Headers/notices carry an
100
111
  empty `sessionId` and are a selection no-op — keep that guard.
101
112
 
102
113
  One worker (`sesh-refresh-worker.sh`, per picker, TERMed on exit) serializes
103
- scans; fzf's `start`/`every(3)`/`change` bindings render only. Selection requests
104
- a fresh poll (`request`/`wait`, ≤12 s) before dispatch — skipped for `--print`,
114
+ scans; fzf's `start`/`every(...)`/`change` bindings render only. Selection requests
115
+ a fresh poll (`request`/`wait`) before dispatch — skipped for `--print`,
105
116
  which is a pure snapshot lookup — and prints a progress line to stderr while it
106
117
  waits, because fzf has already exited and the terminal is idle. opencode has no
107
118
  live-agent API, so `liveState` is always `unknown` and rows render gray unless
@@ -125,14 +136,14 @@ when opencode exits, the picker reopens with the query preserved.
125
136
  `sqlite3 -json` in `-R` mode; it pretty-prints — parse as JSON.
126
137
  - `jq -R` output stays JSON-quoted: use `-Rr` when a shell variable needs the
127
138
  raw string.
128
- - fzf: stock only, `>= 0.73` (`transform:`, `every():`). `{q}` in binds is
139
+ - fzf: stock only (`transform:`, `every():`). `{q}` in binds is
129
140
  shell-quoted by fzf — don't add quoting. Ctrl-G is bind-only (stays open);
130
141
  Ctrl-F is `--expect` (accepts with fork). A `transform-header` inside a chained
131
142
  action must come **before** `reload`/`reload-sync`: placed after, fzf silently
132
- publishes an empty list (verified on 0.74.3).
143
+ publishes an empty list.
133
144
  - Destructive actions confirm. `sesh-delete.sh` prompts on a TTY and refuses
134
145
  without `--yes` when stdin is not one; the TUI arms on the first Option-D and
135
- commits on the second (or `y`).
146
+ commits on the second, `y`, or Enter.
136
147
  - The installer prefers the local opencode version for `@opencode/*` and
137
148
  merges (never clobbers) the config `package.json`; opencode runs `bun install`
138
149
  at startup.
@@ -147,13 +158,18 @@ when opencode exits, the picker reopens with the query preserved.
147
158
 
148
159
  ## TUI panel (`tui-plugins/sesh-panel/tui.tsx`)
149
160
 
150
- - **Sidebar:** `api.slots.register` on `sidebar_content` (prepend mode — native
151
- sidebar content stays). Pinned directories sort first, then directory groups
161
+ - **Sidebar & Home list:** `api.slots.register` on `sidebar_content` (`sidebar.content`
162
+ prepend mode — native sidebar content stays) and `home_bottom` (`home.footer`
163
+ prepend mode). In the sidebar, pinned directories sort first, then directory groups
152
164
  by recency; sessions within each group sort by last update regardless of pins.
165
+ In the home list, pinned sessions sort first.
153
166
  Row markers alone signal session state: a running agent animates a loading
154
167
  spinner marker, the open session shows its own ●, and every other idle row
155
- stays neutral (polled every 15 s); Option-P previews
156
- a row and option+d deletes one (armed, then confirmed). The section lists every
168
+ stays neutral (polled periodically); Option-P previews
169
+ a row and option+d deletes one (armed, then confirmed). Right-clicking a
170
+ sidebar row opens an action context menu (open, preview, cmux workspace when
171
+ inside cmux, fork, pin/unpin, delete); right-clicking a home or picker row
172
+ opens the session in a new cmux workspace. The sidebar section lists every
157
173
  session without a row cap and flex-grows to fill the sidebar, with overflow
158
174
  scrolling inside; the picker remains the place for transcript search.
159
175
  Sessions waiting on the user (unanswered questions, stuck runs) pin a
@@ -167,7 +183,7 @@ when opencode exits, the picker reopens with the query preserved.
167
183
  `api.client.session.fork`; Ctrl-G scopes the list to the selected session's
168
184
  project.
169
185
  - **Picker search:** pages the global session list (`SESSION_PAGE_LIMIT`, capped
170
- at `SESSION_MAX` and reported when truncated) instead of a fixed 500-row
186
+ at `SESSION_MAX` and reported when truncated) rather than a fixed-size
171
187
  window, then indexes transcript text for **every** session in batches
172
188
  (`TRANSCRIPT_BATCH`) with bounded remote concurrency (`REMOTE_CONCURRENCY`).
173
189
  The header shows indexing progress/coverage. Visible transcript hits get
@@ -177,7 +193,7 @@ when opencode exits, the picker reopens with the query preserved.
177
193
  `/sesh-needs` uses the shared needs-input set plus the oldest unresolved
178
194
  question or running-tool timestamp, lists longest-waiting first, and `n`
179
195
  opens the next waiting session. `tests/tui.mjs` locks the data layer.
180
- - **Durable install:** V2 reloads plugins on watched configuration changes;
196
+ - **Durable install:** OpenCode reloads plugins on watched configuration changes;
181
197
  unwatched dependencies may need a restart. If a panel update does not appear,
182
198
  fully quit/reopen opencode. `install.sh --sync-panel`
183
199
  (run by the `postinstall`) refreshes an already-installed panel and is a
@@ -222,8 +238,8 @@ when opencode exits, the picker reopens with the query preserved.
222
238
  (`tests/tui.mjs` asserts the cap stays out); overflow scrolls inside the
223
239
  stretched section.
224
240
  - **Delete in the TUI is two-step.** The first `option+d` arms `pendingDelete`,
225
- the second (or `y`, or Enter in the picker footer) commits the armed row
226
- wherever the pointer or cursor is, and `n`/Esc (or a five-second timeout)
241
+ the second (or `y`, or Enter) commits the armed row
242
+ wherever the pointer or cursor is, and `n`/Esc (or a timeout)
227
243
  cancels. Moving the pointer must never disarm: edge-hover flicker ate the
228
244
  confirm before `confirmArmed` landed. The `y`/`n` layer is registered *only
229
245
  while armed*, so it can never shadow the search box's typing.
@@ -240,9 +256,12 @@ when opencode exits, the picker reopens with the query preserved.
240
256
  - `bun run test:changelog` — release-tooling regressions; builds its own git
241
257
  history in a temp dir (included in `bun run test`).
242
258
  - `bun run test:picker` — PTY suite driving the real fzf picker (needs fzf
243
- `>= 0.73` and Python 3).
259
+ and Python 3).
244
260
  - `bun run typecheck` — `tsc` over `tui-plugins/` and `plugins/`.
245
261
  - `bun run lint:sh` — `bash -n` on every script.
246
262
  - `HOME=/tmp/fakehome bash install.sh` — installer smoke test.
247
263
  - Keep `tui-plugins/sesh-panel/tui.tsx` and any installed copy byte-identical when testing
248
264
  the panel locally.
265
+ - The npm install path needs the plugin in both `opencode.json` (server tool) and
266
+ `cli.json` (TUI panel); `plugin.list` shows only server plugins, so verify the
267
+ panel by launching the TUI against a fixture or the real store.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.3] - 2026-10-17
9
+
10
+ ### Fixed
11
+
12
+ - **tui:** Support ctrl-n and ctrl-p in the session picker (#11)
13
+
8
14
  ## [2.0.2] - 2026-10-10
9
15
 
10
16
  ### Fixed
@@ -494,6 +500,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
494
500
  - Fixture-database regression suite, TypeScript typechecking, and ShellCheck in
495
501
  CI.
496
502
 
503
+ [2.0.3]: https://github.com/ozandogrultan/opencode-sesh/compare/v2.0.2...v2.0.3
497
504
  [2.0.2]: https://github.com/ozandogrultan/opencode-sesh/compare/v2.0.1...v2.0.2
498
505
  [2.0.1]: https://github.com/ozandogrultan/opencode-sesh/compare/v2.0.0...v2.0.1
499
506
  [2.0.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v1.1.0...v2.0.0
package/README.md CHANGED
@@ -1,21 +1,23 @@
1
- # sesh
2
-
3
- [![CI](https://github.com/ozandogrultan/opencode-sesh/actions/workflows/ci.yml/badge.svg)](https://github.com/ozandogrultan/opencode-sesh/actions/workflows/ci.yml)
4
-
5
- **An [OpenCode V2](https://opencode.ai/v2/) plugin and terminal session browser.** Search every session
1
+ <h1 align="center">
2
+ <img src="assets/logo.svg" alt="sesh" height="96">
3
+ </h1>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/ozandogrultan/opencode-sesh/actions/workflows/ci.yml"><img src="https://github.com/ozandogrultan/opencode-sesh/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
7
+ <a href="https://www.npmjs.com/package/opencode-sesh"><img src="https://img.shields.io/npm/v/opencode-sesh" alt="npm"></a>
8
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
9
+ <a href="#contributing"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome"></a>
10
+ <a href="https://github.com/ozandogrultan/opencode-sesh/stargazers"><img src="https://img.shields.io/github/stars/ozandogrultan/opencode-sesh?style=social" alt="GitHub stars"></a>
11
+ </p>
12
+
13
+ **An [OpenCode](https://opencode.ai) plugin and terminal session browser.** Search every session
6
14
  across every project, preview the transcript, and resume in place — from your
7
15
  terminal or from inside the TUI.
8
16
 
9
- The TUI plugin and agent tool require OpenCode V2 and are not compatible with V1.
10
-
11
17
  `opencode` keeps a growing pile of sessions, and its native lists (`<leader>l`,
12
18
  `/sessions`) are scoped to the current project. `sesh` shows all of them —
13
19
  grouped by directory, full-text searchable, and previewable from anywhere.
14
20
 
15
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
16
- [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing)
17
- [![GitHub stars](https://img.shields.io/github/stars/ozandogrultan/opencode-sesh?style=social)](https://github.com/ozandogrultan/opencode-sesh/stargazers)
18
-
19
21
  ```text
20
22
  Sessions (8) ▾ hide
21
23
  search…
@@ -38,9 +40,10 @@ search…
38
40
  subagent child sessions are hidden by default.
39
41
  - **Pin what matters.** Ctrl-S pins a session and Ctrl-D pins its directory in
40
42
  either picker; the TUI sidebar and home list use Option-S and Option-R.
41
- Pins are shared and persist across restarts. Pinned directories rise first;
42
- pinned sessions sort first in the terminal picker and home list. The TUI picker
43
- and sidebar keep sessions within each directory recency-ordered.
43
+ Pins are shared and persist across restarts. Pinned directories rise first
44
+ (followed by directories containing pinned sessions in the TUI picker);
45
+ pinned sessions sort first in the terminal picker and home list, while the
46
+ TUI picker and sidebar keep sessions within each directory ordered by recency.
44
47
  - **Full-text search.** Type to match against session titles *and* the text of
45
48
  the conversation itself. Reasoning and tool output are excluded, so the index
46
49
  stays clean.
@@ -59,7 +62,7 @@ search…
59
62
 
60
63
  ### OpenCode plugin
61
64
 
62
- Add the package to `plugins` in your OpenCode V2 `opencode.json` (server tool)
65
+ Add the package to `plugins` in your `opencode.json` (server tool)
63
66
  and `cli.json` (TUI panel):
64
67
 
65
68
  ```json
@@ -71,7 +74,7 @@ and `cli.json` (TUI panel):
71
74
  OpenCode installs the package and loads its server tool and TUI panel. The
72
75
  published panel is precompiled (`dist/tui.js`) because OpenCode does not apply
73
76
  its Solid transform to sources under `node_modules`. Merge this entry with your
74
- existing plugins. Remove the copied
77
+ existing plugins. Remove any copied
75
78
  `./tui-plugins/sesh-panel` registration and `plugins/sesh-list.ts` tool before
76
79
  switching to avoid loading them twice.
77
80
 
@@ -95,15 +98,15 @@ bash install.sh
95
98
  `sesh install` (or `bash install.sh`) links `sesh` into `$XDG_BIN_HOME`
96
99
  (default `~/.local/bin`), copies
97
100
  the `sesh_list` tool and the TUI panel into
98
- `${XDG_CONFIG_HOME:-~/.config}/opencode`, registers the panel in `opencode.json`, and
101
+ `${XDG_CONFIG_HOME:-~/.config}/opencode`, registers `./tui-plugins/sesh-panel` in `opencode.json`, and
99
102
  declares the plugin dependencies (opencode installs them on next start). The
100
- panel registers the `/sesh` slash command. It never edits your shell rc, and any
101
- file it would overwrite is backed up first.
103
+ panel registers the `/sesh` slash command. It never edits your shell rc or
104
+ overwrites a foreign `sesh` symlink.
102
105
 
103
106
  The npm package ships the same scripts and installer; the runtime tools below
104
107
  are still required.
105
108
 
106
- V2 reloads plugins when watched configuration files change. If a panel update
109
+ OpenCode reloads plugins when watched configuration files change. If a panel update
107
110
  does not appear, fully quit opencode and reopen it. Upgrades
108
111
  refresh an already-installed panel automatically (the npm `postinstall` syncs
109
112
  it), and `sesh --check` reports when the installed panel is out of date.
@@ -122,7 +125,7 @@ option+o # inside opencode — picker
122
125
  ### Requirements
123
126
 
124
127
  - [opencode](https://opencode.ai), used at least once
125
- - `bash`, `jq`, and [`fzf`](https://github.com/junegunn/fzf) `>= 0.73`
128
+ - `bash`, `jq`, and [`fzf`](https://github.com/junegunn/fzf) (modern stock build; run `sesh --check` to verify)
126
129
  - `sqlite3` — the picker queries the session DB directly
127
130
  - [`glow`](https://github.com/charmbracelet/glow) optional — styles the
128
131
  transcript preview as Markdown; without it the preview shows the plain
@@ -168,11 +171,8 @@ confirms first and a non-TTY run needs `--yes`.
168
171
 
169
172
  `sesh costs [--days N] [--json]` sums assistant-message cost per project, with a
170
173
  recent window beside the lifetime total, so a day's work reads as a
171
- per-project record. Legacy V1 history is included when available; sessions
172
- already present in V2 are counted only once.
173
-
174
- Session browsing uses the V2 store. Sessions remaining only in the legacy V1
175
- tables are not listed or resumable through sesh.
174
+ per-project record. Earlier history tables are included when present in the
175
+ database, and sessions are counted only once.
176
176
 
177
177
  When searching, title matches outrank transcript-only matches and the current
178
178
  project rises, so the list answers "where was that thing I worked on" before it
@@ -198,7 +198,8 @@ non-interactive `sesh-delete.sh` refuses unless given `--yes`.
198
198
  | `/sesh-needs` | Sessions waiting on you (Enter opens) |
199
199
  | `option+o` | Open the full picker (also in the command palette) |
200
200
  | Type | Search titles, directories and transcript text |
201
- | `↑`/`↓`, `PgUp`/`PgDn`, `Home`/`End` | Move the selection |
201
+ | `↑`/`↓` or Ctrl-P/Ctrl-N | Move the selection up/down |
202
+ | `PgUp`/`PgDn`, `Home`/`End` | Move by page or jump to the first/last session |
202
203
  | Option-P | Toggle the transcript preview |
203
204
  | Enter | Open the selected session |
204
205
  | Option-D | Arm deletion; press Option-D again, `y`, or Enter to confirm |
@@ -214,7 +215,8 @@ Option-O opens the picker globally; the other keys above apply inside it.
214
215
  The picker groups sessions under collapsible directory headings (click ▾/▸).
215
216
  Mouse-wheel scrolling moves through the tree without changing the selection
216
217
  under a stationary pointer. Option-P opens a full-size transcript preview over
217
- the picker; use ↑/↓ to preview other sessions, then Esc to return to the list.
218
+ the picker; use ↑/↓ or Ctrl-P/Ctrl-N to preview other sessions, then Esc to
219
+ return to the list.
218
220
  Right-click a sidebar session to open its action menu: open, preview, fork,
219
221
  pin/unpin, or delete with confirmation. Inside cmux, the menu also offers opening
220
222
  the session in a new workspace at its directory. Use ↑/↓ and Enter to choose an
@@ -228,7 +230,7 @@ Click the sidebar's `search…` box to filter sessions in place; click
228
230
  elsewhere or press Esc to leave search. Click the **Sessions** heading to drive
229
231
  the list from the keyboard instead of the mouse (`↑`/`↓` to move, Enter to open,
230
232
  Option-P to preview, Option-S to pin a session, Option-R to pin its directory,
231
- Option-D to arm deletion, Esc to leave). Press Option-D again or `y` to confirm
233
+ Option-D to arm deletion, Esc to leave). Press Option-D again, `y`, or Enter to confirm
232
234
  an armed deletion; `n` or Esc cancels. Pinned
233
235
  directories rise first, while sessions within each directory are ordered by
234
236
  last update, newest first. The section lists every session and scrolls to fill
@@ -248,14 +250,15 @@ replace the picker's Ctrl-S/Ctrl-D bindings; Option-S in the picker filters pins
248
250
 
249
251
  ### Transcript search and needs-input triage
250
252
 
251
- The full picker pages through your whole global session list (no fixed window)
253
+ The full picker pages through your global session list
252
254
  and indexes transcript text for every session in the background, showing
253
- indexing progress under the search box. It only stops at a safety cap of 5,000
254
- sessions, and says so when it does. Transcript matches show a short excerpt
255
+ indexing progress under the search box (and noting if the safety cap is reached).
256
+ Transcript matches show a short excerpt
255
257
  under the result; the Project, Needs input, and Pinned filters combine with
256
258
  search. The picker remembers your query and filters until you restart opencode.
257
259
  The Needs input dialog (`/sesh-needs`) shows why and how long each session has
258
- been waiting, oldest first. Press `n` there to open the next waiting session,
260
+ been waiting, oldest first. Use ↑/↓ or Ctrl-P/Ctrl-N to navigate and Enter to
261
+ open the selected session. Press `n` there to open the next waiting session,
259
262
  or reopen it after answering one to continue through the list.
260
263
 
261
264
  ## Configuration
@@ -281,8 +284,8 @@ workspace, and a global "resume from anywhere" hotkey — all caller-side.
281
284
 
282
285
  ## How it works
283
286
 
284
- The `bin/` readers use the V2 opencode session
285
- store (SQLite: `session_v2` / `session_message`). Each refresh extracts session
287
+ The `bin/` readers query the opencode session
288
+ store in SQLite (`session_v2` / `session_message`). Each refresh extracts session
286
289
  metadata and text-only parts into a persistent per-session cache keyed on
287
290
  `time_updated`, message count and the latest message timestamp, then atomically
288
291
  publishes a `snapshot.jsonl`. Every keystroke re-renders that snapshot with `jq`
@@ -290,13 +293,14 @@ alone, so typing never starts competing database scans. Reasoning and tool
290
293
  payloads are never indexed or previewed, and session ids are validated before
291
294
  any SQL interpolation.
292
295
 
293
- Flat scripts back the terminal UI — the picker (`sesh.sh`), the refresh engine
294
- (`sesh-list.sh`, driven by a per-picker `sesh-refresh-worker.sh` that serializes
295
- scans), preview renderer (`sesh-preview.sh`), deleter (`sesh-delete.sh`) and
296
- shortcut help (`sesh-shortcuts.sh`). The TUI panel (`tui-plugins/sesh-panel/tui.tsx`) is a
297
- SolidJS OpenTUI plugin that talks to the opencode SDK over the same store; the
298
- `sesh_list` agent tool reads it read-only through `bun:sqlite`. V2 discovers
299
- the installed tool automatically in the global config directory's `plugins/`.
296
+ Flat scripts in `bin/` back the terminal UI and CLI subcommands — the picker,
297
+ serialized refresh worker, preview renderer, pin store, deleter, pruner,
298
+ waiting-session check, cost digest, and shortcut help. The TUI panel
299
+ (`tui-plugins/sesh-panel/tui.tsx`) is a SolidJS OpenTUI plugin that talks to the
300
+ opencode SDK over the same store; the npm package ships it precompiled as
301
+ `dist/tui.js`. The `sesh_list` agent tool reads the store read-only through
302
+ `bun:sqlite` and is discovered automatically from the global config directory's
303
+ `plugins/`.
300
304
 
301
305
  ## FAQ
302
306
 
@@ -326,8 +330,8 @@ checks before opening a PR:
326
330
 
327
331
  ```bash
328
332
  bun install
329
- bun run test # fixture-database regression suite
330
- bun run test:picker # PTY picker suite (needs fzf >= 0.73)
333
+ bun run test # builds dist/tui.js, then the fixture-database regression suite
334
+ bun run test:picker # PTY picker suite
331
335
  bun run typecheck # tsc over tui-plugins/ and plugins/
332
336
  bun run lint:sh # bash -n on every script
333
337
  ```
package/dist/tui.js CHANGED
@@ -2987,11 +2987,21 @@ const tui = async api => {
2987
2987
  desc: "Previous session",
2988
2988
  preventDefault: true,
2989
2989
  cmd: () => moveCursor(-1)
2990
+ }, {
2991
+ key: "ctrl+p",
2992
+ desc: "Previous session",
2993
+ preventDefault: true,
2994
+ cmd: () => moveCursor(-1)
2990
2995
  }, {
2991
2996
  key: "down",
2992
2997
  desc: "Next session",
2993
2998
  preventDefault: true,
2994
2999
  cmd: () => moveCursor(1)
3000
+ }, {
3001
+ key: "ctrl+n",
3002
+ desc: "Next session",
3003
+ preventDefault: true,
3004
+ cmd: () => moveCursor(1)
2995
3005
  }, {
2996
3006
  key: "pageup",
2997
3007
  desc: "Page up",
@@ -3857,11 +3867,21 @@ const tui = async api => {
3857
3867
  desc: "Previous session",
3858
3868
  preventDefault: true,
3859
3869
  cmd: () => moveCursor(-1)
3870
+ }, {
3871
+ key: "ctrl+p",
3872
+ desc: "Previous session",
3873
+ preventDefault: true,
3874
+ cmd: () => moveCursor(-1)
3860
3875
  }, {
3861
3876
  key: "down",
3862
3877
  desc: "Next session",
3863
3878
  preventDefault: true,
3864
3879
  cmd: () => moveCursor(1)
3880
+ }, {
3881
+ key: "ctrl+n",
3882
+ desc: "Next session",
3883
+ preventDefault: true,
3884
+ cmd: () => moveCursor(1)
3865
3885
  }, {
3866
3886
  key: "enter",
3867
3887
  desc: "Open session",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-sesh",
3
- "version": "2.0.2",
4
- "description": "Browse, search, preview and resume opencode sessions from the terminal and the TUI.",
3
+ "version": "2.0.3",
4
+ "description": "OpenCode session browser: search, preview, and resume every session across every project from the terminal or TUI.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Ozan Dogrultan",
@@ -19,7 +19,9 @@
19
19
  "session-browser",
20
20
  "fzf",
21
21
  "tui",
22
- "cli"
22
+ "cli",
23
+ "opencode-plugin",
24
+ "opencode-tui"
23
25
  ],
24
26
  "exports": {
25
27
  ".": "./plugins/sesh-list.ts",
@@ -70,18 +72,18 @@
70
72
  "solid-js": ">=1.9.0"
71
73
  },
72
74
  "devDependencies": {
73
- "@babel/core": "7.28.0",
74
- "@babel/preset-typescript": "7.27.1",
75
+ "@babel/core": "7.29.7",
76
+ "@babel/preset-typescript": "7.29.7",
75
77
  "@commitlint/cli": "^21.2.2",
76
78
  "@commitlint/config-conventional": "^21.2.2",
77
- "@opencode/client": "2.0.25",
78
- "@opencode/plugin": "2.0.25",
79
- "@opencode/theme": "2.0.25",
80
- "@opentui/core": "^0.5.11",
81
- "@opentui/keymap": "^0.5.11",
82
- "@opentui/solid": "^0.5.11",
83
- "@types/node": "26.6.4",
84
- "babel-preset-solid": "1.9.12",
79
+ "@opencode/client": "2.0.29",
80
+ "@opencode/plugin": "2.0.29",
81
+ "@opencode/theme": "2.0.29",
82
+ "@opentui/core": "^0.6.5",
83
+ "@opentui/keymap": "^0.6.5",
84
+ "@opentui/solid": "^0.6.5",
85
+ "@types/node": "26.6.5",
86
+ "babel-preset-solid": "1.9.16",
85
87
  "husky": "^9.1.7",
86
88
  "solid-js": "1.9.15",
87
89
  "typescript": "7.0.2"
@@ -2507,7 +2507,9 @@ const tui: TuiPlugin = async (api) => {
2507
2507
  priority: 1,
2508
2508
  bindings: [
2509
2509
  { key: "up", desc: "Previous session", preventDefault: true, cmd: () => moveCursor(-1) },
2510
+ { key: "ctrl+p", desc: "Previous session", preventDefault: true, cmd: () => moveCursor(-1) },
2510
2511
  { key: "down", desc: "Next session", preventDefault: true, cmd: () => moveCursor(1) },
2512
+ { key: "ctrl+n", desc: "Next session", preventDefault: true, cmd: () => moveCursor(1) },
2511
2513
  { key: "pageup", desc: "Page up", preventDefault: true, cmd: () => moveCursor(-10) },
2512
2514
  { key: "pagedown", desc: "Page down", preventDefault: true, cmd: () => moveCursor(10) },
2513
2515
  { key: "home", desc: "First session", preventDefault: true, cmd: () => selectCursor(0) },
@@ -3036,7 +3038,9 @@ const tui: TuiPlugin = async (api) => {
3036
3038
  mode: "modal",
3037
3039
  bindings: [
3038
3040
  { key: "up", desc: "Previous session", preventDefault: true, cmd: () => moveCursor(-1) },
3041
+ { key: "ctrl+p", desc: "Previous session", preventDefault: true, cmd: () => moveCursor(-1) },
3039
3042
  { key: "down", desc: "Next session", preventDefault: true, cmd: () => moveCursor(1) },
3043
+ { key: "ctrl+n", desc: "Next session", preventDefault: true, cmd: () => moveCursor(1) },
3040
3044
  { key: "enter", desc: "Open session", preventDefault: true, cmd: () => choose() },
3041
3045
  { key: "n", desc: "Open next waiting session", preventDefault: true, cmd: () => {
3042
3046
  const at = items.findIndex((item) => item.entry.id === currentID)