@sma1lboy/kobe 0.5.0 → 0.5.2

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
@@ -353,7 +353,24 @@ function extractMessage(record, fallbackSessionId) {
353
353
  const content = inner.content;
354
354
  const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
355
355
  const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
356
- return { role, content, timestamp: ts, sessionId: sid };
356
+ const usage = extractUsage(inner.usage);
357
+ return usage ? { role, content, timestamp: ts, sessionId: sid, usage } : { role, content, timestamp: ts, sessionId: sid };
358
+ }
359
+ function extractUsage(v) {
360
+ if (!isObject(v))
361
+ return;
362
+ const inTok = typeof v.input_tokens === "number" ? v.input_tokens : undefined;
363
+ const outTok = typeof v.output_tokens === "number" ? v.output_tokens : undefined;
364
+ if (inTok === undefined || outTok === undefined)
365
+ return;
366
+ const cacheRead = typeof v.cache_read_input_tokens === "number" ? v.cache_read_input_tokens : undefined;
367
+ const cacheCreate = typeof v.cache_creation_input_tokens === "number" ? v.cache_creation_input_tokens : undefined;
368
+ return {
369
+ input_tokens: inTok,
370
+ output_tokens: outTok,
371
+ ...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {},
372
+ ...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
373
+ };
357
374
  }
