@sma1lboy/kobe 0.7.22 → 0.7.23

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
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
90
90
  package_default = {
91
91
  $schema: "https://json.schemastore.org/package.json",
92
92
  name: "@sma1lboy/kobe",
93
- version: "0.7.22",
93
+ version: "0.7.23",
94
94
  description: "TUI orchestrator for Claude Code (codename)",
95
95
  type: "module",
96
96
  packageManager: "bun@1.3.13",
@@ -13256,6 +13256,62 @@ async function handleDiffRequest(req, url) {
13256
13256
  }
13257
13257
  var GIT_TIMEOUT_MS = 15000, UNTRACKED_DIFF_CONCURRENCY = 8;
13258
13258
 
13259
+ // src/web/history.ts
13260
+ import { isAbsolute } from "path";
13261
+ function isSafeVendor(value) {
13262
+ return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9_-]+$/.test(value);
13263
+ }
13264
+ function isSafeSessionId(value) {
13265
+ return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9._-]+$/.test(value) && !value.includes("..");
13266
+ }
13267
+ async function handleSessions(url) {
13268
+ const worktreePath = url.searchParams.get("worktreePath");
13269
+ const vendor = url.searchParams.get("vendor") ?? "claude";
13270
+ if (!worktreePath || !isAbsolute(worktreePath)) {
13271
+ return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
13272
+ }
13273
+ if (!isSafeVendor(vendor)) {
13274
+ return Response.json({ error: "invalid vendor" }, { status: 400 });
13275
+ }
13276
+ try {
13277
+ const reader = engineEntry(vendor).history;
13278
+ const [sessions, latestMtime] = await Promise.all([
13279
+ reader.listSessionIdsForWorktree(worktreePath),
13280
+ reader.latestTranscriptMtimeForWorktree(worktreePath)
13281
+ ]);
13282
+ return Response.json({ sessions, latestMtime });
13283
+ } catch (err) {
13284
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
13285
+ }
13286
+ }
13287
+ async function handleMessages(url) {
13288
+ const vendor = url.searchParams.get("vendor") ?? "claude";
13289
+ const sessionId = url.searchParams.get("sessionId");
13290
+ if (!isSafeVendor(vendor)) {
13291
+ return Response.json({ error: "invalid vendor" }, { status: 400 });
13292
+ }
13293
+ if (!isSafeSessionId(sessionId)) {
13294
+ return Response.json({ error: "invalid sessionId" }, { status: 400 });
13295
+ }
13296
+ try {
13297
+ const messages = await engineEntry(vendor).history.readHistory(sessionId);
13298
+ return Response.json({ messages });
13299
+ } catch (err) {
13300
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
13301
+ }
13302
+ }
13303
+ async function handleHistoryRequest(req, url) {
13304
+ if (url.pathname !== SESSIONS_ROUTE && url.pathname !== MESSAGES_ROUTE)
13305
+ return null;
13306
+ if (req.method !== "GET")
13307
+ return Response.json({ error: "method not allowed" }, { status: 405 });
13308
+ return url.pathname === SESSIONS_ROUTE ? handleSessions(url) : handleMessages(url);
13309
+ }
13310
+ var SESSIONS_ROUTE = "/api/history/sessions", MESSAGES_ROUTE = "/api/history/messages";
13311
+ var init_history4 = __esm(() => {
13312
+ init_registry();
13313
+ });
13314
+
13259
13315
  // src/web/notes.ts
13260
13316
  import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
13261
13317
  import { join as join11 } from "path";
@@ -13321,6 +13377,89 @@ var init_notes = __esm(() => {
13321
13377
  init_env();
13322
13378
  });
13323
13379
 
