@team-semicolon/semo-cli 3.9.0 → 3.12.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 +103 -70
- package/dist/supabase.d.ts +73 -0
- package/dist/supabase.js +532 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -59,6 +59,7 @@ 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 supabase_1 = require("./supabase");
|
|
62
63
|
const PACKAGE_NAME = "@team-semicolon/semo-cli";
|
|
63
64
|
// package.json에서 버전 동적 로드
|
|
64
65
|
function getCliVersion() {
|
|
@@ -673,8 +674,39 @@ function copyRecursive(src, dest) {
|
|
|
673
674
|
}
|
|
674
675
|
}
|
|
675
676
|
const SEMO_REPO = "https://github.com/semicolon-devteam/semo.git";
|
|
676
|
-
//
|
|
677
|
-
|
|
677
|
+
// ============================================================
|
|
678
|
+
// DB 기반 패키지 관리 (v3.10+)
|
|
679
|
+
// ============================================================
|
|
680
|
+
// 캐시된 패키지 데이터 (DB에서 조회 후 캐시)
|
|
681
|
+
let cachedExtensionPackages = null;
|
|
682
|
+
let cachedShortnameMappings = null;
|
|
683
|
+
let cachedPackageDefinitions = null;
|
|
684
|
+
// 패키지 데이터 초기화 (DB에서 조회)
|
|
685
|
+
async function initPackageData() {
|
|
686
|
+
if (cachedExtensionPackages && cachedShortnameMappings)
|
|
687
|
+
return;
|
|
688
|
+
try {
|
|
689
|
+
cachedExtensionPackages = await (0, supabase_1.buildExtensionPackagesFromDb)();
|
|
690
|
+
cachedShortnameMappings = await (0, supabase_1.buildShortnameMappingFromDb)();
|
|
691
|
+
cachedPackageDefinitions = await (0, supabase_1.getExtensionPackages)();
|
|
692
|
+
}
|
|
693
|
+
catch {
|
|
694
|
+
// 폴백: 하드코딩된 데이터 사용
|
|
695
|
+
cachedExtensionPackages = EXTENSION_PACKAGES_FALLBACK;
|
|
696
|
+
cachedShortnameMappings = SHORTNAME_MAPPING_FALLBACK;
|
|
697
|
+
cachedPackageDefinitions = null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
// EXTENSION_PACKAGES 동기 접근용 (초기화 후 사용)
|
|
701
|
+
function getExtensionPackagesSync() {
|
|
702
|
+
return cachedExtensionPackages || EXTENSION_PACKAGES_FALLBACK;
|
|
703
|
+
}
|
|
704
|
+
// SHORTNAME_MAPPING 동기 접근용
|
|
705
|
+
function getShortnameMappingSync() {
|
|
706
|
+
return cachedShortnameMappings || SHORTNAME_MAPPING_FALLBACK;
|
|
707
|
+
}
|
|
708
|
+
// 폴백용 하드코딩 데이터 (DB 연결 실패 시 사용)
|
|
709
|
+
const EXTENSION_PACKAGES_FALLBACK = {
|
|
678
710
|
// Business Layer
|
|
679
711
|
"biz/discovery": { name: "Discovery", desc: "아이템 발굴, 시장 조사, Epic/Task", layer: "biz", detect: [] },
|
|
680
712
|
"biz/design": { name: "Design", desc: "컨셉 설계, 목업, UX", layer: "biz", detect: [] },
|
|
@@ -695,8 +727,8 @@ const EXTENSION_PACKAGES = {
|
|
|
695
727
|
"semo-hooks": { name: "Hooks", desc: "Claude Code Hooks 기반 로깅 시스템", layer: "system", detect: [] },
|
|
696
728
|
"semo-remote": { name: "Remote", desc: "Claude Code 원격 제어 (모바일 PWA)", layer: "system", detect: [] },
|
|
697
729
|
};
|
|
698
|
-
// 단축명 → 전체 패키지 경로 매핑
|
|
699
|
-
const
|
|
730
|
+
// 단축명 → 전체 패키지 경로 매핑 (폴백)
|
|
731
|
+
const SHORTNAME_MAPPING_FALLBACK = {
|
|
700
732
|
// 하위 패키지명 단축 (discovery → biz/discovery)
|
|
701
733
|
discovery: "biz/discovery",
|
|
702
734
|
design: "biz/design",
|
|
@@ -717,11 +749,20 @@ const SHORTNAME_MAPPING = {
|
|
|
717
749
|
hooks: "semo-hooks",
|
|
718
750
|
remote: "semo-remote",
|
|
719
751
|
};
|
|
752
|
+
// 호환성을 위한 상수 별칭 (기존 코드에서 사용)
|
|
753
|
+
const EXTENSION_PACKAGES = EXTENSION_PACKAGES_FALLBACK;
|
|
754
|
+
const SHORTNAME_MAPPING = SHORTNAME_MAPPING_FALLBACK;
|
|
720
755
|
// 그룹 이름 목록 (biz, eng, ops, meta, system)
|
|
721
756
|
const PACKAGE_GROUPS = ["biz", "eng", "ops", "meta", "system"];
|
|
722
|
-
// 그룹명 → 해당 그룹의 모든 패키지 반환
|
|
723
|
-
function
|
|
724
|
-
|
|
757
|
+
// 그룹명 → 해당 그룹의 모든 패키지 반환 (DB 기반)
|
|
758
|
+
async function getPackagesByGroupAsync(group) {
|
|
759
|
+
const packages = await (0, supabase_1.getPackagesByGroup)(group);
|
|
760
|
+
return packages.map(p => p.name);
|
|
761
|
+
}
|
|
762
|
+
// 그룹명 → 해당 그룹의 모든 패키지 반환 (동기, 폴백)
|
|
763
|
+
function getPackagesByGroupSync(group) {
|
|
764
|
+
const extPkgs = getExtensionPackagesSync();
|
|
765
|
+
return Object.entries(extPkgs)
|
|
725
766
|
.filter(([, pkg]) => pkg.layer === group)
|
|
726
767
|
.map(([key]) => key);
|
|
727
768
|
}
|
|
@@ -732,22 +773,25 @@ function resolvePackageInput(input) {
|
|
|
732
773
|
const resolvedPackages = [];
|
|
733
774
|
let isGroup = false;
|
|
734
775
|
let groupName;
|
|
776
|
+
// DB에서 로드된 데이터 또는 폴백 사용
|
|
777
|
+
const extPkgs = getExtensionPackagesSync();
|
|
778
|
+
const shortnames = getShortnameMappingSync();
|
|
735
779
|
for (const part of parts) {
|
|
736
780
|
// 1. 그룹명인지 확인 (biz, eng, ops, meta)
|
|
737
781
|
if (PACKAGE_GROUPS.includes(part)) {
|
|
738
|
-
const groupPackages =
|
|
782
|
+
const groupPackages = getPackagesByGroupSync(part);
|
|
739
783
|
resolvedPackages.push(...groupPackages);
|
|
740
784
|
isGroup = true;
|
|
741
785
|
groupName = part;
|
|
742
786
|
continue;
|
|
743
787
|
}
|
|
744
788
|
// 2. 단축명 매핑 확인 (discovery → biz/discovery 등)
|
|
745
|
-
if (part in
|
|
746
|
-
resolvedPackages.push(
|
|
789
|
+
if (part in shortnames) {
|
|
790
|
+
resolvedPackages.push(shortnames[part]);
|
|
747
791
|
continue;
|
|
748
792
|
}
|
|
749
793
|
// 3. 직접 패키지명 확인
|
|
750
|
-
if (part in
|
|
794
|
+
if (part in extPkgs) {
|
|
751
795
|
resolvedPackages.push(part);
|
|
752
796
|
continue;
|
|
753
797
|
}
|
|
@@ -1132,8 +1176,10 @@ program
|
|
|
1132
1176
|
.action(async (options) => {
|
|
1133
1177
|
console.log(chalk_1.default.cyan.bold("\n🚀 SEMO 설치 시작\n"));
|
|
1134
1178
|
console.log(chalk_1.default.gray("Gemini 하이브리드 전략: White Box + Black Box\n"));
|
|
1179
|
+
// 0. 패키지 데이터 초기화 (DB에서 조회)
|
|
1180
|
+
await initPackageData();
|
|
1135
1181
|
const cwd = process.cwd();
|
|
1136
|
-
// 0. 버전 비교
|
|
1182
|
+
// 0.1. 버전 비교
|
|
1137
1183
|
await showVersionComparison(cwd);
|
|
1138
1184
|
// 0.5. 레거시 환경 감지 및 마이그레이션
|
|
1139
1185
|
const legacyCheck = detectLegacyEnvironment(cwd);
|
|
@@ -1159,55 +1205,27 @@ program
|
|
|
1159
1205
|
spinner.fail("Git 레포지토리가 아닙니다. 'git init'을 먼저 실행하세요.");
|
|
1160
1206
|
process.exit(1);
|
|
1161
1207
|
}
|
|
1162
|
-
// 2.
|
|
1163
|
-
const detected = detectProjectType(cwd);
|
|
1208
|
+
// 2. Extension 패키지 처리 (--with 옵션만 지원, 인터랙션 없음)
|
|
1164
1209
|
let extensionsToInstall = [];
|
|
1210
|
+
const extPkgs = getExtensionPackagesSync();
|
|
1211
|
+
const shortnames = getShortnameMappingSync();
|
|
1165
1212
|
if (options.with) {
|
|
1166
|
-
|
|
1213
|
+
// --with 옵션으로 명시적 패키지 지정 시에만 Extension 설치
|
|
1214
|
+
extensionsToInstall = options.with.split(",").map((p) => p.trim()).filter((p) => p in extPkgs || p in shortnames);
|
|
1215
|
+
// 별칭 처리
|
|
1216
|
+
extensionsToInstall = extensionsToInstall.map((p) => shortnames[p] || p);
|
|
1167
1217
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1218
|
+
// 프로젝트 유형 감지는 정보 제공용으로만 사용 (자동 설치 안 함)
|
|
1219
|
+
const detected = detectProjectType(cwd);
|
|
1220
|
+
if (detected.length > 0 && !options.with) {
|
|
1221
|
+
console.log(chalk_1.default.cyan("\n💡 감지된 프로젝트 유형:"));
|
|
1170
1222
|
detected.forEach(pkg => {
|
|
1171
|
-
|
|
1223
|
+
const pkgInfo = extPkgs[pkg];
|
|
1224
|
+
if (pkgInfo) {
|
|
1225
|
+
console.log(chalk_1.default.gray(` - ${pkgInfo.name}: ${pkgInfo.desc}`));
|
|
1226
|
+
}
|
|
1172
1227
|
});
|
|
1173
|
-
|
|
1174
|
-
{
|
|
1175
|
-
type: "confirm",
|
|
1176
|
-
name: "installDetected",
|
|
1177
|
-
message: "감지된 패키지를 함께 설치할까요?",
|
|
1178
|
-
default: true,
|
|
1179
|
-
},
|
|
1180
|
-
]);
|
|
1181
|
-
if (installDetected) {
|
|
1182
|
-
extensionsToInstall = detected;
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
else {
|
|
1186
|
-
// 프로젝트 유형이 감지되지 않은 경우 패키지 선택 프롬프트
|
|
1187
|
-
console.log(chalk_1.default.cyan("\n📦 추가 패키지 선택"));
|
|
1188
|
-
console.log(chalk_1.default.gray(" 기본 설치 (semo-core + semo-skills) 외에 추가할 패키지를 선택하세요.\n"));
|
|
1189
|
-
// 그룹별로 패키지 구성
|
|
1190
|
-
const packageChoices = [
|
|
1191
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Engineering ──")),
|
|
1192
|
-
{ name: `eng/nextjs - ${EXTENSION_PACKAGES["eng/nextjs"].desc}`, value: "eng/nextjs" },
|
|
1193
|
-
{ name: `eng/spring - ${EXTENSION_PACKAGES["eng/spring"].desc}`, value: "eng/spring" },
|
|
1194
|
-
{ name: `eng/infra - ${EXTENSION_PACKAGES["eng/infra"].desc}`, value: "eng/infra" },
|
|
1195
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Business ──")),
|
|
1196
|
-
{ name: `biz/discovery - ${EXTENSION_PACKAGES["biz/discovery"].desc}`, value: "biz/discovery" },
|
|
1197
|
-
{ name: `biz/management - ${EXTENSION_PACKAGES["biz/management"].desc}`, value: "biz/management" },
|
|
1198
|
-
{ name: `biz/design - ${EXTENSION_PACKAGES["biz/design"].desc}`, value: "biz/design" },
|
|
1199
|
-
new inquirer_1.default.Separator(chalk_1.default.yellow("── Operations ──")),
|
|
1200
|
-
{ name: `ops/qa - ${EXTENSION_PACKAGES["ops/qa"].desc}`, value: "ops/qa" },
|
|
1201
|
-
];
|
|
1202
|
-
const { selectedPackages } = await inquirer_1.default.prompt([
|
|
1203
|
-
{
|
|
1204
|
-
type: "checkbox",
|
|
1205
|
-
name: "selectedPackages",
|
|
1206
|
-
message: "설치할 패키지 선택 (Space로 선택, Enter로 완료):",
|
|
1207
|
-
choices: packageChoices,
|
|
1208
|
-
},
|
|
1209
|
-
]);
|
|
1210
|
-
extensionsToInstall = selectedPackages;
|
|
1228
|
+
console.log(chalk_1.default.gray(`\n 추가 패키지가 필요하면: semo add ${detected[0].split("/")[1] || detected[0]}`));
|
|
1211
1229
|
}
|
|
1212
1230
|
// 3. .claude 디렉토리 생성
|
|
1213
1231
|
const claudeDir = path.join(cwd, ".claude");
|
|
@@ -1265,8 +1283,8 @@ program
|
|
|
1265
1283
|
console.log(chalk_1.default.gray(" 1. Claude Code에서 프로젝트 열기"));
|
|
1266
1284
|
console.log(chalk_1.default.gray(" 2. 자연어로 요청하기 (예: \"댓글 기능 구현해줘\")"));
|
|
1267
1285
|
console.log(chalk_1.default.gray(" 3. /SEMO:help로 도움말 확인"));
|
|
1268
|
-
if (extensionsToInstall.length === 0
|
|
1269
|
-
console.log(chalk_1.default.gray("\n💡 추가
|
|
1286
|
+
if (extensionsToInstall.length === 0) {
|
|
1287
|
+
console.log(chalk_1.default.gray("\n💡 추가 패키지가 필요하면: semo add <package> (예: semo add next)"));
|
|
1270
1288
|
}
|
|
1271
1289
|
console.log();
|
|
1272
1290
|
});
|
|
@@ -2777,6 +2795,8 @@ program
|
|
|
2777
2795
|
.description("Extension 패키지를 추가로 설치합니다 (그룹: biz, eng, ops, system / 개별: biz/discovery, eng/nextjs, semo-hooks)")
|
|
2778
2796
|
.option("-f, --force", "기존 설정 덮어쓰기")
|
|
2779
2797
|
.action(async (packagesInput, options) => {
|
|
2798
|
+
// 패키지 데이터 초기화 (DB에서 조회)
|
|
2799
|
+
await initPackageData();
|
|
2780
2800
|
const cwd = process.cwd();
|
|
2781
2801
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
2782
2802
|
if (!fs.existsSync(semoSystemDir)) {
|
|
@@ -2785,11 +2805,13 @@ program
|
|
|
2785
2805
|
}
|
|
2786
2806
|
// 패키지 입력 해석 (그룹, 레거시, 쉼표 구분 모두 처리)
|
|
2787
2807
|
const { packages, isGroup, groupName } = resolvePackageInput(packagesInput);
|
|
2808
|
+
const extPkgs = getExtensionPackagesSync();
|
|
2809
|
+
const shortnames = getShortnameMappingSync();
|
|
2788
2810
|
if (packages.length === 0) {
|
|
2789
2811
|
console.log(chalk_1.default.red(`\n알 수 없는 패키지: ${packagesInput}`));
|
|
2790
2812
|
console.log(chalk_1.default.gray(`사용 가능한 그룹: ${PACKAGE_GROUPS.join(", ")}`));
|
|
2791
|
-
console.log(chalk_1.default.gray(`사용 가능한 패키지: ${Object.keys(
|
|
2792
|
-
console.log(chalk_1.default.gray(`단축명: ${Object.keys(
|
|
2813
|
+
console.log(chalk_1.default.gray(`사용 가능한 패키지: ${Object.keys(extPkgs).join(", ")}`));
|
|
2814
|
+
console.log(chalk_1.default.gray(`단축명: ${Object.keys(shortnames).join(", ")}\n`));
|
|
2793
2815
|
process.exit(1);
|
|
2794
2816
|
}
|
|
2795
2817
|
// 그룹 설치인 경우 안내
|
|
@@ -2797,21 +2819,24 @@ program
|
|
|
2797
2819
|
console.log(chalk_1.default.cyan.bold(`\n📦 ${groupName?.toUpperCase()} 그룹 패키지 일괄 설치\n`));
|
|
2798
2820
|
console.log(chalk_1.default.gray(" 포함된 패키지:"));
|
|
2799
2821
|
for (const pkg of packages) {
|
|
2800
|
-
|
|
2822
|
+
const pkgInfo = extPkgs[pkg];
|
|
2823
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${pkgInfo?.name || pkg})`));
|
|
2801
2824
|
}
|
|
2802
2825
|
console.log();
|
|
2803
2826
|
}
|
|
2804
2827
|
else if (packages.length === 1) {
|
|
2805
2828
|
// 단일 패키지
|
|
2806
2829
|
const pkg = packages[0];
|
|
2807
|
-
|
|
2808
|
-
console.log(chalk_1.default.
|
|
2830
|
+
const pkgInfo = extPkgs[pkg];
|
|
2831
|
+
console.log(chalk_1.default.cyan(`\n📦 ${pkgInfo?.name || pkg} 패키지 설치\n`));
|
|
2832
|
+
console.log(chalk_1.default.gray(` ${pkgInfo?.desc || ""}\n`));
|
|
2809
2833
|
}
|
|
2810
2834
|
else {
|
|
2811
2835
|
// 여러 패키지 (쉼표 구분)
|
|
2812
2836
|
console.log(chalk_1.default.cyan.bold(`\n📦 ${packages.length}개 패키지 설치\n`));
|
|
2813
2837
|
for (const pkg of packages) {
|
|
2814
|
-
|
|
2838
|
+
const pkgInfo = extPkgs[pkg];
|
|
2839
|
+
console.log(chalk_1.default.gray(` - ${pkg} (${pkgInfo?.name || pkg})`));
|
|
2815
2840
|
}
|
|
2816
2841
|
console.log();
|
|
2817
2842
|
}
|
|
@@ -2850,12 +2875,14 @@ program
|
|
|
2850
2875
|
// 4. CLAUDE.md 재생성 (모든 설치된 패키지 반영)
|
|
2851
2876
|
await setupClaudeMd(cwd, allInstalledPackages, options.force);
|
|
2852
2877
|
if (toInstall.length === 1) {
|
|
2853
|
-
|
|
2878
|
+
const pkgInfo = extPkgs[toInstall[0]];
|
|
2879
|
+
console.log(chalk_1.default.green.bold(`\n✅ ${pkgInfo?.name || toInstall[0]} 패키지 설치 완료!\n`));
|
|
2854
2880
|
}
|
|
2855
2881
|
else {
|
|
2856
2882
|
console.log(chalk_1.default.green.bold(`\n✅ ${toInstall.length}개 패키지 설치 완료!`));
|
|
2857
2883
|
for (const pkg of toInstall) {
|
|
2858
|
-
|
|
2884
|
+
const pkgInfo = extPkgs[pkg];
|
|
2885
|
+
console.log(chalk_1.default.green(` ✓ ${pkgInfo?.name || pkg}`));
|
|
2859
2886
|
}
|
|
2860
2887
|
console.log();
|
|
2861
2888
|
}
|
|
@@ -2864,10 +2891,13 @@ program
|
|
|
2864
2891
|
program
|
|
2865
2892
|
.command("list")
|
|
2866
2893
|
.description("사용 가능한 모든 패키지를 표시합니다")
|
|
2867
|
-
.action(() => {
|
|
2894
|
+
.action(async () => {
|
|
2895
|
+
// 패키지 데이터 초기화 (DB에서 조회)
|
|
2896
|
+
await initPackageData();
|
|
2868
2897
|
const cwd = process.cwd();
|
|
2869
2898
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
2870
|
-
|
|
2899
|
+
const extPkgs = getExtensionPackagesSync();
|
|
2900
|
+
console.log(chalk_1.default.cyan.bold("\n📦 SEMO 패키지 목록 (v3.10 - DB 기반)\n"));
|
|
2871
2901
|
// Standard
|
|
2872
2902
|
console.log(chalk_1.default.white.bold("Standard (필수)"));
|
|
2873
2903
|
const coreInstalled = fs.existsSync(path.join(semoSystemDir, "semo-core"));
|
|
@@ -2884,7 +2914,7 @@ program
|
|
|
2884
2914
|
system: { title: "System", emoji: "🔩" },
|
|
2885
2915
|
};
|
|
2886
2916
|
for (const [layerKey, layerInfo] of Object.entries(layers)) {
|
|
2887
|
-
const layerPackages = Object.entries(
|
|
2917
|
+
const layerPackages = Object.entries(extPkgs).filter(([, pkg]) => pkg.layer === layerKey);
|
|
2888
2918
|
if (layerPackages.length === 0)
|
|
2889
2919
|
continue;
|
|
2890
2920
|
console.log(chalk_1.default.white.bold(`${layerInfo.emoji} ${layerInfo.title}`));
|
|
@@ -2981,6 +3011,8 @@ program
|
|
|
2981
3011
|
.option("--migrate", "레거시 환경 강제 마이그레이션")
|
|
2982
3012
|
.action(async (options) => {
|
|
2983
3013
|
console.log(chalk_1.default.cyan.bold("\n🔄 SEMO 업데이트\n"));
|
|
3014
|
+
// 패키지 데이터 초기화 (DB에서 조회)
|
|
3015
|
+
await initPackageData();
|
|
2984
3016
|
const cwd = process.cwd();
|
|
2985
3017
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
2986
3018
|
const claudeDir = path.join(cwd, ".claude");
|
|
@@ -3033,7 +3065,8 @@ program
|
|
|
3033
3065
|
}
|
|
3034
3066
|
// 설치된 Extensions 확인
|
|
3035
3067
|
const installedExtensions = [];
|
|
3036
|
-
|
|
3068
|
+
const extPkgs = getExtensionPackagesSync();
|
|
3069
|
+
for (const key of Object.keys(extPkgs)) {
|
|
3037
3070
|
if (fs.existsSync(path.join(semoSystemDir, key))) {
|
|
3038
3071
|
installedExtensions.push(key);
|
|
3039
3072
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEMO CLI - Supabase 클라이언트
|
|
3
|
+
*
|
|
4
|
+
* package_definitions 테이블에서 패키지 정보를 조회합니다.
|
|
5
|
+
* DB 연결 실패 시 하드코딩된 폴백 데이터를 사용합니다.
|
|
6
|
+
*/
|
|
7
|
+
export interface PackageDefinition {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
display_name: string;
|
|
11
|
+
description: string | null;
|
|
12
|
+
layer: "standard" | "biz" | "eng" | "ops" | "system" | "meta";
|
|
13
|
+
package_type: "standard" | "extension";
|
|
14
|
+
version: string;
|
|
15
|
+
repo_url: string;
|
|
16
|
+
source_path: string;
|
|
17
|
+
detect_files: string[];
|
|
18
|
+
depends_on: string[];
|
|
19
|
+
aliases: string[];
|
|
20
|
+
is_active: boolean;
|
|
21
|
+
is_required: boolean;
|
|
22
|
+
install_order: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 모든 패키지 목록 조회
|
|
26
|
+
*/
|
|
27
|
+
export declare function getPackages(layer?: string): Promise<PackageDefinition[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Standard 패키지 목록 조회
|
|
30
|
+
*/
|
|
31
|
+
export declare function getStandardPackages(): Promise<PackageDefinition[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Extension 패키지 목록 조회
|
|
34
|
+
*/
|
|
35
|
+
export declare function getExtensionPackages(layer?: string): Promise<PackageDefinition[]>;
|
|
36
|
+
/**
|
|
37
|
+
* 패키지명 또는 별칭으로 패키지 찾기
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolvePackageName(input: string): Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* 패키지 버전 조회
|
|
42
|
+
*/
|
|
43
|
+
export declare function getPackageVersion(name: string): Promise<string | null>;
|
|
44
|
+
/**
|
|
45
|
+
* 그룹별 패키지 목록 조회
|
|
46
|
+
*/
|
|
47
|
+
export declare function getPackagesByGroup(group: string): Promise<PackageDefinition[]>;
|
|
48
|
+
/**
|
|
49
|
+
* 프로젝트 타입 감지용 패키지 조회
|
|
50
|
+
*/
|
|
51
|
+
export declare function getDetectablePackages(): Promise<PackageDefinition[]>;
|
|
52
|
+
/**
|
|
53
|
+
* 패키지 정보를 CLI 포맷으로 변환
|
|
54
|
+
*/
|
|
55
|
+
export declare function toExtensionPackageFormat(pkg: PackageDefinition): {
|
|
56
|
+
name: string;
|
|
57
|
+
desc: string;
|
|
58
|
+
detect: string[];
|
|
59
|
+
layer: string;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* 별칭 매핑 객체 생성 (SHORTNAME_MAPPING 대체)
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildShortnameMappingFromDb(): Promise<Record<string, string>>;
|
|
65
|
+
/**
|
|
66
|
+
* EXTENSION_PACKAGES 형태의 객체 생성 (호환성용)
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildExtensionPackagesFromDb(): Promise<Record<string, {
|
|
69
|
+
name: string;
|
|
70
|
+
desc: string;
|
|
71
|
+
detect: string[];
|
|
72
|
+
layer: string;
|
|
73
|
+
}>>;
|
package/dist/supabase.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SEMO CLI - Supabase 클라이언트
|
|
4
|
+
*
|
|
5
|
+
* package_definitions 테이블에서 패키지 정보를 조회합니다.
|
|
6
|
+
* DB 연결 실패 시 하드코딩된 폴백 데이터를 사용합니다.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getPackages = getPackages;
|
|
10
|
+
exports.getStandardPackages = getStandardPackages;
|
|
11
|
+
exports.getExtensionPackages = getExtensionPackages;
|
|
12
|
+
exports.resolvePackageName = resolvePackageName;
|
|
13
|
+
exports.getPackageVersion = getPackageVersion;
|
|
14
|
+
exports.getPackagesByGroup = getPackagesByGroup;
|
|
15
|
+
exports.getDetectablePackages = getDetectablePackages;
|
|
16
|
+
exports.toExtensionPackageFormat = toExtensionPackageFormat;
|
|
17
|
+
exports.buildShortnameMappingFromDb = buildShortnameMappingFromDb;
|
|
18
|
+
exports.buildExtensionPackagesFromDb = buildExtensionPackagesFromDb;
|
|
19
|
+
const supabase_js_1 = require("@supabase/supabase-js");
|
|
20
|
+
// Supabase 연결 정보 (공개 프로젝트용)
|
|
21
|
+
const SUPABASE_URL = process.env.SEMO_SUPABASE_URL || "https://vdrllieckyeumbhyclkc.supabase.co";
|
|
22
|
+
const SUPABASE_ANON_KEY = process.env.SEMO_SUPABASE_ANON_KEY ||
|
|
23
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZkcmxsaWVja3lldW1iaHljbGtjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc5NzgxMTEsImV4cCI6MjA4MzU1NDExMX0.SrruG4y9geH1bCWE4uLzMHiUA38UcGMQwbAxxaaa718";
|
|
24
|
+
// Supabase 클라이언트 (싱글톤)
|
|
25
|
+
let supabaseClient = null;
|
|
26
|
+
function getSupabaseClient() {
|
|
27
|
+
if (!supabaseClient) {
|
|
28
|
+
supabaseClient = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_ANON_KEY);
|
|
29
|
+
}
|
|
30
|
+
return supabaseClient;
|
|
31
|
+
}
|
|
32
|
+
// DB 연결 가능 여부 확인
|
|
33
|
+
let dbAvailable = null;
|
|
34
|
+
async function checkDbConnection() {
|
|
35
|
+
if (dbAvailable !== null)
|
|
36
|
+
return dbAvailable;
|
|
37
|
+
try {
|
|
38
|
+
const supabase = getSupabaseClient();
|
|
39
|
+
const { error } = await supabase
|
|
40
|
+
.from("package_definitions")
|
|
41
|
+
.select("id")
|
|
42
|
+
.limit(1);
|
|
43
|
+
dbAvailable = !error;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
dbAvailable = false;
|
|
47
|
+
}
|
|
48
|
+
return dbAvailable;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 모든 패키지 목록 조회
|
|
52
|
+
*/
|
|
53
|
+
async function getPackages(layer) {
|
|
54
|
+
const isConnected = await checkDbConnection();
|
|
55
|
+
if (!isConnected) {
|
|
56
|
+
console.warn("⚠️ DB 연결 실패, 폴백 데이터 사용");
|
|
57
|
+
return getFallbackPackages(layer);
|
|
58
|
+
}
|
|
59
|
+
const supabase = getSupabaseClient();
|
|
60
|
+
let query = supabase
|
|
61
|
+
.from("package_definitions")
|
|
62
|
+
.select("*")
|
|
63
|
+
.eq("is_active", true);
|
|
64
|
+
if (layer) {
|
|
65
|
+
query = query.eq("layer", layer);
|
|
66
|
+
}
|
|
67
|
+
const { data, error } = await query.order("install_order");
|
|
68
|
+
if (error) {
|
|
69
|
+
console.warn("⚠️ 패키지 조회 실패, 폴백 데이터 사용");
|
|
70
|
+
return getFallbackPackages(layer);
|
|
71
|
+
}
|
|
72
|
+
return data || [];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Standard 패키지 목록 조회
|
|
76
|
+
*/
|
|
77
|
+
async function getStandardPackages() {
|
|
78
|
+
const isConnected = await checkDbConnection();
|
|
79
|
+
if (!isConnected) {
|
|
80
|
+
return getFallbackPackages().filter((p) => p.package_type === "standard");
|
|
81
|
+
}
|
|
82
|
+
const supabase = getSupabaseClient();
|
|
83
|
+
const { data, error } = await supabase
|
|
84
|
+
.from("package_definitions")
|
|
85
|
+
.select("*")
|
|
86
|
+
.eq("package_type", "standard")
|
|
87
|
+
.eq("is_active", true)
|
|
88
|
+
.order("install_order");
|
|
89
|
+
if (error) {
|
|
90
|
+
return getFallbackPackages().filter((p) => p.package_type === "standard");
|
|
91
|
+
}
|
|
92
|
+
return data || [];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Extension 패키지 목록 조회
|
|
96
|
+
*/
|
|
97
|
+
async function getExtensionPackages(layer) {
|
|
98
|
+
const isConnected = await checkDbConnection();
|
|
99
|
+
if (!isConnected) {
|
|
100
|
+
const fallback = getFallbackPackages(layer);
|
|
101
|
+
return fallback.filter((p) => p.package_type === "extension");
|
|
102
|
+
}
|
|
103
|
+
const supabase = getSupabaseClient();
|
|
104
|
+
let query = supabase
|
|
105
|
+
.from("package_definitions")
|
|
106
|
+
.select("*")
|
|
107
|
+
.eq("package_type", "extension")
|
|
108
|
+
.eq("is_active", true);
|
|
109
|
+
if (layer) {
|
|
110
|
+
query = query.eq("layer", layer);
|
|
111
|
+
}
|
|
112
|
+
const { data, error } = await query.order("install_order");
|
|
113
|
+
if (error) {
|
|
114
|
+
const fallback = getFallbackPackages(layer);
|
|
115
|
+
return fallback.filter((p) => p.package_type === "extension");
|
|
116
|
+
}
|
|
117
|
+
return data || [];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 패키지명 또는 별칭으로 패키지 찾기
|
|
121
|
+
*/
|
|
122
|
+
async function resolvePackageName(input) {
|
|
123
|
+
const isConnected = await checkDbConnection();
|
|
124
|
+
if (!isConnected) {
|
|
125
|
+
return resolveFallbackPackageName(input);
|
|
126
|
+
}
|
|
127
|
+
const supabase = getSupabaseClient();
|
|
128
|
+
// 1. 정확한 이름 매칭
|
|
129
|
+
const { data: exactMatch } = await supabase
|
|
130
|
+
.from("package_definitions")
|
|
131
|
+
.select("name")
|
|
132
|
+
.eq("name", input)
|
|
133
|
+
.eq("is_active", true)
|
|
134
|
+
.single();
|
|
135
|
+
if (exactMatch)
|
|
136
|
+
return exactMatch.name;
|
|
137
|
+
// 2. 별칭 매칭 (aliases 배열에 포함)
|
|
138
|
+
const { data: aliasMatch } = await supabase
|
|
139
|
+
.from("package_definitions")
|
|
140
|
+
.select("name")
|
|
141
|
+
.contains("aliases", [input])
|
|
142
|
+
.eq("is_active", true)
|
|
143
|
+
.single();
|
|
144
|
+
if (aliasMatch)
|
|
145
|
+
return aliasMatch.name;
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 패키지 버전 조회
|
|
150
|
+
*/
|
|
151
|
+
async function getPackageVersion(name) {
|
|
152
|
+
const isConnected = await checkDbConnection();
|
|
153
|
+
if (!isConnected) {
|
|
154
|
+
const pkg = getFallbackPackages().find((p) => p.name === name);
|
|
155
|
+
return pkg?.version || null;
|
|
156
|
+
}
|
|
157
|
+
const supabase = getSupabaseClient();
|
|
158
|
+
const { data } = await supabase
|
|
159
|
+
.from("package_definitions")
|
|
160
|
+
.select("version")
|
|
161
|
+
.eq("name", name)
|
|
162
|
+
.single();
|
|
163
|
+
return data?.version || null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 그룹별 패키지 목록 조회
|
|
167
|
+
*/
|
|
168
|
+
async function getPackagesByGroup(group) {
|
|
169
|
+
return getExtensionPackages(group);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 프로젝트 타입 감지용 패키지 조회
|
|
173
|
+
*/
|
|
174
|
+
async function getDetectablePackages() {
|
|
175
|
+
const packages = await getExtensionPackages();
|
|
176
|
+
return packages.filter((p) => p.detect_files && p.detect_files.length > 0);
|
|
177
|
+
}
|
|
178
|
+
// ============================================================
|
|
179
|
+
// 폴백 데이터 (DB 연결 실패 시 사용)
|
|
180
|
+
// ============================================================
|
|
181
|
+
const FALLBACK_PACKAGES = [
|
|
182
|
+
// Standard
|
|
183
|
+
{
|
|
184
|
+
id: "std-core",
|
|
185
|
+
name: "semo-core",
|
|
186
|
+
display_name: "SEMO Core",
|
|
187
|
+
description: "원칙, 오케스트레이터, 워크플로우 커맨드",
|
|
188
|
+
layer: "standard",
|
|
189
|
+
package_type: "standard",
|
|
190
|
+
version: "3.12.0",
|
|
191
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
192
|
+
source_path: "semo-system/semo-core",
|
|
193
|
+
detect_files: [],
|
|
194
|
+
depends_on: [],
|
|
195
|
+
aliases: [],
|
|
196
|
+
is_active: true,
|
|
197
|
+
is_required: true,
|
|
198
|
+
install_order: 10,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "std-skills",
|
|
202
|
+
name: "semo-skills",
|
|
203
|
+
display_name: "SEMO Skills",
|
|
204
|
+
description: "18개 통합 스킬 (워크플로우 포함)",
|
|
205
|
+
layer: "standard",
|
|
206
|
+
package_type: "standard",
|
|
207
|
+
version: "3.12.0",
|
|
208
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
209
|
+
source_path: "semo-system/semo-skills",
|
|
210
|
+
detect_files: [],
|
|
211
|
+
depends_on: [],
|
|
212
|
+
aliases: [],
|
|
213
|
+
is_active: true,
|
|
214
|
+
is_required: true,
|
|
215
|
+
install_order: 20,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "std-scripts",
|
|
219
|
+
name: "semo-scripts",
|
|
220
|
+
display_name: "SEMO Scripts",
|
|
221
|
+
description: "자동화 스크립트",
|
|
222
|
+
layer: "standard",
|
|
223
|
+
package_type: "standard",
|
|
224
|
+
version: "3.12.0",
|
|
225
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
226
|
+
source_path: "semo-system/semo-scripts",
|
|
227
|
+
detect_files: [],
|
|
228
|
+
depends_on: [],
|
|
229
|
+
aliases: [],
|
|
230
|
+
is_active: true,
|
|
231
|
+
is_required: true,
|
|
232
|
+
install_order: 30,
|
|
233
|
+
},
|
|
234
|
+
// Business Layer
|
|
235
|
+
{
|
|
236
|
+
id: "biz-discovery",
|
|
237
|
+
name: "biz/discovery",
|
|
238
|
+
display_name: "Discovery",
|
|
239
|
+
description: "아이템 발굴, 시장 조사, Epic/Task",
|
|
240
|
+
layer: "biz",
|
|
241
|
+
package_type: "extension",
|
|
242
|
+
version: "1.0.0",
|
|
243
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
244
|
+
source_path: "semo-system/biz/discovery",
|
|
245
|
+
detect_files: [],
|
|
246
|
+
depends_on: [],
|
|
247
|
+
aliases: ["discovery"],
|
|
248
|
+
is_active: true,
|
|
249
|
+
is_required: false,
|
|
250
|
+
install_order: 100,
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: "biz-design",
|
|
254
|
+
name: "biz/design",
|
|
255
|
+
display_name: "Design",
|
|
256
|
+
description: "컨셉 설계, 목업, UX",
|
|
257
|
+
layer: "biz",
|
|
258
|
+
package_type: "extension",
|
|
259
|
+
version: "1.0.0",
|
|
260
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
261
|
+
source_path: "semo-system/biz/design",
|
|
262
|
+
detect_files: [],
|
|
263
|
+
depends_on: [],
|
|
264
|
+
aliases: ["design"],
|
|
265
|
+
is_active: true,
|
|
266
|
+
is_required: false,
|
|
267
|
+
install_order: 101,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "biz-management",
|
|
271
|
+
name: "biz/management",
|
|
272
|
+
display_name: "Management",
|
|
273
|
+
description: "일정/인력/스프린트 관리",
|
|
274
|
+
layer: "biz",
|
|
275
|
+
package_type: "extension",
|
|
276
|
+
version: "1.0.0",
|
|
277
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
278
|
+
source_path: "semo-system/biz/management",
|
|
279
|
+
detect_files: [],
|
|
280
|
+
depends_on: [],
|
|
281
|
+
aliases: ["management"],
|
|
282
|
+
is_active: true,
|
|
283
|
+
is_required: false,
|
|
284
|
+
install_order: 102,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: "biz-poc",
|
|
288
|
+
name: "biz/poc",
|
|
289
|
+
display_name: "PoC",
|
|
290
|
+
description: "빠른 PoC, 패스트트랙",
|
|
291
|
+
layer: "biz",
|
|
292
|
+
package_type: "extension",
|
|
293
|
+
version: "1.0.0",
|
|
294
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
295
|
+
source_path: "semo-system/biz/poc",
|
|
296
|
+
detect_files: [],
|
|
297
|
+
depends_on: [],
|
|
298
|
+
aliases: ["poc", "mvp"],
|
|
299
|
+
is_active: true,
|
|
300
|
+
is_required: false,
|
|
301
|
+
install_order: 103,
|
|
302
|
+
},
|
|
303
|
+
// Engineering Layer
|
|
304
|
+
{
|
|
305
|
+
id: "eng-nextjs",
|
|
306
|
+
name: "eng/nextjs",
|
|
307
|
+
display_name: "Next.js",
|
|
308
|
+
description: "Next.js 프론트엔드 개발",
|
|
309
|
+
layer: "eng",
|
|
310
|
+
package_type: "extension",
|
|
311
|
+
version: "1.0.0",
|
|
312
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
313
|
+
source_path: "semo-system/eng/nextjs",
|
|
314
|
+
detect_files: ["next.config.js", "next.config.mjs", "next.config.ts"],
|
|
315
|
+
depends_on: [],
|
|
316
|
+
aliases: ["nextjs", "next"],
|
|
317
|
+
is_active: true,
|
|
318
|
+
is_required: false,
|
|
319
|
+
install_order: 110,
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
id: "eng-spring",
|
|
323
|
+
name: "eng/spring",
|
|
324
|
+
display_name: "Spring",
|
|
325
|
+
description: "Spring Boot 백엔드 개발",
|
|
326
|
+
layer: "eng",
|
|
327
|
+
package_type: "extension",
|
|
328
|
+
version: "1.0.0",
|
|
329
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
330
|
+
source_path: "semo-system/eng/spring",
|
|
331
|
+
detect_files: ["pom.xml", "build.gradle"],
|
|
332
|
+
depends_on: [],
|
|
333
|
+
aliases: ["spring", "backend"],
|
|
334
|
+
is_active: true,
|
|
335
|
+
is_required: false,
|
|
336
|
+
install_order: 111,
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "eng-ms",
|
|
340
|
+
name: "eng/ms",
|
|
341
|
+
display_name: "Microservice",
|
|
342
|
+
description: "마이크로서비스 아키텍처",
|
|
343
|
+
layer: "eng",
|
|
344
|
+
package_type: "extension",
|
|
345
|
+
version: "1.0.0",
|
|
346
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
347
|
+
source_path: "semo-system/eng/ms",
|
|
348
|
+
detect_files: [],
|
|
349
|
+
depends_on: [],
|
|
350
|
+
aliases: ["ms"],
|
|
351
|
+
is_active: true,
|
|
352
|
+
is_required: false,
|
|
353
|
+
install_order: 112,
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
id: "eng-infra",
|
|
357
|
+
name: "eng/infra",
|
|
358
|
+
display_name: "Infra",
|
|
359
|
+
description: "인프라/배포 관리",
|
|
360
|
+
layer: "eng",
|
|
361
|
+
package_type: "extension",
|
|
362
|
+
version: "1.0.0",
|
|
363
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
364
|
+
source_path: "semo-system/eng/infra",
|
|
365
|
+
detect_files: ["docker-compose.yml", "Dockerfile"],
|
|
366
|
+
depends_on: [],
|
|
367
|
+
aliases: ["infra"],
|
|
368
|
+
is_active: true,
|
|
369
|
+
is_required: false,
|
|
370
|
+
install_order: 113,
|
|
371
|
+
},
|
|
372
|
+
// Operations Layer
|
|
373
|
+
{
|
|
374
|
+
id: "ops-qa",
|
|
375
|
+
name: "ops/qa",
|
|
376
|
+
display_name: "QA",
|
|
377
|
+
description: "테스트/품질 관리",
|
|
378
|
+
layer: "ops",
|
|
379
|
+
package_type: "extension",
|
|
380
|
+
version: "1.0.0",
|
|
381
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
382
|
+
source_path: "semo-system/ops/qa",
|
|
383
|
+
detect_files: [],
|
|
384
|
+
depends_on: [],
|
|
385
|
+
aliases: ["qa"],
|
|
386
|
+
is_active: true,
|
|
387
|
+
is_required: false,
|
|
388
|
+
install_order: 120,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
id: "ops-monitor",
|
|
392
|
+
name: "ops/monitor",
|
|
393
|
+
display_name: "Monitor",
|
|
394
|
+
description: "서비스 현황 모니터링",
|
|
395
|
+
layer: "ops",
|
|
396
|
+
package_type: "extension",
|
|
397
|
+
version: "1.0.0",
|
|
398
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
399
|
+
source_path: "semo-system/ops/monitor",
|
|
400
|
+
detect_files: [],
|
|
401
|
+
depends_on: [],
|
|
402
|
+
aliases: ["monitor"],
|
|
403
|
+
is_active: true,
|
|
404
|
+
is_required: false,
|
|
405
|
+
install_order: 121,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
id: "ops-improve",
|
|
409
|
+
name: "ops/improve",
|
|
410
|
+
display_name: "Improve",
|
|
411
|
+
description: "개선 제안",
|
|
412
|
+
layer: "ops",
|
|
413
|
+
package_type: "extension",
|
|
414
|
+
version: "1.0.0",
|
|
415
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
416
|
+
source_path: "semo-system/ops/improve",
|
|
417
|
+
detect_files: [],
|
|
418
|
+
depends_on: [],
|
|
419
|
+
aliases: ["improve"],
|
|
420
|
+
is_active: true,
|
|
421
|
+
is_required: false,
|
|
422
|
+
install_order: 122,
|
|
423
|
+
},
|
|
424
|
+
// Meta Layer
|
|
425
|
+
{
|
|
426
|
+
id: "meta",
|
|
427
|
+
name: "meta",
|
|
428
|
+
display_name: "Meta",
|
|
429
|
+
description: "SEMO 프레임워크 자체 개발/관리",
|
|
430
|
+
layer: "meta",
|
|
431
|
+
package_type: "extension",
|
|
432
|
+
version: "3.12.0",
|
|
433
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
434
|
+
source_path: "semo-system/meta",
|
|
435
|
+
detect_files: ["semo-core", "semo-skills"],
|
|
436
|
+
depends_on: [],
|
|
437
|
+
aliases: [],
|
|
438
|
+
is_active: true,
|
|
439
|
+
is_required: false,
|
|
440
|
+
install_order: 200,
|
|
441
|
+
},
|
|
442
|
+
// System Layer
|
|
443
|
+
{
|
|
444
|
+
id: "sys-hooks",
|
|
445
|
+
name: "semo-hooks",
|
|
446
|
+
display_name: "Hooks",
|
|
447
|
+
description: "Claude Code Hooks 기반 로깅 시스템",
|
|
448
|
+
layer: "system",
|
|
449
|
+
package_type: "extension",
|
|
450
|
+
version: "1.0.0",
|
|
451
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
452
|
+
source_path: "semo-system/semo-hooks",
|
|
453
|
+
detect_files: [],
|
|
454
|
+
depends_on: [],
|
|
455
|
+
aliases: ["hooks"],
|
|
456
|
+
is_active: true,
|
|
457
|
+
is_required: false,
|
|
458
|
+
install_order: 210,
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
id: "sys-remote",
|
|
462
|
+
name: "semo-remote",
|
|
463
|
+
display_name: "Remote",
|
|
464
|
+
description: "Claude Code 원격 제어 (모바일 PWA)",
|
|
465
|
+
layer: "system",
|
|
466
|
+
package_type: "extension",
|
|
467
|
+
version: "1.0.0",
|
|
468
|
+
repo_url: "https://github.com/semicolon-devteam/semo.git",
|
|
469
|
+
source_path: "semo-system/semo-remote",
|
|
470
|
+
detect_files: [],
|
|
471
|
+
depends_on: [],
|
|
472
|
+
aliases: ["remote"],
|
|
473
|
+
is_active: true,
|
|
474
|
+
is_required: false,
|
|
475
|
+
install_order: 211,
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
function getFallbackPackages(layer) {
|
|
479
|
+
if (layer) {
|
|
480
|
+
return FALLBACK_PACKAGES.filter((p) => p.layer === layer);
|
|
481
|
+
}
|
|
482
|
+
return FALLBACK_PACKAGES;
|
|
483
|
+
}
|
|
484
|
+
function resolveFallbackPackageName(input) {
|
|
485
|
+
// 1. 정확한 이름 매칭
|
|
486
|
+
const exactMatch = FALLBACK_PACKAGES.find((p) => p.name === input);
|
|
487
|
+
if (exactMatch)
|
|
488
|
+
return exactMatch.name;
|
|
489
|
+
// 2. 별칭 매칭
|
|
490
|
+
const aliasMatch = FALLBACK_PACKAGES.find((p) => p.aliases.includes(input));
|
|
491
|
+
if (aliasMatch)
|
|
492
|
+
return aliasMatch.name;
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
// ============================================================
|
|
496
|
+
// 유틸리티
|
|
497
|
+
// ============================================================
|
|
498
|
+
/**
|
|
499
|
+
* 패키지 정보를 CLI 포맷으로 변환
|
|
500
|
+
*/
|
|
501
|
+
function toExtensionPackageFormat(pkg) {
|
|
502
|
+
return {
|
|
503
|
+
name: pkg.display_name,
|
|
504
|
+
desc: pkg.description || "",
|
|
505
|
+
detect: pkg.detect_files,
|
|
506
|
+
layer: pkg.layer,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* 별칭 매핑 객체 생성 (SHORTNAME_MAPPING 대체)
|
|
511
|
+
*/
|
|
512
|
+
async function buildShortnameMappingFromDb() {
|
|
513
|
+
const packages = await getPackages();
|
|
514
|
+
const mapping = {};
|
|
515
|
+
for (const pkg of packages) {
|
|
516
|
+
for (const alias of pkg.aliases) {
|
|
517
|
+
mapping[alias] = pkg.name;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return mapping;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* EXTENSION_PACKAGES 형태의 객체 생성 (호환성용)
|
|
524
|
+
*/
|
|
525
|
+
async function buildExtensionPackagesFromDb() {
|
|
526
|
+
const packages = await getExtensionPackages();
|
|
527
|
+
const result = {};
|
|
528
|
+
for (const pkg of packages) {
|
|
529
|
+
result[pkg.name] = toExtensionPackageFormat(pkg);
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-semicolon/semo-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "SEMO CLI - AI Agent Orchestration Framework Installer",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"directory": "packages/cli"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@supabase/supabase-js": "^2.49.1",
|
|
30
31
|
"chalk": "^4.1.2",
|
|
31
32
|
"commander": "^12.0.0",
|
|
32
33
|
"ora": "^5.4.1",
|