tandem-editor 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import{U as e}from"./index-BIEOrpU_.js";var t=typeof __SENTRY_DEBUG__>`u`||__SENTRY_DEBUG__,n=globalThis,r=`10.56.0`;function i(){return a(n),n}function a(e){let t=e.__SENTRY__=e.__SENTRY__||{};return t.version=t.version||`10.56.0`,t[r]=t[`10.56.0`]||{}}function o(e,t,i=n){let a=i.__SENTRY__=i.__SENTRY__||{},o=a[r]=a[`10.56.0`]||{};return o[e]||(o[e]=t())}var s=[`debug`,`info`,`warn`,`error`,`log`,`assert`,`trace`],c=`Sentry Logger `,l={};function u(e){if(!(`console`in n))return e();let t=n.console,r={},i=Object.keys(l);i.forEach(e=>{let n=l[e];r[e]=t[e],t[e]=n});try{return e()}finally{i.forEach(e=>{t[e]=r[e]})}}function d(){v().enabled=!0}function f(){v().enabled=!1}function p(){return v().enabled}function m(...e){_(`log`,...e)}function h(...e){_(`warn`,...e)}function g(...e){_(`error`,...e)}function _(e,...r){t&&p()&&u(()=>{n.console[e](`${c}[${e}]:`,...r)})}function v(){return t?o(`loggerSettings`,()=>({enabled:!1})):{enabled:!1}}var y={enable:d,disable:f,isEnabled:p,log:m,warn:h,error:g},b=50,x=/\(error: (.*)\)/,S=/captureMessage|captureException/;function C(...e){let t=e.sort((e,t)=>e[0]-t[0]).map(e=>e[1]);return(e,n=0,r=0)=>{let i=[],a=e.split(`
1
+ import{U as e}from"./index-BymYYAwj.js";var t=typeof __SENTRY_DEBUG__>`u`||__SENTRY_DEBUG__,n=globalThis,r=`10.56.0`;function i(){return a(n),n}function a(e){let t=e.__SENTRY__=e.__SENTRY__||{};return t.version=t.version||`10.56.0`,t[r]=t[`10.56.0`]||{}}function o(e,t,i=n){let a=i.__SENTRY__=i.__SENTRY__||{},o=a[r]=a[`10.56.0`]||{};return o[e]||(o[e]=t())}var s=[`debug`,`info`,`warn`,`error`,`log`,`assert`,`trace`],c=`Sentry Logger `,l={};function u(e){if(!(`console`in n))return e();let t=n.console,r={},i=Object.keys(l);i.forEach(e=>{let n=l[e];r[e]=t[e],t[e]=n});try{return e()}finally{i.forEach(e=>{t[e]=r[e]})}}function d(){v().enabled=!0}function f(){v().enabled=!1}function p(){return v().enabled}function m(...e){_(`log`,...e)}function h(...e){_(`warn`,...e)}function g(...e){_(`error`,...e)}function _(e,...r){t&&p()&&u(()=>{n.console[e](`${c}[${e}]:`,...r)})}function v(){return t?o(`loggerSettings`,()=>({enabled:!1})):{enabled:!1}}var y={enable:d,disable:f,isEnabled:p,log:m,warn:h,error:g},b=50,x=/\(error: (.*)\)/,S=/captureMessage|captureException/;function C(...e){let t=e.sort((e,t)=>e[0]-t[0]).map(e=>e[1]);return(e,n=0,r=0)=>{let i=[],a=e.split(`
2
2
  `);for(let e=n;e<a.length;e++){let n=a[e];n.length>1024&&(n=n.slice(0,1024));let o=x.test(n)?n.replace(x,`$1`):n;if(!o.includes(`Error: `)){for(let e of t){let t=e(o);if(t){i.push(t);break}}if(i.length>=b+r)break}}return T(i.slice(r))}}function w(e){return Array.isArray(e)?C(...e):e}function T(e){if(!e.length)return[];let t=Array.from(e);return/sentryWrapped/.test(ee(t).function||``)&&t.pop(),t.reverse(),S.test(ee(t).function||``)&&(t.pop(),S.test(ee(t).function||``)&&t.pop()),t.slice(0,b).map(e=>({...e,filename:e.filename||ee(t).filename,function:e.function||`?`}))}function ee(e){return e[e.length-1]||{}}var te=`<anonymous>`;function E(e){try{return!e||typeof e!=`function`?te:e.name||te}catch{return te}}function ne(e){let t=e.exception;if(t){let e=[];try{return t.values.forEach(t=>{t.stacktrace.frames&&e.push(...t.stacktrace.frames)}),e}catch{return}}}function re(e){let t=e?.startsWith(`file://`)?e.slice(7):e;return t?.match(/\/[A-Z]:/)&&(t=t.slice(1)),t}var ie={},ae={};function oe(e,t){return ie[e]=ie[e]||[],ie[e].push(t),()=>{let n=ie[e];if(n){let e=n.indexOf(t);e!==-1&&n.splice(e,1)}}}function D(e,n){if(!ae[e]){ae[e]=!0;try{n()}catch(n){t&&y.error(`Error while instrumenting ${e}`,n)}}}function O(e,n){let r=e&&ie[e];if(r)for(let i of r)try{i(n)}catch(n){t&&y.error(`Error while triggering instrumentation handler.
3
3
  Type: ${e}
4
4
  Name: ${E(i)}
@@ -276,14 +276,30 @@
276
276
  tab pills, +/add pill, mode segment, status pill, mini-toolbar,
277
277
  wincontrols. Surfaces only need .tandem-floating-pill + a sensible
278
278
  border-radius (pill or 14px). Surface-specific overrides (padding,
279
- max-width, drag-region) layer on top. */
279
+ max-width, drag-region) layer on top.
280
+ Deliberately NO backdrop-filter. --tandem-surface is opaque in
281
+ every theme, so a blur behind it never shows on the opaque consumers —
282
+ but this rule also reaches the ONE transparent consumer, the
283
+ selection-popup shell, where it painted a stray frosted rect in
284
+ PRODUCTION ONLY, and no scoped reset can be relied on to cancel it.
285
+ Why the asymmetry: THIS FILE'S inline \3c style> is emitted verbatim —
286
+ Vite never runs it through lightningcss — so a blur written here
287
+ reaches the browser exactly as typed. A component's `backdrop-filter:
288
+ none` reset does NOT: component CSS IS minified, and lightningcss
289
+ collapses a hand-written `backdrop-filter` + `-webkit-backdrop-filter`
290
+ pair (prefixed line last) into the `-webkit-` form alone, which
291
+ Chromium never implemented. So the recipe's blur survived while the
292
+ reset evaporated — the bug needed both halves. See #1188 and lesson 83.
293
+ Don't re-add a blur here: it buys nothing on an opaque surface, and the
294
+ override that would contain it cannot survive the other pipeline. If you
295
+ ever DO need a prefixed property in this file, hand-write the pair —
296
+ nothing autoprefixes index.html. That rule inverts in component CSS,
297
+ where hand-writing the pair is the bug. */
280
298
  .tandem-floating-pill {
281
299
  background: var(--tandem-surface);
282
300
  border: 1px solid var(--tandem-border);
283
301
  border-radius: var(--tandem-r-pill);
284
302
  box-shadow: var(--c7-pill-shadow);
285
- backdrop-filter: saturate(140%) blur(8px);
286
- -webkit-backdrop-filter: saturate(140%) blur(8px);
287
303
  }
288
304
  [data-theme="dark"] .tandem-floating-pill {
289
305
  --c7-pill-shadow:
@@ -679,8 +695,8 @@
679
695
  line-height: var(--tandem-ui-leading);
680
696
  }
681
697
  </style>
682
- <script type="module" crossorigin src="/assets/index-BIEOrpU_.js"></script>
683
- <link rel="stylesheet" crossorigin href="/assets/index-DdjGUhNd.css">
698
+ <script type="module" crossorigin src="/assets/index-BymYYAwj.js"></script>
699
+ <link rel="stylesheet" crossorigin href="/assets/index-Boow3rxN.css">
684
700
  </head>
685
701
  <body>
686
702
  <div id="root"></div>
@@ -93543,8 +93543,8 @@ function resolveChannelDist(env3 = process.env, exists = existsSync) {
93543
93543
  return resolve3(PACKAGE_ROOT, "dist/channel/index.js");
93544
93544
  }
93545
93545
  function resolveCliVersion() {
93546
- if (true) return "0.16.0";
93547
- if (true) return "0.16.0";
93546
+ if (true) return "0.17.0";
93547
+ if (true) return "0.17.0";
93548
93548
  for (const rel of ["../../package.json", "../../../package.json"]) {
93549
93549
  try {
93550
93550
  const pkg = JSON.parse(readFileSync2(resolve3(__dirname2, rel), "utf8"));
@@ -129433,6 +129433,10 @@ import { request } from "http";
129433
129433
  import { createConnection } from "net";
129434
129434
  import { homedir as homedir3, platform } from "os";
129435
129435
  import { join as join5 } from "path";
129436
+ function errorClass(err) {
129437
+ if (err instanceof Error) return err.name;
129438
+ return typeof err;
129439
+ }
129436
129440
  function checkNodeVersion(r) {
129437
129441
  const version3 = process.version;
129438
129442
  const major2 = Number.parseInt(version3.slice(1), 10);
@@ -129452,6 +129456,201 @@ function checkNodeModules(r) {
129452
129456
  r.fail("node_modules/ not found", "npm install");
129453
129457
  }
129454
129458
  }
129459
+ function recordEvaluation(r, result2) {
129460
+ if (!result2) return;
129461
+ if (result2.status === "warn") {
129462
+ r.warn(result2.message, result2.fix, result2.data);
129463
+ return;
129464
+ }
129465
+ if (result2.status === "skip") {
129466
+ r.pass(`skipped \u2014 ${result2.message}`, result2.fix, { ...result2.data, skipped: true });
129467
+ return;
129468
+ }
129469
+ r.pass(result2.message, result2.fix, result2.data);
129470
+ }
129471
+ function probeTandemEditorRepo(dir) {
129472
+ const read = readJson2(join5(dir, "package.json"));
129473
+ if (read.kind === "absent") return "no";
129474
+ if (read.kind === "unreadable") return "unreadable";
129475
+ const parsed = read.value;
129476
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
129477
+ return "unreadable";
129478
+ }
129479
+ return parsed.name === "tandem-editor" ? "yes" : "no";
129480
+ }
129481
+ function readJson2(path33) {
129482
+ let raw;
129483
+ try {
129484
+ raw = readFileSync3(path33, "utf-8");
129485
+ } catch (err) {
129486
+ const code3 = err.code;
129487
+ if (code3 === "ENOENT") return { kind: "absent" };
129488
+ return { kind: "unreadable", reason: code3 ?? errorClass(err) };
129489
+ }
129490
+ try {
129491
+ return { kind: "ok", value: JSON.parse(raw) };
129492
+ } catch {
129493
+ return { kind: "unreadable", reason: "not valid JSON" };
129494
+ }
129495
+ }
129496
+ function parseLockfileEntry(value) {
129497
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
129498
+ const entry = value;
129499
+ if (entry.version !== void 0 && typeof entry.version !== "string") return null;
129500
+ if (entry.optional !== void 0 && typeof entry.optional !== "boolean") return null;
129501
+ return {
129502
+ version: entry.version,
129503
+ optional: entry.optional
129504
+ };
129505
+ }
129506
+ function parseLockfileJson(value) {
129507
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
129508
+ const obj2 = value;
129509
+ if (obj2.version !== void 0 && typeof obj2.version !== "string") return null;
129510
+ const version3 = obj2.version;
129511
+ if (obj2.packages === void 0) return { version: version3 };
129512
+ if (typeof obj2.packages !== "object" || obj2.packages === null || Array.isArray(obj2.packages)) {
129513
+ return null;
129514
+ }
129515
+ const packages = {};
129516
+ for (const [path33, raw] of Object.entries(obj2.packages)) {
129517
+ const entry = parseLockfileEntry(raw);
129518
+ if (!entry) return null;
129519
+ packages[path33] = entry;
129520
+ }
129521
+ return { version: version3, packages };
129522
+ }
129523
+ function evaluateNpmStaleness(pkgInput, lockInput, hiddenLockInput) {
129524
+ if (!pkgInput || !lockInput || !hiddenLockInput) return null;
129525
+ const lock = parseLockfileJson(lockInput);
129526
+ if (!lock) {
129527
+ return {
129528
+ status: "skip",
129529
+ message: "cannot compare (package-lock.json has an unexpected structure)",
129530
+ fix: "Restore it from git: git checkout package-lock.json",
129531
+ data: { reason: "malformed-lock" }
129532
+ };
129533
+ }
129534
+ const hiddenLock = parseLockfileJson(hiddenLockInput);
129535
+ if (!hiddenLock) {
129536
+ return {
129537
+ status: "skip",
129538
+ message: "cannot compare (node_modules/.package-lock.json has an unexpected structure)",
129539
+ fix: "npm install",
129540
+ data: { reason: "malformed-hidden-lock" }
129541
+ };
129542
+ }
129543
+ const pkg = typeof pkgInput === "object" && !Array.isArray(pkgInput) ? pkgInput : {};
129544
+ const pkgVersion = typeof pkg.version === "string" ? pkg.version : void 0;
129545
+ if (pkgVersion && lock.version && pkgVersion !== lock.version) {
129546
+ return {
129547
+ status: "warn",
129548
+ message: `package-lock.json (v${lock.version}) is out of date with package.json (v${pkgVersion})`,
129549
+ fix: "npm install",
129550
+ data: { packageVersion: pkgVersion, lockVersion: lock.version }
129551
+ };
129552
+ }
129553
+ if (lock.version && hiddenLock.version && lock.version !== hiddenLock.version) {
129554
+ return {
129555
+ status: "warn",
129556
+ message: `node_modules was installed from a different lockfile (v${hiddenLock.version} installed, v${lock.version} expected)`,
129557
+ fix: "npm install",
129558
+ data: { lockVersion: lock.version, installedVersion: hiddenLock.version }
129559
+ };
129560
+ }
129561
+ const wanted = lock.packages ?? {};
129562
+ const installed = hiddenLock.packages ?? {};
129563
+ const wantedCount = Object.keys(wanted).filter((path33) => path33 !== "").length;
129564
+ if (wantedCount === 0) {
129565
+ return {
129566
+ status: "skip",
129567
+ message: "cannot compare (package-lock.json lists no packages \u2014 a lockfileVersion 1 file, or one written by something other than npm)",
129568
+ fix: "npm install",
129569
+ // No inferred `lockfileVersion` here: we did not read that field, and
129570
+ // guessing it from the presence of `packages` would put a fabricated
129571
+ // value under a real npm field name.
129572
+ data: { reason: "no-comparable-packages" }
129573
+ };
129574
+ }
129575
+ const drifted = [];
129576
+ for (const [path33, entry] of Object.entries(wanted)) {
129577
+ if (path33 === "") continue;
129578
+ const got = installed[path33];
129579
+ if (!got) {
129580
+ if (!entry.optional) drifted.push(path33);
129581
+ } else if (entry.version !== got.version) {
129582
+ drifted.push(path33);
129583
+ }
129584
+ }
129585
+ for (const path33 of Object.keys(installed)) {
129586
+ if (path33 !== "" && !(path33 in wanted)) drifted.push(path33);
129587
+ }
129588
+ if (drifted.length > 0) {
129589
+ return {
129590
+ status: "warn",
129591
+ message: `node_modules is stale \u2014 ${drifted.length} package(s) differ from package-lock.json (e.g. after a pull or branch switch)`,
129592
+ fix: "npm install",
129593
+ data: { driftCount: drifted.length, sample: drifted.slice(0, 5) }
129594
+ };
129595
+ }
129596
+ const missingVersions = [];
129597
+ if (!pkgVersion) missingVersions.push("package.json");
129598
+ if (!lock.version) missingVersions.push("package-lock.json");
129599
+ if (!hiddenLock.version) missingVersions.push("node_modules/.package-lock.json");
129600
+ if (missingVersions.length > 0) {
129601
+ return {
129602
+ status: "skip",
129603
+ message: `node_modules matches package-lock.json, but the version check could not run \u2014 no "version" field in ${missingVersions.join(", ")}`,
129604
+ data: { reason: "no-comparable-version", missingVersions }
129605
+ };
129606
+ }
129607
+ return {
129608
+ status: "pass",
129609
+ message: "node_modules matches package-lock.json",
129610
+ data: { packageCount: Object.keys(installed).length }
129611
+ };
129612
+ }
129613
+ function readLockfileOrReport(r, path33, label) {
129614
+ const read = readJson2(path33);
129615
+ if (read.kind === "absent") {
129616
+ r.pass(`skipped \u2014 cannot compare (${label} not found)`, void 0, {
129617
+ skipped: true,
129618
+ reason: "absent",
129619
+ path: label
129620
+ });
129621
+ return { ok: false };
129622
+ }
129623
+ if (read.kind === "ok") {
129624
+ if (typeof read.value === "object" && read.value !== null && !Array.isArray(read.value)) {
129625
+ return { ok: true, value: read.value };
129626
+ }
129627
+ r.warn(
129628
+ `${label} is not a JSON object \u2014 npm install staleness cannot be checked`,
129629
+ "Check for a truncated or hand-edited file, then: npm install",
129630
+ { reason: "not-an-object", path: label }
129631
+ );
129632
+ return { ok: false };
129633
+ }
129634
+ r.warn(
129635
+ `${label} could not be read (${read.reason}) \u2014 npm install staleness cannot be checked`,
129636
+ "Check for merge-conflict markers or a truncated file, then: npm install",
129637
+ { reason: read.reason, path: label }
129638
+ );
129639
+ return { ok: false };
129640
+ }
129641
+ function checkNpmStaleness(r, repoDir) {
129642
+ const pkgRead = readJson2(join5(repoDir, "package.json"));
129643
+ const pkg = pkgRead.kind === "ok" ? pkgRead.value : null;
129644
+ const lock = readLockfileOrReport(r, join5(repoDir, "package-lock.json"), "package-lock.json");
129645
+ if (!lock.ok) return;
129646
+ const hiddenLock = readLockfileOrReport(
129647
+ r,
129648
+ join5(repoDir, "node_modules", ".package-lock.json"),
129649
+ "node_modules/.package-lock.json"
129650
+ );
129651
+ if (!hiddenLock.ok) return;
129652
+ recordEvaluation(r, evaluateNpmStaleness(pkg, lock.value, hiddenLock.value));
129653
+ }
129455
129654
  function checkMcpJson(r) {
129456
129655
  const mcpPath = join5(process.cwd(), ".mcp.json");
129457
129656
  if (!existsSync2(mcpPath)) {
@@ -129577,14 +129776,64 @@ async function checkPorts(r, wsPort2, mcpPort2) {
129577
129776
  }
129578
129777
  return { ws, mcp };
129579
129778
  }
129779
+ function evaluateOrphanedVite(input) {
129780
+ const { viteUp, viteConfirmed, wsUp, mcpUp, wsPort: wsPort2, mcpPort: mcpPort2, vitePort } = input;
129781
+ if (!viteUp) return null;
129782
+ if (!viteConfirmed) {
129783
+ return {
129784
+ status: "skip",
129785
+ message: `cannot identify the process on :${vitePort} \u2014 it is listening but did not answer /@vite/client, so it is probably not a Vite dev server`,
129786
+ data: { vite: false, ws: wsUp, mcp: mcpUp, reason: "unconfirmed-vite" }
129787
+ };
129788
+ }
129789
+ if (!wsUp && !mcpUp) {
129790
+ return {
129791
+ status: "warn",
129792
+ message: `Vite dev server on :${vitePort} is running but the backend (:${wsPort2} + :${mcpPort2}) is down \u2014 likely orphaned by a crashed dev session`,
129793
+ fix: `If you meant to run the client alone (npm run dev:client), this is expected. Otherwise kill the process on :${vitePort} and restart: npm run dev:standalone`,
129794
+ data: { vite: true, ws: wsUp, mcp: mcpUp }
129795
+ };
129796
+ }
129797
+ if (wsUp !== mcpUp) {
129798
+ return {
129799
+ status: "skip",
129800
+ message: `cannot tell whether the Vite dev server on :${vitePort} is orphaned \u2014 the backend is only partially up (:${wsPort2} ${wsUp ? "up" : "down"}, :${mcpPort2} ${mcpUp ? "up" : "down"}); see the ports check`,
129801
+ data: { vite: true, ws: wsUp, mcp: mcpUp, reason: "partial-backend" }
129802
+ };
129803
+ }
129804
+ return {
129805
+ status: "pass",
129806
+ message: `Vite dev server (:${vitePort}) running alongside the backend`,
129807
+ data: { vite: true, ws: wsUp, mcp: mcpUp }
129808
+ };
129809
+ }
129810
+ async function checkOrphanedVite(r, wsUp, mcpUp, wsPort2, mcpPort2, vitePort) {
129811
+ const viteUp = await probePort(vitePort);
129812
+ const viteConfirmed = viteUp ? await isViteDevServer(vitePort) : false;
129813
+ recordEvaluation(
129814
+ r,
129815
+ evaluateOrphanedVite({ viteUp, viteConfirmed, wsUp, mcpUp, wsPort: wsPort2, mcpPort: mcpPort2, vitePort })
129816
+ );
129817
+ }
129580
129818
  function httpGet(url, timeoutMs = 3e3) {
129581
129819
  return new Promise((resolve5) => {
129582
129820
  const req = request(url, { timeout: timeoutMs }, (res) => {
129583
129821
  let body = "";
129822
+ let bytes = 0;
129823
+ let truncated = false;
129584
129824
  res.on("data", (chunk2) => {
129825
+ if (truncated) return;
129826
+ bytes += Buffer.byteLength(chunk2);
129827
+ if (bytes > HTTP_MAX_BYTES) {
129828
+ truncated = true;
129829
+ res.destroy();
129830
+ resolve5({ status: res.statusCode, data: null });
129831
+ return;
129832
+ }
129585
129833
  body += chunk2;
129586
129834
  });
129587
129835
  res.on("end", () => {
129836
+ if (truncated) return;
129588
129837
  try {
129589
129838
  resolve5({ status: res.statusCode, data: JSON.parse(body) });
129590
129839
  } catch {
@@ -129600,6 +129849,10 @@ function httpGet(url, timeoutMs = 3e3) {
129600
129849
  req.end();
129601
129850
  });
129602
129851
  }
129852
+ async function isViteDevServer(port) {
129853
+ const result2 = await httpGet(`http://127.0.0.1:${port}/@vite/client`, 2e3);
129854
+ return result2?.status === 200;
129855
+ }
129603
129856
  async function checkHealth(r, mcpPort2) {
129604
129857
  const result2 = await httpGet(`http://127.0.0.1:${mcpPort2}/health`);
129605
129858
  if (!result2) {
@@ -129839,7 +130092,7 @@ function evaluateStaleGlobal(bundled, globalVersion) {
129839
130092
  };
129840
130093
  }
129841
130094
  async function checkStaleGlobal(r) {
129842
- const bundled = true ? "0.16.0" : null;
130095
+ const bundled = true ? "0.17.0" : null;
129843
130096
  if (!bundled) return;
129844
130097
  let globalVersion;
129845
130098
  try {
@@ -129858,16 +130111,37 @@ async function checkStaleGlobal(r) {
129858
130111
  async function runDoctor(opts = {}) {
129859
130112
  const wsPort2 = opts.wsPort ?? DEFAULT_WS_PORT;
129860
130113
  const mcpPort2 = opts.mcpPort ?? DEFAULT_MCP_PORT;
130114
+ const vitePort = opts.vitePort ?? VITE_DEV_PORT;
129861
130115
  const r = new Recorder();
130116
+ const cwd = process.cwd();
130117
+ const repo = probeTandemEditorRepo(cwd);
130118
+ const devRepo = repo === "yes";
129862
130119
  await r.check("node-version", () => checkNodeVersion(r));
129863
130120
  await r.check("node-modules", () => checkNodeModules(r));
130121
+ if (repo === "unreadable") {
130122
+ await r.check(
130123
+ "dev-repo",
130124
+ () => r.warn(
130125
+ "package.json in the current directory could not be read \u2014 if this is the tandem-editor checkout, the npm-staleness and orphaned-Vite checks are being skipped",
130126
+ "Check for merge-conflict markers or a truncated file: git checkout package.json"
130127
+ )
130128
+ );
130129
+ }
130130
+ if (devRepo) {
130131
+ await r.check("npm-staleness", () => checkNpmStaleness(r, cwd));
130132
+ }
129864
130133
  await r.check("mcp-json", () => checkMcpJson(r));
129865
130134
  await r.check("user-mcp-config", () => checkUserMcpConfig(r));
129866
130135
  await r.check("annotation-store", () => checkAnnotationStore(r));
129867
130136
  await r.check("stale-global", () => checkStaleGlobal(r));
129868
- const { mcp } = await r.check("ports", () => checkPorts(r, wsPort2, mcpPort2));
130137
+ const ports = await r.check("ports", () => checkPorts(r, wsPort2, mcpPort2));
130138
+ const ws = ports?.ws ?? false;
130139
+ const mcp = ports?.mcp ?? false;
130140
+ if (devRepo) {
130141
+ await r.check("orphaned-vite", () => checkOrphanedVite(r, ws, mcp, wsPort2, mcpPort2, vitePort));
130142
+ }
129869
130143
  if (mcp) {
129870
- const healthy = await r.check("health", () => checkHealth(r, mcpPort2));
130144
+ const healthy = await r.check("health", () => checkHealth(r, mcpPort2)) ?? false;
129871
130145
  if (healthy) {
129872
130146
  await r.check("sse", () => checkSseEndpoint(r, mcpPort2));
129873
130147
  }
@@ -129882,7 +130156,7 @@ async function runDoctor(opts = {}) {
129882
130156
  results: r.results
129883
130157
  };
129884
130158
  }
129885
- var Recorder;
130159
+ var Recorder, VITE_DEV_PORT, HTTP_MAX_BYTES;
129886
130160
  var init_doctor = __esm({
129887
130161
  "src/cli/doctor.ts"() {
129888
130162
  "use strict";
@@ -129893,11 +130167,29 @@ var init_doctor = __esm({
129893
130167
  warnings = 0;
129894
130168
  results = [];
129895
130169
  currentCheck = "";
130170
+ /**
130171
+ * Run one check under `name`. A throwing check is contained here and
130172
+ * recorded as a `fail` rather than taking the whole report down.
130173
+ *
130174
+ * Returns `T | undefined` — NOT `T`. The `undefined` is load-bearing and
130175
+ * must not be cast away: a crashed check produced no value, and the two
130176
+ * callers that consume one (`ports` destructures `{ws, mcp}`; `health`
130177
+ * gates the `sse` check) have to say what they want instead. Returning
130178
+ * `undefined as T` would satisfy the compiler and then throw a TypeError
130179
+ * on the destructure — converting a clear crash into a confusing one on
130180
+ * the exact path this containment exists to protect.
130181
+ */
129896
130182
  async check(name2, fn) {
129897
130183
  const prev = this.currentCheck;
129898
130184
  this.currentCheck = name2;
129899
130185
  try {
129900
130186
  return await fn();
130187
+ } catch (err) {
130188
+ this.fail(
130189
+ `${name2} check crashed (${errorClass(err)}) \u2014 the rest of the report is still valid`,
130190
+ "Please report this at https://github.com/bloknayrb/tandem/issues"
130191
+ );
130192
+ return void 0;
129901
130193
  } finally {
129902
130194
  this.currentCheck = prev;
129903
130195
  }
@@ -129920,6 +130212,8 @@ var init_doctor = __esm({
129920
130212
  this.record("fail", msg, fix, fields);
129921
130213
  }
129922
130214
  };
130215
+ VITE_DEV_PORT = 5173;
130216
+ HTTP_MAX_BYTES = 256 * 1024;
129923
130217
  }
129924
130218
  });
129925
130219
 
@@ -129974,7 +130268,13 @@ var init_diagnostics = __esm({
129974
130268
  "use strict";
129975
130269
  init_doctor();
129976
130270
  init_middleware();
129977
- DEV_REPO_CHECKS = /* @__PURE__ */ new Set(["node-modules", "mcp-json"]);
130271
+ DEV_REPO_CHECKS = /* @__PURE__ */ new Set([
130272
+ "node-modules",
130273
+ "mcp-json",
130274
+ "npm-staleness",
130275
+ "orphaned-vite",
130276
+ "dev-repo"
130277
+ ]);
129978
130278
  }
129979
130279
  });
129980
130280
 
@@ -188114,7 +188414,7 @@ init_document2();
188114
188414
  init_document_service();
188115
188415
  init_docx_apply2();
188116
188416
  var esmRequire2 = createRequire2(import.meta.url);
188117
- var APP_VERSION = true ? "0.16.0" : _readVersionFromDisk();
188417
+ var APP_VERSION = true ? "0.17.0" : _readVersionFromDisk();
188118
188418
  var MCP_SDK_VERSION = true ? "1.27.1" : "0.0.0-unknown";
188119
188419
  var __dirname3 = dirname3(fileURLToPath4(import.meta.url));
188120
188420
  var CLIENT_DIST = join7(__dirname3, "../client");