@team-semicolon/semo-cli 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +120 -23
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -153,6 +153,50 @@ const LEGACY_MAPPING = {
|
|
|
153
153
|
design: "biz/design",
|
|
154
154
|
mvp: "biz/poc",
|
|
155
155
|
};
|
|
156
|
+
// 그룹 이름 목록 (biz, eng, ops)
|
|
157
|
+
const PACKAGE_GROUPS = ["biz", "eng", "ops", "meta"];
|
|
158
|
+
// 그룹명 → 해당 그룹의 모든 패키지 반환
|
|
159
|
+
function getPackagesByGroup(group) {
|
|
160
|
+
return Object.entries(EXTENSION_PACKAGES)
|
|
161
|
+
.filter(([, pkg]) => pkg.layer === group)
|
|
162
|
+
.map(([key]) => key);
|
|
163
|
+
}
|
|
164
|
+
// 패키지 입력을 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
165
|
+
function resolvePackageInput(input) {
|
|
166
|
+
// 쉼표로 구분된 여러 패키지 처리
|
|
167
|
+
const parts = input.split(",").map(p => p.trim()).filter(p => p);
|
|
168
|
+
const resolvedPackages = [];
|
|
169
|
+
let isGroup = false;
|
|
170
|
+
let groupName;
|
|
171
|
+
for (const part of parts) {
|
|
172
|
+
// 1. 그룹명인지 확인 (biz, eng, ops, meta)
|
|
173
|
+
if (PACKAGE_GROUPS.includes(part)) {
|
|
174
|
+
const groupPackages = getPackagesByGroup(part);
|
|
175
|
+
resolvedPackages.push(...groupPackages);
|
|
176
|
+
isGroup = true;
|
|
177
|
+
groupName = part;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
// 2. 레거시 매핑 확인
|
|
181
|
+
if (part in LEGACY_MAPPING) {
|
|
182
|
+
resolvedPackages.push(LEGACY_MAPPING[part]);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
// 3. 직접 패키지명 확인
|
|
186
|
+
if (part in EXTENSION_PACKAGES) {
|
|
187
|
+
resolvedPackages.push(part);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
// 4. 유효하지 않은 패키지명
|
|
191
|
+
// (빈 배열 대신 null을 추가하여 나중에 에러 처리)
|
|
192
|
+
}
|
|
193
|
+
// 중복 제거
|
|
194
|
+
return {
|
|
195
|
+
packages: [...new Set(resolvedPackages)],
|
|
196
|
+
isGroup,
|
|
197
|
+
groupName
|
|
198
|
+
};
|
|
199
|
+
}
|
|
156
200
|
const program = new commander_1.Command();
|
|
157
201
|
program
|
|
158
202
|
.name("semo")
|
|
@@ -1165,44 +1209,90 @@ ${packageClaudeMdSections}
|
|
|
1165
1209
|
}
|
|
1166
1210
|
// === add 명령어 ===
|
|
1167
1211
|
program
|
|
1168
|
-
.command("add <
|
|
1169
|
-
.description("Extension 패키지를 추가로 설치합니다")
|
|
1212
|
+
.command("add <packages>")
|
|
1213
|
+
.description("Extension 패키지를 추가로 설치합니다 (그룹: biz, eng, ops / 개별: biz/discovery, eng/nextjs)")
|
|
1170
1214
|
.option("-f, --force", "기존 설정 덮어쓰기")
|
|
1171
|
-
.action(async (
|
|
1215
|
+
.action(async (packagesInput, options) => {
|
|
1172
1216
|
const cwd = process.cwd();
|
|
1173
1217
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
1174
1218
|
if (!fs.existsSync(semoSystemDir)) {
|
|
1175
1219
|
console.log(chalk_1.default.red("\nSEMO가 설치되어 있지 않습니다. 'semo init'을 먼저 실행하세요.\n"));
|
|
1176
1220
|
process.exit(1);
|
|
1177
1221
|
}
|
|
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}`));
|
|
1222
|
+
// 패키지 입력 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
1223
|
+
const { packages, isGroup, groupName } = resolvePackageInput(packagesInput);
|
|
1224
|
+
if (packages.length === 0) {
|
|
1225
|
+
console.log(chalk_1.default.red(`\n알 수 없는 패키지: ${packagesInput}`));
|
|
1226
|
+
console.log(chalk_1.default.gray(`사용 가능한 그룹: ${PACKAGE_GROUPS.join(", ")}`));
|
|
1186
1227
|
console.log(chalk_1.default.gray(`사용 가능한 패키지: ${Object.keys(EXTENSION_PACKAGES).join(", ")}`));
|
|
1187
1228
|
console.log(chalk_1.default.gray(`레거시 별칭: ${Object.keys(LEGACY_MAPPING).join(", ")}\n`));
|
|
1188
1229
|
process.exit(1);
|
|
1189
1230
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
console.log(chalk_1.default.
|
|
1194
|
-
|
|
1231
|
+
// 그룹 설치인 경우 안내
|
|
1232
|
+
if (isGroup) {
|
|
1233
|
+
console.log(chalk_1.default.cyan.bold(`\n📦 ${groupName?.toUpperCase()} 그룹 패키지 일괄 설치\n`));
|
|
1234
|
+
console.log(chalk_1.default.gray(" 포함된 패키지:"));
|
|
1235
|
+
for (const pkg of packages) {
|
|
1236
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${EXTENSION_PACKAGES[pkg].name})`));
|
|
1237
|
+
}
|
|
1238
|
+
console.log();
|
|
1239
|
+
}
|
|
1240
|
+
else if (packages.length === 1) {
|
|
1241
|
+
// 단일 패키지
|
|
1242
|
+
const pkg = packages[0];
|
|
1243
|
+
console.log(chalk_1.default.cyan(`\n📦 ${EXTENSION_PACKAGES[pkg].name} 패키지 설치\n`));
|
|
1244
|
+
console.log(chalk_1.default.gray(` ${EXTENSION_PACKAGES[pkg].desc}\n`));
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
// 여러 패키지 (쉼표 구분)
|
|
1248
|
+
console.log(chalk_1.default.cyan.bold(`\n📦 ${packages.length}개 패키지 설치\n`));
|
|
1249
|
+
for (const pkg of packages) {
|
|
1250
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${EXTENSION_PACKAGES[pkg].name})`));
|
|
1251
|
+
}
|
|
1252
|
+
console.log();
|
|
1253
|
+
}
|
|
1254
|
+
// 이미 설치된 패키지 확인
|
|
1255
|
+
const alreadyInstalled = [];
|
|
1256
|
+
const toInstall = [];
|
|
1257
|
+
for (const pkg of packages) {
|
|
1258
|
+
const pkgPath = path.join(semoSystemDir, pkg);
|
|
1259
|
+
if (fs.existsSync(pkgPath) && !options.force) {
|
|
1260
|
+
alreadyInstalled.push(pkg);
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
toInstall.push(pkg);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (alreadyInstalled.length > 0) {
|
|
1267
|
+
console.log(chalk_1.default.yellow("⚠ 이미 설치된 패키지 (건너뜀):"));
|
|
1268
|
+
for (const pkg of alreadyInstalled) {
|
|
1269
|
+
console.log(chalk_1.default.yellow(` - ${pkg}`));
|
|
1270
|
+
}
|
|
1271
|
+
console.log(chalk_1.default.gray(" 강제 재설치: semo add " + packagesInput + " --force\n"));
|
|
1272
|
+
}
|
|
1273
|
+
if (toInstall.length === 0) {
|
|
1274
|
+
console.log(chalk_1.default.yellow("\n모든 패키지가 이미 설치되어 있습니다.\n"));
|
|
1195
1275
|
return;
|
|
1196
1276
|
}
|
|
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
1277
|
// 1. 다운로드
|
|
1200
|
-
await downloadExtensions(cwd,
|
|
1278
|
+
await downloadExtensions(cwd, toInstall, options.force);
|
|
1201
1279
|
// 2. settings.json 병합
|
|
1202
|
-
await mergeExtensionSettings(cwd,
|
|
1203
|
-
// 3. 심볼릭 링크 설정
|
|
1204
|
-
|
|
1205
|
-
|
|
1280
|
+
await mergeExtensionSettings(cwd, toInstall);
|
|
1281
|
+
// 3. 심볼릭 링크 설정 (모든 설치된 패키지 포함하여 orchestrator 병합)
|
|
1282
|
+
const allInstalledPackages = [...new Set([...alreadyInstalled, ...toInstall])];
|
|
1283
|
+
await setupExtensionSymlinks(cwd, allInstalledPackages);
|
|
1284
|
+
// 4. CLAUDE.md 재생성 (모든 설치된 패키지 반영)
|
|
1285
|
+
await setupClaudeMd(cwd, allInstalledPackages, options.force);
|
|
1286
|
+
if (toInstall.length === 1) {
|
|
1287
|
+
console.log(chalk_1.default.green.bold(`\n✅ ${EXTENSION_PACKAGES[toInstall[0]].name} 패키지 설치 완료!\n`));
|
|
1288
|
+
}
|
|
1289
|
+
else {
|
|
1290
|
+
console.log(chalk_1.default.green.bold(`\n✅ ${toInstall.length}개 패키지 설치 완료!`));
|
|
1291
|
+
for (const pkg of toInstall) {
|
|
1292
|
+
console.log(chalk_1.default.green(` ✓ ${EXTENSION_PACKAGES[pkg].name}`));
|
|
1293
|
+
}
|
|
1294
|
+
console.log();
|
|
1295
|
+
}
|
|
1206
1296
|
});
|
|
1207
1297
|
// === list 명령어 ===
|
|
1208
1298
|
program
|
|
@@ -1240,6 +1330,13 @@ program
|
|
|
1240
1330
|
}
|
|
1241
1331
|
console.log();
|
|
1242
1332
|
}
|
|
1333
|
+
// 그룹 설치 안내
|
|
1334
|
+
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
1335
|
+
console.log(chalk_1.default.white.bold("📦 그룹 일괄 설치"));
|
|
1336
|
+
console.log(chalk_1.default.gray(" semo add biz → Business 전체 (discovery, design, management, poc)"));
|
|
1337
|
+
console.log(chalk_1.default.gray(" semo add eng → Engineering 전체 (nextjs, spring, ms, infra)"));
|
|
1338
|
+
console.log(chalk_1.default.gray(" semo add ops → Operations 전체 (qa, monitor, improve)"));
|
|
1339
|
+
console.log();
|
|
1243
1340
|
// 레거시 호환성 안내
|
|
1244
1341
|
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
1245
1342
|
console.log(chalk_1.default.gray("레거시 명령어도 지원됩니다:"));
|