yg-team-cli 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -264,6 +264,12 @@ var init_utils = __esm({
264
264
  static async writeJson(file, data) {
265
265
  await fs.writeJson(file, data, { spaces: 2 });
266
266
  }
267
+ /**
268
+ * 获取路径的目录名 (ESM 兼容)
269
+ */
270
+ static getDirName(url) {
271
+ return path2.dirname(new URL(url).pathname);
272
+ }
267
273
  };
268
274
  StringUtils2 = class {
269
275
  /**
@@ -310,6 +316,12 @@ var init_utils = __esm({
310
316
  static slugify(str) {
311
317
  return str.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
312
318
  }
319
+ /**
320
+ * 验证项目名称
321
+ */
322
+ static validateProjectName(name) {
323
+ return /^[a-z0-9-]+$/.test(name);
324
+ }
313
325
  };
314
326
  DateUtils = class _DateUtils {
315
327
  /**
@@ -883,13 +895,141 @@ var init_template_version = __esm({
883
895
  }
884
896
  });
885
897
 
898
+ // src/lib/module-registry.ts
899
+ import path5 from "path";
900
+ import fs3 from "fs-extra";
901
+ var ModuleManager;
902
+ var init_module_registry = __esm({
903
+ "src/lib/module-registry.ts"() {
904
+ "use strict";
905
+ init_esm_shims();
906
+ init_utils();
907
+ ModuleManager = class {
908
+ static MODULES = [
909
+ {
910
+ id: "acl",
911
+ name: "ACL (Access Control List)",
912
+ description: "\u57FA\u4E8E\u89D2\u8272\u7684\u8BBF\u95EE\u63A7\u5236\u6A21\u5757\uFF08\u672C\u5730\u6A21\u677F\u7248\uFF09",
913
+ type: "local",
914
+ specs: ["acl/spec.md"],
915
+ backendFragments: [
916
+ {
917
+ source: "acl/backend/AclResultCode.java",
918
+ target: "common/enums/AclResultCode.java"
919
+ },
920
+ {
921
+ source: "acl/backend/RequiredPermission.java",
922
+ target: "common/annotation/RequiredPermission.java"
923
+ }
924
+ ]
925
+ },
926
+ {
927
+ id: "permission-remote",
928
+ name: "\u6743\u9650\u7BA1\u7406 (\u8FDC\u7A0B MCP \u7248)",
929
+ description: "\u4ECE api-metadata \u5B9E\u65F6\u83B7\u53D6\u6700\u65B0\u6743\u9650\u63A5\u53E3\u5B9A\u4E49\u5E76\u751F\u6210\u4EE3\u7801",
930
+ type: "remote",
931
+ mcpServer: "api-metadata",
932
+ specs: ["permission-remote/spec.md"]
933
+ },
934
+ {
935
+ id: "audit-log",
936
+ name: "\u5BA1\u8BA1\u65E5\u5FD7 (Audit Log)",
937
+ description: "\u8BB0\u5F55\u7528\u6237\u64CD\u4F5C\u65E5\u5FD7\uFF0C\u652F\u6301 AOP \u81EA\u52A8\u62E6\u622A\u548C\u5F02\u6B65\u5B58\u50A8",
938
+ type: "local",
939
+ specs: ["audit-log/spec.md"]
940
+ }
941
+ ];
942
+ /**
943
+ * 获取所有可用模块
944
+ */
945
+ static getAvailableModules() {
946
+ return [...this.MODULES];
947
+ }
948
+ /**
949
+ * 根据 ID 获取模块
950
+ */
951
+ static getModuleById(id) {
952
+ return this.MODULES.find((m) => m.id === id);
953
+ }
954
+ /**
955
+ * 注入模块到项目
956
+ */
957
+ static async injectModule(projectPath, moduleId, templatesDir) {
958
+ const module = this.getModuleById(moduleId);
959
+ if (!module) return;
960
+ for (const specRelPath of module.specs) {
961
+ const sourcePath = path5.join(templatesDir, "modules", specRelPath);
962
+ const targetPath = path5.join(projectPath, "docs/specs", path5.basename(specRelPath));
963
+ if (await FileUtils.exists(sourcePath)) {
964
+ await FileUtils.copy(sourcePath, targetPath);
965
+ } else if (module.type === "remote") {
966
+ await this.generateRemoteSpecPlaceholder(targetPath, module);
967
+ }
968
+ }
969
+ if (module.type === "local" && module.backendFragments) {
970
+ const backendBase = path5.join(projectPath, "backend/src/main/java");
971
+ const basePackagePath = await this.findBasePackage(backendBase);
972
+ if (basePackagePath) {
973
+ for (const fragment of module.backendFragments) {
974
+ const sourcePath = path5.join(templatesDir, "modules", fragment.source);
975
+ const targetPath = path5.join(basePackagePath, fragment.target);
976
+ if (await FileUtils.exists(sourcePath)) {
977
+ await FileUtils.ensureDir(path5.dirname(targetPath));
978
+ await FileUtils.copy(sourcePath, targetPath);
979
+ }
980
+ }
981
+ }
982
+ } else if (module.type === "remote") {
983
+ }
984
+ }
985
+ /**
986
+ * 生成远程模块的 Spec 占位符
987
+ */
988
+ static async generateRemoteSpecPlaceholder(targetPath, module) {
989
+ const content = `# ${module.name} (Remote)
990
+
991
+ ## \u529F\u80FD\u6982\u8FF0
992
+ \u8BE5\u6A21\u5757\u901A\u8FC7\u8FDC\u7A0B MCP \u670D\u52A1 \`${module.mcpServer}\` \u83B7\u53D6\u5B9E\u65F6\u5143\u6570\u636E\u3002
993
+
994
+ ## \u4F7F\u7528\u8BF4\u660E
995
+ 1. \u786E\u4FDD\u5DF2\u5B89\u88C5\u5E76\u914D\u7F6E MCP \u670D\u52A1\u3002
996
+ 2. \u8FD0\u884C \`team-cli dev\` \u5E76\u9009\u62E9\u6B64 Spec\u3002
997
+ 3. Claude \u5C06\u81EA\u52A8\u8C03\u7528 MCP \u83B7\u53D6\u6700\u65B0\u7684 API \u4FE1\u606F\u5E76\u5B8C\u6210\u5B9E\u73B0\u3002
998
+ `;
999
+ await FileUtils.write(targetPath, content);
1000
+ }
1001
+ /**
1002
+ * 自动探测项目的 Base Package
1003
+ */
1004
+ static async findBasePackage(searchDir) {
1005
+ if (!await FileUtils.exists(searchDir)) return null;
1006
+ const files = await FileUtils.findFiles("**/Application.java", searchDir);
1007
+ if (files.length > 0) {
1008
+ return path5.dirname(path5.join(searchDir, files[0]));
1009
+ }
1010
+ let current = searchDir;
1011
+ while (true) {
1012
+ const items = await fs3.readdir(current);
1013
+ const dirs = items.filter((c) => fs3.statSync(path5.join(current, c)).isDirectory());
1014
+ if (dirs.length === 1 && !["model", "controller", "service", "mapper", "config", "common"].includes(dirs[0])) {
1015
+ current = path5.join(current, dirs[0]);
1016
+ } else {
1017
+ break;
1018
+ }
1019
+ }
1020
+ return current;
1021
+ }
1022
+ };
1023
+ }
1024
+ });
1025
+
886
1026
  // src/lib/user-config.ts
