yg-team-cli 2.3.0 → 2.3.2

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 path20 = repository;
806
+ if (path20.startsWith("git@")) {
807
+ path20 = path20.replace(/^git@/, "");
808
+ const colonIndex = path20.indexOf(":");
797
809
  if (colonIndex !== -1) {
798
- path18 = path18.substring(colonIndex + 1);
810
+ path20 = path20.substring(colonIndex + 1);
799
811
  }
800
812
  } else {
801
- path18 = path18.replace(/^https?:\/\//, "");
802
- const parts = path18.split("/");
813
+ path20 = path20.replace(/^https?:\/\//, "");
814
+ const parts = path20.split("/");
803
815
  if (parts.length > 1) {
804
- path18 = parts.slice(1).join("/");
816
+ path20 = parts.slice(1).join("/");
805
817
  }
806
818
  }
807
- path18 = path18.replace(/\.git$/, "");
808
- return path18;
819
+ path20 = path20.replace(/\.git$/, "");
820
+ return path20;
809
821
  }
810
822
  /**
811
823
  * 编码项目路径用于 API 请求
@@ -861,14 +873,17 @@ init_esm_shims();
861
873
  init_logger();
862
874
  import { Command as Command16 } from "commander";
863
875
  import chalk4 from "chalk";
876
+ import fs6 from "fs-extra";
877
+ import path19 from "path";
878
+ import { fileURLToPath as fileURLToPath2 } from "url";
864
879
 
865
880
  // src/commands/init.ts
866
881
  init_esm_shims();
867
882
  init_logger();
868
883
  import { Command } from "commander";
869
884
  import inquirer from "inquirer";
870
- import path6 from "path";
871
- import fs3 from "fs-extra";
885
+ import path7 from "path";
886
+ import fs4 from "fs-extra";
872
887
 
873
888
  // src/lib/claude.ts
874
889
  init_esm_shims();
@@ -1323,6 +1338,128 @@ async function updateTemplateVersion(projectPath, type, commit, options) {
1323
1338
  await saveTemplateConfig(projectPath, config);
1324
1339
  }
1325
1340
 
1341
+ // src/lib/module-registry.ts
1342
+ init_esm_shims();
1343
+ init_utils();
1344
+ import path5 from "path";
1345
+ import fs3 from "fs-extra";
1346
+ var ModuleManager = class {
1347
+ static MODULES = [
1348
+ {
1349
+ id: "acl",
1350
+ name: "ACL (Access Control List)",
1351
+ description: "\u57FA\u4E8E\u89D2\u8272\u7684\u8BBF\u95EE\u63A7\u5236\u6A21\u5757\uFF08\u672C\u5730\u6A21\u677F\u7248\uFF09",
1352
+ type: "local",
1353
+ specs: ["acl/spec.md"],
1354
+ backendFragments: [
1355
+ {
1356
+ source: "acl/backend/AclResultCode.java",
1357
+ target: "common/enums/AclResultCode.java"
1358
+ },
1359
+ {
1360
+ source: "acl/backend/RequiredPermission.java",
1361
+ target: "common/annotation/RequiredPermission.java"
1362
+ }
1363
+ ]
1364
+ },
1365
+ {
1366
+ id: "permission-remote",
1367
+ name: "\u6743\u9650\u7BA1\u7406 (\u8FDC\u7A0B MCP \u7248)",
1368
+ description: "\u4ECE api-metadata \u5B9E\u65F6\u83B7\u53D6\u6700\u65B0\u6743\u9650\u63A5\u53E3\u5B9A\u4E49\u5E76\u751F\u6210\u4EE3\u7801",
1369
+ type: "remote",
1370
+ mcpServer: "api-metadata",
1371
+ specs: ["permission-remote/spec.md"]
1372
+ },
1373
+ {
1374
+ id: "audit-log",
1375
+ name: "\u5BA1\u8BA1\u65E5\u5FD7 (Audit Log)",
1376
+ description: "\u8BB0\u5F55\u7528\u6237\u64CD\u4F5C\u65E5\u5FD7\uFF0C\u652F\u6301 AOP \u81EA\u52A8\u62E6\u622A\u548C\u5F02\u6B65\u5B58\u50A8",
1377
+ type: "local",
1378
+ specs: ["audit-log/spec.md"]
1379
+ }
1380
+ ];
1381
+ /**
1382
+ * 获取所有可用模块
1383
+ */
1384
+ static getAvailableModules() {
1385
+ return [...this.MODULES];
1386
+ }
1387
+ /**
1388
+ * 根据 ID 获取模块
1389
+ */
1390
+ static getModuleById(id) {
1391
+ return this.MODULES.find((m) => m.id === id);
1392
+ }
1393
+ /**
1394
+ * 注入模块到项目
1395
+ */
1396
+ static async injectModule(projectPath, moduleId, templatesDir) {
1397
+ const module = this.getModuleById(moduleId);
1398
+ if (!module) return;
1399
+ for (const specRelPath of module.specs) {
1400
+ const sourcePath = path5.join(templatesDir, "modules", specRelPath);
1401
+ const targetPath = path5.join(projectPath, "docs/specs", path5.basename(specRelPath));
1402
+ if (await FileUtils.exists(sourcePath)) {
1403
+ await FileUtils.copy(sourcePath, targetPath);
1404
+ } else if (module.type === "remote") {
1405
+ await this.generateRemoteSpecPlaceholder(targetPath, module);
1406
+ }
1407
+ }
1408
+ if (module.type === "local" && module.backendFragments) {
1409
+ const backendBase = path5.join(projectPath, "backend/src/main/java");
1410
+ const basePackagePath = await this.findBasePackage(backendBase);
1411
+ if (basePackagePath) {
1412
+ for (const fragment of module.backendFragments) {
1413
+ const sourcePath = path5.join(templatesDir, "modules", fragment.source);
1414
+ const targetPath = path5.join(basePackagePath, fragment.target);
1415
+ if (await FileUtils.exists(sourcePath)) {
1416
+ await FileUtils.ensureDir(path5.dirname(targetPath));
1417
+ await FileUtils.copy(sourcePath, targetPath);
1418
+ }
1419
+ }
1420
+ }
1421
+ } else if (module.type === "remote") {
1422
+ }
1423
+ }
1424
+ /**
1425
+ * 生成远程模块的 Spec 占位符
1426
+ */
1427
+ static async generateRemoteSpecPlaceholder(targetPath, module) {
1428
+ const content = `# ${module.name} (Remote)
1429
+
1430
+ ## \u529F\u80FD\u6982\u8FF0
1431
+ \u8BE5\u6A21\u5757\u901A\u8FC7\u8FDC\u7A0B MCP \u670D\u52A1 \`${module.mcpServer}\` \u83B7\u53D6\u5B9E\u65F6\u5143\u6570\u636E\u3002
1432
+
1433
+ ## \u4F7F\u7528\u8BF4\u660E
1434
+ 1. \u786E\u4FDD\u5DF2\u5B89\u88C5\u5E76\u914D\u7F6E MCP \u670D\u52A1\u3002
1435
+ 2. \u8FD0\u884C \`team-cli dev\` \u5E76\u9009\u62E9\u6B64 Spec\u3002
1436
+ 3. Claude \u5C06\u81EA\u52A8\u8C03\u7528 MCP \u83B7\u53D6\u6700\u65B0\u7684 API \u4FE1\u606F\u5E76\u5B8C\u6210\u5B9E\u73B0\u3002
1437
+ `;
1438
+ await FileUtils.write(targetPath, content);
1439
+ }
1440
+ /**
1441
+ * 自动探测项目的 Base Package
1442
+ */
1443
+ static async findBasePackage(searchDir) {
1444
+ if (!await FileUtils.exists(searchDir)) return null;
1445
+ const files = await FileUtils.findFiles("**/Application.java", searchDir);
1446
+ if (files.length > 0) {
1447
+ return path5.dirname(path5.join(searchDir, files[0]));
1448
+ }
1449
+ let current = searchDir;
1450
+ while (true) {
1451
+ const items = await fs3.readdir(current);
1452
+ const dirs = items.filter((c) => fs3.statSync(path5.join(current, c)).isDirectory());
1453
+ if (dirs.length === 1 && !["model", "controller", "service", "mapper", "config", "common"].includes(dirs[0])) {
1454
+ current = path5.join(current, dirs[0]);
1455
+ } else {
1456
+ break;
1457
+ }
1458
+ }
1459
+ return current;
1460
+ }
1461
+ };
1462
+
1326
1463
  // src/commands/init.ts
