glassbox 0.12.0-beta.2 → 0.12.0-beta.3

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/README.md CHANGED
@@ -299,7 +299,7 @@ git difftool HEAD~1 HEAD # step through changed files one at a time
299
299
 
300
300
  When you're finished, click **Done** in the review (a difftool session shows this instead of the normal completion flow) — the `git difftool` command returns to your prompt cleanly, no Ctrl-C needed. Closing the browser tab ends the session too.
301
301
 
302
- **Desktop vs browser:** in a browser (npm) install the accumulating model above applies. If the desktop app is installed, `--dir-diff` opens the whole change set in one Glassbox window (matching what `glassbox --commit` does from the same folder) on macOS, Linux, and Windows; desktop per-file mode currently opens one window per file, so `--dir-diff` is the recommended desktop invocation.
302
+ **Desktop vs browser:** the accumulating model is the same either way — only the window differs. In a browser (npm) install the review opens in a tab; if the desktop app is installed, it opens in one Glassbox window (matching what `glassbox --commit` does from the same folder) on macOS, Linux, and Windows, and per-file mode accumulates into that single window just like the browser. Closing the window ends the session.
303
303
 
304
304
  **Under the hood:** `git difftool --dir-diff` materializes its two snapshot directories asymmetrically — the right side is symlinks pointing into your working tree, which trips up plain `git diff --no-index`. `glassbox-difftool` dereferences those symlinks into a temp tree before handing the dirs to `glassbox --diff`, then cleans up when the session ends. In per-file mode the wrapper reads each file's content before it returns (git deletes the temp files the instant the tool exits) and POSTs it to the accumulating server; the last file holds the connection open so `git difftool` stays attached until you click Done. Without the wrapper you'd see every modified file show up as a "deleted + added" pair instead of a modified entry, and per-file mode would hang waiting for a manual Ctrl-C between files.
305
305
 
