@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.
- package/dist/index.js +228 -23
- 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
|
-
//
|
|
398
|
-
|
|
399
|
-
|
|
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.
|
|
470
|
-
|
|
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.
|
|
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 (필수)
|