yg-team-cli 2.6.3 → 2.6.4
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 +1425 -752
- package/dist/cli.js.map +1 -1
- package/dist/index.js +935 -269
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1003,13 +1003,6 @@ async function saveTemplateConfig(projectPath, config) {
|
|
|
1003
1003
|
const content = JSON.stringify(config, null, 2);
|
|
1004
1004
|
await FileUtils.write(configPath, content);
|
|
1005
1005
|
}
|
|
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
1006
|
async function getLatestCommit(repository) {
|
|
1014
1007
|
try {
|
|
1015
1008
|
const { stdout } = await execa2("git", ["ls-remote", repository, "HEAD"], {
|
|
@@ -1380,455 +1373,222 @@ var init_module_registry = __esm({
|
|
|
1380
1373
|
}
|
|
1381
1374
|
});
|
|
1382
1375
|
|
|
1383
|
-
// src/
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
UserConfigManager: () => UserConfigManager,
|
|
1387
|
-
userConfigManager: () => userConfigManager
|
|
1388
|
-
});
|
|
1376
|
+
// src/commands/init.ts
|
|
1377
|
+
import { Command } from "commander";
|
|
1378
|
+
import inquirer from "inquirer";
|
|
1389
1379
|
import path6 from "path";
|
|
1380
|
+
import fs3 from "fs-extra";
|
|
1390
1381
|
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);
|
|
1382
|
+
import { Listr } from "listr2";
|
|
1383
|
+
async function collectProjectConfig(defaultProjectName) {
|
|
1384
|
+
let projectName = defaultProjectName;
|
|
1385
|
+
if (!projectName) {
|
|
1386
|
+
const nameAnswer = await inquirer.prompt([
|
|
1387
|
+
{
|
|
1388
|
+
type: "input",
|
|
1389
|
+
name: "projectName",
|
|
1390
|
+
message: "\u8BF7\u8F93\u5165\u9879\u76EE\u76EE\u5F55\u540D\u79F0:",
|
|
1391
|
+
default: "my-project",
|
|
1392
|
+
validate: (input) => {
|
|
1393
|
+
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
1394
|
+
return "\u9879\u76EE\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
|
|
1418
1395
|
}
|
|
1419
|
-
return
|
|
1420
|
-
} catch (error) {
|
|
1421
|
-
logger.debug(`\u52A0\u8F7D\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
1422
|
-
return null;
|
|
1396
|
+
return true;
|
|
1423
1397
|
}
|
|
1424
1398
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1399
|
+
]);
|
|
1400
|
+
projectName = nameAnswer.projectName;
|
|
1401
|
+
}
|
|
1402
|
+
const typeAnswer = await inquirer.prompt([
|
|
1403
|
+
{
|
|
1404
|
+
type: "list",
|
|
1405
|
+
name: "projectType",
|
|
1406
|
+
message: "\u8BF7\u9009\u62E9\u9879\u76EE\u7C7B\u578B:",
|
|
1407
|
+
choices: [
|
|
1408
|
+
{ name: "\u5355\u6A21\u5757\u9879\u76EE", value: "single" },
|
|
1409
|
+
{ name: "\u591A\u6A21\u5757\u9879\u76EE (Gradle)", value: "multi" }
|
|
1410
|
+
],
|
|
1411
|
+
default: "single"
|
|
1412
|
+
}
|
|
1413
|
+
]);
|
|
1414
|
+
let rootProjectName = projectName;
|
|
1415
|
+
if (typeAnswer.projectType === "multi") {
|
|
1416
|
+
const rootAnswer = await inquirer.prompt([
|
|
1417
|
+
{
|
|
1418
|
+
type: "input",
|
|
1419
|
+
name: "rootProjectName",
|
|
1420
|
+
message: "\u8BF7\u8F93\u5165\u6839\u9879\u76EE\u540D (settings.gradle rootProject.name):",
|
|
1421
|
+
default: projectName,
|
|
1422
|
+
validate: (input) => {
|
|
1423
|
+
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
1424
|
+
return "\u6839\u9879\u76EE\u540D\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
|
|
1435
1425
|
}
|
|
1436
|
-
|
|
1437
|
-
await FileUtils.write(this.configPath, content);
|
|
1438
|
-
} catch (error) {
|
|
1439
|
-
throw new Error(`\u4FDD\u5B58\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
1426
|
+
return true;
|
|
1440
1427
|
}
|
|
1441
1428
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
if (baseUrl) {
|
|
1455
|
-
config.gitlab.baseUrl = baseUrl;
|
|
1429
|
+
]);
|
|
1430
|
+
rootProjectName = rootAnswer.rootProjectName;
|
|
1431
|
+
}
|
|
1432
|
+
const groupAnswer = await inquirer.prompt([
|
|
1433
|
+
{
|
|
1434
|
+
type: "input",
|
|
1435
|
+
name: "groupId",
|
|
1436
|
+
message: "\u8BF7\u8F93\u5165 Group ID (\u5305\u8DEF\u5F84\u524D\u7F00):",
|
|
1437
|
+
default: "org.yungu",
|
|
1438
|
+
validate: (input) => {
|
|
1439
|
+
if (!/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/.test(input)) {
|
|
1440
|
+
return "Group ID \u683C\u5F0F\u4E0D\u6B63\u786E (\u5982: com.example \u6216 org.yungu)";
|
|
1456
1441
|
}
|
|
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;
|
|
1442
|
+
return true;
|
|
1472
1443
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1444
|
+
}
|
|
1445
|
+
]);
|
|
1446
|
+
const groupId = groupAnswer.groupId;
|
|
1447
|
+
const modules = [];
|
|
1448
|
+
let addMoreModules = true;
|
|
1449
|
+
let moduleIndex = 1;
|
|
1450
|
+
while (addMoreModules) {
|
|
1451
|
+
let defaultModuleName = `module-${moduleIndex}`;
|
|
1452
|
+
if (moduleIndex === 1) {
|
|
1453
|
+
if (typeAnswer.projectType === "single") {
|
|
1454
|
+
defaultModuleName = "app";
|
|
1455
|
+
} else {
|
|
1456
|
+
defaultModuleName = projectName;
|
|
1479
1457
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1458
|
+
}
|
|
1459
|
+
const moduleNameAnswer = await inquirer.prompt([
|
|
1460
|
+
{
|
|
1461
|
+
type: "input",
|
|
1462
|
+
name: "moduleName",
|
|
1463
|
+
message: `\u8BF7\u8F93\u5165\u7B2C ${moduleIndex} \u4E2A\u5B50\u6A21\u5757\u540D\u79F0 (\u76EE\u5F55\u540D):`,
|
|
1464
|
+
default: defaultModuleName,
|
|
1465
|
+
validate: (input) => {
|
|
1466
|
+
if (!/^[a-z][a-z0-9-]*$/.test(input)) {
|
|
1467
|
+
return "\u6A21\u5757\u540D\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u8FDE\u5B57\u7B26";
|
|
1488
1468
|
}
|
|
1489
|
-
|
|
1490
|
-
throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
1469
|
+
return true;
|
|
1491
1470
|
}
|
|
1492
1471
|
}
|
|
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;
|
|
1472
|
+
]);
|
|
1473
|
+
const moduleName = moduleNameAnswer.moduleName;
|
|
1474
|
+
let moduleType = "service";
|
|
1475
|
+
if (typeAnswer.projectType === "multi") {
|
|
1476
|
+
const typeSelectAnswer = await inquirer.prompt([
|
|
1477
|
+
{
|
|
1478
|
+
type: "list",
|
|
1479
|
+
name: "moduleType",
|
|
1480
|
+
message: `\u6A21\u5757 "${moduleName}" \u7C7B\u578B:`,
|
|
1481
|
+
choices: [
|
|
1482
|
+
{ name: "API \u670D\u52A1\u6A21\u5757 (\u72EC\u7ACB\u90E8\u7F72)", value: "service" },
|
|
1483
|
+
{ name: "\u516C\u5171\u6A21\u5757 (\u88AB\u5176\u4ED6\u6A21\u5757\u4F9D\u8D56)", value: "common" },
|
|
1484
|
+
{ name: "API \u63A5\u53E3\u6A21\u5757 (\u4EC5\u63A5\u53E3\u5B9A\u4E49)", value: "api" }
|
|
1485
|
+
],
|
|
1486
|
+
default: "service"
|
|
1519
1487
|
}
|
|
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();
|
|
1488
|
+
]);
|
|
1489
|
+
moduleType = typeSelectAnswer.moduleType;
|
|
1490
|
+
}
|
|
1491
|
+
const modulePackage = `${groupId}.${moduleName}`;
|
|
1492
|
+
const applicationClass = `${toPascalCase(moduleName)}Application`;
|
|
1493
|
+
modules.push({
|
|
1494
|
+
name: moduleName,
|
|
1495
|
+
type: moduleType,
|
|
1496
|
+
packagePath: modulePackage,
|
|
1497
|
+
applicationClass
|
|
1498
|
+
});
|
|
1499
|
+
if (typeAnswer.projectType === "multi") {
|
|
1500
|
+
const moreAnswer = await inquirer.prompt([
|
|
1501
|
+
{
|
|
1502
|
+
type: "confirm",
|
|
1503
|
+
name: "addMore",
|
|
1504
|
+
message: "\u662F\u5426\u8FD8\u9700\u8981\u6DFB\u52A0\u66F4\u591A\u6A21\u5757?",
|
|
1505
|
+
default: false
|
|
1506
|
+
}
|
|
1507
|
+
]);
|
|
1508
|
+
addMoreModules = moreAnswer.addMore;
|
|
1509
|
+
} else {
|
|
1510
|
+
addMoreModules = false;
|
|
1511
|
+
}
|
|
1512
|
+
moduleIndex++;
|
|
1546
1513
|
}
|
|
1547
|
-
|
|
1514
|
+
const javaAnswer = await inquirer.prompt([
|
|
1515
|
+
{
|
|
1516
|
+
type: "list",
|
|
1517
|
+
name: "javaVersion",
|
|
1518
|
+
message: "\u8BF7\u9009\u62E9 Java \u7248\u672C:",
|
|
1519
|
+
choices: [
|
|
1520
|
+
{ name: "Java 8 (1.8)", value: "1.8" },
|
|
1521
|
+
{ name: "Java 11 (LTS)", value: "11" },
|
|
1522
|
+
{ name: "Java 17 (LTS)", value: "17" },
|
|
1523
|
+
{ name: "Java 21 (LTS)", value: "21" }
|
|
1524
|
+
],
|
|
1525
|
+
default: "17"
|
|
1526
|
+
}
|
|
1527
|
+
]);
|
|
1528
|
+
const buildAnswer = await inquirer.prompt([
|
|
1529
|
+
{
|
|
1530
|
+
type: "list",
|
|
1531
|
+
name: "buildTool",
|
|
1532
|
+
message: "\u8BF7\u9009\u62E9\u6784\u5EFA\u5DE5\u5177:",
|
|
1533
|
+
choices: [
|
|
1534
|
+
{ name: "Gradle", value: "gradle" },
|
|
1535
|
+
{ name: "Maven", value: "maven" }
|
|
1536
|
+
],
|
|
1537
|
+
default: "gradle"
|
|
1538
|
+
}
|
|
1539
|
+
]);
|
|
1540
|
+
const frontendAnswer = await inquirer.prompt([
|
|
1541
|
+
{
|
|
1542
|
+
type: "confirm",
|
|
1543
|
+
name: "includeFrontend",
|
|
1544
|
+
message: "\u662F\u5426\u9700\u8981\u5305\u542B\u524D\u7AEF\u9879\u76EE?",
|
|
1545
|
+
default: true
|
|
1546
|
+
}
|
|
1547
|
+
]);
|
|
1548
|
+
return {
|
|
1549
|
+
projectName,
|
|
1550
|
+
projectType: typeAnswer.projectType,
|
|
1551
|
+
rootProjectName,
|
|
1552
|
+
groupId,
|
|
1553
|
+
modules,
|
|
1554
|
+
javaVersion: javaAnswer.javaVersion,
|
|
1555
|
+
buildTool: buildAnswer.buildTool,
|
|
1556
|
+
includeFrontend: frontendAnswer.includeFrontend
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
function toPascalCase(str) {
|
|
1560
|
+
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
1561
|
+
}
|
|
1562
|
+
async function generateTechStack(projectPath, config) {
|
|
1563
|
+
const backendStructure = config.modules.map((m) => {
|
|
1564
|
+
const packagePath = `${config.groupId}.${m.name}`;
|
|
1565
|
+
return `\u251C\u2500\u2500 ${m.name}/
|
|
1566
|
+
\u2502 \u2514\u2500\u2500 src/main/java/${packagePath.replace(/\./g, "/")}/
|
|
1567
|
+
\u2502 \u251C\u2500\u2500 controller/ # API \u63A7\u5236\u5668
|
|
1568
|
+
\u2502 \u251C\u2500\u2500 service/ # \u4E1A\u52A1\u903B\u8F91
|
|
1569
|
+
\u2502 \u251C\u2500\u2500 mapper/ # \u6570\u636E\u8BBF\u95EE
|
|
1570
|
+
\u2502 \u251C\u2500\u2500 entity/ # \u6570\u636E\u6A21\u578B
|
|
1571
|
+
\u2502 \u251C\u2500\u2500 dto/ # \u6570\u636E\u4F20\u8F93\u5BF9\u8C61
|
|
1572
|
+
\u2502 \u2514\u2500\u2500 config/ # \u914D\u7F6E\u7C7B`;
|
|
1573
|
+
}).join("\n");
|
|
1574
|
+
const content = `# \u6280\u672F\u6808
|
|
1548
1575
|
|
|
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
|
-
});
|
|
1576
|
+
## \u9879\u76EE\u914D\u7F6E
|
|
1816
1577
|
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
const content = `# \u6280\u672F\u6808
|
|
1578
|
+
| \u914D\u7F6E\u9879 | \u503C |
|
|
1579
|
+
|-------|------|
|
|
1580
|
+
| \u9879\u76EE\u540D\u79F0 | ${config.projectName} |
|
|
1581
|
+
| Group ID | ${config.groupId} |
|
|
1582
|
+
| \u6839\u9879\u76EE\u540D | ${config.rootProjectName} |
|
|
1583
|
+
| \u6A21\u5757 | ${config.modules.map((m) => m.name).join(", ")} |
|
|
1584
|
+
| Java \u7248\u672C | ${config.javaVersion} |
|
|
1585
|
+
| \u6784\u5EFA\u5DE5\u5177 | ${config.buildTool === "gradle" ? "Gradle" : "Maven"} |
|
|
1826
1586
|
|
|
1827
1587
|
## \u540E\u7AEF\u6280\u672F\u6808
|
|
1828
1588
|
|
|
1829
1589
|
| \u7EC4\u4EF6 | \u6280\u672F\u9009\u578B | \u7248\u672C | \u8BF4\u660E |
|
|
1830
1590
|
|------|---------|------|------|
|
|
1831
|
-
| \u8BED\u8A00 | Java |
|
|
1591
|
+
| \u8BED\u8A00 | Java | ${config.javaVersion} | LTS \u7248\u672C |
|
|
1832
1592
|
| \u6846\u67B6 | Spring Boot | 3.2 | \u73B0\u4EE3\u5316 Java \u6846\u67B6 |
|
|
1833
1593
|
| \u6784\u5EFA\u5DE5\u5177 | Gradle | 8.x | \u5FEB\u901F\u3001\u7075\u6D3B\u7684\u6784\u5EFA\u5DE5\u5177 |
|
|
1834
1594
|
| ORM | MyBatis Plus | 3.5 | \u589E\u5F3A MyBatis\uFF0C\u7B80\u5316 CRUD |
|
|
@@ -1836,7 +1596,7 @@ async function generateTechStack(projectPath) {
|
|
|
1836
1596
|
| \u7F13\u5B58 | Redis | 7.x | \u7F13\u5B58\u548C\u4F1A\u8BDD\u5B58\u50A8 |
|
|
1837
1597
|
| \u6587\u6863 | SpringDoc OpenAPI | 2.3 | API \u6587\u6863\u81EA\u52A8\u751F\u6210 |
|
|
1838
1598
|
|
|
1839
|
-
|
|
1599
|
+
${config.includeFrontend ? `## \u524D\u7AEF\u6280\u672F\u6808
|
|
1840
1600
|
|
|
1841
1601
|
| \u7EC4\u4EF6 | \u6280\u672F\u9009\u578B | \u7248\u672C | \u8BF4\u660E |
|
|
1842
1602
|
|------|---------|------|------|
|
|
@@ -1847,7 +1607,7 @@ async function generateTechStack(projectPath) {
|
|
|
1847
1607
|
| \u56FE\u6807 | Lucide React | latest | \u4E00\u81F4\u6027\u56FE\u6807\u5E93 |
|
|
1848
1608
|
| \u72B6\u6001\u7BA1\u7406 | React Context + Hooks | - | \u8F7B\u91CF\u7EA7\u72B6\u6001\u7BA1\u7406 |
|
|
1849
1609
|
| \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
|
|
1610
|
+
| \u6570\u636E\u9A8C\u8BC1 | Zod | 3.x | TypeScript \u4F18\u5148\u7684\u9A8C\u8BC1\u5E93 |` : ""}
|
|
1851
1611
|
|
|
1852
1612
|
## \u5F00\u53D1\u5DE5\u5177
|
|
1853
1613
|
|
|
@@ -1870,6 +1630,7 @@ async function generateTechStack(projectPath) {
|
|
|
1870
1630
|
- API \u8DEF\u5F84\u4F7F\u7528 kebab-case
|
|
1871
1631
|
- \u7C7B\u540D\u4F7F\u7528 PascalCase
|
|
1872
1632
|
- \u65B9\u6CD5\u540D\u4F7F\u7528 camelCase
|
|
1633
|
+
- \u5305\u8DEF\u5F84: \`${config.groupId}.<module-name>\`
|
|
1873
1634
|
|
|
1874
1635
|
### \u524D\u7AEF\u89C4\u8303
|
|
1875
1636
|
|
|
@@ -1890,26 +1651,19 @@ async function generateTechStack(projectPath) {
|
|
|
1890
1651
|
|
|
1891
1652
|
\`\`\`
|
|
1892
1653
|
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
|
|
1654
|
+
${backendStructure}
|
|
1901
1655
|
\u251C\u2500\u2500 src/main/resources/
|
|
1902
1656
|
\u2502 \u251C\u2500\u2500 mapper/ # MyBatis XML
|
|
1903
1657
|
\u2502 \u2514\u2500\u2500 application.yml # \u914D\u7F6E\u6587\u4EF6
|
|
1904
1658
|
\u2514\u2500\u2500 src/test/ # \u6D4B\u8BD5\u4EE3\u7801
|
|
1905
1659
|
|
|
1906
|
-
frontend/
|
|
1660
|
+
${config.includeFrontend ? `frontend/
|
|
1907
1661
|
\u251C\u2500\u2500 src/
|
|
1908
1662
|
\u2502 \u251C\u2500\u2500 app/ # Next.js App Router
|
|
1909
1663
|
\u2502 \u251C\u2500\u2500 components/ # React \u7EC4\u4EF6
|
|
1910
1664
|
\u2502 \u251C\u2500\u2500 lib/ # \u5DE5\u5177\u5E93
|
|
1911
1665
|
\u2502 \u2514\u2500\u2500 types/ # TypeScript \u7C7B\u578B
|
|
1912
|
-
\u2514\u2500\u2500 public/ # \u9759\u6001\u8D44\u6E90
|
|
1666
|
+
\u2514\u2500\u2500 public/ # \u9759\u6001\u8D44\u6E90` : ""}
|
|
1913
1667
|
|
|
1914
1668
|
docs/
|
|
1915
1669
|
\u251C\u2500\u2500 prd-docs/ # PRD \u9700\u6C42\u6587\u6863
|
|
@@ -1918,20 +1672,28 @@ docs/
|
|
|
1918
1672
|
\u2514\u2500\u2500 sessions/ # \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
|
|
1919
1673
|
\`\`\`
|
|
1920
1674
|
`;
|
|
1921
|
-
await FileUtils.write(
|
|
1675
|
+
await FileUtils.write(path6.join(projectPath, "TECH_STACK.md"), content);
|
|
1922
1676
|
}
|
|
1923
|
-
async function generateConventions(projectPath) {
|
|
1677
|
+
async function generateConventions(projectPath, config) {
|
|
1924
1678
|
const content = `# \u5F00\u53D1\u89C4\u8303
|
|
1925
1679
|
|
|
1680
|
+
## \u5305\u8DEF\u5F84\u89C4\u8303
|
|
1681
|
+
|
|
1682
|
+
\u6240\u6709\u540E\u7AEF\u4EE3\u7801\u7684\u5305\u8DEF\u5F84\u9075\u5FAA: \`${config.groupId}.<module-name>\`
|
|
1683
|
+
|
|
1684
|
+
\u793A\u4F8B:
|
|
1685
|
+
${config.modules.map((m) => `- \`${m.packagePath}\``).join("\n")}
|
|
1686
|
+
|
|
1687
|
+
|
|
1926
1688
|
## \u540E\u7AEF\u5F00\u53D1\u89C4\u8303
|
|
1927
1689
|
|
|
1928
1690
|
### 1. \u5206\u5C42\u67B6\u6784
|
|
1929
1691
|
|
|
1930
|
-
|
|
1692
|
+
\`
|
|
1931
1693
|
Controller \u2192 Service \u2192 Mapper \u2192 Database
|
|
1932
1694
|
\u2193 \u2193
|
|
1933
1695
|
DTO Entity
|
|
1934
|
-
|
|
1696
|
+
\`
|
|
1935
1697
|
|
|
1936
1698
|
**\u804C\u8D23\u5212\u5206:**
|
|
1937
1699
|
- **Controller**: \u63A5\u6536 HTTP \u8BF7\u6C42\uFF0C\u53C2\u6570\u6821\u9A8C\uFF0C\u8C03\u7528 Service
|
|
@@ -1954,13 +1716,13 @@ Controller \u2192 Service \u2192 Mapper \u2192 Database
|
|
|
1954
1716
|
|
|
1955
1717
|
**RESTful \u98CE\u683C:**
|
|
1956
1718
|
|
|
1957
|
-
|
|
1719
|
+
\`
|
|
1958
1720
|
GET /api/users # \u5217\u8868
|
|
1959
1721
|
GET /api/users/{id} # \u8BE6\u60C5
|
|
1960
1722
|
POST /api/users # \u521B\u5EFA
|
|
1961
1723
|
PUT /api/users/{id} # \u66F4\u65B0
|
|
1962
1724
|
DELETE /api/users/{id} # \u5220\u9664
|
|
1963
|
-
|
|
1725
|
+
\`
|
|
1964
1726
|
|
|
1965
1727
|
**\u7EDF\u4E00\u54CD\u5E94\u683C\u5F0F:**
|
|
1966
1728
|
|
|
@@ -2221,7 +1983,7 @@ test('renders user name', () => {
|
|
|
2221
1983
|
});
|
|
2222
1984
|
\`\`\`
|
|
2223
1985
|
`;
|
|
2224
|
-
await FileUtils.write(
|
|
1986
|
+
await FileUtils.write(path6.join(projectPath, "CONVENTIONS.md"), content);
|
|
2225
1987
|
}
|
|
2226
1988
|
async function generateAIMemory(projectPath, projectName) {
|
|
2227
1989
|
const content = `# AI Memory - \u9879\u76EE\u72B6\u6001\u8BB0\u5F55
|
|
@@ -2280,7 +2042,7 @@ async function generateAIMemory(projectPath, projectName) {
|
|
|
2280
2042
|
| Bug ID | \u65E5\u671F | \u95EE\u9898\u63CF\u8FF0 | \u72B6\u6001 |
|
|
2281
2043
|
|--------|------|---------|------|
|
|
2282
2044
|
`;
|
|
2283
|
-
await FileUtils.write(
|
|
2045
|
+
await FileUtils.write(path6.join(projectPath, "AI_MEMORY.md"), content);
|
|
2284
2046
|
}
|
|
2285
2047
|
async function generateSpecTemplate(projectPath) {
|
|
2286
2048
|
const content = `# [\u529F\u80FD\u6807\u9898]
|
|
@@ -2344,82 +2106,10 @@ async function generateSpecTemplate(projectPath) {
|
|
|
2344
2106
|
----
|
|
2345
2107
|
*\u751F\u6210\u4E8E: {{TIMESTAMP}} by team-cli*
|
|
2346
2108
|
`;
|
|
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
|
-
}
|
|
2109
|
+
await FileUtils.write(path6.join(projectPath, "docs/specs/template.md"), content);
|
|
2420
2110
|
}
|
|
2421
2111
|
async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
|
|
2422
|
-
const frontendPath =
|
|
2112
|
+
const frontendPath = path6.join(projectPath, "frontend");
|
|
2423
2113
|
const templates = getDefaultTemplates();
|
|
2424
2114
|
const repository = templates.frontend.repository;
|
|
2425
2115
|
try {
|
|
@@ -2430,7 +2120,7 @@ async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
|
|
|
2430
2120
|
}
|
|
2431
2121
|
const targetVersion = versionOptions.branch || versionOptions.tag || latestTag;
|
|
2432
2122
|
logger.info(`\u6B63\u5728\u514B\u9686\u524D\u7AEF\u6A21\u677F (${targetVersion || "HEAD"})...`);
|
|
2433
|
-
const tempDir =
|
|
2123
|
+
const tempDir = path6.join(os2.tmpdir(), `team-cli-fe-${Date.now()}`);
|
|
2434
2124
|
await FileUtils.ensureDir(tempDir);
|
|
2435
2125
|
const cloneArgs = ["clone", "--depth", "1"];
|
|
2436
2126
|
if (targetVersion) {
|
|
@@ -2450,21 +2140,210 @@ async function cloneFrontendTemplate(projectPath, versionOptions = {}) {
|
|
|
2450
2140
|
logger.success("\u524D\u7AEF\u6A21\u677F\u514B\u9686\u5B8C\u6210");
|
|
2451
2141
|
} catch (error) {
|
|
2452
2142
|
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(
|
|
2143
|
+
await FileUtils.ensureDir(path6.join(frontendPath, "src/app"));
|
|
2144
|
+
await FileUtils.ensureDir(path6.join(frontendPath, "src/components"));
|
|
2455
2145
|
}
|
|
2456
2146
|
}
|
|
2457
|
-
async function
|
|
2458
|
-
const backendPath =
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2147
|
+
async function createBackendFromConfig(projectPath, config) {
|
|
2148
|
+
const backendPath = path6.join(projectPath, "backend");
|
|
2149
|
+
logger.info(`\u6B63\u5728\u521B\u5EFA\u540E\u7AEF\u591A\u6A21\u5757\u7ED3\u6784...`);
|
|
2150
|
+
await FileUtils.ensureDir(backendPath);
|
|
2151
|
+
await createSettingsGradle(backendPath, config);
|
|
2152
|
+
await createRootBuildGradle(backendPath, config);
|
|
2153
|
+
for (const module of config.modules) {
|
|
2154
|
+
await createModuleFromConfig(backendPath, module, config);
|
|
2155
|
+
}
|
|
2156
|
+
if (config.modules.length > 0) {
|
|
2157
|
+
await createCommonModule(backendPath, config);
|
|
2158
|
+
}
|
|
2159
|
+
logger.success("\u540E\u7AEF\u7ED3\u6784\u521B\u5EFA\u5B8C\u6210");
|
|
2160
|
+
}
|
|
2161
|
+
async function createSettingsGradle(backendPath, config) {
|
|
2162
|
+
const lines = [];
|
|
2163
|
+
lines.push(`rootProject.name = '${config.rootProjectName}'`);
|
|
2164
|
+
lines.push("");
|
|
2165
|
+
const allModules = ["common", ...config.modules.map((m) => m.name)].sort();
|
|
2166
|
+
for (const module of allModules) {
|
|
2167
|
+
lines.push(`include '${module}'`);
|
|
2462
2168
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2169
|
+
await FileUtils.write(path6.join(backendPath, "settings.gradle"), lines.join("\n"));
|
|
2170
|
+
logger.info("settings.gradle \u5DF2\u521B\u5EFA");
|
|
2171
|
+
}
|
|
2172
|
+
async function createRootBuildGradle(backendPath, config) {
|
|
2173
|
+
const content = `plugins {
|
|
2174
|
+
id 'java' version '8' apply false
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
allprojects {
|
|
2178
|
+
group = '${config.groupId}'
|
|
2179
|
+
version = '0.0.1-SNAPSHOT'
|
|
2180
|
+
|
|
2181
|
+
repositories {
|
|
2182
|
+
maven { url 'https://maven.aliyun.com/repository/public/' }
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
subprojects {
|
|
2187
|
+
apply plugin: 'java'
|
|
2188
|
+
apply plugin: 'org.springframework.boot'
|
|
2189
|
+
apply plugin: 'io.spring.dependency-management'
|
|
2190
|
+
|
|
2191
|
+
sourceCompatibility = '${config.javaVersion}'
|
|
2192
|
+
targetCompatibility = '${config.javaVersion}'
|
|
2193
|
+
|
|
2194
|
+
dependencies {
|
|
2195
|
+
implementation 'org.springframework.boot:spring-boot-starter-web'
|
|
2196
|
+
implementation 'org.springframework.boot:spring-boot-starter-logging'
|
|
2197
|
+
compileOnly "org.projectlombok:lombok"
|
|
2198
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
2199
|
+
testCompileOnly "org.projectlombok:lombok"
|
|
2200
|
+
testAnnotationProcessor "org.projectlombok:lombok"
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
`;
|
|
2204
|
+
await FileUtils.write(path6.join(backendPath, "build.gradle"), content);
|
|
2205
|
+
logger.info("build.gradle (root) \u5DF2\u521B\u5EFA");
|
|
2206
|
+
}
|
|
2207
|
+
async function createModuleFromConfig(backendPath, module, config) {
|
|
2208
|
+
const modulePath = path6.join(backendPath, module.name);
|
|
2209
|
+
const packagePath = module.packagePath.split(".");
|
|
2210
|
+
logger.info(`\u521B\u5EFA\u6A21\u5757: ${module.name} (${module.type})...`);
|
|
2211
|
+
const baseDirs = [
|
|
2212
|
+
"src/main/java",
|
|
2213
|
+
"src/main/resources",
|
|
2214
|
+
"src/test/java"
|
|
2215
|
+
];
|
|
2216
|
+
for (const dir of baseDirs) {
|
|
2217
|
+
await FileUtils.ensureDir(path6.join(modulePath, dir));
|
|
2218
|
+
}
|
|
2219
|
+
let subDirs = [];
|
|
2220
|
+
if (module.type === "service") {
|
|
2221
|
+
subDirs = ["controller", "service", "mapper", "entity", "dto", "config"];
|
|
2222
|
+
} else if (module.type === "api") {
|
|
2223
|
+
subDirs = ["model", "constant", "enums", "exception"];
|
|
2224
|
+
} else {
|
|
2225
|
+
subDirs = ["util", "common", "config", "manager"];
|
|
2226
|
+
}
|
|
2227
|
+
let javaPath = path6.join(modulePath, "src", "main", "java");
|
|
2228
|
+
for (const subDir of subDirs) {
|
|
2229
|
+
await FileUtils.ensureDir(path6.join(javaPath, ...packagePath, subDir));
|
|
2230
|
+
}
|
|
2231
|
+
await createApplicationClass(
|
|
2232
|
+
path6.join(javaPath, ...packagePath, "config", `${module.applicationClass}.java`),
|
|
2233
|
+
module.packagePath,
|
|
2234
|
+
module.applicationClass
|
|
2235
|
+
);
|
|
2236
|
+
await createModuleBuildGradle(modulePath, module, config);
|
|
2237
|
+
logger.success(`\u6A21\u5757 ${module.name} \u521B\u5EFA\u5B8C\u6210`);
|
|
2238
|
+
}
|
|
2239
|
+
async function createApplicationClass(filePath, packagePath, className) {
|
|
2240
|
+
const content = `package ${packagePath}.config;
|
|
2241
|
+
|
|
2242
|
+
import lombok.extern.slf4j.Slf4j;
|
|
2243
|
+
import org.springframework.boot.SpringApplication;
|
|
2244
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
2245
|
+
import org.springframework.context.ApplicationContext;
|
|
2246
|
+
import org.springframework.context.annotation.ComponentScan;
|
|
2247
|
+
|
|
2248
|
+
@SpringBootApplication
|
|
2249
|
+
@ComponentScan("${packagePath}")
|
|
2250
|
+
@Slf4j
|
|
2251
|
+
public class ${className} {
|
|
2252
|
+
|
|
2253
|
+
public static void main(String[] args) {
|
|
2254
|
+
ApplicationContext context = SpringApplication.run(${className}.class, args);
|
|
2255
|
+
log.info("${className} \u542F\u52A8\u6210\u529F!");
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
`;
|
|
2259
|
+
await FileUtils.ensureDir(path6.dirname(filePath));
|
|
2260
|
+
await FileUtils.write(filePath, content);
|
|
2261
|
+
}
|
|
2262
|
+
async function createModuleBuildGradle(modulePath, module, config) {
|
|
2263
|
+
let applyPlugins = `plugins {
|
|
2264
|
+
id 'java-library'
|
|
2265
|
+
id 'org.springframework.boot'
|
|
2266
|
+
id 'io.spring.dependency-management'
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
group = '${config.groupId}'
|
|
2270
|
+
version = '0.0.1-SNAPSHOT'
|
|
2271
|
+
|
|
2272
|
+
java {
|
|
2273
|
+
sourceCompatibility = '${config.javaVersion}'
|
|
2274
|
+
targetCompatibility = '${config.javaVersion}'
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
repositories {
|
|
2278
|
+
maven { url 'https://maven.aliyun.com/repository/public/' }
|
|
2279
|
+
}
|
|
2280
|
+
`;
|
|
2281
|
+
let dependencies = "";
|
|
2282
|
+
if (module.type === "service") {
|
|
2283
|
+
dependencies = `
|
|
2284
|
+
dependencies {
|
|
2285
|
+
implementation project(":common")
|
|
2286
|
+
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
|
|
2287
|
+
implementation 'com.alibaba:druid-spring-boot-starter'
|
|
2288
|
+
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
|
2289
|
+
compileOnly "org.projectlombok:lombok"
|
|
2290
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
2291
|
+
testCompileOnly "org.projectlombok:lombok"
|
|
2292
|
+
testAnnotationProcessor "org.projectlombok:lombok"
|
|
2293
|
+
}
|
|
2294
|
+
`;
|
|
2295
|
+
} else if (module.type === "api") {
|
|
2296
|
+
dependencies = `
|
|
2297
|
+
dependencies {
|
|
2298
|
+
compileOnly 'org.springframework.boot:spring-boot-starter'
|
|
2299
|
+
compileOnly "org.projectlombok:lombok"
|
|
2300
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
2301
|
+
}
|
|
2302
|
+
`;
|
|
2303
|
+
} else {
|
|
2304
|
+
dependencies = `
|
|
2305
|
+
dependencies {
|
|
2306
|
+
implementation 'org.springframework.boot:spring-boot-starter'
|
|
2307
|
+
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
|
|
2308
|
+
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
|
2309
|
+
compileOnly "org.projectlombok:lombok"
|
|
2310
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
2311
|
+
}
|
|
2312
|
+
`;
|
|
2313
|
+
}
|
|
2314
|
+
await FileUtils.write(path6.join(modulePath, "build.gradle"), applyPlugins + dependencies);
|
|
2315
|
+
}
|
|
2316
|
+
async function createCommonModule(backendPath, config) {
|
|
2317
|
+
const modulePath = path6.join(backendPath, "common");
|
|
2318
|
+
const packagePath = config.groupId.split(".");
|
|
2319
|
+
const subDirs = ["util", "common", "config", "manager", "constant", "enums", "exception"];
|
|
2320
|
+
logger.info("\u521B\u5EFA\u516C\u5171\u6A21\u5757: common...");
|
|
2321
|
+
for (const dir of ["src/main/java", "src/main/resources", "src/test/java"]) {
|
|
2322
|
+
await FileUtils.ensureDir(path6.join(modulePath, dir));
|
|
2323
|
+
}
|
|
2324
|
+
let javaPath = path6.join(modulePath, "src", "main", "java");
|
|
2325
|
+
for (const subDir of subDirs) {
|
|
2326
|
+
await FileUtils.ensureDir(path6.join(javaPath, ...packagePath, subDir));
|
|
2327
|
+
}
|
|
2328
|
+
await createModuleBuildGradle(modulePath, {
|
|
2329
|
+
name: "common",
|
|
2330
|
+
type: "common",
|
|
2331
|
+
packagePath: `${config.groupId}.common`,
|
|
2332
|
+
applicationClass: ""
|
|
2333
|
+
}, config);
|
|
2334
|
+
logger.success("\u516C\u5171\u6A21\u5757 common \u521B\u5EFA\u5B8C\u6210");
|
|
2335
|
+
}
|
|
2336
|
+
async function generateDockerFiles(projectPath, projectName) {
|
|
2337
|
+
const backendPath = path6.join(projectPath, "backend");
|
|
2338
|
+
if (!await FileUtils.exists(backendPath)) {
|
|
2339
|
+
logger.warn("\u672A\u627E\u5230 backend \u76EE\u5F55\uFF0C\u8DF3\u8FC7 Docker \u914D\u7F6E\u751F\u6210");
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
const dockerfile = `FROM openjdk:17-jdk-slim
|
|
2343
|
+
WORKDIR /app
|
|
2344
|
+
COPY build/libs/*.jar app.jar
|
|
2345
|
+
EXPOSE 8080
|
|
2346
|
+
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
|
|
2468
2347
|
`;
|
|
2469
2348
|
const deploySh = `#!/bin/bash
|
|
2470
2349
|
# yg-team-cli \u81EA\u52A8\u751F\u6210\u7684\u90E8\u7F72\u811A\u672C
|
|
@@ -2493,9 +2372,9 @@ docker push \${REGISTRY}/\${PROJECT_NAME}:latest
|
|
|
2493
2372
|
|
|
2494
2373
|
echo "\u2705 \u90E8\u7F72\u5B8C\u6210! \u955C\u50CF\u5730\u5740: \${REGISTRY}/\${IMAGE_TAG}"
|
|
2495
2374
|
`;
|
|
2496
|
-
await fs3.writeFile(
|
|
2497
|
-
await fs3.writeFile(
|
|
2498
|
-
await fs3.chmod(
|
|
2375
|
+
await fs3.writeFile(path6.join(backendPath, "Dockerfile"), dockerfile);
|
|
2376
|
+
await fs3.writeFile(path6.join(backendPath, "deploy.sh"), deploySh);
|
|
2377
|
+
await fs3.chmod(path6.join(backendPath, "deploy.sh"), 493);
|
|
2499
2378
|
}
|
|
2500
2379
|
async function initGit(projectPath, projectName) {
|
|
2501
2380
|
try {
|
|
@@ -2524,36 +2403,21 @@ var init_init = __esm({
|
|
|
2524
2403
|
init_module_registry();
|
|
2525
2404
|
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
2405
|
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
|
-
}
|
|
2406
|
+
const config = await collectProjectConfig(projectName);
|
|
2548
2407
|
logger.header("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6 - \u9879\u76EE\u521D\u59CB\u5316");
|
|
2549
2408
|
logger.newLine();
|
|
2409
|
+
logger.info(`\u9879\u76EE\u7C7B\u578B: ${config.projectType === "multi" ? "\u591A\u6A21\u5757\u9879\u76EE" : "\u5355\u6A21\u5757\u9879\u76EE"}`);
|
|
2410
|
+
logger.info(`Group ID: ${config.groupId}`);
|
|
2411
|
+
logger.info(`\u6A21\u5757: ${config.modules.map((m) => m.name).join(", ")}`);
|
|
2412
|
+
logger.info(`Java \u7248\u672C: ${config.javaVersion}`);
|
|
2413
|
+
logger.newLine();
|
|
2550
2414
|
const hasClaude = await claudeAI.checkInstalled();
|
|
2551
2415
|
if (!hasClaude) {
|
|
2552
2416
|
logger.error("\u672A\u68C0\u6D4B\u5230 Claude CLI");
|
|
2553
2417
|
logger.info("\u8BF7\u5B89\u88C5 Claude CLI: npm install -g @anthropic-ai/claude-code");
|
|
2554
2418
|
process.exit(1);
|
|
2555
2419
|
}
|
|
2556
|
-
const projectPath =
|
|
2420
|
+
const projectPath = path6.resolve(options.dir, config.projectName);
|
|
2557
2421
|
const availableModules = ModuleManager.getAvailableModules();
|
|
2558
2422
|
const { selectedModules } = await inquirer.prompt([
|
|
2559
2423
|
{
|
|
@@ -2577,48 +2441,45 @@ var init_init = __esm({
|
|
|
2577
2441
|
title: "\u521B\u5EFA\u9879\u76EE\u76EE\u5F55\u7ED3\u6784",
|
|
2578
2442
|
task: async () => {
|
|
2579
2443
|
await FileUtils.ensureDir(projectPath);
|
|
2580
|
-
await FileUtils.ensureDir(
|
|
2581
|
-
await FileUtils.ensureDir(
|
|
2582
|
-
await FileUtils.ensureDir(
|
|
2583
|
-
await FileUtils.ensureDir(
|
|
2444
|
+
await FileUtils.ensureDir(path6.join(projectPath, "docs/specs"));
|
|
2445
|
+
await FileUtils.ensureDir(path6.join(projectPath, "docs/prd-docs"));
|
|
2446
|
+
await FileUtils.ensureDir(path6.join(projectPath, "docs/api"));
|
|
2447
|
+
await FileUtils.ensureDir(path6.join(projectPath, "docs/sessions"));
|
|
2584
2448
|
}
|
|
2585
2449
|
},
|
|
2586
2450
|
{
|
|
2587
2451
|
title: "\u751F\u6210\u6280\u672F\u6587\u6863 (Tech Stack, Conventions, Memory)",
|
|
2588
2452
|
task: async () => {
|
|
2589
|
-
await generateTechStack(projectPath);
|
|
2590
|
-
await generateConventions(projectPath);
|
|
2591
|
-
await generateAIMemory(projectPath, projectName);
|
|
2453
|
+
await generateTechStack(projectPath, config);
|
|
2454
|
+
await generateConventions(projectPath, config);
|
|
2455
|
+
await generateAIMemory(projectPath, config.projectName);
|
|
2592
2456
|
await generateSpecTemplate(projectPath);
|
|
2593
2457
|
}
|
|
2594
2458
|
},
|
|
2595
2459
|
{
|
|
2596
|
-
title: "\
|
|
2460
|
+
title: "\u521B\u5EFA\u540E\u7AEF\u7ED3\u6784",
|
|
2597
2461
|
task: async () => {
|
|
2598
|
-
|
|
2599
|
-
const backendBranch = options.backendBranch;
|
|
2600
|
-
await cloneBackendTemplate(projectPath, {
|
|
2601
|
-
tag: backendTag,
|
|
2602
|
-
branch: backendBranch
|
|
2603
|
-
});
|
|
2462
|
+
await createBackendFromConfig(projectPath, config);
|
|
2604
2463
|
}
|
|
2605
2464
|
},
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2465
|
+
...config.includeFrontend ? [
|
|
2466
|
+
{
|
|
2467
|
+
title: "\u514B\u9686\u524D\u7AEF\u6A21\u677F",
|
|
2468
|
+
task: async () => {
|
|
2469
|
+
const frontendTag = options.frontendTag || options.tag;
|
|
2470
|
+
const frontendBranch = options.frontendBranch;
|
|
2471
|
+
await cloneFrontendTemplate(projectPath, {
|
|
2472
|
+
tag: frontendTag,
|
|
2473
|
+
branch: frontendBranch
|
|
2474
|
+
});
|
|
2475
|
+
}
|
|
2615
2476
|
}
|
|
2616
|
-
|
|
2477
|
+
] : [],
|
|
2617
2478
|
{
|
|
2618
2479
|
title: "\u6CE8\u5165\u9009\u5B9A\u7684\u901A\u7528\u6A21\u5757",
|
|
2619
2480
|
task: async (ctx) => {
|
|
2620
2481
|
if (selectedModules.length === 0) return;
|
|
2621
|
-
const templatesDir =
|
|
2482
|
+
const templatesDir = path6.resolve(FileUtils.getDirName(import.meta.url), "../templates");
|
|
2622
2483
|
ctx.addedFiles = [];
|
|
2623
2484
|
for (const moduleId of selectedModules) {
|
|
2624
2485
|
const files = await ModuleManager.injectModule(projectPath, moduleId, templatesDir);
|
|
@@ -2630,7 +2491,7 @@ var init_init = __esm({
|
|
|
2630
2491
|
{
|
|
2631
2492
|
title: "\u751F\u6210 Docker \u90E8\u7F72\u914D\u7F6E",
|
|
2632
2493
|
task: async () => {
|
|
2633
|
-
await generateDockerFiles(projectPath, projectName);
|
|
2494
|
+
await generateDockerFiles(projectPath, config.projectName);
|
|
2634
2495
|
}
|
|
2635
2496
|
}
|
|
2636
2497
|
] : []
|
|
@@ -2725,7 +2586,7 @@ var init_init = __esm({
|
|
|
2725
2586
|
// src/commands/breakdown.ts
|
|
2726
2587
|
import { Command as Command2 } from "commander";
|
|
2727
2588
|
import inquirer2 from "inquirer";
|
|
2728
|
-
import
|
|
2589
|
+
import path7 from "path";
|
|
2729
2590
|
import { Listr as Listr2 } from "listr2";
|
|
2730
2591
|
function mergeMilestones(original, milestones) {
|
|
2731
2592
|
const milestoneHeader = "## \u91CC\u7A0B\u7891 (Milestones)";
|
|
@@ -2892,10 +2753,10 @@ var init_breakdown = __esm({
|
|
|
2892
2753
|
choices: ctx.specs
|
|
2893
2754
|
}
|
|
2894
2755
|
]);
|
|
2895
|
-
ctx.selectedFile =
|
|
2756
|
+
ctx.selectedFile = path7.join("docs/specs", selectedFile);
|
|
2896
2757
|
return;
|
|
2897
2758
|
}
|
|
2898
|
-
const fullPath = specFile.startsWith("docs/specs/") ? specFile :
|
|
2759
|
+
const fullPath = specFile.startsWith("docs/specs/") ? specFile : path7.join("docs/specs", specFile);
|
|
2899
2760
|
const exists = await FileUtils.exists(fullPath);
|
|
2900
2761
|
if (!exists) {
|
|
2901
2762
|
throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${specFile}`);
|
|
@@ -2966,7 +2827,7 @@ var init_breakdown = __esm({
|
|
|
2966
2827
|
// src/commands/dev.ts
|
|
2967
2828
|
import { Command as Command3 } from "commander";
|
|
2968
2829
|
import inquirer3 from "inquirer";
|
|
2969
|
-
import
|
|
2830
|
+
import path8 from "path";
|
|
2970
2831
|
async function selectSpec() {
|
|
2971
2832
|
logger.step("\u6B65\u9AA4 1/3: \u9009\u62E9 spec \u6587\u4EF6...");
|
|
2972
2833
|
logger.newLine();
|
|
@@ -2982,7 +2843,7 @@ async function selectSpec() {
|
|
|
2982
2843
|
}
|
|
2983
2844
|
const specs = [];
|
|
2984
2845
|
for (let i = 0; i < specFiles.length; i++) {
|
|
2985
|
-
const file =
|
|
2846
|
+
const file = path8.join(specDir, specFiles[i]);
|
|
2986
2847
|
const spec = await FileUtils.read(file);
|
|
2987
2848
|
const status = SpecUtils.parseSpecStatus(spec);
|
|
2988
2849
|
const dependencies = parseDependencies(spec);
|
|
@@ -3329,8 +3190,8 @@ async function generateSessionLog(specFile, milestone, todo, taskDescription, re
|
|
|
3329
3190
|
const sessionDir = "docs/sessions";
|
|
3330
3191
|
await FileUtils.ensureDir(sessionDir);
|
|
3331
3192
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3332
|
-
const specName =
|
|
3333
|
-
const logFile =
|
|
3193
|
+
const specName = path8.basename(specFile, ".md");
|
|
3194
|
+
const logFile = path8.join(sessionDir, `${timestamp}_${specName}.md`);
|
|
3334
3195
|
const content = `# \u5F00\u53D1\u4F1A\u8BDD\u8BB0\u5F55
|
|
3335
3196
|
|
|
3336
3197
|
**\u65F6\u95F4**: ${(/* @__PURE__ */ new Date()).toLocaleString("zh-CN")}
|
|
@@ -3485,7 +3346,7 @@ var init_dev = __esm({
|
|
|
3485
3346
|
// src/commands/add-feature.ts
|
|
3486
3347
|
import { Command as Command4 } from "commander";
|
|
3487
3348
|
import inquirer4 from "inquirer";
|
|
3488
|
-
import
|
|
3349
|
+
import path9 from "path";
|
|
3489
3350
|
import { Listr as Listr3 } from "listr2";
|
|
3490
3351
|
async function addFeatureFromPrd(featureName, _featureSlug, prdOutputFile) {
|
|
3491
3352
|
const { prdPath } = await inquirer4.prompt([
|
|
@@ -3514,7 +3375,7 @@ async function addFeatureFromPrd(featureName, _featureSlug, prdOutputFile) {
|
|
|
3514
3375
|
const specs = files.filter((f) => !f.includes("template"));
|
|
3515
3376
|
ctx.completedSpecs = [];
|
|
3516
3377
|
for (const file of specs) {
|
|
3517
|
-
const status = await SpecUtils.getSpecStatus(
|
|
3378
|
+
const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
|
|
3518
3379
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
3519
3380
|
ctx.completedSpecs.push(file.replace(".md", ""));
|
|
3520
3381
|
}
|
|
@@ -3575,7 +3436,7 @@ async function addFeatureSimple(featureName, _featureSlug, prdOutputFile) {
|
|
|
3575
3436
|
const specs = files.filter((f) => !f.includes("template"));
|
|
3576
3437
|
ctx.completedSpecs = [];
|
|
3577
3438
|
for (const file of specs) {
|
|
3578
|
-
const status = await SpecUtils.getSpecStatus(
|
|
3439
|
+
const status = await SpecUtils.getSpecStatus(path9.join(specDir, file));
|
|
3579
3440
|
if (status === "\u5DF2\u5B8C\u6210") {
|
|
3580
3441
|
ctx.completedSpecs.push(file.replace(".md", ""));
|
|
3581
3442
|
}
|
|
@@ -3661,7 +3522,7 @@ async function buildProjectContext() {
|
|
|
3661
3522
|
const files = await FileUtils.findFiles("*.md", "docs/specs");
|
|
3662
3523
|
const specs = files.filter((f) => !f.includes("template"));
|
|
3663
3524
|
for (const file of specs) {
|
|
3664
|
-
const status = await SpecUtils.getSpecStatus(
|
|
3525
|
+
const status = await SpecUtils.getSpecStatus(path9.join("docs/specs", file));
|
|
3665
3526
|
context.push(` - ${file.replace(".md", "")} [${status}]`);
|
|
3666
3527
|
}
|
|
3667
3528
|
}
|
|
@@ -3913,7 +3774,7 @@ var init_add_feature = __esm({
|
|
|
3913
3774
|
process.exit(1);
|
|
3914
3775
|
}
|
|
3915
3776
|
const featureSlug = StringUtils.toKebabCase(featureName);
|
|
3916
|
-
const prdFile =
|
|
3777
|
+
const prdFile = path9.join("docs/prd-docs", `${featureSlug}.md`);
|
|
3917
3778
|
const prdExists = await FileUtils.exists(prdFile);
|
|
3918
3779
|
if (prdExists) {
|
|
3919
3780
|
logger.error(`PRD \u6587\u4EF6\u5DF2\u5B58\u5728: ${prdFile}`);
|
|
@@ -3950,12 +3811,12 @@ var init_add_feature = __esm({
|
|
|
3950
3811
|
|
|
3951
3812
|
// src/commands/split-prd.ts
|
|
3952
3813
|
import { Command as Command5 } from "commander";
|
|
3953
|
-
import
|
|
3814
|
+
import path10 from "path";
|
|
3954
3815
|
import { Listr as Listr4 } from "listr2";
|
|
3955
3816
|
async function processSinglePrd(prdFile) {
|
|
3956
|
-
const baseName =
|
|
3817
|
+
const baseName = path10.basename(prdFile, path10.extname(prdFile));
|
|
3957
3818
|
const featureSlug = StringUtils.toKebabCase(baseName);
|
|
3958
|
-
const specFile =
|
|
3819
|
+
const specFile = path10.join("docs/specs", `${featureSlug}.md`);
|
|
3959
3820
|
const specExists = await FileUtils.exists(specFile);
|
|
3960
3821
|
if (specExists) {
|
|
3961
3822
|
logger.error(`Spec \u6587\u4EF6\u5DF2\u5B58\u5728: ${specFile}`);
|
|
@@ -4019,7 +3880,7 @@ async function processMultiplePrds(prdFolder) {
|
|
|
4019
3880
|
ctx.prdFiles = [];
|
|
4020
3881
|
for (const ext of supportedExtensions) {
|
|
4021
3882
|
const files = await FileUtils.findFiles(`*.${ext}`, prdFolder);
|
|
4022
|
-
ctx.prdFiles.push(...files.map((f) =>
|
|
3883
|
+
ctx.prdFiles.push(...files.map((f) => path10.join(prdFolder, f)));
|
|
4023
3884
|
}
|
|
4024
3885
|
if (ctx.prdFiles.length === 0) {
|
|
4025
3886
|
throw new Error(
|
|
@@ -4037,7 +3898,7 @@ async function processMultiplePrds(prdFolder) {
|
|
|
4037
3898
|
{
|
|
4038
3899
|
title: "\u626B\u63CF\u622A\u56FE\u6587\u4EF6",
|
|
4039
3900
|
task: async (ctx) => {
|
|
4040
|
-
const screenshotDir =
|
|
3901
|
+
const screenshotDir = path10.join(prdFolder, "screenshots");
|
|
4041
3902
|
const dirExists = await FileUtils.exists(screenshotDir);
|
|
4042
3903
|
if (!dirExists) {
|
|
4043
3904
|
logger.info("\u672A\u627E\u5230 screenshots \u76EE\u5F55\uFF0C\u8DF3\u8FC7\u622A\u56FE");
|
|
@@ -4048,7 +3909,7 @@ async function processMultiplePrds(prdFolder) {
|
|
|
4048
3909
|
ctx.screenshots = [];
|
|
4049
3910
|
for (const ext of imageExtensions) {
|
|
4050
3911
|
const files = await FileUtils.findFiles(`*.${ext}`, screenshotDir);
|
|
4051
|
-
ctx.screenshots.push(...files.map((f) =>
|
|
3912
|
+
ctx.screenshots.push(...files.map((f) => path10.join(screenshotDir, f)));
|
|
4052
3913
|
}
|
|
4053
3914
|
logger.success(`\u627E\u5230 ${ctx.screenshots.length} \u4E2A\u622A\u56FE\u6587\u4EF6`);
|
|
4054
3915
|
}
|
|
@@ -4059,8 +3920,8 @@ async function processMultiplePrds(prdFolder) {
|
|
|
4059
3920
|
const entries = await FileUtils.findFiles("*/", prdFolder);
|
|
4060
3921
|
ctx.demoRepos = [];
|
|
4061
3922
|
for (const entry of entries) {
|
|
4062
|
-
const dirPath =
|
|
4063
|
-
const gitDir =
|
|
3923
|
+
const dirPath = path10.join(prdFolder, entry);
|
|
3924
|
+
const gitDir = path10.join(dirPath, ".git");
|
|
4064
3925
|
const hasGit = await FileUtils.exists(gitDir);
|
|
4065
3926
|
if (hasGit) {
|
|
4066
3927
|
ctx.demoRepos.push(dirPath);
|
|
@@ -4388,7 +4249,7 @@ var init_split_prd = __esm({
|
|
|
4388
4249
|
// src/commands/bugfix.ts
|
|
4389
4250
|
import { Command as Command6 } from "commander";
|
|
4390
4251
|
import inquirer5 from "inquirer";
|
|
4391
|
-
import
|
|
4252
|
+
import path11 from "path";
|
|
4392
4253
|
import { Listr as Listr5 } from "listr2";
|
|
4393
4254
|
function generateBugId() {
|
|
4394
4255
|
const date = /* @__PURE__ */ new Date();
|
|
@@ -4415,7 +4276,7 @@ async function findRelatedSpec(description) {
|
|
|
4415
4276
|
}
|
|
4416
4277
|
const keywords = extractKeywords(description);
|
|
4417
4278
|
for (const file of specs) {
|
|
4418
|
-
const filePath =
|
|
4279
|
+
const filePath = path11.join(specDir, file);
|
|
4419
4280
|
const content = await FileUtils.read(filePath);
|
|
4420
4281
|
for (const keyword of keywords) {
|
|
4421
4282
|
if (content.toLowerCase().includes(keyword.toLowerCase())) {
|
|
@@ -4555,7 +4416,7 @@ var init_bugfix = __esm({
|
|
|
4555
4416
|
const relatedSpec = await findRelatedSpec(answers.description);
|
|
4556
4417
|
const bugfixDir = "docs/bugfixes";
|
|
4557
4418
|
await FileUtils.ensureDir(bugfixDir);
|
|
4558
|
-
const bugfixFile =
|
|
4419
|
+
const bugfixFile = path11.join(bugfixDir, `${timestamp}_${bugId}.md`);
|
|
4559
4420
|
const content = formatBugfixDocument({
|
|
4560
4421
|
id: bugId,
|
|
4561
4422
|
severity: answers.severity,
|
|
@@ -4631,7 +4492,7 @@ var init_bugfix = __esm({
|
|
|
4631
4492
|
const timestamp = DateUtils.format(/* @__PURE__ */ new Date(), "YYYY-MM-DD HH:mm:ss");
|
|
4632
4493
|
const hotfixDir = "docs/hotfixes";
|
|
4633
4494
|
await FileUtils.ensureDir(hotfixDir);
|
|
4634
|
-
const hotfixFile =
|
|
4495
|
+
const hotfixFile = path11.join(hotfixDir, `${timestamp}_${hotfixId}.md`);
|
|
4635
4496
|
const content = formatHotfixDocument({
|
|
4636
4497
|
id: hotfixId,
|
|
4637
4498
|
description: answers.description,
|
|
@@ -4687,7 +4548,7 @@ Temporary solution: ${answers.solution}`
|
|
|
4687
4548
|
// src/commands/accept.ts
|
|
4688
4549
|
import { Command as Command7 } from "commander";
|
|
4689
4550
|
import inquirer6 from "inquirer";
|
|
4690
|
-
import
|
|
4551
|
+
import path12 from "path";
|
|
4691
4552
|
async function selectSpec2(defaultSpec) {
|
|
4692
4553
|
logger.step("\u6B65\u9AA4 1/4: \u9009\u62E9 spec \u6587\u4EF6...");
|
|
4693
4554
|
logger.newLine();
|
|
@@ -4702,7 +4563,7 @@ async function selectSpec2(defaultSpec) {
|
|
|
4702
4563
|
throw new Error("\u672A\u627E\u5230 spec \u6587\u4EF6");
|
|
4703
4564
|
}
|
|
4704
4565
|
if (defaultSpec) {
|
|
4705
|
-
const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec :
|
|
4566
|
+
const fullPath = defaultSpec.startsWith("docs/specs/") ? defaultSpec : path12.join(specDir, defaultSpec);
|
|
4706
4567
|
const exists2 = await FileUtils.exists(fullPath);
|
|
4707
4568
|
if (!exists2) {
|
|
4708
4569
|
throw new Error(`Spec \u6587\u4EF6\u4E0D\u5B58\u5728: ${defaultSpec}`);
|
|
@@ -4712,7 +4573,7 @@ async function selectSpec2(defaultSpec) {
|
|
|
4712
4573
|
}
|
|
4713
4574
|
const specs = [];
|
|
4714
4575
|
for (const file of specFiles) {
|
|
4715
|
-
const fullPath =
|
|
4576
|
+
const fullPath = path12.join(specDir, file);
|
|
4716
4577
|
const content = await FileUtils.read(fullPath);
|
|
4717
4578
|
const status = SpecUtils.parseSpecStatus(content);
|
|
4718
4579
|
specs.push({ file: fullPath, name: file, status });
|
|
@@ -5003,9 +4864,9 @@ async function generateAcceptanceReport(result) {
|
|
|
5003
4864
|
const reportDir = "docs/acceptance-reports";
|
|
5004
4865
|
await FileUtils.ensureDir(reportDir);
|
|
5005
4866
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5006
|
-
const specName =
|
|
4867
|
+
const specName = path12.basename(result.specFile, ".md");
|
|
5007
4868
|
const milestoneSafe = result.milestone.replace(/[^a-zA-Z0-9]/g, "-");
|
|
5008
|
-
const reportFile =
|
|
4869
|
+
const reportFile = path12.join(reportDir, `${timestamp}_${specName}_${milestoneSafe}.md`);
|
|
5009
4870
|
const report = generateMarkdownReport(result);
|
|
5010
4871
|
await FileUtils.write(reportFile, report);
|
|
5011
4872
|
logger.success(`\u9A8C\u6536\u62A5\u544A\u5DF2\u751F\u6210: ${reportFile}`);
|
|
@@ -5125,7 +4986,7 @@ async function handleIssues(result) {
|
|
|
5125
4986
|
const bugfixDir = "docs/bugfixes";
|
|
5126
4987
|
await FileUtils.ensureDir(bugfixDir);
|
|
5127
4988
|
for (const issue of result.issues) {
|
|
5128
|
-
const bugfixFile =
|
|
4989
|
+
const bugfixFile = path12.join(
|
|
5129
4990
|
bugfixDir,
|
|
5130
4991
|
`${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}_${issue.id}.md`
|
|
5131
4992
|
);
|
|
@@ -5273,8 +5134,383 @@ var init_accept = __esm({
|
|
|
5273
5134
|
}
|
|
5274
5135
|
});
|
|
5275
5136
|
|
|
5276
|
-
// src/commands/
|
|
5137
|
+
// src/commands/add-module.ts
|
|
5277
5138
|
import { Command as Command8 } from "commander";
|
|
5139
|
+
import inquirer7 from "inquirer";
|
|
5140
|
+
import path13 from "path";
|
|
5141
|
+
import fs4 from "fs-extra";
|
|
5142
|
+
async function scanExistingModules(projectPath) {
|
|
5143
|
+
const modules = [];
|
|
5144
|
+
const settingsGradle = path13.join(projectPath, "settings.gradle");
|
|
5145
|
+
if (await FileUtils.exists(settingsGradle)) {
|
|
5146
|
+
const content = await FileUtils.read(settingsGradle);
|
|
5147
|
+
const includeMatches = content.match(/include\s+'([^']+)'/g);
|
|
5148
|
+
if (includeMatches) {
|
|
5149
|
+
for (const match of includeMatches) {
|
|
5150
|
+
const moduleName = match.replace(/include\s+'/, "").replace(/'/, "");
|
|
5151
|
+
modules.push(moduleName);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
const backendPath = path13.join(projectPath, "backend");
|
|
5156
|
+
if (await FileUtils.exists(backendPath)) {
|
|
5157
|
+
const entries = await fs4.readdir(backendPath, { withFileTypes: true });
|
|
5158
|
+
for (const entry of entries) {
|
|
5159
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "src") {
|
|
5160
|
+
if (!modules.includes(entry.name)) {
|
|
5161
|
+
modules.push(entry.name);
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
return modules;
|
|
5167
|
+
}
|
|
5168
|
+
async function detectGroupId(projectPath) {
|
|
5169
|
+
const buildGradle = path13.join(projectPath, "backend", "build.gradle");
|
|
5170
|
+
if (await FileUtils.exists(buildGradle)) {
|
|
5171
|
+
const content = await FileUtils.read(buildGradle);
|
|
5172
|
+
const groupMatch = content.match(/group\s*=\s*['"]([^'"]+)['"]/);
|
|
5173
|
+
if (groupMatch) {
|
|
5174
|
+
return groupMatch[1];
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
const rootBuildGradle = path13.join(projectPath, "build.gradle");
|
|
5178
|
+
if (await FileUtils.exists(rootBuildGradle)) {
|
|
5179
|
+
const content = await FileUtils.read(rootBuildGradle);
|
|
5180
|
+
const groupMatch = content.match(/group\s*=\s*['"]([^'"]+)['"]/);
|
|
5181
|
+
if (groupMatch) {
|
|
5182
|
+
return groupMatch[1];
|
|
5183
|
+
}
|
|
5184
|
+
}
|
|
5185
|
+
const backendPath = path13.join(projectPath, "backend");
|
|
5186
|
+
if (await FileUtils.exists(backendPath)) {
|
|
5187
|
+
const entries = await fs4.readdir(backendPath, { withFileTypes: true });
|
|
5188
|
+
for (const entry of entries) {
|
|
5189
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
5190
|
+
const srcPath = path13.join(backendPath, entry.name, "src", "main", "java");
|
|
5191
|
+
if (await FileUtils.exists(srcPath)) {
|
|
5192
|
+
const javaDirs = await fs4.readdir(srcPath);
|
|
5193
|
+
if (javaDirs.length > 0) {
|
|
5194
|
+
return javaDirs[0];
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
}
|
|
5200
|
+
return "org.yungu";
|
|
5201
|
+
}
|
|
5202
|
+
async function detectRootProjectName(projectPath) {
|
|
5203
|
+
const settingsGradle = path13.join(projectPath, "settings.gradle");
|
|
5204
|
+
if (await FileUtils.exists(settingsGradle)) {
|
|
5205
|
+
const content = await FileUtils.read(settingsGradle);
|
|
5206
|
+
const rootMatch = content.match(/rootProject\.name\s*=\s*['"]([^'"]+)['"]/);
|
|
5207
|
+
if (rootMatch) {
|
|
5208
|
+
return rootMatch[1];
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
return path13.basename(projectPath);
|
|
5212
|
+
}
|
|
5213
|
+
async function createModuleStructure(projectPath, module, groupId) {
|
|
5214
|
+
const modulePath = path13.join(projectPath, "backend", module.name);
|
|
5215
|
+
logger.info(`\u6B63\u5728\u521B\u5EFA\u6A21\u5757: ${module.name}...`);
|
|
5216
|
+
const baseDirs = [
|
|
5217
|
+
"src/main/java",
|
|
5218
|
+
"src/main/resources",
|
|
5219
|
+
"src/test/java"
|
|
5220
|
+
];
|
|
5221
|
+
for (const dir of baseDirs) {
|
|
5222
|
+
await FileUtils.ensureDir(path13.join(modulePath, dir));
|
|
5223
|
+
}
|
|
5224
|
+
const packageDirs = module.packagePath.split(".");
|
|
5225
|
+
let javaPath = path13.join(modulePath, "src", "main", "java");
|
|
5226
|
+
if (module.type === "service") {
|
|
5227
|
+
for (const subDir of ["controller", "service", "mapper", "entity", "dto", "config"]) {
|
|
5228
|
+
await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
|
|
5229
|
+
}
|
|
5230
|
+
await createApplicationClass2(
|
|
5231
|
+
path13.join(javaPath, ...packageDirs, "config", `${module.applicationClass}.java`),
|
|
5232
|
+
module.packagePath,
|
|
5233
|
+
module.applicationClass
|
|
5234
|
+
);
|
|
5235
|
+
} else if (module.type === "api") {
|
|
5236
|
+
for (const subDir of ["model", "constant", "enums", "exception"]) {
|
|
5237
|
+
await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
|
|
5238
|
+
}
|
|
5239
|
+
} else {
|
|
5240
|
+
for (const subDir of ["util", "common", "config", "manager"]) {
|
|
5241
|
+
await FileUtils.ensureDir(path13.join(javaPath, ...packageDirs, subDir));
|
|
5242
|
+
}
|
|
5243
|
+
}
|
|
5244
|
+
await createModuleBuildGradle2(
|
|
5245
|
+
path13.join(modulePath, "build.gradle"),
|
|
5246
|
+
module,
|
|
5247
|
+
groupId
|
|
5248
|
+
);
|
|
5249
|
+
const gitignore = `# Gradle
|
|
5250
|
+
.gradle/
|
|
5251
|
+
build/
|
|
5252
|
+
|
|
5253
|
+
# IDE
|
|
5254
|
+
.idea/
|
|
5255
|
+
*.iml
|
|
5256
|
+
*.ipr
|
|
5257
|
+
*.iws
|
|
5258
|
+
.vscode/
|
|
5259
|
+
|
|
5260
|
+
# OS
|
|
5261
|
+
.DS_Store
|
|
5262
|
+
Thumbs.db
|
|
5263
|
+
|
|
5264
|
+
# Logs
|
|
5265
|
+
logs/
|
|
5266
|
+
*.log
|
|
5267
|
+
|
|
5268
|
+
# Application
|
|
5269
|
+
application-local.yml
|
|
5270
|
+
application-dev.yml
|
|
5271
|
+
`;
|
|
5272
|
+
await FileUtils.write(path13.join(modulePath, ".gitignore"), gitignore);
|
|
5273
|
+
logger.success(`\u6A21\u5757 ${module.name} \u521B\u5EFA\u5B8C\u6210`);
|
|
5274
|
+
}
|
|
5275
|
+
async function createApplicationClass2(filePath, packagePath, className) {
|
|
5276
|
+
const content = `package ${packagePath}.config;
|
|
5277
|
+
|
|
5278
|
+
import lombok.extern.slf4j.Slf4j;
|
|
5279
|
+
import org.springframework.boot.SpringApplication;
|
|
5280
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
5281
|
+
import org.springframework.context.ApplicationContext;
|
|
5282
|
+
import org.springframework.context.annotation.ComponentScan;
|
|
5283
|
+
|
|
5284
|
+
@SpringBootApplication
|
|
5285
|
+
@ComponentScan("${packagePath}")
|
|
5286
|
+
@Slf4j
|
|
5287
|
+
public class ${className} {
|
|
5288
|
+
|
|
5289
|
+
public static void main(String[] args) {
|
|
5290
|
+
ApplicationContext context = SpringApplication.run(${className}.class, args);
|
|
5291
|
+
log.info("${className} \u542F\u52A8\u6210\u529F!");
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
`;
|
|
5295
|
+
await FileUtils.write(filePath, content);
|
|
5296
|
+
}
|
|
5297
|
+
async function createModuleBuildGradle2(filePath, module, groupId) {
|
|
5298
|
+
let dependencies = "";
|
|
5299
|
+
if (module.type === "service") {
|
|
5300
|
+
dependencies = `
|
|
5301
|
+
dependencies {
|
|
5302
|
+
implementation project(":common")
|
|
5303
|
+
implementation 'org.springframework.boot:spring-boot-starter-web'
|
|
5304
|
+
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
|
|
5305
|
+
implementation 'com.alibaba:druid-spring-boot-starter'
|
|
5306
|
+
compileOnly "org.projectlombok:lombok"
|
|
5307
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
5308
|
+
}
|
|
5309
|
+
`;
|
|
5310
|
+
} else if (module.type === "api") {
|
|
5311
|
+
dependencies = `
|
|
5312
|
+
dependencies {
|
|
5313
|
+
compileOnly 'org.springframework.boot:spring-boot-starter'
|
|
5314
|
+
compileOnly "org.projectlombok:lombok"
|
|
5315
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
5316
|
+
}
|
|
5317
|
+
`;
|
|
5318
|
+
} else {
|
|
5319
|
+
dependencies = `
|
|
5320
|
+
dependencies {
|
|
5321
|
+
implementation 'org.springframework.boot:spring-boot-starter'
|
|
5322
|
+
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
|
|
5323
|
+
compileOnly "org.projectlombok:lombok"
|
|
5324
|
+
annotationProcessor "org.projectlombok:lombok"
|
|
5325
|
+
}
|
|
5326
|
+
`;
|
|
5327
|
+
}
|
|
5328
|
+
const content = `plugins {
|
|
5329
|
+
id 'java-library'
|
|
5330
|
+
id 'org.springframework.boot' version '3.2.0'
|
|
5331
|
+
id 'io.spring.dependency-management' version '1.1.4'
|
|
5332
|
+
}
|
|
5333
|
+
|
|
5334
|
+
group = '${groupId}'
|
|
5335
|
+
version = '0.0.1-SNAPSHOT'
|
|
5336
|
+
|
|
5337
|
+
java {
|
|
5338
|
+
sourceCompatibility = '17'
|
|
5339
|
+
}
|
|
5340
|
+
|
|
5341
|
+
repositories {
|
|
5342
|
+
maven { url 'https://maven.aliyun.com/repository/public/' }
|
|
5343
|
+
}
|
|
5344
|
+
|
|
5345
|
+
${dependencies}
|
|
5346
|
+
`;
|
|
5347
|
+
await FileUtils.write(filePath, content);
|
|
5348
|
+
}
|
|
5349
|
+
async function updateSettingsGradle(projectPath, rootProjectName, newModules) {
|
|
5350
|
+
const settingsPath = path13.join(projectPath, "backend", "settings.gradle");
|
|
5351
|
+
let content = "";
|
|
5352
|
+
if (await FileUtils.exists(settingsPath)) {
|
|
5353
|
+
content = await FileUtils.read(settingsPath);
|
|
5354
|
+
} else {
|
|
5355
|
+
content = `rootProject.name = '${rootProjectName}'
|
|
5356
|
+
`;
|
|
5357
|
+
}
|
|
5358
|
+
const existingModules = /* @__PURE__ */ new Set();
|
|
5359
|
+
const moduleMatches = content.matchAll(/include\s+'([^']+)'/g);
|
|
5360
|
+
for (const match of moduleMatches) {
|
|
5361
|
+
existingModules.add(match[1]);
|
|
5362
|
+
}
|
|
5363
|
+
for (const module of newModules) {
|
|
5364
|
+
if (!existingModules.has(module.name)) {
|
|
5365
|
+
existingModules.add(module.name);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
const lines = [];
|
|
5369
|
+
lines.push(`rootProject.name = '${rootProjectName}'`);
|
|
5370
|
+
lines.push("");
|
|
5371
|
+
const allModules = Array.from(existingModules).sort();
|
|
5372
|
+
for (const module of allModules) {
|
|
5373
|
+
lines.push(`include '${module}'`);
|
|
5374
|
+
}
|
|
5375
|
+
await FileUtils.write(settingsPath, lines.join("\n"));
|
|
5376
|
+
logger.success("settings.gradle \u5DF2\u66F4\u65B0");
|
|
5377
|
+
}
|
|
5378
|
+
function toPascalCase2(str) {
|
|
5379
|
+
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
5380
|
+
}
|
|
5381
|
+
var addModuleCommand;
|
|
5382
|
+
var init_add_module = __esm({
|
|
5383
|
+
"src/commands/add-module.ts"() {
|
|
5384
|
+
"use strict";
|
|
5385
|
+
init_esm_shims();
|
|
5386
|
+
init_utils();
|
|
5387
|
+
init_logger();
|
|
5388
|
+
addModuleCommand = new Command8("add-module").description("\u4E3A\u5DF2\u6709\u9879\u76EE\u65B0\u589E\u6A21\u5757 (\u591A\u6A21\u5757 Gradle \u9879\u76EE)").action(async () => {
|
|
5389
|
+
try {
|
|
5390
|
+
logger.header("\u65B0\u589E\u6A21\u5757");
|
|
5391
|
+
logger.newLine();
|
|
5392
|
+
const projectPath = process.cwd();
|
|
5393
|
+
const hasTechStack = await FileUtils.exists("TECH_STACK.md");
|
|
5394
|
+
if (!hasTechStack) {
|
|
5395
|
+
logger.error("\u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684 team-cli \u9879\u76EE");
|
|
5396
|
+
logger.info("\u8BF7\u5207\u6362\u5230\u9879\u76EE\u6839\u76EE\u5F55\u540E\u518D\u6267\u884C\u6B64\u547D\u4EE4");
|
|
5397
|
+
process.exit(1);
|
|
5398
|
+
}
|
|
5399
|
+
const existingModules = await scanExistingModules(projectPath);
|
|
5400
|
+
if (existingModules.length > 0) {
|
|
5401
|
+
logger.success(`\u68C0\u6D4B\u5230\u73B0\u6709\u6A21\u5757: [${existingModules.join(", ")}]`);
|
|
5402
|
+
} else {
|
|
5403
|
+
logger.info("\u672A\u68C0\u6D4B\u5230\u73B0\u6709\u6A21\u5757");
|
|
5404
|
+
}
|
|
5405
|
+
logger.newLine();
|
|
5406
|
+
const groupId = await detectGroupId(projectPath);
|
|
5407
|
+
logger.info(`\u68C0\u6D4B\u5230 Group ID: ${groupId}`);
|
|
5408
|
+
logger.newLine();
|
|
5409
|
+
const rootProjectName = await detectRootProjectName(projectPath);
|
|
5410
|
+
logger.info(`\u68C0\u6D4B\u5230\u6839\u9879\u76EE\u540D: ${rootProjectName}`);
|
|
5411
|
+
logger.newLine();
|
|
5412
|
+
const newModules = [];
|
|
5413
|
+
let addMore = true;
|
|
5414
|
+
let moduleIndex = 1;
|
|
5415
|
+
while (addMore) {
|
|
5416
|
+
const moduleNameAnswer = await inquirer7.prompt([
|
|
5417
|
+
{
|
|
5418
|
+
type: "input",
|
|
5419
|
+
name: "moduleName",
|
|
5420
|
+
message: `\u8BF7\u8F93\u5165\u7B2C ${moduleIndex} \u4E2A\u65B0\u6A21\u5757\u540D\u79F0 (\u76EE\u5F55\u540D):`,
|
|
5421
|
+
validate: (input) => {
|
|
5422
|
+
if (!/^[a-z][a-z0-9-]*$/.test(input)) {
|
|
5423
|
+
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";
|
|
5424
|
+
}
|
|
5425
|
+
if (existingModules.includes(input)) {
|
|
5426
|
+
return "\u8BE5\u6A21\u5757\u5DF2\u5B58\u5728";
|
|
5427
|
+
}
|
|
5428
|
+
return true;
|
|
5429
|
+
}
|
|
5430
|
+
}
|
|
5431
|
+
]);
|
|
5432
|
+
const moduleName = moduleNameAnswer.moduleName;
|
|
5433
|
+
const typeAnswer = await inquirer7.prompt([
|
|
5434
|
+
{
|
|
5435
|
+
type: "list",
|
|
5436
|
+
name: "moduleType",
|
|
5437
|
+
message: `\u6A21\u5757 "${moduleName}" \u7C7B\u578B:`,
|
|
5438
|
+
choices: [
|
|
5439
|
+
{ name: "API \u670D\u52A1\u6A21\u5757 (\u72EC\u7ACB\u90E8\u7F72)", value: "service" },
|
|
5440
|
+
{ name: "\u516C\u5171\u6A21\u5757 (\u88AB\u5176\u4ED6\u6A21\u5757\u4F9D\u8D56)", value: "common" },
|
|
5441
|
+
{ name: "API \u63A5\u53E3\u6A21\u5757 (\u4EC5\u63A5\u53E3\u5B9A\u4E49)", value: "api" }
|
|
5442
|
+
],
|
|
5443
|
+
default: "service"
|
|
5444
|
+
}
|
|
5445
|
+
]);
|
|
5446
|
+
const defaultPackage = `${groupId}.${moduleName}`;
|
|
5447
|
+
const packageAnswer = await inquirer7.prompt([
|
|
5448
|
+
{
|
|
5449
|
+
type: "input",
|
|
5450
|
+
name: "packagePath",
|
|
5451
|
+
message: "\u6A21\u5757\u5305\u8DEF\u5F84:",
|
|
5452
|
+
default: defaultPackage,
|
|
5453
|
+
validate: (input) => {
|
|
5454
|
+
if (!/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)*$/.test(input)) {
|
|
5455
|
+
return "\u5305\u8DEF\u5F84\u683C\u5F0F\u4E0D\u6B63\u786E";
|
|
5456
|
+
}
|
|
5457
|
+
return true;
|
|
5458
|
+
}
|
|
5459
|
+
}
|
|
5460
|
+
]);
|
|
5461
|
+
const pascalName = toPascalCase2(moduleName);
|
|
5462
|
+
const classAnswer = await inquirer7.prompt([
|
|
5463
|
+
{
|
|
5464
|
+
type: "input",
|
|
5465
|
+
name: "applicationClass",
|
|
5466
|
+
message: "\u542F\u52A8\u7C7B\u540D:",
|
|
5467
|
+
default: `${pascalName}Application`
|
|
5468
|
+
}
|
|
5469
|
+
]);
|
|
5470
|
+
newModules.push({
|
|
5471
|
+
name: moduleName,
|
|
5472
|
+
type: typeAnswer.moduleType,
|
|
5473
|
+
packagePath: packageAnswer.packagePath,
|
|
5474
|
+
applicationClass: classAnswer.applicationClass
|
|
5475
|
+
});
|
|
5476
|
+
const moreAnswer = await inquirer7.prompt([
|
|
5477
|
+
{
|
|
5478
|
+
type: "confirm",
|
|
5479
|
+
name: "addMore",
|
|
5480
|
+
message: "\u662F\u5426\u8FD8\u9700\u8981\u6DFB\u52A0\u66F4\u591A\u6A21\u5757?",
|
|
5481
|
+
default: false
|
|
5482
|
+
}
|
|
5483
|
+
]);
|
|
5484
|
+
addMore = moreAnswer.addMore;
|
|
5485
|
+
moduleIndex++;
|
|
5486
|
+
}
|
|
5487
|
+
logger.newLine();
|
|
5488
|
+
logger.info(`\u5C06\u65B0\u589E\u6A21\u5757: ${newModules.map((m) => m.name).join(", ")}`);
|
|
5489
|
+
logger.newLine();
|
|
5490
|
+
for (const module of newModules) {
|
|
5491
|
+
await createModuleStructure(projectPath, module, groupId);
|
|
5492
|
+
}
|
|
5493
|
+
await updateSettingsGradle(projectPath, rootProjectName, newModules);
|
|
5494
|
+
logger.newLine();
|
|
5495
|
+
logger.success("\u6A21\u5757\u521B\u5EFA\u5B8C\u6210!");
|
|
5496
|
+
logger.newLine();
|
|
5497
|
+
logger.info("\u4E0B\u4E00\u6B65:");
|
|
5498
|
+
logger.step("1. \u8FD0\u884C `team-cli breakdown docs/specs/xxx.md` \u62C6\u5206\u9700\u6C42");
|
|
5499
|
+
logger.step("2. \u8FD0\u884C `team-cli dev` \u5F00\u59CB\u5F00\u53D1");
|
|
5500
|
+
logger.newLine();
|
|
5501
|
+
} catch (error) {
|
|
5502
|
+
logger.error(`\u65B0\u589E\u6A21\u5757\u5931\u8D25: ${error.message}`);
|
|
5503
|
+
if (process.env.DEBUG) {
|
|
5504
|
+
console.error(error);
|
|
5505
|
+
}
|
|
5506
|
+
process.exit(1);
|
|
5507
|
+
}
|
|
5508
|
+
});
|
|
5509
|
+
}
|
|
5510
|
+
});
|
|
5511
|
+
|
|
5512
|
+
// src/commands/lint.ts
|
|
5513
|
+
import { Command as Command9 } from "commander";
|
|
5278
5514
|
import { execa as execa3 } from "execa";
|
|
5279
5515
|
var lintCommand;
|
|
5280
5516
|
var init_lint = __esm({
|
|
@@ -5283,7 +5519,7 @@ var init_lint = __esm({
|
|
|
5283
5519
|
init_esm_shims();
|
|
5284
5520
|
init_utils();
|
|
5285
5521
|
init_logger();
|
|
5286
|
-
lintCommand = new
|
|
5522
|
+
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
5523
|
try {
|
|
5288
5524
|
logger.header("\u4EE3\u7801\u8D28\u91CF\u68C0\u67E5");
|
|
5289
5525
|
logger.newLine();
|
|
@@ -5417,7 +5653,7 @@ var init_lint = __esm({
|
|
|
5417
5653
|
});
|
|
5418
5654
|
|
|
5419
5655
|
// src/commands/status.ts
|
|
5420
|
-
import { Command as
|
|
5656
|
+
import { Command as Command10 } from "commander";
|
|
5421
5657
|
import path14 from "path";
|
|
5422
5658
|
async function displayProjectInfo() {
|
|
5423
5659
|
logger.info("\u9879\u76EE\u4FE1\u606F:");
|
|
@@ -5535,7 +5771,7 @@ var init_status = __esm({
|
|
|
5535
5771
|
init_esm_shims();
|
|
5536
5772
|
init_utils();
|
|
5537
5773
|
init_logger();
|
|
5538
|
-
statusCommand = new
|
|
5774
|
+
statusCommand = new Command10("status").description("\u67E5\u770B\u9879\u76EE\u72B6\u6001").action(async () => {
|
|
5539
5775
|
try {
|
|
5540
5776
|
logger.header("\u9879\u76EE\u72B6\u6001");
|
|
5541
5777
|
logger.newLine();
|
|
@@ -5561,9 +5797,9 @@ var init_status = __esm({
|
|
|
5561
5797
|
});
|
|
5562
5798
|
|
|
5563
5799
|
// src/commands/detect-deps.ts
|
|
5564
|
-
import { Command as
|
|
5800
|
+
import { Command as Command11 } from "commander";
|
|
5565
5801
|
import path15 from "path";
|
|
5566
|
-
import
|
|
5802
|
+
import inquirer8 from "inquirer";
|
|
5567
5803
|
async function detectDependencies(specFile) {
|
|
5568
5804
|
logger.step("\u81EA\u52A8\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB...");
|
|
5569
5805
|
const projectDir = ".";
|
|
@@ -5604,7 +5840,7 @@ async function detectDependencies(specFile) {
|
|
|
5604
5840
|
logger.step(`- ${spec}`);
|
|
5605
5841
|
}
|
|
5606
5842
|
logger.newLine();
|
|
5607
|
-
const answers = await
|
|
5843
|
+
const answers = await inquirer8.prompt([
|
|
5608
5844
|
{
|
|
5609
5845
|
type: "confirm",
|
|
5610
5846
|
name: "autoUpdate",
|
|
@@ -5776,7 +6012,7 @@ var init_detect_deps = __esm({
|
|
|
5776
6012
|
init_esm_shims();
|
|
5777
6013
|
init_utils();
|
|
5778
6014
|
init_logger();
|
|
5779
|
-
detectDepsCommand = new
|
|
6015
|
+
detectDepsCommand = new Command11("detect-deps").argument("[spec-file]", "Spec \u6587\u4EF6\u8DEF\u5F84").description("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB").action(async (specFile) => {
|
|
5780
6016
|
try {
|
|
5781
6017
|
logger.header("\u68C0\u6D4B\u4F9D\u8D56\u5173\u7CFB");
|
|
5782
6018
|
logger.newLine();
|
|
@@ -5828,7 +6064,7 @@ var init_detect_deps = __esm({
|
|
|
5828
6064
|
});
|
|
5829
6065
|
|
|
5830
6066
|
// src/commands/sync-memory.ts
|
|
5831
|
-
import { Command as
|
|
6067
|
+
import { Command as Command12 } from "commander";
|
|
5832
6068
|
import path16 from "path";
|
|
5833
6069
|
async function syncFeatureInventory(aiMemoryFile, projectDir) {
|
|
5834
6070
|
logger.step("\u540C\u6B65\u529F\u80FD\u6E05\u5355...");
|
|
@@ -6079,15 +6315,15 @@ async function syncTemplateVersions(aiMemoryFile, projectDir) {
|
|
|
6079
6315
|
await replaceOrInsertSection(aiMemoryFile, "## \u6A21\u677F\u7248\u672C\u4FE1\u606F", newContent);
|
|
6080
6316
|
}
|
|
6081
6317
|
function extractRepoName(repository) {
|
|
6082
|
-
let
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
const parts =
|
|
6318
|
+
let path22 = repository;
|
|
6319
|
+
path22 = path22.replace(/^https?:\/\//, "");
|
|
6320
|
+
path22 = path22.replace(/^git@/, "");
|
|
6321
|
+
const parts = path22.split("/");
|
|
6086
6322
|
if (parts.length > 1) {
|
|
6087
|
-
|
|
6323
|
+
path22 = parts.slice(1).join("/");
|
|
6088
6324
|
}
|
|
6089
|
-
|
|
6090
|
-
return
|
|
6325
|
+
path22 = path22.replace(/\.git$/, "");
|
|
6326
|
+
return path22;
|
|
6091
6327
|
}
|
|
6092
6328
|
var syncMemoryCommand;
|
|
6093
6329
|
var init_sync_memory = __esm({
|
|
@@ -6097,7 +6333,7 @@ var init_sync_memory = __esm({
|
|
|
6097
6333
|
init_utils();
|
|
6098
6334
|
init_logger();
|
|
6099
6335
|
init_template_version();
|
|
6100
|
-
syncMemoryCommand = new
|
|
6336
|
+
syncMemoryCommand = new Command12("sync-memory").description("\u540C\u6B65 AI_MEMORY.md").action(async () => {
|
|
6101
6337
|
try {
|
|
6102
6338
|
logger.header("\u540C\u6B65 AI_MEMORY.md");
|
|
6103
6339
|
logger.newLine();
|
|
@@ -6131,9 +6367,9 @@ var init_sync_memory = __esm({
|
|
|
6131
6367
|
});
|
|
6132
6368
|
|
|
6133
6369
|
// src/commands/check-api.ts
|
|
6134
|
-
import { Command as
|
|
6370
|
+
import { Command as Command13 } from "commander";
|
|
6135
6371
|
import path17 from "path";
|
|
6136
|
-
import
|
|
6372
|
+
import inquirer9 from "inquirer";
|
|
6137
6373
|
import { Listr as Listr6 } from "listr2";
|
|
6138
6374
|
async function checkApiConflicts(projectDir) {
|
|
6139
6375
|
const backendDir = path17.join(projectDir, "backend");
|
|
@@ -6379,10 +6615,10 @@ function extractApisFromRegistry(registryContent) {
|
|
|
6379
6615
|
let match;
|
|
6380
6616
|
while ((match = apiRegex.exec(registryContent)) !== null) {
|
|
6381
6617
|
const method = match[1];
|
|
6382
|
-
const
|
|
6618
|
+
const path22 = match[2].trim();
|
|
6383
6619
|
const description = match[3].trim();
|
|
6384
|
-
const key = `${method}:${
|
|
6385
|
-
apis.set(key, { method, path:
|
|
6620
|
+
const key = `${method}:${path22}`;
|
|
6621
|
+
apis.set(key, { method, path: path22, description });
|
|
6386
6622
|
}
|
|
6387
6623
|
return apis;
|
|
6388
6624
|
}
|
|
@@ -6405,7 +6641,7 @@ var init_check_api = __esm({
|
|
|
6405
6641
|
init_esm_shims();
|
|
6406
6642
|
init_utils();
|
|
6407
6643
|
init_logger();
|
|
6408
|
-
checkApiCommand = new
|
|
6644
|
+
checkApiCommand = new Command13("check-api").description("API \u68C0\u67E5\uFF08\u51B2\u7A81/\u53D8\u66F4/Registry\uFF09").action(async () => {
|
|
6409
6645
|
try {
|
|
6410
6646
|
logger.header("API \u68C0\u67E5");
|
|
6411
6647
|
logger.newLine();
|
|
@@ -6415,7 +6651,7 @@ var init_check_api = __esm({
|
|
|
6415
6651
|
logger.info("\u8BF7\u5148\u8FD0\u884C 'team-cli init <project-name>' \u6216\u5207\u6362\u5230\u9879\u76EE\u76EE\u5F55");
|
|
6416
6652
|
process.exit(1);
|
|
6417
6653
|
}
|
|
6418
|
-
const answers = await
|
|
6654
|
+
const answers = await inquirer9.prompt([
|
|
6419
6655
|
{
|
|
6420
6656
|
type: "list",
|
|
6421
6657
|
name: "checkType",
|
|
@@ -6469,9 +6705,9 @@ var init_check_api = __esm({
|
|
|
6469
6705
|
});
|
|
6470
6706
|
|
|
6471
6707
|
// src/commands/logs.ts
|
|
6472
|
-
import { Command as
|
|
6708
|
+
import { Command as Command14 } from "commander";
|
|
6473
6709
|
import path18 from "path";
|
|
6474
|
-
import
|
|
6710
|
+
import inquirer10 from "inquirer";
|
|
6475
6711
|
async function collectLogFiles(targetDir) {
|
|
6476
6712
|
const logs = [];
|
|
6477
6713
|
try {
|
|
@@ -6495,7 +6731,7 @@ var init_logs = __esm({
|
|
|
6495
6731
|
init_esm_shims();
|
|
6496
6732
|
init_utils();
|
|
6497
6733
|
init_logger();
|
|
6498
|
-
logsCommand = new
|
|
6734
|
+
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") => {
|
|
6499
6735
|
try {
|
|
6500
6736
|
logger.header("\u4F1A\u8BDD\u65E5\u5FD7");
|
|
6501
6737
|
logger.newLine();
|
|
@@ -6512,103 +6748,537 @@ var init_logs = __esm({
|
|
|
6512
6748
|
logger.info("\u8FD0\u884C 'team-cli dev' \u540E\u4F1A\u81EA\u52A8\u751F\u6210\u65E5\u5FD7");
|
|
6513
6749
|
process.exit(0);
|
|
6514
6750
|
}
|
|
6515
|
-
let targetDir = "";
|
|
6516
|
-
let displayTitle = "";
|
|
6517
|
-
switch (filter) {
|
|
6518
|
-
case "":
|
|
6519
|
-
case "today": {
|
|
6520
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6521
|
-
targetDir = path18.join(sessionsDir, today);
|
|
6522
|
-
const todayExists = await FileUtils.exists(targetDir);
|
|
6523
|
-
if (!todayExists) {
|
|
6524
|
-
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
6525
|
-
process.exit(0);
|
|
6526
|
-
}
|
|
6527
|
-
displayTitle = "\u663E\u793A\u4ECA\u65E5\u4F1A\u8BDD\u65E5\u5FD7:";
|
|
6528
|
-
break;
|
|
6751
|
+
let targetDir = "";
|
|
6752
|
+
let displayTitle = "";
|
|
6753
|
+
switch (filter) {
|
|
6754
|
+
case "":
|
|
6755
|
+
case "today": {
|
|
6756
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6757
|
+
targetDir = path18.join(sessionsDir, today);
|
|
6758
|
+
const todayExists = await FileUtils.exists(targetDir);
|
|
6759
|
+
if (!todayExists) {
|
|
6760
|
+
logger.info("\u4ECA\u65E5\u6682\u65E0\u4F1A\u8BDD\u65E5\u5FD7");
|
|
6761
|
+
process.exit(0);
|
|
6762
|
+
}
|
|
6763
|
+
displayTitle = "\u663E\u793A\u4ECA\u65E5\u4F1A\u8BDD\u65E5\u5FD7:";
|
|
6764
|
+
break;
|
|
6765
|
+
}
|
|
6766
|
+
case "--all":
|
|
6767
|
+
case "-a": {
|
|
6768
|
+
targetDir = sessionsDir;
|
|
6769
|
+
displayTitle = "\u663E\u793A\u6240\u6709\u4F1A\u8BDD\u65E5\u5FD7:";
|
|
6770
|
+
break;
|
|
6771
|
+
}
|
|
6772
|
+
default: {
|
|
6773
|
+
targetDir = path18.join(sessionsDir, filter);
|
|
6774
|
+
const dateExists = await FileUtils.exists(targetDir);
|
|
6775
|
+
if (!dateExists) {
|
|
6776
|
+
logger.error(`\u672A\u627E\u5230\u65E5\u671F '${filter}' \u7684\u65E5\u5FD7`);
|
|
6777
|
+
logger.info("\u53EF\u7528\u65E5\u671F:");
|
|
6778
|
+
const entries = await FileUtils.findFiles("*/", sessionsDir);
|
|
6779
|
+
const dates = entries.slice(0, 10);
|
|
6780
|
+
for (const date of dates) {
|
|
6781
|
+
logger.info(` ${date.replace("/", "")}`);
|
|
6782
|
+
}
|
|
6783
|
+
process.exit(1);
|
|
6784
|
+
}
|
|
6785
|
+
displayTitle = `\u663E\u793A ${filter} \u7684\u4F1A\u8BDD\u65E5\u5FD7:`;
|
|
6786
|
+
break;
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
logger.info(displayTitle);
|
|
6790
|
+
logger.newLine();
|
|
6791
|
+
const logs = await collectLogFiles(targetDir);
|
|
6792
|
+
if (logs.length === 0) {
|
|
6793
|
+
logger.info("\u65E0\u65E5\u5FD7\u6587\u4EF6");
|
|
6794
|
+
process.exit(0);
|
|
6795
|
+
}
|
|
6796
|
+
for (let i = 0; i < logs.length; i++) {
|
|
6797
|
+
const relPath = path18.relative(sessionsDir, logs[i]);
|
|
6798
|
+
logger.step(`${i + 1}) ${relPath}`);
|
|
6799
|
+
}
|
|
6800
|
+
logger.newLine();
|
|
6801
|
+
const answers = await inquirer10.prompt([
|
|
6802
|
+
{
|
|
6803
|
+
type: "input",
|
|
6804
|
+
name: "selection",
|
|
6805
|
+
message: "\u8F93\u5165\u7F16\u53F7\u67E5\u770B\u8BE6\u60C5 (\u6216 Enter \u9000\u51FA):",
|
|
6806
|
+
default: ""
|
|
6807
|
+
}
|
|
6808
|
+
]);
|
|
6809
|
+
const selection = answers.selection.trim();
|
|
6810
|
+
if (selection === "") {
|
|
6811
|
+
process.exit(0);
|
|
6812
|
+
}
|
|
6813
|
+
const selectionNum = parseInt(selection, 10);
|
|
6814
|
+
if (isNaN(selectionNum) || selectionNum < 1 || selectionNum > logs.length) {
|
|
6815
|
+
logger.error("\u65E0\u6548\u7684\u9009\u62E9");
|
|
6816
|
+
process.exit(1);
|
|
6817
|
+
}
|
|
6818
|
+
const selectedLog = logs[selectionNum - 1];
|
|
6819
|
+
logger.newLine();
|
|
6820
|
+
logger.header("\u65E5\u5FD7\u8BE6\u60C5");
|
|
6821
|
+
logger.newLine();
|
|
6822
|
+
const content = await FileUtils.read(selectedLog);
|
|
6823
|
+
console.log(content);
|
|
6824
|
+
} catch (error) {
|
|
6825
|
+
logger.error(`\u67E5\u770B\u65E5\u5FD7\u5931\u8D25: ${error.message}`);
|
|
6826
|
+
if (process.env.DEBUG) {
|
|
6827
|
+
console.error(error);
|
|
6828
|
+
}
|
|
6829
|
+
process.exit(1);
|
|
6830
|
+
}
|
|
6831
|
+
});
|
|
6832
|
+
}
|
|
6833
|
+
});
|
|
6834
|
+
|
|
6835
|
+
// src/lib/user-config.ts
|
|
6836
|
+
var user_config_exports = {};
|
|
6837
|
+
__export(user_config_exports, {
|
|
6838
|
+
UserConfigManager: () => UserConfigManager,
|
|
6839
|
+
userConfigManager: () => userConfigManager
|
|
6840
|
+
});
|
|
6841
|
+
import path19 from "path";
|
|
6842
|
+
import os3 from "os";
|
|
6843
|
+
import crypto from "crypto";
|
|
6844
|
+
var UserConfigManager, userConfigManager;
|
|
6845
|
+
var init_user_config = __esm({
|
|
6846
|
+
"src/lib/user-config.ts"() {
|
|
6847
|
+
"use strict";
|
|
6848
|
+
init_esm_shims();
|
|
6849
|
+
init_utils();
|
|
6850
|
+
init_logger();
|
|
6851
|
+
UserConfigManager = class {
|
|
6852
|
+
configPath;
|
|
6853
|
+
constructor() {
|
|
6854
|
+
const configDir = path19.join(os3.homedir(), ".team-cli");
|
|
6855
|
+
this.configPath = path19.join(configDir, "config.json");
|
|
6856
|
+
}
|
|
6857
|
+
/**
|
|
6858
|
+
* 加载用户配置
|
|
6859
|
+
*/
|
|
6860
|
+
async load() {
|
|
6861
|
+
try {
|
|
6862
|
+
const exists = await FileUtils.exists(this.configPath);
|
|
6863
|
+
if (!exists) {
|
|
6864
|
+
return null;
|
|
6865
|
+
}
|
|
6866
|
+
const content = await FileUtils.read(this.configPath);
|
|
6867
|
+
const config = JSON.parse(content);
|
|
6868
|
+
if (config.gitlab?.accessToken) {
|
|
6869
|
+
config.gitlab.accessToken = this.decrypt(config.gitlab.accessToken);
|
|
6870
|
+
}
|
|
6871
|
+
return config;
|
|
6872
|
+
} catch (error) {
|
|
6873
|
+
logger.debug(`\u52A0\u8F7D\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
6874
|
+
return null;
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6877
|
+
/**
|
|
6878
|
+
* 保存用户配置
|
|
6879
|
+
*/
|
|
6880
|
+
async save(config) {
|
|
6881
|
+
try {
|
|
6882
|
+
const configDir = path19.dirname(this.configPath);
|
|
6883
|
+
await FileUtils.ensureDir(configDir);
|
|
6884
|
+
const configToSave = JSON.parse(JSON.stringify(config));
|
|
6885
|
+
if (configToSave.gitlab?.accessToken) {
|
|
6886
|
+
configToSave.gitlab.accessToken = this.encrypt(configToSave.gitlab.accessToken);
|
|
6887
|
+
}
|
|
6888
|
+
const content = JSON.stringify(configToSave, null, 2);
|
|
6889
|
+
await FileUtils.write(this.configPath, content);
|
|
6890
|
+
} catch (error) {
|
|
6891
|
+
throw new Error(`\u4FDD\u5B58\u7528\u6237\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
/**
|
|
6895
|
+
* 更新 GitLab Token
|
|
6896
|
+
*/
|
|
6897
|
+
async updateGitLabToken(token, baseUrl) {
|
|
6898
|
+
const config = await this.load() || {
|
|
6899
|
+
gitlab: {
|
|
6900
|
+
accessToken: "",
|
|
6901
|
+
baseUrl: "https://gitlab.com",
|
|
6902
|
+
timeout: 3e4
|
|
6903
|
+
}
|
|
6904
|
+
};
|
|
6905
|
+
config.gitlab.accessToken = token;
|
|
6906
|
+
if (baseUrl) {
|
|
6907
|
+
config.gitlab.baseUrl = baseUrl;
|
|
6908
|
+
}
|
|
6909
|
+
await this.save(config);
|
|
6910
|
+
}
|
|
6911
|
+
/**
|
|
6912
|
+
* 获取 GitLab Token
|
|
6913
|
+
*/
|
|
6914
|
+
async getGitLabToken() {
|
|
6915
|
+
const config = await this.load();
|
|
6916
|
+
return config?.gitlab?.accessToken || null;
|
|
6917
|
+
}
|
|
6918
|
+
/**
|
|
6919
|
+
* 获取 GitLab 配置
|
|
6920
|
+
*/
|
|
6921
|
+
async getGitLabConfig() {
|
|
6922
|
+
const config = await this.load();
|
|
6923
|
+
return config?.gitlab || null;
|
|
6924
|
+
}
|
|
6925
|
+
/**
|
|
6926
|
+
* 检查是否已有配置
|
|
6927
|
+
*/
|
|
6928
|
+
async hasConfig() {
|
|
6929
|
+
const config = await this.load();
|
|
6930
|
+
return config !== null && config.gitlab?.accessToken !== void 0;
|
|
6931
|
+
}
|
|
6932
|
+
/**
|
|
6933
|
+
* 删除配置
|
|
6934
|
+
*/
|
|
6935
|
+
async removeConfig() {
|
|
6936
|
+
try {
|
|
6937
|
+
const exists = await FileUtils.exists(this.configPath);
|
|
6938
|
+
if (exists) {
|
|
6939
|
+
await FileUtils.remove(this.configPath);
|
|
6940
|
+
}
|
|
6941
|
+
} catch (error) {
|
|
6942
|
+
throw new Error(`\u5220\u9664\u914D\u7F6E\u5931\u8D25: ${error}`);
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
/**
|
|
6946
|
+
* 简单加密 (使用机器特定密钥)
|
|
6947
|
+
*/
|
|
6948
|
+
encrypt(text) {
|
|
6949
|
+
const key = this.getMachineKey();
|
|
6950
|
+
const iv = crypto.randomBytes(16);
|
|
6951
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
|
6952
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
6953
|
+
encrypted += cipher.final("hex");
|
|
6954
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
6955
|
+
}
|
|
6956
|
+
/**
|
|
6957
|
+
* 简单解密
|
|
6958
|
+
*/
|
|
6959
|
+
decrypt(encryptedText) {
|
|
6960
|
+
try {
|
|
6961
|
+
const key = this.getMachineKey();
|
|
6962
|
+
const parts = encryptedText.split(":");
|
|
6963
|
+
const iv = Buffer.from(parts[0], "hex");
|
|
6964
|
+
const encrypted = parts[1];
|
|
6965
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
|
|
6966
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
6967
|
+
decrypted += decipher.final("utf8");
|
|
6968
|
+
return decrypted;
|
|
6969
|
+
} catch {
|
|
6970
|
+
return encryptedText;
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
/**
|
|
6974
|
+
* 获取机器特定密钥
|
|
6975
|
+
*/
|
|
6976
|
+
getMachineKey() {
|
|
6977
|
+
const hostname = os3.hostname();
|
|
6978
|
+
const platform = os3.platform();
|
|
6979
|
+
const arch = os3.arch();
|
|
6980
|
+
const cpus = os3.cpus();
|
|
6981
|
+
const machineInfo = `${hostname}-${platform}-${arch}-${cpus[0]?.model || "unknown"}`;
|
|
6982
|
+
return crypto.createHash("sha256").update(machineInfo).digest();
|
|
6983
|
+
}
|
|
6984
|
+
/**
|
|
6985
|
+
* 获取配置目录
|
|
6986
|
+
*/
|
|
6987
|
+
getConfigDir() {
|
|
6988
|
+
return path19.dirname(this.configPath);
|
|
6989
|
+
}
|
|
6990
|
+
/**
|
|
6991
|
+
* 获取配置文件路径
|
|
6992
|
+
*/
|
|
6993
|
+
getConfigPath() {
|
|
6994
|
+
return this.configPath;
|
|
6995
|
+
}
|
|
6996
|
+
};
|
|
6997
|
+
userConfigManager = new UserConfigManager();
|
|
6998
|
+
}
|
|
6999
|
+
});
|
|
7000
|
+
|
|
7001
|
+
// src/lib/gitlab-api.ts
|
|
7002
|
+
var gitlab_api_exports = {};
|
|
7003
|
+
__export(gitlab_api_exports, {
|
|
7004
|
+
GitLabAPI: () => GitLabAPI
|
|
7005
|
+
});
|
|
7006
|
+
var GitLabAPI;
|
|
7007
|
+
var init_gitlab_api = __esm({
|
|
7008
|
+
"src/lib/gitlab-api.ts"() {
|
|
7009
|
+
"use strict";
|
|
7010
|
+
init_esm_shims();
|
|
7011
|
+
GitLabAPI = class {
|
|
7012
|
+
config;
|
|
7013
|
+
defaultTimeout = 3e4;
|
|
7014
|
+
constructor(config) {
|
|
7015
|
+
this.config = {
|
|
7016
|
+
...config,
|
|
7017
|
+
timeout: config.timeout || this.defaultTimeout
|
|
7018
|
+
};
|
|
7019
|
+
}
|
|
7020
|
+
/**
|
|
7021
|
+
* 验证 Token 是否有效
|
|
7022
|
+
*/
|
|
7023
|
+
async authenticate() {
|
|
7024
|
+
try {
|
|
7025
|
+
const response = await this.request("/user");
|
|
7026
|
+
return response.ok;
|
|
7027
|
+
} catch (error) {
|
|
7028
|
+
return false;
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
/**
|
|
7032
|
+
* 列出项目的所有 Tags
|
|
7033
|
+
*/
|
|
7034
|
+
async listTags(projectPath) {
|
|
7035
|
+
try {
|
|
7036
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
7037
|
+
const response = await this.request(`/projects/${encodedPath}/repository/tags?per_page=100`);
|
|
7038
|
+
if (!response.ok) {
|
|
7039
|
+
throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
|
|
7040
|
+
}
|
|
7041
|
+
const tags = await response.json();
|
|
7042
|
+
return tags.sort((a, b) => {
|
|
7043
|
+
return this.compareVersionStrings(b.name, a.name);
|
|
7044
|
+
});
|
|
7045
|
+
} catch (error) {
|
|
7046
|
+
throw new Error(`\u83B7\u53D6 Tags \u5931\u8D25: ${error}`);
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
/**
|
|
7050
|
+
* 列出项目的所有 Branches
|
|
7051
|
+
*/
|
|
7052
|
+
async listBranches(projectPath) {
|
|
7053
|
+
try {
|
|
7054
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
7055
|
+
const response = await this.request(`/projects/${encodedPath}/repository/branches?per_page=100`);
|
|
7056
|
+
if (!response.ok) {
|
|
7057
|
+
throw new Error(`GitLab API \u8BF7\u6C42\u5931\u8D25: ${response.status}`);
|
|
7058
|
+
}
|
|
7059
|
+
const branches = await response.json();
|
|
7060
|
+
return branches.sort((a, b) => {
|
|
7061
|
+
if (a.default) return -1;
|
|
7062
|
+
if (b.default) return 1;
|
|
7063
|
+
return a.name.localeCompare(b.name);
|
|
7064
|
+
});
|
|
7065
|
+
} catch (error) {
|
|
7066
|
+
throw new Error(`\u83B7\u53D6 Branches \u5931\u8D25: ${error}`);
|
|
7067
|
+
}
|
|
7068
|
+
}
|
|
7069
|
+
/**
|
|
7070
|
+
* 验证 Tag 是否存在
|
|
7071
|
+
*/
|
|
7072
|
+
async validateTag(projectPath, tag) {
|
|
7073
|
+
try {
|
|
7074
|
+
const tags = await this.listTags(projectPath);
|
|
7075
|
+
return tags.some((t) => t.name === tag);
|
|
7076
|
+
} catch {
|
|
7077
|
+
return false;
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7080
|
+
/**
|
|
7081
|
+
* 验证 Branch 是否存在
|
|
7082
|
+
*/
|
|
7083
|
+
async validateBranch(projectPath, branch) {
|
|
7084
|
+
try {
|
|
7085
|
+
const branches = await this.listBranches(projectPath);
|
|
7086
|
+
return branches.some((b) => b.name === branch);
|
|
7087
|
+
} catch {
|
|
7088
|
+
return false;
|
|
7089
|
+
}
|
|
7090
|
+
}
|
|
7091
|
+
/**
|
|
7092
|
+
* 获取项目信息
|
|
7093
|
+
*/
|
|
7094
|
+
async getProject(projectPath) {
|
|
7095
|
+
try {
|
|
7096
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
7097
|
+
const response = await this.request(`/projects/${encodedPath}`);
|
|
7098
|
+
if (!response.ok) {
|
|
7099
|
+
return null;
|
|
6529
7100
|
}
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
7101
|
+
return await response.json();
|
|
7102
|
+
} catch {
|
|
7103
|
+
return null;
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
/**
|
|
7107
|
+
* 获取指定 Tag 的 commit 信息
|
|
7108
|
+
*/
|
|
7109
|
+
async getTagCommit(projectPath, tag) {
|
|
7110
|
+
try {
|
|
7111
|
+
const tags = await this.listTags(projectPath);
|
|
7112
|
+
const targetTag = tags.find((t) => t.name === tag);
|
|
7113
|
+
return targetTag?.commit.id || null;
|
|
7114
|
+
} catch {
|
|
7115
|
+
return null;
|
|
7116
|
+
}
|
|
7117
|
+
}
|
|
7118
|
+
/**
|
|
7119
|
+
* 获取指定 Branch 的最新 commit 信息
|
|
7120
|
+
*/
|
|
7121
|
+
async getBranchCommit(projectPath, branch) {
|
|
7122
|
+
try {
|
|
7123
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
7124
|
+
const response = await this.request(
|
|
7125
|
+
`/projects/${encodedPath}/repository/branches/${encodeURIComponent(branch)}`
|
|
7126
|
+
);
|
|
7127
|
+
if (!response.ok) {
|
|
7128
|
+
return null;
|
|
6535
7129
|
}
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
7130
|
+
const branchInfo = await response.json();
|
|
7131
|
+
return branchInfo.commit.id;
|
|
7132
|
+
} catch {
|
|
7133
|
+
return null;
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
7136
|
+
/**
|
|
7137
|
+
* 对比两个版本之间的差异
|
|
7138
|
+
*/
|
|
7139
|
+
async compareVersions(projectPath, from, to) {
|
|
7140
|
+
try {
|
|
7141
|
+
const encodedPath = this.encodeProjectPath(projectPath);
|
|
7142
|
+
const response = await this.request(
|
|
7143
|
+
`/projects/${encodedPath}/repository/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`
|
|
7144
|
+
);
|
|
7145
|
+
if (!response.ok) {
|
|
7146
|
+
return null;
|
|
6551
7147
|
}
|
|
7148
|
+
return await response.json();
|
|
7149
|
+
} catch {
|
|
7150
|
+
return null;
|
|
6552
7151
|
}
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
7152
|
+
}
|
|
7153
|
+
/**
|
|
7154
|
+
* 比较两个版本号(用于排序)
|
|
7155
|
+
* 返回值: -1 (v1 < v2), 0 (v1 == v2), 1 (v1 > v2)
|
|
7156
|
+
*/
|
|
7157
|
+
compareVersionStrings(v1, v2) {
|
|
7158
|
+
const version1 = v1.replace(/^v/, "");
|
|
7159
|
+
const version2 = v2.replace(/^v/, "");
|
|
7160
|
+
const parts1 = version1.split(".").map((p) => parseInt(p, 10) || 0);
|
|
7161
|
+
const parts2 = version2.split(".").map((p) => parseInt(p, 10) || 0);
|
|
7162
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
7163
|
+
const p1 = parts1[i] || 0;
|
|
7164
|
+
const p2 = parts2[i] || 0;
|
|
7165
|
+
if (p1 > p2) return 1;
|
|
7166
|
+
if (p1 < p2) return -1;
|
|
6559
7167
|
}
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
7168
|
+
return 0;
|
|
7169
|
+
}
|
|
7170
|
+
/**
|
|
7171
|
+
* 获取最新的版本(优先 Tag,否则 latest commit)
|
|
7172
|
+
*/
|
|
7173
|
+
async getLatestVersion(projectPath) {
|
|
7174
|
+
try {
|
|
7175
|
+
const tags = await this.listTags(projectPath);
|
|
7176
|
+
const branches = await this.listBranches(projectPath);
|
|
7177
|
+
if (tags.length > 0) {
|
|
7178
|
+
const latestTag = tags[0];
|
|
7179
|
+
return {
|
|
7180
|
+
type: "tag",
|
|
7181
|
+
name: latestTag.name,
|
|
7182
|
+
commit: latestTag.commit.id
|
|
7183
|
+
};
|
|
7184
|
+
}
|
|
7185
|
+
const defaultBranch = branches.find((b) => b.default);
|
|
7186
|
+
if (defaultBranch) {
|
|
7187
|
+
return {
|
|
7188
|
+
type: "commit",
|
|
7189
|
+
name: defaultBranch.name,
|
|
7190
|
+
commit: defaultBranch.commit.id
|
|
7191
|
+
};
|
|
7192
|
+
}
|
|
7193
|
+
return null;
|
|
7194
|
+
} catch {
|
|
7195
|
+
return null;
|
|
6563
7196
|
}
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
7197
|
+
}
|
|
7198
|
+
/**
|
|
7199
|
+
* 解析项目路径
|
|
7200
|
+
* 从 Git URL 中提取项目路径
|
|
7201
|
+
*/
|
|
7202
|
+
static parseProjectPath(repository) {
|
|
7203
|
+
let path22 = repository;
|
|
7204
|
+
if (path22.startsWith("git@")) {
|
|
7205
|
+
path22 = path22.replace(/^git@/, "");
|
|
7206
|
+
const colonIndex = path22.indexOf(":");
|
|
7207
|
+
if (colonIndex !== -1) {
|
|
7208
|
+
path22 = path22.substring(colonIndex + 1);
|
|
7209
|
+
}
|
|
7210
|
+
} else {
|
|
7211
|
+
path22 = path22.replace(/^https?:\/\//, "");
|
|
7212
|
+
const parts = path22.split("/");
|
|
7213
|
+
if (parts.length > 1) {
|
|
7214
|
+
path22 = parts.slice(1).join("/");
|
|
6571
7215
|
}
|
|
6572
|
-
]);
|
|
6573
|
-
const selection = answers.selection.trim();
|
|
6574
|
-
if (selection === "") {
|
|
6575
|
-
process.exit(0);
|
|
6576
7216
|
}
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
7217
|
+
path22 = path22.replace(/\.git$/, "");
|
|
7218
|
+
return path22;
|
|
7219
|
+
}
|
|
7220
|
+
/**
|
|
7221
|
+
* 编码项目路径用于 API 请求
|
|
7222
|
+
*/
|
|
7223
|
+
encodeProjectPath(projectPath) {
|
|
7224
|
+
return encodeURIComponent(projectPath).replace(/%2F/g, "%2F");
|
|
7225
|
+
}
|
|
7226
|
+
/**
|
|
7227
|
+
* 发送 HTTP 请求
|
|
7228
|
+
*/
|
|
7229
|
+
async request(endpoint, options = {}) {
|
|
7230
|
+
const url = `${this.config.baseUrl}/api/v4${endpoint}`;
|
|
7231
|
+
const headers = {
|
|
7232
|
+
"PRIVATE-TOKEN": this.config.accessToken,
|
|
7233
|
+
"Content-Type": "application/json",
|
|
7234
|
+
...options.headers
|
|
7235
|
+
};
|
|
7236
|
+
const controller = new AbortController();
|
|
7237
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
7238
|
+
try {
|
|
7239
|
+
const response = await fetch(url, {
|
|
7240
|
+
...options,
|
|
7241
|
+
headers,
|
|
7242
|
+
signal: controller.signal
|
|
7243
|
+
});
|
|
7244
|
+
return response;
|
|
7245
|
+
} catch (error) {
|
|
7246
|
+
if (error.name === "AbortError") {
|
|
7247
|
+
throw new Error("\u8BF7\u6C42\u8D85\u65F6");
|
|
7248
|
+
}
|
|
7249
|
+
throw error;
|
|
7250
|
+
} finally {
|
|
7251
|
+
clearTimeout(timeoutId);
|
|
6581
7252
|
}
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
7253
|
+
}
|
|
7254
|
+
/**
|
|
7255
|
+
* 获取默认分支
|
|
7256
|
+
*/
|
|
7257
|
+
async getDefaultBranch(projectPath) {
|
|
7258
|
+
try {
|
|
7259
|
+
const project = await this.getProject(projectPath);
|
|
7260
|
+
return project?.default_branch || null;
|
|
7261
|
+
} catch {
|
|
7262
|
+
return null;
|
|
6592
7263
|
}
|
|
6593
|
-
process.exit(1);
|
|
6594
7264
|
}
|
|
6595
|
-
}
|
|
7265
|
+
};
|
|
6596
7266
|
}
|
|
6597
7267
|
});
|
|
6598
7268
|
|
|
6599
7269
|
// src/commands/update.ts
|
|
6600
|
-
import { Command as
|
|
6601
|
-
import
|
|
7270
|
+
import { Command as Command15 } from "commander";
|
|
7271
|
+
import path20 from "path";
|
|
6602
7272
|
import { execa as execa4 } from "execa";
|
|
6603
|
-
import
|
|
6604
|
-
import
|
|
7273
|
+
import inquirer11 from "inquirer";
|
|
7274
|
+
import fs5 from "fs-extra";
|
|
6605
7275
|
async function performUpdate(projectPath, updates) {
|
|
6606
7276
|
logger.newLine();
|
|
6607
7277
|
logger.info("\u5F00\u59CB\u66F4\u65B0\u6A21\u677F...");
|
|
6608
7278
|
for (const update of updates) {
|
|
6609
7279
|
const { type, info, updateOptions } = update;
|
|
6610
7280
|
const targetDir = type === "frontend" ? "frontend" : "backend";
|
|
6611
|
-
const targetPath =
|
|
7281
|
+
const targetPath = path20.join(projectPath, targetDir);
|
|
6612
7282
|
logger.newLine();
|
|
6613
7283
|
logger.step(`\u66F4\u65B0 ${type === "frontend" ? "\u524D\u7AEF" : "\u540E\u7AEF"}\u6A21\u677F...`);
|
|
6614
7284
|
if (updateOptions?.tag || updateOptions?.branch) {
|
|
@@ -6639,8 +7309,8 @@ async function performUpdate(projectPath, updates) {
|
|
|
6639
7309
|
}
|
|
6640
7310
|
}
|
|
6641
7311
|
const ref = updateOptions?.tag || updateOptions?.branch || "HEAD";
|
|
6642
|
-
const backupDir =
|
|
6643
|
-
await
|
|
7312
|
+
const backupDir = path20.join(projectPath, `.backup-${Date.now()}`);
|
|
7313
|
+
await fs5.copy(targetPath, path20.join(backupDir, targetDir));
|
|
6644
7314
|
logger.info(`\u5DF2\u521B\u5EFA\u5907\u4EFD: ${backupDir}`);
|
|
6645
7315
|
if (updateOptions?.dryRun) {
|
|
6646
7316
|
logger.info("[Dry Run] \u5C06\u4F1A\u66F4\u65B0\u5230\u4EE5\u4E0B\u7248\u672C:");
|
|
@@ -6650,7 +7320,7 @@ async function performUpdate(projectPath, updates) {
|
|
|
6650
7320
|
continue;
|
|
6651
7321
|
}
|
|
6652
7322
|
try {
|
|
6653
|
-
const tempDir =
|
|
7323
|
+
const tempDir = path20.join(projectPath, `.template-update-${Date.now()}`);
|
|
6654
7324
|
await execa4("git", ["clone", "--depth=1", "--branch", ref, info.repository, tempDir], {
|
|
6655
7325
|
stdio: "pipe"
|
|
6656
7326
|
});
|
|
@@ -6667,17 +7337,17 @@ async function performUpdate(projectPath, updates) {
|
|
|
6667
7337
|
const currentFiles = await FileUtils.findFiles("*", targetPath);
|
|
6668
7338
|
for (const file of currentFiles) {
|
|
6669
7339
|
if (!keepFiles.includes(file)) {
|
|
6670
|
-
const filePath =
|
|
7340
|
+
const filePath = path20.join(targetPath, file);
|
|
6671
7341
|
try {
|
|
6672
|
-
await
|
|
7342
|
+
await fs5.remove(filePath);
|
|
6673
7343
|
} catch {
|
|
6674
7344
|
}
|
|
6675
7345
|
}
|
|
6676
7346
|
}
|
|
6677
|
-
await
|
|
7347
|
+
await fs5.copy(tempDir, targetPath, {
|
|
6678
7348
|
filter: (src) => !src.includes(".git")
|
|
6679
7349
|
});
|
|
6680
|
-
await
|
|
7350
|
+
await fs5.remove(tempDir);
|
|
6681
7351
|
await updateTemplateVersion(projectPath, type, commit.trim(), {
|
|
6682
7352
|
tag: updateOptions?.tag || latestTag,
|
|
6683
7353
|
branch: updateOptions?.branch
|
|
@@ -6688,9 +7358,9 @@ async function performUpdate(projectPath, updates) {
|
|
|
6688
7358
|
} catch (error) {
|
|
6689
7359
|
logger.error(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
6690
7360
|
logger.info("\u6B63\u5728\u6062\u590D\u5907\u4EFD...");
|
|
6691
|
-
await
|
|
6692
|
-
await
|
|
6693
|
-
await
|
|
7361
|
+
await fs5.remove(targetPath);
|
|
7362
|
+
await fs5.copy(path20.join(backupDir, targetDir), targetPath);
|
|
7363
|
+
await fs5.remove(backupDir);
|
|
6694
7364
|
logger.info("\u5DF2\u6062\u590D\u5230\u66F4\u65B0\u524D\u7684\u72B6\u6001");
|
|
6695
7365
|
}
|
|
6696
7366
|
}
|
|
@@ -6712,7 +7382,7 @@ var init_update = __esm({
|
|
|
6712
7382
|
init_template_version();
|
|
6713
7383
|
init_logger();
|
|
6714
7384
|
init_utils();
|
|
6715
|
-
updateCommand = new
|
|
7385
|
+
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
7386
|
try {
|
|
6717
7387
|
logger.header("\u6A21\u677F\u7248\u672C\u68C0\u67E5");
|
|
6718
7388
|
logger.newLine();
|
|
@@ -6784,7 +7454,7 @@ var init_update = __esm({
|
|
|
6784
7454
|
logger.info("Dry run \u6A21\u5F0F\uFF0C\u4E0D\u6267\u884C\u5B9E\u9645\u66F4\u65B0");
|
|
6785
7455
|
return;
|
|
6786
7456
|
}
|
|
6787
|
-
const answers = await
|
|
7457
|
+
const answers = await inquirer11.prompt([
|
|
6788
7458
|
{
|
|
6789
7459
|
type: "confirm",
|
|
6790
7460
|
name: "shouldUpdate",
|
|
@@ -6809,8 +7479,8 @@ var init_update = __esm({
|
|
|
6809
7479
|
});
|
|
6810
7480
|
|
|
6811
7481
|
// src/commands/config.ts
|
|
6812
|
-
import { Command as
|
|
6813
|
-
import
|
|
7482
|
+
import { Command as Command16 } from "commander";
|
|
7483
|
+
import inquirer12 from "inquirer";
|
|
6814
7484
|
import chalk2 from "chalk";
|
|
6815
7485
|
var setTokenCommand, showConfigCommand, removeConfigCommand, validateTokenCommand, configCommand;
|
|
6816
7486
|
var init_config = __esm({
|
|
@@ -6820,13 +7490,13 @@ var init_config = __esm({
|
|
|
6820
7490
|
init_user_config();
|
|
6821
7491
|
init_gitlab_api();
|
|
6822
7492
|
init_logger();
|
|
6823
|
-
setTokenCommand = new
|
|
7493
|
+
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
7494
|
try {
|
|
6825
7495
|
logger.header("GitLab Access Token \u914D\u7F6E");
|
|
6826
7496
|
logger.newLine();
|
|
6827
7497
|
let { token, url } = options;
|
|
6828
7498
|
if (!token) {
|
|
6829
|
-
const answers = await
|
|
7499
|
+
const answers = await inquirer12.prompt([
|
|
6830
7500
|
{
|
|
6831
7501
|
type: "password",
|
|
6832
7502
|
name: "token",
|
|
@@ -6888,7 +7558,7 @@ var init_config = __esm({
|
|
|
6888
7558
|
process.exit(1);
|
|
6889
7559
|
}
|
|
6890
7560
|
});
|
|
6891
|
-
showConfigCommand = new
|
|
7561
|
+
showConfigCommand = new Command16("show").description("\u663E\u793A\u5F53\u524D\u914D\u7F6E").action(async () => {
|
|
6892
7562
|
try {
|
|
6893
7563
|
logger.header("GitLab \u914D\u7F6E");
|
|
6894
7564
|
logger.newLine();
|
|
@@ -6927,14 +7597,14 @@ var init_config = __esm({
|
|
|
6927
7597
|
process.exit(1);
|
|
6928
7598
|
}
|
|
6929
7599
|
});
|
|
6930
|
-
removeConfigCommand = new
|
|
7600
|
+
removeConfigCommand = new Command16("remove").alias("rm").description("\u5220\u9664 GitLab \u914D\u7F6E").action(async () => {
|
|
6931
7601
|
try {
|
|
6932
7602
|
const hasConfig = await userConfigManager.hasConfig();
|
|
6933
7603
|
if (!hasConfig) {
|
|
6934
7604
|
logger.warn("\u672A\u914D\u7F6E GitLab Access Token");
|
|
6935
7605
|
return;
|
|
6936
7606
|
}
|
|
6937
|
-
const answers = await
|
|
7607
|
+
const answers = await inquirer12.prompt([
|
|
6938
7608
|
{
|
|
6939
7609
|
type: "confirm",
|
|
6940
7610
|
name: "confirm",
|
|
@@ -6956,7 +7626,7 @@ var init_config = __esm({
|
|
|
6956
7626
|
process.exit(1);
|
|
6957
7627
|
}
|
|
6958
7628
|
});
|
|
6959
|
-
validateTokenCommand = new
|
|
7629
|
+
validateTokenCommand = new Command16("validate").alias("test").description("\u9A8C\u8BC1\u5F53\u524D Token \u662F\u5426\u6709\u6548").action(async () => {
|
|
6960
7630
|
try {
|
|
6961
7631
|
logger.header("\u9A8C\u8BC1 GitLab Token");
|
|
6962
7632
|
logger.newLine();
|
|
@@ -6986,12 +7656,12 @@ var init_config = __esm({
|
|
|
6986
7656
|
process.exit(1);
|
|
6987
7657
|
}
|
|
6988
7658
|
});
|
|
6989
|
-
configCommand = new
|
|
7659
|
+
configCommand = new Command16("config").description("\u7BA1\u7406 GitLab \u914D\u7F6E").addCommand(setTokenCommand).addCommand(showConfigCommand).addCommand(removeConfigCommand).addCommand(validateTokenCommand);
|
|
6990
7660
|
}
|
|
6991
7661
|
});
|
|
6992
7662
|
|
|
6993
7663
|
// src/commands/diff.ts
|
|
6994
|
-
import { Command as
|
|
7664
|
+
import { Command as Command17 } from "commander";
|
|
6995
7665
|
import chalk3 from "chalk";
|
|
6996
7666
|
async function compareTemplate(projectPath, type, localConfig, remoteTag, remoteBranch, gitlabConfig) {
|
|
6997
7667
|
try {
|
|
@@ -7170,7 +7840,7 @@ var init_diff = __esm({
|
|
|
7170
7840
|
init_utils();
|
|
7171
7841
|
init_user_config();
|
|
7172
7842
|
init_gitlab_api();
|
|
7173
|
-
diffCommand = new
|
|
7843
|
+
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
7844
|
try {
|
|
7175
7845
|
logger.header("\u6A21\u677F\u7248\u672C\u5BF9\u6BD4");
|
|
7176
7846
|
logger.newLine();
|
|
@@ -7292,10 +7962,10 @@ var init_diff = __esm({
|
|
|
7292
7962
|
|
|
7293
7963
|
// src/index.ts
|
|
7294
7964
|
var index_exports = {};
|
|
7295
|
-
import { Command as
|
|
7965
|
+
import { Command as Command18 } from "commander";
|
|
7296
7966
|
import chalk4 from "chalk";
|
|
7297
|
-
import
|
|
7298
|
-
import
|
|
7967
|
+
import fs6 from "fs-extra";
|
|
7968
|
+
import path21 from "path";
|
|
7299
7969
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7300
7970
|
function showHelp() {
|
|
7301
7971
|
console.log("");
|
|
@@ -7307,6 +7977,7 @@ function showHelp() {
|
|
|
7307
7977
|
console.log(" team-cli breakdown [spec-file] \u5C06 spec \u62C6\u5206\u4E3A milestones \u548C todos");
|
|
7308
7978
|
console.log(" team-cli dev \u5F00\u53D1\u6A21\u5F0F\uFF0C\u6267\u884C\u5177\u4F53\u4EFB\u52A1");
|
|
7309
7979
|
console.log(" team-cli accept [spec-file] \u9A8C\u6536\u529F\u80FD\uFF0C\u8D70\u67E5\u6240\u6709\u9700\u6C42");
|
|
7980
|
+
console.log(" team-cli add-module \u4E3A\u5DF2\u6709\u9879\u76EE\u65B0\u589E\u6A21\u5757");
|
|
7310
7981
|
console.log(" team-cli add-feature <name> \u6DFB\u52A0\u65B0\u529F\u80FD");
|
|
7311
7982
|
console.log(" team-cli bugfix \u521B\u5EFA Bugfix \u8BB0\u5F55");
|
|
7312
7983
|
console.log(" team-cli hotfix \u521B\u5EFA Hotfix");
|
|
@@ -7367,6 +8038,7 @@ var init_index = __esm({
|
|
|
7367
8038
|
init_bugfix();
|
|
7368
8039
|
init_bugfix();
|
|
7369
8040
|
init_accept();
|
|
8041
|
+
init_add_module();
|
|
7370
8042
|
init_lint();
|
|
7371
8043
|
init_status();
|
|
7372
8044
|
init_detect_deps();
|
|
@@ -7376,9 +8048,9 @@ var init_index = __esm({
|
|
|
7376
8048
|
init_update();
|
|
7377
8049
|
init_config();
|
|
7378
8050
|
init_diff();
|
|
7379
|
-
__dirname2 =
|
|
7380
|
-
pkg =
|
|
7381
|
-
program = new
|
|
8051
|
+
__dirname2 = path21.dirname(fileURLToPath2(import.meta.url));
|
|
8052
|
+
pkg = fs6.readJsonSync(path21.join(__dirname2, "../package.json"));
|
|
8053
|
+
program = new Command18();
|
|
7382
8054
|
program.name("team-cli").description("AI-Native \u56E2\u961F\u7814\u53D1\u811A\u624B\u67B6").version(pkg.version);
|
|
7383
8055
|
program.option("-v, --verbose", "\u8BE6\u7EC6\u8F93\u51FA\u6A21\u5F0F").option("--debug", "\u8C03\u8BD5\u6A21\u5F0F");
|
|
7384
8056
|
program.addCommand(initCommand);
|
|
@@ -7389,6 +8061,7 @@ var init_index = __esm({
|
|
|
7389
8061
|
program.addCommand(bugfixCommand);
|
|
7390
8062
|
program.addCommand(hotfixCommand);
|
|
7391
8063
|
program.addCommand(acceptCommand);
|
|
8064
|
+
program.addCommand(addModuleCommand);
|
|
7392
8065
|
program.addCommand(lintCommand);
|
|
7393
8066
|
program.addCommand(statusCommand);
|
|
7394
8067
|
program.addCommand(detectDepsCommand);
|