@sma1lboy/kobe 0.7.0 → 0.7.1

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 +274 -165
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -70,7 +70,7 @@ 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.7.0",
73
+ version: "0.7.1",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
@@ -4288,6 +4288,90 @@ var init_auto_title = __esm(() => {
4288
4288
  init_history3();
4289
4289
  });
4290
4290
 
4291
+ // src/cli/invocation.ts
4292
+ import { fileURLToPath } from "url";
4293
+ function kobeCliInvocation() {
4294
+ const isBuilt = import.meta.url.endsWith(".js");
4295
+ if (isBuilt)
4296
+ return ["kobe"];
4297
+ const entry = fileURLToPath(new URL("./index.ts", import.meta.url));
4298
+ const preload = fileURLToPath(import.meta.resolve("@opentui/solid/preload"));
4299
+ return [process.execPath, "--preload", preload, "--conditions=browser", entry];
4300
+ }
4301
+ var init_invocation = () => {};
4302
+
4303
+ // src/tmux/session-layout.ts
4304
+ function shellQuote(s) {
4305
+ return `'${s.replace(/'/g, "'\\''")}'`;
4306
+ }
4307
+ function shellQuoteArgv(argv) {
4308
+ return argv.map(shellQuote).join(" ");
4309
+ }
4310
+ function keepAlive(cmd) {
4311
+ return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
4312
+ }
4313
+ function shQuote(s) {
4314
+ return `'${s.replace(/'/g, "'\\''")}'`;
4315
+ }
4316
+ function homeWelcomeCommand() {
4317
+ const msg = "\\n No task selected\\n\\n Press N to create a task, or pick one on the left.\\n\\n";
4318
+ return `clear; printf ${shQuote(msg)}; exec "\${SHELL:-/bin/sh}"`;
4319
+ }
4320
+ function engineLaunchLine(engineCmd, init) {
4321
+ const tail = keepAlive(engineCmd);
4322
+ const script = init?.initScript?.trim();
4323
+ if (!script)
4324
+ return tail;
4325
+ const group = ["{", script, "}"].join(`
4326
+ `);
4327
+ if (init?.markerPath) {
4328
+ const marker = shQuote(init.markerPath);
4329
+ const markerDir = shQuote(markerDirOf(init.markerPath));
4330
+ return [
4331
+ `if [ ! -f ${marker} ]; then`,
4332
+ group,
4333
+ `if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
4334
+ "fi",
4335
+ tail
4336
+ ].join(`
4337
+ `);
4338
+ }
4339
+ return [group, tail].join(`
4340
+ `);
4341
+ }
4342
+ function markerDirOf(p) {
4343
+ const i = p.lastIndexOf("/");
4344
+ return i <= 0 ? "." : p.slice(0, i);
4345
+ }
4346
+ function fallbackOpsScript(cwd) {
4347
+ return `cd ${shellQuote(cwd)} && while :; do clear; printf "\\033[1m# %s\\033[0m\\n\\n" ${shellQuote(cwd)}; git status --short --branch 2>/dev/null | sed 's/^/ /' || true; printf "\\n"; if command -v lsd >/dev/null 2>&1; then lsd --tree --git -I node_modules -I .git --depth 2 .; elif command -v eza >/dev/null 2>&1; then eza --tree --git -L 2 -I 'node_modules|.git' .; elif command -v tree >/dev/null 2>&1; then tree -L 2 -I 'node_modules|.git'; else ls -la; fi; sleep 2; done`;
4348
+ }
4349
+ function previewWindowCommand(args) {
4350
+ const wt = shellQuote(args.worktree);
4351
+ const file = shellQuote(args.relPath);
4352
+ const inv = args.cliInvocation.map(shellQuote).join(" ");
4353
+ const fallback = `cd ${wt} && if ! git diff --quiet HEAD -- ${file} 2>/dev/null; then ` + `git diff HEAD -- ${file} | { delta --paging=always 2>/dev/null || less -R; }; ` + `else bat --style=plain --paging=always ${file} 2>/dev/null || \${PAGER:-less} ${file} 2>/dev/null || cat ${file}; fi`;
4354
+ return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
4355
+ }
4356
+ function updatePageCommand(args) {
4357
+ return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
4358
+ }
4359
+ function tasksPaneCommand(cliInvocation, opts = {}) {
4360
+ const argv = [...cliInvocation, "tasks"];
4361
+ if (opts.initialTaskId)
4362
+ argv.push("--initial-task-id", opts.initialTaskId);
4363
+ return shellQuoteArgv(argv);
4364
+ }
4365
+ function opsPaneCommand(args) {
4366
+ if (args.taskId && args.claudePaneId) {
4367
+ const inv = args.cliInvocation.map(shellQuote).join(" ");
4368
+ const vendorFlag = args.vendor ? ` --vendor ${shellQuote(args.vendor)}` : "";
4369
+ return `KOBE_FILETREE_WATCH=1 ${inv} ops --task-id ${shellQuote(args.taskId)} --worktree ${shellQuote(args.cwd)} ` + `--target-pane ${shellQuote(args.claudePaneId)}${vendorFlag} || { ${fallbackOpsScript(args.cwd)}; }`;
4370
+ }
4371
+ return fallbackOpsScript(args.cwd);
4372
+ }
4373
+ var TASKS_PANE_WIDTH = 32, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50;
4374
+
4291
4375
  // src/tmux/client.ts
4292
4376
  var exports_client = {};
4293
4377
  __export(exports_client, {
@@ -4325,6 +4409,7 @@ __export(exports_client, {
4325
4409
  CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION,
4326
4410
  CHAT_TAB_SESSION_ID_OPTION: () => CHAT_TAB_SESSION_ID_OPTION
4327
4411
  });
4412
+ import { homedir as homedir6 } from "os";
4328
4413
  function tmuxArgs(...args) {
4329
4414
  return ["tmux", "-L", KOBE_TMUX_SOCKET, ...args];
4330
4415
  }
@@ -4344,16 +4429,34 @@ async function drainText(stream) {
4344
4429
  }
4345
4430
  }
4346
4431
  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()}`);
4432
+ try {
4433
+ const proc = Bun.spawn(tmuxArgs(...args), {
4434
+ stdin: "ignore",
4435
+ cwd: SAFE_SPAWN_CWD,
4436
+ stdout: "ignore",
4437
+ stderr: "pipe"
4438
+ });
4439
+ const [errText, code] = await Promise.all([drainText(proc.stderr), proc.exited]);
4440
+ if (code !== 0 && errText.trim().length > 0) {
4441
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4442
+ }
4443
+ return code;
4444
+ } catch {
4445
+ return 1;
4351
4446
  }
4352
- return code;
4353
4447
  }
4354
4448
  async function runTmuxQuiet(args) {
4355
- const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4356
- return proc.exited;
4449
+ try {
4450
+ const proc = Bun.spawn(tmuxArgs(...args), {
4451
+ stdin: "ignore",
4452
+ cwd: SAFE_SPAWN_CWD,
4453
+ stdout: "ignore",
4454
+ stderr: "ignore"
4455
+ });
4456
+ return await proc.exited;
4457
+ } catch {
4458
+ return 1;
4459
+ }
4357
4460
  }
4358
4461
  function tmuxCommandSequence(commands) {
4359
4462
  const out = [];
@@ -4371,12 +4474,16 @@ async function runTmuxSequence(commands) {
4371
4474
  return args.length === 0 ? 0 : runTmux(args);
4372
4475
  }
4373
4476
  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()}`);
4477
+ try {
4478
+ const proc = Bun.spawn(tmuxArgs(...args), { stdin: "ignore", cwd: SAFE_SPAWN_CWD, stdout: "pipe", stderr: "pipe" });
4479
+ const [stdout, errText, code] = await Promise.all([drainText(proc.stdout), drainText(proc.stderr), proc.exited]);
4480
+ if (code !== 0 && errText.trim().length > 0) {
4481
+ console.error(`[kobe tmux] ${args.join(" ")} (${code}): ${errText.trim()}`);
4482
+ }
4483
+ return { code, stdout };
4484
+ } catch {
4485
+ return { code: 1, stdout: "" };
4378
4486
  }
4379
- return { code, stdout };
4380
4487
  }
4381
4488
  async function runTmuxSequenceCapturing(commands) {
4382
4489
  const args = tmuxCommandSequence(commands);
@@ -4384,7 +4491,7 @@ async function runTmuxSequenceCapturing(commands) {
4384
4491
  }
4385
4492
  async function tmuxAvailable() {
4386
4493
  try {
4387
- const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
4494
+ const proc = Bun.spawn(["tmux", "-V"], { stdin: "ignore", cwd: SAFE_SPAWN_CWD, stdout: "ignore", stderr: "ignore" });
4388
4495
  return await proc.exited === 0;
4389
4496
  } catch {
4390
4497
  return false;
@@ -4499,21 +4606,53 @@ async function killSession(name) {
4499
4606
  }
4500
4607
  async function ensureFallbackSession() {
4501
4608
  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",
4609
+ if (await sessionExists(name)) {
4610
+ if (await getSessionOption(name, HOME_KIND_OPTION) === "tasks")
4611
+ return name;
4612
+ await runTmux(["kill-session", "-t", `=${name}`]);
4613
+ }
4614
+ const r0 = await runTmuxCapturing([
4615
+ "new-session",
4616
+ "-d",
4617
+ "-s",
4618
+ name,
4619
+ "-c",
4620
+ SAFE_SPAWN_CWD,
4621
+ "-x",
4622
+ "220",
4623
+ "-y",
4624
+ "50",
4625
+ "-P",
4626
+ "-F",
4627
+ "#{pane_id}",
4628
+ homeWelcomeCommand()
4629
+ ]);
4630
+ const mainPane = r0.stdout.trim();
4631
+ if (mainPane) {
4632
+ const r1 = await runTmuxCapturing([
4633
+ "split-window",
4634
+ "-h",
4635
+ "-b",
4636
+ "-t",
4637
+ mainPane,
4638
+ "-l",
4639
+ `${TASKS_PANE_WIDTH}`,
4513
4640
  "-c",
4514
- 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
4641
+ SAFE_SPAWN_CWD,
4642
+ "-P",
4643
+ "-F",
4644
+ "#{pane_id}",
4645
+ keepAlive(tasksPaneCommand(kobeCliInvocation()))
4515
4646
  ]);
4647
+ const tasksPane = r1.stdout.trim();
4648
+ if (tasksPane) {
4649
+ await runTmuxSequence([
4650
+ ["set-option", "-p", "-t", tasksPane, PANE_ROLE_OPTION, "tasks"],
4651
+ ["select-pane", "-t", tasksPane]
4652
+ ]);
4653
+ }
4516
4654
  }
4655
+ await setSessionOption(name, HOME_KIND_OPTION, "tasks");
4517
4656
  return name;
4518
4657
  }
4519
4658
  async function switchClientBeforeKill(killedName, nextSessionName) {
@@ -4527,9 +4666,11 @@ async function switchClientBeforeKill(killedName, nextSessionName) {
4527
4666
  const fallback = await ensureFallbackSession();
4528
4667
  await runTmux(["switch-client", "-t", `=${fallback}`]);
4529
4668
  }
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";
4669
+ var KOBE_TMUX_SOCKET, SAFE_SPAWN_CWD, 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", HOME_KIND_OPTION = "@kobe_home";
4531
4670
  var init_client2 = __esm(() => {
4671
+ init_invocation();
4532
4672
  KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
4673
+ SAFE_SPAWN_CWD = homedir6() || "/";
4533
4674
  CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
4534
4675
  });
4535
4676
 
@@ -4704,7 +4845,7 @@ class DaemonEventBus {
4704
4845
 
4705
4846
  // src/daemon/paths.ts
4706
4847
  import { createHash as createHash2 } from "crypto";
4707
- import { homedir as homedir6, tmpdir } from "os";
4848
+ import { homedir as homedir7, tmpdir } from "os";
4708
4849
  import { join as join3 } from "path";
4709
4850
  function shortHomeTag(homeDir2) {
4710
4851
  return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
@@ -4731,16 +4872,16 @@ function defaultDaemonSocketPath(homeDir2) {
4731
4872
  if (runtimeDir && runtimeDir.length > 0) {
4732
4873
  return fitSocketPath(join3(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
4733
4874
  }
4734
- const home = homedir6();
4875
+ const home = homedir7();
4735
4876
  return fitSocketPath(join3(home, ".kobe", "daemon.sock"), home, "daemon");
4736
4877
  }
4737
- function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4878
+ function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir7()) {
4738
4879
  const override = process.env.KOBE_DAEMON_PID_PATH;
4739
4880
  if (override && override.length > 0)
4740
4881
  return override;
4741
4882
  return join3(homeDir2, ".kobe", "daemon.pid");
4742
4883
  }
4743
- function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir6()) {
4884
+ function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir7()) {
4744
4885
  return join3(homeDir2, ".kobe", "daemon.log");
4745
4886
  }
4746
4887
  var SOCKET_PATH_SAFETY_LIMIT = 100;
@@ -5187,7 +5328,7 @@ __export(exports_daemon_process, {
5187
5328
  import { spawn } from "child_process";
5188
5329
  import { closeSync, existsSync, mkdirSync as mkdirSync2, openSync } from "fs";
5189
5330
  import { dirname as dirname4, resolve as resolve2 } from "path";
5190
- import { fileURLToPath } from "url";
5331
+ import { fileURLToPath as fileURLToPath2 } from "url";
5191
5332
  function spawnDetachedDaemon(command, args, env, logPath) {
5192
5333
  let stdio = "ignore";
5193
5334
  let logFd;
@@ -5255,7 +5396,7 @@ async function testDaemonResponds(socketPath, timeoutMs = DAEMON_HELLO_TIMEOUT_M
5255
5396
  return alive;
5256
5397
  }
5257
5398
  function resolveKobeSpawn(subcommand) {
5258
- const here = fileURLToPath(import.meta.url);
5399
+ const here = fileURLToPath2(import.meta.url);
5259
5400
  if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
5260
5401
  return [process.execPath, ...subcommand];
5261
5402
  }
@@ -5513,83 +5654,6 @@ var init_prompt_delivery = __esm(() => {
5513
5654
  init_client2();
5514
5655
  });
5515
5656
 
5516
- // src/cli/invocation.ts
5517
- import { fileURLToPath as fileURLToPath2 } from "url";
5518
- function kobeCliInvocation() {
5519
- const isBuilt = import.meta.url.endsWith(".js");
5520
- if (isBuilt)
5521
- return ["kobe"];
5522
- const entry = fileURLToPath2(new URL("./index.ts", import.meta.url));
5523
- const preload = fileURLToPath2(import.meta.resolve("@opentui/solid/preload"));
5524
- return [process.execPath, "--preload", preload, "--conditions=browser", entry];
5525
- }
5526
- var init_invocation = () => {};
5527
-
5528
- // src/tmux/session-layout.ts
5529
- function shellQuote(s) {
5530
- return `'${s.replace(/'/g, "'\\''")}'`;
5531
- }
5532
- function shellQuoteArgv(argv) {
5533
- return argv.map(shellQuote).join(" ");
5534
- }
5535
- function keepAlive(cmd) {
5536
- return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
5537
- }
5538
- function shQuote(s) {
5539
- return `'${s.replace(/'/g, "'\\''")}'`;
5540
- }
5541
- function engineLaunchLine(engineCmd, init) {
5542
- const tail = keepAlive(engineCmd);
5543
- const script = init?.initScript?.trim();
5544
- if (!script)
5545
- return tail;
5546
- const group = ["{", script, "}"].join(`
5547
- `);
5548
- if (init?.markerPath) {
5549
- const marker = shQuote(init.markerPath);
5550
- const markerDir = shQuote(markerDirOf(init.markerPath));
5551
- return [
5552
- `if [ ! -f ${marker} ]; then`,
5553
- group,
5554
- `if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
5555
- "fi",
5556
- tail
5557
- ].join(`
5558
- `);
5559
- }
5560
- return [group, tail].join(`
5561
- `);
5562
- }
5563
- function markerDirOf(p) {
5564
- const i = p.lastIndexOf("/");
5565
- return i <= 0 ? "." : p.slice(0, i);
5566
- }
5567
- function fallbackOpsScript(cwd) {
5568
- return `cd ${shellQuote(cwd)} && while :; do clear; printf "\\033[1m# %s\\033[0m\\n\\n" ${shellQuote(cwd)}; git status --short --branch 2>/dev/null | sed 's/^/ /' || true; printf "\\n"; if command -v lsd >/dev/null 2>&1; then lsd --tree --git -I node_modules -I .git --depth 2 .; elif command -v eza >/dev/null 2>&1; then eza --tree --git -L 2 -I 'node_modules|.git' .; elif command -v tree >/dev/null 2>&1; then tree -L 2 -I 'node_modules|.git'; else ls -la; fi; sleep 2; done`;
5569
- }
5570
- function previewWindowCommand(args) {
5571
- const wt = shellQuote(args.worktree);
5572
- const file = shellQuote(args.relPath);
5573
- const inv = args.cliInvocation.map(shellQuote).join(" ");
5574
- const fallback = `cd ${wt} && if ! git diff --quiet HEAD -- ${file} 2>/dev/null; then ` + `git diff HEAD -- ${file} | { delta --paging=always 2>/dev/null || less -R; }; ` + `else bat --style=plain --paging=always ${file} 2>/dev/null || \${PAGER:-less} ${file} 2>/dev/null || cat ${file}; fi`;
5575
- return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
5576
- }
5577
- function updatePageCommand(args) {
5578
- return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
5579
- }
5580
- function tasksPaneCommand(cliInvocation) {
5581
- return shellQuoteArgv([...cliInvocation, "tasks"]);
5582
- }
5583
- function opsPaneCommand(args) {
5584
- if (args.taskId && args.claudePaneId) {
5585
- const inv = args.cliInvocation.map(shellQuote).join(" ");
5586
- const vendorFlag = args.vendor ? ` --vendor ${shellQuote(args.vendor)}` : "";
5587
- return `KOBE_FILETREE_WATCH=1 ${inv} ops --task-id ${shellQuote(args.taskId)} --worktree ${shellQuote(args.cwd)} ` + `--target-pane ${shellQuote(args.claudePaneId)}${vendorFlag} || { ${fallbackOpsScript(args.cwd)}; }`;
5588
- }
5589
- return fallbackOpsScript(args.cwd);
5590
- }
5591
- var TASKS_PANE_WIDTH = 32, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50;
5592
-
5593
5657
  // src/tui/panes/terminal/tmux.ts
5594
5658
  var exports_tmux = {};
5595
5659
  __export(exports_tmux, {
@@ -5712,6 +5776,7 @@ async function ensureSessionImpl(opts) {
5712
5776
  const chooseEngineTmuxCommand = `run-shell ${shellQuote(chooseEngineCommand)}`;
5713
5777
  await runTmuxSequence([
5714
5778
  ["set-option", "-g", "status", "on"],
5779
+ ["set-window-option", "-g", "aggressive-resize", "on"],
5715
5780
  ["set-option", "-g", "monitor-activity", "on"],
5716
5781
  ["set-option", "-g", "visual-activity", "off"],
5717
5782
  ["set-option", "-g", "window-status-format", CHAT_TAB_STATUS_FORMAT],
@@ -5811,7 +5876,15 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
5811
5876
  const tasksPane = panes.find((pane) => pane.role === "tasks");
5812
5877
  const opsPane = panes.find((pane) => pane.role === "ops");
5813
5878
  if (tasksPane && tasksPane.version !== CURRENT_VERSION) {
5814
- 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]);
5879
+ commands.push([
5880
+ "respawn-pane",
5881
+ "-k",
5882
+ "-t",
5883
+ tasksPane.paneId,
5884
+ "-c",
5885
+ cwd,
5886
+ keepAlive(envPrefix + tasksPaneCommand(inv, { initialTaskId: taskId }))
5887
+ ], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5815
5888
  }
5816
5889
  if (opsPane && claudePane && opsPane.version !== CURRENT_VERSION) {
5817
5890
  commands.push([
@@ -5863,7 +5936,15 @@ async function refreshKobeWorkspacePanes(session) {
5863
5936
  const tasksPane = panes.find((pane) => pane.role === "tasks");
5864
5937
  const opsPane = panes.find((pane) => pane.role === "ops");
5865
5938
  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]);
5939
+ commands.push([
5940
+ "respawn-pane",
5941
+ "-k",
5942
+ "-t",
5943
+ tasksPane.paneId,
5944
+ "-c",
5945
+ cwd,
5946
+ keepAlive(envPrefix + tasksPaneCommand(inv, { initialTaskId: taskId }))
5947
+ ], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
5867
5948
  }
5868
5949
  if (opsPane && claudePane) {
5869
5950
  commands.push([
@@ -5920,7 +6001,7 @@ async function buildPanesAround(claudePane, args) {
5920
6001
  "-P",
5921
6002
  "-F",
5922
6003
  "tasks=#{pane_id}",
5923
- keepAlive(envPrefix + tasksPaneCommand(args.inv))
6004
+ keepAlive(envPrefix + tasksPaneCommand(args.inv, { initialTaskId: args.taskId }))
5924
6005
  ],
5925
6006
  [
5926
6007
  "split-window",
@@ -6921,9 +7002,9 @@ var init_theme = __esm(() => {
6921
7002
  });
6922
7003
 
6923
7004
  // src/core/index.ts
6924
- import { homedir as homedir7 } from "os";
7005
+ import { homedir as homedir8 } from "os";
6925
7006
  async function createKobeCore(options = {}) {
6926
- const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir7();
7007
+ const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir8();
6927
7008
  const store = new TaskIndexStore({ homeDir: homeDir2 });
6928
7009
  await store.load();
6929
7010
  const worktrees = new GitWorktreeManager;
@@ -7041,10 +7122,10 @@ var init_daemon_cmd = __esm(() => {
7041
7122
 
7042
7123
  // src/lib/skill-install.ts
7043
7124
  import { existsSync as existsSync4 } from "fs";
7044
- import { homedir as homedir8 } from "os";
7125
+ import { homedir as homedir9 } from "os";
7045
7126
  import { join as join7 } from "path";
7046
7127
  function kobeSkillPaths(opts = {}) {
7047
- const home = opts.home ?? homedir8();
7128
+ const home = opts.home ?? homedir9();
7048
7129
  const cwd = opts.cwd ?? process.cwd();
7049
7130
  return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
7050
7131
  }
@@ -12476,7 +12557,7 @@ var init_rename_task_dialog = __esm(() => {
12476
12557
  // src/engine/claude-code-local/binary.ts
12477
12558
  import { spawnSync as spawnSync5 } from "child_process";
12478
12559
  import { existsSync as existsSync7, statSync as statSync3 } from "fs";
12479
- import { homedir as homedir10 } from "os";
12560
+ import { homedir as homedir11 } from "os";
12480
12561
  import path7 from "path";
12481
12562
  async function findClaudeBinary(deps = defaultDeps4) {
12482
12563
  const checked = [];
@@ -12549,7 +12630,7 @@ var init_binary = __esm(() => {
12549
12630
  return process.env[name];
12550
12631
  },
12551
12632
  home() {
12552
- return homedir10();
12633
+ return homedir11();
12553
12634
  },
12554
12635
  which(name) {
12555
12636
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -12580,7 +12661,7 @@ var init_binary = __esm(() => {
12580
12661
  // src/engine/codex-local/binary.ts
12581
12662
  import { spawnSync as spawnSync6 } from "child_process";
12582
12663
  import { existsSync as existsSync8, statSync as statSync4 } from "fs";
12583
- import { homedir as homedir11 } from "os";
12664
+ import { homedir as homedir12 } from "os";
12584
12665
  import path8 from "path";
12585
12666
  async function findCodexBinary(deps = defaultDeps5) {
12586
12667
  const checked = [];
@@ -12637,7 +12718,7 @@ var init_binary2 = __esm(() => {
12637
12718
  return process.env[name];
12638
12719
  },
12639
12720
  home() {
12640
- return homedir11();
12721
+ return homedir12();
12641
12722
  },
12642
12723
  which(name) {
12643
12724
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -12668,7 +12749,7 @@ var init_binary2 = __esm(() => {
12668
12749
  // src/engine/copilot-local/binary.ts
12669
12750
  import { spawnSync as spawnSync7 } from "child_process";
12670
12751
  import { existsSync as existsSync9, statSync as statSync5 } from "fs";
12671
- import { homedir as homedir12 } from "os";
12752
+ import { homedir as homedir13 } from "os";
12672
12753
  import path9 from "path";
12673
12754
  async function findCopilotBinary(deps = defaultDeps6) {
12674
12755
  const checked = [];
@@ -12749,7 +12830,7 @@ var init_binary3 = __esm(() => {
12749
12830
  return process.env[name];
12750
12831
  },
12751
12832
  home() {
12752
- return homedir12();
12833
+ return homedir13();
12753
12834
  },
12754
12835
  which(name) {
12755
12836
  const cmd = process.platform === "win32" ? "where" : "which";
@@ -12774,7 +12855,7 @@ var init_binary3 = __esm(() => {
12774
12855
 
12775
12856
  // src/engine/account-detect.ts
12776
12857
  import { readFileSync as readFileSync7, statSync as statSync6 } from "fs";
12777
- import { homedir as homedir13 } from "os";
12858
+ import { homedir as homedir14 } from "os";
12778
12859
  import path10 from "path";
12779
12860
  function claudeGlobalConfigPath(env, home) {
12780
12861
  const override = env("CLAUDE_CONFIG_DIR")?.trim();
@@ -12992,7 +13073,7 @@ var init_account_detect = __esm(() => {
12992
13073
  return process.env[name];
12993
13074
  },
12994
13075
  home() {
12995
- return homedir13();
13076
+ return homedir14();
12996
13077
  },
12997
13078
  findClaudeBinary() {
12998
13079
  return findClaudeBinary();
@@ -15771,6 +15852,8 @@ function Sidebar(props) {
15771
15852
  createEffect(on(view, () => {
15772
15853
  const ids = flatIds();
15773
15854
  setCursorIndex(ids.length > 0 ? 0 : -1);
15855
+ }, {
15856
+ defer: true
15774
15857
  }));
15775
15858
  createEffect(on([searchMode, searchQuery], () => {
15776
15859
  if (!searchMode())
@@ -16467,13 +16550,20 @@ function TasksShell(props) {
16467
16550
  } = themeCtx;
16468
16551
  const dialog = useDialog();
16469
16552
  const kv = useKV();
16470
- const [selectedId, setSelectedId] = createSignal(props.tasks()[0]?.id ?? null);
16553
+ const [selectedId, setSelectedId] = createSignal(props.tasks().some((t) => t.id === props.initialTaskId) ? props.initialTaskId : props.tasks()[0]?.id ?? null);
16471
16554
  const [updateInfo, setUpdateInfo] = createSignal(null);
16472
16555
  const dimensions = useTerminalDimensions();
16556
+ let activeConfirmed = !props.initialTaskId;
16473
16557
  createEffect(() => {
16474
16558
  const active = props.orch?.activeTaskSignal()();
16475
- if (active)
16476
- setSelectedId(active);
16559
+ if (!active)
16560
+ return;
16561
+ if (!activeConfirmed) {
16562
+ if (active !== props.initialTaskId)
16563
+ return;
16564
+ activeConfirmed = true;
16565
+ }
16566
+ setSelectedId(active);
16477
16567
  });
16478
16568
  onMount(() => {
16479
16569
  themeCtx.setTransparentBackground(props.transparent);
@@ -16582,6 +16672,7 @@ function TasksShell(props) {
16582
16672
  await switchClientBeforeKill(tmuxSessionName(id), nextTask ? tmuxSessionName(nextTask.id) : undefined).catch((err) => {
16583
16673
  console.error("[kobe tasks] switch-client failed:", err);
16584
16674
  });
16675
+ await props.orch.setActiveTask(nextTask?.id ?? null).catch(() => {});
16585
16676
  await killSession(tmuxSessionName(id)).catch((err) => {
16586
16677
  console.error("[kobe tasks] kill tmux session failed:", err);
16587
16678
  });
@@ -16627,13 +16718,14 @@ function TasksShell(props) {
16627
16718
  await switchClientBeforeKill(tmuxSessionName(id), nextTask ? tmuxSessionName(nextTask.id) : undefined).catch((err) => {
16628
16719
  console.error("[kobe tasks] switch-client failed:", err);
16629
16720
  });
16721
+ await props.orch.setActiveTask(nextTask?.id ?? null).catch(() => {});
16630
16722
  await killSession(tmuxSessionName(id)).catch((err) => {
16631
16723
  console.error("[kobe tasks] kill tmux session failed:", err);
16632
16724
  });
16633
16725
  await props.reload();
16634
16726
  if (selectedId() === id) {
16635
16727
  const remaining = props.tasks();
16636
- setSelectedId((remaining.find((t) => !t.archived) ?? remaining[0])?.id ?? null);
16728
+ setSelectedId(nextTask?.id ?? (remaining.find((t) => !t.archived) ?? remaining[0])?.id ?? null);
16637
16729
  }
16638
16730
  }
16639
16731
  async function renameTask(id) {
@@ -16949,7 +17041,7 @@ function ShortcutHints() {
16949
17041
  return _el$3;
16950
17042
  })();
16951
17043
  }
16952
- async function startTasksPane() {
17044
+ async function startTasksPane(opts = {}) {
16953
17045
  for (const {
16954
17046
  name,
16955
17047
  theme
@@ -16994,6 +17086,9 @@ async function startTasksPane() {
16994
17086
  get children() {
16995
17087
  return createComponent2(TasksShell, {
16996
17088
  tasks,
17089
+ get initialTaskId() {
17090
+ return opts.initialTaskId;
17091
+ },
16997
17092
  orch,
16998
17093
  get transparent() {
16999
17094
  return prefs.transparent;
@@ -19135,14 +19230,16 @@ function OpsShell(props) {
19135
19230
  onMount(() => {
19136
19231
  let disposed = false;
19137
19232
  async function poll() {
19138
- const mtime = await latestTranscriptMtime(props.vendor, props.worktree);
19139
- if (disposed)
19140
- return;
19141
- if (!primed) {
19142
- primed = true;
19143
- setBaseline(mtime);
19144
- }
19145
- setLatest(mtime);
19233
+ try {
19234
+ const mtime = await latestTranscriptMtime(props.vendor, props.worktree);
19235
+ if (disposed)
19236
+ return;
19237
+ if (!primed) {
19238
+ primed = true;
19239
+ setBaseline(mtime);
19240
+ }
19241
+ setLatest(mtime);
19242
+ } catch {}
19146
19243
  }
19147
19244
  poll();
19148
19245
  const timer = setInterval(() => void poll(), ACTIVITY_POLL_MS);
@@ -19176,31 +19273,35 @@ function OpsShell(props) {
19176
19273
  await setWindowOption(props.targetPane, CHAT_TAB_STATE_OPTION2, state);
19177
19274
  }
19178
19275
  async function prime() {
19179
- paneHash = fingerprint(await capturePaneById(props.targetPane, 80));
19180
- baselineCompletionId = (await detector.latestCompletion(props.worktree))?.id ?? null;
19181
- await publish(detector.supportsCompletionMarkers() ? "idle" : "unknown");
19276
+ try {
19277
+ paneHash = fingerprint(await capturePaneById(props.targetPane, 80));
19278
+ baselineCompletionId = (await detector.latestCompletion(props.worktree))?.id ?? null;
19279
+ await publish(detector.supportsCompletionMarkers() ? "idle" : "unknown");
19280
+ } catch {}
19182
19281
  }
19183
19282
  async function poll() {
19184
- const nextPaneHash = fingerprint(await capturePaneById(props.targetPane, 80));
19185
- if (disposed)
19186
- return;
19187
- if (nextPaneHash !== paneHash) {
19188
- paneHash = nextPaneHash;
19189
- observedPaneActivity = true;
19283
+ try {
19284
+ const nextPaneHash = fingerprint(await capturePaneById(props.targetPane, 80));
19285
+ if (disposed)
19286
+ return;
19287
+ if (nextPaneHash !== paneHash) {
19288
+ paneHash = nextPaneHash;
19289
+ observedPaneActivity = true;
19290
+ stablePolls = 0;
19291
+ await publish(detector.supportsCompletionMarkers() ? "running" : "unknown");
19292
+ } else if (observedPaneActivity) {
19293
+ stablePolls++;
19294
+ }
19295
+ if (!detector.supportsCompletionMarkers() || !observedPaneActivity || stablePolls < STABLE_POLLS_FOR_DONE)
19296
+ return;
19297
+ const marker = await detector.latestCompletion(props.worktree);
19298
+ if (disposed || !marker || marker.id === baselineCompletionId)
19299
+ return;
19300
+ baselineCompletionId = marker.id;
19301
+ observedPaneActivity = false;
19190
19302
  stablePolls = 0;
19191
- await publish(detector.supportsCompletionMarkers() ? "running" : "unknown");
19192
- } else if (observedPaneActivity) {
19193
- stablePolls++;
19194
- }
19195
- if (!detector.supportsCompletionMarkers() || !observedPaneActivity || stablePolls < STABLE_POLLS_FOR_DONE)
19196
- return;
19197
- const marker = await detector.latestCompletion(props.worktree);
19198
- if (disposed || !marker || marker.id === baselineCompletionId)
19199
- return;
19200
- baselineCompletionId = marker.id;
19201
- observedPaneActivity = false;
19202
- stablePolls = 0;
19203
- await publish("done");
19303
+ await publish("done");
19304
+ } catch {}
19204
19305
  }
19205
19306
  prime();
19206
19307
  const timer = setInterval(() => void poll(), TURN_STATUS_POLL_MS);
@@ -19644,8 +19745,11 @@ async function startDirectTmux() {
19644
19745
  cwdRepo
19645
19746
  });
19646
19747
  if (!task) {
19647
- console.error("kobe: no task available to enter");
19648
- process.exitCode = 1;
19748
+ const home = await ensureFallbackSession();
19749
+ if (await attachTmux(attachArgv(home)) === null) {
19750
+ console.error("kobe: failed to attach to the kobe-home session");
19751
+ process.exitCode = 1;
19752
+ }
19649
19753
  return;
19650
19754
  }
19651
19755
  const cwd = task.worktreePath || await orchestrator.ensureWorktree(task.id);
@@ -19700,6 +19804,7 @@ var init_direct = __esm(() => {
19700
19804
  init_core();
19701
19805
  init_repo_init();
19702
19806
  init_repos();
19807
+ init_client2();
19703
19808
  init_tmux();
19704
19809
  });
19705
19810
 
@@ -21335,7 +21440,7 @@ var exports_app = {};
21335
21440
  __export(exports_app, {
21336
21441
  startApp: () => startApp
21337
21442
  });
21338
- import { homedir as homedir14 } from "os";
21443
+ import { homedir as homedir15 } from "os";
21339
21444
  function Shell(props) {
21340
21445
  const themeCtx = useTheme();
21341
21446
  const {
@@ -21843,7 +21948,7 @@ async function startApp() {
21843
21948
  } of loadUserThemes()) {
21844
21949
  addTheme2(name, theme);
21845
21950
  }
21846
- const homeDir2 = process.env.KOBE_HOME_DIR ?? homedir14();
21951
+ const homeDir2 = process.env.KOBE_HOME_DIR ?? homedir15();
21847
21952
  let orchestrator;
21848
21953
  if (process.env.KOBE_NO_DAEMON === "1") {
21849
21954
  const store2 = new TaskIndexStore({
@@ -22168,6 +22273,9 @@ function parseOpsFlags(argv) {
22168
22273
  } else if (flag === "--repo") {
22169
22274
  flags.repo = value;
22170
22275
  i++;
22276
+ } else if (flag === "--initial-task-id") {
22277
+ flags.initialTaskId = value;
22278
+ i++;
22171
22279
  }
22172
22280
  }
22173
22281
  return flags;
@@ -22264,8 +22372,9 @@ async function main() {
22264
22372
  return;
22265
22373
  }
22266
22374
  if (subcommand === "tasks") {
22375
+ const flags = parseOpsFlags(rest);
22267
22376
  const { startTasksPane: startTasksPane2 } = await Promise.resolve().then(() => (init_host(), exports_host));
22268
- await startTasksPane2();
22377
+ await startTasksPane2({ initialTaskId: flags.initialTaskId });
22269
22378
  return;
22270
22379
  }
22271
22380
  if (subcommand === "settings") {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@sma1lboy/kobe",
4
- "version": "0.7.0",
4
+ "version": "0.7.1",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",