yg-team-cli 2.6.3 → 2.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -212,6 +212,12 @@ var init_utils = __esm({
212
212
  static async read(file) {
213
213
  return await fs.readFile(file, "utf-8");
214
214
  }
215
+ /**
216
+ * 同步读取文件内容
217
+ */
218
+ static readSync(file) {
219
+ return fs.readFileSync(file, "utf-8");
220
+ }
215
221
  /**
216
222
  * 写入文件内容(自动创建父目录)
217
223
  */
@@ -1003,13 +1009,6 @@ async function saveTemplateConfig(projectPath, config) {
1003
1009
  const content = JSON.stringify(config, null, 2);
1004
1010
  await FileUtils.write(configPath, content);
1005
1011
  }
1006
- async function initTemplateConfig(projectPath) {
1007
- const config = await readTemplateConfig(projectPath);
1008
- if (config) {
1009
- return;
1010
- }
1011
- await saveTemplateConfig(projectPath, DEFAULT_TEMPLATES);
1012
- }
1013
1012
  async function getLatestCommit(repository) {
1014
1013
  try {
1015
1014
  const { stdout } = await execa2("git", ["ls-remote", repository, "HEAD"], {
@@ -1339,27 +1338,164 @@ var init_module_registry = __esm({
1339
1338
  } else if (module.type === "remote") {
1340
1339
  await this.generateRemoteSpecPlaceholder(targetPath, module);
1341
1340
  addedFiles.push(path5.relative(projectPath, targetPath));
1341
+ } else {
1342
+ await this.generateLocalSpecPlaceholder(targetPath, module);
1343
+ addedFiles.push(path5.relative(projectPath, targetPath));
1342
1344
  }
1343
1345
  }
1344
- if (module.type === "local" && module.backendFragments) {
1346
+ if (module.backendFragments) {
1345
1347
  const backendJavaDir = path5.join(projectPath, "backend/src/main/java");
1346
- const commonBasePath = path5.join(backendJavaDir, "org/yungu/common");
1348
+ const useTemplates = await FileUtils.exists(path5.join(templatesDir, "modules"));
1347
1349
  for (const fragment of module.backendFragments) {
1350
+ let sourcePath = null;
1351
+ let targetPath;
1352
+ let content;
1353
+ if (useTemplates) {
1354
+ sourcePath = path5.join(templatesDir, "modules", fragment.source);
1355
+ if (await FileUtils.exists(sourcePath)) {
1356
+ content = await FileUtils.read(sourcePath);
1357
+ }
1358
+ }
1359
+ if (!content) {
1360
+ content = this.generatePlaceholderCode(module, fragment);
1361
+ }
1362
+ if (fragment.target.startsWith("common/")) {
1363
+ const relativePath = fragment.target.replace(/^common\//, "org/yungu/common/");
1364
+ targetPath = path5.join(backendJavaDir, relativePath);
1365
+ } else if (fragment.target.startsWith("config/")) {
1366
+ const mainModule = this.findMainModule(projectPath);
1367
+ if (mainModule) {
1368
+ const relativePath = fragment.target.replace(/^config\//, `${mainModule}/config/`);
1369
+ targetPath = path5.join(backendJavaDir, relativePath);
1370
+ } else {
1371
+ targetPath = path5.join(backendJavaDir, fragment.target);
1372
+ }
1373
+ } else {
1374
+ targetPath = path5.join(backendJavaDir, fragment.target);
1375
+ }
1376
+ await FileUtils.ensureDir(path5.dirname(targetPath));
1377
+ await FileUtils.write(targetPath, content);
1378
+ addedFiles.push(path5.relative(projectPath, targetPath));
1379
+ }
1380
+ }
1381
+ if (module.frontendFragments) {
1382
+ const frontendSrcDir = path5.join(projectPath, "frontend/src");
1383
+ for (const fragment of module.frontendFragments) {
1348
1384
  const sourcePath = path5.join(templatesDir, "modules", fragment.source);
1349
- const targetPath = path5.join(commonBasePath, fragment.target);
1385
+ const targetPath = path5.join(frontendSrcDir, fragment.target);
1350
1386
  if (await FileUtils.exists(sourcePath)) {
1351
- await FileUtils.ensureDir(path5.dirname(targetPath));
1352
- let content = await FileUtils.read(sourcePath);
1353
- const targetPackage = "org.yungu.common." + path5.dirname(fragment.target).replace(/\//g, ".");
1354
- content = content.replace(/^package\s+[^;]+;/m, `package ${targetPackage};`);
1355
- await FileUtils.write(targetPath, content);
1387
+ await FileUtils.copy(sourcePath, targetPath);
1356
1388
  addedFiles.push(path5.relative(projectPath, targetPath));
1357
1389
  }
1358
1390
  }
1359
- } else if (module.type === "remote") {
1360
1391
  }
1361
1392
  return addedFiles;
1362
1393
  }
1394
+ /**
1395
+ * 查找主模块名 (第一个 service 类型的模块)
1396
+ */
1397
+ static findMainModule(projectPath) {
1398
+ const settingsPath = path5.join(projectPath, "backend/settings.gradle");
1399
+ const content = FileUtils.readSync(settingsPath);
1400
+ const matches = content.match(/include\s+'([^']+)'/g);
1401
+ if (matches) {
1402
+ for (const match of matches) {
1403
+ const moduleName = match.replace(/include\s+'/, "").replace(/'/, "");
1404
+ if (moduleName !== "common") {
1405
+ return moduleName;
1406
+ }
1407
+ }
1408
+ }
1409
+ return null;
1410
+ }
1411
+ /**
1412
+ * 生成占位符代码
1413
+ */
1414
+ static generatePlaceholderCode(module, fragment) {
1415
+ const targetPackage = path5.dirname(fragment.target).replace(/\//g, ".");
1416
+ const className = path5.basename(fragment.target, ".java");
1417
+ const moduleName = module.name;
1418
+ const moduleId = module.id;
1419
+ return `package ${targetPackage};
1420
+
1421
+ import lombok.extern.slf4j.Slf4j;
1422
+
1423
+ /**
1424
+ * ${moduleName} - \u5360\u4F4D\u7B26
1425
+ *
1426
+ * \u6B64\u6587\u4EF6\u7531 team-cli \u81EA\u52A8\u751F\u6210\u3002
1427
+ * \u6A21\u5757 ID: ${moduleId}
1428
+ *
1429
+ * \u4F7F\u7528\u8BF4\u660E:
1430
+ * 1. \u8BF7\u6839\u636E\u9879\u76EE\u5B9E\u9645\u9700\u6C42\u5B8C\u5584\u6B64\u6587\u4EF6
1431
+ * 2. \u6216\u53C2\u8003 docs/specs/${moduleId}/spec.md \u83B7\u53D6\u8BE6\u7EC6\u9700\u6C42
1432
+ */
1433
+ @Slf4j
1434
+ public class ${className} {
1435
+
1436
+ /**
1437
+ * TODO: \u5B9E\u73B0 ${moduleName} \u529F\u80FD
1438
+ */
1439
+ public void execute() {
1440
+ log.info("${className} - \u6267\u884C\u4E2D...");
1441
+ // \u5728\u6B64\u5B9E\u73B0\u5177\u4F53\u903B\u8F91
1442
+ }
1443
+ }
1444
+ `;
1445
+ }
1446
+ /**
1447
+ * 生成本地模块的 Spec 占位符
1448
+ */
1449
+ static async generateLocalSpecPlaceholder(targetPath, module) {
1450
+ const content = `# ${module.name}
1451
+
1452
+ ## \u529F\u80FD\u6982\u8FF0
1453
+ ${module.description}
1454
+
1455
+ ## \u4F7F\u7528\u8BF4\u660E
1456
+
1457
+ \u6B64\u6A21\u5757\u4E3A\u4E91\u8C37\u901A\u7528\u6A21\u5757\u7684\u5360\u4F4D\u7B26\u914D\u7F6E\u3002
1458
+
1459
+ ### \u914D\u7F6E\u6B65\u9AA4
1460
+
1461
+ 1. **\u6DFB\u52A0\u4F9D\u8D56**
1462
+
1463
+ \u5728 \`backend/build.gradle\` \u4E2D\u6DFB\u52A0:
1464
+
1465
+ \`\`\`groovy
1466
+ ${module.dependencies ? module.dependencies.map((d) => `implementation '${d}'`).join("\n") : ""}
1467
+ \`\`\`
1468
+
1469
+ 2. **\u914D\u7F6E\u6587\u4EF6**
1470
+
1471
+ \u6839\u636E\u9700\u6C42\u914D\u7F6E application.yml:
1472
+
1473
+ \`\`\`yaml
1474
+ # ${module.name} \u914D\u7F6E
1475
+ \`\`\`
1476
+
1477
+ 3. **\u5BFC\u5165\u914D\u7F6E\u7C7B**
1478
+
1479
+ \u5728\u4E3B Application \u7C7B\u4E0A\u6DFB\u52A0\u6CE8\u89E3:
1480
+
1481
+ \`\`\`java
1482
+ @SpringBootApplication
1483
+ @ComponentScan("${module.id}")
1484
+ public class Application {
1485
+ // ...
1486
+ }
1487
+ \`\`\`
1488
+
1489
+ 4. **\u53C2\u8003\u6587\u6863**
1490
+
1491
+ \u67E5\u770B docs/specs/${module.id}/spec.md \u83B7\u53D6\u8BE6\u7EC6\u5B9E\u73B0\u8BF4\u660E\u3002
1492
+
1493
+ ## \u4F9D\u8D56\u5173\u7CFB
1494
+
1495
+ ${module.requires ? `\u9700\u8981\u5148\u542F\u7528: ${module.requires.join(", ")}` : "\u65E0\u524D\u7F6E\u4F9D\u8D56"}
1496
+ `;
1497
+ await FileUtils.write(targetPath, content);
1498
+ }
1363
1499
  /**
1364
1500
  * 生成远程模块的 Spec 占位符
1365
1501
  */
@@ -1380,455 +1516,222 @@ var init_module_registry = __esm({
1380
1516
  }
1381
1517
  });
1382
1518
 
1383
- // src/lib/user-config.ts
1384
- var user_config_exports = {};
1385
- __export(user_config_exports, {
1386
- UserConfigManager: () => UserConfigManager,
1387
- userConfigManager: () => userConfigManager
1388
- });
1519
+ // src/commands/init.ts
1520
+ import { Command } from "commander";
1521
+ import inquirer from "inquirer";
1389
1522
  import path6 from "path";
1523
+ import fs3 from "fs-extra";
1390
1524
  import os2 from "os";
1391
- import crypto from "crypto";
1392
- var UserConfigManager, userConfigManager;
1393
- var init_user_config = __esm({
1394
- "src/lib/user-config.ts"() {
1395
- "use strict";
1396
- init_esm_shims();
1397
- init_utils();
1398
- init_logger();
1399
- UserConfigManager = class {
1400
- configPath;
1401
- constructor() {
1402
- const configDir = path6.join(os2.homedir(), ".team-cli");
1403
- this.configPath = path6.join(configDir, "config.json");
1404
- }
1405
- /**
1406
- * 加载用户配置
1407
- */
1408
- async load() {
1409
- try {
1410
- const exists = await FileUtils.exists(this.configPath);
1411
- if (!exists) {
1412
- return null;
1413
- }
1414
- const content = await FileUtils.read(this.configPath);
1415
- const config = JSON.parse(content);
1416
- if (config.gitlab?.accessToken) {
1417
- config.gitlab.accessToken = this.decrypt(config.gitlab.accessToken);
1525
+ import { Listr } from "listr2";
1526
+ async function collectProjectConfig(defaultProjectName) {
1527
+ let projectName = defaultProjectName;
1528
+ if (!projectName) {
1529
+ const nameAnswer = await inquirer.prompt([
1530
+ {
1531
+ type: "input",
1532
+ name: "projectName",
1533
+ message: "\u8BF7\u8F93\u5165\u9879\u76EE\u76EE\u5F55\u540D\u79F0:",
1534
+ default: "my-project",
1535
+ validate: (input) => {
1536
+ if (!/^[a-z0-9-]+$/.test(input)) {
1537
+ return "\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
1418
1538
  }
1419
- return config;
1420
- } catch (error) {
1421
- logger.debug(`\u52A0\u8F7D\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
1422
- return null;
1539
+ return true;
1423
1540
  }
1424
1541
  }
1425
- /**
1426
- * 保存用户配置
1427
- */
1428
- async save(config) {
1429
- try {
1430
- const configDir = path6.dirname(this.configPath);
1431
- await FileUtils.ensureDir(configDir);
1432
- const configToSave = JSON.parse(JSON.stringify(config));
1433
- if (configToSave.gitlab?.accessToken) {
1434
- configToSave.gitlab.accessToken = this.encrypt(configToSave.gitlab.accessToken);
1542
+ ]);
1543
+ projectName = nameAnswer.projectName;
1544
+ }
1545
+ const typeAnswer = await inquirer.prompt([
1546
+ {
1547
+ type: "list",
1548
+ name: "projectType",
1549
+ message: "\u8BF7\u9009\u62E9\u9879\u76EE\u7C7B\u578B:",
1550
+ choices: [
1551
+ { name: "\u5355\u6A21\u5757\u9879\u76EE", value: "single" },
1552
+ { name: "\u591A\u6A21\u5757\u9879\u76EE (Gradle)", value: "multi" }
1553
+ ],
1554
+ default: "single"
1555
+ }
1556
+ ]);
1557
+ let rootProjectName = projectName;
1558
+ if (typeAnswer.projectType === "multi") {
1559
+ const rootAnswer = await inquirer.prompt([
1560
+ {
1561
+ type: "input",
1562
+ name: "rootProjectName",
1563
+ message: "\u8BF7\u8F93\u5165\u6839\u9879\u76EE\u540D (settings.gradle rootProject.name):",
1564
+ default: projectName,
1565
+ validate: (input) => {
1566
+ if (!/^[a-z0-9-]+$/.test(input)) {
1567
+ return "\u6839\u9879\u76EE\u540D\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
1435
1568
  }
1436
- const content = JSON.stringify(configToSave, null, 2);
1437
- await FileUtils.write(this.configPath, content);
1438
- } catch (error) {
1439
- throw new Error(`\u4FDD\u5B58\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
1569
+ return true;
1440
1570
  }
1441
1571
  }
1442
- /**
1443
- * 更新 GitLab Token
1444
- */
1445
- async updateGitLabToken(token, baseUrl) {
1446
- const config = await this.load() || {
1447
- gitlab: {
1448
- accessToken: "",
1449
- baseUrl: "https://gitlab.com",
1450
- timeout: 3e4
1451
- }
1452
- };
1453
- config.gitlab.accessToken = token;
1454
- if (baseUrl) {
1455
- config.gitlab.baseUrl = baseUrl;
1572
+ ]);
1573
+ rootProjectName = rootAnswer.rootProjectName;
1574
+ }
1575
+ const groupAnswer = await inquirer.prompt([
1576
+ {
1577
+ type: "input",
1578
+ name: "groupId",
1579
+ message: "\u8BF7\u8F93\u5165 Group ID (\u5305\u8DEF\u5F84\u524D\u7F00):",
1580
+ default: "org.yungu",
1581
+ validate: (input) => {
1582
+ if (!/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/.test(input)) {
1583
+ return "Group ID \u683C\u5F0F\u4E0D\u6B63\u786E (\u5982: com.example \u6216 org.yungu)";
1456
1584
  }
1457
- await this.save(config);
1458
- }
1459
- /**
1460
- * 获取 GitLab Token
1461
- */
1462
- async getGitLabToken() {
1463
- const config = await this.load();
1464
- return config?.gitlab?.accessToken || null;
1465
- }
1466
- /**
1467
- * 获取 GitLab 配置
1468
- */
1469
- async getGitLabConfig() {
1470
- const config = await this.load();
1471
- return config?.gitlab || null;
1585
+ return true;
1472
1586
  }
1473
- /**
1474
- * 检查是否已有配置
1475
- */
1476
- async hasConfig() {
1477
- const config = await this.load();
1478
- return config !== null && config.gitlab?.accessToken !== void 0;
1587
+ }
1588
+ ]);
1589
+ const groupId = groupAnswer.groupId;
1590
+ const modules = [];
1591
+ let addMoreModules = true;
1592
+ let moduleIndex = 1;
1593
+ while (addMoreModules) {
1594
+ let defaultModuleName = `module-${moduleIndex}`;
1595
+ if (moduleIndex === 1) {
1596
+ if (typeAnswer.projectType === "single") {
1597
+ defaultModuleName = "app";
1598
+ } else {
1599
+ defaultModuleName = projectName;
1479
1600
  }
1480
- /**
1481
- * 删除配置
1482
- */
1483
- async removeConfig() {
1484
- try {
1485
- const exists = await FileUtils.exists(this.configPath);
1486
- if (exists) {
1487
- await FileUtils.remove(this.configPath);
1601
+ }
1602
+ const moduleNameAnswer = await inquirer.prompt([
1603
+ {
1604
+ type: "input",
1605
+ name: "moduleName",
1606
+ message: `\u8BF7\u8F93\u5165\u7B2C ${moduleIndex} \u4E2A\u5B50\u6A21\u5757\u540D\u79F0 (\u76EE\u5F55\u540D):`,
1607
+ default: defaultModuleName,
1608
+ validate: (input) => {
1609
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
1610
+ return "\u6A21\u5757\u540D\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
1488
1611
  }
1489
- } catch (error) {
1490
- throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
1612
+ return true;
1491
1613
  }
1492
1614
  }
1493
- /**
1494
- * 简单加密 (使用机器特定密钥)
1495
- */
1496
- encrypt(text) {
1497
- const key = this.getMachineKey();
1498
- const iv = crypto.randomBytes(16);
1499
- const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
1500
- let encrypted = cipher.update(text, "utf8", "hex");
1501
- encrypted += cipher.final("hex");
1502
- return iv.toString("hex") + ":" + encrypted;
1503
- }
1504
- /**
1505
- * 简单解密
1506
- */
1507
- decrypt(encryptedText) {
1508
- try {
1509
- const key = this.getMachineKey();
1510
- const parts = encryptedText.split(":");
1511
- const iv = Buffer.from(parts[0], "hex");
1512
- const encrypted = parts[1];
1513
- const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
1514
- let decrypted = decipher.update(encrypted, "hex", "utf8");
1515
- decrypted += decipher.final("utf8");
1516
- return decrypted;
1517
- } catch {
1518
- return encryptedText;
1615
+ ]);
1616
+ const moduleName = moduleNameAnswer.moduleName;
1617
+ let moduleType = "service";
1618
+ if (typeAnswer.projectType === "multi") {
1619
+ const typeSelectAnswer = await inquirer.prompt([
1620
+ {
1621
+ type: "list",
1622
+ name: "moduleType",
1623
+ message: `\u6A21\u5757 "${moduleName}" \u7C7B\u578B:`,
1624
+ choices: [
1625
+ { name: "API \u670D\u52A1\u6A21\u5757 (\u72EC\u7ACB\u90E8\u7F72)", value: "service" },
1626
+ { name: "\u516C\u5171\u6A21\u5757 (\u88AB\u5176\u4ED6\u6A21\u5757\u4F9D\u8D56)", value: "common" },
1627
+ { name: "API \u63A5\u53E3\u6A21\u5757 (\u4EC5\u63A5\u53E3\u5B9A\u4E49)", value: "api" }
1628
+ ],
1629
+ default: "service"
1519
1630
  }
1520
- }
1521
- /**
1522
- * 获取机器特定密钥
1523
- */
1524
- getMachineKey() {
1525
- const hostname = os2.hostname();
1526
- const platform = os2.platform();
1527
- const arch = os2.arch();
1528
- const cpus = os2.cpus();
1529
- const machineInfo = `${hostname}-${platform}-${arch}-${cpus[0]?.model || "unknown"}`;
1530
- return crypto.createHash("sha256").update(machineInfo).digest();
1531
- }
1532
- /**
1533
- * 获取配置目录
1534
- */
1535
- getConfigDir() {
1536
- return path6.dirname(this.configPath);
1537
- }
1538
- /**
1539
- * 获取配置文件路径
1540
- */
1541
- getConfigPath() {
1542
- return this.configPath;
1543
- }
1544
- };
1545
- userConfigManager = new UserConfigManager();
1631
+ ]);
1632
+ moduleType = typeSelectAnswer.moduleType;
1633
+ }
1634
+ const modulePackage = `${groupId}.${moduleName}`;
1635
+ const applicationClass = `${toPascalCase(moduleName)}Application`;
1636
+ modules.push({
1637
+ name: moduleName,
1638
+ type: moduleType,
1639
+ packagePath: modulePackage,
1640
+ applicationClass
1641
+ });
1642
+ if (typeAnswer.projectType === "multi") {
1643
+ const moreAnswer = await inquirer.prompt([
1644
+ {
1645
+ type: "confirm",
1646
+ name: "addMore",
1647
+ message: "\u662F\u5426\u8FD8\u9700\u8981\u6DFB\u52A0\u66F4\u591A\u6A21\u5757?",
1648
+ default: false
1649
+ }
1650
+ ]);
1651
+ addMoreModules = moreAnswer.addMore;
1652
+ } else {
1653
+ addMoreModules = false;
1654
+ }
1655
+ moduleIndex++;
1546
1656
  }
1547
- });
1657
+ const javaAnswer = await inquirer.prompt([
1658
+ {
1659
+ type: "list",
1660
+ name: "javaVersion",
1661
+ message: "\u8BF7\u9009\u62E9 Java \u7248\u672C:",
1662
+ choices: [
1663
+ { name: "Java 8 (1.8)", value: "1.8" },
1664
+ { name: "Java 11 (LTS)", value: "11" },
1665
+ { name: "Java 17 (LTS)", value: "17" },
1666
+ { name: "Java 21 (LTS)", value: "21" }
1667
+ ],
1668
+ default: "17"
1669
+ }
1670
+ ]);
1671
+ const buildAnswer = await inquirer.prompt([
1672
+ {
1673
+ type: "list",
1674
+ name: "buildTool",
1675
+ message: "\u8BF7\u9009\u62E9\u6784\u5EFA\u5DE5\u5177:",
1676
+ choices: [
1677
+ { name: "Gradle", value: "gradle" },
1678
+ { name: "Maven", value: "maven" }
1679
+ ],
1680
+ default: "gradle"
1681
+ }
1682
+ ]);
1683
+ const frontendAnswer = await inquirer.prompt([
1684
+ {
1685
+ type: "confirm",
1686
+ name: "includeFrontend",
1687
+ message: "\u662F\u5426\u9700\u8981\u5305\u542B\u524D\u7AEF\u9879\u76EE?",
1688
+ default: true
1689
+ }
1690
+ ]);
1691
+ return {
1692
+ projectName,
1693
+ projectType: typeAnswer.projectType,
1694
+ rootProjectName,
1695
+ groupId,
1696
+ modules,
1697
+ javaVersion: javaAnswer.javaVersion,
1698
+ buildTool: buildAnswer.buildTool,
1699
+ includeFrontend: frontendAnswer.includeFrontend
1700
+ };
1701
+ }
1702
+ function toPascalCase(str) {
1703
+ return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1704
+ }
1705
+ async function generateTechStack(projectPath, config) {
1706
+ const backendStructure = config.modules.map((m) => {
1707
+ const packagePath = `${config.groupId}.${m.name}`;
1708
+ return `\u251C\u2500\u2500 ${m.name}/
1709
+ \u2502 \u2514\u2500\u2500 src/main/java/${packagePath.replace(/\./g, "/")}/
1710
+ \u2502 \u251C\u2500\u2500 controller/ # API \u63A7\u5236\u5668
1711
+ \u2502 \u251C\u2500\u2500 service/ # \u4E1A\u52A1\u903B\u8F91
1712
+ \u2502 \u251C\u2500\u2500 mapper/ # \u6570\u636E\u8BBF\u95EE
1713
+ \u2502 \u251C\u2500\u2500 entity/ # \u6570\u636E\u6A21\u578B
1714
+ \u2502 \u251C\u2500\u2500 dto/ # \u6570\u636E\u4F20\u8F93\u5BF9\u8C61
1715
+ \u2502 \u2514\u2500\u2500 config/ # \u914D\u7F6E\u7C7B`;
1716
+ }).join("\n");
1717
+ const content = `# \u6280\u672F\u6808
1548
1718
 
1549
- // src/lib/gitlab-api.ts
1550
- var gitlab_api_exports = {};
1551
- __export(gitlab_api_exports, {
1552
- GitLabAPI: () => GitLabAPI
1553
- });
1554
- var GitLabAPI;
1555
- var init_gitlab_api = __esm({
1556
- "src/lib/gitlab-api.ts"() {
1557
- "use strict";
1558
- init_esm_shims();
1559
- GitLabAPI = class {
1560
- config;
1561
- defaultTimeout = 3e4;
1562
- constructor(config) {
1563
- this.config = {
1564
- ...config,
1565
- timeout: config.timeout || this.defaultTimeout
1566
- };
1567
- }
1568
- /**
1569
- * 验证 Token 是否有效
1570
- */
1571
- async authenticate() {
1572
- try {
1573
- const response = await this.request("/user");
1574
- return response.ok;
1575
- } catch (error) {
1576
- return false;
1577
- }
1578
- }
1579
- /**
1580
- * 列出项目的所有 Tags
1581
- */
1582
- async listTags(projectPath) {
1583
- try {
1584
- const encodedPath = this.encodeProjectPath(projectPath);
1585
- const response = await this.request(`/projects/${encodedPath}/repository/tags?per_page=100`);
1586
- if (!response.ok) {
1587
- throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
1588
- }
1589
- const tags = await response.json();
1590
- return tags.sort((a, b) => {
1591
- return this.compareVersionStrings(b.name, a.name);
1592
- });
1593
- } catch (error) {
1594
- throw new Error(`\u83B7\u53D6 Tags \u5931\u8D25: ${error}`);
1595
- }
1596
- }
1597
- /**
1598
- * 列出项目的所有 Branches
1599
- */
1600
- async listBranches(projectPath) {
1601
- try {
1602
- const encodedPath = this.encodeProjectPath(projectPath);
1603
- const response = await this.request(`/projects/${encodedPath}/repository/branches?per_page=100`);
1604
- if (!response.ok) {
1605
- throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
1606
- }
1607
- const branches = await response.json();
1608
- return branches.sort((a, b) => {
1609
- if (a.default) return -1;
1610
- if (b.default) return 1;
1611
- return a.name.localeCompare(b.name);
1612
- });
1613
- } catch (error) {
1614
- throw new Error(`\u83B7\u53D6 Branches \u5931\u8D25: ${error}`);
1615
- }
1616
- }
1617
- /**
1618
- * 验证 Tag 是否存在
1619
- */
1620
- async validateTag(projectPath, tag) {
1621
- try {
1622
- const tags = await this.listTags(projectPath);
1623
- return tags.some((t) => t.name === tag);
1624
- } catch {
1625
- return false;
1626
- }
1627
- }
1628
- /**
1629
- * 验证 Branch 是否存在
1630
- */
1631
- async validateBranch(projectPath, branch) {
1632
- try {
1633
- const branches = await this.listBranches(projectPath);
1634
- return branches.some((b) => b.name === branch);
1635
- } catch {
1636
- return false;
1637
- }
1638
- }
1639
- /**
1640
- * 获取项目信息
1641
- */
1642
- async getProject(projectPath) {
1643
- try {
1644
- const encodedPath = this.encodeProjectPath(projectPath);
1645
- const response = await this.request(`/projects/${encodedPath}`);
1646
- if (!response.ok) {
1647
- return null;
1648
- }
1649
- return await response.json();
1650
- } catch {
1651
- return null;
1652
- }
1653
- }
1654
- /**
1655
- * 获取指定 Tag 的 commit 信息
1656
- */
1657
- async getTagCommit(projectPath, tag) {
1658
- try {
1659
- const tags = await this.listTags(projectPath);
1660
- const targetTag = tags.find((t) => t.name === tag);
1661
- return targetTag?.commit.id || null;
1662
- } catch {
1663
- return null;
1664
- }
1665
- }
1666
- /**
1667
- * 获取指定 Branch 的最新 commit 信息
1668
- */
1669
- async getBranchCommit(projectPath, branch) {
1670
- try {
1671
- const encodedPath = this.encodeProjectPath(projectPath);
1672
- const response = await this.request(
1673
- `/projects/${encodedPath}/repository/branches/${encodeURIComponent(branch)}`
1674
- );
1675
- if (!response.ok) {
1676
- return null;
1677
- }
1678
- const branchInfo = await response.json();
1679
- return branchInfo.commit.id;
1680
- } catch {
1681
- return null;
1682
- }
1683
- }
1684
- /**
1685
- * 对比两个版本之间的差异
1686
- */
1687
- async compareVersions(projectPath, from, to) {
1688
- try {
1689
- const encodedPath = this.encodeProjectPath(projectPath);
1690
- const response = await this.request(
1691
- `/projects/${encodedPath}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
1692
- );
1693
- if (!response.ok) {
1694
- return null;
1695
- }
1696
- return await response.json();
1697
- } catch {
1698
- return null;
1699
- }
1700
- }
1701
- /**
1702
- * 比较两个版本号(用于排序)
1703
- * 返回值: -1 (v1 < v2), 0 (v1 == v2), 1 (v1 > v2)
1704
- */
1705
- compareVersionStrings(v1, v2) {
1706
- const version1 = v1.replace(/^v/, "");
1707
- const version2 = v2.replace(/^v/, "");
1708
- const parts1 = version1.split(".").map((p) => parseInt(p, 10) || 0);
1709
- const parts2 = version2.split(".").map((p) => parseInt(p, 10) || 0);
1710
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
1711
- const p1 = parts1[i] || 0;
1712
- const p2 = parts2[i] || 0;
1713
- if (p1 > p2) return 1;
1714
- if (p1 < p2) return -1;
1715
- }
1716
- return 0;
1717
- }
1718
- /**
1719
- * 获取最新的版本(优先 Tag,否则 latest commit)
1720
- */
1721
- async getLatestVersion(projectPath) {
1722
- try {
1723
- const tags = await this.listTags(projectPath);
1724
- const branches = await this.listBranches(projectPath);
1725
- if (tags.length > 0) {
1726
- const latestTag = tags[0];
1727
- return {
1728
- type: "tag",
1729
- name: latestTag.name,
1730
- commit: latestTag.commit.id
1731
- };
1732
- }
1733
- const defaultBranch = branches.find((b) => b.default);
1734
- if (defaultBranch) {
1735
- return {
1736
- type: "commit",
1737
- name: defaultBranch.name,
1738
- commit: defaultBranch.commit.id
1739
- };
1740
- }
1741
- return null;
1742
- } catch {
1743
- return null;
1744
- }
1745
- }
1746
- /**
1747
- * 解析项目路径
1748
- * 从 Git URL 中提取项目路径
1749
- */
1750
- static parseProjectPath(repository) {
1751
- let path21 = repository;
1752
- if (path21.startsWith("git@")) {
1753
- path21 = path21.replace(/^git@/, "");
1754
- const colonIndex = path21.indexOf(":");
1755
- if (colonIndex !== -1) {
1756
- path21 = path21.substring(colonIndex + 1);
1757
- }
1758
- } else {
1759
- path21 = path21.replace(/^https?:\/\//, "");
1760
- const parts = path21.split("/");
1761
- if (parts.length > 1) {
1762
- path21 = parts.slice(1).join("/");
1763
- }
1764
- }
1765
- path21 = path21.replace(/\.git$/, "");
1766
- return path21;
1767
- }
1768
- /**
1769
- * 编码项目路径用于 API 请求
1770
- */
1771
- encodeProjectPath(projectPath) {
1772
- return encodeURIComponent(projectPath).replace(/%2F/g, "%2F");
1773
- }
1774
- /**
1775
- * 发送 HTTP 请求
1776
- */
1777
- async request(endpoint, options = {}) {
1778
- const url = `${this.config.baseUrl}/api/v4${endpoint}`;
1779
- const headers = {
1780
- "PRIVATE-TOKEN": this.config.accessToken,
1781
- "Content-Type": "application/json",
1782
- ...options.headers
1783
- };
1784
- const controller = new AbortController();
1785
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
1786
- try {
1787
- const response = await fetch(url, {
1788
- ...options,
1789
- headers,
1790
- signal: controller.signal
1791
- });
1792
- return response;
1793
- } catch (error) {
1794
- if (error.name === "AbortError") {
1795
- throw new Error("\u8BF7\u6C42\u8D85\u65F6");
1796
- }
1797
- throw error;
1798
- } finally {
1799
- clearTimeout(timeoutId);
1800
- }
1801
- }
1802
- /**
1803
- * 获取默认分支
1804
- */
1805
- async getDefaultBranch(projectPath) {
1806
- try {
1807
- const project = await this.getProject(projectPath);
1808
- return project?.default_branch || null;
1809
- } catch {
1810
- return null;
1811
- }
1812
- }
1813
- };
1814
- }
1815
- });
1719
+ ## \u9879\u76EE\u914D\u7F6E
1816
1720
 
1817
- // src/commands/init.ts
1818
- import { Command } from "commander";
1819
- import inquirer from "inquirer";
1820
- import path7 from "path";
1821
- import fs3 from "fs-extra";
1822
- import os3 from "os";
1823
- import { Listr } from "listr2";
1824
- async function generateTechStack(projectPath) {
1825
- const content = `# \u6280\u672F\u6808
1721
+ | \u914D\u7F6E\u9879 | \u503C |
1722
+ |-------|------|
1723
+ | \u9879\u76EE\u540D\u79F0 | ${config.projectName} |
1724
+ | Group ID | ${config.groupId} |
1725
+ | \u6839\u9879\u76EE\u540D | ${config.rootProjectName} |
1726
+ | \u6A21\u5757 | ${config.modules.map((m) => m.name).join(", ")} |
1727
+ | Java \u7248\u672C | ${config.javaVersion} |
1728
+ | \u6784\u5EFA\u5DE5\u5177 | ${config.buildTool === "gradle" ? "Gradle" : "Maven"} |
1826
1729
 
1827
1730
  ## \u540E\u7AEF\u6280\u672F\u6808
1828
1731
 
1829
1732
  | \u7EC4\u4EF6 | \u6280\u672F\u9009\u578B | \u7248\u672C | \u8BF4\u660E |
1830
1733
  |------|---------|------|------|
1831
- | \u8BED\u8A00 | Java | 17 | LTS \u7248\u672C |
1734
+ | \u8BED\u8A00 | Java | ${config.javaVersion} | LTS \u7248\u672C |
1832
1735
  | \u6846\u67B6 | Spring Boot | 3.2 | \u73B0\u4EE3\u5316 Java \u6846\u67B6 |
1833
1736
  | \u6784\u5EFA\u5DE5\u5177 | Gradle | 8.x | \u5FEB\u901F\u3001\u7075\u6D3B\u7684\u6784\u5EFA\u5DE5\u5177 |
1834
1737
  | ORM | MyBatis Plus | 3.5 | \u589E\u5F3A MyBatis\uFF0C\u7B80\u5316 CRUD |
@@ -1836,7 +1739,7 @@ async function generateTechStack(projectPath) {
1836
1739
  | \u7F13\u5B58 | Redis | 7.x | \u7F13\u5B58\u548C\u4F1A\u8BDD\u5B58\u50A8 |
1837
1740
  | \u6587\u6863 | SpringDoc OpenAPI | 2.3 | API \u6587\u6863\u81EA\u52A8\u751F\u6210 |
1838
1741
 
1839
- ## \u524D\u7AEF\u6280\u672F\u6808
1742
+ ${config.includeFrontend ? `## \u524D\u7AEF\u6280\u672F\u6808
1840
1743
 
1841
1744
  | \u7EC4\u4EF6 | \u6280\u672F\u9009\u578B | \u7248\u672C | \u8BF4\u660E |
1842
1745
  |------|---------|------|------|
@@ -1847,7 +1750,7 @@ async function generateTechStack(projectPath) {
1847
1750
  | \u56FE\u6807 | Lucide React | latest | \u4E00\u81F4\u6027\u56FE\u6807\u5E93 |
1848
1751
  | \u72B6\u6001\u7BA1\u7406 | React Context + Hooks | - | \u8F7B\u91CF\u7EA7\u72B6\u6001\u7BA1\u7406 |
1849
1752
  | \u8868\u5355 | React Hook Form | 7.x | \u9AD8\u6027\u80FD\u8868\u5355 |
1850
- | \u6570\u636E\u9A8C\u8BC1 | Zod | 3.x | TypeScript \u4F18\u5148\u7684\u9A8C\u8BC1\u5E93 |
1753
+ | \u6570\u636E\u9A8C\u8BC1 | Zod | 3.x | TypeScript \u4F18\u5148\u7684\u9A8C\u8BC1\u5E93 |` : ""}
1851
1754
 
1852
1755
  ## \u5F00\u53D1\u5DE5\u5177
1853
1756
 
@@ -1870,6 +1773,7 @@ async function generateTechStack(projectPath) {
1870
1773
  - API \u8DEF\u5F84\u4F7F\u7528 kebab-case
1871
1774
  - \u7C7B\u540D\u4F7F\u7528 PascalCase
1872
1775
  - \u65B9\u6CD5\u540D\u4F7F\u7528 camelCase
1776
+ - \u5305\u8DEF\u5F84: \`${config.groupId}.<module-name>\`
1873
1777
 
1874
1778
  ### \u524D\u7AEF\u89C4\u8303
1875
1779
 
@@ -1890,26 +1794,19 @@ async function generateTechStack(projectPath) {
1890
1794
 
1891
1795
  \`\`\`
1892
1796
  backend/
1893
- \u251C\u2500\u2500 src/main/java/com/example/demo/
1894
- \u2502 \u251C\u2500\u2500 controller/ # API \u63A7\u5236\u5668
1895
- \u2502 \u251C\u2500\u2500 service/ # \u4E1A\u52A1\u903B\u8F91
1896
- \u2502 \u251C\u2500\u2500 mapper/ # \u6570\u636E\u8BBF\u95EE
1897
- \u2502 \u251C\u2500\u2500 entity/ # \u6570\u636E\u6A21\u578B
1898
- \u2502 \u251C\u2500\u2500 dto/ # \u6570\u636E\u4F20\u8F93\u5BF9\u8C61
1899
- \u2502 \u251C\u2500\u2500 config/ # \u914D\u7F6E\u7C7B
1900
- \u2502 \u2514\u2500\u2500 util/ # \u5DE5\u5177\u7C7B
1797
+ ${backendStructure}
1901
1798
  \u251C\u2500\u2500 src/main/resources/
1902
1799
  \u2502 \u251C\u2500\u2500 mapper/ # MyBatis XML
1903
1800
  \u2502 \u2514\u2500\u2500 application.yml # \u914D\u7F6E\u6587\u4EF6
1904
1801
  \u2514\u2500\u2500 src/test/ # \u6D4B\u8BD5\u4EE3\u7801
1905
1802
 
1906
- frontend/
1803
+ ${config.includeFrontend ? `frontend/
1907
1804
  \u251C\u2500\u2500 src/
1908
1805
  \u2502 \u251C\u2500\u2500 app/ # Next.js App Router
1909
1806
  \u2502 \u251C\u2500\u2500 components/ # React \u7EC4\u4EF6
1910
1807
  \u2502 \u251C\u2500\u2500 lib/ # \u5DE5\u5177\u5E93
1911
1808
  \u2502 \u2514\u2500\u2500 types/ # TypeScript \u7C7B\u578B
1912
- \u2514\u2500\u2500 public/ # \u9759\u6001\u8D44\u6E90
1809
+ \u2514\u2500\u2500 public/ # \u9759\u6001\u8D44\u6E90` : ""}
1913
1810
 
1914
1811
  docs/
1915
1812
  \u251C\u2500\u2500 prd-docs/ # PRD \u9700\u6C42\u6587\u6863
@@ -1918,20 +1815,28 @@ docs/
1918
1815
  \u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
1919
1816
  \`\`\`
1920
1817
  `;
1921
- await FileUtils.write(path7.join(projectPath, "TECH_STACK.md"), content);
1818
+ await FileUtils.write(path6.join(projectPath, "TECH_STACK.md"), content);
1922
1819
  }
1923
- async function generateConventions(projectPath) {
1820
+ async function generateConventions(projectPath, config) {
1924
1821
  const content = `# \u5F00\u53D1\u89C4\u8303
1925
1822
 
1823
+ ## \u5305\u8DEF\u5F84\u89C4\u8303
1824
+
1825
+ \u6240\u6709\u540E\u7AEF\u4EE3\u7801\u7684\u5305\u8DEF\u5F84\u9075\u5FAA: \`${config.groupId}.<module-name>\`
1826
+
1827
+ \u793A\u4F8B:
1828
+ ${config.modules.map((m) => `- \`${m.packagePath}\``).join("\n")}
1829
+
1830
+
1926
1831
  ## \u540E\u7AEF\u5F00\u53D1\u89C4\u8303
1927
1832
 
1928
1833
  ### 1. \u5206\u5C42\u67B6\u6784
1929
1834
 
1930
- \`\`\`
1835
+ \`
1931
1836
  Controller \u2192 Service \u2192 Mapper \u2192 Database
1932
1837
  \u2193 \u2193
1933
1838
  DTO Entity
1934
- \`\`\`
1839
+ \`
1935
1840
 
1936
1841
  **\u804C\u8D23\u5212\u5206:**
1937
1842
  - **Controller**: \u63A5\u6536 HTTP \u8BF7\u6C42\uFF0C\u53C2\u6570\u6821\u9A8C\uFF0C\u8C03\u7528 Service
@@ -1954,13 +1859,13 @@ Controller \u2192 Service \u2192 Mapper \u2192 Database
1954
1859
 
1955
1860
  **RESTful \u98CE\u683C:**
1956
1861
 
1957
- \`\`\`
1862
+ \`
1958
1863
  GET /api/users # \u5217\u8868
1959
1864
  GET /api/users/{id} # \u8BE6\u60C5
1960
1865
  POST /api/users # \u521B\u5EFA
1961
1866
  PUT /api/users/{id} # \u66F4\u65B0
1962
1867
  DELETE /api/users/{id} # \u5220\u9664
1963
- \`\`\`
1868
+ \`
1964
1869
 
1965
1870
  **\u7EDF\u4E00\u54CD\u5E94\u683C\u5F0F:**
1966
1871
 
@@ -2221,7 +2126,7 @@ test('renders user name', () => {
2221
2126
  });
2222
2127
  \`\`\`
2223
2128
  `;
2224
- await FileUtils.write(path7.join(projectPath, "CONVENTIONS.md"), content);
2129
+ await FileUtils.write(path6.join(projectPath, "CONVENTIONS.md"), content);
2225
2130
  }
2226
2131
  async function generateAIMemory(projectPath, projectName) {
2227
2132
  const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
@@ -2280,7 +2185,7 @@ async function generateAIMemory(projectPath, projectName) {
2280
2185
  | Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
2281
2186
  |--------|------|---------|------|
2282
2187
  `;
2283
- await FileUtils.write(path7.join(projectPath, "AI_MEMORY.md"), content);
2188
+ await FileUtils.write(path6.join(projectPath, "AI_MEMORY.md"), content);
2284
2189
  }
2285
2190
  async function generateSpecTemplate(projectPath) {
2286
2191
  const content = `# [\u529F\u80FD\u6807\u9898]
@@ -2344,82 +2249,10 @@ async function generateSpecTemplate(projectPath) {
2344
2249
  ----
2345
2250
  *\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
2346
2251
  `;
2347
- await FileUtils.write(path7.join(projectPath, "docs/specs/template.md"), content);
2348
- }
2349
- async function cloneBackendTemplate(projectPath, versionOptions) {
2350
- const templateRepo = process.env.TEMPLATE_REPO || "git@gitlab.yungu-inc.org:yungu-app/java-scaffold-template.git";
2351
- const backendPath = path7.join(projectPath, "backend");
2352
- try {
2353
- const { execa: e } = await import("execa");
2354
- const tempDir = path7.join(projectPath, ".template-temp");
2355
- if (versionOptions?.tag || versionOptions?.branch) {
2356
- const { userConfigManager: userConfigManager2 } = await Promise.resolve().then(() => (init_user_config(), user_config_exports));
2357
- const { GitLabAPI: GitLabAPI2 } = await Promise.resolve().then(() => (init_gitlab_api(), gitlab_api_exports));
2358
- const config = await userConfigManager2.getGitLabConfig();
2359
- if (config) {
2360
- const gitlabAPI = new GitLabAPI2(config);
2361
- const projectPathEncoded = GitLabAPI2.parseProjectPath(templateRepo);
2362
- if (versionOptions.tag) {
2363
- const isValid = await gitlabAPI.validateTag(projectPathEncoded, versionOptions.tag);
2364
- if (!isValid) {
2365
- logger.error(`\u540E\u7AEF\u6A21\u677F tag "${versionOptions.tag}" \u4E0D\u5B58\u5728`);
2366
- process.exit(1);
2367
- }
2368
- logger.info(`\u4F7F\u7528\u540E\u7AEF\u6A21\u677F tag: ${versionOptions.tag}`);
2369
- }
2370
- if (versionOptions.branch) {
2371
- const isValid = await gitlabAPI.validateBranch(projectPathEncoded, versionOptions.branch);
2372
- if (!isValid) {
2373
- logger.error(`\u540E\u7AEF\u6A21\u677F\u5206\u652F "${versionOptions.branch}" \u4E0D\u5B58\u5728`);
2374
- process.exit(1);
2375
- }
2376
- logger.info(`\u4F7F\u7528\u540E\u7AEF\u6A21\u677F\u5206\u652F: ${versionOptions.branch}`);
2377
- }
2378
- } else {
2379
- logger.warn("\u672A\u914D\u7F6E GitLab Token\uFF0C\u8DF3\u8FC7\u7248\u672C\u9A8C\u8BC1");
2380
- }
2381
- }
2382
- logger.info(`\u6B63\u5728\u514B\u9686\u540E\u7AEF\u6A21\u677F (${versionOptions?.tag || versionOptions?.branch || "latest"})...`);
2383
- const cloneArgs = ["clone", "--depth=1"];
2384
- if (versionOptions?.tag || versionOptions?.branch) {
2385
- cloneArgs.push("--branch", versionOptions.tag || versionOptions.branch);
2386
- }
2387
- cloneArgs.push(templateRepo, tempDir);
2388
- await e("git", cloneArgs, {
2389
- stdio: "inherit",
2390
- timeout: 6e4
2391
- });
2392
- const { stdout: commit } = await e("git", ["rev-parse", "HEAD"], {
2393
- cwd: tempDir,
2394
- stdio: "pipe"
2395
- });
2396
- const { stdout: tags } = await e("git", ["tag", "-l", "--sort=-v:refname"], {
2397
- cwd: tempDir,
2398
- stdio: "pipe"
2399
- });
2400
- const latestTag = tags.split("\n")[0] || void 0;
2401
- await fs3.copy(tempDir, backendPath, {
2402
- filter: (src) => !src.includes(".git")
2403
- });
2404
- await fs3.remove(tempDir);
2405
- const gitDir = path7.join(backendPath, ".git");
2406
- if (await FileUtils.exists(gitDir)) {
2407
- await FileUtils.remove(gitDir);
2408
- }
2409
- await initTemplateConfig(projectPath);
2410
- await updateTemplateVersion(projectPath, "backend", commit.trim(), {
2411
- tag: versionOptions?.tag || latestTag,
2412
- branch: versionOptions?.branch
2413
- });
2414
- } catch (error) {
2415
- logger.warn("\u514B\u9686\u540E\u7AEF\u6A21\u677F\u5931\u8D25\uFF0C\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
2416
- await FileUtils.ensureDir(path7.join(backendPath, "src/main/java/com/example"));
2417
- await FileUtils.ensureDir(path7.join(backendPath, "src/main/resources"));
2418
- await FileUtils.ensureDir(path7.join(backendPath, "src/test/java"));
2419
- }
2252
+ await FileUtils.write(path6.join(projectPath, "docs/specs/template.md"), content);
2420
2253
  }
2421
2254
  async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
2422
- const frontendPath = path7.join(projectPath, "frontend");
2255
+ const frontendPath = path6.join(projectPath, "frontend");
2423
2256
  const templates = getDefaultTemplates();
2424
2257
  const repository = templates.frontend.repository;
2425
2258
  try {
@@ -2430,7 +2263,7 @@ async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
2430
2263
  }
2431
2264
  const targetVersion = versionOptions.branch || versionOptions.tag || latestTag;
2432
2265
  logger.info(`\u6B63\u5728\u514B\u9686\u524D\u7AEF\u6A21\u677F (${targetVersion || "HEAD"})...`);
2433
- const tempDir = path7.join(os3.tmpdir(), `team-cli-fe-${Date.now()}`);
2266
+ const tempDir = path6.join(os2.tmpdir(), `team-cli-fe-${Date.now()}`);
2434
2267
  await FileUtils.ensureDir(tempDir);
2435
2268
  const cloneArgs = ["clone", "--depth", "1"];
2436
2269
  if (targetVersion) {
@@ -2450,12 +2283,201 @@ async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
2450
2283
  logger.success("\u524D\u7AEF\u6A21\u677F\u514B\u9686\u5B8C\u6210");
2451
2284
  } catch (error) {
2452
2285
  logger.warn("\u514B\u9686\u524D\u7AEF\u6A21\u677F\u5931\u8D25\uFF0C\u5C06\u521B\u5EFA\u57FA\u7840\u7ED3\u6784");
2453
- await FileUtils.ensureDir(path7.join(frontendPath, "src/app"));
2454
- await FileUtils.ensureDir(path7.join(frontendPath, "src/components"));
2286
+ await FileUtils.ensureDir(path6.join(frontendPath, "src/app"));
2287
+ await FileUtils.ensureDir(path6.join(frontendPath, "src/components"));
2288
+ }
2289
+ }
2290
+ async function createBackendFromConfig(projectPath, config) {
2291
+ const backendPath = path6.join(projectPath, "backend");
2292
+ logger.info(`\u6B63\u5728\u521B\u5EFA\u540E\u7AEF\u591A\u6A21\u5757\u7ED3\u6784...`);
2293
+ await FileUtils.ensureDir(backendPath);
2294
+ await createSettingsGradle(backendPath, config);
2295
+ await createRootBuildGradle(backendPath, config);
2296
+ for (const module of config.modules) {
2297
+ await createModuleFromConfig(backendPath, module, config);
2298
+ }
2299
+ if (config.modules.length > 0) {
2300
+ await createCommonModule(backendPath, config);
2301
+ }
2302
+ logger.success("\u540E\u7AEF\u7ED3\u6784\u521B\u5EFA\u5B8C\u6210");
2303
+ }
2304
+ async function createSettingsGradle(backendPath, config) {
2305
+ const lines = [];
2306
+ lines.push(`rootProject.name = '${config.rootProjectName}'`);
2307
+ lines.push("");
2308
+ const allModules = ["common", ...config.modules.map((m) => m.name)].sort();
2309
+ for (const module of allModules) {
2310
+ lines.push(`include '${module}'`);
2311
+ }
2312
+ await FileUtils.write(path6.join(backendPath, "settings.gradle"), lines.join("\n"));
2313
+ logger.info("settings.gradle \u5DF2\u521B\u5EFA");
2314
+ }
2315
+ async function createRootBuildGradle(backendPath, config) {
2316
+ const content = `plugins {
2317
+ id 'java' version '8' apply false
2318
+ }
2319
+
2320
+ allprojects {
2321
+ group = '${config.groupId}'
2322
+ version = '0.0.1-SNAPSHOT'
2323
+
2324
+ repositories {
2325
+ maven { url 'https://maven.aliyun.com/repository/public/' }
2326
+ }
2327
+ }
2328
+
2329
+ subprojects {
2330
+ apply plugin: 'java'
2331
+ apply plugin: 'org.springframework.boot'
2332
+ apply plugin: 'io.spring.dependency-management'
2333
+
2334
+ sourceCompatibility = '${config.javaVersion}'
2335
+ targetCompatibility = '${config.javaVersion}'
2336
+
2337
+ dependencies {
2338
+ implementation 'org.springframework.boot:spring-boot-starter-web'
2339
+ implementation 'org.springframework.boot:spring-boot-starter-logging'
2340
+ compileOnly "org.projectlombok:lombok"
2341
+ annotationProcessor "org.projectlombok:lombok"
2342
+ testCompileOnly "org.projectlombok:lombok"
2343
+ testAnnotationProcessor "org.projectlombok:lombok"
2344
+ }
2345
+ }
2346
+ `;
2347
+ await FileUtils.write(path6.join(backendPath, "build.gradle"), content);
2348
+ logger.info("build.gradle (root) \u5DF2\u521B\u5EFA");
2349
+ }
2350
+ async function createModuleFromConfig(backendPath, module, config) {
2351
+ const modulePath = path6.join(backendPath, module.name);
2352
+ const packagePath = module.packagePath.split(".");
2353
+ logger.info(`\u521B\u5EFA\u6A21\u5757: ${module.name} (${module.type})...`);
2354
+ const baseDirs = [
2355
+ "src/main/java",
2356
+ "src/main/resources",
2357
+ "src/test/java"
2358
+ ];
2359
+ for (const dir of baseDirs) {
2360
+ await FileUtils.ensureDir(path6.join(modulePath, dir));
2361
+ }
2362
+ let subDirs = [];
2363
+ if (module.type === "service") {
2364
+ subDirs = ["controller", "service", "mapper", "entity", "dto", "config"];
2365
+ } else if (module.type === "api") {
2366
+ subDirs = ["model", "constant", "enums", "exception"];
2367
+ } else {
2368
+ subDirs = ["util", "common", "config", "manager"];
2369
+ }
2370
+ let javaPath = path6.join(modulePath, "src", "main", "java");
2371
+ for (const subDir of subDirs) {
2372
+ await FileUtils.ensureDir(path6.join(javaPath, ...packagePath, subDir));
2455
2373
  }
2374
+ await createApplicationClass(
2375
+ path6.join(javaPath, ...packagePath, "config", `${module.applicationClass}.java`),
2376
+ module.packagePath,
2377
+ module.applicationClass
2378
+ );
2379
+ await createModuleBuildGradle(modulePath, module, config);
2380
+ logger.success(`\u6A21\u5757 ${module.name} \u521B\u5EFA\u5B8C\u6210`);
2381
+ }
2382
+ async function createApplicationClass(filePath, packagePath, className) {
2383
+ const content = `package ${packagePath}.config;
2384
+
2385
+ import lombok.extern.slf4j.Slf4j;
2386
+ import org.springframework.boot.SpringApplication;
2387
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
2388
+ import org.springframework.context.ApplicationContext;
2389
+ import org.springframework.context.annotation.ComponentScan;
2390
+
2391
+ @SpringBootApplication
2392
+ @ComponentScan("${packagePath}")
2393
+ @Slf4j
2394
+ public class ${className} {
2395
+
2396
+ public static void main(String[] args) {
2397
+ ApplicationContext context = SpringApplication.run(${className}.class, args);
2398
+ log.info("${className} \u542F\u52A8\u6210\u529F!");
2399
+ }
2400
+ }
2401
+ `;
2402
+ await FileUtils.ensureDir(path6.dirname(filePath));
2403
+ await FileUtils.write(filePath, content);
2404
+ }
2405
+ async function createModuleBuildGradle(modulePath, module, config) {
2406
+ let applyPlugins = `plugins {
2407
+ id 'java-library'
2408
+ id 'org.springframework.boot'
2409
+ id 'io.spring.dependency-management'
2410
+ }
2411
+
2412
+ group = '${config.groupId}'
2413
+ version = '0.0.1-SNAPSHOT'
2414
+
2415
+ java {
2416
+ sourceCompatibility = '${config.javaVersion}'
2417
+ targetCompatibility = '${config.javaVersion}'
2418
+ }
2419
+
2420
+ repositories {
2421
+ maven { url 'https://maven.aliyun.com/repository/public/' }
2422
+ }
2423
+ `;
2424
+ let dependencies = "";
2425
+ if (module.type === "service") {
2426
+ dependencies = `
2427
+ dependencies {
2428
+ implementation project(":common")
2429
+ implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
2430
+ implementation 'com.alibaba:druid-spring-boot-starter'
2431
+ implementation 'org.springframework.boot:spring-boot-starter-validation'
2432
+ compileOnly "org.projectlombok:lombok"
2433
+ annotationProcessor "org.projectlombok:lombok"
2434
+ testCompileOnly "org.projectlombok:lombok"
2435
+ testAnnotationProcessor "org.projectlombok:lombok"
2436
+ }
2437
+ `;
2438
+ } else if (module.type === "api") {
2439
+ dependencies = `
2440
+ dependencies {
2441
+ compileOnly 'org.springframework.boot:spring-boot-starter'
2442
+ compileOnly "org.projectlombok:lombok"
2443
+ annotationProcessor "org.projectlombok:lombok"
2444
+ }
2445
+ `;
2446
+ } else {
2447
+ dependencies = `
2448
+ dependencies {
2449
+ implementation 'org.springframework.boot:spring-boot-starter'
2450
+ implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
2451
+ implementation 'org.apache.commons:commons-lang3:3.12.0'
2452
+ compileOnly "org.projectlombok:lombok"
2453
+ annotationProcessor "org.projectlombok:lombok"
2454
+ }
2455
+ `;
2456
+ }
2457
+ await FileUtils.write(path6.join(modulePath, "build.gradle"), applyPlugins + dependencies);
2458
+ }
2459
+ async function createCommonModule(backendPath, config) {
2460
+ const modulePath = path6.join(backendPath, "common");
2461
+ const packagePath = config.groupId.split(".");
2462
+ const subDirs = ["util", "common", "config", "manager", "constant", "enums", "exception"];
2463
+ logger.info("\u521B\u5EFA\u516C\u5171\u6A21\u5757: common...");
2464
+ for (const dir of ["src/main/java", "src/main/resources", "src/test/java"]) {
2465
+ await FileUtils.ensureDir(path6.join(modulePath, dir));
2466
+ }
2467
+ let javaPath = path6.join(modulePath, "src", "main", "java");
2468
+ for (const subDir of subDirs) {
2469
+ await FileUtils.ensureDir(path6.join(javaPath, ...packagePath, subDir));
2470
+ }
2471
+ await createModuleBuildGradle(modulePath, {
2472
+ name: "common",
2473
+ type: "common",
2474
+ packagePath: `${config.groupId}.common`,
2475
+ applicationClass: ""
2476
+ }, config);
2477
+ logger.success("\u516C\u5171\u6A21\u5757 common \u521B\u5EFA\u5B8C\u6210");
2456
2478
  }
2457
2479
  async function generateDockerFiles(projectPath, projectName) {
2458
- const backendPath = path7.join(projectPath, "backend");
2480
+ const backendPath = path6.join(projectPath, "backend");
2459
2481
  if (!await FileUtils.exists(backendPath)) {
2460
2482
  logger.warn("\u672A\u627E\u5230 backend \u76EE\u5F55\uFF0C\u8DF3\u8FC7 Docker \u914D\u7F6E\u751F\u6210");
2461
2483
  return;
@@ -2493,9 +2515,9 @@ docker push \${REGISTRY}/\${PROJECT_NAME}:latest
2493
2515
 
2494
2516
  echo "\u2705 \u90E8\u7F72\u5B8C\u6210! \u955C\u50CF\u5730\u5740: \${REGISTRY}/\${IMAGE_TAG}"
2495
2517
  `;
2496
- await fs3.writeFile(path7.join(backendPath, "Dockerfile"), dockerfile);
2497
- await fs3.writeFile(path7.join(backendPath, "deploy.sh"), deploySh);
2498
- await fs3.chmod(path7.join(backendPath, "deploy.sh"), 493);
2518
+ await fs3.writeFile(path6.join(backendPath, "Dockerfile"), dockerfile);
2519
+ await fs3.writeFile(path6.join(backendPath, "deploy.sh"), deploySh);
2520
+ await fs3.chmod(path6.join(backendPath, "deploy.sh"), 493);
2499
2521
  }
2500
2522
  async function initGit(projectPath, projectName) {
2501
2523
  try {
@@ -2524,36 +2546,21 @@ var init_init = __esm({
2524
2546
  init_module_registry();
2525
2547
  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) => {
2526
2548
  try {
2527
- if (!projectName) {
2528
- const answers = await inquirer.prompt([
2529
- {
2530
- type: "input",
2531
- name: "projectName",
2532
- message: "\u8BF7\u8F93\u5165\u9879\u76EE\u540D\u79F0:",
2533
- default: "my-project",
2534
- validate: (input) => {
2535
- if (!/^[a-z0-9-]+$/.test(input)) {
2536
- return "\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
2537
- }
2538
- return true;
2539
- }
2540
- }
2541
- ]);
2542
- projectName = answers.projectName;
2543
- }
2544
- if (!StringUtils.validateProjectName(projectName)) {
2545
- logger.error("\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26");
2546
- process.exit(1);
2547
- }
2549
+ const config = await collectProjectConfig(projectName);
2548
2550
  logger.header("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6 - \u9879\u76EE\u521D\u59CB\u5316");
2549
2551
  logger.newLine();
2552
+ logger.info(`\u9879\u76EE\u7C7B\u578B: ${config.projectType === "multi" ? "\u591A\u6A21\u5757\u9879\u76EE" : "\u5355\u6A21\u5757\u9879\u76EE"}`);
2553
+ logger.info(`Group ID: ${config.groupId}`);
2554
+ logger.info(`\u6A21\u5757: ${config.modules.map((m) => m.name).join(", ")}`);
2555
+ logger.info(`Java \u7248\u672C: ${config.javaVersion}`);
2556
+ logger.newLine();
2550
2557
  const hasClaude = await claudeAI.checkInstalled();
2551
2558
  if (!hasClaude) {
2552
2559
  logger.error("\u672A\u68C0\u6D4B\u5230 Claude CLI");
2553
2560
  logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
2554
2561
  process.exit(1);
2555
2562
  }
2556
- const projectPath = path7.resolve(options.dir, projectName);
2563
+ const projectPath = path6.resolve(options.dir, config.projectName);
2557
2564
  const availableModules = ModuleManager.getAvailableModules();
2558
2565
  const { selectedModules } = await inquirer.prompt([
2559
2566
  {
@@ -2577,48 +2584,45 @@ var init_init = __esm({
2577
2584
  title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55\u7ED3\u6784",
2578
2585
  task: async () => {
2579
2586
  await FileUtils.ensureDir(projectPath);
2580
- await FileUtils.ensureDir(path7.join(projectPath, "docs/specs"));
2581
- await FileUtils.ensureDir(path7.join(projectPath, "docs/prd-docs"));
2582
- await FileUtils.ensureDir(path7.join(projectPath, "docs/api"));
2583
- await FileUtils.ensureDir(path7.join(projectPath, "docs/sessions"));
2587
+ await FileUtils.ensureDir(path6.join(projectPath, "docs/specs"));
2588
+ await FileUtils.ensureDir(path6.join(projectPath, "docs/prd-docs"));
2589
+ await FileUtils.ensureDir(path6.join(projectPath, "docs/api"));
2590
+ await FileUtils.ensureDir(path6.join(projectPath, "docs/sessions"));
2584
2591
  }
2585
2592
  },
2586
2593
  {
2587
2594
  title: "\u751F\u6210\u6280\u672F\u6587\u6863 (Tech Stack, Conventions, Memory)",
2588
2595
  task: async () => {
2589
- await generateTechStack(projectPath);
2590
- await generateConventions(projectPath);
2591
- await generateAIMemory(projectPath, projectName);
2596
+ await generateTechStack(projectPath, config);
2597
+ await generateConventions(projectPath, config);
2598
+ await generateAIMemory(projectPath, config.projectName);
2592
2599
  await generateSpecTemplate(projectPath);
2593
2600
  }
2594
2601
  },
2595
2602
  {
2596
- title: "\u514B\u9686\u540E\u7AEF\u6A21\u677F",
2603
+ title: "\u521B\u5EFA\u540E\u7AEF\u7ED3\u6784",
2597
2604
  task: async () => {
2598
- const backendTag = options.backendTag || options.tag;
2599
- const backendBranch = options.backendBranch;
2600
- await cloneBackendTemplate(projectPath, {
2601
- tag: backendTag,
2602
- branch: backendBranch
2603
- });
2605
+ await createBackendFromConfig(projectPath, config);
2604
2606
  }
2605
2607
  },
2606
- {
2607
- title: "\u514B\u9686\u524D\u7AEF\u6A21\u677F",
2608
- task: async () => {
2609
- const frontendTag = options.frontendTag || options.tag;
2610
- const frontendBranch = options.frontendBranch;
2611
- await cloneFrontendTemplate(projectPath, {
2612
- tag: frontendTag,
2613
- branch: frontendBranch
2614
- });
2608
+ ...config.includeFrontend ? [
2609
+ {
2610
+ title: "\u514B\u9686\u524D\u7AEF\u6A21\u677F",
2611
+ task: async () => {
2612
+ const frontendTag = options.frontendTag || options.tag;
2613
+ const frontendBranch = options.frontendBranch;
2614
+ await cloneFrontendTemplate(projectPath, {
2615
+ tag: frontendTag,
2616
+ branch: frontendBranch
2617
+ });
2618
+ }
2615
2619
  }
2616
- },
2620
+ ] : [],
2617
2621
  {
2618
2622
  title: "\u6CE8\u5165\u9009\u5B9A\u7684\u901A\u7528\u6A21\u5757",
2619
2623
  task: async (ctx) => {
2620
2624
  if (selectedModules.length === 0) return;
2621
- const templatesDir = path7.resolve(FileUtils.getDirName(import.meta.url), "../templates");
2625
+ const templatesDir = path6.resolve(FileUtils.getDirName(import.meta.url), "../templates");
2622
2626
  ctx.addedFiles = [];
2623
2627
  for (const moduleId of selectedModules) {
2624
2628
  const files = await ModuleManager.injectModule(projectPath, moduleId, templatesDir);
@@ -2630,7 +2634,7 @@ var init_init = __esm({
2630
2634
  {
2631
2635
  title: "\u751F\u6210 Docker \u90E8\u7F72\u914D\u7F6E",
2632
2636
  task: async () => {
2633
- await generateDockerFiles(projectPath, projectName);
2637
+ await generateDockerFiles(projectPath, config.projectName);
2634
2638
  }
2635
2639
  }
2636
2640
  ] : []
@@ -2725,7 +2729,7 @@ var init_init = __esm({
2725
2729
  // src/commands/breakdown.ts
2726
2730
  import { Command as Command2 } from "commander";
2727
2731
  import inquirer2 from "inquirer";
2728
- import path8 from "path";
2732
+ import path7 from "path";
2729
2733
  import { Listr as Listr2 } from "listr2";
2730
2734
  function mergeMilestones(original, milestones) {
2731
2735
  const milestoneHeader = "## \u91CC\u7A0B\u7891 (Milestones)";
@@ -2892,10 +2896,10 @@ var init_breakdown = __esm({
2892
2896
  choices: ctx.specs
2893
2897
  }
2894
2898
  ]);
2895
- ctx.selectedFile = path8.join("docs/specs", selectedFile);
2899
+ ctx.selectedFile = path7.join("docs/specs", selectedFile);
2896
2900
  return;
2897
2901
  }
2898
- const fullPath = specFile.startsWith("docs/specs/") ? specFile : path8.join("docs/specs", specFile);
2902
+ const fullPath = specFile.startsWith("docs/specs/") ? specFile : path7.join("docs/specs", specFile);
2899
2903
  const exists = await FileUtils.exists(fullPath);
2900
2904
  if (!exists) {
2901
2905
  throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
@@ -2966,7 +2970,7 @@ var init_breakdown = __esm({
2966
2970
  // src/commands/dev.ts
2967
2971
  import { Command as Command3 } from "commander";
2968
2972
  import inquirer3 from "inquirer";
2969
- import path9 from "path";
2973
+ import path8 from "path";
2970
2974
  async function selectSpec() {
2971
2975
  logger.step("\u6B65\u9AA4 1/3: \u9009\u62E9 spec \u6587\u4EF6...");
2972
2976
  logger.newLine();
@@ -2982,7 +2986,7 @@ async function selectSpec() {
2982
2986
  }
2983
2987
  const specs = [];
2984
2988
  for (let i = 0; i < specFiles.length; i++) {
2985
- const file = path9.join(specDir, specFiles[i]);
2989
+ const file = path8.join(specDir, specFiles[i]);
2986
2990
  const spec = await FileUtils.read(file);
2987
2991
  const status = SpecUtils.parseSpecStatus(spec);
2988
2992
  const dependencies = parseDependencies(spec);
@@ -3329,8 +3333,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
3329
3333
  const sessionDir = "docs/sessions";
3330
3334
  await FileUtils.ensureDir(sessionDir);
3331
3335
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3332
- const specName = path9.basename(specFile, ".md");
3333
- const logFile = path9.join(sessionDir, `${timestamp}_${specName}.md`);
3336
+ const specName = path8.basename(specFile, ".md");
3337
+ const logFile = path8.join(sessionDir, `${timestamp}_${specName}.md`);
3334
3338
  const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
3335
3339
 
3336
3340
  **\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
@@ -3485,7 +3489,7 @@ var init_dev = __esm({
3485
3489
  // src/commands/add-feature.ts
3486
3490
  import { Command as Command4 } from "commander";
3487
3491
  import inquirer4 from "inquirer";
3488
- import path10 from "path";
3492
+ import path9 from "path";
3489
3493
  import { Listr as Listr3 } from "listr2";
3490
3494
  async function addFeatureFromPrd(featureName, _featureSlug, prdOutputFile) {
3491
3495
  const { prdPath } = await inquirer4.prompt([
@@ -3514,7 +3518,7 @@ async function addFeatureFromPrd(featureName, _featureSlug, prdOutputFile) {
3514
3518
  const specs = files.filter((f) => !f.includes("template"));
3515
3519
  ctx.completedSpecs = [];
3516
3520
  for (const file of specs) {
3517
- const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
3521
+ const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3518
3522
  if (status === "\u5DF2\u5B8C\u6210") {
3519
3523
  ctx.completedSpecs.push(file.replace(".md", ""));
3520
3524
  }
@@ -3575,7 +3579,7 @@ async function addFeatureSimple(featureName, _featureSlug, prdOutputFile) {
3575
3579
  const specs = files.filter((f) => !f.includes("template"));
3576
3580
  ctx.completedSpecs = [];
3577
3581
  for (const file of specs) {
3578
- const status = await SpecUtils.getSpecStatus(path10.join(specDir, file));
3582
+ const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
3579
3583
  if (status === "\u5DF2\u5B8C\u6210") {
3580
3584
  ctx.completedSpecs.push(file.replace(".md", ""));
3581
3585
  }
@@ -3661,7 +3665,7 @@ async function buildProjectContext() {
3661
3665
  const files = await FileUtils.findFiles("*.md", "docs/specs");
3662
3666
  const specs = files.filter((f) => !f.includes("template"));
3663
3667
  for (const file of specs) {
3664
- const status = await SpecUtils.getSpecStatus(path10.join("docs/specs", file));
3668
+ const status = await SpecUtils.getSpecStatus(path9.join("docs/specs", file));
3665
3669
  context.push(` - ${file.replace(".md", "")} [${status}]`);
3666
3670
  }
3667
3671
  }
@@ -3913,7 +3917,7 @@ var init_add_feature = __esm({
3913
3917
  process.exit(1);
3914
3918
  }
3915
3919
  const featureSlug = StringUtils.toKebabCase(featureName);
3916
- const prdFile = path10.join("docs/prd-docs", `${featureSlug}.md`);
3920
+ const prdFile = path9.join("docs/prd-docs", `${featureSlug}.md`);
3917
3921
  const prdExists = await FileUtils.exists(prdFile);
3918
3922
  if (prdExists) {
3919
3923
  logger.error(`PRD \u6587\u4EF6\u5DF2\u5B58\u5728: ${prdFile}`);
@@ -3950,12 +3954,12 @@ var init_add_feature = __esm({
3950
3954
 
3951
3955
  // src/commands/split-prd.ts
3952
3956
  import { Command as Command5 } from "commander";
3953
- import path11 from "path";
3957
+ import path10 from "path";
3954
3958
  import { Listr as Listr4 } from "listr2";
3955
3959
  async function processSinglePrd(prdFile) {
3956
- const baseName = path11.basename(prdFile, path11.extname(prdFile));
3960
+ const baseName = path10.basename(prdFile, path10.extname(prdFile));
3957
3961
  const featureSlug = StringUtils.toKebabCase(baseName);
3958
- const specFile = path11.join("docs/specs", `${featureSlug}.md`);
3962
+ const specFile = path10.join("docs/specs", `${featureSlug}.md`);
3959
3963
  const specExists = await FileUtils.exists(specFile);
3960
3964
  if (specExists) {
3961
3965
  logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
@@ -4019,7 +4023,7 @@ async function processMultiplePrds(prdFolder) {
4019
4023
  ctx.prdFiles = [];
4020
4024
  for (const ext of supportedExtensions) {
4021
4025
  const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
4022
- ctx.prdFiles.push(...files.map((f) => path11.join(prdFolder, f)));
4026
+ ctx.prdFiles.push(...files.map((f) => path10.join(prdFolder, f)));
4023
4027
  }
4024
4028
  if (ctx.prdFiles.length === 0) {
4025
4029
  throw new Error(
@@ -4037,7 +4041,7 @@ async function processMultiplePrds(prdFolder) {
4037
4041
  {
4038
4042
  title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
4039
4043
  task: async (ctx) => {
4040
- const screenshotDir = path11.join(prdFolder, "screenshots");
4044
+ const screenshotDir = path10.join(prdFolder, "screenshots");
4041
4045
  const dirExists = await FileUtils.exists(screenshotDir);
4042
4046
  if (!dirExists) {
4043
4047
  logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
@@ -4048,7 +4052,7 @@ async function processMultiplePrds(prdFolder) {
4048
4052
  ctx.screenshots = [];
4049
4053
  for (const ext of imageExtensions) {
4050
4054
  const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
4051
- ctx.screenshots.push(...files.map((f) => path11.join(screenshotDir, f)));
4055
+ ctx.screenshots.push(...files.map((f) => path10.join(screenshotDir, f)));
4052
4056
  }
4053
4057
  logger.success(`\u627E\u5230 ${ctx.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
4054
4058
  }
@@ -4059,8 +4063,8 @@ async function processMultiplePrds(prdFolder) {
4059
4063
  const entries = await FileUtils.findFiles("*/", prdFolder);
4060
4064
  ctx.demoRepos = [];
4061
4065
  for (const entry of entries) {
4062
- const dirPath = path11.join(prdFolder, entry);
4063
- const gitDir = path11.join(dirPath, ".git");
4066
+ const dirPath = path10.join(prdFolder, entry);
4067
+ const gitDir = path10.join(dirPath, ".git");
4064
4068
  const hasGit = await FileUtils.exists(gitDir);
4065
4069
  if (hasGit) {
4066
4070
  ctx.demoRepos.push(dirPath);
@@ -4388,7 +4392,7 @@ var init_split_prd = __esm({
4388
4392
  // src/commands/bugfix.ts
4389
4393
  import { Command as Command6 } from "commander";
4390
4394
  import inquirer5 from "inquirer";
4391
- import path12 from "path";
4395
+ import path11 from "path";
4392
4396
  import { Listr as Listr5 } from "listr2";
4393
4397
  function generateBugId() {
4394
4398
  const date = /* @__PURE__ */ new Date();
@@ -4415,7 +4419,7 @@ async function findRelatedSpec(description) {
4415
4419
  }
4416
4420
  const keywords = extractKeywords(description);
4417
4421
  for (const file of specs) {
4418
- const filePath = path12.join(specDir, file);
4422
+ const filePath = path11.join(specDir, file);
4419
4423
  const content = await FileUtils.read(filePath);
4420
4424
  for (const keyword of keywords) {
4421
4425
  if (content.toLowerCase().includes(keyword.toLowerCase())) {
@@ -4555,7 +4559,7 @@ var init_bugfix = __esm({
4555
4559
  const relatedSpec = await findRelatedSpec(answers.description);
4556
4560
  const bugfixDir = "docs/bugfixes";
4557
4561
  await FileUtils.ensureDir(bugfixDir);
4558
- const bugfixFile = path12.join(bugfixDir, `${timestamp}_${bugId}.md`);
4562
+ const bugfixFile = path11.join(bugfixDir, `${timestamp}_${bugId}.md`);
4559
4563
  const content = formatBugfixDocument({
4560
4564
  id: bugId,
4561
4565
  severity: answers.severity,
@@ -4631,7 +4635,7 @@ var init_bugfix = __esm({
4631
4635
  const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
4632
4636
  const hotfixDir = "docs/hotfixes";
4633
4637
  await FileUtils.ensureDir(hotfixDir);
4634
- const hotfixFile = path12.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
4638
+ const hotfixFile = path11.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
4635
4639
  const content = formatHotfixDocument({
4636
4640
  id: hotfixId,
4637
4641
  description: answers.description,
@@ -4687,7 +4691,7 @@ Temporary solution: ${answers.solution}`
4687
4691
  // src/commands/accept.ts
4688
4692
  import { Command as Command7 } from "commander";
4689
4693
  import inquirer6 from "inquirer";
4690
- import path13 from "path";
4694
+ import path12 from "path";
4691
4695
  async function selectSpec2(defaultSpec) {
4692
4696
  logger.step("\u6B65\u9AA4 1/4: \u9009\u62E9 spec \u6587\u4EF6...");
4693
4697
  logger.newLine();
@@ -4702,7 +4706,7 @@ async function selectSpec2(defaultSpec) {
4702
4706
  throw new Error("\u672A\u627E\u5230 spec \u6587\u4EF6");
4703
4707
  }
4704
4708
  if (defaultSpec) {
4705
- const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec : path13.join(specDir, defaultSpec);
4709
+ const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec : path12.join(specDir, defaultSpec);
4706
4710
  const exists2 = await FileUtils.exists(fullPath);
4707
4711
  if (!exists2) {
4708
4712
  throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${defaultSpec}`);
@@ -4712,7 +4716,7 @@ async function selectSpec2(defaultSpec) {
4712
4716
  }
4713
4717
  const specs = [];
4714
4718
  for (const file of specFiles) {
4715
- const fullPath = path13.join(specDir, file);
4719
+ const fullPath = path12.join(specDir, file);
4716
4720
  const content = await FileUtils.read(fullPath);
4717
4721
  const status = SpecUtils.parseSpecStatus(content);
4718
4722
  specs.push({ file: fullPath, name: file, status });
@@ -5003,9 +5007,9 @@ async function generateAcceptanceReport(result) {
5003
5007
  const reportDir = "docs/acceptance-reports";
5004
5008
  await FileUtils.ensureDir(reportDir);
5005
5009
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5006
- const specName = path13.basename(result.specFile, ".md");
5010
+ const specName = path12.basename(result.specFile, ".md");
5007
5011
  const milestoneSafe = result.milestone.replace(/[^a-zA-Z0-9]/g, "-");
5008
- const reportFile = path13.join(reportDir, `${timestamp}_${specName}_${milestoneSafe}.md`);
5012
+ const reportFile = path12.join(reportDir, `${timestamp}_${specName}_${milestoneSafe}.md`);
5009
5013
  const report = generateMarkdownReport(result);
5010
5014
  await FileUtils.write(reportFile, report);
5011
5015
  logger.success(`\u9A8C\u6536\u62A5\u544A\u5DF2\u751F\u6210: ${reportFile}`);
@@ -5125,7 +5129,7 @@ async function handleIssues(result) {
5125
5129
  const bugfixDir = "docs/bugfixes";
5126
5130
  await FileUtils.ensureDir(bugfixDir);
5127
5131
  for (const issue of result.issues) {
5128
- const bugfixFile = path13.join(
5132
+ const bugfixFile = path12.join(
5129
5133
  bugfixDir,
5130
5134
  `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}_${issue.id}.md`
5131
5135
  );
@@ -5251,19 +5255,394 @@ var init_accept = __esm({
5251
5255
  logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
5252
5256
  process.exit(1);
5253
5257
  }
5254
- logger.success("\u68C0\u6D4B\u5230\u9879\u76EE\u4E0A\u4E0B\u6587");
5255
- const selectedSpec = await selectSpec2(specFile);
5256
- const selectedMilestone = await selectMilestone2(selectedSpec);
5257
- const result = await runAcceptanceCheck(selectedSpec, selectedMilestone);
5258
- await generateAcceptanceReport(result);
5259
- if (result.issues.length > 0) {
5260
- await handleIssues(result);
5258
+ logger.success("\u68C0\u6D4B\u5230\u9879\u76EE\u4E0A\u4E0B\u6587");
5259
+ const selectedSpec = await selectSpec2(specFile);
5260
+ const selectedMilestone = await selectMilestone2(selectedSpec);
5261
+ const result = await runAcceptanceCheck(selectedSpec, selectedMilestone);
5262
+ await generateAcceptanceReport(result);
5263
+ if (result.issues.length > 0) {
5264
+ await handleIssues(result);
5265
+ }
5266
+ await syncSpecStatus(selectedSpec, result);
5267
+ logger.header("\u9A8C\u6536\u5B8C\u6210!");
5268
+ logger.newLine();
5269
+ } catch (error) {
5270
+ logger.error(`\u9A8C\u6536\u5931\u8D25: ${error.message}`);
5271
+ if (process.env.DEBUG) {
5272
+ console.error(error);
5273
+ }
5274
+ process.exit(1);
5275
+ }
5276
+ });
5277
+ }
5278
+ });
5279
+
5280
+ // src/commands/add-module.ts
5281
+ import { Command as Command8 } from "commander";
5282
+ import inquirer7 from "inquirer";
5283
+ import path13 from "path";
5284
+ import fs4 from "fs-extra";
5285
+ async function scanExistingModules(projectPath) {
5286
+ const modules = [];
5287
+ const settingsGradle = path13.join(projectPath, "settings.gradle");
5288
+ if (await FileUtils.exists(settingsGradle)) {
5289
+ const content = await FileUtils.read(settingsGradle);
5290
+ const includeMatches = content.match(/include\s+'([^']+)'/g);
5291
+ if (includeMatches) {
5292
+ for (const match of includeMatches) {
5293
+ const moduleName = match.replace(/include\s+'/, "").replace(/'/, "");
5294
+ modules.push(moduleName);
5295
+ }
5296
+ }
5297
+ }
5298
+ const backendPath = path13.join(projectPath, "backend");
5299
+ if (await FileUtils.exists(backendPath)) {
5300
+ const entries = await fs4.readdir(backendPath, { withFileTypes: true });
5301
+ for (const entry of entries) {
5302
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "src") {
5303
+ if (!modules.includes(entry.name)) {
5304
+ modules.push(entry.name);
5305
+ }
5306
+ }
5307
+ }
5308
+ }
5309
+ return modules;
5310
+ }
5311
+ async function detectGroupId(projectPath) {
5312
+ const buildGradle = path13.join(projectPath, "backend", "build.gradle");
5313
+ if (await FileUtils.exists(buildGradle)) {
5314
+ const content = await FileUtils.read(buildGradle);
5315
+ const groupMatch = content.match(/group\s*=\s*['"]([^'"]+)['"]/);
5316
+ if (groupMatch) {
5317
+ return groupMatch[1];
5318
+ }
5319
+ }
5320
+ const rootBuildGradle = path13.join(projectPath, "build.gradle");
5321
+ if (await FileUtils.exists(rootBuildGradle)) {
5322
+ const content = await FileUtils.read(rootBuildGradle);
5323
+ const groupMatch = content.match(/group\s*=\s*['"]([^'"]+)['"]/);
5324
+ if (groupMatch) {
5325
+ return groupMatch[1];
5326
+ }
5327
+ }
5328
+ const backendPath = path13.join(projectPath, "backend");
5329
+ if (await FileUtils.exists(backendPath)) {
5330
+ const entries = await fs4.readdir(backendPath, { withFileTypes: true });
5331
+ for (const entry of entries) {
5332
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
5333
+ const srcPath = path13.join(backendPath, entry.name, "src", "main", "java");
5334
+ if (await FileUtils.exists(srcPath)) {
5335
+ const javaDirs = await fs4.readdir(srcPath);
5336
+ if (javaDirs.length > 0) {
5337
+ return javaDirs[0];
5338
+ }
5339
+ }
5340
+ }
5341
+ }
5342
+ }
5343
+ return "org.yungu";
5344
+ }
5345
+ async function detectRootProjectName(projectPath) {
5346
+ const settingsGradle = path13.join(projectPath, "settings.gradle");
5347
+ if (await FileUtils.exists(settingsGradle)) {
5348
+ const content = await FileUtils.read(settingsGradle);
5349
+ const rootMatch = content.match(/rootProject\.name\s*=\s*['"]([^'"]+)['"]/);
5350
+ if (rootMatch) {
5351
+ return rootMatch[1];
5352
+ }
5353
+ }
5354
+ return path13.basename(projectPath);
5355
+ }
5356
+ async function createModuleStructure(projectPath, module, groupId) {
5357
+ const modulePath = path13.join(projectPath, "backend", module.name);
5358
+ logger.info(`\u6B63\u5728\u521B\u5EFA\u6A21\u5757: ${module.name}...`);
5359
+ const baseDirs = [
5360
+ "src/main/java",
5361
+ "src/main/resources",
5362
+ "src/test/java"
5363
+ ];
5364
+ for (const dir of baseDirs) {
5365
+ await FileUtils.ensureDir(path13.join(modulePath, dir));
5366
+ }
5367
+ const packageDirs = module.packagePath.split(".");
5368
+ let javaPath = path13.join(modulePath, "src", "main", "java");
5369
+ if (module.type === "service") {
5370
+ for (const subDir of ["controller", "service", "mapper", "entity", "dto", "config"]) {
5371
+ await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
5372
+ }
5373
+ await createApplicationClass2(
5374
+ path13.join(javaPath, ...packageDirs, "config", `${module.applicationClass}.java`),
5375
+ module.packagePath,
5376
+ module.applicationClass
5377
+ );
5378
+ } else if (module.type === "api") {
5379
+ for (const subDir of ["model", "constant", "enums", "exception"]) {
5380
+ await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
5381
+ }
5382
+ } else {
5383
+ for (const subDir of ["util", "common", "config", "manager"]) {
5384
+ await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
5385
+ }
5386
+ }
5387
+ await createModuleBuildGradle2(
5388
+ path13.join(modulePath, "build.gradle"),
5389
+ module,
5390
+ groupId
5391
+ );
5392
+ const gitignore = `# Gradle
5393
+ .gradle/
5394
+ build/
5395
+
5396
+ # IDE
5397
+ .idea/
5398
+ *.iml
5399
+ *.ipr
5400
+ *.iws
5401
+ .vscode/
5402
+
5403
+ # OS
5404
+ .DS_Store
5405
+ Thumbs.db
5406
+
5407
+ # Logs
5408
+ logs/
5409
+ *.log
5410
+
5411
+ # Application
5412
+ application-local.yml
5413
+ application-dev.yml
5414
+ `;
5415
+ await FileUtils.write(path13.join(modulePath, ".gitignore"), gitignore);
5416
+ logger.success(`\u6A21\u5757 ${module.name} \u521B\u5EFA\u5B8C\u6210`);
5417
+ }
5418
+ async function createApplicationClass2(filePath, packagePath, className) {
5419
+ const content = `package ${packagePath}.config;
5420
+
5421
+ import lombok.extern.slf4j.Slf4j;
5422
+ import org.springframework.boot.SpringApplication;
5423
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
5424
+ import org.springframework.context.ApplicationContext;
5425
+ import org.springframework.context.annotation.ComponentScan;
5426
+
5427
+ @SpringBootApplication
5428
+ @ComponentScan("${packagePath}")
5429
+ @Slf4j
5430
+ public class ${className} {
5431
+
5432
+ public static void main(String[] args) {
5433
+ ApplicationContext context = SpringApplication.run(${className}.class, args);
5434
+ log.info("${className} \u542F\u52A8\u6210\u529F!");
5435
+ }
5436
+ }
5437
+ `;
5438
+ await FileUtils.write(filePath, content);
5439
+ }
5440
+ async function createModuleBuildGradle2(filePath, module, groupId) {
5441
+ let dependencies = "";
5442
+ if (module.type === "service") {
5443
+ dependencies = `
5444
+ dependencies {
5445
+ implementation project(":common")
5446
+ implementation 'org.springframework.boot:spring-boot-starter-web'
5447
+ implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
5448
+ implementation 'com.alibaba:druid-spring-boot-starter'
5449
+ compileOnly "org.projectlombok:lombok"
5450
+ annotationProcessor "org.projectlombok:lombok"
5451
+ }
5452
+ `;
5453
+ } else if (module.type === "api") {
5454
+ dependencies = `
5455
+ dependencies {
5456
+ compileOnly 'org.springframework.boot:spring-boot-starter'
5457
+ compileOnly "org.projectlombok:lombok"
5458
+ annotationProcessor "org.projectlombok:lombok"
5459
+ }
5460
+ `;
5461
+ } else {
5462
+ dependencies = `
5463
+ dependencies {
5464
+ implementation 'org.springframework.boot:spring-boot-starter'
5465
+ implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
5466
+ compileOnly "org.projectlombok:lombok"
5467
+ annotationProcessor "org.projectlombok:lombok"
5468
+ }
5469
+ `;
5470
+ }
5471
+ const content = `plugins {
5472
+ id 'java-library'
5473
+ id 'org.springframework.boot' version '3.2.0'
5474
+ id 'io.spring.dependency-management' version '1.1.4'
5475
+ }
5476
+
5477
+ group = '${groupId}'
5478
+ version = '0.0.1-SNAPSHOT'
5479
+
5480
+ java {
5481
+ sourceCompatibility = '17'
5482
+ }
5483
+
5484
+ repositories {
5485
+ maven { url 'https://maven.aliyun.com/repository/public/' }
5486
+ }
5487
+
5488
+ ${dependencies}
5489
+ `;
5490
+ await FileUtils.write(filePath, content);
5491
+ }
5492
+ async function updateSettingsGradle(projectPath, rootProjectName, newModules) {
5493
+ const settingsPath = path13.join(projectPath, "backend", "settings.gradle");
5494
+ let content = "";
5495
+ if (await FileUtils.exists(settingsPath)) {
5496
+ content = await FileUtils.read(settingsPath);
5497
+ } else {
5498
+ content = `rootProject.name = '${rootProjectName}'
5499
+ `;
5500
+ }
5501
+ const existingModules = /* @__PURE__ */ new Set();
5502
+ const moduleMatches = content.matchAll(/include\s+'([^']+)'/g);
5503
+ for (const match of moduleMatches) {
5504
+ existingModules.add(match[1]);
5505
+ }
5506
+ for (const module of newModules) {
5507
+ if (!existingModules.has(module.name)) {
5508
+ existingModules.add(module.name);
5509
+ }
5510
+ }
5511
+ const lines = [];
5512
+ lines.push(`rootProject.name = '${rootProjectName}'`);
5513
+ lines.push("");
5514
+ const allModules = Array.from(existingModules).sort();
5515
+ for (const module of allModules) {
5516
+ lines.push(`include '${module}'`);
5517
+ }
5518
+ await FileUtils.write(settingsPath, lines.join("\n"));
5519
+ logger.success("settings.gradle \u5DF2\u66F4\u65B0");
5520
+ }
5521
+ function toPascalCase2(str) {
5522
+ return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
5523
+ }
5524
+ var addModuleCommand;
5525
+ var init_add_module = __esm({
5526
+ "src/commands/add-module.ts"() {
5527
+ "use strict";
5528
+ init_esm_shims();
5529
+ init_utils();
5530
+ init_logger();
5531
+ addModuleCommand = new Command8("add-module").description("\u4E3A\u5DF2\u6709\u9879\u76EE\u65B0\u589E\u6A21\u5757 (\u591A\u6A21\u5757 Gradle \u9879\u76EE)").action(async () => {
5532
+ try {
5533
+ logger.header("\u65B0\u589E\u6A21\u5757");
5534
+ logger.newLine();
5535
+ const projectPath = process.cwd();
5536
+ const hasTechStack = await FileUtils.exists("TECH_STACK.md");
5537
+ if (!hasTechStack) {
5538
+ logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
5539
+ logger.info("\u8BF7\u5207\u6362\u5230\u9879\u76EE\u6839\u76EE\u5F55\u540E\u518D\u6267\u884C\u6B64\u547D\u4EE4");
5540
+ process.exit(1);
5541
+ }
5542
+ const existingModules = await scanExistingModules(projectPath);
5543
+ if (existingModules.length > 0) {
5544
+ logger.success(`\u68C0\u6D4B\u5230\u73B0\u6709\u6A21\u5757: [${existingModules.join(", ")}]`);
5545
+ } else {
5546
+ logger.info("\u672A\u68C0\u6D4B\u5230\u73B0\u6709\u6A21\u5757");
5547
+ }
5548
+ logger.newLine();
5549
+ const groupId = await detectGroupId(projectPath);
5550
+ logger.info(`\u68C0\u6D4B\u5230 Group ID: ${groupId}`);
5551
+ logger.newLine();
5552
+ const rootProjectName = await detectRootProjectName(projectPath);
5553
+ logger.info(`\u68C0\u6D4B\u5230\u6839\u9879\u76EE\u540D: ${rootProjectName}`);
5554
+ logger.newLine();
5555
+ const newModules = [];
5556
+ let addMore = true;
5557
+ let moduleIndex = 1;
5558
+ while (addMore) {
5559
+ const moduleNameAnswer = await inquirer7.prompt([
5560
+ {
5561
+ type: "input",
5562
+ name: "moduleName",
5563
+ message: `\u8BF7\u8F93\u5165\u7B2C ${moduleIndex} \u4E2A\u65B0\u6A21\u5757\u540D\u79F0 (\u76EE\u5F55\u540D):`,
5564
+ validate: (input) => {
5565
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
5566
+ return "\u6A21\u5757\u540D\u53EA\u80FD\u4EE5\u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
5567
+ }
5568
+ if (existingModules.includes(input)) {
5569
+ return "\u8BE5\u6A21\u5757\u5DF2\u5B58\u5728";
5570
+ }
5571
+ return true;
5572
+ }
5573
+ }
5574
+ ]);
5575
+ const moduleName = moduleNameAnswer.moduleName;
5576
+ const typeAnswer = await inquirer7.prompt([
5577
+ {
5578
+ type: "list",
5579
+ name: "moduleType",
5580
+ message: `\u6A21\u5757 "${moduleName}" \u7C7B\u578B:`,
5581
+ choices: [
5582
+ { name: "API \u670D\u52A1\u6A21\u5757 (\u72EC\u7ACB\u90E8\u7F72)", value: "service" },
5583
+ { name: "\u516C\u5171\u6A21\u5757 (\u88AB\u5176\u4ED6\u6A21\u5757\u4F9D\u8D56)", value: "common" },
5584
+ { name: "API \u63A5\u53E3\u6A21\u5757 (\u4EC5\u63A5\u53E3\u5B9A\u4E49)", value: "api" }
5585
+ ],
5586
+ default: "service"
5587
+ }
5588
+ ]);
5589
+ const defaultPackage = `${groupId}.${moduleName}`;
5590
+ const packageAnswer = await inquirer7.prompt([
5591
+ {
5592
+ type: "input",
5593
+ name: "packagePath",
5594
+ message: "\u6A21\u5757\u5305\u8DEF\u5F84:",
5595
+ default: defaultPackage,
5596
+ validate: (input) => {
5597
+ if (!/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/.test(input)) {
5598
+ return "\u5305\u8DEF\u5F84\u683C\u5F0F\u4E0D\u6B63\u786E";
5599
+ }
5600
+ return true;
5601
+ }
5602
+ }
5603
+ ]);
5604
+ const pascalName = toPascalCase2(moduleName);
5605
+ const classAnswer = await inquirer7.prompt([
5606
+ {
5607
+ type: "input",
5608
+ name: "applicationClass",
5609
+ message: "\u542F\u52A8\u7C7B\u540D:",
5610
+ default: `${pascalName}Application`
5611
+ }
5612
+ ]);
5613
+ newModules.push({
5614
+ name: moduleName,
5615
+ type: typeAnswer.moduleType,
5616
+ packagePath: packageAnswer.packagePath,
5617
+ applicationClass: classAnswer.applicationClass
5618
+ });
5619
+ const moreAnswer = await inquirer7.prompt([
5620
+ {
5621
+ type: "confirm",
5622
+ name: "addMore",
5623
+ message: "\u662F\u5426\u8FD8\u9700\u8981\u6DFB\u52A0\u66F4\u591A\u6A21\u5757?",
5624
+ default: false
5625
+ }
5626
+ ]);
5627
+ addMore = moreAnswer.addMore;
5628
+ moduleIndex++;
5629
+ }
5630
+ logger.newLine();
5631
+ logger.info(`\u5C06\u65B0\u589E\u6A21\u5757: ${newModules.map((m) => m.name).join(", ")}`);
5632
+ logger.newLine();
5633
+ for (const module of newModules) {
5634
+ await createModuleStructure(projectPath, module, groupId);
5261
5635
  }
5262
- await syncSpecStatus(selectedSpec, result);
5263
- logger.header("\u9A8C\u6536\u5B8C\u6210!");
5636
+ await updateSettingsGradle(projectPath, rootProjectName, newModules);
5637
+ logger.newLine();
5638
+ logger.success("\u6A21\u5757\u521B\u5EFA\u5B8C\u6210!");
5639
+ logger.newLine();
5640
+ logger.info("\u4E0B\u4E00\u6B65:");
5641
+ logger.step("1. \u8FD0\u884C `team-cli breakdown docs/specs/xxx.md` \u62C6\u5206\u9700\u6C42");
5642
+ logger.step("2. \u8FD0\u884C `team-cli dev` \u5F00\u59CB\u5F00\u53D1");
5264
5643
  logger.newLine();
5265
5644
  } catch (error) {
5266
- logger.error(`\u9A8C\u6536\u5931\u8D25: ${error.message}`);
5645
+ logger.error(`\u65B0\u589E\u6A21\u5757\u5931\u8D25: ${error.message}`);
5267
5646
  if (process.env.DEBUG) {
5268
5647
  console.error(error);
5269
5648
  }
@@ -5274,7 +5653,7 @@ var init_accept = __esm({
5274
5653
  });
5275
5654
 
5276
5655
  // src/commands/lint.ts
5277
- import { Command as Command8 } from "commander";
5656
+ import { Command as Command9 } from "commander";
5278
5657
  import { execa as execa3 } from "execa";
5279
5658
  var lintCommand;
5280
5659
  var init_lint = __esm({
@@ -5283,7 +5662,7 @@ var init_lint = __esm({
5283
5662
  init_esm_shims();
5284
5663
  init_utils();
5285
5664
  init_logger();
5286
- lintCommand = new Command8("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").option("--no-type-check", "\u8DF3\u8FC7 TypeScript \u7C7B\u578B\u68C0\u67E5").description("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (\u524D\u7AEF + \u540E\u7AEF)").action(async (options) => {
5665
+ lintCommand = new Command9("lint").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").option("--no-type-check", "\u8DF3\u8FC7 TypeScript \u7C7B\u578B\u68C0\u67E5").description("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5 (\u524D\u7AEF + \u540E\u7AEF)").action(async (options) => {
5287
5666
  try {
5288
5667
  logger.header("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5");
5289
5668
  logger.newLine();
@@ -5417,7 +5796,7 @@ var init_lint = __esm({
5417
5796
  });
5418
5797
 
5419
5798
  // src/commands/status.ts
5420
- import { Command as Command9 } from "commander";
5799
+ import { Command as Command10 } from "commander";
5421
5800
  import path14 from "path";
5422
5801
  async function displayProjectInfo() {
5423
5802
  logger.info("\u9879\u76EE\u4FE1\u606F:");
@@ -5535,7 +5914,7 @@ var init_status = __esm({
5535
5914
  init_esm_shims();
5536
5915
  init_utils();
5537
5916
  init_logger();
5538
- statusCommand = new Command9("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
5917
+ statusCommand = new Command10("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
5539
5918
  try {
5540
5919
  logger.header("\u9879\u76EE\u72B6\u6001");
5541
5920
  logger.newLine();
@@ -5561,9 +5940,9 @@ var init_status = __esm({
5561
5940
  });
5562
5941
 
5563
5942
  // src/commands/detect-deps.ts
5564
- import { Command as Command10 } from "commander";
5943
+ import { Command as Command11 } from "commander";
5565
5944
  import path15 from "path";
5566
- import inquirer7 from "inquirer";
5945
+ import inquirer8 from "inquirer";
5567
5946
  async function detectDependencies(specFile) {
5568
5947
  logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
5569
5948
  const projectDir = ".";
@@ -5604,7 +5983,7 @@ async function detectDependencies(specFile) {
5604
5983
  logger.step(`- ${spec}`);
5605
5984
  }
5606
5985
  logger.newLine();
5607
- const answers = await inquirer7.prompt([
5986
+ const answers = await inquirer8.prompt([
5608
5987
  {
5609
5988
  type: "confirm",
5610
5989
  name: "autoUpdate",
@@ -5776,7 +6155,7 @@ var init_detect_deps = __esm({
5776
6155
  init_esm_shims();
5777
6156
  init_utils();
5778
6157
  init_logger();
5779
- detectDepsCommand = new Command10("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
6158
+ detectDepsCommand = new Command11("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
5780
6159
  try {
5781
6160
  logger.header("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
5782
6161
  logger.newLine();
@@ -5828,7 +6207,7 @@ var init_detect_deps = __esm({
5828
6207
  });
5829
6208
 
5830
6209
  // src/commands/sync-memory.ts
5831
- import { Command as Command11 } from "commander";
6210
+ import { Command as Command12 } from "commander";
5832
6211
  import path16 from "path";
5833
6212
  async function syncFeatureInventory(aiMemoryFile, projectDir) {
5834
6213
  logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
@@ -6079,15 +6458,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
6079
6458
  await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
6080
6459
  }
6081
6460
  function extractRepoName(repository) {
6082
- let path21 = repository;
6083
- path21 = path21.replace(/^https?:\/\//, "");
6084
- path21 = path21.replace(/^git@/, "");
6085
- const parts = path21.split("/");
6461
+ let path22 = repository;
6462
+ path22 = path22.replace(/^https?:\/\//, "");
6463
+ path22 = path22.replace(/^git@/, "");
6464
+ const parts = path22.split("/");
6086
6465
  if (parts.length > 1) {
6087
- path21 = parts.slice(1).join("/");
6466
+ path22 = parts.slice(1).join("/");
6088
6467
  }
6089
- path21 = path21.replace(/\.git$/, "");
6090
- return path21;
6468
+ path22 = path22.replace(/\.git$/, "");
6469
+ return path22;
6091
6470
  }
6092
6471
  var syncMemoryCommand;
6093
6472
  var init_sync_memory = __esm({
@@ -6097,7 +6476,7 @@ var init_sync_memory = __esm({
6097
6476
  init_utils();
6098
6477
  init_logger();
6099
6478
  init_template_version();
6100
- syncMemoryCommand = new Command11("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
6479
+ syncMemoryCommand = new Command12("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
6101
6480
  try {
6102
6481
  logger.header("\u540C\u6B65 AI_MEMORY.md");
6103
6482
  logger.newLine();
@@ -6131,9 +6510,9 @@ var init_sync_memory = __esm({
6131
6510
  });
6132
6511
 
6133
6512
  // src/commands/check-api.ts
6134
- import { Command as Command12 } from "commander";
6513
+ import { Command as Command13 } from "commander";
6135
6514
  import path17 from "path";
6136
- import inquirer8 from "inquirer";
6515
+ import inquirer9 from "inquirer";
6137
6516
  import { Listr as Listr6 } from "listr2";
6138
6517
  async function checkApiConflicts(projectDir) {
6139
6518
  const backendDir = path17.join(projectDir, "backend");
@@ -6379,10 +6758,10 @@ function extractApisFromRegistry(registryContent) {
6379
6758
  let match;
6380
6759
  while ((match = apiRegex.exec(registryContent)) !== null) {
6381
6760
  const method = match[1];
6382
- const path21 = match[2].trim();
6761
+ const path22 = match[2].trim();
6383
6762
  const description = match[3].trim();
6384
- const key = `${method}:${path21}`;
6385
- apis.set(key, { method, path: path21, description });
6763
+ const key = `${method}:${path22}`;
6764
+ apis.set(key, { method, path: path22, description });
6386
6765
  }
6387
6766
  return apis;
6388
6767
  }
@@ -6405,7 +6784,7 @@ var init_check_api = __esm({
6405
6784
  init_esm_shims();
6406
6785
  init_utils();
6407
6786
  init_logger();
6408
- checkApiCommand = new Command12("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
6787
+ checkApiCommand = new Command13("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
6409
6788
  try {
6410
6789
  logger.header("API \u68C0\u67E5");
6411
6790
  logger.newLine();
@@ -6415,7 +6794,7 @@ var init_check_api = __esm({
6415
6794
  logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
6416
6795
  process.exit(1);
6417
6796
  }
6418
- const answers = await inquirer8.prompt([
6797
+ const answers = await inquirer9.prompt([
6419
6798
  {
6420
6799
  type: "list",
6421
6800
  name: "checkType",
@@ -6428,187 +6807,621 @@ var init_check_api = __esm({
6428
6807
  ],
6429
6808
  default: "all"
6430
6809
  }
6431
- ]);
6432
- const tasks = new Listr6([]);
6433
- if (answers.checkType === "conflicts" || answers.checkType === "all") {
6434
- tasks.add({
6435
- title: "\u68C0\u6D4B API \u51B2\u7A81",
6436
- task: async () => {
6437
- await checkApiConflicts(".");
6438
- }
6439
- });
6810
+ ]);
6811
+ const tasks = new Listr6([]);
6812
+ if (answers.checkType === "conflicts" || answers.checkType === "all") {
6813
+ tasks.add({
6814
+ title: "\u68C0\u6D4B API \u51B2\u7A81",
6815
+ task: async () => {
6816
+ await checkApiConflicts(".");
6817
+ }
6818
+ });
6819
+ }
6820
+ if (answers.checkType === "changes" || answers.checkType === "all") {
6821
+ tasks.add({
6822
+ title: "\u68C0\u6D4B API \u53D8\u66F4",
6823
+ task: async () => {
6824
+ await detectApiChanges(".");
6825
+ }
6826
+ });
6827
+ }
6828
+ if (answers.checkType === "registry" || answers.checkType === "all") {
6829
+ tasks.add({
6830
+ title: "\u751F\u6210 API Registry",
6831
+ task: async () => {
6832
+ await generateApiRegistry(".");
6833
+ }
6834
+ });
6835
+ }
6836
+ await tasks.run();
6837
+ logger.newLine();
6838
+ logger.header("API \u68C0\u67E5\u5B8C\u6210");
6839
+ } catch (error) {
6840
+ logger.error(`API \u68C0\u67E5\u5931\u8D25: ${error.message}`);
6841
+ if (process.env.DEBUG) {
6842
+ console.error(error);
6843
+ }
6844
+ process.exit(1);
6845
+ }
6846
+ });
6847
+ }
6848
+ });
6849
+
6850
+ // src/commands/logs.ts
6851
+ import { Command as Command14 } from "commander";
6852
+ import path18 from "path";
6853
+ import inquirer10 from "inquirer";
6854
+ async function collectLogFiles(targetDir) {
6855
+ const logs = [];
6856
+ try {
6857
+ const allFiles = await FileUtils.findFiles("*.md", targetDir);
6858
+ const filtered = allFiles.filter((f) => f !== "index.md");
6859
+ for (const file of filtered) {
6860
+ const filePath = path18.join(targetDir, file);
6861
+ const stat = await FileUtils.exists(filePath);
6862
+ if (stat) {
6863
+ logs.push(filePath);
6864
+ }
6865
+ }
6866
+ } catch (error) {
6867
+ }
6868
+ return logs;
6869
+ }
6870
+ var logsCommand;
6871
+ var init_logs = __esm({
6872
+ "src/commands/logs.ts"() {
6873
+ "use strict";
6874
+ init_esm_shims();
6875
+ init_utils();
6876
+ init_logger();
6877
+ logsCommand = new Command14("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") => {
6878
+ try {
6879
+ logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
6880
+ logger.newLine();
6881
+ const hasTechStack = await FileUtils.exists("TECH_STACK.md");
6882
+ if (!hasTechStack) {
6883
+ logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
6884
+ logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
6885
+ process.exit(1);
6886
+ }
6887
+ const sessionsDir = "docs/sessions";
6888
+ const dirExists = await FileUtils.exists(sessionsDir);
6889
+ if (!dirExists) {
6890
+ logger.info("\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
6891
+ logger.info("\u8FD0\u884C 'team-cli dev' \u540E\u4F1A\u81EA\u52A8\u751F\u6210\u65E5\u5FD7");
6892
+ process.exit(0);
6893
+ }
6894
+ let targetDir = "";
6895
+ let displayTitle = "";
6896
+ switch (filter) {
6897
+ case "":
6898
+ case "today": {
6899
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6900
+ targetDir = path18.join(sessionsDir, today);
6901
+ const todayExists = await FileUtils.exists(targetDir);
6902
+ if (!todayExists) {
6903
+ logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
6904
+ process.exit(0);
6905
+ }
6906
+ displayTitle = "\u663E\u793A\u4ECA\u65E5\u4F1A\u8BDD\u65E5\u5FD7:";
6907
+ break;
6908
+ }
6909
+ case "--all":
6910
+ case "-a": {
6911
+ targetDir = sessionsDir;
6912
+ displayTitle = "\u663E\u793A\u6240\u6709\u4F1A\u8BDD\u65E5\u5FD7:";
6913
+ break;
6914
+ }
6915
+ default: {
6916
+ targetDir = path18.join(sessionsDir, filter);
6917
+ const dateExists = await FileUtils.exists(targetDir);
6918
+ if (!dateExists) {
6919
+ logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
6920
+ logger.info("\u53EF\u7528\u65E5\u671F:");
6921
+ const entries = await FileUtils.findFiles("*/", sessionsDir);
6922
+ const dates = entries.slice(0, 10);
6923
+ for (const date of dates) {
6924
+ logger.info(` ${date.replace("/", "")}`);
6925
+ }
6926
+ process.exit(1);
6927
+ }
6928
+ displayTitle = `\u663E\u793A ${filter} \u7684\u4F1A\u8BDD\u65E5\u5FD7:`;
6929
+ break;
6930
+ }
6931
+ }
6932
+ logger.info(displayTitle);
6933
+ logger.newLine();
6934
+ const logs = await collectLogFiles(targetDir);
6935
+ if (logs.length === 0) {
6936
+ logger.info("\u65E0\u65E5\u5FD7\u6587\u4EF6");
6937
+ process.exit(0);
6938
+ }
6939
+ for (let i = 0; i < logs.length; i++) {
6940
+ const relPath = path18.relative(sessionsDir, logs[i]);
6941
+ logger.step(`${i + 1}) ${relPath}`);
6942
+ }
6943
+ logger.newLine();
6944
+ const answers = await inquirer10.prompt([
6945
+ {
6946
+ type: "input",
6947
+ name: "selection",
6948
+ message: "\u8F93\u5165\u7F16\u53F7\u67E5\u770B\u8BE6\u60C5 (\u6216 Enter \u9000\u51FA):",
6949
+ default: ""
6950
+ }
6951
+ ]);
6952
+ const selection = answers.selection.trim();
6953
+ if (selection === "") {
6954
+ process.exit(0);
6955
+ }
6956
+ const selectionNum = parseInt(selection, 10);
6957
+ if (isNaN(selectionNum) || selectionNum < 1 || selectionNum > logs.length) {
6958
+ logger.error("\u65E0\u6548\u7684\u9009\u62E9");
6959
+ process.exit(1);
6960
+ }
6961
+ const selectedLog = logs[selectionNum - 1];
6962
+ logger.newLine();
6963
+ logger.header("\u65E5\u5FD7\u8BE6\u60C5");
6964
+ logger.newLine();
6965
+ const content = await FileUtils.read(selectedLog);
6966
+ console.log(content);
6967
+ } catch (error) {
6968
+ logger.error(`\u67E5\u770B\u65E5\u5FD7\u5931\u8D25: ${error.message}`);
6969
+ if (process.env.DEBUG) {
6970
+ console.error(error);
6971
+ }
6972
+ process.exit(1);
6973
+ }
6974
+ });
6975
+ }
6976
+ });
6977
+
6978
+ // src/lib/user-config.ts
6979
+ var user_config_exports = {};
6980
+ __export(user_config_exports, {
6981
+ UserConfigManager: () => UserConfigManager,
6982
+ userConfigManager: () => userConfigManager
6983
+ });
6984
+ import path19 from "path";
6985
+ import os3 from "os";
6986
+ import crypto from "crypto";
6987
+ var UserConfigManager, userConfigManager;
6988
+ var init_user_config = __esm({
6989
+ "src/lib/user-config.ts"() {
6990
+ "use strict";
6991
+ init_esm_shims();
6992
+ init_utils();
6993
+ init_logger();
6994
+ UserConfigManager = class {
6995
+ configPath;
6996
+ constructor() {
6997
+ const configDir = path19.join(os3.homedir(), ".team-cli");
6998
+ this.configPath = path19.join(configDir, "config.json");
6999
+ }
7000
+ /**
7001
+ * 加载用户配置
7002
+ */
7003
+ async load() {
7004
+ try {
7005
+ const exists = await FileUtils.exists(this.configPath);
7006
+ if (!exists) {
7007
+ return null;
7008
+ }
7009
+ const content = await FileUtils.read(this.configPath);
7010
+ const config = JSON.parse(content);
7011
+ if (config.gitlab?.accessToken) {
7012
+ config.gitlab.accessToken = this.decrypt(config.gitlab.accessToken);
7013
+ }
7014
+ return config;
7015
+ } catch (error) {
7016
+ logger.debug(`\u52A0\u8F7D\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
7017
+ return null;
7018
+ }
7019
+ }
7020
+ /**
7021
+ * 保存用户配置
7022
+ */
7023
+ async save(config) {
7024
+ try {
7025
+ const configDir = path19.dirname(this.configPath);
7026
+ await FileUtils.ensureDir(configDir);
7027
+ const configToSave = JSON.parse(JSON.stringify(config));
7028
+ if (configToSave.gitlab?.accessToken) {
7029
+ configToSave.gitlab.accessToken = this.encrypt(configToSave.gitlab.accessToken);
7030
+ }
7031
+ const content = JSON.stringify(configToSave, null, 2);
7032
+ await FileUtils.write(this.configPath, content);
7033
+ } catch (error) {
7034
+ throw new Error(`\u4FDD\u5B58\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
6440
7035
  }
6441
- if (answers.checkType === "changes" || answers.checkType === "all") {
6442
- tasks.add({
6443
- title: "\u68C0\u6D4B API \u53D8\u66F4",
6444
- task: async () => {
6445
- await detectApiChanges(".");
6446
- }
6447
- });
7036
+ }
7037
+ /**
7038
+ * 更新 GitLab Token
7039
+ */
7040
+ async updateGitLabToken(token, baseUrl) {
7041
+ const config = await this.load() || {
7042
+ gitlab: {
7043
+ accessToken: "",
7044
+ baseUrl: "https://gitlab.com",
7045
+ timeout: 3e4
7046
+ }
7047
+ };
7048
+ config.gitlab.accessToken = token;
7049
+ if (baseUrl) {
7050
+ config.gitlab.baseUrl = baseUrl;
6448
7051
  }
6449
- if (answers.checkType === "registry" || answers.checkType === "all") {
6450
- tasks.add({
6451
- title: "\u751F\u6210 API Registry",
6452
- task: async () => {
6453
- await generateApiRegistry(".");
6454
- }
6455
- });
7052
+ await this.save(config);
7053
+ }
7054
+ /**
7055
+ * 获取 GitLab Token
7056
+ */
7057
+ async getGitLabToken() {
7058
+ const config = await this.load();
7059
+ return config?.gitlab?.accessToken || null;
7060
+ }
7061
+ /**
7062
+ * 获取 GitLab 配置
7063
+ */
7064
+ async getGitLabConfig() {
7065
+ const config = await this.load();
7066
+ return config?.gitlab || null;
7067
+ }
7068
+ /**
7069
+ * 检查是否已有配置
7070
+ */
7071
+ async hasConfig() {
7072
+ const config = await this.load();
7073
+ return config !== null && config.gitlab?.accessToken !== void 0;
7074
+ }
7075
+ /**
7076
+ * 删除配置
7077
+ */
7078
+ async removeConfig() {
7079
+ try {
7080
+ const exists = await FileUtils.exists(this.configPath);
7081
+ if (exists) {
7082
+ await FileUtils.remove(this.configPath);
7083
+ }
7084
+ } catch (error) {
7085
+ throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
6456
7086
  }
6457
- await tasks.run();
6458
- logger.newLine();
6459
- logger.header("API \u68C0\u67E5\u5B8C\u6210");
6460
- } catch (error) {
6461
- logger.error(`API \u68C0\u67E5\u5931\u8D25: ${error.message}`);
6462
- if (process.env.DEBUG) {
6463
- console.error(error);
7087
+ }
7088
+ /**
7089
+ * 简单加密 (使用机器特定密钥)
7090
+ */
7091
+ encrypt(text) {
7092
+ const key = this.getMachineKey();
7093
+ const iv = crypto.randomBytes(16);
7094
+ const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
7095
+ let encrypted = cipher.update(text, "utf8", "hex");
7096
+ encrypted += cipher.final("hex");
7097
+ return iv.toString("hex") + ":" + encrypted;
7098
+ }
7099
+ /**
7100
+ * 简单解密
7101
+ */
7102
+ decrypt(encryptedText) {
7103
+ try {
7104
+ const key = this.getMachineKey();
7105
+ const parts = encryptedText.split(":");
7106
+ const iv = Buffer.from(parts[0], "hex");
7107
+ const encrypted = parts[1];
7108
+ const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
7109
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
7110
+ decrypted += decipher.final("utf8");
7111
+ return decrypted;
7112
+ } catch {
7113
+ return encryptedText;
6464
7114
  }
6465
- process.exit(1);
6466
7115
  }
6467
- });
7116
+ /**
7117
+ * 获取机器特定密钥
7118
+ */
7119
+ getMachineKey() {
7120
+ const hostname = os3.hostname();
7121
+ const platform = os3.platform();
7122
+ const arch = os3.arch();
7123
+ const cpus = os3.cpus();
7124
+ const machineInfo = `${hostname}-${platform}-${arch}-${cpus[0]?.model || "unknown"}`;
7125
+ return crypto.createHash("sha256").update(machineInfo).digest();
7126
+ }
7127
+ /**
7128
+ * 获取配置目录
7129
+ */
7130
+ getConfigDir() {
7131
+ return path19.dirname(this.configPath);
7132
+ }
7133
+ /**
7134
+ * 获取配置文件路径
7135
+ */
7136
+ getConfigPath() {
7137
+ return this.configPath;
7138
+ }
7139
+ };
7140
+ userConfigManager = new UserConfigManager();
6468
7141
  }
6469
7142
  });
6470
7143
 
6471
- // src/commands/logs.ts
6472
- import { Command as Command13 } from "commander";
6473
- import path18 from "path";
6474
- import inquirer9 from "inquirer";
6475
- async function collectLogFiles(targetDir) {
6476
- const logs = [];
6477
- try {
6478
- const allFiles = await FileUtils.findFiles("*.md", targetDir);
6479
- const filtered = allFiles.filter((f) => f !== "index.md");
6480
- for (const file of filtered) {
6481
- const filePath = path18.join(targetDir, file);
6482
- const stat = await FileUtils.exists(filePath);
6483
- if (stat) {
6484
- logs.push(filePath);
6485
- }
6486
- }
6487
- } catch (error) {
6488
- }
6489
- return logs;
6490
- }
6491
- var logsCommand;
6492
- var init_logs = __esm({
6493
- "src/commands/logs.ts"() {
7144
+ // src/lib/gitlab-api.ts
7145
+ var gitlab_api_exports = {};
7146
+ __export(gitlab_api_exports, {
7147
+ GitLabAPI: () => GitLabAPI
7148
+ });
7149
+ var GitLabAPI;
7150
+ var init_gitlab_api = __esm({
7151
+ "src/lib/gitlab-api.ts"() {
6494
7152
  "use strict";
6495
7153
  init_esm_shims();
6496
- init_utils();
6497
- init_logger();
6498
- logsCommand = new Command13("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") => {
6499
- try {
6500
- logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
6501
- logger.newLine();
6502
- const hasTechStack = await FileUtils.exists("TECH_STACK.md");
6503
- if (!hasTechStack) {
6504
- logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
6505
- logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
6506
- process.exit(1);
7154
+ GitLabAPI = class {
7155
+ config;
7156
+ defaultTimeout = 3e4;
7157
+ constructor(config) {
7158
+ this.config = {
7159
+ ...config,
7160
+ timeout: config.timeout || this.defaultTimeout
7161
+ };
7162
+ }
7163
+ /**
7164
+ * 验证 Token 是否有效
7165
+ */
7166
+ async authenticate() {
7167
+ try {
7168
+ const response = await this.request("/user");
7169
+ return response.ok;
7170
+ } catch (error) {
7171
+ return false;
6507
7172
  }
6508
- const sessionsDir = "docs/sessions";
6509
- const dirExists = await FileUtils.exists(sessionsDir);
6510
- if (!dirExists) {
6511
- logger.info("\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
6512
- logger.info("\u8FD0\u884C 'team-cli dev' \u540E\u4F1A\u81EA\u52A8\u751F\u6210\u65E5\u5FD7");
6513
- process.exit(0);
7173
+ }
7174
+ /**
7175
+ * 列出项目的所有 Tags
7176
+ */
7177
+ async listTags(projectPath) {
7178
+ try {
7179
+ const encodedPath = this.encodeProjectPath(projectPath);
7180
+ const response = await this.request(`/projects/${encodedPath}/repository/tags?per_page=100`);
7181
+ if (!response.ok) {
7182
+ throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
7183
+ }
7184
+ const tags = await response.json();
7185
+ return tags.sort((a, b) => {
7186
+ return this.compareVersionStrings(b.name, a.name);
7187
+ });
7188
+ } catch (error) {
7189
+ throw new Error(`\u83B7\u53D6 Tags \u5931\u8D25: ${error}`);
7190
+ }
7191
+ }
7192
+ /**
7193
+ * 列出项目的所有 Branches
7194
+ */
7195
+ async listBranches(projectPath) {
7196
+ try {
7197
+ const encodedPath = this.encodeProjectPath(projectPath);
7198
+ const response = await this.request(`/projects/${encodedPath}/repository/branches?per_page=100`);
7199
+ if (!response.ok) {
7200
+ throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
7201
+ }
7202
+ const branches = await response.json();
7203
+ return branches.sort((a, b) => {
7204
+ if (a.default) return -1;
7205
+ if (b.default) return 1;
7206
+ return a.name.localeCompare(b.name);
7207
+ });
7208
+ } catch (error) {
7209
+ throw new Error(`\u83B7\u53D6 Branches \u5931\u8D25: ${error}`);
7210
+ }
7211
+ }
7212
+ /**
7213
+ * 验证 Tag 是否存在
7214
+ */
7215
+ async validateTag(projectPath, tag) {
7216
+ try {
7217
+ const tags = await this.listTags(projectPath);
7218
+ return tags.some((t) => t.name === tag);
7219
+ } catch {
7220
+ return false;
6514
7221
  }
6515
- let targetDir = "";
6516
- let displayTitle = "";
6517
- switch (filter) {
6518
- case "":
6519
- case "today": {
6520
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6521
- targetDir = path18.join(sessionsDir, today);
6522
- const todayExists = await FileUtils.exists(targetDir);
6523
- if (!todayExists) {
6524
- logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
6525
- process.exit(0);
6526
- }
6527
- displayTitle = "\u663E\u793A\u4ECA\u65E5\u4F1A\u8BDD\u65E5\u5FD7:";
6528
- break;
7222
+ }
7223
+ /**
7224
+ * 验证 Branch 是否存在
7225
+ */
7226
+ async validateBranch(projectPath, branch) {
7227
+ try {
7228
+ const branches = await this.listBranches(projectPath);
7229
+ return branches.some((b) => b.name === branch);
7230
+ } catch {
7231
+ return false;
7232
+ }
7233
+ }
7234
+ /**
7235
+ * 获取项目信息
7236
+ */
7237
+ async getProject(projectPath) {
7238
+ try {
7239
+ const encodedPath = this.encodeProjectPath(projectPath);
7240
+ const response = await this.request(`/projects/${encodedPath}`);
7241
+ if (!response.ok) {
7242
+ return null;
6529
7243
  }
6530
- case "--all":
6531
- case "-a": {
6532
- targetDir = sessionsDir;
6533
- displayTitle = "\u663E\u793A\u6240\u6709\u4F1A\u8BDD\u65E5\u5FD7:";
6534
- break;
7244
+ return await response.json();
7245
+ } catch {
7246
+ return null;
7247
+ }
7248
+ }
7249
+ /**
7250
+ * 获取指定 Tag 的 commit 信息
7251
+ */
7252
+ async getTagCommit(projectPath, tag) {
7253
+ try {
7254
+ const tags = await this.listTags(projectPath);
7255
+ const targetTag = tags.find((t) => t.name === tag);
7256
+ return targetTag?.commit.id || null;
7257
+ } catch {
7258
+ return null;
7259
+ }
7260
+ }
7261
+ /**
7262
+ * 获取指定 Branch 的最新 commit 信息
7263
+ */
7264
+ async getBranchCommit(projectPath, branch) {
7265
+ try {
7266
+ const encodedPath = this.encodeProjectPath(projectPath);
7267
+ const response = await this.request(
7268
+ `/projects/${encodedPath}/repository/branches/${encodeURIComponent(branch)}`
7269
+ );
7270
+ if (!response.ok) {
7271
+ return null;
6535
7272
  }
6536
- default: {
6537
- targetDir = path18.join(sessionsDir, filter);
6538
- const dateExists = await FileUtils.exists(targetDir);
6539
- if (!dateExists) {
6540
- logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
6541
- logger.info("\u53EF\u7528\u65E5\u671F:");
6542
- const entries = await FileUtils.findFiles("*/", sessionsDir);
6543
- const dates = entries.slice(0, 10);
6544
- for (const date of dates) {
6545
- logger.info(` ${date.replace("/", "")}`);
6546
- }
6547
- process.exit(1);
6548
- }
6549
- displayTitle = `\u663E\u793A ${filter} \u7684\u4F1A\u8BDD\u65E5\u5FD7:`;
6550
- break;
7273
+ const branchInfo = await response.json();
7274
+ return branchInfo.commit.id;
7275
+ } catch {
7276
+ return null;
7277
+ }
7278
+ }
7279
+ /**
7280
+ * 对比两个版本之间的差异
7281
+ */
7282
+ async compareVersions(projectPath, from, to) {
7283
+ try {
7284
+ const encodedPath = this.encodeProjectPath(projectPath);
7285
+ const response = await this.request(
7286
+ `/projects/${encodedPath}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
7287
+ );
7288
+ if (!response.ok) {
7289
+ return null;
6551
7290
  }
7291
+ return await response.json();
7292
+ } catch {
7293
+ return null;
6552
7294
  }
6553
- logger.info(displayTitle);
6554
- logger.newLine();
6555
- const logs = await collectLogFiles(targetDir);
6556
- if (logs.length === 0) {
6557
- logger.info("\u65E0\u65E5\u5FD7\u6587\u4EF6");
6558
- process.exit(0);
7295
+ }
7296
+ /**
7297
+ * 比较两个版本号(用于排序)
7298
+ * 返回值: -1 (v1 < v2), 0 (v1 == v2), 1 (v1 > v2)
7299
+ */
7300
+ compareVersionStrings(v1, v2) {
7301
+ const version1 = v1.replace(/^v/, "");
7302
+ const version2 = v2.replace(/^v/, "");
7303
+ const parts1 = version1.split(".").map((p) => parseInt(p, 10) || 0);
7304
+ const parts2 = version2.split(".").map((p) => parseInt(p, 10) || 0);
7305
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
7306
+ const p1 = parts1[i] || 0;
7307
+ const p2 = parts2[i] || 0;
7308
+ if (p1 > p2) return 1;
7309
+ if (p1 < p2) return -1;
6559
7310
  }
6560
- for (let i = 0; i < logs.length; i++) {
6561
- const relPath = path18.relative(sessionsDir, logs[i]);
6562
- logger.step(`${i + 1}) ${relPath}`);
7311
+ return 0;
7312
+ }
7313
+ /**
7314
+ * 获取最新的版本(优先 Tag,否则 latest commit)
7315
+ */
7316
+ async getLatestVersion(projectPath) {
7317
+ try {
7318
+ const tags = await this.listTags(projectPath);
7319
+ const branches = await this.listBranches(projectPath);
7320
+ if (tags.length > 0) {
7321
+ const latestTag = tags[0];
7322
+ return {
7323
+ type: "tag",
7324
+ name: latestTag.name,
7325
+ commit: latestTag.commit.id
7326
+ };
7327
+ }
7328
+ const defaultBranch = branches.find((b) => b.default);
7329
+ if (defaultBranch) {
7330
+ return {
7331
+ type: "commit",
7332
+ name: defaultBranch.name,
7333
+ commit: defaultBranch.commit.id
7334
+ };
7335
+ }
7336
+ return null;
7337
+ } catch {
7338
+ return null;
6563
7339
  }
6564
- logger.newLine();
6565
- const answers = await inquirer9.prompt([
6566
- {
6567
- type: "input",
6568
- name: "selection",
6569
- message: "\u8F93\u5165\u7F16\u53F7\u67E5\u770B\u8BE6\u60C5 (\u6216 Enter \u9000\u51FA):",
6570
- default: ""
7340
+ }
7341
+ /**
7342
+ * 解析项目路径
7343
+ * 从 Git URL 中提取项目路径
7344
+ */
7345
+ static parseProjectPath(repository) {
7346
+ let path22 = repository;
7347
+ if (path22.startsWith("git@")) {
7348
+ path22 = path22.replace(/^git@/, "");
7349
+ const colonIndex = path22.indexOf(":");
7350
+ if (colonIndex !== -1) {
7351
+ path22 = path22.substring(colonIndex + 1);
7352
+ }
7353
+ } else {
7354
+ path22 = path22.replace(/^https?:\/\//, "");
7355
+ const parts = path22.split("/");
7356
+ if (parts.length > 1) {
7357
+ path22 = parts.slice(1).join("/");
6571
7358
  }
6572
- ]);
6573
- const selection = answers.selection.trim();
6574
- if (selection === "") {
6575
- process.exit(0);
6576
7359
  }
6577
- const selectionNum = parseInt(selection, 10);
6578
- if (isNaN(selectionNum) || selectionNum < 1 || selectionNum > logs.length) {
6579
- logger.error("\u65E0\u6548\u7684\u9009\u62E9");
6580
- process.exit(1);
7360
+ path22 = path22.replace(/\.git$/, "");
7361
+ return path22;
7362
+ }
7363
+ /**
7364
+ * 编码项目路径用于 API 请求
7365
+ */
7366
+ encodeProjectPath(projectPath) {
7367
+ return encodeURIComponent(projectPath).replace(/%2F/g, "%2F");
7368
+ }
7369
+ /**
7370
+ * 发送 HTTP 请求
7371
+ */
7372
+ async request(endpoint, options = {}) {
7373
+ const url = `${this.config.baseUrl}/api/v4${endpoint}`;
7374
+ const headers = {
7375
+ "PRIVATE-TOKEN": this.config.accessToken,
7376
+ "Content-Type": "application/json",
7377
+ ...options.headers
7378
+ };
7379
+ const controller = new AbortController();
7380
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
7381
+ try {
7382
+ const response = await fetch(url, {
7383
+ ...options,
7384
+ headers,
7385
+ signal: controller.signal
7386
+ });
7387
+ return response;
7388
+ } catch (error) {
7389
+ if (error.name === "AbortError") {
7390
+ throw new Error("\u8BF7\u6C42\u8D85\u65F6");
7391
+ }
7392
+ throw error;
7393
+ } finally {
7394
+ clearTimeout(timeoutId);
6581
7395
  }
6582
- const selectedLog = logs[selectionNum - 1];
6583
- logger.newLine();
6584
- logger.header("\u65E5\u5FD7\u8BE6\u60C5");
6585
- logger.newLine();
6586
- const content = await FileUtils.read(selectedLog);
6587
- console.log(content);
6588
- } catch (error) {
6589
- logger.error(`\u67E5\u770B\u65E5\u5FD7\u5931\u8D25: ${error.message}`);
6590
- if (process.env.DEBUG) {
6591
- console.error(error);
7396
+ }
7397
+ /**
7398
+ * 获取默认分支
7399
+ */
7400
+ async getDefaultBranch(projectPath) {
7401
+ try {
7402
+ const project = await this.getProject(projectPath);
7403
+ return project?.default_branch || null;
7404
+ } catch {
7405
+ return null;
6592
7406
  }
6593
- process.exit(1);
6594
7407
  }
6595
- });
7408
+ };
6596
7409
  }
6597
7410
  });
6598
7411
 
6599
7412
  // src/commands/update.ts
6600
- import { Command as Command14 } from "commander";
6601
- import path19 from "path";
7413
+ import { Command as Command15 } from "commander";
7414
+ import path20 from "path";
6602
7415
  import { execa as execa4 } from "execa";
6603
- import inquirer10 from "inquirer";
6604
- import fs4 from "fs-extra";
7416
+ import inquirer11 from "inquirer";
7417
+ import fs5 from "fs-extra";
6605
7418
  async function performUpdate(projectPath, updates) {
6606
7419
  logger.newLine();
6607
7420
  logger.info("\u5F00\u59CB\u66F4\u65B0\u6A21\u677F...");
6608
7421
  for (const update of updates) {
6609
7422
  const { type, info, updateOptions } = update;
6610
7423
  const targetDir = type === "frontend" ? "frontend" : "backend";
6611
- const targetPath = path19.join(projectPath, targetDir);
7424
+ const targetPath = path20.join(projectPath, targetDir);
6612
7425
  logger.newLine();
6613
7426
  logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
6614
7427
  if (updateOptions?.tag || updateOptions?.branch) {
@@ -6639,8 +7452,8 @@ async function performUpdate(projectPath, updates) {
6639
7452
  }
6640
7453
  }
6641
7454
  const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
6642
- const backupDir = path19.join(projectPath, `.backup-${Date.now()}`);
6643
- await fs4.copy(targetPath, path19.join(backupDir, targetDir));
7455
+ const backupDir = path20.join(projectPath, `.backup-${Date.now()}`);
7456
+ await fs5.copy(targetPath, path20.join(backupDir, targetDir));
6644
7457
  logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
6645
7458
  if (updateOptions?.dryRun) {
6646
7459
  logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
@@ -6650,7 +7463,7 @@ async function performUpdate(projectPath, updates) {
6650
7463
  continue;
6651
7464
  }
6652
7465
  try {
6653
- const tempDir = path19.join(projectPath, `.template-update-${Date.now()}`);
7466
+ const tempDir = path20.join(projectPath, `.template-update-${Date.now()}`);
6654
7467
  await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
6655
7468
  stdio: "pipe"
6656
7469
  });
@@ -6667,17 +7480,17 @@ async function performUpdate(projectPath, updates) {
6667
7480
  const currentFiles = await FileUtils.findFiles("*", targetPath);
6668
7481
  for (const file of currentFiles) {
6669
7482
  if (!keepFiles.includes(file)) {
6670
- const filePath = path19.join(targetPath, file);
7483
+ const filePath = path20.join(targetPath, file);
6671
7484
  try {
6672
- await fs4.remove(filePath);
7485
+ await fs5.remove(filePath);
6673
7486
  } catch {
6674
7487
  }
6675
7488
  }
6676
7489
  }
6677
- await fs4.copy(tempDir, targetPath, {
7490
+ await fs5.copy(tempDir, targetPath, {
6678
7491
  filter: (src) => !src.includes(".git")
6679
7492
  });
6680
- await fs4.remove(tempDir);
7493
+ await fs5.remove(tempDir);
6681
7494
  await updateTemplateVersion(projectPath, type, commit.trim(), {
6682
7495
  tag: updateOptions?.tag || latestTag,
6683
7496
  branch: updateOptions?.branch
@@ -6688,9 +7501,9 @@ async function performUpdate(projectPath, updates) {
6688
7501
  } catch (error) {
6689
7502
  logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
6690
7503
  logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
6691
- await fs4.remove(targetPath);
6692
- await fs4.copy(path19.join(backupDir, targetDir), targetPath);
6693
- await fs4.remove(backupDir);
7504
+ await fs5.remove(targetPath);
7505
+ await fs5.copy(path20.join(backupDir, targetDir), targetPath);
7506
+ await fs5.remove(backupDir);
6694
7507
  logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
6695
7508
  }
6696
7509
  }
@@ -6712,7 +7525,7 @@ var init_update = __esm({
6712
7525
  init_template_version();
6713
7526
  init_logger();
6714
7527
  init_utils();
6715
- updateCommand = new Command14("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) => {
7528
+ updateCommand = new Command15("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) => {
6716
7529
  try {
6717
7530
  logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
6718
7531
  logger.newLine();
@@ -6784,7 +7597,7 @@ var init_update = __esm({
6784
7597
  logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
6785
7598
  return;
6786
7599
  }
6787
- const answers = await inquirer10.prompt([
7600
+ const answers = await inquirer11.prompt([
6788
7601
  {
6789
7602
  type: "confirm",
6790
7603
  name: "shouldUpdate",
@@ -6809,8 +7622,8 @@ var init_update = __esm({
6809
7622
  });
6810
7623
 
6811
7624
  // src/commands/config.ts
6812
- import { Command as Command15 } from "commander";
6813
- import inquirer11 from "inquirer";
7625
+ import { Command as Command16 } from "commander";
7626
+ import inquirer12 from "inquirer";
6814
7627
  import chalk2 from "chalk";
6815
7628
  var setTokenCommand, showConfigCommand, removeConfigCommand, validateTokenCommand, configCommand;
6816
7629
  var init_config = __esm({
@@ -6820,13 +7633,13 @@ var init_config = __esm({
6820
7633
  init_user_config();
6821
7634
  init_gitlab_api();
6822
7635
  init_logger();
6823
- setTokenCommand = new Command15("set-token").description("\u8BBE\u7F6E GitLab Access Token").option("-t, --token <token>", "Access Token").option("-u, --url <url>", "GitLab Base URL", "https://gitlab.com").action(async (options) => {
7636
+ setTokenCommand = new Command16("set-token").description("\u8BBE\u7F6E GitLab Access Token").option("-t, --token <token>", "Access Token").option("-u, --url <url>", "GitLab Base URL", "https://gitlab.com").action(async (options) => {
6824
7637
  try {
6825
7638
  logger.header("GitLab Access Token \u914D\u7F6E");
6826
7639
  logger.newLine();
6827
7640
  let { token, url } = options;
6828
7641
  if (!token) {
6829
- const answers = await inquirer11.prompt([
7642
+ const answers = await inquirer12.prompt([
6830
7643
  {
6831
7644
  type: "password",
6832
7645
  name: "token",
@@ -6888,7 +7701,7 @@ var init_config = __esm({
6888
7701
  process.exit(1);
6889
7702
  }
6890
7703
  });
6891
- showConfigCommand = new Command15("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
7704
+ showConfigCommand = new Command16("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
6892
7705
  try {
6893
7706
  logger.header("GitLab \u914D\u7F6E");
6894
7707
  logger.newLine();
@@ -6927,14 +7740,14 @@ var init_config = __esm({
6927
7740
  process.exit(1);
6928
7741
  }
6929
7742
  });
6930
- removeConfigCommand = new Command15("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
7743
+ removeConfigCommand = new Command16("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
6931
7744
  try {
6932
7745
  const hasConfig = await userConfigManager.hasConfig();
6933
7746
  if (!hasConfig) {
6934
7747
  logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
6935
7748
  return;
6936
7749
  }
6937
- const answers = await inquirer11.prompt([
7750
+ const answers = await inquirer12.prompt([
6938
7751
  {
6939
7752
  type: "confirm",
6940
7753
  name: "confirm",
@@ -6956,7 +7769,7 @@ var init_config = __esm({
6956
7769
  process.exit(1);
6957
7770
  }
6958
7771
  });
6959
- validateTokenCommand = new Command15("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
7772
+ validateTokenCommand = new Command16("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
6960
7773
  try {
6961
7774
  logger.header("\u9A8C\u8BC1 GitLab Token");
6962
7775
  logger.newLine();
@@ -6986,12 +7799,12 @@ var init_config = __esm({
6986
7799
  process.exit(1);
6987
7800
  }
6988
7801
  });
6989
- configCommand = new Command15("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
7802
+ configCommand = new Command16("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
6990
7803
  }
6991
7804
  });
6992
7805
 
6993
7806
  // src/commands/diff.ts
6994
- import { Command as Command16 } from "commander";
7807
+ import { Command as Command17 } from "commander";
6995
7808
  import chalk3 from "chalk";
6996
7809
  async function compareTemplate(projectPath, type, localConfig, remoteTag, remoteBranch, gitlabConfig) {
6997
7810
  try {
@@ -7170,7 +7983,7 @@ var init_diff = __esm({
7170
7983
  init_utils();
7171
7984
  init_user_config();
7172
7985
  init_gitlab_api();
7173
- diffCommand = new Command16("diff").description("\u5BF9\u6BD4\u672C\u5730\u4E0E\u8FDC\u7A0B\u6A21\u677F\u5DEE\u5F02").option("-f, --frontend", "\u5BF9\u6BD4\u524D\u7AEF\u6A21\u677F").option("-b, --backend", "\u5BF9\u6BD4\u540E\u7AEF\u6A21\u677F").option("-t, --tag <tag>", "\u6307\u5B9A\u8FDC\u7A0B\u6807\u7B7E").option("-B, --branch <branch>", "\u6307\u5B9A\u8FDC\u7A0B\u5206\u652F").option("-o, --output <format>", "\u8F93\u51FA\u683C\u5F0F (table|json|diff)", "table").action(async (options) => {
7986
+ diffCommand = new Command17("diff").description("\u5BF9\u6BD4\u672C\u5730\u4E0E\u8FDC\u7A0B\u6A21\u677F\u5DEE\u5F02").option("-f, --frontend", "\u5BF9\u6BD4\u524D\u7AEF\u6A21\u677F").option("-b, --backend", "\u5BF9\u6BD4\u540E\u7AEF\u6A21\u677F").option("-t, --tag <tag>", "\u6307\u5B9A\u8FDC\u7A0B\u6807\u7B7E").option("-B, --branch <branch>", "\u6307\u5B9A\u8FDC\u7A0B\u5206\u652F").option("-o, --output <format>", "\u8F93\u51FA\u683C\u5F0F (table|json|diff)", "table").action(async (options) => {
7174
7987
  try {
7175
7988
  logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
7176
7989
  logger.newLine();
@@ -7292,10 +8105,10 @@ var init_diff = __esm({
7292
8105
 
7293
8106
  // src/index.ts
7294
8107
  var index_exports = {};
7295
- import { Command as Command17 } from "commander";
8108
+ import { Command as Command18 } from "commander";
7296
8109
  import chalk4 from "chalk";
7297
- import fs5 from "fs-extra";
7298
- import path20 from "path";
8110
+ import fs6 from "fs-extra";
8111
+ import path21 from "path";
7299
8112
  import { fileURLToPath as fileURLToPath2 } from "url";
7300
8113
  function showHelp() {
7301
8114
  console.log("");
@@ -7307,6 +8120,7 @@ function showHelp() {
7307
8120
  console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
7308
8121
  console.log(" team-cli dev \u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1");
7309
8122
  console.log(" team-cli accept [spec-file] \u9A8C\u6536\u529F\u80FD\uFF0C\u8D70\u67E5\u6240\u6709\u9700\u6C42");
8123
+ console.log(" team-cli add-module \u4E3A\u5DF2\u6709\u9879\u76EE\u65B0\u589E\u6A21\u5757");
7310
8124
  console.log(" team-cli add-feature <name> \u6DFB\u52A0\u65B0\u529F\u80FD");
7311
8125
  console.log(" team-cli bugfix \u521B\u5EFA Bugfix \u8BB0\u5F55");
7312
8126
  console.log(" team-cli hotfix \u521B\u5EFA Hotfix");
@@ -7367,6 +8181,7 @@ var init_index = __esm({
7367
8181
  init_bugfix();
7368
8182
  init_bugfix();
7369
8183
  init_accept();
8184
+ init_add_module();
7370
8185
  init_lint();
7371
8186
  init_status();
7372
8187
  init_detect_deps();
@@ -7376,9 +8191,9 @@ var init_index = __esm({
7376
8191
  init_update();
7377
8192
  init_config();
7378
8193
  init_diff();
7379
- __dirname2 = path20.dirname(fileURLToPath2(import.meta.url));
7380
- pkg = fs5.readJsonSync(path20.join(__dirname2, "../package.json"));
7381
- program = new Command17();
8194
+ __dirname2 = path21.dirname(fileURLToPath2(import.meta.url));
8195
+ pkg = fs6.readJsonSync(path21.join(__dirname2, "../package.json"));
8196
+ program = new Command18();
7382
8197
  program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
7383
8198
  program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
7384
8199
  program.addCommand(initCommand);
@@ -7389,6 +8204,7 @@ var init_index = __esm({
7389
8204
  program.addCommand(bugfixCommand);
7390
8205
  program.addCommand(hotfixCommand);
7391
8206
  program.addCommand(acceptCommand);
8207
+ program.addCommand(addModuleCommand);
7392
8208
  program.addCommand(lintCommand);
7393
8209
  program.addCommand(statusCommand);
7394
8210
  program.addCommand(detectDepsCommand);