@sma1lboy/kobe 0.7.8 → 0.7.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.
Files changed (2) hide show
  1. package/dist/cli/index.js +303 -338
  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.8",
73
+ version: "0.7.10",
74
74
  description: "TUI orchestrator for Claude Code (codename)",
75
75
  type: "module",
76
76
  packageManager: "bun@1.3.13",
@@ -4997,6 +4997,58 @@ 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
+ function findAdoptableWorktree(tasks, cwd) {
5023
+ const target = normalize(cwd);
5024
+ const repos = new Set;
5025
+ const known = new Set;
5026
+ for (const t of tasks) {
5027
+ if (t.repo)
5028
+ repos.add(normalize(t.repo));
5029
+ if (t.worktreePath)
5030
+ known.add(normalize(t.worktreePath));
5031
+ }
5032
+ const seg = `/${KOBE_WORKTREE_ROOT_SUBPATH}/`;
5033
+ for (const repo of repos) {
5034
+ const prefix = `${repo}${seg}`;
5035
+ if (!target.startsWith(prefix))
5036
+ continue;
5037
+ const rest = target.slice(prefix.length);
5038
+ const name = rest.split("/")[0];
5039
+ if (!name)
5040
+ continue;
5041
+ const worktreePath = `${prefix}${name}`;
5042
+ if (known.has(worktreePath))
5043
+ return;
5044
+ return { repo, worktreePath };
5045
+ }
5046
+ return;
5047
+ }
5048
+ var init_cwd_task = __esm(() => {
5049
+ init_paths();
5050
+ });
5051
+
5000
5052
  // src/daemon/event-bus.ts
