@dev.fast/whiteboard 0.2.1-preview.20261007.102 → 0.2.1-preview.20261007.103

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.
@@ -3,8 +3,8 @@ import { a as toolResultText, i as connectReviewInstance, s as ReviewApiError }
3
3
  import { A as devReviewHome } from "./s3-config-CAxhO9u_.js";
4
4
  import { T as traceMachineEnabled } from "./review-home-paths-6zZH-1c9.js";
5
5
  import { d as selectReviewInstance, t as REVIEW_INSTANCE_ENV } from "./desktop-discovery-DK2HoBy5.js";
6
- import { n as reviewSessionAgent } from "./review-telemetry-2UlsdE4D.js";
7
- import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-BB2TENZ5.js";
6
+ import { n as reviewSessionAgent } from "./review-telemetry-Pp4ZsVrh.js";
7
+ import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-DlHTFCrQ.js";
8
8
  import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-Djeeax6q.js";
9
9
  //#region src/review-api/agent-cli.ts
10
10
  const reviewAgentCliHelp = "whiteboard api tools (one line per tool)\nwhiteboard api tools <tool-name> (its description and input schema)\nwhiteboard api <tool-name> '<json>'\nwhiteboard api <tool-name> - (read JSON from stdin)\nwhiteboard mcp (stdio MCP adapter; Whiteboard Desktop or whiteboard server start must be running)\nSelect headless state with DEV_REVIEW_SERVER_DIR or whiteboard --state-dir <path> api/mcp.\n";
@@ -23,7 +23,7 @@ async function runReviewAgentCli(input) {
23
23
  [REVIEW_AGENT_HEADER]: reviewSessionAgent(env)
24
24
  };
