@team-semicolon/semo-cli 3.0.2 → 3.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 +292 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -403,6 +403,148 @@ async function downloadExtensions(cwd, packages, force) {
|
|
|
403
403
|
console.error(chalk_1.default.red(` ${error}`));
|
|
404
404
|
}
|
|
405
405
|
}
|
|
406
|
+
// === Orchestrator 병합 파일 생성 ===
|
|
407
|
+
function createMergedOrchestrator(claudeAgentsDir, orchestratorSources) {
|
|
408
|
+
const orchestratorDir = path.join(claudeAgentsDir, "orchestrator");
|
|
409
|
+
fs.mkdirSync(orchestratorDir, { recursive: true });
|
|
410
|
+
// _packages 디렉토리 생성 (원본 참조용)
|
|
411
|
+
const packagesDir = path.join(orchestratorDir, "_packages");
|
|
412
|
+
fs.mkdirSync(packagesDir, { recursive: true });
|
|
413
|
+
// 각 패키지의 orchestrator 내용 수집
|
|
414
|
+
const routingTables = [];
|
|
415
|
+
const availableAgents = [];
|
|
416
|
+
const availableSkills = [];
|
|
417
|
+
const crossPackageRouting = [];
|
|
418
|
+
for (const source of orchestratorSources) {
|
|
419
|
+
const orchestratorMdPath = path.join(source.path, "orchestrator.md");
|
|
420
|
+
if (!fs.existsSync(orchestratorMdPath))
|
|
421
|
+
continue;
|
|
422
|
+
const content = fs.readFileSync(orchestratorMdPath, "utf-8");
|
|
423
|
+
const pkgShortName = source.pkg.replace(/\//g, "-");
|
|
424
|
+
// 원본 파일 복사 (참조용)
|
|
425
|
+
fs.writeFileSync(path.join(packagesDir, `${pkgShortName}.md`), content);
|
|
426
|
+
// Quick Routing Table 추출
|
|
427
|
+
const routingMatch = content.match(/## 🔴 Quick Routing Table[\s\S]*?\n\n([\s\S]*?)(?=\n## |$)/);
|
|
428
|
+
if (routingMatch) {
|
|
429
|
+
routingTables.push(`### ${EXTENSION_PACKAGES[source.pkg]?.name || source.pkg}\n\n${routingMatch[1].trim()}`);
|
|
430
|
+
}
|
|
431
|
+
// Available Agents 추출
|
|
432
|
+
const agentsMatch = content.match(/## Available Agents[\s\S]*?\n\n([\s\S]*?)(?=\n## |$)/);
|
|
433
|
+
if (agentsMatch) {
|
|
434
|
+
availableAgents.push(`### ${EXTENSION_PACKAGES[source.pkg]?.name || source.pkg}\n\n${agentsMatch[1].trim()}`);
|
|
435
|
+
}
|
|
436
|
+
// Available Skills 추출
|
|
437
|
+
const skillsMatch = content.match(/## Available Skills[\s\S]*?\n\n([\s\S]*?)(?=\n## |$)/);
|
|
438
|
+
if (skillsMatch) {
|
|
439
|
+
availableSkills.push(`### ${EXTENSION_PACKAGES[source.pkg]?.name || source.pkg}\n\n${skillsMatch[1].trim()}`);
|
|
440
|
+
}
|
|
441
|
+
// Cross-Package Routing 추출
|
|
442
|
+
const crossMatch = content.match(/## 🔄 Cross-Package Routing[\s\S]*?\n\n([\s\S]*?)(?=\n## |$)/);
|
|
443
|
+
if (crossMatch) {
|
|
444
|
+
crossPackageRouting.push(crossMatch[1].trim());
|
|
445
|
+
}
|
|
446
|
+
// references 폴더가 있으면 복사
|
|
447
|
+
const refsDir = path.join(source.path, "references");
|
|
448
|
+
if (fs.existsSync(refsDir)) {
|
|
449
|
+
const mergedRefsDir = path.join(orchestratorDir, "references");
|
|
450
|
+
fs.mkdirSync(mergedRefsDir, { recursive: true });
|
|
451
|
+
const refs = fs.readdirSync(refsDir);
|
|
452
|
+
for (const ref of refs) {
|
|
453
|
+
const srcRef = path.join(refsDir, ref);
|
|
454
|
+
const destRef = path.join(mergedRefsDir, `${pkgShortName}-${ref}`);
|
|
455
|
+
if (fs.statSync(srcRef).isFile()) {
|
|
456
|
+
fs.copyFileSync(srcRef, destRef);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
// 병합된 orchestrator.md 생성
|
|
462
|
+
const mergedContent = `---
|
|
463
|
+
name: orchestrator
|
|
464
|
+
description: |
|
|
465
|
+
SEMO Merged Orchestrator - Routes all user requests to appropriate agents/skills.
|
|
466
|
+
This orchestrator combines routing tables from ${orchestratorSources.length} packages.
|
|
467
|
+
PROACTIVELY delegate on ALL requests. Never process directly.
|
|
468
|
+
tools:
|
|
469
|
+
- read_file
|
|
470
|
+
- list_dir
|
|
471
|
+
- run_command
|
|
472
|
+
- glob
|
|
473
|
+
- grep
|
|
474
|
+
- task
|
|
475
|
+
- skill
|
|
476
|
+
model: inherit
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
# SEMO Merged Orchestrator
|
|
480
|
+
|
|
481
|
+
> 이 파일은 **자동 생성**되었습니다. 직접 수정하지 마세요.
|
|
482
|
+
> 원본 파일: \`_packages/\` 디렉토리 참조
|
|
483
|
+
|
|
484
|
+
모든 사용자 요청을 분석하고 적절한 Agent 또는 Skill로 라우팅하는 **Primary Router**입니다.
|
|
485
|
+
|
|
486
|
+
## 🔴 설치된 패키지
|
|
487
|
+
|
|
488
|
+
${orchestratorSources.map(s => `- **${EXTENSION_PACKAGES[s.pkg]?.name || s.pkg}**: \`semo-system/${s.pkg}\``).join("\n")}
|
|
489
|
+
|
|
490
|
+
## �� Quick Routing Table (Merged)
|
|
491
|
+
|
|
492
|
+
> 키워드 매칭 시 **첫 번째 매칭된 패키지**로 라우팅됩니다.
|
|
493
|
+
|
|
494
|
+
${routingTables.join("\n\n---\n\n")}
|
|
495
|
+
|
|
496
|
+
## SEMO 메시지 포맷
|
|
497
|
+
|
|
498
|
+
### Agent 위임
|
|
499
|
+
|
|
500
|
+
\`\`\`markdown
|
|
501
|
+
[SEMO] Orchestrator: 의도 분석 완료 → {intent_category}
|
|
502
|
+
|
|
503
|
+
[SEMO] Agent 위임: {agent_name} (사유: {reason})
|
|
504
|
+
\`\`\`
|
|
505
|
+
|
|
506
|
+
### Skill 호출
|
|
507
|
+
|
|
508
|
+
\`\`\`markdown
|
|
509
|
+
[SEMO] Orchestrator: 의도 분석 완료 → {intent_category}
|
|
510
|
+
|
|
511
|
+
[SEMO] Skill 호출: {skill_name}
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
### 라우팅 실패
|
|
515
|
+
|
|
516
|
+
\`\`\`markdown
|
|
517
|
+
[SEMO] Orchestrator: 라우팅 실패 → 적절한 Agent/Skill 없음
|
|
518
|
+
|
|
519
|
+
⚠️ 직접 처리 필요
|
|
520
|
+
\`\`\`
|
|
521
|
+
|
|
522
|
+
## Critical Rules
|
|
523
|
+
|
|
524
|
+
1. **Routing-Only**: 직접 작업 수행 금지
|
|
525
|
+
2. **SEMO 메시지 필수**: 모든 위임에 SEMO 메시지 포함
|
|
526
|
+
3. **Package Priority**: 라우팅 충돌 시 설치 순서대로 우선순위 적용
|
|
527
|
+
4. **Cross-Package**: 다른 패키지 전문 영역 요청 시 인계 권유
|
|
528
|
+
|
|
529
|
+
${crossPackageRouting.length > 0 ? `## 🔄 Cross-Package Routing
|
|
530
|
+
|
|
531
|
+
${crossPackageRouting[0]}` : ""}
|
|
532
|
+
|
|
533
|
+
${availableAgents.length > 0 ? `## Available Agents (All Packages)
|
|
534
|
+
|
|
535
|
+
${availableAgents.join("\n\n")}` : ""}
|
|
536
|
+
|
|
537
|
+
${availableSkills.length > 0 ? `## Available Skills (All Packages)
|
|
538
|
+
|
|
539
|
+
${availableSkills.join("\n\n")}` : ""}
|
|
540
|
+
|
|
541
|
+
## References
|
|
542
|
+
|
|
543
|
+
- 원본 Orchestrator: \`_packages/\` 디렉토리
|
|
544
|
+
- 병합된 References: \`references/\` 디렉토리
|
|
545
|
+
`;
|
|
546
|
+
fs.writeFileSync(path.join(orchestratorDir, "orchestrator.md"), mergedContent);
|
|
547
|
+
}
|
|
406
548
|
// === Extensions 심볼릭 링크 설정 (agents/skills 병합) ===
|
|
407
549
|
async function setupExtensionSymlinks(cwd, packages) {
|
|
408
550
|
console.log(chalk_1.default.cyan("\n🔗 Extensions 연결"));
|
|
@@ -413,12 +555,12 @@ async function setupExtensionSymlinks(cwd, packages) {
|
|
|
413
555
|
const claudeSkillsDir = path.join(claudeDir, "skills");
|
|
414
556
|
fs.mkdirSync(claudeAgentsDir, { recursive: true });
|
|
415
557
|
fs.mkdirSync(claudeSkillsDir, { recursive: true });
|
|
558
|
+
// Orchestrator 소스 수집 (병합용)
|
|
559
|
+
const orchestratorSources = [];
|
|
416
560
|
for (const pkg of packages) {
|
|
417
561
|
const pkgPath = path.join(semoSystemDir, pkg);
|
|
418
562
|
if (!fs.existsSync(pkgPath))
|
|
419
563
|
continue;
|
|
420
|
-
// Note: .claude/semo-{pkg} 링크는 생성하지 않음 (불필요)
|
|
421
|
-
// Extension의 agents/skills만 개별 링크하여 병합
|
|
422
564
|
// 1. Extension의 agents를 .claude/agents/에 개별 링크
|
|
423
565
|
const extAgentsDir = path.join(pkgPath, "agents");
|
|
424
566
|
if (fs.existsSync(extAgentsDir)) {
|
|
@@ -426,6 +568,11 @@ async function setupExtensionSymlinks(cwd, packages) {
|
|
|
426
568
|
for (const agent of agents) {
|
|
427
569
|
const agentLink = path.join(claudeAgentsDir, agent);
|
|
428
570
|
const agentTarget = path.join(extAgentsDir, agent);
|
|
571
|
+
// Orchestrator는 특별 처리 (병합 필요)
|
|
572
|
+
if (agent === "orchestrator") {
|
|
573
|
+
orchestratorSources.push({ pkg, path: agentTarget });
|
|
574
|
+
continue; // 심볼릭 링크 생성 안 함
|
|
575
|
+
}
|
|
429
576
|
if (!fs.existsSync(agentLink)) {
|
|
430
577
|
createSymlinkOrJunction(agentTarget, agentLink);
|
|
431
578
|
console.log(chalk_1.default.green(` ✓ .claude/agents/${agent} → semo-system/${pkg}/agents/${agent}`));
|
|
@@ -446,6 +593,27 @@ async function setupExtensionSymlinks(cwd, packages) {
|
|
|
446
593
|
}
|
|
447
594
|
}
|
|
448
595
|
}
|
|
596
|
+
// 3. Orchestrator 병합 처리
|
|
597
|
+
if (orchestratorSources.length > 0) {
|
|
598
|
+
// 기존 orchestrator 링크/디렉토리 제거
|
|
599
|
+
const orchestratorPath = path.join(claudeAgentsDir, "orchestrator");
|
|
600
|
+
if (fs.existsSync(orchestratorPath)) {
|
|
601
|
+
removeRecursive(orchestratorPath);
|
|
602
|
+
}
|
|
603
|
+
if (orchestratorSources.length === 1) {
|
|
604
|
+
// 단일 패키지: 심볼릭 링크
|
|
605
|
+
createSymlinkOrJunction(orchestratorSources[0].path, orchestratorPath);
|
|
606
|
+
console.log(chalk_1.default.green(` ✓ .claude/agents/orchestrator → semo-system/${orchestratorSources[0].pkg}/agents/orchestrator`));
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
// 다중 패키지: 병합 파일 생성
|
|
610
|
+
createMergedOrchestrator(claudeAgentsDir, orchestratorSources);
|
|
611
|
+
console.log(chalk_1.default.green(` ✓ .claude/agents/orchestrator (${orchestratorSources.length}개 패키지 병합)`));
|
|
612
|
+
for (const source of orchestratorSources) {
|
|
613
|
+
console.log(chalk_1.default.gray(` - semo-system/${source.pkg}/agents/orchestrator`));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
449
617
|
}
|
|
450
618
|
const BASE_MCP_SERVERS = [
|
|
451
619
|
{
|
|
@@ -1141,10 +1309,40 @@ program
|
|
|
1141
1309
|
program
|
|
1142
1310
|
.command("update")
|
|
1143
1311
|
.description("SEMO를 최신 버전으로 업데이트합니다")
|
|
1144
|
-
.
|
|
1312
|
+
.option("--self", "CLI만 업데이트")
|
|
1313
|
+
.option("--system", "semo-system만 업데이트")
|
|
1314
|
+
.option("--skip-cli", "CLI 업데이트 건너뛰기")
|
|
1315
|
+
.action(async (options) => {
|
|
1145
1316
|
console.log(chalk_1.default.cyan.bold("\n🔄 SEMO 업데이트\n"));
|
|
1146
1317
|
const cwd = process.cwd();
|
|
1147
1318
|
const semoSystemDir = path.join(cwd, "semo-system");
|
|
1319
|
+
const claudeDir = path.join(cwd, ".claude");
|
|
1320
|
+
// === 1. CLI 자체 업데이트 ===
|
|
1321
|
+
if (options.self || (!options.system && !options.skipCli)) {
|
|
1322
|
+
console.log(chalk_1.default.cyan("📦 CLI 업데이트"));
|
|
1323
|
+
const cliSpinner = (0, ora_1.default)(" @team-semicolon/semo-cli 업데이트 중...").start();
|
|
1324
|
+
try {
|
|
1325
|
+
(0, child_process_1.execSync)("npm update -g @team-semicolon/semo-cli", { stdio: "pipe" });
|
|
1326
|
+
cliSpinner.succeed(" CLI 업데이트 완료");
|
|
1327
|
+
}
|
|
1328
|
+
catch (error) {
|
|
1329
|
+
cliSpinner.fail(" CLI 업데이트 실패");
|
|
1330
|
+
const errorMsg = String(error);
|
|
1331
|
+
if (errorMsg.includes("EACCES") || errorMsg.includes("permission")) {
|
|
1332
|
+
console.log(chalk_1.default.yellow("\n 💡 권한 오류: 다음 명령어로 재시도하세요:"));
|
|
1333
|
+
console.log(chalk_1.default.white(" sudo npm update -g @team-semicolon/semo-cli\n"));
|
|
1334
|
+
}
|
|
1335
|
+
else {
|
|
1336
|
+
console.error(chalk_1.default.gray(` ${errorMsg}`));
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
// --self 옵션만 있으면 여기서 종료
|
|
1340
|
+
if (options.self) {
|
|
1341
|
+
console.log(chalk_1.default.green.bold("\n✅ CLI 업데이트 완료!\n"));
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
// === 2. semo-system 업데이트 ===
|
|
1148
1346
|
if (!fs.existsSync(semoSystemDir)) {
|
|
1149
1347
|
console.log(chalk_1.default.red("SEMO가 설치되어 있지 않습니다. 'semo init'을 먼저 실행하세요."));
|
|
1150
1348
|
process.exit(1);
|
|
@@ -1156,13 +1354,14 @@ program
|
|
|
1156
1354
|
installedExtensions.push(key);
|
|
1157
1355
|
}
|
|
1158
1356
|
}
|
|
1159
|
-
console.log(chalk_1.default.cyan("업데이트
|
|
1160
|
-
console.log(chalk_1.default.gray("
|
|
1161
|
-
console.log(chalk_1.default.gray("
|
|
1357
|
+
console.log(chalk_1.default.cyan("\n📚 semo-system 업데이트"));
|
|
1358
|
+
console.log(chalk_1.default.gray(" 대상:"));
|
|
1359
|
+
console.log(chalk_1.default.gray(" - semo-core"));
|
|
1360
|
+
console.log(chalk_1.default.gray(" - semo-skills"));
|
|
1162
1361
|
installedExtensions.forEach(pkg => {
|
|
1163
|
-
console.log(chalk_1.default.gray(`
|
|
1362
|
+
console.log(chalk_1.default.gray(` - ${pkg}`));
|
|
1164
1363
|
});
|
|
1165
|
-
const spinner = (0, ora_1.default)("\n최신 버전 다운로드 중...").start();
|
|
1364
|
+
const spinner = (0, ora_1.default)("\n 최신 버전 다운로드 중...").start();
|
|
1166
1365
|
try {
|
|
1167
1366
|
const tempDir = path.join(cwd, ".semo-temp");
|
|
1168
1367
|
removeRecursive(tempDir);
|
|
@@ -1182,11 +1381,93 @@ program
|
|
|
1182
1381
|
}
|
|
1183
1382
|
}
|
|
1184
1383
|
removeRecursive(tempDir);
|
|
1185
|
-
spinner.succeed("
|
|
1384
|
+
spinner.succeed(" semo-system 업데이트 완료");
|
|
1186
1385
|
}
|
|
1187
1386
|
catch (error) {
|
|
1188
|
-
spinner.fail("업데이트 실패");
|
|
1189
|
-
console.error(chalk_1.default.red(
|
|
1387
|
+
spinner.fail(" semo-system 업데이트 실패");
|
|
1388
|
+
console.error(chalk_1.default.red(` ${error}`));
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
// === 3. 심볼릭 링크 재생성 ===
|
|
1392
|
+
console.log(chalk_1.default.cyan("\n🔗 심볼릭 링크 재생성"));
|
|
1393
|
+
// 기존 링크 정리
|
|
1394
|
+
const claudeAgentsDir = path.join(claudeDir, "agents");
|
|
1395
|
+
const claudeSkillsDir = path.join(claudeDir, "skills");
|
|
1396
|
+
if (fs.existsSync(claudeAgentsDir)) {
|
|
1397
|
+
const existingLinks = fs.readdirSync(claudeAgentsDir);
|
|
1398
|
+
for (const link of existingLinks) {
|
|
1399
|
+
const linkPath = path.join(claudeAgentsDir, link);
|
|
1400
|
+
if (fs.lstatSync(linkPath).isSymbolicLink()) {
|
|
1401
|
+
fs.unlinkSync(linkPath);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
if (fs.existsSync(claudeSkillsDir)) {
|
|
1406
|
+
const existingLinks = fs.readdirSync(claudeSkillsDir);
|
|
1407
|
+
for (const link of existingLinks) {
|
|
1408
|
+
const linkPath = path.join(claudeSkillsDir, link);
|
|
1409
|
+
if (fs.lstatSync(linkPath).isSymbolicLink()) {
|
|
1410
|
+
fs.unlinkSync(linkPath);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
// Standard 심볼릭 링크 재생성
|
|
1415
|
+
await createStandardSymlinks(cwd);
|
|
1416
|
+
// Extensions 심볼릭 링크 재생성
|
|
1417
|
+
if (installedExtensions.length > 0) {
|
|
1418
|
+
await setupExtensionSymlinks(cwd, installedExtensions);
|
|
1419
|
+
}
|
|
1420
|
+
// === 4. CLAUDE.md 재생성 ===
|
|
1421
|
+
console.log(chalk_1.default.cyan("\n📄 CLAUDE.md 재생성"));
|
|
1422
|
+
await setupClaudeMd(cwd, installedExtensions, true);
|
|
1423
|
+
// === 5. MCP 서버 동기화 ===
|
|
1424
|
+
console.log(chalk_1.default.cyan("\n🔧 MCP 서버 동기화"));
|
|
1425
|
+
// Extension의 MCP 설정 확인 및 병합
|
|
1426
|
+
const allServers = [...BASE_MCP_SERVERS];
|
|
1427
|
+
for (const pkg of installedExtensions) {
|
|
1428
|
+
const extSettingsPath = path.join(semoSystemDir, pkg, "settings.local.json");
|
|
1429
|
+
if (fs.existsSync(extSettingsPath)) {
|
|
1430
|
+
try {
|
|
1431
|
+
const extSettings = JSON.parse(fs.readFileSync(extSettingsPath, "utf-8"));
|
|
1432
|
+
if (extSettings.mcpServers) {
|
|
1433
|
+
for (const [name, config] of Object.entries(extSettings.mcpServers)) {
|
|
1434
|
+
const serverConfig = config;
|
|
1435
|
+
allServers.push({
|
|
1436
|
+
name,
|
|
1437
|
+
command: serverConfig.command,
|
|
1438
|
+
args: serverConfig.args,
|
|
1439
|
+
env: serverConfig.env,
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
catch {
|
|
1445
|
+
// 파싱 실패 무시
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
// MCP 서버 등록 상태 확인
|
|
1450
|
+
const missingServers = [];
|
|
1451
|
+
for (const server of allServers) {
|
|
1452
|
+
if (!isMCPServerRegistered(server.name)) {
|
|
1453
|
+
missingServers.push(server);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (missingServers.length === 0) {
|
|
1457
|
+
console.log(chalk_1.default.green(" ✓ 모든 MCP 서버가 등록되어 있습니다"));
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
console.log(chalk_1.default.yellow(` ${missingServers.length}개 MCP 서버 미등록`));
|
|
1461
|
+
for (const server of missingServers) {
|
|
1462
|
+
const result = registerMCPServer(server);
|
|
1463
|
+
if (result.success) {
|
|
1464
|
+
console.log(chalk_1.default.green(` ✓ ${server.name} 등록 완료`));
|
|
1465
|
+
}
|
|
1466
|
+
else {
|
|
1467
|
+
console.log(chalk_1.default.red(` ✗ ${server.name} 등록 실패`));
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1190
1470
|
}
|
|
1471
|
+
console.log(chalk_1.default.green.bold("\n✅ SEMO 업데이트 완료!\n"));
|
|
1191
1472
|
});
|
|
1192
1473
|
program.parse();
|