@team-semicolon/semo-cli 3.5.0 → 3.6.0
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 +41 -270
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -235,48 +235,9 @@ async function showVersionComparison(cwd) {
|
|
|
235
235
|
level: 0,
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
|
-
//
|
|
239
|
-
// 그룹별로 묶어서 계층 구조로 출력
|
|
238
|
+
// Extension 패키지들 (meta, semo-hooks, semo-remote 등) - semo-system 내부
|
|
240
239
|
if (hasSemoSystem) {
|
|
241
|
-
for (const
|
|
242
|
-
const groupVersionPath = path.join(semoSystemDir, group, "VERSION");
|
|
243
|
-
const hasGroupVersion = fs.existsSync(groupVersionPath);
|
|
244
|
-
// 해당 그룹의 하위 패키지 찾기
|
|
245
|
-
const groupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => key.startsWith(`${group}/`));
|
|
246
|
-
const installedGroupExtensions = groupExtensions.filter(key => fs.existsSync(path.join(semoSystemDir, key, "VERSION")));
|
|
247
|
-
// 그룹 버전이 있거나 하위 패키지가 설치된 경우에만 표시
|
|
248
|
-
if (hasGroupVersion || installedGroupExtensions.length > 0) {
|
|
249
|
-
// 그룹 패키지 버전 추가
|
|
250
|
-
if (hasGroupVersion) {
|
|
251
|
-
const localGroup = fs.readFileSync(groupVersionPath, "utf-8").trim();
|
|
252
|
-
const remoteGroup = await getRemotePackageVersion(group);
|
|
253
|
-
versionInfos.push({
|
|
254
|
-
name: group,
|
|
255
|
-
local: localGroup,
|
|
256
|
-
remote: remoteGroup,
|
|
257
|
-
needsUpdate: remoteGroup ? isVersionLower(localGroup, remoteGroup) : false,
|
|
258
|
-
level: 1,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
// 하위 Extension 패키지들 추가
|
|
262
|
-
for (const key of installedGroupExtensions) {
|
|
263
|
-
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
264
|
-
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
265
|
-
const remoteExt = await getRemotePackageVersion(key);
|
|
266
|
-
versionInfos.push({
|
|
267
|
-
name: key,
|
|
268
|
-
local: localExt,
|
|
269
|
-
remote: remoteExt,
|
|
270
|
-
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
271
|
-
level: 2,
|
|
272
|
-
group: group,
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
// 그룹에 속하지 않는 Extension (meta 등)
|
|
278
|
-
const nonGroupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => !PACKAGE_GROUPS.some(g => key.startsWith(`${g}/`)));
|
|
279
|
-
for (const key of nonGroupExtensions) {
|
|
240
|
+
for (const key of Object.keys(EXTENSION_PACKAGES)) {
|
|
280
241
|
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
281
242
|
if (fs.existsSync(extVersionPath)) {
|
|
282
243
|
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
@@ -650,92 +611,39 @@ function copyRecursive(src, dest) {
|
|
|
650
611
|
}
|
|
651
612
|
}
|
|
652
613
|
const SEMO_REPO = "https://github.com/semicolon-devteam/semo.git";
|
|
653
|
-
//
|
|
614
|
+
// Extension 패키지 정의 (통합 구조)
|
|
654
615
|
const EXTENSION_PACKAGES = {
|
|
655
|
-
|
|
656
|
-
"
|
|
657
|
-
"
|
|
658
|
-
"biz/management": { name: "Management", desc: "일정/인력/스프린트 관리", layer: "biz", detect: [] },
|
|
659
|
-
"biz/poc": { name: "PoC", desc: "빠른 PoC, 패스트트랙", layer: "biz", detect: [] },
|
|
660
|
-
// Engineering Layer
|
|
661
|
-
"eng/nextjs": { name: "Next.js", desc: "Next.js 프론트엔드 개발", layer: "eng", detect: ["next.config.js", "next.config.mjs", "next.config.ts"] },
|
|
662
|
-
"eng/spring": { name: "Spring", desc: "Spring Boot 백엔드 개발", layer: "eng", detect: ["pom.xml", "build.gradle"] },
|
|
663
|
-
"eng/ms": { name: "Microservice", desc: "마이크로서비스 아키텍처", layer: "eng", detect: [] },
|
|
664
|
-
"eng/infra": { name: "Infra", desc: "인프라/배포 관리", layer: "eng", detect: ["docker-compose.yml", "Dockerfile"] },
|
|
665
|
-
// Operations Layer
|
|
666
|
-
"ops/qa": { name: "QA", desc: "테스트/품질 관리", layer: "ops", detect: [] },
|
|
667
|
-
"ops/monitor": { name: "Monitor", desc: "서비스 현황 모니터링", layer: "ops", detect: [] },
|
|
668
|
-
"ops/improve": { name: "Improve", desc: "개선 제안", layer: "ops", detect: [] },
|
|
669
|
-
// Meta
|
|
670
|
-
meta: { name: "Meta", desc: "SEMO 프레임워크 자체 개발/관리", layer: "meta", detect: ["semo-core", "semo-skills"] },
|
|
671
|
-
// System (semo-system 하위 패키지)
|
|
672
|
-
"semo-hooks": { name: "Hooks", desc: "Claude Code Hooks 기반 로깅 시스템", layer: "system", detect: [] },
|
|
673
|
-
"semo-remote": { name: "Remote", desc: "Claude Code 원격 제어 (모바일 PWA)", layer: "system", detect: [] },
|
|
616
|
+
meta: { name: "Meta", desc: "SEMO 프레임워크 자체 개발/관리" },
|
|
617
|
+
"semo-hooks": { name: "Hooks", desc: "Claude Code Hooks 기반 로깅 시스템" },
|
|
618
|
+
"semo-remote": { name: "Remote", desc: "Claude Code 원격 제어 (모바일 PWA)" },
|
|
674
619
|
};
|
|
675
620
|
// 단축명 → 전체 패키지 경로 매핑
|
|
676
621
|
const SHORTNAME_MAPPING = {
|
|
677
|
-
// 하위 패키지명 단축 (discovery → biz/discovery)
|
|
678
|
-
discovery: "biz/discovery",
|
|
679
|
-
design: "biz/design",
|
|
680
|
-
management: "biz/management",
|
|
681
|
-
poc: "biz/poc",
|
|
682
|
-
nextjs: "eng/nextjs",
|
|
683
|
-
spring: "eng/spring",
|
|
684
|
-
ms: "eng/ms",
|
|
685
|
-
infra: "eng/infra",
|
|
686
|
-
qa: "ops/qa",
|
|
687
|
-
monitor: "ops/monitor",
|
|
688
|
-
improve: "ops/improve",
|
|
689
|
-
// 추가 별칭
|
|
690
|
-
next: "eng/nextjs",
|
|
691
|
-
backend: "eng/spring",
|
|
692
|
-
mvp: "biz/poc",
|
|
693
|
-
// System 패키지 단축명
|
|
694
622
|
hooks: "semo-hooks",
|
|
695
623
|
remote: "semo-remote",
|
|
696
624
|
};
|
|
697
|
-
//
|
|
698
|
-
const PACKAGE_GROUPS = ["biz", "eng", "ops", "meta", "system"];
|
|
699
|
-
// 그룹명 → 해당 그룹의 모든 패키지 반환
|
|
700
|
-
function getPackagesByGroup(group) {
|
|
701
|
-
return Object.entries(EXTENSION_PACKAGES)
|
|
702
|
-
.filter(([, pkg]) => pkg.layer === group)
|
|
703
|
-
.map(([key]) => key);
|
|
704
|
-
}
|
|
705
|
-
// 패키지 입력을 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
625
|
+
// 패키지 입력을 해석
|
|
706
626
|
function resolvePackageInput(input) {
|
|
707
627
|
// 쉼표로 구분된 여러 패키지 처리
|
|
708
628
|
const parts = input.split(",").map(p => p.trim()).filter(p => p);
|
|
709
629
|
const resolvedPackages = [];
|
|
710
|
-
let isGroup = false;
|
|
711
|
-
let groupName;
|
|
712
630
|
for (const part of parts) {
|
|
713
|
-
// 1.
|
|
714
|
-
if (PACKAGE_GROUPS.includes(part)) {
|
|
715
|
-
const groupPackages = getPackagesByGroup(part);
|
|
716
|
-
resolvedPackages.push(...groupPackages);
|
|
717
|
-
isGroup = true;
|
|
718
|
-
groupName = part;
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
// 2. 단축명 매핑 확인 (discovery → biz/discovery 등)
|
|
631
|
+
// 1. 단축명 매핑 확인 (hooks → semo-hooks 등)
|
|
722
632
|
if (part in SHORTNAME_MAPPING) {
|
|
723
633
|
resolvedPackages.push(SHORTNAME_MAPPING[part]);
|
|
724
634
|
continue;
|
|
725
635
|
}
|
|
726
|
-
//
|
|
636
|
+
// 2. 직접 패키지명 확인
|
|
727
637
|
if (part in EXTENSION_PACKAGES) {
|
|
728
638
|
resolvedPackages.push(part);
|
|
729
639
|
continue;
|
|
730
640
|
}
|
|
731
|
-
//
|
|
641
|
+
// 3. 유효하지 않은 패키지명
|
|
732
642
|
// (빈 배열 대신 null을 추가하여 나중에 에러 처리)
|
|
733
643
|
}
|
|
734
644
|
// 중복 제거
|
|
735
645
|
return {
|
|
736
646
|
packages: [...new Set(resolvedPackages)],
|
|
737
|
-
isGroup,
|
|
738
|
-
groupName
|
|
739
647
|
};
|
|
740
648
|
}
|
|
741
649
|
const program = new commander_1.Command();
|
|
@@ -796,47 +704,10 @@ async function showVersionInfo() {
|
|
|
796
704
|
level: 0,
|
|
797
705
|
});
|
|
798
706
|
}
|
|
799
|
-
// 4.
|
|
707
|
+
// 4. Extension 패키지들 (meta, semo-hooks, semo-remote 등) - semo-system 내부
|
|
800
708
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
801
709
|
if (fs.existsSync(semoSystemDir)) {
|
|
802
|
-
for (const
|
|
803
|
-
const groupVersionPath = path.join(semoSystemDir, group, "VERSION");
|
|
804
|
-
const hasGroupVersion = fs.existsSync(groupVersionPath);
|
|
805
|
-
// 해당 그룹의 하위 패키지 찾기
|
|
806
|
-
const groupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => key.startsWith(`${group}/`));
|
|
807
|
-
const installedGroupExtensions = groupExtensions.filter(key => fs.existsSync(path.join(semoSystemDir, key, "VERSION")));
|
|
808
|
-
if (hasGroupVersion || installedGroupExtensions.length > 0) {
|
|
809
|
-
// 그룹 패키지 버전 추가
|
|
810
|
-
if (hasGroupVersion) {
|
|
811
|
-
const localGroup = fs.readFileSync(groupVersionPath, "utf-8").trim();
|
|
812
|
-
const remoteGroup = await getRemotePackageVersion(group);
|
|
813
|
-
versionInfos.push({
|
|
814
|
-
name: group,
|
|
815
|
-
local: localGroup,
|
|
816
|
-
remote: remoteGroup,
|
|
817
|
-
needsUpdate: remoteGroup ? isVersionLower(localGroup, remoteGroup) : false,
|
|
818
|
-
level: 1,
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
// 하위 Extension 패키지들 추가
|
|
822
|
-
for (const key of installedGroupExtensions) {
|
|
823
|
-
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
824
|
-
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
825
|
-
const remoteExt = await getRemotePackageVersion(key);
|
|
826
|
-
versionInfos.push({
|
|
827
|
-
name: key,
|
|
828
|
-
local: localExt,
|
|
829
|
-
remote: remoteExt,
|
|
830
|
-
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
831
|
-
level: 2,
|
|
832
|
-
group: group,
|
|
833
|
-
});
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
// 그룹에 속하지 않는 Extension (meta 등)
|
|
838
|
-
const nonGroupExtensions = Object.keys(EXTENSION_PACKAGES).filter(key => !PACKAGE_GROUPS.some(g => key.startsWith(`${g}/`)));
|
|
839
|
-
for (const key of nonGroupExtensions) {
|
|
710
|
+
for (const key of Object.keys(EXTENSION_PACKAGES)) {
|
|
840
711
|
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
841
712
|
if (fs.existsSync(extVersionPath)) {
|
|
842
713
|
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
@@ -995,18 +866,6 @@ async function confirmOverwrite(itemName, itemPath) {
|
|
|
995
866
|
]);
|
|
996
867
|
return shouldOverwrite;
|
|
997
868
|
}
|
|
998
|
-
function detectProjectType(cwd) {
|
|
999
|
-
const detected = [];
|
|
1000
|
-
for (const [key, pkg] of Object.entries(EXTENSION_PACKAGES)) {
|
|
1001
|
-
for (const file of pkg.detect) {
|
|
1002
|
-
if (fs.existsSync(path.join(cwd, file))) {
|
|
1003
|
-
detected.push(key);
|
|
1004
|
-
break;
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return detected;
|
|
1009
|
-
}
|
|
1010
869
|
// === 설치된 Extension 패키지 스캔 ===
|
|
1011
870
|
function getInstalledExtensions(cwd) {
|
|
1012
871
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
@@ -1136,56 +995,17 @@ program
|
|
|
1136
995
|
spinner.fail("Git 레포지토리가 아닙니다. 'git init'을 먼저 실행하세요.");
|
|
1137
996
|
process.exit(1);
|
|
1138
997
|
}
|
|
1139
|
-
// 2.
|
|
1140
|
-
const detected = detectProjectType(cwd);
|
|
998
|
+
// 2. Extension 패키지 처리 (--with 옵션으로만 지정 가능)
|
|
1141
999
|
let extensionsToInstall = [];
|
|
1142
1000
|
if (options.with) {
|
|
1143
1001
|
extensionsToInstall = options.with.split(",").map((p) => p.trim()).filter((p) => p in EXTENSION_PACKAGES);
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
});
|
|
1150
|
-
const { installDetected } = await inquirer_1.default.prompt([
|
|
1151
|
-
{
|
|
1152
|
-
type: "confirm",
|
|
1153
|
-
name: "installDetected",
|
|
1154
|
-
message: "감지된 패키지를 함께 설치할까요?",
|
|
1155
|
-
default: true,
|
|
1156
|
-
},
|
|
1157
|
-
]);
|
|
1158
|
-
if (installDetected) {
|
|
1159
|
-
extensionsToInstall = detected;
|
|
1002
|
+
if (extensionsToInstall.length > 0) {
|
|
1003
|
+
console.log(chalk_1.default.cyan("\n📦 추가 Extension 설치:"));
|
|
1004
|
+
extensionsToInstall.forEach(pkg => {
|
|
1005
|
+
console.log(chalk_1.default.gray(` - ${EXTENSION_PACKAGES[pkg].name}: ${EXTENSION_PACKAGES[pkg].desc}`));
|
|
1006
|
+
});
|
|
1160
1007
|
}
|
|
1161
1008
|
}
|
|
1162
|
-
else {
|
|
1163
|
-
// 프로젝트 유형이 감지되지 않은 경우 패키지 선택 프롬프트
|
|
1164
|
-
console.log(chalk_1.default.cyan("\n📦 추가 패키지 선택"));
|
|
1165
|
-
console.log(chalk_1.default.gray(" 기본 설치 (semo-core + semo-skills) 외에 추가할 패키지를 선택하세요.\n"));
|
|
1166
|
-
// 그룹별로 패키지 구성
|
|
1167
|
-
const packageChoices = [
|
|
1168
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Engineering ──")),
|
|
1169
|
-
{ name: `eng/nextjs - ${EXTENSION_PACKAGES["eng/nextjs"].desc}`, value: "eng/nextjs" },
|
|
1170
|
-
{ name: `eng/spring - ${EXTENSION_PACKAGES["eng/spring"].desc}`, value: "eng/spring" },
|
|
1171
|
-
{ name: `eng/infra - ${EXTENSION_PACKAGES["eng/infra"].desc}`, value: "eng/infra" },
|
|
1172
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Business ──")),
|
|
1173
|
-
{ name: `biz/discovery - ${EXTENSION_PACKAGES["biz/discovery"].desc}`, value: "biz/discovery" },
|
|
1174
|
-
{ name: `biz/management - ${EXTENSION_PACKAGES["biz/management"].desc}`, value: "biz/management" },
|
|
1175
|
-
{ name: `biz/design - ${EXTENSION_PACKAGES["biz/design"].desc}`, value: "biz/design" },
|
|
1176
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Operations ──")),
|
|
1177
|
-
{ name: `ops/qa - ${EXTENSION_PACKAGES["ops/qa"].desc}`, value: "ops/qa" },
|
|
1178
|
-
];
|
|
1179
|
-
const { selectedPackages } = await inquirer_1.default.prompt([
|
|
1180
|
-
{
|
|
1181
|
-
type: "checkbox",
|
|
1182
|
-
name: "selectedPackages",
|
|
1183
|
-
message: "설치할 패키지 선택 (Space로 선택, Enter로 완료):",
|
|
1184
|
-
choices: packageChoices,
|
|
1185
|
-
},
|
|
1186
|
-
]);
|
|
1187
|
-
extensionsToInstall = selectedPackages;
|
|
1188
|
-
}
|
|
1189
1009
|
// 3. .claude 디렉토리 생성
|
|
1190
1010
|
const claudeDir = path.join(cwd, ".claude");
|
|
1191
1011
|
if (!fs.existsSync(claudeDir)) {
|
|
@@ -1242,8 +1062,8 @@ program
|
|
|
1242
1062
|
console.log(chalk_1.default.gray(" 1. Claude Code에서 프로젝트 열기"));
|
|
1243
1063
|
console.log(chalk_1.default.gray(" 2. 자연어로 요청하기 (예: \"댓글 기능 구현해줘\")"));
|
|
1244
1064
|
console.log(chalk_1.default.gray(" 3. /SEMO:help로 도움말 확인"));
|
|
1245
|
-
if (extensionsToInstall.length === 0
|
|
1246
|
-
console.log(chalk_1.default.gray("\n💡 추가 패키지: semo add <package> (예: semo add
|
|
1065
|
+
if (extensionsToInstall.length === 0) {
|
|
1066
|
+
console.log(chalk_1.default.gray("\n💡 추가 패키지: semo add <package> (예: semo add meta)"));
|
|
1247
1067
|
}
|
|
1248
1068
|
console.log();
|
|
1249
1069
|
});
|
|
@@ -2493,27 +2313,9 @@ async function setupClaudeMd(cwd, extensions, force) {
|
|
|
2493
2313
|
const extensionsList = extensions.length > 0
|
|
2494
2314
|
? extensions.map(pkg => `├── ${pkg}/ # ${EXTENSION_PACKAGES[pkg].name}`).join("\n")
|
|
2495
2315
|
: "";
|
|
2496
|
-
//
|
|
2316
|
+
// 패키지별 CLAUDE.md 병합 섹션 생성
|
|
2497
2317
|
let packageClaudeMdSections = "";
|
|
2498
|
-
//
|
|
2499
|
-
const installedGroups = [...new Set(extensions.map(pkg => pkg.split("/")[0]).filter(g => PACKAGE_GROUPS.includes(g)))];
|
|
2500
|
-
// 2. 그룹 레벨 CLAUDE.md 먼저 병합 (biz, eng, ops) - 중복 제거 적용
|
|
2501
|
-
for (const group of installedGroups) {
|
|
2502
|
-
const groupClaudeMdPath = path.join(semoSystemDir, group, "CLAUDE.md");
|
|
2503
|
-
if (fs.existsSync(groupClaudeMdPath)) {
|
|
2504
|
-
const groupContent = fs.readFileSync(groupClaudeMdPath, "utf-8");
|
|
2505
|
-
// 중복 제거 후 고유 콘텐츠만 추출
|
|
2506
|
-
const uniqueContent = extractUniqueContent(groupContent, group);
|
|
2507
|
-
// 헤더 레벨 조정 (# → ##, ## → ###)
|
|
2508
|
-
const adjustedContent = uniqueContent
|
|
2509
|
-
.replace(/^# /gm, "## ")
|
|
2510
|
-
.replace(/^## /gm, "### ")
|
|
2511
|
-
.replace(/^### /gm, "#### ");
|
|
2512
|
-
packageClaudeMdSections += `\n\n---\n\n${adjustedContent}`;
|
|
2513
|
-
console.log(chalk_1.default.green(` + ${group}/ 그룹 CLAUDE.md 병합됨 (고유 섹션만)`));
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
// 3. 개별 패키지 CLAUDE.md 병합 - 중복 제거 적용
|
|
2318
|
+
// 개별 패키지 CLAUDE.md 병합 - 중복 제거 적용
|
|
2517
2319
|
for (const pkg of extensions) {
|
|
2518
2320
|
const pkgClaudeMdPath = path.join(semoSystemDir, pkg, "CLAUDE.md");
|
|
2519
2321
|
if (fs.existsSync(pkgClaudeMdPath)) {
|
|
@@ -2680,7 +2482,7 @@ ${packageClaudeMdSections}
|
|
|
2680
2482
|
// === add 명령어 ===
|
|
2681
2483
|
program
|
|
2682
2484
|
.command("add <packages>")
|
|
2683
|
-
.description("Extension 패키지를 추가로 설치합니다 (
|
|
2485
|
+
.description("Extension 패키지를 추가로 설치합니다 (meta, semo-hooks, semo-remote)")
|
|
2684
2486
|
.option("-f, --force", "기존 설정 덮어쓰기")
|
|
2685
2487
|
.action(async (packagesInput, options) => {
|
|
2686
2488
|
const cwd = process.cwd();
|
|
@@ -2689,25 +2491,16 @@ program
|
|
|
2689
2491
|
console.log(chalk_1.default.red("\nSEMO가 설치되어 있지 않습니다. 'semo init'을 먼저 실행하세요.\n"));
|
|
2690
2492
|
process.exit(1);
|
|
2691
2493
|
}
|
|
2692
|
-
// 패키지 입력 해석
|
|
2693
|
-
const { packages
|
|
2494
|
+
// 패키지 입력 해석
|
|
2495
|
+
const { packages } = resolvePackageInput(packagesInput);
|
|
2694
2496
|
if (packages.length === 0) {
|
|
2695
2497
|
console.log(chalk_1.default.red(`\n알 수 없는 패키지: ${packagesInput}`));
|
|
2696
|
-
console.log(chalk_1.default.gray(`사용 가능한 그룹: ${PACKAGE_GROUPS.join(", ")}`));
|
|
2697
2498
|
console.log(chalk_1.default.gray(`사용 가능한 패키지: ${Object.keys(EXTENSION_PACKAGES).join(", ")}`));
|
|
2698
2499
|
console.log(chalk_1.default.gray(`단축명: ${Object.keys(SHORTNAME_MAPPING).join(", ")}\n`));
|
|
2699
2500
|
process.exit(1);
|
|
2700
2501
|
}
|
|
2701
|
-
//
|
|
2702
|
-
if (
|
|
2703
|
-
console.log(chalk_1.default.cyan.bold(`\n📦 ${groupName?.toUpperCase()} 그룹 패키지 일괄 설치\n`));
|
|
2704
|
-
console.log(chalk_1.default.gray(" 포함된 패키지:"));
|
|
2705
|
-
for (const pkg of packages) {
|
|
2706
|
-
console.log(chalk_1.default.gray(` - ${pkg} (${EXTENSION_PACKAGES[pkg].name})`));
|
|
2707
|
-
}
|
|
2708
|
-
console.log();
|
|
2709
|
-
}
|
|
2710
|
-
else if (packages.length === 1) {
|
|
2502
|
+
// 패키지 설치 안내
|
|
2503
|
+
if (packages.length === 1) {
|
|
2711
2504
|
// 단일 패키지
|
|
2712
2505
|
const pkg = packages[0];
|
|
2713
2506
|
console.log(chalk_1.default.cyan(`\n📦 ${EXTENSION_PACKAGES[pkg].name} 패키지 설치\n`));
|
|
@@ -2773,50 +2566,28 @@ program
|
|
|
2773
2566
|
.action(() => {
|
|
2774
2567
|
const cwd = process.cwd();
|
|
2775
2568
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
2776
|
-
console.log(chalk_1.default.cyan.bold("\n📦 SEMO 패키지
|
|
2777
|
-
// Standard
|
|
2569
|
+
console.log(chalk_1.default.cyan.bold("\n📦 SEMO 패키지 목록\n"));
|
|
2570
|
+
// Standard (필수)
|
|
2778
2571
|
console.log(chalk_1.default.white.bold("Standard (필수)"));
|
|
2779
2572
|
const coreInstalled = fs.existsSync(path.join(semoSystemDir, "semo-core"));
|
|
2780
2573
|
const skillsInstalled = fs.existsSync(path.join(semoSystemDir, "semo-skills"));
|
|
2781
2574
|
console.log(` ${coreInstalled ? chalk_1.default.green("✓") : chalk_1.default.gray("○")} semo-core - 원칙, 오케스트레이터`);
|
|
2782
2575
|
console.log(` ${skillsInstalled ? chalk_1.default.green("✓") : chalk_1.default.gray("○")} semo-skills - 통합 스킬`);
|
|
2783
2576
|
console.log();
|
|
2784
|
-
// Extensions
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
const
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
console.log(chalk_1.default.white.bold(`${layerInfo.emoji} ${layerInfo.title}`));
|
|
2797
|
-
for (const [key, pkg] of layerPackages) {
|
|
2798
|
-
const isInstalled = fs.existsSync(path.join(semoSystemDir, key));
|
|
2799
|
-
const status = isInstalled ? chalk_1.default.green("✓") : chalk_1.default.gray("○");
|
|
2800
|
-
const displayKey = key.includes("/") ? key.split("/")[1] : key;
|
|
2801
|
-
console.log(` ${status} ${chalk_1.default.cyan(displayKey)} - ${pkg.desc}`);
|
|
2802
|
-
console.log(chalk_1.default.gray(` semo add ${key}`));
|
|
2803
|
-
}
|
|
2804
|
-
console.log();
|
|
2577
|
+
// Extensions
|
|
2578
|
+
console.log(chalk_1.default.white.bold("Extensions (선택)"));
|
|
2579
|
+
const extensionList = [
|
|
2580
|
+
{ key: "meta", name: "Meta", desc: "SEMO 프레임워크 자체 개발/관리" },
|
|
2581
|
+
{ key: "semo-hooks", name: "Hooks", desc: "Claude Code Hooks 기반 로깅 시스템" },
|
|
2582
|
+
{ key: "semo-remote", name: "Remote", desc: "Claude Code 원격 제어 (모바일 PWA)" },
|
|
2583
|
+
];
|
|
2584
|
+
for (const ext of extensionList) {
|
|
2585
|
+
const isInstalled = fs.existsSync(path.join(semoSystemDir, ext.key));
|
|
2586
|
+
const status = isInstalled ? chalk_1.default.green("✓") : chalk_1.default.gray("○");
|
|
2587
|
+
console.log(` ${status} ${chalk_1.default.cyan(ext.key)} - ${ext.desc}`);
|
|
2588
|
+
console.log(chalk_1.default.gray(` semo add ${ext.key}`));
|
|
2805
2589
|
}
|
|
2806
|
-
// 그룹 설치 안내
|
|
2807
|
-
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
2808
|
-
console.log(chalk_1.default.white.bold("📦 그룹 일괄 설치"));
|
|
2809
|
-
console.log(chalk_1.default.gray(" semo add biz → Business 전체 (discovery, design, management, poc)"));
|
|
2810
|
-
console.log(chalk_1.default.gray(" semo add eng → Engineering 전체 (nextjs, spring, ms, infra)"));
|
|
2811
|
-
console.log(chalk_1.default.gray(" semo add ops → Operations 전체 (qa, monitor, improve)"));
|
|
2812
|
-
console.log(chalk_1.default.gray(" semo add system → System 전체 (hooks, remote)"));
|
|
2813
2590
|
console.log();
|
|
2814
|
-
// 단축명 안내
|
|
2815
|
-
console.log(chalk_1.default.gray("─".repeat(50)));
|
|
2816
|
-
console.log(chalk_1.default.white.bold("⚡ 단축명 지원"));
|
|
2817
|
-
console.log(chalk_1.default.gray(" semo add discovery → biz/discovery"));
|
|
2818
|
-
console.log(chalk_1.default.gray(" semo add qa → ops/qa"));
|
|
2819
|
-
console.log(chalk_1.default.gray(" semo add nextjs → eng/nextjs\n"));
|
|
2820
2591
|
});
|
|
2821
2592
|
// === status 명령어 ===
|
|
2822
2593
|
program
|