@sma1lboy/kobe 0.6.6 → 0.6.8

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 +1330 -494
  2. package/package.json +6 -2
package/dist/cli/index.js CHANGED
@@ -64,7 +64,75 @@ var init_vendor = __esm(() => {
64
64
  ALL_VENDORS = ["claude", "codex", "copilot"];
65
65
  });
66
66
 
67
+ // package.json
68
+ var package_default;
69
+ var init_package = __esm(() => {
70
+ package_default = {
71
+ $schema: "https://json.schemastore.org/package.json",
72
+ name: "@sma1lboy/kobe",
73
+ version: "0.6.8",
74
+ description: "TUI orchestrator for Claude Code (codename)",
75
+ type: "module",
76
+ packageManager: "bun@1.3.13",
77
+ bin: {
78
+ kobe: "dist/cli/index.js"
79
+ },
80
+ files: [
81
+ "dist",
82
+ "README.md",
83
+ "LICENSE"
84
+ ],
85
+ publishConfig: {
86
+ access: "public"
87
+ },
88
+ repository: {
89
+ type: "git",
90
+ url: "git+https://github.com/Sma1lboy/kobe.git"
91
+ },
92
+ homepage: "https://github.com/Sma1lboy/kobe#readme",
93
+ bugs: {
94
+ url: "https://github.com/Sma1lboy/kobe/issues"
95
+ },
96
+ engines: {
97
+ bun: ">=1.3.11"
98
+ },
99
+ scripts: {
100
+ dev: "env KOBE_DEV=1 bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
101
+ "dev:sandbox": "mkdir -p .dev-sandbox/home && env KOBE_DEV=1 KOBE_HOME_DIR=$PWD/.dev-sandbox/home KOBE_TMUX_SOCKET=kobe-sandbox bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
102
+ "dev:sandbox:reset": "env KOBE_TMUX_SOCKET=kobe-sandbox bun ./src/cli/index.ts kill-sessions",
103
+ build: "bun run scripts/build.ts",
104
+ compile: "bun run scripts/compile.ts",
105
+ typecheck: "tsc --noEmit",
106
+ test: "bun run test:fast && bun run test:socket",
107
+ "test:fast": "vitest run --passWithNoTests",
108
+ "test:socket": "KOBE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=1 --passWithNoTests",
109
+ lint: "biome check .",
110
+ knip: "knip-bun",
111
+ postinstall: "bun run scripts/check-preview-deps.ts || true",
112
+ prepublishOnly: "bun run typecheck && bun run build"
113
+ },
114
+ "//": "biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",
115
+ dependencies: {
116
+ "@ansi-tools/parser": "^1.0.15",
117
+ "@opentui/core": "0.2.4",
118
+ "@opentui/solid": "0.2.4",
119
+ "solid-js": "1.9.12"
120
+ },
121
+ devDependencies: {
122
+ "@biomejs/biome": "1.9.4",
123
+ "@tsconfig/bun": "1.0.9",
124
+ "@types/bun": "1.3.12",
125
+ knip: "^6.14.2",
126
+ "node-pty": "^1.1.0",
127
+ typescript: "5.8.2",
128
+ vitest: "2.1.9"
129
+ },
130
+ trustedDependencies: []
131
+ };
132
+ });
133
+
67
134
  // src/env.ts
135
+ import { createHash } from "crypto";
68
136
  import { homedir } from "os";
69
137
  import { join } from "path";