13380
+ // src/web/themes.ts
13381
+ function resolveHex(themeJson, value, chain = []) {
13382
+ if (value === undefined)
13383
+ return null;
13384
+ if (typeof value !== "string")
13385
+ return resolveHex(themeJson, value.dark, chain);
13386
+ if (value === "transparent" || value === "none")
13387
+ return null;
13388
+ if (value.startsWith("#"))
13389
+ return value;
13390
+ if (chain.includes(value))
13391
+ return null;
13392
+ const next = themeJson.defs?.[value] ?? themeJson.theme[value];
13393
+ return resolveHex(themeJson, next, [...chain, value]);
13394
+ }
13395
+ function clampByte(n) {
13396
+ return Math.max(0, Math.min(255, Math.round(n)));
13397
+ }
13398
+ function mix(a, b, t) {
13399
+ const pa = Number.parseInt(a.slice(1), 16);
13400
+ const pb = Number.parseInt(b.slice(1), 16);
13401
+ const ch = (p, shift) => p >> shift & 255;
13402
+ const out = clampByte(ch(pa, 16) + (ch(pb, 16) - ch(pa, 16)) * t) << 16 | clampByte(ch(pa, 8) + (ch(pb, 8) - ch(pa, 8)) * t) << 8 | clampByte(ch(pa, 0) + (ch(pb, 0) - ch(pa, 0)) * t);
13403
+ return `#${out.toString(16).padStart(6, "0")}`;
13404
+ }
13405
+ function toWebPalette(themeJson) {
13406
+ const slot = (name) => resolveHex(themeJson, themeJson.theme[name]);
13407
+ const bg = slot("background");
13408
+ const fg = slot("text");
13409
+ if (!bg || !fg)
13410
+ return null;
13411
+ const muted = slot("textMuted") ?? mix(fg, bg, 0.35);
13412
+ const primary = slot("primary") ?? fg;
13413
+ const line = slot("border") ?? mix(fg, bg, 0.8);
13414
+ return {
13415
+ bg,
13416
+ surface: slot("backgroundPanel") ?? bg,
13417
+ inset: slot("backgroundElement") ?? bg,
13418
+ menu: slot("backgroundMenu") ?? slot("backgroundElement") ?? bg,
13419
+ line,
13420
+ "line-subtle": slot("borderSubtle") ?? line,
13421
+ "line-active": slot("borderActive") ?? mix(line, fg, 0.3),
13422
+ fg,
13423
+ muted,
13424
+ subtle: mix(muted, bg, 0.35),
13425
+ primary,
13426
+ "primary-hover": mix(primary, fg, 0.35),
13427
+ "kobe-orange": primary,
13428
+ "kobe-green": slot("success") ?? fg,
13429
+ "kobe-blue": slot("info") ?? fg,
13430
+ "kobe-red": slot("error") ?? fg,
13431
+ "kobe-yellow": slot("warning") ?? fg,
13432
+ "kobe-violet": slot("secondary") ?? primary
13433
+ };
13434
+ }
13435
+ function handleThemesRequest(req, url) {
13436
+ if (url.pathname !== THEMES_ROUTE)
13437
+ return null;
13438
+ if (req.method !== "GET")
13439
+ return Response.json({ error: "method not allowed" }, { status: 405 });
13440
+ return Response.json({ themes: WEB_THEMES });
13441
+ }
13442
+ var THEME_JSONS, WEB_THEMES, THEMES_ROUTE = "/api/themes";
13443
+ var init_themes = __esm(() => {
13444
+ init_claude();
13445
+ init_conductor();
13446
+ init_dracula();
13447
+ init_nord();
13448
+ init_opencode();
13449
+ init_osaka_jade();
13450
+ init_tokyonight();
13451
+ THEME_JSONS = {
13452
+ claude: claude_default,
13453
+ conductor: conductor_default,
13454
+ dracula: dracula_default,
13455
+ nord: nord_default,
13456
+ opencode: opencode_default,
13457
+ "osaka-jade": osaka_jade_default,
13458
+ tokyonight: tokyonight_default
13459
+ };
13460
+ WEB_THEMES = Object.fromEntries(Object.entries(THEME_JSONS).map(([name, json]) => [name, toWebPalette(json)]).filter((entry) => entry[1] !== null));
13461
+ });
13462
+
13324
13463
  // ../kobe-web/server/spa-channels.ts
13325
13464
  var SPA_CHANNELS, SPA_CHANNEL_SET;
