diffprism 0.31.1 → 0.33.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 +122 -7
- package/dist/chunk-ZLIUNVTW.js +6498 -0
- package/dist/mcp-server.js +184 -6
- package/package.json +1 -1
- package/ui-dist/assets/{index-Cwwzm9DK.js → index-D_thqcUo.js} +78 -73
- package/ui-dist/assets/index-w55XbIEb.css +1 -0
- package/ui-dist/index.html +2 -2
- package/dist/chunk-OJ723D6Z.js +0 -2589
- package/ui-dist/assets/index-r-H-ptFw.css +0 -1
package/dist/mcp-server.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
analyze,
|
|
3
3
|
consumeReviewResult,
|
|
4
|
+
createGitHubClient,
|
|
4
5
|
detectWorktree,
|
|
6
|
+
fetchPullRequest,
|
|
7
|
+
fetchPullRequestDiff,
|
|
5
8
|
getCurrentBranch,
|
|
6
9
|
getDiff,
|
|
7
10
|
isServerAlive,
|
|
11
|
+
normalizePr,
|
|
12
|
+
parsePrRef,
|
|
8
13
|
readReviewResult,
|
|
9
14
|
readWatchFile,
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
resolveGitHubToken,
|
|
16
|
+
startReview,
|
|
17
|
+
submitGitHubReview
|
|
18
|
+
} from "./chunk-ZLIUNVTW.js";
|
|
12
19
|
|
|
13
20
|
// packages/mcp-server/src/index.ts
|
|
14
21
|
import fs from "fs";
|
|
@@ -63,6 +70,29 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
|
63
70
|
const { sessionId } = await createResponse.json();
|
|
64
71
|
lastGlobalSessionId = sessionId;
|
|
65
72
|
lastGlobalServerInfo = serverInfo;
|
|
73
|
+
if (options.annotations?.length) {
|
|
74
|
+
for (const ann of options.annotations) {
|
|
75
|
+
await fetch(
|
|
76
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`,
|
|
77
|
+
{
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
file: ann.file,
|
|
82
|
+
line: ann.line,
|
|
83
|
+
body: ann.body,
|
|
84
|
+
type: ann.type,
|
|
85
|
+
confidence: ann.confidence ?? 1,
|
|
86
|
+
category: ann.category ?? "other",
|
|
87
|
+
source: {
|
|
88
|
+
agent: ann.source_agent ?? "unknown",
|
|
89
|
+
tool: "open_review"
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
66
96
|
const pollIntervalMs = 2e3;
|
|
67
97
|
const maxWaitMs = 600 * 1e3;
|
|
68
98
|
const start = Date.now();
|
|
@@ -83,7 +113,7 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
|
83
113
|
async function startMcpServer() {
|
|
84
114
|
const server = new McpServer({
|
|
85
115
|
name: "diffprism",
|
|
86
|
-
version: true ? "0.
|
|
116
|
+
version: true ? "0.33.0" : "0.0.0-dev"
|
|
87
117
|
});
|
|
88
118
|
server.tool(
|
|
89
119
|
"open_review",
|
|
@@ -94,9 +124,29 @@ async function startMcpServer() {
|
|
|
94
124
|
),
|
|
95
125
|
title: z.string().optional().describe("Title for the review"),
|
|
96
126
|
description: z.string().optional().describe("Description of the changes"),
|
|
97
|
-
reasoning: z.string().optional().describe("Agent reasoning about why these changes were made")
|
|
127
|
+
reasoning: z.string().optional().describe("Agent reasoning about why these changes were made"),
|
|
128
|
+
annotations: z.array(
|
|
129
|
+
z.object({
|
|
130
|
+
file: z.string().describe("File path within the diff to annotate"),
|
|
131
|
+
line: z.number().describe("Line number to annotate"),
|
|
132
|
+
body: z.string().describe("The annotation text"),
|
|
133
|
+
type: z.enum(["finding", "suggestion", "question", "warning"]).describe("Type of annotation"),
|
|
134
|
+
confidence: z.number().min(0).max(1).optional().describe("Confidence in the finding (0-1, defaults to 1)"),
|
|
135
|
+
category: z.enum([
|
|
136
|
+
"security",
|
|
137
|
+
"performance",
|
|
138
|
+
"convention",
|
|
139
|
+
"correctness",
|
|
140
|
+
"complexity",
|
|
141
|
+
"test-coverage",
|
|
142
|
+
"documentation",
|
|
143
|
+
"other"
|
|
144
|
+
]).optional().describe("Category of the finding (defaults to 'other')"),
|
|
145
|
+
source_agent: z.string().optional().describe("Agent identifier (e.g., 'security-reviewer')")
|
|
146
|
+
})
|
|
147
|
+
).optional().describe("Initial annotations to attach to the review")
|
|
98
148
|
},
|
|
99
|
-
async ({ diff_ref, title, description, reasoning }) => {
|
|
149
|
+
async ({ diff_ref, title, description, reasoning, annotations }) => {
|
|
100
150
|
try {
|
|
101
151
|
const serverInfo = await isServerAlive();
|
|
102
152
|
if (serverInfo) {
|
|
@@ -104,7 +154,8 @@ async function startMcpServer() {
|
|
|
104
154
|
title,
|
|
105
155
|
description,
|
|
106
156
|
reasoning,
|
|
107
|
-
cwd: process.cwd()
|
|
157
|
+
cwd: process.cwd(),
|
|
158
|
+
annotations
|
|
108
159
|
});
|
|
109
160
|
return {
|
|
110
161
|
content: [
|
|
@@ -728,6 +779,133 @@ async function startMcpServer() {
|
|
|
728
779
|
}
|
|
729
780
|
}
|
|
730
781
|
);
|
|
782
|
+
server.tool(
|
|
783
|
+
"review_pr",
|
|
784
|
+
"Open a browser-based code review for a GitHub pull request. Fetches the PR diff, runs DiffPrism analysis, and opens the review UI. Blocks until the engineer submits their review decision. Optionally posts the review back to GitHub.",
|
|
785
|
+
{
|
|
786
|
+
pr: z.string().describe(
|
|
787
|
+
'GitHub PR reference: "owner/repo#123" or "https://github.com/owner/repo/pull/123"'
|
|
788
|
+
),
|
|
789
|
+
title: z.string().optional().describe("Override review title"),
|
|
790
|
+
reasoning: z.string().optional().describe("Agent reasoning about the PR changes"),
|
|
791
|
+
post_to_github: z.boolean().optional().describe("Post the review back to GitHub after submission (default: false)")
|
|
792
|
+
},
|
|
793
|
+
async ({ pr, title, reasoning, post_to_github }) => {
|
|
794
|
+
try {
|
|
795
|
+
const token = resolveGitHubToken();
|
|
796
|
+
const { owner, repo, number } = parsePrRef(pr);
|
|
797
|
+
const client = createGitHubClient(token);
|
|
798
|
+
const [prMetadata, rawDiff] = await Promise.all([
|
|
799
|
+
fetchPullRequest(client, owner, repo, number),
|
|
800
|
+
fetchPullRequestDiff(client, owner, repo, number)
|
|
801
|
+
]);
|
|
802
|
+
if (!rawDiff.trim()) {
|
|
803
|
+
return {
|
|
804
|
+
content: [
|
|
805
|
+
{
|
|
806
|
+
type: "text",
|
|
807
|
+
text: JSON.stringify({
|
|
808
|
+
decision: "approved",
|
|
809
|
+
comments: [],
|
|
810
|
+
summary: "PR has no changes to review."
|
|
811
|
+
}, null, 2)
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
const { payload } = normalizePr(rawDiff, prMetadata, { title, reasoning });
|
|
817
|
+
const serverInfo = await isServerAlive();
|
|
818
|
+
let result;
|
|
819
|
+
if (serverInfo) {
|
|
820
|
+
const createResponse = await fetch(
|
|
821
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews`,
|
|
822
|
+
{
|
|
823
|
+
method: "POST",
|
|
824
|
+
headers: { "Content-Type": "application/json" },
|
|
825
|
+
body: JSON.stringify({
|
|
826
|
+
payload,
|
|
827
|
+
projectPath: `github:${owner}/${repo}`,
|
|
828
|
+
diffRef: `PR #${number}`
|
|
829
|
+
})
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
if (!createResponse.ok) {
|
|
833
|
+
throw new Error(`Global server returned ${createResponse.status}`);
|
|
834
|
+
}
|
|
835
|
+
const { sessionId } = await createResponse.json();
|
|
836
|
+
lastGlobalSessionId = sessionId;
|
|
837
|
+
lastGlobalServerInfo = serverInfo;
|
|
838
|
+
const pollIntervalMs = 2e3;
|
|
839
|
+
const maxWaitMs = 600 * 1e3;
|
|
840
|
+
const start = Date.now();
|
|
841
|
+
while (Date.now() - start < maxWaitMs) {
|
|
842
|
+
const resultResponse = await fetch(
|
|
843
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/result`
|
|
844
|
+
);
|
|
845
|
+
if (resultResponse.ok) {
|
|
846
|
+
const data = await resultResponse.json();
|
|
847
|
+
if (data.result) {
|
|
848
|
+
result = data.result;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
853
|
+
}
|
|
854
|
+
result ??= { decision: "dismissed", comments: [], summary: "Review timed out." };
|
|
855
|
+
} else {
|
|
856
|
+
const isDev = fs.existsSync(
|
|
857
|
+
path.join(process.cwd(), "packages", "ui", "src", "App.tsx")
|
|
858
|
+
);
|
|
859
|
+
result = await startReview({
|
|
860
|
+
diffRef: `PR #${number}`,
|
|
861
|
+
title: payload.metadata.title,
|
|
862
|
+
description: payload.metadata.description,
|
|
863
|
+
reasoning: payload.metadata.reasoning,
|
|
864
|
+
cwd: process.cwd(),
|
|
865
|
+
silent: true,
|
|
866
|
+
dev: isDev,
|
|
867
|
+
injectedPayload: payload
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
if (post_to_github && result.decision !== "dismissed") {
|
|
871
|
+
const posted = await submitGitHubReview(client, owner, repo, number, result);
|
|
872
|
+
if (posted) {
|
|
873
|
+
return {
|
|
874
|
+
content: [
|
|
875
|
+
{
|
|
876
|
+
type: "text",
|
|
877
|
+
text: JSON.stringify({
|
|
878
|
+
...result,
|
|
879
|
+
githubReviewId: posted.reviewId,
|
|
880
|
+
githubReviewUrl: `${prMetadata.url}#pullrequestreview-${posted.reviewId}`
|
|
881
|
+
}, null, 2)
|
|
882
|
+
}
|
|
883
|
+
]
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return {
|
|
888
|
+
content: [
|
|
889
|
+
{
|
|
890
|
+
type: "text",
|
|
891
|
+
text: JSON.stringify(result, null, 2)
|
|
892
|
+
}
|
|
893
|
+
]
|
|
894
|
+
};
|
|
895
|
+
} catch (err) {
|
|
896
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
897
|
+
return {
|
|
898
|
+
content: [
|
|
899
|
+
{
|
|
900
|
+
type: "text",
|
|
901
|
+
text: `Error: ${message}`
|
|
902
|
+
}
|
|
903
|
+
],
|
|
904
|
+
isError: true
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
);
|
|
731
909
|
const transport = new StdioServerTransport();
|
|
732
910
|
await server.connect(transport);
|
|
733
911
|
}
|