@team-semicolon/semo-cli 3.0.14 → 3.0.16
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 +148 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -103,6 +103,134 @@ function isVersionLower(current, latest) {
|
|
|
103
103
|
return true;
|
|
104
104
|
return false;
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* GitHub raw URL에서 패키지 버전 가져오기
|
|
108
|
+
*/
|
|
109
|
+
async function getRemotePackageVersion(packagePath) {
|
|
110
|
+
try {
|
|
111
|
+
const url = `https://raw.githubusercontent.com/semicolon-devteam/semo/main/packages/${packagePath}/VERSION`;
|
|
112
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
113
|
+
if (!response.ok)
|
|
114
|
+
return null;
|
|
115
|
+
const version = await response.text();
|
|
116
|
+
return version.trim();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* semo-core/semo-skills 원격 버전 가져오기
|
|
124
|
+
*/
|
|
125
|
+
async function getRemoteCoreVersion(type) {
|
|
126
|
+
try {
|
|
127
|
+
const url = `https://raw.githubusercontent.com/semicolon-devteam/semo/main/${type}/VERSION`;
|
|
128
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
129
|
+
if (!response.ok)
|
|
130
|
+
return null;
|
|
131
|
+
const version = await response.text();
|
|
132
|
+
return version.trim();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* init/update 시작 시 버전 비교 결과 출력
|
|
140
|
+
*/
|
|
141
|
+
async function showVersionComparison(cwd) {
|
|
142
|
+
console.log(chalk_1.default.cyan("📊 버전 확인\n"));
|
|
143
|
+
const spinner = (0, ora_1.default)(" 버전 정보 조회 중...").start();
|
|
144
|
+
try {
|
|
145
|
+
// 1. CLI 버전 비교
|
|
146
|
+
const currentCliVersion = VERSION;
|
|
147
|
+
const latestCliVersion = await getLatestVersion();
|
|
148
|
+
// 2. semo-core, semo-skills 버전 비교
|
|
149
|
+
const semoSystemDir = path.join(cwd, "semo-system");
|
|
150
|
+
const hasSemoSystem = fs.existsSync(semoSystemDir);
|
|
151
|
+
const versionInfos = [];
|
|
152
|
+
// CLI
|
|
153
|
+
versionInfos.push({
|
|
154
|
+
name: "semo-cli (npm)",
|
|
155
|
+
local: currentCliVersion,
|
|
156
|
+
remote: latestCliVersion,
|
|
157
|
+
needsUpdate: latestCliVersion ? isVersionLower(currentCliVersion, latestCliVersion) : false,
|
|
158
|
+
});
|
|
159
|
+
if (hasSemoSystem) {
|
|
160
|
+
// semo-core는 semo-system 바깥에 있음
|
|
161
|
+
const corePath = path.join(cwd, "semo-core", "VERSION");
|
|
162
|
+
const localCore = fs.existsSync(corePath) ? fs.readFileSync(corePath, "utf-8").trim() : null;
|
|
163
|
+
const remoteCore = await getRemoteCoreVersion("semo-core");
|
|
164
|
+
if (localCore) {
|
|
165
|
+
versionInfos.push({
|
|
166
|
+
name: "semo-core",
|
|
167
|
+
local: localCore,
|
|
168
|
+
remote: remoteCore,
|
|
169
|
+
needsUpdate: remoteCore ? isVersionLower(localCore, remoteCore) : false,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// semo-skills
|
|
173
|
+
const skillsPath = path.join(cwd, "semo-skills", "VERSION");
|
|
174
|
+
const localSkills = fs.existsSync(skillsPath) ? fs.readFileSync(skillsPath, "utf-8").trim() : null;
|
|
175
|
+
const remoteSkills = await getRemoteCoreVersion("semo-skills");
|
|
176
|
+
if (localSkills) {
|
|
177
|
+
versionInfos.push({
|
|
178
|
+
name: "semo-skills",
|
|
179
|
+
local: localSkills,
|
|
180
|
+
remote: remoteSkills,
|
|
181
|
+
needsUpdate: remoteSkills ? isVersionLower(localSkills, remoteSkills) : false,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
// Extensions (semo-system 내부)
|
|
185
|
+
for (const key of Object.keys(EXTENSION_PACKAGES)) {
|
|
186
|
+
const extVersionPath = path.join(semoSystemDir, key, "VERSION");
|
|
187
|
+
if (fs.existsSync(extVersionPath)) {
|
|
188
|
+
const localExt = fs.readFileSync(extVersionPath, "utf-8").trim();
|
|
189
|
+
const remoteExt = await getRemotePackageVersion(key);
|
|
190
|
+
versionInfos.push({
|
|
191
|
+
name: key,
|
|
192
|
+
local: localExt,
|
|
193
|
+
remote: remoteExt,
|
|
194
|
+
needsUpdate: remoteExt ? isVersionLower(localExt, remoteExt) : false,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
spinner.stop();
|
|
200
|
+
// 결과 출력
|
|
201
|
+
const needsUpdateCount = versionInfos.filter(v => v.needsUpdate).length;
|
|
202
|
+
console.log(chalk_1.default.gray(" ┌────────────────────────┬──────────┬──────────┬────────┐"));
|
|
203
|
+
console.log(chalk_1.default.gray(" │ 패키지 │ 설치됨 │ 최신 │ 상태 │"));
|
|
204
|
+
console.log(chalk_1.default.gray(" ├────────────────────────┼──────────┼──────────┼────────┤"));
|
|
205
|
+
for (const info of versionInfos) {
|
|
206
|
+
const name = info.name.padEnd(22);
|
|
207
|
+
const local = (info.local || "-").padEnd(8);
|
|
208
|
+
const remote = (info.remote || "-").padEnd(8);
|
|
209
|
+
const status = info.needsUpdate
|
|
210
|
+
? chalk_1.default.yellow("⬆ 업데이트")
|
|
211
|
+
: chalk_1.default.green("✓ 최신");
|
|
212
|
+
if (info.needsUpdate) {
|
|
213
|
+
console.log(chalk_1.default.yellow(` │ ${name} │ ${local} │ ${remote} │ ${status} │`));
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
console.log(chalk_1.default.gray(` │ ${name} │ ${local} │ ${remote} │ `) + status + chalk_1.default.gray(" │"));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
console.log(chalk_1.default.gray(" └────────────────────────┴──────────┴──────────┴────────┘"));
|
|
220
|
+
if (needsUpdateCount > 0) {
|
|
221
|
+
console.log(chalk_1.default.yellow(`\n ⚠ ${needsUpdateCount}개 패키지 업데이트 가능`));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(chalk_1.default.green("\n ✓ 모든 패키지가 최신 버전입니다"));
|
|
225
|
+
}
|
|
226
|
+
console.log("");
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
spinner.fail(" 버전 정보 조회 실패");
|
|
230
|
+
console.log(chalk_1.default.gray(` ${error}`));
|
|
231
|
+
console.log("");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
106
234
|
// === Windows 지원 유틸리티 ===
|
|
107
235
|
const isWindows = os.platform() === "win32";
|
|
108
236
|
/**
|
|
@@ -110,6 +238,21 @@ const isWindows = os.platform() === "win32";
|
|
|
110
238
|
* Junction은 관리자 권한 없이 디렉토리 링크를 생성할 수 있음
|
|
111
239
|
*/
|
|
112
240
|
function createSymlinkOrJunction(targetPath, linkPath) {
|
|
241
|
+
// 이미 존재하는 링크/파일 제거 (깨진 심볼릭 링크도 처리)
|
|
242
|
+
try {
|
|
243
|
+
const stats = fs.lstatSync(linkPath);
|
|
244
|
+
if (stats.isSymbolicLink() || stats.isFile() || stats.isDirectory()) {
|
|
245
|
+
try {
|
|
246
|
+
fs.unlinkSync(linkPath);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
removeRecursive(linkPath);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// 파일이 존재하지 않음 - 정상
|
|
255
|
+
}
|
|
113
256
|
if (isWindows) {
|
|
114
257
|
// Windows: Junction 사용 (절대 경로 필요)
|
|
115
258
|
const absoluteTarget = path.resolve(targetPath);
|
|
@@ -405,7 +548,9 @@ program
|
|
|
405
548
|
console.log(chalk_1.default.cyan.bold("\n🚀 SEMO 설치 시작\n"));
|
|
406
549
|
console.log(chalk_1.default.gray("Gemini 하이브리드 전략: White Box + Black Box\n"));
|
|
407
550
|
const cwd = process.cwd();
|
|
408
|
-
// 0.
|
|
551
|
+
// 0. 버전 비교
|
|
552
|
+
await showVersionComparison(cwd);
|
|
553
|
+
// 1. 필수 도구 확인
|
|
409
554
|
const shouldContinue = await showToolsStatus();
|
|
410
555
|
if (!shouldContinue) {
|
|
411
556
|
console.log(chalk_1.default.yellow("\n설치가 취소되었습니다. 필수 도구 설치 후 다시 시도하세요.\n"));
|
|
@@ -1957,6 +2102,8 @@ program
|
|
|
1957
2102
|
const cwd = process.cwd();
|
|
1958
2103
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
1959
2104
|
const claudeDir = path.join(cwd, ".claude");
|
|
2105
|
+
// 0. 버전 비교
|
|
2106
|
+
await showVersionComparison(cwd);
|
|
1960
2107
|
// --only 옵션 파싱
|
|
1961
2108
|
const onlyPackages = options.only
|
|
1962
2109
|
? options.only.split(",").map((p) => p.trim())
|