5001
5053
  class DaemonEventBus {
5002
5054
  last = new Map;
@@ -5317,10 +5369,24 @@ async function startDaemonServer(orch, options = {}) {
5317
5369
  return {};
5318
5370
  }
5319
5371
  case "engine.reportEvent": {
5320
- const taskId = requireString(payload, "taskId");
5321
5372
  const kind = requireString(payload, "kind");
5322
5373
  if (!isEngineActivityKind(kind))
5323
5374
  throw new Error(`unknown engine event kind: ${kind}`);
5375
+ const explicitId = optionalString(payload, "taskId");
5376
+ const cwd = optionalString(payload, "cwd");
5377
+ if (!explicitId && cwd && kind === "session-start") {
5378
+ const cand = findAdoptableWorktree(orch.listTasks(), cwd);
5379
+ if (cand) {
5380
+ try {
5381
+ await orch.adoptWorktree({ repo: cand.repo, worktreePath: cand.worktreePath, ifExists: "return" });
5382
+ } catch (err) {
5383
+ logDaemonError("worktree-autosync", err);
5384
+ }
5385
+ }
5386
+ }
5387
+ const taskId = explicitId ?? (cwd ? matchTaskByCwd(orch.listTasks(), cwd) : undefined);
5388
+ if (!taskId)
5389
+ return {};
5324
5390
  const detail = optionalActivityDetail(payload);
5325
5391
  reportActivity(taskId, kind, detail);
5326
5392
  return {};
@@ -5464,6 +5530,7 @@ var init_server = __esm(() => {
5464
5530
  init_hook_events();
5465
5531
  init_version();
5466
5532
  init_auto_title_poller();
5533
+ init_cwd_task();
5467
5534
  init_paths2();
5468
5535
  init_protocol();
5469
5536
  DEFAULT_UPDATE_POLL_MS = 6 * 60 * 60 * 1000;
@@ -5863,172 +5930,6 @@ var init_prompt_delivery = __esm(() => {
5863
5930
  init_client2();
5864
5931
  });
5865
5932
 
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
5933
  // src/tui/panes/terminal/tmux.ts
6033
5934
  var exports_tmux = {};
6034
5935
  __export(exports_tmux, {
@@ -6079,12 +5980,6 @@ async function ensureSession(opts) {
6079
5980
  }
6080
5981
  }
6081
5982
  async function ensureSessionImpl(opts) {
6082
- if (opts.taskId && opts.cwd && opts.cwd.includes(`/${KOBE_WORKTREE_ROOT_SUBPATH}/`)) {
6083
- await createEngineHookAdapter(coerceVendorId(opts.vendor)).installTaskHooks({
6084
- worktreeDir: opts.cwd,
6085
- taskId: opts.taskId
6086
- });
6087
- }
6088
5983
  if (await sessionExists(opts.name)) {
6089
5984
  const sessionOptions = await getSessionOptions(opts.name, ["@kobe_worktree", "@kobe_vendor"]);
6090
5985
  const taggedWorktree = sessionOptions["@kobe_worktree"] ?? "";
@@ -6499,10 +6394,8 @@ async function quickCreate(session) {
6499
6394
  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
6395
  var init_tmux = __esm(() => {
6501
6396
  init_invocation();
6502
- init_hook_adapter2();
6503
6397
  init_interactive_command();
6504
6398
  init_env();
6505
- init_paths();
6506
6399
  init_client2();
6507
6400
  init_prompt_delivery();
6508
6401
  init_vendor();
@@ -6545,14 +6438,14 @@ var exports_repo_init = {};
6545
6438
  __export(exports_repo_init, {
6546
6439
  resolveRepoInit: () => resolveRepoInit
6547
6440
  });
6548
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
6549
- import { join as join5 } from "path";
6441
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
6442
+ import { join as join4 } from "path";
6550
6443
  function repoFileScript(worktreePath) {
6551
- return existsSync3(join5(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
6444
+ return existsSync2(join4(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
6552
6445
  }
6553
6446
  function repoFilePrompt(worktreePath) {
6554
- const p = join5(worktreePath, INIT_PROMPT_REL);
6555
- if (!existsSync3(p))
6447
+ const p = join4(worktreePath, INIT_PROMPT_REL);
6448
+ if (!existsSync2(p))
6556
6449
  return;
6557
6450
  try {
6558
6451
  const text = readFileSync3(p, "utf8");
@@ -6573,8 +6466,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
6573
6466
  var INIT_SCRIPT_REL, INIT_PROMPT_REL;
6574
6467
  var init_repo_init = __esm(() => {
6575
6468
  init_repos();
6576
- INIT_SCRIPT_REL = join5(".kobe", "init.sh");
6577
- INIT_PROMPT_REL = join5(".kobe", "init-prompt.md");
6469
+ INIT_SCRIPT_REL = join4(".kobe", "init.sh");
6470
+ INIT_PROMPT_REL = join4(".kobe", "init-prompt.md");
6578
6471
  });
6579
6472
 
6580
6473
  // src/tui/panes/sidebar/worktree-changes.ts
@@ -7586,9 +7479,9 @@ var init_schema = () => {};
7586
7479
 
7587
7480
  // src/tui/context/theme/loader.ts
7588
7481
  import { readFileSync as readFileSync4, readdirSync } from "fs";
7589
- import { join as join6 } from "path";
7482
+ import { join as join5 } from "path";
7590
7483
  function userThemesDir() {
7591
- return join6(kobeStateDir(), "themes");
7484
+ return join5(kobeStateDir(), "themes");
7592
7485
  }
7593
7486
  function loadUserThemes() {
7594
7487
  const dir = userThemesDir();
@@ -7602,7 +7495,7 @@ function loadUserThemes() {
7602
7495
  for (const file of entries) {
7603
7496
  if (!file.endsWith(".json"))
7604
7497
  continue;
7605
- const path6 = join6(dir, file);
7498
+ const path6 = join5(dir, file);
7606
7499
  let parsed;
7607
7500
  try {
7608
7501
  const text = readFileSync4(path6, "utf8");
@@ -7632,8 +7525,8 @@ var exports_theme = {};
7632
7525
  __export(exports_theme, {
7633
7526
  runThemeSubcommand: () => runThemeSubcommand
7634
7527
  });
7635
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
7636
- import { basename as basename3, join as join7, resolve as resolve5 } from "path";
7528
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
7529
+ import { basename as basename3, join as join6, resolve as resolve5 } from "path";
7637
7530
  function fail2(message) {
7638
7531
  process.stderr.write(`kobe theme: ${message}
7639
7532
  `);
@@ -7664,7 +7557,7 @@ function listThemes() {
7664
7557
  } else {
7665
7558
  for (const f of userFiles) {
7666
7559
  const name = f.slice(0, -".json".length);
7667
- const path6 = join7(dir, f);
7560
+ const path6 = join6(dir, f);
7668
7561
  const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
7669
7562
  lines.push(` ${name}${overridesBundled} ${path6}`);
7670
7563
  }
@@ -7754,8 +7647,8 @@ async function addTheme(args) {
7754
7647
  }
7755
7648
  const dir = userThemesDir();
7756
7649
  mkdirSync3(dir, { recursive: true });
7757
- const dest = join7(dir, `${name}.json`);
7758
- if (existsSync4(dest) && !opts.force) {
7650
+ const dest = join6(dir, `${name}.json`);
7651
+ if (existsSync3(dest) && !opts.force) {
7759
7652
  fail2(`${dest} already exists (pass --force to overwrite)`);
7760
7653
  }
7761
7654
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
@@ -7772,8 +7665,8 @@ function removeTheme(args) {
7772
7665
  if (BUNDLED_NAMES.includes(name)) {
7773
7666
  fail2(`"${name}" is a built-in theme and cannot be removed`);
7774
7667
  }
7775
- const dest = join7(userThemesDir(), `${name}.json`);
7776
- if (!existsSync4(dest)) {
7668
+ const dest = join6(userThemesDir(), `${name}.json`);
7669
+ if (!existsSync3(dest)) {
7777
7670
  fail2(`no user theme named "${name}" (looked for ${dest})`);
7778
7671
  }
7779
7672
  unlinkSync(dest);
@@ -7956,9 +7849,9 @@ var init_daemon_cmd = __esm(() => {
7956
7849
  });
7957
7850
 
7958
7851
  // src/lib/skill-install.ts
7959
- import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
7852
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
7960
7853
  import { homedir as homedir9 } from "os";
7961
- import { join as join8 } from "path";
7854
+ import { join as join7 } from "path";
7962
7855
  function npxSkillsArgv(opts = {}) {
7963
7856
  return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
7964
7857
  }
@@ -7968,14 +7861,14 @@ function npxSkillsCommand(opts = {}) {
7968
7861
  function kobeSkillPaths(opts = {}) {
7969
7862
  const home = opts.home ?? homedir9();
7970
7863
  const cwd = opts.cwd ?? process.cwd();
7971
- return [join8(home, SKILL_REL_PATH), join8(cwd, SKILL_REL_PATH)];
7864
+ return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
7972
7865
  }
7973
7866
  function parseSkillVersion(content) {
7974
7867
  const m = content.match(/kobe-skill-version:\s*(\d+)/);
7975
7868
  return m ? Number.parseInt(m[1], 10) : null;
7976
7869
  }
7977
7870
  function kobeSkillState(opts) {
7978
- const path6 = kobeSkillPaths(opts).find((p) => existsSync5(p));
7871
+ const path6 = kobeSkillPaths(opts).find((p) => existsSync4(p));
7979
7872
  if (!path6) {
7980
7873
  return { installed: false, installedVersion: null, currentVersion: KOBE_SKILL_VERSION, stale: false };
7981
7874
  }
@@ -8027,9 +7920,9 @@ __export(exports_maintenance, {
8027
7920
  runReloadSubcommand: () => runReloadSubcommand,
8028
7921
  runDoctorSubcommand: () => runDoctorSubcommand
8029
7922
  });
8030
- import { existsSync as existsSync6, readFileSync as readFileSync7, statSync } from "fs";
7923
+ import { existsSync as existsSync5, readFileSync as readFileSync7, statSync } from "fs";
8031
7924
  import { unlink as unlink6 } from "fs/promises";
8032
- import { join as join9 } from "path";
7925
+ import { join as join8 } from "path";
8033
7926
  import { createInterface } from "readline";
8034
7927
  function isProcessAlive2(pid) {
8035
7928
  try {
@@ -8126,7 +8019,7 @@ async function runDoctorSubcommand(argv = []) {
8126
8019
  const socketPath = defaultDaemonSocketPath();
8127
8020
  const pidPath = defaultDaemonPidPath();
8128
8021
  const logPath = defaultDaemonLogPath();
8129
- const tasksPath = join9(kobeStateDir(), "tasks.json");
8022
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
8130
8023
  const statePath2 = kvStatePath();
8131
8024
  const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
8132
8025
  const status = await probeDaemonStatus(socketPath);
@@ -8154,7 +8047,7 @@ async function runDoctorSubcommand(argv = []) {
8154
8047
  } else {
8155
8048
  out.push("daemon: \u2717 not running (no pidfile)");
8156
8049
  }
8157
- if (existsSync6(socketPath))
8050
+ if (existsSync5(socketPath))
8158
8051
  out.push(` orphan socket file present: ${socketPath}`);
8159
8052
  const tail = tailFile(logPath, 8);
8160
8053
  if (tail) {
@@ -8245,7 +8138,7 @@ async function runResetSubcommand(argv) {
8245
8138
  const yes = argv.includes("--yes") || argv.includes("-y");
8246
8139
  const socketPath = defaultDaemonSocketPath();
8247
8140
  const pidPath = defaultDaemonPidPath();
8248
- const tasksPath = join9(kobeStateDir(), "tasks.json");
8141
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
8249
8142
  const statePath2 = kvStatePath();
8250
8143
  console.log("kobe reset will:");
8251
8144
  console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
@@ -8449,14 +8342,148 @@ var init_skill_cmd = __esm(() => {
8449
8342
  SKILL_VERBS = ["install", "status", "command"];
8450
8343
  });
8451
8344
 
8345
+ // src/engine/claude-code-local/hook-adapter.ts
8346
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
8347
+ import { dirname as dirname6 } from "path";
8348
+ function isObject6(v) {
8349
+ return !!v && typeof v === "object" && !Array.isArray(v);
8350
+ }
8351
+ async function readJsonObject(path6) {
8352
+ try {
8353
+ const parsed = JSON.parse(await readFile6(path6, "utf8"));
8354
+ return isObject6(parsed) ? parsed : {};
8355
+ } catch {
8356
+ return {};
8357
+ }
8358
+ }
8359
+ function isKobeWorktreeSyncGroup(group) {
8360
+ if (!isObject6(group) || !Array.isArray(group.hooks))
8361
+ return false;
8362
+ return group.hooks.some((h) => isObject6(h) && typeof h.command === "string" && h.command.includes(WORKTREE_SYNC_MARKER));
8363
+ }
8364
+ function isKobeActivityGroup(group) {
8365
+ if (!isObject6(group) || !Array.isArray(group.hooks))
8366
+ return false;
8367
+ return group.hooks.some((h) => isObject6(h) && typeof h.command === "string" && ACTIVITY_MARKERS.some((m) => h.command.includes(m)));
8368
+ }
8369
+ function mergeWorktreeSyncHook(current, command) {
8370
+ const { hooks: rawHooks, ...restSettings } = current;
8371
+ const { WorktreeCreate, ...otherHooks } = isObject6(rawHooks) ? rawHooks : {};
8372
+ const prior = Array.isArray(WorktreeCreate) ? WorktreeCreate : [];
8373
+ const kept = prior.filter((g) => !isKobeWorktreeSyncGroup(g));
8374
+ if (command !== null)
8375
+ kept.push({ hooks: [{ type: "command", command }] });
8376
+ const nextHooks = { ...otherHooks };
8377
+ if (kept.length > 0)
8378
+ nextHooks.WorktreeCreate = kept;
8379
+ return Object.keys(nextHooks).length > 0 ? { ...restSettings, hooks: nextHooks } : { ...restSettings };
8380
+ }
8381
+ function buildClaudeHooks(inv = kobeCliInvocation()) {
8382
+ const out = {};
8383
+ for (const { event, matcher, verb } of EVENT_MAP) {
8384
+ const command = shellQuoteArgv([...inv, "hook", verb]);
8385
+ const group = { hooks: [{ type: "command", command }] };
8386
+ if (matcher)
8387
+ group.matcher = matcher;
8388
+ out[event] = [group];
8389
+ }
8390
+ return out;
8391
+ }
8392
+ function mergeActivityHooks(current, install, inv = kobeCliInvocation()) {
8393
+ const { hooks: rawHooks, ...restSettings } = current;
8394
+ const hooks = isObject6(rawHooks) ? { ...rawHooks } : {};
8395
+ const built = install ? buildClaudeHooks(inv) : {};
8396
+ for (const { event } of EVENT_MAP) {
8397
+ const prior = Array.isArray(hooks[event]) ? hooks[event] : [];
8398
+ const kept = prior.filter((g) => !isKobeActivityGroup(g));
8399
+ if (install && Array.isArray(built[event]))
8400
+ kept.push(...built[event]);
8401
+ if (kept.length > 0)
8402
+ hooks[event] = kept;
8403
+ else
8404
+ delete hooks[event];
8405
+ }
8406
+ return Object.keys(hooks).length > 0 ? { ...restSettings, hooks } : { ...restSettings };
8407
+ }
8408
+
8409
+ class ClaudeHookAdapter {
8410
+ vendor = "claude";
8411
+ supportsHooks() {
8412
+ return true;
8413
+ }
8414
+ supportsWorktreeSync() {
8415
+ return true;
8416
+ }
8417
+ async installActivityHooks(settingsFilePath) {
8418
+ await this.editSettings(settingsFilePath, (cur) => mergeActivityHooks(cur, true));
8419
+ }
8420
+ async removeActivityHooks(settingsFilePath) {
8421
+ await this.editSettings(settingsFilePath, (cur) => mergeActivityHooks(cur, false));
8422
+ }
8423
+ async removeWorktreeSyncHook(settingsFilePath) {
8424
+ await this.editSettings(settingsFilePath, (cur) => mergeWorktreeSyncHook(cur, null));
8425
+ }
8426
+ async editSettings(settingsFilePath, transform) {
8427
+ try {
8428
+ const current = await readJsonObject(settingsFilePath);
8429
+ const next = transform(current);
8430
+ if (JSON.stringify(next) === JSON.stringify(current))
8431
+ return;
8432
+ await mkdir5(dirname6(settingsFilePath), { recursive: true });
8433
+ await writeFile4(settingsFilePath, `${JSON.stringify(next, null, 2)}
8434
+ `);
8435
+ } catch {}
8436
+ }
8437
+ }
8438
+ var EVENT_MAP, KOBE_HOOK_EVENTS, ACTIVITY_MARKERS, WORKTREE_SYNC_MARKER = "worktree-created";
8439
+ var init_hook_adapter = __esm(() => {
8440
+ init_invocation();
8441
+ EVENT_MAP = [
8442
+ { event: "SessionStart", verb: "session-start" },
8443
+ { event: "UserPromptSubmit", verb: "turn-start" },
8444
+ { event: "Stop", verb: "turn-complete" },
8445
+ { event: "StopFailure", verb: "turn-failed" },
8446
+ { event: "Notification", matcher: "permission_prompt", verb: "awaiting-input" },
8447
+ { event: "SessionEnd", verb: "session-end" }
8448
+ ];
8449
+ KOBE_HOOK_EVENTS = EVENT_MAP.map((e) => e.event);
8450
+ ACTIVITY_MARKERS = EVENT_MAP.map((e) => shellQuoteArgv(["hook", e.verb]));
8451
+ });
8452
+
8453
+ // src/engine/hook-adapter.ts
8454
+ function createEngineHookAdapter(vendor) {
8455
+ if (vendor === "claude")
8456
+ return new ClaudeHookAdapter;
8457
+ return new NoopHookAdapter(vendor);
8458
+ }
8459
+
8460
+ class NoopHookAdapter {
8461
+ vendor;
8462
+ constructor(vendor) {
8463
+ this.vendor = vendor;
8464
+ }
8465
+ supportsHooks() {
8466
+ return false;
8467
+ }
8468
+ async installActivityHooks() {}
8469
+ async removeActivityHooks() {}
8470
+ supportsWorktreeSync() {
8471
+ return false;
8472
+ }
8473
+ async removeWorktreeSyncHook() {}
8474
+ }
8475
+ var init_hook_adapter2 = __esm(() => {
8476
+ init_hook_adapter();
8477
+ });
8478
+
8452
8479
  // src/cli/hook-cmd.ts
8453
8480
  var exports_hook_cmd = {};
8454
8481
  __export(exports_hook_cmd, {
8455
8482
  runHookSubcommand: () => runHookSubcommand,
8456
- ensureDefaultWorktreeSync: () => ensureDefaultWorktreeSync
8483
+ ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
8457
8484
  });
8458
8485
  import { homedir as homedir10 } from "os";
8459
- import { dirname as dirname7, join as join10, resolve as resolve6 } from "path";
8486
+ import { join as join9, resolve as resolve6 } from "path";
8460
8487
  async function readStdinPayload() {
8461
8488
  try {
8462
8489
  const text = await Promise.race([
@@ -8496,16 +8523,11 @@ async function runHookSubcommand(argv) {
8496
8523
  return;
8497
8524
  }
8498
8525
  try {
8499
- if (verb === "worktree-created") {
8500
- await reportWorktreeCreated();
8501
- return;
8502
- }
8503
8526
  if (!verb || !isEngineActivityKind(verb))
8504
8527
  return;
8505
- const taskId = flagValue(rest, "--task-id");
8506
- if (!taskId)
8507
- return;
8508
8528
  const payload = await readStdinPayload();
8529
+ const taskId = flagValue(rest, "--task-id");
8530
+ const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
8509
8531
  let detail;
8510
8532
  if (verb === "turn-failed") {
8511
8533
  detail = { failure: failureFromErrorType(payload.error_type) };
@@ -8516,126 +8538,69 @@ async function runHookSubcommand(argv) {
8516
8538
  if (!client)
8517
8539
  return;
8518
8540
  try {
8519
- await client.request("engine.reportEvent", { taskId, kind: verb, ...detail ? { detail } : {} });
8541
+ await client.request("engine.reportEvent", {
8542
+ ...taskId ? { taskId } : { cwd },
8543
+ kind: verb,
8544
+ ...detail ? { detail } : {}
8545
+ });
8520
8546
  } finally {
8521
8547
  client.close();
8522
8548
  }
8523
8549
  } catch {}
8524
8550
  }
8525
- async function reportWorktreeCreated() {
8526
- const payload = await readStdinPayload();
8527
- const worktreePath = typeof payload.worktree_path === "string" ? payload.worktree_path : undefined;
8528
- if (!worktreePath)
8529
- return;
8530
- const repo = await deriveRepoRoot(worktreePath);
8531
- if (!repo)
8532
- return;
8533
- const client = await connectIfRunning();
8534
- if (!client)
8535
- return;
8536
- try {
8537
- await client.request("worktree.adopt", { repo, worktreePath, ifExists: "return" });
8538
- } finally {
8539
- client.close();
8540
- }
8541
- }
8542
- async function deriveRepoRoot(worktreePath) {
8543
- try {
8544
- const proc = Bun.spawn(["git", "-C", worktreePath, "rev-parse", "--path-format=absolute", "--git-common-dir"], {
8545
- stdout: "pipe",
8546
- stderr: "ignore"
8547
- });
8548
- const [out, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
8549
- if (code !== 0)
8550
- return;
8551
- const commonDir = out.trim();
8552
- return commonDir ? dirname7(commonDir) : undefined;
8553
- } catch {
8554
- return;
8555
- }
8556
- }
8557
- function syncSettingsPath(scope) {
8558
- if (scope.kind === "repo")
8559
- return join10(resolve6(scope.path), ".claude", "settings.json");
8560
- return join10(homedir10(), ".claude", "settings.json");
8561
- }
8562
8551
  function worktreeSyncAdapters() {
8563
8552
  return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsWorktreeSync());
8564
8553
  }
8554
+ function activityHookAdapters() {
8555
+ return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
8556
+ }
8557
+ function globalSettingsPath() {
8558
+ return join9(homedir10(), ".claude", "settings.json");
8559
+ }
8565
8560
  function persistedSyncPath(stored) {
8566
8561
  if (!stored || stored === "off")
8567
8562
  return;
8568
8563
  if (stored === "global")
8569
- return syncSettingsPath({ kind: "global" });
8564
+ return globalSettingsPath();
8570
8565
  if (stored.startsWith("repo:"))
8571
- return syncSettingsPath({ kind: "repo", path: stored.slice(5) });
8566
+ return join9(resolve6(stored.slice(5)), ".claude", "settings.json");
8572
8567
  return stored;
8573
8568
  }
8574
- async function ensureDefaultWorktreeSync() {
8569
+ async function ensureGlobalKobeHooks() {
8575
8570
  try {
8576
- const stored = getPersistedString(SYNC_SETTING_KEY);
8577
- if (stored === "off")
8578
- return;
8579
- const adapters = worktreeSyncAdapters();
8580
- if (adapters.length === 0)
8581
- return;
8582
- const path6 = persistedSyncPath(stored) ?? syncSettingsPath({ kind: "global" });
8583
- for (const a of adapters)
8584
- await a.installWorktreeSyncHook(path6);
8585
- if (!stored)
8586
- setPersistedString(SYNC_SETTING_KEY, path6);
8571
+ const globalPath = globalSettingsPath();
8572
+ for (const a of activityHookAdapters())
8573
+ await a.installActivityHooks(globalPath);
8574
+ await cleanupWorktreeSyncHook();
8587
8575
  } catch {}
8588
8576
  }
8589
- async function runHookSetup(argv) {
8590
- if (argv.includes("--help") || argv.includes("-h")) {
8591
- process.stdout.write([
8592
- "Usage: kobe hook setup [--global | --repo <path> | --off]",
8593
- "",
8594
- "Syncs an external `claude --worktree` into kobe as a task. This is ON",
8595
- "by default (installed globally into ~/.claude on launch). Use this to",
8596
- "move it to one repo (--repo <path>) or to turn it OFF (--off).",
8597
- ""
8598
- ].join(`
8599
- `));
8600
- return;
8601
- }
8602
- const off = argv.includes("--off");
8603
- const repoIdx = argv.indexOf("--repo");
8604
- const repoPath = repoIdx !== -1 ? argv[repoIdx + 1] : undefined;
8605
- if (repoIdx !== -1 && !repoPath) {
8606
- process.stderr.write(`kobe hook setup: --repo requires a path
8607
- `);
8608
- process.exit(2);
8609
- }
8577
+ async function cleanupWorktreeSyncHook() {
8610
8578
  const adapters = worktreeSyncAdapters();
8611
- if (adapters.length === 0) {
8612
- process.stdout.write(`kobe hook setup: no engine supports external worktree sync \u2014 nothing to do
8613
- `);
8579
+ if (adapters.length === 0)
8614
8580
  return;
8615
- }
8616
- const prevPath = persistedSyncPath(getPersistedString(SYNC_SETTING_KEY));
8617
- if (off) {
8618
- if (prevPath) {
8619
- for (const a of adapters)
8620
- await a.removeWorktreeSyncHook(prevPath);
8621
- }
8622
- setPersistedString(SYNC_SETTING_KEY, "off");
8623
- process.stdout.write(`kobe hook setup: external worktree sync disabled${prevPath ? ` (removed from ${prevPath})` : ""}
8624
- `);
8625
- return;
8626
- }
8627
- const scope = repoPath ? { kind: "repo", path: repoPath } : { kind: "global" };
8628
- const path6 = syncSettingsPath(scope);
8629
- if (prevPath && prevPath !== path6) {
8630
- for (const a of adapters)
8631
- await a.removeWorktreeSyncHook(prevPath);
8632
- }
8581
+ const stored = getPersistedString(SYNC_SETTING_KEY);
8582
+ const paths = new Set([globalSettingsPath()]);
8583
+ const prev = persistedSyncPath(stored);
8584
+ if (prev)
8585
+ paths.add(prev);
8633
8586
  for (const a of adapters)
8634
- await a.installWorktreeSyncHook(path6);
8635
- setPersistedString(SYNC_SETTING_KEY, path6);
8587
+ for (const p of paths)
8588
+ await a.removeWorktreeSyncHook(p);
8589
+ if (stored !== "off")
8590
+ setPersistedString(SYNC_SETTING_KEY, "off");
8591
+ }
8592
+ async function runHookSetup(_argv) {
8593
+ await cleanupWorktreeSyncHook();
8636
8594
  process.stdout.write([
8637
- `kobe hook setup: external worktree sync enabled (${scope.kind}) \u2014 wrote ${path6}`,
8638
- "New `claude --worktree` worktrees will appear as kobe tasks.",
8595
+ "kobe hook setup is deprecated and now a no-op (cleanup only).",
8596
+ "",
8597
+ "The old external-worktree sync used a global WorktreeCreate hook, which is",
8598
+ "a VCS provider hook \u2014 its presence broke `claude --worktree` / EnterWorktree",
8599
+ "in every repo. Any hook kobe previously installed has been removed.",
8600
+ "",
8601
+ "Sync is now automatic: a `claude --worktree` (or any session) started in a",
8602
+ "worktree under a repo kobe already tracks is adopted as a task on launch.",
8603
+ "To adopt existing worktrees on demand, use the New Task dialog or `kobe adopt`.",
8639
8604
  ""
8640
8605
  ].join(`
8641
8606
  `));
@@ -13854,7 +13819,7 @@ var init_rename_task_dialog = __esm(() => {
13854
13819
 
13855
13820
  // src/engine/claude-code-local/binary.ts
13856
13821
  import { spawnSync as spawnSync5 } from "child_process";
13857
- import { existsSync as existsSync8, statSync as statSync3 } from "fs";
13822
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
13858
13823
  import { homedir as homedir12 } from "os";
13859
13824
  import path7 from "path";
13860
13825
  async function findClaudeBinary(deps = defaultDeps4) {
@@ -13941,7 +13906,7 @@ var init_binary = __esm(() => {
13941
13906
  return;
13942
13907
  if (first.startsWith("claude:") && first.includes("aliased to")) {
13943
13908
  const aliasTarget = first.split("aliased to")[1]?.trim();
13944
- return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
13909
+ return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
13945
13910
  }
13946
13911
  return first;
13947
13912
  },
@@ -13958,7 +13923,7 @@ var init_binary = __esm(() => {
13958
13923
 
13959
13924
  // src/engine/codex-local/binary.ts
13960
13925
  import { spawnSync as spawnSync6 } from "child_process";
13961
- import { existsSync as existsSync9, statSync as statSync4 } from "fs";
13926
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
13962
13927
  import { homedir as homedir13 } from "os";
13963
13928
  import path8 from "path";
13964
13929
  async function findCodexBinary(deps = defaultDeps5) {
@@ -14029,7 +13994,7 @@ var init_binary2 = __esm(() => {
14029
13994
  return;
14030
13995
  if (first.startsWith("codex:") && first.includes("aliased to")) {
14031
13996
  const aliasTarget = first.split("aliased to")[1]?.trim();
14032
- return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
13997
+ return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
14033
13998
  }
14034
13999
  return first;
14035
14000
  },
@@ -14046,7 +14011,7 @@ var init_binary2 = __esm(() => {
14046
14011
 
14047
14012
  // src/engine/copilot-local/binary.ts
14048
14013
  import { spawnSync as spawnSync7 } from "child_process";
14049
- import { existsSync as existsSync10, statSync as statSync5 } from "fs";
14014
+ import { existsSync as existsSync9, statSync as statSync5 } from "fs";
14050
14015
  import { homedir as homedir14 } from "os";
14051
14016
  import path9 from "path";
14052
14017
  async function findCopilotBinary(deps = defaultDeps6) {
@@ -14141,7 +14106,7 @@ var init_binary3 = __esm(() => {
14141
14106
  return;
14142
14107
  if (first.startsWith("copilot:") && first.includes("aliased to")) {
14143
14108
  const aliasTarget = first.split("aliased to")[1]?.trim();
14144
- return aliasTarget && existsSync10(aliasTarget) ? aliasTarget : undefined;
14109
+ return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
14145
14110
  }
14146
14111
  return first;
14147
14112
  },
@@ -14530,7 +14495,7 @@ var init_dialog_confirm = __esm(() => {
14530
14495
 
14531
14496
  // src/tui/component/settings-dialog/actions.ts
14532
14497
  import { unlinkSync as unlinkSync2 } from "fs";
14533
- import { join as join12 } from "path";
14498
+ import { join as join11 } from "path";
14534
14499
  function hasRestartableDaemon(orchestrator) {
14535
14500
  return orchestrator instanceof RemoteOrchestrator;
14536
14501
  }
@@ -14547,7 +14512,7 @@ async function confirmResetState(dialog, kv, renderer) {
14547
14512
  return;
14548
14513
  kv.clear();
14549
14514
  try {
14550
- unlinkSync2(join12(homeDir(), ".kobe", "tasks.json"));
14515
+ unlinkSync2(join11(homeDir(), ".kobe", "tasks.json"));
14551
14516
  } catch (err) {
14552
14517
  if (err.code !== "ENOENT") {
14553
14518
  console.error("kobe: failed to delete tasks.json during reset:", err);
@@ -15984,7 +15949,7 @@ var init_focus = __esm(() => {
15984
15949
 
15985
15950
  // src/tui/context/kv.tsx
15986
15951
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
15987
- import { dirname as dirname8 } from "path";
15952
+ import { dirname as dirname7 } from "path";
15988
15953
  function loadInitial() {
15989
15954
  const statePath2 = kvStatePath();
15990
15955
  try {
@@ -16012,7 +15977,7 @@ var init_kv = __esm(() => {
16012
15977
  function writeNow(label) {
16013
15978
  const statePath2 = kvStatePath();
16014
15979
  try {
16015
- mkdirSync4(dirname8(statePath2), {
15980
+ mkdirSync4(dirname7(statePath2), {
16016
15981
  recursive: true
16017
15982
  });
16018
15983
  const tmp = `${statePath2}.tmp`;
@@ -16067,7 +16032,7 @@ var init_kv = __esm(() => {
16067
16032
  }
16068
16033
  const statePath2 = kvStatePath();
16069
16034
  try {
16070
- mkdirSync4(dirname8(statePath2), {
16035
+ mkdirSync4(dirname7(statePath2), {
16071
16036
  recursive: true
16072
16037
  });
16073
16038
  const tmp = `${statePath2}.tmp`;
@@ -16103,8 +16068,8 @@ var init_persisted_ui_prefs = __esm(() => {
16103
16068
 
16104
16069
  // src/tui/lib/worktree-opener.ts
16105
16070
  import { spawn as spawn3 } from "child_process";
16106
- import { existsSync as existsSync11 } from "fs";
16107
- import { basename as basename4, delimiter, isAbsolute, join as join13 } from "path";
16071
+ import { existsSync as existsSync10 } from "fs";
16072
+ import { basename as basename4, delimiter, isAbsolute, join as join12 } from "path";
16108
16073
  function executableOnPath(command, env, exists) {
16109
16074
  if (isAbsolute(command))
16110
16075
  return exists(command);
@@ -16112,7 +16077,7 @@ function executableOnPath(command, env, exists) {
16112
16077
  for (const dir of pathEnv.split(delimiter)) {
16113
16078
  if (!dir)
16114
16079
  continue;
16115
- if (exists(join13(dir, command)))
16080
+ if (exists(join12(dir, command)))
16116
16081
  return true;
16117
16082
  }
16118
16083
  return false;
@@ -16132,7 +16097,7 @@ function labelForOverride(command) {
16132
16097
  function detectWorktreeOpener(deps = {}) {
16133
16098
  const env = deps.env ?? process.env;
16134
16099
  const platform = deps.platform ?? process.platform;
16135
- const exists = deps.exists ?? existsSync11;
16100
+ const exists = deps.exists ?? existsSync10;
16136
16101
  const override = env.KOBE_OPEN_EDITOR?.trim();
16137
16102
  if (override) {
16138
16103
  return { id: "env", label: labelForOverride(override), command: override, args: [] };
@@ -17950,7 +17915,7 @@ var exports_host = {};
17950
17915
  __export(exports_host, {
17951
17916
  startTasksPane: () => startTasksPane
17952
17917
  });
17953
- import { existsSync as existsSync12 } from "fs";
17918
+ import { existsSync as existsSync11 } from "fs";
17954
17919
  import { TextAttributes as TextAttributes10 } from "@opentui/core";
17955
17920
  function TasksShell(props) {
17956
17921
  const themeCtx = useTheme();
@@ -18185,7 +18150,7 @@ function TasksShell(props) {
18185
18150
  async function openSelectedWorktree(id) {
18186
18151
  const task = props.tasks().find((t) => t.id === id);
18187
18152
  let worktree = task?.worktreePath;
18188
- if (!worktree || !existsSync12(worktree)) {
18153
+ if (!worktree || !existsSync11(worktree)) {
18189
18154
  if (!props.orch) {
18190
18155
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
18191
18156
  return;
@@ -18198,7 +18163,7 @@ function TasksShell(props) {
18198
18163
  }
18199
18164
  await props.reload();
18200
18165
  }
18201
- if (!worktree || !existsSync12(worktree))
18166
+ if (!worktree || !existsSync11(worktree))
18202
18167
  return;
18203
18168
  const opener = detectWorktreeOpener();
18204
18169
  if (!opener) {
@@ -18249,7 +18214,7 @@ function TasksShell(props) {
18249
18214
  const exists = await sessionExists(name);
18250
18215
  if (exists) {
18251
18216
  const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
18252
- if (cwd2 && existsSync12(cwd2)) {
18217
+ if (cwd2 && existsSync11(cwd2)) {
18253
18218
  await ensureSession({
18254
18219
  name,
18255
18220
  cwd: cwd2,
@@ -18263,7 +18228,7 @@ function TasksShell(props) {
18263
18228
  return;
18264
18229
  }
18265
18230
  let cwd = task?.worktreePath;
18266
- if (!cwd || !existsSync12(cwd)) {
18231
+ if (!cwd || !existsSync11(cwd)) {
18267
18232
  if (!props.orch) {
18268
18233
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
18269
18234
  return;
@@ -18276,7 +18241,7 @@ function TasksShell(props) {
18276
18241
  }
18277
18242
  await props.reload();
18278
18243
  }
18279
- if (!cwd || !existsSync12(cwd))
18244
+ if (!cwd || !existsSync11(cwd))
18280
18245
  return;
18281
18246
  const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
18282
18247
  const ready = await ensureSession({
@@ -19727,14 +19692,14 @@ var init_keys2 = __esm(() => {
19727
19692
 
19728
19693
  // src/tui/panes/filetree/open-external.ts
19729
19694
  import { spawn as spawn5 } from "child_process";
19730
- import { existsSync as existsSync13 } from "fs";
19695
+ import { existsSync as existsSync12 } from "fs";
19731
19696
  import { platform } from "os";
19732
19697
  function openExternally(absPath) {
19733
19698
  if (!absPath)
19734
19699
  return;
19735
19700
  const plat = platform();
19736
19701
  if (plat === "linux") {
19737
- if (existsSync13("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
19702
+ if (existsSync12("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
19738
19703
  spawnDetached("wslview", [absPath], () => {
19739
19704
  const child = spawn5("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
19740
19705
  let out = "";
@@ -21175,7 +21140,7 @@ async function ensureRepos(orchestrator) {
21175
21140
  }
21176
21141
  async function startDirectTmux() {
21177
21142
  setClientLogContext("gui");
21178
- Promise.resolve().then(() => (init_hook_cmd(), exports_hook_cmd)).then((m) => m.ensureDefaultWorktreeSync());
21143
+ Promise.resolve().then(() => (init_hook_cmd(), exports_hook_cmd)).then((m) => m.ensureGlobalKobeHooks());
21179
21144
  if (!await tmuxAvailable()) {
21180
21145
  console.error("kobe: tmux not found on PATH \u2014 install tmux to use kobe 0.6 direct mode");
21181
21146
  process.exitCode = 1;
@@ -21769,9 +21734,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
21769
21734
  var init_pulse = () => {};
21770
21735
 
21771
21736
  // src/tui/lib/sound.ts
21772
- import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "fs";
21737
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
21773
21738
  import { tmpdir as tmpdir2 } from "os";
21774
- import { basename as basename6, isAbsolute as isAbsolute2, join as join14, resolve as resolve8 } from "path";
21739
+ import { basename as basename6, isAbsolute as isAbsolute2, join as join13, resolve as resolve8 } from "path";
21775
21740
  function args(player, file, volume) {
21776
21741
  if (player === "ffplay")
21777
21742
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -21794,13 +21759,13 @@ function pickPlayer() {
21794
21759
  return cachedPlayer;
21795
21760
  const path12 = process.env.PATH ?? "";
21796
21761
  const segments = path12.split(":").filter(Boolean);
21797
- cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join14(dir, p)))) ?? null;
21762
+ cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync13(join13(dir, p)))) ?? null;
21798
21763
  return cachedPlayer;
21799
21764
  }
21800
21765
  async function ensureAsset() {
21801
21766
  cachedPath ??= (async () => {
21802
21767
  mkdirSync5(DIR, { recursive: true });
21803
- const dest = join14(DIR, basename6(pulseAsset));
21768
+ const dest = join13(DIR, basename6(pulseAsset));
21804
21769
  const out = Bun.file(dest);
21805
21770
  if (await out.exists())
21806
21771
  return dest;
@@ -21830,7 +21795,7 @@ var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
21830
21795
  var init_sound = __esm(() => {
21831
21796
  init_pulse();
21832
21797
  pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
21833
- DIR = join14(tmpdir2(), "kobe-sfx");
21798
+ DIR = join13(tmpdir2(), "kobe-sfx");
21834
21799
  PLAYERS = [
21835
21800
  "ffplay",
21836
21801
  "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.8",
4
+ "version": "0.7.10",
5
5
  "description": "TUI orchestrator for Claude Code (codename)",
6
6
  "type": "module",
7
7
  "packageManager": "bun@1.3.13",