@team-semicolon/semo-cli 3.7.3 → 3.8.1

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 +202 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1163,24 +1163,42 @@ async function createStandardSymlinks(cwd) {
1163
1163
  }
1164
1164
  console.log(chalk_1.default.green(` ✓ .claude/skills/ (${skills.length}개 skill 링크됨)`));
1165
1165
  }
1166
- // commands 링크
1166
+ // commands 링크 (개별 파일 심볼릭 링크 방식 - 여러 패키지 지원)
1167
1167
  const commandsDir = path.join(claudeDir, "commands");
1168
1168
  fs.mkdirSync(commandsDir, { recursive: true });
1169
- const semoCommandsLink = path.join(commandsDir, "SEMO");
1170
- const commandsTarget = path.join(semoSystemDir, "semo-core", "commands", "SEMO");
1171
- // 기존 링크가 있으면 삭제 후 재생성 (업데이트 시에도 최신 반영)
1172
- if (fs.existsSync(semoCommandsLink)) {
1173
- if (fs.lstatSync(semoCommandsLink).isSymbolicLink()) {
1174
- fs.unlinkSync(semoCommandsLink);
1169
+ const semoCommandsDir = path.join(commandsDir, "SEMO");
1170
+ // 기존 링크/디렉토리가 있으면 삭제 재생성
1171
+ if (fs.existsSync(semoCommandsDir)) {
1172
+ if (fs.lstatSync(semoCommandsDir).isSymbolicLink()) {
1173
+ fs.unlinkSync(semoCommandsDir);
1175
1174
  }
1176
1175
  else {
1177
- removeRecursive(semoCommandsLink);
1176
+ removeRecursive(semoCommandsDir);
1178
1177
  }
1179
1178
  }
1180
- if (fs.existsSync(commandsTarget)) {
1181
- createSymlinkOrJunction(commandsTarget, semoCommandsLink);
1182
- console.log(chalk_1.default.green(" ✓ .claude/commands/SEMO semo-system/semo-core/commands/SEMO"));
1179
+ // SEMO 커맨드 디렉토리 생성 (실제 디렉토리)
1180
+ fs.mkdirSync(semoCommandsDir, { recursive: true });
1181
+ // 커맨드 소스 디렉토리 목록 (semo-core + semo-remote)
1182
+ const commandSources = [
1183
+ { name: "semo-core", dir: path.join(semoSystemDir, "semo-core", "commands", "SEMO") },
1184
+ { name: "semo-remote", dir: path.join(semoSystemDir, "semo-remote", "commands", "SEMO") },
1185
+ ];
1186
+ let totalCommands = 0;
1187
+ for (const source of commandSources) {
1188
+ if (fs.existsSync(source.dir)) {
1189
+ const commandFiles = fs.readdirSync(source.dir).filter(f => f.endsWith(".md"));
1190
+ for (const cmdFile of commandFiles) {
1191
+ const cmdTarget = path.join(source.dir, cmdFile);
1192
+ const cmdLink = path.join(semoCommandsDir, cmdFile);
1193
+ // 이미 링크가 있으면 건너뛰기 (semo-core 우선)
1194
+ if (!fs.existsSync(cmdLink)) {
1195
+ createSymlinkOrJunction(cmdTarget, cmdLink);
1196
+ totalCommands++;
1197
+ }
1198
+ }
1199
+ }
1183
1200
  }
1201
+ console.log(chalk_1.default.green(` ✓ .claude/commands/SEMO (${totalCommands}개 command 링크됨)`));
1184
1202
  }
1185
1203
  /**
1186
1204
  * 설치 상태를 검증하고 문제점을 리포트
@@ -1261,15 +1279,26 @@ function verifyInstallation(cwd, installedExtensions = []) {
1261
1279
  }
1262
1280
  }
1263
1281
  }
1264
- // 4. commands 검증 (isSymlinkValid 사용)
1265
- const semoCommandsLink = path.join(claudeDir, "commands", "SEMO");
1282
+ // 4. commands 검증 (디렉토리 또는 개별 파일 심볼릭 링크 방식)
1283
+ const semoCommandsDir = path.join(claudeDir, "commands", "SEMO");
1266
1284
  try {
1267
- const linkExists = fs.existsSync(semoCommandsLink) || fs.lstatSync(semoCommandsLink).isSymbolicLink();
1268
- result.stats.commands.exists = linkExists;
1269
- if (linkExists) {
1270
- result.stats.commands.valid = isSymlinkValid(semoCommandsLink);
1271
- if (!result.stats.commands.valid) {
1272
- result.warnings.push("깨진 링크: .claude/commands/SEMO");
1285
+ const dirExists = fs.existsSync(semoCommandsDir);
1286
+ result.stats.commands.exists = dirExists;
1287
+ if (dirExists) {
1288
+ // 디렉토리인 경우: 내부 파일 검증
1289
+ if (fs.lstatSync(semoCommandsDir).isDirectory() && !fs.lstatSync(semoCommandsDir).isSymbolicLink()) {
1290
+ const cmdFiles = fs.readdirSync(semoCommandsDir).filter(f => f.endsWith(".md"));
1291
+ result.stats.commands.valid = cmdFiles.length > 0;
1292
+ if (!result.stats.commands.valid) {
1293
+ result.warnings.push("commands/SEMO 디렉토리가 비어 있습니다");
1294
+ }
1295
+ }
1296
+ else if (fs.lstatSync(semoCommandsDir).isSymbolicLink()) {
1297
+ // 심볼릭 링크인 경우 (구버전 호환)
1298
+ result.stats.commands.valid = isSymlinkValid(semoCommandsDir);
1299
+ if (!result.stats.commands.valid) {
1300
+ result.warnings.push("깨진 링크: .claude/commands/SEMO");
1301
+ }
1273
1302
  }
1274
1303
  }
1275
1304
  }
@@ -2968,9 +2997,71 @@ program
2968
2997
  }
2969
2998
  }
2970
2999
  }
2971
- // === 6. Hooks 업데이트 ===
2972
- await setupHooks(cwd, true);
2973
- // === 7. 설치 검증 ===
3000
+ // === 6. semo-hooks 체크 및 업데이트 ===
3001
+ console.log(chalk_1.default.cyan("\n🪝 semo-hooks 상태 확인"));
3002
+ const hooksDir = path.join(semoSystemDir, "semo-hooks");
3003
+ if (!fs.existsSync(hooksDir)) {
3004
+ console.log(chalk_1.default.gray(" ⏭️ semo-hooks 미설치 (선택 패키지)"));
3005
+ console.log(chalk_1.default.gray(" 💡 설치: semo add hooks"));
3006
+ }
3007
+ else {
3008
+ const hooksVersionPath = path.join(hooksDir, "VERSION");
3009
+ const hooksVersion = fs.existsSync(hooksVersionPath)
3010
+ ? fs.readFileSync(hooksVersionPath, "utf-8").trim()
3011
+ : "?";
3012
+ console.log(chalk_1.default.green(` ✓ semo-hooks v${hooksVersion} 설치됨`));
3013
+ // hooks 빌드 및 설정 업데이트
3014
+ await setupHooks(cwd, true);
3015
+ }
3016
+ // === 7. semo-mcp 체크 ===
3017
+ console.log(chalk_1.default.cyan("\n📡 semo-mcp 상태 확인"));
3018
+ const userHomeDir2 = process.env.HOME || process.env.USERPROFILE || "";
3019
+ const claudeSettingsPath = path.join(userHomeDir2, ".claude", "settings.local.json");
3020
+ if (!fs.existsSync(claudeSettingsPath)) {
3021
+ console.log(chalk_1.default.gray(" ⏭️ MCP 설정 없음 (선택사항)"));
3022
+ console.log(chalk_1.default.gray(" 💡 장기 기억이 필요하면 settings.local.json 설정 추가"));
3023
+ }
3024
+ else {
3025
+ try {
3026
+ const settings = JSON.parse(fs.readFileSync(claudeSettingsPath, "utf-8"));
3027
+ const mcpServers = settings.mcpServers || {};
3028
+ const semoMcp = mcpServers["semo-integrations"];
3029
+ if (!semoMcp) {
3030
+ console.log(chalk_1.default.gray(" ⏭️ semo-integrations 미등록 (선택사항)"));
3031
+ console.log(chalk_1.default.gray(" 💡 장기 기억/원격 제어가 필요하면 MCP 설정 추가"));
3032
+ }
3033
+ else {
3034
+ console.log(chalk_1.default.green(" ✓ semo-integrations MCP 서버 등록됨"));
3035
+ // v3.0: SEMO_DB_PASSWORD만 체크
3036
+ const env = semoMcp.env || {};
3037
+ if (env["SEMO_DB_PASSWORD"]) {
3038
+ console.log(chalk_1.default.green(" ✓ 장기 기억: 활성화"));
3039
+ }
3040
+ else {
3041
+ console.log(chalk_1.default.gray(" ⏭️ 장기 기억: 비활성화 (SEMO_DB_PASSWORD 미설정)"));
3042
+ }
3043
+ }
3044
+ }
3045
+ catch {
3046
+ console.log(chalk_1.default.yellow(" ⚠ settings.local.json 파싱 오류"));
3047
+ }
3048
+ }
3049
+ // CLI 도구 체크 (v3.0: 스킬에서 CLI 직접 호출)
3050
+ console.log(chalk_1.default.cyan("\n🔧 CLI 도구 확인"));
3051
+ const cliToolsUpdate = [
3052
+ { name: "gh", desc: "GitHub CLI" },
3053
+ { name: "supabase", desc: "Supabase CLI" },
3054
+ ];
3055
+ for (const tool of cliToolsUpdate) {
3056
+ try {
3057
+ (0, child_process_1.execSync)(`${tool.name} --version`, { stdio: ["pipe", "pipe", "pipe"] });
3058
+ console.log(chalk_1.default.green(` ✓ ${tool.name} 설치됨`));
3059
+ }
3060
+ catch {
3061
+ console.log(chalk_1.default.yellow(` ⚠ ${tool.name} 미설치 (${tool.desc})`));
3062
+ }
3063
+ }
3064
+ // === 8. 설치 검증 ===
2974
3065
  const verificationResult = verifyInstallation(cwd, installedExtensions);
2975
3066
  printVerificationResult(verificationResult);
2976
3067
  if (verificationResult.success) {
@@ -3179,8 +3270,95 @@ program
3179
3270
  }
3180
3271
  }
3181
3272
  }
3182
- // 5. 전체 설치 검증
3183
- console.log(chalk_1.default.cyan("\n5. 전체 설치 검증"));
3273
+ // 5. semo-mcp (MCP 서버) 상태 확인
3274
+ // v3.0: semo-mcp는 Memory + Remote만 제공 (선택사항)
3275
+ // Slack/GitHub/Supabase는 스킬에서 CLI 직접 호출
3276
+ console.log(chalk_1.default.cyan("\n5. semo-mcp (MCP 서버) - 선택사항"));
3277
+ const userHomeDir = process.env.HOME || process.env.USERPROFILE || "";
3278
+ const claudeSettingsPath = path.join(userHomeDir, ".claude", "settings.local.json");
3279
+ // MCP 서버 설정 확인
3280
+ if (!fs.existsSync(claudeSettingsPath)) {
3281
+ console.log(chalk_1.default.gray(" ⏭️ settings.local.json 없음 (MCP 미사용)"));
3282
+ console.log(chalk_1.default.gray(" 💡 장기 기억이 필요하면 semo-mcp 설정 추가"));
3283
+ }
3284
+ else {
3285
+ try {
3286
+ const settings = JSON.parse(fs.readFileSync(claudeSettingsPath, "utf-8"));
3287
+ const mcpServers = settings.mcpServers || {};
3288
+ // semo-integrations MCP 서버 확인
3289
+ const semoMcp = mcpServers["semo-integrations"];
3290
+ if (!semoMcp) {
3291
+ console.log(chalk_1.default.gray(" ⏭️ semo-integrations 미등록 (선택사항)"));
3292
+ console.log(chalk_1.default.gray(" 💡 장기 기억/원격 제어가 필요하면 MCP 설정 추가"));
3293
+ }
3294
+ else {
3295
+ console.log(chalk_1.default.green(" ✅ semo-integrations MCP 서버 등록됨"));
3296
+ // 명령어 경로 확인
3297
+ const mcpCommand = semoMcp.command || "";
3298
+ const mcpArgs = semoMcp.args || [];
3299
+ if (mcpCommand === "npx") {
3300
+ console.log(chalk_1.default.green(" ✅ npx 방식 실행 (자동 업데이트)"));
3301
+ }
3302
+ else if (mcpCommand === "node") {
3303
+ const scriptPath = mcpArgs[0] || "";
3304
+ if (scriptPath && fs.existsSync(scriptPath)) {
3305
+ console.log(chalk_1.default.green(` ✅ 로컬 스크립트: ${scriptPath}`));
3306
+ }
3307
+ else if (scriptPath) {
3308
+ console.log(chalk_1.default.red(` ❌ 스크립트 경로 없음: ${scriptPath}`));
3309
+ }
3310
+ }
3311
+ // v3.0: 환경변수 체크 - SEMO_DB_PASSWORD만 확인 (장기 기억용)
3312
+ const env = semoMcp.env || {};
3313
+ if (env["SEMO_DB_PASSWORD"]) {
3314
+ console.log(chalk_1.default.green(" ✅ 장기 기억: 활성화 (SEMO_DB_PASSWORD 설정됨)"));
3315
+ }
3316
+ else {
3317
+ console.log(chalk_1.default.gray(" ⏭️ 장기 기억: 비활성화"));
3318
+ console.log(chalk_1.default.gray(" 💡 SEMO_DB_PASSWORD 설정 시 활성화"));
3319
+ }
3320
+ // v3.0: 도구 목록 업데이트 (Memory + Remote만)
3321
+ const v3Tools = [
3322
+ // Memory
3323
+ "semo_remember", "semo_recall", "semo_save_fact",
3324
+ "semo_get_facts", "semo_get_history", "semo_memory_status",
3325
+ "semo_process_embeddings", "semo_recall_smart",
3326
+ // Remote
3327
+ "semo_remote_request", "semo_remote_respond", "semo_remote_pending",
3328
+ ];
3329
+ console.log(chalk_1.default.gray(` 📦 제공 도구: ${v3Tools.length}개 (Memory ${8}, Remote ${3})`));
3330
+ }
3331
+ // 다른 MCP 서버 확인
3332
+ const otherServers = Object.keys(mcpServers).filter(k => k !== "semo-integrations");
3333
+ if (otherServers.length > 0) {
3334
+ console.log(chalk_1.default.gray(` 📡 기타 MCP 서버: ${otherServers.join(", ")}`));
3335
+ }
3336
+ }
3337
+ catch {
3338
+ console.log(chalk_1.default.red(" ❌ settings.local.json 파싱 오류"));
3339
+ }
3340
+ }
3341
+ // 5-1. CLI 도구 확인 (v3.0: 스킬에서 CLI 직접 호출)
3342
+ console.log(chalk_1.default.cyan("\n5-1. CLI 도구 (Skill용)"));
3343
+ const cliTools = [
3344
+ { name: "gh", desc: "GitHub CLI", check: "gh --version" },
3345
+ { name: "supabase", desc: "Supabase CLI", check: "supabase --version" },
3346
+ ];
3347
+ for (const tool of cliTools) {
3348
+ try {
3349
+ const { execSync } = require("child_process");
3350
+ const version = execSync(tool.check, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0];
3351
+ console.log(chalk_1.default.green(` ✅ ${tool.name}: ${version}`));
3352
+ }
3353
+ catch {
3354
+ console.log(chalk_1.default.yellow(` ⚠️ ${tool.name} 미설치 (${tool.desc})`));
3355
+ console.log(chalk_1.default.gray(` 💡 일부 스킬 기능이 제한될 수 있습니다`));
3356
+ }
3357
+ }
3358
+ // Slack은 curl로 호출하므로 별도 체크 불필요
3359
+ console.log(chalk_1.default.gray(" ℹ️ Slack: curl 사용 (별도 CLI 불필요)"));
3360
+ // 6. 전체 설치 검증
3361
+ console.log(chalk_1.default.cyan("\n6. 전체 설치 검증"));
3184
3362
  const verificationResult = verifyInstallation(cwd, []);
3185
3363
  if (verificationResult.success) {
3186
3364
  console.log(chalk_1.default.green(" ✅ 설치 상태 정상"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semo-cli",
3
- "version": "3.7.3",
3
+ "version": "3.8.1",
4
4
  "description": "SEMO CLI - AI Agent Orchestration Framework Installer",
5
5
  "main": "dist/index.js",
6
6
  "bin": {