zixulu 1.80.6 → 1.80.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ import { capitalize, emailReg, getEnumEntries, getEnumKeys, getEnumValues, isNon
6
6
  import { compress, execAsync, isPathLike, setDefaultOptions, spawnAsync, unzip, zip } from "soda-nodejs";
7
7
  import inquirer_0 from "inquirer";
8
8
  import { chdir, cwd as external_node_process_cwd, exit } from "node:process";
9
- import { copyFile, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
10
- import { basename, join, parse as external_node_path_parse, resolve as external_node_path_resolve } from "node:path";
9
+ import { access, copyFile, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
10
+ import { basename, delimiter, join, parse as external_node_path_parse, resolve as external_node_path_resolve } from "node:path";
11
11
  import node_fetch, { Headers as external_node_fetch_Headers } from "node-fetch";
12
12
  import semver from "semver";
13
13
  import { checkPort } from "get-port-please";
@@ -4788,26 +4788,22 @@ async function sortPackageJson() {
4788
4788
  });
4789
4789
  consola.success("排序 package.json 中的依赖成功");
4790
4790
  }
4791
- const AgentRulesSyncTarget = {
4792
- agentsMd: "AGENTS.md",
4793
- cursor: "Cursor",
4794
- antiGravity: "AntiGravity"
4795
- };
4796
- const syncAgentRules_source = join(".cursor-rules");
4797
- const cursorRulesTarget = join(".cursor", "rules");
4798
- const antiGravityRulesTarget = join(".agent", "rules");
4791
+ const syncAgentRules_source = join(".agent-rules");
4792
+ const agentsMdTarget = "AGENTS.md";
4799
4793
  const orders = [
4800
- "base.mdc",
4801
- "react.mdc",
4802
- "api.mdc",
4803
- "next.mdc"
4794
+ "base.md",
4795
+ "style.md",
4796
+ "react.md",
4797
+ "api.md",
4798
+ "next.md"
4804
4799
  ];
4805
- function getDefaultSyncTargets() {
4806
- const targets = [];
4807
- if (existsSync("AGENTS.md")) targets.push(AgentRulesSyncTarget.agentsMd);
4808
- if (existsSync(".cursor")) targets.push(AgentRulesSyncTarget.cursor);
4809
- if (existsSync(".agent")) targets.push(AgentRulesSyncTarget.antiGravity);
4810
- return targets;
4800
+ async function pathExists(path) {
4801
+ try {
4802
+ await access(path);
4803
+ return true;
4804
+ } catch {
4805
+ return false;
4806
+ }
4811
4807
  }
4812
4808
  function sortAgentRuleFiles(files) {
4813
4809
  return files.toSorted((a, b)=>{
@@ -4819,72 +4815,70 @@ function sortAgentRuleFiles(files) {
4819
4815
  return aIndex - bIndex;
4820
4816
  });
4821
4817
  }
4822
- function transformCursorRuleToAntiGravityRule(source) {
4823
- return source.replace(/^---\nalwaysApply: (true|false)\n---/, (match, p1)=>`---
4824
- trigger: ${"true" === p1 ? "always_on" : "model_decision"}
4825
- glob:
4826
- description:
4827
- ---`);
4818
+ function normalizeMarkdownHeading(heading) {
4819
+ return heading.replace(/\s+#+$/, "").trim().toLocaleLowerCase();
4828
4820
  }
4829
- function transformCursorRuleToAgentsRule(source) {
4830
- return source.replace(/^---\nalwaysApply: (true|false)\n---/, "").replace(/^(#+ )/gm, "#$1");
4821
+ function getMarkdownHeadings(content, level) {
4822
+ const hashes = "#".repeat(level);
4823
+ const regexp = new RegExp(`^${hashes}(?!#)\\s+(.+?)\\s*$`, "gm");
4824
+ return Array.from(content.matchAll(regexp), ([, heading])=>heading.replace(/\s+#+$/, "").trim());
4831
4825
  }
4832
- async function getCursorRuleFileStatusMap(files) {
4833
- const map = {};
4834
- if (!existsSync(cursorRulesTarget)) return Object.fromEntries(files.map((file)=>[
4835
- file,
4836
- "new"
4837
- ]));
4838
- const targetDir = await readdir(cursorRulesTarget);
4839
- for (const file of files){
4840
- if (!targetDir.includes(file)) {
4841
- map[file] = "new";
4842
- continue;
4843
- }
4844
- const sourceContent = await readFile(join(syncAgentRules_source, file), "utf-8");
4845
- const targetContent = await readFile(join(cursorRulesTarget, file), "utf-8");
4846
- if (sourceContent !== targetContent) map[file] = "modified";
4826
+ function getMarkdownFirstHeading(content) {
4827
+ return getMarkdownHeadings(content, 1)[0] ?? "";
4828
+ }
4829
+ function transformMarkdownRuleToAgentsRule(source) {
4830
+ return source.replace(/^(#+ )/gm, "#$1").replace(/\n+$/, "");
4831
+ }
4832
+ async function getMarkdownRuleFilenames() {
4833
+ const entries = await readdir(syncAgentRules_source, {
4834
+ withFileTypes: true
4835
+ });
4836
+ return sortAgentRuleFiles(entries.filter((entry)=>entry.isFile()).map((entry)=>entry.name).filter((filename)=>filename.toLowerCase().endsWith(".md")));
4837
+ }
4838
+ async function readAgentRuleFiles(files) {
4839
+ const rules = [];
4840
+ for (const filename of files){
4841
+ const content = await readFile(join(syncAgentRules_source, filename), "utf-8");
4842
+ const title = getMarkdownFirstHeading(content) || filename;
4843
+ rules.push({
4844
+ filename,
4845
+ title,
4846
+ content
4847
+ });
4847
4848
  }
4848
- return map;
4849
+ return rules;
4849
4850
  }
4850
- async function syncCursorRules(files) {
4851
- const map = await getCursorRuleFileStatusMap(files);
4852
- const changedFiles = Object.keys(map);
4853
- if (0 === changedFiles.length) return;
4854
- const { files: selectedFiles } = await inquirer_0.prompt({
4851
+ async function readOriginalAgentsMd() {
4852
+ if (!await pathExists(agentsMdTarget)) return;
4853
+ return await readFile(agentsMdTarget, "utf-8");
4854
+ }
4855
+ function getDefaultAgentRuleFiles(params) {
4856
+ const { rules, originalContent } = params;
4857
+ if (!originalContent) return rules.map((rule)=>rule.filename);
4858
+ const originalSecondHeadings = new Set(getMarkdownHeadings(originalContent, 2).map(normalizeMarkdownHeading));
4859
+ return rules.filter((rule)=>originalSecondHeadings.has(normalizeMarkdownHeading(rule.title))).map((rule)=>rule.filename);
4860
+ }
4861
+ async function selectAgentRuleFiles(params) {
4862
+ const { rules, defaultFiles } = params;
4863
+ const { files } = await inquirer_0.prompt({
4855
4864
  type: "checkbox",
4856
4865
  name: "files",
4857
- message: "请选择要添加的 Cursor 规则文件",
4858
- choices: Object.entries(map).map(([key, value])=>({
4859
- name: `${key} (${value})`,
4860
- value: key
4866
+ message: "请选择要同步到 AGENTS.md 的规则",
4867
+ choices: rules.map((rule)=>({
4868
+ name: `${rule.title} (${rule.filename})`,
4869
+ value: rule.filename
4861
4870
  })),
4862
- default: changedFiles
4871
+ default: defaultFiles
4863
4872
  });
4864
- await mkdir(cursorRulesTarget, {
4865
- recursive: true
4866
- });
4867
- for (const file of selectedFiles)await copyFile(join(syncAgentRules_source, file), join(cursorRulesTarget, file));
4873
+ return files;
4868
4874
  }
4869
- async function syncAntiGravityRules(files) {
4870
- await mkdir(antiGravityRulesTarget, {
4871
- recursive: true
4872
- });
4873
- for (const file of files){
4874
- const sourceContent = await readFile(join(syncAgentRules_source, file), "utf-8");
4875
- const content = transformCursorRuleToAntiGravityRule(sourceContent);
4876
- await writeFile(join(antiGravityRulesTarget, file.replace(/\.mdc$/, ".md")), content);
4877
- }
4878
- }
4879
- async function syncAgentsMdRules(files) {
4880
- let agentsRule = "# Agent Rules";
4881
- for (const file of files){
4882
- const sourceContent = await readFile(join(syncAgentRules_source, file), "utf-8");
4883
- const content = transformCursorRuleToAgentsRule(sourceContent);
4884
- agentsRule = `${agentsRule}${content}`.replace(/\n+$/, "");
4885
- }
4886
- agentsRule = `${agentsRule}\n`;
4887
- await writeFile("AGENTS.md", agentsRule);
4875
+ async function syncAgentsMdRules(rules) {
4876
+ const sections = rules.map((rule)=>transformMarkdownRuleToAgentsRule(rule.content)).filter(Boolean);
4877
+ const agentsRule = `${[
4878
+ "# Agent Rules",
4879
+ ...sections
4880
+ ].join("\n\n").replace(/\n+$/, "")}\n`;
4881
+ await writeFile(agentsMdTarget, agentsRule);
4888
4882
  }
4889
4883
  async function asyncAgentRules() {
4890
4884
  try {
@@ -4893,33 +4887,33 @@ async function asyncAgentRules() {
4893
4887
  await writePackageJson({
4894
4888
  data: packageJson
4895
4889
  });
4896
- } catch (error) {}
4897
- await spawnAsync("npx gitpick 1adybug/cursor-rule/tree/main/.cursor/rules .cursor-rules");
4890
+ } catch {}
4891
+ await rm(syncAgentRules_source, {
4892
+ recursive: true,
4893
+ force: true
4894
+ });
4898
4895
  try {
4899
- const sourceDir = sortAgentRuleFiles(await readdir(syncAgentRules_source));
4900
- const defaultTargets = getDefaultSyncTargets();
4901
- const { targets } = await inquirer_0.prompt({
4902
- type: "checkbox",
4903
- name: "targets",
4904
- message: "请选择要同步的 Agent 规则",
4905
- choices: Object.values(AgentRulesSyncTarget),
4906
- default: defaultTargets
4896
+ await spawnAsync(`npx gitpick 1adybug/cursor-rule ${syncAgentRules_source}`);
4897
+ const sourceDir = await getMarkdownRuleFilenames();
4898
+ if (0 === sourceDir.length) throw new Error("未找到 Agent 规则文件");
4899
+ const rules = await readAgentRuleFiles(sourceDir);
4900
+ const originalAgentsMd = await readOriginalAgentsMd();
4901
+ const defaultFiles = getDefaultAgentRuleFiles({
4902
+ rules,
4903
+ originalContent: originalAgentsMd
4907
4904
  });
4908
- if (0 === targets.length) throw new Error("未选择同步目标");
4909
- if (targets.includes(AgentRulesSyncTarget.cursor)) await syncCursorRules(sourceDir);
4910
- if (targets.includes(AgentRulesSyncTarget.antiGravity)) await syncAntiGravityRules(sourceDir);
4911
- if (targets.includes(AgentRulesSyncTarget.agentsMd)) await syncAgentsMdRules(sourceDir);
4912
- await rm(syncAgentRules_source, {
4913
- recursive: true,
4914
- force: true
4905
+ const selectedFiles = await selectAgentRuleFiles({
4906
+ rules,
4907
+ defaultFiles
4915
4908
  });
4909
+ if (0 === selectedFiles.length) throw new Error("未选择 Agent 规则");
4910
+ await syncAgentsMdRules(rules.filter((rule)=>selectedFiles.includes(rule.filename)));
4916
4911
  return getCommitMessage("feature", "同步 Agent 规则");
4917
- } catch (error) {
4912
+ } finally{
4918
4913
  await rm(syncAgentRules_source, {
4919
4914
  recursive: true,
4920
4915
  force: true
4921
4916
  });
4922
- throw error;
4923
4917
  }
4924
4918
  }
4925
4919
  const EditorExtensionCommandMap = {
@@ -4927,8 +4921,53 @@ const EditorExtensionCommandMap = {
4927
4921
  Cursor: "cursor",
4928
4922
  Antigravity: "antigravity"
4929
4923
  };
4924
+ const editorExtensionCommandCache = new Map();
4925
+ async function getEditorExtensions_pathExists({ path }) {
4926
+ try {
4927
+ await access(path);
4928
+ return true;
4929
+ } catch {
4930
+ return false;
4931
+ }
4932
+ }
4933
+ function quoteShellCommand({ command }) {
4934
+ if (!/[\s()]/.test(command)) return command;
4935
+ return `"${command.replace(/"/g, '\\"')}"`;
4936
+ }
4937
+ function getEditorWindowsCliCandidates({ editor }) {
4938
+ const command = EditorExtensionCommandMap[editor];
4939
+ const candidates = new Set();
4940
+ const localAppData = process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
4941
+ const programFiles = process.env.ProgramFiles ?? "C:/Program Files";
4942
+ const programFilesX86 = process.env["ProgramFiles(x86)"] ?? "C:/Program Files (x86)";
4943
+ if ("Code" === editor) {
4944
+ candidates.add(join(localAppData, "Programs", "Microsoft VS Code", "bin", "code.cmd"));
4945
+ candidates.add(join(programFiles, "Microsoft VS Code", "bin", "code.cmd"));
4946
+ candidates.add(join(programFilesX86, "Microsoft VS Code", "bin", "code.cmd"));
4947
+ }
4948
+ if ("Cursor" === editor) {
4949
+ candidates.add(join(localAppData, "Programs", "Cursor", "resources", "app", "bin", "cursor.cmd"));
4950
+ candidates.add(join(localAppData, "Programs", "cursor", "resources", "app", "bin", "cursor.cmd"));
4951
+ candidates.add(join(programFiles, "Cursor", "resources", "app", "bin", "cursor.cmd"));
4952
+ candidates.add(join(programFilesX86, "Cursor", "resources", "app", "bin", "cursor.cmd"));
4953
+ }
4954
+ if ("Antigravity" === editor) {
4955
+ candidates.add(join(localAppData, "Programs", "Antigravity", "bin", "antigravity.cmd"));
4956
+ candidates.add(join(localAppData, "Programs", "Antigravity", "resources", "app", "bin", "antigravity.cmd"));
4957
+ candidates.add(join(programFiles, "Antigravity", "bin", "antigravity.cmd"));
4958
+ candidates.add(join(programFiles, "Antigravity", "resources", "app", "bin", "antigravity.cmd"));
4959
+ candidates.add(join(programFilesX86, "Antigravity", "bin", "antigravity.cmd"));
4960
+ candidates.add(join(programFilesX86, "Antigravity", "resources", "app", "bin", "antigravity.cmd"));
4961
+ }
4962
+ const pathDirs = process.env.PATH?.split(delimiter).filter(Boolean) ?? [];
4963
+ for (const dir of pathDirs){
4964
+ candidates.add(join(dir, `${command}.cmd`));
4965
+ candidates.add(join(dir, command));
4966
+ }
4967
+ return Array.from(candidates);
4968
+ }
4930
4969
  async function canGetEditorExtensions({ editor }) {
4931
- const command = getEditorExtensionCommand({
4970
+ const command = await getEditorExtensionCommand({
4932
4971
  editor
4933
4972
  });
4934
4973
  try {
@@ -4938,13 +4977,32 @@ async function canGetEditorExtensions({ editor }) {
4938
4977
  return false;
4939
4978
  }
4940
4979
  }
4941
- function getEditorExtensionCommand({ editor }) {
4942
- return EditorExtensionCommandMap[editor];
4980
+ async function getEditorExtensionCommand({ editor }) {
4981
+ const cachedCommand = editorExtensionCommandCache.get(editor);
4982
+ if (cachedCommand) return cachedCommand;
4983
+ const fallbackCommand = EditorExtensionCommandMap[editor];
4984
+ if ("win32" !== process.platform) {
4985
+ editorExtensionCommandCache.set(editor, fallbackCommand);
4986
+ return fallbackCommand;
4987
+ }
4988
+ for (const candidate of getEditorWindowsCliCandidates({
4989
+ editor
4990
+ })){
4991
+ if (!await getEditorExtensions_pathExists({
4992
+ path: candidate
4993
+ })) continue;
4994
+ const command = quoteShellCommand({
4995
+ command: candidate
4996
+ });
4997
+ editorExtensionCommandCache.set(editor, command);
4998
+ return command;
4999
+ }
5000
+ throw new Error(`${editor} 命令行工具不可用,请确认已安装并加入 PATH`);
4943
5001
  }
4944
5002
  async function getEditorExtensions({ source }) {
4945
5003
  let data = [];
4946
5004
  if ("Online" !== source) {
4947
- const command = getEditorExtensionCommand({
5005
+ const command = await getEditorExtensionCommand({
4948
5006
  editor: source
4949
5007
  });
4950
5008
  const output = await execAsync(`${command} --list-extensions`);
@@ -4971,7 +5029,7 @@ async function syncCursorExtToCode() {
4971
5029
  const vscodeExts = await getEditorExtensions({
4972
5030
  source: "Code"
4973
5031
  });
4974
- const codeCommand = getEditorExtensionCommand({
5032
+ const codeCommand = await getEditorExtensionCommand({
4975
5033
  editor: "Code"
4976
5034
  });
4977
5035
  const installExts = cursorExts.difference(vscodeExts);
@@ -5097,7 +5155,7 @@ async function syncEditorFile({ type, source: { value: sourceValue }, target: {
5097
5155
  consola.success(`${targetValue} 同步完成`);
5098
5156
  }
5099
5157
  async function syncEditorExtensions({ editor, sourceExtensions, targetExtensions }) {
5100
- const command = getEditorExtensionCommand({
5158
+ const command = await getEditorExtensionCommand({
5101
5159
  editor
5102
5160
  });
5103
5161
  const installExtensions = sourceExtensions.difference(targetExtensions);