diffprism 0.30.0 → 0.31.1
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 +13 -4
- package/dist/mcp-server.js +278 -1
- package/package.json +1 -1
- package/ui-dist/assets/index-Cwwzm9DK.js +319 -0
- package/ui-dist/assets/index-r-H-ptFw.css +1 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-D_PeJaM9.css +0 -1
- package/ui-dist/assets/index-swQ2C7w3.js +0 -304
package/dist/bin.js
CHANGED
|
@@ -241,7 +241,10 @@ function setupClaudeSettings(baseDir, force) {
|
|
|
241
241
|
"mcp__diffprism__update_review_context",
|
|
242
242
|
"mcp__diffprism__get_review_result",
|
|
243
243
|
"mcp__diffprism__get_diff",
|
|
244
|
-
"mcp__diffprism__analyze_diff"
|
|
244
|
+
"mcp__diffprism__analyze_diff",
|
|
245
|
+
"mcp__diffprism__add_annotation",
|
|
246
|
+
"mcp__diffprism__get_review_state",
|
|
247
|
+
"mcp__diffprism__flag_for_attention"
|
|
245
248
|
];
|
|
246
249
|
const allPresent = toolNames.every((t) => allow.includes(t));
|
|
247
250
|
if (allPresent && !force) {
|
|
@@ -473,7 +476,10 @@ function isGlobalSetupDone() {
|
|
|
473
476
|
"mcp__diffprism__update_review_context",
|
|
474
477
|
"mcp__diffprism__get_review_result",
|
|
475
478
|
"mcp__diffprism__get_diff",
|
|
476
|
-
"mcp__diffprism__analyze_diff"
|
|
479
|
+
"mcp__diffprism__analyze_diff",
|
|
480
|
+
"mcp__diffprism__add_annotation",
|
|
481
|
+
"mcp__diffprism__get_review_state",
|
|
482
|
+
"mcp__diffprism__flag_for_attention"
|
|
477
483
|
];
|
|
478
484
|
return toolNames.every((t) => allow.includes(t));
|
|
479
485
|
}
|
|
@@ -518,7 +524,10 @@ function teardownClaudePermissions(baseDir) {
|
|
|
518
524
|
"mcp__diffprism__update_review_context",
|
|
519
525
|
"mcp__diffprism__get_review_result",
|
|
520
526
|
"mcp__diffprism__get_diff",
|
|
521
|
-
"mcp__diffprism__analyze_diff"
|
|
527
|
+
"mcp__diffprism__analyze_diff",
|
|
528
|
+
"mcp__diffprism__add_annotation",
|
|
529
|
+
"mcp__diffprism__get_review_state",
|
|
530
|
+
"mcp__diffprism__flag_for_attention"
|
|
522
531
|
];
|
|
523
532
|
const filtered = allow.filter((t) => !toolNames.includes(t));
|
|
524
533
|
if (filtered.length === allow.length) {
|
|
@@ -878,7 +887,7 @@ async function serverStop() {
|
|
|
878
887
|
|
|
879
888
|
// cli/src/index.ts
|
|
880
889
|
var program = new Command();
|
|
881
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
890
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.31.1" : "0.0.0-dev");
|
|
882
891
|
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);
|
|
883
892
|
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);
|
|
884
893
|
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);
|
package/dist/mcp-server.js
CHANGED
|
@@ -83,7 +83,7 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
|
|
|
83
83
|
async function startMcpServer() {
|
|
84
84
|
const server = new McpServer({
|
|
85
85
|
name: "diffprism",
|
|
86
|
-
version: true ? "0.
|
|
86
|
+
version: true ? "0.31.1" : "0.0.0-dev"
|
|
87
87
|
});
|
|
88
88
|
server.tool(
|
|
89
89
|
"open_review",
|
|
@@ -451,6 +451,283 @@ async function startMcpServer() {
|
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
453
|
);
|
|
454
|
+
server.tool(
|
|
455
|
+
"add_annotation",
|
|
456
|
+
"Post a structured finding (annotation) to a review session. Use this to flag issues, suggest improvements, or ask questions about specific lines of code in a review. Requires a running global server (`diffprism server`).",
|
|
457
|
+
{
|
|
458
|
+
session_id: z.string().describe("Review session ID from open_review"),
|
|
459
|
+
file: z.string().describe("File path within the diff to annotate"),
|
|
460
|
+
line: z.number().describe("Line number to annotate"),
|
|
461
|
+
body: z.string().describe("The annotation text \u2014 your finding, suggestion, or question"),
|
|
462
|
+
type: z.enum(["finding", "suggestion", "question", "warning"]).describe("Type of annotation"),
|
|
463
|
+
confidence: z.number().min(0).max(1).optional().describe("Confidence in the finding (0-1, defaults to 1)"),
|
|
464
|
+
category: z.enum([
|
|
465
|
+
"security",
|
|
466
|
+
"performance",
|
|
467
|
+
"convention",
|
|
468
|
+
"correctness",
|
|
469
|
+
"complexity",
|
|
470
|
+
"test-coverage",
|
|
471
|
+
"documentation",
|
|
472
|
+
"other"
|
|
473
|
+
]).optional().describe("Category of the finding (defaults to 'other')"),
|
|
474
|
+
source_agent: z.string().optional().describe("Agent identifier (e.g., 'security-reviewer')")
|
|
475
|
+
},
|
|
476
|
+
async ({
|
|
477
|
+
session_id,
|
|
478
|
+
file,
|
|
479
|
+
line,
|
|
480
|
+
body,
|
|
481
|
+
type,
|
|
482
|
+
confidence,
|
|
483
|
+
category,
|
|
484
|
+
source_agent
|
|
485
|
+
}) => {
|
|
486
|
+
try {
|
|
487
|
+
const serverInfo = await isServerAlive();
|
|
488
|
+
if (!serverInfo) {
|
|
489
|
+
return {
|
|
490
|
+
content: [
|
|
491
|
+
{
|
|
492
|
+
type: "text",
|
|
493
|
+
text: "No global server running. Start one with `diffprism server`."
|
|
494
|
+
}
|
|
495
|
+
],
|
|
496
|
+
isError: true
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const response = await fetch(
|
|
500
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${session_id}/annotations`,
|
|
501
|
+
{
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers: { "Content-Type": "application/json" },
|
|
504
|
+
body: JSON.stringify({
|
|
505
|
+
file,
|
|
506
|
+
line,
|
|
507
|
+
body,
|
|
508
|
+
type,
|
|
509
|
+
confidence: confidence ?? 1,
|
|
510
|
+
category: category ?? "other",
|
|
511
|
+
source: {
|
|
512
|
+
agent: source_agent ?? "unknown",
|
|
513
|
+
tool: "add_annotation"
|
|
514
|
+
}
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
if (!response.ok) {
|
|
519
|
+
const errorData = await response.json().catch(() => ({}));
|
|
520
|
+
const errorMsg = errorData.error ?? `Server returned ${response.status}`;
|
|
521
|
+
return {
|
|
522
|
+
content: [
|
|
523
|
+
{
|
|
524
|
+
type: "text",
|
|
525
|
+
text: `Error: ${errorMsg}`
|
|
526
|
+
}
|
|
527
|
+
],
|
|
528
|
+
isError: true
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
const data = await response.json();
|
|
532
|
+
return {
|
|
533
|
+
content: [
|
|
534
|
+
{
|
|
535
|
+
type: "text",
|
|
536
|
+
text: JSON.stringify(
|
|
537
|
+
{ annotationId: data.annotationId, sessionId: session_id },
|
|
538
|
+
null,
|
|
539
|
+
2
|
|
540
|
+
)
|
|
541
|
+
}
|
|
542
|
+
]
|
|
543
|
+
};
|
|
544
|
+
} catch (err) {
|
|
545
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
546
|
+
return {
|
|
547
|
+
content: [
|
|
548
|
+
{
|
|
549
|
+
type: "text",
|
|
550
|
+
text: `Error: ${message}`
|
|
551
|
+
}
|
|
552
|
+
],
|
|
553
|
+
isError: true
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
);
|
|
558
|
+
server.tool(
|
|
559
|
+
"get_review_state",
|
|
560
|
+
"Get the current state of a review session including session summary and annotations. Returns session metadata, status, and any agent annotations. Use this to check on a review's progress or read agent findings.",
|
|
561
|
+
{
|
|
562
|
+
session_id: z.string().optional().describe(
|
|
563
|
+
"Review session ID. If omitted, uses the most recently created session."
|
|
564
|
+
)
|
|
565
|
+
},
|
|
566
|
+
async ({ session_id }) => {
|
|
567
|
+
try {
|
|
568
|
+
const sessionId = session_id ?? lastGlobalSessionId;
|
|
569
|
+
if (!sessionId) {
|
|
570
|
+
return {
|
|
571
|
+
content: [
|
|
572
|
+
{
|
|
573
|
+
type: "text",
|
|
574
|
+
text: "No session ID provided and no recent session available."
|
|
575
|
+
}
|
|
576
|
+
],
|
|
577
|
+
isError: true
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
const serverInfo = await isServerAlive();
|
|
581
|
+
if (!serverInfo) {
|
|
582
|
+
return {
|
|
583
|
+
content: [
|
|
584
|
+
{
|
|
585
|
+
type: "text",
|
|
586
|
+
text: "No global server running. Start one with `diffprism server`."
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
isError: true
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const [sessionResponse, annotationsResponse] = await Promise.all([
|
|
593
|
+
fetch(
|
|
594
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}`
|
|
595
|
+
),
|
|
596
|
+
fetch(
|
|
597
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`
|
|
598
|
+
)
|
|
599
|
+
]);
|
|
600
|
+
if (!sessionResponse.ok) {
|
|
601
|
+
return {
|
|
602
|
+
content: [
|
|
603
|
+
{
|
|
604
|
+
type: "text",
|
|
605
|
+
text: `Session not found: ${sessionId}`
|
|
606
|
+
}
|
|
607
|
+
],
|
|
608
|
+
isError: true
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
const session = await sessionResponse.json();
|
|
612
|
+
const annotations = annotationsResponse.ok ? await annotationsResponse.json() : { annotations: [] };
|
|
613
|
+
return {
|
|
614
|
+
content: [
|
|
615
|
+
{
|
|
616
|
+
type: "text",
|
|
617
|
+
text: JSON.stringify(
|
|
618
|
+
{
|
|
619
|
+
session,
|
|
620
|
+
annotations: annotations.annotations
|
|
621
|
+
},
|
|
622
|
+
null,
|
|
623
|
+
2
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
]
|
|
627
|
+
};
|
|
628
|
+
} catch (err) {
|
|
629
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
630
|
+
return {
|
|
631
|
+
content: [
|
|
632
|
+
{
|
|
633
|
+
type: "text",
|
|
634
|
+
text: `Error: ${message}`
|
|
635
|
+
}
|
|
636
|
+
],
|
|
637
|
+
isError: true
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
server.tool(
|
|
643
|
+
"flag_for_attention",
|
|
644
|
+
"Mark specific files in a review session for human attention. Posts warning annotations for each flagged file. Use this to highlight files that need careful human review. Requires a running global server (`diffprism server`).",
|
|
645
|
+
{
|
|
646
|
+
session_id: z.string().optional().describe(
|
|
647
|
+
"Review session ID. If omitted, uses the most recently created session."
|
|
648
|
+
),
|
|
649
|
+
files: z.array(
|
|
650
|
+
z.object({
|
|
651
|
+
path: z.string().describe("File path to flag for attention"),
|
|
652
|
+
reason: z.string().describe("Why this file needs human attention"),
|
|
653
|
+
line: z.number().optional().describe("Specific line to highlight (defaults to 1)")
|
|
654
|
+
})
|
|
655
|
+
).describe("Files to flag for human attention"),
|
|
656
|
+
source_agent: z.string().optional().describe("Agent identifier (e.g., 'security-reviewer')")
|
|
657
|
+
},
|
|
658
|
+
async ({ session_id, files, source_agent }) => {
|
|
659
|
+
try {
|
|
660
|
+
const sessionId = session_id ?? lastGlobalSessionId;
|
|
661
|
+
if (!sessionId) {
|
|
662
|
+
return {
|
|
663
|
+
content: [
|
|
664
|
+
{
|
|
665
|
+
type: "text",
|
|
666
|
+
text: "No session ID provided and no recent session available."
|
|
667
|
+
}
|
|
668
|
+
],
|
|
669
|
+
isError: true
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
const serverInfo = await isServerAlive();
|
|
673
|
+
if (!serverInfo) {
|
|
674
|
+
return {
|
|
675
|
+
content: [
|
|
676
|
+
{
|
|
677
|
+
type: "text",
|
|
678
|
+
text: "No global server running. Start one with `diffprism server`."
|
|
679
|
+
}
|
|
680
|
+
],
|
|
681
|
+
isError: true
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
let flagged = 0;
|
|
685
|
+
for (const file of files) {
|
|
686
|
+
const response = await fetch(
|
|
687
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`,
|
|
688
|
+
{
|
|
689
|
+
method: "POST",
|
|
690
|
+
headers: { "Content-Type": "application/json" },
|
|
691
|
+
body: JSON.stringify({
|
|
692
|
+
file: file.path,
|
|
693
|
+
line: file.line ?? 1,
|
|
694
|
+
body: file.reason,
|
|
695
|
+
type: "warning",
|
|
696
|
+
confidence: 1,
|
|
697
|
+
category: "other",
|
|
698
|
+
source: {
|
|
699
|
+
agent: source_agent ?? "flag_for_attention",
|
|
700
|
+
tool: "flag_for_attention"
|
|
701
|
+
}
|
|
702
|
+
})
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
if (response.ok) {
|
|
706
|
+
flagged++;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
content: [
|
|
711
|
+
{
|
|
712
|
+
type: "text",
|
|
713
|
+
text: JSON.stringify({ flagged, sessionId }, null, 2)
|
|
714
|
+
}
|
|
715
|
+
]
|
|
716
|
+
};
|
|
717
|
+
} catch (err) {
|
|
718
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
719
|
+
return {
|
|
720
|
+
content: [
|
|
721
|
+
{
|
|
722
|
+
type: "text",
|
|
723
|
+
text: `Error: ${message}`
|
|
724
|
+
}
|
|
725
|
+
],
|
|
726
|
+
isError: true
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
);
|
|
454
731
|
const transport = new StdioServerTransport();
|
|
455
732
|
await server.connect(transport);
|
|
456
733
|
}
|