diffprism 0.13.8 → 0.14.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 +110 -78
- package/dist/{chunk-QB2PKDLU.js → chunk-TVXIMP3G.js} +37 -7
- package/dist/mcp-server.js +37 -6
- package/package.json +1 -1
- package/ui-dist/assets/index-CKJwY3F0.js +244 -0
- package/ui-dist/assets/index-D39rVNSs.css +1 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-CEmIfNA9.css +0 -1
- package/ui-dist/assets/index-DiJ_eCA-.js +0 -239
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
readWatchFile,
|
|
4
4
|
startReview,
|
|
5
5
|
startWatch
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TVXIMP3G.js";
|
|
7
7
|
|
|
8
8
|
// cli/src/index.ts
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -18,7 +18,7 @@ async function review(ref, flags) {
|
|
|
18
18
|
} else if (ref) {
|
|
19
19
|
diffRef = ref;
|
|
20
20
|
} else {
|
|
21
|
-
diffRef = "
|
|
21
|
+
diffRef = "working-copy";
|
|
22
22
|
}
|
|
23
23
|
try {
|
|
24
24
|
const result = await startReview({
|
|
@@ -66,57 +66,29 @@ Before opening a new review, check if \`diffprism watch\` is already running. Lo
|
|
|
66
66
|
|
|
67
67
|
- **Do NOT call \`open_review\`** (the browser is already open with live-updating diffs)
|
|
68
68
|
- Instead, call \`mcp__diffprism__update_review_context\` to push your reasoning to the existing watch session
|
|
69
|
-
-
|
|
70
|
-
-
|
|
69
|
+
- Then **immediately** call \`mcp__diffprism__get_review_result\` with \`wait: true\` to block until the developer submits their review
|
|
70
|
+
- Tell the user: "DiffPrism watch is running \u2014 pushed reasoning to the live review. Waiting for your feedback..."
|
|
71
|
+
- When the result comes back, handle it per step 5 below
|
|
72
|
+
- Skip steps 2-4
|
|
71
73
|
|
|
72
|
-
###
|
|
74
|
+
### 2. Load Configuration
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- **\`approved\`** \u2014 Acknowledge approval and continue with your current task.
|
|
77
|
-
- **\`approved_with_comments\`** \u2014 Note the comments, address any actionable feedback.
|
|
78
|
-
- **\`changes_requested\`** \u2014 Read the comments carefully, make the requested changes, then push updated reasoning via \`mcp__diffprism__update_review_context\`.
|
|
79
|
-
|
|
80
|
-
If no pending result, continue normally.
|
|
81
|
-
|
|
82
|
-
### 2. Check for Configuration
|
|
83
|
-
|
|
84
|
-
Look for \`diffprism.config.json\` at the project root. If it exists, read it for preferences:
|
|
76
|
+
Look for \`diffprism.config.json\` at the project root. If it exists, read it for preferences. If it doesn't exist, use defaults silently \u2014 do not prompt or create the file.
|
|
85
77
|
|
|
86
78
|
\`\`\`json
|
|
87
79
|
{
|
|
88
80
|
"reviewTrigger": "ask | before_commit | always",
|
|
89
|
-
"defaultDiffScope": "staged | unstaged |
|
|
81
|
+
"defaultDiffScope": "staged | unstaged | working-copy",
|
|
90
82
|
"includeReasoning": true | false
|
|
91
83
|
}
|
|
92
84
|
\`\`\`
|
|
93
85
|
|
|
94
86
|
**Defaults** (when fields are missing or file doesn't exist):
|
|
95
87
|
- \`reviewTrigger\`: \`"ask"\`
|
|
96
|
-
- \`defaultDiffScope\`: \`"
|
|
88
|
+
- \`defaultDiffScope\`: \`"working-copy"\`
|
|
97
89
|
- \`includeReasoning\`: \`true\`
|
|
98
90
|
|
|
99
|
-
### 3.
|
|
100
|
-
|
|
101
|
-
If \`diffprism.config.json\` does **not** exist, ask the user these questions before proceeding:
|
|
102
|
-
|
|
103
|
-
1. **"When should I open DiffPrism reviews?"**
|
|
104
|
-
- \`"ask"\` \u2014 Only when you explicitly ask (default)
|
|
105
|
-
- \`"before_commit"\` \u2014 Automatically before every commit
|
|
106
|
-
- \`"always"\` \u2014 After every code change
|
|
107
|
-
|
|
108
|
-
2. **"What should the default diff scope be?"**
|
|
109
|
-
- \`"all"\` \u2014 All changes, staged and unstaged (default)
|
|
110
|
-
- \`"staged"\` \u2014 Only staged changes
|
|
111
|
-
- \`"unstaged"\` \u2014 Only unstaged changes
|
|
112
|
-
|
|
113
|
-
3. **"Should I include my reasoning about the changes in reviews?"**
|
|
114
|
-
- Yes (default)
|
|
115
|
-
- No
|
|
116
|
-
|
|
117
|
-
After collecting answers, create \`diffprism.config.json\` at the project root with the user's choices. Then proceed to open the review.
|
|
118
|
-
|
|
119
|
-
### 4. Open the Review
|
|
91
|
+
### 3. Open the Review
|
|
120
92
|
|
|
121
93
|
Call \`mcp__diffprism__open_review\` with:
|
|
122
94
|
|
|
@@ -125,7 +97,7 @@ Call \`mcp__diffprism__open_review\` with:
|
|
|
125
97
|
- \`description\`: A brief description of what changed and why.
|
|
126
98
|
- \`reasoning\`: If \`includeReasoning\` is \`true\`, include your reasoning about the implementation decisions.
|
|
127
99
|
|
|
128
|
-
###
|
|
100
|
+
### 4. Handle the Result
|
|
129
101
|
|
|
130
102
|
The tool blocks until the user submits their review in the browser. When it returns:
|
|
131
103
|
|
|
@@ -133,21 +105,24 @@ The tool blocks until the user submits their review in the browser. When it retu
|
|
|
133
105
|
- **\`approved_with_comments\`** \u2014 Note the comments, address any actionable feedback.
|
|
134
106
|
- **\`changes_requested\`** \u2014 Read the comments carefully, make the requested changes, and offer to open another review.
|
|
135
107
|
|
|
136
|
-
###
|
|
108
|
+
### 5. Error Handling
|
|
137
109
|
|
|
138
110
|
If the \`mcp__diffprism__open_review\` tool is not available:
|
|
139
|
-
- Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism
|
|
111
|
+
- Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism start\` to set it up, then restart Claude Code."
|
|
112
|
+
|
|
113
|
+
## Watch Mode: Waiting for Review Feedback
|
|
140
114
|
|
|
141
|
-
|
|
115
|
+
When \`diffprism watch\` is active (detected via \`.diffprism/watch.json\`), the developer can submit reviews at any time in the browser.
|
|
142
116
|
|
|
143
|
-
|
|
117
|
+
**After pushing context to a watch session**, call \`mcp__diffprism__get_review_result\` with \`wait: true\` to block until the developer submits their review. This polls the result file every 2 seconds and returns as soon as feedback is available (up to 5 minutes by default).
|
|
144
118
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
119
|
+
Use this pattern:
|
|
120
|
+
1. Push context via \`update_review_context\`
|
|
121
|
+
2. Call \`get_review_result\` with \`wait: true\` \u2014 this blocks until the developer submits
|
|
122
|
+
3. Handle the result (approved, changes_requested, etc.)
|
|
123
|
+
4. If changes were requested, make fixes, push updated context, and call \`get_review_result\` with \`wait: true\` again
|
|
149
124
|
|
|
150
|
-
|
|
125
|
+
You can also check for feedback without blocking by calling \`get_review_result\` without \`wait\` at natural breakpoints (between tasks, before committing, etc.).
|
|
151
126
|
|
|
152
127
|
## Behavior Rules
|
|
153
128
|
|
|
@@ -156,7 +131,7 @@ Do not poll in a tight loop. Check at natural breakpoints in your workflow (e.g.
|
|
|
156
131
|
- \`"ask"\` \u2014 Never auto-review; only review when the user asks.
|
|
157
132
|
- \`"before_commit"\` \u2014 Open a review before creating any git commit.
|
|
158
133
|
- \`"always"\` \u2014 Open a review after any code change.
|
|
159
|
-
-
|
|
134
|
+
- Power users can create \`diffprism.config.json\` manually to customize defaults.
|
|
160
135
|
`;
|
|
161
136
|
|
|
162
137
|
// cli/src/commands/setup.ts
|
|
@@ -352,11 +327,14 @@ async function setup(flags) {
|
|
|
352
327
|
"Error: Not in a git repository. Run this command from inside a git project."
|
|
353
328
|
);
|
|
354
329
|
process.exit(1);
|
|
355
|
-
return;
|
|
330
|
+
return { created: [], updated: [], skipped: [] };
|
|
356
331
|
}
|
|
357
332
|
const force = flags.force ?? false;
|
|
358
333
|
const global = flags.global ?? false;
|
|
359
|
-
|
|
334
|
+
const quiet = flags.quiet ?? false;
|
|
335
|
+
if (!quiet) {
|
|
336
|
+
console.log("Setting up DiffPrism for Claude Code...\n");
|
|
337
|
+
}
|
|
360
338
|
const result = { created: [], updated: [], skipped: [] };
|
|
361
339
|
const gitignore = await setupGitignore(gitRoot);
|
|
362
340
|
result[gitignore.action].push(gitignore.filePath);
|
|
@@ -365,40 +343,91 @@ async function setup(flags) {
|
|
|
365
343
|
const settings = setupClaudeSettings(gitRoot, force);
|
|
366
344
|
result[settings.action === "skipped" ? "skipped" : settings.action === "created" ? "created" : "updated"].push(settings.filePath);
|
|
367
345
|
const cleaned = cleanDiffprismHooks(gitRoot);
|
|
368
|
-
if (cleaned.removed > 0) {
|
|
346
|
+
if (cleaned.removed > 0 && !quiet) {
|
|
369
347
|
console.log(` Cleaned ${cleaned.removed} stale hook(s)`);
|
|
370
348
|
}
|
|
371
349
|
const hook = setupStopHook(gitRoot, force);
|
|
372
350
|
result[hook.action === "skipped" ? "skipped" : hook.action === "created" ? "created" : "updated"].push(hook.filePath);
|
|
373
351
|
const skill = setupSkill(gitRoot, global, force);
|
|
374
352
|
result[skill.action === "skipped" ? "skipped" : skill.action === "created" ? "created" : "updated"].push(skill.filePath);
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
353
|
+
if (!quiet) {
|
|
354
|
+
if (result.created.length > 0) {
|
|
355
|
+
console.log("Created:");
|
|
356
|
+
for (const f of result.created) {
|
|
357
|
+
console.log(` + ${path.relative(gitRoot, f)}`);
|
|
358
|
+
}
|
|
379
359
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
360
|
+
if (result.updated.length > 0) {
|
|
361
|
+
console.log("Updated:");
|
|
362
|
+
for (const f of result.updated) {
|
|
363
|
+
console.log(` ~ ${path.relative(gitRoot, f)}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (result.skipped.length > 0) {
|
|
367
|
+
console.log("Skipped (already configured):");
|
|
368
|
+
for (const f of result.skipped) {
|
|
369
|
+
console.log(` - ${path.relative(gitRoot, f)}`);
|
|
370
|
+
}
|
|
385
371
|
}
|
|
372
|
+
console.log("\n\u2713 DiffPrism configured for Claude Code.\n");
|
|
373
|
+
console.log("Next steps:");
|
|
374
|
+
console.log(" 1. Restart Claude Code to pick up the MCP configuration");
|
|
375
|
+
console.log(" 2. Use /review in Claude Code to review your changes\n");
|
|
376
|
+
console.log("Tip: Run `diffprism start` to combine setup + live watch mode.");
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// cli/src/commands/start.ts
|
|
382
|
+
async function start(ref, flags) {
|
|
383
|
+
const outcome = await setup({
|
|
384
|
+
global: flags.global,
|
|
385
|
+
force: flags.force,
|
|
386
|
+
quiet: true
|
|
387
|
+
});
|
|
388
|
+
const hasChanges = outcome.created.length > 0 || outcome.updated.length > 0;
|
|
389
|
+
if (hasChanges) {
|
|
390
|
+
console.log("\u2713 DiffPrism configured for Claude Code.");
|
|
386
391
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
392
|
+
let diffRef;
|
|
393
|
+
if (flags.staged) {
|
|
394
|
+
diffRef = "staged";
|
|
395
|
+
} else if (flags.unstaged) {
|
|
396
|
+
diffRef = "unstaged";
|
|
397
|
+
} else if (ref) {
|
|
398
|
+
diffRef = ref;
|
|
399
|
+
} else {
|
|
400
|
+
diffRef = "working-copy";
|
|
401
|
+
}
|
|
402
|
+
const pollInterval = flags.interval ? parseInt(flags.interval, 10) : 1e3;
|
|
403
|
+
try {
|
|
404
|
+
const handle = await startWatch({
|
|
405
|
+
diffRef,
|
|
406
|
+
title: flags.title,
|
|
407
|
+
cwd: process.cwd(),
|
|
408
|
+
dev: flags.dev,
|
|
409
|
+
pollInterval
|
|
410
|
+
});
|
|
411
|
+
console.log("Use /review in Claude Code to send changes for review.");
|
|
412
|
+
if (hasChanges) {
|
|
413
|
+
console.log(
|
|
414
|
+
"If this is your first time, restart Claude Code first to load the MCP server."
|
|
415
|
+
);
|
|
391
416
|
}
|
|
417
|
+
const shutdown = async () => {
|
|
418
|
+
console.log("\nStopping DiffPrism...");
|
|
419
|
+
await handle.stop();
|
|
420
|
+
process.exit(0);
|
|
421
|
+
};
|
|
422
|
+
process.on("SIGINT", shutdown);
|
|
423
|
+
process.on("SIGTERM", shutdown);
|
|
424
|
+
await new Promise(() => {
|
|
425
|
+
});
|
|
426
|
+
} catch (err) {
|
|
427
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
428
|
+
console.error(`Error: ${message}`);
|
|
429
|
+
process.exit(1);
|
|
392
430
|
}
|
|
393
|
-
console.log(
|
|
394
|
-
"\nYou can now use /review in Claude Code to open a DiffPrism review."
|
|
395
|
-
);
|
|
396
|
-
console.log(
|
|
397
|
-
"Or run `diffprism watch --staged` for live-updating reviews."
|
|
398
|
-
);
|
|
399
|
-
console.log(
|
|
400
|
-
"If Claude Code is running, restart it to pick up the new configuration."
|
|
401
|
-
);
|
|
402
431
|
}
|
|
403
432
|
|
|
404
433
|
// cli/src/commands/watch.ts
|
|
@@ -411,7 +440,7 @@ async function watch(ref, flags) {
|
|
|
411
440
|
} else if (ref) {
|
|
412
441
|
diffRef = ref;
|
|
413
442
|
} else {
|
|
414
|
-
diffRef = "
|
|
443
|
+
diffRef = "working-copy";
|
|
415
444
|
}
|
|
416
445
|
const pollInterval = flags.interval ? parseInt(flags.interval, 10) : 1e3;
|
|
417
446
|
try {
|
|
@@ -463,10 +492,13 @@ async function notifyStop() {
|
|
|
463
492
|
|
|
464
493
|
// cli/src/index.ts
|
|
465
494
|
var program = new Command();
|
|
466
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
495
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.14.0" : "0.0.0-dev");
|
|
467
496
|
program.command("review [ref]").description("Open a browser-based diff review").option("--staged", "Review staged changes").option("--unstaged", "Review unstaged changes").option("-t, --title <title>", "Review title").option("--dev", "Use Vite dev server with HMR instead of static files").action(review);
|
|
497
|
+
program.command("start [ref]").description("Set up DiffPrism and start watching for changes").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(start);
|
|
468
498
|
program.command("watch [ref]").description("Start a persistent diff watcher with live-updating browser UI").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").action(watch);
|
|
469
499
|
program.command("notify-stop").description("Signal the watch server to refresh (used by Claude Code hooks)").action(notifyStop);
|
|
470
500
|
program.command("serve").description("Start the MCP server for Claude Code integration").action(serve);
|
|
471
|
-
program.command("setup").description("Configure DiffPrism for Claude Code integration").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(
|
|
501
|
+
program.command("setup").description("Configure DiffPrism for Claude Code integration").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action((flags) => {
|
|
502
|
+
setup(flags);
|
|
503
|
+
});
|
|
472
504
|
program.parse();
|
|
@@ -303,6 +303,9 @@ function parseDiff(rawDiff, baseRef, headRef) {
|
|
|
303
303
|
|
|
304
304
|
// packages/git/src/index.ts
|
|
305
305
|
function getDiff(ref, options) {
|
|
306
|
+
if (ref === "working-copy") {
|
|
307
|
+
return getWorkingCopyDiff(options);
|
|
308
|
+
}
|
|
306
309
|
const rawDiff = getGitDiff(ref, options);
|
|
307
310
|
let baseRef;
|
|
308
311
|
let headRef;
|
|
@@ -323,6 +326,29 @@ function getDiff(ref, options) {
|
|
|
323
326
|
const diffSet = parseDiff(rawDiff, baseRef, headRef);
|
|
324
327
|
return { diffSet, rawDiff };
|
|
325
328
|
}
|
|
329
|
+
function getWorkingCopyDiff(options) {
|
|
330
|
+
const stagedRaw = getGitDiff("staged", options);
|
|
331
|
+
const unstagedRaw = getGitDiff("unstaged", options);
|
|
332
|
+
const stagedDiffSet = parseDiff(stagedRaw, "HEAD", "staged");
|
|
333
|
+
const unstagedDiffSet = parseDiff(unstagedRaw, "staged", "working tree");
|
|
334
|
+
const stagedFiles = stagedDiffSet.files.map((f) => ({
|
|
335
|
+
...f,
|
|
336
|
+
stage: "staged"
|
|
337
|
+
}));
|
|
338
|
+
const unstagedFiles = unstagedDiffSet.files.map((f) => ({
|
|
339
|
+
...f,
|
|
340
|
+
stage: "unstaged"
|
|
341
|
+
}));
|
|
342
|
+
const rawDiff = [stagedRaw, unstagedRaw].filter(Boolean).join("");
|
|
343
|
+
return {
|
|
344
|
+
diffSet: {
|
|
345
|
+
baseRef: "HEAD",
|
|
346
|
+
headRef: "working tree",
|
|
347
|
+
files: [...stagedFiles, ...unstagedFiles]
|
|
348
|
+
},
|
|
349
|
+
rawDiff
|
|
350
|
+
};
|
|
351
|
+
}
|
|
326
352
|
|
|
327
353
|
// packages/analysis/src/deterministic.ts
|
|
328
354
|
function categorizeFiles(files) {
|
|
@@ -1173,25 +1199,29 @@ function createWatchBridge(port, callbacks) {
|
|
|
1173
1199
|
function hashDiff(rawDiff) {
|
|
1174
1200
|
return createHash("sha256").update(rawDiff).digest("hex");
|
|
1175
1201
|
}
|
|
1202
|
+
function fileKey(file) {
|
|
1203
|
+
return file.stage ? `${file.stage}:${file.path}` : file.path;
|
|
1204
|
+
}
|
|
1176
1205
|
function detectChangedFiles(oldDiffSet, newDiffSet) {
|
|
1177
1206
|
if (!oldDiffSet) {
|
|
1178
|
-
return newDiffSet.files.map(
|
|
1207
|
+
return newDiffSet.files.map(fileKey);
|
|
1179
1208
|
}
|
|
1180
1209
|
const oldFiles = new Map(
|
|
1181
|
-
oldDiffSet.files.map((f) => [f
|
|
1210
|
+
oldDiffSet.files.map((f) => [fileKey(f), f])
|
|
1182
1211
|
);
|
|
1183
1212
|
const changed = [];
|
|
1184
1213
|
for (const newFile of newDiffSet.files) {
|
|
1185
|
-
const
|
|
1214
|
+
const key = fileKey(newFile);
|
|
1215
|
+
const oldFile = oldFiles.get(key);
|
|
1186
1216
|
if (!oldFile) {
|
|
1187
|
-
changed.push(
|
|
1217
|
+
changed.push(key);
|
|
1188
1218
|
} else if (oldFile.additions !== newFile.additions || oldFile.deletions !== newFile.deletions) {
|
|
1189
|
-
changed.push(
|
|
1219
|
+
changed.push(key);
|
|
1190
1220
|
}
|
|
1191
1221
|
}
|
|
1192
1222
|
for (const oldFile of oldDiffSet.files) {
|
|
1193
|
-
if (!newDiffSet.files.some((f) => f
|
|
1194
|
-
changed.push(oldFile
|
|
1223
|
+
if (!newDiffSet.files.some((f) => fileKey(f) === fileKey(oldFile))) {
|
|
1224
|
+
changed.push(fileKey(oldFile));
|
|
1195
1225
|
}
|
|
1196
1226
|
}
|
|
1197
1227
|
return changed;
|
package/dist/mcp-server.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
readReviewResult,
|
|
4
4
|
readWatchFile,
|
|
5
5
|
startReview
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TVXIMP3G.js";
|
|
7
7
|
|
|
8
8
|
// packages/mcp-server/src/index.ts
|
|
9
9
|
import fs from "fs";
|
|
@@ -14,14 +14,14 @@ import { z } from "zod";
|
|
|
14
14
|
async function startMcpServer() {
|
|
15
15
|
const server = new McpServer({
|
|
16
16
|
name: "diffprism",
|
|
17
|
-
version: true ? "0.
|
|
17
|
+
version: true ? "0.14.0" : "0.0.0-dev"
|
|
18
18
|
});
|
|
19
19
|
server.tool(
|
|
20
20
|
"open_review",
|
|
21
21
|
"Open a browser-based code review for local git changes. Blocks until the engineer submits their review decision.",
|
|
22
22
|
{
|
|
23
23
|
diff_ref: z.string().describe(
|
|
24
|
-
'Git diff reference: "staged", "unstaged", or a ref range like "HEAD~3..HEAD"'
|
|
24
|
+
'Git diff reference: "staged", "unstaged", "working-copy" (staged+unstaged grouped), or a ref range like "HEAD~3..HEAD"'
|
|
25
25
|
),
|
|
26
26
|
title: z.string().optional().describe("Title for the review"),
|
|
27
27
|
description: z.string().optional().describe("Description of the changes"),
|
|
@@ -124,10 +124,41 @@ async function startMcpServer() {
|
|
|
124
124
|
);
|
|
125
125
|
server.tool(
|
|
126
126
|
"get_review_result",
|
|
127
|
-
"Fetch the most recent review result from a DiffPrism watch 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.",
|
|
128
|
-
{
|
|
129
|
-
|
|
127
|
+
"Fetch the most recent review result from a DiffPrism watch 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
|
+
{
|
|
129
|
+
wait: z.boolean().optional().describe("If true, poll until a review result is available (blocks up to timeout)"),
|
|
130
|
+
timeout: z.number().optional().describe("Max wait time in seconds when wait=true (default: 300, max: 600)")
|
|
131
|
+
},
|
|
132
|
+
async ({ wait, timeout }) => {
|
|
130
133
|
try {
|
|
134
|
+
if (wait) {
|
|
135
|
+
const maxWaitMs = Math.min(timeout ?? 300, 600) * 1e3;
|
|
136
|
+
const pollIntervalMs = 2e3;
|
|
137
|
+
const start = Date.now();
|
|
138
|
+
while (Date.now() - start < maxWaitMs) {
|
|
139
|
+
const data2 = readReviewResult();
|
|
140
|
+
if (data2) {
|
|
141
|
+
consumeReviewResult();
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: JSON.stringify(data2.result, null, 2)
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: "No review result received within timeout."
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
131
162
|
const data = readReviewResult();
|
|
132
163
|
if (!data) {
|
|
133
164
|
return {
|