@sma1lboy/kobe 0.7.28 → 0.7.30
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 +1587 -845
- package/dist/web-ui/assets/AppShell-DB2eNRP2.js +8 -0
- package/dist/web-ui/assets/{ChatTerminal-CcdiyDLi.js → ChatTerminal-dv25rCIz.js} +3 -3
- package/dist/web-ui/assets/IssuePeek-B4sEOPyN.css +1 -0
- package/dist/web-ui/assets/IssuePeek-Dh3qej2l.js +184 -0
- package/dist/web-ui/assets/ViewToggle-GsDQZY-j.js +1 -0
- package/dist/web-ui/assets/board-CbM3YF06.js +1 -0
- package/dist/web-ui/assets/index-LIEeaROP.css +2 -0
- package/dist/web-ui/assets/index-j-izWmuG.js +10 -0
- package/dist/web-ui/assets/issues-BXc_TiwK.js +1 -0
- package/dist/web-ui/assets/routes-CS4Lj7ol.js +1 -0
- package/dist/web-ui/assets/{tabs-7WK4MwGF.js → tabs-CmaDBSUQ.js} +1 -1
- package/dist/web-ui/assets/task._taskId-BQ5h5DZ2.js +1 -0
- package/dist/web-ui/assets/useNavigate-CXvSccc8.js +1 -0
- package/dist/web-ui/assets/vendor-X3nYbtqQ.js +1 -0
- package/dist/web-ui/index.html +4 -3
- package/dist/web-ui/pty-server.mjs +74 -18
- package/package.json +1 -1
- package/dist/web-ui/assets/AppShell-CseeU4ld.js +0 -7
- package/dist/web-ui/assets/ChatTranscript-DBk96m82.js +0 -11
- package/dist/web-ui/assets/arrow-left-DbEl9vGA.js +0 -1
- package/dist/web-ui/assets/board-4GZVbT7l.js +0 -6
- package/dist/web-ui/assets/chips-4L0VTRjY.js +0 -2
- package/dist/web-ui/assets/index-DwX3YPSK.css +0 -2
- package/dist/web-ui/assets/index-Dy89TZk1.js +0 -10
- package/dist/web-ui/assets/overview-DlOA2rw0.js +0 -1
- package/dist/web-ui/assets/routes-BU7iBSg2.js +0 -1
- package/dist/web-ui/assets/task._taskId-9jugSRjg.js +0 -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.30",
|
|
94
94
|
description: "TUI orchestrator for Claude Code (codename)",
|
|
95
95
|
type: "module",
|
|
96
96
|
packageManager: "bun@1.3.13",
|
|
@@ -175,6 +175,9 @@ function kobeSettingsDir() {
|
|
|
175
175
|
function keybindingsConfigPath() {
|
|
176
176
|
return join(kobeSettingsDir(), "keybindings.yaml");
|
|
177
177
|
}
|
|
178
|
+
function issueAssetsDir() {
|
|
179
|
+
return join(kobeStateDir(), "issue-assets");
|
|
180
|
+
}
|
|
178
181
|
function remoteControlSocketPath(host, user, port) {
|
|
179
182
|
const hash = createHash("sha1").update(`${user}@${host}:${port ?? 22}`).digest("hex").slice(0, 16);
|
|
180
183
|
return join(kobeStateDir(), "ssh", `${hash}.sock`);
|
|
@@ -193,7 +196,9 @@ __export(exports_version, {
|
|
|
193
196
|
recommendedGlobalInstallCommand: () => recommendedGlobalInstallCommand,
|
|
194
197
|
isNewerSemver: () => isNewerSemver,
|
|
195
198
|
fetchReleaseSummaries: () => fetchReleaseSummaries,
|
|
199
|
+
fetchReleaseNotesRange: () => fetchReleaseNotesRange,
|
|
196
200
|
fetchReleaseNotes: () => fetchReleaseNotes,
|
|
201
|
+
compareSemver: () => compareSemver,
|
|
197
202
|
checkLatestVersion: () => checkLatestVersion,
|
|
198
203
|
UPDATE_SCRIPT_URL: () => UPDATE_SCRIPT_URL,
|
|
199
204
|
UPDATE_COMMAND: () => UPDATE_COMMAND,
|
|
@@ -234,20 +239,23 @@ async function fetchLatestFromRegistry(packageName) {
|
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
241
|
function isNewerSemver(latest, current) {
|
|
242
|
+
return compareSemver(latest, current) > 0;
|
|
243
|
+
}
|
|
244
|
+
function compareSemver(aVersion, bVersion) {
|
|
237
245
|
const norm = (v) => v.split("-")[0] ?? v;
|
|
238
|
-
const a = norm(
|
|
239
|
-
const b = norm(
|
|
246
|
+
const a = norm(aVersion).split(".").map((s) => Number.parseInt(s, 10));
|
|
247
|
+
const b = norm(bVersion).split(".").map((s) => Number.parseInt(s, 10));
|
|
240
248
|
for (let i = 0;i < 3; i++) {
|
|
241
249
|
const av = a[i] ?? 0;
|
|
242
250
|
const bv = b[i] ?? 0;
|
|
243
251
|
if (Number.isNaN(av) || Number.isNaN(bv))
|
|
244
|
-
return
|
|
252
|
+
return 0;
|
|
245
253
|
if (av > bv)
|
|
246
|
-
return
|
|
254
|
+
return 1;
|
|
247
255
|
if (av < bv)
|
|
248
|
-
return
|
|
256
|
+
return -1;
|
|
249
257
|
}
|
|
250
|
-
return
|
|
258
|
+
return 0;
|
|
251
259
|
}
|
|
252
260
|
async function checkLatestVersion(opts = {}) {
|
|
253
261
|
const fake = process.env.KOBE_FAKE_UPDATE;
|
|
@@ -298,6 +306,42 @@ async function fetchReleaseNotes(version) {
|
|
|
298
306
|
clearTimeout(timer);
|
|
299
307
|
}
|
|
300
308
|
}
|
|
309
|
+
async function fetchReleaseNotesRange(args) {
|
|
310
|
+
const slug = repoSlug();
|
|
311
|
+
if (!slug)
|
|
312
|
+
return [];
|
|
313
|
+
const limit = args.limit ?? 100;
|
|
314
|
+
const ctrl = new AbortController;
|
|
315
|
+
const timer = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
|
|
316
|
+
try {
|
|
317
|
+
const res = await fetch(`https://api.github.com/repos/${slug}/releases?per_page=${limit}`, {
|
|
318
|
+
signal: ctrl.signal,
|
|
319
|
+
headers: {
|
|
320
|
+
accept: "application/vnd.github+json",
|
|
321
|
+
"x-github-api-version": "2022-11-28"
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
if (!res.ok)
|
|
325
|
+
return [];
|
|
326
|
+
const body = await res.json();
|
|
327
|
+
if (!Array.isArray(body))
|
|
328
|
+
return [];
|
|
329
|
+
return body.map((release) => {
|
|
330
|
+
const version = versionFromTagName(release.tag_name);
|
|
331
|
+
if (!version || typeof release.html_url !== "string" || typeof release.body !== "string")
|
|
332
|
+
return null;
|
|
333
|
+
if (compareSemver(version, args.current) <= 0)
|
|
334
|
+
return null;
|
|
335
|
+
if (compareSemver(version, args.latest) > 0)
|
|
336
|
+
return null;
|
|
337
|
+
return { version, url: release.html_url, body: release.body };
|
|
338
|
+
}).filter((release) => release !== null);
|
|
339
|
+
} catch {
|
|
340
|
+
return [];
|
|
341
|
+
} finally {
|
|
342
|
+
clearTimeout(timer);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
301
345
|
async function fetchReleaseSummaries(limit = 12) {
|
|
302
346
|
const slug = repoSlug();
|
|
303
347
|
if (!slug)
|
|
@@ -351,6 +395,9 @@ import { mkdir, readFile as readFileAsync, readdir as readdirAsync } from "fs/pr
|
|
|
351
395
|
function shQuote(s) {
|
|
352
396
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
353
397
|
}
|
|
398
|
+
function shToken(s) {
|
|
399
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(s) ? s : shQuote(s);
|
|
400
|
+
}
|
|
354
401
|
function shJoin(argv) {
|
|
355
402
|
return argv.map(shQuote).join(" ");
|
|
356
403
|
}
|
|
@@ -501,7 +548,8 @@ class RemoteExecHost {
|
|
|
501
548
|
}
|
|
502
549
|
wrapCommand(command, opts = {}) {
|
|
503
550
|
const remote = opts.cwd ? `cd ${shQuote(opts.cwd)} && ${command}` : command;
|
|
504
|
-
|
|
551
|
+
const connect = sshConnectArgs(this.spec, { tty: opts.tty }).map(shToken).join(" ");
|
|
552
|
+
return `${connect} ${shQuote(remote)}`;
|
|
505
553
|
}
|
|
506
554
|
}
|
|
507
555
|
var defaultSpawner = (argv, env) => {
|
|
@@ -622,6 +670,7 @@ __export(exports_repos, {
|
|
|
622
670
|
setRepoInitOverride: () => setRepoInitOverride,
|
|
623
671
|
setPersistedString: () => setPersistedString,
|
|
624
672
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
673
|
+
resolveMainRepoRoot: () => resolveMainRepoRoot,
|
|
625
674
|
removeSavedRepo: () => removeSavedRepo,
|
|
626
675
|
remoteRepoKey: () => remoteRepoKey,
|
|
627
676
|
normalizeSavedRepos: () => normalizeSavedRepos,
|
|
@@ -657,6 +706,19 @@ function resolveRepoRoot(absPath) {
|
|
|
657
706
|
} catch {}
|
|
658
707
|
return top;
|
|
659
708
|
}
|
|
709
|
+
function resolveMainRepoRoot(absPath) {
|
|
710
|
+
if (isRemoteRepoKey(absPath))
|
|
711
|
+
return absPath;
|
|
712
|
+
const r = spawnSync3("git", ["worktree", "list", "--porcelain"], {
|
|
713
|
+
cwd: absPath,
|
|
714
|
+
encoding: "utf8",
|
|
715
|
+
shell: false
|
|
716
|
+
});
|
|
717
|
+
if (r.status !== 0)
|
|
718
|
+
return resolveRepoRoot(absPath);
|
|
719
|
+
const first = (r.stdout ?? "").split(/\r?\n/).find((line) => line.startsWith("worktree "))?.slice("worktree ".length).trim();
|
|
720
|
+
return first || resolveRepoRoot(absPath);
|
|
721
|
+
}
|
|
660
722
|
function statePath() {
|
|
661
723
|
return kvStatePath();
|
|
662
724
|
}
|
|
@@ -967,7 +1029,20 @@ var init_add_remote = __esm(() => {
|
|
|
967
1029
|
});
|
|
968
1030
|
|
|
969
1031
|
// src/types/task.ts
|
|
970
|
-
|
|
1032
|
+
function isTaskStatus(value) {
|
|
1033
|
+
return typeof value === "string" && TASK_STATUSES.includes(value);
|
|
1034
|
+
}
|
|
1035
|
+
var toTaskId = (id) => id, DEFAULT_TASK_VENDOR = "claude", TASK_STATUSES;
|
|
1036
|
+
var init_task = __esm(() => {
|
|
1037
|
+
TASK_STATUSES = [
|
|
1038
|
+
"backlog",
|
|
1039
|
+
"in_progress",
|
|
1040
|
+
"in_review",
|
|
1041
|
+
"done",
|
|
1042
|
+
"canceled",
|
|
1043
|
+
"error"
|
|
1044
|
+
];
|
|
1045
|
+
});
|
|
971
1046
|
|
|
972
1047
|
// src/orchestrator/index/ulid.ts
|
|
973
1048
|
function encodeTime(now, len) {
|
|
@@ -1305,7 +1380,7 @@ function coerceTask(value) {
|
|
|
1305
1380
|
if (typeof v.id !== "string" || typeof v.title !== "string" || typeof v.repo !== "string" || typeof v.branch !== "string" || typeof v.worktreePath !== "string" || typeof v.status !== "string" || typeof v.createdAt !== "string" || typeof v.updatedAt !== "string") {
|
|
1306
1381
|
return null;
|
|
1307
1382
|
}
|
|
1308
|
-
if (!
|
|
1383
|
+
if (!isTaskStatus2(v.status))
|
|
1309
1384
|
return null;
|
|
1310
1385
|
const archived = typeof v.archived === "boolean" ? v.archived : false;
|
|
1311
1386
|
const kind = v.kind === "main" ? "main" : "task";
|
|
@@ -1323,6 +1398,7 @@ function coerceTask(value) {
|
|
|
1323
1398
|
vendor: isVendorId(v.vendor) ? v.vendor : DEFAULT_TASK_VENDOR,
|
|
1324
1399
|
prStatus: coercePRStatus(v.prStatus),
|
|
1325
1400
|
...typeof v.position === "number" && Number.isFinite(v.position) ? { position: v.position } : {},
|
|
1401
|
+
...typeof v.modelEffort === "string" && v.modelEffort.length > 0 ? { modelEffort: v.modelEffort } : {},
|
|
1326
1402
|
createdAt: v.createdAt,
|
|
1327
1403
|
updatedAt: v.updatedAt
|
|
1328
1404
|
};
|
|
@@ -1361,11 +1437,12 @@ function isPRCheckState(v) {
|
|
|
1361
1437
|
function isVendorId(v) {
|
|
1362
1438
|
return v === "claude" || v === "codex";
|
|
1363
1439
|
}
|
|
1364
|
-
function
|
|
1440
|
+
function isTaskStatus2(s) {
|
|
1365
1441
|
return s === "backlog" || s === "in_progress" || s === "in_review" || s === "done" || s === "canceled" || s === "error";
|
|
1366
1442
|
}
|
|
1367
1443
|
var CURRENT_VERSION2 = 3;
|
|
1368
1444
|
var init_store2 = __esm(() => {
|
|
1445
|
+
init_task();
|
|
1369
1446
|
init_ulid();
|
|
1370
1447
|
});
|
|
1371
1448
|
|
|
@@ -3062,9 +3139,10 @@ function deriveTitleFromPrompt(prompt) {
|
|
|
3062
3139
|
const collapsed = prompt.replace(/\s+/g, " ").trim();
|
|
3063
3140
|
if (collapsed.length === 0)
|
|
3064
3141
|
return "";
|
|
3065
|
-
|
|
3142
|
+
const points = [...collapsed];
|
|
3143
|
+
if (points.length <= TITLE_CHAR_CAP)
|
|
3066
3144
|
return collapsed;
|
|
3067
|
-
return `${
|
|
3145
|
+
return `${points.slice(0, TITLE_CHAR_CAP).join("")}\u2026`;
|
|
3068
3146
|
}
|
|
3069
3147
|
function autoBranch(title, taskId) {
|
|
3070
3148
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
|
|
@@ -3630,11 +3708,6 @@ class Orchestrator {
|
|
|
3630
3708
|
return this.tasksAcc;
|
|
3631
3709
|
}
|
|
3632
3710
|
subscribeTasks(listener) {
|
|
3633
|
-
try {
|
|
3634
|
-
listener(this.store.list());
|
|
3635
|
-
} catch (err) {
|
|
3636
|
-
console.error("[kobe Orchestrator] task listener threw on subscribe:", err);
|
|
3637
|
-
}
|
|
3638
3711
|
return this.store.subscribe(listener);
|
|
3639
3712
|
}
|
|
3640
3713
|
dispose() {
|
|
@@ -3657,7 +3730,8 @@ class Orchestrator {
|
|
|
3657
3730
|
worktreePath: "",
|
|
3658
3731
|
status: "backlog",
|
|
3659
3732
|
kind: "task",
|
|
3660
|
-
vendor: input.vendor ?? DEFAULT_TASK_VENDOR
|
|
3733
|
+
vendor: input.vendor ?? DEFAULT_TASK_VENDOR,
|
|
3734
|
+
...input.modelEffort ? { modelEffort: input.modelEffort } : {}
|
|
3661
3735
|
});
|
|
3662
3736
|
if (input.baseRef)
|
|
3663
3737
|
this.pendingBaseRefs.set(task.id, input.baseRef);
|
|
@@ -3922,6 +3996,7 @@ var PLACEHOLDER_TASK_TITLE = "(new task)";
|
|
|
3922
3996
|
var init_core = __esm(() => {
|
|
3923
3997
|
init_dev();
|
|
3924
3998
|
init_repos();
|
|
3999
|
+
init_task();
|
|
3925
4000
|
init_errors();
|
|
3926
4001
|
init_slug_allocator();
|
|
3927
4002
|
});
|
|
@@ -3961,6 +4036,7 @@ function serializeTask(task) {
|
|
|
3961
4036
|
vendor: task.vendor,
|
|
3962
4037
|
prStatus: task.prStatus,
|
|
3963
4038
|
position: task.position,
|
|
4039
|
+
modelEffort: task.modelEffort,
|
|
3964
4040
|
createdAt: task.createdAt,
|
|
3965
4041
|
updatedAt: task.updatedAt
|
|
3966
4042
|
};
|
|
@@ -3973,6 +4049,7 @@ var DAEMON_PROTOCOL_VERSION = 3, MIN_COMPATIBLE_PROTOCOL_VERSION = 2, CHANNEL_NA
|
|
|
3973
4049
|
var init_protocol = __esm(() => {
|
|
3974
4050
|
CHANNEL_NAMES = [
|
|
3975
4051
|
"task.snapshot",
|
|
4052
|
+
"issue.snapshot",
|
|
3976
4053
|
"active-task",
|
|
3977
4054
|
"update",
|
|
3978
4055
|
"engine-state",
|
|
@@ -3980,7 +4057,6 @@ var init_protocol = __esm(() => {
|
|
|
3980
4057
|
"keybindings",
|
|
3981
4058
|
"task.jobs",
|
|
3982
4059
|
"worktree.changes",
|
|
3983
|
-
"task.conflicts",
|
|
3984
4060
|
"session.deliver"
|
|
3985
4061
|
];
|
|
3986
4062
|
CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
|
|
@@ -4093,6 +4169,7 @@ var init_client_log = __esm(() => {
|
|
|
4093
4169
|
|
|
4094
4170
|
// ../kobe-daemon/src/client/index.ts
|
|
4095
4171
|
import { connect } from "net";
|
|
4172
|
+
import { StringDecoder } from "string_decoder";
|
|
4096
4173
|
|
|
4097
4174
|
class KobeDaemonClient {
|
|
4098
4175
|
socketPath;
|
|
@@ -4213,7 +4290,9 @@ class KobeDaemonClient {
|
|
|
4213
4290
|
};
|
|
4214
4291
|
socket.once("connect", onConnect);
|
|
4215
4292
|
socket.once("error", onError);
|
|
4216
|
-
|
|
4293
|
+
const decoder = new StringDecoder("utf8");
|
|
4294
|
+
this.buffer = "";
|
|
4295
|
+
socket.on("data", (chunk) => this.onData(decoder.write(chunk)));
|
|
4217
4296
|
socket.on("close", () => this.onSocketClose(socket));
|
|
4218
4297
|
});
|
|
4219
4298
|
}
|
|
@@ -5006,6 +5085,24 @@ function normalizeClaudeContent(content) {
|
|
|
5006
5085
|
return out;
|
|
5007
5086
|
}
|
|
5008
5087
|
|
|
5088
|
+
// src/engine/claude-code-local/synthetic.ts
|
|
5089
|
+
function isSyntheticClaudeRecord(record) {
|
|
5090
|
+
return record.isMeta === true || record.isCompactSummary === true;
|
|
5091
|
+
}
|
|
5092
|
+
function isClaudeCommandBreadcrumb(blocks) {
|
|
5093
|
+
if (blocks.length === 0)
|
|
5094
|
+
return false;
|
|
5095
|
+
for (const b of blocks) {
|
|
5096
|
+
if (b.type !== "text")
|
|
5097
|
+
return false;
|
|
5098
|
+
const t = b.text.trim();
|
|
5099
|
+
if (!t.startsWith("<command-name>") && !t.startsWith("<command-message>") && !t.startsWith("<local-command")) {
|
|
5100
|
+
return false;
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
return true;
|
|
5104
|
+
}
|
|
5105
|
+
|
|
5009
5106
|
// src/engine/claude-code-local/history.ts
|
|
5010
5107
|
import { appendFile as appendFile2, mkdir as mkdir4, readFile as readFile2, readdir, stat, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
5011
5108
|
import { homedir as homedir8 } from "os";
|
|
@@ -5087,6 +5184,8 @@ function parseJsonl(raw, sessionId) {
|
|
|
5087
5184
|
return out;
|
|
5088
5185
|
}
|
|
5089
5186
|
function extractMessage(record, fallbackSessionId) {
|
|
5187
|
+
if (isSyntheticClaudeRecord(record))
|
|
5188
|
+
return null;
|
|
5090
5189
|
const inner = isObject(record.message) ? record.message : record;
|
|
5091
5190
|
const role = inner.role;
|
|
5092
5191
|
if (role !== "user" && role !== "assistant" && role !== "system")
|
|
@@ -5094,6 +5193,8 @@ function extractMessage(record, fallbackSessionId) {
|
|
|
5094
5193
|
if (!("content" in inner))
|
|
5095
5194
|
return null;
|
|
5096
5195
|
const blocks = normalizeClaudeContent(inner.content);
|
|
5196
|
+
if (role === "user" && isClaudeCommandBreadcrumb(blocks))
|
|
5197
|
+
return null;
|
|
5097
5198
|
const ts = typeof record.timestamp === "string" ? record.timestamp : new Date().toISOString();
|
|
5098
5199
|
const sid = typeof record.sessionId === "string" ? record.sessionId : fallbackSessionId;
|
|
5099
5200
|
const usage = extractUsage(inner.usage);
|
|
@@ -5496,9 +5597,12 @@ async function listRolloutFiles(deps = defaultDeps7) {
|
|
|
5496
5597
|
return out;
|
|
5497
5598
|
}
|
|
5498
5599
|
async function findRolloutFile(sessionId, deps = defaultDeps7) {
|
|
5600
|
+
if (!sessionId)
|
|
5601
|
+
return;
|
|
5602
|
+
const want = sessionId.toLowerCase();
|
|
5499
5603
|
const all = await listRolloutFiles(deps);
|
|
5500
5604
|
for (const p of all) {
|
|
5501
|
-
if (path8.basename(p).
|
|
5605
|
+
if (path8.basename(p).match(UUID_AT_END)?.[1]?.toLowerCase() === want)
|
|
5502
5606
|
return p;
|
|
5503
5607
|
}
|
|
5504
5608
|
return;
|
|
@@ -5794,7 +5898,7 @@ function deriveCodexUsageMetrics(raw) {
|
|
|
5794
5898
|
if (timestampMs !== null && (latestUsageTimestampMs === null || timestampMs > latestUsageTimestampMs)) {
|
|
5795
5899
|
latestUsageTimestampMs = timestampMs;
|
|
5796
5900
|
latestUsage = snapshot;
|
|
5797
|
-
} else if (
|
|
5901
|
+
} else if (latestUsageTimestampMs === null) {
|
|
5798
5902
|
latestUsage = snapshot;
|
|
5799
5903
|
}
|
|
5800
5904
|
}
|
|
@@ -6313,6 +6417,7 @@ var init_registry = __esm(() => {
|
|
|
6313
6417
|
builtin: true,
|
|
6314
6418
|
displayName: "Codex",
|
|
6315
6419
|
defaultCommand: ["codex"],
|
|
6420
|
+
effortLevels: ["none", "low", "medium", "high", "xhigh"],
|
|
6316
6421
|
history: codexHistoryReader,
|
|
6317
6422
|
detectAccount: (deps) => detectCodexAccount(deps),
|
|
6318
6423
|
createHookAdapter: () => new NoopHookAdapter("codex"),
|
|
@@ -6364,6 +6469,7 @@ async function deriveTitleFromSessionId(vendor, sessionId) {
|
|
|
6364
6469
|
var MAX_SESSIONS_SCANNED = 8;
|
|
6365
6470
|
var init_auto_title = __esm(() => {
|
|
6366
6471
|
init_registry();
|
|
6472
|
+
init_task();
|
|
6367
6473
|
});
|
|
6368
6474
|
|
|
6369
6475
|
// src/tui/panes/terminal/launch.ts
|
|
@@ -6797,6 +6903,7 @@ var realRunner, realDeps;
|
|
|
6797
6903
|
var init_chat_tab_naming = __esm(() => {
|
|
6798
6904
|
init_auto_title();
|
|
6799
6905
|
init_client2();
|
|
6906
|
+
init_task();
|
|
6800
6907
|
realRunner = { capture: runTmuxCapturing, run: runTmux };
|
|
6801
6908
|
realDeps = {
|
|
6802
6909
|
runner: realRunner,
|
|
@@ -6891,347 +6998,7 @@ var init_auto_title_poller = __esm(() => {
|
|
|
6891
6998
|
init_auto_title();
|
|
6892
6999
|
init_core();
|
|
6893
7000
|
init_chat_tab_naming();
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
// src/lib/poll-scheduling.ts
|
|
6897
|
-
import { spawn as spawn2 } from "child_process";
|
|
6898
|
-
function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
|
|
6899
|
-
if (timedOut)
|
|
6900
|
-
return startedAt + cfg.slowRetryMs;
|
|
6901
|
-
return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
|
|
6902
|
-
}
|
|
6903
|
-
function shouldPoll(state, now) {
|
|
6904
|
-
return !state.inFlight && now >= state.nextAllowedAt;
|
|
6905
|
-
}
|
|
6906
|
-
function maybeStartScheduledRun(state, cfg, run, onValue) {
|
|
6907
|
-
const startedAt = Date.now();
|
|
6908
|
-
if (!shouldPoll(state, startedAt))
|
|
6909
|
-
return false;
|
|
6910
|
-
state.inFlight = true;
|
|
6911
|
-
const controller = new AbortController;
|
|
6912
|
-
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
6913
|
-
(async () => {
|
|
6914
|
-
let value;
|
|
6915
|
-
let ok = false;
|
|
6916
|
-
try {
|
|
6917
|
-
value = await run(controller.signal);
|
|
6918
|
-
ok = true;
|
|
6919
|
-
} catch {}
|
|
6920
|
-
clearTimeout(timer);
|
|
6921
|
-
const timedOut = controller.signal.aborted;
|
|
6922
|
-
state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
|
|
6923
|
-
state.inFlight = false;
|
|
6924
|
-
if (ok && !timedOut)
|
|
6925
|
-
onValue(value);
|
|
6926
|
-
})();
|
|
6927
|
-
return true;
|
|
6928
|
-
}
|
|
6929
|
-
function spawnCapture(cmd, args, opts) {
|
|
6930
|
-
return new Promise((resolve2) => {
|
|
6931
|
-
let out = "";
|
|
6932
|
-
let settled = false;
|
|
6933
|
-
const finish = (status) => {
|
|
6934
|
-
if (settled)
|
|
6935
|
-
return;
|
|
6936
|
-
settled = true;
|
|
6937
|
-
resolve2({ status, stdout: out });
|
|
6938
|
-
};
|
|
6939
|
-
const child = spawn2(cmd, args.slice(), {
|
|
6940
|
-
cwd: opts.cwd,
|
|
6941
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
6942
|
-
env: opts.env,
|
|
6943
|
-
signal: opts.signal,
|
|
6944
|
-
killSignal: "SIGKILL"
|
|
6945
|
-
});
|
|
6946
|
-
child.stdout?.on("data", (chunk) => {
|
|
6947
|
-
out += String(chunk);
|
|
6948
|
-
});
|
|
6949
|
-
child.on("error", () => finish(null));
|
|
6950
|
-
child.on("close", (code) => finish(code));
|
|
6951
|
-
});
|
|
6952
|
-
}
|
|
6953
|
-
var init_poll_scheduling = () => {};
|
|
6954
|
-
|
|
6955
|
-
// ../kobe-daemon/src/daemon/conflict-collector.ts
|
|
6956
|
-
class GitGate {
|
|
6957
|
-
limit;
|
|
6958
|
-
active = 0;
|
|
6959
|
-
waiters = [];
|
|
6960
|
-
constructor(limit = MAX_CONCURRENT_GIT) {
|
|
6961
|
-
this.limit = limit;
|
|
6962
|
-
}
|
|
6963
|
-
async run(fn) {
|
|
6964
|
-
if (this.active >= this.limit) {
|
|
6965
|
-
await new Promise((resolve2) => this.waiters.push(resolve2));
|
|
6966
|
-
}
|
|
6967
|
-
this.active += 1;
|
|
6968
|
-
try {
|
|
6969
|
-
return await fn();
|
|
6970
|
-
} finally {
|
|
6971
|
-
this.active -= 1;
|
|
6972
|
-
this.waiters.shift()?.();
|
|
6973
|
-
}
|
|
6974
|
-
}
|
|
6975
|
-
}
|
|
6976
|
-
async function git(cwd, args, signal, gate) {
|
|
6977
|
-
return gate.run(() => spawnCapture("git", args, { cwd, env: { ...process.env, ...LOCK_FREE_ENV }, signal }));
|
|
6978
|
-
}
|
|
6979
|
-
function parsePorcelainPaths(stdout) {
|
|
6980
|
-
const paths = [];
|
|
6981
|
-
for (const line of stdout.split(`
|
|
6982
|
-
`)) {
|
|
6983
|
-
if (line.length < 4)
|
|
6984
|
-
continue;
|
|
6985
|
-
const rest = line.slice(3);
|
|
6986
|
-
const arrow = rest.indexOf(" -> ");
|
|
6987
|
-
if (arrow >= 0) {
|
|
6988
|
-
paths.push(rest.slice(0, arrow), rest.slice(arrow + 4));
|
|
6989
|
-
} else {
|
|
6990
|
-
paths.push(rest);
|
|
6991
|
-
}
|
|
6992
|
-
}
|
|
6993
|
-
return paths;
|
|
6994
|
-
}
|
|
6995
|
-
async function resolveBaseRef(worktree, signal, gate) {
|
|
6996
|
-
for (const ref of BASE_REF_CANDIDATES) {
|
|
6997
|
-
const res = await git(worktree, ["rev-parse", "--verify", "--quiet", ref], signal, gate);
|
|
6998
|
-
if (res.status === 0)
|
|
6999
|
-
return ref;
|
|
7000
|
-
}
|
|
7001
|
-
return null;
|
|
7002
|
-
}
|
|
7003
|
-
async function collectFootprint(worktree, repo, baseRef, signal, gate) {
|
|
7004
|
-
const head = await git(worktree, ["rev-parse", "HEAD"], signal, gate);
|
|
7005
|
-
if (head.status !== 0)
|
|
7006
|
-
throw new Error("rev-parse HEAD failed");
|
|
7007
|
-
const files = new Set;
|
|
7008
|
-
const status = await git(worktree, ["status", "--porcelain=v1"], signal, gate);
|
|
7009
|
-
if (status.status !== 0)
|
|
7010
|
-
throw new Error("git status failed");
|
|
7011
|
-
for (const p of parsePorcelainPaths(status.stdout))
|
|
7012
|
-
files.add(p);
|
|
7013
|
-
if (baseRef) {
|
|
7014
|
-
const diff = await git(worktree, ["diff", "--name-only", `${baseRef}...HEAD`], signal, gate);
|
|
7015
|
-
if (diff.status === 0) {
|
|
7016
|
-
for (const p of diff.stdout.split(`
|
|
7017
|
-
`))
|
|
7018
|
-
if (p)
|
|
7019
|
-
files.add(p);
|
|
7020
|
-
}
|
|
7021
|
-
}
|
|
7022
|
-
return { repo, head: head.stdout.trim(), files };
|
|
7023
|
-
}
|
|
7024
|
-
function sameFootprint(a, b) {
|
|
7025
|
-
if (a.head !== b.head || a.files.size !== b.files.size)
|
|
7026
|
-
return false;
|
|
7027
|
-
for (const f of a.files)
|
|
7028
|
-
if (!b.files.has(f))
|
|
7029
|
-
return false;
|
|
7030
|
-
return true;
|
|
7031
|
-
}
|
|
7032
|
-
function trackedConflictTasks(tasks) {
|
|
7033
|
-
return tasks.filter((t) => !t.archived && (t.kind ?? "task") !== "main" && !!t.worktreePath && !isRemoteRepoKey(t.repo) && !isRemoteRepoKey(t.worktreePath));
|
|
7034
|
-
}
|
|
7035
|
-
function overlapPairs(cards) {
|
|
7036
|
-
const ids = [...cards.keys()].sort();
|
|
7037
|
-
const pairs = [];
|
|
7038
|
-
for (let i = 0;i < ids.length; i++) {
|
|
7039
|
-
for (let j = i + 1;j < ids.length; j++) {
|
|
7040
|
-
const a = cards.get(ids[i]);
|
|
7041
|
-
const b = cards.get(ids[j]);
|
|
7042
|
-
if (a.repo !== b.repo)
|
|
7043
|
-
continue;
|
|
7044
|
-
const files = [...a.files].filter((f) => b.files.has(f)).sort();
|
|
7045
|
-
if (files.length === 0)
|
|
7046
|
-
continue;
|
|
7047
|
-
pairs.push({ a: ids[i], b: ids[j], files, level: "overlap" });
|
|
7048
|
-
}
|
|
7049
|
-
}
|
|
7050
|
-
return pairs;
|
|
7051
|
-
}
|
|
7052
|
-
function parseMergeTreeNames(stdout) {
|
|
7053
|
-
const lines = stdout.split(`
|
|
7054
|
-
`);
|
|
7055
|
-
const names = [];
|
|
7056
|
-
for (const line of lines.slice(1)) {
|
|
7057
|
-
if (!line)
|
|
7058
|
-
break;
|
|
7059
|
-
names.push(line);
|
|
7060
|
-
}
|
|
7061
|
-
return names;
|
|
7062
|
-
}
|
|
7063
|
-
function samePairs(a, b) {
|
|
7064
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
7065
|
-
}
|
|
7066
|
-
|
|
7067
|
-
class ConflictCollector {
|
|
7068
|
-
orch;
|
|
7069
|
-
bus;
|
|
7070
|
-
options;
|
|
7071
|
-
entries = new Map;
|
|
7072
|
-
baseRefs = new Map;
|
|
7073
|
-
mergeProbes = new Map;
|
|
7074
|
-
gate = new GitGate;
|
|
7075
|
-
mergeTreeUnsupported = false;
|
|
7076
|
-
lastPublished = [];
|
|
7077
|
-
stopped = false;
|
|
7078
|
-
constructor(orch, bus, options = {}) {
|
|
7079
|
-
this.orch = orch;
|
|
7080
|
-
this.bus = bus;
|
|
7081
|
-
this.options = options;
|
|
7082
|
-
}
|
|
7083
|
-
tick() {
|
|
7084
|
-
if (this.stopped)
|
|
7085
|
-
return;
|
|
7086
|
-
if (this.options.hasSubscribers && !this.options.hasSubscribers())
|
|
7087
|
-
return;
|
|
7088
|
-
try {
|
|
7089
|
-
const tracked = trackedConflictTasks(this.orch.listTasks());
|
|
7090
|
-
const trackedIds = new Set(tracked.map((t) => t.id));
|
|
7091
|
-
let pruned = false;
|
|
7092
|
-
for (const id of this.entries.keys()) {
|
|
7093
|
-
if (trackedIds.has(id))
|
|
7094
|
-
continue;
|
|
7095
|
-
if (this.entries.get(id)?.value)
|
|
7096
|
-
pruned = true;
|
|
7097
|
-
this.entries.delete(id);
|
|
7098
|
-
}
|
|
7099
|
-
if (pruned)
|
|
7100
|
-
this.recompute();
|
|
7101
|
-
for (const task of tracked)
|
|
7102
|
-
this.maybeCollect(task);
|
|
7103
|
-
} catch (err) {
|
|
7104
|
-
logDaemonError("conflict-radar", err);
|
|
7105
|
-
}
|
|
7106
|
-
}
|
|
7107
|
-
stop() {
|
|
7108
|
-
this.stopped = true;
|
|
7109
|
-
}
|
|
7110
|
-
maybeCollect(task) {
|
|
7111
|
-
const id = task.id;
|
|
7112
|
-
let entry = this.entries.get(id);
|
|
7113
|
-
if (!entry) {
|
|
7114
|
-
entry = { inFlight: false, nextAllowedAt: 0 };
|
|
7115
|
-
this.entries.set(id, entry);
|
|
7116
|
-
}
|
|
7117
|
-
const cadence = this.options.cadence ?? {
|
|
7118
|
-
timeoutMs: CONFLICTS_TIMEOUT_MS,
|
|
7119
|
-
slowRetryMs: CONFLICTS_SLOW_RETRY_MS,
|
|
7120
|
-
minIntervalMs: CONFLICTS_MIN_INTERVAL_MS
|
|
7121
|
-
};
|
|
7122
|
-
const run = this.options.footprint ?? (async (t, signal) => {
|
|
7123
|
-
const baseRef = await this.baseRefFor(t.worktreePath, signal);
|
|
7124
|
-
return collectFootprint(t.worktreePath, t.repo, baseRef, signal, this.gate);
|
|
7125
|
-
});
|
|
7126
|
-
maybeStartScheduledRun(entry, cadence, (signal) => run(task, signal), (value) => {
|
|
7127
|
-
if (this.stopped)
|
|
7128
|
-
return;
|
|
7129
|
-
if (this.entries.get(id) !== entry)
|
|
7130
|
-
return;
|
|
7131
|
-
if (entry.value && sameFootprint(entry.value, value))
|
|
7132
|
-
return;
|
|
7133
|
-
entry.value = value;
|
|
7134
|
-
this.recompute();
|
|
7135
|
-
});
|
|
7136
|
-
}
|
|
7137
|
-
baseRefFor(worktree, signal) {
|
|
7138
|
-
const cached = this.baseRefs.get(worktree);
|
|
7139
|
-
if (cached)
|
|
7140
|
-
return cached;
|
|
7141
|
-
const promise = resolveBaseRef(worktree, signal, this.gate).catch(() => null);
|
|
7142
|
-
this.baseRefs.set(worktree, promise);
|
|
7143
|
-
return promise;
|
|
7144
|
-
}
|
|
7145
|
-
recompute() {
|
|
7146
|
-
const cards = new Map;
|
|
7147
|
-
for (const [id, entry] of this.entries) {
|
|
7148
|
-
if (entry.value)
|
|
7149
|
-
cards.set(id, entry.value);
|
|
7150
|
-
}
|
|
7151
|
-
const pairs = overlapPairs(cards);
|
|
7152
|
-
const resolved = [];
|
|
7153
|
-
for (const pair of pairs) {
|
|
7154
|
-
const a = cards.get(pair.a);
|
|
7155
|
-
const b = cards.get(pair.b);
|
|
7156
|
-
const key = [a.repo, ...[a.head, b.head].sort()].join("\x00");
|
|
7157
|
-
const probe2 = this.mergeProbes.get(key);
|
|
7158
|
-
if (probe2?.state === "conflict") {
|
|
7159
|
-
resolved.push({
|
|
7160
|
-
...pair,
|
|
7161
|
-
level: "conflict",
|
|
7162
|
-
files: probe2.files.length > 0 ? probe2.files : pair.files
|
|
7163
|
-
});
|
|
7164
|
-
continue;
|
|
7165
|
-
}
|
|
7166
|
-
resolved.push(pair);
|
|
7167
|
-
if (!probe2 && !this.mergeTreeUnsupported && a.head !== b.head) {
|
|
7168
|
-
this.scheduleMergeProbe(key, cards, pair);
|
|
7169
|
-
}
|
|
7170
|
-
}
|
|
7171
|
-
if (samePairs(this.lastPublished, resolved))
|
|
7172
|
-
return;
|
|
7173
|
-
this.lastPublished = resolved;
|
|
7174
|
-
const payload = { pairs: resolved };
|
|
7175
|
-
this.bus.publish("task.conflicts", payload);
|
|
7176
|
-
}
|
|
7177
|
-
scheduleMergeProbe(key, cards, pair) {
|
|
7178
|
-
const a = cards.get(pair.a);
|
|
7179
|
-
const b = cards.get(pair.b);
|
|
7180
|
-
this.mergeProbes.set(key, { state: "pending" });
|
|
7181
|
-
const worktree = this.worktreeOf(pair.a);
|
|
7182
|
-
const probe2 = this.options.probeMerge ?? (async (wt, headA, headB) => {
|
|
7183
|
-
const res = await git(wt, ["merge-tree", "--write-tree", "--name-only", headA, headB], AbortSignal.timeout(MERGE_TREE_TIMEOUT_MS), this.gate);
|
|
7184
|
-
if (res.status === 0)
|
|
7185
|
-
return { conflict: false, files: [] };
|
|
7186
|
-
if (res.status === 1)
|
|
7187
|
-
return { conflict: true, files: parseMergeTreeNames(res.stdout) };
|
|
7188
|
-
return null;
|
|
7189
|
-
});
|
|
7190
|
-
if (!worktree) {
|
|
7191
|
-
this.mergeProbes.delete(key);
|
|
7192
|
-
return;
|
|
7193
|
-
}
|
|
7194
|
-
probe2(worktree, a.head, b.head).then((result) => {
|
|
7195
|
-
if (this.stopped)
|
|
7196
|
-
return;
|
|
7197
|
-
if (result === null) {
|
|
7198
|
-
if (!this.mergeTreeUnsupported) {
|
|
7199
|
-
this.mergeTreeUnsupported = true;
|
|
7200
|
-
console.log("[conflict-radar] merge-tree dry-run unavailable (git < 2.38 or no merge base) \u2014 radar degrades to file-overlap only");
|
|
7201
|
-
}
|
|
7202
|
-
this.mergeProbes.delete(key);
|
|
7203
|
-
return;
|
|
7204
|
-
}
|
|
7205
|
-
this.mergeProbes.set(key, result.conflict ? { state: "conflict", files: result.files } : { state: "clean" });
|
|
7206
|
-
this.recompute();
|
|
7207
|
-
}).catch((err) => {
|
|
7208
|
-
this.mergeProbes.delete(key);
|
|
7209
|
-
logDaemonError("conflict-radar", err);
|
|
7210
|
-
});
|
|
7211
|
-
}
|
|
7212
|
-
worktreeOf(taskId) {
|
|
7213
|
-
const task = this.orch.listTasks().find((t) => t.id === taskId);
|
|
7214
|
-
return task?.worktreePath || undefined;
|
|
7215
|
-
}
|
|
7216
|
-
}
|
|
7217
|
-
function startConflictCollector(orch, bus, tickMs = DEFAULT_CONFLICTS_TICK_MS, hasSubscribers) {
|
|
7218
|
-
if (tickMs <= 0)
|
|
7219
|
-
return () => {};
|
|
7220
|
-
const collector = new ConflictCollector(orch, bus, { hasSubscribers });
|
|
7221
|
-
collector.tick();
|
|
7222
|
-
const timer = setInterval(() => collector.tick(), tickMs);
|
|
7223
|
-
timer.unref?.();
|
|
7224
|
-
return () => {
|
|
7225
|
-
clearInterval(timer);
|
|
7226
|
-
collector.stop();
|
|
7227
|
-
};
|
|
7228
|
-
}
|
|
7229
|
-
var DEFAULT_CONFLICTS_TICK_MS = 5000, CONFLICTS_TIMEOUT_MS = 5000, CONFLICTS_SLOW_RETRY_MS = 60000, CONFLICTS_MIN_INTERVAL_MS = 1e4, MAX_CONCURRENT_GIT = 3, MERGE_TREE_TIMEOUT_MS = 8000, LOCK_FREE_ENV, BASE_REF_CANDIDATES;
|
|
7230
|
-
var init_conflict_collector = __esm(() => {
|
|
7231
|
-
init_poll_scheduling();
|
|
7232
|
-
init_repos();
|
|
7233
|
-
LOCK_FREE_ENV = { GIT_OPTIONAL_LOCKS: "0" };
|
|
7234
|
-
BASE_REF_CANDIDATES = ["origin/HEAD", "origin/main", "origin/master", "main", "master"];
|
|
7001
|
+
init_task();
|
|
7235
7002
|
});
|
|
7236
7003
|
|
|
7237
7004
|
// ../kobe-daemon/src/daemon/event-bus.ts
|
|
@@ -7466,7 +7233,8 @@ function createDaemonHandlerRegistry() {
|
|
|
7466
7233
|
title: optionalString(payload, "title"),
|
|
7467
7234
|
branch: optionalString(payload, "branch"),
|
|
7468
7235
|
baseRef: optionalString(payload, "baseRef"),
|
|
7469
|
-
vendor: optionalVendor(payload, "vendor")
|
|
7236
|
+
vendor: optionalVendor(payload, "vendor"),
|
|
7237
|
+
modelEffort: optionalString(payload, "effort")
|
|
7470
7238
|
});
|
|
7471
7239
|
return { taskId: task.id, task: serializeTask(task) };
|
|
7472
7240
|
}
|
|
@@ -7539,10 +7307,20 @@ function createDaemonHandlerRegistry() {
|
|
|
7539
7307
|
async handle(payload, ctx) {
|
|
7540
7308
|
const taskId = requireString(payload, "taskId");
|
|
7541
7309
|
const status = requireString(payload, "status");
|
|
7542
|
-
if (status
|
|
7310
|
+
if (!isTaskStatus(status))
|
|
7543
7311
|
throw new Error("status must be a TaskStatus");
|
|
7544
|
-
|
|
7312
|
+
const linked = status === "done" ? ctx.orch.getTask(taskId) : undefined;
|
|
7313
|
+
const prevStatus = linked?.status;
|
|
7545
7314
|
await ctx.orch.setStatus(taskId, status);
|
|
7315
|
+
if (status === "done" && prevStatus !== "done" && linked) {
|
|
7316
|
+
try {
|
|
7317
|
+
const next = await ctx.issues.mirrorTaskDone(linked.repo, taskId);
|
|
7318
|
+
if (next)
|
|
7319
|
+
ctx.bus.publish("issue.snapshot", next);
|
|
7320
|
+
} catch (err) {
|
|
7321
|
+
logDaemonError("issue-done-mirror", err);
|
|
7322
|
+
}
|
|
7323
|
+
}
|
|
7546
7324
|
return {};
|
|
7547
7325
|
}
|
|
7548
7326
|
},
|
|
@@ -7645,6 +7423,20 @@ function createDaemonHandlerRegistry() {
|
|
|
7645
7423
|
return {};
|
|
7646
7424
|
}
|
|
7647
7425
|
},
|
|
7426
|
+
{
|
|
7427
|
+
name: "issue.list",
|
|
7428
|
+
async handle(payload, ctx) {
|
|
7429
|
+
return ctx.issues.list(requireString(payload, "repoRoot"));
|
|
7430
|
+
}
|
|
7431
|
+
},
|
|
7432
|
+
{
|
|
7433
|
+
name: "issue.mutate",
|
|
7434
|
+
async handle(payload, ctx) {
|
|
7435
|
+
const state = await ctx.issues.mutate(requireString(payload, "repoRoot"), payload.op);
|
|
7436
|
+
ctx.bus.publish("issue.snapshot", state);
|
|
7437
|
+
return state;
|
|
7438
|
+
}
|
|
7439
|
+
},
|
|
7648
7440
|
{
|
|
7649
7441
|
name: "session.deliver",
|
|
7650
7442
|
async handle(payload, ctx) {
|
|
@@ -7770,24 +7562,348 @@ function optionalActivityDetail(payload) {
|
|
|
7770
7562
|
var init_handlers = __esm(() => {
|
|
7771
7563
|
init_hook_events();
|
|
7772
7564
|
init_status_rules();
|
|
7565
|
+
init_task();
|
|
7773
7566
|
init_version();
|
|
7774
7567
|
init_cwd_task();
|
|
7775
7568
|
init_protocol();
|
|
7776
7569
|
});
|
|
7777
7570
|
|
|
7571
|
+
// ../kobe-daemon/src/daemon/issues-store.ts
|
|
7572
|
+
import { execFile } from "child_process";
|
|
7573
|
+
import { mkdir as mkdir6, readFile as readFile7, realpath, rename as rename2, stat as stat4, writeFile as writeFile4 } from "fs/promises";
|
|
7574
|
+
import { homedir as homedir13 } from "os";
|
|
7575
|
+
import { dirname as dirname5, isAbsolute, join as join4, resolve as resolve2 } from "path";
|
|
7576
|
+
import { promisify } from "util";
|
|
7577
|
+
function isGitNotRepositoryError(err) {
|
|
7578
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7579
|
+
return message.includes("not a git repository");
|
|
7580
|
+
}
|
|
7581
|
+
function defaultIssuesStorePath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir13()) {
|
|
7582
|
+
return join4(homeDir2, ".kobe", "issues.json");
|
|
7583
|
+
}
|
|
7584
|
+
function isValidStatus(value) {
|
|
7585
|
+
return typeof value === "string" && ISSUE_STATUSES.includes(value);
|
|
7586
|
+
}
|
|
7587
|
+
function normalizeIssue(entry) {
|
|
7588
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry))
|
|
7589
|
+
return null;
|
|
7590
|
+
const raw = entry;
|
|
7591
|
+
if (typeof raw.id !== "number")
|
|
7592
|
+
return null;
|
|
7593
|
+
return {
|
|
7594
|
+
id: raw.id,
|
|
7595
|
+
title: typeof raw.title === "string" ? raw.title : "(untitled)",
|
|
7596
|
+
status: isValidStatus(raw.status) ? raw.status : "open",
|
|
7597
|
+
created: typeof raw.created === "string" ? raw.created : "",
|
|
7598
|
+
body: typeof raw.body === "string" ? raw.body : "",
|
|
7599
|
+
taskId: typeof raw.taskId === "string" ? raw.taskId : undefined
|
|
7600
|
+
};
|
|
7601
|
+
}
|
|
7602
|
+
function emptyStore() {
|
|
7603
|
+
return { version: 1, repos: {} };
|
|
7604
|
+
}
|
|
7605
|
+
function todayStamp() {
|
|
7606
|
+
const d = new Date;
|
|
7607
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
7608
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
7609
|
+
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
7610
|
+
}
|
|
7611
|
+
async function gitCommonDir(path11) {
|
|
7612
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "rev-parse", "--git-common-dir"]);
|
|
7613
|
+
const dir = stdout.trim();
|
|
7614
|
+
return realpath(isAbsolute(dir) ? dir : resolve2(path11, dir));
|
|
7615
|
+
}
|
|
7616
|
+
async function gitTopLevel(path11) {
|
|
7617
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "rev-parse", "--show-toplevel"]);
|
|
7618
|
+
return stdout.trim();
|
|
7619
|
+
}
|
|
7620
|
+
async function gitMainWorktree(path11) {
|
|
7621
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "worktree", "list", "--porcelain"]);
|
|
7622
|
+
const first = stdout.split(/\r?\n/).find((line) => line.startsWith("worktree "))?.slice("worktree ".length).trim();
|
|
7623
|
+
return first ? realpath(first) : gitTopLevel(path11);
|
|
7624
|
+
}
|
|
7625
|
+
async function resolveRepo(raw) {
|
|
7626
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
7627
|
+
throw new Error("repoRoot is required");
|
|
7628
|
+
const absolute = resolve2(raw);
|
|
7629
|
+
const s = await stat4(absolute).catch(() => null);
|
|
7630
|
+
if (!s?.isDirectory())
|
|
7631
|
+
throw new Error("repoRoot does not exist");
|
|
7632
|
+
try {
|
|
7633
|
+
const [repoRoot, repoKey] = await Promise.all([gitMainWorktree(absolute), gitCommonDir(absolute)]);
|
|
7634
|
+
return { repoRoot, repoKey };
|
|
7635
|
+
} catch (err) {
|
|
7636
|
+
if (isGitNotRepositoryError(err))
|
|
7637
|
+
throw new Error("repoRoot is not a git repository");
|
|
7638
|
+
throw err;
|
|
7639
|
+
}
|
|
7640
|
+
}
|
|
7641
|
+
async function readStore(path11) {
|
|
7642
|
+
try {
|
|
7643
|
+
const raw = JSON.parse(await readFile7(path11, "utf8"));
|
|
7644
|
+
const repos = {};
|
|
7645
|
+
if (raw.repos && typeof raw.repos === "object") {
|
|
7646
|
+
for (const [key, value] of Object.entries(raw.repos)) {
|
|
7647
|
+
if (!value || typeof value !== "object")
|
|
7648
|
+
continue;
|
|
7649
|
+
const record = value;
|
|
7650
|
+
repos[key] = {
|
|
7651
|
+
repoRoot: typeof record.repoRoot === "string" ? record.repoRoot : "",
|
|
7652
|
+
nextId: typeof record.nextId === "number" ? record.nextId : 1,
|
|
7653
|
+
issues: Array.isArray(record.issues) ? record.issues.map(normalizeIssue).filter((issue) => issue !== null) : []
|
|
7654
|
+
};
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
return { version: 1, repos };
|
|
7658
|
+
} catch (err) {
|
|
7659
|
+
if (err.code === "ENOENT")
|
|
7660
|
+
return emptyStore();
|
|
7661
|
+
throw err;
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
async function writeStore(path11, store) {
|
|
7665
|
+
await mkdir6(dirname5(path11), { recursive: true });
|
|
7666
|
+
const tmp = `${path11}.tmp`;
|
|
7667
|
+
await writeFile4(tmp, `${JSON.stringify(store, null, 2)}
|
|
7668
|
+
`, "utf8");
|
|
7669
|
+
await rename2(tmp, path11);
|
|
7670
|
+
}
|
|
7671
|
+
function response(repoRoot, record) {
|
|
7672
|
+
return {
|
|
7673
|
+
repoRoot,
|
|
7674
|
+
exists: record !== null,
|
|
7675
|
+
nextId: record?.nextId ?? 1,
|
|
7676
|
+
issues: record?.issues ?? []
|
|
7677
|
+
};
|
|
7678
|
+
}
|
|
7679
|
+
async function withLock(key, fn) {
|
|
7680
|
+
const tail = locks.get(key) ?? Promise.resolve();
|
|
7681
|
+
const run = tail.then(fn);
|
|
7682
|
+
const settled = run.then(() => {
|
|
7683
|
+
return;
|
|
7684
|
+
}, () => {
|
|
7685
|
+
return;
|
|
7686
|
+
});
|
|
7687
|
+
locks.set(key, settled);
|
|
7688
|
+
settled.then(() => {
|
|
7689
|
+
if (locks.get(key) === settled)
|
|
7690
|
+
locks.delete(key);
|
|
7691
|
+
});
|
|
7692
|
+
return run;
|
|
7693
|
+
}
|
|
7694
|
+
|
|
7695
|
+
class IssuesStore {
|
|
7696
|
+
path;
|
|
7697
|
+
constructor(path11 = defaultIssuesStorePath()) {
|
|
7698
|
+
this.path = path11;
|
|
7699
|
+
}
|
|
7700
|
+
async list(repo) {
|
|
7701
|
+
const { repoRoot, repoKey } = await resolveRepo(repo);
|
|
7702
|
+
return withLock(this.path, async () => {
|
|
7703
|
+
const store = await readStore(this.path);
|
|
7704
|
+
const record = store.repos[repoKey] ?? null;
|
|
7705
|
+
if (record && record.repoRoot !== repoRoot) {
|
|
7706
|
+
record.repoRoot = repoRoot;
|
|
7707
|
+
await writeStore(this.path, store);
|
|
7708
|
+
}
|
|
7709
|
+
return response(repoRoot, record);
|
|
7710
|
+
});
|
|
7711
|
+
}
|
|
7712
|
+
async mirrorTaskDone(repo, taskId) {
|
|
7713
|
+
const { repoRoot, repoKey } = await resolveRepo(repo);
|
|
7714
|
+
if (!taskId)
|
|
7715
|
+
return null;
|
|
7716
|
+
return withLock(this.path, async () => {
|
|
7717
|
+
const store = await readStore(this.path);
|
|
7718
|
+
const record = store.repos[repoKey];
|
|
7719
|
+
if (!record)
|
|
7720
|
+
return null;
|
|
7721
|
+
const issue = record.issues.find((i) => i.taskId === taskId);
|
|
7722
|
+
if (!issue || issue.status === "done")
|
|
7723
|
+
return null;
|
|
7724
|
+
issue.status = "done";
|
|
7725
|
+
record.repoRoot = repoRoot;
|
|
7726
|
+
await writeStore(this.path, store);
|
|
7727
|
+
return response(repoRoot, record);
|
|
7728
|
+
});
|
|
7729
|
+
}
|
|
7730
|
+
async mutate(repo, op) {
|
|
7731
|
+
const { repoRoot, repoKey } = await resolveRepo(repo);
|
|
7732
|
+
if (!op || typeof op !== "object" || Array.isArray(op) || typeof op.type !== "string") {
|
|
7733
|
+
throw new Error("missing op");
|
|
7734
|
+
}
|
|
7735
|
+
return withLock(this.path, async () => {
|
|
7736
|
+
const store = await readStore(this.path);
|
|
7737
|
+
let record = store.repos[repoKey];
|
|
7738
|
+
if (!record) {
|
|
7739
|
+
record = { repoRoot, nextId: 1, issues: [] };
|
|
7740
|
+
store.repos[repoKey] = record;
|
|
7741
|
+
}
|
|
7742
|
+
record.repoRoot = repoRoot;
|
|
7743
|
+
const typed = op;
|
|
7744
|
+
if (typed.type === "create") {
|
|
7745
|
+
if (typeof typed.title !== "string" || typed.title.trim().length === 0) {
|
|
7746
|
+
throw new Error("create requires a non-empty title");
|
|
7747
|
+
}
|
|
7748
|
+
if (typed.body !== undefined && typeof typed.body !== "string")
|
|
7749
|
+
throw new Error("body must be a string");
|
|
7750
|
+
record.issues = [
|
|
7751
|
+
{
|
|
7752
|
+
id: record.nextId,
|
|
7753
|
+
title: typed.title,
|
|
7754
|
+
status: "open",
|
|
7755
|
+
created: todayStamp(),
|
|
7756
|
+
body: typeof typed.body === "string" ? typed.body : ""
|
|
7757
|
+
},
|
|
7758
|
+
...record.issues
|
|
7759
|
+
];
|
|
7760
|
+
record.nextId += 1;
|
|
7761
|
+
} else if (typed.type === "setStatus") {
|
|
7762
|
+
if (typeof typed.id !== "number")
|
|
7763
|
+
throw new Error("setStatus requires a numeric id");
|
|
7764
|
+
if (!isValidStatus(typed.status))
|
|
7765
|
+
throw new Error(`invalid status: must be one of ${ISSUE_STATUSES.join(", ")}`);
|
|
7766
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7767
|
+
if (!issue)
|
|
7768
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7769
|
+
issue.status = typed.status;
|
|
7770
|
+
} else if (typed.type === "update") {
|
|
7771
|
+
if (typeof typed.id !== "number")
|
|
7772
|
+
throw new Error("update requires a numeric id");
|
|
7773
|
+
if (typed.title !== undefined && (typeof typed.title !== "string" || typed.title.trim().length === 0)) {
|
|
7774
|
+
throw new Error("title must be a non-empty string");
|
|
7775
|
+
}
|
|
7776
|
+
if (typed.body !== undefined && typeof typed.body !== "string")
|
|
7777
|
+
throw new Error("body must be a string");
|
|
7778
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7779
|
+
if (!issue)
|
|
7780
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7781
|
+
if (typeof typed.title === "string")
|
|
7782
|
+
issue.title = typed.title;
|
|
7783
|
+
if (typeof typed.body === "string")
|
|
7784
|
+
issue.body = typed.body;
|
|
7785
|
+
} else if (typed.type === "link") {
|
|
7786
|
+
if (typeof typed.id !== "number")
|
|
7787
|
+
throw new Error("link requires a numeric id");
|
|
7788
|
+
if (typeof typed.taskId !== "string" || typed.taskId.length === 0) {
|
|
7789
|
+
throw new Error("link requires a non-empty taskId");
|
|
7790
|
+
}
|
|
7791
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7792
|
+
if (!issue)
|
|
7793
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7794
|
+
issue.taskId = typed.taskId;
|
|
7795
|
+
} else if (typed.type === "unlink") {
|
|
7796
|
+
if (typeof typed.id !== "number")
|
|
7797
|
+
throw new Error("unlink requires a numeric id");
|
|
7798
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7799
|
+
if (!issue)
|
|
7800
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7801
|
+
issue.taskId = undefined;
|
|
7802
|
+
} else if (typed.type === "delete") {
|
|
7803
|
+
if (typeof typed.id !== "number")
|
|
7804
|
+
throw new Error("delete requires a numeric id");
|
|
7805
|
+
const nextIssues = record.issues.filter((i) => i.id !== typed.id);
|
|
7806
|
+
if (nextIssues.length === record.issues.length)
|
|
7807
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7808
|
+
record.issues = nextIssues;
|
|
7809
|
+
} else {
|
|
7810
|
+
throw new Error(`unknown op type: ${typed.type}`);
|
|
7811
|
+
}
|
|
7812
|
+
await writeStore(this.path, store);
|
|
7813
|
+
return response(repoRoot, record);
|
|
7814
|
+
});
|
|
7815
|
+
}
|
|
7816
|
+
}
|
|
7817
|
+
var execFileAsync, ISSUE_STATUSES, locks;
|
|
7818
|
+
var init_issues_store = __esm(() => {
|
|
7819
|
+
execFileAsync = promisify(execFile);
|
|
7820
|
+
ISSUE_STATUSES = ["open", "doing", "hold", "done"];
|
|
7821
|
+
locks = new Map;
|
|
7822
|
+
});
|
|
7823
|
+
|
|
7824
|
+
// ../kobe-daemon/src/daemon/lifetime.ts
|
|
7825
|
+
class DaemonLifetime {
|
|
7826
|
+
clients;
|
|
7827
|
+
idleGraceMs;
|
|
7828
|
+
onIdleStop;
|
|
7829
|
+
schedule;
|
|
7830
|
+
log;
|
|
7831
|
+
cancelIdle = null;
|
|
7832
|
+
stopping = false;
|
|
7833
|
+
constructor(options) {
|
|
7834
|
+
this.clients = options.clients;
|
|
7835
|
+
this.idleGraceMs = options.idleGraceMs;
|
|
7836
|
+
this.onIdleStop = options.onIdleStop;
|
|
7837
|
+
this.schedule = options.schedule ?? defaultSchedule;
|
|
7838
|
+
this.log = options.log ?? logDaemonInfo;
|
|
7839
|
+
}
|
|
7840
|
+
guiCount() {
|
|
7841
|
+
let n = 0;
|
|
7842
|
+
for (const c of this.clients())
|
|
7843
|
+
if (c.holdsLifetime)
|
|
7844
|
+
n++;
|
|
7845
|
+
return n;
|
|
7846
|
+
}
|
|
7847
|
+
hasSubscribers() {
|
|
7848
|
+
for (const c of this.clients())
|
|
7849
|
+
if (c.subscribed)
|
|
7850
|
+
return true;
|
|
7851
|
+
return false;
|
|
7852
|
+
}
|
|
7853
|
+
isStopping() {
|
|
7854
|
+
return this.stopping;
|
|
7855
|
+
}
|
|
7856
|
+
markStopping() {
|
|
7857
|
+
this.stopping = true;
|
|
7858
|
+
this.clearIdle();
|
|
7859
|
+
}
|
|
7860
|
+
guiAttached() {
|
|
7861
|
+
this.clearIdle();
|
|
7862
|
+
}
|
|
7863
|
+
clientDisconnected(wasGui) {
|
|
7864
|
+
if (wasGui)
|
|
7865
|
+
this.maybeArm();
|
|
7866
|
+
}
|
|
7867
|
+
clearIdle() {
|
|
7868
|
+
if (this.cancelIdle) {
|
|
7869
|
+
this.cancelIdle();
|
|
7870
|
+
this.cancelIdle = null;
|
|
7871
|
+
}
|
|
7872
|
+
}
|
|
7873
|
+
maybeArm() {
|
|
7874
|
+
if (this.stopping || this.guiCount() > 0)
|
|
7875
|
+
return;
|
|
7876
|
+
this.clearIdle();
|
|
7877
|
+
this.log("idle", `last gui gone \u2014 arming ${this.idleGraceMs}ms idle-stop grace`);
|
|
7878
|
+
this.cancelIdle = this.schedule(() => {
|
|
7879
|
+
this.cancelIdle = null;
|
|
7880
|
+
if (this.stopping || this.guiCount() > 0)
|
|
7881
|
+
return;
|
|
7882
|
+
this.log("idle", "grace elapsed with no gui \u2014 self-stopping");
|
|
7883
|
+
this.onIdleStop();
|
|
7884
|
+
}, this.idleGraceMs);
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
var defaultSchedule = (fn, ms) => {
|
|
7888
|
+
const t = setTimeout(fn, ms);
|
|
7889
|
+
t.unref?.();
|
|
7890
|
+
return () => clearTimeout(t);
|
|
7891
|
+
};
|
|
7892
|
+
var init_lifetime = () => {};
|
|
7893
|
+
|
|
7778
7894
|
// ../kobe-daemon/src/daemon/keybindings-watcher.ts
|
|
7779
7895
|
import { mkdirSync as mkdirSync2, watch } from "fs";
|
|
7780
|
-
import { homedir as
|
|
7781
|
-
import { basename as basename3, dirname as
|
|
7782
|
-
function defaultKeybindingsPath(homeDir2 = process.env.KOBE_HOME_DIR ??
|
|
7783
|
-
return
|
|
7896
|
+
import { homedir as homedir14 } from "os";
|
|
7897
|
+
import { basename as basename3, dirname as dirname6, join as join5 } from "path";
|
|
7898
|
+
function defaultKeybindingsPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir14()) {
|
|
7899
|
+
return join5(homeDir2, ".kobe", "settings", "keybindings.yaml");
|
|
7784
7900
|
}
|
|
7785
7901
|
function startKeybindingsWatcher(bus, options = {}) {
|
|
7786
7902
|
const debounceMs = options.debounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS;
|
|
7787
7903
|
if (debounceMs <= 0)
|
|
7788
7904
|
return () => {};
|
|
7789
7905
|
const filePath = options.path ?? defaultKeybindingsPath();
|
|
7790
|
-
const dir =
|
|
7906
|
+
const dir = dirname6(filePath);
|
|
7791
7907
|
const baseYaml = basename3(filePath);
|
|
7792
7908
|
const baseYml = baseYaml.replace(/\.yaml$/, ".yml");
|
|
7793
7909
|
let rev = 0;
|
|
@@ -7831,10 +7947,10 @@ var init_keybindings_watcher = () => {};
|
|
|
7831
7947
|
|
|
7832
7948
|
// ../kobe-daemon/src/daemon/ui-prefs-watcher.ts
|
|
7833
7949
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, watch as watch2 } from "fs";
|
|
7834
|
-
import { homedir as
|
|
7835
|
-
import { basename as basename4, dirname as
|
|
7836
|
-
function defaultUiPrefsStatePath(homeDir2 = process.env.KOBE_HOME_DIR ??
|
|
7837
|
-
return
|
|
7950
|
+
import { homedir as homedir15 } from "os";
|
|
7951
|
+
import { basename as basename4, dirname as dirname7, join as join6 } from "path";
|
|
7952
|
+
function defaultUiPrefsStatePath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir15()) {
|
|
7953
|
+
return join6(homeDir2, ".config", "kobe", "state.json");
|
|
7838
7954
|
}
|
|
7839
7955
|
function readUiPrefsFromStateFile(statePath2) {
|
|
7840
7956
|
let parsed = {};
|
|
@@ -7858,7 +7974,7 @@ function startUiPrefsWatcher(bus, options = {}) {
|
|
|
7858
7974
|
if (debounceMs <= 0)
|
|
7859
7975
|
return () => {};
|
|
7860
7976
|
const statePath2 = options.statePath ?? defaultUiPrefsStatePath();
|
|
7861
|
-
const stateDir =
|
|
7977
|
+
const stateDir = dirname7(statePath2);
|
|
7862
7978
|
const stateFile = basename4(statePath2);
|
|
7863
7979
|
let last = readUiPrefsFromStateFile(statePath2);
|
|
7864
7980
|
bus.publish("ui-prefs", last);
|
|
@@ -7904,6 +8020,65 @@ var init_ui_prefs_watcher = __esm(() => {
|
|
|
7904
8020
|
FOCUS_ACCENT_SLOT_NAMES = ["primary", "success", "info"];
|
|
7905
8021
|
});
|
|
7906
8022
|
|
|
8023
|
+
// src/lib/poll-scheduling.ts
|
|
8024
|
+
import { spawn as spawn2 } from "child_process";
|
|
8025
|
+
function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
|
|
8026
|
+
if (timedOut)
|
|
8027
|
+
return startedAt + cfg.slowRetryMs;
|
|
8028
|
+
return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
|
|
8029
|
+
}
|
|
8030
|
+
function shouldPoll(state, now) {
|
|
8031
|
+
return !state.inFlight && now >= state.nextAllowedAt;
|
|
8032
|
+
}
|
|
8033
|
+
function maybeStartScheduledRun(state, cfg, run, onValue) {
|
|
8034
|
+
const startedAt = Date.now();
|
|
8035
|
+
if (!shouldPoll(state, startedAt))
|
|
8036
|
+
return false;
|
|
8037
|
+
state.inFlight = true;
|
|
8038
|
+
const controller = new AbortController;
|
|
8039
|
+
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
8040
|
+
(async () => {
|
|
8041
|
+
let value;
|
|
8042
|
+
let ok = false;
|
|
8043
|
+
try {
|
|
8044
|
+
value = await run(controller.signal);
|
|
8045
|
+
ok = true;
|
|
8046
|
+
} catch {}
|
|
8047
|
+
clearTimeout(timer);
|
|
8048
|
+
const timedOut = controller.signal.aborted;
|
|
8049
|
+
state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
|
|
8050
|
+
state.inFlight = false;
|
|
8051
|
+
if (ok && !timedOut)
|
|
8052
|
+
onValue(value);
|
|
8053
|
+
})();
|
|
8054
|
+
return true;
|
|
8055
|
+
}
|
|
8056
|
+
function spawnCapture(cmd, args, opts) {
|
|
8057
|
+
return new Promise((resolve3) => {
|
|
8058
|
+
let out = "";
|
|
8059
|
+
let settled = false;
|
|
8060
|
+
const finish = (status) => {
|
|
8061
|
+
if (settled)
|
|
8062
|
+
return;
|
|
8063
|
+
settled = true;
|
|
8064
|
+
resolve3({ status, stdout: out });
|
|
8065
|
+
};
|
|
8066
|
+
const child = spawn2(cmd, args.slice(), {
|
|
8067
|
+
cwd: opts.cwd,
|
|
8068
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
8069
|
+
env: opts.env,
|
|
8070
|
+
signal: opts.signal,
|
|
8071
|
+
killSignal: "SIGKILL"
|
|
8072
|
+
});
|
|
8073
|
+
child.stdout?.on("data", (chunk) => {
|
|
8074
|
+
out += String(chunk);
|
|
8075
|
+
});
|
|
8076
|
+
child.on("error", () => finish(null));
|
|
8077
|
+
child.on("close", (code) => finish(code));
|
|
8078
|
+
});
|
|
8079
|
+
}
|
|
8080
|
+
var init_poll_scheduling = () => {};
|
|
8081
|
+
|
|
7907
8082
|
// src/tui/panes/sidebar/worktree-changes.ts
|
|
7908
8083
|
var exports_worktree_changes = {};
|
|
7909
8084
|
__export(exports_worktree_changes, {
|
|
@@ -8074,9 +8249,10 @@ var init_worktree_changes_collector = __esm(() => {
|
|
|
8074
8249
|
});
|
|
8075
8250
|
|
|
8076
8251
|
// ../kobe-daemon/src/daemon/server.ts
|
|
8077
|
-
import { mkdir as
|
|
8252
|
+
import { mkdir as mkdir7, readFile as readFile8, unlink as unlink4, writeFile as writeFile5 } from "fs/promises";
|
|
8078
8253
|
import { createServer } from "net";
|
|
8079
|
-
import { dirname as
|
|
8254
|
+
import { dirname as dirname8 } from "path";
|
|
8255
|
+
import { StringDecoder as StringDecoder2 } from "string_decoder";
|
|
8080
8256
|
function resolveIdleGraceMs() {
|
|
8081
8257
|
const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
|
|
8082
8258
|
if (raw === undefined)
|
|
@@ -8090,49 +8266,19 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8090
8266
|
const startedAt = options.startedAt ?? new Date;
|
|
8091
8267
|
const clients = new Set;
|
|
8092
8268
|
let nextClientId = 1;
|
|
8093
|
-
const
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
for (const c of clients)
|
|
8099
|
-
if (c.holdsLifetime)
|
|
8100
|
-
n++;
|
|
8101
|
-
return n;
|
|
8102
|
-
}
|
|
8103
|
-
function hasSubscribers() {
|
|
8104
|
-
for (const c of clients)
|
|
8105
|
-
if (c.subscribed)
|
|
8106
|
-
return true;
|
|
8107
|
-
return false;
|
|
8108
|
-
}
|
|
8109
|
-
function cancelIdleTimer() {
|
|
8110
|
-
if (idleTimer) {
|
|
8111
|
-
clearTimeout(idleTimer);
|
|
8112
|
-
idleTimer = null;
|
|
8113
|
-
}
|
|
8114
|
-
}
|
|
8115
|
-
function maybeArmIdleShutdown() {
|
|
8116
|
-
if (stopping || guiCount() > 0)
|
|
8117
|
-
return;
|
|
8118
|
-
cancelIdleTimer();
|
|
8119
|
-
logDaemonInfo("idle", `last gui gone \u2014 arming ${idleGraceMs}ms idle-stop grace`);
|
|
8120
|
-
idleTimer = setTimeout(() => {
|
|
8121
|
-
idleTimer = null;
|
|
8122
|
-
if (stopping || guiCount() > 0)
|
|
8123
|
-
return;
|
|
8124
|
-
logDaemonInfo("idle", "grace elapsed with no gui \u2014 self-stopping");
|
|
8125
|
-
stopSoon().catch((err) => logDaemonError("daemon-idle-shutdown", err));
|
|
8126
|
-
}, idleGraceMs);
|
|
8127
|
-
idleTimer.unref?.();
|
|
8128
|
-
}
|
|
8269
|
+
const lifetime = new DaemonLifetime({
|
|
8270
|
+
clients: () => clients,
|
|
8271
|
+
idleGraceMs: resolveIdleGraceMs(),
|
|
8272
|
+
onIdleStop: () => void stopSoon().catch((err) => logDaemonError("daemon-idle-shutdown", err))
|
|
8273
|
+
});
|
|
8129
8274
|
const bus = new DaemonEventBus;
|
|
8130
8275
|
bus.onPublish((event) => {
|
|
8131
8276
|
broadcast(clients, { type: "event", name: event.channel, payload: event.payload });
|
|
8132
8277
|
});
|
|
8133
8278
|
const activity = new DaemonActivityRegistry(bus);
|
|
8134
|
-
|
|
8135
|
-
await
|
|
8279
|
+
const issues = new IssuesStore(defaultIssuesStorePath(options.homeDir));
|
|
8280
|
+
await mkdir7(dirname8(socketPath), { recursive: true });
|
|
8281
|
+
await mkdir7(dirname8(pidPath), { recursive: true });
|
|
8136
8282
|
await unlink4(socketPath).catch(() => {});
|
|
8137
8283
|
const server = createServer((socket) => {
|
|
8138
8284
|
const client = {
|
|
@@ -8145,18 +8291,18 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8145
8291
|
channels: null
|
|
8146
8292
|
};
|
|
8147
8293
|
clients.add(client);
|
|
8294
|
+
const decoder = new StringDecoder2("utf8");
|
|
8148
8295
|
socket.on("data", (chunk) => {
|
|
8149
|
-
client.buffer +=
|
|
8296
|
+
client.buffer += decoder.write(chunk);
|
|
8150
8297
|
drainClientBuffer(client);
|
|
8151
8298
|
});
|
|
8152
8299
|
socket.on("error", () => {});
|
|
8153
8300
|
socket.on("close", () => {
|
|
8154
8301
|
clients.delete(client);
|
|
8155
8302
|
if (client.subscribed) {
|
|
8156
|
-
logDaemonInfo("conn", `client #${client.id} (${client.holdsLifetime ? "gui" : "pane"}) disconnected \u2014 ${clients.size} client(s), ${guiCount()} gui left`);
|
|
8303
|
+
logDaemonInfo("conn", `client #${client.id} (${client.holdsLifetime ? "gui" : "pane"}) disconnected \u2014 ${clients.size} client(s), ${lifetime.guiCount()} gui left`);
|
|
8157
8304
|
}
|
|
8158
|
-
|
|
8159
|
-
maybeArmIdleShutdown();
|
|
8305
|
+
lifetime.clientDisconnected(client.holdsLifetime);
|
|
8160
8306
|
});
|
|
8161
8307
|
});
|
|
8162
8308
|
const unsubscribeStore = orch.subscribeTasks((snapshot) => {
|
|
@@ -8174,7 +8320,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8174
8320
|
updateTimer.unref?.();
|
|
8175
8321
|
}
|
|
8176
8322
|
const autoTitlePollMs = options.autoTitlePollMs ?? DEFAULT_AUTO_TITLE_POLL_MS;
|
|
8177
|
-
const stopAutoTitlePoller = startAutoTitlePoller(orch, autoTitlePollMs, hasSubscribers);
|
|
8323
|
+
const stopAutoTitlePoller = startAutoTitlePoller(orch, autoTitlePollMs, () => lifetime.hasSubscribers());
|
|
8178
8324
|
const stopUiPrefsWatcher = startUiPrefsWatcher(bus, {
|
|
8179
8325
|
statePath: defaultUiPrefsStatePath(options.homeDir),
|
|
8180
8326
|
debounceMs: options.uiPrefsDebounceMs ?? DEFAULT_UI_PREFS_DEBOUNCE_MS
|
|
@@ -8183,16 +8329,14 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8183
8329
|
path: defaultKeybindingsPath(options.homeDir),
|
|
8184
8330
|
debounceMs: options.keybindingsDebounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS
|
|
8185
8331
|
});
|
|
8186
|
-
const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS, hasSubscribers);
|
|
8187
|
-
const stopConflictCollector = startConflictCollector(orch, bus, options.conflictsTickMs ?? DEFAULT_CONFLICTS_TICK_MS, hasSubscribers);
|
|
8332
|
+
const stopWorktreeChangesCollector = startWorktreeChangesCollector(orch, bus, options.worktreeChangesTickMs ?? DEFAULT_WORKTREE_CHANGES_TICK_MS, () => lifetime.hasSubscribers());
|
|
8188
8333
|
const serverApi = {
|
|
8189
8334
|
socketPath,
|
|
8190
8335
|
pidPath,
|
|
8191
8336
|
startedAt,
|
|
8192
8337
|
clients,
|
|
8193
8338
|
async close() {
|
|
8194
|
-
|
|
8195
|
-
cancelIdleTimer();
|
|
8339
|
+
lifetime.markStopping();
|
|
8196
8340
|
unsubscribeStore();
|
|
8197
8341
|
if (updateTimer)
|
|
8198
8342
|
clearInterval(updateTimer);
|
|
@@ -8200,32 +8344,30 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8200
8344
|
stopUiPrefsWatcher();
|
|
8201
8345
|
stopKeybindingsWatcher();
|
|
8202
8346
|
stopWorktreeChangesCollector();
|
|
8203
|
-
stopConflictCollector();
|
|
8204
8347
|
activity.close();
|
|
8205
8348
|
broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
|
|
8206
8349
|
for (const client of Array.from(clients)) {
|
|
8207
8350
|
client.socket.destroy();
|
|
8208
8351
|
}
|
|
8209
|
-
await new Promise((
|
|
8352
|
+
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
8210
8353
|
await unlink4(socketPath).catch(() => {});
|
|
8211
8354
|
await unlink4(pidPath).catch(() => {});
|
|
8212
8355
|
}
|
|
8213
8356
|
};
|
|
8214
|
-
await new Promise((
|
|
8357
|
+
await new Promise((resolve3, reject) => {
|
|
8215
8358
|
const evented = server;
|
|
8216
8359
|
evented.once("error", reject);
|
|
8217
8360
|
server.listen(socketPath, () => {
|
|
8218
8361
|
evented.removeListener("error", reject);
|
|
8219
|
-
|
|
8362
|
+
resolve3();
|
|
8220
8363
|
});
|
|
8221
8364
|
});
|
|
8222
|
-
await
|
|
8365
|
+
await writeFile5(pidPath, `${process.pid}
|
|
8223
8366
|
`, "utf8");
|
|
8224
8367
|
async function stopSoon() {
|
|
8225
|
-
if (
|
|
8368
|
+
if (lifetime.isStopping())
|
|
8226
8369
|
return;
|
|
8227
|
-
|
|
8228
|
-
cancelIdleTimer();
|
|
8370
|
+
lifetime.markStopping();
|
|
8229
8371
|
await options.onStop?.();
|
|
8230
8372
|
setTimeout(() => {
|
|
8231
8373
|
serverApi.close().catch((err) => logDaemonError("daemon-shutdown", err));
|
|
@@ -8242,8 +8384,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8242
8384
|
client.channels = normalizeChannelFilter(payload.channels);
|
|
8243
8385
|
const firstSubscriber = !wasSubscribed;
|
|
8244
8386
|
if (client.holdsLifetime)
|
|
8245
|
-
|
|
8246
|
-
logDaemonInfo("conn", `client #${client.id} subscribed as ${role}${client.channels ? ` [${[...client.channels].join(",")}]` : ""} \u2014 ${clients.size} client(s), ${guiCount()} gui${firstSubscriber ? " (collectors resume)" : ""}`);
|
|
8387
|
+
lifetime.guiAttached();
|
|
8388
|
+
logDaemonInfo("conn", `client #${client.id} subscribed as ${role}${client.channels ? ` [${[...client.channels].join(",")}]` : ""} \u2014 ${clients.size} client(s), ${lifetime.guiCount()} gui${firstSubscriber ? " (collectors resume)" : ""}`);
|
|
8247
8389
|
for (const event of bus.snapshot()) {
|
|
8248
8390
|
if (client.channels && !client.channels.has(event.channel))
|
|
8249
8391
|
continue;
|
|
@@ -8264,7 +8406,8 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8264
8406
|
orch,
|
|
8265
8407
|
bus,
|
|
8266
8408
|
activity,
|
|
8267
|
-
|
|
8409
|
+
issues,
|
|
8410
|
+
daemon: { startedAt, socketPath, pid: process.pid, guiCount: () => lifetime.guiCount(), stopSoon },
|
|
8268
8411
|
clientId: client.id
|
|
8269
8412
|
});
|
|
8270
8413
|
}
|
|
@@ -8304,7 +8447,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8304
8447
|
}
|
|
8305
8448
|
async function readPidFile(pidPath) {
|
|
8306
8449
|
try {
|
|
8307
|
-
const raw = await
|
|
8450
|
+
const raw = await readFile8(pidPath, "utf8");
|
|
8308
8451
|
const pid = Number(raw.trim());
|
|
8309
8452
|
return Number.isFinite(pid) ? pid : null;
|
|
8310
8453
|
} catch {
|
|
@@ -8331,14 +8474,16 @@ var init_server = __esm(() => {
|
|
|
8331
8474
|
init_version();
|
|
8332
8475
|
init_activity_registry();
|
|
8333
8476
|
init_auto_title_poller();
|
|
8334
|
-
init_conflict_collector();
|
|
8335
8477
|
init_handlers();
|
|
8478
|
+
init_issues_store();
|
|
8479
|
+
init_lifetime();
|
|
8336
8480
|
init_keybindings_watcher();
|
|
8337
8481
|
init_paths2();
|
|
8338
8482
|
init_protocol();
|
|
8339
8483
|
init_ui_prefs_watcher();
|
|
8340
8484
|
init_worktree_changes_collector();
|
|
8341
8485
|
init_handlers();
|
|
8486
|
+
init_issues_store();
|
|
8342
8487
|
DEFAULT_UPDATE_POLL_MS = 6 * 60 * 60 * 1000;
|
|
8343
8488
|
});
|
|
8344
8489
|
|
|
@@ -8361,7 +8506,7 @@ async function stopDaemonProcess(socketPath, pidPath) {
|
|
|
8361
8506
|
const stopRequest = client.request("daemon.stop").catch(() => {
|
|
8362
8507
|
return;
|
|
8363
8508
|
});
|
|
8364
|
-
const stopTimeout = new Promise((
|
|
8509
|
+
const stopTimeout = new Promise((resolve3) => setTimeout(resolve3, 2000));
|
|
8365
8510
|
await Promise.race([stopRequest, stopTimeout]);
|
|
8366
8511
|
client.close();
|
|
8367
8512
|
if (wasAlive && targetPid !== null) {
|
|
@@ -8380,13 +8525,13 @@ async function stopDaemonProcess(socketPath, pidPath) {
|
|
|
8380
8525
|
method = "sigterm";
|
|
8381
8526
|
escalated = true;
|
|
8382
8527
|
}
|
|
8383
|
-
await new Promise((
|
|
8528
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
8384
8529
|
}
|
|
8385
8530
|
try {
|
|
8386
8531
|
process.kill(targetPid, 0);
|
|
8387
8532
|
process.kill(targetPid, "SIGKILL");
|
|
8388
8533
|
method = "sigkill";
|
|
8389
|
-
await new Promise((
|
|
8534
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
8390
8535
|
} catch {}
|
|
8391
8536
|
}
|
|
8392
8537
|
await unlink5(socketPath).catch(() => {});
|
|
@@ -8408,13 +8553,13 @@ __export(exports_daemon_process, {
|
|
|
8408
8553
|
});
|
|
8409
8554
|
import { spawn as spawn3 } from "child_process";
|
|
8410
8555
|
import { closeSync, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync } from "fs";
|
|
8411
|
-
import { dirname as
|
|
8556
|
+
import { dirname as dirname9, resolve as resolve3 } from "path";
|
|
8412
8557
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8413
8558
|
function spawnDetachedDaemon(command, args, env, logPath) {
|
|
8414
8559
|
let stdio = "ignore";
|
|
8415
8560
|
let logFd;
|
|
8416
8561
|
try {
|
|
8417
|
-
mkdirSync4(
|
|
8562
|
+
mkdirSync4(dirname9(logPath), { recursive: true });
|
|
8418
8563
|
logFd = openSync(logPath, "a");
|
|
8419
8564
|
stdio = ["ignore", logFd, logFd];
|
|
8420
8565
|
} catch {
|
|
@@ -8467,8 +8612,8 @@ async function testDaemonResponds(socketPath, timeoutMs = DAEMON_HELLO_TIMEOUT_M
|
|
|
8467
8612
|
}
|
|
8468
8613
|
const replied = probe2.request("hello", { protocolVersion: DAEMON_PROTOCOL_VERSION }).then(() => true).catch(() => true);
|
|
8469
8614
|
let timer;
|
|
8470
|
-
const timedOut = new Promise((
|
|
8471
|
-
timer = setTimeout(() =>
|
|
8615
|
+
const timedOut = new Promise((resolve4) => {
|
|
8616
|
+
timer = setTimeout(() => resolve4(false), timeoutMs);
|
|
8472
8617
|
});
|
|
8473
8618
|
const alive = await Promise.race([replied, timedOut]);
|
|
8474
8619
|
if (timer)
|
|
@@ -8481,11 +8626,11 @@ function resolveKobeSpawn(subcommand) {
|
|
|
8481
8626
|
if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
|
|
8482
8627
|
return [process.execPath, ...subcommand];
|
|
8483
8628
|
}
|
|
8484
|
-
const dir =
|
|
8629
|
+
const dir = dirname9(here);
|
|
8485
8630
|
const candidates = [
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8631
|
+
resolve3(dir, "../cli/index.ts"),
|
|
8632
|
+
resolve3(dir, "../../../kobe/src/cli/index.ts"),
|
|
8633
|
+
resolve3(dir, "../cli/index.js")
|
|
8489
8634
|
];
|
|
8490
8635
|
const entry = candidates.find((candidate) => existsSync5(candidate));
|
|
8491
8636
|
if (entry)
|
|
@@ -8507,7 +8652,7 @@ __export(exports_repo_cmd, {
|
|
|
8507
8652
|
runRepoSubcommand: () => runRepoSubcommand
|
|
8508
8653
|
});
|
|
8509
8654
|
import { readFileSync as readFileSync4 } from "fs";
|
|
8510
|
-
import { resolve as
|
|
8655
|
+
import { resolve as resolve4 } from "path";
|
|
8511
8656
|
function usageError(message) {
|
|
8512
8657
|
process.stderr.write(`kobe repo: ${message}
|
|
8513
8658
|
|
|
@@ -8517,7 +8662,7 @@ ${REPO_USAGE}
|
|
|
8517
8662
|
}
|
|
8518
8663
|
function readArgFile(path11) {
|
|
8519
8664
|
try {
|
|
8520
|
-
return readFileSync4(
|
|
8665
|
+
return readFileSync4(resolve4(process.cwd(), path11), "utf8");
|
|
8521
8666
|
} catch (err) {
|
|
8522
8667
|
usageError(`cannot read ${path11}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8523
8668
|
}
|
|
@@ -8580,13 +8725,13 @@ async function runRepoSubcommand(args) {
|
|
|
8580
8725
|
}
|
|
8581
8726
|
const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
8582
8727
|
const { existsSync: existsSync6 } = await import("fs");
|
|
8583
|
-
const { join:
|
|
8728
|
+
const { join: join7 } = await import("path");
|
|
8584
8729
|
if (verb === "show") {
|
|
8585
8730
|
const [pathArg] = rest.filter((a) => !a.startsWith("-"));
|
|
8586
|
-
const repo = resolveRepoRoot2(
|
|
8731
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), pathArg ?? "."));
|
|
8587
8732
|
const override = getRepoInitOverride2(repo);
|
|
8588
|
-
const hasFileScript = existsSync6(
|
|
8589
|
-
const hasFilePrompt = existsSync6(
|
|
8733
|
+
const hasFileScript = existsSync6(join7(repo, ".kobe", "init.sh"));
|
|
8734
|
+
const hasFilePrompt = existsSync6(join7(repo, ".kobe", "init-prompt.md"));
|
|
8590
8735
|
console.log(`repo: ${repo}`);
|
|
8591
8736
|
console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
|
|
8592
8737
|
console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
|
|
@@ -8599,7 +8744,7 @@ async function runRepoSubcommand(args) {
|
|
|
8599
8744
|
if (flags.initScript === undefined && flags.initPrompt === undefined) {
|
|
8600
8745
|
usageError("set needs at least one of --init-script(-file) / --init-prompt(-file)");
|
|
8601
8746
|
}
|
|
8602
|
-
const repo = resolveRepoRoot2(
|
|
8747
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), flags.path ?? "."));
|
|
8603
8748
|
const next = setRepoInitOverride2(repo, {
|
|
8604
8749
|
...flags.initScript !== undefined ? { initScript: flags.initScript } : {},
|
|
8605
8750
|
...flags.initPrompt !== undefined ? { initPrompt: flags.initPrompt } : {}
|
|
@@ -8611,7 +8756,7 @@ async function runRepoSubcommand(args) {
|
|
|
8611
8756
|
}
|
|
8612
8757
|
if (verb === "unset") {
|
|
8613
8758
|
const { path: path11, clearScript, clearPrompt } = parseUnsetArgs(rest);
|
|
8614
|
-
const repo = resolveRepoRoot2(
|
|
8759
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), path11 ?? "."));
|
|
8615
8760
|
const next = setRepoInitOverride2(repo, {
|
|
8616
8761
|
...clearScript ? { initScript: "" } : {},
|
|
8617
8762
|
...clearPrompt ? { initPrompt: "" } : {}
|
|
@@ -8684,15 +8829,30 @@ function parseEngineCommand(command) {
|
|
|
8684
8829
|
}
|
|
8685
8830
|
return out;
|
|
8686
8831
|
}
|
|
8687
|
-
function interactiveEngineCommand(vendor) {
|
|
8832
|
+
function interactiveEngineCommand(vendor, effort) {
|
|
8688
8833
|
const v = vendor ?? "claude";
|
|
8689
8834
|
const override = getPersistedString(engineCommandKey(v))?.trim();
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8835
|
+
const base = (() => {
|
|
8836
|
+
if (override) {
|
|
8837
|
+
const argv = parseEngineCommand(override);
|
|
8838
|
+
if (argv.length > 0)
|
|
8839
|
+
return argv;
|
|
8840
|
+
}
|
|
8841
|
+
return defaultEngineCommand(v);
|
|
8842
|
+
})();
|
|
8843
|
+
return withEngineEffort(base, v, effort);
|
|
8844
|
+
}
|
|
8845
|
+
function withEngineEffort(argv, vendor, effort) {
|
|
8846
|
+
const trimmed = effort?.trim();
|
|
8847
|
+
if (!trimmed)
|
|
8848
|
+
return argv;
|
|
8849
|
+
const v = vendor ?? "claude";
|
|
8850
|
+
const levels = engineEntry(v).effortLevels;
|
|
8851
|
+
if (!levels?.includes(trimmed))
|
|
8852
|
+
return argv;
|
|
8853
|
+
if (v === "codex")
|
|
8854
|
+
return [...argv, "-c", `model_reasoning_effort=${trimmed}`];
|
|
8855
|
+
return argv;
|
|
8696
8856
|
}
|
|
8697
8857
|
function withClaudeSessionId(argv, vendor) {
|
|
8698
8858
|
if ((vendor ?? "claude") !== "claude")
|
|
@@ -8929,7 +9089,7 @@ async function deliverFirstPrompt(session, prompt) {
|
|
|
8929
9089
|
return;
|
|
8930
9090
|
await pasteAndSubmit(pane, prompt);
|
|
8931
9091
|
}
|
|
8932
|
-
var sleep = (ms) => new Promise((
|
|
9092
|
+
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8933
9093
|
var init_prompt_delivery = __esm(() => {
|
|
8934
9094
|
init_client2();
|
|
8935
9095
|
});
|
|
@@ -10837,7 +10997,7 @@ function normalizeHex(value) {
|
|
|
10837
10997
|
}
|
|
10838
10998
|
function resolveThemeSlotHex(theme, slot, mode = "dark") {
|
|
10839
10999
|
const defs = theme.defs ?? {};
|
|
10840
|
-
function
|
|
11000
|
+
function resolve5(c, chain) {
|
|
10841
11001
|
if (typeof c === "string") {
|
|
10842
11002
|
if (c === "transparent" || c === "none")
|
|
10843
11003
|
return null;
|
|
@@ -10848,17 +11008,17 @@ function resolveThemeSlotHex(theme, slot, mode = "dark") {
|
|
|
10848
11008
|
const next = defs[c] ?? theme.theme[c];
|
|
10849
11009
|
if (next === undefined)
|
|
10850
11010
|
return null;
|
|
10851
|
-
return
|
|
11011
|
+
return resolve5(next, [...chain, c]);
|
|
10852
11012
|
}
|
|
10853
11013
|
if (!c || typeof c !== "object")
|
|
10854
11014
|
return null;
|
|
10855
11015
|
const variant = c[mode];
|
|
10856
|
-
return typeof variant === "string" ?
|
|
11016
|
+
return typeof variant === "string" ? resolve5(variant, chain) : null;
|
|
10857
11017
|
}
|
|
10858
11018
|
const value = theme.theme[slot];
|
|
10859
11019
|
if (value === undefined)
|
|
10860
11020
|
return null;
|
|
10861
|
-
return
|
|
11021
|
+
return resolve5(value, [slot]);
|
|
10862
11022
|
}
|
|
10863
11023
|
|
|
10864
11024
|
// src/tui/context/theme/schema.ts
|
|
@@ -10913,9 +11073,9 @@ var init_schema = () => {};
|
|
|
10913
11073
|
|
|
10914
11074
|
// src/tui/context/theme/loader.ts
|
|
10915
11075
|
import { readFileSync as readFileSync6, readdirSync } from "fs";
|
|
10916
|
-
import { join as
|
|
11076
|
+
import { join as join7 } from "path";
|
|
10917
11077
|
function userThemesDir() {
|
|
10918
|
-
return
|
|
11078
|
+
return join7(kobeStateDir(), "themes");
|
|
10919
11079
|
}
|
|
10920
11080
|
function loadUserThemes() {
|
|
10921
11081
|
const dir = userThemesDir();
|
|
@@ -10929,7 +11089,7 @@ function loadUserThemes() {
|
|
|
10929
11089
|
for (const file of entries) {
|
|
10930
11090
|
if (!file.endsWith(".json"))
|
|
10931
11091
|
continue;
|
|
10932
|
-
const path11 =
|
|
11092
|
+
const path11 = join7(dir, file);
|
|
10933
11093
|
let parsed;
|
|
10934
11094
|
try {
|
|
10935
11095
|
const text = readFileSync6(path11, "utf8");
|
|
@@ -11027,6 +11187,68 @@ var init_tmux_border_theme = __esm(() => {
|
|
|
11027
11187
|
FOCUS_ACCENT_SLOT_NAMES2 = ["primary", "success", "info"];
|
|
11028
11188
|
});
|
|
11029
11189
|
|
|
11190
|
+
// src/tui/panes/terminal/layout-coord.ts
|
|
11191
|
+
var exports_layout_coord = {};
|
|
11192
|
+
__export(exports_layout_coord, {
|
|
11193
|
+
recordGen: () => recordGen,
|
|
11194
|
+
isLatestGen: () => isLatestGen,
|
|
11195
|
+
genAgeMs: () => genAgeMs,
|
|
11196
|
+
coalesceLayoutWork: () => coalesceLayoutWork,
|
|
11197
|
+
RESIZE_GUARD_MS: () => RESIZE_GUARD_MS,
|
|
11198
|
+
LAYOUT_COALESCE_MS: () => LAYOUT_COALESCE_MS
|
|
11199
|
+
});
|
|
11200
|
+
import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
|
|
11201
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
11202
|
+
import { join as join8 } from "path";
|
|
11203
|
+
function coordDir() {
|
|
11204
|
+
return join8(kobeStateDir(), "layout-coord");
|
|
11205
|
+
}
|
|
11206
|
+
function genPath(session, kind) {
|
|
11207
|
+
const hash = createHash5("sha1").update(session).digest("hex").slice(0, 16);
|
|
11208
|
+
return join8(coordDir(), `${hash}.${kind}`);
|
|
11209
|
+
}
|
|
11210
|
+
function recordGen(session, kind) {
|
|
11211
|
+
const nonce = randomUUID2();
|
|
11212
|
+
try {
|
|
11213
|
+
mkdirSync5(coordDir(), { recursive: true });
|
|
11214
|
+
const path11 = genPath(session, kind);
|
|
11215
|
+
const tmp = `${path11}.${nonce}.tmp`;
|
|
11216
|
+
writeFileSync2(tmp, `${Date.now()}
|
|
11217
|
+
${nonce}`);
|
|
11218
|
+
renameSync2(tmp, path11);
|
|
11219
|
+
} catch {}
|
|
11220
|
+
return nonce;
|
|
11221
|
+
}
|
|
11222
|
+
function isLatestGen(session, kind, nonce) {
|
|
11223
|
+
try {
|
|
11224
|
+
return readFileSync8(genPath(session, kind), "utf8").split(`
|
|
11225
|
+
`)[1]?.trim() === nonce;
|
|
11226
|
+
} catch {
|
|
11227
|
+
return true;
|
|
11228
|
+
}
|
|
11229
|
+
}
|
|
11230
|
+
function genAgeMs(session, kind, now = Date.now()) {
|
|
11231
|
+
try {
|
|
11232
|
+
const ts = Number.parseInt(readFileSync8(genPath(session, kind), "utf8").split(`
|
|
11233
|
+
`)[0] ?? "", 10);
|
|
11234
|
+
return Number.isFinite(ts) ? now - ts : Number.POSITIVE_INFINITY;
|
|
11235
|
+
} catch {
|
|
11236
|
+
return Number.POSITIVE_INFINITY;
|
|
11237
|
+
}
|
|
11238
|
+
}
|
|
11239
|
+
async function coalesceLayoutWork(session, kind, work, debounceMs = LAYOUT_COALESCE_MS) {
|
|
11240
|
+
const nonce = recordGen(session, kind);
|
|
11241
|
+
if (debounceMs > 0)
|
|
11242
|
+
await new Promise((resolve5) => setTimeout(resolve5, debounceMs));
|
|
11243
|
+
if (!isLatestGen(session, kind, nonce))
|
|
11244
|
+
return;
|
|
11245
|
+
await work();
|
|
11246
|
+
}
|
|
11247
|
+
var LAYOUT_COALESCE_MS = 120, RESIZE_GUARD_MS = 400;
|
|
11248
|
+
var init_layout_coord = __esm(() => {
|
|
11249
|
+
init_env();
|
|
11250
|
+
});
|
|
11251
|
+
|
|
11030
11252
|
// src/tui/panes/terminal/pane-heal.ts
|
|
11031
11253
|
function parseKobePaneRows(stdout) {
|
|
11032
11254
|
const rows = [];
|
|
@@ -11140,6 +11362,7 @@ async function globalRightColumnResizeArgs() {
|
|
|
11140
11362
|
return (await globalLayoutPrefs()).rcArgs;
|
|
11141
11363
|
}
|
|
11142
11364
|
async function healWorkspaceLayout(session, versions) {
|
|
11365
|
+
recordGen(session, "resize");
|
|
11143
11366
|
const { tasksWidth, rcArgs } = await globalLayoutPrefs();
|
|
11144
11367
|
const rows = await listKobePanes(session);
|
|
11145
11368
|
if (!rows)
|
|
@@ -11177,7 +11400,7 @@ async function captureGlobalLayout(session) {
|
|
|
11177
11400
|
"-t",
|
|
11178
11401
|
`=${session}`,
|
|
11179
11402
|
"-F",
|
|
11180
|
-
"#{@kobe_role}\t#{pane_width}\t#{pane_height}\t#{window_width}\t#{window_height}"
|
|
11403
|
+
"#{@kobe_role}\t#{pane_width}\t#{pane_height}\t#{window_width}\t#{window_height}\t#{window_zoomed_flag}"
|
|
11181
11404
|
]);
|
|
11182
11405
|
if (code !== 0)
|
|
11183
11406
|
return;
|
|
@@ -11185,6 +11408,8 @@ async function captureGlobalLayout(session) {
|
|
|
11185
11408
|
`).map((line) => line.split("\t")).filter((cols) => (cols[0]?.trim() ?? "") !== "");
|
|
11186
11409
|
if (rows.length === 0)
|
|
11187
11410
|
return;
|
|
11411
|
+
if (rows.some((cols) => cols[5]?.trim() === "1"))
|
|
11412
|
+
return;
|
|
11188
11413
|
const winW = Number.parseInt(rows[0][3]?.trim() ?? "", 10);
|
|
11189
11414
|
const winH = Number.parseInt(rows[0][4]?.trim() ?? "", 10);
|
|
11190
11415
|
const sets = [];
|
|
@@ -11208,6 +11433,28 @@ async function captureGlobalLayout(session) {
|
|
|
11208
11433
|
if (sets.length > 0)
|
|
11209
11434
|
await runTmuxSequence(sets);
|
|
11210
11435
|
}
|
|
11436
|
+
function shouldCaptureDrag(stdout) {
|
|
11437
|
+
const rows = stdout.split(`
|
|
11438
|
+
`).map((line) => line.split("\t")).filter((cols) => (cols[0]?.trim() ?? "") !== "");
|
|
11439
|
+
if (rows.length === 0)
|
|
11440
|
+
return false;
|
|
11441
|
+
if (rows.some((cols) => cols[1]?.trim() === "1"))
|
|
11442
|
+
return false;
|
|
11443
|
+
const roles = new Set(rows.map((cols) => cols[0]?.trim()));
|
|
11444
|
+
return roles.has("tasks") && roles.has("ops");
|
|
11445
|
+
}
|
|
11446
|
+
async function captureGlobalLayoutOnDrag(session) {
|
|
11447
|
+
const { code, stdout } = await runTmuxCapturing([
|
|
11448
|
+
"list-panes",
|
|
11449
|
+
"-t",
|
|
11450
|
+
`=${session}`,
|
|
11451
|
+
"-F",
|
|
11452
|
+
"#{@kobe_role}\t#{window_zoomed_flag}"
|
|
11453
|
+
]);
|
|
11454
|
+
if (code !== 0 || !shouldCaptureDrag(stdout))
|
|
11455
|
+
return;
|
|
11456
|
+
await captureGlobalLayout(session);
|
|
11457
|
+
}
|
|
11211
11458
|
async function refreshKobeWorkspacePanes(session) {
|
|
11212
11459
|
const sessionOptions = await getSessionOptions(session, ["@kobe_worktree", "@kobe_task", "@kobe_vendor"]);
|
|
11213
11460
|
const cwd = sessionOptions["@kobe_worktree"] || process.cwd();
|
|
@@ -11234,6 +11481,7 @@ var init_pane_heal = __esm(() => {
|
|
|
11234
11481
|
init_tmux_border_theme();
|
|
11235
11482
|
init_version();
|
|
11236
11483
|
init_launch();
|
|
11484
|
+
init_layout_coord();
|
|
11237
11485
|
KOBE_PANE_LIST_FORMAT = `#{window_id} #{pane_id} #{@kobe_role} #{${PANE_VERSION_OPTION}} #{pane_width}`;
|
|
11238
11486
|
});
|
|
11239
11487
|
|
|
@@ -11463,6 +11711,7 @@ __export(exports_tmux, {
|
|
|
11463
11711
|
selectTasksPane: () => selectTasksPane,
|
|
11464
11712
|
refreshKobeWorkspacePanes: () => refreshKobeWorkspacePanes,
|
|
11465
11713
|
quickCreate: () => quickCreate,
|
|
11714
|
+
prepareWindowForSwitch: () => prepareWindowForSwitch,
|
|
11466
11715
|
prepareWindowForAttach: () => prepareWindowForAttach,
|
|
11467
11716
|
parseObservedSession: () => parseObservedSession,
|
|
11468
11717
|
openUpdateTab: () => openUpdateTab,
|
|
@@ -11480,6 +11729,7 @@ __export(exports_tmux, {
|
|
|
11480
11729
|
chatTabRenameBinding: () => chatTabRenameBinding,
|
|
11481
11730
|
chatTabCloseBinding: () => chatTabCloseBinding,
|
|
11482
11731
|
chatTabChooseEngineBindings: () => chatTabChooseEngineBindings,
|
|
11732
|
+
captureGlobalLayoutOnDrag: () => captureGlobalLayoutOnDrag,
|
|
11483
11733
|
captureGlobalLayout: () => captureGlobalLayout,
|
|
11484
11734
|
attachArgv: () => attachArgv,
|
|
11485
11735
|
PANE_VERSION_OPTION: () => PANE_VERSION_OPTION,
|
|
@@ -11498,11 +11748,26 @@ function positiveInt(value) {
|
|
|
11498
11748
|
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
11499
11749
|
}
|
|
11500
11750
|
async function prepareWindowForAttach(session) {
|
|
11751
|
+
recordGen(session, "resize");
|
|
11501
11752
|
const sizeArgs = tmuxInitialSizeArgs();
|
|
11502
11753
|
if (sizeArgs.length > 0)
|
|
11503
11754
|
await runTmux(["resize-window", "-t", `=${session}`, ...sizeArgs]);
|
|
11504
11755
|
await healWorkspaceLayout(session);
|
|
11505
11756
|
}
|
|
11757
|
+
async function attachedWindowSizeArgs() {
|
|
11758
|
+
const { code, stdout } = await runTmuxCapturing(["display-message", "-p", "#{window_width}\t#{window_height}"]);
|
|
11759
|
+
if (code !== 0)
|
|
11760
|
+
return [];
|
|
11761
|
+
const [w, h] = stdout.trim().split("\t").map((s) => Number.parseInt(s, 10));
|
|
11762
|
+
return Number.isInteger(w) && w > 0 && Number.isInteger(h) && h > 0 ? ["-x", `${w}`, "-y", `${h}`] : [];
|
|
11763
|
+
}
|
|
11764
|
+
async function prepareWindowForSwitch(session) {
|
|
11765
|
+
recordGen(session, "resize");
|
|
11766
|
+
const sizeArgs = await attachedWindowSizeArgs();
|
|
11767
|
+
if (sizeArgs.length > 0)
|
|
11768
|
+
await runTmux(["resize-window", "-t", `=${session}`, ...sizeArgs]);
|
|
11769
|
+
await healWorkspaceLayout(session);
|
|
11770
|
+
}
|
|
11506
11771
|
function focusBindCommand(key, dir) {
|
|
11507
11772
|
return [
|
|
11508
11773
|
"bind-key",
|
|
@@ -11634,6 +11899,8 @@ async function ensureSessionImpl(opts) {
|
|
|
11634
11899
|
const focusTasksTmuxCommand = `run-shell ${shellQuote(focusTasksCommand)}`;
|
|
11635
11900
|
const healLayoutCommand = `${envStr}${invStr} heal-layout --session '#{session_name}'`;
|
|
11636
11901
|
const healLayoutTmuxCommand = `run-shell -b ${shellQuote(healLayoutCommand)}`;
|
|
11902
|
+
const captureLayoutCommand = `${envStr}${invStr} capture-layout --session '#{session_name}'`;
|
|
11903
|
+
const captureLayoutTmuxCommand = `run-shell -b ${shellQuote(captureLayoutCommand)}`;
|
|
11637
11904
|
const userKeys = resolveUserTmuxKeys();
|
|
11638
11905
|
const unbinds = [];
|
|
11639
11906
|
if (userKeys.overridden.has(TMUX_FOCUS_ID)) {
|
|
@@ -11676,6 +11943,7 @@ async function ensureSessionImpl(opts) {
|
|
|
11676
11943
|
],
|
|
11677
11944
|
["set-option", "-g", "mouse", "on"],
|
|
11678
11945
|
["set-hook", "-g", "window-resized", healLayoutTmuxCommand],
|
|
11946
|
+
["set-hook", "-g", "window-layout-changed", captureLayoutTmuxCommand],
|
|
11679
11947
|
...unbinds,
|
|
11680
11948
|
...b["tmux.detach"] ? [
|
|
11681
11949
|
[
|
|
@@ -11726,6 +11994,7 @@ var init_tmux = __esm(() => {
|
|
|
11726
11994
|
init_tmux_border_theme();
|
|
11727
11995
|
init_chattab();
|
|
11728
11996
|
init_launch();
|
|
11997
|
+
init_layout_coord();
|
|
11729
11998
|
init_pane_heal();
|
|
11730
11999
|
init_client2();
|
|
11731
12000
|
init_chattab();
|
|
@@ -11744,17 +12013,17 @@ var exports_repo_init = {};
|
|
|
11744
12013
|
__export(exports_repo_init, {
|
|
11745
12014
|
resolveRepoInit: () => resolveRepoInit
|
|
11746
12015
|
});
|
|
11747
|
-
import { existsSync as existsSync7, readFileSync as
|
|
11748
|
-
import { join as
|
|
12016
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
|
|
12017
|
+
import { join as join9 } from "path";
|
|
11749
12018
|
function repoFileScript(worktreePath) {
|
|
11750
|
-
return existsSync7(
|
|
12019
|
+
return existsSync7(join9(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
|
|
11751
12020
|
}
|
|
11752
12021
|
function repoFilePrompt(worktreePath) {
|
|
11753
|
-
const p =
|
|
12022
|
+
const p = join9(worktreePath, INIT_PROMPT_REL);
|
|
11754
12023
|
if (!existsSync7(p))
|
|
11755
12024
|
return;
|
|
11756
12025
|
try {
|
|
11757
|
-
const text =
|
|
12026
|
+
const text = readFileSync9(p, "utf8");
|
|
11758
12027
|
return text.trim().length > 0 ? text : undefined;
|
|
11759
12028
|
} catch {
|
|
11760
12029
|
return;
|
|
@@ -11772,8 +12041,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
|
|
|
11772
12041
|
var INIT_SCRIPT_REL, INIT_PROMPT_REL;
|
|
11773
12042
|
var init_repo_init = __esm(() => {
|
|
11774
12043
|
init_repos();
|
|
11775
|
-
INIT_SCRIPT_REL =
|
|
11776
|
-
INIT_PROMPT_REL =
|
|
12044
|
+
INIT_SCRIPT_REL = join9(".kobe", "init.sh");
|
|
12045
|
+
INIT_PROMPT_REL = join9(".kobe", "init-prompt.md");
|
|
11777
12046
|
});
|
|
11778
12047
|
|
|
11779
12048
|
// src/cli/api-cmd.ts
|
|
@@ -11800,7 +12069,7 @@ __export(exports_api_cmd, {
|
|
|
11800
12069
|
API_VERBS: () => API_VERBS,
|
|
11801
12070
|
API_SCHEMA_VERSION: () => API_SCHEMA_VERSION
|
|
11802
12071
|
});
|
|
11803
|
-
import { resolve as
|
|
12072
|
+
import { resolve as resolve5 } from "path";
|
|
11804
12073
|
function groupOf(verbName) {
|
|
11805
12074
|
for (const [group, names] of Object.entries(VERB_GROUPS)) {
|
|
11806
12075
|
if (names.includes(verbName))
|
|
@@ -11965,10 +12234,10 @@ class VerbArgs {
|
|
|
11965
12234
|
}
|
|
11966
12235
|
path(name) {
|
|
11967
12236
|
const v = this.str(name);
|
|
11968
|
-
return v === undefined ? undefined :
|
|
12237
|
+
return v === undefined ? undefined : resolve5(process.cwd(), v);
|
|
11969
12238
|
}
|
|
11970
12239
|
requirePath(name) {
|
|
11971
|
-
return
|
|
12240
|
+
return resolve5(process.cwd(), this.require(name));
|
|
11972
12241
|
}
|
|
11973
12242
|
}
|
|
11974
12243
|
function parseAgentsSpec(spec) {
|
|
@@ -11988,6 +12257,9 @@ function parseAgentsSpec(spec) {
|
|
|
11988
12257
|
if (!Number.isInteger(count) || count <= 0) {
|
|
11989
12258
|
throw new ApiError(`--agents count for "${vendor}" must be a positive integer`, "BAD_FLAG");
|
|
11990
12259
|
}
|
|
12260
|
+
if (out.length + count > FANOUT_CAP) {
|
|
12261
|
+
throw new ApiError(`--agents requests ${out.length + count} agents, exceeds the cap of ${FANOUT_CAP}`, "BAD_FLAG");
|
|
12262
|
+
}
|
|
11991
12263
|
for (let i = 0;i < count; i++)
|
|
11992
12264
|
out.push(vendor);
|
|
11993
12265
|
}
|
|
@@ -12154,10 +12426,22 @@ function daemonOf(ctx) {
|
|
|
12154
12426
|
async function simpleRpc(ctx, name, payload) {
|
|
12155
12427
|
return daemonOf(ctx).request(name, payload);
|
|
12156
12428
|
}
|
|
12429
|
+
async function issueUpdate(ctx) {
|
|
12430
|
+
const title = ctx.args.str("title");
|
|
12431
|
+
const body = ctx.args.str("body");
|
|
12432
|
+
if (title === undefined && body === undefined) {
|
|
12433
|
+
throw new ApiError("issue-update requires --title and/or --body", "MISSING_FLAG");
|
|
12434
|
+
}
|
|
12435
|
+
return simpleRpc(ctx, "issue.mutate", {
|
|
12436
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12437
|
+
op: { type: "update", id: ctx.args.int("id"), title, body }
|
|
12438
|
+
});
|
|
12439
|
+
}
|
|
12157
12440
|
async function add(ctx) {
|
|
12158
12441
|
const daemon = daemonOf(ctx);
|
|
12159
|
-
const { args } = ctx;
|
|
12160
|
-
const
|
|
12442
|
+
const { args, runtime } = ctx;
|
|
12443
|
+
const repo = await runtime.resolveRepoRoot(args.requirePath("repo"));
|
|
12444
|
+
const payload = { repo };
|
|
12161
12445
|
const title = args.str("title");
|
|
12162
12446
|
if (title)
|
|
12163
12447
|
payload.title = title;
|
|
@@ -12167,11 +12451,12 @@ async function add(ctx) {
|
|
|
12167
12451
|
const baseRef = args.str("base-branch");
|
|
12168
12452
|
if (baseRef)
|
|
12169
12453
|
payload.baseRef = baseRef;
|
|
12170
|
-
const vendor = args.vendor();
|
|
12454
|
+
const vendor = args.vendor() ?? await runtime.defaultVendor();
|
|
12171
12455
|
if (vendor)
|
|
12172
12456
|
payload.vendor = vendor;
|
|
12173
12457
|
const res = await daemon.request("task.create", payload);
|
|
12174
12458
|
const taskId = res.taskId;
|
|
12459
|
+
await daemon.request("task.setActive", { taskId });
|
|
12175
12460
|
const status = args.enumOf("status");
|
|
12176
12461
|
if (status)
|
|
12177
12462
|
await daemon.request("task.status", { taskId, status });
|
|
@@ -12186,6 +12471,7 @@ async function add(ctx) {
|
|
|
12186
12471
|
if (!prompt)
|
|
12187
12472
|
return { taskId, task, started: false };
|
|
12188
12473
|
const delivered = await ctx.runtime.deliverPrompt(daemon, { id: taskId, worktreePath: task.worktreePath, vendor: task.vendor, repo: task.repo }, prompt);
|
|
12474
|
+
task = (await daemon.request("task.get", { taskId })).task;
|
|
12189
12475
|
return { taskId, task, started: delivered.started, engineReady: delivered.engineReady, session: delivered.session };
|
|
12190
12476
|
}
|
|
12191
12477
|
async function send(ctx) {
|
|
@@ -12281,13 +12567,14 @@ async function adopt(ctx) {
|
|
|
12281
12567
|
}
|
|
12282
12568
|
async function fanOut(ctx) {
|
|
12283
12569
|
const daemon = daemonOf(ctx);
|
|
12284
|
-
const { args } = ctx;
|
|
12285
|
-
const repo = args.requirePath("repo");
|
|
12570
|
+
const { args, runtime } = ctx;
|
|
12571
|
+
const repo = await runtime.resolveRepoRoot(args.requirePath("repo"));
|
|
12286
12572
|
const prompt = args.require("prompt");
|
|
12287
12573
|
const title = args.str("title");
|
|
12288
12574
|
const baseRef = args.str("base-branch");
|
|
12289
12575
|
const agentsSpec = args.str("agents");
|
|
12290
|
-
const
|
|
12576
|
+
const defaultVendor = await runtime.defaultVendor();
|
|
12577
|
+
const plan = agentsSpec ? parseAgentsSpec(agentsSpec) : new Array(args.int("count") ?? 1).fill(args.vendor() ?? defaultVendor ?? "claude");
|
|
12291
12578
|
if (plan.length > FANOUT_CAP) {
|
|
12292
12579
|
throw new ApiError(`fan-out of ${plan.length} exceeds the cap of ${FANOUT_CAP} \u2014 spawn in batches`, "BAD_FLAG");
|
|
12293
12580
|
}
|
|
@@ -12422,7 +12709,7 @@ ${apiUsage()}`, "BAD_VERB", 2);
|
|
|
12422
12709
|
session?.close();
|
|
12423
12710
|
}
|
|
12424
12711
|
}
|
|
12425
|
-
var API_SCHEMA_VERSION = 2, FANOUT_CAP = 10,
|
|
12712
|
+
var API_SCHEMA_VERSION = 2, FANOUT_CAP = 10, TASK_STATUSES2, ISSUE_STATUSES2, ApiError, F, VERB_ALIASES, VERB_GROUPS, VERBS, API_VERBS, GLOBAL_FLAGS, realPromptDeliveryOps, defaultApiRuntime;
|
|
12426
12713
|
var init_api_cmd = __esm(() => {
|
|
12427
12714
|
init_interactive_command();
|
|
12428
12715
|
init_feedback();
|
|
@@ -12431,7 +12718,8 @@ var init_api_cmd = __esm(() => {
|
|
|
12431
12718
|
init_vendor();
|
|
12432
12719
|
init_version();
|
|
12433
12720
|
init_daemon_session();
|
|
12434
|
-
|
|
12721
|
+
TASK_STATUSES2 = ["backlog", "in_progress", "in_review", "done", "canceled", "error"];
|
|
12722
|
+
ISSUE_STATUSES2 = ["open", "doing", "hold", "done"];
|
|
12435
12723
|
ApiError = class ApiError extends Error {
|
|
12436
12724
|
code;
|
|
12437
12725
|
constructor(message, code) {
|
|
@@ -12477,6 +12765,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12477
12765
|
create: ["add", "fan-out"],
|
|
12478
12766
|
drive: ["send", "dispatch", "note", "set-active"],
|
|
12479
12767
|
edit: ["rename", "set-branch", "set-vendor", "set-status"],
|
|
12768
|
+
issues: ["issue-list", "issue-create", "issue-set-status", "issue-update"],
|
|
12480
12769
|
lifecycle: ["archive", "pin", "delete"],
|
|
12481
12770
|
worktree: ["ensure-worktree", "adopt", "discover-adoptable"],
|
|
12482
12771
|
feedback: ["feedback"]
|
|
@@ -12521,7 +12810,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12521
12810
|
{
|
|
12522
12811
|
name: "status",
|
|
12523
12812
|
type: "enum",
|
|
12524
|
-
values:
|
|
12813
|
+
values: TASK_STATUSES2,
|
|
12525
12814
|
default: "backlog",
|
|
12526
12815
|
description: "Initial lifecycle status."
|
|
12527
12816
|
},
|
|
@@ -12590,8 +12879,51 @@ var init_api_cmd = __esm(() => {
|
|
|
12590
12879
|
description: "Discussion category slug."
|
|
12591
12880
|
}
|
|
12592
12881
|
],
|
|
12593
|
-
offline: true,
|
|
12594
|
-
handler: feedback
|
|
12882
|
+
offline: true,
|
|
12883
|
+
handler: feedback
|
|
12884
|
+
},
|
|
12885
|
+
{
|
|
12886
|
+
name: "issue-list",
|
|
12887
|
+
summary: "List daemon-owned issues for a repo.",
|
|
12888
|
+
flags: [F.repo()],
|
|
12889
|
+
handler: (ctx) => simpleRpc(ctx, "issue.list", { repoRoot: ctx.args.requirePath("repo") })
|
|
12890
|
+
},
|
|
12891
|
+
{
|
|
12892
|
+
name: "issue-create",
|
|
12893
|
+
summary: "Create a daemon-owned issue for a repo.",
|
|
12894
|
+
flags: [
|
|
12895
|
+
F.repo(),
|
|
12896
|
+
{ name: "title", type: "string", required: true, placeholder: "T", description: "Issue title." },
|
|
12897
|
+
{ name: "body", type: "string", placeholder: "TEXT", description: "Issue body." }
|
|
12898
|
+
],
|
|
12899
|
+
handler: (ctx) => simpleRpc(ctx, "issue.mutate", {
|
|
12900
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12901
|
+
op: { type: "create", title: ctx.args.require("title"), body: ctx.args.str("body") }
|
|
12902
|
+
})
|
|
12903
|
+
},
|
|
12904
|
+
{
|
|
12905
|
+
name: "issue-set-status",
|
|
12906
|
+
summary: "Set a daemon-owned issue's status.",
|
|
12907
|
+
flags: [
|
|
12908
|
+
F.repo(),
|
|
12909
|
+
{ name: "id", type: "int", required: true, placeholder: "N", description: "Issue id." },
|
|
12910
|
+
{ name: "status", type: "enum", required: true, values: ISSUE_STATUSES2, description: "New issue status." }
|
|
12911
|
+
],
|
|
12912
|
+
handler: (ctx) => simpleRpc(ctx, "issue.mutate", {
|
|
12913
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12914
|
+
op: { type: "setStatus", id: ctx.args.int("id"), status: ctx.args.requireEnum("status") }
|
|
12915
|
+
})
|
|
12916
|
+
},
|
|
12917
|
+
{
|
|
12918
|
+
name: "issue-update",
|
|
12919
|
+
summary: "Update a daemon-owned issue's title and/or body.",
|
|
12920
|
+
flags: [
|
|
12921
|
+
F.repo(),
|
|
12922
|
+
{ name: "id", type: "int", required: true, placeholder: "N", description: "Issue id." },
|
|
12923
|
+
{ name: "title", type: "string", placeholder: "T", description: "New title." },
|
|
12924
|
+
{ name: "body", type: "string", placeholder: "TEXT", description: "New body." }
|
|
12925
|
+
],
|
|
12926
|
+
handler: issueUpdate
|
|
12595
12927
|
},
|
|
12596
12928
|
{
|
|
12597
12929
|
name: "collect",
|
|
@@ -12631,7 +12963,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12631
12963
|
summary: "Set a task's lifecycle status.",
|
|
12632
12964
|
flags: [
|
|
12633
12965
|
F.taskId(),
|
|
12634
|
-
{ name: "status", type: "enum", required: true, values:
|
|
12966
|
+
{ name: "status", type: "enum", required: true, values: TASK_STATUSES2, description: "New status." }
|
|
12635
12967
|
],
|
|
12636
12968
|
handler: (ctx) => simpleRpc(ctx, "task.status", {
|
|
12637
12969
|
taskId: ctx.args.require("task-id"),
|
|
@@ -12718,7 +13050,12 @@ var init_api_cmd = __esm(() => {
|
|
|
12718
13050
|
defaultApiRuntime = {
|
|
12719
13051
|
isTaskRunning: (taskId) => sessionExists(tmuxSessionName(taskId)),
|
|
12720
13052
|
deliverPrompt: (client, target, prompt) => deliverPrompt(client, target, prompt),
|
|
12721
|
-
resolveRepoRoot: async (absPath) => (await Promise.resolve().then(() => (init_repos(), exports_repos))).
|
|
13053
|
+
resolveRepoRoot: async (absPath) => (await Promise.resolve().then(() => (init_repos(), exports_repos))).resolveMainRepoRoot(absPath),
|
|
13054
|
+
defaultVendor: async () => {
|
|
13055
|
+
const { getPersistedString: getPersistedString2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
13056
|
+
const value = getPersistedString2("lastSelectedVendor")?.trim();
|
|
13057
|
+
return value ? value : undefined;
|
|
13058
|
+
},
|
|
12722
13059
|
readWorktreeChanges: async (worktreePath) => (await Promise.resolve().then(() => (init_worktree_changes(), exports_worktree_changes))).readWorktreeChanges(worktreePath),
|
|
12723
13060
|
tearDownSession: async (taskId) => {
|
|
12724
13061
|
const session = tmuxSessionName(taskId);
|
|
@@ -12819,8 +13156,8 @@ var exports_theme = {};
|
|
|
12819
13156
|
__export(exports_theme, {
|
|
12820
13157
|
runThemeSubcommand: () => runThemeSubcommand
|
|
12821
13158
|
});
|
|
12822
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
12823
|
-
import { basename as basename5, join as
|
|
13159
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync10, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
13160
|
+
import { basename as basename5, join as join10, resolve as resolve6 } from "path";
|
|
12824
13161
|
function fail3(message) {
|
|
12825
13162
|
process.stderr.write(`kobe theme: ${message}
|
|
12826
13163
|
`);
|
|
@@ -12851,7 +13188,7 @@ function listThemes() {
|
|
|
12851
13188
|
} else {
|
|
12852
13189
|
for (const f of userFiles) {
|
|
12853
13190
|
const name = f.slice(0, -".json".length);
|
|
12854
|
-
const path11 =
|
|
13191
|
+
const path11 = join10(dir, f);
|
|
12855
13192
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
12856
13193
|
lines.push(` ${name}${overridesBundled} ${path11}`);
|
|
12857
13194
|
}
|
|
@@ -12877,10 +13214,10 @@ async function readSource(source) {
|
|
|
12877
13214
|
const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
|
|
12878
13215
|
return { text: text2, defaultName: defaultName2 };
|
|
12879
13216
|
}
|
|
12880
|
-
const abs =
|
|
13217
|
+
const abs = resolve6(process.cwd(), source);
|
|
12881
13218
|
let text;
|
|
12882
13219
|
try {
|
|
12883
|
-
text =
|
|
13220
|
+
text = readFileSync10(abs, "utf8");
|
|
12884
13221
|
} catch (err) {
|
|
12885
13222
|
fail3(`failed to read ${abs}: ${err instanceof Error ? err.message : String(err)}`);
|
|
12886
13223
|
}
|
|
@@ -12940,12 +13277,12 @@ async function addTheme(args) {
|
|
|
12940
13277
|
fail3(`invalid theme name "${name}" (use letters, digits, '.', '_', '-')`);
|
|
12941
13278
|
}
|
|
12942
13279
|
const dir = userThemesDir();
|
|
12943
|
-
|
|
12944
|
-
const dest =
|
|
13280
|
+
mkdirSync6(dir, { recursive: true });
|
|
13281
|
+
const dest = join10(dir, `${name}.json`);
|
|
12945
13282
|
if (existsSync8(dest) && !opts.force) {
|
|
12946
13283
|
fail3(`${dest} already exists (pass --force to overwrite)`);
|
|
12947
13284
|
}
|
|
12948
|
-
|
|
13285
|
+
writeFileSync3(dest, `${JSON.stringify(result.theme, null, 2)}
|
|
12949
13286
|
`, "utf8");
|
|
12950
13287
|
process.stdout.write(`installed theme "${name}" -> ${dest}
|
|
12951
13288
|
`);
|
|
@@ -12959,7 +13296,7 @@ function removeTheme(args) {
|
|
|
12959
13296
|
if (BUNDLED_NAMES.includes(name)) {
|
|
12960
13297
|
fail3(`"${name}" is a built-in theme and cannot be removed`);
|
|
12961
13298
|
}
|
|
12962
|
-
const dest =
|
|
13299
|
+
const dest = join10(userThemesDir(), `${name}.json`);
|
|
12963
13300
|
if (!existsSync8(dest)) {
|
|
12964
13301
|
fail3(`no user theme named "${name}" (looked for ${dest})`);
|
|
12965
13302
|
}
|
|
@@ -13029,7 +13366,7 @@ __export(exports_feedback_cmd, {
|
|
|
13029
13366
|
runFeedbackSubcommand: () => runFeedbackSubcommand,
|
|
13030
13367
|
parseFeedbackArgs: () => parseFeedbackArgs
|
|
13031
13368
|
});
|
|
13032
|
-
import { readFileSync as
|
|
13369
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
13033
13370
|
function usageError2(message) {
|
|
13034
13371
|
process.stderr.write(`kobe feedback: ${message}
|
|
13035
13372
|
|
|
@@ -13039,8 +13376,8 @@ ${FEEDBACK_USAGE}
|
|
|
13039
13376
|
}
|
|
13040
13377
|
function readBodyFile(path11) {
|
|
13041
13378
|
if (path11 === "-")
|
|
13042
|
-
return
|
|
13043
|
-
return
|
|
13379
|
+
return readFileSync11(0, "utf8");
|
|
13380
|
+
return readFileSync11(path11, "utf8");
|
|
13044
13381
|
}
|
|
13045
13382
|
function parseFeedbackArgs(args) {
|
|
13046
13383
|
const parsed = { help: false };
|
|
@@ -13120,9 +13457,9 @@ var init_feedback_cmd = __esm(() => {
|
|
|
13120
13457
|
});
|
|
13121
13458
|
|
|
13122
13459
|
// src/core/index.ts
|
|
13123
|
-
import { homedir as
|
|
13460
|
+
import { homedir as homedir16 } from "os";
|
|
13124
13461
|
async function createKobeCore(options = {}) {
|
|
13125
|
-
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
13462
|
+
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir16();
|
|
13126
13463
|
const store = new TaskIndexStore({ homeDir: homeDir2 });
|
|
13127
13464
|
await store.load();
|
|
13128
13465
|
const worktrees = new GitWorktreeManager;
|
|
@@ -13191,6 +13528,8 @@ async function runDaemonSubcommand(argv) {
|
|
|
13191
13528
|
try {
|
|
13192
13529
|
await client.request("daemon.stop");
|
|
13193
13530
|
console.log("kobe daemon: stop requested");
|
|
13531
|
+
} catch {
|
|
13532
|
+
console.log(`kobe daemon: no daemon running at ${socketPath}`);
|
|
13194
13533
|
} finally {
|
|
13195
13534
|
client.close();
|
|
13196
13535
|
}
|
|
@@ -13239,9 +13578,9 @@ var init_daemon_cmd = __esm(() => {
|
|
|
13239
13578
|
});
|
|
13240
13579
|
|
|
13241
13580
|
// src/lib/skill-install.ts
|
|
13242
|
-
import { existsSync as existsSync9, readFileSync as
|
|
13243
|
-
import { homedir as
|
|
13244
|
-
import { join as
|
|
13581
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
|
|
13582
|
+
import { homedir as homedir17 } from "os";
|
|
13583
|
+
import { join as join11 } from "path";
|
|
13245
13584
|
function npxSkillsArgv(opts = {}) {
|
|
13246
13585
|
return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
|
|
13247
13586
|
}
|
|
@@ -13249,9 +13588,9 @@ function npxSkillsCommand(opts = {}) {
|
|
|
13249
13588
|
return `npx ${npxSkillsArgv(opts).join(" ")}`;
|
|
13250
13589
|
}
|
|
13251
13590
|
function kobeSkillPaths(opts = {}) {
|
|
13252
|
-
const home = opts.home ??
|
|
13591
|
+
const home = opts.home ?? homedir17();
|
|
13253
13592
|
const cwd = opts.cwd ?? process.cwd();
|
|
13254
|
-
return [
|
|
13593
|
+
return [join11(home, SKILL_REL_PATH), join11(cwd, SKILL_REL_PATH)];
|
|
13255
13594
|
}
|
|
13256
13595
|
function parseSkillVersion(content) {
|
|
13257
13596
|
const m = content.match(/kobe-skill-version:\s*(\d+)/);
|
|
@@ -13264,7 +13603,7 @@ function kobeSkillState(opts) {
|
|
|
13264
13603
|
}
|
|
13265
13604
|
let installedVersion = null;
|
|
13266
13605
|
try {
|
|
13267
|
-
installedVersion = parseSkillVersion(
|
|
13606
|
+
installedVersion = parseSkillVersion(readFileSync12(path11, "utf8"));
|
|
13268
13607
|
} catch {
|
|
13269
13608
|
installedVersion = null;
|
|
13270
13609
|
}
|
|
@@ -13298,7 +13637,7 @@ kobe: your kobe agent skill is out of date (${was}; this kobe wants v${state.cur
|
|
|
13298
13637
|
`);
|
|
13299
13638
|
}
|
|
13300
13639
|
}
|
|
13301
|
-
var KOBE_SKILL_VERSION =
|
|
13640
|
+
var KOBE_SKILL_VERSION = 2, SKILL_REL_PATH = ".claude/skills/kobe/SKILL.md", SKILL_INSTALL_COMMAND = "kobe skill install", SKILL_SOURCE_SLUG = "Sma1lboy/kobe", DEFAULT_SKILL_AGENT = "claude-code", HINT_SEEN_KEY = "skillHintSeen";
|
|
13302
13641
|
var init_skill_install = __esm(() => {
|
|
13303
13642
|
init_repos();
|
|
13304
13643
|
});
|
|
@@ -13310,9 +13649,9 @@ __export(exports_maintenance, {
|
|
|
13310
13649
|
runReloadSubcommand: () => runReloadSubcommand,
|
|
13311
13650
|
runDoctorSubcommand: () => runDoctorSubcommand
|
|
13312
13651
|
});
|
|
13313
|
-
import { existsSync as existsSync10, readFileSync as
|
|
13652
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
|
|
13314
13653
|
import { unlink as unlink6 } from "fs/promises";
|
|
13315
|
-
import { join as
|
|
13654
|
+
import { join as join12 } from "path";
|
|
13316
13655
|
import { createInterface as createInterface2 } from "readline";
|
|
13317
13656
|
function isProcessAlive2(pid) {
|
|
13318
13657
|
try {
|
|
@@ -13359,7 +13698,7 @@ function describeFile(path11) {
|
|
|
13359
13698
|
}
|
|
13360
13699
|
function taskCount(tasksPath) {
|
|
13361
13700
|
try {
|
|
13362
|
-
const parsed = JSON.parse(
|
|
13701
|
+
const parsed = JSON.parse(readFileSync13(tasksPath, "utf8"));
|
|
13363
13702
|
return Array.isArray(parsed.tasks) ? parsed.tasks.length : null;
|
|
13364
13703
|
} catch {
|
|
13365
13704
|
return null;
|
|
@@ -13367,7 +13706,7 @@ function taskCount(tasksPath) {
|
|
|
13367
13706
|
}
|
|
13368
13707
|
function tailFile(path11, n) {
|
|
13369
13708
|
try {
|
|
13370
|
-
const lines =
|
|
13709
|
+
const lines = readFileSync13(path11, "utf8").split(`
|
|
13371
13710
|
`).filter((l) => l.trim().length > 0);
|
|
13372
13711
|
return lines.slice(-n).join(`
|
|
13373
13712
|
`);
|
|
@@ -13409,7 +13748,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
13409
13748
|
const socketPath = defaultDaemonSocketPath();
|
|
13410
13749
|
const pidPath = defaultDaemonPidPath();
|
|
13411
13750
|
const logPath = defaultDaemonLogPath();
|
|
13412
|
-
const tasksPath =
|
|
13751
|
+
const tasksPath = join12(kobeStateDir(), "tasks.json");
|
|
13413
13752
|
const statePath2 = kvStatePath();
|
|
13414
13753
|
const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
|
|
13415
13754
|
const status = await probeDaemonStatus(socketPath);
|
|
@@ -13476,7 +13815,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
13476
13815
|
async function confirmTty(prompt) {
|
|
13477
13816
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
13478
13817
|
try {
|
|
13479
|
-
const answer = await new Promise((
|
|
13818
|
+
const answer = await new Promise((resolve7) => rl.question(prompt, resolve7));
|
|
13480
13819
|
return /^y(es)?$/i.test(answer.trim());
|
|
13481
13820
|
} finally {
|
|
13482
13821
|
rl.close();
|
|
@@ -13528,7 +13867,7 @@ async function runResetSubcommand(argv) {
|
|
|
13528
13867
|
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
13529
13868
|
const socketPath = defaultDaemonSocketPath();
|
|
13530
13869
|
const pidPath = defaultDaemonPidPath();
|
|
13531
|
-
const tasksPath =
|
|
13870
|
+
const tasksPath = join12(kobeStateDir(), "tasks.json");
|
|
13532
13871
|
const statePath2 = kvStatePath();
|
|
13533
13872
|
console.log("kobe reset will:");
|
|
13534
13873
|
console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
|
|
@@ -13632,6 +13971,22 @@ var init_maintenance = __esm(() => {
|
|
|
13632
13971
|
init_version();
|
|
13633
13972
|
});
|
|
13634
13973
|
|
|
13974
|
+
// src/tui/lib/editor-prefs.ts
|
|
13975
|
+
function normalizeEditorKind(value) {
|
|
13976
|
+
return EDITOR_KINDS.includes(value) ? value : DEFAULT_EDITOR_KIND;
|
|
13977
|
+
}
|
|
13978
|
+
var EDITOR_KINDS, AUTO_EDITOR_CANDIDATES, EDITOR_KIND_KEY = "editor.kind", EDITOR_CUSTOM_KEY = "editor.customCommand", DEFAULT_EDITOR_KIND = "auto";
|
|
13979
|
+
var init_editor_prefs = __esm(() => {
|
|
13980
|
+
EDITOR_KINDS = ["auto", "vim", "nvim", "nano", "emacs", "custom"];
|
|
13981
|
+
AUTO_EDITOR_CANDIDATES = ["nvim", "vim", "emacs", "nano"];
|
|
13982
|
+
});
|
|
13983
|
+
|
|
13984
|
+
// src/tui/lib/settings-surface.ts
|
|
13985
|
+
function normalizeSettingsSurface(value) {
|
|
13986
|
+
return value === "taskpanel" ? "taskpanel" : "chattab";
|
|
13987
|
+
}
|
|
13988
|
+
var SETTINGS_SURFACE_KEY = "settings.surface", DEFAULT_SETTINGS_SURFACE = "chattab";
|
|
13989
|
+
|
|
13635
13990
|
// src/web/diff.ts
|
|
13636
13991
|
async function runGit(cwd, args) {
|
|
13637
13992
|
try {
|
|
@@ -13748,8 +14103,8 @@ async function handleDiffRequest(req, url) {
|
|
|
13748
14103
|
return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
|
|
13749
14104
|
}
|
|
13750
14105
|
try {
|
|
13751
|
-
const
|
|
13752
|
-
if (!
|
|
14106
|
+
const stat5 = await Bun.file(worktreePath).stat();
|
|
14107
|
+
if (!stat5.isDirectory()) {
|
|
13753
14108
|
return Response.json({ error: "worktreePath is not a directory" }, { status: 400 });
|
|
13754
14109
|
}
|
|
13755
14110
|
} catch {
|
|
@@ -13817,7 +14172,7 @@ async function handleDiffRequest(req, url) {
|
|
|
13817
14172
|
var GIT_TIMEOUT_MS = 15000, UNTRACKED_DIFF_CONCURRENCY = 8;
|
|
13818
14173
|
|
|
13819
14174
|
// src/web/history.ts
|
|
13820
|
-
import { isAbsolute } from "path";
|
|
14175
|
+
import { isAbsolute as isAbsolute2 } from "path";
|
|
13821
14176
|
function isSafeVendor(value) {
|
|
13822
14177
|
return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9_-]+$/.test(value);
|
|
13823
14178
|
}
|
|
@@ -13827,7 +14182,7 @@ function isSafeSessionId(value) {
|
|
|
13827
14182
|
async function handleSessions(url) {
|
|
13828
14183
|
const worktreePath = url.searchParams.get("worktreePath");
|
|
13829
14184
|
const vendor = url.searchParams.get("vendor") ?? "claude";
|
|
13830
|
-
if (!worktreePath || !
|
|
14185
|
+
if (!worktreePath || !isAbsolute2(worktreePath)) {
|
|
13831
14186
|
return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
|
|
13832
14187
|
}
|
|
13833
14188
|
if (!isSafeVendor(vendor)) {
|
|
@@ -13873,16 +14228,16 @@ var init_history4 = __esm(() => {
|
|
|
13873
14228
|
});
|
|
13874
14229
|
|
|
13875
14230
|
// src/web/notes.ts
|
|
13876
|
-
import { mkdir as
|
|
13877
|
-
import { join as
|
|
14231
|
+
import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
|
|
14232
|
+
import { join as join13 } from "path";
|
|
13878
14233
|
function notesDir() {
|
|
13879
|
-
return
|
|
14234
|
+
return join13(kobeStateDir(), "notes");
|
|
13880
14235
|
}
|
|
13881
14236
|
function isSafeTaskId(taskId) {
|
|
13882
14237
|
return typeof taskId === "string" && taskId.length > 0 && /^[A-Za-z0-9_-]+$/.test(taskId);
|
|
13883
14238
|
}
|
|
13884
14239
|
function noteFilePath(taskId) {
|
|
13885
|
-
return
|
|
14240
|
+
return join13(notesDir(), `${taskId}.md`);
|
|
13886
14241
|
}
|
|
13887
14242
|
async function handleGet(url) {
|
|
13888
14243
|
const taskId = url.searchParams.get("taskId");
|
|
@@ -13892,7 +14247,7 @@ async function handleGet(url) {
|
|
|
13892
14247
|
try {
|
|
13893
14248
|
let markdown = "";
|
|
13894
14249
|
try {
|
|
13895
|
-
markdown = await
|
|
14250
|
+
markdown = await readFile9(noteFilePath(taskId), "utf8");
|
|
13896
14251
|
} catch (err) {
|
|
13897
14252
|
if (err.code !== "ENOENT")
|
|
13898
14253
|
throw err;
|
|
@@ -13916,8 +14271,8 @@ async function handlePut(req) {
|
|
|
13916
14271
|
return Response.json({ error: "markdown must be a string" }, { status: 400 });
|
|
13917
14272
|
}
|
|
13918
14273
|
try {
|
|
13919
|
-
await
|
|
13920
|
-
await
|
|
14274
|
+
await mkdir8(notesDir(), { recursive: true });
|
|
14275
|
+
await writeFile6(noteFilePath(body.taskId), body.markdown, "utf8");
|
|
13921
14276
|
return Response.json({ ok: true });
|
|
13922
14277
|
} catch (err) {
|
|
13923
14278
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
@@ -14020,17 +14375,37 @@ var init_themes = __esm(() => {
|
|
|
14020
14375
|
WEB_THEMES = Object.fromEntries(Object.entries(THEME_JSONS).map(([name, json]) => [name, toWebPalette(json)]).filter((entry) => entry[1] !== null));
|
|
14021
14376
|
});
|
|
14022
14377
|
|
|
14378
|
+
// ../kobe-web/src/lib/repo-key.ts
|
|
14379
|
+
function normalizeRepoPath(path11) {
|
|
14380
|
+
return path11.length > 1 ? path11.replace(/\/+$/, "") : path11;
|
|
14381
|
+
}
|
|
14382
|
+
function repoSnapshotAliases(tasks, repoRoot) {
|
|
14383
|
+
const root = normalizeRepoPath(repoRoot);
|
|
14384
|
+
const aliases = new Set([repoRoot]);
|
|
14385
|
+
for (const task of tasks) {
|
|
14386
|
+
const taskRepo = normalizeRepoPath(task.repo);
|
|
14387
|
+
const taskWorktree = normalizeRepoPath(task.worktreePath);
|
|
14388
|
+
if (taskRepo === root || taskWorktree === root) {
|
|
14389
|
+
if (task.repo)
|
|
14390
|
+
aliases.add(task.repo);
|
|
14391
|
+
if (task.worktreePath)
|
|
14392
|
+
aliases.add(task.worktreePath);
|
|
14393
|
+
}
|
|
14394
|
+
}
|
|
14395
|
+
return [...aliases];
|
|
14396
|
+
}
|
|
14397
|
+
|
|
14023
14398
|
// ../kobe-web/server/spa-channels.ts
|
|
14024
14399
|
var SPA_CHANNELS, SPA_CHANNEL_SET;
|
|
14025
14400
|
var init_spa_channels = __esm(() => {
|
|
14026
14401
|
SPA_CHANNELS = [
|
|
14027
14402
|
"task.snapshot",
|
|
14403
|
+
"issue.snapshot",
|
|
14028
14404
|
"active-task",
|
|
14029
14405
|
"engine-state",
|
|
14030
14406
|
"update",
|
|
14031
14407
|
"task.jobs",
|
|
14032
14408
|
"worktree.changes",
|
|
14033
|
-
"task.conflicts",
|
|
14034
14409
|
"session.deliver",
|
|
14035
14410
|
"ui-prefs"
|
|
14036
14411
|
];
|
|
@@ -14039,7 +14414,7 @@ var init_spa_channels = __esm(() => {
|
|
|
14039
14414
|
|
|
14040
14415
|
// ../kobe-web/server/daemon-link.ts
|
|
14041
14416
|
function sleep2(ms) {
|
|
14042
|
-
return new Promise((
|
|
14417
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
14043
14418
|
}
|
|
14044
14419
|
|
|
14045
14420
|
class DaemonLink {
|
|
@@ -14055,7 +14430,7 @@ class DaemonLink {
|
|
|
14055
14430
|
update = null;
|
|
14056
14431
|
jobs = {};
|
|
14057
14432
|
worktreeChanges = {};
|
|
14058
|
-
|
|
14433
|
+
issueSnapshots = {};
|
|
14059
14434
|
deliver = null;
|
|
14060
14435
|
uiPrefs = null;
|
|
14061
14436
|
async start() {
|
|
@@ -14069,7 +14444,7 @@ class DaemonLink {
|
|
|
14069
14444
|
update: this.update,
|
|
14070
14445
|
jobs: this.jobs,
|
|
14071
14446
|
worktreeChanges: this.worktreeChanges,
|
|
14072
|
-
|
|
14447
|
+
issueSnapshots: this.issueSnapshots,
|
|
14073
14448
|
deliver: this.deliver,
|
|
14074
14449
|
uiPrefs: this.uiPrefs,
|
|
14075
14450
|
connected: this.connected
|
|
@@ -14181,9 +14556,15 @@ class DaemonLink {
|
|
|
14181
14556
|
case "worktree.changes":
|
|
14182
14557
|
this.worktreeChanges = payload.changes;
|
|
14183
14558
|
break;
|
|
14184
|
-
case "
|
|
14185
|
-
|
|
14559
|
+
case "issue.snapshot": {
|
|
14560
|
+
const state = payload;
|
|
14561
|
+
const next = { ...this.issueSnapshots };
|
|
14562
|
+
for (const alias of repoSnapshotAliases(this.tasks, state.repoRoot)) {
|
|
14563
|
+
next[alias] = { ...state, repoRoot: alias };
|
|
14564
|
+
}
|
|
14565
|
+
this.issueSnapshots = next;
|
|
14186
14566
|
break;
|
|
14567
|
+
}
|
|
14187
14568
|
case "session.deliver":
|
|
14188
14569
|
this.deliver = payload;
|
|
14189
14570
|
break;
|
|
@@ -14218,6 +14599,169 @@ var init_daemon_link = __esm(() => {
|
|
|
14218
14599
|
init_spa_channels();
|
|
14219
14600
|
});
|
|
14220
14601
|
|
|
14602
|
+
// ../kobe-web/server/issue-assets-route.ts
|
|
14603
|
+
import { createHash as createHash6, randomUUID as randomUUID3 } from "crypto";
|
|
14604
|
+
import { mkdir as mkdir9 } from "fs/promises";
|
|
14605
|
+
import { join as join14, resolve as resolve7 } from "path";
|
|
14606
|
+
function repoHashOf(repoRoot) {
|
|
14607
|
+
return createHash6("sha1").update(repoRoot).digest("hex").slice(0, 16);
|
|
14608
|
+
}
|
|
14609
|
+
async function handlePost(req) {
|
|
14610
|
+
const declared = Number.parseInt(req.headers.get("content-length") ?? "", 10);
|
|
14611
|
+
if (Number.isFinite(declared) && declared > MAX_ASSET_BYTES) {
|
|
14612
|
+
return Response.json({ error: "asset too large" }, { status: 413 });
|
|
14613
|
+
}
|
|
14614
|
+
let form;
|
|
14615
|
+
try {
|
|
14616
|
+
form = await req.formData();
|
|
14617
|
+
} catch {
|
|
14618
|
+
return Response.json({ error: "invalid form data" }, { status: 400 });
|
|
14619
|
+
}
|
|
14620
|
+
const repoRoot = form.get("repoRoot");
|
|
14621
|
+
if (typeof repoRoot !== "string" || repoRoot.length === 0) {
|
|
14622
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14623
|
+
}
|
|
14624
|
+
const file = form.get("file");
|
|
14625
|
+
if (!(file instanceof File)) {
|
|
14626
|
+
return Response.json({ error: "file must be a File" }, { status: 400 });
|
|
14627
|
+
}
|
|
14628
|
+
if (file.size > MAX_ASSET_BYTES) {
|
|
14629
|
+
return Response.json({ error: "asset too large" }, { status: 413 });
|
|
14630
|
+
}
|
|
14631
|
+
const ext = CONTENT_TYPE_EXT[file.type];
|
|
14632
|
+
if (!ext) {
|
|
14633
|
+
return Response.json({ error: `unsupported content-type: ${file.type || "unknown"}` }, { status: 415 });
|
|
14634
|
+
}
|
|
14635
|
+
try {
|
|
14636
|
+
const repoHash = repoHashOf(repoRoot);
|
|
14637
|
+
const dir = join14(issueAssetsDir(), repoHash);
|
|
14638
|
+
await mkdir9(dir, { recursive: true });
|
|
14639
|
+
const assetId = randomUUID3();
|
|
14640
|
+
const name = `${assetId}.${ext}`;
|
|
14641
|
+
await Bun.write(join14(dir, name), file);
|
|
14642
|
+
return Response.json({ url: `${ASSETS_ROUTE}/${repoHash}/${name}` });
|
|
14643
|
+
} catch (err) {
|
|
14644
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
14645
|
+
}
|
|
14646
|
+
}
|
|
14647
|
+
async function handleGet2(pathname) {
|
|
14648
|
+
const rest = pathname.slice(ASSETS_ROUTE.length + 1);
|
|
14649
|
+
const slash = rest.indexOf("/");
|
|
14650
|
+
if (slash < 0)
|
|
14651
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
14652
|
+
const repoHash = rest.slice(0, slash);
|
|
14653
|
+
const fileSeg = rest.slice(slash + 1);
|
|
14654
|
+
if (!REPO_HASH_RE.test(repoHash) || !ASSET_FILE_RE.test(fileSeg)) {
|
|
14655
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14656
|
+
}
|
|
14657
|
+
const root = issueAssetsDir();
|
|
14658
|
+
const resolved = resolve7(root, repoHash, fileSeg);
|
|
14659
|
+
if (resolved !== join14(root, repoHash, fileSeg) || !resolved.startsWith(`${root}/`)) {
|
|
14660
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14661
|
+
}
|
|
14662
|
+
const ext = fileSeg.slice(fileSeg.lastIndexOf(".") + 1).toLowerCase();
|
|
14663
|
+
const contentType = EXT_CONTENT_TYPE[ext];
|
|
14664
|
+
if (!contentType)
|
|
14665
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14666
|
+
const file = Bun.file(resolved);
|
|
14667
|
+
if (!await file.exists())
|
|
14668
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
14669
|
+
return new Response(file, {
|
|
14670
|
+
headers: {
|
|
14671
|
+
"content-type": contentType,
|
|
14672
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
14673
|
+
"x-content-type-options": "nosniff"
|
|
14674
|
+
}
|
|
14675
|
+
});
|
|
14676
|
+
}
|
|
14677
|
+
async function handleIssueAssetsRequest(req, url) {
|
|
14678
|
+
if (url.pathname === ASSETS_ROUTE) {
|
|
14679
|
+
if (req.method === "POST")
|
|
14680
|
+
return handlePost(req);
|
|
14681
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14682
|
+
}
|
|
14683
|
+
if (url.pathname.startsWith(`${ASSETS_ROUTE}/`)) {
|
|
14684
|
+
if (req.method === "GET")
|
|
14685
|
+
return handleGet2(url.pathname);
|
|
14686
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14687
|
+
}
|
|
14688
|
+
return null;
|
|
14689
|
+
}
|
|
14690
|
+
var ASSETS_ROUTE = "/api/issue-assets", MAX_ASSET_BYTES, CONTENT_TYPE_EXT, EXT_CONTENT_TYPE, REPO_HASH_RE, ASSET_FILE_RE;
|
|
14691
|
+
var init_issue_assets_route = __esm(() => {
|
|
14692
|
+
init_env();
|
|
14693
|
+
MAX_ASSET_BYTES = 10 * 1024 * 1024;
|
|
14694
|
+
CONTENT_TYPE_EXT = {
|
|
14695
|
+
"image/png": "png",
|
|
14696
|
+
"image/jpeg": "jpg",
|
|
14697
|
+
"image/gif": "gif",
|
|
14698
|
+
"image/webp": "webp"
|
|
14699
|
+
};
|
|
14700
|
+
EXT_CONTENT_TYPE = {
|
|
14701
|
+
png: "image/png",
|
|
14702
|
+
jpg: "image/jpeg",
|
|
14703
|
+
jpeg: "image/jpeg",
|
|
14704
|
+
gif: "image/gif",
|
|
14705
|
+
webp: "image/webp"
|
|
14706
|
+
};
|
|
14707
|
+
REPO_HASH_RE = /^[a-f0-9]{16}$/;
|
|
14708
|
+
ASSET_FILE_RE = /^[A-Za-z0-9_-]+\.[a-z0-9]+$/;
|
|
14709
|
+
});
|
|
14710
|
+
|
|
14711
|
+
// ../kobe-web/server/issues-route.ts
|
|
14712
|
+
function errorResponse(err, status = 500) {
|
|
14713
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status });
|
|
14714
|
+
}
|
|
14715
|
+
function statusForIssueError(err) {
|
|
14716
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14717
|
+
if (/^no issue #\d+$/.test(message))
|
|
14718
|
+
return 404;
|
|
14719
|
+
if (message === "repoRoot is required" || message === "repoRoot does not exist" || message === "repoRoot is not a git repository" || message === "missing op" || message === "create requires a non-empty title" || message === "body must be a string" || message === "setStatus requires a numeric id" || message.startsWith("invalid status:") || message === "update requires a numeric id" || message === "title must be a non-empty string" || message === "link requires a numeric id" || message === "link requires a non-empty taskId" || message === "unlink requires a numeric id" || message === "delete requires a numeric id" || message.startsWith("unknown op type:")) {
|
|
14720
|
+
return 400;
|
|
14721
|
+
}
|
|
14722
|
+
return 500;
|
|
14723
|
+
}
|
|
14724
|
+
async function handleGet3(link, url) {
|
|
14725
|
+
const repoRoot = url.searchParams.get("repoRoot");
|
|
14726
|
+
if (!repoRoot)
|
|
14727
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14728
|
+
try {
|
|
14729
|
+
return Response.json(await link.request("issue.list", { repoRoot }));
|
|
14730
|
+
} catch (err) {
|
|
14731
|
+
return errorResponse(err, statusForIssueError(err));
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
async function handlePost2(link, req) {
|
|
14735
|
+
let parsed;
|
|
14736
|
+
try {
|
|
14737
|
+
parsed = await req.json();
|
|
14738
|
+
} catch {
|
|
14739
|
+
return Response.json({ error: "invalid JSON body" }, { status: 400 });
|
|
14740
|
+
}
|
|
14741
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14742
|
+
return Response.json({ error: "body must be a JSON object" }, { status: 400 });
|
|
14743
|
+
}
|
|
14744
|
+
const body = parsed;
|
|
14745
|
+
if (typeof body.repoRoot !== "string" || body.repoRoot.length === 0) {
|
|
14746
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14747
|
+
}
|
|
14748
|
+
try {
|
|
14749
|
+
return Response.json(await link.request("issue.mutate", { repoRoot: body.repoRoot, op: body.op }));
|
|
14750
|
+
} catch (err) {
|
|
14751
|
+
return errorResponse(err, statusForIssueError(err));
|
|
14752
|
+
}
|
|
14753
|
+
}
|
|
14754
|
+
async function handleIssuesRequest(req, url, link) {
|
|
14755
|
+
if (url.pathname !== ISSUES_ROUTE)
|
|
14756
|
+
return null;
|
|
14757
|
+
if (req.method === "GET")
|
|
14758
|
+
return handleGet3(link, url);
|
|
14759
|
+
if (req.method === "POST")
|
|
14760
|
+
return handlePost2(link, req);
|
|
14761
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14762
|
+
}
|
|
14763
|
+
var ISSUES_ROUTE = "/api/issues";
|
|
14764
|
+
|
|
14221
14765
|
// ../kobe-web/server/rpc-allowlist.ts
|
|
14222
14766
|
var WEB_RPC_ALLOWLIST, WEB_RPC_ALLOWSET;
|
|
14223
14767
|
var init_rpc_allowlist = __esm(() => {
|
|
@@ -14266,7 +14810,7 @@ async function ensureTaskSession(link, taskId) {
|
|
|
14266
14810
|
const ok = await ensureSession({
|
|
14267
14811
|
name: session,
|
|
14268
14812
|
cwd: worktreePath,
|
|
14269
|
-
command: interactiveEngineCommand(task.vendor),
|
|
14813
|
+
command: interactiveEngineCommand(task.vendor, task.modelEffort),
|
|
14270
14814
|
taskId,
|
|
14271
14815
|
vendor: task.vendor,
|
|
14272
14816
|
initScript: init.initScript
|
|
@@ -14284,7 +14828,7 @@ async function engineSpec(link, taskId) {
|
|
|
14284
14828
|
const protocolTaskId = task.kind === "main" ? undefined : taskId;
|
|
14285
14829
|
const dispatcherTaskId = task.kind === "main" ? taskId : undefined;
|
|
14286
14830
|
const argv = [
|
|
14287
|
-
...withDispatcherProtocol(withWorktreeProtocol(interactiveEngineCommand(task.vendor), task.vendor, protocolTaskId), task.vendor, dispatcherTaskId)
|
|
14831
|
+
...withDispatcherProtocol(withWorktreeProtocol(interactiveEngineCommand(task.vendor, task.modelEffort), task.vendor, protocolTaskId), task.vendor, dispatcherTaskId)
|
|
14288
14832
|
];
|
|
14289
14833
|
const init = resolveRepoInit(task.repo ?? "", worktreePath);
|
|
14290
14834
|
const quoted = shellQuote2(argv);
|
|
@@ -14312,7 +14856,21 @@ var init_session = __esm(() => {
|
|
|
14312
14856
|
|
|
14313
14857
|
// ../kobe-web/server/bridge.ts
|
|
14314
14858
|
import { existsSync as existsSync11 } from "fs";
|
|
14315
|
-
import { join as
|
|
14859
|
+
import { join as join15, normalize as normalize2 } from "path";
|
|
14860
|
+
function originAllowed(req, allowedHost) {
|
|
14861
|
+
const origin = req.headers.get("origin");
|
|
14862
|
+
if (!origin)
|
|
14863
|
+
return true;
|
|
14864
|
+
if (LOCAL_ORIGIN.test(origin))
|
|
14865
|
+
return true;
|
|
14866
|
+
if (allowedHost) {
|
|
14867
|
+
try {
|
|
14868
|
+
if (new URL(origin).hostname === allowedHost)
|
|
14869
|
+
return true;
|
|
14870
|
+
} catch {}
|
|
14871
|
+
}
|
|
14872
|
+
return false;
|
|
14873
|
+
}
|
|
14316
14874
|
function sseResponse(register) {
|
|
14317
14875
|
let unregister = null;
|
|
14318
14876
|
let heartbeat = null;
|
|
@@ -14374,12 +14932,140 @@ async function rpcResponse(req, link, tearDown) {
|
|
|
14374
14932
|
async function enginesResponse() {
|
|
14375
14933
|
try {
|
|
14376
14934
|
const ids = await availableEngineIds();
|
|
14377
|
-
const engines = ids.map((id) => ({
|
|
14935
|
+
const engines = ids.map((id) => ({
|
|
14936
|
+
id,
|
|
14937
|
+
label: engineDisplayName(id),
|
|
14938
|
+
effortLevels: engineEntry(id).effortLevels
|
|
14939
|
+
}));
|
|
14378
14940
|
return Response.json({ engines: engines.length > 0 ? engines : [{ id: "claude", label: "Claude" }] });
|
|
14379
14941
|
} catch (err) {
|
|
14380
14942
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
14381
14943
|
}
|
|
14382
14944
|
}
|
|
14945
|
+
function cliInvocationResponse() {
|
|
14946
|
+
return Response.json({ api: kobeApiInvocation() });
|
|
14947
|
+
}
|
|
14948
|
+
function stringValue(value, fallback = "") {
|
|
14949
|
+
return typeof value === "string" ? value : fallback;
|
|
14950
|
+
}
|
|
14951
|
+
function boolValue(value, fallback) {
|
|
14952
|
+
return typeof value === "boolean" ? value : fallback;
|
|
14953
|
+
}
|
|
14954
|
+
function customEngineIdsFrom(state) {
|
|
14955
|
+
const raw = state.customEngineIds;
|
|
14956
|
+
return Array.isArray(raw) ? raw.filter((s) => typeof s === "string" && s.trim().length > 0) : [];
|
|
14957
|
+
}
|
|
14958
|
+
function humanizeSlug(id) {
|
|
14959
|
+
return id.split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
14960
|
+
}
|
|
14961
|
+
function engineCommandText(state, id) {
|
|
14962
|
+
const override = stringValue(state[engineCommandKey(id)]).trim();
|
|
14963
|
+
return override || defaultEngineCommand(id).join(" ");
|
|
14964
|
+
}
|
|
14965
|
+
function engineLabelText(state, id) {
|
|
14966
|
+
const override = stringValue(state[engineNameKey(id)]).trim();
|
|
14967
|
+
return override || engineDisplayName(id);
|
|
14968
|
+
}
|
|
14969
|
+
function settingsSnapshot() {
|
|
14970
|
+
const state = loadStateFile();
|
|
14971
|
+
const custom = customEngineIdsFrom(state);
|
|
14972
|
+
const engineIds = [...BUILTIN_VENDORS, ...custom];
|
|
14973
|
+
const defaultEngine = stringValue(state.lastSelectedVendor, "claude");
|
|
14974
|
+
const focusAccent = stringValue(state.focusAccent, "primary");
|
|
14975
|
+
return Response.json({
|
|
14976
|
+
activeTheme: stringValue(state.activeTheme, "claude"),
|
|
14977
|
+
transparentBackground: boolValue(state.transparentBackground, false),
|
|
14978
|
+
focusAccent: FOCUS_ACCENTS.includes(focusAccent) ? focusAccent : "primary",
|
|
14979
|
+
notificationsToast: state["notifications.toast.enabled"] !== false,
|
|
14980
|
+
notificationsSound: state["notifications.sound.enabled"] !== false,
|
|
14981
|
+
settingsSurface: normalizeSettingsSurface(state[SETTINGS_SURFACE_KEY] ?? DEFAULT_SETTINGS_SURFACE),
|
|
14982
|
+
editorKind: normalizeEditorKind(state[EDITOR_KIND_KEY] ?? DEFAULT_EDITOR_KIND),
|
|
14983
|
+
editorCustomCommand: stringValue(state[EDITOR_CUSTOM_KEY]),
|
|
14984
|
+
remoteProjects: state["experimental.remoteProjects"] === true,
|
|
14985
|
+
autoStatus: state[AUTO_STATUS_KEY] === true,
|
|
14986
|
+
dispatcher: state[DISPATCHER_KEY] === true,
|
|
14987
|
+
defaultEngine,
|
|
14988
|
+
engines: engineIds.map((id) => ({
|
|
14989
|
+
id,
|
|
14990
|
+
label: engineLabelText(state, id),
|
|
14991
|
+
command: engineCommandText(state, id),
|
|
14992
|
+
isBuiltin: isBuiltinVendor(id),
|
|
14993
|
+
isCustom: !isBuiltinVendor(id),
|
|
14994
|
+
isDefault: id === defaultEngine
|
|
14995
|
+
}))
|
|
14996
|
+
});
|
|
14997
|
+
}
|
|
14998
|
+
function putIfString(patch, key, value) {
|
|
14999
|
+
if (typeof value === "string")
|
|
15000
|
+
patch[key] = value.trim();
|
|
15001
|
+
}
|
|
15002
|
+
function putIfBool(patch, key, value) {
|
|
15003
|
+
if (typeof value === "boolean")
|
|
15004
|
+
patch[key] = value;
|
|
15005
|
+
}
|
|
15006
|
+
async function settingsPatch(req) {
|
|
15007
|
+
try {
|
|
15008
|
+
const body = await req.json();
|
|
15009
|
+
const patch = {};
|
|
15010
|
+
putIfString(patch, "activeTheme", body.activeTheme);
|
|
15011
|
+
putIfBool(patch, "transparentBackground", body.transparentBackground);
|
|
15012
|
+
if (FOCUS_ACCENTS.includes(body.focusAccent)) {
|
|
15013
|
+
patch.focusAccent = body.focusAccent;
|
|
15014
|
+
}
|
|
15015
|
+
putIfBool(patch, "notifications.toast.enabled", body.notificationsToast);
|
|
15016
|
+
putIfBool(patch, "notifications.sound.enabled", body.notificationsSound);
|
|
15017
|
+
if (body.settingsSurface === "chattab" || body.settingsSurface === "taskpanel") {
|
|
15018
|
+
patch[SETTINGS_SURFACE_KEY] = body.settingsSurface;
|
|
15019
|
+
}
|
|
15020
|
+
if (EDITOR_KINDS.includes(body.editorKind))
|
|
15021
|
+
patch[EDITOR_KIND_KEY] = body.editorKind;
|
|
15022
|
+
putIfString(patch, EDITOR_CUSTOM_KEY, body.editorCustomCommand);
|
|
15023
|
+
putIfBool(patch, "experimental.remoteProjects", body.remoteProjects);
|
|
15024
|
+
putIfBool(patch, AUTO_STATUS_KEY, body.autoStatus);
|
|
15025
|
+
putIfBool(patch, DISPATCHER_KEY, body.dispatcher);
|
|
15026
|
+
putIfString(patch, "lastSelectedVendor", body.defaultEngine);
|
|
15027
|
+
const state = loadStateFile();
|
|
15028
|
+
const custom = customEngineIdsFrom(state);
|
|
15029
|
+
const known = new Set([...BUILTIN_VENDORS, ...custom]);
|
|
15030
|
+
const updates = Array.isArray(body.engineUpdates) ? body.engineUpdates : [];
|
|
15031
|
+
for (const raw of updates) {
|
|
15032
|
+
if (!raw || typeof raw !== "object")
|
|
15033
|
+
continue;
|
|
15034
|
+
const update = raw;
|
|
15035
|
+
if (typeof update.id !== "string" || !known.has(update.id))
|
|
15036
|
+
continue;
|
|
15037
|
+
putIfString(patch, engineCommandKey(update.id), update.command);
|
|
15038
|
+
putIfString(patch, engineNameKey(update.id), update.label);
|
|
15039
|
+
}
|
|
15040
|
+
if (body.addEngine && typeof body.addEngine === "object") {
|
|
15041
|
+
const add2 = body.addEngine;
|
|
15042
|
+
const id = typeof add2.id === "string" ? add2.id.trim().toLowerCase() : "";
|
|
15043
|
+
if (!ENGINE_ID_RE.test(id) || isBuiltinVendor(id) || known.has(id)) {
|
|
15044
|
+
return Response.json({ error: "invalid or duplicate engine id" }, { status: 400 });
|
|
15045
|
+
}
|
|
15046
|
+
const nextCustom = [...custom, id];
|
|
15047
|
+
patch.customEngineIds = nextCustom;
|
|
15048
|
+
patch[engineCommandKey(id)] = stringValue(add2.command).trim();
|
|
15049
|
+
const label = stringValue(add2.label).trim();
|
|
15050
|
+
patch[engineNameKey(id)] = label && label !== id ? label : humanizeSlug(id);
|
|
15051
|
+
}
|
|
15052
|
+
if (typeof body.removeEngine === "string") {
|
|
15053
|
+
const id = body.removeEngine;
|
|
15054
|
+
if (!isBuiltinVendor(id)) {
|
|
15055
|
+
patch.customEngineIds = custom.filter((engine) => engine !== id);
|
|
15056
|
+
patch[engineCommandKey(id)] = undefined;
|
|
15057
|
+
patch[engineNameKey(id)] = undefined;
|
|
15058
|
+
if (state.lastSelectedVendor === id)
|
|
15059
|
+
patch.lastSelectedVendor = "claude";
|
|
15060
|
+
}
|
|
15061
|
+
}
|
|
15062
|
+
if (Object.keys(patch).length > 0)
|
|
15063
|
+
patchStateFile(patch);
|
|
15064
|
+
return settingsSnapshot();
|
|
15065
|
+
} catch (err) {
|
|
15066
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });
|
|
15067
|
+
}
|
|
15068
|
+
}
|
|
14383
15069
|
async function sessionResponse(req, link) {
|
|
14384
15070
|
try {
|
|
14385
15071
|
const { taskId } = await req.json();
|
|
@@ -14407,6 +15093,9 @@ function createRequestHandler(deps) {
|
|
|
14407
15093
|
const url = new URL(req.url);
|
|
14408
15094
|
if (url.pathname === WEB_HEALTH_PATH)
|
|
14409
15095
|
return new Response(WEB_HEALTH_MARKER);
|
|
15096
|
+
if (!originAllowed(req, deps.allowedHost)) {
|
|
15097
|
+
return new Response("forbidden: cross-origin request rejected", { status: 403 });
|
|
15098
|
+
}
|
|
14410
15099
|
if (url.pathname === "/events") {
|
|
14411
15100
|
return sseResponse((send2) => {
|
|
14412
15101
|
send2("snapshot", link.snapshot());
|
|
@@ -14426,6 +15115,12 @@ function createRequestHandler(deps) {
|
|
|
14426
15115
|
return specResponse(url, link, terminalSpec);
|
|
14427
15116
|
if (url.pathname === "/api/engines" && req.method === "GET")
|
|
14428
15117
|
return enginesResponse();
|
|
15118
|
+
if (url.pathname === "/api/cli-invocation" && req.method === "GET")
|
|
15119
|
+
return cliInvocationResponse();
|
|
15120
|
+
if (url.pathname === "/api/settings" && req.method === "GET")
|
|
15121
|
+
return settingsSnapshot();
|
|
15122
|
+
if (url.pathname === "/api/settings" && req.method === "PATCH")
|
|
15123
|
+
return settingsPatch(req);
|
|
14429
15124
|
if (url.pathname === "/api/quick-prompts" && req.method === "GET")
|
|
14430
15125
|
return quickPromptsGet();
|
|
14431
15126
|
if (url.pathname === "/api/quick-prompts" && req.method === "PUT")
|
|
@@ -14439,6 +15134,12 @@ function createRequestHandler(deps) {
|
|
|
14439
15134
|
const history = await handleHistoryRequest(req, url);
|
|
14440
15135
|
if (history)
|
|
14441
15136
|
return history;
|
|
15137
|
+
const issues = await handleIssuesRequest(req, url, link);
|
|
15138
|
+
if (issues)
|
|
15139
|
+
return issues;
|
|
15140
|
+
const issueAssets = await handleIssueAssetsRequest(req, url);
|
|
15141
|
+
if (issueAssets)
|
|
15142
|
+
return issueAssets;
|
|
14442
15143
|
const themes = handleThemesRequest(req, url);
|
|
14443
15144
|
if (themes)
|
|
14444
15145
|
return themes;
|
|
@@ -14467,10 +15168,10 @@ async function quickPromptsPut(req) {
|
|
|
14467
15168
|
}
|
|
14468
15169
|
async function staticResponse(pathname, staticDir) {
|
|
14469
15170
|
const rel = pathname === "/" ? "/index.html" : pathname;
|
|
14470
|
-
const resolved = normalize2(
|
|
15171
|
+
const resolved = normalize2(join15(staticDir, rel));
|
|
14471
15172
|
if (!resolved.startsWith(staticDir))
|
|
14472
15173
|
return new Response("forbidden", { status: 403 });
|
|
14473
|
-
const file = Bun.file(existsSync11(resolved) ? resolved :
|
|
15174
|
+
const file = Bun.file(existsSync11(resolved) ? resolved : join15(staticDir, "index.html"));
|
|
14474
15175
|
if (!await file.exists()) {
|
|
14475
15176
|
return new Response("kobe web assets not built \u2014 run `bun --filter kobe-web build`", { status: 503 });
|
|
14476
15177
|
}
|
|
@@ -14531,8 +15232,9 @@ async function createBridgeServer(opts = {}) {
|
|
|
14531
15232
|
for (const send2 of sseSends)
|
|
14532
15233
|
send2("snapshot", link.snapshot());
|
|
14533
15234
|
});
|
|
14534
|
-
const handle = createRequestHandler({ link, sseSends, staticDir });
|
|
14535
15235
|
const hostname = process.env.KOBE_WEB_HOST?.trim() || "127.0.0.1";
|
|
15236
|
+
const allowedHost = LOCAL_ORIGIN.test(`http://${hostname}`) ? undefined : hostname;
|
|
15237
|
+
const handle = createRequestHandler({ link, sseSends, staticDir, allowedHost });
|
|
14536
15238
|
const server = Bun.serve({ port, hostname, idleTimeout: 0, fetch: handle });
|
|
14537
15239
|
return {
|
|
14538
15240
|
port: server.port ?? port,
|
|
@@ -14542,17 +15244,27 @@ async function createBridgeServer(opts = {}) {
|
|
|
14542
15244
|
}
|
|
14543
15245
|
};
|
|
14544
15246
|
}
|
|
14545
|
-
var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web", QUICK_PROMPT_KEYS;
|
|
15247
|
+
var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web", LOCAL_ORIGIN, FOCUS_ACCENTS, ENGINE_ID_RE, QUICK_PROMPT_KEYS;
|
|
14546
15248
|
var init_bridge = __esm(() => {
|
|
14547
15249
|
init_account_detect();
|
|
14548
15250
|
init_interactive_command();
|
|
15251
|
+
init_registry();
|
|
15252
|
+
init_auto_status();
|
|
15253
|
+
init_dispatcher();
|
|
14549
15254
|
init_repos();
|
|
15255
|
+
init_store();
|
|
15256
|
+
init_editor_prefs();
|
|
15257
|
+
init_vendor();
|
|
14550
15258
|
init_history4();
|
|
14551
15259
|
init_notes();
|
|
14552
15260
|
init_themes();
|
|
14553
15261
|
init_daemon_link();
|
|
15262
|
+
init_issue_assets_route();
|
|
14554
15263
|
init_rpc_allowlist();
|
|
14555
15264
|
init_session();
|
|
15265
|
+
LOCAL_ORIGIN = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
|
|
15266
|
+
FOCUS_ACCENTS = ["primary", "success", "info"];
|
|
15267
|
+
ENGINE_ID_RE = /^[a-z][a-z0-9_-]{0,47}$/;
|
|
14556
15268
|
QUICK_PROMPT_KEYS = {
|
|
14557
15269
|
review: "boardPrompt.review",
|
|
14558
15270
|
pr: "boardPrompt.pr"
|
|
@@ -14570,18 +15282,18 @@ __export(exports_web_cmd, {
|
|
|
14570
15282
|
runWebSubcommand: () => runWebSubcommand
|
|
14571
15283
|
});
|
|
14572
15284
|
import { existsSync as existsSync12 } from "fs";
|
|
14573
|
-
import { homedir as
|
|
14574
|
-
import { resolve as
|
|
15285
|
+
import { homedir as homedir18 } from "os";
|
|
15286
|
+
import { resolve as resolve8 } from "path";
|
|
14575
15287
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14576
15288
|
function homeLabel() {
|
|
14577
15289
|
const explicit = process.env.KOBE_HOME_DIR?.trim();
|
|
14578
|
-
return explicit ? `sandbox: ${explicit}` : `${
|
|
15290
|
+
return explicit ? `sandbox: ${explicit}` : `${homedir18()}/.kobe (production)`;
|
|
14579
15291
|
}
|
|
14580
15292
|
function resolveStaticDir() {
|
|
14581
15293
|
const here = fileURLToPath3(import.meta.url);
|
|
14582
15294
|
const candidates = [
|
|
14583
|
-
|
|
14584
|
-
|
|
15295
|
+
resolve8(here, "../../../../kobe-web/dist"),
|
|
15296
|
+
resolve8(here, "../../web-ui")
|
|
14585
15297
|
];
|
|
14586
15298
|
for (const dir of candidates) {
|
|
14587
15299
|
if (existsSync12(`${dir}/index.html`))
|
|
@@ -14592,8 +15304,8 @@ function resolveStaticDir() {
|
|
|
14592
15304
|
function resolvePtyServer() {
|
|
14593
15305
|
const here = fileURLToPath3(import.meta.url);
|
|
14594
15306
|
const candidates = [
|
|
14595
|
-
|
|
14596
|
-
|
|
15307
|
+
resolve8(here, "../../../../kobe-web/pty-server.mjs"),
|
|
15308
|
+
resolve8(here, "../../web-ui/pty-server.mjs")
|
|
14597
15309
|
];
|
|
14598
15310
|
for (const file of candidates) {
|
|
14599
15311
|
if (existsSync12(file))
|
|
@@ -14855,15 +15567,15 @@ __export(exports_hook_cmd, {
|
|
|
14855
15567
|
parseWorktreeAddPath: () => parseWorktreeAddPath,
|
|
14856
15568
|
ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
|
|
14857
15569
|
});
|
|
14858
|
-
import { homedir as
|
|
14859
|
-
import { join as
|
|
15570
|
+
import { homedir as homedir19 } from "os";
|
|
15571
|
+
import { join as join16, resolve as resolve9 } from "path";
|
|
14860
15572
|
async function readTextWithTimeout(read, timeoutMs = STDIN_READ_TIMEOUT_MS) {
|
|
14861
15573
|
let raceTimer;
|
|
14862
15574
|
try {
|
|
14863
15575
|
return await Promise.race([
|
|
14864
15576
|
read(),
|
|
14865
|
-
new Promise((
|
|
14866
|
-
raceTimer = setTimeout(() =>
|
|
15577
|
+
new Promise((resolve10) => {
|
|
15578
|
+
raceTimer = setTimeout(() => resolve10(""), timeoutMs);
|
|
14867
15579
|
})
|
|
14868
15580
|
]);
|
|
14869
15581
|
} finally {
|
|
@@ -14932,7 +15644,7 @@ async function runWorktreeCreatedHook() {
|
|
|
14932
15644
|
if (!rawPath)
|
|
14933
15645
|
return;
|
|
14934
15646
|
const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
|
|
14935
|
-
const worktreePath =
|
|
15647
|
+
const worktreePath = resolve9(cwd, rawPath);
|
|
14936
15648
|
const client = await connectIfRunning();
|
|
14937
15649
|
if (!client)
|
|
14938
15650
|
return;
|
|
@@ -14987,7 +15699,7 @@ function activityHookAdapters() {
|
|
|
14987
15699
|
return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
|
|
14988
15700
|
}
|
|
14989
15701
|
function globalSettingsPath() {
|
|
14990
|
-
return
|
|
15702
|
+
return join16(homedir19(), ".claude", "settings.json");
|
|
14991
15703
|
}
|
|
14992
15704
|
function persistedSyncPath(stored) {
|
|
14993
15705
|
if (!stored || stored === "off")
|
|
@@ -14995,7 +15707,7 @@ function persistedSyncPath(stored) {
|
|
|
14995
15707
|
if (stored === "global")
|
|
14996
15708
|
return globalSettingsPath();
|
|
14997
15709
|
if (stored.startsWith("repo:"))
|
|
14998
|
-
return
|
|
15710
|
+
return join16(resolve9(stored.slice(5)), ".claude", "settings.json");
|
|
14999
15711
|
return stored;
|
|
15000
15712
|
}
|
|
15001
15713
|
async function ensureGlobalKobeHooks() {
|
|
@@ -16544,7 +17256,11 @@ class RemoteOrchestrator {
|
|
|
16544
17256
|
});
|
|
16545
17257
|
}
|
|
16546
17258
|
async createTask(input) {
|
|
16547
|
-
const
|
|
17259
|
+
const { modelEffort, ...rest } = input;
|
|
17260
|
+
const res = await this.client.request("task.create", {
|
|
17261
|
+
...rest,
|
|
17262
|
+
effort: modelEffort
|
|
17263
|
+
});
|
|
16548
17264
|
return deserializeTask(res.task);
|
|
16549
17265
|
}
|
|
16550
17266
|
async ensureMainTask(repo) {
|
|
@@ -16717,6 +17433,7 @@ function deserializeTask(s) {
|
|
|
16717
17433
|
pinned: s.pinned,
|
|
16718
17434
|
vendor: s.vendor,
|
|
16719
17435
|
prStatus: s.prStatus,
|
|
17436
|
+
modelEffort: s.modelEffort,
|
|
16720
17437
|
createdAt: s.createdAt,
|
|
16721
17438
|
updatedAt: s.updatedAt
|
|
16722
17439
|
};
|
|
@@ -16727,6 +17444,7 @@ var init_remote_orchestrator = __esm(() => {
|
|
|
16727
17444
|
init_protocol();
|
|
16728
17445
|
init_dev();
|
|
16729
17446
|
init_worktree_changes();
|
|
17447
|
+
init_task();
|
|
16730
17448
|
init_version();
|
|
16731
17449
|
});
|
|
16732
17450
|
|
|
@@ -17043,7 +17761,7 @@ function addTheme2(name, theme) {
|
|
|
17043
17761
|
}
|
|
17044
17762
|
function resolveTheme(theme, mode = "dark") {
|
|
17045
17763
|
const defs = theme.defs ?? {};
|
|
17046
|
-
function
|
|
17764
|
+
function resolve10(c, chain = []) {
|
|
17047
17765
|
if (typeof c === "string") {
|
|
17048
17766
|
if (c === "transparent" || c === "none")
|
|
17049
17767
|
return RGBA.fromInts(0, 0, 0, 0);
|
|
@@ -17055,13 +17773,13 @@ function resolveTheme(theme, mode = "dark") {
|
|
|
17055
17773
|
const next = defs[c] ?? theme.theme[c];
|
|
17056
17774
|
if (next === undefined)
|
|
17057
17775
|
return RGBA.fromInts(0, 0, 0);
|
|
17058
|
-
return
|
|
17776
|
+
return resolve10(next, [...chain, c]);
|
|
17059
17777
|
}
|
|
17060
|
-
return
|
|
17778
|
+
return resolve10(c[mode], chain);
|
|
17061
17779
|
}
|
|
17062
17780
|
const out = {};
|
|
17063
17781
|
for (const [k, v] of Object.entries(theme.theme)) {
|
|
17064
|
-
out[k] =
|
|
17782
|
+
out[k] = resolve10(v);
|
|
17065
17783
|
}
|
|
17066
17784
|
const text = out.text ?? RGBA.fromHex("#ffffff");
|
|
17067
17785
|
const background = out.background ?? RGBA.fromHex("#000000");
|
|
@@ -17538,13 +18256,13 @@ function validateRepoPath(repo) {
|
|
|
17538
18256
|
const trimmed = repo.trim();
|
|
17539
18257
|
if (!trimmed)
|
|
17540
18258
|
return "repo path is required";
|
|
17541
|
-
let
|
|
18259
|
+
let stat5;
|
|
17542
18260
|
try {
|
|
17543
|
-
|
|
18261
|
+
stat5 = fs3.statSync(trimmed);
|
|
17544
18262
|
} catch {
|
|
17545
18263
|
return `path does not exist: ${trimmed}`;
|
|
17546
18264
|
}
|
|
17547
|
-
if (!
|
|
18265
|
+
if (!stat5.isDirectory())
|
|
17548
18266
|
return `not a directory: ${trimmed}`;
|
|
17549
18267
|
try {
|
|
17550
18268
|
const out = spawnSync10("git", ["rev-parse", "--git-dir"], {
|
|
@@ -17726,8 +18444,8 @@ function findAvailableFolderName(parentDir, base) {
|
|
|
17726
18444
|
if (!parentExpanded)
|
|
17727
18445
|
return base;
|
|
17728
18446
|
try {
|
|
17729
|
-
const
|
|
17730
|
-
if (!
|
|
18447
|
+
const stat5 = fs5.statSync(parentExpanded);
|
|
18448
|
+
if (!stat5.isDirectory())
|
|
17731
18449
|
return base;
|
|
17732
18450
|
} catch {
|
|
17733
18451
|
return base;
|
|
@@ -17742,7 +18460,7 @@ function findAvailableFolderName(parentDir, base) {
|
|
|
17742
18460
|
return trimmed;
|
|
17743
18461
|
}
|
|
17744
18462
|
function cloneRepo(url, target, onProgress) {
|
|
17745
|
-
return new Promise((
|
|
18463
|
+
return new Promise((resolve10) => {
|
|
17746
18464
|
let stderrBuf = "";
|
|
17747
18465
|
try {
|
|
17748
18466
|
const child = spawn4("git", ["clone", "--progress", url, target], {
|
|
@@ -17759,18 +18477,18 @@ function cloneRepo(url, target, onProgress) {
|
|
|
17759
18477
|
}
|
|
17760
18478
|
});
|
|
17761
18479
|
child.on("error", (err) => {
|
|
17762
|
-
|
|
18480
|
+
resolve10({ ok: false, error: err.message });
|
|
17763
18481
|
});
|
|
17764
18482
|
child.on("close", (code) => {
|
|
17765
18483
|
if (code === 0) {
|
|
17766
|
-
|
|
18484
|
+
resolve10({ ok: true, path: target });
|
|
17767
18485
|
return;
|
|
17768
18486
|
}
|
|
17769
18487
|
const tail = stderrBuf.split(/[\r\n]+/).filter((s) => s.trim().length > 0).pop() ?? `git clone exited with ${code}`;
|
|
17770
|
-
|
|
18488
|
+
resolve10({ ok: false, error: tail });
|
|
17771
18489
|
});
|
|
17772
18490
|
} catch (err) {
|
|
17773
|
-
|
|
18491
|
+
resolve10({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
17774
18492
|
}
|
|
17775
18493
|
});
|
|
17776
18494
|
}
|
|
@@ -17872,7 +18590,7 @@ function clampCursor(cursor, listLength) {
|
|
|
17872
18590
|
return 0;
|
|
17873
18591
|
return Math.max(0, Math.min(listLength - 1, cursor));
|
|
17874
18592
|
}
|
|
17875
|
-
function
|
|
18593
|
+
function resolveBaseRef(typed, filteredBranches, cursor) {
|
|
17876
18594
|
const picked = filteredBranches[cursor];
|
|
17877
18595
|
if (picked)
|
|
17878
18596
|
return picked;
|
|
@@ -18446,7 +19164,7 @@ function NewTaskDialogView(props) {
|
|
|
18446
19164
|
setBaseRef(stripNewlines(v));
|
|
18447
19165
|
});
|
|
18448
19166
|
setProp(_el$28, "onSubmit", () => {
|
|
18449
|
-
setBaseRef(
|
|
19167
|
+
setBaseRef(resolveBaseRef(baseRef(), branchFiltered(), branchCursor()));
|
|
18450
19168
|
setBaseRefTouched(true);
|
|
18451
19169
|
setField("confirm");
|
|
18452
19170
|
});
|
|
@@ -18960,7 +19678,7 @@ var init_dialog2 = __esm(() => {
|
|
|
18960
19678
|
|
|
18961
19679
|
// src/tui/component/new-task-dialog/index.tsx
|
|
18962
19680
|
function show(dialog, defaultRepo, savedRepos, options) {
|
|
18963
|
-
return new Promise((
|
|
19681
|
+
return new Promise((resolve10) => {
|
|
18964
19682
|
dialog.replace(() => createComponent2(NewTaskDialogView, {
|
|
18965
19683
|
defaultRepo,
|
|
18966
19684
|
savedRepos,
|
|
@@ -18976,9 +19694,9 @@ function show(dialog, defaultRepo, savedRepos, options) {
|
|
|
18976
19694
|
get discoverAdoptable() {
|
|
18977
19695
|
return options?.discoverAdoptable;
|
|
18978
19696
|
},
|
|
18979
|
-
onSubmit: (v) =>
|
|
18980
|
-
onCancel: () =>
|
|
18981
|
-
}), () =>
|
|
19697
|
+
onSubmit: (v) => resolve10(v),
|
|
19698
|
+
onCancel: () => resolve10(undefined)
|
|
19699
|
+
}), () => resolve10(undefined));
|
|
18982
19700
|
dialog.setSize("medium");
|
|
18983
19701
|
});
|
|
18984
19702
|
}
|
|
@@ -19147,11 +19865,11 @@ function QuickTaskComposerView(props) {
|
|
|
19147
19865
|
})();
|
|
19148
19866
|
}
|
|
19149
19867
|
function show2(dialog, opts) {
|
|
19150
|
-
return new Promise((
|
|
19868
|
+
return new Promise((resolve10) => {
|
|
19151
19869
|
dialog.replace(() => createComponent2(QuickTaskComposerView, mergeProps3(opts, {
|
|
19152
|
-
onSubmit: (r) =>
|
|
19153
|
-
onCancel: () =>
|
|
19154
|
-
})), () =>
|
|
19870
|
+
onSubmit: (r) => resolve10(r),
|
|
19871
|
+
onCancel: () => resolve10(undefined)
|
|
19872
|
+
})), () => resolve10(undefined));
|
|
19155
19873
|
dialog.setSize("medium");
|
|
19156
19874
|
});
|
|
19157
19875
|
}
|
|
@@ -19920,9 +20638,9 @@ var pulse_default = "../pulse-n3cq1btw.wav";
|
|
|
19920
20638
|
var init_pulse = () => {};
|
|
19921
20639
|
|
|
19922
20640
|
// src/tui/lib/sound.ts
|
|
19923
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
20641
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
|
|
19924
20642
|
import { tmpdir as tmpdir2 } from "os";
|
|
19925
|
-
import { basename as basename6, isAbsolute as
|
|
20643
|
+
import { basename as basename6, isAbsolute as isAbsolute3, join as join18, resolve as resolve10 } from "path";
|
|
19926
20644
|
function args(player, file, volume) {
|
|
19927
20645
|
if (player === "ffplay")
|
|
19928
20646
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -19945,13 +20663,13 @@ function pickPlayer() {
|
|
|
19945
20663
|
return cachedPlayer;
|
|
19946
20664
|
const path12 = process.env.PATH ?? "";
|
|
19947
20665
|
const segments = path12.split(":").filter(Boolean);
|
|
19948
|
-
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(
|
|
20666
|
+
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join18(dir, p)))) ?? null;
|
|
19949
20667
|
return cachedPlayer;
|
|
19950
20668
|
}
|
|
19951
20669
|
async function ensureAsset() {
|
|
19952
20670
|
cachedPath ??= (async () => {
|
|
19953
|
-
|
|
19954
|
-
const dest =
|
|
20671
|
+
mkdirSync7(DIR, { recursive: true });
|
|
20672
|
+
const dest = join18(DIR, basename6(pulseAsset));
|
|
19955
20673
|
const out = Bun.file(dest);
|
|
19956
20674
|
if (await out.exists())
|
|
19957
20675
|
return dest;
|
|
@@ -19980,8 +20698,8 @@ function pulse(volume = 0.4) {
|
|
|
19980
20698
|
var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
19981
20699
|
var init_sound = __esm(() => {
|
|
19982
20700
|
init_pulse();
|
|
19983
|
-
pulseAsset =
|
|
19984
|
-
DIR =
|
|
20701
|
+
pulseAsset = isAbsolute3(pulse_default) ? pulse_default : resolve10(import.meta.dir, pulse_default);
|
|
20702
|
+
DIR = join18(tmpdir2(), "kobe-sfx");
|
|
19985
20703
|
PLAYERS = [
|
|
19986
20704
|
"ffplay",
|
|
19987
20705
|
"mpv",
|
|
@@ -20106,10 +20824,10 @@ var init_apply_ui_prefs = __esm(() => {
|
|
|
20106
20824
|
});
|
|
20107
20825
|
|
|
20108
20826
|
// src/tui/lib/persisted-ui-prefs.ts
|
|
20109
|
-
import { readFileSync as
|
|
20827
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
20110
20828
|
function readPersistedUiPrefs(fallbackTheme) {
|
|
20111
20829
|
try {
|
|
20112
|
-
const parsed = JSON.parse(
|
|
20830
|
+
const parsed = JSON.parse(readFileSync14(kvStatePath(), "utf8"));
|
|
20113
20831
|
const theme = typeof parsed.activeTheme === "string" && hasTheme(parsed.activeTheme) ? parsed.activeTheme : fallbackTheme;
|
|
20114
20832
|
const transparent = parsed.transparentBackground === true;
|
|
20115
20833
|
const focusAccent = typeof parsed.focusAccent === "string" && FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent) ? parsed.focusAccent : null;
|
|
@@ -20385,6 +21103,7 @@ var init_host = __esm(() => {
|
|
|
20385
21103
|
init_remote_orchestrator();
|
|
20386
21104
|
init_account_detect();
|
|
20387
21105
|
init_repos();
|
|
21106
|
+
init_task();
|
|
20388
21107
|
init_new_task_dialog();
|
|
20389
21108
|
init_theme2();
|
|
20390
21109
|
init_host_boot();
|
|
@@ -20566,6 +21285,10 @@ async function deliverFirstPromptToTask(orch, task, repo, vendor, prompt) {
|
|
|
20566
21285
|
async function jumpToTask(orch, task, repo, vendor) {
|
|
20567
21286
|
await ensureTaskSession2(orch, task, repo, vendor);
|
|
20568
21287
|
await orch.setActiveTask(task.id).catch(() => {});
|
|
21288
|
+
const {
|
|
21289
|
+
prepareWindowForSwitch: prepareWindowForSwitch2
|
|
21290
|
+
} = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
21291
|
+
await prepareWindowForSwitch2(tmuxSessionName(task.id));
|
|
20569
21292
|
await runTmux(["switch-client", "-t", `=${tmuxSessionName(task.id)}`]);
|
|
20570
21293
|
}
|
|
20571
21294
|
function QuickTaskPage(props) {
|
|
@@ -20660,6 +21383,7 @@ var init_host2 = __esm(() => {
|
|
|
20660
21383
|
init_repos();
|
|
20661
21384
|
init_client2();
|
|
20662
21385
|
init_prompt_delivery();
|
|
21386
|
+
init_task();
|
|
20663
21387
|
init_quick_task_composer();
|
|
20664
21388
|
init_theme2();
|
|
20665
21389
|
init_git_snapshot();
|
|
@@ -20999,7 +21723,7 @@ var init_dialog3 = __esm(() => {
|
|
|
20999
21723
|
|
|
21000
21724
|
// src/tui/component/rename-task-dialog/index.tsx
|
|
21001
21725
|
function show3(dialog, currentTitle, opts = {}) {
|
|
21002
|
-
return new Promise((
|
|
21726
|
+
return new Promise((resolve11) => {
|
|
21003
21727
|
dialog.replace(() => createComponent2(RenameTaskDialogView, {
|
|
21004
21728
|
currentTitle,
|
|
21005
21729
|
get dialogTitle() {
|
|
@@ -21017,9 +21741,9 @@ function show3(dialog, currentTitle, opts = {}) {
|
|
|
21017
21741
|
get allowEmpty() {
|
|
21018
21742
|
return opts.allowEmpty;
|
|
21019
21743
|
},
|
|
21020
|
-
onSubmit: (v) =>
|
|
21021
|
-
onCancel: () =>
|
|
21022
|
-
}), () =>
|
|
21744
|
+
onSubmit: (v) => resolve11(v),
|
|
21745
|
+
onCancel: () => resolve11(undefined)
|
|
21746
|
+
}), () => resolve11(undefined));
|
|
21023
21747
|
});
|
|
21024
21748
|
}
|
|
21025
21749
|
var RenameTaskDialog;
|
|
@@ -21031,22 +21755,6 @@ var init_rename_task_dialog = __esm(() => {
|
|
|
21031
21755
|
};
|
|
21032
21756
|
});
|
|
21033
21757
|
|
|
21034
|
-
// src/tui/lib/editor-prefs.ts
|
|
21035
|
-
function normalizeEditorKind(value) {
|
|
21036
|
-
return EDITOR_KINDS.includes(value) ? value : DEFAULT_EDITOR_KIND;
|
|
21037
|
-
}
|
|
21038
|
-
var EDITOR_KINDS, AUTO_EDITOR_CANDIDATES, EDITOR_KIND_KEY = "editor.kind", EDITOR_CUSTOM_KEY = "editor.customCommand", DEFAULT_EDITOR_KIND = "auto";
|
|
21039
|
-
var init_editor_prefs = __esm(() => {
|
|
21040
|
-
EDITOR_KINDS = ["auto", "vim", "nvim", "nano", "emacs", "custom"];
|
|
21041
|
-
AUTO_EDITOR_CANDIDATES = ["nvim", "vim", "emacs", "nano"];
|
|
21042
|
-
});
|
|
21043
|
-
|
|
21044
|
-
// src/tui/lib/settings-surface.ts
|
|
21045
|
-
function normalizeSettingsSurface(value) {
|
|
21046
|
-
return value === "taskpanel" ? "taskpanel" : "chattab";
|
|
21047
|
-
}
|
|
21048
|
-
var SETTINGS_SURFACE_KEY = "settings.surface", DEFAULT_SETTINGS_SURFACE = "chattab";
|
|
21049
|
-
|
|
21050
21758
|
// src/tui/ui/dialog-confirm.tsx
|
|
21051
21759
|
import { TextAttributes as TextAttributes6 } from "@opentui/core";
|
|
21052
21760
|
function titlecase(s) {
|
|
@@ -21157,18 +21865,18 @@ var init_dialog_confirm = __esm(() => {
|
|
|
21157
21865
|
init_keymap();
|
|
21158
21866
|
init_dialog();
|
|
21159
21867
|
DialogConfirm.show = (dialog, title, message, label, confirmLabel, options) => {
|
|
21160
|
-
return new Promise((
|
|
21868
|
+
return new Promise((resolve11) => {
|
|
21161
21869
|
dialog.replace(() => createComponent2(DialogConfirm, {
|
|
21162
21870
|
title,
|
|
21163
21871
|
message,
|
|
21164
|
-
onConfirm: () =>
|
|
21165
|
-
onCancel: () =>
|
|
21872
|
+
onConfirm: () => resolve11(true),
|
|
21873
|
+
onCancel: () => resolve11(false),
|
|
21166
21874
|
label,
|
|
21167
21875
|
confirmLabel,
|
|
21168
21876
|
get initialActive() {
|
|
21169
21877
|
return options?.initialActive;
|
|
21170
21878
|
}
|
|
21171
|
-
}), () =>
|
|
21879
|
+
}), () => resolve11(undefined));
|
|
21172
21880
|
dialog.setSize("small");
|
|
21173
21881
|
});
|
|
21174
21882
|
};
|
|
@@ -21176,7 +21884,7 @@ var init_dialog_confirm = __esm(() => {
|
|
|
21176
21884
|
|
|
21177
21885
|
// src/tui/component/settings-dialog/actions.ts
|
|
21178
21886
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
21179
|
-
import { join as
|
|
21887
|
+
import { join as join19 } from "path";
|
|
21180
21888
|
function hasRestartableDaemon(orchestrator) {
|
|
21181
21889
|
return orchestrator instanceof RemoteOrchestrator;
|
|
21182
21890
|
}
|
|
@@ -21193,7 +21901,7 @@ async function confirmResetState(dialog, kv, renderer) {
|
|
|
21193
21901
|
return;
|
|
21194
21902
|
kv.clear();
|
|
21195
21903
|
try {
|
|
21196
|
-
unlinkSync2(
|
|
21904
|
+
unlinkSync2(join19(homeDir(), ".kobe", "tasks.json"));
|
|
21197
21905
|
} catch (err) {
|
|
21198
21906
|
if (err.code !== "ENOENT") {
|
|
21199
21907
|
console.error("kobe: failed to delete tasks.json during reset:", err);
|
|
@@ -22507,7 +23215,7 @@ var init_sections = __esm(() => {
|
|
|
22507
23215
|
|
|
22508
23216
|
// src/tui/component/settings-dialog.tsx
|
|
22509
23217
|
import { TextAttributes as TextAttributes8 } from "@opentui/core";
|
|
22510
|
-
function
|
|
23218
|
+
function humanizeSlug2(id) {
|
|
22511
23219
|
return id.split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
22512
23220
|
}
|
|
22513
23221
|
function SettingsDialog(props) {
|
|
@@ -22622,7 +23330,7 @@ function SettingsDialog(props) {
|
|
|
22622
23330
|
const v = props.kv.get(engineCommandKey(vendor), "");
|
|
22623
23331
|
return typeof v === "string" ? v.trim() : "";
|
|
22624
23332
|
}
|
|
22625
|
-
function
|
|
23333
|
+
function engineCommandText2(vendor) {
|
|
22626
23334
|
return engineOverride(vendor) || defaultEngineCommand(vendor).join(" ");
|
|
22627
23335
|
}
|
|
22628
23336
|
function engineIsDefault(vendor) {
|
|
@@ -22648,7 +23356,7 @@ function SettingsDialog(props) {
|
|
|
22648
23356
|
setDefaultEngineSig(vendor);
|
|
22649
23357
|
}
|
|
22650
23358
|
async function editEngine(vendor) {
|
|
22651
|
-
const next = await RenameTaskDialog.show(dialog,
|
|
23359
|
+
const next = await RenameTaskDialog.show(dialog, engineCommandText2(vendor), {
|
|
22652
23360
|
dialogTitle: `${engineName(vendor)} launch command`,
|
|
22653
23361
|
fieldLabel: "command",
|
|
22654
23362
|
submitLabel: "save",
|
|
@@ -22707,7 +23415,7 @@ function SettingsDialog(props) {
|
|
|
22707
23415
|
if (command.trim())
|
|
22708
23416
|
props.kv.set(engineCommandKey(id), command.trim());
|
|
22709
23417
|
const typedName = name?.trim() ?? "";
|
|
22710
|
-
props.kv.set(engineNameKey(id), typedName && typedName !== id ? typedName :
|
|
23418
|
+
props.kv.set(engineNameKey(id), typedName && typedName !== id ? typedName : humanizeSlug2(id));
|
|
22711
23419
|
}
|
|
22712
23420
|
function currentEngineRow() {
|
|
22713
23421
|
if (section() !== "engines" || level() !== "body")
|
|
@@ -22973,7 +23681,7 @@ function SettingsDialog(props) {
|
|
|
22973
23681
|
},
|
|
22974
23682
|
isCustom: (v) => !isBuiltinVendor(v),
|
|
22975
23683
|
displayName: engineName,
|
|
22976
|
-
commandText:
|
|
23684
|
+
commandText: engineCommandText2,
|
|
22977
23685
|
isDefault: engineIsDefault,
|
|
22978
23686
|
isDefaultEngine,
|
|
22979
23687
|
editEngine: (v) => void editEngine(v),
|
|
@@ -23079,6 +23787,7 @@ var init_settings_dialog = __esm(() => {
|
|
|
23079
23787
|
init_auto_status();
|
|
23080
23788
|
init_dispatcher();
|
|
23081
23789
|
init_repos();
|
|
23790
|
+
init_task();
|
|
23082
23791
|
init_vendor();
|
|
23083
23792
|
init_theme2();
|
|
23084
23793
|
init_editor_prefs();
|
|
@@ -23090,17 +23799,17 @@ var init_settings_dialog = __esm(() => {
|
|
|
23090
23799
|
init_sections();
|
|
23091
23800
|
SettingsDialog.show = (dialog, kv, orchestrator) => {
|
|
23092
23801
|
let visualPrefsChanged = false;
|
|
23093
|
-
return new Promise((
|
|
23802
|
+
return new Promise((resolve11) => {
|
|
23094
23803
|
dialog.replace(() => createComponent2(SettingsDialog, {
|
|
23095
23804
|
kv,
|
|
23096
23805
|
orchestrator,
|
|
23097
23806
|
onVisualPrefsChange: () => {
|
|
23098
23807
|
visualPrefsChanged = true;
|
|
23099
23808
|
},
|
|
23100
|
-
onClose: () =>
|
|
23809
|
+
onClose: () => resolve11({
|
|
23101
23810
|
visualPrefsChanged
|
|
23102
23811
|
})
|
|
23103
|
-
}), () =>
|
|
23812
|
+
}), () => resolve11({
|
|
23104
23813
|
visualPrefsChanged
|
|
23105
23814
|
}));
|
|
23106
23815
|
});
|
|
@@ -23495,6 +24204,7 @@ var init_task_actions = __esm(() => {
|
|
|
23495
24204
|
init_interactive_command();
|
|
23496
24205
|
init_errors();
|
|
23497
24206
|
init_repos();
|
|
24207
|
+
init_task();
|
|
23498
24208
|
init_vendor();
|
|
23499
24209
|
init_tmux();
|
|
23500
24210
|
});
|
|
@@ -23502,15 +24212,15 @@ var init_task_actions = __esm(() => {
|
|
|
23502
24212
|
// src/tui/lib/worktree-opener.ts
|
|
23503
24213
|
import { spawn as spawn5 } from "child_process";
|
|
23504
24214
|
import { existsSync as existsSync15 } from "fs";
|
|
23505
|
-
import { basename as basename7, delimiter, isAbsolute as
|
|
24215
|
+
import { basename as basename7, delimiter, isAbsolute as isAbsolute4, join as join20 } from "path";
|
|
23506
24216
|
function executableOnPath(command, env, exists) {
|
|
23507
|
-
if (
|
|
24217
|
+
if (isAbsolute4(command))
|
|
23508
24218
|
return exists(command);
|
|
23509
24219
|
const pathEnv = env.PATH ?? "";
|
|
23510
24220
|
for (const dir of pathEnv.split(delimiter)) {
|
|
23511
24221
|
if (!dir)
|
|
23512
24222
|
continue;
|
|
23513
|
-
if (exists(
|
|
24223
|
+
if (exists(join20(dir, command)))
|
|
23514
24224
|
return true;
|
|
23515
24225
|
}
|
|
23516
24226
|
return false;
|
|
@@ -23643,11 +24353,11 @@ var init_background_poll = __esm(() => {
|
|
|
23643
24353
|
});
|
|
23644
24354
|
|
|
23645
24355
|
// src/tui/panes/sidebar/git-head.ts
|
|
23646
|
-
import { stat as
|
|
23647
|
-
import { join as
|
|
24356
|
+
import { stat as stat5 } from "fs/promises";
|
|
24357
|
+
import { join as join21 } from "path";
|
|
23648
24358
|
async function headFingerprint(repo) {
|
|
23649
24359
|
try {
|
|
23650
|
-
const st = await
|
|
24360
|
+
const st = await stat5(join21(repo, ".git", "HEAD"));
|
|
23651
24361
|
return `${st.mtimeMs}:${st.size}`;
|
|
23652
24362
|
} catch {
|
|
23653
24363
|
return null;
|
|
@@ -24130,6 +24840,28 @@ function Sidebar(props) {
|
|
|
24130
24840
|
const dims = useTerminalDimensions();
|
|
24131
24841
|
const [hover, setHover] = createSignal(null);
|
|
24132
24842
|
const [cursorIndex, setCursorIndex] = createSignal(-1);
|
|
24843
|
+
let scrollRef;
|
|
24844
|
+
let outerBoxRef;
|
|
24845
|
+
const rowEls = new Map;
|
|
24846
|
+
createEffect(() => {
|
|
24847
|
+
const w = props.width ? props.width() : SIDEBAR_WIDTH;
|
|
24848
|
+
const el = outerBoxRef;
|
|
24849
|
+
if (!el)
|
|
24850
|
+
return;
|
|
24851
|
+
el.width = w;
|
|
24852
|
+
el.flexShrink = 1;
|
|
24853
|
+
el.minHeight = 0;
|
|
24854
|
+
});
|
|
24855
|
+
createEffect(on([cursorIndex, rows], ([i]) => {
|
|
24856
|
+
if (!scrollRef)
|
|
24857
|
+
return;
|
|
24858
|
+
if (scrollRef.viewport.height <= 0)
|
|
24859
|
+
return;
|
|
24860
|
+
const el = rowEls.get(i);
|
|
24861
|
+
if (!el)
|
|
24862
|
+
return;
|
|
24863
|
+
scrollRef.scrollChildIntoView(el.id);
|
|
24864
|
+
}));
|
|
24133
24865
|
createEffect(on(() => [props.selectedId(), flatIds()], ([id, ids]) => {
|
|
24134
24866
|
const cur = untrack(cursorIndex);
|
|
24135
24867
|
if (id === null) {
|
|
@@ -24142,8 +24874,14 @@ function Sidebar(props) {
|
|
|
24142
24874
|
return;
|
|
24143
24875
|
}
|
|
24144
24876
|
const idx = ids.indexOf(id);
|
|
24145
|
-
if (idx >= 0
|
|
24146
|
-
|
|
24877
|
+
if (idx >= 0) {
|
|
24878
|
+
if (idx !== cur)
|
|
24879
|
+
setCursorIndex(idx);
|
|
24880
|
+
} else if (ids.length === 0) {
|
|
24881
|
+
setCursorIndex(-1);
|
|
24882
|
+
} else if (cur < 0 || cur >= ids.length) {
|
|
24883
|
+
setCursorIndex(ids.length - 1);
|
|
24884
|
+
}
|
|
24147
24885
|
}));
|
|
24148
24886
|
createEffect(on(view, () => {
|
|
24149
24887
|
const ids = flatIds();
|
|
@@ -24242,7 +24980,11 @@ function Sidebar(props) {
|
|
|
24242
24980
|
insertNode(_el$5, _el$6);
|
|
24243
24981
|
insertNode(_el$5, _el$22);
|
|
24244
24982
|
insertNode(_el$5, _el$28);
|
|
24245
|
-
|
|
24983
|
+
use((r) => {
|
|
24984
|
+
outerBoxRef = r;
|
|
24985
|
+
}, _el$5);
|
|
24986
|
+
setProp(_el$5, "flexGrow", 1);
|
|
24987
|
+
setProp(_el$5, "minHeight", 0);
|
|
24246
24988
|
setProp(_el$5, "flexDirection", "column");
|
|
24247
24989
|
setProp(_el$5, "paddingTop", 1);
|
|
24248
24990
|
setProp(_el$5, "paddingBottom", 1);
|
|
@@ -24270,9 +25012,9 @@ function Sidebar(props) {
|
|
|
24270
25012
|
setProp(_el$35, "onMouseUp", () => props.onHeaderStatusClick?.());
|
|
24271
25013
|
insert(_el$35, () => status().label);
|
|
24272
25014
|
effect((_p$) => {
|
|
24273
|
-
var _v$
|
|
24274
|
-
_v$
|
|
24275
|
-
_v$
|
|
25015
|
+
var _v$19 = status().emphasize ? theme.warning : theme.textMuted, _v$20 = status().emphasize ? TextAttributes11.BOLD : TextAttributes11.DIM;
|
|
25016
|
+
_v$19 !== _p$.e && (_p$.e = setProp(_el$35, "fg", _v$19, _p$.e));
|
|
25017
|
+
_v$20 !== _p$.t && (_p$.t = setProp(_el$35, "attributes", _v$20, _p$.t));
|
|
24276
25018
|
return _p$;
|
|
24277
25019
|
}, {
|
|
24278
25020
|
e: undefined,
|
|
@@ -24384,9 +25126,9 @@ function Sidebar(props) {
|
|
|
24384
25126
|
setProp(_el$36, "onMouseUp", () => setView(tab.view));
|
|
24385
25127
|
insert(_el$36, () => tab.label);
|
|
24386
25128
|
effect((_p$) => {
|
|
24387
|
-
var _v$
|
|
24388
|
-
_v$
|
|
24389
|
-
_v$
|
|
25129
|
+
var _v$21 = active() ? theme.primary : theme.textMuted, _v$22 = active() ? TextAttributes11.BOLD : undefined;
|
|
25130
|
+
_v$21 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$21, _p$.e));
|
|
25131
|
+
_v$22 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$22, _p$.t));
|
|
24390
25132
|
return _p$;
|
|
24391
25133
|
}, {
|
|
24392
25134
|
e: undefined,
|
|
@@ -24420,7 +25162,11 @@ function Sidebar(props) {
|
|
|
24420
25162
|
}
|
|
24421
25163
|
}), null);
|
|
24422
25164
|
insertNode(_el$28, _el$29);
|
|
25165
|
+
use((r) => {
|
|
25166
|
+
scrollRef = r;
|
|
25167
|
+
}, _el$28);
|
|
24423
25168
|
setProp(_el$28, "flexGrow", 1);
|
|
25169
|
+
setProp(_el$28, "minHeight", 0);
|
|
24424
25170
|
setProp(_el$28, "verticalScrollbarOptions", {
|
|
24425
25171
|
trackOptions: {
|
|
24426
25172
|
foregroundColor: "transparent"
|
|
@@ -24529,6 +25275,13 @@ function Sidebar(props) {
|
|
|
24529
25275
|
});
|
|
24530
25276
|
}
|
|
24531
25277
|
}), _el$38);
|
|
25278
|
+
use((r) => {
|
|
25279
|
+
rowEls.set(flatIndex, r);
|
|
25280
|
+
onCleanup(() => {
|
|
25281
|
+
if (rowEls.get(flatIndex) === r)
|
|
25282
|
+
rowEls.delete(flatIndex);
|
|
25283
|
+
});
|
|
25284
|
+
}, _el$38);
|
|
24532
25285
|
setProp(_el$38, "flexDirection", "column");
|
|
24533
25286
|
setProp(_el$38, "gap", 0);
|
|
24534
25287
|
setProp(_el$38, "onMouseUp", () => {
|
|
@@ -24565,12 +25318,12 @@ function Sidebar(props) {
|
|
|
24565
25318
|
setProp(_el$43, "flexGrow", 1);
|
|
24566
25319
|
insert(_el$43, () => spacedTitle(rowView().titleText, titleBudget()));
|
|
24567
25320
|
effect((_p$) => {
|
|
24568
|
-
var _v$
|
|
24569
|
-
_v$
|
|
24570
|
-
_v$
|
|
24571
|
-
_v$
|
|
24572
|
-
_v$
|
|
24573
|
-
_v$
|
|
25321
|
+
var _v$23 = barColor(), _v$24 = stateColor(), _v$25 = TextAttributes11.BOLD, _v$26 = theme.text, _v$27 = TextAttributes11.BOLD;
|
|
25322
|
+
_v$23 !== _p$.e && (_p$.e = setProp(_el$40, "fg", _v$23, _p$.e));
|
|
25323
|
+
_v$24 !== _p$.t && (_p$.t = setProp(_el$42, "fg", _v$24, _p$.t));
|
|
25324
|
+
_v$25 !== _p$.a && (_p$.a = setProp(_el$42, "attributes", _v$25, _p$.a));
|
|
25325
|
+
_v$26 !== _p$.o && (_p$.o = setProp(_el$43, "fg", _v$26, _p$.o));
|
|
25326
|
+
_v$27 !== _p$.i && (_p$.i = setProp(_el$43, "attributes", _v$27, _p$.i));
|
|
24574
25327
|
return _p$;
|
|
24575
25328
|
}, {
|
|
24576
25329
|
e: undefined,
|
|
@@ -24624,10 +25377,10 @@ function Sidebar(props) {
|
|
|
24624
25377
|
}
|
|
24625
25378
|
}), null);
|
|
24626
25379
|
effect((_p$) => {
|
|
24627
|
-
var _v$
|
|
24628
|
-
_v$
|
|
24629
|
-
_v$
|
|
24630
|
-
_v$
|
|
25380
|
+
var _v$28 = barColor(), _v$29 = theme.textMuted, _v$30 = TextAttributes11.DIM;
|
|
25381
|
+
_v$28 !== _p$.e && (_p$.e = setProp(_el$45, "fg", _v$28, _p$.e));
|
|
25382
|
+
_v$29 !== _p$.t && (_p$.t = setProp(_el$47, "fg", _v$29, _p$.t));
|
|
25383
|
+
_v$30 !== _p$.a && (_p$.a = setProp(_el$47, "attributes", _v$30, _p$.a));
|
|
24631
25384
|
return _p$;
|
|
24632
25385
|
}, {
|
|
24633
25386
|
e: undefined,
|
|
@@ -24673,12 +25426,12 @@ function Sidebar(props) {
|
|
|
24673
25426
|
}
|
|
24674
25427
|
}), null);
|
|
24675
25428
|
effect((_p$) => {
|
|
24676
|
-
var _v$
|
|
24677
|
-
_v$
|
|
24678
|
-
_v$
|
|
24679
|
-
_v$
|
|
24680
|
-
_v$
|
|
24681
|
-
_v$
|
|
25429
|
+
var _v$31 = barColor(), _v$32 = stateColor(), _v$33 = TextAttributes11.BOLD, _v$34 = theme.text, _v$35 = isSelected() || isCursor() ? TextAttributes11.BOLD : undefined;
|
|
25430
|
+
_v$31 !== _p$.e && (_p$.e = setProp(_el$53, "fg", _v$31, _p$.e));
|
|
25431
|
+
_v$32 !== _p$.t && (_p$.t = setProp(_el$55, "fg", _v$32, _p$.t));
|
|
25432
|
+
_v$33 !== _p$.a && (_p$.a = setProp(_el$55, "attributes", _v$33, _p$.a));
|
|
25433
|
+
_v$34 !== _p$.o && (_p$.o = setProp(_el$56, "fg", _v$34, _p$.o));
|
|
25434
|
+
_v$35 !== _p$.i && (_p$.i = setProp(_el$56, "attributes", _v$35, _p$.i));
|
|
24682
25435
|
return _p$;
|
|
24683
25436
|
}, {
|
|
24684
25437
|
e: undefined,
|
|
@@ -24744,10 +25497,10 @@ function Sidebar(props) {
|
|
|
24744
25497
|
}
|
|
24745
25498
|
}), null);
|
|
24746
25499
|
effect((_p$) => {
|
|
24747
|
-
var _v$
|
|
24748
|
-
_v$
|
|
24749
|
-
_v$
|
|
24750
|
-
_v$
|
|
25500
|
+
var _v$36 = barColor(), _v$37 = theme.textMuted, _v$38 = TextAttributes11.DIM;
|
|
25501
|
+
_v$36 !== _p$.e && (_p$.e = setProp(_el$60, "fg", _v$36, _p$.e));
|
|
25502
|
+
_v$37 !== _p$.t && (_p$.t = setProp(_el$62, "fg", _v$37, _p$.t));
|
|
25503
|
+
_v$38 !== _p$.a && (_p$.a = setProp(_el$62, "attributes", _v$38, _p$.a));
|
|
24751
25504
|
return _p$;
|
|
24752
25505
|
}, {
|
|
24753
25506
|
e: undefined,
|
|
@@ -24852,9 +25605,9 @@ function Sidebar(props) {
|
|
|
24852
25605
|
return () => _c$2() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
|
|
24853
25606
|
})());
|
|
24854
25607
|
effect((_p$) => {
|
|
24855
|
-
var _v$
|
|
24856
|
-
_v$
|
|
24857
|
-
_v$
|
|
25608
|
+
var _v$44 = l.dim ? theme.textMuted : theme.text, _v$45 = l.bold ? TextAttributes11.BOLD : l.dim ? TextAttributes11.DIM : undefined;
|
|
25609
|
+
_v$44 !== _p$.e && (_p$.e = setProp(_el$70, "fg", _v$44, _p$.e));
|
|
25610
|
+
_v$45 !== _p$.t && (_p$.t = setProp(_el$70, "attributes", _v$45, _p$.t));
|
|
24858
25611
|
return _p$;
|
|
24859
25612
|
}, {
|
|
24860
25613
|
e: undefined,
|
|
@@ -24864,12 +25617,12 @@ function Sidebar(props) {
|
|
|
24864
25617
|
})()
|
|
24865
25618
|
}));
|
|
24866
25619
|
effect((_p$) => {
|
|
24867
|
-
var _v$
|
|
24868
|
-
_v$
|
|
24869
|
-
_v$
|
|
24870
|
-
_v$
|
|
24871
|
-
_v$
|
|
24872
|
-
_v$
|
|
25620
|
+
var _v$39 = left(), _v$40 = top(), _v$41 = boxW(), _v$42 = theme.focusAccent, _v$43 = theme.backgroundElement;
|
|
25621
|
+
_v$39 !== _p$.e && (_p$.e = setProp(_el$69, "left", _v$39, _p$.e));
|
|
25622
|
+
_v$40 !== _p$.t && (_p$.t = setProp(_el$69, "top", _v$40, _p$.t));
|
|
25623
|
+
_v$41 !== _p$.a && (_p$.a = setProp(_el$69, "width", _v$41, _p$.a));
|
|
25624
|
+
_v$42 !== _p$.o && (_p$.o = setProp(_el$69, "borderColor", _v$42, _p$.o));
|
|
25625
|
+
_v$43 !== _p$.i && (_p$.i = setProp(_el$69, "backgroundColor", _v$43, _p$.i));
|
|
24873
25626
|
return _p$;
|
|
24874
25627
|
}, {
|
|
24875
25628
|
e: undefined,
|
|
@@ -24883,19 +25636,17 @@ function Sidebar(props) {
|
|
|
24883
25636
|
}
|
|
24884
25637
|
}), null);
|
|
24885
25638
|
effect((_p$) => {
|
|
24886
|
-
var _v$15 =
|
|
24887
|
-
_v$15 !== _p$.e && (_p$.e = setProp(_el$
|
|
24888
|
-
_v$16 !== _p$.t && (_p$.t = setProp(_el$8, "
|
|
24889
|
-
_v$17 !== _p$.a && (_p$.a = setProp(_el$
|
|
24890
|
-
_v$18 !== _p$.o && (_p$.o = setProp(_el$24, "
|
|
24891
|
-
_v$19 !== _p$.i && (_p$.i = setProp(_el$24, "attributes", _v$19, _p$.i));
|
|
25639
|
+
var _v$15 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$16 = TextAttributes11.BOLD, _v$17 = theme.textMuted, _v$18 = TextAttributes11.DIM;
|
|
25640
|
+
_v$15 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$15, _p$.e));
|
|
25641
|
+
_v$16 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$16, _p$.t));
|
|
25642
|
+
_v$17 !== _p$.a && (_p$.a = setProp(_el$24, "fg", _v$17, _p$.a));
|
|
25643
|
+
_v$18 !== _p$.o && (_p$.o = setProp(_el$24, "attributes", _v$18, _p$.o));
|
|
24892
25644
|
return _p$;
|
|
24893
25645
|
}, {
|
|
24894
25646
|
e: undefined,
|
|
24895
25647
|
t: undefined,
|
|
24896
25648
|
a: undefined,
|
|
24897
|
-
o: undefined
|
|
24898
|
-
i: undefined
|
|
25649
|
+
o: undefined
|
|
24899
25650
|
});
|
|
24900
25651
|
return _el$5;
|
|
24901
25652
|
})();
|
|
@@ -24911,6 +25662,7 @@ var init_Sidebar = __esm(() => {
|
|
|
24911
25662
|
init_solid();
|
|
24912
25663
|
init_solid();
|
|
24913
25664
|
init_solid();
|
|
25665
|
+
init_solid();
|
|
24914
25666
|
init_dev();
|
|
24915
25667
|
init_theme2();
|
|
24916
25668
|
init_theme2();
|
|
@@ -24937,7 +25689,7 @@ __export(exports_host3, {
|
|
|
24937
25689
|
legendCap: () => legendCap
|
|
24938
25690
|
});
|
|
24939
25691
|
import { existsSync as existsSync16 } from "fs";
|
|
24940
|
-
import { stat as
|
|
25692
|
+
import { stat as stat6 } from "fs/promises";
|
|
24941
25693
|
import { TextAttributes as TextAttributes12 } from "@opentui/core";
|
|
24942
25694
|
function worktreeCwdUsable(cwd) {
|
|
24943
25695
|
return !!cwd && worktreeUsable(cwd);
|
|
@@ -25201,6 +25953,7 @@ function TasksShell(props) {
|
|
|
25201
25953
|
initPrompt: init3.initPrompt
|
|
25202
25954
|
});
|
|
25203
25955
|
}
|
|
25956
|
+
await prepareWindowForSwitch(name);
|
|
25204
25957
|
await runTmux(["switch-client", "-t", `=${name}`]);
|
|
25205
25958
|
props.orch?.setActiveTask(id).catch(() => {});
|
|
25206
25959
|
return;
|
|
@@ -25239,6 +25992,7 @@ function TasksShell(props) {
|
|
|
25239
25992
|
notifyError("Couldn't start this task's session");
|
|
25240
25993
|
return;
|
|
25241
25994
|
}
|
|
25995
|
+
await prepareWindowForSwitch(name);
|
|
25242
25996
|
await runTmux(["switch-client", "-t", `=${name}`]);
|
|
25243
25997
|
props.orch?.setActiveTask(id).catch(() => {});
|
|
25244
25998
|
}
|
|
@@ -25373,52 +26127,6 @@ function ShortcutHints(props) {
|
|
|
25373
26127
|
label: "move panes"
|
|
25374
26128
|
});
|
|
25375
26129
|
}
|
|
25376
|
-
const prev = b["tmux.tab.prev"];
|
|
25377
|
-
const next = b["tmux.tab.next"];
|
|
25378
|
-
if (prev?.chord === "ctrl+[" && next?.chord === "ctrl+]") {
|
|
25379
|
-
out.push({
|
|
25380
|
-
k: "ctrl+[/]",
|
|
25381
|
-
label: "switch tabs"
|
|
25382
|
-
});
|
|
25383
|
-
} else {
|
|
25384
|
-
if (prev)
|
|
25385
|
-
out.push({
|
|
25386
|
-
k: prev.chord,
|
|
25387
|
-
label: "prev tab"
|
|
25388
|
-
});
|
|
25389
|
-
if (next)
|
|
25390
|
-
out.push({
|
|
25391
|
-
k: next.chord,
|
|
25392
|
-
label: "next tab"
|
|
25393
|
-
});
|
|
25394
|
-
}
|
|
25395
|
-
if (b["tmux.tab.new"])
|
|
25396
|
-
out.push({
|
|
25397
|
-
k: b["tmux.tab.new"].chord,
|
|
25398
|
-
label: "new tab"
|
|
25399
|
-
});
|
|
25400
|
-
if (b["tmux.tab.chooseEngine"])
|
|
25401
|
-
out.push({
|
|
25402
|
-
k: b["tmux.tab.chooseEngine"].chord,
|
|
25403
|
-
label: "engine tab"
|
|
25404
|
-
});
|
|
25405
|
-
out.push({
|
|
25406
|
-
k: "prefix t",
|
|
25407
|
-
label: "engine tab"
|
|
25408
|
-
}, {
|
|
25409
|
-
k: "prefix f",
|
|
25410
|
-
label: "new task"
|
|
25411
|
-
});
|
|
25412
|
-
if (b["tmux.tab.rename"])
|
|
25413
|
-
out.push({
|
|
25414
|
-
k: b["tmux.tab.rename"].chord,
|
|
25415
|
-
label: "rename tab"
|
|
25416
|
-
});
|
|
25417
|
-
if (b["tmux.tab.close"])
|
|
25418
|
-
out.push({
|
|
25419
|
-
k: b["tmux.tab.close"].chord,
|
|
25420
|
-
label: "close tab"
|
|
25421
|
-
});
|
|
25422
26130
|
if (b["tmux.detach"])
|
|
25423
26131
|
out.push({
|
|
25424
26132
|
k: b["tmux.detach"].chord,
|
|
@@ -25430,12 +26138,8 @@ function ShortcutHints(props) {
|
|
|
25430
26138
|
keymapVersion();
|
|
25431
26139
|
const rows = [
|
|
25432
26140
|
{
|
|
25433
|
-
ids: ["
|
|
25434
|
-
label: "
|
|
25435
|
-
},
|
|
25436
|
-
{
|
|
25437
|
-
ids: ["tasks.focusEngine"],
|
|
25438
|
-
label: "focus engine"
|
|
26141
|
+
ids: ["help.open"],
|
|
26142
|
+
label: "full help"
|
|
25439
26143
|
},
|
|
25440
26144
|
{
|
|
25441
26145
|
ids: ["task.new"],
|
|
@@ -25446,34 +26150,24 @@ function ShortcutHints(props) {
|
|
|
25446
26150
|
label: "settings"
|
|
25447
26151
|
},
|
|
25448
26152
|
{
|
|
25449
|
-
ids: ["
|
|
25450
|
-
label: "open
|
|
25451
|
-
},
|
|
25452
|
-
{
|
|
25453
|
-
ids: ["sidebar.view"],
|
|
25454
|
-
label: "views"
|
|
25455
|
-
},
|
|
25456
|
-
{
|
|
25457
|
-
ids: ["sidebar.sort"],
|
|
25458
|
-
label: "sort"
|
|
26153
|
+
ids: ["sidebar.select"],
|
|
26154
|
+
label: "open"
|
|
25459
26155
|
},
|
|
25460
26156
|
{
|
|
25461
|
-
ids: ["
|
|
25462
|
-
label: "
|
|
25463
|
-
dimWhenMain: true
|
|
26157
|
+
ids: ["tasks.focusEngine"],
|
|
26158
|
+
label: "focus engine"
|
|
25464
26159
|
},
|
|
25465
26160
|
{
|
|
25466
|
-
ids: ["
|
|
25467
|
-
label: "
|
|
26161
|
+
ids: ["tasks.openWorktree"],
|
|
26162
|
+
label: "open wt"
|
|
25468
26163
|
},
|
|
25469
26164
|
{
|
|
25470
|
-
ids: ["sidebar.
|
|
25471
|
-
label: "
|
|
25472
|
-
dimWhenMain: true
|
|
26165
|
+
ids: ["sidebar.delete"],
|
|
26166
|
+
label: "delete"
|
|
25473
26167
|
},
|
|
25474
26168
|
{
|
|
25475
|
-
ids: ["
|
|
25476
|
-
label: "
|
|
26169
|
+
ids: ["sidebar.view"],
|
|
26170
|
+
label: "views"
|
|
25477
26171
|
}
|
|
25478
26172
|
];
|
|
25479
26173
|
const out = [];
|
|
@@ -25617,7 +26311,7 @@ async function setupTasksPane(opts) {
|
|
|
25617
26311
|
(async () => {
|
|
25618
26312
|
let fingerprint = "missing";
|
|
25619
26313
|
try {
|
|
25620
|
-
const st = await
|
|
26314
|
+
const st = await stat6(store2.filePath);
|
|
25621
26315
|
fingerprint = `${st.mtimeMs}:${st.size}`;
|
|
25622
26316
|
} catch {}
|
|
25623
26317
|
if (fingerprint === lastTasksFileFingerprint)
|
|
@@ -25889,18 +26583,18 @@ function openExternalUrl(url) {
|
|
|
25889
26583
|
function releaseBodyLines(body) {
|
|
25890
26584
|
return body.replace(/\r\n/g, `
|
|
25891
26585
|
`).split(`
|
|
25892
|
-
`).map((line) => line.trim()).filter(Boolean)
|
|
26586
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
25893
26587
|
}
|
|
25894
26588
|
function waitForKeypress() {
|
|
25895
26589
|
if (!process.stdin.isTTY)
|
|
25896
26590
|
return Promise.resolve();
|
|
25897
|
-
return new Promise((
|
|
26591
|
+
return new Promise((resolve11) => {
|
|
25898
26592
|
const stdin = process.stdin;
|
|
25899
26593
|
const done = () => {
|
|
25900
26594
|
stdin.off("data", done);
|
|
25901
26595
|
stdin.setRawMode?.(false);
|
|
25902
26596
|
stdin.pause();
|
|
25903
|
-
|
|
26597
|
+
resolve11();
|
|
25904
26598
|
};
|
|
25905
26599
|
stdin.setRawMode?.(true);
|
|
25906
26600
|
stdin.resume();
|
|
@@ -25913,13 +26607,12 @@ function UpdatePage() {
|
|
|
25913
26607
|
} = useTheme();
|
|
25914
26608
|
const renderer = useRenderer();
|
|
25915
26609
|
const [info, setInfo] = createSignal(null);
|
|
25916
|
-
const [
|
|
26610
|
+
const [releaseNotes, setReleaseNotes] = createSignal([]);
|
|
25917
26611
|
const [loadingNotes, setLoadingNotes] = createSignal(true);
|
|
25918
26612
|
const [selected, setSelected] = createSignal("update");
|
|
25919
26613
|
const [status, setStatus] = createSignal(null);
|
|
25920
26614
|
const latest = createMemo(() => info()?.latest ?? CURRENT_VERSION);
|
|
25921
|
-
const releaseUrl = createMemo(() =>
|
|
25922
|
-
const lines = createMemo(() => releaseBodyLines(notes()?.body ?? ""));
|
|
26615
|
+
const releaseUrl = createMemo(() => releaseNotes()[0]?.url ?? releasePageUrl(latest()));
|
|
25923
26616
|
const actions = createMemo(() => [{
|
|
25924
26617
|
id: "update",
|
|
25925
26618
|
key: "U",
|
|
@@ -25944,9 +26637,12 @@ function UpdatePage() {
|
|
|
25944
26637
|
force: true
|
|
25945
26638
|
});
|
|
25946
26639
|
setInfo(next);
|
|
25947
|
-
const
|
|
25948
|
-
const fetched = await
|
|
25949
|
-
|
|
26640
|
+
const latestVersion = next?.latest ?? CURRENT_VERSION;
|
|
26641
|
+
const fetched = await fetchReleaseNotesRange({
|
|
26642
|
+
current: CURRENT_VERSION,
|
|
26643
|
+
latest: latestVersion
|
|
26644
|
+
});
|
|
26645
|
+
setReleaseNotes(fetched);
|
|
25950
26646
|
setLoadingNotes(false);
|
|
25951
26647
|
}
|
|
25952
26648
|
function move(delta) {
|
|
@@ -25966,7 +26662,7 @@ function UpdatePage() {
|
|
|
25966
26662
|
}
|
|
25967
26663
|
async function runUpdater() {
|
|
25968
26664
|
setStatus("Leaving the TUI page and running the updater in this tmux window...");
|
|
25969
|
-
await new Promise((
|
|
26665
|
+
await new Promise((resolve11) => setTimeout(resolve11, 30));
|
|
25970
26666
|
renderer?.destroy();
|
|
25971
26667
|
process.stdout.write(`
|
|
25972
26668
|
kobe ${CURRENT_VERSION} -> latest
|
|
@@ -26025,12 +26721,12 @@ kobe update failed with exit code ${code}.
|
|
|
26025
26721
|
}]
|
|
26026
26722
|
}));
|
|
26027
26723
|
return (() => {
|
|
26028
|
-
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$1 = createTextNode(`v`), _el$10 = createElement("text"), _el$12 = createElement("text"), _el$13 = createTextNode(`v`), _el$14 = createElement("box"), _el$16 = createElement("box"), _el$17 = createElement("text"), _el$19 = createElement("scrollbox"), _el$
|
|
26724
|
+
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$5 = createElement("text"), _el$7 = createElement("box"), _el$8 = createElement("text"), _el$0 = createElement("text"), _el$1 = createTextNode(`v`), _el$10 = createElement("text"), _el$12 = createElement("text"), _el$13 = createTextNode(`v`), _el$14 = createElement("box"), _el$16 = createElement("box"), _el$17 = createElement("text"), _el$18 = createTextNode(`\u2500\u2500 changes from v`), _el$19 = createTextNode(` to v`), _el$20 = createTextNode(` \u2500\u2500`), _el$21 = createElement("scrollbox"), _el$22 = createElement("box");
|
|
26029
26725
|
insertNode(_el$, _el$2);
|
|
26030
26726
|
insertNode(_el$, _el$7);
|
|
26031
26727
|
insertNode(_el$, _el$14);
|
|
26032
26728
|
insertNode(_el$, _el$16);
|
|
26033
|
-
insertNode(_el$, _el$
|
|
26729
|
+
insertNode(_el$, _el$21);
|
|
26034
26730
|
setProp(_el$, "flexDirection", "column");
|
|
26035
26731
|
setProp(_el$, "flexGrow", 1);
|
|
26036
26732
|
setProp(_el$, "paddingTop", 1);
|
|
@@ -26073,36 +26769,36 @@ kobe update failed with exit code ${code}.
|
|
|
26073
26769
|
return actions();
|
|
26074
26770
|
},
|
|
26075
26771
|
children: (action) => (() => {
|
|
26076
|
-
var _el$
|
|
26077
|
-
insertNode(_el$25, _el$26);
|
|
26078
|
-
insertNode(_el$25, _el$30);
|
|
26079
|
-
insertNode(_el$25, _el$32);
|
|
26080
|
-
setProp(_el$25, "flexDirection", "row");
|
|
26081
|
-
setProp(_el$25, "gap", 1);
|
|
26082
|
-
setProp(_el$25, "paddingLeft", 1);
|
|
26083
|
-
setProp(_el$25, "paddingRight", 1);
|
|
26084
|
-
setProp(_el$25, "onMouseUp", () => activate(action.id));
|
|
26085
|
-
insertNode(_el$26, _el$27);
|
|
26086
|
-
setProp(_el$26, "width", 4);
|
|
26087
|
-
setProp(_el$26, "flexShrink", 0);
|
|
26772
|
+
var _el$27 = createElement("box"), _el$28 = createElement("box"), _el$29 = createElement("text"), _el$30 = createTextNode(`[`), _el$31 = createTextNode(`]`), _el$32 = createElement("box"), _el$33 = createElement("text"), _el$34 = createElement("text");
|
|
26088
26773
|
insertNode(_el$27, _el$28);
|
|
26089
|
-
insertNode(_el$27, _el$
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
setProp(_el$
|
|
26094
|
-
setProp(_el$
|
|
26095
|
-
setProp(_el$
|
|
26096
|
-
|
|
26097
|
-
setProp(_el$
|
|
26098
|
-
|
|
26774
|
+
insertNode(_el$27, _el$32);
|
|
26775
|
+
insertNode(_el$27, _el$34);
|
|
26776
|
+
setProp(_el$27, "flexDirection", "row");
|
|
26777
|
+
setProp(_el$27, "gap", 1);
|
|
26778
|
+
setProp(_el$27, "paddingLeft", 1);
|
|
26779
|
+
setProp(_el$27, "paddingRight", 1);
|
|
26780
|
+
setProp(_el$27, "onMouseUp", () => activate(action.id));
|
|
26781
|
+
insertNode(_el$28, _el$29);
|
|
26782
|
+
setProp(_el$28, "width", 4);
|
|
26783
|
+
setProp(_el$28, "flexShrink", 0);
|
|
26784
|
+
insertNode(_el$29, _el$30);
|
|
26785
|
+
insertNode(_el$29, _el$31);
|
|
26786
|
+
setProp(_el$29, "wrapMode", "none");
|
|
26787
|
+
insert(_el$29, () => action.key, _el$31);
|
|
26788
|
+
insertNode(_el$32, _el$33);
|
|
26789
|
+
setProp(_el$32, "width", 14);
|
|
26790
|
+
setProp(_el$32, "flexShrink", 0);
|
|
26791
|
+
setProp(_el$33, "wrapMode", "none");
|
|
26792
|
+
insert(_el$33, () => action.label);
|
|
26793
|
+
setProp(_el$34, "wrapMode", "word");
|
|
26794
|
+
insert(_el$34, () => action.detail);
|
|
26099
26795
|
effect((_p$) => {
|
|
26100
26796
|
var _v$12 = selected() === action.id ? theme.primary : undefined, _v$13 = selected() === action.id ? theme.selectedListItemText : theme.accent, _v$14 = TextAttributes13.BOLD, _v$15 = selected() === action.id ? theme.selectedListItemText : theme.text, _v$16 = selected() === action.id ? theme.selectedListItemText : theme.textMuted;
|
|
26101
|
-
_v$12 !== _p$.e && (_p$.e = setProp(_el$
|
|
26102
|
-
_v$13 !== _p$.t && (_p$.t = setProp(_el$
|
|
26103
|
-
_v$14 !== _p$.a && (_p$.a = setProp(_el$
|
|
26104
|
-
_v$15 !== _p$.o && (_p$.o = setProp(_el$
|
|
26105
|
-
_v$16 !== _p$.i && (_p$.i = setProp(_el$
|
|
26797
|
+
_v$12 !== _p$.e && (_p$.e = setProp(_el$27, "backgroundColor", _v$12, _p$.e));
|
|
26798
|
+
_v$13 !== _p$.t && (_p$.t = setProp(_el$29, "fg", _v$13, _p$.t));
|
|
26799
|
+
_v$14 !== _p$.a && (_p$.a = setProp(_el$29, "attributes", _v$14, _p$.a));
|
|
26800
|
+
_v$15 !== _p$.o && (_p$.o = setProp(_el$33, "fg", _v$15, _p$.o));
|
|
26801
|
+
_v$16 !== _p$.i && (_p$.i = setProp(_el$34, "fg", _v$16, _p$.i));
|
|
26106
26802
|
return _p$;
|
|
26107
26803
|
}, {
|
|
26108
26804
|
e: undefined,
|
|
@@ -26111,7 +26807,7 @@ kobe update failed with exit code ${code}.
|
|
|
26111
26807
|
o: undefined,
|
|
26112
26808
|
i: undefined
|
|
26113
26809
|
});
|
|
26114
|
-
return _el$
|
|
26810
|
+
return _el$27;
|
|
26115
26811
|
})()
|
|
26116
26812
|
}));
|
|
26117
26813
|
insert(_el$, createComponent2(Show, {
|
|
@@ -26129,49 +26825,78 @@ kobe update failed with exit code ${code}.
|
|
|
26129
26825
|
insertNode(_el$16, _el$17);
|
|
26130
26826
|
setProp(_el$16, "flexShrink", 0);
|
|
26131
26827
|
setProp(_el$16, "paddingTop", 1);
|
|
26132
|
-
insertNode(_el$17,
|
|
26828
|
+
insertNode(_el$17, _el$18);
|
|
26829
|
+
insertNode(_el$17, _el$19);
|
|
26830
|
+
insertNode(_el$17, _el$20);
|
|
26133
26831
|
setProp(_el$17, "wrapMode", "none");
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
setProp(_el$
|
|
26138
|
-
setProp(_el$
|
|
26139
|
-
setProp(_el$
|
|
26140
|
-
setProp(_el$
|
|
26141
|
-
setProp(_el$
|
|
26142
|
-
|
|
26832
|
+
insert(_el$17, CURRENT_VERSION, _el$19);
|
|
26833
|
+
insert(_el$17, latest, _el$20);
|
|
26834
|
+
insertNode(_el$21, _el$22);
|
|
26835
|
+
setProp(_el$21, "flexGrow", 1);
|
|
26836
|
+
setProp(_el$21, "flexShrink", 1);
|
|
26837
|
+
setProp(_el$21, "stickyScroll", false);
|
|
26838
|
+
setProp(_el$22, "flexDirection", "column");
|
|
26839
|
+
setProp(_el$22, "paddingRight", 1);
|
|
26840
|
+
setProp(_el$22, "paddingBottom", 1);
|
|
26841
|
+
setProp(_el$22, "gap", 0);
|
|
26842
|
+
insert(_el$22, createComponent2(Show, {
|
|
26143
26843
|
get when() {
|
|
26144
26844
|
return loadingNotes();
|
|
26145
26845
|
},
|
|
26146
26846
|
get children() {
|
|
26147
|
-
var _el$
|
|
26148
|
-
insertNode(_el$
|
|
26149
|
-
effect((_$p) => setProp(_el$
|
|
26150
|
-
return _el$
|
|
26847
|
+
var _el$23 = createElement("text");
|
|
26848
|
+
insertNode(_el$23, createTextNode(`Loading release notes...`));
|
|
26849
|
+
effect((_$p) => setProp(_el$23, "fg", theme.textMuted, _$p));
|
|
26850
|
+
return _el$23;
|
|
26151
26851
|
}
|
|
26152
26852
|
}), null);
|
|
26153
|
-
insert(_el$
|
|
26853
|
+
insert(_el$22, createComponent2(Show, {
|
|
26154
26854
|
get when() {
|
|
26155
|
-
return memo2(() => !!!loadingNotes())() &&
|
|
26855
|
+
return memo2(() => !!!loadingNotes())() && releaseNotes().length === 0;
|
|
26156
26856
|
},
|
|
26157
26857
|
get children() {
|
|
26158
|
-
var _el$
|
|
26159
|
-
insertNode(_el$
|
|
26160
|
-
setProp(_el$
|
|
26161
|
-
effect((_$p) => setProp(_el$
|
|
26162
|
-
return _el$
|
|
26858
|
+
var _el$25 = createElement("text");
|
|
26859
|
+
insertNode(_el$25, createTextNode(`Release notes are unavailable. Use Open release to view the GitHub release page.`));
|
|
26860
|
+
setProp(_el$25, "wrapMode", "word");
|
|
26861
|
+
effect((_$p) => setProp(_el$25, "fg", theme.textMuted, _$p));
|
|
26862
|
+
return _el$25;
|
|
26163
26863
|
}
|
|
26164
26864
|
}), null);
|
|
26165
|
-
insert(_el$
|
|
26865
|
+
insert(_el$22, createComponent2(For, {
|
|
26166
26866
|
get each() {
|
|
26167
|
-
return
|
|
26168
|
-
},
|
|
26169
|
-
children: (
|
|
26170
|
-
var _el$
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26867
|
+
return releaseNotes();
|
|
26868
|
+
},
|
|
26869
|
+
children: (release) => (() => {
|
|
26870
|
+
var _el$35 = createElement("box"), _el$36 = createElement("text"), _el$37 = createTextNode(`v`);
|
|
26871
|
+
insertNode(_el$35, _el$36);
|
|
26872
|
+
setProp(_el$35, "flexDirection", "column");
|
|
26873
|
+
setProp(_el$35, "paddingBottom", 1);
|
|
26874
|
+
setProp(_el$35, "gap", 0);
|
|
26875
|
+
insertNode(_el$36, _el$37);
|
|
26876
|
+
setProp(_el$36, "wrapMode", "none");
|
|
26877
|
+
insert(_el$36, () => release.version, null);
|
|
26878
|
+
insert(_el$35, createComponent2(For, {
|
|
26879
|
+
get each() {
|
|
26880
|
+
return releaseBodyLines(release.body);
|
|
26881
|
+
},
|
|
26882
|
+
children: (line) => (() => {
|
|
26883
|
+
var _el$38 = createElement("text");
|
|
26884
|
+
setProp(_el$38, "wrapMode", "word");
|
|
26885
|
+
insert(_el$38, line);
|
|
26886
|
+
effect((_$p) => setProp(_el$38, "fg", theme.textMuted, _$p));
|
|
26887
|
+
return _el$38;
|
|
26888
|
+
})()
|
|
26889
|
+
}), null);
|
|
26890
|
+
effect((_p$) => {
|
|
26891
|
+
var _v$17 = theme.text, _v$18 = TextAttributes13.BOLD;
|
|
26892
|
+
_v$17 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$17, _p$.e));
|
|
26893
|
+
_v$18 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$18, _p$.t));
|
|
26894
|
+
return _p$;
|
|
26895
|
+
}, {
|
|
26896
|
+
e: undefined,
|
|
26897
|
+
t: undefined
|
|
26898
|
+
});
|
|
26899
|
+
return _el$35;
|
|
26175
26900
|
})()
|
|
26176
26901
|
}), null);
|
|
26177
26902
|
effect((_p$) => {
|
|
@@ -26193,7 +26918,7 @@ kobe update failed with exit code ${code}.
|
|
|
26193
26918
|
_v$0 !== _p$.d && (_p$.d = setProp(_el$12, "attributes", _v$0, _p$.d));
|
|
26194
26919
|
_v$1 !== _p$.l && (_p$.l = setProp(_el$17, "fg", _v$1, _p$.l));
|
|
26195
26920
|
_v$10 !== _p$.u && (_p$.u = setProp(_el$17, "attributes", _v$10, _p$.u));
|
|
26196
|
-
_v$11 !== _p$.c && (_p$.c = setProp(_el$
|
|
26921
|
+
_v$11 !== _p$.c && (_p$.c = setProp(_el$21, "verticalScrollbarOptions", _v$11, _p$.c));
|
|
26197
26922
|
return _p$;
|
|
26198
26923
|
}, {
|
|
26199
26924
|
e: undefined,
|
|
@@ -26513,7 +27238,7 @@ var gitWrapper;
|
|
|
26513
27238
|
var init_git2 = __esm(() => {
|
|
26514
27239
|
gitWrapper = {
|
|
26515
27240
|
spawn(args2, cwd) {
|
|
26516
|
-
return new Promise((
|
|
27241
|
+
return new Promise((resolve11, reject) => {
|
|
26517
27242
|
const child = nodeSpawn("git", [...args2], {
|
|
26518
27243
|
cwd,
|
|
26519
27244
|
shell: false,
|
|
@@ -26532,7 +27257,7 @@ var init_git2 = __esm(() => {
|
|
|
26532
27257
|
});
|
|
26533
27258
|
child.on("error", reject);
|
|
26534
27259
|
child.on("close", (status, signal) => {
|
|
26535
|
-
|
|
27260
|
+
resolve11({ stdout, stderr, status, signal });
|
|
26536
27261
|
});
|
|
26537
27262
|
});
|
|
26538
27263
|
}
|
|
@@ -27471,7 +28196,7 @@ var init_filetree = __esm(() => {
|
|
|
27471
28196
|
// src/tui/ops/pr-prompt.ts
|
|
27472
28197
|
import { promises as fs6 } from "fs";
|
|
27473
28198
|
import path12 from "path";
|
|
27474
|
-
async function
|
|
28199
|
+
async function git(cwd, args2) {
|
|
27475
28200
|
const controller = new AbortController;
|
|
27476
28201
|
const timer = setTimeout(() => controller.abort(), GIT_TIMEOUT_MS2);
|
|
27477
28202
|
try {
|
|
@@ -27490,20 +28215,20 @@ async function git2(cwd, args2) {
|
|
|
27490
28215
|
}
|
|
27491
28216
|
}
|
|
27492
28217
|
async function currentBranch2(cwd) {
|
|
27493
|
-
return await
|
|
28218
|
+
return await git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
|
|
27494
28219
|
}
|
|
27495
28220
|
async function targetBranch(cwd) {
|
|
27496
|
-
const out = await
|
|
28221
|
+
const out = await git(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]);
|
|
27497
28222
|
if (!out)
|
|
27498
28223
|
return "main";
|
|
27499
28224
|
return out.startsWith("origin/") ? out.slice("origin/".length) : out;
|
|
27500
28225
|
}
|
|
27501
28226
|
async function hasUpstream(cwd) {
|
|
27502
|
-
const out = await
|
|
28227
|
+
const out = await git(cwd, ["rev-parse", "--abbrev-ref", "@{u}"]);
|
|
27503
28228
|
return out !== null && out.length > 0;
|
|
27504
28229
|
}
|
|
27505
28230
|
async function dirtyCount(cwd) {
|
|
27506
|
-
const out = await
|
|
28231
|
+
const out = await git(cwd, ["status", "--porcelain"]);
|
|
27507
28232
|
if (!out)
|
|
27508
28233
|
return 0;
|
|
27509
28234
|
return out.split(`
|
|
@@ -27584,7 +28309,7 @@ __export(exports_host7, {
|
|
|
27584
28309
|
startOpsHost: () => startOpsHost,
|
|
27585
28310
|
nextActivityPollDelay: () => nextActivityPollDelay
|
|
27586
28311
|
});
|
|
27587
|
-
import { createHash as
|
|
28312
|
+
import { createHash as createHash7 } from "crypto";
|
|
27588
28313
|
import { SyntaxStyle } from "@opentui/core";
|
|
27589
28314
|
function nextActivityPollDelay(currentMs, idleStreak) {
|
|
27590
28315
|
if (idleStreak < ACTIVITY_IDLE_RAMP_POLLS)
|
|
@@ -27757,7 +28482,7 @@ function basename8(p) {
|
|
|
27757
28482
|
return i >= 0 ? p.slice(i + 1) : p;
|
|
27758
28483
|
}
|
|
27759
28484
|
function fingerprint(text) {
|
|
27760
|
-
return
|
|
28485
|
+
return createHash7("sha1").update(text).digest("hex");
|
|
27761
28486
|
}
|
|
27762
28487
|
async function startOpsHost(args2) {
|
|
27763
28488
|
await bootPaneHost({
|
|
@@ -28025,7 +28750,7 @@ __export(exports_direct, {
|
|
|
28025
28750
|
startDirectTmux: () => startDirectTmux,
|
|
28026
28751
|
chooseInitialTask: () => chooseInitialTask
|
|
28027
28752
|
});
|
|
28028
|
-
import { resolve as
|
|
28753
|
+
import { resolve as resolve11 } from "path";
|
|
28029
28754
|
function chooseInitialTask(tasks, choice = {}) {
|
|
28030
28755
|
const byId = (id) => id ? tasks.find((t) => t.id === id) : undefined;
|
|
28031
28756
|
const active = byId(choice.activeTaskId);
|
|
@@ -28061,7 +28786,7 @@ async function ensureRepos(orchestrator) {
|
|
|
28061
28786
|
normalizeSavedRepos();
|
|
28062
28787
|
let repos = [...getSavedRepos()];
|
|
28063
28788
|
if (repos.length === 0) {
|
|
28064
|
-
const added = addSavedRepo(
|
|
28789
|
+
const added = addSavedRepo(resolve11(process.cwd()));
|
|
28065
28790
|
repos = [added.path];
|
|
28066
28791
|
}
|
|
28067
28792
|
for (const repo of repos) {
|
|
@@ -28071,7 +28796,7 @@ async function ensureRepos(orchestrator) {
|
|
|
28071
28796
|
console.error(`[kobe] ensureMainTask failed for ${repo}:`, err);
|
|
28072
28797
|
}
|
|
28073
28798
|
}
|
|
28074
|
-
return repos[0] ??
|
|
28799
|
+
return repos[0] ?? resolve11(process.cwd());
|
|
28075
28800
|
}
|
|
28076
28801
|
async function startDirectTmux() {
|
|
28077
28802
|
setClientLogContext("gui");
|
|
@@ -28182,7 +28907,7 @@ var init_tui = __esm(() => {
|
|
|
28182
28907
|
// src/cli/index.ts
|
|
28183
28908
|
init_path_glob();
|
|
28184
28909
|
init_vendor();
|
|
28185
|
-
import { resolve as
|
|
28910
|
+
import { resolve as resolve12 } from "path";
|
|
28186
28911
|
|
|
28187
28912
|
// src/cli/usage.ts
|
|
28188
28913
|
init_version();
|
|
@@ -28241,7 +28966,7 @@ async function runAddSubcommand(rest) {
|
|
|
28241
28966
|
${ADD_USAGE}`);
|
|
28242
28967
|
process.exit(2);
|
|
28243
28968
|
}
|
|
28244
|
-
const target =
|
|
28969
|
+
const target = resolve12(process.cwd(), arg && arg.length > 0 ? arg : ".");
|
|
28245
28970
|
const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
28246
28971
|
const result = addSavedRepo2(target);
|
|
28247
28972
|
if (result.added) {
|
|
@@ -28340,7 +29065,7 @@ async function runAdoptSubcommand(args2) {
|
|
|
28340
29065
|
}
|
|
28341
29066
|
}
|
|
28342
29067
|
const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
28343
|
-
const repo = resolveRepoRoot2(
|
|
29068
|
+
const repo = resolveRepoRoot2(resolve12(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
|
|
28344
29069
|
const vendor = coerceVendorId(vendorArg);
|
|
28345
29070
|
const orch = await openLocalOrchestrator();
|
|
28346
29071
|
const worktrees = await orch.discoverAdoptableWorktrees(repo);
|
|
@@ -28558,7 +29283,24 @@ async function main() {
|
|
|
28558
29283
|
process.exit(2);
|
|
28559
29284
|
}
|
|
28560
29285
|
const { healSessionLayout: healSessionLayout2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
28561
|
-
await
|
|
29286
|
+
const { coalesceLayoutWork: coalesceLayoutWork2 } = await Promise.resolve().then(() => (init_layout_coord(), exports_layout_coord));
|
|
29287
|
+
await coalesceLayoutWork2(session, "heal", () => healSessionLayout2(session));
|
|
29288
|
+
return;
|
|
29289
|
+
}
|
|
29290
|
+
if (subcommand === "capture-layout") {
|
|
29291
|
+
const flags = parseOpsFlags(rest);
|
|
29292
|
+
const session = flags.session;
|
|
29293
|
+
if (!session) {
|
|
29294
|
+
console.error("kobe capture-layout: --session <name> is required");
|
|
29295
|
+
process.exit(2);
|
|
29296
|
+
}
|
|
29297
|
+
const { captureGlobalLayoutOnDrag: captureGlobalLayoutOnDrag2 } = await Promise.resolve().then(() => (init_tmux(), exports_tmux));
|
|
29298
|
+
const { coalesceLayoutWork: coalesceLayoutWork2, genAgeMs: genAgeMs2, RESIZE_GUARD_MS: RESIZE_GUARD_MS2 } = await Promise.resolve().then(() => (init_layout_coord(), exports_layout_coord));
|
|
29299
|
+
await coalesceLayoutWork2(session, "capture", async () => {
|
|
29300
|
+
if (genAgeMs2(session, "resize") < RESIZE_GUARD_MS2)
|
|
29301
|
+
return;
|
|
29302
|
+
await captureGlobalLayoutOnDrag2(session);
|
|
29303
|
+
});
|
|
28562
29304
|
return;
|
|
28563
29305
|
}
|
|
28564
29306
|
if (subcommand === "quick-task") {
|