@sma1lboy/kobe 0.7.3 → 0.7.4
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 +785 -276
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -70,7 +70,7 @@ var init_package = __esm(() => {
|
|
|
70
70
|
package_default = {
|
|
71
71
|
$schema: "https://json.schemastore.org/package.json",
|
|
72
72
|
name: "@sma1lboy/kobe",
|
|
73
|
-
version: "0.7.
|
|
73
|
+
version: "0.7.4",
|
|
74
74
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
75
75
|
type: "module",
|
|
76
76
|
packageManager: "bun@1.3.13",
|
|
@@ -2325,6 +2325,248 @@ var init_dev = __esm(() => {
|
|
|
2325
2325
|
}
|
|
2326
2326
|
});
|
|
2327
2327
|
|
|
2328
|
+
// src/cli/invocation.ts
|
|
2329
|
+
import { fileURLToPath } from "url";
|
|
2330
|
+
function kobeCliInvocation() {
|
|
2331
|
+
const isBuilt = import.meta.url.endsWith(".js");
|
|
2332
|
+
if (isBuilt)
|
|
2333
|
+
return ["kobe"];
|
|
2334
|
+
const entry = fileURLToPath(new URL("./index.ts", import.meta.url));
|
|
2335
|
+
const preload = fileURLToPath(import.meta.resolve("@opentui/solid/preload"));
|
|
2336
|
+
return [process.execPath, "--preload", preload, "--conditions=browser", entry];
|
|
2337
|
+
}
|
|
2338
|
+
var init_invocation = () => {};
|
|
2339
|
+
|
|
2340
|
+
// src/tmux/session-layout.ts
|
|
2341
|
+
function shellQuote(s) {
|
|
2342
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
2343
|
+
}
|
|
2344
|
+
function shellQuoteArgv(argv) {
|
|
2345
|
+
return argv.map(shellQuote).join(" ");
|
|
2346
|
+
}
|
|
2347
|
+
function keepAlive(cmd) {
|
|
2348
|
+
return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
|
|
2349
|
+
}
|
|
2350
|
+
function shQuote(s) {
|
|
2351
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
2352
|
+
}
|
|
2353
|
+
function homeWelcomeCommand() {
|
|
2354
|
+
const msg = "\\n No task selected\\n\\n Press N to create a task, or pick one on the left.\\n\\n";
|
|
2355
|
+
return `clear; printf ${shQuote(msg)}; exec "\${SHELL:-/bin/sh}"`;
|
|
2356
|
+
}
|
|
2357
|
+
function engineLaunchLine(engineCmd, init) {
|
|
2358
|
+
const tail = keepAlive(engineCmd);
|
|
2359
|
+
const script = init?.initScript?.trim();
|
|
2360
|
+
if (!script)
|
|
2361
|
+
return tail;
|
|
2362
|
+
const group = ["{", script, "}"].join(`
|
|
2363
|
+
`);
|
|
2364
|
+
if (init?.markerPath) {
|
|
2365
|
+
const marker = shQuote(init.markerPath);
|
|
2366
|
+
const markerDir = shQuote(markerDirOf(init.markerPath));
|
|
2367
|
+
return [
|
|
2368
|
+
`if [ ! -f ${marker} ]; then`,
|
|
2369
|
+
group,
|
|
2370
|
+
`if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
|
|
2371
|
+
"fi",
|
|
2372
|
+
tail
|
|
2373
|
+
].join(`
|
|
2374
|
+
`);
|
|
2375
|
+
}
|
|
2376
|
+
return [group, tail].join(`
|
|
2377
|
+
`);
|
|
2378
|
+
}
|
|
2379
|
+
function markerDirOf(p) {
|
|
2380
|
+
const i = p.lastIndexOf("/");
|
|
2381
|
+
return i <= 0 ? "." : p.slice(0, i);
|
|
2382
|
+
}
|
|
2383
|
+
function fallbackOpsScript(cwd) {
|
|
2384
|
+
return `cd ${shellQuote(cwd)} && while :; do clear; printf "\\033[1m# %s\\033[0m\\n\\n" ${shellQuote(cwd)}; git status --short --branch 2>/dev/null | sed 's/^/ /' || true; printf "\\n"; if command -v lsd >/dev/null 2>&1; then lsd --tree --git -I node_modules -I .git --depth 2 .; elif command -v eza >/dev/null 2>&1; then eza --tree --git -L 2 -I 'node_modules|.git' .; elif command -v tree >/dev/null 2>&1; then tree -L 2 -I 'node_modules|.git'; else ls -la; fi; sleep 2; done`;
|
|
2385
|
+
}
|
|
2386
|
+
function previewWindowCommand(args) {
|
|
2387
|
+
const wt = shellQuote(args.worktree);
|
|
2388
|
+
const file = shellQuote(args.relPath);
|
|
2389
|
+
const inv = args.cliInvocation.map(shellQuote).join(" ");
|
|
2390
|
+
const fallback = `cd ${wt} && if ! git diff --quiet HEAD -- ${file} 2>/dev/null; then ` + `git diff HEAD -- ${file} | { delta --paging=always 2>/dev/null || less -R; }; ` + `else bat --style=plain --paging=always ${file} 2>/dev/null || \${PAGER:-less} ${file} 2>/dev/null || cat ${file}; fi`;
|
|
2391
|
+
return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
|
|
2392
|
+
}
|
|
2393
|
+
function updatePageCommand(args) {
|
|
2394
|
+
return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
|
|
2395
|
+
}
|
|
2396
|
+
function tasksPaneCommand(cliInvocation, opts = {}) {
|
|
2397
|
+
const argv = [...cliInvocation, "tasks"];
|
|
2398
|
+
if (opts.initialTaskId)
|
|
2399
|
+
argv.push("--initial-task-id", opts.initialTaskId);
|
|
2400
|
+
return shellQuoteArgv(argv);
|
|
2401
|
+
}
|
|
2402
|
+
function opsPaneCommand(args) {
|
|
2403
|
+
if (args.taskId && args.claudePaneId) {
|
|
2404
|
+
const inv = args.cliInvocation.map(shellQuote).join(" ");
|
|
2405
|
+
const vendorFlag = args.vendor ? ` --vendor ${shellQuote(args.vendor)}` : "";
|
|
2406
|
+
return `KOBE_FILETREE_WATCH=1 ${inv} ops --task-id ${shellQuote(args.taskId)} --worktree ${shellQuote(args.cwd)} ` + `--target-pane ${shellQuote(args.claudePaneId)}${vendorFlag} || { ${fallbackOpsScript(args.cwd)}; }`;
|
|
2407
|
+
}
|
|
2408
|
+
return fallbackOpsScript(args.cwd);
|
|
2409
|
+
}
|
|
2410
|
+
var TASKS_PANE_WIDTH = 32, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50;
|
|
2411
|
+
|
|
2412
|
+
// src/engine/claude-code-local/hook-adapter.ts
|
|
2413
|
+
import { existsSync } from "fs";
|
|
2414
|
+
import { appendFile, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
2415
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
2416
|
+
function isObject(v) {
|
|
2417
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
2418
|
+
}
|
|
2419
|
+
async function readJsonObject(path3) {
|
|
2420
|
+
try {
|
|
2421
|
+
const parsed = JSON.parse(await readFile2(path3, "utf8"));
|
|
2422
|
+
return isObject(parsed) ? parsed : {};
|
|
2423
|
+
} catch {
|
|
2424
|
+
return {};
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
function isKobeWorktreeSyncGroup(group) {
|
|
2428
|
+
if (!isObject(group) || !Array.isArray(group.hooks))
|
|
2429
|
+
return false;
|
|
2430
|
+
return group.hooks.some((h) => isObject(h) && typeof h.command === "string" && h.command.includes(WORKTREE_SYNC_MARKER));
|
|
2431
|
+
}
|
|
2432
|
+
function mergeWorktreeSyncHook(current, command) {
|
|
2433
|
+
const { hooks: rawHooks, ...restSettings } = current;
|
|
2434
|
+
const { WorktreeCreate, ...otherHooks } = isObject(rawHooks) ? rawHooks : {};
|
|
2435
|
+
const prior = Array.isArray(WorktreeCreate) ? WorktreeCreate : [];
|
|
2436
|
+
const kept = prior.filter((g) => !isKobeWorktreeSyncGroup(g));
|
|
2437
|
+
if (command !== null)
|
|
2438
|
+
kept.push({ hooks: [{ type: "command", command }] });
|
|
2439
|
+
const nextHooks = { ...otherHooks };
|
|
2440
|
+
if (kept.length > 0)
|
|
2441
|
+
nextHooks.WorktreeCreate = kept;
|
|
2442
|
+
return Object.keys(nextHooks).length > 0 ? { ...restSettings, hooks: nextHooks } : { ...restSettings };
|
|
2443
|
+
}
|
|
2444
|
+
function buildClaudeHooks(taskId, inv = kobeCliInvocation()) {
|
|
2445
|
+
const out = {};
|
|
2446
|
+
for (const { event, matcher, verb } of EVENT_MAP) {
|
|
2447
|
+
const command = shellQuoteArgv([...inv, "hook", verb, "--task-id", taskId]);
|
|
2448
|
+
const group = { hooks: [{ type: "command", command }] };
|
|
2449
|
+
if (matcher)
|
|
2450
|
+
group.matcher = matcher;
|
|
2451
|
+
out[event] = [group];
|
|
2452
|
+
}
|
|
2453
|
+
return out;
|
|
2454
|
+
}
|
|
2455
|
+
function mergeClaudeHooks(existing, kobeHooks) {
|
|
2456
|
+
const merged = { ...existing };
|
|
2457
|
+
for (const event of KOBE_HOOK_EVENTS)
|
|
2458
|
+
merged[event] = kobeHooks[event];
|
|
2459
|
+
return merged;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
class ClaudeHookAdapter {
|
|
2463
|
+
vendor = "claude";
|
|
2464
|
+
supportsHooks() {
|
|
2465
|
+
return true;
|
|
2466
|
+
}
|
|
2467
|
+
supportsWorktreeSync() {
|
|
2468
|
+
return true;
|
|
2469
|
+
}
|
|
2470
|
+
async installWorktreeSyncHook(settingsFilePath) {
|
|
2471
|
+
await this.editWorktreeSyncHook(settingsFilePath, true);
|
|
2472
|
+
}
|
|
2473
|
+
async removeWorktreeSyncHook(settingsFilePath) {
|
|
2474
|
+
await this.editWorktreeSyncHook(settingsFilePath, false);
|
|
2475
|
+
}
|
|
2476
|
+
async editWorktreeSyncHook(settingsFilePath, install) {
|
|
2477
|
+
const current = await readJsonObject(settingsFilePath);
|
|
2478
|
+
const command = install ? shellQuoteArgv([...kobeCliInvocation(), "hook", "worktree-created"]) : null;
|
|
2479
|
+
const next = mergeWorktreeSyncHook(current, command);
|
|
2480
|
+
await mkdir2(dirname3(settingsFilePath), { recursive: true });
|
|
2481
|
+
await writeFile2(settingsFilePath, `${JSON.stringify(next, null, 2)}
|
|
2482
|
+
`);
|
|
2483
|
+
}
|
|
2484
|
+
async installTaskHooks(ctx) {
|
|
2485
|
+
try {
|
|
2486
|
+
const claudeDir = join3(ctx.worktreeDir, ".claude");
|
|
2487
|
+
const settingsPath = join3(claudeDir, "settings.local.json");
|
|
2488
|
+
await mkdir2(claudeDir, { recursive: true });
|
|
2489
|
+
let current = {};
|
|
2490
|
+
try {
|
|
2491
|
+
const raw = await readFile2(settingsPath, "utf8");
|
|
2492
|
+
const parsed = JSON.parse(raw);
|
|
2493
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
2494
|
+
current = parsed;
|
|
2495
|
+
} catch {}
|
|
2496
|
+
const existingHooks = current.hooks && typeof current.hooks === "object" && !Array.isArray(current.hooks) ? current.hooks : {};
|
|
2497
|
+
current.hooks = mergeClaudeHooks(existingHooks, buildClaudeHooks(ctx.taskId));
|
|
2498
|
+
await writeFile2(settingsPath, `${JSON.stringify(current, null, 2)}
|
|
2499
|
+
`);
|
|
2500
|
+
await hideFromGit(ctx.worktreeDir, ".claude/settings.local.json");
|
|
2501
|
+
} catch {}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
async function hideFromGit(worktreeDir, relPath) {
|
|
2505
|
+
try {
|
|
2506
|
+
const proc = Bun.spawn(["git", "-C", worktreeDir, "rev-parse", "--git-common-dir"], {
|
|
2507
|
+
stdout: "pipe",
|
|
2508
|
+
stderr: "ignore"
|
|
2509
|
+
});
|
|
2510
|
+
const [out, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
2511
|
+
if (code !== 0)
|
|
2512
|
+
return;
|
|
2513
|
+
let commonDir = out.trim();
|
|
2514
|
+
if (!commonDir)
|
|
2515
|
+
return;
|
|
2516
|
+
if (!commonDir.startsWith("/"))
|
|
2517
|
+
commonDir = join3(worktreeDir, commonDir);
|
|
2518
|
+
const excludePath = join3(commonDir, "info", "exclude");
|
|
2519
|
+
const existing = existsSync(excludePath) ? await readFile2(excludePath, "utf8") : "";
|
|
2520
|
+
if (existing.split(`
|
|
2521
|
+
`).some((l) => l.trim() === relPath))
|
|
2522
|
+
return;
|
|
2523
|
+
await mkdir2(join3(commonDir, "info"), { recursive: true });
|
|
2524
|
+
await appendFile(excludePath, `${existing.endsWith(`
|
|
2525
|
+
`) || existing === "" ? "" : `
|
|
2526
|
+
`}${relPath}
|
|
2527
|
+
`);
|
|
2528
|
+
} catch {}
|
|
2529
|
+
}
|
|
2530
|
+
var EVENT_MAP, KOBE_HOOK_EVENTS, WORKTREE_SYNC_MARKER = "worktree-created";
|
|
2531
|
+
var init_hook_adapter = __esm(() => {
|
|
2532
|
+
init_invocation();
|
|
2533
|
+
EVENT_MAP = [
|
|
2534
|
+
{ event: "SessionStart", verb: "session-start" },
|
|
2535
|
+
{ event: "UserPromptSubmit", verb: "turn-start" },
|
|
2536
|
+
{ event: "Stop", verb: "turn-complete" },
|
|
2537
|
+
{ event: "StopFailure", verb: "turn-failed" },
|
|
2538
|
+
{ event: "Notification", matcher: "permission_prompt", verb: "awaiting-input" },
|
|
2539
|
+
{ event: "SessionEnd", verb: "session-end" }
|
|
2540
|
+
];
|
|
2541
|
+
KOBE_HOOK_EVENTS = EVENT_MAP.map((e) => e.event);
|
|
2542
|
+
});
|
|
2543
|
+
|
|
2544
|
+
// src/engine/hook-adapter.ts
|
|
2545
|
+
function createEngineHookAdapter(vendor) {
|
|
2546
|
+
if (vendor === "claude")
|
|
2547
|
+
return new ClaudeHookAdapter;
|
|
2548
|
+
return new NoopHookAdapter(vendor);
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
class NoopHookAdapter {
|
|
2552
|
+
vendor;
|
|
2553
|
+
constructor(vendor) {
|
|
2554
|
+
this.vendor = vendor;
|
|
2555
|
+
}
|
|
2556
|
+
supportsHooks() {
|
|
2557
|
+
return false;
|
|
2558
|
+
}
|
|
2559
|
+
async installTaskHooks() {}
|
|
2560
|
+
supportsWorktreeSync() {
|
|
2561
|
+
return false;
|
|
2562
|
+
}
|
|
2563
|
+
async installWorktreeSyncHook() {}
|
|
2564
|
+
async removeWorktreeSyncHook() {}
|
|
2565
|
+
}
|
|
2566
|
+
var init_hook_adapter2 = __esm(() => {
|
|
2567
|
+
init_hook_adapter();
|
|
2568
|
+
});
|
|
2569
|
+
|
|
2328
2570
|
// src/orchestrator/errors.ts
|
|
2329
2571
|
var IllegalTransitionError, TaskNotFoundError, CannotDeleteMainTaskError, DIRTY_WORKTREE_CODE = "DIRTY_WORKTREE", DirtyWorktreeError, WorktreeRemoveFailedError;
|
|
2330
2572
|
var init_errors = __esm(() => {
|
|
@@ -3003,8 +3245,10 @@ class Orchestrator {
|
|
|
3003
3245
|
const task = this.requireTask(id);
|
|
3004
3246
|
if (task.kind === "main")
|
|
3005
3247
|
return task.repo;
|
|
3006
|
-
if (task.worktreePath)
|
|
3248
|
+
if (task.worktreePath) {
|
|
3249
|
+
await this.installEngineHooks(task);
|
|
3007
3250
|
return task.worktreePath;
|
|
3251
|
+
}
|
|
3008
3252
|
const inflight = this.worktreeLocks.get(task.id);
|
|
3009
3253
|
if (inflight) {
|
|
3010
3254
|
await inflight;
|
|
@@ -3028,6 +3272,7 @@ class Orchestrator {
|
|
|
3028
3272
|
worktreePath: info.path,
|
|
3029
3273
|
branch
|
|
3030
3274
|
});
|
|
3275
|
+
await this.installEngineHooks(this.requireTask(task.id));
|
|
3031
3276
|
} catch (err) {
|
|
3032
3277
|
this.slugs.cancel(task.repo, slug);
|
|
3033
3278
|
throw err;
|
|
@@ -3152,14 +3397,17 @@ class Orchestrator {
|
|
|
3152
3397
|
if (!input.worktreePath)
|
|
3153
3398
|
throw new Error("adoptWorktree: worktreePath is required");
|
|
3154
3399
|
const target = canonPath(input.worktreePath);
|
|
3400
|
+
const existing = this.store.list().find((t) => t.worktreePath && canonPath(t.worktreePath) === target);
|
|
3401
|
+
if (existing) {
|
|
3402
|
+
if (input.ifExists === "return")
|
|
3403
|
+
return existing;
|
|
3404
|
+
throw new Error(`adoptWorktree: ${input.worktreePath} is already adopted as a task`);
|
|
3405
|
+
}
|
|
3155
3406
|
const candidates = await this.worktrees.listAll(input.repo);
|
|
3156
3407
|
const match = candidates.find((wt) => canonPath(wt.path) === target);
|
|
3157
3408
|
if (!match) {
|
|
3158
3409
|
throw new Error(`adoptWorktree: ${input.worktreePath} is not an adoptable git worktree of ${input.repo} (unknown, detached, or the main checkout)`);
|
|
3159
3410
|
}
|
|
3160
|
-
const alreadyLinked = this.store.list().some((t) => t.worktreePath && canonPath(t.worktreePath) === target);
|
|
3161
|
-
if (alreadyLinked)
|
|
3162
|
-
throw new Error(`adoptWorktree: ${input.worktreePath} is already adopted as a task`);
|
|
3163
3411
|
const branch = input.branch?.trim() || match.branch;
|
|
3164
3412
|
const title = (input.title ?? basename2(match.path)).trim() || PLACEHOLDER_TASK_TITLE;
|
|
3165
3413
|
return this.store.create({
|
|
@@ -3172,6 +3420,14 @@ class Orchestrator {
|
|
|
3172
3420
|
vendor: input.vendor ?? DEFAULT_TASK_VENDOR
|
|
3173
3421
|
});
|
|
3174
3422
|
}
|
|
3423
|
+
async installEngineHooks(task) {
|
|
3424
|
+
if (task.kind === "main" || !task.worktreePath)
|
|
3425
|
+
return;
|
|
3426
|
+
await createEngineHookAdapter(task.vendor ?? DEFAULT_TASK_VENDOR).installTaskHooks({
|
|
3427
|
+
worktreeDir: task.worktreePath,
|
|
3428
|
+
taskId: task.id
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3175
3431
|
pendingBaseRefs = new Map;
|
|
3176
3432
|
requireTask(id) {
|
|
3177
3433
|
const task = this.store.get(id);
|
|
@@ -3194,6 +3450,7 @@ function canonPath(p) {
|
|
|
3194
3450
|
var PLACEHOLDER_TASK_TITLE = "(new task)";
|
|
3195
3451
|
var init_core = __esm(() => {
|
|
3196
3452
|
init_dev();
|
|
3453
|
+
init_hook_adapter2();
|
|
3197
3454
|
init_errors();
|
|
3198
3455
|
init_slug_allocator();
|
|
3199
3456
|
});
|
|
@@ -3225,13 +3482,13 @@ function frameToLine(frame) {
|
|
|
3225
3482
|
}
|
|
3226
3483
|
var DAEMON_PROTOCOL_VERSION = 2, MIN_COMPATIBLE_PROTOCOL_VERSION = 2, CHANNEL_NAMES;
|
|
3227
3484
|
var init_protocol = __esm(() => {
|
|
3228
|
-
CHANNEL_NAMES = ["task.snapshot", "active-task", "update"];
|
|
3485
|
+
CHANNEL_NAMES = ["task.snapshot", "active-task", "update", "engine-state"];
|
|
3229
3486
|
});
|
|
3230
3487
|
|
|
3231
3488
|
// src/daemon/paths.ts
|
|
3232
3489
|
import { createHash as createHash2 } from "crypto";
|
|
3233
3490
|
import { homedir as homedir3, tmpdir } from "os";
|
|
3234
|
-
import { join as
|
|
3491
|
+
import { join as join4 } from "path";
|
|
3235
3492
|
function shortHomeTag(homeDir2) {
|
|
3236
3493
|
return createHash2("sha1").update(homeDir2).digest("hex").slice(0, 8);
|
|
3237
3494
|
}
|
|
@@ -3240,7 +3497,7 @@ function fitSocketPath(naturalPath, homeDir2, role, pidTag) {
|
|
|
3240
3497
|
return naturalPath;
|
|
3241
3498
|
const tag = shortHomeTag(homeDir2);
|
|
3242
3499
|
const suffix = pidTag === undefined ? "" : `-${pidTag}`;
|
|
3243
|
-
const fallback =
|
|
3500
|
+
const fallback = join4(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
|
|
3244
3501
|
if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
|
|
3245
3502
|
return fallback;
|
|
3246
3503
|
throw new Error(`kobe socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
|
|
@@ -3251,33 +3508,33 @@ function defaultDaemonSocketPath(homeDir2) {
|
|
|
3251
3508
|
return override;
|
|
3252
3509
|
const explicit = homeDir2 ?? process.env.KOBE_HOME_DIR;
|
|
3253
3510
|
if (explicit && explicit.length > 0) {
|
|
3254
|
-
return fitSocketPath(
|
|
3511
|
+
return fitSocketPath(join4(explicit, ".kobe", "daemon.sock"), explicit, "daemon");
|
|
3255
3512
|
}
|
|
3256
3513
|
const runtimeDir = process.env.XDG_RUNTIME_DIR;
|
|
3257
3514
|
if (runtimeDir && runtimeDir.length > 0) {
|
|
3258
|
-
return fitSocketPath(
|
|
3515
|
+
return fitSocketPath(join4(runtimeDir, "kobe.sock"), runtimeDir, "daemon");
|
|
3259
3516
|
}
|
|
3260
3517
|
const home = homedir3();
|
|
3261
|
-
return fitSocketPath(
|
|
3518
|
+
return fitSocketPath(join4(home, ".kobe", "daemon.sock"), home, "daemon");
|
|
3262
3519
|
}
|
|
3263
3520
|
function defaultDaemonPidPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir3()) {
|
|
3264
3521
|
const override = process.env.KOBE_DAEMON_PID_PATH;
|
|
3265
3522
|
if (override && override.length > 0)
|
|
3266
3523
|
return override;
|
|
3267
|
-
return
|
|
3524
|
+
return join4(homeDir2, ".kobe", "daemon.pid");
|
|
3268
3525
|
}
|
|
3269
3526
|
function defaultDaemonLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir3()) {
|
|
3270
|
-
return
|
|
3527
|
+
return join4(homeDir2, ".kobe", "daemon.log");
|
|
3271
3528
|
}
|
|
3272
3529
|
function defaultClientLogPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir3()) {
|
|
3273
|
-
return
|
|
3530
|
+
return join4(homeDir2, ".kobe", "client.log");
|
|
3274
3531
|
}
|
|
3275
3532
|
var SOCKET_PATH_SAFETY_LIMIT = 100;
|
|
3276
3533
|
var init_paths2 = () => {};
|
|
3277
3534
|
|
|
3278
3535
|
// src/client/client-log.ts
|
|
3279
|
-
import { appendFile, mkdir as
|
|
3280
|
-
import { dirname as
|
|
3536
|
+
import { appendFile as appendFile2, mkdir as mkdir3 } from "fs/promises";
|
|
3537
|
+
import { dirname as dirname4 } from "path";
|
|
3281
3538
|
function setClientLogContext(ctx) {
|
|
3282
3539
|
context = ctx;
|
|
3283
3540
|
}
|
|
@@ -3309,11 +3566,11 @@ function append(line) {
|
|
|
3309
3566
|
const path3 = defaultClientLogPath();
|
|
3310
3567
|
writeChain = writeChain.then(async () => {
|
|
3311
3568
|
try {
|
|
3312
|
-
await
|
|
3569
|
+
await appendFile2(path3, line);
|
|
3313
3570
|
} catch (err) {
|
|
3314
3571
|
if (err?.code === "ENOENT") {
|
|
3315
|
-
await
|
|
3316
|
-
await
|
|
3572
|
+
await mkdir3(dirname4(path3), { recursive: true });
|
|
3573
|
+
await appendFile2(path3, line);
|
|
3317
3574
|
} else {
|
|
3318
3575
|
throw err;
|
|
3319
3576
|
}
|
|
@@ -3515,6 +3772,37 @@ var init_client = __esm(() => {
|
|
|
3515
3772
|
init_client_log();
|
|
3516
3773
|
});
|
|
3517
3774
|
|
|
3775
|
+
// src/engine/hook-events.ts
|
|
3776
|
+
function isEngineActivityKind(v) {
|
|
3777
|
+
return ENGINE_ACTIVITY_KINDS.includes(v);
|
|
3778
|
+
}
|
|
3779
|
+
function reduceActivity(_prev, kind, detail) {
|
|
3780
|
+
switch (kind) {
|
|
3781
|
+
case "session-start":
|
|
3782
|
+
case "session-end":
|
|
3783
|
+
return "idle";
|
|
3784
|
+
case "turn-start":
|
|
3785
|
+
return "running";
|
|
3786
|
+
case "turn-complete":
|
|
3787
|
+
return "turn_complete";
|
|
3788
|
+
case "turn-failed":
|
|
3789
|
+
return detail?.failure === "rate_limit" || detail?.failure === "billing" ? "rate_limited" : "error";
|
|
3790
|
+
case "awaiting-input":
|
|
3791
|
+
return detail?.waiting === "permission" ? "permission_needed" : "running";
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
var ENGINE_ACTIVITY_KINDS;
|
|
3795
|
+
var init_hook_events = __esm(() => {
|
|
3796
|
+
ENGINE_ACTIVITY_KINDS = [
|
|
3797
|
+
"session-start",
|
|
3798
|
+
"turn-start",
|
|
3799
|
+
"turn-complete",
|
|
3800
|
+
"turn-failed",
|
|
3801
|
+
"awaiting-input",
|
|
3802
|
+
"session-end"
|
|
3803
|
+
];
|
|
3804
|
+
});
|
|
3805
|
+
|
|
3518
3806
|
// src/engine/claude-code-local/normalize.ts
|
|
3519
3807
|
function normalizeClaudeContent(content) {
|
|
3520
3808
|
if (typeof content === "string") {
|
|
@@ -3562,7 +3850,7 @@ function normalizeClaudeContent(content) {
|
|
|
3562
3850
|
}
|
|
3563
3851
|
|
|
3564
3852
|
// src/engine/claude-code-local/history.ts
|
|
3565
|
-
import { appendFile as
|
|
3853
|
+
import { appendFile as appendFile3, mkdir as mkdir4, readFile as readFile3, readdir, stat, unlink as unlink2, writeFile as writeFile3 } from "fs/promises";
|
|
3566
3854
|
import { homedir as homedir4 } from "os";
|
|
3567
3855
|
import path3 from "path";
|
|
3568
3856
|
function encodeCwd(cwd) {
|
|
@@ -3633,7 +3921,7 @@ function parseJsonl(raw, sessionId) {
|
|
|
3633
3921
|
} catch {
|
|
3634
3922
|
continue;
|
|
3635
3923
|
}
|
|
3636
|
-
if (!
|
|
3924
|
+
if (!isObject2(parsed))
|
|
3637
3925
|
continue;
|
|
3638
3926
|
const msg = extractMessage(parsed, sessionId);
|
|
3639
3927
|
if (msg)
|
|
@@ -3642,7 +3930,7 @@ function parseJsonl(raw, sessionId) {
|
|
|
3642
3930
|
return out;
|
|
3643
3931
|
}
|
|
3644
3932
|
function extractMessage(record, fallbackSessionId) {
|
|
3645
|
-
const inner =
|
|
3933
|
+
const inner = isObject2(record.message) ? record.message : record;
|
|
3646
3934
|
const role = inner.role;
|
|
3647
3935
|
if (role !== "user" && role !== "assistant" && role !== "system")
|
|
3648
3936
|
return null;
|
|
@@ -3655,7 +3943,7 @@ function extractMessage(record, fallbackSessionId) {
|
|
|
3655
3943
|
return usage ? { role, blocks, timestamp: ts, sessionId: sid, usage } : { role, blocks, timestamp: ts, sessionId: sid };
|
|
3656
3944
|
}
|
|
3657
3945
|
function extractUsage(v) {
|
|
3658
|
-
if (!
|
|
3946
|
+
if (!isObject2(v))
|
|
3659
3947
|
return;
|
|
3660
3948
|
const inTok = typeof v.input_tokens === "number" ? v.input_tokens : undefined;
|
|
3661
3949
|
const outTok = typeof v.output_tokens === "number" ? v.output_tokens : undefined;
|
|
@@ -3670,7 +3958,7 @@ function extractUsage(v) {
|
|
|
3670
3958
|
...cacheCreate !== undefined ? { cache_creation_input_tokens: cacheCreate } : {}
|
|
3671
3959
|
};
|
|
3672
3960
|
}
|
|
3673
|
-
function
|
|
3961
|
+
function isObject2(v) {
|
|
3674
3962
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3675
3963
|
}
|
|
3676
3964
|
var defaultDeps;
|
|
@@ -3687,7 +3975,7 @@ var init_history = __esm(() => {
|
|
|
3687
3975
|
}
|
|
3688
3976
|
},
|
|
3689
3977
|
async readFile(p) {
|
|
3690
|
-
return await
|
|
3978
|
+
return await readFile3(p, "utf8");
|
|
3691
3979
|
},
|
|
3692
3980
|
async pathExists(p) {
|
|
3693
3981
|
try {
|
|
@@ -3714,7 +4002,7 @@ function normalizeCodexContent(raw) {
|
|
|
3714
4002
|
blocks.push({ type: "text", text: item });
|
|
3715
4003
|
continue;
|
|
3716
4004
|
}
|
|
3717
|
-
if (!
|
|
4005
|
+
if (!isObject3(item))
|
|
3718
4006
|
continue;
|
|
3719
4007
|
const t = typeof item.type === "string" ? item.type : undefined;
|
|
3720
4008
|
if (t === "input_text" || t === "output_text") {
|
|
@@ -3728,7 +4016,7 @@ function normalizeCodexContent(raw) {
|
|
|
3728
4016
|
}
|
|
3729
4017
|
return blocks;
|
|
3730
4018
|
}
|
|
3731
|
-
function
|
|
4019
|
+
function isObject3(v) {
|
|
3732
4020
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3733
4021
|
}
|
|
3734
4022
|
|
|
@@ -3778,7 +4066,7 @@ function validPositive(v) {
|
|
|
3778
4066
|
}
|
|
3779
4067
|
|
|
3780
4068
|
// src/engine/codex-local/history.ts
|
|
3781
|
-
import { readFile as
|
|
4069
|
+
import { readFile as readFile4, readdir as readdir2, stat as stat2, unlink as unlink3 } from "fs/promises";
|
|
3782
4070
|
import { homedir as homedir5 } from "os";
|
|
3783
4071
|
import path4 from "path";
|
|
3784
4072
|
async function listRolloutFiles(deps = defaultDeps2) {
|
|
@@ -3913,11 +4201,11 @@ function parseJsonl2(raw, sessionId) {
|
|
|
3913
4201
|
} catch {
|
|
3914
4202
|
continue;
|
|
3915
4203
|
}
|
|
3916
|
-
if (!
|
|
4204
|
+
if (!isObject4(parsed))
|
|
3917
4205
|
continue;
|
|
3918
4206
|
if (parsed.type !== "response_item")
|
|
3919
4207
|
continue;
|
|
3920
|
-
const payload =
|
|
4208
|
+
const payload = isObject4(parsed.payload) ? parsed.payload : undefined;
|
|
3921
4209
|
if (!payload)
|
|
3922
4210
|
continue;
|
|
3923
4211
|
const ts = typeof parsed.timestamp === "string" ? parsed.timestamp : new Date().toISOString();
|
|
@@ -4034,7 +4322,7 @@ function textFromReasoningValue(value) {
|
|
|
4034
4322
|
parts.push(entry);
|
|
4035
4323
|
continue;
|
|
4036
4324
|
}
|
|
4037
|
-
if (!
|
|
4325
|
+
if (!isObject4(entry))
|
|
4038
4326
|
continue;
|
|
4039
4327
|
const text = typeof entry.text === "string" ? entry.text : "";
|
|
4040
4328
|
if (text.length > 0)
|
|
@@ -4076,14 +4364,14 @@ function deriveCodexUsageMetrics(raw) {
|
|
|
4076
4364
|
} catch {
|
|
4077
4365
|
continue;
|
|
4078
4366
|
}
|
|
4079
|
-
if (!
|
|
4367
|
+
if (!isObject4(parsed))
|
|
4080
4368
|
continue;
|
|
4081
4369
|
const timestampMs = typeof parsed.timestamp === "string" ? parseTimestampMs(parsed.timestamp) : null;
|
|
4082
4370
|
if (parsed.type === "response_item")
|
|
4083
4371
|
continue;
|
|
4084
4372
|
if (parsed.type !== "turn.completed")
|
|
4085
4373
|
continue;
|
|
4086
|
-
const usage =
|
|
4374
|
+
const usage = isObject4(parsed.usage) ? parsed.usage : undefined;
|
|
4087
4375
|
if (!usage)
|
|
4088
4376
|
continue;
|
|
4089
4377
|
const snapshot = codexUsageToSnapshot(usage);
|
|
@@ -4102,7 +4390,7 @@ function parseTimestampMs(value) {
|
|
|
4102
4390
|
const ms = new Date(value).getTime();
|
|
4103
4391
|
return Number.isFinite(ms) ? ms : null;
|
|
4104
4392
|
}
|
|
4105
|
-
function
|
|
4393
|
+
function isObject4(v) {
|
|
4106
4394
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4107
4395
|
}
|
|
4108
4396
|
var defaultDeps2, UUID_AT_END, MAX_WORKTREE_SCAN = 200, MAX_MTIME_SCAN = 12;
|
|
@@ -4120,7 +4408,7 @@ var init_history2 = __esm(() => {
|
|
|
4120
4408
|
}
|
|
4121
4409
|
},
|
|
4122
4410
|
async readFile(p) {
|
|
4123
|
-
return await
|
|
4411
|
+
return await readFile4(p, "utf8");
|
|
4124
4412
|
},
|
|
4125
4413
|
stat: stat2
|
|
4126
4414
|
};
|
|
@@ -4129,16 +4417,16 @@ var init_history2 = __esm(() => {
|
|
|
4129
4417
|
|
|
4130
4418
|
// src/engine/copilot-local/usage.ts
|
|
4131
4419
|
function copilotUsageToSnapshot(value) {
|
|
4132
|
-
if (!
|
|
4420
|
+
if (!isObject5(value))
|
|
4133
4421
|
return;
|
|
4134
|
-
const modelMetrics =
|
|
4422
|
+
const modelMetrics = isObject5(value.modelMetrics) ? value.modelMetrics : undefined;
|
|
4135
4423
|
if (!modelMetrics)
|
|
4136
4424
|
return;
|
|
4137
4425
|
let input = 0;
|
|
4138
4426
|
let output = 0;
|
|
4139
4427
|
let cached = 0;
|
|
4140
4428
|
for (const metrics of Object.values(modelMetrics)) {
|
|
4141
|
-
if (!
|
|
4429
|
+
if (!isObject5(metrics) || !isObject5(metrics.usage))
|
|
4142
4430
|
continue;
|
|
4143
4431
|
input += numberOr2(metrics.usage.inputTokens, 0);
|
|
4144
4432
|
output += numberOr2(metrics.usage.outputTokens, 0);
|
|
@@ -4154,7 +4442,7 @@ function copilotUsageToSnapshot(value) {
|
|
|
4154
4442
|
...context2 > 0 ? { context_tokens: context2 } : {}
|
|
4155
4443
|
};
|
|
4156
4444
|
}
|
|
4157
|
-
function
|
|
4445
|
+
function isObject5(v) {
|
|
4158
4446
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4159
4447
|
}
|
|
4160
4448
|
function numberOr2(value, fallback) {
|
|
@@ -4162,7 +4450,7 @@ function numberOr2(value, fallback) {
|
|
|
4162
4450
|
}
|
|
4163
4451
|
|
|
4164
4452
|
// src/engine/copilot-local/history.ts
|
|
4165
|
-
import { readFile as
|
|
4453
|
+
import { readFile as readFile5, readdir as readdir3, rm, stat as stat3 } from "fs/promises";
|
|
4166
4454
|
import { homedir as homedir6 } from "os";
|
|
4167
4455
|
import path5 from "path";
|
|
4168
4456
|
async function listSessionDirs(deps = defaultDeps3) {
|
|
@@ -4259,10 +4547,10 @@ function parseEvents(raw, fallbackSessionId) {
|
|
|
4259
4547
|
} catch {
|
|
4260
4548
|
continue;
|
|
4261
4549
|
}
|
|
4262
|
-
if (!
|
|
4550
|
+
if (!isObject6(record) || typeof record.type !== "string")
|
|
4263
4551
|
continue;
|
|
4264
4552
|
const timestamp = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
|
|
4265
|
-
const data =
|
|
4553
|
+
const data = isObject6(record.data) ? record.data : {};
|
|
4266
4554
|
if (record.type === "session.start") {
|
|
4267
4555
|
const sid = typeof data.sessionId === "string" ? data.sessionId : undefined;
|
|
4268
4556
|
if (sid)
|
|
@@ -4285,7 +4573,7 @@ function parseEvents(raw, fallbackSessionId) {
|
|
|
4285
4573
|
blocks.push({ type: "text", text });
|
|
4286
4574
|
const toolRequests = Array.isArray(data.toolRequests) ? data.toolRequests : [];
|
|
4287
4575
|
for (const req of toolRequests) {
|
|
4288
|
-
if (!
|
|
4576
|
+
if (!isObject6(req))
|
|
4289
4577
|
continue;
|
|
4290
4578
|
const callId = typeof req.id === "string" ? req.id : typeof req.toolCallId === "string" ? req.toolCallId : "tool";
|
|
4291
4579
|
const name = typeof req.name === "string" ? req.name : typeof req.toolName === "string" ? req.toolName : "tool";
|
|
@@ -4322,7 +4610,7 @@ function parseEvents(raw, fallbackSessionId) {
|
|
|
4322
4610
|
}
|
|
4323
4611
|
return { messages, usageMetrics, firstUserMessage };
|
|
4324
4612
|
}
|
|
4325
|
-
function
|
|
4613
|
+
function isObject6(v) {
|
|
4326
4614
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4327
4615
|
}
|
|
4328
4616
|
var defaultDeps3, PREVIEW_CHAR_CAP = 200;
|
|
@@ -4342,7 +4630,7 @@ var init_history3 = __esm(() => {
|
|
|
4342
4630
|
}
|
|
4343
4631
|
},
|
|
4344
4632
|
async readFile(p) {
|
|
4345
|
-
return await
|
|
4633
|
+
return await readFile5(p, "utf8");
|
|
4346
4634
|
},
|
|
4347
4635
|
stat: stat3,
|
|
4348
4636
|
async rm(p) {
|
|
@@ -4404,90 +4692,6 @@ var init_auto_title = __esm(() => {
|
|
|
4404
4692
|
init_history3();
|
|
4405
4693
|
});
|
|
4406
4694
|
|
|
4407
|
-
// src/cli/invocation.ts
|
|
4408
|
-
import { fileURLToPath } from "url";
|
|
4409
|
-
function kobeCliInvocation() {
|
|
4410
|
-
const isBuilt = import.meta.url.endsWith(".js");
|
|
4411
|
-
if (isBuilt)
|
|
4412
|
-
return ["kobe"];
|
|
4413
|
-
const entry = fileURLToPath(new URL("./index.ts", import.meta.url));
|
|
4414
|
-
const preload = fileURLToPath(import.meta.resolve("@opentui/solid/preload"));
|
|
4415
|
-
return [process.execPath, "--preload", preload, "--conditions=browser", entry];
|
|
4416
|
-
}
|
|
4417
|
-
var init_invocation = () => {};
|
|
4418
|
-
|
|
4419
|
-
// src/tmux/session-layout.ts
|
|
4420
|
-
function shellQuote(s) {
|
|
4421
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
4422
|
-
}
|
|
4423
|
-
function shellQuoteArgv(argv) {
|
|
4424
|
-
return argv.map(shellQuote).join(" ");
|
|
4425
|
-
}
|
|
4426
|
-
function keepAlive(cmd) {
|
|
4427
|
-
return `${cmd}; exec "\${SHELL:-/bin/sh}"`;
|
|
4428
|
-
}
|
|
4429
|
-
function shQuote(s) {
|
|
4430
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
4431
|
-
}
|
|
4432
|
-
function homeWelcomeCommand() {
|
|
4433
|
-
const msg = "\\n No task selected\\n\\n Press N to create a task, or pick one on the left.\\n\\n";
|
|
4434
|
-
return `clear; printf ${shQuote(msg)}; exec "\${SHELL:-/bin/sh}"`;
|
|
4435
|
-
}
|
|
4436
|
-
function engineLaunchLine(engineCmd, init) {
|
|
4437
|
-
const tail = keepAlive(engineCmd);
|
|
4438
|
-
const script = init?.initScript?.trim();
|
|
4439
|
-
if (!script)
|
|
4440
|
-
return tail;
|
|
4441
|
-
const group = ["{", script, "}"].join(`
|
|
4442
|
-
`);
|
|
4443
|
-
if (init?.markerPath) {
|
|
4444
|
-
const marker = shQuote(init.markerPath);
|
|
4445
|
-
const markerDir = shQuote(markerDirOf(init.markerPath));
|
|
4446
|
-
return [
|
|
4447
|
-
`if [ ! -f ${marker} ]; then`,
|
|
4448
|
-
group,
|
|
4449
|
-
`if [ $? -eq 0 ]; then mkdir -p ${markerDir} && : > ${marker}; fi`,
|
|
4450
|
-
"fi",
|
|
4451
|
-
tail
|
|
4452
|
-
].join(`
|
|
4453
|
-
`);
|
|
4454
|
-
}
|
|
4455
|
-
return [group, tail].join(`
|
|
4456
|
-
`);
|
|
4457
|
-
}
|
|
4458
|
-
function markerDirOf(p) {
|
|
4459
|
-
const i = p.lastIndexOf("/");
|
|
4460
|
-
return i <= 0 ? "." : p.slice(0, i);
|
|
4461
|
-
}
|
|
4462
|
-
function fallbackOpsScript(cwd) {
|
|
4463
|
-
return `cd ${shellQuote(cwd)} && while :; do clear; printf "\\033[1m# %s\\033[0m\\n\\n" ${shellQuote(cwd)}; git status --short --branch 2>/dev/null | sed 's/^/ /' || true; printf "\\n"; if command -v lsd >/dev/null 2>&1; then lsd --tree --git -I node_modules -I .git --depth 2 .; elif command -v eza >/dev/null 2>&1; then eza --tree --git -L 2 -I 'node_modules|.git' .; elif command -v tree >/dev/null 2>&1; then tree -L 2 -I 'node_modules|.git'; else ls -la; fi; sleep 2; done`;
|
|
4464
|
-
}
|
|
4465
|
-
function previewWindowCommand(args) {
|
|
4466
|
-
const wt = shellQuote(args.worktree);
|
|
4467
|
-
const file = shellQuote(args.relPath);
|
|
4468
|
-
const inv = args.cliInvocation.map(shellQuote).join(" ");
|
|
4469
|
-
const fallback = `cd ${wt} && if ! git diff --quiet HEAD -- ${file} 2>/dev/null; then ` + `git diff HEAD -- ${file} | { delta --paging=always 2>/dev/null || less -R; }; ` + `else bat --style=plain --paging=always ${file} 2>/dev/null || \${PAGER:-less} ${file} 2>/dev/null || cat ${file}; fi`;
|
|
4470
|
-
return `${inv} ops --worktree ${wt} --preview ${file} || { ${fallback}; }`;
|
|
4471
|
-
}
|
|
4472
|
-
function updatePageCommand(args) {
|
|
4473
|
-
return `${shellQuoteArgv([...args.cliInvocation, "update-page"])}`;
|
|
4474
|
-
}
|
|
4475
|
-
function tasksPaneCommand(cliInvocation, opts = {}) {
|
|
4476
|
-
const argv = [...cliInvocation, "tasks"];
|
|
4477
|
-
if (opts.initialTaskId)
|
|
4478
|
-
argv.push("--initial-task-id", opts.initialTaskId);
|
|
4479
|
-
return shellQuoteArgv(argv);
|
|
4480
|
-
}
|
|
4481
|
-
function opsPaneCommand(args) {
|
|
4482
|
-
if (args.taskId && args.claudePaneId) {
|
|
4483
|
-
const inv = args.cliInvocation.map(shellQuote).join(" ");
|
|
4484
|
-
const vendorFlag = args.vendor ? ` --vendor ${shellQuote(args.vendor)}` : "";
|
|
4485
|
-
return `KOBE_FILETREE_WATCH=1 ${inv} ops --task-id ${shellQuote(args.taskId)} --worktree ${shellQuote(args.cwd)} ` + `--target-pane ${shellQuote(args.claudePaneId)}${vendorFlag} || { ${fallbackOpsScript(args.cwd)}; }`;
|
|
4486
|
-
}
|
|
4487
|
-
return fallbackOpsScript(args.cwd);
|
|
4488
|
-
}
|
|
4489
|
-
var TASKS_PANE_WIDTH = 32, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50;
|
|
4490
|
-
|
|
4491
4695
|
// src/tmux/client.ts
|
|
4492
4696
|
var exports_client = {};
|
|
4493
4697
|
__export(exports_client, {
|
|
@@ -4967,9 +5171,9 @@ class DaemonEventBus {
|
|
|
4967
5171
|
}
|
|
4968
5172
|
|
|
4969
5173
|
// src/daemon/server.ts
|
|
4970
|
-
import { mkdir as
|
|
5174
|
+
import { mkdir as mkdir5, readFile as readFile6, unlink as unlink4, writeFile as writeFile4 } from "fs/promises";
|
|
4971
5175
|
import { createServer } from "net";
|
|
4972
|
-
import { dirname as
|
|
5176
|
+
import { dirname as dirname5 } from "path";
|
|
4973
5177
|
function resolveIdleGraceMs() {
|
|
4974
5178
|
const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
|
|
4975
5179
|
if (raw === undefined)
|
|
@@ -4977,6 +5181,13 @@ function resolveIdleGraceMs() {
|
|
|
4977
5181
|
const n = Number(raw);
|
|
4978
5182
|
return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_GRACE_MS;
|
|
4979
5183
|
}
|
|
5184
|
+
function resolveEngineStateTtlMs() {
|
|
5185
|
+
const raw = process.env.KOBE_ENGINE_STATE_TTL_MS;
|
|
5186
|
+
if (raw === undefined)
|
|
5187
|
+
return DEFAULT_ENGINE_STATE_TTL_MS;
|
|
5188
|
+
const n = Number(raw);
|
|
5189
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_ENGINE_STATE_TTL_MS;
|
|
5190
|
+
}
|
|
4980
5191
|
async function startDaemonServer(orch, options = {}) {
|
|
4981
5192
|
const socketPath = options.socketPath ?? defaultDaemonSocketPath(options.homeDir);
|
|
4982
5193
|
const pidPath = options.pidPath ?? defaultDaemonPidPath(options.homeDir);
|
|
@@ -5017,8 +5228,30 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5017
5228
|
bus.onPublish((event) => {
|
|
5018
5229
|
broadcast(clients, { type: "event", name: event.channel, payload: event.payload });
|
|
5019
5230
|
});
|
|
5020
|
-
|
|
5021
|
-
|
|
5231
|
+
const activity = new Map;
|
|
5232
|
+
const ACTIVITY_STALE_MS = resolveEngineStateTtlMs();
|
|
5233
|
+
function reportActivity(taskId, kind, detail) {
|
|
5234
|
+
const prev = activity.get(taskId);
|
|
5235
|
+
if (prev?.lapse)
|
|
5236
|
+
clearTimeout(prev.lapse);
|
|
5237
|
+
const state = reduceActivity(prev?.state, kind, detail);
|
|
5238
|
+
const at = Date.now();
|
|
5239
|
+
const entry = { state, detail, at };
|
|
5240
|
+
if (state !== "idle") {
|
|
5241
|
+
entry.lapse = setTimeout(() => {
|
|
5242
|
+
const cur = activity.get(taskId);
|
|
5243
|
+
if (cur && cur.at === at) {
|
|
5244
|
+
activity.set(taskId, { state: "idle", at: Date.now() });
|
|
5245
|
+
bus.publish("engine-state", { taskId, state: "idle", at: Date.now() });
|
|
5246
|
+
}
|
|
5247
|
+
}, ACTIVITY_STALE_MS);
|
|
5248
|
+
entry.lapse.unref?.();
|
|
5249
|
+
}
|
|
5250
|
+
activity.set(taskId, entry);
|
|
5251
|
+
bus.publish("engine-state", { taskId, state, ...detail ? { detail } : {}, at });
|
|
5252
|
+
}
|
|
5253
|
+
await mkdir5(dirname5(socketPath), { recursive: true });
|
|
5254
|
+
await mkdir5(dirname5(pidPath), { recursive: true });
|
|
5022
5255
|
await unlink4(socketPath).catch(() => {});
|
|
5023
5256
|
const server = createServer((socket) => {
|
|
5024
5257
|
const client = {
|
|
@@ -5088,7 +5321,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5088
5321
|
resolve2();
|
|
5089
5322
|
});
|
|
5090
5323
|
});
|
|
5091
|
-
await
|
|
5324
|
+
await writeFile4(pidPath, `${process.pid}
|
|
5092
5325
|
`, "utf8");
|
|
5093
5326
|
async function stopSoon() {
|
|
5094
5327
|
if (stopping)
|
|
@@ -5181,6 +5414,10 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5181
5414
|
case "task.delete": {
|
|
5182
5415
|
const taskId = requireString(payload, "taskId");
|
|
5183
5416
|
await orch.deleteTask(taskId, { force: optionalBoolean(payload, "force") });
|
|
5417
|
+
const gone = activity.get(taskId);
|
|
5418
|
+
if (gone?.lapse)
|
|
5419
|
+
clearTimeout(gone.lapse);
|
|
5420
|
+
activity.delete(taskId);
|
|
5184
5421
|
return {};
|
|
5185
5422
|
}
|
|
5186
5423
|
case "task.pin": {
|
|
@@ -5218,7 +5455,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5218
5455
|
worktreePath: requireString(payload, "worktreePath"),
|
|
5219
5456
|
branch: optionalString(payload, "branch"),
|
|
5220
5457
|
vendor: optionalVendor(payload, "vendor"),
|
|
5221
|
-
title: optionalString(payload, "title")
|
|
5458
|
+
title: optionalString(payload, "title"),
|
|
5459
|
+
ifExists: optionalString(payload, "ifExists") === "return" ? "return" : "error"
|
|
5222
5460
|
});
|
|
5223
5461
|
return { task: serializeTask(task) };
|
|
5224
5462
|
}
|
|
@@ -5226,6 +5464,15 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5226
5464
|
bus.publish("active-task", { taskId: optionalString(payload, "taskId") ?? null });
|
|
5227
5465
|
return {};
|
|
5228
5466
|
}
|
|
5467
|
+
case "engine.reportEvent": {
|
|
5468
|
+
const taskId = requireString(payload, "taskId");
|
|
5469
|
+
const kind = requireString(payload, "kind");
|
|
5470
|
+
if (!isEngineActivityKind(kind))
|
|
5471
|
+
throw new Error(`unknown engine event kind: ${kind}`);
|
|
5472
|
+
const detail = optionalActivityDetail(payload);
|
|
5473
|
+
reportActivity(taskId, kind, detail);
|
|
5474
|
+
return {};
|
|
5475
|
+
}
|
|
5229
5476
|
case "subscribe": {
|
|
5230
5477
|
client.subscribed = true;
|
|
5231
5478
|
const role = payload.role === "gui" ? "gui" : "pane";
|
|
@@ -5236,6 +5483,15 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5236
5483
|
for (const event of bus.snapshot()) {
|
|
5237
5484
|
writeFrame(client, { type: "event", name: event.channel, payload: event.payload });
|
|
5238
5485
|
}
|
|
5486
|
+
for (const [taskId, entry] of activity) {
|
|
5487
|
+
if (entry.state === "idle")
|
|
5488
|
+
continue;
|
|
5489
|
+
writeFrame(client, {
|
|
5490
|
+
type: "event",
|
|
5491
|
+
name: "engine-state",
|
|
5492
|
+
payload: { taskId, state: entry.state, ...entry.detail ? { detail: entry.detail } : {}, at: entry.at }
|
|
5493
|
+
});
|
|
5494
|
+
}
|
|
5239
5495
|
return {};
|
|
5240
5496
|
}
|
|
5241
5497
|
default:
|
|
@@ -5286,7 +5542,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
5286
5542
|
}
|
|
5287
5543
|
async function readPidFile(pidPath) {
|
|
5288
5544
|
try {
|
|
5289
|
-
const raw = await
|
|
5545
|
+
const raw = await readFile6(pidPath, "utf8");
|
|
5290
5546
|
const pid = Number(raw.trim());
|
|
5291
5547
|
return Number.isFinite(pid) ? pid : null;
|
|
5292
5548
|
} catch {
|
|
@@ -5337,13 +5593,29 @@ function optionalVendor(payload, key) {
|
|
|
5337
5593
|
}
|
|
5338
5594
|
return value;
|
|
5339
5595
|
}
|
|
5340
|
-
|
|
5596
|
+
function optionalActivityDetail(payload) {
|
|
5597
|
+
const raw = payload.detail;
|
|
5598
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
5599
|
+
return;
|
|
5600
|
+
const d = raw;
|
|
5601
|
+
const out = {};
|
|
5602
|
+
if (d.failure === "rate_limit" || d.failure === "billing" || d.failure === "other")
|
|
5603
|
+
out.failure = d.failure;
|
|
5604
|
+
if (d.waiting === "permission" || d.waiting === "input")
|
|
5605
|
+
out.waiting = d.waiting;
|
|
5606
|
+
if (typeof d.note === "string")
|
|
5607
|
+
out.note = d.note;
|
|
5608
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
5609
|
+
}
|
|
5610
|
+
var DEFAULT_UPDATE_POLL_MS, DEFAULT_IDLE_GRACE_MS = 3000, DEFAULT_ENGINE_STATE_TTL_MS;
|
|
5341
5611
|
var init_server = __esm(() => {
|
|
5612
|
+
init_hook_events();
|
|
5342
5613
|
init_version();
|
|
5343
5614
|
init_auto_title_poller();
|
|
5344
5615
|
init_paths2();
|
|
5345
5616
|
init_protocol();
|
|
5346
5617
|
DEFAULT_UPDATE_POLL_MS = 6 * 60 * 60 * 1000;
|
|
5618
|
+
DEFAULT_ENGINE_STATE_TTL_MS = 10 * 60 * 1000;
|
|
5347
5619
|
});
|
|
5348
5620
|
|
|
5349
5621
|
// src/daemon/lifecycle.ts
|
|
@@ -5411,14 +5683,14 @@ __export(exports_daemon_process, {
|
|
|
5411
5683
|
connectIfRunning: () => connectIfRunning
|
|
5412
5684
|
});
|
|
5413
5685
|
import { spawn } from "child_process";
|
|
5414
|
-
import { closeSync, existsSync, mkdirSync as mkdirSync2, openSync } from "fs";
|
|
5415
|
-
import { dirname as
|
|
5686
|
+
import { closeSync, existsSync as existsSync2, mkdirSync as mkdirSync2, openSync } from "fs";
|
|
5687
|
+
import { dirname as dirname6, resolve as resolve2 } from "path";
|
|
5416
5688
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5417
5689
|
function spawnDetachedDaemon(command, args, env, logPath) {
|
|
5418
5690
|
let stdio = "ignore";
|
|
5419
5691
|
let logFd;
|
|
5420
5692
|
try {
|
|
5421
|
-
mkdirSync2(
|
|
5693
|
+
mkdirSync2(dirname6(logPath), { recursive: true });
|
|
5422
5694
|
logFd = openSync(logPath, "a");
|
|
5423
5695
|
stdio = ["ignore", logFd, logFd];
|
|
5424
5696
|
} catch {
|
|
@@ -5485,12 +5757,12 @@ function resolveKobeSpawn(subcommand) {
|
|
|
5485
5757
|
if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
|
|
5486
5758
|
return [process.execPath, ...subcommand];
|
|
5487
5759
|
}
|
|
5488
|
-
const dir =
|
|
5760
|
+
const dir = dirname6(here);
|
|
5489
5761
|
const sourceEntry = resolve2(dir, "../cli/index.ts");
|
|
5490
|
-
if (
|
|
5762
|
+
if (existsSync2(sourceEntry))
|
|
5491
5763
|
return [process.execPath, sourceEntry, ...subcommand];
|
|
5492
5764
|
const distEntry = resolve2(dir, "../cli/index.js");
|
|
5493
|
-
if (
|
|
5765
|
+
if (existsSync2(distEntry))
|
|
5494
5766
|
return [process.execPath, distEntry, ...subcommand];
|
|
5495
5767
|
throw new Error(`kobe: could not locate kobe entry near ${dir}; expected ../cli/index.{ts,js}`);
|
|
5496
5768
|
}
|
|
@@ -5581,14 +5853,14 @@ async function runRepoSubcommand(args) {
|
|
|
5581
5853
|
return;
|
|
5582
5854
|
}
|
|
5583
5855
|
const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
5584
|
-
const { existsSync:
|
|
5585
|
-
const { join:
|
|
5856
|
+
const { existsSync: existsSync3 } = await import("fs");
|
|
5857
|
+
const { join: join5 } = await import("path");
|
|
5586
5858
|
if (verb === "show") {
|
|
5587
5859
|
const [pathArg] = rest.filter((a) => !a.startsWith("-"));
|
|
5588
5860
|
const repo = resolveRepoRoot2(resolve3(process.cwd(), pathArg ?? "."));
|
|
5589
5861
|
const override = getRepoInitOverride2(repo);
|
|
5590
|
-
const hasFileScript =
|
|
5591
|
-
const hasFilePrompt =
|
|
5862
|
+
const hasFileScript = existsSync3(join5(repo, ".kobe", "init.sh"));
|
|
5863
|
+
const hasFilePrompt = existsSync3(join5(repo, ".kobe", "init-prompt.md"));
|
|
5592
5864
|
console.log(`repo: ${repo}`);
|
|
5593
5865
|
console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
|
|
5594
5866
|
console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
|
|
@@ -6247,14 +6519,14 @@ var exports_repo_init = {};
|
|
|
6247
6519
|
__export(exports_repo_init, {
|
|
6248
6520
|
resolveRepoInit: () => resolveRepoInit
|
|
6249
6521
|
});
|
|
6250
|
-
import { existsSync as
|
|
6251
|
-
import { join as
|
|
6522
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
6523
|
+
import { join as join5 } from "path";
|
|
6252
6524
|
function repoFileScript(worktreePath) {
|
|
6253
|
-
return
|
|
6525
|
+
return existsSync3(join5(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
|
|
6254
6526
|
}
|
|
6255
6527
|
function repoFilePrompt(worktreePath) {
|
|
6256
|
-
const p =
|
|
6257
|
-
if (!
|
|
6528
|
+
const p = join5(worktreePath, INIT_PROMPT_REL);
|
|
6529
|
+
if (!existsSync3(p))
|
|
6258
6530
|
return;
|
|
6259
6531
|
try {
|
|
6260
6532
|
const text = readFileSync3(p, "utf8");
|
|
@@ -6275,8 +6547,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
|
|
|
6275
6547
|
var INIT_SCRIPT_REL, INIT_PROMPT_REL;
|
|
6276
6548
|
var init_repo_init = __esm(() => {
|
|
6277
6549
|
init_repos();
|
|
6278
|
-
INIT_SCRIPT_REL =
|
|
6279
|
-
INIT_PROMPT_REL =
|
|
6550
|
+
INIT_SCRIPT_REL = join5(".kobe", "init.sh");
|
|
6551
|
+
INIT_PROMPT_REL = join5(".kobe", "init-prompt.md");
|
|
6280
6552
|
});
|
|
6281
6553
|
|
|
6282
6554
|
// src/tui/panes/sidebar/worktree-changes.ts
|
|
@@ -7288,9 +7560,9 @@ var init_schema = () => {};
|
|
|
7288
7560
|
|
|
7289
7561
|
// src/tui/context/theme/loader.ts
|
|
7290
7562
|
import { readFileSync as readFileSync4, readdirSync } from "fs";
|
|
7291
|
-
import { join as
|
|
7563
|
+
import { join as join6 } from "path";
|
|
7292
7564
|
function userThemesDir() {
|
|
7293
|
-
return
|
|
7565
|
+
return join6(kobeStateDir(), "themes");
|
|
7294
7566
|
}
|
|
7295
7567
|
function loadUserThemes() {
|
|
7296
7568
|
const dir = userThemesDir();
|
|
@@ -7304,7 +7576,7 @@ function loadUserThemes() {
|
|
|
7304
7576
|
for (const file of entries) {
|
|
7305
7577
|
if (!file.endsWith(".json"))
|
|
7306
7578
|
continue;
|
|
7307
|
-
const path6 =
|
|
7579
|
+
const path6 = join6(dir, file);
|
|
7308
7580
|
let parsed;
|
|
7309
7581
|
try {
|
|
7310
7582
|
const text = readFileSync4(path6, "utf8");
|
|
@@ -7334,8 +7606,8 @@ var exports_theme = {};
|
|
|
7334
7606
|
__export(exports_theme, {
|
|
7335
7607
|
runThemeSubcommand: () => runThemeSubcommand
|
|
7336
7608
|
});
|
|
7337
|
-
import { existsSync as
|
|
7338
|
-
import { basename as basename3, join as
|
|
7609
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
7610
|
+
import { basename as basename3, join as join7, resolve as resolve5 } from "path";
|
|
7339
7611
|
function fail2(message) {
|
|
7340
7612
|
process.stderr.write(`kobe theme: ${message}
|
|
7341
7613
|
`);
|
|
@@ -7366,7 +7638,7 @@ function listThemes() {
|
|
|
7366
7638
|
} else {
|
|
7367
7639
|
for (const f of userFiles) {
|
|
7368
7640
|
const name = f.slice(0, -".json".length);
|
|
7369
|
-
const path6 =
|
|
7641
|
+
const path6 = join7(dir, f);
|
|
7370
7642
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
7371
7643
|
lines.push(` ${name}${overridesBundled} ${path6}`);
|
|
7372
7644
|
}
|
|
@@ -7456,8 +7728,8 @@ async function addTheme(args) {
|
|
|
7456
7728
|
}
|
|
7457
7729
|
const dir = userThemesDir();
|
|
7458
7730
|
mkdirSync3(dir, { recursive: true });
|
|
7459
|
-
const dest =
|
|
7460
|
-
if (
|
|
7731
|
+
const dest = join7(dir, `${name}.json`);
|
|
7732
|
+
if (existsSync4(dest) && !opts.force) {
|
|
7461
7733
|
fail2(`${dest} already exists (pass --force to overwrite)`);
|
|
7462
7734
|
}
|
|
7463
7735
|
writeFileSync2(dest, `${JSON.stringify(result.theme, null, 2)}
|
|
@@ -7474,8 +7746,8 @@ function removeTheme(args) {
|
|
|
7474
7746
|
if (BUNDLED_NAMES.includes(name)) {
|
|
7475
7747
|
fail2(`"${name}" is a built-in theme and cannot be removed`);
|
|
7476
7748
|
}
|
|
7477
|
-
const dest =
|
|
7478
|
-
if (!
|
|
7749
|
+
const dest = join7(userThemesDir(), `${name}.json`);
|
|
7750
|
+
if (!existsSync4(dest)) {
|
|
7479
7751
|
fail2(`no user theme named "${name}" (looked for ${dest})`);
|
|
7480
7752
|
}
|
|
7481
7753
|
unlinkSync(dest);
|
|
@@ -7658,9 +7930,9 @@ var init_daemon_cmd = __esm(() => {
|
|
|
7658
7930
|
});
|
|
7659
7931
|
|
|
7660
7932
|
// src/lib/skill-install.ts
|
|
7661
|
-
import { existsSync as
|
|
7933
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
7662
7934
|
import { homedir as homedir9 } from "os";
|
|
7663
|
-
import { join as
|
|
7935
|
+
import { join as join8 } from "path";
|
|
7664
7936
|
function npxSkillsArgv(opts = {}) {
|
|
7665
7937
|
return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
|
|
7666
7938
|
}
|
|
@@ -7670,14 +7942,14 @@ function npxSkillsCommand(opts = {}) {
|
|
|
7670
7942
|
function kobeSkillPaths(opts = {}) {
|
|
7671
7943
|
const home = opts.home ?? homedir9();
|
|
7672
7944
|
const cwd = opts.cwd ?? process.cwd();
|
|
7673
|
-
return [
|
|
7945
|
+
return [join8(home, SKILL_REL_PATH), join8(cwd, SKILL_REL_PATH)];
|
|
7674
7946
|
}
|
|
7675
7947
|
function parseSkillVersion(content) {
|
|
7676
7948
|
const m = content.match(/kobe-skill-version:\s*(\d+)/);
|
|
7677
7949
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
7678
7950
|
}
|
|
7679
7951
|
function kobeSkillState(opts) {
|
|
7680
|
-
const path6 = kobeSkillPaths(opts).find((p) =>
|
|
7952
|
+
const path6 = kobeSkillPaths(opts).find((p) => existsSync5(p));
|
|
7681
7953
|
if (!path6) {
|
|
7682
7954
|
return { installed: false, installedVersion: null, currentVersion: KOBE_SKILL_VERSION, stale: false };
|
|
7683
7955
|
}
|
|
@@ -7729,9 +8001,9 @@ __export(exports_maintenance, {
|
|
|
7729
8001
|
runReloadSubcommand: () => runReloadSubcommand,
|
|
7730
8002
|
runDoctorSubcommand: () => runDoctorSubcommand
|
|
7731
8003
|
});
|
|
7732
|
-
import { existsSync as
|
|
8004
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7, statSync } from "fs";
|
|
7733
8005
|
import { unlink as unlink6 } from "fs/promises";
|
|
7734
|
-
import { join as
|
|
8006
|
+
import { join as join9 } from "path";
|
|
7735
8007
|
import { createInterface } from "readline";
|
|
7736
8008
|
function isProcessAlive2(pid) {
|
|
7737
8009
|
try {
|
|
@@ -7828,7 +8100,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
7828
8100
|
const socketPath = defaultDaemonSocketPath();
|
|
7829
8101
|
const pidPath = defaultDaemonPidPath();
|
|
7830
8102
|
const logPath = defaultDaemonLogPath();
|
|
7831
|
-
const tasksPath =
|
|
8103
|
+
const tasksPath = join9(kobeStateDir(), "tasks.json");
|
|
7832
8104
|
const statePath2 = kvStatePath();
|
|
7833
8105
|
const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
|
|
7834
8106
|
const status = await probeDaemonStatus(socketPath);
|
|
@@ -7849,7 +8121,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
7849
8121
|
} else {
|
|
7850
8122
|
out.push("daemon: \u2717 not running (no pidfile)");
|
|
7851
8123
|
}
|
|
7852
|
-
if (
|
|
8124
|
+
if (existsSync6(socketPath))
|
|
7853
8125
|
out.push(` orphan socket file present: ${socketPath}`);
|
|
7854
8126
|
const tail = tailFile(logPath, 8);
|
|
7855
8127
|
if (tail) {
|
|
@@ -7940,7 +8212,7 @@ async function runResetSubcommand(argv) {
|
|
|
7940
8212
|
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
7941
8213
|
const socketPath = defaultDaemonSocketPath();
|
|
7942
8214
|
const pidPath = defaultDaemonPidPath();
|
|
7943
|
-
const tasksPath =
|
|
8215
|
+
const tasksPath = join9(kobeStateDir(), "tasks.json");
|
|
7944
8216
|
const statePath2 = kvStatePath();
|
|
7945
8217
|
console.log("kobe reset will:");
|
|
7946
8218
|
console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
|
|
@@ -8143,6 +8415,175 @@ var init_skill_cmd = __esm(() => {
|
|
|
8143
8415
|
SKILL_VERBS = ["install", "status", "command"];
|
|
8144
8416
|
});
|
|
8145
8417
|
|
|
8418
|
+
// src/cli/hook-cmd.ts
|
|
8419
|
+
var exports_hook_cmd = {};
|
|
8420
|
+
__export(exports_hook_cmd, {
|
|
8421
|
+
runHookSubcommand: () => runHookSubcommand
|
|
8422
|
+
});
|
|
8423
|
+
import { homedir as homedir10 } from "os";
|
|
8424
|
+
import { dirname as dirname7, join as join10, resolve as resolve6 } from "path";
|
|
8425
|
+
async function readStdinPayload() {
|
|
8426
|
+
try {
|
|
8427
|
+
const text = await Promise.race([
|
|
8428
|
+
Bun.stdin.text(),
|
|
8429
|
+
new Promise((resolve7) => setTimeout(() => resolve7(""), 500))
|
|
8430
|
+
]);
|
|
8431
|
+
if (!text.trim())
|
|
8432
|
+
return {};
|
|
8433
|
+
const parsed = JSON.parse(text);
|
|
8434
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
8435
|
+
} catch {
|
|
8436
|
+
return {};
|
|
8437
|
+
}
|
|
8438
|
+
}
|
|
8439
|
+
function failureFromErrorType(errorType) {
|
|
8440
|
+
if (typeof errorType !== "string")
|
|
8441
|
+
return "other";
|
|
8442
|
+
if (errorType === "rate_limit" || errorType === "overloaded")
|
|
8443
|
+
return "rate_limit";
|
|
8444
|
+
if (errorType === "billing_error")
|
|
8445
|
+
return "billing";
|
|
8446
|
+
return "other";
|
|
8447
|
+
}
|
|
8448
|
+
function flagValue(argv, name) {
|
|
8449
|
+
for (let i = 0;i < argv.length; i++) {
|
|
8450
|
+
if (argv[i] === name)
|
|
8451
|
+
return argv[i + 1];
|
|
8452
|
+
if (argv[i].startsWith(`${name}=`))
|
|
8453
|
+
return argv[i].slice(name.length + 1);
|
|
8454
|
+
}
|
|
8455
|
+
return;
|
|
8456
|
+
}
|
|
8457
|
+
async function runHookSubcommand(argv) {
|
|
8458
|
+
const [verb, ...rest] = argv;
|
|
8459
|
+
if (verb === "setup") {
|
|
8460
|
+
await runHookSetup(rest);
|
|
8461
|
+
return;
|
|
8462
|
+
}
|
|
8463
|
+
try {
|
|
8464
|
+
if (verb === "worktree-created") {
|
|
8465
|
+
await reportWorktreeCreated();
|
|
8466
|
+
return;
|
|
8467
|
+
}
|
|
8468
|
+
if (!verb || !isEngineActivityKind(verb))
|
|
8469
|
+
return;
|
|
8470
|
+
const taskId = flagValue(rest, "--task-id");
|
|
8471
|
+
if (!taskId)
|
|
8472
|
+
return;
|
|
8473
|
+
const payload = await readStdinPayload();
|
|
8474
|
+
let detail;
|
|
8475
|
+
if (verb === "turn-failed") {
|
|
8476
|
+
detail = { failure: failureFromErrorType(payload.error_type) };
|
|
8477
|
+
} else if (verb === "awaiting-input") {
|
|
8478
|
+
detail = { waiting: "permission" };
|
|
8479
|
+
}
|
|
8480
|
+
const client = await connectIfRunning();
|
|
8481
|
+
if (!client)
|
|
8482
|
+
return;
|
|
8483
|
+
try {
|
|
8484
|
+
await client.request("engine.reportEvent", { taskId, kind: verb, ...detail ? { detail } : {} });
|
|
8485
|
+
} finally {
|
|
8486
|
+
client.close();
|
|
8487
|
+
}
|
|
8488
|
+
} catch {}
|
|
8489
|
+
}
|
|
8490
|
+
async function reportWorktreeCreated() {
|
|
8491
|
+
const payload = await readStdinPayload();
|
|
8492
|
+
const worktreePath = typeof payload.worktree_path === "string" ? payload.worktree_path : undefined;
|
|
8493
|
+
if (!worktreePath)
|
|
8494
|
+
return;
|
|
8495
|
+
const repo = await deriveRepoRoot(worktreePath);
|
|
8496
|
+
if (!repo)
|
|
8497
|
+
return;
|
|
8498
|
+
const client = await connectIfRunning();
|
|
8499
|
+
if (!client)
|
|
8500
|
+
return;
|
|
8501
|
+
try {
|
|
8502
|
+
await client.request("worktree.adopt", { repo, worktreePath, ifExists: "return" });
|
|
8503
|
+
} finally {
|
|
8504
|
+
client.close();
|
|
8505
|
+
}
|
|
8506
|
+
}
|
|
8507
|
+
async function deriveRepoRoot(worktreePath) {
|
|
8508
|
+
try {
|
|
8509
|
+
const proc = Bun.spawn(["git", "-C", worktreePath, "rev-parse", "--path-format=absolute", "--git-common-dir"], {
|
|
8510
|
+
stdout: "pipe",
|
|
8511
|
+
stderr: "ignore"
|
|
8512
|
+
});
|
|
8513
|
+
const [out, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
8514
|
+
if (code !== 0)
|
|
8515
|
+
return;
|
|
8516
|
+
const commonDir = out.trim();
|
|
8517
|
+
return commonDir ? dirname7(commonDir) : undefined;
|
|
8518
|
+
} catch {
|
|
8519
|
+
return;
|
|
8520
|
+
}
|
|
8521
|
+
}
|
|
8522
|
+
function syncSettingsPath(scope) {
|
|
8523
|
+
if (scope.kind === "repo")
|
|
8524
|
+
return join10(resolve6(scope.path), ".claude", "settings.json");
|
|
8525
|
+
return join10(homedir10(), ".claude", "settings.json");
|
|
8526
|
+
}
|
|
8527
|
+
function worktreeSyncAdapters() {
|
|
8528
|
+
return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsWorktreeSync());
|
|
8529
|
+
}
|
|
8530
|
+
async function runHookSetup(argv) {
|
|
8531
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
8532
|
+
process.stdout.write([
|
|
8533
|
+
"Usage: kobe hook setup [--global | --repo <path> | --off]",
|
|
8534
|
+
"",
|
|
8535
|
+
"Install (or remove with --off) the hook that syncs an external",
|
|
8536
|
+
"`claude --worktree` into kobe as a task. Default: --global (~/.claude).",
|
|
8537
|
+
""
|
|
8538
|
+
].join(`
|
|
8539
|
+
`));
|
|
8540
|
+
return;
|
|
8541
|
+
}
|
|
8542
|
+
const off = argv.includes("--off");
|
|
8543
|
+
const repoIdx = argv.indexOf("--repo");
|
|
8544
|
+
const repoPath = repoIdx !== -1 ? argv[repoIdx + 1] : undefined;
|
|
8545
|
+
if (repoIdx !== -1 && !repoPath) {
|
|
8546
|
+
process.stderr.write(`kobe hook setup: --repo requires a path
|
|
8547
|
+
`);
|
|
8548
|
+
process.exit(2);
|
|
8549
|
+
}
|
|
8550
|
+
const adapters = worktreeSyncAdapters();
|
|
8551
|
+
if (adapters.length === 0) {
|
|
8552
|
+
process.stdout.write(`kobe hook setup: no engine supports external worktree sync \u2014 nothing to do
|
|
8553
|
+
`);
|
|
8554
|
+
return;
|
|
8555
|
+
}
|
|
8556
|
+
if (off) {
|
|
8557
|
+
const prev = getPersistedString(SYNC_SETTING_KEY);
|
|
8558
|
+
const path7 = prev?.startsWith("repo:") ? syncSettingsPath({ kind: "repo", path: prev.slice(5) }) : syncSettingsPath({ kind: "global" });
|
|
8559
|
+
for (const a of adapters)
|
|
8560
|
+
await a.removeWorktreeSyncHook(path7);
|
|
8561
|
+
setPersistedString(SYNC_SETTING_KEY, "off");
|
|
8562
|
+
process.stdout.write(`kobe hook setup: external worktree sync disabled (removed from ${path7})
|
|
8563
|
+
`);
|
|
8564
|
+
return;
|
|
8565
|
+
}
|
|
8566
|
+
const scope = repoPath ? { kind: "repo", path: repoPath } : { kind: "global" };
|
|
8567
|
+
const path6 = syncSettingsPath(scope);
|
|
8568
|
+
for (const a of adapters)
|
|
8569
|
+
await a.installWorktreeSyncHook(path6);
|
|
8570
|
+
setPersistedString(SYNC_SETTING_KEY, scope.kind === "repo" ? `repo:${resolve6(scope.path)}` : "global");
|
|
8571
|
+
process.stdout.write([
|
|
8572
|
+
`kobe hook setup: external worktree sync enabled (${scope.kind}) \u2014 wrote ${path6}`,
|
|
8573
|
+
"New `claude --worktree` worktrees will appear as kobe tasks.",
|
|
8574
|
+
""
|
|
8575
|
+
].join(`
|
|
8576
|
+
`));
|
|
8577
|
+
}
|
|
8578
|
+
var SYNC_SETTING_KEY = "externalWorktreeSync";
|
|
8579
|
+
var init_hook_cmd = __esm(() => {
|
|
8580
|
+
init_daemon_process();
|
|
8581
|
+
init_hook_adapter2();
|
|
8582
|
+
init_hook_events();
|
|
8583
|
+
init_repos();
|
|
8584
|
+
init_vendor();
|
|
8585
|
+
});
|
|
8586
|
+
|
|
8146
8587
|
// ../../node_modules/.bun/entities@7.0.1/node_modules/entities/dist/esm/decode-codepoint.js
|
|
8147
8588
|
function replaceCodePoint(codePoint) {
|
|
8148
8589
|
var _a2;
|
|
@@ -9428,6 +9869,8 @@ class RemoteOrchestrator {
|
|
|
9428
9869
|
setActiveTaskSig;
|
|
9429
9870
|
updateAcc;
|
|
9430
9871
|
setUpdateSig;
|
|
9872
|
+
engineStateAcc;
|
|
9873
|
+
setEngineStateSig;
|
|
9431
9874
|
connectionStateAcc;
|
|
9432
9875
|
setConnectionState;
|
|
9433
9876
|
ensureReachable;
|
|
@@ -9438,6 +9881,7 @@ class RemoteOrchestrator {
|
|
|
9438
9881
|
const [tasks, setTasks] = createSignal([]);
|
|
9439
9882
|
const [activeTask, setActiveTask] = createSignal(null);
|
|
9440
9883
|
const [update, setUpdate] = createSignal(null);
|
|
9884
|
+
const [engineState, setEngineState] = createSignal(new Map);
|
|
9441
9885
|
const [connectionState, setConnectionState] = createSignal("online");
|
|
9442
9886
|
this.tasksAcc = tasks;
|
|
9443
9887
|
this.setTasks = (next) => setTasks(() => next);
|
|
@@ -9445,6 +9889,8 @@ class RemoteOrchestrator {
|
|
|
9445
9889
|
this.setActiveTaskSig = (next) => setActiveTask(() => next);
|
|
9446
9890
|
this.updateAcc = update;
|
|
9447
9891
|
this.setUpdateSig = (next) => setUpdate(() => next);
|
|
9892
|
+
this.engineStateAcc = engineState;
|
|
9893
|
+
this.setEngineStateSig = (next) => setEngineState(() => next);
|
|
9448
9894
|
this.connectionStateAcc = connectionState;
|
|
9449
9895
|
this.setConnectionState = (next) => setConnectionState(() => next);
|
|
9450
9896
|
this.ensureReachable = options.ensureReachable ?? ensureDaemonReachable;
|
|
@@ -9523,6 +9969,9 @@ class RemoteOrchestrator {
|
|
|
9523
9969
|
updateSignal() {
|
|
9524
9970
|
return this.updateAcc;
|
|
9525
9971
|
}
|
|
9972
|
+
engineStateSignal() {
|
|
9973
|
+
return this.engineStateAcc;
|
|
9974
|
+
}
|
|
9526
9975
|
listTasks() {
|
|
9527
9976
|
return this.tasksAcc();
|
|
9528
9977
|
}
|
|
@@ -9613,6 +10062,18 @@ class RemoteOrchestrator {
|
|
|
9613
10062
|
this.setUpdateSig(info ?? null);
|
|
9614
10063
|
return;
|
|
9615
10064
|
}
|
|
10065
|
+
if (name === "engine-state") {
|
|
10066
|
+
const p = payload;
|
|
10067
|
+
if (typeof p?.taskId !== "string" || typeof p.state !== "string")
|
|
10068
|
+
return;
|
|
10069
|
+
const next = new Map(this.engineStateAcc());
|
|
10070
|
+
if (p.state === "idle")
|
|
10071
|
+
next.delete(p.taskId);
|
|
10072
|
+
else
|
|
10073
|
+
next.set(p.taskId, { state: p.state, detail: p.detail, at: typeof p.at === "number" ? p.at : 0 });
|
|
10074
|
+
this.setEngineStateSig(next);
|
|
10075
|
+
return;
|
|
10076
|
+
}
|
|
9616
10077
|
}
|
|
9617
10078
|
}
|
|
9618
10079
|
function deserializeTask(s) {
|
|
@@ -11298,7 +11759,7 @@ function addTheme2(name, theme) {
|
|
|
11298
11759
|
}
|
|
11299
11760
|
function resolveTheme(theme, mode = "dark") {
|
|
11300
11761
|
const defs = theme.defs ?? {};
|
|
11301
|
-
function
|
|
11762
|
+
function resolve7(c, chain = []) {
|
|
11302
11763
|
if (typeof c === "string") {
|
|
11303
11764
|
if (c === "transparent" || c === "none")
|
|
11304
11765
|
return RGBA.fromInts(0, 0, 0, 0);
|
|
@@ -11310,13 +11771,13 @@ function resolveTheme(theme, mode = "dark") {
|
|
|
11310
11771
|
const next = defs[c] ?? theme.theme[c];
|
|
11311
11772
|
if (next === undefined)
|
|
11312
11773
|
return RGBA.fromInts(0, 0, 0);
|
|
11313
|
-
return
|
|
11774
|
+
return resolve7(next, [...chain, c]);
|
|
11314
11775
|
}
|
|
11315
|
-
return
|
|
11776
|
+
return resolve7(c[mode], chain);
|
|
11316
11777
|
}
|
|
11317
11778
|
const out = {};
|
|
11318
11779
|
for (const [k, v] of Object.entries(theme.theme)) {
|
|
11319
|
-
out[k] =
|
|
11780
|
+
out[k] = resolve7(v);
|
|
11320
11781
|
}
|
|
11321
11782
|
const text = out.text ?? RGBA.fromHex("#ffffff");
|
|
11322
11783
|
const background = out.background ?? RGBA.fromHex("#000000");
|
|
@@ -12095,7 +12556,7 @@ function findAvailableFolderName(parentDir, base) {
|
|
|
12095
12556
|
return trimmed;
|
|
12096
12557
|
}
|
|
12097
12558
|
function cloneRepo(url, target, onProgress) {
|
|
12098
|
-
return new Promise((
|
|
12559
|
+
return new Promise((resolve7) => {
|
|
12099
12560
|
let stderrBuf = "";
|
|
12100
12561
|
try {
|
|
12101
12562
|
const child = spawn2("git", ["clone", "--progress", url, target], {
|
|
@@ -12112,18 +12573,18 @@ function cloneRepo(url, target, onProgress) {
|
|
|
12112
12573
|
}
|
|
12113
12574
|
});
|
|
12114
12575
|
child.on("error", (err) => {
|
|
12115
|
-
|
|
12576
|
+
resolve7({ ok: false, error: err.message });
|
|
12116
12577
|
});
|
|
12117
12578
|
child.on("close", (code) => {
|
|
12118
12579
|
if (code === 0) {
|
|
12119
|
-
|
|
12580
|
+
resolve7({ ok: true, path: target });
|
|
12120
12581
|
return;
|
|
12121
12582
|
}
|
|
12122
12583
|
const tail = stderrBuf.split(/[\r\n]+/).filter((s) => s.trim().length > 0).pop() ?? `git clone exited with ${code}`;
|
|
12123
|
-
|
|
12584
|
+
resolve7({ ok: false, error: tail });
|
|
12124
12585
|
});
|
|
12125
12586
|
} catch (err) {
|
|
12126
|
-
|
|
12587
|
+
resolve7({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
12127
12588
|
}
|
|
12128
12589
|
});
|
|
12129
12590
|
}
|
|
@@ -13186,7 +13647,7 @@ var init_dialog2 = __esm(() => {
|
|
|
13186
13647
|
|
|
13187
13648
|
// src/tui/component/new-task-dialog/index.tsx
|
|
13188
13649
|
function show(dialog, defaultRepo, savedRepos, options) {
|
|
13189
|
-
return new Promise((
|
|
13650
|
+
return new Promise((resolve7) => {
|
|
13190
13651
|
dialog.replace(() => createComponent2(NewTaskDialogView, {
|
|
13191
13652
|
defaultRepo,
|
|
13192
13653
|
savedRepos,
|
|
@@ -13199,9 +13660,9 @@ function show(dialog, defaultRepo, savedRepos, options) {
|
|
|
13199
13660
|
get discoverAdoptable() {
|
|
13200
13661
|
return options?.discoverAdoptable;
|
|
13201
13662
|
},
|
|
13202
|
-
onSubmit: (v) =>
|
|
13203
|
-
onCancel: () =>
|
|
13204
|
-
}), () =>
|
|
13663
|
+
onSubmit: (v) => resolve7(v),
|
|
13664
|
+
onCancel: () => resolve7(undefined)
|
|
13665
|
+
}), () => resolve7(undefined));
|
|
13205
13666
|
dialog.setSize("medium");
|
|
13206
13667
|
});
|
|
13207
13668
|
}
|
|
@@ -13293,15 +13754,15 @@ var init_dialog3 = __esm(() => {
|
|
|
13293
13754
|
|
|
13294
13755
|
// src/tui/component/rename-task-dialog/index.tsx
|
|
13295
13756
|
function show2(dialog, currentTitle, opts = {}) {
|
|
13296
|
-
return new Promise((
|
|
13757
|
+
return new Promise((resolve7) => {
|
|
13297
13758
|
dialog.replace(() => createComponent2(RenameTaskDialogView, {
|
|
13298
13759
|
currentTitle,
|
|
13299
13760
|
get dialogTitle() {
|
|
13300
13761
|
return opts.dialogTitle;
|
|
13301
13762
|
},
|
|
13302
|
-
onSubmit: (v) =>
|
|
13303
|
-
onCancel: () =>
|
|
13304
|
-
}), () =>
|
|
13763
|
+
onSubmit: (v) => resolve7(v),
|
|
13764
|
+
onCancel: () => resolve7(undefined)
|
|
13765
|
+
}), () => resolve7(undefined));
|
|
13305
13766
|
});
|
|
13306
13767
|
}
|
|
13307
13768
|
var RenameTaskDialog;
|
|
@@ -13315,8 +13776,8 @@ var init_rename_task_dialog = __esm(() => {
|
|
|
13315
13776
|
|
|
13316
13777
|
// src/engine/claude-code-local/binary.ts
|
|
13317
13778
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
13318
|
-
import { existsSync as
|
|
13319
|
-
import { homedir as
|
|
13779
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
13780
|
+
import { homedir as homedir12 } from "os";
|
|
13320
13781
|
import path7 from "path";
|
|
13321
13782
|
async function findClaudeBinary(deps = defaultDeps4) {
|
|
13322
13783
|
const checked = [];
|
|
@@ -13389,7 +13850,7 @@ var init_binary = __esm(() => {
|
|
|
13389
13850
|
return process.env[name];
|
|
13390
13851
|
},
|
|
13391
13852
|
home() {
|
|
13392
|
-
return
|
|
13853
|
+
return homedir12();
|
|
13393
13854
|
},
|
|
13394
13855
|
which(name) {
|
|
13395
13856
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -13402,7 +13863,7 @@ var init_binary = __esm(() => {
|
|
|
13402
13863
|
return;
|
|
13403
13864
|
if (first.startsWith("claude:") && first.includes("aliased to")) {
|
|
13404
13865
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
13405
|
-
return aliasTarget &&
|
|
13866
|
+
return aliasTarget && existsSync8(aliasTarget) ? aliasTarget : undefined;
|
|
13406
13867
|
}
|
|
13407
13868
|
return first;
|
|
13408
13869
|
},
|
|
@@ -13419,8 +13880,8 @@ var init_binary = __esm(() => {
|
|
|
13419
13880
|
|
|
13420
13881
|
// src/engine/codex-local/binary.ts
|
|
13421
13882
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
13422
|
-
import { existsSync as
|
|
13423
|
-
import { homedir as
|
|
13883
|
+
import { existsSync as existsSync9, statSync as statSync4 } from "fs";
|
|
13884
|
+
import { homedir as homedir13 } from "os";
|
|
13424
13885
|
import path8 from "path";
|
|
13425
13886
|
async function findCodexBinary(deps = defaultDeps5) {
|
|
13426
13887
|
const checked = [];
|
|
@@ -13477,7 +13938,7 @@ var init_binary2 = __esm(() => {
|
|
|
13477
13938
|
return process.env[name];
|
|
13478
13939
|
},
|
|
13479
13940
|
home() {
|
|
13480
|
-
return
|
|
13941
|
+
return homedir13();
|
|
13481
13942
|
},
|
|
13482
13943
|
which(name) {
|
|
13483
13944
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -13490,7 +13951,7 @@ var init_binary2 = __esm(() => {
|
|
|
13490
13951
|
return;
|
|
13491
13952
|
if (first.startsWith("codex:") && first.includes("aliased to")) {
|
|
13492
13953
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
13493
|
-
return aliasTarget &&
|
|
13954
|
+
return aliasTarget && existsSync9(aliasTarget) ? aliasTarget : undefined;
|
|
13494
13955
|
}
|
|
13495
13956
|
return first;
|
|
13496
13957
|
},
|
|
@@ -13507,8 +13968,8 @@ var init_binary2 = __esm(() => {
|
|
|
13507
13968
|
|
|
13508
13969
|
// src/engine/copilot-local/binary.ts
|
|
13509
13970
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
13510
|
-
import { existsSync as
|
|
13511
|
-
import { homedir as
|
|
13971
|
+
import { existsSync as existsSync10, statSync as statSync5 } from "fs";
|
|
13972
|
+
import { homedir as homedir14 } from "os";
|
|
13512
13973
|
import path9 from "path";
|
|
13513
13974
|
async function findCopilotBinary(deps = defaultDeps6) {
|
|
13514
13975
|
const checked = [];
|
|
@@ -13589,7 +14050,7 @@ var init_binary3 = __esm(() => {
|
|
|
13589
14050
|
return process.env[name];
|
|
13590
14051
|
},
|
|
13591
14052
|
home() {
|
|
13592
|
-
return
|
|
14053
|
+
return homedir14();
|
|
13593
14054
|
},
|
|
13594
14055
|
which(name) {
|
|
13595
14056
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -13602,7 +14063,7 @@ var init_binary3 = __esm(() => {
|
|
|
13602
14063
|
return;
|
|
13603
14064
|
if (first.startsWith("copilot:") && first.includes("aliased to")) {
|
|
13604
14065
|
const aliasTarget = first.split("aliased to")[1]?.trim();
|
|
13605
|
-
return aliasTarget &&
|
|
14066
|
+
return aliasTarget && existsSync10(aliasTarget) ? aliasTarget : undefined;
|
|
13606
14067
|
}
|
|
13607
14068
|
return first;
|
|
13608
14069
|
},
|
|
@@ -13614,7 +14075,7 @@ var init_binary3 = __esm(() => {
|
|
|
13614
14075
|
|
|
13615
14076
|
// src/engine/account-detect.ts
|
|
13616
14077
|
import { readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
13617
|
-
import { homedir as
|
|
14078
|
+
import { homedir as homedir15 } from "os";
|
|
13618
14079
|
import path10 from "path";
|
|
13619
14080
|
function claudeGlobalConfigPath(env, home) {
|
|
13620
14081
|
const override = env("CLAUDE_CONFIG_DIR")?.trim();
|
|
@@ -13832,7 +14293,7 @@ var init_account_detect = __esm(() => {
|
|
|
13832
14293
|
return process.env[name];
|
|
13833
14294
|
},
|
|
13834
14295
|
home() {
|
|
13835
|
-
return
|
|
14296
|
+
return homedir15();
|
|
13836
14297
|
},
|
|
13837
14298
|
findClaudeBinary() {
|
|
13838
14299
|
return findClaudeBinary();
|
|
@@ -13971,18 +14432,18 @@ var init_dialog_confirm = __esm(() => {
|
|
|
13971
14432
|
init_keymap();
|
|
13972
14433
|
init_dialog();
|
|
13973
14434
|
DialogConfirm.show = (dialog, title, message, label, confirmLabel, options) => {
|
|
13974
|
-
return new Promise((
|
|
14435
|
+
return new Promise((resolve7) => {
|
|
13975
14436
|
dialog.replace(() => createComponent2(DialogConfirm, {
|
|
13976
14437
|
title,
|
|
13977
14438
|
message,
|
|
13978
|
-
onConfirm: () =>
|
|
13979
|
-
onCancel: () =>
|
|
14439
|
+
onConfirm: () => resolve7(true),
|
|
14440
|
+
onCancel: () => resolve7(false),
|
|
13980
14441
|
label,
|
|
13981
14442
|
confirmLabel,
|
|
13982
14443
|
get initialActive() {
|
|
13983
14444
|
return options?.initialActive;
|
|
13984
14445
|
}
|
|
13985
|
-
}), () =>
|
|
14446
|
+
}), () => resolve7(undefined));
|
|
13986
14447
|
dialog.setSize("small");
|
|
13987
14448
|
});
|
|
13988
14449
|
};
|
|
@@ -13990,7 +14451,7 @@ var init_dialog_confirm = __esm(() => {
|
|
|
13990
14451
|
|
|
13991
14452
|
// src/tui/component/settings-dialog/actions.ts
|
|
13992
14453
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
13993
|
-
import { join as
|
|
14454
|
+
import { join as join12 } from "path";
|
|
13994
14455
|
function hasRestartableDaemon(orchestrator) {
|
|
13995
14456
|
return orchestrator instanceof RemoteOrchestrator;
|
|
13996
14457
|
}
|
|
@@ -14007,7 +14468,7 @@ async function confirmResetState(dialog, kv, renderer) {
|
|
|
14007
14468
|
return;
|
|
14008
14469
|
kv.clear();
|
|
14009
14470
|
try {
|
|
14010
|
-
unlinkSync2(
|
|
14471
|
+
unlinkSync2(join12(homeDir(), ".kobe", "tasks.json"));
|
|
14011
14472
|
} catch (err) {
|
|
14012
14473
|
if (err.code !== "ENOENT") {
|
|
14013
14474
|
console.error("kobe: failed to delete tasks.json during reset:", err);
|
|
@@ -15298,17 +15759,17 @@ var init_settings_dialog = __esm(() => {
|
|
|
15298
15759
|
init_sections();
|
|
15299
15760
|
SettingsDialog.show = (dialog, kv, orchestrator) => {
|
|
15300
15761
|
let visualPrefsChanged = false;
|
|
15301
|
-
return new Promise((
|
|
15762
|
+
return new Promise((resolve7) => {
|
|
15302
15763
|
dialog.replace(() => createComponent2(SettingsDialog, {
|
|
15303
15764
|
kv,
|
|
15304
15765
|
orchestrator,
|
|
15305
15766
|
onVisualPrefsChange: () => {
|
|
15306
15767
|
visualPrefsChanged = true;
|
|
15307
15768
|
},
|
|
15308
|
-
onClose: () =>
|
|
15769
|
+
onClose: () => resolve7({
|
|
15309
15770
|
visualPrefsChanged
|
|
15310
15771
|
})
|
|
15311
|
-
}), () =>
|
|
15772
|
+
}), () => resolve7({
|
|
15312
15773
|
visualPrefsChanged
|
|
15313
15774
|
}));
|
|
15314
15775
|
});
|
|
@@ -15378,7 +15839,7 @@ var init_focus = __esm(() => {
|
|
|
15378
15839
|
|
|
15379
15840
|
// src/tui/context/kv.tsx
|
|
15380
15841
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
15381
|
-
import { dirname as
|
|
15842
|
+
import { dirname as dirname8 } from "path";
|
|
15382
15843
|
function loadInitial() {
|
|
15383
15844
|
const statePath2 = kvStatePath();
|
|
15384
15845
|
try {
|
|
@@ -15406,7 +15867,7 @@ var init_kv = __esm(() => {
|
|
|
15406
15867
|
function writeNow(label) {
|
|
15407
15868
|
const statePath2 = kvStatePath();
|
|
15408
15869
|
try {
|
|
15409
|
-
mkdirSync4(
|
|
15870
|
+
mkdirSync4(dirname8(statePath2), {
|
|
15410
15871
|
recursive: true
|
|
15411
15872
|
});
|
|
15412
15873
|
const tmp = `${statePath2}.tmp`;
|
|
@@ -15461,7 +15922,7 @@ var init_kv = __esm(() => {
|
|
|
15461
15922
|
}
|
|
15462
15923
|
const statePath2 = kvStatePath();
|
|
15463
15924
|
try {
|
|
15464
|
-
mkdirSync4(
|
|
15925
|
+
mkdirSync4(dirname8(statePath2), {
|
|
15465
15926
|
recursive: true
|
|
15466
15927
|
});
|
|
15467
15928
|
const tmp = `${statePath2}.tmp`;
|
|
@@ -15497,8 +15958,8 @@ var init_persisted_ui_prefs = __esm(() => {
|
|
|
15497
15958
|
|
|
15498
15959
|
// src/tui/lib/worktree-opener.ts
|
|
15499
15960
|
import { spawn as spawn3 } from "child_process";
|
|
15500
|
-
import { existsSync as
|
|
15501
|
-
import { basename as basename4, delimiter, isAbsolute, join as
|
|
15961
|
+
import { existsSync as existsSync11 } from "fs";
|
|
15962
|
+
import { basename as basename4, delimiter, isAbsolute, join as join13 } from "path";
|
|
15502
15963
|
function executableOnPath(command, env, exists) {
|
|
15503
15964
|
if (isAbsolute(command))
|
|
15504
15965
|
return exists(command);
|
|
@@ -15506,7 +15967,7 @@ function executableOnPath(command, env, exists) {
|
|
|
15506
15967
|
for (const dir of pathEnv.split(delimiter)) {
|
|
15507
15968
|
if (!dir)
|
|
15508
15969
|
continue;
|
|
15509
|
-
if (exists(
|
|
15970
|
+
if (exists(join13(dir, command)))
|
|
15510
15971
|
return true;
|
|
15511
15972
|
}
|
|
15512
15973
|
return false;
|
|
@@ -15526,7 +15987,7 @@ function labelForOverride(command) {
|
|
|
15526
15987
|
function detectWorktreeOpener(deps = {}) {
|
|
15527
15988
|
const env = deps.env ?? process.env;
|
|
15528
15989
|
const platform = deps.platform ?? process.platform;
|
|
15529
|
-
const exists = deps.exists ??
|
|
15990
|
+
const exists = deps.exists ?? existsSync11;
|
|
15530
15991
|
const override = env.KOBE_OPEN_EDITOR?.trim();
|
|
15531
15992
|
if (override) {
|
|
15532
15993
|
return { id: "env", label: labelForOverride(override), command: override, args: [] };
|
|
@@ -16861,7 +17322,34 @@ function Sidebar(props) {
|
|
|
16861
17322
|
return readWorktreeChanges(task.worktreePath);
|
|
16862
17323
|
});
|
|
16863
17324
|
const titleText = isMain ? repoBasename(task.repo) : task.title;
|
|
16864
|
-
const
|
|
17325
|
+
const activity = () => props.engineState?.().get(task.id)?.state;
|
|
17326
|
+
const loading = () => activity() === "running" || isLive() || !isMain && task.status === "in_progress";
|
|
17327
|
+
const activityChip = () => {
|
|
17328
|
+
switch (activity()) {
|
|
17329
|
+
case "rate_limited":
|
|
17330
|
+
return {
|
|
17331
|
+
text: "limited",
|
|
17332
|
+
tone: "warning"
|
|
17333
|
+
};
|
|
17334
|
+
case "permission_needed":
|
|
17335
|
+
return {
|
|
17336
|
+
text: "approve?",
|
|
17337
|
+
tone: "warning"
|
|
17338
|
+
};
|
|
17339
|
+
case "error":
|
|
17340
|
+
return {
|
|
17341
|
+
text: "error",
|
|
17342
|
+
tone: "error"
|
|
17343
|
+
};
|
|
17344
|
+
case "turn_complete":
|
|
17345
|
+
return {
|
|
17346
|
+
text: "done",
|
|
17347
|
+
tone: "primary"
|
|
17348
|
+
};
|
|
17349
|
+
default:
|
|
17350
|
+
return null;
|
|
17351
|
+
}
|
|
17352
|
+
};
|
|
16865
17353
|
const subtitleText = createMemo(() => {
|
|
16866
17354
|
if (task.branch.length > 0)
|
|
16867
17355
|
return truncateBranchLabel(task.branch, subtitleBudget());
|
|
@@ -17024,6 +17512,18 @@ function Sidebar(props) {
|
|
|
17024
17512
|
return _el$43;
|
|
17025
17513
|
}
|
|
17026
17514
|
}), null);
|
|
17515
|
+
insert(_el$40, createComponent2(Show, {
|
|
17516
|
+
get when() {
|
|
17517
|
+
return memo2(() => !!!loading())() && activityChip();
|
|
17518
|
+
},
|
|
17519
|
+
children: (chip) => (() => {
|
|
17520
|
+
var _el$55 = createElement("text");
|
|
17521
|
+
setProp(_el$55, "wrapMode", "none");
|
|
17522
|
+
insert(_el$55, () => chip().text);
|
|
17523
|
+
effect((_$p) => setProp(_el$55, "fg", chip().tone === "error" ? theme.error : chip().tone === "warning" ? theme.warning : theme.primary, _$p));
|
|
17524
|
+
return _el$55;
|
|
17525
|
+
})()
|
|
17526
|
+
}), null);
|
|
17027
17527
|
effect((_p$) => {
|
|
17028
17528
|
var _v$22 = barColor(), _v$23 = badgeColor(), _v$24 = TextAttributes8.BOLD, _v$25 = theme.text, _v$26 = isSelected() || isCursor() ? TextAttributes8.BOLD : undefined;
|
|
17029
17529
|
_v$22 !== _p$.e && (_p$.e = setProp(_el$39, "fg", _v$22, _p$.e));
|
|
@@ -17169,43 +17669,43 @@ function Sidebar(props) {
|
|
|
17169
17669
|
const left = createMemo(() => Math.max(0, Math.min(h().x + 2, dims().width - boxW() - 1)));
|
|
17170
17670
|
const top = createMemo(() => Math.max(0, Math.min(h().y + 1, dims().height - boxH() - 1)));
|
|
17171
17671
|
return (() => {
|
|
17172
|
-
var _el$
|
|
17173
|
-
setProp(_el$
|
|
17174
|
-
setProp(_el$
|
|
17175
|
-
setProp(_el$
|
|
17176
|
-
setProp(_el$
|
|
17177
|
-
setProp(_el$
|
|
17178
|
-
setProp(_el$
|
|
17179
|
-
insert(_el$
|
|
17672
|
+
var _el$56 = createElement("box");
|
|
17673
|
+
setProp(_el$56, "position", "absolute");
|
|
17674
|
+
setProp(_el$56, "zIndex", 2600);
|
|
17675
|
+
setProp(_el$56, "flexDirection", "column");
|
|
17676
|
+
setProp(_el$56, "border", true);
|
|
17677
|
+
setProp(_el$56, "paddingLeft", 1);
|
|
17678
|
+
setProp(_el$56, "paddingRight", 1);
|
|
17679
|
+
insert(_el$56, createComponent2(For, {
|
|
17180
17680
|
get each() {
|
|
17181
17681
|
return lines();
|
|
17182
17682
|
},
|
|
17183
17683
|
children: (l) => (() => {
|
|
17184
|
-
var _el$
|
|
17185
|
-
setProp(_el$
|
|
17186
|
-
insert(_el$
|
|
17684
|
+
var _el$57 = createElement("text");
|
|
17685
|
+
setProp(_el$57, "wrapMode", "none");
|
|
17686
|
+
insert(_el$57, (() => {
|
|
17187
17687
|
var _c$4 = memo2(() => !!l.dim);
|
|
17188
17688
|
return () => _c$4() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
|
|
17189
17689
|
})());
|
|
17190
17690
|
effect((_p$) => {
|
|
17191
17691
|
var _v$35 = l.dim ? theme.textMuted : theme.text, _v$36 = l.bold ? TextAttributes8.BOLD : l.dim ? TextAttributes8.DIM : undefined;
|
|
17192
|
-
_v$35 !== _p$.e && (_p$.e = setProp(_el$
|
|
17193
|
-
_v$36 !== _p$.t && (_p$.t = setProp(_el$
|
|
17692
|
+
_v$35 !== _p$.e && (_p$.e = setProp(_el$57, "fg", _v$35, _p$.e));
|
|
17693
|
+
_v$36 !== _p$.t && (_p$.t = setProp(_el$57, "attributes", _v$36, _p$.t));
|
|
17194
17694
|
return _p$;
|
|
17195
17695
|
}, {
|
|
17196
17696
|
e: undefined,
|
|
17197
17697
|
t: undefined
|
|
17198
17698
|
});
|
|
17199
|
-
return _el$
|
|
17699
|
+
return _el$57;
|
|
17200
17700
|
})()
|
|
17201
17701
|
}));
|
|
17202
17702
|
effect((_p$) => {
|
|
17203
17703
|
var _v$30 = left(), _v$31 = top(), _v$32 = boxW(), _v$33 = theme.focusAccent, _v$34 = theme.backgroundElement;
|
|
17204
|
-
_v$30 !== _p$.e && (_p$.e = setProp(_el$
|
|
17205
|
-
_v$31 !== _p$.t && (_p$.t = setProp(_el$
|
|
17206
|
-
_v$32 !== _p$.a && (_p$.a = setProp(_el$
|
|
17207
|
-
_v$33 !== _p$.o && (_p$.o = setProp(_el$
|
|
17208
|
-
_v$34 !== _p$.i && (_p$.i = setProp(_el$
|
|
17704
|
+
_v$30 !== _p$.e && (_p$.e = setProp(_el$56, "left", _v$30, _p$.e));
|
|
17705
|
+
_v$31 !== _p$.t && (_p$.t = setProp(_el$56, "top", _v$31, _p$.t));
|
|
17706
|
+
_v$32 !== _p$.a && (_p$.a = setProp(_el$56, "width", _v$32, _p$.a));
|
|
17707
|
+
_v$33 !== _p$.o && (_p$.o = setProp(_el$56, "borderColor", _v$33, _p$.o));
|
|
17708
|
+
_v$34 !== _p$.i && (_p$.i = setProp(_el$56, "backgroundColor", _v$34, _p$.i));
|
|
17209
17709
|
return _p$;
|
|
17210
17710
|
}, {
|
|
17211
17711
|
e: undefined,
|
|
@@ -17214,7 +17714,7 @@ function Sidebar(props) {
|
|
|
17214
17714
|
o: undefined,
|
|
17215
17715
|
i: undefined
|
|
17216
17716
|
});
|
|
17217
|
-
return _el$
|
|
17717
|
+
return _el$56;
|
|
17218
17718
|
})();
|
|
17219
17719
|
}
|
|
17220
17720
|
}), null);
|
|
@@ -17300,7 +17800,7 @@ var exports_host = {};
|
|
|
17300
17800
|
__export(exports_host, {
|
|
17301
17801
|
startTasksPane: () => startTasksPane
|
|
17302
17802
|
});
|
|
17303
|
-
import { existsSync as
|
|
17803
|
+
import { existsSync as existsSync12 } from "fs";
|
|
17304
17804
|
import { TextAttributes as TextAttributes9 } from "@opentui/core";
|
|
17305
17805
|
function TasksShell(props) {
|
|
17306
17806
|
const themeCtx = useTheme();
|
|
@@ -17535,7 +18035,7 @@ function TasksShell(props) {
|
|
|
17535
18035
|
async function openSelectedWorktree(id) {
|
|
17536
18036
|
const task = props.tasks().find((t) => t.id === id);
|
|
17537
18037
|
let worktree = task?.worktreePath;
|
|
17538
|
-
if (!worktree || !
|
|
18038
|
+
if (!worktree || !existsSync12(worktree)) {
|
|
17539
18039
|
if (!props.orch) {
|
|
17540
18040
|
console.error("[kobe tasks] no daemon; cannot materialise worktree");
|
|
17541
18041
|
return;
|
|
@@ -17548,7 +18048,7 @@ function TasksShell(props) {
|
|
|
17548
18048
|
}
|
|
17549
18049
|
await props.reload();
|
|
17550
18050
|
}
|
|
17551
|
-
if (!worktree || !
|
|
18051
|
+
if (!worktree || !existsSync12(worktree))
|
|
17552
18052
|
return;
|
|
17553
18053
|
const opener = detectWorktreeOpener();
|
|
17554
18054
|
if (!opener) {
|
|
@@ -17599,7 +18099,7 @@ function TasksShell(props) {
|
|
|
17599
18099
|
const exists = await sessionExists(name);
|
|
17600
18100
|
if (exists) {
|
|
17601
18101
|
const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
|
|
17602
|
-
if (cwd2 &&
|
|
18102
|
+
if (cwd2 && existsSync12(cwd2)) {
|
|
17603
18103
|
await ensureSession({
|
|
17604
18104
|
name,
|
|
17605
18105
|
cwd: cwd2,
|
|
@@ -17613,7 +18113,7 @@ function TasksShell(props) {
|
|
|
17613
18113
|
return;
|
|
17614
18114
|
}
|
|
17615
18115
|
let cwd = task?.worktreePath;
|
|
17616
|
-
if (!cwd || !
|
|
18116
|
+
if (!cwd || !existsSync12(cwd)) {
|
|
17617
18117
|
if (!props.orch) {
|
|
17618
18118
|
console.error("[kobe tasks] no daemon; cannot materialise worktree");
|
|
17619
18119
|
return;
|
|
@@ -17626,7 +18126,7 @@ function TasksShell(props) {
|
|
|
17626
18126
|
}
|
|
17627
18127
|
await props.reload();
|
|
17628
18128
|
}
|
|
17629
|
-
if (!cwd || !
|
|
18129
|
+
if (!cwd || !existsSync12(cwd))
|
|
17630
18130
|
return;
|
|
17631
18131
|
const init2 = task?.repo ? resolveRepoInit(task.repo, cwd) : {};
|
|
17632
18132
|
const ready = await ensureSession({
|
|
@@ -17675,6 +18175,9 @@ function TasksShell(props) {
|
|
|
17675
18175
|
headerStatus,
|
|
17676
18176
|
onHeaderStatusClick: () => void openUpdate(),
|
|
17677
18177
|
width: () => dimensions().width,
|
|
18178
|
+
get engineState() {
|
|
18179
|
+
return memo2(() => !!props.orch)() ? props.orch.engineStateSignal() : undefined;
|
|
18180
|
+
},
|
|
17678
18181
|
onAddTask: () => void createTask(),
|
|
17679
18182
|
onRenameRequest: (id) => void renameTask(id),
|
|
17680
18183
|
onDeleteRequest: (id) => void deleteTask(id),
|
|
@@ -17894,6 +18397,7 @@ var init_host = __esm(() => {
|
|
|
17894
18397
|
init_solid();
|
|
17895
18398
|
init_solid();
|
|
17896
18399
|
init_solid();
|
|
18400
|
+
init_solid();
|
|
17897
18401
|
init_client2();
|
|
17898
18402
|
init_solid();
|
|
17899
18403
|
init_dev();
|
|
@@ -18244,13 +18748,13 @@ function releaseBodyLines(body) {
|
|
|
18244
18748
|
function waitForKeypress() {
|
|
18245
18749
|
if (!process.stdin.isTTY)
|
|
18246
18750
|
return Promise.resolve();
|
|
18247
|
-
return new Promise((
|
|
18751
|
+
return new Promise((resolve7) => {
|
|
18248
18752
|
const stdin = process.stdin;
|
|
18249
18753
|
const done = () => {
|
|
18250
18754
|
stdin.off("data", done);
|
|
18251
18755
|
stdin.setRawMode?.(false);
|
|
18252
18756
|
stdin.pause();
|
|
18253
|
-
|
|
18757
|
+
resolve7();
|
|
18254
18758
|
};
|
|
18255
18759
|
stdin.setRawMode?.(true);
|
|
18256
18760
|
stdin.resume();
|
|
@@ -18316,7 +18820,7 @@ function UpdatePage() {
|
|
|
18316
18820
|
}
|
|
18317
18821
|
async function runUpdater() {
|
|
18318
18822
|
setStatus("Leaving the TUI page and running the updater in this tmux window...");
|
|
18319
|
-
await new Promise((
|
|
18823
|
+
await new Promise((resolve7) => setTimeout(resolve7, 30));
|
|
18320
18824
|
renderer?.destroy();
|
|
18321
18825
|
process.stdout.write(`
|
|
18322
18826
|
kobe ${CURRENT_VERSION} -> latest
|
|
@@ -18623,7 +19127,7 @@ var init_host4 = __esm(() => {
|
|
|
18623
19127
|
});
|
|
18624
19128
|
|
|
18625
19129
|
// src/engine/turn-detector.ts
|
|
18626
|
-
import { readFile as
|
|
19130
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
18627
19131
|
|
|
18628
19132
|
class EngineTurnDetector {
|
|
18629
19133
|
supportsCompletionMarkers() {
|
|
@@ -18646,7 +19150,7 @@ function latestClaudeCompletionMarkerFromJsonl(raw, sourceId = "claude", fallbac
|
|
|
18646
19150
|
const record = parseJsonLine(line);
|
|
18647
19151
|
if (!record)
|
|
18648
19152
|
continue;
|
|
18649
|
-
const inner =
|
|
19153
|
+
const inner = isObject7(record.message) ? record.message : record;
|
|
18650
19154
|
if (inner.role !== "assistant")
|
|
18651
19155
|
continue;
|
|
18652
19156
|
if (!("content" in inner))
|
|
@@ -18688,7 +19192,7 @@ function parseJsonLine(line) {
|
|
|
18688
19192
|
return null;
|
|
18689
19193
|
try {
|
|
18690
19194
|
const parsed = JSON.parse(trimmed);
|
|
18691
|
-
return
|
|
19195
|
+
return isObject7(parsed) ? parsed : null;
|
|
18692
19196
|
} catch {
|
|
18693
19197
|
return null;
|
|
18694
19198
|
}
|
|
@@ -18697,7 +19201,7 @@ function timestampFromRecord(record, fallback) {
|
|
|
18697
19201
|
const ts = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : Number.NaN;
|
|
18698
19202
|
return Number.isFinite(ts) ? ts : fallback;
|
|
18699
19203
|
}
|
|
18700
|
-
function
|
|
19204
|
+
function isObject7(v) {
|
|
18701
19205
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
18702
19206
|
}
|
|
18703
19207
|
var ClaudeTurnDetector, CodexTurnDetector, UnknownTurnDetector;
|
|
@@ -18710,7 +19214,7 @@ var init_turn_detector = __esm(() => {
|
|
|
18710
19214
|
const files = await listSessionFilesForWorktree(worktree);
|
|
18711
19215
|
let latest = null;
|
|
18712
19216
|
for (const file of files.slice(0, 4)) {
|
|
18713
|
-
const raw = await
|
|
19217
|
+
const raw = await readFile7(file.path, "utf8").catch(() => "");
|
|
18714
19218
|
const marker = latestClaudeCompletionMarkerFromJsonl(raw, file.path, file.mtimeMs);
|
|
18715
19219
|
if (marker && (!latest || marker.timestampMs > latest.timestampMs))
|
|
18716
19220
|
latest = marker;
|
|
@@ -18729,7 +19233,7 @@ var init_turn_detector = __esm(() => {
|
|
|
18729
19233
|
if (scanned >= 12)
|
|
18730
19234
|
break;
|
|
18731
19235
|
scanned++;
|
|
18732
|
-
const raw = await
|
|
19236
|
+
const raw = await readFile7(file, "utf8").catch(() => "");
|
|
18733
19237
|
if (!raw || rolloutCwd(raw) !== worktree)
|
|
18734
19238
|
continue;
|
|
18735
19239
|
return latestCodexCompletionMarkerFromJsonl(raw, file);
|
|
@@ -18978,7 +19482,7 @@ var gitWrapper;
|
|
|
18978
19482
|
var init_git2 = __esm(() => {
|
|
18979
19483
|
gitWrapper = {
|
|
18980
19484
|
spawn(args, cwd) {
|
|
18981
|
-
return new Promise((
|
|
19485
|
+
return new Promise((resolve7, reject) => {
|
|
18982
19486
|
const child = nodeSpawn("git", [...args], {
|
|
18983
19487
|
cwd,
|
|
18984
19488
|
shell: false,
|
|
@@ -18997,7 +19501,7 @@ var init_git2 = __esm(() => {
|
|
|
18997
19501
|
});
|
|
18998
19502
|
child.on("error", reject);
|
|
18999
19503
|
child.on("close", (status, signal) => {
|
|
19000
|
-
|
|
19504
|
+
resolve7({ stdout, stderr, status, signal });
|
|
19001
19505
|
});
|
|
19002
19506
|
});
|
|
19003
19507
|
}
|
|
@@ -19051,14 +19555,14 @@ var init_keys2 = __esm(() => {
|
|
|
19051
19555
|
|
|
19052
19556
|
// src/tui/panes/filetree/open-external.ts
|
|
19053
19557
|
import { spawn as spawn5 } from "child_process";
|
|
19054
|
-
import { existsSync as
|
|
19558
|
+
import { existsSync as existsSync13 } from "fs";
|
|
19055
19559
|
import { platform } from "os";
|
|
19056
19560
|
function openExternally(absPath) {
|
|
19057
19561
|
if (!absPath)
|
|
19058
19562
|
return;
|
|
19059
19563
|
const plat = platform();
|
|
19060
19564
|
if (plat === "linux") {
|
|
19061
|
-
if (
|
|
19565
|
+
if (existsSync13("/proc/sys/fs/binfmt_misc/WSLInterop") || process.env.WSL_DISTRO_NAME) {
|
|
19062
19566
|
spawnDetached("wslview", [absPath], () => {
|
|
19063
19567
|
const child = spawn5("wslpath", ["-w", absPath], { stdio: ["ignore", "pipe", "ignore"] });
|
|
19064
19568
|
let out = "";
|
|
@@ -20449,7 +20953,7 @@ __export(exports_direct, {
|
|
|
20449
20953
|
startDirectTmux: () => startDirectTmux,
|
|
20450
20954
|
chooseInitialTask: () => chooseInitialTask
|
|
20451
20955
|
});
|
|
20452
|
-
import { resolve as
|
|
20956
|
+
import { resolve as resolve7 } from "path";
|
|
20453
20957
|
function chooseInitialTask(tasks, choice = {}) {
|
|
20454
20958
|
const byId = (id) => id ? tasks.find((t) => t.id === id) : undefined;
|
|
20455
20959
|
const active = byId(choice.activeTaskId);
|
|
@@ -20485,7 +20989,7 @@ async function ensureRepos(orchestrator) {
|
|
|
20485
20989
|
normalizeSavedRepos();
|
|
20486
20990
|
let repos = [...getSavedRepos()];
|
|
20487
20991
|
if (repos.length === 0) {
|
|
20488
|
-
const added = addSavedRepo(
|
|
20992
|
+
const added = addSavedRepo(resolve7(process.cwd()));
|
|
20489
20993
|
repos = [added.path];
|
|
20490
20994
|
}
|
|
20491
20995
|
for (const repo of repos) {
|
|
@@ -20495,7 +20999,7 @@ async function ensureRepos(orchestrator) {
|
|
|
20495
20999
|
console.error(`[kobe] ensureMainTask failed for ${repo}:`, err);
|
|
20496
21000
|
}
|
|
20497
21001
|
}
|
|
20498
|
-
return repos[0] ??
|
|
21002
|
+
return repos[0] ?? resolve7(process.cwd());
|
|
20499
21003
|
}
|
|
20500
21004
|
async function startDirectTmux() {
|
|
20501
21005
|
setClientLogContext("gui");
|
|
@@ -21092,9 +21596,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
|
|
|
21092
21596
|
var init_pulse = () => {};
|
|
21093
21597
|
|
|
21094
21598
|
// src/tui/lib/sound.ts
|
|
21095
|
-
import { existsSync as
|
|
21599
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5 } from "fs";
|
|
21096
21600
|
import { tmpdir as tmpdir2 } from "os";
|
|
21097
|
-
import { basename as basename6, isAbsolute as isAbsolute2, join as
|
|
21601
|
+
import { basename as basename6, isAbsolute as isAbsolute2, join as join14, resolve as resolve8 } from "path";
|
|
21098
21602
|
function args(player, file, volume) {
|
|
21099
21603
|
if (player === "ffplay")
|
|
21100
21604
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -21117,13 +21621,13 @@ function pickPlayer() {
|
|
|
21117
21621
|
return cachedPlayer;
|
|
21118
21622
|
const path12 = process.env.PATH ?? "";
|
|
21119
21623
|
const segments = path12.split(":").filter(Boolean);
|
|
21120
|
-
cachedPlayer = PLAYERS.find((p) => segments.some((dir) =>
|
|
21624
|
+
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join14(dir, p)))) ?? null;
|
|
21121
21625
|
return cachedPlayer;
|
|
21122
21626
|
}
|
|
21123
21627
|
async function ensureAsset() {
|
|
21124
21628
|
cachedPath ??= (async () => {
|
|
21125
21629
|
mkdirSync5(DIR, { recursive: true });
|
|
21126
|
-
const dest =
|
|
21630
|
+
const dest = join14(DIR, basename6(pulseAsset));
|
|
21127
21631
|
const out = Bun.file(dest);
|
|
21128
21632
|
if (await out.exists())
|
|
21129
21633
|
return dest;
|
|
@@ -21152,8 +21656,8 @@ function pulse(volume = 0.4) {
|
|
|
21152
21656
|
var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
21153
21657
|
var init_sound = __esm(() => {
|
|
21154
21658
|
init_pulse();
|
|
21155
|
-
pulseAsset = isAbsolute2(pulse_default) ? pulse_default :
|
|
21156
|
-
DIR =
|
|
21659
|
+
pulseAsset = isAbsolute2(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
|
|
21660
|
+
DIR = join14(tmpdir2(), "kobe-sfx");
|
|
21157
21661
|
PLAYERS = [
|
|
21158
21662
|
"ffplay",
|
|
21159
21663
|
"mpv",
|
|
@@ -21679,7 +22183,7 @@ var init_use_theme_persistence = __esm(() => {
|
|
|
21679
22183
|
});
|
|
21680
22184
|
|
|
21681
22185
|
// src/monitor/cost.ts
|
|
21682
|
-
import { readFile as
|
|
22186
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
21683
22187
|
async function summarizeTaskCost(opts) {
|
|
21684
22188
|
const files = await listSessionFilesForWorktree(opts.worktree);
|
|
21685
22189
|
const base = {
|
|
@@ -21701,7 +22205,7 @@ async function summarizeTaskCost(opts) {
|
|
|
21701
22205
|
for (const file of files) {
|
|
21702
22206
|
let raw;
|
|
21703
22207
|
try {
|
|
21704
|
-
raw = await
|
|
22208
|
+
raw = await readFile8(file.path, "utf8");
|
|
21705
22209
|
} catch {
|
|
21706
22210
|
continue;
|
|
21707
22211
|
}
|
|
@@ -22212,7 +22716,7 @@ var exports_app = {};
|
|
|
22212
22716
|
__export(exports_app, {
|
|
22213
22717
|
startApp: () => startApp
|
|
22214
22718
|
});
|
|
22215
|
-
import { homedir as
|
|
22719
|
+
import { homedir as homedir16 } from "os";
|
|
22216
22720
|
function Shell(props) {
|
|
22217
22721
|
const themeCtx = useTheme();
|
|
22218
22722
|
const {
|
|
@@ -22720,7 +23224,7 @@ async function startApp() {
|
|
|
22720
23224
|
} of loadUserThemes()) {
|
|
22721
23225
|
addTheme2(name, theme);
|
|
22722
23226
|
}
|
|
22723
|
-
const homeDir2 = process.env.KOBE_HOME_DIR ??
|
|
23227
|
+
const homeDir2 = process.env.KOBE_HOME_DIR ?? homedir16();
|
|
22724
23228
|
let orchestrator;
|
|
22725
23229
|
if (process.env.KOBE_NO_DAEMON === "1") {
|
|
22726
23230
|
const store2 = new TaskIndexStore({
|
|
@@ -22833,7 +23337,7 @@ var init_tui = __esm(() => {
|
|
|
22833
23337
|
// src/cli/index.ts
|
|
22834
23338
|
init_path_glob();
|
|
22835
23339
|
init_vendor();
|
|
22836
|
-
import { resolve as
|
|
23340
|
+
import { resolve as resolve9 } from "path";
|
|
22837
23341
|
|
|
22838
23342
|
// src/cli/usage.ts
|
|
22839
23343
|
init_version();
|
|
@@ -22882,7 +23386,7 @@ Usage: kobe add [path]
|
|
|
22882
23386
|
`);
|
|
22883
23387
|
process.exit(2);
|
|
22884
23388
|
}
|
|
22885
|
-
const target =
|
|
23389
|
+
const target = resolve9(process.cwd(), arg && arg.length > 0 ? arg : ".");
|
|
22886
23390
|
const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
22887
23391
|
const result = addSavedRepo2(target);
|
|
22888
23392
|
if (result.added) {
|
|
@@ -22981,7 +23485,7 @@ async function runAdoptSubcommand(args2) {
|
|
|
22981
23485
|
}
|
|
22982
23486
|
}
|
|
22983
23487
|
const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
22984
|
-
const repo = resolveRepoRoot2(
|
|
23488
|
+
const repo = resolveRepoRoot2(resolve9(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
|
|
22985
23489
|
const vendor = coerceVendorId(vendorArg);
|
|
22986
23490
|
const orch = await openLocalOrchestrator();
|
|
22987
23491
|
const worktrees = await orch.discoverAdoptableWorktrees(repo);
|
|
@@ -23119,6 +23623,11 @@ async function main() {
|
|
|
23119
23623
|
await runSkillSubcommand2(rest);
|
|
23120
23624
|
return;
|
|
23121
23625
|
}
|
|
23626
|
+
if (subcommand === "hook") {
|
|
23627
|
+
const { runHookSubcommand: runHookSubcommand2 } = await Promise.resolve().then(() => (init_hook_cmd(), exports_hook_cmd));
|
|
23628
|
+
await runHookSubcommand2(rest);
|
|
23629
|
+
return;
|
|
23630
|
+
}
|
|
23122
23631
|
if (subcommand === "new-chattab") {
|
|
23123
23632
|
const flags = parseOpsFlags(rest);
|
|
23124
23633
|
const session = flags.session;
|