@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.
Files changed (2) hide show
  1. package/dist/index.js +217 -26
  2. 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.0-alpha";
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 <package>")
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 (packageName, options) => {
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
- let resolvedPackage = packageName;
1180
- if (packageName in LEGACY_MAPPING) {
1181
- resolvedPackage = LEGACY_MAPPING[packageName];
1182
- console.log(chalk_1.default.yellow(`\n💡 '${packageName}' '${resolvedPackage}' (v3.0 구조)`));
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
- packageName = resolvedPackage;
1191
- const pkgPath = path.join(semoSystemDir, packageName);
1192
- if (fs.existsSync(pkgPath) && !options.force) {
1193
- console.log(chalk_1.default.yellow(`\n${EXTENSION_PACKAGES[packageName].name} 패키지가 이미 설치되어 있습니다.`));
1194
- console.log(chalk_1.default.gray("강제 재설치: semo add " + packageName + " --force\n"));
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, [packageName], options.force);
1362
+ await downloadExtensions(cwd, toInstall, options.force);
1201
1363
  // 2. settings.json 병합
1202
- await mergeExtensionSettings(cwd, [packageName]);
1203
- // 3. 심볼릭 링크 설정
1204
- await setupExtensionSymlinks(cwd, [packageName]);
1205
- console.log(chalk_1.default.green.bold(`\n✅ ${EXTENSION_PACKAGES[packageName].name} 패키지 설치 완료!\n`));
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semo-cli",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "SEMO CLI - AI Agent Orchestration Framework Installer",
5
5
  "main": "dist/index.js",
6
6
  "bin": {