opencode-sesh 0.7.1 → 0.7.2
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/CHANGELOG.md +18 -1
- package/package.json +1 -1
- package/tui/sesh-panel.tsx +38 -20
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.2] - 2026-09-24
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Switching to a session from the picker (`ctrl+o` / `/sesh`), the home
|
|
15
|
+
"Recent sessions" list, or the needs-input picker now runs the cmux
|
|
16
|
+
click-to-focus bridge: a session already live in another cmux workspace
|
|
17
|
+
focuses that workspace instead of opening a duplicate here. Previously only
|
|
18
|
+
sidebar rows did this.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Every open path shares one cmux-aware handler, and the cmux surface scan is
|
|
23
|
+
cached briefly so the additional paths add no noticeable delay. Terminals
|
|
24
|
+
outside cmux are unaffected — with no `CMUX_WORKSPACE_ID` no cmux call is
|
|
25
|
+
made, now asserted by a test.
|
|
10
26
|
## [0.7.1] - 2026-09-24
|
|
11
27
|
|
|
12
28
|
### Fixed
|
|
@@ -302,7 +318,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
302
318
|
- Fixture-database regression suite, TypeScript typechecking, and ShellCheck in
|
|
303
319
|
CI.
|
|
304
320
|
|
|
305
|
-
[Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.7.
|
|
321
|
+
[Unreleased]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.7.2...HEAD
|
|
322
|
+
[0.7.2]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.7.1...v0.7.2
|
|
306
323
|
[0.7.1]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.7.0...v0.7.1
|
|
307
324
|
[0.7.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.6.0...v0.7.0
|
|
308
325
|
[0.6.0]: https://github.com/ozandogrultan/opencode-sesh/compare/v0.5.0...v0.6.0
|
package/package.json
CHANGED
package/tui/sesh-panel.tsx
CHANGED
|
@@ -437,6 +437,18 @@ async function cmuxSurfaces(): Promise<CmuxSurface[]> {
|
|
|
437
437
|
return surfaces
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
+
// The bridge walks `cmux tree` plus one `surface resume show` per surface, and
|
|
441
|
+
// switching sessions is frequent while the surface set changes rarely, so reuse
|
|
442
|
+
// a recent scan for a moment instead of re-walking cmux on every open.
|
|
443
|
+
let cmuxSurfacesCache: { at: number; surfaces: CmuxSurface[] } | undefined
|
|
444
|
+
async function cmuxSurfacesCached(): Promise<CmuxSurface[]> {
|
|
445
|
+
const now = Date.now()
|
|
446
|
+
if (cmuxSurfacesCache && now - cmuxSurfacesCache.at < 1500) return cmuxSurfacesCache.surfaces
|
|
447
|
+
const surfaces = await cmuxSurfaces()
|
|
448
|
+
cmuxSurfacesCache = { at: now, surfaces }
|
|
449
|
+
return surfaces
|
|
450
|
+
}
|
|
451
|
+
|
|
440
452
|
// Pure selection: the workspace to focus for a session, or undefined to open
|
|
441
453
|
// locally. Prefers a live surface in a different workspace; ignores the
|
|
442
454
|
// caller's own surface and workspace (focusing where you already are is a
|
|
@@ -452,6 +464,26 @@ function pickFocusWorkspace(surfaces: CmuxSurface[], sessionID: string): string
|
|
|
452
464
|
return undefined
|
|
453
465
|
}
|
|
454
466
|
|
|
467
|
+
// Open a session from anywhere in the panel, cmux-aware: if another cmux
|
|
468
|
+
// workspace is already running it, focus that workspace instead of opening a
|
|
469
|
+
// duplicate here; otherwise open it locally. Every switch path (sidebar, home
|
|
470
|
+
// list, picker, needs-input picker) goes through here so none of them silently
|
|
471
|
+
// skips the bridge. cmux calls only run inside cmux.
|
|
472
|
+
async function openSessionEntry(api: TuiPluginApi, entry: Entry): Promise<void> {
|
|
473
|
+
if (process.env.CMUX_WORKSPACE_ID) {
|
|
474
|
+
const target = pickFocusWorkspace(await cmuxSurfacesCached(), entry.id)
|
|
475
|
+
if (target) {
|
|
476
|
+
try {
|
|
477
|
+
await promisify(execFile)("cmux", ["workspace", "select", target])
|
|
478
|
+
return
|
|
479
|
+
} catch {
|
|
480
|
+
// cmux unavailable; fall through to a local open
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
api.route.navigate("session", { sessionID: entry.id })
|
|
485
|
+
}
|
|
486
|
+
|
|
455
487
|
function addTranscript(index: Map<string, string>, sid: string, data: string): void {
|
|
456
488
|
try {
|
|
457
489
|
const part = JSON.parse(data) as { type?: unknown; text?: unknown }
|
|
@@ -853,23 +885,9 @@ function SidebarSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
853
885
|
setSectionCollapsed((value) => !value)
|
|
854
886
|
}
|
|
855
887
|
|
|
856
|
-
//
|
|
857
|
-
// workspace
|
|
858
|
-
|
|
859
|
-
const openSession = async (entry: Entry) => {
|
|
860
|
-
if (process.env.CMUX_WORKSPACE_ID) {
|
|
861
|
-
const target = pickFocusWorkspace(await cmuxSurfaces(), entry.id)
|
|
862
|
-
if (target) {
|
|
863
|
-
try {
|
|
864
|
-
await promisify(execFile)("cmux", ["workspace", "select", target])
|
|
865
|
-
return
|
|
866
|
-
} catch {
|
|
867
|
-
// cmux unavailable; fall through to a local open
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
props.api.route.navigate("session", { sessionID: entry.id })
|
|
872
|
-
}
|
|
888
|
+
// Sidebar rows open through the shared cmux-aware path, so they focus the
|
|
889
|
+
// workspace already running the session instead of opening a duplicate.
|
|
890
|
+
const openSession = (entry: Entry) => openSessionEntry(props.api, entry)
|
|
873
891
|
|
|
874
892
|
// Flat order of the rows a cursor can land on, so keyboard navigation and
|
|
875
893
|
// mouse hover resolve to the same row.
|
|
@@ -1449,7 +1467,7 @@ function HomeSessions(props: { api: TuiPluginApi } & PinProps) {
|
|
|
1449
1467
|
backgroundColor={entry.id === hovered() ? theme().backgroundElement : RGBA.fromInts(0, 0, 0, 0)}
|
|
1450
1468
|
onMouseOver={() => setHovered(entry.id)}
|
|
1451
1469
|
onMouseOut={() => setHovered(undefined)}
|
|
1452
|
-
onMouseDown={() => props.api
|
|
1470
|
+
onMouseDown={() => void openSessionEntry(props.api, entry)}
|
|
1453
1471
|
>
|
|
1454
1472
|
<text flexShrink={0} style={{ fg: theme().textMuted }}>
|
|
1455
1473
|
○
|
|
@@ -1753,7 +1771,7 @@ const tui: TuiPlugin = async (api) => {
|
|
|
1753
1771
|
if (!target) return
|
|
1754
1772
|
cleanup()
|
|
1755
1773
|
api.ui.dialog.clear()
|
|
1756
|
-
api
|
|
1774
|
+
void openSessionEntry(api, target)
|
|
1757
1775
|
}
|
|
1758
1776
|
|
|
1759
1777
|
const disposeNav = api.keymap.registerLayer({
|
|
@@ -2154,7 +2172,7 @@ ORDER BY lifetime_cost DESC`).all() ?? []) as {
|
|
|
2154
2172
|
if (!target) return
|
|
2155
2173
|
disposeNav()
|
|
2156
2174
|
api.ui.dialog.clear()
|
|
2157
|
-
api
|
|
2175
|
+
void openSessionEntry(api, target.entry)
|
|
2158
2176
|
}
|
|
2159
2177
|
const disposeNav = api.keymap.registerLayer({
|
|
2160
2178
|
bindings: [
|