@@ -14590,6 +14590,7 @@ function tryAcquireStartingLock(home = difftoolHome()) {
14590
14590
 
14591
14591
  // src/git/difftool-client.ts
14592
14592
  var DISCOVER_TIMEOUT_MS = 15 * 1e3;
14593
+ var DESKTOP_DISCOVER_TIMEOUT_MS = 30 * 1e3;
14593
14594
  var PROBE_INTERVAL_MS = 150;
14594
14595
  function delay(ms) {
14595
14596
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -14609,7 +14610,7 @@ async function ping(port) {
14609
14610
  return false;
14610
14611
  }
14611
14612
  }
14612
- function spawnDetachedServer(cliPath, cwd) {
14613
+ function spawnDetachedBrowserServer(cliPath, cwd) {
14613
14614
  const child = spawn(process.execPath, [cliPath, "--difftool-serve"], {
14614
14615
  cwd,
14615
14616
  detached: true,
@@ -14617,8 +14618,22 @@ function spawnDetachedServer(cliPath, cwd) {
14617
14618
  });
14618
14619
  child.unref();
14619
14620
  }
14620
- async function discoverOrStartServer(cliPath, cwd) {
14621
- const deadline = Date.now() + DISCOVER_TIMEOUT_MS;
14621
+ function launchDetachedDesktopSession(launcher, cwd) {
14622
+ if (process.platform === "win32") {
14623
+ const child = spawn(`"${launcher}" --difftool-serve`, {
14624
+ cwd,
14625
+ detached: true,
14626
+ stdio: "ignore",
14627
+ shell: true
14628
+ });
14629
+ child.unref();
14630
+ } else {
14631
+ const child = spawn(launcher, ["--difftool-serve"], { cwd, detached: true, stdio: "ignore" });
14632
+ child.unref();
14633
+ }
14634
+ }
14635
+ async function discoverOrStartServer(startServer, timeoutMs = DISCOVER_TIMEOUT_MS) {
14636
+ const deadline = Date.now() + timeoutMs;
14622
14637
  let startedByUs = false;
14623
14638
  while (Date.now() < deadline) {
14624
14639
  const discovery = readDiscovery();
@@ -14627,7 +14642,7 @@ async function discoverOrStartServer(cliPath, cwd) {
14627
14642
  }
14628
14643
  if (!startedByUs && tryAcquireStartingLock()) {
14629
14644
  startedByUs = true;
14630
- spawnDetachedServer(cliPath, cwd);
14645
+ startServer();
14631
14646
  }
14632
14647
  await delay(PROBE_INTERVAL_MS);
14633
14648
  }
@@ -14752,11 +14767,11 @@ function readFileOrEmpty(p) {
14752
14767
  return Buffer.alloc(0);
14753
14768
  }
14754
14769
  }
14755
- async function runThinClient(cliPath) {
14770
+ async function runThinClient(start, timeoutMs) {
14756
14771
  const oldContent = readFileOrEmpty(local);
14757
14772
  const newContent = readFileOrEmpty(remote);
14758
14773
  const displayPath = merged || basename(remote) || basename(local) || "file";
14759
- const port = await discoverOrStartServer(cliPath, process.cwd());
14774
+ const port = await discoverOrStartServer(start, timeoutMs);
14760
14775
  await appendFile(port, displayPath, oldContent, newContent);
14761
14776
  if (shouldHoldForSession(process.env)) {
14762
14777
  await holdUntilEnd(port);
@@ -14799,16 +14814,27 @@ function runBlockingLaunch() {
14799
14814
  cleanup();
14800
14815
  }
14801
14816
  }
14817
+ var onThinClientDone = () => process.exit(0);
14818
+ var onThinClientError = (err) => {
14819
+ console.error(err instanceof Error ? err.message : String(err));
14820
+ return process.exit(1);
14821
+ };
14802
14822
  if (localIsFile && target.kind === "browser") {
14803
- runThinClient(target.cli).then(
14823
+ const cliPath = target.cli;
14824
+ void runThinClient(() => {
14825
+ spawnDetachedBrowserServer(cliPath, process.cwd());
14826
+ }).then(
14827
+ onThinClientDone,
14828
+ onThinClientError
14829
+ );
14830
+ } else if (localIsFile && target.kind === "desktop") {
14831
+ const launcher = target.launcher;
14832
+ void runThinClient(
14804
14833
  () => {
14805
- process.exit(0);
14834
+ launchDetachedDesktopSession(launcher, process.cwd());
14806
14835
  },
14807
- (err) => {
14808
- console.error(err instanceof Error ? err.message : String(err));
14809
- process.exit(1);
14810
- }
14811
- );
14836
+ DESKTOP_DISCOVER_TIMEOUT_MS
14837
+ ).then(onThinClientDone, onThinClientError);
14812
14838
  } else {
14813
14839
  runBlockingLaunch();
14814
14840
  }
package/dist/cli.js CHANGED
@@ -17019,8 +17019,15 @@ import { basename, dirname, join as join3, resolve } from "path";
17019
17019
 
17020
17020
  // src/git/repo.ts
17021
17021
  import { spawnSync as spawnSync3 } from "child_process";
17022
+ function scrubbedGitEnv() {
17023
+ const env = { ...process.env };
17024
+ delete env.GIT_EXTERNAL_DIFF;
17025
+ delete env.GIT_DIFF_PATH_COUNTER;
17026
+ delete env.GIT_DIFF_PATH_TOTAL;
17027
+ return env;
17028
+ }
17022
17029
  function git(args, cwd) {
17023
- const result = spawnSync3("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
17030
+ const result = spawnSync3("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024, env: scrubbedGitEnv() });
17024
17031
  if (result.status === 0) return result.stdout;
17025
17032
  if (result.stdout !== "") return result.stdout;
17026
17033
  const err = new Error(result.stderr);
@@ -17045,7 +17052,7 @@ function isGitRepo(cwd) {
17045
17052
  }
17046
17053
  }
17047
17054
  function getHeadCommit(cwd) {
17048
- return spawnSync3("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8" }).stdout.trim();
17055
+ return spawnSync3("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", env: scrubbedGitEnv() }).stdout.trim();
17049
17056
  }
17050
17057
 
17051
17058
  // src/git/types.ts
@@ -17096,7 +17103,7 @@ function parseDiffData(raw) {
17096
17103
  // src/git/diff.ts
17097
17104
  var toGitArg = (p) => process.platform === "win32" ? p.replace(/\\/g, "/") : p;
17098
17105
  function git2(args, cwd) {
17099
- const result = spawnSync4("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
17106
+ const result = spawnSync4("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024, env: scrubbedGitEnv() });
17100
17107
  if (result.status === 0) return result.stdout;
17101
17108
  if (result.stdout !== "") return result.stdout;
17102
17109
  const err = new Error(result.stderr);
@@ -21050,7 +21057,7 @@ function getNewRef(mode) {
21050
21057
  }
21051
21058
  function gitShowFile(ref, filePath, repoRoot) {
21052
21059
  const spec = ref === ":" ? `:${filePath}` : `${ref}:${filePath}`;
21053
- const result = spawnSync6("git", ["show", spec], { cwd: repoRoot, maxBuffer: 50 * 1024 * 1024 });
21060
+ const result = spawnSync6("git", ["show", spec], { cwd: repoRoot, maxBuffer: 50 * 1024 * 1024, env: scrubbedGitEnv() });
21054
21061
  if (result.status !== 0 || result.stdout.length === 0) return null;
21055
21062
  return result.stdout;
21056
21063
  }
@@ -23810,7 +23817,7 @@ async function main() {
23810
23817
  console.log("AI service test mode enabled \u2014 using mock AI responses");
23811
23818
  }
23812
23819
  if (debug) {
23813
- console.log(`[debug] Build timestamp: ${"2026-06-04T00:06:05.686Z"}`);
23820
+ console.log(`[debug] Build timestamp: ${"2026-06-04T01:28:18.249Z"}`);
23814
23821
  }
23815
23822
  if (projectDir !== null) {
23816
23823
  process.chdir(projectDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glassbox",
3
- "version": "0.12.0-beta.2",
3
+ "version": "0.12.0-beta.3",
4
4
  "description": "A local code review tool for AI-generated code. Review diffs, annotate lines, and export structured feedback that AI tools can act on.",
5
5
  "type": "module",
6
6
  "license": "MIT",