@sma1lboy/kobe 0.6.6 → 0.6.7

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 +1277 -498
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -64,7 +64,71 @@ 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.7",
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: ["dist", "README.md", "LICENSE"],
81
+ publishConfig: {
82
+ access: "public"
83
+ },
84
+ repository: {
85
+ type: "git",
86
+ url: "git+https://github.com/Sma1lboy/kobe.git"
87
+ },
88
+ homepage: "https://github.com/Sma1lboy/kobe#readme",
89
+ bugs: {
90
+ url: "https://github.com/Sma1lboy/kobe/issues"
91
+ },
92
+ engines: {
93
+ bun: ">=1.3.11"
94
+ },
95
+ scripts: {
96
+ dev: "env KOBE_DEV=1 bun --preload @opentui/solid/preload --conditions=browser ./src/cli/index.ts",
97
+ "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",
98
+ "dev:sandbox:reset": "env KOBE_TMUX_SOCKET=kobe-sandbox bun ./src/cli/index.ts kill-sessions",
99
+ build: "bun run scripts/build.ts",
100
+ compile: "bun run scripts/compile.ts",
101
+ typecheck: "tsc --noEmit",
102
+ test: "bun run test:fast && bun run test:socket",
103
+ "test:fast": "vitest run --passWithNoTests",
104
+ "test:socket": "KOBE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=1 --passWithNoTests",
105
+ lint: "biome check .",
106
+ knip: "knip-bun",
107
+ postinstall: "bun run scripts/check-preview-deps.ts || true",
108
+ prepublishOnly: "bun run typecheck && bun run build"
109
+ },
110
+ "//": "biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",
111
+ dependencies: {
112
+ "@ansi-tools/parser": "^1.0.15",
113
+ "@opentui/core": "0.2.4",
114
+ "@opentui/solid": "0.2.4",
115
+ "solid-js": "1.9.12"
116
+ },
117
+ devDependencies: {
118
+ "@biomejs/biome": "1.9.4",
119
+ "@tsconfig/bun": "1.0.9",
120
+ "@types/bun": "1.3.12",
121
+ knip: "^6.14.2",
122
+ "node-pty": "^1.1.0",
123
+ typescript: "5.8.2",
124
+ vitest: "2.1.9"
125
+ },
126
+ trustedDependencies: []
127
+ };
128
+ });
129
+
67
130
  // src/env.ts
131
+ import { createHash } from "crypto";
68
132
  import { homedir } from "os";
69
133
  import { join } from "path";
70
134
  function isDev() {
@@ -79,17 +143,178 @@ function kobeStateDir() {
79
143
  function kvStatePath() {
80
144
  return join(homeDir(), ".config", "kobe", "state.json");
81
145
  }
146
+ function worktreeInitMarkerPath(worktreePath) {
147
+ const hash = createHash("sha1").update(worktreePath).digest("hex").slice(0, 16);
148
+ return join(kobeStateDir(), "worktree-init", hash);
149
+ }
82
150
  var init_env = () => {};
83
151
 
152
+ // src/version.ts
153
+ var exports_version = {};
154
+ __export(exports_version, {
155
+ repoSlug: () => repoSlug,
156
+ releasePageUrl: () => releasePageUrl,
157
+ recommendedGlobalInstallCommand: () => recommendedGlobalInstallCommand,
158
+ isNewerSemver: () => isNewerSemver,
159
+ fetchReleaseSummaries: () => fetchReleaseSummaries,
160
+ fetchReleaseNotes: () => fetchReleaseNotes,
161
+ checkLatestVersion: () => checkLatestVersion,
162
+ UPDATE_SCRIPT_URL: () => UPDATE_SCRIPT_URL,
163
+ UPDATE_COMMAND: () => UPDATE_COMMAND,
164
+ PACKAGE_NAME: () => PACKAGE_NAME,
165
+ CURRENT_VERSION: () => CURRENT_VERSION
166
+ });
167
+ function repoSlug() {
168
+ const url = package_default.repository?.url;
169
+ if (!url)
170
+ return null;
171
+ const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
172
+ if (!m || !m[1] || !m[2])
173
+ return null;
174
+ return `${m[1]}/${m[2]}`;
175
+ }
176
+ function recommendedGlobalInstallCommand() {
177
+ return `npm install -g ${PACKAGE_NAME}@latest`;
178
+ }
179
+ async function fetchLatestFromRegistry(packageName) {
180
+ const ctrl = new AbortController;
181
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
182
+ try {
183
+ const encoded = packageName.replace("/", "%2F");
184
+ const res = await fetch(`https://registry.npmjs.org/${encoded}/latest`, {
185
+ signal: ctrl.signal,
186
+ headers: { accept: "application/json" }
187
+ });
188
+ if (!res.ok)
189
+ return null;
190
+ const body = await res.json();
191
+ if (typeof body.version !== "string")
192
+ return null;
193
+ return body.version;
194
+ } catch {
195
+ return null;
196
+ } finally {
197
+ clearTimeout(timer);
198
+ }
199
+ }
200
+ function isNewerSemver(latest, current) {
201
+ const norm = (v) => v.split("-")[0] ?? v;
202
+ const a = norm(latest).split(".").map((s) => Number.parseInt(s, 10));
203
+ const b = norm(current).split(".").map((s) => Number.parseInt(s, 10));
204
+ for (let i = 0;i < 3; i++) {
205
+ const av = a[i] ?? 0;
206
+ const bv = b[i] ?? 0;
207
+ if (Number.isNaN(av) || Number.isNaN(bv))
208
+ return false;
209
+ if (av > bv)
210
+ return true;
211
+ if (av < bv)
212
+ return false;
213
+ }
214
+ return false;
215
+ }
216
+ async function checkLatestVersion(opts = {}) {
217
+ if (isDev() && !opts.force)
218
+ return null;
219
+ const latest = await fetchLatestFromRegistry(PACKAGE_NAME);
220
+ if (!latest)
221
+ return null;
222
+ return {
223
+ current: CURRENT_VERSION,
224
+ latest,
225
+ hasUpdate: isNewerSemver(latest, CURRENT_VERSION)
226
+ };
227
+ }
228
+ function versionFromTagName(tagName) {
229
+ if (typeof tagName !== "string")
230
+ return null;
231
+ const match = tagName.match(/^v(\d+\.\d+\.\d+)$/);
232
+ return match?.[1] ?? null;
233
+ }
234
+ async function fetchReleaseNotes(version) {
235
+ const slug = repoSlug();
236
+ if (!slug)
237
+ return null;
238
+ const tag = `v${version}`;
239
+ const ctrl = new AbortController;
240
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
241
+ try {
242
+ const res = await fetch(`https://api.github.com/repos/${slug}/releases/tags/${tag}`, {
243
+ signal: ctrl.signal,
244
+ headers: {
245
+ accept: "application/vnd.github+json",
246
+ "x-github-api-version": "2022-11-28"
247
+ }
248
+ });
249
+ if (!res.ok)
250
+ return null;
251
+ const body = await res.json();
252
+ if (typeof body.body !== "string" || typeof body.html_url !== "string")
253
+ return null;
254
+ return { body: body.body, url: body.html_url, version };
255
+ } catch {
256
+ return null;
257
+ } finally {
258
+ clearTimeout(timer);
259
+ }
260
+ }
261
+ async function fetchReleaseSummaries(limit = 12) {
262
+ const slug = repoSlug();
263
+ if (!slug)
264
+ return [];
265
+ const ctrl = new AbortController;
266
+ const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
267
+ try {
268
+ const res = await fetch(`https://api.github.com/repos/${slug}/releases?per_page=${limit}`, {
269
+ signal: ctrl.signal,
270
+ headers: {
271
+ accept: "application/vnd.github+json",
272
+ "x-github-api-version": "2022-11-28"
273
+ }
274
+ });
275
+ if (!res.ok)
276
+ return [];
277
+ const body = await res.json();
278
+ if (!Array.isArray(body))
279
+ return [];
280
+ return body.map((release) => {
281
+ const version = versionFromTagName(release.tag_name);
282
+ if (!version || typeof release.html_url !== "string")
283
+ return null;
284
+ return { version, url: release.html_url };
285
+ }).filter((release) => release !== null);
286
+ } catch {
287
+ return [];
288
+ } finally {
289
+ clearTimeout(timer);
290
+ }
291
+ }
292
+ function releasePageUrl(version) {
293
+ const slug = repoSlug();
294
+ if (!slug)
295
+ return null;
296
+ return `https://github.com/${slug}/releases/tag/v${version}`;
297
+ }
298
+ var CURRENT_VERSION, PACKAGE_NAME, UPDATE_SCRIPT_URL = "https://raw.githubusercontent.com/Sma1lboy/kobe/main/scripts/update.sh", UPDATE_COMMAND, FETCH_TIMEOUT_MS = 3000;
299
+ var init_version = __esm(() => {
300
+ init_package();
301
+ init_env();
302
+ CURRENT_VERSION = package_default.version;
303
+ PACKAGE_NAME = package_default.name;
304
+ UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
305
+ });
306
+
84
307
  // src/state/repos.ts
85
308
  var exports_repos = {};
86
309
  __export(exports_repos, {
87
310
  statePath: () => statePath,
311
+ setRepoInitOverride: () => setRepoInitOverride,
88
312
  setPersistedString: () => setPersistedString,
89
313
  resolveRepoRoot: () => resolveRepoRoot,
90
314
  removeSavedRepo: () => removeSavedRepo,
91
315
  normalizeSavedRepos: () => normalizeSavedRepos,
92
316
  getSavedRepos: () => getSavedRepos,
317
+ getRepoInitOverride: () => getRepoInitOverride,
93
318
  getPersistedString: () => getPersistedString,
94
319
  addSavedRepo: () => addSavedRepo
95
320
  });
@@ -182,6 +407,45 @@ function normalizeSavedRepos() {
182
407
  state.savedRepos = next;
183
408
  save(state);
184
409
  }
410
+ function readRepoConfigs(state) {
411
+ const raw = state.repoConfigs;
412
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
413
+ return {};
414
+ return raw;
415
+ }
416
+ function coerceOverride(entry) {
417
+ if (!entry || typeof entry !== "object")
418
+ return {};
419
+ const e = entry;
420
+ return {
421
+ initScript: typeof e.initScript === "string" && e.initScript.length > 0 ? e.initScript : undefined,
422
+ initPrompt: typeof e.initPrompt === "string" && e.initPrompt.length > 0 ? e.initPrompt : undefined
423
+ };
424
+ }
425
+ function getRepoInitOverride(repoRoot) {
426
+ return coerceOverride(readRepoConfigs(load())[resolveRepoRoot(repoRoot)]);
427
+ }
428
+ function setRepoInitOverride(repoRoot, patch) {
429
+ const normalized = resolveRepoRoot(repoRoot);
430
+ const state = load();
431
+ const configs = { ...readRepoConfigs(state) };
432
+ const cur = coerceOverride(configs[normalized]);
433
+ const nextScript = patch.initScript === undefined ? cur.initScript : patch.initScript || undefined;
434
+ const nextPrompt = patch.initPrompt === undefined ? cur.initPrompt : patch.initPrompt || undefined;
435
+ const next = {
436
+ ...nextScript ? { initScript: nextScript } : {},
437
+ ...nextPrompt ? { initPrompt: nextPrompt } : {}
438
+ };
439
+ if (!next.initScript && !next.initPrompt) {
440
+ const { [normalized]: _dropped, ...rest } = configs;
441
+ state.repoConfigs = rest;
442
+ } else {
443
+ configs[normalized] = next;
444
+ state.repoConfigs = configs;
445
+ }
446
+ save(state);
447
+ return next;
448
+ }
185
449
  function removeSavedRepo(absPath) {
186
450
  const state = load();
187
451
  const cur = getSavedRepos();
@@ -272,7 +536,7 @@ class TaskIndexStore {
272
536
  kobeDir;
273
537
  path;
274
538
  tmpPath;
275
- cache = { version: CURRENT_VERSION, tasks: [] };
539
+ cache = { version: CURRENT_VERSION2, tasks: [] };
276
540
  loaded = false;
277
541
  listeners = new Set;
278
542
  saveChain = Promise.resolve();
@@ -308,7 +572,7 @@ class TaskIndexStore {
308
572
  } catch (err) {
309
573
  const code = err.code;
310
574
  if (code === "ENOENT") {
311
- this.cache = { version: CURRENT_VERSION, tasks: [] };
575
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
312
576
  this.loaded = true;
313
577
  this.notifyListeners();
314
578
  return this.snapshot();
@@ -320,7 +584,7 @@ class TaskIndexStore {
320
584
  parsed = JSON.parse(raw);
321
585
  } catch (err) {
322
586
  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: [] };
587
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
324
588
  this.loaded = true;
325
589
  this.notifyListeners();
326
590
  return this.snapshot();
@@ -420,7 +684,7 @@ class TaskIndexStore {
420
684
  if (err.code !== "ENOENT")
421
685
  throw err;
422
686
  }
423
- this.cache = { version: CURRENT_VERSION, tasks: [] };
687
+ this.cache = { version: CURRENT_VERSION2, tasks: [] };
424
688
  this.loaded = false;
425
689
  }
426
690
  assertLoaded() {
@@ -430,7 +694,7 @@ class TaskIndexStore {
430
694
  }
431
695
  snapshot() {
432
696
  return {
433
- version: CURRENT_VERSION,
697
+ version: CURRENT_VERSION2,
434
698
  tasks: this.cache.tasks.slice()
435
699
  };
436
700
  }
@@ -450,13 +714,13 @@ class TaskIndexStore {
450
714
  function normalizeIndex(parsed, source) {
451
715
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
452
716
  console.warn(`[kobe] tasks.json at ${source} is not an object; recovering with empty index.`);
453
- return { version: CURRENT_VERSION, tasks: [] };
717
+ return { version: CURRENT_VERSION2, tasks: [] };
454
718
  }
455
719
  const obj = parsed;
456
720
  const version = obj.version;
457
721
  if (version !== undefined && version !== 1 && version !== 2 && version !== 3) {
458
722
  console.warn(`[kobe] tasks.json at ${source} has unsupported version=${String(version)}; recovering with empty index.`);
459
- return { version: CURRENT_VERSION, tasks: [] };
723
+ return { version: CURRENT_VERSION2, tasks: [] };
460
724
  }
461
725
  const rawTasks = Array.isArray(obj.tasks) ? obj.tasks : [];
462
726
  const tasks = [];
@@ -468,7 +732,7 @@ function normalizeIndex(parsed, source) {
468
732
  console.warn(`[kobe] dropping malformed task entry from ${source}: ${JSON.stringify(entry)}`);
469
733
  }
470
734
  }
471
- return { version: CURRENT_VERSION, tasks };
735
+ return { version: CURRENT_VERSION2, tasks };
472
736
  }
473
737
  function coerceTask(value) {
474
738
  if (!value || typeof value !== "object")
@@ -534,7 +798,7 @@ function isVendorId(v) {
534
798
  function isTaskStatus(s) {
535
799
  return s === "backlog" || s === "in_progress" || s === "in_review" || s === "done" || s === "canceled" || s === "error";
536
800
  }
537
- var CURRENT_VERSION = 3;
801
+ var CURRENT_VERSION2 = 3;
538
802
  var init_store = __esm(() => {
539
803
  init_ulid();
540
804
  });
@@ -3047,286 +3311,68 @@ class KobeDaemonClient {
3047
3311
  reject(err);
3048
3312
  };
3049
3313
  socket.once("connect", onConnect);
3050
- socket.once("error", onError);
3051
- socket.on("data", (chunk) => this.onData(chunk.toString("utf8")));
3052
- socket.on("close", () => this.onSocketClose(socket));
3053
- });
3054
- }
3055
- onSocketClose(which) {
3056
- 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
- }
3314
+ socket.once("error", onError);
3315
+ socket.on("data", (chunk) => this.onData(chunk.toString("utf8")));
3316
+ socket.on("close", () => this.onSocketClose(socket));
3273
3317
  });
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);
3284
3318
  }
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"
3319
+ onSocketClose(which) {
3320
+ if (this.socket !== which)
3321
+ return;
3322
+ this.socket = null;
3323
+ for (const pending of this.pending.values())
3324
+ pending.reject(new Error("daemon connection closed"));
3325
+ this.pending.clear();
3326
+ this.emitLifecycle("close");
3327
+ }
3328
+ emitLifecycle(name) {
3329
+ for (const handler of this.lifecycleHandlers.get(name) ?? []) {
3330
+ try {
3331
+ handler();
3332
+ } catch (err) {
3333
+ console.error(`[kobe] lifecycle handler for "${name}" threw:`, err);
3298
3334
  }
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);
3335
+ }
3336
+ }
3337
+ onData(chunk) {
3338
+ this.buffer += chunk;
3339
+ let nl = this.buffer.indexOf(`
3340
+ `);
3341
+ while (nl !== -1) {
3342
+ const line = this.buffer.slice(0, nl);
3343
+ this.buffer = this.buffer.slice(nl + 1);
3344
+ if (line.trim().length > 0)
3345
+ this.onLine(line);
3346
+ nl = this.buffer.indexOf(`
3347
+ `);
3348
+ }
3349
+ }
3350
+ onLine(line) {
3351
+ const frame = JSON.parse(line);
3352
+ if (frame.type === "event") {
3353
+ this.emit(frame);
3354
+ return;
3355
+ }
3356
+ if (frame.type !== "response")
3357
+ return;
3358
+ const pending = this.pending.get(frame.id);
3359
+ if (!pending)
3360
+ return;
3361
+ this.pending.delete(frame.id);
3362
+ if (frame.error)
3363
+ pending.reject(new Error(frame.error.message));
3364
+ else
3365
+ pending.resolve(frame.payload);
3366
+ }
3367
+ emit(frame) {
3368
+ for (const handler of this.handlers.get(frame.name) ?? [])
3369
+ handler(frame);
3370
+ for (const handler of this.handlers.get("*") ?? [])
3371
+ handler(frame);
3315
3372
  }
