@d5render/cli 0.1.41 → 0.1.44

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.
@@ -3,7 +3,7 @@ name: code-review
3
3
  description: When the task is code-review, please follow this document to proceed the work.
4
4
  ---
5
5
 
6
- 1. read file [review.instructions.md](./review.instructions.md).
6
+ 1. read file [code-review.instructions.md](./code-review.instructions.md).
7
7
  2. find all Markdown resumes within the project and their basic understanding of the project.
8
8
  3. integrate your understanding of code-review to process task.
9
9
  4. the merge request is only for change reference and is not the main objective of the review; therefore, it does not require in-depth analysis.
@@ -10,7 +10,7 @@ CI MCP 的入口是 [server.ts](.\copilot\server\index.ts) 会经历一轮打包
10
10
 
11
11
  开发的一些注意点:
12
12
 
13
- 1. [copilot.config.js](.\copilot\server\config.ts) 属于公共配置,有需要可以放这里
13
+ 1. [config.ts](.\copilot\server\config.ts) 属于公共配置,有需要可以放这里
14
14
  2. [vscode](.\vscode\index.ts) 插件的 client 需要单独开发
15
15
  3. 本地功能调试 gpt-5-mini 食用最佳🤣,效果测试建议切回当前模型(开发环境已内置)
16
16
  4. 返回 Promise 之后会丢失开发过程中的类型支持,registerTool 调用时可先不写 Promise 看 MCP Server 支持的返回参数
package/CHANGELOG.md CHANGED
@@ -1,7 +1,10 @@
1
- 2026-1-7
1
+ ### 2026-1-16
2
+ 补充安全相关检查
3
+
4
+ ### 2026-1-7
2
5
  代码评审 skills 更新
3
6
 
4
7
  使用内置 skills
5
8
 
6
- 2025-12-31
9
+ ### 2025-12-31
7
10
  基础评审能力上云
package/README.md CHANGED
@@ -1,3 +1,3 @@
1
1
  for devops, please refer [.skills/devops](.skills/devops)
2
2
 
3
- for codereview, please refer[.skills/review/SKILL.md](.skills/review/SKILL.md)
3
+ for codereview, please refer[.skills/code-review/SKILL.md](.skills/code-review/SKILL.md)
package/bin/copilot.js CHANGED
@@ -1,4 +1,4 @@
1
- import process$1, { argv } from "node:process";
1
+ import process$1, { argv, platform } from "node:process";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
 
@@ -18809,7 +18809,6 @@ const getHash = "hash";
18809
18809
  const noMandatory = "dont't call under non-mandatory conditions";
18810
18810
  const file = "bin/copilot.js";
18811
18811
  const RUNTIME_CWD = join(dirname(fileURLToPath(import.meta.url)), "../");
18812
- console.log("RUNTIME_CWD:", RUNTIME_CWD);
18813
18812
  const serveFile = join(RUNTIME_CWD, file);
18814
18813
  const envJson = buildEnv();
