@sma1lboy/kobe 0.5.12 → 0.5.13
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/bin/kobed.js +88 -30
- package/dist/cli/index.js +426 -221
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4494,11 +4494,11 @@ var init_theme = __esm(() => {
|
|
|
4494
4494
|
});
|
|
4495
4495
|
|
|
4496
4496
|
// src/tui/panes/filetree/git.ts
|
|
4497
|
-
import {
|
|
4498
|
-
function runGit(args, cwd) {
|
|
4497
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
4498
|
+
async function runGit(args, cwd) {
|
|
4499
4499
|
if (!cwd)
|
|
4500
4500
|
throw new Error("git(): cwd is required");
|
|
4501
|
-
const result = gitWrapper.
|
|
4501
|
+
const result = await gitWrapper.spawn(args, cwd);
|
|
4502
4502
|
const exitCode = result.status ?? -1;
|
|
4503
4503
|
if (exitCode !== 0) {
|
|
4504
4504
|
const stderr = (result.stderr ?? "").trim();
|
|
@@ -4508,7 +4508,7 @@ function runGit(args, cwd) {
|
|
|
4508
4508
|
return result.stdout ?? "";
|
|
4509
4509
|
}
|
|
4510
4510
|
async function listFiles(worktreePath) {
|
|
4511
|
-
const out = runGit(["ls-files", "--cached", "--others", "--exclude-standard", "--full-name"], worktreePath);
|
|
4511
|
+
const out = await runGit(["ls-files", "--cached", "--others", "--exclude-standard", "--full-name"], worktreePath);
|
|
4512
4512
|
const lines = out.split(`
|
|
4513
4513
|
`).map((l) => l.replace(/\r$/, ""));
|
|
4514
4514
|
const set = new Set;
|
|
@@ -4519,11 +4519,11 @@ async function listFiles(worktreePath) {
|
|
|
4519
4519
|
return Array.from(set).sort();
|
|
4520
4520
|
}
|
|
4521
4521
|
async function statusFiles(worktreePath) {
|
|
4522
|
-
const out = runGit(["status", "--porcelain"], worktreePath);
|
|
4522
|
+
const out = await runGit(["status", "--porcelain"], worktreePath);
|
|
4523
4523
|
const entries = parsePorcelain(out);
|
|
4524
4524
|
let stats = null;
|
|
4525
4525
|
try {
|
|
4526
|
-
const diffOut = runGit(["diff", "--no-color", "--numstat", "HEAD"], worktreePath);
|
|
4526
|
+
const diffOut = await runGit(["diff", "--no-color", "--numstat", "HEAD"], worktreePath);
|
|
4527
4527
|
stats = new Map(parseNumstat(diffOut).map((n) => [n.path, { added: n.added, deleted: n.deleted }]));
|
|
4528
4528
|
} catch {
|
|
4529
4529
|
stats = new Map;
|
|
@@ -4642,11 +4642,27 @@ function parsePorcelain(raw) {
|
|
|
4642
4642
|
var gitWrapper;
|
|
4643
4643
|
var init_git = __esm(() => {
|
|
4644
4644
|
gitWrapper = {
|
|
4645
|
-
|
|
4646
|
-
return
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4645
|
+
spawn(args, cwd) {
|
|
4646
|
+
return new Promise((resolve, reject) => {
|
|
4647
|
+
const child = nodeSpawn("git", [...args], {
|
|
4648
|
+
cwd,
|
|
4649
|
+
shell: false,
|
|
4650
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4651
|
+
});
|
|
4652
|
+
let stdout = "";
|
|
4653
|
+
let stderr = "";
|
|
4654
|
+
child.stdout.setEncoding("utf8");
|
|
4655
|
+
child.stderr.setEncoding("utf8");
|
|
4656
|
+
child.stdout.on("data", (chunk) => {
|
|
4657
|
+
stdout += chunk;
|
|
4658
|
+
});
|
|
4659
|
+
child.stderr.on("data", (chunk) => {
|
|
4660
|
+
stderr += chunk;
|
|
4661
|
+
});
|
|
4662
|
+
child.on("error", reject);
|
|
4663
|
+
child.on("close", (status, signal) => {
|
|
4664
|
+
resolve({ stdout, stderr, status, signal });
|
|
4665
|
+
});
|
|
4650
4666
|
});
|
|
4651
4667
|
}
|
|
4652
4668
|
};
|
|
@@ -5685,7 +5701,9 @@ function FileTree(props) {
|
|
|
5685
5701
|
const [changes, setChanges] = createSignal(null);
|
|
5686
5702
|
const [error, setError] = createSignal(null);
|
|
5687
5703
|
const [expandedDirs, setExpandedDirs] = createSignal(new Set);
|
|
5704
|
+
let fetchSeq = 0;
|
|
5688
5705
|
async function refetch(currentTab, path) {
|
|
5706
|
+
const seq = ++fetchSeq;
|
|
5689
5707
|
if (path == null) {
|
|
5690
5708
|
setAllFiles(null);
|
|
5691
5709
|
setChanges(null);
|
|
@@ -5696,14 +5714,19 @@ function FileTree(props) {
|
|
|
5696
5714
|
try {
|
|
5697
5715
|
if (currentTab === "all") {
|
|
5698
5716
|
const files = await listFiles(path);
|
|
5717
|
+
if (seq !== fetchSeq || props.worktreePath() !== path)
|
|
5718
|
+
return;
|
|
5699
5719
|
setAllFiles(files);
|
|
5700
5720
|
} else if (currentTab === "changes") {
|
|
5701
5721
|
const entries = await statusFiles(path);
|
|
5722
|
+
if (seq !== fetchSeq || props.worktreePath() !== path)
|
|
5723
|
+
return;
|
|
5702
5724
|
setChanges(entries);
|
|
5703
5725
|
}
|
|
5704
5726
|
} catch (err) {
|
|
5705
5727
|
const message = err instanceof Error ? err.message : String(err);
|
|
5706
|
-
|
|
5728
|
+
if (seq === fetchSeq && props.worktreePath() === path)
|
|
5729
|
+
setError(message);
|
|
5707
5730
|
}
|
|
5708
5731
|
}
|
|
5709
5732
|
createEffect(on(props.worktreePath, async (path) => {
|
|
@@ -5717,6 +5740,8 @@ function FileTree(props) {
|
|
|
5717
5740
|
createEffect(on(props.worktreePath, (path) => {
|
|
5718
5741
|
if (path == null)
|
|
5719
5742
|
return;
|
|
5743
|
+
if (process.env.KOBE_FILETREE_WATCH !== "1")
|
|
5744
|
+
return;
|
|
5720
5745
|
let debounceTimer = null;
|
|
5721
5746
|
let watcher = null;
|
|
5722
5747
|
try {
|
|
@@ -5735,7 +5760,7 @@ function FileTree(props) {
|
|
|
5735
5760
|
debounceTimer = setTimeout(() => {
|
|
5736
5761
|
debounceTimer = null;
|
|
5737
5762
|
setRefreshTick((n) => n + 1);
|
|
5738
|
-
},
|
|
5763
|
+
}, 500);
|
|
5739
5764
|
});
|
|
5740
5765
|
watcher.on("error", () => {});
|
|
5741
5766
|
} catch {}
|
|
@@ -6436,18 +6461,62 @@ var init_state = __esm(() => {
|
|
|
6436
6461
|
});
|
|
6437
6462
|
|
|
6438
6463
|
// src/tui/panes/preview/diff.ts
|
|
6439
|
-
import {
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
shell: false
|
|
6445
|
-
});
|
|
6464
|
+
import { spawn } from "child_process";
|
|
6465
|
+
import { open } from "fs/promises";
|
|
6466
|
+
import path2 from "path";
|
|
6467
|
+
async function resolveGitToplevel(cwd) {
|
|
6468
|
+
const r = await runProcess("git", ["rev-parse", "--show-toplevel"], cwd, 64 * 1024);
|
|
6446
6469
|
if (r.status !== 0)
|
|
6447
6470
|
return cwd;
|
|
6448
|
-
const top =
|
|
6471
|
+
const top = r.stdout.trim();
|
|
6449
6472
|
return top || cwd;
|
|
6450
6473
|
}
|
|
6474
|
+
function runProcess(command, args, cwd, maxBytes) {
|
|
6475
|
+
return new Promise((resolve, reject) => {
|
|
6476
|
+
const child = spawn(command, [...args], {
|
|
6477
|
+
cwd,
|
|
6478
|
+
shell: false,
|
|
6479
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6480
|
+
});
|
|
6481
|
+
const stdoutChunks = [];
|
|
6482
|
+
const stderrChunks = [];
|
|
6483
|
+
let stdoutBytes = 0;
|
|
6484
|
+
let stderrBytes = 0;
|
|
6485
|
+
let truncated = false;
|
|
6486
|
+
function pushBounded(chunks, chunk, currentBytes) {
|
|
6487
|
+
const remaining = Math.max(0, maxBytes + 1 - currentBytes);
|
|
6488
|
+
if (remaining <= 0) {
|
|
6489
|
+
truncated = true;
|
|
6490
|
+
return currentBytes + chunk.length;
|
|
6491
|
+
}
|
|
6492
|
+
if (chunk.length > remaining) {
|
|
6493
|
+
chunks.push(chunk.subarray(0, remaining));
|
|
6494
|
+
truncated = true;
|
|
6495
|
+
} else {
|
|
6496
|
+
chunks.push(chunk);
|
|
6497
|
+
}
|
|
6498
|
+
return currentBytes + chunk.length;
|
|
6499
|
+
}
|
|
6500
|
+
child.stdout.on("data", (chunk) => {
|
|
6501
|
+
stdoutBytes = pushBounded(stdoutChunks, chunk, stdoutBytes);
|
|
6502
|
+
if (stdoutBytes > maxBytes)
|
|
6503
|
+
child.kill("SIGTERM");
|
|
6504
|
+
});
|
|
6505
|
+
child.stderr.on("data", (chunk) => {
|
|
6506
|
+
stderrBytes = pushBounded(stderrChunks, chunk, stderrBytes);
|
|
6507
|
+
});
|
|
6508
|
+
child.on("error", reject);
|
|
6509
|
+
child.on("close", (status, signal) => {
|
|
6510
|
+
resolve({
|
|
6511
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
6512
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
6513
|
+
status,
|
|
6514
|
+
signal,
|
|
6515
|
+
truncated
|
|
6516
|
+
});
|
|
6517
|
+
});
|
|
6518
|
+
});
|
|
6519
|
+
}
|
|
6451
6520
|
async function readFile(worktreePath, relPath) {
|
|
6452
6521
|
if (!worktreePath)
|
|
6453
6522
|
return { ok: false, error: "no worktree path" };
|
|
@@ -6456,21 +6525,22 @@ async function readFile(worktreePath, relPath) {
|
|
|
6456
6525
|
if (relPath.split("/").includes("..")) {
|
|
6457
6526
|
return { ok: false, error: "path escapes worktree" };
|
|
6458
6527
|
}
|
|
6459
|
-
const
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
const
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
return { ok:
|
|
6528
|
+
const top = await resolveGitToplevel(worktreePath);
|
|
6529
|
+
const absPath = path2.join(top, relPath);
|
|
6530
|
+
let handle = null;
|
|
6531
|
+
try {
|
|
6532
|
+
handle = await open(absPath, "r");
|
|
6533
|
+
const buf = Buffer.allocUnsafe(MAX_BYTES + 1);
|
|
6534
|
+
const { bytesRead } = await handle.read(buf, 0, MAX_BYTES + 1, 0);
|
|
6535
|
+
const truncated = bytesRead > MAX_BYTES;
|
|
6536
|
+
const raw = buf.subarray(0, Math.min(bytesRead, MAX_BYTES)).toString("utf8");
|
|
6537
|
+
return { ok: true, text: truncated ? raw + TRUNCATED_BANNER : raw, truncated };
|
|
6538
|
+
} catch (err) {
|
|
6539
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6540
|
+
return { ok: false, error: msg };
|
|
6541
|
+
} finally {
|
|
6542
|
+
await handle?.close().catch(() => {});
|
|
6472
6543
|
}
|
|
6473
|
-
return { ok: true, text: raw, truncated: false };
|
|
6474
6544
|
}
|
|
6475
6545
|
async function readDiff(worktreePath, base, relPath) {
|
|
6476
6546
|
if (!worktreePath)
|
|
@@ -6482,31 +6552,22 @@ async function readDiff(worktreePath, base, relPath) {
|
|
|
6482
6552
|
if (relPath.split("/").includes("..")) {
|
|
6483
6553
|
return { ok: false, error: "path escapes worktree" };
|
|
6484
6554
|
}
|
|
6485
|
-
const proc =
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
shell: false,
|
|
6489
|
-
maxBuffer: MAX_BYTES + 4096
|
|
6490
|
-
});
|
|
6491
|
-
if (proc.status !== 0) {
|
|
6492
|
-
const err = (proc.stderr ?? "").trim() || `git diff exited ${proc.status}`;
|
|
6555
|
+
const proc = await runProcess("git", ["diff", "--no-color", base, "--", relPath], await resolveGitToplevel(worktreePath), MAX_BYTES);
|
|
6556
|
+
if (!proc.truncated && proc.status !== 0) {
|
|
6557
|
+
const err = (proc.stderr ?? "").trim() || `git diff exited ${proc.status ?? proc.signal ?? "unknown"}`;
|
|
6493
6558
|
return { ok: false, error: err };
|
|
6494
6559
|
}
|
|
6495
|
-
const raw = proc.stdout
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6560
|
+
const raw = proc.stdout;
|
|
6561
|
+
return {
|
|
6562
|
+
ok: true,
|
|
6563
|
+
text: proc.truncated ? raw.slice(0, MAX_BYTES) + TRUNCATED_BANNER : raw,
|
|
6564
|
+
truncated: proc.truncated
|
|
6565
|
+
};
|
|
6500
6566
|
}
|
|
6501
6567
|
async function isPathChanged(worktreePath, relPath) {
|
|
6502
6568
|
if (!worktreePath || !relPath)
|
|
6503
6569
|
return false;
|
|
6504
|
-
const proc =
|
|
6505
|
-
cwd: resolveGitToplevel(worktreePath),
|
|
6506
|
-
encoding: "utf8",
|
|
6507
|
-
shell: false,
|
|
6508
|
-
maxBuffer: 64 * 1024
|
|
6509
|
-
});
|
|
6570
|
+
const proc = await runProcess("git", ["status", "--porcelain", "--", relPath], await resolveGitToplevel(worktreePath), 64 * 1024);
|
|
6510
6571
|
if (proc.status !== 0)
|
|
6511
6572
|
return false;
|
|
6512
6573
|
return (proc.stdout ?? "").trim().length > 0;
|
|
@@ -7533,7 +7594,7 @@ var init_keys3 = __esm(() => {
|
|
|
7533
7594
|
});
|
|
7534
7595
|
|
|
7535
7596
|
// src/tui/panes/terminal/pty.ts
|
|
7536
|
-
import { spawn, spawnSync as
|
|
7597
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
7537
7598
|
function defaultShell() {
|
|
7538
7599
|
return process.env.SHELL ?? "/bin/bash";
|
|
7539
7600
|
}
|
|
@@ -7543,7 +7604,7 @@ function defaultTmuxBin() {
|
|
|
7543
7604
|
function assertTmuxAvailable(tmuxBin2) {
|
|
7544
7605
|
let result;
|
|
7545
7606
|
try {
|
|
7546
|
-
result =
|
|
7607
|
+
result = spawnSync2(tmuxBin2, ["-V"], { encoding: "utf8" });
|
|
7547
7608
|
} catch (err) {
|
|
7548
7609
|
throw new Error(`kobe terminal pane requires tmux on PATH. Failed to spawn '${tmuxBin2}': ${err.message}.
|
|
7549
7610
|
Install tmux (e.g. 'brew install tmux') or set KOBE_TMUX_BIN to its path.`);
|
|
@@ -7561,7 +7622,7 @@ function sessionNameFor(taskId) {
|
|
|
7561
7622
|
return `${SESSION_NAME_PREFIX}${safe}`;
|
|
7562
7623
|
}
|
|
7563
7624
|
function tmuxSync(tmuxBin2, args) {
|
|
7564
|
-
const result =
|
|
7625
|
+
const result = spawnSync2(tmuxBin2, args, { encoding: "utf8" });
|
|
7565
7626
|
if (result.error) {
|
|
7566
7627
|
throw new Error(`${tmuxBin2} ${args.join(" ")}: ${result.error.message}`);
|
|
7567
7628
|
}
|
|
@@ -7577,7 +7638,7 @@ class TmuxControlClient {
|
|
|
7577
7638
|
buffer = "";
|
|
7578
7639
|
_killed = false;
|
|
7579
7640
|
constructor(tmuxBin2, sessionName) {
|
|
7580
|
-
this.proc =
|
|
7641
|
+
this.proc = spawn2(tmuxBin2, ["-CC", "attach-session", "-t", sessionName], {
|
|
7581
7642
|
stdio: ["pipe", "pipe", "ignore"]
|
|
7582
7643
|
});
|
|
7583
7644
|
this.proc.stdout?.setEncoding("utf8");
|
|
@@ -7677,7 +7738,7 @@ class TmuxTaskPty {
|
|
|
7677
7738
|
this.sessionName = sessionNameFor(opts.taskId);
|
|
7678
7739
|
assertTmuxAvailable(this.tmuxBin);
|
|
7679
7740
|
const shell = opts.shell ?? defaultShell();
|
|
7680
|
-
const has =
|
|
7741
|
+
const has = spawnSync2(this.tmuxBin, ["has-session", "-t", this.sessionName], { encoding: "utf8" });
|
|
7681
7742
|
let existed = has.status === 0;
|
|
7682
7743
|
if (existed) {
|
|
7683
7744
|
try {
|
|
@@ -7800,7 +7861,7 @@ class TmuxTaskPty {
|
|
|
7800
7861
|
for (const b of slice)
|
|
7801
7862
|
hexArgs.push(b.toString(16).padStart(2, "0"));
|
|
7802
7863
|
await new Promise((resolve) => {
|
|
7803
|
-
const proc =
|
|
7864
|
+
const proc = spawn2(this.tmuxBin, ["send-keys", "-t", this.sessionName, "-H", ...hexArgs], {
|
|
7804
7865
|
stdio: ["ignore", "ignore", "ignore"]
|
|
7805
7866
|
});
|
|
7806
7867
|
proc.on("error", () => resolve());
|
|
@@ -7884,7 +7945,7 @@ class TmuxTaskPty {
|
|
|
7884
7945
|
return;
|
|
7885
7946
|
this.markDead();
|
|
7886
7947
|
try {
|
|
7887
|
-
const result =
|
|
7948
|
+
const result = spawnSync2(this.tmuxBin, ["kill-session", "-t", this.sessionName], { encoding: "utf8" });
|
|
7888
7949
|
} catch {}
|
|
7889
7950
|
}
|
|
7890
7951
|
markDead() {
|
|
@@ -8901,10 +8962,10 @@ var init_terminal_host = __esm(() => {
|
|
|
8901
8962
|
});
|
|
8902
8963
|
|
|
8903
8964
|
// src/engine/claude-code-local/binary.ts
|
|
8904
|
-
import { spawnSync as
|
|
8965
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
8905
8966
|
import { existsSync, statSync } from "fs";
|
|
8906
8967
|
import { homedir as homedir2 } from "os";
|
|
8907
|
-
import
|
|
8968
|
+
import path3 from "path";
|
|
8908
8969
|
async function findClaudeBinary(deps = defaultDeps) {
|
|
8909
8970
|
const checked = [];
|
|
8910
8971
|
const tryPath = (p2) => {
|
|
@@ -8920,19 +8981,19 @@ async function findClaudeBinary(deps = defaultDeps) {
|
|
|
8920
8981
|
return whichResult;
|
|
8921
8982
|
}
|
|
8922
8983
|
const home = deps.home();
|
|
8923
|
-
const localInstall = tryPath(
|
|
8984
|
+
const localInstall = tryPath(path3.join(home, ".claude", "local", "claude"));
|
|
8924
8985
|
if (localInstall)
|
|
8925
8986
|
return localInstall;
|
|
8926
8987
|
const nvmBin = deps.env("NVM_BIN");
|
|
8927
8988
|
if (nvmBin) {
|
|
8928
|
-
const candidate = tryPath(
|
|
8989
|
+
const candidate = tryPath(path3.join(nvmBin, "claude"));
|
|
8929
8990
|
if (candidate)
|
|
8930
8991
|
return candidate;
|
|
8931
8992
|
}
|
|
8932
|
-
const nvmRoot =
|
|
8993
|
+
const nvmRoot = path3.join(home, ".nvm", "versions", "node");
|
|
8933
8994
|
const nvmVersions = deps.readdir(nvmRoot).sort().reverse();
|
|
8934
8995
|
for (const v2 of nvmVersions) {
|
|
8935
|
-
const candidate = tryPath(
|
|
8996
|
+
const candidate = tryPath(path3.join(nvmRoot, v2, "bin", "claude"));
|
|
8936
8997
|
if (candidate)
|
|
8937
8998
|
return candidate;
|
|
8938
8999
|
}
|
|
@@ -8948,7 +9009,7 @@ async function findClaudeBinary(deps = defaultDeps) {
|
|
|
8948
9009
|
".bun/bin/claude",
|
|
8949
9010
|
"bin/claude"
|
|
8950
9011
|
]) {
|
|
8951
|
-
const candidate = tryPath(
|
|
9012
|
+
const candidate = tryPath(path3.join(home, rel));
|
|
8952
9013
|
if (candidate)
|
|
8953
9014
|
return candidate;
|
|
8954
9015
|
}
|
|
@@ -8980,7 +9041,7 @@ var init_binary = __esm(() => {
|
|
|
8980
9041
|
},
|
|
8981
9042
|
which(name) {
|
|
8982
9043
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
8983
|
-
const out =
|
|
9044
|
+
const out = spawnSync3(cmd, [name], { encoding: "utf8" });
|
|
8984
9045
|
if (out.status !== 0)
|
|
8985
9046
|
return;
|
|
8986
9047
|
const first = out.stdout.split(`
|
|
@@ -9074,7 +9135,7 @@ var init_ulid = __esm(() => {
|
|
|
9074
9135
|
});
|
|
9075
9136
|
|
|
9076
9137
|
// src/orchestrator/index/store.ts
|
|
9077
|
-
import { mkdir, open, readFile as readFile2, rename, unlink } from "fs/promises";
|
|
9138
|
+
import { mkdir, open as open2, readFile as readFile2, rename, unlink } from "fs/promises";
|
|
9078
9139
|
import { homedir as homedir3 } from "os";
|
|
9079
9140
|
import { dirname as dirname2, join as join2 } from "path";
|
|
9080
9141
|
|
|
@@ -9152,7 +9213,7 @@ class TaskIndexStore {
|
|
|
9152
9213
|
const payload = this.snapshot();
|
|
9153
9214
|
const json = `${JSON.stringify(payload, null, 2)}
|
|
9154
9215
|
`;
|
|
9155
|
-
const handle = await
|
|
9216
|
+
const handle = await open2(this.tmpPath, "w", 420);
|
|
9156
9217
|
try {
|
|
9157
9218
|
await handle.writeFile(json, "utf8");
|
|
9158
9219
|
await handle.sync();
|
|
@@ -9392,32 +9453,32 @@ var init_store = __esm(() => {
|
|
|
9392
9453
|
|
|
9393
9454
|
// src/orchestrator/worktree/paths.ts
|
|
9394
9455
|
import fs2 from "fs";
|
|
9395
|
-
import
|
|
9456
|
+
import path4 from "path";
|
|
9396
9457
|
function worktreeRootFor(repo) {
|
|
9397
|
-
if (!
|
|
9458
|
+
if (!path4.isAbsolute(repo)) {
|
|
9398
9459
|
throw new Error(`worktreeRootFor: repo must be an absolute path, got: ${repo}`);
|
|
9399
9460
|
}
|
|
9400
|
-
return
|
|
9461
|
+
return path4.join(repo, KOBE_WORKTREE_ROOT_SUBPATH);
|
|
9401
9462
|
}
|
|
9402
9463
|
function worktreePathFor(repo, taskId) {
|
|
9403
9464
|
if (!taskId || /[/\\\0]/.test(taskId)) {
|
|
9404
9465
|
throw new Error(`worktreePathFor: invalid taskId: ${JSON.stringify(taskId)}`);
|
|
9405
9466
|
}
|
|
9406
|
-
return
|
|
9467
|
+
return path4.join(worktreeRootFor(repo), taskId);
|
|
9407
9468
|
}
|
|
9408
9469
|
function isKobeManagedPath(repo, candidate) {
|
|
9409
|
-
if (!
|
|
9470
|
+
if (!path4.isAbsolute(repo) || !path4.isAbsolute(candidate))
|
|
9410
9471
|
return false;
|
|
9411
9472
|
const root = canonicalize(worktreeRootFor(repo));
|
|
9412
9473
|
const target = canonicalize(candidate);
|
|
9413
|
-
const rel =
|
|
9414
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
9474
|
+
const rel = path4.relative(root, target);
|
|
9475
|
+
return rel !== "" && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
9415
9476
|
}
|
|
9416
9477
|
function canonicalize(p2) {
|
|
9417
9478
|
try {
|
|
9418
9479
|
return fs2.realpathSync(p2);
|
|
9419
9480
|
} catch {
|
|
9420
|
-
return
|
|
9481
|
+
return path4.resolve(p2);
|
|
9421
9482
|
}
|
|
9422
9483
|
}
|
|
9423
9484
|
var KOBE_WORKTREE_ROOT_SUBPATH = ".claude/worktrees";
|
|
@@ -9429,7 +9490,7 @@ var init_package = __esm(() => {
|
|
|
9429
9490
|
package_default = {
|
|
9430
9491
|
$schema: "https://json.schemastore.org/package.json",
|
|
9431
9492
|
name: "@sma1lboy/kobe",
|
|
9432
|
-
version: "0.5.
|
|
9493
|
+
version: "0.5.13",
|
|
9433
9494
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
9434
9495
|
type: "module",
|
|
9435
9496
|
packageManager: "bun@1.3.13",
|
|
@@ -9599,7 +9660,7 @@ __export(exports_diagnose, {
|
|
|
9599
9660
|
formatBytes: () => formatBytes,
|
|
9600
9661
|
buildDiagnoseReport: () => buildDiagnoseReport
|
|
9601
9662
|
});
|
|
9602
|
-
import { spawnSync as
|
|
9663
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
9603
9664
|
import { existsSync as existsSync2, readdirSync, statSync as statSync2 } from "fs";
|
|
9604
9665
|
import { stat } from "fs/promises";
|
|
9605
9666
|
import { homedir as homedir4, arch as osArch, platform as osPlatform, release as osRelease } from "os";
|
|
@@ -9670,12 +9731,12 @@ function reconcileWorktrees(tasks, onDiskByRepo) {
|
|
|
9670
9731
|
}
|
|
9671
9732
|
function probeVersion(bin) {
|
|
9672
9733
|
try {
|
|
9673
|
-
const out =
|
|
9734
|
+
const out = spawnSync4(bin, ["--version"], {
|
|
9674
9735
|
encoding: "utf8",
|
|
9675
9736
|
timeout: 3000
|
|
9676
9737
|
});
|
|
9677
9738
|
if (out.status !== 0) {
|
|
9678
|
-
const alt =
|
|
9739
|
+
const alt = spawnSync4(bin, ["-V"], { encoding: "utf8", timeout: 3000 });
|
|
9679
9740
|
if (alt.status !== 0)
|
|
9680
9741
|
return null;
|
|
9681
9742
|
return alt.stdout.trim() || alt.stderr.trim() || null;
|
|
@@ -9733,9 +9794,9 @@ function listWorktreeDirs(root) {
|
|
|
9733
9794
|
return [];
|
|
9734
9795
|
}
|
|
9735
9796
|
}
|
|
9736
|
-
function tailLines(
|
|
9797
|
+
function tailLines(path5, n2) {
|
|
9737
9798
|
try {
|
|
9738
|
-
const text = __require("fs").readFileSync(
|
|
9799
|
+
const text = __require("fs").readFileSync(path5, "utf8");
|
|
9739
9800
|
const lines = text.split(`
|
|
9740
9801
|
`).filter((l2) => l2.length > 0);
|
|
9741
9802
|
return lines.slice(-n2);
|
|
@@ -9786,7 +9847,7 @@ async function buildDiagnoseReport() {
|
|
|
9786
9847
|
const tmux = tmuxBin();
|
|
9787
9848
|
let tmuxPath = null;
|
|
9788
9849
|
try {
|
|
9789
|
-
const out =
|
|
9850
|
+
const out = spawnSync4(process.platform === "win32" ? "where" : "which", [tmux], {
|
|
9790
9851
|
encoding: "utf8",
|
|
9791
9852
|
timeout: 3000
|
|
9792
9853
|
});
|
|
@@ -9924,7 +9985,7 @@ __export(exports_update, {
|
|
|
9924
9985
|
runUpdateSubcommand: () => runUpdateSubcommand,
|
|
9925
9986
|
parseUpdateArgs: () => parseUpdateArgs
|
|
9926
9987
|
});
|
|
9927
|
-
import { spawnSync as
|
|
9988
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
9928
9989
|
function fail(message) {
|
|
9929
9990
|
process.stderr.write(`kobe update: ${message}
|
|
9930
9991
|
`);
|
|
@@ -9975,7 +10036,7 @@ function printUsage(out) {
|
|
|
9975
10036
|
}
|
|
9976
10037
|
async function runUpdateSubcommand(args, deps) {
|
|
9977
10038
|
const io = {
|
|
9978
|
-
spawn: deps?.spawn ??
|
|
10039
|
+
spawn: deps?.spawn ?? spawnSync5,
|
|
9979
10040
|
stdout: deps?.stdout ?? process.stdout,
|
|
9980
10041
|
stderr: deps?.stderr ?? process.stderr,
|
|
9981
10042
|
exit: deps?.exit ?? ((code) => process.exit(code))
|
|
@@ -10072,19 +10133,19 @@ function loadUserThemes() {
|
|
|
10072
10133
|
for (const file of entries) {
|
|
10073
10134
|
if (!file.endsWith(".json"))
|
|
10074
10135
|
continue;
|
|
10075
|
-
const
|
|
10136
|
+
const path5 = join4(dir, file);
|
|
10076
10137
|
let parsed;
|
|
10077
10138
|
try {
|
|
10078
|
-
const text = readFileSync2(
|
|
10139
|
+
const text = readFileSync2(path5, "utf8");
|
|
10079
10140
|
parsed = JSON.parse(text);
|
|
10080
10141
|
} catch (err) {
|
|
10081
10142
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10082
|
-
console.warn(`[kobe] skipping user theme ${
|
|
10143
|
+
console.warn(`[kobe] skipping user theme ${path5}: invalid JSON \u2014 ${msg}`);
|
|
10083
10144
|
continue;
|
|
10084
10145
|
}
|
|
10085
10146
|
const result = validateTheme(parsed);
|
|
10086
10147
|
if (!result.ok) {
|
|
10087
|
-
console.warn(`[kobe] skipping user theme ${
|
|
10148
|
+
console.warn(`[kobe] skipping user theme ${path5}: ${result.reason}`);
|
|
10088
10149
|
continue;
|
|
10089
10150
|
}
|
|
10090
10151
|
const name = file.slice(0, -".json".length);
|
|
@@ -10127,9 +10188,9 @@ function listThemes2() {
|
|
|
10127
10188
|
} else {
|
|
10128
10189
|
for (const f2 of userFiles) {
|
|
10129
10190
|
const name = f2.slice(0, -".json".length);
|
|
10130
|
-
const
|
|
10191
|
+
const path5 = join5(dir, f2);
|
|
10131
10192
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
10132
|
-
lines.push(` ${name}${overridesBundled} ${
|
|
10193
|
+
lines.push(` ${name}${overridesBundled} ${path5}`);
|
|
10133
10194
|
}
|
|
10134
10195
|
}
|
|
10135
10196
|
process.stdout.write(`${lines.join(`
|
|
@@ -10312,8 +10373,8 @@ class SocketClient {
|
|
|
10312
10373
|
nextId = 1;
|
|
10313
10374
|
pending = new Map;
|
|
10314
10375
|
connected;
|
|
10315
|
-
constructor(
|
|
10316
|
-
this.socket = connect(
|
|
10376
|
+
constructor(path5) {
|
|
10377
|
+
this.socket = connect(path5);
|
|
10317
10378
|
this.connected = new Promise((resolve2, reject) => {
|
|
10318
10379
|
this.socket.once("connect", resolve2);
|
|
10319
10380
|
this.socket.once("error", reject);
|
|
@@ -10611,30 +10672,36 @@ class KobeDaemonClient {
|
|
|
10611
10672
|
nextId = 1;
|
|
10612
10673
|
pending = new Map;
|
|
10613
10674
|
handlers = new Map;
|
|
10675
|
+
lifecycleHandlers = new Map;
|
|
10676
|
+
connecting = null;
|
|
10677
|
+
disposed = false;
|
|
10614
10678
|
constructor(socketPath) {
|
|
10615
10679
|
this.socketPath = socketPath;
|
|
10616
10680
|
}
|
|
10617
10681
|
connect() {
|
|
10618
10682
|
if (this.socket)
|
|
10619
10683
|
return Promise.resolve();
|
|
10620
|
-
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
10627
|
-
|
|
10628
|
-
|
|
10629
|
-
|
|
10630
|
-
|
|
10631
|
-
|
|
10632
|
-
});
|
|
10684
|
+
if (this.disposed)
|
|
10685
|
+
return Promise.reject(new Error("daemon client disposed"));
|
|
10686
|
+
if (this.connecting)
|
|
10687
|
+
return this.connecting;
|
|
10688
|
+
const p2 = this.openSocket();
|
|
10689
|
+
this.connecting = p2;
|
|
10690
|
+
const cleanup = () => {
|
|
10691
|
+
if (this.connecting === p2)
|
|
10692
|
+
this.connecting = null;
|
|
10693
|
+
};
|
|
10694
|
+
p2.then(cleanup, cleanup);
|
|
10695
|
+
return p2;
|
|
10633
10696
|
}
|
|
10634
10697
|
close() {
|
|
10698
|
+
this.disposed = true;
|
|
10635
10699
|
this.socket?.end();
|
|
10636
10700
|
this.socket = null;
|
|
10637
10701
|
}
|
|
10702
|
+
forceDisconnect() {
|
|
10703
|
+
this.socket?.destroy();
|
|
10704
|
+
}
|
|
10638
10705
|
on(name, handler) {
|
|
10639
10706
|
let set = this.handlers.get(name);
|
|
10640
10707
|
if (!set) {
|
|
@@ -10648,6 +10715,19 @@ class KobeDaemonClient {
|
|
|
10648
10715
|
this.handlers.delete(name);
|
|
10649
10716
|
};
|
|
10650
10717
|
}
|
|
10718
|
+
onLifecycle(name, handler) {
|
|
10719
|
+
let set = this.lifecycleHandlers.get(name);
|
|
10720
|
+
if (!set) {
|
|
10721
|
+
set = new Set;
|
|
10722
|
+
this.lifecycleHandlers.set(name, set);
|
|
10723
|
+
}
|
|
10724
|
+
set.add(handler);
|
|
10725
|
+
return () => {
|
|
10726
|
+
set?.delete(handler);
|
|
10727
|
+
if (set?.size === 0)
|
|
10728
|
+
this.lifecycleHandlers.delete(name);
|
|
10729
|
+
};
|
|
10730
|
+
}
|
|
10651
10731
|
async request(name, payload) {
|
|
10652
10732
|
await this.connect();
|
|
10653
10733
|
const socket = this.socket;
|
|
@@ -10660,6 +10740,44 @@ class KobeDaemonClient {
|
|
|
10660
10740
|
socket.write(frameToLine({ type: "request", id, name, payload }));
|
|
10661
10741
|
return promise;
|
|
10662
10742
|
}
|
|
10743
|
+
openSocket() {
|
|
10744
|
+
return new Promise((resolve2, reject) => {
|
|
10745
|
+
const socket = connect2(this.socketPath);
|
|
10746
|
+
this.socket = socket;
|
|
10747
|
+
const onConnect = () => {
|
|
10748
|
+
socket.off("error", onError);
|
|
10749
|
+
resolve2();
|
|
10750
|
+
};
|
|
10751
|
+
const onError = (err) => {
|
|
10752
|
+
socket.off("connect", onConnect);
|
|
10753
|
+
if (this.socket === socket)
|
|
10754
|
+
this.socket = null;
|
|
10755
|
+
reject(err);
|
|
10756
|
+
};
|
|
10757
|
+
socket.once("connect", onConnect);
|
|
10758
|
+
socket.once("error", onError);
|
|
10759
|
+
socket.on("data", (chunk) => this.onData(chunk.toString("utf8")));
|
|
10760
|
+
socket.on("close", () => this.onSocketClose(socket));
|
|
10761
|
+
});
|
|
10762
|
+
}
|
|
10763
|
+
onSocketClose(which) {
|
|
10764
|
+
if (this.socket !== which && this.socket !== null)
|
|
10765
|
+
return;
|
|
10766
|
+
this.socket = null;
|
|
10767
|
+
for (const pending of this.pending.values())
|
|
10768
|
+
pending.reject(new Error("daemon connection closed"));
|
|
10769
|
+
this.pending.clear();
|
|
10770
|
+
this.emitLifecycle("close");
|
|
10771
|
+
}
|
|
10772
|
+
emitLifecycle(name) {
|
|
10773
|
+
for (const handler of this.lifecycleHandlers.get(name) ?? []) {
|
|
10774
|
+
try {
|
|
10775
|
+
handler();
|
|
10776
|
+
} catch (err) {
|
|
10777
|
+
console.error(`[kobe] lifecycle handler for "${name}" threw:`, err);
|
|
10778
|
+
}
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10663
10781
|
onData(chunk) {
|
|
10664
10782
|
this.buffer += chunk;
|
|
10665
10783
|
let nl = this.buffer.indexOf(`
|
|
@@ -10700,21 +10818,20 @@ class KobeDaemonClient {
|
|
|
10700
10818
|
var init_client = () => {};
|
|
10701
10819
|
|
|
10702
10820
|
// src/client/daemon-process.ts
|
|
10703
|
-
import { spawn as
|
|
10821
|
+
import { spawn as spawn3 } from "child_process";
|
|
10704
10822
|
import { existsSync as existsSync4 } from "fs";
|
|
10705
10823
|
import { dirname as dirname3, join as join7, resolve as resolve2 } from "path";
|
|
10706
10824
|
import { fileURLToPath } from "url";
|
|
10707
|
-
async function
|
|
10825
|
+
async function ensureDaemonReachable() {
|
|
10708
10826
|
const socketPath = defaultDaemonSocketPath();
|
|
10709
|
-
|
|
10710
|
-
|
|
10711
|
-
return client;
|
|
10827
|
+
if (await testCanConnect(socketPath))
|
|
10828
|
+
return socketPath;
|
|
10712
10829
|
const { entry, runWithBun } = resolveKobedEntry();
|
|
10713
|
-
const child = runWithBun ?
|
|
10830
|
+
const child = runWithBun ? spawn3(process.execPath, [entry, "start"], {
|
|
10714
10831
|
detached: true,
|
|
10715
10832
|
stdio: "ignore",
|
|
10716
10833
|
env: process.env
|
|
10717
|
-
}) :
|
|
10834
|
+
}) : spawn3(entry, ["start"], {
|
|
10718
10835
|
detached: true,
|
|
10719
10836
|
stdio: "ignore",
|
|
10720
10837
|
env: process.env
|
|
@@ -10723,24 +10840,26 @@ async function connectOrStartDaemon() {
|
|
|
10723
10840
|
const deadline = Date.now() + 5000;
|
|
10724
10841
|
let lastErr;
|
|
10725
10842
|
while (Date.now() < deadline) {
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
return next;
|
|
10730
|
-
} catch (err) {
|
|
10731
|
-
lastErr = err;
|
|
10732
|
-
next.close();
|
|
10733
|
-
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
10734
|
-
}
|
|
10843
|
+
if (await testCanConnect(socketPath))
|
|
10844
|
+
return socketPath;
|
|
10845
|
+
await new Promise((resolveTimer) => setTimeout(resolveTimer, 100));
|
|
10735
10846
|
}
|
|
10736
|
-
throw new Error(`kobe: daemon did not start at ${socketPath}: ${lastErr instanceof Error ? lastErr.message :
|
|
10847
|
+
throw new Error(`kobe: daemon did not start at ${socketPath}: ${lastErr instanceof Error ? lastErr.message : "timeout"}`);
|
|
10737
10848
|
}
|
|
10738
|
-
async function
|
|
10849
|
+
async function connectOrStartDaemon() {
|
|
10850
|
+
const socketPath = await ensureDaemonReachable();
|
|
10851
|
+
const client = new KobeDaemonClient(socketPath);
|
|
10852
|
+
await client.connect();
|
|
10853
|
+
return client;
|
|
10854
|
+
}
|
|
10855
|
+
async function testCanConnect(socketPath) {
|
|
10856
|
+
const probe = new KobeDaemonClient(socketPath);
|
|
10739
10857
|
try {
|
|
10740
|
-
await
|
|
10858
|
+
await probe.connect();
|
|
10859
|
+
probe.close();
|
|
10741
10860
|
return true;
|
|
10742
10861
|
} catch {
|
|
10743
|
-
|
|
10862
|
+
probe.close();
|
|
10744
10863
|
return false;
|
|
10745
10864
|
}
|
|
10746
10865
|
}
|
|
@@ -10884,7 +11003,7 @@ function withTotalSpeedForTurn(usage, startedAtIso, endedAtIso) {
|
|
|
10884
11003
|
}
|
|
10885
11004
|
|
|
10886
11005
|
// src/orchestrator/metadata-suggester.ts
|
|
10887
|
-
import { spawn as
|
|
11006
|
+
import { spawn as spawn4 } from "child_process";
|
|
10888
11007
|
|
|
10889
11008
|
class MetadataSuggester {
|
|
10890
11009
|
binaryPromise = null;
|
|
@@ -10913,7 +11032,7 @@ class MetadataSuggester {
|
|
|
10913
11032
|
return new Promise((resolve3) => {
|
|
10914
11033
|
let proc;
|
|
10915
11034
|
try {
|
|
10916
|
-
proc =
|
|
11035
|
+
proc = spawn4(binary, ["-p", builder(trimmed)], {
|
|
10917
11036
|
stdio: ["ignore", "pipe", "ignore"],
|
|
10918
11037
|
env: process.env
|
|
10919
11038
|
});
|
|
@@ -11062,10 +11181,10 @@ class InMemoryPendingInputBroker {
|
|
|
11062
11181
|
}
|
|
11063
11182
|
|
|
11064
11183
|
// src/orchestrator/pr/build.ts
|
|
11065
|
-
import { spawnSync as
|
|
11184
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
11066
11185
|
function git(cwd, args) {
|
|
11067
11186
|
try {
|
|
11068
|
-
const out =
|
|
11187
|
+
const out = spawnSync6("git", args.slice(), {
|
|
11069
11188
|
cwd,
|
|
11070
11189
|
encoding: "utf8",
|
|
11071
11190
|
timeout: GIT_TIMEOUT_MS
|
|
@@ -11115,7 +11234,7 @@ var init_build = () => {};
|
|
|
11115
11234
|
|
|
11116
11235
|
// src/orchestrator/pr/instructions.ts
|
|
11117
11236
|
import { promises as fs3 } from "fs";
|
|
11118
|
-
import
|
|
11237
|
+
import path5 from "path";
|
|
11119
11238
|
function dirtyCountSentence(n2) {
|
|
11120
11239
|
if (n2 <= 0)
|
|
11121
11240
|
return "There are no uncommitted changes.";
|
|
@@ -11143,7 +11262,7 @@ function renderPRInstructions(template, state) {
|
|
|
11143
11262
|
async function loadPRInstructionsTemplate(worktreePath) {
|
|
11144
11263
|
if (!worktreePath)
|
|
11145
11264
|
return DEFAULT_PR_INSTRUCTIONS_TEMPLATE;
|
|
11146
|
-
const file =
|
|
11265
|
+
const file = path5.join(worktreePath, ".kobe", "pr-instructions.md");
|
|
11147
11266
|
try {
|
|
11148
11267
|
const text = await fs3.readFile(file, "utf8");
|
|
11149
11268
|
if (text.length === 0)
|
|
@@ -12092,6 +12211,8 @@ class RemoteOrchestrator {
|
|
|
12092
12211
|
setRunState;
|
|
12093
12212
|
planUsageAcc;
|
|
12094
12213
|
setPlanUsage;
|
|
12214
|
+
connectionStateAcc;
|
|
12215
|
+
setConnectionState;
|
|
12095
12216
|
rcBridgeAcc;
|
|
12096
12217
|
setRcBridge;
|
|
12097
12218
|
subscribers = new Map;
|
|
@@ -12101,6 +12222,7 @@ class RemoteOrchestrator {
|
|
|
12101
12222
|
const [tasks, setTasks] = createSignal([]);
|
|
12102
12223
|
const [runState, setRunState] = createSignal(new Map);
|
|
12103
12224
|
const [planUsage, setPlanUsage] = createSignal(null);
|
|
12225
|
+
const [connectionState, setConnectionState] = createSignal("online");
|
|
12104
12226
|
const [rcBridge, setRcBridge] = createSignal({ state: "off" });
|
|
12105
12227
|
this.tasksAcc = tasks;
|
|
12106
12228
|
this.setTasks = (next) => setTasks(() => next);
|
|
@@ -12108,9 +12230,12 @@ class RemoteOrchestrator {
|
|
|
12108
12230
|
this.setRunState = (next) => setRunState(() => next);
|
|
12109
12231
|
this.planUsageAcc = planUsage;
|
|
12110
12232
|
this.setPlanUsage = (next) => setPlanUsage(() => next);
|
|
12233
|
+
this.connectionStateAcc = connectionState;
|
|
12234
|
+
this.setConnectionState = (next) => setConnectionState(() => next);
|
|
12111
12235
|
this.rcBridgeAcc = rcBridge;
|
|
12112
12236
|
this.setRcBridge = (next) => setRcBridge(() => next);
|
|
12113
12237
|
this.client.on("*", (frame) => this.handleEvent(frame.name, frame.payload));
|
|
12238
|
+
this.client.onLifecycle("close", () => this.setConnectionState("disconnected"));
|
|
12114
12239
|
}
|
|
12115
12240
|
async init() {
|
|
12116
12241
|
const hello = await this.client.request("hello", { clientId: `tui-${process.pid}`, version: "1" });
|
|
@@ -12153,6 +12278,17 @@ class RemoteOrchestrator {
|
|
|
12153
12278
|
} catch {}
|
|
12154
12279
|
}));
|
|
12155
12280
|
}
|
|
12281
|
+
async manualReconnect() {
|
|
12282
|
+
this.client.forceDisconnect();
|
|
12283
|
+
await ensureDaemonReachable();
|
|
12284
|
+
for (const task of this.tasksAcc())
|
|
12285
|
+
this.pendingInputBroker.clearForTask(task.id);
|
|
12286
|
+
await this.init();
|
|
12287
|
+
this.setConnectionState("online");
|
|
12288
|
+
}
|
|
12289
|
+
connectionStateSignal() {
|
|
12290
|
+
return this.connectionStateAcc;
|
|
12291
|
+
}
|
|
12156
12292
|
dispose() {
|
|
12157
12293
|
this.client.close();
|
|
12158
12294
|
}
|
|
@@ -12405,15 +12541,16 @@ class RemoteOrchestrator {
|
|
|
12405
12541
|
var init_remote_orchestrator = __esm(() => {
|
|
12406
12542
|
init_dev();
|
|
12407
12543
|
init_core();
|
|
12544
|
+
init_daemon_process();
|
|
12408
12545
|
});
|
|
12409
12546
|
|
|
12410
12547
|
// src/orchestrator/worktree/git.ts
|
|
12411
|
-
import { spawnSync as
|
|
12548
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
12412
12549
|
function git2(args, opts) {
|
|
12413
12550
|
if (!opts.cwd) {
|
|
12414
12551
|
throw new Error("git(): cwd is required; refusing to inherit from process.cwd()");
|
|
12415
12552
|
}
|
|
12416
|
-
const proc =
|
|
12553
|
+
const proc = spawnSync7("git", [...args], {
|
|
12417
12554
|
cwd: opts.cwd,
|
|
12418
12555
|
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
12419
12556
|
encoding: "utf8",
|
|
@@ -12451,7 +12588,7 @@ var init_git2 = __esm(() => {
|
|
|
12451
12588
|
|
|
12452
12589
|
// src/orchestrator/worktree/manager.ts
|
|
12453
12590
|
import fs4 from "fs";
|
|
12454
|
-
import
|
|
12591
|
+
import path6 from "path";
|
|
12455
12592
|
|
|
12456
12593
|
class GitWorktreeManager {
|
|
12457
12594
|
async create(repo, branch, worktreePath, baseRef) {
|
|
@@ -12469,7 +12606,7 @@ class GitWorktreeManager {
|
|
|
12469
12606
|
}
|
|
12470
12607
|
throw new Error(`create(): ${worktreePath} exists but is not a registered git worktree`);
|
|
12471
12608
|
}
|
|
12472
|
-
fs4.mkdirSync(
|
|
12609
|
+
fs4.mkdirSync(path6.dirname(worktreePath), { recursive: true });
|
|
12473
12610
|
const branchExists = this.branchExists(repo, branch);
|
|
12474
12611
|
const args = branchExists ? ["worktree", "add", worktreePath, branch] : baseRef ? ["worktree", "add", "-b", branch, worktreePath, baseRef] : ["worktree", "add", "-b", branch, worktreePath];
|
|
12475
12612
|
git2(args, { cwd: repo });
|
|
@@ -12524,8 +12661,8 @@ class GitWorktreeManager {
|
|
|
12524
12661
|
if (!entry.branch || entry.detached)
|
|
12525
12662
|
continue;
|
|
12526
12663
|
const canonEntry = canonicalize2(entry.path);
|
|
12527
|
-
const rel =
|
|
12528
|
-
const callerPath =
|
|
12664
|
+
const rel = path6.relative(canonRoot, canonEntry);
|
|
12665
|
+
const callerPath = path6.join(callerRoot, rel);
|
|
12529
12666
|
const dirty = await this.isDirty(entry.path);
|
|
12530
12667
|
infos.push({
|
|
12531
12668
|
path: callerPath,
|
|
@@ -12586,9 +12723,9 @@ class GitWorktreeManager {
|
|
|
12586
12723
|
const gitDir = out.stdout.trim();
|
|
12587
12724
|
if (!gitDir)
|
|
12588
12725
|
return null;
|
|
12589
|
-
const absolute =
|
|
12590
|
-
const base =
|
|
12591
|
-
return base === ".git" ?
|
|
12726
|
+
const absolute = path6.isAbsolute(gitDir) ? gitDir : path6.resolve(worktreePath, gitDir);
|
|
12727
|
+
const base = path6.basename(absolute);
|
|
12728
|
+
return base === ".git" ? path6.dirname(absolute) : absolute;
|
|
12592
12729
|
} catch (err) {
|
|
12593
12730
|
if (err instanceof GitCommandError)
|
|
12594
12731
|
return null;
|
|
@@ -12628,7 +12765,7 @@ function parsePorcelain2(out) {
|
|
|
12628
12765
|
return records;
|
|
12629
12766
|
}
|
|
12630
12767
|
function requireAbsolute(name, value) {
|
|
12631
|
-
if (!value || !
|
|
12768
|
+
if (!value || !path6.isAbsolute(value)) {
|
|
12632
12769
|
throw new Error(`${name} must be an absolute path, got: ${JSON.stringify(value)}`);
|
|
12633
12770
|
}
|
|
12634
12771
|
}
|
|
@@ -12636,7 +12773,7 @@ function canonicalize2(p2) {
|
|
|
12636
12773
|
try {
|
|
12637
12774
|
return fs4.realpathSync(p2);
|
|
12638
12775
|
} catch {
|
|
12639
|
-
return
|
|
12776
|
+
return path6.resolve(p2);
|
|
12640
12777
|
}
|
|
12641
12778
|
}
|
|
12642
12779
|
var init_manager = __esm(() => {
|
|
@@ -12711,7 +12848,7 @@ function DialogConfirm(props) {
|
|
|
12711
12848
|
props.onCancel?.();
|
|
12712
12849
|
dialog.clear();
|
|
12713
12850
|
});
|
|
12714
|
-
insert(_el$9, () => titlecase(key === "cancel" ? props.label ?? key : key));
|
|
12851
|
+
insert(_el$9, () => titlecase(key === "cancel" ? props.label ?? key : props.confirmLabel ?? key));
|
|
12715
12852
|
effect((_p$) => {
|
|
12716
12853
|
var _v$5 = key === store2.active ? theme.primary : undefined, _v$6 = key === store2.active ? theme.selectedListItemText : theme.textMuted;
|
|
12717
12854
|
_v$5 !== _p$.e && (_p$.e = setProp(_el$8, "backgroundColor", _v$5, _p$.e));
|
|
@@ -12753,14 +12890,15 @@ var init_dialog_confirm = __esm(() => {
|
|
|
12753
12890
|
init_theme();
|
|
12754
12891
|
init_keymap();
|
|
12755
12892
|
init_dialog();
|
|
12756
|
-
DialogConfirm.show = (dialog, title, message, label) => {
|
|
12893
|
+
DialogConfirm.show = (dialog, title, message, label, confirmLabel) => {
|
|
12757
12894
|
return new Promise((resolve3) => {
|
|
12758
12895
|
dialog.replace(() => createComponent2(DialogConfirm, {
|
|
12759
12896
|
title,
|
|
12760
12897
|
message,
|
|
12761
12898
|
onConfirm: () => resolve3(true),
|
|
12762
12899
|
onCancel: () => resolve3(false),
|
|
12763
|
-
label
|
|
12900
|
+
label,
|
|
12901
|
+
confirmLabel
|
|
12764
12902
|
}), () => resolve3(undefined));
|
|
12765
12903
|
dialog.setSize("small");
|
|
12766
12904
|
});
|
|
@@ -13545,8 +13683,8 @@ function args(player, file, volume) {
|
|
|
13545
13683
|
function pickPlayer() {
|
|
13546
13684
|
if (cachedPlayer !== undefined)
|
|
13547
13685
|
return cachedPlayer;
|
|
13548
|
-
const
|
|
13549
|
-
const segments =
|
|
13686
|
+
const path7 = process.env.PATH ?? "";
|
|
13687
|
+
const segments = path7.split(":").filter(Boolean);
|
|
13550
13688
|
cachedPlayer = PLAYERS.find((p2) => segments.some((dir) => existsSync5(join10(dir, p2)))) ?? null;
|
|
13551
13689
|
return cachedPlayer;
|
|
13552
13690
|
}
|
|
@@ -13566,9 +13704,9 @@ function pulse(volume = 0.4) {
|
|
|
13566
13704
|
const player = pickPlayer();
|
|
13567
13705
|
if (!player)
|
|
13568
13706
|
return;
|
|
13569
|
-
ensureAsset().then((
|
|
13707
|
+
ensureAsset().then((path7) => {
|
|
13570
13708
|
try {
|
|
13571
|
-
const proc = Bun.spawn(args(player,
|
|
13709
|
+
const proc = Bun.spawn(args(player, path7, volume), {
|
|
13572
13710
|
stdin: "ignore",
|
|
13573
13711
|
stdout: "ignore",
|
|
13574
13712
|
stderr: "ignore"
|
|
@@ -15194,7 +15332,7 @@ var init_create_pr_button = __esm(() => {
|
|
|
15194
15332
|
});
|
|
15195
15333
|
|
|
15196
15334
|
// src/tui/lib/worktree-opener.ts
|
|
15197
|
-
import { spawn as
|
|
15335
|
+
import { spawn as spawn5 } from "child_process";
|
|
15198
15336
|
import { existsSync as existsSync6 } from "fs";
|
|
15199
15337
|
import { basename as basename4, delimiter, isAbsolute as isAbsolute2, join as join12 } from "path";
|
|
15200
15338
|
function executableOnPath(command, env, exists) {
|
|
@@ -15236,7 +15374,7 @@ function detectWorktreeOpener(deps = {}) {
|
|
|
15236
15374
|
}
|
|
15237
15375
|
if (platform === "darwin" && executableOnPath("open", env, exists)) {
|
|
15238
15376
|
for (const app of MAC_APP_CANDIDATES) {
|
|
15239
|
-
if (app.paths.some((
|
|
15377
|
+
if (app.paths.some((path7) => exists(path7))) {
|
|
15240
15378
|
return { id: app.id, label: app.label, command: "open", args: ["-a", app.appName] };
|
|
15241
15379
|
}
|
|
15242
15380
|
}
|
|
@@ -15253,7 +15391,7 @@ function buildOpenWorktreeCommand(worktreePath, opener) {
|
|
|
15253
15391
|
function openWorktree(worktreePath, opener, deps = {}) {
|
|
15254
15392
|
if (!worktreePath)
|
|
15255
15393
|
return false;
|
|
15256
|
-
const spawnFn = deps.spawn ??
|
|
15394
|
+
const spawnFn = deps.spawn ?? spawn5;
|
|
15257
15395
|
const [command, args2] = buildOpenWorktreeCommand(worktreePath, opener);
|
|
15258
15396
|
try {
|
|
15259
15397
|
const child = spawnFn(command, args2, { detached: true, stdio: "ignore" });
|
|
@@ -16304,6 +16442,10 @@ function TopBar(props) {
|
|
|
16304
16442
|
theme
|
|
16305
16443
|
} = useTheme();
|
|
16306
16444
|
const dialog = useDialog();
|
|
16445
|
+
const connectionState = createMemo(() => {
|
|
16446
|
+
const orch = props.orchestrator;
|
|
16447
|
+
return orch instanceof RemoteOrchestrator ? orch.connectionStateSignal()() : "online";
|
|
16448
|
+
});
|
|
16307
16449
|
const remoteOrch = props.orchestrator instanceof RemoteOrchestrator ? props.orchestrator : null;
|
|
16308
16450
|
const rcBridge = props.orchestrator.rcBridgeSignal();
|
|
16309
16451
|
const isBridgeOn = () => rcBridge().state === "running" || rcBridge().state === "starting";
|
|
@@ -16389,22 +16531,46 @@ function TopBar(props) {
|
|
|
16389
16531
|
setProp(_el$11, "justifyContent", "center");
|
|
16390
16532
|
insert(_el$11, createComponent2(Show, {
|
|
16391
16533
|
get when() {
|
|
16392
|
-
return
|
|
16534
|
+
return connectionState() === "online";
|
|
16535
|
+
},
|
|
16536
|
+
get fallback() {
|
|
16537
|
+
return (() => {
|
|
16538
|
+
var _el$14 = createElement("text");
|
|
16539
|
+
insertNode(_el$14, createTextNode(`daemon disconnected`));
|
|
16540
|
+
setProp(_el$14, "wrapMode", "none");
|
|
16541
|
+
effect((_p$) => {
|
|
16542
|
+
var _v$0 = theme.error, _v$1 = TextAttributes18.BOLD;
|
|
16543
|
+
_v$0 !== _p$.e && (_p$.e = setProp(_el$14, "fg", _v$0, _p$.e));
|
|
16544
|
+
_v$1 !== _p$.t && (_p$.t = setProp(_el$14, "attributes", _v$1, _p$.t));
|
|
16545
|
+
return _p$;
|
|
16546
|
+
}, {
|
|
16547
|
+
e: undefined,
|
|
16548
|
+
t: undefined
|
|
16549
|
+
});
|
|
16550
|
+
return _el$14;
|
|
16551
|
+
})();
|
|
16393
16552
|
},
|
|
16394
16553
|
get children() {
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16554
|
+
return createComponent2(Show, {
|
|
16555
|
+
get when() {
|
|
16556
|
+
return props.activeTask() !== undefined;
|
|
16557
|
+
},
|
|
16558
|
+
get children() {
|
|
16559
|
+
var _el$12 = createElement("text");
|
|
16560
|
+
setProp(_el$12, "wrapMode", "none");
|
|
16561
|
+
insert(_el$12, () => props.activeTask()?.branch);
|
|
16562
|
+
effect((_p$) => {
|
|
16563
|
+
var _v$5 = theme.text, _v$6 = TextAttributes18.BOLD;
|
|
16564
|
+
_v$5 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$5, _p$.e));
|
|
16565
|
+
_v$6 !== _p$.t && (_p$.t = setProp(_el$12, "attributes", _v$6, _p$.t));
|
|
16566
|
+
return _p$;
|
|
16567
|
+
}, {
|
|
16568
|
+
e: undefined,
|
|
16569
|
+
t: undefined
|
|
16570
|
+
});
|
|
16571
|
+
return _el$12;
|
|
16572
|
+
}
|
|
16406
16573
|
});
|
|
16407
|
-
return _el$12;
|
|
16408
16574
|
}
|
|
16409
16575
|
}));
|
|
16410
16576
|
setProp(_el$13, "flexDirection", "row");
|
|
@@ -16511,7 +16677,7 @@ var init_sync = __esm(() => {
|
|
|
16511
16677
|
import { randomUUID } from "crypto";
|
|
16512
16678
|
import { appendFile, mkdir as mkdir2, readFile as readFile3, readdir, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
16513
16679
|
import { homedir as homedir8 } from "os";
|
|
16514
|
-
import
|
|
16680
|
+
import path7 from "path";
|
|
16515
16681
|
function encodeCwd(cwd) {
|
|
16516
16682
|
return cwd.replace(/[/.]/g, "-");
|
|
16517
16683
|
}
|
|
@@ -16519,7 +16685,7 @@ async function readHistory(sessionId, deps = defaultDeps2) {
|
|
|
16519
16685
|
const root = deps.projectsDir();
|
|
16520
16686
|
const projectDirs = await deps.readdir(root);
|
|
16521
16687
|
for (const dir of projectDirs) {
|
|
16522
|
-
const candidate =
|
|
16688
|
+
const candidate = path7.join(root, dir, `${sessionId}.jsonl`);
|
|
16523
16689
|
let raw;
|
|
16524
16690
|
try {
|
|
16525
16691
|
raw = await deps.readFile(candidate);
|
|
@@ -16534,7 +16700,7 @@ async function deleteHistory(sessionId, deps = defaultDeps2) {
|
|
|
16534
16700
|
const root = deps.projectsDir();
|
|
16535
16701
|
const projectDirs = await deps.readdir(root);
|
|
16536
16702
|
for (const dir of projectDirs) {
|
|
16537
|
-
const candidate =
|
|
16703
|
+
const candidate = path7.join(root, dir, `${sessionId}.jsonl`);
|
|
16538
16704
|
try {
|
|
16539
16705
|
await unlink2(candidate);
|
|
16540
16706
|
} catch (err) {
|
|
@@ -16609,8 +16775,8 @@ function isObject(v2) {
|
|
|
16609
16775
|
async function appendInterruptedUserPrompt(sessionId, cwd, prompt, deps = defaultDeps2) {
|
|
16610
16776
|
if (!prompt || prompt.trim().length === 0)
|
|
16611
16777
|
return;
|
|
16612
|
-
const projectDir =
|
|
16613
|
-
const filePath =
|
|
16778
|
+
const projectDir = path7.join(deps.projectsDir(), encodeCwd(cwd));
|
|
16779
|
+
const filePath = path7.join(projectDir, `${sessionId}.jsonl`);
|
|
16614
16780
|
let lines = [];
|
|
16615
16781
|
try {
|
|
16616
16782
|
const raw = await readFile3(filePath, "utf8");
|
|
@@ -16680,7 +16846,7 @@ var defaultDeps2;
|
|
|
16680
16846
|
var init_history = __esm(() => {
|
|
16681
16847
|
defaultDeps2 = {
|
|
16682
16848
|
projectsDir() {
|
|
16683
|
-
return
|
|
16849
|
+
return path7.join(homedir8(), ".claude", "projects");
|
|
16684
16850
|
},
|
|
16685
16851
|
async readdir(p2) {
|
|
16686
16852
|
try {
|
|
@@ -16765,15 +16931,15 @@ function delay(ms) {
|
|
|
16765
16931
|
// src/engine/claude-code-local/sessions.ts
|
|
16766
16932
|
import { readFile as readFile4, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
16767
16933
|
import { homedir as homedir9 } from "os";
|
|
16768
|
-
import
|
|
16934
|
+
import path8 from "path";
|
|
16769
16935
|
async function listSessionsForCwd(cwd, deps = defaultDeps3) {
|
|
16770
|
-
const projectDir =
|
|
16936
|
+
const projectDir = path8.join(deps.projectsDir(), encodeCwd(cwd));
|
|
16771
16937
|
const entries = await deps.readdir(projectDir);
|
|
16772
16938
|
const jsonlNames = entries.filter((n2) => n2.endsWith(".jsonl"));
|
|
16773
16939
|
const out = [];
|
|
16774
16940
|
for (const name of jsonlNames) {
|
|
16775
16941
|
const sessionId = name.slice(0, -".jsonl".length);
|
|
16776
|
-
const filePath =
|
|
16942
|
+
const filePath = path8.join(projectDir, name);
|
|
16777
16943
|
try {
|
|
16778
16944
|
const [meta, raw] = await Promise.all([deps.stat(filePath), deps.readFile(filePath)]);
|
|
16779
16945
|
const lines = raw.split(`
|
|
@@ -16838,7 +17004,7 @@ var init_sessions = __esm(() => {
|
|
|
16838
17004
|
init_history();
|
|
16839
17005
|
defaultDeps3 = {
|
|
16840
17006
|
projectsDir() {
|
|
16841
|
-
return
|
|
17007
|
+
return path8.join(homedir9(), ".claude", "projects");
|
|
16842
17008
|
},
|
|
16843
17009
|
async readdir(p2) {
|
|
16844
17010
|
try {
|
|
@@ -16858,10 +17024,10 @@ var init_sessions = __esm(() => {
|
|
|
16858
17024
|
});
|
|
16859
17025
|
|
|
16860
17026
|
// src/engine/claude-code-local/spawn.ts
|
|
16861
|
-
import { spawn as
|
|
17027
|
+
import { spawn as spawn6 } from "child_process";
|
|
16862
17028
|
function spawnClaudeProcess(opts) {
|
|
16863
17029
|
const args2 = buildArgs(opts);
|
|
16864
|
-
const proc =
|
|
17030
|
+
const proc = spawn6(opts.binaryPath, args2, {
|
|
16865
17031
|
cwd: opts.cwd,
|
|
16866
17032
|
env: { ...process.env, ...opts.env ?? {} },
|
|
16867
17033
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -17811,8 +17977,8 @@ function validateRepoPath(repo) {
|
|
|
17811
17977
|
if (!stat3.isDirectory())
|
|
17812
17978
|
return `not a directory: ${trimmed}`;
|
|
17813
17979
|
try {
|
|
17814
|
-
const { spawnSync:
|
|
17815
|
-
const out =
|
|
17980
|
+
const { spawnSync: spawnSync8 } = __require("child_process");
|
|
17981
|
+
const out = spawnSync8("git", ["rev-parse", "--git-dir"], {
|
|
17816
17982
|
cwd: trimmed,
|
|
17817
17983
|
encoding: "utf-8",
|
|
17818
17984
|
timeout: 2000,
|
|
@@ -17829,8 +17995,8 @@ function getCurrentBranch(repo) {
|
|
|
17829
17995
|
if (!repo)
|
|
17830
17996
|
return null;
|
|
17831
17997
|
try {
|
|
17832
|
-
const { spawnSync:
|
|
17833
|
-
const out =
|
|
17998
|
+
const { spawnSync: spawnSync8 } = __require("child_process");
|
|
17999
|
+
const out = spawnSync8("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
17834
18000
|
cwd: repo,
|
|
17835
18001
|
encoding: "utf-8",
|
|
17836
18002
|
timeout: 2000,
|
|
@@ -17850,8 +18016,8 @@ function listLocalBranches(repo) {
|
|
|
17850
18016
|
if (!repo)
|
|
17851
18017
|
return [];
|
|
17852
18018
|
try {
|
|
17853
|
-
const { spawnSync:
|
|
17854
|
-
const out =
|
|
18019
|
+
const { spawnSync: spawnSync8 } = __require("child_process");
|
|
18020
|
+
const out = spawnSync8("git", ["for-each-ref", "--format=%(refname:short)", "refs/heads/"], {
|
|
17855
18021
|
cwd: repo,
|
|
17856
18022
|
encoding: "utf-8",
|
|
17857
18023
|
timeout: 2000
|
|
@@ -18970,7 +19136,7 @@ var init_resume_dialog = __esm(() => {
|
|
|
18970
19136
|
});
|
|
18971
19137
|
|
|
18972
19138
|
// src/tui/panes/chat/composer/clipboard-image.ts
|
|
18973
|
-
import { spawnSync as
|
|
19139
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
18974
19140
|
import { statSync as statSync4 } from "fs";
|
|
18975
19141
|
function clipboardImageSupported() {
|
|
18976
19142
|
return process.platform === "darwin";
|
|
@@ -18995,7 +19161,7 @@ function readClipboardImageMacOS(destPath) {
|
|
|
18995
19161
|
"end try"
|
|
18996
19162
|
].join(`
|
|
18997
19163
|
`);
|
|
18998
|
-
const result =
|
|
19164
|
+
const result = spawnSync8("osascript", ["-e", script], {
|
|
18999
19165
|
timeout: 5000,
|
|
19000
19166
|
stdio: ["ignore", "ignore", "ignore"]
|
|
19001
19167
|
});
|
|
@@ -19183,25 +19349,25 @@ function findMentionContext(text, cursor) {
|
|
|
19183
19349
|
}
|
|
19184
19350
|
return { atPos, query: text.slice(atPos + 1, cursor) };
|
|
19185
19351
|
}
|
|
19186
|
-
function formatDisplayPath(
|
|
19187
|
-
if (
|
|
19188
|
-
return
|
|
19189
|
-
return
|
|
19352
|
+
function formatDisplayPath(path9) {
|
|
19353
|
+
if (path9.startsWith("packages/"))
|
|
19354
|
+
return path9.slice("packages/".length);
|
|
19355
|
+
return path9;
|
|
19190
19356
|
}
|
|
19191
19357
|
function filterMentionMatches(files, query, limit) {
|
|
19192
19358
|
if (files.length === 0 || limit <= 0)
|
|
19193
19359
|
return [];
|
|
19194
19360
|
const q2 = query.toLowerCase();
|
|
19195
19361
|
if (q2.length === 0) {
|
|
19196
|
-
return files.slice(0, limit).map((
|
|
19197
|
-
path:
|
|
19198
|
-
displayPath: formatDisplayPath(
|
|
19362
|
+
return files.slice(0, limit).map((path9) => ({
|
|
19363
|
+
path: path9,
|
|
19364
|
+
displayPath: formatDisplayPath(path9),
|
|
19199
19365
|
score: 0
|
|
19200
19366
|
}));
|
|
19201
19367
|
}
|
|
19202
19368
|
const matches = [];
|
|
19203
|
-
for (const
|
|
19204
|
-
const lower =
|
|
19369
|
+
for (const path9 of files) {
|
|
19370
|
+
const lower = path9.toLowerCase();
|
|
19205
19371
|
const slash = lower.lastIndexOf("/");
|
|
19206
19372
|
const filename = slash >= 0 ? lower.slice(slash + 1) : lower;
|
|
19207
19373
|
let score = 0;
|
|
@@ -19215,8 +19381,8 @@ function filterMentionMatches(files, query, limit) {
|
|
|
19215
19381
|
score = 40;
|
|
19216
19382
|
else
|
|
19217
19383
|
continue;
|
|
19218
|
-
score -=
|
|
19219
|
-
matches.push({ path:
|
|
19384
|
+
score -= path9.length * 0.5;
|
|
19385
|
+
matches.push({ path: path9, displayPath: formatDisplayPath(path9), score });
|
|
19220
19386
|
}
|
|
19221
19387
|
matches.sort((a2, b2) => b2.score - a2.score);
|
|
19222
19388
|
return matches.slice(0, limit);
|
|
@@ -19363,7 +19529,7 @@ function Composer(props) {
|
|
|
19363
19529
|
total
|
|
19364
19530
|
};
|
|
19365
19531
|
});
|
|
19366
|
-
function insertMentionSelection(
|
|
19532
|
+
function insertMentionSelection(path9) {
|
|
19367
19533
|
const ref = textareaRef;
|
|
19368
19534
|
const ctx4 = mentionContext();
|
|
19369
19535
|
if (!ref || !ctx4)
|
|
@@ -19371,7 +19537,7 @@ function Composer(props) {
|
|
|
19371
19537
|
const cursor = ref.cursorOffset;
|
|
19372
19538
|
ref.setSelection(ctx4.atPos, cursor);
|
|
19373
19539
|
ref.deleteSelection();
|
|
19374
|
-
const inserted = `@${
|
|
19540
|
+
const inserted = `@${path9} `;
|
|
19375
19541
|
ref.insertText(inserted);
|
|
19376
19542
|
setMentionDismissedAt(null);
|
|
19377
19543
|
setMentionCursor(0);
|
|
@@ -20731,7 +20897,7 @@ function readWriteInput(input) {
|
|
|
20731
20897
|
};
|
|
20732
20898
|
}
|
|
20733
20899
|
function makeHeader(filePath, verb, adds, removes) {
|
|
20734
|
-
const
|
|
20900
|
+
const path9 = filePath || "(unknown file)";
|
|
20735
20901
|
const parts = [];
|
|
20736
20902
|
if (adds > 0) {
|
|
20737
20903
|
parts.push(`Added ${adds} ${adds === 1 ? "line" : "lines"}`);
|
|
@@ -20741,11 +20907,11 @@ function makeHeader(filePath, verb, adds, removes) {
|
|
|
20741
20907
|
parts.push(`${word} ${removes} ${removes === 1 ? "line" : "lines"}`);
|
|
20742
20908
|
}
|
|
20743
20909
|
if (parts.length === 0)
|
|
20744
|
-
return `${verb} ${
|
|
20745
|
-
return `${verb} ${
|
|
20910
|
+
return `${verb} ${path9}`;
|
|
20911
|
+
return `${verb} ${path9} \xB7 ${parts.join(", ")}`;
|
|
20746
20912
|
}
|
|
20747
20913
|
function makeMultiEditHeader(filePath, count, adds, removes) {
|
|
20748
|
-
const
|
|
20914
|
+
const path9 = filePath || "(unknown file)";
|
|
20749
20915
|
const segments = [];
|
|
20750
20916
|
if (count > 0) {
|
|
20751
20917
|
segments.push(`${count} ${count === 1 ? "edit" : "edits"}`);
|
|
@@ -20761,8 +20927,8 @@ function makeMultiEditHeader(filePath, count, adds, removes) {
|
|
|
20761
20927
|
if (lineParts.length > 0)
|
|
20762
20928
|
segments.push(lineParts.join(", "));
|
|
20763
20929
|
if (segments.length === 0)
|
|
20764
|
-
return `Edited ${
|
|
20765
|
-
return `Edited ${
|
|
20930
|
+
return `Edited ${path9}`;
|
|
20931
|
+
return `Edited ${path9} \xB7 ${segments.join(" \xB7 ")}`;
|
|
20766
20932
|
}
|
|
20767
20933
|
var COLLAPSED_LINE_CAP = 10;
|
|
20768
20934
|
|
|
@@ -22742,24 +22908,24 @@ import { join as join14 } from "path";
|
|
|
22742
22908
|
function resolveHome() {
|
|
22743
22909
|
return process.env.HOME ?? homedir11();
|
|
22744
22910
|
}
|
|
22745
|
-
async function safeReaddir(
|
|
22911
|
+
async function safeReaddir(path9) {
|
|
22746
22912
|
try {
|
|
22747
|
-
return await readdir3(
|
|
22913
|
+
return await readdir3(path9);
|
|
22748
22914
|
} catch {
|
|
22749
22915
|
return [];
|
|
22750
22916
|
}
|
|
22751
22917
|
}
|
|
22752
|
-
async function isFile(
|
|
22918
|
+
async function isFile(path9) {
|
|
22753
22919
|
try {
|
|
22754
|
-
const s2 = await stat3(
|
|
22920
|
+
const s2 = await stat3(path9);
|
|
22755
22921
|
return s2.isFile();
|
|
22756
22922
|
} catch {
|
|
22757
22923
|
return false;
|
|
22758
22924
|
}
|
|
22759
22925
|
}
|
|
22760
|
-
async function isDir(
|
|
22926
|
+
async function isDir(path9) {
|
|
22761
22927
|
try {
|
|
22762
|
-
const s2 = await stat3(
|
|
22928
|
+
const s2 = await stat3(path9);
|
|
22763
22929
|
return s2.isDirectory();
|
|
22764
22930
|
} catch {
|
|
22765
22931
|
return false;
|
|
@@ -22830,9 +22996,9 @@ function extractDescription(content) {
|
|
|
22830
22996
|
}
|
|
22831
22997
|
return null;
|
|
22832
22998
|
}
|
|
22833
|
-
async function tryReadDescription(
|
|
22999
|
+
async function tryReadDescription(path9) {
|
|
22834
23000
|
try {
|
|
22835
|
-
const content = await readFile5(
|
|
23001
|
+
const content = await readFile5(path9, "utf-8");
|
|
22836
23002
|
return extractDescription(content);
|
|
22837
23003
|
} catch {
|
|
22838
23004
|
return null;
|
|
@@ -24265,12 +24431,12 @@ var init_sidebar = __esm(() => {
|
|
|
24265
24431
|
});
|
|
24266
24432
|
|
|
24267
24433
|
// src/tui/panes/sidebar/git-head.ts
|
|
24268
|
-
import { spawnSync as
|
|
24434
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
24269
24435
|
function readCurrentBranch(repo) {
|
|
24270
24436
|
if (!repo)
|
|
24271
24437
|
return "";
|
|
24272
24438
|
try {
|
|
24273
|
-
const out =
|
|
24439
|
+
const out = spawnSync9("git", ["symbolic-ref", "--short", "HEAD"], {
|
|
24274
24440
|
cwd: repo,
|
|
24275
24441
|
encoding: "utf8",
|
|
24276
24442
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -24280,7 +24446,7 @@ function readCurrentBranch(repo) {
|
|
|
24280
24446
|
if (name && name !== "HEAD")
|
|
24281
24447
|
return name;
|
|
24282
24448
|
}
|
|
24283
|
-
const head =
|
|
24449
|
+
const head = spawnSync9("git", ["rev-parse", "--verify", "HEAD"], {
|
|
24284
24450
|
cwd: repo,
|
|
24285
24451
|
encoding: "utf8",
|
|
24286
24452
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -24941,8 +25107,8 @@ function Shell(props) {
|
|
|
24941
25107
|
return pp.prompt;
|
|
24942
25108
|
});
|
|
24943
25109
|
const worktreePathAcc = createMemo(() => {
|
|
24944
|
-
const
|
|
24945
|
-
return
|
|
25110
|
+
const path9 = activeTask()?.worktreePath;
|
|
25111
|
+
return path9 ? path9 : null;
|
|
24946
25112
|
});
|
|
24947
25113
|
const taskIdNullAcc = createMemo(() => selectedId());
|
|
24948
25114
|
const diffBaseAcc = createMemo(() => worktreePathAcc() ? "HEAD" : null);
|
|
@@ -24969,6 +25135,44 @@ function Shell(props) {
|
|
|
24969
25135
|
const baseAcc = focus.is(pane);
|
|
24970
25136
|
return () => baseAcc() && dialog.stack.length === 0;
|
|
24971
25137
|
};
|
|
25138
|
+
let showingDisconnectDialog = false;
|
|
25139
|
+
async function showDisconnectDialog() {
|
|
25140
|
+
const orch = props.orchestrator;
|
|
25141
|
+
if (!(orch instanceof RemoteOrchestrator))
|
|
25142
|
+
return;
|
|
25143
|
+
let message = "kobed is no longer reachable. Restart it and reconnect, or quit kobe?";
|
|
25144
|
+
while (true) {
|
|
25145
|
+
const choice = await DialogConfirm.show(dialog, "daemon disconnected", message, "Quit", "Restart");
|
|
25146
|
+
if (choice !== true) {
|
|
25147
|
+
try {
|
|
25148
|
+
renderer?.destroy();
|
|
25149
|
+
} catch {}
|
|
25150
|
+
process.exit(0);
|
|
25151
|
+
}
|
|
25152
|
+
try {
|
|
25153
|
+
await orch.manualReconnect();
|
|
25154
|
+
return;
|
|
25155
|
+
} catch (err) {
|
|
25156
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
25157
|
+
message = `Restart failed: ${errMsg}
|
|
25158
|
+
|
|
25159
|
+
Try again or quit?`;
|
|
25160
|
+
}
|
|
25161
|
+
}
|
|
25162
|
+
}
|
|
25163
|
+
createEffect(() => {
|
|
25164
|
+
const orch = props.orchestrator;
|
|
25165
|
+
if (!(orch instanceof RemoteOrchestrator))
|
|
25166
|
+
return;
|
|
25167
|
+
if (orch.connectionStateSignal()() !== "disconnected")
|
|
25168
|
+
return;
|
|
25169
|
+
if (showingDisconnectDialog)
|
|
25170
|
+
return;
|
|
25171
|
+
showingDisconnectDialog = true;
|
|
25172
|
+
showDisconnectDialog().finally(() => {
|
|
25173
|
+
showingDisconnectDialog = false;
|
|
25174
|
+
});
|
|
25175
|
+
});
|
|
24972
25176
|
const FOCUS_HJKL_TARGETS = {
|
|
24973
25177
|
h: "sidebar",
|
|
24974
25178
|
j: "workspace",
|
|
@@ -25467,6 +25671,7 @@ var init_app = __esm(() => {
|
|
|
25467
25671
|
init_Sidebar();
|
|
25468
25672
|
init_terminal();
|
|
25469
25673
|
init_dialog();
|
|
25674
|
+
init_dialog_confirm();
|
|
25470
25675
|
});
|
|
25471
25676
|
|
|
25472
25677
|
// src/tui/index.tsx
|