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