diffprism 0.44.0 → 0.45.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 +101 -38
- package/dist/bin.js +61 -70
- package/dist/{chunk-4IQOTAHD.js → chunk-RICVPPTE.js} +136 -0
- package/dist/{chunk-LH6H3QSC.js → chunk-RVI74JF5.js} +1 -1
- package/dist/{demo-MDPKCAXZ.js → demo-WVNNI6CE.js} +2 -2
- package/dist/mcp-server.js +430 -72
- package/dist/src-KF5HRJPX.js +23 -0
- package/package.json +1 -1
- package/ui-dist/assets/index-AgilKYdG.css +1 -0
- package/ui-dist/assets/index-BV9fRDP_.js +335 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-DOhngwbV.css +0 -1
- package/ui-dist/assets/index-Dw_IuVjX.js +0 -335
- /package/dist/{chunk-6J6PSBL2.js → chunk-24B33UN6.js} +0 -0
package/dist/mcp-server.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createGitHubClient,
|
|
3
|
-
fetchPullRequest,
|
|
4
|
-
fetchPullRequestDiff,
|
|
5
2
|
isPrRef,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
resolveGitHubToken,
|
|
9
|
-
submitGitHubReview
|
|
10
|
-
} from "./chunk-6J6PSBL2.js";
|
|
3
|
+
parsePrRef
|
|
4
|
+
} from "./chunk-24B33UN6.js";
|
|
11
5
|
import {
|
|
12
6
|
ensureServer,
|
|
13
7
|
isServerAlive,
|
|
14
8
|
submitReviewToServer
|
|
15
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RICVPPTE.js";
|
|
16
10
|
import {
|
|
17
11
|
getDiff
|
|
18
12
|
} from "./chunk-QGWYCEJN.js";
|
|
@@ -27,6 +21,24 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
27
21
|
import { z } from "zod";
|
|
28
22
|
var lastGlobalSessionId = null;
|
|
29
23
|
var lastGlobalServerInfo = null;
|
|
24
|
+
async function resolveSessionId(explicitId, serverInfo) {
|
|
25
|
+
if (explicitId) return explicitId;
|
|
26
|
+
if (lastGlobalSessionId) return lastGlobalSessionId;
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch(
|
|
29
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews`
|
|
30
|
+
);
|
|
31
|
+
if (response.ok) {
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
if (data.sessions.length > 0) {
|
|
34
|
+
const sorted = [...data.sessions].sort((a, b) => b.createdAt - a.createdAt);
|
|
35
|
+
return sorted[0].id;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
30
42
|
async function handleLocalReview(diffRef, options) {
|
|
31
43
|
const serverInfo = await ensureServer({ silent: true });
|
|
32
44
|
const { result, sessionId } = await submitReviewToServer(
|
|
@@ -67,83 +79,63 @@ async function handleLocalReview(diffRef, options) {
|
|
|
67
79
|
};
|
|
68
80
|
}
|
|
69
81
|
async function handlePrReview(pr, options) {
|
|
70
|
-
const token = resolveGitHubToken();
|
|
71
82
|
const { owner, repo, number } = parsePrRef(pr);
|
|
72
|
-
const client = createGitHubClient(token);
|
|
73
|
-
const [prMetadata, rawDiff] = await Promise.all([
|
|
74
|
-
fetchPullRequest(client, owner, repo, number),
|
|
75
|
-
fetchPullRequestDiff(client, owner, repo, number)
|
|
76
|
-
]);
|
|
77
|
-
if (!rawDiff.trim()) {
|
|
78
|
-
return {
|
|
79
|
-
mcpResult: {
|
|
80
|
-
content: [{
|
|
81
|
-
type: "text",
|
|
82
|
-
text: JSON.stringify({
|
|
83
|
-
decision: "approved",
|
|
84
|
-
comments: [],
|
|
85
|
-
summary: "PR has no changes to review."
|
|
86
|
-
}, null, 2)
|
|
87
|
-
}]
|
|
88
|
-
},
|
|
89
|
-
sessionId: "",
|
|
90
|
-
serverInfo: { httpPort: 0, wsPort: 0, pid: 0, startedAt: 0 }
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
const { payload } = normalizePr(rawDiff, prMetadata, {
|
|
94
|
-
title: options.title,
|
|
95
|
-
reasoning: options.reasoning
|
|
96
|
-
});
|
|
97
83
|
const serverInfo = await ensureServer({ silent: true });
|
|
98
|
-
const
|
|
99
|
-
serverInfo
|
|
100
|
-
`PR #${number}`,
|
|
84
|
+
const response = await fetch(
|
|
85
|
+
`http://localhost:${serverInfo.httpPort}/api/pr/open`,
|
|
101
86
|
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
timeoutMs: options.timeoutMs ?? 0
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify({ prUrl: pr })
|
|
106
90
|
}
|
|
107
91
|
);
|
|
108
|
-
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
if (!response.ok || !data.sessionId) {
|
|
109
94
|
return {
|
|
110
95
|
mcpResult: {
|
|
111
|
-
content: [{
|
|
112
|
-
|
|
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
|
-
}]
|
|
96
|
+
content: [{ type: "text", text: `Error: ${data.error ?? "Failed to open PR"}` }],
|
|
97
|
+
isError: true
|
|
120
98
|
},
|
|
121
|
-
sessionId,
|
|
99
|
+
sessionId: "",
|
|
122
100
|
serverInfo
|
|
123
101
|
};
|
|
124
102
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
103
|
+
const sessionId = data.sessionId;
|
|
104
|
+
if (options.timeoutMs && options.timeoutMs > 0) {
|
|
105
|
+
const start = Date.now();
|
|
106
|
+
while (Date.now() - start < options.timeoutMs) {
|
|
107
|
+
const resultResponse = await fetch(
|
|
108
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/result`
|
|
109
|
+
);
|
|
110
|
+
if (resultResponse.ok) {
|
|
111
|
+
const resultData = await resultResponse.json();
|
|
112
|
+
if (resultData.result) {
|
|
113
|
+
return {
|
|
114
|
+
mcpResult: {
|
|
115
|
+
content: [{ type: "text", text: JSON.stringify(resultData.result, null, 2) }]
|
|
116
|
+
},
|
|
117
|
+
sessionId,
|
|
118
|
+
serverInfo
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
142
123
|
}
|
|
143
124
|
}
|
|
144
125
|
return {
|
|
145
126
|
mcpResult: {
|
|
146
|
-
content: [{
|
|
127
|
+
content: [{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: JSON.stringify({
|
|
130
|
+
status: "session_created",
|
|
131
|
+
sessionId,
|
|
132
|
+
pr: `${owner}/${repo}#${number}`,
|
|
133
|
+
fileCount: data.fileCount,
|
|
134
|
+
localRepoConnected: !!data.localRepoPath,
|
|
135
|
+
localRepoPath: data.localRepoPath,
|
|
136
|
+
message: "PR review session opened in DiffPrism. Use get_pr_context, get_file_diff, get_file_context to explore the changes. Use add_review_comment to post findings."
|
|
137
|
+
}, null, 2)
|
|
138
|
+
}]
|
|
147
139
|
},
|
|
148
140
|
sessionId,
|
|
149
141
|
serverInfo
|
|
@@ -152,7 +144,7 @@ async function handlePrReview(pr, options) {
|
|
|
152
144
|
async function startMcpServer() {
|
|
153
145
|
const server = new McpServer({
|
|
154
146
|
name: "diffprism",
|
|
155
|
-
version: true ? "0.
|
|
147
|
+
version: true ? "0.45.0" : "0.0.0-dev"
|
|
156
148
|
});
|
|
157
149
|
server.tool(
|
|
158
150
|
"open_review",
|
|
@@ -743,6 +735,372 @@ async function startMcpServer() {
|
|
|
743
735
|
}
|
|
744
736
|
}
|
|
745
737
|
);
|
|
738
|
+
server.tool(
|
|
739
|
+
"get_pr_context",
|
|
740
|
+
"Get a high-level overview of the active PR review session. Returns PR metadata (title, author, branches, URL), review briefing summary, file list with stats, and local repo path. Use this to orient yourself before diving into specific files.",
|
|
741
|
+
{
|
|
742
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
743
|
+
},
|
|
744
|
+
async ({ session_id }) => {
|
|
745
|
+
try {
|
|
746
|
+
const serverInfo = await isServerAlive();
|
|
747
|
+
if (!serverInfo) {
|
|
748
|
+
return {
|
|
749
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
750
|
+
isError: true
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
754
|
+
if (!sessionId) {
|
|
755
|
+
return {
|
|
756
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
757
|
+
isError: true
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
const response = await fetch(
|
|
761
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
|
|
762
|
+
);
|
|
763
|
+
if (!response.ok) {
|
|
764
|
+
return {
|
|
765
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
766
|
+
isError: true
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
const data = await response.json();
|
|
770
|
+
const { payload, projectPath } = data;
|
|
771
|
+
const result = {
|
|
772
|
+
sessionId,
|
|
773
|
+
projectPath,
|
|
774
|
+
localRepoConnected: !projectPath.startsWith("github:"),
|
|
775
|
+
pr: payload.metadata.githubPr ?? null,
|
|
776
|
+
title: payload.metadata.title,
|
|
777
|
+
description: payload.metadata.description,
|
|
778
|
+
briefingSummary: payload.briefing.summary,
|
|
779
|
+
triage: payload.briefing.triage,
|
|
780
|
+
files: payload.diffSet.files.map((f) => ({
|
|
781
|
+
path: f.path,
|
|
782
|
+
status: f.status,
|
|
783
|
+
additions: f.additions,
|
|
784
|
+
deletions: f.deletions,
|
|
785
|
+
language: f.language
|
|
786
|
+
})),
|
|
787
|
+
totalFiles: payload.diffSet.files.length
|
|
788
|
+
};
|
|
789
|
+
return {
|
|
790
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
791
|
+
};
|
|
792
|
+
} catch (err) {
|
|
793
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
794
|
+
return {
|
|
795
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
796
|
+
isError: true
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
);
|
|
801
|
+
server.tool(
|
|
802
|
+
"get_file_diff",
|
|
803
|
+
"Get the diff hunks for a specific file in the active review session. Returns the file's changes (additions, deletions, hunks with line-level detail) and its briefing categorization. Use this to focus on one file at a time.",
|
|
804
|
+
{
|
|
805
|
+
file: z.string().describe("File path within the diff (e.g., 'src/index.ts')"),
|
|
806
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
807
|
+
},
|
|
808
|
+
async ({ file, session_id }) => {
|
|
809
|
+
try {
|
|
810
|
+
const serverInfo = await isServerAlive();
|
|
811
|
+
if (!serverInfo) {
|
|
812
|
+
return {
|
|
813
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
814
|
+
isError: true
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
818
|
+
if (!sessionId) {
|
|
819
|
+
return {
|
|
820
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
821
|
+
isError: true
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const response = await fetch(
|
|
825
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
|
|
826
|
+
);
|
|
827
|
+
if (!response.ok) {
|
|
828
|
+
return {
|
|
829
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
830
|
+
isError: true
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
const data = await response.json();
|
|
834
|
+
const diffFile = data.payload.diffSet.files.find((f) => f.path === file);
|
|
835
|
+
if (!diffFile) {
|
|
836
|
+
const available = data.payload.diffSet.files.map((f) => f.path);
|
|
837
|
+
return {
|
|
838
|
+
content: [{ type: "text", text: `File not found in diff: "${file}". Available files:
|
|
839
|
+
${available.join("\n")}` }],
|
|
840
|
+
isError: true
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
const { triage } = data.payload.briefing;
|
|
844
|
+
let category = "mechanical";
|
|
845
|
+
if (triage.critical.some((c) => c.file === file)) category = "critical";
|
|
846
|
+
else if (triage.notable.some((n) => n.file === file)) category = "notable";
|
|
847
|
+
return {
|
|
848
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
849
|
+
path: diffFile.path,
|
|
850
|
+
oldPath: diffFile.oldPath,
|
|
851
|
+
status: diffFile.status,
|
|
852
|
+
language: diffFile.language,
|
|
853
|
+
additions: diffFile.additions,
|
|
854
|
+
deletions: diffFile.deletions,
|
|
855
|
+
triageCategory: category,
|
|
856
|
+
hunks: diffFile.hunks
|
|
857
|
+
}, null, 2) }]
|
|
858
|
+
};
|
|
859
|
+
} catch (err) {
|
|
860
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
861
|
+
return {
|
|
862
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
863
|
+
isError: true
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
);
|
|
868
|
+
server.tool(
|
|
869
|
+
"get_file_context",
|
|
870
|
+
"Get the full content of a file from the local repository. Uses `git show` to read the file at the PR's head branch without switching branches. Requires the review session to be connected to a local repo (server must be running from within the repo clone).",
|
|
871
|
+
{
|
|
872
|
+
file: z.string().describe("File path relative to repo root (e.g., 'src/index.ts')"),
|
|
873
|
+
ref: z.string().optional().describe("Git ref to read from (e.g., 'origin/main', 'HEAD'). Defaults to the PR's head branch if available, otherwise HEAD."),
|
|
874
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
875
|
+
},
|
|
876
|
+
async ({ file, ref, session_id }) => {
|
|
877
|
+
try {
|
|
878
|
+
const serverInfo = await isServerAlive();
|
|
879
|
+
if (!serverInfo) {
|
|
880
|
+
return {
|
|
881
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
882
|
+
isError: true
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
886
|
+
if (!sessionId) {
|
|
887
|
+
return {
|
|
888
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
889
|
+
isError: true
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
const response = await fetch(
|
|
893
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
|
|
894
|
+
);
|
|
895
|
+
if (!response.ok) {
|
|
896
|
+
return {
|
|
897
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
898
|
+
isError: true
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const data = await response.json();
|
|
902
|
+
if (data.projectPath.startsWith("github:")) {
|
|
903
|
+
return {
|
|
904
|
+
content: [{ type: "text", text: "No local repo connected. Run the server from within a local clone of the repository to enable file context." }],
|
|
905
|
+
isError: true
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
const gitRef = ref ?? (data.payload.metadata.githubPr?.headBranch ? `origin/${data.payload.metadata.githubPr.headBranch}` : "HEAD");
|
|
909
|
+
const { execSync } = await import("child_process");
|
|
910
|
+
let content;
|
|
911
|
+
try {
|
|
912
|
+
content = execSync(`git show ${gitRef}:${file}`, {
|
|
913
|
+
cwd: data.projectPath,
|
|
914
|
+
encoding: "utf-8",
|
|
915
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
916
|
+
maxBuffer: 10 * 1024 * 1024
|
|
917
|
+
// 10MB
|
|
918
|
+
});
|
|
919
|
+
} catch {
|
|
920
|
+
const fs = await import("fs");
|
|
921
|
+
const path = await import("path");
|
|
922
|
+
const filePath = path.join(data.projectPath, file);
|
|
923
|
+
try {
|
|
924
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
925
|
+
} catch {
|
|
926
|
+
return {
|
|
927
|
+
content: [{ type: "text", text: `File not found: "${file}" (tried git show ${gitRef}:${file} and working tree)` }],
|
|
928
|
+
isError: true
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
934
|
+
file,
|
|
935
|
+
ref: gitRef,
|
|
936
|
+
projectPath: data.projectPath,
|
|
937
|
+
content,
|
|
938
|
+
lineCount: content.split("\n").length
|
|
939
|
+
}, null, 2) }]
|
|
940
|
+
};
|
|
941
|
+
} catch (err) {
|
|
942
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
943
|
+
return {
|
|
944
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
945
|
+
isError: true
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
);
|
|
950
|
+
server.tool(
|
|
951
|
+
"add_review_comment",
|
|
952
|
+
"Post a review comment to the active session. The comment appears in the DiffPrism browser UI in real-time as an inline annotation on the diff. Use this to leave findings, suggestions, or questions about specific lines of code.",
|
|
953
|
+
{
|
|
954
|
+
file: z.string().describe("File path within the diff"),
|
|
955
|
+
line: z.number().describe("Line number to comment on"),
|
|
956
|
+
body: z.string().describe("The comment text"),
|
|
957
|
+
type: z.enum(["comment", "suggestion", "concern"]).optional().describe("Type of comment (default: 'comment')"),
|
|
958
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
959
|
+
},
|
|
960
|
+
async ({ file, line, body, type, session_id }) => {
|
|
961
|
+
try {
|
|
962
|
+
const serverInfo = await isServerAlive();
|
|
963
|
+
if (!serverInfo) {
|
|
964
|
+
return {
|
|
965
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
966
|
+
isError: true
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
970
|
+
if (!sessionId) {
|
|
971
|
+
return {
|
|
972
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
973
|
+
isError: true
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
const annotationType = type === "concern" ? "warning" : type === "suggestion" ? "suggestion" : "finding";
|
|
977
|
+
const response = await fetch(
|
|
978
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`,
|
|
979
|
+
{
|
|
980
|
+
method: "POST",
|
|
981
|
+
headers: { "Content-Type": "application/json" },
|
|
982
|
+
body: JSON.stringify({
|
|
983
|
+
file,
|
|
984
|
+
line,
|
|
985
|
+
body,
|
|
986
|
+
type: annotationType,
|
|
987
|
+
confidence: 1,
|
|
988
|
+
category: "other",
|
|
989
|
+
source: {
|
|
990
|
+
agent: "ai-reviewer",
|
|
991
|
+
tool: "add_review_comment"
|
|
992
|
+
}
|
|
993
|
+
})
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
if (!response.ok) {
|
|
997
|
+
const errorData = await response.json().catch(() => ({}));
|
|
998
|
+
return {
|
|
999
|
+
content: [{ type: "text", text: `Error: ${errorData.error ?? `Server returned ${response.status}`}` }],
|
|
1000
|
+
isError: true
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
const data = await response.json();
|
|
1004
|
+
return {
|
|
1005
|
+
content: [{ type: "text", text: JSON.stringify({ annotationId: data.annotationId, sessionId }, null, 2) }]
|
|
1006
|
+
};
|
|
1007
|
+
} catch (err) {
|
|
1008
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1009
|
+
return {
|
|
1010
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1011
|
+
isError: true
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
);
|
|
1016
|
+
server.tool(
|
|
1017
|
+
"get_review_comments",
|
|
1018
|
+
"Get all comments and annotations on the active review session. Returns findings from agents and inline comments from human reviewers. Use this to see what has already been noted before adding your own comments.",
|
|
1019
|
+
{
|
|
1020
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
1021
|
+
},
|
|
1022
|
+
async ({ session_id }) => {
|
|
1023
|
+
try {
|
|
1024
|
+
const serverInfo = await isServerAlive();
|
|
1025
|
+
if (!serverInfo) {
|
|
1026
|
+
return {
|
|
1027
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
1028
|
+
isError: true
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
1032
|
+
if (!sessionId) {
|
|
1033
|
+
return {
|
|
1034
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
1035
|
+
isError: true
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
const response = await fetch(
|
|
1039
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`
|
|
1040
|
+
);
|
|
1041
|
+
if (!response.ok) {
|
|
1042
|
+
return {
|
|
1043
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
1044
|
+
isError: true
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
const data = await response.json();
|
|
1048
|
+
return {
|
|
1049
|
+
content: [{ type: "text", text: JSON.stringify({ sessionId, annotations: data.annotations }, null, 2) }]
|
|
1050
|
+
};
|
|
1051
|
+
} catch (err) {
|
|
1052
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1053
|
+
return {
|
|
1054
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1055
|
+
isError: true
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
server.tool(
|
|
1061
|
+
"get_user_focus",
|
|
1062
|
+
"Get what the user is currently looking at in the DiffPrism review UI. Returns the file they have selected and any line range they are focused on. Use this to provide context-aware help \u2014 answer questions about the code the user is actively reviewing.",
|
|
1063
|
+
{
|
|
1064
|
+
session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
|
|
1065
|
+
},
|
|
1066
|
+
async ({ session_id }) => {
|
|
1067
|
+
try {
|
|
1068
|
+
const serverInfo = await isServerAlive();
|
|
1069
|
+
if (!serverInfo) {
|
|
1070
|
+
return {
|
|
1071
|
+
content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
|
|
1072
|
+
isError: true
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
const sessionId = await resolveSessionId(session_id, serverInfo);
|
|
1076
|
+
if (!sessionId) {
|
|
1077
|
+
return {
|
|
1078
|
+
content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
|
|
1079
|
+
isError: true
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
const response = await fetch(
|
|
1083
|
+
`http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/focus`
|
|
1084
|
+
);
|
|
1085
|
+
if (!response.ok) {
|
|
1086
|
+
return {
|
|
1087
|
+
content: [{ type: "text", text: `Session not found: ${sessionId}` }],
|
|
1088
|
+
isError: true
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
const data = await response.json();
|
|
1092
|
+
return {
|
|
1093
|
+
content: [{ type: "text", text: JSON.stringify({ sessionId, ...data }, null, 2) }]
|
|
1094
|
+
};
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1097
|
+
return {
|
|
1098
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
1099
|
+
isError: true
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
746
1104
|
const transport = new StdioServerTransport();
|
|
747
1105
|
await server.connect(transport);
|
|
748
1106
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGitHubClient,
|
|
3
|
+
fetchPullRequest,
|
|
4
|
+
fetchPullRequestDiff,
|
|
5
|
+
isPrRef,
|
|
6
|
+
normalizePr,
|
|
7
|
+
parsePrRef,
|
|
8
|
+
resolveGitHubToken,
|
|
9
|
+
submitGitHubReview
|
|
10
|
+
} from "./chunk-24B33UN6.js";
|
|
11
|
+
import "./chunk-QGWYCEJN.js";
|
|
12
|
+
import "./chunk-DHCVZGHE.js";
|
|
13
|
+
import "./chunk-JSBRDJBE.js";
|
|
14
|
+
export {
|
|
15
|
+
createGitHubClient,
|
|
16
|
+
fetchPullRequest,
|
|
17
|
+
fetchPullRequestDiff,
|
|
18
|
+
isPrRef,
|
|
19
|
+
normalizePr,
|
|
20
|
+
parsePrRef,
|
|
21
|
+
resolveGitHubToken,
|
|
22
|
+
submitGitHubReview
|
|
23
|
+
};
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--diff-background-color:initial;--diff-text-color:initial;--diff-font-family:Consolas,Courier,monospace;--diff-selection-background-color:#b3d7ff;--diff-selection-text-color:var(--diff-text-color);--diff-gutter-insert-background-color:#d6fedb;--diff-gutter-insert-text-color:var(--diff-text-color);--diff-gutter-delete-background-color:#fadde0;--diff-gutter-delete-text-color:var(--diff-text-color);--diff-gutter-selected-background-color:#fffce0;--diff-gutter-selected-text-color:var(--diff-text-color);--diff-code-insert-background-color:#eaffee;--diff-code-insert-text-color:var(--diff-text-color);--diff-code-delete-background-color:#fdeff0;--diff-code-delete-text-color:var(--diff-text-color);--diff-code-insert-edit-background-color:#c0dc91;--diff-code-insert-edit-text-color:var(--diff-text-color);--diff-code-delete-edit-background-color:#f39ea2;--diff-code-delete-edit-text-color:var(--diff-text-color);--diff-code-selected-background-color:#fffce0;--diff-code-selected-text-color:var(--diff-text-color);--diff-omit-gutter-line-color:#cb2a1d}.diff{background-color:var(--diff-background-color);border-collapse:collapse;color:var(--diff-text-color);table-layout:fixed;width:100%}.diff::-moz-selection{background-color:#b3d7ff;background-color:var(--diff-selection-background-color);color:var(--diff-text-color);color:var(--diff-selection-text-color)}.diff::selection{background-color:#b3d7ff;background-color:var(--diff-selection-background-color);color:var(--diff-text-color);color:var(--diff-selection-text-color)}.diff td{padding-bottom:0;padding-top:0;vertical-align:top}.diff-line{font-family:Consolas,Courier,monospace;font-family:var(--diff-font-family);line-height:1.5}.diff-gutter>a{color:inherit;display:block}.diff-gutter{cursor:pointer;padding:0 1ch;text-align:right;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diff-gutter-insert{background-color:#d6fedb;background-color:var(--diff-gutter-insert-background-color);color:var(--diff-text-color);color:var(--diff-gutter-insert-text-color)}.diff-gutter-delete{background-color:#fadde0;background-color:var(--diff-gutter-delete-background-color);color:var(--diff-text-color);color:var(--diff-gutter-delete-text-color)}.diff-gutter-omit{cursor:default}.diff-gutter-selected{background-color:#fffce0;background-color:var(--diff-gutter-selected-background-color);color:var(--diff-text-color);color:var(--diff-gutter-selected-text-color)}.diff-code{word-wrap:break-word;padding:0 0 0 .5em;white-space:pre-wrap;word-break:break-all}.diff-code-edit{color:inherit}.diff-code-insert{background-color:#eaffee;background-color:var(--diff-code-insert-background-color);color:var(--diff-text-color);color:var(--diff-code-insert-text-color)}.diff-code-insert .diff-code-edit{background-color:#c0dc91;background-color:var(--diff-code-insert-edit-background-color);color:var(--diff-text-color);color:var(--diff-code-insert-edit-text-color)}.diff-code-delete{background-color:#fdeff0;background-color:var(--diff-code-delete-background-color);color:var(--diff-text-color);color:var(--diff-code-delete-text-color)}.diff-code-delete .diff-code-edit{background-color:#f39ea2;background-color:var(--diff-code-delete-edit-background-color);color:var(--diff-text-color);color:var(--diff-code-delete-edit-text-color)}.diff-code-selected{background-color:#fffce0;background-color:var(--diff-code-selected-background-color);color:var(--diff-text-color);color:var(--diff-code-selected-text-color)}.diff-widget-content{vertical-align:top}.diff-gutter-col{width:7ch}.diff-gutter-omit{height:0}.diff-gutter-omit:before{background-color:#cb2a1d;background-color:var(--diff-omit-gutter-line-color);content:" ";display:block;height:100%;margin-left:4.6ch;overflow:hidden;white-space:pre;width:2px}.diff-decoration{line-height:1.5;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diff-decoration-content{font-family:Consolas,Courier,monospace;font-family:var(--diff-font-family);padding:0}*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.left-3{left:.75rem}.right-0{right:0}.right-1\.5{right:.375rem}.right-2{right:.5rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-full{top:100%}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-\[280px\]{max-height:280px}.max-h-\[40\%\]{max-height:40%}.max-h-\[80vh\]{max-height:80vh}.min-h-0{min-height:0px}.min-h-\[20px\]{min-height:20px}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-52{width:13rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[260px\]{width:260px}.w-\[280px\]{width:280px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.max-w-\[180px\]{max-width:180px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-3{row-gap:.75rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent{border-color:var(--color-accent)}.border-border{border-color:var(--color-border)}.border-transparent{border-color:transparent}.border-l-accent{border-left-color:var(--color-accent)}.border-l-transparent{border-left-color:transparent}.border-l-warning{border-left-color:var(--color-warning)}.border-r-border{border-right-color:var(--color-border)}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-background{background-color:var(--color-background)}.bg-black\/50{background-color:#00000080}.bg-border{background-color:var(--color-border)}.bg-info{background-color:var(--color-info)}.bg-success{background-color:var(--color-success)}.bg-surface{background-color:var(--color-surface)}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-3{padding-bottom:.75rem}.pl-6{padding-left:1.5rem}.pl-9{padding-left:2.25rem}.pr-6{padding-right:1.5rem}.pr-7{padding-right:1.75rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent{color:var(--color-accent)}.text-danger{color:var(--color-danger)}.text-info{color:var(--color-info)}.text-neutral{color:var(--color-neutral)}.text-perf{color:var(--color-perf)}.text-success{color:var(--color-success)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-warning{color:var(--color-warning)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.accent-accent{accent-color:var(--color-accent)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}:root{--color-background: #e5e9f2;--color-surface: #d8dde9;--color-border: #b3bbd0;--color-text-primary: #141828;--color-text-secondary: #434e65;--color-accent: #4f3fba;--color-diff-bg: #e5e9f2;--color-diff-text: #141828;--color-gutter-bg: #d8dde9;--color-gutter-text: #434e65;--color-gutter-border: #b3bbd0;--color-diff-insert-bg: rgba(30, 160, 120, .15);--color-diff-insert-gutter-bg: rgba(30, 160, 120, .22);--color-diff-insert-gutter-text: #14704f;--color-diff-delete-bg: rgba(210, 75, 65, .15);--color-diff-delete-gutter-bg: rgba(210, 75, 65, .22);--color-diff-delete-gutter-text: #b02e26;--color-hunk-bg: rgba(90, 72, 201, .1);--color-hunk-border: #b3bbd0;--color-hunk-gutter-bg: rgba(79, 63, 186, .15);--color-hunk-gutter-text: #4f3fba;--color-hunk-content-text: #434e65;--color-diff-edit-insert: rgba(30, 160, 120, .42);--color-diff-edit-delete: rgba(210, 75, 65, .42);--color-split-divider: #b3bbd0;--color-widget-bg: #d8dde9;--color-comment-btn-bg: #167a5c;--color-comment-indicator: #4f3fba;--color-annotation-indicator: #c07820;--color-scrollbar-track: #e5e9f2;--color-scrollbar-thumb: #b3bbd0;--color-scrollbar-thumb-hover: #98a2b8;--color-token-comment: #6b7a8f;--color-token-punctuation: #1a1f2e;--color-token-property: #0b6e82;--color-token-string: #0e7a5c;--color-token-operator: #c93c33;--color-token-keyword: #6e45c9;--color-token-function: #2870c9;--color-token-variable: #b06e18;--color-success: #0e7a5c;--color-danger: #c93c33;--color-warning: #b06e18;--color-info: #2870c9;--color-neutral: #6b7a8f;--color-perf: #c07820;--color-added: rgba(30, 160, 120, .4);--color-deleted: rgba(210, 75, 65, .4)}.dark{--color-background: #0b0f1a;--color-surface: #131829;--color-border: #252d3f;--color-text-primary: #dfe5f1;--color-text-secondary: #7585a3;--color-accent: #7c6bf0;--color-diff-bg: #0b0f1a;--color-diff-text: #dfe5f1;--color-gutter-bg: #111623;--color-gutter-text: #7585a3;--color-gutter-border: #252d3f;--color-diff-insert-bg: rgba(45, 185, 140, .12);--color-diff-insert-gutter-bg: rgba(45, 185, 140, .18);--color-diff-insert-gutter-text: #5ee0b2;--color-diff-delete-bg: rgba(235, 100, 90, .12);--color-diff-delete-gutter-bg: rgba(235, 100, 90, .18);--color-diff-delete-gutter-text: #f07068;--color-hunk-bg: rgba(124, 107, 240, .08);--color-hunk-border: #252d3f;--color-hunk-gutter-bg: rgba(124, 107, 240, .12);--color-hunk-gutter-text: #9688f0;--color-hunk-content-text: #7585a3;--color-diff-edit-insert: rgba(45, 185, 140, .35);--color-diff-edit-delete: rgba(235, 100, 90, .35);--color-split-divider: #252d3f;--color-widget-bg: #131829;--color-comment-btn-bg: #2db98c;--color-comment-indicator: #7c6bf0;--color-annotation-indicator: #e0a050;--color-scrollbar-track: #0b0f1a;--color-scrollbar-thumb: #252d3f;--color-scrollbar-thumb-hover: #3a4560;--color-token-comment: #5f6f8a;--color-token-punctuation: #bec8db;--color-token-property: #56c8e0;--color-token-string: #42c9a2;--color-token-operator: #f07a6e;--color-token-keyword: #b08df0;--color-token-function: #6aabf7;--color-token-variable: #e0a050;--color-success: #42c9a2;--color-danger: #f07a6e;--color-warning: #e0a050;--color-info: #6aabf7;--color-neutral: #5f6f8a;--color-perf: #e89040;--color-added: rgba(45, 185, 140, .4);--color-deleted: rgba(235, 100, 90, .4)}.diff-unified,.diff-split{background-color:var(--color-diff-bg);color:var(--color-diff-text);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:13px;line-height:20px}.diff-unified .diff-gutter,.diff-split .diff-gutter{background-color:var(--color-gutter-bg);color:var(--color-gutter-text);border-right:1px solid var(--color-gutter-border);padding:0 8px;min-width:50px;text-align:right;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:default}.diff-unified .diff-gutter-col,.diff-split .diff-gutter-col{width:60px;min-width:60px}.diff-unified .diff-code,.diff-split .diff-code{padding:0 12px;white-space:pre}.diff-unified .diff-code-insert,.diff-split .diff-code-insert{background-color:var(--color-diff-insert-bg)}.diff-unified .diff-code-insert .diff-code-text,.diff-split .diff-code-insert .diff-code-text{background-color:transparent}.diff-unified .diff-gutter-insert,.diff-split .diff-gutter-insert{background-color:var(--color-diff-insert-gutter-bg);color:var(--color-diff-insert-gutter-text)}.diff-unified .diff-code-delete,.diff-split .diff-code-delete{background-color:var(--color-diff-delete-bg)}.diff-unified .diff-code-delete .diff-code-text,.diff-split .diff-code-delete .diff-code-text{background-color:transparent}.diff-unified .diff-gutter-delete,.diff-split .diff-gutter-delete{background-color:var(--color-diff-delete-gutter-bg);color:var(--color-diff-delete-gutter-text)}.diff-unified .diff-code-normal,.diff-split .diff-code-normal{background-color:transparent}.diff-unified .diff-gutter-normal,.diff-split .diff-gutter-normal{background-color:var(--color-gutter-bg)}.diff-unified .diff-hunk-header,.diff-split .diff-hunk-header{background-color:var(--color-hunk-bg);border-top:1px solid var(--color-hunk-border);border-bottom:1px solid var(--color-hunk-border)}.diff-unified .diff-hunk-header-gutter,.diff-split .diff-hunk-header-gutter{background-color:var(--color-hunk-gutter-bg);color:var(--color-hunk-gutter-text)}.diff-unified .diff-hunk-header-content,.diff-split .diff-hunk-header-content{color:var(--color-hunk-content-text);padding:4px 12px;font-style:italic}.diff-unified .diff-code-edit .diff-code-text .diff-code-edit-text,.diff-split .diff-code-edit .diff-code-text .diff-code-edit-text{background-color:var(--color-diff-edit-insert);border-radius:2px}.diff-unified .diff-code-delete .diff-code-text .diff-code-edit-text,.diff-split .diff-code-delete .diff-code-text .diff-code-edit-text{background-color:var(--color-diff-edit-delete);border-radius:2px}.diff-unified table,.diff-split table{width:100%;border-collapse:collapse;table-layout:fixed}.diff-unified td,.diff-split td{vertical-align:top}.diff-split .diff-split-side-new .diff-gutter{border-left:1px solid var(--color-split-divider)}.diff-unified .token.comment,.diff-unified .token.prolog,.diff-unified .token.doctype,.diff-unified .token.cdata,.diff-split .token.comment,.diff-split .token.prolog,.diff-split .token.doctype,.diff-split .token.cdata{color:var(--color-token-comment)}.diff-unified .token.punctuation,.diff-split .token.punctuation{color:var(--color-token-punctuation)}.diff-unified .token.property,.diff-unified .token.tag,.diff-unified .token.boolean,.diff-unified .token.number,.diff-unified .token.constant,.diff-unified .token.symbol,.diff-split .token.property,.diff-split .token.tag,.diff-split .token.boolean,.diff-split .token.number,.diff-split .token.constant,.diff-split .token.symbol{color:var(--color-token-property)}.diff-unified .token.selector,.diff-unified .token.attr-name,.diff-unified .token.string,.diff-unified .token.char,.diff-unified .token.builtin,.diff-split .token.selector,.diff-split .token.attr-name,.diff-split .token.string,.diff-split .token.char,.diff-split .token.builtin{color:var(--color-token-string)}.diff-unified .token.operator,.diff-unified .token.entity,.diff-unified .token.url,.diff-split .token.operator,.diff-split .token.entity,.diff-split .token.url{color:var(--color-token-operator)}.diff-unified .token.atrule,.diff-unified .token.attr-value,.diff-unified .token.keyword,.diff-split .token.atrule,.diff-split .token.attr-value,.diff-split .token.keyword{color:var(--color-token-keyword)}.diff-unified .token.function,.diff-unified .token.class-name,.diff-split .token.function,.diff-split .token.class-name{color:var(--color-token-function)}.diff-unified .token.regex,.diff-unified .token.important,.diff-unified .token.variable,.diff-split .token.regex,.diff-split .token.important,.diff-split .token.variable{color:var(--color-token-variable)}.diff-unified .token.string,.diff-split .token.string{color:var(--color-token-string)}.diff-hunk-focused{outline:2px solid var(--color-accent);outline-offset:-2px}.diff-unified .diff-gutter,.diff-split .diff-gutter{cursor:pointer;position:relative}.diff-gutter-add-comment{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:3px;background-color:var(--color-comment-btn-bg);color:#fff;font-size:12px;font-weight:700;line-height:1;position:absolute;left:2px;top:50%;transform:translateY(-50%)}.diff-comment-indicator{display:inline-block;width:6px;height:6px;border-radius:50%;background-color:var(--color-comment-indicator);position:absolute;left:4px;top:50%;transform:translateY(-50%)}.diff-annotation-indicator{display:inline-block;width:6px;height:6px;border-radius:50%;background-color:var(--color-annotation-indicator);position:absolute;left:4px;top:50%;transform:translateY(-50%)}.diff-widget{background-color:var(--color-widget-bg)}.diff-widget-content{padding:0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--color-scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--color-scrollbar-thumb);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--color-scrollbar-thumb-hover)}.placeholder\:text-text-secondary::-moz-placeholder{color:var(--color-text-secondary)}.placeholder\:text-text-secondary::placeholder{color:var(--color-text-secondary)}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-danger:hover{color:var(--color-danger)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-accent:focus{--tw-ring-color:var(--color-accent)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-text-primary{color:var(--color-text-primary)}.group\/annotation:hover .group-hover\/annotation\:opacity-100,.group\/comment:hover .group-hover\/comment\:opacity-100,.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:opacity-40{opacity:.4}
|