@sma1lboy/kobe 0.7.28 → 0.7.29
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 +1228 -751
- package/dist/web-ui/assets/AppShell-D7vN32jP.js +8 -0
- package/dist/web-ui/assets/{ChatTerminal-CcdiyDLi.js → ChatTerminal-Dzrtw3pW.js} +3 -3
- package/dist/web-ui/assets/IssuePeek-B4sEOPyN.css +1 -0
- package/dist/web-ui/assets/IssuePeek-C209KHal.js +184 -0
- package/dist/web-ui/assets/ViewToggle-eyNy-OIE.js +1 -0
- package/dist/web-ui/assets/board-Bf9WC1dC.js +1 -0
- package/dist/web-ui/assets/engines-Dg-YdbV1.js +1 -0
- package/dist/web-ui/assets/index-DfvJr9CT.js +10 -0
- package/dist/web-ui/assets/index-gdVt3uX3.css +2 -0
- package/dist/web-ui/assets/issues-CsoGRcf8.js +1 -0
- package/dist/web-ui/assets/routes-BdRb63td.js +1 -0
- package/dist/web-ui/assets/{tabs-7WK4MwGF.js → tabs-may5sub7.js} +1 -1
- package/dist/web-ui/assets/task._taskId-9HaGMHxf.js +1 -0
- package/dist/web-ui/assets/useNavigate-1MDuT9d4.js +1 -0
- package/dist/web-ui/index.html +4 -3
- 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.29",
|
|
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)
|
|
@@ -622,6 +666,7 @@ __export(exports_repos, {
|
|
|
622
666
|
setRepoInitOverride: () => setRepoInitOverride,
|
|
623
667
|
setPersistedString: () => setPersistedString,
|
|
624
668
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
669
|
+
resolveMainRepoRoot: () => resolveMainRepoRoot,
|
|
625
670
|
removeSavedRepo: () => removeSavedRepo,
|
|
626
671
|
remoteRepoKey: () => remoteRepoKey,
|
|
627
672
|
normalizeSavedRepos: () => normalizeSavedRepos,
|
|
@@ -657,6 +702,19 @@ function resolveRepoRoot(absPath) {
|
|
|
657
702
|
} catch {}
|
|
658
703
|
return top;
|
|
659
704
|
}
|
|
705
|
+
function resolveMainRepoRoot(absPath) {
|
|
706
|
+
if (isRemoteRepoKey(absPath))
|
|
707
|
+
return absPath;
|
|
708
|
+
const r = spawnSync3("git", ["worktree", "list", "--porcelain"], {
|
|
709
|
+
cwd: absPath,
|
|
710
|
+
encoding: "utf8",
|
|
711
|
+
shell: false
|
|
712
|
+
});
|
|
713
|
+
if (r.status !== 0)
|
|
714
|
+
return resolveRepoRoot(absPath);
|
|
715
|
+
const first = (r.stdout ?? "").split(/\r?\n/).find((line) => line.startsWith("worktree "))?.slice("worktree ".length).trim();
|
|
716
|
+
return first || resolveRepoRoot(absPath);
|
|
717
|
+
}
|
|
660
718
|
function statePath() {
|
|
661
719
|
return kvStatePath();
|
|
662
720
|
}
|
|
@@ -1323,6 +1381,7 @@ function coerceTask(value) {
|
|
|
1323
1381
|
vendor: isVendorId(v.vendor) ? v.vendor : DEFAULT_TASK_VENDOR,
|
|
1324
1382
|
prStatus: coercePRStatus(v.prStatus),
|
|
1325
1383
|
...typeof v.position === "number" && Number.isFinite(v.position) ? { position: v.position } : {},
|
|
1384
|
+
...typeof v.modelEffort === "string" && v.modelEffort.length > 0 ? { modelEffort: v.modelEffort } : {},
|
|
1326
1385
|
createdAt: v.createdAt,
|
|
1327
1386
|
updatedAt: v.updatedAt
|
|
1328
1387
|
};
|
|
@@ -3657,7 +3716,8 @@ class Orchestrator {
|
|
|
3657
3716
|
worktreePath: "",
|
|
3658
3717
|
status: "backlog",
|
|
3659
3718
|
kind: "task",
|
|
3660
|
-
vendor: input.vendor ?? DEFAULT_TASK_VENDOR
|
|
3719
|
+
vendor: input.vendor ?? DEFAULT_TASK_VENDOR,
|
|
3720
|
+
...input.modelEffort ? { modelEffort: input.modelEffort } : {}
|
|
3661
3721
|
});
|
|
3662
3722
|
if (input.baseRef)
|
|
3663
3723
|
this.pendingBaseRefs.set(task.id, input.baseRef);
|
|
@@ -3961,6 +4021,7 @@ function serializeTask(task) {
|
|
|
3961
4021
|
vendor: task.vendor,
|
|
3962
4022
|
prStatus: task.prStatus,
|
|
3963
4023
|
position: task.position,
|
|
4024
|
+
modelEffort: task.modelEffort,
|
|
3964
4025
|
createdAt: task.createdAt,
|
|
3965
4026
|
updatedAt: task.updatedAt
|
|
3966
4027
|
};
|
|
@@ -3973,6 +4034,7 @@ var DAEMON_PROTOCOL_VERSION = 3, MIN_COMPATIBLE_PROTOCOL_VERSION = 2, CHANNEL_NA
|
|
|
3973
4034
|
var init_protocol = __esm(() => {
|
|
3974
4035
|
CHANNEL_NAMES = [
|
|
3975
4036
|
"task.snapshot",
|
|
4037
|
+
"issue.snapshot",
|
|
3976
4038
|
"active-task",
|
|
3977
4039
|
"update",
|
|
3978
4040
|
"engine-state",
|
|
@@ -3980,7 +4042,6 @@ var init_protocol = __esm(() => {
|
|
|
3980
4042
|
"keybindings",
|
|
3981
4043
|
"task.jobs",
|
|
3982
4044
|
"worktree.changes",
|
|
3983
|
-
"task.conflicts",
|
|
3984
4045
|
"session.deliver"
|
|
3985
4046
|
];
|
|
3986
4047
|
CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
|
|
@@ -6313,6 +6374,7 @@ var init_registry = __esm(() => {
|
|
|
6313
6374
|
builtin: true,
|
|
6314
6375
|
displayName: "Codex",
|
|
6315
6376
|
defaultCommand: ["codex"],
|
|
6377
|
+
effortLevels: ["none", "low", "medium", "high", "xhigh"],
|
|
6316
6378
|
history: codexHistoryReader,
|
|
6317
6379
|
detectAccount: (deps) => detectCodexAccount(deps),
|
|
6318
6380
|
createHookAdapter: () => new NoopHookAdapter("codex"),
|
|
@@ -6893,347 +6955,6 @@ var init_auto_title_poller = __esm(() => {
|
|
|
6893
6955
|
init_chat_tab_naming();
|
|
6894
6956
|
});
|
|
6895
6957
|
|
|
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"];
|
|
7235
|
-
});
|
|
7236
|
-
|
|
7237
6958
|
// ../kobe-daemon/src/daemon/event-bus.ts
|
|
7238
6959
|
class DaemonEventBus {
|
|
7239
6960
|
last = new Map;
|
|
@@ -7466,7 +7187,8 @@ function createDaemonHandlerRegistry() {
|
|
|
7466
7187
|
title: optionalString(payload, "title"),
|
|
7467
7188
|
branch: optionalString(payload, "branch"),
|
|
7468
7189
|
baseRef: optionalString(payload, "baseRef"),
|
|
7469
|
-
vendor: optionalVendor(payload, "vendor")
|
|
7190
|
+
vendor: optionalVendor(payload, "vendor"),
|
|
7191
|
+
modelEffort: optionalString(payload, "effort")
|
|
7470
7192
|
});
|
|
7471
7193
|
return { taskId: task.id, task: serializeTask(task) };
|
|
7472
7194
|
}
|
|
@@ -7542,7 +7264,25 @@ function createDaemonHandlerRegistry() {
|
|
|
7542
7264
|
if (status !== "backlog" && status !== "in_progress" && status !== "in_review" && status !== "done" && status !== "canceled" && status !== "error") {
|
|
7543
7265
|
throw new Error("status must be a TaskStatus");
|
|
7544
7266
|
}
|
|
7267
|
+
const linked = status === "done" ? ctx.orch.getTask(taskId) : undefined;
|
|
7268
|
+
const prevStatus = linked?.status;
|
|
7545
7269
|
await ctx.orch.setStatus(taskId, status);
|
|
7270
|
+
if (status === "done" && prevStatus !== "done" && linked) {
|
|
7271
|
+
try {
|
|
7272
|
+
const state = await ctx.issues.list(linked.repo);
|
|
7273
|
+
const found = state.issues.find((i) => i.taskId === taskId);
|
|
7274
|
+
if (found && found.status !== "done") {
|
|
7275
|
+
const next = await ctx.issues.mutate(linked.repo, {
|
|
7276
|
+
type: "setStatus",
|
|
7277
|
+
id: found.id,
|
|
7278
|
+
status: "done"
|
|
7279
|
+
});
|
|
7280
|
+
ctx.bus.publish("issue.snapshot", next);
|
|
7281
|
+
}
|
|
7282
|
+
} catch (err) {
|
|
7283
|
+
logDaemonError("issue-done-mirror", err);
|
|
7284
|
+
}
|
|
7285
|
+
}
|
|
7546
7286
|
return {};
|
|
7547
7287
|
}
|
|
7548
7288
|
},
|
|
@@ -7645,6 +7385,20 @@ function createDaemonHandlerRegistry() {
|
|
|
7645
7385
|
return {};
|
|
7646
7386
|
}
|
|
7647
7387
|
},
|
|
7388
|
+
{
|
|
7389
|
+
name: "issue.list",
|
|
7390
|
+
async handle(payload, ctx) {
|
|
7391
|
+
return ctx.issues.list(requireString(payload, "repoRoot"));
|
|
7392
|
+
}
|
|
7393
|
+
},
|
|
7394
|
+
{
|
|
7395
|
+
name: "issue.mutate",
|
|
7396
|
+
async handle(payload, ctx) {
|
|
7397
|
+
const state = await ctx.issues.mutate(requireString(payload, "repoRoot"), payload.op);
|
|
7398
|
+
ctx.bus.publish("issue.snapshot", state);
|
|
7399
|
+
return state;
|
|
7400
|
+
}
|
|
7401
|
+
},
|
|
7648
7402
|
{
|
|
7649
7403
|
name: "session.deliver",
|
|
7650
7404
|
async handle(payload, ctx) {
|
|
@@ -7775,19 +7529,254 @@ var init_handlers = __esm(() => {
|
|
|
7775
7529
|
init_protocol();
|
|
7776
7530
|
});
|
|
7777
7531
|
|
|
7532
|
+
// ../kobe-daemon/src/daemon/issues-store.ts
|
|
7533
|
+
import { execFile } from "child_process";
|
|
7534
|
+
import { mkdir as mkdir6, readFile as readFile7, realpath, rename as rename2, stat as stat4, writeFile as writeFile4 } from "fs/promises";
|
|
7535
|
+
import { homedir as homedir13 } from "os";
|
|
7536
|
+
import { dirname as dirname5, isAbsolute, join as join4, resolve as resolve2 } from "path";
|
|
7537
|
+
import { promisify } from "util";
|
|
7538
|
+
function isGitNotRepositoryError(err) {
|
|
7539
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7540
|
+
return message.includes("not a git repository");
|
|
7541
|
+
}
|
|
7542
|
+
function defaultIssuesStorePath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir13()) {
|
|
7543
|
+
return join4(homeDir2, ".kobe", "issues.json");
|
|
7544
|
+
}
|
|
7545
|
+
function isValidStatus(value) {
|
|
7546
|
+
return typeof value === "string" && ISSUE_STATUSES.includes(value);
|
|
7547
|
+
}
|
|
7548
|
+
function normalizeIssue(entry) {
|
|
7549
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry))
|
|
7550
|
+
return null;
|
|
7551
|
+
const raw = entry;
|
|
7552
|
+
if (typeof raw.id !== "number")
|
|
7553
|
+
return null;
|
|
7554
|
+
return {
|
|
7555
|
+
id: raw.id,
|
|
7556
|
+
title: typeof raw.title === "string" ? raw.title : "(untitled)",
|
|
7557
|
+
status: isValidStatus(raw.status) ? raw.status : "open",
|
|
7558
|
+
created: typeof raw.created === "string" ? raw.created : "",
|
|
7559
|
+
body: typeof raw.body === "string" ? raw.body : "",
|
|
7560
|
+
taskId: typeof raw.taskId === "string" ? raw.taskId : undefined
|
|
7561
|
+
};
|
|
7562
|
+
}
|
|
7563
|
+
function emptyStore() {
|
|
7564
|
+
return { version: 1, repos: {} };
|
|
7565
|
+
}
|
|
7566
|
+
function todayStamp() {
|
|
7567
|
+
const d = new Date;
|
|
7568
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
7569
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
7570
|
+
return `${d.getFullYear()}-${mm}-${dd}`;
|
|
7571
|
+
}
|
|
7572
|
+
async function gitCommonDir(path11) {
|
|
7573
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "rev-parse", "--git-common-dir"]);
|
|
7574
|
+
const dir = stdout.trim();
|
|
7575
|
+
return realpath(isAbsolute(dir) ? dir : resolve2(path11, dir));
|
|
7576
|
+
}
|
|
7577
|
+
async function gitTopLevel(path11) {
|
|
7578
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "rev-parse", "--show-toplevel"]);
|
|
7579
|
+
return stdout.trim();
|
|
7580
|
+
}
|
|
7581
|
+
async function gitMainWorktree(path11) {
|
|
7582
|
+
const { stdout } = await execFileAsync("git", ["-C", path11, "worktree", "list", "--porcelain"]);
|
|
7583
|
+
const first = stdout.split(/\r?\n/).find((line) => line.startsWith("worktree "))?.slice("worktree ".length).trim();
|
|
7584
|
+
return first ? realpath(first) : gitTopLevel(path11);
|
|
7585
|
+
}
|
|
7586
|
+
async function resolveRepo(raw) {
|
|
7587
|
+
if (typeof raw !== "string" || raw.length === 0)
|
|
7588
|
+
throw new Error("repoRoot is required");
|
|
7589
|
+
const absolute = resolve2(raw);
|
|
7590
|
+
const s = await stat4(absolute).catch(() => null);
|
|
7591
|
+
if (!s?.isDirectory())
|
|
7592
|
+
throw new Error("repoRoot does not exist");
|
|
7593
|
+
try {
|
|
7594
|
+
const [repoRoot, repoKey] = await Promise.all([gitMainWorktree(absolute), gitCommonDir(absolute)]);
|
|
7595
|
+
return { repoRoot, repoKey };
|
|
7596
|
+
} catch (err) {
|
|
7597
|
+
if (isGitNotRepositoryError(err))
|
|
7598
|
+
throw new Error("repoRoot is not a git repository");
|
|
7599
|
+
throw err;
|
|
7600
|
+
}
|
|
7601
|
+
}
|
|
7602
|
+
async function readStore(path11) {
|
|
7603
|
+
try {
|
|
7604
|
+
const raw = JSON.parse(await readFile7(path11, "utf8"));
|
|
7605
|
+
const repos = {};
|
|
7606
|
+
if (raw.repos && typeof raw.repos === "object") {
|
|
7607
|
+
for (const [key, value] of Object.entries(raw.repos)) {
|
|
7608
|
+
if (!value || typeof value !== "object")
|
|
7609
|
+
continue;
|
|
7610
|
+
const record = value;
|
|
7611
|
+
repos[key] = {
|
|
7612
|
+
repoRoot: typeof record.repoRoot === "string" ? record.repoRoot : "",
|
|
7613
|
+
nextId: typeof record.nextId === "number" ? record.nextId : 1,
|
|
7614
|
+
issues: Array.isArray(record.issues) ? record.issues.map(normalizeIssue).filter((issue) => issue !== null) : []
|
|
7615
|
+
};
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
return { version: 1, repos };
|
|
7619
|
+
} catch (err) {
|
|
7620
|
+
if (err.code === "ENOENT")
|
|
7621
|
+
return emptyStore();
|
|
7622
|
+
throw err;
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
async function writeStore(path11, store) {
|
|
7626
|
+
await mkdir6(dirname5(path11), { recursive: true });
|
|
7627
|
+
const tmp = `${path11}.tmp`;
|
|
7628
|
+
await writeFile4(tmp, `${JSON.stringify(store, null, 2)}
|
|
7629
|
+
`, "utf8");
|
|
7630
|
+
await rename2(tmp, path11);
|
|
7631
|
+
}
|
|
7632
|
+
function response(repoRoot, record) {
|
|
7633
|
+
return {
|
|
7634
|
+
repoRoot,
|
|
7635
|
+
exists: record !== null,
|
|
7636
|
+
nextId: record?.nextId ?? 1,
|
|
7637
|
+
issues: record?.issues ?? []
|
|
7638
|
+
};
|
|
7639
|
+
}
|
|
7640
|
+
async function withLock(key, fn) {
|
|
7641
|
+
const tail = locks.get(key) ?? Promise.resolve();
|
|
7642
|
+
const run = tail.then(fn);
|
|
7643
|
+
const settled = run.then(() => {
|
|
7644
|
+
return;
|
|
7645
|
+
}, () => {
|
|
7646
|
+
return;
|
|
7647
|
+
});
|
|
7648
|
+
locks.set(key, settled);
|
|
7649
|
+
settled.then(() => {
|
|
7650
|
+
if (locks.get(key) === settled)
|
|
7651
|
+
locks.delete(key);
|
|
7652
|
+
});
|
|
7653
|
+
return run;
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
class IssuesStore {
|
|
7657
|
+
path;
|
|
7658
|
+
constructor(path11 = defaultIssuesStorePath()) {
|
|
7659
|
+
this.path = path11;
|
|
7660
|
+
}
|
|
7661
|
+
async list(repo) {
|
|
7662
|
+
const { repoRoot, repoKey } = await resolveRepo(repo);
|
|
7663
|
+
return withLock(repoKey, async () => {
|
|
7664
|
+
const store = await readStore(this.path);
|
|
7665
|
+
const record = store.repos[repoKey] ?? null;
|
|
7666
|
+
if (record && record.repoRoot !== repoRoot) {
|
|
7667
|
+
record.repoRoot = repoRoot;
|
|
7668
|
+
await writeStore(this.path, store);
|
|
7669
|
+
}
|
|
7670
|
+
return response(repoRoot, record);
|
|
7671
|
+
});
|
|
7672
|
+
}
|
|
7673
|
+
async mutate(repo, op) {
|
|
7674
|
+
const { repoRoot, repoKey } = await resolveRepo(repo);
|
|
7675
|
+
if (!op || typeof op !== "object" || Array.isArray(op) || typeof op.type !== "string") {
|
|
7676
|
+
throw new Error("missing op");
|
|
7677
|
+
}
|
|
7678
|
+
return withLock(repoKey, async () => {
|
|
7679
|
+
const store = await readStore(this.path);
|
|
7680
|
+
let record = store.repos[repoKey];
|
|
7681
|
+
if (!record) {
|
|
7682
|
+
record = { repoRoot, nextId: 1, issues: [] };
|
|
7683
|
+
store.repos[repoKey] = record;
|
|
7684
|
+
}
|
|
7685
|
+
record.repoRoot = repoRoot;
|
|
7686
|
+
const typed = op;
|
|
7687
|
+
if (typed.type === "create") {
|
|
7688
|
+
if (typeof typed.title !== "string" || typed.title.trim().length === 0) {
|
|
7689
|
+
throw new Error("create requires a non-empty title");
|
|
7690
|
+
}
|
|
7691
|
+
if (typed.body !== undefined && typeof typed.body !== "string")
|
|
7692
|
+
throw new Error("body must be a string");
|
|
7693
|
+
record.issues = [
|
|
7694
|
+
{
|
|
7695
|
+
id: record.nextId,
|
|
7696
|
+
title: typed.title,
|
|
7697
|
+
status: "open",
|
|
7698
|
+
created: todayStamp(),
|
|
7699
|
+
body: typeof typed.body === "string" ? typed.body : ""
|
|
7700
|
+
},
|
|
7701
|
+
...record.issues
|
|
7702
|
+
];
|
|
7703
|
+
record.nextId += 1;
|
|
7704
|
+
} else if (typed.type === "setStatus") {
|
|
7705
|
+
if (typeof typed.id !== "number")
|
|
7706
|
+
throw new Error("setStatus requires a numeric id");
|
|
7707
|
+
if (!isValidStatus(typed.status))
|
|
7708
|
+
throw new Error(`invalid status: must be one of ${ISSUE_STATUSES.join(", ")}`);
|
|
7709
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7710
|
+
if (!issue)
|
|
7711
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7712
|
+
issue.status = typed.status;
|
|
7713
|
+
} else if (typed.type === "update") {
|
|
7714
|
+
if (typeof typed.id !== "number")
|
|
7715
|
+
throw new Error("update requires a numeric id");
|
|
7716
|
+
if (typed.title !== undefined && (typeof typed.title !== "string" || typed.title.trim().length === 0)) {
|
|
7717
|
+
throw new Error("title must be a non-empty string");
|
|
7718
|
+
}
|
|
7719
|
+
if (typed.body !== undefined && typeof typed.body !== "string")
|
|
7720
|
+
throw new Error("body must be a string");
|
|
7721
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7722
|
+
if (!issue)
|
|
7723
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7724
|
+
if (typeof typed.title === "string")
|
|
7725
|
+
issue.title = typed.title;
|
|
7726
|
+
if (typeof typed.body === "string")
|
|
7727
|
+
issue.body = typed.body;
|
|
7728
|
+
} else if (typed.type === "link") {
|
|
7729
|
+
if (typeof typed.id !== "number")
|
|
7730
|
+
throw new Error("link requires a numeric id");
|
|
7731
|
+
if (typeof typed.taskId !== "string" || typed.taskId.length === 0) {
|
|
7732
|
+
throw new Error("link requires a non-empty taskId");
|
|
7733
|
+
}
|
|
7734
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7735
|
+
if (!issue)
|
|
7736
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7737
|
+
issue.taskId = typed.taskId;
|
|
7738
|
+
} else if (typed.type === "unlink") {
|
|
7739
|
+
if (typeof typed.id !== "number")
|
|
7740
|
+
throw new Error("unlink requires a numeric id");
|
|
7741
|
+
const issue = record.issues.find((i) => i.id === typed.id);
|
|
7742
|
+
if (!issue)
|
|
7743
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7744
|
+
issue.taskId = undefined;
|
|
7745
|
+
} else if (typed.type === "delete") {
|
|
7746
|
+
if (typeof typed.id !== "number")
|
|
7747
|
+
throw new Error("delete requires a numeric id");
|
|
7748
|
+
const nextIssues = record.issues.filter((i) => i.id !== typed.id);
|
|
7749
|
+
if (nextIssues.length === record.issues.length)
|
|
7750
|
+
throw new Error(`no issue #${typed.id}`);
|
|
7751
|
+
record.issues = nextIssues;
|
|
7752
|
+
} else {
|
|
7753
|
+
throw new Error(`unknown op type: ${typed.type}`);
|
|
7754
|
+
}
|
|
7755
|
+
await writeStore(this.path, store);
|
|
7756
|
+
return response(repoRoot, record);
|
|
7757
|
+
});
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7760
|
+
var execFileAsync, ISSUE_STATUSES, locks;
|
|
7761
|
+
var init_issues_store = __esm(() => {
|
|
7762
|
+
execFileAsync = promisify(execFile);
|
|
7763
|
+
ISSUE_STATUSES = ["open", "doing", "hold", "done"];
|
|
7764
|
+
locks = new Map;
|
|
7765
|
+
});
|
|
7766
|
+
|
|
7778
7767
|
// ../kobe-daemon/src/daemon/keybindings-watcher.ts
|
|
7779
7768
|
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
|
|
7769
|
+
import { homedir as homedir14 } from "os";
|
|
7770
|
+
import { basename as basename3, dirname as dirname6, join as join5 } from "path";
|
|
7771
|
+
function defaultKeybindingsPath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir14()) {
|
|
7772
|
+
return join5(homeDir2, ".kobe", "settings", "keybindings.yaml");
|
|
7784
7773
|
}
|
|
7785
7774
|
function startKeybindingsWatcher(bus, options = {}) {
|
|
7786
7775
|
const debounceMs = options.debounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS;
|
|
7787
7776
|
if (debounceMs <= 0)
|
|
7788
7777
|
return () => {};
|
|
7789
7778
|
const filePath = options.path ?? defaultKeybindingsPath();
|
|
7790
|
-
const dir =
|
|
7779
|
+
const dir = dirname6(filePath);
|
|
7791
7780
|
const baseYaml = basename3(filePath);
|
|
7792
7781
|
const baseYml = baseYaml.replace(/\.yaml$/, ".yml");
|
|
7793
7782
|
let rev = 0;
|
|
@@ -7831,10 +7820,10 @@ var init_keybindings_watcher = () => {};
|
|
|
7831
7820
|
|
|
7832
7821
|
// ../kobe-daemon/src/daemon/ui-prefs-watcher.ts
|
|
7833
7822
|
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
|
|
7823
|
+
import { homedir as homedir15 } from "os";
|
|
7824
|
+
import { basename as basename4, dirname as dirname7, join as join6 } from "path";
|
|
7825
|
+
function defaultUiPrefsStatePath(homeDir2 = process.env.KOBE_HOME_DIR ?? homedir15()) {
|
|
7826
|
+
return join6(homeDir2, ".config", "kobe", "state.json");
|
|
7838
7827
|
}
|
|
7839
7828
|
function readUiPrefsFromStateFile(statePath2) {
|
|
7840
7829
|
let parsed = {};
|
|
@@ -7858,7 +7847,7 @@ function startUiPrefsWatcher(bus, options = {}) {
|
|
|
7858
7847
|
if (debounceMs <= 0)
|
|
7859
7848
|
return () => {};
|
|
7860
7849
|
const statePath2 = options.statePath ?? defaultUiPrefsStatePath();
|
|
7861
|
-
const stateDir =
|
|
7850
|
+
const stateDir = dirname7(statePath2);
|
|
7862
7851
|
const stateFile = basename4(statePath2);
|
|
7863
7852
|
let last = readUiPrefsFromStateFile(statePath2);
|
|
7864
7853
|
bus.publish("ui-prefs", last);
|
|
@@ -7904,6 +7893,65 @@ var init_ui_prefs_watcher = __esm(() => {
|
|
|
7904
7893
|
FOCUS_ACCENT_SLOT_NAMES = ["primary", "success", "info"];
|
|
7905
7894
|
});
|
|
7906
7895
|
|
|
7896
|
+
// src/lib/poll-scheduling.ts
|
|
7897
|
+
import { spawn as spawn2 } from "child_process";
|
|
7898
|
+
function computeNextAllowedAt(startedAt, finishedAt, timedOut, cfg) {
|
|
7899
|
+
if (timedOut)
|
|
7900
|
+
return startedAt + cfg.slowRetryMs;
|
|
7901
|
+
return finishedAt + Math.max(cfg.minIntervalMs, (finishedAt - startedAt) * 5);
|
|
7902
|
+
}
|
|
7903
|
+
function shouldPoll(state, now) {
|
|
7904
|
+
return !state.inFlight && now >= state.nextAllowedAt;
|
|
7905
|
+
}
|
|
7906
|
+
function maybeStartScheduledRun(state, cfg, run, onValue) {
|
|
7907
|
+
const startedAt = Date.now();
|
|
7908
|
+
if (!shouldPoll(state, startedAt))
|
|
7909
|
+
return false;
|
|
7910
|
+
state.inFlight = true;
|
|
7911
|
+
const controller = new AbortController;
|
|
7912
|
+
const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
|
|
7913
|
+
(async () => {
|
|
7914
|
+
let value;
|
|
7915
|
+
let ok = false;
|
|
7916
|
+
try {
|
|
7917
|
+
value = await run(controller.signal);
|
|
7918
|
+
ok = true;
|
|
7919
|
+
} catch {}
|
|
7920
|
+
clearTimeout(timer);
|
|
7921
|
+
const timedOut = controller.signal.aborted;
|
|
7922
|
+
state.nextAllowedAt = computeNextAllowedAt(startedAt, Date.now(), timedOut, cfg);
|
|
7923
|
+
state.inFlight = false;
|
|
7924
|
+
if (ok && !timedOut)
|
|
7925
|
+
onValue(value);
|
|
7926
|
+
})();
|
|
7927
|
+
return true;
|
|
7928
|
+
}
|
|
7929
|
+
function spawnCapture(cmd, args, opts) {
|
|
7930
|
+
return new Promise((resolve3) => {
|
|
7931
|
+
let out = "";
|
|
7932
|
+
let settled = false;
|
|
7933
|
+
const finish = (status) => {
|
|
7934
|
+
if (settled)
|
|
7935
|
+
return;
|
|
7936
|
+
settled = true;
|
|
7937
|
+
resolve3({ status, stdout: out });
|
|
7938
|
+
};
|
|
7939
|
+
const child = spawn2(cmd, args.slice(), {
|
|
7940
|
+
cwd: opts.cwd,
|
|
7941
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
7942
|
+
env: opts.env,
|
|
7943
|
+
signal: opts.signal,
|
|
7944
|
+
killSignal: "SIGKILL"
|
|
7945
|
+
});
|
|
7946
|
+
child.stdout?.on("data", (chunk) => {
|
|
7947
|
+
out += String(chunk);
|
|
7948
|
+
});
|
|
7949
|
+
child.on("error", () => finish(null));
|
|
7950
|
+
child.on("close", (code) => finish(code));
|
|
7951
|
+
});
|
|
7952
|
+
}
|
|
7953
|
+
var init_poll_scheduling = () => {};
|
|
7954
|
+
|
|
7907
7955
|
// src/tui/panes/sidebar/worktree-changes.ts
|
|
7908
7956
|
var exports_worktree_changes = {};
|
|
7909
7957
|
__export(exports_worktree_changes, {
|
|
@@ -8074,9 +8122,9 @@ var init_worktree_changes_collector = __esm(() => {
|
|
|
8074
8122
|
});
|
|
8075
8123
|
|
|
8076
8124
|
// ../kobe-daemon/src/daemon/server.ts
|
|
8077
|
-
import { mkdir as
|
|
8125
|
+
import { mkdir as mkdir7, readFile as readFile8, unlink as unlink4, writeFile as writeFile5 } from "fs/promises";
|
|
8078
8126
|
import { createServer } from "net";
|
|
8079
|
-
import { dirname as
|
|
8127
|
+
import { dirname as dirname8 } from "path";
|
|
8080
8128
|
function resolveIdleGraceMs() {
|
|
8081
8129
|
const raw = process.env.KOBE_DAEMON_IDLE_GRACE_MS;
|
|
8082
8130
|
if (raw === undefined)
|
|
@@ -8131,8 +8179,9 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8131
8179
|
broadcast(clients, { type: "event", name: event.channel, payload: event.payload });
|
|
8132
8180
|
});
|
|
8133
8181
|
const activity = new DaemonActivityRegistry(bus);
|
|
8134
|
-
|
|
8135
|
-
await
|
|
8182
|
+
const issues = new IssuesStore(defaultIssuesStorePath(options.homeDir));
|
|
8183
|
+
await mkdir7(dirname8(socketPath), { recursive: true });
|
|
8184
|
+
await mkdir7(dirname8(pidPath), { recursive: true });
|
|
8136
8185
|
await unlink4(socketPath).catch(() => {});
|
|
8137
8186
|
const server = createServer((socket) => {
|
|
8138
8187
|
const client = {
|
|
@@ -8184,7 +8233,6 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8184
8233
|
debounceMs: options.keybindingsDebounceMs ?? DEFAULT_KEYBINDINGS_DEBOUNCE_MS
|
|
8185
8234
|
});
|
|
8186
8235
|
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);
|
|
8188
8236
|
const serverApi = {
|
|
8189
8237
|
socketPath,
|
|
8190
8238
|
pidPath,
|
|
@@ -8200,26 +8248,25 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8200
8248
|
stopUiPrefsWatcher();
|
|
8201
8249
|
stopKeybindingsWatcher();
|
|
8202
8250
|
stopWorktreeChangesCollector();
|
|
8203
|
-
stopConflictCollector();
|
|
8204
8251
|
activity.close();
|
|
8205
8252
|
broadcast(clients, { type: "event", name: "daemon.stopping", payload: {} });
|
|
8206
8253
|
for (const client of Array.from(clients)) {
|
|
8207
8254
|
client.socket.destroy();
|
|
8208
8255
|
}
|
|
8209
|
-
await new Promise((
|
|
8256
|
+
await new Promise((resolve3) => server.close(() => resolve3()));
|
|
8210
8257
|
await unlink4(socketPath).catch(() => {});
|
|
8211
8258
|
await unlink4(pidPath).catch(() => {});
|
|
8212
8259
|
}
|
|
8213
8260
|
};
|
|
8214
|
-
await new Promise((
|
|
8261
|
+
await new Promise((resolve3, reject) => {
|
|
8215
8262
|
const evented = server;
|
|
8216
8263
|
evented.once("error", reject);
|
|
8217
8264
|
server.listen(socketPath, () => {
|
|
8218
8265
|
evented.removeListener("error", reject);
|
|
8219
|
-
|
|
8266
|
+
resolve3();
|
|
8220
8267
|
});
|
|
8221
8268
|
});
|
|
8222
|
-
await
|
|
8269
|
+
await writeFile5(pidPath, `${process.pid}
|
|
8223
8270
|
`, "utf8");
|
|
8224
8271
|
async function stopSoon() {
|
|
8225
8272
|
if (stopping)
|
|
@@ -8264,6 +8311,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8264
8311
|
orch,
|
|
8265
8312
|
bus,
|
|
8266
8313
|
activity,
|
|
8314
|
+
issues,
|
|
8267
8315
|
daemon: { startedAt, socketPath, pid: process.pid, guiCount, stopSoon },
|
|
8268
8316
|
clientId: client.id
|
|
8269
8317
|
});
|
|
@@ -8304,7 +8352,7 @@ async function startDaemonServer(orch, options = {}) {
|
|
|
8304
8352
|
}
|
|
8305
8353
|
async function readPidFile(pidPath) {
|
|
8306
8354
|
try {
|
|
8307
|
-
const raw = await
|
|
8355
|
+
const raw = await readFile8(pidPath, "utf8");
|
|
8308
8356
|
const pid = Number(raw.trim());
|
|
8309
8357
|
return Number.isFinite(pid) ? pid : null;
|
|
8310
8358
|
} catch {
|
|
@@ -8331,14 +8379,15 @@ var init_server = __esm(() => {
|
|
|
8331
8379
|
init_version();
|
|
8332
8380
|
init_activity_registry();
|
|
8333
8381
|
init_auto_title_poller();
|
|
8334
|
-
init_conflict_collector();
|
|
8335
8382
|
init_handlers();
|
|
8383
|
+
init_issues_store();
|
|
8336
8384
|
init_keybindings_watcher();
|
|
8337
8385
|
init_paths2();
|
|
8338
8386
|
init_protocol();
|
|
8339
8387
|
init_ui_prefs_watcher();
|
|
8340
8388
|
init_worktree_changes_collector();
|
|
8341
8389
|
init_handlers();
|
|
8390
|
+
init_issues_store();
|
|
8342
8391
|
DEFAULT_UPDATE_POLL_MS = 6 * 60 * 60 * 1000;
|
|
8343
8392
|
});
|
|
8344
8393
|
|
|
@@ -8361,7 +8410,7 @@ async function stopDaemonProcess(socketPath, pidPath) {
|
|
|
8361
8410
|
const stopRequest = client.request("daemon.stop").catch(() => {
|
|
8362
8411
|
return;
|
|
8363
8412
|
});
|
|
8364
|
-
const stopTimeout = new Promise((
|
|
8413
|
+
const stopTimeout = new Promise((resolve3) => setTimeout(resolve3, 2000));
|
|
8365
8414
|
await Promise.race([stopRequest, stopTimeout]);
|
|
8366
8415
|
client.close();
|
|
8367
8416
|
if (wasAlive && targetPid !== null) {
|
|
@@ -8380,13 +8429,13 @@ async function stopDaemonProcess(socketPath, pidPath) {
|
|
|
8380
8429
|
method = "sigterm";
|
|
8381
8430
|
escalated = true;
|
|
8382
8431
|
}
|
|
8383
|
-
await new Promise((
|
|
8432
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
8384
8433
|
}
|
|
8385
8434
|
try {
|
|
8386
8435
|
process.kill(targetPid, 0);
|
|
8387
8436
|
process.kill(targetPid, "SIGKILL");
|
|
8388
8437
|
method = "sigkill";
|
|
8389
|
-
await new Promise((
|
|
8438
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
8390
8439
|
} catch {}
|
|
8391
8440
|
}
|
|
8392
8441
|
await unlink5(socketPath).catch(() => {});
|
|
@@ -8408,13 +8457,13 @@ __export(exports_daemon_process, {
|
|
|
8408
8457
|
});
|
|
8409
8458
|
import { spawn as spawn3 } from "child_process";
|
|
8410
8459
|
import { closeSync, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync } from "fs";
|
|
8411
|
-
import { dirname as
|
|
8460
|
+
import { dirname as dirname9, resolve as resolve3 } from "path";
|
|
8412
8461
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8413
8462
|
function spawnDetachedDaemon(command, args, env, logPath) {
|
|
8414
8463
|
let stdio = "ignore";
|
|
8415
8464
|
let logFd;
|
|
8416
8465
|
try {
|
|
8417
|
-
mkdirSync4(
|
|
8466
|
+
mkdirSync4(dirname9(logPath), { recursive: true });
|
|
8418
8467
|
logFd = openSync(logPath, "a");
|
|
8419
8468
|
stdio = ["ignore", logFd, logFd];
|
|
8420
8469
|
} catch {
|
|
@@ -8467,8 +8516,8 @@ async function testDaemonResponds(socketPath, timeoutMs = DAEMON_HELLO_TIMEOUT_M
|
|
|
8467
8516
|
}
|
|
8468
8517
|
const replied = probe2.request("hello", { protocolVersion: DAEMON_PROTOCOL_VERSION }).then(() => true).catch(() => true);
|
|
8469
8518
|
let timer;
|
|
8470
|
-
const timedOut = new Promise((
|
|
8471
|
-
timer = setTimeout(() =>
|
|
8519
|
+
const timedOut = new Promise((resolve4) => {
|
|
8520
|
+
timer = setTimeout(() => resolve4(false), timeoutMs);
|
|
8472
8521
|
});
|
|
8473
8522
|
const alive = await Promise.race([replied, timedOut]);
|
|
8474
8523
|
if (timer)
|
|
@@ -8481,11 +8530,11 @@ function resolveKobeSpawn(subcommand) {
|
|
|
8481
8530
|
if (here.startsWith("/$bunfs") || here.startsWith("B:\\~BUN")) {
|
|
8482
8531
|
return [process.execPath, ...subcommand];
|
|
8483
8532
|
}
|
|
8484
|
-
const dir =
|
|
8533
|
+
const dir = dirname9(here);
|
|
8485
8534
|
const candidates = [
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8535
|
+
resolve3(dir, "../cli/index.ts"),
|
|
8536
|
+
resolve3(dir, "../../../kobe/src/cli/index.ts"),
|
|
8537
|
+
resolve3(dir, "../cli/index.js")
|
|
8489
8538
|
];
|
|
8490
8539
|
const entry = candidates.find((candidate) => existsSync5(candidate));
|
|
8491
8540
|
if (entry)
|
|
@@ -8507,7 +8556,7 @@ __export(exports_repo_cmd, {
|
|
|
8507
8556
|
runRepoSubcommand: () => runRepoSubcommand
|
|
8508
8557
|
});
|
|
8509
8558
|
import { readFileSync as readFileSync4 } from "fs";
|
|
8510
|
-
import { resolve as
|
|
8559
|
+
import { resolve as resolve4 } from "path";
|
|
8511
8560
|
function usageError(message) {
|
|
8512
8561
|
process.stderr.write(`kobe repo: ${message}
|
|
8513
8562
|
|
|
@@ -8517,7 +8566,7 @@ ${REPO_USAGE}
|
|
|
8517
8566
|
}
|
|
8518
8567
|
function readArgFile(path11) {
|
|
8519
8568
|
try {
|
|
8520
|
-
return readFileSync4(
|
|
8569
|
+
return readFileSync4(resolve4(process.cwd(), path11), "utf8");
|
|
8521
8570
|
} catch (err) {
|
|
8522
8571
|
usageError(`cannot read ${path11}: ${err instanceof Error ? err.message : String(err)}`);
|
|
8523
8572
|
}
|
|
@@ -8580,13 +8629,13 @@ async function runRepoSubcommand(args) {
|
|
|
8580
8629
|
}
|
|
8581
8630
|
const { getRepoInitOverride: getRepoInitOverride2, setRepoInitOverride: setRepoInitOverride2, resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
8582
8631
|
const { existsSync: existsSync6 } = await import("fs");
|
|
8583
|
-
const { join:
|
|
8632
|
+
const { join: join7 } = await import("path");
|
|
8584
8633
|
if (verb === "show") {
|
|
8585
8634
|
const [pathArg] = rest.filter((a) => !a.startsWith("-"));
|
|
8586
|
-
const repo = resolveRepoRoot2(
|
|
8635
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), pathArg ?? "."));
|
|
8587
8636
|
const override = getRepoInitOverride2(repo);
|
|
8588
|
-
const hasFileScript = existsSync6(
|
|
8589
|
-
const hasFilePrompt = existsSync6(
|
|
8637
|
+
const hasFileScript = existsSync6(join7(repo, ".kobe", "init.sh"));
|
|
8638
|
+
const hasFilePrompt = existsSync6(join7(repo, ".kobe", "init-prompt.md"));
|
|
8590
8639
|
console.log(`repo: ${repo}`);
|
|
8591
8640
|
console.log(` .kobe/init.sh: ${hasFileScript ? "present (wins)" : "absent"}`);
|
|
8592
8641
|
console.log(` .kobe/init-prompt.md: ${hasFilePrompt ? "present (wins)" : "absent"}`);
|
|
@@ -8599,7 +8648,7 @@ async function runRepoSubcommand(args) {
|
|
|
8599
8648
|
if (flags.initScript === undefined && flags.initPrompt === undefined) {
|
|
8600
8649
|
usageError("set needs at least one of --init-script(-file) / --init-prompt(-file)");
|
|
8601
8650
|
}
|
|
8602
|
-
const repo = resolveRepoRoot2(
|
|
8651
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), flags.path ?? "."));
|
|
8603
8652
|
const next = setRepoInitOverride2(repo, {
|
|
8604
8653
|
...flags.initScript !== undefined ? { initScript: flags.initScript } : {},
|
|
8605
8654
|
...flags.initPrompt !== undefined ? { initPrompt: flags.initPrompt } : {}
|
|
@@ -8611,7 +8660,7 @@ async function runRepoSubcommand(args) {
|
|
|
8611
8660
|
}
|
|
8612
8661
|
if (verb === "unset") {
|
|
8613
8662
|
const { path: path11, clearScript, clearPrompt } = parseUnsetArgs(rest);
|
|
8614
|
-
const repo = resolveRepoRoot2(
|
|
8663
|
+
const repo = resolveRepoRoot2(resolve4(process.cwd(), path11 ?? "."));
|
|
8615
8664
|
const next = setRepoInitOverride2(repo, {
|
|
8616
8665
|
...clearScript ? { initScript: "" } : {},
|
|
8617
8666
|
...clearPrompt ? { initPrompt: "" } : {}
|
|
@@ -8684,15 +8733,30 @@ function parseEngineCommand(command) {
|
|
|
8684
8733
|
}
|
|
8685
8734
|
return out;
|
|
8686
8735
|
}
|
|
8687
|
-
function interactiveEngineCommand(vendor) {
|
|
8736
|
+
function interactiveEngineCommand(vendor, effort) {
|
|
8688
8737
|
const v = vendor ?? "claude";
|
|
8689
8738
|
const override = getPersistedString(engineCommandKey(v))?.trim();
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8739
|
+
const base = (() => {
|
|
8740
|
+
if (override) {
|
|
8741
|
+
const argv = parseEngineCommand(override);
|
|
8742
|
+
if (argv.length > 0)
|
|
8743
|
+
return argv;
|
|
8744
|
+
}
|
|
8745
|
+
return defaultEngineCommand(v);
|
|
8746
|
+
})();
|
|
8747
|
+
return withEngineEffort(base, v, effort);
|
|
8748
|
+
}
|
|
8749
|
+
function withEngineEffort(argv, vendor, effort) {
|
|
8750
|
+
const trimmed = effort?.trim();
|
|
8751
|
+
if (!trimmed)
|
|
8752
|
+
return argv;
|
|
8753
|
+
const v = vendor ?? "claude";
|
|
8754
|
+
const levels = engineEntry(v).effortLevels;
|
|
8755
|
+
if (!levels?.includes(trimmed))
|
|
8756
|
+
return argv;
|
|
8757
|
+
if (v === "codex")
|
|
8758
|
+
return [...argv, "-c", `model_reasoning_effort=${trimmed}`];
|
|
8759
|
+
return argv;
|
|
8696
8760
|
}
|
|
8697
8761
|
function withClaudeSessionId(argv, vendor) {
|
|
8698
8762
|
if ((vendor ?? "claude") !== "claude")
|
|
@@ -8929,7 +8993,7 @@ async function deliverFirstPrompt(session, prompt) {
|
|
|
8929
8993
|
return;
|
|
8930
8994
|
await pasteAndSubmit(pane, prompt);
|
|
8931
8995
|
}
|
|
8932
|
-
var sleep = (ms) => new Promise((
|
|
8996
|
+
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8933
8997
|
var init_prompt_delivery = __esm(() => {
|
|
8934
8998
|
init_client2();
|
|
8935
8999
|
});
|
|
@@ -10837,7 +10901,7 @@ function normalizeHex(value) {
|
|
|
10837
10901
|
}
|
|
10838
10902
|
function resolveThemeSlotHex(theme, slot, mode = "dark") {
|
|
10839
10903
|
const defs = theme.defs ?? {};
|
|
10840
|
-
function
|
|
10904
|
+
function resolve5(c, chain) {
|
|
10841
10905
|
if (typeof c === "string") {
|
|
10842
10906
|
if (c === "transparent" || c === "none")
|
|
10843
10907
|
return null;
|
|
@@ -10848,17 +10912,17 @@ function resolveThemeSlotHex(theme, slot, mode = "dark") {
|
|
|
10848
10912
|
const next = defs[c] ?? theme.theme[c];
|
|
10849
10913
|
if (next === undefined)
|
|
10850
10914
|
return null;
|
|
10851
|
-
return
|
|
10915
|
+
return resolve5(next, [...chain, c]);
|
|
10852
10916
|
}
|
|
10853
10917
|
if (!c || typeof c !== "object")
|
|
10854
10918
|
return null;
|
|
10855
10919
|
const variant = c[mode];
|
|
10856
|
-
return typeof variant === "string" ?
|
|
10920
|
+
return typeof variant === "string" ? resolve5(variant, chain) : null;
|
|
10857
10921
|
}
|
|
10858
10922
|
const value = theme.theme[slot];
|
|
10859
10923
|
if (value === undefined)
|
|
10860
10924
|
return null;
|
|
10861
|
-
return
|
|
10925
|
+
return resolve5(value, [slot]);
|
|
10862
10926
|
}
|
|
10863
10927
|
|
|
10864
10928
|
// src/tui/context/theme/schema.ts
|
|
@@ -10913,9 +10977,9 @@ var init_schema = () => {};
|
|
|
10913
10977
|
|
|
10914
10978
|
// src/tui/context/theme/loader.ts
|
|
10915
10979
|
import { readFileSync as readFileSync6, readdirSync } from "fs";
|
|
10916
|
-
import { join as
|
|
10980
|
+
import { join as join7 } from "path";
|
|
10917
10981
|
function userThemesDir() {
|
|
10918
|
-
return
|
|
10982
|
+
return join7(kobeStateDir(), "themes");
|
|
10919
10983
|
}
|
|
10920
10984
|
function loadUserThemes() {
|
|
10921
10985
|
const dir = userThemesDir();
|
|
@@ -10929,7 +10993,7 @@ function loadUserThemes() {
|
|
|
10929
10993
|
for (const file of entries) {
|
|
10930
10994
|
if (!file.endsWith(".json"))
|
|
10931
10995
|
continue;
|
|
10932
|
-
const path11 =
|
|
10996
|
+
const path11 = join7(dir, file);
|
|
10933
10997
|
let parsed;
|
|
10934
10998
|
try {
|
|
10935
10999
|
const text = readFileSync6(path11, "utf8");
|
|
@@ -11745,12 +11809,12 @@ __export(exports_repo_init, {
|
|
|
11745
11809
|
resolveRepoInit: () => resolveRepoInit
|
|
11746
11810
|
});
|
|
11747
11811
|
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "fs";
|
|
11748
|
-
import { join as
|
|
11812
|
+
import { join as join8 } from "path";
|
|
11749
11813
|
function repoFileScript(worktreePath) {
|
|
11750
|
-
return existsSync7(
|
|
11814
|
+
return existsSync7(join8(worktreePath, INIT_SCRIPT_REL)) ? `sh ${INIT_SCRIPT_REL}` : undefined;
|
|
11751
11815
|
}
|
|
11752
11816
|
function repoFilePrompt(worktreePath) {
|
|
11753
|
-
const p =
|
|
11817
|
+
const p = join8(worktreePath, INIT_PROMPT_REL);
|
|
11754
11818
|
if (!existsSync7(p))
|
|
11755
11819
|
return;
|
|
11756
11820
|
try {
|
|
@@ -11772,8 +11836,8 @@ function resolveRepoInit(repoRoot, worktreePath) {
|
|
|
11772
11836
|
var INIT_SCRIPT_REL, INIT_PROMPT_REL;
|
|
11773
11837
|
var init_repo_init = __esm(() => {
|
|
11774
11838
|
init_repos();
|
|
11775
|
-
INIT_SCRIPT_REL =
|
|
11776
|
-
INIT_PROMPT_REL =
|
|
11839
|
+
INIT_SCRIPT_REL = join8(".kobe", "init.sh");
|
|
11840
|
+
INIT_PROMPT_REL = join8(".kobe", "init-prompt.md");
|
|
11777
11841
|
});
|
|
11778
11842
|
|
|
11779
11843
|
// src/cli/api-cmd.ts
|
|
@@ -11800,7 +11864,7 @@ __export(exports_api_cmd, {
|
|
|
11800
11864
|
API_VERBS: () => API_VERBS,
|
|
11801
11865
|
API_SCHEMA_VERSION: () => API_SCHEMA_VERSION
|
|
11802
11866
|
});
|
|
11803
|
-
import { resolve as
|
|
11867
|
+
import { resolve as resolve5 } from "path";
|
|
11804
11868
|
function groupOf(verbName) {
|
|
11805
11869
|
for (const [group, names] of Object.entries(VERB_GROUPS)) {
|
|
11806
11870
|
if (names.includes(verbName))
|
|
@@ -11965,10 +12029,10 @@ class VerbArgs {
|
|
|
11965
12029
|
}
|
|
11966
12030
|
path(name) {
|
|
11967
12031
|
const v = this.str(name);
|
|
11968
|
-
return v === undefined ? undefined :
|
|
12032
|
+
return v === undefined ? undefined : resolve5(process.cwd(), v);
|
|
11969
12033
|
}
|
|
11970
12034
|
requirePath(name) {
|
|
11971
|
-
return
|
|
12035
|
+
return resolve5(process.cwd(), this.require(name));
|
|
11972
12036
|
}
|
|
11973
12037
|
}
|
|
11974
12038
|
function parseAgentsSpec(spec) {
|
|
@@ -12154,10 +12218,22 @@ function daemonOf(ctx) {
|
|
|
12154
12218
|
async function simpleRpc(ctx, name, payload) {
|
|
12155
12219
|
return daemonOf(ctx).request(name, payload);
|
|
12156
12220
|
}
|
|
12221
|
+
async function issueUpdate(ctx) {
|
|
12222
|
+
const title = ctx.args.str("title");
|
|
12223
|
+
const body = ctx.args.str("body");
|
|
12224
|
+
if (title === undefined && body === undefined) {
|
|
12225
|
+
throw new ApiError("issue-update requires --title and/or --body", "MISSING_FLAG");
|
|
12226
|
+
}
|
|
12227
|
+
return simpleRpc(ctx, "issue.mutate", {
|
|
12228
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12229
|
+
op: { type: "update", id: ctx.args.int("id"), title, body }
|
|
12230
|
+
});
|
|
12231
|
+
}
|
|
12157
12232
|
async function add(ctx) {
|
|
12158
12233
|
const daemon = daemonOf(ctx);
|
|
12159
|
-
const { args } = ctx;
|
|
12160
|
-
const
|
|
12234
|
+
const { args, runtime } = ctx;
|
|
12235
|
+
const repo = await runtime.resolveRepoRoot(args.requirePath("repo"));
|
|
12236
|
+
const payload = { repo };
|
|
12161
12237
|
const title = args.str("title");
|
|
12162
12238
|
if (title)
|
|
12163
12239
|
payload.title = title;
|
|
@@ -12167,11 +12243,12 @@ async function add(ctx) {
|
|
|
12167
12243
|
const baseRef = args.str("base-branch");
|
|
12168
12244
|
if (baseRef)
|
|
12169
12245
|
payload.baseRef = baseRef;
|
|
12170
|
-
const vendor = args.vendor();
|
|
12246
|
+
const vendor = args.vendor() ?? await runtime.defaultVendor();
|
|
12171
12247
|
if (vendor)
|
|
12172
12248
|
payload.vendor = vendor;
|
|
12173
12249
|
const res = await daemon.request("task.create", payload);
|
|
12174
12250
|
const taskId = res.taskId;
|
|
12251
|
+
await daemon.request("task.setActive", { taskId });
|
|
12175
12252
|
const status = args.enumOf("status");
|
|
12176
12253
|
if (status)
|
|
12177
12254
|
await daemon.request("task.status", { taskId, status });
|
|
@@ -12186,6 +12263,7 @@ async function add(ctx) {
|
|
|
12186
12263
|
if (!prompt)
|
|
12187
12264
|
return { taskId, task, started: false };
|
|
12188
12265
|
const delivered = await ctx.runtime.deliverPrompt(daemon, { id: taskId, worktreePath: task.worktreePath, vendor: task.vendor, repo: task.repo }, prompt);
|
|
12266
|
+
task = (await daemon.request("task.get", { taskId })).task;
|
|
12189
12267
|
return { taskId, task, started: delivered.started, engineReady: delivered.engineReady, session: delivered.session };
|
|
12190
12268
|
}
|
|
12191
12269
|
async function send(ctx) {
|
|
@@ -12281,13 +12359,14 @@ async function adopt(ctx) {
|
|
|
12281
12359
|
}
|
|
12282
12360
|
async function fanOut(ctx) {
|
|
12283
12361
|
const daemon = daemonOf(ctx);
|
|
12284
|
-
const { args } = ctx;
|
|
12285
|
-
const repo = args.requirePath("repo");
|
|
12362
|
+
const { args, runtime } = ctx;
|
|
12363
|
+
const repo = await runtime.resolveRepoRoot(args.requirePath("repo"));
|
|
12286
12364
|
const prompt = args.require("prompt");
|
|
12287
12365
|
const title = args.str("title");
|
|
12288
12366
|
const baseRef = args.str("base-branch");
|
|
12289
12367
|
const agentsSpec = args.str("agents");
|
|
12290
|
-
const
|
|
12368
|
+
const defaultVendor = await runtime.defaultVendor();
|
|
12369
|
+
const plan = agentsSpec ? parseAgentsSpec(agentsSpec) : new Array(args.int("count") ?? 1).fill(args.vendor() ?? defaultVendor ?? "claude");
|
|
12291
12370
|
if (plan.length > FANOUT_CAP) {
|
|
12292
12371
|
throw new ApiError(`fan-out of ${plan.length} exceeds the cap of ${FANOUT_CAP} \u2014 spawn in batches`, "BAD_FLAG");
|
|
12293
12372
|
}
|
|
@@ -12422,7 +12501,7 @@ ${apiUsage()}`, "BAD_VERB", 2);
|
|
|
12422
12501
|
session?.close();
|
|
12423
12502
|
}
|
|
12424
12503
|
}
|
|
12425
|
-
var API_SCHEMA_VERSION = 2, FANOUT_CAP = 10, TASK_STATUSES, ApiError, F, VERB_ALIASES, VERB_GROUPS, VERBS, API_VERBS, GLOBAL_FLAGS, realPromptDeliveryOps, defaultApiRuntime;
|
|
12504
|
+
var API_SCHEMA_VERSION = 2, FANOUT_CAP = 10, TASK_STATUSES, ISSUE_STATUSES2, ApiError, F, VERB_ALIASES, VERB_GROUPS, VERBS, API_VERBS, GLOBAL_FLAGS, realPromptDeliveryOps, defaultApiRuntime;
|
|
12426
12505
|
var init_api_cmd = __esm(() => {
|
|
12427
12506
|
init_interactive_command();
|
|
12428
12507
|
init_feedback();
|
|
@@ -12432,6 +12511,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12432
12511
|
init_version();
|
|
12433
12512
|
init_daemon_session();
|
|
12434
12513
|
TASK_STATUSES = ["backlog", "in_progress", "in_review", "done", "canceled", "error"];
|
|
12514
|
+
ISSUE_STATUSES2 = ["open", "doing", "hold", "done"];
|
|
12435
12515
|
ApiError = class ApiError extends Error {
|
|
12436
12516
|
code;
|
|
12437
12517
|
constructor(message, code) {
|
|
@@ -12477,6 +12557,7 @@ var init_api_cmd = __esm(() => {
|
|
|
12477
12557
|
create: ["add", "fan-out"],
|
|
12478
12558
|
drive: ["send", "dispatch", "note", "set-active"],
|
|
12479
12559
|
edit: ["rename", "set-branch", "set-vendor", "set-status"],
|
|
12560
|
+
issues: ["issue-list", "issue-create", "issue-set-status", "issue-update"],
|
|
12480
12561
|
lifecycle: ["archive", "pin", "delete"],
|
|
12481
12562
|
worktree: ["ensure-worktree", "adopt", "discover-adoptable"],
|
|
12482
12563
|
feedback: ["feedback"]
|
|
@@ -12593,6 +12674,49 @@ var init_api_cmd = __esm(() => {
|
|
|
12593
12674
|
offline: true,
|
|
12594
12675
|
handler: feedback
|
|
12595
12676
|
},
|
|
12677
|
+
{
|
|
12678
|
+
name: "issue-list",
|
|
12679
|
+
summary: "List daemon-owned issues for a repo.",
|
|
12680
|
+
flags: [F.repo()],
|
|
12681
|
+
handler: (ctx) => simpleRpc(ctx, "issue.list", { repoRoot: ctx.args.requirePath("repo") })
|
|
12682
|
+
},
|
|
12683
|
+
{
|
|
12684
|
+
name: "issue-create",
|
|
12685
|
+
summary: "Create a daemon-owned issue for a repo.",
|
|
12686
|
+
flags: [
|
|
12687
|
+
F.repo(),
|
|
12688
|
+
{ name: "title", type: "string", required: true, placeholder: "T", description: "Issue title." },
|
|
12689
|
+
{ name: "body", type: "string", placeholder: "TEXT", description: "Issue body." }
|
|
12690
|
+
],
|
|
12691
|
+
handler: (ctx) => simpleRpc(ctx, "issue.mutate", {
|
|
12692
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12693
|
+
op: { type: "create", title: ctx.args.require("title"), body: ctx.args.str("body") }
|
|
12694
|
+
})
|
|
12695
|
+
},
|
|
12696
|
+
{
|
|
12697
|
+
name: "issue-set-status",
|
|
12698
|
+
summary: "Set a daemon-owned issue's status.",
|
|
12699
|
+
flags: [
|
|
12700
|
+
F.repo(),
|
|
12701
|
+
{ name: "id", type: "int", required: true, placeholder: "N", description: "Issue id." },
|
|
12702
|
+
{ name: "status", type: "enum", required: true, values: ISSUE_STATUSES2, description: "New issue status." }
|
|
12703
|
+
],
|
|
12704
|
+
handler: (ctx) => simpleRpc(ctx, "issue.mutate", {
|
|
12705
|
+
repoRoot: ctx.args.requirePath("repo"),
|
|
12706
|
+
op: { type: "setStatus", id: ctx.args.int("id"), status: ctx.args.requireEnum("status") }
|
|
12707
|
+
})
|
|
12708
|
+
},
|
|
12709
|
+
{
|
|
12710
|
+
name: "issue-update",
|
|
12711
|
+
summary: "Update a daemon-owned issue's title and/or body.",
|
|
12712
|
+
flags: [
|
|
12713
|
+
F.repo(),
|
|
12714
|
+
{ name: "id", type: "int", required: true, placeholder: "N", description: "Issue id." },
|
|
12715
|
+
{ name: "title", type: "string", placeholder: "T", description: "New title." },
|
|
12716
|
+
{ name: "body", type: "string", placeholder: "TEXT", description: "New body." }
|
|
12717
|
+
],
|
|
12718
|
+
handler: issueUpdate
|
|
12719
|
+
},
|
|
12596
12720
|
{
|
|
12597
12721
|
name: "collect",
|
|
12598
12722
|
summary: "Read-only comparison snapshot of several tasks (identity, branch, .running, uncommitted .changes).",
|
|
@@ -12718,7 +12842,12 @@ var init_api_cmd = __esm(() => {
|
|
|
12718
12842
|
defaultApiRuntime = {
|
|
12719
12843
|
isTaskRunning: (taskId) => sessionExists(tmuxSessionName(taskId)),
|
|
12720
12844
|
deliverPrompt: (client, target, prompt) => deliverPrompt(client, target, prompt),
|
|
12721
|
-
resolveRepoRoot: async (absPath) => (await Promise.resolve().then(() => (init_repos(), exports_repos))).
|
|
12845
|
+
resolveRepoRoot: async (absPath) => (await Promise.resolve().then(() => (init_repos(), exports_repos))).resolveMainRepoRoot(absPath),
|
|
12846
|
+
defaultVendor: async () => {
|
|
12847
|
+
const { getPersistedString: getPersistedString2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
12848
|
+
const value = getPersistedString2("lastSelectedVendor")?.trim();
|
|
12849
|
+
return value ? value : undefined;
|
|
12850
|
+
},
|
|
12722
12851
|
readWorktreeChanges: async (worktreePath) => (await Promise.resolve().then(() => (init_worktree_changes(), exports_worktree_changes))).readWorktreeChanges(worktreePath),
|
|
12723
12852
|
tearDownSession: async (taskId) => {
|
|
12724
12853
|
const session = tmuxSessionName(taskId);
|
|
@@ -12820,7 +12949,7 @@ __export(exports_theme, {
|
|
|
12820
12949
|
runThemeSubcommand: () => runThemeSubcommand
|
|
12821
12950
|
});
|
|
12822
12951
|
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync9, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
12823
|
-
import { basename as basename5, join as
|
|
12952
|
+
import { basename as basename5, join as join9, resolve as resolve6 } from "path";
|
|
12824
12953
|
function fail3(message) {
|
|
12825
12954
|
process.stderr.write(`kobe theme: ${message}
|
|
12826
12955
|
`);
|
|
@@ -12851,7 +12980,7 @@ function listThemes() {
|
|
|
12851
12980
|
} else {
|
|
12852
12981
|
for (const f of userFiles) {
|
|
12853
12982
|
const name = f.slice(0, -".json".length);
|
|
12854
|
-
const path11 =
|
|
12983
|
+
const path11 = join9(dir, f);
|
|
12855
12984
|
const overridesBundled = BUNDLED_NAMES.includes(name) ? " (overrides built-in)" : "";
|
|
12856
12985
|
lines.push(` ${name}${overridesBundled} ${path11}`);
|
|
12857
12986
|
}
|
|
@@ -12877,7 +13006,7 @@ async function readSource(source) {
|
|
|
12877
13006
|
const defaultName2 = file2.endsWith(".json") ? file2.slice(0, -".json".length) : file2;
|
|
12878
13007
|
return { text: text2, defaultName: defaultName2 };
|
|
12879
13008
|
}
|
|
12880
|
-
const abs =
|
|
13009
|
+
const abs = resolve6(process.cwd(), source);
|
|
12881
13010
|
let text;
|
|
12882
13011
|
try {
|
|
12883
13012
|
text = readFileSync9(abs, "utf8");
|
|
@@ -12941,7 +13070,7 @@ async function addTheme(args) {
|
|
|
12941
13070
|
}
|
|
12942
13071
|
const dir = userThemesDir();
|
|
12943
13072
|
mkdirSync5(dir, { recursive: true });
|
|
12944
|
-
const dest =
|
|
13073
|
+
const dest = join9(dir, `${name}.json`);
|
|
12945
13074
|
if (existsSync8(dest) && !opts.force) {
|
|
12946
13075
|
fail3(`${dest} already exists (pass --force to overwrite)`);
|
|
12947
13076
|
}
|
|
@@ -12959,7 +13088,7 @@ function removeTheme(args) {
|
|
|
12959
13088
|
if (BUNDLED_NAMES.includes(name)) {
|
|
12960
13089
|
fail3(`"${name}" is a built-in theme and cannot be removed`);
|
|
12961
13090
|
}
|
|
12962
|
-
const dest =
|
|
13091
|
+
const dest = join9(userThemesDir(), `${name}.json`);
|
|
12963
13092
|
if (!existsSync8(dest)) {
|
|
12964
13093
|
fail3(`no user theme named "${name}" (looked for ${dest})`);
|
|
12965
13094
|
}
|
|
@@ -13120,9 +13249,9 @@ var init_feedback_cmd = __esm(() => {
|
|
|
13120
13249
|
});
|
|
13121
13250
|
|
|
13122
13251
|
// src/core/index.ts
|
|
13123
|
-
import { homedir as
|
|
13252
|
+
import { homedir as homedir16 } from "os";
|
|
13124
13253
|
async function createKobeCore(options = {}) {
|
|
13125
|
-
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ??
|
|
13254
|
+
const homeDir2 = options.homeDir ?? process.env.KOBE_HOME_DIR ?? homedir16();
|
|
13126
13255
|
const store = new TaskIndexStore({ homeDir: homeDir2 });
|
|
13127
13256
|
await store.load();
|
|
13128
13257
|
const worktrees = new GitWorktreeManager;
|
|
@@ -13240,8 +13369,8 @@ var init_daemon_cmd = __esm(() => {
|
|
|
13240
13369
|
|
|
13241
13370
|
// src/lib/skill-install.ts
|
|
13242
13371
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "fs";
|
|
13243
|
-
import { homedir as
|
|
13244
|
-
import { join as
|
|
13372
|
+
import { homedir as homedir17 } from "os";
|
|
13373
|
+
import { join as join10 } from "path";
|
|
13245
13374
|
function npxSkillsArgv(opts = {}) {
|
|
13246
13375
|
return ["skills", "add", SKILL_SOURCE_SLUG, "--skill", "kobe", "--agent", opts.agent ?? DEFAULT_SKILL_AGENT];
|
|
13247
13376
|
}
|
|
@@ -13249,9 +13378,9 @@ function npxSkillsCommand(opts = {}) {
|
|
|
13249
13378
|
return `npx ${npxSkillsArgv(opts).join(" ")}`;
|
|
13250
13379
|
}
|
|
13251
13380
|
function kobeSkillPaths(opts = {}) {
|
|
13252
|
-
const home = opts.home ??
|
|
13381
|
+
const home = opts.home ?? homedir17();
|
|
13253
13382
|
const cwd = opts.cwd ?? process.cwd();
|
|
13254
|
-
return [
|
|
13383
|
+
return [join10(home, SKILL_REL_PATH), join10(cwd, SKILL_REL_PATH)];
|
|
13255
13384
|
}
|
|
13256
13385
|
function parseSkillVersion(content) {
|
|
13257
13386
|
const m = content.match(/kobe-skill-version:\s*(\d+)/);
|
|
@@ -13298,7 +13427,7 @@ kobe: your kobe agent skill is out of date (${was}; this kobe wants v${state.cur
|
|
|
13298
13427
|
`);
|
|
13299
13428
|
}
|
|
13300
13429
|
}
|
|
13301
|
-
var KOBE_SKILL_VERSION =
|
|
13430
|
+
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
13431
|
var init_skill_install = __esm(() => {
|
|
13303
13432
|
init_repos();
|
|
13304
13433
|
});
|
|
@@ -13312,7 +13441,7 @@ __export(exports_maintenance, {
|
|
|
13312
13441
|
});
|
|
13313
13442
|
import { existsSync as existsSync10, readFileSync as readFileSync12, statSync as statSync5 } from "fs";
|
|
13314
13443
|
import { unlink as unlink6 } from "fs/promises";
|
|
13315
|
-
import { join as
|
|
13444
|
+
import { join as join11 } from "path";
|
|
13316
13445
|
import { createInterface as createInterface2 } from "readline";
|
|
13317
13446
|
function isProcessAlive2(pid) {
|
|
13318
13447
|
try {
|
|
@@ -13409,7 +13538,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
13409
13538
|
const socketPath = defaultDaemonSocketPath();
|
|
13410
13539
|
const pidPath = defaultDaemonPidPath();
|
|
13411
13540
|
const logPath = defaultDaemonLogPath();
|
|
13412
|
-
const tasksPath =
|
|
13541
|
+
const tasksPath = join11(kobeStateDir(), "tasks.json");
|
|
13413
13542
|
const statePath2 = kvStatePath();
|
|
13414
13543
|
const out = ["kobe doctor", ` home: ${homeDir()}`, ` socket: ${socketPath}`, ""];
|
|
13415
13544
|
const status = await probeDaemonStatus(socketPath);
|
|
@@ -13476,7 +13605,7 @@ async function runDoctorSubcommand(argv = []) {
|
|
|
13476
13605
|
async function confirmTty(prompt) {
|
|
13477
13606
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
13478
13607
|
try {
|
|
13479
|
-
const answer = await new Promise((
|
|
13608
|
+
const answer = await new Promise((resolve7) => rl.question(prompt, resolve7));
|
|
13480
13609
|
return /^y(es)?$/i.test(answer.trim());
|
|
13481
13610
|
} finally {
|
|
13482
13611
|
rl.close();
|
|
@@ -13528,7 +13657,7 @@ async function runResetSubcommand(argv) {
|
|
|
13528
13657
|
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
13529
13658
|
const socketPath = defaultDaemonSocketPath();
|
|
13530
13659
|
const pidPath = defaultDaemonPidPath();
|
|
13531
|
-
const tasksPath =
|
|
13660
|
+
const tasksPath = join11(kobeStateDir(), "tasks.json");
|
|
13532
13661
|
const statePath2 = kvStatePath();
|
|
13533
13662
|
console.log("kobe reset will:");
|
|
13534
13663
|
console.log(" \u2022 stop the kobe daemon (graceful \u2192 SIGTERM \u2192 SIGKILL)");
|
|
@@ -13632,6 +13761,22 @@ var init_maintenance = __esm(() => {
|
|
|
13632
13761
|
init_version();
|
|
13633
13762
|
});
|
|
13634
13763
|
|
|
13764
|
+
// src/tui/lib/editor-prefs.ts
|
|
13765
|
+
function normalizeEditorKind(value) {
|
|
13766
|
+
return EDITOR_KINDS.includes(value) ? value : DEFAULT_EDITOR_KIND;
|
|
13767
|
+
}
|
|
13768
|
+
var EDITOR_KINDS, AUTO_EDITOR_CANDIDATES, EDITOR_KIND_KEY = "editor.kind", EDITOR_CUSTOM_KEY = "editor.customCommand", DEFAULT_EDITOR_KIND = "auto";
|
|
13769
|
+
var init_editor_prefs = __esm(() => {
|
|
13770
|
+
EDITOR_KINDS = ["auto", "vim", "nvim", "nano", "emacs", "custom"];
|
|
13771
|
+
AUTO_EDITOR_CANDIDATES = ["nvim", "vim", "emacs", "nano"];
|
|
13772
|
+
});
|
|
13773
|
+
|
|
13774
|
+
// src/tui/lib/settings-surface.ts
|
|
13775
|
+
function normalizeSettingsSurface(value) {
|
|
13776
|
+
return value === "taskpanel" ? "taskpanel" : "chattab";
|
|
13777
|
+
}
|
|
13778
|
+
var SETTINGS_SURFACE_KEY = "settings.surface", DEFAULT_SETTINGS_SURFACE = "chattab";
|
|
13779
|
+
|
|
13635
13780
|
// src/web/diff.ts
|
|
13636
13781
|
async function runGit(cwd, args) {
|
|
13637
13782
|
try {
|
|
@@ -13748,8 +13893,8 @@ async function handleDiffRequest(req, url) {
|
|
|
13748
13893
|
return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
|
|
13749
13894
|
}
|
|
13750
13895
|
try {
|
|
13751
|
-
const
|
|
13752
|
-
if (!
|
|
13896
|
+
const stat5 = await Bun.file(worktreePath).stat();
|
|
13897
|
+
if (!stat5.isDirectory()) {
|
|
13753
13898
|
return Response.json({ error: "worktreePath is not a directory" }, { status: 400 });
|
|
13754
13899
|
}
|
|
13755
13900
|
} catch {
|
|
@@ -13817,7 +13962,7 @@ async function handleDiffRequest(req, url) {
|
|
|
13817
13962
|
var GIT_TIMEOUT_MS = 15000, UNTRACKED_DIFF_CONCURRENCY = 8;
|
|
13818
13963
|
|
|
13819
13964
|
// src/web/history.ts
|
|
13820
|
-
import { isAbsolute } from "path";
|
|
13965
|
+
import { isAbsolute as isAbsolute2 } from "path";
|
|
13821
13966
|
function isSafeVendor(value) {
|
|
13822
13967
|
return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9_-]+$/.test(value);
|
|
13823
13968
|
}
|
|
@@ -13827,7 +13972,7 @@ function isSafeSessionId(value) {
|
|
|
13827
13972
|
async function handleSessions(url) {
|
|
13828
13973
|
const worktreePath = url.searchParams.get("worktreePath");
|
|
13829
13974
|
const vendor = url.searchParams.get("vendor") ?? "claude";
|
|
13830
|
-
if (!worktreePath || !
|
|
13975
|
+
if (!worktreePath || !isAbsolute2(worktreePath)) {
|
|
13831
13976
|
return Response.json({ error: "worktreePath must be an absolute path" }, { status: 400 });
|
|
13832
13977
|
}
|
|
13833
13978
|
if (!isSafeVendor(vendor)) {
|
|
@@ -13873,16 +14018,16 @@ var init_history4 = __esm(() => {
|
|
|
13873
14018
|
});
|
|
13874
14019
|
|
|
13875
14020
|
// src/web/notes.ts
|
|
13876
|
-
import { mkdir as
|
|
13877
|
-
import { join as
|
|
14021
|
+
import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
|
|
14022
|
+
import { join as join12 } from "path";
|
|
13878
14023
|
function notesDir() {
|
|
13879
|
-
return
|
|
14024
|
+
return join12(kobeStateDir(), "notes");
|
|
13880
14025
|
}
|
|
13881
14026
|
function isSafeTaskId(taskId) {
|
|
13882
14027
|
return typeof taskId === "string" && taskId.length > 0 && /^[A-Za-z0-9_-]+$/.test(taskId);
|
|
13883
14028
|
}
|
|
13884
14029
|
function noteFilePath(taskId) {
|
|
13885
|
-
return
|
|
14030
|
+
return join12(notesDir(), `${taskId}.md`);
|
|
13886
14031
|
}
|
|
13887
14032
|
async function handleGet(url) {
|
|
13888
14033
|
const taskId = url.searchParams.get("taskId");
|
|
@@ -13892,7 +14037,7 @@ async function handleGet(url) {
|
|
|
13892
14037
|
try {
|
|
13893
14038
|
let markdown = "";
|
|
13894
14039
|
try {
|
|
13895
|
-
markdown = await
|
|
14040
|
+
markdown = await readFile9(noteFilePath(taskId), "utf8");
|
|
13896
14041
|
} catch (err) {
|
|
13897
14042
|
if (err.code !== "ENOENT")
|
|
13898
14043
|
throw err;
|
|
@@ -13916,8 +14061,8 @@ async function handlePut(req) {
|
|
|
13916
14061
|
return Response.json({ error: "markdown must be a string" }, { status: 400 });
|
|
13917
14062
|
}
|
|
13918
14063
|
try {
|
|
13919
|
-
await
|
|
13920
|
-
await
|
|
14064
|
+
await mkdir8(notesDir(), { recursive: true });
|
|
14065
|
+
await writeFile6(noteFilePath(body.taskId), body.markdown, "utf8");
|
|
13921
14066
|
return Response.json({ ok: true });
|
|
13922
14067
|
} catch (err) {
|
|
13923
14068
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
@@ -14025,12 +14170,12 @@ var SPA_CHANNELS, SPA_CHANNEL_SET;
|
|
|
14025
14170
|
var init_spa_channels = __esm(() => {
|
|
14026
14171
|
SPA_CHANNELS = [
|
|
14027
14172
|
"task.snapshot",
|
|
14173
|
+
"issue.snapshot",
|
|
14028
14174
|
"active-task",
|
|
14029
14175
|
"engine-state",
|
|
14030
14176
|
"update",
|
|
14031
14177
|
"task.jobs",
|
|
14032
14178
|
"worktree.changes",
|
|
14033
|
-
"task.conflicts",
|
|
14034
14179
|
"session.deliver",
|
|
14035
14180
|
"ui-prefs"
|
|
14036
14181
|
];
|
|
@@ -14039,7 +14184,25 @@ var init_spa_channels = __esm(() => {
|
|
|
14039
14184
|
|
|
14040
14185
|
// ../kobe-web/server/daemon-link.ts
|
|
14041
14186
|
function sleep2(ms) {
|
|
14042
|
-
return new Promise((
|
|
14187
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
14188
|
+
}
|
|
14189
|
+
function normalizedPath(path11) {
|
|
14190
|
+
return path11.length > 1 ? path11.replace(/\/+$/, "") : path11;
|
|
14191
|
+
}
|
|
14192
|
+
function issueSnapshotAliases(tasks, repoRoot) {
|
|
14193
|
+
const root = normalizedPath(repoRoot);
|
|
14194
|
+
const aliases = new Set([repoRoot]);
|
|
14195
|
+
for (const task of tasks) {
|
|
14196
|
+
const taskRepo = normalizedPath(task.repo);
|
|
14197
|
+
const taskWorktree = normalizedPath(task.worktreePath);
|
|
14198
|
+
if (taskRepo === root || taskWorktree === root) {
|
|
14199
|
+
if (task.repo)
|
|
14200
|
+
aliases.add(task.repo);
|
|
14201
|
+
if (task.worktreePath)
|
|
14202
|
+
aliases.add(task.worktreePath);
|
|
14203
|
+
}
|
|
14204
|
+
}
|
|
14205
|
+
return [...aliases];
|
|
14043
14206
|
}
|
|
14044
14207
|
|
|
14045
14208
|
class DaemonLink {
|
|
@@ -14055,7 +14218,7 @@ class DaemonLink {
|
|
|
14055
14218
|
update = null;
|
|
14056
14219
|
jobs = {};
|
|
14057
14220
|
worktreeChanges = {};
|
|
14058
|
-
|
|
14221
|
+
issueSnapshots = {};
|
|
14059
14222
|
deliver = null;
|
|
14060
14223
|
uiPrefs = null;
|
|
14061
14224
|
async start() {
|
|
@@ -14069,7 +14232,7 @@ class DaemonLink {
|
|
|
14069
14232
|
update: this.update,
|
|
14070
14233
|
jobs: this.jobs,
|
|
14071
14234
|
worktreeChanges: this.worktreeChanges,
|
|
14072
|
-
|
|
14235
|
+
issueSnapshots: this.issueSnapshots,
|
|
14073
14236
|
deliver: this.deliver,
|
|
14074
14237
|
uiPrefs: this.uiPrefs,
|
|
14075
14238
|
connected: this.connected
|
|
@@ -14181,9 +14344,15 @@ class DaemonLink {
|
|
|
14181
14344
|
case "worktree.changes":
|
|
14182
14345
|
this.worktreeChanges = payload.changes;
|
|
14183
14346
|
break;
|
|
14184
|
-
case "
|
|
14185
|
-
|
|
14347
|
+
case "issue.snapshot": {
|
|
14348
|
+
const state = payload;
|
|
14349
|
+
const next = { ...this.issueSnapshots };
|
|
14350
|
+
for (const alias of issueSnapshotAliases(this.tasks, state.repoRoot)) {
|
|
14351
|
+
next[alias] = { ...state, repoRoot: alias };
|
|
14352
|
+
}
|
|
14353
|
+
this.issueSnapshots = next;
|
|
14186
14354
|
break;
|
|
14355
|
+
}
|
|
14187
14356
|
case "session.deliver":
|
|
14188
14357
|
this.deliver = payload;
|
|
14189
14358
|
break;
|
|
@@ -14218,6 +14387,169 @@ var init_daemon_link = __esm(() => {
|
|
|
14218
14387
|
init_spa_channels();
|
|
14219
14388
|
});
|
|
14220
14389
|
|
|
14390
|
+
// ../kobe-web/server/issue-assets-route.ts
|
|
14391
|
+
import { createHash as createHash5, randomUUID as randomUUID2 } from "crypto";
|
|
14392
|
+
import { mkdir as mkdir9 } from "fs/promises";
|
|
14393
|
+
import { join as join13, resolve as resolve7 } from "path";
|
|
14394
|
+
function repoHashOf(repoRoot) {
|
|
14395
|
+
return createHash5("sha1").update(repoRoot).digest("hex").slice(0, 16);
|
|
14396
|
+
}
|
|
14397
|
+
async function handlePost(req) {
|
|
14398
|
+
const declared = Number.parseInt(req.headers.get("content-length") ?? "", 10);
|
|
14399
|
+
if (Number.isFinite(declared) && declared > MAX_ASSET_BYTES) {
|
|
14400
|
+
return Response.json({ error: "asset too large" }, { status: 413 });
|
|
14401
|
+
}
|
|
14402
|
+
let form;
|
|
14403
|
+
try {
|
|
14404
|
+
form = await req.formData();
|
|
14405
|
+
} catch {
|
|
14406
|
+
return Response.json({ error: "invalid form data" }, { status: 400 });
|
|
14407
|
+
}
|
|
14408
|
+
const repoRoot = form.get("repoRoot");
|
|
14409
|
+
if (typeof repoRoot !== "string" || repoRoot.length === 0) {
|
|
14410
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14411
|
+
}
|
|
14412
|
+
const file = form.get("file");
|
|
14413
|
+
if (!(file instanceof File)) {
|
|
14414
|
+
return Response.json({ error: "file must be a File" }, { status: 400 });
|
|
14415
|
+
}
|
|
14416
|
+
if (file.size > MAX_ASSET_BYTES) {
|
|
14417
|
+
return Response.json({ error: "asset too large" }, { status: 413 });
|
|
14418
|
+
}
|
|
14419
|
+
const ext = CONTENT_TYPE_EXT[file.type];
|
|
14420
|
+
if (!ext) {
|
|
14421
|
+
return Response.json({ error: `unsupported content-type: ${file.type || "unknown"}` }, { status: 415 });
|
|
14422
|
+
}
|
|
14423
|
+
try {
|
|
14424
|
+
const repoHash = repoHashOf(repoRoot);
|
|
14425
|
+
const dir = join13(issueAssetsDir(), repoHash);
|
|
14426
|
+
await mkdir9(dir, { recursive: true });
|
|
14427
|
+
const assetId = randomUUID2();
|
|
14428
|
+
const name = `${assetId}.${ext}`;
|
|
14429
|
+
await Bun.write(join13(dir, name), file);
|
|
14430
|
+
return Response.json({ url: `${ASSETS_ROUTE}/${repoHash}/${name}` });
|
|
14431
|
+
} catch (err) {
|
|
14432
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
14433
|
+
}
|
|
14434
|
+
}
|
|
14435
|
+
async function handleGet2(pathname) {
|
|
14436
|
+
const rest = pathname.slice(ASSETS_ROUTE.length + 1);
|
|
14437
|
+
const slash = rest.indexOf("/");
|
|
14438
|
+
if (slash < 0)
|
|
14439
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
14440
|
+
const repoHash = rest.slice(0, slash);
|
|
14441
|
+
const fileSeg = rest.slice(slash + 1);
|
|
14442
|
+
if (!REPO_HASH_RE.test(repoHash) || !ASSET_FILE_RE.test(fileSeg)) {
|
|
14443
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14444
|
+
}
|
|
14445
|
+
const root = issueAssetsDir();
|
|
14446
|
+
const resolved = resolve7(root, repoHash, fileSeg);
|
|
14447
|
+
if (resolved !== join13(root, repoHash, fileSeg) || !resolved.startsWith(`${root}/`)) {
|
|
14448
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14449
|
+
}
|
|
14450
|
+
const ext = fileSeg.slice(fileSeg.lastIndexOf(".") + 1).toLowerCase();
|
|
14451
|
+
const contentType = EXT_CONTENT_TYPE[ext];
|
|
14452
|
+
if (!contentType)
|
|
14453
|
+
return Response.json({ error: "invalid asset path" }, { status: 400 });
|
|
14454
|
+
const file = Bun.file(resolved);
|
|
14455
|
+
if (!await file.exists())
|
|
14456
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
14457
|
+
return new Response(file, {
|
|
14458
|
+
headers: {
|
|
14459
|
+
"content-type": contentType,
|
|
14460
|
+
"cache-control": "public, max-age=31536000, immutable",
|
|
14461
|
+
"x-content-type-options": "nosniff"
|
|
14462
|
+
}
|
|
14463
|
+
});
|
|
14464
|
+
}
|
|
14465
|
+
async function handleIssueAssetsRequest(req, url) {
|
|
14466
|
+
if (url.pathname === ASSETS_ROUTE) {
|
|
14467
|
+
if (req.method === "POST")
|
|
14468
|
+
return handlePost(req);
|
|
14469
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14470
|
+
}
|
|
14471
|
+
if (url.pathname.startsWith(`${ASSETS_ROUTE}/`)) {
|
|
14472
|
+
if (req.method === "GET")
|
|
14473
|
+
return handleGet2(url.pathname);
|
|
14474
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14475
|
+
}
|
|
14476
|
+
return null;
|
|
14477
|
+
}
|
|
14478
|
+
var ASSETS_ROUTE = "/api/issue-assets", MAX_ASSET_BYTES, CONTENT_TYPE_EXT, EXT_CONTENT_TYPE, REPO_HASH_RE, ASSET_FILE_RE;
|
|
14479
|
+
var init_issue_assets_route = __esm(() => {
|
|
14480
|
+
init_env();
|
|
14481
|
+
MAX_ASSET_BYTES = 10 * 1024 * 1024;
|
|
14482
|
+
CONTENT_TYPE_EXT = {
|
|
14483
|
+
"image/png": "png",
|
|
14484
|
+
"image/jpeg": "jpg",
|
|
14485
|
+
"image/gif": "gif",
|
|
14486
|
+
"image/webp": "webp"
|
|
14487
|
+
};
|
|
14488
|
+
EXT_CONTENT_TYPE = {
|
|
14489
|
+
png: "image/png",
|
|
14490
|
+
jpg: "image/jpeg",
|
|
14491
|
+
jpeg: "image/jpeg",
|
|
14492
|
+
gif: "image/gif",
|
|
14493
|
+
webp: "image/webp"
|
|
14494
|
+
};
|
|
14495
|
+
REPO_HASH_RE = /^[a-f0-9]{16}$/;
|
|
14496
|
+
ASSET_FILE_RE = /^[A-Za-z0-9_-]+\.[a-z0-9]+$/;
|
|
14497
|
+
});
|
|
14498
|
+
|
|
14499
|
+
// ../kobe-web/server/issues-route.ts
|
|
14500
|
+
function errorResponse(err, status = 500) {
|
|
14501
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status });
|
|
14502
|
+
}
|
|
14503
|
+
function statusForIssueError(err) {
|
|
14504
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14505
|
+
if (/^no issue #\d+$/.test(message))
|
|
14506
|
+
return 404;
|
|
14507
|
+
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:")) {
|
|
14508
|
+
return 400;
|
|
14509
|
+
}
|
|
14510
|
+
return 500;
|
|
14511
|
+
}
|
|
14512
|
+
async function handleGet3(link, url) {
|
|
14513
|
+
const repoRoot = url.searchParams.get("repoRoot");
|
|
14514
|
+
if (!repoRoot)
|
|
14515
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14516
|
+
try {
|
|
14517
|
+
return Response.json(await link.request("issue.list", { repoRoot }));
|
|
14518
|
+
} catch (err) {
|
|
14519
|
+
return errorResponse(err, statusForIssueError(err));
|
|
14520
|
+
}
|
|
14521
|
+
}
|
|
14522
|
+
async function handlePost2(link, req) {
|
|
14523
|
+
let parsed;
|
|
14524
|
+
try {
|
|
14525
|
+
parsed = await req.json();
|
|
14526
|
+
} catch {
|
|
14527
|
+
return Response.json({ error: "invalid JSON body" }, { status: 400 });
|
|
14528
|
+
}
|
|
14529
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
14530
|
+
return Response.json({ error: "body must be a JSON object" }, { status: 400 });
|
|
14531
|
+
}
|
|
14532
|
+
const body = parsed;
|
|
14533
|
+
if (typeof body.repoRoot !== "string" || body.repoRoot.length === 0) {
|
|
14534
|
+
return Response.json({ error: "missing repoRoot" }, { status: 400 });
|
|
14535
|
+
}
|
|
14536
|
+
try {
|
|
14537
|
+
return Response.json(await link.request("issue.mutate", { repoRoot: body.repoRoot, op: body.op }));
|
|
14538
|
+
} catch (err) {
|
|
14539
|
+
return errorResponse(err, statusForIssueError(err));
|
|
14540
|
+
}
|
|
14541
|
+
}
|
|
14542
|
+
async function handleIssuesRequest(req, url, link) {
|
|
14543
|
+
if (url.pathname !== ISSUES_ROUTE)
|
|
14544
|
+
return null;
|
|
14545
|
+
if (req.method === "GET")
|
|
14546
|
+
return handleGet3(link, url);
|
|
14547
|
+
if (req.method === "POST")
|
|
14548
|
+
return handlePost2(link, req);
|
|
14549
|
+
return Response.json({ error: "method not allowed" }, { status: 405 });
|
|
14550
|
+
}
|
|
14551
|
+
var ISSUES_ROUTE = "/api/issues";
|
|
14552
|
+
|
|
14221
14553
|
// ../kobe-web/server/rpc-allowlist.ts
|
|
14222
14554
|
var WEB_RPC_ALLOWLIST, WEB_RPC_ALLOWSET;
|
|
14223
14555
|
var init_rpc_allowlist = __esm(() => {
|
|
@@ -14266,7 +14598,7 @@ async function ensureTaskSession(link, taskId) {
|
|
|
14266
14598
|
const ok = await ensureSession({
|
|
14267
14599
|
name: session,
|
|
14268
14600
|
cwd: worktreePath,
|
|
14269
|
-
command: interactiveEngineCommand(task.vendor),
|
|
14601
|
+
command: interactiveEngineCommand(task.vendor, task.modelEffort),
|
|
14270
14602
|
taskId,
|
|
14271
14603
|
vendor: task.vendor,
|
|
14272
14604
|
initScript: init.initScript
|
|
@@ -14284,7 +14616,7 @@ async function engineSpec(link, taskId) {
|
|
|
14284
14616
|
const protocolTaskId = task.kind === "main" ? undefined : taskId;
|
|
14285
14617
|
const dispatcherTaskId = task.kind === "main" ? taskId : undefined;
|
|
14286
14618
|
const argv = [
|
|
14287
|
-
...withDispatcherProtocol(withWorktreeProtocol(interactiveEngineCommand(task.vendor), task.vendor, protocolTaskId), task.vendor, dispatcherTaskId)
|
|
14619
|
+
...withDispatcherProtocol(withWorktreeProtocol(interactiveEngineCommand(task.vendor, task.modelEffort), task.vendor, protocolTaskId), task.vendor, dispatcherTaskId)
|
|
14288
14620
|
];
|
|
14289
14621
|
const init = resolveRepoInit(task.repo ?? "", worktreePath);
|
|
14290
14622
|
const quoted = shellQuote2(argv);
|
|
@@ -14312,7 +14644,7 @@ var init_session = __esm(() => {
|
|
|
14312
14644
|
|
|
14313
14645
|
// ../kobe-web/server/bridge.ts
|
|
14314
14646
|
import { existsSync as existsSync11 } from "fs";
|
|
14315
|
-
import { join as
|
|
14647
|
+
import { join as join14, normalize as normalize2 } from "path";
|
|
14316
14648
|
function sseResponse(register) {
|
|
14317
14649
|
let unregister = null;
|
|
14318
14650
|
let heartbeat = null;
|
|
@@ -14374,12 +14706,140 @@ async function rpcResponse(req, link, tearDown) {
|
|
|
14374
14706
|
async function enginesResponse() {
|
|
14375
14707
|
try {
|
|
14376
14708
|
const ids = await availableEngineIds();
|
|
14377
|
-
const engines = ids.map((id) => ({
|
|
14709
|
+
const engines = ids.map((id) => ({
|
|
14710
|
+
id,
|
|
14711
|
+
label: engineDisplayName(id),
|
|
14712
|
+
effortLevels: engineEntry(id).effortLevels
|
|
14713
|
+
}));
|
|
14378
14714
|
return Response.json({ engines: engines.length > 0 ? engines : [{ id: "claude", label: "Claude" }] });
|
|
14379
14715
|
} catch (err) {
|
|
14380
14716
|
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 500 });
|
|
14381
14717
|
}
|
|
14382
14718
|
}
|
|
14719
|
+
function cliInvocationResponse() {
|
|
14720
|
+
return Response.json({ api: kobeApiInvocation() });
|
|
14721
|
+
}
|
|
14722
|
+
function stringValue(value, fallback = "") {
|
|
14723
|
+
return typeof value === "string" ? value : fallback;
|
|
14724
|
+
}
|
|
14725
|
+
function boolValue(value, fallback) {
|
|
14726
|
+
return typeof value === "boolean" ? value : fallback;
|
|
14727
|
+
}
|
|
14728
|
+
function customEngineIdsFrom(state) {
|
|
14729
|
+
const raw = state.customEngineIds;
|
|
14730
|
+
return Array.isArray(raw) ? raw.filter((s) => typeof s === "string" && s.trim().length > 0) : [];
|
|
14731
|
+
}
|
|
14732
|
+
function humanizeSlug(id) {
|
|
14733
|
+
return id.split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
14734
|
+
}
|
|
14735
|
+
function engineCommandText(state, id) {
|
|
14736
|
+
const override = stringValue(state[engineCommandKey(id)]).trim();
|
|
14737
|
+
return override || defaultEngineCommand(id).join(" ");
|
|
14738
|
+
}
|
|
14739
|
+
function engineLabelText(state, id) {
|
|
14740
|
+
const override = stringValue(state[engineNameKey(id)]).trim();
|
|
14741
|
+
return override || engineDisplayName(id);
|
|
14742
|
+
}
|
|
14743
|
+
function settingsSnapshot() {
|
|
14744
|
+
const state = loadStateFile();
|
|
14745
|
+
const custom = customEngineIdsFrom(state);
|
|
14746
|
+
const engineIds = [...BUILTIN_VENDORS, ...custom];
|
|
14747
|
+
const defaultEngine = stringValue(state.lastSelectedVendor, "claude");
|
|
14748
|
+
const focusAccent = stringValue(state.focusAccent, "primary");
|
|
14749
|
+
return Response.json({
|
|
14750
|
+
activeTheme: stringValue(state.activeTheme, "claude"),
|
|
14751
|
+
transparentBackground: boolValue(state.transparentBackground, false),
|
|
14752
|
+
focusAccent: FOCUS_ACCENTS.includes(focusAccent) ? focusAccent : "primary",
|
|
14753
|
+
notificationsToast: state["notifications.toast.enabled"] !== false,
|
|
14754
|
+
notificationsSound: state["notifications.sound.enabled"] !== false,
|
|
14755
|
+
settingsSurface: normalizeSettingsSurface(state[SETTINGS_SURFACE_KEY] ?? DEFAULT_SETTINGS_SURFACE),
|
|
14756
|
+
editorKind: normalizeEditorKind(state[EDITOR_KIND_KEY] ?? DEFAULT_EDITOR_KIND),
|
|
14757
|
+
editorCustomCommand: stringValue(state[EDITOR_CUSTOM_KEY]),
|
|
14758
|
+
remoteProjects: state["experimental.remoteProjects"] === true,
|
|
14759
|
+
autoStatus: state[AUTO_STATUS_KEY] === true,
|
|
14760
|
+
dispatcher: state[DISPATCHER_KEY] === true,
|
|
14761
|
+
defaultEngine,
|
|
14762
|
+
engines: engineIds.map((id) => ({
|
|
14763
|
+
id,
|
|
14764
|
+
label: engineLabelText(state, id),
|
|
14765
|
+
command: engineCommandText(state, id),
|
|
14766
|
+
isBuiltin: isBuiltinVendor(id),
|
|
14767
|
+
isCustom: !isBuiltinVendor(id),
|
|
14768
|
+
isDefault: id === defaultEngine
|
|
14769
|
+
}))
|
|
14770
|
+
});
|
|
14771
|
+
}
|
|
14772
|
+
function putIfString(patch, key, value) {
|
|
14773
|
+
if (typeof value === "string")
|
|
14774
|
+
patch[key] = value.trim();
|
|
14775
|
+
}
|
|
14776
|
+
function putIfBool(patch, key, value) {
|
|
14777
|
+
if (typeof value === "boolean")
|
|
14778
|
+
patch[key] = value;
|
|
14779
|
+
}
|
|
14780
|
+
async function settingsPatch(req) {
|
|
14781
|
+
try {
|
|
14782
|
+
const body = await req.json();
|
|
14783
|
+
const patch = {};
|
|
14784
|
+
putIfString(patch, "activeTheme", body.activeTheme);
|
|
14785
|
+
putIfBool(patch, "transparentBackground", body.transparentBackground);
|
|
14786
|
+
if (FOCUS_ACCENTS.includes(body.focusAccent)) {
|
|
14787
|
+
patch.focusAccent = body.focusAccent;
|
|
14788
|
+
}
|
|
14789
|
+
putIfBool(patch, "notifications.toast.enabled", body.notificationsToast);
|
|
14790
|
+
putIfBool(patch, "notifications.sound.enabled", body.notificationsSound);
|
|
14791
|
+
if (body.settingsSurface === "chattab" || body.settingsSurface === "taskpanel") {
|
|
14792
|
+
patch[SETTINGS_SURFACE_KEY] = body.settingsSurface;
|
|
14793
|
+
}
|
|
14794
|
+
if (EDITOR_KINDS.includes(body.editorKind))
|
|
14795
|
+
patch[EDITOR_KIND_KEY] = body.editorKind;
|
|
14796
|
+
putIfString(patch, EDITOR_CUSTOM_KEY, body.editorCustomCommand);
|
|
14797
|
+
putIfBool(patch, "experimental.remoteProjects", body.remoteProjects);
|
|
14798
|
+
putIfBool(patch, AUTO_STATUS_KEY, body.autoStatus);
|
|
14799
|
+
putIfBool(patch, DISPATCHER_KEY, body.dispatcher);
|
|
14800
|
+
putIfString(patch, "lastSelectedVendor", body.defaultEngine);
|
|
14801
|
+
const state = loadStateFile();
|
|
14802
|
+
const custom = customEngineIdsFrom(state);
|
|
14803
|
+
const known = new Set([...BUILTIN_VENDORS, ...custom]);
|
|
14804
|
+
const updates = Array.isArray(body.engineUpdates) ? body.engineUpdates : [];
|
|
14805
|
+
for (const raw of updates) {
|
|
14806
|
+
if (!raw || typeof raw !== "object")
|
|
14807
|
+
continue;
|
|
14808
|
+
const update = raw;
|
|
14809
|
+
if (typeof update.id !== "string" || !known.has(update.id))
|
|
14810
|
+
continue;
|
|
14811
|
+
putIfString(patch, engineCommandKey(update.id), update.command);
|
|
14812
|
+
putIfString(patch, engineNameKey(update.id), update.label);
|
|
14813
|
+
}
|
|
14814
|
+
if (body.addEngine && typeof body.addEngine === "object") {
|
|
14815
|
+
const add2 = body.addEngine;
|
|
14816
|
+
const id = typeof add2.id === "string" ? add2.id.trim().toLowerCase() : "";
|
|
14817
|
+
if (!ENGINE_ID_RE.test(id) || isBuiltinVendor(id) || known.has(id)) {
|
|
14818
|
+
return Response.json({ error: "invalid or duplicate engine id" }, { status: 400 });
|
|
14819
|
+
}
|
|
14820
|
+
const nextCustom = [...custom, id];
|
|
14821
|
+
patch.customEngineIds = nextCustom;
|
|
14822
|
+
patch[engineCommandKey(id)] = stringValue(add2.command).trim();
|
|
14823
|
+
const label = stringValue(add2.label).trim();
|
|
14824
|
+
patch[engineNameKey(id)] = label && label !== id ? label : humanizeSlug(id);
|
|
14825
|
+
}
|
|
14826
|
+
if (typeof body.removeEngine === "string") {
|
|
14827
|
+
const id = body.removeEngine;
|
|
14828
|
+
if (!isBuiltinVendor(id)) {
|
|
14829
|
+
patch.customEngineIds = custom.filter((engine) => engine !== id);
|
|
14830
|
+
patch[engineCommandKey(id)] = undefined;
|
|
14831
|
+
patch[engineNameKey(id)] = undefined;
|
|
14832
|
+
if (state.lastSelectedVendor === id)
|
|
14833
|
+
patch.lastSelectedVendor = "claude";
|
|
14834
|
+
}
|
|
14835
|
+
}
|
|
14836
|
+
if (Object.keys(patch).length > 0)
|
|
14837
|
+
patchStateFile(patch);
|
|
14838
|
+
return settingsSnapshot();
|
|
14839
|
+
} catch (err) {
|
|
14840
|
+
return Response.json({ error: err instanceof Error ? err.message : String(err) }, { status: 400 });
|
|
14841
|
+
}
|
|
14842
|
+
}
|
|
14383
14843
|
async function sessionResponse(req, link) {
|
|
14384
14844
|
try {
|
|
14385
14845
|
const { taskId } = await req.json();
|
|
@@ -14426,6 +14886,12 @@ function createRequestHandler(deps) {
|
|
|
14426
14886
|
return specResponse(url, link, terminalSpec);
|
|
14427
14887
|
if (url.pathname === "/api/engines" && req.method === "GET")
|
|
14428
14888
|
return enginesResponse();
|
|
14889
|
+
if (url.pathname === "/api/cli-invocation" && req.method === "GET")
|
|
14890
|
+
return cliInvocationResponse();
|
|
14891
|
+
if (url.pathname === "/api/settings" && req.method === "GET")
|
|
14892
|
+
return settingsSnapshot();
|
|
14893
|
+
if (url.pathname === "/api/settings" && req.method === "PATCH")
|
|
14894
|
+
return settingsPatch(req);
|
|
14429
14895
|
if (url.pathname === "/api/quick-prompts" && req.method === "GET")
|
|
14430
14896
|
return quickPromptsGet();
|
|
14431
14897
|
if (url.pathname === "/api/quick-prompts" && req.method === "PUT")
|
|
@@ -14439,6 +14905,12 @@ function createRequestHandler(deps) {
|
|
|
14439
14905
|
const history = await handleHistoryRequest(req, url);
|
|
14440
14906
|
if (history)
|
|
14441
14907
|
return history;
|
|
14908
|
+
const issues = await handleIssuesRequest(req, url, link);
|
|
14909
|
+
if (issues)
|
|
14910
|
+
return issues;
|
|
14911
|
+
const issueAssets = await handleIssueAssetsRequest(req, url);
|
|
14912
|
+
if (issueAssets)
|
|
14913
|
+
return issueAssets;
|
|
14442
14914
|
const themes = handleThemesRequest(req, url);
|
|
14443
14915
|
if (themes)
|
|
14444
14916
|
return themes;
|
|
@@ -14467,10 +14939,10 @@ async function quickPromptsPut(req) {
|
|
|
14467
14939
|
}
|
|
14468
14940
|
async function staticResponse(pathname, staticDir) {
|
|
14469
14941
|
const rel = pathname === "/" ? "/index.html" : pathname;
|
|
14470
|
-
const resolved = normalize2(
|
|
14942
|
+
const resolved = normalize2(join14(staticDir, rel));
|
|
14471
14943
|
if (!resolved.startsWith(staticDir))
|
|
14472
14944
|
return new Response("forbidden", { status: 403 });
|
|
14473
|
-
const file = Bun.file(existsSync11(resolved) ? resolved :
|
|
14945
|
+
const file = Bun.file(existsSync11(resolved) ? resolved : join14(staticDir, "index.html"));
|
|
14474
14946
|
if (!await file.exists()) {
|
|
14475
14947
|
return new Response("kobe web assets not built \u2014 run `bun --filter kobe-web build`", { status: 503 });
|
|
14476
14948
|
}
|
|
@@ -14542,17 +15014,26 @@ async function createBridgeServer(opts = {}) {
|
|
|
14542
15014
|
}
|
|
14543
15015
|
};
|
|
14544
15016
|
}
|
|
14545
|
-
var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web", QUICK_PROMPT_KEYS;
|
|
15017
|
+
var WEB_HEALTH_MARKER = "kobe-web", WEB_HEALTH_PATH = "/__kobe_web", FOCUS_ACCENTS, ENGINE_ID_RE, QUICK_PROMPT_KEYS;
|
|
14546
15018
|
var init_bridge = __esm(() => {
|
|
14547
15019
|
init_account_detect();
|
|
14548
15020
|
init_interactive_command();
|
|
15021
|
+
init_registry();
|
|
15022
|
+
init_auto_status();
|
|
15023
|
+
init_dispatcher();
|
|
14549
15024
|
init_repos();
|
|
15025
|
+
init_store();
|
|
15026
|
+
init_editor_prefs();
|
|
15027
|
+
init_vendor();
|
|
14550
15028
|
init_history4();
|
|
14551
15029
|
init_notes();
|
|
14552
15030
|
init_themes();
|
|
14553
15031
|
init_daemon_link();
|
|
15032
|
+
init_issue_assets_route();
|
|
14554
15033
|
init_rpc_allowlist();
|
|
14555
15034
|
init_session();
|
|
15035
|
+
FOCUS_ACCENTS = ["primary", "success", "info"];
|
|
15036
|
+
ENGINE_ID_RE = /^[a-z][a-z0-9_-]{0,47}$/;
|
|
14556
15037
|
QUICK_PROMPT_KEYS = {
|
|
14557
15038
|
review: "boardPrompt.review",
|
|
14558
15039
|
pr: "boardPrompt.pr"
|
|
@@ -14570,18 +15051,18 @@ __export(exports_web_cmd, {
|
|
|
14570
15051
|
runWebSubcommand: () => runWebSubcommand
|
|
14571
15052
|
});
|
|
14572
15053
|
import { existsSync as existsSync12 } from "fs";
|
|
14573
|
-
import { homedir as
|
|
14574
|
-
import { resolve as
|
|
15054
|
+
import { homedir as homedir18 } from "os";
|
|
15055
|
+
import { resolve as resolve8 } from "path";
|
|
14575
15056
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14576
15057
|
function homeLabel() {
|
|
14577
15058
|
const explicit = process.env.KOBE_HOME_DIR?.trim();
|
|
14578
|
-
return explicit ? `sandbox: ${explicit}` : `${
|
|
15059
|
+
return explicit ? `sandbox: ${explicit}` : `${homedir18()}/.kobe (production)`;
|
|
14579
15060
|
}
|
|
14580
15061
|
function resolveStaticDir() {
|
|
14581
15062
|
const here = fileURLToPath3(import.meta.url);
|
|
14582
15063
|
const candidates = [
|
|
14583
|
-
|
|
14584
|
-
|
|
15064
|
+
resolve8(here, "../../../../kobe-web/dist"),
|
|
15065
|
+
resolve8(here, "../../web-ui")
|
|
14585
15066
|
];
|
|
14586
15067
|
for (const dir of candidates) {
|
|
14587
15068
|
if (existsSync12(`${dir}/index.html`))
|
|
@@ -14592,8 +15073,8 @@ function resolveStaticDir() {
|
|
|
14592
15073
|
function resolvePtyServer() {
|
|
14593
15074
|
const here = fileURLToPath3(import.meta.url);
|
|
14594
15075
|
const candidates = [
|
|
14595
|
-
|
|
14596
|
-
|
|
15076
|
+
resolve8(here, "../../../../kobe-web/pty-server.mjs"),
|
|
15077
|
+
resolve8(here, "../../web-ui/pty-server.mjs")
|
|
14597
15078
|
];
|
|
14598
15079
|
for (const file of candidates) {
|
|
14599
15080
|
if (existsSync12(file))
|
|
@@ -14855,15 +15336,15 @@ __export(exports_hook_cmd, {
|
|
|
14855
15336
|
parseWorktreeAddPath: () => parseWorktreeAddPath,
|
|
14856
15337
|
ensureGlobalKobeHooks: () => ensureGlobalKobeHooks
|
|
14857
15338
|
});
|
|
14858
|
-
import { homedir as
|
|
14859
|
-
import { join as
|
|
15339
|
+
import { homedir as homedir19 } from "os";
|
|
15340
|
+
import { join as join15, resolve as resolve9 } from "path";
|
|
14860
15341
|
async function readTextWithTimeout(read, timeoutMs = STDIN_READ_TIMEOUT_MS) {
|
|
14861
15342
|
let raceTimer;
|
|
14862
15343
|
try {
|
|
14863
15344
|
return await Promise.race([
|
|
14864
15345
|
read(),
|
|
14865
|
-
new Promise((
|
|
14866
|
-
raceTimer = setTimeout(() =>
|
|
15346
|
+
new Promise((resolve10) => {
|
|
15347
|
+
raceTimer = setTimeout(() => resolve10(""), timeoutMs);
|
|
14867
15348
|
})
|
|
14868
15349
|
]);
|
|
14869
15350
|
} finally {
|
|
@@ -14932,7 +15413,7 @@ async function runWorktreeCreatedHook() {
|
|
|
14932
15413
|
if (!rawPath)
|
|
14933
15414
|
return;
|
|
14934
15415
|
const cwd = typeof payload.cwd === "string" && payload.cwd ? payload.cwd : process.cwd();
|
|
14935
|
-
const worktreePath =
|
|
15416
|
+
const worktreePath = resolve9(cwd, rawPath);
|
|
14936
15417
|
const client = await connectIfRunning();
|
|
14937
15418
|
if (!client)
|
|
14938
15419
|
return;
|
|
@@ -14987,7 +15468,7 @@ function activityHookAdapters() {
|
|
|
14987
15468
|
return ALL_VENDORS.map((v) => createEngineHookAdapter(v)).filter((a) => a.supportsHooks());
|
|
14988
15469
|
}
|
|
14989
15470
|
function globalSettingsPath() {
|
|
14990
|
-
return
|
|
15471
|
+
return join15(homedir19(), ".claude", "settings.json");
|
|
14991
15472
|
}
|
|
14992
15473
|
function persistedSyncPath(stored) {
|
|
14993
15474
|
if (!stored || stored === "off")
|
|
@@ -14995,7 +15476,7 @@ function persistedSyncPath(stored) {
|
|
|
14995
15476
|
if (stored === "global")
|
|
14996
15477
|
return globalSettingsPath();
|
|
14997
15478
|
if (stored.startsWith("repo:"))
|
|
14998
|
-
return
|
|
15479
|
+
return join15(resolve9(stored.slice(5)), ".claude", "settings.json");
|
|
14999
15480
|
return stored;
|
|
15000
15481
|
}
|
|
15001
15482
|
async function ensureGlobalKobeHooks() {
|
|
@@ -16544,7 +17025,11 @@ class RemoteOrchestrator {
|
|
|
16544
17025
|
});
|
|
16545
17026
|
}
|
|
16546
17027
|
async createTask(input) {
|
|
16547
|
-
const
|
|
17028
|
+
const { modelEffort, ...rest } = input;
|
|
17029
|
+
const res = await this.client.request("task.create", {
|
|
17030
|
+
...rest,
|
|
17031
|
+
effort: modelEffort
|
|
17032
|
+
});
|
|
16548
17033
|
return deserializeTask(res.task);
|
|
16549
17034
|
}
|
|
16550
17035
|
async ensureMainTask(repo) {
|
|
@@ -16717,6 +17202,7 @@ function deserializeTask(s) {
|
|
|
16717
17202
|
pinned: s.pinned,
|
|
16718
17203
|
vendor: s.vendor,
|
|
16719
17204
|
prStatus: s.prStatus,
|
|
17205
|
+
modelEffort: s.modelEffort,
|
|
16720
17206
|
createdAt: s.createdAt,
|
|
16721
17207
|
updatedAt: s.updatedAt
|
|
16722
17208
|
};
|
|
@@ -17043,7 +17529,7 @@ function addTheme2(name, theme) {
|
|
|
17043
17529
|
}
|
|
17044
17530
|
function resolveTheme(theme, mode = "dark") {
|
|
17045
17531
|
const defs = theme.defs ?? {};
|
|
17046
|
-
function
|
|
17532
|
+
function resolve10(c, chain = []) {
|
|
17047
17533
|
if (typeof c === "string") {
|
|
17048
17534
|
if (c === "transparent" || c === "none")
|
|
17049
17535
|
return RGBA.fromInts(0, 0, 0, 0);
|
|
@@ -17055,13 +17541,13 @@ function resolveTheme(theme, mode = "dark") {
|
|
|
17055
17541
|
const next = defs[c] ?? theme.theme[c];
|
|
17056
17542
|
if (next === undefined)
|
|
17057
17543
|
return RGBA.fromInts(0, 0, 0);
|
|
17058
|
-
return
|
|
17544
|
+
return resolve10(next, [...chain, c]);
|
|
17059
17545
|
}
|
|
17060
|
-
return
|
|
17546
|
+
return resolve10(c[mode], chain);
|
|
17061
17547
|
}
|
|
17062
17548
|
const out = {};
|
|
17063
17549
|
for (const [k, v] of Object.entries(theme.theme)) {
|
|
17064
|
-
out[k] =
|
|
17550
|
+
out[k] = resolve10(v);
|
|
17065
17551
|
}
|
|
17066
17552
|
const text = out.text ?? RGBA.fromHex("#ffffff");
|
|
17067
17553
|
const background = out.background ?? RGBA.fromHex("#000000");
|
|
@@ -17538,13 +18024,13 @@ function validateRepoPath(repo) {
|
|
|
17538
18024
|
const trimmed = repo.trim();
|
|
17539
18025
|
if (!trimmed)
|
|
17540
18026
|
return "repo path is required";
|
|
17541
|
-
let
|
|
18027
|
+
let stat5;
|
|
17542
18028
|
try {
|
|
17543
|
-
|
|
18029
|
+
stat5 = fs3.statSync(trimmed);
|
|
17544
18030
|
} catch {
|
|
17545
18031
|
return `path does not exist: ${trimmed}`;
|
|
17546
18032
|
}
|
|
17547
|
-
if (!
|
|
18033
|
+
if (!stat5.isDirectory())
|
|
17548
18034
|
return `not a directory: ${trimmed}`;
|
|
17549
18035
|
try {
|
|
17550
18036
|
const out = spawnSync10("git", ["rev-parse", "--git-dir"], {
|
|
@@ -17726,8 +18212,8 @@ function findAvailableFolderName(parentDir, base) {
|
|
|
17726
18212
|
if (!parentExpanded)
|
|
17727
18213
|
return base;
|
|
17728
18214
|
try {
|
|
17729
|
-
const
|
|
17730
|
-
if (!
|
|
18215
|
+
const stat5 = fs5.statSync(parentExpanded);
|
|
18216
|
+
if (!stat5.isDirectory())
|
|
17731
18217
|
return base;
|
|
17732
18218
|
} catch {
|
|
17733
18219
|
return base;
|
|
@@ -17742,7 +18228,7 @@ function findAvailableFolderName(parentDir, base) {
|
|
|
17742
18228
|
return trimmed;
|
|
17743
18229
|
}
|
|
17744
18230
|
function cloneRepo(url, target, onProgress) {
|
|
17745
|
-
return new Promise((
|
|
18231
|
+
return new Promise((resolve10) => {
|
|
17746
18232
|
let stderrBuf = "";
|
|
17747
18233
|
try {
|
|
17748
18234
|
const child = spawn4("git", ["clone", "--progress", url, target], {
|
|
@@ -17759,18 +18245,18 @@ function cloneRepo(url, target, onProgress) {
|
|
|
17759
18245
|
}
|
|
17760
18246
|
});
|
|
17761
18247
|
child.on("error", (err) => {
|
|
17762
|
-
|
|
18248
|
+
resolve10({ ok: false, error: err.message });
|
|
17763
18249
|
});
|
|
17764
18250
|
child.on("close", (code) => {
|
|
17765
18251
|
if (code === 0) {
|
|
17766
|
-
|
|
18252
|
+
resolve10({ ok: true, path: target });
|
|
17767
18253
|
return;
|
|
17768
18254
|
}
|
|
17769
18255
|
const tail = stderrBuf.split(/[\r\n]+/).filter((s) => s.trim().length > 0).pop() ?? `git clone exited with ${code}`;
|
|
17770
|
-
|
|
18256
|
+
resolve10({ ok: false, error: tail });
|
|
17771
18257
|
});
|
|
17772
18258
|
} catch (err) {
|
|
17773
|
-
|
|
18259
|
+
resolve10({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
17774
18260
|
}
|
|
17775
18261
|
});
|
|
17776
18262
|
}
|
|
@@ -17872,7 +18358,7 @@ function clampCursor(cursor, listLength) {
|
|
|
17872
18358
|
return 0;
|
|
17873
18359
|
return Math.max(0, Math.min(listLength - 1, cursor));
|
|
17874
18360
|
}
|
|
17875
|
-
function
|
|
18361
|
+
function resolveBaseRef(typed, filteredBranches, cursor) {
|
|
17876
18362
|
const picked = filteredBranches[cursor];
|
|
17877
18363
|
if (picked)
|
|
17878
18364
|
return picked;
|
|
@@ -18446,7 +18932,7 @@ function NewTaskDialogView(props) {
|
|
|
18446
18932
|
setBaseRef(stripNewlines(v));
|
|
18447
18933
|
});
|
|
18448
18934
|
setProp(_el$28, "onSubmit", () => {
|
|
18449
|
-
setBaseRef(
|
|
18935
|
+
setBaseRef(resolveBaseRef(baseRef(), branchFiltered(), branchCursor()));
|
|
18450
18936
|
setBaseRefTouched(true);
|
|
18451
18937
|
setField("confirm");
|
|
18452
18938
|
});
|
|
@@ -18960,7 +19446,7 @@ var init_dialog2 = __esm(() => {
|
|
|
18960
19446
|
|
|
18961
19447
|
// src/tui/component/new-task-dialog/index.tsx
|
|
18962
19448
|
function show(dialog, defaultRepo, savedRepos, options) {
|
|
18963
|
-
return new Promise((
|
|
19449
|
+
return new Promise((resolve10) => {
|
|
18964
19450
|
dialog.replace(() => createComponent2(NewTaskDialogView, {
|
|
18965
19451
|
defaultRepo,
|
|
18966
19452
|
savedRepos,
|
|
@@ -18976,9 +19462,9 @@ function show(dialog, defaultRepo, savedRepos, options) {
|
|
|
18976
19462
|
get discoverAdoptable() {
|
|
18977
19463
|
return options?.discoverAdoptable;
|
|
18978
19464
|
},
|
|
18979
|
-
onSubmit: (v) =>
|
|
18980
|
-
onCancel: () =>
|
|
18981
|
-
}), () =>
|
|
19465
|
+
onSubmit: (v) => resolve10(v),
|
|
19466
|
+
onCancel: () => resolve10(undefined)
|
|
19467
|
+
}), () => resolve10(undefined));
|
|
18982
19468
|
dialog.setSize("medium");
|
|
18983
19469
|
});
|
|
18984
19470
|
}
|
|
@@ -19147,11 +19633,11 @@ function QuickTaskComposerView(props) {
|
|
|
19147
19633
|
})();
|
|
19148
19634
|
}
|
|
19149
19635
|
function show2(dialog, opts) {
|
|
19150
|
-
return new Promise((
|
|
19636
|
+
return new Promise((resolve10) => {
|
|
19151
19637
|
dialog.replace(() => createComponent2(QuickTaskComposerView, mergeProps3(opts, {
|
|
19152
|
-
onSubmit: (r) =>
|
|
19153
|
-
onCancel: () =>
|
|
19154
|
-
})), () =>
|
|
19638
|
+
onSubmit: (r) => resolve10(r),
|
|
19639
|
+
onCancel: () => resolve10(undefined)
|
|
19640
|
+
})), () => resolve10(undefined));
|
|
19155
19641
|
dialog.setSize("medium");
|
|
19156
19642
|
});
|
|
19157
19643
|
}
|
|
@@ -19922,7 +20408,7 @@ var init_pulse = () => {};
|
|
|
19922
20408
|
// src/tui/lib/sound.ts
|
|
19923
20409
|
import { existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
|
|
19924
20410
|
import { tmpdir as tmpdir2 } from "os";
|
|
19925
|
-
import { basename as basename6, isAbsolute as
|
|
20411
|
+
import { basename as basename6, isAbsolute as isAbsolute3, join as join17, resolve as resolve10 } from "path";
|
|
19926
20412
|
function args(player, file, volume) {
|
|
19927
20413
|
if (player === "ffplay")
|
|
19928
20414
|
return [player, "-autoexit", "-nodisp", "-af", `volume=${volume}`, file];
|
|
@@ -19945,13 +20431,13 @@ function pickPlayer() {
|
|
|
19945
20431
|
return cachedPlayer;
|
|
19946
20432
|
const path12 = process.env.PATH ?? "";
|
|
19947
20433
|
const segments = path12.split(":").filter(Boolean);
|
|
19948
|
-
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(
|
|
20434
|
+
cachedPlayer = PLAYERS.find((p) => segments.some((dir) => existsSync14(join17(dir, p)))) ?? null;
|
|
19949
20435
|
return cachedPlayer;
|
|
19950
20436
|
}
|
|
19951
20437
|
async function ensureAsset() {
|
|
19952
20438
|
cachedPath ??= (async () => {
|
|
19953
20439
|
mkdirSync6(DIR, { recursive: true });
|
|
19954
|
-
const dest =
|
|
20440
|
+
const dest = join17(DIR, basename6(pulseAsset));
|
|
19955
20441
|
const out = Bun.file(dest);
|
|
19956
20442
|
if (await out.exists())
|
|
19957
20443
|
return dest;
|
|
@@ -19980,8 +20466,8 @@ function pulse(volume = 0.4) {
|
|
|
19980
20466
|
var pulseAsset, DIR, PLAYERS, cachedPlayer, cachedPath;
|
|
19981
20467
|
var init_sound = __esm(() => {
|
|
19982
20468
|
init_pulse();
|
|
19983
|
-
pulseAsset =
|
|
19984
|
-
DIR =
|
|
20469
|
+
pulseAsset = isAbsolute3(pulse_default) ? pulse_default : resolve10(import.meta.dir, pulse_default);
|
|
20470
|
+
DIR = join17(tmpdir2(), "kobe-sfx");
|
|
19985
20471
|
PLAYERS = [
|
|
19986
20472
|
"ffplay",
|
|
19987
20473
|
"mpv",
|
|
@@ -20999,7 +21485,7 @@ var init_dialog3 = __esm(() => {
|
|
|
20999
21485
|
|
|
21000
21486
|
// src/tui/component/rename-task-dialog/index.tsx
|
|
21001
21487
|
function show3(dialog, currentTitle, opts = {}) {
|
|
21002
|
-
return new Promise((
|
|
21488
|
+
return new Promise((resolve11) => {
|
|
21003
21489
|
dialog.replace(() => createComponent2(RenameTaskDialogView, {
|
|
21004
21490
|
currentTitle,
|
|
21005
21491
|
get dialogTitle() {
|
|
@@ -21017,9 +21503,9 @@ function show3(dialog, currentTitle, opts = {}) {
|
|
|
21017
21503
|
get allowEmpty() {
|
|
21018
21504
|
return opts.allowEmpty;
|
|
21019
21505
|
},
|
|
21020
|
-
onSubmit: (v) =>
|
|
21021
|
-
onCancel: () =>
|
|
21022
|
-
}), () =>
|
|
21506
|
+
onSubmit: (v) => resolve11(v),
|
|
21507
|
+
onCancel: () => resolve11(undefined)
|
|
21508
|
+
}), () => resolve11(undefined));
|
|
21023
21509
|
});
|
|
21024
21510
|
}
|
|
21025
21511
|
var RenameTaskDialog;
|
|
@@ -21031,22 +21517,6 @@ var init_rename_task_dialog = __esm(() => {
|
|
|
21031
21517
|
};
|
|
21032
21518
|
});
|
|
21033
21519
|
|
|
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
21520
|
// src/tui/ui/dialog-confirm.tsx
|
|
21051
21521
|
import { TextAttributes as TextAttributes6 } from "@opentui/core";
|
|
21052
21522
|
function titlecase(s) {
|
|
@@ -21157,18 +21627,18 @@ var init_dialog_confirm = __esm(() => {
|
|
|
21157
21627
|
init_keymap();
|
|
21158
21628
|
init_dialog();
|
|
21159
21629
|
DialogConfirm.show = (dialog, title, message, label, confirmLabel, options) => {
|
|
21160
|
-
return new Promise((
|
|
21630
|
+
return new Promise((resolve11) => {
|
|
21161
21631
|
dialog.replace(() => createComponent2(DialogConfirm, {
|
|
21162
21632
|
title,
|
|
21163
21633
|
message,
|
|
21164
|
-
onConfirm: () =>
|
|
21165
|
-
onCancel: () =>
|
|
21634
|
+
onConfirm: () => resolve11(true),
|
|
21635
|
+
onCancel: () => resolve11(false),
|
|
21166
21636
|
label,
|
|
21167
21637
|
confirmLabel,
|
|
21168
21638
|
get initialActive() {
|
|
21169
21639
|
return options?.initialActive;
|
|
21170
21640
|
}
|
|
21171
|
-
}), () =>
|
|
21641
|
+
}), () => resolve11(undefined));
|
|
21172
21642
|
dialog.setSize("small");
|
|
21173
21643
|
});
|
|
21174
21644
|
};
|
|
@@ -21176,7 +21646,7 @@ var init_dialog_confirm = __esm(() => {
|
|
|
21176
21646
|
|
|
21177
21647
|
// src/tui/component/settings-dialog/actions.ts
|
|
21178
21648
|
import { unlinkSync as unlinkSync2 } from "fs";
|
|
21179
|
-
import { join as
|
|
21649
|
+
import { join as join18 } from "path";
|
|
21180
21650
|
function hasRestartableDaemon(orchestrator) {
|
|
21181
21651
|
return orchestrator instanceof RemoteOrchestrator;
|
|
21182
21652
|
}
|
|
@@ -21193,7 +21663,7 @@ async function confirmResetState(dialog, kv, renderer) {
|
|
|
21193
21663
|
return;
|
|
21194
21664
|
kv.clear();
|
|
21195
21665
|
try {
|
|
21196
|
-
unlinkSync2(
|
|
21666
|
+
unlinkSync2(join18(homeDir(), ".kobe", "tasks.json"));
|
|
21197
21667
|
} catch (err) {
|
|
21198
21668
|
if (err.code !== "ENOENT") {
|
|
21199
21669
|
console.error("kobe: failed to delete tasks.json during reset:", err);
|
|
@@ -22507,7 +22977,7 @@ var init_sections = __esm(() => {
|
|
|
22507
22977
|
|
|
22508
22978
|
// src/tui/component/settings-dialog.tsx
|
|
22509
22979
|
import { TextAttributes as TextAttributes8 } from "@opentui/core";
|
|
22510
|
-
function
|
|
22980
|
+
function humanizeSlug2(id) {
|
|
22511
22981
|
return id.split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
22512
22982
|
}
|
|
22513
22983
|
function SettingsDialog(props) {
|
|
@@ -22622,7 +23092,7 @@ function SettingsDialog(props) {
|
|
|
22622
23092
|
const v = props.kv.get(engineCommandKey(vendor), "");
|
|
22623
23093
|
return typeof v === "string" ? v.trim() : "";
|
|
22624
23094
|
}
|
|
22625
|
-
function
|
|
23095
|
+
function engineCommandText2(vendor) {
|
|
22626
23096
|
return engineOverride(vendor) || defaultEngineCommand(vendor).join(" ");
|
|
22627
23097
|
}
|
|
22628
23098
|
function engineIsDefault(vendor) {
|
|
@@ -22648,7 +23118,7 @@ function SettingsDialog(props) {
|
|
|
22648
23118
|
setDefaultEngineSig(vendor);
|
|
22649
23119
|
}
|
|
22650
23120
|
async function editEngine(vendor) {
|
|
22651
|
-
const next = await RenameTaskDialog.show(dialog,
|
|
23121
|
+
const next = await RenameTaskDialog.show(dialog, engineCommandText2(vendor), {
|
|
22652
23122
|
dialogTitle: `${engineName(vendor)} launch command`,
|
|
22653
23123
|
fieldLabel: "command",
|
|
22654
23124
|
submitLabel: "save",
|
|
@@ -22707,7 +23177,7 @@ function SettingsDialog(props) {
|
|
|
22707
23177
|
if (command.trim())
|
|
22708
23178
|
props.kv.set(engineCommandKey(id), command.trim());
|
|
22709
23179
|
const typedName = name?.trim() ?? "";
|
|
22710
|
-
props.kv.set(engineNameKey(id), typedName && typedName !== id ? typedName :
|
|
23180
|
+
props.kv.set(engineNameKey(id), typedName && typedName !== id ? typedName : humanizeSlug2(id));
|
|
22711
23181
|
}
|
|
22712
23182
|
function currentEngineRow() {
|
|
22713
23183
|
if (section() !== "engines" || level() !== "body")
|
|
@@ -22973,7 +23443,7 @@ function SettingsDialog(props) {
|
|
|
22973
23443
|
},
|
|
22974
23444
|
isCustom: (v) => !isBuiltinVendor(v),
|
|
22975
23445
|
displayName: engineName,
|
|
22976
|
-
commandText:
|
|
23446
|
+
commandText: engineCommandText2,
|
|
22977
23447
|
isDefault: engineIsDefault,
|
|
22978
23448
|
isDefaultEngine,
|
|
22979
23449
|
editEngine: (v) => void editEngine(v),
|
|
@@ -23090,17 +23560,17 @@ var init_settings_dialog = __esm(() => {
|
|
|
23090
23560
|
init_sections();
|
|
23091
23561
|
SettingsDialog.show = (dialog, kv, orchestrator) => {
|
|
23092
23562
|
let visualPrefsChanged = false;
|
|
23093
|
-
return new Promise((
|
|
23563
|
+
return new Promise((resolve11) => {
|
|
23094
23564
|
dialog.replace(() => createComponent2(SettingsDialog, {
|
|
23095
23565
|
kv,
|
|
23096
23566
|
orchestrator,
|
|
23097
23567
|
onVisualPrefsChange: () => {
|
|
23098
23568
|
visualPrefsChanged = true;
|
|
23099
23569
|
},
|
|
23100
|
-
onClose: () =>
|
|
23570
|
+
onClose: () => resolve11({
|
|
23101
23571
|
visualPrefsChanged
|
|
23102
23572
|
})
|
|
23103
|
-
}), () =>
|
|
23573
|
+
}), () => resolve11({
|
|
23104
23574
|
visualPrefsChanged
|
|
23105
23575
|
}));
|
|
23106
23576
|
});
|
|
@@ -23502,15 +23972,15 @@ var init_task_actions = __esm(() => {
|
|
|
23502
23972
|
// src/tui/lib/worktree-opener.ts
|
|
23503
23973
|
import { spawn as spawn5 } from "child_process";
|
|
23504
23974
|
import { existsSync as existsSync15 } from "fs";
|
|
23505
|
-
import { basename as basename7, delimiter, isAbsolute as
|
|
23975
|
+
import { basename as basename7, delimiter, isAbsolute as isAbsolute4, join as join19 } from "path";
|
|
23506
23976
|
function executableOnPath(command, env, exists) {
|
|
23507
|
-
if (
|
|
23977
|
+
if (isAbsolute4(command))
|
|
23508
23978
|
return exists(command);
|
|
23509
23979
|
const pathEnv = env.PATH ?? "";
|
|
23510
23980
|
for (const dir of pathEnv.split(delimiter)) {
|
|
23511
23981
|
if (!dir)
|
|
23512
23982
|
continue;
|
|
23513
|
-
if (exists(
|
|
23983
|
+
if (exists(join19(dir, command)))
|
|
23514
23984
|
return true;
|
|
23515
23985
|
}
|
|
23516
23986
|
return false;
|
|
@@ -23643,11 +24113,11 @@ var init_background_poll = __esm(() => {
|
|
|
23643
24113
|
});
|
|
23644
24114
|
|
|
23645
24115
|
// src/tui/panes/sidebar/git-head.ts
|
|
23646
|
-
import { stat as
|
|
23647
|
-
import { join as
|
|
24116
|
+
import { stat as stat5 } from "fs/promises";
|
|
24117
|
+
import { join as join20 } from "path";
|
|
23648
24118
|
async function headFingerprint(repo) {
|
|
23649
24119
|
try {
|
|
23650
|
-
const st = await
|
|
24120
|
+
const st = await stat5(join20(repo, ".git", "HEAD"));
|
|
23651
24121
|
return `${st.mtimeMs}:${st.size}`;
|
|
23652
24122
|
} catch {
|
|
23653
24123
|
return null;
|
|
@@ -24130,6 +24600,28 @@ function Sidebar(props) {
|
|
|
24130
24600
|
const dims = useTerminalDimensions();
|
|
24131
24601
|
const [hover, setHover] = createSignal(null);
|
|
24132
24602
|
const [cursorIndex, setCursorIndex] = createSignal(-1);
|
|
24603
|
+
let scrollRef;
|
|
24604
|
+
let outerBoxRef;
|
|
24605
|
+
const rowEls = new Map;
|
|
24606
|
+
createEffect(() => {
|
|
24607
|
+
const w = props.width ? props.width() : SIDEBAR_WIDTH;
|
|
24608
|
+
const el = outerBoxRef;
|
|
24609
|
+
if (!el)
|
|
24610
|
+
return;
|
|
24611
|
+
el.width = w;
|
|
24612
|
+
el.flexShrink = 1;
|
|
24613
|
+
el.minHeight = 0;
|
|
24614
|
+
});
|
|
24615
|
+
createEffect(on([cursorIndex, rows], ([i]) => {
|
|
24616
|
+
if (!scrollRef)
|
|
24617
|
+
return;
|
|
24618
|
+
if (scrollRef.viewport.height <= 0)
|
|
24619
|
+
return;
|
|
24620
|
+
const el = rowEls.get(i);
|
|
24621
|
+
if (!el)
|
|
24622
|
+
return;
|
|
24623
|
+
scrollRef.scrollChildIntoView(el.id);
|
|
24624
|
+
}));
|
|
24133
24625
|
createEffect(on(() => [props.selectedId(), flatIds()], ([id, ids]) => {
|
|
24134
24626
|
const cur = untrack(cursorIndex);
|
|
24135
24627
|
if (id === null) {
|
|
@@ -24242,7 +24734,11 @@ function Sidebar(props) {
|
|
|
24242
24734
|
insertNode(_el$5, _el$6);
|
|
24243
24735
|
insertNode(_el$5, _el$22);
|
|
24244
24736
|
insertNode(_el$5, _el$28);
|
|
24245
|
-
|
|
24737
|
+
use((r) => {
|
|
24738
|
+
outerBoxRef = r;
|
|
24739
|
+
}, _el$5);
|
|
24740
|
+
setProp(_el$5, "flexGrow", 1);
|
|
24741
|
+
setProp(_el$5, "minHeight", 0);
|
|
24246
24742
|
setProp(_el$5, "flexDirection", "column");
|
|
24247
24743
|
setProp(_el$5, "paddingTop", 1);
|
|
24248
24744
|
setProp(_el$5, "paddingBottom", 1);
|
|
@@ -24270,9 +24766,9 @@ function Sidebar(props) {
|
|
|
24270
24766
|
setProp(_el$35, "onMouseUp", () => props.onHeaderStatusClick?.());
|
|
24271
24767
|
insert(_el$35, () => status().label);
|
|
24272
24768
|
effect((_p$) => {
|
|
24273
|
-
var _v$
|
|
24274
|
-
_v$
|
|
24275
|
-
_v$
|
|
24769
|
+
var _v$19 = status().emphasize ? theme.warning : theme.textMuted, _v$20 = status().emphasize ? TextAttributes11.BOLD : TextAttributes11.DIM;
|
|
24770
|
+
_v$19 !== _p$.e && (_p$.e = setProp(_el$35, "fg", _v$19, _p$.e));
|
|
24771
|
+
_v$20 !== _p$.t && (_p$.t = setProp(_el$35, "attributes", _v$20, _p$.t));
|
|
24276
24772
|
return _p$;
|
|
24277
24773
|
}, {
|
|
24278
24774
|
e: undefined,
|
|
@@ -24384,9 +24880,9 @@ function Sidebar(props) {
|
|
|
24384
24880
|
setProp(_el$36, "onMouseUp", () => setView(tab.view));
|
|
24385
24881
|
insert(_el$36, () => tab.label);
|
|
24386
24882
|
effect((_p$) => {
|
|
24387
|
-
var _v$
|
|
24388
|
-
_v$
|
|
24389
|
-
_v$
|
|
24883
|
+
var _v$21 = active() ? theme.primary : theme.textMuted, _v$22 = active() ? TextAttributes11.BOLD : undefined;
|
|
24884
|
+
_v$21 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$21, _p$.e));
|
|
24885
|
+
_v$22 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$22, _p$.t));
|
|
24390
24886
|
return _p$;
|
|
24391
24887
|
}, {
|
|
24392
24888
|
e: undefined,
|
|
@@ -24420,7 +24916,11 @@ function Sidebar(props) {
|
|
|
24420
24916
|
}
|
|
24421
24917
|
}), null);
|
|
24422
24918
|
insertNode(_el$28, _el$29);
|
|
24919
|
+
use((r) => {
|
|
24920
|
+
scrollRef = r;
|
|
24921
|
+
}, _el$28);
|
|
24423
24922
|
setProp(_el$28, "flexGrow", 1);
|
|
24923
|
+
setProp(_el$28, "minHeight", 0);
|
|
24424
24924
|
setProp(_el$28, "verticalScrollbarOptions", {
|
|
24425
24925
|
trackOptions: {
|
|
24426
24926
|
foregroundColor: "transparent"
|
|
@@ -24529,6 +25029,13 @@ function Sidebar(props) {
|
|
|
24529
25029
|
});
|
|
24530
25030
|
}
|
|
24531
25031
|
}), _el$38);
|
|
25032
|
+
use((r) => {
|
|
25033
|
+
rowEls.set(flatIndex, r);
|
|
25034
|
+
onCleanup(() => {
|
|
25035
|
+
if (rowEls.get(flatIndex) === r)
|
|
25036
|
+
rowEls.delete(flatIndex);
|
|
25037
|
+
});
|
|
25038
|
+
}, _el$38);
|
|
24532
25039
|
setProp(_el$38, "flexDirection", "column");
|
|
24533
25040
|
setProp(_el$38, "gap", 0);
|
|
24534
25041
|
setProp(_el$38, "onMouseUp", () => {
|
|
@@ -24565,12 +25072,12 @@ function Sidebar(props) {
|
|
|
24565
25072
|
setProp(_el$43, "flexGrow", 1);
|
|
24566
25073
|
insert(_el$43, () => spacedTitle(rowView().titleText, titleBudget()));
|
|
24567
25074
|
effect((_p$) => {
|
|
24568
|
-
var _v$
|
|
24569
|
-
_v$
|
|
24570
|
-
_v$
|
|
24571
|
-
_v$
|
|
24572
|
-
_v$
|
|
24573
|
-
_v$
|
|
25075
|
+
var _v$23 = barColor(), _v$24 = stateColor(), _v$25 = TextAttributes11.BOLD, _v$26 = theme.text, _v$27 = TextAttributes11.BOLD;
|
|
25076
|
+
_v$23 !== _p$.e && (_p$.e = setProp(_el$40, "fg", _v$23, _p$.e));
|
|
25077
|
+
_v$24 !== _p$.t && (_p$.t = setProp(_el$42, "fg", _v$24, _p$.t));
|
|
25078
|
+
_v$25 !== _p$.a && (_p$.a = setProp(_el$42, "attributes", _v$25, _p$.a));
|
|
25079
|
+
_v$26 !== _p$.o && (_p$.o = setProp(_el$43, "fg", _v$26, _p$.o));
|
|
25080
|
+
_v$27 !== _p$.i && (_p$.i = setProp(_el$43, "attributes", _v$27, _p$.i));
|
|
24574
25081
|
return _p$;
|
|
24575
25082
|
}, {
|
|
24576
25083
|
e: undefined,
|
|
@@ -24624,10 +25131,10 @@ function Sidebar(props) {
|
|
|
24624
25131
|
}
|
|
24625
25132
|
}), null);
|
|
24626
25133
|
effect((_p$) => {
|
|
24627
|
-
var _v$
|
|
24628
|
-
_v$
|
|
24629
|
-
_v$
|
|
24630
|
-
_v$
|
|
25134
|
+
var _v$28 = barColor(), _v$29 = theme.textMuted, _v$30 = TextAttributes11.DIM;
|
|
25135
|
+
_v$28 !== _p$.e && (_p$.e = setProp(_el$45, "fg", _v$28, _p$.e));
|
|
25136
|
+
_v$29 !== _p$.t && (_p$.t = setProp(_el$47, "fg", _v$29, _p$.t));
|
|
25137
|
+
_v$30 !== _p$.a && (_p$.a = setProp(_el$47, "attributes", _v$30, _p$.a));
|
|
24631
25138
|
return _p$;
|
|
24632
25139
|
}, {
|
|
24633
25140
|
e: undefined,
|
|
@@ -24673,12 +25180,12 @@ function Sidebar(props) {
|
|
|
24673
25180
|
}
|
|
24674
25181
|
}), null);
|
|
24675
25182
|
effect((_p$) => {
|
|
24676
|
-
var _v$
|
|
24677
|
-
_v$
|
|
24678
|
-
_v$
|
|
24679
|
-
_v$
|
|
24680
|
-
_v$
|
|
24681
|
-
_v$
|
|
25183
|
+
var _v$31 = barColor(), _v$32 = stateColor(), _v$33 = TextAttributes11.BOLD, _v$34 = theme.text, _v$35 = isSelected() || isCursor() ? TextAttributes11.BOLD : undefined;
|
|
25184
|
+
_v$31 !== _p$.e && (_p$.e = setProp(_el$53, "fg", _v$31, _p$.e));
|
|
25185
|
+
_v$32 !== _p$.t && (_p$.t = setProp(_el$55, "fg", _v$32, _p$.t));
|
|
25186
|
+
_v$33 !== _p$.a && (_p$.a = setProp(_el$55, "attributes", _v$33, _p$.a));
|
|
25187
|
+
_v$34 !== _p$.o && (_p$.o = setProp(_el$56, "fg", _v$34, _p$.o));
|
|
25188
|
+
_v$35 !== _p$.i && (_p$.i = setProp(_el$56, "attributes", _v$35, _p$.i));
|
|
24682
25189
|
return _p$;
|
|
24683
25190
|
}, {
|
|
24684
25191
|
e: undefined,
|
|
@@ -24744,10 +25251,10 @@ function Sidebar(props) {
|
|
|
24744
25251
|
}
|
|
24745
25252
|
}), null);
|
|
24746
25253
|
effect((_p$) => {
|
|
24747
|
-
var _v$
|
|
24748
|
-
_v$
|
|
24749
|
-
_v$
|
|
24750
|
-
_v$
|
|
25254
|
+
var _v$36 = barColor(), _v$37 = theme.textMuted, _v$38 = TextAttributes11.DIM;
|
|
25255
|
+
_v$36 !== _p$.e && (_p$.e = setProp(_el$60, "fg", _v$36, _p$.e));
|
|
25256
|
+
_v$37 !== _p$.t && (_p$.t = setProp(_el$62, "fg", _v$37, _p$.t));
|
|
25257
|
+
_v$38 !== _p$.a && (_p$.a = setProp(_el$62, "attributes", _v$38, _p$.a));
|
|
24751
25258
|
return _p$;
|
|
24752
25259
|
}, {
|
|
24753
25260
|
e: undefined,
|
|
@@ -24852,9 +25359,9 @@ function Sidebar(props) {
|
|
|
24852
25359
|
return () => _c$2() ? truncatePathTail(l.text, innerW()) : truncateTitle(l.text, innerW());
|
|
24853
25360
|
})());
|
|
24854
25361
|
effect((_p$) => {
|
|
24855
|
-
var _v$
|
|
24856
|
-
_v$
|
|
24857
|
-
_v$
|
|
25362
|
+
var _v$44 = l.dim ? theme.textMuted : theme.text, _v$45 = l.bold ? TextAttributes11.BOLD : l.dim ? TextAttributes11.DIM : undefined;
|
|
25363
|
+
_v$44 !== _p$.e && (_p$.e = setProp(_el$70, "fg", _v$44, _p$.e));
|
|
25364
|
+
_v$45 !== _p$.t && (_p$.t = setProp(_el$70, "attributes", _v$45, _p$.t));
|
|
24858
25365
|
return _p$;
|
|
24859
25366
|
}, {
|
|
24860
25367
|
e: undefined,
|
|
@@ -24864,12 +25371,12 @@ function Sidebar(props) {
|
|
|
24864
25371
|
})()
|
|
24865
25372
|
}));
|
|
24866
25373
|
effect((_p$) => {
|
|
24867
|
-
var _v$
|
|
24868
|
-
_v$
|
|
24869
|
-
_v$
|
|
24870
|
-
_v$
|
|
24871
|
-
_v$
|
|
24872
|
-
_v$
|
|
25374
|
+
var _v$39 = left(), _v$40 = top(), _v$41 = boxW(), _v$42 = theme.focusAccent, _v$43 = theme.backgroundElement;
|
|
25375
|
+
_v$39 !== _p$.e && (_p$.e = setProp(_el$69, "left", _v$39, _p$.e));
|
|
25376
|
+
_v$40 !== _p$.t && (_p$.t = setProp(_el$69, "top", _v$40, _p$.t));
|
|
25377
|
+
_v$41 !== _p$.a && (_p$.a = setProp(_el$69, "width", _v$41, _p$.a));
|
|
25378
|
+
_v$42 !== _p$.o && (_p$.o = setProp(_el$69, "borderColor", _v$42, _p$.o));
|
|
25379
|
+
_v$43 !== _p$.i && (_p$.i = setProp(_el$69, "backgroundColor", _v$43, _p$.i));
|
|
24873
25380
|
return _p$;
|
|
24874
25381
|
}, {
|
|
24875
25382
|
e: undefined,
|
|
@@ -24883,19 +25390,17 @@ function Sidebar(props) {
|
|
|
24883
25390
|
}
|
|
24884
25391
|
}), null);
|
|
24885
25392
|
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));
|
|
25393
|
+
var _v$15 = focusedAccessor() ? theme.focusAccent : theme.textMuted, _v$16 = TextAttributes11.BOLD, _v$17 = theme.textMuted, _v$18 = TextAttributes11.DIM;
|
|
25394
|
+
_v$15 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$15, _p$.e));
|
|
25395
|
+
_v$16 !== _p$.t && (_p$.t = setProp(_el$8, "attributes", _v$16, _p$.t));
|
|
25396
|
+
_v$17 !== _p$.a && (_p$.a = setProp(_el$24, "fg", _v$17, _p$.a));
|
|
25397
|
+
_v$18 !== _p$.o && (_p$.o = setProp(_el$24, "attributes", _v$18, _p$.o));
|
|
24892
25398
|
return _p$;
|
|
24893
25399
|
}, {
|
|
24894
25400
|
e: undefined,
|
|
24895
25401
|
t: undefined,
|
|
24896
25402
|
a: undefined,
|
|
24897
|
-
o: undefined
|
|
24898
|
-
i: undefined
|
|
25403
|
+
o: undefined
|
|
24899
25404
|
});
|
|
24900
25405
|
return _el$5;
|
|
24901
25406
|
})();
|
|
@@ -24911,6 +25416,7 @@ var init_Sidebar = __esm(() => {
|
|
|
24911
25416
|
init_solid();
|
|
24912
25417
|
init_solid();
|
|
24913
25418
|
init_solid();
|
|
25419
|
+
init_solid();
|
|
24914
25420
|
init_dev();
|
|
24915
25421
|
init_theme2();
|
|
24916
25422
|
init_theme2();
|
|
@@ -24937,7 +25443,7 @@ __export(exports_host3, {
|
|
|
24937
25443
|
legendCap: () => legendCap
|
|
24938
25444
|
});
|
|
24939
25445
|
import { existsSync as existsSync16 } from "fs";
|
|
24940
|
-
import { stat as
|
|
25446
|
+
import { stat as stat6 } from "fs/promises";
|
|
24941
25447
|
import { TextAttributes as TextAttributes12 } from "@opentui/core";
|
|
24942
25448
|
function worktreeCwdUsable(cwd) {
|
|
24943
25449
|
return !!cwd && worktreeUsable(cwd);
|
|
@@ -25373,52 +25879,6 @@ function ShortcutHints(props) {
|
|
|
25373
25879
|
label: "move panes"
|
|
25374
25880
|
});
|
|
25375
25881
|
}
|
|
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
25882
|
if (b["tmux.detach"])
|
|
25423
25883
|
out.push({
|
|
25424
25884
|
k: b["tmux.detach"].chord,
|
|
@@ -25430,12 +25890,8 @@ function ShortcutHints(props) {
|
|
|
25430
25890
|
keymapVersion();
|
|
25431
25891
|
const rows = [
|
|
25432
25892
|
{
|
|
25433
|
-
ids: ["
|
|
25434
|
-
label: "
|
|
25435
|
-
},
|
|
25436
|
-
{
|
|
25437
|
-
ids: ["tasks.focusEngine"],
|
|
25438
|
-
label: "focus engine"
|
|
25893
|
+
ids: ["help.open"],
|
|
25894
|
+
label: "full help"
|
|
25439
25895
|
},
|
|
25440
25896
|
{
|
|
25441
25897
|
ids: ["task.new"],
|
|
@@ -25446,34 +25902,24 @@ function ShortcutHints(props) {
|
|
|
25446
25902
|
label: "settings"
|
|
25447
25903
|
},
|
|
25448
25904
|
{
|
|
25449
|
-
ids: ["
|
|
25450
|
-
label: "open
|
|
25451
|
-
},
|
|
25452
|
-
{
|
|
25453
|
-
ids: ["sidebar.view"],
|
|
25454
|
-
label: "views"
|
|
25455
|
-
},
|
|
25456
|
-
{
|
|
25457
|
-
ids: ["sidebar.sort"],
|
|
25458
|
-
label: "sort"
|
|
25905
|
+
ids: ["sidebar.select"],
|
|
25906
|
+
label: "open"
|
|
25459
25907
|
},
|
|
25460
25908
|
{
|
|
25461
|
-
ids: ["
|
|
25462
|
-
label: "
|
|
25463
|
-
dimWhenMain: true
|
|
25909
|
+
ids: ["tasks.focusEngine"],
|
|
25910
|
+
label: "focus engine"
|
|
25464
25911
|
},
|
|
25465
25912
|
{
|
|
25466
|
-
ids: ["
|
|
25467
|
-
label: "
|
|
25913
|
+
ids: ["tasks.openWorktree"],
|
|
25914
|
+
label: "open wt"
|
|
25468
25915
|
},
|
|
25469
25916
|
{
|
|
25470
|
-
ids: ["sidebar.
|
|
25471
|
-
label: "
|
|
25472
|
-
dimWhenMain: true
|
|
25917
|
+
ids: ["sidebar.delete"],
|
|
25918
|
+
label: "delete"
|
|
25473
25919
|
},
|
|
25474
25920
|
{
|
|
25475
|
-
ids: ["
|
|
25476
|
-
label: "
|
|
25921
|
+
ids: ["sidebar.view"],
|
|
25922
|
+
label: "views"
|
|
25477
25923
|
}
|
|
25478
25924
|
];
|
|
25479
25925
|
const out = [];
|
|
@@ -25617,7 +26063,7 @@ async function setupTasksPane(opts) {
|
|
|
25617
26063
|
(async () => {
|
|
25618
26064
|
let fingerprint = "missing";
|
|
25619
26065
|
try {
|
|
25620
|
-
const st = await
|
|
26066
|
+
const st = await stat6(store2.filePath);
|
|
25621
26067
|
fingerprint = `${st.mtimeMs}:${st.size}`;
|
|
25622
26068
|
} catch {}
|
|
25623
26069
|
if (fingerprint === lastTasksFileFingerprint)
|
|
@@ -25889,18 +26335,18 @@ function openExternalUrl(url) {
|
|
|
25889
26335
|
function releaseBodyLines(body) {
|
|
25890
26336
|
return body.replace(/\r\n/g, `
|
|
25891
26337
|
`).split(`
|
|
25892
|
-
`).map((line) => line.trim()).filter(Boolean)
|
|
26338
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
25893
26339
|
}
|
|
25894
26340
|
function waitForKeypress() {
|
|
25895
26341
|
if (!process.stdin.isTTY)
|
|
25896
26342
|
return Promise.resolve();
|
|
25897
|
-
return new Promise((
|
|
26343
|
+
return new Promise((resolve11) => {
|
|
25898
26344
|
const stdin = process.stdin;
|
|
25899
26345
|
const done = () => {
|
|
25900
26346
|
stdin.off("data", done);
|
|
25901
26347
|
stdin.setRawMode?.(false);
|
|
25902
26348
|
stdin.pause();
|
|
25903
|
-
|
|
26349
|
+
resolve11();
|
|
25904
26350
|
};
|
|
25905
26351
|
stdin.setRawMode?.(true);
|
|
25906
26352
|
stdin.resume();
|
|
@@ -25913,13 +26359,12 @@ function UpdatePage() {
|
|
|
25913
26359
|
} = useTheme();
|
|
25914
26360
|
const renderer = useRenderer();
|
|
25915
26361
|
const [info, setInfo] = createSignal(null);
|
|
25916
|
-
const [
|
|
26362
|
+
const [releaseNotes, setReleaseNotes] = createSignal([]);
|
|
25917
26363
|
const [loadingNotes, setLoadingNotes] = createSignal(true);
|
|
25918
26364
|
const [selected, setSelected] = createSignal("update");
|
|
25919
26365
|
const [status, setStatus] = createSignal(null);
|
|
25920
26366
|
const latest = createMemo(() => info()?.latest ?? CURRENT_VERSION);
|
|
25921
|
-
const releaseUrl = createMemo(() =>
|
|
25922
|
-
const lines = createMemo(() => releaseBodyLines(notes()?.body ?? ""));
|
|
26367
|
+
const releaseUrl = createMemo(() => releaseNotes()[0]?.url ?? releasePageUrl(latest()));
|
|
25923
26368
|
const actions = createMemo(() => [{
|
|
25924
26369
|
id: "update",
|
|
25925
26370
|
key: "U",
|
|
@@ -25944,9 +26389,12 @@ function UpdatePage() {
|
|
|
25944
26389
|
force: true
|
|
25945
26390
|
});
|
|
25946
26391
|
setInfo(next);
|
|
25947
|
-
const
|
|
25948
|
-
const fetched = await
|
|
25949
|
-
|
|
26392
|
+
const latestVersion = next?.latest ?? CURRENT_VERSION;
|
|
26393
|
+
const fetched = await fetchReleaseNotesRange({
|
|
26394
|
+
current: CURRENT_VERSION,
|
|
26395
|
+
latest: latestVersion
|
|
26396
|
+
});
|
|
26397
|
+
setReleaseNotes(fetched);
|
|
25950
26398
|
setLoadingNotes(false);
|
|
25951
26399
|
}
|
|
25952
26400
|
function move(delta) {
|
|
@@ -25966,7 +26414,7 @@ function UpdatePage() {
|
|
|
25966
26414
|
}
|
|
25967
26415
|
async function runUpdater() {
|
|
25968
26416
|
setStatus("Leaving the TUI page and running the updater in this tmux window...");
|
|
25969
|
-
await new Promise((
|
|
26417
|
+
await new Promise((resolve11) => setTimeout(resolve11, 30));
|
|
25970
26418
|
renderer?.destroy();
|
|
25971
26419
|
process.stdout.write(`
|
|
25972
26420
|
kobe ${CURRENT_VERSION} -> latest
|
|
@@ -26025,12 +26473,12 @@ kobe update failed with exit code ${code}.
|
|
|
26025
26473
|
}]
|
|
26026
26474
|
}));
|
|
26027
26475
|
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$
|
|
26476
|
+
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
26477
|
insertNode(_el$, _el$2);
|
|
26030
26478
|
insertNode(_el$, _el$7);
|
|
26031
26479
|
insertNode(_el$, _el$14);
|
|
26032
26480
|
insertNode(_el$, _el$16);
|
|
26033
|
-
insertNode(_el$, _el$
|
|
26481
|
+
insertNode(_el$, _el$21);
|
|
26034
26482
|
setProp(_el$, "flexDirection", "column");
|
|
26035
26483
|
setProp(_el$, "flexGrow", 1);
|
|
26036
26484
|
setProp(_el$, "paddingTop", 1);
|
|
@@ -26073,36 +26521,36 @@ kobe update failed with exit code ${code}.
|
|
|
26073
26521
|
return actions();
|
|
26074
26522
|
},
|
|
26075
26523
|
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);
|
|
26524
|
+
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
26525
|
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
|
-
|
|
26526
|
+
insertNode(_el$27, _el$32);
|
|
26527
|
+
insertNode(_el$27, _el$34);
|
|
26528
|
+
setProp(_el$27, "flexDirection", "row");
|
|
26529
|
+
setProp(_el$27, "gap", 1);
|
|
26530
|
+
setProp(_el$27, "paddingLeft", 1);
|
|
26531
|
+
setProp(_el$27, "paddingRight", 1);
|
|
26532
|
+
setProp(_el$27, "onMouseUp", () => activate(action.id));
|
|
26533
|
+
insertNode(_el$28, _el$29);
|
|
26534
|
+
setProp(_el$28, "width", 4);
|
|
26535
|
+
setProp(_el$28, "flexShrink", 0);
|
|
26536
|
+
insertNode(_el$29, _el$30);
|
|
26537
|
+
insertNode(_el$29, _el$31);
|
|
26538
|
+
setProp(_el$29, "wrapMode", "none");
|
|
26539
|
+
insert(_el$29, () => action.key, _el$31);
|
|
26540
|
+
insertNode(_el$32, _el$33);
|
|
26541
|
+
setProp(_el$32, "width", 14);
|
|
26542
|
+
setProp(_el$32, "flexShrink", 0);
|
|
26543
|
+
setProp(_el$33, "wrapMode", "none");
|
|
26544
|
+
insert(_el$33, () => action.label);
|
|
26545
|
+
setProp(_el$34, "wrapMode", "word");
|
|
26546
|
+
insert(_el$34, () => action.detail);
|
|
26099
26547
|
effect((_p$) => {
|
|
26100
26548
|
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$
|
|
26549
|
+
_v$12 !== _p$.e && (_p$.e = setProp(_el$27, "backgroundColor", _v$12, _p$.e));
|
|
26550
|
+
_v$13 !== _p$.t && (_p$.t = setProp(_el$29, "fg", _v$13, _p$.t));
|
|
26551
|
+
_v$14 !== _p$.a && (_p$.a = setProp(_el$29, "attributes", _v$14, _p$.a));
|
|
26552
|
+
_v$15 !== _p$.o && (_p$.o = setProp(_el$33, "fg", _v$15, _p$.o));
|
|
26553
|
+
_v$16 !== _p$.i && (_p$.i = setProp(_el$34, "fg", _v$16, _p$.i));
|
|
26106
26554
|
return _p$;
|
|
26107
26555
|
}, {
|
|
26108
26556
|
e: undefined,
|
|
@@ -26111,7 +26559,7 @@ kobe update failed with exit code ${code}.
|
|
|
26111
26559
|
o: undefined,
|
|
26112
26560
|
i: undefined
|
|
26113
26561
|
});
|
|
26114
|
-
return _el$
|
|
26562
|
+
return _el$27;
|
|
26115
26563
|
})()
|
|
26116
26564
|
}));
|
|
26117
26565
|
insert(_el$, createComponent2(Show, {
|
|
@@ -26129,49 +26577,78 @@ kobe update failed with exit code ${code}.
|
|
|
26129
26577
|
insertNode(_el$16, _el$17);
|
|
26130
26578
|
setProp(_el$16, "flexShrink", 0);
|
|
26131
26579
|
setProp(_el$16, "paddingTop", 1);
|
|
26132
|
-
insertNode(_el$17,
|
|
26580
|
+
insertNode(_el$17, _el$18);
|
|
26581
|
+
insertNode(_el$17, _el$19);
|
|
26582
|
+
insertNode(_el$17, _el$20);
|
|
26133
26583
|
setProp(_el$17, "wrapMode", "none");
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
setProp(_el$
|
|
26138
|
-
setProp(_el$
|
|
26139
|
-
setProp(_el$
|
|
26140
|
-
setProp(_el$
|
|
26141
|
-
setProp(_el$
|
|
26142
|
-
|
|
26584
|
+
insert(_el$17, CURRENT_VERSION, _el$19);
|
|
26585
|
+
insert(_el$17, latest, _el$20);
|
|
26586
|
+
insertNode(_el$21, _el$22);
|
|
26587
|
+
setProp(_el$21, "flexGrow", 1);
|
|
26588
|
+
setProp(_el$21, "flexShrink", 1);
|
|
26589
|
+
setProp(_el$21, "stickyScroll", false);
|
|
26590
|
+
setProp(_el$22, "flexDirection", "column");
|
|
26591
|
+
setProp(_el$22, "paddingRight", 1);
|
|
26592
|
+
setProp(_el$22, "paddingBottom", 1);
|
|
26593
|
+
setProp(_el$22, "gap", 0);
|
|
26594
|
+
insert(_el$22, createComponent2(Show, {
|
|
26143
26595
|
get when() {
|
|
26144
26596
|
return loadingNotes();
|
|
26145
26597
|
},
|
|
26146
26598
|
get children() {
|
|
26147
|
-
var _el$
|
|
26148
|
-
insertNode(_el$
|
|
26149
|
-
effect((_$p) => setProp(_el$
|
|
26150
|
-
return _el$
|
|
26599
|
+
var _el$23 = createElement("text");
|
|
26600
|
+
insertNode(_el$23, createTextNode(`Loading release notes...`));
|
|
26601
|
+
effect((_$p) => setProp(_el$23, "fg", theme.textMuted, _$p));
|
|
26602
|
+
return _el$23;
|
|
26151
26603
|
}
|
|
26152
26604
|
}), null);
|
|
26153
|
-
insert(_el$
|
|
26605
|
+
insert(_el$22, createComponent2(Show, {
|
|
26154
26606
|
get when() {
|
|
26155
|
-
return memo2(() => !!!loadingNotes())() &&
|
|
26607
|
+
return memo2(() => !!!loadingNotes())() && releaseNotes().length === 0;
|
|
26156
26608
|
},
|
|
26157
26609
|
get children() {
|
|
26158
|
-
var _el$
|
|
26159
|
-
insertNode(_el$
|
|
26160
|
-
setProp(_el$
|
|
26161
|
-
effect((_$p) => setProp(_el$
|
|
26162
|
-
return _el$
|
|
26610
|
+
var _el$25 = createElement("text");
|
|
26611
|
+
insertNode(_el$25, createTextNode(`Release notes are unavailable. Use Open release to view the GitHub release page.`));
|
|
26612
|
+
setProp(_el$25, "wrapMode", "word");
|
|
26613
|
+
effect((_$p) => setProp(_el$25, "fg", theme.textMuted, _$p));
|
|
26614
|
+
return _el$25;
|
|
26163
26615
|
}
|
|
26164
26616
|
}), null);
|
|
26165
|
-
insert(_el$
|
|
26617
|
+
insert(_el$22, createComponent2(For, {
|
|
26166
26618
|
get each() {
|
|
26167
|
-
return
|
|
26168
|
-
},
|
|
26169
|
-
children: (
|
|
26170
|
-
var _el$
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26619
|
+
return releaseNotes();
|
|
26620
|
+
},
|
|
26621
|
+
children: (release) => (() => {
|
|
26622
|
+
var _el$35 = createElement("box"), _el$36 = createElement("text"), _el$37 = createTextNode(`v`);
|
|
26623
|
+
insertNode(_el$35, _el$36);
|
|
26624
|
+
setProp(_el$35, "flexDirection", "column");
|
|
26625
|
+
setProp(_el$35, "paddingBottom", 1);
|
|
26626
|
+
setProp(_el$35, "gap", 0);
|
|
26627
|
+
insertNode(_el$36, _el$37);
|
|
26628
|
+
setProp(_el$36, "wrapMode", "none");
|
|
26629
|
+
insert(_el$36, () => release.version, null);
|
|
26630
|
+
insert(_el$35, createComponent2(For, {
|
|
26631
|
+
get each() {
|
|
26632
|
+
return releaseBodyLines(release.body);
|
|
26633
|
+
},
|
|
26634
|
+
children: (line) => (() => {
|
|
26635
|
+
var _el$38 = createElement("text");
|
|
26636
|
+
setProp(_el$38, "wrapMode", "word");
|
|
26637
|
+
insert(_el$38, line);
|
|
26638
|
+
effect((_$p) => setProp(_el$38, "fg", theme.textMuted, _$p));
|
|
26639
|
+
return _el$38;
|
|
26640
|
+
})()
|
|
26641
|
+
}), null);
|
|
26642
|
+
effect((_p$) => {
|
|
26643
|
+
var _v$17 = theme.text, _v$18 = TextAttributes13.BOLD;
|
|
26644
|
+
_v$17 !== _p$.e && (_p$.e = setProp(_el$36, "fg", _v$17, _p$.e));
|
|
26645
|
+
_v$18 !== _p$.t && (_p$.t = setProp(_el$36, "attributes", _v$18, _p$.t));
|
|
26646
|
+
return _p$;
|
|
26647
|
+
}, {
|
|
26648
|
+
e: undefined,
|
|
26649
|
+
t: undefined
|
|
26650
|
+
});
|
|
26651
|
+
return _el$35;
|
|
26175
26652
|
})()
|
|
26176
26653
|
}), null);
|
|
26177
26654
|
effect((_p$) => {
|
|
@@ -26193,7 +26670,7 @@ kobe update failed with exit code ${code}.
|
|
|
26193
26670
|
_v$0 !== _p$.d && (_p$.d = setProp(_el$12, "attributes", _v$0, _p$.d));
|
|
26194
26671
|
_v$1 !== _p$.l && (_p$.l = setProp(_el$17, "fg", _v$1, _p$.l));
|
|
26195
26672
|
_v$10 !== _p$.u && (_p$.u = setProp(_el$17, "attributes", _v$10, _p$.u));
|
|
26196
|
-
_v$11 !== _p$.c && (_p$.c = setProp(_el$
|
|
26673
|
+
_v$11 !== _p$.c && (_p$.c = setProp(_el$21, "verticalScrollbarOptions", _v$11, _p$.c));
|
|
26197
26674
|
return _p$;
|
|
26198
26675
|
}, {
|
|
26199
26676
|
e: undefined,
|
|
@@ -26513,7 +26990,7 @@ var gitWrapper;
|
|
|
26513
26990
|
var init_git2 = __esm(() => {
|
|
26514
26991
|
gitWrapper = {
|
|
26515
26992
|
spawn(args2, cwd) {
|
|
26516
|
-
return new Promise((
|
|
26993
|
+
return new Promise((resolve11, reject) => {
|
|
26517
26994
|
const child = nodeSpawn("git", [...args2], {
|
|
26518
26995
|
cwd,
|
|
26519
26996
|
shell: false,
|
|
@@ -26532,7 +27009,7 @@ var init_git2 = __esm(() => {
|
|
|
26532
27009
|
});
|
|
26533
27010
|
child.on("error", reject);
|
|
26534
27011
|
child.on("close", (status, signal) => {
|
|
26535
|
-
|
|
27012
|
+
resolve11({ stdout, stderr, status, signal });
|
|
26536
27013
|
});
|
|
26537
27014
|
});
|
|
26538
27015
|
}
|
|
@@ -27471,7 +27948,7 @@ var init_filetree = __esm(() => {
|
|
|
27471
27948
|
// src/tui/ops/pr-prompt.ts
|
|
27472
27949
|
import { promises as fs6 } from "fs";
|
|
27473
27950
|
import path12 from "path";
|
|
27474
|
-
async function
|
|
27951
|
+
async function git(cwd, args2) {
|
|
27475
27952
|
const controller = new AbortController;
|
|
27476
27953
|
const timer = setTimeout(() => controller.abort(), GIT_TIMEOUT_MS2);
|
|
27477
27954
|
try {
|
|
@@ -27490,20 +27967,20 @@ async function git2(cwd, args2) {
|
|
|
27490
27967
|
}
|
|
27491
27968
|
}
|
|
27492
27969
|
async function currentBranch2(cwd) {
|
|
27493
|
-
return await
|
|
27970
|
+
return await git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]) || "HEAD";
|
|
27494
27971
|
}
|
|
27495
27972
|
async function targetBranch(cwd) {
|
|
27496
|
-
const out = await
|
|
27973
|
+
const out = await git(cwd, ["symbolic-ref", "refs/remotes/origin/HEAD", "--short"]);
|
|
27497
27974
|
if (!out)
|
|
27498
27975
|
return "main";
|
|
27499
27976
|
return out.startsWith("origin/") ? out.slice("origin/".length) : out;
|
|
27500
27977
|
}
|
|
27501
27978
|
async function hasUpstream(cwd) {
|
|
27502
|
-
const out = await
|
|
27979
|
+
const out = await git(cwd, ["rev-parse", "--abbrev-ref", "@{u}"]);
|
|
27503
27980
|
return out !== null && out.length > 0;
|
|
27504
27981
|
}
|
|
27505
27982
|
async function dirtyCount(cwd) {
|
|
27506
|
-
const out = await
|
|
27983
|
+
const out = await git(cwd, ["status", "--porcelain"]);
|
|
27507
27984
|
if (!out)
|
|
27508
27985
|
return 0;
|
|
27509
27986
|
return out.split(`
|
|
@@ -27584,7 +28061,7 @@ __export(exports_host7, {
|
|
|
27584
28061
|
startOpsHost: () => startOpsHost,
|
|
27585
28062
|
nextActivityPollDelay: () => nextActivityPollDelay
|
|
27586
28063
|
});
|
|
27587
|
-
import { createHash as
|
|
28064
|
+
import { createHash as createHash6 } from "crypto";
|
|
27588
28065
|
import { SyntaxStyle } from "@opentui/core";
|
|
27589
28066
|
function nextActivityPollDelay(currentMs, idleStreak) {
|
|
27590
28067
|
if (idleStreak < ACTIVITY_IDLE_RAMP_POLLS)
|
|
@@ -27757,7 +28234,7 @@ function basename8(p) {
|
|
|
27757
28234
|
return i >= 0 ? p.slice(i + 1) : p;
|
|
27758
28235
|
}
|
|
27759
28236
|
function fingerprint(text) {
|
|
27760
|
-
return
|
|
28237
|
+
return createHash6("sha1").update(text).digest("hex");
|
|
27761
28238
|
}
|
|
27762
28239
|
async function startOpsHost(args2) {
|
|
27763
28240
|
await bootPaneHost({
|
|
@@ -28025,7 +28502,7 @@ __export(exports_direct, {
|
|
|
28025
28502
|
startDirectTmux: () => startDirectTmux,
|
|
28026
28503
|
chooseInitialTask: () => chooseInitialTask
|
|
28027
28504
|
});
|
|
28028
|
-
import { resolve as
|
|
28505
|
+
import { resolve as resolve11 } from "path";
|
|
28029
28506
|
function chooseInitialTask(tasks, choice = {}) {
|
|
28030
28507
|
const byId = (id) => id ? tasks.find((t) => t.id === id) : undefined;
|
|
28031
28508
|
const active = byId(choice.activeTaskId);
|
|
@@ -28061,7 +28538,7 @@ async function ensureRepos(orchestrator) {
|
|
|
28061
28538
|
normalizeSavedRepos();
|
|
28062
28539
|
let repos = [...getSavedRepos()];
|
|
28063
28540
|
if (repos.length === 0) {
|
|
28064
|
-
const added = addSavedRepo(
|
|
28541
|
+
const added = addSavedRepo(resolve11(process.cwd()));
|
|
28065
28542
|
repos = [added.path];
|
|
28066
28543
|
}
|
|
28067
28544
|
for (const repo of repos) {
|
|
@@ -28071,7 +28548,7 @@ async function ensureRepos(orchestrator) {
|
|
|
28071
28548
|
console.error(`[kobe] ensureMainTask failed for ${repo}:`, err);
|
|
28072
28549
|
}
|
|
28073
28550
|
}
|
|
28074
|
-
return repos[0] ??
|
|
28551
|
+
return repos[0] ?? resolve11(process.cwd());
|
|
28075
28552
|
}
|
|
28076
28553
|
async function startDirectTmux() {
|
|
28077
28554
|
setClientLogContext("gui");
|
|
@@ -28182,7 +28659,7 @@ var init_tui = __esm(() => {
|
|
|
28182
28659
|
// src/cli/index.ts
|
|
28183
28660
|
init_path_glob();
|
|
28184
28661
|
init_vendor();
|
|
28185
|
-
import { resolve as
|
|
28662
|
+
import { resolve as resolve12 } from "path";
|
|
28186
28663
|
|
|
28187
28664
|
// src/cli/usage.ts
|
|
28188
28665
|
init_version();
|
|
@@ -28241,7 +28718,7 @@ async function runAddSubcommand(rest) {
|
|
|
28241
28718
|
${ADD_USAGE}`);
|
|
28242
28719
|
process.exit(2);
|
|
28243
28720
|
}
|
|
28244
|
-
const target =
|
|
28721
|
+
const target = resolve12(process.cwd(), arg && arg.length > 0 ? arg : ".");
|
|
28245
28722
|
const { addSavedRepo: addSavedRepo2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
28246
28723
|
const result = addSavedRepo2(target);
|
|
28247
28724
|
if (result.added) {
|
|
@@ -28340,7 +28817,7 @@ async function runAdoptSubcommand(args2) {
|
|
|
28340
28817
|
}
|
|
28341
28818
|
}
|
|
28342
28819
|
const { resolveRepoRoot: resolveRepoRoot2 } = await Promise.resolve().then(() => (init_repos(), exports_repos));
|
|
28343
|
-
const repo = resolveRepoRoot2(
|
|
28820
|
+
const repo = resolveRepoRoot2(resolve12(process.cwd(), repoArg && repoArg.length > 0 ? repoArg : "."));
|
|
28344
28821
|
const vendor = coerceVendorId(vendorArg);
|
|
28345
28822
|
const orch = await openLocalOrchestrator();
|
|
28346
28823
|
const worktrees = await orch.discoverAdoptableWorktrees(repo);
|