887
1027
  var user_config_exports = {};
888
1028
  __export(user_config_exports, {
889
1029
  UserConfigManager: () => UserConfigManager,
890
1030
  userConfigManager: () => userConfigManager
891
1031
  });
892
- import path5 from "path";
1032
+ import path6 from "path";
893
1033
  import os2 from "os";
894
1034
  import crypto from "crypto";
895
1035
  var UserConfigManager, userConfigManager;
@@ -902,8 +1042,8 @@ var init_user_config = __esm({
902
1042
  UserConfigManager = class {
903
1043
  configPath;
904
1044
  constructor() {
905
- const configDir = path5.join(os2.homedir(), ".team-cli");
906
- this.configPath = path5.join(configDir, "config.json");
1045
+ const configDir = path6.join(os2.homedir(), ".team-cli");
1046
+ this.configPath = path6.join(configDir, "config.json");
907
1047
  }
908
1048
  /**
909
1049
  * 加载用户配置
@@ -930,7 +1070,7 @@ var init_user_config = __esm({
930
1070
  */
931
1071
  async save(config) {
932
1072
  try {
933
- const configDir = path5.dirname(this.configPath);
1073
+ const configDir = path6.dirname(this.configPath);
934
1074
  await FileUtils.ensureDir(configDir);
935
1075
  const configToSave = JSON.parse(JSON.stringify(config));
936
1076
  if (configToSave.gitlab?.accessToken) {
@@ -1036,7 +1176,7 @@ var init_user_config = __esm({
1036
1176
  * 获取配置目录
1037
1177
  */
1038
1178
  getConfigDir() {
1039
- return path5.dirname(this.configPath);
1179
+ return path6.dirname(this.configPath);
1040
1180
  }
1041
1181
  /**
1042
1182
  * 获取配置文件路径
@@ -1251,22 +1391,22 @@ var init_gitlab_api = __esm({
1251
1391
  * 从 Git URL 中提取项目路径
1252
1392
  */
1253
1393
  static parseProjectPath(repository) {
1254
- let path18 = repository;
1255
- if (path18.startsWith("git@")) {
1256
- path18 = path18.replace(/^git@/, "");
1257
- const colonIndex = path18.indexOf(":");
1394
+ let path19 = repository;
1395
+ if (path19.startsWith("git@")) {
1396
+ path19 = path19.replace(/^git@/, "");
1397
+ const colonIndex = path19.indexOf(":");
1258
1398
  if (colonIndex !== -1) {
1259
- path18 = path18.substring(colonIndex + 1);
1399
+ path19 = path19.substring(colonIndex + 1);
1260
1400
  }
1261
1401
  } else {
1262
- path18 = path18.replace(/^https?:\/\//, "");
1263
- const parts = path18.split("/");
1402
+ path19 = path19.replace(/^https?:\/\//, "");
1403
+ const parts = path19.split("/");
1264
1404
  if (parts.length > 1) {
1265
- path18 = parts.slice(1).join("/");
1405
+ path19 = parts.slice(1).join("/");
1266
1406
  }
1267
1407
  }
1268
- path18 = path18.replace(/\.git$/, "");
1269
- return path18;
1408
+ path19 = path19.replace(/\.git$/, "");
1409
+ return path19;
1270
1410
  }
1271
1411
  /**
1272
1412
  * 编码项目路径用于 API 请求
@@ -1320,8 +1460,8 @@ var init_gitlab_api = __esm({
1320
1460
  // src/commands/init.ts
1321
1461
  import { Command } from "commander";
1322
1462
  import inquirer from "inquirer";
1323
- import path6 from "path";
1324
- import fs3 from "fs-extra";
1463
+ import path7 from "path";
1464
+ import fs4 from "fs-extra";
1325
1465
  import { Listr } from "listr2";
1326
1466
  async function generateTechStack(projectPath) {
1327
1467
  const content = `# \u6280\u672F\u6808
@@ -1419,7 +1559,7 @@ docs/
1419
1559
  \u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
1420
1560
  \`\`\`
1421
1561
  `;
1422
- await FileUtils.write(path6.join(projectPath, "TECH_STACK.md"), content);
1562
+ await FileUtils.write(path7.join(projectPath, "TECH_STACK.md"), content);
1423
1563
  }
1424
1564
  async function generateConventions(projectPath) {
1425
1565
  const content = `# \u5F00\u53D1\u89C4\u8303
@@ -1722,7 +1862,7 @@ test('renders user name', () => {
1722
1862
  });
1723
1863
  \`\`\`
1724
1864
  `;
1725
- await FileUtils.write(path6.join(projectPath, "CONVENTIONS.md"), content);
1865
+ await FileUtils.write(path7.join(projectPath, "CONVENTIONS.md"), content);
1726
1866
  }
1727
1867
  async function generateAIMemory(projectPath, projectName) {
1728
1868
  const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
@@ -1781,7 +1921,7 @@ async function generateAIMemory(projectPath, projectName) {
1781
1921
  | Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
1782
1922
  |--------|------|---------|------|
1783
1923
  `;
1784
- await FileUtils.write(path6.join(projectPath, "AI_MEMORY.md"), content);
1924
+ await FileUtils.write(path7.join(projectPath, "AI_MEMORY.md"), content);
1785
1925
  }
1786
1926
  async function generateSpecTemplate(projectPath) {
1787
1927
  const content = `# [\u529F\u80FD\u6807\u9898]
@@ -1845,14 +1985,14 @@ async function generateSpecTemplate(projectPath) {
1845
1985
  ----
1846
1986
  *\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
1847
1987
  `;
1848
- await FileUtils.write(path6.join(projectPath, "docs/specs/template.md"), content);
1988
+ await FileUtils.write(path7.join(projectPath, "docs/specs/template.md"), content);
1849
1989
  }
1850
1990
  async function cloneBackendTemplate(projectPath, versionOptions) {
1851
1991
  const templateRepo = process.env.TEMPLATE_REPO || "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git";
1852
- const backendPath = path6.join(projectPath, "backend");
1992
+ const backendPath = path7.join(projectPath, "backend");
1853
1993
  try {
1854
1994
  const { execaCommand } = await import("execa");
1855
- const tempDir = path6.join(projectPath, ".template-temp");
1995
+ const tempDir = path7.join(projectPath, ".template-temp");
1856
1996
  if (versionOptions?.tag || versionOptions?.branch) {
1857
1997
  const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
1858
1998
  const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
@@ -1895,11 +2035,11 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
1895
2035
  stdio: "pipe"
1896
2036
  });
1897
2037
  const latestTag = tags.split("\n")[0] || void 0;
1898
- await fs3.copy(tempDir, backendPath, {
2038
+ await fs4.copy(tempDir, backendPath, {
1899
2039
  filter: (src) => !src.includes(".git")
1900
2040
  });
1901
- await fs3.remove(tempDir);
1902
- const gitDir = path6.join(backendPath, ".git");
2041
+ await fs4.remove(tempDir);
2042
+ const gitDir = path7.join(backendPath, ".git");
1903
2043
  if (await FileUtils.exists(gitDir)) {
1904
2044
  await FileUtils.remove(gitDir);
1905
2045
  }
@@ -1910,13 +2050,13 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
1910
2050
  });
1911
2051
  } catch (error) {
1912
2052
  logger.warn("\u514B\u9686\u540E\u7AEF\u6A21\u677F\u5931\u8D25\uFF0C\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
1913
- await FileUtils.ensureDir(path6.join(backendPath, "src/main/java/com/example"));
1914
- await FileUtils.ensureDir(path6.join(backendPath, "src/main/resources"));
1915
- await FileUtils.ensureDir(path6.join(backendPath, "src/test/java"));
2053
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/java/com/example"));
2054
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/resources"));
2055
+ await FileUtils.ensureDir(path7.join(backendPath, "src/test/java"));
1916
2056
  }
1917
2057
  }
1918
2058
  async function generateFrontendScaffold(projectPath) {
1919
- const frontendPath = path6.join(projectPath, "frontend");
2059
+ const frontendPath = path7.join(projectPath, "frontend");
1920
2060
  try {
1921
2061
  const prompt = `Read TECH_STACK.md and CONVENTIONS.md.
1922
2062
  Initialize a Next.js 14 frontend in ./frontend with:
@@ -1929,19 +2069,19 @@ Initialize a Next.js 14 frontend in ./frontend with:
1929
2069
  Do not run any servers, just generate the folder structure and configuration files.`;
1930
2070
  await claudeAI.prompt(prompt, {
1931
2071
  contextFiles: [
1932
- path6.join(projectPath, "TECH_STACK.md"),
1933
- path6.join(projectPath, "CONVENTIONS.md")
2072
+ path7.join(projectPath, "TECH_STACK.md"),
2073
+ path7.join(projectPath, "CONVENTIONS.md")
1934
2074
  ]
1935
2075
  });
1936
2076
  } catch (error) {
1937
2077
  logger.warn("Claude \u751F\u6210\u524D\u7AEF\u5931\u8D25\uFF0C\u5C06\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
1938
- await FileUtils.ensureDir(path6.join(frontendPath, "src/app"));
1939
- await FileUtils.ensureDir(path6.join(frontendPath, "src/components"));
1940
- await FileUtils.ensureDir(path6.join(frontendPath, "src/lib"));
1941
- await FileUtils.ensureDir(path6.join(frontendPath, "public"));
2078
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/app"));
2079
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/components"));
2080
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/lib"));
2081
+ await FileUtils.ensureDir(path7.join(frontendPath, "public"));
1942
2082
  }
1943
2083
  }
1944
- async function generateDockerFiles(projectPath) {
2084
+ async function generateDockerFiles() {
1945
2085
  logger.info("Docker \u914D\u7F6E\u751F\u6210\u5F85\u5B9E\u73B0");
1946
2086
  }
1947
2087
  async function initGit(projectPath, projectName) {
@@ -1967,6 +2107,7 @@ var init_init = __esm({
1967
2107
  init_claude();
1968
2108
  init_utils();
1969
2109
  init_template_version();
2110
+ init_module_registry();
1970
2111
  initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u540D\u79F0").description("\u521D\u59CB\u5316\u65B0\u9879\u76EE").option("-d, --dir <directory>", "\u9879\u76EE\u76EE\u5F55", ".").option("--no-docker", "\u4E0D\u751F\u6210 Docker \u914D\u7F6E").option("--no-git", "\u4E0D\u521D\u59CB\u5316 Git").option("--tag <tag>", "\u6307\u5B9A\u6A21\u677F\u6807\u7B7E (\u524D\u540E\u7AEF\u901A\u7528)").option("--backend-tag <tag>", "\u6307\u5B9A\u540E\u7AEF\u6A21\u677F\u6807\u7B7E").option("--frontend-tag <tag>", "\u6307\u5B9A\u524D\u7AEF\u6A21\u677F\u6807\u7B7E").option("--backend-branch <branch>", "\u6307\u5B9A\u540E\u7AEF\u6A21\u677F\u5206\u652F").option("--frontend-branch <branch>", "\u6307\u5B9A\u524D\u7AEF\u6A21\u677F\u5206\u652F").action(async (projectName, options) => {
1971
2112
  try {
1972
2113
  if (!projectName) {
@@ -1998,7 +2139,19 @@ var init_init = __esm({
1998
2139
  logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
1999
2140
  process.exit(1);
2000
2141
  }
2001
- const projectPath = path6.resolve(options.dir, projectName);
2142
+ const projectPath = path7.resolve(options.dir, projectName);
2143
+ const availableModules = ModuleManager.getAvailableModules();
2144
+ const { selectedModules } = await inquirer.prompt([
2145
+ {
2146
+ type: "checkbox",
2147
+ name: "selectedModules",
2148
+ message: "\u8BF7\u9009\u62E9\u8981\u5F00\u542F\u7684\u5185\u7F6E\u901A\u7528\u6A21\u5757:",
2149
+ choices: availableModules.map((m) => ({
2150
+ name: `${m.name} - ${m.description}`,
2151
+ value: m.id
2152
+ }))
2153
+ }
2154
+ ]);
2002
2155
  if (await FileUtils.exists(projectPath)) {
2003
2156
  logger.error(`\u76EE\u5F55\u5DF2\u5B58\u5728: ${projectPath}`);
2004
2157
  logger.info("\u8BF7\u9009\u62E9\u5176\u4ED6\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55");
@@ -2017,11 +2170,11 @@ var init_init = __esm({
2017
2170
  title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55",
2018
2171
  task: async () => {
2019
2172
  await FileUtils.ensureDir(projectPath);
2020
- await FileUtils.ensureDir(path6.join(projectPath, "frontend"));
2021
- await FileUtils.ensureDir(path6.join(projectPath, "backend"));
2022
- await FileUtils.ensureDir(path6.join(projectPath, "docs/specs"));
2023
- await FileUtils.ensureDir(path6.join(projectPath, "docs/api"));
2024
- await FileUtils.ensureDir(path6.join(projectPath, "docs/sessions"));
2173
+ await FileUtils.ensureDir(path7.join(projectPath, "frontend"));
2174
+ await FileUtils.ensureDir(path7.join(projectPath, "backend"));
2175
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/specs"));
2176
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/api"));
2177
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/sessions"));
2025
2178
  }
2026
2179
  },
2027
2180
  {
@@ -2069,7 +2222,7 @@ var init_init = __esm({
2069
2222
  {
2070
2223
  title: "\u751F\u6210 Docker \u914D\u7F6E",
2071
2224
  task: async () => {
2072
- await generateDockerFiles(projectPath);
2225
+ await generateDockerFiles();
2073
2226
  }
2074
2227
  }
2075
2228
  ] : [],
@@ -2080,7 +2233,61 @@ var init_init = __esm({
2080
2233
  await initGit(projectPath, projectName);
2081
2234
  }
2082
2235
  }
2083
- ] : []
2236
+ ] : [],
2237
+ {
2238
+ title: "\u6CE8\u518C\u8FDC\u7A0B MCP \u670D\u52A1",
2239
+ task: async () => {
2240
+ const hasRemoteModule = selectedModules.some(
2241
+ (id) => ModuleManager.getModuleById(id)?.type === "remote"
2242
+ );
2243
+ if (hasRemoteModule) {
2244
+ const mcpCmd = 'claude_m2 mcp add --transport sse --header "Authorization: Bearer mcp_00557dabb71297b4f9ac5fe748395f2c" -- api-metadata https://api-metadata.yungu.org/sse';
2245
+ const { execaCommand } = await import("execa");
2246
+ try {
2247
+ await execaCommand(mcpCmd);
2248
+ logger.info("\u8FDC\u7A0B MCP \u670D\u52A1 api-metadata \u5DF2\u6CE8\u518C");
2249
+ } catch (err) {
2250
+ logger.warn("\u8FDC\u7A0B MCP \u670D\u52A1\u6CE8\u518C\u5931\u8D25\uFF0C\u53EF\u80FD\u5DF2\u5B58\u5728\u6216\u6743\u9650\u4E0D\u8DB3");
2251
+ }
2252
+ }
2253
+ }
2254
+ },
2255
+ {
2256
+ title: "\u6CE8\u5165\u901A\u7528\u6A21\u5757",
2257
+ task: async () => {
2258
+ if (selectedModules.length === 0) return;
2259
+ const templatesDir = path7.resolve(FileUtils.getDirName(import.meta.url), "../templates");
2260
+ for (const moduleId of selectedModules) {
2261
+ await ModuleManager.injectModule(projectPath, moduleId, templatesDir);
2262
+ logger.info(`\u6CE8\u5165\u6A21\u5757: ${moduleId}`);
2263
+ }
2264
+ }
2265
+ },
2266
+ {
2267
+ title: "\u6267\u884C\u8FDC\u7A0B\u6A21\u5757\u4EE3\u7801\u751F\u6210",
2268
+ task: async () => {
2269
+ const remoteModules = selectedModules.map((id) => ModuleManager.getModuleById(id)).filter((m) => m?.type === "remote");
2270
+ if (remoteModules.length === 0) return;
2271
+ const { execaCommand } = await import("execa");
2272
+ for (const module of remoteModules) {
2273
+ if (!module) continue;
2274
+ logger.info(`\u6B63\u5728\u4E3A\u60A8\u751F\u6210 ${module.name} \u7684\u8FDC\u7A0B\u8C03\u7528\u4EE3\u7801...`);
2275
+ const prompt = `\u4F7F\u7528 api-metadata MCP \u5DE5\u5177\u83B7\u53D6\u6240\u6709\u4E0E "${module.id.replace("-remote", "")}" \u76F8\u5173\u7684 API \u5143\u6570\u636E\u3002
2276
+ \u7136\u540E\u5728\u9879\u76EE\u8DEF\u5F84 \`${projectPath}\` \u7684\u540E\u7AEF\u76EE\u5F55\u4E2D\uFF0C\u76F4\u63A5\u751F\u6210\u5BF9\u5E94\u7684 Java \u8C03\u7528\u4EE3\u7801\uFF08\u5982 Feign Client \u6216 RestTemplate \u5C01\u88C5\uFF09\u3002
2277
+ \u8BF7\u786E\u4FDD\u4EE3\u7801\u7B26\u5408 \`CONVENTIONS.md\` \u4E2D\u7684\u89C4\u8303\u3002
2278
+ \u751F\u6210\u5B8C\u6210\u540E\uFF0C\u8BF7\u7B80\u8981\u5217\u51FA\u751F\u6210\u7684\u6587\u4EF6\u3002`;
2279
+ try {
2280
+ await execaCommand(`claude_m2 -p "${prompt}" --add-dir ${projectPath}`, {
2281
+ stdio: "inherit",
2282
+ timeout: 3e5
2283
+ // 5分钟超时
2284
+ });
2285
+ } catch (err) {
2286
+ logger.error(`${module.name} \u4EE3\u7801\u751F\u6210\u5931\u8D25\uFF0C\u60A8\u53EF\u4EE5\u7A0D\u540E\u5728\u5F00\u53D1\u6A21\u5F0F\u4E0B\u624B\u52A8\u8FD0\u884C\u3002`);
2287
+ }
2288
+ }
2289
+ }
2290
+ }
2084
2291
  ],
2085
2292
  {
2086
2293
  concurrent: false,
@@ -2111,7 +2318,7 @@ var init_init = __esm({
2111
2318
  // src/commands/breakdown.ts
2112
2319
  import { Command as Command2 } from "commander";
2113
2320
  import inquirer2 from "inquirer";
2114
- import path7 from "path";
2321
+ import path8 from "path";
2115
2322
  import { Listr as Listr2 } from "listr2";
2116
2323
  function buildBreakdownPrompt(specContent) {
2117
2324
  return `Role: Senior Technical Lead and Agile Coach
@@ -2226,9 +2433,9 @@ var init_breakdown = __esm({
2226
2433
  choices: ctx.specs
2227
2434
  }
2228
2435
  ]);
2229
- return path7.join("docs/specs", selectedFile);
2436
+ return path8.join("docs/specs", selectedFile);
2230
2437
  }
2231
- const fullPath = specFile.startsWith("docs/specs/") ? specFile : path7.join("docs/specs", specFile);
2438
+ const fullPath = specFile.startsWith("docs/specs/") ? specFile : path8.join("docs/specs", specFile);
2232
2439
  const exists = await FileUtils.exists(fullPath);
2233
2440
  if (!exists) {
2234
2441
  throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
@@ -2298,7 +2505,7 @@ var init_breakdown = __esm({
2298
2505
  // src/commands/dev.ts
2299
2506
  import { Command as Command3 } from "commander";
2300
2507
  import inquirer3 from "inquirer";
2301
- import path8 from "path";
2508
+ import path9 from "path";
2302
2509
  async function selectSpec() {
2303
2510
  logger.step("\u6B65\u9AA4 1/3: \u9009\u62E9 spec \u6587\u4EF6...");
2304
2511
  logger.newLine();
@@ -2314,7 +2521,7 @@ async function selectSpec() {
2314
2521
  }
2315
2522
  const specs = [];
2316
2523
  for (let i = 0; i < specFiles.length; i++) {
2317
- const file = path8.join(specDir, specFiles[i]);
2524
+ const file = path9.join(specDir, specFiles[i]);
2318
2525
  const spec = await FileUtils.read(file);
2319
2526
  const status = parseSpecStatus(spec);
2320
2527
  const dependencies = parseDependencies(spec);
@@ -2636,8 +2843,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
2636
2843
  const sessionDir = "docs/sessions";
2637
2844
  await FileUtils.ensureDir(sessionDir);
2638
2845
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2639
- const specName = path8.basename(specFile, ".md");
2640
- const logFile = path8.join(sessionDir, `${timestamp}_${specName}.md`);
2846
+ const specName = path9.basename(specFile, ".md");
2847
+ const logFile = path9.join(sessionDir, `${timestamp}_${specName}.md`);
2641
2848
  const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
2642
2849
 
2643
2850
  **\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
@@ -2776,7 +2983,7 @@ var init_dev = __esm({
2776
2983
  // src/commands/add-feature.ts
2777
2984
  import { Command as Command4 } from "commander";
2778
2985
  import inquirer4 from "inquirer";
2779
- import path9 from "path";
2986
+ import path10 from "path";
2780
2987
  import { Listr as Listr3 } from "listr2";
2781
2988
  async function addFeatureFromPrd(featureName, featureSlug, specFile) {
2782
2989
  const { prdPath } = await inquirer4.prompt([
@@ -2805,7 +3012,7 @@ async function addFeatureFromPrd(featureName, featureSlug, specFile) {
2805
3012
  const specs = files.filter((f) => !f.includes("template"));
2806
3013
  ctx2.completedSpecs = [];
2807
3014
  for (const file of specs) {
2808
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3015
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2809
3016
  if (status === "\u5DF2\u5B8C\u6210") {
2810
3017
  ctx2.completedSpecs.push(file.replace(".md", ""));
2811
3018
  }
@@ -2877,7 +3084,7 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
2877
3084
  const specs = files.filter((f) => !f.includes("template"));
2878
3085
  ctx2.completedSpecs = [];
2879
3086
  for (const file of specs) {
2880
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3087
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2881
3088
  if (status === "\u5DF2\u5B8C\u6210") {
2882
3089
  ctx2.completedSpecs.push(file.replace(".md", ""));
2883
3090
  }
@@ -2974,7 +3181,7 @@ async function buildProjectContext() {
2974
3181
  const files = await FileUtils.findFiles("*.md", "docs/specs");
2975
3182
  const specs = files.filter((f) => !f.includes("template"));
2976
3183
  for (const file of specs) {
2977
- const status = await SpecUtils.getSpecStatus(path9.join("docs/specs", file));
3184
+ const status = await SpecUtils.getSpecStatus(path10.join("docs/specs", file));
2978
3185
  context.push(` - ${file.replace(".md", "")} [${status}]`);
2979
3186
  }
2980
3187
  }
@@ -3237,7 +3444,7 @@ var init_add_feature = __esm({
3237
3444
  process.exit(1);
3238
3445
  }
3239
3446
  const featureSlug = StringUtils2.toKebabCase(featureName);
3240
- const specFile = path9.join("docs/specs", `${featureSlug}.md`);
3447
+ const specFile = path10.join("docs/specs", `${featureSlug}.md`);
3241
3448
  const specExists = await FileUtils.exists(specFile);
3242
3449
  if (specExists) {
3243
3450
  logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
@@ -3274,7 +3481,7 @@ var init_add_feature = __esm({
3274
3481
 
3275
3482
  // src/commands/split-prd.ts
3276
3483
  import { Command as Command5 } from "commander";
3277
- import path10 from "path";
3484
+ import path11 from "path";
3278
3485
  import { Listr as Listr4 } from "listr2";
3279
3486
  function buildSplitPrdPrompt(prdContent, screenshots, demoRepos) {
3280
3487
  let prompt = `Role: Senior Product Manager and Technical Architect
@@ -3420,7 +3627,7 @@ var init_split_prd = __esm({
3420
3627
  ctx2.prdFiles = [];
3421
3628
  for (const ext of supportedExtensions) {
3422
3629
  const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
3423
- ctx2.prdFiles.push(...files.map((f) => path10.join(prdFolder, f)));
3630
+ ctx2.prdFiles.push(...files.map((f) => path11.join(prdFolder, f)));
3424
3631
  }
3425
3632
  if (ctx2.prdFiles.length === 0) {
3426
3633
  throw new Error(
@@ -3438,7 +3645,7 @@ var init_split_prd = __esm({
3438
3645
  {
3439
3646
  title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
3440
3647
  task: async (ctx2) => {
3441
- const screenshotDir = path10.join(prdFolder, "screenshots");
3648
+ const screenshotDir = path11.join(prdFolder, "screenshots");
3442
3649
  const dirExists = await FileUtils.exists(screenshotDir);
3443
3650
  if (!dirExists) {
3444
3651
  logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
@@ -3449,7 +3656,7 @@ var init_split_prd = __esm({
3449
3656
  ctx2.screenshots = [];
3450
3657
  for (const ext of imageExtensions) {
3451
3658
  const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
3452
- ctx2.screenshots.push(...files.map((f) => path10.join(screenshotDir, f)));
3659
+ ctx2.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
3453
3660
  }
3454
3661
  logger.success(`\u627E\u5230 ${ctx2.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
3455
3662
  }
@@ -3460,8 +3667,8 @@ var init_split_prd = __esm({
3460
3667
  const entries = await FileUtils.findFiles("*/", prdFolder);
3461
3668
  ctx2.demoRepos = [];
3462
3669
  for (const entry of entries) {
3463
- const dirPath = path10.join(prdFolder, entry);
3464
- const gitDir = path10.join(dirPath, ".git");
3670
+ const dirPath = path11.join(prdFolder, entry);
3671
+ const gitDir = path11.join(dirPath, ".git");
3465
3672
  const hasGit = await FileUtils.exists(gitDir);
3466
3673
  if (hasGit) {
3467
3674
  ctx2.demoRepos.push(dirPath);
@@ -3525,7 +3732,7 @@ var init_split_prd = __esm({
3525
3732
  // src/commands/bugfix.ts
3526
3733
  import { Command as Command6 } from "commander";
3527
3734
  import inquirer5 from "inquirer";
3528
- import path11 from "path";
3735
+ import path12 from "path";
3529
3736
  import { Listr as Listr5 } from "listr2";
3530
3737
  function generateBugId() {
3531
3738
  const date = /* @__PURE__ */ new Date();
@@ -3552,7 +3759,7 @@ async function findRelatedSpec(description) {
3552
3759
  }
3553
3760
  const keywords = extractKeywords(description);
3554
3761
  for (const file of specs) {
3555
- const filePath = path11.join(specDir, file);
3762
+ const filePath = path12.join(specDir, file);
3556
3763
  const content = await FileUtils.read(filePath);
3557
3764
  for (const keyword of keywords) {
3558
3765
  if (content.toLowerCase().includes(keyword.toLowerCase())) {
@@ -3692,7 +3899,7 @@ var init_bugfix = __esm({
3692
3899
  const relatedSpec = await findRelatedSpec(answers.description);
3693
3900
  const bugfixDir = "docs/bugfixes";
3694
3901
  await FileUtils.ensureDir(bugfixDir);
3695
- const bugfixFile = path11.join(bugfixDir, `${timestamp}_${bugId}.md`);
3902
+ const bugfixFile = path12.join(bugfixDir, `${timestamp}_${bugId}.md`);
3696
3903
  const content = formatBugfixDocument({
3697
3904
  id: bugId,
3698
3905
  severity: answers.severity,
@@ -3768,7 +3975,7 @@ var init_bugfix = __esm({
3768
3975
  const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
3769
3976
  const hotfixDir = "docs/hotfixes";
3770
3977
  await FileUtils.ensureDir(hotfixDir);
3771
- const hotfixFile = path11.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3978
+ const hotfixFile = path12.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3772
3979
  const content = formatHotfixDocument({
3773
3980
  id: hotfixId,
3774
3981
  description: answers.description,
@@ -3955,7 +4162,7 @@ var init_lint = __esm({
3955
4162
 
3956
4163
  // src/commands/status.ts
3957
4164
  import { Command as Command8 } from "commander";
3958
- import path12 from "path";
4165
+ import path13 from "path";
3959
4166
  async function displayProjectInfo() {
3960
4167
  logger.info("\u9879\u76EE\u4FE1\u606F:");
3961
4168
  logger.newLine();
@@ -3991,7 +4198,7 @@ async function displayFeatureInventory() {
3991
4198
  }
3992
4199
  const inventory = [];
3993
4200
  for (const file of specs) {
3994
- const filePath = path12.join(specDir, file);
4201
+ const filePath = path13.join(specDir, file);
3995
4202
  const content = await FileUtils.read(filePath);
3996
4203
  const status = parseSpecStatus2(content);
3997
4204
  inventory.push({
@@ -4050,7 +4257,7 @@ async function displayRecentActivity() {
4050
4257
  }
4051
4258
  const sorted = files.sort().reverse().slice(0, 5);
4052
4259
  for (const file of sorted) {
4053
- const filePath = path12.join(sessionDir, file);
4260
+ const filePath = path13.join(sessionDir, file);
4054
4261
  const stat = await FileUtils.read(filePath);
4055
4262
  const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
4056
4263
  const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
@@ -4120,13 +4327,13 @@ var init_status = __esm({
4120
4327
 
4121
4328
  // src/commands/detect-deps.ts
4122
4329
  import { Command as Command9 } from "commander";
4123
- import path13 from "path";
4330
+ import path14 from "path";
4124
4331
  import inquirer6 from "inquirer";
4125
4332
  async function detectDependencies(specFile) {
4126
4333
  logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
4127
4334
  const projectDir = ".";
4128
4335
  const allDeps = /* @__PURE__ */ new Set();
4129
- const backendDir = path13.join(projectDir, "backend");
4336
+ const backendDir = path14.join(projectDir, "backend");
4130
4337
  const backendExists = await FileUtils.exists(backendDir);
4131
4338
  if (backendExists) {
4132
4339
  const apiDeps = await scanBackendApiCalls(backendDir);
@@ -4136,7 +4343,7 @@ async function detectDependencies(specFile) {
4136
4343
  const serviceDeps = await scanBackendServiceRefs(backendDir);
4137
4344
  serviceDeps.forEach((d) => allDeps.add(d));
4138
4345
  }
4139
- const frontendDir = path13.join(projectDir, "frontend");
4346
+ const frontendDir = path14.join(projectDir, "frontend");
4140
4347
  const frontendExists = await FileUtils.exists(frontendDir);
4141
4348
  if (frontendExists) {
4142
4349
  const frontendDeps = await scanFrontendApiCalls(frontendDir);
@@ -4177,11 +4384,11 @@ async function detectDependencies(specFile) {
4177
4384
  }
4178
4385
  async function scanBackendApiCalls(backendDir) {
4179
4386
  const deps = [];
4180
- const srcDir = path13.join(backendDir, "src");
4387
+ const srcDir = path14.join(backendDir, "src");
4181
4388
  try {
4182
4389
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4183
4390
  for (const file of javaFiles) {
4184
- const filePath = path13.join(srcDir, file);
4391
+ const filePath = path14.join(srcDir, file);
4185
4392
  const content = await FileUtils.read(filePath);
4186
4393
  const pathRegex = /"(\/api\/[^"]+)"/g;
4187
4394
  let match;
@@ -4199,11 +4406,11 @@ async function scanBackendApiCalls(backendDir) {
4199
4406
  }
4200
4407
  async function scanBackendEntityRelations(backendDir) {
4201
4408
  const deps = [];
4202
- const srcDir = path13.join(backendDir, "src");
4409
+ const srcDir = path14.join(backendDir, "src");
4203
4410
  try {
4204
4411
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4205
4412
  for (const file of javaFiles) {
4206
- const filePath = path13.join(srcDir, file);
4413
+ const filePath = path14.join(srcDir, file);
4207
4414
  const content = await FileUtils.read(filePath);
4208
4415
  if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
4209
4416
  const typeRegex = /type\s*=\s*(\w+)/g;
@@ -4219,11 +4426,11 @@ async function scanBackendEntityRelations(backendDir) {
4219
4426
  }
4220
4427
  async function scanBackendServiceRefs(backendDir) {
4221
4428
  const deps = [];
4222
- const srcDir = path13.join(backendDir, "src");
4429
+ const srcDir = path14.join(backendDir, "src");
4223
4430
  try {
4224
4431
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4225
4432
  for (const file of javaFiles) {
4226
- const filePath = path13.join(srcDir, file);
4433
+ const filePath = path14.join(srcDir, file);
4227
4434
  const content = await FileUtils.read(filePath);
4228
4435
  const serviceRegex = /private\s+(\w+)Service/g;
4229
4436
  let match;
@@ -4239,11 +4446,11 @@ async function scanBackendServiceRefs(backendDir) {
4239
4446
  }
4240
4447
  async function scanFrontendApiCalls(frontendDir) {
4241
4448
  const deps = [];
4242
- const srcDir = path13.join(frontendDir, "src");
4449
+ const srcDir = path14.join(frontendDir, "src");
4243
4450
  try {
4244
4451
  const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
4245
4452
  for (const file of tsFiles) {
4246
- const filePath = path13.join(srcDir, file);
4453
+ const filePath = path14.join(srcDir, file);
4247
4454
  const content = await FileUtils.read(filePath);
4248
4455
  const pathRegex = /"(\/api\/[^"]+)"/g;
4249
4456
  let match;
@@ -4360,7 +4567,7 @@ var init_detect_deps = __esm({
4360
4567
  process.exit(1);
4361
4568
  }
4362
4569
  for (const spec of specs) {
4363
- const specPath = path13.join(specsDir, spec);
4570
+ const specPath = path14.join(specsDir, spec);
4364
4571
  logger.step(`\u5904\u7406: ${spec}`);
4365
4572
  await detectDependencies(specPath);
4366
4573
  logger.newLine();
@@ -4387,10 +4594,10 @@ var init_detect_deps = __esm({
4387
4594
 
4388
4595
  // src/commands/sync-memory.ts
4389
4596
  import { Command as Command10 } from "commander";
4390
- import path14 from "path";
4597
+ import path15 from "path";
4391
4598
  async function syncFeatureInventory(aiMemoryFile, projectDir) {
4392
4599
  logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
4393
- const specsDir = path14.join(projectDir, "docs/specs");
4600
+ const specsDir = path15.join(projectDir, "docs/specs");
4394
4601
  const exists = await FileUtils.exists(specsDir);
4395
4602
  if (!exists) {
4396
4603
  return;
@@ -4405,7 +4612,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4405
4612
  for (const specFile of specs) {
4406
4613
  const name = specFile.replace(".md", "");
4407
4614
  const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
4408
- const specPath = path14.join(specsDir, specFile);
4615
+ const specPath = path15.join(specsDir, specFile);
4409
4616
  const content = await FileUtils.read(specPath);
4410
4617
  const status = parseSpecStatus3(content);
4411
4618
  const progress = getSpecProgress(content);
@@ -4423,7 +4630,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4423
4630
  }
4424
4631
  async function syncApiInventory(aiMemoryFile, projectDir) {
4425
4632
  logger.step("\u540C\u6B65 API \u5217\u8868...");
4426
- const backendDir = path14.join(projectDir, "backend");
4633
+ const backendDir = path15.join(projectDir, "backend");
4427
4634
  const exists = await FileUtils.exists(backendDir);
4428
4635
  if (!exists) {
4429
4636
  return;
@@ -4433,13 +4640,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
4433
4640
  lines.push("");
4434
4641
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
4435
4642
  lines.push("");
4436
- const srcDir = path14.join(backendDir, "src");
4643
+ const srcDir = path15.join(backendDir, "src");
4437
4644
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4438
4645
  if (controllers.length === 0) {
4439
4646
  lines.push("\u6682\u65E0 API");
4440
4647
  } else {
4441
4648
  for (const controllerFile of controllers) {
4442
- const controllerPath = path14.join(srcDir, controllerFile);
4649
+ const controllerPath = path15.join(srcDir, controllerFile);
4443
4650
  const controllerName = controllerFile.replace(".java", "");
4444
4651
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4445
4652
  lines.push(`### ${module} \u6A21\u5757`);
@@ -4516,7 +4723,7 @@ function extractMethodComment(content, methodName) {
4516
4723
  }
4517
4724
  async function syncDataModels(aiMemoryFile, projectDir) {
4518
4725
  logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
4519
- const backendDir = path14.join(projectDir, "backend");
4726
+ const backendDir = path15.join(projectDir, "backend");
4520
4727
  const exists = await FileUtils.exists(backendDir);
4521
4728
  if (!exists) {
4522
4729
  return;
@@ -4526,7 +4733,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4526
4733
  lines.push("");
4527
4734
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
4528
4735
  lines.push("");
4529
- const srcDir = path14.join(backendDir, "src");
4736
+ const srcDir = path15.join(backendDir, "src");
4530
4737
  const entities = await FileUtils.findFiles("*Entity.java", srcDir);
4531
4738
  if (entities.length === 0) {
4532
4739
  lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
@@ -4534,7 +4741,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4534
4741
  lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
4535
4742
  lines.push("|------|------|------|------|");
4536
4743
  for (const entityFile of entities) {
4537
- const entityPath = path14.join(srcDir, entityFile);
4744
+ const entityPath = path15.join(srcDir, entityFile);
4538
4745
  const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
4539
4746
  const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
4540
4747
  const content = await FileUtils.read(entityPath);
@@ -4656,15 +4863,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
4656
4863
  await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
4657
4864
  }
4658
4865
  function extractRepoName(repository) {
4659
- let path18 = repository;
4660
- path18 = path18.replace(/^https?:\/\//, "");
4661
- path18 = path18.replace(/^git@/, "");
4662
- const parts = path18.split("/");
4866
+ let path19 = repository;
4867
+ path19 = path19.replace(/^https?:\/\//, "");
4868
+ path19 = path19.replace(/^git@/, "");
4869
+ const parts = path19.split("/");
4663
4870
  if (parts.length > 1) {
4664
- path18 = parts.slice(1).join("/");
4871
+ path19 = parts.slice(1).join("/");
4665
4872
  }
4666
- path18 = path18.replace(/\.git$/, "");
4667
- return path18;
4873
+ path19 = path19.replace(/\.git$/, "");
4874
+ return path19;
4668
4875
  }
4669
4876
  var syncMemoryCommand;
4670
4877
  var init_sync_memory = __esm({
@@ -4709,11 +4916,11 @@ var init_sync_memory = __esm({
4709
4916
 
4710
4917
  // src/commands/check-api.ts
4711
4918
  import { Command as Command11 } from "commander";
4712
- import path15 from "path";
4919
+ import path16 from "path";
4713
4920
  import inquirer7 from "inquirer";
4714
4921
  import { Listr as Listr6 } from "listr2";
4715
4922
  async function checkApiConflicts(projectDir) {
4716
- const backendDir = path15.join(projectDir, "backend");
4923
+ const backendDir = path16.join(projectDir, "backend");
4717
4924
  const exists = await FileUtils.exists(backendDir);
4718
4925
  if (!exists) {
4719
4926
  logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
@@ -4722,10 +4929,10 @@ async function checkApiConflicts(projectDir) {
4722
4929
  logger.step("\u626B\u63CF\u540E\u7AEF API...");
4723
4930
  logger.newLine();
4724
4931
  const apiMap = /* @__PURE__ */ new Map();
4725
- const srcDir = path15.join(backendDir, "src");
4932
+ const srcDir = path16.join(backendDir, "src");
4726
4933
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4727
4934
  for (const controllerFile of controllers) {
4728
- const controllerPath = path15.join(srcDir, controllerFile);
4935
+ const controllerPath = path16.join(srcDir, controllerFile);
4729
4936
  const apis = await extractApisFromController(controllerPath);
4730
4937
  for (const api of apis) {
4731
4938
  const key = `${api.method}:${api.path}`;
@@ -4756,8 +4963,8 @@ async function checkApiConflicts(projectDir) {
4756
4963
  }
4757
4964
  }
4758
4965
  async function detectApiChanges(projectDir) {
4759
- const backendDir = path15.join(projectDir, "backend");
4760
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
4966
+ const backendDir = path16.join(projectDir, "backend");
4967
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4761
4968
  const registryExists = await FileUtils.exists(registryFile);
4762
4969
  if (!registryExists) {
4763
4970
  logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
@@ -4769,10 +4976,10 @@ async function detectApiChanges(projectDir) {
4769
4976
  const registryContent = await FileUtils.read(registryFile);
4770
4977
  const existingApis = extractApisFromRegistry(registryContent);
4771
4978
  const currentApis = /* @__PURE__ */ new Map();
4772
- const srcDir = path15.join(backendDir, "src");
4979
+ const srcDir = path16.join(backendDir, "src");
4773
4980
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4774
4981
  for (const controllerFile of controllers) {
4775
- const controllerPath = path15.join(srcDir, controllerFile);
4982
+ const controllerPath = path16.join(srcDir, controllerFile);
4776
4983
  const apis = await extractApisFromController(controllerPath);
4777
4984
  for (const api of apis) {
4778
4985
  const key = `${api.method}:${api.path}`;
@@ -4834,9 +5041,9 @@ async function detectApiChanges(projectDir) {
4834
5041
  }
4835
5042
  }
4836
5043
  async function generateApiRegistry(projectDir) {
4837
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
5044
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4838
5045
  logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
4839
- await FileUtils.ensureDir(path15.dirname(registryFile));
5046
+ await FileUtils.ensureDir(path16.dirname(registryFile));
4840
5047
  const header = `# API Registry
4841
5048
 
4842
5049
  > \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
@@ -4865,14 +5072,14 @@ async function generateApiRegistry(projectDir) {
4865
5072
  *\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
4866
5073
  `;
4867
5074
  let content = header;
4868
- const backendDir = path15.join(projectDir, "backend");
5075
+ const backendDir = path16.join(projectDir, "backend");
4869
5076
  const exists = await FileUtils.exists(backendDir);
4870
5077
  if (exists) {
4871
- const srcDir = path15.join(backendDir, "src");
5078
+ const srcDir = path16.join(backendDir, "src");
4872
5079
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4873
5080
  const moduleMap = /* @__PURE__ */ new Map();
4874
5081
  for (const controllerFile of controllers) {
4875
- const controllerPath = path15.join(srcDir, controllerFile);
5082
+ const controllerPath = path16.join(srcDir, controllerFile);
4876
5083
  const controllerName = controllerFile.replace(".java", "");
4877
5084
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4878
5085
  if (!moduleMap.has(module)) {
@@ -4956,10 +5163,10 @@ function extractApisFromRegistry(registryContent) {
4956
5163
  let match;
4957
5164
  while ((match = apiRegex.exec(registryContent)) !== null) {
4958
5165
  const method = match[1];
4959
- const path18 = match[2].trim();
5166
+ const path19 = match[2].trim();
4960
5167
  const description = match[3].trim();
4961
- const key = `${method}:${path18}`;
4962
- apis.set(key, { method, path: path18, description });
5168
+ const key = `${method}:${path19}`;
5169
+ apis.set(key, { method, path: path19, description });
4963
5170
  }
4964
5171
  return apis;
4965
5172
  }
@@ -5047,7 +5254,7 @@ var init_check_api = __esm({
5047
5254
 
5048
5255
  // src/commands/logs.ts
5049
5256
  import { Command as Command12 } from "commander";
5050
- import path16 from "path";
5257
+ import path17 from "path";
5051
5258
  import inquirer8 from "inquirer";
5052
5259
  async function collectLogFiles(targetDir) {
5053
5260
  const logs = [];
@@ -5055,7 +5262,7 @@ async function collectLogFiles(targetDir) {
5055
5262
  const allFiles = await FileUtils.findFiles("*.md", targetDir);
5056
5263
  const filtered = allFiles.filter((f) => f !== "index.md");
5057
5264
  for (const file of filtered) {
5058
- const filePath = path16.join(targetDir, file);
5265
+ const filePath = path17.join(targetDir, file);
5059
5266
  const stat = await FileUtils.exists(filePath);
5060
5267
  if (stat) {
5061
5268
  logs.push(filePath);
@@ -5095,7 +5302,7 @@ var init_logs = __esm({
5095
5302
  case "":
5096
5303
  case "today": {
5097
5304
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5098
- targetDir = path16.join(sessionsDir, today);
5305
+ targetDir = path17.join(sessionsDir, today);
5099
5306
  const todayExists = await FileUtils.exists(targetDir);
5100
5307
  if (!todayExists) {
5101
5308
  logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
@@ -5111,7 +5318,7 @@ var init_logs = __esm({
5111
5318
  break;
5112
5319
  }
5113
5320
  default: {
5114
- targetDir = path16.join(sessionsDir, filter);
5321
+ targetDir = path17.join(sessionsDir, filter);
5115
5322
  const dateExists = await FileUtils.exists(targetDir);
5116
5323
  if (!dateExists) {
5117
5324
  logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
@@ -5135,7 +5342,7 @@ var init_logs = __esm({
5135
5342
  process.exit(0);
5136
5343
  }
5137
5344
  for (let i = 0; i < logs.length; i++) {
5138
- const relPath = path16.relative(sessionsDir, logs[i]);
5345
+ const relPath = path17.relative(sessionsDir, logs[i]);
5139
5346
  logger.step(`${i + 1}) ${relPath}`);
5140
5347
  }
5141
5348
  logger.newLine();
@@ -5175,17 +5382,17 @@ var init_logs = __esm({
5175
5382
 
5176
5383
  // src/commands/update.ts
5177
5384
  import { Command as Command13 } from "commander";
5178
- import path17 from "path";
5385
+ import path18 from "path";
5179
5386
  import { execa as execa4 } from "execa";
5180
5387
  import inquirer9 from "inquirer";
5181
- import fs4 from "fs-extra";
5388
+ import fs5 from "fs-extra";
5182
5389
  async function performUpdate(projectPath, updates) {
5183
5390
  logger.newLine();
5184
5391
  logger.info("\u5F00\u59CB\u66F4\u65B0\u6A21\u677F...");
5185
5392
  for (const update of updates) {
5186
5393
  const { type, info, updateOptions } = update;
5187
5394
  const targetDir = type === "frontend" ? "frontend" : "backend";
5188
- const targetPath = path17.join(projectPath, targetDir);
5395
+ const targetPath = path18.join(projectPath, targetDir);
5189
5396
  logger.newLine();
5190
5397
  logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
5191
5398
  if (updateOptions?.tag || updateOptions?.branch) {
@@ -5216,8 +5423,8 @@ async function performUpdate(projectPath, updates) {
5216
5423
  }
5217
5424
  }
5218
5425
  const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
5219
- const backupDir = path17.join(projectPath, `.backup-${Date.now()}`);
5220
- await fs4.copy(targetPath, path17.join(backupDir, targetDir));
5426
+ const backupDir = path18.join(projectPath, `.backup-${Date.now()}`);
5427
+ await fs5.copy(targetPath, path18.join(backupDir, targetDir));
5221
5428
  logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
5222
5429
  if (updateOptions?.dryRun) {
5223
5430
  logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
@@ -5227,7 +5434,7 @@ async function performUpdate(projectPath, updates) {
5227
5434
  continue;
5228
5435
  }
5229
5436
  try {
5230
- const tempDir = path17.join(projectPath, `.template-update-${Date.now()}`);
5437
+ const tempDir = path18.join(projectPath, `.template-update-${Date.now()}`);
5231
5438
  await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
5232
5439
  stdio: "pipe"
5233
5440
  });
@@ -5244,17 +5451,17 @@ async function performUpdate(projectPath, updates) {
5244
5451
  const currentFiles = await FileUtils.findFiles("*", targetPath);
5245
5452
  for (const file of currentFiles) {
5246
5453
  if (!keepFiles.includes(file)) {
5247
- const filePath = path17.join(targetPath, file);
5454
+ const filePath = path18.join(targetPath, file);
5248
5455
  try {
5249
- await fs4.remove(filePath);
5456
+ await fs5.remove(filePath);
5250
5457
  } catch {
5251
5458
  }
5252
5459
  }
5253
5460
  }
5254
- await fs4.copy(tempDir, targetPath, {
5461
+ await fs5.copy(tempDir, targetPath, {
5255
5462
  filter: (src) => !src.includes(".git")
5256
5463
  });
5257
- await fs4.remove(tempDir);
5464
+ await fs5.remove(tempDir);
5258
5465
  await updateTemplateVersion(projectPath, type, commit.trim(), {
5259
5466
  tag: updateOptions?.tag || latestTag,
5260
5467
  branch: updateOptions?.branch
@@ -5265,9 +5472,9 @@ async function performUpdate(projectPath, updates) {
5265
5472
  } catch (error) {
5266
5473
  logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
5267
5474
  logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
5268
- await fs4.remove(targetPath);
5269
- await fs4.copy(path17.join(backupDir, targetDir), targetPath);
5270
- await fs4.remove(backupDir);
5475
+ await fs5.remove(targetPath);
5476
+ await fs5.copy(path18.join(backupDir, targetDir), targetPath);
5477
+ await fs5.remove(backupDir);
5271
5478
  logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
5272
5479
  }
5273
5480
  }