1327
1464
  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
1465
  try {
@@ -1355,7 +1492,19 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1355
1492
  logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
1356
1493
  process.exit(1);
1357
1494
  }
1358
- const projectPath = path6.resolve(options.dir, projectName);
1495
+ const projectPath = path7.resolve(options.dir, projectName);
1496
+ const availableModules = ModuleManager.getAvailableModules();
1497
+ const { selectedModules } = await inquirer.prompt([
1498
+ {
1499
+ type: "checkbox",
1500
+ name: "selectedModules",
1501
+ message: "\u8BF7\u9009\u62E9\u8981\u5F00\u542F\u7684\u5185\u7F6E\u901A\u7528\u6A21\u5757:",
1502
+ choices: availableModules.map((m) => ({
1503
+ name: `${m.name} - ${m.description}`,
1504
+ value: m.id
1505
+ }))
1506
+ }
1507
+ ]);
1359
1508
  if (await FileUtils.exists(projectPath)) {
1360
1509
  logger.error(`\u76EE\u5F55\u5DF2\u5B58\u5728: ${projectPath}`);
1361
1510
  logger.info("\u8BF7\u9009\u62E9\u5176\u4ED6\u9879\u76EE\u540D\u79F0\u6216\u5220\u9664\u73B0\u6709\u76EE\u5F55");
@@ -1374,11 +1523,11 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1374
1523
  title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55",
1375
1524
  task: async () => {
1376
1525
  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"));
1526
+ await FileUtils.ensureDir(path7.join(projectPath, "frontend"));
1527
+ await FileUtils.ensureDir(path7.join(projectPath, "backend"));
1528
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/specs"));
1529
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/api"));
1530
+ await FileUtils.ensureDir(path7.join(projectPath, "docs/sessions"));
1382
1531
  }
1383
1532
  },
1384
1533
  {
@@ -1426,7 +1575,7 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1426
1575
  {
1427
1576
  title: "\u751F\u6210 Docker \u914D\u7F6E",
1428
1577
  task: async () => {
1429
- await generateDockerFiles(projectPath);
1578
+ await generateDockerFiles();
1430
1579
  }
1431
1580
  }
1432
1581
  ] : [],
@@ -1437,7 +1586,61 @@ var initCommand = new Command("init").argument("[project-name]", "\u9879\u76EE\u
1437
1586
  await initGit(projectPath, projectName);
1438
1587
  }
1439
1588
  }
