diffprism 0.16.0 → 0.17.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/README.md +32 -4
- package/dist/bin.js +83 -32
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ DiffPrism gives you a visual review step for AI-written code — stage your chan
|
|
|
15
15
|
- **Keyboard shortcuts** — `j`/`k` navigate files, `s` cycles file status
|
|
16
16
|
- **Three-way decisions** — approve, request changes, or approve with comments
|
|
17
17
|
- **Branch display** — current git branch shown in the review header
|
|
18
|
+
- **Global server mode** — `diffprism server` runs a persistent multi-session review server, multiple agents post reviews to one browser tab
|
|
19
|
+
- **Multi-session UI** — session list with status badges, branch info, file counts, and change stats when running in server mode
|
|
18
20
|
|
|
19
21
|
## Quick Start
|
|
20
22
|
|
|
@@ -85,6 +87,32 @@ When `diffprism watch` is running:
|
|
|
85
87
|
|
|
86
88
|
Stop the watcher with `Ctrl+C`.
|
|
87
89
|
|
|
90
|
+
### Global Server Mode (multi-session)
|
|
91
|
+
|
|
92
|
+
Run a persistent server that accepts reviews from multiple Claude Code sessions and displays them in one browser tab:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Start the global server (auto-runs global setup if needed)
|
|
96
|
+
diffprism server
|
|
97
|
+
|
|
98
|
+
# Check status and list active sessions
|
|
99
|
+
diffprism server status
|
|
100
|
+
|
|
101
|
+
# Stop the server
|
|
102
|
+
diffprism server stop
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
When the global server is running, MCP tools automatically detect it and route reviews there instead of opening ephemeral browser tabs. Each review appears as a session in the multi-session UI — click to switch between them.
|
|
106
|
+
|
|
107
|
+
**Global setup** (optional, `diffprism server` runs this automatically):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Configure skill + permissions globally (no git repo required)
|
|
111
|
+
diffprism setup --global
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Per-project MCP registration (`.mcp.json`) is still needed via `diffprism setup` in each project.
|
|
115
|
+
|
|
88
116
|
## MCP Tool Reference
|
|
89
117
|
|
|
90
118
|
The MCP server exposes three tools:
|
|
@@ -162,12 +190,12 @@ npx tsc --noEmit -p packages/core/tsconfig.json # Type-check a package
|
|
|
162
190
|
### Project Structure
|
|
163
191
|
|
|
164
192
|
```
|
|
165
|
-
packages/core — Shared types, pipeline orchestrator, WebSocket bridge
|
|
193
|
+
packages/core — Shared types, pipeline orchestrator, WebSocket bridge, global server
|
|
166
194
|
packages/git — Git diff extraction + unified diff parser
|
|
167
195
|
packages/analysis — Deterministic review briefing (complexity, test gaps, patterns)
|
|
168
|
-
packages/ui — React 19 + Vite 6 + Tailwind + Zustand diff viewer
|
|
169
|
-
packages/mcp-server — MCP tool server
|
|
170
|
-
cli/ — Commander CLI
|
|
196
|
+
packages/ui — React 19 + Vite 6 + Tailwind + Zustand diff viewer + session list
|
|
197
|
+
packages/mcp-server — MCP tool server, auto-routes to global server when available
|
|
198
|
+
cli/ — Commander CLI (review, serve, setup, server commands)
|
|
171
199
|
```
|
|
172
200
|
|
|
173
201
|
### Requirements
|
package/dist/bin.js
CHANGED
|
@@ -111,7 +111,13 @@ The tool blocks until the user submits their review in the browser. When it retu
|
|
|
111
111
|
### 5. Error Handling
|
|
112
112
|
|
|
113
113
|
If the \`mcp__diffprism__open_review\` tool is not available:
|
|
114
|
-
- Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism
|
|
114
|
+
- Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism setup\` to set it up, then restart Claude Code."
|
|
115
|
+
|
|
116
|
+
## Global Server Mode
|
|
117
|
+
|
|
118
|
+
When a global DiffPrism server is running (\`diffprism server\`), the MCP tools automatically detect it and route reviews there instead of opening a new browser tab each time. The review appears in the server's multi-session UI at the existing browser tab.
|
|
119
|
+
|
|
120
|
+
This is transparent \u2014 the same \`open_review\`, \`update_review_context\`, and \`get_review_result\` tools work the same way. No changes to the workflow are needed.
|
|
115
121
|
|
|
116
122
|
## Watch Mode: Waiting for Review Feedback
|
|
117
123
|
|
|
@@ -179,8 +185,8 @@ function setupMcpJson(gitRoot, force) {
|
|
|
179
185
|
writeJsonFile(filePath, { ...existing, mcpServers: servers });
|
|
180
186
|
return { action, filePath };
|
|
181
187
|
}
|
|
182
|
-
function setupClaudeSettings(
|
|
183
|
-
const filePath = path.join(
|
|
188
|
+
function setupClaudeSettings(baseDir, force) {
|
|
189
|
+
const filePath = path.join(baseDir, ".claude", "settings.json");
|
|
184
190
|
const existing = readJsonFile(filePath);
|
|
185
191
|
const permissions = existing.permissions ?? {};
|
|
186
192
|
const allow = permissions.allow ?? [];
|
|
@@ -324,54 +330,59 @@ async function setupGitignore(gitRoot) {
|
|
|
324
330
|
return { action: "created", filePath };
|
|
325
331
|
}
|
|
326
332
|
async function setup(flags) {
|
|
333
|
+
const force = flags.force ?? false;
|
|
334
|
+
const global = flags.global ?? false;
|
|
335
|
+
const quiet = flags.quiet ?? false;
|
|
336
|
+
const result = { created: [], updated: [], skipped: [] };
|
|
337
|
+
const home = os.homedir();
|
|
338
|
+
if (global) {
|
|
339
|
+
if (!quiet) {
|
|
340
|
+
console.log("Setting up DiffPrism globally...\n");
|
|
341
|
+
}
|
|
342
|
+
const skill2 = setupSkill("", true, force);
|
|
343
|
+
result[skill2.action].push(skill2.filePath);
|
|
344
|
+
const settings2 = setupClaudeSettings(home, force);
|
|
345
|
+
result[settings2.action].push(settings2.filePath);
|
|
346
|
+
if (!quiet) {
|
|
347
|
+
printSummary(result, home);
|
|
348
|
+
console.log("\n\u2713 DiffPrism configured globally.\n");
|
|
349
|
+
console.log("Next steps:");
|
|
350
|
+
console.log(" 1. Run `diffprism server` to start the global review server");
|
|
351
|
+
console.log(" 2. In each project, run `diffprism setup` to register the MCP server");
|
|
352
|
+
console.log(" 3. Use /review in Claude Code to review your changes\n");
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
327
356
|
const gitRoot = findGitRoot(process.cwd());
|
|
328
357
|
if (!gitRoot) {
|
|
329
358
|
console.error(
|
|
330
359
|
"Error: Not in a git repository. Run this command from inside a git project."
|
|
331
360
|
);
|
|
361
|
+
console.error(
|
|
362
|
+
"Tip: Use `diffprism setup --global` to configure DiffPrism globally without a git repo."
|
|
363
|
+
);
|
|
332
364
|
process.exit(1);
|
|
333
365
|
return { created: [], updated: [], skipped: [] };
|
|
334
366
|
}
|
|
335
|
-
const force = flags.force ?? false;
|
|
336
|
-
const global = flags.global ?? false;
|
|
337
|
-
const quiet = flags.quiet ?? false;
|
|
338
367
|
if (!quiet) {
|
|
339
368
|
console.log("Setting up DiffPrism for Claude Code...\n");
|
|
340
369
|
}
|
|
341
|
-
const result = { created: [], updated: [], skipped: [] };
|
|
342
370
|
const gitignore = await setupGitignore(gitRoot);
|
|
343
371
|
result[gitignore.action].push(gitignore.filePath);
|
|
344
372
|
const mcp = setupMcpJson(gitRoot, force);
|
|
345
|
-
result[mcp.action
|
|
373
|
+
result[mcp.action].push(mcp.filePath);
|
|
346
374
|
const settings = setupClaudeSettings(gitRoot, force);
|
|
347
|
-
result[settings.action
|
|
375
|
+
result[settings.action].push(settings.filePath);
|
|
348
376
|
const cleaned = cleanDiffprismHooks(gitRoot);
|
|
349
377
|
if (cleaned.removed > 0 && !quiet) {
|
|
350
378
|
console.log(` Cleaned ${cleaned.removed} stale hook(s)`);
|
|
351
379
|
}
|
|
352
380
|
const hook = setupStopHook(gitRoot, force);
|
|
353
|
-
result[hook.action
|
|
354
|
-
const skill = setupSkill(gitRoot,
|
|
355
|
-
result[skill.action
|
|
381
|
+
result[hook.action].push(hook.filePath);
|
|
382
|
+
const skill = setupSkill(gitRoot, false, force);
|
|
383
|
+
result[skill.action].push(skill.filePath);
|
|
356
384
|
if (!quiet) {
|
|
357
|
-
|
|
358
|
-
console.log("Created:");
|
|
359
|
-
for (const f of result.created) {
|
|
360
|
-
console.log(` + ${path.relative(gitRoot, f)}`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (result.updated.length > 0) {
|
|
364
|
-
console.log("Updated:");
|
|
365
|
-
for (const f of result.updated) {
|
|
366
|
-
console.log(` ~ ${path.relative(gitRoot, f)}`);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (result.skipped.length > 0) {
|
|
370
|
-
console.log("Skipped (already configured):");
|
|
371
|
-
for (const f of result.skipped) {
|
|
372
|
-
console.log(` - ${path.relative(gitRoot, f)}`);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
385
|
+
printSummary(result, gitRoot);
|
|
375
386
|
console.log("\n\u2713 DiffPrism configured for Claude Code.\n");
|
|
376
387
|
console.log("Next steps:");
|
|
377
388
|
console.log(" 1. Restart Claude Code to pick up the MCP configuration");
|
|
@@ -380,6 +391,41 @@ async function setup(flags) {
|
|
|
380
391
|
}
|
|
381
392
|
return result;
|
|
382
393
|
}
|
|
394
|
+
function printSummary(result, baseDir) {
|
|
395
|
+
if (result.created.length > 0) {
|
|
396
|
+
console.log("Created:");
|
|
397
|
+
for (const f of result.created) {
|
|
398
|
+
console.log(` + ${path.relative(baseDir, f) || f}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (result.updated.length > 0) {
|
|
402
|
+
console.log("Updated:");
|
|
403
|
+
for (const f of result.updated) {
|
|
404
|
+
console.log(` ~ ${path.relative(baseDir, f) || f}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (result.skipped.length > 0) {
|
|
408
|
+
console.log("Skipped (already configured):");
|
|
409
|
+
for (const f of result.skipped) {
|
|
410
|
+
console.log(` - ${path.relative(baseDir, f) || f}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function isGlobalSetupDone() {
|
|
415
|
+
const home = os.homedir();
|
|
416
|
+
const skillPath = path.join(home, ".claude", "skills", "review", "SKILL.md");
|
|
417
|
+
const settingsPath = path.join(home, ".claude", "settings.json");
|
|
418
|
+
if (!fs.existsSync(skillPath)) return false;
|
|
419
|
+
const settings = readJsonFile(settingsPath);
|
|
420
|
+
const permissions = settings.permissions ?? {};
|
|
421
|
+
const allow = permissions.allow ?? [];
|
|
422
|
+
const toolNames = [
|
|
423
|
+
"mcp__diffprism__open_review",
|
|
424
|
+
"mcp__diffprism__update_review_context",
|
|
425
|
+
"mcp__diffprism__get_review_result"
|
|
426
|
+
];
|
|
427
|
+
return toolNames.every((t) => allow.includes(t));
|
|
428
|
+
}
|
|
383
429
|
|
|
384
430
|
// cli/src/commands/start.ts
|
|
385
431
|
async function start(ref, flags) {
|
|
@@ -502,6 +548,11 @@ async function server(flags) {
|
|
|
502
548
|
process.exit(1);
|
|
503
549
|
return;
|
|
504
550
|
}
|
|
551
|
+
if (!isGlobalSetupDone()) {
|
|
552
|
+
console.log("Running global setup...\n");
|
|
553
|
+
await setup({ global: true, quiet: false });
|
|
554
|
+
console.log("");
|
|
555
|
+
}
|
|
505
556
|
const httpPort = flags.port ? parseInt(flags.port, 10) : void 0;
|
|
506
557
|
const wsPort = flags.wsPort ? parseInt(flags.wsPort, 10) : void 0;
|
|
507
558
|
try {
|
|
@@ -578,13 +629,13 @@ async function serverStop() {
|
|
|
578
629
|
|
|
579
630
|
// cli/src/index.ts
|
|
580
631
|
var program = new Command();
|
|
581
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
632
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.17.0" : "0.0.0-dev");
|
|
582
633
|
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);
|
|
583
634
|
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);
|
|
584
635
|
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);
|
|
585
636
|
program.command("notify-stop").description("Signal the watch server to refresh (used by Claude Code hooks)").action(notifyStop);
|
|
586
637
|
program.command("serve").description("Start the MCP server for Claude Code integration").action(serve);
|
|
587
|
-
program.command("setup").description("Configure DiffPrism for Claude Code integration").option("--global", "
|
|
638
|
+
program.command("setup").description("Configure DiffPrism for Claude Code integration").option("--global", "Configure globally (skill + permissions, no git repo required)").option("--force", "Overwrite existing configuration files").action((flags) => {
|
|
588
639
|
setup(flags);
|
|
589
640
|
});
|
|
590
641
|
var serverCmd = program.command("server").description("Start the global DiffPrism server for multi-session reviews").option("-p, --port <port>", "HTTP API port (default: 24680)").option("--ws-port <port>", "WebSocket port (default: 24681)").option("--dev", "Use Vite dev server with HMR instead of static files").action(server);
|
package/dist/mcp-server.js
CHANGED
|
@@ -76,7 +76,7 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
|
76
76
|
async function startMcpServer() {
|
|
77
77
|
const server = new McpServer({
|
|
78
78
|
name: "diffprism",
|
|
79
|
-
version: true ? "0.
|
|
79
|
+
version: true ? "0.17.0" : "0.0.0-dev"
|
|
80
80
|
});
|
|
81
81
|
server.tool(
|
|
82
82
|
"open_review",
|