3316
3373
  }
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`;
3374
+ var init_client = __esm(() => {
3375
+ init_protocol();
3330
3376
  });
3331
3377
 
3332
3378
  // src/daemon/crash-log.ts
@@ -3386,11 +3432,11 @@ class DaemonEventBus {
3386
3432
  }
3387
3433
 
3388
3434
  // src/daemon/paths.ts
3389
- import { createHash } from "crypto";
3435
+ import { createHash as createHash2 } from "crypto";
3390
3436
  import { homedir as homedir3, tmpdir } from "os";
3391
3437
  import { join as join3 } from "path";
3392
3438
  function shortHomeTag(homeDir2) {
3393
- return createHash("sha1").update(homeDir2).digest("hex").slice(0, 8);
3439
+ return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
3394
3440
  }
3395
3441
  function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
3396
3442
  if (Buffer.byteLength(naturalPath, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
@@ -3952,6 +3998,157 @@ var init_daemon_process = __esm(() => {
3952
3998
  DAEMON_START_ARGS = ["daemon", "start"];
3953
3999
  });
3954
4000
 
4001
+ // src/cli/repo-cmd.ts
4002
+ var exports_repo_cmd = {};
4003
+ __export(exports_repo_cmd, {
4004
+ runRepoSubcommand: () => runRepoSubcommand
4005
+ });
4006
+ import { readFileSync as readFileSync2 } from "fs";
4007
+ import { resolve as resolve3 } from "path";
4008
+ function usageError(message) {
4009
+ process.stderr.write(`kobe repo: ${message}
4010
+
4011
+ ${REPO_USAGE}
4012
+ `);
4013
+ process.exit(2);
4014
+ }
4015
+ function readArgFile(path3) {
4016
+ try {
4017
+ return readFileSync2(resolve3(process.cwd(), path3), "utf8");
4018
+ } catch (err) {
4019
+ usageError(`cannot read ${path3}: ${err instanceof Error ? err.message : String(err)}`);
4020
+ }
4021
+ }
4022
+ function parseRepoArgs(args) {
4023
+ const out = {};
4024
+ for (let i = 0;i < args.length; i++) {
4025
+ const a = args[i];
4026
+ const need = () => {
4027
+ const v = args[++i];
4028
+ if (v === undefined)
4029
+ usageError(`${a} requires a value`);
4030
+ return v;
4031
+ };
4032
+ if (a === "--init-script")
4033
+ out.initScript = need();
4034
+ else if (a === "--init-script-file")
4035
+ out.initScript = readArgFile(need());
4036
+ else if (a === "--init-prompt")
4037
+ out.initPrompt = need();
4038
+ else if (a === "--init-prompt-file")
4039
+ out.initPrompt = readArgFile(need());
4040
+ else if (a.startsWith("-"))
4041
+ usageError(`unknown flag "${a}"`);
4042
+ else if (out.path === undefined)
4043
+ out.path = a;
4044
+ else
4045
+ usageError(`unexpected argument "${a}"`);
4046
+ }
4047
+ return out;
4048
+ }
4049
+ function parseUnsetArgs(args) {
4050
+ let path3;
4051
+ let clearScript = false;
4052
+ let clearPrompt = false;
4053
+ for (const a of args) {
4054
+ if (a === "--init-script")
4055
+ clearScript = true;
4056
+ else if (a === "--init-prompt")
4057
+ clearPrompt = true;
4058
+ else if (a.startsWith("-"))
4059
+ usageError(`unknown flag "${a}"`);
4060
+ else if (path3 === undefined)
4061
+ path3 = a;
4062
+ else
4063
+ usageError(`unexpected argument "${a}"`);
4064
+ }
4065
+ if (!clearScript && !clearPrompt) {
4066
+ clearScript = true;
4067
+ clearPrompt = true;
4068
+ }
4069
+ return { path: path3, clearScript, clearPrompt };
4070
+ }
4071
+ async function runRepoSubcommand(args) {
4072
+ const [verb, ...rest] = args;
4073
+ if (verb === undefined || verb === "--help" || verb === "-h" || verb === "help") {
4074
+ process.stdout.write(`${REPO_USAGE}
4075
+ `);
4076
+ return;
4077
+ }
4078
+ const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
4079
+ const { existsSync: existsSync2 } = await import("fs");
4080
+ const { join: join4 } = await import("path");
4081
+ if (verb === "show") {
4082
+ const [pathArg] = rest.filter((a) => !a.startsWith("-"));
4083
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), pathArg ?? "."));
4084
+ const override = getRepoInitOverride2(repo);
4085
+ const hasFileScript = existsSync2(join4(repo, ".kobe", "init.sh"));
4086
+ const hasFilePrompt = existsSync2(join4(repo, ".kobe", "init-prompt.md"));
4087
+ console.log(`repo: ${repo}`);
4088
+ console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
4089
+ console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
4090
+ console.log(` override initScript: ${override.initScript ? quotePreview(override.initScript) : "(unset)"}`);
4091
+ console.log(` override initPrompt: ${override.initPrompt ? quotePreview(override.initPrompt) : "(unset)"}`);
4092
+ return;
4093
+ }
4094
+ if (verb === "set") {
4095
+ const flags = parseRepoArgs(rest);
4096
+ if (flags.initScript === undefined && flags.initPrompt === undefined) {
4097
+ usageError("set needs at least one of --init-script(-file) / --init-prompt(-file)");
4098
+ }
4099
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), flags.path ?? "."));
4100
+ const next = setRepoInitOverride2(repo, {
4101
+ ...flags.initScript !== undefined ? { initScript: flags.initScript } : {},
4102
+ ...flags.initPrompt !== undefined ? { initPrompt: flags.initPrompt } : {}
4103
+ });
4104
+ console.log(`updated override for ${repo}`);
4105
+ console.log(` initScript: ${next.initScript ? quotePreview(next.initScript) : "(unset)"}`);
4106
+ console.log(` initPrompt: ${next.initPrompt ? quotePreview(next.initPrompt) : "(unset)"}`);
4107
+ return;
4108
+ }
4109
+ if (verb === "unset") {
4110
+ const { path: path3, clearScript, clearPrompt } = parseUnsetArgs(rest);
4111
+ const repo = resolveRepoRoot2(resolve3(process.cwd(), path3 ?? "."));
4112
+ const next = setRepoInitOverride2(repo, {
4113
+ ...clearScript ? { initScript: "" } : {},
4114
+ ...clearPrompt ? { initPrompt: "" } : {}
4115
+ });
4116
+ console.log(`cleared override for ${repo}`);
4117
+ console.log(` initScript: ${next.initScript ? quotePreview(next.initScript) : "(unset)"}`);
4118
+ console.log(` initPrompt: ${next.initPrompt ? quotePreview(next.initPrompt) : "(unset)"}`);
4119
+ return;
4120
+ }
4121
+ usageError(`unknown verb "${verb}"`);
4122
+ }
4123
+ function quotePreview(value) {
4124
+ const oneLine = value.replace(/\s+/g, " ").trim();
4125
+ return oneLine.length > 60 ? `"${oneLine.slice(0, 57)}\u2026"` : `"${oneLine}"`;
4126
+ }
4127
+ var REPO_USAGE;
4128
+ var init_repo_cmd = __esm(() => {
4129
+ REPO_USAGE = [
4130
+ "Usage: kobe repo <show|set|unset> [path] [options]",
4131
+ "",
4132
+ "Manage a repo's per-user init override (state.json fallback for repos",
4133
+ "that don't ship .kobe/init.sh / .kobe/init-prompt.md).",
4134
+ "",
4135
+ "Commands:",
4136
+ " show [path] Print the override + whether the repo ships .kobe/ files",
4137
+ " set [path] <options> Set the init script and/or first prompt",
4138
+ " unset [path] [--init-script] [--init-prompt] Clear one or both (default: both)",
4139
+ "",
4140
+ "Set options (later wins; *-file reads from disk):",
4141
+ " --init-script <text> Inline shell to run before the engine",
4142
+ " --init-script-file <path> Read the init script from a file",
4143
+ " --init-prompt <text> Inline first prompt for the engine",
4144
+ " --init-prompt-file <path> Read the first prompt from a file",
4145
+ "",
4146
+ " path defaults to the current directory (resolved to its git toplevel).",
4147
+ ""
4148
+ ].join(`
4149
+ `);
4150
+ });
4151
+
3955
4152
  // src/engine/interactive-command.ts
3956
4153
  function engineCommandKey(vendor) {
3957
4154
  return `engineCommand.${vendor}`;
@@ -4203,6 +4400,41 @@ var init_client2 = __esm(() => {
4203
4400
  CLAUDE_ROLE_OPTION = PANE_ROLE_OPTION;
4204
4401
  });
4205
4402
 
4403
+ // src/tmux/prompt-delivery.ts
4404
+ async function waitForEnginePane(session, fresh) {
4405
+ let prev = null;
4406
+ for (let attempt = 0;attempt < 24; attempt++) {
4407
+ const pane2 = await claudePaneIdStrict(session);
4408
+ if (pane2) {
4409
+ if (!fresh)
4410
+ return { pane: pane2, ready: true };
4411
+ const cur = (await capturePaneById(pane2)).trim();
4412
+ if (cur.length > 0 && cur === prev)
4413
+ return { pane: pane2, ready: true };
4414
+ prev = cur;
4415
+ }
4416
+ await sleep(250);
4417
+ }
4418
+ const pane = await claudePaneIdStrict(session) || await claudePaneId(session);
4419
+ return { pane, ready: false };
4420
+ }
4421
+ async function pasteAndSubmit(pane, text) {
4422
+ const buffer = `kobe-api-${pane.replace(/[^A-Za-z0-9]/g, "")}`;
4423
+ await runTmux(["set-buffer", "-b", buffer, "--", text]);
4424
+ await runTmux(["paste-buffer", "-p", "-d", "-b", buffer, "-t", pane]);
4425
+ await sendKeyName(pane, "Enter");
4426
+ }
4427
+ async function deliverFirstPrompt(session, prompt) {
4428
+ const { pane } = await waitForEnginePane(session, true);
4429
+ if (!pane)
4430
+ return;
4431
+ await pasteAndSubmit(pane, prompt);
4432
+ }
4433
+ var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
4434
+ var init_prompt_delivery = __esm(() => {
4435
+ init_client2();
4436
+ });
4437
+
4206
4438
  // src/cli/invocation.ts
4207
4439
  import { fileURLToPath as fileURLToPath2 } from "url";
4208
4440
  function kobeCliInvocation() {
@@ -4225,6 +4457,35 @@ function shellQuoteArgv(argv) {
4225
4457
  function keepAlive(cmd) {
4226
4458
  return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
4227
4459
  }
4460
+ function shQuote(s) {
4461
+ return `'${s.replace(/'/g, "'\\''")}'`;
4462
+ }
4463
+ function engineLaunchLine(engineCmd, init) {
4464
+ const tail = keepAlive(engineCmd);
4465
+ const script = init?.initScript?.trim();
4466
+ if (!script)
4467
+ return tail;
4468
+ const group = ["{", script, "}"].join(`
4469
+ `);
4470
+ if (init?.markerPath) {
4471
+ const marker = shQuote(init.markerPath);
4472
+ const markerDir = shQuote(markerDirOf(init.markerPath));
4473
+ return [
4474
+ `if [ ! -f ${marker} ]; then`,
4475
+ group,
4476
+ `if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
4477
+ "fi",
4478
+ tail
4479
+ ].join(`
4480
+ `);
4481
+ }
4482
+ return [group, tail].join(`
4483
+ `);
4484
+ }
4485
+ function markerDirOf(p) {
4486
+ const i = p.lastIndexOf("/");
4487
+ return i <= 0 ? "." : p.slice(0, i);
4488
+ }
4228
4489
  function fallbackOpsScript(cwd) {
4229
4490
  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
4491
  }
@@ -4235,6 +4496,9 @@ function previewWindowCommand(args) {
4235
4496
  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
4497
  return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
4237
4498
  }
4499
+ function updatePageCommand(args) {
4500
+ return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
4501
+ }
4238
4502
  function tasksPaneCommand(cliInvocation) {
4239
4503
  return shellQuoteArgv([...cliInvocation, "tasks"]);
4240
4504
  }
@@ -4256,6 +4520,7 @@ __export(exports_tmux, {
4256
4520
  tmuxAvailable: () => tmuxAvailable,
4257
4521
  sessionExists: () => sessionExists,
4258
4522
  quickCreate: () => quickCreate,
4523
+ openUpdateTab: () => openUpdateTab,
4259
4524
  openSettingsTab: () => openSettingsTab,
4260
4525
  openNewTaskTab: () => openNewTaskTab,
4261
4526
  newChatTab: () => newChatTab,
@@ -4334,7 +4599,10 @@ async function ensureSessionImpl(opts) {
4334
4599
  "-P",
4335
4600
  "-F",
4336
4601
  "#{pane_id}",
4337
- keepAlive(shellQuoteArgv(opts.command))
4602
+ engineLaunchLine(shellQuoteArgv(opts.command), {
4603
+ initScript: opts.initScript,
4604
+ markerPath: opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
4605
+ })
4338
4606
  ]);
4339
4607
  const pane0 = r0.stdout.trim();
4340
4608
  if (!pane0) {
@@ -4378,6 +4646,10 @@ async function ensureSessionImpl(opts) {
4378
4646
  ["bind-key", "f", "run-shell", `${envStr}${invStr} quick-create --session '#{session_name}'`]
4379
4647
  ]);
4380
4648
  await runTmux(["select-pane", "-t", pane0]);
4649
+ const initPrompt = opts.initPrompt?.trim();
4650
+ if (initPrompt) {
4651
+ deliverFirstPrompt(opts.name, initPrompt).catch((err) => console.error("[kobe tmux] init prompt delivery failed:", err));
4652
+ }
4381
4653
  return true;
4382
4654
  }
4383
4655
  async function relaunchEngineInAllWindows(session, cwd, command) {
@@ -4454,10 +4726,10 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
4454
4726
  const claudePane = panes.find((pane) => pane.role === "claude")?.paneId;
4455
4727
  const tasksPane = panes.find((pane) => pane.role === "tasks");
4456
4728
  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]);
4729
+ if (tasksPane && tasksPane.version !== CURRENT_VERSION) {
4730
+ 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
4731
  }
4460
- if (opsPane && claudePane && opsPane.version !== CURRENT_VERSION2) {
4732
+ if (opsPane && claudePane && opsPane.version !== CURRENT_VERSION) {
4461
4733
  commands.push([
4462
4734
  "respawn-pane",
4463
4735
  "-k",
@@ -4472,7 +4744,7 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
4472
4744
  cliInvocation: inv,
4473
4745
  vendor
4474
4746
  }))
4475
- ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION2]);
4747
+ ], ["set-option", "-p", "-t", opsPane.paneId, "@kobe_role", "ops"], ["set-option", "-p", "-t", opsPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
4476
4748
  }
4477
4749
  }
4478
4750
  if (commands.length > 0)
@@ -4535,9 +4807,9 @@ async function buildPanesAround(claudePane, args) {
4535
4807
  `).map((line) => line.trim()).filter(Boolean).map((line) => line.split("=", 2)));
4536
4808
  await runTmuxSequence([
4537
4809
  ...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]] : [],
4810
+ ...ids.tasks ? [["set-option", "-p", "-t", ids.tasks, PANE_VERSION_OPTION, CURRENT_VERSION]] : [],
4539
4811
  ...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]] : []
