diffprism 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/bin.js +8 -5
- package/dist/{chunk-F6O5YP3I.js → chunk-35YVBTBH.js} +92 -13
- package/dist/{chunk-FONA4U6B.js → chunk-PJYDCLBP.js} +1 -1
- package/dist/{demo-VNWYEDZ3.js → demo-WIP5MSCH.js} +2 -2
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/ui-dist/assets/{index-CLWtGr9m.js → index-BDR4t-Zx.js} +76 -76
- package/ui-dist/index.html +1 -1
package/README.md
CHANGED
|
@@ -218,6 +218,26 @@ pnpm run build
|
|
|
218
218
|
pnpm cli review --staged # Run CLI from source
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
### Dogfooding a checkout
|
|
222
|
+
|
|
223
|
+
To try unreleased changes in another project without publishing, point your global `diffprism` at a checkout:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
git clone https://github.com/CodeJonesW/diffprism.git diffprism-dogfood
|
|
227
|
+
cd diffprism-dogfood && pnpm install && pnpm build && npm link
|
|
228
|
+
diffprism --version # 1.2.0 (dev build — /path/to/diffprism-dogfood)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Then, for each change you want to try:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
git fetch && git checkout <branch> && git pull && pnpm build
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The next `diffprism` command — a review, the commit gate — replaces the running server with the new build, unless a review is open in it. The dashboard picks up a rebuilt UI on reload. Go back to the release with `npm install -g diffprism@latest`.
|
|
238
|
+
|
|
239
|
+
A server is only ever replaced by a **newer** build. Claude Code keeps `diffprism serve` running on the build it started with until you restart it, so restart Claude Code to put its MCP tools on the new build too.
|
|
240
|
+
|
|
221
241
|
### Project Structure
|
|
222
242
|
|
|
223
243
|
```
|
package/dist/bin.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-EPU4F7WT.js";
|
|
6
6
|
import {
|
|
7
7
|
demo
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PJYDCLBP.js";
|
|
9
9
|
import {
|
|
10
10
|
COMMIT_GATE_DIFF_REF,
|
|
11
11
|
DEFAULT_DIFF_REF,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
recordError,
|
|
27
27
|
startGlobalServer,
|
|
28
28
|
submitReviewToServer
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-35YVBTBH.js";
|
|
30
30
|
import {
|
|
31
31
|
getDiff
|
|
32
32
|
} from "./chunk-3GMPE2ZR.js";
|
|
@@ -79,6 +79,9 @@ ${REPORT_HINT}`);
|
|
|
79
79
|
}
|
|
80
80
|
const where = data.annotation ? ` on ${data.annotation.file}:${data.annotation.line}` : "";
|
|
81
81
|
console.log(`Replied${where}. It shows in the review now.`);
|
|
82
|
+
console.log(
|
|
83
|
+
"The review is still open. Go back to waiting for the decision now: run the same command that opened it again (`git commit` or `diffprism review`). It returns the decision, or the reviewer's next question. Don't ask them in the terminal."
|
|
84
|
+
);
|
|
82
85
|
}
|
|
83
86
|
function fail(message2) {
|
|
84
87
|
console.error(message2);
|
|
@@ -136,7 +139,7 @@ ${REPORT_HINT}`);
|
|
|
136
139
|
if (err instanceof ReviewerAskedError) {
|
|
137
140
|
printQuestions(err.sessionId, err.threads);
|
|
138
141
|
fail2(
|
|
139
|
-
"Commit blocked: the reviewer asked you something before deciding. Answer each question with the command under it \u2014 change the code too if that's what they asked for \u2014 then run git commit again. The review stays open and the decision still comes."
|
|
142
|
+
"Commit blocked: the reviewer asked you something before deciding. Answer each question with the command under it \u2014 change the code too if that's what they asked for \u2014 then run git commit again straight away to keep waiting. The review stays open, the reviewer may ask more, and the decision still comes. Don't stop to ask them in the terminal."
|
|
140
143
|
);
|
|
141
144
|
return;
|
|
142
145
|
}
|
|
@@ -369,7 +372,7 @@ async function reviewLocalFlow(diffRef, flags) {
|
|
|
369
372
|
if (err instanceof ReviewerAskedError) {
|
|
370
373
|
printQuestions(err.sessionId, err.threads);
|
|
371
374
|
console.error(
|
|
372
|
-
"The reviewer asked something before deciding. Answer each question with the command under it, then run diffprism review again \u2014 the review stays open."
|
|
375
|
+
"The reviewer asked something before deciding. Answer each question with the command under it, then run diffprism review again straight away to keep waiting \u2014 the review stays open, and the reviewer may ask more. Don't stop to ask them in the terminal."
|
|
373
376
|
);
|
|
374
377
|
process.exit(1);
|
|
375
378
|
}
|
|
@@ -716,7 +719,7 @@ async function setupInteractive(flags) {
|
|
|
716
719
|
}
|
|
717
720
|
async function runDemo(dev) {
|
|
718
721
|
console.log("");
|
|
719
|
-
const { demo: demo2 } = await import("./demo-
|
|
722
|
+
const { demo: demo2 } = await import("./demo-WIP5MSCH.js");
|
|
720
723
|
await demo2({ dev });
|
|
721
724
|
}
|
|
722
725
|
async function setupBatch(flags) {
|
|
@@ -79,15 +79,13 @@ async function isServerAlive() {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
// packages/core/src/diff-scope.ts
|
|
83
|
-
var DEFAULT_DIFF_REF = "working-copy";
|
|
84
|
-
var COMMIT_GATE_DIFF_REF = "staged";
|
|
85
|
-
var DIFF_REF_DESCRIPTION = 'Which changes to review. "working-copy" (the default): everything not yet committed, staged and unstaged shown as separate groups. "staged": only what the next commit would contain. "unstaged": only edits not yet staged. Or a ref range such as "HEAD~3..HEAD" or "main..feature".';
|
|
86
|
-
|
|
87
82
|
// packages/core/src/build-info.ts
|
|
88
83
|
import fs2 from "fs";
|
|
89
84
|
import path2 from "path";
|
|
90
85
|
import { fileURLToPath } from "url";
|
|
86
|
+
function builtAt() {
|
|
87
|
+
return true ? 1789610123399 : null;
|
|
88
|
+
}
|
|
91
89
|
function getBuildInfo() {
|
|
92
90
|
let dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
93
91
|
while (dir !== path2.dirname(dir)) {
|
|
@@ -112,6 +110,11 @@ function describeVersion(version) {
|
|
|
112
110
|
return info.dev ? `${version} (dev build \u2014 ${info.root})` : version;
|
|
113
111
|
}
|
|
114
112
|
|
|
113
|
+
// packages/core/src/diff-scope.ts
|
|
114
|
+
var DEFAULT_DIFF_REF = "working-copy";
|
|
115
|
+
var COMMIT_GATE_DIFF_REF = "staged";
|
|
116
|
+
var DIFF_REF_DESCRIPTION = 'Which changes to review. "working-copy" (the default): everything not yet committed, staged and unstaged shown as separate groups. "staged": only what the next commit would contain. "unstaged": only edits not yet staged. Or a ref range such as "HEAD~3..HEAD" or "main..feature".';
|
|
117
|
+
|
|
115
118
|
// packages/core/src/feedback.ts
|
|
116
119
|
import fs3 from "fs";
|
|
117
120
|
import os2 from "os";
|
|
@@ -119,7 +122,7 @@ import path3 from "path";
|
|
|
119
122
|
var ISSUES_NEW_URL = "https://github.com/CodeJonesW/diffprism/issues/new";
|
|
120
123
|
var MAX_ERROR_CHARS = 1500;
|
|
121
124
|
function currentVersion() {
|
|
122
|
-
return true ? "1.
|
|
125
|
+
return true ? "1.3.1" : "0.0.0-dev";
|
|
123
126
|
}
|
|
124
127
|
function describeEnvironment() {
|
|
125
128
|
return {
|
|
@@ -226,7 +229,22 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
226
229
|
async function ensureServer(options = {}) {
|
|
227
230
|
const existing = await isServerAlive();
|
|
228
231
|
if (existing) {
|
|
229
|
-
|
|
232
|
+
const decision = await decideOnRunningServer(existing, builtAt());
|
|
233
|
+
if (decision.action === "keep") {
|
|
234
|
+
return existing;
|
|
235
|
+
}
|
|
236
|
+
if (decision.action === "keep-busy") {
|
|
237
|
+
if (!options.silent) {
|
|
238
|
+
console.error(
|
|
239
|
+
`The DiffPrism server (PID ${existing.pid}) runs an older build, but ${decision.openReviews} review${decision.openReviews === 1 ? " is" : "s are"} open in it, so it stays up. Finish or close ${decision.openReviews === 1 ? "it" : "them"} and the next command starts the new build \u2014 or run \`diffprism server stop\`.`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
return existing;
|
|
243
|
+
}
|
|
244
|
+
if (!options.silent) {
|
|
245
|
+
console.error(`Replacing the DiffPrism server (PID ${existing.pid}): it runs an older build than this command.`);
|
|
246
|
+
}
|
|
247
|
+
await stopServer(existing);
|
|
230
248
|
}
|
|
231
249
|
const spawnArgs = options.spawnCommand ?? buildDefaultSpawnCommand(options);
|
|
232
250
|
const logDir = path4.join(os3.homedir(), ".diffprism");
|
|
@@ -256,6 +274,44 @@ async function ensureServer(options = {}) {
|
|
|
256
274
|
`DiffPrism server failed to start within ${timeoutMs / 1e3}s. Check logs at ${logPath}`
|
|
257
275
|
);
|
|
258
276
|
}
|
|
277
|
+
async function decideOnRunningServer(server, ownBuiltAt) {
|
|
278
|
+
if (ownBuiltAt === null) return { action: "keep" };
|
|
279
|
+
if (server.builtAt !== void 0 && server.builtAt >= ownBuiltAt) return { action: "keep" };
|
|
280
|
+
const response = await fetch(`http://localhost:${server.httpPort}/api/reviews`);
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
throw new Error(`Could not list reviews on the running DiffPrism server: it returned ${response.status}`);
|
|
283
|
+
}
|
|
284
|
+
const { sessions: sessions2 } = await response.json();
|
|
285
|
+
const openReviews = sessions2.filter((s) => s.status === "pending" || s.status === "in_review").length;
|
|
286
|
+
return openReviews > 0 ? { action: "keep-busy", openReviews } : { action: "replace" };
|
|
287
|
+
}
|
|
288
|
+
async function stopServer(server, timeoutMs = 5e3) {
|
|
289
|
+
try {
|
|
290
|
+
process.kill(server.pid, "SIGTERM");
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
const start = Date.now();
|
|
294
|
+
while (Date.now() - start < timeoutMs) {
|
|
295
|
+
if (!isProcessRunning(server.pid)) {
|
|
296
|
+
if (readServerFile()?.pid === server.pid) {
|
|
297
|
+
removeServerFile();
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
302
|
+
}
|
|
303
|
+
throw new Error(
|
|
304
|
+
`The old DiffPrism server (PID ${server.pid}) did not stop within ${timeoutMs / 1e3}s. Stop it with \`diffprism server stop\` and try again.`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
function isProcessRunning(pid) {
|
|
308
|
+
try {
|
|
309
|
+
process.kill(pid, 0);
|
|
310
|
+
return true;
|
|
311
|
+
} catch {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
259
315
|
function buildDefaultSpawnCommand(options) {
|
|
260
316
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
261
317
|
const thisDir = path4.dirname(thisFile);
|
|
@@ -861,6 +917,7 @@ var clientSessions = /* @__PURE__ */ new Map();
|
|
|
861
917
|
var sessionWatchers = /* @__PURE__ */ new Map();
|
|
862
918
|
var watchSchedule = DEFAULT_WATCH_SCHEDULE;
|
|
863
919
|
var reopenBrowserIfNeeded = null;
|
|
920
|
+
var DASHBOARD_RECONNECT_GRACE_MS = 3e3;
|
|
864
921
|
var serverUiUrl = null;
|
|
865
922
|
function toSummary(session) {
|
|
866
923
|
const { payload } = session;
|
|
@@ -1741,6 +1798,8 @@ async function startGlobalServer(options = {}) {
|
|
|
1741
1798
|
const {
|
|
1742
1799
|
httpPort: preferredHttpPort = 24680,
|
|
1743
1800
|
wsPort: preferredWsPort = 24681,
|
|
1801
|
+
uiPort: preferredUiPort = 24682,
|
|
1802
|
+
reconnectGraceMs = DASHBOARD_RECONNECT_GRACE_MS,
|
|
1744
1803
|
silent = false,
|
|
1745
1804
|
dev = false,
|
|
1746
1805
|
pollInterval = DEFAULT_WATCH_SCHEDULE.viewedMs,
|
|
@@ -1763,11 +1822,11 @@ async function startGlobalServer(options = {}) {
|
|
|
1763
1822
|
let uiHttpServer = null;
|
|
1764
1823
|
let viteServer = null;
|
|
1765
1824
|
if (dev) {
|
|
1766
|
-
uiPort = await getPort();
|
|
1825
|
+
uiPort = await getPort({ port: preferredUiPort });
|
|
1767
1826
|
const uiRoot = resolveUiRoot();
|
|
1768
1827
|
viteServer = await startViteDevServer(uiRoot, uiPort, silent);
|
|
1769
1828
|
} else {
|
|
1770
|
-
uiPort = await getPort();
|
|
1829
|
+
uiPort = await getPort({ port: preferredUiPort });
|
|
1771
1830
|
const uiDist = resolveUiDist();
|
|
1772
1831
|
uiHttpServer = await createStaticServer(uiDist, uiPort);
|
|
1773
1832
|
}
|
|
@@ -1913,6 +1972,10 @@ async function startGlobalServer(options = {}) {
|
|
|
1913
1972
|
pid: process.pid,
|
|
1914
1973
|
startedAt: Date.now()
|
|
1915
1974
|
};
|
|
1975
|
+
const serverBuiltAt = builtAt();
|
|
1976
|
+
if (serverBuiltAt !== null) {
|
|
1977
|
+
serverInfo.builtAt = serverBuiltAt;
|
|
1978
|
+
}
|
|
1916
1979
|
writeServerFile(serverInfo);
|
|
1917
1980
|
if (!silent) {
|
|
1918
1981
|
console.log(`
|
|
@@ -1930,9 +1993,23 @@ Waiting for reviews...
|
|
|
1930
1993
|
if (openBrowser) {
|
|
1931
1994
|
await open(uiUrl);
|
|
1932
1995
|
}
|
|
1996
|
+
const serverStartedAt = Date.now();
|
|
1997
|
+
const pendingOpens = /* @__PURE__ */ new Set();
|
|
1933
1998
|
reopenBrowserIfNeeded = () => {
|
|
1934
|
-
|
|
1935
|
-
|
|
1999
|
+
const openIfUnwatched = () => {
|
|
2000
|
+
if (reopenBrowserIfNeeded && !hasConnectedClients()) {
|
|
2001
|
+
open(uiUrl);
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
const wait = serverStartedAt + reconnectGraceMs - Date.now();
|
|
2005
|
+
if (wait > 0) {
|
|
2006
|
+
const timer = setTimeout(() => {
|
|
2007
|
+
pendingOpens.delete(timer);
|
|
2008
|
+
openIfUnwatched();
|
|
2009
|
+
}, wait);
|
|
2010
|
+
pendingOpens.add(timer);
|
|
2011
|
+
} else {
|
|
2012
|
+
openIfUnwatched();
|
|
1936
2013
|
}
|
|
1937
2014
|
};
|
|
1938
2015
|
async function stop() {
|
|
@@ -1947,6 +2024,8 @@ Waiting for reviews...
|
|
|
1947
2024
|
}
|
|
1948
2025
|
clientSessions.clear();
|
|
1949
2026
|
sessions.clear();
|
|
2027
|
+
for (const timer of pendingOpens) clearTimeout(timer);
|
|
2028
|
+
pendingOpens.clear();
|
|
1950
2029
|
reopenBrowserIfNeeded = null;
|
|
1951
2030
|
serverUiUrl = null;
|
|
1952
2031
|
await new Promise((resolve) => {
|
|
@@ -1993,11 +2072,11 @@ function mcpToolPermission(name) {
|
|
|
1993
2072
|
export {
|
|
1994
2073
|
readServerFile,
|
|
1995
2074
|
isServerAlive,
|
|
2075
|
+
getBuildInfo,
|
|
2076
|
+
describeVersion,
|
|
1996
2077
|
DEFAULT_DIFF_REF,
|
|
1997
2078
|
COMMIT_GATE_DIFF_REF,
|
|
1998
2079
|
DIFF_REF_DESCRIPTION,
|
|
1999
|
-
getBuildInfo,
|
|
2000
|
-
describeVersion,
|
|
2001
2080
|
currentVersion,
|
|
2002
2081
|
recordError,
|
|
2003
2082
|
readLastError,
|
package/dist/mcp-server.js
CHANGED