@staff0rd/assist 0.220.1 → 0.221.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.220.1",
9
+ version: "0.221.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -3704,19 +3704,19 @@ function moveCaseInsensitive(absSource, absDest) {
3704
3704
  fs8.renameSync(absSource, tmp);
3705
3705
  fs8.renameSync(tmp, absDest);
3706
3706
  }
3707
- function applyMoves(project, moves, cwd, emit) {
3707
+ function applyMoves(project, moves, cwd, emit2) {
3708
3708
  for (const { sourcePath, destPath } of moves) {
3709
3709
  const start3 = performance.now();
3710
3710
  const absSource = path16.resolve(sourcePath);
3711
3711
  const absDest = path16.resolve(destPath);
3712
3712
  for (const r of renameExports(project, absSource, absDest)) {
3713
- emit(` Renamed export ${r} in ${sourcePath}`);
3713
+ emit2(` Renamed export ${r} in ${sourcePath}`);
3714
3714
  }
3715
3715
  const sourceFile = project.getSourceFile(absSource);
3716
3716
  if (sourceFile) sourceFile.move(absDest);
3717
3717
  const ms = (performance.now() - start3).toFixed(0);
3718
3718
  const rel = `${path16.relative(cwd, absSource)} \u2192 ${path16.relative(cwd, absDest)}`;
3719
- emit(` Renamed ${rel} (${ms}ms)`);
3719
+ emit2(` Renamed ${rel} (${ms}ms)`);
3720
3720
  }
3721
3721
  project.saveSync();
3722
3722
  for (const { sourcePath, destPath } of moves) {
@@ -4070,10 +4070,10 @@ function printTaskStatuses(tasks) {
4070
4070
  }
4071
4071
 
4072
4072
  // src/commands/verify/run/createTimerCallback/index.ts
4073
- function logFailedScripts(failed) {
4073
+ function logFailedScripts(failed2) {
4074
4074
  console.error(`
4075
- ${failed.length} script(s) failed:`);
4076
- for (const f of failed) {
4075
+ ${failed2.length} script(s) failed:`);
4076
+ for (const f of failed2) {
4077
4077
  console.error(` - ${f.script} (exit code ${f.code})`);
4078
4078
  }
4079
4079
  }
@@ -4150,9 +4150,9 @@ function runEntry(entry, onComplete) {
4150
4150
  });
4151
4151
  });
4152
4152
  }
4153
- function exitIfFailed(failed) {
4154
- if (failed.length === 0) return;
4155
- logFailedScripts(failed);
4153
+ function exitIfFailed(failed2) {
4154
+ if (failed2.length === 0) return;
4155
+ logFailedScripts(failed2);
4156
4156
  process.exit(1);
4157
4157
  }
4158
4158
  function runAllEntries(entries, timer) {
@@ -8392,6 +8392,303 @@ function registerDotnet(program2) {
8392
8392
  cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
8393
8393
  }
8394
8394
 
8395
+ // src/commands/handover/archive.ts
8396
+ import { existsSync as existsSync30, mkdirSync as mkdirSync7, renameSync as renameSync2 } from "fs";
8397
+ import { join as join31 } from "path";
8398
+
8399
+ // src/commands/handover/formatArchiveTimestamp.ts
8400
+ function formatArchiveTimestamp(date = /* @__PURE__ */ new Date()) {
8401
+ const pad = (n) => n.toString().padStart(2, "0");
8402
+ const yyyy = date.getUTCFullYear().toString();
8403
+ const mm = pad(date.getUTCMonth() + 1);
8404
+ const dd = pad(date.getUTCDate());
8405
+ const hh = pad(date.getUTCHours());
8406
+ const mi = pad(date.getUTCMinutes());
8407
+ const ss = pad(date.getUTCSeconds());
8408
+ return `${yyyy}-${mm}-${dd}T${hh}${mi}${ss}Z`;
8409
+ }
8410
+
8411
+ // src/commands/handover/getHandoverArchiveDir.ts
8412
+ import { join as join29 } from "path";
8413
+ function getHandoverArchiveDir(cwd = process.cwd()) {
8414
+ return join29(cwd, ".assist", "handovers", "archive");
8415
+ }
8416
+
8417
+ // src/commands/handover/getHandoverPath.ts
8418
+ import { join as join30 } from "path";
8419
+ function getHandoverPath(cwd = process.cwd()) {
8420
+ return join30(cwd, ".assist", "HANDOVER.md");
8421
+ }
8422
+
8423
+ // src/commands/handover/archive.ts
8424
+ var MAX_COLLISION_SUFFIX = 99;
8425
+ function buildArchiveFilename(timestamp, suffix) {
8426
+ const base = suffix ? `${timestamp}-${suffix}` : timestamp;
8427
+ return `${base}.md`;
8428
+ }
8429
+ function resolveCollisionPath(archiveDir, timestamp, suffix) {
8430
+ const initial = join31(archiveDir, buildArchiveFilename(timestamp, suffix));
8431
+ if (!existsSync30(initial)) return initial;
8432
+ for (let i = 1; i <= MAX_COLLISION_SUFFIX; i++) {
8433
+ const collisionSuffix = suffix ? `${suffix}-${i}` : `${i}`;
8434
+ const candidate = join31(
8435
+ archiveDir,
8436
+ buildArchiveFilename(timestamp, collisionSuffix)
8437
+ );
8438
+ if (!existsSync30(candidate)) return candidate;
8439
+ }
8440
+ throw new Error(
8441
+ `Exhausted collision suffixes (1-${MAX_COLLISION_SUFFIX}) for ${timestamp}`
8442
+ );
8443
+ }
8444
+ function archive(options2 = {}) {
8445
+ const cwd = options2.cwd ?? process.cwd();
8446
+ const handoverPath = getHandoverPath(cwd);
8447
+ if (!existsSync30(handoverPath)) return void 0;
8448
+ const archiveDir = getHandoverArchiveDir(cwd);
8449
+ mkdirSync7(archiveDir, { recursive: true });
8450
+ const timestamp = formatArchiveTimestamp(options2.now);
8451
+ const destination = resolveCollisionPath(
8452
+ archiveDir,
8453
+ timestamp,
8454
+ options2.suffix
8455
+ );
8456
+ renameSync2(handoverPath, destination);
8457
+ return destination;
8458
+ }
8459
+
8460
+ // src/commands/handover/load.ts
8461
+ import { existsSync as existsSync31, readFileSync as readFileSync27 } from "fs";
8462
+
8463
+ // src/commands/handover/parseLoadInput.ts
8464
+ async function parseLoadInput(stdin) {
8465
+ try {
8466
+ const raw = await stdin();
8467
+ if (!raw.trim()) return {};
8468
+ return JSON.parse(raw);
8469
+ } catch {
8470
+ return {};
8471
+ }
8472
+ }
8473
+
8474
+ // src/commands/handover/findRecentSessionJsonl.ts
8475
+ import { readdirSync as readdirSync5, statSync as statSync4 } from "fs";
8476
+ import { homedir as homedir8 } from "os";
8477
+ import { join as join32 } from "path";
8478
+
8479
+ // src/commands/handover/encodeCwdForProjects.ts
8480
+ function encodeCwdForProjects(cwd) {
8481
+ return cwd.replace(/[\\/.]/g, "-");
8482
+ }
8483
+
8484
+ // src/commands/sessions/summarise/iterateUserEntries.ts
8485
+ import * as fs17 from "fs";
8486
+
8487
+ // src/commands/sessions/summarise/parseUserLine.ts
8488
+ function parseUserLine(line) {
8489
+ let entry;
8490
+ try {
8491
+ entry = JSON.parse(line);
8492
+ } catch {
8493
+ return void 0;
8494
+ }
8495
+ if (entry.type !== "user") return void 0;
8496
+ const msg = entry.message;
8497
+ const c = msg?.content;
8498
+ let text;
8499
+ if (typeof c === "string") {
8500
+ text = c;
8501
+ } else if (Array.isArray(c)) {
8502
+ const collected = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
8503
+ text = collected || void 0;
8504
+ }
8505
+ if (!text) return void 0;
8506
+ return {
8507
+ text,
8508
+ entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
8509
+ };
8510
+ }
8511
+
8512
+ // src/commands/sessions/summarise/iterateUserEntries.ts
8513
+ function* iterateUserEntries(filePath) {
8514
+ let content;
8515
+ try {
8516
+ content = fs17.readFileSync(filePath, "utf8");
8517
+ } catch {
8518
+ return;
8519
+ }
8520
+ for (const line of content.split("\n")) {
8521
+ if (!line) continue;
8522
+ const entry = parseUserLine(line);
8523
+ if (entry) yield entry;
8524
+ }
8525
+ }
8526
+
8527
+ // src/commands/handover/isSdkCliOnly.ts
8528
+ function isSdkCliOnly(jsonlPath) {
8529
+ for (const entry of iterateUserEntries(jsonlPath)) {
8530
+ if (entry.entrypoint !== "sdk-cli") return false;
8531
+ }
8532
+ return true;
8533
+ }
8534
+
8535
+ // src/commands/handover/findRecentSessionJsonl.ts
8536
+ function getProjectDir(cwd) {
8537
+ return join32(homedir8(), ".claude", "projects", encodeCwdForProjects(cwd));
8538
+ }
8539
+ function findRecentSessionJsonl(cwd, options2 = {}) {
8540
+ const projectDir = options2.projectDir ?? getProjectDir(cwd);
8541
+ let entries;
8542
+ try {
8543
+ entries = readdirSync5(projectDir);
8544
+ } catch {
8545
+ return void 0;
8546
+ }
8547
+ const jsonls = entries.filter((f) => f.endsWith(".jsonl")).map((name) => {
8548
+ const path52 = join32(projectDir, name);
8549
+ let mtime = 0;
8550
+ try {
8551
+ mtime = statSync4(path52).mtimeMs;
8552
+ } catch {
8553
+ return void 0;
8554
+ }
8555
+ return { path: path52, name, mtime };
8556
+ }).filter((x) => !!x).sort((a, b) => b.mtime - a.mtime);
8557
+ for (const { path: path52, name } of jsonls) {
8558
+ const sessionId = name.replace(/\.jsonl$/, "");
8559
+ if (options2.excludeSessionId && sessionId === options2.excludeSessionId)
8560
+ continue;
8561
+ if (isSdkCliOnly(path52)) continue;
8562
+ return path52;
8563
+ }
8564
+ return void 0;
8565
+ }
8566
+
8567
+ // src/commands/handover/summarise.ts
8568
+ import { execFileSync as execFileSync4 } from "child_process";
8569
+ var SUMMARISE_RECURSION_GUARD = "_CLAUDE_HOOK_SUMMARISE_RUNNING";
8570
+ var MAX_TURNS = 15;
8571
+ var MAX_PAYLOAD_BYTES = 8 * 1024;
8572
+ var PROMPT_TEMPLATE = [
8573
+ "Summarise what the user has been working on in this Claude Code session in ONE short sentence (under 100 chars).",
8574
+ "Return ONLY the summary, no quotes, prefix, or explanation.",
8575
+ "",
8576
+ "Last user turns (most recent last):"
8577
+ ].join("\n");
8578
+ function summarise(jsonlPath) {
8579
+ const entries = [...iterateUserEntries(jsonlPath)];
8580
+ const humanEntries = entries.filter((e) => e.entrypoint !== "sdk-cli");
8581
+ if (humanEntries.length === 0) return "";
8582
+ const turns = humanEntries.map((e) => stripPreludes(e.text)).filter((t) => t.length > 0).slice(-MAX_TURNS);
8583
+ if (turns.length === 0) return "";
8584
+ const payload = capPayload(turns.join("\n---\n"), MAX_PAYLOAD_BYTES);
8585
+ const prompt = `${PROMPT_TEMPLATE}
8586
+ ${payload}`;
8587
+ try {
8588
+ const output = execFileSync4("claude", ["-p", "--model", "haiku", prompt], {
8589
+ encoding: "utf8",
8590
+ timeout: 3e4,
8591
+ stdio: ["ignore", "pipe", "ignore"],
8592
+ env: { ...process.env, [SUMMARISE_RECURSION_GUARD]: "1" }
8593
+ });
8594
+ return normaliseOutput(output);
8595
+ } catch {
8596
+ return "";
8597
+ }
8598
+ }
8599
+ function stripPreludes(text) {
8600
+ return text.replace(/<command-name>[\s\S]*?<\/command-name>/g, "").replace(/<command-message>[\s\S]*?<\/command-message>/g, "").replace(/<command-args>[\s\S]*?<\/command-args>/g, "").replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
8601
+ }
8602
+ function capPayload(text, maxBytes) {
8603
+ const buf = Buffer.from(text, "utf8");
8604
+ if (buf.length <= maxBytes) return text;
8605
+ return buf.subarray(buf.length - maxBytes).toString("utf8");
8606
+ }
8607
+ function normaliseOutput(raw) {
8608
+ const firstLine = raw.trim().split("\n")[0] ?? "";
8609
+ return firstLine.replace(/^["']|["']$/g, "").trim();
8610
+ }
8611
+
8612
+ // src/commands/handover/resolveLoadOptions.ts
8613
+ function resolveLoadOptions(options2) {
8614
+ return {
8615
+ stdin: options2.stdin ?? readStdin,
8616
+ env: options2.env ?? process.env,
8617
+ cwdFallback: options2.cwdFallback ?? process.cwd(),
8618
+ summariseFn: options2.summariseFn ?? summarise,
8619
+ findRecentFn: options2.findRecentFn ?? ((cwd, sid) => findRecentSessionJsonl(cwd, { excludeSessionId: sid }))
8620
+ };
8621
+ }
8622
+
8623
+ // src/commands/handover/load.ts
8624
+ function loadFromHandover(cwd) {
8625
+ const handoverPath = getHandoverPath(cwd);
8626
+ if (!existsSync31(handoverPath)) return void 0;
8627
+ const content = readFileSync27(handoverPath, "utf-8");
8628
+ archive({ cwd });
8629
+ return {
8630
+ additionalContext: content,
8631
+ systemMessage: "Loaded handover from previous session"
8632
+ };
8633
+ }
8634
+ function loadFromPriorTranscript(cwd, sessionId, findRecent, summariseJsonl) {
8635
+ const jsonlPath = findRecent(cwd, sessionId);
8636
+ if (!jsonlPath) return void 0;
8637
+ const summary = summariseJsonl(jsonlPath);
8638
+ if (!summary) return void 0;
8639
+ const message = `Previous session: ${summary}`;
8640
+ return { additionalContext: message, systemMessage: message };
8641
+ }
8642
+ function emit(context) {
8643
+ const json = JSON.stringify({
8644
+ hookSpecificOutput: {
8645
+ hookEventName: "SessionStart",
8646
+ additionalContext: context.additionalContext
8647
+ },
8648
+ systemMessage: context.systemMessage
8649
+ });
8650
+ console.log(json);
8651
+ return json;
8652
+ }
8653
+ async function load(options2 = {}) {
8654
+ const opts = resolveLoadOptions(options2);
8655
+ if (opts.env[SUMMARISE_RECURSION_GUARD]) return null;
8656
+ const input = await parseLoadInput(opts.stdin);
8657
+ const cwd = input.cwd ?? opts.cwdFallback;
8658
+ const context = loadFromHandover(cwd) ?? loadFromPriorTranscript(
8659
+ cwd,
8660
+ input.session_id,
8661
+ opts.findRecentFn,
8662
+ opts.summariseFn
8663
+ );
8664
+ return context ? emit(context) : null;
8665
+ }
8666
+
8667
+ // src/commands/registerHandover.ts
8668
+ function registerHandover(program2) {
8669
+ const cmd = program2.command("handover").description("Session handover utilities");
8670
+ cmd.command("archive").description(
8671
+ "Archive the current .assist/HANDOVER.md to .assist/handovers/archive/"
8672
+ ).option(
8673
+ "--suffix <suffix>",
8674
+ "Optional suffix appended to the archive filename"
8675
+ ).action((options2) => {
8676
+ const dest = archive({ suffix: options2.suffix });
8677
+ if (dest) console.log(dest);
8678
+ });
8679
+ cmd.command("summarise").description(
8680
+ "Print a one-line summary of a session JSONL via claude -p --model haiku"
8681
+ ).argument("<jsonl>", "Path to a session JSONL file").action((jsonl) => {
8682
+ const line = summarise(jsonl);
8683
+ if (line) console.log(line);
8684
+ });
8685
+ cmd.command("load").description(
8686
+ "SessionStart hook: archive prior handover and emit additionalContext (or fall back to prior-session summary)"
8687
+ ).action(async () => {
8688
+ await load();
8689
+ });
8690
+ }
8691
+
8395
8692
  // src/commands/jira/acceptanceCriteria.ts
8396
8693
  import chalk97 from "chalk";
8397
8694
 
@@ -8401,52 +8698,52 @@ function renderInline(node) {
8401
8698
  if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
8402
8699
  return text;
8403
8700
  }
8404
- function renderChildren(node, indent) {
8405
- return renderNodes(node.content ?? [], indent);
8701
+ function renderChildren(node, indent2) {
8702
+ return renderNodes(node.content ?? [], indent2);
8406
8703
  }
8407
- function renderOrderedList(node, indent) {
8704
+ function renderOrderedList(node, indent2) {
8408
8705
  let counter = 0;
8409
8706
  return (node.content ?? []).map((item) => {
8410
8707
  counter++;
8411
- return renderListItem(item, indent, `${counter}.`);
8708
+ return renderListItem(item, indent2, `${counter}.`);
8412
8709
  }).join("\n");
8413
8710
  }
8414
- function renderBulletList(node, indent) {
8415
- return (node.content ?? []).map((item) => renderListItem(item, indent, "-")).join("\n");
8711
+ function renderBulletList(node, indent2) {
8712
+ return (node.content ?? []).map((item) => renderListItem(item, indent2, "-")).join("\n");
8416
8713
  }
8417
- function renderHeading(node, indent) {
8714
+ function renderHeading(node, indent2) {
8418
8715
  const level = node.attrs?.level ?? 1;
8419
- return `${"#".repeat(level)} ${renderChildren(node, indent)}`;
8716
+ return `${"#".repeat(level)} ${renderChildren(node, indent2)}`;
8420
8717
  }
8421
8718
  var renderers = {
8422
8719
  text: (node) => renderInline(node),
8423
8720
  paragraph: renderChildren,
8424
8721
  orderedList: renderOrderedList,
8425
8722
  bulletList: renderBulletList,
8426
- listItem: (node, indent) => renderListItem(node, indent, "-"),
8723
+ listItem: (node, indent2) => renderListItem(node, indent2, "-"),
8427
8724
  heading: renderHeading,
8428
8725
  doc: renderChildren
8429
8726
  };
8430
- function renderNode(node, indent) {
8727
+ function renderNode(node, indent2) {
8431
8728
  const renderer = renderers[node.type];
8432
- if (renderer) return renderer(node, indent);
8433
- return node.content ? renderChildren(node, indent) : "";
8729
+ if (renderer) return renderer(node, indent2);
8730
+ return node.content ? renderChildren(node, indent2) : "";
8434
8731
  }
8435
- function renderNodes(nodes, indent) {
8436
- return nodes.map((node) => renderNode(node, indent)).join("");
8732
+ function renderNodes(nodes, indent2) {
8733
+ return nodes.map((node) => renderNode(node, indent2)).join("");
8437
8734
  }
8438
8735
  function isListNode(node) {
8439
8736
  return node.type === "orderedList" || node.type === "bulletList";
8440
8737
  }
8441
- function renderListChild(child, indent, pad, marker, isFirst) {
8442
- if (isListNode(child)) return renderNodes([child], indent + 1);
8443
- if (child.type !== "paragraph") return renderNode(child, indent);
8444
- const text = renderChildren(child, indent);
8738
+ function renderListChild(child, indent2, pad, marker, isFirst) {
8739
+ if (isListNode(child)) return renderNodes([child], indent2 + 1);
8740
+ if (child.type !== "paragraph") return renderNode(child, indent2);
8741
+ const text = renderChildren(child, indent2);
8445
8742
  return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
8446
8743
  }
8447
- function renderListItem(node, indent, marker) {
8448
- const pad = " ".repeat(indent);
8449
- return (node.content ?? []).map((child, i) => renderListChild(child, indent, pad, marker, i === 0)).join("\n");
8744
+ function renderListItem(node, indent2, marker) {
8745
+ const pad = " ".repeat(indent2);
8746
+ return (node.content ?? []).map((child, i) => renderListChild(child, indent2, pad, marker, i === 0)).join("\n");
8450
8747
  }
8451
8748
  function adfToText(doc) {
8452
8749
  return renderNodes([doc], 0);
@@ -8507,20 +8804,20 @@ function acceptanceCriteria(issueKey) {
8507
8804
  import { execSync as execSync25 } from "child_process";
8508
8805
 
8509
8806
  // src/shared/loadJson.ts
8510
- import { existsSync as existsSync30, mkdirSync as mkdirSync7, readFileSync as readFileSync26, writeFileSync as writeFileSync20 } from "fs";
8511
- import { homedir as homedir8 } from "os";
8512
- import { join as join29 } from "path";
8807
+ import { existsSync as existsSync32, mkdirSync as mkdirSync8, readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "fs";
8808
+ import { homedir as homedir9 } from "os";
8809
+ import { join as join33 } from "path";
8513
8810
  function getStoreDir() {
8514
- return join29(homedir8(), ".assist");
8811
+ return join33(homedir9(), ".assist");
8515
8812
  }
8516
8813
  function getStorePath(filename) {
8517
- return join29(getStoreDir(), filename);
8814
+ return join33(getStoreDir(), filename);
8518
8815
  }
8519
8816
  function loadJson(filename) {
8520
8817
  const path52 = getStorePath(filename);
8521
- if (existsSync30(path52)) {
8818
+ if (existsSync32(path52)) {
8522
8819
  try {
8523
- return JSON.parse(readFileSync26(path52, "utf-8"));
8820
+ return JSON.parse(readFileSync28(path52, "utf-8"));
8524
8821
  } catch {
8525
8822
  return {};
8526
8823
  }
@@ -8529,8 +8826,8 @@ function loadJson(filename) {
8529
8826
  }
8530
8827
  function saveJson(filename, data) {
8531
8828
  const dir = getStoreDir();
8532
- if (!existsSync30(dir)) {
8533
- mkdirSync7(dir, { recursive: true });
8829
+ if (!existsSync32(dir)) {
8830
+ mkdirSync8(dir, { recursive: true });
8534
8831
  }
8535
8832
  writeFileSync20(getStorePath(filename), JSON.stringify(data, null, 2));
8536
8833
  }
@@ -8619,12 +8916,12 @@ function registerJira(program2) {
8619
8916
  }
8620
8917
 
8621
8918
  // src/commands/mermaid/index.ts
8622
- import { mkdirSync as mkdirSync8, readdirSync as readdirSync5 } from "fs";
8919
+ import { mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "fs";
8623
8920
  import { resolve as resolve10 } from "path";
8624
8921
  import chalk101 from "chalk";
8625
8922
 
8626
8923
  // src/commands/mermaid/exportFile.ts
8627
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync21 } from "fs";
8924
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "fs";
8628
8925
  import { basename as basename7, extname, resolve as resolve9 } from "path";
8629
8926
  import chalk100 from "chalk";
8630
8927
 
@@ -8650,7 +8947,7 @@ async function renderBlock(krokiUrl, source) {
8650
8947
 
8651
8948
  // src/commands/mermaid/exportFile.ts
8652
8949
  async function exportFile(file, outDir, krokiUrl, onlyIndex) {
8653
- const content = readFileSync27(file, "utf8");
8950
+ const content = readFileSync29(file, "utf8");
8654
8951
  const blocks = extractMermaidBlocks(content);
8655
8952
  const stem = basename7(file, extname(file));
8656
8953
  if (onlyIndex !== void 0) {
@@ -8688,7 +8985,7 @@ function extractMermaidBlocks(markdown) {
8688
8985
  async function mermaidExport(file, options2 = {}) {
8689
8986
  const { mermaid } = loadConfig();
8690
8987
  const outDir = resolve10(process.cwd(), options2.out ?? ".");
8691
- mkdirSync8(outDir, { recursive: true });
8988
+ mkdirSync9(outDir, { recursive: true });
8692
8989
  if (options2.index !== void 0) {
8693
8990
  if (!Number.isInteger(options2.index) || options2.index < 1) {
8694
8991
  console.error(
@@ -8701,7 +8998,7 @@ async function mermaidExport(file, options2 = {}) {
8701
8998
  process.exit(1);
8702
8999
  }
8703
9000
  }
8704
- const files = file ? [file] : readdirSync5(process.cwd()).filter((name) => name.toLowerCase().endsWith(".md")).sort();
9001
+ const files = file ? [file] : readdirSync6(process.cwd()).filter((name) => name.toLowerCase().endsWith(".md")).sort();
8705
9002
  if (files.length === 0) {
8706
9003
  console.log(chalk101.gray("No markdown files found in current directory."));
8707
9004
  return;
@@ -9007,7 +9304,7 @@ function registerPrompts(program2) {
9007
9304
  import { spawnSync as spawnSync2 } from "child_process";
9008
9305
  import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync22 } from "fs";
9009
9306
  import { tmpdir as tmpdir4 } from "os";
9010
- import { join as join30 } from "path";
9307
+ import { join as join34 } from "path";
9011
9308
  function buildArgs2(queryFile, vars) {
9012
9309
  const args = ["api", "graphql", "-F", `query=@${queryFile}`];
9013
9310
  for (const [key, value] of Object.entries(vars)) {
@@ -9017,7 +9314,7 @@ function buildArgs2(queryFile, vars) {
9017
9314
  return args;
9018
9315
  }
9019
9316
  function runGhGraphql(mutation, vars) {
9020
- const queryFile = join30(tmpdir4(), `gh-query-${Date.now()}.graphql`);
9317
+ const queryFile = join34(tmpdir4(), `gh-query-${Date.now()}.graphql`);
9021
9318
  writeFileSync22(queryFile, mutation);
9022
9319
  try {
9023
9320
  const result = spawnSync2("gh", buildArgs2(queryFile, vars), {
@@ -9211,26 +9508,26 @@ import { execSync as execSync29 } from "child_process";
9211
9508
  import { execSync as execSync28 } from "child_process";
9212
9509
  import { unlinkSync as unlinkSync8, writeFileSync as writeFileSync23 } from "fs";
9213
9510
  import { tmpdir as tmpdir5 } from "os";
9214
- import { join as join32 } from "path";
9511
+ import { join as join36 } from "path";
9215
9512
 
9216
9513
  // src/commands/prs/loadCommentsCache.ts
9217
- import { existsSync as existsSync31, readFileSync as readFileSync28, unlinkSync as unlinkSync7 } from "fs";
9218
- import { join as join31 } from "path";
9514
+ import { existsSync as existsSync33, readFileSync as readFileSync30, unlinkSync as unlinkSync7 } from "fs";
9515
+ import { join as join35 } from "path";
9219
9516
  import { parse as parse2 } from "yaml";
9220
9517
  function getCachePath(prNumber) {
9221
- return join31(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
9518
+ return join35(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
9222
9519
  }
9223
9520
  function loadCommentsCache(prNumber) {
9224
9521
  const cachePath = getCachePath(prNumber);
9225
- if (!existsSync31(cachePath)) {
9522
+ if (!existsSync33(cachePath)) {
9226
9523
  return null;
9227
9524
  }
9228
- const content = readFileSync28(cachePath, "utf-8");
9525
+ const content = readFileSync30(cachePath, "utf-8");
9229
9526
  return parse2(content);
9230
9527
  }
9231
9528
  function deleteCommentsCache(prNumber) {
9232
9529
  const cachePath = getCachePath(prNumber);
9233
- if (existsSync31(cachePath)) {
9530
+ if (existsSync33(cachePath)) {
9234
9531
  unlinkSync7(cachePath);
9235
9532
  console.log("No more unresolved line comments. Cache dropped.");
9236
9533
  }
@@ -9245,7 +9542,7 @@ function replyToComment(org, repo, prNumber, commentId, message) {
9245
9542
  }
9246
9543
  function resolveThread(threadId) {
9247
9544
  const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
9248
- const queryFile = join32(tmpdir5(), `gh-mutation-${Date.now()}.graphql`);
9545
+ const queryFile = join36(tmpdir5(), `gh-mutation-${Date.now()}.graphql`);
9249
9546
  writeFileSync23(queryFile, mutation);
9250
9547
  try {
9251
9548
  execSync28(
@@ -9327,18 +9624,18 @@ function fixed(commentId, sha) {
9327
9624
  }
9328
9625
 
9329
9626
  // src/commands/prs/listComments/index.ts
9330
- import { existsSync as existsSync32, mkdirSync as mkdirSync9, writeFileSync as writeFileSync25 } from "fs";
9331
- import { join as join34 } from "path";
9627
+ import { existsSync as existsSync34, mkdirSync as mkdirSync10, writeFileSync as writeFileSync25 } from "fs";
9628
+ import { join as join38 } from "path";
9332
9629
  import { stringify } from "yaml";
9333
9630
 
9334
9631
  // src/commands/prs/fetchThreadIds.ts
9335
9632
  import { execSync as execSync30 } from "child_process";
9336
9633
  import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync24 } from "fs";
9337
9634
  import { tmpdir as tmpdir6 } from "os";
9338
- import { join as join33 } from "path";
9635
+ import { join as join37 } from "path";
9339
9636
  var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
9340
9637
  function fetchThreadIds(org, repo, prNumber) {
9341
- const queryFile = join33(tmpdir6(), `gh-query-${Date.now()}.graphql`);
9638
+ const queryFile = join37(tmpdir6(), `gh-query-${Date.now()}.graphql`);
9342
9639
  writeFileSync24(queryFile, THREAD_QUERY);
9343
9640
  try {
9344
9641
  const result = execSync30(
@@ -9425,7 +9722,7 @@ function formatForHuman(comment3) {
9425
9722
  ""
9426
9723
  ].join("\n");
9427
9724
  }
9428
- function summarise(comments2) {
9725
+ function summarise2(comments2) {
9429
9726
  const lineCount = comments2.filter((c) => c.type === "line").length;
9430
9727
  const reviewCount = comments2.filter((c) => c.type === "review").length;
9431
9728
  const parts = [];
@@ -9444,7 +9741,7 @@ function printComments2(result) {
9444
9741
  console.log(formatForHuman(comment3));
9445
9742
  }
9446
9743
  }
9447
- console.log(summarise(comments2));
9744
+ console.log(summarise2(comments2));
9448
9745
  if (cachePath) {
9449
9746
  console.log(`Saved to ${cachePath}`);
9450
9747
  }
@@ -9452,16 +9749,16 @@ function printComments2(result) {
9452
9749
 
9453
9750
  // src/commands/prs/listComments/index.ts
9454
9751
  function writeCommentsCache(prNumber, comments2) {
9455
- const assistDir = join34(process.cwd(), ".assist");
9456
- if (!existsSync32(assistDir)) {
9457
- mkdirSync9(assistDir, { recursive: true });
9752
+ const assistDir = join38(process.cwd(), ".assist");
9753
+ if (!existsSync34(assistDir)) {
9754
+ mkdirSync10(assistDir, { recursive: true });
9458
9755
  }
9459
9756
  const cacheData = {
9460
9757
  prNumber,
9461
9758
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
9462
9759
  comments: comments2
9463
9760
  };
9464
- const cachePath = join34(assistDir, `pr-${prNumber}-comments.yaml`);
9761
+ const cachePath = join38(assistDir, `pr-${prNumber}-comments.yaml`);
9465
9762
  writeFileSync25(cachePath, stringify(cacheData));
9466
9763
  }
9467
9764
  function handleKnownErrors(error) {
@@ -9494,7 +9791,7 @@ async function listComments() {
9494
9791
  ];
9495
9792
  updateCache(prNumber, allComments);
9496
9793
  const hasLineComments = allComments.some((c) => c.type === "line");
9497
- const cachePath = hasLineComments ? join34(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
9794
+ const cachePath = hasLineComments ? join38(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
9498
9795
  return { comments: allComments, cachePath };
9499
9796
  } catch (error) {
9500
9797
  const handled = handleKnownErrors(error);
@@ -10178,17 +10475,17 @@ Refactor check failed:
10178
10475
 
10179
10476
  // src/commands/refactor/check/getViolations/index.ts
10180
10477
  import { execSync as execSync36 } from "child_process";
10181
- import fs18 from "fs";
10478
+ import fs19 from "fs";
10182
10479
  import { minimatch as minimatch4 } from "minimatch";
10183
10480
 
10184
10481
  // src/commands/refactor/check/getViolations/getIgnoredFiles.ts
10185
- import fs17 from "fs";
10482
+ import fs18 from "fs";
10186
10483
  var REFACTOR_YML_PATH = "refactor.yml";
10187
10484
  function parseRefactorYml() {
10188
- if (!fs17.existsSync(REFACTOR_YML_PATH)) {
10485
+ if (!fs18.existsSync(REFACTOR_YML_PATH)) {
10189
10486
  return [];
10190
10487
  }
10191
- const content = fs17.readFileSync(REFACTOR_YML_PATH, "utf-8");
10488
+ const content = fs18.readFileSync(REFACTOR_YML_PATH, "utf-8");
10192
10489
  const entries = [];
10193
10490
  const lines = content.split("\n");
10194
10491
  let currentEntry = {};
@@ -10218,7 +10515,7 @@ function getIgnoredFiles() {
10218
10515
 
10219
10516
  // src/commands/refactor/check/getViolations/index.ts
10220
10517
  function countLines(filePath) {
10221
- const content = fs18.readFileSync(filePath, "utf-8");
10518
+ const content = fs19.readFileSync(filePath, "utf-8");
10222
10519
  return content.split("\n").length;
10223
10520
  }
10224
10521
  function getGitFiles(options2) {
@@ -10283,13 +10580,13 @@ function runScript(script, cwd) {
10283
10580
  });
10284
10581
  });
10285
10582
  }
10286
- function logFailures(failed) {
10287
- for (const f of failed) {
10583
+ function logFailures(failed2) {
10584
+ for (const f of failed2) {
10288
10585
  console.error(f.output);
10289
10586
  }
10290
10587
  console.error(`
10291
- ${failed.length} verify script(s) failed:`);
10292
- for (const f of failed) {
10588
+ ${failed2.length} verify script(s) failed:`);
10589
+ for (const f of failed2) {
10293
10590
  console.error(` - ${f.script} (exit code ${f.code})`);
10294
10591
  }
10295
10592
  }
@@ -10300,9 +10597,9 @@ async function runVerifyQuietly() {
10300
10597
  const results = await Promise.all(
10301
10598
  result.verifyScripts.map((script) => runScript(script, packageDir))
10302
10599
  );
10303
- const failed = results.filter((r) => r.code !== 0);
10304
- if (failed.length > 0) {
10305
- logFailures(failed);
10600
+ const failed2 = results.filter((r) => r.code !== 0);
10601
+ if (failed2.length > 0) {
10602
+ logFailures(failed2);
10306
10603
  return false;
10307
10604
  }
10308
10605
  return true;
@@ -10924,12 +11221,12 @@ import chalk121 from "chalk";
10924
11221
  import { Project as Project3 } from "ts-morph";
10925
11222
 
10926
11223
  // src/commands/refactor/extract/findTsConfig.ts
10927
- import fs19 from "fs";
11224
+ import fs20 from "fs";
10928
11225
  import path34 from "path";
10929
11226
  import { Project as Project2 } from "ts-morph";
10930
11227
  function findTsConfig(sourcePath) {
10931
11228
  const rootConfig = path34.resolve("tsconfig.json");
10932
- if (!fs19.existsSync(rootConfig)) return rootConfig;
11229
+ if (!fs20.existsSync(rootConfig)) return rootConfig;
10933
11230
  const tried = /* @__PURE__ */ new Set();
10934
11231
  const candidates = [rootConfig, ...readReferences(rootConfig)];
10935
11232
  for (const candidate of candidates) {
@@ -10937,7 +11234,7 @@ function findTsConfig(sourcePath) {
10937
11234
  tried.add(candidate);
10938
11235
  if (projectIncludes(candidate, sourcePath)) return candidate;
10939
11236
  }
10940
- const siblings = fs19.readdirSync(path34.dirname(rootConfig)).filter((f) => /^tsconfig.*\.json$/.test(f)).map((f) => path34.resolve(path34.dirname(rootConfig), f));
11237
+ const siblings = fs20.readdirSync(path34.dirname(rootConfig)).filter((f) => /^tsconfig.*\.json$/.test(f)).map((f) => path34.resolve(path34.dirname(rootConfig), f));
10941
11238
  for (const sibling of siblings) {
10942
11239
  if (tried.has(sibling)) continue;
10943
11240
  tried.add(sibling);
@@ -10946,8 +11243,8 @@ function findTsConfig(sourcePath) {
10946
11243
  return rootConfig;
10947
11244
  }
10948
11245
  function readReferences(configPath) {
10949
- if (!fs19.existsSync(configPath)) return [];
10950
- const raw = fs19.readFileSync(configPath, "utf-8");
11246
+ if (!fs20.existsSync(configPath)) return [];
11247
+ const raw = fs20.readFileSync(configPath, "utf-8");
10951
11248
  const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
10952
11249
  let parsed;
10953
11250
  try {
@@ -10959,8 +11256,8 @@ function readReferences(configPath) {
10959
11256
  const cwd = path34.dirname(configPath);
10960
11257
  return parsed.references.map((ref) => {
10961
11258
  const refPath = path34.resolve(cwd, ref.path);
10962
- return fs19.statSync(refPath, { throwIfNoEntry: false })?.isDirectory() ? path34.join(refPath, "tsconfig.json") : refPath;
10963
- }).filter((p) => fs19.existsSync(p));
11259
+ return fs20.statSync(refPath, { throwIfNoEntry: false })?.isDirectory() ? path34.join(refPath, "tsconfig.json") : refPath;
11260
+ }).filter((p) => fs20.existsSync(p));
10964
11261
  }
10965
11262
  function projectIncludes(configPath, sourcePath) {
10966
11263
  try {
@@ -11010,25 +11307,25 @@ async function extract(file, functionName, destination, options2 = {}) {
11010
11307
  }
11011
11308
 
11012
11309
  // src/commands/refactor/ignore.ts
11013
- import fs20 from "fs";
11310
+ import fs21 from "fs";
11014
11311
  import chalk123 from "chalk";
11015
11312
  var REFACTOR_YML_PATH2 = "refactor.yml";
11016
11313
  function ignore(file) {
11017
- if (!fs20.existsSync(file)) {
11314
+ if (!fs21.existsSync(file)) {
11018
11315
  console.error(chalk123.red(`Error: File does not exist: ${file}`));
11019
11316
  process.exit(1);
11020
11317
  }
11021
- const content = fs20.readFileSync(file, "utf-8");
11318
+ const content = fs21.readFileSync(file, "utf-8");
11022
11319
  const lineCount = content.split("\n").length;
11023
11320
  const maxLines = lineCount + 10;
11024
11321
  const entry = `- file: ${file}
11025
11322
  maxLines: ${maxLines}
11026
11323
  `;
11027
- if (fs20.existsSync(REFACTOR_YML_PATH2)) {
11028
- const existing = fs20.readFileSync(REFACTOR_YML_PATH2, "utf-8");
11029
- fs20.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
11324
+ if (fs21.existsSync(REFACTOR_YML_PATH2)) {
11325
+ const existing = fs21.readFileSync(REFACTOR_YML_PATH2, "utf-8");
11326
+ fs21.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
11030
11327
  } else {
11031
- fs20.writeFileSync(REFACTOR_YML_PATH2, entry);
11328
+ fs21.writeFileSync(REFACTOR_YML_PATH2, entry);
11032
11329
  }
11033
11330
  console.log(
11034
11331
  chalk123.green(
@@ -11276,7 +11573,7 @@ function clusterFiles(graph) {
11276
11573
  import path42 from "path";
11277
11574
 
11278
11575
  // src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
11279
- import fs21 from "fs";
11576
+ import fs22 from "fs";
11280
11577
  function getOrCreateList(map, key) {
11281
11578
  const list4 = map.get(key) ?? [];
11282
11579
  if (!map.has(key)) map.set(key, list4);
@@ -11295,7 +11592,7 @@ function rewriteSpecifier(content, oldSpecifier, newSpecifier) {
11295
11592
  return content.replace(pattern2, `$1${newSpecifier}$2`);
11296
11593
  }
11297
11594
  function applyFileRewrites(file, fileRewrites) {
11298
- let content = fs21.readFileSync(file, "utf-8");
11595
+ let content = fs22.readFileSync(file, "utf-8");
11299
11596
  for (const { oldSpecifier, newSpecifier } of fileRewrites) {
11300
11597
  content = rewriteSpecifier(content, oldSpecifier, newSpecifier);
11301
11598
  }
@@ -11423,27 +11720,27 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
11423
11720
  }
11424
11721
 
11425
11722
  // src/commands/refactor/restructure/executePlan.ts
11426
- import fs22 from "fs";
11723
+ import fs23 from "fs";
11427
11724
  import path44 from "path";
11428
11725
  import chalk127 from "chalk";
11429
11726
  function executePlan(plan2) {
11430
11727
  const updatedContents = applyRewrites(plan2.rewrites);
11431
11728
  for (const [file, content] of updatedContents) {
11432
- fs22.writeFileSync(file, content, "utf-8");
11729
+ fs23.writeFileSync(file, content, "utf-8");
11433
11730
  console.log(
11434
11731
  chalk127.cyan(` Rewrote imports in ${path44.relative(process.cwd(), file)}`)
11435
11732
  );
11436
11733
  }
11437
11734
  for (const dir of plan2.newDirectories) {
11438
- fs22.mkdirSync(dir, { recursive: true });
11735
+ fs23.mkdirSync(dir, { recursive: true });
11439
11736
  console.log(chalk127.green(` Created ${path44.relative(process.cwd(), dir)}/`));
11440
11737
  }
11441
11738
  for (const move of plan2.moves) {
11442
11739
  const targetDir = path44.dirname(move.to);
11443
- if (!fs22.existsSync(targetDir)) {
11444
- fs22.mkdirSync(targetDir, { recursive: true });
11740
+ if (!fs23.existsSync(targetDir)) {
11741
+ fs23.mkdirSync(targetDir, { recursive: true });
11445
11742
  }
11446
- fs22.renameSync(move.from, move.to);
11743
+ fs23.renameSync(move.from, move.to);
11447
11744
  console.log(
11448
11745
  chalk127.white(
11449
11746
  ` Moved ${path44.relative(process.cwd(), move.from)} \u2192 ${path44.relative(process.cwd(), move.to)}`
@@ -11455,10 +11752,10 @@ function executePlan(plan2) {
11455
11752
  function removeEmptyDirectories(dirs) {
11456
11753
  const unique = [...new Set(dirs)];
11457
11754
  for (const dir of unique) {
11458
- if (!fs22.existsSync(dir)) continue;
11459
- const entries = fs22.readdirSync(dir);
11755
+ if (!fs23.existsSync(dir)) continue;
11756
+ const entries = fs23.readdirSync(dir);
11460
11757
  if (entries.length === 0) {
11461
- fs22.rmdirSync(dir);
11758
+ fs23.rmdirSync(dir);
11462
11759
  console.log(
11463
11760
  chalk127.dim(
11464
11761
  ` Removed empty directory ${path44.relative(process.cwd(), dir)}`
@@ -11472,18 +11769,18 @@ function removeEmptyDirectories(dirs) {
11472
11769
  import path46 from "path";
11473
11770
 
11474
11771
  // src/commands/refactor/restructure/planFileMoves/shared.ts
11475
- import fs23 from "fs";
11772
+ import fs24 from "fs";
11476
11773
  function emptyResult() {
11477
11774
  return { moves: [], directories: [], warnings: [] };
11478
11775
  }
11479
11776
  function checkDirConflict(result, label2, dir) {
11480
- if (!fs23.existsSync(dir)) return false;
11777
+ if (!fs24.existsSync(dir)) return false;
11481
11778
  result.warnings.push(`Skipping ${label2}: directory ${dir} already exists`);
11482
11779
  return true;
11483
11780
  }
11484
11781
 
11485
11782
  // src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
11486
- import fs24 from "fs";
11783
+ import fs25 from "fs";
11487
11784
  import path45 from "path";
11488
11785
  function collectEntry(results, dir, entry) {
11489
11786
  const full = path45.join(dir, entry.name);
@@ -11491,9 +11788,9 @@ function collectEntry(results, dir, entry) {
11491
11788
  results.push(...items);
11492
11789
  }
11493
11790
  function listFilesRecursive(dir) {
11494
- if (!fs24.existsSync(dir)) return [];
11791
+ if (!fs25.existsSync(dir)) return [];
11495
11792
  const results = [];
11496
- for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
11793
+ for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
11497
11794
  collectEntry(results, dir, entry);
11498
11795
  }
11499
11796
  return results;
@@ -11744,15 +12041,15 @@ ${context.diff.trimEnd()}
11744
12041
  }
11745
12042
 
11746
12043
  // src/commands/review/buildReviewPaths.ts
11747
- import { join as join35 } from "path";
12044
+ import { join as join39 } from "path";
11748
12045
  function buildReviewPaths(repoRoot, key) {
11749
- const reviewDir = join35(repoRoot, ".assist", "reviews", key);
12046
+ const reviewDir = join39(repoRoot, ".assist", "reviews", key);
11750
12047
  return {
11751
12048
  reviewDir,
11752
- requestPath: join35(reviewDir, "request.md"),
11753
- claudePath: join35(reviewDir, "claude.md"),
11754
- codexPath: join35(reviewDir, "codex.md"),
11755
- synthesisPath: join35(reviewDir, "synthesis.md")
12049
+ requestPath: join39(reviewDir, "request.md"),
12050
+ claudePath: join39(reviewDir, "claude.md"),
12051
+ codexPath: join39(reviewDir, "codex.md"),
12052
+ synthesisPath: join39(reviewDir, "synthesis.md")
11756
12053
  };
11757
12054
  }
11758
12055
 
@@ -11870,7 +12167,7 @@ function gatherContext() {
11870
12167
  }
11871
12168
 
11872
12169
  // src/commands/review/postReviewToPr.ts
11873
- import { readFileSync as readFileSync29 } from "fs";
12170
+ import { readFileSync as readFileSync31 } from "fs";
11874
12171
 
11875
12172
  // src/commands/review/parseFindings.ts
11876
12173
  var SEVERITIES = ["blocker", "major", "minor", "nit"];
@@ -12017,21 +12314,21 @@ function buildCommentBody(finding) {
12017
12314
  }
12018
12315
  function postFindings(findings) {
12019
12316
  let posted = 0;
12020
- let failed = 0;
12317
+ let failed2 = 0;
12021
12318
  for (const finding of findings) {
12022
12319
  const body = buildCommentBody(finding);
12023
12320
  try {
12024
12321
  comment2(finding.file, finding.line, body, finding.startLine);
12025
12322
  posted++;
12026
12323
  } catch (error) {
12027
- failed++;
12324
+ failed2++;
12028
12325
  const message = error instanceof Error ? error.message : String(error);
12029
12326
  console.error(
12030
12327
  `Failed to post comment on ${finding.file}:${finding.line}: ${message}`
12031
12328
  );
12032
12329
  }
12033
12330
  }
12034
- return { posted, failed };
12331
+ return { posted, failed: failed2 };
12035
12332
  }
12036
12333
 
12037
12334
  // src/commands/review/submitPendingReview.ts
@@ -12100,7 +12397,7 @@ async function postReviewToPr(synthesisPath, options2) {
12100
12397
  console.log("No PR found for current branch; nothing posted.");
12101
12398
  return;
12102
12399
  }
12103
- const markdown = readFileSync29(synthesisPath, "utf-8");
12400
+ const markdown = readFileSync31(synthesisPath, "utf-8");
12104
12401
  const findings = parseFindings(markdown);
12105
12402
  if (findings.length === 0) {
12106
12403
  console.log("Synthesis contains no findings; nothing to post.");
@@ -12177,14 +12474,14 @@ async function handlePostSynthesis(synthesisPath, options2) {
12177
12474
  }
12178
12475
 
12179
12476
  // src/commands/review/prepareReviewDir.ts
12180
- import { existsSync as existsSync33, mkdirSync as mkdirSync10, unlinkSync as unlinkSync10, writeFileSync as writeFileSync26 } from "fs";
12477
+ import { existsSync as existsSync35, mkdirSync as mkdirSync11, unlinkSync as unlinkSync10, writeFileSync as writeFileSync26 } from "fs";
12181
12478
  function clearReviewFiles(paths) {
12182
12479
  for (const path52 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
12183
- if (existsSync33(path52)) unlinkSync10(path52);
12480
+ if (existsSync35(path52)) unlinkSync10(path52);
12184
12481
  }
12185
12482
  }
12186
12483
  function prepareReviewDir(paths, requestBody, force) {
12187
- mkdirSync10(paths.reviewDir, { recursive: true });
12484
+ mkdirSync11(paths.reviewDir, { recursive: true });
12188
12485
  if (force) clearReviewFiles(paths);
12189
12486
  writeFileSync26(paths.requestPath, requestBody);
12190
12487
  }
@@ -12225,11 +12522,11 @@ async function runApplySession(synthesisPath) {
12225
12522
  }
12226
12523
 
12227
12524
  // src/commands/review/cachedReviewerResult.ts
12228
- import { statSync as statSync4 } from "fs";
12525
+ import { statSync as statSync5 } from "fs";
12229
12526
  function cachedReviewerResult(name, outputPath) {
12230
12527
  let size;
12231
12528
  try {
12232
- size = statSync4(outputPath).size;
12529
+ size = statSync5(outputPath).size;
12233
12530
  } catch {
12234
12531
  return null;
12235
12532
  }
@@ -12386,8 +12683,63 @@ function skippedCodexResult(outputPath) {
12386
12683
  return { name: "codex", outputPath, exitCode: 0, stderr: "" };
12387
12684
  }
12388
12685
 
12686
+ // src/commands/review/formatReviewerFailure.ts
12687
+ var FAST_FAIL_MS = 1e3;
12688
+ var STDOUT_TAIL_LINES = 20;
12689
+ function indent(text) {
12690
+ return text.split(/\r?\n/).map((line) => ` ${line}`);
12691
+ }
12692
+ function tailLines(text, maxLines) {
12693
+ const lines = text.split(/\r?\n/);
12694
+ return lines.length <= maxLines ? text : lines.slice(-maxLines).join("\n");
12695
+ }
12696
+ function isFastFail(input) {
12697
+ return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
12698
+ }
12699
+ function fastFailHint(command) {
12700
+ return [
12701
+ `${command} exited almost immediately \u2014 it likely failed to start.`,
12702
+ `Common causes: not installed on PATH, not authenticated, or misconfigured.`,
12703
+ `Try \`${command} --version\` to confirm install, then \`${command} login\` if authentication may be missing.`
12704
+ ];
12705
+ }
12706
+ function outputDetail(input) {
12707
+ const stderr = input.stderr.trim();
12708
+ if (stderr) return ["stderr:", ...indent(stderr)];
12709
+ const stdout = (input.stdout ?? "").trim();
12710
+ if (stdout) {
12711
+ return [
12712
+ `stdout (no stderr was captured \u2014 showing last ${STDOUT_TAIL_LINES} lines):`,
12713
+ ...indent(tailLines(stdout, STDOUT_TAIL_LINES))
12714
+ ];
12715
+ }
12716
+ return [
12717
+ "No stderr or stdout was captured. Check the review folder for partial output, or rerun with --verbose for full streaming output."
12718
+ ];
12719
+ }
12720
+ function formatReviewerFailure(input) {
12721
+ const command = input.command ?? input.name;
12722
+ const seconds = Math.round((input.elapsedMs ?? 0) / 1e3);
12723
+ const headerLine = `${command} CLI exited with code ${input.exitCode} after ${seconds}s`;
12724
+ const detailLines = [];
12725
+ if (isFastFail(input)) detailLines.push(...fastFailHint(command));
12726
+ detailLines.push(...outputDetail(input));
12727
+ return { headerLine, detailLines };
12728
+ }
12729
+ function printReviewerFailure(input) {
12730
+ if (input.exitCode === 0) return;
12731
+ const diagnostic = formatReviewerFailure(input);
12732
+ console.error(`[${input.name}] ${diagnostic.headerLine}`);
12733
+ for (const line of diagnostic.detailLines) console.error(line);
12734
+ }
12735
+
12736
+ // src/commands/review/printReviewerFailures.ts
12737
+ function printReviewerFailures(results) {
12738
+ for (const r of results) printReviewerFailure(r);
12739
+ }
12740
+
12389
12741
  // src/commands/review/runAndSynthesise.ts
12390
- import { existsSync as existsSync35, unlinkSync as unlinkSync12 } from "fs";
12742
+ import { existsSync as existsSync37, unlinkSync as unlinkSync12 } from "fs";
12391
12743
 
12392
12744
  // src/commands/review/buildReviewerStdin.ts
12393
12745
  var REVIEW_PROMPT = `You are acting as a reviewer for a proposed code change made by another engineer. The full review request \u2014 branch, base, changed files, and unified diff \u2014 is in request.md in the current working directory.
@@ -12450,26 +12802,26 @@ The review request is at: ${requestPath}
12450
12802
  `;
12451
12803
  }
12452
12804
 
12453
- // src/commands/review/printReviewerFailures.ts
12454
- function printReviewerFailures(results) {
12455
- for (const r of results) {
12456
- if (r.exitCode === 0) continue;
12457
- console.error(`[${r.name}] exited with code ${r.exitCode}`);
12458
- if (r.stderr) console.error(r.stderr.trim());
12459
- }
12460
- }
12461
-
12462
12805
  // src/commands/review/runClaudeReviewer.ts
12463
12806
  import { writeFileSync as writeFileSync27 } from "fs";
12464
12807
 
12465
12808
  // src/commands/review/finaliseReviewerSpinner.ts
12466
- function finaliseReviewerSpinner(spinner, name, exitCode, elapsedMs) {
12809
+ var SUMMARY_MAX_LEN = 80;
12810
+ function summariseStderr(stderr) {
12811
+ const firstLine = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
12812
+ if (!firstLine) return "";
12813
+ const trimmed = firstLine.trim();
12814
+ return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
12815
+ }
12816
+ function finaliseReviewerSpinner(spinner, name, exitCode, elapsedMs, stderr = "") {
12467
12817
  const elapsed = Math.round(elapsedMs / 1e3);
12468
12818
  if (exitCode === 0) {
12469
12819
  spinner.succeed(`${name} \u2014 done in ${elapsed}s`);
12470
12820
  return;
12471
12821
  }
12472
- spinner.fail(`${name} \u2014 failed in ${elapsed}s`);
12822
+ const summary = summariseStderr(stderr);
12823
+ const suffix = summary ? `: ${summary}` : "";
12824
+ spinner.fail(`${name} \u2014 failed in ${elapsed}s (exit ${exitCode})${suffix}`);
12473
12825
  }
12474
12826
 
12475
12827
  // src/commands/review/finaliseReviewerRun.ts
@@ -12479,13 +12831,17 @@ function finaliseReviewerRun(spec, spinner, result) {
12479
12831
  spinner,
12480
12832
  spec.name,
12481
12833
  result.exitCode,
12482
- result.elapsedMs
12834
+ result.elapsedMs,
12835
+ result.stderr
12483
12836
  );
12484
12837
  return {
12485
12838
  name: spec.name,
12839
+ command: spec.command,
12486
12840
  outputPath: spec.outputPath,
12487
12841
  exitCode: result.exitCode,
12488
- stderr: result.stderr
12842
+ stderr: result.stderr,
12843
+ stdout: result.stdout,
12844
+ elapsedMs: result.elapsedMs
12489
12845
  };
12490
12846
  }
12491
12847
 
@@ -12610,14 +12966,35 @@ function attachStderrCollector(child) {
12610
12966
  return state;
12611
12967
  }
12612
12968
 
12969
+ // src/commands/review/attachStdoutTail.ts
12970
+ function attachStdoutTail(child, maxBytes = 8192) {
12971
+ const state = { value: "" };
12972
+ child.stdout?.on("data", (chunk) => {
12973
+ state.value += chunk.toString("utf-8");
12974
+ if (state.value.length > maxBytes) {
12975
+ state.value = state.value.slice(state.value.length - maxBytes);
12976
+ }
12977
+ });
12978
+ return state;
12979
+ }
12980
+
12613
12981
  // src/commands/review/logChildClose.ts
12614
- function logChildClose(name, code, elapsedSeconds, stderr) {
12615
- if (code === 0) {
12616
- console.log(`[${name}] done in ${elapsedSeconds}s`);
12982
+ function logChildClose(args) {
12983
+ const elapsedSeconds = Math.round(args.elapsedMs / 1e3);
12984
+ if (args.exitCode === 0) {
12985
+ console.log(`[${args.name}] done in ${elapsedSeconds}s`);
12617
12986
  return;
12618
12987
  }
12619
- console.error(`[${name}] exited with code ${code} in ${elapsedSeconds}s`);
12620
- if (stderr) console.error(stderr.trim());
12988
+ const diagnostic = formatReviewerFailure({
12989
+ name: args.name,
12990
+ command: args.command,
12991
+ exitCode: args.exitCode,
12992
+ stderr: args.stderr,
12993
+ stdout: args.stdout,
12994
+ elapsedMs: args.elapsedMs
12995
+ });
12996
+ console.error(`[${args.name}] ${diagnostic.headerLine}`);
12997
+ for (const line of diagnostic.detailLines) console.error(line);
12621
12998
  }
12622
12999
 
12623
13000
  // src/commands/review/handleChildClose.ts
@@ -12625,8 +13002,14 @@ function handleChildClose(args) {
12625
13002
  const elapsedMs = Date.now() - args.startedAt;
12626
13003
  const exitCode = args.code ?? 0;
12627
13004
  if (!args.quiet) {
12628
- const elapsed = Math.round(elapsedMs / 1e3);
12629
- logChildClose(args.name, exitCode, elapsed, args.stderr);
13005
+ logChildClose({
13006
+ name: args.name,
13007
+ command: args.command,
13008
+ exitCode,
13009
+ elapsedMs,
13010
+ stderr: args.stderr,
13011
+ stdout: args.stdout
13012
+ });
12630
13013
  }
12631
13014
  return { exitCode, elapsedMs };
12632
13015
  }
@@ -12644,6 +13027,7 @@ ${message}` : message;
12644
13027
  return {
12645
13028
  exitCode: 127,
12646
13029
  stderr,
13030
+ stdout: ctx.stdout,
12647
13031
  elapsedMs: Date.now() - ctx.startedAt
12648
13032
  };
12649
13033
  }
@@ -12656,6 +13040,7 @@ function onErrorResult(ctx, err) {
12656
13040
  command: ctx.command,
12657
13041
  name: ctx.name,
12658
13042
  stderr: ctx.stderr.value,
13043
+ stdout: ctx.stdout.value,
12659
13044
  startedAt: ctx.startedAt,
12660
13045
  quiet: ctx.quiet
12661
13046
  },
@@ -12668,10 +13053,12 @@ function onCloseResult(ctx, code) {
12668
13053
  code,
12669
13054
  startedAt: ctx.startedAt,
12670
13055
  name: ctx.name,
13056
+ command: ctx.command,
12671
13057
  stderr: ctx.stderr.value,
13058
+ stdout: ctx.stdout.value,
12672
13059
  quiet: ctx.quiet
12673
13060
  });
12674
- return { ...closed, stderr: ctx.stderr.value };
13061
+ return { ...closed, stderr: ctx.stderr.value, stdout: ctx.stdout.value };
12675
13062
  }
12676
13063
  function waitForChildExit(ctx) {
12677
13064
  return new Promise((resolve15) => {
@@ -12698,21 +13085,24 @@ function writeStdinSafely(child, payload) {
12698
13085
  }
12699
13086
  function startChild(spec) {
12700
13087
  const child = spawn6(spec.command, spec.args, {
12701
- stdio: ["pipe", "pipe", "pipe"]
13088
+ stdio: ["pipe", "pipe", "pipe"],
13089
+ shell: process.platform === "win32"
12702
13090
  });
12703
13091
  const flushPending = attachLineParser(child, spec.onLine);
12704
13092
  const stderr = attachStderrCollector(child);
13093
+ const stdout = attachStdoutTail(child);
12705
13094
  writeStdinSafely(child, spec.stdin);
12706
- return { child, flushPending, stderr };
13095
+ return { child, flushPending, stderr, stdout };
12707
13096
  }
12708
13097
  function runStreamingChild(spec) {
12709
13098
  const startedAt = Date.now();
12710
13099
  if (!spec.quiet) console.log(`[${spec.name}] starting`);
12711
- const { child, flushPending, stderr } = startChild(spec);
13100
+ const { child, flushPending, stderr, stdout } = startChild(spec);
12712
13101
  return waitForChildExit({
12713
13102
  child,
12714
13103
  flushPending,
12715
13104
  stderr,
13105
+ stdout,
12716
13106
  name: spec.name,
12717
13107
  command: spec.command,
12718
13108
  startedAt,
@@ -12724,9 +13114,10 @@ function runStreamingChild(spec) {
12724
13114
  async function runClaudeReviewer(spec) {
12725
13115
  let finalText = "";
12726
13116
  const { spinner } = spec;
13117
+ const command = "claude";
12727
13118
  const result = await runStreamingChild({
12728
13119
  name: spec.name,
12729
- command: "claude",
13120
+ command,
12730
13121
  args: [
12731
13122
  "-p",
12732
13123
  "--add-dir",
@@ -12749,7 +13140,7 @@ async function runClaudeReviewer(spec) {
12749
13140
  });
12750
13141
  if (result.exitCode === 0 && finalText)
12751
13142
  writeFileSync27(spec.outputPath, finalText);
12752
- return finaliseReviewerRun(spec, spinner, result);
13143
+ return finaliseReviewerRun({ ...spec, command }, spinner, result);
12753
13144
  }
12754
13145
 
12755
13146
  // src/commands/review/resolveClaude.ts
@@ -12766,7 +13157,7 @@ function resolveClaude(args) {
12766
13157
  }
12767
13158
 
12768
13159
  // src/commands/review/runCodexReviewer.ts
12769
- import { existsSync as existsSync34, unlinkSync as unlinkSync11 } from "fs";
13160
+ import { existsSync as existsSync36, unlinkSync as unlinkSync11 } from "fs";
12770
13161
 
12771
13162
  // src/commands/review/parseCodexEvent.ts
12772
13163
  function isItemStarted(value) {
@@ -12807,9 +13198,10 @@ function codexArgs(reviewDir, outputPath) {
12807
13198
  }
12808
13199
  async function runCodexReviewer(spec) {
12809
13200
  const { spinner } = spec;
13201
+ const command = "codex";
12810
13202
  const result = await runStreamingChild({
12811
13203
  name: spec.name,
12812
- command: "codex",
13204
+ command,
12813
13205
  args: codexArgs(spec.reviewDir, spec.outputPath),
12814
13206
  stdin: spec.stdin,
12815
13207
  quiet: Boolean(spinner),
@@ -12819,10 +13211,10 @@ async function runCodexReviewer(spec) {
12819
13211
  reportReviewerToolUse(spec.name, event, spinner);
12820
13212
  }
12821
13213
  });
12822
- if (result.exitCode !== 0 && existsSync34(spec.outputPath)) {
13214
+ if (result.exitCode !== 0 && existsSync36(spec.outputPath)) {
12823
13215
  unlinkSync11(spec.outputPath);
12824
13216
  }
12825
- return finaliseReviewerRun(spec, spinner, result);
13217
+ return finaliseReviewerRun({ ...spec, command }, spinner, result);
12826
13218
  }
12827
13219
 
12828
13220
  // src/commands/review/resolveCodex.ts
@@ -12858,13 +13250,12 @@ async function runReviewers(reviewDir, claudePath, codexPath, stdinPrompt, optio
12858
13250
  multi: options2.multi
12859
13251
  });
12860
13252
  const results = await Promise.all([claudePromise, codexPromise]);
12861
- if (options2.multi) printReviewerFailures(results);
12862
13253
  const anyFresh = options2.cachedClaude === null || options2.codexPlan.kind !== "cached";
12863
13254
  return { results, anyFresh };
12864
13255
  }
12865
13256
 
12866
13257
  // src/commands/review/synthesise.ts
12867
- import { readFileSync as readFileSync30 } from "fs";
13258
+ import { readFileSync as readFileSync32 } from "fs";
12868
13259
 
12869
13260
  // src/commands/review/buildSynthesisStdin.ts
12870
13261
  var SYNTHESIS_PROMPT = `You are consolidating two independent code reviews of the same change. The original review request is in request.md. The two reviews are in claude.md and codex.md in the current working directory.
@@ -12920,7 +13311,7 @@ Files:
12920
13311
 
12921
13312
  // src/commands/review/synthesise.ts
12922
13313
  function printSummary(synthesisPath) {
12923
- const markdown = readFileSync30(synthesisPath, "utf-8");
13314
+ const markdown = readFileSync32(synthesisPath, "utf-8");
12924
13315
  console.log("");
12925
13316
  console.log(buildReviewSummary(markdown));
12926
13317
  console.log("");
@@ -12946,12 +13337,14 @@ async function synthesise(paths, options2) {
12946
13337
  outputPath: paths.synthesisPath,
12947
13338
  spinner
12948
13339
  });
12949
- if (multi) printReviewerFailures([result]);
12950
13340
  if (result.exitCode === 0) printSummary(paths.synthesisPath);
12951
13341
  return result;
12952
13342
  }
12953
13343
 
12954
13344
  // src/commands/review/runAndSynthesise.ts
13345
+ function failed(results) {
13346
+ return results.filter((r) => r.exitCode !== 0);
13347
+ }
12955
13348
  async function runAndSynthesise(args) {
12956
13349
  const { paths, multi } = args;
12957
13350
  const { results, anyFresh } = await runReviewers(
@@ -12961,17 +13354,17 @@ async function runAndSynthesise(args) {
12961
13354
  buildReviewerStdin(paths.requestPath),
12962
13355
  { multi, codexPlan: args.codexPlan, cachedClaude: args.cachedClaude }
12963
13356
  );
13357
+ const failures = failed(results);
12964
13358
  if (results.every((r) => r.exitCode !== 0)) {
12965
- console.error(
12966
- "Both reviewers failed; skipping synthesis. See review folder for stderr details."
12967
- );
12968
- return false;
13359
+ console.error("Both reviewers failed; skipping synthesis.");
13360
+ return { ok: false, failures };
12969
13361
  }
12970
- if (anyFresh && existsSync35(paths.synthesisPath)) {
13362
+ if (anyFresh && existsSync37(paths.synthesisPath)) {
12971
13363
  unlinkSync12(paths.synthesisPath);
12972
13364
  }
12973
13365
  const synthesisResult = await synthesise(paths, { multi });
12974
- return synthesisResult.exitCode === 0;
13366
+ if (synthesisResult.exitCode !== 0) failures.push(synthesisResult);
13367
+ return { ok: synthesisResult.exitCode === 0, failures };
12975
13368
  }
12976
13369
 
12977
13370
  // src/commands/review/useSpinnerUi.ts
@@ -12994,19 +13387,25 @@ function finishUi(ui, ok) {
12994
13387
  if (ok) ui.elapsed.succeed(label2);
12995
13388
  else ui.elapsed.fail(label2);
12996
13389
  }
13390
+ function reportFailures(failures, usingSpinner) {
13391
+ if (failures.length === 0) return;
13392
+ if (!usingSpinner) return;
13393
+ printReviewerFailures(failures);
13394
+ }
12997
13395
  async function runReviewPipeline(paths, options2) {
12998
13396
  const cachedClaude = cachedReviewerResult("claude", paths.claudePath);
12999
13397
  const codexPlan = await planCodexReviewer(paths.codexPath);
13000
13398
  const ui = createUi(useSpinnerUi(options2.verbose));
13001
13399
  try {
13002
- const ok = await runAndSynthesise({
13400
+ const outcome = await runAndSynthesise({
13003
13401
  paths,
13004
13402
  cachedClaude,
13005
13403
  codexPlan,
13006
13404
  multi: ui.multi
13007
13405
  });
13008
- finishUi(ui, ok);
13009
- return ok;
13406
+ finishUi(ui, outcome.ok);
13407
+ reportFailures(outcome.failures, ui.multi !== void 0);
13408
+ return outcome.ok;
13010
13409
  } catch (err) {
13011
13410
  ui.multi?.failRemaining();
13012
13411
  throw err;
@@ -13777,8 +14176,8 @@ function registerSql(program2) {
13777
14176
  }
13778
14177
 
13779
14178
  // src/commands/transcript/shared.ts
13780
- import { existsSync as existsSync36, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
13781
- import { basename as basename8, join as join36, relative as relative2 } from "path";
14179
+ import { existsSync as existsSync38, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
14180
+ import { basename as basename8, join as join40, relative as relative2 } from "path";
13782
14181
  import * as readline2 from "readline";
13783
14182
  var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
13784
14183
  function getDatePrefix(daysOffset = 0) {
@@ -13793,11 +14192,11 @@ function isValidDatePrefix(filename) {
13793
14192
  return DATE_PREFIX_REGEX.test(filename);
13794
14193
  }
13795
14194
  function collectFiles(dir, extension) {
13796
- if (!existsSync36(dir)) return [];
14195
+ if (!existsSync38(dir)) return [];
13797
14196
  const results = [];
13798
- for (const entry of readdirSync6(dir)) {
13799
- const fullPath = join36(dir, entry);
13800
- if (statSync5(fullPath).isDirectory()) {
14197
+ for (const entry of readdirSync7(dir)) {
14198
+ const fullPath = join40(dir, entry);
14199
+ if (statSync6(fullPath).isDirectory()) {
13801
14200
  results.push(...collectFiles(fullPath, extension));
13802
14201
  } else if (entry.endsWith(extension)) {
13803
14202
  results.push(fullPath);
@@ -13890,14 +14289,14 @@ async function configure() {
13890
14289
  }
13891
14290
 
13892
14291
  // src/commands/transcript/format/index.ts
13893
- import { existsSync as existsSync38 } from "fs";
14292
+ import { existsSync as existsSync40 } from "fs";
13894
14293
 
13895
14294
  // src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
13896
- import { dirname as dirname20, join as join38 } from "path";
14295
+ import { dirname as dirname20, join as join42 } from "path";
13897
14296
 
13898
14297
  // src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
13899
- import { renameSync as renameSync2 } from "fs";
13900
- import { join as join37 } from "path";
14298
+ import { renameSync as renameSync3 } from "fs";
14299
+ import { join as join41 } from "path";
13901
14300
  async function resolveDate(rl, choice) {
13902
14301
  if (choice === "1") return getDatePrefix(0);
13903
14302
  if (choice === "2") return getDatePrefix(-1);
@@ -13912,7 +14311,7 @@ async function resolveDate(rl, choice) {
13912
14311
  }
13913
14312
  function renameWithPrefix(vttDir, vttFile, prefix2) {
13914
14313
  const newFilename = `${prefix2}.${vttFile}`;
13915
- renameSync2(join37(vttDir, vttFile), join37(vttDir, newFilename));
14314
+ renameSync3(join41(vttDir, vttFile), join41(vttDir, newFilename));
13916
14315
  console.log(`Renamed to: ${newFilename}`);
13917
14316
  return newFilename;
13918
14317
  }
@@ -13946,12 +14345,12 @@ async function fixInvalidDatePrefixes(vttFiles) {
13946
14345
  const vttFileDir = dirname20(vttFile.absolutePath);
13947
14346
  const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
13948
14347
  if (newFilename) {
13949
- const newRelativePath = join38(
14348
+ const newRelativePath = join42(
13950
14349
  dirname20(vttFile.relativePath),
13951
14350
  newFilename
13952
14351
  );
13953
14352
  vttFiles[i] = {
13954
- absolutePath: join38(vttFileDir, newFilename),
14353
+ absolutePath: join42(vttFileDir, newFilename),
13955
14354
  relativePath: newRelativePath,
13956
14355
  filename: newFilename
13957
14356
  };
@@ -13964,8 +14363,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
13964
14363
  }
13965
14364
 
13966
14365
  // src/commands/transcript/format/processVttFile/index.ts
13967
- import { existsSync as existsSync37, mkdirSync as mkdirSync11, readFileSync as readFileSync31, writeFileSync as writeFileSync28 } from "fs";
13968
- import { basename as basename9, dirname as dirname21, join as join39 } from "path";
14366
+ import { existsSync as existsSync39, mkdirSync as mkdirSync12, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
14367
+ import { basename as basename9, dirname as dirname21, join as join43 } from "path";
13969
14368
 
13970
14369
  // src/commands/transcript/cleanText.ts
13971
14370
  function cleanText(text) {
@@ -14175,22 +14574,22 @@ function toMdFilename(vttFilename) {
14175
14574
  return `${basename9(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
14176
14575
  }
14177
14576
  function resolveOutputDir(relativeDir, transcriptsDir) {
14178
- return relativeDir === "." ? transcriptsDir : join39(transcriptsDir, relativeDir);
14577
+ return relativeDir === "." ? transcriptsDir : join43(transcriptsDir, relativeDir);
14179
14578
  }
14180
14579
  function buildOutputPaths(vttFile, transcriptsDir) {
14181
14580
  const mdFile = toMdFilename(vttFile.filename);
14182
14581
  const relativeDir = dirname21(vttFile.relativePath);
14183
14582
  const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
14184
- const outputPath = join39(outputDir, mdFile);
14583
+ const outputPath = join43(outputDir, mdFile);
14185
14584
  return { outputDir, outputPath, mdFile, relativeDir };
14186
14585
  }
14187
14586
  function logSkipped(relativeDir, mdFile) {
14188
- console.log(`Skipping (already exists): ${join39(relativeDir, mdFile)}`);
14587
+ console.log(`Skipping (already exists): ${join43(relativeDir, mdFile)}`);
14189
14588
  return "skipped";
14190
14589
  }
14191
14590
  function ensureDirectory(dir, label2) {
14192
- if (!existsSync37(dir)) {
14193
- mkdirSync11(dir, { recursive: true });
14591
+ if (!existsSync39(dir)) {
14592
+ mkdirSync12(dir, { recursive: true });
14194
14593
  console.log(`Created ${label2}: ${dir}`);
14195
14594
  }
14196
14595
  }
@@ -14212,7 +14611,7 @@ function logReduction(cueCount, messageCount) {
14212
14611
  }
14213
14612
  function readAndParseCues(inputPath) {
14214
14613
  console.log(`Reading: ${inputPath}`);
14215
- return processCues(readFileSync31(inputPath, "utf-8"));
14614
+ return processCues(readFileSync33(inputPath, "utf-8"));
14216
14615
  }
14217
14616
  function writeFormatted(outputPath, content) {
14218
14617
  writeFileSync28(outputPath, content, "utf-8");
@@ -14225,7 +14624,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
14225
14624
  logReduction(cues.length, chatMessages.length);
14226
14625
  }
14227
14626
  function tryProcessVtt(vttFile, paths) {
14228
- if (existsSync37(paths.outputPath))
14627
+ if (existsSync39(paths.outputPath))
14229
14628
  return logSkipped(paths.relativeDir, paths.mdFile);
14230
14629
  convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
14231
14630
  return "processed";
@@ -14251,7 +14650,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
14251
14650
  logSummary(counts);
14252
14651
  }
14253
14652
  function requireVttDir(vttDir) {
14254
- if (!existsSync38(vttDir)) {
14653
+ if (!existsSync40(vttDir)) {
14255
14654
  console.error(`VTT directory not found: ${vttDir}`);
14256
14655
  process.exit(1);
14257
14656
  }
@@ -14283,18 +14682,18 @@ async function format() {
14283
14682
  }
14284
14683
 
14285
14684
  // src/commands/transcript/summarise/index.ts
14286
- import { existsSync as existsSync40 } from "fs";
14287
- import { basename as basename10, dirname as dirname23, join as join41, relative as relative3 } from "path";
14685
+ import { existsSync as existsSync42 } from "fs";
14686
+ import { basename as basename10, dirname as dirname23, join as join45, relative as relative3 } from "path";
14288
14687
 
14289
14688
  // src/commands/transcript/summarise/processStagedFile/index.ts
14290
14689
  import {
14291
- existsSync as existsSync39,
14292
- mkdirSync as mkdirSync12,
14293
- readFileSync as readFileSync32,
14294
- renameSync as renameSync3,
14690
+ existsSync as existsSync41,
14691
+ mkdirSync as mkdirSync13,
14692
+ readFileSync as readFileSync34,
14693
+ renameSync as renameSync4,
14295
14694
  rmSync
14296
14695
  } from "fs";
14297
- import { dirname as dirname22, join as join40 } from "path";
14696
+ import { dirname as dirname22, join as join44 } from "path";
14298
14697
 
14299
14698
  // src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
14300
14699
  import chalk142 from "chalk";
@@ -14323,9 +14722,9 @@ function validateStagedContent(filename, content) {
14323
14722
  }
14324
14723
 
14325
14724
  // src/commands/transcript/summarise/processStagedFile/index.ts
14326
- var STAGING_DIR = join40(process.cwd(), ".assist", "transcript");
14725
+ var STAGING_DIR = join44(process.cwd(), ".assist", "transcript");
14327
14726
  function processStagedFile() {
14328
- if (!existsSync39(STAGING_DIR)) {
14727
+ if (!existsSync41(STAGING_DIR)) {
14329
14728
  return false;
14330
14729
  }
14331
14730
  const stagedFiles = findMdFilesRecursive(STAGING_DIR);
@@ -14334,7 +14733,7 @@ function processStagedFile() {
14334
14733
  }
14335
14734
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
14336
14735
  const stagedFile = stagedFiles[0];
14337
- const content = readFileSync32(stagedFile.absolutePath, "utf-8");
14736
+ const content = readFileSync34(stagedFile.absolutePath, "utf-8");
14338
14737
  validateStagedContent(stagedFile.filename, content);
14339
14738
  const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
14340
14739
  const transcriptFiles = findMdFilesRecursive(transcriptsDir);
@@ -14347,12 +14746,12 @@ function processStagedFile() {
14347
14746
  );
14348
14747
  process.exit(1);
14349
14748
  }
14350
- const destPath = join40(summaryDir, matchingTranscript.relativePath);
14749
+ const destPath = join44(summaryDir, matchingTranscript.relativePath);
14351
14750
  const destDir = dirname22(destPath);
14352
- if (!existsSync39(destDir)) {
14353
- mkdirSync12(destDir, { recursive: true });
14751
+ if (!existsSync41(destDir)) {
14752
+ mkdirSync13(destDir, { recursive: true });
14354
14753
  }
14355
- renameSync3(stagedFile.absolutePath, destPath);
14754
+ renameSync4(stagedFile.absolutePath, destPath);
14356
14755
  const remaining = findMdFilesRecursive(STAGING_DIR);
14357
14756
  if (remaining.length === 0) {
14358
14757
  rmSync(STAGING_DIR, { recursive: true });
@@ -14363,7 +14762,7 @@ function processStagedFile() {
14363
14762
  // src/commands/transcript/summarise/index.ts
14364
14763
  function buildRelativeKey(relativePath, baseName) {
14365
14764
  const relDir = dirname23(relativePath);
14366
- return relDir === "." ? baseName : join41(relDir, baseName);
14765
+ return relDir === "." ? baseName : join45(relDir, baseName);
14367
14766
  }
14368
14767
  function buildSummaryIndex(summaryDir) {
14369
14768
  const summaryFiles = findMdFilesRecursive(summaryDir);
@@ -14373,10 +14772,10 @@ function buildSummaryIndex(summaryDir) {
14373
14772
  )
14374
14773
  );
14375
14774
  }
14376
- function summarise2() {
14775
+ function summarise3() {
14377
14776
  processStagedFile();
14378
14777
  const { transcriptsDir, summaryDir } = getTranscriptConfig();
14379
- if (!existsSync40(transcriptsDir)) {
14778
+ if (!existsSync42(transcriptsDir)) {
14380
14779
  console.log("No transcripts directory found.");
14381
14780
  return;
14382
14781
  }
@@ -14397,8 +14796,8 @@ function summarise2() {
14397
14796
  }
14398
14797
  const next3 = missing[0];
14399
14798
  const outputFilename = `${getTranscriptBaseName(next3.filename)}.md`;
14400
- const outputPath = join41(STAGING_DIR, outputFilename);
14401
- const summaryFileDir = join41(summaryDir, dirname23(next3.relativePath));
14799
+ const outputPath = join45(STAGING_DIR, outputFilename);
14800
+ const summaryFileDir = join45(summaryDir, dirname23(next3.relativePath));
14402
14801
  const relativeTranscriptPath = encodeURI(
14403
14802
  relative3(summaryFileDir, next3.absolutePath).replace(/\\/g, "/")
14404
14803
  );
@@ -14419,7 +14818,7 @@ function registerTranscript(program2) {
14419
14818
  const transcriptCommand = program2.command("transcript").description("Meeting transcript utilities");
14420
14819
  transcriptCommand.command("configure").description("Configure transcript directories").action(configure);
14421
14820
  transcriptCommand.command("format").description("Convert VTT files to formatted markdown transcripts").action(format);
14422
- transcriptCommand.command("summarise").description("List transcripts that do not have summaries").action(summarise2);
14821
+ transcriptCommand.command("summarise").description("List transcripts that do not have summaries").action(summarise3);
14423
14822
  }
14424
14823
 
14425
14824
  // src/commands/registerVerify.ts
@@ -14447,50 +14846,50 @@ function registerVerify(program2) {
14447
14846
 
14448
14847
  // src/commands/voice/devices.ts
14449
14848
  import { spawnSync as spawnSync4 } from "child_process";
14450
- import { join as join43 } from "path";
14849
+ import { join as join47 } from "path";
14451
14850
 
14452
14851
  // src/commands/voice/shared.ts
14453
- import { homedir as homedir9 } from "os";
14454
- import { dirname as dirname24, join as join42 } from "path";
14852
+ import { homedir as homedir10 } from "os";
14853
+ import { dirname as dirname24, join as join46 } from "path";
14455
14854
  import { fileURLToPath as fileURLToPath6 } from "url";
14456
14855
  var __dirname6 = dirname24(fileURLToPath6(import.meta.url));
14457
- var VOICE_DIR = join42(homedir9(), ".assist", "voice");
14856
+ var VOICE_DIR = join46(homedir10(), ".assist", "voice");
14458
14857
  var voicePaths = {
14459
14858
  dir: VOICE_DIR,
14460
- pid: join42(VOICE_DIR, "voice.pid"),
14461
- log: join42(VOICE_DIR, "voice.log"),
14462
- venv: join42(VOICE_DIR, ".venv"),
14463
- lock: join42(VOICE_DIR, "voice.lock")
14859
+ pid: join46(VOICE_DIR, "voice.pid"),
14860
+ log: join46(VOICE_DIR, "voice.log"),
14861
+ venv: join46(VOICE_DIR, ".venv"),
14862
+ lock: join46(VOICE_DIR, "voice.lock")
14464
14863
  };
14465
14864
  function getPythonDir() {
14466
- return join42(__dirname6, "commands", "voice", "python");
14865
+ return join46(__dirname6, "commands", "voice", "python");
14467
14866
  }
14468
14867
  function getVenvPython() {
14469
- return process.platform === "win32" ? join42(voicePaths.venv, "Scripts", "python.exe") : join42(voicePaths.venv, "bin", "python");
14868
+ return process.platform === "win32" ? join46(voicePaths.venv, "Scripts", "python.exe") : join46(voicePaths.venv, "bin", "python");
14470
14869
  }
14471
14870
  function getLockDir() {
14472
14871
  const config = loadConfig();
14473
14872
  return config.voice?.lockDir ?? VOICE_DIR;
14474
14873
  }
14475
14874
  function getLockFile() {
14476
- return join42(getLockDir(), "voice.lock");
14875
+ return join46(getLockDir(), "voice.lock");
14477
14876
  }
14478
14877
 
14479
14878
  // src/commands/voice/devices.ts
14480
14879
  function devices() {
14481
- const script = join43(getPythonDir(), "list_devices.py");
14880
+ const script = join47(getPythonDir(), "list_devices.py");
14482
14881
  spawnSync4(getVenvPython(), [script], { stdio: "inherit" });
14483
14882
  }
14484
14883
 
14485
14884
  // src/commands/voice/logs.ts
14486
- import { existsSync as existsSync41, readFileSync as readFileSync33 } from "fs";
14885
+ import { existsSync as existsSync43, readFileSync as readFileSync35 } from "fs";
14487
14886
  function logs(options2) {
14488
- if (!existsSync41(voicePaths.log)) {
14887
+ if (!existsSync43(voicePaths.log)) {
14489
14888
  console.log("No voice log file found");
14490
14889
  return;
14491
14890
  }
14492
14891
  const count = Number.parseInt(options2.lines ?? "150", 10);
14493
- const content = readFileSync33(voicePaths.log, "utf-8").trim();
14892
+ const content = readFileSync35(voicePaths.log, "utf-8").trim();
14494
14893
  if (!content) {
14495
14894
  console.log("Voice log is empty");
14496
14895
  return;
@@ -14512,13 +14911,13 @@ function logs(options2) {
14512
14911
 
14513
14912
  // src/commands/voice/setup.ts
14514
14913
  import { spawnSync as spawnSync5 } from "child_process";
14515
- import { mkdirSync as mkdirSync14 } from "fs";
14516
- import { join as join45 } from "path";
14914
+ import { mkdirSync as mkdirSync15 } from "fs";
14915
+ import { join as join49 } from "path";
14517
14916
 
14518
14917
  // src/commands/voice/checkLockFile.ts
14519
14918
  import { execSync as execSync41 } from "child_process";
14520
- import { existsSync as existsSync42, mkdirSync as mkdirSync13, readFileSync as readFileSync34, writeFileSync as writeFileSync29 } from "fs";
14521
- import { join as join44 } from "path";
14919
+ import { existsSync as existsSync44, mkdirSync as mkdirSync14, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
14920
+ import { join as join48 } from "path";
14522
14921
  function isProcessAlive2(pid) {
14523
14922
  try {
14524
14923
  process.kill(pid, 0);
@@ -14529,9 +14928,9 @@ function isProcessAlive2(pid) {
14529
14928
  }
14530
14929
  function checkLockFile() {
14531
14930
  const lockFile = getLockFile();
14532
- if (!existsSync42(lockFile)) return;
14931
+ if (!existsSync44(lockFile)) return;
14533
14932
  try {
14534
- const lock = JSON.parse(readFileSync34(lockFile, "utf-8"));
14933
+ const lock = JSON.parse(readFileSync36(lockFile, "utf-8"));
14535
14934
  if (lock.pid && isProcessAlive2(lock.pid)) {
14536
14935
  console.error(
14537
14936
  `Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
@@ -14542,7 +14941,7 @@ function checkLockFile() {
14542
14941
  }
14543
14942
  }
14544
14943
  function bootstrapVenv() {
14545
- if (existsSync42(getVenvPython())) return;
14944
+ if (existsSync44(getVenvPython())) return;
14546
14945
  console.log("Setting up Python environment...");
14547
14946
  const pythonDir = getPythonDir();
14548
14947
  execSync41(
@@ -14555,7 +14954,7 @@ function bootstrapVenv() {
14555
14954
  }
14556
14955
  function writeLockFile(pid) {
14557
14956
  const lockFile = getLockFile();
14558
- mkdirSync13(join44(lockFile, ".."), { recursive: true });
14957
+ mkdirSync14(join48(lockFile, ".."), { recursive: true });
14559
14958
  writeFileSync29(
14560
14959
  lockFile,
14561
14960
  JSON.stringify({
@@ -14568,10 +14967,10 @@ function writeLockFile(pid) {
14568
14967
 
14569
14968
  // src/commands/voice/setup.ts
14570
14969
  function setup() {
14571
- mkdirSync14(voicePaths.dir, { recursive: true });
14970
+ mkdirSync15(voicePaths.dir, { recursive: true });
14572
14971
  bootstrapVenv();
14573
14972
  console.log("\nDownloading models...\n");
14574
- const script = join45(getPythonDir(), "setup_models.py");
14973
+ const script = join49(getPythonDir(), "setup_models.py");
14575
14974
  const result = spawnSync5(getVenvPython(), [script], {
14576
14975
  stdio: "inherit",
14577
14976
  env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
@@ -14584,8 +14983,8 @@ function setup() {
14584
14983
 
14585
14984
  // src/commands/voice/start.ts
14586
14985
  import { spawn as spawn7 } from "child_process";
14587
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync30 } from "fs";
14588
- import { join as join46 } from "path";
14986
+ import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync30 } from "fs";
14987
+ import { join as join50 } from "path";
14589
14988
 
14590
14989
  // src/commands/voice/buildDaemonEnv.ts
14591
14990
  function buildDaemonEnv(options2) {
@@ -14618,12 +15017,12 @@ function spawnBackground(python, script, env) {
14618
15017
  console.log(`Voice daemon started (PID ${pid})`);
14619
15018
  }
14620
15019
  function start2(options2) {
14621
- mkdirSync15(voicePaths.dir, { recursive: true });
15020
+ mkdirSync16(voicePaths.dir, { recursive: true });
14622
15021
  checkLockFile();
14623
15022
  bootstrapVenv();
14624
15023
  const debug = options2.debug || options2.foreground || process.platform === "win32";
14625
15024
  const env = buildDaemonEnv({ debug });
14626
- const script = join46(getPythonDir(), "voice_daemon.py");
15025
+ const script = join50(getPythonDir(), "voice_daemon.py");
14627
15026
  const python = getVenvPython();
14628
15027
  if (options2.foreground) {
14629
15028
  spawnForeground(python, script, env);
@@ -14633,7 +15032,7 @@ function start2(options2) {
14633
15032
  }
14634
15033
 
14635
15034
  // src/commands/voice/status.ts
14636
- import { existsSync as existsSync43, readFileSync as readFileSync35 } from "fs";
15035
+ import { existsSync as existsSync45, readFileSync as readFileSync37 } from "fs";
14637
15036
  function isProcessAlive3(pid) {
14638
15037
  try {
14639
15038
  process.kill(pid, 0);
@@ -14643,16 +15042,16 @@ function isProcessAlive3(pid) {
14643
15042
  }
14644
15043
  }
14645
15044
  function readRecentLogs(count) {
14646
- if (!existsSync43(voicePaths.log)) return [];
14647
- const lines = readFileSync35(voicePaths.log, "utf-8").trim().split("\n");
15045
+ if (!existsSync45(voicePaths.log)) return [];
15046
+ const lines = readFileSync37(voicePaths.log, "utf-8").trim().split("\n");
14648
15047
  return lines.slice(-count);
14649
15048
  }
14650
15049
  function status() {
14651
- if (!existsSync43(voicePaths.pid)) {
15050
+ if (!existsSync45(voicePaths.pid)) {
14652
15051
  console.log("Voice daemon: not running (no PID file)");
14653
15052
  return;
14654
15053
  }
14655
- const pid = Number.parseInt(readFileSync35(voicePaths.pid, "utf-8").trim(), 10);
15054
+ const pid = Number.parseInt(readFileSync37(voicePaths.pid, "utf-8").trim(), 10);
14656
15055
  const alive = isProcessAlive3(pid);
14657
15056
  console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
14658
15057
  const recent = readRecentLogs(5);
@@ -14671,13 +15070,13 @@ function status() {
14671
15070
  }
14672
15071
 
14673
15072
  // src/commands/voice/stop.ts
14674
- import { existsSync as existsSync44, readFileSync as readFileSync36, unlinkSync as unlinkSync13 } from "fs";
15073
+ import { existsSync as existsSync46, readFileSync as readFileSync38, unlinkSync as unlinkSync13 } from "fs";
14675
15074
  function stop2() {
14676
- if (!existsSync44(voicePaths.pid)) {
15075
+ if (!existsSync46(voicePaths.pid)) {
14677
15076
  console.log("Voice daemon is not running (no PID file)");
14678
15077
  return;
14679
15078
  }
14680
- const pid = Number.parseInt(readFileSync36(voicePaths.pid, "utf-8").trim(), 10);
15079
+ const pid = Number.parseInt(readFileSync38(voicePaths.pid, "utf-8").trim(), 10);
14681
15080
  try {
14682
15081
  process.kill(pid, "SIGTERM");
14683
15082
  console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
@@ -14690,7 +15089,7 @@ function stop2() {
14690
15089
  }
14691
15090
  try {
14692
15091
  const lockFile = getLockFile();
14693
- if (existsSync44(lockFile)) unlinkSync13(lockFile);
15092
+ if (existsSync46(lockFile)) unlinkSync13(lockFile);
14694
15093
  } catch {
14695
15094
  }
14696
15095
  console.log("Voice daemon stopped");
@@ -14911,20 +15310,20 @@ async function auth() {
14911
15310
  }
14912
15311
 
14913
15312
  // src/commands/roam/postRoamActivity.ts
14914
- import { execFileSync as execFileSync4 } from "child_process";
14915
- import { readdirSync as readdirSync7, readFileSync as readFileSync37, statSync as statSync6 } from "fs";
14916
- import { join as join47 } from "path";
15313
+ import { execFileSync as execFileSync5 } from "child_process";
15314
+ import { readdirSync as readdirSync8, readFileSync as readFileSync39, statSync as statSync7 } from "fs";
15315
+ import { join as join51 } from "path";
14917
15316
  function findPortFile(roamDir) {
14918
15317
  let entries;
14919
15318
  try {
14920
- entries = readdirSync7(roamDir);
15319
+ entries = readdirSync8(roamDir);
14921
15320
  } catch {
14922
15321
  return void 0;
14923
15322
  }
14924
15323
  const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
14925
- const path52 = join47(roamDir, name);
15324
+ const path52 = join51(roamDir, name);
14926
15325
  try {
14927
- return { path: path52, mtimeMs: statSync6(path52).mtimeMs };
15326
+ return { path: path52, mtimeMs: statSync7(path52).mtimeMs };
14928
15327
  } catch {
14929
15328
  return void 0;
14930
15329
  }
@@ -14934,17 +15333,17 @@ function findPortFile(roamDir) {
14934
15333
  function postRoamActivity(app, event) {
14935
15334
  const appData = process.env.APPDATA;
14936
15335
  if (!appData) return;
14937
- const portFile = findPortFile(join47(appData, "Roam"));
15336
+ const portFile = findPortFile(join51(appData, "Roam"));
14938
15337
  if (!portFile) return;
14939
15338
  let port;
14940
15339
  try {
14941
- port = readFileSync37(portFile, "utf-8").trim();
15340
+ port = readFileSync39(portFile, "utf-8").trim();
14942
15341
  } catch {
14943
15342
  return;
14944
15343
  }
14945
15344
  const url = `http://127.0.0.1:${port}/api/v1/activity/${app}/${event}?pid=${app === "codex" ? 99998 : 99999}`;
14946
15345
  try {
14947
- execFileSync4("curl", ["-sf", "--max-time", "0.2", "-X", "POST", url], {
15346
+ execFileSync5("curl", ["-sf", "--max-time", "0.2", "-X", "POST", url], {
14948
15347
  stdio: "ignore"
14949
15348
  });
14950
15349
  } catch {
@@ -15071,16 +15470,16 @@ function runPreCommands(pre, cwd) {
15071
15470
  }
15072
15471
 
15073
15472
  // src/commands/run/spawnRunCommand.ts
15074
- import { execFileSync as execFileSync5, spawn as spawn8 } from "child_process";
15075
- import { existsSync as existsSync45 } from "fs";
15076
- import { dirname as dirname25, join as join48, resolve as resolve11 } from "path";
15473
+ import { execFileSync as execFileSync6, spawn as spawn8 } from "child_process";
15474
+ import { existsSync as existsSync47 } from "fs";
15475
+ import { dirname as dirname25, join as join52, resolve as resolve11 } from "path";
15077
15476
  function resolveCommand2(command) {
15078
15477
  if (process.platform !== "win32" || command !== "bash") return command;
15079
15478
  try {
15080
- const gitPath = execFileSync5("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
15479
+ const gitPath = execFileSync6("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
15081
15480
  const gitRoot = resolve11(dirname25(gitPath), "..");
15082
- const gitBash = join48(gitRoot, "bin", "bash.exe");
15083
- if (existsSync45(gitBash)) return gitBash;
15481
+ const gitBash = join52(gitRoot, "bin", "bash.exe");
15482
+ if (existsSync47(gitBash)) return gitBash;
15084
15483
  } catch {
15085
15484
  }
15086
15485
  return command;
@@ -15147,8 +15546,8 @@ function run3(name, args) {
15147
15546
  }
15148
15547
 
15149
15548
  // src/commands/run/add.ts
15150
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync31 } from "fs";
15151
- import { join as join49 } from "path";
15549
+ import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync31 } from "fs";
15550
+ import { join as join53 } from "path";
15152
15551
 
15153
15552
  // src/commands/run/extractOption.ts
15154
15553
  function extractOption(args, flag) {
@@ -15209,15 +15608,15 @@ function saveNewRunConfig(name, command, args, cwd) {
15209
15608
  saveConfig(config);
15210
15609
  }
15211
15610
  function createCommandFile(name) {
15212
- const dir = join49(".claude", "commands");
15213
- mkdirSync16(dir, { recursive: true });
15611
+ const dir = join53(".claude", "commands");
15612
+ mkdirSync17(dir, { recursive: true });
15214
15613
  const content = `---
15215
15614
  description: Run ${name}
15216
15615
  ---
15217
15616
 
15218
15617
  Run \`assist run ${name} $ARGUMENTS 2>&1\`.
15219
15618
  `;
15220
- const filePath = join49(dir, `${name}.md`);
15619
+ const filePath = join53(dir, `${name}.md`);
15221
15620
  writeFileSync31(filePath, content);
15222
15621
  console.log(`Created command file: ${filePath}`);
15223
15622
  }
@@ -15273,8 +15672,8 @@ function link2() {
15273
15672
  }
15274
15673
 
15275
15674
  // src/commands/run/remove.ts
15276
- import { existsSync as existsSync46, unlinkSync as unlinkSync14 } from "fs";
15277
- import { join as join50 } from "path";
15675
+ import { existsSync as existsSync48, unlinkSync as unlinkSync14 } from "fs";
15676
+ import { join as join54 } from "path";
15278
15677
  function findRemoveIndex() {
15279
15678
  const idx = process.argv.indexOf("remove");
15280
15679
  if (idx === -1 || idx + 1 >= process.argv.length) return -1;
@@ -15289,8 +15688,8 @@ function parseRemoveName() {
15289
15688
  return process.argv[idx + 1];
15290
15689
  }
15291
15690
  function deleteCommandFile(name) {
15292
- const filePath = join50(".claude", "commands", `${name}.md`);
15293
- if (existsSync46(filePath)) {
15691
+ const filePath = join54(".claude", "commands", `${name}.md`);
15692
+ if (existsSync48(filePath)) {
15294
15693
  unlinkSync14(filePath);
15295
15694
  console.log(`Deleted command file: ${filePath}`);
15296
15695
  }
@@ -15326,9 +15725,9 @@ function registerRun(program2) {
15326
15725
 
15327
15726
  // src/commands/screenshot/index.ts
15328
15727
  import { execSync as execSync44 } from "child_process";
15329
- import { existsSync as existsSync47, mkdirSync as mkdirSync17, unlinkSync as unlinkSync15, writeFileSync as writeFileSync32 } from "fs";
15728
+ import { existsSync as existsSync49, mkdirSync as mkdirSync18, unlinkSync as unlinkSync15, writeFileSync as writeFileSync32 } from "fs";
15330
15729
  import { tmpdir as tmpdir7 } from "os";
15331
- import { join as join51, resolve as resolve13 } from "path";
15730
+ import { join as join55, resolve as resolve13 } from "path";
15332
15731
  import chalk144 from "chalk";
15333
15732
 
15334
15733
  // src/commands/screenshot/captureWindowPs1.ts
@@ -15458,14 +15857,14 @@ Write-Output $OutputPath
15458
15857
 
15459
15858
  // src/commands/screenshot/index.ts
15460
15859
  function buildOutputPath(outputDir, processName) {
15461
- if (!existsSync47(outputDir)) {
15462
- mkdirSync17(outputDir, { recursive: true });
15860
+ if (!existsSync49(outputDir)) {
15861
+ mkdirSync18(outputDir, { recursive: true });
15463
15862
  }
15464
15863
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15465
15864
  return resolve13(outputDir, `${processName}-${timestamp}.png`);
15466
15865
  }
15467
15866
  function runPowerShellScript(processName, outputPath) {
15468
- const scriptPath = join51(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
15867
+ const scriptPath = join55(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
15469
15868
  writeFileSync32(scriptPath, captureWindowPs1, "utf-8");
15470
15869
  try {
15471
15870
  execSync44(
@@ -15492,58 +15891,45 @@ function screenshot(processName) {
15492
15891
  }
15493
15892
 
15494
15893
  // src/commands/sessions/summarise/index.ts
15495
- import * as fs27 from "fs";
15894
+ import * as fs28 from "fs";
15496
15895
  import chalk145 from "chalk";
15497
15896
 
15498
15897
  // src/commands/sessions/summarise/shared.ts
15499
- import * as fs25 from "fs";
15898
+ import * as fs26 from "fs";
15500
15899
  function writeSummary(jsonlPath, summary) {
15501
- fs25.writeFileSync(summaryPathFor(jsonlPath), `${summary.trim()}
15900
+ fs26.writeFileSync(summaryPathFor(jsonlPath), `${summary.trim()}
15502
15901
  `, "utf8");
15503
15902
  }
15504
15903
  function hasSummary(jsonlPath) {
15505
- return fs25.existsSync(summaryPathFor(jsonlPath));
15904
+ return fs26.existsSync(summaryPathFor(jsonlPath));
15506
15905
  }
15507
15906
  function summaryPathFor(jsonlPath) {
15508
15907
  return jsonlPath.replace(/\.jsonl$/, ".summary");
15509
15908
  }
15510
15909
 
15511
15910
  // src/commands/sessions/summarise/summariseSession.ts
15512
- import { execFileSync as execFileSync6 } from "child_process";
15911
+ import { execFileSync as execFileSync7 } from "child_process";
15513
15912
 
15514
15913
  // src/commands/sessions/summarise/iterateUserMessages.ts
15515
- import * as fs26 from "fs";
15914
+ import * as fs27 from "fs";
15516
15915
  function* iterateUserMessages(filePath, maxBytes = 65536) {
15517
15916
  let content;
15518
15917
  try {
15519
- const fd = fs26.openSync(filePath, "r");
15918
+ const fd = fs27.openSync(filePath, "r");
15520
15919
  try {
15521
15920
  const buf = Buffer.alloc(maxBytes);
15522
- const bytesRead = fs26.readSync(fd, buf, 0, buf.length, 0);
15921
+ const bytesRead = fs27.readSync(fd, buf, 0, buf.length, 0);
15523
15922
  content = buf.toString("utf8", 0, bytesRead);
15524
15923
  } finally {
15525
- fs26.closeSync(fd);
15924
+ fs27.closeSync(fd);
15526
15925
  }
15527
15926
  } catch {
15528
15927
  return;
15529
15928
  }
15530
15929
  for (const line of content.split("\n")) {
15531
15930
  if (!line) continue;
15532
- let entry;
15533
- try {
15534
- entry = JSON.parse(line);
15535
- } catch {
15536
- continue;
15537
- }
15538
- if (entry.type !== "user") continue;
15539
- const msg = entry.message;
15540
- const c = msg?.content;
15541
- if (typeof c === "string") {
15542
- yield c;
15543
- } else if (Array.isArray(c)) {
15544
- const text = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
15545
- if (text) yield text;
15546
- }
15931
+ const entry = parseUserLine(line);
15932
+ if (entry) yield entry.text;
15547
15933
  }
15548
15934
  }
15549
15935
 
@@ -15599,7 +15985,7 @@ function summariseSession(jsonlPath) {
15599
15985
  }
15600
15986
  const prompt = buildPrompt2(firstMessage, backlogIds);
15601
15987
  try {
15602
- const output = execFileSync6("claude", ["-p", "--model", "haiku", prompt], {
15988
+ const output = execFileSync7("claude", ["-p", "--model", "haiku", prompt], {
15603
15989
  encoding: "utf8",
15604
15990
  timeout: 3e4,
15605
15991
  stdio: ["ignore", "pipe", "ignore"]
@@ -15630,7 +16016,7 @@ ${firstMessage}`);
15630
16016
  }
15631
16017
 
15632
16018
  // src/commands/sessions/summarise/index.ts
15633
- async function summarise3(options2) {
16019
+ async function summarise4(options2) {
15634
16020
  const files = await discoverSessionJsonlPaths();
15635
16021
  if (files.length === 0) {
15636
16022
  console.log(chalk145.yellow("No sessions found."));
@@ -15646,16 +16032,16 @@ async function summarise3(options2) {
15646
16032
  `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
15647
16033
  )
15648
16034
  );
15649
- const { succeeded, failed } = processSessions(toProcess);
16035
+ const { succeeded, failed: failed2 } = processSessions(toProcess);
15650
16036
  console.log(
15651
- chalk145.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk145.yellow(`, ${failed} skipped`) : "")
16037
+ chalk145.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk145.yellow(`, ${failed2} skipped`) : "")
15652
16038
  );
15653
16039
  }
15654
16040
  function selectCandidates(files, options2) {
15655
16041
  const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
15656
16042
  candidates.sort((a, b) => {
15657
16043
  try {
15658
- return fs27.statSync(b).mtimeMs - fs27.statSync(a).mtimeMs;
16044
+ return fs28.statSync(b).mtimeMs - fs28.statSync(a).mtimeMs;
15659
16045
  } catch {
15660
16046
  return 0;
15661
16047
  }
@@ -15665,7 +16051,7 @@ function selectCandidates(files, options2) {
15665
16051
  }
15666
16052
  function processSessions(files) {
15667
16053
  let succeeded = 0;
15668
- let failed = 0;
16054
+ let failed2 = 0;
15669
16055
  for (let i = 0; i < files.length; i++) {
15670
16056
  const file = files[i];
15671
16057
  process.stdout.write(chalk145.dim(` [${i + 1}/${files.length}] `));
@@ -15676,19 +16062,19 @@ function processSessions(files) {
15676
16062
  process.stdout.write(`${chalk145.green("\u2713")} ${summary}
15677
16063
  `);
15678
16064
  } else {
15679
- failed++;
16065
+ failed2++;
15680
16066
  process.stdout.write(` ${chalk145.yellow("skip")}
15681
16067
  `);
15682
16068
  }
15683
16069
  }
15684
- return { succeeded, failed };
16070
+ return { succeeded, failed: failed2 };
15685
16071
  }
15686
16072
 
15687
16073
  // src/commands/sessions/registerSessions.ts
15688
16074
  function registerSessions(program2) {
15689
16075
  const cmd = program2.command("sessions").description("Web dashboard for Claude Code sessions").action(() => web({ port: "3100" }));
15690
16076
  cmd.command("web").description("Start the sessions web dashboard").option("-p, --port <number>", "Port to listen on", "3100").action(web);
15691
- cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise3);
16077
+ cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise4);
15692
16078
  }
15693
16079
 
15694
16080
  // src/commands/statusLine.ts
@@ -15769,21 +16155,21 @@ async function statusLine() {
15769
16155
  }
15770
16156
 
15771
16157
  // src/commands/sync.ts
15772
- import * as fs30 from "fs";
16158
+ import * as fs31 from "fs";
15773
16159
  import * as os2 from "os";
15774
16160
  import * as path50 from "path";
15775
16161
  import { fileURLToPath as fileURLToPath7 } from "url";
15776
16162
 
15777
16163
  // src/commands/sync/syncClaudeMd.ts
15778
- import * as fs28 from "fs";
16164
+ import * as fs29 from "fs";
15779
16165
  import * as path48 from "path";
15780
16166
  import chalk148 from "chalk";
15781
16167
  async function syncClaudeMd(claudeDir, targetBase, options2) {
15782
16168
  const source = path48.join(claudeDir, "CLAUDE.md");
15783
16169
  const target = path48.join(targetBase, "CLAUDE.md");
15784
- const sourceContent = fs28.readFileSync(source, "utf-8");
15785
- if (fs28.existsSync(target)) {
15786
- const targetContent = fs28.readFileSync(target, "utf-8");
16170
+ const sourceContent = fs29.readFileSync(source, "utf-8");
16171
+ if (fs29.existsSync(target)) {
16172
+ const targetContent = fs29.readFileSync(target, "utf-8");
15787
16173
  if (sourceContent !== targetContent) {
15788
16174
  console.log(
15789
16175
  chalk148.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
@@ -15800,21 +16186,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
15800
16186
  }
15801
16187
  }
15802
16188
  }
15803
- fs28.copyFileSync(source, target);
16189
+ fs29.copyFileSync(source, target);
15804
16190
  console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
15805
16191
  }
15806
16192
 
15807
16193
  // src/commands/sync/syncSettings.ts
15808
- import * as fs29 from "fs";
16194
+ import * as fs30 from "fs";
15809
16195
  import * as path49 from "path";
15810
16196
  import chalk149 from "chalk";
15811
16197
  async function syncSettings(claudeDir, targetBase, options2) {
15812
16198
  const source = path49.join(claudeDir, "settings.json");
15813
16199
  const target = path49.join(targetBase, "settings.json");
15814
- const sourceContent = fs29.readFileSync(source, "utf-8");
16200
+ const sourceContent = fs30.readFileSync(source, "utf-8");
15815
16201
  const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
15816
- if (fs29.existsSync(target)) {
15817
- const targetContent = fs29.readFileSync(target, "utf-8");
16202
+ if (fs30.existsSync(target)) {
16203
+ const targetContent = fs30.readFileSync(target, "utf-8");
15818
16204
  const normalizedTarget = JSON.stringify(
15819
16205
  JSON.parse(targetContent),
15820
16206
  null,
@@ -15840,7 +16226,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
15840
16226
  }
15841
16227
  }
15842
16228
  }
15843
- fs29.writeFileSync(target, mergedContent);
16229
+ fs30.writeFileSync(target, mergedContent);
15844
16230
  console.log("Copied settings.json to ~/.claude/settings.json");
15845
16231
  }
15846
16232
 
@@ -15859,10 +16245,10 @@ async function sync(options2) {
15859
16245
  function syncCommands(claudeDir, targetBase) {
15860
16246
  const sourceDir = path50.join(claudeDir, "commands");
15861
16247
  const targetDir = path50.join(targetBase, "commands");
15862
- fs30.mkdirSync(targetDir, { recursive: true });
15863
- const files = fs30.readdirSync(sourceDir);
16248
+ fs31.mkdirSync(targetDir, { recursive: true });
16249
+ const files = fs31.readdirSync(sourceDir);
15864
16250
  for (const file of files) {
15865
- fs30.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
16251
+ fs31.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
15866
16252
  console.log(`Copied ${file} to ${targetDir}`);
15867
16253
  }
15868
16254
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
@@ -15930,6 +16316,7 @@ program.command("coverage").description("Print global statement coverage percent
15930
16316
  program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
15931
16317
  registerActivity(program);
15932
16318
  registerCliHook(program);
16319
+ registerHandover(program);
15933
16320
  registerJira(program);
15934
16321
  registerMermaid(program);
15935
16322
  registerPrs(program);