@sma1lboy/kobe 0.7.15 → 0.7.16
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 +849 -280
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
|
|
|
90
90
|
package_default = {
|
|
91
91
|
$schema: "https://json.schemastore.org/package.json",
|
|
92
92
|
name: "@sma1lboy/kobe",
|
|
93
|
-
version: "0.7.
|
|
93
|
+
version: "0.7.16",
|
|
94
94
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
95
95
|
type: "module",
|
|
96
96
|
packageManager: "bun@1.3.13",
|
|
@@ -167,6 +167,10 @@ function kobeStateDir() {
|
|
|
167
167
|
function kvStatePath() {
|
|
168
168
|
return join(homeDir(), ".config", "kobe", "state.json");
|
|
169
169
|
}
|
|
170
|
+
function remoteControlSocketPath(host, user, port) {
|
|
171
|
+
const hash = createHash("sha1").update(`${user}@${host}:${port ?? 22}`).digest("hex").slice(0, 16);
|
|
172
|
+
return join(kobeStateDir(), "ssh", `${hash}.sock`);
|
|
173
|
+
}
|
|
170
174
|
function worktreeInitMarkerPath(worktreePath) {
|
|
171
175
|
const hash = createHash("sha1").update(worktreePath).digest("hex").slice(0, 16);
|
|
172
176
|
return join(kobeStateDir(), "worktree-init", hash);
|
|
@@ -332,6 +336,202 @@ var init_version = __esm(() => {
|
|
|
332
336
|
UPDATE_COMMAND = `curl -fsSL ${UPDATE_SCRIPT_URL} | sh`;
|
|
333
337
|
});
|
|
334
338
|
|
|
339
|
+
// src/exec/exec-host.ts
|
|
340
|
+
import { spawnSync } from "child_process";
|
|
341
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync } from "fs";
|
|
342
|
+
function shQuote(s) {
|
|
343
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
344
|
+
}
|
|
345
|
+
function shJoin(argv) {
|
|
346
|
+
return argv.map(shQuote).join(" ");
|
|
347
|
+
}
|
|
348
|
+
function remoteShellCommand(argv, cwd) {
|
|
349
|
+
const cmd = shJoin(argv);
|
|
350
|
+
return cwd ? `cd ${shQuote(cwd)} && ${cmd}` : cmd;
|
|
351
|
+
}
|
|
352
|
+
function sshConnectArgs(spec, opts = {}) {
|
|
353
|
+
const argv = ["ssh"];
|
|
354
|
+
if (opts.tty)
|
|
355
|
+
argv.push("-tt");
|
|
356
|
+
if (opts.batch)
|
|
357
|
+
argv.push("-o", "BatchMode=yes");
|
|
358
|
+
argv.push("-o", "ControlMaster=auto", "-o", `ControlPath=${spec.controlPath}`, "-o", "ControlPersist=300");
|
|
359
|
+
argv.push("-o", "StrictHostKeyChecking=accept-new");
|
|
360
|
+
if (spec.port)
|
|
361
|
+
argv.push("-p", String(spec.port));
|
|
362
|
+
if (spec.auth.kind === "key" && spec.auth.keyPath)
|
|
363
|
+
argv.push("-i", spec.auth.keyPath);
|
|
364
|
+
argv.push(`${spec.user}@${spec.host}`);
|
|
365
|
+
return argv;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
class LocalExecHost {
|
|
369
|
+
isRemote = false;
|
|
370
|
+
run(argv, opts = {}) {
|
|
371
|
+
const [cmd, ...rest] = argv;
|
|
372
|
+
const proc = spawnSync(cmd ?? "", rest, {
|
|
373
|
+
cwd: opts.cwd,
|
|
374
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
375
|
+
encoding: "utf8",
|
|
376
|
+
shell: false
|
|
377
|
+
});
|
|
378
|
+
return toResult(proc);
|
|
379
|
+
}
|
|
380
|
+
async runAsync(argv, opts = {}) {
|
|
381
|
+
return this.run(argv, opts);
|
|
382
|
+
}
|
|
383
|
+
exists(path) {
|
|
384
|
+
return existsSync(path);
|
|
385
|
+
}
|
|
386
|
+
mkdirp(path) {
|
|
387
|
+
mkdirSync(path, { recursive: true });
|
|
388
|
+
}
|
|
389
|
+
readFile(path) {
|
|
390
|
+
try {
|
|
391
|
+
return readFileSync(path, "utf8");
|
|
392
|
+
} catch {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
readdir(path) {
|
|
397
|
+
try {
|
|
398
|
+
return readdirSync(path);
|
|
399
|
+
} catch {
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
wrapCommand(command) {
|
|
404
|
+
return command;
|
|
405
|
+
}
|
|
406
|
+
ensureReady() {}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
class RemoteExecHost {
|
|
410
|
+
spec;
|
|
411
|
+
spawn;
|
|
412
|
+
isRemote = true;
|
|
413
|
+
masterUp = false;
|
|
414
|
+
constructor(spec, spawn = defaultSpawner) {
|
|
415
|
+
this.spec = spec;
|
|
416
|
+
this.spawn = spawn;
|
|
417
|
+
}
|
|
418
|
+
ensureReady() {
|
|
419
|
+
if (this.masterUp)
|
|
420
|
+
return;
|
|
421
|
+
const check = this.spawn([...sshConnectArgs(this.spec, { batch: true }), "-O", "check"]);
|
|
422
|
+
if (check.exitCode === 0) {
|
|
423
|
+
this.masterUp = true;
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const base = [...sshConnectArgs(this.spec, { batch: this.spec.auth.kind !== "password" }), "-fN"];
|
|
427
|
+
if (this.spec.auth.kind === "password") {
|
|
428
|
+
const pw = this.spec.auth.getPassword();
|
|
429
|
+
if (pw != null) {
|
|
430
|
+
this.spawn(["sshpass", "-e", ...base], { SSHPASS: pw });
|
|
431
|
+
this.masterUp = true;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
this.spawn(base);
|
|
436
|
+
this.masterUp = true;
|
|
437
|
+
}
|
|
438
|
+
run(argv, opts = {}) {
|
|
439
|
+
this.ensureReady();
|
|
440
|
+
return this.spawn([...sshConnectArgs(this.spec, { batch: true }), remoteShellCommand(argv, opts.cwd)]);
|
|
441
|
+
}
|
|
442
|
+
async runAsync(argv, opts = {}) {
|
|
443
|
+
return this.run(argv, opts);
|
|
444
|
+
}
|
|
445
|
+
exists(path) {
|
|
446
|
+
return this.run(["test", "-e", path]).exitCode === 0;
|
|
447
|
+
}
|
|
448
|
+
mkdirp(path) {
|
|
449
|
+
this.run(["mkdir", "-p", path]);
|
|
450
|
+
}
|
|
451
|
+
readFile(path) {
|
|
452
|
+
const r = this.run(["cat", path]);
|
|
453
|
+
return r.exitCode === 0 ? r.stdout : null;
|
|
454
|
+
}
|
|
455
|
+
readdir(path) {
|
|
456
|
+
const r = this.run(["ls", "-1A", path]);
|
|
457
|
+
if (r.exitCode !== 0)
|
|
458
|
+
return [];
|
|
459
|
+
return r.stdout.split(`
|
|
460
|
+
`).filter((s) => s.length > 0);
|
|
461
|
+
}
|
|
462
|
+
wrapCommand(command, opts = {}) {
|
|
463
|
+
const remote = opts.cwd ? `cd ${shQuote(opts.cwd)} && ${command}` : command;
|
|
464
|
+
return `${sshConnectArgs(this.spec, { tty: opts.tty }).join(" ")} ${shQuote(remote)}`;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function toResult(proc) {
|
|
468
|
+
return { stdout: proc.stdout ?? "", stderr: proc.stderr ?? "", exitCode: proc.status ?? -1 };
|
|
469
|
+
}
|
|
470
|
+
var defaultSpawner = (argv, env) => {
|
|
471
|
+
const [cmd, ...rest] = argv;
|
|
472
|
+
return toResult(spawnSync(cmd ?? "", rest, {
|
|
473
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
474
|
+
encoding: "utf8",
|
|
475
|
+
shell: false
|
|
476
|
+
}));
|
|
477
|
+
};
|
|
478
|
+
var init_exec_host = () => {};
|
|
479
|
+
|
|
480
|
+
// src/exec/keychain.ts
|
|
481
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
482
|
+
import { platform } from "os";
|
|
483
|
+
function remoteKeychainRef(host, user, port) {
|
|
484
|
+
return { service: KOBE_KEYCHAIN_SERVICE, account: port ? `${user}@${host}:${port}` : `${user}@${host}` };
|
|
485
|
+
}
|
|
486
|
+
function isKeychainSupported(deps = defaultDeps) {
|
|
487
|
+
return deps.platform() === "darwin";
|
|
488
|
+
}
|
|
489
|
+
function setKeychainPassword(ref, password, deps = defaultDeps) {
|
|
490
|
+
if (deps.platform() !== "darwin")
|
|
491
|
+
return false;
|
|
492
|
+
const { exitCode } = deps.run([
|
|
493
|
+
"security",
|
|
494
|
+
"add-generic-password",
|
|
495
|
+
"-U",
|
|
496
|
+
"-s",
|
|
497
|
+
ref.service,
|
|
498
|
+
"-a",
|
|
499
|
+
ref.account,
|
|
500
|
+
"-w",
|
|
501
|
+
password
|
|
502
|
+
]);
|
|
503
|
+
return exitCode === 0;
|
|
504
|
+
}
|
|
505
|
+
function getKeychainPassword(ref, deps = defaultDeps) {
|
|
506
|
+
if (deps.platform() !== "darwin")
|
|
507
|
+
return null;
|
|
508
|
+
const { stdout, exitCode } = deps.run([
|
|
509
|
+
"security",
|
|
510
|
+
"find-generic-password",
|
|
511
|
+
"-s",
|
|
512
|
+
ref.service,
|
|
513
|
+
"-a",
|
|
514
|
+
ref.account,
|
|
515
|
+
"-w"
|
|
516
|
+
]);
|
|
517
|
+
if (exitCode !== 0)
|
|
518
|
+
return null;
|
|
519
|
+
return stdout.replace(/\n$/, "");
|
|
520
|
+
}
|
|
521
|
+
var defaultDeps, KOBE_KEYCHAIN_SERVICE = "kobe-remote-ssh";
|
|
522
|
+
var init_keychain = __esm(() => {
|
|
523
|
+
defaultDeps = {
|
|
524
|
+
run(argv) {
|
|
525
|
+
const [cmd, ...rest] = argv;
|
|
526
|
+
const proc = spawnSync2(cmd ?? "", rest, { encoding: "utf8", shell: false });
|
|
527
|
+
return { stdout: proc.stdout ?? "", exitCode: proc.status ?? -1 };
|
|
528
|
+
},
|
|
529
|
+
platform() {
|
|
530
|
+
return platform();
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
|
|
335
535
|
// src/state/repos.ts
|
|
336
536
|
var exports_repos = {};
|
|
337
537
|
__export(exports_repos, {
|
|
@@ -340,18 +540,26 @@ __export(exports_repos, {
|
|
|
340
540
|
setPersistedString: () => setPersistedString,
|
|
341
541
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
342
542
|
removeSavedRepo: () => removeSavedRepo,
|
|
543
|
+
remoteRepoKey: () => remoteRepoKey,
|
|
343
544
|
normalizeSavedRepos: () => normalizeSavedRepos,
|
|
545
|
+
isRemoteRepoKey: () => isRemoteRepoKey,
|
|
546
|
+
isRemoteProjectsEnabled: () => isRemoteProjectsEnabled,
|
|
344
547
|
getSavedRepos: () => getSavedRepos,
|
|
345
548
|
getRepoInitOverride: () => getRepoInitOverride,
|
|
549
|
+
getRemoteRepos: () => getRemoteRepos,
|
|
550
|
+
getRemoteRepoConfig: () => getRemoteRepoConfig,
|
|
346
551
|
getPersistedString: () => getPersistedString,
|
|
347
552
|
getCustomEngineIds: () => getCustomEngineIds,
|
|
348
|
-
addSavedRepo: () => addSavedRepo
|
|
553
|
+
addSavedRepo: () => addSavedRepo,
|
|
554
|
+
addRemoteRepo: () => addRemoteRepo
|
|
349
555
|
});
|
|
350
|
-
import { spawnSync } from "child_process";
|
|
351
|
-
import { mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from "fs";
|
|
556
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
557
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, renameSync, writeFileSync } from "fs";
|
|
352
558
|
import { dirname } from "path";
|
|
353
559
|
function resolveRepoRoot(absPath) {
|
|
354
|
-
|
|
560
|
+
if (isRemoteRepoKey(absPath))
|
|
561
|
+
return absPath;
|
|
562
|
+
const r = spawnSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
355
563
|
cwd: absPath,
|
|
356
564
|
encoding: "utf8",
|
|
357
565
|
shell: false
|
|
@@ -372,7 +580,7 @@ function statePath() {
|
|
|
372
580
|
}
|
|
373
581
|
function load() {
|
|
374
582
|
try {
|
|
375
|
-
const text =
|
|
583
|
+
const text = readFileSync2(statePath(), "utf8");
|
|
376
584
|
const parsed = JSON.parse(text);
|
|
377
585
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
378
586
|
return parsed;
|
|
@@ -382,7 +590,7 @@ function load() {
|
|
|
382
590
|
}
|
|
383
591
|
function save(state) {
|
|
384
592
|
const path = statePath();
|
|
385
|
-
|
|
593
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
386
594
|
const tmp = `${path}.tmp`;
|
|
387
595
|
writeFileSync(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
388
596
|
renameSync(tmp, path);
|
|
@@ -491,10 +699,197 @@ function removeSavedRepo(absPath) {
|
|
|
491
699
|
save(state);
|
|
492
700
|
return { removed: true, path: absPath, total: cur.length - 1 };
|
|
493
701
|
}
|
|
702
|
+
function isRemoteRepoKey(key) {
|
|
703
|
+
return key.startsWith("ssh://");
|
|
704
|
+
}
|
|
705
|
+
function isRemoteProjectsEnabled() {
|
|
706
|
+
return load()["experimental.remoteProjects"] === true;
|
|
707
|
+
}
|
|
708
|
+
function remoteRepoKey(host, user, port) {
|
|
709
|
+
return port ? `ssh://${user}@${host}:${port}` : `ssh://${user}@${host}`;
|
|
710
|
+
}
|
|
711
|
+
function readRemoteRepos(state) {
|
|
712
|
+
const raw = state.remoteRepos;
|
|
713
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
714
|
+
return {};
|
|
715
|
+
return raw;
|
|
716
|
+
}
|
|
717
|
+
function getRemoteRepoConfig(key) {
|
|
718
|
+
return readRemoteRepos(load())[key] ?? null;
|
|
719
|
+
}
|
|
720
|
+
function getRemoteRepos() {
|
|
721
|
+
return readRemoteRepos(load());
|
|
722
|
+
}
|
|
723
|
+
function addRemoteRepo(config) {
|
|
724
|
+
const key = remoteRepoKey(config.host, config.user, config.port);
|
|
725
|
+
const state = load();
|
|
726
|
+
const repos = { ...readRemoteRepos(state) };
|
|
727
|
+
repos[key] = config;
|
|
728
|
+
state.remoteRepos = repos;
|
|
729
|
+
const saved = getSavedRepos();
|
|
730
|
+
const added = !saved.includes(key);
|
|
731
|
+
if (added)
|
|
732
|
+
state.savedRepos = [...saved, key];
|
|
733
|
+
save(state);
|
|
734
|
+
return { key, added };
|
|
735
|
+
}
|
|
494
736
|
var init_repos = __esm(() => {
|
|
495
737
|
init_env();
|
|
496
738
|
});
|
|
497
739
|
|
|
740
|
+
// src/cli/add-remote.ts
|
|
741
|
+
var exports_add_remote = {};
|
|
742
|
+
__export(exports_add_remote, {
|
|
743
|
+
runAddRemote: () => runAddRemote,
|
|
744
|
+
parseRemoteFlags: () => parseRemoteFlags
|
|
745
|
+
});
|
|
746
|
+
import { createInterface } from "readline";
|
|
747
|
+
function parseRemoteFlags(args) {
|
|
748
|
+
const f = {};
|
|
749
|
+
for (let i = 0;i < args.length; i++) {
|
|
750
|
+
const a = args[i];
|
|
751
|
+
switch (a) {
|
|
752
|
+
case "--host":
|
|
753
|
+
f.host = args[++i];
|
|
754
|
+
break;
|
|
755
|
+
case "--user":
|
|
756
|
+
f.user = args[++i];
|
|
757
|
+
break;
|
|
758
|
+
case "--path":
|
|
759
|
+
f.path = args[++i];
|
|
760
|
+
break;
|
|
761
|
+
case "--port": {
|
|
762
|
+
const n = Number(args[++i]);
|
|
763
|
+
if (!Number.isInteger(n) || n <= 0)
|
|
764
|
+
fail(`invalid --port "${args[i]}"`);
|
|
765
|
+
f.port = n;
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case "--key": {
|
|
769
|
+
const next = args[i + 1];
|
|
770
|
+
if (next && !next.startsWith("-")) {
|
|
771
|
+
f.key = { present: true, path: next };
|
|
772
|
+
i++;
|
|
773
|
+
} else {
|
|
774
|
+
f.key = { present: true };
|
|
775
|
+
}
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
case "--password":
|
|
779
|
+
f.password = true;
|
|
780
|
+
break;
|
|
781
|
+
case "--help":
|
|
782
|
+
case "-h":
|
|
783
|
+
process.stdout.write(USAGE);
|
|
784
|
+
process.exit(0);
|
|
785
|
+
break;
|
|
786
|
+
default:
|
|
787
|
+
fail(`unknown flag "${a}"`);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
return f;
|
|
791
|
+
}
|
|
792
|
+
function fail(msg) {
|
|
793
|
+
process.stderr.write(`kobe add --remote: ${msg}
|
|
794
|
+
|
|
795
|
+
${USAGE}`);
|
|
796
|
+
process.exit(2);
|
|
797
|
+
}
|
|
798
|
+
async function promptHidden(prompt) {
|
|
799
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
800
|
+
const out = process.stdout;
|
|
801
|
+
let muted = false;
|
|
802
|
+
const original = out._writeToOutput?.bind(out);
|
|
803
|
+
out._writeToOutput = (s) => {
|
|
804
|
+
if (!muted || !original)
|
|
805
|
+
process.stdout.write(s);
|
|
806
|
+
};
|
|
807
|
+
process.stdout.write(prompt);
|
|
808
|
+
muted = true;
|
|
809
|
+
return new Promise((res) => {
|
|
810
|
+
rl.question("", (answer) => {
|
|
811
|
+
muted = false;
|
|
812
|
+
if (original)
|
|
813
|
+
out._writeToOutput = original;
|
|
814
|
+
process.stdout.write(`
|
|
815
|
+
`);
|
|
816
|
+
rl.close();
|
|
817
|
+
res(answer);
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
async function runAddRemote(args) {
|
|
822
|
+
if (!isRemoteProjectsEnabled()) {
|
|
823
|
+
fail("remote projects are experimental and disabled \u2014 enable Settings \u2192 Dev \u2192 Experimental \u2192 Remote projects first");
|
|
824
|
+
}
|
|
825
|
+
const f = parseRemoteFlags(args);
|
|
826
|
+
if (!f.host)
|
|
827
|
+
fail("--host is required");
|
|
828
|
+
if (!f.user)
|
|
829
|
+
fail("--user is required");
|
|
830
|
+
if (!f.path)
|
|
831
|
+
fail("--path (remote base path) is required");
|
|
832
|
+
if (f.key && f.password)
|
|
833
|
+
fail("choose ONE of --key or --password, not both");
|
|
834
|
+
if (!f.key && !f.password)
|
|
835
|
+
fail("an auth method is required: --key [path] or --password");
|
|
836
|
+
let auth;
|
|
837
|
+
if (f.password) {
|
|
838
|
+
if (!isKeychainSupported())
|
|
839
|
+
fail("--password needs the macOS keychain (unsupported on this platform)");
|
|
840
|
+
const ref = remoteKeychainRef(f.host, f.user, f.port);
|
|
841
|
+
const pw = await promptHidden(`Password for ${f.user}@${f.host}: `);
|
|
842
|
+
if (pw.length === 0)
|
|
843
|
+
fail("empty password");
|
|
844
|
+
if (!setKeychainPassword(ref, pw))
|
|
845
|
+
fail("failed to store the password in the keychain");
|
|
846
|
+
auth = { kind: "password", keychainRef: ref };
|
|
847
|
+
} else {
|
|
848
|
+
auth = { kind: "key", keyPath: f.key?.path };
|
|
849
|
+
}
|
|
850
|
+
const { key, added } = addRemoteRepo({ host: f.host, user: f.user, port: f.port, basePath: f.path, auth });
|
|
851
|
+
console.log(added ? `added remote project ${key} (base ${f.path})` : `updated remote project ${key} (base ${f.path})`);
|
|
852
|
+
await probe(f, auth);
|
|
853
|
+
}
|
|
854
|
+
async function probe(f, auth) {
|
|
855
|
+
if (auth.kind === "password" && !isKeychainSupported())
|
|
856
|
+
return;
|
|
857
|
+
const runtimeAuth = auth.kind === "key" ? { kind: "key", keyPath: auth.keyPath } : { kind: "password", getPassword: () => getKeychainPassword(auth.keychainRef) };
|
|
858
|
+
const spec = {
|
|
859
|
+
host: f.host,
|
|
860
|
+
user: f.user,
|
|
861
|
+
port: f.port,
|
|
862
|
+
auth: runtimeAuth,
|
|
863
|
+
controlPath: remoteControlSocketPath(f.host, f.user, f.port)
|
|
864
|
+
};
|
|
865
|
+
process.stdout.write("checking connection\u2026 ");
|
|
866
|
+
try {
|
|
867
|
+
const host = new RemoteExecHost(spec);
|
|
868
|
+
const r = host.run(["test", "-d", f.path]);
|
|
869
|
+
if (r.exitCode === 0)
|
|
870
|
+
console.log("ok");
|
|
871
|
+
else
|
|
872
|
+
console.log(`reachable, but base path "${f.path}" is not a directory (you can create it later)`);
|
|
873
|
+
} catch (err) {
|
|
874
|
+
console.log(`could not connect (${err instanceof Error ? err.message : String(err)})`);
|
|
875
|
+
console.log("the project is saved; fix the host/credentials and it will connect on first use.");
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
var USAGE;
|
|
879
|
+
var init_add_remote = __esm(() => {
|
|
880
|
+
init_env();
|
|
881
|
+
init_exec_host();
|
|
882
|
+
init_keychain();
|
|
883
|
+
init_repos();
|
|
884
|
+
USAGE = `Usage: kobe add --remote --host <host> --user <user> --path <basePath>
|
|
885
|
+
` + ` [--port N] [--key [path] | --password]
|
|
886
|
+
|
|
887
|
+
` + `Register an SSH-backed project. Worktrees + the engine run on <host> under
|
|
888
|
+
` + `<basePath>. Choose ONE auth: --key [path] (ssh-agent when path omitted) or
|
|
889
|
+
` + `--password (prompted, stored in the OS keychain \u2014 never in state.json).
|
|
890
|
+
`;
|
|
891
|
+
});
|
|
892
|
+
|
|
498
893
|
// src/types/task.ts
|
|
499
894
|
var toTaskId = (id) => id, DEFAULT_TASK_VENDOR = "claude";
|
|
500
895
|
|
|
@@ -866,28 +1261,40 @@ var init_store = __esm(() => {
|
|
|
866
1261
|
init_ulid();
|
|
867
1262
|
});
|
|
868
1263
|
|
|
869
|
-
// src/
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
shell: false
|
|
880
|
-
});
|
|
881
|
-
const result = {
|
|
882
|
-
stdout: proc.stdout ?? "",
|
|
883
|
-
stderr: proc.stderr ?? "",
|
|
884
|
-
exitCode: proc.status ?? -1
|
|
1264
|
+
// src/exec/resolve.ts
|
|
1265
|
+
function remoteSpecFromConfig(config) {
|
|
1266
|
+
const cfgAuth = config.auth;
|
|
1267
|
+
const auth = cfgAuth.kind === "key" ? { kind: "key", keyPath: cfgAuth.keyPath } : { kind: "password", getPassword: () => getKeychainPassword(cfgAuth.keychainRef) };
|
|
1268
|
+
return {
|
|
1269
|
+
host: config.host,
|
|
1270
|
+
user: config.user,
|
|
1271
|
+
port: config.port,
|
|
1272
|
+
auth,
|
|
1273
|
+
controlPath: remoteControlSocketPath(config.host, config.user, config.port)
|
|
885
1274
|
};
|
|
886
|
-
|
|
887
|
-
|
|
1275
|
+
}
|
|
1276
|
+
function execHostForRepo(repoKey) {
|
|
1277
|
+
const config = getRemoteRepoConfig(repoKey);
|
|
1278
|
+
if (!config)
|
|
1279
|
+
return new LocalExecHost;
|
|
1280
|
+
return new RemoteExecHost(remoteSpecFromConfig(config));
|
|
1281
|
+
}
|
|
1282
|
+
function execHostForWorktreePath(worktreePath) {
|
|
1283
|
+
for (const config of Object.values(getRemoteRepos())) {
|
|
1284
|
+
if (worktreePath === config.basePath || worktreePath.startsWith(`${config.basePath}/`)) {
|
|
1285
|
+
return new RemoteExecHost(remoteSpecFromConfig(config));
|
|
1286
|
+
}
|
|
888
1287
|
}
|
|
889
|
-
return
|
|
1288
|
+
return new LocalExecHost;
|
|
890
1289
|
}
|
|
1290
|
+
var init_resolve = __esm(() => {
|
|
1291
|
+
init_env();
|
|
1292
|
+
init_repos();
|
|
1293
|
+
init_exec_host();
|
|
1294
|
+
init_keychain();
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
// src/orchestrator/worktree/git.ts
|
|
891
1298
|
var GitCommandError;
|
|
892
1299
|
var init_git = __esm(() => {
|
|
893
1300
|
GitCommandError = class GitCommandError extends Error {
|
|
@@ -934,6 +1341,12 @@ function worktreePathFor(repo, slug) {
|
|
|
934
1341
|
return path.join(worktreeRootFor(repo), slug);
|
|
935
1342
|
}
|
|
936
1343
|
function listWorktreeDirNames(repo) {
|
|
1344
|
+
if (isRemoteRepoKey(repo)) {
|
|
1345
|
+
const basePath = getRemoteRepoConfig(repo)?.basePath;
|
|
1346
|
+
if (!basePath)
|
|
1347
|
+
return [];
|
|
1348
|
+
return execHostForRepo(repo).readdir(remoteWorktreeRootFor(basePath));
|
|
1349
|
+
}
|
|
937
1350
|
const names = new Set;
|
|
938
1351
|
for (const root of managedWorktreeRootsFor(repo)) {
|
|
939
1352
|
try {
|
|
@@ -961,6 +1374,18 @@ function managedWorktreeRootForPath(repo, candidate) {
|
|
|
961
1374
|
function isKobeManagedPath(repo, candidate) {
|
|
962
1375
|
return managedWorktreeRootForPath(repo, candidate) !== null;
|
|
963
1376
|
}
|
|
1377
|
+
function remoteWorktreeRootFor(basePath) {
|
|
1378
|
+
return `${stripTrailingSlash(basePath)}/.kobe/worktrees`;
|
|
1379
|
+
}
|
|
1380
|
+
function remoteWorktreePathFor(basePath, slug) {
|
|
1381
|
+
if (!slug || /[/\\\0]/.test(slug)) {
|
|
1382
|
+
throw new Error(`remoteWorktreePathFor: invalid slug: ${JSON.stringify(slug)}`);
|
|
1383
|
+
}
|
|
1384
|
+
return `${remoteWorktreeRootFor(basePath)}/${slug}`;
|
|
1385
|
+
}
|
|
1386
|
+
function stripTrailingSlash(p) {
|
|
1387
|
+
return p.length > 1 && p.endsWith("/") ? p.replace(/\/+$/, "") : p;
|
|
1388
|
+
}
|
|
964
1389
|
function canonicalize(p) {
|
|
965
1390
|
try {
|
|
966
1391
|
return fs.realpathSync(p);
|
|
@@ -977,6 +1402,8 @@ function repoWorktreeDirName(repo) {
|
|
|
977
1402
|
var KOBE_WORKTREE_ROOT_DIR = "worktrees", REPO_LOCAL_KOBE_WORKTREE_ROOT_SUBPATH = ".kobe/worktrees", LEGACY_KOBE_WORKTREE_ROOT_SUBPATH = ".claude/worktrees", REPO_LOCAL_KOBE_MANAGED_WORKTREE_ROOT_SUBPATHS;
|
|
978
1403
|
var init_paths = __esm(() => {
|
|
979
1404
|
init_env();
|
|
1405
|
+
init_resolve();
|
|
1406
|
+
init_repos();
|
|
980
1407
|
REPO_LOCAL_KOBE_MANAGED_WORKTREE_ROOT_SUBPATHS = [
|
|
981
1408
|
REPO_LOCAL_KOBE_WORKTREE_ROOT_SUBPATH,
|
|
982
1409
|
LEGACY_KOBE_WORKTREE_ROOT_SUBPATH
|
|
@@ -992,13 +1419,33 @@ import fs2 from "fs";
|
|
|
992
1419
|
import path2 from "path";
|
|
993
1420
|
|
|
994
1421
|
class GitWorktreeManager {
|
|
1422
|
+
execDeps;
|
|
1423
|
+
constructor(execDeps = defaultExecDeps) {
|
|
1424
|
+
this.execDeps = execDeps;
|
|
1425
|
+
}
|
|
1426
|
+
ctxFor(repoKey) {
|
|
1427
|
+
const basePath = this.execDeps.remoteBasePath(repoKey);
|
|
1428
|
+
return basePath ? { exec: this.execDeps.execForRepo(repoKey), dir: basePath, remote: true } : { exec: this.execDeps.execForRepo(repoKey), dir: repoKey, remote: false };
|
|
1429
|
+
}
|
|
1430
|
+
runGit(exec, args, opts) {
|
|
1431
|
+
if (!opts.cwd) {
|
|
1432
|
+
throw new Error("runGit(): cwd is required; refusing to inherit from process.cwd()");
|
|
1433
|
+
}
|
|
1434
|
+
const r = exec.run(["git", ...args], { cwd: opts.cwd, env: opts.env });
|
|
1435
|
+
const result = { stdout: r.stdout, stderr: r.stderr, exitCode: r.exitCode };
|
|
1436
|
+
if (result.exitCode !== 0 && !opts.allowFail) {
|
|
1437
|
+
throw new GitCommandError(args, opts.cwd, result);
|
|
1438
|
+
}
|
|
1439
|
+
return result;
|
|
1440
|
+
}
|
|
995
1441
|
async create(repo, branch, worktreePath, baseRef) {
|
|
996
|
-
|
|
1442
|
+
const ctx = this.ctxFor(repo);
|
|
1443
|
+
requireAbsolute("repo", ctx.dir);
|
|
997
1444
|
requireAbsolute("path", worktreePath);
|
|
998
1445
|
if (!branch)
|
|
999
1446
|
throw new Error("create(): branch must be a non-empty string");
|
|
1000
|
-
if (
|
|
1001
|
-
const existing = await this.tryDescribe(
|
|
1447
|
+
if (ctx.exec.exists(worktreePath)) {
|
|
1448
|
+
const existing = await this.tryDescribe(ctx, worktreePath);
|
|
1002
1449
|
if (existing) {
|
|
1003
1450
|
if (existing.branch !== branch) {
|
|
1004
1451
|
throw new Error(`worktree at ${worktreePath} is on branch '${existing.branch}', refusing to hijack to '${branch}'`);
|
|
@@ -1007,11 +1454,11 @@ class GitWorktreeManager {
|
|
|
1007
1454
|
}
|
|
1008
1455
|
throw new Error(`create(): ${worktreePath} exists but is not a registered git worktree`);
|
|
1009
1456
|
}
|
|
1010
|
-
|
|
1011
|
-
const branchExists = this.branchExists(
|
|
1457
|
+
ctx.exec.mkdirp(path2.dirname(worktreePath));
|
|
1458
|
+
const branchExists = this.branchExists(ctx, branch);
|
|
1012
1459
|
const args = branchExists ? ["worktree", "add", worktreePath, branch] : baseRef ? ["worktree", "add", "-b", branch, worktreePath, baseRef] : ["worktree", "add", "-b", branch, worktreePath];
|
|
1013
|
-
|
|
1014
|
-
const info = await this.tryDescribe(
|
|
1460
|
+
this.runGit(ctx.exec, args, { cwd: ctx.dir });
|
|
1461
|
+
const info = await this.tryDescribe(ctx, worktreePath);
|
|
1015
1462
|
if (!info) {
|
|
1016
1463
|
throw new Error(`create(): git reported success but ${worktreePath} is not a worktree`);
|
|
1017
1464
|
}
|
|
@@ -1021,19 +1468,21 @@ class GitWorktreeManager {
|
|
|
1021
1468
|
return info;
|
|
1022
1469
|
}
|
|
1023
1470
|
async createForTask(args) {
|
|
1024
|
-
const
|
|
1471
|
+
const basePath = this.execDeps.remoteBasePath(args.repo);
|
|
1472
|
+
const target = basePath ? remoteWorktreePathFor(basePath, args.slug) : worktreePathFor(args.repo, args.slug);
|
|
1025
1473
|
return this.create(args.repo, args.branch, target, args.baseRef);
|
|
1026
1474
|
}
|
|
1027
1475
|
async remove(worktreePath, opts) {
|
|
1028
1476
|
requireAbsolute("path", worktreePath);
|
|
1477
|
+
const exec = this.execDeps.execForPath(worktreePath);
|
|
1029
1478
|
const force = opts?.force === true;
|
|
1030
|
-
if (!
|
|
1031
|
-
const repo2 = this.findRepoFor(worktreePath);
|
|
1479
|
+
if (!exec.exists(worktreePath)) {
|
|
1480
|
+
const repo2 = this.findRepoFor(exec, worktreePath);
|
|
1032
1481
|
if (repo2)
|
|
1033
|
-
|
|
1482
|
+
this.runGit(exec, ["worktree", "prune"], { cwd: repo2, allowFail: true });
|
|
1034
1483
|
return;
|
|
1035
1484
|
}
|
|
1036
|
-
const repo = this.findRepoFor(worktreePath);
|
|
1485
|
+
const repo = this.findRepoFor(exec, worktreePath);
|
|
1037
1486
|
if (!repo) {
|
|
1038
1487
|
throw new Error(`remove(): ${worktreePath} is not a git worktree`);
|
|
1039
1488
|
}
|
|
@@ -1044,18 +1493,19 @@ class GitWorktreeManager {
|
|
|
1044
1493
|
}
|
|
1045
1494
|
}
|
|
1046
1495
|
const args = force ? ["worktree", "remove", "--force", worktreePath] : ["worktree", "remove", worktreePath];
|
|
1047
|
-
|
|
1048
|
-
|
|
1496
|
+
this.runGit(exec, args, { cwd: repo });
|
|
1497
|
+
this.runGit(exec, ["worktree", "prune"], { cwd: repo, allowFail: true });
|
|
1049
1498
|
}
|
|
1050
1499
|
async list(repo) {
|
|
1051
|
-
|
|
1052
|
-
|
|
1500
|
+
const ctx = this.ctxFor(repo);
|
|
1501
|
+
requireAbsolute("repo", ctx.dir);
|
|
1502
|
+
const out = this.runGit(ctx.exec, ["worktree", "list", "--porcelain"], { cwd: ctx.dir });
|
|
1053
1503
|
const all = parsePorcelain(out.stdout);
|
|
1054
1504
|
const infos = [];
|
|
1055
1505
|
for (const entry of all) {
|
|
1056
1506
|
if (!entry.path)
|
|
1057
1507
|
continue;
|
|
1058
|
-
const callerRoot = managedWorktreeRootForPath(repo, entry.path);
|
|
1508
|
+
const callerRoot = ctx.remote ? remoteManagedRootForPath(ctx.dir, entry.path) : managedWorktreeRootForPath(repo, entry.path);
|
|
1059
1509
|
if (!callerRoot)
|
|
1060
1510
|
continue;
|
|
1061
1511
|
if (!entry.branch || entry.detached)
|
|
@@ -1075,10 +1525,11 @@ class GitWorktreeManager {
|
|
|
1075
1525
|
return infos;
|
|
1076
1526
|
}
|
|
1077
1527
|
async listAll(repo) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1528
|
+
const ctx = this.ctxFor(repo);
|
|
1529
|
+
requireAbsolute("repo", ctx.dir);
|
|
1530
|
+
const out = this.runGit(ctx.exec, ["worktree", "list", "--porcelain"], { cwd: ctx.dir });
|
|
1080
1531
|
const all = parsePorcelain(out.stdout);
|
|
1081
|
-
const canonRepo = canonicalize2(
|
|
1532
|
+
const canonRepo = ctx.remote ? ctx.dir : canonicalize2(ctx.dir);
|
|
1082
1533
|
const infos = [];
|
|
1083
1534
|
for (const entry of all) {
|
|
1084
1535
|
if (!entry.path)
|
|
@@ -1087,7 +1538,7 @@ class GitWorktreeManager {
|
|
|
1087
1538
|
continue;
|
|
1088
1539
|
if (!entry.branch || entry.detached)
|
|
1089
1540
|
continue;
|
|
1090
|
-
if (canonicalize2(entry.path) === canonRepo)
|
|
1541
|
+
if ((ctx.remote ? entry.path : canonicalize2(entry.path)) === canonRepo)
|
|
1091
1542
|
continue;
|
|
1092
1543
|
const dirty = await this.isDirty(entry.path);
|
|
1093
1544
|
infos.push({
|
|
@@ -1095,34 +1546,37 @@ class GitWorktreeManager {
|
|
|
1095
1546
|
branch: entry.branch,
|
|
1096
1547
|
head: entry.head ?? "",
|
|
1097
1548
|
dirty,
|
|
1098
|
-
kobeManaged: isKobeManagedPath(repo, entry.path),
|
|
1099
|
-
lastActivityMs: this.lastActivityMs(entry.path)
|
|
1549
|
+
kobeManaged: ctx.remote ? remoteManagedRootForPath(ctx.dir, entry.path) !== null : isKobeManagedPath(repo, entry.path),
|
|
1550
|
+
lastActivityMs: this.lastActivityMs(ctx.exec, entry.path)
|
|
1100
1551
|
});
|
|
1101
1552
|
}
|
|
1102
1553
|
infos.sort((a, b) => b.lastActivityMs - a.lastActivityMs);
|
|
1103
1554
|
return infos;
|
|
1104
1555
|
}
|
|
1105
|
-
lastActivityMs(worktreePath) {
|
|
1556
|
+
lastActivityMs(exec, worktreePath) {
|
|
1106
1557
|
try {
|
|
1107
|
-
const out =
|
|
1558
|
+
const out = this.runGit(exec, ["log", "-1", "--format=%ct"], { cwd: worktreePath });
|
|
1108
1559
|
const secs = Number.parseInt(out.stdout.trim(), 10);
|
|
1109
1560
|
if (Number.isFinite(secs) && secs > 0)
|
|
1110
1561
|
return secs * 1000;
|
|
1111
1562
|
} catch {}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1563
|
+
if (!exec.isRemote) {
|
|
1564
|
+
try {
|
|
1565
|
+
return fs2.statSync(worktreePath).mtimeMs;
|
|
1566
|
+
} catch {}
|
|
1116
1567
|
}
|
|
1568
|
+
return 0;
|
|
1117
1569
|
}
|
|
1118
1570
|
async isDirty(worktreePath) {
|
|
1119
1571
|
requireAbsolute("path", worktreePath);
|
|
1120
|
-
const
|
|
1572
|
+
const exec = this.execDeps.execForPath(worktreePath);
|
|
1573
|
+
const out = this.runGit(exec, ["status", "--porcelain"], { cwd: worktreePath });
|
|
1121
1574
|
return out.stdout.length > 0;
|
|
1122
1575
|
}
|
|
1123
1576
|
async currentBranch(worktreePath) {
|
|
1124
1577
|
requireAbsolute("path", worktreePath);
|
|
1125
|
-
const
|
|
1578
|
+
const exec = this.execDeps.execForPath(worktreePath);
|
|
1579
|
+
const out = this.runGit(exec, ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: worktreePath });
|
|
1126
1580
|
const name = out.stdout.trim();
|
|
1127
1581
|
if (!name || name === "HEAD") {
|
|
1128
1582
|
throw new Error(`currentBranch(): ${worktreePath} is in detached-HEAD state`);
|
|
@@ -1133,16 +1587,18 @@ class GitWorktreeManager {
|
|
|
1133
1587
|
requireAbsolute("path", worktreePath);
|
|
1134
1588
|
if (from === to)
|
|
1135
1589
|
return;
|
|
1136
|
-
const
|
|
1590
|
+
const exec = this.execDeps.execForPath(worktreePath);
|
|
1591
|
+
const repo = this.findRepoFor(exec, worktreePath);
|
|
1137
1592
|
if (!repo)
|
|
1138
1593
|
throw new Error(`renameBranch(): ${worktreePath} is not a git worktree`);
|
|
1139
|
-
|
|
1594
|
+
this.runGit(exec, ["branch", "-m", from, to], { cwd: repo });
|
|
1140
1595
|
}
|
|
1141
|
-
async tryDescribe(
|
|
1142
|
-
const out =
|
|
1596
|
+
async tryDescribe(ctx, worktreePath) {
|
|
1597
|
+
const out = this.runGit(ctx.exec, ["worktree", "list", "--porcelain"], { cwd: ctx.dir });
|
|
1143
1598
|
const entries = parsePorcelain(out.stdout);
|
|
1144
|
-
const
|
|
1145
|
-
const
|
|
1599
|
+
const norm = (p) => ctx.remote ? p : canonicalize2(p);
|
|
1600
|
+
const target = norm(worktreePath);
|
|
1601
|
+
const match = entries.find((e) => e.path && norm(e.path) === target);
|
|
1146
1602
|
if (!match || !match.path || !match.branch || match.detached)
|
|
1147
1603
|
return null;
|
|
1148
1604
|
return {
|
|
@@ -1152,14 +1608,14 @@ class GitWorktreeManager {
|
|
|
1152
1608
|
dirty: await this.isDirty(match.path)
|
|
1153
1609
|
};
|
|
1154
1610
|
}
|
|
1155
|
-
branchExists(
|
|
1611
|
+
branchExists(ctx, branch) {
|
|
1156
1612
|
const ref = `refs/heads/${branch}`;
|
|
1157
|
-
const out =
|
|
1613
|
+
const out = this.runGit(ctx.exec, ["show-ref", "--verify", "--quiet", ref], { cwd: ctx.dir, allowFail: true });
|
|
1158
1614
|
return out.exitCode === 0;
|
|
1159
1615
|
}
|
|
1160
|
-
findRepoFor(worktreePath) {
|
|
1616
|
+
findRepoFor(exec, worktreePath) {
|
|
1161
1617
|
try {
|
|
1162
|
-
const out =
|
|
1618
|
+
const out = this.runGit(exec, ["rev-parse", "--git-common-dir"], { cwd: worktreePath, allowFail: true });
|
|
1163
1619
|
if (out.exitCode !== 0)
|
|
1164
1620
|
return null;
|
|
1165
1621
|
const gitDir = out.stdout.trim();
|
|
@@ -1206,6 +1662,10 @@ function parsePorcelain(out) {
|
|
|
1206
1662
|
records.push(current);
|
|
1207
1663
|
return records;
|
|
1208
1664
|
}
|
|
1665
|
+
function remoteManagedRootForPath(basePath, candidate) {
|
|
1666
|
+
const root = `${basePath.replace(/\/+$/, "")}/.kobe/worktrees`;
|
|
1667
|
+
return candidate === root || candidate.startsWith(`${root}/`) ? root : null;
|
|
1668
|
+
}
|
|
1209
1669
|
function requireAbsolute(name, value) {
|
|
1210
1670
|
if (!value || !path2.isAbsolute(value)) {
|
|
1211
1671
|
throw new Error(`${name} must be an absolute path, got: ${JSON.stringify(value)}`);
|
|
@@ -1218,9 +1678,19 @@ function canonicalize2(p) {
|
|
|
1218
1678
|
return path2.resolve(p);
|
|
1219
1679
|
}
|
|
1220
1680
|
}
|
|
1681
|
+
var defaultExecDeps;
|
|
1221
1682
|
var init_manager = __esm(() => {
|
|
1683
|
+
init_resolve();
|
|
1684
|
+
init_repos();
|
|
1222
1685
|
init_git();
|
|
1223
1686
|
init_paths();
|
|
1687
|
+
defaultExecDeps = {
|
|
1688
|
+
execForRepo: execHostForRepo,
|
|
1689
|
+
execForPath: execHostForWorktreePath,
|
|
1690
|
+
remoteBasePath(repoKey) {
|
|
1691
|
+
return getRemoteRepoConfig(repoKey)?.basePath ?? null;
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1224
1694
|
});
|
|
1225
1695
|
|
|
1226
1696
|
// ../../node_modules/.bun/solid-js@1.9.12/node_modules/solid-js/dist/dev.js
|
|
@@ -2992,6 +3462,9 @@ __export(exports_core, {
|
|
|
2992
3462
|
});
|
|
2993
3463
|
import { realpathSync as realpathSync2 } from "fs";
|
|
2994
3464
|
import { basename as basename2, resolve } from "path";
|
|
3465
|
+
function repoWorkingDir(repo) {
|
|
3466
|
+
return getRemoteRepoConfig(repo)?.basePath ?? repo;
|
|
3467
|
+
}
|
|
2995
3468
|
|
|
2996
3469
|
class Orchestrator {
|
|
2997
3470
|
store;
|
|
@@ -3081,7 +3554,7 @@ class Orchestrator {
|
|
|
3081
3554
|
repo,
|
|
3082
3555
|
title: titleFromRepo(repo),
|
|
3083
3556
|
branch: "",
|
|
3084
|
-
worktreePath: repo,
|
|
3557
|
+
worktreePath: repoWorkingDir(repo),
|
|
3085
3558
|
status: "backlog",
|
|
3086
3559
|
kind: "main",
|
|
3087
3560
|
vendor: DEFAULT_TASK_VENDOR
|
|
@@ -3098,7 +3571,7 @@ class Orchestrator {
|
|
|
3098
3571
|
async ensureWorktree(id) {
|
|
3099
3572
|
const task = this.requireTask(id);
|
|
3100
3573
|
if (task.kind === "main")
|
|
3101
|
-
return task.repo;
|
|
3574
|
+
return repoWorkingDir(task.repo);
|
|
3102
3575
|
if (task.worktreePath)
|
|
3103
3576
|
return task.worktreePath;
|
|
3104
3577
|
const inflight = this.worktreeLocks.get(task.id);
|
|
@@ -3315,6 +3788,7 @@ function canonPath(p) {
|
|
|
3315
3788
|
var PLACEHOLDER_TASK_TITLE = "(new task)";
|
|
3316
3789
|
var init_core = __esm(() => {
|
|
3317
3790
|
init_dev();
|
|
3791
|
+
init_repos();
|
|
3318
3792
|
init_errors();
|
|
3319
3793
|
init_slug_allocator();
|
|
3320
3794
|
});
|
|
@@ -3839,7 +4313,7 @@ async function latestTranscriptMtimeForWorktree(worktree) {
|
|
|
3839
4313
|
const files = await listSessionFilesForWorktree(worktree);
|
|
3840
4314
|
return files[0]?.mtimeMs ?? 0;
|
|
3841
4315
|
}
|
|
3842
|
-
async function readHistory(sessionId, deps =
|
|
4316
|
+
async function readHistory(sessionId, deps = defaultDeps2) {
|
|
3843
4317
|
const root = deps.projectsDir();
|
|
3844
4318
|
const projectDirs = await deps.readdir(root);
|
|
3845
4319
|
for (const dir of projectDirs) {
|
|
@@ -3916,9 +4390,9 @@ function extractUsage(v) {
|
|
|
3916
4390
|
function isObject(v) {
|
|
3917
4391
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3918
4392
|
}
|
|
3919
|
-
var
|
|
4393
|
+
var defaultDeps2;
|
|
3920
4394
|
var init_history = __esm(() => {
|
|
3921
|
-
|
|
4395
|
+
defaultDeps2 = {
|
|
3922
4396
|
projectsDir() {
|
|
3923
4397
|
return path3.join(homedir4(), ".claude", "projects");
|
|
3924
4398
|
},
|
|
@@ -4024,7 +4498,7 @@ function validPositive(v) {
|
|
|
4024
4498
|
import { readFile as readFile3, readdir as readdir2, stat as stat2, unlink as unlink3 } from "fs/promises";
|
|
4025
4499
|
import { homedir as homedir5 } from "os";
|
|
4026
4500
|
import path4 from "path";
|
|
4027
|
-
async function listRolloutFiles(deps =
|
|
4501
|
+
async function listRolloutFiles(deps = defaultDeps3) {
|
|
4028
4502
|
const root = deps.sessionsDir();
|
|
4029
4503
|
const years = (await deps.readdir(root)).sort().reverse();
|
|
4030
4504
|
const out = [];
|
|
@@ -4045,7 +4519,7 @@ async function listRolloutFiles(deps = defaultDeps2) {
|
|
|
4045
4519
|
}
|
|
4046
4520
|
return out;
|
|
4047
4521
|
}
|
|
4048
|
-
async function findRolloutFile(sessionId, deps =
|
|
4522
|
+
async function findRolloutFile(sessionId, deps = defaultDeps3) {
|
|
4049
4523
|
const all = await listRolloutFiles(deps);
|
|
4050
4524
|
for (const p of all) {
|
|
4051
4525
|
if (path4.basename(p).endsWith(`-${sessionId}.jsonl`))
|
|
@@ -4068,7 +4542,7 @@ function rolloutCwd(raw) {
|
|
|
4068
4542
|
}
|
|
4069
4543
|
return "";
|
|
4070
4544
|
}
|
|
4071
|
-
async function listSessionIdsForWorktree(worktree, deps =
|
|
4545
|
+
async function listSessionIdsForWorktree(worktree, deps = defaultDeps3) {
|
|
4072
4546
|
if (!worktree)
|
|
4073
4547
|
return [];
|
|
4074
4548
|
const files = await listRolloutFiles(deps);
|
|
@@ -4092,7 +4566,7 @@ async function listSessionIdsForWorktree(worktree, deps = defaultDeps2) {
|
|
|
4092
4566
|
}
|
|
4093
4567
|
return matches.reverse();
|
|
4094
4568
|
}
|
|
4095
|
-
async function latestTranscriptMtimeForWorktree2(worktree, deps =
|
|
4569
|
+
async function latestTranscriptMtimeForWorktree2(worktree, deps = defaultDeps3) {
|
|
4096
4570
|
if (!worktree)
|
|
4097
4571
|
return 0;
|
|
4098
4572
|
const files = await listRolloutFiles(deps);
|
|
@@ -4117,10 +4591,10 @@ async function latestTranscriptMtimeForWorktree2(worktree, deps = defaultDeps2)
|
|
|
4117
4591
|
}
|
|
4118
4592
|
return 0;
|
|
4119
4593
|
}
|
|
4120
|
-
async function readHistory2(sessionId, deps =
|
|
4594
|
+
async function readHistory2(sessionId, deps = defaultDeps3) {
|
|
4121
4595
|
return (await readHistoryWithMetrics(sessionId, deps)).messages;
|
|
4122
4596
|
}
|
|
4123
|
-
async function readHistoryWithMetrics(sessionId, deps =
|
|
4597
|
+
async function readHistoryWithMetrics(sessionId, deps = defaultDeps3) {
|
|
4124
4598
|
const file = await findRolloutFile(sessionId, deps);
|
|
4125
4599
|
if (!file)
|
|
4126
4600
|
return { messages: [] };
|
|
@@ -4348,10 +4822,10 @@ function parseTimestampMs(value) {
|
|
|
4348
4822
|
function isObject3(v) {
|
|
4349
4823
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4350
4824
|
}
|
|
4351
|
-
var
|
|
4825
|
+
var defaultDeps3, UUID_AT_END, MAX_WORKTREE_SCAN = 200, MAX_MTIME_SCAN = 12;
|
|
4352
4826
|
var init_history2 = __esm(() => {
|
|
4353
4827
|
init_synthetic();
|
|
4354
|
-
|
|
4828
|
+
defaultDeps3 = {
|
|
4355
4829
|
sessionsDir() {
|
|
4356
4830
|
return path4.join(homedir5(), ".codex", "sessions");
|
|
4357
4831
|
},
|
|
@@ -4408,12 +4882,12 @@ function numberOr2(value, fallback) {
|
|
|
4408
4882
|
import { readFile as readFile4, readdir as readdir3, rm, stat as stat3 } from "fs/promises";
|
|
4409
4883
|
import { homedir as homedir6 } from "os";
|
|
4410
4884
|
import path5 from "path";
|
|
4411
|
-
async function listSessionDirs(deps =
|
|
4885
|
+
async function listSessionDirs(deps = defaultDeps4) {
|
|
4412
4886
|
const root = path5.join(deps.copilotDir(), "session-state");
|
|
4413
4887
|
const names = await deps.readdir(root);
|
|
4414
4888
|
return names.map((name) => path5.join(root, name));
|
|
4415
4889
|
}
|
|
4416
|
-
async function listSessionIdsForWorktree2(worktree, deps =
|
|
4890
|
+
async function listSessionIdsForWorktree2(worktree, deps = defaultDeps4) {
|
|
4417
4891
|
const matches = [];
|
|
4418
4892
|
for (const dir of await listSessionDirs(deps)) {
|
|
4419
4893
|
const workspace = await readWorkspace(dir, deps);
|
|
@@ -4423,7 +4897,7 @@ async function listSessionIdsForWorktree2(worktree, deps = defaultDeps3) {
|
|
|
4423
4897
|
}
|
|
4424
4898
|
return matches.sort((a, b) => a.updatedAt.localeCompare(b.updatedAt)).map((m) => m.id);
|
|
4425
4899
|
}
|
|
4426
|
-
async function latestTranscriptMtimeForWorktree3(worktree, deps =
|
|
4900
|
+
async function latestTranscriptMtimeForWorktree3(worktree, deps = defaultDeps4) {
|
|
4427
4901
|
if (!worktree)
|
|
4428
4902
|
return 0;
|
|
4429
4903
|
let newest = 0;
|
|
@@ -4439,7 +4913,7 @@ async function latestTranscriptMtimeForWorktree3(worktree, deps = defaultDeps3)
|
|
|
4439
4913
|
}
|
|
4440
4914
|
return newest;
|
|
4441
4915
|
}
|
|
4442
|
-
async function readHistoryWithMetrics2(sessionId, deps =
|
|
4916
|
+
async function readHistoryWithMetrics2(sessionId, deps = defaultDeps4) {
|
|
4443
4917
|
const dir = await findSessionDir(sessionId, deps);
|
|
4444
4918
|
if (!dir)
|
|
4445
4919
|
return { messages: [] };
|
|
@@ -4447,10 +4921,10 @@ async function readHistoryWithMetrics2(sessionId, deps = defaultDeps3) {
|
|
|
4447
4921
|
const parsed = parseEvents(raw, sessionId);
|
|
4448
4922
|
return { messages: parsed.messages, ...parsed.usageMetrics ? { usageMetrics: parsed.usageMetrics } : {} };
|
|
4449
4923
|
}
|
|
4450
|
-
async function readHistory3(sessionId, deps =
|
|
4924
|
+
async function readHistory3(sessionId, deps = defaultDeps4) {
|
|
4451
4925
|
return (await readHistoryWithMetrics2(sessionId, deps)).messages;
|
|
4452
4926
|
}
|
|
4453
|
-
async function findSessionDir(sessionId, deps =
|
|
4927
|
+
async function findSessionDir(sessionId, deps = defaultDeps4) {
|
|
4454
4928
|
for (const dir of await listSessionDirs(deps)) {
|
|
4455
4929
|
if (path5.basename(dir) === sessionId)
|
|
4456
4930
|
return dir;
|
|
@@ -4460,7 +4934,7 @@ async function findSessionDir(sessionId, deps = defaultDeps3) {
|
|
|
4460
4934
|
}
|
|
4461
4935
|
return;
|
|
4462
4936
|
}
|
|
4463
|
-
async function readWorkspace(dir, deps =
|
|
4937
|
+
async function readWorkspace(dir, deps = defaultDeps4) {
|
|
4464
4938
|
const raw = await deps.readFile(path5.join(dir, "workspace.yaml")).catch(() => "");
|
|
4465
4939
|
return parseWorkspaceYaml(raw);
|
|
4466
4940
|
}
|
|
@@ -4568,9 +5042,9 @@ function parseEvents(raw, fallbackSessionId) {
|
|
|
4568
5042
|
function isObject5(v) {
|
|
4569
5043
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4570
5044
|
}
|
|
4571
|
-
var
|
|
5045
|
+
var defaultDeps4, PREVIEW_CHAR_CAP = 200;
|
|
4572
5046
|
var init_history3 = __esm(() => {
|
|
4573
|
-
|
|
5047
|
+
defaultDeps4 = {
|
|
4574
5048
|
copilotDir() {
|
|
4575
5049
|
const override = process.env.COPILOT_HOME?.trim();
|
|
4576
5050
|
if (override)
|
|
@@ -4674,12 +5148,12 @@ function keepAlive(cmd) {
|
|
|
4674
5148
|
const banner = "\\n \u26A0 Engine exited (code %s). Check Settings \u2192 Engines, fix the launch command, then press R to relaunch.\\n\\n";
|
|
4675
5149
|
return `${cmd}; __rc=$?; [ "$__rc" -ne 0 ] && printf '${banner}' "$__rc"; exec "\${SHELL:-/bin/sh}"`;
|
|
4676
5150
|
}
|
|
4677
|
-
function
|
|
5151
|
+
function shQuote2(s) {
|
|
4678
5152
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
4679
5153
|
}
|
|
4680
5154
|
function homeWelcomeCommand() {
|
|
4681
5155
|
const msg = "\\n No task selected\\n\\n Press N to create a task, or pick one on the left.\\n\\n";
|
|
4682
|
-
return `clear; printf ${
|
|
5156
|
+
return `clear; printf ${shQuote2(msg)}; exec "\${SHELL:-/bin/sh}"`;
|
|
4683
5157
|
}
|
|
4684
5158
|
function engineLaunchLine(engineCmd, init) {
|
|
4685
5159
|
const tail = keepAlive(engineCmd);
|
|
@@ -4689,8 +5163,8 @@ function engineLaunchLine(engineCmd, init) {
|
|
|
4689
5163
|
const group = ["{", script, "}"].join(`
|
|
4690
5164
|
`);
|
|
4691
5165
|
if (init?.markerPath) {
|
|
4692
|
-
const marker =
|
|
4693
|
-
const markerDir =
|
|
5166
|
+
const marker = shQuote2(init.markerPath);
|
|
5167
|
+
const markerDir = shQuote2(markerDirOf(init.markerPath));
|
|
4694
5168
|
return [
|
|
4695
5169
|
`if [ ! -f ${marker} ]; then`,
|
|
4696
5170
|
group,
|
|
@@ -5376,17 +5850,17 @@ var exports_repo_init = {};
|
|
|
5376
5850
|
__export(exports_repo_init, {
|
|
5377
5851
|
resolveRepoInit: () => resolveRepoInit
|
|
5378
5852
|
});
|
|
5379
|
-
import { existsSync, readFileSync as
|
|
5853
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
5380
5854
|
import { join as join4 } from "path";
|
|
5381
5855
|
function repoFileScript(worktreePath) {
|
|
5382
|
-
return
|
|
5856
|
+
return existsSync2(join4(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
|
|
5383
5857
|
}
|
|
5384
5858
|
function repoFilePrompt(worktreePath) {
|
|
5385
5859
|
const p = join4(worktreePath, INIT_PROMPT_REL);
|
|
5386
|
-
if (!
|
|
5860
|
+
if (!existsSync2(p))
|
|
5387
5861
|
return;
|
|
5388
5862
|
try {
|
|
5389
|
-
const text =
|
|
5863
|
+
const text = readFileSync3(p, "utf8");
|
|
5390
5864
|
return text.trim().length > 0 ? text : undefined;
|
|
5391
5865
|
} catch {
|
|
5392
5866
|
return;
|
|
@@ -5473,6 +5947,18 @@ __export(exports_tmux, {
|
|
|
5473
5947
|
CHAT_TAB_CLOSE_BINDING: () => CHAT_TAB_CLOSE_BINDING,
|
|
5474
5948
|
CHAT_TAB_CHOOSE_ENGINE_BINDINGS: () => CHAT_TAB_CHOOSE_ENGINE_BINDINGS
|
|
5475
5949
|
});
|
|
5950
|
+
function localSpawnCwd(cwd) {
|
|
5951
|
+
return execHostForWorktreePath(cwd).isRemote ? homeDir() : cwd;
|
|
5952
|
+
}
|
|
5953
|
+
function wrapEngineLaunch(engineCmd, remoteKey, remoteCwd) {
|
|
5954
|
+
if (!remoteKey || !isRemoteRepoKey(remoteKey))
|
|
5955
|
+
return engineCmd;
|
|
5956
|
+
const host = execHostForRepo(remoteKey);
|
|
5957
|
+
if (!host.isRemote)
|
|
5958
|
+
return engineCmd;
|
|
5959
|
+
host.ensureReady();
|
|
5960
|
+
return host.wrapCommand(engineCmd, { tty: true, cwd: remoteCwd });
|
|
5961
|
+
}
|
|
5476
5962
|
function tmuxInitialSizeArgs(stdout = process.stdout, env = process.env) {
|
|
5477
5963
|
const columns = positiveInt(stdout.columns) ?? positiveInt(env.COLUMNS);
|
|
5478
5964
|
const rows = positiveInt(stdout.rows) ?? positiveInt(env.LINES);
|
|
@@ -5508,7 +5994,7 @@ async function ensureSessionImpl(opts) {
|
|
|
5508
5994
|
return true;
|
|
5509
5995
|
}
|
|
5510
5996
|
if (worktreeOk && !vendorOk && opts.command.length > 0) {
|
|
5511
|
-
if (await relaunchEngineInAllWindows(opts.name, opts.cwd, opts.command)) {
|
|
5997
|
+
if (await relaunchEngineInAllWindows(opts.name, opts.cwd, opts.command, opts.remoteKey)) {
|
|
5512
5998
|
if (opts.vendor)
|
|
5513
5999
|
await setSessionOption(opts.name, "@kobe_vendor", opts.vendor);
|
|
5514
6000
|
await healTaskPaneWidths(opts.name);
|
|
@@ -5525,20 +6011,22 @@ async function ensureSessionImpl(opts) {
|
|
|
5525
6011
|
}
|
|
5526
6012
|
const inv = kobeCliInvocation();
|
|
5527
6013
|
const launch = withClaudeSessionId(opts.command, opts.vendor);
|
|
6014
|
+
const engineCmd = wrapEngineLaunch(shellQuoteArgv(launch.argv), opts.remoteKey, opts.cwd);
|
|
6015
|
+
const remote = Boolean(opts.remoteKey && isRemoteRepoKey(opts.remoteKey));
|
|
5528
6016
|
const r0 = await runTmuxCapturing([
|
|
5529
6017
|
"new-session",
|
|
5530
6018
|
"-d",
|
|
5531
6019
|
"-s",
|
|
5532
6020
|
opts.name,
|
|
5533
6021
|
"-c",
|
|
5534
|
-
opts.cwd,
|
|
6022
|
+
localSpawnCwd(opts.cwd),
|
|
5535
6023
|
...tmuxInitialSizeArgs(),
|
|
5536
6024
|
"-P",
|
|
5537
6025
|
"-F",
|
|
5538
6026
|
"#{pane_id}",
|
|
5539
|
-
engineLaunchLine(
|
|
5540
|
-
initScript: opts.initScript,
|
|
5541
|
-
markerPath: opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
|
|
6027
|
+
engineLaunchLine(engineCmd, {
|
|
6028
|
+
initScript: remote ? undefined : opts.initScript,
|
|
6029
|
+
markerPath: !remote && opts.initScript ? worktreeInitMarkerPath(opts.cwd) : undefined
|
|
5542
6030
|
})
|
|
5543
6031
|
]);
|
|
5544
6032
|
const pane0 = r0.stdout.trim();
|
|
@@ -5551,7 +6039,8 @@ async function ensureSessionImpl(opts) {
|
|
|
5551
6039
|
await runTmuxSequence([
|
|
5552
6040
|
...opts.taskId ? [["set-option", "-t", opts.name, "@kobe_task", opts.taskId]] : [],
|
|
5553
6041
|
["set-option", "-t", opts.name, "@kobe_worktree", opts.cwd],
|
|
5554
|
-
...opts.vendor ? [["set-option", "-t", opts.name, "@kobe_vendor", opts.vendor]] : []
|
|
6042
|
+
...opts.vendor ? [["set-option", "-t", opts.name, "@kobe_vendor", opts.vendor]] : [],
|
|
6043
|
+
...remote ? [["set-option", "-t", opts.name, REMOTE_KEY_OPTION, opts.remoteKey]] : []
|
|
5555
6044
|
]);
|
|
5556
6045
|
await buildPanesAround(pane0, {
|
|
5557
6046
|
cwd: opts.cwd,
|
|
@@ -5595,7 +6084,7 @@ async function ensureSessionImpl(opts) {
|
|
|
5595
6084
|
}
|
|
5596
6085
|
return true;
|
|
5597
6086
|
}
|
|
5598
|
-
async function relaunchEngineInAllWindows(session, cwd, command) {
|
|
6087
|
+
async function relaunchEngineInAllWindows(session, cwd, command, remoteKey) {
|
|
5599
6088
|
const { code, stdout } = await runTmuxCapturing([
|
|
5600
6089
|
"list-panes",
|
|
5601
6090
|
"-s",
|
|
@@ -5610,9 +6099,9 @@ async function relaunchEngineInAllWindows(session, cwd, command) {
|
|
|
5610
6099
|
`).map((line) => line.split("\t")).filter(([, role]) => role?.trim() === "claude").map(([id]) => id?.trim()).filter((id) => !!id);
|
|
5611
6100
|
if (enginePanes.length === 0)
|
|
5612
6101
|
return false;
|
|
5613
|
-
const cmd = keepAlive(shellQuoteArgv(command));
|
|
6102
|
+
const cmd = keepAlive(wrapEngineLaunch(shellQuoteArgv(command), remoteKey, cwd));
|
|
5614
6103
|
for (const pane of enginePanes) {
|
|
5615
|
-
await runTmux(["respawn-pane", "-k", "-c", cwd, "-t", pane, cmd]);
|
|
6104
|
+
await runTmux(["respawn-pane", "-k", "-c", localSpawnCwd(cwd), "-t", pane, cmd]);
|
|
5616
6105
|
}
|
|
5617
6106
|
return true;
|
|
5618
6107
|
}
|
|
@@ -5676,7 +6165,7 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
|
|
|
5676
6165
|
"-t",
|
|
5677
6166
|
tasksPane.paneId,
|
|
5678
6167
|
"-c",
|
|
5679
|
-
cwd,
|
|
6168
|
+
localSpawnCwd(cwd),
|
|
5680
6169
|
keepAlive(envPrefix + tasksPaneCommand(inv, { initialTaskId: taskId }))
|
|
5681
6170
|
], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
|
|
5682
6171
|
}
|
|
@@ -5687,7 +6176,7 @@ async function healKobePaneVersions(session, cwd, taskId, vendor) {
|
|
|
5687
6176
|
"-t",
|
|
5688
6177
|
opsPane.paneId,
|
|
5689
6178
|
"-c",
|
|
5690
|
-
cwd,
|
|
6179
|
+
localSpawnCwd(cwd),
|
|
5691
6180
|
keepAlive(envPrefix + opsPaneCommand({
|
|
5692
6181
|
cwd,
|
|
5693
6182
|
taskId,
|
|
@@ -5736,7 +6225,7 @@ async function refreshKobeWorkspacePanes(session) {
|
|
|
5736
6225
|
"-t",
|
|
5737
6226
|
tasksPane.paneId,
|
|
5738
6227
|
"-c",
|
|
5739
|
-
cwd,
|
|
6228
|
+
localSpawnCwd(cwd),
|
|
5740
6229
|
keepAlive(envPrefix + tasksPaneCommand(inv, { initialTaskId: taskId }))
|
|
5741
6230
|
], ["set-option", "-p", "-t", tasksPane.paneId, "@kobe_role", "tasks"], ["set-option", "-p", "-t", tasksPane.paneId, PANE_VERSION_OPTION, CURRENT_VERSION]);
|
|
5742
6231
|
}
|
|
@@ -5747,7 +6236,7 @@ async function refreshKobeWorkspacePanes(session) {
|
|
|
5747
6236
|
"-t",
|
|
5748
6237
|
opsPane.paneId,
|
|
5749
6238
|
"-c",
|
|
5750
|
-
cwd,
|
|
6239
|
+
localSpawnCwd(cwd),
|
|
5751
6240
|
keepAlive(envPrefix + opsPaneCommand({
|
|
5752
6241
|
cwd,
|
|
5753
6242
|
taskId,
|
|
@@ -5791,7 +6280,7 @@ async function buildPanesAround(claudePane, args) {
|
|
|
5791
6280
|
"-l",
|
|
5792
6281
|
`${TASKS_PANE_WIDTH}`,
|
|
5793
6282
|
"-c",
|
|
5794
|
-
args.cwd,
|
|
6283
|
+
localSpawnCwd(args.cwd),
|
|
5795
6284
|
"-P",
|
|
5796
6285
|
"-F",
|
|
5797
6286
|
"tasks=#{pane_id}",
|
|
@@ -5805,13 +6294,13 @@ async function buildPanesAround(claudePane, args) {
|
|
|
5805
6294
|
"-l",
|
|
5806
6295
|
`${100 - CLAUDE_PANE_PERCENT}%`,
|
|
5807
6296
|
"-c",
|
|
5808
|
-
args.cwd,
|
|
6297
|
+
localSpawnCwd(args.cwd),
|
|
5809
6298
|
"-P",
|
|
5810
6299
|
"-F",
|
|
5811
6300
|
"ops=#{pane_id}",
|
|
5812
6301
|
opsCmd
|
|
5813
6302
|
],
|
|
5814
|
-
["split-window", "-v", "-l", `${100 - OPS_PANE_PERCENT}%`, "-c", args.cwd],
|
|
6303
|
+
["split-window", "-v", "-l", `${100 - OPS_PANE_PERCENT}%`, "-c", localSpawnCwd(args.cwd)],
|
|
5815
6304
|
["select-pane", "-t", claudePane]
|
|
5816
6305
|
]);
|
|
5817
6306
|
const ids = Object.fromEntries(stdout.split(`
|
|
@@ -5826,9 +6315,15 @@ async function buildPanesAround(claudePane, args) {
|
|
|
5826
6315
|
async function newChatTab(session, vendorOverride) {
|
|
5827
6316
|
if (!await sessionExists(session))
|
|
5828
6317
|
return;
|
|
5829
|
-
const sessionOptions = await getSessionOptions(session, [
|
|
6318
|
+
const sessionOptions = await getSessionOptions(session, [
|
|
6319
|
+
"@kobe_worktree",
|
|
6320
|
+
"@kobe_task",
|
|
6321
|
+
"@kobe_vendor",
|
|
6322
|
+
REMOTE_KEY_OPTION
|
|
6323
|
+
]);
|
|
5830
6324
|
const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
|
|
5831
6325
|
const taskId = sessionOptions["@kobe_task"] || undefined;
|
|
6326
|
+
const remoteKey = sessionOptions[REMOTE_KEY_OPTION] || undefined;
|
|
5832
6327
|
const vendor = vendorOverride ?? sessionOptions["@kobe_vendor"];
|
|
5833
6328
|
if (vendorOverride)
|
|
5834
6329
|
await rememberSessionVendor(session, taskId, vendorOverride);
|
|
@@ -5840,11 +6335,11 @@ async function newChatTab(session, vendorOverride) {
|
|
|
5840
6335
|
"-t",
|
|
5841
6336
|
`=${session}`,
|
|
5842
6337
|
"-c",
|
|
5843
|
-
cwd,
|
|
6338
|
+
localSpawnCwd(cwd),
|
|
5844
6339
|
"-P",
|
|
5845
6340
|
"-F",
|
|
5846
6341
|
"#{pane_id}",
|
|
5847
|
-
keepAlive(shellQuoteArgv(launch.argv))
|
|
6342
|
+
keepAlive(wrapEngineLaunch(shellQuoteArgv(launch.argv), remoteKey, cwd))
|
|
5848
6343
|
]);
|
|
5849
6344
|
const claudePane = r.stdout.trim();
|
|
5850
6345
|
if (!claudePane)
|
|
@@ -5861,7 +6356,7 @@ async function openSettingsTab(session) {
|
|
|
5861
6356
|
const inv = kobeCliInvocation();
|
|
5862
6357
|
const envPrefix = inheritedEnvPrefix();
|
|
5863
6358
|
const command = `${envPrefix}${inv.map(shellQuote).join(" ")} settings`;
|
|
5864
|
-
await newWindow(session, { cwd, command, name: "settings" });
|
|
6359
|
+
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "settings" });
|
|
5865
6360
|
}
|
|
5866
6361
|
async function openNewTaskTab(session, defaultRepo) {
|
|
5867
6362
|
if (!await sessionExists(session))
|
|
@@ -5872,7 +6367,7 @@ async function openNewTaskTab(session, defaultRepo) {
|
|
|
5872
6367
|
const envPrefix = inheritedEnvPrefix();
|
|
5873
6368
|
const repoArg = defaultRepo ? ` --repo ${shellQuote(defaultRepo)}` : "";
|
|
5874
6369
|
const command = `${envPrefix}${inv.map(shellQuote).join(" ")} new-task${repoArg}`;
|
|
5875
|
-
await newWindow(session, { cwd, command, name: "new task" });
|
|
6370
|
+
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "new task" });
|
|
5876
6371
|
}
|
|
5877
6372
|
async function openUpdateTab(session) {
|
|
5878
6373
|
if (!await sessionExists(session))
|
|
@@ -5882,7 +6377,7 @@ async function openUpdateTab(session) {
|
|
|
5882
6377
|
const inv = kobeCliInvocation();
|
|
5883
6378
|
const envPrefix = inheritedEnvPrefix();
|
|
5884
6379
|
const command = `${envPrefix}${updatePageCommand({ cliInvocation: inv })}`;
|
|
5885
|
-
await newWindow(session, { cwd, command, name: "update" });
|
|
6380
|
+
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "update" });
|
|
5886
6381
|
}
|
|
5887
6382
|
async function rememberSessionVendor(session, taskId, vendor) {
|
|
5888
6383
|
await setSessionOption(session, "@kobe_vendor", vendor);
|
|
@@ -5917,13 +6412,15 @@ async function quickCreate(session) {
|
|
|
5917
6412
|
const inv = kobeCliInvocation();
|
|
5918
6413
|
const envPrefix = inheritedEnvPrefix();
|
|
5919
6414
|
const command = `${envPrefix}${inv.map(shellQuote).join(" ")} quick-task --session ${shellQuote(session)}`;
|
|
5920
|
-
await newWindow(session, { cwd, command, name: "quick task" });
|
|
6415
|
+
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "quick task" });
|
|
5921
6416
|
}
|
|
5922
|
-
var CHAT_TAB_SWITCH_BINDINGS, CHAT_TAB_CLOSE_BINDING, CHAT_TAB_RENAME_BINDING, CHAT_TAB_ENGINE_PROMPT, CHAT_TAB_CHOOSE_ENGINE_BINDINGS, KOBE_STATUS_RIGHT = "#[fg=brightblack]^h tasks ^q detach ^t tab ", CHAT_TAB_STATE_OPTION = "@kobe_tab_state", PANE_VERSION_OPTION = "@kobe_pane_version", CHAT_TAB_STATUS_FORMAT = "#{?#{==:#{@kobe_tab_state},running},\u25CF,#{?#{==:#{@kobe_tab_state},done},\u2713,#{?#{==:#{@kobe_tab_state},error},!,#{?#{==:#{@kobe_tab_state},unknown},?,\u25CB}}}} #I:#W", CHAT_TAB_STATUS_CURRENT_FORMAT, ensureSessionLocks;
|
|
6417
|
+
var CHAT_TAB_SWITCH_BINDINGS, CHAT_TAB_CLOSE_BINDING, CHAT_TAB_RENAME_BINDING, CHAT_TAB_ENGINE_PROMPT, REMOTE_KEY_OPTION = "@kobe_remote", CHAT_TAB_CHOOSE_ENGINE_BINDINGS, KOBE_STATUS_RIGHT = "#[fg=brightblack]^h tasks ^q detach ^t tab ", CHAT_TAB_STATE_OPTION = "@kobe_tab_state", PANE_VERSION_OPTION = "@kobe_pane_version", CHAT_TAB_STATUS_FORMAT = "#{?#{==:#{@kobe_tab_state},running},\u25CF,#{?#{==:#{@kobe_tab_state},done},\u2713,#{?#{==:#{@kobe_tab_state},error},!,#{?#{==:#{@kobe_tab_state},unknown},?,\u25CB}}}} #I:#W", CHAT_TAB_STATUS_CURRENT_FORMAT, ensureSessionLocks;
|
|
5923
6418
|
var init_tmux = __esm(() => {
|
|
5924
6419
|
init_invocation();
|
|
5925
6420
|
init_interactive_command();
|
|
5926
6421
|
init_env();
|
|
6422
|
+
init_resolve();
|
|
6423
|
+
init_repos();
|
|
5927
6424
|
init_client2();
|
|
5928
6425
|
init_prompt_delivery();
|
|
5929
6426
|
init_vendor();
|
|
@@ -6191,7 +6688,7 @@ var init_notes = __esm(() => {
|
|
|
6191
6688
|
});
|
|
6192
6689
|
|
|
6193
6690
|
// ../kobe-daemon/src/daemon/web.ts
|
|
6194
|
-
import { existsSync as
|
|
6691
|
+
import { existsSync as existsSync3 } from "fs";
|
|
6195
6692
|
import { join as join6, normalize as normalize2 } from "path";
|
|
6196
6693
|
function sseResponse(bus, snapshot) {
|
|
6197
6694
|
let unsubscribe = null;
|
|
@@ -6321,7 +6818,7 @@ async function staticResponse(pathname, staticDir) {
|
|
|
6321
6818
|
const resolved = normalize2(join6(staticDir, rel));
|
|
6322
6819
|
if (!resolved.startsWith(staticDir))
|
|
6323
6820
|
return new Response("forbidden", { status: 403 });
|
|
6324
|
-
const file = Bun.file(
|
|
6821
|
+
const file = Bun.file(existsSync3(resolved) ? resolved : join6(staticDir, "index.html"));
|
|
6325
6822
|
if (!await file.exists()) {
|
|
6326
6823
|
return new Response("kobe web assets not built \u2014 run `bun --filter kobe-web build`", { status: 503 });
|
|
6327
6824
|
}
|
|
@@ -7018,14 +7515,14 @@ __export(exports_daemon_process, {
|
|
|
7018
7515
|
connectIfRunning: () => connectIfRunning
|
|
7019
7516
|
});
|
|
7020
7517
|
import { spawn } from "child_process";
|
|
7021
|
-
import { closeSync, existsSync as
|
|
7518
|
+
import { closeSync, existsSync as existsSync4, mkdirSync as mkdirSync3, openSync } from "fs";
|
|
7022
7519
|
import { dirname as dirname5, resolve as resolve2 } from "path";
|
|
7023
7520
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7024
7521
|
function spawnDetachedDaemon(command, args, env, logPath) {
|
|
7025
7522
|
let stdio = "ignore";
|
|
7026
7523
|
let logFd;
|
|
7027
7524
|
try {
|
|
7028
|
-
|
|
7525
|
+
mkdirSync3(dirname5(logPath), { recursive: true });
|
|
7029
7526
|
logFd = openSync(logPath, "a");
|
|
7030
7527
|
stdio = ["ignore", logFd, logFd];
|
|
7031
7528
|
} catch {
|
|
@@ -7069,14 +7566,14 @@ async function connectIfRunning() {
|
|
|
7069
7566
|
return client;
|
|
7070
7567
|
}
|
|
7071
7568
|
async function testDaemonResponds(socketPath, timeoutMs = DAEMON_HELLO_TIMEOUT_MS) {
|
|
7072
|
-
const
|
|
7569
|
+
const probe2 = new KobeDaemonClient(socketPath);
|
|
7073
7570
|
try {
|
|
7074
|
-
await
|
|
7571
|
+
await probe2.connect();
|
|
7075
7572
|
} catch {
|
|
7076
|
-
|
|
7573
|
+
probe2.close();
|
|
7077
7574
|
return false;
|
|
7078
7575
|
}
|
|
7079
|
-
const replied =
|
|
7576
|
+
const replied = probe2.request("hello", { protocolVersion: DAEMON_PROTOCOL_VERSION }).then(() => true).catch(() => true);
|
|
7080
7577
|
let timer;
|
|
7081
7578
|
const timedOut = new Promise((resolve3) => {
|
|
7082
7579
|
timer = setTimeout(() => resolve3(false), timeoutMs);
|
|
@@ -7084,7 +7581,7 @@ async function testDaemonResponds(socketPath, timeoutMs = DAEMON_HELLO_TIMEOUT_M
|
|
|
7084
7581
|
const alive = await Promise.race([replied, timedOut]);
|
|
7085
7582
|
if (timer)
|
|
7086
7583
|
clearTimeout(timer);
|
|
7087
|
-
|
|
7584
|
+
probe2.close();
|
|
7088
7585
|
return alive;
|
|
7089
7586
|
}
|
|
7090
7587
|
function resolveKobeSpawn(subcommand) {
|
|
@@ -7098,7 +7595,7 @@ function resolveKobeSpawn(subcommand) {
|
|
|
7098
7595
|
resolve2(dir, "../../../kobe/src/cli/index.ts"),
|
|
7099
7596
|
resolve2(dir, "../cli/index.js")
|
|
7100
7597
|
];
|
|
7101
|
-
const entry = candidates.find((candidate) =>
|
|
7598
|
+
const entry = candidates.find((candidate) => existsSync4(candidate));
|
|
7102
7599
|
if (entry)
|
|
7103
7600
|
return [process.execPath, entry, ...subcommand];
|
|
7104
7601
|
throw new Error(`kobe: could not locate kobe entry near ${dir}; checked ${candidates.join(", ")}`);
|
|
@@ -7117,7 +7614,7 @@ var exports_repo_cmd = {};
|
|
|
7117
7614
|
__export(exports_repo_cmd, {
|
|
7118
7615
|
runRepoSubcommand: () => runRepoSubcommand
|
|
7119
7616
|
});
|
|
7120
|
-
import { readFileSync as
|
|
7617
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
7121
7618
|
import { resolve as resolve3 } from "path";
|
|
7122
7619
|
function usageError(message) {
|
|
7123
7620
|
process.stderr.write(`kobe repo: ${message}
|
|
@@ -7128,7 +7625,7 @@ ${REPO_USAGE}
|
|
|
7128
7625
|
}
|
|
7129
7626
|
function readArgFile(path7) {
|
|
7130
7627
|
try {
|
|
7131
|
-
return
|
|
7628
|
+
return readFileSync4(resolve3(process.cwd(), path7), "utf8");
|
|
7132
7629
|
} catch (err) {
|
|
7133
7630
|
usageError(`cannot read ${path7}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7134
7631
|
}
|
|
@@ -7190,14 +7687,14 @@ async function runRepoSubcommand(args) {
|
|
|
7190
7687
|
return;
|
|
7191
7688
|
}
|
|
7192
7689
|
const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
7193
|
-
const { existsSync:
|
|
7690
|
+
const { existsSync: existsSync5 } = await import("fs");
|
|
7194
7691
|
const { join: join7 } = await import("path");
|
|
7195
7692
|
if (verb === "show") {
|
|
7196
7693
|
const [pathArg] = rest.filter((a) => !a.startsWith("-"));
|
|
7197
7694
|
const repo = resolveRepoRoot2(resolve3(process.cwd(), pathArg ?? "."));
|
|
7198
7695
|
const override = getRepoInitOverride2(repo);
|
|
7199
|
-
const hasFileScript =
|
|
7200
|
-
const hasFilePrompt =
|
|
7696
|
+
const hasFileScript = existsSync5(join7(repo, ".kobe", "init.sh"));
|
|
7697
|
+
const hasFilePrompt = existsSync5(join7(repo, ".kobe", "init-prompt.md"));
|
|
7201
7698
|
console.log(`repo: ${repo}`);
|
|
7202
7699
|
console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
|
|
7203
7700
|
console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
|
|
@@ -7264,7 +7761,7 @@ var init_repo_cmd = __esm(() => {
|
|
|
7264
7761
|
});
|
|
7265
7762
|
|
|
7266
7763
|
// src/lib/feedback.ts
|
|
7267
|
-
import { spawnSync as
|
|
7764
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
7268
7765
|
function parseRepoSlug(slug) {
|
|
7269
7766
|
const [owner, name] = slug.split("/");
|
|
7270
7767
|
if (!owner || !name)
|
|
@@ -7319,7 +7816,7 @@ function submitFeedback(input, deps = {}) {
|
|
|
7319
7816
|
throw new Error("package repository is not a GitHub repository");
|
|
7320
7817
|
const { owner, name } = parseRepoSlug(slug);
|
|
7321
7818
|
const categorySlug = input.categorySlug?.trim() || DEFAULT_FEEDBACK_CATEGORY_SLUG;
|
|
7322
|
-
const io = { spawn: deps.spawn ??
|
|
7819
|
+
const io = { spawn: deps.spawn ?? spawnSync4 };
|
|
7323
7820
|
const categoryData = runGhGraphql(DISCUSSION_CATEGORY_QUERY, { owner, name }, io);
|
|
7324
7821
|
const repository = categoryData.repository;
|
|
7325
7822
|
const repositoryId = repository?.id;
|
|
@@ -7378,12 +7875,12 @@ __export(exports_worktree_changes, {
|
|
|
7378
7875
|
readWorktreeChanges: () => readWorktreeChanges,
|
|
7379
7876
|
parsePorcelain: () => parsePorcelain2
|
|
7380
7877
|
});
|
|
7381
|
-
import { spawnSync as
|
|
7878
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
7382
7879
|
function readWorktreeChanges(worktreePath) {
|
|
7383
7880
|
if (!worktreePath)
|
|
7384
7881
|
return ZERO;
|
|
7385
7882
|
try {
|
|
7386
|
-
const out =
|
|
7883
|
+
const out = spawnSync5("git", ["status", "--porcelain=v1"], {
|
|
7387
7884
|
cwd: worktreePath,
|
|
7388
7885
|
encoding: "utf8",
|
|
7389
7886
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -7713,7 +8210,7 @@ function emit(value, pretty) {
|
|
|
7713
8210
|
process.stdout.write(`${text}
|
|
7714
8211
|
`);
|
|
7715
8212
|
}
|
|
7716
|
-
function
|
|
8213
|
+
function fail2(message, code, exitCode = 1) {
|
|
7717
8214
|
process.stderr.write(`${JSON.stringify({ error: { message, code } })}
|
|
7718
8215
|
`);
|
|
7719
8216
|
process.exit(exitCode);
|
|
@@ -7731,6 +8228,7 @@ async function deliverPrompt(client, target, prompt) {
|
|
|
7731
8228
|
if (!existed) {
|
|
7732
8229
|
const { ensureSession: ensureSession2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
7733
8230
|
const { resolveRepoInit: resolveRepoInit2 } = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
|
|
8231
|
+
const { isRemoteRepoKey: isRemoteRepoKey2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
7734
8232
|
const init = resolveRepoInit2(target.repo ?? "", worktree);
|
|
7735
8233
|
const ok = await ensureSession2({
|
|
7736
8234
|
name: session,
|
|
@@ -7738,6 +8236,7 @@ async function deliverPrompt(client, target, prompt) {
|
|
|
7738
8236
|
command: interactiveEngineCommand(target.vendor),
|
|
7739
8237
|
taskId: target.id,
|
|
7740
8238
|
vendor: target.vendor,
|
|
8239
|
+
remoteKey: target.repo && isRemoteRepoKey2(target.repo) ? target.repo : undefined,
|
|
7741
8240
|
initScript: init.initScript
|
|
7742
8241
|
});
|
|
7743
8242
|
if (!ok)
|
|
@@ -7949,14 +8448,14 @@ async function runApiSubcommand(argv) {
|
|
|
7949
8448
|
const [verbName, ...rest] = argv;
|
|
7950
8449
|
if (!verbName || verbName === "--help" || verbName === "-h" || verbName === "help") {
|
|
7951
8450
|
if (!verbName)
|
|
7952
|
-
|
|
8451
|
+
fail2(apiUsage(), "MISSING_VERB", 2);
|
|
7953
8452
|
process.stdout.write(`${apiUsage()}
|
|
7954
8453
|
`);
|
|
7955
8454
|
return;
|
|
7956
8455
|
}
|
|
7957
8456
|
const verb = findVerb(verbName);
|
|
7958
8457
|
if (!verb)
|
|
7959
|
-
|
|
8458
|
+
fail2(`unknown verb: ${verbName}
|
|
7960
8459
|
${apiUsage()}`, "BAD_VERB", 2);
|
|
7961
8460
|
const booleanFlags = new Set(verb.flags.filter((f) => f.type === "bool").map((f) => f.name));
|
|
7962
8461
|
let parsed;
|
|
@@ -7964,8 +8463,8 @@ ${apiUsage()}`, "BAD_VERB", 2);
|
|
|
7964
8463
|
parsed = parseFlags(rest, booleanFlags);
|
|
7965
8464
|
} catch (err) {
|
|
7966
8465
|
if (err instanceof ApiError)
|
|
7967
|
-
|
|
7968
|
-
|
|
8466
|
+
fail2(err.message, err.code, 2);
|
|
8467
|
+
fail2(err instanceof Error ? err.message : String(err), "BAD_FLAG", 2);
|
|
7969
8468
|
}
|
|
7970
8469
|
if (parsed.help) {
|
|
7971
8470
|
process.stdout.write(`${verbHelp(verb)}
|
|
@@ -7976,15 +8475,15 @@ ${apiUsage()}`, "BAD_VERB", 2);
|
|
|
7976
8475
|
validateAgainstSpec(verb, parsed.flags);
|
|
7977
8476
|
} catch (err) {
|
|
7978
8477
|
if (err instanceof ApiError)
|
|
7979
|
-
|
|
7980
|
-
|
|
8478
|
+
fail2(err.message, err.code, 2);
|
|
8479
|
+
fail2(err instanceof Error ? err.message : String(err), "BAD_FLAG", 2);
|
|
7981
8480
|
}
|
|
7982
8481
|
let client = null;
|
|
7983
8482
|
if (!verb.offline) {
|
|
7984
8483
|
try {
|
|
7985
8484
|
client = await connectOrStartDaemon();
|
|
7986
8485
|
} catch (err) {
|
|
7987
|
-
|
|
8486
|
+
fail2(`could not reach or start the kobe daemon: ${err instanceof Error ? err.message : String(err)}`, "BAD_DAEMON", 2);
|
|
7988
8487
|
}
|
|
7989
8488
|
}
|
|
7990
8489
|
try {
|
|
@@ -7992,8 +8491,8 @@ ${apiUsage()}`, "BAD_VERB", 2);
|
|
|
7992
8491
|
emit(result, parsed.pretty);
|
|
7993
8492
|
} catch (err) {
|
|
7994
8493
|
if (err instanceof ApiError)
|
|
7995
|
-
|
|
7996
|
-
|
|
8494
|
+
fail2(err.message, err.code, 1);
|
|
8495
|
+
fail2(err instanceof Error ? err.message : String(err), "RPC_ERROR", 1);
|
|
7997
8496
|
} finally {
|
|
7998
8497
|
client?.close();
|
|
7999
8498
|
}
|
|
@@ -8277,7 +8776,7 @@ __export(exports_update, {
|
|
|
8277
8776
|
runUpdateSubcommand: () => runUpdateSubcommand,
|
|
8278
8777
|
parseUpdateArgs: () => parseUpdateArgs
|
|
8279
8778
|
});
|
|
8280
|
-
import { spawnSync as
|
|
8779
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
8281
8780
|
function updatePlan() {
|
|
8282
8781
|
return {
|
|
8283
8782
|
command: "sh",
|
|
@@ -8327,7 +8826,7 @@ function printUsage(out) {
|
|
|
8327
8826
|
}
|
|
8328
8827
|
async function runUpdateSubcommand(args, deps) {
|
|
8329
8828
|
const io = {
|
|
8330
|
-
spawn: deps?.spawn ??
|
|
8829
|
+
spawn: deps?.spawn ?? spawnSync6,
|
|
8331
8830
|
stdout: deps?.stdout ?? process.stdout,
|
|
8332
8831
|
stderr: deps?.stderr ?? process.stderr,
|
|
8333
8832
|
exit: deps?.exit ?? ((code) => process.exit(code))
|
|
@@ -8407,7 +8906,7 @@ function validateTheme(value) {
|
|
|
8407
8906
|
var init_schema = () => {};
|
|
8408
8907
|
|
|
8409
8908
|
// src/tui/context/theme/loader.ts
|
|
8410
|
-
import { readFileSync as
|
|
8909
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
|
|
8411
8910
|
import { join as join7 } from "path";
|
|
8412
8911
|
function userThemesDir() {
|
|
8413
8912
|
return join7(kobeStateDir(), "themes");
|
|
@@ -8416,7 +8915,7 @@ function loadUserThemes() {
|
|
|
8416
8915
|
const dir = userThemesDir();
|
|
8417
8916
|
let entries;
|
|
8418
8917
|
try {
|
|
8419
|
-
entries =
|
|
8918
|
+
entries = readdirSync2(dir);
|
|
8420
8919
|
} catch {
|
|
8421
8920
|
return [];
|
|
8422
8921
|
}
|
|
@@ -8427,7 +8926,7 @@ function loadUserThemes() {
|
|
|
8427
8926
|
const path7 = join7(dir, file);
|
|
8428
8927
|
let parsed;
|
|
8429
8928
|
try {
|
|
8430
|
-
const text =
|
|
8929
|
+
const text = readFileSync5(path7, "utf8");
|
|
8431
8930
|
parsed = JSON.parse(text);
|
|
8432
8931
|
} catch (err) {
|
|
8433
8932
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -8454,9 +8953,9 @@ var exports_theme = {};
|
|
|
8454
8953
|
__export(exports_theme, {
|
|
8455
8954
|
runThemeSubcommand: () => runThemeSubcommand
|
|
8456
8955
|
});
|
|
8457
|
-
import { existsSync as
|
|
8956
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
8458
8957
|
import { basename as basename3, join as join8, resolve as resolve5 } from "path";
|
|
8459
|
-
function
|
|
8958
|
+
function fail3(message) {
|
|
8460
8959
|
process.stderr.write(`kobe theme: ${message}
|
|
8461
8960
|
`);
|
|
8462
8961
|
process.exit(1);
|
|
@@ -8477,7 +8976,7 @@ function listThemes() {
|
|
|
8477
8976
|
const dir = userThemesDir();
|
|
8478
8977
|
let userFiles = [];
|
|
8479
8978
|
try {
|
|
8480
|
-
userFiles =
|
|
8979
|
+
userFiles = readdirSync3(dir).filter((f) => f.endsWith(".json")).sort();
|
|
8481
8980
|
} catch {}
|
|
8482
8981
|
lines.push("");
|
|
8483
8982
|
lines.push(`user (${dir}):`);
|
|
@@ -8501,10 +9000,10 @@ async function readSource(source) {
|
|
|
8501
9000
|
try {
|
|
8502
9001
|
res = await fetch(source);
|
|
8503
9002
|
} catch (err) {
|
|
8504
|
-
|
|
9003
|
+
fail3(`failed to fetch ${source}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8505
9004
|
}
|
|
8506
9005
|
if (!res.ok) {
|
|
8507
|
-
|
|
9006
|
+
fail3(`failed to fetch ${source}: HTTP ${res.status} ${res.statusText}`);
|
|
8508
9007
|
}
|
|
8509
9008
|
const text2 = await res.text();
|
|
8510
9009
|
const cleanPath = source.split(/[?#]/)[0] ?? source;
|
|
@@ -8515,9 +9014,9 @@ async function readSource(source) {
|
|
|
8515
9014
|
const abs = resolve5(process.cwd(), source);
|
|
8516
9015
|
let text;
|
|
8517
9016
|
try {
|
|
8518
|
-
text =
|
|
9017
|
+
text = readFileSync6(abs, "utf8");
|
|
8519
9018
|
} catch (err) {
|
|
8520
|
-
|
|
9019
|
+
fail3(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8521
9020
|
}
|
|
8522
9021
|
const file = basename3(abs);
|
|
8523
9022
|
const defaultName = file.endsWith(".json") ? file.slice(0, -".json".length) : file;
|
|
@@ -8564,21 +9063,21 @@ async function addTheme(args) {
|
|
|
8564
9063
|
try {
|
|
8565
9064
|
parsed = JSON.parse(text);
|
|
8566
9065
|
} catch (err) {
|
|
8567
|
-
|
|
9066
|
+
fail3(`source is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
8568
9067
|
}
|
|
8569
9068
|
const result = validateTheme(parsed);
|
|
8570
9069
|
if (!result.ok) {
|
|
8571
|
-
|
|
9070
|
+
fail3(`source is not a valid kobe theme: ${result.reason}`);
|
|
8572
9071
|
}
|
|
8573
9072
|
const name = opts.name ?? defaultName;
|
|
8574
9073
|
if (!name || !/^[a-zA-Z0-9._-]+$/.test(name)) {
|
|
8575
|
-
|
|
9074
|
+
fail3(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
|
|
8576
9075
|
}
|
|
8577
9076
|
const dir = userThemesDir();
|
|
8578
|
-
|
|
9077
|
+
mkdirSync4(dir, { recursive: true });
|
|
8579
9078
|
const dest = join8(dir, `${name}.json`);
|
|
8580
|
-
if (
|
|
8581
|
-
|
|
9079
|
+
if (existsSync5(dest) && !opts.force) {
|
|
9080
|
+
fail3(`${dest} already exists (pass --force to overwrite)`);
|
|
8582
9081
|
}
|
|
8583
9082
|
writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
|
|
8584
9083
|
`, "utf8");
|
|
@@ -8592,11 +9091,11 @@ function removeTheme(args) {
|
|
|
8592
9091
|
if (args.length > 1)
|
|
8593
9092
|
failUsage(`unexpected extra arguments after "${name}"`);
|
|
8594
9093
|
if (BUNDLED_NAMES.includes(name)) {
|
|
8595
|
-
|
|
9094
|
+
fail3(`"${name}" is a built-in theme and cannot be removed`);
|
|
8596
9095
|
}
|
|
8597
9096
|
const dest = join8(userThemesDir(), `${name}.json`);
|
|
8598
|
-
if (!
|
|
8599
|
-
|
|
9097
|
+
if (!existsSync5(dest)) {
|
|
9098
|
+
fail3(`no user theme named "${name}" (looked for ${dest})`);
|
|
8600
9099
|
}
|
|
8601
9100
|
unlinkSync(dest);
|
|
8602
9101
|
process.stdout.write(`removed theme "${name}" (${dest})
|
|
@@ -8664,7 +9163,7 @@ __export(exports_feedback_cmd, {
|
|
|
8664
9163
|
runFeedbackSubcommand: () => runFeedbackSubcommand,
|
|
8665
9164
|
parseFeedbackArgs: () => parseFeedbackArgs
|
|
8666
9165
|
});
|
|
8667
|
-
import { readFileSync as
|
|
9166
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
8668
9167
|
function usageError2(message) {
|
|
8669
9168
|
process.stderr.write(`kobe feedback: ${message}
|
|
8670
9169
|
|
|
@@ -8674,8 +9173,8 @@ ${FEEDBACK_USAGE}
|
|
|
8674
9173
|
}
|
|
8675
9174
|
function readBodyFile(path7) {
|
|
8676
9175
|
if (path7 === "-")
|
|
8677
|
-
return
|
|
8678
|
-
return
|
|
9176
|
+
return readFileSync7(0, "utf8");
|
|
9177
|
+
return readFileSync7(path7, "utf8");
|
|
8679
9178
|
}
|
|
8680
9179
|
function parseFeedbackArgs(args) {
|
|
8681
9180
|
const parsed = { help: false };
|
|
@@ -8874,7 +9373,7 @@ var init_daemon_cmd = __esm(() => {
|
|
|
8874
9373
|
});
|
|
8875
9374
|
|
|
8876
9375
|
// src/lib/skill-install.ts
|
|
8877
|
-
import { existsSync as
|
|
9376
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
|
|
8878
9377
|
import { homedir as homedir10 } from "os";
|
|
8879
9378
|
import { join as join9 } from "path";
|
|
8880
9379
|
function npxSkillsArgv(opts = {}) {
|
|
@@ -8893,13 +9392,13 @@ function parseSkillVersion(content) {
|
|
|
8893
9392
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
8894
9393
|
}
|
|
8895
9394
|
function kobeSkillState(opts) {
|
|
8896
|
-
const path7 = kobeSkillPaths(opts).find((p) =>
|
|
9395
|
+
const path7 = kobeSkillPaths(opts).find((p) => existsSync6(p));
|
|
8897
9396
|
if (!path7) {
|
|
8898
9397
|
return { installed: false, installedVersion: null, currentVersion: KOBE_SKILL_VERSION, stale: false };
|
|
8899
9398
|
}
|
|
8900
9399
|
let installedVersion = null;
|
|
8901
9400
|
try {
|
|
8902
|
-
installedVersion = parseSkillVersion(
|
|
9401
|
+
installedVersion = parseSkillVersion(readFileSync8(path7, "utf8"));
|
|
8903
9402
|
} catch {
|
|
8904
9403
|
installedVersion = null;
|
|
8905
9404
|
}
|
|
@@ -8945,10 +9444,10 @@ __export(exports_maintenance, {
|
|
|
8945
9444
|
runReloadSubcommand: () => runReloadSubcommand,
|
|
8946
9445
|
runDoctorSubcommand: () => runDoctorSubcommand
|
|
8947
9446
|
});
|
|
8948
|
-
import { existsSync as
|
|
9447
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, statSync } from "fs";
|
|
8949
9448
|
import { unlink as unlink6 } from "fs/promises";
|
|
8950
9449
|
import { join as join10 } from "path";
|
|
8951
|
-
import { createInterface } from "readline";
|
|
9450
|
+
import { createInterface as createInterface2 } from "readline";
|
|
8952
9451
|
function isProcessAlive2(pid) {
|
|
8953
9452
|
try {
|
|
8954
9453
|
process.kill(pid, 0);
|
|
@@ -8994,7 +9493,7 @@ function describeFile(path7) {
|
|
|
8994
9493
|
}
|
|
8995
9494
|
function taskCount(tasksPath) {
|
|
8996
9495
|
try {
|
|
8997
|
-
const parsed = JSON.parse(
|
|
9496
|
+
const parsed = JSON.parse(readFileSync9(tasksPath, "utf8"));
|
|
8998
9497
|
return Array.isArray(parsed.tasks) ? parsed.tasks.length : null;
|
|
8999
9498
|
} catch {
|
|
9000
9499
|
return null;
|
|
@@ -9002,7 +9501,7 @@ function taskCount(tasksPath) {
|
|
|
9002
9501
|
}
|
|
9003
9502
|
function tailFile(path7, n) {
|
|
9004
9503
|
try {
|
|
9005
|
-
const lines =
|
|
9504
|
+
const lines = readFileSync9(path7, "utf8").split(`
|
|
9006
9505
|
`).filter((l) => l.trim().length > 0);
|
|
9007
9506
|
return lines.slice(-n).join(`
|
|
9008
9507
|
`);
|
|
@@ -9072,7 +9571,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
9072
9571
|
} else {
|
|
9073
9572
|
out.push("daemon: \u2717 not running (no pidfile)");
|
|
9074
9573
|
}
|
|
9075
|
-
if (
|
|
9574
|
+
if (existsSync7(socketPath))
|
|
9076
9575
|
out.push(` orphan socket file present: ${socketPath}`);
|
|
9077
9576
|
const tail = tailFile(logPath, 8);
|
|
9078
9577
|
if (tail) {
|
|
@@ -9109,7 +9608,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
9109
9608
|
`));
|
|
9110
9609
|
}
|
|
9111
9610
|
async function confirmTty(prompt) {
|
|
9112
|
-
const rl =
|
|
9611
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
9113
9612
|
try {
|
|
9114
9613
|
const answer = await new Promise((resolve6) => rl.question(prompt, resolve6));
|
|
9115
9614
|
return /^y(es)?$/i.test(answer.trim());
|
|
@@ -9272,7 +9771,7 @@ var exports_web_cmd = {};
|
|
|
9272
9771
|
__export(exports_web_cmd, {
|
|
9273
9772
|
runWebSubcommand: () => runWebSubcommand
|
|
9274
9773
|
});
|
|
9275
|
-
import { existsSync as
|
|
9774
|
+
import { existsSync as existsSync8 } from "fs";
|
|
9276
9775
|
import { resolve as resolve6 } from "path";
|
|
9277
9776
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9278
9777
|
function resolveStaticDir() {
|
|
@@ -9282,7 +9781,7 @@ function resolveStaticDir() {
|
|
|
9282
9781
|
resolve6(here, "../../web-ui")
|
|
9283
9782
|
];
|
|
9284
9783
|
for (const dir of candidates) {
|
|
9285
|
-
if (
|
|
9784
|
+
if (existsSync8(`${dir}/index.html`))
|
|
9286
9785
|
return dir;
|
|
9287
9786
|
}
|
|
9288
9787
|
return;
|
|
@@ -9294,7 +9793,7 @@ function resolvePtyServer() {
|
|
|
9294
9793
|
resolve6(here, "../../web-ui/pty-server.mjs")
|
|
9295
9794
|
];
|
|
9296
9795
|
for (const file of candidates) {
|
|
9297
|
-
if (
|
|
9796
|
+
if (existsSync8(file))
|
|
9298
9797
|
return file;
|
|
9299
9798
|
}
|
|
9300
9799
|
return;
|
|
@@ -9358,7 +9857,7 @@ async function startPtyServer(opts) {
|
|
|
9358
9857
|
}
|
|
9359
9858
|
async function runWebSubcommand(args) {
|
|
9360
9859
|
if (args.includes("--help") || args.includes("-h")) {
|
|
9361
|
-
process.stdout.write(
|
|
9860
|
+
process.stdout.write(USAGE2);
|
|
9362
9861
|
return;
|
|
9363
9862
|
}
|
|
9364
9863
|
let port = 5173;
|
|
@@ -9426,7 +9925,7 @@ async function runWebSubcommand(args) {
|
|
|
9426
9925
|
process.exit(1);
|
|
9427
9926
|
}
|
|
9428
9927
|
}
|
|
9429
|
-
var
|
|
9928
|
+
var USAGE2 = `Usage: kobe web [options]
|
|
9430
9929
|
|
|
9431
9930
|
Launch the kobe web UI on http://localhost:<port>.
|
|
9432
9931
|
|
|
@@ -11439,11 +11938,11 @@ var init_remote_orchestrator = __esm(() => {
|
|
|
11439
11938
|
});
|
|
11440
11939
|
|
|
11441
11940
|
// src/engine/claude-code-local/binary.ts
|
|
11442
|
-
import { spawnSync as
|
|
11443
|
-
import { existsSync as
|
|
11941
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
11942
|
+
import { existsSync as existsSync9, statSync as statSync2 } from "fs";
|
|
11444
11943
|
import { homedir as homedir12 } from "os";
|
|
11445
11944
|
import path7 from "path";
|
|
11446
|
-
async function findClaudeBinary(deps =
|
|
11945
|
+
async function findClaudeBinary(deps = defaultDeps5) {
|
|
11447
11946
|
const checked = [];
|
|
11448
11947
|
const tryPath = (p) => {
|
|
11449
11948
|
if (!p)
|
|
@@ -11492,7 +11991,7 @@ async function findClaudeBinary(deps = defaultDeps4) {
|
|
|
11492
11991
|
}
|
|
11493
11992
|
throw new ClaudeBinaryNotFoundError(checked);
|
|
11494
11993
|
}
|
|
11495
|
-
var ClaudeBinaryNotFoundError,
|
|
11994
|
+
var ClaudeBinaryNotFoundError, defaultDeps5;
|
|
11496
11995
|
var init_binary = __esm(() => {
|
|
11497
11996
|
ClaudeBinaryNotFoundError = class ClaudeBinaryNotFoundError extends Error {
|
|
11498
11997
|
checkedPaths;
|
|
@@ -11502,7 +12001,7 @@ var init_binary = __esm(() => {
|
|
|
11502
12001
|
this.checkedPaths = checkedPaths;
|
|
11503
12002
|
}
|
|
11504
12003
|
};
|
|
11505
|
-
|
|
12004
|
+
defaultDeps5 = {
|
|
11506
12005
|
fileExists(p) {
|
|
11507
12006
|
try {
|
|
11508
12007
|
return statSync2(p).isFile();
|
|
@@ -11518,7 +12017,7 @@ var init_binary = __esm(() => {
|
|
|
11518
12017
|
},
|
|
11519
12018
|
which(name) {
|
|
11520
12019
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
11521
|
-
const out =
|
|
12020
|
+
const out = spawnSync7(cmd, [name], { encoding: "utf8" });
|
|
11522
12021
|
if (out.status !== 0)
|
|
11523
12022
|
return;
|
|
11524
12023
|
const first = out.stdout.split(`
|
|
@@ -11527,7 +12026,7 @@ var init_binary = __esm(() => {
|
|
|
11527
12026
|
return;
|
|
11528
12027
|
if (first.startsWith("claude:") && first.includes("aliased to")) {
|
|
11529
12028
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
11530
|
-
return aliasTarget &&
|
|
12029
|
+
return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
|
|
11531
12030
|
}
|
|
11532
12031
|
return first;
|
|
11533
12032
|
},
|
|
@@ -11543,11 +12042,11 @@ var init_binary = __esm(() => {
|
|
|
11543
12042
|
});
|
|
11544
12043
|
|
|
11545
12044
|
// src/engine/codex-local/binary.ts
|
|
11546
|
-
import { spawnSync as
|
|
11547
|
-
import { existsSync as
|
|
12045
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
12046
|
+
import { existsSync as existsSync10, statSync as statSync3 } from "fs";
|
|
11548
12047
|
import { homedir as homedir13 } from "os";
|
|
11549
12048
|
import path8 from "path";
|
|
11550
|
-
async function findCodexBinary(deps =
|
|
12049
|
+
async function findCodexBinary(deps = defaultDeps6) {
|
|
11551
12050
|
const checked = [];
|
|
11552
12051
|
const tryPath = (p) => {
|
|
11553
12052
|
if (!p)
|
|
@@ -11580,7 +12079,7 @@ async function findCodexBinary(deps = defaultDeps5) {
|
|
|
11580
12079
|
}
|
|
11581
12080
|
throw new CodexBinaryNotFoundError(checked);
|
|
11582
12081
|
}
|
|
11583
|
-
var CodexBinaryNotFoundError,
|
|
12082
|
+
var CodexBinaryNotFoundError, defaultDeps6;
|
|
11584
12083
|
var init_binary2 = __esm(() => {
|
|
11585
12084
|
CodexBinaryNotFoundError = class CodexBinaryNotFoundError extends Error {
|
|
11586
12085
|
checkedPaths;
|
|
@@ -11590,7 +12089,7 @@ var init_binary2 = __esm(() => {
|
|
|
11590
12089
|
this.checkedPaths = checkedPaths;
|
|
11591
12090
|
}
|
|
11592
12091
|
};
|
|
11593
|
-
|
|
12092
|
+
defaultDeps6 = {
|
|
11594
12093
|
fileExists(p) {
|
|
11595
12094
|
try {
|
|
11596
12095
|
return statSync3(p).isFile();
|
|
@@ -11606,7 +12105,7 @@ var init_binary2 = __esm(() => {
|
|
|
11606
12105
|
},
|
|
11607
12106
|
which(name) {
|
|
11608
12107
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
11609
|
-
const out =
|
|
12108
|
+
const out = spawnSync8(cmd, [name], { encoding: "utf8" });
|
|
11610
12109
|
if (out.status !== 0)
|
|
11611
12110
|
return;
|
|
11612
12111
|
const first = out.stdout.split(`
|
|
@@ -11615,7 +12114,7 @@ var init_binary2 = __esm(() => {
|
|
|
11615
12114
|
return;
|
|
11616
12115
|
if (first.startsWith("codex:") && first.includes("aliased to")) {
|
|
11617
12116
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
11618
|
-
return aliasTarget &&
|
|
12117
|
+
return aliasTarget && existsSync10(aliasTarget) ? aliasTarget : undefined;
|
|
11619
12118
|
}
|
|
11620
12119
|
return first;
|
|
11621
12120
|
},
|
|
@@ -11631,11 +12130,11 @@ var init_binary2 = __esm(() => {
|
|
|
11631
12130
|
});
|
|
11632
12131
|
|
|
11633
12132
|
// src/engine/copilot-local/binary.ts
|
|
11634
|
-
import { spawnSync as
|
|
11635
|
-
import { existsSync as
|
|
12133
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
12134
|
+
import { existsSync as existsSync11, statSync as statSync4 } from "fs";
|
|
11636
12135
|
import { homedir as homedir14 } from "os";
|
|
11637
12136
|
import path9 from "path";
|
|
11638
|
-
async function findCopilotBinary(deps =
|
|
12137
|
+
async function findCopilotBinary(deps = defaultDeps7) {
|
|
11639
12138
|
const checked = [];
|
|
11640
12139
|
const tryPath = (p) => {
|
|
11641
12140
|
if (!p)
|
|
@@ -11692,7 +12191,7 @@ async function findCopilotBinary(deps = defaultDeps6) {
|
|
|
11692
12191
|
}
|
|
11693
12192
|
throw new CopilotBinaryNotFoundError(checked);
|
|
11694
12193
|
}
|
|
11695
|
-
var CopilotBinaryNotFoundError,
|
|
12194
|
+
var CopilotBinaryNotFoundError, defaultDeps7;
|
|
11696
12195
|
var init_binary3 = __esm(() => {
|
|
11697
12196
|
CopilotBinaryNotFoundError = class CopilotBinaryNotFoundError extends Error {
|
|
11698
12197
|
checkedPaths;
|
|
@@ -11702,7 +12201,7 @@ var init_binary3 = __esm(() => {
|
|
|
11702
12201
|
this.checkedPaths = checkedPaths;
|
|
11703
12202
|
}
|
|
11704
12203
|
};
|
|
11705
|
-
|
|
12204
|
+
defaultDeps7 = {
|
|
11706
12205
|
fileExists(p) {
|
|
11707
12206
|
try {
|
|
11708
12207
|
return statSync4(p).isFile();
|
|
@@ -11718,7 +12217,7 @@ var init_binary3 = __esm(() => {
|
|
|
11718
12217
|
},
|
|
11719
12218
|
which(name) {
|
|
11720
12219
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
11721
|
-
const out =
|
|
12220
|
+
const out = spawnSync9(cmd, [name], { encoding: "utf8" });
|
|
11722
12221
|
if (out.status !== 0)
|
|
11723
12222
|
return;
|
|
11724
12223
|
const first = out.stdout.split(`
|
|
@@ -11727,7 +12226,7 @@ var init_binary3 = __esm(() => {
|
|
|
11727
12226
|
return;
|
|
11728
12227
|
if (first.startsWith("copilot:") && first.includes("aliased to")) {
|
|
11729
12228
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
11730
|
-
return aliasTarget &&
|
|
12229
|
+
return aliasTarget && existsSync11(aliasTarget) ? aliasTarget : undefined;
|
|
11731
12230
|
}
|
|
11732
12231
|
return first;
|
|
11733
12232
|
},
|
|
@@ -11738,7 +12237,7 @@ var init_binary3 = __esm(() => {
|
|
|
11738
12237
|
});
|
|
11739
12238
|
|
|
11740
12239
|
// src/engine/account-detect.ts
|
|
11741
|
-
import { readFileSync as
|
|
12240
|
+
import { readFileSync as readFileSync10, statSync as statSync5 } from "fs";
|
|
11742
12241
|
import { homedir as homedir15 } from "os";
|
|
11743
12242
|
import path10 from "path";
|
|
11744
12243
|
function claudeGlobalConfigPath(env, home) {
|
|
@@ -11774,9 +12273,9 @@ function decodeJwtPayload(jwt) {
|
|
|
11774
12273
|
return null;
|
|
11775
12274
|
}
|
|
11776
12275
|
}
|
|
11777
|
-
async function probeBinary(
|
|
12276
|
+
async function probeBinary(probe2) {
|
|
11778
12277
|
try {
|
|
11779
|
-
const p = await
|
|
12278
|
+
const p = await probe2();
|
|
11780
12279
|
return { found: true, path: p };
|
|
11781
12280
|
} catch (err) {
|
|
11782
12281
|
if (err instanceof ClaudeBinaryNotFoundError || err instanceof CodexBinaryNotFoundError || err instanceof CopilotBinaryNotFoundError) {
|
|
@@ -11785,20 +12284,20 @@ async function probeBinary(probe) {
|
|
|
11785
12284
|
return { found: false, error: err instanceof Error ? err.message : String(err) };
|
|
11786
12285
|
}
|
|
11787
12286
|
}
|
|
11788
|
-
async function detectAvailableVendors(deps =
|
|
12287
|
+
async function detectAvailableVendors(deps = defaultDeps8) {
|
|
11789
12288
|
const probes = [
|
|
11790
12289
|
["claude", () => deps.findClaudeBinary()],
|
|
11791
12290
|
["codex", () => deps.findCodexBinary()],
|
|
11792
12291
|
["copilot", () => deps.findCopilotBinary()]
|
|
11793
12292
|
];
|
|
11794
|
-
const detected = await Promise.all(probes.map(async ([vendor,
|
|
12293
|
+
const detected = await Promise.all(probes.map(async ([vendor, probe2]) => (await probeBinary(probe2)).found ? vendor : null));
|
|
11795
12294
|
return detected.filter((v) => v !== null);
|
|
11796
12295
|
}
|
|
11797
|
-
async function availableEngineIds(deps =
|
|
12296
|
+
async function availableEngineIds(deps = defaultDeps8) {
|
|
11798
12297
|
const builtins = await detectAvailableVendors(deps);
|
|
11799
12298
|
return [...builtins, ...getCustomEngineIds()];
|
|
11800
12299
|
}
|
|
11801
|
-
async function detectClaudeAccount(deps =
|
|
12300
|
+
async function detectClaudeAccount(deps = defaultDeps8) {
|
|
11802
12301
|
const binary = await probeBinary(() => deps.findClaudeBinary());
|
|
11803
12302
|
const configPath = claudeGlobalConfigPath(deps.env, deps.home());
|
|
11804
12303
|
let raw;
|
|
@@ -11841,7 +12340,7 @@ async function detectClaudeAccount(deps = defaultDeps7) {
|
|
|
11841
12340
|
}
|
|
11842
12341
|
};
|
|
11843
12342
|
}
|
|
11844
|
-
async function detectCodexAccount(deps =
|
|
12343
|
+
async function detectCodexAccount(deps = defaultDeps8) {
|
|
11845
12344
|
const binary = await probeBinary(() => deps.findCodexBinary());
|
|
11846
12345
|
const authPath = codexAuthPath(deps.env, deps.home());
|
|
11847
12346
|
let raw;
|
|
@@ -11892,7 +12391,7 @@ async function detectCodexAccount(deps = defaultDeps7) {
|
|
|
11892
12391
|
}
|
|
11893
12392
|
return { binary, account: { kind: "none" } };
|
|
11894
12393
|
}
|
|
11895
|
-
async function detectCopilotAccount(deps =
|
|
12394
|
+
async function detectCopilotAccount(deps = defaultDeps8) {
|
|
11896
12395
|
const binary = await probeBinary(() => deps.findCopilotBinary());
|
|
11897
12396
|
for (const source of ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"]) {
|
|
11898
12397
|
if (deps.env(source)?.trim())
|
|
@@ -11950,13 +12449,13 @@ function hasStringDeep(value, interestingKeys, depth = 0) {
|
|
|
11950
12449
|
}
|
|
11951
12450
|
return false;
|
|
11952
12451
|
}
|
|
11953
|
-
var
|
|
12452
|
+
var defaultDeps8;
|
|
11954
12453
|
var init_account_detect = __esm(() => {
|
|
11955
12454
|
init_repos();
|
|
11956
12455
|
init_binary();
|
|
11957
12456
|
init_binary2();
|
|
11958
12457
|
init_binary3();
|
|
11959
|
-
|
|
12458
|
+
defaultDeps8 = {
|
|
11960
12459
|
readFile(p) {
|
|
11961
12460
|
try {
|
|
11962
12461
|
statSync5(p);
|
|
@@ -11965,7 +12464,7 @@ var init_account_detect = __esm(() => {
|
|
|
11965
12464
|
return null;
|
|
11966
12465
|
throw err;
|
|
11967
12466
|
}
|
|
11968
|
-
return
|
|
12467
|
+
return readFileSync10(p, "utf8");
|
|
11969
12468
|
},
|
|
11970
12469
|
env(name) {
|
|
11971
12470
|
return process.env[name];
|
|
@@ -12096,8 +12595,8 @@ function validateRepoPath(repo) {
|
|
|
12096
12595
|
if (!stat4.isDirectory())
|
|
12097
12596
|
return `not a directory: ${trimmed}`;
|
|
12098
12597
|
try {
|
|
12099
|
-
const { spawnSync:
|
|
12100
|
-
const out =
|
|
12598
|
+
const { spawnSync: spawnSync10 } = __require("child_process");
|
|
12599
|
+
const out = spawnSync10("git", ["rev-parse", "--git-dir"], {
|
|
12101
12600
|
cwd: trimmed,
|
|
12102
12601
|
encoding: "utf-8",
|
|
12103
12602
|
timeout: 2000,
|
|
@@ -12114,8 +12613,8 @@ function getCurrentBranch(repo) {
|
|
|
12114
12613
|
if (!repo)
|
|
12115
12614
|
return null;
|
|
12116
12615
|
try {
|
|
12117
|
-
const { spawnSync:
|
|
12118
|
-
const out =
|
|
12616
|
+
const { spawnSync: spawnSync10 } = __require("child_process");
|
|
12617
|
+
const out = spawnSync10("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
12119
12618
|
cwd: repo,
|
|
12120
12619
|
encoding: "utf-8",
|
|
12121
12620
|
timeout: 2000,
|
|
@@ -12135,8 +12634,8 @@ function listLocalBranches(repo) {
|
|
|
12135
12634
|
if (!repo)
|
|
12136
12635
|
return [];
|
|
12137
12636
|
try {
|
|
12138
|
-
const { spawnSync:
|
|
12139
|
-
const out =
|
|
12637
|
+
const { spawnSync: spawnSync10 } = __require("child_process");
|
|
12638
|
+
const out = spawnSync10("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
|
|
12140
12639
|
cwd: repo,
|
|
12141
12640
|
encoding: "utf-8",
|
|
12142
12641
|
timeout: 2000
|
|
@@ -15838,12 +16337,12 @@ var init_focus = __esm(() => {
|
|
|
15838
16337
|
});
|
|
15839
16338
|
|
|
15840
16339
|
// src/tui/context/kv.tsx
|
|
15841
|
-
import { mkdirSync as
|
|
16340
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
15842
16341
|
import { dirname as dirname7 } from "path";
|
|
15843
16342
|
function loadInitial() {
|
|
15844
16343
|
const statePath2 = kvStatePath();
|
|
15845
16344
|
try {
|
|
15846
|
-
const text =
|
|
16345
|
+
const text = readFileSync11(statePath2, "utf8");
|
|
15847
16346
|
const parsed = JSON.parse(text);
|
|
15848
16347
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
15849
16348
|
return parsed;
|
|
@@ -15867,7 +16366,7 @@ var init_kv = __esm(() => {
|
|
|
15867
16366
|
function writeNow(label) {
|
|
15868
16367
|
const statePath2 = kvStatePath();
|
|
15869
16368
|
try {
|
|
15870
|
-
|
|
16369
|
+
mkdirSync5(dirname7(statePath2), {
|
|
15871
16370
|
recursive: true
|
|
15872
16371
|
});
|
|
15873
16372
|
const tmp = `${statePath2}.tmp`;
|
|
@@ -15922,7 +16421,7 @@ var init_kv = __esm(() => {
|
|
|
15922
16421
|
}
|
|
15923
16422
|
const statePath2 = kvStatePath();
|
|
15924
16423
|
try {
|
|
15925
|
-
|
|
16424
|
+
mkdirSync5(dirname7(statePath2), {
|
|
15926
16425
|
recursive: true
|
|
15927
16426
|
});
|
|
15928
16427
|
const tmp = `${statePath2}.tmp`;
|
|
@@ -15939,10 +16438,10 @@ var init_kv = __esm(() => {
|
|
|
15939
16438
|
});
|
|
15940
16439
|
|
|
15941
16440
|
// src/tui/lib/persisted-ui-prefs.ts
|
|
15942
|
-
import { readFileSync as
|
|
16441
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
15943
16442
|
function readPersistedUiPrefs(fallbackTheme) {
|
|
15944
16443
|
try {
|
|
15945
|
-
const parsed = JSON.parse(
|
|
16444
|
+
const parsed = JSON.parse(readFileSync12(kvStatePath(), "utf8"));
|
|
15946
16445
|
const theme = typeof parsed.activeTheme === "string" && hasTheme(parsed.activeTheme) ? parsed.activeTheme : fallbackTheme;
|
|
15947
16446
|
const transparent = parsed.transparentBackground === true;
|
|
15948
16447
|
const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent) ? parsed.focusAccent : null;
|
|
@@ -16222,6 +16721,9 @@ async function ensureTaskSession2(orch, task, repo, vendor) {
|
|
|
16222
16721
|
const {
|
|
16223
16722
|
resolveRepoInit: resolveRepoInit2
|
|
16224
16723
|
} = await Promise.resolve().then(() => (init_repo_init(), exports_repo_init));
|
|
16724
|
+
const {
|
|
16725
|
+
isRemoteRepoKey: isRemoteRepoKey2
|
|
16726
|
+
} = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
16225
16727
|
const init2 = resolveRepoInit2(repo, worktree);
|
|
16226
16728
|
const ok = await ensureSession2({
|
|
16227
16729
|
name: session,
|
|
@@ -16229,6 +16731,7 @@ async function ensureTaskSession2(orch, task, repo, vendor) {
|
|
|
16229
16731
|
command: interactiveEngineCommand(vendor),
|
|
16230
16732
|
taskId: task.id,
|
|
16231
16733
|
vendor,
|
|
16734
|
+
remoteKey: isRemoteRepoKey2(repo) ? repo : undefined,
|
|
16232
16735
|
initScript: init2.initScript
|
|
16233
16736
|
});
|
|
16234
16737
|
if (!ok)
|
|
@@ -17646,6 +18149,9 @@ function engineRowCount(customCount) {
|
|
|
17646
18149
|
return ALL_VENDORS.length + customCount + 1;
|
|
17647
18150
|
}
|
|
17648
18151
|
function devRowCount(hasDaemon) {
|
|
18152
|
+
return (hasDaemon ? 2 : 1) + 1;
|
|
18153
|
+
}
|
|
18154
|
+
function experimentalRemoteRowIndex(hasDaemon) {
|
|
17649
18155
|
return hasDaemon ? 2 : 1;
|
|
17650
18156
|
}
|
|
17651
18157
|
function feedbackRowCount() {
|
|
@@ -18529,12 +19035,15 @@ function DevSettingsSection(props) {
|
|
|
18529
19035
|
} = useTheme();
|
|
18530
19036
|
const resetIsCursor = () => props.level() === "body" && props.bodyRow() === 0;
|
|
18531
19037
|
const restartIsCursor = () => props.level() === "body" && props.bodyRow() === 1;
|
|
19038
|
+
const experimentalRow = () => experimentalRemoteRowIndex(props.hasDaemon);
|
|
19039
|
+
const remoteIsCursor = () => props.level() === "body" && props.bodyRow() === experimentalRow();
|
|
18532
19040
|
return (() => {
|
|
18533
|
-
var _el$128 = createElement("box"), _el$129 = createElement("text"), _el$131 = createElement("text"), _el$133 = createElement("box"), _el$134 = createElement("text"), _el$144 = createElement("text");
|
|
19041
|
+
var _el$128 = createElement("box"), _el$129 = createElement("text"), _el$131 = createElement("text"), _el$133 = createElement("box"), _el$134 = createElement("text"), _el$144 = createElement("text"), _el$146 = createElement("box"), _el$147 = createElement("text"), _el$149 = createElement("text"), _el$151 = createElement("box"), _el$152 = createElement("text");
|
|
18534
19042
|
insertNode(_el$128, _el$129);
|
|
18535
19043
|
insertNode(_el$128, _el$131);
|
|
18536
19044
|
insertNode(_el$128, _el$133);
|
|
18537
19045
|
insertNode(_el$128, _el$144);
|
|
19046
|
+
insertNode(_el$128, _el$146);
|
|
18538
19047
|
setProp(_el$128, "flexDirection", "column");
|
|
18539
19048
|
setProp(_el$128, "gap", 1);
|
|
18540
19049
|
insertNode(_el$129, createTextNode(`Reset UI state`));
|
|
@@ -18597,8 +19106,27 @@ function DevSettingsSection(props) {
|
|
|
18597
19106
|
}), _el$144);
|
|
18598
19107
|
insertNode(_el$144, createTextNode(`Daemon wedged or unresponsive? From a shell, run \`kobe doctor\` to diagnose, or \`kobe reset\` to stop the daemon + kill sessions (keeps your tasks). Use \`kobe reset --hard\` only to also wipe the task index + UI state.`));
|
|
18599
19108
|
setProp(_el$144, "wrapMode", "word");
|
|
19109
|
+
insertNode(_el$146, _el$147);
|
|
19110
|
+
insertNode(_el$146, _el$149);
|
|
19111
|
+
insertNode(_el$146, _el$151);
|
|
19112
|
+
setProp(_el$146, "flexDirection", "column");
|
|
19113
|
+
setProp(_el$146, "gap", 0);
|
|
19114
|
+
setProp(_el$146, "paddingTop", 1);
|
|
19115
|
+
insertNode(_el$147, createTextNode(`Experimental`));
|
|
19116
|
+
insertNode(_el$149, createTextNode(`Remote projects (SSH): register a project whose git worktrees + engine run on another host over SSH, driven from this local kobe. Unfinished \u2014 file/diff panes still degrade for remote. Enables \`kobe add --remote\`.`));
|
|
19117
|
+
setProp(_el$149, "wrapMode", "word");
|
|
19118
|
+
insertNode(_el$151, _el$152);
|
|
19119
|
+
setProp(_el$151, "flexDirection", "row");
|
|
19120
|
+
setProp(_el$151, "paddingLeft", 1);
|
|
19121
|
+
setProp(_el$151, "paddingRight", 1);
|
|
19122
|
+
setProp(_el$151, "onMouseUp", () => {
|
|
19123
|
+
props.setLevel("body");
|
|
19124
|
+
props.setBodyRow(experimentalRow());
|
|
19125
|
+
props.toggleRemoteProjects();
|
|
19126
|
+
});
|
|
19127
|
+
insert(_el$152, () => props.remoteProjectsEnabled() ? "[x] Remote projects (on)" : "[ ] Remote projects (off)");
|
|
18600
19128
|
effect((_p$) => {
|
|
18601
|
-
var _v$86 = theme.text, _v$87 = TextAttributes8.BOLD, _v$88 = theme.textMuted, _v$89 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$90 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$91 = TextAttributes8.BOLD, _v$92 = theme.textMuted;
|
|
19129
|
+
var _v$86 = theme.text, _v$87 = TextAttributes8.BOLD, _v$88 = theme.textMuted, _v$89 = resetIsCursor() ? theme.primary : theme.backgroundElement, _v$90 = resetIsCursor() ? theme.selectedListItemText : theme.warning, _v$91 = TextAttributes8.BOLD, _v$92 = theme.textMuted, _v$93 = theme.text, _v$94 = TextAttributes8.BOLD, _v$95 = theme.textMuted, _v$96 = remoteIsCursor() ? theme.primary : theme.backgroundElement, _v$97 = remoteIsCursor() ? theme.selectedListItemText : theme.text, _v$98 = props.remoteProjectsEnabled() ? TextAttributes8.BOLD : undefined;
|
|
18602
19130
|
_v$86 !== _p$.e && (_p$.e = setProp(_el$129, "fg", _v$86, _p$.e));
|
|
18603
19131
|
_v$87 !== _p$.t && (_p$.t = setProp(_el$129, "attributes", _v$87, _p$.t));
|
|
18604
19132
|
_v$88 !== _p$.a && (_p$.a = setProp(_el$131, "fg", _v$88, _p$.a));
|
|
@@ -18606,6 +19134,12 @@ function DevSettingsSection(props) {
|
|
|
18606
19134
|
_v$90 !== _p$.i && (_p$.i = setProp(_el$134, "fg", _v$90, _p$.i));
|
|
18607
19135
|
_v$91 !== _p$.n && (_p$.n = setProp(_el$134, "attributes", _v$91, _p$.n));
|
|
18608
19136
|
_v$92 !== _p$.s && (_p$.s = setProp(_el$144, "fg", _v$92, _p$.s));
|
|
19137
|
+
_v$93 !== _p$.h && (_p$.h = setProp(_el$147, "fg", _v$93, _p$.h));
|
|
19138
|
+
_v$94 !== _p$.r && (_p$.r = setProp(_el$147, "attributes", _v$94, _p$.r));
|
|
19139
|
+
_v$95 !== _p$.d && (_p$.d = setProp(_el$149, "fg", _v$95, _p$.d));
|
|
19140
|
+
_v$96 !== _p$.l && (_p$.l = setProp(_el$151, "backgroundColor", _v$96, _p$.l));
|
|
19141
|
+
_v$97 !== _p$.u && (_p$.u = setProp(_el$152, "fg", _v$97, _p$.u));
|
|
19142
|
+
_v$98 !== _p$.c && (_p$.c = setProp(_el$152, "attributes", _v$98, _p$.c));
|
|
18609
19143
|
return _p$;
|
|
18610
19144
|
}, {
|
|
18611
19145
|
e: undefined,
|
|
@@ -18614,7 +19148,13 @@ function DevSettingsSection(props) {
|
|
|
18614
19148
|
o: undefined,
|
|
18615
19149
|
i: undefined,
|
|
18616
19150
|
n: undefined,
|
|
18617
|
-
s: undefined
|
|
19151
|
+
s: undefined,
|
|
19152
|
+
h: undefined,
|
|
19153
|
+
r: undefined,
|
|
19154
|
+
d: undefined,
|
|
19155
|
+
l: undefined,
|
|
19156
|
+
u: undefined,
|
|
19157
|
+
c: undefined
|
|
18618
19158
|
});
|
|
18619
19159
|
return _el$128;
|
|
18620
19160
|
})();
|
|
@@ -18739,6 +19279,12 @@ function SettingsDialog(props) {
|
|
|
18739
19279
|
function toggleSound() {
|
|
18740
19280
|
props.kv.set("notifications.sound.enabled", !soundEnabled());
|
|
18741
19281
|
}
|
|
19282
|
+
function remoteProjectsEnabled() {
|
|
19283
|
+
return props.kv.get("experimental.remoteProjects", false) === true;
|
|
19284
|
+
}
|
|
19285
|
+
function toggleRemoteProjects() {
|
|
19286
|
+
props.kv.set("experimental.remoteProjects", !remoteProjectsEnabled());
|
|
19287
|
+
}
|
|
18742
19288
|
function customEngines() {
|
|
18743
19289
|
const raw = props.kv.get("customEngineIds", []);
|
|
18744
19290
|
return Array.isArray(raw) ? raw.filter((s) => typeof s === "string" && s.trim().length > 0) : [];
|
|
@@ -19006,6 +19552,8 @@ function SettingsDialog(props) {
|
|
|
19006
19552
|
confirmResetState(dialog, props.kv, renderer);
|
|
19007
19553
|
else if (hasDaemon && bodyRow() === 1)
|
|
19008
19554
|
confirmRestartDaemon(dialog, props.orchestrator, renderer);
|
|
19555
|
+
else if (bodyRow() === experimentalRemoteRowIndex(hasDaemon))
|
|
19556
|
+
toggleRemoteProjects();
|
|
19009
19557
|
}
|
|
19010
19558
|
}
|
|
19011
19559
|
useBindings(() => ({
|
|
@@ -19196,7 +19744,9 @@ function SettingsDialog(props) {
|
|
|
19196
19744
|
setBodyRow,
|
|
19197
19745
|
hasDaemon,
|
|
19198
19746
|
confirmReset: () => void confirmResetState(dialog, props.kv, renderer),
|
|
19199
|
-
confirmRestartDaemon: () => void confirmRestartDaemon(dialog, props.orchestrator, renderer)
|
|
19747
|
+
confirmRestartDaemon: () => void confirmRestartDaemon(dialog, props.orchestrator, renderer),
|
|
19748
|
+
remoteProjectsEnabled,
|
|
19749
|
+
toggleRemoteProjects
|
|
19200
19750
|
});
|
|
19201
19751
|
}
|
|
19202
19752
|
}), null);
|
|
@@ -19266,7 +19816,7 @@ var pulse_default = "../pulse-n3cq1btw.wav";
|
|
|
19266
19816
|
var init_pulse = () => {};
|
|
19267
19817
|
|
|
19268
19818
|
// src/tui/lib/sound.ts
|
|
19269
|
-
import { existsSync as
|
|
19819
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
19270
19820
|
import { tmpdir as tmpdir2 } from "os";
|
|
19271
19821
|
import { basename as basename4, isAbsolute, join as join14, resolve as resolve8 } from "path";
|
|
19272
19822
|
function args(player, file, volume) {
|
|
@@ -19291,12 +19841,12 @@ function pickPlayer() {
|
|
|
19291
19841
|
return cachedPlayer;
|
|
19292
19842
|
const path12 = process.env.PATH ?? "";
|
|
19293
19843
|
const segments = path12.split(":").filter(Boolean);
|
|
19294
|
-
cachedPlayer = PLAYERS.find((p) => segments.some((dir) =>
|
|
19844
|
+
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync13(join14(dir, p)))) ?? null;
|
|
19295
19845
|
return cachedPlayer;
|
|
19296
19846
|
}
|
|
19297
19847
|
async function ensureAsset() {
|
|
19298
19848
|
cachedPath ??= (async () => {
|
|
19299
|
-
|
|
19849
|
+
mkdirSync6(DIR, { recursive: true });
|
|
19300
19850
|
const dest = join14(DIR, basename4(pulseAsset));
|
|
19301
19851
|
const out = Bun.file(dest);
|
|
19302
19852
|
if (await out.exists())
|
|
@@ -19632,7 +20182,7 @@ var init_task_actions = __esm(() => {
|
|
|
19632
20182
|
|
|
19633
20183
|
// src/tui/lib/worktree-opener.ts
|
|
19634
20184
|
import { spawn as spawn3 } from "child_process";
|
|
19635
|
-
import { existsSync as
|
|
20185
|
+
import { existsSync as existsSync14 } from "fs";
|
|
19636
20186
|
import { basename as basename5, delimiter, isAbsolute as isAbsolute2, join as join15 } from "path";
|
|
19637
20187
|
function executableOnPath(command, env, exists) {
|
|
19638
20188
|
if (isAbsolute2(command))
|
|
@@ -19660,8 +20210,8 @@ function labelForOverride(command) {
|
|
|
19660
20210
|
}
|
|
19661
20211
|
function detectWorktreeOpener(deps = {}) {
|
|
19662
20212
|
const env = deps.env ?? process.env;
|
|
19663
|
-
const
|
|
19664
|
-
const exists = deps.exists ??
|
|
20213
|
+
const platform2 = deps.platform ?? process.platform;
|
|
20214
|
+
const exists = deps.exists ?? existsSync14;
|
|
19665
20215
|
const override = env.KOBE_OPEN_EDITOR?.trim();
|
|
19666
20216
|
if (override) {
|
|
19667
20217
|
return { id: "env", label: labelForOverride(override), command: override, args: [] };
|
|
@@ -19671,7 +20221,7 @@ function detectWorktreeOpener(deps = {}) {
|
|
|
19671
20221
|
return { id: c.id, label: c.label, command: c.command, args: [] };
|
|
19672
20222
|
}
|
|
19673
20223
|
}
|
|
19674
|
-
if (
|
|
20224
|
+
if (platform2 === "darwin" && executableOnPath("open", env, exists)) {
|
|
19675
20225
|
for (const app of MAC_APP_CANDIDATES) {
|
|
19676
20226
|
if (app.paths.some((path12) => exists(path12))) {
|
|
19677
20227
|
return { id: app.id, label: app.label, command: "open", args: ["-a", app.appName] };
|
|
@@ -19679,7 +20229,7 @@ function detectWorktreeOpener(deps = {}) {
|
|
|
19679
20229
|
}
|
|
19680
20230
|
return { id: "mac-open", label: "Finder", command: "open", args: [] };
|
|
19681
20231
|
}
|
|
19682
|
-
if (
|
|
20232
|
+
if (platform2 === "linux" && executableOnPath("xdg-open", env, exists)) {
|
|
19683
20233
|
return { id: "xdg-open", label: "Open", command: "xdg-open", args: [] };
|
|
19684
20234
|
}
|
|
19685
20235
|
return null;
|
|
@@ -19739,12 +20289,12 @@ var init_worktree_opener = __esm(() => {
|
|
|
19739
20289
|
});
|
|
19740
20290
|
|
|
19741
20291
|
// src/tui/panes/sidebar/git-head.ts
|
|
19742
|
-
import { spawnSync as
|
|
20292
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
19743
20293
|
function readCurrentBranch(repo) {
|
|
19744
20294
|
if (!repo)
|
|
19745
20295
|
return "";
|
|
19746
20296
|
try {
|
|
19747
|
-
const out =
|
|
20297
|
+
const out = spawnSync10("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
19748
20298
|
cwd: repo,
|
|
19749
20299
|
encoding: "utf8",
|
|
19750
20300
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -19755,7 +20305,7 @@ function readCurrentBranch(repo) {
|
|
|
19755
20305
|
if (name && name !== "HEAD")
|
|
19756
20306
|
return name;
|
|
19757
20307
|
}
|
|
19758
|
-
const head =
|
|
20308
|
+
const head = spawnSync10("git", ["rev-parse", "--verify", "HEAD"], {
|
|
19759
20309
|
cwd: repo,
|
|
19760
20310
|
encoding: "utf8",
|
|
19761
20311
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -20954,8 +21504,13 @@ var exports_host3 = {};
|
|
|
20954
21504
|
__export(exports_host3, {
|
|
20955
21505
|
startTasksPane: () => startTasksPane
|
|
20956
21506
|
});
|
|
20957
|
-
import { existsSync as
|
|
21507
|
+
import { existsSync as existsSync15 } from "fs";
|
|
20958
21508
|
import { TextAttributes as TextAttributes13 } from "@opentui/core";
|
|
21509
|
+
function worktreeCwdUsable(cwd) {
|
|
21510
|
+
if (!cwd)
|
|
21511
|
+
return false;
|
|
21512
|
+
return execHostForWorktreePath(cwd).isRemote || existsSync15(cwd);
|
|
21513
|
+
}
|
|
20959
21514
|
function TasksShell(props) {
|
|
20960
21515
|
const themeCtx = useTheme();
|
|
20961
21516
|
const {
|
|
@@ -21228,7 +21783,7 @@ function TasksShell(props) {
|
|
|
21228
21783
|
async function openSelectedWorktree(id) {
|
|
21229
21784
|
const task = props.tasks().find((t) => t.id === id);
|
|
21230
21785
|
let worktree = task?.worktreePath;
|
|
21231
|
-
if (!worktree || !
|
|
21786
|
+
if (!worktree || !existsSync15(worktree)) {
|
|
21232
21787
|
if (!props.orch) {
|
|
21233
21788
|
console.error("[kobe tasks] no daemon; cannot materialise worktree");
|
|
21234
21789
|
notifyError("No daemon running \u2014 can't create the worktree");
|
|
@@ -21243,7 +21798,7 @@ function TasksShell(props) {
|
|
|
21243
21798
|
}
|
|
21244
21799
|
await props.reload();
|
|
21245
21800
|
}
|
|
21246
|
-
if (!worktree || !
|
|
21801
|
+
if (!worktree || !existsSync15(worktree))
|
|
21247
21802
|
return;
|
|
21248
21803
|
const opener = detectWorktreeOpener();
|
|
21249
21804
|
if (!opener) {
|
|
@@ -21321,13 +21876,14 @@ function TasksShell(props) {
|
|
|
21321
21876
|
const exists = await sessionExists(name);
|
|
21322
21877
|
if (exists) {
|
|
21323
21878
|
const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
|
|
21324
|
-
if (
|
|
21879
|
+
if (worktreeCwdUsable(cwd2)) {
|
|
21325
21880
|
await ensureSession({
|
|
21326
21881
|
name,
|
|
21327
21882
|
cwd: cwd2,
|
|
21328
21883
|
command: interactiveEngineCommand(task?.vendor),
|
|
21329
21884
|
taskId: id,
|
|
21330
|
-
vendor: task?.vendor
|
|
21885
|
+
vendor: task?.vendor,
|
|
21886
|
+
remoteKey: task?.repo && isRemoteRepoKey(task.repo) ? task.repo : undefined
|
|
21331
21887
|
});
|
|
21332
21888
|
}
|
|
21333
21889
|
await runTmux(["switch-client", "-t", `=${name}`]);
|
|
@@ -21335,7 +21891,7 @@ function TasksShell(props) {
|
|
|
21335
21891
|
return;
|
|
21336
21892
|
}
|
|
21337
21893
|
let cwd = task?.worktreePath;
|
|
21338
|
-
if (!
|
|
21894
|
+
if (!worktreeCwdUsable(cwd)) {
|
|
21339
21895
|
if (!props.orch) {
|
|
21340
21896
|
console.error("[kobe tasks] no daemon; cannot materialise worktree");
|
|
21341
21897
|
notifyError("No daemon running \u2014 can't open this task");
|
|
@@ -21350,7 +21906,7 @@ function TasksShell(props) {
|
|
|
21350
21906
|
}
|
|
21351
21907
|
await props.reload();
|
|
21352
21908
|
}
|
|
21353
|
-
if (!
|
|
21909
|
+
if (!worktreeCwdUsable(cwd))
|
|
21354
21910
|
return;
|
|
21355
21911
|
const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
|
|
21356
21912
|
const ready = await ensureSession({
|
|
@@ -21359,6 +21915,7 @@ function TasksShell(props) {
|
|
|
21359
21915
|
command: interactiveEngineCommand(task?.vendor),
|
|
21360
21916
|
taskId: id,
|
|
21361
21917
|
vendor: task?.vendor,
|
|
21918
|
+
remoteKey: task?.repo && isRemoteRepoKey(task.repo) ? task.repo : undefined,
|
|
21362
21919
|
initScript: init2.initScript,
|
|
21363
21920
|
initPrompt: init2.initPrompt
|
|
21364
21921
|
});
|
|
@@ -21720,6 +22277,7 @@ var init_host3 = __esm(() => {
|
|
|
21720
22277
|
init_account_detect();
|
|
21721
22278
|
init_interactive_command();
|
|
21722
22279
|
init_env();
|
|
22280
|
+
init_resolve();
|
|
21723
22281
|
init_errors();
|
|
21724
22282
|
init_store();
|
|
21725
22283
|
init_repo_init();
|
|
@@ -21906,13 +22464,13 @@ var exports_host5 = {};
|
|
|
21906
22464
|
__export(exports_host5, {
|
|
21907
22465
|
startUpdateHost: () => startUpdateHost
|
|
21908
22466
|
});
|
|
21909
|
-
import { spawn as spawn4, spawnSync as
|
|
22467
|
+
import { spawn as spawn4, spawnSync as spawnSync11 } from "child_process";
|
|
21910
22468
|
import { TextAttributes as TextAttributes14 } from "@opentui/core";
|
|
21911
22469
|
function openExternalUrl(url) {
|
|
21912
22470
|
if (!url)
|
|
21913
22471
|
return false;
|
|
21914
|
-
const
|
|
21915
|
-
const [command, args2] =
|
|
22472
|
+
const platform2 = process.platform;
|
|
22473
|
+
const [command, args2] = platform2 === "darwin" ? ["open", [url]] : platform2 === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
21916
22474
|
try {
|
|
21917
22475
|
const child = spawn4(command, args2, {
|
|
21918
22476
|
stdio: "ignore",
|
|
@@ -22012,7 +22570,7 @@ kobe ${CURRENT_VERSION} -> latest
|
|
|
22012
22570
|
process.stdout.write(`running: ${UPDATE_COMMAND}
|
|
22013
22571
|
|
|
22014
22572
|
`);
|
|
22015
|
-
const result =
|
|
22573
|
+
const result = spawnSync11("sh", ["-c", UPDATE_COMMAND], {
|
|
22016
22574
|
stdio: "inherit"
|
|
22017
22575
|
});
|
|
22018
22576
|
const code = result.status ?? (result.error ? 1 : 0);
|
|
@@ -22799,14 +23357,14 @@ var init_keys2 = __esm(() => {
|
|
|
22799
23357
|
|
|
22800
23358
|
// src/tui/panes/filetree/open-external.ts
|
|
22801
23359
|
import { spawn as spawn5 } from "child_process";
|
|
22802
|
-
import { existsSync as
|
|
22803
|
-
import { platform } from "os";
|
|
23360
|
+
import { existsSync as existsSync16 } from "fs";
|
|
23361
|
+
import { platform as platform2 } from "os";
|
|
22804
23362
|
function openExternally(absPath) {
|
|
22805
23363
|
if (!absPath)
|
|
22806
23364
|
return;
|
|
22807
|
-
const plat =
|
|
23365
|
+
const plat = platform2();
|
|
22808
23366
|
if (plat === "linux") {
|
|
22809
|
-
if (
|
|
23367
|
+
if (existsSync16("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
|
|
22810
23368
|
spawnDetached("wslview", [absPath], () => {
|
|
22811
23369
|
const child = spawn5("wslpath", ["-w", absPath], {
|
|
22812
23370
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -23614,12 +24172,12 @@ var init_filetree = __esm(() => {
|
|
|
23614
24172
|
});
|
|
23615
24173
|
|
|
23616
24174
|
// src/tui/ops/pr-prompt.ts
|
|
23617
|
-
import { spawnSync as
|
|
24175
|
+
import { spawnSync as spawnSync12 } from "child_process";
|
|
23618
24176
|
import { promises as fs4 } from "fs";
|
|
23619
24177
|
import path12 from "path";
|
|
23620
|
-
function
|
|
24178
|
+
function git(cwd, args2) {
|
|
23621
24179
|
try {
|
|
23622
|
-
const out =
|
|
24180
|
+
const out = spawnSync12("git", args2.slice(), {
|
|
23623
24181
|
cwd,
|
|
23624
24182
|
encoding: "utf8",
|
|
23625
24183
|
timeout: GIT_TIMEOUT_MS2,
|
|
@@ -23635,20 +24193,20 @@ function git2(cwd, args2) {
|
|
|
23635
24193
|
}
|
|
23636
24194
|
}
|
|
23637
24195
|
function currentBranch(cwd) {
|
|
23638
|
-
return
|
|
24196
|
+
return git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
|
|
23639
24197
|
}
|
|
23640
24198
|
function targetBranch(cwd) {
|
|
23641
|
-
const out =
|
|
24199
|
+
const out = git(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]);
|
|
23642
24200
|
if (!out)
|
|
23643
24201
|
return "main";
|
|
23644
24202
|
return out.startsWith("origin/") ? out.slice("origin/".length) : out;
|
|
23645
24203
|
}
|
|
23646
24204
|
function hasUpstream(cwd) {
|
|
23647
|
-
const out =
|
|
24205
|
+
const out = git(cwd, ["rev-parse", "--abbrev-ref", "@{u}"]);
|
|
23648
24206
|
return out !== null && out.length > 0;
|
|
23649
24207
|
}
|
|
23650
24208
|
function dirtyCount(cwd) {
|
|
23651
|
-
const out =
|
|
24209
|
+
const out = git(cwd, ["status", "--porcelain"]);
|
|
23652
24210
|
if (!out)
|
|
23653
24211
|
return 0;
|
|
23654
24212
|
return out.split(`
|
|
@@ -24273,6 +24831,7 @@ async function startDirectTmux() {
|
|
|
24273
24831
|
command: interactiveEngineCommand(task.vendor),
|
|
24274
24832
|
taskId: task.id,
|
|
24275
24833
|
vendor: task.vendor,
|
|
24834
|
+
remoteKey: isRemoteRepoKey(task.repo) ? task.repo : undefined,
|
|
24276
24835
|
initScript: init2.initScript,
|
|
24277
24836
|
initPrompt: init2.initPrompt
|
|
24278
24837
|
});
|
|
@@ -24687,7 +25246,7 @@ function repoBasename2(repo) {
|
|
|
24687
25246
|
}
|
|
24688
25247
|
|
|
24689
25248
|
// src/tui/component/top-bar.tsx
|
|
24690
|
-
import { spawnSync as
|
|
25249
|
+
import { spawnSync as spawnSync13 } from "child_process";
|
|
24691
25250
|
import { TextAttributes as TextAttributes18 } from "@opentui/core";
|
|
24692
25251
|
function TopBar(props) {
|
|
24693
25252
|
const {
|
|
@@ -24716,7 +25275,7 @@ function TopBar(props) {
|
|
|
24716
25275
|
`);
|
|
24717
25276
|
process.stderr.write(`running: ${UPDATE_COMMAND}
|
|
24718
25277
|
`);
|
|
24719
|
-
const result =
|
|
25278
|
+
const result = spawnSync13("sh", ["-c", UPDATE_COMMAND], {
|
|
24720
25279
|
stdio: "inherit"
|
|
24721
25280
|
});
|
|
24722
25281
|
if (result.error) {
|
|
@@ -25435,6 +25994,7 @@ async function launchTaskTmux(opts) {
|
|
|
25435
25994
|
command: opts.command,
|
|
25436
25995
|
taskId: opts.taskId,
|
|
25437
25996
|
vendor: opts.vendor,
|
|
25997
|
+
remoteKey: opts.repo && isRemoteRepoKey(opts.repo) ? opts.repo : undefined,
|
|
25438
25998
|
initScript: init2.initScript,
|
|
25439
25999
|
initPrompt: init2.initPrompt
|
|
25440
26000
|
});
|
|
@@ -25535,6 +26095,7 @@ var init_fullscreen = __esm(() => {
|
|
|
25535
26095
|
init_solid();
|
|
25536
26096
|
init_dev();
|
|
25537
26097
|
init_repo_init();
|
|
26098
|
+
init_repos();
|
|
25538
26099
|
init_theme2();
|
|
25539
26100
|
init_keymap();
|
|
25540
26101
|
init_dialog();
|
|
@@ -26214,19 +26775,27 @@ function topLevelUsage() {
|
|
|
26214
26775
|
}
|
|
26215
26776
|
|
|
26216
26777
|
// src/cli/index.ts
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
process.stdout.write(`Usage: kobe add [path]
|
|
26778
|
+
var ADD_USAGE = `Usage: kobe add [path]
|
|
26779
|
+
` + ` kobe add --remote --host <host> --user <user> --path <basePath> [--port N] [--key <path> | --password]
|
|
26220
26780
|
|
|
26221
|
-
Save a repo
|
|
26222
|
-
`
|
|
26781
|
+
` + `Save a repo for the new-task picker. With --remote, register an SSH-backed
|
|
26782
|
+
` + `project whose worktrees + engine run on <host> under <basePath>.
|
|
26783
|
+
`;
|
|
26784
|
+
async function runAddSubcommand(rest) {
|
|
26785
|
+
const arg = rest[0];
|
|
26786
|
+
if (arg === "--help" || arg === "-h" || arg === "help") {
|
|
26787
|
+
process.stdout.write(ADD_USAGE);
|
|
26788
|
+
return;
|
|
26789
|
+
}
|
|
26790
|
+
if (arg === "--remote") {
|
|
26791
|
+
const { runAddRemote: runAddRemote2 } = await Promise.resolve().then(() => (init_add_remote(), exports_add_remote));
|
|
26792
|
+
await runAddRemote2(rest.slice(1));
|
|
26223
26793
|
return;
|
|
26224
26794
|
}
|
|
26225
26795
|
if (arg?.startsWith("-")) {
|
|
26226
26796
|
process.stderr.write(`kobe add: unknown flag "${arg}"
|
|
26227
26797
|
|
|
26228
|
-
|
|
26229
|
-
`);
|
|
26798
|
+
${ADD_USAGE}`);
|
|
26230
26799
|
process.exit(2);
|
|
26231
26800
|
}
|
|
26232
26801
|
const target = resolve10(process.cwd(), arg && arg.length > 0 ? arg : ".");
|
|
@@ -26414,7 +26983,7 @@ async function main() {
|
|
|
26414
26983
|
return;
|
|
26415
26984
|
}
|
|
26416
26985
|
if (subcommand === "add") {
|
|
26417
|
-
await runAddSubcommand(rest
|
|
26986
|
+
await runAddSubcommand(rest);
|
|
26418
26987
|
return;
|
|
26419
26988
|
}
|
|
26420
26989
|
if (subcommand === "adopt") {
|