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 +1647 -831
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1087 -278
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
1346
|
+
if (module.backendFragments) {
|
|
1345
1347
|
const backendJavaDir = path5.join(projectPath, "backend/src/main/java");
|
|
1346
|
-
const
|
|
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(
|
|
1385
|
+
const targetPath = path5.join(frontendSrcDir, fragment.target);
|
|
1350
1386
|
if (await FileUtils.exists(sourcePath)) {
|
|
1351
|
-
await FileUtils.
|
|
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/
|
|
1384
|
-
|
|
1385
|
-
|
|
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
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
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
|
|
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
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
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
|
-
|
|
1490
|
-
throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
1612
|
+
return true;
|
|
1491
1613
|
}
|
|
1492
1614
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
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
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
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 |
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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(
|
|
2454
|
-
await FileUtils.ensureDir(
|
|
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 =
|
|
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(
|
|
2497
|
-
await fs3.writeFile(
|
|
2498
|
-
await fs3.chmod(
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
2581
|
-
await FileUtils.ensureDir(
|
|
2582
|
-
await FileUtils.ensureDir(
|
|
2583
|
-
await FileUtils.ensureDir(
|
|
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: "\
|
|
2603
|
+
title: "\u521B\u5EFA\u540E\u7AEF\u7ED3\u6784",
|
|
2597
2604
|
task: async () => {
|
|
2598
|
-
|
|
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
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
2899
|
+
ctx.selectedFile = path7.join("docs/specs", selectedFile);
|
|
2896
2900
|
return;
|
|
2897
2901
|
}
|
|
2898
|
-
const fullPath = specFile.startsWith("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
|
|
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 =
|
|
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 =
|
|
3333
|
-
const logFile =
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
3957
|
+
import path10 from "path";
|
|
3954
3958
|
import { Listr as Listr4 } from "listr2";
|
|
3955
3959
|
async function processSinglePrd(prdFile) {
|
|
3956
|
-
const baseName =
|
|
3960
|
+
const baseName = path10.basename(prdFile, path10.extname(prdFile));
|
|
3957
3961
|
const featureSlug = StringUtils.toKebabCase(baseName);
|
|
3958
|
-
const specFile =
|
|
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) =>
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
4063
|
-
const gitDir =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 :
|
|
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 =
|
|
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 =
|
|
5010
|
+
const specName = path12.basename(result.specFile, ".md");
|
|
5007
5011
|
const milestoneSafe = result.milestone.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5008
|
-
const reportFile =
|
|
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 =
|
|
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
|
|
5263
|
-
logger.
|
|
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(`\
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5943
|
+
import { Command as Command11 } from "commander";
|
|
5565
5944
|
import path15 from "path";
|
|
5566
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
const parts =
|
|
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
|
-
|
|
6466
|
+
path22 = parts.slice(1).join("/");
|
|
6088
6467
|
}
|
|
6089
|
-
|
|
6090
|
-
return
|
|
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
|
|
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
|
|
6513
|
+
import { Command as Command13 } from "commander";
|
|
6135
6514
|
import path17 from "path";
|
|
6136
|
-
import
|
|
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
|
|
6761
|
+
const path22 = match[2].trim();
|
|
6383
6762
|
const description = match[3].trim();
|
|
6384
|
-
const key = `${method}:${
|
|
6385
|
-
apis.set(key, { method, path:
|
|
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
|
|
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
|
|
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
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
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
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
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
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
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/
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
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
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
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
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
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
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
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
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
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
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
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
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
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
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
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
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
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
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
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
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
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
|
|
6601
|
-
import
|
|
7413
|
+
import { Command as Command15 } from "commander";
|
|
7414
|
+
import path20 from "path";
|
|
6602
7415
|
import { execa as execa4 } from "execa";
|
|
6603
|
-
import
|
|
6604
|
-
import
|
|
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 =
|
|
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 =
|
|
6643
|
-
await
|
|
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 =
|
|
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 =
|
|
7483
|
+
const filePath = path20.join(targetPath, file);
|
|
6671
7484
|
try {
|
|
6672
|
-
await
|
|
7485
|
+
await fs5.remove(filePath);
|
|
6673
7486
|
} catch {
|
|
6674
7487
|
}
|
|
6675
7488
|
}
|
|
6676
7489
|
}
|
|
6677
|
-
await
|
|
7490
|
+
await fs5.copy(tempDir, targetPath, {
|
|
6678
7491
|
filter: (src) => !src.includes(".git")
|
|
6679
7492
|
});
|
|
6680
|
-
await
|
|
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
|
|
6692
|
-
await
|
|
6693
|
-
await
|
|
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
|
|
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
|
|
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
|
|
6813
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8108
|
+
import { Command as Command18 } from "commander";
|
|
7296
8109
|
import chalk4 from "chalk";
|
|
7297
|
-
import
|
|
7298
|
-
import
|
|
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 =
|
|
7380
|
-
pkg =
|
|
7381
|
-
program = new
|
|
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);
|