@staff0rd/assist 0.431.0 → 0.433.0
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/README.md +3 -1
- package/dist/commands/sessions/web/bundle.js +407 -407
- package/dist/index.js +924 -275
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.433.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -432,6 +432,16 @@ var assistConfigSchema = z2.strictObject({
|
|
|
432
432
|
promptJira: z2.boolean().default(false)
|
|
433
433
|
}).optional(),
|
|
434
434
|
repos: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional(),
|
|
435
|
+
worktree: z2.strictObject({
|
|
436
|
+
enabled: z2.boolean().default(false),
|
|
437
|
+
root: z2.string().optional(),
|
|
438
|
+
install: z2.union([z2.boolean(), z2.string()]).default(true),
|
|
439
|
+
copy: z2.array(z2.string()).default([
|
|
440
|
+
".env",
|
|
441
|
+
"settings.local.json",
|
|
442
|
+
".claude/settings.local.json"
|
|
443
|
+
])
|
|
444
|
+
}).optional(),
|
|
435
445
|
run: z2.array(z2.union([runConfigSchema, runLinkSchema])).optional(),
|
|
436
446
|
subtasks: z2.array(
|
|
437
447
|
z2.strictObject({
|
|
@@ -1542,6 +1552,7 @@ function warnIfUnexpectedBranch(config) {
|
|
|
1542
1552
|
if (!expected) return;
|
|
1543
1553
|
const current = currentBranch();
|
|
1544
1554
|
if (current === null || current === expected) return;
|
|
1555
|
+
if (upstreamBranch() === expected) return;
|
|
1545
1556
|
const line = "\u2500".repeat(64);
|
|
1546
1557
|
console.warn(
|
|
1547
1558
|
chalk3.yellow.bold(
|
|
@@ -1565,6 +1576,18 @@ function currentBranch() {
|
|
|
1565
1576
|
return null;
|
|
1566
1577
|
}
|
|
1567
1578
|
}
|
|
1579
|
+
function upstreamBranch() {
|
|
1580
|
+
try {
|
|
1581
|
+
const upstream = execSync(
|
|
1582
|
+
"git rev-parse --abbrev-ref --symbolic-full-name @{upstream}",
|
|
1583
|
+
{ encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1584
|
+
).trim();
|
|
1585
|
+
const slash = upstream.indexOf("/");
|
|
1586
|
+
return slash === -1 ? upstream || null : upstream.slice(slash + 1) || null;
|
|
1587
|
+
} catch {
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1568
1591
|
|
|
1569
1592
|
// src/commands/commit/findConflictMarkers.ts
|
|
1570
1593
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -1726,7 +1749,7 @@ function gitRefUrl(kind, ref, cwd) {
|
|
|
1726
1749
|
}
|
|
1727
1750
|
|
|
1728
1751
|
// src/commands/commit/collectCommitRefs.ts
|
|
1729
|
-
function collectCommitRefs(
|
|
1752
|
+
function collectCommitRefs(message2) {
|
|
1730
1753
|
const refs = [];
|
|
1731
1754
|
const branch2 = git("rev-parse --abbrev-ref HEAD");
|
|
1732
1755
|
if (branch2 && branch2 !== "HEAD") {
|
|
@@ -1743,7 +1766,7 @@ function collectCommitRefs(message) {
|
|
|
1743
1766
|
ref: sha,
|
|
1744
1767
|
url: gitRefUrl("commit", sha)
|
|
1745
1768
|
};
|
|
1746
|
-
const subject =
|
|
1769
|
+
const subject = message2.split("\n", 1)[0];
|
|
1747
1770
|
if (subject) ref.title = subject;
|
|
1748
1771
|
refs.push(ref);
|
|
1749
1772
|
}
|
|
@@ -1762,16 +1785,16 @@ function git(args) {
|
|
|
1762
1785
|
|
|
1763
1786
|
// src/commands/commit/stageAndCommit.ts
|
|
1764
1787
|
import { execSync as execSync5 } from "child_process";
|
|
1765
|
-
function commitStaged(
|
|
1766
|
-
execSync5(`git commit -m ${shellQuote(
|
|
1788
|
+
function commitStaged(message2) {
|
|
1789
|
+
execSync5(`git commit -m ${shellQuote(message2)}`, { stdio: "inherit" });
|
|
1767
1790
|
return execSync5("git rev-parse --short=7 HEAD", {
|
|
1768
1791
|
encoding: "utf8"
|
|
1769
1792
|
}).trim();
|
|
1770
1793
|
}
|
|
1771
|
-
function stageAndCommit(files,
|
|
1794
|
+
function stageAndCommit(files, message2) {
|
|
1772
1795
|
const escaped = files.map(shellQuote).join(" ");
|
|
1773
1796
|
execSync5(`git add ${escaped}`, { stdio: "inherit" });
|
|
1774
|
-
return commitStaged(
|
|
1797
|
+
return commitStaged(message2);
|
|
1775
1798
|
}
|
|
1776
1799
|
|
|
1777
1800
|
// src/commands/sessions/summarise/iterateUserMessages.ts
|
|
@@ -1866,34 +1889,34 @@ function backlogRefError(subject, context, ids) {
|
|
|
1866
1889
|
// src/commands/commit/validateMessage.ts
|
|
1867
1890
|
var MAX_MESSAGE_LENGTH = 50;
|
|
1868
1891
|
var CONVENTIONAL_COMMIT_REGEX = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(!)?(\(.+\))?!?: .+$/;
|
|
1869
|
-
function validateMessage(
|
|
1870
|
-
if (
|
|
1892
|
+
function validateMessage(message2, config) {
|
|
1893
|
+
if (message2.toLowerCase().includes("claude")) {
|
|
1871
1894
|
console.error("Error: Commit message must not reference Claude");
|
|
1872
1895
|
process.exit(1);
|
|
1873
1896
|
}
|
|
1874
|
-
const backlogIds = findBacklogRefs(
|
|
1897
|
+
const backlogIds = findBacklogRefs(message2);
|
|
1875
1898
|
if (backlogIds.length > 0) {
|
|
1876
1899
|
console.error(
|
|
1877
1900
|
backlogRefError("Commit message", "commit messages", backlogIds)
|
|
1878
1901
|
);
|
|
1879
1902
|
process.exit(1);
|
|
1880
1903
|
}
|
|
1881
|
-
if (config.commit?.conventional && !CONVENTIONAL_COMMIT_REGEX.test(
|
|
1904
|
+
if (config.commit?.conventional && !CONVENTIONAL_COMMIT_REGEX.test(message2)) {
|
|
1882
1905
|
console.error(
|
|
1883
1906
|
"Error: Commit message must follow conventional commit format (e.g., 'feat: add feature', 'fix(scope): fix bug')"
|
|
1884
1907
|
);
|
|
1885
1908
|
process.exit(1);
|
|
1886
1909
|
}
|
|
1887
|
-
if (
|
|
1910
|
+
if (message2.length > MAX_MESSAGE_LENGTH) {
|
|
1888
1911
|
console.error(
|
|
1889
|
-
`Error: Commit message must be ${MAX_MESSAGE_LENGTH} characters or less (current: ${
|
|
1912
|
+
`Error: Commit message must be ${MAX_MESSAGE_LENGTH} characters or less (current: ${message2.length})`
|
|
1890
1913
|
);
|
|
1891
1914
|
process.exit(1);
|
|
1892
1915
|
}
|
|
1893
1916
|
}
|
|
1894
1917
|
|
|
1895
1918
|
// src/commands/commit.ts
|
|
1896
|
-
async function execCommit(files,
|
|
1919
|
+
async function execCommit(files, message2, config) {
|
|
1897
1920
|
try {
|
|
1898
1921
|
warnIfUnexpectedBranch(config);
|
|
1899
1922
|
const pulled = Boolean(config.commit?.pull);
|
|
@@ -1901,21 +1924,21 @@ async function execCommit(files, message, config) {
|
|
|
1901
1924
|
execSync6("git pull --autostash", { stdio: "inherit" });
|
|
1902
1925
|
}
|
|
1903
1926
|
abortOnConflicts(files, pulled);
|
|
1904
|
-
const sha = files.length > 0 ? stageAndCommit(files,
|
|
1927
|
+
const sha = files.length > 0 ? stageAndCommit(files, message2) : commitStaged(message2);
|
|
1905
1928
|
console.log(`Committed: ${sha}`);
|
|
1906
1929
|
if (config.commit?.push) {
|
|
1907
1930
|
execSync6("git push", { stdio: "inherit" });
|
|
1908
1931
|
console.log("Pushed to remote");
|
|
1909
1932
|
}
|
|
1910
|
-
await recordCommitActivity(
|
|
1933
|
+
await recordCommitActivity(message2);
|
|
1911
1934
|
process.exit(0);
|
|
1912
1935
|
} catch {
|
|
1913
1936
|
process.exit(1);
|
|
1914
1937
|
}
|
|
1915
1938
|
}
|
|
1916
|
-
async function recordCommitActivity(
|
|
1939
|
+
async function recordCommitActivity(message2) {
|
|
1917
1940
|
if (resolveSessionItemId() === null) return;
|
|
1918
|
-
await recordSessionRefs(collectCommitRefs(
|
|
1941
|
+
await recordSessionRefs(collectCommitRefs(message2));
|
|
1919
1942
|
}
|
|
1920
1943
|
async function commit(args) {
|
|
1921
1944
|
if (args[0] === "status") {
|
|
@@ -1928,11 +1951,11 @@ async function commit(args) {
|
|
|
1928
1951
|
console.error("Usage: assist commit <message> [files...]");
|
|
1929
1952
|
process.exit(1);
|
|
1930
1953
|
}
|
|
1931
|
-
const
|
|
1954
|
+
const message2 = args[0];
|
|
1932
1955
|
const files = args.slice(1);
|
|
1933
1956
|
const config = loadConfig();
|
|
1934
|
-
validateMessage(
|
|
1935
|
-
await execCommit(files,
|
|
1957
|
+
validateMessage(message2, config);
|
|
1958
|
+
await execCommit(files, message2, config);
|
|
1936
1959
|
}
|
|
1937
1960
|
|
|
1938
1961
|
// src/commands/coverage.ts
|
|
@@ -1970,12 +1993,12 @@ async function exitOnCancel(promise) {
|
|
|
1970
1993
|
}
|
|
1971
1994
|
|
|
1972
1995
|
// src/shared/promptMultiselect.ts
|
|
1973
|
-
async function promptMultiselect(
|
|
1996
|
+
async function promptMultiselect(message2, options2) {
|
|
1974
1997
|
const { selected } = await exitOnCancel(
|
|
1975
1998
|
enquirer.prompt({
|
|
1976
1999
|
type: "multiselect",
|
|
1977
2000
|
name: "selected",
|
|
1978
|
-
message,
|
|
2001
|
+
message: message2,
|
|
1979
2002
|
choices: options2.map((opt) => ({
|
|
1980
2003
|
name: opt.value,
|
|
1981
2004
|
message: `${opt.name} - ${chalk4.dim(opt.description)}`
|
|
@@ -2217,12 +2240,12 @@ import chalk13 from "chalk";
|
|
|
2217
2240
|
|
|
2218
2241
|
// src/shared/promptConfirm.ts
|
|
2219
2242
|
import enquirer2 from "enquirer";
|
|
2220
|
-
async function promptConfirm(
|
|
2243
|
+
async function promptConfirm(message2, initial = true) {
|
|
2221
2244
|
const { confirmed } = await exitOnCancel(
|
|
2222
2245
|
enquirer2.prompt({
|
|
2223
2246
|
type: "confirm",
|
|
2224
2247
|
name: "confirmed",
|
|
2225
|
-
message,
|
|
2248
|
+
message: message2,
|
|
2226
2249
|
initial,
|
|
2227
2250
|
// @ts-expect-error - enquirer types don't include symbols but it's supported
|
|
2228
2251
|
symbols: {
|
|
@@ -4532,13 +4555,13 @@ function getSnoreToastPath() {
|
|
|
4532
4555
|
return path21.join(notifierPath, "vendor", "snoreToast", "snoretoast-x64.exe");
|
|
4533
4556
|
}
|
|
4534
4557
|
function showWindowsNotificationFromWsl(options2) {
|
|
4535
|
-
const { title, message, sound } = options2;
|
|
4558
|
+
const { title, message: message2, sound } = options2;
|
|
4536
4559
|
const snoreToastPath = getSnoreToastPath();
|
|
4537
4560
|
try {
|
|
4538
4561
|
fs16.chmodSync(snoreToastPath, 493);
|
|
4539
4562
|
} catch {
|
|
4540
4563
|
}
|
|
4541
|
-
const args = ["-t", title, "-m",
|
|
4564
|
+
const args = ["-t", title, "-m", message2];
|
|
4542
4565
|
if (sound) {
|
|
4543
4566
|
args.push("-s", "ms-winsoundevent:Notification.Default");
|
|
4544
4567
|
}
|
|
@@ -4552,14 +4575,14 @@ function showWindowsNotificationFromWsl(options2) {
|
|
|
4552
4575
|
|
|
4553
4576
|
// src/commands/notify/showNotification/index.ts
|
|
4554
4577
|
function showNotification(options2) {
|
|
4555
|
-
const { title, message, sound } = options2;
|
|
4578
|
+
const { title, message: message2, sound } = options2;
|
|
4556
4579
|
const platform = detectPlatform();
|
|
4557
4580
|
if (platform === "wsl") {
|
|
4558
|
-
return showWindowsNotificationFromWsl({ title, message, sound });
|
|
4581
|
+
return showWindowsNotificationFromWsl({ title, message: message2, sound });
|
|
4559
4582
|
}
|
|
4560
4583
|
const notificationOptions = {
|
|
4561
4584
|
title,
|
|
4562
|
-
message,
|
|
4585
|
+
message: message2,
|
|
4563
4586
|
wait: false
|
|
4564
4587
|
};
|
|
4565
4588
|
if (platform === "windows") {
|
|
@@ -4581,7 +4604,7 @@ async function notify() {
|
|
|
4581
4604
|
}
|
|
4582
4605
|
const inputData = await readStdin();
|
|
4583
4606
|
const data = JSON.parse(inputData);
|
|
4584
|
-
const { notification_type, cwd, message } = data;
|
|
4607
|
+
const { notification_type, cwd, message: message2 } = data;
|
|
4585
4608
|
const projectName = cwd?.split(/[/\\]/).pop() ?? "Unknown Project";
|
|
4586
4609
|
let title;
|
|
4587
4610
|
let body;
|
|
@@ -4589,7 +4612,7 @@ async function notify() {
|
|
|
4589
4612
|
switch (notification_type) {
|
|
4590
4613
|
case "permission_prompt":
|
|
4591
4614
|
title = "Claude needs permission";
|
|
4592
|
-
body = `${projectName} - ${
|
|
4615
|
+
body = `${projectName} - ${message2 || "Permission required"}`;
|
|
4593
4616
|
sound = "Alarm";
|
|
4594
4617
|
break;
|
|
4595
4618
|
case "idle_prompt":
|
|
@@ -4599,7 +4622,7 @@ async function notify() {
|
|
|
4599
4622
|
break;
|
|
4600
4623
|
default:
|
|
4601
4624
|
title = "Claude Code";
|
|
4602
|
-
body =
|
|
4625
|
+
body = message2 ? `${projectName} - ${message2}` : projectName;
|
|
4603
4626
|
sound = "Default";
|
|
4604
4627
|
}
|
|
4605
4628
|
showNotification({ title, message: body, sound });
|
|
@@ -4853,8 +4876,8 @@ function readScheduleBlock(crontab) {
|
|
|
4853
4876
|
}
|
|
4854
4877
|
|
|
4855
4878
|
// src/commands/backup/scheduleBackup.ts
|
|
4856
|
-
function fail(
|
|
4857
|
-
console.error(chalk29.red(
|
|
4879
|
+
function fail(message2) {
|
|
4880
|
+
console.error(chalk29.red(message2));
|
|
4858
4881
|
process.exit(1);
|
|
4859
4882
|
}
|
|
4860
4883
|
async function scheduleBackup({
|
|
@@ -5371,8 +5394,8 @@ function buildArgs(prompt, options2) {
|
|
|
5371
5394
|
|
|
5372
5395
|
// src/commands/sessions/daemon/appendDaemonLog.ts
|
|
5373
5396
|
import { appendFileSync } from "fs";
|
|
5374
|
-
function appendDaemonLog(
|
|
5375
|
-
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${
|
|
5397
|
+
function appendDaemonLog(message2) {
|
|
5398
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${message2}`;
|
|
5376
5399
|
try {
|
|
5377
5400
|
appendFileSync(daemonPaths.log, `${line}
|
|
5378
5401
|
`);
|
|
@@ -6330,10 +6353,10 @@ async function reconcileResumePhase(orm, item, startPhase, resumeSessionId) {
|
|
|
6330
6353
|
if (resumedPhaseIdx === void 0 || resumedPhaseIdx === startPhase) {
|
|
6331
6354
|
return startPhase;
|
|
6332
6355
|
}
|
|
6333
|
-
const
|
|
6334
|
-
appendDaemonLog(`backlog run ${item.id}: ${
|
|
6356
|
+
const message2 = `resume reconciliation: currentPhase implied phase ${startPhase + 1} but the resumed conversation ${resumeSessionId} ran phase ${resumedPhaseIdx + 1}; resuming phase ${resumedPhaseIdx + 1} to match the conversation`;
|
|
6357
|
+
appendDaemonLog(`backlog run ${item.id}: ${message2}`);
|
|
6335
6358
|
try {
|
|
6336
|
-
await appendComment(orm, item.id, `Resume reconciled \u2014 ${
|
|
6359
|
+
await appendComment(orm, item.id, `Resume reconciled \u2014 ${message2}`, {
|
|
6337
6360
|
phase: resumedPhaseIdx + 1
|
|
6338
6361
|
});
|
|
6339
6362
|
} catch {
|
|
@@ -6396,10 +6419,10 @@ async function awaitClaude(done2, context) {
|
|
|
6396
6419
|
try {
|
|
6397
6420
|
return await done2;
|
|
6398
6421
|
} catch (error) {
|
|
6399
|
-
const
|
|
6422
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
6400
6423
|
console.error(
|
|
6401
6424
|
chalk39.red(`
|
|
6402
|
-
Failed to launch Claude for ${context}: ${
|
|
6425
|
+
Failed to launch Claude for ${context}: ${message2}`)
|
|
6403
6426
|
);
|
|
6404
6427
|
return CLAUDE_SPAWN_FAILED;
|
|
6405
6428
|
}
|
|
@@ -6425,7 +6448,7 @@ async function isDaemonRunning() {
|
|
|
6425
6448
|
|
|
6426
6449
|
// src/commands/sessions/daemon/sendToDaemon.ts
|
|
6427
6450
|
var WRITE_TIMEOUT_MS = 500;
|
|
6428
|
-
function sendToDaemon(
|
|
6451
|
+
function sendToDaemon(message2) {
|
|
6429
6452
|
return new Promise((resolve19, reject) => {
|
|
6430
6453
|
connectToDaemon().then((socket) => {
|
|
6431
6454
|
const timer = setTimeout(() => {
|
|
@@ -6436,7 +6459,7 @@ function sendToDaemon(message) {
|
|
|
6436
6459
|
clearTimeout(timer);
|
|
6437
6460
|
reject(error);
|
|
6438
6461
|
});
|
|
6439
|
-
socket.write(`${JSON.stringify(
|
|
6462
|
+
socket.write(`${JSON.stringify(message2)}
|
|
6440
6463
|
`, () => {
|
|
6441
6464
|
clearTimeout(timer);
|
|
6442
6465
|
socket.end();
|
|
@@ -6462,7 +6485,7 @@ function readSocketLines(socket, onLine) {
|
|
|
6462
6485
|
|
|
6463
6486
|
// src/commands/sessions/daemon/sendToDaemonAwaitAck.ts
|
|
6464
6487
|
var ACK_TIMEOUT_MS = 1e3;
|
|
6465
|
-
function sendToDaemonAwaitAck(
|
|
6488
|
+
function sendToDaemonAwaitAck(message2) {
|
|
6466
6489
|
return new Promise((resolve19, reject) => {
|
|
6467
6490
|
connectToDaemon().then((socket) => {
|
|
6468
6491
|
let settled = false;
|
|
@@ -6486,7 +6509,7 @@ function sendToDaemonAwaitAck(message) {
|
|
|
6486
6509
|
"close",
|
|
6487
6510
|
() => finish(new Error("daemon closed before acknowledging"))
|
|
6488
6511
|
);
|
|
6489
|
-
socket.write(`${JSON.stringify(
|
|
6512
|
+
socket.write(`${JSON.stringify(message2)}
|
|
6490
6513
|
`);
|
|
6491
6514
|
}, reject);
|
|
6492
6515
|
});
|
|
@@ -7223,13 +7246,13 @@ async function findSessionJsonlPath(sessionId) {
|
|
|
7223
7246
|
// src/commands/backlog/verifyResumeConversation.ts
|
|
7224
7247
|
async function verifyResumeConversation(itemId2, resumeSessionId, phaseLabel2) {
|
|
7225
7248
|
if (await findSessionJsonlPath(resumeSessionId)) return true;
|
|
7226
|
-
const
|
|
7249
|
+
const message2 = `${phaseLabel2}: resume found no conversation for session ${resumeSessionId}; phase not advanced`;
|
|
7227
7250
|
console.error(chalk42.red(`
|
|
7228
|
-
${
|
|
7229
|
-
appendDaemonLog(`backlog run ${itemId2}: ${
|
|
7251
|
+
${message2}`));
|
|
7252
|
+
appendDaemonLog(`backlog run ${itemId2}: ${message2}`);
|
|
7230
7253
|
try {
|
|
7231
7254
|
const { orm } = await getReady();
|
|
7232
|
-
await appendComment(orm, itemId2, `Resume failed \u2014 ${
|
|
7255
|
+
await appendComment(orm, itemId2, `Resume failed \u2014 ${message2}`);
|
|
7233
7256
|
} catch {
|
|
7234
7257
|
}
|
|
7235
7258
|
return false;
|
|
@@ -9569,8 +9592,8 @@ async function runRestartItem(item, { runRestartDaemon, reExec }) {
|
|
|
9569
9592
|
reExec();
|
|
9570
9593
|
}
|
|
9571
9594
|
} catch (error) {
|
|
9572
|
-
const
|
|
9573
|
-
console.error(chalk57.red(`Restart failed: ${
|
|
9595
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
9596
|
+
console.error(chalk57.red(`Restart failed: ${message2}`));
|
|
9574
9597
|
}
|
|
9575
9598
|
}
|
|
9576
9599
|
|
|
@@ -10120,8 +10143,8 @@ function originToSshUrl(origin) {
|
|
|
10120
10143
|
}
|
|
10121
10144
|
|
|
10122
10145
|
// src/commands/backlog/cloneRepo.ts
|
|
10123
|
-
function fail2(
|
|
10124
|
-
console.log(chalk66.red(
|
|
10146
|
+
function fail2(message2) {
|
|
10147
|
+
console.log(chalk66.red(message2));
|
|
10125
10148
|
process.exitCode = 1;
|
|
10126
10149
|
}
|
|
10127
10150
|
async function cloneRepo(originRaw) {
|
|
@@ -10777,8 +10800,8 @@ function validateLinkTarget(fromItem, fromNum, toNum, linkType) {
|
|
|
10777
10800
|
}
|
|
10778
10801
|
|
|
10779
10802
|
// src/commands/backlog/link.ts
|
|
10780
|
-
function fail3(
|
|
10781
|
-
console.log(chalk78.red(
|
|
10803
|
+
function fail3(message2) {
|
|
10804
|
+
console.log(chalk78.red(message2));
|
|
10782
10805
|
return void 0;
|
|
10783
10806
|
}
|
|
10784
10807
|
function parseLinkType(type) {
|
|
@@ -10900,8 +10923,8 @@ Pass the full origin.`
|
|
|
10900
10923
|
}
|
|
10901
10924
|
|
|
10902
10925
|
// src/commands/backlog/move-repo/index.ts
|
|
10903
|
-
function fail4(
|
|
10904
|
-
console.log(chalk81.red(
|
|
10926
|
+
function fail4(message2) {
|
|
10927
|
+
console.log(chalk81.red(message2));
|
|
10905
10928
|
process.exitCode = 1;
|
|
10906
10929
|
}
|
|
10907
10930
|
async function moveRepo(oldOriginRaw, newOriginRaw, options2 = {}) {
|
|
@@ -11056,11 +11079,11 @@ async function handleLaunchSignal(slashCommand, once) {
|
|
|
11056
11079
|
await next({ allowEdits: true, once });
|
|
11057
11080
|
}
|
|
11058
11081
|
} catch (error) {
|
|
11059
|
-
const
|
|
11082
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
11060
11083
|
console.error(
|
|
11061
11084
|
chalk83.yellow(
|
|
11062
11085
|
`
|
|
11063
|
-
Could not complete post-run step (${
|
|
11086
|
+
Could not complete post-run step (${message2}).
|
|
11064
11087
|
This is usually a transient database/network blip \u2014 the work is saved and the session is safe to resume.`
|
|
11065
11088
|
)
|
|
11066
11089
|
);
|
|
@@ -13168,15 +13191,15 @@ function loadDenyConfig(global) {
|
|
|
13168
13191
|
}
|
|
13169
13192
|
|
|
13170
13193
|
// src/commands/deny/denyAdd.ts
|
|
13171
|
-
function denyAdd(pattern2,
|
|
13194
|
+
function denyAdd(pattern2, message2, options2) {
|
|
13172
13195
|
const { deny, saveDeny } = loadDenyConfig(options2.global);
|
|
13173
13196
|
if (deny.some((r) => r.pattern === pattern2)) {
|
|
13174
13197
|
console.log(chalk110.yellow(`Deny rule already exists for: ${pattern2}`));
|
|
13175
13198
|
return;
|
|
13176
13199
|
}
|
|
13177
|
-
deny.push({ pattern: pattern2, message });
|
|
13200
|
+
deny.push({ pattern: pattern2, message: message2 });
|
|
13178
13201
|
saveDeny(deny);
|
|
13179
|
-
console.log(chalk110.green(`Added deny rule: ${pattern2} \u2192 ${
|
|
13202
|
+
console.log(chalk110.green(`Added deny rule: ${pattern2} \u2192 ${message2}`));
|
|
13180
13203
|
}
|
|
13181
13204
|
|
|
13182
13205
|
// src/commands/deny/denyList.ts
|
|
@@ -14350,8 +14373,10 @@ function resolveRepoTarget(key, value, repo) {
|
|
|
14350
14373
|
|
|
14351
14374
|
// src/commands/config/configSet.ts
|
|
14352
14375
|
function configSet(key, value, options2 = {}) {
|
|
14353
|
-
if (options2.repo !== void 0 && options2.global) {
|
|
14354
|
-
console.error(
|
|
14376
|
+
if (options2.repo !== void 0 && !options2.global) {
|
|
14377
|
+
console.error(
|
|
14378
|
+
chalk127.red("--repo writes to the global config; add -g (e.g. -g --repo)")
|
|
14379
|
+
);
|
|
14355
14380
|
process.exit(1);
|
|
14356
14381
|
}
|
|
14357
14382
|
const resolved = resolveRepoTarget(key, value, options2.repo);
|
|
@@ -14376,7 +14401,7 @@ function registerConfig(program2) {
|
|
|
14376
14401
|
const configCommand = program2.command("config").description("View and modify assist.yml configuration");
|
|
14377
14402
|
configCommand.command("set <key> [value]").description("Set a config value (e.g. commit.push true)").option("-g, --global", "Write to global ~/.assist.yml").option(
|
|
14378
14403
|
"-r, --repo [name]",
|
|
14379
|
-
"
|
|
14404
|
+
"Requires -g: scope the global write to a repo's identity (defaults to the current repo)"
|
|
14380
14405
|
).action((key, value, options2) => configSet(key, value, options2));
|
|
14381
14406
|
configCommand.command("get <key>").description("Get a config value").action(configGet);
|
|
14382
14407
|
configCommand.command("list").description("List all config values").action(configList);
|
|
@@ -14733,14 +14758,14 @@ function parseGitLogCommits(output, ignore3, afterDate) {
|
|
|
14733
14758
|
const commitsByDate = /* @__PURE__ */ new Map();
|
|
14734
14759
|
for (const line of lines) {
|
|
14735
14760
|
const [date, hash, ...messageParts] = line.split("|");
|
|
14736
|
-
const
|
|
14761
|
+
const message2 = messageParts.join("|");
|
|
14737
14762
|
if (afterDate && date <= afterDate) {
|
|
14738
14763
|
continue;
|
|
14739
14764
|
}
|
|
14740
14765
|
const files = getCommitFiles(hash);
|
|
14741
14766
|
if (!shouldIgnoreCommit(files, ignore3)) {
|
|
14742
14767
|
const existing = commitsByDate.get(date) || [];
|
|
14743
|
-
existing.push({ date, hash, message, files });
|
|
14768
|
+
existing.push({ date, hash, message: message2, files });
|
|
14744
14769
|
commitsByDate.set(date, existing);
|
|
14745
14770
|
}
|
|
14746
14771
|
}
|
|
@@ -16353,10 +16378,10 @@ function advisory(count7) {
|
|
|
16353
16378
|
const noun = count7 === 1 ? "handover" : "handovers";
|
|
16354
16379
|
return `${count7} unrecalled ${noun} for this repo. Run /recall to load.`;
|
|
16355
16380
|
}
|
|
16356
|
-
function emit2(
|
|
16381
|
+
function emit2(message2) {
|
|
16357
16382
|
const json = JSON.stringify({
|
|
16358
16383
|
hookSpecificOutput: { hookEventName: "SessionStart" },
|
|
16359
|
-
systemMessage:
|
|
16384
|
+
systemMessage: message2
|
|
16360
16385
|
});
|
|
16361
16386
|
console.log(json);
|
|
16362
16387
|
return json;
|
|
@@ -16540,11 +16565,11 @@ import { execSync as execSync39 } from "child_process";
|
|
|
16540
16565
|
// src/shared/promptInput.ts
|
|
16541
16566
|
import Enquirer from "enquirer";
|
|
16542
16567
|
var prompts = Enquirer;
|
|
16543
|
-
async function promptInput(name,
|
|
16544
|
-
return exitOnCancel(new prompts.Input({ name, message, initial }).run());
|
|
16568
|
+
async function promptInput(name, message2, initial) {
|
|
16569
|
+
return exitOnCancel(new prompts.Input({ name, message: message2, initial }).run());
|
|
16545
16570
|
}
|
|
16546
|
-
async function promptPassword(name,
|
|
16547
|
-
return exitOnCancel(new prompts.Password({ name, message }).run());
|
|
16571
|
+
async function promptPassword(name, message2) {
|
|
16572
|
+
return exitOnCancel(new prompts.Password({ name, message: message2 }).run());
|
|
16548
16573
|
}
|
|
16549
16574
|
|
|
16550
16575
|
// src/commands/jira/jiraAuth.ts
|
|
@@ -18021,9 +18046,9 @@ function deleteCommentsCache(prNumber) {
|
|
|
18021
18046
|
|
|
18022
18047
|
// src/commands/prs/replyToComment.ts
|
|
18023
18048
|
import { execSync as execSync41 } from "child_process";
|
|
18024
|
-
function replyToComment(org, repo, prNumber, commentId,
|
|
18049
|
+
function replyToComment(org, repo, prNumber, commentId, message2) {
|
|
18025
18050
|
execSync41(
|
|
18026
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${
|
|
18051
|
+
`gh api repos/${org}/${repo}/pulls/${prNumber}/comments -f body="${message2.replace(/"/g, String.raw`\"`)}" -F in_reply_to=${commentId}`,
|
|
18027
18052
|
{ stdio: ["inherit", "pipe", "inherit"] }
|
|
18028
18053
|
);
|
|
18029
18054
|
}
|
|
@@ -18071,12 +18096,12 @@ function cleanupCacheIfDone(cache2, prNumber, commentId) {
|
|
|
18071
18096
|
);
|
|
18072
18097
|
if (!hasRemaining) deleteCommentsCache(prNumber);
|
|
18073
18098
|
}
|
|
18074
|
-
function resolveCommentWithReply(commentId,
|
|
18099
|
+
function resolveCommentWithReply(commentId, message2) {
|
|
18075
18100
|
const prNumber = getCurrentPrNumber();
|
|
18076
18101
|
const { org, repo } = getRepoInfo();
|
|
18077
18102
|
const cache2 = requireCache(prNumber);
|
|
18078
18103
|
const comment3 = requireLineComment(cache2, commentId);
|
|
18079
|
-
replyToComment(org, repo, prNumber, commentId,
|
|
18104
|
+
replyToComment(org, repo, prNumber, commentId, message2);
|
|
18080
18105
|
console.log("Reply posted successfully.");
|
|
18081
18106
|
resolveThread(comment3.threadId);
|
|
18082
18107
|
console.log("Thread resolved successfully.");
|
|
@@ -18099,9 +18124,9 @@ function fixed(commentId, sha) {
|
|
|
18099
18124
|
const fullSha = verifySha(sha);
|
|
18100
18125
|
const { org, repo } = getRepoInfo();
|
|
18101
18126
|
const repoUrl = `https://github.com/${org}/${repo}`;
|
|
18102
|
-
const
|
|
18127
|
+
const message2 = `Fixed in [${fullSha}](${repoUrl}/commit/${fullSha})`;
|
|
18103
18128
|
execSync43("git push", { stdio: "inherit" });
|
|
18104
|
-
resolveCommentWithReply(commentId,
|
|
18129
|
+
resolveCommentWithReply(commentId, message2);
|
|
18105
18130
|
} catch (error) {
|
|
18106
18131
|
if (isGhNotInstalled(error)) {
|
|
18107
18132
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
@@ -19018,9 +19043,9 @@ function getItemFields(itemId2) {
|
|
|
19018
19043
|
|
|
19019
19044
|
// src/commands/ravendb/selectOpSecret.ts
|
|
19020
19045
|
var { Input, Select } = Enquirer2;
|
|
19021
|
-
async function selectOne(
|
|
19046
|
+
async function selectOne(message2, choices) {
|
|
19022
19047
|
if (choices.length === 1) return choices[0].value;
|
|
19023
|
-
const selected = await new Select({ name: "choice", message, choices }).run();
|
|
19048
|
+
const selected = await new Select({ name: "choice", message: message2, choices }).run();
|
|
19024
19049
|
return choices.find((c) => c.name === selected)?.value ?? selected;
|
|
19025
19050
|
}
|
|
19026
19051
|
async function selectOpSecret(searchTerm) {
|
|
@@ -20735,8 +20760,8 @@ function findRootParent(file, importedBy, visited) {
|
|
|
20735
20760
|
function clusterFiles(graph) {
|
|
20736
20761
|
const clusters = /* @__PURE__ */ new Map();
|
|
20737
20762
|
for (const file of graph.files) {
|
|
20738
|
-
const
|
|
20739
|
-
if (
|
|
20763
|
+
const basename17 = path52.basename(file, path52.extname(file));
|
|
20764
|
+
if (basename17 === "index") continue;
|
|
20740
20765
|
const importers = graph.importedBy.get(file);
|
|
20741
20766
|
if (!importers || importers.size !== 1) continue;
|
|
20742
20767
|
const parent = [...importers][0];
|
|
@@ -21470,9 +21495,9 @@ function postFindings(findings) {
|
|
|
21470
21495
|
posted++;
|
|
21471
21496
|
} catch (error) {
|
|
21472
21497
|
failed2++;
|
|
21473
|
-
const
|
|
21498
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
21474
21499
|
console.error(
|
|
21475
|
-
`Failed to post comment on ${finding.file}:${finding.line}: ${
|
|
21500
|
+
`Failed to post comment on ${finding.file}:${finding.line}: ${message2}`
|
|
21476
21501
|
);
|
|
21477
21502
|
}
|
|
21478
21503
|
}
|
|
@@ -21491,8 +21516,8 @@ function submitPendingReview(body) {
|
|
|
21491
21516
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
21492
21517
|
return;
|
|
21493
21518
|
}
|
|
21494
|
-
const
|
|
21495
|
-
console.error(`Failed to submit review: ${
|
|
21519
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
21520
|
+
console.error(`Failed to submit review: ${message2}`);
|
|
21496
21521
|
}
|
|
21497
21522
|
}
|
|
21498
21523
|
|
|
@@ -22283,10 +22308,10 @@ function messageFor(err, command) {
|
|
|
22283
22308
|
return err.message || String(err);
|
|
22284
22309
|
}
|
|
22285
22310
|
function handleSpawnError(ctx, err) {
|
|
22286
|
-
const
|
|
22311
|
+
const message2 = messageFor(err, ctx.command);
|
|
22287
22312
|
const stderr = ctx.stderr ? `${ctx.stderr}
|
|
22288
|
-
${
|
|
22289
|
-
if (!ctx.quiet) console.error(`[${ctx.name}] failed: ${
|
|
22313
|
+
${message2}` : message2;
|
|
22314
|
+
if (!ctx.quiet) console.error(`[${ctx.name}] failed: ${message2}`);
|
|
22290
22315
|
return {
|
|
22291
22316
|
exitCode: 127,
|
|
22292
22317
|
stderr,
|
|
@@ -25150,8 +25175,8 @@ import { createInterface as createInterface6 } from "readline";
|
|
|
25150
25175
|
var RING_CAPACITY = 1e3;
|
|
25151
25176
|
var ring = [];
|
|
25152
25177
|
var sink;
|
|
25153
|
-
function daemonLog(
|
|
25154
|
-
emit3(`${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${
|
|
25178
|
+
function daemonLog(message2) {
|
|
25179
|
+
emit3(`${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${message2}`);
|
|
25155
25180
|
}
|
|
25156
25181
|
function relayDaemonLog(line) {
|
|
25157
25182
|
emit3(`[windows] ${line}`);
|
|
@@ -25177,7 +25202,7 @@ var persistedSessionSchema = z5.object({
|
|
|
25177
25202
|
subtitle: z5.string().optional(),
|
|
25178
25203
|
commandType: z5.enum(["claude", "run", "assist"]),
|
|
25179
25204
|
harness: z5.enum(["claude", "codex", "pi"]).optional(),
|
|
25180
|
-
status: z5.enum(["running", "waiting", "done", "error"]).optional(),
|
|
25205
|
+
status: z5.enum(["running", "waiting", "done", "error", "stopped"]).optional(),
|
|
25181
25206
|
cwd: z5.string(),
|
|
25182
25207
|
startedAt: z5.number(),
|
|
25183
25208
|
runningMs: z5.number().optional(),
|
|
@@ -25187,7 +25212,8 @@ var persistedSessionSchema = z5.object({
|
|
|
25187
25212
|
runArgs: z5.array(z5.string()).optional(),
|
|
25188
25213
|
assistArgs: z5.array(z5.string()).optional(),
|
|
25189
25214
|
activity: activitySchema.optional(),
|
|
25190
|
-
starred: z5.boolean().optional()
|
|
25215
|
+
starred: z5.boolean().optional(),
|
|
25216
|
+
undurable: z5.object({ reason: z5.string() }).optional()
|
|
25191
25217
|
});
|
|
25192
25218
|
|
|
25193
25219
|
// src/commands/sessions/daemon/toPersistedSession.ts
|
|
@@ -25208,7 +25234,8 @@ function toPersistedSession(session) {
|
|
|
25208
25234
|
runArgs: session.runArgs,
|
|
25209
25235
|
assistArgs: session.assistArgs,
|
|
25210
25236
|
activity: session.activity,
|
|
25211
|
-
starred: session.starred
|
|
25237
|
+
starred: session.starred,
|
|
25238
|
+
undurable: session.undurable
|
|
25212
25239
|
};
|
|
25213
25240
|
}
|
|
25214
25241
|
function accumulatedRunningMs(session) {
|
|
@@ -25230,7 +25257,7 @@ function savePersistedSessions(sessions) {
|
|
|
25230
25257
|
}
|
|
25231
25258
|
function persistLiveSessions(sessions) {
|
|
25232
25259
|
const live = [...sessions.values()].filter(
|
|
25233
|
-
(s) => s.pty && s.status !== "done"
|
|
25260
|
+
(s) => (s.pty || s.status === "stopped") && s.status !== "done"
|
|
25234
25261
|
);
|
|
25235
25262
|
savePersistedSessions(live.map(toPersistedSession));
|
|
25236
25263
|
logPersist(live);
|
|
@@ -25282,7 +25309,7 @@ function requestDrain(socket) {
|
|
|
25282
25309
|
}
|
|
25283
25310
|
|
|
25284
25311
|
// src/commands/sessions/daemon/runDaemon.ts
|
|
25285
|
-
import { mkdirSync as
|
|
25312
|
+
import { mkdirSync as mkdirSync27 } from "fs";
|
|
25286
25313
|
|
|
25287
25314
|
// src/commands/sessions/daemon/createAutoExit.ts
|
|
25288
25315
|
var DEFAULT_GRACE_MS = 6e4;
|
|
@@ -25385,7 +25412,8 @@ function toSessionInfo({
|
|
|
25385
25412
|
starred,
|
|
25386
25413
|
usedPct,
|
|
25387
25414
|
design,
|
|
25388
|
-
pendingPrPreview
|
|
25415
|
+
pendingPrPreview,
|
|
25416
|
+
undurable
|
|
25389
25417
|
}) {
|
|
25390
25418
|
return {
|
|
25391
25419
|
id,
|
|
@@ -25413,7 +25441,8 @@ function toSessionInfo({
|
|
|
25413
25441
|
starred,
|
|
25414
25442
|
usedPct,
|
|
25415
25443
|
design,
|
|
25416
|
-
pendingPrPreview
|
|
25444
|
+
pendingPrPreview,
|
|
25445
|
+
undurable
|
|
25417
25446
|
};
|
|
25418
25447
|
}
|
|
25419
25448
|
|
|
@@ -25503,20 +25532,56 @@ var ClientHub = class extends Set {
|
|
|
25503
25532
|
};
|
|
25504
25533
|
};
|
|
25505
25534
|
|
|
25535
|
+
// src/commands/sessions/daemon/createSession.ts
|
|
25536
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
25537
|
+
|
|
25538
|
+
// src/commands/sessions/daemon/resolveRunConfig.ts
|
|
25539
|
+
function resolveRunConfig(name, cwd) {
|
|
25540
|
+
try {
|
|
25541
|
+
const { run: run4 } = loadConfigFrom(cwd);
|
|
25542
|
+
const configs = resolveRunConfigs(run4, getConfigDirFrom(cwd));
|
|
25543
|
+
const exact = configs.find((r) => r.name === name);
|
|
25544
|
+
if (exact) return exact;
|
|
25545
|
+
const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
|
|
25546
|
+
return suffixMatches.length === 1 ? suffixMatches[0] : void 0;
|
|
25547
|
+
} catch {
|
|
25548
|
+
return void 0;
|
|
25549
|
+
}
|
|
25550
|
+
}
|
|
25551
|
+
|
|
25552
|
+
// src/commands/sessions/daemon/serverRunMeta.ts
|
|
25553
|
+
function serverRunMeta(runName, cwd) {
|
|
25554
|
+
const dir = cwd ?? process.cwd();
|
|
25555
|
+
const config = resolveRunConfig(runName, dir);
|
|
25556
|
+
if (!config?.server) return { server: false };
|
|
25557
|
+
return { server: true, port: config.port, origin: getCurrentOrigin(dir) };
|
|
25558
|
+
}
|
|
25559
|
+
|
|
25560
|
+
// src/commands/sessions/daemon/readDesignSystemPrompt.ts
|
|
25561
|
+
import * as fs35 from "fs";
|
|
25562
|
+
import * as path58 from "path";
|
|
25563
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
25564
|
+
var __filename4 = fileURLToPath8(import.meta.url);
|
|
25565
|
+
var __dirname6 = path58.dirname(__filename4);
|
|
25566
|
+
function readDesignSystemPrompt() {
|
|
25567
|
+
const promptPath = path58.join(__dirname6, "..", "claude", "system-prompt.md");
|
|
25568
|
+
return fs35.readFileSync(promptPath, "utf8");
|
|
25569
|
+
}
|
|
25570
|
+
|
|
25506
25571
|
// src/commands/sessions/daemon/spawnPty.ts
|
|
25507
25572
|
import * as pty from "node-pty";
|
|
25508
25573
|
|
|
25509
25574
|
// src/commands/sessions/daemon/ensureSpawnHelperExecutable.ts
|
|
25510
25575
|
import { chmodSync, existsSync as existsSync57, statSync as statSync9 } from "fs";
|
|
25511
25576
|
import { createRequire as createRequire3 } from "module";
|
|
25512
|
-
import
|
|
25577
|
+
import path59 from "path";
|
|
25513
25578
|
var require4 = createRequire3(import.meta.url);
|
|
25514
25579
|
var ensured = false;
|
|
25515
25580
|
function ensureSpawnHelperExecutable() {
|
|
25516
25581
|
if (ensured || process.platform !== "darwin") return;
|
|
25517
25582
|
ensured = true;
|
|
25518
|
-
const ptyRoot =
|
|
25519
|
-
const helper =
|
|
25583
|
+
const ptyRoot = path59.join(path59.dirname(require4.resolve("node-pty")), "..");
|
|
25584
|
+
const helper = path59.join(
|
|
25520
25585
|
ptyRoot,
|
|
25521
25586
|
"prebuilds",
|
|
25522
25587
|
`${process.platform}-${process.arch}`,
|
|
@@ -25553,62 +25618,6 @@ function shellEscape(s) {
|
|
|
25553
25618
|
return `'${s.replace(/'/g, String.raw`'\''`)}'`;
|
|
25554
25619
|
}
|
|
25555
25620
|
|
|
25556
|
-
// src/commands/sessions/daemon/createAssistSession.ts
|
|
25557
|
-
function createAssistSession(id, assistArgs, cwd, meta) {
|
|
25558
|
-
const startedAt = Date.now();
|
|
25559
|
-
return {
|
|
25560
|
-
id,
|
|
25561
|
-
name: `assist ${assistArgs.join(" ")}`,
|
|
25562
|
-
title: meta?.title,
|
|
25563
|
-
subtitle: meta?.subtitle,
|
|
25564
|
-
commandType: "assist",
|
|
25565
|
-
status: "running",
|
|
25566
|
-
startedAt,
|
|
25567
|
-
runningMs: 0,
|
|
25568
|
-
runningSince: startedAt,
|
|
25569
|
-
pty: spawnPty(["assist", ...assistArgs], cwd, id),
|
|
25570
|
-
scrollback: "",
|
|
25571
|
-
assistArgs,
|
|
25572
|
-
cwd
|
|
25573
|
-
};
|
|
25574
|
-
}
|
|
25575
|
-
|
|
25576
|
-
// src/commands/sessions/daemon/createSession.ts
|
|
25577
|
-
import { randomUUID as randomUUID5 } from "crypto";
|
|
25578
|
-
|
|
25579
|
-
// src/commands/sessions/daemon/resolveRunConfig.ts
|
|
25580
|
-
function resolveRunConfig(name, cwd) {
|
|
25581
|
-
try {
|
|
25582
|
-
const { run: run4 } = loadConfigFrom(cwd);
|
|
25583
|
-
const configs = resolveRunConfigs(run4, getConfigDirFrom(cwd));
|
|
25584
|
-
const exact = configs.find((r) => r.name === name);
|
|
25585
|
-
if (exact) return exact;
|
|
25586
|
-
const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
|
|
25587
|
-
return suffixMatches.length === 1 ? suffixMatches[0] : void 0;
|
|
25588
|
-
} catch {
|
|
25589
|
-
return void 0;
|
|
25590
|
-
}
|
|
25591
|
-
}
|
|
25592
|
-
|
|
25593
|
-
// src/commands/sessions/daemon/serverRunMeta.ts
|
|
25594
|
-
function serverRunMeta(runName, cwd) {
|
|
25595
|
-
const dir = cwd ?? process.cwd();
|
|
25596
|
-
const config = resolveRunConfig(runName, dir);
|
|
25597
|
-
if (!config?.server) return { server: false };
|
|
25598
|
-
return { server: true, port: config.port, origin: getCurrentOrigin(dir) };
|
|
25599
|
-
}
|
|
25600
|
-
|
|
25601
|
-
// src/commands/sessions/daemon/readDesignSystemPrompt.ts
|
|
25602
|
-
import * as fs35 from "fs";
|
|
25603
|
-
import * as path59 from "path";
|
|
25604
|
-
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
25605
|
-
var __filename4 = fileURLToPath8(import.meta.url);
|
|
25606
|
-
var __dirname6 = path59.dirname(__filename4);
|
|
25607
|
-
function readDesignSystemPrompt() {
|
|
25608
|
-
const promptPath = path59.join(__dirname6, "..", "claude", "system-prompt.md");
|
|
25609
|
-
return fs35.readFileSync(promptPath, "utf8");
|
|
25610
|
-
}
|
|
25611
|
-
|
|
25612
25621
|
// src/commands/sessions/daemon/spawnClaude.ts
|
|
25613
25622
|
function spawnClaude2(opts = {}) {
|
|
25614
25623
|
return spawnPty(buildArgs3(opts), opts.cwd, opts.sessionId);
|
|
@@ -25693,6 +25702,170 @@ function createRunSession(id, runName, runArgs, cwd, meta = serverRunMeta(runNam
|
|
|
25693
25702
|
};
|
|
25694
25703
|
}
|
|
25695
25704
|
|
|
25705
|
+
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
25706
|
+
import { existsSync as existsSync58 } from "fs";
|
|
25707
|
+
|
|
25708
|
+
// src/commands/sessions/daemon/worktree/git.ts
|
|
25709
|
+
import { execFile as execFile7, execFileSync as execFileSync12 } from "child_process";
|
|
25710
|
+
function isWindowsPath(cwd) {
|
|
25711
|
+
return /^[A-Za-z]:[\\/]/.test(cwd);
|
|
25712
|
+
}
|
|
25713
|
+
function gitInvocation(cwd, args) {
|
|
25714
|
+
const windows = isWindowsPath(cwd);
|
|
25715
|
+
return {
|
|
25716
|
+
file: windows ? "git.exe" : "git",
|
|
25717
|
+
argv: windows ? ["-C", cwd, ...args] : args,
|
|
25718
|
+
options: { encoding: "utf8", ...windows ? {} : { cwd } }
|
|
25719
|
+
};
|
|
25720
|
+
}
|
|
25721
|
+
function git3(cwd, args) {
|
|
25722
|
+
const { file, argv, options: options2 } = gitInvocation(cwd, args);
|
|
25723
|
+
return new Promise((resolve19, reject) => {
|
|
25724
|
+
execFile7(file, argv, options2, (error, stdout) => {
|
|
25725
|
+
if (error) reject(error);
|
|
25726
|
+
else resolve19(stdout.toString());
|
|
25727
|
+
});
|
|
25728
|
+
});
|
|
25729
|
+
}
|
|
25730
|
+
async function gitOrNull(cwd, args) {
|
|
25731
|
+
try {
|
|
25732
|
+
return (await git3(cwd, args)).trim() || null;
|
|
25733
|
+
} catch {
|
|
25734
|
+
return null;
|
|
25735
|
+
}
|
|
25736
|
+
}
|
|
25737
|
+
function gitSync(cwd, args) {
|
|
25738
|
+
const { file, argv, options: options2 } = gitInvocation(cwd, args);
|
|
25739
|
+
return execFileSync12(file, argv, {
|
|
25740
|
+
...options2,
|
|
25741
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
25742
|
+
}).toString();
|
|
25743
|
+
}
|
|
25744
|
+
function gitSyncOrNull(cwd, args) {
|
|
25745
|
+
try {
|
|
25746
|
+
return gitSync(cwd, args).trim() || null;
|
|
25747
|
+
} catch {
|
|
25748
|
+
return null;
|
|
25749
|
+
}
|
|
25750
|
+
}
|
|
25751
|
+
function listWorktreePaths(cwd) {
|
|
25752
|
+
const out = gitSyncOrNull(cwd, ["worktree", "list", "--porcelain"]);
|
|
25753
|
+
if (!out) return [];
|
|
25754
|
+
return out.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
|
|
25755
|
+
}
|
|
25756
|
+
function mainWorktree(cwd) {
|
|
25757
|
+
return listWorktreePaths(cwd)[0] ?? null;
|
|
25758
|
+
}
|
|
25759
|
+
function listLocalBranches(cwd) {
|
|
25760
|
+
const out = gitSyncOrNull(cwd, [
|
|
25761
|
+
"for-each-ref",
|
|
25762
|
+
"--format=%(refname:short)",
|
|
25763
|
+
"refs/heads"
|
|
25764
|
+
]);
|
|
25765
|
+
return out ? out.split("\n").map((line) => line.trim()) : [];
|
|
25766
|
+
}
|
|
25767
|
+
function gitCommonDir(cwd) {
|
|
25768
|
+
return gitSyncOrNull(cwd, [
|
|
25769
|
+
"rev-parse",
|
|
25770
|
+
"--path-format=absolute",
|
|
25771
|
+
"--git-common-dir"
|
|
25772
|
+
]);
|
|
25773
|
+
}
|
|
25774
|
+
|
|
25775
|
+
// src/commands/sessions/daemon/worktree/treeDurability.ts
|
|
25776
|
+
function treeDurability(state) {
|
|
25777
|
+
if (state.dirty) return { durable: false, reason: "uncommitted changes" };
|
|
25778
|
+
if (!state.push && state.localOnlyCommits)
|
|
25779
|
+
return { durable: false, reason: "unpushed commits" };
|
|
25780
|
+
return { durable: true };
|
|
25781
|
+
}
|
|
25782
|
+
async function hasLocalOnlyCommits(cwd) {
|
|
25783
|
+
const upstream = await gitOrNull(cwd, [
|
|
25784
|
+
"rev-parse",
|
|
25785
|
+
"--abbrev-ref",
|
|
25786
|
+
"--symbolic-full-name",
|
|
25787
|
+
"@{upstream}"
|
|
25788
|
+
]);
|
|
25789
|
+
if (upstream) {
|
|
25790
|
+
const ahead = await gitOrNull(cwd, [
|
|
25791
|
+
"rev-list",
|
|
25792
|
+
"--count",
|
|
25793
|
+
"@{upstream}..HEAD"
|
|
25794
|
+
]);
|
|
25795
|
+
return Number(ahead ?? "0") > 0;
|
|
25796
|
+
}
|
|
25797
|
+
const remoteBranches = await gitOrNull(cwd, [
|
|
25798
|
+
"branch",
|
|
25799
|
+
"-r",
|
|
25800
|
+
"--contains",
|
|
25801
|
+
"HEAD"
|
|
25802
|
+
]);
|
|
25803
|
+
return !remoteBranches;
|
|
25804
|
+
}
|
|
25805
|
+
async function collectTreeState(cwd, push) {
|
|
25806
|
+
const status3 = await gitOrNull(cwd, ["status", "--porcelain"]);
|
|
25807
|
+
return {
|
|
25808
|
+
dirty: status3 !== null,
|
|
25809
|
+
push,
|
|
25810
|
+
localOnlyCommits: await hasLocalOnlyCommits(cwd)
|
|
25811
|
+
};
|
|
25812
|
+
}
|
|
25813
|
+
function pushOnCommit(cwd) {
|
|
25814
|
+
try {
|
|
25815
|
+
return loadConfigFrom(cwd).commit.push;
|
|
25816
|
+
} catch {
|
|
25817
|
+
return false;
|
|
25818
|
+
}
|
|
25819
|
+
}
|
|
25820
|
+
async function checkDurability(cwd) {
|
|
25821
|
+
return treeDurability(await collectTreeState(cwd, pushOnCommit(cwd)));
|
|
25822
|
+
}
|
|
25823
|
+
|
|
25824
|
+
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
25825
|
+
async function reapWorktree(worktreePath, force = false) {
|
|
25826
|
+
if (!existsSync58(worktreePath)) {
|
|
25827
|
+
daemonLog(`worktree ${worktreePath} already gone; skipping reap`);
|
|
25828
|
+
return false;
|
|
25829
|
+
}
|
|
25830
|
+
if (!force) {
|
|
25831
|
+
const durability = await checkDurability(worktreePath);
|
|
25832
|
+
if (!durability.durable) {
|
|
25833
|
+
daemonLog(`worktree ${worktreePath} not reaped: ${durability.reason}`);
|
|
25834
|
+
return false;
|
|
25835
|
+
}
|
|
25836
|
+
}
|
|
25837
|
+
const clone = mainWorktree(worktreePath) ?? worktreePath;
|
|
25838
|
+
const branch2 = await gitOrNull(worktreePath, [
|
|
25839
|
+
"symbolic-ref",
|
|
25840
|
+
"--short",
|
|
25841
|
+
"HEAD"
|
|
25842
|
+
]);
|
|
25843
|
+
try {
|
|
25844
|
+
await git3(clone, [
|
|
25845
|
+
"worktree",
|
|
25846
|
+
"remove",
|
|
25847
|
+
...force ? ["--force"] : [],
|
|
25848
|
+
worktreePath
|
|
25849
|
+
]);
|
|
25850
|
+
if (branch2) await deleteBranch(clone, branch2);
|
|
25851
|
+
daemonLog(`worktree ${worktreePath} reaped${force ? " (forced)" : ""}`);
|
|
25852
|
+
return true;
|
|
25853
|
+
} catch (error) {
|
|
25854
|
+
daemonLog(
|
|
25855
|
+
`worktree ${worktreePath} reap failed: ${error instanceof Error ? error.message : String(error)}`
|
|
25856
|
+
);
|
|
25857
|
+
return false;
|
|
25858
|
+
}
|
|
25859
|
+
}
|
|
25860
|
+
async function deleteBranch(clone, branch2) {
|
|
25861
|
+
const current = await gitOrNull(clone, ["symbolic-ref", "--short", "HEAD"]);
|
|
25862
|
+
if (branch2 === current) return;
|
|
25863
|
+
try {
|
|
25864
|
+
await git3(clone, ["branch", "-D", branch2]);
|
|
25865
|
+
} catch {
|
|
25866
|
+
}
|
|
25867
|
+
}
|
|
25868
|
+
|
|
25696
25869
|
// src/commands/sessions/daemon/dismissSession.ts
|
|
25697
25870
|
function dismissSession(sessions, id) {
|
|
25698
25871
|
const s = sessions.get(id);
|
|
@@ -25700,10 +25873,12 @@ function dismissSession(sessions, id) {
|
|
|
25700
25873
|
if (s.status !== "done") s.pty?.kill();
|
|
25701
25874
|
s.activityWatcher?.close();
|
|
25702
25875
|
s.transcriptWatcher?.close();
|
|
25876
|
+
s.gitWatcher?.close();
|
|
25703
25877
|
removeActivity(s.id);
|
|
25704
25878
|
if (s.activity?.itemId != null) releaseLock(s.activity.itemId);
|
|
25705
25879
|
sessions.delete(id);
|
|
25706
25880
|
daemonLog(`session ${id} dismissed (${s.name})`);
|
|
25881
|
+
if (s.worktree) void reapWorktree(s.worktree.path);
|
|
25707
25882
|
return true;
|
|
25708
25883
|
}
|
|
25709
25884
|
function drainSessions(sessions, onDrained) {
|
|
@@ -25717,6 +25892,113 @@ function drainSessions(sessions, onDrained) {
|
|
|
25717
25892
|
return ids.length;
|
|
25718
25893
|
}
|
|
25719
25894
|
|
|
25895
|
+
// src/commands/sessions/daemon/killPtyTree.ts
|
|
25896
|
+
function killPtyTree(pty2) {
|
|
25897
|
+
if (process.platform === "win32") {
|
|
25898
|
+
pty2.kill();
|
|
25899
|
+
return;
|
|
25900
|
+
}
|
|
25901
|
+
try {
|
|
25902
|
+
process.kill(-pty2.pid, "SIGHUP");
|
|
25903
|
+
} catch {
|
|
25904
|
+
try {
|
|
25905
|
+
pty2.kill();
|
|
25906
|
+
} catch {
|
|
25907
|
+
}
|
|
25908
|
+
}
|
|
25909
|
+
}
|
|
25910
|
+
|
|
25911
|
+
// src/commands/sessions/daemon/setStatus.ts
|
|
25912
|
+
function setStatus2(session, newStatus) {
|
|
25913
|
+
const now = Date.now();
|
|
25914
|
+
if (session.status === "running" && session.runningSince != null) {
|
|
25915
|
+
session.runningMs += now - session.runningSince;
|
|
25916
|
+
}
|
|
25917
|
+
session.runningSince = newStatus === "running" ? now : null;
|
|
25918
|
+
session.status = newStatus;
|
|
25919
|
+
}
|
|
25920
|
+
|
|
25921
|
+
// src/commands/sessions/daemon/worktree/watchGitState.ts
|
|
25922
|
+
import { existsSync as existsSync59, watch } from "fs";
|
|
25923
|
+
var DEBOUNCE_MS = 500;
|
|
25924
|
+
function watchGitState(cwd, onChange) {
|
|
25925
|
+
const common = gitCommonDir(cwd);
|
|
25926
|
+
if (!common || !existsSync59(common)) return void 0;
|
|
25927
|
+
let timer = null;
|
|
25928
|
+
try {
|
|
25929
|
+
return watch(common, { recursive: true }, () => {
|
|
25930
|
+
if (timer) clearTimeout(timer);
|
|
25931
|
+
timer = setTimeout(onChange, DEBOUNCE_MS);
|
|
25932
|
+
});
|
|
25933
|
+
} catch (error) {
|
|
25934
|
+
daemonLog(
|
|
25935
|
+
`git-state watch failed for ${cwd}: ${error instanceof Error ? error.message : String(error)}`
|
|
25936
|
+
);
|
|
25937
|
+
return void 0;
|
|
25938
|
+
}
|
|
25939
|
+
}
|
|
25940
|
+
|
|
25941
|
+
// src/commands/sessions/daemon/worktree/resolveDoneDurability.ts
|
|
25942
|
+
async function resolveDoneDurability(session, finalize, notify2) {
|
|
25943
|
+
const worktree = session.worktree;
|
|
25944
|
+
if (!worktree) {
|
|
25945
|
+
finalize();
|
|
25946
|
+
return;
|
|
25947
|
+
}
|
|
25948
|
+
const durability = await checkDurability(worktree.path);
|
|
25949
|
+
if (!durability.durable) {
|
|
25950
|
+
holdStopped(session, durability.reason, finalize, notify2);
|
|
25951
|
+
return;
|
|
25952
|
+
}
|
|
25953
|
+
await reapWorktree(worktree.path);
|
|
25954
|
+
session.worktree = void 0;
|
|
25955
|
+
session.undurable = void 0;
|
|
25956
|
+
session.gitWatcher?.close();
|
|
25957
|
+
session.gitWatcher = void 0;
|
|
25958
|
+
finalize();
|
|
25959
|
+
}
|
|
25960
|
+
function holdStopped(session, reason, finalize, notify2) {
|
|
25961
|
+
if (session.undurable?.reason !== reason)
|
|
25962
|
+
daemonLog(`session ${session.id} stopped; reap blocked: ${reason}`);
|
|
25963
|
+
session.undurable = { reason };
|
|
25964
|
+
setStatus2(session, "stopped");
|
|
25965
|
+
if (!session.gitWatcher && session.worktree)
|
|
25966
|
+
session.gitWatcher = watchGitState(session.worktree.path, () => {
|
|
25967
|
+
if (session.status !== "stopped") return;
|
|
25968
|
+
void resolveDoneDurability(session, finalize, notify2);
|
|
25969
|
+
});
|
|
25970
|
+
notify2();
|
|
25971
|
+
}
|
|
25972
|
+
|
|
25973
|
+
// src/commands/sessions/daemon/dismissSessionGated.ts
|
|
25974
|
+
function dismissSessionGated(sessions, id, notify2, discard = false) {
|
|
25975
|
+
const s = sessions.get(id);
|
|
25976
|
+
if (!s?.worktree) {
|
|
25977
|
+
if (dismissSession(sessions, id)) notify2();
|
|
25978
|
+
return;
|
|
25979
|
+
}
|
|
25980
|
+
const removeCard = () => {
|
|
25981
|
+
if (dismissSession(sessions, id)) notify2();
|
|
25982
|
+
};
|
|
25983
|
+
const discardTree = async () => {
|
|
25984
|
+
if (s.worktree) {
|
|
25985
|
+
await reapWorktree(s.worktree.path, true);
|
|
25986
|
+
s.worktree = void 0;
|
|
25987
|
+
}
|
|
25988
|
+
removeCard();
|
|
25989
|
+
};
|
|
25990
|
+
if (s.pty) {
|
|
25991
|
+
s.pendingDismiss = discard ? () => void discardTree() : () => void resolveDoneDurability(s, removeCard, notify2);
|
|
25992
|
+
daemonLog(
|
|
25993
|
+
`session ${id} ${discard ? "discard" : "dismiss"} requested: killing process tree first`
|
|
25994
|
+
);
|
|
25995
|
+
killPtyTree(s.pty);
|
|
25996
|
+
return;
|
|
25997
|
+
}
|
|
25998
|
+
if (discard) void discardTree();
|
|
25999
|
+
else void resolveDoneDurability(s, removeCard, notify2);
|
|
26000
|
+
}
|
|
26001
|
+
|
|
25720
26002
|
// src/shared/db/recordPhaseActiveMs.ts
|
|
25721
26003
|
import { sql as sql20 } from "drizzle-orm";
|
|
25722
26004
|
async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
|
|
@@ -25897,6 +26179,12 @@ function applySetStatus(sessions, id, status3, source, onStatusChange) {
|
|
|
25897
26179
|
);
|
|
25898
26180
|
return;
|
|
25899
26181
|
}
|
|
26182
|
+
if (session.status === "stopped") {
|
|
26183
|
+
daemonLog(
|
|
26184
|
+
`set-status for stopped session id=${id} status=${status3} (held pending durable close; ignoring)`
|
|
26185
|
+
);
|
|
26186
|
+
return;
|
|
26187
|
+
}
|
|
25900
26188
|
if (source === "permission") session.permissionActive = true;
|
|
25901
26189
|
if (status3 === "running") session.permissionActive = false;
|
|
25902
26190
|
onStatusChange(session, status3);
|
|
@@ -26070,16 +26358,6 @@ function applyUsageRecord(sessions, windows, notify2, claudeSessionId, transcrip
|
|
|
26070
26358
|
notify2();
|
|
26071
26359
|
}
|
|
26072
26360
|
|
|
26073
|
-
// src/commands/sessions/daemon/setStatus.ts
|
|
26074
|
-
function setStatus2(session, newStatus) {
|
|
26075
|
-
const now = Date.now();
|
|
26076
|
-
if (session.status === "running" && session.runningSince != null) {
|
|
26077
|
-
session.runningMs += now - session.runningSince;
|
|
26078
|
-
}
|
|
26079
|
-
session.runningSince = newStatus === "running" ? now : null;
|
|
26080
|
-
session.status = newStatus;
|
|
26081
|
-
}
|
|
26082
|
-
|
|
26083
26361
|
// src/commands/sessions/daemon/shouldAutoDismiss.ts
|
|
26084
26362
|
function shouldAutoDismiss(session, exitCode) {
|
|
26085
26363
|
if (session.status !== "done" || exitCode !== 0) {
|
|
@@ -26125,13 +26403,26 @@ function shouldAutoRun(session) {
|
|
|
26125
26403
|
// src/commands/sessions/daemon/applyStatusChange.ts
|
|
26126
26404
|
function applyStatusChange(session, status3, exitCode, dismiss, notify2, reuseForRun) {
|
|
26127
26405
|
if (session.status === status3) return;
|
|
26406
|
+
const deps2 = { dismiss, notify: notify2, reuseForRun };
|
|
26407
|
+
if (status3 === "done" && session.worktree) {
|
|
26408
|
+
void resolveDoneDurability(
|
|
26409
|
+
session,
|
|
26410
|
+
() => finishStatusChange(session, "done", exitCode, deps2),
|
|
26411
|
+
notify2
|
|
26412
|
+
);
|
|
26413
|
+
return;
|
|
26414
|
+
}
|
|
26415
|
+
finishStatusChange(session, status3, exitCode, deps2);
|
|
26416
|
+
}
|
|
26417
|
+
function finishStatusChange(session, status3, exitCode, deps2) {
|
|
26128
26418
|
daemonLog(`session ${session.id} status: ${session.status} -> ${status3}`);
|
|
26129
26419
|
void flushPhaseActiveMs(session);
|
|
26130
26420
|
setStatus2(session, status3);
|
|
26421
|
+
if (status3 !== "waiting") session.undurable = void 0;
|
|
26131
26422
|
const autoRun = shouldAutoRun(session);
|
|
26132
26423
|
if (autoRun.run) {
|
|
26133
|
-
reuseForRun(session, autoRun.itemId);
|
|
26134
|
-
|
|
26424
|
+
deps2.reuseForRun(session, autoRun.itemId);
|
|
26425
|
+
deps2.notify();
|
|
26135
26426
|
return;
|
|
26136
26427
|
}
|
|
26137
26428
|
if (status3 === "done" && autoRun.reason != null) {
|
|
@@ -26139,8 +26430,8 @@ function applyStatusChange(session, status3, exitCode, dismiss, notify2, reuseFo
|
|
|
26139
26430
|
`session ${session.id} auto-run enabled but skipped (exit ${exitCode ?? "none"}): ${autoRun.reason}`
|
|
26140
26431
|
);
|
|
26141
26432
|
}
|
|
26142
|
-
if (shouldAutoDismiss(session, exitCode)) dismiss(session.id);
|
|
26143
|
-
else
|
|
26433
|
+
if (shouldAutoDismiss(session, exitCode)) deps2.dismiss(session.id);
|
|
26434
|
+
else deps2.notify();
|
|
26144
26435
|
}
|
|
26145
26436
|
|
|
26146
26437
|
// src/commands/sessions/daemon/makeStatusChangeHandler.ts
|
|
@@ -26161,7 +26452,7 @@ function handleFailedResume(session, exitCode, onStatusChange) {
|
|
|
26161
26452
|
}
|
|
26162
26453
|
|
|
26163
26454
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
26164
|
-
import { existsSync as
|
|
26455
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync25, watch as watch2 } from "fs";
|
|
26165
26456
|
import { dirname as dirname29 } from "path";
|
|
26166
26457
|
|
|
26167
26458
|
// src/commands/sessions/daemon/applyReviewPause.ts
|
|
@@ -26212,7 +26503,7 @@ async function seedRunningMsFromUsage(session, notify2) {
|
|
|
26212
26503
|
}
|
|
26213
26504
|
|
|
26214
26505
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
26215
|
-
var
|
|
26506
|
+
var DEBOUNCE_MS2 = 50;
|
|
26216
26507
|
function watchActivity(session, notify2, onClaudeSessionId) {
|
|
26217
26508
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
26218
26509
|
const path71 = activityPath(session.id);
|
|
@@ -26237,12 +26528,12 @@ function watchActivity(session, notify2, onClaudeSessionId) {
|
|
|
26237
26528
|
void seedRunningMsFromUsage(session, notify2);
|
|
26238
26529
|
notify2();
|
|
26239
26530
|
};
|
|
26240
|
-
session.activityWatcher =
|
|
26531
|
+
session.activityWatcher = watch2(dir, (_event, filename) => {
|
|
26241
26532
|
if (filename && !path71.endsWith(filename)) return;
|
|
26242
26533
|
if (timer) clearTimeout(timer);
|
|
26243
|
-
timer = setTimeout(read,
|
|
26534
|
+
timer = setTimeout(read, DEBOUNCE_MS2);
|
|
26244
26535
|
});
|
|
26245
|
-
if (
|
|
26536
|
+
if (existsSync60(path71)) read();
|
|
26246
26537
|
}
|
|
26247
26538
|
function refreshActivity(session) {
|
|
26248
26539
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
@@ -26277,6 +26568,15 @@ function handlePtyExit(session, exitCode, onStatusChange) {
|
|
|
26277
26568
|
return;
|
|
26278
26569
|
}
|
|
26279
26570
|
session.pty = null;
|
|
26571
|
+
if (session.pendingDismiss) {
|
|
26572
|
+
const dismiss = session.pendingDismiss;
|
|
26573
|
+
session.pendingDismiss = void 0;
|
|
26574
|
+
daemonLog(
|
|
26575
|
+
`session ${session.id} ("${session.name}") pty exited (code ${exitCode}) for dismiss; resolving durability now the process is gone`
|
|
26576
|
+
);
|
|
26577
|
+
dismiss();
|
|
26578
|
+
return;
|
|
26579
|
+
}
|
|
26280
26580
|
if (handleStoppedExit(session, exitCode, onStatusChange)) return;
|
|
26281
26581
|
refreshActivity(session);
|
|
26282
26582
|
if (handleFailedResume(session, exitCode, onStatusChange)) return;
|
|
@@ -26317,6 +26617,10 @@ function wirePtyEvents(session, clients, onStatusChange) {
|
|
|
26317
26617
|
|
|
26318
26618
|
// src/commands/sessions/daemon/respawnSession.ts
|
|
26319
26619
|
function respawnSession(session, respawn, status3, clients, onStatusChange) {
|
|
26620
|
+
session.gitWatcher?.close();
|
|
26621
|
+
session.gitWatcher = void 0;
|
|
26622
|
+
session.undurable = void 0;
|
|
26623
|
+
session.pendingDismiss = void 0;
|
|
26320
26624
|
session.scrollback = "";
|
|
26321
26625
|
session.startedAt = Date.now();
|
|
26322
26626
|
session.runningMs = 0;
|
|
@@ -26324,6 +26628,11 @@ function respawnSession(session, respawn, status3, clients, onStatusChange) {
|
|
|
26324
26628
|
setStatus2(session, status3);
|
|
26325
26629
|
session.restored = void 0;
|
|
26326
26630
|
session.pty = respawn();
|
|
26631
|
+
if (session.cols && session.rows)
|
|
26632
|
+
try {
|
|
26633
|
+
session.pty?.resize(session.cols, session.rows);
|
|
26634
|
+
} catch {
|
|
26635
|
+
}
|
|
26327
26636
|
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
26328
26637
|
wirePtyEvents(session, clients, onStatusChange);
|
|
26329
26638
|
}
|
|
@@ -26405,20 +26714,6 @@ function restartSession(session, clients, onStatusChange) {
|
|
|
26405
26714
|
}
|
|
26406
26715
|
return true;
|
|
26407
26716
|
}
|
|
26408
|
-
function killPtyTree(pty2) {
|
|
26409
|
-
if (process.platform === "win32") {
|
|
26410
|
-
pty2.kill();
|
|
26411
|
-
return;
|
|
26412
|
-
}
|
|
26413
|
-
try {
|
|
26414
|
-
process.kill(-pty2.pid, "SIGHUP");
|
|
26415
|
-
} catch {
|
|
26416
|
-
try {
|
|
26417
|
-
pty2.kill();
|
|
26418
|
-
} catch {
|
|
26419
|
-
}
|
|
26420
|
-
}
|
|
26421
|
-
}
|
|
26422
26717
|
|
|
26423
26718
|
// src/commands/sessions/daemon/restartManagedSession.ts
|
|
26424
26719
|
function restartManagedSession(sessions, id, clients, onStatusChange) {
|
|
@@ -26519,9 +26814,9 @@ function normalizeEntry(entry) {
|
|
|
26519
26814
|
return null;
|
|
26520
26815
|
}
|
|
26521
26816
|
function normalizeAssistant(entry) {
|
|
26522
|
-
const
|
|
26523
|
-
const stopReason = typeof
|
|
26524
|
-
const content =
|
|
26817
|
+
const message2 = asRecord2(entry.message);
|
|
26818
|
+
const stopReason = typeof message2?.stop_reason === "string" ? message2.stop_reason : null;
|
|
26819
|
+
const content = message2?.content;
|
|
26525
26820
|
const toolUses = [];
|
|
26526
26821
|
if (Array.isArray(content))
|
|
26527
26822
|
for (const block of content) {
|
|
@@ -26699,6 +26994,12 @@ function runningSession(base, persisted, pty2) {
|
|
|
26699
26994
|
function waitingSession(base, persisted, pty2) {
|
|
26700
26995
|
return restoredSession(base, persisted, pty2, "waiting");
|
|
26701
26996
|
}
|
|
26997
|
+
function stoppedSession(base, persisted) {
|
|
26998
|
+
return {
|
|
26999
|
+
...restoredSession(base, persisted, null, "stopped"),
|
|
27000
|
+
undurable: persisted.undurable
|
|
27001
|
+
};
|
|
27002
|
+
}
|
|
26702
27003
|
function restoredSession(base, persisted, pty2, status3) {
|
|
26703
27004
|
const startedAt = Date.now();
|
|
26704
27005
|
return {
|
|
@@ -26781,6 +27082,7 @@ function isUpdate(persisted) {
|
|
|
26781
27082
|
// src/commands/sessions/daemon/restoreSession.ts
|
|
26782
27083
|
function restoreSession(id, persisted) {
|
|
26783
27084
|
const base = restoreBase(id, persisted);
|
|
27085
|
+
if (persisted.status === "stopped") return stoppedSession(base, persisted);
|
|
26784
27086
|
if (isUpdate(persisted)) return updatedSession(id, persisted);
|
|
26785
27087
|
const idle = deriveRestoreStatus(persisted) !== "running";
|
|
26786
27088
|
if (needsWrapperRelaunch(persisted)) {
|
|
@@ -26862,27 +27164,6 @@ function restoreAll(spawn12, sessions) {
|
|
|
26862
27164
|
});
|
|
26863
27165
|
}
|
|
26864
27166
|
|
|
26865
|
-
// src/commands/sessions/daemon/resumeSession.ts
|
|
26866
|
-
function resumeSession(id, sessionId, cwd, name) {
|
|
26867
|
-
const startedAt = Date.now();
|
|
26868
|
-
return {
|
|
26869
|
-
id,
|
|
26870
|
-
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
26871
|
-
commandType: "claude",
|
|
26872
|
-
status: "waiting",
|
|
26873
|
-
startedAt,
|
|
26874
|
-
runningMs: 0,
|
|
26875
|
-
runningSince: null,
|
|
26876
|
-
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId: id }),
|
|
26877
|
-
scrollback: "",
|
|
26878
|
-
cwd,
|
|
26879
|
-
/* why: bind the card to the conversation it is resuming so a daemon restart
|
|
26880
|
-
* re-resumes this same transcript, not whichever .jsonl a cwd poller would
|
|
26881
|
-
* have re-discovered (#413). */
|
|
26882
|
-
claudeSessionId: sessionId
|
|
26883
|
-
};
|
|
26884
|
-
}
|
|
26885
|
-
|
|
26886
27167
|
// src/commands/sessions/daemon/retrySession.ts
|
|
26887
27168
|
function retrySession(session, clients, onStatusChange) {
|
|
26888
27169
|
const respawn = respawnThunk(session);
|
|
@@ -26958,6 +27239,9 @@ function runRetry(sessions, id, replace, deps2) {
|
|
|
26958
27239
|
function reuseSessionForRun(session, itemId2, clients, onStatusChange) {
|
|
26959
27240
|
const assistArgs = ["backlog", "run", String(itemId2)];
|
|
26960
27241
|
session.pty?.kill();
|
|
27242
|
+
session.gitWatcher?.close();
|
|
27243
|
+
session.gitWatcher = void 0;
|
|
27244
|
+
session.undurable = void 0;
|
|
26961
27245
|
session.assistArgs = assistArgs;
|
|
26962
27246
|
session.name = `assist ${assistArgs.join(" ")}`;
|
|
26963
27247
|
session.commandType = "assist";
|
|
@@ -26987,7 +27271,7 @@ function shutdownSessions(sessions) {
|
|
|
26987
27271
|
}
|
|
26988
27272
|
|
|
26989
27273
|
// src/commands/sessions/daemon/watchTranscript.ts
|
|
26990
|
-
import { existsSync as
|
|
27274
|
+
import { existsSync as existsSync62, watch as watch3 } from "fs";
|
|
26991
27275
|
|
|
26992
27276
|
// src/commands/sessions/daemon/reconcileTranscriptStatus.ts
|
|
26993
27277
|
async function resolveTranscriptPath(session) {
|
|
@@ -26999,7 +27283,8 @@ async function resolveTranscriptPath(session) {
|
|
|
26999
27283
|
return filePath;
|
|
27000
27284
|
}
|
|
27001
27285
|
async function reconcileTranscriptStatus(session, onStatusChange) {
|
|
27002
|
-
if (session.status === "done" || session.status === "error")
|
|
27286
|
+
if (session.status === "done" || session.status === "error" || session.status === "stopped")
|
|
27287
|
+
return;
|
|
27003
27288
|
const filePath = await resolveTranscriptPath(session);
|
|
27004
27289
|
if (!filePath) return;
|
|
27005
27290
|
const entries = await readTranscriptTail(filePath);
|
|
@@ -27016,12 +27301,12 @@ async function reconcileTranscriptStatus(session, onStatusChange) {
|
|
|
27016
27301
|
}
|
|
27017
27302
|
|
|
27018
27303
|
// src/commands/sessions/daemon/watchTranscript.ts
|
|
27019
|
-
var
|
|
27304
|
+
var DEBOUNCE_MS3 = 100;
|
|
27020
27305
|
function watchTranscript(session, onStatusChange) {
|
|
27021
27306
|
if (!session.cwd || !session.claudeSessionId) return;
|
|
27022
27307
|
if (session.watchedTranscriptId === session.claudeSessionId) return;
|
|
27023
27308
|
const dir = projectDirForCwd(session.cwd);
|
|
27024
|
-
if (!
|
|
27309
|
+
if (!existsSync62(dir)) return;
|
|
27025
27310
|
if (session.transcriptWatcher) {
|
|
27026
27311
|
session.transcriptWatcher.close();
|
|
27027
27312
|
session.transcriptWatcher = void 0;
|
|
@@ -27034,10 +27319,10 @@ function watchTranscript(session, onStatusChange) {
|
|
|
27034
27319
|
void reconcileTranscriptStatus(session, onStatusChange);
|
|
27035
27320
|
};
|
|
27036
27321
|
try {
|
|
27037
|
-
session.transcriptWatcher =
|
|
27322
|
+
session.transcriptWatcher = watch3(dir, (_event, changed) => {
|
|
27038
27323
|
if (changed && changed !== fileName) return;
|
|
27039
27324
|
if (timer) clearTimeout(timer);
|
|
27040
|
-
timer = setTimeout(run4,
|
|
27325
|
+
timer = setTimeout(run4, DEBOUNCE_MS3);
|
|
27041
27326
|
});
|
|
27042
27327
|
} catch (error) {
|
|
27043
27328
|
daemonLog(
|
|
@@ -27172,19 +27457,19 @@ async function defaultConnect() {
|
|
|
27172
27457
|
}
|
|
27173
27458
|
|
|
27174
27459
|
// src/commands/sessions/daemon/hasPersistedWindowsSessions.ts
|
|
27175
|
-
import { existsSync as
|
|
27460
|
+
import { existsSync as existsSync63, readFileSync as readFileSync49 } from "fs";
|
|
27176
27461
|
import { posix } from "path";
|
|
27177
27462
|
function hasPersistedWindowsSessions() {
|
|
27178
27463
|
const sessionsFile = windowsSessionsFileFromWsl();
|
|
27179
27464
|
if (!sessionsFile) return false;
|
|
27180
27465
|
try {
|
|
27181
|
-
if (!
|
|
27466
|
+
if (!existsSync63(sessionsFile)) return false;
|
|
27182
27467
|
const data = JSON.parse(readFileSync49(sessionsFile, "utf8"));
|
|
27183
27468
|
return Array.isArray(data) && data.length > 0;
|
|
27184
27469
|
} catch (error) {
|
|
27185
|
-
const
|
|
27470
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
27186
27471
|
daemonLog(
|
|
27187
|
-
`windows proxy: could not read windows sessions.json: ${
|
|
27472
|
+
`windows proxy: could not read windows sessions.json: ${message2}`
|
|
27188
27473
|
);
|
|
27189
27474
|
return false;
|
|
27190
27475
|
}
|
|
@@ -27211,8 +27496,8 @@ async function discoverWindowsSessions(conn) {
|
|
|
27211
27496
|
try {
|
|
27212
27497
|
await conn.ensure();
|
|
27213
27498
|
} catch (error) {
|
|
27214
|
-
const
|
|
27215
|
-
daemonLog(`windows proxy: discovery failed: ${
|
|
27499
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
27500
|
+
daemonLog(`windows proxy: discovery failed: ${message2}`);
|
|
27216
27501
|
}
|
|
27217
27502
|
}
|
|
27218
27503
|
|
|
@@ -27250,11 +27535,11 @@ async function forwardWindowsCreate(conn, state, client, data) {
|
|
|
27250
27535
|
state.pendingCreators.push({ client, timer });
|
|
27251
27536
|
conn.write(stripOutboundSessionId(data));
|
|
27252
27537
|
} catch (error) {
|
|
27253
|
-
const
|
|
27254
|
-
daemonLog(`windows proxy: forwardCreate failed: ${
|
|
27538
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
27539
|
+
daemonLog(`windows proxy: forwardCreate failed: ${message2}`);
|
|
27255
27540
|
sendTo(client, {
|
|
27256
27541
|
type: "error",
|
|
27257
|
-
message: `Windows session unavailable: ${
|
|
27542
|
+
message: `Windows session unavailable: ${message2}`
|
|
27258
27543
|
});
|
|
27259
27544
|
}
|
|
27260
27545
|
}
|
|
@@ -27289,10 +27574,10 @@ function takePendingCreator(state) {
|
|
|
27289
27574
|
clearTimeout(pending.timer);
|
|
27290
27575
|
return pending.client;
|
|
27291
27576
|
}
|
|
27292
|
-
function failPendingCreators(state,
|
|
27577
|
+
function failPendingCreators(state, message2) {
|
|
27293
27578
|
for (const { client, timer } of state.pendingCreators) {
|
|
27294
27579
|
clearTimeout(timer);
|
|
27295
|
-
sendTo(client, { type: "error", message });
|
|
27580
|
+
sendTo(client, { type: "error", message: message2 });
|
|
27296
27581
|
}
|
|
27297
27582
|
state.pendingCreators = [];
|
|
27298
27583
|
}
|
|
@@ -27432,9 +27717,9 @@ async function healWindowsDaemon() {
|
|
|
27432
27717
|
try {
|
|
27433
27718
|
await runOnWindowsHost("assist update", UPDATE_TIMEOUT_MS);
|
|
27434
27719
|
} catch (error) {
|
|
27435
|
-
const
|
|
27720
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
27436
27721
|
daemonLog(
|
|
27437
|
-
`windows daemon: auto-heal: \`assist update\` failed: ${
|
|
27722
|
+
`windows daemon: auto-heal: \`assist update\` failed: ${message2}`
|
|
27438
27723
|
);
|
|
27439
27724
|
throw error;
|
|
27440
27725
|
}
|
|
@@ -27609,11 +27894,11 @@ async function autoHealWindowsDaemon(conn, state, heal, version2) {
|
|
|
27609
27894
|
daemonLog("windows proxy: heal complete, reconnecting to windows daemon");
|
|
27610
27895
|
await conn.ensure();
|
|
27611
27896
|
} catch (error) {
|
|
27612
|
-
const
|
|
27613
|
-
daemonLog(`windows proxy: auto-heal failed: ${
|
|
27897
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
27898
|
+
daemonLog(`windows proxy: auto-heal failed: ${message2}`);
|
|
27614
27899
|
state.broadcast({
|
|
27615
27900
|
type: "error",
|
|
27616
|
-
message: `Windows host auto-update failed: ${
|
|
27901
|
+
message: `Windows host auto-update failed: ${message2}`
|
|
27617
27902
|
});
|
|
27618
27903
|
}
|
|
27619
27904
|
}
|
|
@@ -27721,6 +28006,335 @@ var WindowsProxy = class {
|
|
|
27721
28006
|
}
|
|
27722
28007
|
};
|
|
27723
28008
|
|
|
28009
|
+
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
28010
|
+
import { existsSync as existsSync66 } from "fs";
|
|
28011
|
+
import { basename as basename14 } from "path";
|
|
28012
|
+
|
|
28013
|
+
// src/commands/sessions/daemon/worktree/worktreeConfigFor.ts
|
|
28014
|
+
var DISABLED = {
|
|
28015
|
+
enabled: false,
|
|
28016
|
+
install: true,
|
|
28017
|
+
copy: [".env", "settings.local.json", ".claude/settings.local.json"]
|
|
28018
|
+
};
|
|
28019
|
+
function worktreeConfigFor(cwd) {
|
|
28020
|
+
try {
|
|
28021
|
+
return loadConfigFrom(cwd).worktree ?? DISABLED;
|
|
28022
|
+
} catch {
|
|
28023
|
+
return DISABLED;
|
|
28024
|
+
}
|
|
28025
|
+
}
|
|
28026
|
+
|
|
28027
|
+
// src/commands/sessions/daemon/worktree/detectExistingWorktree.ts
|
|
28028
|
+
function detectExistingWorktree(cwd) {
|
|
28029
|
+
const repoRoot = findRepoRoot(cwd) ?? cwd;
|
|
28030
|
+
if (!worktreeConfigFor(repoRoot).enabled) return void 0;
|
|
28031
|
+
const clone = mainWorktree(repoRoot);
|
|
28032
|
+
if (clone && clone !== repoRoot) return { path: repoRoot, clone };
|
|
28033
|
+
return void 0;
|
|
28034
|
+
}
|
|
28035
|
+
|
|
28036
|
+
// src/commands/sessions/daemon/worktree/seedWorktree.ts
|
|
28037
|
+
import { execFile as execFile8 } from "child_process";
|
|
28038
|
+
import { copyFileSync, existsSync as existsSync65, mkdirSync as mkdirSync26 } from "fs";
|
|
28039
|
+
import { dirname as dirname30, join as join71 } from "path";
|
|
28040
|
+
|
|
28041
|
+
// src/commands/sessions/daemon/worktree/resolveInstallCommand.ts
|
|
28042
|
+
import { existsSync as existsSync64 } from "fs";
|
|
28043
|
+
import { join as join70 } from "path";
|
|
28044
|
+
function detectInstallCommand(repoRoot) {
|
|
28045
|
+
if (!existsSync64(join70(repoRoot, "package.json"))) return null;
|
|
28046
|
+
if (existsSync64(join70(repoRoot, "pnpm-lock.yaml"))) return "pnpm install";
|
|
28047
|
+
if (existsSync64(join70(repoRoot, "yarn.lock"))) return "yarn install";
|
|
28048
|
+
if (existsSync64(join70(repoRoot, "bun.lockb"))) return "bun install";
|
|
28049
|
+
return "npm install";
|
|
28050
|
+
}
|
|
28051
|
+
function resolveInstallCommand(repoRoot, install) {
|
|
28052
|
+
if (install === false) return null;
|
|
28053
|
+
if (typeof install === "string") return install;
|
|
28054
|
+
return detectInstallCommand(repoRoot);
|
|
28055
|
+
}
|
|
28056
|
+
|
|
28057
|
+
// src/commands/sessions/daemon/worktree/seedWorktree.ts
|
|
28058
|
+
function seedWorktree(worktreePath, clone) {
|
|
28059
|
+
const cfg = worktreeConfigFor(clone);
|
|
28060
|
+
copyConfigFiles(worktreePath, clone, cfg.copy);
|
|
28061
|
+
runInstall(worktreePath, clone, cfg.install);
|
|
28062
|
+
}
|
|
28063
|
+
function copyConfigFiles(worktreePath, clone, copy) {
|
|
28064
|
+
for (const rel of copy) {
|
|
28065
|
+
const src = join71(clone, rel);
|
|
28066
|
+
if (!existsSync65(src)) continue;
|
|
28067
|
+
const dest = join71(worktreePath, rel);
|
|
28068
|
+
try {
|
|
28069
|
+
mkdirSync26(dirname30(dest), { recursive: true });
|
|
28070
|
+
copyFileSync(src, dest);
|
|
28071
|
+
daemonLog(`worktree ${worktreePath} seeded ${rel}`);
|
|
28072
|
+
} catch (error) {
|
|
28073
|
+
daemonLog(
|
|
28074
|
+
`worktree ${worktreePath} failed to seed ${rel}: ${message(error)}`
|
|
28075
|
+
);
|
|
28076
|
+
}
|
|
28077
|
+
}
|
|
28078
|
+
}
|
|
28079
|
+
function runInstall(worktreePath, clone, install) {
|
|
28080
|
+
const command = resolveInstallCommand(clone, install);
|
|
28081
|
+
if (!command) return;
|
|
28082
|
+
const windows = /^[A-Za-z]:[\\/]/.test(worktreePath);
|
|
28083
|
+
const shell = windows ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
28084
|
+
const shellArgs = windows ? ["/c", command] : ["-l", "-c", command];
|
|
28085
|
+
daemonLog(`worktree ${worktreePath} installing deps: ${command}`);
|
|
28086
|
+
execFile8(shell, shellArgs, { cwd: worktreePath }, (error) => {
|
|
28087
|
+
daemonLog(
|
|
28088
|
+
error ? `worktree ${worktreePath} install failed: ${error.message}` : `worktree ${worktreePath} install complete`
|
|
28089
|
+
);
|
|
28090
|
+
});
|
|
28091
|
+
}
|
|
28092
|
+
function message(error) {
|
|
28093
|
+
return error instanceof Error ? error.message : String(error);
|
|
28094
|
+
}
|
|
28095
|
+
|
|
28096
|
+
// src/commands/sessions/daemon/worktree/bindNewWorktree.ts
|
|
28097
|
+
function boundTreeRoots(sessions) {
|
|
28098
|
+
const roots = /* @__PURE__ */ new Set();
|
|
28099
|
+
for (const session of sessions.values())
|
|
28100
|
+
if (session.cwd) roots.add(findRepoRoot(session.cwd) ?? session.cwd);
|
|
28101
|
+
return roots;
|
|
28102
|
+
}
|
|
28103
|
+
function bindNewWorktree(session, alloc, notify2) {
|
|
28104
|
+
if (alloc.kind !== "worktree" || !alloc.cwd || !alloc.clone || !session)
|
|
28105
|
+
return;
|
|
28106
|
+
session.worktree = { path: alloc.cwd, clone: alloc.clone };
|
|
28107
|
+
if (alloc.created) seedWorktree(alloc.cwd, alloc.clone);
|
|
28108
|
+
notify2();
|
|
28109
|
+
}
|
|
28110
|
+
function bindRestoredWorktrees(sessions) {
|
|
28111
|
+
for (const session of sessions.values()) {
|
|
28112
|
+
if (session.worktree || !session.cwd) continue;
|
|
28113
|
+
const worktree = detectExistingWorktree(session.cwd);
|
|
28114
|
+
if (worktree) session.worktree = worktree;
|
|
28115
|
+
}
|
|
28116
|
+
}
|
|
28117
|
+
function bindResumedWorktree(session, cwd, notify2) {
|
|
28118
|
+
if (!session) return;
|
|
28119
|
+
const worktree = detectExistingWorktree(cwd);
|
|
28120
|
+
if (!worktree) return;
|
|
28121
|
+
session.worktree = worktree;
|
|
28122
|
+
notify2();
|
|
28123
|
+
}
|
|
28124
|
+
|
|
28125
|
+
// src/commands/sessions/daemon/worktree/readWorktreeRegistry.ts
|
|
28126
|
+
var REGISTRY_FILE = "worktrees.json";
|
|
28127
|
+
function readWorktreeRegistry() {
|
|
28128
|
+
const data = loadJson(REGISTRY_FILE);
|
|
28129
|
+
return Object.entries(data).map(([path71, record]) => ({ path: path71, ...record }));
|
|
28130
|
+
}
|
|
28131
|
+
function recordWorktree(path71, clone, origin) {
|
|
28132
|
+
const data = loadJson(REGISTRY_FILE);
|
|
28133
|
+
data[path71] = { clone, origin };
|
|
28134
|
+
saveJson(REGISTRY_FILE, data);
|
|
28135
|
+
}
|
|
28136
|
+
|
|
28137
|
+
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
28138
|
+
function reconcileWorktreesOnRestore(sessions) {
|
|
28139
|
+
bindRestoredWorktrees(sessions);
|
|
28140
|
+
void pruneWorktrees([...sessions.values()].map((s) => s.cwd));
|
|
28141
|
+
}
|
|
28142
|
+
async function pruneWorktrees(liveCwds) {
|
|
28143
|
+
const accounted = /* @__PURE__ */ new Set();
|
|
28144
|
+
for (const cwd of liveCwds) if (cwd) accounted.add(cwd);
|
|
28145
|
+
for (const s of loadPersistedSessions()) accounted.add(s.cwd);
|
|
28146
|
+
const clonesToPrune = /* @__PURE__ */ new Set();
|
|
28147
|
+
const orphanBranches = [];
|
|
28148
|
+
for (const { path: path71, clone } of readWorktreeRegistry()) {
|
|
28149
|
+
if (accounted.has(path71)) continue;
|
|
28150
|
+
if (!existsSync66(path71)) {
|
|
28151
|
+
clonesToPrune.add(clone);
|
|
28152
|
+
orphanBranches.push({ clone, branch: basename14(path71) });
|
|
28153
|
+
continue;
|
|
28154
|
+
}
|
|
28155
|
+
daemonLog(`worktree ${path71} orphaned across restart; reconciling`);
|
|
28156
|
+
await reapWorktree(path71);
|
|
28157
|
+
}
|
|
28158
|
+
for (const clone of clonesToPrune) {
|
|
28159
|
+
if (!existsSync66(clone)) continue;
|
|
28160
|
+
try {
|
|
28161
|
+
await git3(clone, ["worktree", "prune"]);
|
|
28162
|
+
daemonLog(`worktree bookkeeping pruned for clone ${clone}`);
|
|
28163
|
+
} catch {
|
|
28164
|
+
}
|
|
28165
|
+
}
|
|
28166
|
+
for (const { clone, branch: branch2 } of orphanBranches)
|
|
28167
|
+
if (existsSync66(clone)) await pruneStaleWorktreeBranch(clone, branch2);
|
|
28168
|
+
}
|
|
28169
|
+
async function pruneStaleWorktreeBranch(clone, branch2) {
|
|
28170
|
+
if (!listLocalBranches(clone).includes(branch2)) return;
|
|
28171
|
+
try {
|
|
28172
|
+
await git3(clone, ["branch", "-d", branch2]);
|
|
28173
|
+
daemonLog(`stale worktree branch ${branch2} pruned from clone ${clone}`);
|
|
28174
|
+
} catch {
|
|
28175
|
+
daemonLog(
|
|
28176
|
+
`stale worktree branch ${branch2} retained (unmerged; surfaced not destroyed) in clone ${clone}`
|
|
28177
|
+
);
|
|
28178
|
+
}
|
|
28179
|
+
}
|
|
28180
|
+
|
|
28181
|
+
// src/commands/sessions/daemon/worktree/rearmStoppedSessions.ts
|
|
28182
|
+
function rearmStoppedSessions(sessions, notify2) {
|
|
28183
|
+
for (const s of sessions.values()) {
|
|
28184
|
+
if (s.status !== "stopped" || !s.worktree) continue;
|
|
28185
|
+
void resolveDoneDurability(
|
|
28186
|
+
s,
|
|
28187
|
+
() => {
|
|
28188
|
+
if (dismissSession(sessions, s.id)) notify2();
|
|
28189
|
+
},
|
|
28190
|
+
notify2
|
|
28191
|
+
);
|
|
28192
|
+
}
|
|
28193
|
+
}
|
|
28194
|
+
|
|
28195
|
+
// src/commands/sessions/daemon/createAssistSession.ts
|
|
28196
|
+
function createAssistSession(id, assistArgs, cwd, meta) {
|
|
28197
|
+
const startedAt = Date.now();
|
|
28198
|
+
return {
|
|
28199
|
+
id,
|
|
28200
|
+
name: `assist ${assistArgs.join(" ")}`,
|
|
28201
|
+
title: meta?.title,
|
|
28202
|
+
subtitle: meta?.subtitle,
|
|
28203
|
+
commandType: "assist",
|
|
28204
|
+
status: "running",
|
|
28205
|
+
startedAt,
|
|
28206
|
+
runningMs: 0,
|
|
28207
|
+
runningSince: startedAt,
|
|
28208
|
+
pty: spawnPty(["assist", ...assistArgs], cwd, id),
|
|
28209
|
+
scrollback: "",
|
|
28210
|
+
assistArgs,
|
|
28211
|
+
cwd
|
|
28212
|
+
};
|
|
28213
|
+
}
|
|
28214
|
+
|
|
28215
|
+
// src/commands/sessions/daemon/resumeSession.ts
|
|
28216
|
+
function resumeSession(id, sessionId, cwd, name) {
|
|
28217
|
+
const startedAt = Date.now();
|
|
28218
|
+
return {
|
|
28219
|
+
id,
|
|
28220
|
+
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
28221
|
+
commandType: "claude",
|
|
28222
|
+
status: "waiting",
|
|
28223
|
+
startedAt,
|
|
28224
|
+
runningMs: 0,
|
|
28225
|
+
runningSince: null,
|
|
28226
|
+
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId: id }),
|
|
28227
|
+
scrollback: "",
|
|
28228
|
+
cwd,
|
|
28229
|
+
/* why: bind the card to the conversation it is resuming so a daemon restart
|
|
28230
|
+
* re-resumes this same transcript, not whichever .jsonl a cwd poller would
|
|
28231
|
+
* have re-discovered (#413). */
|
|
28232
|
+
claudeSessionId: sessionId
|
|
28233
|
+
};
|
|
28234
|
+
}
|
|
28235
|
+
|
|
28236
|
+
// src/commands/sessions/daemon/worktree/createWorktree.ts
|
|
28237
|
+
import { existsSync as existsSync67 } from "fs";
|
|
28238
|
+
import { basename as basename16, dirname as dirname31 } from "path";
|
|
28239
|
+
|
|
28240
|
+
// src/commands/sessions/daemon/worktree/planAllocation.ts
|
|
28241
|
+
import { basename as basename15, join as join72 } from "path";
|
|
28242
|
+
function planAllocation(clone, boundTreeRoots2) {
|
|
28243
|
+
return boundTreeRoots2.has(clone) ? "spill" : "primary";
|
|
28244
|
+
}
|
|
28245
|
+
function nextWorktreePath(clone, base, isTaken) {
|
|
28246
|
+
const name = basename15(clone);
|
|
28247
|
+
for (let n = 2; n < 1e3; n++) {
|
|
28248
|
+
const candidate = join72(base, `${name}-${n}`);
|
|
28249
|
+
if (!isTaken(candidate)) return candidate;
|
|
28250
|
+
}
|
|
28251
|
+
throw new Error(`no free worktree suffix for ${clone}`);
|
|
28252
|
+
}
|
|
28253
|
+
|
|
28254
|
+
// src/commands/sessions/daemon/worktree/createWorktree.ts
|
|
28255
|
+
function createWorktree(clone, root, boundTreeRoots2) {
|
|
28256
|
+
const base = root ? expandTilde2(root) : dirname31(clone);
|
|
28257
|
+
const registered = new Set(listWorktreePaths(clone));
|
|
28258
|
+
const branches = new Set(listLocalBranches(clone));
|
|
28259
|
+
const path71 = nextWorktreePath(
|
|
28260
|
+
clone,
|
|
28261
|
+
base,
|
|
28262
|
+
(candidate) => registered.has(candidate) || existsSync67(candidate) || boundTreeRoots2.has(candidate) || branches.has(basename16(candidate))
|
|
28263
|
+
);
|
|
28264
|
+
const trunk = cloneTrunkBranch(clone);
|
|
28265
|
+
gitSync(clone, ["config", "push.default", "upstream"]);
|
|
28266
|
+
gitSync(clone, [
|
|
28267
|
+
"worktree",
|
|
28268
|
+
"add",
|
|
28269
|
+
"--track",
|
|
28270
|
+
"-b",
|
|
28271
|
+
basename16(path71),
|
|
28272
|
+
path71,
|
|
28273
|
+
`origin/${trunk}`
|
|
28274
|
+
]);
|
|
28275
|
+
recordWorktree(path71, clone, getCurrentOrigin(clone));
|
|
28276
|
+
daemonLog(
|
|
28277
|
+
`worktree allocated ${path71} (branch ${basename16(path71)} tracking origin/${trunk}) for clone ${clone}`
|
|
28278
|
+
);
|
|
28279
|
+
return path71;
|
|
28280
|
+
}
|
|
28281
|
+
function cloneTrunkBranch(clone) {
|
|
28282
|
+
const branch2 = gitSyncOrNull(clone, ["symbolic-ref", "--short", "HEAD"]);
|
|
28283
|
+
if (branch2) return branch2;
|
|
28284
|
+
const head = gitSyncOrNull(clone, [
|
|
28285
|
+
"symbolic-ref",
|
|
28286
|
+
"--short",
|
|
28287
|
+
"refs/remotes/origin/HEAD"
|
|
28288
|
+
]);
|
|
28289
|
+
if (head) {
|
|
28290
|
+
const slash = head.indexOf("/");
|
|
28291
|
+
return slash === -1 ? head : head.slice(slash + 1);
|
|
28292
|
+
}
|
|
28293
|
+
return "main";
|
|
28294
|
+
}
|
|
28295
|
+
|
|
28296
|
+
// src/commands/sessions/daemon/worktree/allocateTree.ts
|
|
28297
|
+
function allocateTree(requestedCwd, boundTreeRoots2) {
|
|
28298
|
+
if (!requestedCwd)
|
|
28299
|
+
return { cwd: requestedCwd, kind: "primary", created: false };
|
|
28300
|
+
const repoRoot = findRepoRoot(requestedCwd) ?? requestedCwd;
|
|
28301
|
+
const cfg = worktreeConfigFor(repoRoot);
|
|
28302
|
+
if (!cfg.enabled)
|
|
28303
|
+
return { cwd: requestedCwd, kind: "primary", created: false };
|
|
28304
|
+
const clone = mainWorktree(repoRoot) ?? repoRoot;
|
|
28305
|
+
if (planAllocation(clone, boundTreeRoots2) === "primary")
|
|
28306
|
+
return { cwd: clone, kind: "primary", created: false, clone };
|
|
28307
|
+
const path71 = createWorktree(clone, cfg.root, boundTreeRoots2);
|
|
28308
|
+
return { cwd: path71, kind: "worktree", created: true, clone };
|
|
28309
|
+
}
|
|
28310
|
+
|
|
28311
|
+
// src/commands/sessions/daemon/worktree/spawnInTree.ts
|
|
28312
|
+
function allocateAndBind(ctx, cwd, create) {
|
|
28313
|
+
const alloc = allocateTree(cwd, boundTreeRoots(ctx.sessions));
|
|
28314
|
+
const id = ctx.spawnWith((sid) => create(sid, alloc.cwd));
|
|
28315
|
+
bindNewWorktree(ctx.sessions.get(id), alloc, ctx.notify);
|
|
28316
|
+
return id;
|
|
28317
|
+
}
|
|
28318
|
+
function spawnInTree(ctx, prompt, cwd, design, harness) {
|
|
28319
|
+
return allocateAndBind(
|
|
28320
|
+
ctx,
|
|
28321
|
+
cwd,
|
|
28322
|
+
(sid, resolvedCwd) => createSession(sid, prompt, resolvedCwd, design, harness)
|
|
28323
|
+
);
|
|
28324
|
+
}
|
|
28325
|
+
function spawnAssistInTree(ctx, assistArgs, cwd, meta) {
|
|
28326
|
+
return allocateAndBind(
|
|
28327
|
+
ctx,
|
|
28328
|
+
cwd,
|
|
28329
|
+
(sid, resolvedCwd) => createAssistSession(sid, assistArgs, resolvedCwd, meta)
|
|
28330
|
+
);
|
|
28331
|
+
}
|
|
28332
|
+
function resumeInTree(ctx, sessionId, cwd, name) {
|
|
28333
|
+
const id = ctx.spawnWith((sid) => resumeSession(sid, sessionId, cwd, name));
|
|
28334
|
+
bindResumedWorktree(ctx.sessions.get(id), cwd, ctx.notify);
|
|
28335
|
+
return id;
|
|
28336
|
+
}
|
|
28337
|
+
|
|
27724
28338
|
// src/commands/sessions/daemon/watchPromptSubmit.ts
|
|
27725
28339
|
var CARRIAGE_RETURN = "\r";
|
|
27726
28340
|
function isPromptSubmit(data) {
|
|
@@ -27744,6 +28358,8 @@ function writeToSession(sessions, id, data, onStatusChange) {
|
|
|
27744
28358
|
function resizeSession(sessions, id, cols, rows) {
|
|
27745
28359
|
const s = sessions.get(id);
|
|
27746
28360
|
if (!s || s.status === "done" || !s.pty) return;
|
|
28361
|
+
s.cols = cols;
|
|
28362
|
+
s.rows = rows;
|
|
27747
28363
|
try {
|
|
27748
28364
|
s.pty.resize(cols, rows);
|
|
27749
28365
|
} catch (error) {
|
|
@@ -27818,7 +28434,10 @@ var SessionManager = class {
|
|
|
27818
28434
|
);
|
|
27819
28435
|
}
|
|
27820
28436
|
restore() {
|
|
27821
|
-
|
|
28437
|
+
const names = restoreAll(this.spawnWith, this.sessions);
|
|
28438
|
+
reconcileWorktreesOnRestore(this.sessions);
|
|
28439
|
+
rearmStoppedSessions(this.sessions, this.notify);
|
|
28440
|
+
return names;
|
|
27822
28441
|
}
|
|
27823
28442
|
drain = () => drainSessions(this.sessions, this.notify);
|
|
27824
28443
|
add(session) {
|
|
@@ -27828,10 +28447,15 @@ var SessionManager = class {
|
|
|
27828
28447
|
return session.id;
|
|
27829
28448
|
}
|
|
27830
28449
|
spawnWith = (create) => this.add(create(sessionLimits.nextId(this.sessions.size, this.idCounter)));
|
|
28450
|
+
treeCtx() {
|
|
28451
|
+
return {
|
|
28452
|
+
sessions: this.sessions,
|
|
28453
|
+
spawnWith: this.spawnWith,
|
|
28454
|
+
notify: this.notify
|
|
28455
|
+
};
|
|
28456
|
+
}
|
|
27831
28457
|
spawn(prompt, cwd, design, harness) {
|
|
27832
|
-
return this.
|
|
27833
|
-
(id) => createSession(id, prompt, cwd, design, harness)
|
|
27834
|
-
);
|
|
28458
|
+
return spawnInTree(this.treeCtx(), prompt, cwd, design, harness);
|
|
27835
28459
|
}
|
|
27836
28460
|
spawnRun(runName, runArgs, cwd, meta) {
|
|
27837
28461
|
return this.spawnWith(
|
|
@@ -27842,12 +28466,10 @@ var SessionManager = class {
|
|
|
27842
28466
|
return liveServerRun(this.sessions, origin, excludeId);
|
|
27843
28467
|
}
|
|
27844
28468
|
spawnAssist(assistArgs, cwd, meta) {
|
|
27845
|
-
return this.
|
|
27846
|
-
(id) => createAssistSession(id, assistArgs, cwd, meta)
|
|
27847
|
-
);
|
|
28469
|
+
return spawnAssistInTree(this.treeCtx(), assistArgs, cwd, meta);
|
|
27848
28470
|
}
|
|
27849
28471
|
resume(sessionId, cwd, name) {
|
|
27850
|
-
return this.
|
|
28472
|
+
return resumeInTree(this.treeCtx(), sessionId, cwd, name);
|
|
27851
28473
|
}
|
|
27852
28474
|
onStatusChange = makeStatusChangeHandler(
|
|
27853
28475
|
(id) => this.dismissSession(id),
|
|
@@ -27884,7 +28506,10 @@ var SessionManager = class {
|
|
|
27884
28506
|
return result;
|
|
27885
28507
|
}
|
|
27886
28508
|
dismissSession = (id) => {
|
|
27887
|
-
|
|
28509
|
+
dismissSessionGated(this.sessions, id, this.notify);
|
|
28510
|
+
};
|
|
28511
|
+
discardSession = (id) => {
|
|
28512
|
+
dismissSessionGated(this.sessions, id, this.notify, true);
|
|
27888
28513
|
};
|
|
27889
28514
|
stopSession(id) {
|
|
27890
28515
|
stopServerSession(this.sessions, id);
|
|
@@ -28165,6 +28790,7 @@ var messageHandlers = {
|
|
|
28165
28790
|
sendTo(client, { type: "error", message: result.reason });
|
|
28166
28791
|
}),
|
|
28167
28792
|
dismiss: routed((_client, m, d) => m.dismissSession(d.sessionId)),
|
|
28793
|
+
discard: routed((_client, m, d) => m.discardSession(d.sessionId)),
|
|
28168
28794
|
stop: routed((_client, m, d) => m.stopSession(d.sessionId)),
|
|
28169
28795
|
"set-autorun": routed(
|
|
28170
28796
|
(_client, m, d) => m.setAutoRun(d.sessionId, d.enabled)
|
|
@@ -28326,7 +28952,7 @@ async function recoverFromAddrInUse(server, manager, checkAutoExit) {
|
|
|
28326
28952
|
|
|
28327
28953
|
// src/commands/sessions/daemon/runDaemon.ts
|
|
28328
28954
|
async function runDaemon() {
|
|
28329
|
-
|
|
28955
|
+
mkdirSync27(daemonPaths.dir, { recursive: true });
|
|
28330
28956
|
daemonLog(
|
|
28331
28957
|
`starting (reason: ${process.env.ASSIST_DAEMON_SPAWN_REASON ?? "manual"})`
|
|
28332
28958
|
);
|
|
@@ -28379,6 +29005,50 @@ function registerSetStatusCommand(cmd) {
|
|
|
28379
29005
|
);
|
|
28380
29006
|
}
|
|
28381
29007
|
|
|
29008
|
+
// src/commands/sessions/sessionsConfigHelp.ts
|
|
29009
|
+
var sessionsConfigHelp = [
|
|
29010
|
+
{
|
|
29011
|
+
key: "sessions.windowsProjectsRoot",
|
|
29012
|
+
setter: "assist config set sessions.windowsProjectsRoot /mnt/c/Users/you/.claude/projects",
|
|
29013
|
+
note: "WSL path to the Windows .claude/projects root to discover"
|
|
29014
|
+
},
|
|
29015
|
+
{
|
|
29016
|
+
key: "sessions.windowsDaemonHost",
|
|
29017
|
+
setter: "assist config set sessions.windowsDaemonHost 127.0.0.1",
|
|
29018
|
+
note: "host the WSL daemon dials to reach the Windows daemon (default: 127.0.0.1)"
|
|
29019
|
+
},
|
|
29020
|
+
{
|
|
29021
|
+
key: "sessions.windowsDaemonPort",
|
|
29022
|
+
setter: "assist config set sessions.windowsDaemonPort 51764",
|
|
29023
|
+
note: "TCP port the Windows daemon listens on (default: 51764)"
|
|
29024
|
+
},
|
|
29025
|
+
{
|
|
29026
|
+
key: "sessions.windowsVersionCheck",
|
|
29027
|
+
setter: "assist config set sessions.windowsVersionCheck block",
|
|
29028
|
+
note: "Windows daemon version mismatch handling: block | warn | off"
|
|
29029
|
+
},
|
|
29030
|
+
{
|
|
29031
|
+
key: "worktree.enabled",
|
|
29032
|
+
setter: "assist config set worktree.enabled true -g --repo",
|
|
29033
|
+
note: "opt in per repo: spill concurrent sessions into adjacent <clone>-N worktrees (default off)"
|
|
29034
|
+
},
|
|
29035
|
+
{
|
|
29036
|
+
key: "worktree.root",
|
|
29037
|
+
setter: "assist config set worktree.root ~/git -g --repo",
|
|
29038
|
+
note: "optional; parent dir for <clone>-N worktrees (default: the clone's parent)"
|
|
29039
|
+
},
|
|
29040
|
+
{
|
|
29041
|
+
key: "worktree.install",
|
|
29042
|
+
setter: "assist config set worktree.install true -g --repo",
|
|
29043
|
+
note: "per-worktree dep install: true auto-detects the package manager, or give an explicit command; false to skip"
|
|
29044
|
+
},
|
|
29045
|
+
{
|
|
29046
|
+
key: "worktree.copy",
|
|
29047
|
+
setter: "assist config set worktree.copy .env,.claude/settings.local.json -g --repo",
|
|
29048
|
+
note: "gitignored files copied into a new worktree so it can build/run"
|
|
29049
|
+
}
|
|
29050
|
+
];
|
|
29051
|
+
|
|
28382
29052
|
// src/commands/sessions/summarise/index.ts
|
|
28383
29053
|
import * as fs41 from "fs";
|
|
28384
29054
|
import chalk204 from "chalk";
|
|
@@ -28397,7 +29067,7 @@ function summaryPathFor(jsonlPath2) {
|
|
|
28397
29067
|
}
|
|
28398
29068
|
|
|
28399
29069
|
// src/commands/sessions/summarise/summariseSession.ts
|
|
28400
|
-
import { execFileSync as
|
|
29070
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
28401
29071
|
|
|
28402
29072
|
// src/commands/sessions/summarise/extractFirstUserMessage.ts
|
|
28403
29073
|
function extractFirstUserMessage(filePath) {
|
|
@@ -28421,7 +29091,7 @@ function summariseSession(jsonlPath2) {
|
|
|
28421
29091
|
}
|
|
28422
29092
|
const prompt = buildPrompt3(firstMessage, backlogIds);
|
|
28423
29093
|
try {
|
|
28424
|
-
const output =
|
|
29094
|
+
const output = execFileSync13("claude", ["-p", "--model", "haiku", prompt], {
|
|
28425
29095
|
encoding: "utf8",
|
|
28426
29096
|
timeout: 3e4,
|
|
28427
29097
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -28516,28 +29186,7 @@ function registerSessions(program2) {
|
|
|
28516
29186
|
);
|
|
28517
29187
|
cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise2);
|
|
28518
29188
|
registerSetStatusCommand(cmd);
|
|
28519
|
-
configHelp(cmd,
|
|
28520
|
-
{
|
|
28521
|
-
key: "sessions.windowsProjectsRoot",
|
|
28522
|
-
setter: "assist config set sessions.windowsProjectsRoot /mnt/c/Users/you/.claude/projects",
|
|
28523
|
-
note: "WSL path to the Windows .claude/projects root to discover"
|
|
28524
|
-
},
|
|
28525
|
-
{
|
|
28526
|
-
key: "sessions.windowsDaemonHost",
|
|
28527
|
-
setter: "assist config set sessions.windowsDaemonHost 127.0.0.1",
|
|
28528
|
-
note: "host the WSL daemon dials to reach the Windows daemon (default: 127.0.0.1)"
|
|
28529
|
-
},
|
|
28530
|
-
{
|
|
28531
|
-
key: "sessions.windowsDaemonPort",
|
|
28532
|
-
setter: "assist config set sessions.windowsDaemonPort 51764",
|
|
28533
|
-
note: "TCP port the Windows daemon listens on (default: 51764)"
|
|
28534
|
-
},
|
|
28535
|
-
{
|
|
28536
|
-
key: "sessions.windowsVersionCheck",
|
|
28537
|
-
setter: "assist config set sessions.windowsVersionCheck block",
|
|
28538
|
-
note: "Windows daemon version mismatch handling: block | warn | off"
|
|
28539
|
-
}
|
|
28540
|
-
]);
|
|
29189
|
+
configHelp(cmd, sessionsConfigHelp);
|
|
28541
29190
|
}
|
|
28542
29191
|
|
|
28543
29192
|
// src/commands/statusLine.ts
|
|
@@ -28616,9 +29265,9 @@ function buildLimitsSegment(rateLimits) {
|
|
|
28616
29265
|
|
|
28617
29266
|
// src/commands/readGitBranch.ts
|
|
28618
29267
|
import { readFileSync as readFileSync52, statSync as statSync11 } from "fs";
|
|
28619
|
-
import { isAbsolute as isAbsolute2, join as
|
|
29268
|
+
import { isAbsolute as isAbsolute2, join as join73, resolve as resolve17 } from "path";
|
|
28620
29269
|
function resolveGitDir(cwd) {
|
|
28621
|
-
const dotGit =
|
|
29270
|
+
const dotGit = join73(cwd, ".git");
|
|
28622
29271
|
let stat2;
|
|
28623
29272
|
try {
|
|
28624
29273
|
stat2 = statSync11(dotGit);
|
|
@@ -28648,7 +29297,7 @@ function readGitBranch(cwd) {
|
|
|
28648
29297
|
}
|
|
28649
29298
|
let head;
|
|
28650
29299
|
try {
|
|
28651
|
-
head = readFileSync52(
|
|
29300
|
+
head = readFileSync52(join73(gitDir, "HEAD"), "utf8");
|
|
28652
29301
|
} catch {
|
|
28653
29302
|
return null;
|
|
28654
29303
|
}
|