@team-semicolon/semo-cli 2.0.2 → 2.0.4

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 +228 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -394,13 +394,9 @@ async function setupExtensionSymlinks(cwd, packages) {
394
394
  const pkgPath = path.join(semoSystemDir, pkg);
395
395
  if (!fs.existsSync(pkgPath))
396
396
  continue;
397
- // 1. semo-{pkg} 링크
398
- const semoPkgLink = path.join(claudeDir, `semo-${pkg}`);
399
- if (!fs.existsSync(semoPkgLink)) {
400
- createSymlinkOrJunction(pkgPath, semoPkgLink);
401
- console.log(chalk_1.default.green(` ✓ .claude/semo-${pkg} → semo-system/${pkg}`));
402
- }
403
- // 2. Extension의 agents를 .claude/agents/에 개별 링크
397
+ // Note: .claude/semo-{pkg} 링크는 생성하지 않음 (불필요)
398
+ // Extension의 agents/skills만 개별 링크하여 병합
399
+ // 1. Extension의 agents를 .claude/agents/에 개별 링크
404
400
  const extAgentsDir = path.join(pkgPath, "agents");
405
401
  const claudeAgentsDir = path.join(claudeDir, "agents");
406
402
  if (fs.existsSync(extAgentsDir)) {
@@ -430,6 +426,58 @@ async function setupExtensionSymlinks(cwd, packages) {
430
426
  }
431
427
  }
432
428
  }
429
+ const BASE_MCP_SERVERS = [
430
+ {
431
+ name: "semo-integrations",
432
+ command: "npx",
433
+ args: ["-y", "@team-semicolon/semo-mcp"],
434
+ env: {
435
+ GITHUB_TOKEN: "${GITHUB_TOKEN}",
436
+ SLACK_BOT_TOKEN: "${SLACK_BOT_TOKEN}",
437
+ SUPABASE_URL: "${SUPABASE_URL}",
438
+ SUPABASE_KEY: "${SUPABASE_KEY}",
439
+ },
440
+ },
441
+ {
442
+ name: "context7",
443
+ command: "npx",
444
+ args: ["-y", "@upstash/context7-mcp"],
445
+ },
446
+ {
447
+ name: "sequential-thinking",
448
+ command: "npx",
449
+ args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],
450
+ },
451
+ ];
452
+ // === Claude MCP 등록 함수 ===
453
+ function registerMCPServer(server) {
454
+ try {
455
+ // 환경변수가 있는 경우 --env 옵션 추가
456
+ const envArgs = [];
457
+ if (server.env) {
458
+ for (const [key, value] of Object.entries(server.env)) {
459
+ envArgs.push("-e", `${key}=${value}`);
460
+ }
461
+ }
462
+ // claude mcp add 명령어 실행
463
+ const args = [
464
+ "mcp", "add",
465
+ server.name,
466
+ "--",
467
+ server.command,
468
+ ...server.args,
469
+ ];
470
+ // 환경변수가 있으면 명령어 앞에 추가
471
+ if (envArgs.length > 0) {
472
+ args.splice(2, 0, ...envArgs);
473
+ }
474
+ (0, child_process_1.execSync)(`claude ${args.join(" ")}`, { stdio: "pipe" });
475
+ return { success: true };
476
+ }
477
+ catch (error) {
478
+ return { success: false, error: String(error) };
479
+ }
480
+ }
433
481
  // === MCP 설정 ===
