@plannotator/opencode 0.28.38 → 0.28.40

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/embedded.js CHANGED
@@ -338,10 +338,10 @@ function isLoopbackHostHeader(hostHeader, port, localName) {
338
338
  }
339
339
 
340
340
  // ../../packages/shared/inbox/connection.ts
341
- var INBOX_REPLY_TO_CHECK = "Before reply_to into a thread you have not read in this context window, call read_thread with no id and match the subject.", INBOX_REF_LINK_RULE = "A bare #123 links to this project's repository. For an issue or PR in another repo, write owner/repo#123 or its full URL (in a GitLab project, a GitHub one needs its URL).", INBOX_CHANNEL_RULE = "A decision in the Inbox stays there: when the person writes in the chat, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask.", INBOX_USE_RULE = "Use it for a decision only they can make (a :::question block; go on with other work), finished long work (a short summary, files in `attachments`), or when they ask.", INBOX_FOLLOW_THROUGH_RULE = "Be proactive about Inbox updates and about replying to the person through the Inbox when you think they would want you to. Don't withhold important updates or decisions, and don't batch updates to send later: proactivity is better than procrastination.", INBOX_SUBAGENT_RULE = "If another agent started you, report to it instead.", INBOX_AGENT_RULES, INBOX_TOOL_LEAD, INBOX_AGENT_FIELD_DESCRIPTIONS, INBOX_REMINDER_TEXT, INBOX_REMINDER_TEXT_NO_WAKE, INBOX_BRIEFING_NEVER_RUN = "Never run `plannotator inbox` just to send: send_message starts a stopped Inbox itself, and bare `plannotator inbox` opens the window for the person.", INBOX_BRIEFING_FROM_NOW_ON = "For the rest of this session, use the Inbox as above: for a decision the person is not here to make, and to hand over long work you finished.", INBOX_COMMAND_BRIEFING, INBOX_MAX_REQUEST_BYTES;
341
+ var INBOX_REPLY_TO_CHECK = "Before reply_to into a thread you have not read in this context window, call read_thread with no id and match the subject.", INBOX_REF_LINK_RULE = "A bare #123 links to this project's repository. For an issue or PR in another repo, write owner/repo#123 or its full URL (in a GitLab project, a GitHub one needs its URL).", INBOX_CHANNEL_RULE = "A decision in the Inbox stays there: when the person writes in the chat, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask.", INBOX_USE_RULE = "Use it for a decision only they can make (a :::question block; go on with other work), finished long work (a short summary, files in `attachments`), or when they ask.", INBOX_THREAD_RULE = "One thread per work stream, unless the person says otherwise.", INBOX_FOLLOW_THROUGH_RULE = "Be proactive about Inbox updates and about replying to the person through the Inbox when you think they would want you to. Don't withhold important updates or decisions, and don't batch updates to send later: proactivity is better than procrastination.", INBOX_SUBAGENT_RULE = "If another agent started you, report to it instead.", INBOX_AGENT_RULES, INBOX_TOOL_LEAD, INBOX_AGENT_FIELD_DESCRIPTIONS, INBOX_REMINDER_TEXT, INBOX_REMINDER_TEXT_NO_WAKE, INBOX_BRIEFING_NEVER_RUN = "Never run `plannotator inbox` just to send: send_message starts a stopped Inbox itself, and bare `plannotator inbox` opens the window for the person.", INBOX_BRIEFING_FROM_NOW_ON = "For the rest of this session, use the Inbox as above: for a decision the person is not here to make, and to hand over long work you finished.", INBOX_COMMAND_BRIEFING, INBOX_MAX_REQUEST_BYTES;
342
342
  var init_connection = __esm(() => {
343
- INBOX_AGENT_RULES = `${INBOX_USE_RULE} ${INBOX_CHANNEL_RULE} ${INBOX_FOLLOW_THROUGH_RULE} ${INBOX_SUBAGENT_RULE}`;
344
- INBOX_TOOL_LEAD = `The Plannotator Inbox on this machine: message the person and get their answer later, without blocking this session. ${INBOX_AGENT_RULES} Set \`action\` to a tool below, with its fields.`;
343
+ INBOX_AGENT_RULES = `${INBOX_USE_RULE} ${INBOX_THREAD_RULE} ${INBOX_CHANNEL_RULE} ${INBOX_FOLLOW_THROUGH_RULE} ${INBOX_SUBAGENT_RULE}`;
344
+ INBOX_TOOL_LEAD = `The Plannotator Inbox on this machine: message the person, get their answer later; nothing blocks. ${INBOX_AGENT_RULES} Set \`action\` to a tool below, with its fields.`;
345
345
  INBOX_AGENT_FIELD_DESCRIPTIONS = {
346
346
  send_message: {
347
347
  body: [
@@ -384,12 +384,13 @@ var init_connection = __esm(() => {
384
384
  snapshot: "Instead of guide and patch: a complete Plannotator guide snapshot, as an object or its JSON."
385
385
  }
386
386
  };
387
- INBOX_REMINDER_TEXT = `Plannotator Inbox is on: when you finish long work, or need a decision only the person can make, send it with plannotator_inbox send_message and keep going. ${INBOX_FOLLOW_THROUGH_RULE} Their reply comes back here. A decision in the Inbox stays there: when the person writes here, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Run \`plannotator inbox --help\` for how it works.`;
388
- INBOX_REMINDER_TEXT_NO_WAKE = `Plannotator Inbox is on: when you finish long work, or need a decision only the person can make, send it with plannotator_inbox send_message and keep going. ${INBOX_FOLLOW_THROUGH_RULE} Call wait_for_reply when you need their answer. A decision in the Inbox stays there: when the person writes here, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Run \`plannotator inbox --help\` for how it works.`;
387
+ INBOX_REMINDER_TEXT = `Plannotator Inbox is on: when you finish long work, or need a decision only the person can make, send it with plannotator_inbox send_message and keep going. ${INBOX_FOLLOW_THROUGH_RULE} ${INBOX_THREAD_RULE} Their reply comes back here. A decision in the Inbox stays there: when the person writes here, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Run \`plannotator inbox --help\` for how it works.`;
388
+ INBOX_REMINDER_TEXT_NO_WAKE = `Plannotator Inbox is on: when you finish long work, or need a decision only the person can make, send it with plannotator_inbox send_message and keep going. ${INBOX_FOLLOW_THROUGH_RULE} ${INBOX_THREAD_RULE} Call wait_for_reply when you need their answer. A decision in the Inbox stays there: when the person writes here, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Run \`plannotator inbox --help\` for how it works.`;
389
389
  INBOX_COMMAND_BRIEFING = [
390
390
  "The person opened their Plannotator Inbox with /plannotator-inbox and has its address. The Plannotator Inbox is one window on this computer where you leave the person messages, `:::question` blocks they answer with a click, and files. They answer when they can. Nothing blocks: you send, then keep working or end your turn.",
391
391
  "",
392
392
  `- ${INBOX_USE_RULE}`,
393
+ `- ${INBOX_THREAD_RULE}`,
393
394
  "- Send with a `plannotator_inbox` tool when you have one (it can be listed with a prefix and may need loading through tool search first): `action` `send_message`, with a markdown `body`. Otherwise call `send_message` on the Inbox's MCP server, `plannotator inbox mcp`.",
394
395
  "- send_message's result says whether the person's reply arrives in this session by itself. When it does, do not wait or poll for it: end your turn or go on with other work. When it does not, call wait_for_reply when you need the answer.",
395
396
  `- ${INBOX_CHANNEL_RULE}`,
@@ -5175,7 +5176,7 @@ import {
5175
5176
  writeFileSync as writeFileSync5,
5176
5177
  appendFileSync as appendFileSync2
5177
5178
  } from "fs";
5178
- import { homedir as homedir3, hostname } from "os";
5179
+ import { homedir as homedir3, hostname, uptime } from "os";
5179
5180
  import { join as join9, posix, win32 as win322 } from "path";
5180
5181
  function autoUpdatePaths(dataDir = getPlannotatorDataDir()) {
5181
5182
  return {
@@ -5289,25 +5290,39 @@ function thisLockMachine() {
5289
5290
  } catch {}
5290
5291
  return { host: hostname(), boot: readTrimmed("/proc/sys/kernel/random/boot_id"), pidns };
5291
5292
  }
5292
- function lockHolderGone(content, mtimeMs, now = Date.now(), machine = thisLockMachine()) {
5293
- if (process.platform === "win32")
5294
- return false;
5295
- if (now - mtimeMs < UPDATE_LOCK_HANDOFF_MS)
5296
- return false;
5293
+ function judgeLockHolder(content) {
5297
5294
  const holder = parseUpdateLockHolder(content);
5298
5295
  if (!holder || !holder.host)
5299
- return false;
5296
+ return "unknown";
5297
+ const machine = thisLockMachine();
5300
5298
  if (holder.host.toLowerCase() !== machine.host.toLowerCase())
5301
- return false;
5299
+ return "unknown";
5302
5300
  if (holder.boot !== machine.boot || holder.pidns !== machine.pidns)
5303
- return false;
5301
+ return "unknown";
5304
5302
  try {
5305
- process.kill(-holder.group, 0);
5306
- return false;
5303
+ process.kill(process.platform === "win32" ? holder.pid : -holder.group, 0);
5304
+ return "alive";
5307
5305
  } catch (err) {
5308
- return err?.code === "ESRCH";
5306
+ const code = err?.code;
5307
+ return code === "ESRCH" ? "gone" : code === "EPERM" ? "alive" : "unknown";
5309
5308
  }
5310
5309
  }
5310
+ function lockHolderGone(content, mtimeMs, now = Date.now()) {
5311
+ if (now - mtimeMs < UPDATE_LOCK_HANDOFF_MS)
5312
+ return false;
5313
+ return judgeLockHolder(content) === "gone";
5314
+ }
5315
+ function lockHeld(lock, now = Date.now()) {
5316
+ if (lockHolderGone(lock.content, lock.mtimeMs, now))
5317
+ return false;
5318
+ if (!lockExpired(lock.mtimeMs, now))
5319
+ return true;
5320
+ if (process.platform === "win32" || Math.abs(now - lock.mtimeMs) >= AUTO_UPDATE_LOCK_LIVE_MAX_MS)
5321
+ return false;
5322
+ if (!parseUpdateLockHolder(lock.content)?.boot && lock.mtimeMs <= now - uptime() * 1000)
5323
+ return false;
5324
+ return judgeLockHolder(lock.content) === "alive";
5325
+ }
5311
5326
  function removeJudgedLock(lockPath, judged) {
5312
5327
  const aside = `${lockPath}.reaped.${process.pid}.${Math.random().toString(36).slice(2)}`;
5313
5328
  try {
@@ -5341,7 +5356,7 @@ function acquireUpdateLock(lockPath) {
5341
5356
  try {
5342
5357
  const lock = readLock(lockPath);
5343
5358
  if (lock) {
5344
- if (!lockExpired(lock.mtimeMs) && !lockHolderGone(lock.content, lock.mtimeMs))
5359
+ if (lockHeld(lock))
5345
5360
  return false;
5346
5361
  if (!removeJudgedLock(lockPath, lock))
5347
5362
  return false;
@@ -5356,7 +5371,7 @@ function acquireUpdateLock(lockPath) {
5356
5371
  }
5357
5372
  function isUpdateLockHeld(dataDir) {
5358
5373
  const lock = readLock(autoUpdatePaths(dataDir).lock);
5359
- return !!lock && !lockExpired(lock.mtimeMs) && !lockHolderGone(lock.content, lock.mtimeMs);
5374
+ return !!lock && lockHeld(lock);
5360
5375
  }
5361
5376
  function abortLaunch(version, err, dataDir) {
5362
5377
  const paths = autoUpdatePaths(dataDir);
@@ -5474,6 +5489,8 @@ function installerEnv(launch) {
5474
5489
  PLANNOTATOR_UPDATE_SCRIPT_URL: installScriptUrl(launch.platform),
5475
5490
  PLANNOTATOR_UPDATE_BINARY: prepare.binary,
5476
5491
  PLANNOTATOR_UPDATE_ENTRY: prepare.entry,
5492
+ GIT_HTTP_LOW_SPEED_LIMIT: process.env.GIT_HTTP_LOW_SPEED_LIMIT || "1",
5493
+ GIT_HTTP_LOW_SPEED_TIME: process.env.GIT_HTTP_LOW_SPEED_TIME || "60",
5477
5494
  ...commandEnv
5478
5495
  }
5479
5496
  };
@@ -5488,7 +5505,17 @@ function launchInstaller(launch) {
5488
5505
  env: { ...env, PLANNOTATOR_UPDATE_DETACHED: "1" },
5489
5506
  windowsHide: true
5490
5507
  });
5491
- child.on("error", () => {});
5508
+ child.on("error", (err) => {
5509
+ if (child.pid === undefined)
5510
+ abortLaunch(launch.version, err, join9(launch.logPath, ".."));
5511
+ });
5512
+ if (child.pid !== undefined) {
5513
+ const m = thisLockMachine();
5514
+ try {
5515
+ writeFileSync5(launch.lockPath, `pid=${child.pid} group=${child.pid} host=${m.host} boot=${m.boot} pidns=${m.pidns}
5516
+ `);
5517
+ } catch {}
5518
+ }
5492
5519
  child.unref();
5493
5520
  } finally {
5494
5521
  closeSync2(logFd);
@@ -5602,15 +5629,28 @@ function handleUpdateRoute(req, url, ctx) {
5602
5629
  return Response.json({ error: refused[result.outcome] ?? "The update could not start.", code: `update_${result.outcome.replace(/-/g, "_")}` }, { status: 409 });
5603
5630
  })();
5604
5631
  }
5605
- var AUTO_UPDATE_INTERVAL_MS, UPDATE_PREPARE_FAILED, UPDATE_CHECKSUM_MISMATCH, AUTO_UPDATE_LOCK_STALE_MS, UPDATE_LOCK_HANDOFF_MS = 5000, POSIX_WRAPPER = `
5632
+ var AUTO_UPDATE_INTERVAL_MS, UPDATE_PREPARE_FAILED, UPDATE_CHECKSUM_MISMATCH, AUTO_UPDATE_LOCK_STALE_MS, AUTO_UPDATE_LOCK_LIVE_MAX_MS, UPDATE_LOCK_HANDOFF_MS = 5000, POSIX_WRAPPER = `
5606
5633
  # Detached (auto-update, Update now): the lock names this wrapper's process
5607
5634
  # group (it leads it, so the installer and its children are in it) and this
5608
5635
  # machine, so an install that is gone shows as failed at once instead of
5609
5636
  # "Updating\u2026" for the lock's hour. The foreground \`plannotator update\` keeps
5610
5637
  # the lock its own process wrote.
5638
+ own_lock=""
5611
5639
  if [ "\${PLANNOTATOR_UPDATE_DETACHED:-}" = 1 ]; then
5612
5640
  printf 'pid=%s group=%s host=%s boot=%s pidns=%s\\n' "$$" "$$" "$(uname -n 2>/dev/null)" "$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)" "$(readlink /proc/$$/ns/pid 2>/dev/null)" > "$PLANNOTATOR_UPDATE_LOCK"
5641
+ else
5642
+ own_lock=$(cat "$PLANNOTATOR_UPDATE_LOCK" 2>/dev/null)
5613
5643
  fi
5644
+ # Remove the lock only while it is still this run's: its line names this
5645
+ # wrapper, or it is what the lock held when this wrapper started. Never a lock
5646
+ # another install took.
5647
+ release_lock() {
5648
+ lock_now=$(cat "$PLANNOTATOR_UPDATE_LOCK" 2>/dev/null)
5649
+ case "$lock_now" in
5650
+ "pid=$$ "*) rm -f "$PLANNOTATOR_UPDATE_LOCK" ;;
5651
+ *) if [ -n "$own_lock" ] && [ "$lock_now" = "$own_lock" ]; then rm -f "$PLANNOTATOR_UPDATE_LOCK"; fi ;;
5652
+ esac
5653
+ }
5614
5654
  update_dir=""
5615
5655
  code=1
5616
5656
  cleanup() {
@@ -5626,7 +5666,7 @@ on_signal() {
5626
5666
  cleanup
5627
5667
  record 130
5628
5668
  echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] update: interrupted"
5629
- rm -f "$PLANNOTATOR_UPDATE_LOCK"
5669
+ release_lock
5630
5670
  exit 130
5631
5671
  }
5632
5672
  trap on_signal INT TERM HUP
@@ -5669,7 +5709,7 @@ cleanup
5669
5709
  # part of update.log (deriveAutoUpdateNotice tells "prepare" from "download" there).
5670
5710
  echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] update: install.sh exited $code"
5671
5711
  record "$code"
5672
- rm -f "$PLANNOTATOR_UPDATE_LOCK"
5712
+ release_lock
5673
5713
  `, POWERSHELL_ALLOWED_FLAGS, POWERSHELL_WRAPPER, noticeVersion;
5674
5714
  var init_auto_update = __esm(() => {
5675
5715
  init_update_log();
@@ -5683,6 +5723,7 @@ var init_auto_update = __esm(() => {
5683
5723
  UPDATE_PREPARE_FAILED = UPDATE_EXIT_DOWNLOAD_FAILED;
5684
5724
  UPDATE_CHECKSUM_MISMATCH = UPDATE_EXIT_CHECKSUM_MISMATCH;
5685
5725
  AUTO_UPDATE_LOCK_STALE_MS = 60 * 60 * 1000;
5726
+ AUTO_UPDATE_LOCK_LIVE_MAX_MS = 24 * 60 * 60 * 1000;
5686
5727
  POWERSHELL_ALLOWED_FLAGS = Object.values(INSTALL_FLAG_SPELLINGS).map((s) => `'${s.powershell}'`).join(",");
5687
5728
  POWERSHELL_WRAPPER = `
5688
5729
  # 130 until the installer finished: a Ctrl-C stops the pipeline, and the
@@ -5695,6 +5736,10 @@ if ($env:PLANNOTATOR_UPDATE_FLAGS) {
5695
5736
  if ($allowedFlags -ccontains $f) { $installFlags += $f }
5696
5737
  }
5697
5738
  }
5739
+ # The lock is removed at the end only while it is still this run's: a line
5740
+ # naming this process (launchInstaller writes it), or what it held when this
5741
+ # run started. Never a lock another install took.
5742
+ $ownLock = [string](Get-Content -Raw -ErrorAction SilentlyContinue -LiteralPath $env:PLANNOTATOR_UPDATE_LOCK)
5698
5743
  $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("plannotator-install-" + [guid]::NewGuid().ToString('N') + ".ps1")
5699
5744
  try {
5700
5745
  Invoke-WebRequest -UseBasicParsing -TimeoutSec 300 -Uri $env:PLANNOTATOR_UPDATE_SCRIPT_URL -OutFile $tmp
@@ -5707,7 +5752,10 @@ try {
5707
5752
  Remove-Item -Force -ErrorAction SilentlyContinue $tmp
5708
5753
  [System.IO.File]::WriteAllText($env:PLANNOTATOR_UPDATE_RESULT, ('{"version":"' + $env:PLANNOTATOR_UPDATE_VERSION + '","exitCode":' + [int]$code + '}'))
5709
5754
  Write-Output ("[" + (Get-Date).ToUniversalTime().ToString('o') + "] update: install.ps1 exited " + $code)
5710
- Remove-Item -Force -ErrorAction SilentlyContinue $env:PLANNOTATOR_UPDATE_LOCK
5755
+ $lockNow = [string](Get-Content -Raw -ErrorAction SilentlyContinue -LiteralPath $env:PLANNOTATOR_UPDATE_LOCK)
5756
+ if ($lockNow.StartsWith("pid=$PID ") -or ($ownLock -ne '' -and $lockNow -ceq $ownLock)) {
5757
+ Remove-Item -Force -ErrorAction SilentlyContinue -LiteralPath $env:PLANNOTATOR_UPDATE_LOCK
5758
+ }
5711
5759
  }
5712
5760
  `;
5713
5761
  });
@@ -51860,6 +51908,127 @@ var init_command_path = __esm(() => {
51860
51908
  WINDOWS_SHELL_EXTENSIONS = new Set([".cmd", ".bat"]);
51861
51909
  });
51862
51910
 
51911
+ // ../../packages/ai/providers/ask-ai-env.ts
51912
+ import { existsSync as existsSync15, readFileSync as readFileSync15, statSync as statSync14 } from "fs";
51913
+ import { homedir as homedir5 } from "os";
51914
+ import { dirname as dirname8, join as join22, resolve as resolve10 } from "path";
51915
+ function askAiChildEnv(env = process.env) {
51916
+ return { ...env, ...ASK_AI_CHILD_SWITCHES };
51917
+ }
51918
+ function withAskAiChildEnv(start) {
51919
+ const saved = Object.keys(ASK_AI_CHILD_SWITCHES).map((key) => [key, process.env[key]]);
51920
+ Object.assign(process.env, ASK_AI_CHILD_SWITCHES);
51921
+ try {
51922
+ return start();
51923
+ } finally {
51924
+ for (const [key, value] of saved) {
51925
+ if (value === undefined)
51926
+ delete process.env[key];
51927
+ else
51928
+ process.env[key] = value;
51929
+ }
51930
+ }
51931
+ }
51932
+ function codexAskAiConfigArgs(configTexts) {
51933
+ const texts = configTexts === null ? [] : typeof configTexts === "string" ? [configTexts] : configTexts;
51934
+ if (texts.some((text) => codexInboxEntryHasUrl(text))) {
51935
+ return ["-c", `mcp_servers.${CODEX_INBOX_SERVER}.enabled=false`];
51936
+ }
51937
+ return [
51938
+ "-c",
51939
+ `mcp_servers.${CODEX_INBOX_SERVER}={command='plannotator',args=['inbox','mcp'],env={PLANNOTATOR_ASK_AI_CHILD='1'},enabled=false}`
51940
+ ];
51941
+ }
51942
+ function readCodexConfigTexts(cwd, env = process.env) {
51943
+ const start = resolve10(cwd);
51944
+ const home = env.CODEX_HOME?.trim();
51945
+ const codexHome = home ? resolve10(start, home) : join22(homedir5(), ".codex");
51946
+ const dirs = [];
51947
+ for (let dir = start;; dir = dirname8(dir)) {
51948
+ dirs.push(dir);
51949
+ if (isGitRoot(dir))
51950
+ break;
51951
+ if (dirname8(dir) === dir) {
51952
+ dirs.length = 1;
51953
+ break;
51954
+ }
51955
+ }
51956
+ const folders = [codexHome, ...dirs.map((dir) => join22(dir, ".codex")).filter((dot) => dot !== codexHome)];
51957
+ return folders.flatMap((folder) => {
51958
+ try {
51959
+ return [readFileSync15(join22(folder, "config.toml"), "utf8")];
51960
+ } catch {
51961
+ return [];
51962
+ }
51963
+ });
51964
+ }
51965
+ function isGitRoot(dir) {
51966
+ try {
51967
+ const git = statSync14(join22(dir, ".git"));
51968
+ return git.isFile() || git.isDirectory() && existsSync15(join22(dir, ".git", "HEAD"));
51969
+ } catch {
51970
+ return false;
51971
+ }
51972
+ }
51973
+ function tomlKeyPath(text) {
51974
+ const part = /\s*(?:"((?:[^"\\]|\\.)*)"|'([^']*)'|([A-Za-z0-9_-]+))\s*(\.|$)/y;
51975
+ const parts = [];
51976
+ let at = 0;
51977
+ while (at < text.length) {
51978
+ part.lastIndex = at;
51979
+ const match = part.exec(text);
51980
+ if (!match)
51981
+ return null;
51982
+ parts.push(match[1] ?? match[2] ?? match[3]);
51983
+ at = part.lastIndex;
51984
+ if (match[4] !== ".")
51985
+ break;
51986
+ }
51987
+ return parts.length > 0 && at >= text.length ? parts : null;
51988
+ }
51989
+ function inlineTableKeys(value) {
51990
+ let inner = value.replace(/"(?:[^"\\]|\\.)*"|'[^']*'/g, '""').trim();
51991
+ if (!inner.startsWith("{"))
51992
+ return [];
51993
+ const close = inner.lastIndexOf("}");
51994
+ inner = inner.slice(1, close < 0 ? undefined : close);
51995
+ const nested = /[{[][^{}[\]]*[}\]]/;
51996
+ while (nested.test(inner))
51997
+ inner = inner.replace(new RegExp(nested.source, "g"), "");
51998
+ return inner.split(",").map((member) => tomlKeyPath(member.slice(0, Math.max(0, member.indexOf("="))))?.[0]).filter((key) => key !== undefined);
51999
+ }
52000
+ function codexInboxEntryHasUrl(configText) {
52001
+ let table = [];
52002
+ for (const raw of configText.split(/\r?\n/)) {
52003
+ const line = raw.trim();
52004
+ if (!line || line.startsWith("#"))
52005
+ continue;
52006
+ const header = /^\[\[?([^\]]*)\]\]?\s*(?:#.*)?$/.exec(line);
52007
+ if (header) {
52008
+ table = tomlKeyPath(header[1]) ?? [""];
52009
+ continue;
52010
+ }
52011
+ const eq = line.indexOf("=");
52012
+ const key = eq > 0 ? tomlKeyPath(line.slice(0, eq)) : null;
52013
+ if (!key)
52014
+ continue;
52015
+ const path = [...table, ...key];
52016
+ if (path[0] !== "mcp_servers" || path[1] !== CODEX_INBOX_SERVER)
52017
+ continue;
52018
+ if (path.length > 2 ? path[2] === "url" : inlineTableKeys(line.slice(eq + 1)).includes("url"))
52019
+ return true;
52020
+ }
52021
+ return false;
52022
+ }
52023
+ var ASK_AI_CHILD_ENV = "PLANNOTATOR_ASK_AI_CHILD", ASK_AI_CHILD_SWITCHES, CODEX_INBOX_SERVER = "plannotator-inbox";
52024
+ var init_ask_ai_env = __esm(() => {
52025
+ ASK_AI_CHILD_SWITCHES = {
52026
+ PLANNOTATOR_INBOX_TOOL: "0",
52027
+ PLANNOTATOR_AGENT_TOOL: "0",
52028
+ [ASK_AI_CHILD_ENV]: "1"
52029
+ };
52030
+ });
52031
+
51863
52032
  // ../../node_modules/.bun/@anthropic-ai+claude-agent-sdk@0.3.303+cc97d28afeb89aea/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
51864
52033
  import { createRequire as EJ } from "module";
51865
52034
  import { createHash as fv, randomBytes as RJ, randomUUID as Sa } from "crypto";
@@ -82191,6 +82360,7 @@ class ClaudeAgentSDKProvider {
82191
82360
  persistSession: false,
82192
82361
  settingSources: [],
82193
82362
  strictMcpConfig: true,
82363
+ env: askAiChildEnv(),
82194
82364
  ...this.config.claudeExecutablePath && {
82195
82365
  pathToClaudeCodeExecutable: this.config.claudeExecutablePath
82196
82366
  }
@@ -82332,11 +82502,12 @@ function mapSDKMessage(msg) {
82332
82502
  return [{ type: "unknown", raw: msg }];
82333
82503
  }
82334
82504
  }
82335
- var PROVIDER_NAME = "claude-agent-sdk", DEFAULT_ALLOWED_TOOLS, DEFAULT_MAX_TURNS = 99, DEFAULT_MODEL = "sonnet", MODEL_DISCOVERY_TIMEOUT_MS = 1e4, sdkQueryFn = null, ClaudeAgentSDKSession;
82505
+ var PROVIDER_NAME = "claude-agent-sdk", DEFAULT_ALLOWED_TOOLS, DEFAULT_MAX_TURNS = 99, DEFAULT_MODEL = "sonnet", MODEL_DISCOVERY_TIMEOUT_MS = 1e4, ASK_AI_DISALLOWED_TOOLS, sdkQueryFn = null, ClaudeAgentSDKSession;
82336
82506
  var init_claude_agent_sdk = __esm(() => {
82337
82507
  init_context();
82338
82508
  init_base_session();
82339
82509
  init_model_catalog();
82510
+ init_ask_ai_env();
82340
82511
  init_provider();
82341
82512
  DEFAULT_ALLOWED_TOOLS = [
82342
82513
  "Read",
@@ -82351,6 +82522,7 @@ var init_claude_agent_sdk = __esm(() => {
82351
82522
  "Bash(git merge-base:*)",
82352
82523
  "Bash(git ls-files:*)"
82353
82524
  ];
82525
+ ASK_AI_DISALLOWED_TOOLS = ["mcp__plannotator-inbox"];
82354
82526
  ClaudeAgentSDKSession = class ClaudeAgentSDKSession extends BaseSession {
82355
82527
  config;
82356
82528
  _activeQuery = null;
@@ -82414,10 +82586,12 @@ var init_claude_agent_sdk = __esm(() => {
82414
82586
  ...this.config.effort && { effort: this.config.effort },
82415
82587
  maxTurns: this.config.maxTurns,
82416
82588
  allowedTools: this.config.allowedTools,
82589
+ disallowedTools: ASK_AI_DISALLOWED_TOOLS,
82417
82590
  cwd: this.config.cwd,
82418
82591
  abortController: this._currentAbort,
82419
82592
  includePartialMessages: true,
82420
82593
  persistSession: true,
82594
+ env: askAiChildEnv(),
82421
82595
  ...this.config.claudeExecutablePath && {
82422
82596
  pathToClaudeCodeExecutable: this.config.claudeExecutablePath
82423
82597
  },
@@ -82668,14 +82842,15 @@ class CodexAppServerProcess {
82668
82842
  }
82669
82843
  async doStart(codexPath, cwd, initTimeoutMs) {
82670
82844
  const commandPath = resolveWindowsCommandShim(codexPath);
82671
- const command = buildWindowsCommandScriptSpawnCommand(commandPath, ["app-server"]) ?? [
82845
+ const appServerArgs = [...codexAskAiConfigArgs(readCodexConfigTexts(cwd)), "app-server"];
82846
+ const command = buildWindowsCommandScriptSpawnCommand(commandPath, appServerArgs) ?? [
82672
82847
  commandPath,
82673
- "app-server"
82848
+ ...appServerArgs
82674
82849
  ];
82675
82850
  let proc;
82676
82851
  try {
82677
82852
  const [file, ...args] = command;
82678
- proc = spawn4(file, args, { cwd, stdio: ["pipe", "pipe", "ignore"] });
82853
+ proc = spawn4(file, args, { cwd, env: askAiChildEnv(), stdio: ["pipe", "pipe", "ignore"] });
82679
82854
  } catch (err) {
82680
82855
  const error = err instanceof Error ? err : new Error(String(err));
82681
82856
  this.handleProcessEnd(error);
@@ -82951,7 +83126,9 @@ var init_codex_app_server = __esm(() => {
82951
83126
  init_context();
82952
83127
  init_provider();
82953
83128
  init_command_path();
83129
+ init_ask_ai_env();
82954
83130
  init_model_catalog();
83131
+ init_ask_ai_env();
82955
83132
  IDLE_TIMEOUT_MS = 10 * 60000;
82956
83133
  CodexAppServerSession = class CodexAppServerSession extends BaseSession {
82957
83134
  config;
@@ -83348,7 +83525,7 @@ class PiProcess {
83348
83525
  try {
83349
83526
  this.proc = Bun.spawn(command, {
83350
83527
  cwd,
83351
- env: { ...process.env },
83528
+ env: askAiChildEnv(),
83352
83529
  stdin: "pipe",
83353
83530
  stdout: "pipe",
83354
83531
  stderr: "ignore"
@@ -83557,6 +83734,7 @@ var init_pi_sdk = __esm(() => {
83557
83734
  init_model_catalog();
83558
83735
  init_command_path();
83559
83736
  init_pi_version();
83737
+ init_ask_ai_env();
83560
83738
  init_provider();
83561
83739
  PiSDKSession = class PiSDKSession extends BaseSession {
83562
83740
  config;
@@ -85774,11 +85952,11 @@ class OpenCodeProvider {
85774
85952
  const { createOpencodeServer, createOpencodeClient } = await getSDK();
85775
85953
  let server;
85776
85954
  try {
85777
- server = await createOpencodeServer({
85955
+ server = await withAskAiChildEnv(() => createOpencodeServer({
85778
85956
  hostname: this.config.hostname ?? "127.0.0.1",
85779
85957
  port: this.config.port ?? 0,
85780
85958
  timeout: 15000
85781
- });
85959
+ }));
85782
85960
  } catch (err) {
85783
85961
  if (err?.code === "ENOENT") {
85784
85962
  throw new Error("OpenCode CLI (`opencode`) was not found on PATH. Install it (https://opencode.ai) or choose a different AI provider.", { cause: err });
@@ -86115,6 +86293,7 @@ var PROVIDER_NAME4 = "opencode-sdk", sdk = null, OpenCodeSession, OPENCODE_FIRST
86115
86293
  var init_opencode_sdk = __esm(() => {
86116
86294
  init_base_session();
86117
86295
  init_context();
86296
+ init_ask_ai_env();
86118
86297
  init_provider();
86119
86298
  OpenCodeSession = class OpenCodeSession extends BaseSession {
86120
86299
  config;
@@ -86332,18 +86511,18 @@ var QWEN_ASK_AI_SESSION_PREFIX = "a5ca1000";
86332
86511
  // ../../packages/ai/providers/qwen-sdk.ts
86333
86512
  import { spawn as spawn5 } from "child_process";
86334
86513
  import { readFile as readFile2 } from "fs/promises";
86335
- import { homedir as homedir5 } from "os";
86336
- import { join as join23, resolve as resolve11 } from "path";
86337
- function qwenHomeDir(env = process.env, home = homedir5(), cwd = process.cwd()) {
86514
+ import { homedir as homedir6 } from "os";
86515
+ import { join as join24, resolve as resolve12 } from "path";
86516
+ function qwenHomeDir(env = process.env, home = homedir6(), cwd = process.cwd()) {
86338
86517
  const configured = env.QWEN_HOME?.trim();
86339
86518
  if (!configured)
86340
- return join23(home, ".qwen");
86519
+ return join24(home, ".qwen");
86341
86520
  if (configured === "~")
86342
86521
  return home;
86343
86522
  if (configured.startsWith("~/") || configured.startsWith("~\\")) {
86344
- return join23(home, configured.slice(2));
86523
+ return join24(home, configured.slice(2));
86345
86524
  }
86346
- return resolve11(cwd, configured);
86525
+ return resolve12(cwd, configured);
86347
86526
  }
86348
86527
  function stripJsonComments(text) {
86349
86528
  let out = "";
@@ -86376,7 +86555,7 @@ function stripJsonComments(text) {
86376
86555
  }
86377
86556
  async function readQwenCatalog(qwenHome) {
86378
86557
  try {
86379
- const text = (await readFile2(join23(qwenHome, "settings.json"), "utf8")).replace(/^\uFEFF/, "");
86558
+ const text = (await readFile2(join24(qwenHome, "settings.json"), "utf8")).replace(/^\uFEFF/, "");
86380
86559
  return qwenCatalogFromSettings(JSON.parse(stripJsonComments(text)));
86381
86560
  } catch {
86382
86561
  return [];
@@ -86422,7 +86601,7 @@ class QwenProcess {
86422
86601
  try {
86423
86602
  proc = spawn5(file, rest, {
86424
86603
  cwd,
86425
- env: { ...process.env },
86604
+ env: askAiChildEnv(),
86426
86605
  stdio: ["pipe", "pipe", "pipe"],
86427
86606
  windowsHide: true
86428
86607
  });
@@ -86567,7 +86746,7 @@ class QwenSDKProvider {
86567
86746
  }
86568
86747
  async fetchModels() {
86569
86748
  const cwd = this.config.cwd ?? process.cwd();
86570
- const models = await readQwenCatalog(qwenHomeDir(process.env, homedir5(), cwd));
86749
+ const models = await readQwenCatalog(qwenHomeDir(process.env, homedir6(), cwd));
86571
86750
  if (models.length > 0)
86572
86751
  this.models = models;
86573
86752
  }
@@ -86579,6 +86758,7 @@ var init_qwen_sdk = __esm(() => {
86579
86758
  init_context();
86580
86759
  init_provider();
86581
86760
  init_command_path();
86761
+ init_ask_ai_env();
86582
86762
  QWEN_EXCLUDED_TOOLS = [
86583
86763
  "exit_plan_mode",
86584
86764
  "edit",
@@ -88857,8 +89037,8 @@ var init_review_core = __esm(() => {
88857
89037
 
88858
89038
  // ../../packages/shared/review-progress.ts
88859
89039
  import { createHash as createHash3, randomUUID } from "crypto";
88860
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync16, realpathSync as realpathSync8, renameSync as renameSync8, unlinkSync as unlinkSync8, writeFileSync as writeFileSync10 } from "fs";
88861
- import { join as join24, resolve as resolve12 } from "path";
89040
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync17, realpathSync as realpathSync8, renameSync as renameSync8, unlinkSync as unlinkSync8, writeFileSync as writeFileSync10 } from "fs";
89041
+ import { join as join25, resolve as resolve13 } from "path";
88862
89042
  function reviewFileFingerprints(patch, identities) {
88863
89043
  const result = Object.create(null);
88864
89044
  for (const file of parseDiffToFiles(patch)) {
@@ -88920,11 +89100,11 @@ function canonicalPath(path) {
88920
89100
  try {
88921
89101
  return realpathSync8(path);
88922
89102
  } catch {
88923
- return resolve12(path);
89103
+ return resolve13(path);
88924
89104
  }
88925
89105
  }
88926
89106
  function recordPath(snapshot, path) {
88927
- return join24(getPlannotatorDataDir(), "review-progress", snapshot.key, `${hash(path)}.json`);
89107
+ return join25(getPlannotatorDataDir(), "review-progress", snapshot.key, `${hash(path)}.json`);
88928
89108
  }
88929
89109
  function loadReviewProgress(snapshot, platformViewed = []) {
88930
89110
  const viewedFiles = [];
@@ -88933,7 +89113,7 @@ function loadReviewProgress(snapshot, platformViewed = []) {
88933
89113
  for (const [path, fingerprint] of Object.entries(snapshot.fingerprints)) {
88934
89114
  let record;
88935
89115
  try {
88936
- const value = JSON.parse(readFileSync16(recordPath(snapshot, path), "utf8"));
89116
+ const value = JSON.parse(readFileSync17(recordPath(snapshot, path), "utf8"));
88937
89117
  if (value?.v === 1 && value.path === path && typeof value.fingerprint === "string" && typeof value.viewed === "boolean" && typeof value.suppressed === "boolean")
88938
89118
  record = value;
88939
89119
  } catch {}
@@ -88957,7 +89137,7 @@ function saveReviewProgress(snapshot, changes) {
88957
89137
  for (const change of changes) {
88958
89138
  const file = recordPath(snapshot, change.path);
88959
89139
  const tmp = `${file}.${randomUUID()}.tmp`;
88960
- mkdirSync11(join24(getPlannotatorDataDir(), "review-progress", snapshot.key), { recursive: true });
89140
+ mkdirSync11(join25(getPlannotatorDataDir(), "review-progress", snapshot.key), { recursive: true });
88961
89141
  try {
88962
89142
  const record = {
88963
89143
  v: 1,
@@ -89195,7 +89375,7 @@ var init_review_draft = __esm(() => {
89195
89375
  });
89196
89376
 
89197
89377
  // ../../packages/shared/annotate-draft.ts
89198
- import { realpathSync as realpathSync9, statSync as statSync15 } from "fs";
89378
+ import { realpathSync as realpathSync9, statSync as statSync16 } from "fs";
89199
89379
  import { isAbsolute as isAbsolute7, resolve as resolvePath } from "path";
89200
89380
  function annotateFileDraftKey(absolutePath) {
89201
89381
  return `file-${contentHash(`v1|file|${absolutePath}`)}`;
@@ -89512,7 +89692,7 @@ function canonicalDocumentPath(path) {
89512
89692
  return null;
89513
89693
  try {
89514
89694
  const real = realpathSync9(resolvePath(path));
89515
- return statSync15(real).isFile() ? real : null;
89695
+ return statSync16(real).isFile() ? real : null;
89516
89696
  } catch {
89517
89697
  return null;
89518
89698
  }
@@ -89583,14 +89763,14 @@ __export(exports_server, {
89583
89763
  startPlannotatorServer: () => startPlannotatorServer,
89584
89764
  stripH1: () => stripH1
89585
89765
  });
89586
- import { isAbsolute as isAbsolute8, resolve as resolve13 } from "path";
89587
- import { statSync as statSync16 } from "fs";
89766
+ import { isAbsolute as isAbsolute8, resolve as resolve14 } from "path";
89767
+ import { statSync as statSync17 } from "fs";
89588
89768
  async function historyProjects(historyRoot) {
89589
89769
  const cwdProject = await detectProjectName() ?? "_unknown";
89590
- if (!historyRoot || !isAbsolute8(historyRoot) || resolve13(historyRoot) === process.cwd())
89770
+ if (!historyRoot || !isAbsolute8(historyRoot) || resolve14(historyRoot) === process.cwd())
89591
89771
  return [cwdProject];
89592
89772
  try {
89593
- if (!statSync16(historyRoot).isDirectory())
89773
+ if (!statSync17(historyRoot).isDirectory())
89594
89774
  return [cwdProject];
89595
89775
  } catch {
89596
89776
  return [cwdProject];
@@ -90199,11 +90379,11 @@ var init_server2 = __esm(() => {
90199
90379
  // ../../packages/shared/diagram-engine-assets.ts
90200
90380
  import { createHash as createHash4 } from "crypto";
90201
90381
  import { readFile as readFile3 } from "fs/promises";
90202
- import { join as join25 } from "path";
90382
+ import { join as join26 } from "path";
90203
90383
  function diagramEngineSourcesFromDir(dir) {
90204
90384
  return async () => {
90205
90385
  try {
90206
- const [engine, mermaid, graphviz] = await Promise.all(DIAGRAM_ENGINE_FILE_NAMES.map((name) => readFile3(join25(dir, `${name}.js`), "utf8")));
90386
+ const [engine, mermaid, graphviz] = await Promise.all(DIAGRAM_ENGINE_FILE_NAMES.map((name) => readFile3(join26(dir, `${name}.js`), "utf8")));
90207
90387
  return { engine, mermaid, graphviz };
90208
90388
  } catch {
90209
90389
  return null;
@@ -91591,8 +91771,8 @@ var init_package_metadata = __esm(() => {
91591
91771
  });
91592
91772
 
91593
91773
  // ../../packages/shared/package-changes-service.ts
91594
- import { readFileSync as readFileSync17, statSync as statSync17 } from "fs";
91595
- import { dirname as dirname9, join as join26, parse as parsePath } from "path";
91774
+ import { readFileSync as readFileSync18, statSync as statSync18 } from "fs";
91775
+ import { dirname as dirname10, join as join27, parse as parsePath } from "path";
91596
91776
  function settingsNames(env) {
91597
91777
  const names = [
91598
91778
  [".npmrc", "npmrc"],
@@ -91618,7 +91798,7 @@ function userSettingsFiles(env, home) {
91618
91798
  if (userconfig)
91619
91799
  out.push([userconfig, "npmrc"]);
91620
91800
  else if (home)
91621
- out.push([join26(home, ".npmrc"), "npmrc"]);
91801
+ out.push([join27(home, ".npmrc"), "npmrc"]);
91622
91802
  const globalconfig = envValue(env, "npm_config_globalconfig");
91623
91803
  if (globalconfig)
91624
91804
  out.push([globalconfig, "npmrc"]);
@@ -91632,7 +91812,7 @@ function userSettingsFiles(env, home) {
91632
91812
  continue;
91633
91813
  const hasNode = ["node", "node.exe"].some((n) => {
91634
91814
  try {
91635
- return statSync17(join26(dir, n)).isFile();
91815
+ return statSync18(join27(dir, n)).isFile();
91636
91816
  } catch {
91637
91817
  return false;
91638
91818
  }
@@ -91640,30 +91820,30 @@ function userSettingsFiles(env, home) {
91640
91820
  if (!hasNode)
91641
91821
  continue;
91642
91822
  prefixes.add(dir);
91643
- prefixes.add(dirname9(dir));
91823
+ prefixes.add(dirname10(dir));
91644
91824
  }
91645
91825
  for (const p of prefixes)
91646
- out.push([join26(p, "etc", "npmrc"), "npmrc"]);
91826
+ out.push([join27(p, "etc", "npmrc"), "npmrc"]);
91647
91827
  }
91648
91828
  if (home) {
91649
- out.push([join26(home, ".yarnrc.yml"), "yarnrc"], [join26(home, ".yarnrc"), "yarnrcClassic"], [join26(home, ".bunfig.toml"), "bunfig"]);
91829
+ out.push([join27(home, ".yarnrc.yml"), "yarnrc"], [join27(home, ".yarnrc"), "yarnrcClassic"], [join27(home, ".bunfig.toml"), "bunfig"]);
91650
91830
  const custom = envValue(env, "YARN_RC_FILENAME");
91651
91831
  if (custom && !custom.includes("/") && !custom.includes("\\"))
91652
- out.push([join26(home, custom), "yarnrc"]);
91832
+ out.push([join27(home, custom), "yarnrc"]);
91653
91833
  }
91654
- const xdg = envValue(env, "XDG_CONFIG_HOME") ?? (home ? join26(home, ".config") : null);
91834
+ const xdg = envValue(env, "XDG_CONFIG_HOME") ?? (home ? join27(home, ".config") : null);
91655
91835
  if (xdg)
91656
- out.push([join26(xdg, ".bunfig.toml"), "bunfig"]);
91836
+ out.push([join27(xdg, ".bunfig.toml"), "bunfig"]);
91657
91837
  return out;
91658
91838
  }
91659
91839
  function readLocalSettingsFile(path) {
91660
91840
  try {
91661
- const st = statSync17(path);
91841
+ const st = statSync18(path);
91662
91842
  if (!st.isFile())
91663
91843
  return { kind: "unreadable", reason: "not a file" };
91664
91844
  if (st.size > SETTINGS_MAX_BYTES)
91665
91845
  return { kind: "unreadable", reason: "too large" };
91666
- return { kind: "ok", text: readFileSync17(path, "utf-8") };
91846
+ return { kind: "ok", text: readFileSync18(path, "utf-8") };
91667
91847
  } catch (err) {
91668
91848
  const code = err?.code;
91669
91849
  if (code === "ENOENT" || code === "ENOTDIR")
@@ -91726,7 +91906,7 @@ function absoluteAncestors(dir) {
91726
91906
  out.push(cur);
91727
91907
  if (cur === root)
91728
91908
  break;
91729
- const up = dirname9(cur);
91909
+ const up = dirname10(cur);
91730
91910
  if (up === cur)
91731
91911
  break;
91732
91912
  cur = up;
@@ -91910,7 +92090,7 @@ function createPackageChangesService(host) {
91910
92090
  }
91911
92091
  for (const dir of dirs)
91912
92092
  for (const [name, kind] of names)
91913
- take(readLocal(join26(dir, name)), kind);
92093
+ take(readLocal(join27(dir, name)), kind);
91914
92094
  } else {
91915
92095
  const dirs = new Set;
91916
92096
  for (const anchor of anchors)
@@ -92622,7 +92802,7 @@ var init_jj_core = __esm(() => {
92622
92802
 
92623
92803
  // ../../packages/shared/gitbutler-core.ts
92624
92804
  import { lstat as lstat4, readlink } from "fs/promises";
92625
- import { basename as basename7, resolve as resolve14 } from "path";
92805
+ import { basename as basename7, resolve as resolve15 } from "path";
92626
92806
  function isUnexpectedArgumentRejection(result, flag) {
92627
92807
  return result.exitCode !== 0 && result.stderr.includes(`unexpected argument '${flag}'`);
92628
92808
  }
@@ -93097,7 +93277,7 @@ async function gitShow(runtime, ref, path, cwd) {
93097
93277
  return result.exitCode === 0 ? result.stdout : null;
93098
93278
  }
93099
93279
  async function readWorkingTreeFile(runtime, root, path) {
93100
- const fullPath = resolve14(root, path);
93280
+ const fullPath = resolve15(root, path);
93101
93281
  try {
93102
93282
  const fileStat = await lstat4(fullPath);
93103
93283
  if (fileStat.isSymbolicLink())
@@ -93210,12 +93390,12 @@ var init_gitbutler_core = __esm(() => {
93210
93390
  // ../../packages/shared/vcs-core.ts
93211
93391
  import { mkdtemp } from "fs/promises";
93212
93392
  import { tmpdir as tmpdir2 } from "os";
93213
- import { isAbsolute as isAbsolute9, join as join27, relative as relative6, resolve as resolve15 } from "path";
93393
+ import { isAbsolute as isAbsolute9, join as join28, relative as relative6, resolve as resolve16 } from "path";
93214
93394
  import { rmSync as rmSync2 } from "fs";
93215
93395
  function selectNearestProvider(candidates, cwd) {
93216
93396
  if (candidates.length === 0)
93217
93397
  return null;
93218
- const effectiveCwd = resolve15(cwd ?? process.cwd());
93398
+ const effectiveCwd = resolve16(cwd ?? process.cwd());
93219
93399
  const ranked = candidates.map((candidate) => ({
93220
93400
  ...candidate,
93221
93401
  rootDepth: candidate.root ? vcsRootDepth(candidate.root) : -1,
@@ -93230,11 +93410,11 @@ function selectNearestProvider(candidates, cwd) {
93230
93410
  return ranked[0]?.provider ?? null;
93231
93411
  }
93232
93412
  function isSameOrAncestor(root, child) {
93233
- const relativePath = relative6(resolve15(root), child);
93413
+ const relativePath = relative6(resolve16(root), child);
93234
93414
  return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute9(relativePath);
93235
93415
  }
93236
93416
  function vcsRootDepth(root) {
93237
- return resolve15(root).split(/[\\/]+/).filter(Boolean).length;
93417
+ return resolve16(root).split(/[\\/]+/).filter(Boolean).length;
93238
93418
  }
93239
93419
  function createGitProvider(runtime) {
93240
93420
  return {
@@ -93642,8 +93822,8 @@ function removeDirectoryBestEffort(path) {
93642
93822
  } catch {}
93643
93823
  }
93644
93824
  async function createSyntheticSnapshot(runtime, sourceCwd, baseCommit, patches) {
93645
- const tempRoot = await mkdtemp(join27(tmpdir2(), "plannotator-review-snapshot-"));
93646
- const snapshotCwd = join27(tempRoot, "repo");
93825
+ const tempRoot = await mkdtemp(join28(tmpdir2(), "plannotator-review-snapshot-"));
93826
+ const snapshotCwd = join28(tempRoot, "repo");
93647
93827
  const cleanup = () => removeDirectoryBestEffort(tempRoot);
93648
93828
  try {
93649
93829
  await git(runtime, sourceCwd, ["clone", "--shared", "--no-checkout", "--quiet", "--", sourceCwd, snapshotCwd]);
@@ -93756,8 +93936,8 @@ async function materializeJjSnapshot(jjRuntime, gitRuntime, options) {
93756
93936
  const fromRevision = await resolveJjSnapshotEndpoint(jjRuntime, endpoints.from, options.cwd);
93757
93937
  const toRevision = await resolveJjSnapshotEndpoint(jjRuntime, endpoints.to, options.cwd);
93758
93938
  const filesets = options.includedExtensions.map((extension) => `root-glob-i:"**/*${extension}"`);
93759
- const tempRoot = await mkdtemp(join27(tmpdir2(), "plannotator-review-jj-snapshot-"));
93760
- const snapshotCwd = join27(tempRoot, "repo");
93939
+ const tempRoot = await mkdtemp(join28(tmpdir2(), "plannotator-review-jj-snapshot-"));
93940
+ const snapshotCwd = join28(tempRoot, "repo");
93761
93941
  const cleanup = () => removeDirectoryBestEffort(tempRoot);
93762
93942
  try {
93763
93943
  await git(gitRuntime, tempRoot, ["init", "--quiet", "--", snapshotCwd]);
@@ -94097,7 +94277,7 @@ var init_p4 = __esm(() => {
94097
94277
  // ../../packages/shared/repo-file.ts
94098
94278
  import { constants as fsConstants } from "fs";
94099
94279
  import { open as open2 } from "fs/promises";
94100
- import { resolve as resolve16 } from "path";
94280
+ import { resolve as resolve17 } from "path";
94101
94281
  function repoFileErrorBody(failure) {
94102
94282
  return {
94103
94283
  error: failure.message,
@@ -94162,7 +94342,7 @@ async function readRepoFile(root, rawPath, options = {}) {
94162
94342
  } catch {
94163
94343
  return fail("invalid-path", "Invalid path");
94164
94344
  }
94165
- return readFileCapped(resolve16(root, rawPath), options);
94345
+ return readFileCapped(resolve17(root, rawPath), options);
94166
94346
  }
94167
94347
  var MAX_REPO_FILE_BYTES, decodeUtf8 = (bytes) => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString("utf8"), REPO_FILE_ERROR_STATUS, OPEN_FLAGS;
94168
94348
  var init_repo_file = __esm(() => {
@@ -96151,9 +96331,9 @@ var init_review_repo_watch = __esm(() => {
96151
96331
  // ../../packages/shared/semantic-diff.ts
96152
96332
  import { spawn as spawn6 } from "child_process";
96153
96333
  import { createHash as createHash5 } from "crypto";
96154
- import { existsSync as existsSync16, mkdirSync as mkdirSync12 } from "fs";
96334
+ import { existsSync as existsSync17, mkdirSync as mkdirSync12 } from "fs";
96155
96335
  import { tmpdir as tmpdir3 } from "os";
96156
- import { delimiter, join as join28 } from "path";
96336
+ import { delimiter, join as join29 } from "path";
96157
96337
  function defaultRunCommand(command, args, options = {}) {
96158
96338
  return new Promise((resolveResult) => {
96159
96339
  let settled = false;
@@ -96232,7 +96412,7 @@ function defaultRunCommand(command, args, options = {}) {
96232
96412
  function createDefaultSemanticDiffRuntime() {
96233
96413
  return {
96234
96414
  runCommand: defaultRunCommand,
96235
- fileExists: existsSync16,
96415
+ fileExists: existsSync17,
96236
96416
  env: process.env,
96237
96417
  cwd: process.cwd(),
96238
96418
  dataDir: getPlannotatorDataDir(),
@@ -96244,15 +96424,15 @@ function semBinaryName(platform) {
96244
96424
  return platform === "win32" ? "sem.exe" : "sem";
96245
96425
  }
96246
96426
  function getManagedSemBinaryPath(dataDir = getPlannotatorDataDir(), platform = process.platform) {
96247
- return join28(dataDir, "vendor", "sem", PLANNOTATOR_SEM_VERSION, semBinaryName(platform));
96427
+ return join29(dataDir, "vendor", "sem", PLANNOTATOR_SEM_VERSION, semBinaryName(platform));
96248
96428
  }
96249
96429
  function getSemanticDiffScratchCwd(dataDir = getPlannotatorDataDir()) {
96250
- const primary = join28(dataDir, "semantic-diff", "patch-only");
96430
+ const primary = join29(dataDir, "semantic-diff", "patch-only");
96251
96431
  try {
96252
96432
  mkdirSync12(primary, { recursive: true });
96253
96433
  return primary;
96254
96434
  } catch {
96255
- const fallback = join28(tmpdir3(), "plannotator-semantic-diff");
96435
+ const fallback = join29(tmpdir3(), "plannotator-semantic-diff");
96256
96436
  try {
96257
96437
  mkdirSync12(fallback, { recursive: true });
96258
96438
  return fallback;
@@ -96269,7 +96449,7 @@ function pathCandidates(runtime) {
96269
96449
  const pathext = (runtime.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").map((ext) => ext.trim()).filter(Boolean);
96270
96450
  for (const dir of (runtime.env.PATH || "").split(runtime.pathDelimiter)) {
96271
96451
  for (const ext of pathext) {
96272
- const candidate = join28(dir, `sem${ext.toLowerCase()}`);
96452
+ const candidate = join29(dir, `sem${ext.toLowerCase()}`);
96273
96453
  if (runtime.fileExists(candidate)) {
96274
96454
  return [{ command: candidate, source: "path", explicit: false }];
96275
96455
  }
@@ -96572,12 +96752,12 @@ var package_default;
96572
96752
  var init_package = __esm(() => {
96573
96753
  package_default = {
96574
96754
  name: "plannotator-call-flow-runtime",
96575
- version: "0.4.1-plannotator.2",
96755
+ version: "0.6.0-plannotator.1",
96576
96756
  private: true,
96577
96757
  description: "Managed offline runtime for Plannotator call-flow analysis",
96578
96758
  dependencies: {
96579
96759
  "@types/node": "24.2.0",
96580
- calldiff: "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
96760
+ calldiff: "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
96581
96761
  "tree-sitter": "0.25.1",
96582
96762
  "tree-sitter-javascript": "0.25.0",
96583
96763
  "tree-sitter-typescript": "0.23.2",
@@ -96596,16 +96776,16 @@ var package_lock_default;
96596
96776
  var init_package_lock = __esm(() => {
96597
96777
  package_lock_default = {
96598
96778
  name: "plannotator-call-flow-runtime",
96599
- version: "0.4.1-plannotator.2",
96779
+ version: "0.6.0-plannotator.1",
96600
96780
  lockfileVersion: 3,
96601
96781
  requires: true,
96602
96782
  packages: {
96603
96783
  "": {
96604
96784
  name: "plannotator-call-flow-runtime",
96605
- version: "0.4.1-plannotator.2",
96785
+ version: "0.6.0-plannotator.1",
96606
96786
  dependencies: {
96607
96787
  "@types/node": "24.2.0",
96608
- calldiff: "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
96788
+ calldiff: "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
96609
96789
  "tree-sitter": "0.25.1",
96610
96790
  "tree-sitter-javascript": "0.25.0",
96611
96791
  "tree-sitter-typescript": "0.23.2",
@@ -96660,9 +96840,9 @@ var init_package_lock = __esm(() => {
96660
96840
  }
96661
96841
  },
96662
96842
  "node_modules/calldiff": {
96663
- version: "0.4.1",
96664
- resolved: "file:calldiff-a3194d20ca91ef6a314273d634e9b9c0db1c2707.tar.gz",
96665
- integrity: "sha512-5y6tjre5UE00qPrVFPurnk9RBdk8WlRok+0w41lxLt1MyPLOlNUEaAT2/j/nz2xWlhDm3DfDYvW/5KBD2b9TLg==",
96843
+ version: "0.6.0",
96844
+ resolved: "file:calldiff-2086f6e4e9ed3c410bba7c94ca3dde77791b1956.tar.gz",
96845
+ integrity: "sha512-t+YCPz0WkETiwSOznwx3mLxVkEUe7DVACV+it2HJER7q/ha+K0IavN9IfBvP+7/ijzPXoUHgJIkAM/kg1O5S6A==",
96666
96846
  license: "MIT",
96667
96847
  dependencies: {
96668
96848
  incur: "^0.4.26",
@@ -96674,7 +96854,7 @@ var init_package_lock = __esm(() => {
96674
96854
  calldiff: "dist/cli.js"
96675
96855
  },
96676
96856
  engines: {
96677
- node: ">=22"
96857
+ node: ">=22.18.0"
96678
96858
  }
96679
96859
  },
96680
96860
  "node_modules/incur": {
@@ -98325,23 +98505,23 @@ import {
98325
98505
  closeSync as closeSync5,
98326
98506
  mkdirSync as mkdirSync13,
98327
98507
  openSync as openSync5,
98328
- readFileSync as readFileSync18,
98508
+ readFileSync as readFileSync19,
98329
98509
  rmSync as rmSync3,
98330
- statSync as statSync18,
98510
+ statSync as statSync19,
98331
98511
  writeFileSync as writeFileSync11
98332
98512
  } from "fs";
98333
- import { dirname as dirname10, join as join29 } from "path";
98513
+ import { dirname as dirname11, join as join30 } from "path";
98334
98514
  function hasErrorCode(error, code) {
98335
98515
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
98336
98516
  }
98337
98517
  function removeOwnedLock(lockPath, token) {
98338
98518
  try {
98339
- if (readFileSync18(lockPath, "utf8") === token)
98519
+ if (readFileSync19(lockPath, "utf8") === token)
98340
98520
  rmSync3(lockPath, { force: true });
98341
98521
  } catch {}
98342
98522
  }
98343
98523
  async function withCallFlowInstallLock(work, options = {}) {
98344
- const lockPath = options.lockPath ?? join29(getPlannotatorDataDir(), "vendor", "call-flow", ".install.lock");
98524
+ const lockPath = options.lockPath ?? join30(getPlannotatorDataDir(), "vendor", "call-flow", ".install.lock");
98345
98525
  const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
98346
98526
  const waitTimeoutMs = options.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
98347
98527
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -98349,7 +98529,7 @@ async function withCallFlowInstallLock(work, options = {}) {
98349
98529
  const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
98350
98530
  const token = `${process.pid}:${randomUUID2()}`;
98351
98531
  const deadline = now() + waitTimeoutMs;
98352
- mkdirSync13(dirname10(lockPath), { recursive: true });
98532
+ mkdirSync13(dirname11(lockPath), { recursive: true });
98353
98533
  while (true) {
98354
98534
  try {
98355
98535
  const descriptor = openSync5(lockPath, "wx", 384);
@@ -98363,7 +98543,7 @@ async function withCallFlowInstallLock(work, options = {}) {
98363
98543
  if (!hasErrorCode(error, "EEXIST"))
98364
98544
  throw error;
98365
98545
  try {
98366
- if (now() - statSync18(lockPath).mtimeMs > staleAfterMs) {
98546
+ if (now() - statSync19(lockPath).mtimeMs > staleAfterMs) {
98367
98547
  rmSync3(lockPath);
98368
98548
  continue;
98369
98549
  }
@@ -98472,7 +98652,7 @@ var init_call_flow_languages = __esm(() => {
98472
98652
  { id: "java", label: "Java", extensions: [".java"], kind: "pack", packageName: "tree-sitter-java", packageVersion: "0.23.5", installSizeBytes: 1 * MB2 },
98473
98653
  { id: "ruby", label: "Ruby", extensions: [".rb"], kind: "pack", packageName: "tree-sitter-ruby", packageVersion: "0.23.1", installSizeBytes: 3 * MB2 },
98474
98654
  { id: "c", label: "C", extensions: [".c", ".h"], kind: "pack", packageName: "tree-sitter-c", packageVersion: "0.24.1", installSizeBytes: 1 * MB2 },
98475
- { id: "cpp", label: "C++", extensions: [".cc", ".cpp", ".cxx", ".hpp", ".hh"], kind: "pack", packageName: "tree-sitter-cpp", packageVersion: "0.23.4", installSizeBytes: 4 * MB2 },
98655
+ { id: "cpp", label: "C++", extensions: [".cc", ".cpp", ".cu", ".cuh", ".cxx", ".hpp", ".hh"], kind: "pack", packageName: "tree-sitter-cpp", packageVersion: "0.23.4", installSizeBytes: 4 * MB2 },
98476
98656
  { id: "csharp", label: "C#", extensions: [".cs"], kind: "pack", packageName: "tree-sitter-c-sharp", packageVersion: "0.23.1", installSizeBytes: 6 * MB2 },
98477
98657
  { id: "php", label: "PHP", extensions: [".php"], kind: "pack", packageName: "tree-sitter-php", packageVersion: "0.24.2", installSizeBytes: 17 * MB2 },
98478
98658
  { id: "kotlin", label: "Kotlin", extensions: [".kt", ".kts"], kind: "pack", packageName: "tree-sitter-kotlin", packageVersion: "0.3.8", installSizeBytes: 4 * MB2 },
@@ -98674,28 +98854,28 @@ var init_call_flow_types = __esm(() => {
98674
98854
 
98675
98855
  // ../../packages/shared/call-flow.ts
98676
98856
  import { createHash as createHash6 } from "crypto";
98677
- import { chmodSync as chmodSync2, cpSync, existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as readFileSync19, readdirSync as readdirSync6, renameSync as renameSync9, rmSync as rmSync4, statSync as statSync19, writeFileSync as writeFileSync12 } from "fs";
98857
+ import { chmodSync as chmodSync2, cpSync, existsSync as existsSync18, mkdirSync as mkdirSync14, readFileSync as readFileSync20, readdirSync as readdirSync6, renameSync as renameSync9, rmSync as rmSync4, statSync as statSync20, writeFileSync as writeFileSync12 } from "fs";
98678
98858
  import { mkdtemp as mkdtemp2 } from "fs/promises";
98679
98859
  import { tmpdir as tmpdir4 } from "os";
98680
- import { basename as basename8, dirname as dirname11, isAbsolute as isAbsolute11, join as join30 } from "path";
98860
+ import { basename as basename8, dirname as dirname12, isAbsolute as isAbsolute11, join as join31 } from "path";
98681
98861
  import { spawn as spawn7, spawnSync as spawnSync5 } from "child_process";
98682
98862
  function getCallFlowManagedRuntimeDir(dataDir = getPlannotatorDataDir()) {
98683
- return join30(dataDir, "vendor", "call-flow", `calldiff-${CALLDIFF_VERSION}`);
98863
+ return join31(dataDir, "vendor", "call-flow", `calldiff-${CALLDIFF_VERSION}`);
98684
98864
  }
98685
98865
  function getCallFlowGrammarCacheDir(runtimeDir) {
98686
- return join30(runtimeDir, "grammar-cache");
98866
+ return join31(runtimeDir, "grammar-cache");
98687
98867
  }
98688
98868
  function getLanguagePackageRoot(runtimeDir, language) {
98689
98869
  if (language.kind === "core")
98690
- return join30(runtimeDir, "node_modules", "tree-sitter-typescript");
98691
- return join30(getCallFlowGrammarCacheDir(runtimeDir), "node_modules", ...(language.packageName ?? "").split("/"));
98870
+ return join31(runtimeDir, "node_modules", "tree-sitter-typescript");
98871
+ return join31(getCallFlowGrammarCacheDir(runtimeDir), "node_modules", ...(language.packageName ?? "").split("/"));
98692
98872
  }
98693
98873
  function getLanguageLockMarker(runtimeDir, id) {
98694
- return join30(getCallFlowGrammarCacheDir(runtimeDir), ".plannotator-locks", `${id}.json`);
98874
+ return join31(getCallFlowGrammarCacheDir(runtimeDir), ".plannotator-locks", `${id}.json`);
98695
98875
  }
98696
98876
  function packHasCommittedLock(runtimeDir, id) {
98697
98877
  try {
98698
- const installed = JSON.parse(readFileSync19(getLanguageLockMarker(runtimeDir, id), "utf8"));
98878
+ const installed = JSON.parse(readFileSync20(getLanguageLockMarker(runtimeDir, id), "utf8"));
98699
98879
  return JSON.stringify(installed) === JSON.stringify(CALL_FLOW_PACK_LOCKS[id]);
98700
98880
  } catch {
98701
98881
  return false;
@@ -98703,7 +98883,7 @@ function packHasCommittedLock(runtimeDir, id) {
98703
98883
  }
98704
98884
  function installDirHasCommittedPackLock(installDir, id) {
98705
98885
  try {
98706
- const installed = JSON.parse(readFileSync19(join30(installDir, "package-lock.json"), "utf8"));
98886
+ const installed = JSON.parse(readFileSync20(join31(installDir, "package-lock.json"), "utf8"));
98707
98887
  return JSON.stringify(installed) === JSON.stringify(CALL_FLOW_PACK_LOCKS[id]);
98708
98888
  } catch {
98709
98889
  return false;
@@ -98712,22 +98892,22 @@ function installDirHasCommittedPackLock(installDir, id) {
98712
98892
  function isLanguageInstalled(runtimeDir, language, managed) {
98713
98893
  if (language.kind === "core")
98714
98894
  return true;
98715
- const packageRoot = managed ? getLanguagePackageRoot(runtimeDir, language) : join30(runtimeDir, "node_modules", ...(language.packageName ?? "").split("/"));
98895
+ const packageRoot = managed ? getLanguagePackageRoot(runtimeDir, language) : join31(runtimeDir, "node_modules", ...(language.packageName ?? "").split("/"));
98716
98896
  if (readPackageVersion(packageRoot) !== language.packageVersion)
98717
98897
  return false;
98718
98898
  return !managed || packHasCommittedLock(runtimeDir, language.id);
98719
98899
  }
98720
98900
  function readPackageVersion(packageRoot) {
98721
98901
  try {
98722
- const parsed = JSON.parse(readFileSync19(join30(packageRoot, "package.json"), "utf8"));
98902
+ const parsed = JSON.parse(readFileSync20(join31(packageRoot, "package.json"), "utf8"));
98723
98903
  return typeof parsed.version === "string" ? parsed.version : null;
98724
98904
  } catch {
98725
98905
  return null;
98726
98906
  }
98727
98907
  }
98728
98908
  function resolvePackageEntry(packageRoot) {
98729
- const entry = join30(packageRoot, "dist", "run.js");
98730
- return existsSync17(entry) ? entry : null;
98909
+ const entry = join31(packageRoot, "dist", "run.js");
98910
+ return existsSync18(entry) ? entry : null;
98731
98911
  }
98732
98912
  function runtimeJson(value) {
98733
98913
  return `${JSON.stringify(value, null, 2)}
@@ -98735,19 +98915,29 @@ function runtimeJson(value) {
98735
98915
  }
98736
98916
  function hasCommittedRuntimeLock(runtimeDir) {
98737
98917
  try {
98738
- const lock = JSON.parse(readFileSync19(join30(runtimeDir, "package-lock.json"), "utf8"));
98918
+ const lock = JSON.parse(readFileSync20(join31(runtimeDir, "package-lock.json"), "utf8"));
98739
98919
  return JSON.stringify(lock) === JSON.stringify(package_lock_default);
98740
98920
  } catch {
98741
98921
  return false;
98742
98922
  }
98743
98923
  }
98744
- async function checkNode22(nodePath) {
98924
+ function callFlowNodeVersionSupported(version) {
98925
+ const match = /^v?(\d+)\.(\d+)/.exec(version.trim());
98926
+ if (!match)
98927
+ return false;
98928
+ const major = Number(match[1]);
98929
+ const minor = Number(match[2]);
98930
+ const floor = CALL_FLOW_MIN_NODE_VERSION;
98931
+ return major > floor.major || major === floor.major && minor >= floor.minor;
98932
+ }
98933
+ async function checkCallFlowNode(nodePath) {
98745
98934
  const result = await runCommand(nodePath, ["--version"], { timeoutMs: 3000 });
98746
- const match = /^v(\d+)/.exec(result.stdout.trim());
98747
- if (result.exitCode !== 0 || !match)
98935
+ const found = result.stdout.trim();
98936
+ if (result.exitCode !== 0 || !/^v\d+\.\d+/.test(found))
98748
98937
  return { ok: false, message: "Node.js could not be started." };
98749
- if (Number(match[1]) < 22)
98750
- return { ok: false, message: `Node.js 22 or newer is required (found ${result.stdout.trim()}).` };
98938
+ if (!callFlowNodeVersionSupported(found)) {
98939
+ return { ok: false, message: `Call flow needs ${CALL_FLOW_NODE_REQUIREMENT}. You have Node.js ${found}.` };
98940
+ }
98751
98941
  return { ok: true };
98752
98942
  }
98753
98943
  function findExecutable(name) {
@@ -98759,8 +98949,8 @@ function findExecutable(name) {
98759
98949
  if (!directory)
98760
98950
  continue;
98761
98951
  for (const extension of extensions) {
98762
- const candidate = join30(directory, `${name}${extension}`);
98763
- if (existsSync17(candidate))
98952
+ const candidate = join31(directory, `${name}${extension}`);
98953
+ if (existsSync18(candidate))
98764
98954
  return candidate;
98765
98955
  }
98766
98956
  }
@@ -98769,8 +98959,8 @@ function findExecutable(name) {
98769
98959
  async function preflightCallFlowNode() {
98770
98960
  const nodePath = findExecutable("node");
98771
98961
  if (!nodePath)
98772
- return { ok: false, reason: "node-unavailable", message: "Call flow requires Node.js 22 or newer, which was not found on PATH." };
98773
- const nodeCheck = await checkNode22(nodePath);
98962
+ return { ok: false, reason: "node-unavailable", message: `Call flow needs ${CALL_FLOW_NODE_REQUIREMENT}, which was not found on PATH.` };
98963
+ const nodeCheck = await checkCallFlowNode(nodePath);
98774
98964
  if (!nodeCheck.ok)
98775
98965
  return { ok: false, reason: "node-version", message: nodeCheck.message };
98776
98966
  return { ok: true };
@@ -98778,8 +98968,8 @@ async function preflightCallFlowNode() {
98778
98968
  async function resolveCallFlowRuntime() {
98779
98969
  const nodePath = findExecutable("node");
98780
98970
  if (!nodePath)
98781
- return { ok: false, reason: "node-unavailable", message: "Call flow requires Node.js 22 or newer." };
98782
- const nodeCheck = await checkNode22(nodePath);
98971
+ return { ok: false, reason: "node-unavailable", message: `Call flow needs ${CALL_FLOW_NODE_REQUIREMENT}, which was not found on PATH.` };
98972
+ const nodeCheck = await checkCallFlowNode(nodePath);
98783
98973
  if (!nodeCheck.ok)
98784
98974
  return { ok: false, reason: "node-version", message: nodeCheck.message };
98785
98975
  const override = process.env.PLANNOTATOR_CALLDIFF_PATH?.trim();
@@ -98792,7 +98982,7 @@ async function resolveCallFlowRuntime() {
98792
98982
  }
98793
98983
  const runtimeDir = override ?? getCallFlowManagedRuntimeDir();
98794
98984
  const managed = !override;
98795
- const packageRoot = override ? runtimeDir : join30(runtimeDir, "node_modules", "calldiff");
98985
+ const packageRoot = override ? runtimeDir : join31(runtimeDir, "node_modules", "calldiff");
98796
98986
  const version = readPackageVersion(packageRoot);
98797
98987
  const packageEntry = resolvePackageEntry(packageRoot);
98798
98988
  if (!version || !packageEntry) {
@@ -98806,8 +98996,8 @@ async function resolveCallFlowRuntime() {
98806
98996
  return { ok: false, reason: "version-mismatch", message: `CallDiff ${version} is installed; Plannotator requires ${CALLDIFF_VERSION}.` };
98807
98997
  }
98808
98998
  if (!override) {
98809
- const revisionPath = join30(runtimeDir, ".calldiff-revision");
98810
- const installedRevision = existsSync17(revisionPath) ? readFileSync19(revisionPath, "utf8").trim() : "";
98999
+ const revisionPath = join31(runtimeDir, ".calldiff-revision");
99000
+ const installedRevision = existsSync18(revisionPath) ? readFileSync20(revisionPath, "utf8").trim() : "";
98811
99001
  if (installedRevision !== CALLDIFF_COMMIT) {
98812
99002
  return { ok: false, reason: "revision-mismatch", message: "The managed Call flow core is stale and needs reinstalling." };
98813
99003
  }
@@ -98815,7 +99005,7 @@ async function resolveCallFlowRuntime() {
98815
99005
  return { ok: false, reason: "runtime-lock-mismatch", message: "The managed Call flow core dependency lock is stale and needs reinstalling." };
98816
99006
  }
98817
99007
  }
98818
- const invalidCore = CORE_RUNTIME_DEPENDENCIES.filter(({ packageName, version: expected }) => readPackageVersion(join30(runtimeDir, "node_modules", packageName)) !== expected);
99008
+ const invalidCore = CORE_RUNTIME_DEPENDENCIES.filter(({ packageName, version: expected }) => readPackageVersion(join31(runtimeDir, "node_modules", packageName)) !== expected);
98819
99009
  if (invalidCore.length > 0) {
98820
99010
  return {
98821
99011
  ok: false,
@@ -98838,8 +99028,8 @@ async function resolveCallFlowRuntime() {
98838
99028
  };
98839
99029
  }
98840
99030
  function writeRuntimeManifest(runtimeDir) {
98841
- writeFileSync12(join30(runtimeDir, "package.json"), runtimeJson(package_default), "utf8");
98842
- writeFileSync12(join30(runtimeDir, "package-lock.json"), runtimeJson(package_lock_default), "utf8");
99031
+ writeFileSync12(join31(runtimeDir, "package.json"), runtimeJson(package_default), "utf8");
99032
+ writeFileSync12(join31(runtimeDir, "package-lock.json"), runtimeJson(package_lock_default), "utf8");
98843
99033
  }
98844
99034
  function removeDirectoryBestEffort2(path) {
98845
99035
  try {
@@ -98928,14 +99118,14 @@ async function downloadVerifiedLockArtifacts(lock, destination, onStage) {
98928
99118
  if (integrity !== artifact.integrity) {
98929
99119
  throw new Error(`Pinned grammar package failed its repository-owned SHA-512 check: ${artifact.url}`);
98930
99120
  }
98931
- const path = join30(destination, `artifact-${index}.tgz`);
99121
+ const path = join31(destination, `artifact-${index}.tgz`);
98932
99122
  writeFileSync12(path, artifact.bytes);
98933
99123
  verifiedPaths.push(path);
98934
99124
  }
98935
99125
  return verifiedPaths;
98936
99126
  }
98937
99127
  async function seedVerifiedNpmCache(npmPath, installDir, artifacts) {
98938
- const cacheDir = join30(installDir, ".verified-npm-cache");
99128
+ const cacheDir = join31(installDir, ".verified-npm-cache");
98939
99129
  for (const artifact of artifacts) {
98940
99130
  const cached = await runCommand(npmPath, [
98941
99131
  "cache",
@@ -98966,31 +99156,31 @@ function removeDirectoryStrict(path) {
98966
99156
  rmSync4(path, { recursive: true, force: true });
98967
99157
  }
98968
99158
  function assertPathRemoved(path, description) {
98969
- if (existsSync17(path))
99159
+ if (existsSync18(path))
98970
99160
  throw new Error(`Call-flow pruning did not remove ${description}.`);
98971
99161
  }
98972
99162
  function prunePrebuilds(packageRoot) {
98973
- const prebuilds = join30(packageRoot, "prebuilds");
98974
- if (!existsSync17(prebuilds))
99163
+ const prebuilds = join31(packageRoot, "prebuilds");
99164
+ if (!existsSync18(prebuilds))
98975
99165
  return;
98976
99166
  const currentPlatform = `${process.platform}-${process.arch}`;
98977
99167
  for (const entry of readdirSync6(prebuilds)) {
98978
99168
  if (entry !== currentPlatform)
98979
- removeDirectoryStrict(join30(prebuilds, entry));
99169
+ removeDirectoryStrict(join31(prebuilds, entry));
98980
99170
  }
98981
99171
  }
98982
99172
  function assertNativePackagePruned(packageRoot) {
98983
99173
  for (const directory of PRUNED_PACKAGE_DIRECTORIES) {
98984
99174
  if (directory !== "build") {
98985
- assertPathRemoved(join30(packageRoot, directory), `${basename8(packageRoot)}/${directory}`);
99175
+ assertPathRemoved(join31(packageRoot, directory), `${basename8(packageRoot)}/${directory}`);
98986
99176
  }
98987
99177
  }
98988
- assertPathRemoved(join30(packageRoot, "node_modules"), `${basename8(packageRoot)}/node_modules`);
99178
+ assertPathRemoved(join31(packageRoot, "node_modules"), `${basename8(packageRoot)}/node_modules`);
98989
99179
  for (const file of ["binding.gyp", "grammar.js", "tree-sitter.json"]) {
98990
- assertPathRemoved(join30(packageRoot, file), `${basename8(packageRoot)}/${file}`);
99180
+ assertPathRemoved(join31(packageRoot, file), `${basename8(packageRoot)}/${file}`);
98991
99181
  }
98992
- const prebuilds = join30(packageRoot, "prebuilds");
98993
- if (existsSync17(prebuilds)) {
99182
+ const prebuilds = join31(packageRoot, "prebuilds");
99183
+ if (existsSync18(prebuilds)) {
98994
99184
  const currentPlatform = `${process.platform}-${process.arch}`;
98995
99185
  const foreign = readdirSync6(prebuilds).filter((entry) => entry !== currentPlatform);
98996
99186
  if (foreign.length > 0) {
@@ -98998,10 +99188,10 @@ function assertNativePackagePruned(packageRoot) {
98998
99188
  }
98999
99189
  }
99000
99190
  const assertOnlyNativeAddons = (directory) => {
99001
- if (!existsSync17(directory))
99191
+ if (!existsSync18(directory))
99002
99192
  return;
99003
99193
  for (const entry of readdirSync6(directory, { withFileTypes: true })) {
99004
- const path = join30(directory, entry.name);
99194
+ const path = join31(directory, entry.name);
99005
99195
  if (entry.isDirectory())
99006
99196
  assertOnlyNativeAddons(path);
99007
99197
  else if (!entry.isFile() || !entry.name.endsWith(".node")) {
@@ -99009,14 +99199,14 @@ function assertNativePackagePruned(packageRoot) {
99009
99199
  }
99010
99200
  }
99011
99201
  };
99012
- assertOnlyNativeAddons(join30(packageRoot, "build"));
99013
- const bindings = join30(packageRoot, "bindings", "node");
99014
- if (existsSync17(bindings)) {
99202
+ assertOnlyNativeAddons(join31(packageRoot, "build"));
99203
+ const bindings = join31(packageRoot, "bindings", "node");
99204
+ if (existsSync18(bindings)) {
99015
99205
  const retainedSource = readdirSync6(bindings).find((file) => file === "binding.cc" || file.endsWith("_test.js"));
99016
99206
  if (retainedSource)
99017
99207
  throw new Error(`Call-flow pruning retained ${basename8(packageRoot)}/${retainedSource}.`);
99018
99208
  }
99019
- if (existsSync17(packageRoot)) {
99209
+ if (existsSync18(packageRoot)) {
99020
99210
  const wasm = readdirSync6(packageRoot).find((file) => file.endsWith(".wasm"));
99021
99211
  if (wasm)
99022
99212
  throw new Error(`Call-flow pruning retained ${basename8(packageRoot)}/${wasm}.`);
@@ -99025,67 +99215,67 @@ function assertNativePackagePruned(packageRoot) {
99025
99215
  function pruneCallFlowNativePackage(packageRoot) {
99026
99216
  const builtAddons = [];
99027
99217
  const collectBuiltAddons = (directory) => {
99028
- if (!existsSync17(directory))
99218
+ if (!existsSync18(directory))
99029
99219
  return;
99030
99220
  for (const entry of readdirSync6(directory, { withFileTypes: true })) {
99031
- const path = join30(directory, entry.name);
99221
+ const path = join31(directory, entry.name);
99032
99222
  if (entry.isDirectory())
99033
99223
  collectBuiltAddons(path);
99034
99224
  else if (entry.isFile() && entry.name.endsWith(".node")) {
99035
- builtAddons.push({ name: basename8(path), bytes: readFileSync19(path) });
99225
+ builtAddons.push({ name: basename8(path), bytes: readFileSync20(path) });
99036
99226
  }
99037
99227
  }
99038
99228
  };
99039
- collectBuiltAddons(join30(packageRoot, "build"));
99229
+ collectBuiltAddons(join31(packageRoot, "build"));
99040
99230
  prunePrebuilds(packageRoot);
99041
99231
  for (const directory of PRUNED_PACKAGE_DIRECTORIES) {
99042
- removeDirectoryStrict(join30(packageRoot, directory));
99232
+ removeDirectoryStrict(join31(packageRoot, directory));
99043
99233
  }
99044
- removeDirectoryStrict(join30(packageRoot, "node_modules"));
99045
- const bindings = join30(packageRoot, "bindings", "node");
99046
- if (existsSync17(bindings)) {
99234
+ removeDirectoryStrict(join31(packageRoot, "node_modules"));
99235
+ const bindings = join31(packageRoot, "bindings", "node");
99236
+ if (existsSync18(bindings)) {
99047
99237
  for (const file of readdirSync6(bindings)) {
99048
99238
  if (file === "binding.cc" || file.endsWith("_test.js"))
99049
- rmSync4(join30(bindings, file), { force: true });
99239
+ rmSync4(join31(bindings, file), { force: true });
99050
99240
  }
99051
99241
  }
99052
99242
  for (const file of ["binding.gyp", "grammar.js", "tree-sitter.json"]) {
99053
- rmSync4(join30(packageRoot, file), { force: true });
99243
+ rmSync4(join31(packageRoot, file), { force: true });
99054
99244
  }
99055
99245
  if (builtAddons.length > 0) {
99056
- const releaseDir = join30(packageRoot, "build", "Release");
99246
+ const releaseDir = join31(packageRoot, "build", "Release");
99057
99247
  mkdirSync14(releaseDir, { recursive: true });
99058
99248
  for (const addon of builtAddons)
99059
- writeFileSync12(join30(releaseDir, addon.name), addon.bytes);
99249
+ writeFileSync12(join31(releaseDir, addon.name), addon.bytes);
99060
99250
  }
99061
- if (existsSync17(packageRoot)) {
99251
+ if (existsSync18(packageRoot)) {
99062
99252
  for (const file of readdirSync6(packageRoot)) {
99063
99253
  if (file.endsWith(".wasm"))
99064
- rmSync4(join30(packageRoot, file), { force: true });
99254
+ rmSync4(join31(packageRoot, file), { force: true });
99065
99255
  }
99066
99256
  }
99067
99257
  assertNativePackagePruned(packageRoot);
99068
99258
  }
99069
99259
  function pruneCoreRuntime(runtimeDir) {
99070
99260
  for (const packageName of ["tree-sitter", ...CORE_GRAMMAR_PACKAGES]) {
99071
- pruneCallFlowNativePackage(join30(runtimeDir, "node_modules", packageName));
99261
+ pruneCallFlowNativePackage(join31(runtimeDir, "node_modules", packageName));
99072
99262
  }
99073
- const packageRoot = join30(runtimeDir, "node_modules", "calldiff");
99074
- removeDirectoryStrict(join30(packageRoot, "src"));
99075
- for (const file of readdirSync6(join30(packageRoot, "dist"))) {
99263
+ const packageRoot = join31(runtimeDir, "node_modules", "calldiff");
99264
+ removeDirectoryStrict(join31(packageRoot, "src"));
99265
+ for (const file of readdirSync6(join31(packageRoot, "dist"))) {
99076
99266
  if (file.startsWith("cli.") || file === "index.js" || file === "index.d.ts") {
99077
- rmSync4(join30(packageRoot, "dist", file), { force: true });
99267
+ rmSync4(join31(packageRoot, "dist", file), { force: true });
99078
99268
  }
99079
99269
  }
99080
99270
  for (const packagePath of CORE_PRUNED_PACKAGES) {
99081
- removeDirectoryStrict(join30(runtimeDir, "node_modules", ...packagePath.split("/")));
99271
+ removeDirectoryStrict(join31(runtimeDir, "node_modules", ...packagePath.split("/")));
99082
99272
  }
99083
- removeDirectoryStrict(join30(runtimeDir, "node_modules", ".bin"));
99084
- assertPathRemoved(join30(packageRoot, "src"), "calldiff/src");
99273
+ removeDirectoryStrict(join31(runtimeDir, "node_modules", ".bin"));
99274
+ assertPathRemoved(join31(packageRoot, "src"), "calldiff/src");
99085
99275
  for (const packagePath of CORE_PRUNED_PACKAGES) {
99086
- assertPathRemoved(join30(runtimeDir, "node_modules", ...packagePath.split("/")), `node_modules/${packagePath}`);
99276
+ assertPathRemoved(join31(runtimeDir, "node_modules", ...packagePath.split("/")), `node_modules/${packagePath}`);
99087
99277
  }
99088
- assertPathRemoved(join30(runtimeDir, "node_modules", ".bin"), "node_modules/.bin");
99278
+ assertPathRemoved(join31(runtimeDir, "node_modules", ".bin"), "node_modules/.bin");
99089
99279
  }
99090
99280
  function validationFilesForLanguage(id) {
99091
99281
  if (id === CALL_FLOW_CORE_LANGUAGE_ID) {
@@ -99095,7 +99285,7 @@ function validationFilesForLanguage(id) {
99095
99285
  return [`validation${extension}`];
99096
99286
  }
99097
99287
  async function validatePrunedLanguage(nodePath, runtimeDir, grammarCacheDir, ids) {
99098
- const extractEntry = join30(runtimeDir, "node_modules", "calldiff", "dist", "extract.js");
99288
+ const extractEntry = join31(runtimeDir, "node_modules", "calldiff", "dist", "extract.js");
99099
99289
  const script = String.raw`
99100
99290
  import { pathToFileURL } from "node:url";
99101
99291
  const request = JSON.parse(process.argv[1]);
@@ -99131,39 +99321,98 @@ for (const file of request.files) mod.extractFunctions(file, "");
99131
99321
  }
99132
99322
  function ensureGrammarCacheManifest(cacheDir) {
99133
99323
  mkdirSync14(cacheDir, { recursive: true });
99134
- writeFileSync12(join30(cacheDir, "package.json"), runtimeJson({
99324
+ writeFileSync12(join31(cacheDir, "package.json"), runtimeJson({
99135
99325
  name: "plannotator-calldiff-grammar-cache",
99136
99326
  private: true
99137
99327
  }), "utf8");
99138
99328
  }
99139
- async function preserveValidatedLanguagePacks(nodePath, currentRuntimeDir, candidateRuntimeDir) {
99329
+ async function preserveValidatedLanguagePacks(nodePath, sourceRuntimeDirs, candidateRuntimeDir) {
99140
99330
  const candidateCache = getCallFlowGrammarCacheDir(candidateRuntimeDir);
99141
99331
  ensureGrammarCacheManifest(candidateCache);
99332
+ const preserved = [];
99142
99333
  for (const language of CALL_FLOW_LANGUAGES) {
99143
- if (language.kind !== "pack" || !isLanguageInstalled(currentRuntimeDir, language, true))
99334
+ if (language.kind !== "pack")
99144
99335
  continue;
99145
99336
  const packId = language.id;
99146
- const currentRoot = getLanguagePackageRoot(currentRuntimeDir, language);
99147
99337
  const candidateRoot = getLanguagePackageRoot(candidateRuntimeDir, language);
99148
- mkdirSync14(dirname11(candidateRoot), { recursive: true });
99149
- cpSync(currentRoot, candidateRoot, { recursive: true, force: true });
99338
+ for (const sourceRuntimeDir of sourceRuntimeDirs) {
99339
+ if (!isLanguageInstalled(sourceRuntimeDir, language, true))
99340
+ continue;
99341
+ mkdirSync14(dirname12(candidateRoot), { recursive: true });
99342
+ cpSync(getLanguagePackageRoot(sourceRuntimeDir, language), candidateRoot, { recursive: true, force: true });
99343
+ try {
99344
+ await validatePrunedLanguage(nodePath, candidateRuntimeDir, candidateCache, [language.id]);
99345
+ const marker = getLanguageLockMarker(candidateRuntimeDir, language.id);
99346
+ mkdirSync14(dirname12(marker), { recursive: true });
99347
+ writeFileSync12(marker, runtimeJson(CALL_FLOW_PACK_LOCKS[packId]), "utf8");
99348
+ preserved.push(language.id);
99349
+ break;
99350
+ } catch {
99351
+ removeDirectoryStrict(candidateRoot);
99352
+ }
99353
+ }
99354
+ }
99355
+ return preserved;
99356
+ }
99357
+ function compareVersionTriples(a, b) {
99358
+ for (let index = 0;index < 3; index += 1) {
99359
+ if (a[index] !== b[index])
99360
+ return a[index] - b[index];
99361
+ }
99362
+ return 0;
99363
+ }
99364
+ function listStaleCallFlowRuntimeDirs(dataDir = getPlannotatorDataDir()) {
99365
+ const currentDir = getCallFlowManagedRuntimeDir(dataDir);
99366
+ const parent = dirname12(currentDir);
99367
+ const current = CALLDIFF_VERSION.split(".").map(Number);
99368
+ let entries;
99369
+ try {
99370
+ entries = readdirSync6(parent, { withFileTypes: true });
99371
+ } catch {
99372
+ return [];
99373
+ }
99374
+ const stale = [];
99375
+ for (const entry of entries) {
99376
+ if (!entry.isDirectory())
99377
+ continue;
99378
+ const match = MANAGED_CORE_DIR_PATTERN.exec(entry.name);
99379
+ if (!match)
99380
+ continue;
99381
+ const isBackup = match[4] !== undefined;
99382
+ const version = [Number(match[1]), Number(match[2]), Number(match[3])];
99383
+ const order = compareVersionTriples(version, current);
99384
+ if (order < 0 || isBackup && order === 0)
99385
+ stale.push(join31(parent, entry.name));
99386
+ }
99387
+ return stale.sort();
99388
+ }
99389
+ function removeStaleCallFlowRuntimeDirs(dataDir = getPlannotatorDataDir()) {
99390
+ const parent = dirname12(getCallFlowManagedRuntimeDir(dataDir));
99391
+ const stamp = `${process.pid}-${Date.now()}`;
99392
+ listStaleCallFlowRuntimeDirs(dataDir).forEach((path, index) => {
99150
99393
  try {
99151
- await validatePrunedLanguage(nodePath, candidateRuntimeDir, candidateCache, [language.id]);
99152
- const marker = getLanguageLockMarker(candidateRuntimeDir, language.id);
99153
- mkdirSync14(dirname11(marker), { recursive: true });
99154
- writeFileSync12(marker, runtimeJson(CALL_FLOW_PACK_LOCKS[packId]), "utf8");
99155
- } catch {
99156
- removeDirectoryStrict(candidateRoot);
99394
+ renameSync9(path, join31(parent, `.calldiff-stale-${stamp}-${index}`));
99395
+ } catch {}
99396
+ });
99397
+ let entries;
99398
+ try {
99399
+ entries = readdirSync6(parent, { withFileTypes: true });
99400
+ } catch {
99401
+ return;
99402
+ }
99403
+ for (const entry of entries) {
99404
+ if (entry.isDirectory() && STALE_TOMBSTONE_PATTERN.test(entry.name)) {
99405
+ removeDirectoryBestEffort2(join31(parent, entry.name));
99157
99406
  }
99158
99407
  }
99159
99408
  }
99160
99409
  function measureCallFlowDirectoryBytes(path) {
99161
- if (!existsSync17(path))
99410
+ if (!existsSync18(path))
99162
99411
  return 0;
99163
- const stat = statSync19(path);
99412
+ const stat = statSync20(path);
99164
99413
  if (!stat.isDirectory())
99165
99414
  return stat.size;
99166
- return readdirSync6(path).reduce((total, entry) => total + measureCallFlowDirectoryBytes(join30(path, entry)), 0);
99415
+ return readdirSync6(path).reduce((total, entry) => total + measureCallFlowDirectoryBytes(join31(path, entry)), 0);
99167
99416
  }
99168
99417
  async function installCallFlowRuntime(onStage = () => {}) {
99169
99418
  return await withCallFlowInstallLock(() => installCallFlowRuntimeUnlocked(onStage));
@@ -99173,22 +99422,23 @@ async function installCallFlowRuntimeUnlocked(onStage) {
99173
99422
  const nodePath = findExecutable("node");
99174
99423
  const npmPath = findExecutable("npm");
99175
99424
  if (!nodePath || !npmPath) {
99176
- return { ok: false, status: "failed", runtimeDir, message: "Call-flow runtime requires Node.js 22+ and npm." };
99425
+ return { ok: false, status: "failed", runtimeDir, message: `Call flow needs ${CALL_FLOW_NODE_REQUIREMENT} and npm on PATH.` };
99177
99426
  }
99178
- const nodeCheck = await checkNode22(nodePath);
99427
+ const nodeCheck = await checkCallFlowNode(nodePath);
99179
99428
  if (!nodeCheck.ok)
99180
99429
  return { ok: false, status: "failed", runtimeDir, message: nodeCheck.message };
99181
99430
  const existing = await resolveCallFlowRuntime();
99182
99431
  if (existing.ok && existing.runtime.runtimeDir === runtimeDir) {
99432
+ removeStaleCallFlowRuntimeDirs();
99183
99433
  return { ok: true, status: "already-installed", runtimeDir, languageId: CALL_FLOW_CORE_LANGUAGE_ID, message: `Call-flow core already installed at ${runtimeDir}.` };
99184
99434
  }
99185
- const runtimeParent = dirname11(runtimeDir);
99435
+ const runtimeParent = dirname12(runtimeDir);
99186
99436
  mkdirSync14(runtimeParent, { recursive: true });
99187
- const installDir = await mkdtemp2(join30(runtimeParent, ".calldiff-install-"));
99437
+ const installDir = await mkdtemp2(join31(runtimeParent, ".calldiff-install-"));
99188
99438
  const backupDir = `${runtimeDir}.previous-${process.pid}-${Date.now()}`;
99189
99439
  try {
99190
99440
  writeRuntimeManifest(installDir);
99191
- await downloadVerifiedCallDiffArchive(join30(installDir, CALLDIFF_ARCHIVE_FILE), onStage);
99441
+ await downloadVerifiedCallDiffArchive(join31(installDir, CALLDIFF_ARCHIVE_FILE), onStage);
99192
99442
  onStage("installing-deps");
99193
99443
  const install = await runCommand(npmPath, [
99194
99444
  "ci",
@@ -99222,8 +99472,8 @@ async function installCallFlowRuntimeUnlocked(onStage) {
99222
99472
  throw new Error(rebuild.stderr.trim() || rebuild.stdout.trim() || "grammar rebuild failed");
99223
99473
  }
99224
99474
  onStage("building");
99225
- const packageRoot = join30(installDir, "node_modules", "calldiff");
99226
- const tscPath = process.platform === "win32" ? join30(installDir, "node_modules", ".bin", "tsc.cmd") : join30(installDir, "node_modules", ".bin", "tsc");
99475
+ const packageRoot = join31(installDir, "node_modules", "calldiff");
99476
+ const tscPath = process.platform === "win32" ? join31(installDir, "node_modules", ".bin", "tsc.cmd") : join31(installDir, "node_modules", ".bin", "tsc");
99227
99477
  const build = await runCommand(tscPath, ["-p", "tsconfig.json"], {
99228
99478
  cwd: packageRoot,
99229
99479
  timeoutMs: 120000,
@@ -99232,8 +99482,8 @@ async function installCallFlowRuntimeUnlocked(onStage) {
99232
99482
  if (build.exitCode !== 0) {
99233
99483
  throw new Error(build.stderr.trim() || build.stdout.trim() || "CallDiff TypeScript build failed");
99234
99484
  }
99235
- rmSync4(join30(installDir, CALLDIFF_ARCHIVE_FILE), { force: true });
99236
- writeFileSync12(join30(installDir, ".calldiff-revision"), `${CALLDIFF_COMMIT}
99485
+ rmSync4(join31(installDir, CALLDIFF_ARCHIVE_FILE), { force: true });
99486
+ writeFileSync12(join31(installDir, ".calldiff-revision"), `${CALLDIFF_COMMIT}
99237
99487
  `, "utf8");
99238
99488
  if (readPackageVersion(packageRoot) !== CALLDIFF_VERSION || !resolvePackageEntry(packageRoot)) {
99239
99489
  throw new Error("The verified CallDiff package did not produce the expected runtime entry.");
@@ -99246,18 +99496,19 @@ async function installCallFlowRuntimeUnlocked(onStage) {
99246
99496
  await validatePrunedLanguage(nodePath, installDir, getCallFlowGrammarCacheDir(installDir), [CALL_FLOW_CORE_LANGUAGE_ID]);
99247
99497
  const sizeAfterPruneBytes = measureCallFlowDirectoryBytes(installDir);
99248
99498
  const nextCache = getCallFlowGrammarCacheDir(installDir);
99249
- await preserveValidatedLanguagePacks(nodePath, runtimeDir, installDir);
99499
+ await preserveValidatedLanguagePacks(nodePath, [runtimeDir, ...listStaleCallFlowRuntimeDirs()], installDir);
99250
99500
  ensureGrammarCacheManifest(nextCache);
99251
- if (existsSync17(runtimeDir))
99501
+ if (existsSync18(runtimeDir))
99252
99502
  renameSync9(runtimeDir, backupDir);
99253
99503
  try {
99254
99504
  renameSync9(installDir, runtimeDir);
99255
99505
  } catch (error) {
99256
- if (existsSync17(backupDir))
99506
+ if (existsSync18(backupDir))
99257
99507
  renameSync9(backupDir, runtimeDir);
99258
99508
  throw error;
99259
99509
  }
99260
99510
  removeDirectoryBestEffort2(backupDir);
99511
+ removeStaleCallFlowRuntimeDirs();
99261
99512
  return { ok: true, status: "installed", runtimeDir, languageId: CALL_FLOW_CORE_LANGUAGE_ID, sizeBeforePruneBytes, sizeAfterPruneBytes, message: `Installed the pruned CallDiff ${CALLDIFF_VERSION} core at ${runtimeDir}.` };
99262
99513
  } catch (error) {
99263
99514
  return {
@@ -99292,7 +99543,7 @@ async function installCallFlowLanguagePackUnlocked(id, onStage) {
99292
99543
  const nodePath = findExecutable("node");
99293
99544
  const npmPath = findExecutable("npm");
99294
99545
  if (!nodePath || !npmPath) {
99295
- return { ok: false, status: "failed", runtimeDir, message: "Call-flow language packs require Node.js 22+ and npm." };
99546
+ return { ok: false, status: "failed", runtimeDir, message: `Call flow language support needs ${CALL_FLOW_NODE_REQUIREMENT} and npm on PATH.` };
99296
99547
  }
99297
99548
  const runtime = await resolveCallFlowRuntime();
99298
99549
  if (!runtime.ok || !runtime.runtime.managed || runtime.runtime.runtimeDir !== runtimeDir) {
@@ -99301,17 +99552,17 @@ async function installCallFlowLanguagePackUnlocked(id, onStage) {
99301
99552
  if (runtime.runtime.installedLanguageIds.includes(id)) {
99302
99553
  return { ok: true, status: "already-installed", runtimeDir, languageId: id, message: `${language.label} support is already installed.` };
99303
99554
  }
99304
- const runtimeParent = dirname11(runtimeDir);
99305
- const installDir = await mkdtemp2(join30(runtimeParent, `.calldiff-${id}-install-`));
99306
- const stagingCache = await mkdtemp2(join30(runtimeDir, `.grammar-cache-${id}-`));
99307
- const packageRoot = join30(installDir, "node_modules", ...(language.packageName ?? "").split("/"));
99308
- const stagedPackageRoot = join30(stagingCache, "node_modules", ...(language.packageName ?? "").split("/"));
99555
+ const runtimeParent = dirname12(runtimeDir);
99556
+ const installDir = await mkdtemp2(join31(runtimeParent, `.calldiff-${id}-install-`));
99557
+ const stagingCache = await mkdtemp2(join31(runtimeDir, `.grammar-cache-${id}-`));
99558
+ const packageRoot = join31(installDir, "node_modules", ...(language.packageName ?? "").split("/"));
99559
+ const stagedPackageRoot = join31(stagingCache, "node_modules", ...(language.packageName ?? "").split("/"));
99309
99560
  const finalPackageRoot = getLanguagePackageRoot(runtimeDir, language);
99310
99561
  const backupPackageRoot = `${finalPackageRoot}.previous-${process.pid}-${Date.now()}`;
99311
99562
  try {
99312
- writeFileSync12(join30(installDir, "package.json"), runtimeJson(packManifest(id)), "utf8");
99313
- writeFileSync12(join30(installDir, "package-lock.json"), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
99314
- const verifiedArtifacts = await downloadVerifiedLockArtifacts(CALL_FLOW_PACK_LOCKS[id], join30(installDir, ".verified-artifacts"), onStage);
99563
+ writeFileSync12(join31(installDir, "package.json"), runtimeJson(packManifest(id)), "utf8");
99564
+ writeFileSync12(join31(installDir, "package-lock.json"), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
99565
+ const verifiedArtifacts = await downloadVerifiedLockArtifacts(CALL_FLOW_PACK_LOCKS[id], join31(installDir, ".verified-artifacts"), onStage);
99315
99566
  const npmCacheDir = await seedVerifiedNpmCache(npmPath, installDir, verifiedArtifacts);
99316
99567
  onStage("installing-deps");
99317
99568
  const install = await runCommand(npmPath, [
@@ -99362,21 +99613,21 @@ async function installCallFlowLanguagePackUnlocked(id, onStage) {
99362
99613
  const sizeBeforePruneBytes = measureCallFlowDirectoryBytes(packageRoot);
99363
99614
  pruneCallFlowNativePackage(packageRoot);
99364
99615
  const sizeAfterPruneBytes = measureCallFlowDirectoryBytes(packageRoot);
99365
- mkdirSync14(dirname11(stagedPackageRoot), { recursive: true });
99616
+ mkdirSync14(dirname12(stagedPackageRoot), { recursive: true });
99366
99617
  cpSync(packageRoot, stagedPackageRoot, { recursive: true });
99367
99618
  ensureGrammarCacheManifest(stagingCache);
99368
99619
  await validatePrunedLanguage(nodePath, runtimeDir, stagingCache, [id]);
99369
99620
  ensureGrammarCacheManifest(getCallFlowGrammarCacheDir(runtimeDir));
99370
- mkdirSync14(dirname11(finalPackageRoot), { recursive: true });
99371
- if (existsSync17(finalPackageRoot))
99621
+ mkdirSync14(dirname12(finalPackageRoot), { recursive: true });
99622
+ if (existsSync18(finalPackageRoot))
99372
99623
  renameSync9(finalPackageRoot, backupPackageRoot);
99373
99624
  try {
99374
99625
  renameSync9(stagedPackageRoot, finalPackageRoot);
99375
- mkdirSync14(dirname11(getLanguageLockMarker(runtimeDir, id)), { recursive: true });
99626
+ mkdirSync14(dirname12(getLanguageLockMarker(runtimeDir, id)), { recursive: true });
99376
99627
  writeFileSync12(getLanguageLockMarker(runtimeDir, id), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
99377
99628
  } catch (error) {
99378
99629
  removeDirectoryBestEffort2(finalPackageRoot);
99379
- if (existsSync17(backupPackageRoot))
99630
+ if (existsSync18(backupPackageRoot))
99380
99631
  renameSync9(backupPackageRoot, finalPackageRoot);
99381
99632
  throw error;
99382
99633
  }
@@ -99487,15 +99738,15 @@ async function runCommand(executable, args, options = {}) {
99487
99738
  }
99488
99739
  function grammarChecksForLanguages(runtime, ids) {
99489
99740
  const checks = new Map;
99490
- const callDiffRoot = runtime.managed ? join30(runtime.runtimeDir, "node_modules", "calldiff") : runtime.runtimeDir;
99741
+ const callDiffRoot = runtime.managed ? join31(runtime.runtimeDir, "node_modules", "calldiff") : runtime.runtimeDir;
99491
99742
  checks.set("calldiff", {
99492
- packageJson: join30(callDiffRoot, "package.json"),
99743
+ packageJson: join31(callDiffRoot, "package.json"),
99493
99744
  version: CALLDIFF_VERSION,
99494
99745
  message: `CallDiff ${CALLDIFF_VERSION} is not installed.`
99495
99746
  });
99496
99747
  for (const dependency of CORE_RUNTIME_DEPENDENCIES) {
99497
99748
  checks.set(dependency.packageName, {
99498
- packageJson: join30(runtime.runtimeDir, "node_modules", dependency.packageName, "package.json"),
99749
+ packageJson: join31(runtime.runtimeDir, "node_modules", dependency.packageName, "package.json"),
99499
99750
  version: dependency.version,
99500
99751
  message: `${dependency.label} support is not installed at the required version.`
99501
99752
  });
@@ -99504,9 +99755,9 @@ function grammarChecksForLanguages(runtime, ids) {
99504
99755
  const language = getCallFlowLanguage(id);
99505
99756
  if (language.kind !== "pack" || !language.packageName || !language.packageVersion)
99506
99757
  continue;
99507
- const packageRoot = runtime.managed ? getLanguagePackageRoot(runtime.runtimeDir, language) : join30(runtime.runtimeDir, "node_modules", ...language.packageName.split("/"));
99758
+ const packageRoot = runtime.managed ? getLanguagePackageRoot(runtime.runtimeDir, language) : join31(runtime.runtimeDir, "node_modules", ...language.packageName.split("/"));
99508
99759
  checks.set(language.packageName, {
99509
- packageJson: join30(packageRoot, "package.json"),
99760
+ packageJson: join31(packageRoot, "package.json"),
99510
99761
  version: language.packageVersion,
99511
99762
  message: `${language.label} support is not installed.`
99512
99763
  });
@@ -99514,14 +99765,14 @@ function grammarChecksForLanguages(runtime, ids) {
99514
99765
  return [...checks.values()];
99515
99766
  }
99516
99767
  async function createNpmBlocker() {
99517
- const root = await mkdtemp2(join30(tmpdir4(), "plannotator-call-flow-path-"));
99768
+ const root = await mkdtemp2(join31(tmpdir4(), "plannotator-call-flow-path-"));
99518
99769
  if (process.platform === "win32") {
99519
- writeFileSync12(join30(root, "npm.cmd"), `@echo CallDiff package installation is disabled during analysis. 1>&2\r
99770
+ writeFileSync12(join31(root, "npm.cmd"), `@echo CallDiff package installation is disabled during analysis. 1>&2\r
99520
99771
  @exit /b 91\r
99521
99772
  `, "utf8");
99522
- writeFileSync12(join30(root, "npm.exe"), "", "utf8");
99773
+ writeFileSync12(join31(root, "npm.exe"), "", "utf8");
99523
99774
  } else {
99524
- const blocker = join30(root, "npm");
99775
+ const blocker = join31(root, "npm");
99525
99776
  writeFileSync12(blocker, `#!/bin/sh
99526
99777
  echo 'CallDiff package installation is disabled during analysis.' >&2
99527
99778
  exit 91
@@ -99884,7 +100135,7 @@ class CallFlowService {
99884
100135
  }
99885
100136
  }
99886
100137
  }
99887
- var CALLDIFF_VERSION = "0.4.1", CALLDIFF_COMMIT = "a3194d20ca91ef6a314273d634e9b9c0db1c2707", CALLDIFF_SOURCE_SPEC, CALLDIFF_SOURCE_INTEGRITY = "sha512-5y6tjre5UE00qPrVFPurnk9RBdk8WlRok+0w41lxLt1MyPLOlNUEaAT2/j/nz2xWlhDm3DfDYvW/5KBD2b9TLg==", CALLDIFF_TREE_SITTER_VERSION = "0.25.1", CALLDIFF_ARCHIVE_FILE, MAX_CALLDIFF_ARCHIVE_BYTES, MAX_PACK_ARCHIVES_BYTES, CORE_GRAMMAR_PACKAGES, CORE_RUNTIME_DEPENDENCIES, CORE_PRUNED_PACKAGES, WORKER_TIMEOUT_MS = 45000, GIT_TIMEOUT_MS = 20000, MAX_PROCESS_OUTPUT_BYTES, CallFlowMissingGrammarError, WORKER_SOURCE, PRUNED_PACKAGE_DIRECTORIES;
100138
+ var CALLDIFF_VERSION = "0.6.0", CALLDIFF_COMMIT = "2086f6e4e9ed3c410bba7c94ca3dde77791b1956", CALLDIFF_SOURCE_SPEC, CALLDIFF_SOURCE_INTEGRITY = "sha512-t+YCPz0WkETiwSOznwx3mLxVkEUe7DVACV+it2HJER7q/ha+K0IavN9IfBvP+7/ijzPXoUHgJIkAM/kg1O5S6A==", CALLDIFF_TREE_SITTER_VERSION = "0.25.1", CALL_FLOW_MIN_NODE_VERSION, CALL_FLOW_NODE_REQUIREMENT = "Node.js 22.18 or newer", CALLDIFF_ARCHIVE_FILE, MAX_CALLDIFF_ARCHIVE_BYTES, MAX_PACK_ARCHIVES_BYTES, CORE_GRAMMAR_PACKAGES, CORE_RUNTIME_DEPENDENCIES, CORE_PRUNED_PACKAGES, WORKER_TIMEOUT_MS = 45000, GIT_TIMEOUT_MS = 20000, MAX_PROCESS_OUTPUT_BYTES, CallFlowMissingGrammarError, WORKER_SOURCE, PRUNED_PACKAGE_DIRECTORIES, MANAGED_CORE_DIR_PATTERN, STALE_TOMBSTONE_PATTERN;
99888
100139
  var init_call_flow = __esm(() => {
99889
100140
  init_package();
99890
100141
  init_package_lock();
@@ -99895,6 +100146,7 @@ var init_call_flow = __esm(() => {
99895
100146
  init_review_core();
99896
100147
  init_call_flow_types();
99897
100148
  CALLDIFF_SOURCE_SPEC = `https://github.com/tanishqkancharla/calldiff/archive/${CALLDIFF_COMMIT}.tar.gz`;
100149
+ CALL_FLOW_MIN_NODE_VERSION = { major: 22, minor: 18 };
99898
100150
  CALLDIFF_ARCHIVE_FILE = `calldiff-${CALLDIFF_COMMIT}.tar.gz`;
99899
100151
  MAX_CALLDIFF_ARCHIVE_BYTES = 20 * 1024 * 1024;
99900
100152
  MAX_PACK_ARCHIVES_BYTES = 256 * 1024 * 1024;
@@ -99978,6 +100230,8 @@ try {
99978
100230
  "typescript",
99979
100231
  "tsx"
99980
100232
  ];
100233
+ MANAGED_CORE_DIR_PATTERN = /^calldiff-(\d+)\.(\d+)\.(\d+)(\.previous-\d+-\d+)?$/;
100234
+ STALE_TOMBSTONE_PATTERN = /^\.calldiff-stale-\d+-\d+-\d+$/;
99981
100235
  });
99982
100236
 
99983
100237
  // ../../packages/shared/call-flow-install.ts
@@ -100984,15 +101238,16 @@ var init_claude_review = __esm(() => {
100984
101238
  "Bash(glab api *)",
100985
101239
  "Bash(jj --config*)",
100986
101240
  "Bash(jj * --config*)",
100987
- "Bash(jj * --tool*)"
101241
+ "Bash(jj * --tool*)",
101242
+ "mcp__plannotator-inbox"
100988
101243
  ];
100989
101244
  CLAUDE_SHELL_BLOCKED_WARNING = "Claude Code refused every shell command this job tried, so it could not inspect the repository with git. " + "If your Claude Code settings enable its sandbox and the sandbox cannot start on this machine " + "(for example Linux without bubblewrap and socat), set PLANNOTATOR_CLAUDE_SANDBOX=0 or " + '{ "claudeSandbox": false } in ~/.plannotator/config.json and run the job again.';
100990
101245
  });
100991
101246
 
100992
101247
  // ../../packages/shared/agent-program.ts
100993
101248
  import { spawn as spawn8 } from "child_process";
100994
- import { existsSync as existsSync18, realpathSync as realpathSync10, statSync as statSync20 } from "fs";
100995
- import { dirname as dirname12, join as join31 } from "path";
101249
+ import { existsSync as existsSync19, realpathSync as realpathSync10, statSync as statSync21 } from "fs";
101250
+ import { dirname as dirname13, join as join32 } from "path";
100996
101251
  function killTree(pid) {
100997
101252
  if (pid === undefined)
100998
101253
  return;
@@ -101045,8 +101300,8 @@ function readVersionLine(path, timeoutMs = VERSION_PROBE_TIMEOUT_MS) {
101045
101300
  }
101046
101301
  function inInstallLayout(real, realName, platform) {
101047
101302
  if (platform === "win32") {
101048
- const dir = dirname12(real);
101049
- return existsSync18(join31(dir, `${realName}.exe`)) || existsSync18(join31(dir, `${realName}.cmd`));
101303
+ const dir = dirname13(real);
101304
+ return existsSync19(join32(dir, `${realName}.exe`)) || existsSync19(join32(dir, `${realName}.cmd`));
101050
101305
  }
101051
101306
  const parts = real.split("/");
101052
101307
  const n = parts.length;
@@ -101077,7 +101332,7 @@ var CURSOR_CLI_SIGNATURE, VERSION_PROBE_TIMEOUT_MS = 2000, MAX_VERSION_OUTPUT, W
101077
101332
  return false;
101078
101333
  let key;
101079
101334
  try {
101080
- const st = statSync20(real);
101335
+ const st = statSync21(real);
101081
101336
  if (!st.isFile())
101082
101337
  return false;
101083
101338
  key = [real, st.size, st.mtimeMs, signature.versionLine.source].join("\x00");
@@ -101877,10 +102132,10 @@ var init_guide2 = __esm(() => {
101877
102132
  });
101878
102133
 
101879
102134
  // ../../packages/shared/guide-instructions-store.ts
101880
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync20, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "fs";
101881
- import { dirname as dirname13, join as join32 } from "path";
102135
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync21, unlinkSync as unlinkSync9, writeFileSync as writeFileSync13 } from "fs";
102136
+ import { dirname as dirname14, join as join33 } from "path";
101882
102137
  function instructionsPath() {
101883
- return join32(getPlannotatorDataDir(), "guide-instructions.md");
102138
+ return join33(getPlannotatorDataDir(), "guide-instructions.md");
101884
102139
  }
101885
102140
  function bound(value) {
101886
102141
  const trimmed = value.trim();
@@ -101888,7 +102143,7 @@ function bound(value) {
101888
102143
  }
101889
102144
  function readGuideInstructions() {
101890
102145
  try {
101891
- return bound(readFileSync20(instructionsPath(), "utf8"));
102146
+ return bound(readFileSync21(instructionsPath(), "utf8"));
101892
102147
  } catch {
101893
102148
  return "";
101894
102149
  }
@@ -101902,7 +102157,7 @@ function writeGuideInstructions(value) {
101902
102157
  } catch {}
101903
102158
  return "";
101904
102159
  }
101905
- mkdirSync15(dirname13(path), { recursive: true });
102160
+ mkdirSync15(dirname14(path), { recursive: true });
101906
102161
  writeFileSync13(path, bounded + `
101907
102162
  `, "utf8");
101908
102163
  return bounded;
@@ -102034,7 +102289,7 @@ function createAgentJobHandler(options) {
102034
102289
  stdout: captureStdout ? "pipe" : "ignore",
102035
102290
  stderr: "pipe",
102036
102291
  env: {
102037
- ...process.env,
102292
+ ...askAiChildEnv(),
102038
102293
  ...gitColorFreeEnvironment(process.env),
102039
102294
  PWD: resolvePath3(spawnCwd),
102040
102295
  PLANNOTATOR_AGENT_SOURCE: source,
@@ -102467,6 +102722,7 @@ function createAgentJobHandler(options) {
102467
102722
  }
102468
102723
  var BASE2 = "/api/agents", JOBS, JOBS_STREAM, CAPABILITIES, SERVER_BUILT_PROVIDERS;
102469
102724
  var init_agent_jobs2 = __esm(() => {
102725
+ init_ask_ai_env();
102470
102726
  init_claude_review();
102471
102727
  init_marker_review();
102472
102728
  init_agent_jobs();
@@ -102491,10 +102747,10 @@ var init_agent_jobs2 = __esm(() => {
102491
102747
  });
102492
102748
 
102493
102749
  // ../../packages/server/codex-review.ts
102494
- import { dirname as dirname14, join as join33 } from "path";
102750
+ import { dirname as dirname15, join as join34 } from "path";
102495
102751
  import { tmpdir as tmpdir5 } from "os";
102496
102752
  import { appendFile, mkdir, unlink, writeFile, readFile as readFile4 } from "fs/promises";
102497
- import { existsSync as existsSync19 } from "fs";
102753
+ import { existsSync as existsSync20 } from "fs";
102498
102754
  async function debugLog(label, data) {
102499
102755
  if (!DEBUG_ENABLED)
102500
102756
  return;
@@ -102505,16 +102761,16 @@ async function debugLog(label, data) {
102505
102761
  const line = data !== undefined ? `[${timestamp}] ${label}: ${typeof data === "string" ? data : JSON.stringify(data, null, 2)}
102506
102762
  ` : `[${timestamp}] ${label}
102507
102763
  `;
102508
- await appendFile(join33(dataDir, "codex-review-debug.log"), line);
102764
+ await appendFile(join34(dataDir, "codex-review-debug.log"), line);
102509
102765
  } catch {}
102510
102766
  }
102511
102767
  function getCodexReviewSchemaPath() {
102512
- return join33(getPlannotatorDataDir(), "codex-review-schema.json");
102768
+ return join34(getPlannotatorDataDir(), "codex-review-schema.json");
102513
102769
  }
102514
102770
  async function ensureSchemaFile() {
102515
102771
  const schemaPath = getCodexReviewSchemaPath();
102516
102772
  if (!materializedSchemaPaths.has(schemaPath)) {
102517
- await mkdir(dirname14(schemaPath), { recursive: true });
102773
+ await mkdir(dirname15(schemaPath), { recursive: true });
102518
102774
  await writeFile(schemaPath, CODEX_REVIEW_SCHEMA);
102519
102775
  materializedSchemaPaths.add(schemaPath);
102520
102776
  }
@@ -102531,6 +102787,7 @@ async function buildCodexCommand(options) {
102531
102787
  ...model ? ["-m", model] : [],
102532
102788
  ...reasoningEffort ? ["-c", `model_reasoning_effort=${reasoningEffort}`] : [],
102533
102789
  ...fastMode ? ["-c", "service_tier=fast"] : [],
102790
+ ...codexAskAiConfigArgs(readCodexConfigTexts(cwd)),
102534
102791
  "exec",
102535
102792
  "--output-schema",
102536
102793
  schemaPath,
@@ -102552,12 +102809,12 @@ async function buildCodexCommand(options) {
102552
102809
  return command;
102553
102810
  }
102554
102811
  function generateOutputPath() {
102555
- return join33(tmpdir5(), `plannotator-codex-${crypto.randomUUID()}.json`);
102812
+ return join34(tmpdir5(), `plannotator-codex-${crypto.randomUUID()}.json`);
102556
102813
  }
102557
102814
  async function parseCodexOutput(outputPath) {
102558
102815
  await debugLog("PARSE_OUTPUT_START", { outputPath });
102559
102816
  try {
102560
- if (!existsSync19(outputPath)) {
102817
+ if (!existsSync20(outputPath)) {
102561
102818
  await debugLog("PARSE_OUTPUT_FILE_MISSING", outputPath);
102562
102819
  return null;
102563
102820
  }
@@ -102684,6 +102941,7 @@ var init_codex_review = __esm(() => {
102684
102941
  init_data_dir();
102685
102942
  init_review_profiles();
102686
102943
  init_external_annotation2();
102944
+ init_ask_ai_env();
102687
102945
  DEBUG_ENABLED = !!process.env.PLANNOTATOR_DEBUG;
102688
102946
  CODEX_REVIEW_SCHEMA = JSON.stringify({
102689
102947
  type: "object",
@@ -103584,7 +103842,7 @@ var init_pr_github = __esm(() => {
103584
103842
  });
103585
103843
 
103586
103844
  // ../../packages/shared/pr-gitlab.ts
103587
- import { join as join34 } from "path";
103845
+ import { join as join35 } from "path";
103588
103846
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
103589
103847
  function encodeProject(projectPath) {
103590
103848
  return encodeURIComponent(projectPath);
@@ -104101,10 +104359,10 @@ async function submitGlMRReview(runtime, ref, headSha, action, body, fileComment
104101
104359
  const failed = failedFileComments.map((failure) => failure.comment);
104102
104360
  let savedTo = null;
104103
104361
  try {
104104
- const dir = join34(getPlannotatorDataDir(), "failed-comments");
104362
+ const dir = join35(getPlannotatorDataDir(), "failed-comments");
104105
104363
  mkdirSync16(dir, { recursive: true });
104106
104364
  const slug = `${ref.host}-${ref.projectPath.replace(/\//g, "_")}-mr${ref.iid}-${Date.now()}`;
104107
- savedTo = join34(dir, `${slug}.json`);
104365
+ savedTo = join35(dir, `${slug}.json`);
104108
104366
  writeFileSync14(savedTo, JSON.stringify({ ref, headSha, baseSha, startSha, errors, failedComments: failed }, null, 2));
104109
104367
  } catch (writeErr) {
104110
104368
  console.error(`[plannotator] Failed to persist unposted comments: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
@@ -104163,7 +104421,7 @@ var init_pr_gitlab = __esm(() => {
104163
104421
  });
104164
104422
 
104165
104423
  // ../../packages/shared/pr-bitbucket.ts
104166
- import { join as join35 } from "path";
104424
+ import { join as join36 } from "path";
104167
104425
  import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "fs";
104168
104426
  function context(runtime) {
104169
104427
  const apiBase = resolveBitbucketApiUrl();
@@ -104644,10 +104902,10 @@ async function submitBbPRReview(runtime, ref, headSha, action, body, fileComment
104644
104902
  const errors = failedFileComments.map((f) => f.error);
104645
104903
  let savedTo = null;
104646
104904
  try {
104647
- const dir = join35(getPlannotatorDataDir(), "failed-comments");
104905
+ const dir = join36(getPlannotatorDataDir(), "failed-comments");
104648
104906
  mkdirSync17(dir, { recursive: true });
104649
104907
  const slug = `${ref.host}-${ref.workspace}_${ref.repo}-pr${ref.number}-${Date.now()}`;
104650
- savedTo = join35(dir, `${slug}.json`);
104908
+ savedTo = join36(dir, `${slug}.json`);
104651
104909
  writeFileSync15(savedTo, JSON.stringify({ ref, headSha, errors, failedComments: failedFileComments.map((f) => f.comment) }, null, 2));
104652
104910
  } catch (writeErr) {
104653
104911
  console.error(`[plannotator] Failed to persist unposted comments: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
@@ -105201,7 +105459,7 @@ var init_agent_review_message = __esm(() => {
105201
105459
  });
105202
105460
 
105203
105461
  // ../../packages/server/tour/tour-review.ts
105204
- import { dirname as dirname15, join as join36 } from "path";
105462
+ import { dirname as dirname16, join as join37 } from "path";
105205
105463
  import { tmpdir as tmpdir6 } from "os";
105206
105464
  import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile5, unlink as unlink2 } from "fs/promises";
105207
105465
  function buildTourUserMessage(patch, diffType, options, prMetadata) {
@@ -105297,19 +105555,19 @@ function buildTourClaudeCommand(prompt, model = "sonnet", effort, opts) {
105297
105555
  };
105298
105556
  }
105299
105557
  function tourSchemaPath() {
105300
- return join36(getPlannotatorDataDir(), "tour-schema.json");
105558
+ return join37(getPlannotatorDataDir(), "tour-schema.json");
105301
105559
  }
105302
105560
  async function ensureTourSchemaFile() {
105303
105561
  const schemaPath = tourSchemaPath();
105304
105562
  if (!materializedTourSchemaPaths.has(schemaPath)) {
105305
- await mkdir2(dirname15(schemaPath), { recursive: true });
105563
+ await mkdir2(dirname16(schemaPath), { recursive: true });
105306
105564
  await writeFile2(schemaPath, TOUR_SCHEMA_JSON);
105307
105565
  materializedTourSchemaPaths.add(schemaPath);
105308
105566
  }
105309
105567
  return schemaPath;
105310
105568
  }
105311
105569
  function generateTourOutputPath() {
105312
- return join36(tmpdir6(), `plannotator-tour-${crypto.randomUUID()}.json`);
105570
+ return join37(tmpdir6(), `plannotator-tour-${crypto.randomUUID()}.json`);
105313
105571
  }
105314
105572
  async function buildTourCodexCommand(options) {
105315
105573
  const { cwd, outputPath, prompt, model, reasoningEffort, fastMode } = options;
@@ -105319,6 +105577,7 @@ async function buildTourCodexCommand(options) {
105319
105577
  ...model ? ["-m", model] : [],
105320
105578
  ...reasoningEffort ? ["-c", `model_reasoning_effort=${reasoningEffort}`] : [],
105321
105579
  ...fastMode ? ["-c", "service_tier=fast"] : [],
105580
+ ...codexAskAiConfigArgs(readCodexConfigTexts(cwd)),
105322
105581
  "exec",
105323
105582
  "--output-schema",
105324
105583
  schemaPath,
@@ -105628,6 +105887,7 @@ callouts. The primary question is "what does this change do and why?" not
105628
105887
  "what's wrong with this code?"`, materializedTourSchemaPaths;
105629
105888
  var init_tour_review = __esm(() => {
105630
105889
  init_data_dir();
105890
+ init_ask_ai_env();
105631
105891
  init_pr();
105632
105892
  init_agent_review_message();
105633
105893
  init_claude_review();
@@ -106131,7 +106391,7 @@ var init_guide_prompt2 = __esm(() => {
106131
106391
  });
106132
106392
 
106133
106393
  // ../../packages/server/guide/guide-review.ts
106134
- import { dirname as dirname16, join as join37 } from "path";
106394
+ import { dirname as dirname17, join as join38 } from "path";
106135
106395
  import { tmpdir as tmpdir7 } from "os";
106136
106396
  import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile6, unlink as unlink3 } from "fs/promises";
106137
106397
  function composeGuideMethodology(extraInstructions) {
@@ -106176,19 +106436,19 @@ function buildGuideClaudeCommand(prompt, model = "sonnet", effort, opts) {
106176
106436
  };
106177
106437
  }
106178
106438
  function guideSchemaPath() {
106179
- return join37(getPlannotatorDataDir(), "guide-schema.json");
106439
+ return join38(getPlannotatorDataDir(), "guide-schema.json");
106180
106440
  }
106181
106441
  async function ensureGuideSchemaFile() {
106182
106442
  const schemaPath = guideSchemaPath();
106183
106443
  if (!materializedGuideSchemaPaths.has(schemaPath)) {
106184
- await mkdir3(dirname16(schemaPath), { recursive: true });
106444
+ await mkdir3(dirname17(schemaPath), { recursive: true });
106185
106445
  await writeFile3(schemaPath, GUIDE_SCHEMA_JSON);
106186
106446
  materializedGuideSchemaPaths.add(schemaPath);
106187
106447
  }
106188
106448
  return schemaPath;
106189
106449
  }
106190
106450
  function generateGuideOutputPath() {
106191
- return join37(tmpdir7(), `plannotator-guide-${crypto.randomUUID()}.json`);
106451
+ return join38(tmpdir7(), `plannotator-guide-${crypto.randomUUID()}.json`);
106192
106452
  }
106193
106453
  async function buildGuideCodexCommand(options) {
106194
106454
  const { cwd, outputPath, prompt, model, reasoningEffort, fastMode } = options;
@@ -106198,6 +106458,7 @@ async function buildGuideCodexCommand(options) {
106198
106458
  ...model ? ["-m", model] : [],
106199
106459
  ...reasoningEffort ? ["-c", `model_reasoning_effort=${reasoningEffort}`] : [],
106200
106460
  ...fastMode ? ["-c", "service_tier=fast"] : [],
106461
+ ...codexAskAiConfigArgs(readCodexConfigTexts(cwd)),
106201
106462
  "exec",
106202
106463
  "--output-schema",
106203
106464
  schemaPath,
@@ -106662,6 +106923,7 @@ function createGuideSession() {
106662
106923
  var GUIDE_EMPTY_OUTPUT_ERROR = "Guide generation returned empty or malformed output", materializedGuideSchemaPaths, MAX_FAILED_PAYLOAD_CHARS = 200000, MAX_LAUNCH_REVIEWS = 20;
106663
106924
  var init_guide_review = __esm(() => {
106664
106925
  init_data_dir();
106926
+ init_ask_ai_env();
106665
106927
  init_config2();
106666
106928
  init_claude_review();
106667
106929
  init_guide_prompt2();
@@ -107187,8 +107449,8 @@ var init_guide_share = __esm(() => {
107187
107449
  });
107188
107450
 
107189
107451
  // ../../packages/shared/guide-store.ts
107190
- import { join as join38, resolve as resolve17, basename as basename10 } from "path";
107191
- import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16, readFileSync as readFileSync21, readdirSync as readdirSync7, renameSync as renameSync10, unlinkSync as unlinkSync10, existsSync as existsSync20 } from "fs";
107452
+ import { join as join39, resolve as resolve18, basename as basename10 } from "path";
107453
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16, readFileSync as readFileSync22, readdirSync as readdirSync7, renameSync as renameSync10, unlinkSync as unlinkSync10, existsSync as existsSync21 } from "fs";
107192
107454
  import { createHash as createHash7 } from "crypto";
107193
107455
  function sanitizeKeySegment(segment) {
107194
107456
  const cleaned = segment.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "");
@@ -107215,7 +107477,7 @@ function deriveGuideRepoKeyFromPRUrl(prUrl) {
107215
107477
  return [ref.host, ...path.split("/").filter(Boolean)].map(sanitizeKeySegment).join("__");
107216
107478
  }
107217
107479
  function deriveGuideRepoKeyFallback(dirPath) {
107218
- const resolved = resolve17(dirPath);
107480
+ const resolved = resolve18(dirPath);
107219
107481
  const name = sanitizeKeySegment(basename10(resolved) || "repo");
107220
107482
  return `${name}-${hash8(resolved)}`;
107221
107483
  }
@@ -107227,13 +107489,13 @@ function makeGuideId(title, now = Date.now()) {
107227
107489
  return `${now}-${slug || "guide"}`;
107228
107490
  }
107229
107491
  function guidesDir(repoKey) {
107230
- return join38(getPlannotatorDataDir(), "guides", repoKey);
107492
+ return join39(getPlannotatorDataDir(), "guides", repoKey);
107231
107493
  }
107232
107494
  function guidePath(repoKey, id) {
107233
- return join38(guidesDir(repoKey), `${id}.json`);
107495
+ return join39(guidesDir(repoKey), `${id}.json`);
107234
107496
  }
107235
107497
  function guidePatchPath(repoKey, id) {
107236
- return join38(guidesDir(repoKey), `${id}.patch`);
107498
+ return join39(guidesDir(repoKey), `${id}.patch`);
107237
107499
  }
107238
107500
  function coerceReviewed(value, sectionCount) {
107239
107501
  const out = new Array(sectionCount).fill(false);
@@ -107361,10 +107623,10 @@ function loadGuidePatch(repoKey, envelope) {
107361
107623
  if (!envelope.review)
107362
107624
  return null;
107363
107625
  try {
107364
- const filePath = join38(guidesDir(repoKey), envelope.review.patchFile);
107365
- if (!existsSync20(filePath))
107626
+ const filePath = join39(guidesDir(repoKey), envelope.review.patchFile);
107627
+ if (!existsSync21(filePath))
107366
107628
  return null;
107367
- return readFileSync21(filePath, "utf-8");
107629
+ return readFileSync22(filePath, "utf-8");
107368
107630
  } catch {
107369
107631
  return null;
107370
107632
  }
@@ -107396,9 +107658,9 @@ function loadGuide(repoKey, id) {
107396
107658
  return null;
107397
107659
  try {
107398
107660
  const filePath = guidePath(repoKey, id);
107399
- if (!existsSync20(filePath))
107661
+ if (!existsSync21(filePath))
107400
107662
  return null;
107401
- return parseEnvelope(readFileSync21(filePath, "utf-8"));
107663
+ return parseEnvelope(readFileSync22(filePath, "utf-8"));
107402
107664
  } catch {
107403
107665
  return null;
107404
107666
  }
@@ -107445,12 +107707,12 @@ function deleteGuide(repoKey, id) {
107445
107707
  return false;
107446
107708
  try {
107447
107709
  const filePath = guidePath(repoKey, id);
107448
- if (!existsSync20(filePath))
107710
+ if (!existsSync21(filePath))
107449
107711
  return false;
107450
107712
  unlinkSync10(filePath);
107451
107713
  try {
107452
107714
  const patchPath = guidePatchPath(repoKey, id);
107453
- if (existsSync20(patchPath))
107715
+ if (existsSync21(patchPath))
107454
107716
  unlinkSync10(patchPath);
107455
107717
  } catch {}
107456
107718
  return true;
@@ -107649,10 +107911,10 @@ var init_guide_store = __esm(() => {
107649
107911
  var GUIDE_VIEWER_MANIFEST;
107650
107912
  var init_guide_viewer_manifest = __esm(() => {
107651
107913
  GUIDE_VIEWER_MANIFEST = {
107652
- js: "viewer.BqcO9v_d.js",
107653
- css: "viewer.DejL7_DK.css",
107654
- jsIntegrity: "sha384-hv0Y+2gJFlY9p99fic5BMf0H8suh2cpCqppFI1GPYYBfCbMLs/4fTmf3astg5o8b",
107655
- cssIntegrity: "sha384-RC1v6OjikD4krGxtTP5dn1SNTtuT4wQWdRSS5HSVrmAdJk97gNs5wbKca+3/0Hdl",
107914
+ js: "viewer.CdCl9A4y.js",
107915
+ css: "viewer.DwkeqVw0.css",
107916
+ jsIntegrity: "sha384-2X50K3TjsKUyCt69GjPLTwRrZcYV0LS4+5Nv93J3b8gE1sFjbXaviLKYZJTZb3HS",
107917
+ cssIntegrity: "sha384-xdeDB20PiSkAJ7ueRdgFndT6bub5IF/tjxEaq7rwDqCBe1U2gH2B7GHtIr5R59/F",
107656
107918
  langs: {
107657
107919
  astro: "chunks/astro.Ts5EKq2l.js",
107658
107920
  c: "chunks/c.BIGW1oBm.js",
@@ -116597,15 +116859,15 @@ var init_html_assets = __esm(() => {
116597
116859
  });
116598
116860
 
116599
116861
  // ../../packages/shared/html-assets-node.ts
116600
- import { existsSync as existsSync21, readFileSync as readFileSync22, realpathSync as realpathSync11, statSync as statSync21 } from "fs";
116601
- import { dirname as dirname17, isAbsolute as isAbsolute12, relative as relative9, resolve as resolvePath4, posix as pathPosix2, sep as sep4 } from "path";
116862
+ import { existsSync as existsSync22, readFileSync as readFileSync23, realpathSync as realpathSync11, statSync as statSync22 } from "fs";
116863
+ import { dirname as dirname18, isAbsolute as isAbsolute12, relative as relative9, resolve as resolvePath4, posix as pathPosix2, sep as sep4 } from "path";
116602
116864
  function htmlAssetContext(htmlFilePath, folderPath) {
116603
- const directory = dirname17(resolvePath4(htmlFilePath));
116865
+ const directory = dirname18(resolvePath4(htmlFilePath));
116604
116866
  const ownFolder = { root: directory, basePath: "", docDir: null };
116605
116867
  if (!folderPath || !isWithinDirectory(htmlFilePath, folderPath))
116606
116868
  return ownFolder;
116607
116869
  const root = realpathSync11(folderPath);
116608
- const documentDirectory = dirname17(realpathSync11(htmlFilePath));
116870
+ const documentDirectory = dirname18(realpathSync11(htmlFilePath));
116609
116871
  const basePath = relative9(root, documentDirectory).split(sep4).join("/");
116610
116872
  if (basePath.split("/").some((segment) => segment.startsWith(".")))
116611
116873
  return ownFolder;
@@ -116643,12 +116905,12 @@ function inlineHtmlLocalAssets(html, htmlFilePath, folderPath) {
116643
116905
  return null;
116644
116906
  if (docDir && !folderSessionAssetAllowed(root, docDir, assetPath))
116645
116907
  return null;
116646
- if (!existsSync21(resolved))
116908
+ if (!existsSync22(resolved))
116647
116909
  return null;
116648
- const stat = statSync21(resolved);
116910
+ const stat = statSync22(resolved);
116649
116911
  if (!stat.isFile() || stat.size > MAX_HTML_ASSET_BYTES)
116650
116912
  return null;
116651
- let bytes = readFileSync22(resolved);
116913
+ let bytes = readFileSync23(resolved);
116652
116914
  if (contentType.startsWith("text/css") && !activeCss.has(assetPath)) {
116653
116915
  activeCss.add(assetPath);
116654
116916
  try {
@@ -116686,7 +116948,7 @@ function isWithinDirectory(filePath, root) {
116686
116948
  function resolveOpenInTarget(filePath, base, resolveRoot) {
116687
116949
  const provided = resolveRoot?.();
116688
116950
  const roots = (provided == null ? [
116689
- base ? resolvePath4(base) : isAbsolute12(filePath) ? dirname17(resolvePath4(filePath)) : resolvePath4(process.cwd())
116951
+ base ? resolvePath4(base) : isAbsolute12(filePath) ? dirname18(resolvePath4(filePath)) : resolvePath4(process.cwd())
116690
116952
  ] : Array.isArray(provided) ? provided : [provided]).filter((r) => !!r).map((r) => resolvePath4(r));
116691
116953
  for (const root of roots) {
116692
116954
  const abs = resolvePath4(root, filePath);
@@ -116952,8 +117214,8 @@ var init_open_in = __esm(() => {
116952
117214
  });
116953
117215
 
116954
117216
  // ../../packages/shared/review-workspace-node.ts
116955
- import { existsSync as existsSync22, readdirSync as readdirSync8, realpathSync as realpathSync12, statSync as statSync22 } from "fs";
116956
- import { basename as basename11, relative as relative10, resolve as resolve18 } from "path";
117217
+ import { existsSync as existsSync23, readdirSync as readdirSync8, realpathSync as realpathSync12, statSync as statSync23 } from "fs";
117218
+ import { basename as basename11, relative as relative10, resolve as resolve19 } from "path";
116957
117219
  function normalizeWorkspacePath(path) {
116958
117220
  return path.replace(/\\/g, "/").replace(/^\/+/, "");
116959
117221
  }
@@ -117056,11 +117318,11 @@ function resolveWorkspaceFilePath(repos, prefixedPath) {
117056
117318
  return null;
117057
117319
  }
117058
117320
  function hasVcsMarker(dirPath) {
117059
- return VCS_MARKERS.some((marker) => existsSync22(resolve18(dirPath, marker)));
117321
+ return VCS_MARKERS.some((marker) => existsSync23(resolve19(dirPath, marker)));
117060
117322
  }
117061
117323
  function resolveDirectoryRealPath(path) {
117062
117324
  try {
117063
- if (!statSync22(path).isDirectory())
117325
+ if (!statSync23(path).isDirectory())
117064
117326
  return null;
117065
117327
  return realpathSync12(path);
117066
117328
  } catch {
@@ -117098,11 +117360,11 @@ function collectWorkspaceRepos(root, current, state, results) {
117098
117360
  continue;
117099
117361
  if (!entry.isDirectory() && !entry.isSymbolicLink())
117100
117362
  continue;
117101
- collectWorkspaceRepos(root, resolve18(current, entry.name), state, results);
117363
+ collectWorkspaceRepos(root, resolve19(current, entry.name), state, results);
117102
117364
  }
117103
117365
  }
117104
117366
  function discoverWorkspaceRepoPaths(root) {
117105
- const resolvedRoot = resolve18(root);
117367
+ const resolvedRoot = resolve19(root);
117106
117368
  const results = [];
117107
117369
  collectWorkspaceRepos(resolvedRoot, resolvedRoot, { visitedRealPaths: new Set, limit: getFileBrowserMaxFiles() }, results);
117108
117370
  return results.sort();
@@ -117129,7 +117391,7 @@ function buildRepoLabel(root, cwd, used) {
117129
117391
  return next;
117130
117392
  }
117131
117393
  function buildWorkspaceRepoLabels(root, repoPaths) {
117132
- const resolvedRoot = resolve18(root);
117394
+ const resolvedRoot = resolve19(root);
117133
117395
  const usedLabels = new Set;
117134
117396
  return repoPaths.map((cwd) => buildRepoLabel(resolvedRoot, cwd, usedLabels));
117135
117397
  }
@@ -117163,7 +117425,7 @@ var init_review_workspace_node = __esm(() => {
117163
117425
  });
117164
117426
 
117165
117427
  // ../../packages/shared/review-workspace.ts
117166
- import { isAbsolute as isAbsolute13, relative as relative11, resolve as resolve19 } from "path";
117428
+ import { isAbsolute as isAbsolute13, relative as relative11, resolve as resolve20 } from "path";
117167
117429
  function isWorkspaceDiffType(value) {
117168
117430
  return !!value && WORKSPACE_DIFF_TYPES.has(value);
117169
117431
  }
@@ -117298,7 +117560,7 @@ class WorkspaceReviewSession {
117298
117560
  hideWhitespace;
117299
117561
  constructor(runtime, root, repos, diffType, hideWhitespace) {
117300
117562
  this.runtime = runtime;
117301
- this.root = resolve19(root);
117563
+ this.root = resolve20(root);
117302
117564
  this.repos = repos;
117303
117565
  this.diffType = diffType;
117304
117566
  this.hideWhitespace = hideWhitespace;
@@ -117309,7 +117571,7 @@ class WorkspaceReviewSession {
117309
117571
  this.error = snapshot.error;
117310
117572
  }
117311
117573
  static async create(runtime, root, options = {}) {
117312
- const resolvedRoot = resolve19(root);
117574
+ const resolvedRoot = resolve20(root);
117313
117575
  const repoPaths = discoverWorkspaceRepoPaths(resolvedRoot);
117314
117576
  const labels = buildWorkspaceRepoLabels(resolvedRoot, repoPaths);
117315
117577
  const repos = await Promise.all(repoPaths.map(async (cwd, index) => {
@@ -118125,7 +118387,7 @@ var init_code_nav = __esm(() => {
118125
118387
  });
118126
118388
 
118127
118389
  // ../../packages/server/code-nav.ts
118128
- import { resolve as resolve20 } from "path";
118390
+ import { resolve as resolve21 } from "path";
118129
118391
  async function handleCodeNavResolve(req, cwd, changedFiles) {
118130
118392
  try {
118131
118393
  const body = await req.json();
@@ -118184,7 +118446,7 @@ var init_code_nav2 = __esm(() => {
118184
118446
  return { stdout, stderr, exitCode };
118185
118447
  },
118186
118448
  async readFile(path, options) {
118187
- const read = await readFileCapped(options?.cwd ? resolve20(options.cwd, path) : path, {
118449
+ const read = await readFileCapped(options?.cwd ? resolve21(options.cwd, path) : path, {
118188
118450
  maxBytes: CODE_NAV_MAX_FILE_BYTES,
118189
118451
  decode: decodeBunText
118190
118452
  });
@@ -118224,9 +118486,9 @@ var init_repo_files_node = __esm(() => {
118224
118486
  });
118225
118487
 
118226
118488
  // ../../packages/server/review.ts
118227
- import { homedir as homedir6 } from "os";
118228
- import { basename as basename12, dirname as dirname18, join as join39 } from "path";
118229
- import { existsSync as existsSync23 } from "fs";
118489
+ import { homedir as homedir7 } from "os";
118490
+ import { basename as basename12, dirname as dirname19, join as join40 } from "path";
118491
+ import { existsSync as existsSync24 } from "fs";
118230
118492
  async function startReviewServer(options) {
118231
118493
  const { htmlContent, origin, gitContext, sharingEnabled = true, shareBaseUrl, onReady } = options;
118232
118494
  const approvalNotesSupported = options.approvalNotesSupported === true;
@@ -118336,7 +118598,7 @@ async function startReviewServer(options) {
118336
118598
  let baseEverSwitched = false;
118337
118599
  let baseExplicitlyChosen = options.initialBaseExplicit === true;
118338
118600
  const remoteCheckEnabled = resolveGitRemoteCheck(options.gitRemoteCheck === false, loadConfig());
118339
- const agentCwdIfExists = () => options.agentCwd && existsSync23(options.agentCwd) ? options.agentCwd : undefined;
118601
+ const agentCwdIfExists = () => options.agentCwd && existsSync24(options.agentCwd) ? options.agentCwd : undefined;
118340
118602
  const resolvePRLocalCwd = (meta = prMetadata) => {
118341
118603
  const pool = options.worktreePool;
118342
118604
  if (pool && meta) {
@@ -118411,6 +118673,7 @@ async function startReviewServer(options) {
118411
118673
  const fileContentFingerprintProbes = new SingleFlight;
118412
118674
  const callFlowService = new CallFlowService;
118413
118675
  const callFlowInstall = new CallFlowInstallCoordinator({
118676
+ ...options.callFlowInstall,
118414
118677
  onSettled: (ok) => {
118415
118678
  if (ok)
118416
118679
  callFlowService.invalidateRuntimeState();
@@ -119296,7 +119559,7 @@ async function startReviewServer(options) {
119296
119559
  kind: "local",
119297
119560
  dirOf: async (path) => {
119298
119561
  const target = resolveWorkspaceFilePath(session.repos, path);
119299
- return target ? dirname18(join39(target.repo.cwd, target.repoRelativePath)) : null;
119562
+ return target ? dirname19(join40(target.repo.cwd, target.repoRelativePath)) : null;
119300
119563
  }
119301
119564
  };
119302
119565
  }
@@ -119325,13 +119588,13 @@ async function startReviewServer(options) {
119325
119588
  };
119326
119589
  }
119327
119590
  const root = await packageRepoRoot.root;
119328
- return root ? dirname18(join39(root, path)) : null;
119591
+ return root ? dirname19(join40(root, path)) : null;
119329
119592
  }
119330
119593
  };
119331
119594
  },
119332
119595
  homeDir: () => {
119333
119596
  try {
119334
- return homedir6();
119597
+ return homedir7();
119335
119598
  } catch {
119336
119599
  return null;
119337
119600
  }
@@ -121185,7 +121448,7 @@ var init_annotate_history = __esm(() => {
121185
121448
  });
121186
121449
 
121187
121450
  // ../../packages/shared/annotate-live-version.ts
121188
- import { statSync as statSync23 } from "fs";
121451
+ import { statSync as statSync24 } from "fs";
121189
121452
  function refuse(status, code, error) {
121190
121453
  return { status, body: { ok: false, code, error } };
121191
121454
  }
@@ -121210,7 +121473,7 @@ function saveAnnotateLiveVersion(body, ctx) {
121210
121473
  const tooLarge = () => refuse(413, "too_large", "The file is too large to keep as a version (max 2MB).");
121211
121474
  let snapshot;
121212
121475
  try {
121213
- if ((ctx.sizeOf ?? ((path) => statSync23(path).size))(ctx.resolvedFilePath) > MAX_ANNOTATABLE_FILE_BYTES) {
121476
+ if ((ctx.sizeOf ?? ((path) => statSync24(path).size))(ctx.resolvedFilePath) > MAX_ANNOTATABLE_FILE_BYTES) {
121214
121477
  return tooLarge();
121215
121478
  }
121216
121479
  snapshot = (ctx.readSnapshot ?? readSourceFileSnapshot)(ctx.resolvedFilePath);
@@ -121579,7 +121842,7 @@ var init_html_diff = __esm(() => {
121579
121842
 
121580
121843
  // ../../packages/shared/annotate-reference-roots-node.ts
121581
121844
  import { realpathSync as realpathSync13 } from "fs";
121582
- import { dirname as dirname19 } from "path";
121845
+ import { dirname as dirname20 } from "path";
121583
121846
  function getAnnotateReferenceRootPaths(options) {
121584
121847
  const roots = [];
121585
121848
  const addRoot = (root) => {
@@ -121601,9 +121864,9 @@ function getAnnotateReferenceRootPaths(options) {
121601
121864
  addRoot(process.cwd());
121602
121865
  if (options.mode === "annotate-bundle" && options.bundlePaths) {
121603
121866
  for (const path of options.bundlePaths) {
121604
- addRoot(dirname19(path));
121867
+ addRoot(dirname20(path));
121605
121868
  try {
121606
- addRoot(dirname19(realpathSync13(path)));
121869
+ addRoot(dirname20(realpathSync13(path)));
121607
121870
  } catch {}
121608
121871
  }
121609
121872
  return roots;
@@ -121611,8 +121874,8 @@ function getAnnotateReferenceRootPaths(options) {
121611
121874
  if (/^https?:\/\//i.test(options.filePath)) {
121612
121875
  return roots;
121613
121876
  }
121614
- addRoot(dirname19(options.filePath));
121615
- addRoot(options.initialSingleFileSourcePath ? dirname19(options.initialSingleFileSourcePath) : null);
121877
+ addRoot(dirname20(options.filePath));
121878
+ addRoot(options.initialSingleFileSourcePath ? dirname20(options.initialSingleFileSourcePath) : null);
121616
121879
  return roots;
121617
121880
  }
121618
121881
  var init_annotate_reference_roots_node = __esm(() => {
@@ -122053,11 +122316,11 @@ function shutdown() {
122053
122316
  var init_agent_terminal_node_sidecar = () => {};
122054
122317
 
122055
122318
  // ../../packages/server/agent-terminal-runtime.ts
122056
- import { existsSync as existsSync24, mkdirSync as mkdirSync19, readFileSync as readFileSync24, writeFileSync as writeFileSync17 } from "fs";
122057
- import { join as join40 } from "path";
122319
+ import { existsSync as existsSync25, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "fs";
122320
+ import { join as join41 } from "path";
122058
122321
  import { fileURLToPath } from "url";
122059
122322
  function getAgentTerminalManagedRuntimeDir(dataDir = getPlannotatorDataDir()) {
122060
- return join40(dataDir, "vendor", "agent-terminal", `webtui-${AGENT_TERMINAL_WEBTUI_VERSION}`);
122323
+ return join41(dataDir, "vendor", "agent-terminal", `webtui-${AGENT_TERMINAL_WEBTUI_VERSION}`);
122061
122324
  }
122062
122325
  function isAgentTerminalRemoteEnabled(env = process.env) {
122063
122326
  return isTruthy(env.PLANNOTATOR_AGENT_TERMINAL_REMOTE);
@@ -122086,7 +122349,7 @@ function resolveBundledAgentTerminalSidecarPath(moduleUrl = import.meta.url) {
122086
122349
  const bundledSidecarPath = fileURLToPath(new URL("./agent-terminal-node-sidecar.mjs", moduleUrl));
122087
122350
  if (isBunVirtualPath(bundledSidecarPath))
122088
122351
  return null;
122089
- return existsSync24(bundledSidecarPath) ? bundledSidecarPath : null;
122352
+ return existsSync25(bundledSidecarPath) ? bundledSidecarPath : null;
122090
122353
  }
122091
122354
  async function resolveBundledAgentTerminalRuntime(nodePath, bundledSidecarPath) {
122092
122355
  const webtuiCoreUrl = resolveImportUrl("@plannotator/webtui/core");
@@ -122138,7 +122401,7 @@ async function resolveManagedAgentTerminalRuntime(nodePath) {
122138
122401
  }
122139
122402
  function materializeAgentTerminalSidecar(runtimeDir) {
122140
122403
  mkdirSync19(runtimeDir, { recursive: true });
122141
- const sidecarPath = join40(runtimeDir, "agent-terminal-node-sidecar.mjs");
122404
+ const sidecarPath = join41(runtimeDir, "agent-terminal-node-sidecar.mjs");
122142
122405
  writeFileSync17(sidecarPath, agent_terminal_node_sidecar_default, "utf8");
122143
122406
  return sidecarPath;
122144
122407
  }
@@ -122154,11 +122417,11 @@ function tryMaterializeAgentTerminalSidecar(runtimeDir) {
122154
122417
  }
122155
122418
  }
122156
122419
  function readInstalledWebTuiVersion(runtimeDir) {
122157
- const packageJsonPath = join40(runtimeDir, "node_modules", "@plannotator", "webtui", "package.json");
122158
- if (!existsSync24(packageJsonPath))
122420
+ const packageJsonPath = join41(runtimeDir, "node_modules", "@plannotator", "webtui", "package.json");
122421
+ if (!existsSync25(packageJsonPath))
122159
122422
  return null;
122160
122423
  try {
122161
- const parsed = JSON.parse(readFileSync24(packageJsonPath, "utf8"));
122424
+ const parsed = JSON.parse(readFileSync25(packageJsonPath, "utf8"));
122162
122425
  return typeof parsed.version === "string" ? parsed.version : null;
122163
122426
  } catch {
122164
122427
  return null;
@@ -123985,10 +124248,10 @@ function annotateBundleDocumentCounts(paths, annotations) {
123985
124248
  var init_annotate_bundle = () => {};
123986
124249
 
123987
124250
  // ../../packages/server/annotate.ts
123988
- import { existsSync as existsSync25 } from "fs";
123989
- import { dirname as dirname20, resolve as resolvePath6 } from "path";
124251
+ import { existsSync as existsSync26 } from "fs";
124252
+ import { dirname as dirname21, resolve as resolvePath6 } from "path";
123990
124253
  import { randomBytes as randomBytes2 } from "crypto";
123991
- import { readFileSync as readFileSync25 } from "fs";
124254
+ import { readFileSync as readFileSync26 } from "fs";
123992
124255
  function runGuardedShutdown(steps, closeListener, log = (message, error) => console.error(message, error)) {
123993
124256
  try {
123994
124257
  for (const [name, dispose] of steps) {
@@ -124077,7 +124340,7 @@ async function startAnnotateServer(options) {
124077
124340
  if (file.renderAs === "html" || !isAnnotatableTextPath2(file.path))
124078
124341
  continue;
124079
124342
  try {
124080
- computeFolderAnnotateHistory(file.path, readFileSync25(file.path, "utf-8"));
124343
+ computeFolderAnnotateHistory(file.path, readFileSync26(file.path, "utf-8"));
124081
124344
  } catch {}
124082
124345
  }
124083
124346
  }
@@ -124202,9 +124465,9 @@ async function startAnnotateServer(options) {
124202
124465
  if (folderPath)
124203
124466
  roots.add(folderPath);
124204
124467
  for (const path of bundlePaths)
124205
- roots.add(dirname20(path));
124468
+ roots.add(dirname21(path));
124206
124469
  if (!bundleFiles && !/^https?:\/\//i.test(filePath))
124207
- roots.add(dirname20(filePath));
124470
+ roots.add(dirname21(filePath));
124208
124471
  for (const root of roots) {
124209
124472
  if (isWithinDirectory(targetPath, root))
124210
124473
  return true;
@@ -124571,7 +124834,7 @@ async function startAnnotateServer(options) {
124571
124834
  const docUrl = new URL(req.url);
124572
124835
  let changed = false;
124573
124836
  if (!docUrl.searchParams.has("base") && !/^https?:\/\//i.test(filePath)) {
124574
- docUrl.searchParams.set("base", mode === "annotate-folder" && folderPath ? folderPath : bundleRoot ?? dirname20(filePath));
124837
+ docUrl.searchParams.set("base", mode === "annotate-folder" && folderPath ? folderPath : bundleRoot ?? dirname21(filePath));
124575
124838
  changed = true;
124576
124839
  }
124577
124840
  if (convertHtml && !docUrl.searchParams.has("convert")) {
@@ -124614,7 +124877,7 @@ async function startAnnotateServer(options) {
124614
124877
  targetPath = capability.enabled ? capability.path : initialSingleFileSourcePath;
124615
124878
  } else if (mode === "annotate-folder" && folderPath && typeof body.path === "string") {
124616
124879
  targetPath = body.allowMissingBase ? resolveFolderSourceFileForSave(body.path, folderPath) : resolveFolderSourceFile(body.path, folderPath);
124617
- if (body.allowMissingBase && targetPath && !existsSync25(targetPath) && !openedSourceFilePaths.has(targetPath)) {
124880
+ if (body.allowMissingBase && targetPath && !existsSync26(targetPath) && !openedSourceFilePaths.has(targetPath)) {
124618
124881
  targetPath = null;
124619
124882
  }
124620
124883
  } else if (bundleSourceSavePaths && typeof body.path === "string") {
@@ -125209,7 +125472,7 @@ var init_plannotator_tool = __esm(() => {
125209
125472
  });
125210
125473
 
125211
125474
  // ../../packages/shared/annotate-target.ts
125212
- import { existsSync as existsSync26, realpathSync as realpathSync14, statSync as statSync24 } from "fs";
125475
+ import { existsSync as existsSync27, realpathSync as realpathSync14, statSync as statSync25 } from "fs";
125213
125476
  function probeAnnotateToken(token, projectRoot, options) {
125214
125477
  if (!token)
125215
125478
  return null;
@@ -125220,7 +125483,7 @@ function probeAnnotateToken(token, projectRoot, options) {
125220
125483
  if (allowBareDirectory || /[\\/]/.test(token)) {
125221
125484
  const folder = resolveAtReference(token, (candidate) => {
125222
125485
  try {
125223
- return statSync24(resolveUserPath(candidate, projectRoot)).isDirectory();
125486
+ return statSync25(resolveUserPath(candidate, projectRoot)).isDirectory();
125224
125487
  } catch {
125225
125488
  return false;
125226
125489
  }
@@ -125230,7 +125493,7 @@ function probeAnnotateToken(token, projectRoot, options) {
125230
125493
  }
125231
125494
  const html = resolveAtReference(token, (candidate) => {
125232
125495
  const abs = resolveUserPath(candidate, projectRoot);
125233
- return /\.html?$/i.test(abs) && existsSync26(abs);
125496
+ return /\.html?$/i.test(abs) && existsSync27(abs);
125234
125497
  });
125235
125498
  if (html !== null)
125236
125499
  return resolveUserPath(html, projectRoot);
@@ -125244,7 +125507,7 @@ function probeAnnotateToken(token, projectRoot, options) {
125244
125507
  return unwrapped;
125245
125508
  const literal = resolveAtReference(token, (candidate) => {
125246
125509
  try {
125247
- return statSync24(resolveUserPath(candidate, projectRoot)).isFile();
125510
+ return statSync25(resolveUserPath(candidate, projectRoot)).isFile();
125248
125511
  } catch {
125249
125512
  return false;
125250
125513
  }
@@ -125266,7 +125529,7 @@ function probeAnnotateBundlePath(token, projectRoot) {
125266
125529
  return null;
125267
125530
  const candidate = resolveAtReference(token, (value) => {
125268
125531
  try {
125269
- return statSync24(resolveUserPath(value, projectRoot)).isFile();
125532
+ return statSync25(resolveUserPath(value, projectRoot)).isFile();
125270
125533
  } catch {
125271
125534
  return false;
125272
125535
  }
@@ -125274,7 +125537,7 @@ function probeAnnotateBundlePath(token, projectRoot) {
125274
125537
  return candidate === null ? null : resolveUserPath(candidate, projectRoot);
125275
125538
  }
125276
125539
  function annotatePathExists(token, projectRoot) {
125277
- return resolveAtReference(token, (value) => existsSync26(resolveUserPath(value, projectRoot))) !== null;
125540
+ return resolveAtReference(token, (value) => existsSync27(resolveUserPath(value, projectRoot))) !== null;
125278
125541
  }
125279
125542
  function bundleIdentity(path) {
125280
125543
  try {
@@ -125363,7 +125626,7 @@ function resolveAnnotateBundleFiles(paths, options = {}) {
125363
125626
  identities.add(identity);
125364
125627
  let size;
125365
125628
  try {
125366
- const stat = statSync24(path);
125629
+ const stat = statSync25(path);
125367
125630
  if (!stat.isFile())
125368
125631
  return { ok: false, message: `Not a file: ${path}` };
125369
125632
  size = stat.size;
@@ -125418,7 +125681,7 @@ var init_annotate_target = __esm(() => {
125418
125681
  });
125419
125682
 
125420
125683
  // ../../packages/shared/review-args.ts
125421
- import { statSync as statSync25 } from "fs";
125684
+ import { statSync as statSync26 } from "fs";
125422
125685
  function parseReviewArgs(input) {
125423
125686
  const tokens = Array.isArray(input) ? input.map((token) => stripWrappingQuotes(token.trim())).filter(Boolean) : tokenizeReviewArgs(input ?? "");
125424
125687
  let vcsType;
@@ -125562,7 +125825,7 @@ function resolveReviewTarget(parsed, cwd) {
125562
125825
  const resolved = resolveUserPath(word, cwd);
125563
125826
  let isDirectory;
125564
125827
  try {
125565
- isDirectory = statSync25(resolved).isDirectory();
125828
+ isDirectory = statSync26(resolved).isDirectory();
125566
125829
  } catch {
125567
125830
  isDirectory = undefined;
125568
125831
  }
@@ -126035,7 +126298,7 @@ __export(exports_commands, {
126035
126298
  handleAnnotateLastCommand: () => handleAnnotateLastCommand,
126036
126299
  handleReviewCommand: () => handleReviewCommand
126037
126300
  });
126038
- import { statSync as statSync26 } from "fs";
126301
+ import { statSync as statSync27 } from "fs";
126039
126302
  import path3 from "path";
126040
126303
  async function handleReviewCommand(event, deps) {
126041
126304
  const { client, reviewHtmlContent, getSharingEnabled, getShareBaseUrl, directory } = deps;
@@ -126326,7 +126589,7 @@ async function handleAnnotateCommand(event, deps) {
126326
126589
  const projectRoot = agentCwd;
126327
126590
  const resolvedArg = resolveUserPath(filePath, projectRoot);
126328
126591
  try {
126329
- isFolder = statSync26(resolvedArg).isDirectory();
126592
+ isFolder = statSync27(resolvedArg).isDirectory();
126330
126593
  } catch {}
126331
126594
  if (isFolder) {
126332
126595
  if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, getAnnotatableDocRegex())) {
@@ -126340,7 +126603,7 @@ async function handleAnnotateCommand(event, deps) {
126340
126603
  client.app.log({ level: "info", message: `Opening annotation UI for folder ${resolvedArg}...` });
126341
126604
  } else if (/\.html?$/i.test(resolvedArg)) {
126342
126605
  try {
126343
- statSync26(resolvedArg);
126606
+ statSync27(resolvedArg);
126344
126607
  } catch {
126345
126608
  client.app.log({ level: "error", message: `File not found: ${filePath}` });
126346
126609
  return;