glassbox 0.2.0 → 0.2.2

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.
Files changed (3) hide show
  1. package/README.md +103 -46
  2. package/dist/cli.js +44 -20
  3. package/package.json +6 -1
package/README.md CHANGED
@@ -12,15 +12,30 @@ No accounts. No pull requests. No waiting. Just you, the diff, and a tight feedb
12
12
 
13
13
  <br>
14
14
 
15
+ **Desktop app** (recommended) — download from [GitHub Releases](https://github.com/brianwestphal/glassbox/releases):
16
+
17
+ | Platform | Download |
18
+ |----------|----------|
19
+ | macOS (Apple Silicon) | `.dmg` (arm64) |
20
+ | macOS (Intel) | `.dmg` (x64) |
21
+ | Linux | `.AppImage` / `.deb` |
22
+ | Windows | `.msi` / `.exe` |
23
+
24
+ After installing, open the app and click **Install CLI** to add the `glassbox` command to your PATH.
25
+
26
+ **Or install via npm:**
27
+
15
28
  ```bash
16
29
  npm install -g glassbox
17
30
  ```
18
31
 
32
+ Then, from any git repository:
33
+
19
34
  ```bash
20
35
  glassbox
21
36
  ```
22
37
 
23
- That's it. Opens in your browser. Works in any git repo.
38
+ That's it. Data stays local. Works in any git repo.
24
39
 
25
40
  <br>
26
41
 
@@ -34,21 +49,21 @@ That's it. Opens in your browser. Works in any git repo.
34
49
 
35
50
  AI coding tools generate a lot of code fast. But "fast" doesn't mean "correct." The bottleneck isn't generation — it's **review**.
36
51
 
37
- Most developers review AI output by skimming files in their editor, mentally diffing what changed, and then either accepting it or rewriting it by hand. That's slow, error-prone, and throws away the most valuable signal: your expert judgment about *what specifically* was wrong and why.
52
+ Most developers review AI output by skimming files in their editor, mentally diffing what changed, and then either accepting it or rewriting it by hand. That's slow, error-prone, and throws away the most valuable signal: your expert judgment about _what specifically_ was wrong and why.
38
53
 
39
54
  Glassbox gives you a proper diff viewer with annotation categories designed for AI feedback:
40
55
 
41
56
  <img src="assets/demo-annotations.png" alt="Inline annotations with category badges" width="720">
42
57
 
43
- | Category | What it tells the AI |
44
- |----------|---------------------|
45
- | **Bug** | "This is broken. Fix it." |
46
- | **Fix needed** | "This needs a specific change." |
47
- | **Style** | "I prefer it done this way." |
48
- | **Pattern to follow** | "This is good. Keep doing this." |
49
- | **Pattern to avoid** | "This is an anti-pattern. Stop." |
50
- | **Note** | Context for the AI to consider. |
51
- | **Remember** | A rule to persist to the AI's long-term config. |
58
+ | Category | What it tells the AI |
59
+ | --------------------- | ----------------------------------------------- |
60
+ | **Bug** | "This is broken. Fix it." |
61
+ | **Fix needed** | "This needs a specific change." |
62
+ | **Style** | "I prefer it done this way." |
63
+ | **Pattern to follow** | "This is good. Keep doing this." |
64
+ | **Pattern to avoid** | "This is an anti-pattern. Stop." |
65
+ | **Note** | Context for the AI to consider. |
66
+ | **Remember** | A rule to persist to the AI's long-term config. |
52
67
 
53
68
  When you're done, click **Complete Review** and tell your AI tool:
54
69
 
@@ -85,7 +100,7 @@ Then you run `glassbox` again. Your previous annotations carry forward — match
85
100
  - **Automatic .gitignore prompt** — reminds you to exclude `.glassbox/` from version control
86
101
  - **Auto port selection** — if the default port is busy, it finds an open one
87
102
  - **Fully local** — no network calls (unless you opt into AI features), no accounts, no telemetry. Your code stays on your machine.
88
- - **AI-powered analysis** *(optional)* — risk scoring, narrative reading order, and guided review to help you focus and learn as you review
103
+ - **AI-powered analysis** _(optional)_ — risk scoring, narrative reading order, and guided review to help you focus and learn as you review
89
104
 
90
105
  ---
91
106
 
@@ -93,7 +108,7 @@ Then you run `glassbox` again. Your previous annotations carry forward — match
93
108
 
94
109
  > Entirely optional. Glassbox is fully functional without it.
95
110
 
96
- When reviewing a large diff, knowing *where to look first* is half the battle. Glassbox can optionally connect to an AI provider to analyze your changes and surface what matters:
111
+ When reviewing a large diff, knowing _where to look first_ is half the battle. Glassbox can optionally connect to an AI provider to analyze your changes and surface what matters:
97
112
 
98
113
  ### Risk Analysis
99
114
 
@@ -123,11 +138,11 @@ Click the shield or book icon in the sidebar to switch from the default folder v
123
138
 
124
139
  ### Supported providers
125
140
 
126
- | Provider | Models | Env variable |
127
- |----------|--------|-------------|
128
- | **Anthropic** | Claude Sonnet 4, Claude Haiku 4 | `ANTHROPIC_API_KEY` |
129
- | **OpenAI** | GPT-4o, GPT-4o Mini | `OPENAI_API_KEY` |
130
- | **Google** | Gemini 2.5 Flash, Gemini 2.5 Pro | `GEMINI_API_KEY` |
141
+ | Provider | Models | Env variable |
142
+ | ------------- | -------------------------------- | ------------------- |
143
+ | **Anthropic** | Claude Sonnet 4, Claude Haiku 4 | `ANTHROPIC_API_KEY` |
144
+ | **OpenAI** | GPT-4o, GPT-4o Mini | `OPENAI_API_KEY` |
145
+ | **Google** | Gemini 2.5 Flash, Gemini 2.5 Pro | `GEMINI_API_KEY` |
131
146
 
132
147
  You can switch providers and models in the settings dialog (gear icon in the sidebar).
133
148
 
@@ -145,6 +160,28 @@ Keys entered through the settings dialog are stored in the OS keychain by defaul
145
160
 
146
161
  ## Install
147
162
 
163
+ ### Desktop app (recommended)
164
+
165
+ Download the latest release for your platform from [GitHub Releases](https://github.com/brianwestphal/glassbox/releases).
166
+
167
+ On first launch, the app will prompt you to install the `glassbox` CLI command. This creates a symlink so you can launch the desktop app from any project directory. You can also install it manually:
168
+
169
+ **macOS:**
170
+ ```bash
171
+ sudo ln -sf "/Applications/Glassbox.app/Contents/Resources/resources/glassbox" /usr/local/bin/glassbox
172
+ ```
173
+
174
+ **Linux:**
175
+ ```bash
176
+ ln -sf /path/to/glassbox/resources/glassbox-linux ~/.local/bin/glassbox
177
+ ```
178
+
179
+ The desktop app includes automatic updates — new versions are downloaded and applied in the background.
180
+
181
+ ### npm
182
+
183
+ Alternatively, install via npm (runs in your browser instead of a native window):
184
+
148
185
  ```bash
149
186
  npm install -g glassbox
150
187
  ```
@@ -185,22 +222,37 @@ glassbox --resume
185
222
 
186
223
  ### All options
187
224
 
188
- | Flag | Description |
189
- |------|-------------|
190
- | *(no flag)* | Same as `--uncommitted` |
191
- | `--uncommitted` | Staged + unstaged + untracked changes |
192
- | `--staged` | Only staged changes |
193
- | `--unstaged` | Only unstaged changes |
194
- | `--commit <sha>` | Changes from a specific commit |
195
- | `--range <from>..<to>` | Changes between two refs |
196
- | `--branch <name>` | Current branch vs the named branch |
197
- | `--files <patterns>` | Specific files (comma-separated globs) |
198
- | `--all` | Entire codebase (all tracked files) |
199
- | `--port <number>` | Port to run on (default: 4173) |
200
- | `--resume` | Resume the latest in-progress review for this mode |
201
- | `--check-for-updates` | Check for a newer version on npm |
202
- | `--debug` | Show build timestamp and debug info |
203
- | `--help` | Show help |
225
+ | Flag | Description |
226
+ | ---------------------- | -------------------------------------------------- |
227
+ | _(no flag)_ | Same as `--uncommitted` |
228
+ | `--uncommitted` | Staged + unstaged + untracked changes |
229
+ | `--staged` | Only staged changes |
230
+ | `--unstaged` | Only unstaged changes |
231
+ | `--commit <sha>` | Changes from a specific commit |
232
+ | `--range <from>..<to>` | Changes between two refs |
233
+ | `--branch <name>` | Current branch vs the named branch |
234
+ | `--files <patterns>` | Specific files (comma-separated globs) |
235
+ | `--all` | Entire codebase (all tracked files) |
236
+ | `--port <number>` | Port to run on (default: 4183) |
237
+ | `--resume` | Resume the latest in-progress review for this mode |
238
+ | `--browser` | Open in browser instead of desktop window |
239
+ | `--check-for-updates` | Check for a newer version on npm |
240
+ | `--debug` | Show build timestamp and debug info |
241
+ | `--help` | Show help |
242
+
243
+ ### Settings file
244
+
245
+ Create `.glassbox/settings.json` in your project directory to configure per-project options:
246
+
247
+ ```json
248
+ {
249
+ "appName": "Glassbox — My Project"
250
+ }
251
+ ```
252
+
253
+ | Key | Description |
254
+ |-----|-------------|
255
+ | `appName` | Custom window title and Dock name (defaults to "Glassbox — _folder name_") |
204
256
 
205
257
  ---
206
258
 
@@ -231,17 +283,20 @@ Point the tool at the file. The export includes an "Instructions for AI Tools" s
231
283
 
232
284
  ## Architecture
233
285
 
234
- | Layer | Technology |
235
- |-------|-----------|
236
- | CLI | TypeScript, Node.js |
237
- | Server | Hono |
238
- | Database | PGLite (embedded PostgreSQL) |
239
- | UI | Custom server-side JSX (no React), vanilla client JS |
240
- | Build | tsup (single-file bundle) |
241
- | Storage | `~/.glassbox/data/` |
286
+ | Layer | Technology |
287
+ | -------- | ---------------------------------------------------- |
288
+ | Desktop | Tauri v2 (native window, auto-updates) |
289
+ | CLI | TypeScript, Node.js |
290
+ | Server | Hono |
291
+ | Database | PGLite (embedded PostgreSQL) |
292
+ | UI | Custom server-side JSX (no React), vanilla client JS |
293
+ | Build | tsup (single-file bundle) |
294
+ | Storage | `~/.glassbox/data/` |
242
295
 
243
296
  Data stays local. The only network calls are an optional once-per-day npm update check and AI analysis requests if you opt in.
244
297
 
298
+ > **Note:** We're actively developing and testing on macOS. Linux and Windows builds are provided but less tested — if you run into issues on those platforms, please [open an issue](https://github.com/brianwestphal/glassbox/issues).
299
+
245
300
  ## Development
246
301
 
247
302
  ```bash
@@ -249,10 +304,12 @@ git clone <repo-url>
249
304
  cd glassbox
250
305
  npm install
251
306
 
252
- npm run dev -- --uncommitted # Run with tsx (no build step)
253
- npm run build # Build to dist/cli.js
254
- npm run clean # Remove dist and caches
255
- npm link # Symlink for global 'glassbox' command
307
+ npm run dev # Build client assets, then run via tsx
308
+ npm run build # Build to dist/cli.js
309
+ npm run tauri:dev # Run desktop app in dev mode
310
+ npm run tauri:build # Build desktop app for distribution
311
+ npm run clean # Remove dist and caches
312
+ npm link # Symlink for global 'glassbox' command
256
313
  ```
257
314
 
258
315
  ## License
package/dist/cli.js CHANGED
@@ -4123,7 +4123,7 @@ function tryServe(fetch2, port) {
4123
4123
  });
4124
4124
  });
