@staff0rd/assist 0.410.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/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.410.0",
9
+ version: "0.412.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -61,6 +61,7 @@ var package_default = {
61
61
  "log-update": "^8.0.0",
62
62
  marked: "^15.0.12",
63
63
  "marked-terminal": "^7.3.0",
64
+ mermaid: "^11.16.0",
64
65
  minimatch: "^10.1.1",
65
66
  mssql: "^12.5.0",
66
67
  "node-notifier": "^10.0.1",
@@ -142,10 +143,10 @@ import { stringify as stringifyYaml } from "yaml";
142
143
  // src/shared/loadRawYaml.ts
143
144
  import { existsSync, readFileSync } from "fs";
144
145
  import { parse as parseYaml } from "yaml";
145
- function loadRawYaml(path70) {
146
- if (!existsSync(path70)) return {};
146
+ function loadRawYaml(path71) {
147
+ if (!existsSync(path71)) return {};
147
148
  try {
148
- const content = readFileSync(path70, "utf8");
149
+ const content = readFileSync(path71, "utf8");
149
150
  return parseYaml(content) || {};
150
151
  } catch {
151
152
  return {};
@@ -1441,8 +1442,8 @@ function findUnmergedPaths() {
1441
1442
  }
1442
1443
  const found = /* @__PURE__ */ new Set();
1443
1444
  for (const line of output.split("\n")) {
1444
- const path70 = line.split(" ")[1];
1445
- if (path70) found.add(path70.trim());
1445
+ const path71 = line.split(" ")[1];
1446
+ if (path71) found.add(path71.trim());
1446
1447
  }
1447
1448
  return [...found];
1448
1449
  }
@@ -1541,8 +1542,8 @@ function getPreferredRemoteRepo(cwd) {
1541
1542
  function gitRefUrl(kind, ref, cwd) {
1542
1543
  const repo = getPreferredRemoteRepo(cwd);
1543
1544
  if (!repo) return void 0;
1544
- const path70 = kind === "branch" ? "tree" : "commit";
1545
- return `https://github.com/${repo.org}/${repo.repo}/${path70}/${ref}`;
1545
+ const path71 = kind === "branch" ? "tree" : "commit";
1546
+ return `https://github.com/${repo.org}/${repo.repo}/${path71}/${ref}`;
1546
1547
  }
1547
1548
 
1548
1549
  // src/commands/commit/collectCommitRefs.ts
@@ -3409,9 +3410,9 @@ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
3409
3410
 
3410
3411
  // src/commands/verify/forbiddenStrings/findForbiddenStrings.ts
3411
3412
  import { minimatch as minimatch2 } from "minimatch";
3412
- function resolveStringsAtPath(data, path70) {
3413
+ function resolveStringsAtPath(data, path71) {
3413
3414
  let current = data;
3414
- for (const segment of path70.split(".")) {
3415
+ for (const segment of path71.split(".")) {
3415
3416
  if (current === null || typeof current !== "object") return [];
3416
3417
  current = current[segment];
3417
3418
  }
@@ -3424,10 +3425,10 @@ function resolveStringsAtPath(data, path70) {
3424
3425
  }
3425
3426
  function findRuleViolations(data, rule) {
3426
3427
  const violations = [];
3427
- for (const path70 of rule.paths) {
3428
- for (const value of resolveStringsAtPath(data, path70)) {
3428
+ for (const path71 of rule.paths) {
3429
+ for (const value of resolveStringsAtPath(data, path71)) {
3429
3430
  if (minimatch2(value, rule.disallowed))
3430
- violations.push({ file: rule.file, path: path70, value });
3431
+ violations.push({ file: rule.file, path: path71, value });
3431
3432
  }
3432
3433
  }
3433
3434
  return violations;
@@ -3466,8 +3467,8 @@ function forbiddenStrings() {
3466
3467
  process.exit(0);
3467
3468
  }
3468
3469
  console.log("Forbidden strings found:\n");
3469
- for (const { file, path: path70, value } of violations) {
3470
- console.log(` ${file} ${path70}: ${value}`);
3470
+ for (const { file, path: path71, value } of violations) {
3471
+ console.log(` ${file} ${path71}: ${value}`);
3471
3472
  }
3472
3473
  console.log(`
3473
3474
  Total: ${violations.length} forbidden string(s).`);
@@ -3759,15 +3760,15 @@ function readBaselineMigrations(repoRelativeDir, ref) {
3759
3760
  } catch {
3760
3761
  return baseline;
3761
3762
  }
3762
- const paths = listing.split("\n").map((line) => line.trim()).filter(Boolean).filter((path70) => MIGRATION_FILE3.test(basename(path70)));
3763
- for (const path70 of paths) {
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) {
3764
3765
  try {
3765
- const content = execSync12(`git show "${ref}:${path70}"`, {
3766
+ const content = execSync12(`git show "${ref}:${path71}"`, {
3766
3767
  encoding: "utf8",
3767
3768
  maxBuffer: 16 * 1024 * 1024,
3768
3769
  stdio: ["pipe", "pipe", "pipe"]
3769
3770
  });
3770
- baseline.set(basename(path70), content);
3771
+ baseline.set(basename(path71), content);
3771
3772
  } catch {
3772
3773
  }
3773
3774
  }
@@ -5105,8 +5106,16 @@ var daemonPaths = {
5105
5106
  };
5106
5107
 
5107
5108
  // src/commands/sessions/daemon/ensureHooksSettings.ts
5108
- var RUNNING = "assist sessions set-status running";
5109
- var WAITING = "assist sessions set-status waiting";
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
+ }
5110
5119
  function on(command, matcher) {
5111
5120
  const hooks = [{ type: "command", command }];
5112
5121
  return [matcher == null ? { hooks } : { matcher, hooks }];
@@ -5115,27 +5124,22 @@ var ASK_USER_QUESTION = "AskUserQuestion";
5115
5124
  var NOT_ASK_USER_QUESTION = `^(?!${ASK_USER_QUESTION}$).*`;
5116
5125
  var hooksSettings = {
5117
5126
  hooks: {
5118
- UserPromptSubmit: on(RUNNING),
5127
+ UserPromptSubmit: on(running("prompt")),
5119
5128
  PreToolUse: [
5120
- ...on(WAITING, ASK_USER_QUESTION),
5121
- ...on(RUNNING, NOT_ASK_USER_QUESTION)
5129
+ ...on(waiting("pretool"), ASK_USER_QUESTION),
5130
+ ...on(running("pretool"), NOT_ASK_USER_QUESTION)
5122
5131
  ],
5123
- // tool finished, agent resumes; restores running
5124
- PostToolUse: on(RUNNING),
5125
- Stop: on(WAITING),
5126
- Notification: on(WAITING),
5127
- /* why: a tool/plan approval prompt appears mid-turn, after PreToolUse has
5128
- * already set running; PermissionRequest fires when the agent blocks on
5129
- * that decision, so the card shows waiting rather than staying running while
5130
- * it is genuinely awaiting the user (#449). */
5131
- PermissionRequest: on(WAITING)
5132
+ PostToolUse: on(running("posttool")),
5133
+ Stop: on(ackWaiting("stop")),
5134
+ Notification: on(ackWaiting("notification")),
5135
+ PermissionRequest: on(ackWaiting("permission"))
5132
5136
  }
5133
5137
  };
5134
5138
  function ensureHooksSettings() {
5135
- const path70 = daemonPaths.hooksSettings;
5136
- mkdirSync5(dirname12(path70), { recursive: true });
5137
- writeFileSync15(path70, JSON.stringify(hooksSettings, null, 2));
5138
- return path70;
5139
+ const path71 = daemonPaths.hooksSettings;
5140
+ mkdirSync5(dirname12(path71), { recursive: true });
5141
+ writeFileSync15(path71, JSON.stringify(hooksSettings, null, 2));
5142
+ return path71;
5139
5143
  }
5140
5144
 
5141
5145
  // src/shared/spawnInherit.ts
@@ -5400,9 +5404,9 @@ var LOCAL_FILES = ["backlog.jsonl", "backlog.db"];
5400
5404
  function backupLocalBacklogFiles(dir) {
5401
5405
  const moved = [];
5402
5406
  for (const name of LOCAL_FILES) {
5403
- const path70 = join15(dir, ".assist", name);
5404
- if (existsSync18(path70)) {
5405
- renameSync(path70, `${path70}.bak`);
5407
+ const path71 = join15(dir, ".assist", name);
5408
+ if (existsSync18(path71)) {
5409
+ renameSync(path71, `${path71}.bak`);
5406
5410
  moved.push(`${name} \u2192 ${name}.bak`);
5407
5411
  }
5408
5412
  }
@@ -5874,8 +5878,8 @@ var backlogItemSchema = z3.strictObject({
5874
5878
  var backlogFileSchema = z3.array(backlogItemSchema);
5875
5879
 
5876
5880
  // src/commands/backlog/parseBacklogJsonl.ts
5877
- function parseBacklogJsonl(path70) {
5878
- const content = readFileSync14(path70, "utf8").trim();
5881
+ function parseBacklogJsonl(path71) {
5882
+ const content = readFileSync14(path71, "utf8").trim();
5879
5883
  if (content.length === 0) return [];
5880
5884
  return content.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => backlogItemSchema.parse(JSON.parse(line)));
5881
5885
  }
@@ -5957,8 +5961,8 @@ function findBacklogUp(startDir) {
5957
5961
 
5958
5962
  // src/commands/backlog/getCurrentOrigin.ts
5959
5963
  import { execFileSync } from "child_process";
5960
- function stripLeadingSlashes(path70) {
5961
- return path70.replace(/^\/+/, "");
5964
+ function stripLeadingSlashes(path71) {
5965
+ return path71.replace(/^\/+/, "");
5962
5966
  }
5963
5967
  function normalizeOrigin(raw) {
5964
5968
  const trimmed = raw.trim().replace(/\.git$/i, "").replace(/\/+$/, "");
@@ -6306,20 +6310,87 @@ function sendToDaemon(message) {
6306
6310
  });
6307
6311
  }
6308
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
+
6309
6360
  // src/commands/sessions/setSessionStatus.ts
6310
6361
  var DELIVERY_ATTEMPTS = 3;
6311
6362
  var RETRY_DELAY_MS = 100;
6312
- async function setSessionStatus(status3) {
6363
+ async function setSessionStatus(status3, opts = {}) {
6313
6364
  const sessionId = process.env.ASSIST_SESSION_ID;
6314
- logHookFired(status3, sessionId);
6365
+ logHookFired(status3, sessionId, opts.source);
6315
6366
  if (!sessionId) return;
6316
- await deliverStatus(sessionId, status3);
6367
+ const payload = buildPayload(sessionId, status3, opts);
6368
+ if (opts.ack) await deliverReliably(sessionId, status3, payload);
6369
+ else await deliverBestEffort(sessionId, status3, payload);
6317
6370
  }
6318
- async function deliverStatus(sessionId, status3) {
6319
- const payload = { type: "set-status", sessionId, status: status3 };
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) {
6320
6391
  for (let attempt = 1; attempt <= DELIVERY_ATTEMPTS; attempt++) {
6321
6392
  try {
6322
- await sendToDaemon(payload);
6393
+ await sendToDaemonAwaitAck(payload);
6323
6394
  return;
6324
6395
  } catch (error) {
6325
6396
  if (attempt < DELIVERY_ATTEMPTS) {
@@ -6327,7 +6398,7 @@ async function deliverStatus(sessionId, status3) {
6327
6398
  continue;
6328
6399
  }
6329
6400
  appendDaemonLog(
6330
- `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)}`
6331
6402
  );
6332
6403
  }
6333
6404
  }
@@ -6338,9 +6409,9 @@ function sleep(ms) {
6338
6409
  function describeError(error) {
6339
6410
  return error instanceof Error ? error.message : String(error);
6340
6411
  }
6341
- function logHookFired(status3, sessionId) {
6412
+ function logHookFired(status3, sessionId, source) {
6342
6413
  appendDaemonLog(
6343
- `set-status hook fired: id=${sessionId ?? "none"} status=${status3}`
6414
+ `set-status hook fired: id=${sessionId ?? "none"} status=${status3}${source ? ` source=${source}` : ""}`
6344
6415
  );
6345
6416
  }
6346
6417
 
@@ -6498,24 +6569,24 @@ function getOwnerPath(itemId2) {
6498
6569
  function recordSignalOwner(itemId2) {
6499
6570
  const sessionId = process.env.ASSIST_SESSION_ID;
6500
6571
  if (!sessionId) return;
6501
- const path70 = getOwnerPath(itemId2);
6502
- mkdirSync7(dirname14(path70), { recursive: true });
6503
- writeFileSync17(path70, JSON.stringify({ sessionId }));
6572
+ const path71 = getOwnerPath(itemId2);
6573
+ mkdirSync7(dirname14(path71), { recursive: true });
6574
+ writeFileSync17(path71, JSON.stringify({ sessionId }));
6504
6575
  }
6505
6576
  function readSignalOwner(itemId2) {
6506
- const path70 = getOwnerPath(itemId2);
6507
- if (!existsSync21(path70)) return void 0;
6577
+ const path71 = getOwnerPath(itemId2);
6578
+ if (!existsSync21(path71)) return void 0;
6508
6579
  try {
6509
- const parsed = JSON.parse(readFileSync15(path70, "utf8"));
6580
+ const parsed = JSON.parse(readFileSync15(path71, "utf8"));
6510
6581
  return parsed.sessionId;
6511
6582
  } catch {
6512
6583
  return void 0;
6513
6584
  }
6514
6585
  }
6515
6586
  function clearSignalOwner(itemId2) {
6516
- const path70 = getOwnerPath(itemId2);
6587
+ const path71 = getOwnerPath(itemId2);
6517
6588
  try {
6518
- rmSync(path70);
6589
+ rmSync(path71);
6519
6590
  } catch {
6520
6591
  }
6521
6592
  }
@@ -6552,19 +6623,19 @@ function resolveSignalTarget(event, data) {
6552
6623
  function writeSignal(event, data) {
6553
6624
  const target = resolveSignalTarget(event, data);
6554
6625
  if (!target) return;
6555
- const path70 = getSignalPath(target);
6556
- if (!path70) return;
6626
+ const path71 = getSignalPath(target);
6627
+ if (!path71) return;
6557
6628
  const signal = { event, sessionId: target, ...data };
6558
- mkdirSync8(dirname15(path70), { recursive: true });
6559
- writeFileSync18(path70, JSON.stringify(signal));
6629
+ mkdirSync8(dirname15(path71), { recursive: true });
6630
+ writeFileSync18(path71, JSON.stringify(signal));
6560
6631
  }
6561
6632
 
6562
6633
  // src/commands/backlog/readSignal.ts
6563
6634
  function readSignal() {
6564
- const path70 = getSignalPath();
6565
- if (!path70 || !existsSync22(path70)) return void 0;
6635
+ const path71 = getSignalPath();
6636
+ if (!path71 || !existsSync22(path71)) return void 0;
6566
6637
  try {
6567
- return JSON.parse(readFileSync16(path70, "utf8"));
6638
+ return JSON.parse(readFileSync16(path71, "utf8"));
6568
6639
  } catch {
6569
6640
  return void 0;
6570
6641
  }
@@ -6697,13 +6768,13 @@ function activityPath(sessionId) {
6697
6768
  function emitActivity(activity2) {
6698
6769
  const sessionId = process.env.ASSIST_ACTIVITY_ID;
6699
6770
  if (!sessionId) return;
6700
- const path70 = activityPath(sessionId);
6701
- mkdirSync9(dirname16(path70), { recursive: true });
6702
- writeFileSync19(path70, JSON.stringify({ ...activity2, startedAt: Date.now() }));
6771
+ const path71 = activityPath(sessionId);
6772
+ mkdirSync9(dirname16(path71), { recursive: true });
6773
+ writeFileSync19(path71, JSON.stringify({ ...activity2, startedAt: Date.now() }));
6703
6774
  }
6704
- function readActivity(path70) {
6775
+ function readActivity(path71) {
6705
6776
  try {
6706
- return JSON.parse(readFileSync17(path70, "utf8"));
6777
+ return JSON.parse(readFileSync17(path71, "utf8"));
6707
6778
  } catch {
6708
6779
  return void 0;
6709
6780
  }
@@ -6713,9 +6784,9 @@ function reconcileActivity(sessionId, activity2) {
6713
6784
  removeActivity(sessionId);
6714
6785
  return;
6715
6786
  }
6716
- const path70 = activityPath(sessionId);
6717
- mkdirSync9(dirname16(path70), { recursive: true });
6718
- writeFileSync19(path70, JSON.stringify(activity2));
6787
+ const path71 = activityPath(sessionId);
6788
+ mkdirSync9(dirname16(path71), { recursive: true });
6789
+ writeFileSync19(path71, JSON.stringify(activity2));
6719
6790
  }
6720
6791
  function removeActivity(sessionId) {
6721
6792
  try {
@@ -7968,8 +8039,8 @@ async function loadItemSummaries(orm, origin) {
7968
8039
  // src/commands/backlog/originDisplayName.ts
7969
8040
  function originDisplayName(origin) {
7970
8041
  if (origin.startsWith("local:")) {
7971
- const path70 = origin.slice("local:".length).replace(/\/+$/, "");
7972
- const segments = path70.split("/").filter(Boolean);
8042
+ const path71 = origin.slice("local:".length).replace(/\/+$/, "");
8043
+ const segments = path71.split("/").filter(Boolean);
7973
8044
  return segments[segments.length - 1] ?? origin;
7974
8045
  }
7975
8046
  const firstSlash = origin.indexOf("/");
@@ -8405,7 +8476,7 @@ function findSynthesisForBranch(repoReviewsDir, branch2) {
8405
8476
  const parent = dirname20(branchKeyPath);
8406
8477
  const branchPrefix = basename5(branchKeyPath);
8407
8478
  if (!existsSync26(parent)) return null;
8408
- const synthesisFiles = readdirSync2(parent).filter((name) => name.startsWith(branchPrefix)).map((name) => join24(parent, name, "synthesis.md")).filter((path70) => existsSync26(path70)).map((path70) => ({ path: path70, mtime: statSync2(path70).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
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);
8409
8480
  return synthesisFiles[0]?.path ?? null;
8410
8481
  }
8411
8482
 
@@ -8459,12 +8530,12 @@ async function getReviewSynthesis(req, res) {
8459
8530
  const cwd = getCwdParam(req, res);
8460
8531
  if (!cwd) return;
8461
8532
  try {
8462
- const path70 = await resolveSynthesisPath(cwd);
8463
- if (!path70) {
8533
+ const path71 = await resolveSynthesisPath(cwd);
8534
+ if (!path71) {
8464
8535
  respondJson(res, 404, { error: "No synthesis found" });
8465
8536
  return;
8466
8537
  }
8467
- respondJson(res, 200, { synthesis: readFileSync19(path70, "utf8") });
8538
+ respondJson(res, 200, { synthesis: readFileSync19(path71, "utf8") });
8468
8539
  } catch {
8469
8540
  respondJson(res, 404, { error: "No synthesis found" });
8470
8541
  }
@@ -8611,10 +8682,10 @@ function getStorePath(filename) {
8611
8682
  return join27(getStoreDir(), filename);
8612
8683
  }
8613
8684
  function loadJson(filename) {
8614
- const path70 = getStorePath(filename);
8615
- if (existsSync27(path70)) {
8685
+ const path71 = getStorePath(filename);
8686
+ if (existsSync27(path71)) {
8616
8687
  try {
8617
- return JSON.parse(readFileSync20(path70, "utf8"));
8688
+ return JSON.parse(readFileSync20(path71, "utf8"));
8618
8689
  } catch {
8619
8690
  return {};
8620
8691
  }
@@ -9724,9 +9795,9 @@ function originToSshUrl(origin) {
9724
9795
  const firstSlash = origin.indexOf("/");
9725
9796
  if (firstSlash <= 0) return null;
9726
9797
  const host = origin.slice(0, firstSlash);
9727
- const path70 = origin.slice(firstSlash + 1);
9728
- if (!path70) return null;
9729
- return `git@${host}:${path70}.git`;
9798
+ const path71 = origin.slice(firstSlash + 1);
9799
+ if (!path71) return null;
9800
+ return `git@${host}:${path71}.git`;
9730
9801
  }
9731
9802
 
9732
9803
  // src/commands/backlog/cloneRepo.ts
@@ -12035,9 +12106,9 @@ var __dirname4 = dirname21(__filename3);
12035
12106
  function packageRoot() {
12036
12107
  return __dirname4;
12037
12108
  }
12038
- function readLines(path70) {
12039
- if (!existsSync29(path70)) return [];
12040
- return readFileSync23(path70, "utf8").split("\n").filter((line) => line.trim() !== "");
12109
+ function readLines(path71) {
12110
+ if (!existsSync29(path71)) return [];
12111
+ return readFileSync23(path71, "utf8").split("\n").filter((line) => line.trim() !== "");
12041
12112
  }
12042
12113
  var cachedReads;
12043
12114
  var cachedWrites;
@@ -12529,14 +12600,14 @@ function showProgress(p, label2) {
12529
12600
  const pct = Math.round(p.done / p.total * 100);
12530
12601
  process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
12531
12602
  }
12532
- async function resolveCommand(cli, path70, description, depth, p) {
12533
- showProgress(p, path70.join(" "));
12534
- const subHelp = await runHelp([cli, ...path70]);
12603
+ async function resolveCommand(cli, path71, description, depth, p) {
12604
+ showProgress(p, path71.join(" "));
12605
+ const subHelp = await runHelp([cli, ...path71]);
12535
12606
  if (!subHelp || !hasSubcommands(subHelp)) {
12536
- return [{ path: path70, description }];
12607
+ return [{ path: path71, description }];
12537
12608
  }
12538
- const children = await discoverAt(cli, path70, depth + 1, p);
12539
- return children.length > 0 ? children : [{ path: path70, description }];
12609
+ const children = await discoverAt(cli, path71, depth + 1, p);
12610
+ return children.length > 0 ? children : [{ path: path71, description }];
12540
12611
  }
12541
12612
  async function discoverAt(cli, parentPath, depth, p) {
12542
12613
  if (depth > SAFETY_DEPTH) return [];
@@ -12684,9 +12755,9 @@ function logPath(cli) {
12684
12755
  return join31(homedir16(), ".assist", `cli-discover-${safeName}.log`);
12685
12756
  }
12686
12757
  function readCache(cli) {
12687
- const path70 = logPath(cli);
12688
- if (!existsSync31(path70)) return void 0;
12689
- return readFileSync25(path70, "utf8");
12758
+ const path71 = logPath(cli);
12759
+ if (!existsSync31(path71)) return void 0;
12760
+ return readFileSync25(path71, "utf8");
12690
12761
  }
12691
12762
  function writeCache(cli, output) {
12692
12763
  const dir = join31(homedir16(), ".assist");
@@ -12858,9 +12929,9 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
12858
12929
  }
12859
12930
  const cutoff = Date.now() - STALE_AFTER_MS;
12860
12931
  for (const entry of entries) {
12861
- const path70 = join33(dir, entry);
12932
+ const path71 = join33(dir, entry);
12862
12933
  try {
12863
- if (statSync3(path70).mtimeMs < cutoff) unlinkSync7(path70);
12934
+ if (statSync3(path71).mtimeMs < cutoff) unlinkSync7(path71);
12864
12935
  } catch {
12865
12936
  continue;
12866
12937
  }
@@ -12870,10 +12941,10 @@ function sweepRestrictedDir(dir = getRestrictedDir()) {
12870
12941
  // src/commands/codeComment/readPinState.ts
12871
12942
  import { existsSync as existsSync32, readFileSync as readFileSync26 } from "fs";
12872
12943
  function readPinState(pin) {
12873
- const path70 = getPinStatePath(pin);
12874
- if (!existsSync32(path70)) return void 0;
12944
+ const path71 = getPinStatePath(pin);
12945
+ if (!existsSync32(path71)) return void 0;
12875
12946
  try {
12876
- const state = JSON.parse(readFileSync26(path70, "utf8"));
12947
+ const state = JSON.parse(readFileSync26(path71, "utf8"));
12877
12948
  if (state.pin !== pin) return void 0;
12878
12949
  return state;
12879
12950
  } catch {
@@ -13769,8 +13840,8 @@ function stepIntoNested(container, key, nextKey) {
13769
13840
  }
13770
13841
  return ensureObject(container, resolved);
13771
13842
  }
13772
- function setNestedValue(obj, path70, value) {
13773
- const keys = path70.split(".");
13843
+ function setNestedValue(obj, path71, value) {
13844
+ const keys = path71.split(".");
13774
13845
  const result = { ...obj };
13775
13846
  let current = result;
13776
13847
  for (let i = 0; i < keys.length - 1; i++) {
@@ -13850,9 +13921,9 @@ function isTraversable(value) {
13850
13921
  function stepInto(current, key) {
13851
13922
  return isTraversable(current) ? current[key] : void 0;
13852
13923
  }
13853
- function getNestedValue(obj, path70) {
13924
+ function getNestedValue(obj, path71) {
13854
13925
  let current = obj;
13855
- for (const key of path70.split(".")) current = stepInto(current, key);
13926
+ for (const key of path71.split(".")) current = stepInto(current, key);
13856
13927
  return current;
13857
13928
  }
13858
13929
 
@@ -13968,10 +14039,10 @@ function getMigrationApprovalPath(migrationId) {
13968
14039
  // src/commands/dbMigration/readMigrationPinState.ts
13969
14040
  import { existsSync as existsSync34, readFileSync as readFileSync28 } from "fs";
13970
14041
  function readMigrationPinState(pin) {
13971
- const path70 = getMigrationPinPath(pin);
13972
- if (!existsSync34(path70)) return void 0;
14042
+ const path71 = getMigrationPinPath(pin);
14043
+ if (!existsSync34(path71)) return void 0;
13973
14044
  try {
13974
- const state = JSON.parse(readFileSync28(path70, "utf8"));
14045
+ const state = JSON.parse(readFileSync28(path71, "utf8"));
13975
14046
  if (state.pin !== pin) return void 0;
13976
14047
  if (!Number.isInteger(state.migrationId)) return void 0;
13977
14048
  return state;
@@ -15410,10 +15481,10 @@ function decideCommentGuard(input, existingContent) {
15410
15481
  import { existsSync as existsSync41, unlinkSync as unlinkSync11 } from "fs";
15411
15482
  function consumeMigrationApproval(migrationId) {
15412
15483
  sweepRestrictedDir();
15413
- const path70 = getMigrationApprovalPath(migrationId);
15414
- if (!existsSync41(path70)) return false;
15484
+ const path71 = getMigrationApprovalPath(migrationId);
15485
+ if (!existsSync41(path71)) return false;
15415
15486
  try {
15416
- unlinkSync11(path70);
15487
+ unlinkSync11(path71);
15417
15488
  return true;
15418
15489
  } catch {
15419
15490
  return false;
@@ -16587,9 +16658,9 @@ function parseRscRows(flight) {
16587
16658
  }
16588
16659
  function makeRscResolver(rows) {
16589
16660
  return (ref) => {
16590
- const [id, ...path70] = ref.slice(1).replace(/^[L@]/, "").split(":");
16661
+ const [id, ...path71] = ref.slice(1).replace(/^[L@]/, "").split(":");
16591
16662
  let v = rows[id];
16592
- for (const key of path70) {
16663
+ for (const key of path71) {
16593
16664
  if (v == null || typeof v !== "object") return void 0;
16594
16665
  v = v[key];
16595
16666
  }
@@ -17273,15 +17344,15 @@ function postComment(vars) {
17273
17344
  const stdout = startLine === void 0 ? runGhGraphql(MUTATION_SINGLE, base) : runGhGraphql(MUTATION_MULTI, { ...base, startLine });
17274
17345
  assertThreadCreated(stdout);
17275
17346
  }
17276
- function comment2(path70, line, body, startLine) {
17347
+ function comment2(path71, line, body, startLine) {
17277
17348
  validateBody(body);
17278
17349
  validateLine(line);
17279
17350
  if (startLine !== void 0) validateLine(startLine);
17280
17351
  try {
17281
17352
  const prId = getCurrentPrNodeId();
17282
- postComment({ prId, body, path: path70, line, startLine });
17353
+ postComment({ prId, body, path: path71, line, startLine });
17283
17354
  const range = startLine !== void 0 ? `${startLine}-${line}` : `${line}`;
17284
- console.log(`Added review comment on ${path70}:${range}`);
17355
+ console.log(`Added review comment on ${path71}:${range}`);
17285
17356
  } catch (error) {
17286
17357
  if (isGhNotInstalled(error)) {
17287
17358
  console.error("Error: GitHub CLI (gh) is not installed.");
@@ -18160,8 +18231,8 @@ function registerPrs(program2) {
18160
18231
  prsCommand.command("reply <comment-id> <body>").description("Reply to a comment thread without resolving it").action((commentId, body) => {
18161
18232
  reply(Number.parseInt(commentId, 10), body);
18162
18233
  });
18163
- prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path70, line, body) => {
18164
- comment2(path70, Number.parseInt(line, 10), body);
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);
18165
18236
  });
18166
18237
  configHelp(prsCommand, prsConfigHelp);
18167
18238
  }
@@ -18414,10 +18485,10 @@ function resolveOpSecret(reference) {
18414
18485
  }
18415
18486
 
18416
18487
  // src/commands/ravendb/ravenFetch.ts
18417
- async function ravenFetch(connection, path70) {
18488
+ async function ravenFetch(connection, path71) {
18418
18489
  const apiKey = resolveOpSecret(connection.apiKeyRef);
18419
18490
  let accessToken = await getAccessToken(apiKey);
18420
- const url = `${connection.url}${path70}`;
18491
+ const url = `${connection.url}${path71}`;
18421
18492
  const headers = {
18422
18493
  Authorization: `Bearer ${accessToken}`,
18423
18494
  "Content-Type": "application/json"
@@ -18521,16 +18592,16 @@ import chalk170 from "chalk";
18521
18592
  // src/commands/ravendb/buildQueryPath.ts
18522
18593
  function buildQueryPath(opts) {
18523
18594
  const db = encodeURIComponent(opts.db);
18524
- let path70;
18595
+ let path71;
18525
18596
  if (opts.collection) {
18526
- path70 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
18597
+ path71 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
18527
18598
  } else {
18528
- path70 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
18599
+ path71 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
18529
18600
  }
18530
18601
  if (opts.query) {
18531
- path70 += `&query=${encodeURIComponent(opts.query)}`;
18602
+ path71 += `&query=${encodeURIComponent(opts.query)}`;
18532
18603
  }
18533
- return path70;
18604
+ return path71;
18534
18605
  }
18535
18606
 
18536
18607
  // src/commands/ravendb/fetchAllPages.ts
@@ -18539,7 +18610,7 @@ async function fetchAllPages(connection, opts) {
18539
18610
  let start3 = 0;
18540
18611
  while (true) {
18541
18612
  const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
18542
- const path70 = buildQueryPath({
18613
+ const path71 = buildQueryPath({
18543
18614
  db: connection.database,
18544
18615
  collection: opts.collection,
18545
18616
  start: start3,
@@ -18547,7 +18618,7 @@ async function fetchAllPages(connection, opts) {
18547
18618
  sort: opts.sort,
18548
18619
  query: opts.query
18549
18620
  });
18550
- const data = await ravenFetch(connection, path70);
18621
+ const data = await ravenFetch(connection, path71);
18551
18622
  const results = data.Results ?? [];
18552
18623
  const totalResults = data.TotalResults ?? 0;
18553
18624
  if (results.length === 0) break;
@@ -20967,8 +21038,8 @@ async function handlePostSynthesis(synthesisPath, options2) {
20967
21038
  // src/commands/review/prepareReviewDir.ts
20968
21039
  import { existsSync as existsSync45, mkdirSync as mkdirSync18, unlinkSync as unlinkSync16, writeFileSync as writeFileSync36 } from "fs";
20969
21040
  function clearReviewFiles(paths) {
20970
- for (const path70 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
20971
- if (existsSync45(path70)) unlinkSync16(path70);
21041
+ for (const path71 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
21042
+ if (existsSync45(path71)) unlinkSync16(path71);
20972
21043
  }
20973
21044
  }
20974
21045
  function prepareReviewDir(paths, requestBody, force) {
@@ -22116,8 +22187,8 @@ import chalk191 from "chalk";
22116
22187
 
22117
22188
  // src/commands/seq/fetchSeq.ts
22118
22189
  import chalk188 from "chalk";
22119
- async function fetchSeq(conn, path70, params) {
22120
- const url = `${conn.url}${path70}?${params}`;
22190
+ async function fetchSeq(conn, path71, params) {
22191
+ const url = `${conn.url}${path71}?${params}`;
22121
22192
  const response = await fetch(url, {
22122
22193
  headers: {
22123
22194
  Accept: "application/json",
@@ -23604,9 +23675,9 @@ function findPortFile(roamDir) {
23604
23675
  return void 0;
23605
23676
  }
23606
23677
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
23607
- const path70 = join62(roamDir, name);
23678
+ const path71 = join62(roamDir, name);
23608
23679
  try {
23609
- return { path: path70, mtimeMs: statSync8(path70).mtimeMs };
23680
+ return { path: path71, mtimeMs: statSync8(path71).mtimeMs };
23610
23681
  } catch {
23611
23682
  return void 0;
23612
23683
  }
@@ -24025,11 +24096,11 @@ function findLinkIndex() {
24025
24096
  function parseLinkArgs() {
24026
24097
  const idx = findLinkIndex();
24027
24098
  if (idx === -1) return null;
24028
- const path70 = process.argv[idx + 1];
24099
+ const path71 = process.argv[idx + 1];
24029
24100
  const rest = process.argv.slice(idx + 2);
24030
24101
  const { value: prefix2 } = extractOption(rest, "--prefix");
24031
24102
  if (!prefix2) return null;
24032
- return { path: path70, prefix: prefix2 };
24103
+ return { path: path71, prefix: prefix2 };
24033
24104
  }
24034
24105
  function hasDuplicateLink(runList, linkPath) {
24035
24106
  return runList.some(
@@ -24910,6 +24981,7 @@ function dismissSession(sessions, id) {
24910
24981
  if (!s) return false;
24911
24982
  if (s.status !== "done") s.pty?.kill();
24912
24983
  s.activityWatcher?.close();
24984
+ s.transcriptWatcher?.close();
24913
24985
  removeActivity(s.id);
24914
24986
  if (s.activity?.itemId != null) releaseLock(s.activity.itemId);
24915
24987
  sessions.delete(id);
@@ -25016,113 +25088,18 @@ function logSpawnedSession(session) {
25016
25088
  );
25017
25089
  }
25018
25090
 
25019
- // src/commands/sessions/daemon/setStatus.ts
25020
- function setStatus2(session, newStatus) {
25021
- const now = Date.now();
25022
- if (session.status === "running" && session.runningSince != null) {
25023
- session.runningMs += now - session.runningSince;
25024
- }
25025
- session.runningSince = newStatus === "running" ? now : null;
25026
- session.status = newStatus;
25027
- }
25028
-
25029
- // src/commands/sessions/daemon/shouldAutoDismiss.ts
25030
- function shouldAutoDismiss(session, exitCode) {
25031
- if (session.status !== "done" || exitCode !== 0) {
25032
- return false;
25033
- }
25034
- if (session.reviewStarted) {
25035
- return session.autoAdvance === true;
25036
- }
25037
- const args = session.assistArgs;
25038
- if (args === void 0) return false;
25039
- if (args[0] === "update") return true;
25040
- return args.includes("--once") && args[0] !== "next";
25041
- }
25042
-
25043
- // src/commands/sessions/daemon/shouldAutoRun.ts
25044
- function shouldAutoRun(session) {
25045
- if (!session.autoRun) return { run: false, reason: null };
25046
- if (session.status !== "done")
25047
- return {
25048
- run: false,
25049
- reason: `session status is "${session.status}", not "done"`
25050
- };
25051
- if (session.commandType !== "assist")
25052
- return {
25053
- run: false,
25054
- reason: `command type is "${session.commandType}", not "assist"`
25055
- };
25056
- const cmd = session.assistArgs?.[0];
25057
- if (cmd !== "draft" && cmd !== "bug" && cmd !== "refine")
25058
- return {
25059
- run: false,
25060
- reason: `command "${cmd ?? "(none)"}" is not draft/bug/refine`
25061
- };
25062
- const itemId2 = session.activity?.itemId;
25063
- if (itemId2 == null)
25064
- return {
25065
- run: false,
25066
- reason: "no itemId in activity (draft did not create an item)"
25067
- };
25068
- return { run: true, itemId: itemId2 };
25069
- }
25070
-
25071
- // src/commands/sessions/daemon/watchEscInterrupt.ts
25072
- var ESC3 = "\x1B";
25073
- var SETTLE_MS = 1e3;
25074
- function isInterruptKey(data) {
25075
- return data === ESC3;
25076
- }
25077
- function scheduleSettle(session, onStatusChange) {
25078
- if (session.escInterruptTimer) clearTimeout(session.escInterruptTimer);
25079
- session.escInterruptTimer = setTimeout(() => {
25080
- session.escInterruptTimer = void 0;
25081
- if (session.status !== "running") return;
25082
- daemonLog(`session ${session.id} esc-interrupt settled -> waiting`);
25083
- onStatusChange(session, "waiting");
25084
- }, SETTLE_MS);
25085
- }
25086
- function watchEscInterrupt(session, data, onStatusChange) {
25087
- if (session.status !== "running") return;
25088
- if (!isInterruptKey(data)) return;
25089
- scheduleSettle(session, onStatusChange);
25090
- }
25091
- function noteOutputForEscInterrupt(session, onStatusChange) {
25092
- if (!session.escInterruptTimer) return;
25093
- scheduleSettle(session, onStatusChange);
25094
- }
25095
- function disarmEscInterrupt(session) {
25096
- if (!session.escInterruptTimer) return;
25097
- clearTimeout(session.escInterruptTimer);
25098
- session.escInterruptTimer = void 0;
25099
- }
25100
-
25101
- // src/commands/sessions/daemon/applyStatusChange.ts
25102
- function applyStatusChange(session, status3, exitCode, dismiss, notify2, reuseForRun) {
25103
- disarmEscInterrupt(session);
25104
- if (session.status === status3) return;
25105
- daemonLog(`session ${session.id} status: ${session.status} -> ${status3}`);
25106
- void flushPhaseActiveMs(session);
25107
- setStatus2(session, status3);
25108
- const autoRun = shouldAutoRun(session);
25109
- if (autoRun.run) {
25110
- reuseForRun(session, autoRun.itemId);
25111
- notify2();
25112
- return;
25113
- }
25114
- 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) {
25115
25095
  daemonLog(
25116
- `session ${session.id} auto-run enabled but skipped (exit ${exitCode ?? "none"}): ${autoRun.reason}`
25096
+ `set-status for unknown session id=${id} status=${status3} (no live session; ignoring)`
25117
25097
  );
25098
+ return;
25118
25099
  }
25119
- if (shouldAutoDismiss(session, exitCode)) dismiss(session.id);
25120
- else notify2();
25121
- }
25122
-
25123
- // src/commands/sessions/daemon/makeStatusChangeHandler.ts
25124
- function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
25125
- 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);
25126
25103
  }
25127
25104
 
25128
25105
  // src/shared/db/recordPhaseCycleContext.ts
@@ -25281,12 +25258,102 @@ function recordSessionUsage(sessions, claudeSessionId, transcriptPath, usedPct,
25281
25258
  return false;
25282
25259
  }
25283
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
+
25284
25351
  // src/commands/sessions/daemon/errorTail.ts
25285
- var ESC4 = String.fromCharCode(27);
25352
+ var ESC3 = String.fromCharCode(27);
25286
25353
  var CSI = String.fromCharCode(155);
25287
25354
  var BEL = String.fromCharCode(7);
25288
25355
  var ANSI = new RegExp(
25289
- String.raw`[${ESC4}${CSI}][[\]()#;?]*(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?${BEL}|(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])`,
25356
+ String.raw`[${ESC3}${CSI}][[\]()#;?]*(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?${BEL}|(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~])`,
25290
25357
  "g"
25291
25358
  );
25292
25359
  var MAX_TAIL_LINES = 40;
@@ -25354,10 +25421,10 @@ async function seedRunningMsFromUsage(session, notify2) {
25354
25421
 
25355
25422
  // src/commands/sessions/daemon/watchActivity.ts
25356
25423
  var DEBOUNCE_MS = 50;
25357
- function watchActivity(session, notify2) {
25424
+ function watchActivity(session, notify2, onClaudeSessionId) {
25358
25425
  if (session.commandType !== "assist" || !session.cwd) return;
25359
- const path70 = activityPath(session.id);
25360
- const dir = dirname29(path70);
25426
+ const path71 = activityPath(session.id);
25427
+ const dir = dirname29(path71);
25361
25428
  try {
25362
25429
  mkdirSync25(dir, { recursive: true });
25363
25430
  } catch {
@@ -25367,21 +25434,23 @@ function watchActivity(session, notify2) {
25367
25434
  let timer = null;
25368
25435
  const read = () => {
25369
25436
  timer = null;
25370
- const activity2 = readActivity(path70);
25437
+ const activity2 = readActivity(path71);
25371
25438
  if (!activity2) return;
25372
25439
  session.activity = activity2;
25373
- if (activity2.claudeSessionId)
25440
+ if (activity2.claudeSessionId) {
25374
25441
  session.claudeSessionId = activity2.claudeSessionId;
25442
+ onClaudeSessionId?.(session);
25443
+ }
25375
25444
  applyReviewPause(session, activity2);
25376
25445
  void seedRunningMsFromUsage(session, notify2);
25377
25446
  notify2();
25378
25447
  };
25379
25448
  session.activityWatcher = watch(dir, (_event, filename) => {
25380
- if (filename && !path70.endsWith(filename)) return;
25449
+ if (filename && !path71.endsWith(filename)) return;
25381
25450
  if (timer) clearTimeout(timer);
25382
25451
  timer = setTimeout(read, DEBOUNCE_MS);
25383
25452
  });
25384
- if (existsSync58(path70)) read();
25453
+ if (existsSync58(path71)) read();
25385
25454
  }
25386
25455
  function refreshActivity(session) {
25387
25456
  if (session.commandType !== "assist" || !session.cwd) return;
@@ -25444,7 +25513,6 @@ function wirePtyEvents(session, clients, onStatusChange) {
25444
25513
  if (!session.pty) return;
25445
25514
  session.pty.onData((data) => {
25446
25515
  appendScrollback(session, data);
25447
- noteOutputForEscInterrupt(session, onStatusChange);
25448
25516
  broadcast(clients, { type: "output", sessionId: session.id, data });
25449
25517
  });
25450
25518
  session.pty.onExit(
@@ -25612,6 +25680,222 @@ function errorSession(id, persisted, error) {
25612
25680
  };
25613
25681
  }
25614
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
+
25615
25899
  // src/commands/sessions/daemon/runningSession.ts
25616
25900
  function runningSession(base, persisted, pty2) {
25617
25901
  return restoredSession(base, persisted, pty2, "running");
@@ -25635,17 +25919,16 @@ function restoredSession(base, persisted, pty2, status3) {
25635
25919
  }
25636
25920
 
25637
25921
  // src/commands/sessions/daemon/restoreInteractiveSession.ts
25638
- function restoreInteractiveSession(id, persisted, base) {
25922
+ function restoreInteractiveSession(id, persisted, base, idle) {
25639
25923
  if (persisted.commandType !== "run" && persisted.claudeSessionId) {
25640
- return resumeViaClaude(id, persisted, base);
25924
+ return resumeViaClaude(id, persisted, base, idle);
25641
25925
  }
25642
25926
  if (persisted.commandType === "claude") {
25643
25927
  return unrecoverableClaude(id, persisted);
25644
25928
  }
25645
25929
  return notRestoredStub(base, persisted);
25646
25930
  }
25647
- function resumeViaClaude(id, persisted, base) {
25648
- const idle = persisted.status !== "running";
25931
+ function resumeViaClaude(id, persisted, base, idle) {
25649
25932
  const pty2 = spawnClaude2({
25650
25933
  resumeSessionId: persisted.claudeSessionId,
25651
25934
  prompt: idle ? void 0 : buildResumePrompt(),
@@ -25694,8 +25977,8 @@ function isUpdate(persisted) {
25694
25977
  function restoreSession(id, persisted) {
25695
25978
  const base = restoreBase(id, persisted);
25696
25979
  if (isUpdate(persisted)) return updatedSession(id, persisted);
25980
+ const idle = deriveRestoreStatus(persisted) !== "running";
25697
25981
  if (needsWrapperRelaunch(persisted)) {
25698
- const idle = persisted.status !== "running";
25699
25982
  const pty2 = spawnPty(
25700
25983
  assistResumeArgs(persisted),
25701
25984
  persisted.cwd,
@@ -25704,7 +25987,7 @@ function restoreSession(id, persisted) {
25704
25987
  );
25705
25988
  return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
25706
25989
  }
25707
- return restoreInteractiveSession(id, persisted, base);
25990
+ return restoreInteractiveSession(id, persisted, base, idle);
25708
25991
  }
25709
25992
  function needsWrapperRelaunch(persisted) {
25710
25993
  return isBacklogRun(persisted) || isOnceLaunch(persisted) && !!persisted.claudeSessionId;
@@ -25845,6 +26128,77 @@ function shutdownSessions(sessions) {
25845
26128
  }
25846
26129
  }
25847
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
+
25848
26202
  // src/commands/sessions/daemon/connectToWindowsDaemon.ts
25849
26203
  import * as net2 from "net";
25850
26204
 
@@ -25960,13 +26314,13 @@ async function defaultConnect() {
25960
26314
  }
25961
26315
 
25962
26316
  // src/commands/sessions/daemon/hasPersistedWindowsSessions.ts
25963
- import { existsSync as existsSync59, readFileSync as readFileSync49 } from "fs";
26317
+ import { existsSync as existsSync61, readFileSync as readFileSync49 } from "fs";
25964
26318
  import { posix } from "path";
25965
26319
  function hasPersistedWindowsSessions() {
25966
26320
  const sessionsFile = windowsSessionsFileFromWsl();
25967
26321
  if (!sessionsFile) return false;
25968
26322
  try {
25969
- if (!existsSync59(sessionsFile)) return false;
26323
+ if (!existsSync61(sessionsFile)) return false;
25970
26324
  const data = JSON.parse(readFileSync49(sessionsFile, "utf8"));
25971
26325
  return Array.isArray(data) && data.length > 0;
25972
26326
  } catch (error) {
@@ -26527,7 +26881,6 @@ function writeToSession(sessions, id, data, onStatusChange) {
26527
26881
  const s = sessions.get(id);
26528
26882
  if (!s || s.status === "done") return;
26529
26883
  s.pty?.write(data);
26530
- watchEscInterrupt(s, data, onStatusChange);
26531
26884
  watchPromptSubmit(s, data, onStatusChange);
26532
26885
  }
26533
26886
  function resizeSession(sessions, id, cols, rows) {
@@ -26607,7 +26960,7 @@ var SessionManager = class {
26607
26960
  drain = () => drainSessions(this.sessions, this.notify);
26608
26961
  add(session) {
26609
26962
  this.wire(session);
26610
- watchActivity(session, this.notify);
26963
+ wireSessionWatchers(session, this.notify, this.onStatusChange);
26611
26964
  logSpawnedSession(session);
26612
26965
  return session.id;
26613
26966
  }
@@ -26670,27 +27023,20 @@ var SessionManager = class {
26670
27023
  setStarred(id, starred) {
26671
27024
  if (setStarred2(this.sessions, id, starred)) this.notify();
26672
27025
  }
26673
- setStatus(id, status3) {
26674
- const session = this.sessions.get(id);
26675
- if (!session) {
26676
- daemonLog(
26677
- `set-status for unknown session id=${id} status=${status3} (no live session; ignoring)`
26678
- );
26679
- return;
26680
- }
26681
- this.onStatusChange(session, status3);
27026
+ setStatus(id, status3, source) {
27027
+ applySetStatus(this.sessions, id, status3, source, this.onStatusChange);
26682
27028
  }
26683
27029
  /* why: the status line relays token totals keyed by Claude's session id; join
26684
27030
  * it to the running backlog phase so the spend accumulates against that row. */
26685
27031
  recordUsage(claudeSessionId, transcriptPath, usedPct) {
26686
- if (recordSessionUsage(
27032
+ applyUsageRecord(
26687
27033
  this.sessions.values(),
27034
+ this.clients.currentWindows(),
27035
+ this.notify,
26688
27036
  claudeSessionId,
26689
27037
  transcriptPath,
26690
- usedPct,
26691
- this.clients.currentWindows()
26692
- ))
26693
- this.notify();
27038
+ usedPct
27039
+ );
26694
27040
  }
26695
27041
  listSessions = () => {
26696
27042
  const local = [...this.sessions.values()].map(toSessionInfo);
@@ -26711,8 +27057,30 @@ import * as net3 from "net";
26711
27057
  // src/commands/sessions/daemon/handleConnection.ts
26712
27058
  import { createInterface as createInterface9 } from "readline";
26713
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
+
26714
27082
  // src/commands/sessions/shared/parseTranscript.ts
26715
- import * as fs37 from "fs";
27083
+ import * as fs39 from "fs";
26716
27084
 
26717
27085
  // src/commands/sessions/shared/toolTarget.ts
26718
27086
  function toolTarget(input) {
@@ -26764,7 +27132,7 @@ async function parseTranscript(sessionId) {
26764
27132
  const filePath = await findSessionJsonlPath(sessionId);
26765
27133
  if (!filePath) return [];
26766
27134
  try {
26767
- const raw = await fs37.promises.readFile(filePath, "utf8");
27135
+ const raw = await fs39.promises.readFile(filePath, "utf8");
26768
27136
  return parseTranscriptLines(raw.split("\n"));
26769
27137
  } catch {
26770
27138
  return [];
@@ -26828,12 +27196,6 @@ function spawnCreate(m, d) {
26828
27196
  }
26829
27197
 
26830
27198
  // src/commands/sessions/daemon/messageHandlers.ts
26831
- function creator(isNew, spawn12) {
26832
- return (client, m, d) => {
26833
- if (m.windowsProxy.route(client, d)) return;
26834
- sendTo(client, { type: "created", sessionId: spawn12(m, d), isNew });
26835
- };
26836
- }
26837
27199
  function routed(local) {
26838
27200
  return (client, m, d) => {
26839
27201
  if (!m.windowsProxy.route(client, d)) local(client, m, d);
@@ -26902,11 +27264,7 @@ var messageHandlers = {
26902
27264
  (_client, m, d) => m.setStarred(d.sessionId, d.starred)
26903
27265
  ),
26904
27266
  "set-active": (_client, m, d) => m.active.set(d.cwd, d.sessionId),
26905
- "set-status": (client, m, d) => {
26906
- daemonLog(`set-status received: id=${d.sessionId} status=${d.status}`);
26907
- if (!m.windowsProxy.route(client, d))
26908
- m.setStatus(d.sessionId, d.status);
26909
- },
27267
+ "set-status": handleSetStatus,
26910
27268
  "ui-status": (_client, _m, d) => daemonLog(`ui rendered: id=${d.sessionId} status=${d.status}`)
26911
27269
  };
26912
27270
 
@@ -27092,18 +27450,33 @@ function registerDaemon(program2) {
27092
27450
  ).action(drainDaemon);
27093
27451
  }
27094
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
+
27095
27468
  // src/commands/sessions/summarise/index.ts
27096
- import * as fs39 from "fs";
27469
+ import * as fs41 from "fs";
27097
27470
  import chalk200 from "chalk";
27098
27471
 
27099
27472
  // src/commands/sessions/summarise/shared.ts
27100
- import * as fs38 from "fs";
27473
+ import * as fs40 from "fs";
27101
27474
  function writeSummary(jsonlPath2, summary) {
27102
- fs38.writeFileSync(summaryPathFor(jsonlPath2), `${summary.trim()}
27475
+ fs40.writeFileSync(summaryPathFor(jsonlPath2), `${summary.trim()}
27103
27476
  `, "utf8");
27104
27477
  }
27105
27478
  function hasSummary(jsonlPath2) {
27106
- return fs38.existsSync(summaryPathFor(jsonlPath2));
27479
+ return fs40.existsSync(summaryPathFor(jsonlPath2));
27107
27480
  }
27108
27481
  function summaryPathFor(jsonlPath2) {
27109
27482
  return jsonlPath2.replace(/\.jsonl$/, ".summary");
@@ -27190,7 +27563,7 @@ function selectCandidates(files, options2) {
27190
27563
  const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
27191
27564
  candidates.sort((a, b) => {
27192
27565
  try {
27193
- return fs39.statSync(b).mtimeMs - fs39.statSync(a).mtimeMs;
27566
+ return fs41.statSync(b).mtimeMs - fs41.statSync(a).mtimeMs;
27194
27567
  } catch {
27195
27568
  return 0;
27196
27569
  }
@@ -27228,9 +27601,7 @@ function registerSessions(program2) {
27228
27601
  (options2, command) => web({ ...options2, open: command.optsWithGlobals().open })
27229
27602
  );
27230
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);
27231
- cmd.command("set-status <status>").description(
27232
- "Report the current session's status to the sessions daemon (used by Claude Code hooks)"
27233
- ).action(setSessionStatus);
27604
+ registerSetStatusCommand(cmd);
27234
27605
  configHelp(cmd, [
27235
27606
  {
27236
27607
  key: "sessions.windowsProjectsRoot",
@@ -27331,9 +27702,9 @@ function buildLimitsSegment(rateLimits) {
27331
27702
 
27332
27703
  // src/commands/readGitBranch.ts
27333
27704
  import { readFileSync as readFileSync52, statSync as statSync11 } from "fs";
27334
- import { isAbsolute as isAbsolute2, join as join68, resolve as resolve17 } from "path";
27705
+ import { isAbsolute as isAbsolute2, join as join69, resolve as resolve17 } from "path";
27335
27706
  function resolveGitDir(cwd) {
27336
- const dotGit = join68(cwd, ".git");
27707
+ const dotGit = join69(cwd, ".git");
27337
27708
  let stat2;
27338
27709
  try {
27339
27710
  stat2 = statSync11(dotGit);
@@ -27363,7 +27734,7 @@ function readGitBranch(cwd) {
27363
27734
  }
27364
27735
  let head;
27365
27736
  try {
27366
- head = readFileSync52(join68(gitDir, "HEAD"), "utf8");
27737
+ head = readFileSync52(join69(gitDir, "HEAD"), "utf8");
27367
27738
  } catch {
27368
27739
  return null;
27369
27740
  }
@@ -27431,21 +27802,21 @@ async function statusLine() {
27431
27802
  }
27432
27803
 
27433
27804
  // src/commands/sync.ts
27434
- import * as fs46 from "fs";
27435
- import * as os4 from "os";
27436
- import * as path68 from "path";
27805
+ import * as fs48 from "fs";
27806
+ import * as os5 from "os";
27807
+ import * as path69 from "path";
27437
27808
  import { fileURLToPath as fileURLToPath9 } from "url";
27438
27809
 
27439
27810
  // src/commands/sync/syncClaudeMd.ts
27440
- import * as fs40 from "fs";
27441
- import * as path60 from "path";
27811
+ import * as fs42 from "fs";
27812
+ import * as path61 from "path";
27442
27813
  import chalk203 from "chalk";
27443
27814
  async function syncClaudeMd(claudeDir, targetBase, options2) {
27444
- const source = path60.join(claudeDir, "CLAUDE.md");
27445
- const target = path60.join(targetBase, "CLAUDE.md");
27446
- const sourceContent = fs40.readFileSync(source, "utf8");
27447
- if (fs40.existsSync(target)) {
27448
- const targetContent = fs40.readFileSync(target, "utf8");
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");
27449
27820
  if (sourceContent !== targetContent) {
27450
27821
  console.log(
27451
27822
  chalk203.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
@@ -27462,38 +27833,38 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
27462
27833
  }
27463
27834
  }
27464
27835
  }
27465
- fs40.copyFileSync(source, target);
27836
+ fs42.copyFileSync(source, target);
27466
27837
  console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
27467
27838
  }
27468
27839
 
27469
27840
  // src/commands/sync/syncCodex.ts
27470
- import * as path63 from "path";
27841
+ import * as path64 from "path";
27471
27842
 
27472
27843
  // src/commands/sync/installHarnessCommands.ts
27473
- import * as fs41 from "fs";
27474
- import * as path61 from "path";
27844
+ import * as fs43 from "fs";
27845
+ import * as path62 from "path";
27475
27846
  function installHarnessCommands(claudeDir, harness, transform) {
27476
- const commandsSource = path61.join(claudeDir, "commands");
27477
- const files = fs41.readdirSync(commandsSource);
27847
+ const commandsSource = path62.join(claudeDir, "commands");
27848
+ const files = fs43.readdirSync(commandsSource);
27478
27849
  let synced = 0;
27479
27850
  for (const file of files) {
27480
27851
  if (!file.endsWith(".md")) continue;
27481
27852
  const name = file.replace(/\.md$/, "");
27482
- const content = fs41.readFileSync(path61.join(commandsSource, file), "utf8");
27483
- const target = path61.join(harness.homeDir, harness.sync.commandDest(name));
27484
- fs41.mkdirSync(path61.dirname(target), { recursive: true });
27485
- fs41.writeFileSync(target, transform(name, content));
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));
27486
27857
  synced++;
27487
27858
  }
27488
- const agentsTarget = path61.join(harness.homeDir, harness.sync.agentsFile);
27489
- fs41.mkdirSync(path61.dirname(agentsTarget), { recursive: true });
27490
- fs41.copyFileSync(path61.join(claudeDir, "CLAUDE.md"), agentsTarget);
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);
27491
27862
  return { total: files.length, synced };
27492
27863
  }
27493
27864
 
27494
27865
  // src/commands/sync/syncCodexHooks.ts
27495
- import * as fs42 from "fs";
27496
- import * as path62 from "path";
27866
+ import * as fs44 from "fs";
27867
+ import * as path63 from "path";
27497
27868
  var BEGIN = "# >>> assist codex hooks (managed) \u2014 do not edit >>>";
27498
27869
  var END = "# <<< assist codex hooks (managed) <<<";
27499
27870
  function upsertManagedBlock(existing, body) {
@@ -27523,11 +27894,11 @@ ${rest}
27523
27894
  `;
27524
27895
  }
27525
27896
  function syncCodexHooks(sourcePath) {
27526
- const body = fs42.readFileSync(sourcePath, "utf8");
27527
- const configPath = path62.join(harnesses.codex.homeDir, "config.toml");
27528
- const existing = fs42.existsSync(configPath) ? fs42.readFileSync(configPath, "utf8") : "";
27529
- fs42.mkdirSync(path62.dirname(configPath), { recursive: true });
27530
- fs42.writeFileSync(configPath, upsertManagedBlock(existing, body));
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));
27531
27902
  console.log(
27532
27903
  "Registered assist codex-hook in ~/.codex/config.toml (PreToolUse, PermissionRequest)"
27533
27904
  );
@@ -27558,50 +27929,50 @@ function syncCodex(claudeDir) {
27558
27929
  harnesses.codex,
27559
27930
  commandToSkill
27560
27931
  );
27561
- syncCodexHooks(path63.join(claudeDir, "..", "codex", "config.toml"));
27932
+ syncCodexHooks(path64.join(claudeDir, "..", "codex", "config.toml"));
27562
27933
  console.log(
27563
27934
  `Synced ${total} skill(s) to ~/.codex/skills and CLAUDE.md to ~/.codex/AGENTS.md`
27564
27935
  );
27565
27936
  }
27566
27937
 
27567
27938
  // src/commands/sync/syncDesign.ts
27568
- import * as fs43 from "fs";
27569
- import * as path64 from "path";
27939
+ import * as fs45 from "fs";
27940
+ import * as path65 from "path";
27570
27941
  function syncDesign(claudeDir, targetBase) {
27571
- const systemPromptSource = path64.join(claudeDir, "system-prompt.md");
27572
- const systemPromptTarget = path64.join(targetBase, "system-prompt.md");
27573
- fs43.copyFileSync(systemPromptSource, systemPromptTarget);
27942
+ const systemPromptSource = path65.join(claudeDir, "system-prompt.md");
27943
+ const systemPromptTarget = path65.join(targetBase, "system-prompt.md");
27944
+ fs45.copyFileSync(systemPromptSource, systemPromptTarget);
27574
27945
  console.log("Copied system-prompt.md to ~/.claude/system-prompt.md");
27575
- const skillsSource = path64.join(claudeDir, "skills");
27576
- const skillsTarget = path64.join(targetBase, "skills");
27577
- fs43.mkdirSync(skillsTarget, { recursive: true });
27578
- const files = fs43.readdirSync(skillsSource);
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);
27579
27950
  for (const file of files) {
27580
- fs43.copyFileSync(
27581
- path64.join(skillsSource, file),
27582
- path64.join(skillsTarget, file)
27951
+ fs45.copyFileSync(
27952
+ path65.join(skillsSource, file),
27953
+ path65.join(skillsTarget, file)
27583
27954
  );
27584
27955
  }
27585
27956
  console.log(`Synced ${files.length} design skill(s) to ~/.claude/skills`);
27586
27957
  }
27587
27958
 
27588
27959
  // src/commands/sync/syncPi.ts
27589
- import * as path66 from "path";
27960
+ import * as path67 from "path";
27590
27961
 
27591
27962
  // src/commands/sync/syncPiHooks.ts
27592
- import * as fs44 from "fs";
27593
- import * as path65 from "path";
27963
+ import * as fs46 from "fs";
27964
+ import * as path66 from "path";
27594
27965
  function piExtensionsDir() {
27595
- return path65.join(harnesses.pi.homeDir, "extensions");
27966
+ return path66.join(harnesses.pi.homeDir, "extensions");
27596
27967
  }
27597
27968
  function syncPiHooks(sourceDir) {
27598
27969
  const target = piExtensionsDir();
27599
- fs44.mkdirSync(target, { recursive: true });
27600
- const files = fs44.readdirSync(sourceDir).filter((f) => f.endsWith(".ts"));
27970
+ fs46.mkdirSync(target, { recursive: true });
27971
+ const files = fs46.readdirSync(sourceDir).filter((f) => f.endsWith(".ts"));
27601
27972
  for (const file of files) {
27602
- fs44.copyFileSync(
27603
- path65.join(sourceDir, file),
27604
- path65.join(target, `assist-${file}`)
27973
+ fs46.copyFileSync(
27974
+ path66.join(sourceDir, file),
27975
+ path66.join(target, `assist-${file}`)
27605
27976
  );
27606
27977
  }
27607
27978
  console.log(
@@ -27638,23 +28009,23 @@ function syncPi(claudeDir) {
27638
28009
  harnesses.pi,
27639
28010
  commandToPrompt
27640
28011
  );
27641
- syncPiHooks(path66.join(claudeDir, "..", "pi"));
28012
+ syncPiHooks(path67.join(claudeDir, "..", "pi"));
27642
28013
  console.log(
27643
28014
  `Synced ${synced} prompt(s) to ~/.pi/agent/prompts and CLAUDE.md to ~/.pi/agent/AGENTS.md`
27644
28015
  );
27645
28016
  }
27646
28017
 
27647
28018
  // src/commands/sync/syncSettings.ts
27648
- import * as fs45 from "fs";
27649
- import * as path67 from "path";
28019
+ import * as fs47 from "fs";
28020
+ import * as path68 from "path";
27650
28021
  import chalk204 from "chalk";
27651
28022
  async function syncSettings(claudeDir, targetBase, options2) {
27652
- const source = path67.join(claudeDir, "settings.json");
27653
- const target = path67.join(targetBase, "settings.json");
27654
- const sourceContent = fs45.readFileSync(source, "utf8");
28023
+ const source = path68.join(claudeDir, "settings.json");
28024
+ const target = path68.join(targetBase, "settings.json");
28025
+ const sourceContent = fs47.readFileSync(source, "utf8");
27655
28026
  const sourceSettings = JSON.parse(sourceContent);
27656
- const targetExists = fs45.existsSync(target);
27657
- const targetContent = targetExists ? fs45.readFileSync(target, "utf8") : "";
28027
+ const targetExists = fs47.existsSync(target);
28028
+ const targetContent = targetExists ? fs47.readFileSync(target, "utf8") : "";
27658
28029
  const preservedUserSettings = targetExists ? JSON.parse(targetContent) : {};
27659
28030
  const mergedContent = JSON.stringify(
27660
28031
  { ...preservedUserSettings, ...sourceSettings },
@@ -27683,18 +28054,18 @@ async function syncSettings(claudeDir, targetBase, options2) {
27683
28054
  }
27684
28055
  }
27685
28056
  }
27686
- fs45.writeFileSync(target, mergedContent);
28057
+ fs47.writeFileSync(target, mergedContent);
27687
28058
  console.log("Copied settings.json to ~/.claude/settings.json");
27688
28059
  }
27689
28060
 
27690
28061
  // src/commands/sync.ts
27691
28062
  var __filename5 = fileURLToPath9(import.meta.url);
27692
- var __dirname7 = path68.dirname(__filename5);
28063
+ var __dirname7 = path69.dirname(__filename5);
27693
28064
  async function sync(options2) {
27694
28065
  const config = loadConfig();
27695
28066
  const yes = options2?.yes ?? config.sync.autoConfirm;
27696
- const claudeDir = path68.join(__dirname7, "..", "claude");
27697
- const targetBase = path68.join(os4.homedir(), ".claude");
28067
+ const claudeDir = path69.join(__dirname7, "..", "claude");
28068
+ const targetBase = path69.join(os5.homedir(), ".claude");
27698
28069
  syncCommands(claudeDir, targetBase);
27699
28070
  syncDesign(claudeDir, targetBase);
27700
28071
  await syncSettings(claudeDir, targetBase, { yes });
@@ -27703,12 +28074,12 @@ async function sync(options2) {
27703
28074
  syncPi(claudeDir);
27704
28075
  }
27705
28076
  function syncCommands(claudeDir, targetBase) {
27706
- const sourceDir = path68.join(claudeDir, "commands");
27707
- const targetDir = path68.join(targetBase, "commands");
27708
- fs46.mkdirSync(targetDir, { recursive: true });
27709
- const files = fs46.readdirSync(sourceDir);
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);
27710
28081
  for (const file of files) {
27711
- fs46.copyFileSync(path68.join(sourceDir, file), path68.join(targetDir, file));
28082
+ fs48.copyFileSync(path69.join(sourceDir, file), path69.join(targetDir, file));
27712
28083
  console.log(`Copied ${file} to ${targetDir}`);
27713
28084
  }
27714
28085
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -27716,7 +28087,7 @@ function syncCommands(claudeDir, targetBase) {
27716
28087
 
27717
28088
  // src/commands/update.ts
27718
28089
  import { execSync as execSync59 } from "child_process";
27719
- import * as path69 from "path";
28090
+ import * as path70 from "path";
27720
28091
 
27721
28092
  // src/commands/restartDaemonAfterUpdate.ts
27722
28093
  async function restartDaemonAfterUpdate() {
@@ -27735,12 +28106,12 @@ async function restartDaemonAfterUpdate() {
27735
28106
  // src/commands/update.ts
27736
28107
  function isGlobalNpmInstall(dir) {
27737
28108
  try {
27738
- const resolved = path69.resolve(dir);
27739
- if (resolved.split(path69.sep).includes("node_modules")) {
28109
+ const resolved = path70.resolve(dir);
28110
+ if (resolved.split(path70.sep).includes("node_modules")) {
27740
28111
  return true;
27741
28112
  }
27742
28113
  const globalPrefix = execSync59("npm prefix -g", { stdio: "pipe" }).toString().trim();
27743
- return resolved.toLowerCase().startsWith(path69.resolve(globalPrefix).toLowerCase());
28114
+ return resolved.toLowerCase().startsWith(path70.resolve(globalPrefix).toLowerCase());
27744
28115
  } catch {
27745
28116
  return false;
27746
28117
  }