@sma1lboy/kobe 0.5.9 → 0.5.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -5271,6 +5271,13 @@ var init_keybindings = __esm(() => {
5271
5271
  description: "New task",
5272
5272
  hint: { keys: "n", label: "new" }
5273
5273
  },
5274
+ {
5275
+ id: "task.openEditor",
5276
+ scope: "global",
5277
+ keys: ["ctrl+o"],
5278
+ category: "Global",
5279
+ description: "Open active task worktree in editor"
5280
+ },
5274
5281
  {
5275
5282
  id: "settings.open",
5276
5283
  scope: "global",
@@ -8857,7 +8864,7 @@ var init_package = __esm(() => {
8857
8864
  package_default = {
8858
8865
  $schema: "https://json.schemastore.org/package.json",
8859
8866
  name: "@sma1lboy/kobe",
8860
- version: "0.5.9",
8867
+ version: "0.5.11",
8861
8868
  description: "TUI orchestrator for Claude Code (codename)",
8862
8869
  type: "module",
8863
8870
  packageManager: "bun@1.3.13",
@@ -8887,8 +8894,10 @@ var init_package = __esm(() => {
8887
8894
  build: "bun run scripts/build.ts",
8888
8895
  compile: "bun run scripts/compile.ts",
8889
8896
  typecheck: "tsc --noEmit",
8890
- test: "vitest run --passWithNoTests",
8891
- "test:behavior": "vitest run test/behavior --passWithNoTests",
8897
+ test: "bun run test:fast && bun run test:socket",
8898
+ "test:fast": "vitest run --passWithNoTests",
8899
+ "test:socket": "KOBE_INCLUDE_SOCKET=1 vitest run test/daemon test/orchestrator/bridge.test.ts --pool forks --minWorkers=1 --maxWorkers=1 --passWithNoTests",
8900
+ "test:behavior": "KOBE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --passWithNoTests",
8892
8901
  lint: "biome check .",
8893
8902
  prepublishOnly: "bun run typecheck && bun run build"
8894
8903
  },
@@ -8922,6 +8931,9 @@ function repoSlug() {
8922
8931
  return null;
8923
8932
  return `${m[1]}/${m[2]}`;
8924
8933
  }
8934
+ function recommendedGlobalInstallCommand() {
8935
+ return `npm install -g ${PACKAGE_NAME}@latest`;
8936
+ }
8925
8937
  function cachePath() {
8926
8938
  return join3(kobeStateDir(), "version-check.json");
8927
8939
  }
@@ -9037,13 +9049,13 @@ function releasePageUrl(version) {
9037
9049
  return null;
9038
9050
  return `https://github.com/${slug}/releases/tag/v${version}`;
9039
9051
  }
9040
- var CURRENT_VERSION, PACKAGE_NAME, INSTALL_COMMAND, CACHE_TTL_MS, FETCH_TIMEOUT_MS = 3000;
9052
+ var CURRENT_VERSION, PACKAGE_NAME, UPDATE_SCRIPT_URL = "https://raw.githubusercontent.com/Sma1lboy/kobe/main/scripts/update.sh", UPDATE_COMMAND, CACHE_TTL_MS, FETCH_TIMEOUT_MS = 3000;
9041
9053
  var init_version = __esm(() => {
9042
9054
  init_package();
9043
9055
  init_env();
9044
9056
  CURRENT_VERSION = package_default.version;
9045
9057
  PACKAGE_NAME = package_default.name;
9046
- INSTALL_COMMAND = `bun install -g ${PACKAGE_NAME}@latest`;
9058
+ UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
9047
9059
  CACHE_TTL_MS = 6 * 60 * 60 * 1000;
9048
9060
  });
9049
9061
 
@@ -9375,6 +9387,93 @@ var init_diagnose = __esm(() => {
9375
9387
  STATUS_ORDER = ["backlog", "in_progress", "in_review", "done", "canceled", "error"];
9376
9388
  });
9377
9389
 
9390
+ // src/cli/update.ts
9391
+ var exports_update = {};
9392
+ __export(exports_update, {
9393
+ updatePlan: () => updatePlan,
9394
+ runUpdateSubcommand: () => runUpdateSubcommand,
9395
+ parseUpdateArgs: () => parseUpdateArgs
9396
+ });
9397
+ import { spawnSync as spawnSync6 } from "child_process";
9398
+ function fail(message) {
9399
+ process.stderr.write(`kobe update: ${message}
9400
+ `);
9401
+ process.exit(1);
9402
+ }
9403
+ function updatePlan() {
9404
+ return {
9405
+ command: "sh",
9406
+ args: ["-c", UPDATE_COMMAND],
9407
+ display: UPDATE_COMMAND
9408
+ };
9409
+ }
9410
+ function parseUpdateArgs(args) {
9411
+ let dryRun = false;
9412
+ for (let i = 0;i < args.length; i += 1) {
9413
+ const arg = args[i];
9414
+ if (arg === "--help" || arg === "-h" || arg === "help")
9415
+ return { help: true, dryRun };
9416
+ if (arg === "--dry-run") {
9417
+ dryRun = true;
9418
+ continue;
9419
+ }
9420
+ fail(`unknown argument "${arg}"`);
9421
+ }
9422
+ return { help: false, dryRun };
9423
+ }
9424
+ function printUsage(out) {
9425
+ out.write([
9426
+ "Usage: kobe update [--dry-run]",
9427
+ "",
9428
+ "Runs kobe's GitHub-hosted update script.",
9429
+ "",
9430
+ "Default command:",
9431
+ ` ${UPDATE_COMMAND}`,
9432
+ "",
9433
+ "Script URL:",
9434
+ ` ${UPDATE_SCRIPT_URL}`,
9435
+ "",
9436
+ "Manual fallback:",
9437
+ ` ${recommendedGlobalInstallCommand()}`,
9438
+ "",
9439
+ "Examples:",
9440
+ " kobe update",
9441
+ " kobe update --dry-run",
9442
+ ""
9443
+ ].join(`
9444
+ `));
9445
+ }
9446
+ async function runUpdateSubcommand(args, deps) {
9447
+ const io = {
9448
+ spawn: deps?.spawn ?? spawnSync6,
9449
+ stdout: deps?.stdout ?? process.stdout,
9450
+ stderr: deps?.stderr ?? process.stderr,
9451
+ exit: deps?.exit ?? ((code) => process.exit(code))
9452
+ };
9453
+ const parsed = parseUpdateArgs(args);
9454
+ if (parsed.help) {
9455
+ printUsage(io.stdout);
9456
+ return;
9457
+ }
9458
+ const plan = updatePlan();
9459
+ io.stdout.write(`kobe ${CURRENT_VERSION} -> latest
9460
+ `);
9461
+ io.stdout.write(`running: ${plan.display}
9462
+ `);
9463
+ if (parsed.dryRun)
9464
+ return;
9465
+ const result = io.spawn(plan.command, plan.args, { stdio: "inherit" });
9466
+ if (result.error) {
9467
+ io.stderr.write(`kobe update: failed to run ${plan.command}: ${result.error.message}
9468
+ `);
9469
+ io.exit(1);
9470
+ }
9471
+ io.exit(result.status ?? 1);
9472
+ }
9473
+ var init_update = __esm(() => {
9474
+ init_version();
9475
+ });
9476
+
9378
9477
  // src/tui/context/theme/schema.ts
9379
9478
  function isPlainObject(v) {
9380
9479
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -9475,7 +9574,7 @@ __export(exports_theme, {
9475
9574
  });
9476
9575
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9477
9576
  import { basename as basename2, join as join6, resolve } from "path";
9478
- function fail(message) {
9577
+ function fail2(message) {
9479
9578
  process.stderr.write(`kobe theme: ${message}
9480
9579
  `);
9481
9580
  process.exit(1);
@@ -9513,10 +9612,10 @@ async function readSource(source) {
9513
9612
  try {
9514
9613
  res = await fetch(source);
9515
9614
  } catch (err) {
9516
- fail(`failed to fetch ${source}: ${err instanceof Error ? err.message : String(err)}`);
9615
+ fail2(`failed to fetch ${source}: ${err instanceof Error ? err.message : String(err)}`);
9517
9616
  }
9518
9617
  if (!res.ok) {
9519
- fail(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);
9618
+ fail2(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);
9520
9619
  }
9521
9620
  const text2 = await res.text();
9522
9621
  const cleanPath = source.split(/[?#]/)[0] ?? source;
@@ -9529,7 +9628,7 @@ async function readSource(source) {
9529
9628
  try {
9530
9629
  text = readFileSync3(abs, "utf8");
9531
9630
  } catch (err) {
9532
- fail(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
9631
+ fail2(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
9533
9632
  }
9534
9633
  const file = basename2(abs);
9535
9634
  const defaultName = file.endsWith(".json") ? file.slice(0, -".json".length) : file;
@@ -9548,7 +9647,7 @@ function parseAddArgs(args) {
9548
9647
  if (a === "--name" || a === "-n") {
9549
9648
  const next = args[i + 1];
9550
9649
  if (next === undefined)
9551
- fail("--name requires a value");
9650
+ fail2("--name requires a value");
9552
9651
  name = next;
9553
9652
  i += 1;
9554
9653
  continue;
@@ -9558,15 +9657,15 @@ function parseAddArgs(args) {
9558
9657
  continue;
9559
9658
  }
9560
9659
  if (a.startsWith("--"))
9561
- fail(`unknown flag: ${a}`);
9660
+ fail2(`unknown flag: ${a}`);
9562
9661
  if (source === null) {
9563
9662
  source = a;
9564
9663
  continue;
9565
9664
  }
9566
- fail(`unexpected positional argument: ${a}`);
9665
+ fail2(`unexpected positional argument: ${a}`);
9567
9666
  }
9568
9667
  if (source === null)
9569
- fail("missing <source> (URL or path to theme JSON)");
9668
+ fail2("missing <source> (URL or path to theme JSON)");
9570
9669
  return { source, opts: { name, force } };
9571
9670
  }
9572
9671
  async function addTheme2(args) {
@@ -9576,21 +9675,21 @@ async function addTheme2(args) {
9576
9675
  try {
9577
9676
  parsed = JSON.parse(text);
9578
9677
  } catch (err) {
9579
- fail(`source is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
9678
+ fail2(`source is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
9580
9679
  }
9581
9680
  const result = validateTheme(parsed);
9582
9681
  if (!result.ok) {
9583
- fail(`source is not a valid kobe theme: ${result.reason}`);
9682
+ fail2(`source is not a valid kobe theme: ${result.reason}`);
9584
9683
  }
9585
9684
  const name = opts.name ?? defaultName;
9586
9685
  if (!name || !/^[a-zA-Z0-9._-]+$/.test(name)) {
9587
- fail(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
9686
+ fail2(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
9588
9687
  }
9589
9688
  const dir = userThemesDir();
9590
9689
  mkdirSync2(dir, { recursive: true });
9591
9690
  const dest = join6(dir, `${name}.json`);
9592
9691
  if (existsSync3(dest) && !opts.force) {
9593
- fail(`${dest} already exists (pass --force to overwrite)`);
9692
+ fail2(`${dest} already exists (pass --force to overwrite)`);
9594
9693
  }
9595
9694
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
9596
9695
  `, "utf8");
@@ -9600,21 +9699,21 @@ async function addTheme2(args) {
9600
9699
  function removeTheme(args) {
9601
9700
  const name = args[0];
9602
9701
  if (!name)
9603
- fail("missing <name>");
9702
+ fail2("missing <name>");
9604
9703
  if (args.length > 1)
9605
- fail(`unexpected extra arguments after "${name}"`);
9704
+ fail2(`unexpected extra arguments after "${name}"`);
9606
9705
  if (BUNDLED_NAMES.includes(name)) {
9607
- fail(`"${name}" is a built-in theme and cannot be removed`);
9706
+ fail2(`"${name}" is a built-in theme and cannot be removed`);
9608
9707
  }
9609
9708
  const dest = join6(userThemesDir(), `${name}.json`);
9610
9709
  if (!existsSync3(dest)) {
9611
- fail(`no user theme named "${name}" (looked for ${dest})`);
9710
+ fail2(`no user theme named "${name}" (looked for ${dest})`);
9612
9711
  }
9613
9712
  unlinkSync(dest);
9614
9713
  process.stdout.write(`removed theme "${name}" (${dest})
9615
9714
  `);
9616
9715
  }
9617
- function printUsage() {
9716
+ function printUsage2() {
9618
9717
  process.stderr.write([
9619
9718
  "Usage: kobe theme <command> [args]",
9620
9719
  "",
@@ -9633,14 +9732,14 @@ function printUsage() {
9633
9732
  async function runThemeSubcommand(args) {
9634
9733
  const [action, ...rest] = args;
9635
9734
  if (!action || action === "--help" || action === "-h" || action === "help") {
9636
- printUsage();
9735
+ printUsage2();
9637
9736
  if (!action)
9638
9737
  process.exit(1);
9639
9738
  return;
9640
9739
  }
9641
9740
  if (action === "list" || action === "ls") {
9642
9741
  if (rest.length > 0)
9643
- fail(`"${action}" takes no arguments`);
9742
+ fail2(`"${action}" takes no arguments`);
9644
9743
  listThemes2();
9645
9744
  return;
9646
9745
  }
@@ -9652,7 +9751,7 @@ async function runThemeSubcommand(args) {
9652
9751
  removeTheme(rest);
9653
9752
  return;
9654
9753
  }
9655
- fail(`unknown action "${action}" (try "list", "add", or "remove")`);
9754
+ fail2(`unknown action "${action}" (try "list", "add", or "remove")`);
9656
9755
  }
9657
9756
  var BUNDLED_NAMES;
9658
9757
  var init_theme2 = __esm(() => {
@@ -9775,7 +9874,7 @@ async function handleMcpFrame(client, line) {
9775
9874
  process.stdout.write(`${JSON.stringify(out)}
9776
9875
  `);
9777
9876
  };
9778
- const fail2 = (code, message) => {
9877
+ const fail3 = (code, message) => {
9779
9878
  const out = { jsonrpc: "2.0", id: msg.id, error: { code, message } };
9780
9879
  process.stdout.write(`${JSON.stringify(out)}
9781
9880
  `);
@@ -9797,7 +9896,7 @@ async function handleMcpFrame(client, line) {
9797
9896
  const name = params.name;
9798
9897
  const args = params.arguments ?? {};
9799
9898
  if (!name) {
9800
- fail2(-32602, "missing tool name");
9899
+ fail3(-32602, "missing tool name");
9801
9900
  return;
9802
9901
  }
9803
9902
  const result = await invokeTool(client, name, args);
@@ -9807,11 +9906,11 @@ async function handleMcpFrame(client, line) {
9807
9906
  return;
9808
9907
  }
9809
9908
  default:
9810
- fail2(-32601, `method not found: ${msg.method}`);
9909
+ fail3(-32601, `method not found: ${msg.method}`);
9811
9910
  return;
9812
9911
  }
9813
9912
  } catch (err) {
9814
- fail2(-32000, err instanceof Error ? err.message : String(err));
9913
+ fail3(-32000, err instanceof Error ? err.message : String(err));
9815
9914
  }
9816
9915
  }
9817
9916
  async function invokeTool(client, name, args) {
@@ -10174,6 +10273,86 @@ var init_claude_settings = __esm(() => {
10174
10273
  SETTINGS_PATH = join9(homedir6(), ".claude", "settings.json");
10175
10274
  });
10176
10275
 
10276
+ // src/session/usage-metrics.ts
10277
+ function totalContextTokens(u) {
10278
+ return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
10279
+ }
10280
+ function parseTimestampMs(value) {
10281
+ const ms = new Date(value).getTime();
10282
+ return Number.isFinite(ms) ? ms : null;
10283
+ }
10284
+ function mergeIntervals(intervals) {
10285
+ if (intervals.length === 0)
10286
+ return [];
10287
+ const sorted = [...intervals].sort((a, b) => a.startMs - b.startMs);
10288
+ const first = sorted[0];
10289
+ if (!first)
10290
+ return [];
10291
+ const merged = [{ startMs: first.startMs, endMs: first.endMs }];
10292
+ for (let i = 1;i < sorted.length; i++) {
10293
+ const current = sorted[i];
10294
+ const last = merged[merged.length - 1];
10295
+ if (!current || !last)
10296
+ continue;
10297
+ if (current.startMs <= last.endMs) {
10298
+ last.endMs = Math.max(last.endMs, current.endMs);
10299
+ } else {
10300
+ merged.push({ startMs: current.startMs, endMs: current.endMs });
10301
+ }
10302
+ }
10303
+ return merged;
10304
+ }
10305
+ function durationMs(intervals) {
10306
+ return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
10307
+ }
10308
+ function deriveSessionUsageMetrics(past) {
10309
+ let latestUsage;
10310
+ let latestUsageTimestampMs = null;
10311
+ let lastUserTimestampMs = null;
10312
+ let inputTokens = 0;
10313
+ let outputTokens = 0;
10314
+ const intervals = [];
10315
+ for (const message of past) {
10316
+ const timestampMs = parseTimestampMs(message.timestamp);
10317
+ if (message.role === "user" && timestampMs !== null) {
10318
+ lastUserTimestampMs = timestampMs;
10319
+ continue;
10320
+ }
10321
+ if (message.role !== "assistant" || !message.usage)
10322
+ continue;
10323
+ if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
10324
+ latestUsageTimestampMs = timestampMs;
10325
+ latestUsage = message.usage;
10326
+ } else if (latestUsage === undefined) {
10327
+ latestUsage = message.usage;
10328
+ }
10329
+ inputTokens += message.usage.input_tokens;
10330
+ outputTokens += message.usage.output_tokens;
10331
+ if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
10332
+ intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
10333
+ }
10334
+ }
10335
+ if (!latestUsage)
10336
+ return;
10337
+ const totalDurationMs = durationMs(mergeIntervals(intervals));
10338
+ if (totalDurationMs <= 0)
10339
+ return latestUsage;
10340
+ return {
10341
+ ...latestUsage,
10342
+ total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
10343
+ };
10344
+ }
10345
+ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
10346
+ const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
10347
+ const endMs = parseTimestampMs(endedAtIso);
10348
+ if (startMs === null || endMs === null || endMs <= startMs)
10349
+ return usage;
10350
+ return {
10351
+ ...usage,
10352
+ total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
10353
+ };
10354
+ }
10355
+
10177
10356
  // src/orchestrator/metadata-suggester.ts
10178
10357
  import { spawn as spawn3 } from "child_process";
10179
10358
 
@@ -10353,10 +10532,10 @@ class InMemoryPendingInputBroker {
10353
10532
  }
10354
10533
 
10355
10534
  // src/orchestrator/pr/build.ts
10356
- import { spawnSync as spawnSync6 } from "child_process";
10535
+ import { spawnSync as spawnSync7 } from "child_process";
10357
10536
  function git(cwd, args) {
10358
10537
  try {
10359
- const out = spawnSync6("git", args.slice(), {
10538
+ const out = spawnSync7("git", args.slice(), {
10360
10539
  cwd,
10361
10540
  encoding: "utf8",
10362
10541
  timeout: GIT_TIMEOUT_MS
@@ -11049,6 +11228,14 @@ class Orchestrator {
11049
11228
  return [];
11050
11229
  }
11051
11230
  }
11231
+ async readHistoryWithMetrics(sessionId) {
11232
+ const messages = await this.readHistory(sessionId);
11233
+ const usageMetrics = deriveSessionUsageMetrics(messages);
11234
+ return {
11235
+ messages,
11236
+ ...usageMetrics ? { usageMetrics } : {}
11237
+ };
11238
+ }
11052
11239
  async listSessions(id) {
11053
11240
  const task = this.requireTask(id);
11054
11241
  if (!task.worktreePath)
@@ -11524,15 +11711,21 @@ class RemoteOrchestrator {
11524
11711
  await this.client.request("chat.tab.activate", { taskId, tabId });
11525
11712
  }
11526
11713
  async readHistory(sessionId) {
11714
+ return (await this.readHistoryWithMetrics(sessionId)).messages;
11715
+ }
11716
+ async readHistoryWithMetrics(sessionId) {
11527
11717
  const task = this.tasksAcc().find((t) => t.sessionId === sessionId || t.tabs.some((tab) => tab.sessionId === sessionId));
11528
11718
  if (!task)
11529
- return [];
11719
+ return { messages: [] };
11530
11720
  const res = await this.client.request("chat.history", {
11531
11721
  taskId: task.id,
11532
11722
  sessionId,
11533
11723
  limit: 500
11534
11724
  });
11535
- return res.messages;
11725
+ return {
11726
+ messages: res.messages,
11727
+ ...res.usageMetrics ? { usageMetrics: res.usageMetrics } : {}
11728
+ };
11536
11729
  }
11537
11730
  async listSessions(taskId) {
11538
11731
  const res = await this.client.request("chat.sessions", { taskId });
@@ -11685,12 +11878,12 @@ var init_remote_orchestrator = __esm(() => {
11685
11878
  });
11686
11879
 
11687
11880
  // src/orchestrator/worktree/git.ts
11688
- import { spawnSync as spawnSync7 } from "child_process";
11881
+ import { spawnSync as spawnSync8 } from "child_process";
11689
11882
  function git2(args, opts) {
11690
11883
  if (!opts.cwd) {
11691
11884
  throw new Error("git(): cwd is required; refusing to inherit from process.cwd()");
11692
11885
  }
11693
- const proc = spawnSync7("git", [...args], {
11886
+ const proc = spawnSync8("git", [...args], {
11694
11887
  cwd: opts.cwd,
11695
11888
  env: opts.env ? { ...process.env, ...opts.env } : process.env,
11696
11889
  encoding: "utf8",
@@ -12766,6 +12959,7 @@ function useAppKeymap(deps) {
12766
12959
  useBindings(() => ({
12767
12960
  enabled: dialog.stack.length === 0,
12768
12961
  bindings: bindByIds({
12962
+ "task.openEditor": deps.openActiveTaskInEditor,
12769
12963
  "settings.open": () => {
12770
12964
  SettingsDialog.show(dialog, deps.kv, deps.orchestrator);
12771
12965
  }
@@ -14469,6 +14663,173 @@ var init_create_pr_button = __esm(() => {
14469
14663
  init_theme();
14470
14664
  });
14471
14665
 
14666
+ // src/tui/lib/worktree-opener.ts
14667
+ import { spawn as spawn4 } from "child_process";
14668
+ import { existsSync as existsSync6 } from "fs";
14669
+ import { basename as basename5, delimiter, isAbsolute as isAbsolute2, join as join13 } from "path";
14670
+ function executableOnPath(command, env, exists) {
14671
+ if (isAbsolute2(command))
14672
+ return exists(command);
14673
+ const pathEnv = env.PATH ?? "";
14674
+ for (const dir of pathEnv.split(delimiter)) {
14675
+ if (!dir)
14676
+ continue;
14677
+ if (exists(join13(dir, command)))
14678
+ return true;
14679
+ }
14680
+ return false;
14681
+ }
14682
+ function labelForOverride(command) {
14683
+ const name = basename5(command);
14684
+ if (name === "code")
14685
+ return "VS Code";
14686
+ if (name === "cursor")
14687
+ return "Cursor";
14688
+ if (name === "windsurf")
14689
+ return "Windsurf";
14690
+ if (name === "zed")
14691
+ return "Zed";
14692
+ return name || command;
14693
+ }
14694
+ function detectWorktreeOpener(deps = {}) {
14695
+ const env = deps.env ?? process.env;
14696
+ const platform = deps.platform ?? process.platform;
14697
+ const exists = deps.exists ?? existsSync6;
14698
+ const override = env.KOBE_OPEN_EDITOR?.trim();
14699
+ if (override) {
14700
+ return { id: "env", label: labelForOverride(override), command: override, args: [] };
14701
+ }
14702
+ for (const c of CLI_CANDIDATES) {
14703
+ if (executableOnPath(c.command, env, exists)) {
14704
+ return { id: c.id, label: c.label, command: c.command, args: [] };
14705
+ }
14706
+ }
14707
+ if (platform === "darwin" && executableOnPath("open", env, exists)) {
14708
+ for (const app of MAC_APP_CANDIDATES) {
14709
+ if (app.paths.some((path6) => exists(path6))) {
14710
+ return { id: app.id, label: app.label, command: "open", args: ["-a", app.appName] };
14711
+ }
14712
+ }
14713
+ return { id: "mac-open", label: "Finder", command: "open", args: [] };
14714
+ }
14715
+ if (platform === "linux" && executableOnPath("xdg-open", env, exists)) {
14716
+ return { id: "xdg-open", label: "Open", command: "xdg-open", args: [] };
14717
+ }
14718
+ return null;
14719
+ }
14720
+ function buildOpenWorktreeCommand(worktreePath, opener) {
14721
+ return [opener.command, [...opener.args, worktreePath]];
14722
+ }
14723
+ function openWorktree(worktreePath, opener, deps = {}) {
14724
+ if (!worktreePath)
14725
+ return false;
14726
+ const spawnFn = deps.spawn ?? spawn4;
14727
+ const [command, args2] = buildOpenWorktreeCommand(worktreePath, opener);
14728
+ try {
14729
+ const child = spawnFn(command, args2, { detached: true, stdio: "ignore" });
14730
+ if (child.pid === undefined)
14731
+ return false;
14732
+ child.unref();
14733
+ return true;
14734
+ } catch {
14735
+ return false;
14736
+ }
14737
+ }
14738
+ var CLI_CANDIDATES, MAC_APP_CANDIDATES;
14739
+ var init_worktree_opener = __esm(() => {
14740
+ CLI_CANDIDATES = [
14741
+ { id: "code", label: "VS Code", command: "code" },
14742
+ { id: "cursor", label: "Cursor", command: "cursor" },
14743
+ { id: "windsurf", label: "Windsurf", command: "windsurf" },
14744
+ { id: "zed", label: "Zed", command: "zed" }
14745
+ ];
14746
+ MAC_APP_CANDIDATES = [
14747
+ {
14748
+ id: "vscode.app",
14749
+ label: "VS Code",
14750
+ appName: "Visual Studio Code",
14751
+ paths: ["/Applications/Visual Studio Code.app"]
14752
+ },
14753
+ {
14754
+ id: "cursor.app",
14755
+ label: "Cursor",
14756
+ appName: "Cursor",
14757
+ paths: ["/Applications/Cursor.app", "/System/Applications/Cursor.app"]
14758
+ },
14759
+ {
14760
+ id: "windsurf.app",
14761
+ label: "Windsurf",
14762
+ appName: "Windsurf",
14763
+ paths: ["/Applications/Windsurf.app"]
14764
+ },
14765
+ {
14766
+ id: "zed.app",
14767
+ label: "Zed",
14768
+ appName: "Zed",
14769
+ paths: ["/Applications/Zed.app"]
14770
+ }
14771
+ ];
14772
+ });
14773
+
14774
+ // src/tui/component/open-worktree-button.tsx
14775
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
14776
+ function canOpen(task, opener) {
14777
+ return Boolean(task?.worktreePath && opener);
14778
+ }
14779
+ function OpenWorktreeButton(props) {
14780
+ const {
14781
+ theme
14782
+ } = useTheme();
14783
+ const enabled = () => canOpen(props.activeTask(), props.opener());
14784
+ const label = () => props.opener()?.label ?? "Editor";
14785
+ const color = () => enabled() ? theme.accent : theme.textMuted;
14786
+ function onClick() {
14787
+ const task = props.activeTask();
14788
+ const opener = props.opener();
14789
+ if (!task?.worktreePath || !opener)
14790
+ return;
14791
+ if (!openWorktree(task.worktreePath, opener)) {
14792
+ console.error("[kobe] failed to open worktree:", task.worktreePath);
14793
+ }
14794
+ }
14795
+ return (() => {
14796
+ var _el$ = createElement("box"), _el$2 = createElement("text"), _el$4 = createElement("text");
14797
+ insertNode(_el$, _el$2);
14798
+ insertNode(_el$, _el$4);
14799
+ setProp(_el$, "flexDirection", "row");
14800
+ setProp(_el$, "gap", 1);
14801
+ setProp(_el$, "flexShrink", 0);
14802
+ insertNode(_el$2, createTextNode(`[Open]`));
14803
+ setProp(_el$2, "wrapMode", "none");
14804
+ setProp(_el$4, "wrapMode", "none");
14805
+ insert(_el$4, label);
14806
+ effect((_p$) => {
14807
+ var _v$ = enabled() ? onClick : undefined, _v$2 = color(), _v$3 = TextAttributes16.BOLD, _v$4 = theme.textMuted;
14808
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "onMouseUp", _v$, _p$.e));
14809
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "fg", _v$2, _p$.t));
14810
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$2, "attributes", _v$3, _p$.a));
14811
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
14812
+ return _p$;
14813
+ }, {
14814
+ e: undefined,
14815
+ t: undefined,
14816
+ a: undefined,
14817
+ o: undefined
14818
+ });
14819
+ return _el$;
14820
+ })();
14821
+ }
14822
+ var init_open_worktree_button = __esm(() => {
14823
+ init_solid();
14824
+ init_solid();
14825
+ init_solid();
14826
+ init_solid();
14827
+ init_solid();
14828
+ init_solid();
14829
+ init_theme();
14830
+ init_worktree_opener();
14831
+ });
14832
+
14472
14833
  // src/tui/panes/chat/markdown-parser.ts
14473
14834
  function splitTableRow(line) {
14474
14835
  const trimmed = line.trim().replace(/^\||\|$/g, "");
@@ -14735,7 +15096,7 @@ var init_markdown_parser = __esm(() => {
14735
15096
  });
14736
15097
 
14737
15098
  // src/tui/panes/chat/Markdown.tsx
14738
- import { TextAttributes as TextAttributes16 } from "@opentui/core";
15099
+ import { TextAttributes as TextAttributes17 } from "@opentui/core";
14739
15100
  function InlineSpans(props) {
14740
15101
  const {
14741
15102
  theme
@@ -14765,7 +15126,7 @@ function InlineSpans(props) {
14765
15126
  insert(_el$3, () => t.text, _el$5);
14766
15127
  effect((_$p) => setProp(_el$3, "style", {
14767
15128
  fg: theme.accent,
14768
- attributes: TextAttributes16.DIM
15129
+ attributes: TextAttributes17.DIM
14769
15130
  }, _$p));
14770
15131
  return _el$3;
14771
15132
  })();
@@ -14777,7 +15138,7 @@ function InlineSpans(props) {
14777
15138
  insert(_el$6, () => showUrl ? t.text : t.href);
14778
15139
  effect((_$p) => setProp(_el$6, "style", {
14779
15140
  fg: theme.accent,
14780
- attributes: TextAttributes16.UNDERLINE
15141
+ attributes: TextAttributes17.UNDERLINE
14781
15142
  }, _$p));
14782
15143
  return _el$6;
14783
15144
  })(), createComponent2(Show, {
@@ -14789,7 +15150,7 @@ function InlineSpans(props) {
14789
15150
  insert(_el$7, () => t.href, _el$9);
14790
15151
  effect((_$p) => setProp(_el$7, "style", {
14791
15152
  fg: theme.textMuted,
14792
- attributes: TextAttributes16.DIM
15153
+ attributes: TextAttributes17.DIM
14793
15154
  }, _$p));
14794
15155
  return _el$7;
14795
15156
  }
@@ -14966,7 +15327,7 @@ function VerticalTable(props) {
14966
15327
  var _el$20 = createElement("text");
14967
15328
  insertNode(_el$20, createTextNode(`\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`));
14968
15329
  effect((_p$) => {
14969
- var _v$4 = theme.textMuted, _v$5 = TextAttributes16.DIM;
15330
+ var _v$4 = theme.textMuted, _v$5 = TextAttributes17.DIM;
14970
15331
  _v$4 !== _p$.e && (_p$.e = setProp(_el$20, "fg", _v$4, _p$.e));
14971
15332
  _v$5 !== _p$.t && (_p$.t = setProp(_el$20, "attributes", _v$5, _p$.t));
14972
15333
  return _p$;
@@ -15057,7 +15418,7 @@ function BlockNode(props) {
15057
15418
  })();
15058
15419
  }
15059
15420
  if (b.kind === "heading") {
15060
- const attrs = b.level === 1 ? TextAttributes16.BOLD | TextAttributes16.UNDERLINE : TextAttributes16.BOLD;
15421
+ const attrs = b.level === 1 ? TextAttributes17.BOLD | TextAttributes17.UNDERLINE : TextAttributes17.BOLD;
15061
15422
  const fg = b.level <= 2 ? theme.accent : theme.text;
15062
15423
  const tokens = parseInline(b.text);
15063
15424
  return (() => {
@@ -15083,7 +15444,7 @@ function BlockNode(props) {
15083
15444
  const checkbox = item.checked ? "[x] " : "[ ] ";
15084
15445
  const bullet = b.ordered ? `${b.start + idx()}. ` : "\u2022 ";
15085
15446
  const prefix = isTask ? checkbox : bullet;
15086
- const labelAttrs = isTask && item.checked ? TextAttributes16.DIM : 0;
15447
+ const labelAttrs = isTask && item.checked ? TextAttributes17.DIM : 0;
15087
15448
  return (() => {
15088
15449
  var _el$30 = createElement("text"), _el$31 = createElement("span");
15089
15450
  insertNode(_el$30, _el$31);
@@ -15130,9 +15491,9 @@ function BlockNode(props) {
15130
15491
  }
15131
15492
  }), null);
15132
15493
  effect((_p$) => {
15133
- var _v$10 = theme.textMuted, _v$11 = TextAttributes16.ITALIC, _v$12 = {
15494
+ var _v$10 = theme.textMuted, _v$11 = TextAttributes17.ITALIC, _v$12 = {
15134
15495
  fg: theme.textMuted,
15135
- attributes: TextAttributes16.DIM
15496
+ attributes: TextAttributes17.DIM
15136
15497
  };
15137
15498
  _v$10 !== _p$.e && (_p$.e = setProp(_el$33, "fg", _v$10, _p$.e));
15138
15499
  _v$11 !== _p$.t && (_p$.t = setProp(_el$33, "attributes", _v$11, _p$.t));
@@ -15174,7 +15535,7 @@ function BlockNode(props) {
15174
15535
  var _el$37 = createElement("text");
15175
15536
  insert(_el$37, () => b.lang);
15176
15537
  effect((_p$) => {
15177
- var _v$13 = theme.textMuted, _v$14 = TextAttributes16.DIM;
15538
+ var _v$13 = theme.textMuted, _v$14 = TextAttributes17.DIM;
15178
15539
  _v$13 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$13, _p$.e));
15179
15540
  _v$14 !== _p$.t && (_p$.t = setProp(_el$37, "attributes", _v$14, _p$.t));
15180
15541
  return _p$;
@@ -15234,7 +15595,7 @@ var init_Markdown = __esm(() => {
15234
15595
  });
15235
15596
 
15236
15597
  // src/tui/component/update-dialog.tsx
15237
- import { TextAttributes as TextAttributes17 } from "@opentui/core";
15598
+ import { TextAttributes as TextAttributes18 } from "@opentui/core";
15238
15599
  function UpdateDialog(props) {
15239
15600
  const dialog = useDialog();
15240
15601
  const {
@@ -15243,11 +15604,11 @@ function UpdateDialog(props) {
15243
15604
  const [notes] = createResource(() => fetchReleaseNotes(props.info.latest));
15244
15605
  const fallbackUrl = () => releasePageUrl(props.info.latest);
15245
15606
  return (() => {
15246
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createTextNode(`v`), _el$0 = createElement("text"), _el$10 = createElement("text"), _el$11 = createTextNode(`v`), _el$12 = createElement("box"), _el$13 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createElement("box"), _el$18 = createElement("text"), _el$20 = createElement("box");
15607
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createTextNode(`v`), _el$0 = createElement("text"), _el$10 = createElement("text"), _el$11 = createTextNode(`v`), _el$12 = createElement("box"), _el$13 = createElement("text"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createElement("box"), _el$18 = createElement("text"), _el$19 = createTextNode(`Manual fallback: `), _el$20 = createElement("box"), _el$21 = createElement("text"), _el$23 = createElement("box");
15247
15608
  insertNode(_el$, _el$2);
15248
15609
  insertNode(_el$, _el$7);
15249
15610
  insertNode(_el$, _el$12);
15250
- insertNode(_el$, _el$17);
15611
+ insertNode(_el$, _el$20);
15251
15612
  setProp(_el$, "paddingLeft", 2);
15252
15613
  setProp(_el$, "paddingRight", 2);
15253
15614
  setProp(_el$, "gap", 1);
@@ -15270,86 +15631,91 @@ function UpdateDialog(props) {
15270
15631
  insert(_el$10, () => props.info.latest, null);
15271
15632
  insertNode(_el$12, _el$13);
15272
15633
  insertNode(_el$12, _el$15);
15634
+ insertNode(_el$12, _el$17);
15273
15635
  setProp(_el$12, "gap", 0);
15274
15636
  insertNode(_el$13, createTextNode(`Run this to update:`));
15275
15637
  insertNode(_el$15, _el$16);
15276
15638
  setProp(_el$15, "paddingLeft", 2);
15277
- insert(_el$16, INSTALL_COMMAND);
15639
+ insert(_el$16, UPDATE_COMMAND);
15278
15640
  insertNode(_el$17, _el$18);
15279
- insertNode(_el$17, _el$20);
15280
- setProp(_el$17, "gap", 0);
15281
- insertNode(_el$18, createTextNode(`What's new:`));
15282
- setProp(_el$20, "paddingLeft", 2);
15283
- setProp(_el$20, "paddingTop", 1);
15284
- insert(_el$20, createComponent2(Switch, {
15641
+ setProp(_el$17, "paddingLeft", 2);
15642
+ insertNode(_el$18, _el$19);
15643
+ insert(_el$18, recommendedGlobalInstallCommand, null);
15644
+ insertNode(_el$20, _el$21);
15645
+ insertNode(_el$20, _el$23);
15646
+ setProp(_el$20, "gap", 0);
15647
+ insertNode(_el$21, createTextNode(`What's new:`));
15648
+ setProp(_el$23, "paddingLeft", 2);
15649
+ setProp(_el$23, "paddingTop", 1);
15650
+ insert(_el$23, createComponent2(Switch, {
15285
15651
  get children() {
15286
15652
  return [createComponent2(Match, {
15287
15653
  get when() {
15288
15654
  return notes.loading;
15289
15655
  },
15290
15656
  get children() {
15291
- var _el$21 = createElement("text");
15292
- insertNode(_el$21, createTextNode(`Loading release notes\u2026`));
15293
- effect((_$p) => setProp(_el$21, "fg", theme.textMuted, _$p));
15294
- return _el$21;
15657
+ var _el$24 = createElement("text");
15658
+ insertNode(_el$24, createTextNode(`Loading release notes\u2026`));
15659
+ effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
15660
+ return _el$24;
15295
15661
  }
15296
15662
  }), createComponent2(Match, {
15297
15663
  get when() {
15298
15664
  return notes() === null;
15299
15665
  },
15300
15666
  get children() {
15301
- var _el$23 = createElement("box"), _el$24 = createElement("text");
15302
- insertNode(_el$23, _el$24);
15303
- setProp(_el$23, "gap", 0);
15304
- insertNode(_el$24, createTextNode(`(couldn't reach GitHub \u2014 see the release page directly)`));
15305
- insert(_el$23, createComponent2(Show, {
15667
+ var _el$26 = createElement("box"), _el$27 = createElement("text");
15668
+ insertNode(_el$26, _el$27);
15669
+ setProp(_el$26, "gap", 0);
15670
+ insertNode(_el$27, createTextNode(`(couldn't reach GitHub \u2014 see the release page directly)`));
15671
+ insert(_el$26, createComponent2(Show, {
15306
15672
  get when() {
15307
15673
  return fallbackUrl();
15308
15674
  },
15309
15675
  get children() {
15310
- var _el$26 = createElement("text");
15311
- insert(_el$26, fallbackUrl);
15312
- effect((_$p) => setProp(_el$26, "fg", theme.accent, _$p));
15313
- return _el$26;
15676
+ var _el$29 = createElement("text");
15677
+ insert(_el$29, fallbackUrl);
15678
+ effect((_$p) => setProp(_el$29, "fg", theme.accent, _$p));
15679
+ return _el$29;
15314
15680
  }
15315
15681
  }), null);
15316
- effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
15317
- return _el$23;
15682
+ effect((_$p) => setProp(_el$27, "fg", theme.textMuted, _$p));
15683
+ return _el$26;
15318
15684
  }
15319
15685
  }), createComponent2(Match, {
15320
15686
  get when() {
15321
15687
  return notes();
15322
15688
  },
15323
15689
  get children() {
15324
- var _el$27 = createElement("box");
15325
- setProp(_el$27, "flexDirection", "column");
15326
- setProp(_el$27, "gap", 0);
15327
- insert(_el$27, createComponent2(Markdown, {
15690
+ var _el$30 = createElement("box");
15691
+ setProp(_el$30, "flexDirection", "column");
15692
+ setProp(_el$30, "gap", 0);
15693
+ insert(_el$30, createComponent2(Markdown, {
15328
15694
  get source() {
15329
15695
  return notes()?.body ?? "";
15330
15696
  }
15331
15697
  }), null);
15332
- insert(_el$27, createComponent2(Show, {
15698
+ insert(_el$30, createComponent2(Show, {
15333
15699
  get when() {
15334
15700
  return notes()?.url;
15335
15701
  },
15336
15702
  get children() {
15337
- var _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(`Full release: `);
15338
- insertNode(_el$28, _el$29);
15339
- setProp(_el$28, "paddingTop", 1);
15340
- insertNode(_el$29, _el$30);
15341
- insert(_el$29, () => notes()?.url, null);
15342
- effect((_$p) => setProp(_el$29, "fg", theme.textMuted, _$p));
15343
- return _el$28;
15703
+ var _el$31 = createElement("box"), _el$32 = createElement("text"), _el$33 = createTextNode(`Full release: `);
15704
+ insertNode(_el$31, _el$32);
15705
+ setProp(_el$31, "paddingTop", 1);
15706
+ insertNode(_el$32, _el$33);
15707
+ insert(_el$32, () => notes()?.url, null);
15708
+ effect((_$p) => setProp(_el$32, "fg", theme.textMuted, _$p));
15709
+ return _el$31;
15344
15710
  }
15345
15711
  }), null);
15346
- return _el$27;
15712
+ return _el$30;
15347
15713
  }
15348
15714
  })];
15349
15715
  }
15350
15716
  }));
15351
15717
  effect((_p$) => {
15352
- var _v$ = TextAttributes17.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted, _v$5 = theme.textMuted, _v$6 = theme.warning, _v$7 = TextAttributes17.BOLD, _v$8 = theme.textMuted, _v$9 = theme.accent, _v$0 = TextAttributes17.BOLD, _v$1 = theme.textMuted;
15718
+ var _v$ = TextAttributes18.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted, _v$5 = theme.textMuted, _v$6 = theme.warning, _v$7 = TextAttributes18.BOLD, _v$8 = theme.textMuted, _v$9 = theme.accent, _v$0 = TextAttributes18.BOLD, _v$1 = theme.textMuted, _v$10 = theme.textMuted;
15353
15719
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
15354
15720
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
15355
15721
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -15361,6 +15727,7 @@ function UpdateDialog(props) {
15361
15727
  _v$9 !== _p$.r && (_p$.r = setProp(_el$16, "fg", _v$9, _p$.r));
15362
15728
  _v$0 !== _p$.d && (_p$.d = setProp(_el$16, "attributes", _v$0, _p$.d));
15363
15729
  _v$1 !== _p$.l && (_p$.l = setProp(_el$18, "fg", _v$1, _p$.l));
15730
+ _v$10 !== _p$.u && (_p$.u = setProp(_el$21, "fg", _v$10, _p$.u));
15364
15731
  return _p$;
15365
15732
  }, {
15366
15733
  e: undefined,
@@ -15373,7 +15740,8 @@ function UpdateDialog(props) {
15373
15740
  h: undefined,
15374
15741
  r: undefined,
15375
15742
  d: undefined,
15376
- l: undefined
15743
+ l: undefined,
15744
+ u: undefined
15377
15745
  });
15378
15746
  return _el$;
15379
15747
  })();
@@ -15400,7 +15768,7 @@ var init_update_dialog = __esm(() => {
15400
15768
  });
15401
15769
 
15402
15770
  // src/tui/component/top-bar.tsx
15403
- import { TextAttributes as TextAttributes18 } from "@opentui/core";
15771
+ import { TextAttributes as TextAttributes19 } from "@opentui/core";
15404
15772
  function TopBar(props) {
15405
15773
  const {
15406
15774
  theme
@@ -15410,10 +15778,10 @@ function TopBar(props) {
15410
15778
  const rcBridge = props.orchestrator.rcBridgeSignal();
15411
15779
  const isBridgeOn = () => rcBridge().state === "running" || rcBridge().state === "starting";
15412
15780
  return (() => {
15413
- var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$6 = createTextNode(`v`), _el$10 = createElement("box"), _el$12 = createElement("box");
15781
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$6 = createTextNode(`v`), _el$11 = createElement("box"), _el$13 = createElement("box");
15414
15782
  insertNode(_el$, _el$2);
15415
- insertNode(_el$, _el$10);
15416
- insertNode(_el$, _el$12);
15783
+ insertNode(_el$, _el$11);
15784
+ insertNode(_el$, _el$13);
15417
15785
  setProp(_el$, "flexDirection", "row");
15418
15786
  setProp(_el$, "paddingLeft", 2);
15419
15787
  setProp(_el$, "paddingRight", 2);
@@ -15444,7 +15812,7 @@ function TopBar(props) {
15444
15812
  });
15445
15813
  insert(_el$7, () => props.updateInfo()?.latest, _el$9);
15446
15814
  effect((_p$) => {
15447
- var _v$ = theme.warning, _v$2 = TextAttributes18.BOLD;
15815
+ var _v$ = theme.warning, _v$2 = TextAttributes19.BOLD;
15448
15816
  _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
15449
15817
  _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "attributes", _v$2, _p$.t));
15450
15818
  return _p$;
@@ -15472,7 +15840,7 @@ function TopBar(props) {
15472
15840
  return () => _c$() ? rcBridge().bound?.taskTitle ?? rcBridge().envId ?? "RC" : "RC connecting\u2026";
15473
15841
  })(), null);
15474
15842
  effect((_p$) => {
15475
- var _v$3 = theme.accent, _v$4 = TextAttributes18.BOLD;
15843
+ var _v$3 = theme.accent, _v$4 = TextAttributes19.BOLD;
15476
15844
  _v$3 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$3, _p$.e));
15477
15845
  _v$4 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$4, _p$.t));
15478
15846
  return _p$;
@@ -15483,47 +15851,56 @@ function TopBar(props) {
15483
15851
  return _el$0;
15484
15852
  }
15485
15853
  }), null);
15486
- setProp(_el$10, "flexDirection", "row");
15487
- setProp(_el$10, "flexGrow", 1);
15488
- setProp(_el$10, "flexShrink", 1);
15489
- setProp(_el$10, "flexBasis", 0);
15490
- setProp(_el$10, "gap", 1);
15491
- setProp(_el$10, "justifyContent", "center");
15492
- insert(_el$10, createComponent2(Show, {
15854
+ setProp(_el$11, "flexDirection", "row");
15855
+ setProp(_el$11, "flexGrow", 1);
15856
+ setProp(_el$11, "flexShrink", 1);
15857
+ setProp(_el$11, "flexBasis", 0);
15858
+ setProp(_el$11, "gap", 1);
15859
+ setProp(_el$11, "justifyContent", "center");
15860
+ insert(_el$11, createComponent2(Show, {
15493
15861
  get when() {
15494
15862
  return props.activeTask() !== undefined;
15495
15863
  },
15496
15864
  get children() {
15497
- var _el$11 = createElement("text");
15498
- setProp(_el$11, "wrapMode", "none");
15499
- insert(_el$11, () => props.activeTask()?.branch);
15865
+ var _el$12 = createElement("text");
15866
+ setProp(_el$12, "wrapMode", "none");
15867
+ insert(_el$12, () => props.activeTask()?.branch);
15500
15868
  effect((_p$) => {
15501
- var _v$5 = theme.text, _v$6 = TextAttributes18.BOLD;
15502
- _v$5 !== _p$.e && (_p$.e = setProp(_el$11, "fg", _v$5, _p$.e));
15503
- _v$6 !== _p$.t && (_p$.t = setProp(_el$11, "attributes", _v$6, _p$.t));
15869
+ var _v$5 = theme.text, _v$6 = TextAttributes19.BOLD;
15870
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$5, _p$.e));
15871
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$12, "attributes", _v$6, _p$.t));
15504
15872
  return _p$;
15505
15873
  }, {
15506
15874
  e: undefined,
15507
15875
  t: undefined
15508
15876
  });
15509
- return _el$11;
15877
+ return _el$12;
15510
15878
  }
15511
15879
  }));
15512
- setProp(_el$12, "flexDirection", "row");
15513
- setProp(_el$12, "flexGrow", 1);
15514
- setProp(_el$12, "flexShrink", 1);
15515
- setProp(_el$12, "flexBasis", 0);
15516
- setProp(_el$12, "justifyContent", "flex-end");
15517
- insert(_el$12, createComponent2(CreatePRButton, {
15880
+ setProp(_el$13, "flexDirection", "row");
15881
+ setProp(_el$13, "flexGrow", 1);
15882
+ setProp(_el$13, "flexShrink", 1);
15883
+ setProp(_el$13, "flexBasis", 0);
15884
+ setProp(_el$13, "gap", 2);
15885
+ setProp(_el$13, "justifyContent", "flex-end");
15886
+ insert(_el$13, createComponent2(OpenWorktreeButton, {
15887
+ get activeTask() {
15888
+ return props.activeTask;
15889
+ },
15890
+ get opener() {
15891
+ return props.worktreeOpener;
15892
+ }
15893
+ }), null);
15894
+ insert(_el$13, createComponent2(CreatePRButton, {
15518
15895
  get orchestrator() {
15519
15896
  return props.orchestrator;
15520
15897
  },
15521
15898
  get activeTask() {
15522
15899
  return props.activeTask;
15523
15900
  }
15524
- }));
15901
+ }), null);
15525
15902
  effect((_p$) => {
15526
- var _v$7 = theme.primary, _v$8 = TextAttributes18.BOLD, _v$9 = theme.textMuted;
15903
+ var _v$7 = theme.primary, _v$8 = TextAttributes19.BOLD, _v$9 = theme.textMuted;
15527
15904
  _v$7 !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$7, _p$.e));
15528
15905
  _v$8 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$8, _p$.t));
15529
15906
  _v$9 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$9, _p$.a));
@@ -15551,6 +15928,7 @@ var init_top_bar = __esm(() => {
15551
15928
  init_theme();
15552
15929
  init_dialog();
15553
15930
  init_create_pr_button();
15931
+ init_open_worktree_button();
15554
15932
  init_rc_bridge_dialog();
15555
15933
  init_update_dialog();
15556
15934
  });
@@ -15950,10 +16328,10 @@ var init_sessions = __esm(() => {
15950
16328
  });
15951
16329
 
15952
16330
  // src/engine/claude-code-local/spawn.ts
15953
- import { spawn as spawn4 } from "child_process";
16331
+ import { spawn as spawn5 } from "child_process";
15954
16332
  function spawnClaudeProcess(opts) {
15955
16333
  const args2 = buildArgs(opts);
15956
- const proc = spawn4(opts.binaryPath, args2, {
16334
+ const proc = spawn5(opts.binaryPath, args2, {
15957
16335
  cwd: opts.cwd,
15958
16336
  env: { ...process.env, ...opts.env ?? {} },
15959
16337
  stdio: ["pipe", "pipe", "pipe"]
@@ -16903,8 +17281,8 @@ function validateRepoPath(repo) {
16903
17281
  if (!stat3.isDirectory())
16904
17282
  return `not a directory: ${trimmed}`;
16905
17283
  try {
16906
- const { spawnSync: spawnSync8 } = __require("child_process");
16907
- const out = spawnSync8("git", ["rev-parse", "--git-dir"], {
17284
+ const { spawnSync: spawnSync9 } = __require("child_process");
17285
+ const out = spawnSync9("git", ["rev-parse", "--git-dir"], {
16908
17286
  cwd: trimmed,
16909
17287
  encoding: "utf-8",
16910
17288
  timeout: 2000,
@@ -16921,8 +17299,8 @@ function getCurrentBranch(repo) {
16921
17299
  if (!repo)
16922
17300
  return null;
16923
17301
  try {
16924
- const { spawnSync: spawnSync8 } = __require("child_process");
16925
- const out = spawnSync8("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
17302
+ const { spawnSync: spawnSync9 } = __require("child_process");
17303
+ const out = spawnSync9("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
16926
17304
  cwd: repo,
16927
17305
  encoding: "utf-8",
16928
17306
  timeout: 2000,
@@ -16942,8 +17320,8 @@ function listLocalBranches(repo) {
16942
17320
  if (!repo)
16943
17321
  return [];
16944
17322
  try {
16945
- const { spawnSync: spawnSync8 } = __require("child_process");
16946
- const out = spawnSync8("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
17323
+ const { spawnSync: spawnSync9 } = __require("child_process");
17324
+ const out = spawnSync9("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
16947
17325
  cwd: repo,
16948
17326
  encoding: "utf-8",
16949
17327
  timeout: 2000
@@ -17027,7 +17405,7 @@ var DEFAULT_BASE_REF = "main", PICKER_MAX_VISIBLE = 8;
17027
17405
  var init_state2 = () => {};
17028
17406
 
17029
17407
  // src/tui/component/new-task-dialog/dialog.tsx
17030
- import { TextAttributes as TextAttributes19 } from "@opentui/core";
17408
+ import { TextAttributes as TextAttributes20 } from "@opentui/core";
17031
17409
  function NewTaskDialogView(props) {
17032
17410
  const dialog = useDialog();
17033
17411
  const {
@@ -17255,7 +17633,7 @@ function NewTaskDialogView(props) {
17255
17633
  insert(_el$35, suffix, null);
17256
17634
  insert(_el$35, tag, null);
17257
17635
  effect((_p$) => {
17258
- var _v$12 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$13 = isCursor() ? TextAttributes19.BOLD : undefined;
17636
+ var _v$12 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$13 = isCursor() ? TextAttributes20.BOLD : undefined;
17259
17637
  _v$12 !== _p$.e && (_p$.e = setProp(_el$35, "fg", _v$12, _p$.e));
17260
17638
  _v$13 !== _p$.t && (_p$.t = setProp(_el$35, "attributes", _v$13, _p$.t));
17261
17639
  return _p$;
@@ -17369,7 +17747,7 @@ function NewTaskDialogView(props) {
17369
17747
  insert(_el$36, () => isCursor() ? "\u25B8 " : " ", null);
17370
17748
  insert(_el$36, name, null);
17371
17749
  effect((_p$) => {
17372
- var _v$14 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$15 = isCursor() ? TextAttributes19.BOLD : undefined;
17750
+ var _v$14 = isCursor() ? theme.primary : isSelected() ? theme.accent : theme.textMuted, _v$15 = isCursor() ? TextAttributes20.BOLD : undefined;
17373
17751
  _v$14 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$14, _p$.e));
17374
17752
  _v$15 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$15, _p$.t));
17375
17753
  return _p$;
@@ -17407,7 +17785,7 @@ function NewTaskDialogView(props) {
17407
17785
  setProp(_el$34, "onMouseUp", () => commit());
17408
17786
  insert(_el$34, () => field() === "confirm" ? "\u25B8 [ Create ]" : " [ Create ]");
17409
17787
  effect((_p$) => {
17410
- var _v$ = TextAttributes19.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = field() === "repo" ? theme.accent : theme.textMuted, _v$5 = repo(), _v$6 = props.defaultRepo, _v$7 = field() === "repo", _v$8 = field() === "baseRef" ? theme.accent : theme.textMuted, _v$9 = baseRef(), _v$0 = field() === "baseRef", _v$1 = theme.textMuted, _v$10 = field() === "confirm" ? theme.primary : theme.text, _v$11 = field() === "confirm" ? TextAttributes19.BOLD : undefined;
17788
+ var _v$ = TextAttributes20.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = field() === "repo" ? theme.accent : theme.textMuted, _v$5 = repo(), _v$6 = props.defaultRepo, _v$7 = field() === "repo", _v$8 = field() === "baseRef" ? theme.accent : theme.textMuted, _v$9 = baseRef(), _v$0 = field() === "baseRef", _v$1 = theme.textMuted, _v$10 = field() === "confirm" ? theme.primary : theme.text, _v$11 = field() === "confirm" ? TextAttributes20.BOLD : undefined;
17411
17789
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
17412
17790
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
17413
17791
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -17479,7 +17857,7 @@ var init_new_task_dialog = __esm(() => {
17479
17857
  });
17480
17858
 
17481
17859
  // src/tui/component/rename-task-dialog/dialog.tsx
17482
- import { TextAttributes as TextAttributes20 } from "@opentui/core";
17860
+ import { TextAttributes as TextAttributes21 } from "@opentui/core";
17483
17861
  function RenameTaskDialogView(props) {
17484
17862
  const dialog = useDialog();
17485
17863
  const {
@@ -17519,7 +17897,7 @@ function RenameTaskDialogView(props) {
17519
17897
  setProp(_el$0, "paddingBottom", 1);
17520
17898
  insertNode(_el$1, createTextNode(`enter rename \xB7 esc cancel`));
17521
17899
  effect((_p$) => {
17522
- var _v$ = TextAttributes20.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.accent, _v$5 = title(), _v$6 = props.currentTitle, _v$7 = theme.textMuted;
17900
+ var _v$ = TextAttributes21.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.accent, _v$5 = title(), _v$6 = props.currentTitle, _v$7 = theme.textMuted;
17523
17901
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
17524
17902
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
17525
17903
  _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
@@ -17848,7 +18226,7 @@ var init_use_workspace_tabs = __esm(() => {
17848
18226
  });
17849
18227
 
17850
18228
  // src/tui/component/resume-dialog.tsx
17851
- import { TextAttributes as TextAttributes21 } from "@opentui/core";
18229
+ import { TextAttributes as TextAttributes22 } from "@opentui/core";
17852
18230
  function ResumeDialog(props) {
17853
18231
  const dialog = useDialog();
17854
18232
  const {
@@ -17995,7 +18373,7 @@ function ResumeDialog(props) {
17995
18373
  setProp(_el$9, "paddingBottom", 1);
17996
18374
  insertNode(_el$0, createTextNode(`j/k or \u2191\u2193 navigate \u2022 enter resume \u2022 esc dismiss`));
17997
18375
  effect((_p$) => {
17998
- var _v$ = TextAttributes21.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
18376
+ var _v$ = TextAttributes22.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
17999
18377
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
18000
18378
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
18001
18379
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -18062,7 +18440,7 @@ var init_resume_dialog = __esm(() => {
18062
18440
  });
18063
18441
 
18064
18442
  // src/tui/panes/chat/composer/clipboard-image.ts
18065
- import { spawnSync as spawnSync8 } from "child_process";
18443
+ import { spawnSync as spawnSync9 } from "child_process";
18066
18444
  import { statSync as statSync4 } from "fs";
18067
18445
  function clipboardImageSupported() {
18068
18446
  return process.platform === "darwin";
@@ -18087,7 +18465,7 @@ function readClipboardImageMacOS(destPath) {
18087
18465
  "end try"
18088
18466
  ].join(`
18089
18467
  `);
18090
- const result = spawnSync8("osascript", ["-e", script], {
18468
+ const result = spawnSync9("osascript", ["-e", script], {
18091
18469
  timeout: 5000,
18092
18470
  stdio: ["ignore", "ignore", "ignore"]
18093
18471
  });
@@ -18135,9 +18513,9 @@ var init_history2 = __esm(() => {
18135
18513
  // src/tui/panes/chat/composer/image-paste.ts
18136
18514
  import { randomUUID as randomUUID2 } from "crypto";
18137
18515
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
18138
- import { join as join13 } from "path";
18516
+ import { join as join14 } from "path";
18139
18517
  function pastedImagesDir() {
18140
- return join13(kobeStateDir(), "pasted-images");
18518
+ return join14(kobeStateDir(), "pasted-images");
18141
18519
  }
18142
18520
 
18143
18521
  class ImagePasteRegistry {
@@ -18217,7 +18595,7 @@ function mimeTypeToExt(mimeType) {
18217
18595
  return ".png";
18218
18596
  }
18219
18597
  function mintPath(ext) {
18220
- return join13(pastedImagesDir(), `${randomUUID2()}${ext}`);
18598
+ return join14(pastedImagesDir(), `${randomUUID2()}${ext}`);
18221
18599
  }
18222
18600
  var IMAGE_TOKEN_RE;
18223
18601
  var init_image_paste = __esm(() => {
@@ -18320,7 +18698,7 @@ var init_mention = __esm(() => {
18320
18698
  });
18321
18699
 
18322
18700
  // src/tui/panes/chat/Composer.tsx
18323
- import { TextAttributes as TextAttributes22 } from "@opentui/core";
18701
+ import { TextAttributes as TextAttributes23 } from "@opentui/core";
18324
18702
  function resolvePlaceholder(opts) {
18325
18703
  if (!opts.hasTask)
18326
18704
  return opts.noTaskMessage ?? "(no task \u2014 press n to create)";
@@ -18888,7 +19266,7 @@ function Composer(props) {
18888
19266
  }
18889
19267
  }), null);
18890
19268
  effect((_p$) => {
18891
- var _v$23 = active() ? theme.primary : theme.text, _v$24 = active() ? TextAttributes22.BOLD : undefined;
19269
+ var _v$23 = active() ? theme.primary : theme.text, _v$24 = active() ? TextAttributes23.BOLD : undefined;
18892
19270
  _v$23 !== _p$.e && (_p$.e = setProp(_el$33, "fg", _v$23, _p$.e));
18893
19271
  _v$24 !== _p$.t && (_p$.t = setProp(_el$33, "attributes", _v$24, _p$.t));
18894
19272
  return _p$;
@@ -18995,7 +19373,7 @@ function Composer(props) {
18995
19373
  }
18996
19374
  }), null);
18997
19375
  effect((_p$) => {
18998
- var _v$25 = active() ? theme.primary : theme.text, _v$26 = active() ? TextAttributes22.BOLD : undefined;
19376
+ var _v$25 = active() ? theme.primary : theme.text, _v$26 = active() ? TextAttributes23.BOLD : undefined;
18999
19377
  _v$25 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$25, _p$.e));
19000
19378
  _v$26 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$26, _p$.t));
19001
19379
  return _p$;
@@ -19079,7 +19457,7 @@ function Composer(props) {
19079
19457
  insertNode(_el$50, createTextNode(`[x]`));
19080
19458
  setProp(_el$50, "onMouseUp", () => props.onCancelQueued?.(entry.id));
19081
19459
  effect((_p$) => {
19082
- var _v$27 = theme.textMuted, _v$28 = TextAttributes22.BOLD, _v$29 = theme.textMuted, _v$30 = theme.text, _v$31 = theme.primary, _v$32 = TextAttributes22.BOLD, _v$33 = theme.error, _v$34 = TextAttributes22.BOLD;
19460
+ var _v$27 = theme.textMuted, _v$28 = TextAttributes23.BOLD, _v$29 = theme.textMuted, _v$30 = theme.text, _v$31 = theme.primary, _v$32 = TextAttributes23.BOLD, _v$33 = theme.error, _v$34 = TextAttributes23.BOLD;
19083
19461
  _v$27 !== _p$.e && (_p$.e = setProp(_el$41, "fg", _v$27, _p$.e));
19084
19462
  _v$28 !== _p$.t && (_p$.t = setProp(_el$41, "attributes", _v$28, _p$.t));
19085
19463
  _v$29 !== _p$.a && (_p$.a = setProp(_el$43, "fg", _v$29, _p$.a));
@@ -19116,7 +19494,7 @@ function Composer(props) {
19116
19494
  insertNode(_el$18, createTextNode(`+`));
19117
19495
  insert(_el$20, () => `\u2026 ${(props.queue?.() ?? []).length - QUEUE_VISIBLE_CAP} more queued`);
19118
19496
  effect((_p$) => {
19119
- var _v$7 = theme.textMuted, _v$8 = TextAttributes22.BOLD, _v$9 = theme.textMuted;
19497
+ var _v$7 = theme.textMuted, _v$8 = TextAttributes23.BOLD, _v$9 = theme.textMuted;
19120
19498
  _v$7 !== _p$.e && (_p$.e = setProp(_el$18, "fg", _v$7, _p$.e));
19121
19499
  _v$8 !== _p$.t && (_p$.t = setProp(_el$18, "attributes", _v$8, _p$.t));
19122
19500
  _v$9 !== _p$.a && (_p$.a = setProp(_el$20, "fg", _v$9, _p$.a));
@@ -19918,7 +20296,7 @@ function summarizeGlob(input, output, done) {
19918
20296
  }
19919
20297
 
19920
20298
  // src/tui/panes/chat/MessageList.tsx
19921
- import { TextAttributes as TextAttributes23 } from "@opentui/core";
20299
+ import { TextAttributes as TextAttributes24 } from "@opentui/core";
19922
20300
  function previewToolInput(input) {
19923
20301
  if (input == null)
19924
20302
  return "";
@@ -20014,7 +20392,7 @@ function UserRow(props) {
20014
20392
  })() : null;
20015
20393
  })(), null);
20016
20394
  effect((_p$) => {
20017
- var _v$ = theme.accent, _v$2 = TextAttributes23.BOLD, _v$3 = theme.primary, _v$4 = TextAttributes23.BOLD;
20395
+ var _v$ = theme.accent, _v$2 = TextAttributes24.BOLD, _v$3 = theme.primary, _v$4 = TextAttributes24.BOLD;
20018
20396
  _v$ !== _p$.e && (_p$.e = setProp(_el$2, "fg", _v$, _p$.e));
20019
20397
  _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "attributes", _v$2, _p$.t));
20020
20398
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -20094,7 +20472,7 @@ function UserRow(props) {
20094
20472
  setProp(_el$21, "flexGrow", 1);
20095
20473
  insert(_el$22, () => view.text);
20096
20474
  effect((_p$) => {
20097
- var _v$9 = theme.accent, _v$0 = TextAttributes23.BOLD, _v$1 = theme.text;
20475
+ var _v$9 = theme.accent, _v$0 = TextAttributes24.BOLD, _v$1 = theme.text;
20098
20476
  _v$9 !== _p$.e && (_p$.e = setProp(_el$19, "fg", _v$9, _p$.e));
20099
20477
  _v$0 !== _p$.t && (_p$.t = setProp(_el$19, "attributes", _v$0, _p$.t));
20100
20478
  _v$1 !== _p$.a && (_p$.a = setProp(_el$22, "fg", _v$1, _p$.a));
@@ -20130,7 +20508,7 @@ function AssistantRow(props) {
20130
20508
  }
20131
20509
  }));
20132
20510
  effect((_p$) => {
20133
- var _v$10 = theme.accent, _v$11 = TextAttributes23.BOLD;
20511
+ var _v$10 = theme.accent, _v$11 = TextAttributes24.BOLD;
20134
20512
  _v$10 !== _p$.e && (_p$.e = setProp(_el$25, "fg", _v$10, _p$.e));
20135
20513
  _v$11 !== _p$.t && (_p$.t = setProp(_el$25, "attributes", _v$11, _p$.t));
20136
20514
  return _p$;
@@ -20205,7 +20583,7 @@ function ToolRow(props) {
20205
20583
  }), null);
20206
20584
  effect((_p$) => {
20207
20585
  var _v$18 = theme.text, _v$19 = {
20208
- attributes: TextAttributes23.BOLD
20586
+ attributes: TextAttributes24.BOLD
20209
20587
  };
20210
20588
  _v$18 !== _p$.e && (_p$.e = setProp(_el$45, "fg", _v$18, _p$.e));
20211
20589
  _v$19 !== _p$.t && (_p$.t = setProp(_el$46, "style", _v$19, _p$.t));
@@ -20373,7 +20751,7 @@ function ToolRow(props) {
20373
20751
  }
20374
20752
  }), null);
20375
20753
  effect((_p$) => {
20376
- var _v$16 = prefixColor(), _v$17 = TextAttributes23.BOLD;
20754
+ var _v$16 = prefixColor(), _v$17 = TextAttributes24.BOLD;
20377
20755
  _v$16 !== _p$.e && (_p$.e = setProp(_el$29, "fg", _v$16, _p$.e));
20378
20756
  _v$17 !== _p$.t && (_p$.t = setProp(_el$29, "attributes", _v$17, _p$.t));
20379
20757
  return _p$;
@@ -20612,7 +20990,7 @@ function BashBanner(props) {
20612
20990
  }
20613
20991
  }), null);
20614
20992
  effect((_p$) => {
20615
- var _v$28 = theme.accent, _v$29 = TextAttributes23.BOLD, _v$30 = theme.text;
20993
+ var _v$28 = theme.accent, _v$29 = TextAttributes24.BOLD, _v$30 = theme.text;
20616
20994
  _v$28 !== _p$.e && (_p$.e = setProp(_el$73, "fg", _v$28, _p$.e));
20617
20995
  _v$29 !== _p$.t && (_p$.t = setProp(_el$73, "attributes", _v$29, _p$.t));
20618
20996
  _v$30 !== _p$.a && (_p$.a = setProp(_el$76, "fg", _v$30, _p$.a));
@@ -20700,7 +21078,7 @@ function ReadGrepGlobBanner(props) {
20700
21078
  }), null);
20701
21079
  effect((_p$) => {
20702
21080
  var _v$31 = theme.text, _v$32 = {
20703
- attributes: TextAttributes23.BOLD
21081
+ attributes: TextAttributes24.BOLD
20704
21082
  };
20705
21083
  _v$31 !== _p$.e && (_p$.e = setProp(_el$81, "fg", _v$31, _p$.e));
20706
21084
  _v$32 !== _p$.t && (_p$.t = setProp(_el$82, "style", _v$32, _p$.t));
@@ -20729,7 +21107,7 @@ function SystemRow(props) {
20729
21107
  setProp(_el$87, "flexGrow", 1);
20730
21108
  insert(_el$88, () => props.text);
20731
21109
  effect((_p$) => {
20732
- var _v$33 = theme.textMuted, _v$34 = TextAttributes23.DIM, _v$35 = isError() ? theme.error : theme.textMuted;
21110
+ var _v$33 = theme.textMuted, _v$34 = TextAttributes24.DIM, _v$35 = isError() ? theme.error : theme.textMuted;
20733
21111
  _v$33 !== _p$.e && (_p$.e = setProp(_el$85, "fg", _v$33, _p$.e));
20734
21112
  _v$34 !== _p$.t && (_p$.t = setProp(_el$85, "attributes", _v$34, _p$.t));
20735
21113
  _v$35 !== _p$.a && (_p$.a = setProp(_el$88, "fg", _v$35, _p$.a));
@@ -20819,7 +21197,7 @@ function ApprovalRow(props) {
20819
21197
  insertNode(_el$103, _el$105);
20820
21198
  insert(_el$103, () => r().status, _el$105);
20821
21199
  effect((_p$) => {
20822
- var _v$44 = r().status === "approved" ? theme.success : theme.error, _v$45 = TextAttributes23.BOLD;
21200
+ var _v$44 = r().status === "approved" ? theme.success : theme.error, _v$45 = TextAttributes24.BOLD;
20823
21201
  _v$44 !== _p$.e && (_p$.e = setProp(_el$103, "fg", _v$44, _p$.e));
20824
21202
  _v$45 !== _p$.t && (_p$.t = setProp(_el$103, "attributes", _v$45, _p$.t));
20825
21203
  return _p$;
@@ -20836,7 +21214,7 @@ function ApprovalRow(props) {
20836
21214
  insertNode(_el$99, createTextNode(`[ Approve ]`));
20837
21215
  setProp(_el$99, "onMouseUp", () => props.onApprove(true));
20838
21216
  effect((_p$) => {
20839
- var _v$36 = theme.success, _v$37 = TextAttributes23.BOLD;
21217
+ var _v$36 = theme.success, _v$37 = TextAttributes24.BOLD;
20840
21218
  _v$36 !== _p$.e && (_p$.e = setProp(_el$99, "fg", _v$36, _p$.e));
20841
21219
  _v$37 !== _p$.t && (_p$.t = setProp(_el$99, "attributes", _v$37, _p$.t));
20842
21220
  return _p$;
@@ -20850,7 +21228,7 @@ function ApprovalRow(props) {
20850
21228
  insertNode(_el$101, createTextNode(`[ Reject ]`));
20851
21229
  setProp(_el$101, "onMouseUp", () => props.onApprove(false));
20852
21230
  effect((_p$) => {
20853
- var _v$38 = theme.error, _v$39 = TextAttributes23.BOLD;
21231
+ var _v$38 = theme.error, _v$39 = TextAttributes24.BOLD;
20854
21232
  _v$38 !== _p$.e && (_p$.e = setProp(_el$101, "fg", _v$38, _p$.e));
20855
21233
  _v$39 !== _p$.t && (_p$.t = setProp(_el$101, "attributes", _v$39, _p$.t));
20856
21234
  return _p$;
@@ -20863,7 +21241,7 @@ function ApprovalRow(props) {
20863
21241
  }
20864
21242
  }));
20865
21243
  effect((_p$) => {
20866
- var _v$40 = headerColor(), _v$41 = TextAttributes23.BOLD, _v$42 = headerColor(), _v$43 = TextAttributes23.BOLD;
21244
+ var _v$40 = headerColor(), _v$41 = TextAttributes24.BOLD, _v$42 = headerColor(), _v$43 = TextAttributes24.BOLD;
20867
21245
  _v$40 !== _p$.e && (_p$.e = setProp(_el$91, "fg", _v$40, _p$.e));
20868
21246
  _v$41 !== _p$.t && (_p$.t = setProp(_el$91, "attributes", _v$41, _p$.t));
20869
21247
  _v$42 !== _p$.a && (_p$.a = setProp(_el$92, "fg", _v$42, _p$.a));
@@ -21085,7 +21463,7 @@ function QuestionRow(props) {
21085
21463
  insertNode(_el$116, _el$118);
21086
21464
  insert(_el$116, () => q.header, _el$118);
21087
21465
  effect((_p$) => {
21088
- var _v$52 = theme.accent, _v$53 = TextAttributes23.BOLD;
21466
+ var _v$52 = theme.accent, _v$53 = TextAttributes24.BOLD;
21089
21467
  _v$52 !== _p$.e && (_p$.e = setProp(_el$116, "fg", _v$52, _p$.e));
21090
21468
  _v$53 !== _p$.t && (_p$.t = setProp(_el$116, "attributes", _v$53, _p$.t));
21091
21469
  return _p$;
@@ -21199,7 +21577,7 @@ function QuestionRow(props) {
21199
21577
  }
21200
21578
  }), null);
21201
21579
  effect((_p$) => {
21202
- var _v$58 = isHl() ? theme.accent : theme.textMuted, _v$59 = TextAttributes23.BOLD, _v$60 = theme.textMuted, _v$61 = isPicked() ? theme.accent : theme.textMuted, _v$62 = TextAttributes23.BOLD, _v$63 = theme.text;
21580
+ var _v$58 = isHl() ? theme.accent : theme.textMuted, _v$59 = TextAttributes24.BOLD, _v$60 = theme.textMuted, _v$61 = isPicked() ? theme.accent : theme.textMuted, _v$62 = TextAttributes24.BOLD, _v$63 = theme.text;
21203
21581
  _v$58 !== _p$.e && (_p$.e = setProp(_el$136, "fg", _v$58, _p$.e));
21204
21582
  _v$59 !== _p$.t && (_p$.t = setProp(_el$136, "attributes", _v$59, _p$.t));
21205
21583
  _v$60 !== _p$.a && (_p$.a = setProp(_el$137, "fg", _v$60, _p$.a));
@@ -21244,7 +21622,7 @@ function QuestionRow(props) {
21244
21622
  insertNode(_el$147, createTextNode(`Other`));
21245
21623
  insertNode(_el$149, createTextNode(`Type your own answer`));
21246
21624
  effect((_p$) => {
21247
- var _v$64 = isOtherHl() ? theme.accent : theme.textMuted, _v$65 = TextAttributes23.BOLD, _v$66 = theme.textMuted, _v$67 = otherPicked() ? theme.accent : theme.textMuted, _v$68 = TextAttributes23.BOLD, _v$69 = theme.text, _v$70 = theme.textMuted;
21625
+ var _v$64 = isOtherHl() ? theme.accent : theme.textMuted, _v$65 = TextAttributes24.BOLD, _v$66 = theme.textMuted, _v$67 = otherPicked() ? theme.accent : theme.textMuted, _v$68 = TextAttributes24.BOLD, _v$69 = theme.text, _v$70 = theme.textMuted;
21248
21626
  _v$64 !== _p$.e && (_p$.e = setProp(_el$143, "fg", _v$64, _p$.e));
21249
21627
  _v$65 !== _p$.t && (_p$.t = setProp(_el$143, "attributes", _v$65, _p$.t));
21250
21628
  _v$66 !== _p$.a && (_p$.a = setProp(_el$144, "fg", _v$66, _p$.a));
@@ -21300,7 +21678,7 @@ function QuestionRow(props) {
21300
21678
  }
21301
21679
  }), null);
21302
21680
  effect((_p$) => {
21303
- var _v$54 = isQuestionComplete(index()) ? theme.success : theme.textMuted, _v$55 = TextAttributes23.BOLD;
21681
+ var _v$54 = isQuestionComplete(index()) ? theme.success : theme.textMuted, _v$55 = TextAttributes24.BOLD;
21304
21682
  _v$54 !== _p$.e && (_p$.e = setProp(_el$132, "fg", _v$54, _p$.e));
21305
21683
  _v$55 !== _p$.t && (_p$.t = setProp(_el$132, "attributes", _v$55, _p$.t));
21306
21684
  return _p$;
@@ -21336,7 +21714,7 @@ function QuestionRow(props) {
21336
21714
  setProp(_el$111, "paddingTop", 1);
21337
21715
  insertNode(_el$112, createTextNode(`[submitted]`));
21338
21716
  effect((_p$) => {
21339
- var _v$46 = theme.success, _v$47 = TextAttributes23.BOLD;
21717
+ var _v$46 = theme.success, _v$47 = TextAttributes24.BOLD;
21340
21718
  _v$46 !== _p$.e && (_p$.e = setProp(_el$112, "fg", _v$46, _p$.e));
21341
21719
  _v$47 !== _p$.t && (_p$.t = setProp(_el$112, "attributes", _v$47, _p$.t));
21342
21720
  return _p$;
@@ -21348,7 +21726,7 @@ function QuestionRow(props) {
21348
21726
  }
21349
21727
  }), null);
21350
21728
  effect((_p$) => {
21351
- var _v$48 = theme.warning, _v$49 = TextAttributes23.BOLD, _v$50 = theme.warning, _v$51 = TextAttributes23.BOLD;
21729
+ var _v$48 = theme.warning, _v$49 = TextAttributes24.BOLD, _v$50 = theme.warning, _v$51 = TextAttributes24.BOLD;
21352
21730
  _v$48 !== _p$.e && (_p$.e = setProp(_el$108, "fg", _v$48, _p$.e));
21353
21731
  _v$49 !== _p$.t && (_p$.t = setProp(_el$108, "attributes", _v$49, _p$.t));
21354
21732
  _v$50 !== _p$.a && (_p$.a = setProp(_el$110, "fg", _v$50, _p$.a));
@@ -21468,7 +21846,7 @@ function MessageList(props) {
21468
21846
  insertNode(_el$160, _el$161);
21469
21847
  insert(_el$160, () => props.error, null);
21470
21848
  effect((_p$) => {
21471
- var _v$71 = theme.error, _v$72 = TextAttributes23.BOLD, _v$73 = theme.error;
21849
+ var _v$71 = theme.error, _v$72 = TextAttributes24.BOLD, _v$73 = theme.error;
21472
21850
  _v$71 !== _p$.e && (_p$.e = setProp(_el$158, "fg", _v$71, _p$.e));
21473
21851
  _v$72 !== _p$.t && (_p$.t = setProp(_el$158, "attributes", _v$72, _p$.t));
21474
21852
  _v$73 !== _p$.a && (_p$.a = setProp(_el$160, "fg", _v$73, _p$.a));
@@ -21624,7 +22002,7 @@ function ToolFoldRow(props) {
21624
22002
  setProp(_el$164, "flexGrow", 1);
21625
22003
  insert(_el$165, () => props.summary);
21626
22004
  effect((_p$) => {
21627
- var _v$74 = fg(), _v$75 = TextAttributes23.DIM, _v$76 = theme.textMuted;
22005
+ var _v$74 = fg(), _v$75 = TextAttributes24.DIM, _v$76 = theme.textMuted;
21628
22006
  _v$74 !== _p$.e && (_p$.e = setProp(_el$163, "fg", _v$74, _p$.e));
21629
22007
  _v$75 !== _p$.t && (_p$.t = setProp(_el$163, "attributes", _v$75, _p$.t));
21630
22008
  _v$76 !== _p$.a && (_p$.a = setProp(_el$165, "fg", _v$76, _p$.a));
@@ -21689,7 +22067,7 @@ var init_models = __esm(() => {
21689
22067
  });
21690
22068
 
21691
22069
  // src/tui/panes/chat/composer/ModelPicker.tsx
21692
- import { TextAttributes as TextAttributes24 } from "@opentui/core";
22070
+ import { TextAttributes as TextAttributes25 } from "@opentui/core";
21693
22071
  function ModelPicker(props) {
21694
22072
  const dialog = useDialog();
21695
22073
  const {
@@ -21769,7 +22147,7 @@ function ModelPicker(props) {
21769
22147
  })() : null;
21770
22148
  })(), null);
21771
22149
  effect((_p$) => {
21772
- var _v$5 = active() ? theme.primary : undefined, _v$6 = active() ? theme.selectedListItemText : theme.text, _v$7 = active() ? TextAttributes24.BOLD : undefined;
22150
+ var _v$5 = active() ? theme.primary : undefined, _v$6 = active() ? theme.selectedListItemText : theme.text, _v$7 = active() ? TextAttributes25.BOLD : undefined;
21773
22151
  _v$5 !== _p$.e && (_p$.e = setProp(_el$1, "backgroundColor", _v$5, _p$.e));
21774
22152
  _v$6 !== _p$.t && (_p$.t = setProp(_el$10, "fg", _v$6, _p$.t));
21775
22153
  _v$7 !== _p$.a && (_p$.a = setProp(_el$10, "attributes", _v$7, _p$.a));
@@ -21787,7 +22165,7 @@ function ModelPicker(props) {
21787
22165
  setProp(_el$8, "paddingBottom", 1);
21788
22166
  insertNode(_el$9, createTextNode(`\u2191\u2193 pick \xB7 enter select \xB7 esc cancel`));
21789
22167
  effect((_p$) => {
21790
- var _v$ = TextAttributes24.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
22168
+ var _v$ = TextAttributes25.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = theme.textMuted;
21791
22169
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
21792
22170
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
21793
22171
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -21830,7 +22208,7 @@ var init_ModelPicker = __esm(() => {
21830
22208
  // src/tui/panes/chat/composer/user-slashes.ts
21831
22209
  import { readFile as readFile6, readdir as readdir3, stat as stat3 } from "fs/promises";
21832
22210
  import { homedir as homedir11 } from "os";
21833
- import { join as join14 } from "path";
22211
+ import { join as join15 } from "path";
21834
22212
  function resolveHome() {
21835
22213
  return process.env.HOME ?? homedir11();
21836
22214
  }
@@ -21936,7 +22314,7 @@ async function scanCommandsDir(dir) {
21936
22314
  for (const entry of entries) {
21937
22315
  if (!entry.endsWith(".md"))
21938
22316
  continue;
21939
- const full = join14(dir, entry);
22317
+ const full = join15(dir, entry);
21940
22318
  if (!await isFile(full))
21941
22319
  continue;
21942
22320
  const name = entry.slice(0, -3);
@@ -21949,10 +22327,10 @@ async function scanSkillsDir(dir) {
21949
22327
  const entries = await safeReaddir(dir);
21950
22328
  const out = [];
21951
22329
  for (const entry of entries) {
21952
- const sub = join14(dir, entry);
22330
+ const sub = join15(dir, entry);
21953
22331
  if (!await isDir(sub))
21954
22332
  continue;
21955
- const skillMd = join14(sub, "SKILL.md");
22333
+ const skillMd = join15(sub, "SKILL.md");
21956
22334
  if (!await isFile(skillMd))
21957
22335
  continue;
21958
22336
  const description = await tryReadDescription(skillMd) ?? "";
@@ -21962,8 +22340,8 @@ async function scanSkillsDir(dir) {
21962
22340
  }
21963
22341
  async function scanBasePath(claudeDir) {
21964
22342
  const [skills, commands] = await Promise.all([
21965
- scanSkillsDir(join14(claudeDir, "skills")),
21966
- scanCommandsDir(join14(claudeDir, "commands"))
22343
+ scanSkillsDir(join15(claudeDir, "skills")),
22344
+ scanCommandsDir(join15(claudeDir, "commands"))
21967
22345
  ]);
21968
22346
  const map = new Map;
21969
22347
  for (const e of skills)
@@ -21973,8 +22351,8 @@ async function scanBasePath(claudeDir) {
21973
22351
  return [...map.values()];
21974
22352
  }
21975
22353
  async function loadUserSlashes(worktreePath) {
21976
- const projectScan = worktreePath ? scanBasePath(join14(worktreePath, ".claude")) : Promise.resolve([]);
21977
- const globalScan = scanBasePath(join14(resolveHome(), ".claude"));
22354
+ const projectScan = worktreePath ? scanBasePath(join15(worktreePath, ".claude")) : Promise.resolve([]);
22355
+ const globalScan = scanBasePath(join15(resolveHome(), ".claude"));
21978
22356
  const [project, global] = await Promise.all([projectScan, globalScan]);
21979
22357
  const map = new Map;
21980
22358
  for (const e of global)
@@ -21986,93 +22364,30 @@ async function loadUserSlashes(worktreePath) {
21986
22364
  var init_user_slashes = () => {};
21987
22365
 
21988
22366
  // src/tui/panes/chat/context-meter.ts
21989
- function totalContextTokens(u) {
21990
- return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
21991
- }
21992
- function parseTimestampMs(value) {
21993
- const ms = new Date(value).getTime();
21994
- return Number.isFinite(ms) ? ms : null;
21995
- }
21996
- function mergeIntervals(intervals) {
21997
- if (intervals.length === 0)
21998
- return [];
21999
- const sorted = [...intervals].sort((a, b) => a.startMs - b.startMs);
22000
- const first = sorted[0];
22001
- if (!first)
22002
- return [];
22003
- const merged = [{ startMs: first.startMs, endMs: first.endMs }];
22004
- for (let i = 1;i < sorted.length; i++) {
22005
- const current = sorted[i];
22006
- const last = merged[merged.length - 1];
22007
- if (!current || !last)
22008
- continue;
22009
- if (current.startMs <= last.endMs) {
22010
- last.endMs = Math.max(last.endMs, current.endMs);
22011
- } else {
22012
- merged.push({ startMs: current.startMs, endMs: current.endMs });
22367
+ function parseContextWindowSize(modelIdentifier) {
22368
+ const delimitedMatch = /(?:\(|\[)\s*(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])\s*(?:\)|\])/i.exec(modelIdentifier);
22369
+ if (delimitedMatch?.[1] && delimitedMatch[2]) {
22370
+ const parsed2 = Number.parseFloat(delimitedMatch[1].replace(/[,_]/g, ""));
22371
+ if (Number.isFinite(parsed2) && parsed2 > 0) {
22372
+ return Math.round(parsed2 * (delimitedMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
22013
22373
  }
22014
22374
  }
22015
- return merged;
22016
- }
22017
- function durationMs(intervals) {
22018
- return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
22019
- }
22020
- function deriveUsageMetricsFromHistory(past) {
22021
- let latestUsage;
22022
- let latestUsageTimestampMs = null;
22023
- let lastUserTimestampMs = null;
22024
- let inputTokens = 0;
22025
- let outputTokens = 0;
22026
- const intervals = [];
22027
- for (const message of past) {
22028
- const timestampMs = parseTimestampMs(message.timestamp);
22029
- if (message.role === "user" && timestampMs !== null) {
22030
- lastUserTimestampMs = timestampMs;
22031
- continue;
22032
- }
22033
- if (message.role !== "assistant" || !message.usage)
22034
- continue;
22035
- if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
22036
- latestUsageTimestampMs = timestampMs;
22037
- latestUsage = message.usage;
22038
- } else if (latestUsage === undefined) {
22039
- latestUsage = message.usage;
22040
- }
22041
- inputTokens += message.usage.input_tokens;
22042
- outputTokens += message.usage.output_tokens;
22043
- if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
22044
- intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
22045
- }
22046
- }
22047
- if (!latestUsage)
22048
- return;
22049
- const totalDurationMs = durationMs(mergeIntervals(intervals));
22050
- if (totalDurationMs <= 0)
22051
- return latestUsage;
22052
- return {
22053
- ...latestUsage,
22054
- total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
22055
- };
22056
- }
22057
- function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
22058
- const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
22059
- const endMs = parseTimestampMs(endedAtIso);
22060
- if (startMs === null || endMs === null || endMs <= startMs)
22061
- return usage;
22062
- return {
22063
- ...usage,
22064
- total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
22065
- };
22375
+ const contextMatch = /\b(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])(?:\s*(?:token\s*)?context)?\b/i.exec(modelIdentifier);
22376
+ if (!contextMatch?.[1] || !contextMatch[2])
22377
+ return null;
22378
+ const parsed = Number.parseFloat(contextMatch[1].replace(/[,_]/g, ""));
22379
+ if (!Number.isFinite(parsed) || parsed <= 0)
22380
+ return null;
22381
+ return Math.round(parsed * (contextMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
22066
22382
  }
22067
22383
  function contextWindowTokensForModel(modelId) {
22068
22384
  const id = modelId ?? resolveDefaultModelId();
22069
- if (id.includes("[1m]"))
22070
- return LONG_CTX;
22385
+ const parsedWindow = parseContextWindowSize(id);
22386
+ if (parsedWindow !== null)
22387
+ return parsedWindow;
22071
22388
  const inPicker = MODEL_CHOICES.some((m) => m.id === id);
22072
22389
  if (inPicker)
22073
22390
  return STD_CTX;
22074
- if (id.includes("1m") || id.includes("[1M]"))
22075
- return LONG_CTX;
22076
22391
  return STD_CTX;
22077
22392
  }
22078
22393
  function formatTokShort(n) {
@@ -22102,7 +22417,7 @@ function formatContextUsageCompact(u, modelId) {
22102
22417
  const speed = formatTotalSpeed(u.total_speed_tokens_per_second);
22103
22418
  return [`${pct2}% \xB7 ${formatTokShort(total)}/${formatTokShort(window)}`, speed].filter(Boolean).join(" \xB7 ");
22104
22419
  }
22105
- var LONG_CTX = 1e6, STD_CTX = 200000;
22420
+ var STD_CTX = 200000;
22106
22421
  var init_context_meter = __esm(() => {
22107
22422
  init_claude_settings2();
22108
22423
  init_models();
@@ -22146,13 +22461,13 @@ function createInitialState() {
22146
22461
  queue: []
22147
22462
  };
22148
22463
  }
22149
- function setMessagesFromHistory(state, past) {
22464
+ function setMessagesFromHistory(state, past, usageMetrics) {
22150
22465
  const rows = [];
22151
22466
  const toolIndexById = new Map;
22152
22467
  for (const m of past) {
22153
22468
  appendRowsFromMessage(rows, toolIndexById, m);
22154
22469
  }
22155
- const latestUsage = deriveUsageMetricsFromHistory(past);
22470
+ const latestUsage = usageMetrics ?? deriveSessionUsageMetrics(past);
22156
22471
  return {
22157
22472
  ...state,
22158
22473
  messages: capMessages(rows, new Date().toISOString()),
@@ -22422,7 +22737,6 @@ function findLastIndex(arr, pred) {
22422
22737
  }
22423
22738
  var SCROLLBACK_CAP = 1000, SENTINEL_PREFIX = "(scrollback truncated \u2014 ", SENTINEL_SUFFIX = " rows dropped)", CLAUDE_NOISE_TAGS, NOISE_TAG_PATTERN, QUEUE_SOFT_CAP = 50;
22424
22739
  var init_store2 = __esm(() => {
22425
- init_context_meter();
22426
22740
  CLAUDE_NOISE_TAGS = [
22427
22741
  "local-command-caveat",
22428
22742
  "command-message",
@@ -22533,10 +22847,10 @@ function useChatSession(opts) {
22533
22847
  };
22534
22848
  if (tab.sessionId) {
22535
22849
  const sid = tab.sessionId;
22536
- orchestrator.readHistory(sid).then((past) => {
22850
+ orchestrator.readHistoryWithMetrics(sid).then(({ messages, usageMetrics }) => {
22537
22851
  if (opts.taskId() !== taskId)
22538
22852
  return;
22539
- patchStateForTab(tabId, (s) => setMessagesFromHistory(s, past));
22853
+ patchStateForTab(tabId, (s) => setMessagesFromHistory(s, messages, usageMetrics));
22540
22854
  replayPending();
22541
22855
  }).catch((err) => {
22542
22856
  const msg = err instanceof Error ? err.message : String(err);
@@ -23276,7 +23590,7 @@ var init_Chat = __esm(() => {
23276
23590
  });
23277
23591
 
23278
23592
  // src/tui/component/sidebar.tsx
23279
- import { TextAttributes as TextAttributes25 } from "@opentui/core";
23593
+ import { TextAttributes as TextAttributes26 } from "@opentui/core";
23280
23594
  function Sidebar(props) {
23281
23595
  const {
23282
23596
  theme
@@ -23302,7 +23616,7 @@ function Sidebar(props) {
23302
23616
  setProp(_el$2, "paddingBottom", 1);
23303
23617
  insert(_el$3, () => props.title);
23304
23618
  effect((_p$) => {
23305
- var _v$ = theme.text, _v$2 = TextAttributes25.BOLD;
23619
+ var _v$ = theme.text, _v$2 = TextAttributes26.BOLD;
23306
23620
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
23307
23621
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
23308
23622
  return _p$;
@@ -23421,12 +23735,12 @@ var init_sidebar = __esm(() => {
23421
23735
  });
23422
23736
 
23423
23737
  // src/tui/panes/sidebar/git-head.ts
23424
- import { spawnSync as spawnSync9 } from "child_process";
23738
+ import { spawnSync as spawnSync10 } from "child_process";
23425
23739
  function readCurrentBranch(repo) {
23426
23740
  if (!repo)
23427
23741
  return "";
23428
23742
  try {
23429
- const out = spawnSync9("git", ["symbolic-ref", "--short", "HEAD"], {
23743
+ const out = spawnSync10("git", ["symbolic-ref", "--short", "HEAD"], {
23430
23744
  cwd: repo,
23431
23745
  encoding: "utf8",
23432
23746
  stdio: ["ignore", "pipe", "pipe"]
@@ -23436,7 +23750,7 @@ function readCurrentBranch(repo) {
23436
23750
  if (name && name !== "HEAD")
23437
23751
  return name;
23438
23752
  }
23439
- const head = spawnSync9("git", ["rev-parse", "--verify", "HEAD"], {
23753
+ const head = spawnSync10("git", ["rev-parse", "--verify", "HEAD"], {
23440
23754
  cwd: repo,
23441
23755
  encoding: "utf8",
23442
23756
  stdio: ["ignore", "pipe", "pipe"]
@@ -23561,7 +23875,7 @@ var init_keys4 = __esm(() => {
23561
23875
  });
23562
23876
 
23563
23877
  // src/tui/panes/sidebar/Sidebar.tsx
23564
- import { TextAttributes as TextAttributes26 } from "@opentui/core";
23878
+ import { TextAttributes as TextAttributes27 } from "@opentui/core";
23565
23879
  function Sidebar2(props) {
23566
23880
  const {
23567
23881
  theme
@@ -23653,7 +23967,7 @@ function Sidebar2(props) {
23653
23967
  return () => _c$() ? `[ ${tab.label} ]` : tab.label;
23654
23968
  })());
23655
23969
  effect((_p$) => {
23656
- var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ? TextAttributes26.BOLD : undefined;
23970
+ var _v$7 = active() ? theme.primary : theme.textMuted, _v$8 = active() ? TextAttributes27.BOLD : undefined;
23657
23971
  _v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
23658
23972
  _v$8 !== _p$.t && (_p$.t = setProp(_el$13, "attributes", _v$8, _p$.t));
23659
23973
  return _p$;
@@ -23748,7 +24062,7 @@ function Sidebar2(props) {
23748
24062
  }
23749
24063
  }), null);
23750
24064
  effect((_p$) => {
23751
- var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes26.BOLD : undefined;
24065
+ var _v$9 = isCursor() ? theme.primary : isSelected() ? theme.backgroundElement : undefined, _v$0 = isCursor() ? theme.selectedListItemText : badgeColor(), _v$1 = isCursor() ? theme.selectedListItemText : theme.text, _v$10 = (isMain || isSelected() && !isCursor()) && !isCursor() ? TextAttributes27.BOLD : undefined;
23752
24066
  _v$9 !== _p$.e && (_p$.e = setProp(_el$14, "backgroundColor", _v$9, _p$.e));
23753
24067
  _v$0 !== _p$.t && (_p$.t = setProp(_el$15, "fg", _v$0, _p$.t));
23754
24068
  _v$1 !== _p$.a && (_p$.a = setProp(_el$16, "fg", _v$1, _p$.a));
@@ -23786,7 +24100,7 @@ function Sidebar2(props) {
23786
24100
  setProp(_el$11, "wrapMode", "none");
23787
24101
  setProp(_el$11, "onMouseUp", () => props.onAddTask?.());
23788
24102
  effect((_p$) => {
23789
- var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 = TextAttributes26.BOLD, _v$4 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$5 = TextAttributes26.BOLD, _v$6 = theme.textMuted;
24103
+ var _v$ = props.width ? props.width() : SIDEBAR_WIDTH, _v$2 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$3 = TextAttributes27.BOLD, _v$4 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$5 = TextAttributes27.BOLD, _v$6 = theme.textMuted;
23790
24104
  _v$ !== _p$.e && (_p$.e = setProp(_el$, "width", _v$, _p$.e));
23791
24105
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
23792
24106
  _v$3 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$3, _p$.a));
@@ -23991,18 +24305,29 @@ var init_server = () => {};
23991
24305
  // src/orchestrator/bridge/index.ts
23992
24306
  var exports_bridge = {};
23993
24307
  __export(exports_bridge, {
23994
- startBridge: () => startBridge
24308
+ startBridge: () => startBridge,
24309
+ bridgeSocketPathForHome: () => bridgeSocketPathForHome
23995
24310
  });
23996
- import { writeFile as writeFile4 } from "fs/promises";
23997
- import { homedir as homedir12 } from "os";
23998
- import { join as join15 } from "path";
24311
+ import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
24312
+ import { homedir as homedir12, tmpdir as tmpdir3 } from "os";
24313
+ import { join as join16 } from "path";
23999
24314
  import { fileURLToPath as fileURLToPath2 } from "url";
24315
+ function bridgeSocketPathForHome(home, pid = process.pid) {
24316
+ const runDir = join16(home, ".kobe", "run");
24317
+ const preferred = join16(runDir, `bridge-${pid}.sock`);
24318
+ const macTempSocket = process.platform === "darwin" && preferred.startsWith(tmpdir3());
24319
+ if (preferred.length <= UNIX_SOCKET_PATH_LIMIT && !macTempSocket)
24320
+ return preferred;
24321
+ const shortTmp = process.platform === "darwin" ? "/tmp" : tmpdir3();
24322
+ return join16(shortTmp, `kobe-bridge-${pid}.sock`);
24323
+ }
24000
24324
  async function startBridge(orch, opts = {}) {
24001
24325
  const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir12();
24002
- const runDir = join15(home, ".kobe", "run");
24003
- const socketPath = join15(runDir, `bridge-${process.pid}.sock`);
24004
- const mcpConfigPath = join15(runDir, `mcp-${process.pid}.json`);
24326
+ const runDir = join16(home, ".kobe", "run");
24327
+ const socketPath = bridgeSocketPathForHome(home);
24328
+ const mcpConfigPath = join16(runDir, `mcp-${process.pid}.json`);
24005
24329
  const server = await startBridgeServer(orch, socketPath);
24330
+ await mkdir5(runDir, { recursive: true });
24006
24331
  const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
24007
24332
  const entry = fileURLToPath2(new URL(`../../cli/index${moduleExt}`, import.meta.url));
24008
24333
  const mcpConfig = {
@@ -24023,13 +24348,14 @@ async function startBridge(orch, opts = {}) {
24023
24348
  }
24024
24349
  };
24025
24350
  }
24351
+ var UNIX_SOCKET_PATH_LIMIT = 103;
24026
24352
  var init_bridge = __esm(() => {
24027
24353
  init_server();
24028
24354
  });
24029
24355
 
24030
24356
  // src/tui/app.tsx
24031
24357
  import { homedir as homedir13 } from "os";
24032
- import { join as join16 } from "path";
24358
+ import { join as join17 } from "path";
24033
24359
  function Shell(props) {
24034
24360
  const themeCtx = useTheme();
24035
24361
  const {
@@ -24061,6 +24387,16 @@ function Shell(props) {
24061
24387
  return;
24062
24388
  return tasksAcc().find((t) => t.id === id);
24063
24389
  });
24390
+ const worktreeOpener = createMemo(() => detectWorktreeOpener());
24391
+ function openActiveTaskInEditor() {
24392
+ const task = activeTask();
24393
+ const opener = worktreeOpener();
24394
+ if (!task?.worktreePath || !opener)
24395
+ return;
24396
+ if (!openWorktree(task.worktreePath, opener)) {
24397
+ console.error("[kobe] failed to open worktree:", task.worktreePath);
24398
+ }
24399
+ }
24064
24400
  const taskIdAcc = createMemo(() => selectedId() ?? undefined);
24065
24401
  createEffect(on(taskIdAcc, () => {
24066
24402
  setWorkspaceContextAside(null);
@@ -24152,6 +24488,15 @@ function Shell(props) {
24152
24488
  });
24153
24489
  onCleanup(unregister);
24154
24490
  });
24491
+ onMount(() => {
24492
+ const unregister = palette.addCommand({
24493
+ name: "task.openEditor",
24494
+ title: "Open task in editor",
24495
+ desc: "Open the active task worktree in Cursor, VS Code, or the detected system editor.",
24496
+ run: openActiveTaskInEditor
24497
+ });
24498
+ onCleanup(unregister);
24499
+ });
24155
24500
  let pendingPersistedId = persistedSelectedId ?? null;
24156
24501
  createEffect(() => {
24157
24502
  const tasks = tasksAcc();
@@ -24208,7 +24553,8 @@ function Shell(props) {
24208
24553
  kv,
24209
24554
  orchestrator: props.orchestrator,
24210
24555
  renderer,
24211
- activeTask
24556
+ activeTask,
24557
+ openActiveTaskInEditor
24212
24558
  });
24213
24559
  const visibleTabKey = createMemo(() => {
24214
24560
  const taskId = selectedId();
@@ -24249,7 +24595,8 @@ function Shell(props) {
24249
24595
  },
24250
24596
  activeTask,
24251
24597
  activeChatTabId: activeChatTabIdAcc,
24252
- updateInfo
24598
+ updateInfo,
24599
+ worktreeOpener
24253
24600
  }), _el$2);
24254
24601
  insertNode(_el$2, _el$3);
24255
24602
  insertNode(_el$2, _el$4);
@@ -24583,6 +24930,7 @@ var init_app = __esm(() => {
24583
24930
  init_use_task_actions();
24584
24931
  init_use_theme_persistence();
24585
24932
  init_use_workspace_tabs();
24933
+ init_worktree_opener();
24586
24934
  init_Chat();
24587
24935
  init_filetree();
24588
24936
  init_preview();
@@ -24596,7 +24944,7 @@ var exports_tui = {};
24596
24944
  __export(exports_tui, {
24597
24945
  startTui: () => startTui
24598
24946
  });
24599
- import { TextAttributes as TextAttributes27 } from "@opentui/core";
24947
+ import { TextAttributes as TextAttributes28 } from "@opentui/core";
24600
24948
  function HelpHint() {
24601
24949
  const {
24602
24950
  theme
@@ -24670,7 +25018,7 @@ function Banner() {
24670
25018
  insert(_el$20, selected);
24671
25019
  insert(_el$12, createComponent2(HelpHint, {}), null);
24672
25020
  effect((_p$) => {
24673
- var _v$7 = theme.primary, _v$8 = TextAttributes27.BOLD, _v$9 = theme.borderActive, _v$0 = theme.text, _v$1 = theme.textMuted, _v$10 = {
25021
+ var _v$7 = theme.primary, _v$8 = TextAttributes28.BOLD, _v$9 = theme.borderActive, _v$0 = theme.text, _v$1 = theme.textMuted, _v$10 = {
24674
25022
  fg: theme.accent
24675
25023
  };
24676
25024
  _v$7 !== _p$.e && (_p$.e = setProp(_el$13, "fg", _v$7, _p$.e));
@@ -24824,6 +25172,11 @@ async function main() {
24824
25172
  await runDiagnoseSubcommand2();
24825
25173
  return;
24826
25174
  }
25175
+ if (subcommand === "update") {
25176
+ const { runUpdateSubcommand: runUpdateSubcommand2 } = await Promise.resolve().then(() => (init_update(), exports_update));
25177
+ await runUpdateSubcommand2(rest);
25178
+ return;
25179
+ }
24827
25180
  if (subcommand === "theme") {
24828
25181
  const { runThemeSubcommand: runThemeSubcommand2 } = await Promise.resolve().then(() => (init_theme2(), exports_theme));
24829
25182
  await runThemeSubcommand2(rest);