@sma1lboy/kobe 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +264 -254
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -70,7 +70,7 @@ var init_package = __esm(() => {
70
70
  package_default = {
71
71
  $schema: "https://json.schemastore.org/package.json",
72
72
  name: "@sma1lboy/kobe",
73
- version: "0.7.7",
73
+ version: "0.7.9",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
@@ -4997,6 +4997,29 @@ var init_auto_title_poller = __esm(() => {
4997
4997
  init_chat_tab_naming();
4998
4998
  });
4999
4999
 
5000
+ // src/daemon/cwd-task.ts
5001
+ function normalize(p) {
5002
+ return p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
5003
+ }
5004
+ function isAncestorOrSelf(wt, cwd) {
5005
+ return cwd === wt || cwd.startsWith(`${wt}/`);
5006
+ }
5007
+ function matchTaskByCwd(tasks, cwd) {
5008
+ const target = normalize(cwd);
5009
+ let bestId;
5010
+ let bestLen = -1;
5011
+ for (const t of tasks) {
5012
+ if (!t.worktreePath)
5013
+ continue;
5014
+ const wt = normalize(t.worktreePath);
5015
+ if (isAncestorOrSelf(wt, target) && wt.length > bestLen) {
5016
+ bestLen = wt.length;
5017
+ bestId = t.id;
5018
+ }
5019
+ }
5020
+ return bestId;
5021
+ }
5022
+
5000
5023
  // src/daemon/event-bus.ts
5001
5024
  class DaemonEventBus {
5002
5025
  last = new Map;
@@ -5317,10 +5340,14 @@ async function startDaemonServer(orch, options = {}) {
5317
5340
  return {};
5318
5341
  }
5319
5342
  case "engine.reportEvent": {
5320
- const taskId = requireString(payload, "taskId");
5321
5343
  const kind = requireString(payload, "kind");
5322
5344
  if (!isEngineActivityKind(kind))
5323
5345
  throw new Error(`unknown engine event kind: ${kind}`);
5346
+ const explicitId = optionalString(payload, "taskId");
5347
+ const cwd = optionalString(payload, "cwd");
5348
+ const taskId = explicitId ?? (cwd ? matchTaskByCwd(orch.listTasks(), cwd) : undefined);
5349
+ if (!taskId)
5350
+ return {};
5324
5351
  const detail = optionalActivityDetail(payload);
5325
5352
  reportActivity(taskId, kind, detail);
5326
5353
  return {};
@@ -5863,172 +5890,6 @@ var init_prompt_delivery = __esm(() => {
5863
5890
  init_client2();
5864
5891
  });
5865
5892
 
5866
- // src/engine/claude-code-local/hook-adapter.ts
5867
- import { existsSync as existsSync2 } from "fs";
5868
- import { appendFile as appendFile3, mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
5869
- import { dirname as dirname6, join as join4 } from "path";
5870
- function isObject6(v) {
5871
- return !!v && typeof v === "object" && !Array.isArray(v);
5872
- }
5873
- async function readJsonObject(path6) {
5874
- try {
5875
- const parsed = JSON.parse(await readFile6(path6, "utf8"));
5876
- return isObject6(parsed) ? parsed : {};
5877
- } catch {
5878
- return {};
5879
- }
5880
- }
5881
- function isKobeWorktreeSyncGroup(group) {
5882
- if (!isObject6(group) || !Array.isArray(group.hooks))
5883
- return false;
5884
- return group.hooks.some((h) => isObject6(h) && typeof h.command === "string" && h.command.includes(WORKTREE_SYNC_MARKER));
5885
- }
5886
- function mergeWorktreeSyncHook(current, command) {
5887
- const { hooks: rawHooks, ...restSettings } = current;
5888
- const { WorktreeCreate, ...otherHooks } = isObject6(rawHooks) ? rawHooks : {};
5889
- const prior = Array.isArray(WorktreeCreate) ? WorktreeCreate : [];
5890
- const kept = prior.filter((g) => !isKobeWorktreeSyncGroup(g));
5891
- if (command !== null)
5892
- kept.push({ hooks: [{ type: "command", command }] });
5893
- const nextHooks = { ...otherHooks };
5894
- if (kept.length > 0)
5895
- nextHooks.WorktreeCreate = kept;
5896
- return Object.keys(nextHooks).length > 0 ? { ...restSettings, hooks: nextHooks } : { ...restSettings };
5897
- }
5898
- function buildClaudeHooks(taskId, inv = kobeCliInvocation()) {
5899
- const out = {};
5900
- for (const { event, matcher, verb } of EVENT_MAP) {
5901
- const command = shellQuoteArgv([...inv, "hook", verb, "--task-id", taskId]);
5902
- const group = { hooks: [{ type: "command", command }] };
5903
- if (matcher)
5904
- group.matcher = matcher;
5905
- out[event] = [group];
5906
- }
5907
- return out;
5908
- }
5909
- function mergeClaudeHooks(existing, kobeHooks) {
5910
- const merged = { ...existing };
5911
- for (const event of KOBE_HOOK_EVENTS)
5912
- merged[event] = kobeHooks[event];
5913
- return merged;
5914
- }
5915
-
5916
- class ClaudeHookAdapter {
5917
- vendor = "claude";
5918
- supportsHooks() {
5919
- return true;
5920
- }
5921
- supportsWorktreeSync() {
5922
- return true;
5923
- }
5924
- async installWorktreeSyncHook(settingsFilePath) {
5925
- await this.editWorktreeSyncHook(settingsFilePath, true);
5926
- }
5927
- async removeWorktreeSyncHook(settingsFilePath) {
5928
- await this.editWorktreeSyncHook(settingsFilePath, false);
5929
- }
5930
- async editWorktreeSyncHook(settingsFilePath, install) {
5931
- const current = await readJsonObject(settingsFilePath);
5932
- const command = install ? shellQuoteArgv([...kobeCliInvocation(), "hook", "worktree-created"]) : null;
5933
- const next = mergeWorktreeSyncHook(current, command);
5934
- if (JSON.stringify(next) === JSON.stringify(current))
5935
- return;
5936
- await mkdir5(dirname6(settingsFilePath), { recursive: true });
5937
- await writeFile4(settingsFilePath, `${JSON.stringify(next, null, 2)}
5938
- `);
5939
- }
5940
- async installTaskHooks(ctx) {
5941
- try {
5942
- const claudeDir = join4(ctx.worktreeDir, ".claude");
5943
- const settingsPath = join4(claudeDir, "settings.local.json");
5944
- await mkdir5(claudeDir, { recursive: true });
5945
- let current = {};
5946
- try {
5947
- const raw = await readFile6(settingsPath, "utf8");
5948
- const parsed = JSON.parse(raw);
5949
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
5950
- current = parsed;
5951
- } catch {}
5952
- const existingHooks = current.hooks && typeof current.hooks === "object" && !Array.isArray(current.hooks) ? current.hooks : {};
5953
- current.hooks = mergeClaudeHooks(existingHooks, buildClaudeHooks(ctx.taskId));
5954
- await writeFile4(settingsPath, `${JSON.stringify(current, null, 2)}
5955
- `);
5956
- await hideFromGit(ctx.worktreeDir, ".claude/settings.local.json");
5957
- } catch {}
5958
- }
5959
- }
5960
- async function hideFromGit(worktreeDir, relPath) {
5961
- if (hiddenWorktrees.has(worktreeDir))
5962
- return;
5963
- try {
5964
- const proc = Bun.spawn(["git", "-C", worktreeDir, "rev-parse", "--git-common-dir"], {
5965
- stdout: "pipe",
5966
- stderr: "ignore"
5967
- });
5968
- const [out, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
5969
- if (code !== 0)
5970
- return;
5971
- let commonDir = out.trim();
5972
- if (!commonDir)
5973
- return;
5974
- if (!commonDir.startsWith("/"))
5975
- commonDir = join4(worktreeDir, commonDir);
5976
- const excludePath = join4(commonDir, "info", "exclude");
5977
- const existing = existsSync2(excludePath) ? await readFile6(excludePath, "utf8") : "";
5978
- if (existing.split(`
5979
- `).some((l) => l.trim() === relPath)) {
5980
- hiddenWorktrees.add(worktreeDir);
5981
- return;
5982
- }
5983
- await mkdir5(join4(commonDir, "info"), { recursive: true });
5984
- await appendFile3(excludePath, `${existing.endsWith(`
5985
- `) || existing === "" ? "" : `
5986
- `}${relPath}
5987
- `);
5988
- hiddenWorktrees.add(worktreeDir);
5989
- } catch {}
5990
- }
5991
- var EVENT_MAP, KOBE_HOOK_EVENTS, WORKTREE_SYNC_MARKER = "worktree-created", hiddenWorktrees;
5992
- var init_hook_adapter = __esm(() => {
5993
- init_invocation();
5994
- EVENT_MAP = [
5995
- { event: "SessionStart", verb: "session-start" },
5996
- { event: "UserPromptSubmit", verb: "turn-start" },
5997
- { event: "Stop", verb: "turn-complete" },
5998
- { event: "StopFailure", verb: "turn-failed" },
5999
- { event: "Notification", matcher: "permission_prompt", verb: "awaiting-input" },
6000
- { event: "SessionEnd", verb: "session-end" }
6001
- ];
6002
- KOBE_HOOK_EVENTS = EVENT_MAP.map((e) => e.event);
6003
- hiddenWorktrees = new Set;
6004
- });
6005
-
6006
- // src/engine/hook-adapter.ts
6007
- function createEngineHookAdapter(vendor) {
6008
- if (vendor === "claude")
6009
- return new ClaudeHookAdapter;
6010
- return new NoopHookAdapter(vendor);
6011
- }
6012
-
6013
- class NoopHookAdapter {
6014
- vendor;
6015
- constructor(vendor) {
6016
- this.vendor = vendor;
6017
- }
6018
- supportsHooks() {
6019
- return false;
6020
- }
6021
- async installTaskHooks() {}
6022
- supportsWorktreeSync() {
6023
- return false;
6024
- }
6025
- async installWorktreeSyncHook() {}
6026
- async removeWorktreeSyncHook() {}
6027
- }
6028
- var init_hook_adapter2 = __esm(() => {
6029
- init_hook_adapter();
6030
- });
6031
-
6032
5893
  // src/tui/panes/terminal/tmux.ts
6033
5894
  var exports_tmux = {};
6034
5895
  __export(exports_tmux, {
@@ -6079,12 +5940,6 @@ async function ensureSession(opts) {
6079
5940
  }
6080
5941
  }
6081
5942
  async function ensureSessionImpl(opts) {
6082
- if (opts.taskId && opts.cwd) {
6083
- await createEngineHookAdapter(coerceVendorId(opts.vendor)).installTaskHooks({
6084
- worktreeDir: opts.cwd,
6085
- taskId: opts.taskId
6086
- });
6087
- }
6088
5943
  if (await sessionExists(opts.name)) {
6089
5944
  const sessionOptions = await getSessionOptions(opts.name, ["@kobe_worktree", "@kobe_vendor"]);
6090
5945
  const taggedWorktree = sessionOptions["@kobe_worktree"] ?? "";
@@ -6499,7 +6354,6 @@ async function quickCreate(session) {
6499
6354
  var CHAT_TAB_SWITCH_BINDINGS, CHAT_TAB_CLOSE_BINDING, CHAT_TAB_RENAME_BINDING, CHAT_TAB_ENGINE_PROMPT, CHAT_TAB_CHOOSE_ENGINE_BINDINGS, CHAT_TAB_STATE_OPTION = "@kobe_tab_state", PANE_VERSION_OPTION = "@kobe_pane_version", CHAT_TAB_STATUS_FORMAT = "#{?#{==:#{@kobe_tab_state},running},\u25CF,#{?#{==:#{@kobe_tab_state},done},\u2713,#{?#{==:#{@kobe_tab_state},error},!,#{?#{==:#{@kobe_tab_state},unknown},?,\u25CB}}}} #I:#W", CHAT_TAB_STATUS_CURRENT_FORMAT, ensureSessionLocks;
6500
6355
  var init_tmux = __esm(() => {
6501
6356
  init_invocation();
6502
- init_hook_adapter2();
6503
6357
  init_interactive_command();
6504
6358
  init_env();
6505
6359
  init_client2();
@@ -6544,14 +6398,14 @@ var exports_repo_init = {};
6544
6398
  __export(exports_repo_init, {
6545
6399
  resolveRepoInit: () => resolveRepoInit
6546
6400
  });
6547
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
6548
- import { join as join5 } from "path";
6401
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
6402
+ import { join as join4 } from "path";
6549
6403
  function repoFileScript(worktreePath) {
6550
- return existsSync3(join5(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
6404
+ return existsSync2(join4(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
6551
6405
  }
6552
6406
  function repoFilePrompt(worktreePath) {
6553
- const p = join5(worktreePath, INIT_PROMPT_REL);
6554
- if (!existsSync3(p))
6407
+ const p = join4(worktreePath, INIT_PROMPT_REL);
6408
+ if (!existsSync2(p))
6555
6409
  return;
6556
6410
  try {
6557
6411
  const text = readFileSync3(p, "utf8");
@@ -6572,8 +6426,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
6572
6426
  var INIT_SCRIPT_REL, INIT_PROMPT_REL;
6573
6427
  var init_repo_init = __esm(() => {
6574
6428
  init_repos();
6575
- INIT_SCRIPT_REL = join5(".kobe", "init.sh");
6576
- INIT_PROMPT_REL = join5(".kobe", "init-prompt.md");
6429
+ INIT_SCRIPT_REL = join4(".kobe", "init.sh");
6430
+ INIT_PROMPT_REL = join4(".kobe", "init-prompt.md");
6577
6431
  });
6578
6432
 
6579
6433
  // src/tui/panes/sidebar/worktree-changes.ts
@@ -7585,9 +7439,9 @@ var init_schema = () => {};
7585
7439
 
7586
7440
  // src/tui/context/theme/loader.ts
7587
7441
  import { readFileSync as readFileSync4, readdirSync } from "fs";
7588
- import { join as join6 } from "path";
7442
+ import { join as join5 } from "path";
7589
7443
  function userThemesDir() {
7590
- return join6(kobeStateDir(), "themes");
7444
+ return join5(kobeStateDir(), "themes");
7591
7445
  }
7592
7446
  function loadUserThemes() {
7593
7447
  const dir = userThemesDir();
@@ -7601,7 +7455,7 @@ function loadUserThemes() {
7601
7455
  for (const file of entries) {
7602
7456
  if (!file.endsWith(".json"))
7603
7457
  continue;
7604
- const path6 = join6(dir, file);
7458
+ const path6 = join5(dir, file);
7605
7459
  let parsed;
7606
7460
  try {
7607
7461
  const text = readFileSync4(path6, "utf8");
@@ -7631,8 +7485,8 @@ var exports_theme = {};
7631
7485
  __export(exports_theme, {
7632
7486
  runThemeSubcommand: () => runThemeSubcommand
7633
7487
  });
7634
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
7635
- import { basename as basename3, join as join7, resolve as resolve5 } from "path";
7488
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
7489
+ import { basename as basename3, join as join6, resolve as resolve5 } from "path";
7636
7490
  function fail2(message) {
7637
7491
  process.stderr.write(`kobe theme: ${message}
7638
7492
  `);
@@ -7663,7 +7517,7 @@ function listThemes() {
7663
7517
  } else {
7664
7518
  for (const f of userFiles) {
7665
7519
  const name = f.slice(0, -".json".length);
7666
- const path6 = join7(dir, f);
7520
+ const path6 = join6(dir, f);
7667
7521
  const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
7668
7522
  lines.push(` ${name}${overridesBundled} ${path6}`);
7669
7523
  }
@@ -7753,8 +7607,8 @@ async function addTheme(args) {
7753
7607
  }
7754
7608
  const dir = userThemesDir();
7755
7609
  mkdirSync3(dir, { recursive: true });
7756
- const dest = join7(dir, `${name}.json`);
7757
- if (existsSync4(dest) && !opts.force) {
7610
+ const dest = join6(dir, `${name}.json`);
7611
+ if (existsSync3(dest) && !opts.force) {
7758
7612
  fail2(`${dest} already exists (pass --force to overwrite)`);
7759
7613
  }
7760
7614
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
@@ -7771,8 +7625,8 @@ function removeTheme(args) {
7771
7625
  if (BUNDLED_NAMES.includes(name)) {
7772
7626
  fail2(`"${name}" is a built-in theme and cannot be removed`);
7773
7627
  }
7774
- const dest = join7(userThemesDir(), `${name}.json`);
7775
- if (!existsSync4(dest)) {
7628
+ const dest = join6(userThemesDir(), `${name}.json`);
7629
+ if (!existsSync3(dest)) {
7776
7630
  fail2(`no user theme named "${name}" (looked for ${dest})`);
7777
7631
  }
7778
7632
  unlinkSync(dest);
@@ -7955,9 +7809,9 @@ var init_daemon_cmd = __esm(() => {
7955
7809
  });
7956
7810
 
7957
7811
  // src/lib/skill-install.ts
7958
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
7812
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
7959
7813
  import { homedir as homedir9 } from "os";
7960
- import { join as join8 } from "path";
7814
+ import { join as join7 } from "path";
7961
7815
  function npxSkillsArgv(opts = {}) {
7962
7816
  return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
7963
7817
  }
@@ -7967,14 +7821,14 @@ function npxSkillsCommand(opts = {}) {
7967
7821
  function kobeSkillPaths(opts = {}) {
7968
7822
  const home = opts.home ?? homedir9();
7969
7823
  const cwd = opts.cwd ?? process.cwd();
7970
- return [join8(home, SKILL_REL_PATH), join8(cwd, SKILL_REL_PATH)];
7824
+ return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
7971
7825
  }
7972
7826
  function parseSkillVersion(content) {
7973
7827
  const m = content.match(/kobe-skill-version:\s*(\d+)/);
7974
7828
  return m ? Number.parseInt(m[1], 10) : null;
7975
7829
  }
7976
7830
  function kobeSkillState(opts) {
7977
- const path6 = kobeSkillPaths(opts).find((p) => existsSync5(p));
7831
+ const path6 = kobeSkillPaths(opts).find((p) => existsSync4(p));
7978
7832
  if (!path6) {
7979
7833
  return { installed: false, installedVersion: null, currentVersion: KOBE_SKILL_VERSION, stale: false };
7980
7834
  }
@@ -8026,9 +7880,9 @@ __export(exports_maintenance, {
8026
7880
  runReloadSubcommand: () => runReloadSubcommand,
8027
7881
  runDoctorSubcommand: () => runDoctorSubcommand
8028
7882
  });
8029
- import { existsSync as existsSync6, readFileSync as readFileSync7, statSync } from "fs";
7883
+ import { existsSync as existsSync5, readFileSync as readFileSync7, statSync } from "fs";
8030
7884
  import { unlink as unlink6 } from "fs/promises";
8031
- import { join as join9 } from "path";
7885
+ import { join as join8 } from "path";
8032
7886
  import { createInterface } from "readline";
8033
7887
  function isProcessAlive2(pid) {
8034
7888
  try {
@@ -8125,7 +7979,7 @@ async function runDoctorSubcommand(argv = []) {
8125
7979
  const socketPath = defaultDaemonSocketPath();
8126
7980
  const pidPath = defaultDaemonPidPath();
8127
7981
  const logPath = defaultDaemonLogPath();
8128
- const tasksPath = join9(kobeStateDir(), "tasks.json");
7982
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
8129
7983
  const statePath2 = kvStatePath();
8130
7984
  const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
8131
7985
  const status = await probeDaemonStatus(socketPath);
@@ -8153,7 +8007,7 @@ async function runDoctorSubcommand(argv = []) {
8153
8007
  } else {
8154
8008
  out.push("daemon: \u2717 not running (no pidfile)");
8155
8009
  }
8156
- if (existsSync6(socketPath))
8010
+ if (existsSync5(socketPath))
8157
8011
  out.push(` orphan socket file present: ${socketPath}`);
8158
8012
  const tail = tailFile(logPath, 8);
8159
8013
  if (tail) {
@@ -8244,7 +8098,7 @@ async function runResetSubcommand(argv) {
8244
8098
  const yes = argv.includes("--yes") || argv.includes("-y");
8245
8099
  const socketPath = defaultDaemonSocketPath();
8246
8100
  const pidPath = defaultDaemonPidPath();
8247
- const tasksPath = join9(kobeStateDir(), "tasks.json");
8101
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
8248
8102
  const statePath2 = kvStatePath();
8249
8103
  console.log("kobe reset will:");
8250
8104
  console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
@@ -8448,14 +8302,153 @@ var init_skill_cmd = __esm(() => {
8448
8302
  SKILL_VERBS = ["install", "status", "command"];
8449
8303
  });
8450
8304
 
8305
+ // src/engine/claude-code-local/hook-adapter.ts
8306
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
8307
+ import { dirname as dirname6 } from "path";
8308
+ function isObject6(v) {
8309
+ return !!v && typeof v === "object" && !Array.isArray(v);
8310
+ }
8311
+ async function readJsonObject(path6) {
8312
+ try {
8313
+ const parsed = JSON.parse(await readFile6(path6, "utf8"));
8314
+ return isObject6(parsed) ? parsed : {};
8315
+ } catch {
8316
+ return {};
8317
+ }
8318
+ }
8319
+ function isKobeWorktreeSyncGroup(group) {
8320
+ if (!isObject6(group) || !Array.isArray(group.hooks))
8321
+ return false;
8322
+ return group.hooks.some((h) => isObject6(h) && typeof h.command === "string" && h.command.includes(WORKTREE_SYNC_MARKER));
8323
+ }
8324
+ function isKobeActivityGroup(group) {
8325
+ if (!isObject6(group) || !Array.isArray(group.hooks))
8326
+ return false;
8327
+ return group.hooks.some((h) => isObject6(h) && typeof h.command === "string" && ACTIVITY_MARKERS.some((m) => h.command.includes(m)));
8328
+ }
8329
+ function mergeWorktreeSyncHook(current, command) {
8330
+ const { hooks: rawHooks, ...restSettings } = current;
8331
+ const { WorktreeCreate, ...otherHooks } = isObject6(rawHooks) ? rawHooks : {};
8332
+ const prior = Array.isArray(WorktreeCreate) ? WorktreeCreate : [];
8333
+ const kept = prior.filter((g) => !isKobeWorktreeSyncGroup(g));
8334
+ if (command !== null)
8335
+ kept.push({ hooks: [{ type: "command", command }] });
8336
+ const nextHooks = { ...otherHooks };
8337
+ if (kept.length > 0)
8338
+ nextHooks.WorktreeCreate = kept;
8339
+ return Object.keys(nextHooks).length > 0 ? { ...restSettings, hooks: nextHooks } : { ...restSettings };
8340
+ }
8341
+ function buildClaudeHooks(inv = kobeCliInvocation()) {
8342
+ const out = {};
8343
+ for (const { event, matcher, verb } of EVENT_MAP) {
8344
+ const command = shellQuoteArgv([...inv, "hook", verb]);
8345
+ const group = { hooks: [{ type: "command", command }] };
8346
+ if (matcher)
8347
+ group.matcher = matcher;
8348
+ out[event] = [group];
8349
+ }
8350
+ return out;
8351
+ }
8352
+ function mergeActivityHooks(current, install, inv = kobeCliInvocation()) {
8353
+ const { hooks: rawHooks, ...restSettings } = current;
8354
+ const hooks = isObject6(rawHooks) ? { ...rawHooks } : {};
8355
+ const built = install ? buildClaudeHooks(inv) : {};
8356
+ for (const { event } of EVENT_MAP) {
8357
+ const prior = Array.isArray(hooks[event]) ? hooks[event] : [];
8358
+ const kept = prior.filter((g) => !isKobeActivityGroup(g));
8359
+ if (install && Array.isArray(built[event]))
8360
+ kept.push(...built[event]);
8361
+ if (kept.length > 0)
8362
+ hooks[event] = kept;
8363
+ else
8364
+ delete hooks[event];
8365
+ }
8366
+ return Object.keys(hooks).length > 0 ? { ...restSettings, hooks } : { ...restSettings };
8367
+ }
8368
+
8369
+ class ClaudeHookAdapter {
8370
+ vendor = "claude";
8371
+ supportsHooks() {
8372
+ return true;
8373
+ }
8374
+ supportsWorktreeSync() {
8375
+ return true;
8376
+ }
8377
+ async installActivityHooks(settingsFilePath) {
8378
+ await this.editSettings(settingsFilePath, (cur) => mergeActivityHooks(cur, true));
8379
+ }
8380
+ async removeActivityHooks(settingsFilePath) {
8381
+ await this.editSettings(settingsFilePath, (cur) => mergeActivityHooks(cur, false));
8382
+ }
8383
+ async installWorktreeSyncHook(settingsFilePath) {
8384
+ const command = shellQuoteArgv([...kobeCliInvocation(), "hook", "worktree-created"]);
8385
+ await this.editSettings(settingsFilePath, (cur) => mergeWorktreeSyncHook(cur, command));
8386
+ }
8387
+ async removeWorktreeSyncHook(settingsFilePath) {
8388
+ await this.editSettings(settingsFilePath, (cur) => mergeWorktreeSyncHook(cur, null));
8389
+ }
8390
+ async editSettings(settingsFilePath, transform) {
8391
+ try {
8392
+ const current = await readJsonObject(settingsFilePath);
8393
+ const next = transform(current);
8394
+ if (JSON.stringify(next) === JSON.stringify(current))
8395
+ return;
8396
+ await mkdir5(dirname6(settingsFilePath), { recursive: true });
8397
+ await writeFile4(settingsFilePath, `${JSON.stringify(next, null, 2)}
8398
+ `);
8399
+ } catch {}
8400
+ }
8401
+ }
8402
+ var EVENT_MAP, KOBE_HOOK_EVENTS, ACTIVITY_MARKERS, WORKTREE_SYNC_MARKER = "worktree-created";
8403
+ var init_hook_adapter = __esm(() => {
8404
+ init_invocation();
8405
+ EVENT_MAP = [
8406
+ { event: "SessionStart", verb: "session-start" },
8407
+ { event: "UserPromptSubmit", verb: "turn-start" },
8408
+ { event: "Stop", verb: "turn-complete" },
8409
+ { event: "StopFailure", verb: "turn-failed" },
8410
+ { event: "Notification", matcher: "permission_prompt", verb: "awaiting-input" },
8411
+ { event: "SessionEnd", verb: "session-end" }
8412
+ ];
8413
+ KOBE_HOOK_EVENTS = EVENT_MAP.map((e) => e.event);
8414
+ ACTIVITY_MARKERS = EVENT_MAP.map((e) => shellQuoteArgv(["hook", e.verb]));
8415
+ });
8416
+
8417
+ // src/engine/hook-adapter.ts
8418
+ function createEngineHookAdapter(vendor) {
8419
+ if (vendor === "claude")
8420
+ return new ClaudeHookAdapter;
8421
+ return new NoopHookAdapter(vendor);
8422
+ }
8423
+
8424
+ class NoopHookAdapter {
8425
+ vendor;
8426
+ constructor(vendor) {
8427
+ this.vendor = vendor;
8428
+ }
8429
+ supportsHooks() {
8430
+ return false;
8431
+ }
8432
+ async installActivityHooks() {}
8433
+ async removeActivityHooks() {}
8434
+ supportsWorktreeSync() {
8435
+ return false;
8436
+ }
8437
+ async installWorktreeSyncHook() {}
8438
+ async removeWorktreeSyncHook() {}
8439
+ }
8440
+ var init_hook_adapter2 = __esm(() => {
8441
+ init_hook_adapter();
8442
+ });
8443
+
8451
8444
  // src/cli/hook-cmd.ts
8452
8445
  var exports_hook_cmd = {};
8453
8446
  __export(exports_hook_cmd, {
8454
8447
  runHookSubcommand: () => runHookSubcommand,
8455
- ensureDefaultWorktreeSync: () => ensureDefaultWorktreeSync
8448
+ ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
8456
8449
  });
8457
8450
  import { homedir as homedir10 } from "os";
8458
- import { dirname as dirname7, join as join10, resolve as resolve6 } from "path";
8451
+ import { dirname as dirname7, join as join9, resolve as resolve6 } from "path";
8459
8452
  async function readStdinPayload() {
8460
8453
  try {
8461
8454
  const text = await Promise.race([
@@ -8501,10 +8494,9 @@ async function runHookSubcommand(argv) {
8501
8494
  }
8502
8495
  if (!verb || !isEngineActivityKind(verb))
8503
8496
  return;
8504
- const taskId = flagValue(rest, "--task-id");
8505
- if (!taskId)
8506
- return;
8507
8497
  const payload = await readStdinPayload();
8498
+ const taskId = flagValue(rest, "--task-id");
8499
+ const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
8508
8500
  let detail;
8509
8501
  if (verb === "turn-failed") {
8510
8502
  detail = { failure: failureFromErrorType(payload.error_type) };
@@ -8515,7 +8507,11 @@ async function runHookSubcommand(argv) {
8515
8507
  if (!client)
8516
8508
  return;
8517
8509
  try {
8518
- await client.request("engine.reportEvent", { taskId, kind: verb, ...detail ? { detail } : {} });
8510
+ await client.request("engine.reportEvent", {
8511
+ ...taskId ? { taskId } : { cwd },
8512
+ kind: verb,
8513
+ ...detail ? { detail } : {}
8514
+ });
8519
8515
  } finally {
8520
8516
  client.close();
8521
8517
  }
@@ -8555,12 +8551,18 @@ async function deriveRepoRoot(worktreePath) {
8555
8551
  }
8556
8552
  function syncSettingsPath(scope) {
8557
8553
  if (scope.kind === "repo")
8558
- return join10(resolve6(scope.path), ".claude", "settings.json");
8559
- return join10(homedir10(), ".claude", "settings.json");
8554
+ return join9(resolve6(scope.path), ".claude", "settings.json");
8555
+ return globalSettingsPath();
8560
8556
  }
8561
8557
  function worktreeSyncAdapters() {
8562
8558
  return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsWorktreeSync());
8563
8559
  }
8560
+ function activityHookAdapters() {
8561
+ return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
8562
+ }
8563
+ function globalSettingsPath() {
8564
+ return join9(homedir10(), ".claude", "settings.json");
8565
+ }
8564
8566
  function persistedSyncPath(stored) {
8565
8567
  if (!stored || stored === "off")
8566
8568
  return;
@@ -8570,16 +8572,19 @@ function persistedSyncPath(stored) {
8570
8572
  return syncSettingsPath({ kind: "repo", path: stored.slice(5) });
8571
8573
  return stored;
8572
8574
  }
8573
- async function ensureDefaultWorktreeSync() {
8575
+ async function ensureGlobalKobeHooks() {
8574
8576
  try {
8577
+ const globalPath = globalSettingsPath();
8578
+ for (const a of activityHookAdapters())
8579
+ await a.installActivityHooks(globalPath);
8575
8580
  const stored = getPersistedString(SYNC_SETTING_KEY);
8576
8581
  if (stored === "off")
8577
8582
  return;
8578
- const adapters = worktreeSyncAdapters();
8579
- if (adapters.length === 0)
8583
+ const syncAdapters = worktreeSyncAdapters();
8584
+ if (syncAdapters.length === 0)
8580
8585
  return;
8581
8586
  const path6 = persistedSyncPath(stored) ?? syncSettingsPath({ kind: "global" });
8582
- for (const a of adapters)
8587
+ for (const a of syncAdapters)
8583
8588
  await a.installWorktreeSyncHook(path6);
8584
8589
  if (!stored)
8585
8590
  setPersistedString(SYNC_SETTING_KEY, path6);
@@ -13853,7 +13858,7 @@ var init_rename_task_dialog = __esm(() => {
13853
13858
 
13854
13859
  // src/engine/claude-code-local/binary.ts
13855
13860
  import { spawnSync as spawnSync5 } from "child_process";
13856
- import { existsSync as existsSync8, statSync as statSync3 } from "fs";
13861
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
13857
13862
  import { homedir as homedir12 } from "os";
13858
13863
  import path7 from "path";
13859
13864
  async function findClaudeBinary(deps = defaultDeps4) {
@@ -13940,7 +13945,7 @@ var init_binary = __esm(() => {
13940
13945
  return;
13941
13946
  if (first.startsWith("claude:") && first.includes("aliased to")) {
13942
13947
  const aliasTarget = first.split("aliased to")[1]?.trim();
13943
- return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
13948
+ return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
13944
13949
  }
13945
13950
  return first;
13946
13951
  },
@@ -13957,7 +13962,7 @@ var init_binary = __esm(() => {
13957
13962
 
13958
13963
  // src/engine/codex-local/binary.ts
13959
13964
  import { spawnSync as spawnSync6 } from "child_process";
13960
- import { existsSync as existsSync9, statSync as statSync4 } from "fs";
13965
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
13961
13966
  import { homedir as homedir13 } from "os";
13962
13967
  import path8 from "path";
13963
13968
  async function findCodexBinary(deps = defaultDeps5) {
@@ -14028,7 +14033,7 @@ var init_binary2 = __esm(() => {
14028
14033
  return;
14029
14034
  if (first.startsWith("codex:") && first.includes("aliased to")) {
14030
14035
  const aliasTarget = first.split("aliased to")[1]?.trim();
14031
- return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
14036
+ return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
14032
14037
  }
14033
14038
  return first;
14034
14039
  },
@@ -14045,7 +14050,7 @@ var init_binary2 = __esm(() => {
14045
14050
 
14046
14051
  // src/engine/copilot-local/binary.ts
14047
14052
  import { spawnSync as spawnSync7 } from "child_process";
14048
- import { existsSync as existsSync10, statSync as statSync5 } from "fs";
14053
+ import { existsSync as existsSync9, statSync as statSync5 } from "fs";
14049
14054
  import { homedir as homedir14 } from "os";
14050
14055
  import path9 from "path";
14051
14056
  async function findCopilotBinary(deps = defaultDeps6) {
@@ -14140,7 +14145,7 @@ var init_binary3 = __esm(() => {
14140
14145
  return;
14141
14146
  if (first.startsWith("copilot:") && first.includes("aliased to")) {
14142
14147
  const aliasTarget = first.split("aliased to")[1]?.trim();
14143
- return aliasTarget && existsSync10(aliasTarget) ? aliasTarget : undefined;
14148
+ return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
14144
14149
  }
14145
14150
  return first;
14146
14151
  },
@@ -14529,7 +14534,7 @@ var init_dialog_confirm = __esm(() => {
14529
14534
 
14530
14535
  // src/tui/component/settings-dialog/actions.ts
14531
14536
  import { unlinkSync as unlinkSync2 } from "fs";
14532
- import { join as join12 } from "path";
14537
+ import { join as join11 } from "path";
14533
14538
  function hasRestartableDaemon(orchestrator) {
14534
14539
  return orchestrator instanceof RemoteOrchestrator;
14535
14540
  }
@@ -14546,7 +14551,7 @@ async function confirmResetState(dialog, kv, renderer) {
14546
14551
  return;
14547
14552
  kv.clear();
14548
14553
  try {
14549
- unlinkSync2(join12(homeDir(), ".kobe", "tasks.json"));
14554
+ unlinkSync2(join11(homeDir(), ".kobe", "tasks.json"));
14550
14555
  } catch (err) {
14551
14556
  if (err.code !== "ENOENT") {
14552
14557
  console.error("kobe: failed to delete tasks.json during reset:", err);
@@ -16102,8 +16107,8 @@ var init_persisted_ui_prefs = __esm(() => {
16102
16107
 
16103
16108
  // src/tui/lib/worktree-opener.ts
16104
16109
  import { spawn as spawn3 } from "child_process";
16105
- import { existsSync as existsSync11 } from "fs";
16106
- import { basename as basename4, delimiter, isAbsolute, join as join13 } from "path";
16110
+ import { existsSync as existsSync10 } from "fs";
16111
+ import { basename as basename4, delimiter, isAbsolute, join as join12 } from "path";
16107
16112
  function executableOnPath(command, env, exists) {
16108
16113
  if (isAbsolute(command))
16109
16114
  return exists(command);
@@ -16111,7 +16116,7 @@ function executableOnPath(command, env, exists) {
16111
16116
  for (const dir of pathEnv.split(delimiter)) {
16112
16117
  if (!dir)
16113
16118
  continue;
16114
- if (exists(join13(dir, command)))
16119
+ if (exists(join12(dir, command)))
16115
16120
  return true;
16116
16121
  }
16117
16122
  return false;
@@ -16131,7 +16136,7 @@ function labelForOverride(command) {
16131
16136
  function detectWorktreeOpener(deps = {}) {
16132
16137
  const env = deps.env ?? process.env;
16133
16138
  const platform = deps.platform ?? process.platform;
16134
- const exists = deps.exists ?? existsSync11;
16139
+ const exists = deps.exists ?? existsSync10;
16135
16140
  const override = env.KOBE_OPEN_EDITOR?.trim();
16136
16141
  if (override) {
16137
16142
  return { id: "env", label: labelForOverride(override), command: override, args: [] };
@@ -17101,13 +17106,7 @@ var init_keys = __esm(() => {
17101
17106
  init_keymap();
17102
17107
  });
17103
17108
 
17104
- // src/tui/panes/sidebar/Sidebar.tsx
17105
- import { TextAttributes as TextAttributes9 } from "@opentui/core";
17106
- function truncateBranchLabel(branch, max = BRANCH_LABEL_MAX) {
17107
- if (branch.length <= max)
17108
- return branch;
17109
- return `${branch.slice(0, Math.max(0, max - 1))}\u2026`;
17110
- }
17109
+ // src/tui/panes/sidebar/labels.ts
17111
17110
  function truncateTitle(title, max) {
17112
17111
  if (max <= 0)
17113
17112
  return "";
@@ -17115,6 +17114,17 @@ function truncateTitle(title, max) {
17115
17114
  return title;
17116
17115
  return `${title.slice(0, Math.max(0, max - 1))}\u2026`;
17117
17116
  }
17117
+ function spacedTitle(title, max) {
17118
+ return ` ${truncateTitle(title, Math.max(0, max))}`;
17119
+ }
17120
+
17121
+ // src/tui/panes/sidebar/Sidebar.tsx
17122
+ import { TextAttributes as TextAttributes9 } from "@opentui/core";
17123
+ function truncateBranchLabel(branch, max = BRANCH_LABEL_MAX) {
17124
+ if (branch.length <= max)
17125
+ return branch;
17126
+ return `${branch.slice(0, Math.max(0, max - 1))}\u2026`;
17127
+ }
17118
17128
  function approxCellWidth(s) {
17119
17129
  let n = 0;
17120
17130
  for (const ch of s)
@@ -17560,7 +17570,7 @@ function Sidebar(props) {
17560
17570
  setProp(_el$32, "flexDirection", "row");
17561
17571
  setProp(_el$32, "flexGrow", 1);
17562
17572
  setProp(_el$32, "paddingRight", 1);
17563
- setProp(_el$32, "gap", 1);
17573
+ setProp(_el$32, "gap", 0);
17564
17574
  setProp(_el$33, "wrapMode", "none");
17565
17575
  insert(_el$33, (() => {
17566
17576
  var _c$2 = memo2(() => !!loading());
@@ -17568,14 +17578,14 @@ function Sidebar(props) {
17568
17578
  })());
17569
17579
  setProp(_el$34, "wrapMode", "none");
17570
17580
  setProp(_el$34, "flexGrow", 1);
17571
- insert(_el$34, () => truncateTitle(titleText, titleBudget()));
17581
+ insert(_el$34, () => spacedTitle(titleText, titleBudget()));
17572
17582
  insert(_el$32, createComponent2(Show, {
17573
17583
  get when() {
17574
17584
  return loading();
17575
17585
  },
17576
17586
  get children() {
17577
17587
  var _el$35 = createElement("text");
17578
- insertNode(_el$35, createTextNode(`working`));
17588
+ insertNode(_el$35, createTextNode(` working`));
17579
17589
  setProp(_el$35, "wrapMode", "none");
17580
17590
  effect((_$p) => setProp(_el$35, "fg", theme.primary, _$p));
17581
17591
  return _el$35;
@@ -17588,7 +17598,7 @@ function Sidebar(props) {
17588
17598
  get children() {
17589
17599
  var _el$37 = createElement("text");
17590
17600
  setProp(_el$37, "wrapMode", "none");
17591
- insert(_el$37, () => truncatePathTail(abbrevHome(task.repo), subtitleBudget()));
17601
+ insert(_el$37, () => ` ${truncatePathTail(abbrevHome(task.repo), subtitleBudget())}`);
17592
17602
  effect((_p$) => {
17593
17603
  var _v$15 = theme.textMuted, _v$16 = TextAttributes9.DIM;
17594
17604
  _v$15 !== _p$.e && (_p$.e = setProp(_el$37, "fg", _v$15, _p$.e));
@@ -17635,7 +17645,7 @@ function Sidebar(props) {
17635
17645
  setProp(_el$40, "flexDirection", "row");
17636
17646
  setProp(_el$40, "flexGrow", 1);
17637
17647
  setProp(_el$40, "paddingRight", 1);
17638
- setProp(_el$40, "gap", 1);
17648
+ setProp(_el$40, "gap", 0);
17639
17649
  setProp(_el$41, "wrapMode", "none");
17640
17650
  insert(_el$41, (() => {
17641
17651
  var _c$3 = memo2(() => !!loading());
@@ -17643,14 +17653,14 @@ function Sidebar(props) {
17643
17653
  })());
17644
17654
  setProp(_el$42, "wrapMode", "none");
17645
17655
  setProp(_el$42, "flexGrow", 1);
17646
- insert(_el$42, () => truncateTitle(titleText, titleBudget()));
17656
+ insert(_el$42, () => spacedTitle(titleText, titleBudget()));
17647
17657
  insert(_el$40, createComponent2(Show, {
17648
17658
  get when() {
17649
17659
  return loading();
17650
17660
  },
17651
17661
  get children() {
17652
17662
  var _el$43 = createElement("text");
17653
- insertNode(_el$43, createTextNode(`working`));
17663
+ insertNode(_el$43, createTextNode(` working`));
17654
17664
  setProp(_el$43, "wrapMode", "none");
17655
17665
  effect((_$p) => setProp(_el$43, "fg", theme.primary, _$p));
17656
17666
  return _el$43;
@@ -17663,7 +17673,7 @@ function Sidebar(props) {
17663
17673
  children: (chip) => (() => {
17664
17674
  var _el$55 = createElement("text");
17665
17675
  setProp(_el$55, "wrapMode", "none");
17666
- insert(_el$55, () => chip().text);
17676
+ insert(_el$55, () => ` ${chip().text}`);
17667
17677
  effect((_$p) => setProp(_el$55, "fg", chip().tone === "error" ? theme.error : chip().tone === "warning" ? theme.warning : theme.primary, _$p));
17668
17678
  return _el$55;
17669
17679
  })()
@@ -17944,7 +17954,7 @@ var exports_host = {};
17944
17954
  __export(exports_host, {
17945
17955
  startTasksPane: () => startTasksPane
17946
17956
  });
17947
- import { existsSync as existsSync12 } from "fs";
17957
+ import { existsSync as existsSync11 } from "fs";
17948
17958
  import { TextAttributes as TextAttributes10 } from "@opentui/core";
17949
17959
  function TasksShell(props) {
17950
17960
  const themeCtx = useTheme();
@@ -18179,7 +18189,7 @@ function TasksShell(props) {
18179
18189
  async function openSelectedWorktree(id) {
18180
18190
  const task = props.tasks().find((t) => t.id === id);
18181
18191
  let worktree = task?.worktreePath;
18182
- if (!worktree || !existsSync12(worktree)) {
18192
+ if (!worktree || !existsSync11(worktree)) {
18183
18193
  if (!props.orch) {
18184
18194
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
18185
18195
  return;
@@ -18192,7 +18202,7 @@ function TasksShell(props) {
18192
18202
  }
18193
18203
  await props.reload();
18194
18204
  }
18195
- if (!worktree || !existsSync12(worktree))
18205
+ if (!worktree || !existsSync11(worktree))
18196
18206
  return;
18197
18207
  const opener = detectWorktreeOpener();
18198
18208
  if (!opener) {
@@ -18243,7 +18253,7 @@ function TasksShell(props) {
18243
18253
  const exists = await sessionExists(name);
18244
18254
  if (exists) {
18245
18255
  const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
18246
- if (cwd2 && existsSync12(cwd2)) {
18256
+ if (cwd2 && existsSync11(cwd2)) {
18247
18257
  await ensureSession({
18248
18258
  name,
18249
18259
  cwd: cwd2,
@@ -18257,7 +18267,7 @@ function TasksShell(props) {
18257
18267
  return;
18258
18268
  }
18259
18269
  let cwd = task?.worktreePath;
18260
- if (!cwd || !existsSync12(cwd)) {
18270
+ if (!cwd || !existsSync11(cwd)) {
18261
18271
  if (!props.orch) {
18262
18272
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
18263
18273
  return;
@@ -18270,7 +18280,7 @@ function TasksShell(props) {
18270
18280
  }
18271
18281
  await props.reload();
18272
18282
  }
18273
- if (!cwd || !existsSync12(cwd))
18283
+ if (!cwd || !existsSync11(cwd))
18274
18284
  return;
18275
18285
  const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
18276
18286
  const ready = await ensureSession({
@@ -19721,14 +19731,14 @@ var init_keys2 = __esm(() => {
19721
19731
 
19722
19732
  // src/tui/panes/filetree/open-external.ts
19723
19733
  import { spawn as spawn5 } from "child_process";
19724
- import { existsSync as existsSync13 } from "fs";
19734
+ import { existsSync as existsSync12 } from "fs";
19725
19735
  import { platform } from "os";
19726
19736
  function openExternally(absPath) {
19727
19737
  if (!absPath)
19728
19738
  return;
19729
19739
  const plat = platform();
19730
19740
  if (plat === "linux") {
19731
- if (existsSync13("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
19741
+ if (existsSync12("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
19732
19742
  spawnDetached("wslview", [absPath], () => {
19733
19743
  const child = spawn5("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
19734
19744
  let out = "";
@@ -21169,7 +21179,7 @@ async function ensureRepos(orchestrator) {
21169
21179
  }
21170
21180
  async function startDirectTmux() {
21171
21181
  setClientLogContext("gui");
21172
- Promise.resolve().then(() => (init_hook_cmd(), exports_hook_cmd)).then((m) => m.ensureDefaultWorktreeSync());
21182
+ Promise.resolve().then(() => (init_hook_cmd(), exports_hook_cmd)).then((m) => m.ensureGlobalKobeHooks());
21173
21183
  if (!await tmuxAvailable()) {
21174
21184
  console.error("kobe: tmux not found on PATH \u2014 install tmux to use kobe 0.6 direct mode");
21175
21185
  process.exitCode = 1;
@@ -21763,9 +21773,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
21763
21773
  var init_pulse = () => {};
21764
21774
 
21765
21775
  // src/tui/lib/sound.ts
21766
- import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "fs";
21776
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
21767
21777
  import { tmpdir as tmpdir2 } from "os";
21768
- import { basename as basename6, isAbsolute as isAbsolute2, join as join14, resolve as resolve8 } from "path";
21778
+ import { basename as basename6, isAbsolute as isAbsolute2, join as join13, resolve as resolve8 } from "path";
21769
21779
  function args(player, file, volume) {
21770
21780
  if (player === "ffplay")
21771
21781
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -21788,13 +21798,13 @@ function pickPlayer() {
21788
21798
  return cachedPlayer;
21789
21799
  const path12 = process.env.PATH ?? "";
21790
21800
  const segments = path12.split(":").filter(Boolean);
21791
- cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join14(dir, p)))) ?? null;
21801
+ cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync13(join13(dir, p)))) ?? null;
21792
21802
  return cachedPlayer;
21793
21803
  }
21794
21804
  async function ensureAsset() {
21795
21805
  cachedPath ??= (async () => {
21796
21806
  mkdirSync5(DIR, { recursive: true });
21797
- const dest = join14(DIR, basename6(pulseAsset));
21807
+ const dest = join13(DIR, basename6(pulseAsset));
21798
21808
  const out = Bun.file(dest);
21799
21809
  if (await out.exists())
21800
21810
  return dest;
@@ -21824,7 +21834,7 @@ var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
21824
21834
  var init_sound = __esm(() => {
21825
21835
  init_pulse();
21826
21836
  pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
21827
- DIR = join14(tmpdir2(), "kobe-sfx");
21837
+ DIR = join13(tmpdir2(), "kobe-sfx");
21828
21838
  PLAYERS = [
21829
21839
  "ffplay",
21830
21840
  "mpv",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@sma1lboy/kobe",
4
- "version": "0.7.7",
4
+ "version": "0.7.9",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",