@team-semicolon/semo-cli 3.13.1 → 3.14.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.
package/dist/index.js CHANGED
@@ -59,7 +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
+ const database_1 = require("./database");
63
63
  const PACKAGE_NAME = "@team-semicolon/semo-cli";
64
64
  // package.json에서 버전 동적 로드
65
65
  function getCliVersion() {
@@ -675,27 +675,20 @@ function copyRecursive(src, dest) {
675
675
  }
676
676
  const SEMO_REPO = "https://github.com/semicolon-devteam/semo.git";
677
677
  // ============================================================
678
- // DB 기반 패키지 관리 (v3.10+)
678
+ // 패키지 관리 (v3.14.0 - 폴백 데이터 사용)
679
679
  // ============================================================
680
- // 캐시된 패키지 데이터 (DB에서 조회 캐시)
680
+ // v3.14.0: Extensions는 아직 git 기반이므로 폴백 데이터 직접 사용
681
+ // 향후 Extensions도 DB 기반으로 전환 예정
682
+ // 캐시된 패키지 데이터
681
683
  let cachedExtensionPackages = null;
682
684
  let cachedShortnameMappings = null;
683
- let cachedPackageDefinitions = null;
684
- // 패키지 데이터 초기화 (DB에서 조회)
685
+ // 패키지 데이터 초기화 (폴백 데이터 사용)
685
686
  async function initPackageData() {
686
687
  if (cachedExtensionPackages && cachedShortnameMappings)
687
688
  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
- }
689
+ // v3.14.0: Extensions는 아직 git 기반이므로 폴백 데이터 사용
690
+ cachedExtensionPackages = EXTENSION_PACKAGES_FALLBACK;
691
+ cachedShortnameMappings = SHORTNAME_MAPPING_FALLBACK;
699
692
  }
700
693
  // EXTENSION_PACKAGES 동기 접근용 (초기화 후 사용)
701
694
  function getExtensionPackagesSync() {
@@ -754,10 +747,10 @@ const EXTENSION_PACKAGES = EXTENSION_PACKAGES_FALLBACK;
754
747
  const SHORTNAME_MAPPING = SHORTNAME_MAPPING_FALLBACK;
755
748
  // 그룹 이름 목록 (biz, eng, ops, meta, system)
756
749
  const PACKAGE_GROUPS = ["biz", "eng", "ops", "meta", "system"];
757
- // 그룹명 → 해당 그룹의 모든 패키지 반환 (DB 기반)
750
+ // 그룹명 → 해당 그룹의 모든 패키지 반환
758
751
  async function getPackagesByGroupAsync(group) {
759
- const packages = await (0, supabase_1.getPackagesByGroup)(group);
760
- return packages.map(p => p.name);
752
+ // v3.14.0: 동기 함수와 동일하게 폴백 데이터 사용
753
+ return getPackagesByGroupSync(group);
761
754
  }
762
755
  // 그룹명 → 해당 그룹의 모든 패키지 반환 (동기, 폴백)
763
756
  function getPackagesByGroupSync(group) {
@@ -1288,50 +1281,176 @@ program
1288
1281
  }
1289
1282
  console.log();
1290
1283
  });
