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.
@@ -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
- startReview
11
- } from "./chunk-OJ723D6Z.js";
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.31.1" : "0.0.0-dev"
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffprism",
3
- "version": "0.31.1",
3
+ "version": "0.33.0",
4
4
  "type": "module",
5
5
  "description": "Local-first code review tool for agent-generated code changes",
6
6
  "bin": {