@sma1lboy/kobe 0.7.17 → 0.7.19
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 +1116 -325
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -90,7 +90,7 @@ var init_package = __esm(() => {
|
|
|
90
90
|
package_default = {
|
|
91
91
|
$schema: "https://json.schemastore.org/package.json",
|
|
92
92
|
name: "@sma1lboy/kobe",
|
|
93
|
-
version: "0.7.
|
|
93
|
+
version: "0.7.19",
|
|
94
94
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
95
95
|
type: "module",
|
|
96
96
|
packageManager: "bun@1.3.13",
|
|
@@ -3918,7 +3918,10 @@ var init_protocol = __esm(() => {
|
|
|
3918
3918
|
"active-task",
|
|
3919
3919
|
"update",
|
|
3920
3920
|
"engine-state",
|
|
3921
|
-
"ui-prefs"
|
|
3921
|
+
"ui-prefs",
|
|
3922
|
+
"keybindings",
|
|
3923
|
+
"task.jobs",
|
|
3924
|
+
"worktree.changes"
|
|
3922
3925
|
];
|
|
3923
3926
|
});
|
|
3924
3927
|
|
|
@@ -4066,6 +4069,7 @@ class KobeDaemonClient {
|
|
|
4066
4069
|
this.disposed = true;
|
|
4067
4070
|
this.socket?.end();
|
|
4068
4071
|
this.socket = null;
|
|
4072
|
+
this.failPending();
|
|
4069
4073
|
}
|
|
4070
4074
|
forceDisconnect() {
|
|
4071
4075
|
const socket = this.socket;
|
|
@@ -4073,6 +4077,15 @@ class KobeDaemonClient {
|
|
|
4073
4077
|
return;
|
|
4074
4078
|
this.socket = null;
|
|
4075
4079
|
socket.destroy();
|
|
4080
|
+
this.failPending();
|
|
4081
|
+
}
|
|
4082
|
+
failPending() {
|
|
4083
|
+
if (this.pending.size === 0)
|
|
4084
|
+
return;
|
|
4085
|
+
const err = new Error("daemon connection closed");
|
|
4086
|
+
for (const pending of this.pending.values())
|
|
4087
|
+
pending.reject(err);
|
|
4088
|
+
this.pending.clear();
|
|
4076
4089
|
}
|
|
4077
4090
|
on(name, handler) {
|
|
4078
4091
|
let set = this.handlers.get(name);
|
|
@@ -4147,9 +4160,7 @@ class KobeDaemonClient {
|
|
|
4147
4160
|
if (this.socket !== which)
|
|
4148
4161
|
return;
|
|
4149
4162
|
this.socket = null;
|
|
4150
|
-
|
|
4151
|
-
pending.reject(new Error("daemon connection closed"));
|
|
4152
|
-
this.pending.clear();
|
|
4163
|
+
this.failPending();
|
|
4153
4164
|
this.emitLifecycle("close");
|
|
4154
4165
|
}
|
|
4155
4166
|
emitLifecycle(name) {
|
|
@@ -5131,6 +5142,16 @@ function kobeCliInvocation() {
|
|
|
5131
5142
|
var init_invocation = () => {};
|
|
5132
5143
|
|
|
5133
5144
|
// src/tmux/session-layout.ts
|
|
5145
|
+
function clampTasksPaneWidth(width) {
|
|
5146
|
+
if (!Number.isFinite(width))
|
|
5147
|
+
return TASKS_PANE_WIDTH;
|
|
5148
|
+
return Math.max(TASKS_PANE_WIDTH_MIN, Math.min(TASKS_PANE_WIDTH_MAX, Math.round(width)));
|
|
5149
|
+
}
|
|
5150
|
+
function clampPanePercent(percent) {
|
|
5151
|
+
if (!Number.isFinite(percent))
|
|
5152
|
+
return null;
|
|
5153
|
+
return Math.max(PANE_PERCENT_MIN, Math.min(PANE_PERCENT_MAX, Math.round(percent)));
|
|
5154
|
+
}
|
|
5134
5155
|
function shellQuote(s) {
|
|
5135
5156
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
5136
5157
|
}
|
|
@@ -5201,7 +5222,7 @@ function opsPaneCommand(args) {
|
|
|
5201
5222
|
}
|
|
5202
5223
|
return fallbackOpsScript(args.cwd);
|
|
5203
5224
|
}
|
|
5204
|
-
var TASKS_PANE_WIDTH = 32, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50;
|
|
5225
|
+
var TASKS_PANE_WIDTH = 32, TASKS_WIDTH_OPTION = "@kobe_tasks_width", TASKS_PANE_WIDTH_MIN = 16, TASKS_PANE_WIDTH_MAX = 120, CLAUDE_PANE_PERCENT = 60, OPS_PANE_PERCENT = 50, RIGHT_COLUMN_WIDTH_OPTION = "@kobe_right_width_pct", OPS_HEIGHT_OPTION = "@kobe_ops_height_pct", PANE_PERCENT_MIN = 10, PANE_PERCENT_MAX = 90;
|
|
5205
5226
|
|
|
5206
5227
|
// src/engine/claude-code-local/hook-adapter.ts
|
|
5207
5228
|
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -5930,7 +5951,7 @@ function parseEvents(raw, fallbackSessionId) {
|
|
|
5930
5951
|
if (!text)
|
|
5931
5952
|
continue;
|
|
5932
5953
|
if (!firstUserMessage)
|
|
5933
|
-
firstUserMessage = text.slice(0, PREVIEW_CHAR_CAP);
|
|
5954
|
+
firstUserMessage = Buffer.from(text.slice(0, PREVIEW_CHAR_CAP), "utf8").toString("utf8");
|
|
5934
5955
|
messages.push({ role: "user", blocks: [{ type: "text", text }], timestamp, sessionId });
|
|
5935
5956
|
continue;
|
|
5936
5957
|
}
|
|
@@ -6266,7 +6287,8 @@ function titleFromMessages(messages) {
|
|
|
6266
6287
|
if (!firstUser)
|
|
6267
6288
|
return "";
|
|
6268
6289
|
const text = firstUser.blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
6269
|
-
|
|
6290
|
+
const title = deriveTitleFromPrompt(text);
|
|
6291
|
+
return title.length > 0 ? Buffer.from(title, "utf8").toString("utf8") : title;
|
|
6270
6292
|
}
|
|
6271
6293
|
async function deriveTitleFromSession(worktree, vendor = DEFAULT_TASK_VENDOR) {
|
|
6272
6294
|
if (!worktree)
|
|
@@ -6317,8 +6339,10 @@ __export(exports_client, {
|
|
|
6317
6339
|
paneIdByRole: () => paneIdByRole,
|
|
6318
6340
|
newWindow: () => newWindow,
|
|
6319
6341
|
killSession: () => killSession,
|
|
6342
|
+
globalTasksPaneWidth: () => globalTasksPaneWidth,
|
|
6320
6343
|
getSessionOptions: () => getSessionOptions,
|
|
6321
6344
|
getSessionOption: () => getSessionOption,
|
|
6345
|
+
getServerOption: () => getServerOption,
|
|
6322
6346
|
ensureFallbackSession: () => ensureFallbackSession,
|
|
6323
6347
|
currentSessionName: () => currentSessionName,
|
|
6324
6348
|
claudePaneIdStrict: () => claudePaneIdStrict,
|
|
@@ -6452,6 +6476,15 @@ async function getSessionOptions(session, options) {
|
|
|
6452
6476
|
}
|
|
6453
6477
|
return values;
|
|
6454
6478
|
}
|
|
6479
|
+
async function getServerOption(option) {
|
|
6480
|
+
const { code, stdout } = await runTmuxCapturing(["show-options", "-sqv", option]);
|
|
6481
|
+
return code === 0 ? stdout.trim() : "";
|
|
6482
|
+
}
|
|
6483
|
+
async function globalTasksPaneWidth() {
|
|
6484
|
+
const raw = await getServerOption(TASKS_WIDTH_OPTION);
|
|
6485
|
+
const n = Number.parseInt(raw, 10);
|
|
6486
|
+
return Number.isFinite(n) && n > 0 ? clampTasksPaneWidth(n) : TASKS_PANE_WIDTH;
|
|
6487
|
+
}
|
|
6455
6488
|
async function tagPaneRole(paneId, role) {
|
|
6456
6489
|
await runTmux(["set-option", "-p", "-t", paneId, PANE_ROLE_OPTION, role]);
|
|
6457
6490
|
}
|
|
@@ -6551,6 +6584,7 @@ async function ensureFallbackSession() {
|
|
|
6551
6584
|
]);
|
|
6552
6585
|
const mainPane = r0.stdout.trim();
|
|
6553
6586
|
if (mainPane) {
|
|
6587
|
+
const tasksWidth = await globalTasksPaneWidth();
|
|
6554
6588
|
const r1 = await runTmuxCapturing([
|
|
6555
6589
|
"split-window",
|
|
6556
6590
|
"-h",
|
|
@@ -6558,7 +6592,7 @@ async function ensureFallbackSession() {
|
|
|
6558
6592
|
"-t",
|
|
6559
6593
|
mainPane,
|
|
6560
6594
|
"-l",
|
|
6561
|
-
`${
|
|
6595
|
+
`${tasksWidth}`,
|
|
6562
6596
|
"-c",
|
|
6563
6597
|
SAFE_SPAWN_CWD,
|
|
6564
6598
|
"-P",
|
|
@@ -7048,8 +7082,20 @@ function createDaemonHandlerRegistry() {
|
|
|
7048
7082
|
name: "task.ensureWorktree",
|
|
7049
7083
|
async handle(payload, ctx) {
|
|
7050
7084
|
const taskId = requireString(payload, "taskId");
|
|
7051
|
-
|
|
7052
|
-
|
|
7085
|
+
ctx.bus.publish("task.jobs", { taskId, kind: "ensureWorktree", phase: "running" });
|
|
7086
|
+
try {
|
|
7087
|
+
const path11 = await ctx.orch.ensureWorktree(taskId);
|
|
7088
|
+
ctx.bus.publish("task.jobs", { taskId, kind: "ensureWorktree", phase: "done" });
|
|
7089
|
+
return { worktreePath: path11 };
|
|
7090
|
+
} catch (err) {
|
|
7091
|
+
ctx.bus.publish("task.jobs", {
|
|
7092
|
+
taskId,
|
|
7093
|
+
kind: "ensureWorktree",
|
|
7094
|
+
phase: "error",
|
|
7095
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7096
|
+
});
|
|
7097
|
+
throw err;
|
|
7098
|
+
}
|
|
7053
7099
|
}
|
|
7054
7100
|
},
|
|
7055
7101
|
{
|
|
@@ -7181,12 +7227,66 @@ var init_handlers = __esm(() => {
|
|
|
7181
7227
|
init_protocol();
|
|
7182
7228
|
});
|
|
7183
7229
|
|
|
7184
|
-
// ../kobe-daemon/src/daemon/
|
|
7185
|
-
import { mkdirSync as mkdirSync2,
|
|
7230
|
+
// ../kobe-daemon/src/daemon/keybindings-watcher.ts
|
|
7231
|
+
import { mkdirSync as mkdirSync2, watch } from "fs";
|
|
7186
7232
|
import { homedir as homedir13 } from "os";
|
|
7187
7233
|
import { basename as basename3, dirname as dirname5, join as join4 } from "path";
|
|
7188
|
-
function
|
|
7189
|
-
return join4(homeDir2, ".
|
|
7234
|
+
function defaultKeybindingsPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir13()) {
|
|
7235
|
+
return join4(homeDir2, ".kobe", "settings", "keybindings.yaml");
|
|
7236
|
+
}
|
|
7237
|
+
function startKeybindingsWatcher(bus, options = {}) {
|
|
7238
|
+
const debounceMs = options.debounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS;
|
|
7239
|
+
if (debounceMs <= 0)
|
|
7240
|
+
return () => {};
|
|
7241
|
+
const filePath = options.path ?? defaultKeybindingsPath();
|
|
7242
|
+
const dir = dirname5(filePath);
|
|
7243
|
+
const baseYaml = basename3(filePath);
|
|
7244
|
+
const baseYml = baseYaml.replace(/\.yaml$/, ".yml");
|
|
7245
|
+
let rev = 0;
|
|
7246
|
+
bus.publish("keybindings", { rev });
|
|
7247
|
+
let timer = null;
|
|
7248
|
+
const bump = () => {
|
|
7249
|
+
timer = null;
|
|
7250
|
+
try {
|
|
7251
|
+
rev += 1;
|
|
7252
|
+
bus.publish("keybindings", { rev });
|
|
7253
|
+
} catch (err) {
|
|
7254
|
+
logDaemonError("keybindings-watcher", err);
|
|
7255
|
+
}
|
|
7256
|
+
};
|
|
7257
|
+
let watcher = null;
|
|
7258
|
+
try {
|
|
7259
|
+
mkdirSync2(dir, { recursive: true });
|
|
7260
|
+
watcher = watch(dir, (_event, filename) => {
|
|
7261
|
+
if (filename !== null && filename !== baseYaml && filename !== baseYml)
|
|
7262
|
+
return;
|
|
7263
|
+
if (timer)
|
|
7264
|
+
clearTimeout(timer);
|
|
7265
|
+
timer = setTimeout(bump, debounceMs);
|
|
7266
|
+
timer.unref?.();
|
|
7267
|
+
});
|
|
7268
|
+
watcher.on("error", (err) => logDaemonError("keybindings-watcher", err));
|
|
7269
|
+
} catch (err) {
|
|
7270
|
+
logDaemonError("keybindings-watcher", err);
|
|
7271
|
+
}
|
|
7272
|
+
return () => {
|
|
7273
|
+
if (timer) {
|
|
7274
|
+
clearTimeout(timer);
|
|
7275
|
+
timer = null;
|
|
7276
|
+
}
|
|
7277
|
+
watcher?.close();
|
|
7278
|
+
watcher = null;
|
|
7279
|
+
};
|
|
7280
|
+
}
|
|
7281
|
+
var DEFAULT_KEYBINDINGS_DEBOUNCE_MS = 200;
|
|
7282
|
+
var init_keybindings_watcher = () => {};
|
|
7283
|
+
|
|
7284
|
+
// ../kobe-daemon/src/daemon/ui-prefs-watcher.ts
|
|
7285
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, watch as watch2 } from "fs";
|
|
7286
|
+
import { homedir as homedir14 } from "os";
|
|
7287
|
+
import { basename as basename4, dirname as dirname6, join as join5 } from "path";
|
|
7288
|
+
function defaultUiPrefsStatePath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir14()) {
|
|
7289
|
+
return join5(homeDir2, ".config", "kobe", "state.json");
|
|
7190
7290
|
}
|
|
7191
7291
|
function readUiPrefsFromStateFile(statePath2) {
|
|
7192
7292
|
let parsed = {};
|
|
@@ -7198,18 +7298,20 @@ function readUiPrefsFromStateFile(statePath2) {
|
|
|
7198
7298
|
const theme = typeof parsed.activeTheme === "string" && parsed.activeTheme.length > 0 ? parsed.activeTheme : "claude";
|
|
7199
7299
|
const transparentBackground = parsed.transparentBackground === true;
|
|
7200
7300
|
const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOT_NAMES.includes(parsed.focusAccent) ? parsed.focusAccent : null;
|
|
7201
|
-
|
|
7301
|
+
const sortMode = parsed.activeSortMode === "recent" ? "recent" : "default";
|
|
7302
|
+
const keysCollapsed = parsed["tasksPane.keysCollapsed"] === true;
|
|
7303
|
+
return { theme, transparentBackground, focusAccent, sortMode, keysCollapsed };
|
|
7202
7304
|
}
|
|
7203
7305
|
function samePrefs(a, b) {
|
|
7204
|
-
return a.theme === b.theme && a.transparentBackground === b.transparentBackground && a.focusAccent === b.focusAccent;
|
|
7306
|
+
return a.theme === b.theme && a.transparentBackground === b.transparentBackground && a.focusAccent === b.focusAccent && a.sortMode === b.sortMode && a.keysCollapsed === b.keysCollapsed;
|
|
7205
7307
|
}
|
|
7206
7308
|
function startUiPrefsWatcher(bus, options = {}) {
|
|
7207
7309
|
const debounceMs = options.debounceMs ?? DEFAULT_UI_PREFS_DEBOUNCE_MS;
|
|
7208
7310
|
if (debounceMs <= 0)
|
|
7209
7311
|
return () => {};
|
|
7210
7312
|
const statePath2 = options.statePath ?? defaultUiPrefsStatePath();
|
|
7211
|
-
const stateDir =
|
|
7212
|
-
const stateFile =
|
|
7313
|
+
const stateDir = dirname6(statePath2);
|
|
7314
|
+
const stateFile = basename4(statePath2);
|
|
7213
7315
|
let last = readUiPrefsFromStateFile(statePath2);
|
|
7214
7316
|
bus.publish("ui-prefs", last);
|
|
7215
7317
|
let timer = null;
|
|
@@ -7227,8 +7329,8 @@ function startUiPrefsWatcher(bus, options = {}) {
|
|
|
7227
7329
|
};
|
|
7228
7330
|
let watcher = null;
|
|
7229
7331
|
try {
|
|
7230
|
-
|
|
7231
|
-
watcher =
|
|
7332
|
+
mkdirSync3(stateDir, { recursive: true });
|
|
7333
|
+
watcher = watch2(stateDir, (_event, filename) => {
|
|
7232
7334
|
if (filename !== null && filename !== stateFile)
|
|
7233
7335
|
return;
|
|
7234
7336
|
if (timer)
|
|
@@ -7254,10 +7356,236 @@ var init_ui_prefs_watcher = __esm(() => {
|
|
|
7254
7356
|
FOCUS_ACCENT_SLOT_NAMES = ["primary", "success", "info"];
|
|
7255
7357
|
});
|
|
7256
7358
|
|
|
7359
|
+
// src/lib/poll-scheduling.ts
|
|
7360
|
+
import { spawn as spawn2 } from "child_process";
|
|
7361
|
+
function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
|
|
7362
|
+
if (timedOut)
|
|
7363
|
+
return startedAt + cfg.slowRetryMs;
|
|
7364
|
+
return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
|
|
7365
|
+
}
|
|
7366
|
+
function shouldPoll(state, now) {
|
|
7367
|
+
return !state.inFlight && now >= state.nextAllowedAt;
|
|
7368
|
+
}
|
|
7369
|
+
function maybeStartScheduledRun(state, cfg, run, onValue) {
|
|
7370
|
+
const startedAt = Date.now();
|
|
7371
|
+
if (!shouldPoll(state, startedAt))
|
|
7372
|
+
return false;
|
|
7373
|
+
state.inFlight = true;
|
|
7374
|
+
const controller = new AbortController;
|
|
7375
|
+
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
7376
|
+
(async () => {
|
|
7377
|
+
let value;
|
|
7378
|
+
let ok = false;
|
|
7379
|
+
try {
|
|
7380
|
+
value = await run(controller.signal);
|
|
7381
|
+
ok = true;
|
|
7382
|
+
} catch {}
|
|
7383
|
+
clearTimeout(timer);
|
|
7384
|
+
const timedOut = controller.signal.aborted;
|
|
7385
|
+
state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
|
|
7386
|
+
state.inFlight = false;
|
|
7387
|
+
if (ok && !timedOut)
|
|
7388
|
+
onValue(value);
|
|
7389
|
+
})();
|
|
7390
|
+
return true;
|
|
7391
|
+
}
|
|
7392
|
+
function spawnCapture(cmd, args, opts) {
|
|
7393
|
+
return new Promise((resolve2) => {
|
|
7394
|
+
let out = "";
|
|
7395
|
+
let settled = false;
|
|
7396
|
+
const finish = (status) => {
|
|
7397
|
+
if (settled)
|
|
7398
|
+
return;
|
|
7399
|
+
settled = true;
|
|
7400
|
+
resolve2({ status, stdout: out });
|
|
7401
|
+
};
|
|
7402
|
+
const child = spawn2(cmd, args.slice(), {
|
|
7403
|
+
cwd: opts.cwd,
|
|
7404
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
7405
|
+
env: opts.env,
|
|
7406
|
+
signal: opts.signal,
|
|
7407
|
+
killSignal: "SIGKILL"
|
|
7408
|
+
});
|
|
7409
|
+
child.stdout?.on("data", (chunk) => {
|
|
7410
|
+
out += String(chunk);
|
|
7411
|
+
});
|
|
7412
|
+
child.on("error", () => finish(null));
|
|
7413
|
+
child.on("close", (code) => finish(code));
|
|
7414
|
+
});
|
|
7415
|
+
}
|
|
7416
|
+
var init_poll_scheduling = () => {};
|
|
7417
|
+
|
|
7418
|
+
// src/tui/panes/sidebar/worktree-changes.ts
|
|
7419
|
+
var exports_worktree_changes = {};
|
|
7420
|
+
__export(exports_worktree_changes, {
|
|
7421
|
+
sameWorktreeChanges: () => sameWorktreeChanges,
|
|
7422
|
+
readWorktreeChanges: () => readWorktreeChanges,
|
|
7423
|
+
pickPushedChanges: () => pickPushedChanges,
|
|
7424
|
+
parsePorcelain: () => parsePorcelain2
|
|
7425
|
+
});
|
|
7426
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
7427
|
+
function sameWorktreeChanges(a, b) {
|
|
7428
|
+
return a.added === b.added && a.deleted === b.deleted;
|
|
7429
|
+
}
|
|
7430
|
+
function pickPushedChanges(pushed, worktreePath) {
|
|
7431
|
+
if (!pushed)
|
|
7432
|
+
return null;
|
|
7433
|
+
return pushed.get(worktreePath) ?? ZERO;
|
|
7434
|
+
}
|
|
7435
|
+
function readWorktreeChanges(worktreePath) {
|
|
7436
|
+
if (!worktreePath)
|
|
7437
|
+
return ZERO;
|
|
7438
|
+
try {
|
|
7439
|
+
const out = spawnSync7("git", ["status", "--porcelain=v1"], {
|
|
7440
|
+
cwd: worktreePath,
|
|
7441
|
+
encoding: "utf8",
|
|
7442
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
7443
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
|
|
7444
|
+
});
|
|
7445
|
+
if (out.status !== 0 || !out.stdout)
|
|
7446
|
+
return ZERO;
|
|
7447
|
+
return parsePorcelain2(out.stdout);
|
|
7448
|
+
} catch {
|
|
7449
|
+
return ZERO;
|
|
7450
|
+
}
|
|
7451
|
+
}
|
|
7452
|
+
function parsePorcelain2(text) {
|
|
7453
|
+
let added = 0;
|
|
7454
|
+
let deleted = 0;
|
|
7455
|
+
for (const line of text.split(`
|
|
7456
|
+
`)) {
|
|
7457
|
+
if (!line || line.startsWith("##"))
|
|
7458
|
+
continue;
|
|
7459
|
+
const x = line.charAt(0);
|
|
7460
|
+
const y = line.charAt(1);
|
|
7461
|
+
if (x === "D" || y === "D")
|
|
7462
|
+
deleted += 1;
|
|
7463
|
+
else
|
|
7464
|
+
added += 1;
|
|
7465
|
+
}
|
|
7466
|
+
return { added, deleted };
|
|
7467
|
+
}
|
|
7468
|
+
var ZERO;
|
|
7469
|
+
var init_worktree_changes = __esm(() => {
|
|
7470
|
+
ZERO = { added: 0, deleted: 0 };
|
|
7471
|
+
});
|
|
7472
|
+
|
|
7473
|
+
// ../kobe-daemon/src/daemon/worktree-changes-collector.ts
|
|
7474
|
+
async function runGitStatus(worktreePath, signal) {
|
|
7475
|
+
const res = await spawnCapture("git", ["status", "--porcelain=v1"], {
|
|
7476
|
+
cwd: worktreePath,
|
|
7477
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
7478
|
+
signal
|
|
7479
|
+
});
|
|
7480
|
+
if (res.status !== 0)
|
|
7481
|
+
throw new Error("git status failed");
|
|
7482
|
+
return parsePorcelain2(res.stdout);
|
|
7483
|
+
}
|
|
7484
|
+
function trackedWorktreePaths(tasks) {
|
|
7485
|
+
const paths = new Set;
|
|
7486
|
+
for (const task of tasks) {
|
|
7487
|
+
if (task.archived)
|
|
7488
|
+
continue;
|
|
7489
|
+
if (!task.worktreePath)
|
|
7490
|
+
continue;
|
|
7491
|
+
if (isRemoteRepoKey(task.repo) || isRemoteRepoKey(task.worktreePath))
|
|
7492
|
+
continue;
|
|
7493
|
+
paths.add(task.worktreePath);
|
|
7494
|
+
}
|
|
7495
|
+
return paths;
|
|
7496
|
+
}
|
|
7497
|
+
|
|
7498
|
+
class WorktreeChangesCollector {
|
|
7499
|
+
orch;
|
|
7500
|
+
bus;
|
|
7501
|
+
options;
|
|
7502
|
+
entries = new Map;
|
|
7503
|
+
stopped = false;
|
|
7504
|
+
constructor(orch, bus, options = {}) {
|
|
7505
|
+
this.orch = orch;
|
|
7506
|
+
this.bus = bus;
|
|
7507
|
+
this.options = options;
|
|
7508
|
+
}
|
|
7509
|
+
tick() {
|
|
7510
|
+
if (this.stopped)
|
|
7511
|
+
return;
|
|
7512
|
+
try {
|
|
7513
|
+
const tracked = trackedWorktreePaths(this.orch.listTasks());
|
|
7514
|
+
let pruned = false;
|
|
7515
|
+
for (const path11 of this.entries.keys()) {
|
|
7516
|
+
if (tracked.has(path11))
|
|
7517
|
+
continue;
|
|
7518
|
+
const entry = this.entries.get(path11);
|
|
7519
|
+
if (entry?.value)
|
|
7520
|
+
pruned = true;
|
|
7521
|
+
this.entries.delete(path11);
|
|
7522
|
+
}
|
|
7523
|
+
if (pruned)
|
|
7524
|
+
this.publish();
|
|
7525
|
+
for (const path11 of tracked)
|
|
7526
|
+
this.maybeCollect(path11);
|
|
7527
|
+
} catch (err) {
|
|
7528
|
+
logDaemonError("worktree-changes", err);
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
stop() {
|
|
7532
|
+
this.stopped = true;
|
|
7533
|
+
}
|
|
7534
|
+
maybeCollect(worktreePath) {
|
|
7535
|
+
let entry = this.entries.get(worktreePath);
|
|
7536
|
+
if (!entry) {
|
|
7537
|
+
entry = { inFlight: false, nextAllowedAt: 0 };
|
|
7538
|
+
this.entries.set(worktreePath, entry);
|
|
7539
|
+
}
|
|
7540
|
+
const cadence = this.options.cadence ?? {
|
|
7541
|
+
timeoutMs: WORKTREE_CHANGES_TIMEOUT_MS,
|
|
7542
|
+
slowRetryMs: WORKTREE_CHANGES_SLOW_RETRY_MS,
|
|
7543
|
+
minIntervalMs: WORKTREE_CHANGES_MIN_INTERVAL_MS
|
|
7544
|
+
};
|
|
7545
|
+
const run = this.options.run ?? runGitStatus;
|
|
7546
|
+
maybeStartScheduledRun(entry, cadence, (signal) => run(worktreePath, signal), (value) => {
|
|
7547
|
+
if (this.stopped)
|
|
7548
|
+
return;
|
|
7549
|
+
if (this.entries.get(worktreePath) !== entry)
|
|
7550
|
+
return;
|
|
7551
|
+
if (entry.value && sameWorktreeChanges(entry.value, value))
|
|
7552
|
+
return;
|
|
7553
|
+
entry.value = value;
|
|
7554
|
+
this.publish();
|
|
7555
|
+
});
|
|
7556
|
+
}
|
|
7557
|
+
publish() {
|
|
7558
|
+
const changes = {};
|
|
7559
|
+
for (const [path11, entry] of this.entries) {
|
|
7560
|
+
if (entry.value)
|
|
7561
|
+
changes[path11] = entry.value;
|
|
7562
|
+
}
|
|
7563
|
+
this.bus.publish("worktree.changes", { changes });
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
function startWorktreeChangesCollector(orch, bus, tickMs = DEFAULT_WORKTREE_CHANGES_TICK_MS) {
|
|
7567
|
+
if (tickMs <= 0)
|
|
7568
|
+
return () => {};
|
|
7569
|
+
const collector = new WorktreeChangesCollector(orch, bus);
|
|
7570
|
+
collector.tick();
|
|
7571
|
+
const timer = setInterval(() => collector.tick(), tickMs);
|
|
7572
|
+
timer.unref?.();
|
|
7573
|
+
return () => {
|
|
7574
|
+
clearInterval(timer);
|
|
7575
|
+
collector.stop();
|
|
7576
|
+
};
|
|
7577
|
+
}
|
|
7578
|
+
var DEFAULT_WORKTREE_CHANGES_TICK_MS = 2000, WORKTREE_CHANGES_TIMEOUT_MS = 4000, WORKTREE_CHANGES_SLOW_RETRY_MS = 60000, WORKTREE_CHANGES_MIN_INTERVAL_MS = 1500;
|
|
7579
|
+
var init_worktree_changes_collector = __esm(() => {
|
|
7580
|
+
init_poll_scheduling();
|
|
7581
|
+
init_repos();
|
|
7582
|
+
init_worktree_changes();
|
|
7583
|
+
});
|
|
7584
|
+
|
|
7257
7585
|
// ../kobe-daemon/src/daemon/server.ts
|
|
7258
7586
|
import { mkdir as mkdir6, readFile as readFile8, unlink as unlink4, writeFile as writeFile4 } from "fs/promises";
|
|
7259
7587
|
import { createServer } from "net";
|
|
7260
|
-
import { dirname as
|
|
7588
|
+
import { dirname as dirname7 } from "path";
|
|
7261
7589
|
function resolveIdleGraceMs() {
|
|
7262
7590
|
const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
|
|
7263
7591
|
if (raw === undefined)
|
|
@@ -7306,8 +7634,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7306
7634
|
broadcast(clients, { type: "event", name: event.channel, payload: event.payload });
|
|
7307
7635
|
});
|
|
7308
7636
|
const activity = new DaemonActivityRegistry(bus);
|
|
7309
|
-
await mkdir6(
|
|
7310
|
-
await mkdir6(
|
|
7637
|
+
await mkdir6(dirname7(socketPath), { recursive: true });
|
|
7638
|
+
await mkdir6(dirname7(pidPath), { recursive: true });
|
|
7311
7639
|
await unlink4(socketPath).catch(() => {});
|
|
7312
7640
|
const server = createServer((socket) => {
|
|
7313
7641
|
const client = {
|
|
@@ -7353,6 +7681,11 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7353
7681
|
statePath: defaultUiPrefsStatePath(options.homeDir),
|
|
7354
7682
|
debounceMs: options.uiPrefsDebounceMs ?? DEFAULT_UI_PREFS_DEBOUNCE_MS
|
|
7355
7683
|
});
|
|
7684
|
+
const stopKeybindingsWatcher = startKeybindingsWatcher(bus, {
|
|
7685
|
+
path: defaultKeybindingsPath(options.homeDir),
|
|
7686
|
+
debounceMs: options.keybindingsDebounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS
|
|
7687
|
+
});
|
|
7688
|
+
const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS);
|
|
7356
7689
|
const serverApi = {
|
|
7357
7690
|
socketPath,
|
|
7358
7691
|
pidPath,
|
|
@@ -7366,6 +7699,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
7366
7699
|
clearInterval(updateTimer);
|
|
7367
7700
|
stopAutoTitlePoller();
|
|
7368
7701
|
stopUiPrefsWatcher();
|
|
7702
|
+
stopKeybindingsWatcher();
|
|
7703
|
+
stopWorktreeChangesCollector();
|
|
7369
7704
|
activity.close();
|
|
7370
7705
|
broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
|
|
7371
7706
|
for (const client of Array.from(clients)) {
|
|
@@ -7485,9 +7820,11 @@ var init_server = __esm(() => {
|
|
|
7485
7820
|
init_activity_registry();
|
|
7486
7821
|
init_auto_title_poller();
|
|
7487
7822
|
init_handlers();
|
|
7823
|
+
init_keybindings_watcher();
|
|
7488
7824
|
init_paths2();
|
|
7489
7825
|
init_protocol();
|
|
7490
7826
|
init_ui_prefs_watcher();
|
|
7827
|
+
init_worktree_changes_collector();
|
|
7491
7828
|
init_handlers();
|
|
7492
7829
|
DEFAULT_UPDATE_POLL_MS = 6 * 60 * 60 * 1000;
|
|
7493
7830
|
});
|
|
@@ -7556,21 +7893,21 @@ __export(exports_daemon_process, {
|
|
|
7556
7893
|
connectOrStartDaemon: () => connectOrStartDaemon,
|
|
7557
7894
|
connectIfRunning: () => connectIfRunning
|
|
7558
7895
|
});
|
|
7559
|
-
import { spawn as
|
|
7560
|
-
import { closeSync, existsSync as existsSync5, mkdirSync as
|
|
7561
|
-
import { dirname as
|
|
7896
|
+
import { spawn as spawn3 } from "child_process";
|
|
7897
|
+
import { closeSync, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync } from "fs";
|
|
7898
|
+
import { dirname as dirname8, resolve as resolve2 } from "path";
|
|
7562
7899
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7563
7900
|
function spawnDetachedDaemon(command, args, env, logPath) {
|
|
7564
7901
|
let stdio = "ignore";
|
|
7565
7902
|
let logFd;
|
|
7566
7903
|
try {
|
|
7567
|
-
|
|
7904
|
+
mkdirSync4(dirname8(logPath), { recursive: true });
|
|
7568
7905
|
logFd = openSync(logPath, "a");
|
|
7569
7906
|
stdio = ["ignore", logFd, logFd];
|
|
7570
7907
|
} catch {
|
|
7571
7908
|
stdio = "ignore";
|
|
7572
7909
|
}
|
|
7573
|
-
const child =
|
|
7910
|
+
const child = spawn3(command, [...args], { detached: true, stdio, env });
|
|
7574
7911
|
child.unref();
|
|
7575
7912
|
if (logFd !== undefined) {
|
|
7576
7913
|
try {
|
|
@@ -7631,7 +7968,7 @@ function resolveKobeSpawn(subcommand) {
|
|
|
7631
7968
|
if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
|
|
7632
7969
|
return [process.execPath, ...subcommand];
|
|
7633
7970
|
}
|
|
7634
|
-
const dir =
|
|
7971
|
+
const dir = dirname8(here);
|
|
7635
7972
|
const candidates = [
|
|
7636
7973
|
resolve2(dir, "../cli/index.ts"),
|
|
7637
7974
|
resolve2(dir, "../../../kobe/src/cli/index.ts"),
|
|
@@ -7730,13 +8067,13 @@ async function runRepoSubcommand(args) {
|
|
|
7730
8067
|
}
|
|
7731
8068
|
const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
7732
8069
|
const { existsSync: existsSync6 } = await import("fs");
|
|
7733
|
-
const { join:
|
|
8070
|
+
const { join: join6 } = await import("path");
|
|
7734
8071
|
if (verb === "show") {
|
|
7735
8072
|
const [pathArg] = rest.filter((a) => !a.startsWith("-"));
|
|
7736
8073
|
const repo = resolveRepoRoot2(resolve3(process.cwd(), pathArg ?? "."));
|
|
7737
8074
|
const override = getRepoInitOverride2(repo);
|
|
7738
|
-
const hasFileScript = existsSync6(
|
|
7739
|
-
const hasFilePrompt = existsSync6(
|
|
8075
|
+
const hasFileScript = existsSync6(join6(repo, ".kobe", "init.sh"));
|
|
8076
|
+
const hasFilePrompt = existsSync6(join6(repo, ".kobe", "init-prompt.md"));
|
|
7740
8077
|
console.log(`repo: ${repo}`);
|
|
7741
8078
|
console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
|
|
7742
8079
|
console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
|
|
@@ -7853,7 +8190,7 @@ var init_interactive_command = __esm(() => {
|
|
|
7853
8190
|
});
|
|
7854
8191
|
|
|
7855
8192
|
// src/lib/feedback.ts
|
|
7856
|
-
import { spawnSync as
|
|
8193
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
7857
8194
|
function parseRepoSlug(slug) {
|
|
7858
8195
|
const [owner, name] = slug.split("/");
|
|
7859
8196
|
if (!owner || !name)
|
|
@@ -7908,7 +8245,7 @@ function submitFeedback(input, deps = {}) {
|
|
|
7908
8245
|
throw new Error("package repository is not a GitHub repository");
|
|
7909
8246
|
const { owner, name } = parseRepoSlug(slug);
|
|
7910
8247
|
const categorySlug = input.categorySlug?.trim() || DEFAULT_FEEDBACK_CATEGORY_SLUG;
|
|
7911
|
-
const io = { spawn: deps.spawn ??
|
|
8248
|
+
const io = { spawn: deps.spawn ?? spawnSync8 };
|
|
7912
8249
|
const categoryData = runGhGraphql(DISCUSSION_CATEGORY_QUERY, { owner, name }, io);
|
|
7913
8250
|
const repository = categoryData.repository;
|
|
7914
8251
|
const repositoryId = repository?.id;
|
|
@@ -8009,6 +8346,9 @@ var init_daemon_session = __esm(() => {
|
|
|
8009
8346
|
|
|
8010
8347
|
// src/state/keybindings-file.ts
|
|
8011
8348
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
8349
|
+
function resetKeybindingsFileCache() {
|
|
8350
|
+
cached = null;
|
|
8351
|
+
}
|
|
8012
8352
|
function resolveConfigFile() {
|
|
8013
8353
|
const canonical = keybindingsConfigPath();
|
|
8014
8354
|
if (existsSync6(canonical))
|
|
@@ -8044,6 +8384,13 @@ var init_keybindings_file = __esm(() => {
|
|
|
8044
8384
|
});
|
|
8045
8385
|
|
|
8046
8386
|
// src/tui/lib/keymap-overrides.ts
|
|
8387
|
+
function pairContract(first, second) {
|
|
8388
|
+
const layout = `alternating [${first}, ${second}] pairs`;
|
|
8389
|
+
return {
|
|
8390
|
+
layout,
|
|
8391
|
+
validateCount: (count) => count >= 2 && count % 2 === 0 ? null : `needs ${layout} (an even number of chords \u2014 got ${count})`
|
|
8392
|
+
};
|
|
8393
|
+
}
|
|
8047
8394
|
function normalizeChord(raw, opts) {
|
|
8048
8395
|
const trimmed = raw.trim().toLowerCase();
|
|
8049
8396
|
if (!trimmed)
|
|
@@ -8183,6 +8530,14 @@ function applyKeymapOverrides(keymap, entries) {
|
|
|
8183
8530
|
warnings.push(`${entry.id}: not customizable \u2014 the key is handled outside the keymap (doc-only row)`);
|
|
8184
8531
|
continue;
|
|
8185
8532
|
}
|
|
8533
|
+
const contract = SLOT_CONTRACTS[entry.id];
|
|
8534
|
+
if (contract && entry.keys.length > 0) {
|
|
8535
|
+
const problem = contract.validateCount(entry.keys.length);
|
|
8536
|
+
if (problem) {
|
|
8537
|
+
warnings.push(`${entry.id}: ${problem} \u2014 keeping the default`);
|
|
8538
|
+
continue;
|
|
8539
|
+
}
|
|
8540
|
+
}
|
|
8186
8541
|
const keys = entry.keys.filter((chord) => {
|
|
8187
8542
|
if (chord.length === 1 && NO_BARE_LETTER_SCOPES.has(row.scope)) {
|
|
8188
8543
|
warnings.push(`${entry.id}: "${chord}" dropped \u2014 a bare character on a ${row.scope}-scope binding would steal typed input (add a modifier)`);
|
|
@@ -8194,6 +8549,10 @@ function applyKeymapOverrides(keymap, entries) {
|
|
|
8194
8549
|
warnings.push(`${entry.id}: no chords survived validation \u2014 keeping the default`);
|
|
8195
8550
|
continue;
|
|
8196
8551
|
}
|
|
8552
|
+
if (contract && keys.length !== entry.keys.length) {
|
|
8553
|
+
warnings.push(`${entry.id}: a dropped chord would shift the slot layout (${contract.layout}) \u2014 keeping the default`);
|
|
8554
|
+
continue;
|
|
8555
|
+
}
|
|
8197
8556
|
const defaultKeys = row.keys;
|
|
8198
8557
|
const mutable = row;
|
|
8199
8558
|
mutable.keys = keys;
|
|
@@ -8224,21 +8583,23 @@ function applyKeymapOverrides(keymap, entries) {
|
|
|
8224
8583
|
}
|
|
8225
8584
|
return { applied, warnings };
|
|
8226
8585
|
}
|
|
8227
|
-
var FIXED_BINDING_IDS, NO_BARE_LETTER_SCOPES, MOD_ALIASES, KEY_ALIASES, KNOWN_NAMED_KEYS, MOD_ORDER;
|
|
8586
|
+
var FIXED_BINDING_IDS, SLOT_CONTRACTS, NO_BARE_LETTER_SCOPES, MOD_ALIASES, KEY_ALIASES, KNOWN_NAMED_KEYS, MOD_ORDER;
|
|
8228
8587
|
var init_keymap_overrides = __esm(() => {
|
|
8229
8588
|
FIXED_BINDING_IDS = {
|
|
8230
|
-
"focus.numeric": "pane focus is positional (h/j/k/l \u2192 pane) and mirrors the tmux-layer ctrl+hjkl bindings",
|
|
8231
|
-
"sidebar.
|
|
8232
|
-
"sidebar.
|
|
8233
|
-
"sidebar.
|
|
8234
|
-
"
|
|
8235
|
-
"
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
"
|
|
8239
|
-
"files.
|
|
8240
|
-
"
|
|
8241
|
-
"
|
|
8589
|
+
"focus.numeric": "pane focus is positional (h/j/k/l \u2192 pane) and mirrors the tmux-layer ctrl+hjkl bindings \u2014 rebind tmux.focus instead",
|
|
8590
|
+
"sidebar.goto": "gg vs Shift+G is discriminated via evt.shift; shift+<letter> chords are inexpressible, so a rebind can't carry both halves",
|
|
8591
|
+
"sidebar.pin": "fires on Shift+P via evt.shift; shift+<letter> chords are inexpressible, so a rebind can't work",
|
|
8592
|
+
"sidebar.localMerge": "fires on Shift+M via evt.shift; shift+<letter> chords are inexpressible, so a rebind can't work",
|
|
8593
|
+
"chat.question.nav": "the question picker has no live registration site (display-only row) \u2014 rebinding would change Help without changing behavior",
|
|
8594
|
+
"chat.question.pick-number": "digits map to options positionally and the question picker has no live registration site (display-only row)"
|
|
8595
|
+
};
|
|
8596
|
+
SLOT_CONTRACTS = {
|
|
8597
|
+
"sidebar.nav": pairContract("down", "up"),
|
|
8598
|
+
"files.nav": pairContract("down", "up"),
|
|
8599
|
+
"sidebar.search.nav": pairContract("down", "up"),
|
|
8600
|
+
"files.hierarchy": pairContract("collapse", "expand"),
|
|
8601
|
+
"sidebar.view": pairContract("previous view", "next view"),
|
|
8602
|
+
"files.tab": pairContract("previous tab", "next tab")
|
|
8242
8603
|
};
|
|
8243
8604
|
NO_BARE_LETTER_SCOPES = new Set(["global", "workspace", "terminal"]);
|
|
8244
8605
|
MOD_ALIASES = {
|
|
@@ -8399,6 +8760,9 @@ function resolveTmuxKeyEntries(entries) {
|
|
|
8399
8760
|
function tmuxChordOptsFor(id) {
|
|
8400
8761
|
return id.startsWith("tmux.") ? { allowShiftCharacter: true } : {};
|
|
8401
8762
|
}
|
|
8763
|
+
function resetTmuxKeysCache() {
|
|
8764
|
+
cached2 = null;
|
|
8765
|
+
}
|
|
8402
8766
|
function resolveUserTmuxKeys() {
|
|
8403
8767
|
if (cached2)
|
|
8404
8768
|
return cached2;
|
|
@@ -9948,9 +10312,9 @@ var init_schema = () => {};
|
|
|
9948
10312
|
|
|
9949
10313
|
// src/tui/context/theme/loader.ts
|
|
9950
10314
|
import { readFileSync as readFileSync6, readdirSync } from "fs";
|
|
9951
|
-
import { join as
|
|
10315
|
+
import { join as join6 } from "path";
|
|
9952
10316
|
function userThemesDir() {
|
|
9953
|
-
return
|
|
10317
|
+
return join6(kobeStateDir(), "themes");
|
|
9954
10318
|
}
|
|
9955
10319
|
function loadUserThemes() {
|
|
9956
10320
|
const dir = userThemesDir();
|
|
@@ -9964,7 +10328,7 @@ function loadUserThemes() {
|
|
|
9964
10328
|
for (const file of entries) {
|
|
9965
10329
|
if (!file.endsWith(".json"))
|
|
9966
10330
|
continue;
|
|
9967
|
-
const path11 =
|
|
10331
|
+
const path11 = join6(dir, file);
|
|
9968
10332
|
let parsed;
|
|
9969
10333
|
try {
|
|
9970
10334
|
const text = readFileSync6(path11, "utf8");
|
|
@@ -10166,11 +10530,106 @@ async function relaunchEngineInAllWindows(session, cwd, command, remoteKey) {
|
|
|
10166
10530
|
return true;
|
|
10167
10531
|
}
|
|
10168
10532
|
async function healTaskPaneWidths(session) {
|
|
10169
|
-
const
|
|
10170
|
-
|
|
10533
|
+
const target = await globalTasksPaneWidth();
|
|
10534
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
10535
|
+
"list-panes",
|
|
10536
|
+
"-s",
|
|
10537
|
+
"-t",
|
|
10538
|
+
`=${session}`,
|
|
10539
|
+
"-F",
|
|
10540
|
+
"#{pane_id}\t#{@kobe_role}\t#{pane_width}"
|
|
10541
|
+
]);
|
|
10542
|
+
if (code !== 0)
|
|
10543
|
+
return;
|
|
10544
|
+
const mismatched = stdout.split(`
|
|
10545
|
+
`).map((line) => line.split("\t")).filter(([, role]) => role?.trim() === "tasks").filter(([, , width]) => Number.parseInt(width?.trim() ?? "", 10) !== target).map(([id]) => id?.trim()).filter((id) => !!id);
|
|
10546
|
+
if (mismatched.length === 0)
|
|
10547
|
+
return;
|
|
10548
|
+
await runTmuxSequence(mismatched.map((pane) => ["resize-pane", "-t", pane, "-x", `${target}`]));
|
|
10549
|
+
}
|
|
10550
|
+
async function rightColumnPercents() {
|
|
10551
|
+
const [width, height] = await Promise.all([
|
|
10552
|
+
getServerOption(RIGHT_COLUMN_WIDTH_OPTION),
|
|
10553
|
+
getServerOption(OPS_HEIGHT_OPTION)
|
|
10554
|
+
]);
|
|
10555
|
+
return {
|
|
10556
|
+
widthPct: clampPanePercent(Number.parseInt(width, 10)),
|
|
10557
|
+
heightPct: clampPanePercent(Number.parseInt(height, 10))
|
|
10558
|
+
};
|
|
10559
|
+
}
|
|
10560
|
+
function rightColumnResizeArgs(geom) {
|
|
10561
|
+
const args = [];
|
|
10562
|
+
if (geom.widthPct !== null)
|
|
10563
|
+
args.push("-x", `${geom.widthPct}%`);
|
|
10564
|
+
if (geom.heightPct !== null)
|
|
10565
|
+
args.push("-y", `${geom.heightPct}%`);
|
|
10566
|
+
return args;
|
|
10567
|
+
}
|
|
10568
|
+
async function globalRightColumnResizeArgs() {
|
|
10569
|
+
return rightColumnResizeArgs(await rightColumnPercents());
|
|
10570
|
+
}
|
|
10571
|
+
async function healRightColumn(session) {
|
|
10572
|
+
const args = await globalRightColumnResizeArgs();
|
|
10573
|
+
if (args.length === 0)
|
|
10574
|
+
return;
|
|
10575
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
10576
|
+
"list-panes",
|
|
10577
|
+
"-s",
|
|
10578
|
+
"-t",
|
|
10579
|
+
`=${session}`,
|
|
10580
|
+
"-F",
|
|
10581
|
+
"#{pane_id}\t#{@kobe_role}"
|
|
10582
|
+
]);
|
|
10583
|
+
if (code !== 0)
|
|
10584
|
+
return;
|
|
10585
|
+
const opsPanes = stdout.split(`
|
|
10586
|
+
`).map((line) => line.split("\t")).filter(([, role]) => role?.trim() === "ops").map(([id]) => id?.trim()).filter((id) => !!id);
|
|
10587
|
+
if (opsPanes.length === 0)
|
|
10588
|
+
return;
|
|
10589
|
+
await runTmuxSequence(opsPanes.map((pane) => ["resize-pane", "-t", pane, ...args]));
|
|
10590
|
+
}
|
|
10591
|
+
async function healSessionLayout(session) {
|
|
10592
|
+
if (!await sessionExists(session))
|
|
10593
|
+
return;
|
|
10594
|
+
await healTaskPaneWidths(session);
|
|
10595
|
+
await healRightColumn(session);
|
|
10596
|
+
}
|
|
10597
|
+
async function captureGlobalLayout(session) {
|
|
10598
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
10599
|
+
"list-panes",
|
|
10600
|
+
"-t",
|
|
10601
|
+
`=${session}`,
|
|
10602
|
+
"-F",
|
|
10603
|
+
"#{@kobe_role}\t#{pane_width}\t#{pane_height}\t#{window_width}\t#{window_height}"
|
|
10604
|
+
]);
|
|
10605
|
+
if (code !== 0)
|
|
10606
|
+
return;
|
|
10607
|
+
const rows = stdout.split(`
|
|
10608
|
+
`).map((line) => line.split("\t")).filter((cols) => (cols[0]?.trim() ?? "") !== "");
|
|
10609
|
+
if (rows.length === 0)
|
|
10171
10610
|
return;
|
|
10172
|
-
const
|
|
10173
|
-
|
|
10611
|
+
const winW = Number.parseInt(rows[0][3]?.trim() ?? "", 10);
|
|
10612
|
+
const winH = Number.parseInt(rows[0][4]?.trim() ?? "", 10);
|
|
10613
|
+
const sets = [];
|
|
10614
|
+
const tasks = rows.find(([role]) => role?.trim() === "tasks");
|
|
10615
|
+
if (tasks) {
|
|
10616
|
+
const width = Number.parseInt(tasks[1]?.trim() ?? "", 10);
|
|
10617
|
+
if (Number.isFinite(width) && width > 0)
|
|
10618
|
+
sets.push(["set-option", "-s", TASKS_WIDTH_OPTION, `${clampTasksPaneWidth(width)}`]);
|
|
10619
|
+
}
|
|
10620
|
+
const ops = rows.find(([role]) => role?.trim() === "ops");
|
|
10621
|
+
if (ops && Number.isFinite(winW) && winW > 0 && Number.isFinite(winH) && winH > 0) {
|
|
10622
|
+
const opsW = Number.parseInt(ops[1]?.trim() ?? "", 10);
|
|
10623
|
+
const opsH = Number.parseInt(ops[2]?.trim() ?? "", 10);
|
|
10624
|
+
const widthPct = Number.isFinite(opsW) ? clampPanePercent(100 * opsW / winW) : null;
|
|
10625
|
+
const heightPct = Number.isFinite(opsH) ? clampPanePercent(100 * opsH / winH) : null;
|
|
10626
|
+
if (widthPct !== null)
|
|
10627
|
+
sets.push(["set-option", "-s", RIGHT_COLUMN_WIDTH_OPTION, `${widthPct}`]);
|
|
10628
|
+
if (heightPct !== null)
|
|
10629
|
+
sets.push(["set-option", "-s", OPS_HEIGHT_OPTION, `${heightPct}`]);
|
|
10630
|
+
}
|
|
10631
|
+
if (sets.length > 0)
|
|
10632
|
+
await runTmuxSequence(sets);
|
|
10174
10633
|
}
|
|
10175
10634
|
async function healKobePaneVersions(session, cwd, taskId, vendor) {
|
|
10176
10635
|
const rows = await listKobePanes(session);
|
|
@@ -10253,6 +10712,7 @@ function kobeStatusRight(keys) {
|
|
|
10253
10712
|
}
|
|
10254
10713
|
async function buildPanesAround(claudePane, args) {
|
|
10255
10714
|
const envPrefix = inheritedEnvPrefix();
|
|
10715
|
+
const tasksWidth = await globalTasksPaneWidth();
|
|
10256
10716
|
const opsCmd = keepAlive(args.opsCommand ?? envPrefix + opsPaneCommand({
|
|
10257
10717
|
cwd: args.cwd,
|
|
10258
10718
|
taskId: args.taskId,
|
|
@@ -10270,7 +10730,7 @@ async function buildPanesAround(claudePane, args) {
|
|
|
10270
10730
|
"-t",
|
|
10271
10731
|
claudePane,
|
|
10272
10732
|
"-l",
|
|
10273
|
-
`${
|
|
10733
|
+
`${tasksWidth}`,
|
|
10274
10734
|
"-c",
|
|
10275
10735
|
localSpawnCwd(args.cwd),
|
|
10276
10736
|
"-P",
|
|
@@ -10303,6 +10763,11 @@ async function buildPanesAround(claudePane, args) {
|
|
|
10303
10763
|
...ids.ops ? [["set-option", "-p", "-t", ids.ops, "@kobe_role", "ops"]] : [],
|
|
10304
10764
|
...ids.ops ? [["set-option", "-p", "-t", ids.ops, PANE_VERSION_OPTION, CURRENT_VERSION]] : []
|
|
10305
10765
|
]);
|
|
10766
|
+
if (ids.ops) {
|
|
10767
|
+
const rcArgs = await globalRightColumnResizeArgs();
|
|
10768
|
+
if (rcArgs.length > 0)
|
|
10769
|
+
await runTmux(["resize-pane", "-t", ids.ops, ...rcArgs]);
|
|
10770
|
+
}
|
|
10306
10771
|
}
|
|
10307
10772
|
async function newChatTab(session, vendorOverride) {
|
|
10308
10773
|
if (!await sessionExists(session))
|
|
@@ -10350,6 +10815,16 @@ async function openSettingsTab(session) {
|
|
|
10350
10815
|
const command = `${envPrefix}${inv.map(shellQuote).join(" ")} settings`;
|
|
10351
10816
|
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "settings" });
|
|
10352
10817
|
}
|
|
10818
|
+
async function openHelpTab(session) {
|
|
10819
|
+
if (!await sessionExists(session))
|
|
10820
|
+
return;
|
|
10821
|
+
const sessionOptions = await getSessionOptions(session, ["@kobe_worktree"]);
|
|
10822
|
+
const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
|
|
10823
|
+
const inv = kobeCliInvocation();
|
|
10824
|
+
const envPrefix = inheritedEnvPrefix();
|
|
10825
|
+
const command = `${envPrefix}${inv.map(shellQuote).join(" ")} help-page`;
|
|
10826
|
+
await newWindow(session, { cwd: localSpawnCwd(cwd), command, name: "help" });
|
|
10827
|
+
}
|
|
10353
10828
|
async function openNewTaskTab(session, defaultRepo) {
|
|
10354
10829
|
if (!await sessionExists(session))
|
|
10355
10830
|
return;
|
|
@@ -10422,18 +10897,22 @@ __export(exports_tmux, {
|
|
|
10422
10897
|
selectTasksPane: () => selectTasksPane,
|
|
10423
10898
|
refreshKobeWorkspacePanes: () => refreshKobeWorkspacePanes,
|
|
10424
10899
|
quickCreate: () => quickCreate,
|
|
10900
|
+
prepareWindowForAttach: () => prepareWindowForAttach,
|
|
10425
10901
|
openUpdateTab: () => openUpdateTab,
|
|
10426
10902
|
openSettingsTab: () => openSettingsTab,
|
|
10427
10903
|
openNewTaskTab: () => openNewTaskTab,
|
|
10904
|
+
openHelpTab: () => openHelpTab,
|
|
10428
10905
|
newChatTab: () => newChatTab,
|
|
10429
10906
|
kobeStatusRight: () => kobeStatusRight,
|
|
10430
10907
|
killSession: () => killSession,
|
|
10908
|
+
healSessionLayout: () => healSessionLayout,
|
|
10431
10909
|
ensureSession: () => ensureSession,
|
|
10432
10910
|
currentSessionName: () => currentSessionName,
|
|
10433
10911
|
chatTabSwitchBindings: () => chatTabSwitchBindings,
|
|
10434
10912
|
chatTabRenameBinding: () => chatTabRenameBinding,
|
|
10435
10913
|
chatTabCloseBinding: () => chatTabCloseBinding,
|
|
10436
10914
|
chatTabChooseEngineBindings: () => chatTabChooseEngineBindings,
|
|
10915
|
+
captureGlobalLayout: () => captureGlobalLayout,
|
|
10437
10916
|
attachArgv: () => attachArgv,
|
|
10438
10917
|
PANE_VERSION_OPTION: () => PANE_VERSION_OPTION,
|
|
10439
10918
|
CHAT_TAB_STATUS_FORMAT: () => CHAT_TAB_STATUS_FORMAT,
|
|
@@ -10450,6 +10929,13 @@ function positiveInt(value) {
|
|
|
10450
10929
|
const n = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
10451
10930
|
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
10452
10931
|
}
|
|
10932
|
+
async function prepareWindowForAttach(session) {
|
|
10933
|
+
const sizeArgs = tmuxInitialSizeArgs();
|
|
10934
|
+
if (sizeArgs.length > 0)
|
|
10935
|
+
await runTmux(["resize-window", "-t", `=${session}`, ...sizeArgs]);
|
|
10936
|
+
await healTaskPaneWidths(session);
|
|
10937
|
+
await healRightColumn(session);
|
|
10938
|
+
}
|
|
10453
10939
|
async function ensureSession(opts) {
|
|
10454
10940
|
const inflight = ensureSessionLocks.get(opts.name);
|
|
10455
10941
|
if (inflight)
|
|
@@ -10483,6 +10969,7 @@ async function ensureSessionImpl(opts) {
|
|
|
10483
10969
|
const remoteKey = remoteKeyForRepo(opts.repo);
|
|
10484
10970
|
if (action.kind === "reuse") {
|
|
10485
10971
|
await healTaskPaneWidths(opts.name);
|
|
10972
|
+
await healRightColumn(opts.name);
|
|
10486
10973
|
await healKobePaneVersions(opts.name, opts.cwd, opts.taskId, opts.vendor);
|
|
10487
10974
|
return true;
|
|
10488
10975
|
}
|
|
@@ -10491,6 +10978,7 @@ async function ensureSessionImpl(opts) {
|
|
|
10491
10978
|
if (opts.vendor)
|
|
10492
10979
|
await setSessionOption(opts.name, "@kobe_vendor", opts.vendor);
|
|
10493
10980
|
await healTaskPaneWidths(opts.name);
|
|
10981
|
+
await healRightColumn(opts.name);
|
|
10494
10982
|
await healKobePaneVersions(opts.name, opts.cwd, opts.taskId, opts.vendor);
|
|
10495
10983
|
return true;
|
|
10496
10984
|
}
|
|
@@ -10544,6 +11032,8 @@ async function ensureSessionImpl(opts) {
|
|
|
10544
11032
|
const chooseEngineTmuxCommand = `run-shell ${shellQuote(chooseEngineCommand)}`;
|
|
10545
11033
|
const focusTasksCommand = `${envStr}${invStr} focus-tasks --session '#{session_name}'`;
|
|
10546
11034
|
const focusTasksTmuxCommand = `run-shell ${shellQuote(focusTasksCommand)}`;
|
|
11035
|
+
const healLayoutCommand = `${envStr}${invStr} heal-layout --session '#{session_name}'`;
|
|
11036
|
+
const healLayoutTmuxCommand = `run-shell -b ${shellQuote(healLayoutCommand)}`;
|
|
10547
11037
|
const userKeys = resolveUserTmuxKeys();
|
|
10548
11038
|
const unbinds = [];
|
|
10549
11039
|
if (userKeys.overridden.has(TMUX_FOCUS_ID)) {
|
|
@@ -10585,6 +11075,7 @@ async function ensureSessionImpl(opts) {
|
|
|
10585
11075
|
})
|
|
10586
11076
|
],
|
|
10587
11077
|
["set-option", "-g", "mouse", "on"],
|
|
11078
|
+
["set-hook", "-g", "window-resized", healLayoutTmuxCommand],
|
|
10588
11079
|
...unbinds,
|
|
10589
11080
|
...b["tmux.detach"] ? [
|
|
10590
11081
|
[
|
|
@@ -10648,12 +11139,12 @@ __export(exports_repo_init, {
|
|
|
10648
11139
|
resolveRepoInit: () => resolveRepoInit
|
|
10649
11140
|
});
|
|
10650
11141
|
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
10651
|
-
import { join as
|
|
11142
|
+
import { join as join7 } from "path";
|
|
10652
11143
|
function repoFileScript(worktreePath) {
|
|
10653
|
-
return existsSync7(
|
|
11144
|
+
return existsSync7(join7(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
|
|
10654
11145
|
}
|
|
10655
11146
|
function repoFilePrompt(worktreePath) {
|
|
10656
|
-
const p =
|
|
11147
|
+
const p = join7(worktreePath, INIT_PROMPT_REL);
|
|
10657
11148
|
if (!existsSync7(p))
|
|
10658
11149
|
return;
|
|
10659
11150
|
try {
|
|
@@ -10675,53 +11166,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
|
|
|
10675
11166
|
var INIT_SCRIPT_REL, INIT_PROMPT_REL;
|
|
10676
11167
|
var init_repo_init = __esm(() => {
|
|
10677
11168
|
init_repos();
|
|
10678
|
-
INIT_SCRIPT_REL =
|
|
10679
|
-
INIT_PROMPT_REL =
|
|
10680
|
-
});
|
|
10681
|
-
|
|
10682
|
-
// src/tui/panes/sidebar/worktree-changes.ts
|
|
10683
|
-
var exports_worktree_changes = {};
|
|
10684
|
-
__export(exports_worktree_changes, {
|
|
10685
|
-
readWorktreeChanges: () => readWorktreeChanges,
|
|
10686
|
-
parsePorcelain: () => parsePorcelain2
|
|
10687
|
-
});
|
|
10688
|
-
import { spawnSync as spawnSync8 } from "child_process";
|
|
10689
|
-
function readWorktreeChanges(worktreePath) {
|
|
10690
|
-
if (!worktreePath)
|
|
10691
|
-
return ZERO;
|
|
10692
|
-
try {
|
|
10693
|
-
const out = spawnSync8("git", ["status", "--porcelain=v1"], {
|
|
10694
|
-
cwd: worktreePath,
|
|
10695
|
-
encoding: "utf8",
|
|
10696
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
10697
|
-
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" }
|
|
10698
|
-
});
|
|
10699
|
-
if (out.status !== 0 || !out.stdout)
|
|
10700
|
-
return ZERO;
|
|
10701
|
-
return parsePorcelain2(out.stdout);
|
|
10702
|
-
} catch {
|
|
10703
|
-
return ZERO;
|
|
10704
|
-
}
|
|
10705
|
-
}
|
|
10706
|
-
function parsePorcelain2(text) {
|
|
10707
|
-
let added = 0;
|
|
10708
|
-
let deleted = 0;
|
|
10709
|
-
for (const line of text.split(`
|
|
10710
|
-
`)) {
|
|
10711
|
-
if (!line || line.startsWith("##"))
|
|
10712
|
-
continue;
|
|
10713
|
-
const x = line.charAt(0);
|
|
10714
|
-
const y = line.charAt(1);
|
|
10715
|
-
if (x === "D" || y === "D")
|
|
10716
|
-
deleted += 1;
|
|
10717
|
-
else
|
|
10718
|
-
added += 1;
|
|
10719
|
-
}
|
|
10720
|
-
return { added, deleted };
|
|
10721
|
-
}
|
|
10722
|
-
var ZERO;
|
|
10723
|
-
var init_worktree_changes = __esm(() => {
|
|
10724
|
-
ZERO = { added: 0, deleted: 0 };
|
|
11169
|
+
INIT_SCRIPT_REL = join7(".kobe", "init.sh");
|
|
11170
|
+
INIT_PROMPT_REL = join7(".kobe", "init-prompt.md");
|
|
10725
11171
|
});
|
|
10726
11172
|
|
|
10727
11173
|
// src/cli/api-cmd.ts
|
|
@@ -11717,8 +12163,8 @@ var exports_theme = {};
|
|
|
11717
12163
|
__export(exports_theme, {
|
|
11718
12164
|
runThemeSubcommand: () => runThemeSubcommand
|
|
11719
12165
|
});
|
|
11720
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
11721
|
-
import { basename as
|
|
12166
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync9, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
12167
|
+
import { basename as basename5, join as join8, resolve as resolve5 } from "path";
|
|
11722
12168
|
function fail3(message) {
|
|
11723
12169
|
process.stderr.write(`kobe theme: ${message}
|
|
11724
12170
|
`);
|
|
@@ -11749,7 +12195,7 @@ function listThemes() {
|
|
|
11749
12195
|
} else {
|
|
11750
12196
|
for (const f of userFiles) {
|
|
11751
12197
|
const name = f.slice(0, -".json".length);
|
|
11752
|
-
const path11 =
|
|
12198
|
+
const path11 = join8(dir, f);
|
|
11753
12199
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
11754
12200
|
lines.push(` ${name}${overridesBundled} ${path11}`);
|
|
11755
12201
|
}
|
|
@@ -11771,7 +12217,7 @@ async function readSource(source) {
|
|
|
11771
12217
|
}
|
|
11772
12218
|
const text2 = await res.text();
|
|
11773
12219
|
const cleanPath = source.split(/[?#]/)[0] ?? source;
|
|
11774
|
-
const file2 =
|
|
12220
|
+
const file2 = basename5(cleanPath) || "theme.json";
|
|
11775
12221
|
const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
|
|
11776
12222
|
return { text: text2, defaultName: defaultName2 };
|
|
11777
12223
|
}
|
|
@@ -11782,7 +12228,7 @@ async function readSource(source) {
|
|
|
11782
12228
|
} catch (err) {
|
|
11783
12229
|
fail3(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11784
12230
|
}
|
|
11785
|
-
const file =
|
|
12231
|
+
const file = basename5(abs);
|
|
11786
12232
|
const defaultName = file.endsWith(".json") ? file.slice(0, -".json".length) : file;
|
|
11787
12233
|
return { text, defaultName };
|
|
11788
12234
|
}
|
|
@@ -11838,8 +12284,8 @@ async function addTheme(args) {
|
|
|
11838
12284
|
fail3(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
|
|
11839
12285
|
}
|
|
11840
12286
|
const dir = userThemesDir();
|
|
11841
|
-
|
|
11842
|
-
const dest =
|
|
12287
|
+
mkdirSync5(dir, { recursive: true });
|
|
12288
|
+
const dest = join8(dir, `${name}.json`);
|
|
11843
12289
|
if (existsSync8(dest) && !opts.force) {
|
|
11844
12290
|
fail3(`${dest} already exists (pass --force to overwrite)`);
|
|
11845
12291
|
}
|
|
@@ -11857,7 +12303,7 @@ function removeTheme(args) {
|
|
|
11857
12303
|
if (BUNDLED_NAMES.includes(name)) {
|
|
11858
12304
|
fail3(`"${name}" is a built-in theme and cannot be removed`);
|
|
11859
12305
|
}
|
|
11860
|
-
const dest =
|
|
12306
|
+
const dest = join8(userThemesDir(), `${name}.json`);
|
|
11861
12307
|
if (!existsSync8(dest)) {
|
|
11862
12308
|
fail3(`no user theme named "${name}" (looked for ${dest})`);
|
|
11863
12309
|
}
|
|
@@ -12018,9 +12464,9 @@ var init_feedback_cmd = __esm(() => {
|
|
|
12018
12464
|
});
|
|
12019
12465
|
|
|
12020
12466
|
// src/core/index.ts
|
|
12021
|
-
import { homedir as
|
|
12467
|
+
import { homedir as homedir15 } from "os";
|
|
12022
12468
|
async function createKobeCore(options = {}) {
|
|
12023
|
-
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
12469
|
+
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir15();
|
|
12024
12470
|
const store = new TaskIndexStore({ homeDir: homeDir2 });
|
|
12025
12471
|
await store.load();
|
|
12026
12472
|
const worktrees = new GitWorktreeManager;
|
|
@@ -12138,8 +12584,8 @@ var init_daemon_cmd = __esm(() => {
|
|
|
12138
12584
|
|
|
12139
12585
|
// src/lib/skill-install.ts
|
|
12140
12586
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
12141
|
-
import { homedir as
|
|
12142
|
-
import { join as
|
|
12587
|
+
import { homedir as homedir16 } from "os";
|
|
12588
|
+
import { join as join9 } from "path";
|
|
12143
12589
|
function npxSkillsArgv(opts = {}) {
|
|
12144
12590
|
return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
|
|
12145
12591
|
}
|
|
@@ -12147,9 +12593,9 @@ function npxSkillsCommand(opts = {}) {
|
|
|
12147
12593
|
return `npx ${npxSkillsArgv(opts).join(" ")}`;
|
|
12148
12594
|
}
|
|
12149
12595
|
function kobeSkillPaths(opts = {}) {
|
|
12150
|
-
const home = opts.home ??
|
|
12596
|
+
const home = opts.home ?? homedir16();
|
|
12151
12597
|
const cwd = opts.cwd ?? process.cwd();
|
|
12152
|
-
return [
|
|
12598
|
+
return [join9(home, SKILL_REL_PATH), join9(cwd, SKILL_REL_PATH)];
|
|
12153
12599
|
}
|
|
12154
12600
|
function parseSkillVersion(content) {
|
|
12155
12601
|
const m = content.match(/kobe-skill-version:\s*(\d+)/);
|
|
@@ -12210,7 +12656,7 @@ __export(exports_maintenance, {
|
|
|
12210
12656
|
});
|
|
12211
12657
|
import { existsSync as existsSync10, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
|
|
12212
12658
|
import { unlink as unlink6 } from "fs/promises";
|
|
12213
|
-
import { join as
|
|
12659
|
+
import { join as join10 } from "path";
|
|
12214
12660
|
import { createInterface as createInterface2 } from "readline";
|
|
12215
12661
|
function isProcessAlive2(pid) {
|
|
12216
12662
|
try {
|
|
@@ -12307,7 +12753,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
12307
12753
|
const socketPath = defaultDaemonSocketPath();
|
|
12308
12754
|
const pidPath = defaultDaemonPidPath();
|
|
12309
12755
|
const logPath = defaultDaemonLogPath();
|
|
12310
|
-
const tasksPath =
|
|
12756
|
+
const tasksPath = join10(kobeStateDir(), "tasks.json");
|
|
12311
12757
|
const statePath2 = kvStatePath();
|
|
12312
12758
|
const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
|
|
12313
12759
|
const status = await probeDaemonStatus(socketPath);
|
|
@@ -12426,7 +12872,7 @@ async function runResetSubcommand(argv) {
|
|
|
12426
12872
|
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
12427
12873
|
const socketPath = defaultDaemonSocketPath();
|
|
12428
12874
|
const pidPath = defaultDaemonPidPath();
|
|
12429
|
-
const tasksPath =
|
|
12875
|
+
const tasksPath = join10(kobeStateDir(), "tasks.json");
|
|
12430
12876
|
const statePath2 = kvStatePath();
|
|
12431
12877
|
console.log("kobe reset will:");
|
|
12432
12878
|
console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
|
|
@@ -12696,15 +13142,15 @@ var GIT_TIMEOUT_MS = 15000;
|
|
|
12696
13142
|
|
|
12697
13143
|
// src/web/notes.ts
|
|
12698
13144
|
import { mkdir as mkdir7, readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
|
|
12699
|
-
import { join as
|
|
13145
|
+
import { join as join11 } from "path";
|
|
12700
13146
|
function notesDir() {
|
|
12701
|
-
return
|
|
13147
|
+
return join11(kobeStateDir(), "notes");
|
|
12702
13148
|
}
|
|
12703
13149
|
function isSafeTaskId(taskId) {
|
|
12704
13150
|
return typeof taskId === "string" && taskId.length > 0 && /^[A-Za-z0-9_-]+$/.test(taskId);
|
|
12705
13151
|
}
|
|
12706
13152
|
function noteFilePath(taskId) {
|
|
12707
|
-
return
|
|
13153
|
+
return join11(notesDir(), `${taskId}.md`);
|
|
12708
13154
|
}
|
|
12709
13155
|
async function handleGet(url) {
|
|
12710
13156
|
const taskId = url.searchParams.get("taskId");
|
|
@@ -12817,16 +13263,21 @@ class DaemonLink {
|
|
|
12817
13263
|
const socketPath = allowSpawn ? await ensureDaemonReachable() : defaultDaemonSocketPath();
|
|
12818
13264
|
const client = new KobeDaemonClient(socketPath);
|
|
12819
13265
|
await client.connect();
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
|
|
12825
|
-
|
|
12826
|
-
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
13266
|
+
try {
|
|
13267
|
+
const hello = await client.request("hello", {
|
|
13268
|
+
protocolVersion: DAEMON_PROTOCOL_VERSION,
|
|
13269
|
+
minProtocolVersion: MIN_COMPATIBLE_PROTOCOL_VERSION
|
|
13270
|
+
});
|
|
13271
|
+
if (hello.tasks)
|
|
13272
|
+
this.tasks = hello.tasks;
|
|
13273
|
+
this.engineStates = {};
|
|
13274
|
+
client.on("*", (frame) => this.onFrame(frame.name, frame.payload));
|
|
13275
|
+
client.onLifecycle("close", () => this.onDrop(client));
|
|
13276
|
+
await client.subscribe({ role: "gui" });
|
|
13277
|
+
} catch (err) {
|
|
13278
|
+
client.close();
|
|
13279
|
+
throw err;
|
|
13280
|
+
}
|
|
12830
13281
|
this.client = client;
|
|
12831
13282
|
this.setConnected(true);
|
|
12832
13283
|
}
|
|
@@ -12950,7 +13401,7 @@ var init_session = __esm(() => {
|
|
|
12950
13401
|
|
|
12951
13402
|
// ../kobe-web/server/bridge.ts
|
|
12952
13403
|
import { existsSync as existsSync11 } from "fs";
|
|
12953
|
-
import { join as
|
|
13404
|
+
import { join as join12, normalize as normalize2 } from "path";
|
|
12954
13405
|
function sseResponse(register) {
|
|
12955
13406
|
let unregister = null;
|
|
12956
13407
|
let heartbeat = null;
|
|
@@ -13024,10 +13475,10 @@ async function specResponse(url, link, spec) {
|
|
|
13024
13475
|
}
|
|
13025
13476
|
async function staticResponse(pathname, staticDir) {
|
|
13026
13477
|
const rel = pathname === "/" ? "/index.html" : pathname;
|
|
13027
|
-
const resolved = normalize2(
|
|
13478
|
+
const resolved = normalize2(join12(staticDir, rel));
|
|
13028
13479
|
if (!resolved.startsWith(staticDir))
|
|
13029
13480
|
return new Response("forbidden", { status: 403 });
|
|
13030
|
-
const file = Bun.file(existsSync11(resolved) ? resolved :
|
|
13481
|
+
const file = Bun.file(existsSync11(resolved) ? resolved : join12(staticDir, "index.html"));
|
|
13031
13482
|
if (!await file.exists()) {
|
|
13032
13483
|
return new Response("kobe web assets not built \u2014 run `bun --filter kobe-web build`", { status: 503 });
|
|
13033
13484
|
}
|
|
@@ -13424,8 +13875,8 @@ __export(exports_hook_cmd, {
|
|
|
13424
13875
|
parseWorktreeAddPath: () => parseWorktreeAddPath,
|
|
13425
13876
|
ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
|
|
13426
13877
|
});
|
|
13427
|
-
import { homedir as
|
|
13428
|
-
import { join as
|
|
13878
|
+
import { homedir as homedir17 } from "os";
|
|
13879
|
+
import { join as join13, resolve as resolve7 } from "path";
|
|
13429
13880
|
async function readStdinPayload() {
|
|
13430
13881
|
try {
|
|
13431
13882
|
const text = await Promise.race([
|
|
@@ -13545,7 +13996,7 @@ function activityHookAdapters() {
|
|
|
13545
13996
|
return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
|
|
13546
13997
|
}
|
|
13547
13998
|
function globalSettingsPath() {
|
|
13548
|
-
return
|
|
13999
|
+
return join13(homedir17(), ".claude", "settings.json");
|
|
13549
14000
|
}
|
|
13550
14001
|
function persistedSyncPath(stored) {
|
|
13551
14002
|
if (!stored || stored === "off")
|
|
@@ -13553,7 +14004,7 @@ function persistedSyncPath(stored) {
|
|
|
13553
14004
|
if (stored === "global")
|
|
13554
14005
|
return globalSettingsPath();
|
|
13555
14006
|
if (stored.startsWith("repo:"))
|
|
13556
|
-
return
|
|
14007
|
+
return join13(resolve7(stored.slice(5)), ".claude", "settings.json");
|
|
13557
14008
|
return stored;
|
|
13558
14009
|
}
|
|
13559
14010
|
async function ensureGlobalKobeHooks() {
|
|
@@ -14883,6 +15334,30 @@ var init_solid = __esm(() => {
|
|
|
14883
15334
|
});
|
|
14884
15335
|
|
|
14885
15336
|
// src/client/remote-orchestrator.ts
|
|
15337
|
+
function parseWorktreeChangesPayload(payload) {
|
|
15338
|
+
const changes = payload?.changes;
|
|
15339
|
+
if (!changes || typeof changes !== "object" || Array.isArray(changes))
|
|
15340
|
+
return null;
|
|
15341
|
+
const map = new Map;
|
|
15342
|
+
for (const [path11, value] of Object.entries(changes)) {
|
|
15343
|
+
const counts = value;
|
|
15344
|
+
if (typeof counts?.added !== "number" || typeof counts.deleted !== "number")
|
|
15345
|
+
return null;
|
|
15346
|
+
map.set(path11, { added: counts.added, deleted: counts.deleted });
|
|
15347
|
+
}
|
|
15348
|
+
return map;
|
|
15349
|
+
}
|
|
15350
|
+
function sameWorktreeChangesMap(a, b) {
|
|
15351
|
+
if (a.size !== b.size)
|
|
15352
|
+
return false;
|
|
15353
|
+
for (const [path11, counts] of a) {
|
|
15354
|
+
const other = b.get(path11);
|
|
15355
|
+
if (!other || !sameWorktreeChanges(counts, other))
|
|
15356
|
+
return false;
|
|
15357
|
+
}
|
|
15358
|
+
return true;
|
|
15359
|
+
}
|
|
15360
|
+
|
|
14886
15361
|
class RemoteOrchestrator {
|
|
14887
15362
|
client;
|
|
14888
15363
|
tasksAcc;
|
|
@@ -14895,8 +15370,14 @@ class RemoteOrchestrator {
|
|
|
14895
15370
|
setDaemonVersionSig;
|
|
14896
15371
|
engineStateAcc;
|
|
14897
15372
|
setEngineStateSig;
|
|
15373
|
+
taskJobsAcc;
|
|
15374
|
+
setTaskJobsSig;
|
|
15375
|
+
worktreeChangesAcc;
|
|
15376
|
+
setWorktreeChangesSig;
|
|
14898
15377
|
uiPrefsAcc;
|
|
14899
15378
|
setUiPrefsSig;
|
|
15379
|
+
keybindingsRevAcc;
|
|
15380
|
+
setKeybindingsRevSig;
|
|
14900
15381
|
connectionStateAcc;
|
|
14901
15382
|
setConnectionState;
|
|
14902
15383
|
ensureReachable;
|
|
@@ -14909,7 +15390,10 @@ class RemoteOrchestrator {
|
|
|
14909
15390
|
const [update, setUpdate] = createSignal(null);
|
|
14910
15391
|
const [daemonVersion, setDaemonVersion] = createSignal(null);
|
|
14911
15392
|
const [engineState, setEngineState] = createSignal(new Map);
|
|
15393
|
+
const [taskJobs, setTaskJobs] = createSignal(new Map);
|
|
15394
|
+
const [worktreeChanges, setWorktreeChanges] = createSignal(null);
|
|
14912
15395
|
const [uiPrefs, setUiPrefs] = createSignal(null);
|
|
15396
|
+
const [keybindingsRev, setKeybindingsRev] = createSignal(null);
|
|
14913
15397
|
const [connectionState, setConnectionState] = createSignal("online");
|
|
14914
15398
|
this.tasksAcc = tasks;
|
|
14915
15399
|
this.setTasks = (next) => setTasks(() => next);
|
|
@@ -14921,8 +15405,14 @@ class RemoteOrchestrator {
|
|
|
14921
15405
|
this.setDaemonVersionSig = (next) => setDaemonVersion(() => next);
|
|
14922
15406
|
this.engineStateAcc = engineState;
|
|
14923
15407
|
this.setEngineStateSig = (next) => setEngineState(() => next);
|
|
15408
|
+
this.taskJobsAcc = taskJobs;
|
|
15409
|
+
this.setTaskJobsSig = (next) => setTaskJobs(() => next);
|
|
15410
|
+
this.worktreeChangesAcc = worktreeChanges;
|
|
15411
|
+
this.setWorktreeChangesSig = (next) => setWorktreeChanges(() => next);
|
|
14924
15412
|
this.uiPrefsAcc = uiPrefs;
|
|
14925
15413
|
this.setUiPrefsSig = (next) => setUiPrefs(() => next);
|
|
15414
|
+
this.keybindingsRevAcc = keybindingsRev;
|
|
15415
|
+
this.setKeybindingsRevSig = (next) => setKeybindingsRev(() => next);
|
|
14926
15416
|
this.connectionStateAcc = connectionState;
|
|
14927
15417
|
this.setConnectionState = (next) => setConnectionState(() => next);
|
|
14928
15418
|
this.ensureReachable = options.ensureReachable ?? ensureDaemonReachable;
|
|
@@ -14979,6 +15469,12 @@ class RemoteOrchestrator {
|
|
|
14979
15469
|
if (hello.tasks)
|
|
14980
15470
|
this.setTasks(hello.tasks.map(deserializeTask));
|
|
14981
15471
|
await this.client.subscribe({ role: this.role });
|
|
15472
|
+
if (hello.capabilities?.includes("worktree.changes")) {
|
|
15473
|
+
if (this.worktreeChangesAcc() === null)
|
|
15474
|
+
this.setWorktreeChangesSig(new Map);
|
|
15475
|
+
} else {
|
|
15476
|
+
this.setWorktreeChangesSig(null);
|
|
15477
|
+
}
|
|
14982
15478
|
this.setConnectionState("online");
|
|
14983
15479
|
logClient("orch", `subscribed as ${this.role} (${this.tasksAcc().length} tasks)`);
|
|
14984
15480
|
}
|
|
@@ -15011,9 +15507,18 @@ class RemoteOrchestrator {
|
|
|
15011
15507
|
engineStateSignal() {
|
|
15012
15508
|
return this.engineStateAcc;
|
|
15013
15509
|
}
|
|
15510
|
+
taskJobsSignal() {
|
|
15511
|
+
return this.taskJobsAcc;
|
|
15512
|
+
}
|
|
15513
|
+
worktreeChangesSignal() {
|
|
15514
|
+
return this.worktreeChangesAcc;
|
|
15515
|
+
}
|
|
15014
15516
|
uiPrefsSignal() {
|
|
15015
15517
|
return this.uiPrefsAcc;
|
|
15016
15518
|
}
|
|
15519
|
+
keybindingsRevSignal() {
|
|
15520
|
+
return this.keybindingsRevAcc;
|
|
15521
|
+
}
|
|
15017
15522
|
listTasks() {
|
|
15018
15523
|
return this.tasksAcc();
|
|
15019
15524
|
}
|
|
@@ -15093,8 +15598,11 @@ class RemoteOrchestrator {
|
|
|
15093
15598
|
handleEvent(name, payload) {
|
|
15094
15599
|
if (name === "task.snapshot") {
|
|
15095
15600
|
const value = payload?.tasks;
|
|
15096
|
-
if (Array.isArray(value))
|
|
15601
|
+
if (Array.isArray(value)) {
|
|
15097
15602
|
this.setTasks(value.map(deserializeTask));
|
|
15603
|
+
this.pruneEngineState(value);
|
|
15604
|
+
this.pruneTaskJobs(value);
|
|
15605
|
+
}
|
|
15098
15606
|
return;
|
|
15099
15607
|
}
|
|
15100
15608
|
if (name === "active-task") {
|
|
@@ -15119,6 +15627,34 @@ class RemoteOrchestrator {
|
|
|
15119
15627
|
this.setEngineStateSig(next);
|
|
15120
15628
|
return;
|
|
15121
15629
|
}
|
|
15630
|
+
if (name === "task.jobs") {
|
|
15631
|
+
const p = payload;
|
|
15632
|
+
if (typeof p?.taskId !== "string" || p.kind !== "ensureWorktree")
|
|
15633
|
+
return;
|
|
15634
|
+
const current = this.taskJobsAcc();
|
|
15635
|
+
if (p.phase === "running") {
|
|
15636
|
+
const next = new Map(current);
|
|
15637
|
+
next.set(p.taskId, { kind: p.kind });
|
|
15638
|
+
this.setTaskJobsSig(next);
|
|
15639
|
+
return;
|
|
15640
|
+
}
|
|
15641
|
+
if ((p.phase === "done" || p.phase === "error") && current.has(p.taskId)) {
|
|
15642
|
+
const next = new Map(current);
|
|
15643
|
+
next.delete(p.taskId);
|
|
15644
|
+
this.setTaskJobsSig(next);
|
|
15645
|
+
}
|
|
15646
|
+
return;
|
|
15647
|
+
}
|
|
15648
|
+
if (name === "worktree.changes") {
|
|
15649
|
+
const next = parseWorktreeChangesPayload(payload);
|
|
15650
|
+
if (!next)
|
|
15651
|
+
return;
|
|
15652
|
+
const current = this.worktreeChangesAcc();
|
|
15653
|
+
if (current && sameWorktreeChangesMap(current, next))
|
|
15654
|
+
return;
|
|
15655
|
+
this.setWorktreeChangesSig(next);
|
|
15656
|
+
return;
|
|
15657
|
+
}
|
|
15122
15658
|
if (name === "ui-prefs") {
|
|
15123
15659
|
const p = payload;
|
|
15124
15660
|
if (typeof p?.theme !== "string")
|
|
@@ -15126,10 +15662,51 @@ class RemoteOrchestrator {
|
|
|
15126
15662
|
this.setUiPrefsSig({
|
|
15127
15663
|
theme: p.theme,
|
|
15128
15664
|
transparentBackground: p.transparentBackground === true,
|
|
15129
|
-
focusAccent: typeof p.focusAccent === "string" ? p.focusAccent : null
|
|
15665
|
+
focusAccent: typeof p.focusAccent === "string" ? p.focusAccent : null,
|
|
15666
|
+
sortMode: p.sortMode === "recent" ? "recent" : "default",
|
|
15667
|
+
keysCollapsed: p.keysCollapsed === true
|
|
15130
15668
|
});
|
|
15131
15669
|
return;
|
|
15132
15670
|
}
|
|
15671
|
+
if (name === "keybindings") {
|
|
15672
|
+
const p = payload;
|
|
15673
|
+
if (typeof p?.rev !== "number")
|
|
15674
|
+
return;
|
|
15675
|
+
this.setKeybindingsRevSig(p.rev);
|
|
15676
|
+
return;
|
|
15677
|
+
}
|
|
15678
|
+
}
|
|
15679
|
+
pruneEngineState(tasks) {
|
|
15680
|
+
const current = this.engineStateAcc();
|
|
15681
|
+
if (current.size === 0)
|
|
15682
|
+
return;
|
|
15683
|
+
const live = new Set(tasks.map((t) => t.id));
|
|
15684
|
+
let next = null;
|
|
15685
|
+
for (const key of current.keys()) {
|
|
15686
|
+
if (live.has(key))
|
|
15687
|
+
continue;
|
|
15688
|
+
if (!next)
|
|
15689
|
+
next = new Map(current);
|
|
15690
|
+
next.delete(key);
|
|
15691
|
+
}
|
|
15692
|
+
if (next)
|
|
15693
|
+
this.setEngineStateSig(next);
|
|
15694
|
+
}
|
|
15695
|
+
pruneTaskJobs(tasks) {
|
|
15696
|
+
const current = this.taskJobsAcc();
|
|
15697
|
+
if (current.size === 0)
|
|
15698
|
+
return;
|
|
15699
|
+
const live = new Set(tasks.map((t) => t.id));
|
|
15700
|
+
let next = null;
|
|
15701
|
+
for (const key of current.keys()) {
|
|
15702
|
+
if (live.has(key))
|
|
15703
|
+
continue;
|
|
15704
|
+
if (!next)
|
|
15705
|
+
next = new Map(current);
|
|
15706
|
+
next.delete(key);
|
|
15707
|
+
}
|
|
15708
|
+
if (next)
|
|
15709
|
+
this.setTaskJobsSig(next);
|
|
15133
15710
|
}
|
|
15134
15711
|
}
|
|
15135
15712
|
function deserializeTask(s) {
|
|
@@ -15154,6 +15731,7 @@ var init_remote_orchestrator = __esm(() => {
|
|
|
15154
15731
|
init_daemon_process();
|
|
15155
15732
|
init_protocol();
|
|
15156
15733
|
init_dev();
|
|
15734
|
+
init_worktree_changes();
|
|
15157
15735
|
init_version();
|
|
15158
15736
|
});
|
|
15159
15737
|
|
|
@@ -15523,10 +16101,6 @@ function resolveTheme(theme, mode = "dark") {
|
|
|
15523
16101
|
...out
|
|
15524
16102
|
};
|
|
15525
16103
|
}
|
|
15526
|
-
function withAlpha(color, alpha) {
|
|
15527
|
-
const [r, g, b] = color.toInts();
|
|
15528
|
-
return RGBA.fromInts(r ?? 0, g ?? 0, b ?? 0, alpha);
|
|
15529
|
-
}
|
|
15530
16104
|
var BUNDLED_THEMES, FOCUS_ACCENT_SLOTS, store, setStore, useTheme, ThemeProvider;
|
|
15531
16105
|
var init_theme2 = __esm(() => {
|
|
15532
16106
|
init_solid();
|
|
@@ -15598,8 +16172,7 @@ var init_theme2 = __esm(() => {
|
|
|
15598
16172
|
return {
|
|
15599
16173
|
...v,
|
|
15600
16174
|
background: transparent,
|
|
15601
|
-
backgroundPanel: transparent
|
|
15602
|
-
backgroundDialog: withAlpha(v.backgroundDialog, 128)
|
|
16175
|
+
backgroundPanel: transparent
|
|
15603
16176
|
};
|
|
15604
16177
|
});
|
|
15605
16178
|
createEffect(() => {
|
|
@@ -15687,7 +16260,7 @@ function dispatchKeyEvent(bindingStack, evt) {
|
|
|
15687
16260
|
continue;
|
|
15688
16261
|
const hit = cfg.bindings.find((b) => candidates.includes(b.key));
|
|
15689
16262
|
if (hit) {
|
|
15690
|
-
hit.cmd(evt);
|
|
16263
|
+
hit.cmd(evt, hit.slot);
|
|
15691
16264
|
evt.preventDefault();
|
|
15692
16265
|
return true;
|
|
15693
16266
|
}
|
|
@@ -16098,7 +16671,7 @@ function joinDrill(typedValue, baseExpanded, name) {
|
|
|
16098
16671
|
var init_path_helpers = () => {};
|
|
16099
16672
|
|
|
16100
16673
|
// src/tui/component/new-task-dialog/clone.ts
|
|
16101
|
-
import { spawn as
|
|
16674
|
+
import { spawn as spawn4 } from "child_process";
|
|
16102
16675
|
import * as fs5 from "fs";
|
|
16103
16676
|
import * as path11 from "path";
|
|
16104
16677
|
function deriveFolderName(url) {
|
|
@@ -16177,7 +16750,7 @@ function cloneRepo(url, target, onProgress) {
|
|
|
16177
16750
|
return new Promise((resolve8) => {
|
|
16178
16751
|
let stderrBuf = "";
|
|
16179
16752
|
try {
|
|
16180
|
-
const child =
|
|
16753
|
+
const child = spawn4("git", ["clone", "--progress", url, target], {
|
|
16181
16754
|
stdio: ["ignore", "ignore", "pipe"]
|
|
16182
16755
|
});
|
|
16183
16756
|
child.stderr?.setEncoding("utf-8");
|
|
@@ -17673,6 +18246,19 @@ var init_focus = __esm(() => {
|
|
|
17673
18246
|
});
|
|
17674
18247
|
|
|
17675
18248
|
// src/tui/context/keybindings.ts
|
|
18249
|
+
function resetKeymapToDefaults() {
|
|
18250
|
+
for (const row of KobeKeymap) {
|
|
18251
|
+
const def = KEYMAP_DEFAULTS.get(row.id);
|
|
18252
|
+
if (!def)
|
|
18253
|
+
continue;
|
|
18254
|
+
const mutable = row;
|
|
18255
|
+
mutable.keys = [...def.keys];
|
|
18256
|
+
mutable.hint = def.hint ? { ...def.hint } : undefined;
|
|
18257
|
+
}
|
|
18258
|
+
}
|
|
18259
|
+
function bumpKeymapVersion() {
|
|
18260
|
+
setKeymapVersion((v) => v + 1);
|
|
18261
|
+
}
|
|
17676
18262
|
function findBinding(id) {
|
|
17677
18263
|
return KobeKeymap.find((b) => b.id === id);
|
|
17678
18264
|
}
|
|
@@ -17690,20 +18276,20 @@ function bindByIds(handlers) {
|
|
|
17690
18276
|
console.warn(`[kobe/keybindings] bindByIds: id="${id}" has no chords (or doesn't exist in KobeKeymap)`);
|
|
17691
18277
|
continue;
|
|
17692
18278
|
}
|
|
17693
|
-
|
|
17694
|
-
out.push({ key: c, cmd });
|
|
18279
|
+
chords.forEach((c, slot) => out.push({ key: c, cmd, slot }));
|
|
17695
18280
|
}
|
|
17696
18281
|
return out;
|
|
17697
18282
|
}
|
|
17698
|
-
var KobeKeymap;
|
|
18283
|
+
var KobeKeymap, KEYMAP_DEFAULTS, keymapVersion, setKeymapVersion;
|
|
17699
18284
|
var init_keybindings2 = __esm(() => {
|
|
18285
|
+
init_dev();
|
|
17700
18286
|
KobeKeymap = [
|
|
17701
18287
|
{
|
|
17702
18288
|
id: "help.open",
|
|
17703
18289
|
scope: "global",
|
|
17704
18290
|
keys: ["f1"],
|
|
17705
18291
|
category: "Global",
|
|
17706
|
-
description: "Show
|
|
18292
|
+
description: "Show keybindings help",
|
|
17707
18293
|
hint: { keys: "F1", label: "help", pin: "right" }
|
|
17708
18294
|
},
|
|
17709
18295
|
{
|
|
@@ -18154,6 +18740,8 @@ var init_keybindings2 = __esm(() => {
|
|
|
18154
18740
|
hint: { keys: "ctrl+[/]", label: "tab" }
|
|
18155
18741
|
}
|
|
18156
18742
|
];
|
|
18743
|
+
KEYMAP_DEFAULTS = new Map(KobeKeymap.map((b) => [b.id, { keys: [...b.keys], hint: b.hint ? { ...b.hint } : undefined }]));
|
|
18744
|
+
[keymapVersion, setKeymapVersion] = createSignal(0);
|
|
18157
18745
|
});
|
|
18158
18746
|
|
|
18159
18747
|
// src/tui/context/keybindings-user.ts
|
|
@@ -18201,6 +18789,15 @@ function applyUserKeybindings() {
|
|
|
18201
18789
|
function userKeybindingsReport() {
|
|
18202
18790
|
return cached3 ?? applyUserKeybindings();
|
|
18203
18791
|
}
|
|
18792
|
+
function reloadUserKeybindings() {
|
|
18793
|
+
cached3 = null;
|
|
18794
|
+
resetKeybindingsFileCache();
|
|
18795
|
+
resetTmuxKeysCache();
|
|
18796
|
+
resetKeymapToDefaults();
|
|
18797
|
+
const report = applyUserKeybindings();
|
|
18798
|
+
bumpKeymapVersion();
|
|
18799
|
+
return report;
|
|
18800
|
+
}
|
|
18204
18801
|
var cached3 = null;
|
|
18205
18802
|
var init_keybindings_user = __esm(() => {
|
|
18206
18803
|
init_keybindings_file();
|
|
@@ -18299,9 +18896,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
|
|
|
18299
18896
|
var init_pulse = () => {};
|
|
18300
18897
|
|
|
18301
18898
|
// src/tui/lib/sound.ts
|
|
18302
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
18899
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
|
|
18303
18900
|
import { tmpdir as tmpdir2 } from "os";
|
|
18304
|
-
import { basename as
|
|
18901
|
+
import { basename as basename6, isAbsolute, join as join15, resolve as resolve8 } from "path";
|
|
18305
18902
|
function args(player, file, volume) {
|
|
18306
18903
|
if (player === "ffplay")
|
|
18307
18904
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -18324,13 +18921,13 @@ function pickPlayer() {
|
|
|
18324
18921
|
return cachedPlayer;
|
|
18325
18922
|
const path12 = process.env.PATH ?? "";
|
|
18326
18923
|
const segments = path12.split(":").filter(Boolean);
|
|
18327
|
-
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(
|
|
18924
|
+
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join15(dir, p)))) ?? null;
|
|
18328
18925
|
return cachedPlayer;
|
|
18329
18926
|
}
|
|
18330
18927
|
async function ensureAsset() {
|
|
18331
18928
|
cachedPath ??= (async () => {
|
|
18332
|
-
|
|
18333
|
-
const dest =
|
|
18929
|
+
mkdirSync6(DIR, { recursive: true });
|
|
18930
|
+
const dest = join15(DIR, basename6(pulseAsset));
|
|
18334
18931
|
const out = Bun.file(dest);
|
|
18335
18932
|
if (await out.exists())
|
|
18336
18933
|
return dest;
|
|
@@ -18360,7 +18957,7 @@ var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
|
18360
18957
|
var init_sound = __esm(() => {
|
|
18361
18958
|
init_pulse();
|
|
18362
18959
|
pulseAsset = isAbsolute(pulse_default) ? pulse_default : resolve8(import.meta.dir, pulse_default);
|
|
18363
|
-
DIR =
|
|
18960
|
+
DIR = join15(tmpdir2(), "kobe-sfx");
|
|
18364
18961
|
PLAYERS = [
|
|
18365
18962
|
"ffplay",
|
|
18366
18963
|
"mpv",
|
|
@@ -18584,20 +19181,28 @@ function UiPrefsSync(props) {
|
|
|
18584
19181
|
focusAccent: props.boot.focusAccent
|
|
18585
19182
|
});
|
|
18586
19183
|
const [prefsOrch, setPrefsOrch] = createSignal(null);
|
|
19184
|
+
let disposed = false;
|
|
18587
19185
|
onMount(() => {
|
|
18588
19186
|
(async () => {
|
|
19187
|
+
let remote = null;
|
|
18589
19188
|
try {
|
|
18590
19189
|
const client = await connectIfRunning();
|
|
18591
19190
|
if (!client) {
|
|
18592
19191
|
logClient("ui-prefs", "no daemon \u2014 keeping boot-time visual prefs");
|
|
18593
19192
|
return;
|
|
18594
19193
|
}
|
|
18595
|
-
|
|
19194
|
+
remote = new RemoteOrchestrator(client);
|
|
18596
19195
|
await remote.init();
|
|
18597
|
-
setPrefsOrch(remote);
|
|
18598
19196
|
} catch (err) {
|
|
18599
19197
|
logClientError("ui-prefs", err);
|
|
19198
|
+
remote?.dispose();
|
|
19199
|
+
return;
|
|
18600
19200
|
}
|
|
19201
|
+
if (disposed) {
|
|
19202
|
+
remote.dispose();
|
|
19203
|
+
return;
|
|
19204
|
+
}
|
|
19205
|
+
setPrefsOrch(remote);
|
|
18601
19206
|
})();
|
|
18602
19207
|
});
|
|
18603
19208
|
createEffect(() => {
|
|
@@ -18605,7 +19210,22 @@ function UiPrefsSync(props) {
|
|
|
18605
19210
|
if (payload)
|
|
18606
19211
|
applyUiPrefs(target, payload);
|
|
18607
19212
|
});
|
|
18608
|
-
|
|
19213
|
+
let lastKeybindingsRev = null;
|
|
19214
|
+
createEffect(() => {
|
|
19215
|
+
const rev = prefsOrch()?.keybindingsRevSignal()();
|
|
19216
|
+
if (rev == null)
|
|
19217
|
+
return;
|
|
19218
|
+
if (lastKeybindingsRev === null || rev === lastKeybindingsRev) {
|
|
19219
|
+
lastKeybindingsRev = rev;
|
|
19220
|
+
return;
|
|
19221
|
+
}
|
|
19222
|
+
lastKeybindingsRev = rev;
|
|
19223
|
+
reloadUserKeybindings();
|
|
19224
|
+
});
|
|
19225
|
+
onCleanup(() => {
|
|
19226
|
+
disposed = true;
|
|
19227
|
+
prefsOrch()?.dispose();
|
|
19228
|
+
});
|
|
18609
19229
|
return null;
|
|
18610
19230
|
}
|
|
18611
19231
|
async function bootPaneHost(opts) {
|
|
@@ -18830,6 +19450,31 @@ function repoBasename(repo) {
|
|
|
18830
19450
|
function flattenIds(rows) {
|
|
18831
19451
|
return rows.map((r) => r.task.id);
|
|
18832
19452
|
}
|
|
19453
|
+
function sameSidebarRowTask(a, b) {
|
|
19454
|
+
return a === b || a.id === b.id && a.kind === b.kind && a.title === b.title && a.repo === b.repo && a.branch === b.branch && a.worktreePath === b.worktreePath && a.status === b.status && a.archived === b.archived && a.pinned === b.pinned && a.vendor === b.vendor;
|
|
19455
|
+
}
|
|
19456
|
+
function reconcileSidebarRows(prev, next) {
|
|
19457
|
+
if (prev.length === 0)
|
|
19458
|
+
return next;
|
|
19459
|
+
const prevById = new Map;
|
|
19460
|
+
for (const row of prev)
|
|
19461
|
+
prevById.set(row.task.id, row);
|
|
19462
|
+
let allReused = prev.length === next.length;
|
|
19463
|
+
const out = new Array(next.length);
|
|
19464
|
+
for (let i = 0;i < next.length; i++) {
|
|
19465
|
+
const fresh = next[i];
|
|
19466
|
+
const old = prevById.get(fresh.task.id);
|
|
19467
|
+
if (old && old.flatIndex === fresh.flatIndex && sameSidebarRowTask(old.task, fresh.task)) {
|
|
19468
|
+
out[i] = old;
|
|
19469
|
+
if (allReused && prev[i] !== old)
|
|
19470
|
+
allReused = false;
|
|
19471
|
+
} else {
|
|
19472
|
+
out[i] = fresh;
|
|
19473
|
+
allReused = false;
|
|
19474
|
+
}
|
|
19475
|
+
}
|
|
19476
|
+
return allReused ? prev : out;
|
|
19477
|
+
}
|
|
18833
19478
|
var init_groups = () => {};
|
|
18834
19479
|
|
|
18835
19480
|
// src/tui/quick-task/host.tsx
|
|
@@ -19107,12 +19752,13 @@ function groupBindings(keymap) {
|
|
|
19107
19752
|
rows: groups.get(cat)
|
|
19108
19753
|
}));
|
|
19109
19754
|
}
|
|
19110
|
-
function HelpDialog() {
|
|
19755
|
+
function HelpDialog(props = {}) {
|
|
19111
19756
|
const dialog = useDialog();
|
|
19112
19757
|
const {
|
|
19113
19758
|
theme
|
|
19114
19759
|
} = useTheme();
|
|
19115
19760
|
const grouped = () => groupBindings(KobeKeymap);
|
|
19761
|
+
const close = () => props.onClose ? props.onClose() : dialog.clear();
|
|
19116
19762
|
const [prefixGlyph, setPrefixGlyph] = createSignal("\u2303B");
|
|
19117
19763
|
onMount(() => {
|
|
19118
19764
|
runTmuxCapturing(["show-options", "-g", "prefix"]).then(({
|
|
@@ -19129,7 +19775,7 @@ function HelpDialog() {
|
|
|
19129
19775
|
useBindings(() => ({
|
|
19130
19776
|
bindings: [{
|
|
19131
19777
|
key: "?",
|
|
19132
|
-
cmd:
|
|
19778
|
+
cmd: close
|
|
19133
19779
|
}]
|
|
19134
19780
|
}));
|
|
19135
19781
|
return (() => {
|
|
@@ -19147,7 +19793,7 @@ function HelpDialog() {
|
|
|
19147
19793
|
setProp(_el$2, "flexShrink", 0);
|
|
19148
19794
|
insertNode(_el$3, createTextNode(`kobe \u2014 keybindings`));
|
|
19149
19795
|
insertNode(_el$5, createTextNode(`esc`));
|
|
19150
|
-
setProp(_el$5, "onMouseUp",
|
|
19796
|
+
setProp(_el$5, "onMouseUp", close);
|
|
19151
19797
|
insertNode(_el$7, _el$8);
|
|
19152
19798
|
setProp(_el$7, "flexShrink", 1);
|
|
19153
19799
|
setProp(_el$7, "flexGrow", 1);
|
|
@@ -19517,7 +20163,7 @@ var init_dialog_confirm = __esm(() => {
|
|
|
19517
20163
|
|
|
19518
20164
|
// src/tui/component/settings-dialog/actions.ts
|
|
19519
20165
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
19520
|
-
import { join as
|
|
20166
|
+
import { join as join16 } from "path";
|
|
19521
20167
|
function hasRestartableDaemon(orchestrator) {
|
|
19522
20168
|
return orchestrator instanceof RemoteOrchestrator;
|
|
19523
20169
|
}
|
|
@@ -19534,7 +20180,7 @@ async function confirmResetState(dialog, kv, renderer) {
|
|
|
19534
20180
|
return;
|
|
19535
20181
|
kv.clear();
|
|
19536
20182
|
try {
|
|
19537
|
-
unlinkSync2(
|
|
20183
|
+
unlinkSync2(join16(homeDir(), ".kobe", "tasks.json"));
|
|
19538
20184
|
} catch (err) {
|
|
19539
20185
|
if (err.code !== "ENOENT") {
|
|
19540
20186
|
console.error("kobe: failed to delete tasks.json during reset:", err);
|
|
@@ -21771,9 +22417,9 @@ var init_task_actions = __esm(() => {
|
|
|
21771
22417
|
});
|
|
21772
22418
|
|
|
21773
22419
|
// src/tui/lib/worktree-opener.ts
|
|
21774
|
-
import { spawn as
|
|
22420
|
+
import { spawn as spawn5 } from "child_process";
|
|
21775
22421
|
import { existsSync as existsSync15 } from "fs";
|
|
21776
|
-
import { basename as
|
|
22422
|
+
import { basename as basename7, delimiter, isAbsolute as isAbsolute2, join as join17 } from "path";
|
|
21777
22423
|
function executableOnPath(command, env, exists) {
|
|
21778
22424
|
if (isAbsolute2(command))
|
|
21779
22425
|
return exists(command);
|
|
@@ -21781,13 +22427,13 @@ function executableOnPath(command, env, exists) {
|
|
|
21781
22427
|
for (const dir of pathEnv.split(delimiter)) {
|
|
21782
22428
|
if (!dir)
|
|
21783
22429
|
continue;
|
|
21784
|
-
if (exists(
|
|
22430
|
+
if (exists(join17(dir, command)))
|
|
21785
22431
|
return true;
|
|
21786
22432
|
}
|
|
21787
22433
|
return false;
|
|
21788
22434
|
}
|
|
21789
22435
|
function labelForOverride(command) {
|
|
21790
|
-
const name =
|
|
22436
|
+
const name = basename7(command);
|
|
21791
22437
|
if (name === "code")
|
|
21792
22438
|
return "VS Code";
|
|
21793
22439
|
if (name === "cursor")
|
|
@@ -21830,7 +22476,7 @@ function buildOpenWorktreeCommand(worktreePath, opener) {
|
|
|
21830
22476
|
function openWorktree(worktreePath, opener, deps = {}) {
|
|
21831
22477
|
if (!worktreePath)
|
|
21832
22478
|
return false;
|
|
21833
|
-
const spawnFn = deps.spawn ??
|
|
22479
|
+
const spawnFn = deps.spawn ?? spawn5;
|
|
21834
22480
|
const [command, args2] = buildOpenWorktreeCommand(worktreePath, opener);
|
|
21835
22481
|
try {
|
|
21836
22482
|
const child = spawnFn(command, args2, { detached: true, stdio: "ignore" });
|
|
@@ -21879,15 +22525,6 @@ var init_worktree_opener = __esm(() => {
|
|
|
21879
22525
|
});
|
|
21880
22526
|
|
|
21881
22527
|
// src/tui/lib/background-poll.ts
|
|
21882
|
-
import { spawn as spawn5 } from "child_process";
|
|
21883
|
-
function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
|
|
21884
|
-
if (timedOut)
|
|
21885
|
-
return startedAt + cfg.slowRetryMs;
|
|
21886
|
-
return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
|
|
21887
|
-
}
|
|
21888
|
-
function shouldPoll(state, now) {
|
|
21889
|
-
return !state.inFlight && now >= state.nextAllowedAt;
|
|
21890
|
-
}
|
|
21891
22528
|
function createBackgroundPoller(cfg) {
|
|
21892
22529
|
const entries = new Map;
|
|
21893
22530
|
function entryFor(key) {
|
|
@@ -21909,58 +22546,17 @@ function createBackgroundPoller(cfg) {
|
|
|
21909
22546
|
if (!key)
|
|
21910
22547
|
return;
|
|
21911
22548
|
const entry = entryFor(key);
|
|
21912
|
-
|
|
21913
|
-
if (!shouldPoll(entry, startedAt))
|
|
21914
|
-
return;
|
|
21915
|
-
entry.inFlight = true;
|
|
21916
|
-
const controller = new AbortController;
|
|
21917
|
-
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
21918
|
-
(async () => {
|
|
21919
|
-
let value;
|
|
21920
|
-
let ok = false;
|
|
21921
|
-
try {
|
|
21922
|
-
value = await cfg.run(key, controller.signal);
|
|
21923
|
-
ok = true;
|
|
21924
|
-
} catch {}
|
|
21925
|
-
clearTimeout(timer);
|
|
21926
|
-
const timedOut = controller.signal.aborted;
|
|
21927
|
-
entry.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
|
|
21928
|
-
entry.inFlight = false;
|
|
21929
|
-
if (ok && !timedOut)
|
|
21930
|
-
entry.write(value);
|
|
21931
|
-
})();
|
|
22549
|
+
maybeStartScheduledRun(entry, cfg, (signal) => cfg.run(key, signal), (value) => entry.write(value));
|
|
21932
22550
|
},
|
|
21933
22551
|
reset() {
|
|
21934
22552
|
entries.clear();
|
|
21935
22553
|
}
|
|
21936
22554
|
};
|
|
21937
22555
|
}
|
|
21938
|
-
function spawnCapture(cmd, args2, opts) {
|
|
21939
|
-
return new Promise((resolve9) => {
|
|
21940
|
-
let out = "";
|
|
21941
|
-
let settled = false;
|
|
21942
|
-
const finish = (status) => {
|
|
21943
|
-
if (settled)
|
|
21944
|
-
return;
|
|
21945
|
-
settled = true;
|
|
21946
|
-
resolve9({ status, stdout: out });
|
|
21947
|
-
};
|
|
21948
|
-
const child = spawn5(cmd, args2.slice(), {
|
|
21949
|
-
cwd: opts.cwd,
|
|
21950
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
21951
|
-
env: opts.env,
|
|
21952
|
-
signal: opts.signal,
|
|
21953
|
-
killSignal: "SIGKILL"
|
|
21954
|
-
});
|
|
21955
|
-
child.stdout?.on("data", (chunk) => {
|
|
21956
|
-
out += String(chunk);
|
|
21957
|
-
});
|
|
21958
|
-
child.on("error", () => finish(null));
|
|
21959
|
-
child.on("close", (code) => finish(code));
|
|
21960
|
-
});
|
|
21961
|
-
}
|
|
21962
22556
|
var init_background_poll = __esm(() => {
|
|
21963
22557
|
init_dev();
|
|
22558
|
+
init_poll_scheduling();
|
|
22559
|
+
init_poll_scheduling();
|
|
21964
22560
|
});
|
|
21965
22561
|
|
|
21966
22562
|
// src/tui/panes/sidebar/git-head.ts
|
|
@@ -22098,20 +22694,18 @@ function useSidebarBindings(opts) {
|
|
|
22098
22694
|
useBindings(() => ({
|
|
22099
22695
|
enabled: opts.focused() && !searchModeAccessor(),
|
|
22100
22696
|
bindings: bindByIds({
|
|
22101
|
-
"sidebar.nav": (
|
|
22697
|
+
"sidebar.nav": (_evt, slot) => {
|
|
22698
|
+
const down = (slot ?? 0) % 2 === 0;
|
|
22102
22699
|
if (moveModeAccessor()) {
|
|
22103
22700
|
const id = cursorTaskId();
|
|
22104
22701
|
if (id === undefined)
|
|
22105
22702
|
return;
|
|
22106
|
-
|
|
22107
|
-
opts.onMoveRequest?.(id, 1);
|
|
22108
|
-
else if (evt.name === "k" || evt.name === "up")
|
|
22109
|
-
opts.onMoveRequest?.(id, -1);
|
|
22703
|
+
opts.onMoveRequest?.(id, down ? 1 : -1);
|
|
22110
22704
|
return;
|
|
22111
22705
|
}
|
|
22112
|
-
if (
|
|
22706
|
+
if (down)
|
|
22113
22707
|
ctrl.moveDown();
|
|
22114
|
-
else
|
|
22708
|
+
else
|
|
22115
22709
|
ctrl.moveUp();
|
|
22116
22710
|
},
|
|
22117
22711
|
"sidebar.select": () => {
|
|
@@ -22185,21 +22779,18 @@ function useSidebarBindings(opts) {
|
|
|
22185
22779
|
useBindings(() => ({
|
|
22186
22780
|
enabled: opts.focused(),
|
|
22187
22781
|
bindings: bindByIds({
|
|
22188
|
-
"sidebar.view": (
|
|
22189
|
-
|
|
22190
|
-
opts.onViewSwitch?.(1);
|
|
22191
|
-
else
|
|
22192
|
-
opts.onViewSwitch?.(-1);
|
|
22782
|
+
"sidebar.view": (_evt, slot) => {
|
|
22783
|
+
opts.onViewSwitch?.((slot ?? 0) % 2 === 0 ? -1 : 1);
|
|
22193
22784
|
}
|
|
22194
22785
|
})
|
|
22195
22786
|
}));
|
|
22196
22787
|
useBindings(() => ({
|
|
22197
22788
|
enabled: opts.focused() && searchModeAccessor(),
|
|
22198
22789
|
bindings: bindByIds({
|
|
22199
|
-
"sidebar.search.nav": (
|
|
22200
|
-
if (
|
|
22790
|
+
"sidebar.search.nav": (_evt, slot) => {
|
|
22791
|
+
if ((slot ?? 0) % 2 === 0)
|
|
22201
22792
|
ctrl.moveDown();
|
|
22202
|
-
else
|
|
22793
|
+
else
|
|
22203
22794
|
ctrl.moveUp();
|
|
22204
22795
|
},
|
|
22205
22796
|
"sidebar.search.submit": () => {
|
|
@@ -22255,11 +22846,12 @@ function buildSidebarRowView(opts) {
|
|
|
22255
22846
|
const activityBadge = activityBadgeFor(activityState);
|
|
22256
22847
|
const activityLabel = activityLabelFor(activityState);
|
|
22257
22848
|
const untrackedCustomEngine = isCustomEngineTask(task) && !hasActivity;
|
|
22258
|
-
const
|
|
22849
|
+
const materializing = opts.job !== undefined;
|
|
22850
|
+
const loading = materializing || !untrackedCustomEngine && (activityState === "running" || opts.live || !hasActivity && !isMain && task.status === "in_progress");
|
|
22259
22851
|
const spinner = IN_PROGRESS_SPINNER[opts.spinnerFrame] ?? IN_PROGRESS_SPINNER[0];
|
|
22260
|
-
const tone = untrackedCustomEngine ? "textMuted" : activityLabel?.tone ?? (loading ? "primary" : activityBadge?.tone ?? badge.tone);
|
|
22852
|
+
const tone = materializing ? "primary" : untrackedCustomEngine ? "textMuted" : activityLabel?.tone ?? (loading ? "primary" : activityBadge?.tone ?? badge.tone);
|
|
22261
22853
|
const fallbackSubtitle = untrackedCustomEngine ? NO_TRACKING_SUBTITLE : STATUS_LABEL[task.status];
|
|
22262
|
-
const subtitleText = activityLabel ? opts.truncateBranch(activityLabel.text, opts.subtitleBudget) : branch.length > 0 ? opts.truncateBranch(branch, opts.subtitleBudget) : opts.truncateBranch(fallbackSubtitle, opts.subtitleBudget);
|
|
22854
|
+
const subtitleText = materializing ? opts.truncateBranch(MATERIALIZING_SUBTITLE, opts.subtitleBudget) : activityLabel ? opts.truncateBranch(activityLabel.text, opts.subtitleBudget) : branch.length > 0 ? opts.truncateBranch(branch, opts.subtitleBudget) : opts.truncateBranch(fallbackSubtitle, opts.subtitleBudget);
|
|
22263
22855
|
const restGlyph = untrackedCustomEngine ? NO_TRACKING_GLYPH : activityBadge?.glyph ?? badge.glyph;
|
|
22264
22856
|
const restProjectGlyph = untrackedCustomEngine ? NO_TRACKING_GLYPH : activityBadge?.glyph ?? "\u2605";
|
|
22265
22857
|
return {
|
|
@@ -22286,7 +22878,7 @@ function activityBadgeFor(state) {
|
|
|
22286
22878
|
return null;
|
|
22287
22879
|
}
|
|
22288
22880
|
}
|
|
22289
|
-
var STATUS_BADGE, STATUS_LABEL, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, NO_TRACKING_GLYPH = "\xB7", NO_TRACKING_SUBTITLE = "no activity tracking";
|
|
22881
|
+
var STATUS_BADGE, STATUS_LABEL, IN_PROGRESS_SPINNER, SPINNER_FRAME_MS = 100, NO_TRACKING_GLYPH = "\xB7", NO_TRACKING_SUBTITLE = "no activity tracking", MATERIALIZING_SUBTITLE = "materializing";
|
|
22290
22882
|
var init_row_view = __esm(() => {
|
|
22291
22883
|
init_vendor();
|
|
22292
22884
|
init_groups();
|
|
@@ -22324,7 +22916,7 @@ var init_worktree_changes_poller = __esm(() => {
|
|
|
22324
22916
|
ZERO2 = { added: 0, deleted: 0 };
|
|
22325
22917
|
poller2 = createBackgroundPoller({
|
|
22326
22918
|
initial: ZERO2,
|
|
22327
|
-
equals:
|
|
22919
|
+
equals: sameWorktreeChanges,
|
|
22328
22920
|
timeoutMs: POLL_TIMEOUT_MS,
|
|
22329
22921
|
slowRetryMs: SLOW_REPO_RETRY_MS,
|
|
22330
22922
|
minIntervalMs: MIN_POLL_INTERVAL_MS,
|
|
@@ -22406,12 +22998,13 @@ function Sidebar(props) {
|
|
|
22406
22998
|
onCleanup(() => r.keyInput.off("keypress", listener2));
|
|
22407
22999
|
});
|
|
22408
23000
|
const [branchTick, setBranchTick] = createSignal(0);
|
|
22409
|
-
setInterval(() => setBranchTick((n) => n + 1), MAIN_BRANCH_POLL_MS);
|
|
23001
|
+
const branchInterval = setInterval(() => setBranchTick((n) => n + 1), MAIN_BRANCH_POLL_MS);
|
|
23002
|
+
onCleanup(() => clearInterval(branchInterval));
|
|
22410
23003
|
const [spinnerFrame, setSpinnerFrame] = createSignal(0);
|
|
22411
23004
|
const spinnerInterval = setInterval(() => setSpinnerFrame((n) => (n + 1) % IN_PROGRESS_SPINNER.length), SPINNER_FRAME_MS);
|
|
22412
23005
|
onCleanup(() => clearInterval(spinnerInterval));
|
|
22413
23006
|
const sortMode = () => props.sortMode?.() ?? "default";
|
|
22414
|
-
const rows = createMemo(() => buildRows(props.tasks(), view(), searchMode() ? searchQuery() : "", sortMode()));
|
|
23007
|
+
const rows = createMemo((prev) => reconcileSidebarRows(prev, buildRows(props.tasks(), view(), searchMode() ? searchQuery() : "", sortMode())), []);
|
|
22415
23008
|
const flatIds = createMemo(() => flattenIds(rows()));
|
|
22416
23009
|
const firstTaskFlatIndex = createMemo(() => {
|
|
22417
23010
|
const r = rows();
|
|
@@ -22760,10 +23353,15 @@ function Sidebar(props) {
|
|
|
22760
23353
|
}
|
|
22761
23354
|
};
|
|
22762
23355
|
const changes = createMemo(() => {
|
|
23356
|
+
const pushed = pickPushedChanges(props.worktreeChanges?.(), task.worktreePath);
|
|
23357
|
+
if (pushed)
|
|
23358
|
+
return pushed;
|
|
22763
23359
|
branchTick();
|
|
22764
23360
|
if (!task.archived)
|
|
22765
23361
|
pollWorktreeChanges(task.worktreePath);
|
|
22766
23362
|
return worktreeChanges(task.worktreePath);
|
|
23363
|
+
}, undefined, {
|
|
23364
|
+
equals: sameWorktreeChanges
|
|
22767
23365
|
});
|
|
22768
23366
|
const projectBranch = createMemo(() => {
|
|
22769
23367
|
branchTick();
|
|
@@ -22774,6 +23372,7 @@ function Sidebar(props) {
|
|
|
22774
23372
|
const rowView = createMemo(() => buildSidebarRowView({
|
|
22775
23373
|
task,
|
|
22776
23374
|
activity: props.engineState?.().get(task.id),
|
|
23375
|
+
job: props.taskJobs?.().get(task.id),
|
|
22777
23376
|
live: isLive(),
|
|
22778
23377
|
spinnerFrame: spinnerFrame(),
|
|
22779
23378
|
subtitleBudget: subtitleBudget(),
|
|
@@ -23206,6 +23805,7 @@ var init_Sidebar = __esm(() => {
|
|
|
23206
23805
|
init_groups();
|
|
23207
23806
|
init_keys();
|
|
23208
23807
|
init_row_view();
|
|
23808
|
+
init_worktree_changes();
|
|
23209
23809
|
init_worktree_changes_poller();
|
|
23210
23810
|
VIEW_TABS = [{
|
|
23211
23811
|
view: "active",
|
|
@@ -23251,7 +23851,7 @@ function TasksShell(props) {
|
|
|
23251
23851
|
});
|
|
23252
23852
|
}
|
|
23253
23853
|
const [moveMode, setMoveMode] = createSignal(false);
|
|
23254
|
-
const [sortMode, setSortMode] = createSignal("default");
|
|
23854
|
+
const [sortMode, setSortMode] = createSignal(kv.get("activeSortMode") === "recent" ? "recent" : "default");
|
|
23255
23855
|
const [updateInfo, setUpdateInfo] = createSignal(null);
|
|
23256
23856
|
const dimensions = useTerminalDimensions();
|
|
23257
23857
|
let activeConfirmed = !props.initialTaskId;
|
|
@@ -23266,6 +23866,11 @@ function TasksShell(props) {
|
|
|
23266
23866
|
}
|
|
23267
23867
|
setSelectedId(active);
|
|
23268
23868
|
});
|
|
23869
|
+
createEffect(() => {
|
|
23870
|
+
const payload = props.orch?.uiPrefsSignal()();
|
|
23871
|
+
if (payload && payload.sortMode !== sortMode())
|
|
23872
|
+
setSortMode(payload.sortMode);
|
|
23873
|
+
});
|
|
23269
23874
|
createEffect(() => {
|
|
23270
23875
|
const info = props.orch?.updateSignal()();
|
|
23271
23876
|
if (info)
|
|
@@ -23333,6 +23938,14 @@ function TasksShell(props) {
|
|
|
23333
23938
|
console.error("[kobe tasks] failed to refresh workspace panes:", err);
|
|
23334
23939
|
}
|
|
23335
23940
|
}
|
|
23941
|
+
async function openHelp() {
|
|
23942
|
+
const session = await currentSessionName();
|
|
23943
|
+
if (session) {
|
|
23944
|
+
await openHelpTab(session);
|
|
23945
|
+
return;
|
|
23946
|
+
}
|
|
23947
|
+
HelpDialog.show(dialog);
|
|
23948
|
+
}
|
|
23336
23949
|
async function openUpdate() {
|
|
23337
23950
|
const info = updateInfo();
|
|
23338
23951
|
if (!info?.hasUpdate) {
|
|
@@ -23404,13 +24017,21 @@ function TasksShell(props) {
|
|
|
23404
24017
|
setSelectedId(id);
|
|
23405
24018
|
await props.reload();
|
|
23406
24019
|
}
|
|
23407
|
-
const keysCollapsed = (
|
|
23408
|
-
const setKeysCollapsed = (next) =>
|
|
24020
|
+
const [keysCollapsed, setKeysCollapsedSig] = createSignal(kv.get("tasksPane.keysCollapsed", false) === true);
|
|
24021
|
+
const setKeysCollapsed = (next) => {
|
|
24022
|
+
setKeysCollapsedSig(next);
|
|
24023
|
+
kv.set("tasksPane.keysCollapsed", next);
|
|
24024
|
+
};
|
|
24025
|
+
createEffect(() => {
|
|
24026
|
+
const payload = props.orch?.uiPrefsSignal()();
|
|
24027
|
+
if (payload && payload.keysCollapsed !== keysCollapsed())
|
|
24028
|
+
setKeysCollapsedSig(payload.keysCollapsed);
|
|
24029
|
+
});
|
|
23409
24030
|
const [searchActive, setSearchActive] = createSignal(false);
|
|
23410
24031
|
useBindings(() => ({
|
|
23411
24032
|
enabled: dialog.stack.length === 0 && !searchActive(),
|
|
23412
24033
|
bindings: bindByIds({
|
|
23413
|
-
"help.open": () =>
|
|
24034
|
+
"help.open": () => void openHelp(),
|
|
23414
24035
|
"task.new": () => void createTask(),
|
|
23415
24036
|
"settings.open.sidebar": () => void openSettings(),
|
|
23416
24037
|
"tasks.update": () => void openUpdate(),
|
|
@@ -23435,6 +24056,9 @@ function TasksShell(props) {
|
|
|
23435
24056
|
async function switchTo(id) {
|
|
23436
24057
|
const name = tmuxSessionName(id);
|
|
23437
24058
|
const task = props.tasks().find((t) => t.id === id);
|
|
24059
|
+
const from = await currentSessionName();
|
|
24060
|
+
if (from && from !== name)
|
|
24061
|
+
await captureGlobalLayout(from);
|
|
23438
24062
|
const exists = await sessionExists(name);
|
|
23439
24063
|
if (exists) {
|
|
23440
24064
|
const cwd2 = await getSessionOption(name, "@kobe_worktree") || task?.worktreePath || "";
|
|
@@ -23532,6 +24156,17 @@ function TasksShell(props) {
|
|
|
23532
24156
|
get engineState() {
|
|
23533
24157
|
return memo2(() => !!props.orch)() ? props.orch.engineStateSignal() : undefined;
|
|
23534
24158
|
},
|
|
24159
|
+
get taskJobs() {
|
|
24160
|
+
return memo2(() => !!props.orch)() ? props.orch.taskJobsSignal() : undefined;
|
|
24161
|
+
},
|
|
24162
|
+
get worktreeChanges() {
|
|
24163
|
+
return props.orch ? () => {
|
|
24164
|
+
const orch = props.orch;
|
|
24165
|
+
if (!orch || orch.connectionStateSignal()() !== "online")
|
|
24166
|
+
return null;
|
|
24167
|
+
return orch.worktreeChangesSignal()();
|
|
24168
|
+
} : undefined;
|
|
24169
|
+
},
|
|
23535
24170
|
onRenameRequest: (id) => void renameTask(id),
|
|
23536
24171
|
onDeleteRequest: (id) => void deleteTask(id),
|
|
23537
24172
|
onArchiveRequest: (id) => void archiveTask(id),
|
|
@@ -23546,7 +24181,11 @@ function TasksShell(props) {
|
|
|
23546
24181
|
onMoveRequest: (id, delta) => void moveTask(id, delta),
|
|
23547
24182
|
onMoveModeExit: () => setMoveMode(false),
|
|
23548
24183
|
sortMode,
|
|
23549
|
-
onSortModeToggle: () =>
|
|
24184
|
+
onSortModeToggle: () => {
|
|
24185
|
+
const next = sortMode() === "default" ? "recent" : "default";
|
|
24186
|
+
setSortMode(next);
|
|
24187
|
+
kv.set("activeSortMode", next);
|
|
24188
|
+
},
|
|
23550
24189
|
focused: () => dialog.stack.length === 0,
|
|
23551
24190
|
onSearchActiveChange: setSearchActive
|
|
23552
24191
|
}));
|
|
@@ -23578,6 +24217,7 @@ function ShortcutHints(props) {
|
|
|
23578
24217
|
});
|
|
23579
24218
|
});
|
|
23580
24219
|
const tmuxHints = () => {
|
|
24220
|
+
keymapVersion();
|
|
23581
24221
|
const res = resolveUserTmuxKeys();
|
|
23582
24222
|
const b = res.binds;
|
|
23583
24223
|
const out = [];
|
|
@@ -23985,9 +24625,73 @@ var init_host4 = __esm(() => {
|
|
|
23985
24625
|
init_dialog();
|
|
23986
24626
|
});
|
|
23987
24627
|
|
|
23988
|
-
// src/tui/
|
|
24628
|
+
// src/tui/help/host.tsx
|
|
23989
24629
|
var exports_host5 = {};
|
|
23990
24630
|
__export(exports_host5, {
|
|
24631
|
+
startHelpHost: () => startHelpHost
|
|
24632
|
+
});
|
|
24633
|
+
function HelpPage() {
|
|
24634
|
+
const dialog = useDialog();
|
|
24635
|
+
const {
|
|
24636
|
+
theme
|
|
24637
|
+
} = useTheme();
|
|
24638
|
+
function exit() {
|
|
24639
|
+
process.exit(0);
|
|
24640
|
+
}
|
|
24641
|
+
useBindings(() => ({
|
|
24642
|
+
enabled: dialog.stack.length === 0,
|
|
24643
|
+
bindings: [{
|
|
24644
|
+
key: "escape",
|
|
24645
|
+
cmd: exit
|
|
24646
|
+
}, {
|
|
24647
|
+
key: "q",
|
|
24648
|
+
cmd: exit
|
|
24649
|
+
}, {
|
|
24650
|
+
key: "f1",
|
|
24651
|
+
cmd: exit
|
|
24652
|
+
}, {
|
|
24653
|
+
key: "ctrl+c",
|
|
24654
|
+
cmd: exit
|
|
24655
|
+
}]
|
|
24656
|
+
}));
|
|
24657
|
+
return (() => {
|
|
24658
|
+
var _el$ = createElement("box");
|
|
24659
|
+
setProp(_el$, "flexGrow", 1);
|
|
24660
|
+
setProp(_el$, "paddingTop", 1);
|
|
24661
|
+
insert(_el$, createComponent2(HelpDialog, {
|
|
24662
|
+
onClose: exit
|
|
24663
|
+
}));
|
|
24664
|
+
effect((_$p) => setProp(_el$, "backgroundColor", theme.background, _$p));
|
|
24665
|
+
return _el$;
|
|
24666
|
+
})();
|
|
24667
|
+
}
|
|
24668
|
+
async function startHelpHost() {
|
|
24669
|
+
await bootPaneHost({
|
|
24670
|
+
providers: {
|
|
24671
|
+
kv: false,
|
|
24672
|
+
focus: false
|
|
24673
|
+
},
|
|
24674
|
+
setup: () => ({
|
|
24675
|
+
root: () => createComponent2(HelpPage, {})
|
|
24676
|
+
})
|
|
24677
|
+
});
|
|
24678
|
+
}
|
|
24679
|
+
var init_host5 = __esm(() => {
|
|
24680
|
+
init_solid();
|
|
24681
|
+
init_solid();
|
|
24682
|
+
init_solid();
|
|
24683
|
+
init_solid();
|
|
24684
|
+
init_solid();
|
|
24685
|
+
init_help_dialog();
|
|
24686
|
+
init_theme2();
|
|
24687
|
+
init_host_boot();
|
|
24688
|
+
init_keymap();
|
|
24689
|
+
init_dialog();
|
|
24690
|
+
});
|
|
24691
|
+
|
|
24692
|
+
// src/tui/update/host.tsx
|
|
24693
|
+
var exports_host6 = {};
|
|
24694
|
+
__export(exports_host6, {
|
|
23991
24695
|
startUpdateHost: () => startUpdateHost
|
|
23992
24696
|
});
|
|
23993
24697
|
import { spawn as spawn6, spawnSync as spawnSync11 } from "child_process";
|
|
@@ -24342,7 +25046,7 @@ async function startUpdateHost() {
|
|
|
24342
25046
|
})
|
|
24343
25047
|
});
|
|
24344
25048
|
}
|
|
24345
|
-
var
|
|
25049
|
+
var init_host6 = __esm(() => {
|
|
24346
25050
|
init_solid();
|
|
24347
25051
|
init_solid();
|
|
24348
25052
|
init_solid();
|
|
@@ -24666,26 +25370,24 @@ function useFileTreeBindings(opts) {
|
|
|
24666
25370
|
useBindings(() => ({
|
|
24667
25371
|
enabled: opts.focused(),
|
|
24668
25372
|
bindings: bindByIds({
|
|
24669
|
-
"files.nav": (
|
|
24670
|
-
if (
|
|
25373
|
+
"files.nav": (_evt, slot) => {
|
|
25374
|
+
if ((slot ?? 0) % 2 === 0)
|
|
24671
25375
|
opts.moveDown();
|
|
24672
|
-
else
|
|
25376
|
+
else
|
|
24673
25377
|
opts.moveUp();
|
|
24674
25378
|
},
|
|
24675
|
-
"files.hierarchy": (
|
|
24676
|
-
if (
|
|
24677
|
-
opts.expandOrDescend();
|
|
24678
|
-
else if (evt.name === "h" || evt.name === "left")
|
|
25379
|
+
"files.hierarchy": (_evt, slot) => {
|
|
25380
|
+
if ((slot ?? 0) % 2 === 0)
|
|
24679
25381
|
opts.collapseOrParent();
|
|
25382
|
+
else
|
|
25383
|
+
opts.expandOrDescend();
|
|
24680
25384
|
},
|
|
24681
|
-
"files.tab": (
|
|
25385
|
+
"files.tab": (_evt, slot) => {
|
|
24682
25386
|
const cur = opts.currentTab();
|
|
24683
25387
|
const idx = TAB_ORDER.indexOf(cur);
|
|
24684
25388
|
if (idx < 0)
|
|
24685
25389
|
return;
|
|
24686
|
-
const delta =
|
|
24687
|
-
if (delta === 0)
|
|
24688
|
-
return;
|
|
25390
|
+
const delta = (slot ?? 0) % 2 === 0 ? -1 : 1;
|
|
24689
25391
|
const next = TAB_ORDER[(idx + delta + TAB_ORDER.length) % TAB_ORDER.length];
|
|
24690
25392
|
if (next)
|
|
24691
25393
|
opts.setTab(next);
|
|
@@ -24753,8 +25455,109 @@ function spawnDetached(cmd, args2, onError) {
|
|
|
24753
25455
|
}
|
|
24754
25456
|
var init_open_external = () => {};
|
|
24755
25457
|
|
|
25458
|
+
// src/tui/panes/filetree/rows.ts
|
|
25459
|
+
function flattenTree(node, expanded, depth, out) {
|
|
25460
|
+
for (const child of node.children) {
|
|
25461
|
+
if (child.isDir) {
|
|
25462
|
+
const isOpen = expanded.has(child.path);
|
|
25463
|
+
out.push({
|
|
25464
|
+
kind: "dir",
|
|
25465
|
+
path: child.path,
|
|
25466
|
+
name: child.name,
|
|
25467
|
+
depth,
|
|
25468
|
+
expanded: isOpen,
|
|
25469
|
+
hasChildren: child.children.length > 0
|
|
25470
|
+
});
|
|
25471
|
+
if (isOpen)
|
|
25472
|
+
flattenTree(child, expanded, depth + 1, out);
|
|
25473
|
+
} else {
|
|
25474
|
+
out.push({ kind: "file", path: child.path, name: child.name, depth });
|
|
25475
|
+
}
|
|
25476
|
+
}
|
|
25477
|
+
}
|
|
25478
|
+
function statusRows(entries) {
|
|
25479
|
+
return entries.map((e) => ({
|
|
25480
|
+
kind: "status",
|
|
25481
|
+
path: e.path,
|
|
25482
|
+
status: e.status,
|
|
25483
|
+
added: e.added,
|
|
25484
|
+
deleted: e.deleted
|
|
25485
|
+
}));
|
|
25486
|
+
}
|
|
25487
|
+
function rowKey(row) {
|
|
25488
|
+
return `${row.kind}\x00${row.path}`;
|
|
25489
|
+
}
|
|
25490
|
+
function rowEquals(a, b) {
|
|
25491
|
+
if (a.kind !== b.kind || a.path !== b.path)
|
|
25492
|
+
return false;
|
|
25493
|
+
switch (a.kind) {
|
|
25494
|
+
case "file": {
|
|
25495
|
+
const o = b;
|
|
25496
|
+
return a.name === o.name && a.depth === o.depth;
|
|
25497
|
+
}
|
|
25498
|
+
case "dir": {
|
|
25499
|
+
const o = b;
|
|
25500
|
+
return a.name === o.name && a.depth === o.depth && a.expanded === o.expanded && a.hasChildren === o.hasChildren;
|
|
25501
|
+
}
|
|
25502
|
+
case "status": {
|
|
25503
|
+
const o = b;
|
|
25504
|
+
return a.status === o.status && a.added === o.added && a.deleted === o.deleted;
|
|
25505
|
+
}
|
|
25506
|
+
}
|
|
25507
|
+
}
|
|
25508
|
+
function reconcileRows(prev, next) {
|
|
25509
|
+
if (prev.length === 0)
|
|
25510
|
+
return next;
|
|
25511
|
+
const prevByKey = new Map;
|
|
25512
|
+
for (const row of prev)
|
|
25513
|
+
prevByKey.set(rowKey(row), row);
|
|
25514
|
+
let allReused = prev.length === next.length;
|
|
25515
|
+
const out = new Array(next.length);
|
|
25516
|
+
for (let i = 0;i < next.length; i++) {
|
|
25517
|
+
const fresh = next[i];
|
|
25518
|
+
const old = prevByKey.get(rowKey(fresh));
|
|
25519
|
+
if (old && rowEquals(old, fresh)) {
|
|
25520
|
+
out[i] = old;
|
|
25521
|
+
if (allReused && prev[i] !== old)
|
|
25522
|
+
allReused = false;
|
|
25523
|
+
} else {
|
|
25524
|
+
out[i] = fresh;
|
|
25525
|
+
allReused = false;
|
|
25526
|
+
}
|
|
25527
|
+
}
|
|
25528
|
+
return allReused ? prev : out;
|
|
25529
|
+
}
|
|
25530
|
+
function sameFileList(a, b) {
|
|
25531
|
+
if (a === b)
|
|
25532
|
+
return true;
|
|
25533
|
+
if (a == null || b == null)
|
|
25534
|
+
return false;
|
|
25535
|
+
if (a.length !== b.length)
|
|
25536
|
+
return false;
|
|
25537
|
+
for (let i = 0;i < a.length; i++) {
|
|
25538
|
+
if (a[i] !== b[i])
|
|
25539
|
+
return false;
|
|
25540
|
+
}
|
|
25541
|
+
return true;
|
|
25542
|
+
}
|
|
25543
|
+
function sameStatusEntries(a, b) {
|
|
25544
|
+
if (a === b)
|
|
25545
|
+
return true;
|
|
25546
|
+
if (a == null || b == null)
|
|
25547
|
+
return false;
|
|
25548
|
+
if (a.length !== b.length)
|
|
25549
|
+
return false;
|
|
25550
|
+
for (let i = 0;i < a.length; i++) {
|
|
25551
|
+
const x = a[i];
|
|
25552
|
+
const y = b[i];
|
|
25553
|
+
if (x.path !== y.path || x.status !== y.status || x.added !== y.added || x.deleted !== y.deleted)
|
|
25554
|
+
return false;
|
|
25555
|
+
}
|
|
25556
|
+
return true;
|
|
25557
|
+
}
|
|
25558
|
+
|
|
24756
25559
|
// src/tui/panes/filetree/FileTree.tsx
|
|
24757
|
-
import { watch as
|
|
25560
|
+
import { watch as watch3 } from "fs";
|
|
24758
25561
|
import { TextAttributes as TextAttributes14 } from "@opentui/core";
|
|
24759
25562
|
function statusToken(s) {
|
|
24760
25563
|
switch (s) {
|
|
@@ -24801,8 +25604,12 @@ function FileTree(props) {
|
|
|
24801
25604
|
const [tab, setTab] = createSignal("all");
|
|
24802
25605
|
const [cursorIndex, setCursorIndex] = createSignal(0);
|
|
24803
25606
|
const [refreshTick, setRefreshTick] = createSignal(0);
|
|
24804
|
-
const [allFiles, setAllFiles] = createSignal(null
|
|
24805
|
-
|
|
25607
|
+
const [allFiles, setAllFiles] = createSignal(null, {
|
|
25608
|
+
equals: sameFileList
|
|
25609
|
+
});
|
|
25610
|
+
const [changes, setChanges] = createSignal(null, {
|
|
25611
|
+
equals: sameStatusEntries
|
|
25612
|
+
});
|
|
24806
25613
|
const [error, setError] = createSignal(null);
|
|
24807
25614
|
const [expandedDirs, setExpandedDirs] = createSignal(new Set);
|
|
24808
25615
|
let fetchSeq = 0;
|
|
@@ -24849,7 +25656,7 @@ function FileTree(props) {
|
|
|
24849
25656
|
let debounceTimer = null;
|
|
24850
25657
|
let watcher = null;
|
|
24851
25658
|
try {
|
|
24852
|
-
watcher =
|
|
25659
|
+
watcher = watch3(path12, {
|
|
24853
25660
|
recursive: true
|
|
24854
25661
|
}, (_event, filename) => {
|
|
24855
25662
|
if (filename == null)
|
|
@@ -24898,53 +25705,19 @@ function FileTree(props) {
|
|
|
24898
25705
|
return null;
|
|
24899
25706
|
return buildTree(files);
|
|
24900
25707
|
});
|
|
24901
|
-
|
|
24902
|
-
|
|
24903
|
-
if (child.isDir) {
|
|
24904
|
-
const isOpen = expanded.has(child.path);
|
|
24905
|
-
out.push({
|
|
24906
|
-
kind: "dir",
|
|
24907
|
-
path: child.path,
|
|
24908
|
-
name: child.name,
|
|
24909
|
-
depth,
|
|
24910
|
-
expanded: isOpen,
|
|
24911
|
-
hasChildren: child.children.length > 0
|
|
24912
|
-
});
|
|
24913
|
-
if (isOpen)
|
|
24914
|
-
flattenTree(child, expanded, depth + 1, out);
|
|
24915
|
-
} else {
|
|
24916
|
-
out.push({
|
|
24917
|
-
kind: "file",
|
|
24918
|
-
path: child.path,
|
|
24919
|
-
name: child.name,
|
|
24920
|
-
depth
|
|
24921
|
-
});
|
|
24922
|
-
}
|
|
24923
|
-
}
|
|
24924
|
-
}
|
|
24925
|
-
const rows = createMemo(() => {
|
|
25708
|
+
const rows = createMemo((prev) => {
|
|
25709
|
+
const next = [];
|
|
24926
25710
|
if (tab() === "all") {
|
|
24927
25711
|
const root = tree();
|
|
24928
|
-
if (root
|
|
24929
|
-
|
|
24930
|
-
|
|
24931
|
-
flattenTree(root, expandedDirs(), 0, out);
|
|
24932
|
-
return out;
|
|
24933
|
-
}
|
|
24934
|
-
if (tab() === "changes") {
|
|
25712
|
+
if (root != null)
|
|
25713
|
+
flattenTree(root, expandedDirs(), 0, next);
|
|
25714
|
+
} else if (tab() === "changes") {
|
|
24935
25715
|
const list2 = changes();
|
|
24936
|
-
if (list2
|
|
24937
|
-
|
|
24938
|
-
return list2.map((e) => ({
|
|
24939
|
-
kind: "status",
|
|
24940
|
-
path: e.path,
|
|
24941
|
-
status: e.status,
|
|
24942
|
-
added: e.added,
|
|
24943
|
-
deleted: e.deleted
|
|
24944
|
-
}));
|
|
25716
|
+
if (list2 != null)
|
|
25717
|
+
next.push(...statusRows(list2));
|
|
24945
25718
|
}
|
|
24946
|
-
return [];
|
|
24947
|
-
});
|
|
25719
|
+
return reconcileRows(prev ?? [], next);
|
|
25720
|
+
}, []);
|
|
24948
25721
|
const statWidths = createMemo(() => {
|
|
24949
25722
|
let added = 0;
|
|
24950
25723
|
let deleted = 0;
|
|
@@ -25631,8 +26404,8 @@ var init_pr_prompt = __esm(() => {
|
|
|
25631
26404
|
});
|
|
25632
26405
|
|
|
25633
26406
|
// src/tui/ops/host.tsx
|
|
25634
|
-
var
|
|
25635
|
-
__export(
|
|
26407
|
+
var exports_host7 = {};
|
|
26408
|
+
__export(exports_host7, {
|
|
25636
26409
|
startOpsPreview: () => startOpsPreview,
|
|
25637
26410
|
startOpsHost: () => startOpsHost
|
|
25638
26411
|
});
|
|
@@ -25738,7 +26511,7 @@ function OpsShell(props) {
|
|
|
25738
26511
|
relPath: rel,
|
|
25739
26512
|
cliInvocation: kobeCliInvocation()
|
|
25740
26513
|
}),
|
|
25741
|
-
name:
|
|
26514
|
+
name: basename8(rel)
|
|
25742
26515
|
});
|
|
25743
26516
|
}
|
|
25744
26517
|
function openFile(rel) {
|
|
@@ -25781,7 +26554,7 @@ function OpsShell(props) {
|
|
|
25781
26554
|
return _el$;
|
|
25782
26555
|
})();
|
|
25783
26556
|
}
|
|
25784
|
-
function
|
|
26557
|
+
function basename8(p) {
|
|
25785
26558
|
const i = p.lastIndexOf("/");
|
|
25786
26559
|
return i >= 0 ? p.slice(i + 1) : p;
|
|
25787
26560
|
}
|
|
@@ -26026,7 +26799,7 @@ async function startOpsPreview(args2) {
|
|
|
26026
26799
|
});
|
|
26027
26800
|
}
|
|
26028
26801
|
var ACTIVITY_POLL_MS = 2500, TURN_STATUS_POLL_MS = 1500, STABLE_POLLS_FOR_DONE = 2, CHAT_TAB_STATE_OPTION2 = "@kobe_tab_state";
|
|
26029
|
-
var
|
|
26802
|
+
var init_host7 = __esm(() => {
|
|
26030
26803
|
init_solid();
|
|
26031
26804
|
init_solid();
|
|
26032
26805
|
init_solid();
|
|
@@ -26124,6 +26897,7 @@ async function startDirectTmux() {
|
|
|
26124
26897
|
if (!task) {
|
|
26125
26898
|
const home = await ensureFallbackSession();
|
|
26126
26899
|
await applyTmuxPaneBorderTheme();
|
|
26900
|
+
await prepareWindowForAttach(home);
|
|
26127
26901
|
if (await attachTmux(attachArgv(home)) === null) {
|
|
26128
26902
|
console.error("kobe: failed to attach to the kobe-home session");
|
|
26129
26903
|
process.exitCode = 1;
|
|
@@ -26151,6 +26925,7 @@ async function startDirectTmux() {
|
|
|
26151
26925
|
return;
|
|
26152
26926
|
}
|
|
26153
26927
|
await applyTmuxPaneBorderTheme();
|
|
26928
|
+
await prepareWindowForAttach(name);
|
|
26154
26929
|
const exitCode = await attachTmux(attachArgv(name));
|
|
26155
26930
|
if (exitCode === null) {
|
|
26156
26931
|
console.error(`kobe: failed to attach to tmux session ${name}`);
|
|
@@ -26576,6 +27351,17 @@ async function main() {
|
|
|
26576
27351
|
await selectTasksPane2(session);
|
|
26577
27352
|
return;
|
|
26578
27353
|
}
|
|
27354
|
+
if (subcommand === "heal-layout") {
|
|
27355
|
+
const flags = parseOpsFlags(rest);
|
|
27356
|
+
const session = flags.session;
|
|
27357
|
+
if (!session) {
|
|
27358
|
+
console.error("kobe heal-layout: --session <name> is required");
|
|
27359
|
+
process.exit(2);
|
|
27360
|
+
}
|
|
27361
|
+
const { healSessionLayout: healSessionLayout2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
27362
|
+
await healSessionLayout2(session);
|
|
27363
|
+
return;
|
|
27364
|
+
}
|
|
26579
27365
|
if (subcommand === "quick-task") {
|
|
26580
27366
|
const flags = parseOpsFlags(rest);
|
|
26581
27367
|
const { startQuickTaskHost: startQuickTaskHost2 } = await Promise.resolve().then(() => (init_host2(), exports_host2));
|
|
@@ -26593,6 +27379,11 @@ async function main() {
|
|
|
26593
27379
|
await startSettingsHost2();
|
|
26594
27380
|
return;
|
|
26595
27381
|
}
|
|
27382
|
+
if (subcommand === "help-page") {
|
|
27383
|
+
const { startHelpHost: startHelpHost2 } = await Promise.resolve().then(() => (init_host5(), exports_host5));
|
|
27384
|
+
await startHelpHost2();
|
|
27385
|
+
return;
|
|
27386
|
+
}
|
|
26596
27387
|
if (subcommand === "new-task") {
|
|
26597
27388
|
const flags = parseOpsFlags(rest);
|
|
26598
27389
|
const { startNewTaskHost: startNewTaskHost2 } = await Promise.resolve().then(() => (init_host(), exports_host));
|
|
@@ -26600,7 +27391,7 @@ async function main() {
|
|
|
26600
27391
|
return;
|
|
26601
27392
|
}
|
|
26602
27393
|
if (subcommand === "update-page") {
|
|
26603
|
-
const { startUpdateHost: startUpdateHost2 } = await Promise.resolve().then(() => (
|
|
27394
|
+
const { startUpdateHost: startUpdateHost2 } = await Promise.resolve().then(() => (init_host6(), exports_host6));
|
|
26604
27395
|
await startUpdateHost2();
|
|
26605
27396
|
return;
|
|
26606
27397
|
}
|
|
@@ -26611,11 +27402,11 @@ async function main() {
|
|
|
26611
27402
|
process.exit(2);
|
|
26612
27403
|
}
|
|
26613
27404
|
if (flags.preview) {
|
|
26614
|
-
const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (
|
|
27405
|
+
const { startOpsPreview: startOpsPreview2 } = await Promise.resolve().then(() => (init_host7(), exports_host7));
|
|
26615
27406
|
await startOpsPreview2({ worktree: flags.worktree, relPath: flags.preview });
|
|
26616
27407
|
return;
|
|
26617
27408
|
}
|
|
26618
|
-
const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (
|
|
27409
|
+
const { startOpsHost: startOpsHost2 } = await Promise.resolve().then(() => (init_host7(), exports_host7));
|
|
26619
27410
|
await startOpsHost2({
|
|
26620
27411
|
taskId: flags.taskId ?? "",
|
|
26621
27412
|
worktree: flags.worktree,
|