4812
+ ...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION]] : []
4541
4813
  ]);
4542
4814
  }
4543
4815
  async function newChatTab(session, vendorOverride) {
@@ -4588,6 +4860,16 @@ async function openNewTaskTab(session, defaultRepo) {
4588
4860
  const command = `${envPrefix}${inv.map(shellQuote).join(" ")} new-task${repoArg}`;
4589
4861
  await newWindow(session, { cwd, command, name: "new task" });
4590
4862
  }
4863
+ async function openUpdateTab(session) {
4864
+ if (!await sessionExists(session))
4865
+ return;
4866
+ const sessionOptions = await getSessionOptions(session, ["@kobe_worktree"]);
4867
+ const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
4868
+ const inv = kobeCliInvocation();
4869
+ const envPrefix = inheritedEnvPrefix();
4870
+ const command = `${envPrefix}${updatePageCommand({ cliInvocation: inv })}`;
4871
+ await newWindow(session, { cwd, command, name: "update" });
4872
+ }
4591
4873
  async function rememberSessionVendor(session, taskId, vendor) {
4592
4874
  await setSessionOption(session, "@kobe_vendor", vendor);
4593
4875
  if (!taskId)
@@ -4617,7 +4899,9 @@ var CHAT_TAB_SWITCH_BINDINGS, CHAT_TAB_CLOSE_BINDING, CHAT_TAB_RENAME_BINDING, C
4617
4899
  var init_tmux = __esm(() => {
4618
4900
  init_invocation();
4619
4901
  init_interactive_command();
4902
+ init_env();
4620
4903
  init_client2();
4904
+ init_prompt_delivery();
4621
4905
  init_vendor();
4622
4906
  init_version();
4623
4907
  init_client2();
@@ -4653,6 +4937,43 @@ var init_tmux = __esm(() => {
4653
4937
  ensureSessionLocks = new Map;
4654
4938
  });
4655
4939
 
4940
+ // src/state/repo-init.ts
4941
+ var exports_repo_init = {};
4942
+ __export(exports_repo_init, {
4943
+ resolveRepoInit: () => resolveRepoInit
4944
+ });
4945
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
4946
+ import { join as join4 } from "path";
4947
+ function repoFileScript(worktreePath) {
4948
+ return existsSync2(join4(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
4949
+ }
4950
+ function repoFilePrompt(worktreePath) {
4951
+ const p = join4(worktreePath, INIT_PROMPT_REL);
4952
+ if (!existsSync2(p))
4953
+ return;
4954
+ try {
4955
+ const text = readFileSync3(p, "utf8");
4956
+ return text.trim().length > 0 ? text : undefined;
4957
+ } catch {
4958
+ return;
4959
+ }
4960
+ }
4961
+ function resolveRepoInit(repoRoot, worktreePath) {
4962
+ const override = repoRoot ? getRepoInitOverride(repoRoot) : {};
4963
+ const initScript = repoFileScript(worktreePath) ?? override.initScript;
4964
+ const initPrompt = repoFilePrompt(worktreePath) ?? override.initPrompt;
4965
+ return {
4966
+ initScript: initScript && initScript.trim().length > 0 ? initScript : undefined,
4967
+ initPrompt: initPrompt && initPrompt.trim().length > 0 ? initPrompt : undefined
4968
+ };
4969
+ }
4970
+ var INIT_SCRIPT_REL, INIT_PROMPT_REL;
4971
+ var init_repo_init = __esm(() => {
4972
+ init_repos();
4973
+ INIT_SCRIPT_REL = join4(".kobe", "init.sh");
4974
+ INIT_PROMPT_REL = join4(".kobe", "init-prompt.md");
4975
+ });
4976
+
4656
4977
  // src/tui/panes/sidebar/worktree-changes.ts
4657
4978
  var exports_worktree_changes = {};
4658
4979
  __export(exports_worktree_changes, {
@@ -4708,7 +5029,7 @@ __export(exports_api_cmd, {
4708
5029
  ApiError: () => ApiError,
4709
5030
  API_VERBS: () => API_VERBS
4710
5031
  });
4711
- import { resolve as resolve3 } from "path";
5032
+ import { resolve as resolve4 } from "path";
4712
5033
  function parseFlags(argv) {
4713
5034
  const flags = new Map;
4714
5035
  let pretty = false;
@@ -4822,29 +5143,6 @@ function fail(message, code, exitCode = 1) {
4822
5143
  `);
4823
5144
  process.exit(exitCode);
4824
5145
  }
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
5146
  async function deliverPrompt(client, target, prompt) {
4849
5147
  let worktree = target.worktreePath;
4850
5148
  if (!worktree) {
@@ -4857,12 +5155,15 @@ async function deliverPrompt(client, target, prompt) {
4857
5155
  const existed = await sessionExists(session);
4858
5156
  if (!existed) {
4859
5157
  const { ensureSession: ensureSession2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
5158
+ const { resolveRepoInit: resolveRepoInit2 } = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
5159
+ const init = resolveRepoInit2(target.repo ?? "", worktree);
4860
5160
  const ok = await ensureSession2({
4861
5161
  name: session,
4862
5162
  cwd: worktree,
4863
5163
  command: interactiveEngineCommand(target.vendor),
4864
5164
  taskId: target.id,
4865
- vendor: target.vendor
5165
+ vendor: target.vendor,
5166
+ initScript: init.initScript
4866
5167
  });
4867
5168
  if (!ok)
4868
5169
  throw new ApiError(`failed to start tmux session for ${target.id}`, "SESSION_FAILED");
@@ -4902,7 +5203,12 @@ async function spawnTask(client, parsed) {
4902
5203
  if (!prompt) {
4903
5204
  return { taskId: res.taskId, task: res.task, started: false };
4904
5205
  }
4905
- const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor: res.task.vendor }, prompt);
5206
+ const delivered = await deliverPrompt(client, {
5207
+ id: res.taskId,
5208
+ worktreePath: res.task.worktreePath,
5209
+ vendor: res.task.vendor,
5210
+ repo: res.task.repo
5211
+ }, prompt);
4906
5212
  return {
4907
5213
  taskId: res.taskId,
4908
5214
  task: res.task,
@@ -4923,7 +5229,12 @@ async function send(client, parsed) {
4923
5229
  taskId = active;
4924
5230
  }
4925
5231
  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);
5232
+ const delivered = await deliverPrompt(client, {
5233
+ id: taskId,
5234
+ worktreePath: res.task.worktreePath,
5235
+ vendor: res.task.vendor,
5236
+ repo: res.task.repo
5237
+ }, prompt);
4927
5238
  return {
4928
5239
  ok: true,
4929
5240
  taskId,
@@ -4960,7 +5271,7 @@ async function fanOut(client, parsed) {
4960
5271
  if (baseRef)
4961
5272
  payload.baseRef = baseRef;
4962
5273
  const res = await client.request("task.create", payload);
4963
- const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor }, prompt);
5274
+ const delivered = await deliverPrompt(client, { id: res.taskId, worktreePath: res.task.worktreePath, vendor, repo: res.task.repo }, prompt);
4964
5275
  tasks.push({
4965
5276
  taskId: res.taskId,
4966
5277
  vendor,
@@ -4980,7 +5291,7 @@ async function collect(client, parsed) {
4980
5291
  taskIds = idsFlag.split(",").map((s) => s.trim()).filter(Boolean);
4981
5292
  } else if (repoFlag) {
4982
5293
  const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
4983
- const target = resolveRepoRoot2(resolve3(process.cwd(), repoFlag));
5294
+ const target = resolveRepoRoot2(resolve4(process.cwd(), repoFlag));
4984
5295
  const { tasks } = await client.request("task.list");
4985
5296
  taskIds = tasks.filter((t) => !t.archived && resolveRepoRoot2(t.repo) === target).map((t) => t.id);
4986
5297
  } else {
@@ -5064,11 +5375,12 @@ ${apiUsage()}`, "BAD_VERB", 2);
5064
5375
  client.close();
5065
5376
  }
5066
5377
  }
5067
- var API_VERBS, FANOUT_CAP = 10, ApiError, sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
5378
+ var API_VERBS, FANOUT_CAP = 10, ApiError;
5068
5379
  var init_api_cmd = __esm(() => {
5069
5380
  init_daemon_process();
5070
5381
  init_interactive_command();
5071
5382
  init_client2();
5383
+ init_prompt_delivery();
5072
5384
  init_vendor();
5073
5385
  API_VERBS = ["spawn-task", "fan-out", "send", "get-task", "collect", "list"];
5074
5386
  ApiError = class ApiError extends Error {
@@ -5148,7 +5460,7 @@ async function runUpdateSubcommand(args, deps) {
5148
5460
  return;
5149
5461
  }
5150
5462
  const plan = updatePlan();
5151
- io.stdout.write(`kobe ${CURRENT_VERSION2} -> latest
5463
+ io.stdout.write(`kobe ${CURRENT_VERSION} -> latest
5152
5464
  `);
5153
5465
  io.stdout.write(`running: ${plan.display}
5154
5466
  `);
@@ -5217,10 +5529,10 @@ function validateTheme(value) {
5217
5529
  var init_schema = () => {};
5218
5530
 
5219
5531
  // src/tui/context/theme/loader.ts
5220
- import { readFileSync as readFileSync2, readdirSync } from "fs";
5221
- import { join as join4 } from "path";
5532
+ import { readFileSync as readFileSync4, readdirSync } from "fs";
5533
+ import { join as join5 } from "path";
5222
5534
  function userThemesDir() {
5223
- return join4(kobeStateDir(), "themes");
5535
+ return join5(kobeStateDir(), "themes");
5224
5536
  }
5225
5537
  function loadUserThemes() {
5226
5538
  const dir = userThemesDir();
@@ -5234,10 +5546,10 @@ function loadUserThemes() {
5234
5546
  for (const file of entries) {
5235
5547
  if (!file.endsWith(".json"))
5236
5548
  continue;
5237
- const path3 = join4(dir, file);
5549
+ const path3 = join5(dir, file);
5238
5550
  let parsed;
5239
5551
  try {
5240
- const text = readFileSync2(path3, "utf8");
5552
+ const text = readFileSync4(path3, "utf8");
5241
5553
  parsed = JSON.parse(text);
5242
5554
  } catch (err) {
5243
5555
  const msg = err instanceof Error ? err.message : String(err);
@@ -5264,8 +5576,8 @@ var exports_theme = {};
5264
5576
  __export(exports_theme, {
5265
5577
  runThemeSubcommand: () => runThemeSubcommand
5266
5578
  });
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";
5579
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
5580
+ import { basename as basename3, join as join6, resolve as resolve5 } from "path";
5269
5581
  function fail2(message) {
5270
5582
  process.stderr.write(`kobe theme: ${message}
5271
5583
  `);
@@ -5296,7 +5608,7 @@ function listThemes() {
5296
5608
  } else {
5297
5609
  for (const f of userFiles) {
5298
5610
  const name = f.slice(0, -".json".length);
5299
- const path3 = join5(dir, f);
5611
+ const path3 = join6(dir, f);
5300
5612
  const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
5301
5613
  lines.push(` ${name}${overridesBundled} ${path3}`);
5302
5614
  }
@@ -5322,10 +5634,10 @@ async function readSource(source) {
5322
5634
  const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
5323
5635
  return { text: text2, defaultName: defaultName2 };
5324
5636
  }
5325
- const abs = resolve4(process.cwd(), source);
5637
+ const abs = resolve5(process.cwd(), source);
5326
5638
  let text;
5327
5639
  try {
5328
- text = readFileSync3(abs, "utf8");
5640
+ text = readFileSync5(abs, "utf8");
5329
5641
  } catch (err) {
5330
5642
  fail2(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
5331
5643
  }
@@ -5386,8 +5698,8 @@ async function addTheme(args) {
5386
5698
  }
5387
5699
  const dir = userThemesDir();
5388
5700
  mkdirSync3(dir, { recursive: true });
5389
- const dest = join5(dir, `${name}.json`);
5390
- if (existsSync2(dest) && !opts.force) {
5701
+ const dest = join6(dir, `${name}.json`);
5702
+ if (existsSync3(dest) && !opts.force) {
5391
5703
  fail2(`${dest} already exists (pass --force to overwrite)`);
5392
5704
  }
5393
5705
  writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
@@ -5404,8 +5716,8 @@ function removeTheme(args) {
5404
5716
  if (BUNDLED_NAMES.includes(name)) {
5405
5717
  fail2(`"${name}" is a built-in theme and cannot be removed`);
5406
5718
  }
5407
- const dest = join5(userThemesDir(), `${name}.json`);
5408
- if (!existsSync2(dest)) {
5719
+ const dest = join6(userThemesDir(), `${name}.json`);
5720
+ if (!existsSync3(dest)) {
5409
5721
  fail2(`no user theme named "${name}" (looked for ${dest})`);
5410
5722
  }
5411
5723
  unlinkSync(dest);
@@ -5588,16 +5900,16 @@ var init_daemon_cmd = __esm(() => {
5588
5900
  });
5589
5901
 
5590
5902
  // src/lib/skill-install.ts
5591
- import { existsSync as existsSync3 } from "fs";
5903
+ import { existsSync as existsSync4 } from "fs";
5592
5904
  import { homedir as homedir5 } from "os";
5593
- import { join as join6 } from "path";
5905
+ import { join as join7 } from "path";
5594
5906
  function kobeSkillPaths(opts = {}) {
5595
5907
  const home = opts.home ?? homedir5();
5596
5908
  const cwd = opts.cwd ?? process.cwd();
5597
- return [join6(home, SKILL_REL_PATH), join6(cwd, SKILL_REL_PATH)];
5909
+ return [join7(home, SKILL_REL_PATH), join7(cwd, SKILL_REL_PATH)];
5598
5910
  }
5599
5911
  function isKobeSkillInstalled(opts) {
5600
- return kobeSkillPaths(opts).some((p) => existsSync3(p));
5912
+ return kobeSkillPaths(opts).some((p) => existsSync4(p));
5601
5913
  }
5602
5914
  function maybeHintSkillInstall() {
5603
5915
  if (isKobeSkillInstalled())
@@ -5623,9 +5935,9 @@ __export(exports_maintenance, {
5623
5935
  runResetSubcommand: () => runResetSubcommand,
5624
5936
  runDoctorSubcommand: () => runDoctorSubcommand
5625
5937
  });
5626
- import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "fs";
5938
+ import { existsSync as existsSync5, readFileSync as readFileSync6, statSync } from "fs";
5627
5939
  import { unlink as unlink4 } from "fs/promises";
5628
- import { join as join7 } from "path";
5940
+ import { join as join8 } from "path";
5629
5941
  import { createInterface } from "readline";
5630
5942
  function isProcessAlive2(pid) {
5631
5943
  try {
@@ -5672,7 +5984,7 @@ function describeFile(path3) {
5672
5984
  }
5673
5985
  function taskCount(tasksPath) {
5674
5986
  try {
5675
- const parsed = JSON.parse(readFileSync4(tasksPath, "utf8"));
5987
+ const parsed = JSON.parse(readFileSync6(tasksPath, "utf8"));
5676
5988
  return Array.isArray(parsed.tasks) ? parsed.tasks.length : null;
5677
5989
  } catch {
5678
5990
  return null;
@@ -5680,7 +5992,7 @@ function taskCount(tasksPath) {
5680
5992
  }
5681
5993
  function tailFile(path3, n) {
5682
5994
  try {
5683
- const lines = readFileSync4(path3, "utf8").split(`
5995
+ const lines = readFileSync6(path3, "utf8").split(`
5684
5996
  `).filter((l) => l.trim().length > 0);
5685
5997
  return lines.slice(-n).join(`
5686
5998
  `);
@@ -5722,7 +6034,7 @@ async function runDoctorSubcommand(argv = []) {
5722
6034
  const socketPath = defaultDaemonSocketPath();
5723
6035
  const pidPath = defaultDaemonPidPath();
5724
6036
  const logPath = defaultDaemonLogPath();
5725
- const tasksPath = join7(kobeStateDir(), "tasks.json");
6037
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
5726
6038
  const statePath2 = kvStatePath();
5727
6039
  const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
5728
6040
  const status = await probeDaemonStatus(socketPath);
@@ -5743,7 +6055,7 @@ async function runDoctorSubcommand(argv = []) {
5743
6055
  } else {
5744
6056
  out.push("daemon: \u2717 not running (no pidfile)");
5745
6057
  }
5746
- if (existsSync4(socketPath))
6058
+ if (existsSync5(socketPath))
5747
6059
  out.push(` orphan socket file present: ${socketPath}`);
5748
6060
  const tail = tailFile(logPath, 8);
5749
6061
  if (tail) {
@@ -5777,7 +6089,7 @@ async function runDoctorSubcommand(argv = []) {
5777
6089
  async function confirmTty(prompt) {
5778
6090
  const rl = createInterface({ input: process.stdin, output: process.stdout });
5779
6091
  try {
5780
- const answer = await new Promise((resolve5) => rl.question(prompt, resolve5));
6092
+ const answer = await new Promise((resolve6) => rl.question(prompt, resolve6));
5781
6093
  return /^y(es)?$/i.test(answer.trim());
5782
6094
  } finally {
5783
6095
  rl.close();
@@ -5829,7 +6141,7 @@ async function runResetSubcommand(argv) {
5829
6141
  const yes = argv.includes("--yes") || argv.includes("-y");
5830
6142
  const socketPath = defaultDaemonSocketPath();
5831
6143
  const pidPath = defaultDaemonPidPath();
5832
- const tasksPath = join7(kobeStateDir(), "tasks.json");
6144
+ const tasksPath = join8(kobeStateDir(), "tasks.json");
5833
6145
  const statePath2 = kvStatePath();
5834
6146
  console.log("kobe reset will:");
5835
6147
  console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
@@ -9004,7 +9316,7 @@ function addTheme2(name, theme) {
9004
9316
  }
9005
9317
  function resolveTheme(theme, mode = "dark") {
9006
9318
  const defs = theme.defs ?? {};
9007
- function resolve5(c, chain = []) {
9319
+ function resolve6(c, chain = []) {
9008
9320
  if (typeof c === "string") {
9009
9321
  if (c === "transparent" || c === "none")
9010
9322
  return RGBA.fromInts(0, 0, 0, 0);
@@ -9016,13 +9328,13 @@ function resolveTheme(theme, mode = "dark") {
9016
9328
  const next = defs[c] ?? theme.theme[c];
9017
9329
  if (next === undefined)
9018
9330
  return RGBA.fromInts(0, 0, 0);
9019
- return resolve5(next, [...chain, c]);
9331
+ return resolve6(next, [...chain, c]);
9020
9332
  }
9021
- return resolve5(c[mode], chain);
9333
+ return resolve6(c[mode], chain);
9022
9334
  }
9023
9335
  const out = {};
9024
9336
  for (const [k, v] of Object.entries(theme.theme)) {
9025
- out[k] = resolve5(v);
9337
+ out[k] = resolve6(v);
9026
9338
  }
9027
9339
  const text = out.text ?? RGBA.fromHex("#ffffff");
9028
9340
  const background = out.background ?? RGBA.fromHex("#000000");
@@ -9793,7 +10105,7 @@ function findAvailableFolderName(parentDir, base) {
9793
10105
  return trimmed;
9794
10106
  }
9795
10107
  function cloneRepo(url, target, onProgress) {
9796
- return new Promise((resolve5) => {
10108
+ return new Promise((resolve6) => {
9797
10109
  let stderrBuf = "";
9798
10110
  try {
9799
10111
  const child = spawn2("git", ["clone", "--progress", url, target], {
@@ -9810,18 +10122,18 @@ function cloneRepo(url, target, onProgress) {
9810
10122
  }
9811
10123
  });
9812
10124
  child.on("error", (err) => {
9813
- resolve5({ ok: false, error: err.message });
10125
+ resolve6({ ok: false, error: err.message });
9814
10126
  });
9815
10127
  child.on("close", (code) => {
9816
10128
  if (code === 0) {
9817
- resolve5({ ok: true, path: target });
10129
+ resolve6({ ok: true, path: target });
9818
10130
  return;
9819
10131
  }
9820
10132
  const tail = stderrBuf.split(/[\r\n]+/).filter((s) => s.trim().length > 0).pop() ?? `git clone exited with ${code}`;
9821
- resolve5({ ok: false, error: tail });
10133
+ resolve6({ ok: false, error: tail });
9822
10134
  });
9823
10135
  } catch (err) {
9824
- resolve5({ ok: false, error: err instanceof Error ? err.message : String(err) });
10136
+ resolve6({ ok: false, error: err instanceof Error ? err.message : String(err) });
9825
10137
  }
9826
10138
  });
9827
10139
  }
@@ -10884,7 +11196,7 @@ var init_dialog2 = __esm(() => {
10884
11196
 
10885
11197
  // src/tui/component/new-task-dialog/index.tsx
10886
11198
  function show(dialog, defaultRepo, savedRepos, options) {
10887
- return new Promise((resolve5) => {
11199
+ return new Promise((resolve6) => {
10888
11200
  dialog.replace(() => createComponent2(NewTaskDialogView, {
10889
11201
  defaultRepo,
10890
11202
  savedRepos,
@@ -10897,9 +11209,9 @@ function show(dialog, defaultRepo, savedRepos, options) {
10897
11209
  get discoverAdoptable() {
10898
11210
  return options?.discoverAdoptable;
10899
11211
  },
10900
- onSubmit: (v) => resolve5(v),
10901
- onCancel: () => resolve5(undefined)
10902
- }), () => resolve5(undefined));
11212
+ onSubmit: (v) => resolve6(v),
11213
+ onCancel: () => resolve6(undefined)
11214
+ }), () => resolve6(undefined));
10903
11215
  dialog.setSize("medium");
10904
11216
  });
10905
11217
  }
@@ -10991,15 +11303,15 @@ var init_dialog3 = __esm(() => {
10991
11303
 
10992
11304
  // src/tui/component/rename-task-dialog/index.tsx
10993
11305
  function show2(dialog, currentTitle, opts = {}) {
10994
- return new Promise((resolve5) => {
11306
+ return new Promise((resolve6) => {
10995
11307
  dialog.replace(() => createComponent2(RenameTaskDialogView, {
10996
11308
  currentTitle,
10997
11309
  get dialogTitle() {
10998
11310
  return opts.dialogTitle;
10999
11311
  },
11000
- onSubmit: (v) => resolve5(v),
11001
- onCancel: () => resolve5(undefined)
11002
- }), () => resolve5(undefined));
11312
+ onSubmit: (v) => resolve6(v),
11313
+ onCancel: () => resolve6(undefined)
11314
+ }), () => resolve6(undefined));
11003
11315
  });
11004
11316
  }
11005
11317
  var RenameTaskDialog;
@@ -11013,7 +11325,7 @@ var init_rename_task_dialog = __esm(() => {
11013
11325
 
11014
11326
  // src/engine/claude-code-local/binary.ts
11015
11327
  import { spawnSync as spawnSync5 } from "child_process";
11016
- import { existsSync as existsSync6, statSync as statSync3 } from "fs";
11328
+ import { existsSync as existsSync7, statSync as statSync3 } from "fs";
11017
11329
  import { homedir as homedir7 } from "os";
11018
11330
  import path4 from "path";
11019
11331
  async function findClaudeBinary(deps = defaultDeps) {
@@ -11100,7 +11412,7 @@ var init_binary = __esm(() => {
11100
11412
  return;
11101
11413
  if (first.startsWith("claude:") && first.includes("aliased to")) {
11102
11414
  const aliasTarget = first.split("aliased to")[1]?.trim();
11103
- return aliasTarget && existsSync6(aliasTarget) ? aliasTarget : undefined;
11415
+ return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
11104
11416
  }
11105
11417
  return first;
11106
11418
  },
@@ -11117,7 +11429,7 @@ var init_binary = __esm(() => {
11117
11429
 
11118
11430
  // src/engine/codex-local/binary.ts
11119
11431
  import { spawnSync as spawnSync6 } from "child_process";
11120
- import { existsSync as existsSync7, statSync as statSync4 } from "fs";
11432
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
11121
11433
  import { homedir as homedir8 } from "os";
11122
11434
  import path5 from "path";
11123
11435
  async function findCodexBinary(deps = defaultDeps2) {
@@ -11188,7 +11500,7 @@ var init_binary2 = __esm(() => {
11188
11500
  return;
11189
11501
  if (first.startsWith("codex:") && first.includes("aliased to")) {
11190
11502
  const aliasTarget = first.split("aliased to")[1]?.trim();
11191
- return aliasTarget && existsSync7(aliasTarget) ? aliasTarget : undefined;
11503
+ return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
11192
11504
  }
11193
11505
  return first;
11194
11506
  },
@@ -11205,7 +11517,7 @@ var init_binary2 = __esm(() => {
11205
11517
 
11206
11518
  // src/engine/copilot-local/binary.ts
11207
11519
  import { spawnSync as spawnSync7 } from "child_process";
11208
- import { existsSync as existsSync8, statSync as statSync5 } from "fs";
11520
+ import { existsSync as existsSync9, statSync as statSync5 } from "fs";
11209
11521
  import { homedir as homedir9 } from "os";
11210
11522
  import path6 from "path";
11211
11523
  async function findCopilotBinary(deps = defaultDeps3) {
@@ -11300,7 +11612,7 @@ var init_binary3 = __esm(() => {
11300
11612
  return;
11301
11613
  if (first.startsWith("copilot:") && first.includes("aliased to")) {
11302
11614
  const aliasTarget = first.split("aliased to")[1]?.trim();
11303
- return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
11615
+ return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
11304
11616
  }
11305
11617
  return first;
11306
11618
  },
@@ -11311,7 +11623,7 @@ var init_binary3 = __esm(() => {
11311
11623
  });
11312
11624
 
11313
11625
  // src/engine/account-detect.ts
11314
- import { readFileSync as readFileSync5, statSync as statSync6 } from "fs";
11626
+ import { readFileSync as readFileSync7, statSync as statSync6 } from "fs";
11315
11627
  import { homedir as homedir10 } from "os";
11316
11628
  import path7 from "path";
11317
11629
  function claudeGlobalConfigPath(env, home) {
@@ -11524,7 +11836,7 @@ var init_account_detect = __esm(() => {
11524
11836
  return null;
11525
11837
  throw err;
11526
11838
  }
11527
- return readFileSync5(p, "utf8");
11839
+ return readFileSync7(p, "utf8");
11528
11840
  },
11529
11841
  env(name) {
11530
11842
  return process.env[name];
@@ -11660,18 +11972,18 @@ var init_dialog_confirm = __esm(() => {
11660
11972
  init_keymap();
11661
11973
  init_dialog();
11662
11974
  DialogConfirm.show = (dialog, title, message, label, confirmLabel, options) => {
11663
- return new Promise((resolve5) => {
11975
+ return new Promise((resolve6) => {
11664
11976
  dialog.replace(() => createComponent2(DialogConfirm, {
11665
11977
  title,
11666
11978
  message,
11667
- onConfirm: () => resolve5(true),
11668
- onCancel: () => resolve5(false),
11979
+ onConfirm: () => resolve6(true),
11980
+ onCancel: () => resolve6(false),
11669
11981
  label,
11670
11982
  confirmLabel,
11671
11983
  get initialActive() {
11672
11984
  return options?.initialActive;
11673
11985
  }
11674
- }), () => resolve5(undefined));
11986
+ }), () => resolve6(undefined));
11675
11987
  dialog.setSize("small");
11676
11988
  });
11677
11989
  };
@@ -11679,7 +11991,7 @@ var init_dialog_confirm = __esm(() => {
11679
11991
 
11680
11992
  // src/tui/component/settings-dialog/actions.ts
11681
11993
  import { unlinkSync as unlinkSync2 } from "fs";
11682
- import { join as join9 } from "path";
11994
+ import { join as join10 } from "path";
11683
11995
  function hasRestartableDaemon(orchestrator) {
11684
11996
  return orchestrator instanceof RemoteOrchestrator;
11685
11997
  }
@@ -11696,7 +12008,7 @@ async function confirmResetState(dialog, kv, renderer) {
11696
12008
  return;
11697
12009
  kv.clear();
11698
12010
  try {
11699
- unlinkSync2(join9(homeDir(), ".kobe", "tasks.json"));
12011
+ unlinkSync2(join10(homeDir(), ".kobe", "tasks.json"));
11700
12012
  } catch (err) {
11701
12013
  if (err.code !== "ENOENT") {
11702
12014
  console.error("kobe: failed to delete tasks.json during reset:", err);
@@ -12853,12 +13165,12 @@ var init_settings_dialog = __esm(() => {
12853
13165
  init_model();
12854
13166
  init_sections();
12855
13167
  SettingsDialog.show = (dialog, kv, orchestrator) => {
12856
- return new Promise((resolve5) => {
13168
+ return new Promise((resolve6) => {
12857
13169
  dialog.replace(() => createComponent2(SettingsDialog, {
12858
13170
  kv,
12859
13171
  orchestrator,
12860
- onClose: () => resolve5()
12861
- }), () => resolve5());
13172
+ onClose: () => resolve6()
13173
+ }), () => resolve6());
12862
13174
  });
12863
13175
  };
12864
13176
  });
@@ -12925,12 +13237,12 @@ var init_focus = __esm(() => {
12925
13237
  });
12926
13238
 
12927
13239
  // src/tui/context/kv.tsx
12928
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
13240
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
12929
13241
  import { dirname as dirname5 } from "path";
12930
13242
  function loadInitial() {
12931
13243
  const statePath2 = kvStatePath();
12932
13244
  try {
12933
- const text = readFileSync6(statePath2, "utf8");
13245
+ const text = readFileSync8(statePath2, "utf8");
12934
13246
  const parsed = JSON.parse(text);
12935
13247
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
12936
13248
  return parsed;
@@ -13014,10 +13326,10 @@ var init_kv = __esm(() => {
13014
13326
  });
13015
13327
 
13016
13328
  // src/tui/lib/persisted-ui-prefs.ts
13017
- import { readFileSync as readFileSync7 } from "fs";
13329
+ import { readFileSync as readFileSync9 } from "fs";
13018
13330
  function readPersistedUiPrefs(fallbackTheme) {
13019
13331
  try {
13020
- const parsed = JSON.parse(readFileSync7(kvStatePath(), "utf8"));
13332
+ const parsed = JSON.parse(readFileSync9(kvStatePath(), "utf8"));
13021
13333
  const theme = typeof parsed.activeTheme === "string" && hasTheme(parsed.activeTheme) ? parsed.activeTheme : fallbackTheme;
13022
13334
  const transparent = parsed.transparentBackground === true;
13023
13335
  const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent) ? parsed.focusAccent : null;
@@ -13033,8 +13345,8 @@ var init_persisted_ui_prefs = __esm(() => {
13033
13345
 
13034
13346
  // src/tui/lib/worktree-opener.ts
13035
13347
  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";
13348
+ import { existsSync as existsSync10 } from "fs";
13349
+ import { basename as basename4, delimiter, isAbsolute, join as join11 } from "path";
13038
13350
  function executableOnPath(command, env, exists) {
13039
13351
  if (isAbsolute(command))
13040
13352
  return exists(command);
@@ -13042,7 +13354,7 @@ function executableOnPath(command, env, exists) {
13042
13354
  for (const dir of pathEnv.split(delimiter)) {
13043
13355
  if (!dir)
13044
13356
  continue;
13045
- if (exists(join10(dir, command)))
13357
+ if (exists(join11(dir, command)))
13046
13358
  return true;
13047
13359
  }
13048
13360
  return false;
@@ -13062,7 +13374,7 @@ function labelForOverride(command) {
13062
13374
  function detectWorktreeOpener(deps = {}) {
13063
13375
  const env = deps.env ?? process.env;
13064
13376
  const platform = deps.platform ?? process.platform;
13065
- const exists = deps.exists ?? existsSync9;
13377
+ const exists = deps.exists ?? existsSync10;
13066
13378
  const override = env.KOBE_OPEN_EDITOR?.trim();
13067
13379
  if (override) {
13068
13380
  return { id: "env", label: labelForOverride(override), command: override, args: [] };
@@ -14563,7 +14875,7 @@ var exports_host = {};
14563
14875
  __export(exports_host, {
14564
14876
  startTasksPane: () => startTasksPane
14565
14877
  });
14566
- import { existsSync as existsSync10 } from "fs";
14878
+ import { existsSync as existsSync11 } from "fs";
14567
14879
  import { TextAttributes as TextAttributes9 } from "@opentui/core";
14568
14880
  function TasksShell(props) {
14569
14881
  const themeCtx = useTheme();
@@ -14654,6 +14966,15 @@ function TasksShell(props) {
14654
14966
  }
14655
14967
  SettingsDialog.show(dialog, kv, props.orch ?? undefined);
14656
14968
  }
14969
+ async function openUpdate() {
14970
+ const info = updateInfo();
14971
+ if (!info?.hasUpdate)
14972
+ return;
14973
+ const session = await currentSessionName();
14974
+ if (!session)
14975
+ return;
14976
+ await openUpdateTab(session);
14977
+ }
14657
14978
  async function archiveTask(id) {
14658
14979
  const task = props.tasks().find((t) => t.id === id);
14659
14980
  if (!task || !props.orch)
@@ -14760,7 +15081,7 @@ function TasksShell(props) {
14760
15081
  async function openSelectedWorktree(id) {
14761
15082
  const task = props.tasks().find((t) => t.id === id);
14762
15083
  let worktree = task?.worktreePath;
14763
- if (!worktree || !existsSync10(worktree)) {
15084
+ if (!worktree || !existsSync11(worktree)) {
14764
15085
  if (!props.orch) {
14765
15086
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
14766
15087
  return;
@@ -14773,7 +15094,7 @@ function TasksShell(props) {
14773
15094
  }
14774
15095
  await props.reload();
14775
15096
  }
14776
- if (!worktree || !existsSync10(worktree))
15097
+ if (!worktree || !existsSync11(worktree))
14777
15098
  return;
14778
15099
  const opener = detectWorktreeOpener();
14779
15100
  if (!opener) {
@@ -14792,6 +15113,9 @@ function TasksShell(props) {
14792
15113
  }, {
14793
15114
  key: "s",
14794
15115
  cmd: () => void openSettings()
15116
+ }, {
15117
+ key: "u",
15118
+ cmd: () => void openUpdate()
14795
15119
  }, {
14796
15120
  key: "o",
14797
15121
  cmd: () => {
@@ -14821,7 +15145,7 @@ function TasksShell(props) {
14821
15145
  const exists = await sessionExists(name);
14822
15146
  if (exists) {
14823
15147
  const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
14824
- if (cwd2 && existsSync10(cwd2)) {
15148
+ if (cwd2 && existsSync11(cwd2)) {
14825
15149
  await ensureSession({
14826
15150
  name,
14827
15151
  cwd: cwd2,
@@ -14835,7 +15159,7 @@ function TasksShell(props) {
14835
15159
  return;
14836
15160
  }
14837
15161
  let cwd = task?.worktreePath;
14838
- if (!cwd || !existsSync10(cwd)) {
15162
+ if (!cwd || !existsSync11(cwd)) {
14839
15163
  if (!props.orch) {
14840
15164
  console.error("[kobe tasks] no daemon; cannot materialise worktree");
14841
15165
  return;
@@ -14848,14 +15172,17 @@ function TasksShell(props) {
14848
15172
  }
14849
15173
  await props.reload();
14850
15174
  }
14851
- if (!cwd || !existsSync10(cwd))
15175
+ if (!cwd || !existsSync11(cwd))
14852
15176
  return;
15177
+ const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
14853
15178
  const ready = await ensureSession({
14854
15179
  name,
14855
15180
  cwd,
14856
15181
  command: interactiveEngineCommand(task?.vendor),
14857
15182
  taskId: id,
14858
- vendor: task?.vendor
15183
+ vendor: task?.vendor,
15184
+ initScript: init2.initScript,
15185
+ initPrompt: init2.initPrompt
14859
15186
  });
14860
15187
  if (!ready) {
14861
15188
  console.error(`[kobe tasks] failed to start session ${name}`);
@@ -14886,7 +15213,8 @@ function TasksShell(props) {
14886
15213
  focused: () => dialog.stack.length === 0
14887
15214
  }));
14888
15215
  insert(_el$, createComponent2(ShortcutHints, {
14889
- updateInfo
15216
+ updateInfo,
15217
+ onOpenUpdate: () => void openUpdate()
14890
15218
  }), null);
14891
15219
  effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
14892
15220
  return _el$;
@@ -14899,8 +15227,8 @@ function ShortcutHints(props) {
14899
15227
  const updateLabel = createMemo(() => {
14900
15228
  const info = props.updateInfo();
14901
15229
  if (!info)
14902
- return `v${CURRENT_VERSION2}`;
14903
- return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION2} latest`;
15230
+ return `v${CURRENT_VERSION}`;
15231
+ return info.hasUpdate ? `v${info.latest} available` : `v${CURRENT_VERSION} latest`;
14904
15232
  });
14905
15233
  const HINTS = [{
14906
15234
  k: "\u23CE",
@@ -14952,10 +15280,10 @@ function ShortcutHints(props) {
14952
15280
  label: "detach"
14953
15281
  }];
14954
15282
  return (() => {
14955
- var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$0 = createElement("text");
15283
+ var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("box"), _el$7 = createElement("text"), _el$12 = createElement("text");
14956
15284
  insertNode(_el$3, _el$4);
14957
15285
  insertNode(_el$3, _el$6);
14958
- insertNode(_el$3, _el$0);
15286
+ insertNode(_el$3, _el$12);
14959
15287
  setProp(_el$3, "flexShrink", 0);
14960
15288
  setProp(_el$3, "flexDirection", "column");
14961
15289
  setProp(_el$3, "paddingLeft", 1);
@@ -14968,68 +15296,86 @@ function ShortcutHints(props) {
14968
15296
  setProp(_el$6, "flexDirection", "row");
14969
15297
  setProp(_el$6, "gap", 1);
14970
15298
  setProp(_el$7, "wrapMode", "none");
15299
+ setProp(_el$7, "onMouseUp", () => {
15300
+ if (props.updateInfo()?.hasUpdate)
15301
+ props.onOpenUpdate();
15302
+ });
14971
15303
  insert(_el$7, updateLabel);
14972
15304
  insert(_el$3, createComponent2(Show, {
14973
15305
  get when() {
14974
15306
  return props.updateInfo()?.hasUpdate;
14975
15307
  },
14976
15308
  get children() {
14977
- var _el$8 = createElement("text");
14978
- insertNode(_el$8, createTextNode(`run: kobe update`));
14979
- setProp(_el$8, "wrapMode", "none");
15309
+ var _el$8 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$10 = createElement("text");
15310
+ insertNode(_el$8, _el$9);
15311
+ insertNode(_el$8, _el$10);
15312
+ setProp(_el$8, "flexDirection", "row");
15313
+ setProp(_el$8, "gap", 1);
15314
+ setProp(_el$8, "onMouseUp", () => props.onOpenUpdate());
15315
+ insertNode(_el$9, _el$0);
15316
+ setProp(_el$9, "width", 10);
15317
+ setProp(_el$9, "flexShrink", 0);
15318
+ insertNode(_el$0, createTextNode(`[U]`));
15319
+ setProp(_el$0, "wrapMode", "none");
15320
+ insertNode(_el$10, createTextNode(`update page`));
15321
+ setProp(_el$10, "wrapMode", "none");
14980
15322
  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));
15323
+ var _v$ = theme.accent, _v$2 = TextAttributes9.BOLD, _v$3 = theme.accent, _v$4 = TextAttributes9.DIM;
15324
+ _v$ !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$, _p$.e));
15325
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$2, _p$.t));
15326
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$10, "fg", _v$3, _p$.a));
15327
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$10, "attributes", _v$4, _p$.o));
14984
15328
  return _p$;
14985
15329
  }, {
14986
15330
  e: undefined,
14987
- t: undefined
15331
+ t: undefined,
15332
+ a: undefined,
15333
+ o: undefined
14988
15334
  });
14989
15335
  return _el$8;
14990
15336
  }
14991
- }), _el$0);
14992
- insertNode(_el$0, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
14993
- setProp(_el$0, "wrapMode", "none");
15337
+ }), _el$12);
15338
+ insertNode(_el$12, createTextNode(`\u2500\u2500 keys \u2500\u2500`));
15339
+ setProp(_el$12, "wrapMode", "none");
14994
15340
  insert(_el$3, createComponent2(For, {
14995
15341
  each: HINTS,
14996
15342
  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);
15343
+ var _el$14 = createElement("box"), _el$15 = createElement("box"), _el$16 = createElement("text"), _el$17 = createTextNode(`[`), _el$18 = createTextNode(`]`), _el$19 = createElement("text");
15344
+ insertNode(_el$14, _el$15);
15345
+ insertNode(_el$14, _el$19);
15346
+ setProp(_el$14, "flexDirection", "row");
15347
+ setProp(_el$14, "gap", 1);
15348
+ insertNode(_el$15, _el$16);
15349
+ setProp(_el$15, "width", 10);
15350
+ setProp(_el$15, "flexShrink", 0);
15351
+ insertNode(_el$16, _el$17);
15352
+ insertNode(_el$16, _el$18);
15353
+ setProp(_el$16, "wrapMode", "none");
15354
+ insert(_el$16, () => h.k, _el$18);
15355
+ setProp(_el$19, "wrapMode", "none");
15356
+ insert(_el$19, () => h.label);
15011
15357
  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));
15358
+ var _v$1 = theme.accent, _v$10 = TextAttributes9.BOLD, _v$11 = theme.textMuted;
15359
+ _v$1 !== _p$.e && (_p$.e = setProp(_el$16, "fg", _v$1, _p$.e));
15360
+ _v$10 !== _p$.t && (_p$.t = setProp(_el$16, "attributes", _v$10, _p$.t));
15361
+ _v$11 !== _p$.a && (_p$.a = setProp(_el$19, "fg", _v$11, _p$.a));
15016
15362
  return _p$;
15017
15363
  }, {
15018
15364
  e: undefined,
15019
15365
  t: undefined,
15020
15366
  a: undefined
15021
15367
  });
15022
- return _el$10;
15368
+ return _el$14;
15023
15369
  })()
15024
15370
  }), null);
15025
15371
  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));
15372
+ 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;
15373
+ _v$5 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$5, _p$.e));
15374
+ _v$6 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$6, _p$.t));
15375
+ _v$7 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$7, _p$.a));
15376
+ _v$8 !== _p$.o && (_p$.o = setProp(_el$7, "attributes", _v$8, _p$.o));
15377
+ _v$9 !== _p$.i && (_p$.i = setProp(_el$12, "fg", _v$9, _p$.i));
15378
+ _v$0 !== _p$.n && (_p$.n = setProp(_el$12, "attributes", _v$0, _p$.n));
15033
15379
  return _p$;
15034
15380
  }, {
15035
15381
  e: undefined,
@@ -15134,6 +15480,7 @@ var init_host = __esm(() => {
15134
15480
  init_env();
15135
15481
  init_errors();
15136
15482
  init_store();
15483
+ init_repo_init();
15137
15484
  init_repos();
15138
15485
  init_vendor();
15139
15486
  init_version();
@@ -15417,6 +15764,415 @@ var init_host3 = __esm(() => {
15417
15764
  init_dialog();
15418
15765
  });
15419
15766
 
15767
+ // src/tui/update/host.tsx
15768
+ var exports_host4 = {};
15769
+ __export(exports_host4, {
15770
+ startUpdateHost: () => startUpdateHost
15771
+ });
15772
+ import { spawn as spawn4, spawnSync as spawnSync9 } from "child_process";
15773
+ import { TextAttributes as TextAttributes10 } from "@opentui/core";
15774
+ function openExternalUrl(url) {
15775
+ if (!url)
15776
+ return false;
15777
+ const platform = process.platform;
15778
+ const [command, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
15779
+ try {
15780
+ const child = spawn4(command, args, {
15781
+ stdio: "ignore",
15782
+ detached: true
15783
+ });
15784
+ child.unref();
15785
+ return true;
15786
+ } catch {
15787
+ return false;
15788
+ }
15789
+ }
15790
+ function releaseBodyLines(body) {
15791
+ return body.replace(/\r\n/g, `
15792
+ `).split(`
15793
+ `).map((line) => line.trim()).filter(Boolean).slice(0, 40);
15794
+ }
15795
+ function waitForKeypress() {
15796
+ if (!process.stdin.isTTY)
15797
+ return Promise.resolve();
15798
+ return new Promise((resolve6) => {
15799
+ const stdin = process.stdin;
15800
+ const done = () => {
15801
+ stdin.off("data", done);
15802
+ stdin.setRawMode?.(false);
15803
+ stdin.pause();
15804
+ resolve6();
15805
+ };
15806
+ stdin.setRawMode?.(true);
15807
+ stdin.resume();
15808
+ stdin.once("data", done);
15809
+ });
15810
+ }
15811
+ function UpdatePage() {
15812
+ const {
15813
+ theme
15814
+ } = useTheme();
15815
+ const renderer = useRenderer();
15816
+ const [info, setInfo] = createSignal(null);
15817
+ const [notes, setNotes] = createSignal(null);
15818
+ const [loadingNotes, setLoadingNotes] = createSignal(true);
15819
+ const [selected, setSelected] = createSignal("update");
15820
+ const [status, setStatus] = createSignal(null);
15821
+ const latest = createMemo(() => info()?.latest ?? CURRENT_VERSION);
15822
+ const releaseUrl = createMemo(() => notes()?.url ?? releasePageUrl(latest()));
15823
+ const lines = createMemo(() => releaseBodyLines(notes()?.body ?? ""));
15824
+ const actions = createMemo(() => [{
15825
+ id: "update",
15826
+ key: "U",
15827
+ label: "Update now",
15828
+ detail: UPDATE_COMMAND
15829
+ }, {
15830
+ id: "release",
15831
+ key: "R",
15832
+ label: "Open release",
15833
+ detail: releaseUrl() ?? "release URL unavailable"
15834
+ }, {
15835
+ id: "close",
15836
+ key: "Q",
15837
+ label: "Close",
15838
+ detail: "return to the previous tmux window"
15839
+ }]);
15840
+ onMount(() => {
15841
+ load2();
15842
+ });
15843
+ async function load2() {
15844
+ const next = await checkLatestVersion({
15845
+ force: true
15846
+ });
15847
+ setInfo(next);
15848
+ const version = next?.latest ?? CURRENT_VERSION;
15849
+ const fetched = await fetchReleaseNotes(version);
15850
+ setNotes(fetched);
15851
+ setLoadingNotes(false);
15852
+ }
15853
+ function move(delta) {
15854
+ const ids = actions().map((a) => a.id);
15855
+ const index = ids.indexOf(selected());
15856
+ const next = (index + delta + ids.length) % ids.length;
15857
+ setSelected(ids[next] ?? "update");
15858
+ }
15859
+ function activate(id = selected()) {
15860
+ if (id === "close")
15861
+ process.exit(0);
15862
+ if (id === "release") {
15863
+ setStatus(openExternalUrl(releaseUrl()) ? "Opened release page in your browser." : "Could not open release URL.");
15864
+ return;
15865
+ }
15866
+ runUpdater();
15867
+ }
15868
+ async function runUpdater() {
15869
+ setStatus("Leaving the TUI page and running the updater in this tmux window...");
15870
+ await new Promise((resolve6) => setTimeout(resolve6, 30));
15871
+ renderer?.destroy();
15872
+ process.stdout.write(`
15873
+ kobe ${CURRENT_VERSION} -> latest
15874
+ `);
15875
+ process.stdout.write(`running: ${UPDATE_COMMAND}
15876
+
15877
+ `);
15878
+ const result = spawnSync9("sh", ["-c", UPDATE_COMMAND], {
15879
+ stdio: "inherit"
15880
+ });
15881
+ const code = result.status ?? (result.error ? 1 : 0);
15882
+ if (result.error)
15883
+ process.stderr.write(`
15884
+ kobe update: failed to start updater: ${result.error.message}
15885
+ `);
15886
+ process.stdout.write(code === 0 ? `
15887
+ kobe update complete. Relaunch kobe to use the new version.
15888
+ ` : `
15889
+ kobe update failed with exit code ${code}.
15890
+ `);
15891
+ process.stdout.write("Press any key to close this update window.");
15892
+ await waitForKeypress();
15893
+ process.exit(code);
15894
+ }
15895
+ useBindings(() => ({
15896
+ bindings: [{
15897
+ key: "up",
15898
+ cmd: () => move(-1)
15899
+ }, {
15900
+ key: "down",
15901
+ cmd: () => move(1)
15902
+ }, {
15903
+ key: "k",
15904
+ cmd: () => move(-1)
15905
+ }, {
15906
+ key: "j",
15907
+ cmd: () => move(1)
15908
+ }, {
15909
+ key: "return",
15910
+ cmd: () => activate()
15911
+ }, {
15912
+ key: "u",
15913
+ cmd: () => activate("update")
15914
+ }, {
15915
+ key: "r",
15916
+ cmd: () => activate("release")
15917
+ }, {
15918
+ key: "q",
15919
+ cmd: () => activate("close")
15920
+ }, {
15921
+ key: "escape",
15922
+ cmd: () => activate("close")
15923
+ }, {
15924
+ key: "ctrl+c",
15925
+ cmd: () => activate("close")
15926
+ }]
15927
+ }));
15928
+ return (() => {
15929
+ 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");
15930
+ insertNode(_el$, _el$2);
15931
+ insertNode(_el$, _el$7);
15932
+ insertNode(_el$, _el$14);
15933
+ insertNode(_el$, _el$16);
15934
+ insertNode(_el$, _el$19);
15935
+ setProp(_el$, "flexDirection", "column");
15936
+ setProp(_el$, "flexGrow", 1);
15937
+ setProp(_el$, "paddingTop", 1);
15938
+ setProp(_el$, "paddingLeft", 2);
15939
+ setProp(_el$, "paddingRight", 2);
15940
+ insertNode(_el$2, _el$3);
15941
+ insertNode(_el$2, _el$5);
15942
+ setProp(_el$2, "flexDirection", "row");
15943
+ setProp(_el$2, "justifyContent", "space-between");
15944
+ setProp(_el$2, "flexShrink", 0);
15945
+ insertNode(_el$3, createTextNode(`KOBE UPDATE`));
15946
+ setProp(_el$3, "wrapMode", "none");
15947
+ insertNode(_el$5, createTextNode(`q / esc`));
15948
+ setProp(_el$5, "wrapMode", "none");
15949
+ setProp(_el$5, "onMouseUp", () => activate("close"));
15950
+ insertNode(_el$7, _el$8);
15951
+ insertNode(_el$7, _el$0);
15952
+ insertNode(_el$7, _el$10);
15953
+ insertNode(_el$7, _el$12);
15954
+ setProp(_el$7, "flexDirection", "row");
15955
+ setProp(_el$7, "gap", 2);
15956
+ setProp(_el$7, "flexShrink", 0);
15957
+ setProp(_el$7, "paddingTop", 1);
15958
+ insertNode(_el$8, createTextNode(`current`));
15959
+ setProp(_el$8, "wrapMode", "none");
15960
+ insertNode(_el$0, _el$1);
15961
+ setProp(_el$0, "wrapMode", "none");
15962
+ insert(_el$0, CURRENT_VERSION, null);
15963
+ insertNode(_el$10, createTextNode(`latest`));
15964
+ setProp(_el$10, "wrapMode", "none");
15965
+ insertNode(_el$12, _el$13);
15966
+ setProp(_el$12, "wrapMode", "none");
15967
+ insert(_el$12, latest, null);
15968
+ setProp(_el$14, "flexDirection", "column");
15969
+ setProp(_el$14, "flexShrink", 0);
15970
+ setProp(_el$14, "paddingTop", 1);
15971
+ setProp(_el$14, "gap", 0);
15972
+ insert(_el$14, createComponent2(For, {
15973
+ get each() {
15974
+ return actions();
15975
+ },
15976
+ children: (action) => (() => {
15977
+ 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");
15978
+ insertNode(_el$25, _el$26);
15979
+ insertNode(_el$25, _el$30);
15980
+ insertNode(_el$25, _el$32);
15981
+ setProp(_el$25, "flexDirection", "row");
15982
+ setProp(_el$25, "gap", 1);
15983
+ setProp(_el$25, "paddingLeft", 1);
15984
+ setProp(_el$25, "paddingRight", 1);
15985
+ setProp(_el$25, "onMouseUp", () => activate(action.id));
15986
+ insertNode(_el$26, _el$27);
15987
+ setProp(_el$26, "width", 4);
15988
+ setProp(_el$26, "flexShrink", 0);
15989
+ insertNode(_el$27, _el$28);
15990
+ insertNode(_el$27, _el$29);
15991
+ setProp(_el$27, "wrapMode", "none");
15992
+ insert(_el$27, () => action.key, _el$29);
15993
+ insertNode(_el$30, _el$31);
15994
+ setProp(_el$30, "width", 14);
15995
+ setProp(_el$30, "flexShrink", 0);
15996
+ setProp(_el$31, "wrapMode", "none");
15997
+ insert(_el$31, () => action.label);
15998
+ setProp(_el$32, "wrapMode", "word");
15999
+ insert(_el$32, () => action.detail);
16000
+ effect((_p$) => {
16001
+ 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;
16002
+ _v$12 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$12, _p$.e));
16003
+ _v$13 !== _p$.t && (_p$.t = setProp(_el$27, "fg", _v$13, _p$.t));
16004
+ _v$14 !== _p$.a && (_p$.a = setProp(_el$27, "attributes", _v$14, _p$.a));
16005
+ _v$15 !== _p$.o && (_p$.o = setProp(_el$31, "fg", _v$15, _p$.o));
16006
+ _v$16 !== _p$.i && (_p$.i = setProp(_el$32, "fg", _v$16, _p$.i));
16007
+ return _p$;
16008
+ }, {
16009
+ e: undefined,
16010
+ t: undefined,
16011
+ a: undefined,
16012
+ o: undefined,
16013
+ i: undefined
16014
+ });
16015
+ return _el$25;
16016
+ })()
16017
+ }));
16018
+ insert(_el$, createComponent2(Show, {
16019
+ get when() {
16020
+ return status();
16021
+ },
16022
+ get children() {
16023
+ var _el$15 = createElement("text");
16024
+ setProp(_el$15, "wrapMode", "word");
16025
+ insert(_el$15, status);
16026
+ effect((_$p) => setProp(_el$15, "fg", theme.info, _$p));
16027
+ return _el$15;
16028
+ }
16029
+ }), _el$16);
16030
+ insertNode(_el$16, _el$17);
16031
+ setProp(_el$16, "flexShrink", 0);
16032
+ setProp(_el$16, "paddingTop", 1);
16033
+ insertNode(_el$17, createTextNode(`\u2500\u2500 release notes \u2500\u2500`));
16034
+ setProp(_el$17, "wrapMode", "none");
16035
+ insertNode(_el$19, _el$20);
16036
+ setProp(_el$19, "flexGrow", 1);
16037
+ setProp(_el$19, "flexShrink", 1);
16038
+ setProp(_el$19, "stickyScroll", false);
16039
+ setProp(_el$20, "flexDirection", "column");
16040
+ setProp(_el$20, "paddingRight", 1);
16041
+ setProp(_el$20, "paddingBottom", 1);
16042
+ setProp(_el$20, "gap", 0);
16043
+ insert(_el$20, createComponent2(Show, {
16044
+ get when() {
16045
+ return loadingNotes();
16046
+ },
16047
+ get children() {
16048
+ var _el$21 = createElement("text");
16049
+ insertNode(_el$21, createTextNode(`Loading release notes...`));
16050
+ effect((_$p) => setProp(_el$21, "fg", theme.textMuted, _$p));
16051
+ return _el$21;
16052
+ }
16053
+ }), null);
16054
+ insert(_el$20, createComponent2(Show, {
16055
+ get when() {
16056
+ return memo2(() => !!!loadingNotes())() && lines().length === 0;
16057
+ },
16058
+ get children() {
16059
+ var _el$23 = createElement("text");
16060
+ insertNode(_el$23, createTextNode(`Release notes are unavailable. Use Open release to view the GitHub release page.`));
16061
+ setProp(_el$23, "wrapMode", "word");
16062
+ effect((_$p) => setProp(_el$23, "fg", theme.textMuted, _$p));
16063
+ return _el$23;
16064
+ }
16065
+ }), null);
16066
+ insert(_el$20, createComponent2(For, {
16067
+ get each() {
16068
+ return lines();
16069
+ },
16070
+ children: (line) => (() => {
16071
+ var _el$33 = createElement("text");
16072
+ setProp(_el$33, "wrapMode", "word");
16073
+ insert(_el$33, line);
16074
+ effect((_$p) => setProp(_el$33, "fg", theme.textMuted, _$p));
16075
+ return _el$33;
16076
+ })()
16077
+ }), null);
16078
+ effect((_p$) => {
16079
+ 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 = {
16080
+ trackOptions: {
16081
+ backgroundColor: theme.background,
16082
+ foregroundColor: theme.borderActive
16083
+ }
16084
+ };
16085
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "backgroundColor", _v$, _p$.e));
16086
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
16087
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$3, _p$.a));
16088
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$5, "fg", _v$4, _p$.o));
16089
+ _v$5 !== _p$.i && (_p$.i = setProp(_el$8, "fg", _v$5, _p$.i));
16090
+ _v$6 !== _p$.n && (_p$.n = setProp(_el$0, "fg", _v$6, _p$.n));
16091
+ _v$7 !== _p$.s && (_p$.s = setProp(_el$0, "attributes", _v$7, _p$.s));
16092
+ _v$8 !== _p$.h && (_p$.h = setProp(_el$10, "fg", _v$8, _p$.h));
16093
+ _v$9 !== _p$.r && (_p$.r = setProp(_el$12, "fg", _v$9, _p$.r));
16094
+ _v$0 !== _p$.d && (_p$.d = setProp(_el$12, "attributes", _v$0, _p$.d));
16095
+ _v$1 !== _p$.l && (_p$.l = setProp(_el$17, "fg", _v$1, _p$.l));
16096
+ _v$10 !== _p$.u && (_p$.u = setProp(_el$17, "attributes", _v$10, _p$.u));
16097
+ _v$11 !== _p$.c && (_p$.c = setProp(_el$19, "verticalScrollbarOptions", _v$11, _p$.c));
16098
+ return _p$;
16099
+ }, {
16100
+ e: undefined,
16101
+ t: undefined,
16102
+ a: undefined,
16103
+ o: undefined,
16104
+ i: undefined,
16105
+ n: undefined,
16106
+ s: undefined,
16107
+ h: undefined,
16108
+ r: undefined,
16109
+ d: undefined,
16110
+ l: undefined,
16111
+ u: undefined,
16112
+ c: undefined
16113
+ });
16114
+ return _el$;
16115
+ })();
16116
+ }
16117
+ async function startUpdateHost() {
16118
+ for (const {
16119
+ name,
16120
+ theme
16121
+ } of loadUserThemes()) {
16122
+ addTheme2(name, theme);
16123
+ }
16124
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
16125
+ await render(() => createComponent2(ThemeProvider, {
16126
+ mode: "dark",
16127
+ get theme() {
16128
+ return prefs.theme;
16129
+ },
16130
+ get children() {
16131
+ return createComponent2(KVProvider, {
16132
+ get children() {
16133
+ return createComponent2(FocusProvider, {
16134
+ initial: "sidebar",
16135
+ get children() {
16136
+ return createComponent2(DialogProvider, {
16137
+ get children() {
16138
+ return createComponent2(UpdatePage, {});
16139
+ }
16140
+ });
16141
+ }
16142
+ });
16143
+ }
16144
+ });
16145
+ }
16146
+ }), {
16147
+ backgroundColor: "transparent",
16148
+ externalOutputMode: "passthrough",
16149
+ exitOnCtrlC: false,
16150
+ screenMode: "alternate-screen",
16151
+ useKittyKeyboard: {}
16152
+ });
16153
+ }
16154
+ var FALLBACK_THEME4 = "claude";
16155
+ var init_host4 = __esm(() => {
16156
+ init_solid();
16157
+ init_solid();
16158
+ init_solid();
16159
+ init_solid();
16160
+ init_solid();
16161
+ init_solid();
16162
+ init_solid();
16163
+ init_solid();
16164
+ init_solid();
16165
+ init_dev();
16166
+ init_version();
16167
+ init_focus();
16168
+ init_kv();
16169
+ init_theme2();
16170
+ init_loader();
16171
+ init_keymap();
16172
+ init_persisted_ui_prefs();
16173
+ init_dialog();
16174
+ });
16175
+
15420
16176
  // src/engine/claude-code-local/normalize.ts
15421
16177
  function normalizeClaudeContent(content) {
15422
16178
  if (typeof content === "string") {
@@ -16552,7 +17308,7 @@ var gitWrapper;
16552
17308
  var init_git2 = __esm(() => {
16553
17309
  gitWrapper = {
16554
17310
  spawn(args, cwd) {
16555
- return new Promise((resolve5, reject) => {
17311
+ return new Promise((resolve6, reject) => {
16556
17312
  const child = nodeSpawn("git", [...args], {
16557
17313
  cwd,
16558
17314
  shell: false,
@@ -16570,7 +17326,7 @@ var init_git2 = __esm(() => {
16570
17326
  });
16571
17327
  child.on("error", reject);
16572
17328
  child.on("close", (status, signal) => {
16573
- resolve5({ stdout, stderr, status, signal });
17329
+ resolve6({ stdout, stderr, status, signal });
16574
17330
  });
16575
17331
  });
16576
17332
  }
@@ -16622,17 +17378,17 @@ var init_keys2 = __esm(() => {
16622
17378
  });
16623
17379
 
16624
17380
  // src/tui/panes/filetree/open-external.ts
16625
- import { spawn as spawn4 } from "child_process";
16626
- import { existsSync as existsSync11 } from "fs";
17381
+ import { spawn as spawn5 } from "child_process";
17382
+ import { existsSync as existsSync12 } from "fs";
16627
17383
  import { platform } from "os";
16628
17384
  function openExternally(absPath) {
16629
17385
  if (!absPath)
16630
17386
  return;
16631
17387
  const plat = platform();
16632
17388
  if (plat === "linux") {
16633
- if (existsSync11("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
17389
+ if (existsSync12("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
16634
17390
  spawnDetached("wslview", [absPath], () => {
16635
- const child = spawn4("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
17391
+ const child = spawn5("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
16636
17392
  let out = "";
16637
17393
  child.stdout.on("data", (b) => {
16638
17394
  out += b.toString();
@@ -16658,7 +17414,7 @@ function openExternally(absPath) {
16658
17414
  }
16659
17415
  function spawnDetached(cmd, args, onError) {
16660
17416
  try {
16661
- const child = spawn4(cmd, args, { stdio: "ignore", detached: true });
17417
+ const child = spawn5(cmd, args, { stdio: "ignore", detached: true });
16662
17418
  child.on("error", () => onError?.());
16663
17419
  child.unref();
16664
17420
  } catch {
@@ -16669,7 +17425,7 @@ var init_open_external = () => {};
16669
17425
 
16670
17426
  // src/tui/panes/filetree/FileTree.tsx
16671
17427
  import { watch } from "fs";
16672
- import { TextAttributes as TextAttributes10 } from "@opentui/core";
17428
+ import { TextAttributes as TextAttributes11 } from "@opentui/core";
16673
17429
  function statusToken(s) {
16674
17430
  switch (s) {
16675
17431
  case "M":
@@ -17046,7 +17802,7 @@ function FileTree(props) {
17046
17802
  insertNode(_el$6, createTextNode(`create PR`));
17047
17803
  setProp(_el$6, "wrapMode", "none");
17048
17804
  effect((_p$) => {
17049
- var _v$ = theme.accent, _v$2 = TextAttributes10.BOLD, _v$3 = theme.text;
17805
+ var _v$ = theme.accent, _v$2 = TextAttributes11.BOLD, _v$3 = theme.text;
17050
17806
  _v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
17051
17807
  _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$2, _p$.t));
17052
17808
  _v$3 !== _p$.a && (_p$.a = setProp(_el$6, "fg", _v$3, _p$.a));
@@ -17076,7 +17832,7 @@ function FileTree(props) {
17076
17832
  setProp(_el$23, "onMouseUp", () => setTab(t));
17077
17833
  insert(_el$23, () => TAB_LABEL[t]);
17078
17834
  effect((_p$) => {
17079
- var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes10.BOLD : undefined;
17835
+ var _v$6 = isActive() ? theme.primary : theme.textMuted, _v$7 = isActive() ? TextAttributes11.BOLD : undefined;
17080
17836
  _v$6 !== _p$.e && (_p$.e = setProp(_el$23, "fg", _v$6, _p$.e));
17081
17837
  _v$7 !== _p$.t && (_p$.t = setProp(_el$23, "attributes", _v$7, _p$.t));
17082
17838
  return _p$;
@@ -17097,7 +17853,7 @@ function FileTree(props) {
17097
17853
  setProp(_el$24, "wrapMode", "none");
17098
17854
  insert(_el$24, () => badge().text);
17099
17855
  effect((_p$) => {
17100
- var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes10.BOLD : undefined;
17856
+ var _v$8 = badge().active ? theme.accent : theme.textMuted, _v$9 = badge().active ? TextAttributes11.BOLD : undefined;
17101
17857
  _v$8 !== _p$.e && (_p$.e = setProp(_el$24, "fg", _v$8, _p$.e));
17102
17858
  _v$9 !== _p$.t && (_p$.t = setProp(_el$24, "attributes", _v$9, _p$.t));
17103
17859
  return _p$;
@@ -17239,7 +17995,7 @@ function FileTree(props) {
17239
17995
  setProp(_el$26, "wrapMode", "none");
17240
17996
  insert(_el$26, () => `${indent}${marker} ${row.name}/`);
17241
17997
  effect((_p$) => {
17242
- var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes10.BOLD;
17998
+ var _v$0 = isCursor() ? theme.primary : undefined, _v$1 = isCursor() ? theme.selectedListItemText : theme.textMuted, _v$10 = TextAttributes11.BOLD;
17243
17999
  _v$0 !== _p$.e && (_p$.e = setProp(_el$25, "backgroundColor", _v$0, _p$.e));
17244
18000
  _v$1 !== _p$.t && (_p$.t = setProp(_el$26, "fg", _v$1, _p$.t));
17245
18001
  _v$10 !== _p$.a && (_p$.a = setProp(_el$26, "attributes", _v$10, _p$.a));
@@ -17388,12 +18144,12 @@ var init_filetree = __esm(() => {
17388
18144
  });
17389
18145
 
17390
18146
  // src/tui/ops/pr-prompt.ts
17391
- import { spawnSync as spawnSync9 } from "child_process";
18147
+ import { spawnSync as spawnSync10 } from "child_process";
17392
18148
  import { promises as fs4 } from "fs";
17393
18149
  import path11 from "path";
17394
18150
  function git2(cwd, args) {
17395
18151
  try {
17396
- const out = spawnSync9("git", args.slice(), {
18152
+ const out = spawnSync10("git", args.slice(), {
17397
18153
  cwd,
17398
18154
  encoding: "utf8",
17399
18155
  timeout: GIT_TIMEOUT_MS
@@ -17487,12 +18243,12 @@ If any of these steps fail, ask the user for help.`;
17487
18243
  var init_pr_prompt = () => {};
17488
18244
 
17489
18245
  // src/tui/ops/host.tsx
17490
- var exports_host4 = {};
17491
- __export(exports_host4, {
18246
+ var exports_host5 = {};
18247
+ __export(exports_host5, {
17492
18248
  startOpsPreview: () => startOpsPreview,
17493
18249
  startOpsHost: () => startOpsHost
17494
18250
  });
17495
- import { createHash as createHash2 } from "crypto";
18251
+ import { createHash as createHash3 } from "crypto";
17496
18252
  import { SyntaxStyle } from "@opentui/core";
17497
18253
  function OpsShell(props) {
17498
18254
  const themeCtx = useTheme();
@@ -17631,7 +18387,7 @@ function basename5(p) {
17631
18387
  return i >= 0 ? p.slice(i + 1) : p;
17632
18388
  }
17633
18389
  function fingerprint(text) {
17634
- return createHash2("sha1").update(text).digest("hex");
18390
+ return createHash3("sha1").update(text).digest("hex");
17635
18391
  }
17636
18392
  function OpsApp(props) {
17637
18393
  return createComponent2(ThemeProvider, {
@@ -17655,7 +18411,7 @@ async function startOpsHost(args) {
17655
18411
  } of loadUserThemes()) {
17656
18412
  addTheme2(name, theme);
17657
18413
  }
17658
- const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
18414
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME5);
17659
18415
  await render(() => createComponent2(OpsApp, mergeProps3(args, {
17660
18416
  prefs
17661
18417
  })), {
@@ -17883,7 +18639,7 @@ async function startOpsPreview(args) {
17883
18639
  } of loadUserThemes()) {
17884
18640
  addTheme2(name, theme);
17885
18641
  }
17886
- const prefs = readPersistedUiPrefs(FALLBACK_THEME4);
18642
+ const prefs = readPersistedUiPrefs(FALLBACK_THEME5);
17887
18643
  await render(() => createComponent2(ThemeProvider, {
17888
18644
  mode: "dark",
17889
18645
  get theme() {
@@ -17904,8 +18660,8 @@ async function startOpsPreview(args) {
17904
18660
  useKittyKeyboard: {}
17905
18661
  });
17906
18662
  }
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(() => {
18663
+ 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";
18664
+ var init_host5 = __esm(() => {
17909
18665
  init_solid();
17910
18666
  init_solid();
17911
18667
  init_solid();
@@ -17971,7 +18727,7 @@ __export(exports_direct, {
17971
18727
  startDirectTmux: () => startDirectTmux,
17972
18728
  chooseInitialTask: () => chooseInitialTask
17973
18729
  });
17974
- import { resolve as resolve5 } from "path";
18730
+ import { resolve as resolve6 } from "path";
17975
18731
  function chooseInitialTask(tasks, choice = {}) {
17976
18732
  const byId = (id) => id ? tasks.find((t) => t.id === id) : undefined;
17977
18733
  const active = byId(choice.activeTaskId);
@@ -18007,7 +18763,7 @@ async function ensureRepos(orchestrator) {
18007
18763
  normalizeSavedRepos();
18008
18764
  let repos = [...getSavedRepos()];
18009
18765
  if (repos.length === 0) {
18010
- const added = addSavedRepo(resolve5(process.cwd()));
18766
+ const added = addSavedRepo(resolve6(process.cwd()));
18011
18767
  repos = [added.path];
18012
18768
  }
18013
18769
  for (const repo of repos) {
@@ -18017,7 +18773,7 @@ async function ensureRepos(orchestrator) {
18017
18773
  console.error(`[kobe] ensureMainTask failed for ${repo}:`, err);
18018
18774
  }
18019
18775
  }
18020
- return repos[0] ?? resolve5(process.cwd());
18776
+ return repos[0] ?? resolve6(process.cwd());
18021
18777
  }
18022
18778
  async function startDirectTmux() {
18023
18779
  if (!await tmuxAvailable()) {
@@ -18045,12 +18801,15 @@ async function startDirectTmux() {
18045
18801
  const name = tmuxSessionName(task.id);
18046
18802
  await orchestrator.setActiveTask(task.id).catch(() => {});
18047
18803
  setPersistedString("lastSelectedTaskId", task.id);
18804
+ const init2 = resolveRepoInit(task.repo, cwd);
18048
18805
  const ready = await ensureSession({
18049
18806
  name,
18050
18807
  cwd,
18051
18808
  command: interactiveEngineCommand(task.vendor),
18052
18809
  taskId: task.id,
18053
- vendor: task.vendor
18810
+ vendor: task.vendor,
18811
+ initScript: init2.initScript,
18812
+ initPrompt: init2.initPrompt
18054
18813
  });
18055
18814
  if (!ready) {
18056
18815
  console.error(`kobe: tmux session ${name} failed to start (check the daemon log)`);
@@ -18088,12 +18847,13 @@ var init_direct = __esm(() => {
18088
18847
  init_interactive_command();
18089
18848
  init_auto_title();
18090
18849
  init_core();
18850
+ init_repo_init();
18091
18851
  init_repos();
18092
18852
  init_tmux();
18093
18853
  });
18094
18854
 
18095
18855
  // src/tui/component/help-dialog.tsx
18096
- import { TextAttributes as TextAttributes11 } from "@opentui/core";
18856
+ import { TextAttributes as TextAttributes12 } from "@opentui/core";
18097
18857
  function groupBindings(keymap) {
18098
18858
  const groups = new Map;
18099
18859
  const order = [];
@@ -18197,7 +18957,7 @@ function HelpDialog() {
18197
18957
  }
18198
18958
  }), null);
18199
18959
  effect((_p$) => {
18200
- var _v$5 = theme.accent, _v$6 = TextAttributes11.BOLD;
18960
+ var _v$5 = theme.accent, _v$6 = TextAttributes12.BOLD;
18201
18961
  _v$5 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$5, _p$.e));
18202
18962
  _v$6 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$6, _p$.t));
18203
18963
  return _p$;
@@ -18209,7 +18969,7 @@ function HelpDialog() {
18209
18969
  })()
18210
18970
  }));
18211
18971
  effect((_p$) => {
18212
- var _v$ = TextAttributes11.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = {
18972
+ var _v$ = TextAttributes12.BOLD, _v$2 = theme.text, _v$3 = theme.textMuted, _v$4 = {
18213
18973
  trackOptions: {
18214
18974
  backgroundColor: theme.backgroundDialog,
18215
18975
  foregroundColor: theme.borderActive
@@ -18249,7 +19009,7 @@ var init_help_dialog = __esm(() => {
18249
19009
  });
18250
19010
 
18251
19011
  // src/tui/component/pane-header.tsx
18252
- import { TextAttributes as TextAttributes12 } from "@opentui/core";
19012
+ import { TextAttributes as TextAttributes13 } from "@opentui/core";
18253
19013
  function PaneHeader(props) {
18254
19014
  const {
18255
19015
  theme
@@ -18279,7 +19039,7 @@ function PaneHeader(props) {
18279
19039
  setProp(_el$3, "wrapMode", "none");
18280
19040
  insert(_el$3, () => props.ordinal);
18281
19041
  effect((_p$) => {
18282
- var _v$ = titleColor(), _v$2 = TextAttributes12.BOLD;
19042
+ var _v$ = titleColor(), _v$2 = TextAttributes13.BOLD;
18283
19043
  _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
18284
19044
  _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$2, _p$.t));
18285
19045
  return _p$;
@@ -18330,7 +19090,7 @@ function PaneHeader(props) {
18330
19090
  }
18331
19091
  }), null);
18332
19092
  effect((_p$) => {
18333
- var _v$3 = titleColor(), _v$4 = TextAttributes12.BOLD;
19093
+ var _v$3 = titleColor(), _v$4 = TextAttributes13.BOLD;
18334
19094
  _v$3 !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$3, _p$.e));
18335
19095
  _v$4 !== _p$.t && (_p$.t = setProp(_el$4, "attributes", _v$4, _p$.t));
18336
19096
  return _p$;
@@ -18455,7 +19215,7 @@ var init_resizable_edge = __esm(() => {
18455
19215
  });
18456
19216
 
18457
19217
  // src/tui/component/status-bar.tsx
18458
- import { TextAttributes as TextAttributes13 } from "@opentui/core";
19218
+ import { TextAttributes as TextAttributes14 } from "@opentui/core";
18459
19219
  function Hotkey(props) {
18460
19220
  const {
18461
19221
  theme
@@ -18474,7 +19234,7 @@ function Hotkey(props) {
18474
19234
  setProp(_el$5, "wrapMode", "none");
18475
19235
  insert(_el$5, () => props.label);
18476
19236
  effect((_p$) => {
18477
- var _v$ = theme.accent, _v$2 = TextAttributes13.BOLD, _v$3 = theme.textMuted;
19237
+ var _v$ = theme.accent, _v$2 = TextAttributes14.BOLD, _v$3 = theme.textMuted;
18478
19238
  _v$ !== _p$.e && (_p$.e = setProp(_el$2, "fg", _v$, _p$.e));
18479
19239
  _v$2 !== _p$.t && (_p$.t = setProp(_el$2, "attributes", _v$2, _p$.t));
18480
19240
  _v$3 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$3, _p$.a));
@@ -18562,7 +19322,7 @@ function StatusBar() {
18562
19322
  insertNode(_el$0, createTextNode(`Press Ctrl+C again to exit`));
18563
19323
  setProp(_el$0, "wrapMode", "none");
18564
19324
  effect((_p$) => {
18565
- var _v$4 = theme.warning, _v$5 = TextAttributes13.BOLD;
19325
+ var _v$4 = theme.warning, _v$5 = TextAttributes14.BOLD;
18566
19326
  _v$4 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$4, _p$.e));
18567
19327
  _v$5 !== _p$.t && (_p$.t = setProp(_el$0, "attributes", _v$5, _p$.t));
18568
19328
  return _p$;
@@ -18574,7 +19334,7 @@ function StatusBar() {
18574
19334
  }
18575
19335
  }), null);
18576
19336
  effect((_p$) => {
18577
- var _v$6 = theme.primary, _v$7 = TextAttributes13.BOLD;
19337
+ var _v$6 = theme.primary, _v$7 = TextAttributes14.BOLD;
18578
19338
  _v$6 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$6, _p$.e));
18579
19339
  _v$7 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$7, _p$.t));
18580
19340
  return _p$;
@@ -18604,9 +19364,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
18604
19364
  var init_pulse = () => {};
18605
19365
 
18606
19366
  // src/tui/lib/sound.ts
18607
- import { existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
19367
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
18608
19368
  import { tmpdir as tmpdir2 } from "os";
18609
- import { basename as basename6, isAbsolute as isAbsolute2, join as join11, resolve as resolve6 } from "path";
19369
+ import { basename as basename6, isAbsolute as isAbsolute2, join as join12, resolve as resolve7 } from "path";
18610
19370
  function args(player, file, volume) {
18611
19371
  if (player === "ffplay")
18612
19372
  return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
@@ -18629,13 +19389,13 @@ function pickPlayer() {
18629
19389
  return cachedPlayer;
18630
19390
  const path12 = process.env.PATH ?? "";
18631
19391
  const segments = path12.split(":").filter(Boolean);
18632
- cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync12(join11(dir, p)))) ?? null;
19392
+ cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync13(join12(dir, p)))) ?? null;
18633
19393
  return cachedPlayer;
18634
19394
  }
18635
19395
  async function ensureAsset() {
18636
19396
  cachedPath ??= (async () => {
18637
19397
  mkdirSync5(DIR, { recursive: true });
18638
- const dest = join11(DIR, basename6(pulseAsset));
19398
+ const dest = join12(DIR, basename6(pulseAsset));
18639
19399
  const out = Bun.file(dest);
18640
19400
  if (await out.exists())
18641
19401
  return dest;
@@ -18664,8 +19424,8 @@ function pulse(volume = 0.4) {
18664
19424
  var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
18665
19425
  var init_sound = __esm(() => {
18666
19426
  init_pulse();
18667
- pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve6(import.meta.dir, pulse_default);
18668
- DIR = join11(tmpdir2(), "kobe-sfx");
19427
+ pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve7(import.meta.dir, pulse_default);
19428
+ DIR = join12(tmpdir2(), "kobe-sfx");
18669
19429
  PLAYERS = [
18670
19430
  "ffplay",
18671
19431
  "mpv",
@@ -18763,7 +19523,7 @@ var init_notifications = __esm(() => {
18763
19523
  });
18764
19524
 
18765
19525
  // src/tui/component/toast-overlay.tsx
18766
- import { TextAttributes as TextAttributes14 } from "@opentui/core";
19526
+ import { TextAttributes as TextAttributes15 } from "@opentui/core";
18767
19527
  function ToastOverlay() {
18768
19528
  const {
18769
19529
  theme
@@ -18806,7 +19566,7 @@ function ToastOverlay() {
18806
19566
  setProp(_el$4, "wrapMode", "none");
18807
19567
  insert(_el$4, () => toast.title);
18808
19568
  effect((_p$) => {
18809
- var _v$3 = bg(), _v$4 = fg(), _v$5 = TextAttributes14.BOLD, _v$6 = fg();
19569
+ var _v$3 = bg(), _v$4 = fg(), _v$5 = TextAttributes15.BOLD, _v$6 = fg();
18810
19570
  _v$3 !== _p$.e && (_p$.e = setProp(_el$2, "backgroundColor", _v$3, _p$.e));
18811
19571
  _v$4 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$4, _p$.t));
18812
19572
  _v$5 !== _p$.a && (_p$.a = setProp(_el$3, "attributes", _v$5, _p$.a));
@@ -18865,8 +19625,8 @@ function repoBasename2(repo) {
18865
19625
  }
18866
19626
 
18867
19627
  // src/tui/component/top-bar.tsx
18868
- import { spawnSync as spawnSync10 } from "child_process";
18869
- import { TextAttributes as TextAttributes15 } from "@opentui/core";
19628
+ import { spawnSync as spawnSync11 } from "child_process";
19629
+ import { TextAttributes as TextAttributes16 } from "@opentui/core";
18870
19630
  function TopBar(props) {
18871
19631
  const {
18872
19632
  theme
@@ -18894,7 +19654,7 @@ function TopBar(props) {
18894
19654
  `);
18895
19655
  process.stderr.write(`running: ${UPDATE_COMMAND}
18896
19656
  `);
18897
- const result = spawnSync10("sh", ["-c", UPDATE_COMMAND], {
19657
+ const result = spawnSync11("sh", ["-c", UPDATE_COMMAND], {
18898
19658
  stdio: "inherit"
18899
19659
  });
18900
19660
  if (result.error) {
@@ -18941,7 +19701,7 @@ function TopBar(props) {
18941
19701
  insertNode(_el$7, createTextNode(`[Update]`));
18942
19702
  setProp(_el$7, "onMouseUp", () => void confirmUpdate());
18943
19703
  effect((_p$) => {
18944
- var _v$ = theme.warning, _v$2 = TextAttributes15.BOLD;
19704
+ var _v$ = theme.warning, _v$2 = TextAttributes16.BOLD;
18945
19705
  _v$ !== _p$.e && (_p$.e = setProp(_el$7, "fg", _v$, _p$.e));
18946
19706
  _v$2 !== _p$.t && (_p$.t = setProp(_el$7, "attributes", _v$2, _p$.t));
18947
19707
  return _p$;
@@ -18977,7 +19737,7 @@ function TopBar(props) {
18977
19737
  insertNode(_el$12, createTextNode(`daemon disconnected`));
18978
19738
  setProp(_el$12, "wrapMode", "none");
18979
19739
  effect((_p$) => {
18980
- var _v$6 = theme.error, _v$7 = TextAttributes15.BOLD;
19740
+ var _v$6 = theme.error, _v$7 = TextAttributes16.BOLD;
18981
19741
  _v$6 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$6, _p$.e));
18982
19742
  _v$7 !== _p$.t && (_p$.t = setProp(_el$12, "attributes", _v$7, _p$.t));
18983
19743
  return _p$;
@@ -19007,7 +19767,7 @@ function TopBar(props) {
19007
19767
  setProp(_el$15, "wrapMode", "none");
19008
19768
  insert(_el$15, () => label().repoName);
19009
19769
  effect((_p$) => {
19010
- var _v$8 = label().branch ? theme.textMuted : theme.text, _v$9 = label().branch ? undefined : TextAttributes15.BOLD;
19770
+ var _v$8 = label().branch ? theme.textMuted : theme.text, _v$9 = label().branch ? undefined : TextAttributes16.BOLD;
19011
19771
  _v$8 !== _p$.e && (_p$.e = setProp(_el$15, "fg", _v$8, _p$.e));
19012
19772
  _v$9 !== _p$.t && (_p$.t = setProp(_el$15, "attributes", _v$9, _p$.t));
19013
19773
  return _p$;
@@ -19039,7 +19799,7 @@ function TopBar(props) {
19039
19799
  setProp(_el$18, "wrapMode", "none");
19040
19800
  insert(_el$18, () => label().branch);
19041
19801
  effect((_p$) => {
19042
- var _v$0 = theme.text, _v$1 = TextAttributes15.BOLD;
19802
+ var _v$0 = theme.text, _v$1 = TextAttributes16.BOLD;
19043
19803
  _v$0 !== _p$.e && (_p$.e = setProp(_el$18, "fg", _v$0, _p$.e));
19044
19804
  _v$1 !== _p$.t && (_p$.t = setProp(_el$18, "attributes", _v$1, _p$.t));
19045
19805
  return _p$;
@@ -19062,7 +19822,7 @@ function TopBar(props) {
19062
19822
  setProp(_el$11, "gap", 2);
19063
19823
  setProp(_el$11, "justifyContent", "flex-end");
19064
19824
  effect((_p$) => {
19065
- var _v$3 = theme.primary, _v$4 = TextAttributes15.BOLD, _v$5 = theme.textMuted;
19825
+ var _v$3 = theme.primary, _v$4 = TextAttributes16.BOLD, _v$5 = theme.textMuted;
19066
19826
  _v$3 !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$3, _p$.e));
19067
19827
  _v$4 !== _p$.t && (_p$.t = setProp(_el$3, "attributes", _v$4, _p$.t));
19068
19828
  _v$5 !== _p$.a && (_p$.a = setProp(_el$5, "fg", _v$5, _p$.a));
@@ -19606,12 +20366,15 @@ async function launchTaskTmux(opts) {
19606
20366
  }
19607
20367
  }
19608
20368
  const name = tmuxSessionName(opts.taskId);
20369
+ const init2 = opts.repo ? resolveRepoInit(opts.repo, cwd) : {};
19609
20370
  const ready = await ensureSession({
19610
20371
  name,
19611
20372
  cwd,
19612
20373
  command: opts.command,
19613
20374
  taskId: opts.taskId,
19614
- vendor: opts.vendor
20375
+ vendor: opts.vendor,
20376
+ initScript: init2.initScript,
20377
+ initPrompt: init2.initPrompt
19615
20378
  });
19616
20379
  if (!ready) {
19617
20380
  return {
@@ -19709,6 +20472,7 @@ var init_fullscreen = __esm(() => {
19709
20472
  init_solid();
19710
20473
  init_solid();
19711
20474
  init_dev();
20475
+ init_repo_init();
19712
20476
  init_theme2();
19713
20477
  init_keymap();
19714
20478
  init_dialog();
@@ -19792,6 +20556,7 @@ function Shell(props) {
19792
20556
  cwd: task.worktreePath || null,
19793
20557
  command: interactiveEngineCommand(task.vendor),
19794
20558
  vendor: task.vendor,
20559
+ repo: task.repo,
19795
20560
  onEnsureWorktree: (taskId) => props.orchestrator.ensureWorktree(taskId)
19796
20561
  });
19797
20562
  if (res.kind === "error") {
@@ -20327,11 +21092,14 @@ var init_tui = __esm(() => {
20327
21092
  // src/cli/index.ts
20328
21093
  init_path_glob();
20329
21094
  init_vendor();
20330
- import { resolve as resolve7 } from "path";
21095
+ import { resolve as resolve8 } from "path";
20331
21096
 
20332
21097
  // src/cli/usage.ts
21098
+ init_version();
20333
21099
  function topLevelUsage() {
20334
21100
  return [
21101
+ `kobe ${CURRENT_VERSION}`,
21102
+ "",
20335
21103
  "Usage: kobe [command] [options]",
20336
21104
  "",
20337
21105
  "Run with no command to launch the TUI.",
@@ -20339,6 +21107,7 @@ function topLevelUsage() {
20339
21107
  "Commands:",
20340
21108
  " add [path] Save a repo path for the new-task picker",
20341
21109
  " adopt [glob] Import existing git worktrees as tasks",
21110
+ " repo <verb> Per-repo init script + first prompt (show|set|unset)",
20342
21111
  " api <verb> Scriptable RPC surface for agents (see `kobe api --help`)",
20343
21112
  " daemon <verb> Manage the daemon (start|stop|status|restart)",
20344
21113
  " theme <verb> Manage user themes (list|add|remove)",
@@ -20370,7 +21139,7 @@ Usage: kobe add [path]
20370
21139
  `);
20371
21140
  process.exit(2);
20372
21141
  }
20373
- const target = resolve7(process.cwd(), arg && arg.length > 0 ? arg : ".");
21142
+ const target = resolve8(process.cwd(), arg && arg.length > 0 ? arg : ".");
20374
21143
  const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
20375
21144
  const result = addSavedRepo2(target);
20376
21145
  if (result.added) {
@@ -20469,7 +21238,7 @@ async function runAdoptSubcommand(args2) {
20469
21238
  }
20470
21239
  }
20471
21240
  const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
20472
- const repo = resolveRepoRoot2(resolve7(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
21241
+ const repo = resolveRepoRoot2(resolve8(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
20473
21242
  const vendor = coerceVendorId(vendorArg);
20474
21243
  const orch = await openLocalOrchestrator();
20475
21244
  const worktrees = await orch.discoverAdoptableWorktrees(repo);
@@ -20559,6 +21328,11 @@ async function main() {
20559
21328
  await runAdoptSubcommand(rest);
20560
21329
  return;
20561
21330
  }
21331
+ if (subcommand === "repo") {
21332
+ const { runRepoSubcommand: runRepoSubcommand2 } = await Promise.resolve().then(() => (init_repo_cmd(), exports_repo_cmd));
21333
+ await runRepoSubcommand2(rest);
21334
+ return;
21335
+ }
20562
21336
  if (subcommand === "api") {
20563
21337
  const { runApiSubcommand: runApiSubcommand2 } = await Promise.resolve().then(() => (init_api_cmd(), exports_api_cmd));
20564
21338
  await runApiSubcommand2(rest);
@@ -20641,6 +21415,11 @@ async function main() {
20641
21415
  await startNewTaskHost2({ defaultRepo: flags.repo });
20642
21416
  return;
20643
21417
  }
21418
+ if (subcommand === "update-page") {
21419
+ const { startUpdateHost: startUpdateHost2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21420
+ await startUpdateHost2();
21421
+ return;
21422
+ }
20644
21423
  if (subcommand === "ops") {
20645
21424
  const flags = parseOpsFlags(rest);
20646
21425
  if (!flags.worktree) {
@@ -20648,11 +21427,11 @@ async function main() {
20648
21427
  process.exit(2);
20649
21428
  }
20650
21429
  if (flags.preview) {
20651
- const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21430
+ const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (init_host5(), exports_host5));
20652
21431
  await startOpsPreview2({ worktree: flags.worktree, relPath: flags.preview });
20653
21432
  return;
20654
21433
  }
20655
- const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (init_host4(), exports_host4));
21434
+ const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (init_host5(), exports_host5));
20656
21435
  await startOpsHost2({
20657
21436
  taskId: flags.taskId ?? "",
20658
21437
  worktree: flags.worktree,