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 +2 -0
- package/CHANGELOG.md +12 -1
- package/README.md +3 -1
- package/ROADMAP.md +2 -3
- package/contrib/cmux/README.md +2 -2
- package/install.sh +1 -7
- package/package.json +1 -1
- package/tui/sesh-panel.tsx +28 -3
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.
|
|
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
|
|
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:**
|
|
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
|
|
40
|
+
own workspace; the TUI's right-click action opens one directly.
|
|
42
41
|
|
|
43
42
|
## 4. Per-project cost digest (done)
|
|
44
43
|
|
package/contrib/cmux/README.md
CHANGED
|
@@ -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:
|
|
5
|
-
|
|
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" ||
|
|
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
package/tui/sesh-panel.tsx
CHANGED
|
@@ -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={(
|
|
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={(
|
|
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={() =>
|
|
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
|