@d5render/cli 0.1.60 → 0.1.61
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/.github/instructions/severity.instructions.md +34 -19
- package/.skills/code-review/SKILL.md +32 -31
- package/.skills/devops/{README.md → SKILL.md} +21 -15
- package/README.md +32 -2
- package/bin/d5cli +205 -205
- package/bin/{copilot.js → mcpServer.js} +243 -204
- package/package.json +26 -22
- package/CHANGELOG.md +0 -10
|
@@ -18799,48 +18799,43 @@ var StdioServerTransport = class {
|
|
|
18799
18799
|
};
|
|
18800
18800
|
|
|
18801
18801
|
//#endregion
|
|
18802
|
-
//#region
|
|
18802
|
+
//#region packages/env.ts
|
|
18803
18803
|
const name = "d5_mcp_review_builtin";
|
|
18804
18804
|
const report = "report";
|
|
18805
18805
|
const getHash = "hash";
|
|
18806
|
-
const
|
|
18807
|
-
const file = "bin/copilot.js";
|
|
18806
|
+
const file = "bin/mcpServer.js";
|
|
18808
18807
|
const RUNTIME_CWD = join(dirname(fileURLToPath(import.meta.url)), "../");
|
|
18809
18808
|
const serveFile = join(RUNTIME_CWD, file);
|
|
18810
18809
|
const envJson = buildEnv();
|
|
18811
|
-
const
|
|
18812
|
-
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18818
|
-
|
|
18819
|
-
|
|
18820
|
-
|
|
18821
|
-
|
|
18822
|
-
|
|
18823
|
-
|
|
18824
|
-
|
|
18825
|
-
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
const tools = [
|
|
18830
|
-
"--additional-mcp-config",
|
|
18831
|
-
JSON.stringify({ mcpServers: { [name]: {
|
|
18832
|
-
type: "local",
|
|
18833
|
-
command: "node",
|
|
18834
|
-
args: [serveFile, `--customizenv=${JSON.stringify(envUsed)}`],
|
|
18835
|
-
tools: ["*"]
|
|
18836
|
-
} } }),
|
|
18837
|
-
"--allow-all-paths",
|
|
18838
|
-
"--allow-all-tools",
|
|
18839
|
-
"--deny-tool",
|
|
18840
|
-
"write",
|
|
18841
|
-
"--deny-tool",
|
|
18842
|
-
"github-mcp-server"
|
|
18810
|
+
const envConfigKeys = [
|
|
18811
|
+
"CI_SERVER_URL",
|
|
18812
|
+
"CI_PROJECT_PATH",
|
|
18813
|
+
"CI_PROJECT_ID",
|
|
18814
|
+
"CI_PROJECT_NAME",
|
|
18815
|
+
"CI_COMMIT_SHA",
|
|
18816
|
+
"CI_COMMIT_BEFORE_SHA",
|
|
18817
|
+
"CI_MERGE_REQUEST_IID",
|
|
18818
|
+
"CI_MERGE_REQUEST_TITLE",
|
|
18819
|
+
"CI_MERGE_REQUEST_DESCRIPTION",
|
|
18820
|
+
"JIRA_BASE_URL",
|
|
18821
|
+
"DINGTALK_WEBHOOK",
|
|
18822
|
+
"GITLAB_TOKEN",
|
|
18823
|
+
"JIRA_PAT",
|
|
18824
|
+
"JIRA_COOKIE",
|
|
18825
|
+
"JIRA_USERNAME",
|
|
18826
|
+
"JIRA_PASSWORD",
|
|
18827
|
+
"TOKEN_USAGE"
|
|
18843
18828
|
];
|
|
18829
|
+
const envUsed = Object.defineProperty({}, "JIRA_BASE_URL", {
|
|
18830
|
+
get: () => toEnv("CI_SERVER_URL", "http://jira.d5techs.com.cn"),
|
|
18831
|
+
enumerable: true,
|
|
18832
|
+
configurable: true
|
|
18833
|
+
});
|
|
18834
|
+
envConfigKeys.forEach((key) => Object.defineProperty(envUsed, key, {
|
|
18835
|
+
get: () => toEnv(key),
|
|
18836
|
+
enumerable: true,
|
|
18837
|
+
configurable: true
|
|
18838
|
+
}));
|
|
18844
18839
|
function toEnv(key, defaultValue) {
|
|
18845
18840
|
return envJson[key] || process.env[key] || defaultValue;
|
|
18846
18841
|
}
|
|
@@ -18850,6 +18845,7 @@ function buildEnv() {
|
|
|
18850
18845
|
if (envArg) envJson = JSON.parse(envArg.replace("--customizenv=", ""));
|
|
18851
18846
|
return envJson;
|
|
18852
18847
|
}
|
|
18848
|
+
const noMandatory = "dont't call under non-mandatory conditions";
|
|
18853
18849
|
|
|
18854
18850
|
//#endregion
|
|
18855
18851
|
//#region packages/gitlab/url.ts
|
|
@@ -18897,60 +18893,58 @@ const commits = () => {
|
|
|
18897
18893
|
if (api) return `${api}/repository/commits`;
|
|
18898
18894
|
};
|
|
18899
18895
|
|
|
18900
|
-
//#endregion
|
|
18901
|
-
//#region packages/gitlab/commit.ts
|
|
18902
|
-
const getCommits = () => {
|
|
18903
|
-
const url = pipelineMerge();
|
|
18904
|
-
if (url) return fetch(`${url}/commits`, { headers: buildHeaders() }).then((res) => {
|
|
18905
|
-
if (!res.ok) throw new Error("请求GitLab API失败" + res.statusText);
|
|
18906
|
-
return res.json();
|
|
18907
|
-
}).then((commits) => ({ content: [{
|
|
18908
|
-
type: "text",
|
|
18909
|
-
description: "commits from `merge pipeline`",
|
|
18910
|
-
text: `${commits.map((commit) => commit.id).join(",")}, the above review is from \`merge pipeline\` commits.`
|
|
18911
|
-
}] })).catch((error) => ({
|
|
18912
|
-
content: [{
|
|
18913
|
-
type: "text",
|
|
18914
|
-
text: "error " + (error.message || "请求GitLab API失败,请检查网络连接或访问权限")
|
|
18915
|
-
}],
|
|
18916
|
-
isError: true
|
|
18917
|
-
}));
|
|
18918
|
-
const { CI_COMMIT_SHA, CI_COMMIT_BEFORE_SHA } = envUsed;
|
|
18919
|
-
if (CI_COMMIT_SHA && CI_COMMIT_BEFORE_SHA) return { content: [{
|
|
18920
|
-
type: "text",
|
|
18921
|
-
description: "commit from push pipeline",
|
|
18922
|
-
text: `from ${CI_COMMIT_BEFORE_SHA} to ${CI_COMMIT_SHA}.`
|
|
18923
|
-
}] };
|
|
18924
|
-
return { content: [{
|
|
18925
|
-
type: "text",
|
|
18926
|
-
text: `latest commit`
|
|
18927
|
-
}] };
|
|
18928
|
-
};
|
|
18929
|
-
const getMergeInfomation = () => {
|
|
18930
|
-
const { CI_MERGE_REQUEST_DESCRIPTION, CI_MERGE_REQUEST_TITLE } = envUsed;
|
|
18931
|
-
if (CI_MERGE_REQUEST_DESCRIPTION || CI_MERGE_REQUEST_TITLE) return { content: [{
|
|
18932
|
-
type: "text",
|
|
18933
|
-
text: `title: ${CI_MERGE_REQUEST_TITLE || ""}, description: ${CI_MERGE_REQUEST_DESCRIPTION || ""}`
|
|
18934
|
-
}] };
|
|
18935
|
-
return {
|
|
18936
|
-
content: [{
|
|
18937
|
-
type: "text",
|
|
18938
|
-
text: "error"
|
|
18939
|
-
}],
|
|
18940
|
-
isError: true
|
|
18941
|
-
};
|
|
18942
|
-
};
|
|
18943
|
-
|
|
18944
18896
|
//#endregion
|
|
18945
18897
|
//#region packages/gitlab/index.ts
|
|
18946
18898
|
function install$2(server) {
|
|
18947
|
-
server.registerTool(getHash, { description: noMandatory },
|
|
18899
|
+
server.registerTool(getHash, { description: noMandatory }, () => {
|
|
18900
|
+
const url = pipelineMerge();
|
|
18901
|
+
if (url) return fetch(`${url}/commits`, { headers: buildHeaders() }).then((res) => {
|
|
18902
|
+
if (!res.ok) throw new Error("请求GitLab API失败" + res.statusText);
|
|
18903
|
+
return res.json();
|
|
18904
|
+
}).then((commits) => ({ content: [{
|
|
18905
|
+
type: "text",
|
|
18906
|
+
text: `${commits.map((commit) => commit.id).join(",")}, the above review is from \`merge pipeline\` commits.`
|
|
18907
|
+
}] })).catch(() => ({
|
|
18908
|
+
content: [{
|
|
18909
|
+
type: "text",
|
|
18910
|
+
text: "latest commit"
|
|
18911
|
+
}],
|
|
18912
|
+
isError: true
|
|
18913
|
+
}));
|
|
18914
|
+
const { CI_COMMIT_SHA, CI_COMMIT_BEFORE_SHA } = envUsed;
|
|
18915
|
+
if (CI_COMMIT_SHA && CI_COMMIT_BEFORE_SHA) return { content: [{
|
|
18916
|
+
type: "text",
|
|
18917
|
+
text: `from ${CI_COMMIT_BEFORE_SHA} to ${CI_COMMIT_SHA}.`
|
|
18918
|
+
}] };
|
|
18919
|
+
return { content: [{
|
|
18920
|
+
type: "text",
|
|
18921
|
+
text: `latest commit`
|
|
18922
|
+
}] };
|
|
18923
|
+
});
|
|
18948
18924
|
server.registerTool("getMergeDescription", {
|
|
18949
18925
|
title: "merge pipeline description",
|
|
18950
18926
|
description: "When review task from `merge pipeline`, get the merge request description."
|
|
18951
|
-
},
|
|
18927
|
+
}, () => {
|
|
18928
|
+
let { CI_MERGE_REQUEST_DESCRIPTION: CMED, CI_MERGE_REQUEST_TITLE: CMRT } = envUsed;
|
|
18929
|
+
if (CMED || CMRT) {
|
|
18930
|
+
CMED = CMED ? `, description: ${CMED || ""}` : "";
|
|
18931
|
+
CMRT = CMRT ? `title: ${CMRT || ""}` : "";
|
|
18932
|
+
return { content: [{
|
|
18933
|
+
type: "text",
|
|
18934
|
+
text: CMRT + CMED
|
|
18935
|
+
}] };
|
|
18936
|
+
}
|
|
18937
|
+
return { content: [{
|
|
18938
|
+
type: "text",
|
|
18939
|
+
text: ""
|
|
18940
|
+
}] };
|
|
18941
|
+
});
|
|
18952
18942
|
}
|
|
18953
18943
|
|
|
18944
|
+
//#endregion
|
|
18945
|
+
//#region packages/jira/schema.ts
|
|
18946
|
+
const jiraSchema = { key: string().describe("JIRA task number such as `FUS-123`") };
|
|
18947
|
+
|
|
18954
18948
|
//#endregion
|
|
18955
18949
|
//#region packages/jira/url.ts
|
|
18956
18950
|
function commonHeaders() {
|
|
@@ -18967,100 +18961,70 @@ function commonHeaders() {
|
|
|
18967
18961
|
}
|
|
18968
18962
|
|
|
18969
18963
|
//#endregion
|
|
18970
|
-
//#region packages/jira/
|
|
18971
|
-
|
|
18964
|
+
//#region packages/jira/index.ts
|
|
18965
|
+
function install$1(server) {
|
|
18966
|
+
server.registerTool("get_jira_context", {
|
|
18967
|
+
title: "get jira context",
|
|
18968
|
+
description: "When the submission message contains a JIRA task number (e.g., `FUS-123`), this tool will be used to retrieve the contents of the JIRA task and supplement the current context.",
|
|
18969
|
+
inputSchema: jiraSchema
|
|
18970
|
+
}, caller);
|
|
18971
|
+
}
|
|
18972
|
+
async function caller(request) {
|
|
18972
18973
|
const { key } = request;
|
|
18973
18974
|
const { JIRA_BASE_URL } = envUsed;
|
|
18974
|
-
|
|
18975
|
-
const
|
|
18975
|
+
try {
|
|
18976
|
+
const headers = commonHeaders();
|
|
18977
|
+
const res = await (await fetch(`${JIRA_BASE_URL}/rest/api/latest/issue/${key}`, { headers })).json();
|
|
18978
|
+
if (!res.fields) throw new Error("JIRA issue not found");
|
|
18979
|
+
const { fields = {} } = res;
|
|
18976
18980
|
const { comment = {}, description = "", summary = "", priority = {} } = fields;
|
|
18977
18981
|
const { comments = [] } = comment;
|
|
18978
|
-
|
|
18982
|
+
const content = [
|
|
18983
|
+
...summary ? [{
|
|
18984
|
+
type: "text",
|
|
18985
|
+
description: "summary of the issue",
|
|
18986
|
+
text: summary
|
|
18987
|
+
}] : [],
|
|
18988
|
+
...priority.name ? [{
|
|
18989
|
+
type: "text",
|
|
18990
|
+
description: "priority of the issue",
|
|
18991
|
+
text: priority.name
|
|
18992
|
+
}] : [],
|
|
18993
|
+
...description ? [{
|
|
18994
|
+
type: "text",
|
|
18995
|
+
description: "description of the issue",
|
|
18996
|
+
text: description
|
|
18997
|
+
}] : [],
|
|
18998
|
+
...comments.map((v) => ({
|
|
18999
|
+
type: "text",
|
|
19000
|
+
description: "one of comment of the issue, format like `author` - `comment`",
|
|
19001
|
+
text: (v.author?.displayName || "nobody") + " - " + v.body
|
|
19002
|
+
}))
|
|
19003
|
+
];
|
|
19004
|
+
if (content.length > 0) content.unshift({
|
|
18979
19005
|
type: "text",
|
|
18980
19006
|
description: `tools has find the JIRA issue ${key}`,
|
|
18981
19007
|
text: `JIRA ${key} find`
|
|
18982
|
-
}
|
|
18983
|
-
|
|
18984
|
-
description: "summary of the issue",
|
|
18985
|
-
text: summary
|
|
18986
|
-
}] : [], priority.name ? [{
|
|
18987
|
-
type: "text",
|
|
18988
|
-
description: "priority of the issue",
|
|
18989
|
-
text: priority.name
|
|
18990
|
-
}] : [], description ? [{
|
|
19008
|
+
});
|
|
19009
|
+
else content.push({
|
|
18991
19010
|
type: "text",
|
|
18992
|
-
description:
|
|
18993
|
-
text:
|
|
18994
|
-
}
|
|
19011
|
+
description: `tools has find the JIRA issue ${key}`,
|
|
19012
|
+
text: `JIRA ${key} find with no content`
|
|
19013
|
+
});
|
|
19014
|
+
return { content };
|
|
19015
|
+
} catch {
|
|
19016
|
+
return { content: [{
|
|
18995
19017
|
type: "text",
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
}).catch(() => ({ content: [{
|
|
19000
|
-
type: "text",
|
|
19001
|
-
text: `JIRA ${key} Non Exist.`
|
|
19002
|
-
}] }));
|
|
19003
|
-
};
|
|
19004
|
-
|
|
19005
|
-
//#endregion
|
|
19006
|
-
//#region packages/jira/schema.ts
|
|
19007
|
-
const jiraSchema = { key: string().describe("JIRA task number such as `FUS-123`") };
|
|
19008
|
-
|
|
19009
|
-
//#endregion
|
|
19010
|
-
//#region packages/jira/index.ts
|
|
19011
|
-
function install$1(server) {
|
|
19012
|
-
server.registerTool("get_jira_context", {
|
|
19013
|
-
title: "get jira context",
|
|
19014
|
-
description: "When the submission message contains a JIRA task number (e.g., `FUS-123`), this tool will be used to retrieve the contents of the JIRA task and supplement the current context.",
|
|
19015
|
-
inputSchema: jiraSchema
|
|
19016
|
-
}, getIssue);
|
|
19018
|
+
text: `JIRA ${key} Non Exist.`
|
|
19019
|
+
}] };
|
|
19020
|
+
}
|
|
19017
19021
|
}
|
|
19018
19022
|
|
|
19019
|
-
//#endregion
|
|
19020
|
-
//#region packages/message/props.ts
|
|
19021
|
-
const one = "If it can be explained in one sentence, then passing in one entry is sufficient.";
|
|
19022
|
-
const code = {
|
|
19023
|
-
file: string().optional().describe("The file path where the issue is found"),
|
|
19024
|
-
line: string().optional().describe("Line number, e.g., '1-345' pr '205' or '17,35', **must** provide the line of final file"),
|
|
19025
|
-
codeExample: string().optional().describe(`Code modification suggestions, recommended to use code in diff format, like: \`\`\`
|
|
19026
|
-
while (condition) {
|
|
19027
|
-
unchanged line;
|
|
19028
|
-
- remove this;
|
|
19029
|
-
+ replace it with this;
|
|
19030
|
-
+ and this;
|
|
19031
|
-
but keep this the same;
|
|
19032
|
-
}\`\`\`.`)
|
|
19033
|
-
};
|
|
19034
|
-
const severity = "such as CRITICAL, HIGH, MEDIUM, LOW";
|
|
19035
|
-
const reportSchema = {
|
|
19036
|
-
title: string().describe("Please describe the problem in the shortest possible way."),
|
|
19037
|
-
summary: array(string().describe(one)).optional().describe("Regarding the description of the current review, please summarize the main logic of the submission as concisely as possible."),
|
|
19038
|
-
issues: array(object({
|
|
19039
|
-
severity: string().optional().describe(`Bug severity levels, ${severity}.`),
|
|
19040
|
-
commitTitle: string().describe("The commit title associated with the issue"),
|
|
19041
|
-
commitAuthor: string().describe("The commit latest author associated with the issue"),
|
|
19042
|
-
commitSha: string().optional().describe("The commit SHA associated with the issue"),
|
|
19043
|
-
title: string().optional().describe("A short title for the issue"),
|
|
19044
|
-
details: array(string()).optional().describe("Details about the issue"),
|
|
19045
|
-
suggestions: array(string()).optional().describe("Improvement suggestion"),
|
|
19046
|
-
...code
|
|
19047
|
-
})).optional().describe(`List all bugs, order by 'severity' from heaviest to lightest, such as ${severity}`),
|
|
19048
|
-
jiras: array(object({
|
|
19049
|
-
key: string().describe("JIRA issue key, e.g., 'FUS-123'"),
|
|
19050
|
-
summary: string().describe("Summary of the JIRA issue")
|
|
19051
|
-
}).describe("Related JIRA issues")).optional().describe("List of related JIRA issues"),
|
|
19052
|
-
risks: array(string().describe(one).describe("A short summary of the risk")).optional().describe("After summarizing all the bugs, identify the main risks that could potentially impact the business."),
|
|
19053
|
-
suggestions: array(object({
|
|
19054
|
-
details: string().describe("Suggestions with the same meaning should be stated in one sentence; please do not increase the array length."),
|
|
19055
|
-
example: object(code).optional().describe("Pass in when you feel it is necessary to provide code.")
|
|
19056
|
-
})).optional().describe("After gaining a comprehensive understanding of the files involved in the commits, propose suggestions for code corrections beyond the diff.")
|
|
19057
|
-
};
|
|
19058
|
-
|
|
19059
19023
|
//#endregion
|
|
19060
19024
|
//#region packages/message/sendding.ts
|
|
19061
19025
|
async function sendding(title, text) {
|
|
19062
|
-
if (!envUsed.DINGTALK_WEBHOOK) throw new Error("non DINGTALK_WEBHOOK");
|
|
19063
19026
|
let res = new Response();
|
|
19027
|
+
if (!envUsed.DINGTALK_WEBHOOK) throw res;
|
|
19064
19028
|
for (const url of envUsed.DINGTALK_WEBHOOK.split(",")) res = await fetch(url, {
|
|
19065
19029
|
method: "POST",
|
|
19066
19030
|
headers: { "Content-Type": "application/json" },
|
|
@@ -19085,14 +19049,22 @@ const lastComment = { value: {} };
|
|
|
19085
19049
|
const schemaGitlabPipeline = "gitlab_pipeline";
|
|
19086
19050
|
const schemaGitlabCommit = "gitlab_commit";
|
|
19087
19051
|
const schemaDingdingTalk = "dingding_talk";
|
|
19052
|
+
const schemaRecord = "record";
|
|
19088
19053
|
function distReports(input) {
|
|
19089
|
-
const { CI_PROJECT_NAME = "", CI_COMMIT_SHA = "", JIRA_BASE_URL, CI_MERGE_REQUEST_IID, DINGTALK_WEBHOOK } = envUsed;
|
|
19090
|
-
const { title = "代码审查报告", summary = [], issues = [], jiras = [], risks = [], suggestions = [] } = input;
|
|
19054
|
+
const { CI_PROJECT_NAME = "", CI_COMMIT_SHA = "", JIRA_BASE_URL, CI_MERGE_REQUEST_IID, DINGTALK_WEBHOOK, TOKEN_USAGE = "" } = envUsed;
|
|
19055
|
+
const { title = "代码审查报告", summary: _summary = [], issues = [], jiras = [], risks: _risks = [], suggestions: _sugg = [] } = input;
|
|
19056
|
+
const usage = () => {
|
|
19057
|
+
if (TOKEN_USAGE) gitlabReportMessage += `\n\n> Last Token Usage: ${TOKEN_USAGE}`;
|
|
19058
|
+
};
|
|
19059
|
+
const summary = Array.isArray(_summary) ? _summary : [_summary];
|
|
19060
|
+
const risks = Array.isArray(_risks) ? _risks : [_risks];
|
|
19061
|
+
const suggestions = Array.isArray(_sugg) ? _sugg : [_sugg];
|
|
19091
19062
|
const mergeURL = pipelineMerge();
|
|
19092
19063
|
const commitsURL = commits();
|
|
19093
19064
|
let gitlabReportMessage = `## 📖 ${title}\n`;
|
|
19094
19065
|
const commitComments = [];
|
|
19095
19066
|
let dingdingReportMessage = "";
|
|
19067
|
+
let { url, type } = visitPipeline();
|
|
19096
19068
|
const gitlabReport = {
|
|
19097
19069
|
title: schemaGitlabPipeline,
|
|
19098
19070
|
report: [() => {
|
|
@@ -19116,8 +19088,10 @@ function distReports(input) {
|
|
|
19116
19088
|
method: "GET",
|
|
19117
19089
|
headers: buildHeaders()
|
|
19118
19090
|
})).then((res) => res.json()).then((res) => {
|
|
19119
|
-
|
|
19120
|
-
|
|
19091
|
+
if (res.length <= 0) return;
|
|
19092
|
+
const { notes = [] } = res.at(-1) || {};
|
|
19093
|
+
if (notes.length <= 0) return;
|
|
19094
|
+
lastComment.value = notes.at(-1) || {};
|
|
19121
19095
|
});
|
|
19122
19096
|
}
|
|
19123
19097
|
return Promise.resolve(void 0);
|
|
@@ -19141,15 +19115,31 @@ function distReports(input) {
|
|
|
19141
19115
|
});
|
|
19142
19116
|
}]
|
|
19143
19117
|
};
|
|
19118
|
+
let last_token_usage = Number(TOKEN_USAGE);
|
|
19119
|
+
last_token_usage = Number.isNaN(last_token_usage) ? null : last_token_usage;
|
|
19144
19120
|
const result = [
|
|
19145
19121
|
gitlabReport,
|
|
19146
19122
|
gitlabCommitReport,
|
|
19147
|
-
dingdingReport
|
|
19123
|
+
dingdingReport,
|
|
19124
|
+
{
|
|
19125
|
+
title: schemaRecord,
|
|
19126
|
+
report: [() => fetch("http://ai-code-review.d5techs.com.cn/api/code_review_logs", {
|
|
19127
|
+
method: "POST",
|
|
19128
|
+
headers: { "Content-Type": "application/json" },
|
|
19129
|
+
body: JSON.stringify({
|
|
19130
|
+
project_name: envUsed.CI_PROJECT_PATH || "unknown",
|
|
19131
|
+
commit_messages: url,
|
|
19132
|
+
last_token_usage,
|
|
19133
|
+
main_risk: risks.join("<br>"),
|
|
19134
|
+
review_result: gitlabReportMessage
|
|
19135
|
+
})
|
|
19136
|
+
})]
|
|
19137
|
+
}
|
|
19148
19138
|
];
|
|
19149
19139
|
if (issues.length === 0 && suggestions.length === 0) {
|
|
19150
|
-
gitlabReport.report = [];
|
|
19151
19140
|
gitlabCommitReport.report = [];
|
|
19152
19141
|
dingdingReport.report = [];
|
|
19142
|
+
usage();
|
|
19153
19143
|
return result;
|
|
19154
19144
|
}
|
|
19155
19145
|
const fileMap = /* @__PURE__ */ new Map();
|
|
@@ -19161,12 +19151,12 @@ function distReports(input) {
|
|
|
19161
19151
|
if (commitSha) {
|
|
19162
19152
|
const lines = line.split(",").map((line) => {
|
|
19163
19153
|
const str = line.trim().split("-");
|
|
19164
|
-
const num = Number.parseInt(str
|
|
19154
|
+
const num = Number.parseInt(str.at(-1), 10);
|
|
19165
19155
|
return Number.isNaN(num) ? void 0 : num;
|
|
19166
19156
|
}).filter((v) => v !== void 0);
|
|
19167
19157
|
const attach = file ? {
|
|
19168
19158
|
file,
|
|
19169
|
-
line: lines.length > 0 ? lines
|
|
19159
|
+
line: lines.length > 0 ? lines.at(-1) : void 0
|
|
19170
19160
|
} : {};
|
|
19171
19161
|
commitComments.push({
|
|
19172
19162
|
sha: commitSha,
|
|
@@ -19195,30 +19185,30 @@ function distReports(input) {
|
|
|
19195
19185
|
for (const [file, fileIssues] of fileMap) {
|
|
19196
19186
|
gitlabReportMessage += `\n-----\n\n#### 文件: ${file}`;
|
|
19197
19187
|
for (const issue of fileIssues) {
|
|
19198
|
-
const { severity = "NON", line = "NON", title: issueTitle = "NON", details = [], suggestions: issueSuggestions = [], codeExample = "" } = issue;
|
|
19188
|
+
const { severity = "NON", line = "NON", title: issueTitle = "NON", details = [], suggestions: issueSuggestions = [], codeExample = "", score = "60" } = issue;
|
|
19199
19189
|
gitlabReportMessage += `\n\n**${severity}** | ${line} | ${issueTitle}`;
|
|
19200
|
-
gitlabReportMessage += `\n-
|
|
19201
|
-
gitlabReportMessage += `\n-
|
|
19190
|
+
gitlabReportMessage += `\n- subagent 可信度:${score}`;
|
|
19191
|
+
gitlabReportMessage += `\n- 详情:${details.length === 1 ? ` ${details[0]}` : `\n - ${details.join("\n - ")}`}`;
|
|
19192
|
+
gitlabReportMessage += `\n- 建议:${issueSuggestions.length === 1 ? ` ${issueSuggestions[0]}` : `\n - ${issueSuggestions.join("\n - ")}`}`;
|
|
19202
19193
|
if (codeExample) gitlabReportMessage += `\n${toCode(codeExample)}\n`;
|
|
19203
19194
|
}
|
|
19204
19195
|
}
|
|
19205
19196
|
}
|
|
19206
19197
|
if (suggestions.length > 0) {
|
|
19207
|
-
gitlabReportMessage += "\n\n### 📈 其他建议\n
|
|
19198
|
+
gitlabReportMessage += "\n\n### 📈 其他建议\n";
|
|
19208
19199
|
let withNoFile = "", withFile = "";
|
|
19209
19200
|
for (const suggestion of suggestions) {
|
|
19210
|
-
const { details = "",
|
|
19211
|
-
const { file = "", line = "", codeExample = "" } = example || {};
|
|
19201
|
+
const { details = "", file = "", line = "", codeExample = "" } = typeof suggestion === "string" ? { details: suggestion } : suggestion;
|
|
19212
19202
|
if (file) {
|
|
19213
|
-
withFile += `\n-----\n\n#### 文件: ${file}`;
|
|
19214
|
-
|
|
19215
|
-
withFile += `\n
|
|
19216
|
-
if (codeExample) withFile += `\n${toCode(codeExample)}\n`;
|
|
19203
|
+
withFile += `\n\n-----\n\n#### 文件: ${file}\n`;
|
|
19204
|
+
withFile += `\n\n**${line === "" ? "文件整体逻辑问题" : line}** | ` + details;
|
|
19205
|
+
if (codeExample) withFile += `\n\n${toCode(codeExample)}\n`;
|
|
19217
19206
|
} else withNoFile += `\n - ${details}`;
|
|
19218
19207
|
}
|
|
19219
19208
|
gitlabReportMessage += withNoFile + withFile;
|
|
19220
19209
|
}
|
|
19221
|
-
|
|
19210
|
+
usage();
|
|
19211
|
+
let subCommitUrl = url;
|
|
19222
19212
|
if (DINGTALK_WEBHOOK) {
|
|
19223
19213
|
dingdingReportMessage = `### 📖 ${title}\n\n**共计**:${issues.length}个问题\n\n${risks.length > 0 ? "**主要风险**:\n\n" + risks.join("\n\n") + "\n\n" : "\n\n"}-----\n\n**项目名**:${CI_PROJECT_NAME}`;
|
|
19224
19214
|
if (type) {
|
|
@@ -19226,35 +19216,39 @@ function distReports(input) {
|
|
|
19226
19216
|
let { id: note = "" } = value || {};
|
|
19227
19217
|
note = note ? `#note_${note}` : "";
|
|
19228
19218
|
const commit = note ? "&评论" : "";
|
|
19229
|
-
|
|
19230
|
-
|
|
19231
|
-
|
|
19232
|
-
|
|
19233
|
-
|
|
19234
|
-
|
|
19235
|
-
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
|
|
19240
|
-
|
|
19241
|
-
|
|
19219
|
+
url += note;
|
|
19220
|
+
dingdingReportMessage += `${type === "merge_request" ? `\n\n**合并${commit}**:[${CI_MERGE_REQUEST_IID}](${url})` : `\n\n**提交${commit}**:[${CI_COMMIT_SHA.slice(0, 8)}](${url})`}`;
|
|
19221
|
+
}
|
|
19222
|
+
if (severityMap.size > 0) {
|
|
19223
|
+
dingdingReportMessage += `${commitsMap.size > 0 ? `\n\n含问题的commits:${[...commitsMap.keys()].map((v) => `[${v.slice(0, 8)}](${linkBase}/${v})`).join(", ")}` : ""}\n\n-----\n\n#### 🐞 Issues\n\n`;
|
|
19224
|
+
let bugLength = 0;
|
|
19225
|
+
const maxBugLength = 5;
|
|
19226
|
+
for (const [severity, severityIssues] of severityMap) {
|
|
19227
|
+
if (bugLength > maxBugLength) break;
|
|
19228
|
+
dingdingReportMessage += `\n\n**${severity}**`;
|
|
19229
|
+
for (const issue of severityIssues) {
|
|
19230
|
+
if (bugLength > maxBugLength) {
|
|
19231
|
+
dingdingReportMessage += `\n\n\n...以及其他 ${issues.length - bugLength} 个问题,[详见报告](${url})`;
|
|
19232
|
+
break;
|
|
19233
|
+
}
|
|
19234
|
+
const { title: issueTitle = "NON", commitSha = "", file = "NON", line = "NON", commitAuthor = "", score = "60" } = issue;
|
|
19235
|
+
const shortSha = (commitSha || "").slice(0, 8);
|
|
19236
|
+
const commitLink = linkBase && commitSha ? `${linkBase}/${commitSha}` : void 0;
|
|
19237
|
+
dingdingReportMessage += `\n- ${issueTitle}\n\n - score: ${score}\n\n - hash: ${commitLink ? `[${shortSha}](${commitLink}) ` : shortSha}\n\n - author: ${commitAuthor}\n\n - file: ${file}\n\n - line: ${line}`;
|
|
19238
|
+
bugLength++;
|
|
19242
19239
|
}
|
|
19243
|
-
const { title: issueTitle = "NON", commitSha = "", file = "NON", line = "NON", commitAuthor = "" } = issue;
|
|
19244
|
-
const shortSha = (commitSha || "").slice(0, 8);
|
|
19245
|
-
const commitLink = linkBase && commitSha ? `${linkBase}/${commitSha}` : void 0;
|
|
19246
|
-
dingdingReportMessage += `\n- ${issueTitle}\n\n - hash: ${commitLink ? `[${shortSha}](${commitLink}) ` : shortSha}\n\n - author: ${commitAuthor}\n\n - file: ${file}\n\n - line: ${line}`;
|
|
19247
|
-
bugLength++;
|
|
19248
19240
|
}
|
|
19241
|
+
} else {
|
|
19242
|
+
const { value = {} } = lastComment;
|
|
19243
|
+
let { id = "" } = value || {};
|
|
19244
|
+
dingdingReportMessage += `\n\n-----\n\n#### [**改进建议请查看**](${url}#note_${id})\n`;
|
|
19249
19245
|
}
|
|
19250
|
-
if (jiras.length > 0) dingdingReportMessage += `\n\n
|
|
19246
|
+
if (jiras.length > 0) dingdingReportMessage += `\n\n#### 🔗 JIRA \n\n\n\n${jiras.map((v) => `[${v.key}](${JIRA_BASE_URL}/browse/${v.key}): ${v.summary}`).join("\n\n")}`;
|
|
19251
19247
|
}
|
|
19252
19248
|
if (commitsURL) gitlabCommitReport.report = commitComments.map(({ sha, file, line, note }) => () => {
|
|
19253
19249
|
const { value = {} } = lastComment;
|
|
19254
19250
|
let { id = "" } = value || {};
|
|
19255
|
-
if (id) note +=
|
|
19256
|
-
|
|
19257
|
-
[**完整评论请查看**](${url}#note_${id})`;
|
|
19251
|
+
if (id) note += `\n\n[**完整评论请查看**](${subCommitUrl}#note_${id})`;
|
|
19258
19252
|
return fetch(`${commitsURL}/${sha}/comments`, {
|
|
19259
19253
|
method: "POST",
|
|
19260
19254
|
headers: buildHeaders(),
|
|
@@ -19275,13 +19269,58 @@ async function runReport(input) {
|
|
|
19275
19269
|
error: error.message || "未知错误"
|
|
19276
19270
|
})));
|
|
19277
19271
|
const error = results.find((v) => v.error);
|
|
19278
|
-
return error ? `error: ${error.title}\n${error.error}` : "✅
|
|
19272
|
+
return error ? `error: ${error.title}\n${error.error}` : "✅ Report Done";
|
|
19279
19273
|
}
|
|
19280
19274
|
function toCode(code) {
|
|
19281
19275
|
const ct = code.trim();
|
|
19282
|
-
return ct.startsWith("```") && ct.endsWith("```") ? code : `\`\`\`\n${code}\n\`\`\``;
|
|
19276
|
+
return ct.startsWith("```") && ct.endsWith("```") ? code.endsWith("\n```") ? code : code.slice(0, -3) + "\n```" : `\`\`\`\n${code}\n\`\`\``;
|
|
19283
19277
|
}
|
|
19284
19278
|
|
|
19279
|
+
//#endregion
|
|
19280
|
+
//#region packages/message/schema.ts
|
|
19281
|
+
const code = {
|
|
19282
|
+
file: string().optional().describe("The file path where the issue is found"),
|
|
19283
|
+
line: string().optional().describe("Line number, e.g., '1-345' pr '205' or '17,35', **must** provide the line of final file"),
|
|
19284
|
+
codeExample: string().optional().describe(`Code modification suggestions, recommended to use code in diff format, like: \`\`\`
|
|
19285
|
+
while (condition) {
|
|
19286
|
+
unchanged line;
|
|
19287
|
+
- remove this;
|
|
19288
|
+
+ replace it with this;
|
|
19289
|
+
+ and this;
|
|
19290
|
+
but keep this the same;
|
|
19291
|
+
}\`\`\`.`)
|
|
19292
|
+
};
|
|
19293
|
+
const severity = "such as CRITICAL, HIGH, MEDIUM, LOW";
|
|
19294
|
+
const nonInfo = "When the current change no longer exists in the latest code, please take the relevant information from the most recent change.";
|
|
19295
|
+
const reportSchema = {
|
|
19296
|
+
title: string().describe("Describe the problem in the shortest possible way."),
|
|
19297
|
+
summary: union([string(), array(string())]).optional().describe("Regarding the description of the current review, please summarize the main logic of the submission as concisely as possible. If you need to split the summary into multiple points, please use Markdown's splitting syntax such as <br> to do so."),
|
|
19298
|
+
issues: array(object({
|
|
19299
|
+
severity: string().optional().describe(`Issue severity levels, ${severity}.`),
|
|
19300
|
+
score: string().optional().describe(`Issue credibility score, from 0-100`),
|
|
19301
|
+
commitTitle: string().describe(`The commit title associated with the issue, ${nonInfo}`),
|
|
19302
|
+
commitAuthor: string().describe(`The commit latest author associated with the issue, ${nonInfo}`),
|
|
19303
|
+
commitSha: string().describe(`The commit SHA associated with the issue, ${nonInfo}`),
|
|
19304
|
+
title: string().optional().describe("A short title for the issue"),
|
|
19305
|
+
details: array(string()).optional().describe("Details about the issue"),
|
|
19306
|
+
suggestions: array(string()).optional().describe("Improvement suggestion"),
|
|
19307
|
+
...code
|
|
19308
|
+
})).optional().describe(`List all bugs involved in the modified code. **Must** only list the issues related to the modified parts. **Must** order by 'severity' from heaviest to lightest, ${severity}`),
|
|
19309
|
+
jiras: array(object({
|
|
19310
|
+
key: string().describe("JIRA issue key, e.g., 'FUS-123'"),
|
|
19311
|
+
summary: string().describe("Summary of the JIRA issue")
|
|
19312
|
+
}).describe("Related JIRA issues")).optional().describe("List JIRA issues in review"),
|
|
19313
|
+
risks: union([string(), array(string())]).optional().describe("After summarizing all the bugs, identify the main risks that could potentially impact the business."),
|
|
19314
|
+
suggestions: union([
|
|
19315
|
+
string(),
|
|
19316
|
+
array(string()),
|
|
19317
|
+
array(object({
|
|
19318
|
+
details: string().describe("Suggestions with the same meaning should be stated in one sentence; please do not increase the array length."),
|
|
19319
|
+
...code
|
|
19320
|
+
})).describe("For other issues in the code, and for issues with a score below 50, please post the bugs and suggested fixes here.")
|
|
19321
|
+
]).optional().describe("Please provide other suggestions for modifying the code or related code.")
|
|
19322
|
+
};
|
|
19323
|
+
|
|
19285
19324
|
//#endregion
|
|
19286
19325
|
//#region packages/message/index.ts
|
|
19287
19326
|
function install(server) {
|
|
@@ -19295,7 +19334,7 @@ function install(server) {
|
|
|
19295
19334
|
}
|
|
19296
19335
|
|
|
19297
19336
|
//#endregion
|
|
19298
|
-
//#region
|
|
19337
|
+
//#region packages/server/index.ts
|
|
19299
19338
|
const server = new McpServer({
|
|
19300
19339
|
name,
|
|
19301
19340
|
version: "1.0.0"
|
package/package.json
CHANGED
|
@@ -1,36 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@d5render/cli",
|
|
3
|
-
"
|
|
3
|
+
"version": "0.1.61",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "jasirou",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"devDependencies": {
|
|
9
|
-
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
10
|
-
"@types/node": "^25.0.3",
|
|
11
|
-
"@types/vscode": "^1.107.0",
|
|
12
|
-
"@vscode/vsce": "^3.7.1",
|
|
13
|
-
"json5": "^2.2.3",
|
|
14
|
-
"oxfmt": "^0.21.0",
|
|
15
|
-
"oxlint": "^1.37.0",
|
|
16
|
-
"rolldown": "1.0.0-beta.58",
|
|
17
|
-
"vitest": "^4.0.17",
|
|
18
|
-
"zod": "^4.3.5"
|
|
6
|
+
"bin": {
|
|
7
|
+
"d5cli": "bin/d5cli"
|
|
19
8
|
},
|
|
20
9
|
"files": [
|
|
21
|
-
".
|
|
22
|
-
".skills/devops",
|
|
10
|
+
"CHANGELOG.md",
|
|
23
11
|
".skills/code-review",
|
|
24
|
-
"
|
|
12
|
+
".skills/devops",
|
|
13
|
+
"bin/mcpServer.js",
|
|
25
14
|
"bin/d5cli",
|
|
26
|
-
"
|
|
15
|
+
".github/instructions/severity.instructions.md"
|
|
27
16
|
],
|
|
28
|
-
"
|
|
29
|
-
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./bin/d5cli",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"vscode-languageserver-protocol": "^3.17.5"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
24
|
+
"@types/node": "^25.1.0",
|
|
25
|
+
"@types/vscode": "^1.108.1",
|
|
26
|
+
"@vscode/vsce": "^3.7.1",
|
|
27
|
+
"json5": "^2.2.3",
|
|
28
|
+
"oxfmt": "^0.21.0",
|
|
29
|
+
"oxlint": "^1.42.0",
|
|
30
|
+
"rolldown": "1.0.0-rc.2",
|
|
31
|
+
"vitest": "^4.0.18",
|
|
32
|
+
"zod": "^4.3.6"
|
|
30
33
|
},
|
|
31
34
|
"scripts": {
|
|
32
|
-
"build
|
|
35
|
+
"build": "node ./setup.js build",
|
|
33
36
|
"release": "node ./setup.js publish",
|
|
34
|
-
"setup": "node ./setup.js"
|
|
37
|
+
"setup": "node ./setup.js",
|
|
38
|
+
"test": "node ./setup.js build:development"
|
|
35
39
|
}
|
|
36
40
|
}
|