1440
- ] : []
1589
+ ] : [],
1590
+ {
1591
+ title: "\u6CE8\u518C\u8FDC\u7A0B MCP \u670D\u52A1",
1592
+ task: async () => {
1593
+ const hasRemoteModule = selectedModules.some(
1594
+ (id) => ModuleManager.getModuleById(id)?.type === "remote"
1595
+ );
1596
+ if (hasRemoteModule) {
1597
+ const mcpCmd = 'claude_m2 mcp add --transport sse --header "Authorization: Bearer mcp_00557dabb71297b4f9ac5fe748395f2c" -- api-metadata https://api-metadata.yungu.org/sse';
1598
+ const { execaCommand } = await import("execa");
1599
+ try {
1600
+ await execaCommand(mcpCmd);
1601
+ logger.info("\u8FDC\u7A0B MCP \u670D\u52A1 api-metadata \u5DF2\u6CE8\u518C");
1602
+ } catch (err) {
1603
+ logger.warn("\u8FDC\u7A0B MCP \u670D\u52A1\u6CE8\u518C\u5931\u8D25\uFF0C\u53EF\u80FD\u5DF2\u5B58\u5728\u6216\u6743\u9650\u4E0D\u8DB3");
1604
+ }
1605
+ }
1606
+ }
1607
+ },
1608
+ {
1609
+ title: "\u6CE8\u5165\u901A\u7528\u6A21\u5757",
1610
+ task: async () => {
1611
+ if (selectedModules.length === 0) return;
1612
+ const templatesDir = path7.resolve(FileUtils.getDirName(import.meta.url), "../templates");
1613
+ for (const moduleId of selectedModules) {
1614
+ await ModuleManager.injectModule(projectPath, moduleId, templatesDir);
1615
+ logger.info(`\u6CE8\u5165\u6A21\u5757: ${moduleId}`);
1616
+ }
1617
+ }
1618
+ },
1619
+ {
1620
+ title: "\u6267\u884C\u8FDC\u7A0B\u6A21\u5757\u4EE3\u7801\u751F\u6210",
1621
+ task: async () => {
1622
+ const remoteModules = selectedModules.map((id) => ModuleManager.getModuleById(id)).filter((m) => m?.type === "remote");
1623
+ if (remoteModules.length === 0) return;
1624
+ const { execaCommand } = await import("execa");
1625
+ for (const module of remoteModules) {
1626
+ if (!module) continue;
1627
+ logger.info(`\u6B63\u5728\u4E3A\u60A8\u751F\u6210 ${module.name} \u7684\u8FDC\u7A0B\u8C03\u7528\u4EE3\u7801...`);
1628
+ 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
1629
+ \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
1630
+ \u8BF7\u786E\u4FDD\u4EE3\u7801\u7B26\u5408 \`CONVENTIONS.md\` \u4E2D\u7684\u89C4\u8303\u3002
1631
+ \u751F\u6210\u5B8C\u6210\u540E\uFF0C\u8BF7\u7B80\u8981\u5217\u51FA\u751F\u6210\u7684\u6587\u4EF6\u3002`;
1632
+ try {
1633
+ await execaCommand(`claude_m2 -p "${prompt}" --add-dir ${projectPath}`, {
1634
+ stdio: "inherit",
1635
+ timeout: 3e5
1636
+ // 5分钟超时
1637
+ });
1638
+ } catch (err) {
1639
+ 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`);
1640
+ }
1641
+ }
1642
+ }
1643
+ }
1441
1644
  ],
1442
1645
  {
1443
1646
  concurrent: false,
@@ -1558,7 +1761,7 @@ docs/
1558
1761
  \u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
1559
1762
  \`\`\`
1560
1763
  `;
1561
- await FileUtils.write(path6.join(projectPath, "TECH_STACK.md"), content);
1764
+ await FileUtils.write(path7.join(projectPath, "TECH_STACK.md"), content);
1562
1765
  }
1563
1766
  async function generateConventions(projectPath) {
1564
1767
  const content = `# \u5F00\u53D1\u89C4\u8303
@@ -1861,7 +2064,7 @@ test('renders user name', () => {
1861
2064
  });
1862
2065
  \`\`\`
1863
2066
  `;
1864
- await FileUtils.write(path6.join(projectPath, "CONVENTIONS.md"), content);
2067
+ await FileUtils.write(path7.join(projectPath, "CONVENTIONS.md"), content);
1865
2068
  }
1866
2069
  async function generateAIMemory(projectPath, projectName) {
1867
2070
  const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
@@ -1920,7 +2123,7 @@ async function generateAIMemory(projectPath, projectName) {
1920
2123
  | Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
1921
2124
  |--------|------|---------|------|
1922
2125
  `;
1923
- await FileUtils.write(path6.join(projectPath, "AI_MEMORY.md"), content);
2126
+ await FileUtils.write(path7.join(projectPath, "AI_MEMORY.md"), content);
1924
2127
  }
1925
2128
  async function generateSpecTemplate(projectPath) {
1926
2129
  const content = `# [\u529F\u80FD\u6807\u9898]
@@ -1984,14 +2187,14 @@ async function generateSpecTemplate(projectPath) {
1984
2187
  ----
1985
2188
  *\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
1986
2189
  `;
1987
- await FileUtils.write(path6.join(projectPath, "docs/specs/template.md"), content);
2190
+ await FileUtils.write(path7.join(projectPath, "docs/specs/template.md"), content);
1988
2191
  }
1989
2192
  async function cloneBackendTemplate(projectPath, versionOptions) {
1990
2193
  const templateRepo = process.env.TEMPLATE_REPO || "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git";
1991
- const backendPath = path6.join(projectPath, "backend");
2194
+ const backendPath = path7.join(projectPath, "backend");
1992
2195
  try {
1993
2196
  const { execaCommand } = await import("execa");
1994
- const tempDir = path6.join(projectPath, ".template-temp");
2197
+ const tempDir = path7.join(projectPath, ".template-temp");
1995
2198
  if (versionOptions?.tag || versionOptions?.branch) {
1996
2199
  const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
1997
2200
  const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
@@ -2034,11 +2237,11 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
2034
2237
  stdio: "pipe"
2035
2238
  });
2036
2239
  const latestTag = tags.split("\n")[0] || void 0;
2037
- await fs3.copy(tempDir, backendPath, {
2240
+ await fs4.copy(tempDir, backendPath, {
2038
2241
  filter: (src) => !src.includes(".git")
2039
2242
  });
2040
- await fs3.remove(tempDir);
2041
- const gitDir = path6.join(backendPath, ".git");
2243
+ await fs4.remove(tempDir);
2244
+ const gitDir = path7.join(backendPath, ".git");
2042
2245
  if (await FileUtils.exists(gitDir)) {
2043
2246
  await FileUtils.remove(gitDir);
2044
2247
  }
@@ -2049,13 +2252,13 @@ async function cloneBackendTemplate(projectPath, versionOptions) {
2049
2252
  });
2050
2253
  } catch (error) {
2051
2254
  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"));
2255
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/java/com/example"));
2256
+ await FileUtils.ensureDir(path7.join(backendPath, "src/main/resources"));
2257
+ await FileUtils.ensureDir(path7.join(backendPath, "src/test/java"));
2055
2258
  }
2056
2259
  }
2057
2260
  async function generateFrontendScaffold(projectPath) {
2058
- const frontendPath = path6.join(projectPath, "frontend");
2261
+ const frontendPath = path7.join(projectPath, "frontend");
2059
2262
  try {
2060
2263
  const prompt = `Read TECH_STACK.md and CONVENTIONS.md.
2061
2264
  Initialize a Next.js 14 frontend in ./frontend with:
@@ -2068,19 +2271,19 @@ Initialize a Next.js 14 frontend in ./frontend with:
2068
2271
  Do not run any servers, just generate the folder structure and configuration files.`;
2069
2272
  await claudeAI.prompt(prompt, {
2070
2273
  contextFiles: [
2071
- path6.join(projectPath, "TECH_STACK.md"),
2072
- path6.join(projectPath, "CONVENTIONS.md")
2274
+ path7.join(projectPath, "TECH_STACK.md"),
2275
+ path7.join(projectPath, "CONVENTIONS.md")
2073
2276
  ]
2074
2277
  });
2075
2278
  } catch (error) {
2076
2279
  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"));
2280
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/app"));
2281
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/components"));
2282
+ await FileUtils.ensureDir(path7.join(frontendPath, "src/lib"));
2283
+ await FileUtils.ensureDir(path7.join(frontendPath, "public"));
2081
2284
  }
2082
2285
  }
2083
- async function generateDockerFiles(projectPath) {
2286
+ async function generateDockerFiles() {
2084
2287
  logger.info("Docker \u914D\u7F6E\u751F\u6210\u5F85\u5B9E\u73B0");
2085
2288
  }
2086
2289
  async function initGit(projectPath, projectName) {
@@ -2104,7 +2307,7 @@ init_utils();
2104
2307
  init_logger();
2105
2308
  import { Command as Command2 } from "commander";
2106
2309
  import inquirer2 from "inquirer";
2107
- import path7 from "path";
2310
+ import path8 from "path";
2108
2311
  import { Listr as Listr2 } from "listr2";
2109
2312
  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
2313
  try {
@@ -2150,9 +2353,9 @@ var breakdownCommand = new Command2("breakdown").argument("[spec-file]", "Spec \
2150
2353
  choices: ctx.specs
2151
2354
  }
2152
2355
  ]);
2153
- return path7.join("docs/specs", selectedFile);
2356
+ return path8.join("docs/specs", selectedFile);
2154
2357
  }
2155
- const fullPath = specFile.startsWith("docs/specs/") ? specFile : path7.join("docs/specs", specFile);
2358
+ const fullPath = specFile.startsWith("docs/specs/") ? specFile : path8.join("docs/specs", specFile);
2156
2359
  const exists = await FileUtils.exists(fullPath);
2157
2360
  if (!exists) {
2158
2361
  throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
@@ -2284,7 +2487,7 @@ init_utils();
2284
2487
  init_logger();
2285
2488
  import { Command as Command3 } from "commander";
2286
2489
  import inquirer3 from "inquirer";
2287
- import path8 from "path";
2490
+ import path9 from "path";
2288
2491
  var devCommand = new Command3("dev").description("\u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1").action(async () => {
2289
2492
  try {
2290
2493
  logger.header("\u5F00\u53D1\u6A21\u5F0F");
@@ -2329,7 +2532,7 @@ async function selectSpec() {
2329
2532
  }
2330
2533
  const specs = [];
2331
2534
  for (let i = 0; i < specFiles.length; i++) {
2332
- const file = path8.join(specDir, specFiles[i]);
2535
+ const file = path9.join(specDir, specFiles[i]);
2333
2536
  const spec = await FileUtils.read(file);
2334
2537
  const status = parseSpecStatus(spec);
2335
2538
  const dependencies = parseDependencies(spec);
@@ -2651,8 +2854,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
2651
2854
  const sessionDir = "docs/sessions";
2652
2855
  await FileUtils.ensureDir(sessionDir);
2653
2856
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2654
- const specName = path8.basename(specFile, ".md");
2655
- const logFile = path8.join(sessionDir, `${timestamp}_${specName}.md`);
2857
+ const specName = path9.basename(specFile, ".md");
2858
+ const logFile = path9.join(sessionDir, `${timestamp}_${specName}.md`);
2656
2859
  const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
2657
2860
 
2658
2861
  **\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
@@ -2755,7 +2958,7 @@ init_utils();
2755
2958
  init_logger();
2756
2959
  import { Command as Command4 } from "commander";
2757
2960
  import inquirer4 from "inquirer";
2758
- import path9 from "path";
2961
+ import path10 from "path";
2759
2962
  import { Listr as Listr3 } from "listr2";
2760
2963
  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
2964
  try {
@@ -2774,7 +2977,7 @@ var addFeatureCommand = new Command4("add-feature").argument("<feature-name>", "
2774
2977
  process.exit(1);
2775
2978
  }
2776
2979
  const featureSlug = StringUtils2.toKebabCase(featureName);
2777
- const specFile = path9.join("docs/specs", `${featureSlug}.md`);
2980
+ const specFile = path10.join("docs/specs", `${featureSlug}.md`);
2778
2981
  const specExists = await FileUtils.exists(specFile);
2779
2982
  if (specExists) {
2780
2983
  logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
@@ -2833,7 +3036,7 @@ async function addFeatureFromPrd(featureName, featureSlug, specFile) {
2833
3036
  const specs = files.filter((f) => !f.includes("template"));
2834
3037
  ctx2.completedSpecs = [];
2835
3038
  for (const file of specs) {
2836
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3039
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2837
3040
  if (status === "\u5DF2\u5B8C\u6210") {
2838
3041
  ctx2.completedSpecs.push(file.replace(".md", ""));
2839
3042
  }
@@ -2905,7 +3108,7 @@ async function addFeatureSimple(featureName, featureSlug, specFile) {
2905
3108
  const specs = files.filter((f) => !f.includes("template"));
2906
3109
  ctx2.completedSpecs = [];
2907
3110
  for (const file of specs) {
2908
- const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3111
+ const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
2909
3112
  if (status === "\u5DF2\u5B8C\u6210") {
2910
3113
  ctx2.completedSpecs.push(file.replace(".md", ""));
2911
3114
  }
@@ -3002,7 +3205,7 @@ async function buildProjectContext() {
3002
3205
  const files = await FileUtils.findFiles("*.md", "docs/specs");
3003
3206
  const specs = files.filter((f) => !f.includes("template"));
3004
3207
  for (const file of specs) {
3005
- const status = await SpecUtils.getSpecStatus(path9.join("docs/specs", file));
3208
+ const status = await SpecUtils.getSpecStatus(path10.join("docs/specs", file));
3006
3209
  context.push(` - ${file.replace(".md", "")} [${status}]`);
3007
3210
  }
3008
3211
  }
@@ -3246,7 +3449,7 @@ init_esm_shims();
3246
3449
  init_utils();
3247
3450
  init_logger();
3248
3451
  import { Command as Command5 } from "commander";
3249
- import path10 from "path";
3452
+ import path11 from "path";
3250
3453
  import { Listr as Listr4 } from "listr2";
3251
3454
  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
3455
  try {
@@ -3276,7 +3479,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3276
3479
  ctx2.prdFiles = [];
3277
3480
  for (const ext of supportedExtensions) {
3278
3481
  const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
3279
- ctx2.prdFiles.push(...files.map((f) => path10.join(prdFolder, f)));
3482
+ ctx2.prdFiles.push(...files.map((f) => path11.join(prdFolder, f)));
3280
3483
  }
3281
3484
  if (ctx2.prdFiles.length === 0) {
3282
3485
  throw new Error(
@@ -3294,7 +3497,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3294
3497
  {
3295
3498
  title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
3296
3499
  task: async (ctx2) => {
3297
- const screenshotDir = path10.join(prdFolder, "screenshots");
3500
+ const screenshotDir = path11.join(prdFolder, "screenshots");
3298
3501
  const dirExists = await FileUtils.exists(screenshotDir);
3299
3502
  if (!dirExists) {
3300
3503
  logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
@@ -3305,7 +3508,7 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3305
3508
  ctx2.screenshots = [];
3306
3509
  for (const ext of imageExtensions) {
3307
3510
  const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
3308
- ctx2.screenshots.push(...files.map((f) => path10.join(screenshotDir, f)));
3511
+ ctx2.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
3309
3512
  }
3310
3513
  logger.success(`\u627E\u5230 ${ctx2.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
3311
3514
  }
@@ -3316,8 +3519,8 @@ var splitPrdCommand = new Command5("split-prd").argument("<prd-folder>", "PRD \u
3316
3519
  const entries = await FileUtils.findFiles("*/", prdFolder);
3317
3520
  ctx2.demoRepos = [];
3318
3521
  for (const entry of entries) {
3319
- const dirPath = path10.join(prdFolder, entry);
3320
- const gitDir = path10.join(dirPath, ".git");
3522
+ const dirPath = path11.join(prdFolder, entry);
3523
+ const gitDir = path11.join(dirPath, ".git");
3321
3524
  const hasGit = await FileUtils.exists(gitDir);
3322
3525
  if (hasGit) {
3323
3526
  ctx2.demoRepos.push(dirPath);
@@ -3490,7 +3693,7 @@ init_utils();
3490
3693
  init_logger();
3491
3694
  import { Command as Command6 } from "commander";
3492
3695
  import inquirer5 from "inquirer";
3493
- import path11 from "path";
3696
+ import path12 from "path";
3494
3697
  import { Listr as Listr5 } from "listr2";
3495
3698
  var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8BB0\u5F55").action(async () => {
3496
3699
  try {
@@ -3539,7 +3742,7 @@ var bugfixCommand = new Command6("bugfix").description("\u521B\u5EFA Bugfix \u8B
3539
3742
  const relatedSpec = await findRelatedSpec(answers.description);
3540
3743
  const bugfixDir = "docs/bugfixes";
3541
3744
  await FileUtils.ensureDir(bugfixDir);
3542
- const bugfixFile = path11.join(bugfixDir, `${timestamp}_${bugId}.md`);
3745
+ const bugfixFile = path12.join(bugfixDir, `${timestamp}_${bugId}.md`);
3543
3746
  const content = formatBugfixDocument({
3544
3747
  id: bugId,
3545
3748
  severity: answers.severity,
@@ -3615,7 +3818,7 @@ var hotfixCommand = new Command6("hotfix").description("\u7D27\u6025\u4FEE\u590D
3615
3818
  const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
3616
3819
  const hotfixDir = "docs/hotfixes";
3617
3820
  await FileUtils.ensureDir(hotfixDir);
3618
- const hotfixFile = path11.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3821
+ const hotfixFile = path12.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
3619
3822
  const content = formatHotfixDocument({
3620
3823
  id: hotfixId,
3621
3824
  description: answers.description,
@@ -3690,7 +3893,7 @@ async function findRelatedSpec(description) {
3690
3893
  }
3691
3894
  const keywords = extractKeywords(description);
3692
3895
  for (const file of specs) {
3693
- const filePath = path11.join(specDir, file);
3896
+ const filePath = path12.join(specDir, file);
3694
3897
  const content = await FileUtils.read(filePath);
3695
3898
  for (const keyword of keywords) {
3696
3899
  if (content.toLowerCase().includes(keyword.toLowerCase())) {
@@ -3908,7 +4111,7 @@ init_esm_shims();
3908
4111
  init_utils();
3909
4112
  init_logger();
3910
4113
  import { Command as Command8 } from "commander";
3911
- import path12 from "path";
4114
+ import path13 from "path";
3912
4115
  var statusCommand = new Command8("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
3913
4116
  try {
3914
4117
  logger.header("\u9879\u76EE\u72B6\u6001");
@@ -3966,7 +4169,7 @@ async function displayFeatureInventory() {
3966
4169
  }
3967
4170
  const inventory = [];
3968
4171
  for (const file of specs) {
3969
- const filePath = path12.join(specDir, file);
4172
+ const filePath = path13.join(specDir, file);
3970
4173
  const content = await FileUtils.read(filePath);
3971
4174
  const status = parseSpecStatus2(content);
3972
4175
  inventory.push({
@@ -4025,7 +4228,7 @@ async function displayRecentActivity() {
4025
4228
  }
4026
4229
  const sorted = files.sort().reverse().slice(0, 5);
4027
4230
  for (const file of sorted) {
4028
- const filePath = path12.join(sessionDir, file);
4231
+ const filePath = path13.join(sessionDir, file);
4029
4232
  const stat = await FileUtils.read(filePath);
4030
4233
  const specMatch = stat.match(/\*\*Spec\*\*:\s*(.+)/);
4031
4234
  const spec = specMatch ? specMatch[1].trim() : "\u672A\u77E5";
@@ -4067,7 +4270,7 @@ init_esm_shims();
4067
4270
  init_utils();
4068
4271
  init_logger();
4069
4272
  import { Command as Command9 } from "commander";
4070
- import path13 from "path";
4273
+ import path14 from "path";
4071
4274
  import inquirer6 from "inquirer";
4072
4275
  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
4276
  try {
@@ -4095,7 +4298,7 @@ var detectDepsCommand = new Command9("detect-deps").argument("[spec-file]", "Spe
4095
4298
  process.exit(1);
4096
4299
  }
4097
4300
  for (const spec of specs) {
4098
- const specPath = path13.join(specsDir, spec);
4301
+ const specPath = path14.join(specsDir, spec);
4099
4302
  logger.step(`\u5904\u7406: ${spec}`);
4100
4303
  await detectDependencies(specPath);
4101
4304
  logger.newLine();
@@ -4121,7 +4324,7 @@ async function detectDependencies(specFile) {
4121
4324
  logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
4122
4325
  const projectDir = ".";
4123
4326
  const allDeps = /* @__PURE__ */ new Set();
4124
- const backendDir = path13.join(projectDir, "backend");
4327
+ const backendDir = path14.join(projectDir, "backend");
4125
4328
  const backendExists = await FileUtils.exists(backendDir);
4126
4329
  if (backendExists) {
4127
4330
  const apiDeps = await scanBackendApiCalls(backendDir);
@@ -4131,7 +4334,7 @@ async function detectDependencies(specFile) {
4131
4334
  const serviceDeps = await scanBackendServiceRefs(backendDir);
4132
4335
  serviceDeps.forEach((d) => allDeps.add(d));
4133
4336
  }
4134
- const frontendDir = path13.join(projectDir, "frontend");
4337
+ const frontendDir = path14.join(projectDir, "frontend");
4135
4338
  const frontendExists = await FileUtils.exists(frontendDir);
4136
4339
  if (frontendExists) {
4137
4340
  const frontendDeps = await scanFrontendApiCalls(frontendDir);
@@ -4172,11 +4375,11 @@ async function detectDependencies(specFile) {
4172
4375
  }
4173
4376
  async function scanBackendApiCalls(backendDir) {
4174
4377
  const deps = [];
4175
- const srcDir = path13.join(backendDir, "src");
4378
+ const srcDir = path14.join(backendDir, "src");
4176
4379
  try {
4177
4380
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4178
4381
  for (const file of javaFiles) {
4179
- const filePath = path13.join(srcDir, file);
4382
+ const filePath = path14.join(srcDir, file);
4180
4383
  const content = await FileUtils.read(filePath);
4181
4384
  const pathRegex = /"(\/api\/[^"]+)"/g;
4182
4385
  let match;
@@ -4194,11 +4397,11 @@ async function scanBackendApiCalls(backendDir) {
4194
4397
  }
4195
4398
  async function scanBackendEntityRelations(backendDir) {
4196
4399
  const deps = [];
4197
- const srcDir = path13.join(backendDir, "src");
4400
+ const srcDir = path14.join(backendDir, "src");
4198
4401
  try {
4199
4402
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4200
4403
  for (const file of javaFiles) {
4201
- const filePath = path13.join(srcDir, file);
4404
+ const filePath = path14.join(srcDir, file);
4202
4405
  const content = await FileUtils.read(filePath);
4203
4406
  if (content.includes("@JoinColumn") || content.includes("@ManyToOne") || content.includes("@OneToMany")) {
4204
4407
  const typeRegex = /type\s*=\s*(\w+)/g;
@@ -4214,11 +4417,11 @@ async function scanBackendEntityRelations(backendDir) {
4214
4417
  }
4215
4418
  async function scanBackendServiceRefs(backendDir) {
4216
4419
  const deps = [];
4217
- const srcDir = path13.join(backendDir, "src");
4420
+ const srcDir = path14.join(backendDir, "src");
4218
4421
  try {
4219
4422
  const javaFiles = await FileUtils.findFiles("*.java", srcDir);
4220
4423
  for (const file of javaFiles) {
4221
- const filePath = path13.join(srcDir, file);
4424
+ const filePath = path14.join(srcDir, file);
4222
4425
  const content = await FileUtils.read(filePath);
4223
4426
  const serviceRegex = /private\s+(\w+)Service/g;
4224
4427
  let match;
@@ -4234,11 +4437,11 @@ async function scanBackendServiceRefs(backendDir) {
4234
4437
  }
4235
4438
  async function scanFrontendApiCalls(frontendDir) {
4236
4439
  const deps = [];
4237
- const srcDir = path13.join(frontendDir, "src");
4440
+ const srcDir = path14.join(frontendDir, "src");
4238
4441
  try {
4239
4442
  const tsFiles = await FileUtils.findFiles("*.{ts,tsx,js,jsx}", srcDir);
4240
4443
  for (const file of tsFiles) {
4241
- const filePath = path13.join(srcDir, file);
4444
+ const filePath = path14.join(srcDir, file);
4242
4445
  const content = await FileUtils.read(filePath);
4243
4446
  const pathRegex = /"(\/api\/[^"]+)"/g;
4244
4447
  let match;
@@ -4328,7 +4531,7 @@ init_esm_shims();
4328
4531
  init_utils();
4329
4532
  init_logger();
4330
4533
  import { Command as Command10 } from "commander";
4331
- import path14 from "path";
4534
+ import path15 from "path";
4332
4535
  var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
4333
4536
  try {
4334
4537
  logger.header("\u540C\u6B65 AI_MEMORY.md");
@@ -4361,7 +4564,7 @@ var syncMemoryCommand = new Command10("sync-memory").description("\u540C\u6B65 A
4361
4564
  });
4362
4565
  async function syncFeatureInventory(aiMemoryFile, projectDir) {
4363
4566
  logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
4364
- const specsDir = path14.join(projectDir, "docs/specs");
4567
+ const specsDir = path15.join(projectDir, "docs/specs");
4365
4568
  const exists = await FileUtils.exists(specsDir);
4366
4569
  if (!exists) {
4367
4570
  return;
@@ -4376,7 +4579,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4376
4579
  for (const specFile of specs) {
4377
4580
  const name = specFile.replace(".md", "");
4378
4581
  const displayName = name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
4379
- const specPath = path14.join(specsDir, specFile);
4582
+ const specPath = path15.join(specsDir, specFile);
4380
4583
  const content = await FileUtils.read(specPath);
4381
4584
  const status = parseSpecStatus3(content);
4382
4585
  const progress = getSpecProgress(content);
@@ -4394,7 +4597,7 @@ async function syncFeatureInventory(aiMemoryFile, projectDir) {
4394
4597
  }
4395
4598
  async function syncApiInventory(aiMemoryFile, projectDir) {
4396
4599
  logger.step("\u540C\u6B65 API \u5217\u8868...");
4397
- const backendDir = path14.join(projectDir, "backend");
4600
+ const backendDir = path15.join(projectDir, "backend");
4398
4601
  const exists = await FileUtils.exists(backendDir);
4399
4602
  if (!exists) {
4400
4603
  return;
@@ -4404,13 +4607,13 @@ async function syncApiInventory(aiMemoryFile, projectDir) {
4404
4607
  lines.push("");
4405
4608
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Controller \u751F\u6210");
4406
4609
  lines.push("");
4407
- const srcDir = path14.join(backendDir, "src");
4610
+ const srcDir = path15.join(backendDir, "src");
4408
4611
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4409
4612
  if (controllers.length === 0) {
4410
4613
  lines.push("\u6682\u65E0 API");
4411
4614
  } else {
4412
4615
  for (const controllerFile of controllers) {
4413
- const controllerPath = path14.join(srcDir, controllerFile);
4616
+ const controllerPath = path15.join(srcDir, controllerFile);
4414
4617
  const controllerName = controllerFile.replace(".java", "");
4415
4618
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4416
4619
  lines.push(`### ${module} \u6A21\u5757`);
@@ -4487,7 +4690,7 @@ function extractMethodComment(content, methodName) {
4487
4690
  }
4488
4691
  async function syncDataModels(aiMemoryFile, projectDir) {
4489
4692
  logger.step("\u540C\u6B65\u6570\u636E\u6A21\u578B...");
4490
- const backendDir = path14.join(projectDir, "backend");
4693
+ const backendDir = path15.join(projectDir, "backend");
4491
4694
  const exists = await FileUtils.exists(backendDir);
4492
4695
  if (!exists) {
4493
4696
  return;
@@ -4497,7 +4700,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4497
4700
  lines.push("");
4498
4701
  lines.push("> \u672C\u90E8\u5206\u7531 team-cli \u81EA\u52A8\u626B\u63CF\u540E\u7AEF Entity \u751F\u6210");
4499
4702
  lines.push("");
4500
- const srcDir = path14.join(backendDir, "src");
4703
+ const srcDir = path15.join(backendDir, "src");
4501
4704
  const entities = await FileUtils.findFiles("*Entity.java", srcDir);
4502
4705
  if (entities.length === 0) {
4503
4706
  lines.push("\u6682\u65E0\u6570\u636E\u6A21\u578B");
@@ -4505,7 +4708,7 @@ async function syncDataModels(aiMemoryFile, projectDir) {
4505
4708
  lines.push("| \u6A21\u578B | \u8BF4\u660E | \u5B57\u6BB5 | \u5173\u8054 |");
4506
4709
  lines.push("|------|------|------|------|");
4507
4710
  for (const entityFile of entities) {
4508
- const entityPath = path14.join(srcDir, entityFile);
4711
+ const entityPath = path15.join(srcDir, entityFile);
4509
4712
  const entityName = entityFile.replace(".java", "").replace(/Entity$/, "");
4510
4713
  const displayName = entityName.split(/(?=[A-Z])/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
4511
4714
  const content = await FileUtils.read(entityPath);
@@ -4627,15 +4830,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
4627
4830
  await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
4628
4831
  }
4629
4832
  function extractRepoName(repository) {
4630
- let path18 = repository;
4631
- path18 = path18.replace(/^https?:\/\//, "");
4632
- path18 = path18.replace(/^git@/, "");
4633
- const parts = path18.split("/");
4833
+ let path20 = repository;
4834
+ path20 = path20.replace(/^https?:\/\//, "");
4835
+ path20 = path20.replace(/^git@/, "");
4836
+ const parts = path20.split("/");
4634
4837
  if (parts.length > 1) {
4635
- path18 = parts.slice(1).join("/");
4838
+ path20 = parts.slice(1).join("/");
4636
4839
  }
4637
- path18 = path18.replace(/\.git$/, "");
4638
- return path18;
4840
+ path20 = path20.replace(/\.git$/, "");
4841
+ return path20;
4639
4842
  }
4640
4843
 
4641
4844
  // src/commands/check-api.ts
@@ -4643,7 +4846,7 @@ init_esm_shims();
4643
4846
  init_utils();
4644
4847
  init_logger();
4645
4848
  import { Command as Command11 } from "commander";
4646
- import path15 from "path";
4849
+ import path16 from "path";
4647
4850
  import inquirer7 from "inquirer";
4648
4851
  import { Listr as Listr6 } from "listr2";
4649
4852
  var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
@@ -4707,7 +4910,7 @@ var checkApiCommand = new Command11("check-api").description("API \u68C0\u67E5\u
4707
4910
  }
4708
4911
  });
4709
4912
  async function checkApiConflicts(projectDir) {
4710
- const backendDir = path15.join(projectDir, "backend");
4913
+ const backendDir = path16.join(projectDir, "backend");
4711
4914
  const exists = await FileUtils.exists(backendDir);
4712
4915
  if (!exists) {
4713
4916
  logger.info("\u672A\u627E\u5230\u540E\u7AEF\u9879\u76EE");
@@ -4716,10 +4919,10 @@ async function checkApiConflicts(projectDir) {
4716
4919
  logger.step("\u626B\u63CF\u540E\u7AEF API...");
4717
4920
  logger.newLine();
4718
4921
  const apiMap = /* @__PURE__ */ new Map();
4719
- const srcDir = path15.join(backendDir, "src");
4922
+ const srcDir = path16.join(backendDir, "src");
4720
4923
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4721
4924
  for (const controllerFile of controllers) {
4722
- const controllerPath = path15.join(srcDir, controllerFile);
4925
+ const controllerPath = path16.join(srcDir, controllerFile);
4723
4926
  const apis = await extractApisFromController(controllerPath);
4724
4927
  for (const api of apis) {
4725
4928
  const key = `${api.method}:${api.path}`;
@@ -4750,8 +4953,8 @@ async function checkApiConflicts(projectDir) {
4750
4953
  }
4751
4954
  }
4752
4955
  async function detectApiChanges(projectDir) {
4753
- const backendDir = path15.join(projectDir, "backend");
4754
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
4956
+ const backendDir = path16.join(projectDir, "backend");
4957
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4755
4958
  const registryExists = await FileUtils.exists(registryFile);
4756
4959
  if (!registryExists) {
4757
4960
  logger.info("API Registry \u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7\u53D8\u66F4\u68C0\u6D4B");
@@ -4763,10 +4966,10 @@ async function detectApiChanges(projectDir) {
4763
4966
  const registryContent = await FileUtils.read(registryFile);
4764
4967
  const existingApis = extractApisFromRegistry(registryContent);
4765
4968
  const currentApis = /* @__PURE__ */ new Map();
4766
- const srcDir = path15.join(backendDir, "src");
4969
+ const srcDir = path16.join(backendDir, "src");
4767
4970
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4768
4971
  for (const controllerFile of controllers) {
4769
- const controllerPath = path15.join(srcDir, controllerFile);
4972
+ const controllerPath = path16.join(srcDir, controllerFile);
4770
4973
  const apis = await extractApisFromController(controllerPath);
4771
4974
  for (const api of apis) {
4772
4975
  const key = `${api.method}:${api.path}`;
@@ -4828,9 +5031,9 @@ async function detectApiChanges(projectDir) {
4828
5031
  }
4829
5032
  }
4830
5033
  async function generateApiRegistry(projectDir) {
4831
- const registryFile = path15.join(projectDir, "docs/api-registry.md");
5034
+ const registryFile = path16.join(projectDir, "docs/api-registry.md");
4832
5035
  logger.step("\u626B\u63CF\u5E76\u751F\u6210 API Registry...");
4833
- await FileUtils.ensureDir(path15.dirname(registryFile));
5036
+ await FileUtils.ensureDir(path16.dirname(registryFile));
4834
5037
  const header = `# API Registry
4835
5038
 
4836
5039
  > \u672C\u6587\u4EF6\u8BB0\u5F55\u6240\u6709 API \u7684\u5B9A\u4E49\u3001\u7248\u672C\u548C\u53D8\u66F4\u5386\u53F2
@@ -4859,14 +5062,14 @@ async function generateApiRegistry(projectDir) {
4859
5062
  *\u6700\u540E\u66F4\u65B0: ${DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss")}*
4860
5063
  `;
4861
5064
  let content = header;
4862
- const backendDir = path15.join(projectDir, "backend");
5065
+ const backendDir = path16.join(projectDir, "backend");
4863
5066
  const exists = await FileUtils.exists(backendDir);
4864
5067
  if (exists) {
4865
- const srcDir = path15.join(backendDir, "src");
5068
+ const srcDir = path16.join(backendDir, "src");
4866
5069
  const controllers = await FileUtils.findFiles("*Controller.java", srcDir);
4867
5070
  const moduleMap = /* @__PURE__ */ new Map();
4868
5071
  for (const controllerFile of controllers) {
4869
- const controllerPath = path15.join(srcDir, controllerFile);
5072
+ const controllerPath = path16.join(srcDir, controllerFile);
4870
5073
  const controllerName = controllerFile.replace(".java", "");
4871
5074
  const module = controllerName.replace(/Controller$/, "").toLowerCase();
4872
5075
  if (!moduleMap.has(module)) {
@@ -4950,10 +5153,10 @@ function extractApisFromRegistry(registryContent) {
4950
5153
  let match;
4951
5154
  while ((match = apiRegex.exec(registryContent)) !== null) {
4952
5155
  const method = match[1];
4953
- const path18 = match[2].trim();
5156
+ const path20 = match[2].trim();
4954
5157
  const description = match[3].trim();
4955
- const key = `${method}:${path18}`;
4956
- apis.set(key, { method, path: path18, description });
5158
+ const key = `${method}:${path20}`;
5159
+ apis.set(key, { method, path: path20, description });
4957
5160
  }
4958
5161
  return apis;
4959
5162
  }
@@ -4975,7 +5178,7 @@ init_esm_shims();
4975
5178
  init_utils();
4976
5179
  init_logger();
4977
5180
  import { Command as Command12 } from "commander";
4978
- import path16 from "path";
5181
+ import path17 from "path";
4979
5182
  import inquirer8 from "inquirer";
4980
5183
  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
5184
  try {
@@ -5000,7 +5203,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5000
5203
  case "":
5001
5204
  case "today": {
5002
5205
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5003
- targetDir = path16.join(sessionsDir, today);
5206
+ targetDir = path17.join(sessionsDir, today);
5004
5207
  const todayExists = await FileUtils.exists(targetDir);
5005
5208
  if (!todayExists) {
5006
5209
  logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
@@ -5016,7 +5219,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5016
5219
  break;
5017
5220
  }
5018
5221
  default: {
5019
- targetDir = path16.join(sessionsDir, filter);
5222
+ targetDir = path17.join(sessionsDir, filter);
5020
5223
  const dateExists = await FileUtils.exists(targetDir);
5021
5224
  if (!dateExists) {
5022
5225
  logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
@@ -5040,7 +5243,7 @@ var logsCommand = new Command12("logs").argument("[filter]", "\u8FC7\u6EE4\u5668
5040
5243
  process.exit(0);
5041
5244
  }
5042
5245
  for (let i = 0; i < logs.length; i++) {
5043
- const relPath = path16.relative(sessionsDir, logs[i]);
5246
+ const relPath = path17.relative(sessionsDir, logs[i]);
5044
5247
  logger.step(`${i + 1}) ${relPath}`);
5045
5248
  }
5046
5249
  logger.newLine();
@@ -5081,7 +5284,7 @@ async function collectLogFiles(targetDir) {
5081
5284
  const allFiles = await FileUtils.findFiles("*.md", targetDir);
5082
5285
  const filtered = allFiles.filter((f) => f !== "index.md");
5083
5286
  for (const file of filtered) {
5084
- const filePath = path16.join(targetDir, file);
5287
+ const filePath = path17.join(targetDir, file);
5085
5288
  const stat = await FileUtils.exists(filePath);
5086
5289
  if (stat) {
5087
5290
  logs.push(filePath);
@@ -5095,12 +5298,12 @@ async function collectLogFiles(targetDir) {
5095
5298
  // src/commands/update.ts
5096
5299
  init_esm_shims();
5097
5300
  import { Command as Command13 } from "commander";
5098
- import path17 from "path";
5301
+ import path18 from "path";
5099
5302
  init_logger();
5100
5303
  init_utils();
5101
5304
  import { execa as execa4 } from "execa";
5102
5305
  import inquirer9 from "inquirer";
5103
- import fs4 from "fs-extra";
5306
+ import fs5 from "fs-extra";
5104
5307
  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
5308
  try {
5106
5309
  logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
@@ -5200,7 +5403,7 @@ async function performUpdate(projectPath, updates) {
5200
5403
  for (const update of updates) {
5201
5404
  const { type, info, updateOptions } = update;
5202
5405
  const targetDir = type === "frontend" ? "frontend" : "backend";
5203
- const targetPath = path17.join(projectPath, targetDir);
5406
+ const targetPath = path18.join(projectPath, targetDir);
5204
5407
  logger.newLine();
5205
5408
  logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
5206
5409
  if (updateOptions?.tag || updateOptions?.branch) {
@@ -5231,8 +5434,8 @@ async function performUpdate(projectPath, updates) {
5231
5434
  }
5232
5435
  }
5233
5436
  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));
5437
+ const backupDir = path18.join(projectPath, `.backup-${Date.now()}`);
5438
+ await fs5.copy(targetPath, path18.join(backupDir, targetDir));
5236
5439
  logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
5237
5440
  if (updateOptions?.dryRun) {
5238
5441
  logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
@@ -5242,7 +5445,7 @@ async function performUpdate(projectPath, updates) {
5242
5445
  continue;
5243
5446
  }
5244
5447
  try {
5245
- const tempDir = path17.join(projectPath, `.template-update-${Date.now()}`);
5448
+ const tempDir = path18.join(projectPath, `.template-update-${Date.now()}`);
5246
5449
  await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
5247
5450
  stdio: "pipe"
5248
5451
  });
@@ -5259,17 +5462,17 @@ async function performUpdate(projectPath, updates) {
5259
5462
  const currentFiles = await FileUtils.findFiles("*", targetPath);
5260
5463
  for (const file of currentFiles) {
5261
5464
  if (!keepFiles.includes(file)) {
5262
- const filePath = path17.join(targetPath, file);
5465
+ const filePath = path18.join(targetPath, file);
5263
5466
  try {
5264
- await fs4.remove(filePath);
5467
+ await fs5.remove(filePath);
5265
5468
  } catch {
5266
5469
  }
5267
5470
  }
5268
5471
  }
5269
- await fs4.copy(tempDir, targetPath, {
5472
+ await fs5.copy(tempDir, targetPath, {
5270
5473
  filter: (src) => !src.includes(".git")
5271
5474
  });
5272
- await fs4.remove(tempDir);
5475
+ await fs5.remove(tempDir);
5273
5476
  await updateTemplateVersion(projectPath, type, commit.trim(), {
5274
5477
  tag: updateOptions?.tag || latestTag,
5275
5478
  branch: updateOptions?.branch
@@ -5280,9 +5483,9 @@ async function performUpdate(projectPath, updates) {
5280
5483
  } catch (error) {
5281
5484
  logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
5282
5485
  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);
5486
+ await fs5.remove(targetPath);
5487
+ await fs5.copy(path18.join(backupDir, targetDir), targetPath);
5488
+ await fs5.remove(backupDir);
5286
5489
  logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
5287
5490
  }
5288
5491
  }
@@ -5767,8 +5970,10 @@ var Table = class {
5767
5970
  };
5768
5971
 
5769
5972
  // src/index.ts
5973
+ var __dirname2 = path19.dirname(fileURLToPath2(import.meta.url));
5974
+ var pkg = fs6.readJsonSync(path19.join(__dirname2, "../package.json"));
5770
5975
  var program = new Command16();
5771
- program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version("2.1.9");
5976
+ program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
5772
5977
  program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
5773
5978
  program.addCommand(initCommand);
5774
5979
  program.addCommand(splitPrdCommand);