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