glassbox 0.7.9 → 0.8.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.
- package/dist/channel.js +113 -0
- package/dist/cli.js +258 -89
- package/dist/client/app.global.js +9 -9
- package/dist/client/styles.css +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -393,9 +393,9 @@ var init_queries = __esm({
|
|
|
393
393
|
// src/cli.ts
|
|
394
394
|
init_connection();
|
|
395
395
|
init_queries();
|
|
396
|
-
import { mkdirSync as
|
|
396
|
+
import { mkdirSync as mkdirSync9, realpathSync } from "fs";
|
|
397
397
|
import { tmpdir } from "os";
|
|
398
|
-
import { join as
|
|
398
|
+
import { join as join13, resolve as resolve6 } from "path";
|
|
399
399
|
|
|
400
400
|
// src/debug.ts
|
|
401
401
|
var debugEnabled = false;
|
|
@@ -1725,10 +1725,10 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1725
1725
|
// src/server.ts
|
|
1726
1726
|
import { serve } from "@hono/node-server";
|
|
1727
1727
|
import { exec } from "child_process";
|
|
1728
|
-
import { existsSync as
|
|
1729
|
-
import { Hono as
|
|
1730
|
-
import { dirname, join as
|
|
1731
|
-
import { fileURLToPath } from "url";
|
|
1728
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
|
|
1729
|
+
import { Hono as Hono8 } from "hono";
|
|
1730
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
1731
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1732
1732
|
|
|
1733
1733
|
// src/routes/ai-api.ts
|
|
1734
1734
|
import { Hono as Hono3 } from "hono";
|
|
@@ -2450,8 +2450,8 @@ function isRetriable(err) {
|
|
|
2450
2450
|
return msg.includes("429") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("rate_limit");
|
|
2451
2451
|
}
|
|
2452
2452
|
function sleep(ms) {
|
|
2453
|
-
return new Promise((
|
|
2454
|
-
setTimeout(
|
|
2453
|
+
return new Promise((resolve7) => {
|
|
2454
|
+
setTimeout(resolve7, ms);
|
|
2455
2455
|
});
|
|
2456
2456
|
}
|
|
2457
2457
|
|
|
@@ -2495,8 +2495,8 @@ function randomLines(count) {
|
|
|
2495
2495
|
return lines.sort((a, b) => a.line - b.line);
|
|
2496
2496
|
}
|
|
2497
2497
|
function sleep2(ms) {
|
|
2498
|
-
return new Promise((
|
|
2499
|
-
setTimeout(
|
|
2498
|
+
return new Promise((resolve7) => {
|
|
2499
|
+
setTimeout(resolve7, ms);
|
|
2500
2500
|
});
|
|
2501
2501
|
}
|
|
2502
2502
|
async function mockRiskAnalysisBatch(files) {
|
|
@@ -4370,10 +4370,178 @@ apiRoutes.post("/share-prompt/tick", async (c) => {
|
|
|
4370
4370
|
return c.json({ totalOpenMs: sp.totalOpenMs });
|
|
4371
4371
|
});
|
|
4372
4372
|
|
|
4373
|
-
// src/routes/
|
|
4374
|
-
import {
|
|
4373
|
+
// src/routes/channel-api.ts
|
|
4374
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
4375
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
4375
4376
|
import { Hono as Hono5 } from "hono";
|
|
4376
|
-
import {
|
|
4377
|
+
import { homedir as homedir4 } from "os";
|
|
4378
|
+
import { join as join8 } from "path";
|
|
4379
|
+
|
|
4380
|
+
// src/channel-config.ts
|
|
4381
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4382
|
+
import { dirname, join as join7, resolve as resolve4 } from "path";
|
|
4383
|
+
import { fileURLToPath } from "url";
|
|
4384
|
+
var MCP_SERVER_KEY = "glassbox-channel";
|
|
4385
|
+
function getChannelServerPath() {
|
|
4386
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
4387
|
+
const distPath = resolve4(thisDir, "channel.js");
|
|
4388
|
+
if (existsSync6(distPath)) {
|
|
4389
|
+
return { command: process.execPath, args: [distPath] };
|
|
4390
|
+
}
|
|
4391
|
+
const srcPath = resolve4(thisDir, "channel.ts");
|
|
4392
|
+
if (existsSync6(srcPath)) {
|
|
4393
|
+
return { command: "npx", args: ["tsx", srcPath] };
|
|
4394
|
+
}
|
|
4395
|
+
return { command: process.execPath, args: [distPath] };
|
|
4396
|
+
}
|
|
4397
|
+
function projectRoot(dataDir) {
|
|
4398
|
+
return dataDir.replace(/\/.glassbox\/?$/, "");
|
|
4399
|
+
}
|
|
4400
|
+
function registerChannel(dataDir) {
|
|
4401
|
+
const root = projectRoot(dataDir);
|
|
4402
|
+
const mcpPath = join7(root, ".mcp.json");
|
|
4403
|
+
const { command, args } = getChannelServerPath();
|
|
4404
|
+
let config = {};
|
|
4405
|
+
if (existsSync6(mcpPath)) {
|
|
4406
|
+
try {
|
|
4407
|
+
config = JSON.parse(readFileSync8(mcpPath, "utf-8"));
|
|
4408
|
+
} catch {
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
if (config.mcpServers === void 0) config.mcpServers = {};
|
|
4412
|
+
config.mcpServers[MCP_SERVER_KEY] = {
|
|
4413
|
+
command,
|
|
4414
|
+
args: [...args, "--data-dir", dataDir]
|
|
4415
|
+
};
|
|
4416
|
+
writeFileSync5(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
4417
|
+
}
|
|
4418
|
+
function unregisterChannel(dataDir) {
|
|
4419
|
+
const root = projectRoot(dataDir);
|
|
4420
|
+
const mcpPath = join7(root, ".mcp.json");
|
|
4421
|
+
if (!existsSync6(mcpPath)) return;
|
|
4422
|
+
try {
|
|
4423
|
+
const config = JSON.parse(readFileSync8(mcpPath, "utf-8"));
|
|
4424
|
+
if (config.mcpServers?.[MCP_SERVER_KEY] !== void 0) {
|
|
4425
|
+
const servers = { ...config.mcpServers };
|
|
4426
|
+
delete servers[MCP_SERVER_KEY];
|
|
4427
|
+
config.mcpServers = servers;
|
|
4428
|
+
writeFileSync5(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
4429
|
+
}
|
|
4430
|
+
} catch {
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
function getChannelPort(dataDir) {
|
|
4434
|
+
try {
|
|
4435
|
+
const portStr = readFileSync8(join7(dataDir, "channel-port"), "utf-8").trim();
|
|
4436
|
+
const port = parseInt(portStr, 10);
|
|
4437
|
+
return isNaN(port) ? null : port;
|
|
4438
|
+
} catch {
|
|
4439
|
+
return null;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
async function isChannelAlive(dataDir) {
|
|
4443
|
+
const port = getChannelPort(dataDir);
|
|
4444
|
+
if (port === null) return false;
|
|
4445
|
+
try {
|
|
4446
|
+
const res = await fetch(`http://127.0.0.1:${port}/health`);
|
|
4447
|
+
const data = await res.json();
|
|
4448
|
+
return data.ok;
|
|
4449
|
+
} catch {
|
|
4450
|
+
return false;
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
async function triggerChannel(dataDir, message) {
|
|
4454
|
+
const port = getChannelPort(dataDir);
|
|
4455
|
+
if (port === null) return false;
|
|
4456
|
+
try {
|
|
4457
|
+
const res = await fetch(`http://127.0.0.1:${port}/trigger`, {
|
|
4458
|
+
method: "POST",
|
|
4459
|
+
body: message
|
|
4460
|
+
});
|
|
4461
|
+
return res.ok;
|
|
4462
|
+
} catch {
|
|
4463
|
+
return false;
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
|
|
4467
|
+
// src/routes/channel-api.ts
|
|
4468
|
+
var channelApiRoutes = new Hono5();
|
|
4469
|
+
var CONFIG_DIR2 = join8(homedir4(), ".glassbox");
|
|
4470
|
+
var CONFIG_PATH2 = join8(CONFIG_DIR2, "config.json");
|
|
4471
|
+
function readGlobalConfig2() {
|
|
4472
|
+
try {
|
|
4473
|
+
if (existsSync7(CONFIG_PATH2)) {
|
|
4474
|
+
return JSON.parse(readFileSync9(CONFIG_PATH2, "utf-8"));
|
|
4475
|
+
}
|
|
4476
|
+
} catch {
|
|
4477
|
+
}
|
|
4478
|
+
return {};
|
|
4479
|
+
}
|
|
4480
|
+
function writeGlobalConfig2(config) {
|
|
4481
|
+
mkdirSync5(CONFIG_DIR2, { recursive: true });
|
|
4482
|
+
writeFileSync6(CONFIG_PATH2, JSON.stringify(config, null, 2), "utf-8");
|
|
4483
|
+
}
|
|
4484
|
+
channelApiRoutes.get("/status", async (c) => {
|
|
4485
|
+
const config = readGlobalConfig2();
|
|
4486
|
+
const enabled = config.channelEnabled === true;
|
|
4487
|
+
const repoRoot = c.get("repoRoot");
|
|
4488
|
+
const dataDir = join8(repoRoot, ".glassbox");
|
|
4489
|
+
const connected = enabled ? await isChannelAlive(dataDir) : false;
|
|
4490
|
+
return c.json({ enabled, connected });
|
|
4491
|
+
});
|
|
4492
|
+
channelApiRoutes.post("/enable", (c) => {
|
|
4493
|
+
const config = readGlobalConfig2();
|
|
4494
|
+
config.channelEnabled = true;
|
|
4495
|
+
writeGlobalConfig2(config);
|
|
4496
|
+
const repoRoot = c.get("repoRoot");
|
|
4497
|
+
const dataDir = join8(repoRoot, ".glassbox");
|
|
4498
|
+
mkdirSync5(dataDir, { recursive: true });
|
|
4499
|
+
registerChannel(dataDir);
|
|
4500
|
+
return c.json({ ok: true });
|
|
4501
|
+
});
|
|
4502
|
+
channelApiRoutes.post("/disable", (c) => {
|
|
4503
|
+
const config = readGlobalConfig2();
|
|
4504
|
+
config.channelEnabled = false;
|
|
4505
|
+
writeGlobalConfig2(config);
|
|
4506
|
+
const repoRoot = c.get("repoRoot");
|
|
4507
|
+
const dataDir = join8(repoRoot, ".glassbox");
|
|
4508
|
+
unregisterChannel(dataDir);
|
|
4509
|
+
return c.json({ ok: true });
|
|
4510
|
+
});
|
|
4511
|
+
channelApiRoutes.post("/trigger", async (c) => {
|
|
4512
|
+
const body = await c.req.json();
|
|
4513
|
+
const repoRoot = c.get("repoRoot");
|
|
4514
|
+
const dataDir = join8(repoRoot, ".glassbox");
|
|
4515
|
+
const sent = await triggerChannel(dataDir, body.message);
|
|
4516
|
+
if (!sent) {
|
|
4517
|
+
return c.json({ error: "Channel not connected" }, 503);
|
|
4518
|
+
}
|
|
4519
|
+
return c.json({ ok: true });
|
|
4520
|
+
});
|
|
4521
|
+
channelApiRoutes.get("/claude-check", (c) => {
|
|
4522
|
+
try {
|
|
4523
|
+
const result = spawnSync8("claude", ["--version"], { encoding: "utf-8", timeout: 5e3 });
|
|
4524
|
+
if (result.status !== 0) {
|
|
4525
|
+
return c.json({ installed: false, version: null, meetsMinimum: false });
|
|
4526
|
+
}
|
|
4527
|
+
const version = result.stdout.trim();
|
|
4528
|
+
const match = version.match(/(\d+\.\d+\.\d+)/);
|
|
4529
|
+
const ver = match !== null ? match[1] : null;
|
|
4530
|
+
let meetsMinimum = false;
|
|
4531
|
+
if (ver !== null) {
|
|
4532
|
+
const parts = ver.split(".").map(Number);
|
|
4533
|
+
meetsMinimum = parts[0] > 2 || parts[0] === 2 && parts[1] > 1 || parts[0] === 2 && parts[1] === 1 && parts[2] >= 80;
|
|
4534
|
+
}
|
|
4535
|
+
return c.json({ installed: true, version: ver, meetsMinimum });
|
|
4536
|
+
} catch {
|
|
4537
|
+
return c.json({ installed: false, version: null, meetsMinimum: false });
|
|
4538
|
+
}
|
|
4539
|
+
});
|
|
4540
|
+
|
|
4541
|
+
// src/routes/pages.tsx
|
|
4542
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
4543
|
+
import { Hono as Hono6 } from "hono";
|
|
4544
|
+
import { resolve as resolve5 } from "path";
|
|
4377
4545
|
|
|
4378
4546
|
// src/utils/escapeHtml.ts
|
|
4379
4547
|
function escapeHtml(str) {
|
|
@@ -5619,24 +5787,24 @@ function themeToInlineStyle(colors) {
|
|
|
5619
5787
|
}
|
|
5620
5788
|
|
|
5621
5789
|
// src/themes/config.ts
|
|
5622
|
-
import { existsSync as
|
|
5623
|
-
import { homedir as
|
|
5624
|
-
import { join as
|
|
5625
|
-
var
|
|
5626
|
-
var
|
|
5627
|
-
var THEMES_DIR =
|
|
5790
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readdirSync, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
5791
|
+
import { homedir as homedir5 } from "os";
|
|
5792
|
+
import { join as join9 } from "path";
|
|
5793
|
+
var CONFIG_DIR3 = join9(homedir5(), ".glassbox");
|
|
5794
|
+
var CONFIG_PATH3 = join9(CONFIG_DIR3, "config.json");
|
|
5795
|
+
var THEMES_DIR = join9(CONFIG_DIR3, "themes");
|
|
5628
5796
|
function readConfigFile2() {
|
|
5629
5797
|
try {
|
|
5630
|
-
if (
|
|
5631
|
-
return JSON.parse(
|
|
5798
|
+
if (existsSync8(CONFIG_PATH3)) {
|
|
5799
|
+
return JSON.parse(readFileSync10(CONFIG_PATH3, "utf-8"));
|
|
5632
5800
|
}
|
|
5633
5801
|
} catch {
|
|
5634
5802
|
}
|
|
5635
5803
|
return {};
|
|
5636
5804
|
}
|
|
5637
5805
|
function writeConfigFile2(config) {
|
|
5638
|
-
|
|
5639
|
-
|
|
5806
|
+
mkdirSync6(CONFIG_DIR3, { recursive: true });
|
|
5807
|
+
writeFileSync7(CONFIG_PATH3, JSON.stringify(config, null, 2), "utf-8");
|
|
5640
5808
|
}
|
|
5641
5809
|
function getActiveThemeId() {
|
|
5642
5810
|
const config = readConfigFile2();
|
|
@@ -5651,13 +5819,13 @@ function setActiveThemeId(id) {
|
|
|
5651
5819
|
writeConfigFile2(config);
|
|
5652
5820
|
}
|
|
5653
5821
|
function loadCustomThemes() {
|
|
5654
|
-
if (!
|
|
5822
|
+
if (!existsSync8(THEMES_DIR)) return [];
|
|
5655
5823
|
const themes = [];
|
|
5656
5824
|
try {
|
|
5657
5825
|
const files = readdirSync(THEMES_DIR).filter((f) => f.endsWith(".json"));
|
|
5658
5826
|
for (const file of files) {
|
|
5659
5827
|
try {
|
|
5660
|
-
const data = JSON.parse(
|
|
5828
|
+
const data = JSON.parse(readFileSync10(join9(THEMES_DIR, file), "utf-8"));
|
|
5661
5829
|
if (data.id !== void 0 && data.id !== "" && data.name !== void 0 && data.name !== "" && data.colors !== void 0) {
|
|
5662
5830
|
themes.push({ id: data.id, name: data.name, colors: data.colors, builtIn: false, baseTheme: data.baseTheme ?? "" });
|
|
5663
5831
|
}
|
|
@@ -5669,21 +5837,21 @@ function loadCustomThemes() {
|
|
|
5669
5837
|
return themes;
|
|
5670
5838
|
}
|
|
5671
5839
|
function saveCustomTheme(theme) {
|
|
5672
|
-
|
|
5673
|
-
const filePath =
|
|
5674
|
-
|
|
5840
|
+
mkdirSync6(THEMES_DIR, { recursive: true });
|
|
5841
|
+
const filePath = join9(THEMES_DIR, `${theme.id}.json`);
|
|
5842
|
+
writeFileSync7(filePath, JSON.stringify(theme, null, 2), "utf-8");
|
|
5675
5843
|
}
|
|
5676
5844
|
function deleteCustomTheme(id) {
|
|
5677
|
-
const filePath =
|
|
5678
|
-
if (
|
|
5845
|
+
const filePath = join9(THEMES_DIR, `${id}.json`);
|
|
5846
|
+
if (existsSync8(filePath)) {
|
|
5679
5847
|
unlinkSync2(filePath);
|
|
5680
5848
|
}
|
|
5681
5849
|
}
|
|
5682
5850
|
function getCustomTheme(id) {
|
|
5683
|
-
const filePath =
|
|
5684
|
-
if (!
|
|
5851
|
+
const filePath = join9(THEMES_DIR, `${id}.json`);
|
|
5852
|
+
if (!existsSync8(filePath)) return void 0;
|
|
5685
5853
|
try {
|
|
5686
|
-
const data = JSON.parse(
|
|
5854
|
+
const data = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
5687
5855
|
return { ...data, builtIn: false };
|
|
5688
5856
|
} catch {
|
|
5689
5857
|
return void 0;
|
|
@@ -5793,7 +5961,7 @@ function ReviewHistory({ reviews, currentReviewId }) {
|
|
|
5793
5961
|
|
|
5794
5962
|
// src/routes/pages.tsx
|
|
5795
5963
|
init_queries();
|
|
5796
|
-
var pageRoutes = new
|
|
5964
|
+
var pageRoutes = new Hono6();
|
|
5797
5965
|
pageRoutes.get("/", async (c) => {
|
|
5798
5966
|
const reviewId = c.get("reviewId");
|
|
5799
5967
|
const review = await getReview(reviewId);
|
|
@@ -5952,7 +6120,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
5952
6120
|
const repoRoot = c.get("repoRoot");
|
|
5953
6121
|
let content;
|
|
5954
6122
|
try {
|
|
5955
|
-
content =
|
|
6123
|
+
content = readFileSync11(resolve5(repoRoot, filePath), "utf-8");
|
|
5956
6124
|
} catch {
|
|
5957
6125
|
return c.text("File not found", 404);
|
|
5958
6126
|
}
|
|
@@ -6080,8 +6248,8 @@ pageRoutes.get("/history", async (c) => {
|
|
|
6080
6248
|
});
|
|
6081
6249
|
|
|
6082
6250
|
// src/routes/theme-api.ts
|
|
6083
|
-
import { Hono as
|
|
6084
|
-
var themeApiRoutes = new
|
|
6251
|
+
import { Hono as Hono7 } from "hono";
|
|
6252
|
+
var themeApiRoutes = new Hono7();
|
|
6085
6253
|
function validateColors(colors) {
|
|
6086
6254
|
if (typeof colors !== "object" || colors === null || Array.isArray(colors)) {
|
|
6087
6255
|
return "colors must be an object";
|
|
@@ -6215,10 +6383,10 @@ themeApiRoutes.delete("/:id", (c) => {
|
|
|
6215
6383
|
|
|
6216
6384
|
// src/server.ts
|
|
6217
6385
|
function tryServe(appFetch, port) {
|
|
6218
|
-
return new Promise((
|
|
6386
|
+
return new Promise((resolve7, reject) => {
|
|
6219
6387
|
const server = serve({ fetch: appFetch, port, hostname: "127.0.0.1" });
|
|
6220
6388
|
server.on("listening", () => {
|
|
6221
|
-
|
|
6389
|
+
resolve7(port);
|
|
6222
6390
|
});
|
|
6223
6391
|
server.on("error", (err) => {
|
|
6224
6392
|
if (err.code === "EADDRINUSE") {
|
|
@@ -6230,30 +6398,31 @@ function tryServe(appFetch, port) {
|
|
|
6230
6398
|
});
|
|
6231
6399
|
}
|
|
6232
6400
|
async function startServer(port, reviewId, repoRoot, options) {
|
|
6233
|
-
const app = new
|
|
6401
|
+
const app = new Hono8();
|
|
6234
6402
|
app.use("*", async (c, next) => {
|
|
6235
6403
|
c.set("reviewId", reviewId);
|
|
6236
6404
|
c.set("currentReviewId", reviewId);
|
|
6237
6405
|
c.set("repoRoot", repoRoot);
|
|
6238
6406
|
await next();
|
|
6239
6407
|
});
|
|
6240
|
-
const selfDir =
|
|
6241
|
-
const distDir =
|
|
6408
|
+
const selfDir = dirname2(fileURLToPath2(import.meta.url));
|
|
6409
|
+
const distDir = existsSync9(join10(selfDir, "client", "styles.css")) ? join10(selfDir, "client") : join10(selfDir, "..", "dist", "client");
|
|
6242
6410
|
app.get("/static/styles.css", (c) => {
|
|
6243
|
-
const css =
|
|
6411
|
+
const css = readFileSync12(join10(distDir, "styles.css"), "utf-8");
|
|
6244
6412
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
6245
6413
|
});
|
|
6246
6414
|
app.get("/static/app.js", (c) => {
|
|
6247
|
-
const js =
|
|
6415
|
+
const js = readFileSync12(join10(distDir, "app.global.js"), "utf-8");
|
|
6248
6416
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
6249
6417
|
});
|
|
6250
6418
|
app.get("/static/history.js", (c) => {
|
|
6251
|
-
const js =
|
|
6419
|
+
const js = readFileSync12(join10(distDir, "history.global.js"), "utf-8");
|
|
6252
6420
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
6253
6421
|
});
|
|
6254
6422
|
app.route("/api", apiRoutes);
|
|
6255
6423
|
app.route("/api/ai", aiApiRoutes);
|
|
6256
6424
|
app.route("/api/themes", themeApiRoutes);
|
|
6425
|
+
app.route("/api/channel", channelApiRoutes);
|
|
6257
6426
|
app.route("/", pageRoutes);
|
|
6258
6427
|
let actualPort = port;
|
|
6259
6428
|
if (options?.strictPort === true) {
|
|
@@ -6285,8 +6454,8 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
6285
6454
|
}
|
|
6286
6455
|
|
|
6287
6456
|
// src/skills.ts
|
|
6288
|
-
import { existsSync as
|
|
6289
|
-
import { join as
|
|
6457
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
|
|
6458
|
+
import { join as join11 } from "path";
|
|
6290
6459
|
var SKILL_VERSION = 1;
|
|
6291
6460
|
function versionHeader() {
|
|
6292
6461
|
return `<!-- glassbox-skill-version: ${SKILL_VERSION} -->`;
|
|
@@ -6297,14 +6466,14 @@ function parseVersionHeader(content) {
|
|
|
6297
6466
|
return parseInt(match[1], 10);
|
|
6298
6467
|
}
|
|
6299
6468
|
function updateFile(path, content) {
|
|
6300
|
-
if (
|
|
6301
|
-
const existing =
|
|
6469
|
+
if (existsSync10(path)) {
|
|
6470
|
+
const existing = readFileSync13(path, "utf-8");
|
|
6302
6471
|
const version = parseVersionHeader(existing);
|
|
6303
6472
|
if (version !== null && version >= SKILL_VERSION) {
|
|
6304
6473
|
return false;
|
|
6305
6474
|
}
|
|
6306
6475
|
}
|
|
6307
|
-
|
|
6476
|
+
writeFileSync8(path, content, "utf-8");
|
|
6308
6477
|
return true;
|
|
6309
6478
|
}
|
|
6310
6479
|
function skillBody() {
|
|
@@ -6324,8 +6493,8 @@ function skillBody() {
|
|
|
6324
6493
|
].join("\n");
|
|
6325
6494
|
}
|
|
6326
6495
|
function ensureClaudeSkills(cwd) {
|
|
6327
|
-
const dir =
|
|
6328
|
-
|
|
6496
|
+
const dir = join11(cwd, ".claude", "skills", "glassbox");
|
|
6497
|
+
mkdirSync7(dir, { recursive: true });
|
|
6329
6498
|
const content = [
|
|
6330
6499
|
"---",
|
|
6331
6500
|
"name: glassbox",
|
|
@@ -6337,11 +6506,11 @@ function ensureClaudeSkills(cwd) {
|
|
|
6337
6506
|
skillBody(),
|
|
6338
6507
|
""
|
|
6339
6508
|
].join("\n");
|
|
6340
|
-
return updateFile(
|
|
6509
|
+
return updateFile(join11(dir, "SKILL.md"), content);
|
|
6341
6510
|
}
|
|
6342
6511
|
function ensureCursorRules(cwd) {
|
|
6343
|
-
const rulesDir =
|
|
6344
|
-
|
|
6512
|
+
const rulesDir = join11(cwd, ".cursor", "rules");
|
|
6513
|
+
mkdirSync7(rulesDir, { recursive: true });
|
|
6345
6514
|
const content = [
|
|
6346
6515
|
"---",
|
|
6347
6516
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -6352,11 +6521,11 @@ function ensureCursorRules(cwd) {
|
|
|
6352
6521
|
skillBody(),
|
|
6353
6522
|
""
|
|
6354
6523
|
].join("\n");
|
|
6355
|
-
return updateFile(
|
|
6524
|
+
return updateFile(join11(rulesDir, "glassbox.mdc"), content);
|
|
6356
6525
|
}
|
|
6357
6526
|
function ensureCopilotPrompts(cwd) {
|
|
6358
|
-
const promptsDir =
|
|
6359
|
-
|
|
6527
|
+
const promptsDir = join11(cwd, ".github", "prompts");
|
|
6528
|
+
mkdirSync7(promptsDir, { recursive: true });
|
|
6360
6529
|
const content = [
|
|
6361
6530
|
"---",
|
|
6362
6531
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -6366,11 +6535,11 @@ function ensureCopilotPrompts(cwd) {
|
|
|
6366
6535
|
skillBody(),
|
|
6367
6536
|
""
|
|
6368
6537
|
].join("\n");
|
|
6369
|
-
return updateFile(
|
|
6538
|
+
return updateFile(join11(promptsDir, "glassbox.prompt.md"), content);
|
|
6370
6539
|
}
|
|
6371
6540
|
function ensureWindsurfRules(cwd) {
|
|
6372
|
-
const rulesDir =
|
|
6373
|
-
|
|
6541
|
+
const rulesDir = join11(cwd, ".windsurf", "rules");
|
|
6542
|
+
mkdirSync7(rulesDir, { recursive: true });
|
|
6374
6543
|
const content = [
|
|
6375
6544
|
"---",
|
|
6376
6545
|
"trigger: manual",
|
|
@@ -6381,39 +6550,39 @@ function ensureWindsurfRules(cwd) {
|
|
|
6381
6550
|
skillBody(),
|
|
6382
6551
|
""
|
|
6383
6552
|
].join("\n");
|
|
6384
|
-
return updateFile(
|
|
6553
|
+
return updateFile(join11(rulesDir, "glassbox.md"), content);
|
|
6385
6554
|
}
|
|
6386
6555
|
function ensureSkills() {
|
|
6387
6556
|
const cwd = process.cwd();
|
|
6388
6557
|
const platforms = [];
|
|
6389
|
-
if (
|
|
6558
|
+
if (existsSync10(join11(cwd, ".claude"))) {
|
|
6390
6559
|
if (ensureClaudeSkills(cwd)) platforms.push("Claude Code");
|
|
6391
6560
|
}
|
|
6392
|
-
if (
|
|
6561
|
+
if (existsSync10(join11(cwd, ".cursor"))) {
|
|
6393
6562
|
if (ensureCursorRules(cwd)) platforms.push("Cursor");
|
|
6394
6563
|
}
|
|
6395
|
-
if (
|
|
6564
|
+
if (existsSync10(join11(cwd, ".github", "prompts")) || existsSync10(join11(cwd, ".github", "copilot-instructions.md"))) {
|
|
6396
6565
|
if (ensureCopilotPrompts(cwd)) platforms.push("GitHub Copilot");
|
|
6397
6566
|
}
|
|
6398
|
-
if (
|
|
6567
|
+
if (existsSync10(join11(cwd, ".windsurf"))) {
|
|
6399
6568
|
if (ensureWindsurfRules(cwd)) platforms.push("Windsurf");
|
|
6400
6569
|
}
|
|
6401
6570
|
return platforms;
|
|
6402
6571
|
}
|
|
6403
6572
|
|
|
6404
6573
|
// src/update-check.ts
|
|
6405
|
-
import { existsSync as
|
|
6574
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
|
|
6406
6575
|
import { get } from "https";
|
|
6407
|
-
import { homedir as
|
|
6408
|
-
import { dirname as
|
|
6409
|
-
import { fileURLToPath as
|
|
6410
|
-
var DATA_DIR =
|
|
6411
|
-
var CHECK_FILE =
|
|
6576
|
+
import { homedir as homedir6 } from "os";
|
|
6577
|
+
import { dirname as dirname3, join as join12 } from "path";
|
|
6578
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6579
|
+
var DATA_DIR = join12(homedir6(), ".glassbox");
|
|
6580
|
+
var CHECK_FILE = join12(DATA_DIR, "last-update-check");
|
|
6412
6581
|
var PACKAGE_NAME = "glassbox";
|
|
6413
6582
|
function getCurrentVersion() {
|
|
6414
6583
|
try {
|
|
6415
|
-
const dir =
|
|
6416
|
-
const pkg = JSON.parse(
|
|
6584
|
+
const dir = dirname3(fileURLToPath3(import.meta.url));
|
|
6585
|
+
const pkg = JSON.parse(readFileSync14(join12(dir, "..", "package.json"), "utf-8"));
|
|
6417
6586
|
return pkg.version;
|
|
6418
6587
|
} catch {
|
|
6419
6588
|
return "0.0.0";
|
|
@@ -6421,16 +6590,16 @@ function getCurrentVersion() {
|
|
|
6421
6590
|
}
|
|
6422
6591
|
function getLastCheckDate() {
|
|
6423
6592
|
try {
|
|
6424
|
-
if (
|
|
6425
|
-
return
|
|
6593
|
+
if (existsSync11(CHECK_FILE)) {
|
|
6594
|
+
return readFileSync14(CHECK_FILE, "utf-8").trim();
|
|
6426
6595
|
}
|
|
6427
6596
|
} catch {
|
|
6428
6597
|
}
|
|
6429
6598
|
return null;
|
|
6430
6599
|
}
|
|
6431
6600
|
function saveCheckDate() {
|
|
6432
|
-
|
|
6433
|
-
|
|
6601
|
+
mkdirSync8(DATA_DIR, { recursive: true });
|
|
6602
|
+
writeFileSync9(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
|
|
6434
6603
|
}
|
|
6435
6604
|
function isFirstUseToday() {
|
|
6436
6605
|
const last = getLastCheckDate();
|
|
@@ -6439,10 +6608,10 @@ function isFirstUseToday() {
|
|
|
6439
6608
|
return last !== today;
|
|
6440
6609
|
}
|
|
6441
6610
|
function fetchLatestVersion() {
|
|
6442
|
-
return new Promise((
|
|
6611
|
+
return new Promise((resolve7) => {
|
|
6443
6612
|
const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
6444
6613
|
if (res.statusCode !== 200) {
|
|
6445
|
-
|
|
6614
|
+
resolve7(null);
|
|
6446
6615
|
return;
|
|
6447
6616
|
}
|
|
6448
6617
|
let data = "";
|
|
@@ -6451,18 +6620,18 @@ function fetchLatestVersion() {
|
|
|
6451
6620
|
});
|
|
6452
6621
|
res.on("end", () => {
|
|
6453
6622
|
try {
|
|
6454
|
-
|
|
6623
|
+
resolve7(JSON.parse(data).version);
|
|
6455
6624
|
} catch {
|
|
6456
|
-
|
|
6625
|
+
resolve7(null);
|
|
6457
6626
|
}
|
|
6458
6627
|
});
|
|
6459
6628
|
});
|
|
6460
6629
|
req.on("error", () => {
|
|
6461
|
-
|
|
6630
|
+
resolve7(null);
|
|
6462
6631
|
});
|
|
6463
6632
|
req.on("timeout", () => {
|
|
6464
6633
|
req.destroy();
|
|
6465
|
-
|
|
6634
|
+
resolve7(null);
|
|
6466
6635
|
});
|
|
6467
6636
|
});
|
|
6468
6637
|
}
|
|
@@ -6600,7 +6769,7 @@ function parseArgs(argv) {
|
|
|
6600
6769
|
port = parseInt(args[++i], 10);
|
|
6601
6770
|
break;
|
|
6602
6771
|
case "--data-dir":
|
|
6603
|
-
dataDir =
|
|
6772
|
+
dataDir = resolve6(args[++i]);
|
|
6604
6773
|
break;
|
|
6605
6774
|
case "--resume":
|
|
6606
6775
|
resume = true;
|
|
@@ -6656,13 +6825,13 @@ async function main() {
|
|
|
6656
6825
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
6657
6826
|
}
|
|
6658
6827
|
if (debug) {
|
|
6659
|
-
console.log(`[debug] Build timestamp: ${"2026-04-
|
|
6828
|
+
console.log(`[debug] Build timestamp: ${"2026-04-08T00:38:37.701Z"}`);
|
|
6660
6829
|
}
|
|
6661
6830
|
if (projectDir !== null) {
|
|
6662
6831
|
process.chdir(projectDir);
|
|
6663
6832
|
}
|
|
6664
6833
|
if (dataDir === null) {
|
|
6665
|
-
dataDir =
|
|
6834
|
+
dataDir = join13(process.cwd(), ".glassbox");
|
|
6666
6835
|
}
|
|
6667
6836
|
if (demo !== null) {
|
|
6668
6837
|
const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
|
|
@@ -6674,13 +6843,13 @@ async function main() {
|
|
|
6674
6843
|
}
|
|
6675
6844
|
process.exit(1);
|
|
6676
6845
|
}
|
|
6677
|
-
dataDir =
|
|
6846
|
+
dataDir = join13(tmpdir(), `glassbox-demo-${demo}-${Date.now()}`);
|
|
6678
6847
|
setDemoMode(demo);
|
|
6679
6848
|
console.log(`
|
|
6680
6849
|
DEMO MODE: ${scenario.label}
|
|
6681
6850
|
`);
|
|
6682
6851
|
}
|
|
6683
|
-
|
|
6852
|
+
mkdirSync9(dataDir, { recursive: true });
|
|
6684
6853
|
if (demo === null) {
|
|
6685
6854
|
acquireLock(dataDir);
|
|
6686
6855
|
}
|