1291
- // === Standard 설치 (semo-core + semo-skills) ===
1284
+ // === Standard 설치 (DB 기반) ===
1292
1285
  async function setupStandard(cwd, force) {
1293
- const semoSystemDir = path.join(cwd, "semo-system");
1294
- console.log(chalk_1.default.cyan("\n📚 Standard 설치 (White Box)"));
1295
- console.log(chalk_1.default.gray(" semo-core: 원칙, 오케스트레이터"));
1296
- console.log(chalk_1.default.gray(" semo-skills: 13개 통합 스킬"));
1297
- console.log(chalk_1.default.gray(" semo-agents: 14개 페르소나 Agent"));
1298
- console.log(chalk_1.default.gray(" semo-scripts: 자동화 스크립트\n"));
1299
- // 기존 디렉토리 확인
1300
- if (fs.existsSync(semoSystemDir) && !force) {
1301
- const shouldOverwrite = await confirmOverwrite("semo-system/", semoSystemDir);
1302
- if (!shouldOverwrite) {
1303
- console.log(chalk_1.default.gray(" → semo-system/ 건너뜀"));
1304
- return;
1305
- }
1306
- removeRecursive(semoSystemDir);
1307
- console.log(chalk_1.default.green(" ✓ 기존 semo-system/ 삭제됨"));
1308
- }
1309
- const spinner = (0, ora_1.default)("semo-core, semo-skills, semo-agents, semo-scripts 다운로드 중...").start();
1286
+ const claudeDir = path.join(cwd, ".claude");
1287
+ console.log(chalk_1.default.cyan("\n📚 Standard 설치 (DB 기반)"));
1288
+ console.log(chalk_1.default.gray(" 스킬: DB에서 조회하여 파일 생성"));
1289
+ console.log(chalk_1.default.gray(" 커맨드: DB에서 조회하여 파일 생성"));
1290
+ console.log(chalk_1.default.gray(" 에이전트: DB에서 조회하여 파일 생성\n"));
1291
+ const spinner = (0, ora_1.default)("DB에서 스킬/커맨드/에이전트 조회 중...").start();
1310
1292
  try {
1311
- const tempDir = path.join(cwd, ".semo-temp");
1312
- removeRecursive(tempDir);
1313
- (0, child_process_1.execSync)(`git clone --depth 1 ${SEMO_REPO} "${tempDir}"`, { stdio: "pipe" });
1314
- fs.mkdirSync(semoSystemDir, { recursive: true });
1315
- // Standard 패키지 목록 (semo-system/ 하위에 있는 것들)
1316
- const standardPackages = ["semo-core", "semo-skills", "semo-agents", "semo-scripts"];
1317
- for (const pkg of standardPackages) {
1318
- const srcPath = path.join(tempDir, "semo-system", pkg);
1319
- const destPath = path.join(semoSystemDir, pkg);
1320
- if (fs.existsSync(srcPath)) {
1321
- copyRecursive(srcPath, destPath);
1322
- }
1293
+ // DB 연결 확인
1294
+ const connected = await (0, database_1.isDbConnected)();
1295
+ if (connected) {
1296
+ spinner.text = "DB 연결 성공, 데이터 조회 중...";
1323
1297
  }
1324
- removeRecursive(tempDir);
1325
- spinner.succeed("Standard 설치 완료");
1326
- // 심볼릭 링크 생성
1327
- await createStandardSymlinks(cwd);
1298
+ else {
1299
+ spinner.text = "DB 연결 실패, 폴백 데이터 사용 중...";
1300
+ }
1301
+ // .claude 디렉토리 생성
1302
+ fs.mkdirSync(claudeDir, { recursive: true });
1303
+ // 1. 스킬 설치
1304
+ const skillsDir = path.join(claudeDir, "skills");
1305
+ if (force && fs.existsSync(skillsDir)) {
1306
+ removeRecursive(skillsDir);
1307
+ }
1308
+ fs.mkdirSync(skillsDir, { recursive: true });
1309
+ const skills = await (0, database_1.getActiveSkills)();
1310
+ for (const skill of skills) {
1311
+ const skillFolder = path.join(skillsDir, skill.name);
1312
+ fs.mkdirSync(skillFolder, { recursive: true });
1313
+ fs.writeFileSync(path.join(skillFolder, "SKILL.md"), skill.content);
1314
+ }
1315
+ console.log(chalk_1.default.green(` ✓ skills 설치 완료 (${skills.length}개)`));
1316
+ // 2. 커맨드 설치
1317
+ const commandsDir = path.join(claudeDir, "commands");
1318
+ if (force && fs.existsSync(commandsDir)) {
1319
+ removeRecursive(commandsDir);
1320
+ }
1321
+ fs.mkdirSync(commandsDir, { recursive: true });
1322
+ const commands = await (0, database_1.getCommands)();
1323
+ // 폴더별로 그룹핑
1324
+ const commandsByFolder = {};
1325
+ for (const cmd of commands) {
1326
+ if (!commandsByFolder[cmd.folder]) {
1327
+ commandsByFolder[cmd.folder] = [];
1328
+ }
1329
+ commandsByFolder[cmd.folder].push(cmd);
1330
+ }
1331
+ let cmdCount = 0;
1332
+ for (const [folder, cmds] of Object.entries(commandsByFolder)) {
1333
+ const folderPath = path.join(commandsDir, folder);
1334
+ fs.mkdirSync(folderPath, { recursive: true });
1335
+ for (const cmd of cmds) {
1336
+ fs.writeFileSync(path.join(folderPath, `${cmd.name}.md`), cmd.content);
1337
+ cmdCount++;
1338
+ }
1339
+ }
1340
+ console.log(chalk_1.default.green(` ✓ commands 설치 완료 (${cmdCount}개)`));
1341
+ // 3. 에이전트 설치
1342
+ const agentsDir = path.join(claudeDir, "agents");
1343
+ if (force && fs.existsSync(agentsDir)) {
1344
+ removeRecursive(agentsDir);
1345
+ }
1346
+ fs.mkdirSync(agentsDir, { recursive: true });
1347
+ const agents = await (0, database_1.getAgents)();
1348
+ for (const agent of agents) {
1349
+ const agentFolder = path.join(agentsDir, agent.name);
1350
+ fs.mkdirSync(agentFolder, { recursive: true });
1351
+ fs.writeFileSync(path.join(agentFolder, `${agent.name}.md`), agent.content);
1352
+ }
1353
+ console.log(chalk_1.default.green(` ✓ agents 설치 완료 (${agents.length}개)`));
1354
+ spinner.succeed("Standard 설치 완료 (DB 기반)");
1355
+ // CLAUDE.md 생성
1356
+ await generateClaudeMd(cwd);
1328
1357
  }
1329
1358
  catch (error) {
1330
1359
  spinner.fail("Standard 설치 실패");
1331
1360
  console.error(chalk_1.default.red(` ${error}`));
1332
1361
  }
1333
1362
  }
1334
- // === Standard 심볼릭 링크 ===
1363
+ // === CLAUDE.md 생성 (DB 기반) ===
1364
+ async function generateClaudeMd(cwd) {
1365
+ console.log(chalk_1.default.cyan("\n📄 CLAUDE.md 생성"));
1366
+ const claudeMdPath = path.join(cwd, ".claude", "CLAUDE.md");
1367
+ const skills = await (0, database_1.getActiveSkills)();
1368
+ const skillCategories = await (0, database_1.getSkillCountByCategory)();
1369
+ const skillList = Object.entries(skillCategories)
1370
+ .map(([cat, count]) => ` - ${cat}: ${count}개`)
1371
+ .join("\n");
1372
+ const claudeMdContent = `# SEMO Project Configuration
1373
+
1374
+ > SEMO (Semicolon Orchestrate) - AI Agent Orchestration Framework v3.14.0
1375
+
1376
+ ---
1377
+
1378
+ ## 🔴 MANDATORY: Orchestrator-First Execution
1379
+
1380
+ > **⚠️ 이 규칙은 모든 사용자 요청에 적용됩니다. 예외 없음.**
1381
+
1382
+ ### 실행 흐름 (필수)
1383
+
1384
+ \`\`\`
1385
+ 1. 사용자 요청 수신
1386
+ 2. Orchestrator가 의도 분석 후 적절한 Agent/Skill 라우팅
1387
+ 3. Agent/Skill이 작업 수행
1388
+ 4. 실행 결과 반환
1389
+ \`\`\`
1390
+
1391
+ ### Orchestrator 참조
1392
+
1393
+ **Primary Orchestrator**: \`.claude/agents/orchestrator/orchestrator.md\`
1394
+
1395
+ 이 파일에서 라우팅 테이블, 의도 분류, 메시지 포맷을 확인하세요.
1396
+
1397
+ ---
1398
+
1399
+ ## 🔴 NON-NEGOTIABLE RULES
1400
+
1401
+ ### 1. Orchestrator-First Policy
1402
+
1403
+ > **모든 요청은 반드시 Orchestrator를 통해 라우팅됩니다. 직접 처리 금지.**
1404
+
1405
+ **직접 처리 금지 항목**:
1406
+ - 코드 작성/수정 → \`write-code\` 스킬
1407
+ - Git 커밋/푸시 → \`git-workflow\` 스킬
1408
+ - 품질 검증 → \`quality-gate\` 스킬
1409
+
1410
+ ### 2. Pre-Commit Quality Gate
1411
+
1412
+ > **코드 변경이 포함된 커밋 전 반드시 Quality Gate를 통과해야 합니다.**
1413
+
1414
+ \`\`\`bash
1415
+ # 필수 검증 순서
1416
+ npm run lint # 1. ESLint 검사
1417
+ npx tsc --noEmit # 2. TypeScript 타입 체크
1418
+ npm run build # 3. 빌드 검증
1419
+ \`\`\`
1420
+
1421
+ ---
1422
+
1423
+ ## 설치된 구성
1424
+
1425
+ ### 스킬 (${skills.length}개)
1426
+ ${skillList}
1427
+
1428
+ ## 구조
1429
+
1430
+ \`\`\`
1431
+ .claude/
1432
+ ├── settings.json # MCP 서버 설정
1433
+ ├── agents/ # 에이전트 (DB 기반 설치)
1434
+ ├── skills/ # 스킬 (DB 기반 설치)
1435
+ └── commands/ # 커맨드 (DB 기반 설치)
1436
+ \`\`\`
1437
+
1438
+ ## 사용 가능한 커맨드
1439
+
1440
+ | 커맨드 | 설명 |
1441
+ |--------|------|
1442
+ | \`/SEMO:help\` | 도움말 |
1443
+ | \`/SEMO:dry-run {프롬프트}\` | 명령 검증 (라우팅 시뮬레이션) |
1444
+ | \`/SEMO-workflow:greenfield\` | Greenfield 워크플로우 시작 |
1445
+
1446
+ ---
1447
+
1448
+ > Generated by SEMO CLI v3.14.0 (DB-based installation)
1449
+ `;
1450
+ fs.writeFileSync(claudeMdPath, claudeMdContent);
1451
+ console.log(chalk_1.default.green("✓ .claude/CLAUDE.md 생성됨"));
1452
+ }
1453
+ // === Standard 심볼릭 링크 (레거시 호환) ===
1335
1454
  async function createStandardSymlinks(cwd) {
1336
1455
  const claudeDir = path.join(cwd, ".claude");
1337
1456
  const semoSystemDir = path.join(cwd, "semo-system");
@@ -1364,7 +1483,7 @@ async function createStandardSymlinks(cwd) {
1364
1483
  }
1365
1484
  fs.mkdirSync(claudeSkillsDir, { recursive: true });
1366
1485
  // DB에서 활성 스킬 목록 조회 (19개 핵심 스킬만)
1367
- const activeSkillNames = await (0, supabase_1.getActiveSkillNames)();
1486
+ const activeSkillNames = await (0, database_1.getActiveSkillNames)();
1368
1487
  let linkedCount = 0;
1369
1488
  for (const skillName of activeSkillNames) {
1370
1489
  const skillLink = path.join(claudeSkillsDir, skillName);
@@ -1413,10 +1532,12 @@ async function createStandardSymlinks(cwd) {
1413
1532
  }
1414
1533
  /**
1415
1534
  * 설치 상태를 검증하고 문제점을 리포트
1535
+ * v3.14.0: DB 기반 설치 지원 (semo-system 없이도 검증 가능)
1416
1536
  */
1417
1537
  function verifyInstallation(cwd, installedExtensions = []) {
1418
1538
  const claudeDir = path.join(cwd, ".claude");
1419
1539
  const semoSystemDir = path.join(cwd, "semo-system");
1540
+ const hasSemoSystem = fs.existsSync(semoSystemDir);
1420
1541
  const result = {
1421
1542
  success: true,
1422
1543
  errors: [],
@@ -1428,12 +1549,42 @@ function verifyInstallation(cwd, installedExtensions = []) {
1428
1549
  extensions: [],
1429
1550
  },
1430
1551
  };
1431
- // 1. semo-system 기본 구조 검증
1432
- if (!fs.existsSync(semoSystemDir)) {
1433
- result.errors.push("semo-system 디렉토리가 없습니다");
1552
+ // v3.14.0: DB 기반 설치 시 semo-system 없어도
1553
+ // .claude/ 디렉토리가 있으면 DB 기반으로 설치된 것으로 간주
1554
+ if (!hasSemoSystem && !fs.existsSync(claudeDir)) {
1555
+ result.errors.push(".claude 디렉토리가 없습니다");
1434
1556
  result.success = false;
1435
1557
  return result;
1436
1558
  }
1559
+ // DB 기반 설치 검증 (semo-system 없음)
1560
+ if (!hasSemoSystem) {
1561
+ // agents 검증 (실제 파일 존재 여부)
1562
+ const claudeAgentsDir = path.join(claudeDir, "agents");
1563
+ if (fs.existsSync(claudeAgentsDir)) {
1564
+ const agents = fs.readdirSync(claudeAgentsDir).filter(f => {
1565
+ const p = path.join(claudeAgentsDir, f);
1566
+ return fs.existsSync(p) && fs.statSync(p).isDirectory();
1567
+ });
1568
+ result.stats.agents.expected = agents.length;
1569
+ result.stats.agents.linked = agents.length; // DB 기반이므로 실제 파일
1570
+ }
1571
+ // skills 검증 (실제 파일 존재 여부)
1572
+ const claudeSkillsDir = path.join(claudeDir, "skills");
1573
+ if (fs.existsSync(claudeSkillsDir)) {
1574
+ const skills = fs.readdirSync(claudeSkillsDir).filter(f => {
1575
+ const p = path.join(claudeSkillsDir, f);
1576
+ return fs.existsSync(p) && fs.statSync(p).isDirectory();
1577
+ });
1578
+ result.stats.skills.expected = skills.length;
1579
+ result.stats.skills.linked = skills.length; // DB 기반이므로 실제 파일
1580
+ }
1581
+ // commands 검증 (실제 폴더 존재 여부)
1582
+ const semoCommandsDir = path.join(claudeDir, "commands", "SEMO");
1583
+ result.stats.commands.exists = fs.existsSync(semoCommandsDir);
1584
+ result.stats.commands.valid = result.stats.commands.exists;
1585
+ return result;
1586
+ }
1587
+ // === 레거시: semo-system 기반 설치 검증 ===
1437
1588
  const coreDir = path.join(semoSystemDir, "semo-core");
1438
1589
  const skillsDir = path.join(semoSystemDir, "semo-skills");
1439
1590
  if (!fs.existsSync(coreDir)) {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@team-semicolon/semo-cli",
3
- "version": "3.13.1",
3
+ "version": "3.14.1",
4
4
  "description": "SEMO CLI - AI Agent Orchestration Framework Installer",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "semo": "./dist/index.js",
8
- "semo-cli": "./dist/index.js"
7
+ "semo": "dist/index.js",
8
+ "semo-cli": "dist/index.js"
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc",
@@ -23,21 +23,22 @@
23
23
  "license": "MIT",
24
24
  "repository": {
25
25
  "type": "git",
26
- "url": "https://github.com/semicolon-devteam/semo.git",
26
+ "url": "git+https://github.com/semicolon-devteam/semo.git",
27
27
  "directory": "packages/cli"
28
28
  },
29
29
  "dependencies": {
30
- "@supabase/supabase-js": "^2.49.1",
31
30
  "chalk": "^4.1.2",
32
31
  "commander": "^12.0.0",
32
+ "inquirer": "^8.2.6",
33
33
  "ora": "^5.4.1",
34
- "inquirer": "^8.2.6"
34
+ "pg": "^8.17.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "^20.0.0",
38
37
  "@types/inquirer": "^8.2.0",
39
- "typescript": "^5.0.0",
40
- "ts-node": "^10.0.0"
38
+ "@types/node": "^20.0.0",
39
+ "@types/pg": "^8.16.0",
40
+ "ts-node": "^10.0.0",
41
+ "typescript": "^5.0.0"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=18.0.0"
@@ -1,97 +0,0 @@
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
- }>>;
74
- export interface SkillDefinition {
75
- id: string;
76
- name: string;
77
- display_name: string;
78
- description: string | null;
79
- category: "workflow" | "discovery" | "planning" | "solutioning" | "implementation" | "supporting";
80
- source_path: string;
81
- is_active: boolean;
82
- is_required: boolean;
83
- install_order: number;
84
- version: string;
85
- }
86
- /**
87
- * 활성 스킬 목록 조회 (설치할 스킬)
88
- */
89
- export declare function getActiveSkills(): Promise<SkillDefinition[]>;
90
- /**
91
- * 스킬 이름 목록만 조회
92
- */
93
- export declare function getActiveSkillNames(): Promise<string[]>;
94
- /**
95
- * 카테고리별 스킬 개수 조회
96
- */
97
- export declare function getSkillCountByCategory(): Promise<Record<string, number>>;