@sma1lboy/kobe 0.5.9 → 0.5.10

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/bin/kobed.js CHANGED
@@ -2506,6 +2506,86 @@ var init_claude_settings = __esm(() => {
2506
2506
  SETTINGS_PATH = join4(homedir6(), ".claude", "settings.json");
2507
2507
  });
2508
2508
 
2509
+ // src/session/usage-metrics.ts
2510
+ function totalContextTokens(u) {
2511
+ return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
2512
+ }
2513
+ function parseTimestampMs(value) {
2514
+ const ms = new Date(value).getTime();
2515
+ return Number.isFinite(ms) ? ms : null;
2516
+ }
2517
+ function mergeIntervals(intervals) {
2518
+ if (intervals.length === 0)
2519
+ return [];
2520
+ const sorted = [...intervals].sort((a, b) => a.startMs - b.startMs);
2521
+ const first = sorted[0];
2522
+ if (!first)
2523
+ return [];
2524
+ const merged = [{ startMs: first.startMs, endMs: first.endMs }];
2525
+ for (let i = 1;i < sorted.length; i++) {
2526
+ const current = sorted[i];
2527
+ const last = merged[merged.length - 1];
2528
+ if (!current || !last)
2529
+ continue;
2530
+ if (current.startMs <= last.endMs) {
2531
+ last.endMs = Math.max(last.endMs, current.endMs);
2532
+ } else {
2533
+ merged.push({ startMs: current.startMs, endMs: current.endMs });
2534
+ }
2535
+ }
2536
+ return merged;
2537
+ }
2538
+ function durationMs(intervals) {
2539
+ return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
2540
+ }
2541
+ function deriveSessionUsageMetrics(past) {
2542
+ let latestUsage;
2543
+ let latestUsageTimestampMs = null;
2544
+ let lastUserTimestampMs = null;
2545
+ let inputTokens = 0;
2546
+ let outputTokens = 0;
2547
+ const intervals = [];
2548
+ for (const message of past) {
2549
+ const timestampMs = parseTimestampMs(message.timestamp);
2550
+ if (message.role === "user" && timestampMs !== null) {
2551
+ lastUserTimestampMs = timestampMs;
2552
+ continue;
2553
+ }
2554
+ if (message.role !== "assistant" || !message.usage)
2555
+ continue;
2556
+ if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
2557
+ latestUsageTimestampMs = timestampMs;
2558
+ latestUsage = message.usage;
2559
+ } else if (latestUsage === undefined) {
2560
+ latestUsage = message.usage;
2561
+ }
2562
+ inputTokens += message.usage.input_tokens;
2563
+ outputTokens += message.usage.output_tokens;
2564
+ if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
2565
+ intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
2566
+ }
2567
+ }
2568
+ if (!latestUsage)
2569
+ return;
2570
+ const totalDurationMs = durationMs(mergeIntervals(intervals));
2571
+ if (totalDurationMs <= 0)
2572
+ return latestUsage;
2573
+ return {
2574
+ ...latestUsage,
2575
+ total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
2576
+ };
2577
+ }
2578
+ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
2579
+ const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
2580
+ const endMs = parseTimestampMs(endedAtIso);
2581
+ if (startMs === null || endMs === null || endMs <= startMs)
2582
+ return usage;
2583
+ return {
2584
+ ...usage,
2585
+ total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
2586
+ };
2587
+ }
2588
+
2509
2589
  // src/env.ts
2510
2590
  import { homedir as homedir7 } from "os";
2511
2591
  import { join as join5 } from "path";
@@ -3588,6 +3668,14 @@ class Orchestrator {
3588
3668
  return [];
3589
3669
  }
3590
3670
  }
