@sma1lboy/kobe 0.6.9 → 0.7.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.
Files changed (2) hide show
  1. package/dist/cli/index.js +1795 -1165
  2. package/package.json +2 -6
package/dist/cli/index.js CHANGED
@@ -70,18 +70,14 @@ var init_package = __esm(() => {
70
70
  package_default = {
71
71
  $schema: "https://json.schemastore.org/package.json",
72
72
  name: "@sma1lboy/kobe",
73
- version: "0.6.9",
73
+ version: "0.7.0",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
77
77
  bin: {
78
78
  kobe: "dist/cli/index.js"
79
79
  },
80
- files: [
81
- "dist",
82
- "README.md",
83
- "LICENSE"
84
- ],
80
+ files: ["dist", "README.md", "LICENSE"],
85
81
  publishConfig: {
86
82
  access: "public"
87
83
  },
@@ -218,6 +214,10 @@ function isNewerSemver(latest, current) {
218
214
  return false;
219
215
  }
220
216
  async function checkLatestVersion(opts = {}) {
217
+ const fake = process.env.KOBE_FAKE_UPDATE;
218
+ if (fake) {
219
+ return { current: CURRENT_VERSION, latest: fake, hasUpdate: isNewerSemver(fake, CURRENT_VERSION) };
220
+ }
221
221
  if (isDev() && !opts.force)
222
222
  return null;
223
223
  const latest = await fetchLatestFromRegistry(PACKAGE_NAME);
@@ -3049,6 +3049,21 @@ class Orchestrator {
3049
3049
  if (task.title === trimmed)
3050
3050
  return;
3051
3051
  await this.store.update(task.id, { title: trimmed });
3052
+ await this.followBranchToTitle(task, trimmed);
3053
+ }
3054
+ async followBranchToTitle(taskBefore, newTitle) {
3055
+ if (taskBefore.kind === "main" || !taskBefore.worktreePath)
3056
+ return;
3057
+ if (taskBefore.branch !== autoBranch(PLACEHOLDER_TASK_TITLE, taskBefore.id))
3058
+ return;
3059
+ const nextBranch = autoBranch(newTitle, taskBefore.id);
3060
+ if (nextBranch === taskBefore.branch)
3061
+ return;
3062
+ try {
3063
+ await this.setBranch(taskBefore.id, nextBranch);
3064
+ } catch (err) {
3065
+ console.error(`[kobe] follow-branch-to-title failed for ${taskBefore.id}:`, err);
3066
+ }
3052
3067
  }
3053
3068
  async setBranch(id, branch) {
3054
3069
  const trimmed = branch.trim();
@@ -3272,8 +3287,13 @@ class KobeDaemonClient {
3272
3287
  onChannel(channel, handler) {
3273
3288
  return this.on(channel, (frame) => handler(frame.payload));
3274
3289
  }
3275
- subscribe(channels) {
3276
- return this.request("subscribe", channels ? { channels } : {});
3290
+ subscribe(opts = {}) {
3291
+ const payload = {};
3292
+ if (opts.channels)
3293
+ payload.channels = opts.channels;
3294
+ if (opts.role)
3295
+ payload.role = opts.role;
3296
+ return this.request("subscribe", payload);
3277
3297
  }
3278
3298
  onLifecycle(name, handler) {
3279
3299
  let set = this.lifecycleHandlers.get(name);
@@ -4216,6 +4236,13 @@ var init_history3 = __esm(() => {
4216
4236
  });
4217
4237
 
4218
4238
  // src/monitor/auto-title.ts
4239
+ function readerFor(vendor) {
4240
+ if (vendor === "codex")
4241
+ return readHistory2;
4242
+ if (vendor === "copilot")
4243
+ return readHistory3;
4244
+ return readHistory;
4245
+ }
4219
4246
  async function originSessions(worktree, vendor) {
4220
4247
  if (vendor === "codex") {
4221
4248
  return { ids: await listSessionIdsForWorktree(worktree), read: readHistory2 };
@@ -4227,22 +4254,33 @@ async function originSessions(worktree, vendor) {
4227
4254
  const ids = [...files].sort((a, b) => a.mtimeMs - b.mtimeMs).map((f) => f.sessionId);
4228
4255
  return { ids, read: readHistory };
4229
4256
  }
4257
+ function titleFromMessages(messages) {
4258
+ const firstUser = messages.find((m) => m.role === "user");
4259
+ if (!firstUser)
4260
+ return "";
4261
+ const text = firstUser.blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4262
+ return deriveTitleFromPrompt(text);
4263
+ }
4230
4264
  async function deriveTitleFromSession(worktree, vendor = DEFAULT_TASK_VENDOR) {
4231
4265
  if (!worktree)
4232
4266
  return "";
4233
4267
  const { ids, read } = await originSessions(worktree, vendor);
4234
4268
  for (const sessionId of ids.slice(0, MAX_SESSIONS_SCANNED)) {
4235
- const messages = await read(sessionId);
4236
- const firstUser = messages.find((m) => m.role === "user");
4237
- if (!firstUser)
4238
- continue;
4239
- const text = firstUser.blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
4240
- const title = deriveTitleFromPrompt(text);
4269
+ const title = titleFromMessages(await read(sessionId));
4241
4270
  if (title)
4242
4271
  return title;
4243
4272
  }
4244
4273
  return "";
4245
4274
  }
4275
+ async function deriveTitleFromSessionId(vendor, sessionId) {
4276
+ if (!sessionId)
4277
+ return "";
4278
+ try {
4279
+ return titleFromMessages(await readerFor(vendor)(sessionId));
4280
+ } catch {
4281
+ return "";
4282
+ }
4283
+ }
4246
4284
  var MAX_SESSIONS_SCANNED = 8;
4247
4285
  var init_auto_title = __esm(() => {
4248
4286
  init_history();
@@ -4250,173 +4288,488 @@ var init_auto_title = __esm(() => {
4250
4288
  init_history3();
4251
4289
  });
4252
4290
 
4253
- // src/daemon/crash-log.ts
4254
- function toError(err) {
4255
- if (err instanceof Error)
4256
- return err;
4257
- let text;
4291
+ // src/tmux/client.ts
4292
+ var exports_client = {};
4293
+ __export(exports_client, {
4294
+ windowCount: () => windowCount,
4295
+ tmuxSessionName: () => tmuxSessionName,
4296
+ tmuxCommandSequence: () => tmuxCommandSequence,
4297
+ tmuxAvailable: () => tmuxAvailable,
4298
+ tmuxArgs: () => tmuxArgs,
4299
+ tagPaneRole: () => tagPaneRole,
4300
+ tagClaudePane: () => tagClaudePane,
4301
+ switchClientBeforeKill: () => switchClientBeforeKill,
4302
+ setWindowOption: () => setWindowOption,
4303
+ setSessionOption: () => setSessionOption,
4304
+ sessionExists: () => sessionExists,
4305
+ sendKeys: () => sendKeys,
4306
+ sendKeyName: () => sendKeyName,
4307
+ runTmuxSequenceCapturing: () => runTmuxSequenceCapturing,
4308
+ runTmuxSequence: () => runTmuxSequence,
4309
+ runTmuxCapturing: () => runTmuxCapturing,
4310
+ runTmux: () => runTmux,
4311
+ paneIdByRole: () => paneIdByRole,
4312
+ newWindow: () => newWindow,
4313
+ killSession: () => killSession,
4314
+ getSessionOptions: () => getSessionOptions,
4315
+ getSessionOption: () => getSessionOption,
4316
+ ensureFallbackSession: () => ensureFallbackSession,
4317
+ currentSessionName: () => currentSessionName,
4318
+ claudePaneIdStrict: () => claudePaneIdStrict,
4319
+ claudePaneId: () => claudePaneId,
4320
+ capturePaneById: () => capturePaneById,
4321
+ attachArgv: () => attachArgv,
4322
+ PANE_ROLE_OPTION: () => PANE_ROLE_OPTION,
4323
+ KOBE_TMUX_SOCKET: () => KOBE_TMUX_SOCKET,
4324
+ KOBE_HOME_SESSION: () => KOBE_HOME_SESSION,
4325
+ CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION,
4326
+ CHAT_TAB_SESSION_ID_OPTION: () => CHAT_TAB_SESSION_ID_OPTION
4327
+ });
4328
+ function tmuxArgs(...args) {
4329
+ return ["tmux", "-L", KOBE_TMUX_SOCKET, ...args];
4330
+ }
4331
+ function tmuxSessionName(taskId) {
4332
+ return `kobe-${taskId.replace(/[^A-Za-z0-9_-]/g, "")}`;
4333
+ }
4334
+ function attachArgv(name) {
4335
+ return tmuxArgs("attach-session", "-t", `=${name}`);
4336
+ }
4337
+ async function drainText(stream) {
4338
+ if (!stream)
4339
+ return "";
4258
4340
  try {
4259
- text = typeof err === "string" ? err : JSON.stringify(err);
4341
+ return await new Response(stream).text();
4260
4342
  } catch {
4261
- text = String(err);
4343
+ return "";
4262
4344
  }
4263
- return new Error(text);
4264
- }
4265
- function formatCrashEntry(kind, err, now = new Date) {
4266
- const e = toError(err);
4267
- return `[${now.toISOString()}] daemon ${kind}: ${e.stack ?? `${e.name}: ${e.message}`}
4268
- `;
4269
- }
4270
- function formatDaemonError(subsystem, err, now = new Date) {
4271
- const e = toError(err);
4272
- return `[${now.toISOString()}] daemon error [${subsystem}]: ${e.stack ?? `${e.name}: ${e.message}`}
4273
- `;
4274
4345
  }
4275
- function logDaemonError(subsystem, err) {
4276
- process.stderr.write(formatDaemonError(subsystem, err));
4346
+ async function runTmux(args) {
4347
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "pipe" });
4348
+ const [errText, code] = await Promise.all([drainText(proc.stderr), proc.exited]);
4349
+ if (code !== 0 && errText.trim().length > 0) {
4350
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4351
+ }
4352
+ return code;
4277
4353
  }
4278
- function installDaemonCrashHandlers(log = (l) => process.stderr.write(l)) {
4279
- if (onRejection || onException)
4280
- return;
4281
- onRejection = (reason) => log(formatCrashEntry("unhandledRejection", reason));
4282
- onException = (err) => log(formatCrashEntry("uncaughtException", err));
4283
- process.on("unhandledRejection", onRejection);
4284
- process.on("uncaughtException", onException);
4354
+ async function runTmuxQuiet(args) {
4355
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4356
+ return proc.exited;
4285
4357
  }
4286
- var onRejection, onException;
4287
-
4288
- // src/daemon/auto-title-poller.ts
4289
- async function runAutoTitlePass(orch, derive = deriveTitleFromSession) {
4290
- let renamed = 0;
4291
- for (const task of orch.listTasks()) {
4292
- if (task.title !== PLACEHOLDER_TASK_TITLE || !task.worktreePath)
4358
+ function tmuxCommandSequence(commands) {
4359
+ const out = [];
4360
+ for (const cmd of commands) {
4361
+ if (cmd.length === 0)
4293
4362
  continue;
4294
- try {
4295
- const title = await derive(task.worktreePath, task.vendor ?? DEFAULT_TASK_VENDOR);
4296
- if (!title)
4297
- continue;
4298
- const current = orch.getTask(task.id);
4299
- if (!current || current.title !== PLACEHOLDER_TASK_TITLE)
4300
- continue;
4301
- await orch.setTitle(task.id, title);
4302
- renamed++;
4303
- } catch (err) {
4304
- logDaemonError("auto-title-poller", err);
4305
- }
4363
+ if (out.length > 0)
4364
+ out.push(";");
4365
+ out.push(...cmd);
4306
4366
  }
4307
- return renamed;
4367
+ return out;
4308
4368
  }
4309
- function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS) {
4310
- if (intervalMs <= 0)
4311
- return () => {};
4312
- let running = false;
4313
- const tick = () => {
4314
- if (running)
4315
- return;
4316
- running = true;
4317
- runAutoTitlePass(orch).catch((err) => logDaemonError("auto-title-poller", err)).finally(() => {
4318
- running = false;
4319
- });
4320
- };
4321
- const timer = setInterval(tick, intervalMs);
4322
- timer.unref?.();
4323
- return () => clearInterval(timer);
4369
+ async function runTmuxSequence(commands) {
4370
+ const args = tmuxCommandSequence(commands);
4371
+ return args.length === 0 ? 0 : runTmux(args);
4324
4372
  }
4325
- var DEFAULT_AUTO_TITLE_POLL_MS = 4000;
4326
- var init_auto_title_poller = __esm(() => {
4327
- init_auto_title();
4328
- init_core();
4329
- });
4330
-
4331
- // src/daemon/event-bus.ts
4332
- class DaemonEventBus {
4333
- last = new Map;
4334
- sinks = new Set;
4335
- publish(channel, payload) {
4336
- this.last.set(channel, payload);
4337
- const event = { channel, payload };
4338
- for (const sink of this.sinks)
4339
- sink(event);
4340
- }
4341
- snapshot() {
4342
- return [...this.last].map(([channel, payload]) => ({ channel, payload }));
4343
- }
4344
- onPublish(sink) {
4345
- this.sinks.add(sink);
4346
- return () => {
4347
- this.sinks.delete(sink);
4348
- };
4373
+ async function runTmuxCapturing(args) {
4374
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
4375
+ const [stdout, errText, code] = await Promise.all([drainText(proc.stdout), drainText(proc.stderr), proc.exited]);
4376
+ if (code !== 0 && errText.trim().length > 0) {
4377
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4349
4378
  }
4379
+ return { code, stdout };
4350
4380
  }
4351
-
4352
- // src/daemon/paths.ts
4353
- import { createHash as createHash2 } from "crypto";
4354
- import { homedir as homedir6, tmpdir } from "os";
4355
- import { join as join3 } from "path";
4356
- function shortHomeTag(homeDir2) {
4357
- return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
4358
- }
4359
- function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
4360
- if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4361
- return naturalPath;
4362
- const tag = shortHomeTag(homeDir2);
4363
- const suffix = pidTag === undefined ? "" : `-${pidTag}`;
4364
- const fallback = join3(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
4365
- if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4366
- return fallback;
4367
- throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
4381
+ async function runTmuxSequenceCapturing(commands) {
4382
+ const args = tmuxCommandSequence(commands);
4383
+ return args.length === 0 ? { code: 0, stdout: "" } : runTmuxCapturing(args);
4368
4384
  }
4369
- function defaultDaemonSocketPath(homeDir2) {
4370
- const override = process.env.KOBE_DAEMON_SOCKET_PATH;
4371
- if (override && override.length > 0)
4372
- return override;
4373
- const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
4374
- if (explicit && explicit.length > 0) {
4375
- return fitSocketPath(join3(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
4376
- }
4377
- const runtimeDir = process.env.XDG_RUNTIME_DIR;
4378
- if (runtimeDir && runtimeDir.length > 0) {
4379
- return fitSocketPath(join3(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
4385
+ async function tmuxAvailable() {
4386
+ try {
4387
+ const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4388
+ return await proc.exited === 0;
4389
+ } catch {
4390
+ return false;
4380
4391
  }
4381
- const home = homedir6();
4382
- return fitSocketPath(join3(home, ".kobe", "daemon.sock"), home, "daemon");
4383
4392
  }
4384
- function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4385
- const override = process.env.KOBE_DAEMON_PID_PATH;
4386
- if (override && override.length > 0)
4387
- return override;
4388
- return join3(homeDir2, ".kobe", "daemon.pid");
4393
+ async function sessionExists(name) {
4394
+ return await runTmuxQuiet(["has-session", "-t", `=${name}`]) === 0;
4389
4395
  }
4390
- function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4391
- return join3(homeDir2, ".kobe", "daemon.log");
4396
+ async function windowCount(sessionName) {
4397
+ const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${sessionName}`, "-F", "#{window_id}"]);
4398
+ if (code !== 0)
4399
+ return 0;
4400
+ return stdout.split(`
4401
+ `).filter((l) => l.trim().length > 0).length;
4392
4402
  }
4393
- var SOCKET_PATH_SAFETY_LIMIT = 100;
4394
- var init_paths2 = () => {};
4395
-
4396
- // src/daemon/server.ts
4397
- import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4398
- import { createServer } from "net";
4399
- import { dirname as dirname3 } from "path";
4400
- function resolveIdleGraceMs() {
4401
- const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
4402
- if (raw === undefined)
4403
- return DEFAULT_IDLE_GRACE_MS;
4404
- const n = Number(raw);
4405
- return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_GRACE_MS;
4403
+ async function setSessionOption(session, option, value) {
4404
+ await runTmux(["set-option", "-t", session, option, value]);
4406
4405
  }
4407
- async function startDaemonServer(orch, options = {}) {
4408
- const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
4409
- const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
4410
- const startedAt = options.startedAt ?? new Date;
4411
- const clients = new Set;
4412
- let nextClientId = 1;
4413
- const idleGraceMs = resolveIdleGraceMs();
4406
+ async function getSessionOption(session, option) {
4407
+ const { code, stdout } = await runTmuxCapturing(["show-options", "-v", "-t", session, option]);
4408
+ return code === 0 ? stdout.trim() : "";
4409
+ }
4410
+ async function getSessionOptions(session, options) {
4411
+ const values = Object.fromEntries(options.map((option) => [option, undefined]));
4412
+ const { code, stdout } = await runTmuxSequenceCapturing(options.map((option) => ["show-options", "-q", "-t", session, option]));
4413
+ if (code !== 0)
4414
+ return values;
4415
+ for (const line of stdout.split(`
4416
+ `)) {
4417
+ const idx = line.indexOf(" ");
4418
+ if (idx <= 0)
4419
+ continue;
4420
+ const option = line.slice(0, idx);
4421
+ if (option in values)
4422
+ values[option] = line.slice(idx + 1).trim();
4423
+ }
4424
+ return values;
4425
+ }
4426
+ async function tagPaneRole(paneId, role) {
4427
+ await runTmux(["set-option", "-p", "-t", paneId, PANE_ROLE_OPTION, role]);
4428
+ }
4429
+ async function setWindowOption(target, option, value) {
4430
+ await runTmux(["set-window-option", "-t", target, option, value]);
4431
+ }
4432
+ async function tagClaudePane(paneId) {
4433
+ await tagPaneRole(paneId, CLAUDE_ROLE_VALUE);
4434
+ }
4435
+ async function paneIdByRole(sessionName, role, fallbackFirst = false) {
4436
+ const { code, stdout } = await runTmuxCapturing([
4437
+ "list-panes",
4438
+ "-t",
4439
+ `=${sessionName}`,
4440
+ "-F",
4441
+ `#{pane_id} #{${PANE_ROLE_OPTION}}`
4442
+ ]);
4443
+ if (code !== 0)
4444
+ return "";
4445
+ let firstId = "";
4446
+ for (const line of stdout.split(`
4447
+ `)) {
4448
+ const [id, paneRole] = line.split("\t");
4449
+ if (!id)
4450
+ continue;
4451
+ if (!firstId)
4452
+ firstId = id.trim();
4453
+ if (paneRole?.trim() === role)
4454
+ return id.trim();
4455
+ }
4456
+ return fallbackFirst ? firstId : "";
4457
+ }
4458
+ async function claudePaneId(sessionName) {
4459
+ return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, true);
4460
+ }
4461
+ async function claudePaneIdStrict(sessionName) {
4462
+ return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, false);
4463
+ }
4464
+ async function capturePaneById(paneId, lines) {
4465
+ if (!paneId)
4466
+ return "";
4467
+ const args = ["capture-pane", "-t", paneId, "-p"];
4468
+ if (typeof lines === "number" && lines > 0)
4469
+ args.push("-S", String(-lines));
4470
+ const { code, stdout } = await runTmuxCapturing(args);
4471
+ return code === 0 ? stdout : "";
4472
+ }
4473
+ async function sendKeys(target, text) {
4474
+ await runTmux(["send-keys", "-t", target, "-l", "--", text]);
4475
+ }
4476
+ async function sendKeyName(target, key) {
4477
+ await runTmux(["send-keys", "-t", target, key]);
4478
+ }
4479
+ async function newWindow(session, opts) {
4480
+ const args = ["new-window", "-t", `=${session}`, "-c", opts.cwd];
4481
+ if (opts.name)
4482
+ args.push("-n", opts.name);
4483
+ args.push(opts.command);
4484
+ await runTmux(args);
4485
+ }
4486
+ async function currentSessionName() {
4487
+ const args = ["display-message", "-p"];
4488
+ const target = process.env.TMUX_PANE;
4489
+ if (target && target.length > 0)
4490
+ args.push("-t", target);
4491
+ args.push("#{session_name}");
4492
+ const { code, stdout } = await runTmuxCapturing(args);
4493
+ const name = stdout.trim();
4494
+ return code === 0 && name.length > 0 ? name : null;
4495
+ }
4496
+ async function killSession(name) {
4497
+ if (await sessionExists(name))
4498
+ await runTmux(["kill-session", "-t", `=${name}`]);
4499
+ }
4500
+ async function ensureFallbackSession() {
4501
+ const name = KOBE_HOME_SESSION;
4502
+ if (!await sessionExists(name)) {
4503
+ await runTmux([
4504
+ "new-session",
4505
+ "-d",
4506
+ "-s",
4507
+ name,
4508
+ "-x",
4509
+ "220",
4510
+ "-y",
4511
+ "50",
4512
+ "sh",
4513
+ "-c",
4514
+ 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
4515
+ ]);
4516
+ }
4517
+ return name;
4518
+ }
4519
+ async function switchClientBeforeKill(killedName, nextSessionName) {
4520
+ const current = await currentSessionName();
4521
+ if (current !== killedName)
4522
+ return;
4523
+ if (nextSessionName && nextSessionName !== killedName && await sessionExists(nextSessionName)) {
4524
+ await runTmux(["switch-client", "-t", `=${nextSessionName}`]);
4525
+ return;
4526
+ }
4527
+ const fallback = await ensureFallbackSession();
4528
+ await runTmux(["switch-client", "-t", `=${fallback}`]);
4529
+ }
4530
+ var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", CHAT_TAB_SESSION_ID_OPTION = "@kobe_session_id", KOBE_HOME_SESSION = "kobe-home";
4531
+ var init_client2 = __esm(() => {
4532
+ KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
4533
+ CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
4534
+ });
4535
+
4536
+ // src/tmux/chat-tab-naming.ts
4537
+ async function listChatTabWindows(session, runner = realRunner) {
4538
+ const { code, stdout } = await runner.capture([
4539
+ "list-windows",
4540
+ "-t",
4541
+ `=${session}`,
4542
+ "-F",
4543
+ `#{window_index} #{${CHAT_TAB_SESSION_ID_OPTION}}`
4544
+ ]);
4545
+ if (code !== 0)
4546
+ return [];
4547
+ const out = [];
4548
+ for (const line of stdout.split(`
4549
+ `)) {
4550
+ const tab = line.indexOf("\t");
4551
+ const index = Number.parseInt((tab >= 0 ? line.slice(0, tab) : line).trim(), 10);
4552
+ if (!Number.isInteger(index))
4553
+ continue;
4554
+ out.push({ index, sessionId: tab >= 0 ? line.slice(tab + 1).trim() : "" });
4555
+ }
4556
+ return out;
4557
+ }
4558
+ async function windowNamedManually(session, index, runner) {
4559
+ const { code, stdout } = await runner.capture([
4560
+ "show-window-options",
4561
+ "-t",
4562
+ `=${session}:${index}`,
4563
+ "automatic-rename"
4564
+ ]);
4565
+ return code === 0 && /\boff\b/.test(stdout);
4566
+ }
4567
+ async function renameWindow(session, index, title, runner) {
4568
+ return await runner.run(["rename-window", "-t", `=${session}:${index}`, "--", title]) === 0;
4569
+ }
4570
+ async function runChatTabNamingPass(orch, deps = realDeps) {
4571
+ let renamed = 0;
4572
+ for (const task of orch.listTasks()) {
4573
+ if (task.kind === "main" || !task.worktreePath)
4574
+ continue;
4575
+ const session = tmuxSessionName(task.id);
4576
+ const windows = await listChatTabWindows(session, deps.runner);
4577
+ if (windows.length === 0)
4578
+ continue;
4579
+ const originIndex = windows.reduce((min, w) => Math.min(min, w.index), Number.POSITIVE_INFINITY);
4580
+ const vendor = task.vendor ?? DEFAULT_TASK_VENDOR;
4581
+ for (const w of windows) {
4582
+ try {
4583
+ if (await windowNamedManually(session, w.index, deps.runner))
4584
+ continue;
4585
+ const title = w.sessionId ? await deps.titleFromSessionId(vendor, w.sessionId) : w.index === originIndex ? await deps.titleFromWorktree(task.worktreePath, vendor) : "";
4586
+ if (title && await renameWindow(session, w.index, title, deps.runner))
4587
+ renamed++;
4588
+ } catch {}
4589
+ }
4590
+ }
4591
+ return renamed;
4592
+ }
4593
+ var realRunner, realDeps;
4594
+ var init_chat_tab_naming = __esm(() => {
4595
+ init_auto_title();
4596
+ init_client2();
4597
+ realRunner = { capture: runTmuxCapturing, run: runTmux };
4598
+ realDeps = {
4599
+ runner: realRunner,
4600
+ titleFromSessionId: deriveTitleFromSessionId,
4601
+ titleFromWorktree: deriveTitleFromSession
4602
+ };
4603
+ });
4604
+
4605
+ // src/daemon/crash-log.ts
4606
+ function toError(err) {
4607
+ if (err instanceof Error)
4608
+ return err;
4609
+ let text;
4610
+ try {
4611
+ text = typeof err === "string" ? err : JSON.stringify(err);
4612
+ } catch {
4613
+ text = String(err);
4614
+ }
4615
+ return new Error(text);
4616
+ }
4617
+ function formatCrashEntry(kind, err, now = new Date) {
4618
+ const e = toError(err);
4619
+ return `[${now.toISOString()}] daemon ${kind}: ${e.stack ?? `${e.name}: ${e.message}`}
4620
+ `;
4621
+ }
4622
+ function formatDaemonError(subsystem, err, now = new Date) {
4623
+ const e = toError(err);
4624
+ return `[${now.toISOString()}] daemon error [${subsystem}]: ${e.stack ?? `${e.name}: ${e.message}`}
4625
+ `;
4626
+ }
4627
+ function logDaemonError(subsystem, err) {
4628
+ process.stderr.write(formatDaemonError(subsystem, err));
4629
+ }
4630
+ function installDaemonCrashHandlers(log = (l) => process.stderr.write(l)) {
4631
+ if (onRejection || onException)
4632
+ return;
4633
+ onRejection = (reason) => log(formatCrashEntry("unhandledRejection", reason));
4634
+ onException = (err) => log(formatCrashEntry("uncaughtException", err));
4635
+ process.on("unhandledRejection", onRejection);
4636
+ process.on("uncaughtException", onException);
4637
+ }
4638
+ var onRejection, onException;
4639
+
4640
+ // src/daemon/auto-title-poller.ts
4641
+ async function runAutoTitlePass(orch, derive = deriveTitleFromSession) {
4642
+ const renamed = [];
4643
+ for (const task of orch.listTasks()) {
4644
+ if (task.title !== PLACEHOLDER_TASK_TITLE || !task.worktreePath)
4645
+ continue;
4646
+ try {
4647
+ const title = await derive(task.worktreePath, task.vendor ?? DEFAULT_TASK_VENDOR);
4648
+ if (!title)
4649
+ continue;
4650
+ const current = orch.getTask(task.id);
4651
+ if (!current || current.title !== PLACEHOLDER_TASK_TITLE)
4652
+ continue;
4653
+ await orch.setTitle(task.id, title);
4654
+ renamed.push({ id: task.id, title });
4655
+ } catch (err) {
4656
+ logDaemonError("auto-title-poller", err);
4657
+ }
4658
+ }
4659
+ return renamed;
4660
+ }
4661
+ function startAutoTitlePoller(orch, intervalMs = DEFAULT_AUTO_TITLE_POLL_MS) {
4662
+ if (intervalMs <= 0)
4663
+ return () => {};
4664
+ let running = false;
4665
+ const tick = () => {
4666
+ if (running)
4667
+ return;
4668
+ running = true;
4669
+ runAutoTitlePass(orch).then(() => runChatTabNamingPass(orch)).catch((err) => logDaemonError("auto-title-poller", err)).finally(() => {
4670
+ running = false;
4671
+ });
4672
+ };
4673
+ const timer = setInterval(tick, intervalMs);
4674
+ timer.unref?.();
4675
+ return () => clearInterval(timer);
4676
+ }
4677
+ var DEFAULT_AUTO_TITLE_POLL_MS = 4000;
4678
+ var init_auto_title_poller = __esm(() => {
4679
+ init_auto_title();
4680
+ init_core();
4681
+ init_chat_tab_naming();
4682
+ });
4683
+
4684
+ // src/daemon/event-bus.ts
4685
+ class DaemonEventBus {
4686
+ last = new Map;
4687
+ sinks = new Set;
4688
+ publish(channel, payload) {
4689
+ this.last.set(channel, payload);
4690
+ const event = { channel, payload };
4691
+ for (const sink of this.sinks)
4692
+ sink(event);
4693
+ }
4694
+ snapshot() {
4695
+ return [...this.last].map(([channel, payload]) => ({ channel, payload }));
4696
+ }
4697
+ onPublish(sink) {
4698
+ this.sinks.add(sink);
4699
+ return () => {
4700
+ this.sinks.delete(sink);
4701
+ };
4702
+ }
4703
+ }
4704
+
4705
+ // src/daemon/paths.ts
4706
+ import { createHash as createHash2 } from "crypto";
4707
+ import { homedir as homedir6, tmpdir } from "os";
4708
+ import { join as join3 } from "path";
4709
+ function shortHomeTag(homeDir2) {
4710
+ return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
4711
+ }
4712
+ function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
4713
+ if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4714
+ return naturalPath;
4715
+ const tag = shortHomeTag(homeDir2);
4716
+ const suffix = pidTag === undefined ? "" : `-${pidTag}`;
4717
+ const fallback = join3(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
4718
+ if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
4719
+ return fallback;
4720
+ throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
4721
+ }
4722
+ function defaultDaemonSocketPath(homeDir2) {
4723
+ const override = process.env.KOBE_DAEMON_SOCKET_PATH;
4724
+ if (override && override.length > 0)
4725
+ return override;
4726
+ const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
4727
+ if (explicit && explicit.length > 0) {
4728
+ return fitSocketPath(join3(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
4729
+ }
4730
+ const runtimeDir = process.env.XDG_RUNTIME_DIR;
4731
+ if (runtimeDir && runtimeDir.length > 0) {
4732
+ return fitSocketPath(join3(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
4733
+ }
4734
+ const home = homedir6();
4735
+ return fitSocketPath(join3(home, ".kobe", "daemon.sock"), home, "daemon");
4736
+ }
4737
+ function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4738
+ const override = process.env.KOBE_DAEMON_PID_PATH;
4739
+ if (override && override.length > 0)
4740
+ return override;
4741
+ return join3(homeDir2, ".kobe", "daemon.pid");
4742
+ }
4743
+ function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4744
+ return join3(homeDir2, ".kobe", "daemon.log");
4745
+ }
4746
+ var SOCKET_PATH_SAFETY_LIMIT = 100;
4747
+ var init_paths2 = () => {};
4748
+
4749
+ // src/daemon/server.ts
4750
+ import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4751
+ import { createServer } from "net";
4752
+ import { dirname as dirname3 } from "path";
4753
+ function resolveIdleGraceMs() {
4754
+ const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
4755
+ if (raw === undefined)
4756
+ return DEFAULT_IDLE_GRACE_MS;
4757
+ const n = Number(raw);
4758
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_GRACE_MS;
4759
+ }
4760
+ async function startDaemonServer(orch, options = {}) {
4761
+ const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
4762
+ const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
4763
+ const startedAt = options.startedAt ?? new Date;
4764
+ const clients = new Set;
4765
+ let nextClientId = 1;
4766
+ const idleGraceMs = resolveIdleGraceMs();
4414
4767
  let idleTimer = null;
4415
4768
  let stopping = false;
4416
- function subscriberCount() {
4769
+ function guiCount() {
4417
4770
  let n = 0;
4418
4771
  for (const c of clients)
4419
- if (c.subscribed)
4772
+ if (c.holdsLifetime)
4420
4773
  n++;
4421
4774
  return n;
4422
4775
  }
@@ -4427,12 +4780,12 @@ async function startDaemonServer(orch, options = {}) {
4427
4780
  }
4428
4781
  }
4429
4782
  function maybeArmIdleShutdown() {
4430
- if (stopping || subscriberCount() > 0)
4783
+ if (stopping || guiCount() > 0)
4431
4784
  return;
4432
4785
  cancelIdleTimer();
4433
4786
  idleTimer = setTimeout(() => {
4434
4787
  idleTimer = null;
4435
- if (stopping || subscriberCount() > 0)
4788
+ if (stopping || guiCount() > 0)
4436
4789
  return;
4437
4790
  stopSoon().catch((err) => logDaemonError("daemon-idle-shutdown", err));
4438
4791
  }, idleGraceMs);
@@ -4451,7 +4804,8 @@ async function startDaemonServer(orch, options = {}) {
4451
4804
  connectedAt: new Date,
4452
4805
  socket,
4453
4806
  buffer: "",
4454
- subscribed: false
4807
+ subscribed: false,
4808
+ holdsLifetime: false
4455
4809
  };
4456
4810
  clients.add(client);
4457
4811
  socket.on("data", (chunk) => {
@@ -4461,7 +4815,7 @@ async function startDaemonServer(orch, options = {}) {
4461
4815
  socket.on("error", () => {});
4462
4816
  socket.on("close", () => {
4463
4817
  clients.delete(client);
4464
- if (client.subscribed)
4818
+ if (client.holdsLifetime)
4465
4819
  maybeArmIdleShutdown();
4466
4820
  });
4467
4821
  });
@@ -4549,7 +4903,7 @@ async function startDaemonServer(orch, options = {}) {
4549
4903
  daemonPid: process.pid,
4550
4904
  uptimeMs: Date.now() - startedAt.getTime(),
4551
4905
  startedAt: startedAt.toISOString(),
4552
- attachedClients: subscriberCount(),
4906
+ attachedClients: guiCount(),
4553
4907
  taskCount: orch.listTasks().length,
4554
4908
  socketPath
4555
4909
  };
@@ -4649,7 +5003,10 @@ async function startDaemonServer(orch, options = {}) {
4649
5003
  }
4650
5004
  case "subscribe": {
4651
5005
  client.subscribed = true;
4652
- cancelIdleTimer();
5006
+ const role = payload.role === "gui" ? "gui" : "pane";
5007
+ client.holdsLifetime = role === "gui";
5008
+ if (client.holdsLifetime)
5009
+ cancelIdleTimer();
4653
5010
  for (const event of bus.snapshot()) {
4654
5011
  writeFrame(client, { type: "event", name: event.channel, payload: event.payload });
4655
5012
  }
@@ -5071,288 +5428,54 @@ var init_repo_cmd = __esm(() => {
5071
5428
  `);
5072
5429
  });
5073
5430
 
5074
- // src/engine/interactive-command.ts
5075
- function engineCommandKey(vendor) {
5076
- return `engineCommand.${vendor}`;
5077
- }
5078
- function defaultEngineCommand(vendor) {
5079
- return DEFAULT_COMMANDS[vendor ?? "claude"] ?? DEFAULT_COMMANDS.claude;
5080
- }
5081
- function parseEngineCommand(command) {
5082
- const out = [];
5083
- const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
5084
- for (let m = re.exec(command);m !== null; m = re.exec(command)) {
5085
- out.push(m[1] ?? m[2] ?? m[3] ?? "");
5086
- }
5087
- return out;
5088
- }
5089
- function interactiveEngineCommand(vendor) {
5090
- const v = vendor ?? "claude";
5091
- const override = getPersistedString(engineCommandKey(v))?.trim();
5092
- if (override) {
5093
- const argv = parseEngineCommand(override);
5094
- if (argv.length > 0)
5095
- return argv;
5096
- }
5097
- return defaultEngineCommand(v);
5098
- }
5099
- var DEFAULT_COMMANDS, VENDOR_LABEL;
5100
- var init_interactive_command = __esm(() => {
5101
- init_repos();
5102
- DEFAULT_COMMANDS = {
5103
- claude: ["claude"],
5104
- codex: ["codex"],
5105
- copilot: ["copilot"]
5106
- };
5107
- VENDOR_LABEL = {
5108
- claude: "Claude",
5109
- codex: "Codex",
5110
- copilot: "Copilot"
5111
- };
5112
- });
5113
-
5114
- // src/tmux/client.ts
5115
- var exports_client = {};
5116
- __export(exports_client, {
5117
- windowCount: () => windowCount,
5118
- tmuxSessionName: () => tmuxSessionName,
5119
- tmuxCommandSequence: () => tmuxCommandSequence,
5120
- tmuxAvailable: () => tmuxAvailable,
5121
- tmuxArgs: () => tmuxArgs,
5122
- tagPaneRole: () => tagPaneRole,
5123
- tagClaudePane: () => tagClaudePane,
5124
- switchClientBeforeKill: () => switchClientBeforeKill,
5125
- setWindowOption: () => setWindowOption,
5126
- setSessionOption: () => setSessionOption,
5127
- sessionExists: () => sessionExists,
5128
- sendKeys: () => sendKeys,
5129
- sendKeyName: () => sendKeyName,
5130
- runTmuxSequenceCapturing: () => runTmuxSequenceCapturing,
5131
- runTmuxSequence: () => runTmuxSequence,
5132
- runTmuxCapturing: () => runTmuxCapturing,
5133
- runTmux: () => runTmux,
5134
- paneIdByRole: () => paneIdByRole,
5135
- newWindow: () => newWindow,
5136
- killSession: () => killSession,
5137
- getSessionOptions: () => getSessionOptions,
5138
- getSessionOption: () => getSessionOption,
5139
- ensureFallbackSession: () => ensureFallbackSession,
5140
- currentSessionName: () => currentSessionName,
5141
- claudePaneIdStrict: () => claudePaneIdStrict,
5142
- claudePaneId: () => claudePaneId,
5143
- capturePaneById: () => capturePaneById,
5144
- attachArgv: () => attachArgv,
5145
- PANE_ROLE_OPTION: () => PANE_ROLE_OPTION,
5146
- KOBE_TMUX_SOCKET: () => KOBE_TMUX_SOCKET,
5147
- KOBE_HOME_SESSION: () => KOBE_HOME_SESSION,
5148
- CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION
5149
- });
5150
- function tmuxArgs(...args) {
5151
- return ["tmux", "-L", KOBE_TMUX_SOCKET, ...args];
5152
- }
5153
- function tmuxSessionName(taskId) {
5154
- return `kobe-${taskId.replace(/[^A-Za-z0-9_-]/g, "")}`;
5155
- }
5156
- function attachArgv(name) {
5157
- return tmuxArgs("attach-session", "-t", `=${name}`);
5158
- }
5159
- async function drainText(stream) {
5160
- if (!stream)
5161
- return "";
5162
- try {
5163
- return await new Response(stream).text();
5164
- } catch {
5165
- return "";
5166
- }
5167
- }
5168
- async function runTmux(args) {
5169
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "pipe" });
5170
- const [errText, code] = await Promise.all([drainText(proc.stderr), proc.exited]);
5171
- if (code !== 0 && errText.trim().length > 0) {
5172
- console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
5173
- }
5174
- return code;
5175
- }
5176
- async function runTmuxQuiet(args) {
5177
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
5178
- return proc.exited;
5179
- }
5180
- function tmuxCommandSequence(commands) {
5181
- const out = [];
5182
- for (const cmd of commands) {
5183
- if (cmd.length === 0)
5184
- continue;
5185
- if (out.length > 0)
5186
- out.push(";");
5187
- out.push(...cmd);
5188
- }
5189
- return out;
5190
- }
5191
- async function runTmuxSequence(commands) {
5192
- const args = tmuxCommandSequence(commands);
5193
- return args.length === 0 ? 0 : runTmux(args);
5194
- }
5195
- async function runTmuxCapturing(args) {
5196
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "pipe", stderr: "pipe" });
5197
- const [stdout, errText, code] = await Promise.all([drainText(proc.stdout), drainText(proc.stderr), proc.exited]);
5198
- if (code !== 0 && errText.trim().length > 0) {
5199
- console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
5200
- }
5201
- return { code, stdout };
5202
- }
5203
- async function runTmuxSequenceCapturing(commands) {
5204
- const args = tmuxCommandSequence(commands);
5205
- return args.length === 0 ? { code: 0, stdout: "" } : runTmuxCapturing(args);
5206
- }
5207
- async function tmuxAvailable() {
5208
- try {
5209
- const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
5210
- return await proc.exited === 0;
5211
- } catch {
5212
- return false;
5213
- }
5214
- }
5215
- async function sessionExists(name) {
5216
- return await runTmuxQuiet(["has-session", "-t", `=${name}`]) === 0;
5217
- }
5218
- async function windowCount(sessionName) {
5219
- const { code, stdout } = await runTmuxCapturing(["list-windows", "-t", `=${sessionName}`, "-F", "#{window_id}"]);
5220
- if (code !== 0)
5221
- return 0;
5222
- return stdout.split(`
5223
- `).filter((l) => l.trim().length > 0).length;
5224
- }
5225
- async function setSessionOption(session, option, value) {
5226
- await runTmux(["set-option", "-t", session, option, value]);
5227
- }
5228
- async function getSessionOption(session, option) {
5229
- const { code, stdout } = await runTmuxCapturing(["show-options", "-v", "-t", session, option]);
5230
- return code === 0 ? stdout.trim() : "";
5231
- }
5232
- async function getSessionOptions(session, options) {
5233
- const values = Object.fromEntries(options.map((option) => [option, undefined]));
5234
- const { code, stdout } = await runTmuxSequenceCapturing(options.map((option) => ["show-options", "-q", "-t", session, option]));
5235
- if (code !== 0)
5236
- return values;
5237
- for (const line of stdout.split(`
5238
- `)) {
5239
- const idx = line.indexOf(" ");
5240
- if (idx <= 0)
5241
- continue;
5242
- const option = line.slice(0, idx);
5243
- if (option in values)
5244
- values[option] = line.slice(idx + 1).trim();
5245
- }
5246
- return values;
5247
- }
5248
- async function tagPaneRole(paneId, role) {
5249
- await runTmux(["set-option", "-p", "-t", paneId, PANE_ROLE_OPTION, role]);
5250
- }
5251
- async function setWindowOption(target, option, value) {
5252
- await runTmux(["set-window-option", "-t", target, option, value]);
5253
- }
5254
- async function tagClaudePane(paneId) {
5255
- await tagPaneRole(paneId, CLAUDE_ROLE_VALUE);
5256
- }
5257
- async function paneIdByRole(sessionName, role, fallbackFirst = false) {
5258
- const { code, stdout } = await runTmuxCapturing([
5259
- "list-panes",
5260
- "-t",
5261
- `=${sessionName}`,
5262
- "-F",
5263
- `#{pane_id} #{${PANE_ROLE_OPTION}}`
5264
- ]);
5265
- if (code !== 0)
5266
- return "";
5267
- let firstId = "";
5268
- for (const line of stdout.split(`
5269
- `)) {
5270
- const [id, paneRole] = line.split("\t");
5271
- if (!id)
5272
- continue;
5273
- if (!firstId)
5274
- firstId = id.trim();
5275
- if (paneRole?.trim() === role)
5276
- return id.trim();
5277
- }
5278
- return fallbackFirst ? firstId : "";
5279
- }
5280
- async function claudePaneId(sessionName) {
5281
- return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, true);
5282
- }
5283
- async function claudePaneIdStrict(sessionName) {
5284
- return paneIdByRole(sessionName, CLAUDE_ROLE_VALUE, false);
5285
- }
5286
- async function capturePaneById(paneId, lines) {
5287
- if (!paneId)
5288
- return "";
5289
- const args = ["capture-pane", "-t", paneId, "-p"];
5290
- if (typeof lines === "number" && lines > 0)
5291
- args.push("-S", String(-lines));
5292
- const { code, stdout } = await runTmuxCapturing(args);
5293
- return code === 0 ? stdout : "";
5294
- }
5295
- async function sendKeys(target, text) {
5296
- await runTmux(["send-keys", "-t", target, "-l", "--", text]);
5297
- }
5298
- async function sendKeyName(target, key) {
5299
- await runTmux(["send-keys", "-t", target, key]);
5300
- }
5301
- async function newWindow(session, opts) {
5302
- const args = ["new-window", "-t", `=${session}`, "-c", opts.cwd];
5303
- if (opts.name)
5304
- args.push("-n", opts.name);
5305
- args.push(opts.command);
5306
- await runTmux(args);
5307
- }
5308
- async function currentSessionName() {
5309
- const args = ["display-message", "-p"];
5310
- const target = process.env.TMUX_PANE;
5311
- if (target && target.length > 0)
5312
- args.push("-t", target);
5313
- args.push("#{session_name}");
5314
- const { code, stdout } = await runTmuxCapturing(args);
5315
- const name = stdout.trim();
5316
- return code === 0 && name.length > 0 ? name : null;
5431
+ // src/engine/interactive-command.ts
5432
+ import { randomUUID } from "crypto";
5433
+ function engineCommandKey(vendor) {
5434
+ return `engineCommand.${vendor}`;
5317
5435
  }
5318
- async function killSession(name) {
5319
- if (await sessionExists(name))
5320
- await runTmux(["kill-session", "-t", `=${name}`]);
5436
+ function defaultEngineCommand(vendor) {
5437
+ return DEFAULT_COMMANDS[vendor ?? "claude"] ?? DEFAULT_COMMANDS.claude;
5321
5438
  }
5322
- async function ensureFallbackSession() {
5323
- const name = KOBE_HOME_SESSION;
5324
- if (!await sessionExists(name)) {
5325
- await runTmux([
5326
- "new-session",
5327
- "-d",
5328
- "-s",
5329
- name,
5330
- "-x",
5331
- "220",
5332
- "-y",
5333
- "50",
5334
- "sh",
5335
- "-c",
5336
- 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
5337
- ]);
5439
+ function parseEngineCommand(command) {
5440
+ const out = [];
5441
+ const re = /"([^"]*)"|'([^']*)'|(\S+)/g;
5442
+ for (let m = re.exec(command);m !== null; m = re.exec(command)) {
5443
+ out.push(m[1] ?? m[2] ?? m[3] ?? "");
5338
5444
  }
5339
- return name;
5445
+ return out;
5340
5446
  }
5341
- async function switchClientBeforeKill(killedName, nextSessionName) {
5342
- const current = await currentSessionName();
5343
- if (current !== killedName)
5344
- return;
5345
- if (nextSessionName && nextSessionName !== killedName && await sessionExists(nextSessionName)) {
5346
- await runTmux(["switch-client", "-t", `=${nextSessionName}`]);
5347
- return;
5447
+ function interactiveEngineCommand(vendor) {
5448
+ const v = vendor ?? "claude";
5449
+ const override = getPersistedString(engineCommandKey(v))?.trim();
5450
+ if (override) {
5451
+ const argv = parseEngineCommand(override);
5452
+ if (argv.length > 0)
5453
+ return argv;
5348
5454
  }
5349
- const fallback = await ensureFallbackSession();
5350
- await runTmux(["switch-client", "-t", `=${fallback}`]);
5455
+ return defaultEngineCommand(v);
5351
5456
  }
5352
- var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", KOBE_HOME_SESSION = "kobe-home";
5353
- var init_client2 = __esm(() => {
5354
- KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
5355
- CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
5457
+ function withClaudeSessionId(argv, vendor) {
5458
+ if ((vendor ?? "claude") !== "claude")
5459
+ return { argv, sessionId: null };
5460
+ if (argv.some((a) => CLAUDE_SESSION_CONTROL_FLAGS.has(a)))
5461
+ return { argv, sessionId: null };
5462
+ const sessionId = randomUUID();
5463
+ return { argv: [...argv, "--session-id", sessionId], sessionId };
5464
+ }
5465
+ var DEFAULT_COMMANDS, VENDOR_LABEL, CLAUDE_SESSION_CONTROL_FLAGS;
5466
+ var init_interactive_command = __esm(() => {
5467
+ init_repos();
5468
+ DEFAULT_COMMANDS = {
5469
+ claude: ["claude"],
5470
+ codex: ["codex"],
5471
+ copilot: ["copilot"]
5472
+ };
5473
+ VENDOR_LABEL = {
5474
+ claude: "Claude",
5475
+ codex: "Codex",
5476
+ copilot: "Copilot"
5477
+ };
5478
+ CLAUDE_SESSION_CONTROL_FLAGS = new Set(["--session-id", "--resume", "-r", "--continue", "-c", "--from-pr"]);
5356
5479
  });
5357
5480
 
5358
5481
  // src/tmux/prompt-delivery.ts
@@ -5475,6 +5598,7 @@ __export(exports_tmux, {
5475
5598
  tmuxAvailable: () => tmuxAvailable,
5476
5599
  switchClientBeforeKill: () => switchClientBeforeKill,
5477
5600
  sessionExists: () => sessionExists,
5601
+ refreshKobeWorkspacePanes: () => refreshKobeWorkspacePanes,
5478
5602
  quickCreate: () => quickCreate,
5479
5603
  openUpdateTab: () => openUpdateTab,
5480
5604
  openSettingsTab: () => openSettingsTab,
@@ -5482,6 +5606,7 @@ __export(exports_tmux, {
5482
5606
  newChatTab: () => newChatTab,
5483
5607
  killSession: () => killSession,
5484
5608
  ensureSession: () => ensureSession,
5609
+ currentSessionName: () => currentSessionName,
5485
5610
  attachArgv: () => attachArgv,
5486
5611
  PANE_VERSION_OPTION: () => PANE_VERSION_OPTION,
5487
5612
  CHAT_TAB_SWITCH_BINDINGS: () => CHAT_TAB_SWITCH_BINDINGS,
@@ -5544,6 +5669,7 @@ async function ensureSessionImpl(opts) {
5544
5669
  await runTmux(["kill-session", "-t", `=${opts.name}`]);
5545
5670
  }
5546
5671
  const inv = kobeCliInvocation();
5672
+ const launch = withClaudeSessionId(opts.command, opts.vendor);
5547
5673
  const r0 = await runTmuxCapturing([
5548
5674
  "new-session",
5549
5675
  "-d",
@@ -5555,7 +5681,7 @@ async function ensureSessionImpl(opts) {
5555
5681
  "-P",
5556
5682
  "-F",
5557
5683
  "#{pane_id}",
5558
- engineLaunchLine(shellQuoteArgv(opts.command), {
5684
+ engineLaunchLine(shellQuoteArgv(launch.argv), {
5559
5685
  initScript: opts.initScript,
5560
5686
  markerPath: opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
5561
5687
  })
@@ -5565,6 +5691,8 @@ async function ensureSessionImpl(opts) {
5565
5691
  console.error("[kobe tmux] new-session returned no pane id; session creation failed");
5566
5692
  return false;
5567
5693
  }
5694
+ if (launch.sessionId)
5695
+ await setWindowOption(pane0, CHAT_TAB_SESSION_ID_OPTION, launch.sessionId);
5568
5696
  await runTmuxSequence([
5569
5697
  ...opts.taskId ? [["set-option", "-t", opts.name, "@kobe_task", opts.taskId]] : [],
5570
5698
  ["set-option", "-t", opts.name, "@kobe_worktree", opts.cwd],
@@ -5706,6 +5834,58 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
5706
5834
  if (commands.length > 0)
5707
5835
  await runTmuxSequence(commands);
5708
5836
  }
5837
+ async function refreshKobeWorkspacePanes(session) {
5838
+ const sessionOptions = await getSessionOptions(session, ["@kobe_worktree", "@kobe_task", "@kobe_vendor"]);
5839
+ const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
5840
+ const taskId = sessionOptions["@kobe_task"] || undefined;
5841
+ const vendor = sessionOptions["@kobe_vendor"] || undefined;
5842
+ const inv = kobeCliInvocation();
5843
+ const envPrefix = inheritedEnvPrefix();
5844
+ const { code, stdout } = await runTmuxCapturing([
5845
+ "list-panes",
5846
+ "-s",
5847
+ "-t",
5848
+ `=${session}`,
5849
+ "-F",
5850
+ `#{window_id} #{pane_id} #{@kobe_role} #{${PANE_VERSION_OPTION}}`
5851
+ ]);
5852
+ if (code !== 0)
5853
+ return;
5854
+ const byWindow = new Map;
5855
+ for (const row of parseKobePaneRows(stdout)) {
5856
+ const panes = byWindow.get(row.windowId) ?? [];
5857
+ panes.push(row);
5858
+ byWindow.set(row.windowId, panes);
5859
+ }
5860
+ const commands = [];
5861
+ for (const panes of byWindow.values()) {
5862
+ const claudePane = panes.find((pane) => pane.role === "claude")?.paneId;
5863
+ const tasksPane = panes.find((pane) => pane.role === "tasks");
5864
+ const opsPane = panes.find((pane) => pane.role === "ops");
5865
+ if (tasksPane) {
5866
+ commands.push(["respawn-pane", "-k", "-t", tasksPane.paneId, "-c", cwd, keepAlive(envPrefix + tasksPaneCommand(inv))], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5867
+ }
5868
+ if (opsPane && claudePane) {
5869
+ commands.push([
5870
+ "respawn-pane",
5871
+ "-k",
5872
+ "-t",
5873
+ opsPane.paneId,
5874
+ "-c",
5875
+ cwd,
5876
+ keepAlive(envPrefix + opsPaneCommand({
5877
+ cwd,
5878
+ taskId,
5879
+ claudePaneId: claudePane,
5880
+ cliInvocation: inv,
5881
+ vendor
5882
+ }))
5883
+ ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5884
+ }
5885
+ }
5886
+ if (commands.length > 0)
5887
+ await runTmuxSequence(commands);
5888
+ }
5709
5889
  function inheritedEnvPrefix() {
5710
5890
  const parts = [];
5711
5891
  for (const key of ["KOBE_HOME_DIR", "KOBE_DAEMON_SOCKET_PATH", "KOBE_TMUX_SOCKET"]) {
@@ -5778,6 +5958,7 @@ async function newChatTab(session, vendorOverride) {
5778
5958
  if (vendorOverride)
5779
5959
  await rememberSessionVendor(session, taskId, vendorOverride);
5780
5960
  const command = interactiveEngineCommand(vendor);
5961
+ const launch = withClaudeSessionId(command, vendor);
5781
5962
  const inv = kobeCliInvocation();
5782
5963
  const r = await runTmuxCapturing([
5783
5964
  "new-window",
@@ -5788,11 +5969,13 @@ async function newChatTab(session, vendorOverride) {
5788
5969
  "-P",
5789
5970
  "-F",
5790
5971
  "#{pane_id}",
5791
- keepAlive(shellQuoteArgv(command))
5972
+ keepAlive(shellQuoteArgv(launch.argv))
5792
5973
  ]);
5793
5974
  const claudePane = r.stdout.trim();
5794
5975
  if (!claudePane)
5795
5976
  return;
5977
+ if (launch.sessionId)
5978
+ await setWindowOption(claudePane, CHAT_TAB_SESSION_ID_OPTION, launch.sessionId);
5796
5979
  await buildPanesAround(claudePane, { cwd, taskId, inv, vendor });
5797
5980
  }
5798
5981
  async function openSettingsTab(session) {
@@ -5944,7 +6127,8 @@ function readWorktreeChanges(worktreePath) {
5944
6127
  const out = spawnSync3("git", ["status", "--porcelain=v1"], {
5945
6128
  cwd: worktreePath,
5946
6129
  encoding: "utf8",
5947
- stdio: ["ignore", "pipe", "pipe"]
6130
+ stdio: ["ignore", "pipe", "pipe"],
6131
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
5948
6132
  });
5949
6133
  if (out.status !== 0 || !out.stdout)
5950
6134
  return ZERO;
@@ -8440,6 +8624,7 @@ class RemoteOrchestrator {
8440
8624
  connectionStateAcc;
8441
8625
  setConnectionState;
8442
8626
  ensureReachable;
8627
+ role;
8443
8628
  constructor(client, options = {}) {
8444
8629
  this.client = client;
8445
8630
  const [tasks, setTasks] = createSignal([]);
@@ -8455,6 +8640,7 @@ class RemoteOrchestrator {
8455
8640
  this.connectionStateAcc = connectionState;
8456
8641
  this.setConnectionState = (next) => setConnectionState(() => next);
8457
8642
  this.ensureReachable = options.ensureReachable ?? ensureDaemonReachable;
8643
+ this.role = options.role ?? "pane";
8458
8644
  this.client.on("*", (frame) => this.handleEvent(frame.name, frame.payload));
8459
8645
  this.client.onLifecycle("close", () => this.setConnectionState("disconnected"));
8460
8646
  }
@@ -8475,7 +8661,7 @@ class RemoteOrchestrator {
8475
8661
  }
8476
8662
  if (hello.tasks)
8477
8663
  this.setTasks(hello.tasks.map(deserializeTask));
8478
- await this.client.subscribe();
8664
+ await this.client.subscribe({ role: this.role });
8479
8665
  this.setConnectionState("online");
8480
8666
  }
8481
8667
  connectionStateSignal() {
@@ -12820,6 +13006,15 @@ var init_account_detect = __esm(() => {
12820
13006
  };
12821
13007
  });
12822
13008
 
13009
+ // src/tui/lib/editor-prefs.ts
13010
+ function normalizeEditorKind(value) {
13011
+ return value === "nano" || value === "custom" || value === "vim" ? value : DEFAULT_EDITOR_KIND;
13012
+ }
13013
+ var EDITOR_KINDS, EDITOR_KIND_KEY = "editor.kind", EDITOR_CUSTOM_KEY = "editor.customCommand", DEFAULT_EDITOR_KIND = "vim";
13014
+ var init_editor_prefs = __esm(() => {
13015
+ EDITOR_KINDS = ["vim", "nano", "custom"];
13016
+ });
13017
+
12823
13018
  // src/tui/lib/settings-surface.ts
12824
13019
  function normalizeSettingsSurface(value) {
12825
13020
  return value === "taskpanel" ? "taskpanel" : "chattab";
@@ -13008,7 +13203,7 @@ function devRowCount(hasDaemon) {
13008
13203
  return hasDaemon ? 2 : 1;
13009
13204
  }
13010
13205
  function generalRowCount(themeCount, focusAccentCount) {
13011
- return themeCount + 1 + focusAccentCount + 4;
13206
+ return themeCount + 1 + focusAccentCount + 6;
13012
13207
  }
13013
13208
  function bodyRowCount(section, themeCount, focusAccentCount, hasDaemon) {
13014
13209
  if (section === "general")
@@ -13043,6 +13238,12 @@ function surfaceChattabRowIndex(themeCount, focusAccentCount) {
13043
13238
  function surfaceTaskpanelRowIndex(themeCount, focusAccentCount) {
13044
13239
  return surfaceChattabRowIndex(themeCount, focusAccentCount) + 1;
13045
13240
  }
13241
+ function editorKindRowIndex(themeCount, focusAccentCount) {
13242
+ return surfaceTaskpanelRowIndex(themeCount, focusAccentCount) + 1;
13243
+ }
13244
+ function editorCustomRowIndex(themeCount, focusAccentCount) {
13245
+ return editorKindRowIndex(themeCount, focusAccentCount) + 1;
13246
+ }
13046
13247
  var SECTIONS, FOCUS_ACCENT_LABEL;
13047
13248
  var init_model = __esm(() => {
13048
13249
  init_vendor();
@@ -13111,13 +13312,17 @@ function GeneralSettingsSection(props) {
13111
13312
  const soundRow = () => soundRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13112
13313
  const surfaceChattabRow = () => surfaceChattabRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13113
13314
  const surfaceTaskpanelRow = () => surfaceTaskpanelRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13315
+ const editorKindRow = () => editorKindRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13316
+ const editorCustomRow = () => editorCustomRowIndex(props.themeNames().length, FOCUS_ACCENT_SLOTS.length);
13114
13317
  const isTransparentRow = () => props.bodyRow() === transparentRowIndex(props.themeNames().length);
13115
13318
  const isToastRow = () => props.bodyRow() === toastRow();
13116
13319
  const isSoundRow = () => props.bodyRow() === soundRow();
13117
13320
  const isSurfaceChattabRow = () => props.bodyRow() === surfaceChattabRow();
13118
13321
  const isSurfaceTaskpanelRow = () => props.bodyRow() === surfaceTaskpanelRow();
13322
+ const isEditorKindRow = () => props.bodyRow() === editorKindRow();
13323
+ const isEditorCustomRow = () => props.bodyRow() === editorCustomRow();
13119
13324
  return (() => {
13120
- var _el$4 = createElement("box"), _el$5 = createElement("text"), _el$7 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$18 = createElement("text"), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("text"), _el$25 = createElement("box"), _el$26 = createElement("text"), _el$27 = createTextNode(` Toast`), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(` Sound`), _el$31 = createElement("box"), _el$32 = createElement("text"), _el$34 = createElement("text"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createTextNode(` ChatTab (separate page)`), _el$39 = createElement("box"), _el$40 = createElement("text"), _el$41 = createTextNode(` Task panel (in-pane overlay)`);
13325
+ var _el$4 = createElement("box"), _el$5 = createElement("text"), _el$7 = createElement("text"), _el$9 = createElement("box"), _el$0 = createElement("box"), _el$1 = createElement("text"), _el$11 = createElement("text"), _el$13 = createElement("box"), _el$14 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$18 = createElement("text"), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("text"), _el$25 = createElement("box"), _el$26 = createElement("text"), _el$27 = createTextNode(` Toast`), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(` Sound`), _el$31 = createElement("box"), _el$32 = createElement("text"), _el$34 = createElement("text"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createTextNode(` ChatTab (separate page)`), _el$39 = createElement("box"), _el$40 = createElement("text"), _el$41 = createTextNode(` Task panel (in-pane overlay)`), _el$42 = createElement("box"), _el$43 = createElement("text"), _el$45 = createElement("text"), _el$47 = createElement("box"), _el$48 = createElement("text"), _el$49 = createElement("box"), _el$50 = createElement("text");
13121
13326
  insertNode(_el$4, _el$5);
13122
13327
  insertNode(_el$4, _el$7);
13123
13328
  insertNode(_el$4, _el$9);
@@ -13125,6 +13330,7 @@ function GeneralSettingsSection(props) {
13125
13330
  insertNode(_el$4, _el$15);
13126
13331
  insertNode(_el$4, _el$20);
13127
13332
  insertNode(_el$4, _el$31);
13333
+ insertNode(_el$4, _el$42);
13128
13334
  setProp(_el$4, "flexDirection", "column");
13129
13335
  setProp(_el$4, "gap", 1);
13130
13336
  insertNode(_el$5, createTextNode(`Theme`));
@@ -13139,33 +13345,33 @@ function GeneralSettingsSection(props) {
13139
13345
  const isCursor = () => props.level() === "body" && props.bodyRow() === i();
13140
13346
  const isSelected = () => name === themeCtx.selected;
13141
13347
  return (() => {
13142
- var _el$42 = createElement("box"), _el$43 = createElement("text");
13143
- insertNode(_el$42, _el$43);
13144
- setProp(_el$42, "flexDirection", "row");
13145
- setProp(_el$42, "gap", 1);
13146
- setProp(_el$42, "paddingLeft", 1);
13147
- setProp(_el$42, "paddingRight", 1);
13148
- setProp(_el$42, "onMouseUp", () => {
13348
+ var _el$51 = createElement("box"), _el$52 = createElement("text");
13349
+ insertNode(_el$51, _el$52);
13350
+ setProp(_el$51, "flexDirection", "row");
13351
+ setProp(_el$51, "gap", 1);
13352
+ setProp(_el$51, "paddingLeft", 1);
13353
+ setProp(_el$51, "paddingRight", 1);
13354
+ setProp(_el$51, "onMouseUp", () => {
13149
13355
  props.setLevel("body");
13150
13356
  props.setBodyRow(i());
13151
13357
  props.setThemeCursor(i());
13152
- themeCtx.set(name);
13358
+ props.selectTheme(name);
13153
13359
  });
13154
- setProp(_el$43, "wrapMode", "none");
13155
- insert(_el$43, () => isSelected() ? "\u25CF " : " ", null);
13156
- insert(_el$43, name, null);
13360
+ setProp(_el$52, "wrapMode", "none");
13361
+ insert(_el$52, () => isSelected() ? "\u25CF " : " ", null);
13362
+ insert(_el$52, name, null);
13157
13363
  effect((_p$) => {
13158
- var _v$32 = isCursor() ? theme.primary : undefined, _v$33 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$34 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13159
- _v$32 !== _p$.e && (_p$.e = setProp(_el$42, "backgroundColor", _v$32, _p$.e));
13160
- _v$33 !== _p$.t && (_p$.t = setProp(_el$43, "fg", _v$33, _p$.t));
13161
- _v$34 !== _p$.a && (_p$.a = setProp(_el$43, "attributes", _v$34, _p$.a));
13364
+ var _v$40 = isCursor() ? theme.primary : undefined, _v$41 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.accent : theme.text, _v$42 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13365
+ _v$40 !== _p$.e && (_p$.e = setProp(_el$51, "backgroundColor", _v$40, _p$.e));
13366
+ _v$41 !== _p$.t && (_p$.t = setProp(_el$52, "fg", _v$41, _p$.t));
13367
+ _v$42 !== _p$.a && (_p$.a = setProp(_el$52, "attributes", _v$42, _p$.a));
13162
13368
  return _p$;
13163
13369
  }, {
13164
13370
  e: undefined,
13165
13371
  t: undefined,
13166
13372
  a: undefined
13167
13373
  });
13168
- return _el$42;
13374
+ return _el$51;
13169
13375
  })();
13170
13376
  }
13171
13377
  }));
@@ -13185,7 +13391,7 @@ function GeneralSettingsSection(props) {
13185
13391
  setProp(_el$13, "onMouseUp", () => {
13186
13392
  props.setLevel("body");
13187
13393
  props.setBodyRow(transparentRowIndex(props.themeNames().length));
13188
- themeCtx.setTransparentBackground(!themeCtx.transparentBackground);
13394
+ props.toggleTransparent();
13189
13395
  });
13190
13396
  setProp(_el$14, "wrapMode", "none");
13191
13397
  insert(_el$14, () => themeCtx.transparentBackground ? "[x] on" : "[ ] off");
@@ -13204,32 +13410,32 @@ function GeneralSettingsSection(props) {
13204
13410
  const isCursor = () => props.level() === "body" && props.bodyRow() === rowIndex();
13205
13411
  const isSelected = () => themeCtx.focusAccent === slot;
13206
13412
  return (() => {
13207
- var _el$44 = createElement("box"), _el$45 = createElement("text");
13208
- insertNode(_el$44, _el$45);
13209
- setProp(_el$44, "flexDirection", "row");
13210
- setProp(_el$44, "gap", 1);
13211
- setProp(_el$44, "paddingLeft", 1);
13212
- setProp(_el$44, "paddingRight", 1);
13213
- setProp(_el$44, "onMouseUp", () => {
13413
+ var _el$53 = createElement("box"), _el$54 = createElement("text");
13414
+ insertNode(_el$53, _el$54);
13415
+ setProp(_el$53, "flexDirection", "row");
13416
+ setProp(_el$53, "gap", 1);
13417
+ setProp(_el$53, "paddingLeft", 1);
13418
+ setProp(_el$53, "paddingRight", 1);
13419
+ setProp(_el$53, "onMouseUp", () => {
13214
13420
  props.setLevel("body");
13215
13421
  props.setBodyRow(rowIndex());
13216
- themeCtx.setFocusAccent(slot);
13422
+ props.selectFocusAccent(slot);
13217
13423
  });
13218
- setProp(_el$45, "wrapMode", "none");
13219
- insert(_el$45, () => isSelected() ? "\u25CF " : " ", null);
13220
- insert(_el$45, () => FOCUS_ACCENT_LABEL[slot], null);
13424
+ setProp(_el$54, "wrapMode", "none");
13425
+ insert(_el$54, () => isSelected() ? "\u25CF " : " ", null);
13426
+ insert(_el$54, () => FOCUS_ACCENT_LABEL[slot], null);
13221
13427
  effect((_p$) => {
13222
- var _v$35 = isCursor() ? theme.primary : undefined, _v$36 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$37 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13223
- _v$35 !== _p$.e && (_p$.e = setProp(_el$44, "backgroundColor", _v$35, _p$.e));
13224
- _v$36 !== _p$.t && (_p$.t = setProp(_el$45, "fg", _v$36, _p$.t));
13225
- _v$37 !== _p$.a && (_p$.a = setProp(_el$45, "attributes", _v$37, _p$.a));
13428
+ var _v$43 = isCursor() ? theme.primary : undefined, _v$44 = isCursor() ? theme.selectedListItemText : isSelected() ? theme.focusAccent : theme.text, _v$45 = isCursor() || isSelected() ? TextAttributes5.BOLD : undefined;
13429
+ _v$43 !== _p$.e && (_p$.e = setProp(_el$53, "backgroundColor", _v$43, _p$.e));
13430
+ _v$44 !== _p$.t && (_p$.t = setProp(_el$54, "fg", _v$44, _p$.t));
13431
+ _v$45 !== _p$.a && (_p$.a = setProp(_el$54, "attributes", _v$45, _p$.a));
13226
13432
  return _p$;
13227
13433
  }, {
13228
13434
  e: undefined,
13229
13435
  t: undefined,
13230
13436
  a: undefined
13231
13437
  });
13232
- return _el$44;
13438
+ return _el$53;
13233
13439
  })();
13234
13440
  }
13235
13441
  }), null);
@@ -13305,8 +13511,42 @@ function GeneralSettingsSection(props) {
13305
13511
  insertNode(_el$40, _el$41);
13306
13512
  setProp(_el$40, "wrapMode", "none");
13307
13513
  insert(_el$40, () => props.settingsSurface() === "taskpanel" ? "[x]" : "[ ]", _el$41);
13514
+ insertNode(_el$42, _el$43);
13515
+ insertNode(_el$42, _el$45);
13516
+ insertNode(_el$42, _el$47);
13517
+ insertNode(_el$42, _el$49);
13518
+ setProp(_el$42, "flexDirection", "column");
13519
+ setProp(_el$42, "gap", 0);
13520
+ setProp(_el$42, "paddingTop", 1);
13521
+ insertNode(_el$43, createTextNode(`Editor`));
13522
+ insertNode(_el$45, createTextNode(`What \`e\` opens a file with in the file tree (enter stays the read-only preview). enter on the row below cycles vim / nano / custom; if the editor isn't installed it falls back to the preview.`));
13523
+ setProp(_el$45, "wrapMode", "word");
13524
+ insertNode(_el$47, _el$48);
13525
+ setProp(_el$47, "flexDirection", "row");
13526
+ setProp(_el$47, "gap", 1);
13527
+ setProp(_el$47, "paddingLeft", 1);
13528
+ setProp(_el$47, "paddingRight", 1);
13529
+ setProp(_el$47, "onMouseUp", () => {
13530
+ props.setLevel("body");
13531
+ props.setBodyRow(editorKindRow());
13532
+ props.cycleEditorKind();
13533
+ });
13534
+ setProp(_el$48, "wrapMode", "none");
13535
+ insert(_el$48, () => `editor: < ${props.editorKind()} > (enter to change)`);
13536
+ insertNode(_el$49, _el$50);
13537
+ setProp(_el$49, "flexDirection", "row");
13538
+ setProp(_el$49, "gap", 1);
13539
+ setProp(_el$49, "paddingLeft", 1);
13540
+ setProp(_el$49, "paddingRight", 1);
13541
+ setProp(_el$49, "onMouseUp", () => {
13542
+ props.setLevel("body");
13543
+ props.setBodyRow(editorCustomRow());
13544
+ props.editEditorCustom();
13545
+ });
13546
+ setProp(_el$50, "wrapMode", "none");
13547
+ insert(_el$50, () => `custom: ${props.editorCustomCommand().trim() || "(unset \u2014 enter to edit)"}`);
13308
13548
  effect((_p$) => {
13309
- var _v$4 = theme.text, _v$5 = TextAttributes5.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = TextAttributes5.BOLD, _v$9 = theme.textMuted, _v$0 = isTransparentRow() ? theme.primary : undefined, _v$1 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$10 = TextAttributes5.BOLD, _v$11 = theme.text, _v$12 = TextAttributes5.BOLD, _v$13 = theme.textMuted, _v$14 = theme.text, _v$15 = TextAttributes5.BOLD, _v$16 = theme.textMuted, _v$17 = isToastRow() ? theme.primary : undefined, _v$18 = isToastRow() ? theme.selectedListItemText : props.toastEnabled() ? theme.accent : theme.textMuted, _v$19 = TextAttributes5.BOLD, _v$20 = isSoundRow() ? theme.primary : undefined, _v$21 = isSoundRow() ? theme.selectedListItemText : props.soundEnabled() ? theme.accent : theme.textMuted, _v$22 = TextAttributes5.BOLD, _v$23 = theme.text, _v$24 = TextAttributes5.BOLD, _v$25 = theme.textMuted, _v$26 = isSurfaceChattabRow() ? theme.primary : undefined, _v$27 = isSurfaceChattabRow() ? theme.selectedListItemText : props.settingsSurface() === "chattab" ? theme.accent : theme.textMuted, _v$28 = TextAttributes5.BOLD, _v$29 = isSurfaceTaskpanelRow() ? theme.primary : undefined, _v$30 = isSurfaceTaskpanelRow() ? theme.selectedListItemText : props.settingsSurface() === "taskpanel" ? theme.accent : theme.textMuted, _v$31 = TextAttributes5.BOLD;
13549
+ var _v$4 = theme.text, _v$5 = TextAttributes5.BOLD, _v$6 = theme.textMuted, _v$7 = theme.text, _v$8 = TextAttributes5.BOLD, _v$9 = theme.textMuted, _v$0 = isTransparentRow() ? theme.primary : undefined, _v$1 = isTransparentRow() ? theme.selectedListItemText : themeCtx.transparentBackground ? theme.accent : theme.textMuted, _v$10 = TextAttributes5.BOLD, _v$11 = theme.text, _v$12 = TextAttributes5.BOLD, _v$13 = theme.textMuted, _v$14 = theme.text, _v$15 = TextAttributes5.BOLD, _v$16 = theme.textMuted, _v$17 = isToastRow() ? theme.primary : undefined, _v$18 = isToastRow() ? theme.selectedListItemText : props.toastEnabled() ? theme.accent : theme.textMuted, _v$19 = TextAttributes5.BOLD, _v$20 = isSoundRow() ? theme.primary : undefined, _v$21 = isSoundRow() ? theme.selectedListItemText : props.soundEnabled() ? theme.accent : theme.textMuted, _v$22 = TextAttributes5.BOLD, _v$23 = theme.text, _v$24 = TextAttributes5.BOLD, _v$25 = theme.textMuted, _v$26 = isSurfaceChattabRow() ? theme.primary : undefined, _v$27 = isSurfaceChattabRow() ? theme.selectedListItemText : props.settingsSurface() === "chattab" ? theme.accent : theme.textMuted, _v$28 = TextAttributes5.BOLD, _v$29 = isSurfaceTaskpanelRow() ? theme.primary : undefined, _v$30 = isSurfaceTaskpanelRow() ? theme.selectedListItemText : props.settingsSurface() === "taskpanel" ? theme.accent : theme.textMuted, _v$31 = TextAttributes5.BOLD, _v$32 = theme.text, _v$33 = TextAttributes5.BOLD, _v$34 = theme.textMuted, _v$35 = isEditorKindRow() ? theme.primary : undefined, _v$36 = isEditorKindRow() ? theme.selectedListItemText : theme.accent, _v$37 = TextAttributes5.BOLD, _v$38 = isEditorCustomRow() ? theme.primary : undefined, _v$39 = isEditorCustomRow() ? theme.selectedListItemText : props.editorKind() === "custom" ? theme.text : theme.textMuted;
13310
13550
  _v$4 !== _p$.e && (_p$.e = setProp(_el$5, "fg", _v$4, _p$.e));
13311
13551
  _v$5 !== _p$.t && (_p$.t = setProp(_el$5, "attributes", _v$5, _p$.t));
13312
13552
  _v$6 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$6, _p$.a));
@@ -13337,6 +13577,14 @@ function GeneralSettingsSection(props) {
13337
13577
  _v$29 !== _p$.B && (_p$.B = setProp(_el$39, "backgroundColor", _v$29, _p$.B));
13338
13578
  _v$30 !== _p$.v && (_p$.v = setProp(_el$40, "fg", _v$30, _p$.v));
13339
13579
  _v$31 !== _p$.k && (_p$.k = setProp(_el$40, "attributes", _v$31, _p$.k));
13580
+ _v$32 !== _p$.x && (_p$.x = setProp(_el$43, "fg", _v$32, _p$.x));
13581
+ _v$33 !== _p$.j && (_p$.j = setProp(_el$43, "attributes", _v$33, _p$.j));
13582
+ _v$34 !== _p$.q && (_p$.q = setProp(_el$45, "fg", _v$34, _p$.q));
13583
+ _v$35 !== _p$.z && (_p$.z = setProp(_el$47, "backgroundColor", _v$35, _p$.z));
13584
+ _v$36 !== _p$.P && (_p$.P = setProp(_el$48, "fg", _v$36, _p$.P));
13585
+ _v$37 !== _p$.H && (_p$.H = setProp(_el$48, "attributes", _v$37, _p$.H));
13586
+ _v$38 !== _p$.F && (_p$.F = setProp(_el$49, "backgroundColor", _v$38, _p$.F));
13587
+ _v$39 !== _p$.M && (_p$.M = setProp(_el$50, "fg", _v$39, _p$.M));
13340
13588
  return _p$;
13341
13589
  }, {
13342
13590
  e: undefined,
@@ -13368,7 +13616,15 @@ function GeneralSettingsSection(props) {
13368
13616
  C: undefined,
13369
13617
  B: undefined,
13370
13618
  v: undefined,
13371
- k: undefined
13619
+ k: undefined,
13620
+ x: undefined,
13621
+ j: undefined,
13622
+ q: undefined,
13623
+ z: undefined,
13624
+ P: undefined,
13625
+ H: undefined,
13626
+ F: undefined,
13627
+ M: undefined
13372
13628
  });
13373
13629
  return _el$4;
13374
13630
  })();
@@ -13378,47 +13634,47 @@ function EngineSettingsSection(props) {
13378
13634
  theme
13379
13635
  } = useTheme();
13380
13636
  return (() => {
13381
- var _el$46 = createElement("box"), _el$47 = createElement("text"), _el$49 = createElement("text"), _el$51 = createElement("box");
13382
- insertNode(_el$46, _el$47);
13383
- insertNode(_el$46, _el$49);
13384
- insertNode(_el$46, _el$51);
13385
- setProp(_el$46, "flexDirection", "column");
13386
- setProp(_el$46, "gap", 1);
13387
- insertNode(_el$47, createTextNode(`Launch command`));
13388
- insertNode(_el$49, createTextNode(`The command each engine's task pane runs. Override it when your binary isn't on PATH as \`claude\` / \`codex\` (e.g. it's \`cl\`) or to pass default flags. enter to edit \xB7 takes effect on the next task enter.`));
13389
- setProp(_el$49, "wrapMode", "word");
13390
- setProp(_el$51, "flexDirection", "column");
13391
- setProp(_el$51, "gap", 0);
13392
- insert(_el$51, createComponent2(For, {
13637
+ var _el$55 = createElement("box"), _el$56 = createElement("text"), _el$58 = createElement("text"), _el$60 = createElement("box");
13638
+ insertNode(_el$55, _el$56);
13639
+ insertNode(_el$55, _el$58);
13640
+ insertNode(_el$55, _el$60);
13641
+ setProp(_el$55, "flexDirection", "column");
13642
+ setProp(_el$55, "gap", 1);
13643
+ insertNode(_el$56, createTextNode(`Launch command`));
13644
+ insertNode(_el$58, createTextNode(`The command each engine's task pane runs. Override it when your binary isn't on PATH as \`claude\` / \`codex\` (e.g. it's \`cl\`) or to pass default flags. enter to edit \xB7 takes effect on the next task enter.`));
13645
+ setProp(_el$58, "wrapMode", "word");
13646
+ setProp(_el$60, "flexDirection", "column");
13647
+ setProp(_el$60, "gap", 0);
13648
+ insert(_el$60, createComponent2(For, {
13393
13649
  get each() {
13394
13650
  return props.vendors;
13395
13651
  },
13396
13652
  children: (vendor, i) => {
13397
13653
  const isCursor = () => props.level() === "body" && props.bodyRow() === i();
13398
13654
  return (() => {
13399
- var _el$52 = createElement("box"), _el$53 = createElement("text"), _el$54 = createElement("text");
13400
- insertNode(_el$52, _el$53);
13401
- insertNode(_el$52, _el$54);
13402
- setProp(_el$52, "flexDirection", "row");
13403
- setProp(_el$52, "gap", 1);
13404
- setProp(_el$52, "paddingLeft", 1);
13405
- setProp(_el$52, "paddingRight", 1);
13406
- setProp(_el$52, "onMouseUp", () => {
13655
+ var _el$61 = createElement("box"), _el$62 = createElement("text"), _el$63 = createElement("text");
13656
+ insertNode(_el$61, _el$62);
13657
+ insertNode(_el$61, _el$63);
13658
+ setProp(_el$61, "flexDirection", "row");
13659
+ setProp(_el$61, "gap", 1);
13660
+ setProp(_el$61, "paddingLeft", 1);
13661
+ setProp(_el$61, "paddingRight", 1);
13662
+ setProp(_el$61, "onMouseUp", () => {
13407
13663
  props.setLevel("body");
13408
13664
  props.setBodyRow(i());
13409
13665
  props.editEngine(vendor);
13410
13666
  });
13411
- setProp(_el$53, "wrapMode", "none");
13412
- insert(_el$53, () => VENDOR_LABEL[vendor]);
13413
- setProp(_el$54, "wrapMode", "none");
13414
- insert(_el$54, () => props.commandText(vendor), null);
13415
- insert(_el$54, () => props.isDefault(vendor) ? " (default)" : "", null);
13667
+ setProp(_el$62, "wrapMode", "none");
13668
+ insert(_el$62, () => VENDOR_LABEL[vendor]);
13669
+ setProp(_el$63, "wrapMode", "none");
13670
+ insert(_el$63, () => props.commandText(vendor), null);
13671
+ insert(_el$63, () => props.isDefault(vendor) ? " (default)" : "", null);
13416
13672
  effect((_p$) => {
13417
- var _v$41 = isCursor() ? theme.primary : undefined, _v$42 = isCursor() ? theme.selectedListItemText : theme.text, _v$43 = TextAttributes5.BOLD, _v$44 = isCursor() ? theme.selectedListItemText : props.isDefault(vendor) ? theme.textMuted : theme.accent;
13418
- _v$41 !== _p$.e && (_p$.e = setProp(_el$52, "backgroundColor", _v$41, _p$.e));
13419
- _v$42 !== _p$.t && (_p$.t = setProp(_el$53, "fg", _v$42, _p$.t));
13420
- _v$43 !== _p$.a && (_p$.a = setProp(_el$53, "attributes", _v$43, _p$.a));
13421
- _v$44 !== _p$.o && (_p$.o = setProp(_el$54, "fg", _v$44, _p$.o));
13673
+ var _v$49 = isCursor() ? theme.primary : undefined, _v$50 = isCursor() ? theme.selectedListItemText : theme.text, _v$51 = TextAttributes5.BOLD, _v$52 = isCursor() ? theme.selectedListItemText : props.isDefault(vendor) ? theme.textMuted : theme.accent;
13674
+ _v$49 !== _p$.e && (_p$.e = setProp(_el$61, "backgroundColor", _v$49, _p$.e));
13675
+ _v$50 !== _p$.t && (_p$.t = setProp(_el$62, "fg", _v$50, _p$.t));
13676
+ _v$51 !== _p$.a && (_p$.a = setProp(_el$62, "attributes", _v$51, _p$.a));
13677
+ _v$52 !== _p$.o && (_p$.o = setProp(_el$63, "fg", _v$52, _p$.o));
13422
13678
  return _p$;
13423
13679
  }, {
13424
13680
  e: undefined,
@@ -13426,22 +13682,22 @@ function EngineSettingsSection(props) {
13426
13682
  a: undefined,
13427
13683
  o: undefined
13428
13684
  });
13429
- return _el$52;
13685
+ return _el$61;
13430
13686
  })();
13431
13687
  }
13432
13688
  }));
13433
13689
  effect((_p$) => {
13434
- var _v$38 = theme.text, _v$39 = TextAttributes5.BOLD, _v$40 = theme.textMuted;
13435
- _v$38 !== _p$.e && (_p$.e = setProp(_el$47, "fg", _v$38, _p$.e));
13436
- _v$39 !== _p$.t && (_p$.t = setProp(_el$47, "attributes", _v$39, _p$.t));
13437
- _v$40 !== _p$.a && (_p$.a = setProp(_el$49, "fg", _v$40, _p$.a));
13690
+ var _v$46 = theme.text, _v$47 = TextAttributes5.BOLD, _v$48 = theme.textMuted;
13691
+ _v$46 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$46, _p$.e));
13692
+ _v$47 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$47, _p$.t));
13693
+ _v$48 !== _p$.a && (_p$.a = setProp(_el$58, "fg", _v$48, _p$.a));
13438
13694
  return _p$;
13439
13695
  }, {
13440
13696
  e: undefined,
13441
13697
  t: undefined,
13442
13698
  a: undefined
13443
13699
  });
13444
- return _el$46;
13700
+ return _el$55;
13445
13701
  })();
13446
13702
  }
13447
13703
  function AccountsSettingsSection(props) {
@@ -13450,221 +13706,221 @@ function AccountsSettingsSection(props) {
13450
13706
  } = useTheme();
13451
13707
  const binaryLine = (s) => s.binary.found ? `Binary: ${s.binary.path}` : `Binary: ${s.binary.error}`;
13452
13708
  return (() => {
13453
- var _el$55 = createElement("box"), _el$56 = createElement("text"), _el$58 = createElement("text"), _el$60 = createElement("box"), _el$61 = createElement("text"), _el$65 = createElement("box"), _el$66 = createElement("text"), _el$70 = createElement("box"), _el$71 = createElement("text");
13454
- insertNode(_el$55, _el$56);
13455
- insertNode(_el$55, _el$58);
13456
- insertNode(_el$55, _el$60);
13457
- insertNode(_el$55, _el$65);
13458
- insertNode(_el$55, _el$70);
13459
- setProp(_el$55, "flexDirection", "column");
13460
- setProp(_el$55, "gap", 1);
13461
- insertNode(_el$56, createTextNode(`Accounts`));
13462
- insertNode(_el$58, createTextNode(`Read-only view of locally-detected engine accounts. Login flows land here later.`));
13463
- setProp(_el$58, "wrapMode", "word");
13464
- insertNode(_el$60, _el$61);
13465
- setProp(_el$60, "flexDirection", "column");
13466
- setProp(_el$60, "gap", 0);
13467
- insertNode(_el$61, createTextNode(`claude-code`));
13468
- insert(_el$60, createComponent2(Show, {
13709
+ var _el$64 = createElement("box"), _el$65 = createElement("text"), _el$67 = createElement("text"), _el$69 = createElement("box"), _el$70 = createElement("text"), _el$74 = createElement("box"), _el$75 = createElement("text"), _el$79 = createElement("box"), _el$80 = createElement("text");
13710
+ insertNode(_el$64, _el$65);
13711
+ insertNode(_el$64, _el$67);
13712
+ insertNode(_el$64, _el$69);
13713
+ insertNode(_el$64, _el$74);
13714
+ insertNode(_el$64, _el$79);
13715
+ setProp(_el$64, "flexDirection", "column");
13716
+ setProp(_el$64, "gap", 1);
13717
+ insertNode(_el$65, createTextNode(`Accounts`));
13718
+ insertNode(_el$67, createTextNode(`Read-only view of locally-detected engine accounts. Login flows land here later.`));
13719
+ setProp(_el$67, "wrapMode", "word");
13720
+ insertNode(_el$69, _el$70);
13721
+ setProp(_el$69, "flexDirection", "column");
13722
+ setProp(_el$69, "gap", 0);
13723
+ insertNode(_el$70, createTextNode(`claude-code`));
13724
+ insert(_el$69, createComponent2(Show, {
13469
13725
  get when() {
13470
13726
  return props.claudeStatus() === null;
13471
13727
  },
13472
13728
  get children() {
13473
- var _el$63 = createElement("text");
13474
- insertNode(_el$63, createTextNode(`Checking\u2026`));
13475
- effect((_$p) => setProp(_el$63, "fg", theme.textMuted, _$p));
13476
- return _el$63;
13729
+ var _el$72 = createElement("text");
13730
+ insertNode(_el$72, createTextNode(`Checking\u2026`));
13731
+ effect((_$p) => setProp(_el$72, "fg", theme.textMuted, _$p));
13732
+ return _el$72;
13477
13733
  }
13478
13734
  }), null);
13479
- insert(_el$60, createComponent2(Show, {
13735
+ insert(_el$69, createComponent2(Show, {
13480
13736
  get when() {
13481
13737
  return props.claudeStatus();
13482
13738
  },
13483
13739
  children: (s) => (() => {
13484
- var _el$75 = createElement("box"), _el$76 = createElement("text");
13485
- insertNode(_el$75, _el$76);
13486
- setProp(_el$75, "flexDirection", "column");
13487
- setProp(_el$75, "gap", 0);
13488
- setProp(_el$76, "wrapMode", "word");
13489
- insert(_el$76, () => binaryLine(s()));
13490
- insert(_el$75, () => {
13740
+ var _el$84 = createElement("box"), _el$85 = createElement("text");
13741
+ insertNode(_el$84, _el$85);
13742
+ setProp(_el$84, "flexDirection", "column");
13743
+ setProp(_el$84, "gap", 0);
13744
+ setProp(_el$85, "wrapMode", "word");
13745
+ insert(_el$85, () => binaryLine(s()));
13746
+ insert(_el$84, () => {
13491
13747
  const a = s().account;
13492
13748
  if (a.kind === "oauth") {
13493
13749
  const tail = [a.organization, a.billingType].filter((x) => !!x).join(" \xB7 ");
13494
13750
  return (() => {
13495
- var _el$77 = createElement("text");
13496
- setProp(_el$77, "wrapMode", "word");
13497
- insert(_el$77, () => `\u25CF Logged in: ${a.email}${tail ? ` (${tail})` : ""}`);
13498
- effect((_$p) => setProp(_el$77, "fg", theme.success, _$p));
13499
- return _el$77;
13751
+ var _el$86 = createElement("text");
13752
+ setProp(_el$86, "wrapMode", "word");
13753
+ insert(_el$86, () => `\u25CF Logged in: ${a.email}${tail ? ` (${tail})` : ""}`);
13754
+ effect((_$p) => setProp(_el$86, "fg", theme.success, _$p));
13755
+ return _el$86;
13500
13756
  })();
13501
13757
  }
13502
13758
  return (() => {
13503
- var _el$78 = createElement("text");
13504
- insertNode(_el$78, createTextNode(`\u25CB Not logged in`));
13505
- effect((_$p) => setProp(_el$78, "fg", theme.textMuted, _$p));
13506
- return _el$78;
13759
+ var _el$87 = createElement("text");
13760
+ insertNode(_el$87, createTextNode(`\u25CB Not logged in`));
13761
+ effect((_$p) => setProp(_el$87, "fg", theme.textMuted, _$p));
13762
+ return _el$87;
13507
13763
  })();
13508
13764
  }, null);
13509
- insert(_el$75, createComponent2(Show, {
13765
+ insert(_el$84, createComponent2(Show, {
13510
13766
  get when() {
13511
13767
  return s().accountError;
13512
13768
  },
13513
13769
  children: (err) => (() => {
13514
- var _el$80 = createElement("text");
13515
- setProp(_el$80, "wrapMode", "word");
13516
- insert(_el$80, () => `! ${err()}`);
13517
- effect((_$p) => setProp(_el$80, "fg", theme.warning, _$p));
13518
- return _el$80;
13770
+ var _el$89 = createElement("text");
13771
+ setProp(_el$89, "wrapMode", "word");
13772
+ insert(_el$89, () => `! ${err()}`);
13773
+ effect((_$p) => setProp(_el$89, "fg", theme.warning, _$p));
13774
+ return _el$89;
13519
13775
  })()
13520
13776
  }), null);
13521
- effect((_$p) => setProp(_el$76, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13522
- return _el$75;
13777
+ effect((_$p) => setProp(_el$85, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13778
+ return _el$84;
13523
13779
  })()
13524
13780
  }), null);
13525
- insertNode(_el$65, _el$66);
13526
- setProp(_el$65, "flexDirection", "column");
13527
- setProp(_el$65, "gap", 0);
13528
- insertNode(_el$66, createTextNode(`codex`));
13529
- insert(_el$65, createComponent2(Show, {
13781
+ insertNode(_el$74, _el$75);
13782
+ setProp(_el$74, "flexDirection", "column");
13783
+ setProp(_el$74, "gap", 0);
13784
+ insertNode(_el$75, createTextNode(`codex`));
13785
+ insert(_el$74, createComponent2(Show, {
13530
13786
  get when() {
13531
13787
  return props.codexStatus() === null;
13532
13788
  },
13533
13789
  get children() {
13534
- var _el$68 = createElement("text");
13535
- insertNode(_el$68, createTextNode(`Checking\u2026`));
13536
- effect((_$p) => setProp(_el$68, "fg", theme.textMuted, _$p));
13537
- return _el$68;
13790
+ var _el$77 = createElement("text");
13791
+ insertNode(_el$77, createTextNode(`Checking\u2026`));
13792
+ effect((_$p) => setProp(_el$77, "fg", theme.textMuted, _$p));
13793
+ return _el$77;
13538
13794
  }
13539
13795
  }), null);
13540
- insert(_el$65, createComponent2(Show, {
13796
+ insert(_el$74, createComponent2(Show, {
13541
13797
  get when() {
13542
13798
  return props.codexStatus();
13543
13799
  },
13544
13800
  children: (s) => (() => {
13545
- var _el$81 = createElement("box"), _el$82 = createElement("text");
13546
- insertNode(_el$81, _el$82);
13547
- setProp(_el$81, "flexDirection", "column");
13548
- setProp(_el$81, "gap", 0);
13549
- setProp(_el$82, "wrapMode", "word");
13550
- insert(_el$82, () => binaryLine(s()));
13551
- insert(_el$81, () => {
13801
+ var _el$90 = createElement("box"), _el$91 = createElement("text");
13802
+ insertNode(_el$90, _el$91);
13803
+ setProp(_el$90, "flexDirection", "column");
13804
+ setProp(_el$90, "gap", 0);
13805
+ setProp(_el$91, "wrapMode", "word");
13806
+ insert(_el$91, () => binaryLine(s()));
13807
+ insert(_el$90, () => {
13552
13808
  const a = s().account;
13553
13809
  if (a.kind === "chatgpt") {
13554
13810
  return (() => {
13555
- var _el$83 = createElement("text");
13556
- setProp(_el$83, "wrapMode", "word");
13557
- insert(_el$83, () => `\u25CF ChatGPT login: ${a.email}${a.plan ? ` (${a.plan})` : ""}`);
13558
- effect((_$p) => setProp(_el$83, "fg", theme.success, _$p));
13559
- return _el$83;
13811
+ var _el$92 = createElement("text");
13812
+ setProp(_el$92, "wrapMode", "word");
13813
+ insert(_el$92, () => `\u25CF ChatGPT login: ${a.email}${a.plan ? ` (${a.plan})` : ""}`);
13814
+ effect((_$p) => setProp(_el$92, "fg", theme.success, _$p));
13815
+ return _el$92;
13560
13816
  })();
13561
13817
  }
13562
13818
  if (a.kind === "apikey")
13563
13819
  return (() => {
13564
- var _el$84 = createElement("text");
13565
- insertNode(_el$84, createTextNode(`\u25CF API key configured`));
13566
- effect((_$p) => setProp(_el$84, "fg", theme.success, _$p));
13567
- return _el$84;
13820
+ var _el$93 = createElement("text");
13821
+ insertNode(_el$93, createTextNode(`\u25CF API key configured`));
13822
+ effect((_$p) => setProp(_el$93, "fg", theme.success, _$p));
13823
+ return _el$93;
13568
13824
  })();
13569
13825
  return (() => {
13570
- var _el$86 = createElement("text");
13571
- insertNode(_el$86, createTextNode(`\u25CB Not logged in`));
13572
- effect((_$p) => setProp(_el$86, "fg", theme.textMuted, _$p));
13573
- return _el$86;
13826
+ var _el$95 = createElement("text");
13827
+ insertNode(_el$95, createTextNode(`\u25CB Not logged in`));
13828
+ effect((_$p) => setProp(_el$95, "fg", theme.textMuted, _$p));
13829
+ return _el$95;
13574
13830
  })();
13575
13831
  }, null);
13576
- insert(_el$81, createComponent2(Show, {
13832
+ insert(_el$90, createComponent2(Show, {
13577
13833
  get when() {
13578
13834
  return s().accountError;
13579
13835
  },
13580
13836
  children: (err) => (() => {
13581
- var _el$88 = createElement("text");
13582
- setProp(_el$88, "wrapMode", "word");
13583
- insert(_el$88, () => `! ${err()}`);
13584
- effect((_$p) => setProp(_el$88, "fg", theme.warning, _$p));
13585
- return _el$88;
13837
+ var _el$97 = createElement("text");
13838
+ setProp(_el$97, "wrapMode", "word");
13839
+ insert(_el$97, () => `! ${err()}`);
13840
+ effect((_$p) => setProp(_el$97, "fg", theme.warning, _$p));
13841
+ return _el$97;
13586
13842
  })()
13587
13843
  }), null);
13588
- effect((_$p) => setProp(_el$82, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13589
- return _el$81;
13844
+ effect((_$p) => setProp(_el$91, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13845
+ return _el$90;
13590
13846
  })()
13591
13847
  }), null);
13592
- insertNode(_el$70, _el$71);
13593
- setProp(_el$70, "flexDirection", "column");
13594
- setProp(_el$70, "gap", 0);
13595
- insertNode(_el$71, createTextNode(`copilot`));
13596
- insert(_el$70, createComponent2(Show, {
13848
+ insertNode(_el$79, _el$80);
13849
+ setProp(_el$79, "flexDirection", "column");
13850
+ setProp(_el$79, "gap", 0);
13851
+ insertNode(_el$80, createTextNode(`copilot`));
13852
+ insert(_el$79, createComponent2(Show, {
13597
13853
  get when() {
13598
13854
  return props.copilotStatus() === null;
13599
13855
  },
13600
13856
  get children() {
13601
- var _el$73 = createElement("text");
13602
- insertNode(_el$73, createTextNode(`Checking\u2026`));
13603
- effect((_$p) => setProp(_el$73, "fg", theme.textMuted, _$p));
13604
- return _el$73;
13857
+ var _el$82 = createElement("text");
13858
+ insertNode(_el$82, createTextNode(`Checking\u2026`));
13859
+ effect((_$p) => setProp(_el$82, "fg", theme.textMuted, _$p));
13860
+ return _el$82;
13605
13861
  }
13606
13862
  }), null);
13607
- insert(_el$70, createComponent2(Show, {
13863
+ insert(_el$79, createComponent2(Show, {
13608
13864
  get when() {
13609
13865
  return props.copilotStatus();
13610
13866
  },
13611
13867
  children: (s) => (() => {
13612
- var _el$89 = createElement("box"), _el$90 = createElement("text");
13613
- insertNode(_el$89, _el$90);
13614
- setProp(_el$89, "flexDirection", "column");
13615
- setProp(_el$89, "gap", 0);
13616
- setProp(_el$90, "wrapMode", "word");
13617
- insert(_el$90, () => binaryLine(s()));
13618
- insert(_el$89, () => {
13868
+ var _el$98 = createElement("box"), _el$99 = createElement("text");
13869
+ insertNode(_el$98, _el$99);
13870
+ setProp(_el$98, "flexDirection", "column");
13871
+ setProp(_el$98, "gap", 0);
13872
+ setProp(_el$99, "wrapMode", "word");
13873
+ insert(_el$99, () => binaryLine(s()));
13874
+ insert(_el$98, () => {
13619
13875
  const a = s().account;
13620
13876
  if (a.kind === "token")
13621
13877
  return (() => {
13622
- var _el$91 = createElement("text");
13623
- insert(_el$91, () => `\u25CF Token configured (${a.source})`);
13624
- effect((_$p) => setProp(_el$91, "fg", theme.success, _$p));
13625
- return _el$91;
13878
+ var _el$100 = createElement("text");
13879
+ insert(_el$100, () => `\u25CF Token configured (${a.source})`);
13880
+ effect((_$p) => setProp(_el$100, "fg", theme.success, _$p));
13881
+ return _el$100;
13626
13882
  })();
13627
13883
  if (a.kind === "oauth")
13628
13884
  return (() => {
13629
- var _el$92 = createElement("text");
13630
- insertNode(_el$92, createTextNode(`\u25CF Copilot login detected`));
13631
- effect((_$p) => setProp(_el$92, "fg", theme.success, _$p));
13632
- return _el$92;
13885
+ var _el$101 = createElement("text");
13886
+ insertNode(_el$101, createTextNode(`\u25CF Copilot login detected`));
13887
+ effect((_$p) => setProp(_el$101, "fg", theme.success, _$p));
13888
+ return _el$101;
13633
13889
  })();
13634
13890
  return (() => {
13635
- var _el$94 = createElement("text");
13636
- insertNode(_el$94, createTextNode(`\u25CB Not logged in`));
13637
- effect((_$p) => setProp(_el$94, "fg", theme.textMuted, _$p));
13638
- return _el$94;
13891
+ var _el$103 = createElement("text");
13892
+ insertNode(_el$103, createTextNode(`\u25CB Not logged in`));
13893
+ effect((_$p) => setProp(_el$103, "fg", theme.textMuted, _$p));
13894
+ return _el$103;
13639
13895
  })();
13640
13896
  }, null);
13641
- insert(_el$89, createComponent2(Show, {
13897
+ insert(_el$98, createComponent2(Show, {
13642
13898
  get when() {
13643
13899
  return s().accountError;
13644
13900
  },
13645
13901
  children: (err) => (() => {
13646
- var _el$96 = createElement("text");
13647
- setProp(_el$96, "wrapMode", "word");
13648
- insert(_el$96, () => `! ${err()}`);
13649
- effect((_$p) => setProp(_el$96, "fg", theme.warning, _$p));
13650
- return _el$96;
13902
+ var _el$105 = createElement("text");
13903
+ setProp(_el$105, "wrapMode", "word");
13904
+ insert(_el$105, () => `! ${err()}`);
13905
+ effect((_$p) => setProp(_el$105, "fg", theme.warning, _$p));
13906
+ return _el$105;
13651
13907
  })()
13652
13908
  }), null);
13653
- effect((_$p) => setProp(_el$90, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13654
- return _el$89;
13909
+ effect((_$p) => setProp(_el$99, "fg", s().binary.found ? theme.textMuted : theme.warning, _$p));
13910
+ return _el$98;
13655
13911
  })()
13656
13912
  }), null);
13657
13913
  effect((_p$) => {
13658
- var _v$45 = theme.text, _v$46 = TextAttributes5.BOLD, _v$47 = theme.textMuted, _v$48 = theme.text, _v$49 = TextAttributes5.BOLD, _v$50 = theme.text, _v$51 = TextAttributes5.BOLD, _v$52 = theme.text, _v$53 = TextAttributes5.BOLD;
13659
- _v$45 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$45, _p$.e));
13660
- _v$46 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$46, _p$.t));
13661
- _v$47 !== _p$.a && (_p$.a = setProp(_el$58, "fg", _v$47, _p$.a));
13662
- _v$48 !== _p$.o && (_p$.o = setProp(_el$61, "fg", _v$48, _p$.o));
13663
- _v$49 !== _p$.i && (_p$.i = setProp(_el$61, "attributes", _v$49, _p$.i));
13664
- _v$50 !== _p$.n && (_p$.n = setProp(_el$66, "fg", _v$50, _p$.n));
13665
- _v$51 !== _p$.s && (_p$.s = setProp(_el$66, "attributes", _v$51, _p$.s));
13666
- _v$52 !== _p$.h && (_p$.h = setProp(_el$71, "fg", _v$52, _p$.h));
13667
- _v$53 !== _p$.r && (_p$.r = setProp(_el$71, "attributes", _v$53, _p$.r));
13914
+ var _v$53 = theme.text, _v$54 = TextAttributes5.BOLD, _v$55 = theme.textMuted, _v$56 = theme.text, _v$57 = TextAttributes5.BOLD, _v$58 = theme.text, _v$59 = TextAttributes5.BOLD, _v$60 = theme.text, _v$61 = TextAttributes5.BOLD;
13915
+ _v$53 !== _p$.e && (_p$.e = setProp(_el$65, "fg", _v$53, _p$.e));
13916
+ _v$54 !== _p$.t && (_p$.t = setProp(_el$65, "attributes", _v$54, _p$.t));
13917
+ _v$55 !== _p$.a && (_p$.a = setProp(_el$67, "fg", _v$55, _p$.a));
13918
+ _v$56 !== _p$.o && (_p$.o = setProp(_el$70, "fg", _v$56, _p$.o));
13919
+ _v$57 !== _p$.i && (_p$.i = setProp(_el$70, "attributes", _v$57, _p$.i));
13920
+ _v$58 !== _p$.n && (_p$.n = setProp(_el$75, "fg", _v$58, _p$.n));
13921
+ _v$59 !== _p$.s && (_p$.s = setProp(_el$75, "attributes", _v$59, _p$.s));
13922
+ _v$60 !== _p$.h && (_p$.h = setProp(_el$80, "fg", _v$60, _p$.h));
13923
+ _v$61 !== _p$.r && (_p$.r = setProp(_el$80, "attributes", _v$61, _p$.r));
13668
13924
  return _p$;
13669
13925
  }, {
13670
13926
  e: undefined,
@@ -13677,7 +13933,7 @@ function AccountsSettingsSection(props) {
13677
13933
  h: undefined,
13678
13934
  r: undefined
13679
13935
  });
13680
- return _el$55;
13936
+ return _el$64;
13681
13937
  })();
13682
13938
  }
13683
13939
  function DevSettingsSection(props) {
@@ -13687,58 +13943,58 @@ function DevSettingsSection(props) {
13687
13943
  const resetIsCursor = () => props.level() === "body" && props.bodyRow() === 0;
13688
13944
  const restartIsCursor = () => props.level() === "body" && props.bodyRow() === 1;
13689
13945
  return (() => {
13690
- var _el$97 = createElement("box"), _el$98 = createElement("text"), _el$100 = createElement("text"), _el$102 = createElement("box"), _el$103 = createElement("text");
13691
- insertNode(_el$97, _el$98);
13692
- insertNode(_el$97, _el$100);
13693
- insertNode(_el$97, _el$102);
13694
- setProp(_el$97, "flexDirection", "column");
13695
- setProp(_el$97, "gap", 1);
13696
- insertNode(_el$98, createTextNode(`Reset UI state`));
13697
- insertNode(_el$100, createTextNode(`Clears ~/.config/kobe/state.json and ~/.kobe/tasks.json, then quits kobe \u2014 relaunch to start fresh. Working session / Archive lists, pane sizes, theme, model picks all reset. Worktrees on disk and Claude Code session history are not touched.`));
13698
- setProp(_el$100, "wrapMode", "word");
13699
- insertNode(_el$102, _el$103);
13700
- setProp(_el$102, "flexDirection", "row");
13701
- setProp(_el$102, "paddingLeft", 1);
13702
- setProp(_el$102, "paddingRight", 1);
13703
- setProp(_el$102, "onMouseUp", () => {
13946
+ var _el$106 = createElement("box"), _el$107 = createElement("text"), _el$109 = createElement("text"), _el$111 = createElement("box"), _el$112 = createElement("text");
13947
+ insertNode(_el$106, _el$107);
13948
+ insertNode(_el$106, _el$109);
13949
+ insertNode(_el$106, _el$111);
13950
+ setProp(_el$106, "flexDirection", "column");
13951
+ setProp(_el$106, "gap", 1);
13952
+ insertNode(_el$107, createTextNode(`Reset UI state`));
13953
+ insertNode(_el$109, createTextNode(`Clears ~/.config/kobe/state.json and ~/.kobe/tasks.json, then quits kobe \u2014 relaunch to start fresh. Working session / Archive lists, pane sizes, theme, model picks all reset. Worktrees on disk and Claude Code session history are not touched.`));
13954
+ setProp(_el$109, "wrapMode", "word");
13955
+ insertNode(_el$111, _el$112);
13956
+ setProp(_el$111, "flexDirection", "row");
13957
+ setProp(_el$111, "paddingLeft", 1);
13958
+ setProp(_el$111, "paddingRight", 1);
13959
+ setProp(_el$111, "onMouseUp", () => {
13704
13960
  props.setLevel("body");
13705
13961
  props.setBodyRow(0);
13706
13962
  props.confirmReset();
13707
13963
  });
13708
- insertNode(_el$103, createTextNode(`[enter] Reset`));
13709
- insert(_el$97, createComponent2(Show, {
13964
+ insertNode(_el$112, createTextNode(`[enter] Reset`));
13965
+ insert(_el$106, createComponent2(Show, {
13710
13966
  get when() {
13711
13967
  return props.hasDaemon;
13712
13968
  },
13713
13969
  get children() {
13714
- var _el$105 = createElement("box"), _el$106 = createElement("text"), _el$108 = createElement("text"), _el$110 = createElement("box"), _el$111 = createElement("text");
13715
- insertNode(_el$105, _el$106);
13716
- insertNode(_el$105, _el$108);
13717
- insertNode(_el$105, _el$110);
13718
- setProp(_el$105, "flexDirection", "column");
13719
- setProp(_el$105, "gap", 0);
13720
- setProp(_el$105, "paddingTop", 1);
13721
- insertNode(_el$106, createTextNode(`Restart backend`));
13722
- insertNode(_el$108, createTextNode(`Stops the kobe daemon and quits this kobe window so the next launch spawns a fresh daemon \u2014 picks up daemon / orchestrator / engine edits without a process kill. Other attached kobe windows will lose their connection too.`));
13723
- setProp(_el$108, "wrapMode", "word");
13724
- insertNode(_el$110, _el$111);
13725
- setProp(_el$110, "flexDirection", "row");
13726
- setProp(_el$110, "paddingLeft", 1);
13727
- setProp(_el$110, "paddingRight", 1);
13728
- setProp(_el$110, "onMouseUp", () => {
13970
+ var _el$114 = createElement("box"), _el$115 = createElement("text"), _el$117 = createElement("text"), _el$119 = createElement("box"), _el$120 = createElement("text");
13971
+ insertNode(_el$114, _el$115);
13972
+ insertNode(_el$114, _el$117);
13973
+ insertNode(_el$114, _el$119);
13974
+ setProp(_el$114, "flexDirection", "column");
13975
+ setProp(_el$114, "gap", 0);
13976
+ setProp(_el$114, "paddingTop", 1);
13977
+ insertNode(_el$115, createTextNode(`Restart backend`));
13978
+ insertNode(_el$117, createTextNode(`Stops the kobe daemon and quits this kobe window so the next launch spawns a fresh daemon \u2014 picks up daemon / orchestrator / engine edits without a process kill. Other attached kobe windows will lose their connection too.`));
13979
+ setProp(_el$117, "wrapMode", "word");
13980
+ insertNode(_el$119, _el$120);
13981
+ setProp(_el$119, "flexDirection", "row");
13982
+ setProp(_el$119, "paddingLeft", 1);
13983
+ setProp(_el$119, "paddingRight", 1);
13984
+ setProp(_el$119, "onMouseUp", () => {
13729
13985
  props.setLevel("body");
13730
13986
  props.setBodyRow(1);
13731
13987
  props.confirmRestartDaemon();
13732
13988
  });
13733
- insertNode(_el$111, createTextNode(`[enter] Restart`));
13989
+ insertNode(_el$120, createTextNode(`[enter] Restart`));
13734
13990
  effect((_p$) => {
13735
- var _v$54 = theme.text, _v$55 = TextAttributes5.BOLD, _v$56 = theme.textMuted, _v$57 = restartIsCursor() ? theme.primary : theme.backgroundElement, _v$58 = restartIsCursor() ? theme.selectedListItemText : theme.accent, _v$59 = TextAttributes5.BOLD;
13736
- _v$54 !== _p$.e && (_p$.e = setProp(_el$106, "fg", _v$54, _p$.e));
13737
- _v$55 !== _p$.t && (_p$.t = setProp(_el$106, "attributes", _v$55, _p$.t));
13738
- _v$56 !== _p$.a && (_p$.a = setProp(_el$108, "fg", _v$56, _p$.a));
13739
- _v$57 !== _p$.o && (_p$.o = setProp(_el$110, "backgroundColor", _v$57, _p$.o));
13740
- _v$58 !== _p$.i && (_p$.i = setProp(_el$111, "fg", _v$58, _p$.i));
13741
- _v$59 !== _p$.n && (_p$.n = setProp(_el$111, "attributes", _v$59, _p$.n));
13991
+ var _v$62 = theme.text, _v$63 = TextAttributes5.BOLD, _v$64 = theme.textMuted, _v$65 = restartIsCursor() ? theme.primary : theme.backgroundElement, _v$66 = restartIsCursor() ? theme.selectedListItemText : theme.accent, _v$67 = TextAttributes5.BOLD;
13992
+ _v$62 !== _p$.e && (_p$.e = setProp(_el$115, "fg", _v$62, _p$.e));
13993
+ _v$63 !== _p$.t && (_p$.t = setProp(_el$115, "attributes", _v$63, _p$.t));
13994
+ _v$64 !== _p$.a && (_p$.a = setProp(_el$117, "fg", _v$64, _p$.a));
13995
+ _v$65 !== _p$.o && (_p$.o = setProp(_el$119, "backgroundColor", _v$65, _p$.o));
13996
+ _v$66 !== _p$.i && (_p$.i = setProp(_el$120, "fg", _v$66, _p$.i));
13997
+ _v$67 !== _p$.n && (_p$.n = setProp(_el$120, "attributes", _v$67, _p$.n));
13742
13998
  return _p$;
13743
13999
  }, {
13744
14000
  e: undefined,
@@ -13748,17 +14004,17 @@ function DevSettingsSection(props) {
13748
14004
  i: undefined,
13749
14005
  n: undefined
13750
14006
  });
13751
- return _el$105;
14007
+ return _el$114;
13752
14008
  }
13753
14009
  }), null);
13754
14010
  effect((_p$) => {
13755
- var _v$60 = theme.text, _v$61 = TextAttributes5.BOLD, _v$62 = theme.textMuted, _v$63 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$64 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$65 = TextAttributes5.BOLD;
13756
- _v$60 !== _p$.e && (_p$.e = setProp(_el$98, "fg", _v$60, _p$.e));
13757
- _v$61 !== _p$.t && (_p$.t = setProp(_el$98, "attributes", _v$61, _p$.t));
13758
- _v$62 !== _p$.a && (_p$.a = setProp(_el$100, "fg", _v$62, _p$.a));
13759
- _v$63 !== _p$.o && (_p$.o = setProp(_el$102, "backgroundColor", _v$63, _p$.o));
13760
- _v$64 !== _p$.i && (_p$.i = setProp(_el$103, "fg", _v$64, _p$.i));
13761
- _v$65 !== _p$.n && (_p$.n = setProp(_el$103, "attributes", _v$65, _p$.n));
14011
+ var _v$68 = theme.text, _v$69 = TextAttributes5.BOLD, _v$70 = theme.textMuted, _v$71 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$72 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$73 = TextAttributes5.BOLD;
14012
+ _v$68 !== _p$.e && (_p$.e = setProp(_el$107, "fg", _v$68, _p$.e));
14013
+ _v$69 !== _p$.t && (_p$.t = setProp(_el$107, "attributes", _v$69, _p$.t));
14014
+ _v$70 !== _p$.a && (_p$.a = setProp(_el$109, "fg", _v$70, _p$.a));
14015
+ _v$71 !== _p$.o && (_p$.o = setProp(_el$111, "backgroundColor", _v$71, _p$.o));
14016
+ _v$72 !== _p$.i && (_p$.i = setProp(_el$112, "fg", _v$72, _p$.i));
14017
+ _v$73 !== _p$.n && (_p$.n = setProp(_el$112, "attributes", _v$73, _p$.n));
13762
14018
  return _p$;
13763
14019
  }, {
13764
14020
  e: undefined,
@@ -13768,7 +14024,7 @@ function DevSettingsSection(props) {
13768
14024
  i: undefined,
13769
14025
  n: undefined
13770
14026
  });
13771
- return _el$97;
14027
+ return _el$106;
13772
14028
  })();
13773
14029
  }
13774
14030
  var init_sections = __esm(() => {
@@ -13837,9 +14093,40 @@ function SettingsDialog(props) {
13837
14093
  function isSurfaceTaskpanelRow() {
13838
14094
  return section() === "general" && bodyRow() === surfaceTaskpanelRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
13839
14095
  }
14096
+ function isEditorKindRow() {
14097
+ return section() === "general" && bodyRow() === editorKindRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
14098
+ }
14099
+ function isEditorCustomRow() {
14100
+ return section() === "general" && bodyRow() === editorCustomRowIndex(themeNames().length, FOCUS_ACCENT_SLOTS.length);
14101
+ }
13840
14102
  function settingsSurface() {
13841
14103
  return normalizeSettingsSurface(props.kv.get(SETTINGS_SURFACE_KEY, DEFAULT_SETTINGS_SURFACE));
13842
14104
  }
14105
+ function selectTheme(name) {
14106
+ if (themeCtx.selected === name)
14107
+ return;
14108
+ if (!themeCtx.set(name))
14109
+ return;
14110
+ props.kv.set("activeTheme", name);
14111
+ props.onVisualPrefsChange?.();
14112
+ }
14113
+ function setTransparentBackground(next) {
14114
+ if (themeCtx.transparentBackground === next)
14115
+ return;
14116
+ themeCtx.setTransparentBackground(next);
14117
+ props.kv.set("transparentBackground", next);
14118
+ props.onVisualPrefsChange?.();
14119
+ }
14120
+ function toggleTransparent() {
14121
+ setTransparentBackground(!themeCtx.transparentBackground);
14122
+ }
14123
+ function selectFocusAccent(slot) {
14124
+ if (themeCtx.focusAccent === slot)
14125
+ return;
14126
+ themeCtx.setFocusAccent(slot);
14127
+ props.kv.set("focusAccent", slot);
14128
+ props.onVisualPrefsChange?.();
14129
+ }
13843
14130
  function selectSurface(surface) {
13844
14131
  props.kv.set(SETTINGS_SURFACE_KEY, surface);
13845
14132
  }
@@ -13873,6 +14160,30 @@ function SettingsDialog(props) {
13873
14160
  return;
13874
14161
  props.kv.set(engineCommandKey(vendor), next.trim());
13875
14162
  }
14163
+ function editorKind() {
14164
+ return normalizeEditorKind(props.kv.get(EDITOR_KIND_KEY, DEFAULT_EDITOR_KIND));
14165
+ }
14166
+ function cycleEditorKind() {
14167
+ const i = EDITOR_KINDS.indexOf(editorKind());
14168
+ const next = EDITOR_KINDS[(i + 1) % EDITOR_KINDS.length];
14169
+ if (next)
14170
+ props.kv.set(EDITOR_KIND_KEY, next);
14171
+ }
14172
+ function editorCustomCommand() {
14173
+ const v = props.kv.get(EDITOR_CUSTOM_KEY, "");
14174
+ return typeof v === "string" ? v : "";
14175
+ }
14176
+ async function editEditorCustom() {
14177
+ const next = await RenameTaskDialog.show(dialog, editorCustomCommand(), {
14178
+ dialogTitle: "Custom editor command (use {file} for the path)"
14179
+ });
14180
+ if (next === undefined)
14181
+ return;
14182
+ const cmd = next.trim();
14183
+ props.kv.set(EDITOR_CUSTOM_KEY, cmd);
14184
+ if (cmd)
14185
+ props.kv.set(EDITOR_KIND_KEY, "custom");
14186
+ }
13876
14187
  function enterBody() {
13877
14188
  if (level() !== "sidebar" || bodyRowCount2() === 0)
13878
14189
  return;
@@ -13909,14 +14220,14 @@ function SettingsDialog(props) {
13909
14220
  function activateBodyRow() {
13910
14221
  if (section() === "general") {
13911
14222
  if (isTransparentRow()) {
13912
- themeCtx.setTransparentBackground(!themeCtx.transparentBackground);
14223
+ toggleTransparent();
13913
14224
  return;
13914
14225
  }
13915
14226
  const focusIdx = currentFocusAccentRow();
13916
14227
  if (focusIdx !== null) {
13917
14228
  const slot = FOCUS_ACCENT_SLOTS[focusIdx];
13918
14229
  if (slot)
13919
- themeCtx.setFocusAccent(slot);
14230
+ selectFocusAccent(slot);
13920
14231
  return;
13921
14232
  }
13922
14233
  if (isToastRow()) {
@@ -13935,9 +14246,17 @@ function SettingsDialog(props) {
13935
14246
  selectSurface("taskpanel");
13936
14247
  return;
13937
14248
  }
14249
+ if (isEditorKindRow()) {
14250
+ cycleEditorKind();
14251
+ return;
14252
+ }
14253
+ if (isEditorCustomRow()) {
14254
+ editEditorCustom();
14255
+ return;
14256
+ }
13938
14257
  const name = themeNames()[bodyRow()];
13939
14258
  if (name)
13940
- themeCtx.set(name);
14259
+ selectTheme(name);
13941
14260
  return;
13942
14261
  }
13943
14262
  if (section() === "engines") {
@@ -13954,6 +14273,7 @@ function SettingsDialog(props) {
13954
14273
  }
13955
14274
  }
13956
14275
  useBindings(() => ({
14276
+ enabled: !props.standalone || dialog.stack.length === 0,
13957
14277
  bindings: [{
13958
14278
  key: "down",
13959
14279
  cmd: () => moveCursor(1)
@@ -13992,7 +14312,7 @@ function SettingsDialog(props) {
13992
14312
  }
13993
14313
  }, {
13994
14314
  key: "t",
13995
- cmd: () => themeCtx.setTransparentBackground(!themeCtx.transparentBackground)
14315
+ cmd: toggleTransparent
13996
14316
  }]
13997
14317
  }));
13998
14318
  return (() => {
@@ -14035,12 +14355,19 @@ function SettingsDialog(props) {
14035
14355
  setBodyRow,
14036
14356
  themeNames,
14037
14357
  setThemeCursor,
14358
+ selectTheme,
14359
+ toggleTransparent,
14360
+ selectFocusAccent,
14038
14361
  toastEnabled,
14039
14362
  soundEnabled,
14040
14363
  toggleToast,
14041
14364
  toggleSound,
14042
14365
  settingsSurface,
14043
- selectSurface
14366
+ selectSurface,
14367
+ editorKind,
14368
+ cycleEditorKind,
14369
+ editorCustomCommand,
14370
+ editEditorCustom: () => void editEditorCustom()
14044
14371
  });
14045
14372
  }
14046
14373
  }), null);
@@ -14122,6 +14449,7 @@ var init_settings_dialog = __esm(() => {
14122
14449
  init_interactive_command();
14123
14450
  init_vendor();
14124
14451
  init_theme2();
14452
+ init_editor_prefs();
14125
14453
  init_keymap();
14126
14454
  init_dialog();
14127
14455
  init_rename_task_dialog();
@@ -14129,12 +14457,20 @@ var init_settings_dialog = __esm(() => {
14129
14457
  init_model();
14130
14458
  init_sections();
14131
14459
  SettingsDialog.show = (dialog, kv, orchestrator) => {
14460
+ let visualPrefsChanged = false;
14132
14461
  return new Promise((resolve6) => {
14133
14462
  dialog.replace(() => createComponent2(SettingsDialog, {
14134
14463
  kv,
14135
14464
  orchestrator,
14136
- onClose: () => resolve6()
14137
- }), () => resolve6());
14465
+ onVisualPrefsChange: () => {
14466
+ visualPrefsChanged = true;
14467
+ },
14468
+ onClose: () => resolve6({
14469
+ visualPrefsChanged
14470
+ })
14471
+ }), () => resolve6({
14472
+ visualPrefsChanged
14473
+ }));
14138
14474
  });
14139
14475
  };
14140
14476
  });
@@ -14227,22 +14563,27 @@ var init_kv = __esm(() => {
14227
14563
  init: () => {
14228
14564
  const [store2, setStore2] = createStore(loadInitial());
14229
14565
  let writeTimer = null;
14566
+ function writeNow(label) {
14567
+ const statePath2 = kvStatePath();
14568
+ try {
14569
+ mkdirSync4(dirname5(statePath2), {
14570
+ recursive: true
14571
+ });
14572
+ const tmp = `${statePath2}.tmp`;
14573
+ writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14574
+ renameSync2(tmp, statePath2);
14575
+ return true;
14576
+ } catch (err) {
14577
+ console.error(`[kobe] kv ${label} failed:`, err);
14578
+ return false;
14579
+ }
14580
+ }
14230
14581
  function scheduleWrite() {
14231
14582
  if (writeTimer)
14232
14583
  clearTimeout(writeTimer);
14233
14584
  writeTimer = setTimeout(() => {
14234
14585
  writeTimer = null;
14235
- const statePath2 = kvStatePath();
14236
- try {
14237
- mkdirSync4(dirname5(statePath2), {
14238
- recursive: true
14239
- });
14240
- const tmp = `${statePath2}.tmp`;
14241
- writeFileSync3(tmp, JSON.stringify(store2, null, 2), "utf8");
14242
- renameSync2(tmp, statePath2);
14243
- } catch (err) {
14244
- console.error("[kobe] kv write failed:", err);
14245
- }
14586
+ writeNow("write");
14246
14587
  }, WRITE_DEBOUNCE_MS);
14247
14588
  }
14248
14589
  const result = {
@@ -14264,6 +14605,13 @@ var init_kv = __esm(() => {
14264
14605
  setStore2(key, value);
14265
14606
  scheduleWrite();
14266
14607
  },
14608
+ flush() {
14609
+ if (writeTimer) {
14610
+ clearTimeout(writeTimer);
14611
+ writeTimer = null;
14612
+ }
14613
+ return writeNow("flush");
14614
+ },
14267
14615
  clear() {
14268
14616
  for (const k of Object.keys(store2))
14269
14617
  setStore2(k, undefined);
@@ -14415,36 +14763,6 @@ var init_worktree_opener = __esm(() => {
14415
14763
  ];
14416
14764
  });
14417
14765
 
14418
- // src/tui/panes/sidebar/git-head.ts
14419
- import { spawnSync as spawnSync8 } from "child_process";
14420
- function readCurrentBranch(repo) {
14421
- if (!repo)
14422
- return "";
14423
- try {
14424
- const out = spawnSync8("git", ["symbolic-ref", "--short", "HEAD"], {
14425
- cwd: repo,
14426
- encoding: "utf8",
14427
- stdio: ["ignore", "pipe", "pipe"]
14428
- });
14429
- if (out.status === 0 && out.stdout) {
14430
- const name = out.stdout.trim();
14431
- if (name && name !== "HEAD")
14432
- return name;
14433
- }
14434
- const head = spawnSync8("git", ["rev-parse", "--verify", "HEAD"], {
14435
- cwd: repo,
14436
- encoding: "utf8",
14437
- stdio: ["ignore", "pipe", "pipe"]
14438
- });
14439
- if (head.status === 0)
14440
- return "(detached)";
14441
- return "";
14442
- } catch {
14443
- return "";
14444
- }
14445
- }
14446
- var init_git_head = () => {};
14447
-
14448
14766
  // src/tui/panes/sidebar/fuzzy.ts
14449
14767
  function fuzzyMatch(query, haystack) {
14450
14768
  if (!query)
@@ -15072,8 +15390,16 @@ var init_keybindings = __esm(() => {
15072
15390
  scope: "files",
15073
15391
  keys: ["return"],
15074
15392
  category: "Files",
15075
- description: "Open file",
15076
- hint: { keys: "enter", label: "open" }
15393
+ description: "Open file read-only preview",
15394
+ hint: { keys: "enter", label: "preview" }
15395
+ },
15396
+ {
15397
+ id: "files.edit",
15398
+ scope: "files",
15399
+ keys: ["e"],
15400
+ category: "Files",
15401
+ description: "Open file in the configured editor (vim / nano / custom)",
15402
+ hint: { keys: "e", label: "edit" }
15077
15403
  },
15078
15404
  {
15079
15405
  id: "files.tab",
@@ -15355,6 +15681,13 @@ function truncatePathTail(path11, max) {
15355
15681
  return path11;
15356
15682
  return `\u2026${path11.slice(path11.length - Math.max(0, max - 1))}`;
15357
15683
  }
15684
+ function abbrevHome(path11) {
15685
+ const home = process.env.HOME;
15686
+ if (home && (path11 === home || path11.startsWith(`${home}/`))) {
15687
+ return `~${path11.slice(home.length)}`;
15688
+ }
15689
+ return path11;
15690
+ }
15358
15691
  function Sidebar(props) {
15359
15692
  const {
15360
15693
  theme
@@ -15408,14 +15741,15 @@ function Sidebar(props) {
15408
15741
  onCleanup(() => clearInterval(spinnerInterval));
15409
15742
  const rows = createMemo(() => buildRows(props.tasks(), view(), searchMode() ? searchQuery() : ""));
15410
15743
  const flatIds = createMemo(() => flattenIds(rows()));
15744
+ const firstTaskFlatIndex = createMemo(() => {
15745
+ const r = rows();
15746
+ const idx = r.findIndex((row) => row.task.kind !== "main");
15747
+ return idx < 0 ? -1 : r[idx].flatIndex;
15748
+ });
15411
15749
  const totalRows = createMemo(() => flattenIds(buildRows(props.tasks(), view(), "")).length);
15412
15750
  const effectiveWidth = () => props.width ? props.width() : SIDEBAR_WIDTH;
15413
- const showChangesCol = createMemo(() => effectiveWidth() >= SHOW_CHANGES_MIN_WIDTH);
15414
- const showBranchCol = createMemo(() => effectiveWidth() >= SHOW_BRANCH_MIN_WIDTH);
15415
- const titleBudget = createMemo(() => {
15416
- const reserved = 9 + (showChangesCol() ? CHANGES_COLUMN_WIDTH + 1 : 0) + (showBranchCol() ? BRANCH_LABEL_MAX + 2 : 0);
15417
- return Math.max(4, effectiveWidth() - reserved);
15418
- });
15751
+ const titleBudget = createMemo(() => Math.max(6, effectiveWidth() - 9));
15752
+ const subtitleBudget = createMemo(() => Math.max(6, effectiveWidth() - 16));
15419
15753
  const dims = useTerminalDimensions();
15420
15754
  const [hover, setHover] = createSignal(null);
15421
15755
  const [cursorIndex, setCursorIndex] = createSignal(-1);
@@ -15473,80 +15807,127 @@ function Sidebar(props) {
15473
15807
  onSearchEnter: () => enterSearch(),
15474
15808
  onSearchExit: (select) => exitSearch(select)
15475
15809
  });
15476
- return (() => {
15477
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$17 = createElement("box"), _el$18 = createElement("scrollbox"), _el$19 = createElement("box"), _el$22 = createElement("box"), _el$23 = createElement("text");
15810
+ const SectionHeader = (p) => (() => {
15811
+ var _el$ = createElement("box"), _el$2 = createElement("text"), _el$3 = createElement("text");
15478
15812
  insertNode(_el$, _el$2);
15479
- insertNode(_el$, _el$17);
15480
- insertNode(_el$, _el$18);
15481
- insertNode(_el$, _el$22);
15482
- setProp(_el$, "flexShrink", 0);
15483
- setProp(_el$, "flexDirection", "column");
15484
- setProp(_el$, "paddingTop", 1);
15485
- setProp(_el$, "paddingBottom", 1);
15486
- setProp(_el$, "paddingLeft", 2);
15487
- setProp(_el$, "paddingRight", 2);
15488
- insertNode(_el$2, _el$3);
15489
- insertNode(_el$2, _el$5);
15490
- setProp(_el$2, "flexDirection", "row");
15491
- setProp(_el$2, "gap", 1);
15492
- setProp(_el$2, "paddingBottom", 1);
15493
- setProp(_el$2, "paddingLeft", 1);
15494
- insertNode(_el$3, createTextNode(`h`));
15813
+ insertNode(_el$, _el$3);
15814
+ setProp(_el$, "flexDirection", "row");
15815
+ setProp(_el$, "gap", 1);
15816
+ setProp(_el$, "paddingLeft", 1);
15817
+ setProp(_el$, "paddingRight", 1);
15818
+ setProp(_el$2, "wrapMode", "none");
15819
+ insert(_el$2, () => p.label);
15495
15820
  setProp(_el$3, "wrapMode", "none");
15496
- insertNode(_el$5, createTextNode(`TASKS`));
15497
- setProp(_el$5, "wrapMode", "none");
15498
- insert(_el$, createComponent2(Show, {
15821
+ insert(_el$3, () => "\u2500".repeat(Math.max(2, effectiveWidth() - 9 - p.label.length)));
15822
+ effect((_p$) => {
15823
+ var _v$ = p.topPad ? 1 : 0, _v$2 = theme.textMuted, _v$3 = TextAttributes8.BOLD, _v$4 = theme.border;
15824
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "paddingTop", _v$, _p$.e));
15825
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "fg", _v$2, _p$.t));
15826
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$2, "attributes", _v$3, _p$.a));
15827
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$3, "fg", _v$4, _p$.o));
15828
+ return _p$;
15829
+ }, {
15830
+ e: undefined,
15831
+ t: undefined,
15832
+ a: undefined,
15833
+ o: undefined
15834
+ });
15835
+ return _el$;
15836
+ })();
15837
+ return (() => {
15838
+ var _el$4 = createElement("box"), _el$5 = createElement("box"), _el$6 = createElement("text"), _el$18 = createElement("box"), _el$19 = createElement("scrollbox"), _el$20 = createElement("box"), _el$23 = createElement("box"), _el$24 = createElement("text");
15839
+ insertNode(_el$4, _el$5);
15840
+ insertNode(_el$4, _el$18);
15841
+ insertNode(_el$4, _el$19);
15842
+ insertNode(_el$4, _el$23);
15843
+ setProp(_el$4, "flexShrink", 0);
15844
+ setProp(_el$4, "flexDirection", "column");
15845
+ setProp(_el$4, "paddingTop", 1);
15846
+ setProp(_el$4, "paddingBottom", 1);
15847
+ setProp(_el$4, "paddingLeft", 0);
15848
+ setProp(_el$4, "paddingRight", 0);
15849
+ insertNode(_el$5, _el$6);
15850
+ setProp(_el$5, "flexDirection", "row");
15851
+ setProp(_el$5, "justifyContent", "space-between");
15852
+ setProp(_el$5, "gap", 1);
15853
+ setProp(_el$5, "paddingBottom", 1);
15854
+ setProp(_el$5, "paddingLeft", 1);
15855
+ setProp(_el$5, "paddingRight", 1);
15856
+ insertNode(_el$6, createTextNode(`KOBE`));
15857
+ setProp(_el$6, "wrapMode", "none");
15858
+ insert(_el$5, createComponent2(Show, {
15859
+ get when() {
15860
+ return props.headerStatus?.();
15861
+ },
15862
+ children: (status) => (() => {
15863
+ var _el$26 = createElement("text");
15864
+ setProp(_el$26, "wrapMode", "none");
15865
+ setProp(_el$26, "onMouseUp", () => props.onHeaderStatusClick?.());
15866
+ insert(_el$26, () => status().label);
15867
+ effect((_p$) => {
15868
+ var _v$11 = status().emphasize ? theme.warning : theme.textMuted, _v$12 = status().emphasize ? TextAttributes8.BOLD : TextAttributes8.DIM;
15869
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$26, "fg", _v$11, _p$.e));
15870
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$26, "attributes", _v$12, _p$.t));
15871
+ return _p$;
15872
+ }, {
15873
+ e: undefined,
15874
+ t: undefined
15875
+ });
15876
+ return _el$26;
15877
+ })()
15878
+ }), null);
15879
+ insert(_el$4, createComponent2(Show, {
15499
15880
  get when() {
15500
15881
  return searchMode();
15501
15882
  },
15502
15883
  get children() {
15503
- var _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$1 = createElement("text");
15504
- insertNode(_el$7, _el$8);
15505
- insertNode(_el$7, _el$0);
15506
- insertNode(_el$7, _el$1);
15507
- setProp(_el$7, "flexDirection", "row");
15508
- setProp(_el$7, "gap", 0);
15509
- setProp(_el$7, "paddingBottom", 1);
15510
- setProp(_el$7, "paddingLeft", 1);
15511
- insertNode(_el$8, createTextNode(`/`));
15512
- setProp(_el$8, "wrapMode", "none");
15513
- setProp(_el$0, "wrapMode", "none");
15514
- insert(_el$0, searchQuery);
15515
- insertNode(_el$1, createTextNode(`\u2588`));
15884
+ var _el$8 = createElement("box"), _el$9 = createElement("text"), _el$1 = createElement("text"), _el$10 = createElement("text");
15885
+ insertNode(_el$8, _el$9);
15886
+ insertNode(_el$8, _el$1);
15887
+ insertNode(_el$8, _el$10);
15888
+ setProp(_el$8, "flexDirection", "row");
15889
+ setProp(_el$8, "gap", 0);
15890
+ setProp(_el$8, "paddingBottom", 1);
15891
+ setProp(_el$8, "paddingLeft", 1);
15892
+ insertNode(_el$9, createTextNode(`/`));
15893
+ setProp(_el$9, "wrapMode", "none");
15516
15894
  setProp(_el$1, "wrapMode", "none");
15517
- insert(_el$7, createComponent2(Show, {
15895
+ insert(_el$1, searchQuery);
15896
+ insertNode(_el$10, createTextNode(`\u2588`));
15897
+ setProp(_el$10, "wrapMode", "none");
15898
+ insert(_el$8, createComponent2(Show, {
15518
15899
  get when() {
15519
15900
  return searchQuery().length === 0;
15520
15901
  },
15521
15902
  get children() {
15522
- var _el$11 = createElement("text"), _el$12 = createTextNode(` fuzzy filter`);
15523
- insertNode(_el$11, _el$12);
15524
- setProp(_el$11, "wrapMode", "none");
15525
- effect((_$p) => setProp(_el$11, "fg", theme.textMuted, _$p));
15526
- return _el$11;
15903
+ var _el$12 = createElement("text"), _el$13 = createTextNode(` fuzzy filter`);
15904
+ insertNode(_el$12, _el$13);
15905
+ setProp(_el$12, "wrapMode", "none");
15906
+ effect((_$p) => setProp(_el$12, "fg", theme.textMuted, _$p));
15907
+ return _el$12;
15527
15908
  }
15528
15909
  }), null);
15529
- insert(_el$7, createComponent2(Show, {
15910
+ insert(_el$8, createComponent2(Show, {
15530
15911
  get when() {
15531
15912
  return searchQuery().length > 0;
15532
15913
  },
15533
15914
  get children() {
15534
- var _el$14 = createElement("text"), _el$15 = createTextNode(` `), _el$16 = createTextNode(`/`);
15535
- insertNode(_el$14, _el$15);
15536
- insertNode(_el$14, _el$16);
15537
- setProp(_el$14, "wrapMode", "none");
15538
- insert(_el$14, () => flatIds().length, _el$16);
15539
- insert(_el$14, totalRows, null);
15540
- effect((_$p) => setProp(_el$14, "fg", theme.textMuted, _$p));
15541
- return _el$14;
15915
+ var _el$15 = createElement("text"), _el$16 = createTextNode(` `), _el$17 = createTextNode(`/`);
15916
+ insertNode(_el$15, _el$16);
15917
+ insertNode(_el$15, _el$17);
15918
+ setProp(_el$15, "wrapMode", "none");
15919
+ insert(_el$15, () => flatIds().length, _el$17);
15920
+ insert(_el$15, totalRows, null);
15921
+ effect((_$p) => setProp(_el$15, "fg", theme.textMuted, _$p));
15922
+ return _el$15;
15542
15923
  }
15543
15924
  }), null);
15544
15925
  effect((_p$) => {
15545
- var { info: _v$, text: _v$2, info: _v$3 } = theme, _v$4 = TextAttributes8.BLINK;
15546
- _v$ !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$, _p$.e));
15547
- _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "fg", _v$2, _p$.t));
15548
- _v$3 !== _p$.a && (_p$.a = setProp(_el$1, "fg", _v$3, _p$.a));
15549
- _v$4 !== _p$.o && (_p$.o = setProp(_el$1, "attributes", _v$4, _p$.o));
15926
+ var { info: _v$5, text: _v$6, info: _v$7 } = theme, _v$8 = TextAttributes8.BLINK;
15927
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$9, "fg", _v$5, _p$.e));
15928
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$6, _p$.t));
15929
+ _v$7 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$7, _p$.a));
15930
+ _v$8 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$8, _p$.o));
15550
15931
  return _p$;
15551
15932
  }, {
15552
15933
  e: undefined,
@@ -15554,46 +15935,46 @@ function Sidebar(props) {
15554
15935
  a: undefined,
15555
15936
  o: undefined
15556
15937
  });
15557
- return _el$7;
15938
+ return _el$8;
15558
15939
  }
15559
- }), _el$17);
15560
- setProp(_el$17, "flexDirection", "row");
15561
- setProp(_el$17, "gap", 2);
15562
- setProp(_el$17, "paddingBottom", 1);
15563
- setProp(_el$17, "paddingLeft", 1);
15564
- insert(_el$17, createComponent2(For, {
15940
+ }), _el$18);
15941
+ setProp(_el$18, "flexDirection", "row");
15942
+ setProp(_el$18, "gap", 2);
15943
+ setProp(_el$18, "paddingBottom", 1);
15944
+ setProp(_el$18, "paddingLeft", 1);
15945
+ insert(_el$18, createComponent2(For, {
15565
15946
  each: VIEW_TABS,
15566
15947
  children: (tab) => {
15567
15948
  const active = () => view() === tab.view;
15568
15949
  return (() => {
15569
- var _el$25 = createElement("text");
15570
- setProp(_el$25, "wrapMode", "none");
15571
- setProp(_el$25, "onMouseUp", () => setView(tab.view));
15572
- insert(_el$25, () => tab.label);
15950
+ var _el$27 = createElement("text");
15951
+ setProp(_el$27, "wrapMode", "none");
15952
+ setProp(_el$27, "onMouseUp", () => setView(tab.view));
15953
+ insert(_el$27, () => tab.label);
15573
15954
  effect((_p$) => {
15574
- var _v$1 = active() ? theme.primary : theme.textMuted, _v$10 = active() ? TextAttributes8.BOLD : undefined;
15575
- _v$1 !== _p$.e && (_p$.e = setProp(_el$25, "fg", _v$1, _p$.e));
15576
- _v$10 !== _p$.t && (_p$.t = setProp(_el$25, "attributes", _v$10, _p$.t));
15955
+ var _v$13 = active() ? theme.primary : theme.textMuted, _v$14 = active() ? TextAttributes8.BOLD : undefined;
15956
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$13, _p$.e));
15957
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$14, _p$.t));
15577
15958
  return _p$;
15578
15959
  }, {
15579
15960
  e: undefined,
15580
15961
  t: undefined
15581
15962
  });
15582
- return _el$25;
15963
+ return _el$27;
15583
15964
  })();
15584
15965
  }
15585
15966
  }));
15586
- insertNode(_el$18, _el$19);
15587
- setProp(_el$18, "flexGrow", 1);
15588
- setProp(_el$18, "verticalScrollbarOptions", {
15967
+ insertNode(_el$19, _el$20);
15968
+ setProp(_el$19, "flexGrow", 1);
15969
+ setProp(_el$19, "verticalScrollbarOptions", {
15589
15970
  trackOptions: {
15590
15971
  foregroundColor: "transparent"
15591
15972
  }
15592
15973
  });
15593
- setProp(_el$19, "flexShrink", 0);
15594
- setProp(_el$19, "gap", 0);
15595
- setProp(_el$19, "paddingRight", 1);
15596
- insert(_el$19, createComponent2(For, {
15974
+ setProp(_el$20, "flexShrink", 0);
15975
+ setProp(_el$20, "gap", 0);
15976
+ setProp(_el$20, "paddingRight", 1);
15977
+ insert(_el$20, createComponent2(For, {
15597
15978
  get each() {
15598
15979
  return rows();
15599
15980
  },
@@ -15633,175 +16014,290 @@ function Sidebar(props) {
15633
16014
  return theme.textMuted;
15634
16015
  }
15635
16016
  };
15636
- const branchLabel = createMemo(() => {
15637
- if (!isMain)
15638
- return "";
15639
- branchTick();
15640
- return readCurrentBranch(task.repo);
15641
- });
15642
16017
  const changes = createMemo(() => {
15643
16018
  branchTick();
15644
16019
  return readWorktreeChanges(task.worktreePath);
15645
16020
  });
15646
16021
  const titleText = isMain ? repoBasename(task.repo) : task.title;
16022
+ const loading = () => isLive() || task.status === "in_progress";
16023
+ const subtitleText = createMemo(() => {
16024
+ if (task.branch.length > 0)
16025
+ return truncateBranchLabel(task.branch, subtitleBudget());
16026
+ return STATUS_LABEL[task.status];
16027
+ });
16028
+ const barColor = () => isCursor() ? theme.focusAccent : isSelected() ? theme.primary : undefined;
16029
+ const barGlyph = () => isCursor() || isSelected() ? "\u258C" : " ";
16030
+ const showProjectsHeader = () => isMain && flatIndex === 0;
16031
+ const showTasksHeader = () => !isMain && flatIndex === firstTaskFlatIndex();
15647
16032
  return (() => {
15648
- var _el$26 = createElement("box"), _el$27 = createElement("text"), _el$28 = createElement("text");
15649
- insertNode(_el$26, _el$27);
15650
- insertNode(_el$26, _el$28);
15651
- setProp(_el$26, "flexDirection", "row");
15652
- setProp(_el$26, "paddingLeft", 1);
15653
- setProp(_el$26, "paddingRight", 1);
15654
- setProp(_el$26, "gap", 1);
15655
- setProp(_el$26, "onMouseUp", () => {
16033
+ var _el$28 = createElement("box"), _el$29 = createElement("box");
16034
+ insertNode(_el$28, _el$29);
16035
+ setProp(_el$28, "flexDirection", "column");
16036
+ setProp(_el$28, "gap", 0);
16037
+ setProp(_el$28, "paddingBottom", isMain ? 0 : 1);
16038
+ insert(_el$28, createComponent2(Show, {
16039
+ get when() {
16040
+ return showProjectsHeader();
16041
+ },
16042
+ get children() {
16043
+ return createComponent2(SectionHeader, {
16044
+ label: "PROJECTS"
16045
+ });
16046
+ }
16047
+ }), _el$29);
16048
+ insert(_el$28, createComponent2(Show, {
16049
+ get when() {
16050
+ return showTasksHeader();
16051
+ },
16052
+ get children() {
16053
+ return createComponent2(SectionHeader, {
16054
+ label: "TASKS",
16055
+ get topPad() {
16056
+ return firstTaskFlatIndex() > 0;
16057
+ }
16058
+ });
16059
+ }
16060
+ }), _el$29);
16061
+ setProp(_el$29, "flexDirection", "column");
16062
+ setProp(_el$29, "gap", 0);
16063
+ setProp(_el$29, "onMouseUp", () => {
15656
16064
  props.onSelect(task.id);
15657
16065
  if (props.activateOnClick)
15658
16066
  props.onActivate?.(task.id);
15659
16067
  });
15660
- setProp(_el$26, "onMouseOver", (e) => setHover({
16068
+ setProp(_el$29, "onMouseOver", (e) => setHover({
15661
16069
  task,
15662
16070
  x: e.x,
15663
16071
  y: e.y
15664
16072
  }));
15665
- setProp(_el$26, "onMouseOut", () => setHover((h) => h?.task.id === task.id ? null : h));
15666
- setProp(_el$27, "wrapMode", "none");
15667
- insert(_el$27, () => isMain ? "\u2605" : isLive() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : task.status === "in_progress" ? "\u25CF" : badge.glyph);
15668
- setProp(_el$28, "wrapMode", "none");
15669
- setProp(_el$28, "flexGrow", 1);
15670
- insert(_el$28, () => truncateTitle(titleText, titleBudget()));
15671
- insert(_el$26, createComponent2(Show, {
15672
- get when() {
15673
- return showChangesCol();
15674
- },
15675
- get children() {
15676
- var _el$29 = createElement("box");
15677
- setProp(_el$29, "width", 5);
15678
- setProp(_el$29, "flexShrink", 0);
15679
- setProp(_el$29, "flexDirection", "row");
15680
- setProp(_el$29, "justifyContent", "flex-end");
15681
- setProp(_el$29, "gap", 1);
15682
- insert(_el$29, createComponent2(Show, {
15683
- get when() {
15684
- return changes().added > 0;
15685
- },
15686
- get children() {
15687
- var _el$30 = createElement("text"), _el$31 = createTextNode(`+`);
15688
- insertNode(_el$30, _el$31);
15689
- setProp(_el$30, "wrapMode", "none");
15690
- insert(_el$30, () => changes().added, null);
15691
- effect((_$p) => setProp(_el$30, "fg", isCursor() ? theme.selectedListItemText : theme.success, _$p));
15692
- return _el$30;
15693
- }
15694
- }), null);
15695
- insert(_el$29, createComponent2(Show, {
15696
- get when() {
15697
- return changes().deleted > 0;
15698
- },
15699
- get children() {
15700
- var _el$32 = createElement("text"), _el$33 = createTextNode(`\u2212`);
15701
- insertNode(_el$32, _el$33);
15702
- setProp(_el$32, "wrapMode", "none");
15703
- insert(_el$32, () => changes().deleted, null);
15704
- effect((_$p) => setProp(_el$32, "fg", isCursor() ? theme.selectedListItemText : theme.error, _$p));
15705
- return _el$32;
15706
- }
15707
- }), null);
15708
- return _el$29;
15709
- }
15710
- }), null);
15711
- insert(_el$26, createComponent2(Show, {
15712
- get when() {
15713
- return memo2(() => !!showBranchCol())() && (isMain && branchLabel().length > 0 || !isMain && (task.branch.length > 0 || task.pinned === true));
15714
- },
16073
+ setProp(_el$29, "onMouseOut", () => setHover((h) => h?.task.id === task.id ? null : h));
16074
+ insert(_el$29, createComponent2(Show, {
16075
+ when: isMain,
15715
16076
  get children() {
15716
- var _el$34 = createElement("box");
15717
- setProp(_el$34, "flexDirection", "row");
15718
- setProp(_el$34, "justifyContent", "flex-end");
15719
- setProp(_el$34, "gap", 1);
15720
- setProp(_el$34, "paddingLeft", 1);
15721
- setProp(_el$34, "flexShrink", 0);
15722
- insert(_el$34, createComponent2(Show, {
16077
+ var _el$30 = createElement("box"), _el$31 = createElement("text"), _el$32 = createElement("box"), _el$33 = createElement("text"), _el$34 = createElement("text");
16078
+ insertNode(_el$30, _el$31);
16079
+ insertNode(_el$30, _el$32);
16080
+ setProp(_el$30, "flexDirection", "row");
16081
+ setProp(_el$30, "gap", 0);
16082
+ setProp(_el$31, "wrapMode", "none");
16083
+ insert(_el$31, barGlyph);
16084
+ insertNode(_el$32, _el$33);
16085
+ insertNode(_el$32, _el$34);
16086
+ setProp(_el$32, "flexDirection", "row");
16087
+ setProp(_el$32, "flexGrow", 1);
16088
+ setProp(_el$32, "paddingRight", 1);
16089
+ setProp(_el$32, "gap", 1);
16090
+ setProp(_el$33, "wrapMode", "none");
16091
+ insert(_el$33, (() => {
16092
+ var _c$2 = memo2(() => !!loading());
16093
+ return () => _c$2() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : "\u2605";
16094
+ })());
16095
+ setProp(_el$34, "wrapMode", "none");
16096
+ setProp(_el$34, "flexGrow", 1);
16097
+ insert(_el$34, () => truncateTitle(titleText, titleBudget()));
16098
+ insert(_el$32, createComponent2(Show, {
15723
16099
  get when() {
15724
- return isMain && branchLabel().length > 0;
16100
+ return loading();
15725
16101
  },
15726
16102
  get children() {
15727
16103
  var _el$35 = createElement("text");
16104
+ insertNode(_el$35, createTextNode(`working`));
15728
16105
  setProp(_el$35, "wrapMode", "none");
15729
- insert(_el$35, branchLabel);
15730
- effect((_$p) => setProp(_el$35, "fg", isCursor() ? theme.selectedListItemText : theme.textMuted, _$p));
16106
+ effect((_$p) => setProp(_el$35, "fg", theme.primary, _$p));
15731
16107
  return _el$35;
15732
16108
  }
15733
16109
  }), null);
15734
- insert(_el$34, createComponent2(Show, {
15735
- get when() {
15736
- return !isMain && task.branch.length > 0;
15737
- },
15738
- get children() {
15739
- var _el$36 = createElement("text");
15740
- setProp(_el$36, "wrapMode", "none");
15741
- insert(_el$36, () => truncateBranchLabel(task.branch));
15742
- effect((_$p) => setProp(_el$36, "fg", isCursor() ? theme.selectedListItemText : theme.textMuted, _$p));
15743
- return _el$36;
15744
- }
15745
- }), null);
15746
- insert(_el$34, createComponent2(Show, {
16110
+ insert(_el$32, createComponent2(Show, {
15747
16111
  get when() {
15748
- return !isMain && task.pinned === true;
16112
+ return !loading();
15749
16113
  },
15750
16114
  get children() {
15751
16115
  var _el$37 = createElement("text");
15752
- insertNode(_el$37, createTextNode(`\u25B4`));
15753
16116
  setProp(_el$37, "wrapMode", "none");
15754
- effect((_$p) => setProp(_el$37, "fg", isCursor() ? theme.selectedListItemText : theme.warning, _$p));
16117
+ insert(_el$37, () => truncatePathTail(abbrevHome(task.repo), subtitleBudget()));
16118
+ effect((_p$) => {
16119
+ var _v$15 = theme.textMuted, _v$16 = TextAttributes8.DIM;
16120
+ _v$15 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$15, _p$.e));
16121
+ _v$16 !== _p$.t && (_p$.t = setProp(_el$37, "attributes", _v$16, _p$.t));
16122
+ return _p$;
16123
+ }, {
16124
+ e: undefined,
16125
+ t: undefined
16126
+ });
15755
16127
  return _el$37;
15756
16128
  }
15757
16129
  }), null);
15758
- return _el$34;
16130
+ effect((_p$) => {
16131
+ var _v$17 = barColor(), _v$18 = theme.primary, _v$19 = TextAttributes8.BOLD, _v$20 = theme.text, _v$21 = TextAttributes8.BOLD;
16132
+ _v$17 !== _p$.e && (_p$.e = setProp(_el$31, "fg", _v$17, _p$.e));
16133
+ _v$18 !== _p$.t && (_p$.t = setProp(_el$33, "fg", _v$18, _p$.t));
16134
+ _v$19 !== _p$.a && (_p$.a = setProp(_el$33, "attributes", _v$19, _p$.a));
16135
+ _v$20 !== _p$.o && (_p$.o = setProp(_el$34, "fg", _v$20, _p$.o));
16136
+ _v$21 !== _p$.i && (_p$.i = setProp(_el$34, "attributes", _v$21, _p$.i));
16137
+ return _p$;
16138
+ }, {
16139
+ e: undefined,
16140
+ t: undefined,
16141
+ a: undefined,
16142
+ o: undefined,
16143
+ i: undefined
16144
+ });
16145
+ return _el$30;
15759
16146
  }
15760
16147
  }), null);
15761
- effect((_p$) => {
15762
- var _v$11 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$12 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$13 = TextAttributes8.BOLD, _v$14 = isCursor() ? theme.selectedListItemText : theme.text, _v$15 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes8.BOLD : undefined;
15763
- _v$11 !== _p$.e && (_p$.e = setProp(_el$26, "backgroundColor", _v$11, _p$.e));
15764
- _v$12 !== _p$.t && (_p$.t = setProp(_el$27, "fg", _v$12, _p$.t));
15765
- _v$13 !== _p$.a && (_p$.a = setProp(_el$27, "attributes", _v$13, _p$.a));
15766
- _v$14 !== _p$.o && (_p$.o = setProp(_el$28, "fg", _v$14, _p$.o));
15767
- _v$15 !== _p$.i && (_p$.i = setProp(_el$28, "attributes", _v$15, _p$.i));
15768
- return _p$;
15769
- }, {
15770
- e: undefined,
15771
- t: undefined,
15772
- a: undefined,
15773
- o: undefined,
15774
- i: undefined
15775
- });
15776
- return _el$26;
16148
+ insert(_el$29, createComponent2(Show, {
16149
+ when: !isMain,
16150
+ get children() {
16151
+ return [(() => {
16152
+ var _el$38 = createElement("box"), _el$39 = createElement("text"), _el$40 = createElement("box"), _el$41 = createElement("text"), _el$42 = createElement("text");
16153
+ insertNode(_el$38, _el$39);
16154
+ insertNode(_el$38, _el$40);
16155
+ setProp(_el$38, "flexDirection", "row");
16156
+ setProp(_el$38, "gap", 0);
16157
+ setProp(_el$39, "wrapMode", "none");
16158
+ insert(_el$39, barGlyph);
16159
+ insertNode(_el$40, _el$41);
16160
+ insertNode(_el$40, _el$42);
16161
+ setProp(_el$40, "flexDirection", "row");
16162
+ setProp(_el$40, "flexGrow", 1);
16163
+ setProp(_el$40, "paddingRight", 1);
16164
+ setProp(_el$40, "gap", 1);
16165
+ setProp(_el$41, "wrapMode", "none");
16166
+ insert(_el$41, (() => {
16167
+ var _c$3 = memo2(() => !!loading());
16168
+ return () => _c$3() ? IN_PROGRESS_SPINNER[spinnerFrame()] ?? IN_PROGRESS_SPINNER[0] : badge.glyph;
16169
+ })());
16170
+ setProp(_el$42, "wrapMode", "none");
16171
+ setProp(_el$42, "flexGrow", 1);
16172
+ insert(_el$42, () => truncateTitle(titleText, titleBudget()));
16173
+ insert(_el$40, createComponent2(Show, {
16174
+ get when() {
16175
+ return loading();
16176
+ },
16177
+ get children() {
16178
+ var _el$43 = createElement("text");
16179
+ insertNode(_el$43, createTextNode(`working`));
16180
+ setProp(_el$43, "wrapMode", "none");
16181
+ effect((_$p) => setProp(_el$43, "fg", theme.primary, _$p));
16182
+ return _el$43;
16183
+ }
16184
+ }), null);
16185
+ effect((_p$) => {
16186
+ var _v$22 = barColor(), _v$23 = badgeColor(), _v$24 = TextAttributes8.BOLD, _v$25 = theme.text, _v$26 = isSelected() || isCursor() ? TextAttributes8.BOLD : undefined;
16187
+ _v$22 !== _p$.e && (_p$.e = setProp(_el$39, "fg", _v$22, _p$.e));
16188
+ _v$23 !== _p$.t && (_p$.t = setProp(_el$41, "fg", _v$23, _p$.t));
16189
+ _v$24 !== _p$.a && (_p$.a = setProp(_el$41, "attributes", _v$24, _p$.a));
16190
+ _v$25 !== _p$.o && (_p$.o = setProp(_el$42, "fg", _v$25, _p$.o));
16191
+ _v$26 !== _p$.i && (_p$.i = setProp(_el$42, "attributes", _v$26, _p$.i));
16192
+ return _p$;
16193
+ }, {
16194
+ e: undefined,
16195
+ t: undefined,
16196
+ a: undefined,
16197
+ o: undefined,
16198
+ i: undefined
16199
+ });
16200
+ return _el$38;
16201
+ })(), (() => {
16202
+ var _el$45 = createElement("box"), _el$46 = createElement("text"), _el$47 = createElement("box"), _el$48 = createElement("text");
16203
+ insertNode(_el$45, _el$46);
16204
+ insertNode(_el$45, _el$47);
16205
+ setProp(_el$45, "flexDirection", "row");
16206
+ setProp(_el$45, "gap", 0);
16207
+ setProp(_el$46, "wrapMode", "none");
16208
+ insert(_el$46, barGlyph);
16209
+ insertNode(_el$47, _el$48);
16210
+ setProp(_el$47, "flexDirection", "row");
16211
+ setProp(_el$47, "flexGrow", 1);
16212
+ setProp(_el$47, "paddingLeft", 2);
16213
+ setProp(_el$47, "paddingRight", 1);
16214
+ setProp(_el$47, "gap", 1);
16215
+ setProp(_el$48, "wrapMode", "none");
16216
+ setProp(_el$48, "flexGrow", 1);
16217
+ insert(_el$48, subtitleText);
16218
+ insert(_el$47, createComponent2(Show, {
16219
+ get when() {
16220
+ return task.pinned === true;
16221
+ },
16222
+ get children() {
16223
+ var _el$49 = createElement("text");
16224
+ insertNode(_el$49, createTextNode(`\u25B4`));
16225
+ setProp(_el$49, "wrapMode", "none");
16226
+ effect((_$p) => setProp(_el$49, "fg", theme.warning, _$p));
16227
+ return _el$49;
16228
+ }
16229
+ }), null);
16230
+ insert(_el$47, createComponent2(Show, {
16231
+ get when() {
16232
+ return changes().added > 0;
16233
+ },
16234
+ get children() {
16235
+ var _el$51 = createElement("text"), _el$52 = createTextNode(`+`);
16236
+ insertNode(_el$51, _el$52);
16237
+ setProp(_el$51, "wrapMode", "none");
16238
+ insert(_el$51, () => changes().added, null);
16239
+ effect((_$p) => setProp(_el$51, "fg", theme.success, _$p));
16240
+ return _el$51;
16241
+ }
16242
+ }), null);
16243
+ insert(_el$47, createComponent2(Show, {
16244
+ get when() {
16245
+ return changes().deleted > 0;
16246
+ },
16247
+ get children() {
16248
+ var _el$53 = createElement("text"), _el$54 = createTextNode(`\u2212`);
16249
+ insertNode(_el$53, _el$54);
16250
+ setProp(_el$53, "wrapMode", "none");
16251
+ insert(_el$53, () => changes().deleted, null);
16252
+ effect((_$p) => setProp(_el$53, "fg", theme.error, _$p));
16253
+ return _el$53;
16254
+ }
16255
+ }), null);
16256
+ effect((_p$) => {
16257
+ var _v$27 = barColor(), _v$28 = theme.textMuted, _v$29 = TextAttributes8.DIM;
16258
+ _v$27 !== _p$.e && (_p$.e = setProp(_el$46, "fg", _v$27, _p$.e));
16259
+ _v$28 !== _p$.t && (_p$.t = setProp(_el$48, "fg", _v$28, _p$.t));
16260
+ _v$29 !== _p$.a && (_p$.a = setProp(_el$48, "attributes", _v$29, _p$.a));
16261
+ return _p$;
16262
+ }, {
16263
+ e: undefined,
16264
+ t: undefined,
16265
+ a: undefined
16266
+ });
16267
+ return _el$45;
16268
+ })()];
16269
+ }
16270
+ }), null);
16271
+ effect((_$p) => setProp(_el$29, "backgroundColor", isCursor() ? theme.backgroundElement : undefined, _$p));
16272
+ return _el$28;
15777
16273
  })();
15778
16274
  }
15779
16275
  }), null);
15780
- insert(_el$19, createComponent2(Show, {
16276
+ insert(_el$20, createComponent2(Show, {
15781
16277
  get when() {
15782
16278
  return flatIds().length === 0;
15783
16279
  },
15784
16280
  get children() {
15785
- var _el$20 = createElement("box"), _el$21 = createElement("text");
15786
- insertNode(_el$20, _el$21);
15787
- setProp(_el$20, "paddingTop", 1);
15788
- setProp(_el$20, "paddingLeft", 1);
15789
- insert(_el$21, (() => {
16281
+ var _el$21 = createElement("box"), _el$22 = createElement("text");
16282
+ insertNode(_el$21, _el$22);
16283
+ setProp(_el$21, "paddingTop", 1);
16284
+ setProp(_el$21, "paddingLeft", 1);
16285
+ insert(_el$22, (() => {
15790
16286
  var _c$ = memo2(() => !!(searchMode() && searchQuery().trim().length > 0));
15791
16287
  return () => _c$() ? "No matching tasks \u2014 esc to clear." : view() === "active" ? "No active tasks." : "No archived tasks.";
15792
16288
  })());
15793
- effect((_$p) => setProp(_el$21, "fg", theme.textMuted, _$p));
15794
- return _el$20;
16289
+ effect((_$p) => setProp(_el$22, "fg", theme.textMuted, _$p));
16290
+ return _el$21;
15795
16291
  }
15796
16292
  }), null);
15797
- insertNode(_el$22, _el$23);
15798
- setProp(_el$22, "flexShrink", 0);
15799
- setProp(_el$22, "paddingTop", 1);
15800
- setProp(_el$22, "paddingLeft", 1);
15801
- insertNode(_el$23, createTextNode(`+ New task`));
15802
- setProp(_el$23, "wrapMode", "none");
15803
- setProp(_el$23, "onMouseUp", () => props.onAddTask?.());
15804
- insert(_el$, createComponent2(Show, {
16293
+ insertNode(_el$23, _el$24);
16294
+ setProp(_el$23, "flexShrink", 0);
16295
+ setProp(_el$23, "paddingTop", 1);
16296
+ setProp(_el$23, "paddingLeft", 1);
16297
+ insertNode(_el$24, createTextNode(`+ New task`));
16298
+ setProp(_el$24, "wrapMode", "none");
16299
+ setProp(_el$24, "onMouseUp", () => props.onAddTask?.());
16300
+ insert(_el$4, createComponent2(Show, {
15805
16301
  get when() {
15806
16302
  return hover();
15807
16303
  },
@@ -15831,43 +16327,43 @@ function Sidebar(props) {
15831
16327
  const left = createMemo(() => Math.max(0, Math.min(h().x + 2, dims().width - boxW() - 1)));
15832
16328
  const top = createMemo(() => Math.max(0, Math.min(h().y + 1, dims().height - boxH() - 1)));
15833
16329
  return (() => {
15834
- var _el$39 = createElement("box");
15835
- setProp(_el$39, "position", "absolute");
15836
- setProp(_el$39, "zIndex", 2600);
15837
- setProp(_el$39, "flexDirection", "column");
15838
- setProp(_el$39, "border", true);
15839
- setProp(_el$39, "paddingLeft", 1);
15840
- setProp(_el$39, "paddingRight", 1);
15841
- insert(_el$39, createComponent2(For, {
16330
+ var _el$55 = createElement("box");
16331
+ setProp(_el$55, "position", "absolute");
16332
+ setProp(_el$55, "zIndex", 2600);
16333
+ setProp(_el$55, "flexDirection", "column");
16334
+ setProp(_el$55, "border", true);
16335
+ setProp(_el$55, "paddingLeft", 1);
16336
+ setProp(_el$55, "paddingRight", 1);
16337
+ insert(_el$55, createComponent2(For, {
15842
16338
  get each() {
15843
16339
  return lines();
15844
16340
  },
15845
16341
  children: (l) => (() => {
15846
- var _el$40 = createElement("text");
15847
- setProp(_el$40, "wrapMode", "none");
15848
- insert(_el$40, (() => {
15849
- var _c$2 = memo2(() => !!l.dim);
15850
- return () => _c$2() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
16342
+ var _el$56 = createElement("text");
16343
+ setProp(_el$56, "wrapMode", "none");
16344
+ insert(_el$56, (() => {
16345
+ var _c$4 = memo2(() => !!l.dim);
16346
+ return () => _c$4() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
15851
16347
  })());
15852
16348
  effect((_p$) => {
15853
- var _v$21 = l.dim ? theme.textMuted : theme.text, _v$22 = l.bold ? TextAttributes8.BOLD : l.dim ? TextAttributes8.DIM : undefined;
15854
- _v$21 !== _p$.e && (_p$.e = setProp(_el$40, "fg", _v$21, _p$.e));
15855
- _v$22 !== _p$.t && (_p$.t = setProp(_el$40, "attributes", _v$22, _p$.t));
16349
+ var _v$35 = l.dim ? theme.textMuted : theme.text, _v$36 = l.bold ? TextAttributes8.BOLD : l.dim ? TextAttributes8.DIM : undefined;
16350
+ _v$35 !== _p$.e && (_p$.e = setProp(_el$56, "fg", _v$35, _p$.e));
16351
+ _v$36 !== _p$.t && (_p$.t = setProp(_el$56, "attributes", _v$36, _p$.t));
15856
16352
  return _p$;
15857
16353
  }, {
15858
16354
  e: undefined,
15859
16355
  t: undefined
15860
16356
  });
15861
- return _el$40;
16357
+ return _el$56;
15862
16358
  })()
15863
16359
  }));
15864
16360
  effect((_p$) => {
15865
- var _v$16 = left(), _v$17 = top(), _v$18 = boxW(), _v$19 = theme.focusAccent, _v$20 = theme.backgroundElement;
15866
- _v$16 !== _p$.e && (_p$.e = setProp(_el$39, "left", _v$16, _p$.e));
15867
- _v$17 !== _p$.t && (_p$.t = setProp(_el$39, "top", _v$17, _p$.t));
15868
- _v$18 !== _p$.a && (_p$.a = setProp(_el$39, "width", _v$18, _p$.a));
15869
- _v$19 !== _p$.o && (_p$.o = setProp(_el$39, "borderColor", _v$19, _p$.o));
15870
- _v$20 !== _p$.i && (_p$.i = setProp(_el$39, "backgroundColor", _v$20, _p$.i));
16361
+ var _v$30 = left(), _v$31 = top(), _v$32 = boxW(), _v$33 = theme.focusAccent, _v$34 = theme.backgroundElement;
16362
+ _v$30 !== _p$.e && (_p$.e = setProp(_el$55, "left", _v$30, _p$.e));
16363
+ _v$31 !== _p$.t && (_p$.t = setProp(_el$55, "top", _v$31, _p$.t));
16364
+ _v$32 !== _p$.a && (_p$.a = setProp(_el$55, "width", _v$32, _p$.a));
16365
+ _v$33 !== _p$.o && (_p$.o = setProp(_el$55, "borderColor", _v$33, _p$.o));
16366
+ _v$34 !== _p$.i && (_p$.i = setProp(_el$55, "backgroundColor", _v$34, _p$.i));
15871
16367
  return _p$;
15872
16368
  }, {
15873
16369
  e: undefined,
@@ -15876,31 +16372,27 @@ function Sidebar(props) {
15876
16372
  o: undefined,
15877
16373
  i: undefined
15878
16374
  });
15879
- return _el$39;
16375
+ return _el$55;
15880
16376
  })();
15881
16377
  }
15882
16378
  }), null);
15883
16379
  effect((_p$) => {
15884
- var _v$5 = props.width ? props.width() : SIDEBAR_WIDTH, _v$6 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$7 = TextAttributes8.BOLD, _v$8 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$9 = TextAttributes8.BOLD, _v$0 = theme.textMuted;
15885
- _v$5 !== _p$.e && (_p$.e = setProp(_el$, "width", _v$5, _p$.e));
15886
- _v$6 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$6, _p$.t));
15887
- _v$7 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$7, _p$.a));
15888
- _v$8 !== _p$.o && (_p$.o = setProp(_el$5, "fg", _v$8, _p$.o));
15889
- _v$9 !== _p$.i && (_p$.i = setProp(_el$5, "attributes", _v$9, _p$.i));
15890
- _v$0 !== _p$.n && (_p$.n = setProp(_el$23, "fg", _v$0, _p$.n));
16380
+ var _v$9 = props.width ? props.width() : SIDEBAR_WIDTH, _v$0 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$1 = TextAttributes8.BOLD, _v$10 = theme.textMuted;
16381
+ _v$9 !== _p$.e && (_p$.e = setProp(_el$4, "width", _v$9, _p$.e));
16382
+ _v$0 !== _p$.t && (_p$.t = setProp(_el$6, "fg", _v$0, _p$.t));
16383
+ _v$1 !== _p$.a && (_p$.a = setProp(_el$6, "attributes", _v$1, _p$.a));
16384
+ _v$10 !== _p$.o && (_p$.o = setProp(_el$24, "fg", _v$10, _p$.o));
15891
16385
  return _p$;
15892
16386
  }, {
15893
16387
  e: undefined,
15894
16388
  t: undefined,
15895
16389
  a: undefined,
15896
- o: undefined,
15897
- i: undefined,
15898
- n: undefined
16390
+ o: undefined
15899
16391
  });
15900
- return _el$;
16392
+ return _el$4;
15901
16393
  })();
15902
16394
  }
15903
- var SIDEBAR_WIDTH = 32, STATUS_BADGE, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, VIEW_TABS, MAIN_BRANCH_POLL_MS = 2000, CHANGES_COLUMN_WIDTH = 5, BRANCH_LABEL_MAX = 16, SHOW_CHANGES_MIN_WIDTH = 30, SHOW_BRANCH_MIN_WIDTH = 44;
16395
+ var SIDEBAR_WIDTH = 32, STATUS_BADGE, STATUS_LABEL, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, VIEW_TABS, MAIN_BRANCH_POLL_MS = 2000, BRANCH_LABEL_MAX = 16;
15904
16396
  var init_Sidebar = __esm(() => {
15905
16397
  init_solid();
15906
16398
  init_solid();
@@ -15914,7 +16406,6 @@ var init_Sidebar = __esm(() => {
15914
16406
  init_dev();
15915
16407
  init_theme2();
15916
16408
  init_theme2();
15917
- init_git_head();
15918
16409
  init_groups();
15919
16410
  init_keys();
15920
16411
  init_worktree_changes();
@@ -15944,6 +16435,14 @@ var init_Sidebar = __esm(() => {
15944
16435
  tone: "error"
15945
16436
  }
15946
16437
  };
16438
+ STATUS_LABEL = {
16439
+ done: "done",
16440
+ in_review: "in review",
16441
+ in_progress: "working",
16442
+ backlog: "backlog",
16443
+ canceled: "canceled",
16444
+ error: "error"
16445
+ };
15947
16446
  IN_PROGRESS_SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
15948
16447
  VIEW_TABS = [{
15949
16448
  view: "active",
@@ -16049,7 +16548,18 @@ function TasksShell(props) {
16049
16548
  return;
16050
16549
  }
16051
16550
  }
16052
- SettingsDialog.show(dialog, kv, props.orch ?? undefined);
16551
+ const result = await SettingsDialog.show(dialog, kv, props.orch ?? undefined);
16552
+ if (!result.visualPrefsChanged)
16553
+ return;
16554
+ if (!kv.flush())
16555
+ return;
16556
+ try {
16557
+ const session = await currentSessionName();
16558
+ if (session)
16559
+ await refreshKobeWorkspacePanes(session);
16560
+ } catch (err) {
16561
+ console.error("[kobe tasks] failed to refresh workspace panes:", err);
16562
+ }
16053
16563
  }
16054
16564
  async function openUpdate() {
16055
16565
  const info = updateInfo();
@@ -16284,6 +16794,18 @@ function TasksShell(props) {
16284
16794
  await runTmux(["switch-client", "-t", `=${name}`]);
16285
16795
  props.orch?.setActiveTask(id).catch(() => {});
16286
16796
  }
16797
+ const headerStatus = createMemo(() => {
16798
+ const info = updateInfo();
16799
+ if (info?.hasUpdate)
16800
+ return {
16801
+ label: `v${info.latest} \u2191`,
16802
+ emphasize: true
16803
+ };
16804
+ return {
16805
+ label: `v${CURRENT_VERSION}`,
16806
+ emphasize: false
16807
+ };
16808
+ });
16287
16809
  return (() => {
16288
16810
  var _el$ = createElement("box"), _el$2 = createElement("box");
16289
16811
  insertNode(_el$, _el$2);
@@ -16299,6 +16821,8 @@ function TasksShell(props) {
16299
16821
  onSelect: setSelectedId,
16300
16822
  onActivate: (id) => void switchTo(id),
16301
16823
  activateOnClick: true,
16824
+ headerStatus,
16825
+ onHeaderStatusClick: () => void openUpdate(),
16302
16826
  width: () => dimensions().width,
16303
16827
  onAddTask: () => void createTask(),
16304
16828
  onRenameRequest: (id) => void renameTask(id),
@@ -16306,24 +16830,15 @@ function TasksShell(props) {
16306
16830
  onArchiveRequest: (id) => void archiveTask(id),
16307
16831
  focused: () => dialog.stack.length === 0
16308
16832
  }));
16309
- insert(_el$, createComponent2(ShortcutHints, {
16310
- updateInfo,
16311
- onOpenUpdate: () => void openUpdate()
16312
- }), null);
16833
+ insert(_el$, createComponent2(ShortcutHints, {}), null);
16313
16834
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
16314
16835
  return _el$;
16315
16836
  })();
16316
16837
  }
16317
- function ShortcutHints(props) {
16838
+ function ShortcutHints() {
16318
16839
  const {
16319
16840
  theme
16320
16841
  } = useTheme();
16321
- const updateLabel = createMemo(() => {
16322
- const info = props.updateInfo();
16323
- if (!info)
16324
- return `v${CURRENT_VERSION}`;
16325
- return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION} latest`;
16326
- });
16327
16842
  const HINTS = [{
16328
16843
  k: "\u23CE",
16329
16844
  label: "open"
@@ -16373,111 +16888,63 @@ function ShortcutHints(props) {
16373
16888
  k: "\u2303Q",
16374
16889
  label: "detach"
16375
16890
  }];
16891
+ const LABEL_COL_MAX = 18;
16892
+ const labelColWidth = Math.min(LABEL_COL_MAX, Math.max(...HINTS.map((h) => h.label.length)));
16893
+ const clipLabel = (s) => s.length <= labelColWidth ? s : `${s.slice(0, Math.max(0, labelColWidth - 1))}\u2026`;
16376
16894
  return (() => {
16377
- var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$12 = createElement("text");
16895
+ var _el$3 = createElement("box"), _el$4 = createElement("text");
16378
16896
  insertNode(_el$3, _el$4);
16379
- insertNode(_el$3, _el$6);
16380
- insertNode(_el$3, _el$12);
16381
16897
  setProp(_el$3, "flexShrink", 0);
16382
16898
  setProp(_el$3, "flexDirection", "column");
16383
16899
  setProp(_el$3, "paddingLeft", 1);
16384
16900
  setProp(_el$3, "paddingRight", 1);
16385
16901
  setProp(_el$3, "paddingTop", 1);
16386
16902
  setProp(_el$3, "gap", 0);
16387
- insertNode(_el$4, createTextNode(`\u2500\u2500 system \u2500\u2500`));
16903
+ insertNode(_el$4, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
16388
16904
  setProp(_el$4, "wrapMode", "none");
16389
- insertNode(_el$6, _el$7);
16390
- setProp(_el$6, "flexDirection", "row");
16391
- setProp(_el$6, "gap", 1);
16392
- setProp(_el$7, "wrapMode", "none");
16393
- setProp(_el$7, "onMouseUp", () => {
16394
- if (props.updateInfo()?.hasUpdate)
16395
- props.onOpenUpdate();
16396
- });
16397
- insert(_el$7, updateLabel);
16398
- insert(_el$3, createComponent2(Show, {
16399
- get when() {
16400
- return props.updateInfo()?.hasUpdate;
16401
- },
16402
- get children() {
16403
- var _el$8 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$10 = createElement("text");
16404
- insertNode(_el$8, _el$9);
16405
- insertNode(_el$8, _el$10);
16406
- setProp(_el$8, "flexDirection", "row");
16407
- setProp(_el$8, "gap", 1);
16408
- setProp(_el$8, "onMouseUp", () => props.onOpenUpdate());
16409
- insertNode(_el$9, _el$0);
16410
- setProp(_el$9, "width", 10);
16411
- setProp(_el$9, "flexShrink", 0);
16412
- insertNode(_el$0, createTextNode(`[U]`));
16413
- setProp(_el$0, "wrapMode", "none");
16414
- insertNode(_el$10, createTextNode(`update page`));
16415
- setProp(_el$10, "wrapMode", "none");
16416
- effect((_p$) => {
16417
- var _v$ = theme.accent, _v$2 = TextAttributes9.BOLD, _v$3 = theme.accent, _v$4 = TextAttributes9.DIM;
16418
- _v$ !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$, _p$.e));
16419
- _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$2, _p$.t));
16420
- _v$3 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$3, _p$.a));
16421
- _v$4 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$4, _p$.o));
16422
- return _p$;
16423
- }, {
16424
- e: undefined,
16425
- t: undefined,
16426
- a: undefined,
16427
- o: undefined
16428
- });
16429
- return _el$8;
16430
- }
16431
- }), _el$12);
16432
- insertNode(_el$12, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
16433
- setProp(_el$12, "wrapMode", "none");
16434
16905
  insert(_el$3, createComponent2(For, {
16435
16906
  each: HINTS,
16436
16907
  children: (h) => (() => {
16437
- var _el$14 = createElement("box"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createTextNode(`[`), _el$18 = createTextNode(`]`), _el$19 = createElement("text");
16438
- insertNode(_el$14, _el$15);
16439
- insertNode(_el$14, _el$19);
16440
- setProp(_el$14, "flexDirection", "row");
16441
- setProp(_el$14, "gap", 1);
16442
- insertNode(_el$15, _el$16);
16443
- setProp(_el$15, "width", 10);
16444
- setProp(_el$15, "flexShrink", 0);
16445
- insertNode(_el$16, _el$17);
16446
- insertNode(_el$16, _el$18);
16447
- setProp(_el$16, "wrapMode", "none");
16448
- insert(_el$16, () => h.k, _el$18);
16449
- setProp(_el$19, "wrapMode", "none");
16450
- insert(_el$19, () => h.label);
16908
+ var _el$6 = createElement("box"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createTextNode(`[`), _el$0 = createTextNode(`]`), _el$1 = createElement("box"), _el$10 = createElement("text");
16909
+ insertNode(_el$6, _el$7);
16910
+ insertNode(_el$6, _el$1);
16911
+ setProp(_el$6, "flexDirection", "row");
16912
+ setProp(_el$6, "gap", 1);
16913
+ setProp(_el$6, "justifyContent", "space-between");
16914
+ insertNode(_el$7, _el$8);
16915
+ setProp(_el$7, "width", 10);
16916
+ setProp(_el$7, "flexShrink", 0);
16917
+ insertNode(_el$8, _el$9);
16918
+ insertNode(_el$8, _el$0);
16919
+ setProp(_el$8, "wrapMode", "none");
16920
+ insert(_el$8, () => h.k, _el$0);
16921
+ insertNode(_el$1, _el$10);
16922
+ setProp(_el$1, "width", labelColWidth);
16923
+ setProp(_el$1, "flexShrink", 0);
16924
+ setProp(_el$10, "wrapMode", "none");
16925
+ insert(_el$10, () => clipLabel(h.label));
16451
16926
  effect((_p$) => {
16452
- var _v$1 = theme.accent, _v$10 = TextAttributes9.BOLD, _v$11 = theme.textMuted;
16453
- _v$1 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$1, _p$.e));
16454
- _v$10 !== _p$.t && (_p$.t = setProp(_el$16, "attributes", _v$10, _p$.t));
16455
- _v$11 !== _p$.a && (_p$.a = setProp(_el$19, "fg", _v$11, _p$.a));
16927
+ var _v$3 = theme.accent, _v$4 = TextAttributes9.BOLD, _v$5 = theme.textMuted;
16928
+ _v$3 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$3, _p$.e));
16929
+ _v$4 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$4, _p$.t));
16930
+ _v$5 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$5, _p$.a));
16456
16931
  return _p$;
16457
16932
  }, {
16458
16933
  e: undefined,
16459
16934
  t: undefined,
16460
16935
  a: undefined
16461
16936
  });
16462
- return _el$14;
16937
+ return _el$6;
16463
16938
  })()
16464
16939
  }), null);
16465
16940
  effect((_p$) => {
16466
- var _v$5 = theme.textMuted, _v$6 = TextAttributes9.DIM, _v$7 = props.updateInfo()?.hasUpdate ? theme.warning : theme.textMuted, _v$8 = props.updateInfo()?.hasUpdate ? TextAttributes9.BOLD : TextAttributes9.DIM, _v$9 = theme.textMuted, _v$0 = TextAttributes9.DIM;
16467
- _v$5 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$5, _p$.e));
16468
- _v$6 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$6, _p$.t));
16469
- _v$7 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$7, _p$.a));
16470
- _v$8 !== _p$.o && (_p$.o = setProp(_el$7, "attributes", _v$8, _p$.o));
16471
- _v$9 !== _p$.i && (_p$.i = setProp(_el$12, "fg", _v$9, _p$.i));
16472
- _v$0 !== _p$.n && (_p$.n = setProp(_el$12, "attributes", _v$0, _p$.n));
16941
+ var _v$ = theme.textMuted, _v$2 = TextAttributes9.DIM;
16942
+ _v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
16943
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$2, _p$.t));
16473
16944
  return _p$;
16474
16945
  }, {
16475
16946
  e: undefined,
16476
- t: undefined,
16477
- a: undefined,
16478
- o: undefined,
16479
- i: undefined,
16480
- n: undefined
16947
+ t: undefined
16481
16948
  });
16482
16949
  return _el$3;
16483
16950
  })();
@@ -16606,13 +17073,29 @@ function SettingsPage(props) {
16606
17073
  const {
16607
17074
  theme
16608
17075
  } = themeCtx;
17076
+ let visualPrefsChanged = false;
17077
+ let exiting = false;
16609
17078
  onMount(() => {
16610
17079
  themeCtx.setTransparentBackground(props.transparent);
16611
17080
  if (props.focusAccent)
16612
17081
  themeCtx.setFocusAccent(props.focusAccent);
16613
17082
  });
16614
- function exit() {
16615
- process.exit(0);
17083
+ async function exit() {
17084
+ if (exiting)
17085
+ return;
17086
+ exiting = true;
17087
+ try {
17088
+ const flushed = kv.flush();
17089
+ if (flushed && visualPrefsChanged) {
17090
+ const session = await currentSessionName();
17091
+ if (session)
17092
+ await refreshKobeWorkspacePanes(session);
17093
+ }
17094
+ } catch (err) {
17095
+ console.error("[kobe settings] failed to refresh workspace panes:", err);
17096
+ } finally {
17097
+ process.exit(0);
17098
+ }
16616
17099
  }
16617
17100
  useBindings(() => ({
16618
17101
  enabled: dialog.stack.length === 0,
@@ -16628,15 +17111,23 @@ function SettingsPage(props) {
16628
17111
  }]
16629
17112
  }));
16630
17113
  return (() => {
16631
- var _el$ = createElement("box");
16632
- setProp(_el$, "flexDirection", "column");
17114
+ var _el$ = createElement("scrollbox");
16633
17115
  setProp(_el$, "flexGrow", 1);
16634
17116
  setProp(_el$, "paddingTop", 1);
17117
+ setProp(_el$, "verticalScrollbarOptions", {
17118
+ trackOptions: {
17119
+ foregroundColor: "transparent"
17120
+ }
17121
+ });
16635
17122
  insert(_el$, createComponent2(SettingsDialog, {
16636
17123
  kv,
16637
17124
  get orchestrator() {
16638
17125
  return props.orchestrator ?? undefined;
16639
17126
  },
17127
+ standalone: true,
17128
+ onVisualPrefsChange: () => {
17129
+ visualPrefsChanged = true;
17130
+ },
16640
17131
  onClose: exit
16641
17132
  }));
16642
17133
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
@@ -16719,6 +17210,7 @@ var init_host2 = __esm(() => {
16719
17210
  init_loader();
16720
17211
  init_keymap();
16721
17212
  init_persisted_ui_prefs();
17213
+ init_tmux();
16722
17214
  init_dialog();
16723
17215
  });
16724
17216
 
@@ -16863,7 +17355,7 @@ var exports_host4 = {};
16863
17355
  __export(exports_host4, {
16864
17356
  startUpdateHost: () => startUpdateHost
16865
17357
  });
16866
- import { spawn as spawn4, spawnSync as spawnSync9 } from "child_process";
17358
+ import { spawn as spawn4, spawnSync as spawnSync8 } from "child_process";
16867
17359
  import { TextAttributes as TextAttributes10 } from "@opentui/core";
16868
17360
  function openExternalUrl(url) {
16869
17361
  if (!url)
@@ -16969,7 +17461,7 @@ kobe ${CURRENT_VERSION} -> latest
16969
17461
  process.stdout.write(`running: ${UPDATE_COMMAND}
16970
17462
 
16971
17463
  `);
16972
- const result = spawnSync9("sh", ["-c", UPDATE_COMMAND], {
17464
+ const result = spawnSync8("sh", ["-c", UPDATE_COMMAND], {
16973
17465
  stdio: "inherit"
16974
17466
  });
16975
17467
  const code = result.status ?? (result.error ? 1 : 0);
@@ -17416,6 +17908,63 @@ var init_activity = __esm(() => {
17416
17908
  init_history3();
17417
17909
  });
17418
17910
 
17911
+ // src/tmux/editor-launch.ts
17912
+ function firstToken(cmd) {
17913
+ return cmd.trim().split(/\s+/)[0] ?? "";
17914
+ }
17915
+ function buildEditorCommand(kind, customCommand, absPath, envEditor) {
17916
+ const file = shellQuote(absPath);
17917
+ if (kind === "vim")
17918
+ return { bin: "vim", command: `vim ${file}` };
17919
+ if (kind === "nano")
17920
+ return { bin: "nano", command: `nano ${file}` };
17921
+ const tmpl = (customCommand.trim() || (envEditor ?? "").trim()).trim();
17922
+ if (!tmpl)
17923
+ return null;
17924
+ const bin = firstToken(tmpl);
17925
+ if (!bin)
17926
+ return null;
17927
+ const command = tmpl.includes(FILE_PLACEHOLDER) ? tmpl.split(FILE_PLACEHOLDER).join(file) : `${tmpl} ${file}`;
17928
+ return { bin, command };
17929
+ }
17930
+ function resolveEditorCommand(absPath) {
17931
+ const kind = normalizeEditorKind(getPersistedString(EDITOR_KIND_KEY));
17932
+ const custom = getPersistedString(EDITOR_CUSTOM_KEY) ?? "";
17933
+ const env = process.env.VISUAL ?? process.env.EDITOR ?? "";
17934
+ return buildEditorCommand(kind, custom, absPath, env);
17935
+ }
17936
+ async function binaryAvailable(bin) {
17937
+ try {
17938
+ const proc = Bun.spawn(["sh", "-c", `command -v ${shellQuote(bin)} >/dev/null 2>&1`], {
17939
+ stdin: "ignore",
17940
+ stdout: "ignore",
17941
+ stderr: "ignore"
17942
+ });
17943
+ return await proc.exited === 0;
17944
+ } catch {
17945
+ return false;
17946
+ }
17947
+ }
17948
+ async function openInEditor(session, worktree, absPath) {
17949
+ const resolved = resolveEditorCommand(absPath);
17950
+ if (!resolved)
17951
+ return false;
17952
+ if (!await binaryAvailable(resolved.bin))
17953
+ return false;
17954
+ await newWindow(session, { cwd: worktree, command: resolved.command, name: editorWindowLabel(absPath) });
17955
+ return true;
17956
+ }
17957
+ function editorWindowLabel(absPath) {
17958
+ const base = absPath.slice(absPath.lastIndexOf("/") + 1).trim();
17959
+ return base.length > 0 ? base : "edit";
17960
+ }
17961
+ var FILE_PLACEHOLDER = "{file}";
17962
+ var init_editor_launch = __esm(() => {
17963
+ init_repos();
17964
+ init_editor_prefs();
17965
+ init_client2();
17966
+ });
17967
+
17419
17968
  // src/tui/panes/filetree/git.ts
17420
17969
  import { spawn as nodeSpawn } from "child_process";
17421
17970
  async function runGit(args, cwd) {
@@ -17570,7 +18119,8 @@ var init_git2 = __esm(() => {
17570
18119
  const child = nodeSpawn("git", [...args], {
17571
18120
  cwd,
17572
18121
  shell: false,
17573
- stdio: ["ignore", "pipe", "pipe"]
18122
+ stdio: ["ignore", "pipe", "pipe"],
18123
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
17574
18124
  });
17575
18125
  let stdout = "";
17576
18126
  let stderr = "";
@@ -17621,6 +18171,7 @@ function useFileTreeBindings(opts) {
17621
18171
  opts.setTab(next);
17622
18172
  },
17623
18173
  "files.open": () => opts.openCurrent(),
18174
+ "files.edit": () => opts.editCurrent?.(),
17624
18175
  "files.mention": () => opts.mentionCurrent?.(),
17625
18176
  "files.createPR": () => opts.createPR?.(),
17626
18177
  "files.openExternal": () => opts.openExternal(),
@@ -17715,10 +18266,16 @@ function summarizeGitError(raw) {
17715
18266
  return raw.slice(colon + 2).trim() || "git command failed";
17716
18267
  return raw.trim() || "git command failed";
17717
18268
  }
18269
+ function truncatePathTail2(path11, max) {
18270
+ if (max <= 0 || path11.length <= max)
18271
+ return path11;
18272
+ return `\u2026${path11.slice(path11.length - Math.max(0, max - 1))}`;
18273
+ }
17718
18274
  function FileTree(props) {
17719
18275
  const {
17720
18276
  theme
17721
18277
  } = useTheme();
18278
+ const dims = useTerminalDimensions();
17722
18279
  const focusedAccessor = () => props.focused ? props.focused() : true;
17723
18280
  const [tab, setTab] = createSignal("all");
17724
18281
  const [cursorIndex, setCursorIndex] = createSignal(0);
@@ -17883,6 +18440,11 @@ function FileTree(props) {
17883
18440
  deleted
17884
18441
  };
17885
18442
  });
18443
+ const pathBudget = createMemo(() => {
18444
+ const w = statWidths();
18445
+ const stats = (w.added > 0 ? w.added + 1 : 0) + (w.deleted > 0 ? w.deleted + 1 : 0);
18446
+ return Math.max(8, dims().width - 6 - stats);
18447
+ });
17886
18448
  function moveDown() {
17887
18449
  const r = rows();
17888
18450
  if (r.length === 0)
@@ -17975,6 +18537,16 @@ function FileTree(props) {
17975
18537
  return;
17976
18538
  props.onMention?.(row.path);
17977
18539
  }
18540
+ function editCurrent() {
18541
+ const r = rows();
18542
+ const i = cursorIndex();
18543
+ if (i < 0 || i >= r.length)
18544
+ return;
18545
+ const row = r[i];
18546
+ if (!row || row.kind === "dir")
18547
+ return;
18548
+ props.onEditFile?.(row.path);
18549
+ }
17978
18550
  function refresh() {
17979
18551
  setRefreshTick((n) => n + 1);
17980
18552
  props.onRefresh?.();
@@ -18001,6 +18573,7 @@ function FileTree(props) {
18001
18573
  currentTab: tab,
18002
18574
  openCurrent,
18003
18575
  mentionCurrent,
18576
+ editCurrent,
18004
18577
  createPR: props.onCreatePR,
18005
18578
  openExternal,
18006
18579
  refresh,
@@ -18037,8 +18610,8 @@ function FileTree(props) {
18037
18610
  setProp(_el$, "flexGrow", 1);
18038
18611
  setProp(_el$, "paddingTop", 1);
18039
18612
  setProp(_el$, "paddingBottom", 1);
18040
- setProp(_el$, "paddingLeft", 2);
18041
- setProp(_el$, "paddingRight", 2);
18613
+ setProp(_el$, "paddingLeft", 0);
18614
+ setProp(_el$, "paddingRight", 0);
18042
18615
  insert(_el$, createComponent2(Show, {
18043
18616
  get when() {
18044
18617
  return props.onCreatePR;
@@ -18085,20 +18658,20 @@ function FileTree(props) {
18085
18658
  children: (t) => {
18086
18659
  const isActive = () => tab() === t;
18087
18660
  return (() => {
18088
- var _el$23 = createElement("text");
18089
- setProp(_el$23, "wrapMode", "none");
18090
- setProp(_el$23, "onMouseUp", () => setTab(t));
18091
- insert(_el$23, () => TAB_LABEL[t]);
18661
+ var _el$26 = createElement("text");
18662
+ setProp(_el$26, "wrapMode", "none");
18663
+ setProp(_el$26, "onMouseUp", () => setTab(t));
18664
+ insert(_el$26, () => TAB_LABEL[t]);
18092
18665
  effect((_p$) => {
18093
18666
  var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes11.BOLD : undefined;
18094
- _v$6 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$6, _p$.e));
18095
- _v$7 !== _p$.t && (_p$.t = setProp(_el$23, "attributes", _v$7, _p$.t));
18667
+ _v$6 !== _p$.e && (_p$.e = setProp(_el$26, "fg", _v$6, _p$.e));
18668
+ _v$7 !== _p$.t && (_p$.t = setProp(_el$26, "attributes", _v$7, _p$.t));
18096
18669
  return _p$;
18097
18670
  }, {
18098
18671
  e: undefined,
18099
18672
  t: undefined
18100
18673
  });
18101
- return _el$23;
18674
+ return _el$26;
18102
18675
  })();
18103
18676
  }
18104
18677
  }));
@@ -18107,19 +18680,19 @@ function FileTree(props) {
18107
18680
  return props.cornerBadge?.();
18108
18681
  },
18109
18682
  children: (badge) => (() => {
18110
- var _el$24 = createElement("text");
18111
- setProp(_el$24, "wrapMode", "none");
18112
- insert(_el$24, () => badge().text);
18683
+ var _el$27 = createElement("text");
18684
+ setProp(_el$27, "wrapMode", "none");
18685
+ insert(_el$27, () => badge().text);
18113
18686
  effect((_p$) => {
18114
18687
  var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes11.BOLD : undefined;
18115
- _v$8 !== _p$.e && (_p$.e = setProp(_el$24, "fg", _v$8, _p$.e));
18116
- _v$9 !== _p$.t && (_p$.t = setProp(_el$24, "attributes", _v$9, _p$.t));
18688
+ _v$8 !== _p$.e && (_p$.e = setProp(_el$27, "fg", _v$8, _p$.e));
18689
+ _v$9 !== _p$.t && (_p$.t = setProp(_el$27, "attributes", _v$9, _p$.t));
18117
18690
  return _p$;
18118
18691
  }, {
18119
18692
  e: undefined,
18120
18693
  t: undefined
18121
18694
  });
18122
- return _el$24;
18695
+ return _el$27;
18123
18696
  })()
18124
18697
  }), null);
18125
18698
  insert(_el$, createComponent2(Show, {
@@ -18231,15 +18804,23 @@ function FileTree(props) {
18231
18804
  },
18232
18805
  children: (row, index) => {
18233
18806
  const isCursor = () => index() === cursorIndex();
18807
+ const bar = (() => {
18808
+ var _el$28 = createElement("text");
18809
+ setProp(_el$28, "wrapMode", "none");
18810
+ insert(_el$28, () => isCursor() ? "\u258C" : " ");
18811
+ effect((_$p) => setProp(_el$28, "fg", isCursor() ? theme.focusAccent : undefined, _$p));
18812
+ return _el$28;
18813
+ })();
18814
+ const rowBg = () => isCursor() ? theme.backgroundElement : undefined;
18234
18815
  if (row.kind === "dir") {
18235
18816
  const indent = " ".repeat(row.depth);
18236
18817
  const marker = row.expanded ? "\u25BE" : "\u25B8";
18237
18818
  return (() => {
18238
- var _el$25 = createElement("box"), _el$26 = createElement("text");
18239
- insertNode(_el$25, _el$26);
18240
- setProp(_el$25, "paddingLeft", 1);
18241
- setProp(_el$25, "paddingRight", 1);
18242
- setProp(_el$25, "onMouseUp", () => {
18819
+ var _el$29 = createElement("box"), _el$30 = createElement("box"), _el$31 = createElement("text");
18820
+ insertNode(_el$29, _el$30);
18821
+ setProp(_el$29, "flexDirection", "row");
18822
+ setProp(_el$29, "gap", 0);
18823
+ setProp(_el$29, "onMouseUp", () => {
18243
18824
  setCursorIndex(index());
18244
18825
  setExpandedDirs((prev) => {
18245
18826
  const next = new Set(prev);
@@ -18250,45 +18831,53 @@ function FileTree(props) {
18250
18831
  return next;
18251
18832
  });
18252
18833
  });
18253
- setProp(_el$26, "wrapMode", "none");
18254
- insert(_el$26, () => `${indent}${marker} ${row.name}/`);
18834
+ insert(_el$29, bar, _el$30);
18835
+ insertNode(_el$30, _el$31);
18836
+ setProp(_el$30, "flexGrow", 1);
18837
+ setProp(_el$30, "paddingRight", 1);
18838
+ setProp(_el$31, "wrapMode", "none");
18839
+ insert(_el$31, () => `${indent}${marker} ${row.name}/`);
18255
18840
  effect((_p$) => {
18256
- var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes11.BOLD;
18257
- _v$0 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$0, _p$.e));
18258
- _v$1 !== _p$.t && (_p$.t = setProp(_el$26, "fg", _v$1, _p$.t));
18259
- _v$10 !== _p$.a && (_p$.a = setProp(_el$26, "attributes", _v$10, _p$.a));
18841
+ var _v$0 = rowBg(), _v$1 = theme.textMuted, _v$10 = TextAttributes11.BOLD;
18842
+ _v$0 !== _p$.e && (_p$.e = setProp(_el$29, "backgroundColor", _v$0, _p$.e));
18843
+ _v$1 !== _p$.t && (_p$.t = setProp(_el$31, "fg", _v$1, _p$.t));
18844
+ _v$10 !== _p$.a && (_p$.a = setProp(_el$31, "attributes", _v$10, _p$.a));
18260
18845
  return _p$;
18261
18846
  }, {
18262
18847
  e: undefined,
18263
18848
  t: undefined,
18264
18849
  a: undefined
18265
18850
  });
18266
- return _el$25;
18851
+ return _el$29;
18267
18852
  })();
18268
18853
  }
18269
18854
  if (row.kind === "file") {
18270
18855
  const indent = " ".repeat(row.depth);
18271
18856
  return (() => {
18272
- var _el$27 = createElement("box"), _el$28 = createElement("text");
18273
- insertNode(_el$27, _el$28);
18274
- setProp(_el$27, "paddingLeft", 1);
18275
- setProp(_el$27, "paddingRight", 1);
18276
- setProp(_el$27, "onMouseUp", () => {
18857
+ var _el$32 = createElement("box"), _el$33 = createElement("box"), _el$34 = createElement("text");
18858
+ insertNode(_el$32, _el$33);
18859
+ setProp(_el$32, "flexDirection", "row");
18860
+ setProp(_el$32, "gap", 0);
18861
+ setProp(_el$32, "onMouseUp", () => {
18277
18862
  setCursorIndex(index());
18278
18863
  props.onOpenFile(row.path);
18279
18864
  });
18280
- setProp(_el$28, "wrapMode", "none");
18281
- insert(_el$28, () => `${indent} ${row.name}`);
18865
+ insert(_el$32, bar, _el$33);
18866
+ insertNode(_el$33, _el$34);
18867
+ setProp(_el$33, "flexGrow", 1);
18868
+ setProp(_el$33, "paddingRight", 1);
18869
+ setProp(_el$34, "wrapMode", "none");
18870
+ insert(_el$34, () => `${indent} ${row.name}`);
18282
18871
  effect((_p$) => {
18283
- var _v$11 = isCursor() ? theme.primary : undefined, _v$12 = isCursor() ? theme.selectedListItemText : theme.text;
18284
- _v$11 !== _p$.e && (_p$.e = setProp(_el$27, "backgroundColor", _v$11, _p$.e));
18285
- _v$12 !== _p$.t && (_p$.t = setProp(_el$28, "fg", _v$12, _p$.t));
18872
+ var _v$11 = rowBg(), _v$12 = theme.text;
18873
+ _v$11 !== _p$.e && (_p$.e = setProp(_el$32, "backgroundColor", _v$11, _p$.e));
18874
+ _v$12 !== _p$.t && (_p$.t = setProp(_el$34, "fg", _v$12, _p$.t));
18286
18875
  return _p$;
18287
18876
  }, {
18288
18877
  e: undefined,
18289
18878
  t: undefined
18290
18879
  });
18291
- return _el$27;
18880
+ return _el$32;
18292
18881
  })();
18293
18882
  }
18294
18883
  const tone = statusToken(row.status);
@@ -18310,64 +18899,84 @@ function FileTree(props) {
18310
18899
  const addedText = row.added == null ? " ".repeat(w.added) : `+${row.added}`.padStart(w.added);
18311
18900
  const deletedText = row.deleted == null ? " ".repeat(w.deleted) : `-${row.deleted}`.padStart(w.deleted);
18312
18901
  return (() => {
18313
- var _el$29 = createElement("box"), _el$30 = createElement("text"), _el$31 = createElement("text");
18314
- insertNode(_el$29, _el$30);
18315
- insertNode(_el$29, _el$31);
18316
- setProp(_el$29, "flexDirection", "row");
18317
- setProp(_el$29, "gap", 1);
18318
- setProp(_el$29, "paddingLeft", 1);
18319
- setProp(_el$29, "paddingRight", 1);
18320
- setProp(_el$29, "onMouseUp", () => {
18902
+ var _el$35 = createElement("box"), _el$36 = createElement("box"), _el$37 = createElement("text"), _el$38 = createElement("text");
18903
+ insertNode(_el$35, _el$36);
18904
+ setProp(_el$35, "flexDirection", "row");
18905
+ setProp(_el$35, "gap", 0);
18906
+ setProp(_el$35, "onMouseUp", () => {
18321
18907
  setCursorIndex(index());
18322
18908
  props.onOpenFile(row.path);
18323
18909
  });
18324
- setProp(_el$30, "wrapMode", "none");
18325
- insert(_el$30, () => row.status);
18326
- setProp(_el$31, "wrapMode", "none");
18327
- setProp(_el$31, "flexGrow", 1);
18328
- insert(_el$31, () => row.path);
18329
- insert(_el$29, createComponent2(Show, {
18910
+ insert(_el$35, bar, _el$36);
18911
+ insertNode(_el$36, _el$37);
18912
+ insertNode(_el$36, _el$38);
18913
+ setProp(_el$36, "flexDirection", "row");
18914
+ setProp(_el$36, "flexGrow", 1);
18915
+ setProp(_el$36, "gap", 1);
18916
+ setProp(_el$36, "paddingRight", 1);
18917
+ setProp(_el$37, "wrapMode", "none");
18918
+ insert(_el$37, () => row.status);
18919
+ setProp(_el$38, "wrapMode", "none");
18920
+ setProp(_el$38, "flexGrow", 1);
18921
+ insert(_el$38, () => truncatePathTail2(row.path, pathBudget()));
18922
+ insert(_el$36, createComponent2(Show, {
18330
18923
  get when() {
18331
18924
  return w.added > 0;
18332
18925
  },
18333
18926
  get children() {
18334
- var _el$32 = createElement("text");
18335
- setProp(_el$32, "wrapMode", "none");
18336
- insert(_el$32, addedText);
18337
- effect((_$p) => setProp(_el$32, "fg", isCursor() ? theme.selectedListItemText : theme.success, _$p));
18338
- return _el$32;
18927
+ var _el$39 = createElement("text");
18928
+ setProp(_el$39, "wrapMode", "none");
18929
+ insert(_el$39, addedText);
18930
+ effect((_$p) => setProp(_el$39, "fg", theme.success, _$p));
18931
+ return _el$39;
18339
18932
  }
18340
18933
  }), null);
18341
- insert(_el$29, createComponent2(Show, {
18934
+ insert(_el$36, createComponent2(Show, {
18342
18935
  get when() {
18343
18936
  return w.deleted > 0;
18344
18937
  },
18345
18938
  get children() {
18346
- var _el$33 = createElement("text");
18347
- setProp(_el$33, "wrapMode", "none");
18348
- insert(_el$33, deletedText);
18349
- effect((_$p) => setProp(_el$33, "fg", isCursor() ? theme.selectedListItemText : theme.error, _$p));
18350
- return _el$33;
18939
+ var _el$40 = createElement("text");
18940
+ setProp(_el$40, "wrapMode", "none");
18941
+ insert(_el$40, deletedText);
18942
+ effect((_$p) => setProp(_el$40, "fg", theme.error, _$p));
18943
+ return _el$40;
18351
18944
  }
18352
18945
  }), null);
18353
18946
  effect((_p$) => {
18354
- var _v$13 = isCursor() ? theme.primary : undefined, _v$14 = isCursor() ? theme.selectedListItemText : statusColor(), _v$15 = isCursor() ? theme.selectedListItemText : theme.text;
18355
- _v$13 !== _p$.e && (_p$.e = setProp(_el$29, "backgroundColor", _v$13, _p$.e));
18356
- _v$14 !== _p$.t && (_p$.t = setProp(_el$30, "fg", _v$14, _p$.t));
18357
- _v$15 !== _p$.a && (_p$.a = setProp(_el$31, "fg", _v$15, _p$.a));
18947
+ var _v$13 = rowBg(), _v$14 = statusColor(), _v$15 = theme.text;
18948
+ _v$13 !== _p$.e && (_p$.e = setProp(_el$35, "backgroundColor", _v$13, _p$.e));
18949
+ _v$14 !== _p$.t && (_p$.t = setProp(_el$37, "fg", _v$14, _p$.t));
18950
+ _v$15 !== _p$.a && (_p$.a = setProp(_el$38, "fg", _v$15, _p$.a));
18358
18951
  return _p$;
18359
18952
  }, {
18360
18953
  e: undefined,
18361
18954
  t: undefined,
18362
18955
  a: undefined
18363
18956
  });
18364
- return _el$29;
18957
+ return _el$35;
18365
18958
  })();
18366
18959
  }
18367
18960
  }));
18368
18961
  return _el$22;
18369
18962
  }
18370
18963
  }), null);
18964
+ insert(_el$, createComponent2(Show, {
18965
+ get when() {
18966
+ return props.worktreePath() != null;
18967
+ },
18968
+ get children() {
18969
+ var _el$23 = createElement("box"), _el$24 = createElement("text");
18970
+ insertNode(_el$23, _el$24);
18971
+ setProp(_el$23, "flexDirection", "row");
18972
+ setProp(_el$23, "paddingTop", 1);
18973
+ setProp(_el$23, "flexShrink", 0);
18974
+ insertNode(_el$24, createTextNode(`\u21B5 preview \xB7 e edit`));
18975
+ setProp(_el$24, "wrapMode", "none");
18976
+ effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
18977
+ return _el$23;
18978
+ }
18979
+ }), null);
18371
18980
  return _el$;
18372
18981
  })();
18373
18982
  }
@@ -18382,6 +18991,7 @@ var init_FileTree = __esm(() => {
18382
18991
  init_solid();
18383
18992
  init_solid();
18384
18993
  init_solid();
18994
+ init_solid();
18385
18995
  init_dev();
18386
18996
  init_theme2();
18387
18997
  init_git2();
@@ -18402,15 +19012,16 @@ var init_filetree = __esm(() => {
18402
19012
  });
18403
19013
 
18404
19014
  // src/tui/ops/pr-prompt.ts
18405
- import { spawnSync as spawnSync10 } from "child_process";
19015
+ import { spawnSync as spawnSync9 } from "child_process";
18406
19016
  import { promises as fs4 } from "fs";
18407
19017
  import path11 from "path";
18408
19018
  function git2(cwd, args) {
18409
19019
  try {
18410
- const out = spawnSync10("git", args.slice(), {
19020
+ const out = spawnSync9("git", args.slice(), {
18411
19021
  cwd,
18412
19022
  encoding: "utf8",
18413
- timeout: GIT_TIMEOUT_MS
19023
+ timeout: GIT_TIMEOUT_MS,
19024
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
18414
19025
  });
18415
19026
  if (out.error)
18416
19027
  return null;
@@ -18611,6 +19222,17 @@ function OpsShell(props) {
18611
19222
  name: basename5(rel)
18612
19223
  });
18613
19224
  }
19225
+ function openEditor(rel) {
19226
+ if (!props.taskId) {
19227
+ openPreview(rel);
19228
+ return;
19229
+ }
19230
+ const abs = `${props.worktree}/${rel}`;
19231
+ openInEditor(tmuxSessionName(props.taskId), props.worktree, abs).then((launched) => {
19232
+ if (!launched)
19233
+ openPreview(rel);
19234
+ });
19235
+ }
18614
19236
  function injectMention(rel) {
18615
19237
  if (!props.targetPane)
18616
19238
  return;
@@ -18631,6 +19253,7 @@ function OpsShell(props) {
18631
19253
  worktreePath: () => props.worktree,
18632
19254
  focused: () => true,
18633
19255
  onOpenFile: openPreview,
19256
+ onEditFile: openEditor,
18634
19257
  onMention: injectMention,
18635
19258
  onCreatePR: () => void createPR(),
18636
19259
  cornerBadge,
@@ -18706,7 +19329,11 @@ async function gitDiff(worktree, relPath) {
18706
19329
  cwd: worktree,
18707
19330
  stdin: "ignore",
18708
19331
  stdout: "pipe",
18709
- stderr: "ignore"
19332
+ stderr: "ignore",
19333
+ env: {
19334
+ ...process.env,
19335
+ GIT_OPTIONAL_LOCKS: "0"
19336
+ }
18710
19337
  });
18711
19338
  const text = await new Response(proc.stdout).text();
18712
19339
  await proc.exited;
@@ -18933,6 +19560,7 @@ var init_host5 = __esm(() => {
18933
19560
  init_turn_detector();
18934
19561
  init_activity();
18935
19562
  init_client2();
19563
+ init_editor_launch();
18936
19564
  init_solid();
18937
19565
  init_dev();
18938
19566
  init_theme2();
@@ -19005,7 +19633,7 @@ async function startDirectTmux() {
19005
19633
  return;
19006
19634
  }
19007
19635
  const client = await connectOrStartDaemon();
19008
- const orchestrator = new RemoteOrchestrator(client);
19636
+ const orchestrator = new RemoteOrchestrator(client, { role: "gui" });
19009
19637
  try {
19010
19638
  process.env.KOBE_DAEMON_SOCKET_PATH = client.socketPath;
19011
19639
  await orchestrator.init();
@@ -19848,7 +20476,7 @@ function repoBasename2(repo) {
19848
20476
  }
19849
20477
 
19850
20478
  // src/tui/component/top-bar.tsx
19851
- import { spawnSync as spawnSync11 } from "child_process";
20479
+ import { spawnSync as spawnSync10 } from "child_process";
19852
20480
  import { TextAttributes as TextAttributes16 } from "@opentui/core";
19853
20481
  function TopBar(props) {
19854
20482
  const {
@@ -19877,7 +20505,7 @@ function TopBar(props) {
19877
20505
  `);
19878
20506
  process.stderr.write(`running: ${UPDATE_COMMAND}
19879
20507
  `);
19880
- const result = spawnSync11("sh", ["-c", UPDATE_COMMAND], {
20508
+ const result = spawnSync10("sh", ["-c", UPDATE_COMMAND], {
19881
20509
  stdio: "inherit"
19882
20510
  });
19883
20511
  if (result.error) {
@@ -21229,7 +21857,9 @@ async function startApp() {
21229
21857
  });
21230
21858
  } else {
21231
21859
  const client = await connectOrStartDaemon();
21232
- orchestrator = new RemoteOrchestrator(client);
21860
+ orchestrator = new RemoteOrchestrator(client, {
21861
+ role: "gui"
21862
+ });
21233
21863
  process.env.KOBE_DAEMON_SOCKET_PATH = client.socketPath;
21234
21864
  await orchestrator.init();
21235
21865
  }