358
375
  function isObject(v) {
359
376
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -1021,13 +1038,15 @@ __export(exports_bridge, {
1021
1038
  import { writeFile } from "fs/promises";
1022
1039
  import { homedir as homedir4 } from "os";
1023
1040
  import { join } from "path";
1041
+ import { fileURLToPath } from "url";
1024
1042
  async function startBridge(orch, opts = {}) {
1025
1043
  const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir4();
1026
1044
  const runDir = join(home, ".kobe", "run");
1027
1045
  const socketPath = join(runDir, `bridge-${process.pid}.sock`);
1028
1046
  const mcpConfigPath = join(runDir, `mcp-${process.pid}.json`);
1029
1047
  const server = await startBridgeServer(orch, socketPath);
1030
- const entry = process.argv[1] ?? "";
1048
+ const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
1049
+ const entry = fileURLToPath(new URL(`../../cli/index${moduleExt}`, import.meta.url));
1031
1050
  const mcpConfig = {
1032
1051
  mcpServers: {
1033
1052
  kobe: {
@@ -2961,6 +2980,9 @@ class Orchestrator {
2961
2980
  tasksSignal() {
2962
2981
  return this.tasksAcc;
2963
2982
  }
2983
+ planUsageSignal() {
2984
+ return () => null;
2985
+ }
2964
2986
  subscribeTasks(listener) {
2965
2987
  return this.store.subscribe(listener);
2966
2988
  }
@@ -4294,9 +4316,166 @@ init_paths2();
4294
4316
 
4295
4317
  // src/daemon/server.ts
4296
4318
  init_paths2();
4297
- import { mkdir as mkdir3, readFile as readFile4, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4319
+ import { mkdir as mkdir3, readFile as readFile5, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
4298
4320
  import { createServer as createServer2 } from "net";
4299
4321
  import { dirname as dirname4 } from "path";
4322
+
4323
+ // src/engine/claude-code-local/plan-usage.ts
4324
+ import { execFile } from "child_process";
4325
+ import { createHash } from "crypto";
4326
+ import { readFile as readFile4 } from "fs/promises";
4327
+ import { homedir as homedir10, userInfo } from "os";
4328
+ import { join as join6 } from "path";
4329
+ import { promisify } from "util";
4330
+ var execFileAsync = promisify(execFile);
4331
+ var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
4332
+ var FETCH_TIMEOUT_MS = 5000;
4333
+ var KEYCHAIN_BASE = "Claude Code";
4334
+ var KEYCHAIN_SUFFIX = "-credentials";
4335
+ function keychainServiceName() {
4336
+ const configDir = process.env.CLAUDE_CONFIG_DIR;
4337
+ if (!configDir)
4338
+ return `${KEYCHAIN_BASE}${KEYCHAIN_SUFFIX}`;
4339
+ const hash = createHash("sha256").update(configDir).digest("hex").slice(0, 8);
4340
+ return `${KEYCHAIN_BASE}${KEYCHAIN_SUFFIX}-${hash}`;
4341
+ }
4342
+ function keychainAccount() {
4343
+ return process.env.USER || userInfo().username || "claude-code-user";
4344
+ }
4345
+ async function readKeychainToken() {
4346
+ if (process.platform !== "darwin")
4347
+ return null;
4348
+ try {
4349
+ const { stdout } = await execFileAsync("security", [
4350
+ "find-generic-password",
4351
+ "-a",
4352
+ keychainAccount(),
4353
+ "-w",
4354
+ "-s",
4355
+ keychainServiceName()
4356
+ ]);
4357
+ return parseStoredOAuth(stdout);
4358
+ } catch {
4359
+ return null;
4360
+ }
4361
+ }
4362
+ async function readPlainTextToken() {
4363
+ const configDir = process.env.CLAUDE_CONFIG_DIR ?? join6(homedir10(), ".claude");
4364
+ const path7 = join6(configDir, ".credentials.json");
4365
+ try {
4366
+ const raw = await readFile4(path7, "utf8");
4367
+ return parseStoredOAuth(raw);
4368
+ } catch {
4369
+ return null;
4370
+ }
4371
+ }
4372
+ function parseStoredOAuth(raw) {
4373
+ const trimmed = raw.trim();
4374
+ if (!trimmed)
4375
+ return null;
4376
+ try {
4377
+ const parsed = JSON.parse(trimmed);
4378
+ const tok = parsed.claudeAiOauth;
4379
+ if (!tok || typeof tok.accessToken !== "string" || tok.accessToken.length === 0)
4380
+ return null;
4381
+ return tok;
4382
+ } catch {
4383
+ return null;
4384
+ }
4385
+ }
4386
+ async function loadToken() {
4387
+ return await readKeychainToken() ?? await readPlainTextToken();
4388
+ }
4389
+ function normalizeBucket(b) {
4390
+ if (!b)
4391
+ return null;
4392
+ return {
4393
+ utilization: typeof b.utilization === "number" && Number.isFinite(b.utilization) ? b.utilization : null,
4394
+ resetsAt: typeof b.resets_at === "string" && b.resets_at.length > 0 ? b.resets_at : null
4395
+ };
4396
+ }
4397
+ async function fetchPlanUsage(now = Date.now()) {
4398
+ const token = await loadToken();
4399
+ if (!token)
4400
+ return null;
4401
+ if (typeof token.expiresAt === "number" && token.expiresAt > 0 && token.expiresAt < now)
4402
+ return null;
4403
+ const ctrl = new AbortController;
4404
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
4405
+ try {
4406
+ const res = await fetch(USAGE_URL, {
4407
+ signal: ctrl.signal,
4408
+ headers: {
4409
+ accept: "application/json",
4410
+ "content-type": "application/json",
4411
+ "user-agent": "kobe-plan-usage",
4412
+ "anthropic-beta": "oauth-2025-04-20",
4413
+ authorization: `Bearer ${token.accessToken}`
4414
+ }
4415
+ });
4416
+ if (!res.ok)
4417
+ return null;
4418
+ const body = await res.json();
4419
+ return {
4420
+ fiveHour: normalizeBucket(body.five_hour),
4421
+ sevenDay: normalizeBucket(body.seven_day),
4422
+ sevenDayOpus: normalizeBucket(body.seven_day_opus),
4423
+ sevenDaySonnet: normalizeBucket(body.seven_day_sonnet),
4424
+ fetchedAt: new Date(now).toISOString()
4425
+ };
4426
+ } catch {
4427
+ return null;
4428
+ } finally {
4429
+ clearTimeout(timer);
4430
+ }
4431
+ }
4432
+
4433
+ // src/daemon/plan-usage-poller.ts
4434
+ var DEFAULT_INTERVAL_MS = 60000;
4435
+ function createPlanUsagePoller(options) {
4436
+ const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
4437
+ const fetcher = options.fetcher ?? fetchPlanUsage;
4438
+ let timer = null;
4439
+ let last = null;
4440
+ let inflight = false;
4441
+ async function tick() {
4442
+ if (inflight)
4443
+ return;
4444
+ inflight = true;
4445
+ try {
4446
+ const usage = await fetcher();
4447
+ if (usage) {
4448
+ last = usage;
4449
+ options.onUpdate(usage);
4450
+ }
4451
+ } finally {
4452
+ inflight = false;
4453
+ }
4454
+ }
4455
+ return {
4456
+ start() {
4457
+ if (timer)
4458
+ return;
4459
+ tick();
4460
+ timer = setInterval(() => void tick(), intervalMs);
4461
+ timer.unref?.();
4462
+ },
4463
+ stop() {
4464
+ if (timer) {
4465
+ clearInterval(timer);
4466
+ timer = null;
4467
+ }
4468
+ },
4469
+ current() {
4470
+ return last;
4471
+ },
4472
+ async refresh() {
4473
+ await tick();
4474
+ }
4475
+ };
4476
+ }
4477
+
4478
+ // src/daemon/server.ts
4300
4479
  async function startDaemonServer(orch, options = {}) {
4301
4480
  const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
4302
4481
  const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
@@ -4327,12 +4506,16 @@ async function startDaemonServer(orch, options = {}) {
4327
4506
  clients.delete(client);
4328
4507
  });
4329
4508
  });
4509
+ const planUsagePoller = options.planUsagePoller ?? createPlanUsagePoller({
4510
+ onUpdate: (usage) => broadcast(clients, { type: "event", name: "plan.usage", payload: { usage } })
4511
+ });
4330
4512
  const serverApi = {
4331
4513
  socketPath,
4332
4514
  pidPath,
4333
4515
  startedAt,
4334
4516
  clients,
4335
4517
  async close() {
4518
+ planUsagePoller.stop();
4336
4519
  broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
4337
4520
  await new Promise((resolve) => server.close(() => resolve()));
4338
4521
  for (const client of Array.from(clients)) {
@@ -4345,6 +4528,7 @@ async function startDaemonServer(orch, options = {}) {
4345
4528
  await unlink4(pidPath).catch(() => {});
4346
4529
  }
4347
4530
  };
4531
+ planUsagePoller.start();
4348
4532
  await new Promise((resolve, reject) => {
4349
4533
  server.once("error", reject);
4350
4534
  server.listen(socketPath, () => {
@@ -4380,7 +4564,8 @@ async function startDaemonServer(orch, options = {}) {
4380
4564
  clientId: client.id,
4381
4565
  tasks: tasks.map(serializeTask),
4382
4566
  pending,
4383
- runState
4567
+ runState,
4568
+ planUsage: planUsagePoller.current()
4384
4569
  };
4385
4570
  }
4386
4571
  case "daemon.status":
@@ -4606,7 +4791,7 @@ async function startDaemonServer(orch, options = {}) {
4606
4791
  }
4607
4792
  async function readPidFile(pidPath) {
4608
4793
  try {
4609
- const raw = await readFile4(pidPath, "utf8");
4794
+ const raw = await readFile5(pidPath, "utf8");
4610
4795
  const pid = Number(raw.trim());
4611
4796
  return Number.isFinite(pid) ? pid : null;
4612
4797
  } catch {
package/dist/cli/index.js CHANGED
@@ -8825,7 +8825,7 @@ var init_package = __esm(() => {
8825
8825
  package_default = {
8826
8826
  $schema: "https://json.schemastore.org/package.json",
8827
8827
  name: "@sma1lboy/kobe",
8828
- version: "0.5.0",
8828
+ version: "0.5.2",
8829
8829
  description: "TUI orchestrator for Claude Code (codename)",
8830
8830
  type: "module",
8831
8831
  packageManager: "bun@1.3.13",
@@ -10584,6 +10584,9 @@ class Orchestrator {
10584
10584
  tasksSignal() {
10585
10585
  return this.tasksAcc;
10586
10586
  }
10587
+ planUsageSignal() {
10588
+ return () => null;
10589
+ }
10587
10590
  subscribeTasks(listener2) {
10588
10591
  return this.store.subscribe(listener2);
10589
10592
  }
@@ -11287,16 +11290,21 @@ class RemoteOrchestrator {
11287
11290
  setTasks;
11288
11291
  runStateAcc;
11289
11292
  setRunState;
11293
+ planUsageAcc;
11294
+ setPlanUsage;
11290
11295
  subscribers = new Map;
11291
11296
  pendingInputBroker = new InMemoryPendingInputBroker;
11292
11297
  constructor(client) {
11293
11298
  this.client = client;
11294
11299
  const [tasks, setTasks] = createSignal([]);
11295
11300
  const [runState, setRunState] = createSignal(new Map);
11301
+ const [planUsage, setPlanUsage] = createSignal(null);
11296
11302
  this.tasksAcc = tasks;
11297
11303
  this.setTasks = (next) => setTasks(() => next);
11298
11304
  this.runStateAcc = runState;
11299
11305
  this.setRunState = (next) => setRunState(() => next);
11306
+ this.planUsageAcc = planUsage;
11307
+ this.setPlanUsage = (next) => setPlanUsage(() => next);
11300
11308
  this.client.on("*", (frame) => this.handleEvent(frame.name, frame.payload));
11301
11309
  }
11302
11310
  async init() {
@@ -11316,6 +11324,8 @@ class RemoteOrchestrator {
11316
11324
  if (seed.size > 0)
11317
11325
  this.setRunState(seed);
11318
11326
  }
11327
+ if (hello.planUsage)
11328
+ this.setPlanUsage(hello.planUsage);
11319
11329
  await this.client.request("subscribe", { taskIds: "all" });
11320
11330
  if (hello.pending) {
11321
11331
  for (const [taskId, entries] of Object.entries(hello.pending)) {
@@ -11345,6 +11355,9 @@ class RemoteOrchestrator {
11345
11355
  chatRunStateSignal() {
11346
11356
  return this.runStateAcc;
11347
11357
  }
11358
+ planUsageSignal() {
11359
+ return this.planUsageAcc;
11360
+ }
11348
11361
  listTasks() {
11349
11362
  return this.tasksAcc().slice();
11350
11363
  }
@@ -11477,6 +11490,11 @@ class RemoteOrchestrator {
11477
11490
  }
11478
11491
  return;
11479
11492
  }
11493
+ if (name === "plan.usage") {
11494
+ const usage = obj.usage;
11495
+ this.setPlanUsage(usage ?? null);
11496
+ return;
11497
+ }
11480
11498
  const taskId = obj.taskId;
11481
11499
  const tabId = obj.tabId;
11482
11500
  if (!taskId || !tabId)
@@ -13179,8 +13197,10 @@ var init_resizable_edge = __esm(() => {
13179
13197
  // src/tui/context/focus.tsx
13180
13198
  function FocusProvider(props) {
13181
13199
  const [focused, setFocusedSignal] = createSignal(props.initial ?? "sidebar");
13200
+ const [refocusTick, setRefocusTick] = createSignal(0);
13182
13201
  const renderer = useRenderer();
13183
13202
  function setFocused(pane) {
13203
+ setRefocusTick((t) => t + 1);
13184
13204
  if (focused() === pane)
13185
13205
  return;
13186
13206
  const current = renderer?.currentFocusedRenderable;
@@ -13209,7 +13229,8 @@ function FocusProvider(props) {
13209
13229
  focused,
13210
13230
  is,
13211
13231
  setFocused,
13212
- cycle
13232
+ cycle,
13233
+ refocusTick
13213
13234
  };
13214
13235
  return createComponent2(FocusContext.Provider, {
13215
13236
  value,
@@ -14704,7 +14725,24 @@ function extractMessage(record, fallbackSessionId) {
14704
14725
  const content = inner.content;
14705
14726
  const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
14706
14727
  const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
14707
- return { role, content, timestamp: ts, sessionId: sid };
14728
+ const usage = extractUsage(inner.usage);
14729
+ return usage ? { role, content, timestamp: ts, sessionId: sid, usage } : { role, content, timestamp: ts, sessionId: sid };
14730
+ }
14731
+ function extractUsage(v) {
14732
+ if (!isObject(v))
14733
+ return;
14734
+ const inTok = typeof v.input_tokens === "number" ? v.input_tokens : undefined;
14735
+ const outTok = typeof v.output_tokens === "number" ? v.output_tokens : undefined;
14736
+ if (inTok === undefined || outTok === undefined)
14737
+ return;
14738
+ const cacheRead = typeof v.cache_read_input_tokens === "number" ? v.cache_read_input_tokens : undefined;
14739
+ const cacheCreate = typeof v.cache_creation_input_tokens === "number" ? v.cache_creation_input_tokens : undefined;
14740
+ return {
14741
+ input_tokens: inTok,
14742
+ output_tokens: outTok,
14743
+ ...cacheRead !== undefined ? { cache_read_input_tokens: cacheRead } : {},
14744
+ ...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
14745
+ };
14708
14746
  }
14709
14747
  function isObject(v) {
14710
14748
  return typeof v === "object" && v !== null && !Array.isArray(v);
@@ -15598,6 +15636,27 @@ var init_engine_bootstrap = __esm(() => {
15598
15636
  init_claude_code_local();
15599
15637
  });
15600
15638
 
15639
+ // src/tui/lib/format-plan-usage.ts
15640
+ function pct(value) {
15641
+ if (typeof value !== "number" || !Number.isFinite(value))
15642
+ return null;
15643
+ return `${Math.round(value)}%`;
15644
+ }
15645
+ function formatPlanUsageCompact(usage) {
15646
+ if (!usage)
15647
+ return null;
15648
+ const fiveHour = pct(usage.fiveHour?.utilization);
15649
+ const sevenDay = pct(usage.sevenDay?.utilization);
15650
+ const parts = [];
15651
+ if (fiveHour)
15652
+ parts.push(`5h ${fiveHour}`);
15653
+ if (sevenDay)
15654
+ parts.push(`7d ${sevenDay}`);
15655
+ if (parts.length === 0)
15656
+ return null;
15657
+ return `Plan ${parts.join(" \xB7 ")}`;
15658
+ }
15659
+
15601
15660
  // src/tui/lib/use-pane-sizes.ts
15602
15661
  function usePaneSizes(kv) {
15603
15662
  const dims = useTerminalDimensions();
@@ -17020,6 +17079,7 @@ function Composer(props) {
17020
17079
  const {
17021
17080
  theme
17022
17081
  } = useTheme();
17082
+ const focusCtx = useFocus();
17023
17083
  let textareaRef;
17024
17084
  let historyIndex = null;
17025
17085
  let liveDraftSnapshot = "";
@@ -17054,6 +17114,7 @@ function Composer(props) {
17054
17114
  setSlashCursor((cur) => len === 0 ? 0 : Math.min(cur, len - 1));
17055
17115
  });
17056
17116
  createEffect(() => {
17117
+ focusCtx.refocusTick();
17057
17118
  const ref = textareaRef;
17058
17119
  if (!ref)
17059
17120
  return;
@@ -17888,6 +17949,7 @@ var init_Composer = __esm(() => {
17888
17949
  init_solid();
17889
17950
  init_dev();
17890
17951
  init_border();
17952
+ init_focus();
17891
17953
  init_theme();
17892
17954
  init_clipboard_image();
17893
17955
  init_history2();
@@ -20349,7 +20411,7 @@ var init_user_slashes = () => {};
20349
20411
 
20350
20412
  // src/tui/panes/chat/context-meter.ts
20351
20413
  function totalContextTokens(u) {
20352
- return u.input_tokens + u.output_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
20414
+ return u.input_tokens + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);
20353
20415
  }
20354
20416
  function contextWindowTokensForModel(modelId) {
20355
20417
  const id = modelId ?? resolveDefaultModelId();
@@ -20378,8 +20440,8 @@ function formatContextUsageCompact(u, modelId) {
20378
20440
  const total = totalContextTokens(u);
20379
20441
  if (total <= 0 || window <= 0)
20380
20442
  return null;
20381
- const pct = Math.min(100, Math.max(0, Math.round(total / window * 100)));
20382
- return `${pct}% \xB7 ${formatTokShort(total)}/${formatTokShort(window)}`;
20443
+ const pct2 = Math.min(100, Math.max(0, Math.round(total / window * 100)));
20444
+ return `${pct2}% \xB7 ${formatTokShort(total)}/${formatTokShort(window)}`;
20383
20445
  }
20384
20446
  var LONG_CTX = 1e6, STD_CTX = 200000;
20385
20447
  var init_context_meter = __esm(() => {
@@ -20431,7 +20493,19 @@ function setMessagesFromHistory(state, past) {
20431
20493
  for (const m of past) {
20432
20494
  appendRowsFromMessage(rows, toolIndexById, m);
20433
20495
  }
20434
- return { ...state, messages: capMessages(rows, new Date().toISOString()) };
20496
+ let latestUsage;
20497
+ for (let i = past.length - 1;i >= 0; i--) {
20498
+ const u = past[i]?.usage;
20499
+ if (u) {
20500
+ latestUsage = u;
20501
+ break;
20502
+ }
20503
+ }
20504
+ return {
20505
+ ...state,
20506
+ messages: capMessages(rows, new Date().toISOString()),
20507
+ ...latestUsage ? { lastUsage: latestUsage } : {}
20508
+ };
20435
20509
  }
20436
20510
  function enqueuePrompt(state, prompt, nowIso = new Date().toISOString()) {
20437
20511
  if (state.queue.length >= QUEUE_SOFT_CAP)
@@ -22305,13 +22379,15 @@ __export(exports_bridge, {
22305
22379
  import { writeFile as writeFile3 } from "fs/promises";
22306
22380
  import { homedir as homedir11 } from "os";
22307
22381
  import { join as join14 } from "path";
22382
+ import { fileURLToPath as fileURLToPath2 } from "url";
22308
22383
  async function startBridge(orch, opts = {}) {
22309
22384
  const home = opts.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir11();
22310
22385
  const runDir = join14(home, ".kobe", "run");
22311
22386
  const socketPath = join14(runDir, `bridge-${process.pid}.sock`);
22312
22387
  const mcpConfigPath = join14(runDir, `mcp-${process.pid}.json`);
22313
22388
  const server = await startBridgeServer(orch, socketPath);
22314
- const entry = process.argv[1] ?? "";
22389
+ const moduleExt = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
22390
+ const entry = fileURLToPath2(new URL(`../../cli/index${moduleExt}`, import.meta.url));
22315
22391
  const mcpConfig = {
22316
22392
  mcpServers: {
22317
22393
  kobe: {
@@ -22351,6 +22427,8 @@ function Shell(props) {
22351
22427
  const [selectedId, setSelectedId] = createSignal(null);
22352
22428
  const [pendingPrompt, setPendingPrompt] = createSignal(null);
22353
22429
  const [workspaceContextAside, setWorkspaceContextAside] = createSignal(null);
22430
+ const planUsageAcc = props.orchestrator.planUsageSignal();
22431
+ const workspacePlanAside = createMemo(() => formatPlanUsageCompact(planUsageAcc()));
22354
22432
  const [updateInfo, setUpdateInfo] = createSignal(null);
22355
22433
  onMount(() => {
22356
22434
  checkLatestVersion().then((info) => {
@@ -22433,6 +22511,14 @@ function Shell(props) {
22433
22511
  selectFileTab,
22434
22512
  closeFileTab
22435
22513
  } = workspaceTabs;
22514
+ const workspaceAsideRight = createMemo(() => {
22515
+ const plan = workspacePlanAside();
22516
+ const ctx3 = isChatTabActive() ? workspaceContextAside() : null;
22517
+ const parts = [plan, ctx3].filter((v) => Boolean(v));
22518
+ if (parts.length === 0)
22519
+ return;
22520
+ return parts.join(" \u2022 ");
22521
+ });
22436
22522
  let pendingPersistedId = persistedSelectedId ?? null;
22437
22523
  createEffect(() => {
22438
22524
  const tasks = tasksAcc();
@@ -22565,7 +22651,7 @@ function Shell(props) {
22565
22651
  return activeTask()?.title ?? "no task";
22566
22652
  },
22567
22653
  get asideRight() {
22568
- return memo2(() => !!isChatTabActive())() ? workspaceContextAside() ?? undefined : undefined;
22654
+ return workspaceAsideRight();
22569
22655
  },
22570
22656
  get focused() {
22571
22657
  return focusedPane() === "workspace";
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.0",
4
+ "version": "0.5.2",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",