diffprism 0.38.0 → 0.40.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 +68 -16
- package/dist/{chunk-ITPHDFOS.js → chunk-2HMWCMPU.js} +5 -1
- package/dist/{chunk-UYZ3A2PB.js → chunk-FCDO5LRP.js} +8 -4
- package/dist/{demo-JH5YOKTZ.js → demo-75VOJNCC.js} +2 -2
- package/dist/mcp-server.js +49 -48
- package/package.json +1 -1
- package/ui-dist/assets/index-C5mYpcCe.js +320 -0
- package/ui-dist/assets/index-DRh_waHF.css +1 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-DqG6b0pP.js +0 -325
- package/ui-dist/assets/index-oPcRh7AZ.css +0 -1
package/dist/bin.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
} from "./chunk-6J6PSBL2.js";
|
|
12
12
|
import {
|
|
13
13
|
demo
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-FCDO5LRP.js";
|
|
15
15
|
import {
|
|
16
16
|
ensureServer,
|
|
17
17
|
isServerAlive,
|
|
18
18
|
readServerFile,
|
|
19
19
|
startGlobalServer,
|
|
20
20
|
submitReviewToServer
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-2HMWCMPU.js";
|
|
22
22
|
import "./chunk-QGWYCEJN.js";
|
|
23
23
|
import "./chunk-DHCVZGHE.js";
|
|
24
24
|
import "./chunk-JSBRDJBE.js";
|
|
@@ -89,7 +89,7 @@ async function reviewPrFlow(pr, flags) {
|
|
|
89
89
|
diffRef: `PR #${number}`
|
|
90
90
|
});
|
|
91
91
|
console.log(JSON.stringify(result, null, 2));
|
|
92
|
-
if (flags.postToGithub || result.decision !== "dismissed" && await promptPostToGithub()) {
|
|
92
|
+
if (result && (flags.postToGithub || result.decision !== "dismissed" && await promptPostToGithub())) {
|
|
93
93
|
console.log("Posting review to GitHub...");
|
|
94
94
|
const posted = await submitGitHubReview(client, owner, repo, number, result);
|
|
95
95
|
if (posted) {
|
|
@@ -134,29 +134,81 @@ description: Open current code changes in DiffPrism's browser-based review UI fo
|
|
|
134
134
|
|
|
135
135
|
# DiffPrism Review
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
You have 9 DiffPrism MCP tools available. Use them proactively \u2014 don't wait for the user to ask.
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
- \`title\`: Brief summary of the changes
|
|
141
|
-
- \`reasoning\`: Your reasoning about the implementation decisions
|
|
142
|
-
- \`post_to_github\`: Set to \`true\` to post the review back to GitHub (only for PR refs)
|
|
139
|
+
## Workflow 1: Self-Review Before Human Review
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
After making multi-file changes, **proactively self-review before requesting human review**:
|
|
145
142
|
|
|
143
|
+
1. Run \`mcp__diffprism__analyze_diff\` with \`diff_ref: "working-copy"\`
|
|
144
|
+
2. Check the response for:
|
|
145
|
+
- **Pattern flags** \u2014 console.logs left in, TODO comments, security issues (hardcoded secrets, SQL injection, XSS)
|
|
146
|
+
- **Test coverage gaps** \u2014 new code paths without corresponding tests
|
|
147
|
+
- **High complexity scores** \u2014 functions or files with elevated complexity
|
|
148
|
+
3. **Fix any issues found** \u2014 remove debug logs, add missing tests, simplify complex code
|
|
149
|
+
4. Only then open a human review with \`mcp__diffprism__open_review\`
|
|
150
|
+
|
|
151
|
+
This should happen automatically after significant changes. You don't need the user to ask.
|
|
152
|
+
|
|
153
|
+
## Workflow 2: Annotated Human Review
|
|
154
|
+
|
|
155
|
+
When opening a review, help the reviewer by flagging what matters:
|
|
156
|
+
|
|
157
|
+
1. Call \`mcp__diffprism__open_review\` with:
|
|
158
|
+
- \`diff_ref\`: \`"working-copy"\` (or what the user specified, e.g. \`"staged"\`, \`"HEAD~3..HEAD"\`)
|
|
159
|
+
- \`title\`: Brief summary of the changes
|
|
160
|
+
- \`reasoning\`: Your reasoning about implementation decisions
|
|
161
|
+
- \`annotations\`: Array of inline findings to pre-populate the review (see tool schema)
|
|
162
|
+
2. Use annotations to flag:
|
|
163
|
+
- Areas of uncertainty ("I chose approach X over Y because...")
|
|
164
|
+
- Security-sensitive changes
|
|
165
|
+
- Performance implications
|
|
166
|
+
- Anything the reviewer should look at closely
|
|
167
|
+
3. After opening, use \`mcp__diffprism__flag_for_attention\` to highlight files that need careful review (e.g. auth logic, data migrations, public API changes)
|
|
168
|
+
4. Use \`mcp__diffprism__add_annotation\` to post additional findings about specific lines if you discover issues while the review is open
|
|
169
|
+
|
|
170
|
+
Handle the review result:
|
|
146
171
|
- **\`approved\`** \u2014 Proceed with the task.
|
|
147
172
|
- **\`changes_requested\`** \u2014 Read comments, make fixes, offer to re-review.
|
|
148
173
|
- If \`postReviewAction\` is \`"commit"\` \u2014 commit the changes.
|
|
149
174
|
- If \`postReviewAction\` is \`"commit_and_pr"\` \u2014 commit and open a PR.
|
|
150
175
|
|
|
151
|
-
##
|
|
176
|
+
## Workflow 3: PR Review
|
|
177
|
+
|
|
178
|
+
To review a GitHub pull request:
|
|
179
|
+
|
|
180
|
+
1. Call \`mcp__diffprism__review_pr\` with \`pr: "owner/repo#123"\` or a full GitHub PR URL
|
|
181
|
+
2. Set \`post_to_github: true\` to post the review back to GitHub after the human submits
|
|
182
|
+
3. The tool fetches the PR diff, runs analysis, and opens the review UI
|
|
183
|
+
|
|
184
|
+
## Tool Reference
|
|
185
|
+
|
|
186
|
+
### Review Lifecycle
|
|
187
|
+
| Tool | Purpose |
|
|
188
|
+
|------|---------|
|
|
189
|
+
| \`open_review\` | Open browser review UI for local changes. Blocks until submitted. |
|
|
190
|
+
| \`review_pr\` | Open browser review UI for a GitHub PR. Blocks until submitted. |
|
|
191
|
+
| \`get_review_result\` | Fetch result from a previous review (advanced \u2014 \`open_review\` already returns it). |
|
|
192
|
+
| \`update_review_context\` | Push updated reasoning/description to a running review session. |
|
|
193
|
+
|
|
194
|
+
### Headless Analysis
|
|
195
|
+
| Tool | Purpose |
|
|
196
|
+
|------|---------|
|
|
197
|
+
| \`analyze_diff\` | Returns analysis JSON (patterns, complexity, test gaps) without opening a browser. |
|
|
198
|
+
| \`get_diff\` | Returns structured diff JSON (file-level and hunk-level changes). |
|
|
152
199
|
|
|
153
|
-
|
|
154
|
-
|
|
200
|
+
### Annotation & Flagging
|
|
201
|
+
| Tool | Purpose |
|
|
202
|
+
|------|---------|
|
|
203
|
+
| \`add_annotation\` | Post a finding/suggestion/question on a specific line in a running review. |
|
|
204
|
+
| \`flag_for_attention\` | Mark files for human attention with warning annotations. |
|
|
205
|
+
| \`get_review_state\` | Get current state of a review session including all annotations. |
|
|
155
206
|
|
|
156
207
|
## Rules
|
|
157
208
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
209
|
+
- **Self-review is proactive** \u2014 run \`analyze_diff\` after significant changes without being asked.
|
|
210
|
+
- **Human review requires explicit request** \u2014 only open \`open_review\` when the user asks (\`/review\`, "review my changes", or as part of a defined workflow like PR creation).
|
|
211
|
+
- **Annotate generously** \u2014 the more context you provide in annotations, the faster the reviewer can make decisions.
|
|
160
212
|
`;
|
|
161
213
|
|
|
162
214
|
// cli/src/commands/setup.ts
|
|
@@ -327,7 +379,7 @@ async function setupInteractive(flags) {
|
|
|
327
379
|
}
|
|
328
380
|
async function runDemo(dev) {
|
|
329
381
|
console.log("");
|
|
330
|
-
const { demo: demo2 } = await import("./demo-
|
|
382
|
+
const { demo: demo2 } = await import("./demo-75VOJNCC.js");
|
|
331
383
|
await demo2({ dev });
|
|
332
384
|
}
|
|
333
385
|
async function setupBatch(flags) {
|
|
@@ -756,7 +808,7 @@ async function serverStop() {
|
|
|
756
808
|
|
|
757
809
|
// cli/src/index.ts
|
|
758
810
|
var program = new Command();
|
|
759
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
811
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.40.0" : "0.0.0-dev");
|
|
760
812
|
program.command("demo").description("Open a sample review to see DiffPrism in action").option("--dev", "Use Vite dev server").action(demo);
|
|
761
813
|
program.command("review [ref]").description("Open a browser-based diff review (local git ref or GitHub PR ref like owner/repo#123)").option("--staged", "Review staged changes").option("--unstaged", "Review unstaged changes").option("-t, --title <title>", "Review title").option("--reasoning <text>", "Agent reasoning about the changes").option("--dev", "Use Vite dev server with HMR instead of static files").option("--post-to-github", "Automatically post review back to GitHub without prompting").action(review);
|
|
762
814
|
program.command("review-pr <pr>", { hidden: true }).action((pr, flags) => review(pr, flags));
|
|
@@ -230,8 +230,11 @@ async function submitReviewToServer(serverInfo, diffRef, options = {}) {
|
|
|
230
230
|
);
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
|
-
const pollIntervalMs = 2e3;
|
|
234
233
|
const maxWaitMs = options.timeoutMs ?? 6e5;
|
|
234
|
+
if (maxWaitMs <= 0) {
|
|
235
|
+
return { result: null, sessionId };
|
|
236
|
+
}
|
|
237
|
+
const pollIntervalMs = 2e3;
|
|
235
238
|
const start = Date.now();
|
|
236
239
|
while (Date.now() - start < maxWaitMs) {
|
|
237
240
|
const resultResponse = await fetch(
|
|
@@ -493,6 +496,7 @@ function toSummary(session) {
|
|
|
493
496
|
projectPath: session.projectPath,
|
|
494
497
|
branch: payload.metadata.currentBranch,
|
|
495
498
|
title: payload.metadata.title,
|
|
499
|
+
reasoning: payload.metadata.reasoning,
|
|
496
500
|
fileCount,
|
|
497
501
|
additions,
|
|
498
502
|
deletions,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureServer,
|
|
3
3
|
submitReviewToServer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2HMWCMPU.js";
|
|
5
5
|
import {
|
|
6
6
|
parseDiff
|
|
7
7
|
} from "./chunk-QGWYCEJN.js";
|
|
@@ -210,10 +210,14 @@ async function demo(flags) {
|
|
|
210
210
|
projectPath: "demo",
|
|
211
211
|
diffRef: "demo"
|
|
212
212
|
});
|
|
213
|
-
|
|
213
|
+
if (result) {
|
|
214
|
+
console.log(`
|
|
214
215
|
Review submitted: ${result.decision}`);
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
if (result.comments.length > 0) {
|
|
217
|
+
console.log(`${result.comments.length} comment(s)`);
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
console.log("\nReview opened in browser.");
|
|
217
221
|
}
|
|
218
222
|
console.log("\nNext steps:");
|
|
219
223
|
console.log(" Run `npx diffprism setup` to configure for Claude Code");
|
package/dist/mcp-server.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ensureServer,
|
|
13
13
|
isServerAlive,
|
|
14
14
|
submitReviewToServer
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-2HMWCMPU.js";
|
|
16
16
|
import {
|
|
17
17
|
getDiff
|
|
18
18
|
} from "./chunk-QGWYCEJN.js";
|
|
@@ -38,12 +38,29 @@ async function handleLocalReview(diffRef, options) {
|
|
|
38
38
|
reasoning: options.reasoning,
|
|
39
39
|
cwd: process.cwd(),
|
|
40
40
|
annotations: options.annotations,
|
|
41
|
-
diffRef
|
|
41
|
+
diffRef,
|
|
42
|
+
timeoutMs: options.timeoutMs ?? 0
|
|
42
43
|
}
|
|
43
44
|
);
|
|
45
|
+
if (result) {
|
|
46
|
+
return {
|
|
47
|
+
mcpResult: {
|
|
48
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
49
|
+
},
|
|
50
|
+
sessionId,
|
|
51
|
+
serverInfo
|
|
52
|
+
};
|
|
53
|
+
}
|
|
44
54
|
return {
|
|
45
55
|
mcpResult: {
|
|
46
|
-
content: [{
|
|
56
|
+
content: [{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: JSON.stringify({
|
|
59
|
+
status: "session_created",
|
|
60
|
+
sessionId,
|
|
61
|
+
message: "Review session opened in DiffPrism dashboard. Use get_review_result to check for a decision."
|
|
62
|
+
}, null, 2)
|
|
63
|
+
}]
|
|
47
64
|
},
|
|
48
65
|
sessionId,
|
|
49
66
|
serverInfo
|
|
@@ -84,9 +101,27 @@ async function handlePrReview(pr, options) {
|
|
|
84
101
|
{
|
|
85
102
|
injectedPayload: payload,
|
|
86
103
|
projectPath: `github:${owner}/${repo}`,
|
|
87
|
-
diffRef: `PR #${number}
|
|
104
|
+
diffRef: `PR #${number}`,
|
|
105
|
+
timeoutMs: options.timeoutMs ?? 0
|
|
88
106
|
}
|
|
89
107
|
);
|
|
108
|
+
if (!result) {
|
|
109
|
+
return {
|
|
110
|
+
mcpResult: {
|
|
111
|
+
content: [{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: JSON.stringify({
|
|
114
|
+
status: "session_created",
|
|
115
|
+
sessionId,
|
|
116
|
+
pr: `${owner}/${repo}#${number}`,
|
|
117
|
+
message: "Review session opened in DiffPrism dashboard. Use get_review_result to check for a decision."
|
|
118
|
+
}, null, 2)
|
|
119
|
+
}]
|
|
120
|
+
},
|
|
121
|
+
sessionId,
|
|
122
|
+
serverInfo
|
|
123
|
+
};
|
|
124
|
+
}
|
|
90
125
|
if ((options.post_to_github || result.postToGithub) && result.decision !== "dismissed") {
|
|
91
126
|
const posted = await submitGitHubReview(client, owner, repo, number, result);
|
|
92
127
|
if (posted) {
|
|
@@ -117,19 +152,20 @@ async function handlePrReview(pr, options) {
|
|
|
117
152
|
async function startMcpServer() {
|
|
118
153
|
const server = new McpServer({
|
|
119
154
|
name: "diffprism",
|
|
120
|
-
version: true ? "0.
|
|
155
|
+
version: true ? "0.40.0" : "0.0.0-dev"
|
|
121
156
|
});
|
|
122
157
|
server.tool(
|
|
123
158
|
"open_review",
|
|
124
|
-
"Open a
|
|
159
|
+
"Open a review session in the DiffPrism dashboard for local git changes or a GitHub pull request. Returns immediately with the session ID after registering the session. Use `get_review_result` with `wait: true` when you need the reviewer's decision before proceeding.",
|
|
125
160
|
{
|
|
126
161
|
diff_ref: z.string().describe(
|
|
127
162
|
'Git diff reference: "staged", "unstaged", "working-copy" (staged+unstaged grouped), a ref range like "HEAD~3..HEAD", or a GitHub PR ref like "owner/repo#123" or a GitHub PR URL'
|
|
128
163
|
),
|
|
129
164
|
title: z.string().optional().describe("Title for the review"),
|
|
130
165
|
description: z.string().optional().describe("Description of the changes"),
|
|
131
|
-
reasoning: z.string().optional().describe("
|
|
166
|
+
reasoning: z.string().optional().describe("Summarize what you were trying to accomplish in this session in plain English. This is displayed as the session subtitle in the DiffPrism dashboard and is the primary way users identify sessions at a glance. Always populate this."),
|
|
132
167
|
post_to_github: z.boolean().optional().describe("Post the review back to GitHub after submission (only for PR refs, default: false)"),
|
|
168
|
+
timeout_ms: z.number().optional().describe("How long to wait for a review decision (ms). Defaults to 0 (non-blocking, returns immediately after session creation). Set to a positive value to poll for a result up to that duration before returning."),
|
|
133
169
|
annotations: z.array(
|
|
134
170
|
z.object({
|
|
135
171
|
file: z.string().describe("File path within the diff to annotate"),
|
|
@@ -151,7 +187,7 @@ async function startMcpServer() {
|
|
|
151
187
|
})
|
|
152
188
|
).optional().describe("Initial annotations to attach to the review")
|
|
153
189
|
},
|
|
154
|
-
async ({ diff_ref, title, description, reasoning, post_to_github, annotations }) => {
|
|
190
|
+
async ({ diff_ref, title, description, reasoning, post_to_github, timeout_ms, annotations }) => {
|
|
155
191
|
try {
|
|
156
192
|
let mcpResult;
|
|
157
193
|
let sessionId;
|
|
@@ -160,14 +196,16 @@ async function startMcpServer() {
|
|
|
160
196
|
({ mcpResult, sessionId, serverInfo } = await handlePrReview(diff_ref, {
|
|
161
197
|
title,
|
|
162
198
|
reasoning,
|
|
163
|
-
post_to_github
|
|
199
|
+
post_to_github,
|
|
200
|
+
timeoutMs: timeout_ms
|
|
164
201
|
}));
|
|
165
202
|
} else {
|
|
166
203
|
({ mcpResult, sessionId, serverInfo } = await handleLocalReview(diff_ref, {
|
|
167
204
|
title,
|
|
168
205
|
description,
|
|
169
206
|
reasoning,
|
|
170
|
-
annotations
|
|
207
|
+
annotations,
|
|
208
|
+
timeoutMs: timeout_ms
|
|
171
209
|
}));
|
|
172
210
|
}
|
|
173
211
|
if (sessionId) {
|
|
@@ -249,7 +287,7 @@ async function startMcpServer() {
|
|
|
249
287
|
);
|
|
250
288
|
server.tool(
|
|
251
289
|
"get_review_result",
|
|
252
|
-
"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. Use wait=true to block until a result is available
|
|
290
|
+
"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. Use wait=true to block until a result is available \u2014 this is the standard way to wait for a reviewer's decision after calling open_review.",
|
|
253
291
|
{
|
|
254
292
|
wait: z.boolean().optional().describe("If true, poll until a review result is available (blocks up to timeout)"),
|
|
255
293
|
timeout: z.number().optional().describe("Max wait time in seconds when wait=true (default: 300, max: 600)")
|
|
@@ -705,43 +743,6 @@ async function startMcpServer() {
|
|
|
705
743
|
}
|
|
706
744
|
}
|
|
707
745
|
);
|
|
708
|
-
server.tool(
|
|
709
|
-
"review_pr",
|
|
710
|
-
"Alias for open_review with a GitHub PR ref. Prefer using open_review with a PR ref in diff_ref instead.",
|
|
711
|
-
{
|
|
712
|
-
pr: z.string().describe(
|
|
713
|
-
'GitHub PR reference: "owner/repo#123" or "https://github.com/owner/repo/pull/123"'
|
|
714
|
-
),
|
|
715
|
-
title: z.string().optional().describe("Override review title"),
|
|
716
|
-
reasoning: z.string().optional().describe("Agent reasoning about the PR changes"),
|
|
717
|
-
post_to_github: z.boolean().optional().describe("Post the review back to GitHub after submission (default: false)")
|
|
718
|
-
},
|
|
719
|
-
async ({ pr, title, reasoning, post_to_github }) => {
|
|
720
|
-
try {
|
|
721
|
-
const { mcpResult, sessionId, serverInfo } = await handlePrReview(pr, {
|
|
722
|
-
title,
|
|
723
|
-
reasoning,
|
|
724
|
-
post_to_github
|
|
725
|
-
});
|
|
726
|
-
if (sessionId) {
|
|
727
|
-
lastGlobalSessionId = sessionId;
|
|
728
|
-
lastGlobalServerInfo = serverInfo;
|
|
729
|
-
}
|
|
730
|
-
return mcpResult;
|
|
731
|
-
} catch (err) {
|
|
732
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
733
|
-
return {
|
|
734
|
-
content: [
|
|
735
|
-
{
|
|
736
|
-
type: "text",
|
|
737
|
-
text: `Error: ${message}`
|
|
738
|
-
}
|
|
739
|
-
],
|
|
740
|
-
isError: true
|
|
741
|
-
};
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
);
|
|
745
746
|
const transport = new StdioServerTransport();
|
|
746
747
|
await server.connect(transport);
|
|
747
748
|
}
|