@staff0rd/assist 0.411.0 → 0.412.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 +1 -1
- package/dist/commands/sessions/web/bundle.js +1 -1
- package/dist/index.js +744 -374
- 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.412.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -143,10 +143,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
143
143
|
// src/shared/loadRawYaml.ts
|
|
144
144
|
import { existsSync, readFileSync } from "fs";
|
|
145
145
|
import { parse as parseYaml } from "yaml";
|
|
146
|
-
function loadRawYaml(
|
|
147
|
-
if (!existsSync(
|
|
146
|
+
function loadRawYaml(path71) {
|
|
147
|
+
if (!existsSync(path71)) return {};
|
|
148
148
|
try {
|
|
149
|
-
const content = readFileSync(
|
|
149
|
+
const content = readFileSync(path71, "utf8");
|
|
150
150
|
return parseYaml(content) || {};
|
|
151
151
|
} catch {
|
|
152
152
|
return {};
|
|
@@ -1442,8 +1442,8 @@ function findUnmergedPaths() {
|
|
|
1442
1442
|
}
|
|
1443
1443
|
const found = /* @__PURE__ */ new Set();
|
|
1444
1444
|
for (const line of output.split("\n")) {
|
|
1445
|
-
const
|
|
1446
|
-
if (
|
|
1445
|
+
const path71 = line.split(" ")[1];
|
|
1446
|
+
if (path71) found.add(path71.trim());
|
|
1447
1447
|
}
|
|
1448
1448
|
return [...found];
|
|
1449
1449
|
}
|
|
@@ -1542,8 +1542,8 @@ function getPreferredRemoteRepo(cwd) {
|
|
|
1542
1542
|
function gitRefUrl(kind, ref, cwd) {
|
|
1543
1543
|
const repo = getPreferredRemoteRepo(cwd);
|
|
1544
1544
|
if (!repo) return void 0;
|
|
1545
|
-
const
|
|
1546
|
-
return `https://github.com/${repo.org}/${repo.repo}/${
|
|
1545
|
+
const path71 = kind === "branch" ? "tree" : "commit";
|
|
1546
|
+
return `https://github.com/${repo.org}/${repo.repo}/${path71}/${ref}`;
|
|
1547
1547
|
}
|
|
1548
1548
|
|
|
1549
1549
|
// src/commands/commit/collectCommitRefs.ts
|
|
@@ -3410,9 +3410,9 @@ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
|
3410
3410
|
|
|
3411
3411
|
// src/commands/verify/forbiddenStrings/findForbiddenStrings.ts
|
|
3412
3412
|
import { minimatch as minimatch2 } from "minimatch";
|
|
3413
|
-
function resolveStringsAtPath(data,
|
|
3413
|
+
function resolveStringsAtPath(data, path71) {
|
|
3414
3414
|
let current = data;
|
|
3415
|
-
for (const segment of
|
|
3415
|
+
for (const segment of path71.split(".")) {
|
|
3416
3416
|
if (current === null || typeof current !== "object") return [];
|
|
3417
3417
|
current = current[segment];
|
|
3418
3418
|
}
|
|
@@ -3425,10 +3425,10 @@ function resolveStringsAtPath(data, path70) {
|
|
|
3425
3425
|
}
|
|
3426
3426
|
function findRuleViolations(data, rule) {
|
|
3427
3427
|
const violations = [];
|
|
3428
|
-
for (const
|
|
3429
|
-
for (const value of resolveStringsAtPath(data,
|
|
3428
|
+
for (const path71 of rule.paths) {
|
|
3429
|
+
for (const value of resolveStringsAtPath(data, path71)) {
|
|
3430
3430
|
if (minimatch2(value, rule.disallowed))
|
|
3431
|
-
violations.push({ file: rule.file, path:
|
|
3431
|
+
violations.push({ file: rule.file, path: path71, value });
|
|
3432
3432
|
}
|
|
3433
3433
|
}
|
|
3434
3434
|
return violations;
|
|
@@ -3467,8 +3467,8 @@ function forbiddenStrings() {
|
|
|
3467
3467
|
process.exit(0);
|
|
3468
3468
|
}
|
|
3469
3469
|
console.log("Forbidden strings found:\n");
|
|
3470
|
-
for (const { file, path:
|
|
3471
|
-
console.log(` ${file} ${
|
|
3470
|
+
for (const { file, path: path71, value } of violations) {
|
|
3471
|
+
console.log(` ${file} ${path71}: ${value}`);
|
|
3472
3472
|
}
|
|
3473
3473
|
console.log(`
|
|
3474
3474
|
Total: ${violations.length} forbidden string(s).`);
|
|
@@ -3760,15 +3760,15 @@ function readBaselineMigrations(repoRelativeDir, ref) {
|
|
|
3760
3760
|
} catch {
|
|
3761
3761
|
return baseline;
|
|
3762
3762
|
}
|
|
3763
|
-
const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((
|
|
3764
|
-
for (const
|
|
3763
|
+
const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path71) => MIGRATION_FILE3.test(basename(path71)));
|
|
3764
|
+
for (const path71 of paths) {
|
|
3765
3765
|
try {
|
|
3766
|
-
const content = execSync12(`git show "${ref}:${
|
|
3766
|
+
const content = execSync12(`git show "${ref}:${path71}"`, {
|
|
3767
3767
|
encoding: "utf8",
|
|
3768
3768
|
maxBuffer: 16 * 1024 * 1024,
|
|
3769
3769
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3770
3770
|
});
|
|
3771
|
-
baseline.set(basename(
|
|
3771
|
+
baseline.set(basename(path71), content);
|
|
3772
3772
|
} catch {
|
|
3773
3773
|
}
|
|
3774
3774
|
}
|
|
@@ -5106,8 +5106,16 @@ var daemonPaths = {
|
|
|
5106
5106
|
};
|
|
5107
5107
|
|
|
5108
5108
|
// src/commands/sessions/daemon/ensureHooksSettings.ts
|
|
5109
|
-
var
|
|
5110
|
-
|
|
5109
|
+
var SET_STATUS = "assist sessions set-status";
|
|
5110
|
+
function running(source) {
|
|
5111
|
+
return `${SET_STATUS} running --source ${source}`;
|
|
5112
|
+
}
|
|
5113
|
+
function waiting(source) {
|
|
5114
|
+
return `${SET_STATUS} waiting --source ${source}`;
|
|
5115
|
+
}
|
|
5116
|
+
function ackWaiting(source) {
|
|
5117
|
+
return `${SET_STATUS} waiting --source ${source} --ack`;
|
|
5118
|
+
}
|
|
5111
5119
|
function on(command, matcher) {
|
|
5112
5120
|
const hooks = [{ type: "command", command }];
|
|
5113
5121
|
return [matcher == null ? { hooks } : { matcher, hooks }];
|
|
@@ -5116,27 +5124,22 @@ var ASK_USER_QUESTION = "AskUserQuestion";
|
|
|
5116
5124
|
var NOT_ASK_USER_QUESTION = `^(?!${ASK_USER_QUESTION}$).*`;
|
|
5117
5125
|
var hooksSettings = {
|
|
5118
5126
|
hooks: {
|
|
5119
|
-
UserPromptSubmit: on(
|
|
5127
|
+
UserPromptSubmit: on(running("prompt")),
|
|
5120
5128
|
PreToolUse: [
|
|
5121
|
-
...on(
|
|
5122
|
-
...on(
|
|
5129
|
+
...on(waiting("pretool"), ASK_USER_QUESTION),
|
|
5130
|
+
...on(running("pretool"), NOT_ASK_USER_QUESTION)
|
|
5123
5131
|
],
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
/* why: a tool/plan approval prompt appears mid-turn, after PreToolUse has
|
|
5129
|
-
* already set running; PermissionRequest fires when the agent blocks on
|
|
5130
|
-
* that decision, so the card shows waiting rather than staying running while
|
|
5131
|
-
* it is genuinely awaiting the user (#449). */
|
|
5132
|
-
PermissionRequest: on(WAITING)
|
|
5132
|
+
PostToolUse: on(running("posttool")),
|
|
5133
|
+
Stop: on(ackWaiting("stop")),
|
|
5134
|
+
Notification: on(ackWaiting("notification")),
|
|
5135
|
+
PermissionRequest: on(ackWaiting("permission"))
|
|
5133
5136
|
}
|
|
5134
5137
|
};
|
|
5135
5138
|
function ensureHooksSettings() {
|
|
5136
|
-
const
|
|
5137
|
-
mkdirSync5(dirname12(
|
|
5138
|
-
writeFileSync15(
|
|
5139
|
-
return
|
|
5139
|
+
const path71 = daemonPaths.hooksSettings;
|
|
5140
|
+
mkdirSync5(dirname12(path71), { recursive: true });
|
|
5141
|
+
writeFileSync15(path71, JSON.stringify(hooksSettings, null, 2));
|
|
5142
|
+
return path71;
|
|
5140
5143
|
}
|
|
5141
5144
|
|
|
5142
5145
|
// src/shared/spawnInherit.ts
|
|
@@ -5401,9 +5404,9 @@ var LOCAL_FILES = ["backlog.jsonl", "backlog.db"];
|
|
|
5401
5404
|
function backupLocalBacklogFiles(dir) {
|
|
5402
5405
|
const moved = [];
|
|
5403
5406
|
for (const name of LOCAL_FILES) {
|
|
5404
|
-
const
|
|
5405
|
-
if (existsSync18(
|
|
5406
|
-
renameSync(
|
|
5407
|
+
const path71 = join15(dir, ".assist", name);
|
|
5408
|
+
if (existsSync18(path71)) {
|
|
5409
|
+
renameSync(path71, `${path71}.bak`);
|
|
5407
5410
|
moved.push(`${name} \u2192 ${name}.bak`);
|
|
5408
5411
|
}
|
|
5409
5412
|
}
|
|
@@ -5875,8 +5878,8 @@ var backlogItemSchema = z3.strictObject({
|
|
|
5875
5878
|
var backlogFileSchema = z3.array(backlogItemSchema);
|
|
5876
5879
|
|
|
5877
5880
|
// src/commands/backlog/parseBacklogJsonl.ts
|
|
5878
|
-
function parseBacklogJsonl(
|
|
5879
|
-
const content = readFileSync14(
|
|
5881
|
+
function parseBacklogJsonl(path71) {
|
|
5882
|
+
const content = readFileSync14(path71, "utf8").trim();
|
|
5880
5883
|
if (content.length === 0) return [];
|
|
5881
5884
|
return content.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => backlogItemSchema.parse(JSON.parse(line)));
|
|
5882
5885
|
}
|
|
@@ -5958,8 +5961,8 @@ function findBacklogUp(startDir) {
|
|
|
5958
5961
|
|
|
5959
5962
|
// src/commands/backlog/getCurrentOrigin.ts
|
|
5960
5963
|
import { execFileSync } from "child_process";
|
|
5961
|
-
function stripLeadingSlashes(
|
|
5962
|
-
return
|
|
5964
|
+
function stripLeadingSlashes(path71) {
|
|
5965
|
+
return path71.replace(/^\/+/, "");
|
|
5963
5966
|
}
|
|
5964
5967
|
function normalizeOrigin(raw) {
|
|
5965
5968
|
const trimmed = raw.trim().replace(/\.git$/i, "").replace(/\/+$/, "");
|
|
@@ -6307,20 +6310,87 @@ function sendToDaemon(message) {
|
|
|
6307
6310
|
});
|
|
6308
6311
|
}
|
|
6309
6312
|
|
|
6313
|
+
// src/commands/sessions/daemon/sendToDaemonAwaitAck.ts
|
|
6314
|
+
var ACK_TIMEOUT_MS = 1e3;
|
|
6315
|
+
function sendToDaemonAwaitAck(message) {
|
|
6316
|
+
return new Promise((resolve19, reject) => {
|
|
6317
|
+
connectToDaemon().then((socket) => {
|
|
6318
|
+
let settled = false;
|
|
6319
|
+
const finish = (error) => {
|
|
6320
|
+
if (settled) return;
|
|
6321
|
+
settled = true;
|
|
6322
|
+
clearTimeout(timer);
|
|
6323
|
+
socket.destroy();
|
|
6324
|
+
if (error) reject(error);
|
|
6325
|
+
else resolve19();
|
|
6326
|
+
};
|
|
6327
|
+
const timer = setTimeout(
|
|
6328
|
+
() => finish(new Error("timed out awaiting daemon ack")),
|
|
6329
|
+
ACK_TIMEOUT_MS
|
|
6330
|
+
);
|
|
6331
|
+
let buffer = "";
|
|
6332
|
+
socket.on("data", (chunk) => {
|
|
6333
|
+
buffer += chunk.toString("utf8");
|
|
6334
|
+
let newline = buffer.indexOf("\n");
|
|
6335
|
+
while (newline !== -1) {
|
|
6336
|
+
const line = buffer.slice(0, newline);
|
|
6337
|
+
buffer = buffer.slice(newline + 1);
|
|
6338
|
+
if (isAck(line)) return finish();
|
|
6339
|
+
newline = buffer.indexOf("\n");
|
|
6340
|
+
}
|
|
6341
|
+
});
|
|
6342
|
+
socket.on("error", (error) => finish(error));
|
|
6343
|
+
socket.on(
|
|
6344
|
+
"close",
|
|
6345
|
+
() => finish(new Error("daemon closed before acknowledging"))
|
|
6346
|
+
);
|
|
6347
|
+
socket.write(`${JSON.stringify(message)}
|
|
6348
|
+
`);
|
|
6349
|
+
}, reject);
|
|
6350
|
+
});
|
|
6351
|
+
}
|
|
6352
|
+
function isAck(line) {
|
|
6353
|
+
try {
|
|
6354
|
+
return JSON.parse(line).type === "ack";
|
|
6355
|
+
} catch {
|
|
6356
|
+
return false;
|
|
6357
|
+
}
|
|
6358
|
+
}
|
|
6359
|
+
|
|
6310
6360
|
// src/commands/sessions/setSessionStatus.ts
|
|
6311
6361
|
var DELIVERY_ATTEMPTS = 3;
|
|
6312
6362
|
var RETRY_DELAY_MS = 100;
|
|
6313
|
-
async function setSessionStatus(status3) {
|
|
6363
|
+
async function setSessionStatus(status3, opts = {}) {
|
|
6314
6364
|
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
6315
|
-
logHookFired(status3, sessionId);
|
|
6365
|
+
logHookFired(status3, sessionId, opts.source);
|
|
6316
6366
|
if (!sessionId) return;
|
|
6317
|
-
|
|
6367
|
+
const payload = buildPayload(sessionId, status3, opts);
|
|
6368
|
+
if (opts.ack) await deliverReliably(sessionId, status3, payload);
|
|
6369
|
+
else await deliverBestEffort(sessionId, status3, payload);
|
|
6318
6370
|
}
|
|
6319
|
-
|
|
6320
|
-
const payload = {
|
|
6371
|
+
function buildPayload(sessionId, status3, opts) {
|
|
6372
|
+
const payload = {
|
|
6373
|
+
type: "set-status",
|
|
6374
|
+
sessionId,
|
|
6375
|
+
status: status3
|
|
6376
|
+
};
|
|
6377
|
+
if (opts.source) payload.source = opts.source;
|
|
6378
|
+
if (opts.ack) payload.ack = true;
|
|
6379
|
+
return payload;
|
|
6380
|
+
}
|
|
6381
|
+
async function deliverBestEffort(sessionId, status3, payload) {
|
|
6382
|
+
try {
|
|
6383
|
+
await sendToDaemon(payload);
|
|
6384
|
+
} catch (error) {
|
|
6385
|
+
appendDaemonLog(
|
|
6386
|
+
`set-status best-effort send failed: id=${sessionId} status=${status3}: ${describeError(error)}`
|
|
6387
|
+
);
|
|
6388
|
+
}
|
|
6389
|
+
}
|
|
6390
|
+
async function deliverReliably(sessionId, status3, payload) {
|
|
6321
6391
|
for (let attempt = 1; attempt <= DELIVERY_ATTEMPTS; attempt++) {
|
|
6322
6392
|
try {
|
|
6323
|
-
await
|
|
6393
|
+
await sendToDaemonAwaitAck(payload);
|
|
6324
6394
|
return;
|
|
6325
6395
|
} catch (error) {
|
|
6326
6396
|
if (attempt < DELIVERY_ATTEMPTS) {
|
|
@@ -6328,7 +6398,7 @@ async function deliverStatus(sessionId, status3) {
|
|
|
6328
6398
|
continue;
|
|
6329
6399
|
}
|
|
6330
6400
|
appendDaemonLog(
|
|
6331
|
-
`set-status delivery failed after ${DELIVERY_ATTEMPTS} attempts: id=${sessionId} status=${status3}: ${describeError(error)}`
|
|
6401
|
+
`set-status ack'd delivery failed after ${DELIVERY_ATTEMPTS} attempts: id=${sessionId} status=${status3}: ${describeError(error)}`
|
|
6332
6402
|
);
|
|
6333
6403
|
}
|
|
6334
6404
|
}
|
|
@@ -6339,9 +6409,9 @@ function sleep(ms) {
|
|
|
6339
6409
|
function describeError(error) {
|
|
6340
6410
|
return error instanceof Error ? error.message : String(error);
|
|
6341
6411
|
}
|
|
6342
|
-
function logHookFired(status3, sessionId) {
|
|
6412
|
+
function logHookFired(status3, sessionId, source) {
|
|
6343
6413
|
appendDaemonLog(
|
|
6344
|
-
`set-status hook fired: id=${sessionId ?? "none"} status=${status3}`
|
|
6414
|
+
`set-status hook fired: id=${sessionId ?? "none"} status=${status3}${source ? ` source=${source}` : ""}`
|
|
6345
6415
|
);
|
|
6346
6416
|
}
|
|
6347
6417
|
|
|
@@ -6499,24 +6569,24 @@ function getOwnerPath(itemId2) {
|
|
|
6499
6569
|
function recordSignalOwner(itemId2) {
|
|
6500
6570
|
const sessionId = process.env.ASSIST_SESSION_ID;
|
|
6501
6571
|
if (!sessionId) return;
|
|
6502
|
-
const
|
|
6503
|
-
mkdirSync7(dirname14(
|
|
6504
|
-
writeFileSync17(
|
|
6572
|
+
const path71 = getOwnerPath(itemId2);
|
|
6573
|
+
mkdirSync7(dirname14(path71), { recursive: true });
|
|
6574
|
+
writeFileSync17(path71, JSON.stringify({ sessionId }));
|
|
6505
6575
|
}
|
|
6506
6576
|
function readSignalOwner(itemId2) {
|
|
6507
|
-
const
|
|
6508
|
-
if (!existsSync21(
|
|
6577
|
+
const path71 = getOwnerPath(itemId2);
|
|
6578
|
+
if (!existsSync21(path71)) return void 0;
|
|
6509
6579
|
try {
|
|
6510
|
-
const parsed = JSON.parse(readFileSync15(
|
|
6580
|
+
const parsed = JSON.parse(readFileSync15(path71, "utf8"));
|
|
6511
6581
|
return parsed.sessionId;
|
|
6512
6582
|
} catch {
|
|
6513
6583
|
return void 0;
|
|
6514
6584
|
}
|
|
6515
6585
|
}
|
|
6516
6586
|
function clearSignalOwner(itemId2) {
|
|
6517
|
-
const
|
|
6587
|
+
const path71 = getOwnerPath(itemId2);
|
|
6518
6588
|
try {
|
|
6519
|
-
rmSync(
|
|
6589
|
+
rmSync(path71);
|
|
6520
6590
|
} catch {
|
|
6521
6591
|
}
|
|
6522
6592
|
}
|
|
@@ -6553,19 +6623,19 @@ function resolveSignalTarget(event, data) {
|
|
|
6553
6623
|
function writeSignal(event, data) {
|
|
6554
6624
|
const target = resolveSignalTarget(event, data);
|
|
6555
6625
|
if (!target) return;
|
|
6556
|
-
const
|
|
6557
|
-
if (!
|
|
6626
|
+
const path71 = getSignalPath(target);
|
|
6627
|
+
if (!path71) return;
|
|
6558
6628
|
const signal = { event, sessionId: target, ...data };
|
|
6559
|
-
mkdirSync8(dirname15(
|
|
6560
|
-
writeFileSync18(
|
|
6629
|
+
mkdirSync8(dirname15(path71), { recursive: true });
|
|
6630
|
+
writeFileSync18(path71, JSON.stringify(signal));
|
|
6561
6631
|
}
|
|
6562
6632
|
|
|
6563
6633
|
// src/commands/backlog/readSignal.ts
|
|
6564
6634
|
function readSignal() {
|
|
6565
|
-
const
|
|
6566
|
-
if (!
|
|
6635
|
+
const path71 = getSignalPath();
|
|
6636
|
+
if (!path71 || !existsSync22(path71)) return void 0;
|
|
6567
6637
|
try {
|
|
6568
|
-
return JSON.parse(readFileSync16(
|
|
6638
|
+
return JSON.parse(readFileSync16(path71, "utf8"));
|
|
6569
6639
|
} catch {
|
|
6570
6640
|
return void 0;
|
|
6571
6641
|
}
|
|
@@ -6698,13 +6768,13 @@ function activityPath(sessionId) {
|
|
|
6698
6768
|
function emitActivity(activity2) {
|
|
6699
6769
|
const sessionId = process.env.ASSIST_ACTIVITY_ID;
|
|
6700
6770
|
if (!sessionId) return;
|
|
6701
|
-
const
|
|
6702
|
-
mkdirSync9(dirname16(
|
|
6703
|
-
writeFileSync19(
|
|
6771
|
+
const path71 = activityPath(sessionId);
|
|
6772
|
+
mkdirSync9(dirname16(path71), { recursive: true });
|
|
6773
|
+
writeFileSync19(path71, JSON.stringify({ ...activity2, startedAt: Date.now() }));
|
|
6704
6774
|
}
|
|
6705
|
-
function readActivity(
|
|
6775
|
+
function readActivity(path71) {
|
|
6706
6776
|
try {
|
|
6707
|
-
return JSON.parse(readFileSync17(
|
|
6777
|
+
return JSON.parse(readFileSync17(path71, "utf8"));
|
|
6708
6778
|
} catch {
|
|
6709
6779
|
return void 0;
|
|
6710
6780
|
}
|
|
@@ -6714,9 +6784,9 @@ function reconcileActivity(sessionId, activity2) {
|
|
|
6714
6784
|
removeActivity(sessionId);
|
|
6715
6785
|
return;
|
|
6716
6786
|
}
|
|
6717
|
-
const
|
|
6718
|
-
mkdirSync9(dirname16(
|
|
6719
|
-
writeFileSync19(
|
|
6787
|
+
const path71 = activityPath(sessionId);
|
|
6788
|
+
mkdirSync9(dirname16(path71), { recursive: true });
|
|
6789
|
+
writeFileSync19(path71, JSON.stringify(activity2));
|
|
6720
6790
|
}
|
|
6721
6791
|
function removeActivity(sessionId) {
|
|
6722
6792
|
try {
|
|
@@ -7969,8 +8039,8 @@ async function loadItemSummaries(orm, origin) {
|
|
|
7969
8039
|
// src/commands/backlog/originDisplayName.ts
|
|
7970
8040
|
function originDisplayName(origin) {
|
|
7971
8041
|
if (origin.startsWith("local:")) {
|
|
7972
|
-
const
|
|
7973
|
-
const segments =
|
|
8042
|
+
const path71 = origin.slice("local:".length).replace(/\/+$/, "");
|
|
8043
|
+
const segments = path71.split("/").filter(Boolean);
|
|
7974
8044
|
return segments[segments.length - 1] ?? origin;
|
|
7975
8045
|
}
|
|
7976
8046
|
const firstSlash = origin.indexOf("/");
|
|
@@ -8406,7 +8476,7 @@ function findSynthesisForBranch(repoReviewsDir, branch2) {
|
|
|
8406
8476
|
const parent = dirname20(branchKeyPath);
|
|
8407
8477
|
const branchPrefix = basename5(branchKeyPath);
|
|
8408
8478
|
if (!existsSync26(parent)) return null;
|
|
8409
|
-
const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join24(parent, name, "synthesis.md")).filter((
|
|
8479
|
+
const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join24(parent, name, "synthesis.md")).filter((path71) => existsSync26(path71)).map((path71) => ({ path: path71, mtime: statSync2(path71).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
8410
8480
|
return synthesisFiles[0]?.path ?? null;
|
|
8411
8481
|
}
|
|
8412
8482
|
|
|
@@ -8460,12 +8530,12 @@ async function getReviewSynthesis(req, res) {
|
|
|
8460
8530
|
const cwd = getCwdParam(req, res);
|
|
8461
8531
|
if (!cwd) return;
|
|
8462
8532
|
try {
|
|
8463
|
-
const
|
|
8464
|
-
if (!
|
|
8533
|
+
const path71 = await resolveSynthesisPath(cwd);
|
|
8534
|
+
if (!path71) {
|
|
8465
8535
|
respondJson(res, 404, { error: "No synthesis found" });
|
|
8466
8536
|
return;
|
|
8467
8537
|
}
|
|
8468
|
-
respondJson(res, 200, { synthesis: readFileSync19(
|
|
8538
|
+
respondJson(res, 200, { synthesis: readFileSync19(path71, "utf8") });
|
|
8469
8539
|
} catch {
|
|
8470
8540
|
respondJson(res, 404, { error: "No synthesis found" });
|
|
8471
8541
|
}
|
|
@@ -8612,10 +8682,10 @@ function getStorePath(filename) {
|
|
|
8612
8682
|
return join27(getStoreDir(), filename);
|
|
8613
8683
|
}
|
|
8614
8684
|
function loadJson(filename) {
|
|
8615
|
-
const
|
|
8616
|
-
if (existsSync27(
|
|
8685
|
+
const path71 = getStorePath(filename);
|
|
8686
|
+
if (existsSync27(path71)) {
|
|
8617
8687
|
try {
|
|
8618
|
-
return JSON.parse(readFileSync20(
|
|
8688
|
+
return JSON.parse(readFileSync20(path71, "utf8"));
|
|
8619
8689
|
} catch {
|
|
8620
8690
|
return {};
|
|
8621
8691
|
}
|
|
@@ -9725,9 +9795,9 @@ function originToSshUrl(origin) {
|
|
|
9725
9795
|
const firstSlash = origin.indexOf("/");
|
|
9726
9796
|
if (firstSlash <= 0) return null;
|
|
9727
9797
|
const host = origin.slice(0, firstSlash);
|
|
9728
|
-
const
|
|
9729
|
-
if (!
|
|
9730
|
-
return `git@${host}:${
|
|
9798
|
+
const path71 = origin.slice(firstSlash + 1);
|
|
9799
|
+
if (!path71) return null;
|
|
9800
|
+
return `git@${host}:${path71}.git`;
|
|
9731
9801
|
}
|
|
9732
9802
|
|
|
9733
9803
|
// src/commands/backlog/cloneRepo.ts
|
|
@@ -12036,9 +12106,9 @@ var __dirname4 = dirname21(__filename3);
|
|
|
12036
12106
|
function packageRoot() {
|
|
12037
12107
|
return __dirname4;
|
|
12038
12108
|
}
|
|
12039
|
-
function readLines(
|
|
12040
|
-
if (!existsSync29(
|
|
12041
|
-
return readFileSync23(
|
|
12109
|
+
function readLines(path71) {
|
|
12110
|
+
if (!existsSync29(path71)) return [];
|
|
12111
|
+
return readFileSync23(path71, "utf8").split("\n").filter((line) => line.trim() !== "");
|
|
12042
12112
|
}
|
|
12043
12113
|
var cachedReads;
|
|
12044
12114
|
var cachedWrites;
|
|
@@ -12530,14 +12600,14 @@ function showProgress(p, label2) {
|
|
|
12530
12600
|
const pct = Math.round(p.done / p.total * 100);
|
|
12531
12601
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
12532
12602
|
}
|
|
12533
|
-
async function resolveCommand(cli,
|
|
12534
|
-
showProgress(p,
|
|
12535
|
-
const subHelp = await runHelp([cli, ...
|
|
12603
|
+
async function resolveCommand(cli, path71, description, depth, p) {
|
|
12604
|
+
showProgress(p, path71.join(" "));
|
|
12605
|
+
const subHelp = await runHelp([cli, ...path71]);
|
|
12536
12606
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
12537
|
-
return [{ path:
|
|
12607
|
+
return [{ path: path71, description }];
|
|
12538
12608
|
}
|
|
12539
|
-
const children = await discoverAt(cli,
|
|
12540
|
-
return children.length > 0 ? children : [{ path:
|
|
12609
|
+
const children = await discoverAt(cli, path71, depth + 1, p);
|
|
12610
|
+
return children.length > 0 ? children : [{ path: path71, description }];
|
|
12541
12611
|
}
|
|
12542
12612
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
12543
12613
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -12685,9 +12755,9 @@ function logPath(cli) {
|
|
|
12685
12755
|
return join31(homedir16(), ".assist", `cli-discover-${safeName}.log`);
|
|
12686
12756
|
}
|
|
12687
12757
|
function readCache(cli) {
|
|
12688
|
-
const
|
|
12689
|
-
if (!existsSync31(
|
|
12690
|
-
return readFileSync25(
|
|
12758
|
+
const path71 = logPath(cli);
|
|
12759
|
+
if (!existsSync31(path71)) return void 0;
|
|
12760
|
+
return readFileSync25(path71, "utf8");
|
|
12691
12761
|
}
|
|
12692
12762
|
function writeCache(cli, output) {
|
|
12693
12763
|
const dir = join31(homedir16(), ".assist");
|
|
@@ -12859,9 +12929,9 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
|
|
|
12859
12929
|
}
|
|
12860
12930
|
const cutoff = Date.now() - STALE_AFTER_MS;
|
|
12861
12931
|
for (const entry of entries) {
|
|
12862
|
-
const
|
|
12932
|
+
const path71 = join33(dir, entry);
|
|
12863
12933
|
try {
|
|
12864
|
-
if (statSync3(
|
|
12934
|
+
if (statSync3(path71).mtimeMs < cutoff) unlinkSync7(path71);
|
|
12865
12935
|
} catch {
|
|
12866
12936
|
continue;
|
|
12867
12937
|
}
|
|
@@ -12871,10 +12941,10 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
|
|
|
12871
12941
|
// src/commands/codeComment/readPinState.ts
|
|
12872
12942
|
import { existsSync as existsSync32, readFileSync as readFileSync26 } from "fs";
|
|
12873
12943
|
function readPinState(pin) {
|
|
12874
|
-
const
|
|
12875
|
-
if (!existsSync32(
|
|
12944
|
+
const path71 = getPinStatePath(pin);
|
|
12945
|
+
if (!existsSync32(path71)) return void 0;
|
|
12876
12946
|
try {
|
|
12877
|
-
const state = JSON.parse(readFileSync26(
|
|
12947
|
+
const state = JSON.parse(readFileSync26(path71, "utf8"));
|
|
12878
12948
|
if (state.pin !== pin) return void 0;
|
|
12879
12949
|
return state;
|
|
12880
12950
|
} catch {
|
|
@@ -13770,8 +13840,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
13770
13840
|
}
|
|
13771
13841
|
return ensureObject(container, resolved);
|
|
13772
13842
|
}
|
|
13773
|
-
function setNestedValue(obj,
|
|
13774
|
-
const keys =
|
|
13843
|
+
function setNestedValue(obj, path71, value) {
|
|
13844
|
+
const keys = path71.split(".");
|
|
13775
13845
|
const result = { ...obj };
|
|
13776
13846
|
let current = result;
|
|
13777
13847
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -13851,9 +13921,9 @@ function isTraversable(value) {
|
|
|
13851
13921
|
function stepInto(current, key) {
|
|
13852
13922
|
return isTraversable(current) ? current[key] : void 0;
|
|
13853
13923
|
}
|
|
13854
|
-
function getNestedValue(obj,
|
|
13924
|
+
function getNestedValue(obj, path71) {
|
|
13855
13925
|
let current = obj;
|
|
13856
|
-
for (const key of
|
|
13926
|
+
for (const key of path71.split(".")) current = stepInto(current, key);
|
|
13857
13927
|
return current;
|
|
13858
13928
|
}
|
|
13859
13929
|
|
|
@@ -13969,10 +14039,10 @@ function getMigrationApprovalPath(migrationId) {
|
|
|
13969
14039
|
// src/commands/dbMigration/readMigrationPinState.ts
|
|
13970
14040
|
import { existsSync as existsSync34, readFileSync as readFileSync28 } from "fs";
|
|
13971
14041
|
function readMigrationPinState(pin) {
|
|
13972
|
-
const
|
|
13973
|
-
if (!existsSync34(
|
|
14042
|
+
const path71 = getMigrationPinPath(pin);
|
|
14043
|
+
if (!existsSync34(path71)) return void 0;
|
|
13974
14044
|
try {
|
|
13975
|
-
const state = JSON.parse(readFileSync28(
|
|
14045
|
+
const state = JSON.parse(readFileSync28(path71, "utf8"));
|
|
13976
14046
|
if (state.pin !== pin) return void 0;
|
|
13977
14047
|
if (!Number.isInteger(state.migrationId)) return void 0;
|
|
13978
14048
|
return state;
|
|
@@ -15411,10 +15481,10 @@ function decideCommentGuard(input, existingContent) {
|
|
|
15411
15481
|
import { existsSync as existsSync41, unlinkSync as unlinkSync11 } from "fs";
|
|
15412
15482
|
function consumeMigrationApproval(migrationId) {
|
|
15413
15483
|
sweepRestrictedDir();
|
|
15414
|
-
const
|
|
15415
|
-
if (!existsSync41(
|
|
15484
|
+
const path71 = getMigrationApprovalPath(migrationId);
|
|
15485
|
+
if (!existsSync41(path71)) return false;
|
|
15416
15486
|
try {
|
|
15417
|
-
unlinkSync11(
|
|
15487
|
+
unlinkSync11(path71);
|
|
15418
15488
|
return true;
|
|
15419
15489
|
} catch {
|
|
15420
15490
|
return false;
|
|
@@ -16588,9 +16658,9 @@ function parseRscRows(flight) {
|
|
|
16588
16658
|
}
|
|
16589
16659
|
function makeRscResolver(rows) {
|
|
16590
16660
|
return (ref) => {
|
|
16591
|
-
const [id, ...
|
|
16661
|
+
const [id, ...path71] = ref.slice(1).replace(/^[L@]/, "").split(":");
|
|
16592
16662
|
let v = rows[id];
|
|
16593
|
-
for (const key of
|
|
16663
|
+
for (const key of path71) {
|
|
16594
16664
|
if (v == null || typeof v !== "object") return void 0;
|
|
16595
16665
|
v = v[key];
|
|
16596
16666
|
}
|
|
@@ -17274,15 +17344,15 @@ function postComment(vars) {
|
|
|
17274
17344
|
const stdout = startLine === void 0 ? runGhGraphql(MUTATION_SINGLE, base) : runGhGraphql(MUTATION_MULTI, { ...base, startLine });
|
|
17275
17345
|
assertThreadCreated(stdout);
|
|
17276
17346
|
}
|
|
17277
|
-
function comment2(
|
|
17347
|
+
function comment2(path71, line, body, startLine) {
|
|
17278
17348
|
validateBody(body);
|
|
17279
17349
|
validateLine(line);
|
|
17280
17350
|
if (startLine !== void 0) validateLine(startLine);
|
|
17281
17351
|
try {
|
|
17282
17352
|
const prId = getCurrentPrNodeId();
|
|
17283
|
-
postComment({ prId, body, path:
|
|
17353
|
+
postComment({ prId, body, path: path71, line, startLine });
|
|
17284
17354
|
const range = startLine !== void 0 ? `${startLine}-${line}` : `${line}`;
|
|
17285
|
-
console.log(`Added review comment on ${
|
|
17355
|
+
console.log(`Added review comment on ${path71}:${range}`);
|
|
17286
17356
|
} catch (error) {
|
|
17287
17357
|
if (isGhNotInstalled(error)) {
|
|
17288
17358
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
@@ -18161,8 +18231,8 @@ function registerPrs(program2) {
|
|
|
18161
18231
|
prsCommand.command("reply <comment-id> <body>").description("Reply to a comment thread without resolving it").action((commentId, body) => {
|
|
18162
18232
|
reply(Number.parseInt(commentId, 10), body);
|
|
18163
18233
|
});
|
|
18164
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
18165
|
-
comment2(
|
|
18234
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path71, line, body) => {
|
|
18235
|
+
comment2(path71, Number.parseInt(line, 10), body);
|
|
18166
18236
|
});
|
|
18167
18237
|
configHelp(prsCommand, prsConfigHelp);
|
|
18168
18238
|
}
|
|
@@ -18415,10 +18485,10 @@ function resolveOpSecret(reference) {
|
|
|
18415
18485
|
}
|
|
18416
18486
|
|
|
18417
18487
|
// src/commands/ravendb/ravenFetch.ts
|
|
18418
|
-
async function ravenFetch(connection,
|
|
18488
|
+
async function ravenFetch(connection, path71) {
|
|
18419
18489
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
18420
18490
|
let accessToken = await getAccessToken(apiKey);
|
|
18421
|
-
const url = `${connection.url}${
|
|
18491
|
+
const url = `${connection.url}${path71}`;
|
|
18422
18492
|
const headers = {
|
|
18423
18493
|
Authorization: `Bearer ${accessToken}`,
|
|
18424
18494
|
"Content-Type": "application/json"
|
|
@@ -18522,16 +18592,16 @@ import chalk170 from "chalk";
|
|
|
18522
18592
|
// src/commands/ravendb/buildQueryPath.ts
|
|
18523
18593
|
function buildQueryPath(opts) {
|
|
18524
18594
|
const db = encodeURIComponent(opts.db);
|
|
18525
|
-
let
|
|
18595
|
+
let path71;
|
|
18526
18596
|
if (opts.collection) {
|
|
18527
|
-
|
|
18597
|
+
path71 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
18528
18598
|
} else {
|
|
18529
|
-
|
|
18599
|
+
path71 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
18530
18600
|
}
|
|
18531
18601
|
if (opts.query) {
|
|
18532
|
-
|
|
18602
|
+
path71 += `&query=${encodeURIComponent(opts.query)}`;
|
|
18533
18603
|
}
|
|
18534
|
-
return
|
|
18604
|
+
return path71;
|
|
18535
18605
|
}
|
|
18536
18606
|
|
|
18537
18607
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -18540,7 +18610,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
18540
18610
|
let start3 = 0;
|
|
18541
18611
|
while (true) {
|
|
18542
18612
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
18543
|
-
const
|
|
18613
|
+
const path71 = buildQueryPath({
|
|
18544
18614
|
db: connection.database,
|
|
18545
18615
|
collection: opts.collection,
|
|
18546
18616
|
start: start3,
|
|
@@ -18548,7 +18618,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
18548
18618
|
sort: opts.sort,
|
|
18549
18619
|
query: opts.query
|
|
18550
18620
|
});
|
|
18551
|
-
const data = await ravenFetch(connection,
|
|
18621
|
+
const data = await ravenFetch(connection, path71);
|
|
18552
18622
|
const results = data.Results ?? [];
|
|
18553
18623
|
const totalResults = data.TotalResults ?? 0;
|
|
18554
18624
|
if (results.length === 0) break;
|
|
@@ -20968,8 +21038,8 @@ async function handlePostSynthesis(synthesisPath, options2) {
|
|
|
20968
21038
|
// src/commands/review/prepareReviewDir.ts
|
|
20969
21039
|
import { existsSync as existsSync45, mkdirSync as mkdirSync18, unlinkSync as unlinkSync16, writeFileSync as writeFileSync36 } from "fs";
|
|
20970
21040
|
function clearReviewFiles(paths) {
|
|
20971
|
-
for (const
|
|
20972
|
-
if (existsSync45(
|
|
21041
|
+
for (const path71 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
|
|
21042
|
+
if (existsSync45(path71)) unlinkSync16(path71);
|
|
20973
21043
|
}
|
|
20974
21044
|
}
|
|
20975
21045
|
function prepareReviewDir(paths, requestBody, force) {
|
|
@@ -22117,8 +22187,8 @@ import chalk191 from "chalk";
|
|
|
22117
22187
|
|
|
22118
22188
|
// src/commands/seq/fetchSeq.ts
|
|
22119
22189
|
import chalk188 from "chalk";
|
|
22120
|
-
async function fetchSeq(conn,
|
|
22121
|
-
const url = `${conn.url}${
|
|
22190
|
+
async function fetchSeq(conn, path71, params) {
|
|
22191
|
+
const url = `${conn.url}${path71}?${params}`;
|
|
22122
22192
|
const response = await fetch(url, {
|
|
22123
22193
|
headers: {
|
|
22124
22194
|
Accept: "application/json",
|
|
@@ -23605,9 +23675,9 @@ function findPortFile(roamDir) {
|
|
|
23605
23675
|
return void 0;
|
|
23606
23676
|
}
|
|
23607
23677
|
const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
|
|
23608
|
-
const
|
|
23678
|
+
const path71 = join62(roamDir, name);
|
|
23609
23679
|
try {
|
|
23610
|
-
return { path:
|
|
23680
|
+
return { path: path71, mtimeMs: statSync8(path71).mtimeMs };
|
|
23611
23681
|
} catch {
|
|
23612
23682
|
return void 0;
|
|
23613
23683
|
}
|
|
@@ -24026,11 +24096,11 @@ function findLinkIndex() {
|
|
|
24026
24096
|
function parseLinkArgs() {
|
|
24027
24097
|
const idx = findLinkIndex();
|
|
24028
24098
|
if (idx === -1) return null;
|
|
24029
|
-
const
|
|
24099
|
+
const path71 = process.argv[idx + 1];
|
|
24030
24100
|
const rest = process.argv.slice(idx + 2);
|
|
24031
24101
|
const { value: prefix2 } = extractOption(rest, "--prefix");
|
|
24032
24102
|
if (!prefix2) return null;
|
|
24033
|
-
return { path:
|
|
24103
|
+
return { path: path71, prefix: prefix2 };
|
|
24034
24104
|
}
|
|
24035
24105
|
function hasDuplicateLink(runList, linkPath) {
|
|
24036
24106
|
return runList.some(
|
|
@@ -24911,6 +24981,7 @@ function dismissSession(sessions, id) {
|
|
|
24911
24981
|
if (!s) return false;
|
|
24912
24982
|
if (s.status !== "done") s.pty?.kill();
|
|
24913
24983
|
s.activityWatcher?.close();
|
|
24984
|
+
s.transcriptWatcher?.close();
|
|
24914
24985
|
removeActivity(s.id);
|
|
24915
24986
|
if (s.activity?.itemId != null) releaseLock(s.activity.itemId);
|
|
24916
24987
|
sessions.delete(id);
|
|
@@ -25017,113 +25088,18 @@ function logSpawnedSession(session) {
|
|
|
25017
25088
|
);
|
|
25018
25089
|
}
|
|
25019
25090
|
|
|
25020
|
-
// src/commands/sessions/daemon/
|
|
25021
|
-
function
|
|
25022
|
-
const
|
|
25023
|
-
if (session
|
|
25024
|
-
session.runningMs += now - session.runningSince;
|
|
25025
|
-
}
|
|
25026
|
-
session.runningSince = newStatus === "running" ? now : null;
|
|
25027
|
-
session.status = newStatus;
|
|
25028
|
-
}
|
|
25029
|
-
|
|
25030
|
-
// src/commands/sessions/daemon/shouldAutoDismiss.ts
|
|
25031
|
-
function shouldAutoDismiss(session, exitCode) {
|
|
25032
|
-
if (session.status !== "done" || exitCode !== 0) {
|
|
25033
|
-
return false;
|
|
25034
|
-
}
|
|
25035
|
-
if (session.reviewStarted) {
|
|
25036
|
-
return session.autoAdvance === true;
|
|
25037
|
-
}
|
|
25038
|
-
const args = session.assistArgs;
|
|
25039
|
-
if (args === void 0) return false;
|
|
25040
|
-
if (args[0] === "update") return true;
|
|
25041
|
-
return args.includes("--once") && args[0] !== "next";
|
|
25042
|
-
}
|
|
25043
|
-
|
|
25044
|
-
// src/commands/sessions/daemon/shouldAutoRun.ts
|
|
25045
|
-
function shouldAutoRun(session) {
|
|
25046
|
-
if (!session.autoRun) return { run: false, reason: null };
|
|
25047
|
-
if (session.status !== "done")
|
|
25048
|
-
return {
|
|
25049
|
-
run: false,
|
|
25050
|
-
reason: `session status is "${session.status}", not "done"`
|
|
25051
|
-
};
|
|
25052
|
-
if (session.commandType !== "assist")
|
|
25053
|
-
return {
|
|
25054
|
-
run: false,
|
|
25055
|
-
reason: `command type is "${session.commandType}", not "assist"`
|
|
25056
|
-
};
|
|
25057
|
-
const cmd = session.assistArgs?.[0];
|
|
25058
|
-
if (cmd !== "draft" && cmd !== "bug" && cmd !== "refine")
|
|
25059
|
-
return {
|
|
25060
|
-
run: false,
|
|
25061
|
-
reason: `command "${cmd ?? "(none)"}" is not draft/bug/refine`
|
|
25062
|
-
};
|
|
25063
|
-
const itemId2 = session.activity?.itemId;
|
|
25064
|
-
if (itemId2 == null)
|
|
25065
|
-
return {
|
|
25066
|
-
run: false,
|
|
25067
|
-
reason: "no itemId in activity (draft did not create an item)"
|
|
25068
|
-
};
|
|
25069
|
-
return { run: true, itemId: itemId2 };
|
|
25070
|
-
}
|
|
25071
|
-
|
|
25072
|
-
// src/commands/sessions/daemon/watchEscInterrupt.ts
|
|
25073
|
-
var ESC3 = "\x1B";
|
|
25074
|
-
var SETTLE_MS = 1e3;
|
|
25075
|
-
function isInterruptKey(data) {
|
|
25076
|
-
return data === ESC3;
|
|
25077
|
-
}
|
|
25078
|
-
function scheduleSettle(session, onStatusChange) {
|
|
25079
|
-
if (session.escInterruptTimer) clearTimeout(session.escInterruptTimer);
|
|
25080
|
-
session.escInterruptTimer = setTimeout(() => {
|
|
25081
|
-
session.escInterruptTimer = void 0;
|
|
25082
|
-
if (session.status !== "running") return;
|
|
25083
|
-
daemonLog(`session ${session.id} esc-interrupt settled -> waiting`);
|
|
25084
|
-
onStatusChange(session, "waiting");
|
|
25085
|
-
}, SETTLE_MS);
|
|
25086
|
-
}
|
|
25087
|
-
function watchEscInterrupt(session, data, onStatusChange) {
|
|
25088
|
-
if (session.status !== "running") return;
|
|
25089
|
-
if (!isInterruptKey(data)) return;
|
|
25090
|
-
scheduleSettle(session, onStatusChange);
|
|
25091
|
-
}
|
|
25092
|
-
function noteOutputForEscInterrupt(session, onStatusChange) {
|
|
25093
|
-
if (!session.escInterruptTimer) return;
|
|
25094
|
-
scheduleSettle(session, onStatusChange);
|
|
25095
|
-
}
|
|
25096
|
-
function disarmEscInterrupt(session) {
|
|
25097
|
-
if (!session.escInterruptTimer) return;
|
|
25098
|
-
clearTimeout(session.escInterruptTimer);
|
|
25099
|
-
session.escInterruptTimer = void 0;
|
|
25100
|
-
}
|
|
25101
|
-
|
|
25102
|
-
// src/commands/sessions/daemon/applyStatusChange.ts
|
|
25103
|
-
function applyStatusChange(session, status3, exitCode, dismiss, notify2, reuseForRun) {
|
|
25104
|
-
disarmEscInterrupt(session);
|
|
25105
|
-
if (session.status === status3) return;
|
|
25106
|
-
daemonLog(`session ${session.id} status: ${session.status} -> ${status3}`);
|
|
25107
|
-
void flushPhaseActiveMs(session);
|
|
25108
|
-
setStatus2(session, status3);
|
|
25109
|
-
const autoRun = shouldAutoRun(session);
|
|
25110
|
-
if (autoRun.run) {
|
|
25111
|
-
reuseForRun(session, autoRun.itemId);
|
|
25112
|
-
notify2();
|
|
25113
|
-
return;
|
|
25114
|
-
}
|
|
25115
|
-
if (status3 === "done" && autoRun.reason != null) {
|
|
25091
|
+
// src/commands/sessions/daemon/applySetStatus.ts
|
|
25092
|
+
function applySetStatus(sessions, id, status3, source, onStatusChange) {
|
|
25093
|
+
const session = sessions.get(id);
|
|
25094
|
+
if (!session) {
|
|
25116
25095
|
daemonLog(
|
|
25117
|
-
`session
|
|
25096
|
+
`set-status for unknown session id=${id} status=${status3} (no live session; ignoring)`
|
|
25118
25097
|
);
|
|
25098
|
+
return;
|
|
25119
25099
|
}
|
|
25120
|
-
if (
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25124
|
-
// src/commands/sessions/daemon/makeStatusChangeHandler.ts
|
|
25125
|
-
function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
|
|
25126
|
-
return (s, status3, exitCode) => applyStatusChange(s, status3, exitCode, dismiss, notify2, reuseForRun);
|
|
25100
|
+
if (source === "permission") session.permissionActive = true;
|
|
25101
|
+
if (status3 === "running") session.permissionActive = false;
|
|
25102
|
+
onStatusChange(session, status3);
|
|
25127
25103
|
}
|
|
25128
25104
|
|
|
25129
25105
|
// src/shared/db/recordPhaseCycleContext.ts
|
|
@@ -25282,12 +25258,102 @@ function recordSessionUsage(sessions, claudeSessionId, transcriptPath, usedPct,
|
|
|
25282
25258
|
return false;
|
|
25283
25259
|
}
|
|
25284
25260
|
|
|
25261
|
+
// src/commands/sessions/daemon/applyUsageRecord.ts
|
|
25262
|
+
function applyUsageRecord(sessions, windows, notify2, claudeSessionId, transcriptPath, usedPct) {
|
|
25263
|
+
if (recordSessionUsage(
|
|
25264
|
+
sessions,
|
|
25265
|
+
claudeSessionId,
|
|
25266
|
+
transcriptPath,
|
|
25267
|
+
usedPct,
|
|
25268
|
+
windows
|
|
25269
|
+
))
|
|
25270
|
+
notify2();
|
|
25271
|
+
}
|
|
25272
|
+
|
|
25273
|
+
// src/commands/sessions/daemon/setStatus.ts
|
|
25274
|
+
function setStatus2(session, newStatus) {
|
|
25275
|
+
const now = Date.now();
|
|
25276
|
+
if (session.status === "running" && session.runningSince != null) {
|
|
25277
|
+
session.runningMs += now - session.runningSince;
|
|
25278
|
+
}
|
|
25279
|
+
session.runningSince = newStatus === "running" ? now : null;
|
|
25280
|
+
session.status = newStatus;
|
|
25281
|
+
}
|
|
25282
|
+
|
|
25283
|
+
// src/commands/sessions/daemon/shouldAutoDismiss.ts
|
|
25284
|
+
function shouldAutoDismiss(session, exitCode) {
|
|
25285
|
+
if (session.status !== "done" || exitCode !== 0) {
|
|
25286
|
+
return false;
|
|
25287
|
+
}
|
|
25288
|
+
if (session.reviewStarted) {
|
|
25289
|
+
return session.autoAdvance === true;
|
|
25290
|
+
}
|
|
25291
|
+
const args = session.assistArgs;
|
|
25292
|
+
if (args === void 0) return false;
|
|
25293
|
+
if (args[0] === "update") return true;
|
|
25294
|
+
return args.includes("--once") && args[0] !== "next";
|
|
25295
|
+
}
|
|
25296
|
+
|
|
25297
|
+
// src/commands/sessions/daemon/shouldAutoRun.ts
|
|
25298
|
+
function shouldAutoRun(session) {
|
|
25299
|
+
if (!session.autoRun) return { run: false, reason: null };
|
|
25300
|
+
if (session.status !== "done")
|
|
25301
|
+
return {
|
|
25302
|
+
run: false,
|
|
25303
|
+
reason: `session status is "${session.status}", not "done"`
|
|
25304
|
+
};
|
|
25305
|
+
if (session.commandType !== "assist")
|
|
25306
|
+
return {
|
|
25307
|
+
run: false,
|
|
25308
|
+
reason: `command type is "${session.commandType}", not "assist"`
|
|
25309
|
+
};
|
|
25310
|
+
const cmd = session.assistArgs?.[0];
|
|
25311
|
+
if (cmd !== "draft" && cmd !== "bug" && cmd !== "refine")
|
|
25312
|
+
return {
|
|
25313
|
+
run: false,
|
|
25314
|
+
reason: `command "${cmd ?? "(none)"}" is not draft/bug/refine`
|
|
25315
|
+
};
|
|
25316
|
+
const itemId2 = session.activity?.itemId;
|
|
25317
|
+
if (itemId2 == null)
|
|
25318
|
+
return {
|
|
25319
|
+
run: false,
|
|
25320
|
+
reason: "no itemId in activity (draft did not create an item)"
|
|
25321
|
+
};
|
|
25322
|
+
return { run: true, itemId: itemId2 };
|
|
25323
|
+
}
|
|
25324
|
+
|
|
25325
|
+
// src/commands/sessions/daemon/applyStatusChange.ts
|
|
25326
|
+
function applyStatusChange(session, status3, exitCode, dismiss, notify2, reuseForRun) {
|
|
25327
|
+
if (session.status === status3) return;
|
|
25328
|
+
daemonLog(`session ${session.id} status: ${session.status} -> ${status3}`);
|
|
25329
|
+
void flushPhaseActiveMs(session);
|
|
25330
|
+
setStatus2(session, status3);
|
|
25331
|
+
const autoRun = shouldAutoRun(session);
|
|
25332
|
+
if (autoRun.run) {
|
|
25333
|
+
reuseForRun(session, autoRun.itemId);
|
|
25334
|
+
notify2();
|
|
25335
|
+
return;
|
|
25336
|
+
}
|
|
25337
|
+
if (status3 === "done" && autoRun.reason != null) {
|
|
25338
|
+
daemonLog(
|
|
25339
|
+
`session ${session.id} auto-run enabled but skipped (exit ${exitCode ?? "none"}): ${autoRun.reason}`
|
|
25340
|
+
);
|
|
25341
|
+
}
|
|
25342
|
+
if (shouldAutoDismiss(session, exitCode)) dismiss(session.id);
|
|
25343
|
+
else notify2();
|
|
25344
|
+
}
|
|
25345
|
+
|
|
25346
|
+
// src/commands/sessions/daemon/makeStatusChangeHandler.ts
|
|
25347
|
+
function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
|
|
25348
|
+
return (s, status3, exitCode) => applyStatusChange(s, status3, exitCode, dismiss, notify2, reuseForRun);
|
|
25349
|
+
}
|
|
25350
|
+
|
|
25285
25351
|
// src/commands/sessions/daemon/errorTail.ts
|
|
25286
|
-
var
|
|
25352
|
+
var ESC3 = String.fromCharCode(27);
|
|
25287
25353
|
var CSI = String.fromCharCode(155);
|
|
25288
25354
|
var BEL = String.fromCharCode(7);
|
|
25289
25355
|
var ANSI = new RegExp(
|
|
25290
|
-
String.raw`[${
|
|
25356
|
+
String.raw`[${ESC3}${CSI}][[\]()#;?]*(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?${BEL}|(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])`,
|
|
25291
25357
|
"g"
|
|
25292
25358
|
);
|
|
25293
25359
|
var MAX_TAIL_LINES = 40;
|
|
@@ -25355,10 +25421,10 @@ async function seedRunningMsFromUsage(session, notify2) {
|
|
|
25355
25421
|
|
|
25356
25422
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
25357
25423
|
var DEBOUNCE_MS = 50;
|
|
25358
|
-
function watchActivity(session, notify2) {
|
|
25424
|
+
function watchActivity(session, notify2, onClaudeSessionId) {
|
|
25359
25425
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
25360
|
-
const
|
|
25361
|
-
const dir = dirname29(
|
|
25426
|
+
const path71 = activityPath(session.id);
|
|
25427
|
+
const dir = dirname29(path71);
|
|
25362
25428
|
try {
|
|
25363
25429
|
mkdirSync25(dir, { recursive: true });
|
|
25364
25430
|
} catch {
|
|
@@ -25368,21 +25434,23 @@ function watchActivity(session, notify2) {
|
|
|
25368
25434
|
let timer = null;
|
|
25369
25435
|
const read = () => {
|
|
25370
25436
|
timer = null;
|
|
25371
|
-
const activity2 = readActivity(
|
|
25437
|
+
const activity2 = readActivity(path71);
|
|
25372
25438
|
if (!activity2) return;
|
|
25373
25439
|
session.activity = activity2;
|
|
25374
|
-
if (activity2.claudeSessionId)
|
|
25440
|
+
if (activity2.claudeSessionId) {
|
|
25375
25441
|
session.claudeSessionId = activity2.claudeSessionId;
|
|
25442
|
+
onClaudeSessionId?.(session);
|
|
25443
|
+
}
|
|
25376
25444
|
applyReviewPause(session, activity2);
|
|
25377
25445
|
void seedRunningMsFromUsage(session, notify2);
|
|
25378
25446
|
notify2();
|
|
25379
25447
|
};
|
|
25380
25448
|
session.activityWatcher = watch(dir, (_event, filename) => {
|
|
25381
|
-
if (filename && !
|
|
25449
|
+
if (filename && !path71.endsWith(filename)) return;
|
|
25382
25450
|
if (timer) clearTimeout(timer);
|
|
25383
25451
|
timer = setTimeout(read, DEBOUNCE_MS);
|
|
25384
25452
|
});
|
|
25385
|
-
if (existsSync58(
|
|
25453
|
+
if (existsSync58(path71)) read();
|
|
25386
25454
|
}
|
|
25387
25455
|
function refreshActivity(session) {
|
|
25388
25456
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
@@ -25445,7 +25513,6 @@ function wirePtyEvents(session, clients, onStatusChange) {
|
|
|
25445
25513
|
if (!session.pty) return;
|
|
25446
25514
|
session.pty.onData((data) => {
|
|
25447
25515
|
appendScrollback(session, data);
|
|
25448
|
-
noteOutputForEscInterrupt(session, onStatusChange);
|
|
25449
25516
|
broadcast(clients, { type: "output", sessionId: session.id, data });
|
|
25450
25517
|
});
|
|
25451
25518
|
session.pty.onExit(
|
|
@@ -25613,6 +25680,222 @@ function errorSession(id, persisted, error) {
|
|
|
25613
25680
|
};
|
|
25614
25681
|
}
|
|
25615
25682
|
|
|
25683
|
+
// src/commands/sessions/shared/normalizeUser.ts
|
|
25684
|
+
var INTERRUPT_PREFIX = "[Request interrupted";
|
|
25685
|
+
function normalizeUser(entry) {
|
|
25686
|
+
const content = asRecord(entry.message)?.content;
|
|
25687
|
+
if (typeof content === "string")
|
|
25688
|
+
return isInterruptText(content) ? { kind: "interrupt" } : { kind: "user" };
|
|
25689
|
+
if (!Array.isArray(content)) return { kind: "user" };
|
|
25690
|
+
const toolResultIds = [];
|
|
25691
|
+
let hasInterrupt = false;
|
|
25692
|
+
let hasNonToolResult = false;
|
|
25693
|
+
for (const block of content) {
|
|
25694
|
+
const b = asRecord(block);
|
|
25695
|
+
if (!b) continue;
|
|
25696
|
+
if (b.type === "tool_result") {
|
|
25697
|
+
if (typeof b.tool_use_id === "string") toolResultIds.push(b.tool_use_id);
|
|
25698
|
+
} else if (b.type === "text" && isInterruptText(b.text)) {
|
|
25699
|
+
hasInterrupt = true;
|
|
25700
|
+
} else {
|
|
25701
|
+
hasNonToolResult = true;
|
|
25702
|
+
}
|
|
25703
|
+
}
|
|
25704
|
+
if (hasInterrupt) return { kind: "interrupt" };
|
|
25705
|
+
if (hasNonToolResult) return { kind: "user" };
|
|
25706
|
+
return { kind: "toolResult", ids: toolResultIds };
|
|
25707
|
+
}
|
|
25708
|
+
function asRecord(value) {
|
|
25709
|
+
return value && typeof value === "object" ? value : null;
|
|
25710
|
+
}
|
|
25711
|
+
function isInterruptText(value) {
|
|
25712
|
+
return typeof value === "string" && value.startsWith(INTERRUPT_PREFIX);
|
|
25713
|
+
}
|
|
25714
|
+
|
|
25715
|
+
// src/commands/sessions/shared/normalizeTranscriptEvents.ts
|
|
25716
|
+
function normalizeTranscriptEvents(entries) {
|
|
25717
|
+
return entries.map(normalizeEntry).filter((event) => event !== null);
|
|
25718
|
+
}
|
|
25719
|
+
function normalizeEntry(entry) {
|
|
25720
|
+
if (entry.isSidechain || entry.isMeta) return null;
|
|
25721
|
+
if (entry.type === "assistant") return normalizeAssistant(entry);
|
|
25722
|
+
if (entry.type === "user") return normalizeUser(entry);
|
|
25723
|
+
return null;
|
|
25724
|
+
}
|
|
25725
|
+
function normalizeAssistant(entry) {
|
|
25726
|
+
const message = asRecord2(entry.message);
|
|
25727
|
+
const stopReason = typeof message?.stop_reason === "string" ? message.stop_reason : null;
|
|
25728
|
+
const content = message?.content;
|
|
25729
|
+
const toolUses = [];
|
|
25730
|
+
if (Array.isArray(content))
|
|
25731
|
+
for (const block of content) {
|
|
25732
|
+
const b = asRecord2(block);
|
|
25733
|
+
if (b?.type === "tool_use")
|
|
25734
|
+
toolUses.push({
|
|
25735
|
+
id: typeof b.id === "string" ? b.id : "",
|
|
25736
|
+
name: typeof b.name === "string" ? b.name : ""
|
|
25737
|
+
});
|
|
25738
|
+
}
|
|
25739
|
+
return { kind: "assistant", stopReason, toolUses };
|
|
25740
|
+
}
|
|
25741
|
+
function asRecord2(value) {
|
|
25742
|
+
return value && typeof value === "object" ? value : null;
|
|
25743
|
+
}
|
|
25744
|
+
|
|
25745
|
+
// src/commands/sessions/shared/deriveTranscriptStatus.ts
|
|
25746
|
+
var ASK_USER_QUESTION2 = "AskUserQuestion";
|
|
25747
|
+
var TURN_END_STOP_REASONS = /* @__PURE__ */ new Set(["end_turn", "stop_sequence"]);
|
|
25748
|
+
function deriveTranscriptStatus(entries, opts = {}) {
|
|
25749
|
+
const events = normalizeTranscriptEvents(entries);
|
|
25750
|
+
const lastIdx = lastDecisiveIndex(events);
|
|
25751
|
+
if (lastIdx === -1) return null;
|
|
25752
|
+
const ev = events[lastIdx];
|
|
25753
|
+
if (ev.kind === "interrupt") return "waiting";
|
|
25754
|
+
if (ev.kind === "user") return "running";
|
|
25755
|
+
if (ev.kind === "toolResult") return null;
|
|
25756
|
+
if (ev.stopReason != null && TURN_END_STOP_REASONS.has(ev.stopReason))
|
|
25757
|
+
return "waiting";
|
|
25758
|
+
const resolved = resolvedToolUseIds(events, lastIdx);
|
|
25759
|
+
const pending = ev.toolUses.filter((tool) => !resolved.has(tool.id));
|
|
25760
|
+
if (pending.length === 0) return "running";
|
|
25761
|
+
if (pending.some((tool) => tool.name === ASK_USER_QUESTION2))
|
|
25762
|
+
return "waiting";
|
|
25763
|
+
if (opts.permissionActive) return "waiting";
|
|
25764
|
+
return "running";
|
|
25765
|
+
}
|
|
25766
|
+
function lastDecisiveIndex(events) {
|
|
25767
|
+
for (let i = events.length - 1; i >= 0; i--)
|
|
25768
|
+
if (events[i].kind !== "toolResult") return i;
|
|
25769
|
+
return -1;
|
|
25770
|
+
}
|
|
25771
|
+
function resolvedToolUseIds(events, afterIdx) {
|
|
25772
|
+
const ids = /* @__PURE__ */ new Set();
|
|
25773
|
+
for (let i = afterIdx + 1; i < events.length; i++) {
|
|
25774
|
+
const event = events[i];
|
|
25775
|
+
if (event.kind === "toolResult") for (const id of event.ids) ids.add(id);
|
|
25776
|
+
}
|
|
25777
|
+
return ids;
|
|
25778
|
+
}
|
|
25779
|
+
|
|
25780
|
+
// src/commands/sessions/shared/readTranscriptTail.ts
|
|
25781
|
+
import * as fs37 from "fs";
|
|
25782
|
+
var DEFAULT_MAX_BYTES = 256 * 1024;
|
|
25783
|
+
function parseTailEntries(raw) {
|
|
25784
|
+
const entries = [];
|
|
25785
|
+
for (const line of raw.split("\n")) {
|
|
25786
|
+
const trimmed = line.trim();
|
|
25787
|
+
if (!trimmed) continue;
|
|
25788
|
+
try {
|
|
25789
|
+
const parsed = JSON.parse(trimmed);
|
|
25790
|
+
if (parsed && typeof parsed === "object")
|
|
25791
|
+
entries.push(parsed);
|
|
25792
|
+
} catch {
|
|
25793
|
+
}
|
|
25794
|
+
}
|
|
25795
|
+
return entries;
|
|
25796
|
+
}
|
|
25797
|
+
function dropPartialFirstLine(raw, sliced) {
|
|
25798
|
+
if (!sliced) return raw;
|
|
25799
|
+
const newline = raw.indexOf("\n");
|
|
25800
|
+
return newline === -1 ? "" : raw.slice(newline + 1);
|
|
25801
|
+
}
|
|
25802
|
+
async function readTranscriptTail(filePath, maxBytes = DEFAULT_MAX_BYTES) {
|
|
25803
|
+
let handle;
|
|
25804
|
+
try {
|
|
25805
|
+
handle = await fs37.promises.open(filePath, "r");
|
|
25806
|
+
const { size } = await handle.stat();
|
|
25807
|
+
const start3 = Math.max(0, size - maxBytes);
|
|
25808
|
+
const length = size - start3;
|
|
25809
|
+
if (length === 0) return [];
|
|
25810
|
+
const buf = Buffer.alloc(length);
|
|
25811
|
+
await handle.read(buf, 0, length, start3);
|
|
25812
|
+
return parseTailEntries(
|
|
25813
|
+
dropPartialFirstLine(buf.toString("utf8"), start3 > 0)
|
|
25814
|
+
);
|
|
25815
|
+
} catch {
|
|
25816
|
+
return [];
|
|
25817
|
+
} finally {
|
|
25818
|
+
await handle?.close();
|
|
25819
|
+
}
|
|
25820
|
+
}
|
|
25821
|
+
function readTranscriptTailSync(filePath, maxBytes = DEFAULT_MAX_BYTES) {
|
|
25822
|
+
let fd;
|
|
25823
|
+
try {
|
|
25824
|
+
fd = fs37.openSync(filePath, "r");
|
|
25825
|
+
const { size } = fs37.fstatSync(fd);
|
|
25826
|
+
const start3 = Math.max(0, size - maxBytes);
|
|
25827
|
+
const length = size - start3;
|
|
25828
|
+
if (length === 0) return [];
|
|
25829
|
+
const buf = Buffer.alloc(length);
|
|
25830
|
+
fs37.readSync(fd, buf, 0, length, start3);
|
|
25831
|
+
return parseTailEntries(
|
|
25832
|
+
dropPartialFirstLine(buf.toString("utf8"), start3 > 0)
|
|
25833
|
+
);
|
|
25834
|
+
} catch {
|
|
25835
|
+
return [];
|
|
25836
|
+
} finally {
|
|
25837
|
+
if (fd !== void 0) fs37.closeSync(fd);
|
|
25838
|
+
}
|
|
25839
|
+
}
|
|
25840
|
+
|
|
25841
|
+
// src/commands/sessions/shared/findTranscriptPathSync.ts
|
|
25842
|
+
import * as fs38 from "fs";
|
|
25843
|
+
import * as os4 from "os";
|
|
25844
|
+
import * as path60 from "path";
|
|
25845
|
+
function projectDirForCwd(cwd) {
|
|
25846
|
+
return path60.join(
|
|
25847
|
+
os4.homedir(),
|
|
25848
|
+
".claude",
|
|
25849
|
+
"projects",
|
|
25850
|
+
cwd.replace(/[^a-zA-Z0-9]/g, "-")
|
|
25851
|
+
);
|
|
25852
|
+
}
|
|
25853
|
+
function transcriptPathFor(cwd, claudeSessionId) {
|
|
25854
|
+
return path60.join(projectDirForCwd(cwd), `${claudeSessionId}.jsonl`);
|
|
25855
|
+
}
|
|
25856
|
+
function findTranscriptPathSync(cwd, claudeSessionId) {
|
|
25857
|
+
const direct = transcriptPathFor(cwd, claudeSessionId);
|
|
25858
|
+
if (fs38.existsSync(direct)) return direct;
|
|
25859
|
+
const dir = projectDirForCwd(cwd);
|
|
25860
|
+
let files;
|
|
25861
|
+
try {
|
|
25862
|
+
files = fs38.readdirSync(dir);
|
|
25863
|
+
} catch {
|
|
25864
|
+
return null;
|
|
25865
|
+
}
|
|
25866
|
+
for (const file of files) {
|
|
25867
|
+
if (!file.endsWith(".jsonl")) continue;
|
|
25868
|
+
if (headContainsSessionId(path60.join(dir, file), claudeSessionId))
|
|
25869
|
+
return path60.join(dir, file);
|
|
25870
|
+
}
|
|
25871
|
+
return null;
|
|
25872
|
+
}
|
|
25873
|
+
function headContainsSessionId(filePath, claudeSessionId) {
|
|
25874
|
+
let fd;
|
|
25875
|
+
try {
|
|
25876
|
+
fd = fs38.openSync(filePath, "r");
|
|
25877
|
+
const buf = Buffer.alloc(16384);
|
|
25878
|
+
const bytesRead = fs38.readSync(fd, buf, 0, buf.length, 0);
|
|
25879
|
+
return buf.toString("utf8", 0, bytesRead).includes(claudeSessionId);
|
|
25880
|
+
} catch {
|
|
25881
|
+
return false;
|
|
25882
|
+
} finally {
|
|
25883
|
+
if (fd !== void 0) fs38.closeSync(fd);
|
|
25884
|
+
}
|
|
25885
|
+
}
|
|
25886
|
+
|
|
25887
|
+
// src/commands/sessions/daemon/deriveRestoreStatus.ts
|
|
25888
|
+
function deriveRestoreStatus(persisted) {
|
|
25889
|
+
if (!persisted.claudeSessionId) return "waiting";
|
|
25890
|
+
const filePath = findTranscriptPathSync(
|
|
25891
|
+
persisted.cwd,
|
|
25892
|
+
persisted.claudeSessionId
|
|
25893
|
+
);
|
|
25894
|
+
if (!filePath) return "waiting";
|
|
25895
|
+
const entries = readTranscriptTailSync(filePath);
|
|
25896
|
+
return deriveTranscriptStatus(entries) === "running" ? "running" : "waiting";
|
|
25897
|
+
}
|
|
25898
|
+
|
|
25616
25899
|
// src/commands/sessions/daemon/runningSession.ts
|
|
25617
25900
|
function runningSession(base, persisted, pty2) {
|
|
25618
25901
|
return restoredSession(base, persisted, pty2, "running");
|
|
@@ -25636,17 +25919,16 @@ function restoredSession(base, persisted, pty2, status3) {
|
|
|
25636
25919
|
}
|
|
25637
25920
|
|
|
25638
25921
|
// src/commands/sessions/daemon/restoreInteractiveSession.ts
|
|
25639
|
-
function restoreInteractiveSession(id, persisted, base) {
|
|
25922
|
+
function restoreInteractiveSession(id, persisted, base, idle) {
|
|
25640
25923
|
if (persisted.commandType !== "run" && persisted.claudeSessionId) {
|
|
25641
|
-
return resumeViaClaude(id, persisted, base);
|
|
25924
|
+
return resumeViaClaude(id, persisted, base, idle);
|
|
25642
25925
|
}
|
|
25643
25926
|
if (persisted.commandType === "claude") {
|
|
25644
25927
|
return unrecoverableClaude(id, persisted);
|
|
25645
25928
|
}
|
|
25646
25929
|
return notRestoredStub(base, persisted);
|
|
25647
25930
|
}
|
|
25648
|
-
function resumeViaClaude(id, persisted, base) {
|
|
25649
|
-
const idle = persisted.status !== "running";
|
|
25931
|
+
function resumeViaClaude(id, persisted, base, idle) {
|
|
25650
25932
|
const pty2 = spawnClaude2({
|
|
25651
25933
|
resumeSessionId: persisted.claudeSessionId,
|
|
25652
25934
|
prompt: idle ? void 0 : buildResumePrompt(),
|
|
@@ -25695,8 +25977,8 @@ function isUpdate(persisted) {
|
|
|
25695
25977
|
function restoreSession(id, persisted) {
|
|
25696
25978
|
const base = restoreBase(id, persisted);
|
|
25697
25979
|
if (isUpdate(persisted)) return updatedSession(id, persisted);
|
|
25980
|
+
const idle = deriveRestoreStatus(persisted) !== "running";
|
|
25698
25981
|
if (needsWrapperRelaunch(persisted)) {
|
|
25699
|
-
const idle = persisted.status !== "running";
|
|
25700
25982
|
const pty2 = spawnPty(
|
|
25701
25983
|
assistResumeArgs(persisted),
|
|
25702
25984
|
persisted.cwd,
|
|
@@ -25705,7 +25987,7 @@ function restoreSession(id, persisted) {
|
|
|
25705
25987
|
);
|
|
25706
25988
|
return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
|
|
25707
25989
|
}
|
|
25708
|
-
return restoreInteractiveSession(id, persisted, base);
|
|
25990
|
+
return restoreInteractiveSession(id, persisted, base, idle);
|
|
25709
25991
|
}
|
|
25710
25992
|
function needsWrapperRelaunch(persisted) {
|
|
25711
25993
|
return isBacklogRun(persisted) || isOnceLaunch(persisted) && !!persisted.claudeSessionId;
|
|
@@ -25846,6 +26128,77 @@ function shutdownSessions(sessions) {
|
|
|
25846
26128
|
}
|
|
25847
26129
|
}
|
|
25848
26130
|
|
|
26131
|
+
// src/commands/sessions/daemon/watchTranscript.ts
|
|
26132
|
+
import { existsSync as existsSync60, watch as watch2 } from "fs";
|
|
26133
|
+
|
|
26134
|
+
// src/commands/sessions/daemon/reconcileTranscriptStatus.ts
|
|
26135
|
+
async function resolveTranscriptPath(session) {
|
|
26136
|
+
if (session.transcriptPath) return session.transcriptPath;
|
|
26137
|
+
if (!session.claudeSessionId) return null;
|
|
26138
|
+
const direct = session.cwd ? findTranscriptPathSync(session.cwd, session.claudeSessionId) : null;
|
|
26139
|
+
const filePath = direct ?? await findSessionJsonlPath(session.claudeSessionId);
|
|
26140
|
+
if (filePath) session.transcriptPath = filePath;
|
|
26141
|
+
return filePath;
|
|
26142
|
+
}
|
|
26143
|
+
async function reconcileTranscriptStatus(session, onStatusChange) {
|
|
26144
|
+
if (session.status === "done" || session.status === "error") return;
|
|
26145
|
+
const filePath = await resolveTranscriptPath(session);
|
|
26146
|
+
if (!filePath) return;
|
|
26147
|
+
const entries = await readTranscriptTail(filePath);
|
|
26148
|
+
const derived = deriveTranscriptStatus(entries, {
|
|
26149
|
+
permissionActive: session.permissionActive
|
|
26150
|
+
});
|
|
26151
|
+
if (!derived) return;
|
|
26152
|
+
if (derived === "running") session.permissionActive = false;
|
|
26153
|
+
if (derived === session.status) return;
|
|
26154
|
+
daemonLog(
|
|
26155
|
+
`session ${session.id} transcript reconcile: ${session.status} -> ${derived}`
|
|
26156
|
+
);
|
|
26157
|
+
onStatusChange(session, derived);
|
|
26158
|
+
}
|
|
26159
|
+
|
|
26160
|
+
// src/commands/sessions/daemon/watchTranscript.ts
|
|
26161
|
+
var DEBOUNCE_MS2 = 100;
|
|
26162
|
+
function watchTranscript(session, onStatusChange) {
|
|
26163
|
+
if (!session.cwd || !session.claudeSessionId) return;
|
|
26164
|
+
if (session.watchedTranscriptId === session.claudeSessionId) return;
|
|
26165
|
+
const dir = projectDirForCwd(session.cwd);
|
|
26166
|
+
if (!existsSync60(dir)) return;
|
|
26167
|
+
if (session.transcriptWatcher) {
|
|
26168
|
+
session.transcriptWatcher.close();
|
|
26169
|
+
session.transcriptWatcher = void 0;
|
|
26170
|
+
}
|
|
26171
|
+
session.transcriptPath = void 0;
|
|
26172
|
+
const fileName = `${session.claudeSessionId}.jsonl`;
|
|
26173
|
+
let timer = null;
|
|
26174
|
+
const run4 = () => {
|
|
26175
|
+
timer = null;
|
|
26176
|
+
void reconcileTranscriptStatus(session, onStatusChange);
|
|
26177
|
+
};
|
|
26178
|
+
try {
|
|
26179
|
+
session.transcriptWatcher = watch2(dir, (_event, changed) => {
|
|
26180
|
+
if (changed && changed !== fileName) return;
|
|
26181
|
+
if (timer) clearTimeout(timer);
|
|
26182
|
+
timer = setTimeout(run4, DEBOUNCE_MS2);
|
|
26183
|
+
});
|
|
26184
|
+
} catch (error) {
|
|
26185
|
+
daemonLog(
|
|
26186
|
+
`session ${session.id} transcript watch failed: ${error instanceof Error ? error.message : String(error)}`
|
|
26187
|
+
);
|
|
26188
|
+
return;
|
|
26189
|
+
}
|
|
26190
|
+
session.watchedTranscriptId = session.claudeSessionId;
|
|
26191
|
+
daemonLog(`session ${session.id} watching transcript ${fileName}`);
|
|
26192
|
+
run4();
|
|
26193
|
+
}
|
|
26194
|
+
|
|
26195
|
+
// src/commands/sessions/daemon/wireSessionWatchers.ts
|
|
26196
|
+
function wireSessionWatchers(session, notify2, onStatusChange) {
|
|
26197
|
+
const wireTranscript = (s) => watchTranscript(s, onStatusChange);
|
|
26198
|
+
watchActivity(session, notify2, wireTranscript);
|
|
26199
|
+
wireTranscript(session);
|
|
26200
|
+
}
|
|
26201
|
+
|
|
25849
26202
|
// src/commands/sessions/daemon/connectToWindowsDaemon.ts
|
|
25850
26203
|
import * as net2 from "net";
|
|
25851
26204
|
|
|
@@ -25961,13 +26314,13 @@ async function defaultConnect() {
|
|
|
25961
26314
|
}
|
|
25962
26315
|
|
|
25963
26316
|
// src/commands/sessions/daemon/hasPersistedWindowsSessions.ts
|
|
25964
|
-
import { existsSync as
|
|
26317
|
+
import { existsSync as existsSync61, readFileSync as readFileSync49 } from "fs";
|
|
25965
26318
|
import { posix } from "path";
|
|
25966
26319
|
function hasPersistedWindowsSessions() {
|
|
25967
26320
|
const sessionsFile = windowsSessionsFileFromWsl();
|
|
25968
26321
|
if (!sessionsFile) return false;
|
|
25969
26322
|
try {
|
|
25970
|
-
if (!
|
|
26323
|
+
if (!existsSync61(sessionsFile)) return false;
|
|
25971
26324
|
const data = JSON.parse(readFileSync49(sessionsFile, "utf8"));
|
|
25972
26325
|
return Array.isArray(data) && data.length > 0;
|
|
25973
26326
|
} catch (error) {
|
|
@@ -26528,7 +26881,6 @@ function writeToSession(sessions, id, data, onStatusChange) {
|
|
|
26528
26881
|
const s = sessions.get(id);
|
|
26529
26882
|
if (!s || s.status === "done") return;
|
|
26530
26883
|
s.pty?.write(data);
|
|
26531
|
-
watchEscInterrupt(s, data, onStatusChange);
|
|
26532
26884
|
watchPromptSubmit(s, data, onStatusChange);
|
|
26533
26885
|
}
|
|
26534
26886
|
function resizeSession(sessions, id, cols, rows) {
|
|
@@ -26608,7 +26960,7 @@ var SessionManager = class {
|
|
|
26608
26960
|
drain = () => drainSessions(this.sessions, this.notify);
|
|
26609
26961
|
add(session) {
|
|
26610
26962
|
this.wire(session);
|
|
26611
|
-
|
|
26963
|
+
wireSessionWatchers(session, this.notify, this.onStatusChange);
|
|
26612
26964
|
logSpawnedSession(session);
|
|
26613
26965
|
return session.id;
|
|
26614
26966
|
}
|
|
@@ -26671,27 +27023,20 @@ var SessionManager = class {
|
|
|
26671
27023
|
setStarred(id, starred) {
|
|
26672
27024
|
if (setStarred2(this.sessions, id, starred)) this.notify();
|
|
26673
27025
|
}
|
|
26674
|
-
setStatus(id, status3) {
|
|
26675
|
-
|
|
26676
|
-
if (!session) {
|
|
26677
|
-
daemonLog(
|
|
26678
|
-
`set-status for unknown session id=${id} status=${status3} (no live session; ignoring)`
|
|
26679
|
-
);
|
|
26680
|
-
return;
|
|
26681
|
-
}
|
|
26682
|
-
this.onStatusChange(session, status3);
|
|
27026
|
+
setStatus(id, status3, source) {
|
|
27027
|
+
applySetStatus(this.sessions, id, status3, source, this.onStatusChange);
|
|
26683
27028
|
}
|
|
26684
27029
|
/* why: the status line relays token totals keyed by Claude's session id; join
|
|
26685
27030
|
* it to the running backlog phase so the spend accumulates against that row. */
|
|
26686
27031
|
recordUsage(claudeSessionId, transcriptPath, usedPct) {
|
|
26687
|
-
|
|
27032
|
+
applyUsageRecord(
|
|
26688
27033
|
this.sessions.values(),
|
|
27034
|
+
this.clients.currentWindows(),
|
|
27035
|
+
this.notify,
|
|
26689
27036
|
claudeSessionId,
|
|
26690
27037
|
transcriptPath,
|
|
26691
|
-
usedPct
|
|
26692
|
-
|
|
26693
|
-
))
|
|
26694
|
-
this.notify();
|
|
27038
|
+
usedPct
|
|
27039
|
+
);
|
|
26695
27040
|
}
|
|
26696
27041
|
listSessions = () => {
|
|
26697
27042
|
const local = [...this.sessions.values()].map(toSessionInfo);
|
|
@@ -26712,8 +27057,30 @@ import * as net3 from "net";
|
|
|
26712
27057
|
// src/commands/sessions/daemon/handleConnection.ts
|
|
26713
27058
|
import { createInterface as createInterface9 } from "readline";
|
|
26714
27059
|
|
|
27060
|
+
// src/commands/sessions/daemon/creator.ts
|
|
27061
|
+
function creator(isNew, spawn12) {
|
|
27062
|
+
return (client, m, d) => {
|
|
27063
|
+
if (m.windowsProxy.route(client, d)) return;
|
|
27064
|
+
sendTo(client, { type: "created", sessionId: spawn12(m, d), isNew });
|
|
27065
|
+
};
|
|
27066
|
+
}
|
|
27067
|
+
|
|
27068
|
+
// src/commands/sessions/daemon/handleSetStatus.ts
|
|
27069
|
+
function handleSetStatus(client, m, d) {
|
|
27070
|
+
daemonLog(
|
|
27071
|
+
`set-status received: id=${d.sessionId} status=${d.status}${d.source ? ` source=${d.source}` : ""}`
|
|
27072
|
+
);
|
|
27073
|
+
if (m.windowsProxy.route(client, d)) return;
|
|
27074
|
+
m.setStatus(
|
|
27075
|
+
d.sessionId,
|
|
27076
|
+
d.status,
|
|
27077
|
+
d.source
|
|
27078
|
+
);
|
|
27079
|
+
if (d.ack) sendTo(client, { type: "ack", sessionId: d.sessionId });
|
|
27080
|
+
}
|
|
27081
|
+
|
|
26715
27082
|
// src/commands/sessions/shared/parseTranscript.ts
|
|
26716
|
-
import * as
|
|
27083
|
+
import * as fs39 from "fs";
|
|
26717
27084
|
|
|
26718
27085
|
// src/commands/sessions/shared/toolTarget.ts
|
|
26719
27086
|
function toolTarget(input) {
|
|
@@ -26765,7 +27132,7 @@ async function parseTranscript(sessionId) {
|
|
|
26765
27132
|
const filePath = await findSessionJsonlPath(sessionId);
|
|
26766
27133
|
if (!filePath) return [];
|
|
26767
27134
|
try {
|
|
26768
|
-
const raw = await
|
|
27135
|
+
const raw = await fs39.promises.readFile(filePath, "utf8");
|
|
26769
27136
|
return parseTranscriptLines(raw.split("\n"));
|
|
26770
27137
|
} catch {
|
|
26771
27138
|
return [];
|
|
@@ -26829,12 +27196,6 @@ function spawnCreate(m, d) {
|
|
|
26829
27196
|
}
|
|
26830
27197
|
|
|
26831
27198
|
// src/commands/sessions/daemon/messageHandlers.ts
|
|
26832
|
-
function creator(isNew, spawn12) {
|
|
26833
|
-
return (client, m, d) => {
|
|
26834
|
-
if (m.windowsProxy.route(client, d)) return;
|
|
26835
|
-
sendTo(client, { type: "created", sessionId: spawn12(m, d), isNew });
|
|
26836
|
-
};
|
|
26837
|
-
}
|
|
26838
27199
|
function routed(local) {
|
|
26839
27200
|
return (client, m, d) => {
|
|
26840
27201
|
if (!m.windowsProxy.route(client, d)) local(client, m, d);
|
|
@@ -26903,11 +27264,7 @@ var messageHandlers = {
|
|
|
26903
27264
|
(_client, m, d) => m.setStarred(d.sessionId, d.starred)
|
|
26904
27265
|
),
|
|
26905
27266
|
"set-active": (_client, m, d) => m.active.set(d.cwd, d.sessionId),
|
|
26906
|
-
"set-status":
|
|
26907
|
-
daemonLog(`set-status received: id=${d.sessionId} status=${d.status}`);
|
|
26908
|
-
if (!m.windowsProxy.route(client, d))
|
|
26909
|
-
m.setStatus(d.sessionId, d.status);
|
|
26910
|
-
},
|
|
27267
|
+
"set-status": handleSetStatus,
|
|
26911
27268
|
"ui-status": (_client, _m, d) => daemonLog(`ui rendered: id=${d.sessionId} status=${d.status}`)
|
|
26912
27269
|
};
|
|
26913
27270
|
|
|
@@ -27093,18 +27450,33 @@ function registerDaemon(program2) {
|
|
|
27093
27450
|
).action(drainDaemon);
|
|
27094
27451
|
}
|
|
27095
27452
|
|
|
27453
|
+
// src/commands/sessions/registerSetStatusCommand.ts
|
|
27454
|
+
function registerSetStatusCommand(cmd) {
|
|
27455
|
+
cmd.command("set-status <status>").description(
|
|
27456
|
+
"Report the current session's status to the sessions daemon (used by Claude Code hooks)"
|
|
27457
|
+
).option(
|
|
27458
|
+
"--source <source>",
|
|
27459
|
+
"Originating hook (permission, stop, notification, prompt, pretool, posttool)"
|
|
27460
|
+
).option(
|
|
27461
|
+
"--ack",
|
|
27462
|
+
"Wait for the daemon to acknowledge delivery, retrying on failure"
|
|
27463
|
+
).action(
|
|
27464
|
+
(status3, options2) => setSessionStatus(status3, { source: options2.source, ack: options2.ack })
|
|
27465
|
+
);
|
|
27466
|
+
}
|
|
27467
|
+
|
|
27096
27468
|
// src/commands/sessions/summarise/index.ts
|
|
27097
|
-
import * as
|
|
27469
|
+
import * as fs41 from "fs";
|
|
27098
27470
|
import chalk200 from "chalk";
|
|
27099
27471
|
|
|
27100
27472
|
// src/commands/sessions/summarise/shared.ts
|
|
27101
|
-
import * as
|
|
27473
|
+
import * as fs40 from "fs";
|
|
27102
27474
|
function writeSummary(jsonlPath2, summary) {
|
|
27103
|
-
|
|
27475
|
+
fs40.writeFileSync(summaryPathFor(jsonlPath2), `${summary.trim()}
|
|
27104
27476
|
`, "utf8");
|
|
27105
27477
|
}
|
|
27106
27478
|
function hasSummary(jsonlPath2) {
|
|
27107
|
-
return
|
|
27479
|
+
return fs40.existsSync(summaryPathFor(jsonlPath2));
|
|
27108
27480
|
}
|
|
27109
27481
|
function summaryPathFor(jsonlPath2) {
|
|
27110
27482
|
return jsonlPath2.replace(/\.jsonl$/, ".summary");
|
|
@@ -27191,7 +27563,7 @@ function selectCandidates(files, options2) {
|
|
|
27191
27563
|
const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
|
|
27192
27564
|
candidates.sort((a, b) => {
|
|
27193
27565
|
try {
|
|
27194
|
-
return
|
|
27566
|
+
return fs41.statSync(b).mtimeMs - fs41.statSync(a).mtimeMs;
|
|
27195
27567
|
} catch {
|
|
27196
27568
|
return 0;
|
|
27197
27569
|
}
|
|
@@ -27229,9 +27601,7 @@ function registerSessions(program2) {
|
|
|
27229
27601
|
(options2, command) => web({ ...options2, open: command.optsWithGlobals().open })
|
|
27230
27602
|
);
|
|
27231
27603
|
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);
|
|
27232
|
-
cmd
|
|
27233
|
-
"Report the current session's status to the sessions daemon (used by Claude Code hooks)"
|
|
27234
|
-
).action(setSessionStatus);
|
|
27604
|
+
registerSetStatusCommand(cmd);
|
|
27235
27605
|
configHelp(cmd, [
|
|
27236
27606
|
{
|
|
27237
27607
|
key: "sessions.windowsProjectsRoot",
|
|
@@ -27332,9 +27702,9 @@ function buildLimitsSegment(rateLimits) {
|
|
|
27332
27702
|
|
|
27333
27703
|
// src/commands/readGitBranch.ts
|
|
27334
27704
|
import { readFileSync as readFileSync52, statSync as statSync11 } from "fs";
|
|
27335
|
-
import { isAbsolute as isAbsolute2, join as
|
|
27705
|
+
import { isAbsolute as isAbsolute2, join as join69, resolve as resolve17 } from "path";
|
|
27336
27706
|
function resolveGitDir(cwd) {
|
|
27337
|
-
const dotGit =
|
|
27707
|
+
const dotGit = join69(cwd, ".git");
|
|
27338
27708
|
let stat2;
|
|
27339
27709
|
try {
|
|
27340
27710
|
stat2 = statSync11(dotGit);
|
|
@@ -27364,7 +27734,7 @@ function readGitBranch(cwd) {
|
|
|
27364
27734
|
}
|
|
27365
27735
|
let head;
|
|
27366
27736
|
try {
|
|
27367
|
-
head = readFileSync52(
|
|
27737
|
+
head = readFileSync52(join69(gitDir, "HEAD"), "utf8");
|
|
27368
27738
|
} catch {
|
|
27369
27739
|
return null;
|
|
27370
27740
|
}
|
|
@@ -27432,21 +27802,21 @@ async function statusLine() {
|
|
|
27432
27802
|
}
|
|
27433
27803
|
|
|
27434
27804
|
// src/commands/sync.ts
|
|
27435
|
-
import * as
|
|
27436
|
-
import * as
|
|
27437
|
-
import * as
|
|
27805
|
+
import * as fs48 from "fs";
|
|
27806
|
+
import * as os5 from "os";
|
|
27807
|
+
import * as path69 from "path";
|
|
27438
27808
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
27439
27809
|
|
|
27440
27810
|
// src/commands/sync/syncClaudeMd.ts
|
|
27441
|
-
import * as
|
|
27442
|
-
import * as
|
|
27811
|
+
import * as fs42 from "fs";
|
|
27812
|
+
import * as path61 from "path";
|
|
27443
27813
|
import chalk203 from "chalk";
|
|
27444
27814
|
async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
27445
|
-
const source =
|
|
27446
|
-
const target =
|
|
27447
|
-
const sourceContent =
|
|
27448
|
-
if (
|
|
27449
|
-
const targetContent =
|
|
27815
|
+
const source = path61.join(claudeDir, "CLAUDE.md");
|
|
27816
|
+
const target = path61.join(targetBase, "CLAUDE.md");
|
|
27817
|
+
const sourceContent = fs42.readFileSync(source, "utf8");
|
|
27818
|
+
if (fs42.existsSync(target)) {
|
|
27819
|
+
const targetContent = fs42.readFileSync(target, "utf8");
|
|
27450
27820
|
if (sourceContent !== targetContent) {
|
|
27451
27821
|
console.log(
|
|
27452
27822
|
chalk203.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
@@ -27463,38 +27833,38 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
27463
27833
|
}
|
|
27464
27834
|
}
|
|
27465
27835
|
}
|
|
27466
|
-
|
|
27836
|
+
fs42.copyFileSync(source, target);
|
|
27467
27837
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
27468
27838
|
}
|
|
27469
27839
|
|
|
27470
27840
|
// src/commands/sync/syncCodex.ts
|
|
27471
|
-
import * as
|
|
27841
|
+
import * as path64 from "path";
|
|
27472
27842
|
|
|
27473
27843
|
// src/commands/sync/installHarnessCommands.ts
|
|
27474
|
-
import * as
|
|
27475
|
-
import * as
|
|
27844
|
+
import * as fs43 from "fs";
|
|
27845
|
+
import * as path62 from "path";
|
|
27476
27846
|
function installHarnessCommands(claudeDir, harness, transform) {
|
|
27477
|
-
const commandsSource =
|
|
27478
|
-
const files =
|
|
27847
|
+
const commandsSource = path62.join(claudeDir, "commands");
|
|
27848
|
+
const files = fs43.readdirSync(commandsSource);
|
|
27479
27849
|
let synced = 0;
|
|
27480
27850
|
for (const file of files) {
|
|
27481
27851
|
if (!file.endsWith(".md")) continue;
|
|
27482
27852
|
const name = file.replace(/\.md$/, "");
|
|
27483
|
-
const content =
|
|
27484
|
-
const target =
|
|
27485
|
-
|
|
27486
|
-
|
|
27853
|
+
const content = fs43.readFileSync(path62.join(commandsSource, file), "utf8");
|
|
27854
|
+
const target = path62.join(harness.homeDir, harness.sync.commandDest(name));
|
|
27855
|
+
fs43.mkdirSync(path62.dirname(target), { recursive: true });
|
|
27856
|
+
fs43.writeFileSync(target, transform(name, content));
|
|
27487
27857
|
synced++;
|
|
27488
27858
|
}
|
|
27489
|
-
const agentsTarget =
|
|
27490
|
-
|
|
27491
|
-
|
|
27859
|
+
const agentsTarget = path62.join(harness.homeDir, harness.sync.agentsFile);
|
|
27860
|
+
fs43.mkdirSync(path62.dirname(agentsTarget), { recursive: true });
|
|
27861
|
+
fs43.copyFileSync(path62.join(claudeDir, "CLAUDE.md"), agentsTarget);
|
|
27492
27862
|
return { total: files.length, synced };
|
|
27493
27863
|
}
|
|
27494
27864
|
|
|
27495
27865
|
// src/commands/sync/syncCodexHooks.ts
|
|
27496
|
-
import * as
|
|
27497
|
-
import * as
|
|
27866
|
+
import * as fs44 from "fs";
|
|
27867
|
+
import * as path63 from "path";
|
|
27498
27868
|
var BEGIN = "# >>> assist codex hooks (managed) \u2014 do not edit >>>";
|
|
27499
27869
|
var END = "# <<< assist codex hooks (managed) <<<";
|
|
27500
27870
|
function upsertManagedBlock(existing, body) {
|
|
@@ -27524,11 +27894,11 @@ ${rest}
|
|
|
27524
27894
|
`;
|
|
27525
27895
|
}
|
|
27526
27896
|
function syncCodexHooks(sourcePath) {
|
|
27527
|
-
const body =
|
|
27528
|
-
const configPath =
|
|
27529
|
-
const existing =
|
|
27530
|
-
|
|
27531
|
-
|
|
27897
|
+
const body = fs44.readFileSync(sourcePath, "utf8");
|
|
27898
|
+
const configPath = path63.join(harnesses.codex.homeDir, "config.toml");
|
|
27899
|
+
const existing = fs44.existsSync(configPath) ? fs44.readFileSync(configPath, "utf8") : "";
|
|
27900
|
+
fs44.mkdirSync(path63.dirname(configPath), { recursive: true });
|
|
27901
|
+
fs44.writeFileSync(configPath, upsertManagedBlock(existing, body));
|
|
27532
27902
|
console.log(
|
|
27533
27903
|
"Registered assist codex-hook in ~/.codex/config.toml (PreToolUse, PermissionRequest)"
|
|
27534
27904
|
);
|
|
@@ -27559,50 +27929,50 @@ function syncCodex(claudeDir) {
|
|
|
27559
27929
|
harnesses.codex,
|
|
27560
27930
|
commandToSkill
|
|
27561
27931
|
);
|
|
27562
|
-
syncCodexHooks(
|
|
27932
|
+
syncCodexHooks(path64.join(claudeDir, "..", "codex", "config.toml"));
|
|
27563
27933
|
console.log(
|
|
27564
27934
|
`Synced ${total} skill(s) to ~/.codex/skills and CLAUDE.md to ~/.codex/AGENTS.md`
|
|
27565
27935
|
);
|
|
27566
27936
|
}
|
|
27567
27937
|
|
|
27568
27938
|
// src/commands/sync/syncDesign.ts
|
|
27569
|
-
import * as
|
|
27570
|
-
import * as
|
|
27939
|
+
import * as fs45 from "fs";
|
|
27940
|
+
import * as path65 from "path";
|
|
27571
27941
|
function syncDesign(claudeDir, targetBase) {
|
|
27572
|
-
const systemPromptSource =
|
|
27573
|
-
const systemPromptTarget =
|
|
27574
|
-
|
|
27942
|
+
const systemPromptSource = path65.join(claudeDir, "system-prompt.md");
|
|
27943
|
+
const systemPromptTarget = path65.join(targetBase, "system-prompt.md");
|
|
27944
|
+
fs45.copyFileSync(systemPromptSource, systemPromptTarget);
|
|
27575
27945
|
console.log("Copied system-prompt.md to ~/.claude/system-prompt.md");
|
|
27576
|
-
const skillsSource =
|
|
27577
|
-
const skillsTarget =
|
|
27578
|
-
|
|
27579
|
-
const files =
|
|
27946
|
+
const skillsSource = path65.join(claudeDir, "skills");
|
|
27947
|
+
const skillsTarget = path65.join(targetBase, "skills");
|
|
27948
|
+
fs45.mkdirSync(skillsTarget, { recursive: true });
|
|
27949
|
+
const files = fs45.readdirSync(skillsSource);
|
|
27580
27950
|
for (const file of files) {
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27951
|
+
fs45.copyFileSync(
|
|
27952
|
+
path65.join(skillsSource, file),
|
|
27953
|
+
path65.join(skillsTarget, file)
|
|
27584
27954
|
);
|
|
27585
27955
|
}
|
|
27586
27956
|
console.log(`Synced ${files.length} design skill(s) to ~/.claude/skills`);
|
|
27587
27957
|
}
|
|
27588
27958
|
|
|
27589
27959
|
// src/commands/sync/syncPi.ts
|
|
27590
|
-
import * as
|
|
27960
|
+
import * as path67 from "path";
|
|
27591
27961
|
|
|
27592
27962
|
// src/commands/sync/syncPiHooks.ts
|
|
27593
|
-
import * as
|
|
27594
|
-
import * as
|
|
27963
|
+
import * as fs46 from "fs";
|
|
27964
|
+
import * as path66 from "path";
|
|
27595
27965
|
function piExtensionsDir() {
|
|
27596
|
-
return
|
|
27966
|
+
return path66.join(harnesses.pi.homeDir, "extensions");
|
|
27597
27967
|
}
|
|
27598
27968
|
function syncPiHooks(sourceDir) {
|
|
27599
27969
|
const target = piExtensionsDir();
|
|
27600
|
-
|
|
27601
|
-
const files =
|
|
27970
|
+
fs46.mkdirSync(target, { recursive: true });
|
|
27971
|
+
const files = fs46.readdirSync(sourceDir).filter((f) => f.endsWith(".ts"));
|
|
27602
27972
|
for (const file of files) {
|
|
27603
|
-
|
|
27604
|
-
|
|
27605
|
-
|
|
27973
|
+
fs46.copyFileSync(
|
|
27974
|
+
path66.join(sourceDir, file),
|
|
27975
|
+
path66.join(target, `assist-${file}`)
|
|
27606
27976
|
);
|
|
27607
27977
|
}
|
|
27608
27978
|
console.log(
|
|
@@ -27639,23 +28009,23 @@ function syncPi(claudeDir) {
|
|
|
27639
28009
|
harnesses.pi,
|
|
27640
28010
|
commandToPrompt
|
|
27641
28011
|
);
|
|
27642
|
-
syncPiHooks(
|
|
28012
|
+
syncPiHooks(path67.join(claudeDir, "..", "pi"));
|
|
27643
28013
|
console.log(
|
|
27644
28014
|
`Synced ${synced} prompt(s) to ~/.pi/agent/prompts and CLAUDE.md to ~/.pi/agent/AGENTS.md`
|
|
27645
28015
|
);
|
|
27646
28016
|
}
|
|
27647
28017
|
|
|
27648
28018
|
// src/commands/sync/syncSettings.ts
|
|
27649
|
-
import * as
|
|
27650
|
-
import * as
|
|
28019
|
+
import * as fs47 from "fs";
|
|
28020
|
+
import * as path68 from "path";
|
|
27651
28021
|
import chalk204 from "chalk";
|
|
27652
28022
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
27653
|
-
const source =
|
|
27654
|
-
const target =
|
|
27655
|
-
const sourceContent =
|
|
28023
|
+
const source = path68.join(claudeDir, "settings.json");
|
|
28024
|
+
const target = path68.join(targetBase, "settings.json");
|
|
28025
|
+
const sourceContent = fs47.readFileSync(source, "utf8");
|
|
27656
28026
|
const sourceSettings = JSON.parse(sourceContent);
|
|
27657
|
-
const targetExists =
|
|
27658
|
-
const targetContent = targetExists ?
|
|
28027
|
+
const targetExists = fs47.existsSync(target);
|
|
28028
|
+
const targetContent = targetExists ? fs47.readFileSync(target, "utf8") : "";
|
|
27659
28029
|
const preservedUserSettings = targetExists ? JSON.parse(targetContent) : {};
|
|
27660
28030
|
const mergedContent = JSON.stringify(
|
|
27661
28031
|
{ ...preservedUserSettings, ...sourceSettings },
|
|
@@ -27684,18 +28054,18 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
27684
28054
|
}
|
|
27685
28055
|
}
|
|
27686
28056
|
}
|
|
27687
|
-
|
|
28057
|
+
fs47.writeFileSync(target, mergedContent);
|
|
27688
28058
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
27689
28059
|
}
|
|
27690
28060
|
|
|
27691
28061
|
// src/commands/sync.ts
|
|
27692
28062
|
var __filename5 = fileURLToPath9(import.meta.url);
|
|
27693
|
-
var __dirname7 =
|
|
28063
|
+
var __dirname7 = path69.dirname(__filename5);
|
|
27694
28064
|
async function sync(options2) {
|
|
27695
28065
|
const config = loadConfig();
|
|
27696
28066
|
const yes = options2?.yes ?? config.sync.autoConfirm;
|
|
27697
|
-
const claudeDir =
|
|
27698
|
-
const targetBase =
|
|
28067
|
+
const claudeDir = path69.join(__dirname7, "..", "claude");
|
|
28068
|
+
const targetBase = path69.join(os5.homedir(), ".claude");
|
|
27699
28069
|
syncCommands(claudeDir, targetBase);
|
|
27700
28070
|
syncDesign(claudeDir, targetBase);
|
|
27701
28071
|
await syncSettings(claudeDir, targetBase, { yes });
|
|
@@ -27704,12 +28074,12 @@ async function sync(options2) {
|
|
|
27704
28074
|
syncPi(claudeDir);
|
|
27705
28075
|
}
|
|
27706
28076
|
function syncCommands(claudeDir, targetBase) {
|
|
27707
|
-
const sourceDir =
|
|
27708
|
-
const targetDir =
|
|
27709
|
-
|
|
27710
|
-
const files =
|
|
28077
|
+
const sourceDir = path69.join(claudeDir, "commands");
|
|
28078
|
+
const targetDir = path69.join(targetBase, "commands");
|
|
28079
|
+
fs48.mkdirSync(targetDir, { recursive: true });
|
|
28080
|
+
const files = fs48.readdirSync(sourceDir);
|
|
27711
28081
|
for (const file of files) {
|
|
27712
|
-
|
|
28082
|
+
fs48.copyFileSync(path69.join(sourceDir, file), path69.join(targetDir, file));
|
|
27713
28083
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
27714
28084
|
}
|
|
27715
28085
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -27717,7 +28087,7 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
27717
28087
|
|
|
27718
28088
|
// src/commands/update.ts
|
|
27719
28089
|
import { execSync as execSync59 } from "child_process";
|
|
27720
|
-
import * as
|
|
28090
|
+
import * as path70 from "path";
|
|
27721
28091
|
|
|
27722
28092
|
// src/commands/restartDaemonAfterUpdate.ts
|
|
27723
28093
|
async function restartDaemonAfterUpdate() {
|
|
@@ -27736,12 +28106,12 @@ async function restartDaemonAfterUpdate() {
|
|
|
27736
28106
|
// src/commands/update.ts
|
|
27737
28107
|
function isGlobalNpmInstall(dir) {
|
|
27738
28108
|
try {
|
|
27739
|
-
const resolved =
|
|
27740
|
-
if (resolved.split(
|
|
28109
|
+
const resolved = path70.resolve(dir);
|
|
28110
|
+
if (resolved.split(path70.sep).includes("node_modules")) {
|
|
27741
28111
|
return true;
|
|
27742
28112
|
}
|
|
27743
28113
|
const globalPrefix = execSync59("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
27744
|
-
return resolved.toLowerCase().startsWith(
|
|
28114
|
+
return resolved.toLowerCase().startsWith(path70.resolve(globalPrefix).toLowerCase());
|
|
27745
28115
|
} catch {
|
|
27746
28116
|
return false;
|
|
27747
28117
|
}
|