@staff0rd/assist 0.488.4 → 0.489.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.488.4",
9
+ version: "0.489.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -2415,8 +2415,8 @@ function printDiff(oldContent, newContent) {
2415
2415
  normalizeJson(newContent)
2416
2416
  );
2417
2417
  for (const change of changes) {
2418
- const lines = change.value.replace(/\n$/, "").split("\n");
2419
- for (const line of lines) {
2418
+ const lines2 = change.value.replace(/\n$/, "").split("\n");
2419
+ for (const line of lines2) {
2420
2420
  if (change.added) {
2421
2421
  console.log(chalk12.green(`+ ${line}`));
2422
2422
  } else if (change.removed) {
@@ -2863,11 +2863,11 @@ function removeVscodeFromGitignore() {
2863
2863
  return;
2864
2864
  }
2865
2865
  const content = fs3.readFileSync(gitignorePath, "utf8");
2866
- const lines = content.split("\n");
2867
- const filteredLines = lines.filter(
2866
+ const lines2 = content.split("\n");
2867
+ const filteredLines = lines2.filter(
2868
2868
  (line) => !line.trim().toLowerCase().includes(".vscode")
2869
2869
  );
2870
- if (filteredLines.length !== lines.length) {
2870
+ if (filteredLines.length !== lines2.length) {
2871
2871
  fs3.writeFileSync(gitignorePath, filteredLines.join("\n"));
2872
2872
  console.log(chalk20.dim("Removed .vscode references from .gitignore"));
2873
2873
  }
@@ -3269,11 +3269,11 @@ ${checkName} failed:
3269
3269
  // src/commands/lint/lint/runImportExtensionCheck.ts
3270
3270
  function checkForImportExtensions(filePath) {
3271
3271
  const content = fs12.readFileSync(filePath, "utf8");
3272
- const lines = content.split("\n");
3272
+ const lines2 = content.split("\n");
3273
3273
  const violations = [];
3274
3274
  const importExtensionPattern = /from\s+["']\..*\.(js|ts)["']/;
3275
- for (let i = 0; i < lines.length; i++) {
3276
- const line = lines[i];
3275
+ for (let i = 0; i < lines2.length; i++) {
3276
+ const line = lines2[i];
3277
3277
  if (importExtensionPattern.test(line)) {
3278
3278
  violations.push({
3279
3279
  filePath,
@@ -3305,12 +3305,12 @@ function runImportExtensionCheck() {
3305
3305
  import fs13 from "fs";
3306
3306
  function checkForDynamicImports(filePath) {
3307
3307
  const content = fs13.readFileSync(filePath, "utf8");
3308
- const lines = content.split("\n");
3308
+ const lines2 = content.split("\n");
3309
3309
  const violations = [];
3310
3310
  const requirePattern = /\brequire\s*\(/;
3311
3311
  const dynamicImportPattern = /\bimport\s*\(/;
3312
- for (let i = 0; i < lines.length; i++) {
3313
- const line = lines[i];
3312
+ for (let i = 0; i < lines2.length; i++) {
3313
+ const line = lines2[i];
3314
3314
  if (requirePattern.test(line) || dynamicImportPattern.test(line)) {
3315
3315
  violations.push({
3316
3316
  filePath,
@@ -3412,9 +3412,9 @@ function collectBicepComments(content) {
3412
3412
  comments3.push({ line, text: match });
3413
3413
  return blankNonNewline(match);
3414
3414
  });
3415
- const lines = work.split("\n");
3416
- for (let i = 0; i < lines.length; i++) {
3417
- const match = lines[i].match(LINE_COMMENT);
3415
+ const lines2 = work.split("\n");
3416
+ for (let i = 0; i < lines2.length; i++) {
3417
+ const match = lines2[i].match(LINE_COMMENT);
3418
3418
  if (match) comments3.push({ line: i + 1, text: match[0] });
3419
3419
  }
3420
3420
  return comments3;
@@ -3429,14 +3429,14 @@ function isHeaderLine(line) {
3429
3429
  return trimmed === "" || trimmed.startsWith("#");
3430
3430
  }
3431
3431
  function collectHashComments(content, options2) {
3432
- const lines = content.split("\n");
3432
+ const lines2 = content.split("\n");
3433
3433
  let start3 = 0;
3434
3434
  if (options2.skipHeader) {
3435
- while (start3 < lines.length && isHeaderLine(lines[start3])) start3++;
3435
+ while (start3 < lines2.length && isHeaderLine(lines2[start3])) start3++;
3436
3436
  }
3437
3437
  const comments3 = [];
3438
- for (let i = start3; i < lines.length; i++) {
3439
- const work = lines[i].replace(
3438
+ for (let i = start3; i < lines2.length; i++) {
3439
+ const work = lines2[i].replace(
3440
3440
  /"(?:[^"\\]|\\.)*"|'(?:[^']|'')*'/g,
3441
3441
  blankNonNewline2
3442
3442
  );
@@ -3470,8 +3470,8 @@ function collectComments(sourceFile) {
3470
3470
  var MAINTAINABILITY_OVERRIDE_MARKER = "assist-maintainability-override";
3471
3471
  var OVERRIDE_MARKER = /^\s*\/\/\s*assist-maintainability-override:?\s*(-?\d+)\s*$/;
3472
3472
  function parseMaintainabilityOverride(content) {
3473
- const lines = content.split("\n").slice(0, 10);
3474
- for (const line of lines) {
3473
+ const lines2 = content.split("\n").slice(0, 10);
3474
+ for (const line of lines2) {
3475
3475
  const match = line.match(OVERRIDE_MARKER);
3476
3476
  if (!match) continue;
3477
3477
  const value = Number(match[1]);
@@ -3508,12 +3508,12 @@ function isCommentExempt(text17) {
3508
3508
  function toSingleLine(text17) {
3509
3509
  return text17.replace(/\s+/g, " ").trim();
3510
3510
  }
3511
- function collectSourceFindings(file, lines, project) {
3511
+ function collectSourceFindings(file, lines2, project) {
3512
3512
  const findings = [];
3513
3513
  const sourceFile = project.addSourceFileAtPath(file);
3514
3514
  for (const { pos, text: text17 } of collectComments(sourceFile)) {
3515
3515
  const { line } = sourceFile.getLineAndColumnAtPos(pos);
3516
- if (!lines.has(line)) continue;
3516
+ if (!lines2.has(line)) continue;
3517
3517
  if (isCommentExempt(text17)) continue;
3518
3518
  findings.push({ file, line, text: toSingleLine(text17) });
3519
3519
  }
@@ -3533,29 +3533,29 @@ function collectYamlComments(content) {
3533
3533
  }
3534
3534
 
3535
3535
  // src/commands/verify/blockCodeComments/collectFileComments.ts
3536
- function toFindings(file, lines, raw, exempt) {
3536
+ function toFindings(file, lines2, raw, exempt) {
3537
3537
  const findings = [];
3538
3538
  for (const { line, text: text17 } of raw) {
3539
- if (!lines.has(line)) continue;
3539
+ if (!lines2.has(line)) continue;
3540
3540
  if (exempt && isCommentExempt(text17)) continue;
3541
3541
  findings.push({ file, line, text: text17.replace(/\s+/g, " ").trim() });
3542
3542
  }
3543
3543
  return findings;
3544
3544
  }
3545
- function collectFileComments(file, lines, project) {
3545
+ function collectFileComments(file, lines2, project) {
3546
3546
  const read = () => fs14.readFileSync(file, "utf8");
3547
3547
  if (isYamlFile(file))
3548
- return toFindings(file, lines, collectYamlComments(read()), false);
3548
+ return toFindings(file, lines2, collectYamlComments(read()), false);
3549
3549
  if (isDockerfile(file) || isEnvFile(file) || isShellFile(file))
3550
3550
  return toFindings(
3551
3551
  file,
3552
- lines,
3552
+ lines2,
3553
3553
  collectHashComments(read(), { skipHeader: isShellFile(file) }),
3554
3554
  true
3555
3555
  );
3556
3556
  if (isBicepFile(file))
3557
- return toFindings(file, lines, collectBicepComments(read()), true);
3558
- return collectSourceFindings(file, lines, project);
3557
+ return toFindings(file, lines2, collectBicepComments(read()), true);
3558
+ return collectSourceFindings(file, lines2, project);
3559
3559
  }
3560
3560
 
3561
3561
  // src/commands/verify/blockCodeComments/parseDiffAddedLines.ts
@@ -3623,9 +3623,9 @@ function findComments(options2) {
3623
3623
  compilerOptions: { allowJs: true }
3624
3624
  });
3625
3625
  const findings = [];
3626
- for (const [file, lines] of addedLines) {
3626
+ for (const [file, lines2] of addedLines) {
3627
3627
  if (!shouldScan(file, options2.ignoreGlobs)) continue;
3628
- findings.push(...collectFileComments(file, lines, project));
3628
+ findings.push(...collectFileComments(file, lines2, project));
3629
3629
  }
3630
3630
  findings.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
3631
3631
  return findings;
@@ -3658,10 +3658,10 @@ function getDocumentedConfigKeys() {
3658
3658
  }
3659
3659
  function renderConfigHelp(entries, preamble) {
3660
3660
  const width = Math.max(...entries.map((entry) => entry.setter.length));
3661
- const lines = entries.map(
3661
+ const lines2 = entries.map(
3662
3662
  (entry) => ` ${entry.setter.padEnd(width)} # ${entry.note}`
3663
3663
  );
3664
- const body = preamble ? ["", preamble, "", "Config:", ...lines] : ["", "Config:", ...lines];
3664
+ const body = preamble ? ["", preamble, "", "Config:", ...lines2] : ["", "Config:", ...lines2];
3665
3665
  return body.join("\n");
3666
3666
  }
3667
3667
  function configHelp(command, entries, preamble) {
@@ -3960,14 +3960,14 @@ function findRuleViolations(data, rule) {
3960
3960
  }
3961
3961
  return violations;
3962
3962
  }
3963
- function findForbiddenStrings(rules, readJson) {
3964
- return rules.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
3963
+ function findForbiddenStrings(rules2, readJson) {
3964
+ return rules2.flatMap((rule) => findRuleViolations(readJson(rule.file), rule));
3965
3965
  }
3966
3966
 
3967
3967
  // src/commands/verify/forbiddenStrings/index.ts
3968
3968
  function forbiddenStrings() {
3969
- const rules = loadConfig().forbiddenStrings ?? [];
3970
- if (rules.length === 0) {
3969
+ const rules2 = loadConfig().forbiddenStrings ?? [];
3970
+ if (rules2.length === 0) {
3971
3971
  console.log("No forbidden-strings rules configured.");
3972
3972
  process.exit(0);
3973
3973
  }
@@ -3988,7 +3988,7 @@ function forbiddenStrings() {
3988
3988
  cache4.set(file, parsed);
3989
3989
  return parsed;
3990
3990
  };
3991
- const violations = findForbiddenStrings(rules, readJson);
3991
+ const violations = findForbiddenStrings(rules2, readJson);
3992
3992
  if (violations.length === 0) {
3993
3993
  console.log("No forbidden strings found.");
3994
3994
  process.exit(0);
@@ -4015,18 +4015,18 @@ function hardcodedColors() {
4015
4015
  const output = execSync13(`grep -rEnH '${pattern}' src/`, {
4016
4016
  encoding: "utf8"
4017
4017
  });
4018
- const lines = output.trim().split("\n").filter((line) => {
4018
+ const lines2 = output.trim().split("\n").filter((line) => {
4019
4019
  const match = line.match(/^(.+?):\d+:/);
4020
4020
  if (!match) return true;
4021
4021
  const file = match[1];
4022
4022
  return !ignoreGlobs.some((glob) => minimatch3(file, glob));
4023
4023
  });
4024
- if (lines.length === 0) {
4024
+ if (lines2.length === 0) {
4025
4025
  console.log("No hardcoded colors found.");
4026
4026
  process.exit(0);
4027
4027
  }
4028
4028
  console.log("Hardcoded colors found:\n");
4029
- for (const line of lines) {
4029
+ for (const line of lines2) {
4030
4030
  const match = line.match(/^(.+):(\d+):(.+)$/);
4031
4031
  if (match) {
4032
4032
  const [, file, lineNum, content] = match;
@@ -4036,7 +4036,7 @@ function hardcodedColors() {
4036
4036
  }
4037
4037
  }
4038
4038
  console.log(`
4039
- Total: ${lines.length} hardcoded color(s)`);
4039
+ Total: ${lines2.length} hardcoded color(s)`);
4040
4040
  console.log("\nUse colors from the 'open-color' (oc) library instead.");
4041
4041
  console.log("\nExample fix:");
4042
4042
  console.log(" Before: color: '#228be6'");
@@ -4523,7 +4523,7 @@ ${failed2.length} script(s) failed:`);
4523
4523
  }
4524
4524
  }
4525
4525
  function runEntry(entry) {
4526
- return new Promise((resolve21) => {
4526
+ return new Promise((resolve22) => {
4527
4527
  const startTime = Date.now();
4528
4528
  const child = spawnCommand(
4529
4529
  entry.fullCommand,
@@ -4535,7 +4535,7 @@ function runEntry(entry) {
4535
4535
  child.on("close", (code) => {
4536
4536
  const exitCode = code ?? 1;
4537
4537
  flushIfFailed(exitCode, chunks);
4538
- resolve21({
4538
+ resolve22({
4539
4539
  script: entry.name,
4540
4540
  code: exitCode,
4541
4541
  durationMs: Date.now() - startTime
@@ -5158,38 +5158,38 @@ var END_MARKER = "# <<< assist backup schedule <<<";
5158
5158
  function buildBlock(every, cronLine) {
5159
5159
  return [BEGIN_MARKER, `# every ${every}`, cronLine, END_MARKER];
5160
5160
  }
5161
- function findBlockRange(lines) {
5162
- const start3 = lines.indexOf(BEGIN_MARKER);
5161
+ function findBlockRange(lines2) {
5162
+ const start3 = lines2.indexOf(BEGIN_MARKER);
5163
5163
  if (start3 === -1) return void 0;
5164
- const end = lines.indexOf(END_MARKER, start3);
5164
+ const end = lines2.indexOf(END_MARKER, start3);
5165
5165
  if (end === -1) return void 0;
5166
5166
  return { start: start3, end };
5167
5167
  }
5168
5168
  function upsertScheduleBlock(crontab, every, cronLine) {
5169
- const lines = crontab.length === 0 ? [] : crontab.replace(/\n$/, "").split("\n");
5169
+ const lines2 = crontab.length === 0 ? [] : crontab.replace(/\n$/, "").split("\n");
5170
5170
  const block = buildBlock(every, cronLine);
5171
- const range = findBlockRange(lines);
5172
- const next3 = range === void 0 ? [...lines, ...block] : [
5173
- ...lines.slice(0, range.start),
5171
+ const range = findBlockRange(lines2);
5172
+ const next3 = range === void 0 ? [...lines2, ...block] : [
5173
+ ...lines2.slice(0, range.start),
5174
5174
  ...block,
5175
- ...lines.slice(range.end + 1)
5175
+ ...lines2.slice(range.end + 1)
5176
5176
  ];
5177
5177
  return `${next3.join("\n")}
5178
5178
  `;
5179
5179
  }
5180
5180
  function removeScheduleBlock(crontab) {
5181
- const lines = crontab.length === 0 ? [] : crontab.replace(/\n$/, "").split("\n");
5182
- const range = findBlockRange(lines);
5181
+ const lines2 = crontab.length === 0 ? [] : crontab.replace(/\n$/, "").split("\n");
5182
+ const range = findBlockRange(lines2);
5183
5183
  if (range === void 0) return crontab;
5184
- const next3 = [...lines.slice(0, range.start), ...lines.slice(range.end + 1)];
5184
+ const next3 = [...lines2.slice(0, range.start), ...lines2.slice(range.end + 1)];
5185
5185
  return next3.length === 0 ? "" : `${next3.join("\n")}
5186
5186
  `;
5187
5187
  }
5188
5188
  function readScheduleBlock(crontab) {
5189
- const lines = crontab.length === 0 ? [] : crontab.split("\n");
5190
- const range = findBlockRange(lines);
5189
+ const lines2 = crontab.length === 0 ? [] : crontab.split("\n");
5190
+ const range = findBlockRange(lines2);
5191
5191
  if (range === void 0) return void 0;
5192
- const body = lines.slice(range.start + 1, range.end);
5192
+ const body = lines2.slice(range.start + 1, range.end);
5193
5193
  const everyLine = body.find((line) => line.startsWith("# every "));
5194
5194
  const cronLine = body.find(
5195
5195
  (line) => !line.startsWith("#") && line.trim() !== ""
@@ -5705,8 +5705,8 @@ function spawnInherit(command, args, options2 = {}) {
5705
5705
  env,
5706
5706
  cwd: options2.cwd
5707
5707
  });
5708
- const done2 = new Promise((resolve21, reject) => {
5709
- child.on("close", (code) => resolve21(code ?? 0));
5708
+ const done2 = new Promise((resolve22, reject) => {
5709
+ child.on("close", (code) => resolve22(code ?? 0));
5710
5710
  child.on("error", reject);
5711
5711
  });
5712
5712
  return { child, done: done2 };
@@ -6851,9 +6851,9 @@ Failed to launch Claude for ${context}: ${message3}`)
6851
6851
  // src/commands/sessions/daemon/connectToDaemon.ts
6852
6852
  import * as net from "net";
6853
6853
  function connectToDaemon() {
6854
- return new Promise((resolve21, reject) => {
6854
+ return new Promise((resolve22, reject) => {
6855
6855
  const socket = net.connect(daemonPaths.socket);
6856
- socket.once("connect", () => resolve21(socket));
6856
+ socket.once("connect", () => resolve22(socket));
6857
6857
  socket.once("error", reject);
6858
6858
  });
6859
6859
  }
@@ -6869,7 +6869,7 @@ async function isDaemonRunning() {
6869
6869
  // src/commands/sessions/daemon/sendToDaemon.ts
6870
6870
  var WRITE_TIMEOUT_MS = 500;
6871
6871
  function sendToDaemon(message3) {
6872
- return new Promise((resolve21, reject) => {
6872
+ return new Promise((resolve22, reject) => {
6873
6873
  connectToDaemon().then((socket) => {
6874
6874
  const timer = setTimeout(() => {
6875
6875
  socket.destroy();
@@ -6883,7 +6883,7 @@ function sendToDaemon(message3) {
6883
6883
  `, () => {
6884
6884
  clearTimeout(timer);
6885
6885
  socket.end();
6886
- resolve21();
6886
+ resolve22();
6887
6887
  });
6888
6888
  }, reject);
6889
6889
  });
@@ -6906,7 +6906,7 @@ function readSocketLines(socket, onLine) {
6906
6906
  // src/commands/sessions/daemon/sendToDaemonAwaitAck.ts
6907
6907
  var ACK_TIMEOUT_MS = 1e3;
6908
6908
  function sendToDaemonAwaitAck(message3) {
6909
- return new Promise((resolve21, reject) => {
6909
+ return new Promise((resolve22, reject) => {
6910
6910
  connectToDaemon().then((socket) => {
6911
6911
  let settled = false;
6912
6912
  const finish = (error) => {
@@ -6915,7 +6915,7 @@ function sendToDaemonAwaitAck(message3) {
6915
6915
  clearTimeout(timer);
6916
6916
  socket.destroy();
6917
6917
  if (error) reject(error);
6918
- else resolve21();
6918
+ else resolve22();
6919
6919
  };
6920
6920
  const timer = setTimeout(
6921
6921
  () => finish(new Error("timed out awaiting daemon ack")),
@@ -6989,7 +6989,7 @@ async function deliverReliably(sessionId, status3, payload) {
6989
6989
  }
6990
6990
  }
6991
6991
  function sleep(ms) {
6992
- return new Promise((resolve21) => setTimeout(resolve21, ms));
6992
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
6993
6993
  }
6994
6994
  function describeError(error) {
6995
6995
  return error instanceof Error ? error.message : String(error);
@@ -7631,14 +7631,14 @@ function backlogRunMarkers(text17) {
7631
7631
  }
7632
7632
 
7633
7633
  // src/commands/sessions/shared/extractSessionMeta.ts
7634
- function extractSessionMeta(lines) {
7634
+ function extractSessionMeta(lines2) {
7635
7635
  let sessionId = "";
7636
7636
  let cwd = "";
7637
7637
  let timestamp6 = "";
7638
7638
  let name = "";
7639
7639
  let commandName = "";
7640
7640
  let commandArgs = "";
7641
- for (const line of lines) {
7641
+ for (const line of lines2) {
7642
7642
  const entry = safeParse(line);
7643
7643
  if (!entry) continue;
7644
7644
  sessionId ||= strField(entry, "sessionId");
@@ -8645,7 +8645,7 @@ function spawnDaemon(reason4) {
8645
8645
  child.unref();
8646
8646
  }
8647
8647
  function delay(ms) {
8648
- return new Promise((resolve21) => setTimeout(resolve21, ms));
8648
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
8649
8649
  }
8650
8650
 
8651
8651
  // src/commands/sessions/daemon/isWindowsCwd.ts
@@ -8682,10 +8682,10 @@ function gitInvocation(cwd, args) {
8682
8682
  }
8683
8683
  function git2(cwd, args) {
8684
8684
  const { file, argv, options: options2 } = gitInvocation(cwd, args);
8685
- return new Promise((resolve21, reject) => {
8685
+ return new Promise((resolve22, reject) => {
8686
8686
  execFile2(file, argv, options2, (error, stdout) => {
8687
8687
  if (error) reject(error);
8688
- else resolve21(stdout.toString());
8688
+ else resolve22(stdout.toString());
8689
8689
  });
8690
8690
  });
8691
8691
  }
@@ -9097,12 +9097,12 @@ async function loadVisibleItems(req) {
9097
9097
 
9098
9098
  // src/commands/backlog/web/parseStatusBody.ts
9099
9099
  function readBody(req) {
9100
- return new Promise((resolve21, reject) => {
9100
+ return new Promise((resolve22, reject) => {
9101
9101
  let body = "";
9102
9102
  req.on("data", (chunk) => {
9103
9103
  body += chunk.toString();
9104
9104
  });
9105
- req.on("end", () => resolve21(body));
9105
+ req.on("end", () => resolve22(body));
9106
9106
  req.on("error", reject);
9107
9107
  });
9108
9108
  }
@@ -10586,17 +10586,17 @@ async function stopDaemon() {
10586
10586
  }
10587
10587
  }
10588
10588
  function closedBeforeTimeout(socket) {
10589
- return new Promise((resolve21) => {
10589
+ return new Promise((resolve22) => {
10590
10590
  const timer = setTimeout(() => {
10591
10591
  socket.destroy();
10592
- resolve21(false);
10592
+ resolve22(false);
10593
10593
  }, STOP_TIMEOUT_MS);
10594
10594
  socket.resume();
10595
10595
  socket.on("error", () => {
10596
10596
  });
10597
10597
  socket.once("close", () => {
10598
10598
  clearTimeout(timer);
10599
- resolve21(true);
10599
+ resolve22(true);
10600
10600
  });
10601
10601
  });
10602
10602
  }
@@ -10647,8 +10647,8 @@ async function restartWeb(req, res, deps2 = {}) {
10647
10647
  respondJson(res, 400, { error: "Invalid target" });
10648
10648
  return;
10649
10649
  }
10650
- await new Promise((resolve21) => {
10651
- res.once("finish", resolve21);
10650
+ await new Promise((resolve22) => {
10651
+ res.once("finish", resolve22);
10652
10652
  respondJson(res, 200, { ok: true });
10653
10653
  });
10654
10654
  if (target === "daemon" || target === "both") {
@@ -11185,8 +11185,8 @@ async function runGhImage(filePath, cwd) {
11185
11185
  `gh image failed: ${stderr.trim() || err.message || "unknown error"}`
11186
11186
  );
11187
11187
  }
11188
- const lines = stdout.split("\n").map((line) => line.trim()).filter(Boolean);
11189
- const markdown = lines.find((line) => /^!\[.*]\(.+\)$/.test(line)) ?? lines.find((line) => line.includes("http")) ?? lines[0];
11188
+ const lines2 = stdout.split("\n").map((line) => line.trim()).filter(Boolean);
11189
+ const markdown = lines2.find((line) => /^!\[.*]\(.+\)$/.test(line)) ?? lines2.find((line) => line.includes("http")) ?? lines2[0];
11190
11190
  if (!markdown) throw new Error("gh image produced no output");
11191
11191
  return markdown;
11192
11192
  }
@@ -11377,10 +11377,10 @@ async function openDaemonConnection(ws, ctx) {
11377
11377
  }
11378
11378
  }
11379
11379
  function relayDaemonLines(conn, ws, repoCwd) {
11380
- const lines = createInterface2({ input: conn });
11381
- lines.on("error", () => {
11380
+ const lines2 = createInterface2({ input: conn });
11381
+ lines2.on("error", () => {
11382
11382
  });
11383
- lines.on("line", (line) => {
11383
+ lines2.on("line", (line) => {
11384
11384
  if (ws.readyState === ws.OPEN) ws.send(withRepoCwd(line, repoCwd));
11385
11385
  });
11386
11386
  conn.on("error", () => {
@@ -11522,7 +11522,7 @@ function firstEnabledIndex(items2) {
11522
11522
  // src/commands/sessions/web/restartMenu/renderRestartMenu.ts
11523
11523
  import chalk59 from "chalk";
11524
11524
  function renderRestartMenu(items2, selected) {
11525
- const lines = [chalk59.bold.cyan("assist \u2014 restart menu")];
11525
+ const lines2 = [chalk59.bold.cyan("assist \u2014 restart menu")];
11526
11526
  items2.forEach((item, i) => {
11527
11527
  const active = i === selected;
11528
11528
  const pointer = active ? chalk59.cyan("\u276F ") : " ";
@@ -11531,10 +11531,10 @@ function renderRestartMenu(items2, selected) {
11531
11531
  let label2 = item.label;
11532
11532
  if (item.disabled) label2 = chalk59.dim(label2);
11533
11533
  else if (active) label2 = chalk59.cyan.bold(label2);
11534
- lines.push(`${pointer}${number}${label2}${note}`);
11534
+ lines2.push(`${pointer}${number}${label2}${note}`);
11535
11535
  });
11536
- lines.push(chalk59.dim("\u2191/\u2193 move \xB7 1-3 jump \xB7 enter select \xB7 esc close"));
11537
- return lines.join("\n");
11536
+ lines2.push(chalk59.dim("\u2191/\u2193 move \xB7 1-3 jump \xB7 enter select \xB7 esc close"));
11537
+ return lines2.join("\n");
11538
11538
  }
11539
11539
 
11540
11540
  // src/commands/sessions/web/restartMenu/createMenuState.ts
@@ -11660,10 +11660,10 @@ async function connect2() {
11660
11660
  }
11661
11661
  }
11662
11662
  function wire(socket) {
11663
- const lines = createInterface3({ input: socket });
11664
- lines.on("error", () => {
11663
+ const lines2 = createInterface3({ input: socket });
11664
+ lines2.on("error", () => {
11665
11665
  });
11666
- lines.on("line", emit);
11666
+ lines2.on("line", emit);
11667
11667
  socket.on("error", () => {
11668
11668
  });
11669
11669
  socket.on("close", scheduleReconnect);
@@ -12203,11 +12203,11 @@ async function countRows(client, table) {
12203
12203
  return rows[0].n;
12204
12204
  }
12205
12205
  function printSummary(tables, current, incoming) {
12206
- const lines = tables.map(
12206
+ const lines2 = tables.map(
12207
12207
  (t, i) => ` ${t.name}: ${current[i]} \u2192 ${incoming[i]} rows`
12208
12208
  );
12209
12209
  console.error(chalk71.bold("\nThis will REPLACE all backlog data:"));
12210
- console.error(`${lines.join("\n")}
12210
+ console.error(`${lines2.join("\n")}
12211
12211
  `);
12212
12212
  }
12213
12213
  async function confirmReplace(client, tables, incoming, fromStdin) {
@@ -12600,7 +12600,7 @@ function parsePreviewDecision(line, requestId) {
12600
12600
 
12601
12601
  // src/commands/sessions/shared/requestPreviewDecision.ts
12602
12602
  function requestPreviewDecision(request) {
12603
- return new Promise((resolve21, reject) => {
12603
+ return new Promise((resolve22, reject) => {
12604
12604
  connectToDaemon().then((socket) => {
12605
12605
  let settled = false;
12606
12606
  const finish = (error, decision) => {
@@ -12608,7 +12608,7 @@ function requestPreviewDecision(request) {
12608
12608
  settled = true;
12609
12609
  socket.destroy();
12610
12610
  if (error) reject(error);
12611
- else resolve21(decision);
12611
+ else resolve22(decision);
12612
12612
  };
12613
12613
  readSocketLines(socket, (line) => {
12614
12614
  const incoming = parsePreviewDecision(line, request.requestId);
@@ -14859,13 +14859,13 @@ function saveCliReads(commands) {
14859
14859
  );
14860
14860
  cachedReads = void 0;
14861
14861
  }
14862
- function findMatch(command, lines) {
14862
+ function findMatch(command, lines2) {
14863
14863
  const words = command.split(/\s+/);
14864
14864
  if (words.length === 0) return void 0;
14865
- if (lines.includes(words[0])) return words[0];
14865
+ if (lines2.includes(words[0])) return words[0];
14866
14866
  if (words.length < 2) return void 0;
14867
14867
  const prefix2 = `${words[0]} ${words[1]}`;
14868
- const candidates = lines.filter(
14868
+ const candidates = lines2.filter(
14869
14869
  (line) => line === prefix2 || line.startsWith(`${prefix2} `)
14870
14870
  );
14871
14871
  return candidates.sort((a, b) => b.length - a.length).find((rc) => command === rc || command.startsWith(`${rc} `));
@@ -15299,12 +15299,12 @@ function hasSubcommands(helpText) {
15299
15299
  // src/commands/permitCliReads/runHelp.ts
15300
15300
  import { exec as exec2 } from "child_process";
15301
15301
  function runHelp(args) {
15302
- return new Promise((resolve21) => {
15302
+ return new Promise((resolve22) => {
15303
15303
  exec2(
15304
15304
  `${args.join(" ")} --help`,
15305
15305
  { encoding: "utf8", timeout: 3e4 },
15306
15306
  (_err, stdout, stderr) => {
15307
- resolve21(stdout || stderr || "");
15307
+ resolve22(stdout || stderr || "");
15308
15308
  }
15309
15309
  );
15310
15310
  });
@@ -15432,14 +15432,14 @@ function formatHuman(cli, commands) {
15432
15432
  const sorted = [...commands].sort(
15433
15433
  (a, b) => a.path.join(" ").localeCompare(b.path.join(" "))
15434
15434
  );
15435
- const lines = [`Discovered ${commands.length} commands for "${cli}":
15435
+ const lines2 = [`Discovered ${commands.length} commands for "${cli}":
15436
15436
  `];
15437
15437
  for (const cmd of sorted) {
15438
15438
  const full = `${cli} ${cmd.path.join(" ")}`;
15439
15439
  const text17 = cmd.description ? `${full} \u2014 ${cmd.description}` : full;
15440
- lines.push(`${prefix(classifyVerb(cmd.path))}${text17}`);
15440
+ lines2.push(`${prefix(classifyVerb(cmd.path))}${text17}`);
15441
15441
  }
15442
- return lines.join("\n");
15442
+ return lines2.join("\n");
15443
15443
  }
15444
15444
 
15445
15445
  // src/commands/permitCliReads/parseCached.ts
@@ -15686,22 +15686,22 @@ function codeCommentConfirm(pin) {
15686
15686
  return;
15687
15687
  }
15688
15688
  const original = readFileSync29(state.file, "utf8");
15689
- const lines = original.split("\n");
15689
+ const lines2 = original.split("\n");
15690
15690
  const index3 = state.line - 1;
15691
- if (index3 > lines.length) {
15691
+ if (index3 > lines2.length) {
15692
15692
  console.error(
15693
15693
  chalk118.red(
15694
- `Line ${state.line} is beyond the end of ${state.file} (${lines.length} lines).`
15694
+ `Line ${state.line} is beyond the end of ${state.file} (${lines2.length} lines).`
15695
15695
  )
15696
15696
  );
15697
15697
  process.exitCode = 1;
15698
15698
  return;
15699
15699
  }
15700
15700
  const marker = isHashCommentFile(state.file) ? "#" : "//";
15701
- const indentSource = lines[index3] ?? "";
15701
+ const indentSource = lines2[index3] ?? "";
15702
15702
  const indent2 = indentSource.match(/^\s*/)?.[0] ?? "";
15703
- lines.splice(index3, 0, `${indent2}${marker} ${state.text}`);
15704
- writeFileSync24(state.file, lines.join("\n"));
15703
+ lines2.splice(index3, 0, `${indent2}${marker} ${state.text}`);
15704
+ writeFileSync24(state.file, lines2.join("\n"));
15705
15705
  unlinkSync8(getPinStatePath(pin));
15706
15706
  console.log(
15707
15707
  chalk118.green(
@@ -16413,17 +16413,17 @@ async function sloc(pattern2 = "**/*.ts", options2 = {}) {
16413
16413
  let hasViolation = false;
16414
16414
  for (const file of files) {
16415
16415
  const content = fs22.readFileSync(file, "utf8");
16416
- const lines = countSloc(content);
16417
- results.push({ file, lines });
16418
- if (options2.threshold !== void 0 && lines > options2.threshold) {
16416
+ const lines2 = countSloc(content);
16417
+ results.push({ file, lines: lines2 });
16418
+ if (options2.threshold !== void 0 && lines2 > options2.threshold) {
16419
16419
  hasViolation = true;
16420
16420
  }
16421
16421
  }
16422
16422
  results.sort((a, b) => b.lines - a.lines);
16423
- for (const { file, lines } of results) {
16424
- const exceedsThreshold = options2.threshold !== void 0 && lines > options2.threshold;
16423
+ for (const { file, lines: lines2 } of results) {
16424
+ const exceedsThreshold = options2.threshold !== void 0 && lines2 > options2.threshold;
16425
16425
  const color = exceedsThreshold ? chalk127.red : chalk127.white;
16426
- console.log(`${color(file)} \u2192 ${chalk127.cyan(lines)} lines`);
16426
+ console.log(`${color(file)} \u2192 ${chalk127.cyan(lines2)} lines`);
16427
16427
  }
16428
16428
  const total = results.reduce((sum, r) => sum + r.lines, 0);
16429
16429
  console.log(
@@ -17010,9 +17010,9 @@ function printCommitsWithFiles(commits2, ignore3, verbose) {
17010
17010
  }
17011
17011
  }
17012
17012
  function parseGitLogCommits(output, ignore3, afterDate) {
17013
- const lines = output.trim().split("\n");
17013
+ const lines2 = output.trim().split("\n");
17014
17014
  const commitsByDate = /* @__PURE__ */ new Map();
17015
- for (const line of lines) {
17015
+ for (const line of lines2) {
17016
17016
  const [date, hash, ...messageParts] = line.split("|");
17017
17017
  const message3 = messageParts.join("|");
17018
17018
  if (afterDate && date <= afterDate) {
@@ -18108,12 +18108,12 @@ function isHeaderLine2(line) {
18108
18108
  return trimmed === "" || trimmed.startsWith("#");
18109
18109
  }
18110
18110
  function extractShellComments(text17) {
18111
- const lines = text17.split("\n");
18111
+ const lines2 = text17.split("\n");
18112
18112
  let firstCodeLine = 0;
18113
- while (firstCodeLine < lines.length && isHeaderLine2(lines[firstCodeLine])) {
18113
+ while (firstCodeLine < lines2.length && isHeaderLine2(lines2[firstCodeLine])) {
18114
18114
  firstCodeLine++;
18115
18115
  }
18116
- return extractYamlComments(lines.slice(firstCodeLine).join("\n"));
18116
+ return extractYamlComments(lines2.slice(firstCodeLine).join("\n"));
18117
18117
  }
18118
18118
 
18119
18119
  // src/commands/editHook/introducedComments.ts
@@ -19159,9 +19159,28 @@ function keptInPlace(cwd) {
19159
19159
  return { cwd, kind: "primary", created: false };
19160
19160
  }
19161
19161
 
19162
+ // src/commands/sessions/daemon/describePersistedSession.ts
19163
+ function describePersistedSession(entry) {
19164
+ const { id, name, cwd } = readDescribed(entry);
19165
+ return `id=${id ?? "?"} name=${JSON.stringify(name ?? "?")} cwd=${cwd ?? "?"}`;
19166
+ }
19167
+ function readDescribed(entry) {
19168
+ if (typeof entry !== "object" || entry === null) return {};
19169
+ const record = entry;
19170
+ return {
19171
+ id: stringOrUndefined(record.id),
19172
+ name: stringOrUndefined(record.name),
19173
+ cwd: stringOrUndefined(record.cwd)
19174
+ };
19175
+ }
19176
+ function stringOrUndefined(value) {
19177
+ return typeof value === "string" ? value : void 0;
19178
+ }
19179
+
19162
19180
  // src/commands/sessions/daemon/persistedSessionSchema.ts
19163
19181
  import { z as z7 } from "zod";
19164
19182
  var persistedSessionSchema = z7.object({
19183
+ id: z7.string().optional(),
19165
19184
  name: z7.string(),
19166
19185
  title: z7.string().optional(),
19167
19186
  generatedTitle: z7.string().optional(),
@@ -19185,6 +19204,7 @@ var persistedSessionSchema = z7.object({
19185
19204
  // src/commands/sessions/daemon/toPersistedSession.ts
19186
19205
  function toPersistedSession(session) {
19187
19206
  return {
19207
+ id: session.id,
19188
19208
  name: session.name,
19189
19209
  title: session.title,
19190
19210
  generatedTitle: session.generatedTitle,
@@ -19214,10 +19234,25 @@ var SESSIONS_FILE = "sessions.json";
19214
19234
  function loadPersistedSessions() {
19215
19235
  const data = loadJson(SESSIONS_FILE);
19216
19236
  if (!Array.isArray(data)) return [];
19217
- return data.flatMap((entry) => {
19237
+ const rejected = [];
19238
+ const loaded = data.flatMap((entry) => {
19218
19239
  const parsed = persistedSessionSchema.safeParse(entry);
19219
- return parsed.success ? [parsed.data] : [];
19240
+ if (parsed.success) return [parsed.data];
19241
+ rejected.push(
19242
+ `${describePersistedSession(entry)}: ${parsed.error.issues.map((i) => `${i.path.join(".")} ${i.message}`).join("; ")}`
19243
+ );
19244
+ return [];
19220
19245
  });
19246
+ logRejected(rejected);
19247
+ return loaded;
19248
+ }
19249
+ var lastRejectedSignature = "";
19250
+ function logRejected(rejected) {
19251
+ const signature = rejected.join("|");
19252
+ if (signature === lastRejectedSignature) return;
19253
+ lastRejectedSignature = signature;
19254
+ for (const entry of rejected)
19255
+ daemonLog(`unreadable persisted session dropped \u2014 ${entry}`);
19221
19256
  }
19222
19257
  function savePersistedSessions(sessions) {
19223
19258
  saveJson(SESSIONS_FILE, sessions);
@@ -19389,7 +19424,7 @@ function placedByDaemon() {
19389
19424
  function seed(worktreePath, clone) {
19390
19425
  console.log(`Preparing ${worktreePath}\u2026`);
19391
19426
  return new Promise(
19392
- (resolve21) => seedWorktree(worktreePath, clone, resolve21)
19427
+ (resolve22) => seedWorktree(worktreePath, clone, resolve22)
19393
19428
  );
19394
19429
  }
19395
19430
  async function moveToPrCheckoutTree() {
@@ -19896,25 +19931,25 @@ function isVisibleText(t) {
19896
19931
  return /[a-zA-Z]{3,}/.test(t);
19897
19932
  }
19898
19933
  var isHashtag = (t) => /^#[A-Za-z0-9_]+$/.test(t);
19899
- function collectRscText(v, resolve21, sink2, seen) {
19934
+ function collectRscText(v, resolve22, sink2, seen) {
19900
19935
  if (v == null) return;
19901
19936
  if (typeof v === "string") {
19902
19937
  if (isRscRef(v)) {
19903
19938
  if (!seen.has(v)) {
19904
19939
  seen.add(v);
19905
- collectRscText(resolve21(v), resolve21, sink2, seen);
19940
+ collectRscText(resolve22(v), resolve22, sink2, seen);
19906
19941
  }
19907
19942
  } else if (isHashtag(v)) sink2.hashtags.push(v);
19908
19943
  else if (isVisibleText(v)) sink2.text.push(v);
19909
19944
  return;
19910
19945
  }
19911
19946
  if (Array.isArray(v)) {
19912
- for (const x of v) collectRscText(x, resolve21, sink2, seen);
19947
+ for (const x of v) collectRscText(x, resolve22, sink2, seen);
19913
19948
  return;
19914
19949
  }
19915
19950
  if (typeof v === "object") {
19916
19951
  for (const val of Object.values(v)) {
19917
- collectRscText(val, resolve21, sink2, seen);
19952
+ collectRscText(val, resolve22, sink2, seen);
19918
19953
  }
19919
19954
  }
19920
19955
  }
@@ -19946,7 +19981,7 @@ function visitObjects(root, fn) {
19946
19981
  }
19947
19982
  }
19948
19983
  }
19949
- function buildMentionMap(rows, resolve21) {
19984
+ function buildMentionMap(rows, resolve22) {
19950
19985
  const map = /* @__PURE__ */ new Map();
19951
19986
  visitObjects(rows, (o) => {
19952
19987
  const url = profileActionUrl(o);
@@ -19954,7 +19989,7 @@ function buildMentionMap(rows, resolve21) {
19954
19989
  const slug = slugFromProfileUrl(url);
19955
19990
  if (!slug || map.has(slug)) return;
19956
19991
  const sink2 = { text: [], hashtags: [] };
19957
- collectRscText(o.children, resolve21, sink2, /* @__PURE__ */ new Set());
19992
+ collectRscText(o.children, resolve22, sink2, /* @__PURE__ */ new Set());
19958
19993
  const name = sink2.text.join(" ").replace(/\s+/g, " ").trim();
19959
19994
  map.set(slug, name ? { slug, name, url } : { slug, url });
19960
19995
  });
@@ -20040,10 +20075,10 @@ function buildPost(raw, mentionMap, author) {
20040
20075
 
20041
20076
  // src/commands/netcap/walkPostRow.ts
20042
20077
  var isCommentary = (o) => asObject(o.viewTrackingSpecs)?.viewName === "feed-commentary";
20043
- function walkPostRow(v, resolve21, raw) {
20078
+ function walkPostRow(v, resolve22, raw) {
20044
20079
  if (v == null || typeof v !== "object") return;
20045
20080
  if (Array.isArray(v)) {
20046
- for (const x of v) walkPostRow(x, resolve21, raw);
20081
+ for (const x of v) walkPostRow(x, resolve22, raw);
20047
20082
  return;
20048
20083
  }
20049
20084
  const o = v;
@@ -20057,9 +20092,9 @@ function walkPostRow(v, resolve21, raw) {
20057
20092
  }
20058
20093
  if (isCommentary(o)) {
20059
20094
  const sink2 = { text: raw.text, hashtags: raw.hashtags };
20060
- collectRscText(o.children, resolve21, sink2, /* @__PURE__ */ new Set());
20095
+ collectRscText(o.children, resolve22, sink2, /* @__PURE__ */ new Set());
20061
20096
  }
20062
- for (const val of Object.values(o)) walkPostRow(val, resolve21, raw);
20097
+ for (const val of Object.values(o)) walkPostRow(val, resolve22, raw);
20063
20098
  }
20064
20099
 
20065
20100
  // src/commands/netcap/extractLinkedInPosts.ts
@@ -20073,8 +20108,8 @@ function findCommentaryRows(rows) {
20073
20108
  }
20074
20109
  function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
20075
20110
  const rows = parseRscRows(flight);
20076
- const resolve21 = makeRscResolver(rows);
20077
- const mentionMap = buildMentionMap(rows, resolve21);
20111
+ const resolve22 = makeRscResolver(rows);
20112
+ const mentionMap = buildMentionMap(rows, resolve22);
20078
20113
  const posts = [];
20079
20114
  for (const id of findCommentaryRows(rows)) {
20080
20115
  const raw = {
@@ -20084,7 +20119,7 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
20084
20119
  links: [],
20085
20120
  related: []
20086
20121
  };
20087
- walkPostRow(rows[id], resolve21, raw);
20122
+ walkPostRow(rows[id], resolve22, raw);
20088
20123
  const post = buildPost(raw, mentionMap, author);
20089
20124
  if (post) posts.push(post);
20090
20125
  }
@@ -20253,9 +20288,9 @@ function extractVoyagerPosts(body) {
20253
20288
 
20254
20289
  // src/commands/netcap/extractPostsFromCapture.ts
20255
20290
  function captureEntries(captureFile) {
20256
- const lines = readFileSync39(captureFile, "utf8").split("\n").filter(Boolean);
20291
+ const lines2 = readFileSync39(captureFile, "utf8").split("\n").filter(Boolean);
20257
20292
  const entries = [];
20258
- for (const line of lines) {
20293
+ for (const line of lines2) {
20259
20294
  let entry;
20260
20295
  try {
20261
20296
  entry = JSON.parse(line);
@@ -20695,11 +20730,11 @@ function findWallOfText(body) {
20695
20730
  function splitParagraphs(body) {
20696
20731
  const paragraphs = [];
20697
20732
  let section3 = "(intro)";
20698
- let lines = [];
20733
+ let lines2 = [];
20699
20734
  const flush = () => {
20700
- if (lines.length > 0) {
20701
- paragraphs.push({ section: section3, lines });
20702
- lines = [];
20735
+ if (lines2.length > 0) {
20736
+ paragraphs.push({ section: section3, lines: lines2 });
20737
+ lines2 = [];
20703
20738
  }
20704
20739
  };
20705
20740
  for (const line of body.split("\n")) {
@@ -20710,17 +20745,17 @@ function splitParagraphs(body) {
20710
20745
  } else if (line.trim() === "") {
20711
20746
  flush();
20712
20747
  } else {
20713
- lines.push(line);
20748
+ lines2.push(line);
20714
20749
  }
20715
20750
  }
20716
20751
  flush();
20717
20752
  return paragraphs;
20718
20753
  }
20719
- function isWallOfText(lines) {
20720
- if (lines.some(isListLine)) {
20754
+ function isWallOfText(lines2) {
20755
+ if (lines2.some(isListLine)) {
20721
20756
  return false;
20722
20757
  }
20723
- const text17 = lines.join(" ").trim();
20758
+ const text17 = lines2.join(" ").trim();
20724
20759
  return text17.length > MAX_PARAGRAPH_CHARS || countSentences(text17) > MAX_PARAGRAPH_SENTENCES;
20725
20760
  }
20726
20761
  function countSentences(paragraph) {
@@ -22163,9 +22198,9 @@ function parseRefactorYml() {
22163
22198
  }
22164
22199
  const content = fs24.readFileSync(REFACTOR_YML_PATH, "utf8");
22165
22200
  const entries = [];
22166
- const lines = content.split("\n");
22201
+ const lines2 = content.split("\n");
22167
22202
  let currentEntry = {};
22168
- for (const line of lines) {
22203
+ for (const line of lines2) {
22169
22204
  const trimmed = line.trim();
22170
22205
  if (trimmed.startsWith("- file:")) {
22171
22206
  if (currentEntry.file) {
@@ -22238,7 +22273,7 @@ function getViolations(pattern2, options2 = {}, maxLines = DEFAULT_MAX_LINES) {
22238
22273
 
22239
22274
  // src/commands/refactor/check/index.ts
22240
22275
  function runScript(script, cwd) {
22241
- return new Promise((resolve21) => {
22276
+ return new Promise((resolve22) => {
22242
22277
  const child = spawn6("npm", ["run", script], {
22243
22278
  stdio: "pipe",
22244
22279
  shell: true,
@@ -22252,7 +22287,7 @@ function runScript(script, cwd) {
22252
22287
  output += data.toString();
22253
22288
  });
22254
22289
  child.on("close", (code) => {
22255
- resolve21({ script, code: code ?? 1, output });
22290
+ resolve22({ script, code: code ?? 1, output });
22256
22291
  });
22257
22292
  });
22258
22293
  }
@@ -22753,22 +22788,22 @@ function formatImportLine(imp) {
22753
22788
 
22754
22789
  // src/commands/refactor/extract/buildDestinationContent.ts
22755
22790
  function buildDestinationContent(functionTexts, imports, sourceRelativePath, sourceImportNames) {
22756
- const lines = [];
22791
+ const lines2 = [];
22757
22792
  for (const imp of imports) {
22758
- lines.push(formatImportLine(imp));
22793
+ lines2.push(formatImportLine(imp));
22759
22794
  }
22760
22795
  if (sourceImportNames.length > 0) {
22761
- lines.push(
22796
+ lines2.push(
22762
22797
  `import { ${sourceImportNames.join(", ")} } from "${sourceRelativePath}";`
22763
22798
  );
22764
22799
  }
22765
- if (lines.length > 0) lines.push("");
22800
+ if (lines2.length > 0) lines2.push("");
22766
22801
  for (let i = 0; i < functionTexts.length; i++) {
22767
- if (i > 0) lines.push("");
22768
- lines.push(functionTexts[i]);
22802
+ if (i > 0) lines2.push("");
22803
+ lines2.push(functionTexts[i]);
22769
22804
  }
22770
- lines.push("");
22771
- return lines.join("\n");
22805
+ lines2.push("");
22806
+ return lines2.join("\n");
22772
22807
  }
22773
22808
 
22774
22809
  // src/commands/refactor/extract/getRelativeImportPath.ts
@@ -23379,9 +23414,9 @@ function groupReferences(symbol, cwd) {
23379
23414
  const grouped = /* @__PURE__ */ new Map();
23380
23415
  for (const ref of refs) {
23381
23416
  const refFile = path50.relative(cwd, ref.getSourceFile().getFilePath());
23382
- const lines = grouped.get(refFile) ?? [];
23383
- if (!grouped.has(refFile)) grouped.set(refFile, lines);
23384
- lines.push(ref.getStartLineNumber());
23417
+ const lines2 = grouped.get(refFile) ?? [];
23418
+ if (!grouped.has(refFile)) grouped.set(refFile, lines2);
23419
+ lines2.push(ref.getStartLineNumber());
23385
23420
  }
23386
23421
  return grouped;
23387
23422
  }
@@ -23401,9 +23436,9 @@ async function renameSymbol(file, oldName, newName, options2 = {}) {
23401
23436
  chalk189.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
23402
23437
  `)
23403
23438
  );
23404
- for (const [refFile, lines] of grouped) {
23439
+ for (const [refFile, lines2] of grouped) {
23405
23440
  console.log(
23406
- ` ${chalk189.dim(refFile)}: lines ${chalk189.cyan(lines.join(", "))}`
23441
+ ` ${chalk189.dim(refFile)}: lines ${chalk189.cyan(lines2.join(", "))}`
23407
23442
  );
23408
23443
  }
23409
23444
  if (options2.apply) {
@@ -23844,12 +23879,12 @@ function headerFor(c) {
23844
23879
  return tags ? `### ${location} [${tags}]` : `### ${location}`;
23845
23880
  }
23846
23881
  function formatThread(thread) {
23847
- const lines = [
23882
+ const lines2 = [
23848
23883
  headerFor(thread.root),
23849
23884
  `**${thread.root.author}**: ${thread.root.body.trim()}`,
23850
23885
  ...thread.replies.map((r) => `**${r.author}** (reply): ${r.body.trim()}`)
23851
23886
  ];
23852
- return lines.join("\n\n");
23887
+ return lines2.join("\n\n");
23853
23888
  }
23854
23889
  var INTRO = `The PR already has the review comments below (including resolved and outdated threads). Avoid re-raising findings that a prior comment substantively covers.`;
23855
23890
  function formatPriorComments(comments3) {
@@ -24156,13 +24191,13 @@ function summariseSynthesis(markdown) {
24156
24191
  return { summary: extractSummary(markdown), totals, findingCount };
24157
24192
  }
24158
24193
  function formatSynthesisSummary(summary) {
24159
- const lines = [];
24194
+ const lines2 = [];
24160
24195
  const { totals, findingCount } = summary;
24161
- lines.push(
24196
+ lines2.push(
24162
24197
  `Findings: ${findingCount} (blocker ${totals.blocker}, major ${totals.major}, minor ${totals.minor}, nit ${totals.nit})`
24163
24198
  );
24164
- if (summary.summary) lines.push("", summary.summary);
24165
- return lines.join("\n");
24199
+ if (summary.summary) lines2.push("", summary.summary);
24200
+ return lines2.join("\n");
24166
24201
  }
24167
24202
 
24168
24203
  // src/commands/review/buildReviewSummary.ts
@@ -24173,12 +24208,12 @@ function formatFindingLine(finding) {
24173
24208
  function buildReviewSummary(markdown) {
24174
24209
  const summary = summariseSynthesis(markdown);
24175
24210
  const findings = parseFindings(markdown);
24176
- const lines = ["## Code review summary", "", formatSynthesisSummary(summary)];
24211
+ const lines2 = ["## Code review summary", "", formatSynthesisSummary(summary)];
24177
24212
  if (findings.length > 0) {
24178
- lines.push("", "### Findings", "");
24179
- for (const finding of findings) lines.push(formatFindingLine(finding));
24213
+ lines2.push("", "### Findings", "");
24214
+ for (const finding of findings) lines2.push(formatFindingLine(finding));
24180
24215
  }
24181
- return lines.join("\n");
24216
+ return lines2.join("\n");
24182
24217
  }
24183
24218
 
24184
24219
  // src/commands/review/sanitiseReviewerNames.ts
@@ -24189,13 +24224,13 @@ function sanitiseReviewerNames(value) {
24189
24224
 
24190
24225
  // src/commands/review/postFindings.ts
24191
24226
  function buildCommentBody(finding) {
24192
- const lines = [];
24227
+ const lines2 = [];
24193
24228
  const severityLabel = finding.severity ?? "finding";
24194
- lines.push(`**${severityLabel}: ${finding.title}**`);
24195
- if (finding.impact) lines.push("", `Impact: ${finding.impact}`);
24229
+ lines2.push(`**${severityLabel}: ${finding.title}**`);
24230
+ if (finding.impact) lines2.push("", `Impact: ${finding.impact}`);
24196
24231
  if (finding.recommendation)
24197
- lines.push("", `Recommendation: ${finding.recommendation}`);
24198
- return sanitiseReviewerNames(lines.join("\n"));
24232
+ lines2.push("", `Recommendation: ${finding.recommendation}`);
24233
+ return sanitiseReviewerNames(lines2.join("\n"));
24199
24234
  }
24200
24235
  function postFindings(findings) {
24201
24236
  let posted = 0;
@@ -24294,10 +24329,10 @@ function buildDiffLineIndex(diff3) {
24294
24329
 
24295
24330
  // src/commands/review/partitionFindingsByDiff.ts
24296
24331
  function isWithinDiff(finding, index3) {
24297
- const lines = index3.get(finding.file);
24298
- if (!lines) return false;
24299
- if (!lines.has(finding.line)) return false;
24300
- if (finding.startLine !== void 0 && !lines.has(finding.startLine)) {
24332
+ const lines2 = index3.get(finding.file);
24333
+ if (!lines2) return false;
24334
+ if (!lines2.has(finding.line)) return false;
24335
+ if (finding.startLine !== void 0 && !lines2.has(finding.startLine)) {
24301
24336
  return false;
24302
24337
  }
24303
24338
  return true;
@@ -24688,8 +24723,8 @@ function indent(text17) {
24688
24723
  return text17.split(/\r?\n/).map((line) => ` ${line}`);
24689
24724
  }
24690
24725
  function tailLines(text17, maxLines) {
24691
- const lines = text17.split(/\r?\n/);
24692
- return lines.length <= maxLines ? text17 : lines.slice(-maxLines).join("\n");
24726
+ const lines2 = text17.split(/\r?\n/);
24727
+ return lines2.length <= maxLines ? text17 : lines2.slice(-maxLines).join("\n");
24693
24728
  }
24694
24729
  function isFastFail(input) {
24695
24730
  return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
@@ -25061,12 +25096,12 @@ function onCloseResult(ctx, code) {
25061
25096
  return { ...closed, stderr: ctx.stderr.value, stdout: ctx.stdout.value };
25062
25097
  }
25063
25098
  function waitForChildExit(ctx) {
25064
- return new Promise((resolve21) => {
25099
+ return new Promise((resolve22) => {
25065
25100
  let settled = false;
25066
25101
  const settle = (result) => {
25067
25102
  if (settled) return;
25068
25103
  settled = true;
25069
- resolve21(result);
25104
+ resolve22(result);
25070
25105
  };
25071
25106
  ctx.child.on("error", (err) => settle(onErrorResult(ctx, err)));
25072
25107
  ctx.child.on("close", (code) => settle(onCloseResult(ctx, code)));
@@ -25721,13 +25756,13 @@ function formatEvent(event) {
25721
25756
  const abbrev = levelAbbrev(event.Level);
25722
25757
  const ts8 = chalk198.dim(formatTimestamp(event.Timestamp));
25723
25758
  const msg = renderMessage(event);
25724
- const lines = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
25759
+ const lines2 = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
25725
25760
  if (event.Exception) {
25726
25761
  for (const line of event.Exception.split("\n")) {
25727
- lines.push(chalk198.red(` ${line}`));
25762
+ lines2.push(chalk198.red(` ${line}`));
25728
25763
  }
25729
25764
  }
25730
- return lines.join("\n");
25765
+ return lines2.join("\n");
25731
25766
  }
25732
25767
 
25733
25768
  // src/commands/seq/parseRelativeTime.ts
@@ -26166,9 +26201,9 @@ function createReadlineInterface() {
26166
26201
  });
26167
26202
  }
26168
26203
  function askQuestion(rl, question) {
26169
- return new Promise((resolve21) => {
26204
+ return new Promise((resolve22) => {
26170
26205
  rl.question(question, (answer) => {
26171
- resolve21(answer.trim());
26206
+ resolve22(answer.trim());
26172
26207
  });
26173
26208
  });
26174
26209
  }
@@ -26388,13 +26423,13 @@ function extractSpeaker(fullText) {
26388
26423
  function isTextLine(line) {
26389
26424
  return !!line.trim() && !line.includes("-->");
26390
26425
  }
26391
- function scanTextLines(lines, start3) {
26426
+ function scanTextLines(lines2, start3) {
26392
26427
  let i = start3;
26393
- while (i < lines.length && isTextLine(lines[i])) i++;
26394
- return { texts: lines.slice(start3, i).map((l) => l.trim()), end: i };
26428
+ while (i < lines2.length && isTextLine(lines2[i])) i++;
26429
+ return { texts: lines2.slice(start3, i).map((l) => l.trim()), end: i };
26395
26430
  }
26396
- function collectTextLines(lines, startIndex) {
26397
- const { texts, end } = scanTextLines(lines, startIndex);
26431
+ function collectTextLines(lines2, startIndex) {
26432
+ const { texts, end } = scanTextLines(lines2, startIndex);
26398
26433
  return { text: texts.join(" "), nextIndex: end };
26399
26434
  }
26400
26435
  function parseTimestampLine(line) {
@@ -26405,30 +26440,30 @@ function buildCue(startMs, endMs, fullText) {
26405
26440
  const { speaker, text: text17 } = extractSpeaker(fullText);
26406
26441
  return text17 ? { startMs, endMs, speaker, text: text17 } : null;
26407
26442
  }
26408
- function parseCueLine(lines, i) {
26409
- const { startMs, endMs } = parseTimestampLine(lines[i]);
26410
- const { text: text17, nextIndex: nextIndex2 } = collectTextLines(lines, i + 1);
26443
+ function parseCueLine(lines2, i) {
26444
+ const { startMs, endMs } = parseTimestampLine(lines2[i]);
26445
+ const { text: text17, nextIndex: nextIndex2 } = collectTextLines(lines2, i + 1);
26411
26446
  return { cue: buildCue(startMs, endMs, text17), nextIndex: nextIndex2 };
26412
26447
  }
26413
26448
  function isCueSeparator(line) {
26414
26449
  return line.trim().includes("-->");
26415
26450
  }
26416
- function skipHeader(lines) {
26451
+ function skipHeader(lines2) {
26417
26452
  let i = 0;
26418
- while (i < lines.length && !isCueSeparator(lines[i])) i++;
26453
+ while (i < lines2.length && !isCueSeparator(lines2[i])) i++;
26419
26454
  return i;
26420
26455
  }
26421
- function processLine(cues, lines, i) {
26422
- if (!isCueSeparator(lines[i])) return i + 1;
26423
- const { cue, nextIndex: nextIndex2 } = parseCueLine(lines, i);
26456
+ function processLine(cues, lines2, i) {
26457
+ if (!isCueSeparator(lines2[i])) return i + 1;
26458
+ const { cue, nextIndex: nextIndex2 } = parseCueLine(lines2, i);
26424
26459
  if (cue) cues.push(cue);
26425
26460
  return nextIndex2;
26426
26461
  }
26427
26462
  function parseVtt(content) {
26428
26463
  const cues = [];
26429
- const lines = content.split(/\r?\n/);
26430
- let i = skipHeader(lines);
26431
- while (i < lines.length) i = processLine(cues, lines, i);
26464
+ const lines2 = content.split(/\r?\n/);
26465
+ let i = skipHeader(lines2);
26466
+ while (i < lines2.length) i = processLine(cues, lines2, i);
26432
26467
  return cues;
26433
26468
  }
26434
26469
 
@@ -26653,8 +26688,8 @@ function logs(options2) {
26653
26688
  console.log("Voice log is empty");
26654
26689
  return;
26655
26690
  }
26656
- const lines = content.split("\n").slice(-count8);
26657
- for (const line of lines) {
26691
+ const lines2 = content.split("\n").slice(-count8);
26692
+ for (const line of lines2) {
26658
26693
  try {
26659
26694
  const event = JSON.parse(line);
26660
26695
  const time = event.timestamp?.slice(11, 19) ?? "";
@@ -26802,8 +26837,8 @@ function isProcessAlive3(pid) {
26802
26837
  }
26803
26838
  function readRecentLogs(count8) {
26804
26839
  if (!existsSync56(voicePaths.log)) return [];
26805
- const lines = readFileSync46(voicePaths.log, "utf8").trim().split("\n");
26806
- return lines.slice(-count8);
26840
+ const lines2 = readFileSync46(voicePaths.log, "utf8").trim().split("\n");
26841
+ return lines2.slice(-count8);
26807
26842
  }
26808
26843
  function status2() {
26809
26844
  if (!existsSync56(voicePaths.pid)) {
@@ -26910,6 +26945,152 @@ function registerVoice(program2) {
26910
26945
  configHelp(voiceCommand, voiceConfigHelp);
26911
26946
  }
26912
26947
 
26948
+ // src/commands/watch/readBuiltVersion.ts
26949
+ import { join as join67 } from "path";
26950
+
26951
+ // src/commands/watch/resolveUpstream.ts
26952
+ import { execFileSync as execFileSync10 } from "child_process";
26953
+ function runGit2(args, cwd) {
26954
+ return execFileSync10("git", args, {
26955
+ encoding: "utf8",
26956
+ stdio: ["pipe", "pipe", "pipe"],
26957
+ cwd
26958
+ }).trim();
26959
+ }
26960
+ function resolveUpstream(cwd) {
26961
+ try {
26962
+ runGit2(["rev-parse", "--is-inside-work-tree"], cwd);
26963
+ } catch {
26964
+ throw new Error(
26965
+ "not a git repository \u2014 run assist watch wait from inside a repo"
26966
+ );
26967
+ }
26968
+ let branch2;
26969
+ try {
26970
+ branch2 = runGit2(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd);
26971
+ } catch {
26972
+ throw new Error(
26973
+ "HEAD is detached \u2014 check out a branch before waiting on its upstream"
26974
+ );
26975
+ }
26976
+ try {
26977
+ return {
26978
+ branch: branch2,
26979
+ upstream: runGit2(
26980
+ ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
26981
+ cwd
26982
+ )
26983
+ };
26984
+ } catch {
26985
+ throw new Error(
26986
+ `branch "${branch2}" has no upstream \u2014 set one with: git push -u origin ${branch2}`
26987
+ );
26988
+ }
26989
+ }
26990
+
26991
+ // src/commands/watch/readBuiltVersion.ts
26992
+ function readBuiltVersion(cwd) {
26993
+ try {
26994
+ const root = runGit2(["rev-parse", "--show-toplevel"], cwd);
26995
+ return readPackageJson(join67(root, "package.json")).version ?? "unknown";
26996
+ } catch {
26997
+ return "unknown";
26998
+ }
26999
+ }
27000
+
27001
+ // src/commands/watch/readRecentCommits.ts
27002
+ function readRecentCommits(count8 = 10, cwd) {
27003
+ const output = runGit2(
27004
+ ["log", `-${count8}`, "--pretty=format:%H%x09%h%x09%ar%x09%s"],
27005
+ cwd
27006
+ );
27007
+ if (!output) return [];
27008
+ return output.split("\n").map((line) => {
27009
+ const [sha, short2, when, ...subject] = line.split(" ");
27010
+ return { sha, short: short2, when, subject: subject.join(" ") };
27011
+ });
27012
+ }
27013
+
27014
+ // src/commands/watch/renderWatchReport.ts
27015
+ var escapeCell = (text17) => text17.replaceAll("|", String.raw`\|`);
27016
+ function renderWatchReport({
27017
+ version: version2,
27018
+ commits: commits2,
27019
+ newShas,
27020
+ restarts
27021
+ }) {
27022
+ const isNew = new Set(newShas);
27023
+ const lines2 = [`**Version** ${version2}`, ""];
27024
+ if (commits2.length === 0) {
27025
+ lines2.push("_no commits_");
27026
+ } else {
27027
+ lines2.push("| SHA | When | Subject |", "| --- | --- | --- |");
27028
+ for (const commit2 of commits2) {
27029
+ const marker = isNew.has(commit2.sha) ? " \u2190 new" : "";
27030
+ lines2.push(
27031
+ `| \`${commit2.short}\` | ${commit2.when} | ${escapeCell(commit2.subject)}${marker} |`
27032
+ );
27033
+ }
27034
+ }
27035
+ lines2.push("", "**Restarts**", "");
27036
+ lines2.push(
27037
+ ...restarts.length === 0 ? ["- none needed"] : restarts.map((restart) => `- ${restart}`)
27038
+ );
27039
+ return lines2.join("\n");
27040
+ }
27041
+
27042
+ // src/commands/watch/restartAdvice.ts
27043
+ var webUiPrefix = "src/commands/sessions/web/ui/";
27044
+ var sessionsPrefix = "src/commands/sessions/";
27045
+ var rules = [
27046
+ {
27047
+ matches: (path71) => path71.startsWith(webUiPrefix),
27048
+ advice: "restart the web server, then hard-reload the browser tab"
27049
+ },
27050
+ {
27051
+ matches: (path71) => path71.startsWith(sessionsPrefix) && !path71.startsWith(webUiPrefix),
27052
+ advice: "restart the daemon"
27053
+ }
27054
+ ];
27055
+ function restartAdvice(paths) {
27056
+ return rules.filter((rule) => paths.some(rule.matches)).map((rule) => rule.advice);
27057
+ }
27058
+
27059
+ // src/commands/watch/buildWatchReport.ts
27060
+ var lines = (output) => output.split("\n").filter((line) => line.length > 0);
27061
+ function buildWatchReport(from, cwd) {
27062
+ const range = from ? `${from}..HEAD` : void 0;
27063
+ return renderWatchReport({
27064
+ version: readBuiltVersion(cwd),
27065
+ commits: readRecentCommits(10, cwd),
27066
+ newShas: range ? lines(runGit2(["rev-list", range], cwd)) : [],
27067
+ restarts: restartAdvice(
27068
+ range ? lines(runGit2(["diff", "--name-only", range], cwd)) : []
27069
+ )
27070
+ });
27071
+ }
27072
+
27073
+ // src/commands/watch/gitFailureReason.ts
27074
+ function gitFailureReason(error) {
27075
+ const streams = error;
27076
+ for (const stream of [streams?.stderr, streams?.stdout]) {
27077
+ const text17 = stream == null ? "" : String(stream).trim();
27078
+ if (text17) return text17;
27079
+ }
27080
+ const message3 = error instanceof Error ? error.message : String(error);
27081
+ return message3.trim() || "git failed without reporting a reason";
27082
+ }
27083
+
27084
+ // src/commands/watch/watchReport.ts
27085
+ function watchReport(options2) {
27086
+ try {
27087
+ console.log(buildWatchReport(options2.from));
27088
+ } catch (error) {
27089
+ console.error(`cannot build the report: ${gitFailureReason(error)}`);
27090
+ process.exit(1);
27091
+ }
27092
+ }
27093
+
26913
27094
  // src/commands/watch/describeOutcome.ts
26914
27095
  var short = (sha) => sha.slice(0, 7);
26915
27096
  function describeOutcome(outcome) {
@@ -26962,72 +27143,248 @@ function parseDuration(value) {
26962
27143
  return amount * UNIT_MS[match[2]];
26963
27144
  }
26964
27145
 
26965
- // src/commands/watch/gitFailureReason.ts
26966
- function gitFailureReason(error) {
26967
- const streams = error;
26968
- for (const stream of [streams?.stderr, streams?.stdout]) {
26969
- const text17 = stream == null ? "" : String(stream).trim();
26970
- if (text17) return text17;
27146
+ // src/commands/watch/parseWatchDurations.ts
27147
+ function parseOrExit(value) {
27148
+ try {
27149
+ return parseDuration(value);
27150
+ } catch (error) {
27151
+ console.error(error instanceof Error ? error.message : String(error));
27152
+ return process.exit(1);
26971
27153
  }
26972
- const message3 = error instanceof Error ? error.message : String(error);
26973
- return message3.trim() || "git failed without reporting a reason";
27154
+ }
27155
+ function parseWatchDurations(interval, timeout) {
27156
+ return {
27157
+ intervalMs: parseOrExit(interval),
27158
+ timeoutMs: timeout.trim() === "none" ? void 0 : parseOrExit(timeout)
27159
+ };
26974
27160
  }
26975
27161
 
26976
- // src/commands/watch/resolveUpstream.ts
26977
- import { execFileSync as execFileSync10 } from "child_process";
26978
- function runGit2(args, cwd) {
26979
- return execFileSync10("git", args, {
26980
- encoding: "utf8",
26981
- stdio: ["pipe", "pipe", "pipe"],
26982
- cwd
26983
- }).trim();
27162
+ // src/commands/watch/pullFastForward.ts
27163
+ var STASH_MESSAGE = "assist watch";
27164
+ function attemptGit(args, cwd) {
27165
+ try {
27166
+ runGit2(args, cwd);
27167
+ return { ok: true };
27168
+ } catch (error) {
27169
+ return { ok: false, reason: gitFailureReason(error) };
27170
+ }
26984
27171
  }
26985
- function resolveUpstream(cwd) {
27172
+ function fastForwarded(cwd) {
27173
+ return { kind: "fast-forwarded", sha: runGit2(["rev-parse", "@"], cwd) };
27174
+ }
27175
+ function operationInProgress(cwd) {
27176
+ return ["MERGE_HEAD", "REBASE_HEAD"].some(
27177
+ (ref) => attemptGit(["rev-parse", "--verify", "--quiet", ref], cwd).ok
27178
+ );
27179
+ }
27180
+ function headMatchesUpstream(cwd) {
26986
27181
  try {
26987
- runGit2(["rev-parse", "--is-inside-work-tree"], cwd);
27182
+ return runGit2(["rev-parse", "@"], cwd) === runGit2(["rev-parse", "@{u}"], cwd);
26988
27183
  } catch {
26989
- throw new Error(
26990
- "not a git repository \u2014 run assist watch wait from inside a repo"
26991
- );
27184
+ return false;
26992
27185
  }
26993
- let branch2;
27186
+ }
27187
+ function behindUpstream(cwd) {
27188
+ return attemptGit(["merge-base", "--is-ancestor", "@", "@{u}"], cwd).ok;
27189
+ }
27190
+ function stashDirtyTree(cwd) {
27191
+ let dirty;
26994
27192
  try {
26995
- branch2 = runGit2(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd);
26996
- } catch {
26997
- throw new Error(
26998
- "HEAD is detached \u2014 check out a branch before waiting on its upstream"
26999
- );
27193
+ dirty = runGit2(["status", "--porcelain"], cwd) !== "";
27194
+ } catch (error) {
27195
+ return { ok: false, reason: gitFailureReason(error) };
27196
+ }
27197
+ if (!dirty) return { ok: true, stashed: false };
27198
+ const push = attemptGit(
27199
+ ["stash", "push", "--include-untracked", "--message", STASH_MESSAGE],
27200
+ cwd
27201
+ );
27202
+ return push.ok ? { ok: true, stashed: true } : push;
27203
+ }
27204
+ function mergeBehindBranch(cwd) {
27205
+ const stash = stashDirtyTree(cwd);
27206
+ if (!stash.ok) return { kind: "blocked", reason: stash.reason };
27207
+ const merge = attemptGit(["merge", "--ff-only", "@{u}"], cwd);
27208
+ const restore2 = stash.stashed ? attemptGit(["stash", "pop"], cwd) : { ok: true };
27209
+ if (!merge.ok) return { kind: "blocked", reason: merge.reason };
27210
+ if (!restore2.ok) return { kind: "blocked", reason: restore2.reason };
27211
+ return fastForwarded(cwd);
27212
+ }
27213
+ function pullFastForward(cwd) {
27214
+ const pull = attemptGit(["pull", "--ff-only"], cwd);
27215
+ if (pull.ok) return fastForwarded(cwd);
27216
+ if (operationInProgress(cwd)) return { kind: "blocked", reason: pull.reason };
27217
+ if (headMatchesUpstream(cwd)) return fastForwarded(cwd);
27218
+ if (!behindUpstream(cwd)) return { kind: "blocked", reason: pull.reason };
27219
+ return mergeBehindBranch(cwd);
27220
+ }
27221
+
27222
+ // src/commands/watch/runWatchBuild.ts
27223
+ import { resolve as resolve17 } from "path";
27224
+
27225
+ // src/commands/run/findRunConfig.ts
27226
+ function exitNoRunConfigs() {
27227
+ console.error("No run configurations found in assist.yml");
27228
+ process.exit(1);
27229
+ }
27230
+ function exitWithConfigNotFound(name, configs) {
27231
+ console.error(`No run configuration found with name: ${name}`);
27232
+ console.error("Available configurations:");
27233
+ for (const r of configs) {
27234
+ console.error(` - ${r.name}`);
27235
+ }
27236
+ process.exit(1);
27237
+ }
27238
+ function exitWithAmbiguousConfig(name, matches) {
27239
+ console.error(`Ambiguous run configuration: ${name}`);
27240
+ console.error("Did you mean:");
27241
+ for (const r of matches) {
27242
+ console.error(` - ${r.name}`);
27243
+ }
27244
+ process.exit(1);
27245
+ }
27246
+ function requireRunConfigs() {
27247
+ const { run: run4 } = loadConfig();
27248
+ const configs = resolveRunConfigs(run4, getConfigDir());
27249
+ if (configs.length === 0) return exitNoRunConfigs();
27250
+ return configs;
27251
+ }
27252
+ function lookupRunConfig(name) {
27253
+ const configs = requireRunConfigs();
27254
+ const exact = configs.find((r) => r.name === name);
27255
+ if (exact) return { kind: "match", config: exact };
27256
+ const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
27257
+ if (suffixMatches.length === 1)
27258
+ return { kind: "match", config: suffixMatches[0] };
27259
+ if (suffixMatches.length > 1)
27260
+ return { kind: "ambiguous", matches: suffixMatches };
27261
+ return { kind: "not-found" };
27262
+ }
27263
+ function findRunConfig(name) {
27264
+ const result = lookupRunConfig(name);
27265
+ if (result.kind === "match") return result.config;
27266
+ if (result.kind === "ambiguous")
27267
+ return exitWithAmbiguousConfig(name, result.matches);
27268
+ return exitWithConfigNotFound(name, requireRunConfigs());
27269
+ }
27270
+
27271
+ // src/commands/run/resolveParams.ts
27272
+ function resolveParams(params, cliArgs) {
27273
+ if (!params || params.length === 0) return cliArgs;
27274
+ const resolved = [];
27275
+ const missing = [];
27276
+ for (let i = 0; i < params.length; i++) {
27277
+ const param = params[i];
27278
+ const value = cliArgs[i] ?? param.default;
27279
+ if (value !== void 0) {
27280
+ resolved.push(value);
27281
+ } else if (param.required) {
27282
+ missing.push(param.name);
27283
+ }
27284
+ }
27285
+ if (missing.length > 0) {
27286
+ const s = missing.length > 1 ? "s" : "";
27287
+ const names = missing.map((n) => `"${n}"`).join(", ");
27288
+ console.error(`Missing required param${s}: ${names}`);
27289
+ process.exit(1);
27000
27290
  }
27291
+ resolved.push(...cliArgs.slice(params.length));
27292
+ return resolved;
27293
+ }
27294
+
27295
+ // src/commands/run/runCommandToCompletion.ts
27296
+ import { execFileSync as execFileSync11, spawn as spawn9 } from "child_process";
27297
+ import { existsSync as existsSync58 } from "fs";
27298
+ import { dirname as dirname31, join as join68, resolve as resolve16 } from "path";
27299
+ function resolveCommand2(command) {
27300
+ if (process.platform !== "win32" || command !== "bash") return command;
27001
27301
  try {
27002
- return {
27003
- branch: branch2,
27004
- upstream: runGit2(
27005
- ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
27006
- cwd
27007
- )
27008
- };
27302
+ const gitPath = execFileSync11("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
27303
+ const gitRoot = resolve16(dirname31(gitPath), "..");
27304
+ const gitBash = join68(gitRoot, "bin", "bash.exe");
27305
+ if (existsSync58(gitBash)) return gitBash;
27009
27306
  } catch {
27010
- throw new Error(
27011
- `branch "${branch2}" has no upstream \u2014 set one with: git push -u origin ${branch2}`
27012
- );
27307
+ return command;
27013
27308
  }
27309
+ return command;
27310
+ }
27311
+ function runCommandToCompletion(command, args, env, cwd, quiet) {
27312
+ return new Promise((resolveResult) => {
27313
+ const child = spawn9(resolveCommand2(command), args, {
27314
+ stdio: quiet ? "pipe" : "inherit",
27315
+ env: env ? { ...process.env, ...expandEnv(env) } : void 0,
27316
+ cwd
27317
+ });
27318
+ const chunks = [];
27319
+ if (quiet) {
27320
+ child.stdout?.on("data", (data) => chunks.push(data));
27321
+ child.stderr?.on("data", (data) => chunks.push(data));
27322
+ }
27323
+ child.on("close", (code) => {
27324
+ resolveResult({
27325
+ kind: "completed",
27326
+ exitCode: code ?? 0,
27327
+ output: Buffer.concat(chunks).toString()
27328
+ });
27329
+ });
27330
+ child.on("error", (err) => {
27331
+ resolveResult({
27332
+ kind: "failed",
27333
+ message: `Failed to execute command: ${err.message}`
27334
+ });
27335
+ });
27336
+ });
27014
27337
  }
27015
27338
 
27016
- // src/commands/watch/pullFastForward.ts
27017
- function pullFastForward(cwd) {
27018
- try {
27019
- runGit2(["pull", "--ff-only"], cwd);
27020
- return { kind: "fast-forwarded", sha: runGit2(["rev-parse", "@"], cwd) };
27021
- } catch (error) {
27022
- return { kind: "blocked", reason: gitFailureReason(error) };
27339
+ // src/commands/run/runPreCommands.ts
27340
+ import { execSync as execSync60 } from "child_process";
27341
+ function runPreCommands(pre, cwd) {
27342
+ for (const cmd of pre) {
27343
+ try {
27344
+ execSync60(cmd, { stdio: "inherit", cwd });
27345
+ } catch (error) {
27346
+ const code = error && typeof error === "object" && "status" in error ? error.status : 1;
27347
+ process.exit(code);
27348
+ }
27349
+ }
27350
+ }
27351
+
27352
+ // src/commands/watch/runWatchBuild.ts
27353
+ async function runWatchBuild(entry) {
27354
+ const config = findRunConfig(entry);
27355
+ const cwd = config.cwd ? resolve17(getConfigDir(), config.cwd) : void 0;
27356
+ if (config.pre) runPreCommands(config.pre, cwd);
27357
+ const result = await runCommandToCompletion(
27358
+ config.command,
27359
+ [...config.args ?? [], ...resolveParams(config.params, [])],
27360
+ config.env,
27361
+ cwd,
27362
+ config.quiet
27363
+ );
27364
+ if (result.kind === "failed")
27365
+ return { kind: "failed", exitCode: 1, output: result.message };
27366
+ if (result.exitCode !== 0)
27367
+ return { kind: "failed", exitCode: result.exitCode, output: result.output };
27368
+ return { kind: "built" };
27369
+ }
27370
+
27371
+ // src/commands/watch/reportBuildOrExit.ts
27372
+ async function reportBuildOrExit(entry) {
27373
+ const outcome = await runWatchBuild(entry);
27374
+ if (outcome.kind === "built") {
27375
+ console.log(`built with "${entry}"`);
27376
+ return;
27023
27377
  }
27378
+ if (outcome.output.length > 0) process.stdout.write(outcome.output);
27379
+ console.error(`build "${entry}" failed with exit code ${outcome.exitCode}`);
27380
+ process.exit(4);
27024
27381
  }
27025
27382
 
27026
27383
  // src/commands/watch/fetchQuietly.ts
27027
- import { execFileSync as execFileSync11 } from "child_process";
27384
+ import { execFileSync as execFileSync12 } from "child_process";
27028
27385
  function fetchQuietly(cwd, timeoutMs) {
27029
27386
  try {
27030
- execFileSync11("git", ["fetch", "--quiet"], {
27387
+ execFileSync12("git", ["fetch", "--quiet"], {
27031
27388
  stdio: ["pipe", "pipe", "pipe"],
27032
27389
  cwd,
27033
27390
  timeout: timeoutMs
@@ -27055,26 +27412,12 @@ function readMovement(cwd) {
27055
27412
  }
27056
27413
  }
27057
27414
 
27058
- // src/commands/watch/waitForUpstream.ts
27415
+ // src/commands/watch/pollForMovement.ts
27059
27416
  var MIN_FETCH_TIMEOUT_MS = 6e4;
27060
- function waitForUpstream(options2) {
27061
- const { intervalMs, timeoutMs, timeout, cwd, onStart } = options2;
27062
- let upstream;
27063
- try {
27064
- upstream = resolveUpstream(cwd).upstream;
27065
- } catch (error) {
27066
- return Promise.resolve({
27067
- kind: "unavailable",
27068
- reason: error instanceof Error ? error.message : String(error)
27069
- });
27070
- }
27071
- onStart?.(upstream);
27072
- const moved = readMovement(cwd);
27073
- if (moved) {
27074
- return Promise.resolve({ kind: "moved", upstream, ...moved });
27075
- }
27417
+ function pollForMovement(options2) {
27418
+ const { upstream, intervalMs, timeoutMs, timeout, cwd } = options2;
27076
27419
  const fetchTimeoutMs = Math.max(intervalMs, MIN_FETCH_TIMEOUT_MS);
27077
- return new Promise((resolve21) => {
27420
+ return new Promise((resolve22) => {
27078
27421
  let settled = false;
27079
27422
  const finish = (outcome) => {
27080
27423
  if (settled) return;
@@ -27082,7 +27425,7 @@ function waitForUpstream(options2) {
27082
27425
  clearInterval(ticker);
27083
27426
  clearTimeout(deadline);
27084
27427
  process.off("SIGINT", onInterrupt);
27085
- resolve21(outcome);
27428
+ resolve22(outcome);
27086
27429
  };
27087
27430
  const onInterrupt = () => finish({ kind: "interrupted" });
27088
27431
  const ticker = setInterval(() => {
@@ -27090,7 +27433,7 @@ function waitForUpstream(options2) {
27090
27433
  const found = readMovement(cwd);
27091
27434
  if (found) finish({ kind: "moved", upstream, ...found });
27092
27435
  }, intervalMs);
27093
- const deadline = setTimeout(
27436
+ const deadline = timeoutMs === void 0 ? void 0 : setTimeout(
27094
27437
  () => finish({ kind: "timeout", upstream, timeout }),
27095
27438
  timeoutMs
27096
27439
  );
@@ -27098,22 +27441,35 @@ function waitForUpstream(options2) {
27098
27441
  });
27099
27442
  }
27100
27443
 
27101
- // src/commands/watch/watchWait.ts
27102
- function parseOrExit(value) {
27444
+ // src/commands/watch/waitForUpstream.ts
27445
+ function waitForUpstream(options2) {
27446
+ const { intervalMs, timeoutMs, timeout, cwd, onStart } = options2;
27447
+ let upstream;
27103
27448
  try {
27104
- return parseDuration(value);
27449
+ upstream = resolveUpstream(cwd).upstream;
27105
27450
  } catch (error) {
27106
- console.error(error instanceof Error ? error.message : String(error));
27107
- return process.exit(1);
27451
+ return Promise.resolve({
27452
+ kind: "unavailable",
27453
+ reason: error instanceof Error ? error.message : String(error)
27454
+ });
27108
27455
  }
27456
+ onStart?.(upstream);
27457
+ const moved = readMovement(cwd);
27458
+ if (moved) return Promise.resolve({ kind: "moved", upstream, ...moved });
27459
+ return pollForMovement({ upstream, intervalMs, timeoutMs, timeout, cwd });
27109
27460
  }
27461
+
27462
+ // src/commands/watch/watchWait.ts
27463
+ var DEFAULT_BUILD_ENTRY = "auto-build";
27110
27464
  function report({ exitCode, message: message3 }) {
27111
27465
  if (exitCode === 0) console.log(message3);
27112
27466
  else console.error(message3);
27113
27467
  }
27114
27468
  async function watchWait(options2) {
27115
- const intervalMs = parseOrExit(options2.interval);
27116
- const timeoutMs = parseOrExit(options2.timeout);
27469
+ const { intervalMs, timeoutMs } = parseWatchDurations(
27470
+ options2.interval,
27471
+ options2.timeout
27472
+ );
27117
27473
  const outcome = await waitForUpstream({
27118
27474
  intervalMs,
27119
27475
  timeoutMs,
@@ -27122,29 +27478,47 @@ async function watchWait(options2) {
27122
27478
  });
27123
27479
  const waitReport = describeOutcome(outcome);
27124
27480
  report(waitReport);
27125
- if (outcome.kind === "moved" && options2.pull) {
27126
- const pullReport = describePull(pullFastForward());
27127
- report(pullReport);
27128
- process.exit(pullReport.exitCode);
27481
+ if (outcome.kind !== "moved" || !options2.pull)
27482
+ return process.exit(waitReport.exitCode);
27483
+ const pullResult = pullFastForward();
27484
+ const pullReport = describePull(pullResult);
27485
+ report(pullReport);
27486
+ if (pullResult.kind !== "fast-forwarded")
27487
+ return process.exit(pullReport.exitCode);
27488
+ console.log(`
27489
+ ${buildWatchReport(outcome.from)}`);
27490
+ if (options2.build) {
27491
+ await reportBuildOrExit(
27492
+ typeof options2.build === "string" ? options2.build : DEFAULT_BUILD_ENTRY
27493
+ );
27129
27494
  }
27130
- process.exit(waitReport.exitCode);
27495
+ process.exit(0);
27131
27496
  }
27132
27497
 
27133
27498
  // src/commands/registerWatch.ts
27134
27499
  function registerWatch(program2) {
27135
27500
  const watchCommand = program2.command("watch").description("Wait on upstream movement for the current branch");
27136
27501
  watchCommand.command("wait").description(
27137
- "Block until the current branch's upstream gains commits, then exit 0 (2 on timeout, 3 when --pull is not a fast-forward, 1 when waiting is impossible, 130 on interrupt)"
27502
+ "Block until the current branch's upstream gains commits, then exit 0 (2 on timeout, 3 when --pull hits genuine divergence, 4 when --build fails, 1 when waiting is impossible, 130 on interrupt)"
27138
27503
  ).option("--interval <duration>", "How often to fetch (e.g. 30s, 2m)", "30s").option(
27139
27504
  "--timeout <duration>",
27140
- "Give up and exit 2 after this long (e.g. 60m, 2h)",
27141
- "60m"
27505
+ "Give up and exit 2 after this long (e.g. 60m, 2h), or none to wait indefinitely",
27506
+ "none"
27142
27507
  ).option(
27143
27508
  "--pull",
27144
- "On movement, fast-forward with git pull --ff-only; exit 3 with git's reason if it is not a clean fast-forward"
27509
+ "On movement, fast-forward with git pull --ff-only, recovering a dirty tree or a merely-behind branch; exit 3 with git's reason on genuine divergence"
27510
+ ).option(
27511
+ "--build [entry]",
27512
+ "After a successful pull, run this run entry (default auto-build); exit 4 with its output when it fails"
27145
27513
  ).action(
27146
27514
  (options2) => watchWait(options2)
27147
27515
  );
27516
+ watchCommand.command("report").description(
27517
+ "Print the built version, the last 10 commits as a markdown table, and the restarts the new commits make necessary"
27518
+ ).option(
27519
+ "--from <sha>",
27520
+ "Mark commits reachable from HEAD but not <sha> as new, and derive restart advice from the files they changed"
27521
+ ).action((options2) => watchReport(options2));
27148
27522
  }
27149
27523
 
27150
27524
  // src/commands/roam/auth.ts
@@ -27170,7 +27544,7 @@ function extractCode(url, expectedState) {
27170
27544
  return code;
27171
27545
  }
27172
27546
  function waitForCallback(port, expectedState) {
27173
- return new Promise((resolve21, reject) => {
27547
+ return new Promise((resolve22, reject) => {
27174
27548
  const timeout = setTimeout(() => {
27175
27549
  server.close();
27176
27550
  reject(new Error("Authorization timed out after 120 seconds"));
@@ -27187,7 +27561,7 @@ function waitForCallback(port, expectedState) {
27187
27561
  const code = extractCode(url, expectedState);
27188
27562
  respondHtml(res, 200, "Authorization successful!");
27189
27563
  server.close();
27190
- resolve21(code);
27564
+ resolve22(code);
27191
27565
  } catch (error) {
27192
27566
  respondHtml(res, 400, error.message);
27193
27567
  server.close();
@@ -27307,9 +27681,9 @@ async function auth() {
27307
27681
  }
27308
27682
 
27309
27683
  // src/commands/roam/postRoamActivity.ts
27310
- import { execFileSync as execFileSync12 } from "child_process";
27684
+ import { execFileSync as execFileSync13 } from "child_process";
27311
27685
  import { readdirSync as readdirSync12, readFileSync as readFileSync48, statSync as statSync10 } from "fs";
27312
- import { join as join67 } from "path";
27686
+ import { join as join69 } from "path";
27313
27687
  function findPortFile(roamDir) {
27314
27688
  let entries;
27315
27689
  try {
@@ -27318,7 +27692,7 @@ function findPortFile(roamDir) {
27318
27692
  return void 0;
27319
27693
  }
27320
27694
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
27321
- const path71 = join67(roamDir, name);
27695
+ const path71 = join69(roamDir, name);
27322
27696
  try {
27323
27697
  return { path: path71, mtimeMs: statSync10(path71).mtimeMs };
27324
27698
  } catch {
@@ -27330,7 +27704,7 @@ function findPortFile(roamDir) {
27330
27704
  function postRoamActivity(app, event) {
27331
27705
  const appData = process.env.APPDATA;
27332
27706
  if (!appData) return;
27333
- const portFile = findPortFile(join67(appData, "Roam"));
27707
+ const portFile = findPortFile(join69(appData, "Roam"));
27334
27708
  if (!portFile) return;
27335
27709
  let port;
27336
27710
  try {
@@ -27340,7 +27714,7 @@ function postRoamActivity(app, event) {
27340
27714
  }
27341
27715
  const url = `http://127.0.0.1:${port}/api/v1/activity/${app}/${event}?pid=${app === "codex" ? 99998 : 99999}`;
27342
27716
  try {
27343
- execFileSync12("curl", ["-sf", "--max-time", "0.2", "-X", "POST", url], {
27717
+ execFileSync13("curl", ["-sf", "--max-time", "0.2", "-X", "POST", url], {
27344
27718
  stdio: "ignore"
27345
27719
  });
27346
27720
  } catch {
@@ -27464,53 +27838,7 @@ var rootConfigHelp = {
27464
27838
  };
27465
27839
 
27466
27840
  // src/commands/run/index.ts
27467
- import { resolve as resolve17 } from "path";
27468
-
27469
- // src/commands/run/findRunConfig.ts
27470
- function exitNoRunConfigs() {
27471
- console.error("No run configurations found in assist.yml");
27472
- process.exit(1);
27473
- }
27474
- function exitWithConfigNotFound(name, configs) {
27475
- console.error(`No run configuration found with name: ${name}`);
27476
- console.error("Available configurations:");
27477
- for (const r of configs) {
27478
- console.error(` - ${r.name}`);
27479
- }
27480
- process.exit(1);
27481
- }
27482
- function exitWithAmbiguousConfig(name, matches) {
27483
- console.error(`Ambiguous run configuration: ${name}`);
27484
- console.error("Did you mean:");
27485
- for (const r of matches) {
27486
- console.error(` - ${r.name}`);
27487
- }
27488
- process.exit(1);
27489
- }
27490
- function requireRunConfigs() {
27491
- const { run: run4 } = loadConfig();
27492
- const configs = resolveRunConfigs(run4, getConfigDir());
27493
- if (configs.length === 0) return exitNoRunConfigs();
27494
- return configs;
27495
- }
27496
- function lookupRunConfig(name) {
27497
- const configs = requireRunConfigs();
27498
- const exact = configs.find((r) => r.name === name);
27499
- if (exact) return { kind: "match", config: exact };
27500
- const suffixMatches = configs.filter((r) => r.name.endsWith(`:${name}`));
27501
- if (suffixMatches.length === 1)
27502
- return { kind: "match", config: suffixMatches[0] };
27503
- if (suffixMatches.length > 1)
27504
- return { kind: "ambiguous", matches: suffixMatches };
27505
- return { kind: "not-found" };
27506
- }
27507
- function findRunConfig(name) {
27508
- const result = lookupRunConfig(name);
27509
- if (result.kind === "match") return result.config;
27510
- if (result.kind === "ambiguous")
27511
- return exitWithAmbiguousConfig(name, result.matches);
27512
- return exitWithConfigNotFound(name, requireRunConfigs());
27513
- }
27841
+ import { resolve as resolve18 } from "path";
27514
27842
 
27515
27843
  // src/commands/run/formatConfiguredCommands.ts
27516
27844
  function formatConfiguredCommands() {
@@ -27523,84 +27851,23 @@ Configured commands:
27523
27851
  ${names}`;
27524
27852
  }
27525
27853
 
27526
- // src/commands/run/resolveParams.ts
27527
- function resolveParams(params, cliArgs) {
27528
- if (!params || params.length === 0) return cliArgs;
27529
- const resolved = [];
27530
- const missing = [];
27531
- for (let i = 0; i < params.length; i++) {
27532
- const param = params[i];
27533
- const value = cliArgs[i] ?? param.default;
27534
- if (value !== void 0) {
27535
- resolved.push(value);
27536
- } else if (param.required) {
27537
- missing.push(param.name);
27538
- }
27539
- }
27540
- if (missing.length > 0) {
27541
- const s = missing.length > 1 ? "s" : "";
27542
- const names = missing.map((n) => `"${n}"`).join(", ");
27543
- console.error(`Missing required param${s}: ${names}`);
27544
- process.exit(1);
27545
- }
27546
- resolved.push(...cliArgs.slice(params.length));
27547
- return resolved;
27548
- }
27549
-
27550
- // src/commands/run/runPreCommands.ts
27551
- import { execSync as execSync60 } from "child_process";
27552
- function runPreCommands(pre, cwd) {
27553
- for (const cmd of pre) {
27554
- try {
27555
- execSync60(cmd, { stdio: "inherit", cwd });
27556
- } catch (error) {
27557
- const code = error && typeof error === "object" && "status" in error ? error.status : 1;
27558
- process.exit(code);
27559
- }
27560
- }
27561
- }
27562
-
27563
27854
  // src/commands/run/spawnRunCommand.ts
27564
- import { execFileSync as execFileSync13, spawn as spawn9 } from "child_process";
27565
- import { existsSync as existsSync58 } from "fs";
27566
- import { dirname as dirname31, join as join68, resolve as resolve16 } from "path";
27567
- function resolveCommand2(command) {
27568
- if (process.platform !== "win32" || command !== "bash") return command;
27569
- try {
27570
- const gitPath = execFileSync13("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
27571
- const gitRoot = resolve16(dirname31(gitPath), "..");
27572
- const gitBash = join68(gitRoot, "bin", "bash.exe");
27573
- if (existsSync58(gitBash)) return gitBash;
27574
- } catch {
27575
- }
27576
- return command;
27577
- }
27578
27855
  function spawnRunCommand(command, args, env, cwd, quiet) {
27579
27856
  const start3 = Date.now();
27580
- const child = spawn9(resolveCommand2(command), args, {
27581
- stdio: quiet ? "pipe" : "inherit",
27582
- env: env ? { ...process.env, ...expandEnv(env) } : void 0,
27583
- cwd
27584
- });
27585
- const chunks = [];
27586
- if (quiet) {
27587
- child.stdout?.on("data", (data) => chunks.push(data));
27588
- child.stderr?.on("data", (data) => chunks.push(data));
27589
- }
27590
- child.on("close", (code) => {
27591
- const exitCode = code ?? 0;
27592
- if (quiet && exitCode !== 0 && chunks.length > 0) {
27593
- process.stdout.write(Buffer.concat(chunks));
27857
+ void runCommandToCompletion(command, args, env, cwd, quiet).then((result) => {
27858
+ if (result.kind === "failed") {
27859
+ console.error(result.message);
27860
+ process.exit(1);
27861
+ }
27862
+ const { exitCode, output } = result;
27863
+ if (quiet && exitCode !== 0 && output.length > 0) {
27864
+ process.stdout.write(output);
27594
27865
  }
27595
27866
  const elapsed = formatElapsed(Date.now() - start3);
27596
27867
  if (!quiet || exitCode !== 0) console.log(`
27597
27868
  Done in ${elapsed}`);
27598
27869
  process.exit(exitCode);
27599
27870
  });
27600
- child.on("error", (err) => {
27601
- console.error(`Failed to execute command: ${err.message}`);
27602
- process.exit(1);
27603
- });
27604
27871
  }
27605
27872
 
27606
27873
  // src/commands/run/index.ts
@@ -27616,7 +27883,7 @@ function listRunConfigs(verbose) {
27616
27883
  }
27617
27884
  }
27618
27885
  function execRunConfig(config, args) {
27619
- const cwd = config.cwd ? resolve17(getConfigDir(), config.cwd) : void 0;
27886
+ const cwd = config.cwd ? resolve18(getConfigDir(), config.cwd) : void 0;
27620
27887
  if (config.pre) runPreCommands(config.pre, cwd);
27621
27888
  const resolved = resolveParams(config.params, args);
27622
27889
  spawnRunCommand(
@@ -27658,7 +27925,7 @@ async function run3(name, args) {
27658
27925
 
27659
27926
  // src/commands/run/add.ts
27660
27927
  import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync41 } from "fs";
27661
- import { join as join69 } from "path";
27928
+ import { join as join70 } from "path";
27662
27929
 
27663
27930
  // src/commands/run/extractOption.ts
27664
27931
  function extractOption(args, flag) {
@@ -27719,7 +27986,7 @@ function saveNewRunConfig(name, command, args, cwd) {
27719
27986
  saveConfig(config);
27720
27987
  }
27721
27988
  function createCommandFile(name) {
27722
- const dir = join69(".claude", "commands");
27989
+ const dir = join70(".claude", "commands");
27723
27990
  mkdirSync24(dir, { recursive: true });
27724
27991
  const content = `---
27725
27992
  description: Run ${name}
@@ -27727,7 +27994,7 @@ description: Run ${name}
27727
27994
 
27728
27995
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
27729
27996
  `;
27730
- const filePath = join69(dir, `${name}.md`);
27997
+ const filePath = join70(dir, `${name}.md`);
27731
27998
  writeFileSync41(filePath, content);
27732
27999
  console.log(`Created command file: ${filePath}`);
27733
28000
  }
@@ -27784,7 +28051,7 @@ function link2() {
27784
28051
 
27785
28052
  // src/commands/run/remove.ts
27786
28053
  import { existsSync as existsSync59, unlinkSync as unlinkSync21 } from "fs";
27787
- import { join as join70 } from "path";
28054
+ import { join as join71 } from "path";
27788
28055
  function findRemoveIndex() {
27789
28056
  const idx = process.argv.indexOf("remove");
27790
28057
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -27799,7 +28066,7 @@ function parseRemoveName() {
27799
28066
  return process.argv[idx + 1];
27800
28067
  }
27801
28068
  function deleteCommandFile(name) {
27802
- const filePath = join70(".claude", "commands", `${name}.md`);
28069
+ const filePath = join71(".claude", "commands", `${name}.md`);
27803
28070
  if (existsSync59(filePath)) {
27804
28071
  unlinkSync21(filePath);
27805
28072
  console.log(`Deleted command file: ${filePath}`);
@@ -27856,7 +28123,7 @@ function registerRun(program2) {
27856
28123
  import { execSync as execSync61 } from "child_process";
27857
28124
  import { existsSync as existsSync60, mkdirSync as mkdirSync25, unlinkSync as unlinkSync22, writeFileSync as writeFileSync42 } from "fs";
27858
28125
  import { tmpdir as tmpdir8 } from "os";
27859
- import { join as join71, resolve as resolve18 } from "path";
28126
+ import { join as join72, resolve as resolve19 } from "path";
27860
28127
  import chalk209 from "chalk";
27861
28128
 
27862
28129
  // src/commands/screenshot/captureWindowPs1.ts
@@ -27990,10 +28257,10 @@ function buildOutputPath(outputDir, processName) {
27990
28257
  mkdirSync25(outputDir, { recursive: true });
27991
28258
  }
27992
28259
  const timestamp6 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27993
- return resolve18(outputDir, `${processName}-${timestamp6}.png`);
28260
+ return resolve19(outputDir, `${processName}-${timestamp6}.png`);
27994
28261
  }
27995
28262
  function runPowerShellScript(processName, outputPath) {
27996
- const scriptPath = join71(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
28263
+ const scriptPath = join72(tmpdir8(), `assist-screenshot-${Date.now()}.ps1`);
27997
28264
  writeFileSync42(scriptPath, captureWindowPs1, "utf8");
27998
28265
  try {
27999
28266
  execSync61(
@@ -28006,7 +28273,7 @@ function runPowerShellScript(processName, outputPath) {
28006
28273
  }
28007
28274
  function screenshot(processName) {
28008
28275
  const config = loadConfig();
28009
- const outputDir = resolve18(config.screenshot.outputDir);
28276
+ const outputDir = resolve19(config.screenshot.outputDir);
28010
28277
  const outputPath = buildOutputPath(outputDir, processName);
28011
28278
  console.log(chalk209.gray(`Capturing window for process "${processName}" ...`));
28012
28279
  try {
@@ -28039,18 +28306,18 @@ var STATUS_TIMEOUT_MS = 5e3;
28039
28306
  function queryDaemon(socket) {
28040
28307
  socket.write(`${JSON.stringify({ type: "ping" })}
28041
28308
  `);
28042
- return new Promise((resolve21) => {
28309
+ return new Promise((resolve22) => {
28043
28310
  const result = { sessions: [] };
28044
28311
  const pending = /* @__PURE__ */ new Set(["sessions", "pong"]);
28045
- const timer = setTimeout(() => resolve21(result), STATUS_TIMEOUT_MS);
28046
- const lines = createInterface5({ input: socket });
28047
- lines.on("error", () => {
28312
+ const timer = setTimeout(() => resolve22(result), STATUS_TIMEOUT_MS);
28313
+ const lines2 = createInterface5({ input: socket });
28314
+ lines2.on("error", () => {
28048
28315
  });
28049
- lines.on("line", (line) => {
28316
+ lines2.on("line", (line) => {
28050
28317
  applyLine(result, pending, line);
28051
28318
  if (pending.size === 0) {
28052
28319
  clearTimeout(timer);
28053
- resolve21(result);
28320
+ resolve22(result);
28054
28321
  }
28055
28322
  });
28056
28323
  });
@@ -28135,12 +28402,12 @@ function clearPersistedSessionsOnDrain() {
28135
28402
  }
28136
28403
 
28137
28404
  // src/commands/sessions/daemon/readDaemonMessage.ts
28138
- function readDaemonMessage(lines, timeoutMs, fallback, match) {
28139
- return new Promise((resolve21) => {
28405
+ function readDaemonMessage(lines2, timeoutMs, fallback, match) {
28406
+ return new Promise((resolve22) => {
28140
28407
  const finish = (value) => {
28141
28408
  clearTimeout(timer);
28142
- lines.off("line", onLine);
28143
- resolve21(value);
28409
+ lines2.off("line", onLine);
28410
+ resolve22(value);
28144
28411
  };
28145
28412
  const timer = setTimeout(() => finish(fallback), timeoutMs);
28146
28413
  const onLine = (line) => {
@@ -28150,7 +28417,7 @@ function readDaemonMessage(lines, timeoutMs, fallback, match) {
28150
28417
  } catch {
28151
28418
  }
28152
28419
  };
28153
- lines.on("line", onLine);
28420
+ lines2.on("line", onLine);
28154
28421
  });
28155
28422
  }
28156
28423
 
@@ -28165,10 +28432,10 @@ async function drainDaemon(options2 = {}) {
28165
28432
  clearPersistedSessionsOnDrain();
28166
28433
  return;
28167
28434
  }
28168
- const lines = createInterface6({ input: socket });
28169
- lines.on("error", () => {
28435
+ const lines2 = createInterface6({ input: socket });
28436
+ lines2.on("error", () => {
28170
28437
  });
28171
- const live = await liveSessions(lines);
28438
+ const live = await liveSessions(lines2);
28172
28439
  if (live.length > 0 && options2.yes !== true) {
28173
28440
  reportLive(live);
28174
28441
  if (!await confirmDrain()) {
@@ -28176,7 +28443,7 @@ async function drainDaemon(options2 = {}) {
28176
28443
  return;
28177
28444
  }
28178
28445
  }
28179
- const count8 = await requestDrain(socket, lines);
28446
+ const count8 = await requestDrain(socket, lines2);
28180
28447
  socket.destroy();
28181
28448
  console.log(`Drained ${count8} session(s)`);
28182
28449
  }
@@ -28195,9 +28462,9 @@ async function confirmDrain() {
28195
28462
  console.log("Drain cancelled");
28196
28463
  return false;
28197
28464
  }
28198
- function liveSessions(lines) {
28465
+ function liveSessions(lines2) {
28199
28466
  return readDaemonMessage(
28200
- lines,
28467
+ lines2,
28201
28468
  LIST_TIMEOUT_MS,
28202
28469
  [],
28203
28470
  (data) => data.type === "sessions" ? (data.sessions ?? []).filter(
@@ -28205,11 +28472,11 @@ function liveSessions(lines) {
28205
28472
  ) : void 0
28206
28473
  );
28207
28474
  }
28208
- function requestDrain(socket, lines) {
28475
+ function requestDrain(socket, lines2) {
28209
28476
  socket.write(`${JSON.stringify({ type: "drain" })}
28210
28477
  `);
28211
28478
  return readDaemonMessage(
28212
- lines,
28479
+ lines2,
28213
28480
  DRAIN_TIMEOUT_MS,
28214
28481
  0,
28215
28482
  (data) => data.type === "drained" ? data.count ?? 0 : void 0
@@ -28674,12 +28941,12 @@ import { basename as basename18 } from "path";
28674
28941
 
28675
28942
  // src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
28676
28943
  import { existsSync as existsSync63 } from "fs";
28677
- import { join as join74 } from "path";
28944
+ import { join as join75 } from "path";
28678
28945
 
28679
28946
  // src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
28680
28947
  import { statSync as statSync12 } from "fs";
28681
28948
  import { rm as rm2 } from "fs/promises";
28682
- import { join as join73 } from "path";
28949
+ import { join as join74 } from "path";
28683
28950
  async function deleteTreeDirectly(clone, worktreePath, why) {
28684
28951
  if (holdsAGitDirectoryRatherThanALink(worktreePath)) {
28685
28952
  daemonLog(
@@ -28706,7 +28973,7 @@ async function deleteTreeDirectly(clone, worktreePath, why) {
28706
28973
  return true;
28707
28974
  }
28708
28975
  function holdsAGitDirectoryRatherThanALink(worktreePath) {
28709
- return statSync12(join73(worktreePath, ".git"), {
28976
+ return statSync12(join74(worktreePath, ".git"), {
28710
28977
  throwIfNoEntry: false
28711
28978
  })?.isDirectory() === true;
28712
28979
  }
@@ -28742,7 +29009,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
28742
29009
  );
28743
29010
  }
28744
29011
  function strandedReason(worktreePath, cause) {
28745
- if (!existsSync63(join74(worktreePath, ".git")))
29012
+ if (!existsSync63(join75(worktreePath, ".git")))
28746
29013
  return "its .git link is already gone";
28747
29014
  if (/not a working tree|not a git repository/i.test(reason2(cause)))
28748
29015
  return "git no longer recognises it as a working tree";
@@ -29836,6 +30103,40 @@ function registerSpawnedSession(session, sessions, clients, onStatusChange, noti
29836
30103
  return session.id;
29837
30104
  }
29838
30105
 
30106
+ // src/commands/sessions/daemon/sessionLimits.ts
30107
+ var MAX_LIVE = 12;
30108
+ var sessionLimits = {
30109
+ maxRestore: MAX_LIVE,
30110
+ /** Allocate the next session id from counter, refusing past the absolute
30111
+ * ceiling regardless of caller (restore, web create, retry) so no trigger
30112
+ * can fan out without bound. The counter advances only when allowed. */
30113
+ nextId(liveCount, counter) {
30114
+ if (liveCount >= MAX_LIVE) {
30115
+ daemonLog(`refusing to spawn: at ceiling of ${MAX_LIVE} live sessions`);
30116
+ throw new Error(`session ceiling of ${MAX_LIVE} reached`);
30117
+ }
30118
+ return String(counter.next++);
30119
+ },
30120
+ recoveryCardId(counter) {
30121
+ return String(counter.next++);
30122
+ }
30123
+ };
30124
+
30125
+ // src/commands/sessions/daemon/makeSessionSpawner.ts
30126
+ function makeSessionSpawner(sessions, clients, counter, onStatusChange, notify2) {
30127
+ const register = (allocateId) => (create) => registerSpawnedSession(
30128
+ create(allocateId()),
30129
+ sessions,
30130
+ clients,
30131
+ onStatusChange(),
30132
+ notify2
30133
+ );
30134
+ return {
30135
+ spawn: register(() => sessionLimits.nextId(sessions.size, counter)),
30136
+ recoveryCard: register(() => sessionLimits.recoveryCardId(counter))
30137
+ };
30138
+ }
30139
+
29839
30140
  // src/commands/sessions/daemon/applySetStatus.ts
29840
30141
  function applySetStatus(sessions, id, status3, source, onStatusChange) {
29841
30142
  const session = sessions.get(id);
@@ -29931,9 +30232,9 @@ async function recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown) {
29931
30232
 
29932
30233
  // src/commands/sessions/shared/transcriptUsage.ts
29933
30234
  import * as fs38 from "fs";
29934
- function transcriptUsage(lines) {
30235
+ function transcriptUsage(lines2) {
29935
30236
  const byId = /* @__PURE__ */ new Map();
29936
- for (const line of lines) {
30237
+ for (const line of lines2) {
29937
30238
  if (!line.trim()) continue;
29938
30239
  let entry;
29939
30240
  try {
@@ -30298,6 +30599,43 @@ function restoreBase(id, persisted) {
30298
30599
  };
30299
30600
  }
30300
30601
 
30602
+ // src/commands/sessions/daemon/deferredSession.ts
30603
+ function deferredSession(id, persisted, cap) {
30604
+ return {
30605
+ ...restoreBase(id, persisted),
30606
+ status: "stopped",
30607
+ startedAt: persisted.startedAt,
30608
+ runningMs: persisted.runningMs ?? 0,
30609
+ runningSince: null,
30610
+ waitingSince: null,
30611
+ pty: null,
30612
+ claudeSessionId: persisted.claudeSessionId,
30613
+ runName: persisted.runName,
30614
+ runArgs: persisted.runArgs,
30615
+ activity: persisted.activity,
30616
+ restored: false,
30617
+ scrollback: deferralNotice(persisted, cap)
30618
+ };
30619
+ }
30620
+ function deferralNotice(persisted, cap) {
30621
+ return [
30622
+ `\r
30623
+ \x1B[33mNot resumed: restore was already at its cap of ${cap} session(s)\x1B[0m\r
30624
+ `,
30625
+ `This card is the handle for it. Its command, working directory (${persisted.cwd})\r
30626
+ `,
30627
+ "and transcript are intact \u2014 restart to put an agent back in it, or dismiss it.\r\n"
30628
+ ].join("");
30629
+ }
30630
+
30631
+ // src/commands/sessions/daemon/logDroppedSession.ts
30632
+ function logDroppedSession(persisted, error) {
30633
+ const reason4 = error instanceof Error ? error.message : String(error);
30634
+ daemonLog(
30635
+ `dropped persisted session ${describePersistedSession(persisted)}: ${reason4}`
30636
+ );
30637
+ }
30638
+
30301
30639
  // src/commands/sessions/daemon/errorSession.ts
30302
30640
  function errorSession(id, persisted, error) {
30303
30641
  return {
@@ -30460,16 +30798,20 @@ function restoreSession(id, persisted) {
30460
30798
  }
30461
30799
 
30462
30800
  // src/commands/sessions/daemon/restoreOne.ts
30463
- function restoreOne(persisted, spawn13, sessions) {
30801
+ function restoreOne(persisted, spawner, sessions) {
30464
30802
  try {
30465
- const id = spawn13((sid) => restoreSession(sid, persisted));
30803
+ const id = spawner.spawn((sid) => restoreSession(sid, persisted));
30466
30804
  logUnresumable(persisted.name, id, sessions.get(id));
30467
30805
  } catch (error) {
30468
- const reason4 = logRestoreError(persisted.name, error);
30469
- try {
30470
- spawn13((id) => errorSession(id, persisted, reason4));
30471
- } catch {
30472
- }
30806
+ const reason4 = logRestoreError(persisted, error);
30807
+ spawnErrorCard(persisted, spawner, reason4);
30808
+ }
30809
+ }
30810
+ function spawnErrorCard(persisted, spawner, reason4) {
30811
+ try {
30812
+ spawner.recoveryCard((id) => errorSession(id, persisted, reason4));
30813
+ } catch (cardError) {
30814
+ logDroppedSession(persisted, cardError);
30473
30815
  }
30474
30816
  }
30475
30817
  function logUnresumable(name, id, session) {
@@ -30478,42 +30820,36 @@ function logUnresumable(name, id, session) {
30478
30820
  `could not resume restored session "${name}" (id ${id}): ${session.error}`
30479
30821
  );
30480
30822
  }
30481
- function logRestoreError(name, error) {
30823
+ function logRestoreError(persisted, error) {
30482
30824
  const reason4 = error instanceof Error ? error.message : String(error);
30483
- daemonLog(`failed to restore session "${name}": ${reason4}`);
30825
+ daemonLog(
30826
+ `failed to restore session ${describePersistedSession(persisted)}: ${reason4}`
30827
+ );
30484
30828
  return reason4;
30485
30829
  }
30486
30830
 
30487
- // src/commands/sessions/daemon/sessionLimits.ts
30488
- var MAX_LIVE = 12;
30489
- var MAX_RESTORE = 10;
30490
- var sessionLimits = {
30491
- maxRestore: MAX_RESTORE,
30492
- /** Allocate the next session id from counter, refusing past the absolute
30493
- * ceiling regardless of caller (restore, web create, retry) so no trigger
30494
- * can fan out without bound. The counter advances only when allowed. */
30495
- nextId(liveCount, counter) {
30496
- if (liveCount >= MAX_LIVE) {
30497
- daemonLog(`refusing to spawn: at ceiling of ${MAX_LIVE} live sessions`);
30498
- throw new Error(`session ceiling of ${MAX_LIVE} reached`);
30499
- }
30500
- return String(counter.next++);
30501
- }
30502
- };
30503
-
30504
30831
  // src/commands/sessions/daemon/restoreAll.ts
30505
- function restoreAll(spawn13, sessions) {
30832
+ function restoreAll(spawner, sessions) {
30506
30833
  const persisted = loadPersistedSessions();
30507
- const toRestore = persisted.slice(0, sessionLimits.maxRestore);
30508
- const skipped = persisted.length - toRestore.length;
30509
- if (skipped > 0)
30834
+ const names = persisted.slice(0, sessionLimits.maxRestore).map((entry) => {
30835
+ restoreOne(entry, spawner, sessions);
30836
+ return entry.name;
30837
+ });
30838
+ for (const over of persisted.slice(sessionLimits.maxRestore))
30839
+ deferOne(over, spawner.recoveryCard);
30840
+ return names;
30841
+ }
30842
+ function deferOne(persisted, spawnRecoveryCard) {
30843
+ try {
30844
+ const id = spawnRecoveryCard(
30845
+ (sid) => deferredSession(sid, persisted, sessionLimits.maxRestore)
30846
+ );
30510
30847
  daemonLog(
30511
- `restore capped at ${sessionLimits.maxRestore}; skipping ${skipped} persisted session(s)`
30848
+ `restore capped at ${sessionLimits.maxRestore}: ${describePersistedSession(persisted)} deferred to stopped card ${id}`
30512
30849
  );
30513
- return toRestore.map((persisted2) => {
30514
- restoreOne(persisted2, spawn13, sessions);
30515
- return persisted2.name;
30516
- });
30850
+ } catch (error) {
30851
+ logDroppedSession(persisted, error);
30852
+ }
30517
30853
  }
30518
30854
 
30519
30855
  // src/commands/sessions/daemon/worktree/rearmStoppedSessions.ts
@@ -30597,29 +30933,29 @@ async function describeHeldWork(path71, reason4) {
30597
30933
  }
30598
30934
  async function changedFiles(path71) {
30599
30935
  const status3 = await gitResult(path71, ["status", "--porcelain"]);
30600
- const lines = status3.ok ? nonEmptyLines(status3.out) : [];
30936
+ const lines2 = status3.ok ? nonEmptyLines(status3.out) : [];
30601
30937
  return {
30602
- summary: `${lines.length} uncommitted ${lines.length === 1 ? "file" : "files"}`,
30603
- items: capped(lines)
30938
+ summary: `${lines2.length} uncommitted ${lines2.length === 1 ? "file" : "files"}`,
30939
+ items: capped(lines2)
30604
30940
  };
30605
30941
  }
30606
30942
  async function unpushedCommits(path71, reason4) {
30607
30943
  const log2 = await gitResult(path71, ["log", "--oneline", "@{upstream}..HEAD"]);
30608
30944
  if (!log2.ok) return { summary: reason4, items: [] };
30609
- const lines = nonEmptyLines(log2.out);
30945
+ const lines2 = nonEmptyLines(log2.out);
30610
30946
  return {
30611
- summary: `${lines.length} unpushed ${lines.length === 1 ? "commit" : "commits"}`,
30612
- items: capped(lines)
30947
+ summary: `${lines2.length} unpushed ${lines2.length === 1 ? "commit" : "commits"}`,
30948
+ items: capped(lines2)
30613
30949
  };
30614
30950
  }
30615
30951
  function nonEmptyLines(out) {
30616
30952
  return out.split("\n").map((line) => line.trim()).filter((line) => line !== "");
30617
30953
  }
30618
- function capped(lines) {
30619
- if (lines.length <= MAX_ITEMS) return lines;
30954
+ function capped(lines2) {
30955
+ if (lines2.length <= MAX_ITEMS) return lines2;
30620
30956
  return [
30621
- ...lines.slice(0, MAX_ITEMS),
30622
- `\u2026 and ${lines.length - MAX_ITEMS} more`
30957
+ ...lines2.slice(0, MAX_ITEMS),
30958
+ `\u2026 and ${lines2.length - MAX_ITEMS} more`
30623
30959
  ];
30624
30960
  }
30625
30961
 
@@ -30764,9 +31100,9 @@ async function recoverOrphan(sessions, spawnWith, orphan, notify2) {
30764
31100
  }
30765
31101
 
30766
31102
  // src/commands/sessions/daemon/restoreAllSessions.ts
30767
- function restoreAllSessions(spawnWith, sessions, notify2) {
30768
- const names = restoreAll(spawnWith, sessions);
30769
- reconcileWorktreesOnRestore(sessions, spawnWith, notify2);
31103
+ function restoreAllSessions(spawner, sessions, notify2) {
31104
+ const names = restoreAll(spawner, sessions);
31105
+ reconcileWorktreesOnRestore(sessions, spawner.recoveryCard, notify2);
30770
31106
  rearmStoppedSessions(sessions, notify2);
30771
31107
  return names;
30772
31108
  }
@@ -31100,7 +31436,7 @@ function windowsDaemonHost() {
31100
31436
  var CONNECT_TIMEOUT_MS = 2e3;
31101
31437
  var KEEPALIVE_PROBE_MS = 1e4;
31102
31438
  function connectToWindowsDaemon() {
31103
- return new Promise((resolve21, reject) => {
31439
+ return new Promise((resolve22, reject) => {
31104
31440
  const socket = net2.connect(windowsDaemonPort(), windowsDaemonHost());
31105
31441
  socket.setTimeout(CONNECT_TIMEOUT_MS);
31106
31442
  socket.once("timeout", () => {
@@ -31110,7 +31446,7 @@ function connectToWindowsDaemon() {
31110
31446
  socket.once("connect", () => {
31111
31447
  socket.setTimeout(0);
31112
31448
  socket.setKeepAlive(true, KEEPALIVE_PROBE_MS);
31113
- resolve21(socket);
31449
+ resolve22(socket);
31114
31450
  });
31115
31451
  socket.once("error", reject);
31116
31452
  });
@@ -31131,9 +31467,9 @@ import { spawn as spawn11 } from "child_process";
31131
31467
  import { createInterface as createInterface7 } from "readline";
31132
31468
  function logChildStream(stream, label2) {
31133
31469
  if (!stream) return;
31134
- const lines = createInterface7({ input: stream });
31135
- lines.on("line", (line) => daemonLog(`[${label2}] ${line}`));
31136
- lines.on("error", () => {
31470
+ const lines2 = createInterface7({ input: stream });
31471
+ lines2.on("line", (line) => daemonLog(`[${label2}] ${line}`));
31472
+ lines2.on("error", () => {
31137
31473
  });
31138
31474
  }
31139
31475
 
@@ -31188,7 +31524,7 @@ async function waitForWindowsDaemon() {
31188
31524
  );
31189
31525
  }
31190
31526
  function delay2(ms) {
31191
- return new Promise((resolve21) => setTimeout(resolve21, ms));
31527
+ return new Promise((resolve22) => setTimeout(resolve22, ms));
31192
31528
  }
31193
31529
 
31194
31530
  // src/commands/sessions/daemon/defaultConnect.ts
@@ -31472,7 +31808,7 @@ async function healWindowsDaemon() {
31472
31808
  daemonLog("windows daemon: auto-heal: stale daemon stopped");
31473
31809
  }
31474
31810
  function runOnWindowsHost(command, timeoutMs) {
31475
- return new Promise((resolve21, reject) => {
31811
+ return new Promise((resolve22, reject) => {
31476
31812
  const child = spawn12("pwsh.exe", ["-Command", command], {
31477
31813
  stdio: ["ignore", "pipe", "pipe"]
31478
31814
  });
@@ -31492,7 +31828,7 @@ function runOnWindowsHost(command, timeoutMs) {
31492
31828
  });
31493
31829
  child.on("exit", (code) => {
31494
31830
  clearTimeout(timer);
31495
- if (code === 0) resolve21();
31831
+ if (code === 0) resolve22();
31496
31832
  else
31497
31833
  reject(
31498
31834
  new Error(
@@ -31603,10 +31939,10 @@ var WindowsConnection = class {
31603
31939
  return socket;
31604
31940
  }
31605
31941
  wire(socket) {
31606
- const lines = createInterface8({ input: socket });
31607
- lines.on("error", () => {
31942
+ const lines2 = createInterface8({ input: socket });
31943
+ lines2.on("error", () => {
31608
31944
  });
31609
- lines.on("line", (line) => this.deps.onLine(line));
31945
+ lines2.on("line", (line) => this.deps.onLine(line));
31610
31946
  socket.on("error", () => {
31611
31947
  });
31612
31948
  socket.on("close", () => {
@@ -31961,16 +32297,17 @@ var SessionManager = class {
31961
32297
  );
31962
32298
  }
31963
32299
  restore() {
31964
- return restoreAllSessions(this.spawnWith, this.sessions, this.notify);
32300
+ return restoreAllSessions(this.spawner, this.sessions, this.notify);
31965
32301
  }
31966
32302
  drain = () => drainSessions(this.sessions, this.notify);
31967
- spawnWith = (create) => registerSpawnedSession(
31968
- create(sessionLimits.nextId(this.sessions.size, this.idCounter)),
32303
+ spawner = makeSessionSpawner(
31969
32304
  this.sessions,
31970
32305
  this.clients,
31971
- this.onStatusChange,
31972
- this.notify
32306
+ this.idCounter,
32307
+ () => this.onStatusChange,
32308
+ () => this.notify()
31973
32309
  );
32310
+ spawnWith = this.spawner.spawn;
31974
32311
  treeCtx() {
31975
32312
  return treeSpawnContext(
31976
32313
  this.sessions,
@@ -32204,9 +32541,9 @@ async function parseTranscript(sessionId) {
32204
32541
  return [];
32205
32542
  }
32206
32543
  }
32207
- function parseTranscriptLines(lines) {
32544
+ function parseTranscriptLines(lines2) {
32208
32545
  const messages = [];
32209
- for (const line of lines) {
32546
+ for (const line of lines2) {
32210
32547
  const entry = line.trim() ? safeParse2(line) : null;
32211
32548
  if (!entry || entry.isSidechain || entry.isMeta) continue;
32212
32549
  messages.push(...entryMessages(entry));
@@ -32388,10 +32725,10 @@ function handleConnection(socket, manager) {
32388
32725
  };
32389
32726
  manager.addClient(client);
32390
32727
  manager.clients.greet(client);
32391
- const lines = createInterface9({ input: socket });
32392
- lines.on("error", () => {
32728
+ const lines2 = createInterface9({ input: socket });
32729
+ lines2.on("error", () => {
32393
32730
  });
32394
- lines.on("line", (line) => {
32731
+ lines2.on("line", (line) => {
32395
32732
  let data;
32396
32733
  try {
32397
32734
  data = JSON.parse(line);
@@ -32852,9 +33189,9 @@ function buildLimitsSegment(rateLimits) {
32852
33189
 
32853
33190
  // src/commands/readGitBranch.ts
32854
33191
  import { readFileSync as readFileSync54, statSync as statSync14 } from "fs";
32855
- import { isAbsolute as isAbsolute4, join as join76, resolve as resolve19 } from "path";
33192
+ import { isAbsolute as isAbsolute4, join as join77, resolve as resolve20 } from "path";
32856
33193
  function resolveGitDir(cwd) {
32857
- const dotGit = join76(cwd, ".git");
33194
+ const dotGit = join77(cwd, ".git");
32858
33195
  let stat3;
32859
33196
  try {
32860
33197
  stat3 = statSync14(dotGit);
@@ -32875,7 +33212,7 @@ function resolveGitDir(cwd) {
32875
33212
  return null;
32876
33213
  }
32877
33214
  const gitDir = match[1].trim();
32878
- return isAbsolute4(gitDir) ? gitDir : resolve19(cwd, gitDir);
33215
+ return isAbsolute4(gitDir) ? gitDir : resolve20(cwd, gitDir);
32879
33216
  }
32880
33217
  function readGitBranch(cwd) {
32881
33218
  const gitDir = resolveGitDir(cwd);
@@ -32884,7 +33221,7 @@ function readGitBranch(cwd) {
32884
33221
  }
32885
33222
  let head;
32886
33223
  try {
32887
- head = readFileSync54(join76(gitDir, "HEAD"), "utf8");
33224
+ head = readFileSync54(join77(gitDir, "HEAD"), "utf8");
32888
33225
  } catch {
32889
33226
  return null;
32890
33227
  }