18815
18814
  const envUsed = {
@@ -18845,7 +18844,7 @@ const tools = [
18845
18844
  "--deny-tool",
18846
18845
  "github-mcp-server"
18847
18846
  ];
18848
- tools.push("--model", "claude-sonnet-4.5");
18847
+ if (platform === "linux") tools.push("--model", "gemini-3-pro-preview");
18849
18848
  function toEnv(key, defaultValue) {
18850
18849
  return envJson[key] || process.env[key] || defaultValue;
18851
18850
  }
@@ -18866,19 +18865,29 @@ function buildHeaders() {
18866
18865
  else headers.set("Authorization", `Bearer ${GITLAB_TOKEN}`);
18867
18866
  return headers;
18868
18867
  }
18868
+ function apiHost() {
18869
+ const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
18870
+ if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}`;
18871
+ }
18872
+ function apiProject() {
18873
+ const { CI_PROJECT_PATH, CI_SERVER_URL } = envUsed;
18874
+ if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}`;
18875
+ }
18869
18876
  const pipelineMerge = () => {
18870
- const { CI_MERGE_REQUEST_IID, CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
18871
- if (CI_MERGE_REQUEST_IID && CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}`;
18877
+ const api = apiHost();
18878
+ const { CI_MERGE_REQUEST_IID } = envUsed;
18879
+ if (CI_MERGE_REQUEST_IID && api) return `${api}/merge_requests/${CI_MERGE_REQUEST_IID}`;
18872
18880
  };
18873
18881
  const visitCommit = () => {
18874
- const { CI_PROJECT_PATH, CI_SERVER_URL } = envUsed;
18875
- if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/commit`;
18882
+ const api = apiProject();
18883
+ if (api) return `${api}/-/commit`;
18876
18884
  };
18877
18885
  function visitPipeline(mid) {
18878
- const { CI_SERVER_URL, CI_PROJECT_PATH, CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
18879
- if (!CI_SERVER_URL || !CI_PROJECT_PATH) return {};
18886
+ const api = apiProject();
18887
+ if (!api) return {};
18888
+ const { CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
18880
18889
  if (CI_MERGE_REQUEST_IID) return {
18881
- url: `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
18890
+ url: `${api}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
18882
18891
  type: "merge_request"
18883
18892
  };
18884
18893
  if (CI_COMMIT_SHA) return {
@@ -18888,45 +18897,29 @@ function visitPipeline(mid) {
18888
18897
  return {};
18889
18898
  }
18890
18899
  const commits = () => {
18891
- const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
18892
- if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits`;
18900
+ const api = apiHost();
18901
+ if (api) return `${api}/repository/commits`;
18893
18902
  };
18894
18903
 
18895
18904
  //#endregion
18896
18905
  //#region packages/gitlab/commit.ts
18897
18906
  const getCommits = () => {
18898
- const { CI_COMMIT_SHA, CI_COMMIT_BEFORE_SHA } = envUsed;
18899
18907
  const url = pipelineMerge();
18900
18908
  if (url) return fetch(`${url}/commits`, { headers: buildHeaders() }).then((res) => {
18901
- if (!res.ok) throw new Error("");
18909
+ if (!res.ok) throw new Error("请求GitLab API失败" + res.statusText);
18902
18910
  return res.json();
18903
- }).then((commits$1) => {
18904
- console.log(`[调试] 从 GitLab API 获取了 ${commits$1.length} 个提交记录`);
18905
- commits$1.forEach((commit, index) => {
18906
- console.log(`[调试] 提交记录 ${index + 1}:`, {
18907
- 提交ID: commit.id,
18908
- 短ID: commit.short_id,
18909
- 标题: commit.title,
18910
- 消息: commit.message?.substring(0, 50),
18911
- 作者姓名: commit.author_name,
18912
- 作者邮箱: commit.author_email,
18913
- 提交日期: commit.authored_date,
18914
- 提交者姓名: commit.committer_name,
18915
- 提交者邮箱: commit.committer_email
18916
- });
18917
- });
18918
- return { content: [{
18919
- type: "text",
18920
- description: "commits from `merge pipeline`",
18921
- text: `${commits$1.map((commit) => commit.id).join(",")}, the above review is from \`merge pipeline\` commits.`
18922
- }] };
18923
- }).catch((error) => ({
18911
+ }).then((commits$1) => ({ content: [{
18912
+ type: "text",
18913
+ description: "commits from `merge pipeline`",
18914
+ text: `${commits$1.map((commit) => commit.id).join(",")}, the above review is from \`merge pipeline\` commits.`
18915
+ }] })).catch((error) => ({
18924
18916
  content: [{
18925
18917
  type: "text",
18926
- text: "error" + error.message ? ", reason: " + error.message || "请求GitLab API失败" : ""
18918
+ text: "error " + (error.message || "请求GitLab API失败,请检查网络连接或访问权限")
18927
18919
  }],
18928
18920
  isError: true
18929
18921
  }));
18922
+ const { CI_COMMIT_SHA, CI_COMMIT_BEFORE_SHA } = envUsed;
18930
18923
  if (CI_COMMIT_SHA && CI_COMMIT_BEFORE_SHA) return { content: [{
18931
18924
  type: "text",
18932
18925
  description: "commit from push pipeline",
@@ -18982,7 +18975,8 @@ function commonHeaders() {
18982
18975
  const getIssue = (request) => {
18983
18976
  const { key } = request;
18984
18977
  const { JIRA_BASE_URL } = envUsed;
18985
- return fetch(`${JIRA_BASE_URL}/rest/api/latest/issue/${key}`, { headers: commonHeaders() }).then((res) => res.json()).then(({ fields = {}, key: key$1 = "" }) => {
18978
+ return fetch(`${JIRA_BASE_URL}/rest/api/latest/issue/${key}`, { headers: commonHeaders() }).then((res) => res.json()).then((res) => {
18979
+ const { fields = {}, key: key$1 = "" } = res;
18986
18980
  const { comment = {}, description = "", summary = "", priority = {} } = fields;
18987
18981
  const { comments = [] } = comment;
18988
18982
  return { content: [{
@@ -19096,22 +19090,64 @@ const schemaGitlabPipeline = "gitlab_pipeline";
19096
19090
  const schemaGitlabCommit = "gitlab_commit";
19097
19091
  const schemaDingdingTalk = "dingding_talk";
19098
19092
  function distReports(input) {
19093
+ const { CI_PROJECT_NAME = "", CI_COMMIT_SHA = "", JIRA_BASE_URL, CI_MERGE_REQUEST_IID, DINGTALK_WEBHOOK } = envUsed;
19094
+ const { title = "代码审查报告", summary = [], issues = [], jiras = [], risks = [], suggestions = [] } = input;
19095
+ const mergeURL = pipelineMerge();
19096
+ const commitsURL = commits();
19097
+ let gitlabReportMessage = `## 📖 ${title}\n`;
19098
+ const commitComments = [];
19099
+ let dingdingReportMessage = "";
19099
19100
  const gitlabReport = {
19100
19101
  title: schemaGitlabPipeline,
19101
- report: [() => Promise.resolve(void 0)]
19102
+ report: [() => {
19103
+ if (mergeURL) return fetch(`${mergeURL}/notes`, {
19104
+ method: "POST",
19105
+ headers: buildHeaders(),
19106
+ body: JSON.stringify({ body: gitlabReportMessage })
19107
+ }).then((res) => res.json()).then((res) => {
19108
+ lastComment.value = res;
19109
+ });
19110
+ else if (commitsURL) {
19111
+ const pipeURL = `${commitsURL}/${CI_COMMIT_SHA}`;
19112
+ return fetch(`${pipeURL}/comments`, {
19113
+ method: "POST",
19114
+ headers: buildHeaders(),
19115
+ body: JSON.stringify({
19116
+ note: gitlabReportMessage,
19117
+ line_type: "new"
19118
+ })
19119
+ }).then(() => fetch(`${pipeURL}/discussions`, {
19120
+ method: "GET",
19121
+ headers: buildHeaders()
19122
+ })).then((res) => res.json()).then((res) => {
19123
+ const { notes = [] } = res[res.length - 1] || {};
19124
+ lastComment.value = notes[notes.length - 1] || {};
19125
+ });
19126
+ }
19127
+ return Promise.resolve(void 0);
19128
+ }]
19102
19129
  };
19103
19130
  const gitlabCommitReport = {
19104
19131
  title: schemaGitlabCommit,
19105
- report: [() => Promise.resolve(void 0)]
19132
+ report: [() => Promise.resolve()]
19106
19133
  };
19107
19134
  const dingdingReport = {
19108
19135
  title: schemaDingdingTalk,
19109
- report: [() => Promise.resolve(void 0)]
19136
+ report: [() => {
19137
+ if (!DINGTALK_WEBHOOK) return Promise.resolve(void 0);
19138
+ return sendding(title, dingdingReportMessage).then((response) => {
19139
+ if (!response.ok) throw new Error(response.statusText);
19140
+ return response.json();
19141
+ }).then((res) => {
19142
+ if (res.errcode === 0) return;
19143
+ if (res.errcode === 31e4) return;
19144
+ throw new Error("Post comments to DingTalk with error code:" + res.errcode + ", message: " + res.errmsg);
19145
+ });
19146
+ }]
19110
19147
  };
19111
- const { title = "代码审查报告", summary = [], issues = [], jiras = [], risks = [], suggestions = [] } = input;
19112
19148
  const result = [
19113
- gitlabCommitReport,
19114
19149
  gitlabReport,
19150
+ gitlabCommitReport,
19115
19151
  dingdingReport
19116
19152
  ];
19117
19153
  if (issues.length === 0 && suggestions.length === 0) {
@@ -19120,43 +19156,40 @@ function distReports(input) {
19120
19156
  dingdingReport.report = [];
19121
19157
  return result;
19122
19158
  }
19123
- const { CI_PROJECT_NAME = "", CI_COMMIT_SHA = "", JIRA_BASE_URL, CI_MERGE_REQUEST_IID, DINGTALK_WEBHOOK } = envUsed;
19124
19159
  const fileMap = /* @__PURE__ */ new Map();
19125
19160
  const severityMap = /* @__PURE__ */ new Map();
19126
19161
  const commitsSet = /* @__PURE__ */ new Set();
19127
19162
  const commitsMap = /* @__PURE__ */ new Map();
19128
- const commitComments = [];
19129
- const mergeURL = pipelineMerge();
19130
19163
  const linkBase = visitCommit();
19131
- const commitsURL = commits();
19132
19164
  function distCommitComments({ file: file$1, commitSha, severity: severity$1 = "NON", line = "", details = [], suggestions: suggestions$1 = [], codeExample = "" }) {
19133
- if (commitSha && file$1) {
19165
+ if (commitSha) {
19134
19166
  const lines = line.split(",").map((line$1) => {
19135
19167
  const str = line$1.trim().split("-");
19136
19168
  const num = Number.parseInt(str[str.length - 1], 10);
19137
19169
  return Number.isNaN(num) ? void 0 : num;
19138
19170
  }).filter((v) => v !== void 0);
19171
+ const attach = file$1 ? {
19172
+ file: file$1,
19173
+ line: lines.length > 0 ? lines[lines.length - 1] : void 0
19174
+ } : {};
19139
19175
  commitComments.push({
19140
19176
  sha: commitSha,
19141
- file: file$1,
19142
- line: lines.length > 0 ? lines[lines.length - 1] : void 0,
19143
- note: `**${severity$1}:** ${file$1}\n\n**问题:**\n\n - ${details.join("\n - ")}\n\n-------\n\n**修正建议:**\n - ${suggestions$1.join("\n - ")}${codeExample ? `\n------\n\n**代码示例:**\n${toCode(codeExample)}\n` : ""}`
19177
+ note: `**${severity$1}:** ${file$1}\n\n**问题:**\n\n - ${details.join("\n - ")}\n\n-------\n\n**修正建议:**\n - ${suggestions$1.join("\n - ")}${codeExample ? `\n------\n\n**代码示例:**\n${toCode(codeExample)}\n` : ""}`,
19178
+ ...attach
19144
19179
  });
19145
19180
  }
19146
19181
  }
19147
19182
  for (const issue$1 of issues) {
19148
- const { file: file$1 = "NON", commitSha = "", severity: severity$1 = "NON", commitSha: NCommitSha = "NON", commitAuthor = "" } = issue$1;
19183
+ const { file: file$1 = "NON", commitSha = "", severity: severity$1 = "NON", commitSha: NCommitSha = "NON" } = issue$1;
19149
19184
  const fileList = fileMap.get(file$1) || [], issueList = severityMap.get(severity$1) || [];
19150
19185
  fileList.push(issue$1);
19151
19186
  fileMap.set(file$1, fileList);
19152
19187
  issueList.push(issue$1);
19153
19188
  severityMap.set(severity$1, issueList);
19154
- if (commitSha || commitAuthor) console.error(`[调试] 问题提交信息 - 提交SHA: ${commitSha}, 作者: ${commitAuthor}, 文件: ${file$1}`);
19155
19189
  if (commitSha) commitsSet.add(commitSha);
19156
19190
  commitsMap.set(NCommitSha, issue$1);
19157
19191
  distCommitComments(issue$1);
19158
19192
  }
19159
- let gitlabReportMessage = `## 📖 ${title}\n`;
19160
19193
  if (summary && summary.length > 0) gitlabReportMessage += `> ${summary.join(" \n> ")}\n\n`;
19161
19194
  if (risks.length > 0) gitlabReportMessage += "\n**📋 主要风险**\n - " + risks.join("\n - ");
19162
19195
  if (fileMap.size > 0 || commitsSet.size > 0) {
@@ -19189,19 +19222,15 @@ function distReports(input) {
19189
19222
  }
19190
19223
  gitlabReportMessage += withNoFile + withFile;
19191
19224
  }
19192
- let dingdingReportMessage = "";
19225
+ const { url, type } = visitPipeline();
19193
19226
  if (DINGTALK_WEBHOOK) {
19194
19227
  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}`;
19195
- const { url, type } = visitPipeline();
19196
19228
  if (type) {
19197
19229
  const { value = {} } = lastComment;
19198
19230
  let { id: note = "" } = value || {};
19199
19231
  note = note ? `#note_${note}` : "";
19200
- dingdingReportMessage += `${type === "merge_request" ? `
19201
-
19202
- **合并**:[${CI_MERGE_REQUEST_IID}](${url}${note})` : `
19203
-
19204
- **提交**:[${CI_COMMIT_SHA.slice(0, 8)}](${url})`}`;
19232
+ const commit = note ? "&评论" : "";
19233
+ dingdingReportMessage += `${type === "merge_request" ? `\n\n**合并${commit}**:[${CI_MERGE_REQUEST_IID}](${url}${note})` : `\n\n**提交${commit}**:[${CI_COMMIT_SHA.slice(0, 8)}](${url}${note})`}`;
19205
19234
  }
19206
19235
  dingdingReportMessage += `\n\n含问题的commits:${[...commitsMap.keys()].map((v) => `[${v.slice(0, 8)}](${linkBase}/${v})`).join(", ")}\n\n-----\n`;
19207
19236
  dingdingReportMessage += "\n#### 🐞 问题列表\n";
@@ -19216,7 +19245,6 @@ function distReports(input) {
19216
19245
  break;
19217
19246
  }
19218
19247
  const { title: issueTitle = "NON", commitSha = "", file: file$1 = "NON", line = "NON", commitAuthor = "" } = issue$1;
19219
- console.error(`[调试] 在钉钉报告中使用作者信息 - 提交SHA: ${commitSha}, 作者: ${commitAuthor}, 标题: ${issueTitle}`);
19220
19248
  const shortSha = (commitSha || "").slice(0, 8);
19221
19249
  const commitLink = linkBase && commitSha ? `${linkBase}/${commitSha}` : void 0;
19222
19250
  dingdingReportMessage += `\n- ${issueTitle}\n\n - hash: ${commitLink ? `[${shortSha}](${commitLink}) ` : shortSha}\n\n - author: ${commitAuthor}\n\n - file: ${file$1}\n\n - line: ${line}`;
@@ -19225,40 +19253,23 @@ function distReports(input) {
19225
19253
  }
19226
19254
  if (jiras.length > 0) dingdingReportMessage += `\n\n\n\n#### 🔗 关联 JIRA\n ${jiras.map((v) => `[${v.key}](${JIRA_BASE_URL}/browse/${v.key}): ${v.summary}`).join("\n\n")}`;
19227
19255
  }
19228
- gitlabCommitReport.report = commitsURL ? commitComments.map(({ sha, file: file$1, line, note }) => () => fetch(`${commitsURL}/${sha}/comments`, {
19229
- method: "POST",
19230
- headers: buildHeaders(),
19231
- body: JSON.stringify({
19232
- note,
19233
- path: file$1,
19234
- line,
19235
- line_type: "new"
19236
- })
19237
- })) : [];
19238
- gitlabReport.report = mergeURL ? [() => fetch(mergeURL + `/notes`, {
19239
- method: "POST",
19240
- headers: buildHeaders(),
19241
- body: JSON.stringify({ body: gitlabReportMessage })
19242
- }).then((res) => res.json()).then((res) => {
19243
- lastComment.value = res;
19244
- })] : CI_COMMIT_SHA ? [() => fetch(`${commitsURL}/${CI_COMMIT_SHA}/comments`, {
19245
- method: "POST",
19246
- headers: buildHeaders(),
19247
- body: JSON.stringify({
19248
- note: gitlabReportMessage,
19249
- line_type: "new"
19250
- })
19251
- }).then((res) => res.json()).then((res) => {
19252
- lastComment.value = res;
19253
- })] : [() => Promise.reject(/* @__PURE__ */ new Error("⚠️ 报告内容已生成但未发布。"))];
19254
- dingdingReport.report = DINGTALK_WEBHOOK ? [() => sendding(title, dingdingReportMessage).then((response) => {
19255
- if (!response.ok) throw new Error(response.statusText);
19256
- return response.json();
19257
- }).then((res) => {
19258
- if (res.errcode === 0) return;
19259
- if (res.errcode === 31e4) return;
19260
- throw new Error("Post comments to DingTalk with error code:" + res.errcode + ", message: " + res.errmsg);
19261
- })] : [];
19256
+ if (commitsURL) gitlabCommitReport.report = commitComments.map(({ sha, file: file$1, line, note }) => () => {
19257
+ const { value = {} } = lastComment;
19258
+ let { id = "" } = value || {};
19259
+ if (id) note += `
19260
+
19261
+ [**完整评论请查看**](${url}#note_${id})`;
19262
+ return fetch(`${commitsURL}/${sha}/comments`, {
19263
+ method: "POST",
19264
+ headers: buildHeaders(),
19265
+ body: JSON.stringify({
19266
+ note,
19267
+ path: file$1,
19268
+ line,
19269
+ line_type: "new"
19270
+ })
19271
+ });
19272
+ });
19262
19273
  return result;
19263
19274
  }
19264
19275
  async function runReport(input) {
package/bin/d5cli CHANGED
@@ -11,7 +11,6 @@ const report = "report";
11
11
  const getHash = "hash";
12
12
  const file = "bin/copilot.js";
13
13
  const RUNTIME_CWD = join(dirname(fileURLToPath(import.meta.url)), "../");
14
- console.log("RUNTIME_CWD:", RUNTIME_CWD);
15
14
  const serveFile = join(RUNTIME_CWD, file);
16
15
  const envJson = buildEnv();
17
16
  const envUsed = {
@@ -47,7 +46,7 @@ const tools = [
47
46
  "--deny-tool",
48
47
  "github-mcp-server"
49
48
  ];
50
- tools.push("--model", "claude-sonnet-4.5");
49
+ if (platform === "linux") tools.push("--model", "gemini-3-pro-preview");
51
50
  function toEnv(key, defaultValue) {
52
51
  return envJson[key] || process.env[key] || defaultValue;
53
52
  }
@@ -109,7 +108,7 @@ function installCopilot() {
109
108
  //#endregion
110
109
  //#region package.json
111
110
  var name = "@d5render/cli";
112
- var version = "0.1.41";
111
+ var version = "0.1.44";
113
112
 
114
113
  //#endregion
115
114
  //#region packages/gitlab/url.ts
@@ -121,15 +120,24 @@ function buildHeaders() {
121
120
  else headers.set("Authorization", `Bearer ${GITLAB_TOKEN}`);
122
121
  return headers;
123
122
  }
124
- const visitCommit = () => {
123
+ function apiHost() {
124
+ const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
125
+ if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}`;
126
+ }
127
+ function apiProject() {
125
128
  const { CI_PROJECT_PATH, CI_SERVER_URL } = envUsed;
126
- if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/commit`;
129
+ if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}`;
130
+ }
131
+ const visitCommit = () => {
132
+ const api = apiProject();
133
+ if (api) return `${api}/-/commit`;
127
134
  };
128
135
  function visitPipeline(mid) {
129
- const { CI_SERVER_URL, CI_PROJECT_PATH, CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
130
- if (!CI_SERVER_URL || !CI_PROJECT_PATH) return {};
136
+ const api = apiProject();
137
+ if (!api) return {};
138
+ const { CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
131
139
  if (CI_MERGE_REQUEST_IID) return {
132
- url: `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
140
+ url: `${api}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
133
141
  type: "merge_request"
134
142
  };
135
143
  if (CI_COMMIT_SHA) return {
@@ -139,8 +147,8 @@ function visitPipeline(mid) {
139
147
  return {};
140
148
  }
141
149
  const commits = () => {
142
- const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
143
- if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits`;
150
+ const api = apiHost();
151
+ if (api) return `${api}/repository/commits`;
144
152
  };
145
153
 
146
154
  //#endregion
@@ -170,8 +178,6 @@ async function sendding(title, text) {
170
178
  //#region copilot/bin/utils.ts
171
179
  const NAME = name.replaceAll("/", "_");
172
180
  const VERSION = version;
173
- const HOME = env.USERPROFILE ?? env.HOME ?? env.HOMEPATH;
174
- if (!HOME) throw new Error("cannot find `USERPROFILE` directory");
175
181
  const TEMP = env.CI_PROJECT_DIR + "." + NAME;
176
182
  const dingding = async (...args) => {
177
183
  try {
@@ -183,32 +189,38 @@ const dingding = async (...args) => {
183
189
  };
184
190
  async function deploy() {
185
191
  if (!env.CI) return;
186
- const config = join(HOME, ".copilot/config.json"), dir = join(HOME, ".copilot/skills/codereview");
192
+ const HOME = env.USERPROFILE ?? env.HOME ?? env.HOMEPATH;
193
+ if (!HOME) throw new Error("cannot find `USERPROFILE` directory");
194
+ const config = join(HOME, ".copilot/config.json"), dir = join(HOME, ".copilot/skills/code-review");
187
195
  console.log("deploy...");
188
196
  if (existsSync(config)) {
189
197
  rmSync(config);
190
198
  console.log("removed config cache.");
191
199
  }
192
200
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
193
- const versiono = readFileSync(join(RUNTIME_CWD, ".skills/review/version"), "utf8");
201
+ const versiono = readFileSync(join(RUNTIME_CWD, ".skills/code-review/version"), "utf8");
194
202
  const changelog = readFileSync(join(RUNTIME_CWD, "CHANGELOG.md"), "utf8");
195
- const cachepath = join(TEMP, "CHANGELOG");
203
+ const cachepath = join(TEMP, "CHANGELOG-" + env.CI_RUNNER_ID || "0");
196
204
  if (changelog !== (existsSync(cachepath) ? readFileSync(cachepath, "utf8") : "")) {
197
205
  if (!existsSync(TEMP)) mkdirSync(TEMP, { recursive: true });
198
206
  writeFileSync(cachepath, changelog, "utf8");
199
207
  console.log("updated CHANGELOG cache.");
200
- await dingding("NOTICE", `codereview skills has been updated to version ${versiono}\n\nfor more details, please refer to [ONLINE](https://www.npmjs.com/package/@d5render/cli?activeTab=readme)`);
208
+ await dingding("NOTICE", `code-review/SKILL.md 更新到${versiono}\n\n请参考[线上文档内容](https://www.npmjs.com/package/@d5render/cli?activeTab=readme)`);
201
209
  }
202
210
  const versionnPath = join(dir, "version");
203
211
  if ((existsSync(versionnPath) ? readFileSync(versionnPath, "utf8") : "") === versiono) return;
204
- const skillRoot = join(RUNTIME_CWD, ".skills/review");
212
+ if (existsSync(dir)) rmSync(dir, {
213
+ recursive: true,
214
+ force: true
215
+ });
216
+ const skillRoot = join(RUNTIME_CWD, ".skills/code-review");
205
217
  readdirSync(skillRoot).forEach((skill) => copyFileSync(join(skillRoot, skill), join(dir, skill)));
206
218
  const instructionsRoot = join(RUNTIME_CWD, ".github/instructions");
207
219
  readdirSync(instructionsRoot).forEach((instruction) => copyFileSync(join(instructionsRoot, instruction), join(dir, instruction)));
208
220
  console.log("to new skill.");
209
221
  }
210
222
  async function need() {
211
- if (!env.CI) return;
223
+ if (!env.CI) return true;
212
224
  const { CI_MERGE_REQUEST_IID, CI_COMMIT_SHA } = env;
213
225
  const file$1 = join(TEMP, "CODEREVIEW");
214
226
  if (CI_MERGE_REQUEST_IID) {
@@ -278,29 +290,18 @@ Otherwise, use chinese as default language to call the mcp tool '${name$1}-${rep
278
290
  ],
279
291
  ...platform === "win32" && { windowsHide: true }
280
292
  });
281
- copilot.stdout.on("data", (chunk) => console.log("Stdout:" + String(chunk)));
282
- copilot.stderr.on("data", (chunk) => console.error("Stderr:" + String(chunk)));
293
+ copilot.stdout.on("data", (chunk) => console.log(String(chunk)));
294
+ copilot.stderr.on("data", (chunk) => console.error(String(chunk)));
283
295
  copilot.on("close", (code) => {
284
296
  if (!code) dingding("CRITICAL", "CI ERROR: 代码审查任务失败,请自行检查日志");
285
297
  exit(code);
286
298
  });
287
299
  }
288
- /**
289
- * 根据操作系统和架构获取对应的 copilot 包名
290
- */
291
- function getCopilotPackageName() {
292
- return "@github/copilot";
293
- }
294
300
  function findCopilopt() {
295
- const platformPackage = getCopilotPackageName();
296
301
  let copilot = "";
297
302
  try {
298
- copilot = execSync(`npm list ${platformPackage} -g -p`).toString().trim();
299
- } catch {
300
- try {
301
- copilot = execSync("npm list @github/copilot -g -p").toString().trim();
302
- } catch {}
303
- }
303
+ copilot = execSync("npm list @github/copilot -g -p").toString().trim();
304
+ } catch {}
304
305
  if (!copilot) {
305
306
  const first = platform === "win32" ? win : linux;
306
307
  const second = platform === "win32" ? linux : win;
@@ -311,7 +312,7 @@ function findCopilopt() {
311
312
  const pkg = join(copilot, "package.json");
312
313
  if (!existsSync(pkg)) throw new Error("安装的包找不到正确版本 " + pkg);
313
314
  const copilotPackage = JSON.parse(readFileSync(pkg, "utf8"));
314
- const binPath = typeof copilotPackage.bin === "string" ? copilotPackage.bin : copilotPackage.bin?.copilot || copilotPackage.bin?.[platformPackage] || copilotPackage.bin?.["@github/copilot"];
315
+ const binPath = typeof copilotPackage.bin === "string" ? copilotPackage.bin : copilotPackage.bin?.copilot || copilotPackage.bin?.["@github/copilot"];
315
316
  if (!binPath) throw new Error("non copilot executable found");
316
317
  const copilotVersion = copilotPackage.version || "unknown";
317
318
  const copilotPath = join(copilot, binPath);
@@ -328,9 +329,7 @@ function win() {
328
329
  const pathSeparator = platform === "win32" ? ";" : ":";
329
330
  const npm = pathEnv.split(pathSeparator).find((p) => p.includes("npm"));
330
331
  if (!npm) return "";
331
- const packagePath = join(npm, "node_modules", ...getCopilotPackageName().split("/"));
332
- if (existsSync(join(packagePath, "package.json"))) return packagePath;
333
- const fallbackPath = join(npm, "node_modules", "@github", "copilot");
332
+ const fallbackPath = join(npm, "node_modules", "@github/copilot");
334
333
  if (existsSync(join(fallbackPath, "package.json"))) return fallbackPath;
335
334
  return "";
336
335
  }
@@ -343,10 +342,7 @@ function linux() {
343
342
  if (npm) cached = npm;
344
343
  }
345
344
  if (!cached) return "";
346
- const platformPackage = getCopilotPackageName();
347
- const packagePath = join(cached, "..", "lib", "node_modules", ...platformPackage.split("/"));
348
- if (existsSync(join(packagePath, "package.json"))) return packagePath;
349
- const fallbackPath = join(cached, "..", "lib", "node_modules", "@github", "copilot");
345
+ const fallbackPath = join(cached, "..", "lib", "node_modules", "@github/copilot");
350
346
  if (existsSync(join(fallbackPath, "package.json"))) return fallbackPath;
351
347
  return "";
352
348
  }
package/package.json CHANGED
@@ -4,22 +4,24 @@
4
4
  "license": "MIT",
5
5
  "author": "jasirou",
6
6
  "main": "./bin/d5cli",
7
- "version": "0.1.41",
7
+ "version": "0.1.44",
8
8
  "devDependencies": {
9
9
  "@modelcontextprotocol/sdk": "^1.25.1",
10
10
  "@types/node": "^25.0.3",
11
11
  "@types/vscode": "^1.107.0",
12
12
  "@vscode/vsce": "^3.7.1",
13
+ "json5": "^2.2.3",
13
14
  "oxfmt": "^0.21.0",
14
15
  "oxlint": "^1.37.0",
15
16
  "rolldown": "1.0.0-beta.58",
17
+ "vitest": "^4.0.17",
16
18
  "zod": "^4.3.5"
17
19
  },
18
20
  "files": [
19
- ".github/instructions/review.instructions.md",
21
+ ".github/instructions/code-review.instructions.md",
20
22
  ".github/instructions/severity.instructions.md",
21
23
  ".skills/devops",
22
- ".skills/review",
24
+ ".skills/code-review",
23
25
  "bin/copilot.js",
24
26
  "bin/d5cli",
25
27
  "CHANGELOG.md"
File without changes