getgloss 0.4.1 → 0.5.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/cli/index.js +17 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/server/daemon.js +53 -63
- package/dist/server/daemon.js.map +1 -1
- package/dist/web/assets/{index-Da2TNJX9.js → index-AW7l4N7K.js} +45 -45
- package/dist/web/assets/index-rvNgmEHK.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -3
- package/dist/web/assets/index-uGiivUSv.css +0 -1
package/dist/server/daemon.js
CHANGED
|
@@ -16,7 +16,7 @@ import path from "path";
|
|
|
16
16
|
// package.json
|
|
17
17
|
var package_default = {
|
|
18
18
|
name: "getgloss",
|
|
19
|
-
version: "0.
|
|
19
|
+
version: "0.5.0",
|
|
20
20
|
description: "Local browser-based diff review for coding-agent loops.",
|
|
21
21
|
type: "module",
|
|
22
22
|
packageManager: "pnpm@10.33.2",
|
|
@@ -58,7 +58,6 @@ var package_default = {
|
|
|
58
58
|
react: "^19.1.0",
|
|
59
59
|
"react-dom": "^19.1.0",
|
|
60
60
|
ulid: "^3.0.0",
|
|
61
|
-
zod: "^4.4.3",
|
|
62
61
|
zustand: "^5.0.5"
|
|
63
62
|
},
|
|
64
63
|
devDependencies: {
|
|
@@ -67,7 +66,6 @@ var package_default = {
|
|
|
67
66
|
"@types/react": "^19.1.6",
|
|
68
67
|
"@types/react-dom": "^19.1.5",
|
|
69
68
|
"@vitejs/plugin-react": "^4.5.2",
|
|
70
|
-
playwright: "^1.52.0",
|
|
71
69
|
tsup: "^8.5.0",
|
|
72
70
|
tsx: "^4.20.3",
|
|
73
71
|
typescript: "^5.8.3",
|
|
@@ -148,6 +146,7 @@ import { readFile as readFile3 } from "fs/promises";
|
|
|
148
146
|
import path2 from "path";
|
|
149
147
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
150
148
|
import { Hono } from "hono";
|
|
149
|
+
import { streamSSE } from "hono/streaming";
|
|
151
150
|
|
|
152
151
|
// src/server/store.ts
|
|
153
152
|
import { readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -336,7 +335,7 @@ var ReviewStore = class {
|
|
|
336
335
|
comments
|
|
337
336
|
};
|
|
338
337
|
record.meta = { ...record.meta, status: "resolved", resolvedAt };
|
|
339
|
-
return this.persistResolution(record, resolution);
|
|
338
|
+
return this.persistResolution(record, resolution, "review-resolved");
|
|
340
339
|
}
|
|
341
340
|
async resolveComment(id, commentId, summary) {
|
|
342
341
|
const record = await this.get(id);
|
|
@@ -371,7 +370,7 @@ var ReviewStore = class {
|
|
|
371
370
|
comments
|
|
372
371
|
};
|
|
373
372
|
record.meta = fullyResolved ? { ...record.meta, status: "resolved", resolvedAt } : { ...record.meta, status: "submitted", resolvedAt: void 0 };
|
|
374
|
-
return this.persistResolution(record, resolution);
|
|
373
|
+
return this.persistResolution(record, resolution, "comment-resolved");
|
|
375
374
|
}
|
|
376
375
|
async reopenComment(id, commentId) {
|
|
377
376
|
const record = await this.get(id);
|
|
@@ -395,7 +394,7 @@ var ReviewStore = class {
|
|
|
395
394
|
comments
|
|
396
395
|
};
|
|
397
396
|
record.meta = fullyResolved ? { ...record.meta, status: "resolved", resolvedAt: resolvedAt ?? void 0 } : { ...record.meta, status: "submitted", resolvedAt: void 0 };
|
|
398
|
-
return this.persistResolution(record, resolution);
|
|
397
|
+
return this.persistResolution(record, resolution, "comment-reopened");
|
|
399
398
|
}
|
|
400
399
|
subscribe(reviewId, listener) {
|
|
401
400
|
const listeners = this.listeners.get(reviewId) ?? /* @__PURE__ */ new Set();
|
|
@@ -495,7 +494,7 @@ var ReviewStore = class {
|
|
|
495
494
|
throw new Error(`Comment ${commentId} not found`);
|
|
496
495
|
}
|
|
497
496
|
}
|
|
498
|
-
async persistResolution(record, resolution) {
|
|
497
|
+
async persistResolution(record, resolution, reason) {
|
|
499
498
|
record.resolution = resolution;
|
|
500
499
|
this.reviews.set(record.meta.id, record);
|
|
501
500
|
const resolvedPath = globalReviewResolvedFile(record.meta.id);
|
|
@@ -506,7 +505,7 @@ var ReviewStore = class {
|
|
|
506
505
|
writeFile2(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}
|
|
507
506
|
`)
|
|
508
507
|
]);
|
|
509
|
-
|
|
508
|
+
const result = {
|
|
510
509
|
ok: true,
|
|
511
510
|
reviewId: record.meta.id,
|
|
512
511
|
status: record.meta.status,
|
|
@@ -515,6 +514,15 @@ var ReviewStore = class {
|
|
|
515
514
|
path: resolvedPath,
|
|
516
515
|
resolution
|
|
517
516
|
};
|
|
517
|
+
this.emit({
|
|
518
|
+
type: "review.updated",
|
|
519
|
+
reviewId: record.meta.id,
|
|
520
|
+
reason,
|
|
521
|
+
status: result.status,
|
|
522
|
+
resolutionStatus: result.resolutionStatus,
|
|
523
|
+
counts: result.comments
|
|
524
|
+
});
|
|
525
|
+
return result;
|
|
518
526
|
}
|
|
519
527
|
sortResolvedComments(comments, record) {
|
|
520
528
|
const feedbackIndex = new Map(
|
|
@@ -588,71 +596,53 @@ function createApp(origin2) {
|
|
|
588
596
|
if (!record) {
|
|
589
597
|
return c.json({ error: "review not found" }, 404);
|
|
590
598
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if (!closed) {
|
|
599
|
-
controller.enqueue(encoder.encode(chunk));
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
const close = () => {
|
|
599
|
+
return streamSSE(c, async (stream) => {
|
|
600
|
+
let closed = false;
|
|
601
|
+
let pending = Promise.resolve();
|
|
602
|
+
let cleanup = null;
|
|
603
|
+
let close = null;
|
|
604
|
+
const closedPromise = new Promise((resolve) => {
|
|
605
|
+
close = () => {
|
|
603
606
|
if (closed) {
|
|
604
607
|
return;
|
|
605
608
|
}
|
|
606
609
|
closed = true;
|
|
607
|
-
if (heartbeat) {
|
|
608
|
-
clearInterval(heartbeat);
|
|
609
|
-
}
|
|
610
610
|
cleanup?.();
|
|
611
|
+
resolve();
|
|
611
612
|
};
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
close();
|
|
618
|
-
controller.close();
|
|
613
|
+
});
|
|
614
|
+
const send = (event) => {
|
|
615
|
+
pending = pending.then(() => stream.writeSSE({ data: JSON.stringify(event) })).then(() => {
|
|
616
|
+
if (event.type === "review.cancelled") {
|
|
617
|
+
close?.();
|
|
619
618
|
}
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
619
|
+
});
|
|
620
|
+
void pending.catch(() => close?.());
|
|
621
|
+
};
|
|
622
|
+
const unsubscribe = reviewStore.subscribe(id, send);
|
|
623
|
+
const heartbeat = setInterval(() => {
|
|
624
|
+
pending = pending.then(() => stream.write(`: keep-alive ${Date.now()}
|
|
624
625
|
|
|
625
|
-
`);
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
626
|
+
`));
|
|
627
|
+
void pending.catch(() => close?.());
|
|
628
|
+
}, eventStreamHeartbeatMs);
|
|
629
|
+
cleanup = () => {
|
|
630
|
+
clearInterval(heartbeat);
|
|
631
|
+
unsubscribe();
|
|
632
|
+
};
|
|
633
|
+
stream.onAbort(() => close?.());
|
|
634
|
+
send({ type: "review.opened", reviewId: id });
|
|
635
|
+
if ((record.meta.status === "submitted" || record.meta.status === "resolved") && record.feedback) {
|
|
636
|
+
send({
|
|
637
|
+
type: "review.submitted",
|
|
638
|
+
reviewId: id,
|
|
639
|
+
counts: {
|
|
640
|
+
files: new Set(record.feedback.comments.map((comment) => comment.filePath)).size,
|
|
641
|
+
comments: record.feedback.comments.length
|
|
630
642
|
}
|
|
631
|
-
|
|
632
|
-
};
|
|
633
|
-
send({ type: "review.opened", reviewId: id });
|
|
634
|
-
if ((record.meta.status === "submitted" || record.meta.status === "resolved") && record.feedback) {
|
|
635
|
-
send({
|
|
636
|
-
type: "review.submitted",
|
|
637
|
-
reviewId: id,
|
|
638
|
-
counts: {
|
|
639
|
-
files: new Set(record.feedback.comments.map((comment) => comment.filePath)).size,
|
|
640
|
-
comments: record.feedback.comments.length
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
},
|
|
645
|
-
cancel() {
|
|
646
|
-
cleanup?.();
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
|
-
return new Response(stream, {
|
|
650
|
-
headers: {
|
|
651
|
-
"cache-control": "no-cache, no-transform",
|
|
652
|
-
connection: "keep-alive",
|
|
653
|
-
"content-type": "text/event-stream",
|
|
654
|
-
"x-accel-buffering": "no"
|
|
643
|
+
});
|
|
655
644
|
}
|
|
645
|
+
await closedPromise;
|
|
656
646
|
});
|
|
657
647
|
});
|
|
658
648
|
app.post("/api/reviews/:id/submit", async (c) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/daemon.ts","../../src/cli/lifecycle.ts","../../src/shared/paths.ts","../../package.json","../../src/server/index.ts","../../src/server/store.ts","../../src/shared/markdown.ts"],"sourcesContent":["import { serve } from '@hono/node-server';\nimport { writeServerInfo } from '../cli/lifecycle';\nimport { globalStateDir, packageVersion } from '../shared/paths';\nimport { createApp } from './index';\n\nconst port = Number(process.env.GLOSS_PORT ?? '0');\n\nif (!port) {\n throw new Error('GLOSS_PORT is required');\n}\n\nconst origin = `http://localhost:${port}`;\nconst server = serve({\n fetch: createApp(origin).fetch,\n port\n});\n\nawait writeServerInfo({\n pid: process.pid,\n port,\n version: packageVersion,\n startedAt: new Date().toISOString(),\n stateDir: globalStateDir()\n});\n\nprocess.on('SIGTERM', () => {\n server.close(() => {\n process.exit(0);\n });\n});\n","import { spawn } from 'node:child_process';\nimport { existsSync, openSync } from 'node:fs';\nimport { readFile, rm, writeFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport getPort from 'get-port';\nimport {\n ensureDir,\n globalLogDir,\n globalServerFile,\n globalServerLogFile,\n globalStateDir,\n packageVersion\n} from '../shared/paths';\nimport type { ServerInfo } from '../shared/types';\nimport { ServerClient } from './server-client';\n\nexport async function readServerInfo(): Promise<ServerInfo | null> {\n try {\n return JSON.parse(await readFile(globalServerFile(), 'utf8')) as ServerInfo;\n } catch {\n return null;\n }\n}\n\nexport function serverUrl(info: Pick<ServerInfo, 'port'>): string {\n return `http://localhost:${info.port}`;\n}\n\nexport async function isServerResponsive(info: ServerInfo): Promise<boolean> {\n if (!isPidAlive(info.pid)) {\n return false;\n }\n try {\n const health = await new ServerClient(serverUrl(info)).health();\n return health.ok === true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureServer(options: { port?: number } = {}): Promise<ServerInfo> {\n const existing = await readServerInfo();\n if (existing && (await isServerResponsive(existing))) {\n return existing;\n }\n return startServer(options);\n}\n\nexport async function startServer(options: { port?: number } = {}): Promise<ServerInfo> {\n const existing = await readServerInfo();\n if (existing && (await isServerResponsive(existing))) {\n return existing;\n }\n\n await ensureDir(globalStateDir());\n await ensureDir(globalLogDir());\n const port = options.port ?? (await getPort());\n const daemonPath = fileURLToPath(new URL('../server/daemon.js', import.meta.url));\n if (!existsSync(daemonPath)) {\n throw new Error(`Cannot find server daemon at ${daemonPath}. Run pnpm build first.`);\n }\n\n const logFd = openSync(globalServerLogFile(), 'a');\n const child = spawn(process.execPath, [daemonPath], {\n detached: true,\n env: {\n ...process.env,\n GLOSS_PORT: String(port),\n GLOSS_STATE_DIR: globalStateDir()\n },\n stdio: ['ignore', logFd, logFd]\n });\n child.unref();\n\n const info: ServerInfo = {\n pid: child.pid ?? -1,\n port,\n version: packageVersion,\n startedAt: new Date().toISOString(),\n stateDir: globalStateDir()\n };\n await writeFile(globalServerFile(), `${JSON.stringify(info, null, 2)}\\n`);\n\n const deadline = Date.now() + 8000;\n while (Date.now() < deadline) {\n if (await isServerResponsive(info)) {\n return info;\n }\n await new Promise((resolve) => setTimeout(resolve, 150));\n }\n\n throw new Error(`Server did not become responsive. See ${globalServerLogFile()}`);\n}\n\nexport async function stopServer(): Promise<{ stopped: boolean; info: ServerInfo | null }> {\n const info = await readServerInfo();\n if (!info) {\n return { stopped: false, info: null };\n }\n\n if (isPidAlive(info.pid)) {\n process.kill(info.pid, 'SIGTERM');\n }\n await rm(globalServerFile(), { force: true });\n return { stopped: true, info };\n}\n\nexport async function writeServerInfo(info: ServerInfo): Promise<void> {\n await ensureDir(globalStateDir());\n await writeFile(globalServerFile(), `${JSON.stringify(info, null, 2)}\\n`);\n}\n\nexport function isPidAlive(pid: number): boolean {\n if (pid <= 0) {\n return false;\n }\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\nimport packageJson from '../../package.json';\n\nexport const packageVersion = packageJson.version;\n\nexport function expandHome(input: string): string {\n if (input === '~') {\n return homedir();\n }\n if (input.startsWith('~/')) {\n return path.join(homedir(), input.slice(2));\n }\n return input;\n}\n\nexport function globalStateDir(): string {\n return expandHome(process.env.GLOSS_STATE_DIR ?? '~/.gloss');\n}\n\nexport function globalServerFile(): string {\n return path.join(globalStateDir(), 'server.json');\n}\n\nexport function globalLogDir(): string {\n return path.join(globalStateDir(), 'logs');\n}\n\nexport function globalServerLogFile(): string {\n return path.join(globalLogDir(), 'server.log');\n}\n\nexport function globalReviewsDir(): string {\n return path.join(globalStateDir(), 'reviews');\n}\n\nexport function globalReviewDir(reviewId: string): string {\n return path.join(globalReviewsDir(), reviewId);\n}\n\nexport function globalReviewMetaFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'meta.json');\n}\n\nexport function globalReviewDiffFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'diff.json');\n}\n\nexport function globalReviewFeedbackFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'feedback.json');\n}\n\nexport function globalReviewMarkdownFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'feedback.md');\n}\n\nexport function globalReviewResolvedFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'resolved.json');\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n","{\n \"name\": \"getgloss\",\n \"version\": \"0.4.1\",\n \"description\": \"Local browser-based diff review for coding-agent loops.\",\n \"type\": \"module\",\n \"packageManager\": \"pnpm@10.33.2\",\n \"bin\": {\n \"getgloss\": \"./dist/cli/index.js\",\n \"gloss\": \"./dist/cli/index.js\"\n },\n \"files\": [\n \"dist\",\n \"skill\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"pnpm build:web && pnpm build:node\",\n \"build:web\": \"vite build\",\n \"build:node\": \"tsup\",\n \"check\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"prepack\": \"pnpm build\",\n \"dev:web\": \"vite --host 127.0.0.1\",\n \"setup\": \"tsx scripts/dev-cli.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"engines\": {\n \"node\": \">=20\"\n },\n \"dependencies\": {\n \"@hono/node-server\": \"^1.14.4\",\n \"@pierre/diffs\": \"^1.2.1\",\n \"@tailwindcss/vite\": \"^4.1.7\",\n \"commander\": \"^14.0.0\",\n \"execa\": \"^9.5.3\",\n \"get-port\": \"^7.1.0\",\n \"hono\": \"^4.7.10\",\n \"lucide-react\": \"^1.16.0\",\n \"open\": \"^10.1.2\",\n \"react\": \"^19.1.0\",\n \"react-dom\": \"^19.1.0\",\n \"ulid\": \"^3.0.0\",\n \"zod\": \"^4.4.3\",\n \"zustand\": \"^5.0.5\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.0.6\",\n \"@types/node\": \"^24.0.1\",\n \"@types/react\": \"^19.1.6\",\n \"@types/react-dom\": \"^19.1.5\",\n \"@vitejs/plugin-react\": \"^4.5.2\",\n \"playwright\": \"^1.52.0\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.3\",\n \"typescript\": \"^5.8.3\",\n \"vite\": \"^6.3.5\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"diff\",\n \"review\",\n \"coding-agents\"\n ],\n \"author\": \"Raj Joshi\",\n \"license\": \"MIT\",\n \"homepage\": \"https://getgloss.dev\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/iamrajjoshi/gloss.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/iamrajjoshi/gloss/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Context } from 'hono';\nimport { Hono } from 'hono';\nimport { globalReviewDir, packageVersion } from '../shared/paths';\nimport type { Comment, DiffPayload, ReviewEvent } from '../shared/types';\nimport { reviewStore } from './store';\n\nconst webRoot = fileURLToPath(new URL('../web', import.meta.url));\nconst eventStreamHeartbeatMs = 15_000;\n\nconst mimeTypes: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.sh': 'text/x-shellscript; charset=utf-8',\n '.svg': 'image/svg+xml'\n};\n\nexport function createApp(origin: string): Hono {\n const app = new Hono();\n\n app.get('/api/health', async (c) => {\n const reviews = await reviewStore.list();\n return c.json({\n ok: true,\n version: packageVersion,\n activeReviews: reviews.filter((review) => review.status === 'pending').length\n });\n });\n\n app.get('/api/reviews', async (c) => c.json({ reviews: await reviewStore.list() }));\n\n app.post('/api/reviews', async (c) => {\n const diff = (await c.req.json()) as DiffPayload;\n const record = await reviewStore.create(diff);\n return c.json({ meta: record.meta, url: `${origin}/review/${record.meta.id}` }, 201);\n });\n\n app.get('/api/reviews/:id', async (c) => {\n const record = await reviewStore.get(c.req.param('id'));\n if (!record) {\n return c.json({ error: 'review not found' }, 404);\n }\n return c.json(record);\n });\n\n app.get('/api/reviews/:id/feedback', async (c) => {\n const feedback = await reviewStore.feedback(c.req.param('id'));\n if (!feedback) {\n return c.json({ error: 'feedback not found' }, 404);\n }\n return c.json(feedback);\n });\n\n app.get('/api/reviews/:id/events', async (c) => {\n const id = c.req.param('id');\n const record = await reviewStore.get(id);\n if (!record) {\n return c.json({ error: 'review not found' }, 404);\n }\n\n const encoder = new TextEncoder();\n let cleanup: (() => void) | null = null;\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n let closed = false;\n let heartbeat: ReturnType<typeof setInterval> | null = null;\n const write = (chunk: string) => {\n if (!closed) {\n controller.enqueue(encoder.encode(chunk));\n }\n };\n const close = () => {\n if (closed) {\n return;\n }\n closed = true;\n if (heartbeat) {\n clearInterval(heartbeat);\n }\n cleanup?.();\n };\n const send = (event: ReviewEvent) => {\n write(`data: ${JSON.stringify(event)}\\n\\n`);\n if (event.type === 'review.submitted' || event.type === 'review.cancelled') {\n close();\n controller.close();\n }\n };\n const unsubscribe = reviewStore.subscribe(id, send);\n heartbeat = setInterval(() => {\n write(`: keep-alive ${Date.now()}\\n\\n`);\n }, eventStreamHeartbeatMs);\n cleanup = () => {\n if (heartbeat) {\n clearInterval(heartbeat);\n }\n unsubscribe();\n };\n send({ type: 'review.opened', reviewId: id });\n if (\n (record.meta.status === 'submitted' || record.meta.status === 'resolved') &&\n record.feedback\n ) {\n send({\n type: 'review.submitted',\n reviewId: id,\n counts: {\n files: new Set(record.feedback.comments.map((comment) => comment.filePath)).size,\n comments: record.feedback.comments.length\n }\n });\n }\n },\n cancel() {\n cleanup?.();\n }\n });\n\n return new Response(stream, {\n headers: {\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n 'content-type': 'text/event-stream',\n 'x-accel-buffering': 'no'\n }\n });\n });\n\n app.post('/api/reviews/:id/submit', async (c) => {\n const id = c.req.param('id');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'pending') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be submitted` }, 409);\n }\n const body = (await c.req.json()) as { comments: Comment[] };\n const { record, feedbackPath, markdownPath } = await reviewStore.submit(\n id,\n body.comments ?? []\n );\n return c.json({\n reviewId: id,\n url: `${origin}/review/${id}`,\n files: record.diff.files.length,\n comments: body.comments?.length ?? 0,\n artifactDir: record.meta.artifactDir,\n feedbackPath,\n markdownPath\n });\n });\n\n app.post('/api/reviews/:id/resolved', async (c) => {\n const id = c.req.param('id');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback) {\n return c.json({ error: 'submitted feedback not found' }, 409);\n }\n const body = (await c.req.json().catch(() => ({}))) as { summary?: string };\n return c.json(await reviewStore.markResolved(id, body.summary));\n });\n\n app.post('/api/reviews/:id/comments/:commentId/resolved', async (c) => {\n const id = c.req.param('id');\n const commentId = c.req.param('commentId');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback?.comments.some((comment) => comment.id === commentId)) {\n return c.json({ error: 'comment not found' }, 404);\n }\n const body = (await c.req.json().catch(() => ({}))) as { summary?: string };\n return c.json(await reviewStore.resolveComment(id, commentId, body.summary));\n });\n\n app.delete('/api/reviews/:id/comments/:commentId/resolved', async (c) => {\n const id = c.req.param('id');\n const commentId = c.req.param('commentId');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback?.comments.some((comment) => comment.id === commentId)) {\n return c.json({ error: 'comment not found' }, 404);\n }\n return c.json(await reviewStore.reopenComment(id, commentId));\n });\n\n app.get('/logo.svg', serveRootFile('logo.svg', mimeTypes['.svg']));\n app.get('/logo-mark.svg', serveRootFile('logo-mark.svg', mimeTypes['.svg']));\n app.get('/og.png', serveRootFile('og.png', mimeTypes['.png']));\n app.get('/install.sh', serveRootFile('install.sh', mimeTypes['.sh']));\n app.get('/setup.md', serveRootFile('setup.md', 'text/markdown; charset=utf-8'));\n app.get('/prompt.md', serveRootFile('prompt.md', 'text/markdown; charset=utf-8'));\n app.get('/assets/*', serveAsset);\n app.get('/setup', serveIndex);\n app.get('/setup/', serveIndex);\n app.get('/review/:id', serveIndex);\n app.get('/', serveIndex);\n\n return app;\n}\n\nasync function serveAsset(c: Context) {\n const requestPath = new URL(c.req.url).pathname.replace(/^\\/assets\\//, '');\n const normalized = path.normalize(requestPath).replace(/^(\\.\\.(\\/|\\\\|$))+/, '');\n const assetPath = path.join(webRoot, 'assets', normalized);\n try {\n const body = await readFile(assetPath);\n return new Response(body, {\n headers: {\n 'content-type': mimeTypes[path.extname(assetPath)] ?? 'application/octet-stream'\n }\n });\n } catch {\n return new Response('Not found', { status: 404 });\n }\n}\n\nasync function serveIndex() {\n try {\n const body = await readFile(path.join(webRoot, 'index.html'));\n return new Response(body, {\n headers: { 'content-type': 'text/html; charset=utf-8' }\n });\n } catch {\n return new Response('Gloss web assets are missing. Run pnpm build.', { status: 500 });\n }\n}\n\nfunction serveRootFile(fileName: string, contentType: string) {\n return async () => {\n try {\n const body = await readFile(path.join(webRoot, fileName));\n return new Response(body, {\n headers: { 'content-type': contentType }\n });\n } catch {\n return new Response(`${fileName} is missing. Run pnpm build.`, { status: 404 });\n }\n };\n}\n\nexport function getReviewArtifactDir(_cwd: string, reviewId: string): string {\n return globalReviewDir(reviewId);\n}\n","import type { Dirent } from 'node:fs';\nimport { readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { ulid } from 'ulid';\nimport { serializeFeedbackMarkdown } from '../shared/markdown';\nimport {\n ensureDir,\n globalReviewDiffFile,\n globalReviewDir,\n globalReviewFeedbackFile,\n globalReviewMarkdownFile,\n globalReviewMetaFile,\n globalReviewResolvedFile,\n globalReviewsDir\n} from '../shared/paths';\nimport type {\n Comment,\n DiffPayload,\n FeedbackBundle,\n ResolutionBundle,\n ResolvedComment,\n ResolveResult,\n ReviewEvent,\n ReviewMeta,\n ReviewRecord\n} from '../shared/types';\n\ntype Listener = (event: ReviewEvent) => void;\n\nexport class ReviewStore {\n private readonly reviews = new Map<string, ReviewRecord>();\n private readonly listeners = new Map<string, Set<Listener>>();\n\n async create(diff: DiffPayload): Promise<ReviewRecord> {\n const id = ulid();\n const createdAt = new Date().toISOString();\n const meta: ReviewMeta = {\n id,\n cwd: diff.cwd,\n base: diff.base,\n branch: diff.branch,\n status: 'pending',\n createdAt,\n artifactDir: globalReviewDir(id)\n };\n const record: ReviewRecord = { meta, diff };\n this.reviews.set(id, record);\n await this.persistInitial(record);\n this.emit({ type: 'review.opened', reviewId: id });\n return record;\n }\n\n async list(): Promise<ReviewMeta[]> {\n await this.loadAllReviews();\n return [...this.reviews.values()]\n .map((record) => record.meta)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n }\n\n async get(id: string): Promise<ReviewRecord | null> {\n return this.reviews.get(id) ?? (await this.loadKnownReview(id));\n }\n\n async submit(\n id: string,\n comments: Comment[]\n ): Promise<{ record: ReviewRecord; feedbackPath: string; markdownPath: string }> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n if (record.meta.status !== 'pending') {\n throw new Error(`Review ${id} is ${record.meta.status} and cannot be submitted`);\n }\n const timestamp = new Date().toISOString();\n const feedback: FeedbackBundle = {\n version: 1,\n reviewId: id,\n timestamp,\n base: record.diff.base,\n branch: record.diff.branch,\n comments: [...comments].sort(\n (a, b) =>\n a.filePath.localeCompare(b.filePath) ||\n a.startLine - b.startLine ||\n a.endLine - b.endLine ||\n a.side.localeCompare(b.side)\n )\n };\n record.feedback = feedback;\n record.meta = { ...record.meta, status: 'submitted', submittedAt: timestamp };\n this.reviews.set(id, record);\n\n const artifactDir = globalReviewDir(id);\n const feedbackPath = globalReviewFeedbackFile(id);\n const markdownPath = globalReviewMarkdownFile(id);\n record.meta = {\n ...record.meta,\n artifactDir,\n feedbackPath,\n markdownPath\n };\n await ensureDir(artifactDir);\n await Promise.all([\n writeFile(globalReviewMetaFile(id), `${JSON.stringify(record.meta, null, 2)}\\n`),\n writeFile(feedbackPath, `${JSON.stringify(feedback, null, 2)}\\n`),\n writeFile(markdownPath, serializeFeedbackMarkdown(feedback))\n ]);\n\n this.emit({\n type: 'review.submitted',\n reviewId: id,\n counts: {\n files: new Set(feedback.comments.map((comment) => comment.filePath)).size,\n comments: feedback.comments.length\n }\n });\n return { record, feedbackPath, markdownPath };\n }\n\n async feedback(id: string): Promise<FeedbackBundle | null> {\n const record = await this.get(id);\n return record?.feedback ?? null;\n }\n\n async markResolved(id: string, summary?: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n const resolvedAt = new Date().toISOString();\n const existingById = new Map(\n (record.resolution?.comments ?? []).map((comment) => [comment.commentId, comment])\n );\n const comments = this.sortResolvedComments(\n (record.feedback?.comments ?? []).map((comment) => ({\n ...existingById.get(comment.id),\n commentId: comment.id,\n status: 'resolved' as const,\n resolvedAt: existingById.get(comment.id)?.resolvedAt ?? resolvedAt\n })),\n record\n );\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: 'resolved',\n summary: summary ?? record.resolution?.summary ?? null,\n resolvedAt,\n comments\n };\n record.meta = { ...record.meta, status: 'resolved', resolvedAt };\n return this.persistResolution(record, resolution);\n }\n\n async resolveComment(id: string, commentId: string, summary?: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n this.assertCommentExists(record, commentId);\n\n const resolvedAt = new Date().toISOString();\n const previous = record.resolution?.comments.find((comment) => comment.commentId === commentId);\n const nextSummary = summary ?? previous?.summary;\n const nextComment: ResolvedComment = {\n commentId,\n status: 'resolved',\n ...(nextSummary ? { summary: nextSummary } : {}),\n resolvedAt\n };\n const comments = this.sortResolvedComments(\n [\n ...(record.resolution?.comments ?? []).filter((comment) => comment.commentId !== commentId),\n nextComment\n ],\n record\n );\n const counts = this.resolutionCounts(record, comments);\n const fullyResolved = counts.total === counts.resolved;\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: fullyResolved ? 'resolved' : 'partial',\n summary: fullyResolved ? (record.resolution?.summary ?? null) : null,\n resolvedAt: fullyResolved ? resolvedAt : null,\n comments\n };\n record.meta = fullyResolved\n ? { ...record.meta, status: 'resolved', resolvedAt }\n : { ...record.meta, status: 'submitted', resolvedAt: undefined };\n return this.persistResolution(record, resolution);\n }\n\n async reopenComment(id: string, commentId: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n this.assertCommentExists(record, commentId);\n\n const comments = this.sortResolvedComments(\n (record.resolution?.comments ?? []).filter((comment) => comment.commentId !== commentId),\n record\n );\n const counts = this.resolutionCounts(record, comments);\n const fullyResolved = counts.total > 0 && counts.total === counts.resolved;\n const resolvedAt = fullyResolved ? new Date().toISOString() : null;\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: fullyResolved ? 'resolved' : 'partial',\n summary: fullyResolved ? (record.resolution?.summary ?? null) : null,\n resolvedAt,\n comments\n };\n record.meta = fullyResolved\n ? { ...record.meta, status: 'resolved', resolvedAt: resolvedAt ?? undefined }\n : { ...record.meta, status: 'submitted', resolvedAt: undefined };\n return this.persistResolution(record, resolution);\n }\n\n subscribe(reviewId: string, listener: Listener): () => void {\n const listeners = this.listeners.get(reviewId) ?? new Set<Listener>();\n listeners.add(listener);\n this.listeners.set(reviewId, listeners);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.listeners.delete(reviewId);\n }\n };\n }\n\n private emit(event: ReviewEvent): void {\n for (const listener of this.listeners.get(event.reviewId) ?? []) {\n listener(event);\n }\n }\n\n private async persistInitial(record: ReviewRecord): Promise<void> {\n const dir = globalReviewDir(record.meta.id);\n await ensureDir(dir);\n await Promise.all([\n writeFile(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}\\n`),\n writeFile(globalReviewDiffFile(record.meta.id), `${JSON.stringify(record.diff, null, 2)}\\n`)\n ]);\n }\n\n private async loadKnownReview(id: string): Promise<ReviewRecord | null> {\n const existing = this.reviews.get(id);\n if (existing) {\n return existing;\n }\n\n return this.loadReview(id);\n }\n\n private async loadAllReviews(): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(globalReviewsDir(), { withFileTypes: true });\n } catch {\n return;\n }\n\n await Promise.all(\n entries.filter((entry) => entry.isDirectory()).map((entry) => this.loadReview(entry.name))\n );\n }\n\n private async loadReview(id: string): Promise<ReviewRecord | null> {\n try {\n const [metaRaw, diffRaw] = await Promise.all([\n readFile(globalReviewMetaFile(id), 'utf8'),\n readFile(globalReviewDiffFile(id), 'utf8')\n ]);\n const meta = JSON.parse(metaRaw) as ReviewMeta;\n const diff = JSON.parse(diffRaw) as DiffPayload;\n let feedback: FeedbackBundle | undefined;\n let resolution: ResolutionBundle | undefined;\n try {\n feedback = JSON.parse(\n await readFile(globalReviewFeedbackFile(id), 'utf8')\n ) as FeedbackBundle;\n } catch {\n feedback = undefined;\n }\n try {\n resolution = JSON.parse(\n await readFile(globalReviewResolvedFile(id), 'utf8')\n ) as ResolutionBundle;\n } catch {\n resolution = undefined;\n }\n\n const record: ReviewRecord = {\n meta: {\n ...meta,\n artifactDir: meta.artifactDir ?? globalReviewDir(id),\n feedbackPath: meta.feedbackPath ?? (feedback ? globalReviewFeedbackFile(id) : undefined),\n markdownPath: meta.markdownPath ?? (feedback ? globalReviewMarkdownFile(id) : undefined)\n },\n diff,\n feedback,\n resolution\n };\n this.reviews.set(id, record);\n return record;\n } catch {\n return null;\n }\n }\n\n private assertResolvable(\n record: ReviewRecord,\n id: string\n ): asserts record is ReviewRecord & {\n feedback: FeedbackBundle;\n } {\n if (record.meta.status !== 'submitted' && record.meta.status !== 'resolved') {\n throw new Error(`Review ${id} is ${record.meta.status} and cannot be resolved`);\n }\n if (!record.feedback) {\n throw new Error(`Review ${id} has no submitted feedback`);\n }\n }\n\n private assertCommentExists(\n record: ReviewRecord & { feedback: FeedbackBundle },\n commentId: string\n ): void {\n if (!record.feedback.comments.some((comment) => comment.id === commentId)) {\n throw new Error(`Comment ${commentId} not found`);\n }\n }\n\n private async persistResolution(\n record: ReviewRecord & { feedback: FeedbackBundle },\n resolution: ResolutionBundle\n ): Promise<ResolveResult> {\n record.resolution = resolution;\n this.reviews.set(record.meta.id, record);\n const resolvedPath = globalReviewResolvedFile(record.meta.id);\n await ensureDir(globalReviewDir(record.meta.id));\n await Promise.all([\n writeFile(resolvedPath, `${JSON.stringify(resolution, null, 2)}\\n`),\n writeFile(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}\\n`)\n ]);\n return {\n ok: true,\n reviewId: record.meta.id,\n status: record.meta.status,\n resolutionStatus: resolution.status,\n comments: this.resolutionCounts(record, resolution.comments),\n path: resolvedPath,\n resolution\n };\n }\n\n private sortResolvedComments(\n comments: ResolvedComment[],\n record: ReviewRecord & { feedback: FeedbackBundle }\n ): ResolvedComment[] {\n const feedbackIndex = new Map(\n record.feedback.comments.map((comment, index) => [comment.id, index] as const)\n );\n return comments\n .filter((comment) => feedbackIndex.has(comment.commentId))\n .sort(\n (a, b) =>\n (feedbackIndex.get(a.commentId) ?? Number.MAX_SAFE_INTEGER) -\n (feedbackIndex.get(b.commentId) ?? Number.MAX_SAFE_INTEGER)\n );\n }\n\n private resolutionCounts(\n record: ReviewRecord & { feedback: FeedbackBundle },\n comments: ResolvedComment[]\n ): { total: number; resolved: number; open: number } {\n const total = record.feedback.comments.length;\n const resolvedIds = new Set(comments.map((comment) => comment.commentId));\n const resolved = record.feedback.comments.filter((comment) =>\n resolvedIds.has(comment.id)\n ).length;\n return {\n total,\n resolved,\n open: total - resolved\n };\n }\n}\n\nexport const reviewStore = new ReviewStore();\n\nexport async function removeReviewArtifacts(_cwd: string, id: string): Promise<void> {\n await rm(globalReviewDir(id), { force: true, recursive: true });\n}\n","import type { Comment, FeedbackBundle } from './types';\n\nfunction formatLineRange(comment: Comment): string {\n const prefix = comment.side;\n if (comment.startLine === comment.endLine) {\n return `${prefix}${comment.startLine}`;\n }\n return `${prefix}${comment.startLine}-${prefix}${comment.endLine}`;\n}\n\nfunction fenceFor(snippet: string): string {\n let fence = '```';\n while (snippet.includes(fence)) {\n fence += '`';\n }\n return fence;\n}\n\nfunction languageForPath(filePath: string): string {\n const ext = filePath.split('.').pop()?.toLowerCase();\n const map: Record<string, string> = {\n cjs: 'js',\n css: 'css',\n go: 'go',\n html: 'html',\n js: 'js',\n json: 'json',\n jsx: 'jsx',\n md: 'markdown',\n mjs: 'js',\n py: 'python',\n rb: 'ruby',\n rs: 'rust',\n sh: 'bash',\n swift: 'swift',\n ts: 'ts',\n tsx: 'tsx',\n yaml: 'yaml',\n yml: 'yaml'\n };\n return ext ? (map[ext] ?? ext) : '';\n}\n\nfunction languageForSnippet(filePath: string, snippet: string): string {\n const lines = snippet.split('\\n').filter((line) => line.length > 0);\n const looksLikeUnifiedDiff =\n lines.length > 0 &&\n lines.some((line) => line.startsWith('+') || line.startsWith('-')) &&\n lines.every((line) => line.startsWith('+') || line.startsWith('-') || line.startsWith(' '));\n return looksLikeUnifiedDiff ? 'diff' : languageForPath(filePath);\n}\n\nfunction byFileThenLine(a: Comment, b: Comment): number {\n return (\n a.filePath.localeCompare(b.filePath) ||\n a.startLine - b.startLine ||\n a.endLine - b.endLine ||\n a.side.localeCompare(b.side)\n );\n}\n\nexport function serializeFeedbackMarkdown(bundle: FeedbackBundle): string {\n const comments = [...bundle.comments].sort(byFileThenLine);\n const files = [...new Set(comments.map((comment) => comment.filePath))];\n const lines: string[] = [\n `# Gloss feedback - ${bundle.timestamp}`,\n `Review: ${bundle.reviewId}`,\n `Base: ${bundle.base.ref} (${bundle.base.sha.slice(0, 7)}) Branch: ${bundle.branch ?? '(detached)'}`,\n `Files: ${files.length} Comments: ${comments.length}`,\n ''\n ];\n\n for (const filePath of files) {\n lines.push(`## ${filePath}`, '');\n for (const comment of comments.filter((item) => item.filePath === filePath)) {\n const snippet = comment.originalSnippet.trimEnd();\n const firstSnippetLine = snippet.split('\\n').find((line) => line.trim().length > 0);\n const heading =\n comment.startLine === comment.endLine && firstSnippetLine\n ? `### ${formatLineRange(comment)} - \\`${firstSnippetLine.trim().slice(0, 80)}\\``\n : `### ${formatLineRange(comment)}`;\n lines.push(heading, comment.body.trim(), '');\n if (snippet) {\n const fence = fenceFor(snippet);\n lines.push(`${fence}${languageForSnippet(comment.filePath, snippet)}`, snippet, fence, '');\n }\n }\n }\n\n return `${lines.join('\\n').trimEnd()}\\n`;\n}\n"],"mappings":"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","names":["readFile","path","fileURLToPath","readFile","rm","writeFile","writeFile","readFile","fileURLToPath","origin","path","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/daemon.ts","../../src/cli/lifecycle.ts","../../src/shared/paths.ts","../../package.json","../../src/server/index.ts","../../src/server/store.ts","../../src/shared/markdown.ts"],"sourcesContent":["import { serve } from '@hono/node-server';\nimport { writeServerInfo } from '../cli/lifecycle';\nimport { globalStateDir, packageVersion } from '../shared/paths';\nimport { createApp } from './index';\n\nconst port = Number(process.env.GLOSS_PORT ?? '0');\n\nif (!port) {\n throw new Error('GLOSS_PORT is required');\n}\n\nconst origin = `http://localhost:${port}`;\nconst server = serve({\n fetch: createApp(origin).fetch,\n port\n});\n\nawait writeServerInfo({\n pid: process.pid,\n port,\n version: packageVersion,\n startedAt: new Date().toISOString(),\n stateDir: globalStateDir()\n});\n\nprocess.on('SIGTERM', () => {\n server.close(() => {\n process.exit(0);\n });\n});\n","import { spawn } from 'node:child_process';\nimport { existsSync, openSync } from 'node:fs';\nimport { readFile, rm, writeFile } from 'node:fs/promises';\nimport { fileURLToPath } from 'node:url';\nimport getPort from 'get-port';\nimport {\n ensureDir,\n globalLogDir,\n globalServerFile,\n globalServerLogFile,\n globalStateDir,\n packageVersion\n} from '../shared/paths';\nimport type { ServerInfo } from '../shared/types';\nimport { ServerClient } from './server-client';\n\nexport async function readServerInfo(): Promise<ServerInfo | null> {\n try {\n return JSON.parse(await readFile(globalServerFile(), 'utf8')) as ServerInfo;\n } catch {\n return null;\n }\n}\n\nexport function serverUrl(info: Pick<ServerInfo, 'port'>): string {\n return `http://localhost:${info.port}`;\n}\n\nexport async function isServerResponsive(info: ServerInfo): Promise<boolean> {\n if (!isPidAlive(info.pid)) {\n return false;\n }\n try {\n const health = await new ServerClient(serverUrl(info)).health();\n return health.ok === true && health.version === packageVersion;\n } catch {\n return false;\n }\n}\n\nexport async function ensureServer(options: { port?: number } = {}): Promise<ServerInfo> {\n const existing = await readServerInfo();\n if (existing && (await isServerResponsive(existing))) {\n return existing;\n }\n return startServer(options);\n}\n\nexport async function startServer(options: { port?: number } = {}): Promise<ServerInfo> {\n const existing = await readServerInfo();\n if (existing && (await isServerResponsive(existing))) {\n return existing;\n }\n\n await ensureDir(globalStateDir());\n await ensureDir(globalLogDir());\n const port = options.port ?? (await getPort());\n const daemonPath = fileURLToPath(new URL('../server/daemon.js', import.meta.url));\n if (!existsSync(daemonPath)) {\n throw new Error(`Cannot find server daemon at ${daemonPath}. Run pnpm build first.`);\n }\n\n const logFd = openSync(globalServerLogFile(), 'a');\n const child = spawn(process.execPath, [daemonPath], {\n detached: true,\n env: {\n ...process.env,\n GLOSS_PORT: String(port),\n GLOSS_STATE_DIR: globalStateDir()\n },\n stdio: ['ignore', logFd, logFd]\n });\n child.unref();\n\n const info: ServerInfo = {\n pid: child.pid ?? -1,\n port,\n version: packageVersion,\n startedAt: new Date().toISOString(),\n stateDir: globalStateDir()\n };\n await writeFile(globalServerFile(), `${JSON.stringify(info, null, 2)}\\n`);\n\n const deadline = Date.now() + 8000;\n while (Date.now() < deadline) {\n if (await isServerResponsive(info)) {\n return info;\n }\n await new Promise((resolve) => setTimeout(resolve, 150));\n }\n\n throw new Error(`Server did not become responsive. See ${globalServerLogFile()}`);\n}\n\nexport async function stopServer(): Promise<{ stopped: boolean; info: ServerInfo | null }> {\n const info = await readServerInfo();\n if (!info) {\n return { stopped: false, info: null };\n }\n\n if (isPidAlive(info.pid)) {\n process.kill(info.pid, 'SIGTERM');\n }\n await rm(globalServerFile(), { force: true });\n return { stopped: true, info };\n}\n\nexport async function writeServerInfo(info: ServerInfo): Promise<void> {\n await ensureDir(globalStateDir());\n await writeFile(globalServerFile(), `${JSON.stringify(info, null, 2)}\\n`);\n}\n\nexport function isPidAlive(pid: number): boolean {\n if (pid <= 0) {\n return false;\n }\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n","import { mkdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\nimport packageJson from '../../package.json';\n\nexport const packageVersion = packageJson.version;\n\nexport function expandHome(input: string): string {\n if (input === '~') {\n return homedir();\n }\n if (input.startsWith('~/')) {\n return path.join(homedir(), input.slice(2));\n }\n return input;\n}\n\nexport function globalStateDir(): string {\n return expandHome(process.env.GLOSS_STATE_DIR ?? '~/.gloss');\n}\n\nexport function globalServerFile(): string {\n return path.join(globalStateDir(), 'server.json');\n}\n\nexport function globalLogDir(): string {\n return path.join(globalStateDir(), 'logs');\n}\n\nexport function globalServerLogFile(): string {\n return path.join(globalLogDir(), 'server.log');\n}\n\nexport function globalReviewsDir(): string {\n return path.join(globalStateDir(), 'reviews');\n}\n\nexport function globalReviewDir(reviewId: string): string {\n return path.join(globalReviewsDir(), reviewId);\n}\n\nexport function globalReviewMetaFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'meta.json');\n}\n\nexport function globalReviewDiffFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'diff.json');\n}\n\nexport function globalReviewFeedbackFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'feedback.json');\n}\n\nexport function globalReviewMarkdownFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'feedback.md');\n}\n\nexport function globalReviewResolvedFile(reviewId: string): string {\n return path.join(globalReviewDir(reviewId), 'resolved.json');\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n","{\n \"name\": \"getgloss\",\n \"version\": \"0.5.0\",\n \"description\": \"Local browser-based diff review for coding-agent loops.\",\n \"type\": \"module\",\n \"packageManager\": \"pnpm@10.33.2\",\n \"bin\": {\n \"getgloss\": \"./dist/cli/index.js\",\n \"gloss\": \"./dist/cli/index.js\"\n },\n \"files\": [\n \"dist\",\n \"skill\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"pnpm build:web && pnpm build:node\",\n \"build:web\": \"vite build\",\n \"build:node\": \"tsup\",\n \"check\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"prepack\": \"pnpm build\",\n \"dev:web\": \"vite --host 127.0.0.1\",\n \"setup\": \"tsx scripts/dev-cli.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"engines\": {\n \"node\": \">=20\"\n },\n \"dependencies\": {\n \"@hono/node-server\": \"^1.14.4\",\n \"@pierre/diffs\": \"^1.2.1\",\n \"@tailwindcss/vite\": \"^4.1.7\",\n \"commander\": \"^14.0.0\",\n \"execa\": \"^9.5.3\",\n \"get-port\": \"^7.1.0\",\n \"hono\": \"^4.7.10\",\n \"lucide-react\": \"^1.16.0\",\n \"open\": \"^10.1.2\",\n \"react\": \"^19.1.0\",\n \"react-dom\": \"^19.1.0\",\n \"ulid\": \"^3.0.0\",\n \"zustand\": \"^5.0.5\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"^2.0.6\",\n \"@types/node\": \"^24.0.1\",\n \"@types/react\": \"^19.1.6\",\n \"@types/react-dom\": \"^19.1.5\",\n \"@vitejs/plugin-react\": \"^4.5.2\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.3\",\n \"typescript\": \"^5.8.3\",\n \"vite\": \"^6.3.5\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"diff\",\n \"review\",\n \"coding-agents\"\n ],\n \"author\": \"Raj Joshi\",\n \"license\": \"MIT\",\n \"homepage\": \"https://getgloss.dev\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/iamrajjoshi/gloss.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/iamrajjoshi/gloss/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Context } from 'hono';\nimport { Hono } from 'hono';\nimport { streamSSE } from 'hono/streaming';\nimport { globalReviewDir, packageVersion } from '../shared/paths';\nimport type { Comment, DiffPayload, ReviewEvent } from '../shared/types';\nimport { reviewStore } from './store';\n\nconst webRoot = fileURLToPath(new URL('../web', import.meta.url));\nconst eventStreamHeartbeatMs = 15_000;\n\nconst mimeTypes: Record<string, string> = {\n '.css': 'text/css; charset=utf-8',\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json; charset=utf-8',\n '.png': 'image/png',\n '.sh': 'text/x-shellscript; charset=utf-8',\n '.svg': 'image/svg+xml'\n};\n\nexport function createApp(origin: string): Hono {\n const app = new Hono();\n\n app.get('/api/health', async (c) => {\n const reviews = await reviewStore.list();\n return c.json({\n ok: true,\n version: packageVersion,\n activeReviews: reviews.filter((review) => review.status === 'pending').length\n });\n });\n\n app.get('/api/reviews', async (c) => c.json({ reviews: await reviewStore.list() }));\n\n app.post('/api/reviews', async (c) => {\n const diff = (await c.req.json()) as DiffPayload;\n const record = await reviewStore.create(diff);\n return c.json({ meta: record.meta, url: `${origin}/review/${record.meta.id}` }, 201);\n });\n\n app.get('/api/reviews/:id', async (c) => {\n const record = await reviewStore.get(c.req.param('id'));\n if (!record) {\n return c.json({ error: 'review not found' }, 404);\n }\n return c.json(record);\n });\n\n app.get('/api/reviews/:id/feedback', async (c) => {\n const feedback = await reviewStore.feedback(c.req.param('id'));\n if (!feedback) {\n return c.json({ error: 'feedback not found' }, 404);\n }\n return c.json(feedback);\n });\n\n app.get('/api/reviews/:id/events', async (c) => {\n const id = c.req.param('id');\n const record = await reviewStore.get(id);\n if (!record) {\n return c.json({ error: 'review not found' }, 404);\n }\n\n return streamSSE(c, async (stream) => {\n let closed = false;\n let pending: Promise<unknown> = Promise.resolve();\n let cleanup: (() => void) | null = null;\n let close: (() => void) | null = null;\n const closedPromise = new Promise<void>((resolve) => {\n close = () => {\n if (closed) {\n return;\n }\n closed = true;\n cleanup?.();\n resolve();\n };\n });\n const send = (event: ReviewEvent) => {\n pending = pending\n .then(() => stream.writeSSE({ data: JSON.stringify(event) }))\n .then(() => {\n if (event.type === 'review.cancelled') {\n close?.();\n }\n });\n void pending.catch(() => close?.());\n };\n const unsubscribe = reviewStore.subscribe(id, send);\n const heartbeat = setInterval(() => {\n pending = pending.then(() => stream.write(`: keep-alive ${Date.now()}\\n\\n`));\n void pending.catch(() => close?.());\n }, eventStreamHeartbeatMs);\n cleanup = () => {\n clearInterval(heartbeat);\n unsubscribe();\n };\n stream.onAbort(() => close?.());\n\n send({ type: 'review.opened', reviewId: id });\n if (\n (record.meta.status === 'submitted' || record.meta.status === 'resolved') &&\n record.feedback\n ) {\n send({\n type: 'review.submitted',\n reviewId: id,\n counts: {\n files: new Set(record.feedback.comments.map((comment) => comment.filePath)).size,\n comments: record.feedback.comments.length\n }\n });\n }\n await closedPromise;\n });\n });\n\n app.post('/api/reviews/:id/submit', async (c) => {\n const id = c.req.param('id');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'pending') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be submitted` }, 409);\n }\n const body = (await c.req.json()) as { comments: Comment[] };\n const { record, feedbackPath, markdownPath } = await reviewStore.submit(\n id,\n body.comments ?? []\n );\n return c.json({\n reviewId: id,\n url: `${origin}/review/${id}`,\n files: record.diff.files.length,\n comments: body.comments?.length ?? 0,\n artifactDir: record.meta.artifactDir,\n feedbackPath,\n markdownPath\n });\n });\n\n app.post('/api/reviews/:id/resolved', async (c) => {\n const id = c.req.param('id');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback) {\n return c.json({ error: 'submitted feedback not found' }, 409);\n }\n const body = (await c.req.json().catch(() => ({}))) as { summary?: string };\n return c.json(await reviewStore.markResolved(id, body.summary));\n });\n\n app.post('/api/reviews/:id/comments/:commentId/resolved', async (c) => {\n const id = c.req.param('id');\n const commentId = c.req.param('commentId');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback?.comments.some((comment) => comment.id === commentId)) {\n return c.json({ error: 'comment not found' }, 404);\n }\n const body = (await c.req.json().catch(() => ({}))) as { summary?: string };\n return c.json(await reviewStore.resolveComment(id, commentId, body.summary));\n });\n\n app.delete('/api/reviews/:id/comments/:commentId/resolved', async (c) => {\n const id = c.req.param('id');\n const commentId = c.req.param('commentId');\n const existing = await reviewStore.get(id);\n if (!existing) {\n return c.json({ error: 'review not found' }, 404);\n }\n if (existing.meta.status !== 'submitted' && existing.meta.status !== 'resolved') {\n return c.json({ error: `review is ${existing.meta.status} and cannot be resolved` }, 409);\n }\n if (!existing.feedback?.comments.some((comment) => comment.id === commentId)) {\n return c.json({ error: 'comment not found' }, 404);\n }\n return c.json(await reviewStore.reopenComment(id, commentId));\n });\n\n app.get('/logo.svg', serveRootFile('logo.svg', mimeTypes['.svg']));\n app.get('/logo-mark.svg', serveRootFile('logo-mark.svg', mimeTypes['.svg']));\n app.get('/og.png', serveRootFile('og.png', mimeTypes['.png']));\n app.get('/install.sh', serveRootFile('install.sh', mimeTypes['.sh']));\n app.get('/setup.md', serveRootFile('setup.md', 'text/markdown; charset=utf-8'));\n app.get('/prompt.md', serveRootFile('prompt.md', 'text/markdown; charset=utf-8'));\n app.get('/assets/*', serveAsset);\n app.get('/setup', serveIndex);\n app.get('/setup/', serveIndex);\n app.get('/review/:id', serveIndex);\n app.get('/', serveIndex);\n\n return app;\n}\n\nasync function serveAsset(c: Context) {\n const requestPath = new URL(c.req.url).pathname.replace(/^\\/assets\\//, '');\n const normalized = path.normalize(requestPath).replace(/^(\\.\\.(\\/|\\\\|$))+/, '');\n const assetPath = path.join(webRoot, 'assets', normalized);\n try {\n const body = await readFile(assetPath);\n return new Response(body, {\n headers: {\n 'content-type': mimeTypes[path.extname(assetPath)] ?? 'application/octet-stream'\n }\n });\n } catch {\n return new Response('Not found', { status: 404 });\n }\n}\n\nasync function serveIndex() {\n try {\n const body = await readFile(path.join(webRoot, 'index.html'));\n return new Response(body, {\n headers: { 'content-type': 'text/html; charset=utf-8' }\n });\n } catch {\n return new Response('Gloss web assets are missing. Run pnpm build.', { status: 500 });\n }\n}\n\nfunction serveRootFile(fileName: string, contentType: string) {\n return async () => {\n try {\n const body = await readFile(path.join(webRoot, fileName));\n return new Response(body, {\n headers: { 'content-type': contentType }\n });\n } catch {\n return new Response(`${fileName} is missing. Run pnpm build.`, { status: 404 });\n }\n };\n}\n\nexport function getReviewArtifactDir(_cwd: string, reviewId: string): string {\n return globalReviewDir(reviewId);\n}\n","import type { Dirent } from 'node:fs';\nimport { readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { ulid } from 'ulid';\nimport { serializeFeedbackMarkdown } from '../shared/markdown';\nimport {\n ensureDir,\n globalReviewDiffFile,\n globalReviewDir,\n globalReviewFeedbackFile,\n globalReviewMarkdownFile,\n globalReviewMetaFile,\n globalReviewResolvedFile,\n globalReviewsDir\n} from '../shared/paths';\nimport type {\n Comment,\n DiffPayload,\n FeedbackBundle,\n ResolutionBundle,\n ResolvedComment,\n ResolveResult,\n ReviewEvent,\n ReviewMeta,\n ReviewRecord,\n ReviewUpdateReason\n} from '../shared/types';\n\ntype Listener = (event: ReviewEvent) => void;\n\nexport class ReviewStore {\n private readonly reviews = new Map<string, ReviewRecord>();\n private readonly listeners = new Map<string, Set<Listener>>();\n\n async create(diff: DiffPayload): Promise<ReviewRecord> {\n const id = ulid();\n const createdAt = new Date().toISOString();\n const meta: ReviewMeta = {\n id,\n cwd: diff.cwd,\n base: diff.base,\n branch: diff.branch,\n status: 'pending',\n createdAt,\n artifactDir: globalReviewDir(id)\n };\n const record: ReviewRecord = { meta, diff };\n this.reviews.set(id, record);\n await this.persistInitial(record);\n this.emit({ type: 'review.opened', reviewId: id });\n return record;\n }\n\n async list(): Promise<ReviewMeta[]> {\n await this.loadAllReviews();\n return [...this.reviews.values()]\n .map((record) => record.meta)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n }\n\n async get(id: string): Promise<ReviewRecord | null> {\n return this.reviews.get(id) ?? (await this.loadKnownReview(id));\n }\n\n async submit(\n id: string,\n comments: Comment[]\n ): Promise<{ record: ReviewRecord; feedbackPath: string; markdownPath: string }> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n if (record.meta.status !== 'pending') {\n throw new Error(`Review ${id} is ${record.meta.status} and cannot be submitted`);\n }\n const timestamp = new Date().toISOString();\n const feedback: FeedbackBundle = {\n version: 1,\n reviewId: id,\n timestamp,\n base: record.diff.base,\n branch: record.diff.branch,\n comments: [...comments].sort(\n (a, b) =>\n a.filePath.localeCompare(b.filePath) ||\n a.startLine - b.startLine ||\n a.endLine - b.endLine ||\n a.side.localeCompare(b.side)\n )\n };\n record.feedback = feedback;\n record.meta = { ...record.meta, status: 'submitted', submittedAt: timestamp };\n this.reviews.set(id, record);\n\n const artifactDir = globalReviewDir(id);\n const feedbackPath = globalReviewFeedbackFile(id);\n const markdownPath = globalReviewMarkdownFile(id);\n record.meta = {\n ...record.meta,\n artifactDir,\n feedbackPath,\n markdownPath\n };\n await ensureDir(artifactDir);\n await Promise.all([\n writeFile(globalReviewMetaFile(id), `${JSON.stringify(record.meta, null, 2)}\\n`),\n writeFile(feedbackPath, `${JSON.stringify(feedback, null, 2)}\\n`),\n writeFile(markdownPath, serializeFeedbackMarkdown(feedback))\n ]);\n\n this.emit({\n type: 'review.submitted',\n reviewId: id,\n counts: {\n files: new Set(feedback.comments.map((comment) => comment.filePath)).size,\n comments: feedback.comments.length\n }\n });\n return { record, feedbackPath, markdownPath };\n }\n\n async feedback(id: string): Promise<FeedbackBundle | null> {\n const record = await this.get(id);\n return record?.feedback ?? null;\n }\n\n async markResolved(id: string, summary?: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n const resolvedAt = new Date().toISOString();\n const existingById = new Map(\n (record.resolution?.comments ?? []).map((comment) => [comment.commentId, comment])\n );\n const comments = this.sortResolvedComments(\n (record.feedback?.comments ?? []).map((comment) => ({\n ...existingById.get(comment.id),\n commentId: comment.id,\n status: 'resolved' as const,\n resolvedAt: existingById.get(comment.id)?.resolvedAt ?? resolvedAt\n })),\n record\n );\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: 'resolved',\n summary: summary ?? record.resolution?.summary ?? null,\n resolvedAt,\n comments\n };\n record.meta = { ...record.meta, status: 'resolved', resolvedAt };\n return this.persistResolution(record, resolution, 'review-resolved');\n }\n\n async resolveComment(id: string, commentId: string, summary?: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n this.assertCommentExists(record, commentId);\n\n const resolvedAt = new Date().toISOString();\n const previous = record.resolution?.comments.find((comment) => comment.commentId === commentId);\n const nextSummary = summary ?? previous?.summary;\n const nextComment: ResolvedComment = {\n commentId,\n status: 'resolved',\n ...(nextSummary ? { summary: nextSummary } : {}),\n resolvedAt\n };\n const comments = this.sortResolvedComments(\n [\n ...(record.resolution?.comments ?? []).filter((comment) => comment.commentId !== commentId),\n nextComment\n ],\n record\n );\n const counts = this.resolutionCounts(record, comments);\n const fullyResolved = counts.total === counts.resolved;\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: fullyResolved ? 'resolved' : 'partial',\n summary: fullyResolved ? (record.resolution?.summary ?? null) : null,\n resolvedAt: fullyResolved ? resolvedAt : null,\n comments\n };\n record.meta = fullyResolved\n ? { ...record.meta, status: 'resolved', resolvedAt }\n : { ...record.meta, status: 'submitted', resolvedAt: undefined };\n return this.persistResolution(record, resolution, 'comment-resolved');\n }\n\n async reopenComment(id: string, commentId: string): Promise<ResolveResult> {\n const record = await this.get(id);\n if (!record) {\n throw new Error(`Review ${id} not found`);\n }\n this.assertResolvable(record, id);\n this.assertCommentExists(record, commentId);\n\n const comments = this.sortResolvedComments(\n (record.resolution?.comments ?? []).filter((comment) => comment.commentId !== commentId),\n record\n );\n const counts = this.resolutionCounts(record, comments);\n const fullyResolved = counts.total > 0 && counts.total === counts.resolved;\n const resolvedAt = fullyResolved ? new Date().toISOString() : null;\n const resolution: ResolutionBundle = {\n reviewId: id,\n status: fullyResolved ? 'resolved' : 'partial',\n summary: fullyResolved ? (record.resolution?.summary ?? null) : null,\n resolvedAt,\n comments\n };\n record.meta = fullyResolved\n ? { ...record.meta, status: 'resolved', resolvedAt: resolvedAt ?? undefined }\n : { ...record.meta, status: 'submitted', resolvedAt: undefined };\n return this.persistResolution(record, resolution, 'comment-reopened');\n }\n\n subscribe(reviewId: string, listener: Listener): () => void {\n const listeners = this.listeners.get(reviewId) ?? new Set<Listener>();\n listeners.add(listener);\n this.listeners.set(reviewId, listeners);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.listeners.delete(reviewId);\n }\n };\n }\n\n private emit(event: ReviewEvent): void {\n for (const listener of this.listeners.get(event.reviewId) ?? []) {\n listener(event);\n }\n }\n\n private async persistInitial(record: ReviewRecord): Promise<void> {\n const dir = globalReviewDir(record.meta.id);\n await ensureDir(dir);\n await Promise.all([\n writeFile(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}\\n`),\n writeFile(globalReviewDiffFile(record.meta.id), `${JSON.stringify(record.diff, null, 2)}\\n`)\n ]);\n }\n\n private async loadKnownReview(id: string): Promise<ReviewRecord | null> {\n const existing = this.reviews.get(id);\n if (existing) {\n return existing;\n }\n\n return this.loadReview(id);\n }\n\n private async loadAllReviews(): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(globalReviewsDir(), { withFileTypes: true });\n } catch {\n return;\n }\n\n await Promise.all(\n entries.filter((entry) => entry.isDirectory()).map((entry) => this.loadReview(entry.name))\n );\n }\n\n private async loadReview(id: string): Promise<ReviewRecord | null> {\n try {\n const [metaRaw, diffRaw] = await Promise.all([\n readFile(globalReviewMetaFile(id), 'utf8'),\n readFile(globalReviewDiffFile(id), 'utf8')\n ]);\n const meta = JSON.parse(metaRaw) as ReviewMeta;\n const diff = JSON.parse(diffRaw) as DiffPayload;\n let feedback: FeedbackBundle | undefined;\n let resolution: ResolutionBundle | undefined;\n try {\n feedback = JSON.parse(\n await readFile(globalReviewFeedbackFile(id), 'utf8')\n ) as FeedbackBundle;\n } catch {\n feedback = undefined;\n }\n try {\n resolution = JSON.parse(\n await readFile(globalReviewResolvedFile(id), 'utf8')\n ) as ResolutionBundle;\n } catch {\n resolution = undefined;\n }\n\n const record: ReviewRecord = {\n meta: {\n ...meta,\n artifactDir: meta.artifactDir ?? globalReviewDir(id),\n feedbackPath: meta.feedbackPath ?? (feedback ? globalReviewFeedbackFile(id) : undefined),\n markdownPath: meta.markdownPath ?? (feedback ? globalReviewMarkdownFile(id) : undefined)\n },\n diff,\n feedback,\n resolution\n };\n this.reviews.set(id, record);\n return record;\n } catch {\n return null;\n }\n }\n\n private assertResolvable(\n record: ReviewRecord,\n id: string\n ): asserts record is ReviewRecord & {\n feedback: FeedbackBundle;\n } {\n if (record.meta.status !== 'submitted' && record.meta.status !== 'resolved') {\n throw new Error(`Review ${id} is ${record.meta.status} and cannot be resolved`);\n }\n if (!record.feedback) {\n throw new Error(`Review ${id} has no submitted feedback`);\n }\n }\n\n private assertCommentExists(\n record: ReviewRecord & { feedback: FeedbackBundle },\n commentId: string\n ): void {\n if (!record.feedback.comments.some((comment) => comment.id === commentId)) {\n throw new Error(`Comment ${commentId} not found`);\n }\n }\n\n private async persistResolution(\n record: ReviewRecord & { feedback: FeedbackBundle },\n resolution: ResolutionBundle,\n reason: ReviewUpdateReason\n ): Promise<ResolveResult> {\n record.resolution = resolution;\n this.reviews.set(record.meta.id, record);\n const resolvedPath = globalReviewResolvedFile(record.meta.id);\n await ensureDir(globalReviewDir(record.meta.id));\n await Promise.all([\n writeFile(resolvedPath, `${JSON.stringify(resolution, null, 2)}\\n`),\n writeFile(globalReviewMetaFile(record.meta.id), `${JSON.stringify(record.meta, null, 2)}\\n`)\n ]);\n const result: ResolveResult = {\n ok: true,\n reviewId: record.meta.id,\n status: record.meta.status,\n resolutionStatus: resolution.status,\n comments: this.resolutionCounts(record, resolution.comments),\n path: resolvedPath,\n resolution\n };\n this.emit({\n type: 'review.updated',\n reviewId: record.meta.id,\n reason,\n status: result.status,\n resolutionStatus: result.resolutionStatus,\n counts: result.comments\n });\n return result;\n }\n\n private sortResolvedComments(\n comments: ResolvedComment[],\n record: ReviewRecord & { feedback: FeedbackBundle }\n ): ResolvedComment[] {\n const feedbackIndex = new Map(\n record.feedback.comments.map((comment, index) => [comment.id, index] as const)\n );\n return comments\n .filter((comment) => feedbackIndex.has(comment.commentId))\n .sort(\n (a, b) =>\n (feedbackIndex.get(a.commentId) ?? Number.MAX_SAFE_INTEGER) -\n (feedbackIndex.get(b.commentId) ?? Number.MAX_SAFE_INTEGER)\n );\n }\n\n private resolutionCounts(\n record: ReviewRecord & { feedback: FeedbackBundle },\n comments: ResolvedComment[]\n ): { total: number; resolved: number; open: number } {\n const total = record.feedback.comments.length;\n const resolvedIds = new Set(comments.map((comment) => comment.commentId));\n const resolved = record.feedback.comments.filter((comment) =>\n resolvedIds.has(comment.id)\n ).length;\n return {\n total,\n resolved,\n open: total - resolved\n };\n }\n}\n\nexport const reviewStore = new ReviewStore();\n\nexport async function removeReviewArtifacts(_cwd: string, id: string): Promise<void> {\n await rm(globalReviewDir(id), { force: true, recursive: true });\n}\n","import type { Comment, FeedbackBundle } from './types';\n\nfunction formatLineRange(comment: Comment): string {\n const prefix = comment.side;\n if (comment.startLine === comment.endLine) {\n return `${prefix}${comment.startLine}`;\n }\n return `${prefix}${comment.startLine}-${prefix}${comment.endLine}`;\n}\n\nfunction fenceFor(snippet: string): string {\n let fence = '```';\n while (snippet.includes(fence)) {\n fence += '`';\n }\n return fence;\n}\n\nfunction languageForPath(filePath: string): string {\n const ext = filePath.split('.').pop()?.toLowerCase();\n const map: Record<string, string> = {\n cjs: 'js',\n css: 'css',\n go: 'go',\n html: 'html',\n js: 'js',\n json: 'json',\n jsx: 'jsx',\n md: 'markdown',\n mjs: 'js',\n py: 'python',\n rb: 'ruby',\n rs: 'rust',\n sh: 'bash',\n swift: 'swift',\n ts: 'ts',\n tsx: 'tsx',\n yaml: 'yaml',\n yml: 'yaml'\n };\n return ext ? (map[ext] ?? ext) : '';\n}\n\nfunction languageForSnippet(filePath: string, snippet: string): string {\n const lines = snippet.split('\\n').filter((line) => line.length > 0);\n const looksLikeUnifiedDiff =\n lines.length > 0 &&\n lines.some((line) => line.startsWith('+') || line.startsWith('-')) &&\n lines.every((line) => line.startsWith('+') || line.startsWith('-') || line.startsWith(' '));\n return looksLikeUnifiedDiff ? 'diff' : languageForPath(filePath);\n}\n\nfunction byFileThenLine(a: Comment, b: Comment): number {\n return (\n a.filePath.localeCompare(b.filePath) ||\n a.startLine - b.startLine ||\n a.endLine - b.endLine ||\n a.side.localeCompare(b.side)\n );\n}\n\nexport function serializeFeedbackMarkdown(bundle: FeedbackBundle): string {\n const comments = [...bundle.comments].sort(byFileThenLine);\n const files = [...new Set(comments.map((comment) => comment.filePath))];\n const lines: string[] = [\n `# Gloss feedback - ${bundle.timestamp}`,\n `Review: ${bundle.reviewId}`,\n `Base: ${bundle.base.ref} (${bundle.base.sha.slice(0, 7)}) Branch: ${bundle.branch ?? '(detached)'}`,\n `Files: ${files.length} Comments: ${comments.length}`,\n ''\n ];\n\n for (const filePath of files) {\n lines.push(`## ${filePath}`, '');\n for (const comment of comments.filter((item) => item.filePath === filePath)) {\n const snippet = comment.originalSnippet.trimEnd();\n const firstSnippetLine = snippet.split('\\n').find((line) => line.trim().length > 0);\n const heading =\n comment.startLine === comment.endLine && firstSnippetLine\n ? `### ${formatLineRange(comment)} - \\`${firstSnippetLine.trim().slice(0, 80)}\\``\n : `### ${formatLineRange(comment)}`;\n lines.push(heading, comment.body.trim(), '');\n if (snippet) {\n const fence = fenceFor(snippet);\n lines.push(`${fence}${languageForSnippet(comment.filePath, snippet)}`, snippet, fence, '');\n }\n }\n }\n\n return `${lines.join('\\n').trimEnd()}\\n`;\n}\n"],"mappings":"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","names":["readFile","path","fileURLToPath","readFile","rm","writeFile","writeFile","readFile","fileURLToPath","origin","path","readFile"]}
|