@sma1lboy/kobe 0.7.31 → 0.7.33
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/dist/cli/index.js +986 -141
- package/dist/web-ui/assets/{AppShell-79UyF3Dn.js → AppShell-CT-Wueej.js} +3 -3
- package/dist/web-ui/assets/{ChatTerminal-DyPmK4ll.js → ChatTerminal-WIjnp057.js} +1 -1
- package/dist/web-ui/assets/{IssuePeek-z77QfvyZ.js → IssuePeek-DS8gwn8L.js} +1 -1
- package/dist/web-ui/assets/{ViewToggle-DAvvHlkd.js → ViewToggle-BM9Leqct.js} +1 -1
- package/dist/web-ui/assets/board-BicE8Mfe.js +1 -0
- package/dist/web-ui/assets/{index-Bp9dfUdH.js → index-BsVnxIKo.js} +2 -2
- package/dist/web-ui/assets/{issues-Fo-1cGh2.js → issues-Djlu09em.js} +1 -1
- package/dist/web-ui/assets/routes-BmyEyMrf.js +1 -0
- package/dist/web-ui/assets/{task._taskId-xBuyIrYG.js → task._taskId-CmRCr8jG.js} +1 -1
- package/dist/web-ui/assets/{vendor-BOFoJ49C.js → vendor-Bfrh7rZ5.js} +1 -1
- package/dist/web-ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/web-ui/assets/board-DEmq9YrO.js +0 -1
- package/dist/web-ui/assets/routes-K4B3f4it.js +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
|
|
|
90
90
|
package_default = {
|
|
91
91
|
$schema: "https://json.schemastore.org/package.json",
|
|
92
92
|
name: "@sma1lboy/kobe",
|
|
93
|
-
version: "0.7.
|
|
93
|
+
version: "0.7.33",
|
|
94
94
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
95
95
|
type: "module",
|
|
96
96
|
packageManager: "bun@1.3.13",
|
|
@@ -5259,6 +5259,14 @@ function kobeCliInvocation() {
|
|
|
5259
5259
|
var init_invocation = () => {};
|
|
5260
5260
|
|
|
5261
5261
|
// src/tmux/session-layout.ts
|
|
5262
|
+
function hiddenTerminalSessionName(session) {
|
|
5263
|
+
const safe = session.replace(/[^A-Za-z0-9_-]/g, "");
|
|
5264
|
+
return `kobe-hidden-${safe || "session"}`;
|
|
5265
|
+
}
|
|
5266
|
+
function hiddenTerminalWindowIndex(windowId) {
|
|
5267
|
+
const n = Number.parseInt(windowId.replace(/^@/, ""), 10);
|
|
5268
|
+
return Number.isFinite(n) && n >= 0 ? 1000 + n : 1000;
|
|
5269
|
+
}
|
|
5262
5270
|
function clampTasksPaneWidth(width) {
|
|
5263
5271
|
if (!Number.isFinite(width))
|
|
5264
5272
|
return TASKS_PANE_WIDTH;
|
|
@@ -5339,7 +5347,7 @@ function opsPaneCommand(args) {
|
|
|
5339
5347
|
}
|
|
5340
5348
|
return fallbackOpsScript(args.cwd);
|
|
5341
5349
|
}
|
|
5342
|
-
var TASKS_PANE_WIDTH = 32, TASKS_WIDTH_OPTION = "@kobe_tasks_width", TASKS_PANE_WIDTH_MIN = 16, TASKS_PANE_WIDTH_MAX = 120, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50, RIGHT_COLUMN_WIDTH_OPTION = "@kobe_right_width_pct", OPS_HEIGHT_OPTION = "@kobe_ops_height_pct", PANE_PERCENT_MIN = 10, PANE_PERCENT_MAX = 90;
|
|
5350
|
+
var TASKS_PANE_WIDTH = 32, TASKS_PANE_ROLE = "tasks", ENGINE_PANE_ROLE = "claude", OPS_PANE_ROLE = "ops", SHELL_PANE_ROLE = "shell", WORKSPACE_AUX_PANE_ROLE = "workspace_aux", WORKSPACE_SPLIT_MAX_PANES = 4, HIDDEN_TERMINAL_PANE_OPTION = "@kobe_hidden_shell_pane", HIDDEN_TASKS_PANE_OPTION = "@kobe_hidden_tasks_pane", TASKS_WIDTH_OPTION = "@kobe_tasks_width", TASKS_PANE_WIDTH_MIN = 16, TASKS_PANE_WIDTH_MAX = 120, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50, RIGHT_COLUMN_WIDTH_OPTION = "@kobe_right_width_pct", OPS_HEIGHT_OPTION = "@kobe_ops_height_pct", PANE_PERCENT_MIN = 10, PANE_PERCENT_MAX = 90;
|
|
5343
5351
|
|
|
5344
5352
|
// src/engine/claude-code-local/hook-adapter.ts
|
|
5345
5353
|
import { mkdir as mkdir5, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -6751,6 +6759,11 @@ async function currentSessionName() {
|
|
|
6751
6759
|
return code === 0 && name.length > 0 ? name : null;
|
|
6752
6760
|
}
|
|
6753
6761
|
async function killSession(name) {
|
|
6762
|
+
if (!name.startsWith("kobe-hidden-")) {
|
|
6763
|
+
const hidden = hiddenTerminalSessionName(name);
|
|
6764
|
+
if (await sessionExists(hidden))
|
|
6765
|
+
await runTmux(["kill-session", "-t", `=${hidden}`]);
|
|
6766
|
+
}
|
|
6754
6767
|
if (await sessionExists(name))
|
|
6755
6768
|
await runTmux(["kill-session", "-t", `=${name}`]);
|
|
6756
6769
|
}
|
|
@@ -9407,7 +9420,10 @@ var init_keymap_overrides = __esm(() => {
|
|
|
9407
9420
|
});
|
|
9408
9421
|
|
|
9409
9422
|
// src/tmux/keybindings.ts
|
|
9410
|
-
function
|
|
9423
|
+
function isTmuxPrefixBindingId(id) {
|
|
9424
|
+
return id in TMUX_PREFIX_BINDING_DEFAULTS;
|
|
9425
|
+
}
|
|
9426
|
+
function chordToTmuxKey(chord, opts) {
|
|
9411
9427
|
const parts = chord.split("+");
|
|
9412
9428
|
let key = parts.pop() ?? "";
|
|
9413
9429
|
if (key === "" && parts.length > 0) {
|
|
@@ -9429,7 +9445,7 @@ function chordToTmuxKey(chord) {
|
|
|
9429
9445
|
tmuxKey = named;
|
|
9430
9446
|
}
|
|
9431
9447
|
const isFKey = /^f\d+$/.test(key);
|
|
9432
|
-
if (mods.size === 0 && !isFKey) {
|
|
9448
|
+
if (mods.size === 0 && !isFKey && !opts?.allowBare) {
|
|
9433
9449
|
return {
|
|
9434
9450
|
error: `"${chord}": tmux session keys are no-prefix root bindings live in every pane \u2014 a bare key would shadow typing (add a modifier, or use an F-key)`
|
|
9435
9451
|
};
|
|
@@ -9446,7 +9462,7 @@ function chordToTmuxKey(chord) {
|
|
|
9446
9462
|
function defaultResolution() {
|
|
9447
9463
|
const binds = {};
|
|
9448
9464
|
for (const [id, chord] of Object.entries(TMUX_SINGLE_BINDING_DEFAULTS)) {
|
|
9449
|
-
const t = chordToTmuxKey(chord);
|
|
9465
|
+
const t = chordToTmuxKey(chord, { allowBare: isTmuxPrefixBindingId(id) });
|
|
9450
9466
|
if ("error" in t)
|
|
9451
9467
|
throw new Error(`default tmux chord for ${id} failed to translate: ${t.error}`);
|
|
9452
9468
|
binds[id] = { chord, key: t.key };
|
|
@@ -9506,7 +9522,7 @@ function resolveTmuxKeyEntries(entries) {
|
|
|
9506
9522
|
res.warnings.push(`${id}: tmux bindings take ONE chord \u2014 using "${entry.keys[0]}", ignoring the rest`);
|
|
9507
9523
|
}
|
|
9508
9524
|
const chord = entry.keys[0];
|
|
9509
|
-
const t = chordToTmuxKey(chord);
|
|
9525
|
+
const t = chordToTmuxKey(chord, { allowBare: isTmuxPrefixBindingId(id) });
|
|
9510
9526
|
if ("error" in t) {
|
|
9511
9527
|
res.warnings.push(`${id}: ${t.error} \u2014 keeping the default`);
|
|
9512
9528
|
continue;
|
|
@@ -9541,11 +9557,11 @@ function resolveUserTmuxKeys() {
|
|
|
9541
9557
|
cached2 = res;
|
|
9542
9558
|
return cached2;
|
|
9543
9559
|
}
|
|
9544
|
-
var TMUX_SINGLE_BINDING_DEFAULTS, TMUX_FOCUS_ID = "tmux.focus", TMUX_FOCUS_DEFAULTS, TMUX_NAMED_KEYS, cached2 = null;
|
|
9560
|
+
var TMUX_ROOT_BINDING_DEFAULTS, TMUX_PREFIX_BINDING_DEFAULTS, TMUX_LEGACY_LAYOUT_ROOT_KEYS, TMUX_SINGLE_BINDING_DEFAULTS, TMUX_FOCUS_ID = "tmux.focus", TMUX_FOCUS_DEFAULTS, TMUX_NAMED_KEYS, cached2 = null;
|
|
9545
9561
|
var init_keybindings = __esm(() => {
|
|
9546
9562
|
init_keybindings_file();
|
|
9547
9563
|
init_keymap_overrides();
|
|
9548
|
-
|
|
9564
|
+
TMUX_ROOT_BINDING_DEFAULTS = {
|
|
9549
9565
|
"tmux.detach": "ctrl+q",
|
|
9550
9566
|
"tmux.tab.new": "ctrl+t",
|
|
9551
9567
|
"tmux.tab.chooseEngine": "ctrl+shift+t",
|
|
@@ -9554,6 +9570,19 @@ var init_keybindings = __esm(() => {
|
|
|
9554
9570
|
"tmux.tab.close": "ctrl+w",
|
|
9555
9571
|
"tmux.tab.rename": "f2"
|
|
9556
9572
|
};
|
|
9573
|
+
TMUX_PREFIX_BINDING_DEFAULTS = {
|
|
9574
|
+
"tmux.layout.workspaceSplit": "s",
|
|
9575
|
+
"tmux.layout.workspaceClose": "x",
|
|
9576
|
+
"tmux.layout.workspaceReset": "r",
|
|
9577
|
+
"tmux.layout.tasksToggle": "a",
|
|
9578
|
+
"tmux.layout.opsToggle": "o",
|
|
9579
|
+
"tmux.layout.terminalToggle": "z"
|
|
9580
|
+
};
|
|
9581
|
+
TMUX_LEGACY_LAYOUT_ROOT_KEYS = ["F6", "F7", "F8", "F9", "F10", "F11"];
|
|
9582
|
+
TMUX_SINGLE_BINDING_DEFAULTS = {
|
|
9583
|
+
...TMUX_ROOT_BINDING_DEFAULTS,
|
|
9584
|
+
...TMUX_PREFIX_BINDING_DEFAULTS
|
|
9585
|
+
};
|
|
9557
9586
|
TMUX_FOCUS_DEFAULTS = ["ctrl+h", "ctrl+j", "ctrl+k", "ctrl+l"];
|
|
9558
9587
|
TMUX_NAMED_KEYS = {
|
|
9559
9588
|
up: "Up",
|
|
@@ -11400,7 +11429,7 @@ async function captureGlobalLayout(session) {
|
|
|
11400
11429
|
"-t",
|
|
11401
11430
|
`=${session}`,
|
|
11402
11431
|
"-F",
|
|
11403
|
-
|
|
11432
|
+
`#{@kobe_role} #{pane_width} #{pane_height} #{window_width} #{window_height} #{window_zoomed_flag} #{${HIDDEN_TERMINAL_PANE_OPTION}} #{${HIDDEN_TASKS_PANE_OPTION}}`
|
|
11404
11433
|
]);
|
|
11405
11434
|
if (code !== 0)
|
|
11406
11435
|
return;
|
|
@@ -11410,6 +11439,10 @@ async function captureGlobalLayout(session) {
|
|
|
11410
11439
|
return;
|
|
11411
11440
|
if (rows.some((cols) => cols[5]?.trim() === "1"))
|
|
11412
11441
|
return;
|
|
11442
|
+
if (rows.some((cols) => (cols[6]?.trim() ?? "") !== ""))
|
|
11443
|
+
return;
|
|
11444
|
+
if (rows.some((cols) => (cols[7]?.trim() ?? "") !== ""))
|
|
11445
|
+
return;
|
|
11413
11446
|
const winW = Number.parseInt(rows[0][3]?.trim() ?? "", 10);
|
|
11414
11447
|
const winH = Number.parseInt(rows[0][4]?.trim() ?? "", 10);
|
|
11415
11448
|
const sets = [];
|
|
@@ -11440,6 +11473,10 @@ function shouldCaptureDrag(stdout) {
|
|
|
11440
11473
|
return false;
|
|
11441
11474
|
if (rows.some((cols) => cols[1]?.trim() === "1"))
|
|
11442
11475
|
return false;
|
|
11476
|
+
if (rows.some((cols) => (cols[2]?.trim() ?? "") !== ""))
|
|
11477
|
+
return false;
|
|
11478
|
+
if (rows.some((cols) => (cols[3]?.trim() ?? "") !== ""))
|
|
11479
|
+
return false;
|
|
11443
11480
|
const roles = new Set(rows.map((cols) => cols[0]?.trim()));
|
|
11444
11481
|
return roles.has("tasks") && roles.has("ops");
|
|
11445
11482
|
}
|
|
@@ -11449,7 +11486,7 @@ async function captureGlobalLayoutOnDrag(session) {
|
|
|
11449
11486
|
"-t",
|
|
11450
11487
|
`=${session}`,
|
|
11451
11488
|
"-F",
|
|
11452
|
-
|
|
11489
|
+
`#{@kobe_role} #{window_zoomed_flag} #{${HIDDEN_TERMINAL_PANE_OPTION}} #{${HIDDEN_TASKS_PANE_OPTION}}`
|
|
11453
11490
|
]);
|
|
11454
11491
|
if (code !== 0 || !shouldCaptureDrag(stdout))
|
|
11455
11492
|
return;
|
|
@@ -11492,7 +11529,7 @@ function chatTabSwitchBindings(prevKey, nextKey) {
|
|
|
11492
11529
|
["bind-key", "-n", nextKey, "next-window"]
|
|
11493
11530
|
];
|
|
11494
11531
|
}
|
|
11495
|
-
function chatTabCloseBinding(key) {
|
|
11532
|
+
function chatTabCloseBinding(key, closeCommand = "kill-window") {
|
|
11496
11533
|
return [
|
|
11497
11534
|
"bind-key",
|
|
11498
11535
|
"-n",
|
|
@@ -11500,7 +11537,7 @@ function chatTabCloseBinding(key) {
|
|
|
11500
11537
|
"if-shell",
|
|
11501
11538
|
"-F",
|
|
11502
11539
|
"#{>:#{session_windows},1}",
|
|
11503
|
-
|
|
11540
|
+
closeCommand,
|
|
11504
11541
|
"display-message 'Cannot close the only ChatTab'"
|
|
11505
11542
|
];
|
|
11506
11543
|
}
|
|
@@ -11520,7 +11557,9 @@ function kobeStatusRight(keys) {
|
|
|
11520
11557
|
const segments = [
|
|
11521
11558
|
keys.focusLeft ? `${tmuxKeyCap(keys.focusLeft)} tasks` : null,
|
|
11522
11559
|
keys.detach ? `${tmuxKeyCap(keys.detach)} detach` : null,
|
|
11523
|
-
keys.newTab ? `${tmuxKeyCap(keys.newTab)} tab` : null
|
|
11560
|
+
keys.newTab ? `${tmuxKeyCap(keys.newTab)} tab` : null,
|
|
11561
|
+
keys.layoutSplits ? `prefix ${keys.layoutSplits} splits` : null,
|
|
11562
|
+
keys.layoutPanes ? `prefix ${keys.layoutPanes} panes` : null
|
|
11524
11563
|
].filter((s) => s !== null);
|
|
11525
11564
|
return `#[fg=brightblack]${segments.join(" ")} `;
|
|
11526
11565
|
}
|
|
@@ -11566,16 +11605,27 @@ async function buildPanesAround(claudePane, args) {
|
|
|
11566
11605
|
"ops=#{pane_id}",
|
|
11567
11606
|
opsCmd
|
|
11568
11607
|
],
|
|
11569
|
-
[
|
|
11608
|
+
[
|
|
11609
|
+
"split-window",
|
|
11610
|
+
"-v",
|
|
11611
|
+
"-l",
|
|
11612
|
+
`${100 - OPS_PANE_PERCENT}%`,
|
|
11613
|
+
"-c",
|
|
11614
|
+
localSpawnCwd(args.cwd),
|
|
11615
|
+
"-P",
|
|
11616
|
+
"-F",
|
|
11617
|
+
"shell=#{pane_id}"
|
|
11618
|
+
],
|
|
11570
11619
|
["select-pane", "-t", claudePane]
|
|
11571
11620
|
]);
|
|
11572
11621
|
const ids = Object.fromEntries(stdout.split(`
|
|
11573
11622
|
`).map((line) => line.trim()).filter(Boolean).map((line) => line.split("=", 2)));
|
|
11574
11623
|
await runTmuxSequence([
|
|
11575
|
-
...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, "@kobe_role",
|
|
11624
|
+
...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, "@kobe_role", TASKS_PANE_ROLE]] : [],
|
|
11576
11625
|
...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, PANE_VERSION_OPTION, CURRENT_VERSION]] : [],
|
|
11577
|
-
...ids.ops ? [["set-option", "-p", "-t", ids.ops, "@kobe_role",
|
|
11578
|
-
...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION]] : []
|
|
11626
|
+
...ids.ops ? [["set-option", "-p", "-t", ids.ops, "@kobe_role", OPS_PANE_ROLE]] : [],
|
|
11627
|
+
...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION]] : [],
|
|
11628
|
+
...ids.shell ? [["set-option", "-p", "-t", ids.shell, "@kobe_role", SHELL_PANE_ROLE]] : []
|
|
11579
11629
|
]);
|
|
11580
11630
|
if (ids.ops) {
|
|
11581
11631
|
const rcArgs = await globalRightColumnResizeArgs();
|
|
@@ -11700,6 +11750,644 @@ var init_chattab = __esm(() => {
|
|
|
11700
11750
|
CHAT_TAB_STATUS_CURRENT_FORMAT = CHAT_TAB_STATUS_FORMAT;
|
|
11701
11751
|
});
|
|
11702
11752
|
|
|
11753
|
+
// src/tui/panes/terminal/layout-actions.ts
|
|
11754
|
+
function parseLayoutPaneRows(stdout) {
|
|
11755
|
+
const rows = [];
|
|
11756
|
+
for (const raw of stdout.split(`
|
|
11757
|
+
`)) {
|
|
11758
|
+
const line = raw.trim();
|
|
11759
|
+
if (!line)
|
|
11760
|
+
continue;
|
|
11761
|
+
const [paneId, role, active, paneWidth, paneHeight, windowWidth, windowHeight] = line.split("\t");
|
|
11762
|
+
if (!paneId)
|
|
11763
|
+
continue;
|
|
11764
|
+
const width = Number.parseInt(paneWidth ?? "", 10);
|
|
11765
|
+
const height = Number.parseInt(paneHeight ?? "", 10);
|
|
11766
|
+
const winW = Number.parseInt(windowWidth ?? "", 10);
|
|
11767
|
+
const winH = Number.parseInt(windowHeight ?? "", 10);
|
|
11768
|
+
rows.push({
|
|
11769
|
+
paneId: paneId.trim(),
|
|
11770
|
+
role: role?.trim() ?? "",
|
|
11771
|
+
active: active?.trim() === "1",
|
|
11772
|
+
paneWidth: Number.isFinite(width) ? width : 0,
|
|
11773
|
+
paneHeight: Number.isFinite(height) ? height : 0,
|
|
11774
|
+
windowWidth: Number.isFinite(winW) ? winW : 0,
|
|
11775
|
+
windowHeight: Number.isFinite(winH) ? winH : 0
|
|
11776
|
+
});
|
|
11777
|
+
}
|
|
11778
|
+
return rows;
|
|
11779
|
+
}
|
|
11780
|
+
function planWorkspaceSplit(rows) {
|
|
11781
|
+
const engine = rows.find((row) => row.role === ENGINE_PANE_ROLE);
|
|
11782
|
+
if (!engine)
|
|
11783
|
+
return { kind: "missing-engine" };
|
|
11784
|
+
const aux = rows.filter((row) => row.role === WORKSPACE_AUX_PANE_ROLE);
|
|
11785
|
+
if (aux.length + 1 >= WORKSPACE_SPLIT_MAX_PANES)
|
|
11786
|
+
return { kind: "maxed" };
|
|
11787
|
+
if (aux.length === 0)
|
|
11788
|
+
return { kind: "split", targetPane: engine.paneId, direction: "-h" };
|
|
11789
|
+
if (aux.length === 1)
|
|
11790
|
+
return { kind: "split", targetPane: aux[0]?.paneId ?? engine.paneId, direction: "-v" };
|
|
11791
|
+
return { kind: "split", targetPane: engine.paneId, direction: "-v" };
|
|
11792
|
+
}
|
|
11793
|
+
function resolveShellPane(rows) {
|
|
11794
|
+
return rows.find((row) => row.role === SHELL_PANE_ROLE) ?? rows.find((row) => row.role === "");
|
|
11795
|
+
}
|
|
11796
|
+
function expandedTerminalHeightPercent(rawOpsHeightPercent) {
|
|
11797
|
+
const opsPct = clampPanePercent(rawOpsHeightPercent ?? Number.NaN) ?? OPS_PANE_PERCENT;
|
|
11798
|
+
return 100 - opsPct;
|
|
11799
|
+
}
|
|
11800
|
+
function parsePositiveInt(raw) {
|
|
11801
|
+
const n = Number.parseInt(raw ?? "", 10);
|
|
11802
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
11803
|
+
}
|
|
11804
|
+
async function windowPanes(session, windowId) {
|
|
11805
|
+
const target = windowId?.trim() || `=${session}`;
|
|
11806
|
+
const { code, stdout } = await runTmuxCapturing(["list-panes", "-t", target, "-F", ACTIVE_WINDOW_LAYOUT_FORMAT]);
|
|
11807
|
+
return code === 0 ? parseLayoutPaneRows(stdout) : null;
|
|
11808
|
+
}
|
|
11809
|
+
async function activeWindowId(session) {
|
|
11810
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
11811
|
+
"list-windows",
|
|
11812
|
+
"-t",
|
|
11813
|
+
`=${session}`,
|
|
11814
|
+
"-F",
|
|
11815
|
+
"#{window_active}\t#{window_id}"
|
|
11816
|
+
]);
|
|
11817
|
+
if (code !== 0)
|
|
11818
|
+
return "";
|
|
11819
|
+
for (const line of stdout.split(`
|
|
11820
|
+
`)) {
|
|
11821
|
+
const [active, windowId] = line.split("\t");
|
|
11822
|
+
if (active?.trim() === "1" && windowId?.trim())
|
|
11823
|
+
return windowId.trim();
|
|
11824
|
+
}
|
|
11825
|
+
return "";
|
|
11826
|
+
}
|
|
11827
|
+
async function windowOption(target, option) {
|
|
11828
|
+
const { code, stdout } = await runTmuxCapturing(["show-options", "-wqv", "-t", target, option]);
|
|
11829
|
+
return code === 0 ? stdout.trim() : "";
|
|
11830
|
+
}
|
|
11831
|
+
async function setActiveWindowOption(windowId, option, value) {
|
|
11832
|
+
await runTmux(["set-window-option", "-t", windowId, option, value]);
|
|
11833
|
+
}
|
|
11834
|
+
async function clearActiveWindowOption(windowId, option) {
|
|
11835
|
+
await runTmux(["set-window-option", "-u", "-t", windowId, option]);
|
|
11836
|
+
}
|
|
11837
|
+
async function paneExists(paneId) {
|
|
11838
|
+
const { code, stdout } = await runTmuxCapturing(["display-message", "-p", "-t", paneId, "#{pane_id}"]);
|
|
11839
|
+
return code === 0 && stdout.trim() === paneId;
|
|
11840
|
+
}
|
|
11841
|
+
async function display(session, message) {
|
|
11842
|
+
await runTmux(["display-message", "-t", session, message]);
|
|
11843
|
+
}
|
|
11844
|
+
async function sessionWorktree(session) {
|
|
11845
|
+
const opts = await getSessionOptions(session, ["@kobe_worktree", "@kobe_task", "@kobe_vendor"]);
|
|
11846
|
+
return {
|
|
11847
|
+
cwd: opts["@kobe_worktree"] || process.cwd(),
|
|
11848
|
+
taskId: opts["@kobe_task"] || undefined,
|
|
11849
|
+
vendor: opts["@kobe_vendor"] || undefined
|
|
11850
|
+
};
|
|
11851
|
+
}
|
|
11852
|
+
function opsPaneLaunchCommand(args) {
|
|
11853
|
+
const inv = kobeCliInvocation();
|
|
11854
|
+
const envPrefix = inheritedEnvPrefix();
|
|
11855
|
+
return keepAlive(envPrefix + opsPaneCommand({
|
|
11856
|
+
cwd: args.cwd,
|
|
11857
|
+
taskId: args.taskId,
|
|
11858
|
+
claudePaneId: args.enginePaneId,
|
|
11859
|
+
cliInvocation: inv,
|
|
11860
|
+
vendor: args.vendor
|
|
11861
|
+
}));
|
|
11862
|
+
}
|
|
11863
|
+
async function resolveActionWindowId(session, windowId) {
|
|
11864
|
+
return windowId?.trim() || await activeWindowId(session);
|
|
11865
|
+
}
|
|
11866
|
+
async function addWorkspaceSplit(session, windowId) {
|
|
11867
|
+
const rows = await windowPanes(session, windowId);
|
|
11868
|
+
if (!rows)
|
|
11869
|
+
return;
|
|
11870
|
+
const plan = planWorkspaceSplit(rows);
|
|
11871
|
+
if (plan.kind === "missing-engine") {
|
|
11872
|
+
await display(windowId, "kobe: no engine pane in this window");
|
|
11873
|
+
return;
|
|
11874
|
+
}
|
|
11875
|
+
if (plan.kind === "maxed") {
|
|
11876
|
+
await display(windowId, `kobe: workspace split limit is ${WORKSPACE_SPLIT_MAX_PANES} panes`);
|
|
11877
|
+
return;
|
|
11878
|
+
}
|
|
11879
|
+
const { cwd } = await sessionWorktree(session);
|
|
11880
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
11881
|
+
"split-window",
|
|
11882
|
+
plan.direction,
|
|
11883
|
+
"-t",
|
|
11884
|
+
plan.targetPane,
|
|
11885
|
+
"-l",
|
|
11886
|
+
"50%",
|
|
11887
|
+
"-c",
|
|
11888
|
+
localSpawnCwd(cwd),
|
|
11889
|
+
"-P",
|
|
11890
|
+
"-F",
|
|
11891
|
+
"#{pane_id}",
|
|
11892
|
+
keepAlive("true")
|
|
11893
|
+
]);
|
|
11894
|
+
const paneId = stdout.trim();
|
|
11895
|
+
if (code !== 0 || !paneId)
|
|
11896
|
+
return;
|
|
11897
|
+
await runTmuxSequence([
|
|
11898
|
+
["set-option", "-p", "-t", paneId, "@kobe_role", WORKSPACE_AUX_PANE_ROLE],
|
|
11899
|
+
["select-pane", "-t", paneId]
|
|
11900
|
+
]);
|
|
11901
|
+
}
|
|
11902
|
+
async function closeWorkspaceSplit(session, windowId) {
|
|
11903
|
+
const rows = await windowPanes(session, windowId);
|
|
11904
|
+
if (!rows)
|
|
11905
|
+
return;
|
|
11906
|
+
const aux = rows.filter((row) => row.role === WORKSPACE_AUX_PANE_ROLE);
|
|
11907
|
+
if (aux.length === 0) {
|
|
11908
|
+
await display(windowId, "kobe: no workspace split to close");
|
|
11909
|
+
return;
|
|
11910
|
+
}
|
|
11911
|
+
const activeAux = aux.find((row) => row.active);
|
|
11912
|
+
const target = activeAux ?? aux[aux.length - 1];
|
|
11913
|
+
if (!target)
|
|
11914
|
+
return;
|
|
11915
|
+
await runTmux(["kill-pane", "-t", target.paneId]);
|
|
11916
|
+
}
|
|
11917
|
+
async function resetWorkspaceSplits(session, windowId) {
|
|
11918
|
+
const rows = await windowPanes(session, windowId);
|
|
11919
|
+
if (!rows)
|
|
11920
|
+
return;
|
|
11921
|
+
const aux = rows.filter((row) => row.role === WORKSPACE_AUX_PANE_ROLE);
|
|
11922
|
+
if (aux.length === 0) {
|
|
11923
|
+
await display(windowId, "kobe: no workspace splits to reset");
|
|
11924
|
+
return;
|
|
11925
|
+
}
|
|
11926
|
+
await runTmuxSequence(aux.map((row) => ["kill-pane", "-t", row.paneId]));
|
|
11927
|
+
}
|
|
11928
|
+
async function preferredTasksWidth() {
|
|
11929
|
+
const opts = await getServerOptions([TASKS_WIDTH_OPTION]);
|
|
11930
|
+
return clampTasksPaneWidth(parsePositiveInt(opts[TASKS_WIDTH_OPTION]) ?? TASKS_PANE_WIDTH);
|
|
11931
|
+
}
|
|
11932
|
+
async function tasksPaneLaunchCommand(session) {
|
|
11933
|
+
const { cwd, taskId } = await sessionWorktree(session);
|
|
11934
|
+
const inv = kobeCliInvocation();
|
|
11935
|
+
const envPrefix = inheritedEnvPrefix();
|
|
11936
|
+
return {
|
|
11937
|
+
cwd,
|
|
11938
|
+
command: keepAlive(envPrefix + tasksPaneCommand(inv, { initialTaskId: taskId }))
|
|
11939
|
+
};
|
|
11940
|
+
}
|
|
11941
|
+
async function toggleOpsPane(session, windowId) {
|
|
11942
|
+
const rows = await windowPanes(session, windowId);
|
|
11943
|
+
if (!rows)
|
|
11944
|
+
return;
|
|
11945
|
+
const ops = rows.find((row) => row.role === OPS_PANE_ROLE);
|
|
11946
|
+
if (ops) {
|
|
11947
|
+
await runTmux(["kill-pane", "-t", ops.paneId]);
|
|
11948
|
+
return;
|
|
11949
|
+
}
|
|
11950
|
+
const shell = resolveShellPane(rows);
|
|
11951
|
+
const engine = rows.find((row) => row.role === ENGINE_PANE_ROLE);
|
|
11952
|
+
if (!engine) {
|
|
11953
|
+
await display(windowId, "kobe: cannot restore file pane in this layout");
|
|
11954
|
+
return;
|
|
11955
|
+
}
|
|
11956
|
+
const { cwd, taskId, vendor } = await sessionWorktree(session);
|
|
11957
|
+
const opsCmd = opsPaneLaunchCommand({ cwd, taskId, vendor, enginePaneId: engine.paneId });
|
|
11958
|
+
const splitArgs = shell ? [
|
|
11959
|
+
"split-window",
|
|
11960
|
+
"-v",
|
|
11961
|
+
"-b",
|
|
11962
|
+
"-t",
|
|
11963
|
+
shell.paneId,
|
|
11964
|
+
"-l",
|
|
11965
|
+
`${await preferredOpsHeightPercent()}%`,
|
|
11966
|
+
"-c",
|
|
11967
|
+
localSpawnCwd(cwd),
|
|
11968
|
+
"-P",
|
|
11969
|
+
"-F",
|
|
11970
|
+
"#{pane_id}",
|
|
11971
|
+
opsCmd
|
|
11972
|
+
] : [
|
|
11973
|
+
"split-window",
|
|
11974
|
+
"-h",
|
|
11975
|
+
"-t",
|
|
11976
|
+
engine.paneId,
|
|
11977
|
+
"-l",
|
|
11978
|
+
`${await preferredRightColumnWidthPercent()}%`,
|
|
11979
|
+
"-c",
|
|
11980
|
+
localSpawnCwd(cwd),
|
|
11981
|
+
"-P",
|
|
11982
|
+
"-F",
|
|
11983
|
+
"#{pane_id}",
|
|
11984
|
+
opsCmd
|
|
11985
|
+
];
|
|
11986
|
+
const { code, stdout } = await runTmuxCapturing(splitArgs);
|
|
11987
|
+
const paneId = stdout.trim();
|
|
11988
|
+
if (code !== 0 || !paneId)
|
|
11989
|
+
return;
|
|
11990
|
+
const commands = [
|
|
11991
|
+
["set-option", "-p", "-t", paneId, "@kobe_role", OPS_PANE_ROLE],
|
|
11992
|
+
["set-option", "-p", "-t", paneId, PANE_VERSION_OPTION, CURRENT_VERSION]
|
|
11993
|
+
];
|
|
11994
|
+
if (shell?.role === "")
|
|
11995
|
+
commands.push(["set-option", "-p", "-t", shell.paneId, "@kobe_role", SHELL_PANE_ROLE]);
|
|
11996
|
+
await runTmuxSequence(commands);
|
|
11997
|
+
}
|
|
11998
|
+
async function preferredOpsHeightPercent() {
|
|
11999
|
+
const opts = await getServerOptions([OPS_HEIGHT_OPTION]);
|
|
12000
|
+
return clampPanePercent(Number.parseInt(opts[OPS_HEIGHT_OPTION] ?? "", 10)) ?? OPS_PANE_PERCENT;
|
|
12001
|
+
}
|
|
12002
|
+
async function preferredRightColumnWidthPercent() {
|
|
12003
|
+
const opts = await getServerOptions([RIGHT_COLUMN_WIDTH_OPTION]);
|
|
12004
|
+
return clampPanePercent(Number.parseInt(opts[RIGHT_COLUMN_WIDTH_OPTION] ?? "", 10)) ?? 100 - CLAUDE_PANE_PERCENT;
|
|
12005
|
+
}
|
|
12006
|
+
async function preferredTerminalHeightPercent() {
|
|
12007
|
+
const opts = await getServerOptions([OPS_HEIGHT_OPTION]);
|
|
12008
|
+
return expandedTerminalHeightPercent(Number.parseInt(opts[OPS_HEIGHT_OPTION] ?? "", 10));
|
|
12009
|
+
}
|
|
12010
|
+
async function hiddenWindowIndices(hiddenSession) {
|
|
12011
|
+
const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${hiddenSession}`, "-F", "#{window_index}"]);
|
|
12012
|
+
if (code !== 0)
|
|
12013
|
+
return new Set;
|
|
12014
|
+
return new Set(stdout.split(`
|
|
12015
|
+
`).map((line) => Number.parseInt(line.trim(), 10)).filter((n) => Number.isFinite(n)));
|
|
12016
|
+
}
|
|
12017
|
+
async function nextHiddenWindowIndex(hiddenSession, windowId) {
|
|
12018
|
+
const used = await hiddenWindowIndices(hiddenSession);
|
|
12019
|
+
let idx = hiddenTerminalWindowIndex(windowId);
|
|
12020
|
+
while (used.has(idx))
|
|
12021
|
+
idx++;
|
|
12022
|
+
return idx;
|
|
12023
|
+
}
|
|
12024
|
+
async function ensureHiddenPaneSession(session) {
|
|
12025
|
+
const hidden = hiddenTerminalSessionName(session);
|
|
12026
|
+
if (await sessionExists(hidden))
|
|
12027
|
+
return hidden;
|
|
12028
|
+
await runTmux([
|
|
12029
|
+
"new-session",
|
|
12030
|
+
"-d",
|
|
12031
|
+
"-s",
|
|
12032
|
+
hidden,
|
|
12033
|
+
"-n",
|
|
12034
|
+
"hidden-panes",
|
|
12035
|
+
"-c",
|
|
12036
|
+
localSpawnCwd(process.cwd()),
|
|
12037
|
+
"while :; do sleep 3600; done"
|
|
12038
|
+
]);
|
|
12039
|
+
return hidden;
|
|
12040
|
+
}
|
|
12041
|
+
async function cleanupHiddenPaneSessionIfEmpty(session) {
|
|
12042
|
+
const hidden = hiddenTerminalSessionName(session);
|
|
12043
|
+
if (!await sessionExists(hidden))
|
|
12044
|
+
return;
|
|
12045
|
+
const { code, stdout } = await runTmuxCapturing(["list-panes", "-s", "-t", `=${hidden}`, "-F", "#{@kobe_role}"]);
|
|
12046
|
+
if (code !== 0)
|
|
12047
|
+
return;
|
|
12048
|
+
const hasHiddenPane = stdout.split(`
|
|
12049
|
+
`).some((line) => {
|
|
12050
|
+
const role = line.trim();
|
|
12051
|
+
return role === SHELL_PANE_ROLE || role === TASKS_PANE_ROLE;
|
|
12052
|
+
});
|
|
12053
|
+
if (!hasHiddenPane)
|
|
12054
|
+
await runTmux(["kill-session", "-t", `=${hidden}`]);
|
|
12055
|
+
}
|
|
12056
|
+
async function cleanupHiddenTerminalForWindow(session, windowId) {
|
|
12057
|
+
await cleanupHiddenPaneForWindow(session, windowId, HIDDEN_TERMINAL_PANE_OPTION);
|
|
12058
|
+
}
|
|
12059
|
+
async function cleanupHiddenTasksForWindow(session, windowId) {
|
|
12060
|
+
await cleanupHiddenPaneForWindow(session, windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12061
|
+
}
|
|
12062
|
+
async function cleanupHiddenPaneForWindow(session, windowId, option) {
|
|
12063
|
+
const hiddenPane = await windowOption(windowId, option);
|
|
12064
|
+
if (!hiddenPane)
|
|
12065
|
+
return;
|
|
12066
|
+
if (await paneExists(hiddenPane)) {
|
|
12067
|
+
await runTmux(["kill-pane", "-t", hiddenPane]);
|
|
12068
|
+
}
|
|
12069
|
+
await clearActiveWindowOption(windowId, option);
|
|
12070
|
+
}
|
|
12071
|
+
async function cleanupHiddenPanesForWindow(session, windowId) {
|
|
12072
|
+
await cleanupHiddenTerminalForWindow(session, windowId);
|
|
12073
|
+
await cleanupHiddenTasksForWindow(session, windowId);
|
|
12074
|
+
await cleanupHiddenPaneSessionIfEmpty(session);
|
|
12075
|
+
}
|
|
12076
|
+
function restoreTasksTarget(rows) {
|
|
12077
|
+
return rows.find((row) => row.role === ENGINE_PANE_ROLE) ?? rows[0];
|
|
12078
|
+
}
|
|
12079
|
+
async function createTasksPane(session, windowId, rows) {
|
|
12080
|
+
const target = restoreTasksTarget(rows);
|
|
12081
|
+
if (!target) {
|
|
12082
|
+
await display(windowId, "kobe: cannot restore Tasks pane in this layout");
|
|
12083
|
+
return false;
|
|
12084
|
+
}
|
|
12085
|
+
const width = await preferredTasksWidth();
|
|
12086
|
+
const { cwd, command } = await tasksPaneLaunchCommand(session);
|
|
12087
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
12088
|
+
"split-window",
|
|
12089
|
+
"-h",
|
|
12090
|
+
"-b",
|
|
12091
|
+
"-t",
|
|
12092
|
+
target.paneId,
|
|
12093
|
+
"-l",
|
|
12094
|
+
`${width}`,
|
|
12095
|
+
"-c",
|
|
12096
|
+
localSpawnCwd(cwd),
|
|
12097
|
+
"-P",
|
|
12098
|
+
"-F",
|
|
12099
|
+
"#{pane_id}",
|
|
12100
|
+
command
|
|
12101
|
+
]);
|
|
12102
|
+
const paneId = stdout.trim();
|
|
12103
|
+
if (code !== 0 || !paneId)
|
|
12104
|
+
return false;
|
|
12105
|
+
await runTmuxSequence([
|
|
12106
|
+
["set-option", "-p", "-t", paneId, "@kobe_role", TASKS_PANE_ROLE],
|
|
12107
|
+
["set-option", "-p", "-t", paneId, PANE_VERSION_OPTION, CURRENT_VERSION],
|
|
12108
|
+
["select-pane", "-t", paneId]
|
|
12109
|
+
]);
|
|
12110
|
+
await clearActiveWindowOption(windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12111
|
+
await display(windowId, "kobe: Tasks pane restored");
|
|
12112
|
+
return true;
|
|
12113
|
+
}
|
|
12114
|
+
async function hideTasksPane(session, windowId, tasks) {
|
|
12115
|
+
const hidden = await ensureHiddenPaneSession(session);
|
|
12116
|
+
const hiddenIndex = await nextHiddenWindowIndex(hidden, windowId);
|
|
12117
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
12118
|
+
"break-pane",
|
|
12119
|
+
"-d",
|
|
12120
|
+
"-s",
|
|
12121
|
+
tasks.paneId,
|
|
12122
|
+
"-t",
|
|
12123
|
+
`${hidden}:${hiddenIndex}`,
|
|
12124
|
+
"-P",
|
|
12125
|
+
"-F",
|
|
12126
|
+
"#{pane_id}"
|
|
12127
|
+
]);
|
|
12128
|
+
const hiddenPane = stdout.trim() || tasks.paneId;
|
|
12129
|
+
if (code !== 0 || !hiddenPane) {
|
|
12130
|
+
await display(windowId, "kobe: could not hide Tasks pane");
|
|
12131
|
+
return;
|
|
12132
|
+
}
|
|
12133
|
+
await setActiveWindowOption(windowId, HIDDEN_TASKS_PANE_OPTION, hiddenPane);
|
|
12134
|
+
await display(windowId, "kobe: Tasks pane hidden");
|
|
12135
|
+
}
|
|
12136
|
+
async function restoreHiddenTasksPane(session, windowId, hiddenPane) {
|
|
12137
|
+
const rows = await windowPanes(session, windowId);
|
|
12138
|
+
if (!rows)
|
|
12139
|
+
return;
|
|
12140
|
+
if (!await paneExists(hiddenPane)) {
|
|
12141
|
+
await clearActiveWindowOption(windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12142
|
+
const visibleTasks = rows.find((row) => row.role === TASKS_PANE_ROLE);
|
|
12143
|
+
if (visibleTasks) {
|
|
12144
|
+
await runTmux(["select-pane", "-t", visibleTasks.paneId]);
|
|
12145
|
+
return;
|
|
12146
|
+
}
|
|
12147
|
+
await createTasksPane(session, windowId, rows);
|
|
12148
|
+
return;
|
|
12149
|
+
}
|
|
12150
|
+
const target = restoreTasksTarget(rows);
|
|
12151
|
+
if (!target) {
|
|
12152
|
+
await display(windowId, "kobe: cannot restore Tasks pane in this layout");
|
|
12153
|
+
return;
|
|
12154
|
+
}
|
|
12155
|
+
const code = await runTmux([
|
|
12156
|
+
"join-pane",
|
|
12157
|
+
"-h",
|
|
12158
|
+
"-b",
|
|
12159
|
+
"-s",
|
|
12160
|
+
hiddenPane,
|
|
12161
|
+
"-t",
|
|
12162
|
+
target.paneId,
|
|
12163
|
+
"-l",
|
|
12164
|
+
`${await preferredTasksWidth()}`
|
|
12165
|
+
]);
|
|
12166
|
+
if (code !== 0) {
|
|
12167
|
+
await display(windowId, "kobe: could not restore Tasks pane");
|
|
12168
|
+
return;
|
|
12169
|
+
}
|
|
12170
|
+
await runTmuxSequence([
|
|
12171
|
+
["set-option", "-p", "-t", hiddenPane, "@kobe_role", TASKS_PANE_ROLE],
|
|
12172
|
+
["set-option", "-p", "-t", hiddenPane, PANE_VERSION_OPTION, CURRENT_VERSION],
|
|
12173
|
+
["select-pane", "-t", hiddenPane]
|
|
12174
|
+
]);
|
|
12175
|
+
await clearActiveWindowOption(windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12176
|
+
await cleanupHiddenPaneSessionIfEmpty(session);
|
|
12177
|
+
await display(windowId, "kobe: Tasks pane restored");
|
|
12178
|
+
}
|
|
12179
|
+
async function toggleTasksPane(session, windowId) {
|
|
12180
|
+
const hiddenPane = await windowOption(windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12181
|
+
if (hiddenPane) {
|
|
12182
|
+
await restoreHiddenTasksPane(session, windowId, hiddenPane);
|
|
12183
|
+
return;
|
|
12184
|
+
}
|
|
12185
|
+
const rows = await windowPanes(session, windowId);
|
|
12186
|
+
if (!rows)
|
|
12187
|
+
return;
|
|
12188
|
+
const tasks = rows.find((row) => row.role === TASKS_PANE_ROLE);
|
|
12189
|
+
if (tasks) {
|
|
12190
|
+
await hideTasksPane(session, windowId, tasks);
|
|
12191
|
+
return;
|
|
12192
|
+
}
|
|
12193
|
+
await createTasksPane(session, windowId, rows);
|
|
12194
|
+
}
|
|
12195
|
+
async function restoreTasksPane(session, windowId) {
|
|
12196
|
+
const hiddenPane = await windowOption(windowId, HIDDEN_TASKS_PANE_OPTION);
|
|
12197
|
+
if (hiddenPane) {
|
|
12198
|
+
await restoreHiddenTasksPane(session, windowId, hiddenPane);
|
|
12199
|
+
return;
|
|
12200
|
+
}
|
|
12201
|
+
const rows = await windowPanes(session, windowId);
|
|
12202
|
+
if (!rows)
|
|
12203
|
+
return;
|
|
12204
|
+
const tasks = rows.find((row) => row.role === TASKS_PANE_ROLE);
|
|
12205
|
+
if (tasks) {
|
|
12206
|
+
await runTmux(["select-pane", "-t", tasks.paneId]);
|
|
12207
|
+
return;
|
|
12208
|
+
}
|
|
12209
|
+
await createTasksPane(session, windowId, rows);
|
|
12210
|
+
}
|
|
12211
|
+
async function hideTerminalPane(session, windowId, shell) {
|
|
12212
|
+
if (shell.role === "") {
|
|
12213
|
+
await runTmux(["set-option", "-p", "-t", shell.paneId, "@kobe_role", SHELL_PANE_ROLE]);
|
|
12214
|
+
}
|
|
12215
|
+
const hidden = await ensureHiddenPaneSession(session);
|
|
12216
|
+
const hiddenIndex = await nextHiddenWindowIndex(hidden, windowId);
|
|
12217
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
12218
|
+
"break-pane",
|
|
12219
|
+
"-d",
|
|
12220
|
+
"-s",
|
|
12221
|
+
shell.paneId,
|
|
12222
|
+
"-t",
|
|
12223
|
+
`${hidden}:${hiddenIndex}`,
|
|
12224
|
+
"-P",
|
|
12225
|
+
"-F",
|
|
12226
|
+
"#{pane_id}"
|
|
12227
|
+
]);
|
|
12228
|
+
const hiddenPane = stdout.trim() || shell.paneId;
|
|
12229
|
+
if (code !== 0 || !hiddenPane) {
|
|
12230
|
+
await display(windowId, "kobe: could not hide terminal pane");
|
|
12231
|
+
return;
|
|
12232
|
+
}
|
|
12233
|
+
await setActiveWindowOption(windowId, HIDDEN_TERMINAL_PANE_OPTION, hiddenPane);
|
|
12234
|
+
await display(windowId, "kobe: Terminal pane hidden");
|
|
12235
|
+
}
|
|
12236
|
+
async function createTerminalPane(session, windowId, rows) {
|
|
12237
|
+
const ops = rows.find((row) => row.role === OPS_PANE_ROLE);
|
|
12238
|
+
const engine = rows.find((row) => row.role === ENGINE_PANE_ROLE);
|
|
12239
|
+
if (!ops && !engine) {
|
|
12240
|
+
await display(windowId, "kobe: cannot restore terminal pane in this layout");
|
|
12241
|
+
return false;
|
|
12242
|
+
}
|
|
12243
|
+
const { cwd } = await sessionWorktree(session);
|
|
12244
|
+
const splitArgs = ops ? [
|
|
12245
|
+
"split-window",
|
|
12246
|
+
"-v",
|
|
12247
|
+
"-t",
|
|
12248
|
+
ops.paneId,
|
|
12249
|
+
"-l",
|
|
12250
|
+
`${await preferredTerminalHeightPercent()}%`,
|
|
12251
|
+
"-c",
|
|
12252
|
+
localSpawnCwd(cwd),
|
|
12253
|
+
"-P",
|
|
12254
|
+
"-F",
|
|
12255
|
+
"#{pane_id}"
|
|
12256
|
+
] : [
|
|
12257
|
+
"split-window",
|
|
12258
|
+
"-h",
|
|
12259
|
+
"-t",
|
|
12260
|
+
engine?.paneId ?? "",
|
|
12261
|
+
"-l",
|
|
12262
|
+
`${await preferredRightColumnWidthPercent()}%`,
|
|
12263
|
+
"-c",
|
|
12264
|
+
localSpawnCwd(cwd),
|
|
12265
|
+
"-P",
|
|
12266
|
+
"-F",
|
|
12267
|
+
"#{pane_id}"
|
|
12268
|
+
];
|
|
12269
|
+
const { code, stdout } = await runTmuxCapturing(splitArgs);
|
|
12270
|
+
const paneId = stdout.trim();
|
|
12271
|
+
if (code !== 0 || !paneId)
|
|
12272
|
+
return false;
|
|
12273
|
+
await runTmux(["set-option", "-p", "-t", paneId, "@kobe_role", SHELL_PANE_ROLE]);
|
|
12274
|
+
await clearActiveWindowOption(windowId, HIDDEN_TERMINAL_PANE_OPTION);
|
|
12275
|
+
await display(windowId, "kobe: Terminal pane restored");
|
|
12276
|
+
return true;
|
|
12277
|
+
}
|
|
12278
|
+
async function restoreHiddenTerminalPane(session, windowId, hiddenPane) {
|
|
12279
|
+
let rows = await windowPanes(session, windowId);
|
|
12280
|
+
if (!rows)
|
|
12281
|
+
return;
|
|
12282
|
+
if (!await paneExists(hiddenPane)) {
|
|
12283
|
+
await clearActiveWindowOption(windowId, HIDDEN_TERMINAL_PANE_OPTION);
|
|
12284
|
+
await createTerminalPane(session, windowId, rows);
|
|
12285
|
+
return;
|
|
12286
|
+
}
|
|
12287
|
+
const ops = rows.find((row) => row.role === OPS_PANE_ROLE);
|
|
12288
|
+
const engine = rows.find((row) => row.role === ENGINE_PANE_ROLE);
|
|
12289
|
+
if (!ops && !engine) {
|
|
12290
|
+
await display(windowId, "kobe: cannot restore terminal pane in this layout");
|
|
12291
|
+
return;
|
|
12292
|
+
}
|
|
12293
|
+
const joinArgs = ops ? ["join-pane", "-v", "-s", hiddenPane, "-t", ops.paneId, "-l", `${await preferredTerminalHeightPercent()}%`] : [
|
|
12294
|
+
"join-pane",
|
|
12295
|
+
"-h",
|
|
12296
|
+
"-s",
|
|
12297
|
+
hiddenPane,
|
|
12298
|
+
"-t",
|
|
12299
|
+
engine?.paneId ?? "",
|
|
12300
|
+
"-l",
|
|
12301
|
+
`${await preferredRightColumnWidthPercent()}%`
|
|
12302
|
+
];
|
|
12303
|
+
const code = await runTmux(joinArgs);
|
|
12304
|
+
if (code !== 0) {
|
|
12305
|
+
await display(windowId, "kobe: could not restore terminal pane");
|
|
12306
|
+
return;
|
|
12307
|
+
}
|
|
12308
|
+
await runTmux(["set-option", "-p", "-t", hiddenPane, "@kobe_role", SHELL_PANE_ROLE]);
|
|
12309
|
+
await clearActiveWindowOption(windowId, HIDDEN_TERMINAL_PANE_OPTION);
|
|
12310
|
+
await cleanupHiddenPaneSessionIfEmpty(session);
|
|
12311
|
+
rows = await windowPanes(session, windowId);
|
|
12312
|
+
const restored = rows?.find((row) => row.paneId === hiddenPane);
|
|
12313
|
+
if (restored)
|
|
12314
|
+
await runTmux(["select-pane", "-t", restored.paneId]);
|
|
12315
|
+
await display(windowId, "kobe: Terminal pane restored");
|
|
12316
|
+
}
|
|
12317
|
+
async function toggleTerminalPane(session, windowId) {
|
|
12318
|
+
const hiddenPane = await windowOption(windowId, HIDDEN_TERMINAL_PANE_OPTION);
|
|
12319
|
+
if (hiddenPane) {
|
|
12320
|
+
await restoreHiddenTerminalPane(session, windowId, hiddenPane);
|
|
12321
|
+
return;
|
|
12322
|
+
}
|
|
12323
|
+
const rows = await windowPanes(session, windowId);
|
|
12324
|
+
if (!rows)
|
|
12325
|
+
return;
|
|
12326
|
+
const shell = resolveShellPane(rows);
|
|
12327
|
+
if (shell) {
|
|
12328
|
+
await hideTerminalPane(session, windowId, shell);
|
|
12329
|
+
return;
|
|
12330
|
+
}
|
|
12331
|
+
await createTerminalPane(session, windowId, rows);
|
|
12332
|
+
}
|
|
12333
|
+
async function activeSessionWindowCount(session) {
|
|
12334
|
+
const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${session}`, "-F", "#{window_id}"]);
|
|
12335
|
+
if (code !== 0)
|
|
12336
|
+
return 0;
|
|
12337
|
+
return stdout.split(`
|
|
12338
|
+
`).filter((line) => line.trim().length > 0).length;
|
|
12339
|
+
}
|
|
12340
|
+
async function closeChatTab(session, windowId) {
|
|
12341
|
+
if (await activeSessionWindowCount(session) <= 1) {
|
|
12342
|
+
await display(windowId, "Cannot close the only ChatTab");
|
|
12343
|
+
return;
|
|
12344
|
+
}
|
|
12345
|
+
await cleanupHiddenPanesForWindow(session, windowId);
|
|
12346
|
+
await runTmux(["kill-window", "-t", windowId]);
|
|
12347
|
+
}
|
|
12348
|
+
async function runLayoutAction(session, action, opts = {}) {
|
|
12349
|
+
if (!await sessionExists(session))
|
|
12350
|
+
return;
|
|
12351
|
+
const windowId = await resolveActionWindowId(session, opts.windowId);
|
|
12352
|
+
if (!windowId)
|
|
12353
|
+
return;
|
|
12354
|
+
switch (action) {
|
|
12355
|
+
case "workspace-split":
|
|
12356
|
+
await addWorkspaceSplit(session, windowId);
|
|
12357
|
+
return;
|
|
12358
|
+
case "workspace-close":
|
|
12359
|
+
await closeWorkspaceSplit(session, windowId);
|
|
12360
|
+
return;
|
|
12361
|
+
case "workspace-reset":
|
|
12362
|
+
await resetWorkspaceSplits(session, windowId);
|
|
12363
|
+
return;
|
|
12364
|
+
case "tasks-toggle":
|
|
12365
|
+
await toggleTasksPane(session, windowId);
|
|
12366
|
+
return;
|
|
12367
|
+
case "tasks-restore":
|
|
12368
|
+
await restoreTasksPane(session, windowId);
|
|
12369
|
+
return;
|
|
12370
|
+
case "ops-toggle":
|
|
12371
|
+
await toggleOpsPane(session, windowId);
|
|
12372
|
+
return;
|
|
12373
|
+
case "terminal-toggle":
|
|
12374
|
+
await toggleTerminalPane(session, windowId);
|
|
12375
|
+
return;
|
|
12376
|
+
case "chat-tab-close":
|
|
12377
|
+
await closeChatTab(session, windowId);
|
|
12378
|
+
return;
|
|
12379
|
+
}
|
|
12380
|
+
}
|
|
12381
|
+
var ACTIVE_WINDOW_LAYOUT_FORMAT = "#{pane_id}\t#{@kobe_role}\t#{pane_active}\t#{pane_width}\t#{pane_height}\t#{window_width}\t#{window_height}";
|
|
12382
|
+
var init_layout_actions = __esm(() => {
|
|
12383
|
+
init_invocation();
|
|
12384
|
+
init_resolve();
|
|
12385
|
+
init_client2();
|
|
12386
|
+
init_version();
|
|
12387
|
+
init_launch();
|
|
12388
|
+
init_pane_heal();
|
|
12389
|
+
});
|
|
12390
|
+
|
|
11703
12391
|
// src/tui/panes/terminal/tmux.ts
|
|
11704
12392
|
var exports_tmux = {};
|
|
11705
12393
|
__export(exports_tmux, {
|
|
@@ -11709,6 +12397,7 @@ __export(exports_tmux, {
|
|
|
11709
12397
|
switchClientBeforeKill: () => switchClientBeforeKill,
|
|
11710
12398
|
sessionExists: () => sessionExists,
|
|
11711
12399
|
selectTasksPane: () => selectTasksPane,
|
|
12400
|
+
runLayoutAction: () => runLayoutAction,
|
|
11712
12401
|
refreshKobeWorkspacePanes: () => refreshKobeWorkspacePanes,
|
|
11713
12402
|
quickCreate: () => quickCreate,
|
|
11714
12403
|
prepareWindowForSwitch: () => prepareWindowForSwitch,
|
|
@@ -11768,16 +12457,12 @@ async function prepareWindowForSwitch(session) {
|
|
|
11768
12457
|
await runTmux(["resize-window", "-t", `=${session}`, ...sizeArgs]);
|
|
11769
12458
|
await healWorkspaceLayout(session);
|
|
11770
12459
|
}
|
|
11771
|
-
function focusBindCommand(key, dir) {
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
"-n",
|
|
11775
|
-
|
|
11776
|
-
|
|
11777
|
-
"-F",
|
|
11778
|
-
`#{?window_zoomed_flag,1,#{?${FOCUS_EDGE_VARS[dir]},,1}}`,
|
|
11779
|
-
`select-pane ${dir}`
|
|
11780
|
-
];
|
|
12460
|
+
function focusBindCommand(key, dir, edgeCommand) {
|
|
12461
|
+
const condition = `#{?window_zoomed_flag,1,#{?${FOCUS_EDGE_VARS[dir]},,1}}`;
|
|
12462
|
+
if (edgeCommand) {
|
|
12463
|
+
return ["bind-key", "-n", key, "if-shell", "-F", condition, `select-pane ${dir}`, edgeCommand];
|
|
12464
|
+
}
|
|
12465
|
+
return ["bind-key", "-n", key, "if-shell", "-F", condition, `select-pane ${dir}`];
|
|
11781
12466
|
}
|
|
11782
12467
|
async function ensureSession(opts) {
|
|
11783
12468
|
const inflight = ensureSessionLocks.get(opts.name);
|
|
@@ -11845,7 +12530,7 @@ async function ensureSessionImpl(opts) {
|
|
|
11845
12530
|
}
|
|
11846
12531
|
}
|
|
11847
12532
|
if (action.kind === "rebuild" || action.kind === "respawn-engine") {
|
|
11848
|
-
await
|
|
12533
|
+
await killSession(opts.name);
|
|
11849
12534
|
}
|
|
11850
12535
|
const inv = kobeCliInvocation();
|
|
11851
12536
|
const launch = withClaudeSessionId(opts.command, opts.vendor);
|
|
@@ -11895,14 +12580,19 @@ async function ensureSessionImpl(opts) {
|
|
|
11895
12580
|
const newChatTabCommand = `${envStr}${invStr} new-chattab --session '#{session_name}'`;
|
|
11896
12581
|
const chooseEngineCommand = `${newChatTabCommand} --vendor '%%'`;
|
|
11897
12582
|
const chooseEngineTmuxCommand = `run-shell ${shellQuote(chooseEngineCommand)}`;
|
|
11898
|
-
const focusTasksCommand = `${envStr}${invStr} focus-tasks --session '#{session_name}'`;
|
|
12583
|
+
const focusTasksCommand = `${envStr}${invStr} focus-tasks --session '#{session_name}' --window '#{window_id}'`;
|
|
11899
12584
|
const focusTasksTmuxCommand = `run-shell ${shellQuote(focusTasksCommand)}`;
|
|
12585
|
+
const layoutCommand = (action2) => `${envStr}${invStr} layout --session '#{session_name}' --window '#{window_id}' --action ${action2}`;
|
|
12586
|
+
const restoreTasksCommand = layoutCommand("tasks-restore");
|
|
12587
|
+
const restoreTasksTmuxCommand = `run-shell ${shellQuote(restoreTasksCommand)}`;
|
|
12588
|
+
const closeChatTabCommand = layoutCommand("chat-tab-close");
|
|
12589
|
+
const closeChatTabTmuxCommand = `run-shell ${shellQuote(closeChatTabCommand)}`;
|
|
11900
12590
|
const healLayoutCommand = `${envStr}${invStr} heal-layout --session '#{session_name}'`;
|
|
11901
12591
|
const healLayoutTmuxCommand = `run-shell -b ${shellQuote(healLayoutCommand)}`;
|
|
11902
12592
|
const captureLayoutCommand = `${envStr}${invStr} capture-layout --session '#{session_name}'`;
|
|
11903
12593
|
const captureLayoutTmuxCommand = `run-shell -b ${shellQuote(captureLayoutCommand)}`;
|
|
11904
12594
|
const userKeys = resolveUserTmuxKeys();
|
|
11905
|
-
const unbinds = [];
|
|
12595
|
+
const unbinds = TMUX_LEGACY_LAYOUT_ROOT_KEYS.map((key) => ["unbind-key", "-n", key]);
|
|
11906
12596
|
if (userKeys.overridden.has(TMUX_FOCUS_ID)) {
|
|
11907
12597
|
for (const chord of TMUX_FOCUS_DEFAULTS) {
|
|
11908
12598
|
const t = chordToTmuxKey(chord);
|
|
@@ -11914,16 +12604,26 @@ async function ensureSessionImpl(opts) {
|
|
|
11914
12604
|
if (id === TMUX_FOCUS_ID)
|
|
11915
12605
|
continue;
|
|
11916
12606
|
const def = TMUX_SINGLE_BINDING_DEFAULTS[id];
|
|
11917
|
-
const
|
|
12607
|
+
const isPrefix = isTmuxPrefixBindingId(id);
|
|
12608
|
+
const t = chordToTmuxKey(def, { allowBare: isPrefix });
|
|
11918
12609
|
if ("key" in t)
|
|
11919
|
-
unbinds.push(["unbind-key", "-n", t.key]);
|
|
12610
|
+
unbinds.push(isPrefix ? ["unbind-key", t.key] : ["unbind-key", "-n", t.key]);
|
|
11920
12611
|
}
|
|
11921
12612
|
const focusDirections = ["-L", "-D", "-U", "-R"];
|
|
11922
12613
|
const focusBinds = userKeys.focus.flatMap((bind, i) => {
|
|
11923
12614
|
const dir = focusDirections[i];
|
|
11924
|
-
|
|
12615
|
+
const edgeCommand = dir === "-L" ? restoreTasksTmuxCommand : undefined;
|
|
12616
|
+
return bind && dir ? [focusBindCommand(bind.key, dir, edgeCommand)] : [];
|
|
11925
12617
|
});
|
|
11926
12618
|
const b = userKeys.binds;
|
|
12619
|
+
const layoutBind = (id, action2) => {
|
|
12620
|
+
const bind = b[id];
|
|
12621
|
+
return bind ? [["bind-key", bind.key, "run-shell", layoutCommand(action2)]] : [];
|
|
12622
|
+
};
|
|
12623
|
+
const layoutChordGroup = (...ids) => {
|
|
12624
|
+
const chords = ids.map((id) => b[id]?.chord).filter((chord) => !!chord);
|
|
12625
|
+
return chords.length > 0 ? chords.join("/") : null;
|
|
12626
|
+
};
|
|
11927
12627
|
await runTmuxSequence([
|
|
11928
12628
|
["set-option", "-g", "status", "on"],
|
|
11929
12629
|
["set-window-option", "-g", "aggressive-resize", "on"],
|
|
@@ -11938,7 +12638,9 @@ async function ensureSessionImpl(opts) {
|
|
|
11938
12638
|
kobeStatusRight({
|
|
11939
12639
|
focusLeft: userKeys.focus[0]?.key ?? null,
|
|
11940
12640
|
detach: b["tmux.detach"]?.key ?? null,
|
|
11941
|
-
newTab: b["tmux.tab.new"]?.key ?? null
|
|
12641
|
+
newTab: b["tmux.tab.new"]?.key ?? null,
|
|
12642
|
+
layoutSplits: layoutChordGroup("tmux.layout.workspaceSplit", "tmux.layout.workspaceClose", "tmux.layout.workspaceReset"),
|
|
12643
|
+
layoutPanes: layoutChordGroup("tmux.layout.tasksToggle", "tmux.layout.opsToggle", "tmux.layout.terminalToggle")
|
|
11942
12644
|
})
|
|
11943
12645
|
],
|
|
11944
12646
|
["set-option", "-g", "mouse", "on"],
|
|
@@ -11952,7 +12654,7 @@ async function ensureSessionImpl(opts) {
|
|
|
11952
12654
|
b["tmux.detach"].key,
|
|
11953
12655
|
"if-shell",
|
|
11954
12656
|
"-F",
|
|
11955
|
-
|
|
12657
|
+
`#{?#{${HIDDEN_TASKS_PANE_OPTION}},1,#{==:#{@kobe_role},tasks}}`,
|
|
11956
12658
|
"detach-client",
|
|
11957
12659
|
focusTasksTmuxCommand
|
|
11958
12660
|
]
|
|
@@ -11961,8 +12663,14 @@ async function ensureSessionImpl(opts) {
|
|
|
11961
12663
|
...b["tmux.tab.new"] ? [["bind-key", "-n", b["tmux.tab.new"].key, "run-shell", newChatTabCommand]] : [],
|
|
11962
12664
|
...b["tmux.tab.chooseEngine"] ? chatTabChooseEngineBindings(b["tmux.tab.chooseEngine"].key).map((binding) => [...binding, chooseEngineTmuxCommand]) : [],
|
|
11963
12665
|
...b["tmux.tab.prev"] && b["tmux.tab.next"] ? chatTabSwitchBindings(b["tmux.tab.prev"].key, b["tmux.tab.next"].key) : b["tmux.tab.prev"] ? [["bind-key", "-n", b["tmux.tab.prev"].key, "previous-window"]] : b["tmux.tab.next"] ? [["bind-key", "-n", b["tmux.tab.next"].key, "next-window"]] : [],
|
|
11964
|
-
...b["tmux.tab.close"] ? [chatTabCloseBinding(b["tmux.tab.close"].key)] : [],
|
|
12666
|
+
...b["tmux.tab.close"] ? [chatTabCloseBinding(b["tmux.tab.close"].key, closeChatTabTmuxCommand)] : [],
|
|
11965
12667
|
...b["tmux.tab.rename"] ? [chatTabRenameBinding(b["tmux.tab.rename"].key)] : [],
|
|
12668
|
+
...layoutBind("tmux.layout.workspaceSplit", "workspace-split"),
|
|
12669
|
+
...layoutBind("tmux.layout.workspaceClose", "workspace-close"),
|
|
12670
|
+
...layoutBind("tmux.layout.workspaceReset", "workspace-reset"),
|
|
12671
|
+
...layoutBind("tmux.layout.tasksToggle", "tasks-toggle"),
|
|
12672
|
+
...layoutBind("tmux.layout.opsToggle", "ops-toggle"),
|
|
12673
|
+
...layoutBind("tmux.layout.terminalToggle", "terminal-toggle"),
|
|
11966
12674
|
["bind-key", "f", "run-shell", `${envStr}${invStr} quick-create --session '#{session_name}'`]
|
|
11967
12675
|
]);
|
|
11968
12676
|
await applyTmuxPaneBorderTheme();
|
|
@@ -11973,10 +12681,27 @@ async function ensureSessionImpl(opts) {
|
|
|
11973
12681
|
}
|
|
11974
12682
|
return true;
|
|
11975
12683
|
}
|
|
11976
|
-
async function
|
|
12684
|
+
async function paneIdByRoleInWindow(session, role, windowId) {
|
|
12685
|
+
const target = windowId?.trim() || `=${session}`;
|
|
12686
|
+
const { code, stdout } = await runTmuxCapturing(["list-panes", "-t", target, "-F", "#{pane_id}\t#{@kobe_role}"]);
|
|
12687
|
+
if (code !== 0)
|
|
12688
|
+
return "";
|
|
12689
|
+
for (const line of stdout.split(`
|
|
12690
|
+
`)) {
|
|
12691
|
+
const [paneId, paneRole] = line.split("\t");
|
|
12692
|
+
if (paneId?.trim() && paneRole?.trim() === role)
|
|
12693
|
+
return paneId.trim();
|
|
12694
|
+
}
|
|
12695
|
+
return "";
|
|
12696
|
+
}
|
|
12697
|
+
async function selectTasksPane(session, opts = {}) {
|
|
11977
12698
|
if (!await sessionExists(session))
|
|
11978
12699
|
return "";
|
|
11979
|
-
|
|
12700
|
+
let tasksPane = await paneIdByRoleInWindow(session, "tasks", opts.windowId);
|
|
12701
|
+
if (!tasksPane) {
|
|
12702
|
+
await runLayoutAction(session, "tasks-restore", { windowId: opts.windowId });
|
|
12703
|
+
tasksPane = await paneIdByRoleInWindow(session, "tasks", opts.windowId);
|
|
12704
|
+
}
|
|
11980
12705
|
if (!tasksPane)
|
|
11981
12706
|
return "";
|
|
11982
12707
|
await runTmux(["select-pane", "-t", tasksPane]);
|
|
@@ -11994,11 +12719,13 @@ var init_tmux = __esm(() => {
|
|
|
11994
12719
|
init_tmux_border_theme();
|
|
11995
12720
|
init_chattab();
|
|
11996
12721
|
init_launch();
|
|
12722
|
+
init_layout_actions();
|
|
11997
12723
|
init_layout_coord();
|
|
11998
12724
|
init_pane_heal();
|
|
11999
12725
|
init_client2();
|
|
12000
12726
|
init_chattab();
|
|
12001
12727
|
init_pane_heal();
|
|
12728
|
+
init_layout_actions();
|
|
12002
12729
|
FOCUS_EDGE_VARS = {
|
|
12003
12730
|
"-L": "pane_at_left",
|
|
12004
12731
|
"-D": "pane_at_bottom",
|
|
@@ -20263,6 +20990,54 @@ var init_keybindings2 = __esm(() => {
|
|
|
20263
20990
|
description: "Open the engine in a new tab (tmux prefix, then t)",
|
|
20264
20991
|
hint: { keys: "prefix t", label: "engine tab", status: false }
|
|
20265
20992
|
},
|
|
20993
|
+
{
|
|
20994
|
+
id: "tmux.layout.workspaceSplit",
|
|
20995
|
+
scope: "global",
|
|
20996
|
+
keys: [],
|
|
20997
|
+
category: "Workspace (tmux)",
|
|
20998
|
+
description: "Add a temporary workspace split (tmux prefix, then s)",
|
|
20999
|
+
hint: { keys: "prefix s", label: "split", status: false }
|
|
21000
|
+
},
|
|
21001
|
+
{
|
|
21002
|
+
id: "tmux.layout.workspaceClose",
|
|
21003
|
+
scope: "global",
|
|
21004
|
+
keys: [],
|
|
21005
|
+
category: "Workspace (tmux)",
|
|
21006
|
+
description: "Close the focused temporary workspace split (tmux prefix, then x)",
|
|
21007
|
+
hint: { keys: "prefix x", label: "close split", status: false }
|
|
21008
|
+
},
|
|
21009
|
+
{
|
|
21010
|
+
id: "tmux.layout.workspaceReset",
|
|
21011
|
+
scope: "global",
|
|
21012
|
+
keys: [],
|
|
21013
|
+
category: "Workspace (tmux)",
|
|
21014
|
+
description: "Close all temporary workspace splits (tmux prefix, then r)",
|
|
21015
|
+
hint: { keys: "prefix r", label: "reset splits", status: false }
|
|
21016
|
+
},
|
|
21017
|
+
{
|
|
21018
|
+
id: "tmux.layout.tasksToggle",
|
|
21019
|
+
scope: "global",
|
|
21020
|
+
keys: [],
|
|
21021
|
+
category: "Workspace (tmux)",
|
|
21022
|
+
description: "Hide / restore the Tasks pane (tmux prefix, then a)",
|
|
21023
|
+
hint: { keys: "prefix a", label: "tasks pane", status: false }
|
|
21024
|
+
},
|
|
21025
|
+
{
|
|
21026
|
+
id: "tmux.layout.opsToggle",
|
|
21027
|
+
scope: "global",
|
|
21028
|
+
keys: [],
|
|
21029
|
+
category: "Workspace (tmux)",
|
|
21030
|
+
description: "Toggle the file/Ops pane (tmux prefix, then o)",
|
|
21031
|
+
hint: { keys: "prefix o", label: "file pane", status: false }
|
|
21032
|
+
},
|
|
21033
|
+
{
|
|
21034
|
+
id: "tmux.layout.terminalToggle",
|
|
21035
|
+
scope: "global",
|
|
21036
|
+
keys: [],
|
|
21037
|
+
category: "Workspace (tmux)",
|
|
21038
|
+
description: "Hide / restore the terminal pane (tmux prefix, then z)",
|
|
21039
|
+
hint: { keys: "prefix z", label: "terminal", status: false }
|
|
21040
|
+
},
|
|
20266
21041
|
{
|
|
20267
21042
|
id: "chat.send",
|
|
20268
21043
|
scope: "workspace",
|
|
@@ -20526,6 +21301,10 @@ function applyUserKeybindings() {
|
|
|
20526
21301
|
keys: bind ? [bind.chord] : [],
|
|
20527
21302
|
defaultKeys: [TMUX_SINGLE_BINDING_DEFAULTS[id]]
|
|
20528
21303
|
});
|
|
21304
|
+
const displayRow = KobeKeymap.find((row) => row.id === id);
|
|
21305
|
+
if (displayRow?.hint) {
|
|
21306
|
+
displayRow.hint.keys = bind ? `${isTmuxPrefixBindingId(id) ? "prefix " : ""}${bind.chord}` : "\u2014";
|
|
21307
|
+
}
|
|
20529
21308
|
}
|
|
20530
21309
|
}
|
|
20531
21310
|
for (const w of warnings)
|
|
@@ -21007,6 +21786,45 @@ var init_host_boot = __esm(() => {
|
|
|
21007
21786
|
init_persisted_ui_prefs();
|
|
21008
21787
|
});
|
|
21009
21788
|
|
|
21789
|
+
// src/tui/lib/task-enter.ts
|
|
21790
|
+
async function ensureTaskSession2(orch, task, repo, vendor, opts = {}) {
|
|
21791
|
+
const session = tmuxSessionName(task.id);
|
|
21792
|
+
if (await sessionExists(session))
|
|
21793
|
+
return true;
|
|
21794
|
+
let worktree = task.worktreePath;
|
|
21795
|
+
if (!worktree)
|
|
21796
|
+
worktree = await orch.ensureWorktree(task.id);
|
|
21797
|
+
if (!worktree)
|
|
21798
|
+
throw new Error(`task ${task.id} has no worktree`);
|
|
21799
|
+
const { ensureSession: ensureSession2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
21800
|
+
const { resolveRepoInit: resolveRepoInit2 } = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
|
|
21801
|
+
const init2 = resolveRepoInit2(repo, worktree);
|
|
21802
|
+
const ok = await ensureSession2({
|
|
21803
|
+
name: session,
|
|
21804
|
+
cwd: worktree,
|
|
21805
|
+
command: interactiveEngineCommand(vendor),
|
|
21806
|
+
taskId: task.id,
|
|
21807
|
+
vendor,
|
|
21808
|
+
repo,
|
|
21809
|
+
initScript: init2.initScript,
|
|
21810
|
+
initPrompt: opts.includeInitPrompt ? init2.initPrompt : undefined
|
|
21811
|
+
});
|
|
21812
|
+
if (!ok)
|
|
21813
|
+
throw new Error(`failed to start tmux session for ${task.id}`);
|
|
21814
|
+
return false;
|
|
21815
|
+
}
|
|
21816
|
+
async function jumpToTask(orch, task, repo, vendor, opts = {}) {
|
|
21817
|
+
await ensureTaskSession2(orch, task, repo, vendor, opts);
|
|
21818
|
+
await orch.setActiveTask(task.id).catch(() => {});
|
|
21819
|
+
const { prepareWindowForSwitch: prepareWindowForSwitch2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
21820
|
+
await prepareWindowForSwitch2(tmuxSessionName(task.id));
|
|
21821
|
+
await runTmux(["switch-client", "-t", `=${tmuxSessionName(task.id)}`]);
|
|
21822
|
+
}
|
|
21823
|
+
var init_task_enter = __esm(() => {
|
|
21824
|
+
init_interactive_command();
|
|
21825
|
+
init_client2();
|
|
21826
|
+
});
|
|
21827
|
+
|
|
21010
21828
|
// src/tui/new-task/host.tsx
|
|
21011
21829
|
var exports_host = {};
|
|
21012
21830
|
__export(exports_host, {
|
|
@@ -21041,10 +21859,11 @@ function NewTaskPage(props) {
|
|
|
21041
21859
|
console.error("[kobe new-task] no daemon; cannot create task");
|
|
21042
21860
|
process.exit(1);
|
|
21043
21861
|
}
|
|
21862
|
+
let entered;
|
|
21044
21863
|
try {
|
|
21045
21864
|
if (result.mode === "adopt") {
|
|
21046
21865
|
for (const w of result.adopt) {
|
|
21047
|
-
await orch.adoptWorktree({
|
|
21866
|
+
entered = await orch.adoptWorktree({
|
|
21048
21867
|
repo: result.repo,
|
|
21049
21868
|
worktreePath: w.worktreePath,
|
|
21050
21869
|
branch: w.branch,
|
|
@@ -21052,7 +21871,7 @@ function NewTaskPage(props) {
|
|
|
21052
21871
|
});
|
|
21053
21872
|
}
|
|
21054
21873
|
} else {
|
|
21055
|
-
await orch.createTask({
|
|
21874
|
+
entered = await orch.createTask({
|
|
21056
21875
|
repo: result.repo,
|
|
21057
21876
|
baseRef: result.baseRef,
|
|
21058
21877
|
vendor: result.vendor
|
|
@@ -21062,6 +21881,15 @@ function NewTaskPage(props) {
|
|
|
21062
21881
|
console.error("[kobe new-task] task.create/adopt failed:", err);
|
|
21063
21882
|
process.exit(1);
|
|
21064
21883
|
}
|
|
21884
|
+
if (entered) {
|
|
21885
|
+
try {
|
|
21886
|
+
await jumpToTask(orch, entered, result.repo, result.vendor, {
|
|
21887
|
+
includeInitPrompt: result.mode !== "adopt"
|
|
21888
|
+
});
|
|
21889
|
+
} catch (err) {
|
|
21890
|
+
console.error("[kobe new-task] auto-enter failed:", err);
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21065
21893
|
process.exit(0);
|
|
21066
21894
|
}
|
|
21067
21895
|
return (() => {
|
|
@@ -21112,6 +21940,7 @@ var init_host = __esm(() => {
|
|
|
21112
21940
|
init_new_task_dialog();
|
|
21113
21941
|
init_theme2();
|
|
21114
21942
|
init_host_boot();
|
|
21943
|
+
init_task_enter();
|
|
21115
21944
|
init_dialog();
|
|
21116
21945
|
});
|
|
21117
21946
|
|
|
@@ -21249,35 +22078,6 @@ async function resolveQuickTaskContext(orch, session) {
|
|
|
21249
22078
|
fallbackRepo
|
|
21250
22079
|
};
|
|
21251
22080
|
}
|
|
21252
|
-
async function ensureTaskSession2(orch, task, repo, vendor) {
|
|
21253
|
-
const session = tmuxSessionName(task.id);
|
|
21254
|
-
if (await sessionExists(session))
|
|
21255
|
-
return true;
|
|
21256
|
-
let worktree = task.worktreePath;
|
|
21257
|
-
if (!worktree)
|
|
21258
|
-
worktree = await orch.ensureWorktree(task.id);
|
|
21259
|
-
if (!worktree)
|
|
21260
|
-
throw new Error(`task ${task.id} has no worktree`);
|
|
21261
|
-
const {
|
|
21262
|
-
ensureSession: ensureSession2
|
|
21263
|
-
} = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
21264
|
-
const {
|
|
21265
|
-
resolveRepoInit: resolveRepoInit2
|
|
21266
|
-
} = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
|
|
21267
|
-
const init2 = resolveRepoInit2(repo, worktree);
|
|
21268
|
-
const ok = await ensureSession2({
|
|
21269
|
-
name: session,
|
|
21270
|
-
cwd: worktree,
|
|
21271
|
-
command: interactiveEngineCommand(vendor),
|
|
21272
|
-
taskId: task.id,
|
|
21273
|
-
vendor,
|
|
21274
|
-
repo,
|
|
21275
|
-
initScript: init2.initScript
|
|
21276
|
-
});
|
|
21277
|
-
if (!ok)
|
|
21278
|
-
throw new Error(`failed to start tmux session for ${task.id}`);
|
|
21279
|
-
return false;
|
|
21280
|
-
}
|
|
21281
22081
|
async function deliverFirstPromptToTask(orch, task, repo, vendor, prompt) {
|
|
21282
22082
|
const existed = await ensureTaskSession2(orch, task, repo, vendor);
|
|
21283
22083
|
const session = tmuxSessionName(task.id);
|
|
@@ -21287,15 +22087,6 @@ async function deliverFirstPromptToTask(orch, task, repo, vendor, prompt) {
|
|
|
21287
22087
|
if (pane)
|
|
21288
22088
|
await pasteAndSubmit(pane, prompt);
|
|
21289
22089
|
}
|
|
21290
|
-
async function jumpToTask(orch, task, repo, vendor) {
|
|
21291
|
-
await ensureTaskSession2(orch, task, repo, vendor);
|
|
21292
|
-
await orch.setActiveTask(task.id).catch(() => {});
|
|
21293
|
-
const {
|
|
21294
|
-
prepareWindowForSwitch: prepareWindowForSwitch2
|
|
21295
|
-
} = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
21296
|
-
await prepareWindowForSwitch2(tmuxSessionName(task.id));
|
|
21297
|
-
await runTmux(["switch-client", "-t", `=${tmuxSessionName(task.id)}`]);
|
|
21298
|
-
}
|
|
21299
22090
|
function QuickTaskPage(props) {
|
|
21300
22091
|
const {
|
|
21301
22092
|
theme
|
|
@@ -21394,6 +22185,7 @@ var init_host2 = __esm(() => {
|
|
|
21394
22185
|
init_git_snapshot();
|
|
21395
22186
|
init_host_boot();
|
|
21396
22187
|
init_path_helpers();
|
|
22188
|
+
init_task_enter();
|
|
21397
22189
|
init_host();
|
|
21398
22190
|
init_groups();
|
|
21399
22191
|
init_dialog();
|
|
@@ -21432,8 +22224,10 @@ function formatChord(chord, prefixGlyph = "\u2303B") {
|
|
|
21432
22224
|
if (!s)
|
|
21433
22225
|
return s;
|
|
21434
22226
|
const pm = /^prefix\s+(.+)$/i.exec(s);
|
|
21435
|
-
if (pm)
|
|
21436
|
-
|
|
22227
|
+
if (pm) {
|
|
22228
|
+
const suffix = pm[1] ?? "";
|
|
22229
|
+
return `${prefixGlyph} ${suffix.includes("+") ? formatChord(suffix, prefixGlyph) : formatKey(suffix, true)}`;
|
|
22230
|
+
}
|
|
21437
22231
|
const parts = s.split("+");
|
|
21438
22232
|
if (parts.length === 1)
|
|
21439
22233
|
return formatKey(parts[0] ?? "", false);
|
|
@@ -23022,12 +23816,12 @@ function KeybindingsSettingsSection() {
|
|
|
23022
23816
|
const report = userKeybindingsReport();
|
|
23023
23817
|
const fixedIds = Object.keys(FIXED_BINDING_IDS).sort();
|
|
23024
23818
|
return (() => {
|
|
23025
|
-
var _el$161 = createElement("box"), _el$162 = createElement("text"), _el$164 = createElement("text"), _el$166 = createElement("box"), _el$167 = createElement("text"), _el$169 = createElement("text"), _el$
|
|
23819
|
+
var _el$161 = createElement("box"), _el$162 = createElement("text"), _el$164 = createElement("text"), _el$166 = createElement("box"), _el$167 = createElement("text"), _el$169 = createElement("text"), _el$199 = createElement("text"), _el$201 = createElement("text");
|
|
23026
23820
|
insertNode(_el$161, _el$162);
|
|
23027
23821
|
insertNode(_el$161, _el$164);
|
|
23028
23822
|
insertNode(_el$161, _el$166);
|
|
23029
|
-
insertNode(_el$161, _el$197);
|
|
23030
23823
|
insertNode(_el$161, _el$199);
|
|
23824
|
+
insertNode(_el$161, _el$201);
|
|
23031
23825
|
setProp(_el$161, "flexDirection", "column");
|
|
23032
23826
|
setProp(_el$161, "gap", 1);
|
|
23033
23827
|
insertNode(_el$162, createTextNode(`Keybindings`));
|
|
@@ -23046,7 +23840,7 @@ function KeybindingsSettingsSection() {
|
|
|
23046
23840
|
return !report.exists;
|
|
23047
23841
|
},
|
|
23048
23842
|
get children() {
|
|
23049
|
-
var _el$170 = createElement("box"), _el$171 = createElement("text"), _el$173 = createElement("text"), _el$175 = createElement("text"), _el$177 = createElement("text"), _el$179 = createElement("text"), _el$181 = createElement("text"), _el$183 = createElement("text"), _el$185 = createElement("text"), _el$187 = createElement("text");
|
|
23843
|
+
var _el$170 = createElement("box"), _el$171 = createElement("text"), _el$173 = createElement("text"), _el$175 = createElement("text"), _el$177 = createElement("text"), _el$179 = createElement("text"), _el$181 = createElement("text"), _el$183 = createElement("text"), _el$185 = createElement("text"), _el$187 = createElement("text"), _el$189 = createElement("text");
|
|
23050
23844
|
insertNode(_el$170, _el$171);
|
|
23051
23845
|
insertNode(_el$170, _el$173);
|
|
23052
23846
|
insertNode(_el$170, _el$175);
|
|
@@ -23056,6 +23850,7 @@ function KeybindingsSettingsSection() {
|
|
|
23056
23850
|
insertNode(_el$170, _el$183);
|
|
23057
23851
|
insertNode(_el$170, _el$185);
|
|
23058
23852
|
insertNode(_el$170, _el$187);
|
|
23853
|
+
insertNode(_el$170, _el$189);
|
|
23059
23854
|
setProp(_el$170, "flexDirection", "column");
|
|
23060
23855
|
setProp(_el$170, "gap", 0);
|
|
23061
23856
|
insertNode(_el$171, createTextNode(`Example`));
|
|
@@ -23064,11 +23859,12 @@ function KeybindingsSettingsSection() {
|
|
|
23064
23859
|
insertNode(_el$177, createTextNode(` sidebar.select: [enter] # list = several chords`));
|
|
23065
23860
|
insertNode(_el$179, createTextNode(` files.createPR: null # null = unbind`));
|
|
23066
23861
|
insertNode(_el$181, createTextNode(` tmux.tab.new: ctrl+y # tmux session key (see below)`));
|
|
23067
|
-
insertNode(_el$183, createTextNode(`
|
|
23068
|
-
insertNode(_el$185, createTextNode(`
|
|
23069
|
-
insertNode(_el$187, createTextNode(`
|
|
23862
|
+
insertNode(_el$183, createTextNode(` tmux.layout.workspaceSplit: g # prefix g`));
|
|
23863
|
+
insertNode(_el$185, createTextNode(`darwin: # platform overlay (also: linux)`));
|
|
23864
|
+
insertNode(_el$187, createTextNode(` bindings:`));
|
|
23865
|
+
insertNode(_el$189, createTextNode(` palette.open: [cmd+p, ctrl+p]`));
|
|
23070
23866
|
effect((_p$) => {
|
|
23071
|
-
var _v$107 = theme.text, _v$108 = TextAttributes7.BOLD, _v$109 = theme.textMuted, _v$110 = theme.textMuted, _v$111 = theme.textMuted, _v$112 = theme.textMuted, _v$113 = theme.textMuted, _v$114 = theme.textMuted, _v$115 = theme.textMuted, _v$116 = theme.textMuted;
|
|
23867
|
+
var _v$107 = theme.text, _v$108 = TextAttributes7.BOLD, _v$109 = theme.textMuted, _v$110 = theme.textMuted, _v$111 = theme.textMuted, _v$112 = theme.textMuted, _v$113 = theme.textMuted, _v$114 = theme.textMuted, _v$115 = theme.textMuted, _v$116 = theme.textMuted, _v$117 = theme.textMuted;
|
|
23072
23868
|
_v$107 !== _p$.e && (_p$.e = setProp(_el$171, "fg", _v$107, _p$.e));
|
|
23073
23869
|
_v$108 !== _p$.t && (_p$.t = setProp(_el$171, "attributes", _v$108, _p$.t));
|
|
23074
23870
|
_v$109 !== _p$.a && (_p$.a = setProp(_el$173, "fg", _v$109, _p$.a));
|
|
@@ -23079,6 +23875,7 @@ function KeybindingsSettingsSection() {
|
|
|
23079
23875
|
_v$114 !== _p$.h && (_p$.h = setProp(_el$183, "fg", _v$114, _p$.h));
|
|
23080
23876
|
_v$115 !== _p$.r && (_p$.r = setProp(_el$185, "fg", _v$115, _p$.r));
|
|
23081
23877
|
_v$116 !== _p$.d && (_p$.d = setProp(_el$187, "fg", _v$116, _p$.d));
|
|
23878
|
+
_v$117 !== _p$.l && (_p$.l = setProp(_el$189, "fg", _v$117, _p$.l));
|
|
23082
23879
|
return _p$;
|
|
23083
23880
|
}, {
|
|
23084
23881
|
e: undefined,
|
|
@@ -23090,104 +23887,105 @@ function KeybindingsSettingsSection() {
|
|
|
23090
23887
|
s: undefined,
|
|
23091
23888
|
h: undefined,
|
|
23092
23889
|
r: undefined,
|
|
23093
|
-
d: undefined
|
|
23890
|
+
d: undefined,
|
|
23891
|
+
l: undefined
|
|
23094
23892
|
});
|
|
23095
23893
|
return _el$170;
|
|
23096
23894
|
}
|
|
23097
|
-
}), _el$
|
|
23895
|
+
}), _el$199);
|
|
23098
23896
|
insert(_el$161, createComponent2(Show, {
|
|
23099
23897
|
get when() {
|
|
23100
23898
|
return report.exists;
|
|
23101
23899
|
},
|
|
23102
23900
|
get children() {
|
|
23103
|
-
var _el$
|
|
23104
|
-
insertNode(_el$
|
|
23105
|
-
setProp(_el$
|
|
23106
|
-
setProp(_el$
|
|
23107
|
-
insertNode(_el$
|
|
23108
|
-
insert(_el$
|
|
23901
|
+
var _el$191 = createElement("box"), _el$192 = createElement("text");
|
|
23902
|
+
insertNode(_el$191, _el$192);
|
|
23903
|
+
setProp(_el$191, "flexDirection", "column");
|
|
23904
|
+
setProp(_el$191, "gap", 0);
|
|
23905
|
+
insertNode(_el$192, createTextNode(`Overrides applied`));
|
|
23906
|
+
insert(_el$191, createComponent2(Show, {
|
|
23109
23907
|
get when() {
|
|
23110
23908
|
return report.applied.length === 0;
|
|
23111
23909
|
},
|
|
23112
23910
|
get children() {
|
|
23113
|
-
var _el$
|
|
23114
|
-
insertNode(_el$
|
|
23115
|
-
effect((_$p) => setProp(_el$
|
|
23116
|
-
return _el$
|
|
23911
|
+
var _el$194 = createElement("text");
|
|
23912
|
+
insertNode(_el$194, createTextNode(`none`));
|
|
23913
|
+
effect((_$p) => setProp(_el$194, "fg", theme.textMuted, _$p));
|
|
23914
|
+
return _el$194;
|
|
23117
23915
|
}
|
|
23118
23916
|
}), null);
|
|
23119
|
-
insert(_el$
|
|
23917
|
+
insert(_el$191, createComponent2(For, {
|
|
23120
23918
|
get each() {
|
|
23121
23919
|
return report.applied;
|
|
23122
23920
|
},
|
|
23123
23921
|
children: (o) => (() => {
|
|
23124
|
-
var _el$
|
|
23125
|
-
setProp(_el$
|
|
23126
|
-
insert(_el$
|
|
23127
|
-
effect((_$p) => setProp(_el$
|
|
23128
|
-
return _el$
|
|
23922
|
+
var _el$202 = createElement("text");
|
|
23923
|
+
setProp(_el$202, "wrapMode", "word");
|
|
23924
|
+
insert(_el$202, () => `${o.id} \u2192 ${o.keys.length > 0 ? o.keys.join(" / ") : "(unbound)"} (default: ${o.defaultKeys.join(" / ")})`);
|
|
23925
|
+
effect((_$p) => setProp(_el$202, "fg", theme.text, _$p));
|
|
23926
|
+
return _el$202;
|
|
23129
23927
|
})()
|
|
23130
23928
|
}), null);
|
|
23131
23929
|
effect((_p$) => {
|
|
23132
|
-
var _v$
|
|
23133
|
-
_v$
|
|
23134
|
-
_v$
|
|
23930
|
+
var _v$118 = theme.text, _v$119 = TextAttributes7.BOLD;
|
|
23931
|
+
_v$118 !== _p$.e && (_p$.e = setProp(_el$192, "fg", _v$118, _p$.e));
|
|
23932
|
+
_v$119 !== _p$.t && (_p$.t = setProp(_el$192, "attributes", _v$119, _p$.t));
|
|
23135
23933
|
return _p$;
|
|
23136
23934
|
}, {
|
|
23137
23935
|
e: undefined,
|
|
23138
23936
|
t: undefined
|
|
23139
23937
|
});
|
|
23140
|
-
return _el$
|
|
23938
|
+
return _el$191;
|
|
23141
23939
|
}
|
|
23142
|
-
}), _el$
|
|
23940
|
+
}), _el$199);
|
|
23143
23941
|
insert(_el$161, createComponent2(Show, {
|
|
23144
23942
|
get when() {
|
|
23145
23943
|
return report.warnings.length > 0;
|
|
23146
23944
|
},
|
|
23147
23945
|
get children() {
|
|
23148
|
-
var _el$
|
|
23149
|
-
insertNode(_el$
|
|
23150
|
-
setProp(_el$
|
|
23151
|
-
setProp(_el$
|
|
23152
|
-
insertNode(_el$
|
|
23153
|
-
insert(_el$
|
|
23946
|
+
var _el$196 = createElement("box"), _el$197 = createElement("text");
|
|
23947
|
+
insertNode(_el$196, _el$197);
|
|
23948
|
+
setProp(_el$196, "flexDirection", "column");
|
|
23949
|
+
setProp(_el$196, "gap", 0);
|
|
23950
|
+
insertNode(_el$197, createTextNode(`Warnings`));
|
|
23951
|
+
insert(_el$196, createComponent2(For, {
|
|
23154
23952
|
get each() {
|
|
23155
23953
|
return report.warnings;
|
|
23156
23954
|
},
|
|
23157
23955
|
children: (w) => (() => {
|
|
23158
|
-
var _el$
|
|
23159
|
-
setProp(_el$
|
|
23160
|
-
insert(_el$
|
|
23161
|
-
effect((_$p) => setProp(_el$
|
|
23162
|
-
return _el$
|
|
23956
|
+
var _el$203 = createElement("text");
|
|
23957
|
+
setProp(_el$203, "wrapMode", "word");
|
|
23958
|
+
insert(_el$203, `! ${w}`);
|
|
23959
|
+
effect((_$p) => setProp(_el$203, "fg", theme.warning, _$p));
|
|
23960
|
+
return _el$203;
|
|
23163
23961
|
})()
|
|
23164
23962
|
}), null);
|
|
23165
23963
|
effect((_p$) => {
|
|
23166
|
-
var _v$
|
|
23167
|
-
_v$
|
|
23168
|
-
_v$
|
|
23964
|
+
var _v$120 = theme.warning, _v$121 = TextAttributes7.BOLD;
|
|
23965
|
+
_v$120 !== _p$.e && (_p$.e = setProp(_el$197, "fg", _v$120, _p$.e));
|
|
23966
|
+
_v$121 !== _p$.t && (_p$.t = setProp(_el$197, "attributes", _v$121, _p$.t));
|
|
23169
23967
|
return _p$;
|
|
23170
23968
|
}, {
|
|
23171
23969
|
e: undefined,
|
|
23172
23970
|
t: undefined
|
|
23173
23971
|
});
|
|
23174
|
-
return _el$
|
|
23972
|
+
return _el$196;
|
|
23175
23973
|
}
|
|
23176
|
-
}), _el$
|
|
23177
|
-
insertNode(_el$
|
|
23178
|
-
setProp(_el$197, "wrapMode", "word");
|
|
23974
|
+
}), _el$199);
|
|
23975
|
+
insertNode(_el$199, createTextNode(`tmux session keys use the same file: tmux.tab.new (ctrl+t), tmux.tab.prev/next (ctrl+[/]), tmux.tab.close (ctrl+w), tmux.tab.rename (f2), tmux.tab.chooseEngine (ctrl+shift+t), tmux.detach (ctrl+q), tmux.focus (4 chords, left/down/up/right), and prefix layout keys: workspaceSplit (s), workspaceClose (x), workspaceReset (r), tasksToggle (a), opsToggle (o), terminalToggle (z). They apply when a session is (re)built.`));
|
|
23179
23976
|
setProp(_el$199, "wrapMode", "word");
|
|
23180
|
-
|
|
23977
|
+
setProp(_el$201, "wrapMode", "word");
|
|
23978
|
+
insert(_el$201, () => `Fixed (not rebindable): ${fixedIds.join(", ")}.`);
|
|
23181
23979
|
effect((_p$) => {
|
|
23182
|
-
var _v$
|
|
23183
|
-
_v$
|
|
23184
|
-
_v$
|
|
23185
|
-
_v$
|
|
23186
|
-
_v$
|
|
23187
|
-
_v$
|
|
23188
|
-
_v$
|
|
23189
|
-
_v$
|
|
23190
|
-
_v$
|
|
23980
|
+
var _v$122 = theme.text, _v$123 = TextAttributes7.BOLD, _v$124 = theme.textMuted, _v$125 = theme.text, _v$126 = TextAttributes7.BOLD, _v$127 = theme.textMuted, _v$128 = theme.textMuted, _v$129 = theme.textMuted;
|
|
23981
|
+
_v$122 !== _p$.e && (_p$.e = setProp(_el$162, "fg", _v$122, _p$.e));
|
|
23982
|
+
_v$123 !== _p$.t && (_p$.t = setProp(_el$162, "attributes", _v$123, _p$.t));
|
|
23983
|
+
_v$124 !== _p$.a && (_p$.a = setProp(_el$164, "fg", _v$124, _p$.a));
|
|
23984
|
+
_v$125 !== _p$.o && (_p$.o = setProp(_el$167, "fg", _v$125, _p$.o));
|
|
23985
|
+
_v$126 !== _p$.i && (_p$.i = setProp(_el$167, "attributes", _v$126, _p$.i));
|
|
23986
|
+
_v$127 !== _p$.n && (_p$.n = setProp(_el$169, "fg", _v$127, _p$.n));
|
|
23987
|
+
_v$128 !== _p$.s && (_p$.s = setProp(_el$199, "fg", _v$128, _p$.s));
|
|
23988
|
+
_v$129 !== _p$.h && (_p$.h = setProp(_el$201, "fg", _v$129, _p$.h));
|
|
23191
23989
|
return _p$;
|
|
23192
23990
|
}, {
|
|
23193
23991
|
e: undefined,
|
|
@@ -24201,8 +24999,10 @@ async function createTaskFlow(ctx3) {
|
|
|
24201
24999
|
return;
|
|
24202
25000
|
}
|
|
24203
25001
|
await ctx3.reload?.();
|
|
24204
|
-
if (createdId)
|
|
25002
|
+
if (createdId) {
|
|
24205
25003
|
ctx3.selectTask?.(createdId);
|
|
25004
|
+
await ctx3.enterTask?.(createdId);
|
|
25005
|
+
}
|
|
24206
25006
|
}
|
|
24207
25007
|
var init_task_actions = __esm(() => {
|
|
24208
25008
|
init_account_detect();
|
|
@@ -25783,7 +26583,8 @@ function TasksShell(props) {
|
|
|
25783
26583
|
await openNewTaskTab(session, defaultRepo);
|
|
25784
26584
|
return true;
|
|
25785
26585
|
},
|
|
25786
|
-
selectTask: (id) => setSelectedId(id)
|
|
26586
|
+
selectTask: (id) => setSelectedId(id),
|
|
26587
|
+
enterTask: (id) => switchTo(id)
|
|
25787
26588
|
};
|
|
25788
26589
|
async function createTask() {
|
|
25789
26590
|
await createTaskFlow(taskActions);
|
|
@@ -26132,11 +26933,21 @@ function ShortcutHints(props) {
|
|
|
26132
26933
|
label: "move panes"
|
|
26133
26934
|
});
|
|
26134
26935
|
}
|
|
26936
|
+
const layoutGroup = (label, ids) => {
|
|
26937
|
+
const chords = ids.map((id) => b[id]?.chord).filter((chord) => !!chord);
|
|
26938
|
+
if (chords.length > 0)
|
|
26939
|
+
out.push({
|
|
26940
|
+
k: `prefix ${chords.join("/")}`,
|
|
26941
|
+
label
|
|
26942
|
+
});
|
|
26943
|
+
};
|
|
26135
26944
|
if (b["tmux.detach"])
|
|
26136
26945
|
out.push({
|
|
26137
26946
|
k: b["tmux.detach"].chord,
|
|
26138
26947
|
label: "tasks\u2192detach"
|
|
26139
26948
|
});
|
|
26949
|
+
layoutGroup("splits", ["tmux.layout.workspaceSplit", "tmux.layout.workspaceClose", "tmux.layout.workspaceReset"]);
|
|
26950
|
+
layoutGroup("panes", ["tmux.layout.tasksToggle", "tmux.layout.opsToggle", "tmux.layout.terminalToggle"]);
|
|
26140
26951
|
return out;
|
|
26141
26952
|
};
|
|
26142
26953
|
const defaultHints = () => {
|
|
@@ -29139,6 +29950,12 @@ function parseOpsFlags(argv) {
|
|
|
29139
29950
|
} else if (flag === "--initial-task-id") {
|
|
29140
29951
|
flags.initialTaskId = value;
|
|
29141
29952
|
i++;
|
|
29953
|
+
} else if (flag === "--action") {
|
|
29954
|
+
flags.action = value;
|
|
29955
|
+
i++;
|
|
29956
|
+
} else if (flag === "--window") {
|
|
29957
|
+
flags.windowId = value;
|
|
29958
|
+
i++;
|
|
29142
29959
|
}
|
|
29143
29960
|
}
|
|
29144
29961
|
return flags;
|
|
@@ -29277,7 +30094,7 @@ async function main() {
|
|
|
29277
30094
|
process.exit(2);
|
|
29278
30095
|
}
|
|
29279
30096
|
const { selectTasksPane: selectTasksPane2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
29280
|
-
await selectTasksPane2(session);
|
|
30097
|
+
await selectTasksPane2(session, { windowId: flags.windowId });
|
|
29281
30098
|
return;
|
|
29282
30099
|
}
|
|
29283
30100
|
if (subcommand === "heal-layout") {
|
|
@@ -29308,6 +30125,34 @@ async function main() {
|
|
|
29308
30125
|
});
|
|
29309
30126
|
return;
|
|
29310
30127
|
}
|
|
30128
|
+
if (subcommand === "layout") {
|
|
30129
|
+
const flags = parseOpsFlags(rest);
|
|
30130
|
+
const session = flags.session;
|
|
30131
|
+
if (!session) {
|
|
30132
|
+
console.error("kobe layout: --session <name> is required");
|
|
30133
|
+
process.exit(2);
|
|
30134
|
+
}
|
|
30135
|
+
const action = flags.action;
|
|
30136
|
+
const valid = new Set([
|
|
30137
|
+
"workspace-split",
|
|
30138
|
+
"workspace-close",
|
|
30139
|
+
"workspace-reset",
|
|
30140
|
+
"tasks-toggle",
|
|
30141
|
+
"tasks-restore",
|
|
30142
|
+
"ops-toggle",
|
|
30143
|
+
"terminal-toggle",
|
|
30144
|
+
"chat-tab-close"
|
|
30145
|
+
]);
|
|
30146
|
+
if (!action || !valid.has(action)) {
|
|
30147
|
+
console.error("kobe layout: --action must be one of workspace-split, workspace-close, workspace-reset, tasks-toggle, tasks-restore, ops-toggle, terminal-toggle, chat-tab-close");
|
|
30148
|
+
process.exit(2);
|
|
30149
|
+
}
|
|
30150
|
+
const { runLayoutAction: runLayoutAction2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
30151
|
+
await runLayoutAction2(session, action, {
|
|
30152
|
+
windowId: flags.windowId
|
|
30153
|
+
});
|
|
30154
|
+
return;
|
|
30155
|
+
}
|
|
29311
30156
|
if (subcommand === "quick-task") {
|
|
29312
30157
|
const flags = parseOpsFlags(rest);
|
|
29313
30158
|
const { startQuickTaskHost: startQuickTaskHost2 } = await Promise.resolve().then(() => (init_host2(), exports_host2));
|