diffprism 0.14.0 → 0.16.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/bin.js +91 -2
- package/dist/{chunk-TVXIMP3G.js → chunk-4WN4FIY4.js} +450 -19
- package/dist/mcp-server.js +173 -13
- package/package.json +1 -1
- package/ui-dist/assets/{index-CKJwY3F0.js → index-CNL0CLAo.js} +67 -62
- package/ui-dist/assets/index-ymFelmyA.css +1 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-D39rVNSs.css +0 -1
package/dist/mcp-server.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
analyze,
|
|
2
3
|
consumeReviewResult,
|
|
4
|
+
getCurrentBranch,
|
|
5
|
+
getDiff,
|
|
6
|
+
isServerAlive,
|
|
3
7
|
readReviewResult,
|
|
4
8
|
readWatchFile,
|
|
5
9
|
startReview
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4WN4FIY4.js";
|
|
7
11
|
|
|
8
12
|
// packages/mcp-server/src/index.ts
|
|
9
13
|
import fs from "fs";
|
|
@@ -11,10 +15,68 @@ import path from "path";
|
|
|
11
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
12
16
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
17
|
import { z } from "zod";
|
|
18
|
+
var lastGlobalSessionId = null;
|
|
19
|
+
var lastGlobalServerInfo = null;
|
|
20
|
+
async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
21
|
+
const cwd = options.cwd ?? process.cwd();
|
|
22
|
+
const { diffSet, rawDiff } = getDiff(diffRef, { cwd });
|
|
23
|
+
const currentBranch = getCurrentBranch({ cwd });
|
|
24
|
+
if (diffSet.files.length === 0) {
|
|
25
|
+
return {
|
|
26
|
+
decision: "approved",
|
|
27
|
+
comments: [],
|
|
28
|
+
summary: "No changes to review."
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const briefing = analyze(diffSet);
|
|
32
|
+
const payload = {
|
|
33
|
+
reviewId: "",
|
|
34
|
+
// Server assigns the real ID
|
|
35
|
+
diffSet,
|
|
36
|
+
rawDiff,
|
|
37
|
+
briefing,
|
|
38
|
+
metadata: {
|
|
39
|
+
title: options.title,
|
|
40
|
+
description: options.description,
|
|
41
|
+
reasoning: options.reasoning,
|
|
42
|
+
currentBranch
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const createResponse = await fetch(
|
|
46
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews`,
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: { "Content-Type": "application/json" },
|
|
50
|
+
body: JSON.stringify({ payload, projectPath: cwd })
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
if (!createResponse.ok) {
|
|
54
|
+
throw new Error(`Global server returned ${createResponse.status} on create`);
|
|
55
|
+
}
|
|
56
|
+
const { sessionId } = await createResponse.json();
|
|
57
|
+
lastGlobalSessionId = sessionId;
|
|
58
|
+
lastGlobalServerInfo = serverInfo;
|
|
59
|
+
const pollIntervalMs = 2e3;
|
|
60
|
+
const maxWaitMs = 600 * 1e3;
|
|
61
|
+
const start = Date.now();
|
|
62
|
+
while (Date.now() - start < maxWaitMs) {
|
|
63
|
+
const resultResponse = await fetch(
|
|
64
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/result`
|
|
65
|
+
);
|
|
66
|
+
if (resultResponse.ok) {
|
|
67
|
+
const data = await resultResponse.json();
|
|
68
|
+
if (data.result) {
|
|
69
|
+
return data.result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
73
|
+
}
|
|
74
|
+
throw new Error("Review timed out waiting for submission.");
|
|
75
|
+
}
|
|
14
76
|
async function startMcpServer() {
|
|
15
77
|
const server = new McpServer({
|
|
16
78
|
name: "diffprism",
|
|
17
|
-
version: true ? "0.
|
|
79
|
+
version: true ? "0.16.0" : "0.0.0-dev"
|
|
18
80
|
});
|
|
19
81
|
server.tool(
|
|
20
82
|
"open_review",
|
|
@@ -29,6 +91,23 @@ async function startMcpServer() {
|
|
|
29
91
|
},
|
|
30
92
|
async ({ diff_ref, title, description, reasoning }) => {
|
|
31
93
|
try {
|
|
94
|
+
const serverInfo = await isServerAlive();
|
|
95
|
+
if (serverInfo) {
|
|
96
|
+
const result2 = await reviewViaGlobalServer(serverInfo, diff_ref, {
|
|
97
|
+
title,
|
|
98
|
+
description,
|
|
99
|
+
reasoning,
|
|
100
|
+
cwd: process.cwd()
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: JSON.stringify(result2, null, 2)
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
32
111
|
const isDev = fs.existsSync(
|
|
33
112
|
path.join(process.cwd(), "packages", "ui", "src", "App.tsx")
|
|
34
113
|
);
|
|
@@ -39,7 +118,6 @@ async function startMcpServer() {
|
|
|
39
118
|
reasoning,
|
|
40
119
|
cwd: process.cwd(),
|
|
41
120
|
silent: true,
|
|
42
|
-
// Suppress stdout — MCP uses stdio
|
|
43
121
|
dev: isDev
|
|
44
122
|
});
|
|
45
123
|
return {
|
|
@@ -66,7 +144,7 @@ async function startMcpServer() {
|
|
|
66
144
|
);
|
|
67
145
|
server.tool(
|
|
68
146
|
"update_review_context",
|
|
69
|
-
"Push reasoning/context to a running DiffPrism
|
|
147
|
+
"Push reasoning/context to a running DiffPrism review session. Non-blocking \u2014 returns immediately. Use this when `diffprism watch` or `diffprism server` is running to update the review UI with agent reasoning without opening a new review.",
|
|
70
148
|
{
|
|
71
149
|
reasoning: z.string().optional().describe("Agent reasoning about the current changes"),
|
|
72
150
|
title: z.string().optional().describe("Updated title for the review"),
|
|
@@ -74,21 +152,44 @@ async function startMcpServer() {
|
|
|
74
152
|
},
|
|
75
153
|
async ({ reasoning, title, description }) => {
|
|
76
154
|
try {
|
|
155
|
+
const payload = {};
|
|
156
|
+
if (reasoning !== void 0) payload.reasoning = reasoning;
|
|
157
|
+
if (title !== void 0) payload.title = title;
|
|
158
|
+
if (description !== void 0) payload.description = description;
|
|
159
|
+
if (lastGlobalSessionId && lastGlobalServerInfo) {
|
|
160
|
+
const serverInfo = await isServerAlive();
|
|
161
|
+
if (serverInfo) {
|
|
162
|
+
const response2 = await fetch(
|
|
163
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/context`,
|
|
164
|
+
{
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify(payload)
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
if (response2.ok) {
|
|
171
|
+
return {
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: "Context updated in DiffPrism global server session."
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
77
182
|
const watchInfo = readWatchFile();
|
|
78
183
|
if (!watchInfo) {
|
|
79
184
|
return {
|
|
80
185
|
content: [
|
|
81
186
|
{
|
|
82
187
|
type: "text",
|
|
83
|
-
text: "No DiffPrism
|
|
188
|
+
text: "No DiffPrism session is running. Start one with `diffprism watch` or `diffprism server`."
|
|
84
189
|
}
|
|
85
190
|
]
|
|
86
191
|
};
|
|
87
192
|
}
|
|
88
|
-
const payload = {};
|
|
89
|
-
if (reasoning !== void 0) payload.reasoning = reasoning;
|
|
90
|
-
if (title !== void 0) payload.title = title;
|
|
91
|
-
if (description !== void 0) payload.description = description;
|
|
92
193
|
const response = await fetch(
|
|
93
194
|
`http://localhost:${watchInfo.wsPort}/api/context`,
|
|
94
195
|
{
|
|
@@ -114,7 +215,7 @@ async function startMcpServer() {
|
|
|
114
215
|
content: [
|
|
115
216
|
{
|
|
116
217
|
type: "text",
|
|
117
|
-
text: `Error updating
|
|
218
|
+
text: `Error updating review context: ${message}`
|
|
118
219
|
}
|
|
119
220
|
],
|
|
120
221
|
isError: true
|
|
@@ -124,16 +225,75 @@ async function startMcpServer() {
|
|
|
124
225
|
);
|
|
125
226
|
server.tool(
|
|
126
227
|
"get_review_result",
|
|
127
|
-
"Fetch the most recent review result from a DiffPrism
|
|
228
|
+
"Fetch the most recent review result from a DiffPrism session. Returns the reviewer's decision and comments if a review has been submitted, or a message indicating no pending result. The result is marked as consumed after retrieval so it won't be returned again. Use wait=true to block until a result is available (recommended after pushing context to a watch session).",
|
|
128
229
|
{
|
|
129
230
|
wait: z.boolean().optional().describe("If true, poll until a review result is available (blocks up to timeout)"),
|
|
130
231
|
timeout: z.number().optional().describe("Max wait time in seconds when wait=true (default: 300, max: 600)")
|
|
131
232
|
},
|
|
132
233
|
async ({ wait, timeout }) => {
|
|
133
234
|
try {
|
|
235
|
+
const maxWaitMs = Math.min(timeout ?? 300, 600) * 1e3;
|
|
236
|
+
const pollIntervalMs = 2e3;
|
|
237
|
+
if (lastGlobalSessionId && lastGlobalServerInfo) {
|
|
238
|
+
const serverInfo = await isServerAlive();
|
|
239
|
+
if (serverInfo) {
|
|
240
|
+
if (wait) {
|
|
241
|
+
const start = Date.now();
|
|
242
|
+
while (Date.now() - start < maxWaitMs) {
|
|
243
|
+
const response2 = await fetch(
|
|
244
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/result`
|
|
245
|
+
);
|
|
246
|
+
if (response2.ok) {
|
|
247
|
+
const data2 = await response2.json();
|
|
248
|
+
if (data2.result) {
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: JSON.stringify(data2.result, null, 2)
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: "No review result received within timeout."
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const response = await fetch(
|
|
271
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${lastGlobalSessionId}/result`
|
|
272
|
+
);
|
|
273
|
+
if (response.ok) {
|
|
274
|
+
const data2 = await response.json();
|
|
275
|
+
if (data2.result) {
|
|
276
|
+
return {
|
|
277
|
+
content: [
|
|
278
|
+
{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: JSON.stringify(data2.result, null, 2)
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
content: [
|
|
288
|
+
{
|
|
289
|
+
type: "text",
|
|
290
|
+
text: "No pending review result."
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
134
296
|
if (wait) {
|
|
135
|
-
const maxWaitMs = Math.min(timeout ?? 300, 600) * 1e3;
|
|
136
|
-
const pollIntervalMs = 2e3;
|
|
137
297
|
const start = Date.now();
|
|
138
298
|
while (Date.now() - start < maxWaitMs) {
|
|
139
299
|
const data2 = readReviewResult();
|