70
138
  function isDev() {
@@ -79,17 +147,178 @@ function kobeStateDir() {
79
147
  function kvStatePath() {
80
148
  return join(homeDir(), ".config", "kobe", "state.json");
81
149
  }
150
+ function worktreeInitMarkerPath(worktreePath) {
151
+ const hash = createHash("sha1").update(worktreePath).digest("hex").slice(0, 16);
152
+ return join(kobeStateDir(), "worktree-init", hash);
153
+ }
82
154
  var init_env = () => {};
83
155
 
156
+ // src/version.ts
157
+ var exports_version = {};
158
+ __export(exports_version, {
159
+ repoSlug: () => repoSlug,
160
+ releasePageUrl: () => releasePageUrl,
161
+ recommendedGlobalInstallCommand: () => recommendedGlobalInstallCommand,
162
+ isNewerSemver: () => isNewerSemver,
163
+ fetchReleaseSummaries: () => fetchReleaseSummaries,
164
+ fetchReleaseNotes: () => fetchReleaseNotes,
165
+ checkLatestVersion: () => checkLatestVersion,
166
+ UPDATE_SCRIPT_URL: () => UPDATE_SCRIPT_URL,
167
+ UPDATE_COMMAND: () => UPDATE_COMMAND,
168
+ PACKAGE_NAME: () => PACKAGE_NAME,
169
+ CURRENT_VERSION: () => CURRENT_VERSION
170
+ });
171
+ function repoSlug() {
172
+ const url = package_default.repository?.url;
173
+ if (!url)
174
+ return null;
175
+ const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
176
+ if (!m || !m[1] || !m[2])
177
+ return null;
178
+ return `${m[1]}/${m[2]}`;
179
+ }
180
+ function recommendedGlobalInstallCommand() {
181
+ return `npm install -g ${PACKAGE_NAME}@latest`;
182
+ }
183
+ async function fetchLatestFromRegistry(packageName) {
184
+ const ctrl = new AbortController;
185
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
186
+ try {
187
+ const encoded = packageName.replace("/", "%2F");
188
+ const res = await fetch(`https://registry.npmjs.org/${encoded}/latest`, {
189
+ signal: ctrl.signal,
190
+ headers: { accept: "application/json" }
191
+ });
192
+ if (!res.ok)
193
+ return null;
194
+ const body = await res.json();
195
+ if (typeof body.version !== "string")
196
+ return null;
197
+ return body.version;
198
+ } catch {
199
+ return null;
200
+ } finally {
201
+ clearTimeout(timer);
202
+ }
203
+ }
204
+ function isNewerSemver(latest, current) {
205
+ const norm = (v) => v.split("-")[0] ?? v;
206
+ const a = norm(latest).split(".").map((s) => Number.parseInt(s, 10));
207
+ const b = norm(current).split(".").map((s) => Number.parseInt(s, 10));
208
+ for (let i = 0;i < 3; i++) {
209
+ const av = a[i] ?? 0;
210
+ const bv = b[i] ?? 0;
211
+ if (Number.isNaN(av) || Number.isNaN(bv))
212
+ return false;
213
+ if (av > bv)
214
+ return true;
215
+ if (av < bv)
216
+ return false;
217
+ }
218
+ return false;
219
+ }
220
+ async function checkLatestVersion(opts = {}) {
221
+ if (isDev() && !opts.force)
222
+ return null;
223
+ const latest = await fetchLatestFromRegistry(PACKAGE_NAME);
224
+ if (!latest)
225
+ return null;
226
+ return {
227
+ current: CURRENT_VERSION,
228
+ latest,
229
+ hasUpdate: isNewerSemver(latest, CURRENT_VERSION)
230
+ };
231
+ }
232
+ function versionFromTagName(tagName) {
233
+ if (typeof tagName !== "string")
234
+ return null;
235
+ const match = tagName.match(/^v(\d+\.\d+\.\d+)$/);
236
+ return match?.[1] ?? null;
237
+ }
238
+ async function fetchReleaseNotes(version) {
239
+ const slug = repoSlug();
240
+ if (!slug)
241
+ return null;
242
+ const tag = `v${version}`;
243
+ const ctrl = new AbortController;
244
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
245
+ try {
246
+ const res = await fetch(`https://api.github.com/repos/${slug}/releases/tags/${tag}`, {
247
+ signal: ctrl.signal,
248
+ headers: {
249
+ accept: "application/vnd.github+json",
250
+ "x-github-api-version": "2022-11-28"
251
+ }
252
+ });
253
+ if (!res.ok)
254
+ return null;
255
+ const body = await res.json();
256
+ if (typeof body.body !== "string" || typeof body.html_url !== "string")
257
+ return null;
258
+ return { body: body.body, url: body.html_url, version };
259
+ } catch {
260
+ return null;
261
+ } finally {
262
+ clearTimeout(timer);
263
+ }
264
+ }
265
+ async function fetchReleaseSummaries(limit = 12) {
266
+ const slug = repoSlug();
267
+ if (!slug)
268
+ return [];
269
+ const ctrl = new AbortController;
270
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
271
+ try {
272
+ const res = await fetch(`https://api.github.com/repos/${slug}/releases?per_page=${limit}`, {
273
+ signal: ctrl.signal,
274
+ headers: {
275
+ accept: "application/vnd.github+json",
276
+ "x-github-api-version": "2022-11-28"
277
+ }
278
+ });
279
+ if (!res.ok)
280
+ return [];
281
+ const body = await res.json();
282
+ if (!Array.isArray(body))
283
+ return [];
284
+ return body.map((release) => {
285
+ const version = versionFromTagName(release.tag_name);
286
+ if (!version || typeof release.html_url !== "string")
287
+ return null;
288
+ return { version, url: release.html_url };
289
+ }).filter((release) => release !== null);
290
+ } catch {
291
+ return [];
292
+ } finally {
293
+ clearTimeout(timer);
294
+ }
295
+ }
296
+ function releasePageUrl(version) {
297
+ const slug = repoSlug();
298
+ if (!slug)
299
+ return null;
300
+ return `https://github.com/${slug}/releases/tag/v${version}`;
301
+ }
302
+ var CURRENT_VERSION, PACKAGE_NAME, UPDATE_SCRIPT_URL = "https://raw.githubusercontent.com/Sma1lboy/kobe/main/scripts/update.sh", UPDATE_COMMAND, FETCH_TIMEOUT_MS = 3000;
303
+ var init_version = __esm(() => {
304
+ init_package();
305
+ init_env();
306
+ CURRENT_VERSION = package_default.version;
307
+ PACKAGE_NAME = package_default.name;
308
+ UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
309
+ });
310
+
84
311
  // src/state/repos.ts
85
312
  var exports_repos = {};
86
313
  __export(exports_repos, {
87
314
  statePath: () => statePath,
315
+ setRepoInitOverride: () => setRepoInitOverride,
88
316
  setPersistedString: () => setPersistedString,
89
317
  resolveRepoRoot: () => resolveRepoRoot,
90
318
  removeSavedRepo: () => removeSavedRepo,
91
319
  normalizeSavedRepos: () => normalizeSavedRepos,
92
320
  getSavedRepos: () => getSavedRepos,
321
+ getRepoInitOverride: () => getRepoInitOverride,
93
322
  getPersistedString: () => getPersistedString,
94
323
  addSavedRepo: () => addSavedRepo
95
324
  });
@@ -182,6 +411,45 @@ function normalizeSavedRepos() {
182
411
  state.savedRepos = next;
183
412
  save(state);
184
413
  }
414
+ function readRepoConfigs(state) {
415
+ const raw = state.repoConfigs;
416
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
417
+ return {};
418
+ return raw;
419
+ }
420
+ function coerceOverride(entry) {
421
+ if (!entry || typeof entry !== "object")
422
+ return {};
423
+ const e = entry;
424
+ return {
425
+ initScript: typeof e.initScript === "string" && e.initScript.length > 0 ? e.initScript : undefined,
426
+ initPrompt: typeof e.initPrompt === "string" && e.initPrompt.length > 0 ? e.initPrompt : undefined
427
+ };
428
+ }
429
+ function getRepoInitOverride(repoRoot) {
430
+ return coerceOverride(readRepoConfigs(load())[resolveRepoRoot(repoRoot)]);
431
+ }
432
+ function setRepoInitOverride(repoRoot, patch) {
433
+ const normalized = resolveRepoRoot(repoRoot);
434
+ const state = load();
435
+ const configs = { ...readRepoConfigs(state) };
436
+ const cur = coerceOverride(configs[normalized]);
437
+ const nextScript = patch.initScript === undefined ? cur.initScript : patch.initScript || undefined;
438
+ const nextPrompt = patch.initPrompt === undefined ? cur.initPrompt : patch.initPrompt || undefined;
439
+ const next = {
440
+ ...nextScript ? { initScript: nextScript } : {},
441
+ ...nextPrompt ? { initPrompt: nextPrompt } : {}
442
+ };
443
+ if (!next.initScript && !next.initPrompt) {
444
+ const { [normalized]: _dropped, ...rest } = configs;
445
+ state.repoConfigs = rest;
446
+ } else {
447
+ configs[normalized] = next;
448
+ state.repoConfigs = configs;
449
+ }
450
+ save(state);
451
+ return next;
452
+ }
185
453
  function removeSavedRepo(absPath) {
186
454
  const state = load();
187
455
  const cur = getSavedRepos();
@@ -272,7 +540,7 @@ class TaskIndexStore {
272
540
  kobeDir;
273
541
  path;
274
542
  tmpPath;
275
- cache = { version: CURRENT_VERSION, tasks: [] };
543
+ cache = { version: CURRENT_VERSION2, tasks: [] };
276
544
  loaded = false;
277
545
  listeners = new Set;
278
546
  saveChain = Promise.resolve();
@@ -308,7 +576,7 @@ class TaskIndexStore {
308
576
  } catch (err) {
309
577
  const code = err.code;
310
578
  if (code === "ENOENT") {
311
- this.cache = { version: CURRENT_VERSION, tasks: [] };
579
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
312
580
  this.loaded = true;
313
581
  this.notifyListeners();
314
582
  return this.snapshot();
@@ -320,7 +588,7 @@ class TaskIndexStore {
320
588
  parsed = JSON.parse(raw);
321
589
  } catch (err) {
322
590
  console.warn(`[kobe] tasks.json at ${this.path} is corrupted (${err.message}); recovering with empty index. The stale file is left in place.`);
323
- this.cache = { version: CURRENT_VERSION, tasks: [] };
591
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
324
592
  this.loaded = true;
325
593
  this.notifyListeners();
326
594
  return this.snapshot();
@@ -420,7 +688,7 @@ class TaskIndexStore {
420
688
  if (err.code !== "ENOENT")
421
689
  throw err;
422
690
  }
423
- this.cache = { version: CURRENT_VERSION, tasks: [] };
691
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
424
692
  this.loaded = false;
425
693
  }
426
694
  assertLoaded() {
@@ -430,7 +698,7 @@ class TaskIndexStore {
430
698
  }
431
699
  snapshot() {
432
700
  return {
433
- version: CURRENT_VERSION,
701
+ version: CURRENT_VERSION2,
434
702
  tasks: this.cache.tasks.slice()
435
703
  };
436
704
  }
@@ -450,13 +718,13 @@ class TaskIndexStore {
450
718
  function normalizeIndex(parsed, source) {
451
719
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
452
720
  console.warn(`[kobe] tasks.json at ${source} is not an object; recovering with empty index.`);
453
- return { version: CURRENT_VERSION, tasks: [] };
721
+ return { version: CURRENT_VERSION2, tasks: [] };
454
722
  }
455
723
  const obj = parsed;
456
724
  const version = obj.version;
457
725
  if (version !== undefined && version !== 1 && version !== 2 && version !== 3) {
458
726
  console.warn(`[kobe] tasks.json at ${source} has unsupported version=${String(version)}; recovering with empty index.`);
459
- return { version: CURRENT_VERSION, tasks: [] };
727
+ return { version: CURRENT_VERSION2, tasks: [] };
460
728
  }
461
729
  const rawTasks = Array.isArray(obj.tasks) ? obj.tasks : [];
462
730
  const tasks = [];
@@ -468,7 +736,7 @@ function normalizeIndex(parsed, source) {
468
736
  console.warn(`[kobe] dropping malformed task entry from ${source}: ${JSON.stringify(entry)}`);
469
737
  }
470
738
  }
471
- return { version: CURRENT_VERSION, tasks };
739
+ return { version: CURRENT_VERSION2, tasks };
472
740
  }
473
741
  function coerceTask(value) {
474
742
  if (!value || typeof value !== "object")
@@ -534,7 +802,7 @@ function isVendorId(v) {
534
802
  function isTaskStatus(s) {
535
803
  return s === "backlog" || s === "in_progress" || s === "in_review" || s === "done" || s === "canceled" || s === "error";
536
804
  }
537
- var CURRENT_VERSION = 3;
805
+ var CURRENT_VERSION2 = 3;
538
806
  var init_store = __esm(() => {
539
807
  init_ulid();
540
808
  });
@@ -3054,279 +3322,61 @@ class KobeDaemonClient {
3054
3322
  }
3055
3323
  onSocketClose(which) {
3056
3324
  if (this.socket !== which)
3057
- return;
3058
- this.socket = null;
3059
- for (const pending of this.pending.values())
3060
- pending.reject(new Error("daemon connection closed"));
3061
- this.pending.clear();
3062
- this.emitLifecycle("close");
3063
- }
3064
- emitLifecycle(name) {
3065
- for (const handler of this.lifecycleHandlers.get(name) ?? []) {
3066
- try {
3067
- handler();
3068
- } catch (err) {
3069
- console.error(`[kobe] lifecycle handler for "${name}" threw:`, err);
3070
- }
3071
- }
3072
- }
3073
- onData(chunk) {
3074
- this.buffer += chunk;
3075
- let nl = this.buffer.indexOf(`
3076
- `);
3077
- while (nl !== -1) {
3078
- const line = this.buffer.slice(0, nl);
3079
- this.buffer = this.buffer.slice(nl + 1);
3080
- if (line.trim().length > 0)
3081
- this.onLine(line);
3082
- nl = this.buffer.indexOf(`
3083
- `);
3084
- }
3085
- }
3086
- onLine(line) {
3087
- const frame = JSON.parse(line);
3088
- if (frame.type === "event") {
3089
- this.emit(frame);
3090
- return;
3091
- }
3092
- if (frame.type !== "response")
3093
- return;
3094
- const pending = this.pending.get(frame.id);
3095
- if (!pending)
3096
- return;
3097
- this.pending.delete(frame.id);
3098
- if (frame.error)
3099
- pending.reject(new Error(frame.error.message));
3100
- else
3101
- pending.resolve(frame.payload);
3102
- }
3103
- emit(frame) {
3104
- for (const handler of this.handlers.get(frame.name) ?? [])
3105
- handler(frame);
3106
- for (const handler of this.handlers.get("*") ?? [])
3107
- handler(frame);
3108
- }
3109
- }
3110
- var init_client = __esm(() => {
3111
- init_protocol();
3112
- });
3113
-
3114
- // package.json
3115
- var package_default;
3116
- var init_package = __esm(() => {
3117
- package_default = {
3118
- $schema: "https://json.schemastore.org/package.json",
3119
- name: "@sma1lboy/kobe",
3120
- version: "0.6.6",
3121
- description: "TUI orchestrator for Claude Code (codename)",
3122
- type: "module",
3123
- packageManager: "bun@1.3.13",
3124
- bin: {
3125
- kobe: "dist/cli/index.js"
3126
- },
3127
- files: ["dist", "README.md", "LICENSE"],
3128
- publishConfig: {
3129
- access: "public"
3130
- },
3131
- repository: {
3132
- type: "git",
3133
- url: "git+https://github.com/Sma1lboy/kobe.git"
3134
- },
3135
- homepage: "https://github.com/Sma1lboy/kobe#readme",
3136
- bugs: {
3137
- url: "https://github.com/Sma1lboy/kobe/issues"
3138
- },
3139
- engines: {
3140
- bun: ">=1.3.11"
3141
- },
3142
- scripts: {
3143
- dev: "env KOBE_DEV=1 bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
3144
- "dev:sandbox": "mkdir -p .dev-sandbox/home && env KOBE_DEV=1 KOBE_HOME_DIR=$PWD/.dev-sandbox/home KOBE_TMUX_SOCKET=kobe-sandbox bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
3145
- "dev:sandbox:reset": "env KOBE_TMUX_SOCKET=kobe-sandbox bun ./src/cli/index.ts kill-sessions",
3146
- build: "bun run scripts/build.ts",
3147
- compile: "bun run scripts/compile.ts",
3148
- typecheck: "tsc --noEmit",
3149
- test: "bun run test:fast && bun run test:socket",
3150
- "test:fast": "vitest run --passWithNoTests",
3151
- "test:socket": "KOBE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=1 --passWithNoTests",
3152
- lint: "biome check .",
3153
- knip: "knip-bun",
3154
- postinstall: "bun run scripts/check-preview-deps.ts || true",
3155
- prepublishOnly: "bun run typecheck && bun run build"
3156
- },
3157
- "//": "biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",
3158
- dependencies: {
3159
- "@ansi-tools/parser": "^1.0.15",
3160
- "@opentui/core": "0.2.4",
3161
- "@opentui/solid": "0.2.4",
3162
- "solid-js": "1.9.12"
3163
- },
3164
- devDependencies: {
3165
- "@biomejs/biome": "1.9.4",
3166
- "@tsconfig/bun": "1.0.9",
3167
- "@types/bun": "1.3.12",
3168
- knip: "^6.14.2",
3169
- "node-pty": "^1.1.0",
3170
- typescript: "5.8.2",
3171
- vitest: "2.1.9"
3172
- },
3173
- trustedDependencies: []
3174
- };
3175
- });
3176
-
3177
- // src/version.ts
3178
- var exports_version = {};
3179
- __export(exports_version, {
3180
- repoSlug: () => repoSlug,
3181
- releasePageUrl: () => releasePageUrl,
3182
- recommendedGlobalInstallCommand: () => recommendedGlobalInstallCommand,
3183
- isNewerSemver: () => isNewerSemver,
3184
- fetchReleaseSummaries: () => fetchReleaseSummaries,
3185
- fetchReleaseNotes: () => fetchReleaseNotes,
3186
- checkLatestVersion: () => checkLatestVersion,
3187
- UPDATE_SCRIPT_URL: () => UPDATE_SCRIPT_URL,
3188
- UPDATE_COMMAND: () => UPDATE_COMMAND,
3189
- PACKAGE_NAME: () => PACKAGE_NAME,
3190
- CURRENT_VERSION: () => CURRENT_VERSION2
3191
- });
3192
- function repoSlug() {
3193
- const url = package_default.repository?.url;
3194
- if (!url)
3195
- return null;
3196
- const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
3197
- if (!m || !m[1] || !m[2])
3198
- return null;
3199
- return `${m[1]}/${m[2]}`;
3200
- }
3201
- function recommendedGlobalInstallCommand() {
3202
- return `npm install -g ${PACKAGE_NAME}@latest`;
3203
- }
3204
- async function fetchLatestFromRegistry(packageName) {
3205
- const ctrl = new AbortController;
3206
- const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
3207
- try {
3208
- const encoded = packageName.replace("/", "%2F");
3209
- const res = await fetch(`https://registry.npmjs.org/${encoded}/latest`, {
3210
- signal: ctrl.signal,
3211
- headers: { accept: "application/json" }
3212
- });
3213
- if (!res.ok)
3214
- return null;
3215
- const body = await res.json();
3216
- if (typeof body.version !== "string")
3217
- return null;
3218
- return body.version;
3219
- } catch {
3220
- return null;
3221
- } finally {
3222
- clearTimeout(timer);
3223
- }
3224
- }
3225
- function isNewerSemver(latest, current) {
3226
- const norm = (v) => v.split("-")[0] ?? v;
3227
- const a = norm(latest).split(".").map((s) => Number.parseInt(s, 10));
3228
- const b = norm(current).split(".").map((s) => Number.parseInt(s, 10));
3229
- for (let i = 0;i < 3; i++) {
3230
- const av = a[i] ?? 0;
3231
- const bv = b[i] ?? 0;
3232
- if (Number.isNaN(av) || Number.isNaN(bv))
3233
- return false;
3234
- if (av > bv)
3235
- return true;
3236
- if (av < bv)
3237
- return false;
3238
- }
3239
- return false;
3240
- }
3241
- async function checkLatestVersion(opts = {}) {
3242
- if (isDev() && !opts.force)
3243
- return null;
3244
- const latest = await fetchLatestFromRegistry(PACKAGE_NAME);
3245
- if (!latest)
3246
- return null;
3247
- return {
3248
- current: CURRENT_VERSION2,
3249
- latest,
3250
- hasUpdate: isNewerSemver(latest, CURRENT_VERSION2)
3251
- };
3252
- }
3253
- function versionFromTagName(tagName) {
3254
- if (typeof tagName !== "string")
3255
- return null;
3256
- const match = tagName.match(/^v(\d+\.\d+\.\d+)$/);
3257
- return match?.[1] ?? null;
3258
- }
3259
- async function fetchReleaseNotes(version) {
3260
- const slug = repoSlug();
3261
- if (!slug)
3262
- return null;
3263
- const tag = `v${version}`;
3264
- const ctrl = new AbortController;
3265
- const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
3266
- try {
3267
- const res = await fetch(`https://api.github.com/repos/${slug}/releases/tags/${tag}`, {
3268
- signal: ctrl.signal,
3269
- headers: {
3270
- accept: "application/vnd.github+json",
3271
- "x-github-api-version": "2022-11-28"
3272
- }
3273
- });
3274
- if (!res.ok)
3275
- return null;
3276
- const body = await res.json();
3277
- if (typeof body.body !== "string" || typeof body.html_url !== "string")
3278
- return null;
3279
- return { body: body.body, url: body.html_url, version };
3280
- } catch {
3281
- return null;
3282
- } finally {
3283
- clearTimeout(timer);
3325
+ return;
3326
+ this.socket = null;
3327
+ for (const pending of this.pending.values())
3328
+ pending.reject(new Error("daemon connection closed"));
3329
+ this.pending.clear();
3330
+ this.emitLifecycle("close");
3284
3331
  }
3285
- }
3286
- async function fetchReleaseSummaries(limit = 12) {
3287
- const slug = repoSlug();
3288
- if (!slug)
3289
- return [];
3290
- const ctrl = new AbortController;
3291
- const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
3292
- try {
3293
- const res = await fetch(`https://api.github.com/repos/${slug}/releases?per_page=${limit}`, {
3294
- signal: ctrl.signal,
3295
- headers: {
3296
- accept: "application/vnd.github+json",
3297
- "x-github-api-version": "2022-11-28"
3332
+ emitLifecycle(name) {
3333
+ for (const handler of this.lifecycleHandlers.get(name) ?? []) {
3334
+ try {
3335
+ handler();
3336
+ } catch (err) {
3337
+ console.error(`[kobe] lifecycle handler for "${name}" threw:`, err);
3298
3338
  }
3299
- });
3300
- if (!res.ok)
3301
- return [];
3302
- const body = await res.json();
3303
- if (!Array.isArray(body))
3304
- return [];
3305
- return body.map((release) => {
3306
- const version = versionFromTagName(release.tag_name);
3307
- if (!version || typeof release.html_url !== "string")
3308
- return null;
3309
- return { version, url: release.html_url };
3310
- }).filter((release) => release !== null);
3311
- } catch {
3312
- return [];
3313
- } finally {
3314
- clearTimeout(timer);
3339
+ }
3340
+ }
3341
+ onData(chunk) {
3342
+ this.buffer += chunk;
3343
+ let nl = this.buffer.indexOf(`
3344
+ `);
3345
+ while (nl !== -1) {
3346
+ const line = this.buffer.slice(0, nl);
3347
+ this.buffer = this.buffer.slice(nl + 1);
3348
+ if (line.trim().length > 0)
3349
+ this.onLine(line);
3350
+ nl = this.buffer.indexOf(`
3351
+ `);
3352
+ }
3353
+ }
3354
+ onLine(line) {
3355
+ const frame = JSON.parse(line);
3356
+ if (frame.type === "event") {
3357
+ this.emit(frame);
3358
+ return;
3359
+ }
3360
+ if (frame.type !== "response")
3361
+ return;
3362
+ const pending = this.pending.get(frame.id);
3363
+ if (!pending)
3364
+ return;
3365
+ this.pending.delete(frame.id);
3366
+ if (frame.error)
3367
+ pending.reject(new Error(frame.error.message));
3368
+ else
3369
+ pending.resolve(frame.payload);
3370
+ }
3371
+ emit(frame) {
3372
+ for (const handler of this.handlers.get(frame.name) ?? [])
3373
+ handler(frame);
3374
+ for (const handler of this.handlers.get("*") ?? [])
3375
+ handler(frame);
3315
3376
  }
3316
3377
  }
3317
- function releasePageUrl(version) {
3318
- const slug = repoSlug();
3319
- if (!slug)
3320
- return null;
3321
- return `https://github.com/${slug}/releases/tag/v${version}`;
3322
- }
3323
- var CURRENT_VERSION2, PACKAGE_NAME, UPDATE_SCRIPT_URL = "https://raw.githubusercontent.com/Sma1lboy/kobe/main/scripts/update.sh", UPDATE_COMMAND, FETCH_TIMEOUT_MS = 3000;
3324
- var init_version = __esm(() => {
3325
- init_package();
3326
- init_env();
3327
- CURRENT_VERSION2 = package_default.version;
3328
- PACKAGE_NAME = package_default.name;
3329
- UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
3378
+ var init_client = __esm(() => {
3379
+ init_protocol();
3330
3380
  });
3331
3381
 
3332
3382
  // src/daemon/crash-log.ts
@@ -3386,11 +3436,11 @@ class DaemonEventBus {
3386
3436
  }
3387
3437
 
3388
3438
  // src/daemon/paths.ts
3389
- import { createHash } from "crypto";
3439
+ import { createHash as createHash2 } from "crypto";
3390
3440
  import { homedir as homedir3, tmpdir } from "os";
3391
3441
  import { join as join3 } from "path";
3392
3442
  function shortHomeTag(homeDir2) {
3393
- return createHash("sha1").update(homeDir2).digest("hex").slice(0, 8);
3443
+ return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
3394
3444
  }
3395
3445
  function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
3396
3446
  if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
@@ -3952,6 +4002,157 @@ var init_daemon_process = __esm(() => {
3952
4002
  DAEMON_START_ARGS = ["daemon", "start"];
3953
4003
  });
3954
4004
 
4005
+ // src/cli/repo-cmd.ts
4006
+ var exports_repo_cmd = {};
4007
+ __export(exports_repo_cmd, {
4008
+ runRepoSubcommand: () => runRepoSubcommand
4009
+ });
4010
+ import { readFileSync as readFileSync2 } from "fs";
4011
+ import { resolve as resolve3 } from "path";
4012
+ function usageError(message) {
4013
+ process.stderr.write(`kobe repo: ${message}
4014
+
4015
+ ${REPO_USAGE}
4016
+ `);
4017
+ process.exit(2);
4018
+ }
4019
+ function readArgFile(path3) {
4020
+ try {
4021
+ return readFileSync2(resolve3(process.cwd(), path3), "utf8");
4022
+ } catch (err) {
4023
+ usageError(`cannot read ${path3}: ${err instanceof Error ? err.message : String(err)}`);
4024
+ }
4025
+ }
4026
+ function parseRepoArgs(args) {
4027
+ const out = {};
4028
+ for (let i = 0;i < args.length; i++) {
4029
+ const a = args[i];
4030
+ const need = () => {
4031
+ const v = args[++i];
4032
+ if (v === undefined)
4033
+ usageError(`${a} requires a value`);
4034
+ return v;
4035
+ };
4036
+ if (a === "--init-script")
4037
+ out.initScript = need();
4038
+ else if (a === "--init-script-file")
4039
+ out.initScript = readArgFile(need());
4040
+ else if (a === "--init-prompt")
4041
+ out.initPrompt = need();
4042
+ else if (a === "--init-prompt-file")
4043
+ out.initPrompt = readArgFile(need());
4044
+ else if (a.startsWith("-"))
4045
+ usageError(`unknown flag "${a}"`);
4046
+ else if (out.path === undefined)
4047
+ out.path = a;
4048
+ else
4049
+ usageError(`unexpected argument "${a}"`);
4050
+ }
4051
+ return out;
4052
+ }
4053
+ function parseUnsetArgs(args) {
4054
+ let path3;
4055
+ let clearScript = false;
4056
+ let clearPrompt = false;
4057
+ for (const a of args) {
4058
+ if (a === "--init-script")
4059
+ clearScript = true;
4060
+ else if (a === "--init-prompt")
4061
+ clearPrompt = true;
4062
+ else if (a.startsWith("-"))
4063
+ usageError(`unknown flag "${a}"`);
4064
+ else if (path3 === undefined)
4065
+ path3 = a;
4066
+ else
4067
+ usageError(`unexpected argument "${a}"`);
4068
+ }
4069
+ if (!clearScript && !clearPrompt) {
4070
+ clearScript = true;
4071
+ clearPrompt = true;
4072
+ }
4073
+ return { path: path3, clearScript, clearPrompt };
4074
+ }
4075
+ async function runRepoSubcommand(args) {
4076
+ const [verb, ...rest] = args;
4077
+ if (verb === undefined || verb === "--help" || verb === "-h" || verb === "help") {
4078
+ process.stdout.write(`${REPO_USAGE}
4079
+ `);
4080
+ return;
4081
+ }
4082
+ const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
4083
+ const { existsSync: existsSync2 } = await import("fs");
4084
+ const { join: join4 } = await import("path");
4085
+ if (verb === "show") {
4086
+ const [pathArg] = rest.filter((a) => !a.startsWith("-"));
4087
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), pathArg ?? "."));
4088
+ const override = getRepoInitOverride2(repo);
4089
+ const hasFileScript = existsSync2(join4(repo, ".kobe", "init.sh"));
4090
+ const hasFilePrompt = existsSync2(join4(repo, ".kobe", "init-prompt.md"));
4091
+ console.log(`repo: ${repo}`);
4092
+ console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
4093
+ console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
4094
+ console.log(` override initScript: ${override.initScript ? quotePreview(override.initScript) : "(unset)"}`);
4095
+ console.log(` override initPrompt: ${override.initPrompt ? quotePreview(override.initPrompt) : "(unset)"}`);
4096
+ return;
4097
+ }
4098
+ if (verb === "set") {
4099
+ const flags = parseRepoArgs(rest);
4100
+ if (flags.initScript === undefined && flags.initPrompt === undefined) {
4101
+ usageError("set needs at least one of --init-script(-file) / --init-prompt(-file)");
4102
+ }
4103
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), flags.path ?? "."));
4104
+ const next = setRepoInitOverride2(repo, {
4105
+ ...flags.initScript !== undefined ? { initScript: flags.initScript } : {},
4106
+ ...flags.initPrompt !== undefined ? { initPrompt: flags.initPrompt } : {}
4107
+ });
4108
+ console.log(`updated override for ${repo}`);
4109
+ console.log(` initScript: ${next.initScript ? quotePreview(next.initScript) : "(unset)"}`);
4110
+ console.log(` initPrompt: ${next.initPrompt ? quotePreview(next.initPrompt) : "(unset)"}`);
4111
+ return;
4112
+ }
4113
+ if (verb === "unset") {
4114
+ const { path: path3, clearScript, clearPrompt } = parseUnsetArgs(rest);
4115
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), path3 ?? "."));
4116
+ const next = setRepoInitOverride2(repo, {
4117
+ ...clearScript ? { initScript: "" } : {},
4118
+ ...clearPrompt ? { initPrompt: "" } : {}
4119
+ });
4120
+ console.log(`cleared override for ${repo}`);
4121
+ console.log(` initScript: ${next.initScript ? quotePreview(next.initScript) : "(unset)"}`);
4122
+ console.log(` initPrompt: ${next.initPrompt ? quotePreview(next.initPrompt) : "(unset)"}`);
4123
+ return;
4124
+ }
4125
+ usageError(`unknown verb "${verb}"`);
4126
+ }
4127
+ function quotePreview(value) {
4128
+ const oneLine = value.replace(/\s+/g, " ").trim();
4129
+ return oneLine.length > 60 ? `"${oneLine.slice(0, 57)}\u2026"` : `"${oneLine}"`;
4130
+ }
4131
+ var REPO_USAGE;
4132
+ var init_repo_cmd = __esm(() => {
4133
+ REPO_USAGE = [
4134
+ "Usage: kobe repo <show|set|unset> [path] [options]",
4135
+ "",
4136
+ "Manage a repo's per-user init override (state.json fallback for repos",
4137
+ "that don't ship .kobe/init.sh / .kobe/init-prompt.md).",
4138
+ "",
4139
+ "Commands:",
4140
+ " show [path] Print the override + whether the repo ships .kobe/ files",
4141
+ " set [path] <options> Set the init script and/or first prompt",
4142
+ " unset [path] [--init-script] [--init-prompt] Clear one or both (default: both)",
4143
+ "",
4144
+ "Set options (later wins; *-file reads from disk):",
4145
+ " --init-script <text> Inline shell to run before the engine",
4146
+ " --init-script-file <path> Read the init script from a file",
4147
+ " --init-prompt <text> Inline first prompt for the engine",
4148
+ " --init-prompt-file <path> Read the first prompt from a file",
4149
+ "",
4150
+ " path defaults to the current directory (resolved to its git toplevel).",
4151
+ ""
4152
+ ].join(`
4153
+ `);
4154
+ });
4155
+
3955
4156
  // src/engine/interactive-command.ts
3956
4157
  function engineCommandKey(vendor) {
3957
4158
  return `engineCommand.${vendor}`;
@@ -4002,6 +4203,7 @@ __export(exports_client, {
4002
4203
  tmuxArgs: () => tmuxArgs,
4003
4204
  tagPaneRole: () => tagPaneRole,
4004
4205
  tagClaudePane: () => tagClaudePane,
4206
+ switchClientBeforeKill: () => switchClientBeforeKill,
4005
4207
  setWindowOption: () => setWindowOption,
4006
4208
  setSessionOption: () => setSessionOption,
4007
4209
  sessionExists: () => sessionExists,
@@ -4016,6 +4218,7 @@ __export(exports_client, {
4016
4218
  killSession: () => killSession,
4017
4219
  getSessionOptions: () => getSessionOptions,
4018
4220
  getSessionOption: () => getSessionOption,
4221
+ ensureFallbackSession: () => ensureFallbackSession,
4019
4222
  currentSessionName: () => currentSessionName,
4020
4223
  claudePaneIdStrict: () => claudePaneIdStrict,
4021
4224
  claudePaneId: () => claudePaneId,
@@ -4023,6 +4226,7 @@ __export(exports_client, {
4023
4226
  attachArgv: () => attachArgv,
4024
4227
  PANE_ROLE_OPTION: () => PANE_ROLE_OPTION,
4025
4228
  KOBE_TMUX_SOCKET: () => KOBE_TMUX_SOCKET,
4229
+ KOBE_HOME_SESSION: () => KOBE_HOME_SESSION,
4026
4230
  CLAUDE_ROLE_OPTION: () => CLAUDE_ROLE_OPTION
4027
4231
  });
4028
4232
  function tmuxArgs(...args) {
@@ -4197,12 +4401,77 @@ async function killSession(name) {
4197
4401
  if (await sessionExists(name))
4198
4402
  await runTmux(["kill-session", "-t", `=${name}`]);
4199
4403
  }
4200
- var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude";
4404
+ async function ensureFallbackSession() {
4405
+ const name = KOBE_HOME_SESSION;
4406
+ if (!await sessionExists(name)) {
4407
+ await runTmux([
4408
+ "new-session",
4409
+ "-d",
4410
+ "-s",
4411
+ name,
4412
+ "-x",
4413
+ "220",
4414
+ "-y",
4415
+ "50",
4416
+ "sh",
4417
+ "-c",
4418
+ 'clear; printf "\\n No active task\\n\\n Select a task from the sidebar to continue.\\n\\n"; exec ${SHELL:-sh}'
4419
+ ]);
4420
+ }
4421
+ return name;
4422
+ }
4423
+ async function switchClientBeforeKill(killedName, nextSessionName) {
4424
+ const current = await currentSessionName();
4425
+ if (current !== killedName)
4426
+ return;
4427
+ if (nextSessionName && nextSessionName !== killedName && await sessionExists(nextSessionName)) {
4428
+ await runTmux(["switch-client", "-t", `=${nextSessionName}`]);
4429
+ return;
4430
+ }
4431
+ const fallback = await ensureFallbackSession();
4432
+ await runTmux(["switch-client", "-t", `=${fallback}`]);
4433
+ }
4434
+ var KOBE_TMUX_SOCKET, PANE_ROLE_OPTION = "@kobe_role", CLAUDE_ROLE_OPTION, CLAUDE_ROLE_VALUE = "claude", KOBE_HOME_SESSION = "kobe-home";
4201
4435
  var init_client2 = __esm(() => {
4202
4436
  KOBE_TMUX_SOCKET = process.env.KOBE_TMUX_SOCKET?.trim() || "kobe";
4203
4437
  CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
4204
4438
  });
4205
4439
 
4440
+ // src/tmux/prompt-delivery.ts
4441
+ async function waitForEnginePane(session, fresh) {
4442
+ let prev = null;
4443
+ for (let attempt = 0;attempt < 24; attempt++) {
4444
+ const pane2 = await claudePaneIdStrict(session);
4445
+ if (pane2) {
4446
+ if (!fresh)
4447
+ return { pane: pane2, ready: true };
4448
+ const cur = (await capturePaneById(pane2)).trim();
4449
+ if (cur.length > 0 && cur === prev)
4450
+ return { pane: pane2, ready: true };
4451
+ prev = cur;
4452
+ }
4453
+ await sleep(250);
4454
+ }
4455
+ const pane = await claudePaneIdStrict(session) || await claudePaneId(session);
4456
+ return { pane, ready: false };
4457
+ }
4458
+ async function pasteAndSubmit(pane, text) {
4459
+ const buffer = `kobe-api-${pane.replace(/[^A-Za-z0-9]/g, "")}`;
4460
+ await runTmux(["set-buffer", "-b", buffer, "--", text]);
4461
+ await runTmux(["paste-buffer", "-p", "-d", "-b", buffer, "-t", pane]);
4462
+ await sendKeyName(pane, "Enter");
4463
+ }
4464
+ async function deliverFirstPrompt(session, prompt) {
4465
+ const { pane } = await waitForEnginePane(session, true);
4466
+ if (!pane)
4467
+ return;
4468
+ await pasteAndSubmit(pane, prompt);
4469
+ }
4470
+ var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
4471
+ var init_prompt_delivery = __esm(() => {
4472
+ init_client2();
4473
+ });
4474
+
4206
4475
  // src/cli/invocation.ts
4207
4476
  import { fileURLToPath as fileURLToPath2 } from "url";
4208
4477
  function kobeCliInvocation() {
@@ -4225,6 +4494,35 @@ function shellQuoteArgv(argv) {
4225
4494
  function keepAlive(cmd) {
4226
4495
  return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
4227
4496
  }
4497
+ function shQuote(s) {
4498
+ return `'${s.replace(/'/g, "'\\''")}'`;
4499
+ }
4500
+ function engineLaunchLine(engineCmd, init) {
4501
+ const tail = keepAlive(engineCmd);
4502
+ const script = init?.initScript?.trim();
4503
+ if (!script)
4504
+ return tail;
4505
+ const group = ["{", script, "}"].join(`
4506
+ `);
4507
+ if (init?.markerPath) {
4508
+ const marker = shQuote(init.markerPath);
4509
+ const markerDir = shQuote(markerDirOf(init.markerPath));
4510
+ return [
4511
+ `if [ ! -f ${marker} ]; then`,
4512
+ group,
4513
+ `if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
4514
+ "fi",
4515
+ tail
4516
+ ].join(`
4517
+ `);
4518
+ }
4519
+ return [group, tail].join(`
4520
+ `);
4521
+ }
4522
+ function markerDirOf(p) {
4523
+ const i = p.lastIndexOf("/");
4524
+ return i <= 0 ? "." : p.slice(0, i);
4525
+ }
4228
4526
  function fallbackOpsScript(cwd) {
4229
4527
  return `cd ${shellQuote(cwd)} && while :; do clear; printf "\\033[1m# %s\\033[0m\\n\\n" ${shellQuote(cwd)}; git status --short --branch 2>/dev/null | sed 's/^/ /' || true; printf "\\n"; if command -v lsd >/dev/null 2>&1; then lsd --tree --git -I node_modules -I .git --depth 2 .; elif command -v eza >/dev/null 2>&1; then eza --tree --git -L 2 -I 'node_modules|.git' .; elif command -v tree >/dev/null 2>&1; then tree -L 2 -I 'node_modules|.git'; else ls -la; fi; sleep 2; done`;
4230
4528
  }
@@ -4235,6 +4533,9 @@ function previewWindowCommand(args) {
4235
4533
  const fallback = `cd ${wt} && if ! git diff --quiet HEAD -- ${file} 2>/dev/null; then ` + `git diff HEAD -- ${file} | { delta --paging=always 2>/dev/null || less -R; }; ` + `else bat --style=plain --paging=always ${file} 2>/dev/null || \${PAGER:-less} ${file} 2>/dev/null || cat ${file}; fi`;
4236
4534
  return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
4237
4535
  }
4536
+ function updatePageCommand(args) {
4537
+ return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
4538
+ }
4238
4539
  function tasksPaneCommand(cliInvocation) {
4239
4540
  return shellQuoteArgv([...cliInvocation, "tasks"]);
4240
4541
  }
@@ -4254,8 +4555,10 @@ __export(exports_tmux, {
4254
4555
  tmuxSessionName: () => tmuxSessionName,
4255
4556
  tmuxInitialSizeArgs: () => tmuxInitialSizeArgs,
4256
4557
  tmuxAvailable: () => tmuxAvailable,
4558
+ switchClientBeforeKill: () => switchClientBeforeKill,
4257
4559
  sessionExists: () => sessionExists,
4258
4560
  quickCreate: () => quickCreate,
4561
+ openUpdateTab: () => openUpdateTab,
4259
4562
  openSettingsTab: () => openSettingsTab,
4260
4563
  openNewTaskTab: () => openNewTaskTab,
4261
4564
  newChatTab: () => newChatTab,
@@ -4334,7 +4637,10 @@ async function ensureSessionImpl(opts) {
4334
4637
  "-P",
4335
4638
  "-F",
4336
4639
  "#{pane_id}",
4337
- keepAlive(shellQuoteArgv(opts.command))
4640
+ engineLaunchLine(shellQuoteArgv(opts.command), {
4641
+ initScript: opts.initScript,
4642
+ markerPath: opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
4643
+ })
4338
4644
  ]);
4339
4645
  const pane0 = r0.stdout.trim();
4340
4646
  if (!pane0) {
@@ -4378,6 +4684,10 @@ async function ensureSessionImpl(opts) {
4378
4684
  ["bind-key", "f", "run-shell", `${envStr}${invStr} quick-create --session '#{session_name}'`]
4379
4685
  ]);
4380
4686
  await runTmux(["select-pane", "-t", pane0]);
4687
+ const initPrompt = opts.initPrompt?.trim();
4688
+ if (initPrompt) {
4689
+ deliverFirstPrompt(opts.name, initPrompt).catch((err) => console.error("[kobe tmux] init prompt delivery failed:", err));
4690
+ }
4381
4691
  return true;
4382
4692
  }
4383
4693
  async function relaunchEngineInAllWindows(session, cwd, command) {
@@ -4454,10 +4764,10 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
4454
4764
  const claudePane = panes.find((pane) => pane.role === "claude")?.paneId;
4455
4765
  const tasksPane = panes.find((pane) => pane.role === "tasks");
4456
4766
  const opsPane = panes.find((pane) => pane.role === "ops");
4457
- if (tasksPane && tasksPane.version !== CURRENT_VERSION2) {
4458
- commands.push(["respawn-pane", "-k", "-t", tasksPane.paneId, "-c", cwd, keepAlive(envPrefix + tasksPaneCommand(inv))], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION2]);
4767
+ if (tasksPane && tasksPane.version !== CURRENT_VERSION) {
4768
+ commands.push(["respawn-pane", "-k", "-t", tasksPane.paneId, "-c", cwd, keepAlive(envPrefix + tasksPaneCommand(inv))], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
4459
4769
  }
4460
- if (opsPane && claudePane && opsPane.version !== CURRENT_VERSION2) {
4770
+ if (opsPane && claudePane && opsPane.version !== CURRENT_VERSION) {
4461
4771
  commands.push([
4462
4772
  "respawn-pane",
4463
4773
  "-k",
@@ -4472,7 +4782,7 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
4472
4782
  cliInvocation: inv,
4473
4783
  vendor
4474
4784
  }))
4475
- ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION2]);
4785
+ ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
4476
4786
  }
4477
4787
  }
4478
4788
  if (commands.length > 0)
@@ -4535,9 +4845,9 @@ async function buildPanesAround(claudePane, args) {
4535
4845
  `).map((line) => line.trim()).filter(Boolean).map((line) => line.split("=", 2)));
4536
4846
  await runTmuxSequence([
4537
4847
  ...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, "@kobe_role", "tasks"]] : [],
4538
- ...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, PANE_VERSION_OPTION, CURRENT_VERSION2]] : [],
4848
+ ...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, PANE_VERSION_OPTION, CURRENT_VERSION]] : [],
4539
4849
  ...ids.ops ? [["set-option", "-p", "-t", ids.ops, "@kobe_role", "ops"]] : [],
4540
- ...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION2]] : []
4850
+ ...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION]] : []
4541
4851
  ]);
4542
4852
  }