3671
+ async readHistoryWithMetrics(sessionId) {
3672
+ const messages = await this.readHistory(sessionId);
3673
+ const usageMetrics = deriveSessionUsageMetrics(messages);
3674
+ return {
3675
+ messages,
3676
+ ...usageMetrics ? { usageMetrics } : {}
3677
+ };
3678
+ }
3591
3679
  async listSessions(id) {
3592
3680
  const task = this.requireTask(id);
3593
3681
  if (!task.worktreePath)
@@ -5123,6 +5211,7 @@ async function startDaemonServer(orch, options = {}) {
5123
5211
  const result = await readTaskHistory(orch, taskId, sessionId, limit, before);
5124
5212
  return {
5125
5213
  messages: serializeMessages(result.messages),
5214
+ ...result.usageMetrics ? { usageMetrics: result.usageMetrics } : {},
5126
5215
  nextBefore: result.nextBefore,
5127
5216
  hasMore: result.hasMore
5128
5217
  };
@@ -5270,6 +5359,7 @@ async function readTaskHistory(orch, taskId, requestedSessionId, limit, before)
5270
5359
  if (!sessionId)
5271
5360
  return { messages: [], nextBefore: null, hasMore: false };
5272
5361
  const messages = await orch.readHistory(sessionId);
5362
+ const usageMetrics = deriveSessionUsageMetrics(messages);
5273
5363
  const beforeIdx = before ? messages.findIndex((m) => `${m.timestamp}:${m.sessionId}` === before) : -1;
5274
5364
  const end = beforeIdx >= 0 ? beforeIdx : messages.length;
5275
5365
  const start = Math.max(0, end - limit);
@@ -5277,7 +5367,7 @@ async function readTaskHistory(orch, taskId, requestedSessionId, limit, before)
5277
5367
  const hasMore = start > 0;
5278
5368
  const first = page[0];
5279
5369
  const nextBefore = hasMore && first ? `${first.timestamp}:${first.sessionId}` : null;
5280
- return { messages: page, nextBefore, hasMore };
5370
+ return { messages: page, ...usageMetrics ? { usageMetrics } : {}, nextBefore, hasMore };
5281
5371
  }
5282
5372
  function writeFrame(client, frame) {
5283
5373
  client.socket.write(frameToLine(frame));
package/dist/cli/index.js CHANGED
@@ -8857,7 +8857,7 @@ var init_package = __esm(() => {
8857
8857
  package_default = {
8858
8858
  $schema: "https://json.schemastore.org/package.json",
8859
8859
  name: "@sma1lboy/kobe",
8860
- version: "0.5.9",
8860
+ version: "0.5.10",
8861
8861
  description: "TUI orchestrator for Claude Code (codename)",
8862
8862
  type: "module",
8863
8863
  packageManager: "bun@1.3.13",
@@ -8922,6 +8922,9 @@ function repoSlug() {
8922
8922
  return null;
8923
8923
  return `${m[1]}/${m[2]}`;
8924
8924
  }
8925
+ function recommendedGlobalInstallCommand() {
8926
+ return `npm install -g ${PACKAGE_NAME}@latest`;
8927
+ }
8925
8928
  function cachePath() {
8926
8929
  return join3(kobeStateDir(), "version-check.json");
8927
8930
  }
@@ -9037,13 +9040,13 @@ function releasePageUrl(version) {
9037
9040
  return null;
9038
9041
  return `https://github.com/${slug}/releases/tag/v${version}`;
9039
9042
  }
9040
- var CURRENT_VERSION, PACKAGE_NAME, INSTALL_COMMAND, CACHE_TTL_MS, FETCH_TIMEOUT_MS = 3000;
9043
+ 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
9044
  var init_version = __esm(() => {
9042
9045
  init_package();
9043
9046
  init_env();
9044
9047
  CURRENT_VERSION = package_default.version;
9045
9048
  PACKAGE_NAME = package_default.name;
9046
- INSTALL_COMMAND = `bun install -g ${PACKAGE_NAME}@latest`;
9049
+ UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
9047
9050
  CACHE_TTL_MS = 6 * 60 * 60 * 1000;
9048
9051
  });
9049
9052
 
@@ -9375,6 +9378,93 @@ var init_diagnose = __esm(() => {
9375
9378
  STATUS_ORDER = ["backlog", "in_progress", "in_review", "done", "canceled", "error"];
9376
9379
  });
9377
9380
 
9381
+ // src/cli/update.ts
9382
+ var exports_update = {};
9383
+ __export(exports_update, {
9384
+ updatePlan: () => updatePlan,
9385
+ runUpdateSubcommand: () => runUpdateSubcommand,
9386
+ parseUpdateArgs: () => parseUpdateArgs
9387
+ });
9388
+ import { spawnSync as spawnSync6 } from "child_process";
9389
+ function fail(message) {
9390
+ process.stderr.write(`kobe update: ${message}
9391
+ `);
9392
+ process.exit(1);
9393
+ }
9394
+ function updatePlan() {
9395
+ return {
9396
+ command: "sh",
9397
+ args: ["-c", UPDATE_COMMAND],
9398
+ display: UPDATE_COMMAND
9399
+ };
9400
+ }
9401
+ function parseUpdateArgs(args) {
9402
+ let dryRun = false;
9403
+ for (let i = 0;i < args.length; i += 1) {
9404
+ const arg = args[i];
9405
+ if (arg === "--help" || arg === "-h" || arg === "help")
9406
+ return { help: true, dryRun };
9407
+ if (arg === "--dry-run") {
9408
+ dryRun = true;
9409
+ continue;
9410
+ }
9411
+ fail(`unknown argument "${arg}"`);
9412
+ }
9413
+ return { help: false, dryRun };
9414
+ }
9415
+ function printUsage(out) {
9416
+ out.write([
9417
+ "Usage: kobe update [--dry-run]",
9418
+ "",
9419
+ "Runs kobe's GitHub-hosted update script.",
9420
+ "",
9421
+ "Default command:",
9422
+ ` ${UPDATE_COMMAND}`,
9423
+ "",
9424
+ "Script URL:",
9425
+ ` ${UPDATE_SCRIPT_URL}`,
9426
+ "",
9427
+ "Manual fallback:",
9428
+ ` ${recommendedGlobalInstallCommand()}`,
9429
+ "",
9430
+ "Examples:",
9431
+ " kobe update",
9432
+ " kobe update --dry-run",
9433
+ ""
9434
+ ].join(`
9435
+ `));
9436
+ }
9437
+ async function runUpdateSubcommand(args, deps) {
9438
+ const io = {
9439
+ spawn: deps?.spawn ?? spawnSync6,
9440
+ stdout: deps?.stdout ?? process.stdout,
9441
+ stderr: deps?.stderr ?? process.stderr,
9442
+ exit: deps?.exit ?? ((code) => process.exit(code))
9443
+ };
9444
+ const parsed = parseUpdateArgs(args);
9445
+ if (parsed.help) {
9446
+ printUsage(io.stdout);
9447
+ return;
9448
+ }
9449
+ const plan = updatePlan();
9450
+ io.stdout.write(`kobe ${CURRENT_VERSION} -> latest
9451
+ `);
9452
+ io.stdout.write(`running: ${plan.display}
9453
+ `);
9454
+ if (parsed.dryRun)
9455
+ return;
9456
+ const result = io.spawn(plan.command, plan.args, { stdio: "inherit" });
9457
+ if (result.error) {
9458
+ io.stderr.write(`kobe update: failed to run ${plan.command}: ${result.error.message}
9459
+ `);
9460
+ io.exit(1);
9461
+ }
9462
+ io.exit(result.status ?? 1);
9463
+ }
9464
+ var init_update = __esm(() => {
9465
+ init_version();
9466
+ });
9467
+
9378
9468
  // src/tui/context/theme/schema.ts
9379
9469
  function isPlainObject(v) {
9380
9470
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -9475,7 +9565,7 @@ __export(exports_theme, {
9475
9565
  });
9476
9566
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync as readdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
9477
9567
  import { basename as basename2, join as join6, resolve } from "path";
9478
- function fail(message) {
9568
+ function fail2(message) {
9479
9569
  process.stderr.write(`kobe theme: ${message}
9480
9570
  `);
9481
9571
  process.exit(1);
@@ -9513,10 +9603,10 @@ async function readSource(source) {
9513
9603
  try {
9514
9604
  res = await fetch(source);
9515
9605
  } catch (err) {
9516
- fail(`failed to fetch ${source}: ${err instanceof Error ? err.message : String(err)}`);
9606
+ fail2(`failed to fetch ${source}: ${err instanceof Error ? err.message : String(err)}`);
9517
9607
  }
9518
9608
  if (!res.ok) {
9519
- fail(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);
9609
+ fail2(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);
9520
9610
  }
9521
9611
  const text2 = await res.text();
9522
9612
  const cleanPath = source.split(/[?#]/)[0] ?? source;
@@ -9529,7 +9619,7 @@ async function readSource(source) {
9529
9619
  try {
9530
9620
  text = readFileSync3(abs, "utf8");
9531
9621
  } catch (err) {
9532
- fail(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
9622
+ fail2(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
9533
9623
  }
9534
9624
  const file = basename2(abs);
9535
9625
  const defaultName = file.endsWith(".json") ? file.slice(0, -".json".length) : file;
@@ -9548,7 +9638,7 @@ function parseAddArgs(args) {
9548
9638
  if (a === "--name" || a === "-n") {
9549
9639
  const next = args[i + 1];
9550
9640
  if (next === undefined)
9551
- fail("--name requires a value");
9641
+ fail2("--name requires a value");
9552
9642
  name = next;
9553
9643
  i += 1;
9554
9644
  continue;
@@ -9558,15 +9648,15 @@ function parseAddArgs(args) {
9558
9648
  continue;
9559
9649
  }
9560
9650
  if (a.startsWith("--"))
9561
- fail(`unknown flag: ${a}`);
9651
+ fail2(`unknown flag: ${a}`);
9562
9652
  if (source === null) {
9563
9653
  source = a;
9564
9654
  continue;
9565
9655
  }
9566
- fail(`unexpected positional argument: ${a}`);
9656
+ fail2(`unexpected positional argument: ${a}`);
9567
9657
  }
9568
9658
  if (source === null)
9569
- fail("missing <source> (URL or path to theme JSON)");
9659
+ fail2("missing <source> (URL or path to theme JSON)");
9570
9660
  return { source, opts: { name, force } };
9571
9661
  }
9572
9662
  async function addTheme2(args) {
@@ -9576,21 +9666,21 @@ async function addTheme2(args) {
9576
9666
  try {
9577
9667
  parsed = JSON.parse(text);
9578
9668
  } catch (err) {
9579
- fail(`source is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
9669
+ fail2(`source is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
9580
9670
  }
9581
9671
  const result = validateTheme(parsed);
9582
9672
  if (!result.ok) {
9583
- fail(`source is not a valid kobe theme: ${result.reason}`);
9673
+ fail2(`source is not a valid kobe theme: ${result.reason}`);
9584
9674
  }
9585
9675
  const name = opts.name ?? defaultName;
9586
9676
  if (!name || !/^[a-zA-Z0-9._-]+$/.test(name)) {
9587
- fail(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
9677
+ fail2(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
9588
9678
  }
9589
9679
  const dir = userThemesDir();
9590
9680
  mkdirSync2(dir, { recursive: true });
9591
9681
  const dest = join6(dir, `${name}.json`);
9592
9682
  if (existsSync3(dest) && !opts.force) {
9593
- fail(`${dest} already exists (pass --force to overwrite)`);
9683
+ fail2(`${dest} already exists (pass --force to overwrite)`);
9594
9684
  }
9595
9685
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
9596
9686
  `, "utf8");
@@ -9600,21 +9690,21 @@ async function addTheme2(args) {
9600
9690
  function removeTheme(args) {
9601
9691
  const name = args[0];
9602
9692
  if (!name)
9603
- fail("missing <name>");
9693
+ fail2("missing <name>");
9604
9694
  if (args.length > 1)
9605
- fail(`unexpected extra arguments after "${name}"`);
9695
+ fail2(`unexpected extra arguments after "${name}"`);
9606
9696
  if (BUNDLED_NAMES.includes(name)) {
9607
- fail(`"${name}" is a built-in theme and cannot be removed`);
9697
+ fail2(`"${name}" is a built-in theme and cannot be removed`);
9608
9698
  }
9609
9699
  const dest = join6(userThemesDir(), `${name}.json`);
9610
9700
  if (!existsSync3(dest)) {
9611
- fail(`no user theme named "${name}" (looked for ${dest})`);
9701
+ fail2(`no user theme named "${name}" (looked for ${dest})`);
9612
9702
  }
9613
9703
  unlinkSync(dest);
9614
9704
  process.stdout.write(`removed theme "${name}" (${dest})
9615
9705
  `);
9616
9706
  }
9617
- function printUsage() {
9707
+ function printUsage2() {
9618
9708
  process.stderr.write([
9619
9709
  "Usage: kobe theme <command> [args]",
9620
9710
  "",
@@ -9633,14 +9723,14 @@ function printUsage() {
9633
9723
  async function runThemeSubcommand(args) {
9634
9724
  const [action, ...rest] = args;
9635
9725
  if (!action || action === "--help" || action === "-h" || action === "help") {
9636
- printUsage();
9726
+ printUsage2();
9637
9727
  if (!action)
9638
9728
  process.exit(1);
9639
9729
  return;
9640
9730
  }
9641
9731
  if (action === "list" || action === "ls") {
9642
9732
  if (rest.length > 0)
9643
- fail(`"${action}" takes no arguments`);
9733
+ fail2(`"${action}" takes no arguments`);
9644
9734
  listThemes2();
9645
9735
  return;
9646
9736
  }
@@ -9652,7 +9742,7 @@ async function runThemeSubcommand(args) {
9652
9742
  removeTheme(rest);
9653
9743
  return;
9654
9744
  }
9655
- fail(`unknown action "${action}" (try "list", "add", or "remove")`);
9745
+ fail2(`unknown action "${action}" (try "list", "add", or "remove")`);
9656
9746
  }
9657
9747
  var BUNDLED_NAMES;
9658
9748
  var init_theme2 = __esm(() => {
@@ -9775,7 +9865,7 @@ async function handleMcpFrame(client, line) {
9775
9865
  process.stdout.write(`${JSON.stringify(out)}
9776
9866
  `);
9777
9867
  };
9778
- const fail2 = (code, message) => {
9868
+ const fail3 = (code, message) => {
9779
9869
  const out = { jsonrpc: "2.0", id: msg.id, error: { code, message } };
9780
9870
  process.stdout.write(`${JSON.stringify(out)}
9781
9871
  `);
@@ -9797,7 +9887,7 @@ async function handleMcpFrame(client, line) {
9797
9887
  const name = params.name;
9798
9888
  const args = params.arguments ?? {};
9799
9889
  if (!name) {
9800
- fail2(-32602, "missing tool name");
9890
+ fail3(-32602, "missing tool name");
9801
9891
  return;
9802
9892
  }
9803
9893
  const result = await invokeTool(client, name, args);
@@ -9807,11 +9897,11 @@ async function handleMcpFrame(client, line) {
9807
9897
  return;
9808
9898
  }
9809
9899
  default:
9810
- fail2(-32601, `method not found: ${msg.method}`);
9900
+ fail3(-32601, `method not found: ${msg.method}`);
9811
9901
  return;
9812
9902
  }
9813
9903
  } catch (err) {
9814
- fail2(-32000, err instanceof Error ? err.message : String(err));
9904
+ fail3(-32000, err instanceof Error ? err.message : String(err));
9815
9905
  }
9816
9906
  }
9817
9907
  async function invokeTool(client, name, args) {
@@ -10174,6 +10264,86 @@ var init_claude_settings = __esm(() => {
10174
10264
  SETTINGS_PATH = join9(homedir6(), ".claude", "settings.json");
10175
10265
  });
10176
10266
 
10267
+ // src/session/usage-metrics.ts
10268
+ function totalContextTokens(u) {
10269
+ return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
10270
+ }
10271
+ function parseTimestampMs(value) {
10272
+ const ms = new Date(value).getTime();
10273
+ return Number.isFinite(ms) ? ms : null;
10274
+ }
10275
+ function mergeIntervals(intervals) {
10276
+ if (intervals.length === 0)
10277
+ return [];
10278
+ const sorted = [...intervals].sort((a, b) => a.startMs - b.startMs);
10279
+ const first = sorted[0];
10280
+ if (!first)
10281
+ return [];
10282
+ const merged = [{ startMs: first.startMs, endMs: first.endMs }];
10283
+ for (let i = 1;i < sorted.length; i++) {
10284
+ const current = sorted[i];
10285
+ const last = merged[merged.length - 1];
10286
+ if (!current || !last)
10287
+ continue;
10288
+ if (current.startMs <= last.endMs) {
10289
+ last.endMs = Math.max(last.endMs, current.endMs);
10290
+ } else {
10291
+ merged.push({ startMs: current.startMs, endMs: current.endMs });
10292
+ }
10293
+ }
10294
+ return merged;
10295
+ }
10296
+ function durationMs(intervals) {
10297
+ return intervals.reduce((total, interval) => total + (interval.endMs - interval.startMs), 0);
10298
+ }
10299
+ function deriveSessionUsageMetrics(past) {
10300
+ let latestUsage;
10301
+ let latestUsageTimestampMs = null;
10302
+ let lastUserTimestampMs = null;
10303
+ let inputTokens = 0;
10304
+ let outputTokens = 0;
10305
+ const intervals = [];
10306
+ for (const message of past) {
10307
+ const timestampMs = parseTimestampMs(message.timestamp);
10308
+ if (message.role === "user" && timestampMs !== null) {
10309
+ lastUserTimestampMs = timestampMs;
10310
+ continue;
10311
+ }
10312
+ if (message.role !== "assistant" || !message.usage)
10313
+ continue;
10314
+ if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
10315
+ latestUsageTimestampMs = timestampMs;
10316
+ latestUsage = message.usage;
10317
+ } else if (latestUsage === undefined) {
10318
+ latestUsage = message.usage;
10319
+ }
10320
+ inputTokens += message.usage.input_tokens;
10321
+ outputTokens += message.usage.output_tokens;
10322
+ if (timestampMs !== null && lastUserTimestampMs !== null && timestampMs > lastUserTimestampMs) {
10323
+ intervals.push({ startMs: lastUserTimestampMs, endMs: timestampMs });
10324
+ }
10325
+ }
10326
+ if (!latestUsage)
10327
+ return;
10328
+ const totalDurationMs = durationMs(mergeIntervals(intervals));
10329
+ if (totalDurationMs <= 0)
10330
+ return latestUsage;
10331
+ return {
10332
+ ...latestUsage,
10333
+ total_speed_tokens_per_second: (inputTokens + outputTokens) / (totalDurationMs / 1000)
10334
+ };
10335
+ }
10336
+ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
10337
+ const startMs = startedAtIso ? parseTimestampMs(startedAtIso) : null;
10338
+ const endMs = parseTimestampMs(endedAtIso);
10339
+ if (startMs === null || endMs === null || endMs <= startMs)
10340
+ return usage;
10341
+ return {
10342
+ ...usage,
10343
+ total_speed_tokens_per_second: (usage.input_tokens + usage.output_tokens) / ((endMs - startMs) / 1000)
10344
+ };
10345
+ }
10346
+
10177
10347
  // src/orchestrator/metadata-suggester.ts
10178
10348
  import { spawn as spawn3 } from "child_process";
10179
10349
 
@@ -10353,10 +10523,10 @@ class InMemoryPendingInputBroker {
10353
10523
  }
10354
10524
 
10355
10525
  // src/orchestrator/pr/build.ts
10356
- import { spawnSync as spawnSync6 } from "child_process";
10526
+ import { spawnSync as spawnSync7 } from "child_process";
10357
10527
  function git(cwd, args) {
10358
10528
  try {
10359
- const out = spawnSync6("git", args.slice(), {
10529
+ const out = spawnSync7("git", args.slice(), {
10360
10530
  cwd,
10361
10531
  encoding: "utf8",
10362
10532
  timeout: GIT_TIMEOUT_MS
@@ -11049,6 +11219,14 @@ class Orchestrator {
11049
11219
  return [];
11050
11220
  }
11051
11221
  }
11222
+ async readHistoryWithMetrics(sessionId) {
11223
+ const messages = await this.readHistory(sessionId);
11224
+ const usageMetrics = deriveSessionUsageMetrics(messages);
11225
+ return {
11226
+ messages,
11227
+ ...usageMetrics ? { usageMetrics } : {}
11228
+ };
11229
+ }
11052
11230
  async listSessions(id) {
11053
11231
  const task = this.requireTask(id);
11054
11232
  if (!task.worktreePath)
@@ -11524,15 +11702,21 @@ class RemoteOrchestrator {
11524
11702
  await this.client.request("chat.tab.activate", { taskId, tabId });
11525
11703
  }
11526
11704
  async readHistory(sessionId) {
11705
+ return (await this.readHistoryWithMetrics(sessionId)).messages;
11706
+ }
11707
+ async readHistoryWithMetrics(sessionId) {
11527
11708
  const task = this.tasksAcc().find((t) => t.sessionId === sessionId || t.tabs.some((tab) => tab.sessionId === sessionId));
11528
11709
  if (!task)
11529
- return [];
11710
+ return { messages: [] };
11530
11711
  const res = await this.client.request("chat.history", {
11531
11712
  taskId: task.id,
11532
11713
  sessionId,
11533
11714
  limit: 500
11534
11715
  });
11535
- return res.messages;
11716
+ return {
11717
+ messages: res.messages,
11718
+ ...res.usageMetrics ? { usageMetrics: res.usageMetrics } : {}
11719
+ };
11536
11720
  }
11537
11721
  async listSessions(taskId) {
11538
11722
  const res = await this.client.request("chat.sessions", { taskId });
@@ -11685,12 +11869,12 @@ var init_remote_orchestrator = __esm(() => {
11685
11869
  });
11686
11870
 
11687
11871
  // src/orchestrator/worktree/git.ts
11688
- import { spawnSync as spawnSync7 } from "child_process";
11872
+ import { spawnSync as spawnSync8 } from "child_process";
11689
11873
  function git2(args, opts) {
11690
11874
  if (!opts.cwd) {
11691
11875
  throw new Error("git(): cwd is required; refusing to inherit from process.cwd()");
11692
11876
  }
11693
- const proc = spawnSync7("git", [...args], {
11877
+ const proc = spawnSync8("git", [...args], {
11694
11878
  cwd: opts.cwd,
11695
11879
  env: opts.env ? { ...process.env, ...opts.env } : process.env,
11696
11880
  encoding: "utf8",
@@ -15243,11 +15427,11 @@ function UpdateDialog(props) {
15243
15427
  const [notes] = createResource(() => fetchReleaseNotes(props.info.latest));
15244
15428
  const fallbackUrl = () => releasePageUrl(props.info.latest);
15245
15429
  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");
15430
+ 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
15431
  insertNode(_el$, _el$2);
15248
15432
  insertNode(_el$, _el$7);
15249
15433
  insertNode(_el$, _el$12);
15250
- insertNode(_el$, _el$17);
15434
+ insertNode(_el$, _el$20);
15251
15435
  setProp(_el$, "paddingLeft", 2);
15252
15436
  setProp(_el$, "paddingRight", 2);
15253
15437
  setProp(_el$, "gap", 1);
@@ -15270,86 +15454,91 @@ function UpdateDialog(props) {
15270
15454
  insert(_el$10, () => props.info.latest, null);
15271
15455
  insertNode(_el$12, _el$13);
15272
15456
  insertNode(_el$12, _el$15);
15457
+ insertNode(_el$12, _el$17);
15273
15458
  setProp(_el$12, "gap", 0);
15274
15459
  insertNode(_el$13, createTextNode(`Run this to update:`));
15275
15460
  insertNode(_el$15, _el$16);
15276
15461
  setProp(_el$15, "paddingLeft", 2);
15277
- insert(_el$16, INSTALL_COMMAND);
15462
+ insert(_el$16, UPDATE_COMMAND);
15278
15463
  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, {
15464
+ setProp(_el$17, "paddingLeft", 2);
15465
+ insertNode(_el$18, _el$19);
15466
+ insert(_el$18, recommendedGlobalInstallCommand, null);
15467
+ insertNode(_el$20, _el$21);
15468
+ insertNode(_el$20, _el$23);
15469
+ setProp(_el$20, "gap", 0);
15470
+ insertNode(_el$21, createTextNode(`What's new:`));
15471
+ setProp(_el$23, "paddingLeft", 2);
15472
+ setProp(_el$23, "paddingTop", 1);
15473
+ insert(_el$23, createComponent2(Switch, {
15285
15474
  get children() {
15286
15475
  return [createComponent2(Match, {
15287
15476
  get when() {
15288
15477
  return notes.loading;
15289
15478
  },
15290
15479
  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;
15480
+ var _el$24 = createElement("text");
15481
+ insertNode(_el$24, createTextNode(`Loading release notes\u2026`));
15482
+ effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
15483
+ return _el$24;
15295
15484
  }
15296
15485
  }), createComponent2(Match, {
15297
15486
  get when() {
15298
15487
  return notes() === null;
15299
15488
  },
15300
15489
  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, {
15490
+ var _el$26 = createElement("box"), _el$27 = createElement("text");
15491
+ insertNode(_el$26, _el$27);
15492
+ setProp(_el$26, "gap", 0);
15493
+ insertNode(_el$27, createTextNode(`(couldn't reach GitHub \u2014 see the release page directly)`));
15494
+ insert(_el$26, createComponent2(Show, {
15306
15495
  get when() {
15307
15496
  return fallbackUrl();
15308
15497
  },
15309
15498
  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;
15499
+ var _el$29 = createElement("text");
15500
+ insert(_el$29, fallbackUrl);
15501
+ effect((_$p) => setProp(_el$29, "fg", theme.accent, _$p));
15502
+ return _el$29;
15314
15503
  }
15315
15504
  }), null);
15316
- effect((_$p) => setProp(_el$24, "fg", theme.textMuted, _$p));
15317
- return _el$23;
15505
+ effect((_$p) => setProp(_el$27, "fg", theme.textMuted, _$p));
15506
+ return _el$26;
15318
15507
  }
15319
15508
  }), createComponent2(Match, {
15320
15509
  get when() {
15321
15510
  return notes();
15322
15511
  },
15323
15512
  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, {
15513
+ var _el$30 = createElement("box");
15514
+ setProp(_el$30, "flexDirection", "column");
15515
+ setProp(_el$30, "gap", 0);
15516
+ insert(_el$30, createComponent2(Markdown, {
15328
15517
  get source() {
15329
15518
  return notes()?.body ?? "";
15330
15519
  }
15331
15520
  }), null);
15332
- insert(_el$27, createComponent2(Show, {
15521
+ insert(_el$30, createComponent2(Show, {
15333
15522
  get when() {
15334
15523
  return notes()?.url;
15335
15524
  },
15336
15525
  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;
15526
+ var _el$31 = createElement("box"), _el$32 = createElement("text"), _el$33 = createTextNode(`Full release: `);
15527
+ insertNode(_el$31, _el$32);
15528
+ setProp(_el$31, "paddingTop", 1);
15529
+ insertNode(_el$32, _el$33);
15530
+ insert(_el$32, () => notes()?.url, null);
15531
+ effect((_$p) => setProp(_el$32, "fg", theme.textMuted, _$p));
15532
+ return _el$31;
15344
15533
  }
15345
15534
  }), null);
15346
- return _el$27;
15535
+ return _el$30;
15347
15536
  }
15348
15537
  })];
15349
15538
  }
15350
15539
  }));
15351
15540
  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;
15541
+ 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, _v$10 = theme.textMuted;
15353
15542
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "attributes", _v$, _p$.e));
15354
15543
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
15355
15544
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -15361,6 +15550,7 @@ function UpdateDialog(props) {
15361
15550
  _v$9 !== _p$.r && (_p$.r = setProp(_el$16, "fg", _v$9, _p$.r));
15362
15551
  _v$0 !== _p$.d && (_p$.d = setProp(_el$16, "attributes", _v$0, _p$.d));
15363
15552
  _v$1 !== _p$.l && (_p$.l = setProp(_el$18, "fg", _v$1, _p$.l));
15553
+ _v$10 !== _p$.u && (_p$.u = setProp(_el$21, "fg", _v$10, _p$.u));
15364
15554
  return _p$;
15365
15555
  }, {
15366
15556
  e: undefined,
@@ -15373,7 +15563,8 @@ function UpdateDialog(props) {
15373
15563
  h: undefined,
15374
15564
  r: undefined,
15375
15565
  d: undefined,
15376
- l: undefined
15566
+ l: undefined,
15567
+ u: undefined
15377
15568
  });
15378
15569
  return _el$;
15379
15570
  })();
@@ -16903,8 +17094,8 @@ function validateRepoPath(repo) {
16903
17094
  if (!stat3.isDirectory())
16904
17095
  return `not a directory: ${trimmed}`;
16905
17096
  try {
16906
- const { spawnSync: spawnSync8 } = __require("child_process");
16907
- const out = spawnSync8("git", ["rev-parse", "--git-dir"], {
17097
+ const { spawnSync: spawnSync9 } = __require("child_process");
17098
+ const out = spawnSync9("git", ["rev-parse", "--git-dir"], {
16908
17099
  cwd: trimmed,
16909
17100
  encoding: "utf-8",
16910
17101
  timeout: 2000,
@@ -16921,8 +17112,8 @@ function getCurrentBranch(repo) {
16921
17112
  if (!repo)
16922
17113
  return null;
16923
17114
  try {
16924
- const { spawnSync: spawnSync8 } = __require("child_process");
16925
- const out = spawnSync8("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
17115
+ const { spawnSync: spawnSync9 } = __require("child_process");
17116
+ const out = spawnSync9("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
16926
17117
  cwd: repo,
16927
17118
  encoding: "utf-8",
16928
17119
  timeout: 2000,
@@ -16942,8 +17133,8 @@ function listLocalBranches(repo) {
16942
17133
  if (!repo)
16943
17134
  return [];
16944
17135
  try {
16945
- const { spawnSync: spawnSync8 } = __require("child_process");
16946
- const out = spawnSync8("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
17136
+ const { spawnSync: spawnSync9 } = __require("child_process");
17137
+ const out = spawnSync9("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
16947
17138
  cwd: repo,
16948
17139
  encoding: "utf-8",
16949
17140
  timeout: 2000
@@ -18062,7 +18253,7 @@ var init_resume_dialog = __esm(() => {
18062
18253
  });
18063
18254
 
18064
18255
  // src/tui/panes/chat/composer/clipboard-image.ts
18065
- import { spawnSync as spawnSync8 } from "child_process";
18256
+ import { spawnSync as spawnSync9 } from "child_process";
18066
18257
  import { statSync as statSync4 } from "fs";
18067
18258
  function clipboardImageSupported() {
18068
18259
  return process.platform === "darwin";
@@ -18087,7 +18278,7 @@ function readClipboardImageMacOS(destPath) {
18087
18278
  "end try"
18088
18279
  ].join(`
18089
18280
  `);
18090
- const result = spawnSync8("osascript", ["-e", script], {
18281
+ const result = spawnSync9("osascript", ["-e", script], {
18091
18282
  timeout: 5000,
18092
18283
  stdio: ["ignore", "ignore", "ignore"]
18093
18284
  });
@@ -21986,93 +22177,30 @@ async function loadUserSlashes(worktreePath) {
21986
22177
  var init_user_slashes = () => {};
21987
22178
 
21988
22179
  // 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 });
22180
+ function parseContextWindowSize(modelIdentifier) {
22181
+ const delimitedMatch = /(?:\(|\[)\s*(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])\s*(?:\)|\])/i.exec(modelIdentifier);
22182
+ if (delimitedMatch?.[1] && delimitedMatch[2]) {
22183
+ const parsed2 = Number.parseFloat(delimitedMatch[1].replace(/[,_]/g, ""));
22184
+ if (Number.isFinite(parsed2) && parsed2 > 0) {
22185
+ return Math.round(parsed2 * (delimitedMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
22013
22186
  }
22014
22187
  }
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
- };
22188
+ const contextMatch = /\b(\d+(?:[,_]\d+)*(?:\.\d+)?)\s*([km])(?:\s*(?:token\s*)?context)?\b/i.exec(modelIdentifier);
22189
+ if (!contextMatch?.[1] || !contextMatch[2])
22190
+ return null;
22191
+ const parsed = Number.parseFloat(contextMatch[1].replace(/[,_]/g, ""));
22192
+ if (!Number.isFinite(parsed) || parsed <= 0)
22193
+ return null;
22194
+ return Math.round(parsed * (contextMatch[2].toLowerCase() === "m" ? 1e6 : 1000));
22066
22195
  }
22067
22196
  function contextWindowTokensForModel(modelId) {
22068
22197
  const id = modelId ?? resolveDefaultModelId();
22069
- if (id.includes("[1m]"))
22070
- return LONG_CTX;
22198
+ const parsedWindow = parseContextWindowSize(id);
22199
+ if (parsedWindow !== null)
22200
+ return parsedWindow;
22071
22201
  const inPicker = MODEL_CHOICES.some((m) => m.id === id);
22072
22202
  if (inPicker)
22073
22203
  return STD_CTX;
22074
- if (id.includes("1m") || id.includes("[1M]"))
22075
- return LONG_CTX;
22076
22204
  return STD_CTX;
22077
22205
  }
22078
22206
  function formatTokShort(n) {
@@ -22102,7 +22230,7 @@ function formatContextUsageCompact(u, modelId) {
22102
22230
  const speed = formatTotalSpeed(u.total_speed_tokens_per_second);
22103
22231
  return [`${pct2}% \xB7 ${formatTokShort(total)}/${formatTokShort(window)}`, speed].filter(Boolean).join(" \xB7 ");
22104
22232
  }
22105
- var LONG_CTX = 1e6, STD_CTX = 200000;
22233
+ var STD_CTX = 200000;
22106
22234
  var init_context_meter = __esm(() => {
22107
22235
  init_claude_settings2();
22108
22236
  init_models();
@@ -22146,13 +22274,13 @@ function createInitialState() {
22146
22274
  queue: []
22147
22275
  };
22148
22276
  }
22149
- function setMessagesFromHistory(state, past) {
22277
+ function setMessagesFromHistory(state, past, usageMetrics) {
22150
22278
  const rows = [];
22151
22279
  const toolIndexById = new Map;
22152
22280
  for (const m of past) {
22153
22281
  appendRowsFromMessage(rows, toolIndexById, m);
22154
22282
  }
22155
- const latestUsage = deriveUsageMetricsFromHistory(past);
22283
+ const latestUsage = usageMetrics ?? deriveSessionUsageMetrics(past);
22156
22284
  return {
22157
22285
  ...state,
22158
22286
  messages: capMessages(rows, new Date().toISOString()),
@@ -22422,7 +22550,6 @@ function findLastIndex(arr, pred) {
22422
22550
  }
22423
22551
  var SCROLLBACK_CAP = 1000, SENTINEL_PREFIX = "(scrollback truncated \u2014 ", SENTINEL_SUFFIX = " rows dropped)", CLAUDE_NOISE_TAGS, NOISE_TAG_PATTERN, QUEUE_SOFT_CAP = 50;
22424
22552
  var init_store2 = __esm(() => {
22425
- init_context_meter();
22426
22553
  CLAUDE_NOISE_TAGS = [
22427
22554
  "local-command-caveat",
22428
22555
  "command-message",
@@ -22533,10 +22660,10 @@ function useChatSession(opts) {
22533
22660
  };
22534
22661
  if (tab.sessionId) {
22535
22662
  const sid = tab.sessionId;
22536
- orchestrator.readHistory(sid).then((past) => {
22663
+ orchestrator.readHistoryWithMetrics(sid).then(({ messages, usageMetrics }) => {
22537
22664
  if (opts.taskId() !== taskId)
22538
22665
  return;
22539
- patchStateForTab(tabId, (s) => setMessagesFromHistory(s, past));
22666
+ patchStateForTab(tabId, (s) => setMessagesFromHistory(s, messages, usageMetrics));
22540
22667
  replayPending();
22541
22668
  }).catch((err) => {
22542
22669
  const msg = err instanceof Error ? err.message : String(err);
@@ -23421,12 +23548,12 @@ var init_sidebar = __esm(() => {
23421
23548
  });
23422
23549
 
23423
23550
  // src/tui/panes/sidebar/git-head.ts
23424
- import { spawnSync as spawnSync9 } from "child_process";
23551
+ import { spawnSync as spawnSync10 } from "child_process";
23425
23552
  function readCurrentBranch(repo) {
23426
23553
  if (!repo)
23427
23554
  return "";
23428
23555
  try {
23429
- const out = spawnSync9("git", ["symbolic-ref", "--short", "HEAD"], {
23556
+ const out = spawnSync10("git", ["symbolic-ref", "--short", "HEAD"], {
23430
23557
  cwd: repo,
23431
23558
  encoding: "utf8",
23432
23559
  stdio: ["ignore", "pipe", "pipe"]
@@ -23436,7 +23563,7 @@ function readCurrentBranch(repo) {
23436
23563
  if (name && name !== "HEAD")
23437
23564
  return name;
23438
23565
  }
23439
- const head = spawnSync9("git", ["rev-parse", "--verify", "HEAD"], {
23566
+ const head = spawnSync10("git", ["rev-parse", "--verify", "HEAD"], {
23440
23567
  cwd: repo,
23441
23568
  encoding: "utf8",
23442
23569
  stdio: ["ignore", "pipe", "pipe"]
@@ -24824,6 +24951,11 @@ async function main() {
24824
24951
  await runDiagnoseSubcommand2();
24825
24952
  return;
24826
24953
  }
24954
+ if (subcommand === "update") {
24955
+ const { runUpdateSubcommand: runUpdateSubcommand2 } = await Promise.resolve().then(() => (init_update(), exports_update));
24956
+ await runUpdateSubcommand2(rest);
24957
+ return;
24958
+ }
24827
24959
  if (subcommand === "theme") {
24828
24960
  const { runThemeSubcommand: runThemeSubcommand2 } = await Promise.resolve().then(() => (init_theme2(), exports_theme));
24829
24961
  await runThemeSubcommand2(rest);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@sma1lboy/kobe",
4
- "version": "0.5.9",
4
+ "version": "0.5.10",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",