@team-semicolon/semo-cli 3.0.4 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +217 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,7 +59,50 @@ const child_process_1 = require("child_process");
|
|
|
59
59
|
const fs = __importStar(require("fs"));
|
|
60
60
|
const path = __importStar(require("path"));
|
|
61
61
|
const os = __importStar(require("os"));
|
|
62
|
-
const VERSION = "3.0.
|
|
62
|
+
const VERSION = "3.0.6";
|
|
63
|
+
const PACKAGE_NAME = "@team-semicolon/semo-cli";
|
|
64
|
+
// === 버전 비교 유틸리티 ===
|
|
65
|
+
/**
|
|
66
|
+
* npm registry에서 최신 버전을 가져옴
|
|
67
|
+
*/
|
|
68
|
+
async function getLatestVersion() {
|
|
69
|
+
try {
|
|
70
|
+
const result = (0, child_process_1.execSync)(`npm view ${PACKAGE_NAME} version`, {
|
|
71
|
+
stdio: "pipe",
|
|
72
|
+
encoding: "utf-8",
|
|
73
|
+
timeout: 10000, // 10초 타임아웃
|
|
74
|
+
});
|
|
75
|
+
return result.trim();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 시맨틱 버전 비교 (v1이 v2보다 낮으면 true)
|
|
83
|
+
* 예: isVersionLower("1.0.0", "1.0.1") => true
|
|
84
|
+
*/
|
|
85
|
+
function isVersionLower(current, latest) {
|
|
86
|
+
// alpha, beta 등 pre-release 태그 제거 후 비교
|
|
87
|
+
const cleanVersion = (v) => v.replace(/-.*$/, "");
|
|
88
|
+
const currentParts = cleanVersion(current).split(".").map(Number);
|
|
89
|
+
const latestParts = cleanVersion(latest).split(".").map(Number);
|
|
90
|
+
for (let i = 0; i < 3; i++) {
|
|
91
|
+
const c = currentParts[i] || 0;
|
|
92
|
+
const l = latestParts[i] || 0;
|
|
93
|
+
if (c < l)
|
|
94
|
+
return true;
|
|
95
|
+
if (c > l)
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
// 숫자가 같으면 pre-release 여부 확인
|
|
99
|
+
// current가 pre-release이고 latest가 정식이면 낮은 버전
|
|
100
|
+
const currentIsPrerelease = current.includes("-");
|
|
101
|
+
const latestIsPrerelease = latest.includes("-");
|
|
102
|
+
if (currentIsPrerelease && !latestIsPrerelease)
|
|
103
|
+
return true;
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
63
106
|
// === Windows 지원 유틸리티 ===
|
|
64
107
|
const isWindows = os.platform() === "win32";
|
|
65
108
|
/**
|
|
@@ -153,11 +196,96 @@ const LEGACY_MAPPING = {
|
|
|
153
196
|
design: "biz/design",
|
|
154
197
|
mvp: "biz/poc",
|
|
155
198
|
};
|
|
199
|
+
// 그룹 이름 목록 (biz, eng, ops)
|
|
200
|
+
const PACKAGE_GROUPS = ["biz", "eng", "ops", "meta"];
|
|
201
|
+
// 그룹명 → 해당 그룹의 모든 패키지 반환
|
|
202
|
+
function getPackagesByGroup(group) {
|
|
203
|
+
return Object.entries(EXTENSION_PACKAGES)
|
|
204
|
+
.filter(([, pkg]) => pkg.layer === group)
|
|
205
|
+
.map(([key]) => key);
|
|
206
|
+
}
|
|
207
|
+
// 패키지 입력을 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
208
|
+
function resolvePackageInput(input) {
|
|
209
|
+
// 쉼표로 구분된 여러 패키지 처리
|
|
210
|
+
const parts = input.split(",").map(p => p.trim()).filter(p => p);
|
|
211
|
+
const resolvedPackages = [];
|
|
212
|
+
let isGroup = false;
|
|
213
|
+
let groupName;
|
|
214
|
+
for (const part of parts) {
|
|
215
|
+
// 1. 그룹명인지 확인 (biz, eng, ops, meta)
|
|
216
|
+
if (PACKAGE_GROUPS.includes(part)) {
|
|
217
|
+
const groupPackages = getPackagesByGroup(part);
|
|
218
|
+
resolvedPackages.push(...groupPackages);
|
|
219
|
+
isGroup = true;
|
|
220
|
+
groupName = part;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// 2. 레거시 매핑 확인
|
|
224
|
+
if (part in LEGACY_MAPPING) {
|
|
225
|
+
resolvedPackages.push(LEGACY_MAPPING[part]);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
// 3. 직접 패키지명 확인
|
|
229
|
+
if (part in EXTENSION_PACKAGES) {
|
|
230
|
+
resolvedPackages.push(part);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
// 4. 유효하지 않은 패키지명
|
|
234
|
+
// (빈 배열 대신 null을 추가하여 나중에 에러 처리)
|
|
235
|
+
}
|
|
236
|
+
// 중복 제거
|
|
237
|
+
return {
|
|
238
|
+
packages: [...new Set(resolvedPackages)],
|
|
239
|
+
isGroup,
|
|
240
|
+
groupName
|
|
241
|
+
};
|
|
242
|
+
}
|
|
156
243
|
const program = new commander_1.Command();
|
|
157
244
|
program
|
|
158
245
|
.name("semo")
|
|
159
246
|
.description("SEMO CLI - AI Agent Orchestration Framework")
|
|
160
|
-
.version(VERSION);
|
|
247
|
+
.version(VERSION, "-V, --version-simple", "버전 번호만 출력");
|
|
248
|
+
// === version 명령어 (상세 버전 정보) ===
|
|
249
|
+
program
|
|
250
|
+
.command("version")
|
|
251
|
+
.description("버전 정보 및 업데이트 확인")
|
|
252
|
+
.action(async () => {
|
|
253
|
+
await showVersionInfo();
|
|
254
|
+
});
|
|
255
|
+
/**
|
|
256
|
+
* 상세 버전 정보 표시 및 업데이트 확인
|
|
257
|
+
*/
|
|
258
|
+
async function showVersionInfo() {
|
|
259
|
+
console.log(chalk_1.default.cyan.bold("\n📦 SEMO CLI 버전 정보\n"));
|
|
260
|
+
// 현재 버전 표시
|
|
261
|
+
console.log(chalk_1.default.white(` 현재 버전: ${chalk_1.default.green.bold(VERSION)}`));
|
|
262
|
+
// 최신 버전 확인
|
|
263
|
+
const spinner = (0, ora_1.default)(" 최신 버전 확인 중...").start();
|
|
264
|
+
const latestVersion = await getLatestVersion();
|
|
265
|
+
if (latestVersion === null) {
|
|
266
|
+
spinner.warn(" 최신 버전 확인 실패 (네트워크 오류)");
|
|
267
|
+
console.log(chalk_1.default.gray(" npm registry에 접속할 수 없습니다.\n"));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
spinner.stop();
|
|
271
|
+
console.log(chalk_1.default.white(` 최신 버전: ${chalk_1.default.blue.bold(latestVersion)}`));
|
|
272
|
+
// 버전 비교 및 업데이트 권유
|
|
273
|
+
if (isVersionLower(VERSION, latestVersion)) {
|
|
274
|
+
console.log();
|
|
275
|
+
console.log(chalk_1.default.yellow.bold(" ⚠️ 새로운 버전이 있습니다!"));
|
|
276
|
+
console.log();
|
|
277
|
+
console.log(chalk_1.default.white(" 업데이트 방법:"));
|
|
278
|
+
console.log(chalk_1.default.cyan(` npm update -g ${PACKAGE_NAME}`));
|
|
279
|
+
console.log();
|
|
280
|
+
console.log(chalk_1.default.gray(" 또는 프로젝트 내 SEMO 업데이트:"));
|
|
281
|
+
console.log(chalk_1.default.gray(" semo update"));
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.log();
|
|
285
|
+
console.log(chalk_1.default.green(" ✓ 최신 버전을 사용 중입니다."));
|
|
286
|
+
}
|
|
287
|
+
console.log();
|
|
288
|
+
}
|
|
161
289
|
// === 유틸리티 함수들 ===
|
|
162
290
|
async function confirmOverwrite(itemName, itemPath) {
|
|
163
291
|
if (!fs.existsSync(itemPath)) {
|
|
@@ -1165,44 +1293,90 @@ ${packageClaudeMdSections}
|
|
|
1165
1293
|
}
|
|
1166
1294
|
// === add 명령어 ===
|
|
1167
1295
|
program
|
|
1168
|
-
.command("add <
|
|
1169
|
-
.description("Extension 패키지를 추가로 설치합니다")
|
|
1296
|
+
.command("add <packages>")
|
|
1297
|
+
.description("Extension 패키지를 추가로 설치합니다 (그룹: biz, eng, ops / 개별: biz/discovery, eng/nextjs)")
|
|
1170
1298
|
.option("-f, --force", "기존 설정 덮어쓰기")
|
|
1171
|
-
.action(async (
|
|
1299
|
+
.action(async (packagesInput, options) => {
|
|
1172
1300
|
const cwd = process.cwd();
|
|
1173
1301
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
1174
1302
|
if (!fs.existsSync(semoSystemDir)) {
|
|
1175
1303
|
console.log(chalk_1.default.red("\nSEMO가 설치되어 있지 않습니다. 'semo init'을 먼저 실행하세요.\n"));
|
|
1176
1304
|
process.exit(1);
|
|
1177
1305
|
}
|
|
1178
|
-
//
|
|
1179
|
-
|
|
1180
|
-
if (
|
|
1181
|
-
|
|
1182
|
-
console.log(chalk_1.default.
|
|
1183
|
-
}
|
|
1184
|
-
if (!(resolvedPackage in EXTENSION_PACKAGES)) {
|
|
1185
|
-
console.log(chalk_1.default.red(`\n알 수 없는 패키지: ${packageName}`));
|
|
1306
|
+
// 패키지 입력 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
1307
|
+
const { packages, isGroup, groupName } = resolvePackageInput(packagesInput);
|
|
1308
|
+
if (packages.length === 0) {
|
|
1309
|
+
console.log(chalk_1.default.red(`\n알 수 없는 패키지: ${packagesInput}`));
|
|
1310
|
+
console.log(chalk_1.default.gray(`사용 가능한 그룹: ${PACKAGE_GROUPS.join(", ")}`));
|
|
1186
1311
|
console.log(chalk_1.default.gray(`사용 가능한 패키지: ${Object.keys(EXTENSION_PACKAGES).join(", ")}`));
|
|
1187
1312
|
console.log(chalk_1.default.gray(`레거시 별칭: ${Object.keys(LEGACY_MAPPING).join(", ")}\n`));
|
|
1188
1313
|
process.exit(1);
|
|
1189
1314
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
console.log(chalk_1.default.
|
|
1194
|
-
|
|
1315
|
+
// 그룹 설치인 경우 안내
|
|
1316
|
+
if (isGroup) {
|
|
1317
|
+
console.log(chalk_1.default.cyan.bold(`\n📦 ${groupName?.toUpperCase()} 그룹 패키지 일괄 설치\n`));
|
|
1318
|
+
console.log(chalk_1.default.gray(" 포함된 패키지:"));
|
|
1319
|
+
for (const pkg of packages) {
|
|
1320
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${EXTENSION_PACKAGES[pkg].name})`));
|
|
1321
|
+
}
|
|
1322
|
+
console.log();
|
|
1323
|
+
}
|
|
1324
|
+
else if (packages.length === 1) {
|
|
1325
|
+
// 단일 패키지
|
|
1326
|
+
const pkg = packages[0];
|
|
1327
|
+
console.log(chalk_1.default.cyan(`\n📦 ${EXTENSION_PACKAGES[pkg].name} 패키지 설치\n`));
|
|
1328
|
+
console.log(chalk_1.default.gray(` ${EXTENSION_PACKAGES[pkg].desc}\n`));
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
// 여러 패키지 (쉼표 구분)
|
|
1332
|
+
console.log(chalk_1.default.cyan.bold(`\n📦 ${packages.length}개 패키지 설치\n`));
|
|
1333
|
+
for (const pkg of packages) {
|
|
1334
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${EXTENSION_PACKAGES[pkg].name})`));
|
|
1335
|
+
}
|
|
1336
|
+
console.log();
|
|
1337
|
+
}
|
|
1338
|
+
// 이미 설치된 패키지 확인
|
|
1339
|
+
const alreadyInstalled = [];
|
|
1340
|
+
const toInstall = [];
|
|
1341
|
+
for (const pkg of packages) {
|
|
1342
|
+
const pkgPath = path.join(semoSystemDir, pkg);
|
|
1343
|
+
if (fs.existsSync(pkgPath) && !options.force) {
|
|
1344
|
+
alreadyInstalled.push(pkg);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
toInstall.push(pkg);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
if (alreadyInstalled.length > 0) {
|
|
1351
|
+
console.log(chalk_1.default.yellow("⚠ 이미 설치된 패키지 (건너뜀):"));
|
|
1352
|
+
for (const pkg of alreadyInstalled) {
|
|
1353
|
+
console.log(chalk_1.default.yellow(` - ${pkg}`));
|
|
1354
|
+
}
|
|
1355
|
+
console.log(chalk_1.default.gray(" 강제 재설치: semo add " + packagesInput + " --force\n"));
|
|
1356
|
+
}
|
|
1357
|
+
if (toInstall.length === 0) {
|
|
1358
|
+
console.log(chalk_1.default.yellow("\n모든 패키지가 이미 설치되어 있습니다.\n"));
|
|
1195
1359
|
return;
|
|
1196
1360
|
}
|
|
1197
|
-
console.log(chalk_1.default.cyan(`\n📦 ${EXTENSION_PACKAGES[packageName].name} 패키지 설치\n`));
|
|
1198
|
-
console.log(chalk_1.default.gray(` ${EXTENSION_PACKAGES[packageName].desc}\n`));
|
|
1199
1361
|
// 1. 다운로드
|
|
1200
|
-
await downloadExtensions(cwd,
|
|
1362
|
+
await downloadExtensions(cwd, toInstall, options.force);
|
|
1201
1363
|
// 2. settings.json 병합
|
|
1202
|
-
await mergeExtensionSettings(cwd,
|
|
1203
|
-
// 3. 심볼릭 링크 설정
|
|
1204
|
-
|
|
1205
|
-
|
|
1364
|
+
await mergeExtensionSettings(cwd, toInstall);
|
|
1365
|
+
// 3. 심볼릭 링크 설정 (모든 설치된 패키지 포함하여 orchestrator 병합)
|
|
1366
|
+
const allInstalledPackages = [...new Set([...alreadyInstalled, ...toInstall])];
|
|
1367
|
+
await setupExtensionSymlinks(cwd, allInstalledPackages);
|
|
1368
|
+
// 4. CLAUDE.md 재생성 (모든 설치된 패키지 반영)
|
|
1369
|
+
await setupClaudeMd(cwd, allInstalledPackages, options.force);
|
|
1370
|
+
if (toInstall.length === 1) {
|
|
1371
|
+
console.log(chalk_1.default.green.bold(`\n✅ ${EXTENSION_PACKAGES[toInstall[0]].name} 패키지 설치 완료!\n`));
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
console.log(chalk_1.default.green.bold(`\n✅ ${toInstall.length}개 패키지 설치 완료!`));
|
|
1375
|
+
for (const pkg of toInstall) {
|
|
1376
|
+
console.log(chalk_1.default.green(` ✓ ${EXTENSION_PACKAGES[pkg].name}`));
|
|
1377
|
+
}
|
|
1378
|
+
console.log();
|
|
1379
|
+
}
|
|
1206
1380
|
});
|
|
1207
1381
|
// === list 명령어 ===
|
|
1208
1382
|
program
|
|
@@ -1240,6 +1414,13 @@ program
|
|
|
1240
1414
|
}
|
|
1241
1415
|
console.log();
|
|
1242
1416
|
}
|
|
1417
|
+
// 그룹 설치 안내
|
|
1418
|
+
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
1419
|
+
console.log(chalk_1.default.white.bold("📦 그룹 일괄 설치"));
|
|
1420
|
+
console.log(chalk_1.default.gray(" semo add biz → Business 전체 (discovery, design, management, poc)"));
|
|
1421
|
+
console.log(chalk_1.default.gray(" semo add eng → Engineering 전체 (nextjs, spring, ms, infra)"));
|
|
1422
|
+
console.log(chalk_1.default.gray(" semo add ops → Operations 전체 (qa, monitor, improve)"));
|
|
1423
|
+
console.log();
|
|
1243
1424
|
// 레거시 호환성 안내
|
|
1244
1425
|
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
1245
1426
|
console.log(chalk_1.default.gray("레거시 명령어도 지원됩니다:"));
|
|
@@ -1470,4 +1651,14 @@ program
|
|
|
1470
1651
|
}
|
|
1471
1652
|
console.log(chalk_1.default.green.bold("\n✅ SEMO 업데이트 완료!\n"));
|
|
1472
1653
|
});
|
|
1473
|
-
program.parse
|
|
1654
|
+
// === -v 옵션 처리 (program.parse 전에 직접 처리) ===
|
|
1655
|
+
async function main() {
|
|
1656
|
+
const args = process.argv.slice(2);
|
|
1657
|
+
// semo -v 또는 semo --version-info 처리
|
|
1658
|
+
if (args.length === 1 && (args[0] === "-v" || args[0] === "--version-info")) {
|
|
1659
|
+
await showVersionInfo();
|
|
1660
|
+
process.exit(0);
|
|
1661
|
+
}
|
|
1662
|
+
program.parse();
|
|
1663
|
+
}
|
|
1664
|
+
main();
|