4543
4853
  async function newChatTab(session, vendorOverride) {
@@ -4588,6 +4898,16 @@ async function openNewTaskTab(session, defaultRepo) {
4588
4898
  const command = `${envPrefix}${inv.map(shellQuote).join(" ")} new-task${repoArg}`;
4589
4899
  await newWindow(session, { cwd, command, name: "new task" });
4590
4900
  }
4901
+ async function openUpdateTab(session) {
4902
+ if (!await sessionExists(session))
4903
+ return;
4904
+ const sessionOptions = await getSessionOptions(session, ["@kobe_worktree"]);
4905
+ const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
4906
+ const inv = kobeCliInvocation();
4907
+ const envPrefix = inheritedEnvPrefix();
4908
+ const command = `${envPrefix}${updatePageCommand({ cliInvocation: inv })}`;
4909
+ await newWindow(session, { cwd, command, name: "update" });
4910
+ }
4591
4911
  async function rememberSessionVendor(session, taskId, vendor) {
4592
4912
  await setSessionOption(session, "@kobe_vendor", vendor);
4593
4913
  if (!taskId)
@@ -4617,7 +4937,9 @@ var CHAT_TAB_SWITCH_BINDINGS, CHAT_TAB_CLOSE_BINDING, CHAT_TAB_RENAME_BINDING, C
4617
4937
  var init_tmux = __esm(() => {
4618
4938
  init_invocation();
4619
4939
  init_interactive_command();
4940
+ init_env();
4620
4941
  init_client2();
4942
+ init_prompt_delivery();
4621
4943
  init_vendor();
4622
4944
  init_version();
4623
4945
  init_client2();
@@ -4653,6 +4975,43 @@ var init_tmux = __esm(() => {
4653
4975
  ensureSessionLocks = new Map;
4654
4976
  });
4655
4977
 
4978
+ // src/state/repo-init.ts
4979
+ var exports_repo_init = {};
4980
+ __export(exports_repo_init, {
4981
+ resolveRepoInit: () => resolveRepoInit
4982
+ });
4983
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
4984
+ import { join as join4 } from "path";
4985
+ function repoFileScript(worktreePath) {
4986
+ return existsSync2(join4(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
4987
+ }
4988
+ function repoFilePrompt(worktreePath) {
4989
+ const p = join4(worktreePath, INIT_PROMPT_REL);
4990
+ if (!existsSync2(p))
4991
+ return;
4992
+ try {
4993
+ const text = readFileSync3(p, "utf8");
4994
+ return text.trim().length > 0 ? text : undefined;
4995
+ } catch {
4996
+ return;
4997
+ }
4998
+ }
4999
+ function resolveRepoInit(repoRoot, worktreePath) {
5000
+ const override = repoRoot ? getRepoInitOverride(repoRoot) : {};
5001
+ const initScript = repoFileScript(worktreePath) ?? override.initScript;
5002
+ const initPrompt = repoFilePrompt(worktreePath) ?? override.initPrompt;
5003
+ return {
5004
+ initScript: initScript && initScript.trim().length > 0 ? initScript : undefined,
5005
+ initPrompt: initPrompt && initPrompt.trim().length > 0 ? initPrompt : undefined
5006
+ };
5007
+ }
5008
+ var INIT_SCRIPT_REL, INIT_PROMPT_REL;
5009
+ var init_repo_init = __esm(() => {
5010
+ init_repos();
5011
+ INIT_SCRIPT_REL = join4(".kobe", "init.sh");
5012
+ INIT_PROMPT_REL = join4(".kobe", "init-prompt.md");
5013
+ });
5014
+
4656
5015
  // src/tui/panes/sidebar/worktree-changes.ts
4657
5016
  var exports_worktree_changes = {};
4658
5017
  __export(exports_worktree_changes, {
@@ -4708,7 +5067,7 @@ __export(exports_api_cmd, {
4708
5067
  ApiError: () => ApiError,
4709
5068
  API_VERBS: () => API_VERBS
4710
5069
  });
4711
- import { resolve as resolve3 } from "path";
5070
+ import { resolve as resolve4 } from "path";
4712
5071
  function parseFlags(argv) {
4713
5072
  const flags = new Map;
4714
5073
  let pretty = false;
@@ -4822,29 +5181,6 @@ function fail(message, code, exitCode = 1) {
4822
5181
  `);
4823
5182
  process.exit(exitCode);
4824
5183
  }
4825
- async function waitForEnginePane(session, fresh) {
4826
- let prev = null;
4827
- for (let attempt = 0;attempt < 24; attempt++) {
4828
- const pane2 = await claudePaneIdStrict(session);
4829
- if (pane2) {
4830
- if (!fresh)
4831
- return { pane: pane2, ready: true };
4832
- const cur = (await capturePaneById(pane2)).trim();
4833
- if (cur.length > 0 && cur === prev)
4834
- return { pane: pane2, ready: true };
4835
- prev = cur;
4836
- }
4837
- await sleep(250);
4838
- }
4839
- const pane = await claudePaneIdStrict(session) || await claudePaneId(session);
4840
- return { pane, ready: false };
4841
- }
4842
- async function pasteAndSubmit(pane, text) {
4843
- const buffer = `kobe-api-${pane.replace(/[^A-Za-z0-9]/g, "")}`;
4844
- await runTmux(["set-buffer", "-b", buffer, "--", text]);
4845
- await runTmux(["paste-buffer", "-p", "-d", "-b", buffer, "-t", pane]);
4846
- await sendKeyName(pane, "Enter");
4847
- }
4848
5184
  async function deliverPrompt(client, target, prompt) {
4849
5185
  let worktree = target.worktreePath;
4850
5186
  if (!worktree) {
@@ -4857,12 +5193,15 @@ async function deliverPrompt(client, target, prompt) {
4857
5193
  const existed = await sessionExists(session);
4858
5194
  if (!existed) {
4859
5195
  const { ensureSession: ensureSession2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
5196
+ const { resolveRepoInit: resolveRepoInit2 } = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
5197
+ const init = resolveRepoInit2(target.repo ?? "", worktree);
4860
5198
  const ok = await ensureSession2({
4861
5199
  name: session,
4862
5200
  cwd: worktree,
4863
5201
  command: interactiveEngineCommand(target.vendor),
4864
5202
  taskId: target.id,
4865
- vendor: target.vendor
5203
+ vendor: target.vendor,
5204
+ initScript: init.initScript
4866
5205
  });
4867
5206
  if (!ok)
4868
5207
  throw new ApiError(`failed to start tmux session for ${target.id}`, "SESSION_FAILED");
@@ -4902,7 +5241,12 @@ async function spawnTask(client, parsed) {
4902
5241
  if (!prompt) {
4903
5242
  return { taskId: res.taskId, task: res.task, started: false };
4904
5243
  }
4905
- const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor: res.task.vendor }, prompt);
5244
+ const delivered = await deliverPrompt(client, {
5245
+ id: res.taskId,
5246
+ worktreePath: res.task.worktreePath,
5247
+ vendor: res.task.vendor,
5248
+ repo: res.task.repo
5249
+ }, prompt);
4906
5250
  return {
4907
5251
  taskId: res.taskId,
4908
5252
  task: res.task,
@@ -4923,7 +5267,12 @@ async function send(client, parsed) {
4923
5267
  taskId = active;
4924
5268
  }
4925
5269
  const res = await client.request("task.get", { taskId });
4926
- const delivered = await deliverPrompt(client, { id: taskId, worktreePath: res.task.worktreePath, vendor: res.task.vendor }, prompt);
5270
+ const delivered = await deliverPrompt(client, {
5271
+ id: taskId,
5272
+ worktreePath: res.task.worktreePath,
5273
+ vendor: res.task.vendor,
5274
+ repo: res.task.repo
5275
+ }, prompt);
4927
5276
  return {
4928
5277
  ok: true,
4929
5278
  taskId,
@@ -4960,7 +5309,7 @@ async function fanOut(client, parsed) {
4960
5309
  if (baseRef)
4961
5310
  payload.baseRef = baseRef;
4962
5311
  const res = await client.request("task.create", payload);
4963
- const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor }, prompt);
5312
+ const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor, repo: res.task.repo }, prompt);
4964
5313
  tasks.push({
4965
5314
  taskId: res.taskId,
4966
5315
  vendor,
@@ -4980,7 +5329,7 @@ async function collect(client, parsed) {
4980
5329
  taskIds = idsFlag.split(",").map((s) => s.trim()).filter(Boolean);
4981
5330
  } else if (repoFlag) {
4982
5331
  const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
4983
- const target = resolveRepoRoot2(resolve3(process.cwd(), repoFlag));
5332
+ const target = resolveRepoRoot2(resolve4(process.cwd(), repoFlag));
4984
5333
  const { tasks } = await client.request("task.list");
4985
5334
  taskIds = tasks.filter((t) => !t.archived && resolveRepoRoot2(t.repo) === target).map((t) => t.id);
4986
5335
  } else {
@@ -5064,11 +5413,12 @@ ${apiUsage()}`, "BAD_VERB", 2);
5064
5413
  client.close();
5065
5414
  }
5066
5415
  }
5067
- var API_VERBS, FANOUT_CAP = 10, ApiError, sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
5416
+ var API_VERBS, FANOUT_CAP = 10, ApiError;
5068
5417
  var init_api_cmd = __esm(() => {
5069
5418
  init_daemon_process();
5070
5419
  init_interactive_command();
5071
5420
  init_client2();
5421
+ init_prompt_delivery();
5072
5422
  init_vendor();
5073
5423
  API_VERBS = ["spawn-task", "fan-out", "send", "get-task", "collect", "list"];
5074
5424
  ApiError = class ApiError extends Error {
@@ -5148,7 +5498,7 @@ async function runUpdateSubcommand(args, deps) {
5148
5498
  return;
5149
5499
  }
5150
5500
  const plan = updatePlan();
5151
- io.stdout.write(`kobe ${CURRENT_VERSION2} -> latest
5501
+ io.stdout.write(`kobe ${CURRENT_VERSION} -> latest
5152
5502
  `);
5153
5503
  io.stdout.write(`running: ${plan.display}
5154
5504
  `);
@@ -5217,10 +5567,10 @@ function validateTheme(value) {
5217
5567
  var init_schema = () => {};
5218
5568
 
5219
5569
  // src/tui/context/theme/loader.ts
5220
- import { readFileSync as readFileSync2, readdirSync } from "fs";
5221
- import { join as join4 } from "path";
5570
+ import { readFileSync as readFileSync4, readdirSync } from "fs";
5571
+ import { join as join5 } from "path";
5222
5572
  function userThemesDir() {
5223
- return join4(kobeStateDir(), "themes");
5573
+ return join5(kobeStateDir(), "themes");
5224
5574
  }
5225
5575
  function loadUserThemes() {
5226
5576
  const dir = userThemesDir();
@@ -5234,10 +5584,10 @@ function loadUserThemes() {
5234
5584
  for (const file of entries) {
5235
5585
  if (!file.endsWith(".json"))
5236
5586
  continue;
5237
- const path3 = join4(dir, file);
5587
+ const path3 = join5(dir, file);
5238
5588
  let parsed;
5239
5589
  try {
5240
- const text = readFileSync2(path3, "utf8");
5590
+ const text = readFileSync4(path3, "utf8");
5241
5591
  parsed = JSON.parse(text);
5242
5592
  } catch (err) {
5243
5593
  const msg = err instanceof Error ? err.message : String(err);
@@ -5264,8 +5614,8 @@ var exports_theme = {};
5264
5614
  __export(exports_theme, {
5265
5615
  runThemeSubcommand: () => runThemeSubcommand
5266
5616
  });
5267
- import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5268
- import { basename as basename3, join as join5, resolve as resolve4 } from "path";
5617
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5618
+ import { basename as basename3, join as join6, resolve as resolve5 } from "path";
5269
5619
  function fail2(message) {
5270
5620
  process.stderr.write(`kobe theme: ${message}
5271
5621
  `);
@@ -5296,7 +5646,7 @@ function listThemes() {
5296
5646
  } else {
5297
5647
  for (const f of userFiles) {
5298
5648
  const name = f.slice(0, -".json".length);
5299
- const path3 = join5(dir, f);
5649
+ const path3 = join6(dir, f);
5300
5650
  const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
5301
5651
  lines.push(` ${name}${overridesBundled} ${path3}`);
5302
5652
  }
@@ -5322,10 +5672,10 @@ async function readSource(source) {
5322
5672
  const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
5323
5673
  return { text: text2, defaultName: defaultName2 };
5324
5674
  }
5325
- const abs = resolve4(process.cwd(), source);
5675
+ const abs = resolve5(process.cwd(), source);
5326
5676
  let text;
5327
5677
  try {
5328
- text = readFileSync3(abs, "utf8");
5678
+ text = readFileSync5(abs, "utf8");
5329
5679
  } catch (err) {
5330
5680
  fail2(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
5331
5681
  }
@@ -5386,8 +5736,8 @@ async function addTheme(args) {
5386
5736
  }
5387
5737
  const dir = userThemesDir();
5388
5738
  mkdirSync3(dir, { recursive: true });
5389
- const dest = join5(dir, `${name}.json`);
5390
- if (existsSync2(dest) && !opts.force) {
5739
+ const dest = join6(dir, `${name}.json`);
5740
+ if (existsSync3(dest) && !opts.force) {
5391
5741
  fail2(`${dest} already exists (pass --force to overwrite)`);
5392
5742
  }
5393
5743
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
@@ -5404,8 +5754,8 @@ function removeTheme(args) {
5404
5754
  if (BUNDLED_NAMES.includes(name)) {
5405
5755
  fail2(`"${name}" is a built-in theme and cannot be removed`);
5406
5756
  }
5407
- const dest = join5(userThemesDir(), `${name}.json`);
5408
- if (!existsSync2(dest)) {
5757
+ const dest = join6(userThemesDir(), `${name}.json`);
5758
+ if (!existsSync3(dest)) {
5409
5759
  fail2(`no user theme named "${name}" (looked for ${dest})`);
5410
5760
  }
5411
5761
  unlinkSync(dest);
@@ -5588,16 +5938,16 @@ var init_daemon_cmd = __esm(() => {
5588
5938
  });
5589
5939
 
5590
5940
  // src/lib/skill-install.ts
5591
- import { existsSync as existsSync3 } from "fs";
5941
+ import { existsSync as existsSync4 } from "fs";
5592
5942
  import { homedir as homedir5 } from "os";
5593
- import { join as join6 } from "path";
5943
+ import { join as join7 } from "path";
5594
5944
  function kobeSkillPaths(opts = {}) {
5595
5945
  const home = opts.home ?? homedir5();
5596
5946
  const cwd = opts.cwd ?? process.cwd();
5597
- return [join6(home, SKILL_REL_PATH), join6(cwd, SKILL_REL_PATH)];
5947
+ return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
5598
5948
  }
5599
5949
  function isKobeSkillInstalled(opts) {
5600
- return kobeSkillPaths(opts).some((p) => existsSync3(p));
5950
+ return kobeSkillPaths(opts).some((p) => existsSync4(p));
5601
5951
  }
5602
5952
  function maybeHintSkillInstall() {
5603
5953
  if (isKobeSkillInstalled())
@@ -5623,9 +5973,9 @@ __export(exports_maintenance, {
5623
5973
  runResetSubcommand: () => runResetSubcommand,
5624
5974
  runDoctorSubcommand: () => runDoctorSubcommand
5625
5975
  });
5626
- import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "fs";
5976
+ import { existsSync as existsSync5, readFileSync as readFileSync6, statSync } from "fs";
5627
5977
  import { unlink as unlink4 } from "fs/promises";
5628
- import { join as join7 } from "path";
5978
+ import { join as join8 } from "path";
5629
5979
  import { createInterface } from "readline";
5630
5980
  function isProcessAlive2(pid) {
5631
5981
  try {
@@ -5672,7 +6022,7 @@ function describeFile(path3) {
5672
6022
  }
5673
6023
  function taskCount(tasksPath) {
5674
6024
  try {
5675
- const parsed = JSON.parse(readFileSync4(tasksPath, "utf8"));
6025
+ const parsed = JSON.parse(readFileSync6(tasksPath, "utf8"));
5676
6026
  return Array.isArray(parsed.tasks) ? parsed.tasks.length : null;
5677
6027
  } catch {
5678
6028
  return null;
@@ -5680,7 +6030,7 @@ function taskCount(tasksPath) {
5680
6030
  }
5681
6031
  function tailFile(path3, n) {
5682
6032
  try {
5683
- const lines = readFileSync4(path3, "utf8").split(`
6033
+ const lines = readFileSync6(path3, "utf8").split(`
5684
6034
  `).filter((l) => l.trim().length > 0);
5685
6035
  return lines.slice(-n).join(`
5686
6036
  `);
@@ -5722,7 +6072,7 @@ async function runDoctorSubcommand(argv = []) {
5722
6072
  const socketPath = defaultDaemonSocketPath();
5723
6073
  const pidPath = defaultDaemonPidPath();
5724
6074
  const logPath = defaultDaemonLogPath();
5725
- const tasksPath = join7(kobeStateDir(), "tasks.json");
6075
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
5726
6076
  const statePath2 = kvStatePath();
5727
6077
  const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
5728
6078
  const status = await probeDaemonStatus(socketPath);
@@ -5743,7 +6093,7 @@ async function runDoctorSubcommand(argv = []) {
5743
6093
  } else {
5744
6094
  out.push("daemon: \u2717 not running (no pidfile)");
5745
6095
  }
5746
- if (existsSync4(socketPath))
6096
+ if (existsSync5(socketPath))
5747
6097
  out.push(` orphan socket file present: ${socketPath}`);
5748
6098
  const tail = tailFile(logPath, 8);
5749
6099
  if (tail) {
@@ -5777,7 +6127,7 @@ async function runDoctorSubcommand(argv = []) {
5777
6127
  async function confirmTty(prompt) {
5778
6128
  const rl = createInterface({ input: process.stdin, output: process.stdout });
5779
6129
  try {
5780
- const answer = await new Promise((resolve5) => rl.question(prompt, resolve5));
6130
+ const answer = await new Promise((resolve6) => rl.question(prompt, resolve6));
5781
6131
  return /^y(es)?$/i.test(answer.trim());
5782
6132
  } finally {
5783
6133
  rl.close();
@@ -5829,7 +6179,7 @@ async function runResetSubcommand(argv) {
5829
6179
  const yes = argv.includes("--yes") || argv.includes("-y");
5830
6180
  const socketPath = defaultDaemonSocketPath();
5831
6181
  const pidPath = defaultDaemonPidPath();
5832
- const tasksPath = join7(kobeStateDir(), "tasks.json");
6182
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
5833
6183
  const statePath2 = kvStatePath();
5834
6184
  console.log("kobe reset will:");
5835
6185
  console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
@@ -9004,7 +9354,7 @@ function addTheme2(name, theme) {
9004
9354
  }
9005
9355
  function resolveTheme(theme, mode = "dark") {
9006
9356
  const defs = theme.defs ?? {};
9007
- function resolve5(c, chain = []) {
9357
+ function resolve6(c, chain = []) {
9008
9358
  if (typeof c === "string") {
9009
9359
  if (c === "transparent" || c === "none")
9010
9360
  return RGBA.fromInts(0, 0, 0, 0);
@@ -9016,13 +9366,13 @@ function resolveTheme(theme, mode = "dark") {
9016
9366
  const next = defs[c] ?? theme.theme[c];
9017
9367
  if (next === undefined)
9018
9368
  return RGBA.fromInts(0, 0, 0);
9019
- return resolve5(next, [...chain, c]);
9369
+ return resolve6(next, [...chain, c]);
9020
9370
  }
9021
- return resolve5(c[mode], chain);
9371
+ return resolve6(c[mode], chain);
9022
9372
  }
9023
9373
  const out = {};
9024
9374
  for (const [k, v] of Object.entries(theme.theme)) {
9025
- out[k] = resolve5(v);
9375
+ out[k] = resolve6(v);
9026
9376
  }
9027
9377
  const text = out.text ?? RGBA.fromHex("#ffffff");
9028
9378
  const background = out.background ?? RGBA.fromHex("#000000");
@@ -9793,7 +10143,7 @@ function findAvailableFolderName(parentDir, base) {
9793
10143
  return trimmed;
9794
10144
  }
9795
10145
  function cloneRepo(url, target, onProgress) {
9796
- return new Promise((resolve5) => {
10146
+ return new Promise((resolve6) => {
9797
10147
  let stderrBuf = "";
9798
10148
  try {
9799
10149
  const child = spawn2("git", ["clone", "--progress", url, target], {
@@ -9810,18 +10160,18 @@ function cloneRepo(url, target, onProgress) {
9810
10160
  }
9811
10161
  });
9812
10162
  child.on("error", (err) => {
9813
- resolve5({ ok: false, error: err.message });
10163
+ resolve6({ ok: false, error: err.message });
9814
10164
  });
9815
10165
  child.on("close", (code) => {
9816
10166
  if (code === 0) {
9817
- resolve5({ ok: true, path: target });
10167
+ resolve6({ ok: true, path: target });
9818
10168
  return;
9819
10169
  }
9820
10170
  const tail = stderrBuf.split(/[\r\n]+/).filter((s) => s.trim().length > 0).pop() ?? `git clone exited with ${code}`;
9821
- resolve5({ ok: false, error: tail });
10171
+ resolve6({ ok: false, error: tail });
9822
10172
  });
9823
10173
  } catch (err) {
9824
- resolve5({ ok: false, error: err instanceof Error ? err.message : String(err) });
10174
+ resolve6({ ok: false, error: err instanceof Error ? err.message : String(err) });
9825
10175
  }
9826
10176
  });
9827
10177
  }
@@ -10884,7 +11234,7 @@ var init_dialog2 = __esm(() => {
10884
11234
 
10885
11235
  // src/tui/component/new-task-dialog/index.tsx
10886
11236
  function show(dialog, defaultRepo, savedRepos, options) {
10887
- return new Promise((resolve5) => {
11237
+ return new Promise((resolve6) => {
10888
11238
  dialog.replace(() => createComponent2(NewTaskDialogView, {
10889
11239
  defaultRepo,
10890
11240
  savedRepos,
@@ -10897,9 +11247,9 @@ function show(dialog, defaultRepo, savedRepos, options) {
10897
11247
  get discoverAdoptable() {
10898
11248
  return options?.discoverAdoptable;
10899
11249
  },
10900
- onSubmit: (v) => resolve5(v),
10901
- onCancel: () => resolve5(undefined)
10902
- }), () => resolve5(undefined));
11250
+ onSubmit: (v) => resolve6(v),
11251
+ onCancel: () => resolve6(undefined)
11252
+ }), () => resolve6(undefined));
10903
11253
  dialog.setSize("medium");
10904
11254
  });
10905
11255
  }
@@ -10991,15 +11341,15 @@ var init_dialog3 = __esm(() => {
10991
11341
 
10992
11342
  // src/tui/component/rename-task-dialog/index.tsx
10993
11343
  function show2(dialog, currentTitle, opts = {}) {
10994
- return new Promise((resolve5) => {
11344
+ return new Promise((resolve6) => {
10995
11345
  dialog.replace(() => createComponent2(RenameTaskDialogView, {
10996
11346
  currentTitle,
10997
11347
  get dialogTitle() {
10998
11348
  return opts.dialogTitle;
10999
11349
  },
11000
- onSubmit: (v) => resolve5(v),
11001
- onCancel: () => resolve5(undefined)
11002
- }), () => resolve5(undefined));
11350
+ onSubmit: (v) => resolve6(v),
11351
+ onCancel: () => resolve6(undefined)
11352
+ }), () => resolve6(undefined));
11003
11353
  });
11004
11354
  }
11005
11355
  var RenameTaskDialog;
@@ -11013,7 +11363,7 @@ var init_rename_task_dialog = __esm(() => {
11013
11363
 
11014
11364
  // src/engine/claude-code-local/binary.ts
11015
11365
  import { spawnSync as spawnSync5 } from "child_process";
11016
- import { existsSync as existsSync6, statSync as statSync3 } from "fs";
11366
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
11017
11367
  import { homedir as homedir7 } from "os";
11018
11368
  import path4 from "path";
11019
11369
  async function findClaudeBinary(deps = defaultDeps) {
@@ -11100,7 +11450,7 @@ var init_binary = __esm(() => {
11100
11450
  return;
11101
11451
  if (first.startsWith("claude:") && first.includes("aliased to")) {
11102
11452
  const aliasTarget = first.split("aliased to")[1]?.trim();
11103
- return aliasTarget && existsSync6(aliasTarget) ? aliasTarget : undefined;
11453
+ return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
11104
11454
  }
11105
11455
  return first;
11106
11456
  },
@@ -11117,7 +11467,7 @@ var init_binary = __esm(() => {
11117
11467
 
11118
11468
  // src/engine/codex-local/binary.ts
11119
11469
  import { spawnSync as spawnSync6 } from "child_process";
11120
- import { existsSync as existsSync7, statSync as statSync4 } from "fs";
11470
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
11121
11471
  import { homedir as homedir8 } from "os";
11122
11472
  import path5 from "path";
11123
11473
  async function findCodexBinary(deps = defaultDeps2) {
@@ -11188,7 +11538,7 @@ var init_binary2 = __esm(() => {
11188
11538
  return;
11189
11539
  if (first.startsWith("codex:") && first.includes("aliased to")) {
11190
11540
  const aliasTarget = first.split("aliased to")[1]?.trim();
11191
- return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
11541
+ return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
11192
11542
  }
11193
11543
  return first;
11194
11544
  },
@@ -11205,7 +11555,7 @@ var init_binary2 = __esm(() => {
11205
11555
 
11206
11556
  // src/engine/copilot-local/binary.ts
11207
11557
  import { spawnSync as spawnSync7 } from "child_process";
11208
- import { existsSync as existsSync8, statSync as statSync5 } from "fs";
11558
+ import { existsSync as existsSync9, statSync as statSync5 } from "fs";
11209
11559
  import { homedir as homedir9 } from "os";
11210
11560
  import path6 from "path";
11211
11561
  async function findCopilotBinary(deps = defaultDeps3) {
@@ -11300,7 +11650,7 @@ var init_binary3 = __esm(() => {
11300
11650
  return;
11301
11651
  if (first.startsWith("copilot:") && first.includes("aliased to")) {
11302
11652
  const aliasTarget = first.split("aliased to")[1]?.trim();
11303
- return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
11653
+ return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
11304
11654
  }
11305
11655
  return first;
11306
11656
  },
@@ -11311,7 +11661,7 @@ var init_binary3 = __esm(() => {
11311
11661
  });
11312
11662
 
11313
11663
  // src/engine/account-detect.ts
11314
- import { readFileSync as readFileSync5, statSync as statSync6 } from "fs";
11664
+ import { readFileSync as readFileSync7, statSync as statSync6 } from "fs";
11315
11665
  import { homedir as homedir10 } from "os";
11316
11666
  import path7 from "path";
11317
11667
  function claudeGlobalConfigPath(env, home) {
@@ -11524,7 +11874,7 @@ var init_account_detect = __esm(() => {
11524
11874
  return null;
11525
11875
  throw err;
11526
11876
  }
11527
- return readFileSync5(p, "utf8");
11877
+ return readFileSync7(p, "utf8");
11528
11878
  },
11529
11879
  env(name) {
11530
11880
  return process.env[name];
@@ -11660,18 +12010,18 @@ var init_dialog_confirm = __esm(() => {
11660
12010
  init_keymap();
11661
12011
  init_dialog();
11662
12012
  DialogConfirm.show = (dialog, title, message, label, confirmLabel, options) => {
11663
- return new Promise((resolve5) => {
12013
+ return new Promise((resolve6) => {
11664
12014
  dialog.replace(() => createComponent2(DialogConfirm, {
11665
12015
  title,
11666
12016
  message,
11667
- onConfirm: () => resolve5(true),
11668
- onCancel: () => resolve5(false),
12017
+ onConfirm: () => resolve6(true),
12018
+ onCancel: () => resolve6(false),
11669
12019
  label,
11670
12020
  confirmLabel,
11671
12021
  get initialActive() {
11672
12022
  return options?.initialActive;
11673
12023
  }
11674
- }), () => resolve5(undefined));
12024
+ }), () => resolve6(undefined));
11675
12025
  dialog.setSize("small");
11676
12026
  });
11677
12027
  };
@@ -11679,7 +12029,7 @@ var init_dialog_confirm = __esm(() => {
11679
12029
 
11680
12030
  // src/tui/component/settings-dialog/actions.ts
11681
12031
  import { unlinkSync as unlinkSync2 } from "fs";
11682
- import { join as join9 } from "path";
12032
+ import { join as join10 } from "path";
11683
12033
  function hasRestartableDaemon(orchestrator) {
11684
12034
  return orchestrator instanceof RemoteOrchestrator;
11685
12035
  }
@@ -11696,7 +12046,7 @@ async function confirmResetState(dialog, kv, renderer) {
11696
12046
  return;
11697
12047
  kv.clear();
11698
12048
  try {
11699
- unlinkSync2(join9(homeDir(), ".kobe", "tasks.json"));
12049
+ unlinkSync2(join10(homeDir(), ".kobe", "tasks.json"));
11700
12050
  } catch (err) {
11701
12051
  if (err.code !== "ENOENT") {
11702
12052
  console.error("kobe: failed to delete tasks.json during reset:", err);
@@ -12853,12 +13203,12 @@ var init_settings_dialog = __esm(() => {
12853
13203
  init_model();
12854
13204
  init_sections();
12855
13205
  SettingsDialog.show = (dialog, kv, orchestrator) => {
12856
- return new Promise((resolve5) => {
13206
+ return new Promise((resolve6) => {
12857
13207
  dialog.replace(() => createComponent2(SettingsDialog, {
12858
13208
  kv,
12859
13209
  orchestrator,
12860
- onClose: () => resolve5()
12861
- }), () => resolve5());
13210
+ onClose: () => resolve6()
13211
+ }), () => resolve6());
12862
13212
  });
12863
13213
  };
12864
13214
  });
@@ -12925,12 +13275,12 @@ var init_focus = __esm(() => {
12925
13275
  });
12926
13276
 
12927
13277
  // src/tui/context/kv.tsx
12928
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
13278
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
12929
13279
  import { dirname as dirname5 } from "path";
12930
13280
  function loadInitial() {
12931
13281
  const statePath2 = kvStatePath();
12932
13282
  try {
12933
- const text = readFileSync6(statePath2, "utf8");
13283
+ const text = readFileSync8(statePath2, "utf8");
12934
13284
  const parsed = JSON.parse(text);
12935
13285
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
12936
13286
  return parsed;
@@ -13014,10 +13364,10 @@ var init_kv = __esm(() => {
13014
13364
  });
13015
13365
 
13016
13366
  // src/tui/lib/persisted-ui-prefs.ts
13017
- import { readFileSync as readFileSync7 } from "fs";
13367
+ import { readFileSync as readFileSync9 } from "fs";
13018
13368
  function readPersistedUiPrefs(fallbackTheme) {
13019
13369
  try {
13020
- const parsed = JSON.parse(readFileSync7(kvStatePath(), "utf8"));
13370
+ const parsed = JSON.parse(readFileSync9(kvStatePath(), "utf8"));
13021
13371
  const theme = typeof parsed.activeTheme === "string" && hasTheme(parsed.activeTheme) ? parsed.activeTheme : fallbackTheme;
13022
13372
  const transparent = parsed.transparentBackground === true;
13023
13373
  const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent) ? parsed.focusAccent : null;
@@ -13033,8 +13383,8 @@ var init_persisted_ui_prefs = __esm(() => {
13033
13383
 
13034
13384
  // src/tui/lib/worktree-opener.ts
13035
13385
  import { spawn as spawn3 } from "child_process";
13036
- import { existsSync as existsSync9 } from "fs";
13037
- import { basename as basename4, delimiter, isAbsolute, join as join10 } from "path";
13386
+ import { existsSync as existsSync10 } from "fs";
13387
+ import { basename as basename4, delimiter, isAbsolute, join as join11 } from "path";
13038
13388
  function executableOnPath(command, env, exists) {
13039
13389
  if (isAbsolute(command))
13040
13390
  return exists(command);
@@ -13042,7 +13392,7 @@ function executableOnPath(command, env, exists) {
13042
13392
  for (const dir of pathEnv.split(delimiter)) {
13043
13393
  if (!dir)
13044
13394
  continue;
13045
- if (exists(join10(dir, command)))
13395
+ if (exists(join11(dir, command)))
13046
13396
  return true;
13047
13397
  }
13048
13398
  return false;
@@ -13062,7 +13412,7 @@ function labelForOverride(command) {
13062
13412
  function detectWorktreeOpener(deps = {}) {
13063
13413
  const env = deps.env ?? process.env;
13064
13414
  const platform = deps.platform ?? process.platform;
13065
- const exists = deps.exists ?? existsSync9;
13415
+ const exists = deps.exists ?? existsSync10;
13066
13416
  const override = env.KOBE_OPEN_EDITOR?.trim();
13067
13417
  if (override) {
13068
13418
  return { id: "env", label: labelForOverride(override), command: override, args: [] };
@@ -14563,7 +14913,7 @@ var exports_host = {};
14563
14913
  __export(exports_host, {
14564
14914
  startTasksPane: () => startTasksPane
14565
14915
  });
14566
- import { existsSync as existsSync10 } from "fs";
14916
+ import { existsSync as existsSync11 } from "fs";
14567
14917
  import { TextAttributes as TextAttributes9 } from "@opentui/core";
14568
14918
  function TasksShell(props) {
14569
14919
  const themeCtx = useTheme();
@@ -14654,6 +15004,15 @@ function TasksShell(props) {
14654
15004
  }
14655
15005
  SettingsDialog.show(dialog, kv, props.orch ?? undefined);
14656
15006
  }
15007
+ async function openUpdate() {
15008
+ const info = updateInfo();
15009
+ if (!info?.hasUpdate)
15010
+ return;
15011
+ const session = await currentSessionName();
15012
+ if (!session)
15013
+ return;
15014
+ await openUpdateTab(session);
15015
+ }
14657
15016
  async function archiveTask(id) {
14658
15017
  const task = props.tasks().find((t) => t.id === id);
14659
15018
  if (!task || !props.orch)
@@ -14662,6 +15021,10 @@ function TasksShell(props) {
14662
15021
  try {
14663
15022
  await props.orch.setArchived(id, nextArchived);
14664
15023
  if (nextArchived) {
15024
+ const nextTask = props.tasks().find((t) => t.id !== id && !t.archived);
15025
+ await switchClientBeforeKill(tmuxSessionName(id), nextTask ? tmuxSessionName(nextTask.id) : undefined).catch((err) => {
15026
+ console.error("[kobe tasks] switch-client failed:", err);
15027
+ });
14665
15028
  await killSession(tmuxSessionName(id)).catch((err) => {
14666
15029
  console.error("[kobe tasks] kill tmux session failed:", err);
14667
15030
  });
@@ -14703,6 +15066,10 @@ function TasksShell(props) {
14703
15066
  }
14704
15067
  if (!deleted)
14705
15068
  return;
15069
+ const nextTask = props.tasks().find((t) => t.id !== id && !t.archived);
15070
+ await switchClientBeforeKill(tmuxSessionName(id), nextTask ? tmuxSessionName(nextTask.id) : undefined).catch((err) => {
15071
+ console.error("[kobe tasks] switch-client failed:", err);
15072
+ });
14706
15073
  await killSession(tmuxSessionName(id)).catch((err) => {
14707
15074
  console.error("[kobe tasks] kill tmux session failed:", err);
14708
15075
  });
@@ -14760,7 +15127,7 @@ function TasksShell(props) {
14760
15127
  async function openSelectedWorktree(id) {
14761
15128
  const task = props.tasks().find((t) => t.id === id);
14762
15129
  let worktree = task?.worktreePath;
14763
- if (!worktree || !existsSync10(worktree)) {
15130
+ if (!worktree || !existsSync11(worktree)) {
14764
15131
  if (!props.orch) {
14765
15132
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
14766
15133
  return;
@@ -14773,7 +15140,7 @@ function TasksShell(props) {
14773
15140
  }
14774
15141
  await props.reload();
14775
15142
  }
14776
- if (!worktree || !existsSync10(worktree))
15143
+ if (!worktree || !existsSync11(worktree))
14777
15144
  return;
14778
15145
  const opener = detectWorktreeOpener();
14779
15146
  if (!opener) {
@@ -14792,6 +15159,9 @@ function TasksShell(props) {
14792
15159
  }, {
14793
15160
  key: "s",
14794
15161
  cmd: () => void openSettings()
15162
+ }, {
15163
+ key: "u",
15164
+ cmd: () => void openUpdate()
14795
15165
  }, {
14796
15166
  key: "o",
14797
15167
  cmd: () => {
@@ -14821,7 +15191,7 @@ function TasksShell(props) {
14821
15191
  const exists = await sessionExists(name);
14822
15192
  if (exists) {
14823
15193
  const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
14824
- if (cwd2 && existsSync10(cwd2)) {
15194
+ if (cwd2 && existsSync11(cwd2)) {
14825
15195
  await ensureSession({
14826
15196
  name,
14827
15197
  cwd: cwd2,
@@ -14835,7 +15205,7 @@ function TasksShell(props) {
14835
15205
  return;
14836
15206
  }
14837
15207
  let cwd = task?.worktreePath;
14838
- if (!cwd || !existsSync10(cwd)) {
15208
+ if (!cwd || !existsSync11(cwd)) {
14839
15209
  if (!props.orch) {
14840
15210
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
14841
15211
  return;
@@ -14848,14 +15218,17 @@ function TasksShell(props) {
14848
15218
  }
14849
15219
  await props.reload();
14850
15220
  }
14851
- if (!cwd || !existsSync10(cwd))
15221
+ if (!cwd || !existsSync11(cwd))
14852
15222
  return;
15223
+ const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
14853
15224
  const ready = await ensureSession({
14854
15225
  name,
14855
15226
  cwd,
14856
15227
  command: interactiveEngineCommand(task?.vendor),
14857
15228
  taskId: id,
14858
- vendor: task?.vendor
15229
+ vendor: task?.vendor,
15230
+ initScript: init2.initScript,
15231
+ initPrompt: init2.initPrompt
14859
15232
  });
14860
15233
  if (!ready) {
14861
15234
  console.error(`[kobe tasks] failed to start session ${name}`);
@@ -14886,7 +15259,8 @@ function TasksShell(props) {
14886
15259
  focused: () => dialog.stack.length === 0
14887
15260
  }));
14888
15261
  insert(_el$, createComponent2(ShortcutHints, {
14889
- updateInfo
15262
+ updateInfo,
15263
+ onOpenUpdate: () => void openUpdate()
14890
15264
  }), null);
14891
15265
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
14892
15266
  return _el$;
@@ -14899,8 +15273,8 @@ function ShortcutHints(props) {
14899
15273
  const updateLabel = createMemo(() => {
14900
15274
  const info = props.updateInfo();
14901
15275
  if (!info)
14902
- return `v${CURRENT_VERSION2}`;
14903
- return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION2} latest`;
15276
+ return `v${CURRENT_VERSION}`;
15277
+ return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION} latest`;
14904
15278
  });
14905
15279
  const HINTS = [{
14906
15280
  k: "\u23CE",
@@ -14952,10 +15326,10 @@ function ShortcutHints(props) {
14952
15326
  label: "detach"
14953
15327
  }];
14954
15328
  return (() => {
14955
- var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$0 = createElement("text");
15329
+ var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$12 = createElement("text");
14956
15330
  insertNode(_el$3, _el$4);
14957
15331
  insertNode(_el$3, _el$6);
14958
- insertNode(_el$3, _el$0);
15332
+ insertNode(_el$3, _el$12);
14959
15333
  setProp(_el$3, "flexShrink", 0);
14960
15334
  setProp(_el$3, "flexDirection", "column");
14961
15335
  setProp(_el$3, "paddingLeft", 1);
@@ -14968,68 +15342,86 @@ function ShortcutHints(props) {
14968
15342
  setProp(_el$6, "flexDirection", "row");
14969
15343
  setProp(_el$6, "gap", 1);
14970
15344
  setProp(_el$7, "wrapMode", "none");
15345
+ setProp(_el$7, "onMouseUp", () => {
15346
+ if (props.updateInfo()?.hasUpdate)
15347
+ props.onOpenUpdate();
15348
+ });
14971
15349
  insert(_el$7, updateLabel);
14972
15350
  insert(_el$3, createComponent2(Show, {
14973
15351
  get when() {
14974
15352
  return props.updateInfo()?.hasUpdate;
14975
15353
  },
14976
15354
  get children() {
14977
- var _el$8 = createElement("text");
14978
- insertNode(_el$8, createTextNode(`run: kobe update`));
14979
- setProp(_el$8, "wrapMode", "none");
15355
+ var _el$8 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$10 = createElement("text");
15356
+ insertNode(_el$8, _el$9);
15357
+ insertNode(_el$8, _el$10);
15358
+ setProp(_el$8, "flexDirection", "row");
15359
+ setProp(_el$8, "gap", 1);
15360
+ setProp(_el$8, "onMouseUp", () => props.onOpenUpdate());
15361
+ insertNode(_el$9, _el$0);
15362
+ setProp(_el$9, "width", 10);
15363
+ setProp(_el$9, "flexShrink", 0);
15364
+ insertNode(_el$0, createTextNode(`[U]`));
15365
+ setProp(_el$0, "wrapMode", "none");
15366
+ insertNode(_el$10, createTextNode(`update page`));
15367
+ setProp(_el$10, "wrapMode", "none");
14980
15368
  effect((_p$) => {
14981
- var _v$ = theme.accent, _v$2 = TextAttributes9.DIM;
14982
- _v$ !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$, _p$.e));
14983
- _v$2 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$2, _p$.t));
15369
+ var _v$ = theme.accent, _v$2 = TextAttributes9.BOLD, _v$3 = theme.accent, _v$4 = TextAttributes9.DIM;
15370
+ _v$ !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$, _p$.e));
15371
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$2, _p$.t));
15372
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$3, _p$.a));
15373
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$4, _p$.o));
14984
15374
  return _p$;
14985
15375
  }, {
14986
15376
  e: undefined,
14987
- t: undefined
15377
+ t: undefined,
15378
+ a: undefined,
15379
+ o: undefined
14988
15380
  });
14989
15381
  return _el$8;
14990
15382
  }
14991
- }), _el$0);
14992
- insertNode(_el$0, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
14993
- setProp(_el$0, "wrapMode", "none");
15383
+ }), _el$12);
15384
+ insertNode(_el$12, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
15385
+ setProp(_el$12, "wrapMode", "none");
14994
15386
  insert(_el$3, createComponent2(For, {
14995
15387
  each: HINTS,
14996
15388
  children: (h) => (() => {
14997
- var _el$10 = createElement("box"), _el$11 = createElement("box"), _el$12 = createElement("text"), _el$13 = createTextNode(`[`), _el$14 = createTextNode(`]`), _el$15 = createElement("text");
14998
- insertNode(_el$10, _el$11);
14999
- insertNode(_el$10, _el$15);
15000
- setProp(_el$10, "flexDirection", "row");
15001
- setProp(_el$10, "gap", 1);
15002
- insertNode(_el$11, _el$12);
15003
- setProp(_el$11, "width", 10);
15004
- setProp(_el$11, "flexShrink", 0);
15005
- insertNode(_el$12, _el$13);
15006
- insertNode(_el$12, _el$14);
15007
- setProp(_el$12, "wrapMode", "none");
15008
- insert(_el$12, () => h.k, _el$14);
15009
- setProp(_el$15, "wrapMode", "none");
15010
- insert(_el$15, () => h.label);
15389
+ var _el$14 = createElement("box"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createTextNode(`[`), _el$18 = createTextNode(`]`), _el$19 = createElement("text");
15390
+ insertNode(_el$14, _el$15);
15391
+ insertNode(_el$14, _el$19);
15392
+ setProp(_el$14, "flexDirection", "row");
15393
+ setProp(_el$14, "gap", 1);
15394
+ insertNode(_el$15, _el$16);
15395
+ setProp(_el$15, "width", 10);
15396
+ setProp(_el$15, "flexShrink", 0);
15397
+ insertNode(_el$16, _el$17);
15398
+ insertNode(_el$16, _el$18);
15399
+ setProp(_el$16, "wrapMode", "none");
15400
+ insert(_el$16, () => h.k, _el$18);
15401
+ setProp(_el$19, "wrapMode", "none");
15402
+ insert(_el$19, () => h.label);
15011
15403
  effect((_p$) => {
15012
- var _v$9 = theme.accent, _v$0 = TextAttributes9.BOLD, _v$1 = theme.textMuted;
15013
- _v$9 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$9, _p$.e));
15014
- _v$0 !== _p$.t && (_p$.t = setProp(_el$12, "attributes", _v$0, _p$.t));
15015
- _v$1 !== _p$.a && (_p$.a = setProp(_el$15, "fg", _v$1, _p$.a));
15404
+ var _v$1 = theme.accent, _v$10 = TextAttributes9.BOLD, _v$11 = theme.textMuted;
15405
+ _v$1 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$1, _p$.e));
15406
+ _v$10 !== _p$.t && (_p$.t = setProp(_el$16, "attributes", _v$10, _p$.t));
15407
+ _v$11 !== _p$.a && (_p$.a = setProp(_el$19, "fg", _v$11, _p$.a));
15016
15408
  return _p$;
15017
15409
  }, {
15018
15410
  e: undefined,
15019
15411
  t: undefined,
15020
15412
  a: undefined
15021
15413
  });
15022
- return _el$10;
15414
+ return _el$14;
15023
15415
  })()
15024
15416
  }), null);
15025
15417
  effect((_p$) => {
15026
- var _v$3 = theme.textMuted, _v$4 = TextAttributes9.DIM, _v$5 = props.updateInfo()?.hasUpdate ? theme.warning : theme.textMuted, _v$6 = props.updateInfo()?.hasUpdate ? TextAttributes9.BOLD : TextAttributes9.DIM, _v$7 = theme.textMuted, _v$8 = TextAttributes9.DIM;
15027
- _v$3 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$3, _p$.e));
15028
- _v$4 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$4, _p$.t));
15029
- _v$5 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$5, _p$.a));
15030
- _v$6 !== _p$.o && (_p$.o = setProp(_el$7, "attributes", _v$6, _p$.o));
15031
- _v$7 !== _p$.i && (_p$.i = setProp(_el$0, "fg", _v$7, _p$.i));
15032
- _v$8 !== _p$.n && (_p$.n = setProp(_el$0, "attributes", _v$8, _p$.n));
15418
+ var _v$5 = theme.textMuted, _v$6 = TextAttributes9.DIM, _v$7 = props.updateInfo()?.hasUpdate ? theme.warning : theme.textMuted, _v$8 = props.updateInfo()?.hasUpdate ? TextAttributes9.BOLD : TextAttributes9.DIM, _v$9 = theme.textMuted, _v$0 = TextAttributes9.DIM;
15419
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$5, _p$.e));
15420
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$6, _p$.t));
15421
+ _v$7 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$7, _p$.a));
15422
+ _v$8 !== _p$.o && (_p$.o = setProp(_el$7, "attributes", _v$8, _p$.o));
15423
+ _v$9 !== _p$.i && (_p$.i = setProp(_el$12, "fg", _v$9, _p$.i));
15424
+ _v$0 !== _p$.n && (_p$.n = setProp(_el$12, "attributes", _v$0, _p$.n));
15033
15425
  return _p$;
15034
15426
  }, {
15035
15427
  e: undefined,
@@ -15134,6 +15526,7 @@ var init_host = __esm(() => {
15134
15526
  init_env();
15135
15527
  init_errors();
15136
15528
  init_store();
15529
+ init_repo_init();
15137
15530
  init_repos();
15138
15531
  init_vendor();
15139
15532
  init_version();
@@ -15417,6 +15810,415 @@ var init_host3 = __esm(() => {
15417
15810
  init_dialog();
15418
15811
  });
15419
15812
 
15813
+ // src/tui/update/host.tsx
15814
+ var exports_host4 = {};
15815
+ __export(exports_host4, {
15816
+ startUpdateHost: () => startUpdateHost
15817
+ });
15818
+ import { spawn as spawn4, spawnSync as spawnSync9 } from "child_process";
15819
+ import { TextAttributes as TextAttributes10 } from "@opentui/core";
15820
+ function openExternalUrl(url) {
15821
+ if (!url)
15822
+ return false;
15823
+ const platform = process.platform;
15824
+ const [command, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
15825
+ try {
15826
+ const child = spawn4(command, args, {
15827
+ stdio: "ignore",
15828
+ detached: true
15829
+ });
15830
+ child.unref();
15831
+ return true;
15832
+ } catch {
15833
+ return false;
15834
+ }
15835
+ }
15836
+ function releaseBodyLines(body) {
15837
+ return body.replace(/\r\n/g, `
15838
+ `).split(`
15839
+ `).map((line) => line.trim()).filter(Boolean).slice(0, 40);
15840
+ }
15841
+ function waitForKeypress() {
15842
+ if (!process.stdin.isTTY)
15843
+ return Promise.resolve();
15844
+ return new Promise((resolve6) => {
15845
+ const stdin = process.stdin;
15846
+ const done = () => {
15847
+ stdin.off("data", done);
15848
+ stdin.setRawMode?.(false);
15849
+ stdin.pause();
15850
+ resolve6();
15851
+ };
15852
+ stdin.setRawMode?.(true);
15853
+ stdin.resume();
15854
+ stdin.once("data", done);
15855
+ });
15856
+ }
15857
+ function UpdatePage() {
15858
+ const {
15859
+ theme
15860
+ } = useTheme();
15861
+ const renderer = useRenderer();
15862
+ const [info, setInfo] = createSignal(null);
15863
+ const [notes, setNotes] = createSignal(null);
15864
+ const [loadingNotes, setLoadingNotes] = createSignal(true);
15865
+ const [selected, setSelected] = createSignal("update");
15866
+ const [status, setStatus] = createSignal(null);
15867
+ const latest = createMemo(() => info()?.latest ?? CURRENT_VERSION);
15868
+ const releaseUrl = createMemo(() => notes()?.url ?? releasePageUrl(latest()));
15869
+ const lines = createMemo(() => releaseBodyLines(notes()?.body ?? ""));
15870
+ const actions = createMemo(() => [{
15871
+ id: "update",
15872
+ key: "U",
15873
+ label: "Update now",
15874
+ detail: UPDATE_COMMAND
15875
+ }, {
15876
+ id: "release",
15877
+ key: "R",
15878
+ label: "Open release",
15879
+ detail: releaseUrl() ?? "release URL unavailable"
15880
+ }, {
15881
+ id: "close",
15882
+ key: "Q",
15883
+ label: "Close",
15884
+ detail: "return to the previous tmux window"
15885
+ }]);
15886
+ onMount(() => {
15887
+ load2();
15888
+ });
15889
+ async function load2() {
15890
+ const next = await checkLatestVersion({
15891
+ force: true
15892
+ });
15893
+ setInfo(next);
15894
+ const version = next?.latest ?? CURRENT_VERSION;
15895
+ const fetched = await fetchReleaseNotes(version);
15896
+ setNotes(fetched);
15897
+ setLoadingNotes(false);
15898
+ }
15899
+ function move(delta) {
15900
+ const ids = actions().map((a) => a.id);
15901
+ const index = ids.indexOf(selected());
15902
+ const next = (index + delta + ids.length) % ids.length;
15903
+ setSelected(ids[next] ?? "update");
15904
+ }
15905
+ function activate(id = selected()) {
15906
+ if (id === "close")
15907
+ process.exit(0);
15908
+ if (id === "release") {
15909
+ setStatus(openExternalUrl(releaseUrl()) ? "Opened release page in your browser." : "Could not open release URL.");
15910
+ return;
15911
+ }
15912
+ runUpdater();
15913
+ }
15914
+ async function runUpdater() {
15915
+ setStatus("Leaving the TUI page and running the updater in this tmux window...");
15916
+ await new Promise((resolve6) => setTimeout(resolve6, 30));
15917
+ renderer?.destroy();
15918
+ process.stdout.write(`
15919
+ kobe ${CURRENT_VERSION} -> latest
15920
+ `);
15921
+ process.stdout.write(`running: ${UPDATE_COMMAND}
15922
+
15923
+ `);
15924
+ const result = spawnSync9("sh", ["-c", UPDATE_COMMAND], {
15925
+ stdio: "inherit"
15926
+ });
15927
+ const code = result.status ?? (result.error ? 1 : 0);
15928
+ if (result.error)
15929
+ process.stderr.write(`
15930
+ kobe update: failed to start updater: ${result.error.message}
15931
+ `);
15932
+ process.stdout.write(code === 0 ? `
15933
+ kobe update complete. Relaunch kobe to use the new version.
15934
+ ` : `
15935
+ kobe update failed with exit code ${code}.
15936
+ `);
15937
+ process.stdout.write("Press any key to close this update window.");
15938
+ await waitForKeypress();
15939
+ process.exit(code);
15940
+ }
15941
+ useBindings(() => ({
15942
+ bindings: [{
15943
+ key: "up",
15944
+ cmd: () => move(-1)
15945
+ }, {
15946
+ key: "down",
15947
+ cmd: () => move(1)
15948
+ }, {
15949
+ key: "k",
15950
+ cmd: () => move(-1)
15951
+ }, {
15952
+ key: "j",
15953
+ cmd: () => move(1)
15954
+ }, {
15955
+ key: "return",
15956
+ cmd: () => activate()
15957
+ }, {
15958
+ key: "u",
15959
+ cmd: () => activate("update")
15960
+ }, {
15961
+ key: "r",
15962
+ cmd: () => activate("release")
15963
+ }, {
15964
+ key: "q",
15965
+ cmd: () => activate("close")
15966
+ }, {
15967
+ key: "escape",
15968
+ cmd: () => activate("close")
15969
+ }, {
15970
+ key: "ctrl+c",
15971
+ cmd: () => activate("close")
15972
+ }]
15973
+ }));
15974
+ return (() => {
15975
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$1 = createTextNode(`v`), _el$10 = createElement("text"), _el$12 = createElement("text"), _el$13 = createTextNode(`v`), _el$14 = createElement("box"), _el$16 = createElement("box"), _el$17 = createElement("text"), _el$19 = createElement("scrollbox"), _el$20 = createElement("box");
15976
+ insertNode(_el$, _el$2);
15977
+ insertNode(_el$, _el$7);
15978
+ insertNode(_el$, _el$14);
15979
+ insertNode(_el$, _el$16);
15980
+ insertNode(_el$, _el$19);
15981
+ setProp(_el$, "flexDirection", "column");
15982
+ setProp(_el$, "flexGrow", 1);
15983
+ setProp(_el$, "paddingTop", 1);
15984
+ setProp(_el$, "paddingLeft", 2);
15985
+ setProp(_el$, "paddingRight", 2);
15986
+ insertNode(_el$2, _el$3);
15987
+ insertNode(_el$2, _el$5);
15988
+ setProp(_el$2, "flexDirection", "row");
15989
+ setProp(_el$2, "justifyContent", "space-between");
15990
+ setProp(_el$2, "flexShrink", 0);
15991
+ insertNode(_el$3, createTextNode(`KOBE UPDATE`));
15992
+ setProp(_el$3, "wrapMode", "none");
15993
+ insertNode(_el$5, createTextNode(`q / esc`));
15994
+ setProp(_el$5, "wrapMode", "none");
15995
+ setProp(_el$5, "onMouseUp", () => activate("close"));
15996
+ insertNode(_el$7, _el$8);
15997
+ insertNode(_el$7, _el$0);
15998
+ insertNode(_el$7, _el$10);
15999
+ insertNode(_el$7, _el$12);
16000
+ setProp(_el$7, "flexDirection", "row");
16001
+ setProp(_el$7, "gap", 2);
16002
+ setProp(_el$7, "flexShrink", 0);
16003
+ setProp(_el$7, "paddingTop", 1);
16004
+ insertNode(_el$8, createTextNode(`current`));
16005
+ setProp(_el$8, "wrapMode", "none");
16006
+ insertNode(_el$0, _el$1);
16007
+ setProp(_el$0, "wrapMode", "none");
16008
+ insert(_el$0, CURRENT_VERSION, null);
16009
+ insertNode(_el$10, createTextNode(`latest`));
16010
+ setProp(_el$10, "wrapMode", "none");
16011
+ insertNode(_el$12, _el$13);
16012
+ setProp(_el$12, "wrapMode", "none");
16013
+ insert(_el$12, latest, null);
16014
+ setProp(_el$14, "flexDirection", "column");
16015
+ setProp(_el$14, "flexShrink", 0);
16016
+ setProp(_el$14, "paddingTop", 1);
16017
+ setProp(_el$14, "gap", 0);
16018
+ insert(_el$14, createComponent2(For, {
16019
+ get each() {
16020
+ return actions();
16021
+ },
16022
+ children: (action) => (() => {
16023
+ var _el$25 = createElement("box"), _el$26 = createElement("box"), _el$27 = createElement("text"), _el$28 = createTextNode(`[`), _el$29 = createTextNode(`]`), _el$30 = createElement("box"), _el$31 = createElement("text"), _el$32 = createElement("text");
16024
+ insertNode(_el$25, _el$26);
16025
+ insertNode(_el$25, _el$30);
16026
+ insertNode(_el$25, _el$32);
16027
+ setProp(_el$25, "flexDirection", "row");
16028
+ setProp(_el$25, "gap", 1);
16029
+ setProp(_el$25, "paddingLeft", 1);
16030
+ setProp(_el$25, "paddingRight", 1);
16031
+ setProp(_el$25, "onMouseUp", () => activate(action.id));
16032
+ insertNode(_el$26, _el$27);
16033
+ setProp(_el$26, "width", 4);
16034
+ setProp(_el$26, "flexShrink", 0);
16035
+ insertNode(_el$27, _el$28);
16036
+ insertNode(_el$27, _el$29);
16037
+ setProp(_el$27, "wrapMode", "none");
16038
+ insert(_el$27, () => action.key, _el$29);
16039
+ insertNode(_el$30, _el$31);
16040
+ setProp(_el$30, "width", 14);
16041
+ setProp(_el$30, "flexShrink", 0);
16042
+ setProp(_el$31, "wrapMode", "none");
16043
+ insert(_el$31, () => action.label);
16044
+ setProp(_el$32, "wrapMode", "word");
16045
+ insert(_el$32, () => action.detail);
16046
+ effect((_p$) => {
16047
+ var _v$12 = selected() === action.id ? theme.primary : undefined, _v$13 = selected() === action.id ? theme.selectedListItemText : theme.accent, _v$14 = TextAttributes10.BOLD, _v$15 = selected() === action.id ? theme.selectedListItemText : theme.text, _v$16 = selected() === action.id ? theme.selectedListItemText : theme.textMuted;
16048
+ _v$12 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$12, _p$.e));
16049
+ _v$13 !== _p$.t && (_p$.t = setProp(_el$27, "fg", _v$13, _p$.t));
16050
+ _v$14 !== _p$.a && (_p$.a = setProp(_el$27, "attributes", _v$14, _p$.a));
16051
+ _v$15 !== _p$.o && (_p$.o = setProp(_el$31, "fg", _v$15, _p$.o));
16052
+ _v$16 !== _p$.i && (_p$.i = setProp(_el$32, "fg", _v$16, _p$.i));
16053
+ return _p$;
16054
+ }, {
16055
+ e: undefined,
16056
+ t: undefined,
16057
+ a: undefined,
16058
+ o: undefined,
16059
+ i: undefined
16060
+ });
16061
+ return _el$25;
16062
+ })()
16063
+ }));
16064
+ insert(_el$, createComponent2(Show, {
16065
+ get when() {
16066
+ return status();
16067
+ },
16068
+ get children() {
16069
+ var _el$15 = createElement("text");
16070
+ setProp(_el$15, "wrapMode", "word");
16071
+ insert(_el$15, status);
16072
+ effect((_$p) => setProp(_el$15, "fg", theme.info, _$p));
16073
+ return _el$15;
16074
+ }
16075
+ }), _el$16);
16076
+ insertNode(_el$16, _el$17);
16077
+ setProp(_el$16, "flexShrink", 0);
16078
+ setProp(_el$16, "paddingTop", 1);
16079
+ insertNode(_el$17, createTextNode(`\u2500\u2500 release notes \u2500\u2500`));
16080
+ setProp(_el$17, "wrapMode", "none");
16081
+ insertNode(_el$19, _el$20);
16082
+ setProp(_el$19, "flexGrow", 1);
16083
+ setProp(_el$19, "flexShrink", 1);
16084
+ setProp(_el$19, "stickyScroll", false);
16085
+ setProp(_el$20, "flexDirection", "column");
16086
+ setProp(_el$20, "paddingRight", 1);
16087
+ setProp(_el$20, "paddingBottom", 1);
16088
+ setProp(_el$20, "gap", 0);
16089
+ insert(_el$20, createComponent2(Show, {
16090
+ get when() {
16091
+ return loadingNotes();
16092
+ },
16093
+ get children() {
16094
+ var _el$21 = createElement("text");
16095
+ insertNode(_el$21, createTextNode(`Loading release notes...`));
16096
+ effect((_$p) => setProp(_el$21, "fg", theme.textMuted, _$p));
16097
+ return _el$21;
16098
+ }
16099
+ }), null);
16100
+ insert(_el$20, createComponent2(Show, {
16101
+ get when() {
16102
+ return memo2(() => !!!loadingNotes())() && lines().length === 0;
16103
+ },
16104
+ get children() {
16105
+ var _el$23 = createElement("text");
16106
+ insertNode(_el$23, createTextNode(`Release notes are unavailable. Use Open release to view the GitHub release page.`));
16107
+ setProp(_el$23, "wrapMode", "word");
16108
+ effect((_$p) => setProp(_el$23, "fg", theme.textMuted, _$p));
16109
+ return _el$23;
16110
+ }
16111
+ }), null);
16112
+ insert(_el$20, createComponent2(For, {
16113
+ get each() {
16114
+ return lines();
16115
+ },
16116
+ children: (line) => (() => {
16117
+ var _el$33 = createElement("text");
16118
+ setProp(_el$33, "wrapMode", "word");
16119
+ insert(_el$33, line);
16120
+ effect((_$p) => setProp(_el$33, "fg", theme.textMuted, _$p));
16121
+ return _el$33;
16122
+ })()
16123
+ }), null);
16124
+ effect((_p$) => {
16125
+ var { background: _v$, text: _v$2 } = theme, _v$3 = TextAttributes10.BOLD, _v$4 = theme.textMuted, _v$5 = theme.textMuted, _v$6 = theme.text, _v$7 = TextAttributes10.BOLD, _v$8 = theme.textMuted, _v$9 = info()?.hasUpdate ? theme.warning : theme.success, _v$0 = TextAttributes10.BOLD, _v$1 = theme.textMuted, _v$10 = TextAttributes10.DIM, _v$11 = {
16126
+ trackOptions: {
16127
+ backgroundColor: theme.background,
16128
+ foregroundColor: theme.borderActive
16129
+ }
16130
+ };
16131
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "backgroundColor", _v$, _p$.e));
16132
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
16133
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$3, _p$.a));
16134
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$5, "fg", _v$4, _p$.o));
16135
+ _v$5 !== _p$.i && (_p$.i = setProp(_el$8, "fg", _v$5, _p$.i));
16136
+ _v$6 !== _p$.n && (_p$.n = setProp(_el$0, "fg", _v$6, _p$.n));
16137
+ _v$7 !== _p$.s && (_p$.s = setProp(_el$0, "attributes", _v$7, _p$.s));
16138
+ _v$8 !== _p$.h && (_p$.h = setProp(_el$10, "fg", _v$8, _p$.h));
16139
+ _v$9 !== _p$.r && (_p$.r = setProp(_el$12, "fg", _v$9, _p$.r));
16140
+ _v$0 !== _p$.d && (_p$.d = setProp(_el$12, "attributes", _v$0, _p$.d));
16141
+ _v$1 !== _p$.l && (_p$.l = setProp(_el$17, "fg", _v$1, _p$.l));
16142
+ _v$10 !== _p$.u && (_p$.u = setProp(_el$17, "attributes", _v$10, _p$.u));
16143
+ _v$11 !== _p$.c && (_p$.c = setProp(_el$19, "verticalScrollbarOptions", _v$11, _p$.c));
16144
+ return _p$;
16145
+ }, {
16146
+ e: undefined,
16147
+ t: undefined,
16148
+ a: undefined,
16149
+ o: undefined,
16150
+ i: undefined,
16151
+ n: undefined,
16152
+ s: undefined,
16153
+ h: undefined,
16154
+ r: undefined,
16155
+ d: undefined,
16156
+ l: undefined,
16157
+ u: undefined,
16158
+ c: undefined
16159
+ });
16160
+ return _el$;
16161
+ })();
16162
+ }
16163
+ async function startUpdateHost() {
16164
+ for (const {
16165
+ name,
16166
+ theme
16167
+ } of loadUserThemes()) {
16168
+ addTheme2(name, theme);
16169
+ }
16170
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
16171
+ await render(() => createComponent2(ThemeProvider, {
16172
+ mode: "dark",
16173
+ get theme() {
16174
+ return prefs.theme;
16175
+ },
16176
+ get children() {
16177
+ return createComponent2(KVProvider, {
16178
+ get children() {
16179
+ return createComponent2(FocusProvider, {
16180
+ initial: "sidebar",
16181
+ get children() {
16182
+ return createComponent2(DialogProvider, {
16183
+ get children() {
16184
+ return createComponent2(UpdatePage, {});
16185
+ }
16186
+ });
16187
+ }
16188
+ });
16189
+ }
16190
+ });
16191
+ }
16192
+ }), {
16193
+ backgroundColor: "transparent",
16194
+ externalOutputMode: "passthrough",
16195
+ exitOnCtrlC: false,
16196
+ screenMode: "alternate-screen",
16197
+ useKittyKeyboard: {}
16198
+ });
16199
+ }
16200
+ var FALLBACK_THEME4 = "claude";
16201
+ var init_host4 = __esm(() => {
16202
+ init_solid();
16203
+ init_solid();
16204
+ init_solid();
16205
+ init_solid();
16206
+ init_solid();
16207
+ init_solid();
16208
+ init_solid();
16209
+ init_solid();
16210
+ init_solid();
16211
+ init_dev();
16212
+ init_version();
16213
+ init_focus();
16214
+ init_kv();
16215
+ init_theme2();
16216
+ init_loader();
16217
+ init_keymap();
16218
+ init_persisted_ui_prefs();
16219
+ init_dialog();
16220
+ });
16221
+
15420
16222
  // src/engine/claude-code-local/normalize.ts
15421
16223
  function normalizeClaudeContent(content) {
15422
16224
  if (typeof content === "string") {
@@ -16552,7 +17354,7 @@ var gitWrapper;
16552
17354
  var init_git2 = __esm(() => {
16553
17355
  gitWrapper = {
16554
17356
  spawn(args, cwd) {
16555
- return new Promise((resolve5, reject) => {
17357
+ return new Promise((resolve6, reject) => {
16556
17358
  const child = nodeSpawn("git", [...args], {
16557
17359
  cwd,
16558
17360
  shell: false,
@@ -16570,7 +17372,7 @@ var init_git2 = __esm(() => {
16570
17372
  });
16571
17373
  child.on("error", reject);
16572
17374
  child.on("close", (status, signal) => {
16573
- resolve5({ stdout, stderr, status, signal });
17375
+ resolve6({ stdout, stderr, status, signal });
16574
17376
  });
16575
17377
  });
16576
17378
  }
@@ -16622,17 +17424,17 @@ var init_keys2 = __esm(() => {
16622
17424
  });
16623
17425
 
16624
17426
  // src/tui/panes/filetree/open-external.ts
16625
- import { spawn as spawn4 } from "child_process";
16626
- import { existsSync as existsSync11 } from "fs";
17427
+ import { spawn as spawn5 } from "child_process";
17428
+ import { existsSync as existsSync12 } from "fs";
16627
17429
  import { platform } from "os";
16628
17430
  function openExternally(absPath) {
16629
17431
  if (!absPath)
16630
17432
  return;
16631
17433
  const plat = platform();
16632
17434
  if (plat === "linux") {
16633
- if (existsSync11("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
17435
+ if (existsSync12("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
16634
17436
  spawnDetached("wslview", [absPath], () => {
16635
- const child = spawn4("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
17437
+ const child = spawn5("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
16636
17438
  let out = "";
16637
17439
  child.stdout.on("data", (b) => {
16638
17440
  out += b.toString();
@@ -16658,7 +17460,7 @@ function openExternally(absPath) {
16658
17460
  }
16659
17461
  function spawnDetached(cmd, args, onError) {
16660
17462
  try {
16661
- const child = spawn4(cmd, args, { stdio: "ignore", detached: true });
17463
+ const child = spawn5(cmd, args, { stdio: "ignore", detached: true });
16662
17464
  child.on("error", () => onError?.());
16663
17465
  child.unref();
16664
17466
  } catch {
@@ -16669,7 +17471,7 @@ var init_open_external = () => {};
16669
17471
 
16670
17472
  // src/tui/panes/filetree/FileTree.tsx
16671
17473
  import { watch } from "fs";
16672
- import { TextAttributes as TextAttributes10 } from "@opentui/core";
17474
+ import { TextAttributes as TextAttributes11 } from "@opentui/core";
16673
17475
  function statusToken(s) {
16674
17476
  switch (s) {
16675
17477
  case "M":
@@ -17046,7 +17848,7 @@ function FileTree(props) {
17046
17848
  insertNode(_el$6, createTextNode(`create PR`));
17047
17849
  setProp(_el$6, "wrapMode", "none");
17048
17850
  effect((_p$) => {
17049
- var _v$ = theme.accent, _v$2 = TextAttributes10.BOLD, _v$3 = theme.text;
17851
+ var _v$ = theme.accent, _v$2 = TextAttributes11.BOLD, _v$3 = theme.text;
17050
17852
  _v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
17051
17853
  _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$2, _p$.t));
17052
17854
  _v$3 !== _p$.a && (_p$.a = setProp(_el$6, "fg", _v$3, _p$.a));
@@ -17076,7 +17878,7 @@ function FileTree(props) {
17076
17878
  setProp(_el$23, "onMouseUp", () => setTab(t));
17077
17879
  insert(_el$23, () => TAB_LABEL[t]);
17078
17880
  effect((_p$) => {
17079
- var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes10.BOLD : undefined;
17881
+ var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes11.BOLD : undefined;
17080
17882
  _v$6 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$6, _p$.e));
17081
17883
  _v$7 !== _p$.t && (_p$.t = setProp(_el$23, "attributes", _v$7, _p$.t));
17082
17884
  return _p$;
@@ -17097,7 +17899,7 @@ function FileTree(props) {
17097
17899
  setProp(_el$24, "wrapMode", "none");
17098
17900
  insert(_el$24, () => badge().text);
17099
17901
  effect((_p$) => {
17100
- var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes10.BOLD : undefined;
17902
+ var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes11.BOLD : undefined;
17101
17903
  _v$8 !== _p$.e && (_p$.e = setProp(_el$24, "fg", _v$8, _p$.e));
17102
17904
  _v$9 !== _p$.t && (_p$.t = setProp(_el$24, "attributes", _v$9, _p$.t));
17103
17905
  return _p$;
@@ -17239,7 +18041,7 @@ function FileTree(props) {
17239
18041
  setProp(_el$26, "wrapMode", "none");
17240
18042
  insert(_el$26, () => `${indent}${marker} ${row.name}/`);
17241
18043
  effect((_p$) => {
17242
- var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes10.BOLD;
18044
+ var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes11.BOLD;
17243
18045
  _v$0 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$0, _p$.e));
17244
18046
  _v$1 !== _p$.t && (_p$.t = setProp(_el$26, "fg", _v$1, _p$.t));
17245
18047
  _v$10 !== _p$.a && (_p$.a = setProp(_el$26, "attributes", _v$10, _p$.a));
@@ -17388,12 +18190,12 @@ var init_filetree = __esm(() => {
17388
18190
  });
17389
18191
 
17390
18192
  // src/tui/ops/pr-prompt.ts
17391
- import { spawnSync as spawnSync9 } from "child_process";
18193
+ import { spawnSync as spawnSync10 } from "child_process";
17392
18194
  import { promises as fs4 } from "fs";
17393
18195
  import path11 from "path";
17394
18196
  function git2(cwd, args) {
17395
18197
  try {
17396
- const out = spawnSync9("git", args.slice(), {
18198
+ const out = spawnSync10("git", args.slice(), {
17397
18199
  cwd,
17398
18200
  encoding: "utf8",
17399
18201
  timeout: GIT_TIMEOUT_MS
@@ -17487,12 +18289,12 @@ If any of these steps fail, ask the user for help.`;
17487
18289
  var init_pr_prompt = () => {};
17488
18290
 
17489
18291
  // src/tui/ops/host.tsx
17490
- var exports_host4 = {};
17491
- __export(exports_host4, {
18292
+ var exports_host5 = {};
18293
+ __export(exports_host5, {
17492
18294
  startOpsPreview: () => startOpsPreview,
17493
18295
  startOpsHost: () => startOpsHost
17494
18296
  });
17495
- import { createHash as createHash2 } from "crypto";
18297
+ import { createHash as createHash3 } from "crypto";
17496
18298
  import { SyntaxStyle } from "@opentui/core";
17497
18299
  function OpsShell(props) {
17498
18300
  const themeCtx = useTheme();
@@ -17631,7 +18433,7 @@ function basename5(p) {
17631
18433
  return i >= 0 ? p.slice(i + 1) : p;
17632
18434
  }
17633
18435
  function fingerprint(text) {
17634
- return createHash2("sha1").update(text).digest("hex");
18436
+ return createHash3("sha1").update(text).digest("hex");
17635
18437
  }
17636
18438
  function OpsApp(props) {
17637
18439
  return createComponent2(ThemeProvider, {
@@ -17655,7 +18457,7 @@ async function startOpsHost(args) {
17655
18457
  } of loadUserThemes()) {
17656
18458
  addTheme2(name, theme);
17657
18459
  }
17658
- const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
18460
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME5);
17659
18461
  await render(() => createComponent2(OpsApp, mergeProps3(args, {
17660
18462
  prefs
17661
18463
  })), {
@@ -17883,7 +18685,7 @@ async function startOpsPreview(args) {
17883
18685
  } of loadUserThemes()) {
17884
18686
  addTheme2(name, theme);
17885
18687
  }
17886
- const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
18688
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME5);
17887
18689
  await render(() => createComponent2(ThemeProvider, {
17888
18690
  mode: "dark",
17889
18691
  get theme() {
@@ -17904,8 +18706,8 @@ async function startOpsPreview(args) {
17904
18706
  useKittyKeyboard: {}
17905
18707
  });
17906
18708
  }
17907
- var FALLBACK_THEME4 = "claude", ACTIVITY_POLL_MS = 2500, TURN_STATUS_POLL_MS = 1500, STABLE_POLLS_FOR_DONE = 2, CHAT_TAB_STATE_OPTION2 = "@kobe_tab_state";
17908
- var init_host4 = __esm(() => {
18709
+ var FALLBACK_THEME5 = "claude", ACTIVITY_POLL_MS = 2500, TURN_STATUS_POLL_MS = 1500, STABLE_POLLS_FOR_DONE = 2, CHAT_TAB_STATE_OPTION2 = "@kobe_tab_state";
18710
+ var init_host5 = __esm(() => {
17909
18711
  init_solid();
17910
18712
  init_solid();
17911
18713
  init_solid();
@@ -17971,7 +18773,7 @@ __export(exports_direct, {
17971
18773
  startDirectTmux: () => startDirectTmux,
17972
18774
  chooseInitialTask: () => chooseInitialTask
17973
18775
  });
17974
- import { resolve as resolve5 } from "path";
18776
+ import { resolve as resolve6 } from "path";
17975
18777
  function chooseInitialTask(tasks, choice = {}) {
17976
18778
  const byId = (id) => id ? tasks.find((t) => t.id === id) : undefined;
17977
18779
  const active = byId(choice.activeTaskId);
@@ -18007,7 +18809,7 @@ async function ensureRepos(orchestrator) {
18007
18809
  normalizeSavedRepos();
18008
18810
  let repos = [...getSavedRepos()];
18009
18811
  if (repos.length === 0) {
18010
- const added = addSavedRepo(resolve5(process.cwd()));
18812
+ const added = addSavedRepo(resolve6(process.cwd()));
18011
18813
  repos = [added.path];
18012
18814
  }
18013
18815
  for (const repo of repos) {
@@ -18017,7 +18819,7 @@ async function ensureRepos(orchestrator) {
18017
18819
  console.error(`[kobe] ensureMainTask failed for ${repo}:`, err);
18018
18820
  }
18019
18821
  }
18020
- return repos[0] ?? resolve5(process.cwd());
18822
+ return repos[0] ?? resolve6(process.cwd());
18021
18823
  }
18022
18824
  async function startDirectTmux() {
18023
18825
  if (!await tmuxAvailable()) {
@@ -18045,12 +18847,15 @@ async function startDirectTmux() {
18045
18847
  const name = tmuxSessionName(task.id);
18046
18848
  await orchestrator.setActiveTask(task.id).catch(() => {});
18047
18849
  setPersistedString("lastSelectedTaskId", task.id);
18850
+ const init2 = resolveRepoInit(task.repo, cwd);
18048
18851
  const ready = await ensureSession({
18049
18852
  name,
18050
18853
  cwd,
18051
18854
  command: interactiveEngineCommand(task.vendor),
18052
18855
  taskId: task.id,
18053
- vendor: task.vendor
18856
+ vendor: task.vendor,
18857
+ initScript: init2.initScript,
18858
+ initPrompt: init2.initPrompt
18054
18859
  });
18055
18860
  if (!ready) {
18056
18861
  console.error(`kobe: tmux session ${name} failed to start (check the daemon log)`);
@@ -18088,12 +18893,13 @@ var init_direct = __esm(() => {
18088
18893
  init_interactive_command();
18089
18894
  init_auto_title();
18090
18895
  init_core();
18896
+ init_repo_init();
18091
18897
  init_repos();
18092
18898
  init_tmux();
18093
18899
  });
18094
18900
 
18095
18901
  // src/tui/component/help-dialog.tsx
18096
- import { TextAttributes as TextAttributes11 } from "@opentui/core";
18902
+ import { TextAttributes as TextAttributes12 } from "@opentui/core";
18097
18903
  function groupBindings(keymap) {
18098
18904
  const groups = new Map;
18099
18905
  const order = [];
@@ -18197,7 +19003,7 @@ function HelpDialog() {
18197
19003
  }
18198
19004
  }), null);
18199
19005
  effect((_p$) => {
18200
- var _v$5 = theme.accent, _v$6 = TextAttributes11.BOLD;
19006
+ var _v$5 = theme.accent, _v$6 = TextAttributes12.BOLD;
18201
19007
  _v$5 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$5, _p$.e));
18202
19008
  _v$6 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$6, _p$.t));
18203
19009
  return _p$;
@@ -18209,7 +19015,7 @@ function HelpDialog() {
18209
19015
  })()
18210
19016
  }));
18211
19017
  effect((_p$) => {
18212
- var _v$ = TextAttributes11.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = {
19018
+ var _v$ = TextAttributes12.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = {
18213
19019
  trackOptions: {
18214
19020
  backgroundColor: theme.backgroundDialog,
18215
19021
  foregroundColor: theme.borderActive
@@ -18249,7 +19055,7 @@ var init_help_dialog = __esm(() => {
18249
19055
  });
18250
19056
 
18251
19057
  // src/tui/component/pane-header.tsx
18252
- import { TextAttributes as TextAttributes12 } from "@opentui/core";
19058
+ import { TextAttributes as TextAttributes13 } from "@opentui/core";
18253
19059
  function PaneHeader(props) {
18254
19060
  const {
18255
19061
  theme
@@ -18279,7 +19085,7 @@ function PaneHeader(props) {
18279
19085
  setProp(_el$3, "wrapMode", "none");
18280
19086
  insert(_el$3, () => props.ordinal);
18281
19087
  effect((_p$) => {
18282
- var _v$ = titleColor(), _v$2 = TextAttributes12.BOLD;
19088
+ var _v$ = titleColor(), _v$2 = TextAttributes13.BOLD;
18283
19089
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
18284
19090
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
18285
19091
  return _p$;
@@ -18330,7 +19136,7 @@ function PaneHeader(props) {
18330
19136
  }
18331
19137
  }), null);
18332
19138
  effect((_p$) => {
18333
- var _v$3 = titleColor(), _v$4 = TextAttributes12.BOLD;
19139
+ var _v$3 = titleColor(), _v$4 = TextAttributes13.BOLD;
18334
19140
  _v$3 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$3, _p$.e));
18335
19141
  _v$4 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$4, _p$.t));
18336
19142
  return _p$;
@@ -18455,7 +19261,7 @@ var init_resizable_edge = __esm(() => {
18455
19261
  });
18456
19262
 
18457
19263
  // src/tui/component/status-bar.tsx
18458
- import { TextAttributes as TextAttributes13 } from "@opentui/core";
19264
+ import { TextAttributes as TextAttributes14 } from "@opentui/core";
18459
19265
  function Hotkey(props) {
18460
19266
  const {
18461
19267
  theme
@@ -18474,7 +19280,7 @@ function Hotkey(props) {
18474
19280
  setProp(_el$5, "wrapMode", "none");
18475
19281
  insert(_el$5, () => props.label);
18476
19282
  effect((_p$) => {
18477
- var _v$ = theme.accent, _v$2 = TextAttributes13.BOLD, _v$3 = theme.textMuted;
19283
+ var _v$ = theme.accent, _v$2 = TextAttributes14.BOLD, _v$3 = theme.textMuted;
18478
19284
  _v$ !== _p$.e && (_p$.e = setProp(_el$2, "fg", _v$, _p$.e));
18479
19285
  _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "attributes", _v$2, _p$.t));
18480
19286
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -18562,7 +19368,7 @@ function StatusBar() {
18562
19368
  insertNode(_el$0, createTextNode(`Press Ctrl+C again to exit`));
18563
19369
  setProp(_el$0, "wrapMode", "none");
18564
19370
  effect((_p$) => {
18565
- var _v$4 = theme.warning, _v$5 = TextAttributes13.BOLD;
19371
+ var _v$4 = theme.warning, _v$5 = TextAttributes14.BOLD;
18566
19372
  _v$4 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$4, _p$.e));
18567
19373
  _v$5 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$5, _p$.t));
18568
19374
  return _p$;
@@ -18574,7 +19380,7 @@ function StatusBar() {
18574
19380
  }
18575
19381
  }), null);
18576
19382
  effect((_p$) => {
18577
- var _v$6 = theme.primary, _v$7 = TextAttributes13.BOLD;
19383
+ var _v$6 = theme.primary, _v$7 = TextAttributes14.BOLD;
18578
19384
  _v$6 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$6, _p$.e));
18579
19385
  _v$7 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$7, _p$.t));
18580
19386
  return _p$;
@@ -18604,9 +19410,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
18604
19410
  var init_pulse = () => {};
18605
19411
 
18606
19412
  // src/tui/lib/sound.ts
18607
- import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
19413
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
18608
19414
  import { tmpdir as tmpdir2 } from "os";
18609
- import { basename as basename6, isAbsolute as isAbsolute2, join as join11, resolve as resolve6 } from "path";
19415
+ import { basename as basename6, isAbsolute as isAbsolute2, join as join12, resolve as resolve7 } from "path";
18610
19416
  function args(player, file, volume) {
18611
19417
  if (player === "ffplay")
18612
19418
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -18629,13 +19435,13 @@ function pickPlayer() {
18629
19435
  return cachedPlayer;
18630
19436
  const path12 = process.env.PATH ?? "";
18631
19437
  const segments = path12.split(":").filter(Boolean);
18632
- cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync12(join11(dir, p)))) ?? null;
19438
+ cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync13(join12(dir, p)))) ?? null;
18633
19439
  return cachedPlayer;
18634
19440
  }
18635
19441
  async function ensureAsset() {
18636
19442
  cachedPath ??= (async () => {
18637
19443
  mkdirSync5(DIR, { recursive: true });
18638
- const dest = join11(DIR, basename6(pulseAsset));
19444
+ const dest = join12(DIR, basename6(pulseAsset));
18639
19445
  const out = Bun.file(dest);
18640
19446
  if (await out.exists())
18641
19447
  return dest;
@@ -18664,8 +19470,8 @@ function pulse(volume = 0.4) {
18664
19470
  var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
18665
19471
  var init_sound = __esm(() => {
18666
19472
  init_pulse();
18667
- pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve6(import.meta.dir, pulse_default);
18668
- DIR = join11(tmpdir2(), "kobe-sfx");
19473
+ pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve7(import.meta.dir, pulse_default);
19474
+ DIR = join12(tmpdir2(), "kobe-sfx");
18669
19475
  PLAYERS = [
18670
19476
  "ffplay",
18671
19477
  "mpv",
@@ -18763,7 +19569,7 @@ var init_notifications = __esm(() => {
18763
19569
  });
18764
19570
 
18765
19571
  // src/tui/component/toast-overlay.tsx
18766
- import { TextAttributes as TextAttributes14 } from "@opentui/core";
19572
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
18767
19573
  function ToastOverlay() {
18768
19574
  const {
18769
19575
  theme
@@ -18806,7 +19612,7 @@ function ToastOverlay() {
18806
19612
  setProp(_el$4, "wrapMode", "none");
18807
19613
  insert(_el$4, () => toast.title);
18808
19614
  effect((_p$) => {
18809
- var _v$3 = bg(), _v$4 = fg(), _v$5 = TextAttributes14.BOLD, _v$6 = fg();
19615
+ var _v$3 = bg(), _v$4 = fg(), _v$5 = TextAttributes15.BOLD, _v$6 = fg();
18810
19616
  _v$3 !== _p$.e && (_p$.e = setProp(_el$2, "backgroundColor", _v$3, _p$.e));
18811
19617
  _v$4 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$4, _p$.t));
18812
19618
  _v$5 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$5, _p$.a));
@@ -18865,8 +19671,8 @@ function repoBasename2(repo) {
18865
19671
  }
18866
19672
 
18867
19673
  // src/tui/component/top-bar.tsx
18868
- import { spawnSync as spawnSync10 } from "child_process";
18869
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
19674
+ import { spawnSync as spawnSync11 } from "child_process";
19675
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
18870
19676
  function TopBar(props) {
18871
19677
  const {
18872
19678
  theme
@@ -18894,7 +19700,7 @@ function TopBar(props) {
18894
19700
  `);
18895
19701
  process.stderr.write(`running: ${UPDATE_COMMAND}
18896
19702
  `);
18897
- const result = spawnSync10("sh", ["-c", UPDATE_COMMAND], {
19703
+ const result = spawnSync11("sh", ["-c", UPDATE_COMMAND], {
18898
19704
  stdio: "inherit"
18899
19705
  });
18900
19706
  if (result.error) {
@@ -18941,7 +19747,7 @@ function TopBar(props) {
18941
19747
  insertNode(_el$7, createTextNode(`[Update]`));
18942
19748
  setProp(_el$7, "onMouseUp", () => void confirmUpdate());
18943
19749
  effect((_p$) => {
18944
- var _v$ = theme.warning, _v$2 = TextAttributes15.BOLD;
19750
+ var _v$ = theme.warning, _v$2 = TextAttributes16.BOLD;
18945
19751
  _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
18946
19752
  _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "attributes", _v$2, _p$.t));
18947
19753
  return _p$;
@@ -18977,7 +19783,7 @@ function TopBar(props) {
18977
19783
  insertNode(_el$12, createTextNode(`daemon disconnected`));
18978
19784
  setProp(_el$12, "wrapMode", "none");
18979
19785
  effect((_p$) => {
18980
- var _v$6 = theme.error, _v$7 = TextAttributes15.BOLD;
19786
+ var _v$6 = theme.error, _v$7 = TextAttributes16.BOLD;
18981
19787
  _v$6 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$6, _p$.e));
18982
19788
  _v$7 !== _p$.t && (_p$.t = setProp(_el$12, "attributes", _v$7, _p$.t));
18983
19789
  return _p$;
@@ -19007,7 +19813,7 @@ function TopBar(props) {
19007
19813
  setProp(_el$15, "wrapMode", "none");
19008
19814
  insert(_el$15, () => label().repoName);
19009
19815
  effect((_p$) => {
19010
- var _v$8 = label().branch ? theme.textMuted : theme.text, _v$9 = label().branch ? undefined : TextAttributes15.BOLD;
19816
+ var _v$8 = label().branch ? theme.textMuted : theme.text, _v$9 = label().branch ? undefined : TextAttributes16.BOLD;
19011
19817
  _v$8 !== _p$.e && (_p$.e = setProp(_el$15, "fg", _v$8, _p$.e));
19012
19818
  _v$9 !== _p$.t && (_p$.t = setProp(_el$15, "attributes", _v$9, _p$.t));
19013
19819
  return _p$;
@@ -19039,7 +19845,7 @@ function TopBar(props) {
19039
19845
  setProp(_el$18, "wrapMode", "none");
19040
19846
  insert(_el$18, () => label().branch);
19041
19847
  effect((_p$) => {
19042
- var _v$0 = theme.text, _v$1 = TextAttributes15.BOLD;
19848
+ var _v$0 = theme.text, _v$1 = TextAttributes16.BOLD;
19043
19849
  _v$0 !== _p$.e && (_p$.e = setProp(_el$18, "fg", _v$0, _p$.e));
19044
19850
  _v$1 !== _p$.t && (_p$.t = setProp(_el$18, "attributes", _v$1, _p$.t));
19045
19851
  return _p$;
@@ -19062,7 +19868,7 @@ function TopBar(props) {
19062
19868
  setProp(_el$11, "gap", 2);
19063
19869
  setProp(_el$11, "justifyContent", "flex-end");
19064
19870
  effect((_p$) => {
19065
- var _v$3 = theme.primary, _v$4 = TextAttributes15.BOLD, _v$5 = theme.textMuted;
19871
+ var _v$3 = theme.primary, _v$4 = TextAttributes16.BOLD, _v$5 = theme.textMuted;
19066
19872
  _v$3 !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$3, _p$.e));
19067
19873
  _v$4 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$4, _p$.t));
19068
19874
  _v$5 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$5, _p$.a));
@@ -19606,12 +20412,15 @@ async function launchTaskTmux(opts) {
19606
20412
  }
19607
20413
  }
19608
20414
  const name = tmuxSessionName(opts.taskId);
20415
+ const init2 = opts.repo ? resolveRepoInit(opts.repo, cwd) : {};
19609
20416
  const ready = await ensureSession({
19610
20417
  name,
19611
20418
  cwd,
19612
20419
  command: opts.command,
19613
20420
  taskId: opts.taskId,
19614
- vendor: opts.vendor
20421
+ vendor: opts.vendor,
20422
+ initScript: init2.initScript,
20423
+ initPrompt: init2.initPrompt
19615
20424
  });
19616
20425
  if (!ready) {
19617
20426
  return {
@@ -19709,6 +20518,7 @@ var init_fullscreen = __esm(() => {
19709
20518
  init_solid();
19710
20519
  init_solid();
19711
20520
  init_dev();
20521
+ init_repo_init();
19712
20522
  init_theme2();
19713
20523
  init_keymap();
19714
20524
  init_dialog();
@@ -19792,6 +20602,7 @@ function Shell(props) {
19792
20602
  cwd: task.worktreePath || null,
19793
20603
  command: interactiveEngineCommand(task.vendor),
19794
20604
  vendor: task.vendor,
20605
+ repo: task.repo,
19795
20606
  onEnsureWorktree: (taskId) => props.orchestrator.ensureWorktree(taskId)
19796
20607
  });
19797
20608
  if (res.kind === "error") {
@@ -19900,8 +20711,19 @@ function Shell(props) {
19900
20711
  HelpDialog.show(dialog);
19901
20712
  }
19902
20713
  async function archiveTask(taskId) {
19903
- await props.orchestrator.setArchived(taskId).catch((err) => {
20714
+ try {
20715
+ await props.orchestrator.setArchived(taskId);
20716
+ } catch (err) {
19904
20717
  console.error("[kobe] archive failed:", err);
20718
+ return;
20719
+ }
20720
+ const sessionName = tmuxSessionName(taskId);
20721
+ const nextTask = tasksAcc().find((t) => t.id !== taskId && !t.archived);
20722
+ await switchClientBeforeKill(sessionName, nextTask ? tmuxSessionName(nextTask.id) : undefined).catch((err) => {
20723
+ console.error("[kobe] switch-client failed:", err);
20724
+ });
20725
+ await killSession(sessionName).catch((err) => {
20726
+ console.error("[kobe] kill tmux session failed:", err);
19905
20727
  });
19906
20728
  }
19907
20729
  async function renameTask(taskId) {
@@ -20327,11 +21149,14 @@ var init_tui = __esm(() => {
20327
21149
  // src/cli/index.ts
20328
21150
  init_path_glob();
20329
21151
  init_vendor();
20330
- import { resolve as resolve7 } from "path";
21152
+ import { resolve as resolve8 } from "path";
20331
21153
 
20332
21154
  // src/cli/usage.ts
21155
+ init_version();
20333
21156
  function topLevelUsage() {
20334
21157
  return [
21158
+ `kobe ${CURRENT_VERSION}`,
21159
+ "",
20335
21160
  "Usage: kobe [command] [options]",
20336
21161
  "",
20337
21162
  "Run with no command to launch the TUI.",
@@ -20339,6 +21164,7 @@ function topLevelUsage() {
20339
21164
  "Commands:",
20340
21165
  " add [path] Save a repo path for the new-task picker",
20341
21166
  " adopt [glob] Import existing git worktrees as tasks",
21167
+ " repo <verb> Per-repo init script + first prompt (show|set|unset)",
20342
21168
  " api <verb> Scriptable RPC surface for agents (see `kobe api --help`)",
20343
21169
  " daemon <verb> Manage the daemon (start|stop|status|restart)",
20344
21170
  " theme <verb> Manage user themes (list|add|remove)",
@@ -20370,7 +21196,7 @@ Usage: kobe add [path]
20370
21196
  `);
20371
21197
  process.exit(2);
20372
21198
  }
20373
- const target = resolve7(process.cwd(), arg && arg.length > 0 ? arg : ".");
21199
+ const target = resolve8(process.cwd(), arg && arg.length > 0 ? arg : ".");
20374
21200
  const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
20375
21201
  const result = addSavedRepo2(target);
20376
21202
  if (result.added) {
@@ -20469,7 +21295,7 @@ async function runAdoptSubcommand(args2) {
20469
21295
  }
20470
21296
  }
20471
21297
  const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
20472
- const repo = resolveRepoRoot2(resolve7(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
21298
+ const repo = resolveRepoRoot2(resolve8(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
20473
21299
  const vendor = coerceVendorId(vendorArg);
20474
21300
  const orch = await openLocalOrchestrator();
20475
21301
  const worktrees = await orch.discoverAdoptableWorktrees(repo);
@@ -20559,6 +21385,11 @@ async function main() {
20559
21385
  await runAdoptSubcommand(rest);
20560
21386
  return;
20561
21387
  }
21388
+ if (subcommand === "repo") {
21389
+ const { runRepoSubcommand: runRepoSubcommand2 } = await Promise.resolve().then(() => (init_repo_cmd(), exports_repo_cmd));
21390
+ await runRepoSubcommand2(rest);
21391
+ return;
21392
+ }
20562
21393
  if (subcommand === "api") {
20563
21394
  const { runApiSubcommand: runApiSubcommand2 } = await Promise.resolve().then(() => (init_api_cmd(), exports_api_cmd));
20564
21395
  await runApiSubcommand2(rest);
@@ -20641,6 +21472,11 @@ async function main() {
20641
21472
  await startNewTaskHost2({ defaultRepo: flags.repo });
20642
21473
  return;
20643
21474
  }
21475
+ if (subcommand === "update-page") {
21476
+ const { startUpdateHost: startUpdateHost2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21477
+ await startUpdateHost2();
21478
+ return;
21479
+ }
20644
21480
  if (subcommand === "ops") {
20645
21481
  const flags = parseOpsFlags(rest);
20646
21482
  if (!flags.worktree) {
@@ -20648,11 +21484,11 @@ async function main() {
20648
21484
  process.exit(2);
20649
21485
  }
20650
21486
  if (flags.preview) {
20651
- const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21487
+ const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (init_host5(), exports_host5));
20652
21488
  await startOpsPreview2({ worktree: flags.worktree, relPath: flags.preview });
20653
21489
  return;
20654
21490
  }
20655
- const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21491
+ const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (init_host5(), exports_host5));
20656
21492
  await startOpsHost2({
20657
21493
  taskId: flags.taskId ?? "",
20658
21494
  worktree: flags.worktree,