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