4125
4125
  }
4126
- async function startServer(port, reviewId, repoRoot) {
4126
+ async function startServer(port, reviewId, repoRoot, options) {
4127
4127
  const app = new Hono4();
4128
4128
  app.use("*", async (c, next) => {
4129
4129
  c.set("reviewId", reviewId);
@@ -4145,15 +4145,19 @@ async function startServer(port, reviewId, repoRoot) {
4145
4145
  app.route("/api/ai", aiApiRoutes);
4146
4146
  app.route("/", pageRoutes);
4147
4147
  let actualPort = port;
4148
- for (let attempt = 0; attempt < 20; attempt++) {
4149
- try {
4150
- actualPort = await tryServe(app.fetch, port + attempt);
4151
- break;
4152
- } catch (err) {
4153
- if (err instanceof Error && err.code === "EADDRINUSE" && attempt < 19) {
4154
- continue;
4148
+ if (options?.strictPort) {
4149
+ actualPort = await tryServe(app.fetch, port);
4150
+ } else {
4151
+ for (let attempt = 0; attempt < 20; attempt++) {
4152
+ try {
4153
+ actualPort = await tryServe(app.fetch, port + attempt);
4154
+ break;
4155
+ } catch (err) {
4156
+ if (err instanceof Error && err.code === "EADDRINUSE" && attempt < 19) {
4157
+ continue;
4158
+ }
4159
+ throw err;
4155
4160
  }
4156
- throw err;
4157
4161
  }
4158
4162
  }
4159
4163
  if (actualPort !== port) {
@@ -4163,8 +4167,10 @@ async function startServer(port, reviewId, repoRoot) {
4163
4167
  console.log(`
4164
4168
  Glassbox running at ${url}
4165
4169
  `);
4166
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4167
- exec(`${openCmd} ${url}`);
4170
+ if (!options?.noOpen) {
4171
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4172
+ exec(`${openCmd} ${url}`);
4173
+ }
4168
4174
  }
4169
4175
 
4170
4176
  // src/update-check.ts
@@ -4297,8 +4303,11 @@ Modes (pick one):
4297
4303
  --all Review entire codebase
4298
4304
 
4299
4305
  Options:
4300
- --port <number> Port to run on (default: 4173)
4306
+ --port <number> Port to run on (default: 4183)
4301
4307
  --resume Resume the latest in-progress review for this mode
4308
+ --no-open Don't open browser automatically
4309
+ --strict-port Fail if the requested port is in use
4310
+ --project-dir <dir> Run as if invoked from <dir> (used by Tauri desktop app)
4302
4311
  --check-for-updates Check for a newer version on npm
4303
4312
  --ai-service-test Use mock AI responses (no API calls, no tokens used)
4304
4313
  --help Show this help message
@@ -4314,12 +4323,15 @@ Examples:
4314
4323
  function parseArgs(argv) {
4315
4324
  const args = argv.slice(2);
4316
4325
  let mode = null;
4317
- let port = 4173;
4326
+ let port = 4183;
4318
4327
  let resume = false;
4319
4328
  let forceUpdateCheck = false;
4320
4329
  let debug = false;
4321
4330
  let aiServiceTest = false;
4322
4331
  let demo = null;
4332
+ let noOpen = false;
4333
+ let strictPort = false;
4334
+ let projectDir = null;
4323
4335
  for (let i = 0; i < args.length; i++) {
4324
4336
  const arg = args[i];
4325
4337
  switch (arg) {
@@ -4369,6 +4381,15 @@ function parseArgs(argv) {
4369
4381
  case "--ai-service-test":
4370
4382
  aiServiceTest = true;
4371
4383
  break;
4384
+ case "--no-open":
4385
+ noOpen = true;
4386
+ break;
4387
+ case "--strict-port":
4388
+ strictPort = true;
4389
+ break;
4390
+ case "--project-dir":
4391
+ projectDir = args[++i];
4392
+ break;
4372
4393
  default:
4373
4394
  if (arg.startsWith("--demo:")) {
4374
4395
  demo = parseInt(arg.slice(7), 10);
@@ -4386,7 +4407,7 @@ function parseArgs(argv) {
4386
4407
  if (!mode) {
4387
4408
  mode = { type: "uncommitted" };
4388
4409
  }
4389
- return { mode, port, resume, forceUpdateCheck, debug, aiServiceTest, demo };
4410
+ return { mode, port, resume, forceUpdateCheck, debug, aiServiceTest, demo, noOpen, strictPort, projectDir };
4390
4411
  }
4391
4412
  async function main() {
4392
4413
  const parsed = parseArgs(process.argv);
@@ -4394,14 +4415,17 @@ async function main() {
4394
4415
  printUsage();
4395
4416
  process.exit(1);
4396
4417
  }
4397
- const { mode, port, resume, forceUpdateCheck, debug, aiServiceTest, demo } = parsed;
4418
+ const { mode, port, resume, forceUpdateCheck, debug, aiServiceTest, demo, noOpen, strictPort, projectDir } = parsed;
4398
4419
  setDebug(debug);
4399
4420
  setAIServiceTest(aiServiceTest);
4400
4421
  if (aiServiceTest) {
4401
4422
  console.log("AI service test mode enabled \u2014 using mock AI responses");
4402
4423
  }
4403
4424
  if (debug) {
4404
- console.log(`[debug] Build timestamp: ${"2026-03-10T06:39:37.519Z"}`);
4425
+ console.log(`[debug] Build timestamp: ${"2026-03-13T08:17:01.192Z"}`);
4426
+ }
4427
+ if (projectDir) {
4428
+ process.chdir(projectDir);
4405
4429
  }
4406
4430
  if (demo !== null) {
4407
4431
  const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
@@ -4418,7 +4442,7 @@ async function main() {
4418
4442
  DEMO MODE: ${scenario.label}
4419
4443
  `);
4420
4444
  const { reviewId } = await setupDemoReview(demo);
4421
- await startServer(port, reviewId, process.cwd());
4445
+ await startServer(port, reviewId, process.cwd(), { noOpen, strictPort });
4422
4446
  return;
4423
4447
  }
4424
4448
  await checkForUpdates(forceUpdateCheck);
@@ -4439,12 +4463,12 @@ async function main() {
4439
4463
  const diffs2 = getFileDiffs(mode, cwd);
4440
4464
  const result = await updateReviewDiffs(existing.id, diffs2, headCommit);
4441
4465
  console.log(`Updated ${result.updated} file(s), ${result.added} added, ${result.stale} stale annotation(s)`);
4442
- await startServer(port, existing.id, repoRoot);
4466
+ await startServer(port, existing.id, repoRoot, { noOpen, strictPort });
4443
4467
  return;
4444
4468
  }
4445
4469
  if (resume) {
4446
4470
  console.log(`Resuming review ${existing.id} (started ${existing.created_at})`);
4447
- await startServer(port, existing.id, repoRoot);
4471
+ await startServer(port, existing.id, repoRoot, { noOpen, strictPort });
4448
4472
  return;
4449
4473
  }
4450
4474
  } else if (resume) {
@@ -4462,7 +4486,7 @@ async function main() {
4462
4486
  await addReviewFile(review.id, diff.filePath, JSON.stringify(diff));
4463
4487
  }
4464
4488
  console.log(`Review ${review.id} created.`);
4465
- await startServer(port, review.id, repoRoot);
4489
+ await startServer(port, review.id, repoRoot, { noOpen, strictPort });
4466
4490
  }
4467
4491
  main().catch((err) => {
4468
4492
  console.error(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glassbox",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",
@@ -31,6 +31,10 @@
31
31
  "dev": "npm run build:client && tsx --tsconfig tsconfig.json src/cli.ts",
32
32
  "build": "tsup",
33
33
  "build:client": "mkdir -p dist/client && npx esbuild src/client/app.ts --bundle --format=iife --outfile=dist/client/app.global.js --target=es2020 --jsx=automatic --jsx-import-source=#jsx --alias:#jsx/jsx-runtime=./src/jsx-runtime.ts && npx sass src/client/styles.scss dist/client/styles.css --style compressed --no-source-map",
34
+ "dev:server": "npm run build:client && tsx --tsconfig tsconfig.json src/cli.ts --no-open --strict-port",
35
+ "tauri": "tauri",
36
+ "tauri:dev": "bash scripts/ensure-sidecar-stub.sh && tauri dev",
37
+ "tauri:build": "bash scripts/build-sidecar.sh && tauri build",
34
38
  "lint": "eslint src/",
35
39
  "clean": "rm -rf dist node_modules/.cache",
36
40
  "release": "bash scripts/release.sh",
@@ -47,6 +51,7 @@
47
51
  },
48
52
  "devDependencies": {
49
53
  "@eslint/js": "^9.39.3",
54
+ "@tauri-apps/cli": "^2.10.1",
50
55
  "@types/node": "^22.0.0",
51
56
  "esbuild": "^0.27.3",
52
57
  "eslint": "^9.39.3",