13326
13465
  var init_spa_channels = __esm(() => {
@@ -13328,7 +13467,10 @@ var init_spa_channels = __esm(() => {
13328
13467
  "task.snapshot",
13329
13468
  "active-task",
13330
13469
  "engine-state",
13331
- "update"
13470
+ "update",
13471
+ "task.jobs",
13472
+ "worktree.changes",
13473
+ "ui-prefs"
13332
13474
  ];
13333
13475
  SPA_CHANNEL_SET = new Set(SPA_CHANNELS);
13334
13476
  });
@@ -13349,6 +13491,9 @@ class DaemonLink {
13349
13491
  activeTaskId = null;
13350
13492
  engineStates = {};
13351
13493
  update = null;
13494
+ jobs = {};
13495
+ worktreeChanges = {};
13496
+ uiPrefs = null;
13352
13497
  async start() {
13353
13498
  await this.connectOnce(true);
13354
13499
  }
@@ -13358,6 +13503,9 @@ class DaemonLink {
13358
13503
  activeTaskId: this.activeTaskId,
13359
13504
  engineStates: this.engineStates,
13360
13505
  update: this.update,
13506
+ jobs: this.jobs,
13507
+ worktreeChanges: this.worktreeChanges,
13508
+ uiPrefs: this.uiPrefs,
13361
13509
  connected: this.connected
13362
13510
  };
13363
13511
  }
@@ -13399,6 +13547,7 @@ class DaemonLink {
13399
13547
  if (hello.tasks)
13400
13548
  this.tasks = hello.tasks;
13401
13549
  this.engineStates = {};
13550
+ this.jobs = {};
13402
13551
  client.on("*", (frame) => this.onFrame(frame.name, frame.payload));
13403
13552
  client.onLifecycle("close", () => this.onDrop(client));
13404
13553
  await client.subscribe({ role: "gui", channels: SPA_CHANNELS });
@@ -13432,9 +13581,16 @@ class DaemonLink {
13432
13581
  }
13433
13582
  onFrame(name, payload) {
13434
13583
  switch (name) {
13435
- case "task.snapshot":
13436
- this.tasks = payload.tasks;
13584
+ case "task.snapshot": {
13585
+ const tasks = payload.tasks;
13586
+ this.tasks = tasks;
13587
+ const live = new Set(tasks.map((t) => t.id));
13588
+ const kept = Object.entries(this.engineStates).filter(([id]) => live.has(id));
13589
+ if (kept.length !== Object.keys(this.engineStates).length) {
13590
+ this.engineStates = Object.fromEntries(kept);
13591
+ }
13437
13592
  break;
13593
+ }
13438
13594
  case "active-task":
13439
13595
  this.activeTaskId = payload.taskId;
13440
13596
  break;
@@ -13446,6 +13602,22 @@ class DaemonLink {
13446
13602
  case "update":
13447
13603
  this.update = payload.info;
13448
13604
  break;
13605
+ case "task.jobs": {
13606
+ const job = payload;
13607
+ if (job.phase === "running") {
13608
+ this.jobs = { ...this.jobs, [job.taskId]: job };
13609
+ } else {
13610
+ const { [job.taskId]: _done, ...rest } = this.jobs;
13611
+ this.jobs = rest;
13612
+ }
13613
+ break;
13614
+ }
13615
+ case "worktree.changes":
13616
+ this.worktreeChanges = payload.changes;
13617
+ break;
13618
+ case "ui-prefs":
13619
+ this.uiPrefs = payload;
13620
+ break;
13449
13621
  case "daemon.stopping":
13450
13622
  return;
13451
13623
  default:
@@ -13474,6 +13646,31 @@ var init_daemon_link = __esm(() => {
13474
13646
  init_spa_channels();
13475
13647
  });
13476
13648
 
13649
+ // ../kobe-web/server/rpc-allowlist.ts
13650
+ var WEB_RPC_ALLOWLIST, WEB_RPC_ALLOWSET;
13651
+ var init_rpc_allowlist = __esm(() => {
13652
+ WEB_RPC_ALLOWLIST = [
13653
+ "daemon.status",
13654
+ "task.list",
13655
+ "task.get",
13656
+ "task.create",
13657
+ "task.archive",
13658
+ "task.rename",
13659
+ "task.setBranch",
13660
+ "task.setVendor",
13661
+ "task.delete",
13662
+ "task.pin",
13663
+ "task.move",
13664
+ "task.status",
13665
+ "task.ensureMain",
13666
+ "task.ensureWorktree",
13667
+ "task.setActive",
13668
+ "worktree.discoverAdoptable",
13669
+ "worktree.adopt"
13670
+ ];
13671
+ WEB_RPC_ALLOWSET = new Set(WEB_RPC_ALLOWLIST);
13672
+ });
13673
+
13477
13674
  // ../kobe-web/server/session.ts
13478
13675
  async function getTask2(link, taskId) {
13479
13676
  const { task } = await link.request("task.get", { taskId });
@@ -13524,9 +13721,15 @@ async function terminalSpec(link, taskId) {
13524
13721
  const shell = process.env.SHELL?.trim() || "/bin/zsh";
13525
13722
  return { cwd: worktreePath, command: [shell, "-il"] };
13526
13723
  }
13724
+ async function tearDownTaskSession(taskId) {
13725
+ const session = tmuxSessionName(taskId);
13726
+ await switchClientBeforeKill(session).catch(() => {});
13727
+ await killSession(session).catch(() => {});
13728
+ }
13527
13729
  var init_session = __esm(() => {
13528
13730
  init_interactive_command();
13529
13731
  init_repo_init();
13732
+ init_client2();
13530
13733
  init_tmux();
13531
13734
  });
13532
13735
 
@@ -13570,20 +13773,35 @@ data: ${JSON.stringify(data)}
13570
13773
  }
13571
13774
  });
13572
13775
  }
13573
- async function rpcResponse(req, link) {
13776
+ async function rpcResponse(req, link, tearDown) {
13574
13777
  try {
13575
13778
  const { name, payload } = await req.json();
13576
13779
  if (!name)
13577
13780
  return Response.json({ error: "missing rpc name" }, { status: 400 });
13578
- if (name === "hello" || name === "subscribe" || name === "daemon.stop") {
13579
- throw new Error(`rpc ${name} is not exposed to the web UI`);
13781
+ if (!WEB_RPC_ALLOWSET.has(name)) {
13782
+ return Response.json({ error: `rpc ${name} is not exposed to the web UI` }, { status: 403 });
13580
13783
  }
13581
13784
  const result = await link.request(name, payload);
13785
+ const taskId = payload?.taskId;
13786
+ if (typeof taskId === "string") {
13787
+ const archiving = name === "task.archive" && payload.archived !== false;
13788
+ if (name === "task.delete" || archiving)
13789
+ tearDown(taskId);
13790
+ }
13582
13791
  return Response.json({ result });
13583
13792
  } catch (err) {
13584
13793
  return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
13585
13794
  }
13586
13795
  }
13796
+ async function enginesResponse() {
13797
+ try {
13798
+ const ids = await availableEngineIds();
13799
+ const engines = ids.map((id) => ({ id, label: engineDisplayName(id) }));
13800
+ return Response.json({ engines: engines.length > 0 ? engines : [{ id: "claude", label: "Claude" }] });
13801
+ } catch (err) {
13802
+ return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
13803
+ }
13804
+ }
13587
13805
  async function sessionResponse(req, link) {
13588
13806
  try {
13589
13807
  const { taskId } = await req.json();
@@ -13604,6 +13822,49 @@ async function specResponse(url, link, spec) {
13604
13822
  return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
13605
13823
  }
13606
13824
  }
13825
+ function createRequestHandler(deps) {
13826
+ const { link, sseSends, staticDir } = deps;
13827
+ const tearDown = deps.tearDownSession ?? ((taskId) => void tearDownTaskSession(taskId));
13828
+ return async function handle(req) {
13829
+ const url = new URL(req.url);
13830
+ if (url.pathname === WEB_HEALTH_PATH)
13831
+ return new Response(WEB_HEALTH_MARKER);
13832
+ if (url.pathname === "/events") {
13833
+ return sseResponse((send2) => {
13834
+ send2("snapshot", link.snapshot());
13835
+ sseSends.add(send2);
13836
+ return () => {
13837
+ sseSends.delete(send2);
13838
+ };
13839
+ });
13840
+ }
13841
+ if (url.pathname === "/api/rpc" && req.method === "POST")
13842
+ return rpcResponse(req, link, tearDown);
13843
+ if (url.pathname === "/api/session" && req.method === "POST")
13844
+ return sessionResponse(req, link);
13845
+ if (url.pathname === "/api/engine-spec" && req.method === "GET")
13846
+ return specResponse(url, link, engineSpec);
13847
+ if (url.pathname === "/api/terminal-spec" && req.method === "GET")
13848
+ return specResponse(url, link, terminalSpec);
13849
+ if (url.pathname === "/api/engines" && req.method === "GET")
13850
+ return enginesResponse();
13851
+ const notes = await handleNotesRequest(req, url);
13852
+ if (notes)
13853
+ return notes;
13854
+ const diff = await handleDiffRequest(req, url);
13855
+ if (diff)
13856
+ return diff;
13857
+ const history = await handleHistoryRequest(req, url);
13858
+ if (history)
13859
+ return history;
13860
+ const themes = handleThemesRequest(req, url);
13861
+ if (themes)
13862
+ return themes;
13863
+ if (staticDir)
13864
+ return staticResponse(url.pathname, staticDir);
13865
+ return new Response("not found", { status: 404 });
13866
+ };
13867
+ }
13607
13868
  async function staticResponse(pathname, staticDir) {
13608
13869
  const rel = pathname === "/" ? "/index.html" : pathname;
13609
13870
  const resolved = normalize2(join12(staticDir, rel));
@@ -13670,41 +13931,9 @@ async function createBridgeServer(opts = {}) {
13670
13931
  for (const send2 of sseSends)
13671
13932
  send2("snapshot", link.snapshot());
13672
13933
  });
13673
- const server = Bun.serve({
13674
- port,
13675
- idleTimeout: 0,
13676
- async fetch(req) {
13677
- const url = new URL(req.url);
13678
- if (url.pathname === WEB_HEALTH_PATH)
13679
- return new Response(WEB_HEALTH_MARKER);
13680
- if (url.pathname === "/events") {
13681
- return sseResponse((send2) => {
13682
- send2("snapshot", link.snapshot());
13683
- sseSends.add(send2);
13684
- return () => {
13685
- sseSends.delete(send2);
13686
- };
13687
- });
13688
- }
13689
- if (url.pathname === "/api/rpc" && req.method === "POST")
13690
- return rpcResponse(req, link);
13691
- if (url.pathname === "/api/session" && req.method === "POST")
13692
- return sessionResponse(req, link);
13693
- if (url.pathname === "/api/engine-spec" && req.method === "GET")
13694
- return specResponse(url, link, engineSpec);
13695
- if (url.pathname === "/api/terminal-spec" && req.method === "GET")
13696
- return specResponse(url, link, terminalSpec);
13697
- const notes = await handleNotesRequest(req, url);
13698
- if (notes)
13699
- return notes;
13700
- const diff = await handleDiffRequest(req, url);
13701
- if (diff)
13702
- return diff;
13703
- if (staticDir)
13704
- return staticResponse(url.pathname, staticDir);
13705
- return new Response("not found", { status: 404 });
13706
- }
13707
- });
13934
+ const handle = createRequestHandler({ link, sseSends, staticDir });
13935
+ const hostname = process.env.KOBE_WEB_HOST?.trim() || "127.0.0.1";
13936
+ const server = Bun.serve({ port, hostname, idleTimeout: 0, fetch: handle });
13708
13937
  return {
13709
13938
  port: server.port ?? port,
13710
13939
  close() {
@@ -13715,8 +13944,13 @@ async function createBridgeServer(opts = {}) {
13715
13944
  }
13716
13945
  var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web";
13717
13946
  var init_bridge = __esm(() => {
13947
+ init_account_detect();
13948
+ init_interactive_command();
13949
+ init_history4();
13718
13950
  init_notes();
13951
+ init_themes();
13719
13952
  init_daemon_link();
13953
+ init_rpc_allowlist();
13720
13954
  init_session();
13721
13955
  });
13722
13956
 
@@ -13731,8 +13965,13 @@ __export(exports_web_cmd, {
13731
13965
  runWebSubcommand: () => runWebSubcommand
13732
13966
  });
13733
13967
  import { existsSync as existsSync12 } from "fs";
13968
+ import { homedir as homedir17 } from "os";
13734
13969
  import { resolve as resolve6 } from "path";
13735
13970
  import { fileURLToPath as fileURLToPath3 } from "url";
13971
+ function homeLabel() {
13972
+ const explicit = process.env.KOBE_HOME_DIR?.trim();
13973
+ return explicit ? `sandbox: ${explicit}` : `${homedir17()}/.kobe (production)`;
13974
+ }
13736
13975
  function resolveStaticDir() {
13737
13976
  const here = fileURLToPath3(import.meta.url);
13738
13977
  const candidates = [
@@ -13850,10 +14089,14 @@ async function runWebSubcommand(args) {
13850
14089
  };
13851
14090
  if (bridgeOnly) {
13852
14091
  process.stdout.write(`kobe web bridge listening on http://localhost:${bridge.port} (routes only)
14092
+ `);
14093
+ process.stdout.write(` home: ${homeLabel()}
13853
14094
  `);
13854
14095
  } else {
13855
14096
  pty = await startPtyServer({ webPort: bridge.port, takeover });
13856
14097
  process.stdout.write(`kobe web \u2192 http://localhost:${bridge.port}
14098
+ `);
14099
+ process.stdout.write(` home: ${homeLabel()}
13857
14100
  `);
13858
14101
  if (!pty) {
13859
14102
  process.stderr.write(`kobe web: PTY server not found; terminal tabs will be unavailable
@@ -14007,7 +14250,7 @@ __export(exports_hook_cmd, {
14007
14250
  parseWorktreeAddPath: () => parseWorktreeAddPath,
14008
14251
  ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
14009
14252
  });
14010
- import { homedir as homedir17 } from "os";
14253
+ import { homedir as homedir18 } from "os";
14011
14254
  import { join as join13, resolve as resolve7 } from "path";
14012
14255
  async function readTextWithTimeout(read, timeoutMs = STDIN_READ_TIMEOUT_MS) {
14013
14256
  let raceTimer;
@@ -14139,7 +14382,7 @@ function activityHookAdapters() {
14139
14382
  return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
14140
14383
  }
14141
14384
  function globalSettingsPath() {
14142
- return join13(homedir17(), ".claude", "settings.json");
14385
+ return join13(homedir18(), ".claude", "settings.json");
14143
14386
  }
14144
14387
  function persistedSyncPath(stored) {
14145
14388
  if (!stored || stored === "off")
@@ -19074,7 +19317,7 @@ var init_pulse = () => {};
19074
19317
  // src/tui/lib/sound.ts
19075
19318
  import { existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
19076
19319
  import { tmpdir as tmpdir2 } from "os";
19077
- import { basename as basename6, isAbsolute, join as join15, resolve as resolve8 } from "path";
19320
+ import { basename as basename6, isAbsolute as isAbsolute2, join as join15, resolve as resolve8 } from "path";
19078
19321
  function args(player, file, volume) {
19079
19322
  if (player === "ffplay")
19080
19323
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -19132,7 +19375,7 @@ function pulse(volume = 0.4) {
19132
19375
  var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
19133
19376
  var init_sound = __esm(() => {
19134
19377
  init_pulse();
19135
- pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
19378
+ pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
19136
19379
  DIR = join15(tmpdir2(), "kobe-sfx");
19137
19380
  PLAYERS = [
19138
19381
  "ffplay",
@@ -22586,9 +22829,9 @@ var init_task_actions = __esm(() => {
22586
22829
  // src/tui/lib/worktree-opener.ts
22587
22830
  import { spawn as spawn5 } from "child_process";
22588
22831
  import { existsSync as existsSync15 } from "fs";
22589
- import { basename as basename7, delimiter, isAbsolute as isAbsolute2, join as join17 } from "path";
22832
+ import { basename as basename7, delimiter, isAbsolute as isAbsolute3, join as join17 } from "path";
22590
22833
  function executableOnPath(command, env, exists) {
22591
- if (isAbsolute2(command))
22834
+ if (isAbsolute3(command))
22592
22835
  return exists(command);
22593
22836
  const pathEnv = env.PATH ?? "";
22594
22837
  for (const dir of pathEnv.split(delimiter)) {