opencode-sesh 0.11.1 → 0.12.0

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
@@ -17,6 +17,8 @@ territory: read this first, then the specific file you need.
17
17
  db` 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
21
+ `cmux new-workspace` when running inside cmux.
20
22
  opencode has no persistent-sidebar API, so the "sidebar" is a `sidebar_content`
21
23
  slot section plus an xlarge modal picker; the terminal UI is fullscreen fzf.
22
24
 
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.12.0] - 2026-09-30
11
+
12
+ ### Added
13
+
14
+ - Right-clicking a session in the TUI sidebar, home list or picker opens it in a
15
+ new cmux workspace at its directory; outside cmux it shows a toast.
16
+
17
+ ### Fixed
18
+
19
+ - Avoid creating `.bak` backup files on install
10
20
  ## [0.11.1] - 2026-09-25
11
21
 
12
22
  ### Fixed
@@ -378,7 +388,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
378
388
  - Fixture-database regression suite, TypeScript typechecking, and ShellCheck in
379
389
  CI.
380
390
 
381
- [Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.11.1...HEAD
391
+ [Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.12.0...HEAD
392
+ [0.12.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.11.1...v0.12.0
382
393
  [0.11.1]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.11.0...v0.11.1
383
394
  [0.11.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.10.0...v0.11.0
384
395
  [0.10.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.9.1...v0.10.0
package/README.md CHANGED
@@ -185,6 +185,8 @@ The picker groups sessions under collapsible directory headings (click ▾/▸).
185
185
  Mouse-wheel scrolling moves through the tree without changing the selection
186
186
  under a stationary pointer. Option-P opens a full-size transcript preview over
187
187
  the picker; use ↑/↓ to preview other sessions, then Esc to return to the list.
188
+ Right-click a session (sidebar, home list or picker) to open it in a new cmux
189
+ workspace at its directory; outside cmux it shows a toast instead.
188
190
 
189
191
  Click the sidebar's `search…` box to filter recent sessions in place; click
190
192
  elsewhere or press Esc to leave search. Click the **Sessions** heading to drive
@@ -228,7 +230,7 @@ All variables are optional.
228
230
 
229
231
  ## Integrations
230
232
 
231
- sesh is terminal-agnostic on purpose: it has no window-management code. The
233
+ sesh is terminal-agnostic apart from the TUI's right-click action. The
232
234
  [`contrib/cmux/`](contrib/cmux/README.md) directory shows how to drive it from
233
235
  cmux — command-palette entries, shortcuts, resuming a session in its own
234
236
  workspace, and a global "resume from anywhere" hotkey — all caller-side.
package/ROADMAP.md CHANGED
@@ -35,10 +35,9 @@ candidate summary; non-TTY runs require `--yes`; `--dry-run` lists only.
35
35
 
36
36
  ## 3. sesh ↔ cmux bridge (done)
37
37
 
38
- **Approach:** kept caller-side. sesh has no window-management code (a hard
39
- repo rule), so [`contrib/cmux/`](contrib/cmux/README.md) ships the cmux
38
+ **Approach:** [`contrib/cmux/`](contrib/cmux/README.md) ships the cmux
40
39
  actions, shortcuts and a `--print --json` recipe that resumes a session in its
41
- own workspace. cmux calls sesh; sesh never calls cmux.
40
+ own workspace; the TUI's right-click action opens one directly.
42
41
 
43
42
  ## 4. Per-project cost digest (done)
44
43
 
@@ -1,8 +1,8 @@
1
1
  # sesh ↔ cmux
2
2
 
3
3
  Optional glue for running sesh inside [cmux](https://cmux.dev). Nothing here is
4
- bundled into the picker: sesh has no window-management code, and these recipes
5
- keep it that way. cmux calls sesh; sesh never calls cmux.
4
+ bundled into the picker: these recipes are caller-side. The only place sesh
5
+ calls cmux is the TUI's right-click action, which runs `cmux new-workspace`.
6
6
 
7
7
  ## Command palette + shortcuts
8
8
 
package/install.sh CHANGED
@@ -87,7 +87,7 @@ remove_tui_entry() {
87
87
  local tmp
88
88
  tmp=$(mktemp "${TMPDIR:-/tmp}/sesh-tui.XXXXXX") || return 0
89
89
  if jq 'if (.plugin | type) == "array" then .plugin = [.plugin[] | select(. != "./plugins/sesh-panel.tsx")] else . end' "$TUI_JSON" > "$tmp" 2>/dev/null; then
90
- cmp -s "$TUI_JSON" "$tmp" || { cp -p "$TUI_JSON" "$TUI_JSON.bak.$(date +%Y%m%d%H%M%S)"; mv "$tmp" "$TUI_JSON"; }
90
+ cmp -s "$TUI_JSON" "$tmp" || mv "$tmp" "$TUI_JSON"
91
91
  rm -f "$tmp"
92
92
  else
93
93
  rm -f "$tmp"
@@ -120,10 +120,6 @@ install_file() {
120
120
  note "up to date $dst"
121
121
  return 0
122
122
  fi
123
- if [ -f "$dst" ]; then
124
- cp -p "$dst" "$dst.bak.$(date +%Y%m%d%H%M%S)"
125
- note "backed up existing $(basename "$dst")"
126
- fi
127
123
  cp -p "$src" "$dst"
128
124
  note "installed $dst"
129
125
  }
@@ -157,7 +153,6 @@ install_tui_panel() {
157
153
  note "tui.json already registers the sesh panel"
158
154
  rm -f "$tui_tmp"
159
155
  else
160
- [ "$tui_created" = 1 ] || cp -p "$TUI_JSON" "$TUI_JSON.bak.$(date +%Y%m%d%H%M%S)"
161
156
  mv "$tui_tmp" "$TUI_JSON"
162
157
  note "registered ./plugins/sesh-panel.tsx in tui.json"
163
158
  fi
@@ -190,7 +185,6 @@ install_tui_panel() {
190
185
  note "plugin dependencies already present in $PACKAGE_JSON"
191
186
  rm -f "$pkg_tmp"
192
187
  else
193
- [ "$pkg_created" = 1 ] || cp -p "$PACKAGE_JSON" "$PACKAGE_JSON.bak.$(date +%Y%m%d%H%M%S)"
194
188
  mv "$pkg_tmp" "$PACKAGE_JSON"
195
189
  note "declared plugin dependencies in $PACKAGE_JSON (opencode installs them on next start)"
196
190
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sesh",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "Browse, search, preview and resume opencode sessions from the terminal and the TUI.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4,6 +4,7 @@ import type { Project, Session } from "@opencode-ai/sdk/v2"
4
4
  import { createEffect, createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-js"
5
5
  import { mkdir, readFile, rename, rmdir, unlink, writeFile } from "node:fs/promises"
6
6
  import { dirname, join } from "node:path"
7
+ import { spawn } from "node:child_process"
7
8
  import { getTreeSitterClient, RGBA, SyntaxStyle, TextAttributes } from "@opentui/core"
8
9
 
9
10
  type ThemeColors = TuiPluginApi["theme"]["current"]
@@ -250,6 +251,22 @@ type Entry = { id: string; title: string; dir: string; group: string; updated: n
250
251
  type EntryResult = { entries: Entry[]; truncated: boolean }
251
252
  type SidebarMarker = "current" | "running" | "idle"
252
253
 
254
+ const RIGHT_BUTTON = 2
255
+
256
+ function openInCmuxWorkspace(api: TuiPluginApi, entry: Entry) {
257
+ if (!process.env.CMUX_WORKSPACE_ID) {
258
+ api.ui.toast({ message: "Right-click opens a new workspace only inside cmux", variant: "info" })
259
+ return
260
+ }
261
+ const args = ["new-workspace", "--name", entry.title || entry.id, "--command", `opencode --session ${entry.id}`, "--focus", "true"]
262
+ if (entry.dir) args.push("--cwd", entry.dir)
263
+ const child = spawn("cmux", args, { stdio: "ignore" })
264
+ child.on("error", () => api.ui.toast({ message: "Could not open a cmux workspace", variant: "error" }))
265
+ child.on("exit", (code) => {
266
+ if (code) api.ui.toast({ message: "Could not open a cmux workspace", variant: "error" })
267
+ })
268
+ }
269
+
253
270
  // Loading-spinner frames for the marker of a session whose agent is working.
254
271
  const SIDEBAR_SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
255
272
 
@@ -1319,7 +1336,9 @@ function SidebarSessions(props: { api: TuiPluginApi } & PinProps) {
1319
1336
  }
1320
1337
  }}
1321
1338
  onMouseOut={() => setHovered(undefined)}
1322
- onMouseDown={() => void openSession(row.entry)}
1339
+ onMouseDown={(event: { button?: number }) =>
1340
+ event.button === RIGHT_BUTTON ? openInCmuxWorkspace(props.api, row.entry) : void openSession(row.entry)
1341
+ }
1323
1342
  >
1324
1343
  <text flexShrink={0}>
1325
1344
  <span style={{ fg: theme().textMuted }}>{row.last ? "└" : "├"}</span>
@@ -1617,7 +1636,11 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
1617
1636
  backgroundColor={entry.id === hovered() ? theme().backgroundElement : RGBA.fromInts(0, 0, 0, 0)}
1618
1637
  onMouseOver={() => setHovered(entry.id)}
1619
1638
  onMouseOut={() => setHovered(undefined)}
1620
- onMouseDown={() => props.api.route.navigate("session", { sessionID: entry.id })}
1639
+ onMouseDown={(event: { button?: number }) =>
1640
+ event.button === RIGHT_BUTTON
1641
+ ? openInCmuxWorkspace(props.api, entry)
1642
+ : props.api.route.navigate("session", { sessionID: entry.id })
1643
+ }
1621
1644
  >
1622
1645
  <text flexShrink={0} style={{ fg: theme().textMuted }}>
1623
1646
  ○
@@ -2242,7 +2265,9 @@ const tui: TuiPlugin = async (api) => {
2242
2265
  const index = selectableEntries().findIndex((entry) => entry.id === row.entry.id)
2243
2266
  if (index >= 0) setCursor(index)
2244
2267
  }}
2245
- onMouseDown={() => choose(row.entry)}
2268
+ onMouseDown={(event: { button?: number }) =>
2269
+ event.button === RIGHT_BUTTON ? (cleanup(), api.ui.dialog.clear(), openInCmuxWorkspace(api, row.entry)) : choose(row.entry)
2270
+ }
2246
2271
  >
2247
2272
  <box flexDirection="row" gap={1}>
2248
2273
  <text flexShrink={0} style={{ fg: row.entry.id === selectableEntries()[cursor()]?.id