yg-team-cli 2.2.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/index.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
  /**
@@ -428,7 +440,7 @@ __export(user_config_exports, {
428
440
  UserConfigManager: () => UserConfigManager,
429
441
  userConfigManager: () => userConfigManager
430
442
  });
431
- import path5 from "path";
443
+ import path6 from "path";
432
444
  import os2 from "os";
433
445
  import crypto from "crypto";
434
446
  var UserConfigManager, userConfigManager;
@@ -441,8 +453,8 @@ var init_user_config = __esm({
441
453
  UserConfigManager = class {
442
454
  configPath;
443
455
  constructor() {
444
- const configDir = path5.join(os2.homedir(), ".team-cli");
445
- this.configPath = path5.join(configDir, "config.json");
456
+ const configDir = path6.join(os2.homedir(), ".team-cli");
457
+ this.configPath = path6.join(configDir, "config.json");
446
458
  }
447
459
  /**
448
460
  * 加载用户配置
@@ -469,7 +481,7 @@ var init_user_config = __esm({
469
481
  */
470
482
  async save(config) {
471
483
  try {
472
- const configDir = path5.dirname(this.configPath);
484
+ const configDir = path6.dirname(this.configPath);
473
485
  await FileUtils.ensureDir(configDir);
474
486
  const configToSave = JSON.parse(JSON.stringify(config));
475
487
  if (configToSave.gitlab?.accessToken) {
@@ -575,7 +587,7 @@ var init_user_config = __esm({
575
587
  * 获取配置目录
576
588
  */
577
589
  getConfigDir() {
578
- return path5.dirname(this.configPath);
590
+ return path6.dirname(this.configPath);
579
591
  }
580
592
  /**
581
593
  * 获取配置文件路径
@@ -790,22 +802,22 @@ var init_gitlab_api = __esm({
790
802
  * 从 Git URL 中提取项目路径
791
803
  */
792
804
  static parseProjectPath(repository) {
793
- let path18 = repository;
794
- if (path18.startsWith("git@")) {
795
- path18 = path18.replace(/^git@/, "");
796
- const colonIndex = path18.indexOf(":");
805
+ let path19 = repository;
806
+ if (path19.startsWith("git@")) {
807
+ path19 = path19.replace(/^git@/, "");
808
+ const colonIndex = path19.indexOf(":");
797
809
  if (colonIndex !== -1) {
798
- path18 = path18.substring(colonIndex + 1);
810
+ path19 = path19.substring(colonIndex + 1);
799
811
  }
800
812
  } else {
801
- path18 = path18.replace(/^https?:\/\//, "");
802
- const parts = path18.split("/");
813
+ path19 = path19.replace(/^https?:\/\//, "");
814
+ const parts = path19.split("/");
803
815
  if (parts.length > 1) {
804
- path18 = parts.slice(1).join("/");
816
+ path19 = parts.slice(1).join("/");
805
817
  }
806
818
  }
807
- path18 = path18.replace(/\.git$/, "");
808
- return path18;
819
+ path19 = path19.replace(/\.git$/, "");
820
+ return path19;
809
821
  }
810
822
  /**
811
823
  * 编码项目路径用于 API 请求
@@ -867,8 +879,8 @@ init_esm_shims();
867
879
  init_logger();
868
880
  import { Command } from "commander";
869
881
  import inquirer from "inquirer";
870
- import path6 from "path";
871
- import fs3 from "fs-extra";
882
+ import path7 from "path";
883
+ import fs4 from "fs-extra";
872
884
 
873
885
  // src/lib/claude.ts
874
886
  init_esm_shims();
@@ -1323,6 +1335,128 @@ async function updateTemplateVersion(projectPath, type, commit, options) {
1323
1335
  await saveTemplateConfig(projectPath, config);
1324
1336
  }
1325
1337
 
1338
+ // src/lib/module-registry.ts
1339
+ init_esm_shims();
1340
+ init_utils();
1341
+ import path5 from "path";
1342
+ import fs3 from "fs-extra";
1343
+ var ModuleManager = class {
1344
+ static MODULES = [
1345
+ {
1346
+ id: "acl",
1347
+ name: "ACL (Access Control List)",
1348
+ description: "\u57FA\u4E8E\u89D2\u8272\u7684\u8BBF\u95EE\u63A7\u5236\u6A21\u5757\uFF08\u672C\u5730\u6A21\u677F\u7248\uFF09",
1349
+ type: "local",
1350
+ specs: ["acl/spec.md"],
1351
+ backendFragments: [
1352
+ {
1353
+ source: "acl/backend/AclResultCode.java",
1354
+ target: "common/enums/AclResultCode.java"
1355
+ },
1356
+ {
1357
+ source: "acl/backend/RequiredPermission.java",
1358
+ target: "common/annotation/RequiredPermission.java"
1359
+ }
1360
+ ]
1361
+ },
1362
+ {
1363
+ id: "permission-remote",
1364
+ name: "\u6743\u9650\u7BA1\u7406 (\u8FDC\u7A0B MCP \u7248)",
1365
+ description: "\u4ECE api-metadata \u5B9E\u65F6\u83B7\u53D6\u6700\u65B0\u6743\u9650\u63A5\u53E3\u5B9A\u4E49\u5E76\u751F\u6210\u4EE3\u7801",
1366
+ type: "remote",
1367
+ mcpServer: "api-metadata",
1368
+ specs: ["permission-remote/spec.md"]
1369
+ },
1370
+ {
1371
+ id: "audit-log",
1372
+ name: "\u5BA1\u8BA1\u65E5\u5FD7 (Audit Log)",
1373
+ description: "\u8BB0\u5F55\u7528\u6237\u64CD\u4F5C\u65E5\u5FD7\uFF0C\u652F\u6301 AOP \u81EA\u52A8\u62E6\u622A\u548C\u5F02\u6B65\u5B58\u50A8",
1374
+ type: "local",
1375
+ specs: ["audit-log/spec.md"]
1376
+ }
1377
+ ];
1378
+ /**
1379
+ * 获取所有可用模块
1380
+ */
1381
+ static getAvailableModules() {
1382
+ return [...this.MODULES];
1383
+ }
1384
+ /**
1385
+ * 根据 ID 获取模块
1386
+ */
1387
+ static getModuleById(id) {
1388
+ return this.MODULES.find((m) => m.id === id);
1389
+ }
1390
+ /**
1391
+ * 注入模块到项目
1392
+ */
1393
+ static async injectModule(projectPath, moduleId, templatesDir) {
1394
+ const module = this.getModuleById(moduleId);
1395
+ if (!module) return;
1396
+ for (const specRelPath of module.specs) {
1397
+ const sourcePath = path5.join(templatesDir, "modules", specRelPath);
1398
+ const targetPath = path5.join(projectPath, "docs/specs", path5.basename(specRelPath));
1399
+ if (await FileUtils.exists(sourcePath)) {
1400
+ await FileUtils.copy(sourcePath, targetPath);
1401
+ } else if (module.type === "remote") {
1402
+ await this.generateRemoteSpecPlaceholder(targetPath, module);
1403
+ }
1404
+ }
1405
+ if (module.type === "local" && module.backendFragments) {
1406
+ const backendBase = path5.join(projectPath, "backend/src/main/java");
1407
+ const basePackagePath = await this.findBasePackage(backendBase);
1408
+ if (basePackagePath) {
1409
+ for (const fragment of module.backendFragments) {
1410
+ const sourcePath = path5.join(templatesDir, "modules", fragment.source);
1411
+ const targetPath = path5.join(basePackagePath, fragment.target);
1412
+ if (await FileUtils.exists(sourcePath)) {
1413
+ await FileUtils.ensureDir(path5.dirname(targetPath));
1414
+ await FileUtils.copy(sourcePath, targetPath);
1415
+ }
1416
+ }
1417
+ }
1418
+ } else if (module.type === "remote") {
1419
+ }
1420
+ }
1421
+ /**
1422
+ * 生成远程模块的 Spec 占位符
1423
+ */
1424
+ static async generateRemoteSpecPlaceholder(targetPath, module) {
1425
+ const content = `# ${module.name} (Remote)
1426
+
1427
+ ## \u529F\u80FD\u6982\u8FF0
1428
+ \u8BE5\u6A21\u5757\u901A\u8FC7\u8FDC\u7A0B MCP \u670D\u52A1 \`${module.mcpServer}\` \u83B7\u53D6\u5B9E\u65F6\u5143\u6570\u636E\u3002
1429
+
1430
+ ## \u4F7F\u7528\u8BF4\u660E
1431
+ 1. \u786E\u4FDD\u5DF2\u5B89\u88C5\u5E76\u914D\u7F6E MCP \u670D\u52A1\u3002
1432
+ 2. \u8FD0\u884C \`team-cli dev\` \u5E76\u9009\u62E9\u6B64 Spec\u3002
1433
+ 3. Claude \u5C06\u81EA\u52A8\u8C03\u7528 MCP \u83B7\u53D6\u6700\u65B0\u7684 API \u4FE1\u606F\u5E76\u5B8C\u6210\u5B9E\u73B0\u3002
1434
+ `;
1435
+ await FileUtils.write(targetPath, content);
1436
+ }
1437
+ /**
1438
+ * 自动探测项目的 Base Package
1439
+ */
1440
+ static async findBasePackage(searchDir) {
1441
+ if (!await FileUtils.exists(searchDir)) return null;
1442
+ const files = await FileUtils.findFiles("**/Application.java", searchDir);
1443
+ if (files.length > 0) {
1444
+ return path5.dirname(path5.join(searchDir, files[0]));
1445
+ }
1446
+ let current = searchDir;
1447
+ while (true) {
1448
+ const items = await fs3.readdir(current);
1449
+ const dirs = items.filter((c) => fs3.statSync(path5.join(current, c)).isDirectory());
1450
+ if (dirs.length === 1 && !["model", "controller", "service", "mapper", "config", "common"].includes(dirs[0])) {
1451
+ current = path5.join(current, dirs[0]);
1452
+ } else {
1453
+ break;
1454
+ }
1455
+ }
1456
+ return current;
1457
+ }
1458
+ };
1459
+
1326
1460
  // src/commands/init.ts
1327
1461
  var 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) => {
1328
1462
  try {
@@ -1355,7 +1489,19 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1355
1489
  logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
1356
1490
  process.exit(1);
1357
1491
  }
1358
- const projectPath = path6.resolve(options.dir, projectName);
1492
+ const projectPath = path7.resolve(options.dir, projectName);
1493
+ const availableModules = ModuleManager.getAvailableModules();
1494
+ const { selectedModules } = await inquirer.prompt([
1495
+ {
1496
+ type: "checkbox",
1497
+ name: "selectedModules",
1498
+ message: "\u8BF7\u9009\u62E9\u8981\u5F00\u542F\u7684\u5185\u7F6E\u901A\u7528\u6A21\u5757:",
1499
+ choices: availableModules.map((m) => ({
1500
+ name: `${m.name} - ${m.description}`,
1501
+ value: m.id
1502
+ }))
1503
+ }
1504
+ ]);
1359
1505
  if (await FileUtils.exists(projectPath)) {
1360
1506
  logger.error(`\u76EE\u5F55\u5DF2\u5B58\u5728: ${projectPath}`);
1361
1507
  logger.info("\u8BF7\u9009\u62E9\u5176\u4ED6\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55");
@@ -1374,11 +1520,11 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1374
1520
  title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55",
1375
1521
  task: async () => {
1376
1522
  await FileUtils.ensureDir(projectPath);
1377
- await FileUtils.ensureDir(path6.join(projectPath, "frontend"));
1378
- await FileUtils.ensureDir(path6.join(projectPath, "backend"));
1379
- await FileUtils.ensureDir(path6.join(projectPath, "docs/specs"));
1380
- await FileUtils.ensureDir(path6.join(projectPath, "docs/api"));
1381
- await FileUtils.ensureDir(path6.join(projectPath, "docs/sessions"));
1523
+ await FileUtils.ensureDir(path7.join(projectPath, "frontend"));
1524
+ await FileUtils.ensureDir(path7.join(projectPath, "backend"));
1525
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/specs"));
1526
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/api"));
1527
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/sessions"));
1382
1528
  }
1383
1529
  },
1384
1530
  {
@@ -1426,7 +1572,7 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1426
1572
  {
1427
1573
  title: "\u751F\u6210 Docker \u914D\u7F6E",
1428
1574
  task: async () => {
1429
- await generateDockerFiles(projectPath);
1575
+ await generateDockerFiles();
1430
1576
  }
1431
1577
  }
1432
1578
  ] : [],
@@ -1437,7 +1583,61 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1437
1583
  await initGit(projectPath, projectName);
1438
1584
  }
1439
1585
  }
1440
- ] : []
1586
+ ] : [],
1587
+ {
1588
+ title: "\u6CE8\u518C\u8FDC\u7A0B MCP \u670D\u52A1",
1589
+ task: async () => {
1590
+ const hasRemoteModule = selectedModules.some(
1591
+ (id) => ModuleManager.getModuleById(id)?.type === "remote"
1592
+ );
1593
+ if (hasRemoteModule) {
1594
+ const mcpCmd = 'claude_m2 mcp add --transport sse --header "Authorization: Bearer mcp_00557dabb71297b4f9ac5fe748395f2c" -- api-metadata https://api-metadata.yungu.org/sse';
1595
+ const { execaCommand } = await import("execa");
1596
+ try {
1597
+ await execaCommand(mcpCmd);
1598
+ logger.info("\u8FDC\u7A0B MCP \u670D\u52A1 api-metadata \u5DF2\u6CE8\u518C");
1599
+ } catch (err) {
1600
+ logger.warn("\u8FDC\u7A0B MCP \u670D\u52A1\u6CE8\u518C\u5931\u8D25\uFF0C\u53EF\u80FD\u5DF2\u5B58\u5728\u6216\u6743\u9650\u4E0D\u8DB3");
1601
+ }
1602
+ }
1603
+ }
1604
+ },
1605
+ {
1606
+ title: "\u6CE8\u5165\u901A\u7528\u6A21\u5757",
1607
+ task: async () => {
1608
+ if (selectedModules.length === 0) return;
1609
+ const templatesDir = path7.resolve(FileUtils.getDirName(import.meta.url), "../templates");
1610
+ for (const moduleId of selectedModules) {
1611
+ await ModuleManager.injectModule(projectPath, moduleId, templatesDir);
1612
+ logger.info(`\u6CE8\u5165\u6A21\u5757: ${moduleId}`);
1613
+ }
1614
+ }
1615
+ },
1616
+ {
1617
+ title: "\u6267\u884C\u8FDC\u7A0B\u6A21\u5757\u4EE3\u7801\u751F\u6210",
1618
+ task: async () => {
1619
+ const remoteModules = selectedModules.map((id) => ModuleManager.getModuleById(id)).filter((m) => m?.type === "remote");
1620
+ if (remoteModules.length === 0) return;
1621
+ const { execaCommand } = await import("execa");
1622
+ for (const module of remoteModules) {
1623
+ if (!module) continue;
1624
+ logger.info(`\u6B63\u5728\u4E3A\u60A8\u751F\u6210 ${module.name} \u7684\u8FDC\u7A0B\u8C03\u7528\u4EE3\u7801...`);
1625
+ 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
1626
+ \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
1627
+ \u8BF7\u786E\u4FDD\u4EE3\u7801\u7B26\u5408 \`CONVENTIONS.md\` \u4E2D\u7684\u89C4\u8303\u3002
1628
+ \u751F\u6210\u5B8C\u6210\u540E\uFF0C\u8BF7\u7B80\u8981\u5217\u51FA\u751F\u6210\u7684\u6587\u4EF6\u3002`;
1629
+ try {
1630
+ await execaCommand(`claude_m2 -p "${prompt}" --add-dir ${projectPath}`, {
1631
+ stdio: "inherit",
1632
+ timeout: 3e5
1633
+ // 5分钟超时
1634
+ });
1635
+ } catch (err) {
1636
+ 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`);
1637
+ }
1638
+ }
1639
+ }
1640
+ }
1441
1641
  ],
1442
1642
  {
1443
1643
  concurrent: false,
@@ -1558,7 +1758,7 @@ docs/
1558
1758
  \u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
1559
1759
  \`\`\`
1560
1760
  `;
1561
- await FileUtils.write(path6.join(projectPath, "TECH_STACK.md"), content);
1761
+ await FileUtils.write(path7.join(projectPath, "TECH_STACK.md"), content);
1562
1762
  }
1563
1763
  async function generateConventions(projectPath) {
1564
1764
  const content = `# \u5F00\u53D1\u89C4\u8303
@@ -1861,7 +2061,7 @@ test('renders user name', () => {
1861
2061
  });
1862
2062
  \`\`\`
1863
2063
  `;
1864
- await FileUtils.write(path6.join(projectPath, "CONVENTIONS.md"), content);
2064
+ await FileUtils.write(path7.join(projectPath, "CONVENTIONS.md"), content);
1865
2065
  }
1866
2066
  async function generateAIMemory(projectPath, projectName) {
1867
2067
  const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
@@ -1920,7 +2120,7 @@ async function generateAIMemory(projectPath, projectName) {
1920
2120
  | Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
1921
2121
  |--------|------|---------|------|
1922
2122
  `;
1923
- await FileUtils.write(path6.join(projectPath, "AI_MEMORY.md"), content);
2123
+ await FileUtils.write(path7.join(projectPath, "AI_MEMORY.md"), content);
1924
2124
  }
1925
2125
  async function generateSpecTemplate(projectPath) {
1926
2126
  const content = `# [\u529F\u80FD\u6807\u9898]
@@ -1984,14 +2184,14 @@ async function generateSpecTemplate(projectPath) {
1984
2184
  ----
1985
2185
  *\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
1986
2186
  `;
1987
- await FileUtils.write(path6.join(projectPath, "docs/specs/template.md"), content);
2187
+ await FileUtils.write(path7.join(projectPath, "docs/specs/template.md"), content);
1988
2188
  }
1989
2189
  async function cloneBackendTemplate(projectPath, versionOptions) {
1990
2190
  const templateRepo = process.env.TEMPLATE_REPO || "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git";
1991
- const backendPath = path6.join(projectPath, "backend");
2191
+ const backendPath = path7.join(projectPath, "backend");
1992
2192
  try {
1993
2193
  const { execaCommand } = await import("execa");
1994
- const tempDir = path6.join(projectPath, ".template-temp");
2194
+ const tempDir = path7.join(projectPath, ".template-temp");
1995
2195
  if (versionOptions?.tag || versionOptions?.branch) {
1996
2196
  const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
1997
2197
  const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
@@ -2034,11 +2234,11 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
2034
2234
  stdio: "pipe"
2035
2235
  });
2036
2236
  const latestTag = tags.split("\n")[0] || void 0;
2037
- await fs3.copy(tempDir, backendPath, {
2237
+ await fs4.copy(tempDir, backendPath, {
2038
2238
  filter: (src) => !src.includes(".git")
2039
2239
  });
2040
- await fs3.remove(tempDir);
2041
- const gitDir = path6.join(backendPath, ".git");
2240
+ await fs4.remove(tempDir);
2241
+ const gitDir = path7.join(backendPath, ".git");
2042
2242
  if (await FileUtils.exists(gitDir)) {
2043
2243
  await FileUtils.remove(gitDir);
2044
2244
  }
@@ -2049,13 +2249,13 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
2049
2249
  });
2050
2250
  } catch (error) {
2051
2251
  logger.warn("\u514B\u9686\u540E\u7AEF\u6A21\u677F\u5931\u8D25\uFF0C\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
2052
- await FileUtils.ensureDir(path6.join(backendPath, "src/main/java/com/example"));
2053
- await FileUtils.ensureDir(path6.join(backendPath, "src/main/resources"));
2054
- await FileUtils.ensureDir(path6.join(backendPath, "src/test/java"));
2252
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/java/com/example"));
2253
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/resources"));
2254
+ await FileUtils.ensureDir(path7.join(backendPath, "src/test/java"));
2055
2255
  }
2056
2256
  }
2057
2257
  async function generateFrontendScaffold(projectPath) {
2058
- const frontendPath = path6.join(projectPath, "frontend");
2258
+ const frontendPath = path7.join(projectPath, "frontend");
2059
2259
  try {
2060
2260
  const prompt = `Read TECH_STACK.md and CONVENTIONS.md.
2061
2261
  Initialize a Next.js 14 frontend in ./frontend with:
@@ -2068,19 +2268,19 @@ Initialize a Next.js 14 frontend in ./frontend with:
2068
2268
  Do not run any servers, just generate the folder structure and configuration files.`;
2069
2269
  await claudeAI.prompt(prompt, {
2070
2270
  contextFiles: [
2071
- path6.join(projectPath, "TECH_STACK.md"),
2072
- path6.join(projectPath, "CONVENTIONS.md")
2271
+ path7.join(projectPath, "TECH_STACK.md"),
2272
+ path7.join(projectPath, "CONVENTIONS.md")
2073
2273
  ]
2074
2274
  });
2075
2275
  } catch (error) {
2076
2276
  logger.warn("Claude \u751F\u6210\u524D\u7AEF\u5931\u8D25\uFF0C\u5C06\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
2077
- await FileUtils.ensureDir(path6.join(frontendPath, "src/app"));
2078
- await FileUtils.ensureDir(path6.join(frontendPath, "src/components"));
2079
- await FileUtils.ensureDir(path6.join(frontendPath, "src/lib"));
2080
- await FileUtils.ensureDir(path6.join(frontendPath, "public"));
2277
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/app"));
2278
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/components"));
2279
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/lib"));
2280
+ await FileUtils.ensureDir(path7.join(frontendPath, "public"));
2081
2281
  }
2082
2282
  }
2083
- async function generateDockerFiles(projectPath) {
2283
+ async function generateDockerFiles() {
2084
2284
  logger.info("Docker \u914D\u7F6E\u751F\u6210\u5F85\u5B9E\u73B0");
2085
2285
  }
2086
2286
  async function initGit(projectPath, projectName) {
@@ -2104,7 +2304,7 @@ init_utils();
2104
2304
  init_logger();
2105
2305
  import { Command as Command2 } from "commander";
2106
2306
  import inquirer2 from "inquirer";
2107
- import path7 from "path";
2307
+ import path8 from "path";
2108
2308
  import { Listr as Listr2 } from "listr2";
2109
2309
  var breakdownCommand = new Command2("breakdown").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos").action(async (specFile) => {
2110
2310
  try {
@@ -2150,9 +2350,9 @@ var breakdownCommand = new Command2("breakdown").argument("[spec-file]", "Spec \
2150
2350
  choices: ctx.specs
2151
2351
  }
2152
2352
  ]);
2153
- return path7.join("docs/specs", selectedFile);
2353
+ return path8.join("docs/specs", selectedFile);
2154
2354
  }
2155
- const fullPath = specFile.startsWith("docs/specs/") ? specFile : path7.join("docs/specs", specFile);
2355
+ const fullPath = specFile.startsWith("docs/specs/") ? specFile : path8.join("docs/specs", specFile);
2156
2356
  const exists = await FileUtils.exists(fullPath);
2157
2357
  if (!exists) {
2158
2358
  throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
@@ -2284,7 +2484,7 @@ init_utils();
2284
2484
  init_logger();
2285
2485
  import { Command as Command3 } from "commander";
2286
2486
  import inquirer3 from "inquirer";
2287
- import path8 from "path";
2487
+ import path9 from "path";
2288
2488
  var devCommand = new Command3("dev").description("\u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1").action(async () => {
2289
2489
  try {
2290
2490
  logger.header("\u5F00\u53D1\u6A21\u5F0F");
@@ -2329,7 +2529,7 @@ async function selectSpec() {
2329
2529
  }
2330
2530
  const specs = [];
2331
2531
  for (let i = 0; i < specFiles.length; i++) {
2332
- const file = path8.join(specDir, specFiles[i]);
2532
+ const file = path9.join(specDir, specFiles[i]);
2333
2533
  const spec = await FileUtils.read(file);
2334
2534
  const status = parseSpecStatus(spec);
2335
2535
  const dependencies = parseDependencies(spec);
@@ -2651,8 +2851,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
2651
2851
  const sessionDir = "docs/sessions";
2652
2852
  await FileUtils.ensureDir(sessionDir);
2653
2853
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2654
- const specName = path8.basename(specFile, ".md");
2655
- const logFile = path8.join(sessionDir, `${timestamp}_${specName}.md`);
2854
+ const specName = path9.basename(specFile, ".md");
2855
+ const logFile = path9.join(sessionDir, `${timestamp}_${specName}.md`);
2656
2856
  const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
2657
2857
 
2658
2858
  **\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
@@ -2755,7 +2955,7 @@ init_utils();
2755
2955
  init_logger();
2756
2956
  import { Command as Command4 } from "commander";
2757
2957
  import inquirer4 from "inquirer";
2758
- import path9 from "path";
2958
+ import path10 from "path";
2759
2959
  import { Listr as Listr3 } from "listr2";
2760
2960
  var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "\u529F\u80FD\u540D\u79F0").description("\u6DFB\u52A0\u65B0\u529F\u80FD\uFF08\u652F\u6301 PRD \u6216\u7B80\u5355\u63CF\u8FF0\u6A21\u5F0F\uFF09").action(async (featureName) => {
2761
2961
  try {
@@ -2774,7 +2974,7 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
2774
2974
  process.exit(1);
2775
2975
  }
2776
2976
  const featureSlug = StringUtils2.toKebabCase(featureName);
2777
- const specFile = path9.join("docs/specs", `${featureSlug}.md`);
2977
+ const specFile = path10.join("docs/specs", `${featureSlug}.md`);
2778
2978
  const specExists = await FileUtils.exists(specFile);
2779
2979
  if (specExists) {
2780
2980
  logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
@@ -2833,7 +3033,7 @@ async function addFeatureFromPrd(featureName, featureSlug, specFile) {
2833
3033
  const specs = files.filter((f) => !f.includes("template"));
2834
3034
  ctx2.completedSpecs = [];
2835
3035
  for (const file of specs) {
2836
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3036
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2837
3037
  if (status === "\u5DF2\u5B8C\u6210") {
2838
3038
  ctx2.completedSpecs.push(file.replace(".md", ""));
2839
3039
  }
@@ -2905,7 +3105,7 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
2905
3105
  const specs = files.filter((f) => !f.includes("template"));
2906
3106
  ctx2.completedSpecs = [];
2907
3107
  for (const file of specs) {
2908
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3108
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2909
3109
  if (status === "\u5DF2\u5B8C\u6210") {
2910
3110
  ctx2.completedSpecs.push(file.replace(".md", ""));
2911
3111
  }
@@ -3002,7 +3202,7 @@ async function buildProjectContext() {
3002
3202
  const files = await FileUtils.findFiles("*.md", "docs/specs");
3003
3203
  const specs = files.filter((f) => !f.includes("template"));
3004
3204
  for (const file of specs) {
3005
- const status = await SpecUtils.getSpecStatus(path9.join("docs/specs", file));
3205
+ const status = await SpecUtils.getSpecStatus(path10.join("docs/specs", file));
3006
3206
  context.push(` - ${file.replace(".md", "")} [${status}]`);
3007
3207
  }
3008
3208
  }
@@ -3246,7 +3446,7 @@ init_esm_shims();
3246
3446
  init_utils();
3247
3447
  init_logger();
3248
3448
  import { Command as Command5 } from "commander";
3249
- import path10 from "path";
3449
+ import path11 from "path";
3250
3450
  import { Listr as Listr4 } from "listr2";
3251
3451
  var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u6587\u6863\u76EE\u5F55").description("\u5C06 PRD \u62C6\u5206\u6210\u591A\u4E2A specs").action(async (prdFolder) => {
3252
3452
  try {
@@ -3276,7 +3476,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3276
3476
  ctx2.prdFiles = [];
3277
3477
  for (const ext of supportedExtensions) {
3278
3478
  const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
3279
- ctx2.prdFiles.push(...files.map((f) => path10.join(prdFolder, f)));
3479
+ ctx2.prdFiles.push(...files.map((f) => path11.join(prdFolder, f)));
3280
3480
  }
3281
3481
  if (ctx2.prdFiles.length === 0) {
3282
3482
  throw new Error(
@@ -3294,7 +3494,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3294
3494
  {
3295
3495
  title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
3296
3496
  task: async (ctx2) => {
3297
- const screenshotDir = path10.join(prdFolder, "screenshots");
3497
+ const screenshotDir = path11.join(prdFolder, "screenshots");
3298
3498
  const dirExists = await FileUtils.exists(screenshotDir);
3299
3499
  if (!dirExists) {
3300
3500
  logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
@@ -3305,7 +3505,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3305
3505
  ctx2.screenshots = [];
3306
3506
  for (const ext of imageExtensions) {
3307
3507
  const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
3308
- ctx2.screenshots.push(...files.map((f) => path10.join(screenshotDir, f)));
3508
+ ctx2.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
3309
3509
  }
3310
3510
  logger.success(`\u627E\u5230 ${ctx2.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
3311
3511
  }
@@ -3316,8 +3516,8 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3316
3516
  const entries = await FileUtils.findFiles("*/", prdFolder);
3317
3517
  ctx2.demoRepos = [];
3318
3518
  for (const entry of entries) {
3319
- const dirPath = path10.join(prdFolder, entry);
3320
- const gitDir = path10.join(dirPath, ".git");
3519
+ const dirPath = path11.join(prdFolder, entry);
3520
+ const gitDir = path11.join(dirPath, ".git");
3321
3521
  const hasGit = await FileUtils.exists(gitDir);
3322
3522
  if (hasGit) {
3323
3523
  ctx2.demoRepos.push(dirPath);
@@ -3490,7 +3690,7 @@ init_utils();
3490
3690
  init_logger();
3491
3691
  import { Command as Command6 } from "commander";
3492
3692
  import inquirer5 from "inquirer";
3493
- import path11 from "path";
3693
+ import path12 from "path";
3494
3694
  import { Listr as Listr5 } from "listr2";
3495
3695
  var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8BB0\u5F55").action(async () => {
3496
3696
  try {
@@ -3539,7 +3739,7 @@ var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8B
3539
3739
  const relatedSpec = await findRelatedSpec(answers.description);
3540
3740
  const bugfixDir = "docs/bugfixes";
3541
3741
  await FileUtils.ensureDir(bugfixDir);
3542
- const bugfixFile = path11.join(bugfixDir, `${timestamp}_${bugId}.md`);
3742
+ const bugfixFile = path12.join(bugfixDir, `${timestamp}_${bugId}.md`);
3543
3743
  const content = formatBugfixDocument({
3544
3744
  id: bugId,
3545
3745
  severity: answers.severity,
@@ -3615,7 +3815,7 @@ var hotfixCommand = new Command6("hotfix").description("\u7D27\u6025\u4FEE\u590D
3615
3815
  const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
3616
3816
  const hotfixDir = "docs/hotfixes";
3617
3817
  await FileUtils.ensureDir(hotfixDir);
3618
- const hotfixFile = path11.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3818
+ const hotfixFile = path12.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3619
3819
  const content = formatHotfixDocument({
3620
3820
  id: hotfixId,
3621
3821
  description: answers.description,
@@ -3690,7 +3890,7 @@ async function findRelatedSpec(description) {
3690
3890
  }
3691
3891
  const keywords = extractKeywords(description);
3692
3892
  for (const file of specs) {
3693
- const filePath = path11.join(specDir, file);
3893
+ const filePath = path12.join(specDir, file);
3694
3894
  const content = await FileUtils.read(filePath);
3695
3895
  for (const keyword of keywords) {
3696
3896
  if (content.toLowerCase().includes(keyword.toLowerCase())) {
@@ -3908,7 +4108,7 @@ init_esm_shims();
3908
4108
  init_utils();
3909
4109
  init_logger();
3910
4110
  import { Command as Command8 } from "commander";
3911
- import path12 from "path";
4111
+ import path13 from "path";
3912
4112
  var statusCommand = new Command8("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
3913
4113
  try {
3914
4114
  logger.header("\u9879\u76EE\u72B6\u6001");
@@ -3966,7 +4166,7 @@ async function displayFeatureInventory() {
3966
4166
  }
3967
4167
  const inventory = [];
3968
4168
  for (const file of specs) {
3969
- const filePath = path12.join(specDir, file);
4169
+ const filePath = path13.join(specDir, file);
3970
4170
  const content = await FileUtils.read(filePath);
3971
4171
  const status = parseSpecStatus2(content);
3972
4172
  inventory.push({
@@ -4025,7 +4225,7 @@ async function displayRecentActivity() {
4025
4225
  }
4026
4226
  const sorted = files.sort().reverse().slice(0, 5);
4027
4227
  for (const file of sorted) {
4028
- const filePath = path12.join(sessionDir, file);
4228
+ const filePath = path13.join(sessionDir, file);
4029
4229
  const stat = await FileUtils.read(filePath);
4030
4230
  const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
4031
4231
  const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
@@ -4067,7 +4267,7 @@ init_esm_shims();
4067
4267
  init_utils();
4068
4268
  init_logger();
4069
4269
  import { Command as Command9 } from "commander";
4070
- import path13 from "path";
4270
+ import path14 from "path";
4071
4271
  import inquirer6 from "inquirer";
4072
4272
  var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
4073
4273
  try {
@@ -4095,7 +4295,7 @@ var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spe
4095
4295
  process.exit(1);
4096
4296
  }
4097
4297
  for (const spec of specs) {
4098
- const specPath = path13.join(specsDir, spec);
4298
+ const specPath = path14.join(specsDir, spec);
4099
4299
  logger.step(`\u5904\u7406: ${spec}`);
4100
4300
  await detectDependencies(specPath);
4101
4301
  logger.newLine();
@@ -4121,7 +4321,7 @@ async function detectDependencies(specFile) {
4121
4321
  logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
4122
4322
  const projectDir = ".";
4123
4323
  const allDeps = /* @__PURE__ */ new Set();
4124
- const backendDir = path13.join(projectDir, "backend");
4324
+ const backendDir = path14.join(projectDir, "backend");
4125
4325
  const backendExists = await FileUtils.exists(backendDir);
4126
4326
  if (backendExists) {
4127
4327
  const apiDeps = await scanBackendApiCalls(backendDir);
@@ -4131,7 +4331,7 @@ async function detectDependencies(specFile) {
4131
4331
  const serviceDeps = await scanBackendServiceRefs(backendDir);
4132
4332
  serviceDeps.forEach((d) => allDeps.add(d));
4133
4333
  }
4134
- const frontendDir = path13.join(projectDir, "frontend");
4334
+ const frontendDir = path14.join(projectDir, "frontend");
4135
4335
  const frontendExists = await FileUtils.exists(frontendDir);
4136
4336
  if (frontendExists) {
4137
4337
  const frontendDeps = await scanFrontendApiCalls(frontendDir);
@@ -4172,11 +4372,11 @@ async function detectDependencies(specFile) {
4172
4372
  }
4173
4373
  async function scanBackendApiCalls(backendDir) {
4174
4374
  const deps = [];
4175
- const srcDir = path13.join(backendDir, "src");
4375
+ const srcDir = path14.join(backendDir, "src");
4176
4376
  try {
4177
4377
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4178
4378
  for (const file of javaFiles) {
4179
- const filePath = path13.join(srcDir, file);
4379
+ const filePath = path14.join(srcDir, file);
4180
4380
  const content = await FileUtils.read(filePath);
4181
4381
  const pathRegex = /"(\/api\/[^"]+)"/g;
4182
4382
  let match;
@@ -4194,11 +4394,11 @@ async function scanBackendApiCalls(backendDir) {
4194
4394
  }
4195
4395
  async function scanBackendEntityRelations(backendDir) {
4196
4396
  const deps = [];
4197
- const srcDir = path13.join(backendDir, "src");
4397
+ const srcDir = path14.join(backendDir, "src");
4198
4398
  try {
4199
4399
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4200
4400
  for (const file of javaFiles) {
4201
- const filePath = path13.join(srcDir, file);
4401
+ const filePath = path14.join(srcDir, file);
4202
4402
  const content = await FileUtils.read(filePath);
4203
4403
  if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
4204
4404
  const typeRegex = /type\s*=\s*(\w+)/g;
@@ -4214,11 +4414,11 @@ async function scanBackendEntityRelations(backendDir) {
4214
4414
  }
4215
4415
  async function scanBackendServiceRefs(backendDir) {
4216
4416
  const deps = [];
4217
- const srcDir = path13.join(backendDir, "src");
4417
+ const srcDir = path14.join(backendDir, "src");
4218
4418
  try {
4219
4419
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4220
4420
  for (const file of javaFiles) {
4221
- const filePath = path13.join(srcDir, file);
4421
+ const filePath = path14.join(srcDir, file);
4222
4422
  const content = await FileUtils.read(filePath);
4223
4423
  const serviceRegex = /private\s+(\w+)Service/g;
4224
4424
  let match;
@@ -4234,11 +4434,11 @@ async function scanBackendServiceRefs(backendDir) {
4234
4434
  }
4235
4435
  async function scanFrontendApiCalls(frontendDir) {
4236
4436
  const deps = [];
4237
- const srcDir = path13.join(frontendDir, "src");
4437
+ const srcDir = path14.join(frontendDir, "src");
4238
4438
  try {
4239
4439
  const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
4240
4440
  for (const file of tsFiles) {
4241
- const filePath = path13.join(srcDir, file);
4441
+ const filePath = path14.join(srcDir, file);
4242
4442
  const content = await FileUtils.read(filePath);
4243
4443
  const pathRegex = /"(\/api\/[^"]+)"/g;
4244
4444
  let match;
@@ -4328,7 +4528,7 @@ init_esm_shims();
4328
4528
  init_utils();
4329
4529
  init_logger();
4330
4530
  import { Command as Command10 } from "commander";
4331
- import path14 from "path";
4531
+ import path15 from "path";
4332
4532
  var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
4333
4533
  try {
4334
4534
  logger.header("\u540C\u6B65 AI_MEMORY.md");
@@ -4361,7 +4561,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
4361
4561
  });
4362
4562
  async function syncFeatureInventory(aiMemoryFile, projectDir) {
4363
4563
  logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
4364
- const specsDir = path14.join(projectDir, "docs/specs");
4564
+ const specsDir = path15.join(projectDir, "docs/specs");
4365
4565
  const exists = await FileUtils.exists(specsDir);
4366
4566
  if (!exists) {
4367
4567
  return;
@@ -4376,7 +4576,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4376
4576
  for (const specFile of specs) {
4377
4577
  const name = specFile.replace(".md", "");
4378
4578
  const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
4379
- const specPath = path14.join(specsDir, specFile);
4579
+ const specPath = path15.join(specsDir, specFile);
4380
4580
  const content = await FileUtils.read(specPath);
4381
4581
  const status = parseSpecStatus3(content);
4382
4582
  const progress = getSpecProgress(content);
@@ -4394,7 +4594,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4394
4594
  }
4395
4595
  async function syncApiInventory(aiMemoryFile, projectDir) {
4396
4596
  logger.step("\u540C\u6B65 API \u5217\u8868...");
4397
- const backendDir = path14.join(projectDir, "backend");
4597
+ const backendDir = path15.join(projectDir, "backend");
4398
4598
  const exists = await FileUtils.exists(backendDir);
4399
4599
  if (!exists) {
4400
4600
  return;
@@ -4404,13 +4604,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
4404
4604
  lines.push("");
4405
4605
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
4406
4606
  lines.push("");
4407
- const srcDir = path14.join(backendDir, "src");
4607
+ const srcDir = path15.join(backendDir, "src");
4408
4608
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4409
4609
  if (controllers.length === 0) {
4410
4610
  lines.push("\u6682\u65E0 API");
4411
4611
  } else {
4412
4612
  for (const controllerFile of controllers) {
4413
- const controllerPath = path14.join(srcDir, controllerFile);
4613
+ const controllerPath = path15.join(srcDir, controllerFile);
4414
4614
  const controllerName = controllerFile.replace(".java", "");
4415
4615
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4416
4616
  lines.push(`### ${module} \u6A21\u5757`);
@@ -4487,7 +4687,7 @@ function extractMethodComment(content, methodName) {
4487
4687
  }
4488
4688
  async function syncDataModels(aiMemoryFile, projectDir) {
4489
4689
  logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
4490
- const backendDir = path14.join(projectDir, "backend");
4690
+ const backendDir = path15.join(projectDir, "backend");
4491
4691
  const exists = await FileUtils.exists(backendDir);
4492
4692
  if (!exists) {
4493
4693
  return;
@@ -4497,7 +4697,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4497
4697
  lines.push("");
4498
4698
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
4499
4699
  lines.push("");
4500
- const srcDir = path14.join(backendDir, "src");
4700
+ const srcDir = path15.join(backendDir, "src");
4501
4701
  const entities = await FileUtils.findFiles("*Entity.java", srcDir);
4502
4702
  if (entities.length === 0) {
4503
4703
  lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
@@ -4505,7 +4705,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4505
4705
  lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
4506
4706
  lines.push("|------|------|------|------|");
4507
4707
  for (const entityFile of entities) {
4508
- const entityPath = path14.join(srcDir, entityFile);
4708
+ const entityPath = path15.join(srcDir, entityFile);
4509
4709
  const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
4510
4710
  const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
4511
4711
  const content = await FileUtils.read(entityPath);
@@ -4627,15 +4827,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
4627
4827
  await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
4628
4828
  }
4629
4829
  function extractRepoName(repository) {
4630
- let path18 = repository;
4631
- path18 = path18.replace(/^https?:\/\//, "");
4632
- path18 = path18.replace(/^git@/, "");
4633
- const parts = path18.split("/");
4830
+ let path19 = repository;
4831
+ path19 = path19.replace(/^https?:\/\//, "");
4832
+ path19 = path19.replace(/^git@/, "");
4833
+ const parts = path19.split("/");
4634
4834
  if (parts.length > 1) {
4635
- path18 = parts.slice(1).join("/");
4835
+ path19 = parts.slice(1).join("/");
4636
4836
  }
4637
- path18 = path18.replace(/\.git$/, "");
4638
- return path18;
4837
+ path19 = path19.replace(/\.git$/, "");
4838
+ return path19;
4639
4839
  }
4640
4840
 
4641
4841
  // src/commands/check-api.ts
@@ -4643,7 +4843,7 @@ init_esm_shims();
4643
4843
  init_utils();
4644
4844
  init_logger();
4645
4845
  import { Command as Command11 } from "commander";
4646
- import path15 from "path";
4846
+ import path16 from "path";
4647
4847
  import inquirer7 from "inquirer";
4648
4848
  import { Listr as Listr6 } from "listr2";
4649
4849
  var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
@@ -4707,7 +4907,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
4707
4907
  }
4708
4908
  });
4709
4909
  async function checkApiConflicts(projectDir) {
4710
- const backendDir = path15.join(projectDir, "backend");
4910
+ const backendDir = path16.join(projectDir, "backend");
4711
4911
  const exists = await FileUtils.exists(backendDir);
4712
4912
  if (!exists) {
4713
4913
  logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
@@ -4716,10 +4916,10 @@ async function checkApiConflicts(projectDir) {
4716
4916
  logger.step("\u626B\u63CF\u540E\u7AEF API...");
4717
4917
  logger.newLine();
4718
4918
  const apiMap = /* @__PURE__ */ new Map();
4719
- const srcDir = path15.join(backendDir, "src");
4919
+ const srcDir = path16.join(backendDir, "src");
4720
4920
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4721
4921
  for (const controllerFile of controllers) {
4722
- const controllerPath = path15.join(srcDir, controllerFile);
4922
+ const controllerPath = path16.join(srcDir, controllerFile);
4723
4923
  const apis = await extractApisFromController(controllerPath);
4724
4924
  for (const api of apis) {
4725
4925
  const key = `${api.method}:${api.path}`;
@@ -4750,8 +4950,8 @@ async function checkApiConflicts(projectDir) {
4750
4950
  }
4751
4951
  }
4752
4952
  async function detectApiChanges(projectDir) {
4753
- const backendDir = path15.join(projectDir, "backend");
4754
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
4953
+ const backendDir = path16.join(projectDir, "backend");
4954
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4755
4955
  const registryExists = await FileUtils.exists(registryFile);
4756
4956
  if (!registryExists) {
4757
4957
  logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
@@ -4763,10 +4963,10 @@ async function detectApiChanges(projectDir) {
4763
4963
  const registryContent = await FileUtils.read(registryFile);
4764
4964
  const existingApis = extractApisFromRegistry(registryContent);
4765
4965
  const currentApis = /* @__PURE__ */ new Map();
4766
- const srcDir = path15.join(backendDir, "src");
4966
+ const srcDir = path16.join(backendDir, "src");
4767
4967
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4768
4968
  for (const controllerFile of controllers) {
4769
- const controllerPath = path15.join(srcDir, controllerFile);
4969
+ const controllerPath = path16.join(srcDir, controllerFile);
4770
4970
  const apis = await extractApisFromController(controllerPath);
4771
4971
  for (const api of apis) {
4772
4972
  const key = `${api.method}:${api.path}`;
@@ -4828,9 +5028,9 @@ async function detectApiChanges(projectDir) {
4828
5028
  }
4829
5029
  }
4830
5030
  async function generateApiRegistry(projectDir) {
4831
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
5031
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4832
5032
  logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
4833
- await FileUtils.ensureDir(path15.dirname(registryFile));
5033
+ await FileUtils.ensureDir(path16.dirname(registryFile));
4834
5034
  const header = `# API Registry
4835
5035
 
4836
5036
  > \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
@@ -4859,14 +5059,14 @@ async function generateApiRegistry(projectDir) {
4859
5059
  *\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
4860
5060
  `;
4861
5061
  let content = header;
4862
- const backendDir = path15.join(projectDir, "backend");
5062
+ const backendDir = path16.join(projectDir, "backend");
4863
5063
  const exists = await FileUtils.exists(backendDir);
4864
5064
  if (exists) {
4865
- const srcDir = path15.join(backendDir, "src");
5065
+ const srcDir = path16.join(backendDir, "src");
4866
5066
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4867
5067
  const moduleMap = /* @__PURE__ */ new Map();
4868
5068
  for (const controllerFile of controllers) {
4869
- const controllerPath = path15.join(srcDir, controllerFile);
5069
+ const controllerPath = path16.join(srcDir, controllerFile);
4870
5070
  const controllerName = controllerFile.replace(".java", "");
4871
5071
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4872
5072
  if (!moduleMap.has(module)) {
@@ -4950,10 +5150,10 @@ function extractApisFromRegistry(registryContent) {
4950
5150
  let match;
4951
5151
  while ((match = apiRegex.exec(registryContent)) !== null) {
4952
5152
  const method = match[1];
4953
- const path18 = match[2].trim();
5153
+ const path19 = match[2].trim();
4954
5154
  const description = match[3].trim();
4955
- const key = `${method}:${path18}`;
4956
- apis.set(key, { method, path: path18, description });
5155
+ const key = `${method}:${path19}`;
5156
+ apis.set(key, { method, path: path19, description });
4957
5157
  }
4958
5158
  return apis;
4959
5159
  }
@@ -4975,7 +5175,7 @@ init_esm_shims();
4975
5175
  init_utils();
4976
5176
  init_logger();
4977
5177
  import { Command as Command12 } from "commander";
4978
- import path16 from "path";
5178
+ import path17 from "path";
4979
5179
  import inquirer8 from "inquirer";
4980
5180
  var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668 (today, --all, \u6216\u65E5\u671F YYYY-MM-DD)").description("\u67E5\u770B\u4F1A\u8BDD\u65E5\u5FD7").action(async (filter = "today") => {
4981
5181
  try {
@@ -5000,7 +5200,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5000
5200
  case "":
5001
5201
  case "today": {
5002
5202
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5003
- targetDir = path16.join(sessionsDir, today);
5203
+ targetDir = path17.join(sessionsDir, today);
5004
5204
  const todayExists = await FileUtils.exists(targetDir);
5005
5205
  if (!todayExists) {
5006
5206
  logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
@@ -5016,7 +5216,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5016
5216
  break;
5017
5217
  }
5018
5218
  default: {
5019
- targetDir = path16.join(sessionsDir, filter);
5219
+ targetDir = path17.join(sessionsDir, filter);
5020
5220
  const dateExists = await FileUtils.exists(targetDir);
5021
5221
  if (!dateExists) {
5022
5222
  logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
@@ -5040,7 +5240,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5040
5240
  process.exit(0);
5041
5241
  }
5042
5242
  for (let i = 0; i < logs.length; i++) {
5043
- const relPath = path16.relative(sessionsDir, logs[i]);
5243
+ const relPath = path17.relative(sessionsDir, logs[i]);
5044
5244
  logger.step(`${i + 1}) ${relPath}`);
5045
5245
  }
5046
5246
  logger.newLine();
@@ -5081,7 +5281,7 @@ async function collectLogFiles(targetDir) {
5081
5281
  const allFiles = await FileUtils.findFiles("*.md", targetDir);
5082
5282
  const filtered = allFiles.filter((f) => f !== "index.md");
5083
5283
  for (const file of filtered) {
5084
- const filePath = path16.join(targetDir, file);
5284
+ const filePath = path17.join(targetDir, file);
5085
5285
  const stat = await FileUtils.exists(filePath);
5086
5286
  if (stat) {
5087
5287
  logs.push(filePath);
@@ -5095,12 +5295,12 @@ async function collectLogFiles(targetDir) {
5095
5295
  // src/commands/update.ts
5096
5296
  init_esm_shims();
5097
5297
  import { Command as Command13 } from "commander";
5098
- import path17 from "path";
5298
+ import path18 from "path";
5099
5299
  init_logger();
5100
5300
  init_utils();
5101
5301
  import { execa as execa4 } from "execa";
5102
5302
  import inquirer9 from "inquirer";
5103
- import fs4 from "fs-extra";
5303
+ import fs5 from "fs-extra";
5104
5304
  var updateCommand = new Command13("update").description("\u68C0\u67E5\u5E76\u66F4\u65B0\u6A21\u677F\u7248\u672C").option("-f, --frontend", "\u68C0\u67E5\u524D\u7AEF\u6A21\u677F\u66F4\u65B0").option("-b, --backend", "\u68C0\u67E5\u540E\u7AEF\u6A21\u677F\u66F4\u65B0").option("-a, --all", "\u68C0\u67E5\u6240\u6709\u6A21\u677F (\u9ED8\u8BA4)").option("-t, --tag <tag>", "\u66F4\u65B0\u5230\u6307\u5B9A\u6807\u7B7E").option("-B, --branch <branch>", "\u66F4\u65B0\u5230\u6307\u5B9A\u5206\u652F").option("--dry-run", "\u9884\u89C8\u66F4\u65B0\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").action(async (options) => {
5105
5305
  try {
5106
5306
  logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
@@ -5200,7 +5400,7 @@ async function performUpdate(projectPath, updates) {
5200
5400
  for (const update of updates) {
5201
5401
  const { type, info, updateOptions } = update;
5202
5402
  const targetDir = type === "frontend" ? "frontend" : "backend";
5203
- const targetPath = path17.join(projectPath, targetDir);
5403
+ const targetPath = path18.join(projectPath, targetDir);
5204
5404
  logger.newLine();
5205
5405
  logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
5206
5406
  if (updateOptions?.tag || updateOptions?.branch) {
@@ -5231,8 +5431,8 @@ async function performUpdate(projectPath, updates) {
5231
5431
  }
5232
5432
  }
5233
5433
  const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
5234
- const backupDir = path17.join(projectPath, `.backup-${Date.now()}`);
5235
- await fs4.copy(targetPath, path17.join(backupDir, targetDir));
5434
+ const backupDir = path18.join(projectPath, `.backup-${Date.now()}`);
5435
+ await fs5.copy(targetPath, path18.join(backupDir, targetDir));
5236
5436
  logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
5237
5437
  if (updateOptions?.dryRun) {
5238
5438
  logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
@@ -5242,7 +5442,7 @@ async function performUpdate(projectPath, updates) {
5242
5442
  continue;
5243
5443
  }
5244
5444
  try {
5245
- const tempDir = path17.join(projectPath, `.template-update-${Date.now()}`);
5445
+ const tempDir = path18.join(projectPath, `.template-update-${Date.now()}`);
5246
5446
  await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
5247
5447
  stdio: "pipe"
5248
5448
  });
@@ -5259,17 +5459,17 @@ async function performUpdate(projectPath, updates) {
5259
5459
  const currentFiles = await FileUtils.findFiles("*", targetPath);
5260
5460
  for (const file of currentFiles) {
5261
5461
  if (!keepFiles.includes(file)) {
5262
- const filePath = path17.join(targetPath, file);
5462
+ const filePath = path18.join(targetPath, file);
5263
5463
  try {
5264
- await fs4.remove(filePath);
5464
+ await fs5.remove(filePath);
5265
5465
  } catch {
5266
5466
  }
5267
5467
  }
5268
5468
  }
5269
- await fs4.copy(tempDir, targetPath, {
5469
+ await fs5.copy(tempDir, targetPath, {
5270
5470
  filter: (src) => !src.includes(".git")
5271
5471
  });
5272
- await fs4.remove(tempDir);
5472
+ await fs5.remove(tempDir);
5273
5473
  await updateTemplateVersion(projectPath, type, commit.trim(), {
5274
5474
  tag: updateOptions?.tag || latestTag,
5275
5475
  branch: updateOptions?.branch
@@ -5280,9 +5480,9 @@ async function performUpdate(projectPath, updates) {
5280
5480
  } catch (error) {
5281
5481
  logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
5282
5482
  logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
5283
- await fs4.remove(targetPath);
5284
- await fs4.copy(path17.join(backupDir, targetDir), targetPath);
5285
- await fs4.remove(backupDir);
5483
+ await fs5.remove(targetPath);
5484
+ await fs5.copy(path18.join(backupDir, targetDir), targetPath);
5485
+ await fs5.remove(backupDir);
5286
5486
  logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
5287
5487
  }
5288
5488
  }