25
25
  if (mode === "mcp") {
26
- const { serveReviewMcp } = await import("./mcp-DGKhDPMs.js");
26
+ const { serveReviewMcp } = await import("./mcp-BF2_RoAR.js");
27
27
  await serveReviewMcp(async (key, agentKind) => {
28
28
  const connected = await connectReviewInstance(key ? {
29
29
  ...env,
@@ -0,0 +1,2 @@
1
+ import { n as runReviewAgentCli } from "./agent-cli-DTY727wD.js";
2
+ export { runReviewAgentCli };
@@ -1,4 +1,4 @@
1
- import { g as activityUpdateSchema, h as activityEndSchema, i as commandSchema, m as activityBeginSchema, n as uploadSchema } from "./local-data-Dy8AngNb.js";
1
+ import { g as activityUpdateSchema, h as activityEndSchema, i as commandSchema, m as activityBeginSchema, n as uploadSchema } from "./local-data-B_Kfm3Id.js";
2
2
  import { x as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-T9Uj5MkN.js";
3
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
4
4
  import { t as findReviewPackageRoot } from "./package-paths-B6-zxvIO.js";
@@ -1 +1 @@
1
- {"version":"0.2.1-preview.20261007.102","commit":"912c9427199c112f1f66e09b6a9ca86cc6797968","dirty":true,"builtAt":"2026-10-07T13:39:16.096Z"}
1
+ {"version":"0.2.1-preview.20261007.103","commit":"41906b158c7a3e7cbf8a4bcd4e66a3441006b53b","dirty":true,"builtAt":"2026-10-07T16:52:59.104Z"}
@@ -9,11 +9,11 @@ import { t as errorMessage } from "./error-message-OtiDonty.js";
9
9
  import { A as runTraceSessions, D as runTraceAllow, E as traceMachineStatus, G as traceScope, H as resolveTraceCommand, I as traceRepositoryStatus, L as HOSTED_CAPTURE_SCOPE_DESCRIPTION, M as runTraceStoreInfo, O as runTraceDeny, T as traceMachineEnabled, W as traceHomeDir, _ as runTraceRepair, b as runTraceGitHook, c as registerTraceCommands, d as runTraceList$1, f as runTracePull$1, g as runTraceInstallMachine, h as runTraceEnable, j as runTraceStoreDelete, k as runTraceOnboard, l as resolveTraceReadStorage, m as runTraceDisable, p as runTraceShow, u as runTraceBlame, v as runTraceStatus, w as readLegacyCaptureSettings, x as runTraceHook, y as runTraceSync } from "./review-home-paths-6zZH-1c9.js";
10
10
  import { i as readReviewPackageVersion, r as readBuildInfo, t as findReviewPackageRoot } from "./package-paths-B6-zxvIO.js";
11
11
  import { a as readDefaultReviewInstance, d as selectReviewInstance, f as writeDefaultReviewInstance, i as healthyReviewInstance, l as reviewInstanceStartHint, n as ReviewInstanceUnavailableError, o as readReviewInstances, r as clearDefaultReviewInstance, s as requireHealthyReviewDesktop, t as REVIEW_INSTANCE_ENV, u as reviewInstanceUnavailable } from "./desktop-discovery-DK2HoBy5.js";
12
- import { t as ReviewTelemetry } from "./review-telemetry-2UlsdE4D.js";
12
+ import { t as ReviewTelemetry } from "./review-telemetry-Pp4ZsVrh.js";
13
13
  import { a as isOwnedShim, f as windowsInstallerCommand, m as connectPrompts, o as pathShimPath, p as scanLegacySkills, t as aliasInstallationToAccount } from "./account-alias-DLE9XZhi.js";
14
14
  import { t as fuzzyRank } from "./fuzzy-match-BoAmcyak.js";
15
15
  import { n as readReviewServerDiscovery, o as reviewServerStateDir, r as readReviewServerHealth, s as serverNotReady } from "./server-discovery-Dbzn3w6Z.js";
16
- import { t as reviewAgentCliHelp } from "./agent-cli-d0YJQNrR.js";
16
+ import { t as reviewAgentCliHelp } from "./agent-cli-DTY727wD.js";
17
17
  import { closeSync, existsSync, fstatSync, mkdirSync, mkdtempSync, openSync, readSync, renameSync, writeFileSync } from "node:fs";
18
18
  import { realpath } from "node:fs/promises";
19
19
  import path from "node:path";
@@ -4495,7 +4495,7 @@ async function runReviewCli(input) {
4495
4495
  process.once("SIGINT", stop);
4496
4496
  process.once("SIGTERM", stop);
4497
4497
  try {
4498
- const { runHeadlessServer } = await import("./headless-host-YxYKMcI5.js");
4498
+ const { runHeadlessServer } = await import("./headless-host-CUyloTAd.js");
4499
4499
  await runHeadlessServer({
4500
4500
  stateDir,
4501
4501
  port,
@@ -4546,7 +4546,7 @@ async function runReviewCli(input) {
4546
4546
  if (problem) throw new Error(`Cannot tell whether a Whiteboard Desktop is using ${stateDir}: ${problem.message}`);
4547
4547
  const inUse = () => /* @__PURE__ */ new Error(`A Whiteboard server is using ${stateDir}. Stop it first, then run whiteboard server reset-id again.`);
4548
4548
  if (desktops.instances.some(({ discovery }) => [discovery.appPid, discovery.serverPid].some((pid) => processIsAlive(pid)))) throw inUse();
4549
- const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-D8XSrJYI.js"), import("./headless-host-YxYKMcI5.js")]);
4549
+ const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-BKncMvSE.js"), import("./headless-host-CUyloTAd.js")]);
4550
4550
  const reset = await withHeadlessServerLock(await realpath(stateDir), async () => {
4551
4551
  const local = await openReviewProfile(stateDir, { manageWorkspaces: false });
4552
4552
  try {
@@ -4774,7 +4774,7 @@ async function runReviewCli(input) {
4774
4774
  return commands;
4775
4775
  } });
4776
4776
  for (const [name, description] of [["api", "Call a JSON review authoring tool on the running server"], ["mcp", "Serve the JSON review authoring tools over stdio MCP"]]) configureOutput(program.command(name).description(description).argument("[args...]", "tool name and JSON input").allowUnknownOption().allowExcessArguments().helpOption(false).passThroughOptions().addHelpText("after", `\n${reviewAgentCliHelp}`), "plain").action(async (args) => {
4777
- const { runReviewAgentCli } = await import("./agent-cli-DdfuiyOF.js");
4777
+ const { runReviewAgentCli } = await import("./agent-cli-Q_EpjIbV.js");
4778
4778
  state.exitCode = await runReviewAgentCli({
4779
4779
  ...input,
4780
4780
  env: authoringEnv(),
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ async function runCli(effectivePath) {
17
17
  `);
18
18
  return 1;
19
19
  }
20
- const { runReviewCli } = await import("./cli-runner-CSEH_mQN.js");
20
+ const { runReviewCli } = await import("./cli-runner-BApCwwRD.js");
21
21
  return runReviewCli({
22
22
  argv,
23
23
  cliPaths: {
@@ -1,8 +1,8 @@
1
1
  import { p as isObjectValue } from "./src-X9phtB2j.js";
2
2
  import { O as withFileLock, P as writePrivateJsonAtomic } from "./s3-config-CAxhO9u_.js";
3
3
  import "./review-home-paths-6zZH-1c9.js";
4
- import { t as openReviewProfile } from "./profile-B6jrb2QN.js";
5
- import { i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-DN5BpZnc.js";
4
+ import { t as openReviewProfile } from "./profile-CeT6-ZWV.js";
5
+ import { i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-Gp3cCIu4.js";
6
6
  import { i as reviewServerDiscoveryPath, t as headlessServerLockPath } from "./server-discovery-Dbzn3w6Z.js";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
8
8
  import { mkdir, realpath, rm } from "node:fs/promises";
@@ -2409,6 +2409,7 @@ var ReviewStore = class {
2409
2409
  repositoryGroups = /* @__PURE__ */ new Map();
2410
2410
  repositoryGroup(root) {
2411
2411
  if (this.repositoryGroups.has(root)) return this.repositoryGroups.get(root);
2412
+ if (!existsSync(root)) return void 0;
2412
2413
  const context = resolveRepoContextSync(root);
2413
2414
  if (!context) return void 0;
2414
2415
  const group = context.githubSlug ? {
@@ -3332,6 +3333,47 @@ var ReviewWorkspaces = class {
3332
3333
  }
3333
3334
  };
3334
3335
  //#endregion
3336
+ //#region src/review-api/worktree-events.ts
3337
+ const exec$1 = promisify(execFile);
3338
+ const normalize = (name) => name.replaceAll("\\", "/").normalize("NFC");
3339
+ const parent = (file) => file.slice(0, Math.max(0, file.lastIndexOf("/")));
3340
+ /** Directories that hold tracked files, the root as "". */
3341
+ function trackedDirectories(files) {
3342
+ const directories = /* @__PURE__ */ new Set([""]);
3343
+ for (const file of files) for (let dir = parent(normalize(file)); !directories.has(dir); dir = parent(dir)) directories.add(dir);
3344
+ return directories;
3345
+ }
3346
+ /** Only a tracked directory's entries can change an inspection. */
3347
+ function rootEventMatters(name, tracked) {
3348
+ const file = normalize(name);
3349
+ if (file === ".git" || file.startsWith(".git/")) return false;
3350
+ return !tracked || tracked.has(parent(file));
3351
+ }
3352
+ const CHECKOUT_STATE = /^(?:index|[A-Z_]+|(?:rebase-merge|rebase-apply|sequencer|refs\/(?:bisect|worktree|rewritten))(?:\/.*)?)$/;
3353
+ /** Objects, reflogs, locks and other checkouts' state cannot change this one. */
3354
+ function gitEventMatters(name, prefix) {
3355
+ const file = normalize(name);
3356
+ if (file.endsWith(".lock") || /^(?:objects|logs)\//.test(file)) return false;
3357
+ if (file.startsWith("worktrees/")) return file.startsWith(`${prefix}/`) && !file.startsWith(`${prefix}/logs/`);
3358
+ return !prefix || !CHECKOUT_STATE.test(file);
3359
+ }
3360
+ /** Where this checkout's git directory sits in the common one: "" for the main checkout. */
3361
+ async function gitDirectoryPrefix(rootPath, common) {
3362
+ try {
3363
+ const { stdout } = await exec$1("git", [
3364
+ "-C",
3365
+ rootPath,
3366
+ "rev-parse",
3367
+ "--path-format=absolute",
3368
+ "--git-dir"
3369
+ ]);
3370
+ const prefix = normalize(relative(common, stdout.trim()));
3371
+ return isAbsolute(prefix) || prefix.startsWith("..") ? null : prefix;
3372
+ } catch {
3373
+ return null;
3374
+ }
3375
+ }
3376
+ //#endregion
3335
3377
  //#region src/review-api/worktree-source.ts
3336
3378
  const { lstat: lstat$1, readFile: readFile$1, readlink, realpath: realpath$1 } = checkoutFs;
3337
3379
  const exec = promisify(execFile);
@@ -3403,7 +3445,8 @@ async function untrackedFileCount(vcs) {
3403
3445
  async function inspectWorktree(repositoryId, vcs) {
3404
3446
  const commit = (await vcs.resolveRevision(vcs.kind === "jj" ? "@" : "HEAD"))?.commit ?? "empty-worktree-baseline";
3405
3447
  const hash = createHash("sha256").update(`${repositoryId}\0${commit}`);
3406
- for (const file of await workingFiles(vcs)) try {
3448
+ const files = await workingFiles(vcs);
3449
+ for (const file of files) try {
3407
3450
  const info = await lstat$1(resolve(vcs.rootPath, file), { bigint: true });
3408
3451
  hash.update(`\0${file}\0${info.ino}:${info.size}:${info.mode}:${info.mtimeNs}:${info.ctimeNs}`);
3409
3452
  } catch (error) {
@@ -3411,7 +3454,8 @@ async function inspectWorktree(repositoryId, vcs) {
3411
3454
  }
3412
3455
  return {
3413
3456
  revision: hash.digest("hex"),
3414
- commit
3457
+ commit,
3458
+ files
3415
3459
  };
3416
3460
  }
3417
3461
  async function readWorkingFile(root, file) {
@@ -3732,12 +3776,13 @@ var LocalReviewData = class {
3732
3776
  };
3733
3777
  this.worktrees.set(repositoryId, entry);
3734
3778
  const state = entry;
3735
- const roots = /* @__PURE__ */ new Set([vcs.rootPath]);
3736
3779
  const common = await gitCommonDir(vcs.rootPath);
3737
- if (common) roots.add(common);
3738
- for (const root of roots) try {
3739
- const watcher = (this.options.watch ?? watch)(root, { recursive: true }, () => {
3740
- state.epoch++;
3780
+ const prefix = vcs.kind === "git" && common ? await gitDirectoryPrefix(vcs.rootPath, common) : null;
3781
+ const roots = /* @__PURE__ */ new Map([[vcs.rootPath, (name) => prefix === null || rootEventMatters(name, state.tracked)]]);
3782
+ if (common) roots.set(common, (name) => prefix === null || gitEventMatters(name, prefix));
3783
+ for (const [root, matters] of roots) try {
3784
+ const watcher = (this.options.watch ?? watch)(root, { recursive: true }, (_event, name) => {
3785
+ if (name === null || matters(String(name))) state.epoch++;
3741
3786
  });
3742
3787
  watcher.on("error", () => {
3743
3788
  state.epoch++;
@@ -3756,8 +3801,9 @@ var LocalReviewData = class {
3756
3801
  forks: entry.forks
3757
3802
  };
3758
3803
  const epoch = entry.epoch;
3759
- const inspected = await inspectWorktree(repositoryId, vcs);
3804
+ const { files, ...inspected } = await inspectWorktree(repositoryId, vcs);
3760
3805
  entry.inspection = inspected;
3806
+ entry.tracked = trackedDirectories(files);
3761
3807
  entry.inspectedEpoch = epoch;
3762
3808
  entry.forks = /* @__PURE__ */ new Map();
3763
3809
  return {
@@ -3786,6 +3832,10 @@ var LocalReviewData = class {
3786
3832
  this.closeReader(repositoryId);
3787
3833
  this.forgetWorktree(repositoryId);
3788
3834
  const rootPath = this.store.repositoryPath(repositoryId);
3835
+ if (!existsSync(rootPath)) {
3836
+ this.repositories.delete(repositoryId);
3837
+ return Promise.resolve(null);
3838
+ }
3789
3839
  const forget = () => {
3790
3840
  this.repositories.delete(repositoryId);
3791
3841
  this.closeReader(repositoryId);
@@ -1,6 +1,6 @@
1
1
  import { a as toolResultText, s as ReviewApiError } from "./agent-client-DoaODH6I.js";
2
2
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
3
- import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-Bvre8hN2.js";
3
+ import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-ml8wRIRj.js";
4
4
  import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-Djeeax6q.js";
5
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -1,6 +1,6 @@
1
1
  import { c as jsonString, m as isStringValue, n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
2
  import { a as sharedReviewId, c as verifyShareRepository, d as StreamLimitError, f as readBoundedStream, l as exportShare, m as shareIdSchema, n as ShareClient, p as parseShareLink, r as SharePreflightError, s as readShareRepository, t as ShareAuthError, u as validateShareSources } from "./client-D01H2KOV.js";
3
- import { _ as invalidateStructuralComparisons, a as inspectSnapshot, c as askAgentIds, d as askQuestionSchema, f as AgentSelectionSchema, i as commandSchema, l as askChoiceKinds, p as selectionMarkdown, r as SCRATCHPAD_ID, s as applyAskChange, u as askPicksSchema, v as diffrExecutable, y as diffrMissingError } from "./local-data-Dy8AngNb.js";
3
+ import { _ as invalidateStructuralComparisons, a as inspectSnapshot, c as askAgentIds, d as askQuestionSchema, f as AgentSelectionSchema, i as commandSchema, l as askChoiceKinds, p as selectionMarkdown, r as SCRATCHPAD_ID, s as applyAskChange, u as askPicksSchema, v as diffrExecutable, y as diffrMissingError } from "./local-data-B_Kfm3Id.js";
4
4
  import { d as REVIEW_CLIENT_REMOTE, i as parseReviewVerbRequest, r as parseReviewDesktopVerbResult, u as REVIEW_CLIENT_HEADER, v as reviewDiffrSummarizerInputSchema, y as decodeStructuralDiffEvent } from "./src-DHiDJQDy.js";
5
5
  import { C as TraceStorageDeniedError, G as readStoreAuth, H as DEFAULT_STORE_ORIGIN, U as clearStoreAuth, W as openUrlInBrowser, _ as isTraceStorageConfigured, b as selectTraceStorage, o as listReviewTraceSessions, q as runStoreLogin, s as loadReviewAgentTrace, y as resolveTraceStorage } from "./review-agent-traces-DNJFYzc8.js";
6
6
  import { $ as unionIntervals, G as selectionKey, H as comparisonKey, I as fileLineRangeSchema, J as coverageSources, N as lensSelections, Q as subtractIntervals, T as selectionReferences, U as formatAnchor, V as anchorSelection, W as resolveDiffSelection, Y as emptyCoverage, Z as scopedCoverage, h as elements, k as UNCATEGORIZED_LENS_ID, u as anchorPins } from "./stored-document-migration-T9Uj5MkN.js";
@@ -9,8 +9,8 @@ import { A as devReviewHome, h as TraceConfigurationError, i as isS3MockMode } f
9
9
  import { t as errorMessage } from "./error-message-OtiDonty.js";
10
10
  import { T as traceMachineEnabled } from "./review-home-paths-6zZH-1c9.js";
11
11
  import { i as readReviewPackageVersion, n as readBuildCommit } from "./package-paths-B6-zxvIO.js";
12
- import { a as REVIEW_APP_SESSION_ID_HEADER, c as sanitizeUiTelemetryEvent, i as BUNDLE_FRAME_PATTERN, l as cleanTelemetryText, r as reviewRequestOrigin, s as isReportableCleanedMessage } from "./request-origin-BB2TENZ5.js";
13
- import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-Bvre8hN2.js";
12
+ import { a as REVIEW_APP_SESSION_ID_HEADER, c as sanitizeUiTelemetryEvent, i as BUNDLE_FRAME_PATTERN, l as cleanTelemetryText, r as reviewRequestOrigin, s as isReportableCleanedMessage } from "./request-origin-DlHTFCrQ.js";
13
+ import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-ml8wRIRj.js";
14
14
  import { t as fuzzyRank } from "./fuzzy-match-BoAmcyak.js";
15
15
  import { createRequire } from "node:module";
16
16
  import crypto$1, { createHash, randomUUID } from "node:crypto";
@@ -0,0 +1,2 @@
1
+ import { t as openReviewProfile } from "./profile-CeT6-ZWV.js";
2
+ export { openReviewProfile };
@@ -1,4 +1,4 @@
1
- import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-Dy8AngNb.js";
1
+ import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-B_Kfm3Id.js";
2
2
  import { O as withFileLock } from "./s3-config-CAxhO9u_.js";
3
3
  import "./review-home-paths-6zZH-1c9.js";
4
4
  import { access, mkdir } from "node:fs/promises";
@@ -228,7 +228,8 @@ const SETTING_NAME = [
228
228
  "theme",
229
229
  "ctrl_tab",
230
230
  "ready_notification",
231
- "document_width"
231
+ "document_width",
232
+ "code_font_size"
232
233
  ];
233
234
  const REVIEW_OPENED_VIA = [
234
235
  "home",
@@ -3,7 +3,7 @@ import "./src-DHiDJQDy.js";
3
3
  import { A as devReviewHome, D as processIsAlive, M as writeFileAtomic, O as withFileLock } from "./s3-config-CAxhO9u_.js";
4
4
  import "./review-home-paths-6zZH-1c9.js";
5
5
  import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "./package-paths-B6-zxvIO.js";
6
- import "./request-origin-BB2TENZ5.js";
6
+ import "./request-origin-DlHTFCrQ.js";
7
7
  import { createHmac, randomUUID } from "node:crypto";
8
8
  import { z } from "zod";
9
9
  import { existsSync, readFileSync, rmSync } from "node:fs";
@@ -9,12 +9,12 @@ import { A as devReviewHome, P as writePrivateJsonAtomic } from "../s3-config-CA
9
9
  import { U as shellQuote, i as reviewInstanceDiscoveryPath, o as reviewLegacyDiscoveryPath } from "../review-home-paths-6zZH-1c9.js";
10
10
  import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "../package-paths-B6-zxvIO.js";
11
11
  import { c as reviewInstanceIdentity, t as REVIEW_INSTANCE_ENV } from "../desktop-discovery-DK2HoBy5.js";
12
- import { t as openReviewProfile } from "../profile-B6jrb2QN.js";
13
- import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-2UlsdE4D.js";
14
- import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-BB2TENZ5.js";
12
+ import { t as openReviewProfile } from "../profile-CeT6-ZWV.js";
13
+ import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-Pp4ZsVrh.js";
14
+ import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-DlHTFCrQ.js";
15
15
  import { c as removeLegacyReviewSkills, d as skipCliInstall, i as finishCliInstallUpdate, l as resetCliInstall, n as applyCliInstall, r as declineCliInstall, s as removeCliInstall, t as aliasInstallationToAccount, u as resolveCliInstallStatus } from "../account-alias-DLE9XZhi.js";
16
- import { _ as setDiffrConfigValue, a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, g as saveDiffrSummarizer, h as readDiffrConfig, i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson, v as testDiffrSummarizer } from "../process-error-telemetry-DN5BpZnc.js";
17
- import { c as readQuerySchemas } from "../authoring-tools-Bvre8hN2.js";
16
+ import { _ as setDiffrConfigValue, a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, g as saveDiffrSummarizer, h as readDiffrConfig, i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson, v as testDiffrSummarizer } from "../process-error-telemetry-Gp3cCIu4.js";
17
+ import { c as readQuerySchemas } from "../authoring-tools-ml8wRIRj.js";
18
18
  import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
19
19
  import { z } from "zod";
20
20
  import fs, { existsSync } from "node:fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.fast/whiteboard",
3
- "version": "0.2.1-preview.20261007.102",
3
+ "version": "0.2.1-preview.20261007.103",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,8 +45,8 @@
45
45
  "@agentclientprotocol/sdk": "1.5.1",
46
46
  "@dev.fast/diffr": "0.1.14",
47
47
  "@hono/node-server": "2.0.12",
48
- "@img/sharp-wasm32": "0.35.4",
49
- "@modelcontextprotocol/sdk": "1.30.0",
48
+ "@img/sharp-wasm32": "0.35.5",
49
+ "@modelcontextprotocol/sdk": "1.31.0",
50
50
  "commander": "14.0.3",
51
51
  "fuzzysort": "4.0.2",
52
52
  "hono": "4.13.7",
@@ -57,8 +57,8 @@
57
57
  "pi-acp": "0.0.34",
58
58
  "pino": "10.3.1",
59
59
  "semver": "7.8.5",
60
- "sharp": "0.35.4",
61
- "smol-toml": "1.8.0",
60
+ "sharp": "0.35.5",
61
+ "smol-toml": "1.9.0",
62
62
  "zod": "4.6.5"
63
63
  },
64
64
  "devDependencies": {
@@ -72,9 +72,9 @@
72
72
  "typescript": "6.0.3",
73
73
  "vitest": "4.1.11",
74
74
  "@dev.fast/json": "0.0.0",
75
+ "@dev.fast/review-share-protocol": "0.1.2",
75
76
  "@dev.fast/trace-core": "0.0.0",
76
77
  "@dev.fast/trace-protocol": "0.5.0",
77
- "@dev.fast/review-share-protocol": "0.1.2",
78
78
  "@dev.fast/review-protocol": "0.0.0"
79
79
  },
80
80
  "engines": {
@@ -85,7 +85,7 @@
85
85
  "repo": "devdotfast/review",
86
86
  "docs": "packages/review/instructions/authoring.md"
87
87
  },
88
- "gitHead": "912c9427199c112f1f66e09b6a9ca86cc6797968",
88
+ "gitHead": "41906b158c7a3e7cbf8a4bcd4e66a3441006b53b",
89
89
  "scripts": {
90
90
  "prebuild": "pnpm run build:workspace-deps",
91
91
  "build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",
@@ -87,6 +87,12 @@ import {
87
87
  } from "./store.js";
88
88
  import { traceSchema } from "./trace-schema.js";
89
89
  import { ReviewWorkspaces } from "./workspaces.js";
90
+ import {
91
+ gitDirectoryPrefix,
92
+ gitEventMatters,
93
+ rootEventMatters,
94
+ trackedDirectories,
95
+ } from "./worktree-events.js";
90
96
  import {
91
97
  EMPTY_SOURCE,
92
98
  inspectWorktree,
@@ -636,7 +642,8 @@ export class LocalReviewData {
636
642
  inspectedEpoch: number;
637
643
  watchers: FSWatcher[];
638
644
  healthy: boolean;
639
- inspection?: Awaited<ReturnType<typeof inspectWorktree>>;
645
+ inspection?: Omit<Awaited<ReturnType<typeof inspectWorktree>>, "files">;
646
+ tracked?: Set<string>;
640
647
  /** Fork points by base ref, valid for the inspected epoch. */
641
648
  forks: Map<string, Promise<{ ref: string; commit: string }>>;
642
649
  }
@@ -662,18 +669,35 @@ export class LocalReviewData {
662
669
  };
663
670
  this.worktrees.set(repositoryId, entry);
664
671
  const state = entry;
665
- const roots = new Set([vcs.rootPath]);
666
672
  const common = await gitCommonDir(vcs.rootPath);
667
673
 
668
- if (common) roots.add(common);
674
+ // jj snapshots untracked files, so every event can matter there.
675
+ const prefix =
676
+ vcs.kind === "git" && common
677
+ ? await gitDirectoryPrefix(vcs.rootPath, common)
678
+ : null;
679
+
680
+ const roots = new Map([
681
+ [
682
+ vcs.rootPath,
683
+ (name: string) =>
684
+ prefix === null || rootEventMatters(name, state.tracked),
685
+ ],
686
+ ]);
687
+
688
+ if (common)
689
+ roots.set(
690
+ common,
691
+ (name) => prefix === null || gitEventMatters(name, prefix),
692
+ );
669
693
 
670
- for (const root of roots) {
694
+ for (const [root, matters] of roots) {
671
695
  try {
672
696
  const watcher = (this.options.watch ?? watch)(
673
697
  root,
674
698
  { recursive: true },
675
- () => {
676
- state.epoch++;
699
+ (_event, name) => {
700
+ if (name === null || matters(String(name))) state.epoch++;
677
701
  },
678
702
  );
679
703
 
@@ -700,8 +724,9 @@ export class LocalReviewData {
700
724
  return { ...entry.inspection, forks: entry.forks };
701
725
 
702
726
  const epoch = entry.epoch;
703
- const inspected = await inspectWorktree(repositoryId, vcs);
727
+ const { files, ...inspected } = await inspectWorktree(repositoryId, vcs);
704
728
  entry.inspection = inspected;
729
+ entry.tracked = trackedDirectories(files);
705
730
  entry.inspectedEpoch = epoch;
706
731
  entry.forks = new Map();
707
732
 
@@ -738,6 +763,13 @@ export class LocalReviewData {
738
763
  this.forgetWorktree(repositoryId);
739
764
  const rootPath = this.store.repositoryPath(repositoryId);
740
765
 
766
+ // Detection in a missing directory spawns and fails as "tools missing".
767
+ if (!existsSync(rootPath)) {
768
+ this.repositories.delete(repositoryId);
769
+
770
+ return Promise.resolve(null);
771
+ }
772
+
741
773
  const forget = () => {
742
774
  this.repositories.delete(repositoryId);
743
775
  this.closeReader(repositoryId);
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { existsSync } from "node:fs";
2
3
  import path from "node:path";
3
4
  import { DatabaseSync } from "node:sqlite";
4
5
 
@@ -527,6 +528,9 @@ export class ReviewStore {
527
528
  private repositoryGroup(root: string): ReviewApiSummary["repositoryGroup"] {
528
529
  if (this.repositoryGroups.has(root)) return this.repositoryGroups.get(root);
529
530
 
531
+ // Resolving a missing checkout would spawn on every listing.
532
+ if (!existsSync(root)) return undefined;
533
+
530
534
  const context = resolveRepoContextSync(root);
531
535
 
532
536
  if (!context) return undefined;
@@ -0,0 +1,76 @@
1
+ import { execFile } from "node:child_process";
2
+ import { isAbsolute, relative } from "node:path";
3
+ import { promisify } from "node:util";
4
+
5
+ const exec = promisify(execFile);
6
+
7
+ const normalize = (name: string) => name.replaceAll("\\", "/").normalize("NFC");
8
+
9
+ const parent = (file: string) =>
10
+ file.slice(0, Math.max(0, file.lastIndexOf("/")));
11
+
12
+ /** Directories that hold tracked files, the root as "". */
13
+ export function trackedDirectories(files: readonly string[]): Set<string> {
14
+ const directories = new Set([""]);
15
+
16
+ for (const file of files)
17
+ for (
18
+ let dir = parent(normalize(file));
19
+ !directories.has(dir);
20
+ dir = parent(dir)
21
+ )
22
+ directories.add(dir);
23
+
24
+ return directories;
25
+ }
26
+
27
+ /** Only a tracked directory's entries can change an inspection. */
28
+ export function rootEventMatters(
29
+ name: string,
30
+ tracked: Set<string> | undefined,
31
+ ): boolean {
32
+ const file = normalize(name);
33
+
34
+ // The git directory has its own watcher.
35
+ if (file === ".git" || file.startsWith(".git/")) return false;
36
+
37
+ return !tracked || tracked.has(parent(file));
38
+ }
39
+
40
+ // A checkout's own state, per gitrepository-layout(5).
41
+ const CHECKOUT_STATE =
42
+ /^(?:index|[A-Z_]+|(?:rebase-merge|rebase-apply|sequencer|refs\/(?:bisect|worktree|rewritten))(?:\/.*)?)$/;
43
+
44
+ /** Objects, reflogs, locks and other checkouts' state cannot change this one. */
45
+ export function gitEventMatters(name: string, prefix: string): boolean {
46
+ const file = normalize(name);
47
+
48
+ if (file.endsWith(".lock") || /^(?:objects|logs)\//.test(file)) return false;
49
+
50
+ if (file.startsWith("worktrees/"))
51
+ return file.startsWith(`${prefix}/`) && !file.startsWith(`${prefix}/logs/`);
52
+
53
+ return !prefix || !CHECKOUT_STATE.test(file);
54
+ }
55
+
56
+ /** Where this checkout's git directory sits in the common one: "" for the main checkout. */
57
+ export async function gitDirectoryPrefix(
58
+ rootPath: string,
59
+ common: string,
60
+ ): Promise<string | null> {
61
+ try {
62
+ const { stdout } = await exec("git", [
63
+ "-C",
64
+ rootPath,
65
+ "rev-parse",
66
+ "--path-format=absolute",
67
+ "--git-dir",
68
+ ]);
69
+
70
+ const prefix = normalize(relative(common, stdout.trim()));
71
+
72
+ return isAbsolute(prefix) || prefix.startsWith("..") ? null : prefix;
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
@@ -127,8 +127,9 @@ export async function inspectWorktree(repositoryId: string, vcs: LocalVcs) {
127
127
  EMPTY_SOURCE;
128
128
 
129
129
  const hash = createHash("sha256").update(`${repositoryId}\0${commit}`);
130
+ const files = await workingFiles(vcs);
130
131
 
131
- for (const file of await workingFiles(vcs)) {
132
+ for (const file of files) {
132
133
  try {
133
134
  const info = await lstat(resolve(vcs.rootPath, file), { bigint: true });
134
135
  hash.update(
@@ -139,7 +140,7 @@ export async function inspectWorktree(repositoryId: string, vcs: LocalVcs) {
139
140
  }
140
141
  }
141
142
 
142
- return { revision: hash.digest("hex"), commit };
143
+ return { revision: hash.digest("hex"), commit, files };
143
144
  }
144
145
 
145
146
  export async function readWorkingFile(
@@ -141,6 +141,7 @@ export const SETTING_NAME = [
141
141
  "ctrl_tab",
142
142
  "ready_notification",
143
143
  "document_width",
144
+ "code_font_size",
144
145
  ] as const;
145
146
 
146
147
  export const REVIEW_OPENED_VIA = ["home", "cli", "other"] as const;
@@ -1,2 +0,0 @@
1
- import { n as runReviewAgentCli } from "./agent-cli-d0YJQNrR.js";
2
- export { runReviewAgentCli };
@@ -1,2 +0,0 @@
1
- import { t as openReviewProfile } from "./profile-B6jrb2QN.js";
2
- export { openReviewProfile };