434
482
  async function setupMCP(cwd, extensions, force) {
435
483
  console.log(chalk_1.default.cyan("\n🔧 Black Box 설정 (MCP Server)"));
@@ -444,19 +492,10 @@ async function setupMCP(cwd, extensions, force) {
444
492
  }
445
493
  // Base settings (Standard)
446
494
  const settings = {
447
- mcpServers: {
448
- "semo-integrations": {
449
- command: "npx",
450
- args: ["-y", "@team-semicolon/semo-mcp"],
451
- env: {
452
- GITHUB_TOKEN: "${GITHUB_TOKEN}",
453
- SLACK_BOT_TOKEN: "${SLACK_BOT_TOKEN}",
454
- SUPABASE_URL: "${SUPABASE_URL}",
455
- SUPABASE_KEY: "${SUPABASE_KEY}",
456
- },
457
- },
458
- },
495
+ mcpServers: {},
459
496
  };
497
+ // MCP 서버 목록 수집
498
+ const allServers = [...BASE_MCP_SERVERS];
460
499
  // Extension settings 병합
461
500
  const semoSystemDir = path.join(cwd, "semo-system");
462
501
  for (const pkg of extensions) {
@@ -466,8 +505,16 @@ async function setupMCP(cwd, extensions, force) {
466
505
  const extSettings = JSON.parse(fs.readFileSync(extSettingsPath, "utf-8"));
467
506
  // mcpServers 병합
468
507
  if (extSettings.mcpServers) {
469
- Object.assign(settings.mcpServers, extSettings.mcpServers);
470
- console.log(chalk_1.default.gray(` + ${pkg} MCP 설정 병합됨`));
508
+ for (const [name, config] of Object.entries(extSettings.mcpServers)) {
509
+ const serverConfig = config;
510
+ allServers.push({
511
+ name,
512
+ command: serverConfig.command,
513
+ args: serverConfig.args,
514
+ env: serverConfig.env,
515
+ });
516
+ }
517
+ console.log(chalk_1.default.gray(` + ${pkg} MCP 설정 수집됨`));
471
518
  }
472
519
  // permissions 병합
473
520
  if (extSettings.permissions) {
@@ -494,8 +541,53 @@ async function setupMCP(cwd, extensions, force) {
494
541
  }
495
542
  }
496
543
  }
544
+ // settings.json에 mcpServers 저장 (백업용)
545
+ for (const server of allServers) {
546
+ const serverConfig = {
547
+ command: server.command,
548
+ args: server.args,
549
+ };
550
+ if (server.env) {
551
+ serverConfig.env = server.env;
552
+ }
553
+ settings.mcpServers[server.name] = serverConfig;
554
+ }
497
555
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
498
- console.log(chalk_1.default.green("✓ .claude/settings.json 생성됨 (MCP 설정)"));
556
+ console.log(chalk_1.default.green("✓ .claude/settings.json 생성됨 (MCP 설정 백업)"));
557
+ // Claude Code에 MCP 서버 등록 시도
558
+ console.log(chalk_1.default.cyan("\n🔌 Claude Code에 MCP 서버 등록 중..."));
559
+ const successServers = [];
560
+ const failedServers = [];
561
+ for (const server of allServers) {
562
+ const spinner = (0, ora_1.default)(` ${server.name} 등록 중...`).start();
563
+ const result = registerMCPServer(server);
564
+ if (result.success) {
565
+ spinner.succeed(` ${server.name} 등록 완료`);
566
+ successServers.push(server.name);
567
+ }
568
+ else {
569
+ spinner.fail(` ${server.name} 등록 실패`);
570
+ failedServers.push(server);
571
+ }
572
+ }
573
+ // 결과 요약
574
+ if (successServers.length > 0) {
575
+ console.log(chalk_1.default.green(`\n✓ ${successServers.length}개 MCP 서버 자동 등록 완료`));
576
+ }
577
+ // 실패한 서버가 있으면 수동 등록 안내
578
+ if (failedServers.length > 0) {
579
+ console.log(chalk_1.default.yellow(`\n⚠ ${failedServers.length}개 MCP 서버 자동 등록 실패`));
580
+ console.log(chalk_1.default.cyan("\n📋 수동 등록 명령어:"));
581
+ console.log(chalk_1.default.gray(" 다음 명령어를 터미널에서 실행하세요:\n"));
582
+ for (const server of failedServers) {
583
+ const envArgs = server.env
584
+ ? Object.entries(server.env).map(([k, v]) => `-e ${k}="${v}"`).join(" ")
585
+ : "";
586
+ const cmd = `claude mcp add ${server.name} ${envArgs} -- ${server.command} ${server.args.join(" ")}`.trim();
587
+ console.log(chalk_1.default.white(` ${cmd}`));
588
+ }
589
+ console.log();
590
+ }
499
591
  }
500
592
  // === Extension settings 병합 (add 명령어용) ===
501
593
  async function mergeExtensionSettings(cwd, packages) {
@@ -506,6 +598,7 @@ async function mergeExtensionSettings(cwd, packages) {
506
598
  return;
507
599
  }
508
600
  const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
601
+ const newServers = [];
509
602
  for (const pkg of packages) {
510
603
  const extSettingsPath = path.join(semoSystemDir, pkg, "settings.local.json");
511
604
  if (fs.existsSync(extSettingsPath)) {
@@ -514,7 +607,16 @@ async function mergeExtensionSettings(cwd, packages) {
514
607
  // mcpServers 병합
515
608
  if (extSettings.mcpServers) {
516
609
  settings.mcpServers = settings.mcpServers || {};
517
- Object.assign(settings.mcpServers, extSettings.mcpServers);
610
+ for (const [name, config] of Object.entries(extSettings.mcpServers)) {
611
+ const serverConfig = config;
612
+ settings.mcpServers[name] = serverConfig;
613
+ newServers.push({
614
+ name,
615
+ command: serverConfig.command,
616
+ args: serverConfig.args,
617
+ env: serverConfig.env,
618
+ });
619
+ }
518
620
  console.log(chalk_1.default.gray(` + ${pkg} MCP 설정 병합됨`));
519
621
  }
520
622
  // permissions 병합
@@ -541,6 +643,39 @@ async function mergeExtensionSettings(cwd, packages) {
541
643
  }
542
644
  }
543
645
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
646
+ // 새 MCP 서버 Claude Code에 등록
647
+ if (newServers.length > 0) {
648
+ console.log(chalk_1.default.cyan("\n🔌 Claude Code에 MCP 서버 등록 중..."));
649
+ const successServers = [];
650
+ const failedServers = [];
651
+ for (const server of newServers) {
652
+ const spinner = (0, ora_1.default)(` ${server.name} 등록 중...`).start();
653
+ const result = registerMCPServer(server);
654
+ if (result.success) {
655
+ spinner.succeed(` ${server.name} 등록 완료`);
656
+ successServers.push(server.name);
657
+ }
658
+ else {
659
+ spinner.fail(` ${server.name} 등록 실패`);
660
+ failedServers.push(server);
661
+ }
662
+ }
663
+ if (successServers.length > 0) {
664
+ console.log(chalk_1.default.green(`\n✓ ${successServers.length}개 MCP 서버 자동 등록 완료`));
665
+ }
666
+ if (failedServers.length > 0) {
667
+ console.log(chalk_1.default.yellow(`\n⚠ ${failedServers.length}개 MCP 서버 자동 등록 실패`));
668
+ console.log(chalk_1.default.cyan("\n📋 수동 등록 명령어:"));
669
+ for (const server of failedServers) {
670
+ const envArgs = server.env
671
+ ? Object.entries(server.env).map(([k, v]) => `-e ${k}="${v}"`).join(" ")
672
+ : "";
673
+ const cmd = `claude mcp add ${server.name} ${envArgs} -- ${server.command} ${server.args.join(" ")}`.trim();
674
+ console.log(chalk_1.default.white(` ${cmd}`));
675
+ }
676
+ console.log();
677
+ }
678
+ }
544
679
  }
545
680
  // === Context Mesh 초기화 ===
546
681
  async function setupContextMesh(cwd) {
@@ -665,6 +800,76 @@ async function setupClaudeMd(cwd, extensions, force) {
665
800
 
666
801
  > SEMO (Semicolon Orchestrate) - AI Agent Orchestration Framework v${VERSION}
667
802
 
803
+ ---
804
+
805
+ ## 🔴 MANDATORY: Orchestrator-First Execution
806
+
807
+ > **⚠️ 이 규칙은 모든 사용자 요청에 적용됩니다. 예외 없음.**
808
+
809
+ ### 실행 흐름 (필수)
810
+
811
+ \`\`\`
812
+ 1. 사용자 요청 수신
813
+ 2. [SEMO] Orchestrator 메시지 출력 (의도 분석)
814
+ 3. Orchestrator가 적절한 Agent/Skill 라우팅
815
+ 4. [SEMO] Agent/Skill 메시지 출력
816
+ 5. 실행 결과 반환
817
+ \`\`\`
818
+
819
+ ### 모든 응답은 다음으로 시작
820
+
821
+ \`\`\`
822
+ [SEMO] Orchestrator: 의도 분석 완료 → {intent_category}
823
+ [SEMO] {Agent/Skill} 호출: {target} (사유: {reason})
824
+ \`\`\`
825
+
826
+ ### Orchestrator 참조
827
+
828
+ **반드시 읽어야 할 파일**: \`semo-system/semo-core/agents/orchestrator/orchestrator.md\`
829
+
830
+ 이 파일에서 라우팅 테이블, 의도 분류, 메시지 포맷을 확인하세요.
831
+
832
+ ---
833
+
834
+ ## 🔴 NON-NEGOTIABLE RULES
835
+
836
+ ### 1. Orchestrator-First Policy
837
+
838
+ > **모든 요청은 반드시 Orchestrator를 통해 라우팅됩니다. 직접 처리 금지.**
839
+
840
+ **직접 처리 금지 항목**:
841
+ - 코드 작성/수정 → \`implementation-master\` 또는 \`coder\` 스킬
842
+ - Git 커밋/푸시 → \`git-workflow\` 스킬
843
+ - 품질 검증 → \`quality-master\` 또는 \`verify\` 스킬
844
+ - 명세 작성 → \`spec-master\`
845
+ - 일반 작업 → Orchestrator 분석 후 라우팅
846
+
847
+ ### 2. Pre-Commit Quality Gate
848
+
849
+ > **코드 변경이 포함된 커밋 전 반드시 Quality Gate를 통과해야 합니다.**
850
+
851
+ \`\`\`bash
852
+ # 필수 검증 순서
853
+ npm run lint # 1. ESLint 검사
854
+ npx tsc --noEmit # 2. TypeScript 타입 체크
855
+ npm run build # 3. 빌드 검증 (Next.js/TypeScript 프로젝트)
856
+ \`\`\`
857
+
858
+ **차단 항목**:
859
+ - \`--no-verify\` 플래그 사용 금지
860
+ - Quality Gate 우회 시도 거부
861
+ - "그냥 커밋해줘", "빌드 생략해줘" 등 거부
862
+
863
+ ### 3. SEMO Message Format
864
+
865
+ 모든 SEMO 동작은 시스템 메시지로 시작:
866
+
867
+ \`\`\`
868
+ [SEMO] {Component}: {Action} → {Result}
869
+ \`\`\`
870
+
871
+ ---
872
+
668
873
  ## 설치된 구성
669
874
 
670
875
  ### Standard (필수)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semo-cli",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "SEMO CLI - AI Agent Orchestration Framework Installer",
5
5
  "main": "dist/index.js",
6
6
  "bin": {