feat(tui): added Zellij and WezTerm pane fallbacks for terminal session continuity

This commit is contained in:
enieuwy
2026-06-12 23:33:59 +08:00
parent db421bb2ef
commit 432ef0115a
5 changed files with 68 additions and 2 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Changed
- Updated the session-continuation tip to mention Zellij and WezTerm panes.
## [15.12.3] - 2026-06-12
### Fixed
@@ -6,7 +6,7 @@ Find out which model you emotionally abuse the most with `omp stats`
Try task isolation to create CoW worktrees
Need a cheap nested model call? Use `completion(x...)`. Have a big batch of tasks? Ask clanker to use it!
Spaghetti code? Try complaining with /omfg
Did you know? Each kitty/tmux/cmux split keeps its own session — `omp -c` resumes the right one
Did you know? Each kitty/tmux/cmux/zellij/wezterm split keeps its own session — `omp -c` resumes the right one
Drop the word `ultrathink` in your message for harder multi-step reasoning — watch it glow rainbow as you type
Say `orchestrate` in your message to drive a multi-phase task with parallel subagents — watch it glow as you type
Say `workflowz` in your message to drive the task with parallel subagents in eval — watch it glow as you type
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Added
- Added Zellij and WezTerm pane environment fallbacks for terminal-specific session continuation when no TTY path is available.
## [15.11.8] - 2026-06-12
### Changed
+14
View File
@@ -51,15 +51,29 @@ export function getTerminalId(): string | null {
// Fallback to terminal-specific env vars
// Prefer inner multiplexers over host terminal emulators when stdin has no TTY path.
const zellijPane = process.env.ZELLIJ_PANE_ID;
if (zellijPane) {
// Session names are user-chosen (`zellij -s …`) and the id is used as a
// breadcrumb filename — normalize path separators like the TTY branch does.
const zellijSession = process.env.ZELLIJ_SESSION_NAME?.replace(/[\\/]/g, "-");
return zellijSession ? `zellij-${zellijSession}-${zellijPane}` : `zellij-${zellijPane}`;
}
const tmuxPane = process.env.TMUX_PANE;
if (tmuxPane) return `tmux-${tmuxPane}`;
const cmuxSurface = process.env.CMUX_SURFACE_ID;
if (cmuxSurface) return `cmux-${cmuxSurface}`;
// Kitty before WezTerm/others, matching terminal-capabilities.ts detection
// order. Inherited env makes either order wrong for some nesting; staying
// consistent with the capability detector keeps the two answers aligned.
const kittyId = process.env.KITTY_WINDOW_ID;
if (kittyId) return `kitty-${kittyId}`;
const weztermPane = process.env.WEZTERM_PANE;
if (weztermPane) return `wezterm-${weztermPane}`;
const terminalSessionId = process.env.TERM_SESSION_ID; // macOS Terminal.app
if (terminalSessionId) return `apple-${terminalSessionId}`;
+45 -1
View File
@@ -2,7 +2,16 @@ import { afterEach, describe, expect, it } from "bun:test";
import { getTerminalId } from "@oh-my-pi/pi-tui/ttyid";
const stdinIsTtyDescriptor = Object.getOwnPropertyDescriptor(process.stdin, "isTTY");
const terminalEnvKeys = ["TMUX_PANE", "CMUX_SURFACE_ID", "KITTY_WINDOW_ID", "TERM_SESSION_ID", "WT_SESSION"] as const;
const terminalEnvKeys = [
"ZELLIJ_PANE_ID",
"ZELLIJ_SESSION_NAME",
"TMUX_PANE",
"CMUX_SURFACE_ID",
"WEZTERM_PANE",
"KITTY_WINDOW_ID",
"TERM_SESSION_ID",
"WT_SESSION",
] as const;
const originalTerminalEnv = Object.fromEntries(terminalEnvKeys.map(key => [key, process.env[key]]));
function restoreProperty(target: object, key: string, descriptor: PropertyDescriptor | undefined): void {
@@ -64,4 +73,39 @@ describe("getTerminalId", () => {
expect(getTerminalId()).toBe("kitty-window-42");
});
it("prefers ZELLIJ_PANE_ID over TMUX_PANE", () => {
Object.defineProperty(process.stdin, "isTTY", { value: false, configurable: true });
setTerminalEnv({ ZELLIJ_PANE_ID: "123", TMUX_PANE: "%7" });
expect(getTerminalId()).toBe("zellij-123");
});
it("scopes ZELLIJ_PANE_ID by ZELLIJ_SESSION_NAME when present", () => {
Object.defineProperty(process.stdin, "isTTY", { value: false, configurable: true });
setTerminalEnv({ ZELLIJ_PANE_ID: "123", ZELLIJ_SESSION_NAME: "work" });
expect(getTerminalId()).toBe("zellij-work-123");
});
it("normalizes path separators in ZELLIJ_SESSION_NAME so the id stays filename-safe", () => {
Object.defineProperty(process.stdin, "isTTY", { value: false, configurable: true });
setTerminalEnv({ ZELLIJ_PANE_ID: "123", ZELLIJ_SESSION_NAME: "foo/bar" });
expect(getTerminalId()).toBe("zellij-foo-bar-123");
});
it("prefers KITTY_WINDOW_ID over an inherited WEZTERM_PANE", () => {
Object.defineProperty(process.stdin, "isTTY", { value: false, configurable: true });
setTerminalEnv({ KITTY_WINDOW_ID: "window-42", WEZTERM_PANE: "pane-456" });
expect(getTerminalId()).toBe("kitty-window-42");
});
it("uses WEZTERM_PANE when no multiplexer or kitty markers are present", () => {
Object.defineProperty(process.stdin, "isTTY", { value: false, configurable: true });
setTerminalEnv({ WEZTERM_PANE: "pane-456", TERM_SESSION_ID: "abc" });
expect(getTerminalId()).toBe("wezterm-pane-456");
});
});